/* * 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 #include #include #include #include /* 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=] [--shared[=perms]] \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); }