summaryrefslogtreecommitdiff
path: root/examples/init.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/init.c')
-rw-r--r--examples/init.c245
1 files changed, 245 insertions, 0 deletions
diff --git a/examples/init.c b/examples/init.c
new file mode 100644
index 000000000..4a379c6e3
--- /dev/null
+++ b/examples/init.c
@@ -0,0 +1,245 @@
+/*
+ * This is a sample program that is similar to "git init". See the
+ * documentation for that (try "git help init") to understand what this
+ * program is emulating.
+ *
+ * This demonstrates using the libgit2 APIs to initialize a new repository.
+ *
+ * This also contains a special additional option that regular "git init"
+ * does not support which is "--initial-commit" to make a first empty commit.
+ * That is demonstrated in the "create_initial_commit" helper function.
+ *
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <stdio.h>
+#include <git2.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+/* not actually good error handling */
+static void fail(const char *msg, const char *arg)
+{
+ if (arg)
+ fprintf(stderr, "%s %s\n", msg, arg);
+ else
+ fprintf(stderr, "%s\n", msg);
+ exit(1);
+}
+
+static void usage(const char *error, const char *arg)
+{
+ fprintf(stderr, "error: %s '%s'\n", error, arg);
+ fprintf(stderr, "usage: init [-q | --quiet] [--bare] "
+ "[--template=<dir>] [--shared[=perms]] <directory>\n");
+ exit(1);
+}
+
+/* simple string prefix test used in argument parsing */
+static size_t is_prefixed(const char *arg, const char *pfx)
+{
+ size_t len = strlen(pfx);
+ return !strncmp(arg, pfx, len) ? len : 0;
+}
+
+/* parse the tail of the --shared= argument */
+static uint32_t parse_shared(const char *shared)
+{
+ if (!strcmp(shared, "false") || !strcmp(shared, "umask"))
+ return GIT_REPOSITORY_INIT_SHARED_UMASK;
+
+ else if (!strcmp(shared, "true") || !strcmp(shared, "group"))
+ return GIT_REPOSITORY_INIT_SHARED_GROUP;
+
+ else if (!strcmp(shared, "all") || !strcmp(shared, "world") ||
+ !strcmp(shared, "everybody"))
+ return GIT_REPOSITORY_INIT_SHARED_ALL;
+
+ else if (shared[0] == '0') {
+ long val;
+ char *end = NULL;
+ val = strtol(shared + 1, &end, 8);
+ if (end == shared + 1 || *end != 0)
+ usage("invalid octal value for --shared", shared);
+ return (uint32_t)val;
+ }
+
+ else
+ usage("unknown value for --shared", shared);
+
+ return 0;
+}
+
+/* forward declaration of helper to make an empty parent-less commit */
+static void create_initial_commit(git_repository *repo);
+
+
+int main(int argc, char *argv[])
+{
+ git_repository *repo = NULL;
+ int no_options = 1, quiet = 0, bare = 0, initial_commit = 0, i;
+ uint32_t shared = GIT_REPOSITORY_INIT_SHARED_UMASK;
+ const char *template = NULL, *gitdir = NULL, *dir = NULL;
+ size_t pfxlen;
+
+ git_threads_init();
+
+ /* Process arguments */
+
+ for (i = 1; i < argc; ++i) {
+ char *a = argv[i];
+
+ if (a[0] == '-')
+ no_options = 0;
+
+ if (a[0] != '-') {
+ if (dir != NULL)
+ usage("extra argument", a);
+ dir = a;
+ }
+ else if (!strcmp(a, "-q") || !strcmp(a, "--quiet"))
+ quiet = 1;
+ else if (!strcmp(a, "--bare"))
+ bare = 1;
+ else if ((pfxlen = is_prefixed(a, "--template=")) > 0)
+ template = a + pfxlen;
+ else if (!strcmp(a, "--separate-git-dir"))
+ gitdir = argv[++i];
+ else if ((pfxlen = is_prefixed(a, "--separate-git-dir=")) > 0)
+ gitdir = a + pfxlen;
+ else if (!strcmp(a, "--shared"))
+ shared = GIT_REPOSITORY_INIT_SHARED_GROUP;
+ else if ((pfxlen = is_prefixed(a, "--shared=")) > 0)
+ shared = parse_shared(a + pfxlen);
+ else if (!strcmp(a, "--initial-commit"))
+ initial_commit = 1;
+ else
+ usage("unknown option", a);
+ }
+
+ if (!dir)
+ usage("must specify directory to init", NULL);
+
+ /* Initialize repository */
+
+ if (no_options) {
+ /* No options were specified, so let's demonstrate the default
+ * simple case of git_repository_init() API usage...
+ */
+
+ if (git_repository_init(&repo, dir, 0) < 0)
+ fail("Could not initialize repository", dir);
+ }
+ else {
+ /* Some command line options were specified, so we'll use the
+ * extended init API to handle them
+ */
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+
+ if (bare)
+ opts.flags |= GIT_REPOSITORY_INIT_BARE;
+
+ if (template) {
+ opts.flags |= GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
+ opts.template_path = template;
+ }
+
+ if (gitdir) {
+ /* if you specified a separate git directory, then initialize
+ * the repository at that path and use the second path as the
+ * working directory of the repository (with a git-link file)
+ */
+ opts.workdir_path = dir;
+ dir = gitdir;
+ }
+
+ if (shared != 0)
+ opts.mode = shared;
+
+ if (git_repository_init_ext(&repo, dir, &opts) < 0)
+ fail("Could not initialize repository", dir);
+ }
+
+ /* Print a message to stdout like "git init" does */
+
+ if (!quiet) {
+ if (bare || gitdir)
+ dir = git_repository_path(repo);
+ else
+ dir = git_repository_workdir(repo);
+
+ printf("Initialized empty Git repository in %s\n", dir);
+ }
+
+ /* As an extension to the basic "git init" command, this example
+ * gives the option to create an empty initial commit. This is
+ * mostly to demonstrate what it takes to do that, but also some
+ * people like to have that empty base commit in their repo.
+ */
+ if (initial_commit) {
+ create_initial_commit(repo);
+ printf("Created empty initial commit\n");
+ }
+
+ git_repository_free(repo);
+ git_threads_shutdown();
+
+ return 0;
+}
+
+/* Unlike regular "git init", this example shows how to create an initial
+ * empty commit in the repository. This is the helper function that does
+ * that.
+ */
+static void create_initial_commit(git_repository *repo)
+{
+ git_signature *sig;
+ git_index *index;
+ git_oid tree_id, commit_id;
+ git_tree *tree;
+
+ /* First use the config to initialize a commit signature for the user */
+
+ if (git_signature_default(&sig, repo) < 0)
+ fail("Unable to create a commit signature.",
+ "Perhaps 'user.name' and 'user.email' are not set");
+
+ /* Now let's create an empty tree for this commit */
+
+ if (git_repository_index(&index, repo) < 0)
+ fail("Could not open repository index", NULL);
+
+ /* Outside of this example, you could call git_index_add_bypath()
+ * here to put actual files into the index. For our purposes, we'll
+ * leave it empty for now.
+ */
+
+ if (git_index_write_tree(&tree_id, index) < 0)
+ fail("Unable to write initial tree from index", NULL);
+
+ git_index_free(index);
+
+ if (git_tree_lookup(&tree, repo, &tree_id) < 0)
+ fail("Could not look up initial tree", NULL);
+
+ /* Ready to create the initial commit
+ *
+ * Normally creating a commit would involve looking up the current
+ * HEAD commit and making that be the parent of the initial commit,
+ * but here this is the first commit so there will be no parent.
+ */
+
+ if (git_commit_create_v(
+ &commit_id, repo, "HEAD", sig, sig,
+ NULL, "Initial commit", tree, 0) < 0)
+ fail("Could not create the initial commit", NULL);
+
+ /* Clean up so we don't leak memory */
+
+ git_tree_free(tree);
+ git_signature_free(sig);
+}