Linus Torvalds | 8bc9a0c | 2005-04-07 15:16:10 -0700 | [diff] [blame] | 1 | /* |
| 2 | * GIT - The information manager from hell |
| 3 | * |
| 4 | * Copyright (C) Linus Torvalds, 2005 |
| 5 | */ |
Linus Torvalds | e83c516 | 2005-04-07 15:13:13 -0700 | [diff] [blame] | 6 | #include "cache.h" |
| 7 | |
Junio C Hamano | d3af621 | 2005-08-06 12:50:14 -0700 | [diff] [blame] | 8 | #ifndef DEFAULT_GIT_TEMPLATE_DIR |
| 9 | #define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates/" |
| 10 | #endif |
| 11 | |
Johannes Schindelin | af6e277 | 2005-12-22 23:19:37 +0100 | [diff] [blame] | 12 | static void safe_create_dir(const char *dir, int share) |
Zach Welch | cb126d8 | 2005-04-19 21:48:15 -0700 | [diff] [blame] | 13 | { |
Junio C Hamano | f312de0 | 2005-07-06 01:21:46 -0700 | [diff] [blame] | 14 | if (mkdir(dir, 0777) < 0) { |
Zach Welch | cb126d8 | 2005-04-19 21:48:15 -0700 | [diff] [blame] | 15 | if (errno != EEXIST) { |
| 16 | perror(dir); |
| 17 | exit(1); |
| 18 | } |
| 19 | } |
Johannes Schindelin | af6e277 | 2005-12-22 23:19:37 +0100 | [diff] [blame] | 20 | else if (share && adjust_shared_perm(dir)) |
| 21 | die("Could not make %s writable by group\n", dir); |
Zach Welch | cb126d8 | 2005-04-19 21:48:15 -0700 | [diff] [blame] | 22 | } |
| 23 | |
Junio C Hamano | 8d5afef | 2005-08-02 16:45:21 -0700 | [diff] [blame] | 24 | static int copy_file(const char *dst, const char *src, int mode) |
| 25 | { |
Junio C Hamano | 32276c8 | 2005-11-05 11:07:22 -0800 | [diff] [blame] | 26 | int fdi, fdo, status; |
Junio C Hamano | 8d5afef | 2005-08-02 16:45:21 -0700 | [diff] [blame] | 27 | |
| 28 | mode = (mode & 0111) ? 0777 : 0666; |
| 29 | if ((fdi = open(src, O_RDONLY)) < 0) |
| 30 | return fdi; |
| 31 | if ((fdo = open(dst, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) { |
| 32 | close(fdi); |
| 33 | return fdo; |
| 34 | } |
Junio C Hamano | 32276c8 | 2005-11-05 11:07:22 -0800 | [diff] [blame] | 35 | status = copy_fd(fdi, fdo); |
Junio C Hamano | 8d5afef | 2005-08-02 16:45:21 -0700 | [diff] [blame] | 36 | close(fdo); |
Johannes Schindelin | af6e277 | 2005-12-22 23:19:37 +0100 | [diff] [blame] | 37 | |
| 38 | if (!status && adjust_shared_perm(dst)) |
| 39 | return -1; |
| 40 | |
Junio C Hamano | 32276c8 | 2005-11-05 11:07:22 -0800 | [diff] [blame] | 41 | return status; |
Junio C Hamano | 8d5afef | 2005-08-02 16:45:21 -0700 | [diff] [blame] | 42 | } |
| 43 | |
| 44 | static void copy_templates_1(char *path, int baselen, |
| 45 | char *template, int template_baselen, |
| 46 | DIR *dir) |
| 47 | { |
| 48 | struct dirent *de; |
| 49 | |
| 50 | /* Note: if ".git/hooks" file exists in the repository being |
| 51 | * re-initialized, /etc/core-git/templates/hooks/update would |
| 52 | * cause git-init-db to fail here. I think this is sane but |
| 53 | * it means that the set of templates we ship by default, along |
| 54 | * with the way the namespace under .git/ is organized, should |
| 55 | * be really carefully chosen. |
| 56 | */ |
Johannes Schindelin | af6e277 | 2005-12-22 23:19:37 +0100 | [diff] [blame] | 57 | safe_create_dir(path, 1); |
Junio C Hamano | 8d5afef | 2005-08-02 16:45:21 -0700 | [diff] [blame] | 58 | while ((de = readdir(dir)) != NULL) { |
| 59 | struct stat st_git, st_template; |
| 60 | int namelen; |
| 61 | int exists = 0; |
| 62 | |
| 63 | if (de->d_name[0] == '.') |
| 64 | continue; |
| 65 | namelen = strlen(de->d_name); |
| 66 | if ((PATH_MAX <= baselen + namelen) || |
| 67 | (PATH_MAX <= template_baselen + namelen)) |
| 68 | die("insanely long template name %s", de->d_name); |
| 69 | memcpy(path + baselen, de->d_name, namelen+1); |
| 70 | memcpy(template + template_baselen, de->d_name, namelen+1); |
| 71 | if (lstat(path, &st_git)) { |
| 72 | if (errno != ENOENT) |
| 73 | die("cannot stat %s", path); |
| 74 | } |
| 75 | else |
| 76 | exists = 1; |
| 77 | |
| 78 | if (lstat(template, &st_template)) |
| 79 | die("cannot stat template %s", template); |
| 80 | |
| 81 | if (S_ISDIR(st_template.st_mode)) { |
| 82 | DIR *subdir = opendir(template); |
| 83 | int baselen_sub = baselen + namelen; |
| 84 | int template_baselen_sub = template_baselen + namelen; |
| 85 | if (!subdir) |
| 86 | die("cannot opendir %s", template); |
| 87 | path[baselen_sub++] = |
| 88 | template[template_baselen_sub++] = '/'; |
| 89 | path[baselen_sub] = |
| 90 | template[template_baselen_sub] = 0; |
| 91 | copy_templates_1(path, baselen_sub, |
| 92 | template, template_baselen_sub, |
| 93 | subdir); |
| 94 | closedir(subdir); |
| 95 | } |
| 96 | else if (exists) |
| 97 | continue; |
| 98 | else if (S_ISLNK(st_template.st_mode)) { |
| 99 | char lnk[256]; |
| 100 | int len; |
| 101 | len = readlink(template, lnk, sizeof(lnk)); |
| 102 | if (len < 0) |
| 103 | die("cannot readlink %s", template); |
| 104 | if (sizeof(lnk) <= len) |
| 105 | die("insanely long symlink %s", template); |
| 106 | lnk[len] = 0; |
| 107 | if (symlink(lnk, path)) |
| 108 | die("cannot symlink %s %s", lnk, path); |
| 109 | } |
| 110 | else if (S_ISREG(st_template.st_mode)) { |
| 111 | if (copy_file(path, template, st_template.st_mode)) |
| 112 | die("cannot copy %s to %s", template, path); |
| 113 | } |
| 114 | else |
| 115 | error("ignoring template %s", template); |
| 116 | } |
| 117 | } |
| 118 | |
Junio C Hamano | d3af621 | 2005-08-06 12:50:14 -0700 | [diff] [blame] | 119 | static void copy_templates(const char *git_dir, int len, char *template_dir) |
Junio C Hamano | 8d5afef | 2005-08-02 16:45:21 -0700 | [diff] [blame] | 120 | { |
| 121 | char path[PATH_MAX]; |
| 122 | char template_path[PATH_MAX]; |
Junio C Hamano | d3af621 | 2005-08-06 12:50:14 -0700 | [diff] [blame] | 123 | int template_len; |
Junio C Hamano | 8d5afef | 2005-08-02 16:45:21 -0700 | [diff] [blame] | 124 | DIR *dir; |
| 125 | |
Junio C Hamano | 8d5afef | 2005-08-02 16:45:21 -0700 | [diff] [blame] | 126 | if (!template_dir) |
Junio C Hamano | d3af621 | 2005-08-06 12:50:14 -0700 | [diff] [blame] | 127 | template_dir = DEFAULT_GIT_TEMPLATE_DIR; |
Junio C Hamano | 8d5afef | 2005-08-02 16:45:21 -0700 | [diff] [blame] | 128 | strcpy(template_path, template_dir); |
| 129 | template_len = strlen(template_path); |
| 130 | if (template_path[template_len-1] != '/') { |
| 131 | template_path[template_len++] = '/'; |
| 132 | template_path[template_len] = 0; |
| 133 | } |
Junio C Hamano | 8d5afef | 2005-08-02 16:45:21 -0700 | [diff] [blame] | 134 | dir = opendir(template_path); |
Junio C Hamano | d3af621 | 2005-08-06 12:50:14 -0700 | [diff] [blame] | 135 | if (!dir) { |
| 136 | fprintf(stderr, "warning: templates not found %s\n", |
| 137 | template_dir); |
Junio C Hamano | 8d5afef | 2005-08-02 16:45:21 -0700 | [diff] [blame] | 138 | return; |
Junio C Hamano | d3af621 | 2005-08-06 12:50:14 -0700 | [diff] [blame] | 139 | } |
| 140 | |
Junio C Hamano | 4f62953 | 2005-11-25 16:03:56 -0800 | [diff] [blame] | 141 | /* Make sure that template is from the correct vintage */ |
| 142 | strcpy(template_path + template_len, "config"); |
| 143 | repository_format_version = 0; |
| 144 | git_config_from_file(check_repository_format_version, |
| 145 | template_path); |
| 146 | template_path[template_len] = 0; |
| 147 | |
| 148 | if (repository_format_version && |
| 149 | repository_format_version != GIT_REPO_VERSION) { |
| 150 | fprintf(stderr, "warning: not copying templates of " |
| 151 | "a wrong format version %d from '%s'\n", |
| 152 | repository_format_version, |
| 153 | template_dir); |
| 154 | closedir(dir); |
| 155 | return; |
| 156 | } |
| 157 | |
Junio C Hamano | d3af621 | 2005-08-06 12:50:14 -0700 | [diff] [blame] | 158 | memcpy(path, git_dir, len); |
Petr Baudis | 1f961c1 | 2005-09-20 02:19:50 +0200 | [diff] [blame] | 159 | path[len] = 0; |
Junio C Hamano | 8d5afef | 2005-08-02 16:45:21 -0700 | [diff] [blame] | 160 | copy_templates_1(path, len, |
| 161 | template_path, template_len, |
| 162 | dir); |
| 163 | closedir(dir); |
| 164 | } |
| 165 | |
Junio C Hamano | 4f62953 | 2005-11-25 16:03:56 -0800 | [diff] [blame] | 166 | static void create_default_files(const char *git_dir, char *template_path) |
Linus Torvalds | cad88fd | 2005-05-30 10:20:44 -0700 | [diff] [blame] | 167 | { |
| 168 | unsigned len = strlen(git_dir); |
| 169 | static char path[PATH_MAX]; |
Junio C Hamano | 8098a17 | 2005-09-30 14:26:57 -0700 | [diff] [blame] | 170 | unsigned char sha1[20]; |
Junio C Hamano | 4f62953 | 2005-11-25 16:03:56 -0800 | [diff] [blame] | 171 | struct stat st1; |
| 172 | char repo_version_string[10]; |
Linus Torvalds | cad88fd | 2005-05-30 10:20:44 -0700 | [diff] [blame] | 173 | |
| 174 | if (len > sizeof(path)-50) |
| 175 | die("insane git directory %s", git_dir); |
| 176 | memcpy(path, git_dir, len); |
| 177 | |
| 178 | if (len && path[len-1] != '/') |
| 179 | path[len++] = '/'; |
| 180 | |
| 181 | /* |
| 182 | * Create .git/refs/{heads,tags} |
| 183 | */ |
| 184 | strcpy(path + len, "refs"); |
Johannes Schindelin | af6e277 | 2005-12-22 23:19:37 +0100 | [diff] [blame] | 185 | safe_create_dir(path, 1); |
Linus Torvalds | cad88fd | 2005-05-30 10:20:44 -0700 | [diff] [blame] | 186 | strcpy(path + len, "refs/heads"); |
Johannes Schindelin | af6e277 | 2005-12-22 23:19:37 +0100 | [diff] [blame] | 187 | safe_create_dir(path, 1); |
Linus Torvalds | cad88fd | 2005-05-30 10:20:44 -0700 | [diff] [blame] | 188 | strcpy(path + len, "refs/tags"); |
Johannes Schindelin | af6e277 | 2005-12-22 23:19:37 +0100 | [diff] [blame] | 189 | safe_create_dir(path, 1); |
Linus Torvalds | cad88fd | 2005-05-30 10:20:44 -0700 | [diff] [blame] | 190 | |
Junio C Hamano | 4f62953 | 2005-11-25 16:03:56 -0800 | [diff] [blame] | 191 | /* First copy the templates -- we might have the default |
| 192 | * config file there, in which case we would want to read |
| 193 | * from it after installing. |
| 194 | */ |
| 195 | path[len] = 0; |
| 196 | copy_templates(path, len, template_path); |
| 197 | |
| 198 | git_config(git_default_config); |
| 199 | |
Linus Torvalds | cad88fd | 2005-05-30 10:20:44 -0700 | [diff] [blame] | 200 | /* |
| 201 | * Create the default symlink from ".git/HEAD" to the "master" |
Junio C Hamano | 8098a17 | 2005-09-30 14:26:57 -0700 | [diff] [blame] | 202 | * branch, if it does not exist yet. |
Linus Torvalds | cad88fd | 2005-05-30 10:20:44 -0700 | [diff] [blame] | 203 | */ |
| 204 | strcpy(path + len, "HEAD"); |
Junio C Hamano | 8098a17 | 2005-09-30 14:26:57 -0700 | [diff] [blame] | 205 | if (read_ref(path, sha1) < 0) { |
| 206 | if (create_symref(path, "refs/heads/master") < 0) |
Linus Torvalds | cad88fd | 2005-05-30 10:20:44 -0700 | [diff] [blame] | 207 | exit(1); |
Linus Torvalds | cad88fd | 2005-05-30 10:20:44 -0700 | [diff] [blame] | 208 | } |
Junio C Hamano | 4f62953 | 2005-11-25 16:03:56 -0800 | [diff] [blame] | 209 | |
| 210 | /* This forces creation of new config file */ |
| 211 | sprintf(repo_version_string, "%d", GIT_REPO_VERSION); |
| 212 | git_config_set("core.repositoryformatversion", repo_version_string); |
| 213 | |
Junio C Hamano | 8098a17 | 2005-09-30 14:26:57 -0700 | [diff] [blame] | 214 | path[len] = 0; |
Johannes Schindelin | e24317b | 2005-10-26 01:43:03 +0200 | [diff] [blame] | 215 | strcpy(path + len, "config"); |
Johannes Schindelin | e24317b | 2005-10-26 01:43:03 +0200 | [diff] [blame] | 216 | |
Junio C Hamano | 4f62953 | 2005-11-25 16:03:56 -0800 | [diff] [blame] | 217 | /* Check filemode trustability */ |
| 218 | if (!lstat(path, &st1)) { |
| 219 | struct stat st2; |
| 220 | int filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) && |
| 221 | !lstat(path, &st2) && |
| 222 | st1.st_mode != st2.st_mode); |
| 223 | git_config_set("core.filemode", |
| 224 | filemode ? "true" : "false"); |
Johannes Schindelin | e24317b | 2005-10-26 01:43:03 +0200 | [diff] [blame] | 225 | } |
Linus Torvalds | cad88fd | 2005-05-30 10:20:44 -0700 | [diff] [blame] | 226 | } |
| 227 | |
Junio C Hamano | d3af621 | 2005-08-06 12:50:14 -0700 | [diff] [blame] | 228 | static const char init_db_usage[] = |
Johannes Schindelin | af6e277 | 2005-12-22 23:19:37 +0100 | [diff] [blame] | 229 | "git-init-db [--template=<template-directory>] [--shared]"; |
Junio C Hamano | d3af621 | 2005-08-06 12:50:14 -0700 | [diff] [blame] | 230 | |
Zach Welch | 4696cb9 | 2005-04-19 21:48:15 -0700 | [diff] [blame] | 231 | /* |
| 232 | * If you want to, you can share the DB area with any number of branches. |
| 233 | * That has advantages: you can save space by sharing all the SHA1 objects. |
| 234 | * On the other hand, it might just make lookup slower and messier. You |
| 235 | * be the judge. The default case is to have one DB per managed directory. |
| 236 | */ |
Linus Torvalds | e83c516 | 2005-04-07 15:13:13 -0700 | [diff] [blame] | 237 | int main(int argc, char **argv) |
| 238 | { |
Linus Torvalds | cad88fd | 2005-05-30 10:20:44 -0700 | [diff] [blame] | 239 | const char *git_dir; |
Junio C Hamano | d19938a | 2005-05-09 17:57:56 -0700 | [diff] [blame] | 240 | const char *sha1_dir; |
Junio C Hamano | d3af621 | 2005-08-06 12:50:14 -0700 | [diff] [blame] | 241 | char *path, *template_dir = NULL; |
Linus Torvalds | 19b2860 | 2005-04-08 09:59:28 -0700 | [diff] [blame] | 242 | int len, i; |
Linus Torvalds | e83c516 | 2005-04-07 15:13:13 -0700 | [diff] [blame] | 243 | |
Junio C Hamano | d3af621 | 2005-08-06 12:50:14 -0700 | [diff] [blame] | 244 | for (i = 1; i < argc; i++, argv++) { |
| 245 | char *arg = argv[1]; |
Junio C Hamano | 4a62eae | 2005-12-05 22:29:36 -0800 | [diff] [blame] | 246 | if (!strncmp(arg, "--template=", 11)) |
Junio C Hamano | d3af621 | 2005-08-06 12:50:14 -0700 | [diff] [blame] | 247 | template_dir = arg+11; |
Johannes Schindelin | af6e277 | 2005-12-22 23:19:37 +0100 | [diff] [blame] | 248 | else if (!strcmp(arg, "--shared")) |
| 249 | shared_repository = 1; |
Junio C Hamano | d3af621 | 2005-08-06 12:50:14 -0700 | [diff] [blame] | 250 | else |
| 251 | die(init_db_usage); |
| 252 | } |
| 253 | |
Linus Torvalds | cad88fd | 2005-05-30 10:20:44 -0700 | [diff] [blame] | 254 | /* |
| 255 | * Set up the default .git directory contents |
| 256 | */ |
Junio C Hamano | a9ab586 | 2005-09-09 14:48:54 -0700 | [diff] [blame] | 257 | git_dir = getenv(GIT_DIR_ENVIRONMENT); |
Linus Torvalds | cad88fd | 2005-05-30 10:20:44 -0700 | [diff] [blame] | 258 | if (!git_dir) { |
| 259 | git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; |
Zach Welch | addb315 | 2005-04-19 21:48:15 -0700 | [diff] [blame] | 260 | fprintf(stderr, "defaulting to local storage area\n"); |
Linus Torvalds | e83c516 | 2005-04-07 15:13:13 -0700 | [diff] [blame] | 261 | } |
Johannes Schindelin | af6e277 | 2005-12-22 23:19:37 +0100 | [diff] [blame] | 262 | safe_create_dir(git_dir, 0); |
Junio C Hamano | 4f62953 | 2005-11-25 16:03:56 -0800 | [diff] [blame] | 263 | |
| 264 | /* Check to see if the repository version is right. |
| 265 | * Note that a newly created repository does not have |
| 266 | * config file, so this will not fail. What we are catching |
| 267 | * is an attempt to reinitialize new repository with an old tool. |
| 268 | */ |
| 269 | check_repository_format(); |
| 270 | |
Junio C Hamano | d3af621 | 2005-08-06 12:50:14 -0700 | [diff] [blame] | 271 | create_default_files(git_dir, template_dir); |
Linus Torvalds | cad88fd | 2005-05-30 10:20:44 -0700 | [diff] [blame] | 272 | |
| 273 | /* |
| 274 | * And set up the object store. |
| 275 | */ |
| 276 | sha1_dir = get_object_directory(); |
Linus Torvalds | e83c516 | 2005-04-07 15:13:13 -0700 | [diff] [blame] | 277 | len = strlen(sha1_dir); |
Christopher Li | 812666c | 2005-04-26 12:00:58 -0700 | [diff] [blame] | 278 | path = xmalloc(len + 40); |
Linus Torvalds | e83c516 | 2005-04-07 15:13:13 -0700 | [diff] [blame] | 279 | memcpy(path, sha1_dir, len); |
Zach Welch | cb126d8 | 2005-04-19 21:48:15 -0700 | [diff] [blame] | 280 | |
Johannes Schindelin | af6e277 | 2005-12-22 23:19:37 +0100 | [diff] [blame] | 281 | safe_create_dir(sha1_dir, 1); |
Linus Torvalds | f49fb35 | 2005-06-27 18:26:11 -0700 | [diff] [blame] | 282 | strcpy(path+len, "/pack"); |
Johannes Schindelin | af6e277 | 2005-12-22 23:19:37 +0100 | [diff] [blame] | 283 | safe_create_dir(path, 1); |
Junio C Hamano | d57306c | 2005-08-20 02:05:31 -0700 | [diff] [blame] | 284 | strcpy(path+len, "/info"); |
Johannes Schindelin | af6e277 | 2005-12-22 23:19:37 +0100 | [diff] [blame] | 285 | safe_create_dir(path, 1); |
| 286 | |
| 287 | if (shared_repository) |
| 288 | git_config_set("core.sharedRepository", "true"); |
| 289 | |
Linus Torvalds | e83c516 | 2005-04-07 15:13:13 -0700 | [diff] [blame] | 290 | return 0; |
| 291 | } |