@matheusbsbs / matheus-bagliano / commits / 01904572a5
Fork 0

Move 'unpack_trees()' over to 'traverse_trees()' interface

This not only deletes more code than it adds, it gets rid of a singularly hard-to-understand function (unpack_trees_rec()), and replaces it with a set of smaller and simpler functions that use the generic tree traversal mechanism to walk over one or more git trees in parallel. It's still not the most wonderful interface, and by no means is the new code easy to understand either, but it's at least a bit less opaque. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Linus Torvalds committed Mar 5, 2008 at 20:15 UTC 01904572a5cf869983fab454f552542f16b1fe1f
1 file changed +249 -281
unpack-trees.c
+249 -281
@@ -7,270 +7,12 @@
7 #include "progress.h"
8 #include "refs.h"
9
10 -#define DBRT_DEBUG 1
11 -
12 -struct tree_entry_list {
13 - struct tree_entry_list *next;
14 - unsigned int mode;
15 - const char *name;
16 - const unsigned char *sha1;
17 -};
18 -
19 -static struct tree_entry_list *create_tree_entry_list(struct tree_desc *desc)
20 -{
21 - struct name_entry one;
22 - struct tree_entry_list *ret = NULL;
23 - struct tree_entry_list **list_p = &ret;
24 -
25 - while (tree_entry(desc, &one)) {
26 - struct tree_entry_list *entry;
27 -
28 - entry = xmalloc(sizeof(struct tree_entry_list));
29 - entry->name = one.path;
30 - entry->sha1 = one.sha1;
31 - entry->mode = one.mode;
32 - entry->next = NULL;
33 -
34 - *list_p = entry;
35 - list_p = &entry->next;
36 - }
37 - return ret;
38 -}
39 -
40 -static int entcmp(const char *name1, int dir1, const char *name2, int dir2)
41 -{
42 - int len1 = strlen(name1);
43 - int len2 = strlen(name2);
44 - int len = len1 < len2 ? len1 : len2;
45 - int ret = memcmp(name1, name2, len);
46 - unsigned char c1, c2;
47 - if (ret)
48 - return ret;
49 - c1 = name1[len];
50 - c2 = name2[len];
51 - if (!c1 && dir1)
52 - c1 = '/';
53 - if (!c2 && dir2)
54 - c2 = '/';
55 - ret = (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
56 - if (c1 && c2 && !ret)
57 - ret = len1 - len2;
58 - return ret;
59 -}
60 -
10 static inline void remove_entry(int remove)
11 {
12 if (remove >= 0)
13 remove_cache_entry_at(remove);
14 }
15
67 -static int unpack_trees_rec(struct tree_entry_list **posns, int len,
68 - const char *base, struct unpack_trees_options *o,
69 - struct tree_entry_list *df_conflict_list)
70 -{
71 - int remove;
72 - int baselen = strlen(base);
73 - int src_size = len + 1;
74 - int retval = 0;
75 -
76 - do {
77 - int i;
78 - const char *first;
79 - int firstdir = 0;
80 - int pathlen;
81 - unsigned ce_size;
82 - struct tree_entry_list **subposns;
83 - struct cache_entry **src;
84 - int any_files = 0;
85 - int any_dirs = 0;
86 - char *cache_name;
87 - int ce_stage;
88 - int skip_entry = 0;
89 -
90 - /* Find the first name in the input. */
91 -
92 - first = NULL;
93 - cache_name = NULL;
94 -
95 - /* Check the cache */
96 - if (o->merge && o->pos < active_nr) {
97 - /* This is a bit tricky: */
98 - /* If the index has a subdirectory (with
99 - * contents) as the first name, it'll get a
100 - * filename like "foo/bar". But that's after
101 - * "foo", so the entry in trees will get
102 - * handled first, at which point we'll go into
103 - * "foo", and deal with "bar" from the index,
104 - * because the base will be "foo/". The only
105 - * way we can actually have "foo/bar" first of
106 - * all the things is if the trees don't
107 - * contain "foo" at all, in which case we'll
108 - * handle "foo/bar" without going into the
109 - * directory, but that's fine (and will return
110 - * an error anyway, with the added unknown
111 - * file case.
112 - */
113 -
114 - cache_name = active_cache[o->pos]->name;
115 - if (strlen(cache_name) > baselen &&
116 - !memcmp(cache_name, base, baselen)) {
117 - cache_name += baselen;
118 - first = cache_name;
119 - } else {
120 - cache_name = NULL;
121 - }
122 - }
123 -
124 -#if DBRT_DEBUG > 1
125 - if (first)
126 - fprintf(stderr, "index %s\n", first);
127 -#endif
128 - for (i = 0; i < len; i++) {
129 - if (!posns[i] || posns[i] == df_conflict_list)
130 - continue;
131 -#if DBRT_DEBUG > 1
132 - fprintf(stderr, "%d %s\n", i + 1, posns[i]->name);
133 -#endif
134 - if (!first || entcmp(first, firstdir,
135 - posns[i]->name,
136 - S_ISDIR(posns[i]->mode)) > 0) {
137 - first = posns[i]->name;
138 - firstdir = S_ISDIR(posns[i]->mode);
139 - }
140 - }
141 - /* No name means we're done */
142 - if (!first)
143 - goto leave_directory;
144 -
145 - pathlen = strlen(first);
146 - ce_size = cache_entry_size(baselen + pathlen);
147 -
148 - src = xcalloc(src_size, sizeof(struct cache_entry *));
149 -
150 - subposns = xcalloc(len, sizeof(struct tree_list_entry *));
151 -
152 - remove = -1;
153 - if (cache_name && !strcmp(cache_name, first)) {
154 - any_files = 1;
155 - src[0] = active_cache[o->pos];
156 - remove = o->pos;
157 - if (o->skip_unmerged && ce_stage(src[0]))
158 - skip_entry = 1;
159 - }
160 -
161 - for (i = 0; i < len; i++) {
162 - struct cache_entry *ce;
163 -
164 - if (!posns[i] ||
165 - (posns[i] != df_conflict_list &&
166 - strcmp(first, posns[i]->name))) {
167 - continue;
168 - }
169 -
170 - if (posns[i] == df_conflict_list) {
171 - src[i + o->merge] = o->df_conflict_entry;
172 - continue;
173 - }
174 -
175 - if (S_ISDIR(posns[i]->mode)) {
176 - struct tree *tree = lookup_tree(posns[i]->sha1);
177 - struct tree_desc t;
178 - any_dirs = 1;
179 - parse_tree(tree);
180 - init_tree_desc(&t, tree->buffer, tree->size);
181 - subposns[i] = create_tree_entry_list(&t);
182 - posns[i] = posns[i]->next;
183 - src[i + o->merge] = o->df_conflict_entry;
184 - continue;
185 - }
186 -
187 - if (skip_entry) {
188 - subposns[i] = df_conflict_list;
189 - posns[i] = posns[i]->next;
190 - continue;
191 - }
192 -
193 - if (!o->merge)
194 - ce_stage = 0;
195 - else if (i + 1 < o->head_idx)
196 - ce_stage = 1;
197 - else if (i + 1 > o->head_idx)
198 - ce_stage = 3;
199 - else
200 - ce_stage = 2;
201 -
202 - ce = xcalloc(1, ce_size);
203 - ce->ce_mode = create_ce_mode(posns[i]->mode);
204 - ce->ce_flags = create_ce_flags(baselen + pathlen,
205 - ce_stage);
206 - memcpy(ce->name, base, baselen);
207 - memcpy(ce->name + baselen, first, pathlen + 1);
208 -
209 - any_files = 1;
210 -
211 - hashcpy(ce->sha1, posns[i]->sha1);
212 - src[i + o->merge] = ce;
213 - subposns[i] = df_conflict_list;
214 - posns[i] = posns[i]->next;
215 - }
216 - if (any_files) {
217 - if (skip_entry) {
218 - o->pos++;
219 - while (o->pos < active_nr &&
220 - !strcmp(active_cache[o->pos]->name,
221 - src[0]->name))
222 - o->pos++;
223 - } else if (o->merge) {
224 - int ret;
225 -
226 -#if DBRT_DEBUG > 1
227 - fprintf(stderr, "%s:\n", first);
228 - for (i = 0; i < src_size; i++) {
229 - fprintf(stderr, " %d ", i);
230 - if (src[i])
231 - fprintf(stderr, "%06x %s\n", src[i]->ce_mode, sha1_to_hex(src[i]->sha1));
232 - else
233 - fprintf(stderr, "\n");
234 - }
235 -#endif
236 - ret = o->fn(src, o, remove);
237 - if (ret < 0)
238 - return ret;
239 -
240 -#if DBRT_DEBUG > 1
241 - fprintf(stderr, "Added %d entries\n", ret);
242 -#endif
243 - o->pos += ret;
244 - } else {
245 - remove_entry(remove);
246 - for (i = 0; i < src_size; i++) {
247 - if (src[i]) {
248 - add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
249 - }
250 - }
251 - }
252 - }
253 - if (any_dirs) {
254 - char *newbase = xmalloc(baselen + 2 + pathlen);
255 - memcpy(newbase, base, baselen);
256 - memcpy(newbase + baselen, first, pathlen);
257 - newbase[baselen + pathlen] = '/';
258 - newbase[baselen + pathlen + 1] = '\0';
259 - if (unpack_trees_rec(subposns, len, newbase, o,
260 - df_conflict_list)) {
261 - retval = -1;
262 - goto leave_directory;
263 - }
264 - free(newbase);
265 - }
266 - free(subposns);
267 - free(src);
268 - } while (1);
269 -
270 - leave_directory:
271 - return retval;
272 -}
273 -
16 /* Unlink the last component and attempt to remove leading
17 * directories, in case this unlink is the removal of the
18 * last entry in the directory -- empty directories are removed.
@@ -346,15 +88,241 @@ static void check_updates(struct unpack_trees_options *o)
88 stop_progress(&progress);
89 }
90
349 -int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
91 +static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_options *o, int remove)
92 +{
93 + int ret = o->fn(src, o, remove);
94 + if (ret > 0) {
95 + o->pos += ret;
96 + ret = 0;
97 + }
98 + return ret;
99 +}
100 +
101 +static int unpack_index_entry(struct cache_entry *ce, struct unpack_trees_options *o)
102 +{
103 + struct cache_entry *src[5] = { ce, };
104 + if (ce_stage(ce)) {
105 + if (o->skip_unmerged) {
106 + o->pos++;
107 + } else {
108 + remove_entry(o->pos);
109 + }
110 + return 0;
111 + }
112 + return call_unpack_fn(src, o, o->pos);
113 +}
114 +
115 +int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long df_conflicts, struct name_entry *names, struct traverse_info *info)
116 +{
117 + int i;
118 + struct tree_desc t[3];
119 + struct traverse_info newinfo;
120 + struct name_entry *p;
121 +
122 + p = names;
123 + while (!p->mode)
124 + p++;
125 +
126 + newinfo = *info;
127 + newinfo.prev = info;
128 + newinfo.name = *p;
129 + newinfo.pathlen += tree_entry_len(p->path, p->sha1) + 1;
130 + newinfo.conflicts |= df_conflicts;
131 +
132 + for (i = 0; i < n; i++, dirmask >>= 1) {
133 + const unsigned char *sha1 = NULL;
134 + if (dirmask & 1)
135 + sha1 = names[i].sha1;
136 + fill_tree_descriptor(t+i, sha1);
137 + }
138 + traverse_trees(n, t, &newinfo);
139 + return 0;
140 +}
141 +
142 +/*
143 + * Compare the traverse-path to the cache entry without actually
144 + * having to generate the textual representation of the traverse
145 + * path.
146 + *
147 + * NOTE! This *only* compares up to the size of the traverse path
148 + * itself - the caller needs to do the final check for the cache
149 + * entry having more data at the end!
150 + */
151 +static int do_compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
152 +{
153 + int len, pathlen, ce_len;
154 + const char *ce_name;
155 +
156 + if (info->prev) {
157 + int cmp = do_compare_entry(ce, info->prev, &info->name);
158 + if (cmp)
159 + return cmp;
160 + }
161 + pathlen = info->pathlen;
162 + ce_len = ce_namelen(ce);
163 +
164 + /* If ce_len < pathlen then we must have previously hit "name == directory" entry */
165 + if (ce_len < pathlen)
166 + return -1;
167 +
168 + ce_len -= pathlen;
169 + ce_name = ce->name + pathlen;
170 +
171 + len = tree_entry_len(n->path, n->sha1);
172 + return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
173 +}
174 +
175 +static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
176 +{
177 + int cmp = do_compare_entry(ce, info, n);
178 + if (cmp)
179 + return cmp;
180 +
181 + /*
182 + * Even if the beginning compared identically, the ce should
183 + * compare as bigger than a directory leading up to it!
184 + */
185 + return ce_namelen(ce) > traverse_path_len(info, n);
186 +}
187 +
188 +static struct cache_entry *create_ce_entry(const struct traverse_info *info, const struct name_entry *n, int stage)
189 +{
190 + int len = traverse_path_len(info, n);
191 + struct cache_entry *ce = xcalloc(1, cache_entry_size(len));
192 +
193 + ce->ce_mode = create_ce_mode(n->mode);
194 + ce->ce_flags = create_ce_flags(len, stage);
195 + hashcpy(ce->sha1, n->sha1);
196 + make_traverse_path(ce->name, info, n);
197 +
198 + return ce;
199 +}
200 +
201 +static int unpack_nondirectories(int n, unsigned long mask, unsigned long dirmask, struct cache_entry *src[5],
202 + const struct name_entry *names, const struct traverse_info *info, int remove)
203 {
351 - struct tree_entry_list **posns;
204 int i;
353 - struct tree_entry_list df_conflict_list;
205 + struct unpack_trees_options *o = info->data;
206 + unsigned long conflicts;
207 +
208 + /* Do we have *only* directories? Nothing to do */
209 + if (mask == dirmask && !src[0])
210 + return 0;
211 +
212 + conflicts = info->conflicts;
213 + if (o->merge)
214 + conflicts >>= 1;
215 + conflicts |= dirmask;
216 +
217 + /*
218 + * Ok, we've filled in up to any potential index entry in src[0],
219 + * now do the rest.
220 + */
221 + for (i = 0; i < n; i++) {
222 + int stage;
223 + unsigned int bit = 1ul << i;
224 + if (conflicts & bit) {
225 + src[i + o->merge] = o->df_conflict_entry;
226 + continue;
227 + }
228 + if (!(mask & bit))
229 + continue;
230 + if (!o->merge)
231 + stage = 0;
232 + else if (i + 1 < o->head_idx)
233 + stage = 1;
234 + else if (i + 1 > o->head_idx)
235 + stage = 3;
236 + else
237 + stage = 2;
238 + src[i + o->merge] = create_ce_entry(info, names + i, stage);
239 + }
240 +
241 + if (o->merge)
242 + return call_unpack_fn(src, o, remove);
243 +
244 + n += o->merge;
245 + remove_entry(remove);
246 + for (i = 0; i < n; i++)
247 + add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
248 + return 0;
249 +}
250 +
251 +static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *names, struct traverse_info *info)
252 +{
253 + struct cache_entry *src[5] = { NULL, };
254 + struct unpack_trees_options *o = info->data;
255 + int remove = -1;
256 + const struct name_entry *p = names;
257 +
258 + /* Find first entry with a real name (we could use "mask" too) */
259 + while (!p->mode)
260 + p++;
261 +
262 + /* Are we supposed to look at the index too? */
263 + if (o->merge) {
264 + while (o->pos < active_nr) {
265 + struct cache_entry *ce = active_cache[o->pos];
266 + int cmp = compare_entry(ce, info, p);
267 + if (cmp < 0) {
268 + if (unpack_index_entry(ce, o) < 0)
269 + return -1;
270 + continue;
271 + }
272 + if (!cmp) {
273 + if (ce_stage(ce)) {
274 + /*
275 + * If we skip unmerged index entries, we'll skip this
276 + * entry *and* the tree entries associated with it!
277 + */
278 + if (o->skip_unmerged)
279 + return mask;
280 + remove_entry(o->pos);
281 + continue;
282 + }
283 + src[0] = ce;
284 + remove = o->pos;
285 + }
286 + break;
287 + }
288 + }
289 +
290 + if (unpack_nondirectories(n, mask, dirmask, src, names, info, remove) < 0)
291 + return -1;
292 +
293 + /* Now handle any directories.. */
294 + if (dirmask) {
295 + unsigned long conflicts = mask & ~dirmask;
296 + if (o->merge) {
297 + conflicts <<= 1;
298 + if (src[0])
299 + conflicts |= 1;
300 + }
301 + traverse_trees_recursive(n, dirmask, conflicts, names, info);
302 + return mask;
303 + }
304 +
305 + return mask;
306 +}
307 +
308 +static int unpack_failed(struct unpack_trees_options *o, const char *message)
309 +{
310 + if (!o->gently) {
311 + if (message)
312 + return error(message);
313 + return -1;
314 + }
315 + discard_cache();
316 + read_cache();
317 + return -1;
318 +}
319 +
320 +int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
321 +{
322 static struct cache_entry *dfc;
323
356 - memset(&df_conflict_list, 0, sizeof(df_conflict_list));
357 - df_conflict_list.next = &df_conflict_list;
324 + if (len > 4)
325 + die("unpack_trees takes at most four trees");
326 memset(&state, 0, sizeof(state));
327 state.base_dir = "";
328 state.force = 1;
@@ -368,29 +336,29 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
336 o->df_conflict_entry = dfc;
337
338 if (len) {
371 - posns = xmalloc(len * sizeof(struct tree_entry_list *));
372 - for (i = 0; i < len; i++)
373 - posns[i] = create_tree_entry_list(t+i);
374 -
375 - if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "",
376 - o, &df_conflict_list)) {
377 - if (o->gently) {
378 - discard_cache();
379 - read_cache();
380 - }
381 - return -1;
382 - }
339 + const char *prefix = o->prefix ? o->prefix : "";
340 + struct traverse_info info;
341 +
342 + setup_traverse_info(&info, prefix);
343 + info.fn = unpack_callback;
344 + info.data = o;
345 +
346 + if (traverse_trees(len, t, &info) < 0)
347 + return unpack_failed(o, NULL);
348 }
349
385 - if (o->trivial_merges_only && o->nontrivial_merge) {
386 - if (o->gently) {
387 - discard_cache();
388 - read_cache();
350 + /* Any left-over entries in the index? */
351 + if (o->merge) {
352 + while (o->pos < active_nr) {
353 + struct cache_entry *ce = active_cache[o->pos];
354 + if (unpack_index_entry(ce, o) < 0)
355 + return unpack_failed(o, NULL);
356 }
390 - return o->gently ? -1 :
391 - error("Merge requires file-level merging");
357 }
358
359 + if (o->trivial_merges_only && o->nontrivial_merge)
360 + return unpack_failed(o, "Merge requires file-level merging");
361 +
362 check_updates(o);
363 return 0;
364 }