Support working directory located at root
[git] / setup.c
1 #include "cache.h"
2 #include "dir.h"
3
4 static int inside_git_dir = -1;
5 static int inside_work_tree = -1;
6
7 const char *prefix_path(const char *prefix, int len, const char *path)
8 {
9         const char *orig = path;
10         char *sanitized = xmalloc(len + strlen(path) + 1);
11         if (is_absolute_path(orig))
12                 strcpy(sanitized, path);
13         else {
14                 if (len)
15                         memcpy(sanitized, prefix, len);
16                 strcpy(sanitized + len, path);
17         }
18         if (normalize_path_copy(sanitized, sanitized))
19                 goto error_out;
20         if (is_absolute_path(orig)) {
21                 size_t root_len, len, total;
22                 const char *work_tree = get_git_work_tree();
23                 if (!work_tree)
24                         goto error_out;
25                 len = strlen(work_tree);
26                 root_len = offset_1st_component(work_tree);
27                 total = strlen(sanitized) + 1;
28                 if (strncmp(sanitized, work_tree, len) ||
29                     (len > root_len && sanitized[len] != '\0' && sanitized[len] != '/')) {
30                 error_out:
31                         die("'%s' is outside repository", orig);
32                 }
33                 if (sanitized[len] == '/')
34                         len++;
35                 memmove(sanitized, sanitized + len, total - len);
36         }
37         return sanitized;
38 }
39
40 /*
41  * Unlike prefix_path, this should be used if the named file does
42  * not have to interact with index entry; i.e. name of a random file
43  * on the filesystem.
44  */
45 const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
46 {
47         static char path[PATH_MAX];
48 #ifndef WIN32
49         if (!pfx || !*pfx || is_absolute_path(arg))
50                 return arg;
51         memcpy(path, pfx, pfx_len);
52         strcpy(path + pfx_len, arg);
53 #else
54         char *p;
55         /* don't add prefix to absolute paths, but still replace '\' by '/' */
56         if (is_absolute_path(arg))
57                 pfx_len = 0;
58         else
59                 memcpy(path, pfx, pfx_len);
60         strcpy(path + pfx_len, arg);
61         for (p = path + pfx_len; *p; p++)
62                 if (*p == '\\')
63                         *p = '/';
64 #endif
65         return path;
66 }
67
68 int check_filename(const char *prefix, const char *arg)
69 {
70         const char *name;
71         struct stat st;
72
73         name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
74         if (!lstat(name, &st))
75                 return 1; /* file exists */
76         if (errno == ENOENT || errno == ENOTDIR)
77                 return 0; /* file does not exist */
78         die_errno("failed to stat '%s'", arg);
79 }
80
81 /*
82  * Verify a filename that we got as an argument for a pathspec
83  * entry. Note that a filename that begins with "-" never verifies
84  * as true, because even if such a filename were to exist, we want
85  * it to be preceded by the "--" marker (or we want the user to
86  * use a format like "./-filename")
87  */
88 void verify_filename(const char *prefix, const char *arg)
89 {
90         if (*arg == '-')
91                 die("bad flag '%s' used after filename", arg);
92         if (check_filename(prefix, arg))
93                 return;
94         die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
95             "Use '--' to separate paths from revisions", arg);
96 }
97
98 /*
99  * Opposite of the above: the command line did not have -- marker
100  * and we parsed the arg as a refname.  It should not be interpretable
101  * as a filename.
102  */
103 void verify_non_filename(const char *prefix, const char *arg)
104 {
105         if (!is_inside_work_tree() || is_inside_git_dir())
106                 return;
107         if (*arg == '-')
108                 return; /* flag */
109         if (!check_filename(prefix, arg))
110                 return;
111         die("ambiguous argument '%s': both revision and filename\n"
112             "Use '--' to separate filenames from revisions", arg);
113 }
114
115 const char **get_pathspec(const char *prefix, const char **pathspec)
116 {
117         const char *entry = *pathspec;
118         const char **src, **dst;
119         int prefixlen;
120
121         if (!prefix && !entry)
122                 return NULL;
123
124         if (!entry) {
125                 static const char *spec[2];
126                 spec[0] = prefix;
127                 spec[1] = NULL;
128                 return spec;
129         }
130
131         /* Otherwise we have to re-write the entries.. */
132         src = pathspec;
133         dst = pathspec;
134         prefixlen = prefix ? strlen(prefix) : 0;
135         while (*src) {
136                 const char *p = prefix_path(prefix, prefixlen, *src);
137                 *(dst++) = p;
138                 src++;
139         }
140         *dst = NULL;
141         if (!*pathspec)
142                 return NULL;
143         return pathspec;
144 }
145
146 /*
147  * Test if it looks like we're at a git directory.
148  * We want to see:
149  *
150  *  - either an objects/ directory _or_ the proper
151  *    GIT_OBJECT_DIRECTORY environment variable
152  *  - a refs/ directory
153  *  - either a HEAD symlink or a HEAD file that is formatted as
154  *    a proper "ref:", or a regular file HEAD that has a properly
155  *    formatted sha1 object name.
156  */
157 static int is_git_directory(const char *suspect)
158 {
159         char path[PATH_MAX];
160         size_t len = strlen(suspect);
161
162         strcpy(path, suspect);
163         if (getenv(DB_ENVIRONMENT)) {
164                 if (access(getenv(DB_ENVIRONMENT), X_OK))
165                         return 0;
166         }
167         else {
168                 strcpy(path + len, "/objects");
169                 if (access(path, X_OK))
170                         return 0;
171         }
172
173         strcpy(path + len, "/refs");
174         if (access(path, X_OK))
175                 return 0;
176
177         strcpy(path + len, "/HEAD");
178         if (validate_headref(path))
179                 return 0;
180
181         return 1;
182 }
183
184 int is_inside_git_dir(void)
185 {
186         if (inside_git_dir < 0)
187                 inside_git_dir = is_inside_dir(get_git_dir());
188         return inside_git_dir;
189 }
190
191 int is_inside_work_tree(void)
192 {
193         if (inside_work_tree < 0)
194                 inside_work_tree = is_inside_dir(get_git_work_tree());
195         return inside_work_tree;
196 }
197
198 /*
199  * set_work_tree() is only ever called if you set GIT_DIR explicitely.
200  * The old behaviour (which we retain here) is to set the work tree root
201  * to the cwd, unless overridden by the config, the command line, or
202  * GIT_WORK_TREE.
203  */
204 static const char *set_work_tree(const char *dir)
205 {
206         char buffer[PATH_MAX + 1];
207
208         if (!getcwd(buffer, sizeof(buffer)))
209                 die ("Could not get the current working directory");
210         git_work_tree_cfg = xstrdup(buffer);
211         inside_work_tree = 1;
212
213         return NULL;
214 }
215
216 void setup_work_tree(void)
217 {
218         const char *work_tree, *git_dir;
219         static int initialized = 0;
220
221         if (initialized)
222                 return;
223         work_tree = get_git_work_tree();
224         git_dir = get_git_dir();
225         if (!is_absolute_path(git_dir))
226                 git_dir = make_absolute_path(git_dir);
227         if (!work_tree || chdir(work_tree))
228                 die("This operation must be run in a work tree");
229         set_git_dir(make_relative_path(git_dir, work_tree));
230         initialized = 1;
231 }
232
233 static int check_repository_format_gently(int *nongit_ok)
234 {
235         git_config(check_repository_format_version, NULL);
236         if (GIT_REPO_VERSION < repository_format_version) {
237                 if (!nongit_ok)
238                         die ("Expected git repo version <= %d, found %d",
239                              GIT_REPO_VERSION, repository_format_version);
240                 warning("Expected git repo version <= %d, found %d",
241                         GIT_REPO_VERSION, repository_format_version);
242                 warning("Please upgrade Git");
243                 *nongit_ok = -1;
244                 return -1;
245         }
246         return 0;
247 }
248
249 /*
250  * Try to read the location of the git directory from the .git file,
251  * return path to git directory if found.
252  */
253 const char *read_gitfile_gently(const char *path)
254 {
255         char *buf;
256         struct stat st;
257         int fd;
258         size_t len;
259
260         if (stat(path, &st))
261                 return NULL;
262         if (!S_ISREG(st.st_mode))
263                 return NULL;
264         fd = open(path, O_RDONLY);
265         if (fd < 0)
266                 die_errno("Error opening '%s'", path);
267         buf = xmalloc(st.st_size + 1);
268         len = read_in_full(fd, buf, st.st_size);
269         close(fd);
270         if (len != st.st_size)
271                 die("Error reading %s", path);
272         buf[len] = '\0';
273         if (prefixcmp(buf, "gitdir: "))
274                 die("Invalid gitfile format: %s", path);
275         while (buf[len - 1] == '\n' || buf[len - 1] == '\r')
276                 len--;
277         if (len < 9)
278                 die("No path in gitfile: %s", path);
279         buf[len] = '\0';
280         if (!is_git_directory(buf + 8))
281                 die("Not a git repository: %s", buf + 8);
282         path = make_absolute_path(buf + 8);
283         free(buf);
284         return path;
285 }
286
287 /*
288  * We cannot decide in this function whether we are in the work tree or
289  * not, since the config can only be read _after_ this function was called.
290  */
291 const char *setup_git_directory_gently(int *nongit_ok)
292 {
293         const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
294         const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
295         static char cwd[PATH_MAX+1];
296         const char *gitdirenv;
297         const char *gitfile_dir;
298         int len, offset, ceil_offset, root_len;
299
300         /*
301          * Let's assume that we are in a git repository.
302          * If it turns out later that we are somewhere else, the value will be
303          * updated accordingly.
304          */
305         if (nongit_ok)
306                 *nongit_ok = 0;
307
308         /*
309          * If GIT_DIR is set explicitly, we're not going
310          * to do any discovery, but we still do repository
311          * validation.
312          */
313         gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
314         if (gitdirenv) {
315                 if (PATH_MAX - 40 < strlen(gitdirenv))
316                         die("'$%s' too big", GIT_DIR_ENVIRONMENT);
317                 if (is_git_directory(gitdirenv)) {
318                         static char buffer[1024 + 1];
319                         const char *retval;
320
321                         if (!work_tree_env) {
322                                 retval = set_work_tree(gitdirenv);
323                                 /* config may override worktree */
324                                 if (check_repository_format_gently(nongit_ok))
325                                         return NULL;
326                                 return retval;
327                         }
328                         if (check_repository_format_gently(nongit_ok))
329                                 return NULL;
330                         retval = get_relative_cwd(buffer, sizeof(buffer) - 1,
331                                         get_git_work_tree());
332                         if (!retval || !*retval)
333                                 return NULL;
334                         set_git_dir(make_absolute_path(gitdirenv));
335                         if (chdir(work_tree_env) < 0)
336                                 die_errno ("Could not chdir to '%s'", work_tree_env);
337                         strcat(buffer, "/");
338                         return retval;
339                 }
340                 if (nongit_ok) {
341                         *nongit_ok = 1;
342                         return NULL;
343                 }
344                 die("Not a git repository: '%s'", gitdirenv);
345         }
346
347         if (!getcwd(cwd, sizeof(cwd)-1))
348                 die_errno("Unable to read current working directory");
349
350         ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);
351         if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
352                 ceil_offset = 1;
353
354         /*
355          * Test in the following order (relative to the cwd):
356          * - .git (file containing "gitdir: <path>")
357          * - .git/
358          * - ./ (bare)
359          * - ../.git
360          * - ../.git/
361          * - ../ (bare)
362          * - ../../.git/
363          *   etc.
364          */
365         offset = len = strlen(cwd);
366         for (;;) {
367                 gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
368                 if (gitfile_dir) {
369                         if (set_git_dir(gitfile_dir))
370                                 die("Repository setup failed");
371                         break;
372                 }
373                 if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
374                         break;
375                 if (is_git_directory(".")) {
376                         inside_git_dir = 1;
377                         if (!work_tree_env)
378                                 inside_work_tree = 0;
379                         if (offset != len) {
380                                 root_len = offset_1st_component(cwd);
381                                 cwd[offset > root_len ? offset : root_len] = '\0';
382                                 set_git_dir(cwd);
383                         } else
384                                 set_git_dir(".");
385                         check_repository_format_gently(nongit_ok);
386                         return NULL;
387                 }
388                 while (--offset > ceil_offset && cwd[offset] != '/');
389                 if (offset <= ceil_offset) {
390                         if (nongit_ok) {
391                                 if (chdir(cwd))
392                                         die_errno("Cannot come back to cwd");
393                                 *nongit_ok = 1;
394                                 return NULL;
395                         }
396                         die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT);
397                 }
398                 if (chdir(".."))
399                         die_errno("Cannot change to '%s/..'", cwd);
400         }
401
402         inside_git_dir = 0;
403         if (!work_tree_env)
404                 inside_work_tree = 1;
405         root_len = offset_1st_component(cwd);
406         git_work_tree_cfg = xstrndup(cwd, offset > root_len ? offset : root_len);
407         if (check_repository_format_gently(nongit_ok))
408                 return NULL;
409         if (offset == len)
410                 return NULL;
411
412         /* Make "offset" point to past the '/', and add a '/' at the end */
413         offset++;
414         cwd[len++] = '/';
415         cwd[len] = 0;
416         return cwd + offset;
417 }
418
419 int git_config_perm(const char *var, const char *value)
420 {
421         int i;
422         char *endptr;
423
424         if (value == NULL)
425                 return PERM_GROUP;
426
427         if (!strcmp(value, "umask"))
428                 return PERM_UMASK;
429         if (!strcmp(value, "group"))
430                 return PERM_GROUP;
431         if (!strcmp(value, "all") ||
432             !strcmp(value, "world") ||
433             !strcmp(value, "everybody"))
434                 return PERM_EVERYBODY;
435
436         /* Parse octal numbers */
437         i = strtol(value, &endptr, 8);
438
439         /* If not an octal number, maybe true/false? */
440         if (*endptr != 0)
441                 return git_config_bool(var, value) ? PERM_GROUP : PERM_UMASK;
442
443         /*
444          * Treat values 0, 1 and 2 as compatibility cases, otherwise it is
445          * a chmod value to restrict to.
446          */
447         switch (i) {
448         case PERM_UMASK:               /* 0 */
449                 return PERM_UMASK;
450         case OLD_PERM_GROUP:           /* 1 */
451                 return PERM_GROUP;
452         case OLD_PERM_EVERYBODY:       /* 2 */
453                 return PERM_EVERYBODY;
454         }
455
456         /* A filemode value was given: 0xxx */
457
458         if ((i & 0600) != 0600)
459                 die("Problem with core.sharedRepository filemode value "
460                     "(0%.3o).\nThe owner of files must always have "
461                     "read and write permissions.", i);
462
463         /*
464          * Mask filemode value. Others can not get write permission.
465          * x flags for directories are handled separately.
466          */
467         return -(i & 0666);
468 }
469
470 int check_repository_format_version(const char *var, const char *value, void *cb)
471 {
472         if (strcmp(var, "core.repositoryformatversion") == 0)
473                 repository_format_version = git_config_int(var, value);
474         else if (strcmp(var, "core.sharedrepository") == 0)
475                 shared_repository = git_config_perm(var, value);
476         else if (strcmp(var, "core.bare") == 0) {
477                 is_bare_repository_cfg = git_config_bool(var, value);
478                 if (is_bare_repository_cfg == 1)
479                         inside_work_tree = -1;
480         } else if (strcmp(var, "core.worktree") == 0) {
481                 if (!value)
482                         return config_error_nonbool(var);
483                 free(git_work_tree_cfg);
484                 git_work_tree_cfg = xstrdup(value);
485                 inside_work_tree = -1;
486         }
487         return 0;
488 }
489
490 int check_repository_format(void)
491 {
492         return check_repository_format_gently(NULL);
493 }
494
495 const char *setup_git_directory(void)
496 {
497         const char *retval = setup_git_directory_gently(NULL);
498
499         /* If the work tree is not the default one, recompute prefix */
500         if (inside_work_tree < 0) {
501                 static char buffer[PATH_MAX + 1];
502                 char *rel;
503                 if (retval && chdir(retval))
504                         die_errno ("Could not jump back into original cwd");
505                 rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree());
506                 if (rel && *rel && chdir(get_git_work_tree()))
507                         die_errno ("Could not jump to working directory");
508                 return rel && *rel ? strcat(rel, "/") : NULL;
509         }
510
511         return retval;
512 }