]>
Commit | Line | Data |
---|---|---|
d288a700 LT |
1 | #include "cache.h" |
2 | ||
6b5ee137 | 3 | const char *prefix_path(const char *prefix, int len, const char *path) |
f332726e | 4 | { |
6b5ee137 | 5 | const char *orig = path; |
f332726e LT |
6 | for (;;) { |
7 | char c; | |
8 | if (*path != '.') | |
9 | break; | |
10 | c = path[1]; | |
11 | /* "." */ | |
12 | if (!c) { | |
13 | path++; | |
14 | break; | |
15 | } | |
16 | /* "./" */ | |
17 | if (c == '/') { | |
18 | path += 2; | |
19 | continue; | |
20 | } | |
21 | if (c != '.') | |
22 | break; | |
23 | c = path[2]; | |
24 | if (!c) | |
25 | path += 2; | |
26 | else if (c == '/') | |
27 | path += 3; | |
28 | else | |
29 | break; | |
30 | /* ".." and "../" */ | |
31 | /* Remove last component of the prefix */ | |
32 | do { | |
33 | if (!len) | |
34 | die("'%s' is outside repository", orig); | |
35 | len--; | |
36 | } while (len && prefix[len-1] != '/'); | |
37 | continue; | |
38 | } | |
39 | if (len) { | |
40 | int speclen = strlen(path); | |
41 | char *n = xmalloc(speclen + len + 1); | |
42 | ||
43 | memcpy(n, prefix, len); | |
44 | memcpy(n + len, path, speclen+1); | |
45 | path = n; | |
46 | } | |
47 | return path; | |
48 | } | |
49 | ||
6b5ee137 | 50 | const char **get_pathspec(const char *prefix, const char **pathspec) |
d288a700 | 51 | { |
6b5ee137 JH |
52 | const char *entry = *pathspec; |
53 | const char **p; | |
d288a700 LT |
54 | int prefixlen; |
55 | ||
f332726e LT |
56 | if (!prefix && !entry) |
57 | return NULL; | |
d288a700 LT |
58 | |
59 | if (!entry) { | |
60 | static const char *spec[2]; | |
61 | spec[0] = prefix; | |
62 | spec[1] = NULL; | |
63 | return spec; | |
64 | } | |
65 | ||
66 | /* Otherwise we have to re-write the entries.. */ | |
d288a700 | 67 | p = pathspec; |
f332726e | 68 | prefixlen = prefix ? strlen(prefix) : 0; |
d288a700 | 69 | do { |
f332726e | 70 | *p = prefix_path(prefix, prefixlen, entry); |
d288a700 LT |
71 | } while ((entry = *++p) != NULL); |
72 | return (const char **) pathspec; | |
73 | } | |
74 | ||
5f5608bc LT |
75 | /* |
76 | * Test it it looks like we're at the top | |
77 | * level git directory. We want to see a | |
78 | * | |
79 | * - a HEAD symlink and a refs/ directory under ".git" | |
80 | * - either a .git/objects/ directory _or_ the proper | |
81 | * GIT_OBJECT_DIRECTORY environment variable | |
82 | */ | |
83 | static int is_toplevel_directory(void) | |
84 | { | |
85 | struct stat st; | |
86 | ||
87 | return !lstat(".git/HEAD", &st) && | |
88 | S_ISLNK(st.st_mode) && | |
89 | !access(".git/refs/", X_OK) && | |
a9ab586a | 90 | (getenv(DB_ENVIRONMENT) || !access(".git/objects/", X_OK)); |
5f5608bc LT |
91 | } |
92 | ||
d288a700 LT |
93 | const char *setup_git_directory(void) |
94 | { | |
95 | static char cwd[PATH_MAX+1]; | |
96 | int len, offset; | |
97 | ||
98 | /* | |
99 | * If GIT_DIR is set explicitly, we're not going | |
100 | * to do any discovery | |
101 | */ | |
a9ab586a | 102 | if (getenv(GIT_DIR_ENVIRONMENT)) |
d288a700 LT |
103 | return NULL; |
104 | ||
105 | if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/') | |
106 | die("Unable to read current working directory"); | |
107 | ||
108 | offset = len = strlen(cwd); | |
109 | for (;;) { | |
5f5608bc LT |
110 | if (is_toplevel_directory()) |
111 | break; | |
d288a700 LT |
112 | chdir(".."); |
113 | do { | |
114 | if (!offset) | |
115 | die("Not a git repository"); | |
116 | } while (cwd[--offset] != '/'); | |
117 | } | |
118 | ||
119 | if (offset == len) | |
120 | return NULL; | |
121 | ||
122 | /* Make "offset" point to past the '/', and add a '/' at the end */ | |
123 | offset++; | |
124 | cwd[len++] = '/'; | |
125 | cwd[len] = 0; | |
126 | return cwd + offset; | |
127 | } |