summaryrefslogtreecommitdiffstats
path: root/builtin/init-db.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/init-db.c')
-rw-r--r--builtin/init-db.c240
1 files changed, 240 insertions, 0 deletions
diff --git a/builtin/init-db.c b/builtin/init-db.c
new file mode 100644
index 0000000..cb727c8
--- /dev/null
+++ b/builtin/init-db.c
@@ -0,0 +1,240 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "builtin.h"
+#include "abspath.h"
+#include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "object-file.h"
+#include "parse-options.h"
+#include "path.h"
+#include "setup.h"
+#include "strbuf.h"
+
+static int guess_repository_type(const char *git_dir)
+{
+ const char *slash;
+ char *cwd;
+ int cwd_is_git_dir;
+
+ /*
+ * "GIT_DIR=. git init" is always bare.
+ * "GIT_DIR=`pwd` git init" too.
+ */
+ if (!strcmp(".", git_dir))
+ return 1;
+ cwd = xgetcwd();
+ cwd_is_git_dir = !strcmp(git_dir, cwd);
+ free(cwd);
+ if (cwd_is_git_dir)
+ return 1;
+ /*
+ * "GIT_DIR=.git or GIT_DIR=something/.git is usually not.
+ */
+ if (!strcmp(git_dir, ".git"))
+ return 0;
+ slash = strrchr(git_dir, '/');
+ if (slash && !strcmp(slash, "/.git"))
+ return 0;
+
+ /*
+ * Otherwise it is often bare. At this point
+ * we are just guessing.
+ */
+ return 1;
+}
+
+static int shared_callback(const struct option *opt, const char *arg, int unset)
+{
+ BUG_ON_OPT_NEG(unset);
+ *((int *) opt->value) = (arg) ? git_config_perm("arg", arg) : PERM_GROUP;
+ return 0;
+}
+
+static const char *const init_db_usage[] = {
+ N_("git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+ " [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+ " [-b <branch-name> | --initial-branch=<branch-name>]\n"
+ " [--shared[=<permissions>]] [<directory>]"),
+ NULL
+};
+
+/*
+ * If you want to, you can share the DB area with any number of branches.
+ * That has advantages: you can save space by sharing all the SHA1 objects.
+ * On the other hand, it might just make lookup slower and messier. You
+ * be the judge. The default case is to have one DB per managed directory.
+ */
+int cmd_init_db(int argc, const char **argv, const char *prefix)
+{
+ const char *git_dir;
+ const char *real_git_dir = NULL;
+ const char *work_tree;
+ const char *template_dir = NULL;
+ unsigned int flags = 0;
+ const char *object_format = NULL;
+ const char *initial_branch = NULL;
+ int hash_algo = GIT_HASH_UNKNOWN;
+ int init_shared_repository = -1;
+ const struct option init_db_options[] = {
+ OPT_STRING(0, "template", &template_dir, N_("template-directory"),
+ N_("directory from which templates will be used")),
+ OPT_SET_INT(0, "bare", &is_bare_repository_cfg,
+ N_("create a bare repository"), 1),
+ { OPTION_CALLBACK, 0, "shared", &init_shared_repository,
+ N_("permissions"),
+ N_("specify that the git repository is to be shared amongst several users"),
+ PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
+ OPT_BIT('q', "quiet", &flags, N_("be quiet"), INIT_DB_QUIET),
+ OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
+ N_("separate git dir from working tree")),
+ OPT_STRING('b', "initial-branch", &initial_branch, N_("name"),
+ N_("override the name of the initial branch")),
+ OPT_STRING(0, "object-format", &object_format, N_("hash"),
+ N_("specify the hash algorithm to use")),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
+
+ if (real_git_dir && is_bare_repository_cfg == 1)
+ die(_("options '%s' and '%s' cannot be used together"), "--separate-git-dir", "--bare");
+
+ if (real_git_dir && !is_absolute_path(real_git_dir))
+ real_git_dir = real_pathdup(real_git_dir, 1);
+
+ if (template_dir && *template_dir && !is_absolute_path(template_dir)) {
+ template_dir = absolute_pathdup(template_dir);
+ UNLEAK(template_dir);
+ }
+
+ if (argc == 1) {
+ int mkdir_tried = 0;
+ retry:
+ if (chdir(argv[0]) < 0) {
+ if (!mkdir_tried) {
+ int saved;
+ /*
+ * At this point we haven't read any configuration,
+ * and we know shared_repository should always be 0;
+ * but just in case we play safe.
+ */
+ saved = get_shared_repository();
+ set_shared_repository(0);
+ switch (safe_create_leading_directories_const(argv[0])) {
+ case SCLD_OK:
+ case SCLD_PERMS:
+ break;
+ case SCLD_EXISTS:
+ errno = EEXIST;
+ /* fallthru */
+ default:
+ die_errno(_("cannot mkdir %s"), argv[0]);
+ break;
+ }
+ set_shared_repository(saved);
+ if (mkdir(argv[0], 0777) < 0)
+ die_errno(_("cannot mkdir %s"), argv[0]);
+ mkdir_tried = 1;
+ goto retry;
+ }
+ die_errno(_("cannot chdir to %s"), argv[0]);
+ }
+ } else if (0 < argc) {
+ usage(init_db_usage[0]);
+ }
+ if (is_bare_repository_cfg == 1) {
+ char *cwd = xgetcwd();
+ setenv(GIT_DIR_ENVIRONMENT, cwd, argc > 0);
+ free(cwd);
+ }
+
+ if (object_format) {
+ hash_algo = hash_algo_by_name(object_format);
+ if (hash_algo == GIT_HASH_UNKNOWN)
+ die(_("unknown hash algorithm '%s'"), object_format);
+ }
+
+ if (init_shared_repository != -1)
+ set_shared_repository(init_shared_repository);
+
+ /*
+ * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
+ * without --bare. Catch the error early.
+ */
+ git_dir = xstrdup_or_null(getenv(GIT_DIR_ENVIRONMENT));
+ work_tree = xstrdup_or_null(getenv(GIT_WORK_TREE_ENVIRONMENT));
+ if ((!git_dir || is_bare_repository_cfg == 1) && work_tree)
+ die(_("%s (or --work-tree=<directory>) not allowed without "
+ "specifying %s (or --git-dir=<directory>)"),
+ GIT_WORK_TREE_ENVIRONMENT,
+ GIT_DIR_ENVIRONMENT);
+
+ /*
+ * Set up the default .git directory contents
+ */
+ if (!git_dir)
+ git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
+
+ /*
+ * When --separate-git-dir is used inside a linked worktree, take
+ * care to ensure that the common .git/ directory is relocated, not
+ * the worktree-specific .git/worktrees/<id>/ directory.
+ */
+ if (real_git_dir) {
+ int err;
+ const char *p;
+ struct strbuf sb = STRBUF_INIT;
+
+ p = read_gitfile_gently(git_dir, &err);
+ if (p && get_common_dir(&sb, p)) {
+ struct strbuf mainwt = STRBUF_INIT;
+
+ strbuf_addbuf(&mainwt, &sb);
+ strbuf_strip_suffix(&mainwt, "/.git");
+ if (chdir(mainwt.buf) < 0)
+ die_errno(_("cannot chdir to %s"), mainwt.buf);
+ strbuf_release(&mainwt);
+ git_dir = strbuf_detach(&sb, NULL);
+ }
+ strbuf_release(&sb);
+ }
+
+ if (is_bare_repository_cfg < 0)
+ is_bare_repository_cfg = guess_repository_type(git_dir);
+
+ if (!is_bare_repository_cfg) {
+ const char *git_dir_parent = strrchr(git_dir, '/');
+ if (git_dir_parent) {
+ char *rel = xstrndup(git_dir, git_dir_parent - git_dir);
+ git_work_tree_cfg = real_pathdup(rel, 1);
+ free(rel);
+ }
+ if (!git_work_tree_cfg)
+ git_work_tree_cfg = xgetcwd();
+ if (work_tree)
+ set_git_work_tree(work_tree);
+ else
+ set_git_work_tree(git_work_tree_cfg);
+ if (access(get_git_work_tree(), X_OK))
+ die_errno (_("Cannot access work tree '%s'"),
+ get_git_work_tree());
+ }
+ else {
+ if (real_git_dir)
+ die(_("--separate-git-dir incompatible with bare repository"));
+ if (work_tree)
+ set_git_work_tree(work_tree);
+ }
+
+ UNLEAK(real_git_dir);
+ UNLEAK(git_dir);
+ UNLEAK(work_tree);
+
+ flags |= INIT_DB_EXIST_OK;
+ return init_db(git_dir, real_git_dir, template_dir, hash_algo,
+ initial_branch, init_shared_repository, flags);
+}