| 1 | /* |
| 2 | * GIT - The information manager from hell |
| 3 | * |
| 4 | * Copyright (C) Linus Torvalds, 2005 |
| 5 | */ |
| 6 | #define USE_THE_REPOSITORY_VARIABLE |
| 7 | #include "builtin.h" |
| 8 | #include "abspath.h" |
| 9 | #include "environment.h" |
| 10 | #include "gettext.h" |
| 11 | #include "parse-options.h" |
| 12 | #include "path.h" |
| 13 | #include "refs.h" |
| 14 | #include "setup.h" |
| 15 | #include "strbuf.h" |
| 16 | |
| 17 | static int guess_repository_type(const char *git_dir) |
| 18 | { |
| 19 | const char *slash; |
| 20 | char *cwd; |
| 21 | int cwd_is_git_dir; |
| 22 | |
| 23 | /* |
| 24 | * "GIT_DIR=. git init" is always bare. |
| 25 | * "GIT_DIR=`pwd` git init" too. |
| 26 | */ |
| 27 | if (!strcmp(".", git_dir)) |
| 28 | return 1; |
| 29 | cwd = xgetcwd(); |
| 30 | cwd_is_git_dir = !strcmp(git_dir, cwd); |
| 31 | free(cwd); |
| 32 | if (cwd_is_git_dir) |
| 33 | return 1; |
| 34 | /* |
| 35 | * "GIT_DIR=.git or GIT_DIR=something/.git is usually not. |
| 36 | */ |
| 37 | if (!strcmp(git_dir, ".git")) |
| 38 | return 0; |
| 39 | slash = strrchr(git_dir, '/'); |
| 40 | if (slash && !strcmp(slash, "/.git")) |
| 41 | return 0; |
| 42 | |
| 43 | /* |
| 44 | * Otherwise it is often bare. At this point |
| 45 | * we are just guessing. |
| 46 | */ |
| 47 | return 1; |
| 48 | } |
| 49 | |
| 50 | static int shared_callback(const struct option *opt, const char *arg, int unset) |
| 51 | { |
| 52 | BUG_ON_OPT_NEG(unset); |
| 53 | *((int *) opt->value) = (arg) ? git_config_perm("arg", arg) : PERM_GROUP; |
| 54 | return 0; |
| 55 | } |
| 56 | |
| 57 | static const char *const init_db_usage[] = { |
| 58 | N_("git init [-q | --quiet] [--bare] [--template=<template-directory>]\n" |
| 59 | " [--separate-git-dir <git-dir>] [--object-format=<format>]\n" |
| 60 | " [--ref-format=<format>]\n" |
| 61 | " [-b <branch-name> | --initial-branch=<branch-name>]\n" |
| 62 | " [--shared[=<permissions>]] [<directory>]"), |
| 63 | NULL |
| 64 | }; |
| 65 | |
| 66 | /* |
| 67 | * If you want to, you can share the DB area with any number of branches. |
| 68 | * That has advantages: you can save space by sharing all the SHA1 objects. |
| 69 | * On the other hand, it might just make lookup slower and messier. You |
| 70 | * be the judge. The default case is to have one DB per managed directory. |
| 71 | */ |
| 72 | int cmd_init_db(int argc, |
| 73 | const char **argv, |
| 74 | const char *prefix, |
| 75 | struct repository *repo UNUSED) |
| 76 | { |
| 77 | char *git_dir; |
| 78 | const char *real_git_dir = NULL; |
| 79 | char *real_git_dir_to_free = NULL; |
| 80 | char *work_tree = NULL; |
| 81 | const char *template_dir = NULL; |
| 82 | char *template_dir_to_free = NULL; |
| 83 | unsigned int flags = 0; |
| 84 | int bare = startup_info->force_bare_repository ? 1 : -1; |
| 85 | const char *object_format = NULL; |
| 86 | const char *ref_format = NULL; |
| 87 | const char *initial_branch = NULL; |
| 88 | int hash_algo = GIT_HASH_UNKNOWN; |
| 89 | enum ref_storage_format ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN; |
| 90 | int init_shared_repository = -1; |
| 91 | const struct option init_db_options[] = { |
| 92 | OPT_STRING(0, "template", &template_dir, N_("template-directory"), |
| 93 | N_("directory from which templates will be used")), |
| 94 | OPT_SET_INT(0, "bare", &bare, |
| 95 | N_("create a bare repository"), 1), |
| 96 | { |
| 97 | .type = OPTION_CALLBACK, |
| 98 | .long_name = "shared", |
| 99 | .value = &init_shared_repository, |
| 100 | .argh = N_("permissions"), |
| 101 | .help = N_("specify that the git repository is to be shared amongst several users"), |
| 102 | .flags = PARSE_OPT_OPTARG | PARSE_OPT_NONEG, |
| 103 | .callback = shared_callback |
| 104 | }, |
| 105 | OPT_BIT('q', "quiet", &flags, N_("be quiet"), INIT_DB_QUIET), |
| 106 | OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), |
| 107 | N_("separate git dir from working tree")), |
| 108 | OPT_STRING('b', "initial-branch", &initial_branch, N_("name"), |
| 109 | N_("override the name of the initial branch")), |
| 110 | OPT_STRING(0, "object-format", &object_format, N_("hash"), |
| 111 | N_("specify the hash algorithm to use")), |
| 112 | OPT_STRING(0, "ref-format", &ref_format, N_("format"), |
| 113 | N_("specify the reference format to use")), |
| 114 | OPT_END() |
| 115 | }; |
| 116 | int ret; |
| 117 | |
| 118 | argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0); |
| 119 | |
| 120 | if (real_git_dir && bare == 1) |
| 121 | die(_("options '%s' and '%s' cannot be used together"), "--separate-git-dir", "--bare"); |
| 122 | |
| 123 | if (real_git_dir && !is_absolute_path(real_git_dir)) |
| 124 | real_git_dir = real_git_dir_to_free = real_pathdup(real_git_dir, 1); |
| 125 | |
| 126 | if (template_dir && *template_dir && !is_absolute_path(template_dir)) |
| 127 | template_dir = template_dir_to_free = absolute_pathdup(template_dir); |
| 128 | |
| 129 | if (argc == 1) { |
| 130 | int mkdir_tried = 0; |
| 131 | retry: |
| 132 | if (chdir(argv[0]) < 0) { |
| 133 | if (!mkdir_tried) { |
| 134 | int saved; |
| 135 | /* |
| 136 | * At this point we haven't read any configuration, |
| 137 | * and we know shared_repository should always be 0; |
| 138 | * but just in case we play safe. |
| 139 | */ |
| 140 | saved = repo_settings_get_shared_repository(the_repository); |
| 141 | repo_settings_set_shared_repository(the_repository, 0); |
| 142 | switch (safe_create_leading_directories_const(the_repository, argv[0])) { |
| 143 | case SCLD_OK: |
| 144 | case SCLD_PERMS: |
| 145 | break; |
| 146 | case SCLD_EXISTS: |
| 147 | errno = EEXIST; |
| 148 | /* fallthru */ |
| 149 | default: |
| 150 | die_errno(_("cannot mkdir %s"), argv[0]); |
| 151 | break; |
| 152 | } |
| 153 | repo_settings_set_shared_repository(the_repository, saved); |
| 154 | if (mkdir(argv[0], 0777) < 0) |
| 155 | die_errno(_("cannot mkdir %s"), argv[0]); |
| 156 | mkdir_tried = 1; |
| 157 | goto retry; |
| 158 | } |
| 159 | die_errno(_("cannot chdir to %s"), argv[0]); |
| 160 | } |
| 161 | } else if (0 < argc) { |
| 162 | usage(init_db_usage[0]); |
| 163 | } |
| 164 | if (bare == 1) { |
| 165 | char *cwd = xgetcwd(); |
| 166 | setenv(GIT_DIR_ENVIRONMENT, cwd, argc > 0); |
| 167 | free(cwd); |
| 168 | } |
| 169 | |
| 170 | if (object_format) { |
| 171 | hash_algo = hash_algo_by_name(object_format); |
| 172 | if (hash_algo == GIT_HASH_UNKNOWN) |
| 173 | die(_("unknown hash algorithm '%s'"), object_format); |
| 174 | } |
| 175 | |
| 176 | if (ref_format) { |
| 177 | ref_storage_format = ref_storage_format_by_name(ref_format); |
| 178 | if (ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN) |
| 179 | die(_("unknown ref storage format '%s'"), ref_format); |
| 180 | } |
| 181 | |
| 182 | if (init_shared_repository != -1) |
| 183 | repo_settings_set_shared_repository(the_repository, init_shared_repository); |
| 184 | |
| 185 | /* |
| 186 | * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR |
| 187 | * without --bare. Catch the error early. |
| 188 | */ |
| 189 | git_dir = xstrdup_or_null(getenv(GIT_DIR_ENVIRONMENT)); |
| 190 | work_tree = xstrdup_or_null(getenv(GIT_WORK_TREE_ENVIRONMENT)); |
| 191 | if ((!git_dir || bare == 1) && work_tree) |
| 192 | die(_("%s (or --work-tree=<directory>) not allowed without " |
| 193 | "specifying %s (or --git-dir=<directory>)"), |
| 194 | GIT_WORK_TREE_ENVIRONMENT, |
| 195 | GIT_DIR_ENVIRONMENT); |
| 196 | |
| 197 | /* |
| 198 | * Set up the default .git directory contents |
| 199 | */ |
| 200 | if (!git_dir) |
| 201 | git_dir = xstrdup(DEFAULT_GIT_DIR_ENVIRONMENT); |
| 202 | |
| 203 | /* |
| 204 | * When --separate-git-dir is used inside a linked worktree, take |
| 205 | * care to ensure that the common .git/ directory is relocated, not |
| 206 | * the worktree-specific .git/worktrees/<id>/ directory. |
| 207 | */ |
| 208 | if (real_git_dir) { |
| 209 | int err; |
| 210 | const char *p; |
| 211 | struct strbuf sb = STRBUF_INIT; |
| 212 | |
| 213 | p = read_gitfile_gently(git_dir, &err); |
| 214 | if (p && get_common_dir(&sb, p)) { |
| 215 | struct strbuf mainwt = STRBUF_INIT; |
| 216 | |
| 217 | strbuf_addbuf(&mainwt, &sb); |
| 218 | strbuf_strip_suffix(&mainwt, "/.git"); |
| 219 | if (chdir(mainwt.buf) < 0) |
| 220 | die_errno(_("cannot chdir to %s"), mainwt.buf); |
| 221 | strbuf_release(&mainwt); |
| 222 | free(git_dir); |
| 223 | git_dir = strbuf_detach(&sb, NULL); |
| 224 | } |
| 225 | strbuf_release(&sb); |
| 226 | } |
| 227 | |
| 228 | if (bare < 0) |
| 229 | bare = guess_repository_type(git_dir); |
| 230 | |
| 231 | if (!bare) { |
| 232 | const char *git_dir_parent = strrchr(git_dir, '/'); |
| 233 | |
| 234 | if (!work_tree) { |
| 235 | if (git_dir_parent) { |
| 236 | char *rel = xstrndup(git_dir, git_dir_parent - git_dir); |
| 237 | work_tree = real_pathdup(rel, 1); |
| 238 | free(rel); |
| 239 | } else { |
| 240 | work_tree = xgetcwd(); |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | if (access(work_tree, X_OK)) |
| 245 | die_errno (_("Cannot access work tree '%s'"), work_tree); |
| 246 | } else if (real_git_dir) { |
| 247 | die(_("--separate-git-dir incompatible with bare repository")); |
| 248 | } |
| 249 | |
| 250 | flags |= INIT_DB_EXIST_OK; |
| 251 | ret = init_db(the_repository, git_dir, real_git_dir, work_tree, |
| 252 | template_dir, hash_algo, ref_storage_format, initial_branch, |
| 253 | init_shared_repository, flags); |
| 254 | |
| 255 | free(template_dir_to_free); |
| 256 | free(real_git_dir_to_free); |
| 257 | free(work_tree); |
| 258 | free(git_dir); |
| 259 | return ret; |
| 260 | } |