]>
Commit | Line | Data |
---|---|---|
ae563542 LT |
1 | #include "cache.h" |
2 | #include "tag.h" | |
3 | #include "blob.h" | |
4 | #include "tree.h" | |
5 | #include "commit.h" | |
6 | #include "refs.h" | |
7 | #include "revision.h" | |
8 | ||
9 | static char *path_name(struct name_path *path, const char *name) | |
10 | { | |
11 | struct name_path *p; | |
12 | char *n, *m; | |
13 | int nlen = strlen(name); | |
14 | int len = nlen + 1; | |
15 | ||
16 | for (p = path; p; p = p->up) { | |
17 | if (p->elem_len) | |
18 | len += p->elem_len + 1; | |
19 | } | |
20 | n = xmalloc(len); | |
21 | m = n + len - (nlen + 1); | |
22 | strcpy(m, name); | |
23 | for (p = path; p; p = p->up) { | |
24 | if (p->elem_len) { | |
25 | m -= p->elem_len + 1; | |
26 | memcpy(m, p->elem, p->elem_len); | |
27 | m[p->elem_len] = '/'; | |
28 | } | |
29 | } | |
30 | return n; | |
31 | } | |
32 | ||
33 | struct object_list **add_object(struct object *obj, | |
34 | struct object_list **p, | |
35 | struct name_path *path, | |
36 | const char *name) | |
37 | { | |
38 | struct object_list *entry = xmalloc(sizeof(*entry)); | |
39 | entry->item = obj; | |
40 | entry->next = *p; | |
41 | entry->name = path_name(path, name); | |
42 | *p = entry; | |
43 | return &entry->next; | |
44 | } | |
45 | ||
46 | static void mark_blob_uninteresting(struct blob *blob) | |
47 | { | |
48 | if (blob->object.flags & UNINTERESTING) | |
49 | return; | |
50 | blob->object.flags |= UNINTERESTING; | |
51 | } | |
52 | ||
53 | void mark_tree_uninteresting(struct tree *tree) | |
54 | { | |
55 | struct object *obj = &tree->object; | |
56 | struct tree_entry_list *entry; | |
57 | ||
58 | if (obj->flags & UNINTERESTING) | |
59 | return; | |
60 | obj->flags |= UNINTERESTING; | |
61 | if (!has_sha1_file(obj->sha1)) | |
62 | return; | |
63 | if (parse_tree(tree) < 0) | |
64 | die("bad tree %s", sha1_to_hex(obj->sha1)); | |
65 | entry = tree->entries; | |
66 | tree->entries = NULL; | |
67 | while (entry) { | |
68 | struct tree_entry_list *next = entry->next; | |
69 | if (entry->directory) | |
70 | mark_tree_uninteresting(entry->item.tree); | |
71 | else | |
72 | mark_blob_uninteresting(entry->item.blob); | |
73 | free(entry); | |
74 | entry = next; | |
75 | } | |
76 | } | |
77 | ||
78 | void mark_parents_uninteresting(struct commit *commit) | |
79 | { | |
80 | struct commit_list *parents = commit->parents; | |
81 | ||
82 | while (parents) { | |
83 | struct commit *commit = parents->item; | |
84 | commit->object.flags |= UNINTERESTING; | |
85 | ||
86 | /* | |
87 | * Normally we haven't parsed the parent | |
88 | * yet, so we won't have a parent of a parent | |
89 | * here. However, it may turn out that we've | |
90 | * reached this commit some other way (where it | |
91 | * wasn't uninteresting), in which case we need | |
92 | * to mark its parents recursively too.. | |
93 | */ | |
94 | if (commit->parents) | |
95 | mark_parents_uninteresting(commit); | |
96 | ||
97 | /* | |
98 | * A missing commit is ok iff its parent is marked | |
99 | * uninteresting. | |
100 | * | |
101 | * We just mark such a thing parsed, so that when | |
102 | * it is popped next time around, we won't be trying | |
103 | * to parse it and get an error. | |
104 | */ | |
105 | if (!has_sha1_file(commit->object.sha1)) | |
106 | commit->object.parsed = 1; | |
107 | parents = parents->next; | |
108 | } | |
109 | } | |
110 | ||
111 | static void add_pending_object(struct rev_info *revs, struct object *obj, const char *name) | |
112 | { | |
113 | add_object(obj, &revs->pending_objects, NULL, name); | |
114 | } | |
115 | ||
116 | static struct commit *get_commit_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags) | |
117 | { | |
118 | struct object *object; | |
119 | ||
120 | object = parse_object(sha1); | |
121 | if (!object) | |
122 | die("bad object %s", name); | |
123 | ||
124 | /* | |
125 | * Tag object? Look what it points to.. | |
126 | */ | |
127 | while (object->type == tag_type) { | |
128 | struct tag *tag = (struct tag *) object; | |
129 | object->flags |= flags; | |
130 | if (revs->tag_objects && !(object->flags & UNINTERESTING)) | |
131 | add_pending_object(revs, object, tag->tag); | |
132 | object = parse_object(tag->tagged->sha1); | |
133 | if (!object) | |
134 | die("bad object %s", sha1_to_hex(tag->tagged->sha1)); | |
135 | } | |
136 | ||
137 | /* | |
138 | * Commit object? Just return it, we'll do all the complex | |
139 | * reachability crud. | |
140 | */ | |
141 | if (object->type == commit_type) { | |
142 | struct commit *commit = (struct commit *)object; | |
143 | object->flags |= flags; | |
144 | if (parse_commit(commit) < 0) | |
145 | die("unable to parse commit %s", name); | |
146 | if (flags & UNINTERESTING) | |
147 | mark_parents_uninteresting(commit); | |
148 | return commit; | |
149 | } | |
150 | ||
151 | /* | |
152 | * Tree object? Either mark it uniniteresting, or add it | |
153 | * to the list of objects to look at later.. | |
154 | */ | |
155 | if (object->type == tree_type) { | |
156 | struct tree *tree = (struct tree *)object; | |
157 | if (!revs->tree_objects) | |
158 | return NULL; | |
159 | if (flags & UNINTERESTING) { | |
160 | mark_tree_uninteresting(tree); | |
161 | return NULL; | |
162 | } | |
163 | add_pending_object(revs, object, ""); | |
164 | return NULL; | |
165 | } | |
166 | ||
167 | /* | |
168 | * Blob object? You know the drill by now.. | |
169 | */ | |
170 | if (object->type == blob_type) { | |
171 | struct blob *blob = (struct blob *)object; | |
172 | if (!revs->blob_objects) | |
173 | return NULL; | |
174 | if (flags & UNINTERESTING) { | |
175 | mark_blob_uninteresting(blob); | |
176 | return NULL; | |
177 | } | |
178 | add_pending_object(revs, object, ""); | |
179 | return NULL; | |
180 | } | |
181 | die("%s is unknown object", name); | |
182 | } | |
183 | ||
184 | static void add_one_commit(struct commit *commit, struct rev_info *revs) | |
185 | { | |
186 | if (!commit || (commit->object.flags & SEEN)) | |
187 | return; | |
188 | commit->object.flags |= SEEN; | |
189 | commit_list_insert(commit, &revs->commits); | |
190 | } | |
191 | ||
192 | static int all_flags; | |
193 | static struct rev_info *all_revs; | |
194 | ||
195 | static int handle_one_ref(const char *path, const unsigned char *sha1) | |
196 | { | |
197 | struct commit *commit = get_commit_reference(all_revs, path, sha1, all_flags); | |
198 | add_one_commit(commit, all_revs); | |
199 | return 0; | |
200 | } | |
201 | ||
202 | static void handle_all(struct rev_info *revs, unsigned flags) | |
203 | { | |
204 | all_revs = revs; | |
205 | all_flags = flags; | |
206 | for_each_ref(handle_one_ref); | |
207 | } | |
208 | ||
209 | /* | |
210 | * Parse revision information, filling in the "rev_info" structure, | |
211 | * and removing the used arguments from the argument list. | |
212 | * | |
213 | * Returns the number of arguments left ("new argc"). | |
214 | */ | |
215 | int setup_revisions(int argc, const char **argv, struct rev_info *revs) | |
216 | { | |
217 | int i, flags, seen_dashdash; | |
218 | const char *def = NULL; | |
219 | const char **unrecognized = argv+1; | |
220 | int left = 1; | |
221 | ||
222 | memset(revs, 0, sizeof(*revs)); | |
223 | revs->lifo = 1; | |
224 | revs->dense = 1; | |
225 | revs->prefix = setup_git_directory(); | |
226 | revs->max_age = -1; | |
227 | revs->min_age = -1; | |
228 | revs->max_count = -1; | |
229 | ||
230 | /* First, search for "--" */ | |
231 | seen_dashdash = 0; | |
232 | for (i = 1; i < argc; i++) { | |
233 | const char *arg = argv[i]; | |
234 | if (strcmp(arg, "--")) | |
235 | continue; | |
236 | argv[i] = NULL; | |
237 | argc = i; | |
238 | revs->paths = get_pathspec(revs->prefix, argv + i + 1); | |
239 | seen_dashdash = 1; | |
240 | break; | |
241 | } | |
242 | ||
243 | flags = 0; | |
244 | for (i = 1; i < argc; i++) { | |
245 | struct commit *commit; | |
246 | const char *arg = argv[i]; | |
247 | unsigned char sha1[20]; | |
248 | char *dotdot; | |
249 | int local_flags; | |
250 | ||
251 | if (*arg == '-') { | |
252 | if (!strncmp(arg, "--max-count=", 12)) { | |
253 | revs->max_count = atoi(arg + 12); | |
254 | continue; | |
255 | } | |
256 | if (!strncmp(arg, "--max-age=", 10)) { | |
257 | revs->max_age = atoi(arg + 10); | |
258 | continue; | |
259 | } | |
260 | if (!strncmp(arg, "--min-age=", 10)) { | |
261 | revs->min_age = atoi(arg + 10); | |
262 | continue; | |
263 | } | |
264 | if (!strcmp(arg, "--all")) { | |
265 | handle_all(revs, flags); | |
266 | continue; | |
267 | } | |
268 | if (!strcmp(arg, "--not")) { | |
269 | flags ^= UNINTERESTING; | |
270 | continue; | |
271 | } | |
272 | if (!strcmp(arg, "--default")) { | |
273 | if (++i >= argc) | |
274 | die("bad --default argument"); | |
275 | def = argv[i]; | |
276 | continue; | |
277 | } | |
278 | if (!strcmp(arg, "--topo-order")) { | |
279 | revs->topo_order = 1; | |
280 | continue; | |
281 | } | |
282 | if (!strcmp(arg, "--date-order")) { | |
283 | revs->lifo = 0; | |
284 | revs->topo_order = 1; | |
285 | continue; | |
286 | } | |
287 | if (!strcmp(arg, "--dense")) { | |
288 | revs->dense = 1; | |
289 | continue; | |
290 | } | |
291 | if (!strcmp(arg, "--sparse")) { | |
292 | revs->dense = 0; | |
293 | continue; | |
294 | } | |
295 | if (!strcmp(arg, "--remove-empty")) { | |
296 | revs->remove_empty_trees = 1; | |
297 | continue; | |
298 | } | |
299 | if (!strcmp(arg, "--objects")) { | |
300 | revs->tag_objects = 1; | |
301 | revs->tree_objects = 1; | |
302 | revs->blob_objects = 1; | |
303 | continue; | |
304 | } | |
305 | if (!strcmp(arg, "--objects-edge")) { | |
306 | revs->tag_objects = 1; | |
307 | revs->tree_objects = 1; | |
308 | revs->blob_objects = 1; | |
309 | revs->edge_hint = 1; | |
310 | continue; | |
311 | } | |
312 | *unrecognized++ = arg; | |
313 | left++; | |
314 | continue; | |
315 | } | |
316 | dotdot = strstr(arg, ".."); | |
317 | if (dotdot) { | |
318 | unsigned char from_sha1[20]; | |
319 | char *next = dotdot + 2; | |
320 | *dotdot = 0; | |
321 | if (!*next) | |
322 | next = "HEAD"; | |
323 | if (!get_sha1(arg, from_sha1) && !get_sha1(next, sha1)) { | |
324 | struct commit *exclude; | |
325 | struct commit *include; | |
326 | ||
327 | exclude = get_commit_reference(revs, arg, from_sha1, flags ^ UNINTERESTING); | |
328 | include = get_commit_reference(revs, next, sha1, flags); | |
329 | if (!exclude || !include) | |
330 | die("Invalid revision range %s..%s", arg, next); | |
331 | add_one_commit(exclude, revs); | |
332 | add_one_commit(include, revs); | |
333 | continue; | |
334 | } | |
335 | *dotdot = '.'; | |
336 | } | |
337 | local_flags = 0; | |
338 | if (*arg == '^') { | |
339 | local_flags = UNINTERESTING; | |
340 | arg++; | |
341 | } | |
342 | if (get_sha1(arg, sha1) < 0) { | |
343 | struct stat st; | |
344 | int j; | |
345 | ||
346 | if (seen_dashdash || local_flags) | |
347 | die("bad revision '%s'", arg); | |
348 | ||
349 | /* If we didn't have a "--", all filenames must exist */ | |
350 | for (j = i; j < argc; j++) { | |
351 | if (lstat(argv[j], &st) < 0) | |
352 | die("'%s': %s", arg, strerror(errno)); | |
353 | } | |
354 | revs->paths = get_pathspec(revs->prefix, argv + i); | |
355 | break; | |
356 | } | |
357 | commit = get_commit_reference(revs, arg, sha1, flags ^ local_flags); | |
358 | add_one_commit(commit, revs); | |
359 | } | |
360 | if (def && !revs->commits) { | |
361 | unsigned char sha1[20]; | |
362 | struct commit *commit; | |
363 | if (get_sha1(def, sha1) < 0) | |
364 | die("bad default revision '%s'", def); | |
365 | commit = get_commit_reference(revs, def, sha1, 0); | |
366 | add_one_commit(commit, revs); | |
367 | } | |
368 | *unrecognized = NULL; | |
369 | return left; | |
370 | } |