]>
Commit | Line | Data |
---|---|---|
16da134b JS |
1 | #include <signal.h> |
2 | #include <sys/time.h> | |
3 | #include "cache.h" | |
4 | #include "tree.h" | |
5 | #include "tree-walk.h" | |
6 | #include "unpack-trees.h" | |
7 | ||
8 | struct tree_entry_list { | |
9 | struct tree_entry_list *next; | |
10 | unsigned directory : 1; | |
11 | unsigned executable : 1; | |
12 | unsigned symlink : 1; | |
13 | unsigned int mode; | |
14 | const char *name; | |
15 | const unsigned char *sha1; | |
16 | }; | |
17 | ||
18 | static struct tree_entry_list *create_tree_entry_list(struct tree *tree) | |
19 | { | |
20 | struct tree_desc desc; | |
21 | struct name_entry one; | |
22 | struct tree_entry_list *ret = NULL; | |
23 | struct tree_entry_list **list_p = &ret; | |
24 | ||
25 | desc.buf = tree->buffer; | |
26 | desc.size = tree->size; | |
27 | ||
28 | while (tree_entry(&desc, &one)) { | |
29 | struct tree_entry_list *entry; | |
30 | ||
31 | entry = xmalloc(sizeof(struct tree_entry_list)); | |
32 | entry->name = one.path; | |
33 | entry->sha1 = one.sha1; | |
34 | entry->mode = one.mode; | |
35 | entry->directory = S_ISDIR(one.mode) != 0; | |
36 | entry->executable = (one.mode & S_IXUSR) != 0; | |
37 | entry->symlink = S_ISLNK(one.mode) != 0; | |
38 | entry->next = NULL; | |
39 | ||
40 | *list_p = entry; | |
41 | list_p = &entry->next; | |
42 | } | |
43 | return ret; | |
44 | } | |
45 | ||
46 | static int entcmp(const char *name1, int dir1, const char *name2, int dir2) | |
47 | { | |
48 | int len1 = strlen(name1); | |
49 | int len2 = strlen(name2); | |
50 | int len = len1 < len2 ? len1 : len2; | |
51 | int ret = memcmp(name1, name2, len); | |
52 | unsigned char c1, c2; | |
53 | if (ret) | |
54 | return ret; | |
55 | c1 = name1[len]; | |
56 | c2 = name2[len]; | |
57 | if (!c1 && dir1) | |
58 | c1 = '/'; | |
59 | if (!c2 && dir2) | |
60 | c2 = '/'; | |
61 | ret = (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0; | |
62 | if (c1 && c2 && !ret) | |
63 | ret = len1 - len2; | |
64 | return ret; | |
65 | } | |
66 | ||
67 | static int unpack_trees_rec(struct tree_entry_list **posns, int len, | |
68 | const char *base, struct unpack_trees_options *o, | |
69 | int *indpos, | |
70 | struct tree_entry_list *df_conflict_list) | |
71 | { | |
72 | int baselen = strlen(base); | |
73 | int src_size = len + 1; | |
74 | do { | |
75 | int i; | |
76 | const char *first; | |
77 | int firstdir = 0; | |
78 | int pathlen; | |
79 | unsigned ce_size; | |
80 | struct tree_entry_list **subposns; | |
81 | struct cache_entry **src; | |
82 | int any_files = 0; | |
83 | int any_dirs = 0; | |
84 | char *cache_name; | |
85 | int ce_stage; | |
86 | ||
87 | /* Find the first name in the input. */ | |
88 | ||
89 | first = NULL; | |
90 | cache_name = NULL; | |
91 | ||
92 | /* Check the cache */ | |
93 | if (o->merge && *indpos < active_nr) { | |
94 | /* This is a bit tricky: */ | |
95 | /* If the index has a subdirectory (with | |
96 | * contents) as the first name, it'll get a | |
97 | * filename like "foo/bar". But that's after | |
98 | * "foo", so the entry in trees will get | |
99 | * handled first, at which point we'll go into | |
100 | * "foo", and deal with "bar" from the index, | |
101 | * because the base will be "foo/". The only | |
102 | * way we can actually have "foo/bar" first of | |
103 | * all the things is if the trees don't | |
104 | * contain "foo" at all, in which case we'll | |
105 | * handle "foo/bar" without going into the | |
106 | * directory, but that's fine (and will return | |
107 | * an error anyway, with the added unknown | |
108 | * file case. | |
109 | */ | |
110 | ||
111 | cache_name = active_cache[*indpos]->name; | |
112 | if (strlen(cache_name) > baselen && | |
113 | !memcmp(cache_name, base, baselen)) { | |
114 | cache_name += baselen; | |
115 | first = cache_name; | |
116 | } else { | |
117 | cache_name = NULL; | |
118 | } | |
119 | } | |
120 | ||
121 | #if DBRT_DEBUG > 1 | |
122 | if (first) | |
123 | printf("index %s\n", first); | |
124 | #endif | |
125 | for (i = 0; i < len; i++) { | |
126 | if (!posns[i] || posns[i] == df_conflict_list) | |
127 | continue; | |
128 | #if DBRT_DEBUG > 1 | |
129 | printf("%d %s\n", i + 1, posns[i]->name); | |
130 | #endif | |
131 | if (!first || entcmp(first, firstdir, | |
132 | posns[i]->name, | |
133 | posns[i]->directory) > 0) { | |
134 | first = posns[i]->name; | |
135 | firstdir = posns[i]->directory; | |
136 | } | |
137 | } | |
138 | /* No name means we're done */ | |
139 | if (!first) | |
140 | return 0; | |
141 | ||
142 | pathlen = strlen(first); | |
143 | ce_size = cache_entry_size(baselen + pathlen); | |
144 | ||
145 | src = xcalloc(src_size, sizeof(struct cache_entry *)); | |
146 | ||
147 | subposns = xcalloc(len, sizeof(struct tree_list_entry *)); | |
148 | ||
149 | if (cache_name && !strcmp(cache_name, first)) { | |
150 | any_files = 1; | |
151 | src[0] = active_cache[*indpos]; | |
152 | remove_cache_entry_at(*indpos); | |
153 | } | |
154 | ||
155 | for (i = 0; i < len; i++) { | |
156 | struct cache_entry *ce; | |
157 | ||
158 | if (!posns[i] || | |
159 | (posns[i] != df_conflict_list && | |
160 | strcmp(first, posns[i]->name))) { | |
161 | continue; | |
162 | } | |
163 | ||
164 | if (posns[i] == df_conflict_list) { | |
165 | src[i + o->merge] = o->df_conflict_entry; | |
166 | continue; | |
167 | } | |
168 | ||
169 | if (posns[i]->directory) { | |
170 | struct tree *tree = lookup_tree(posns[i]->sha1); | |
171 | any_dirs = 1; | |
172 | parse_tree(tree); | |
173 | subposns[i] = create_tree_entry_list(tree); | |
174 | posns[i] = posns[i]->next; | |
175 | src[i + o->merge] = o->df_conflict_entry; | |
176 | continue; | |
177 | } | |
178 | ||
179 | if (!o->merge) | |
180 | ce_stage = 0; | |
181 | else if (i + 1 < o->head_idx) | |
182 | ce_stage = 1; | |
183 | else if (i + 1 > o->head_idx) | |
184 | ce_stage = 3; | |
185 | else | |
186 | ce_stage = 2; | |
187 | ||
188 | ce = xcalloc(1, ce_size); | |
189 | ce->ce_mode = create_ce_mode(posns[i]->mode); | |
190 | ce->ce_flags = create_ce_flags(baselen + pathlen, | |
191 | ce_stage); | |
192 | memcpy(ce->name, base, baselen); | |
193 | memcpy(ce->name + baselen, first, pathlen + 1); | |
194 | ||
195 | any_files = 1; | |
196 | ||
197 | memcpy(ce->sha1, posns[i]->sha1, 20); | |
198 | src[i + o->merge] = ce; | |
199 | subposns[i] = df_conflict_list; | |
200 | posns[i] = posns[i]->next; | |
201 | } | |
202 | if (any_files) { | |
203 | if (o->merge) { | |
204 | int ret; | |
205 | ||
206 | #if DBRT_DEBUG > 1 | |
207 | printf("%s:\n", first); | |
208 | for (i = 0; i < src_size; i++) { | |
209 | printf(" %d ", i); | |
210 | if (src[i]) | |
211 | printf("%s\n", sha1_to_hex(src[i]->sha1)); | |
212 | else | |
213 | printf("\n"); | |
214 | } | |
215 | #endif | |
216 | ret = o->fn(src, o); | |
217 | ||
218 | #if DBRT_DEBUG > 1 | |
219 | printf("Added %d entries\n", ret); | |
220 | #endif | |
221 | *indpos += ret; | |
222 | } else { | |
223 | for (i = 0; i < src_size; i++) { | |
224 | if (src[i]) { | |
225 | add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK); | |
226 | } | |
227 | } | |
228 | } | |
229 | } | |
230 | if (any_dirs) { | |
231 | char *newbase = xmalloc(baselen + 2 + pathlen); | |
232 | memcpy(newbase, base, baselen); | |
233 | memcpy(newbase + baselen, first, pathlen); | |
234 | newbase[baselen + pathlen] = '/'; | |
235 | newbase[baselen + pathlen + 1] = '\0'; | |
236 | if (unpack_trees_rec(subposns, len, newbase, o, | |
237 | indpos, df_conflict_list)) | |
238 | return -1; | |
239 | free(newbase); | |
240 | } | |
241 | free(subposns); | |
242 | free(src); | |
243 | } while (1); | |
244 | } | |
245 | ||
246 | /* Unlink the last component and attempt to remove leading | |
247 | * directories, in case this unlink is the removal of the | |
248 | * last entry in the directory -- empty directories are removed. | |
249 | */ | |
250 | static void unlink_entry(char *name) | |
251 | { | |
252 | char *cp, *prev; | |
253 | ||
254 | if (unlink(name)) | |
255 | return; | |
256 | prev = NULL; | |
257 | while (1) { | |
258 | int status; | |
259 | cp = strrchr(name, '/'); | |
260 | if (prev) | |
261 | *prev = '/'; | |
262 | if (!cp) | |
263 | break; | |
264 | ||
265 | *cp = 0; | |
266 | status = rmdir(name); | |
267 | if (status) { | |
268 | *cp = '/'; | |
269 | break; | |
270 | } | |
271 | prev = cp; | |
272 | } | |
273 | } | |
274 | ||
275 | static volatile int progress_update = 0; | |
276 | ||
277 | static void progress_interval(int signum) | |
278 | { | |
279 | progress_update = 1; | |
280 | } | |
281 | ||
282 | static void setup_progress_signal(void) | |
283 | { | |
284 | struct sigaction sa; | |
285 | struct itimerval v; | |
286 | ||
287 | memset(&sa, 0, sizeof(sa)); | |
288 | sa.sa_handler = progress_interval; | |
289 | sigemptyset(&sa.sa_mask); | |
290 | sa.sa_flags = SA_RESTART; | |
291 | sigaction(SIGALRM, &sa, NULL); | |
292 | ||
293 | v.it_interval.tv_sec = 1; | |
294 | v.it_interval.tv_usec = 0; | |
295 | v.it_value = v.it_interval; | |
296 | setitimer(ITIMER_REAL, &v, NULL); | |
297 | } | |
298 | ||
299 | static struct checkout state; | |
300 | static void check_updates(struct cache_entry **src, int nr, | |
301 | struct unpack_trees_options *o) | |
302 | { | |
303 | unsigned short mask = htons(CE_UPDATE); | |
304 | unsigned last_percent = 200, cnt = 0, total = 0; | |
305 | ||
306 | if (o->update && o->verbose_update) { | |
307 | for (total = cnt = 0; cnt < nr; cnt++) { | |
308 | struct cache_entry *ce = src[cnt]; | |
309 | if (!ce->ce_mode || ce->ce_flags & mask) | |
310 | total++; | |
311 | } | |
312 | ||
313 | /* Don't bother doing this for very small updates */ | |
314 | if (total < 250) | |
315 | total = 0; | |
316 | ||
317 | if (total) { | |
318 | fprintf(stderr, "Checking files out...\n"); | |
319 | setup_progress_signal(); | |
320 | progress_update = 1; | |
321 | } | |
322 | cnt = 0; | |
323 | } | |
324 | ||
325 | while (nr--) { | |
326 | struct cache_entry *ce = *src++; | |
327 | ||
328 | if (total) { | |
329 | if (!ce->ce_mode || ce->ce_flags & mask) { | |
330 | unsigned percent; | |
331 | cnt++; | |
332 | percent = (cnt * 100) / total; | |
333 | if (percent != last_percent || | |
334 | progress_update) { | |
335 | fprintf(stderr, "%4u%% (%u/%u) done\r", | |
336 | percent, cnt, total); | |
337 | last_percent = percent; | |
338 | progress_update = 0; | |
339 | } | |
340 | } | |
341 | } | |
342 | if (!ce->ce_mode) { | |
343 | if (o->update) | |
344 | unlink_entry(ce->name); | |
345 | continue; | |
346 | } | |
347 | if (ce->ce_flags & mask) { | |
348 | ce->ce_flags &= ~mask; | |
349 | if (o->update) | |
350 | checkout_entry(ce, &state, NULL); | |
351 | } | |
352 | } | |
353 | if (total) { | |
354 | signal(SIGALRM, SIG_IGN); | |
355 | fputc('\n', stderr); | |
356 | } | |
357 | } | |
358 | ||
359 | int unpack_trees(struct object_list *trees, struct unpack_trees_options *o) | |
360 | { | |
361 | int indpos = 0; | |
362 | unsigned len = object_list_length(trees); | |
363 | struct tree_entry_list **posns; | |
364 | int i; | |
365 | struct object_list *posn = trees; | |
366 | struct tree_entry_list df_conflict_list; | |
367 | struct cache_entry df_conflict_entry; | |
368 | ||
369 | memset(&df_conflict_list, 0, sizeof(df_conflict_list)); | |
370 | df_conflict_list.next = &df_conflict_list; | |
371 | state.base_dir = ""; | |
372 | state.force = 1; | |
373 | state.quiet = 1; | |
374 | state.refresh_cache = 1; | |
375 | ||
376 | o->merge_size = len; | |
377 | o->df_conflict_entry = &df_conflict_entry; | |
378 | ||
379 | if (len) { | |
380 | posns = xmalloc(len * sizeof(struct tree_entry_list *)); | |
381 | for (i = 0; i < len; i++) { | |
382 | posns[i] = create_tree_entry_list((struct tree *) posn->item); | |
383 | posn = posn->next; | |
384 | } | |
385 | if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "", | |
386 | o, &indpos, &df_conflict_list)) | |
387 | return -1; | |
388 | } | |
389 | ||
390 | if (o->trivial_merges_only && o->nontrivial_merge) | |
391 | die("Merge requires file-level merging"); | |
392 | ||
393 | check_updates(active_cache, active_nr, o); | |
394 | return 0; | |
395 | } |