1 /* 2 * libgit2 "init" example - shows how to initialize a new repo 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.init; 15 16 17 private static import core.stdc.config; 18 private static import core.stdc.stdio; 19 private static import core.stdc.stdlib; 20 private static import core.stdc..string; 21 private static import libgit2_d.commit; 22 private static import libgit2_d.example.args; 23 private static import libgit2_d.example.common; 24 private static import libgit2_d.index; 25 private static import libgit2_d.oid; 26 private static import libgit2_d.repository; 27 private static import libgit2_d.signature; 28 private static import libgit2_d.tree; 29 private static import libgit2_d.types; 30 31 package: 32 33 /** 34 * This is a sample program that is similar to "git init". See the 35 * documentation for that (try "git help init") to understand what this 36 * program is emulating. 37 * 38 * This demonstrates using the libgit2 APIs to initialize a new repository. 39 * 40 * This also contains a special additional option that regular "git init" 41 * does not support which is "--initial-commit" to make a first empty commit. 42 * That is demonstrated in the "create_initial_commit" helper function. 43 */ 44 45 /** 46 * Forward declarations of helpers 47 */ 48 public struct init_opts 49 { 50 int no_options; 51 int quiet; 52 int bare; 53 int initial_commit; 54 uint shared_; 55 const (char)* template_; 56 const (char)* gitdir; 57 const (char)* dir; 58 } 59 60 extern (C) 61 nothrow @nogc 62 //int lg2_init(libgit2_d.types.git_repository* repo, int argc, char*[] argv) 63 public int lg2_init(libgit2_d.types.git_repository* repo, int argc, char** argv) 64 65 in 66 { 67 } 68 69 do 70 { 71 .init_opts o = {1, 0, 0, 0, libgit2_d.repository.git_repository_init_mode_t.GIT_REPOSITORY_INIT_SHARED_UMASK, null, null, null}; 72 73 .parse_opts(&o, argc, argv); 74 75 /* Initialize repository. */ 76 77 if (o.no_options) { 78 /** 79 * No options were specified, so let's demonstrate the default 80 * simple case of libgit2_d.repository.git_repository_init() API usage... 81 */ 82 libgit2_d.example.common.check_lg2(libgit2_d.repository.git_repository_init(&repo, o.dir, 0), "Could not initialize repository", null); 83 } else { 84 /** 85 * Some command line options were specified, so we'll use the 86 * extended init API to handle them 87 */ 88 libgit2_d.repository.git_repository_init_options initopts = libgit2_d.repository.GIT_REPOSITORY_INIT_OPTIONS_INIT(); 89 initopts.flags = libgit2_d.repository.git_repository_init_flag_t.GIT_REPOSITORY_INIT_MKPATH; 90 91 if (o.bare) { 92 initopts.flags |= libgit2_d.repository.git_repository_init_flag_t.GIT_REPOSITORY_INIT_BARE; 93 } 94 95 if (o.template_ != null) { 96 initopts.flags |= libgit2_d.repository.git_repository_init_flag_t.GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE; 97 initopts.template_path = o.template_; 98 } 99 100 if (o.gitdir != null) { 101 /** 102 * If you specified a separate git directory, then initialize 103 * the repository at that path and use the second path as the 104 * working directory of the repository (with a git-link file) 105 */ 106 initopts.workdir_path = o.dir; 107 o.dir = o.gitdir; 108 } 109 110 if (o.shared_ != 0) { 111 initopts.mode = o.shared_; 112 } 113 114 libgit2_d.example.common.check_lg2(libgit2_d.repository.git_repository_init_ext(&repo, o.dir, &initopts), "Could not initialize repository", null); 115 } 116 117 /** Print a message to stdout like "git init" does. */ 118 119 if (!o.quiet) { 120 if ((o.bare) || (o.gitdir)) { 121 o.dir = libgit2_d.repository.git_repository_path(repo); 122 } else { 123 o.dir = libgit2_d.repository.git_repository_workdir(repo); 124 } 125 126 core.stdc.stdio.printf("Initialized empty Git repository in %s\n", o.dir); 127 } 128 129 /** 130 * As an extension to the basic "git init" command, this example 131 * gives the option to create an empty initial commit. This is 132 * mostly to demonstrate what it takes to do that, but also some 133 * people like to have that empty base commit in their repo. 134 */ 135 if (o.initial_commit) { 136 .create_initial_commit(repo); 137 core.stdc.stdio.printf("Created empty initial commit\n"); 138 } 139 140 libgit2_d.repository.git_repository_free(repo); 141 142 return 0; 143 } 144 145 /** 146 * Unlike regular "git init", this example shows how to create an initial 147 * empty commit in the repository. This is the helper function that does 148 * that. 149 */ 150 nothrow @nogc 151 private void create_initial_commit(libgit2_d.types.git_repository* repo) 152 153 in 154 { 155 } 156 157 do 158 { 159 libgit2_d.types.git_signature* sig; 160 161 /** First use the config to initialize a commit signature for the user. */ 162 163 if (libgit2_d.signature.git_signature_default(&sig, repo) < 0) { 164 libgit2_d.example.common.fatal("Unable to create a commit signature.", "Perhaps 'user.name' and 'user.email' are not set"); 165 } 166 167 /* Now let's create an empty tree for this commit */ 168 169 libgit2_d.types.git_index* index; 170 171 if (libgit2_d.repository.git_repository_index(&index, repo) < 0) { 172 libgit2_d.example.common.fatal("Could not open repository index", null); 173 } 174 175 /** 176 * Outside of this example, you could call libgit2_d.index.git_index_add_bypath() 177 * here to put actual files into the index. For our purposes, we'll 178 * leave it empty for now. 179 */ 180 181 libgit2_d.oid.git_oid tree_id; 182 183 if (libgit2_d.index.git_index_write_tree(&tree_id, index) < 0) { 184 libgit2_d.example.common.fatal("Unable to write initial tree from index", null); 185 } 186 187 libgit2_d.index.git_index_free(index); 188 189 libgit2_d.types.git_tree* tree; 190 191 if (libgit2_d.tree.git_tree_lookup(&tree, repo, &tree_id) < 0) { 192 libgit2_d.example.common.fatal("Could not look up initial tree", null); 193 } 194 195 /** 196 * Ready to create the initial commit. 197 * 198 * Normally creating a commit would involve looking up the current 199 * HEAD commit and making that be the parent of the initial commit, 200 * but here this is the first commit so there will be no parent. 201 */ 202 203 libgit2_d.oid.git_oid commit_id; 204 205 if (libgit2_d.commit.git_commit_create_v(&commit_id, repo, "HEAD", sig, sig, null, "Initial commit", tree, 0) < 0) { 206 libgit2_d.example.common.fatal("Could not create the initial commit", null); 207 } 208 209 /** Clean up so we don't leak memory. */ 210 211 libgit2_d.tree.git_tree_free(tree); 212 libgit2_d.signature.git_signature_free(sig); 213 } 214 215 nothrow @nogc 216 private void usage(const (char)* error, const (char)* arg) 217 218 in 219 { 220 } 221 222 do 223 { 224 core.stdc.stdio.fprintf(core.stdc.stdio.stderr, "error: %s '%s'\n", error, arg); 225 226 core.stdc.stdio.fprintf(core.stdc.stdio.stderr, 227 "usage: init [-q | --quiet] [--bare] [--template=<dir>]\n" 228 ~ " [--shared[=perms]] [--initial-commit]\n" 229 ~ " [--separate-git-dir] <directory>\n"); 230 231 core.stdc.stdlib.exit(1); 232 } 233 234 /** 235 * Parse the tail of the --shared= argument. 236 */ 237 nothrow @nogc 238 private uint parse_shared(const (char)* shared_) 239 240 in 241 { 242 } 243 244 do 245 { 246 if ((!core.stdc..string.strcmp(shared_, "false")) || (!core.stdc..string.strcmp(shared_, "umask"))) { 247 return libgit2_d.repository.git_repository_init_mode_t.GIT_REPOSITORY_INIT_SHARED_UMASK; 248 } else if ((!core.stdc..string.strcmp(shared_, "true")) || (!core.stdc..string.strcmp(shared_, "group"))) { 249 return libgit2_d.repository.git_repository_init_mode_t.GIT_REPOSITORY_INIT_SHARED_GROUP; 250 } else if ((!core.stdc..string.strcmp(shared_, "all")) || (!core.stdc..string.strcmp(shared_, "world")) || (!core.stdc..string.strcmp(shared_, "everybody"))) { 251 return libgit2_d.repository.git_repository_init_mode_t.GIT_REPOSITORY_INIT_SHARED_ALL; 252 } else if (shared_[0] == '0') { 253 core.stdc.config.c_long val; 254 const (char)* end = null; 255 val = core.stdc.stdlib.strtol(shared_ + 1, &end, 8); 256 257 if ((end == (shared_ + 1)) || (*end != 0)) { 258 .usage("invalid octal value for --shared", shared_); 259 } 260 261 return cast(uint)(val); 262 } else { 263 .usage("unknown value for --shared", shared_); 264 } 265 266 return 0; 267 } 268 269 nothrow @nogc 270 private void parse_opts(.init_opts* o, int argc, char** argv) 271 272 in 273 { 274 } 275 276 do 277 { 278 libgit2_d.example.args.args_info args = libgit2_d.example.args.ARGS_INFO_INIT(argc, argv); 279 const (char)* sharedarg; 280 281 /** Process arguments. */ 282 283 for (args.pos = 1; args.pos < argc; ++args.pos) { 284 char* a = argv[args.pos]; 285 286 if (a[0] == '-') { 287 o.no_options = 0; 288 } 289 290 if (a[0] != '-') { 291 if (o.dir != null) { 292 .usage("extra argument", a); 293 } 294 295 o.dir = a; 296 } else if ((!core.stdc..string.strcmp(a, "-q")) || (!core.stdc..string.strcmp(a, "--quiet"))) { 297 o.quiet = 1; 298 } else if (!core.stdc..string.strcmp(a, "--bare")) { 299 o.bare = 1; 300 } else if (!core.stdc..string.strcmp(a, "--shared")) { 301 o.shared_ = libgit2_d.repository.git_repository_init_mode_t.GIT_REPOSITORY_INIT_SHARED_GROUP; 302 } else if (!core.stdc..string.strcmp(a, "--initial-commit")) { 303 o.initial_commit = 1; 304 } else if (libgit2_d.example.args.match_str_arg(&sharedarg, &args, "--shared")) { 305 o.shared_ = .parse_shared(sharedarg); 306 } else if ((!libgit2_d.example.args.match_str_arg(&o.template_, &args, "--template")) || (!libgit2_d.example.args.match_str_arg(&o.gitdir, &args, "--separate-git-dir"))) { 307 .usage("unknown option", a); 308 } 309 } 310 311 if (o.dir == null) { 312 .usage("must specify directory to init", ""); 313 } 314 }