]>
Commit | Line | Data |
---|---|---|
1 | #include "cache.h" | |
2 | #include "blob.h" | |
3 | ||
4 | static void create_directories(const char *path, struct checkout *state) | |
5 | { | |
6 | int len = strlen(path); | |
7 | char *buf = xmalloc(len + 1); | |
8 | const char *slash = path; | |
9 | ||
10 | while ((slash = strchr(slash+1, '/')) != NULL) { | |
11 | len = slash - path; | |
12 | memcpy(buf, path, len); | |
13 | buf[len] = 0; | |
14 | if (mkdir(buf, 0777)) { | |
15 | if (errno == EEXIST) { | |
16 | struct stat st; | |
17 | if (len > state->base_dir_len && state->force && !unlink(buf) && !mkdir(buf, 0777)) | |
18 | continue; | |
19 | if (!stat(buf, &st) && S_ISDIR(st.st_mode)) | |
20 | continue; /* ok */ | |
21 | } | |
22 | die("cannot create directory at %s", buf); | |
23 | } | |
24 | } | |
25 | free(buf); | |
26 | } | |
27 | ||
28 | static void remove_subtree(const char *path) | |
29 | { | |
30 | DIR *dir = opendir(path); | |
31 | struct dirent *de; | |
32 | char pathbuf[PATH_MAX]; | |
33 | char *name; | |
34 | ||
35 | if (!dir) | |
36 | die("cannot opendir %s", path); | |
37 | strcpy(pathbuf, path); | |
38 | name = pathbuf + strlen(path); | |
39 | *name++ = '/'; | |
40 | while ((de = readdir(dir)) != NULL) { | |
41 | struct stat st; | |
42 | if ((de->d_name[0] == '.') && | |
43 | ((de->d_name[1] == 0) || | |
44 | ((de->d_name[1] == '.') && de->d_name[2] == 0))) | |
45 | continue; | |
46 | strcpy(name, de->d_name); | |
47 | if (lstat(pathbuf, &st)) | |
48 | die("cannot lstat %s", pathbuf); | |
49 | if (S_ISDIR(st.st_mode)) | |
50 | remove_subtree(pathbuf); | |
51 | else if (unlink(pathbuf)) | |
52 | die("cannot unlink %s", pathbuf); | |
53 | } | |
54 | closedir(dir); | |
55 | if (rmdir(path)) | |
56 | die("cannot rmdir %s", path); | |
57 | } | |
58 | ||
59 | static int create_file(const char *path, unsigned int mode) | |
60 | { | |
61 | mode = (mode & 0100) ? 0777 : 0666; | |
62 | return open(path, O_WRONLY | O_CREAT | O_EXCL, mode); | |
63 | } | |
64 | ||
65 | static int write_entry(struct cache_entry *ce, char *path, struct checkout *state, int to_tempfile) | |
66 | { | |
67 | int fd; | |
68 | void *new; | |
69 | unsigned long size; | |
70 | long wrote; | |
71 | char type[20]; | |
72 | ||
73 | new = read_sha1_file(ce->sha1, type, &size); | |
74 | if (!new || strcmp(type, blob_type)) { | |
75 | if (new) | |
76 | free(new); | |
77 | return error("git-checkout-index: unable to read sha1 file of %s (%s)", | |
78 | path, sha1_to_hex(ce->sha1)); | |
79 | } | |
80 | switch (ntohl(ce->ce_mode) & S_IFMT) { | |
81 | case S_IFREG: | |
82 | if (to_tempfile) { | |
83 | strcpy(path, ".merge_file_XXXXXX"); | |
84 | fd = mkstemp(path); | |
85 | } else | |
86 | fd = create_file(path, ntohl(ce->ce_mode)); | |
87 | if (fd < 0) { | |
88 | free(new); | |
89 | return error("git-checkout-index: unable to create file %s (%s)", | |
90 | path, strerror(errno)); | |
91 | } | |
92 | /* FIXME: LF -> CRLF conversion goes here, based on "ce->name" */ | |
93 | wrote = write_in_full(fd, new, size); | |
94 | close(fd); | |
95 | free(new); | |
96 | if (wrote != size) | |
97 | return error("git-checkout-index: unable to write file %s", path); | |
98 | break; | |
99 | case S_IFLNK: | |
100 | if (to_tempfile) { | |
101 | strcpy(path, ".merge_link_XXXXXX"); | |
102 | fd = mkstemp(path); | |
103 | if (fd < 0) { | |
104 | free(new); | |
105 | return error("git-checkout-index: unable to create " | |
106 | "file %s (%s)", path, strerror(errno)); | |
107 | } | |
108 | wrote = write_in_full(fd, new, size); | |
109 | close(fd); | |
110 | free(new); | |
111 | if (wrote != size) | |
112 | return error("git-checkout-index: unable to write file %s", | |
113 | path); | |
114 | } else { | |
115 | wrote = symlink(new, path); | |
116 | free(new); | |
117 | if (wrote) | |
118 | return error("git-checkout-index: unable to create " | |
119 | "symlink %s (%s)", path, strerror(errno)); | |
120 | } | |
121 | break; | |
122 | default: | |
123 | free(new); | |
124 | return error("git-checkout-index: unknown file mode for %s", path); | |
125 | } | |
126 | ||
127 | if (state->refresh_cache) { | |
128 | struct stat st; | |
129 | lstat(ce->name, &st); | |
130 | fill_stat_cache_info(ce, &st); | |
131 | } | |
132 | return 0; | |
133 | } | |
134 | ||
135 | int checkout_entry(struct cache_entry *ce, struct checkout *state, char *topath) | |
136 | { | |
137 | static char path[PATH_MAX + 1]; | |
138 | struct stat st; | |
139 | int len = state->base_dir_len; | |
140 | ||
141 | if (topath) | |
142 | return write_entry(ce, topath, state, 1); | |
143 | ||
144 | memcpy(path, state->base_dir, len); | |
145 | strcpy(path + len, ce->name); | |
146 | ||
147 | if (!lstat(path, &st)) { | |
148 | unsigned changed = ce_match_stat(ce, &st, 1); | |
149 | if (!changed) | |
150 | return 0; | |
151 | if (!state->force) { | |
152 | if (!state->quiet) | |
153 | fprintf(stderr, "git-checkout-index: %s already exists\n", path); | |
154 | return -1; | |
155 | } | |
156 | ||
157 | /* | |
158 | * We unlink the old file, to get the new one with the | |
159 | * right permissions (including umask, which is nasty | |
160 | * to emulate by hand - much easier to let the system | |
161 | * just do the right thing) | |
162 | */ | |
163 | unlink(path); | |
164 | if (S_ISDIR(st.st_mode)) { | |
165 | if (!state->force) | |
166 | return error("%s is a directory", path); | |
167 | remove_subtree(path); | |
168 | } | |
169 | } else if (state->not_new) | |
170 | return 0; | |
171 | create_directories(path, state); | |
172 | return write_entry(ce, path, state, 0); | |
173 | } |