diff options
Diffstat (limited to 'builtin/init-db.c')
-rw-r--r-- | builtin/init-db.c | 240 |
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); +} |