full integration of rev-cache into git, completed test suite
[git] / builtin-rev-cache.c
1 #include "cache.h"
2 #include "object.h"
3 #include "commit.h"
4 #include "diff.h"
5 #include "revision.h"
6 #include "rev-cache.h"
7 #include "list-objects.h"
8
9 unsigned long default_ignore_size = 50 * 1024 * 1024; /* 50mb */
10
11 /* porcelain for rev-cache.c */
12 static int handle_add(int argc, const char *argv[]) /* args beyond this command */
13 {
14         struct rev_info revs;
15         struct rev_cache_info rci;
16         char dostdin = 0;
17         unsigned int flags = 0;
18         int i, retval;
19         unsigned char cache_sha1[20];
20         struct commit_list *starts = 0, *ends = 0;
21         struct commit *commit;
22
23         init_revisions(&revs, 0);
24         init_rev_cache_info(&rci);
25
26         for (i = 0; i < argc; i++) {
27                 if (!strcmp(argv[i], "--stdin"))
28                         dostdin = 1;
29                 else if (!strcmp(argv[i], "--fresh") || !strcmp(argv[i], "--incremental"))
30                         starts_from_slices(&revs, UNINTERESTING, 0, 0);
31                 else if (!strcmp(argv[i], "--not"))
32                         flags ^= UNINTERESTING;
33                 else if (!strcmp(argv[i], "--legs"))
34                         rci.legs = 1;
35                 else if (!strcmp(argv[i], "--no-objects"))
36                         rci.objects = 0;
37                 else if (!strcmp(argv[i], "--all")) {
38                         const char *args[2];
39                         int argn = 0;
40
41                         args[argn++] = "rev-list";
42                         args[argn++] = "--all";
43                         setup_revisions(argn, args, &revs, 0);
44                 } else
45                         handle_revision_arg(argv[i], &revs, flags, 1);
46         }
47
48         if (dostdin) {
49                 char line[1000];
50
51                 flags = 0;
52                 while (fgets(line, sizeof(line), stdin)) {
53                         int len = strlen(line);
54                         while (len && (line[len - 1] == '\n' || line[len - 1] == '\r'))
55                                 line[--len] = 0;
56
57                         if (!len)
58                                 break;
59
60                         if (!strcmp(line, "--not"))
61                                 flags ^= UNINTERESTING;
62                         else
63                                 handle_revision_arg(line, &revs, flags, 1);
64                 }
65         }
66
67         retval = make_cache_slice(&rci, &revs, &starts, &ends, cache_sha1);
68         if (retval < 0)
69                 return retval;
70
71         printf("%s\n", sha1_to_hex(cache_sha1));
72
73         fprintf(stderr, "endpoints:\n");
74         while ((commit = pop_commit(&starts)))
75                 fprintf(stderr, "S %s\n", sha1_to_hex(commit->object.sha1));
76         while ((commit = pop_commit(&ends)))
77                 fprintf(stderr, "E %s\n", sha1_to_hex(commit->object.sha1));
78
79         return 0;
80 }
81
82 static void show_commit(struct commit *commit, void *data)
83 {
84         printf("%s\n", sha1_to_hex(commit->object.sha1));
85 }
86
87 static void show_object(struct object *obj, const struct name_path *path, const char *last)
88 {
89         printf("%s\n", sha1_to_hex(obj->sha1));
90 }
91
92 static int test_rev_list(int argc, const char *argv[])
93 {
94         struct rev_info revs;
95         unsigned int flags = 0;
96         int i;
97
98         init_revisions(&revs, 0);
99
100         for (i = 0; i < argc; i++) {
101                 if (!strcmp(argv[i], "--not"))
102                         flags ^= UNINTERESTING;
103                 else if (!strcmp(argv[i], "--objects"))
104                         revs.tree_objects = revs.blob_objects = 1;
105                 else
106                         handle_revision_arg(argv[i], &revs, flags, 1);
107         }
108
109         setup_revisions(0, 0, &revs, 0);
110         revs.topo_order = 1;
111         revs.lifo = 1;
112         prepare_revision_walk(&revs);
113
114         traverse_commit_list(&revs, show_commit, show_object, 0);
115
116         return 0;
117 }
118
119 static int handle_walk(int argc, const char *argv[])
120 {
121         struct commit *commit;
122         struct rev_info revs;
123         struct commit_list *queue, *work, **qp;
124         unsigned char *sha1p, *sha1pt;
125         unsigned long date = 0;
126         unsigned int flags = 0;
127         int retval, slop = 5, i;
128
129         init_revisions(&revs, 0);
130
131         for (i = 0; i < argc; i++) {
132                 if (!strcmp(argv[i], "--not"))
133                         flags ^= UNINTERESTING;
134                 else if (!strcmp(argv[i], "--objects"))
135                         revs.tree_objects = revs.blob_objects = 1;
136                 else
137                         handle_revision_arg(argv[i], &revs, flags, 1);
138         }
139
140         work = 0;
141         sha1p = 0;
142         for (i = 0; i < revs.pending.nr; i++) {
143                 commit = lookup_commit(revs.pending.objects[i].item->sha1);
144
145                 sha1pt = get_cache_slice(commit);
146                 if (!sha1pt)
147                         die("%s: not in a cache slice", sha1_to_hex(commit->object.sha1));
148
149                 if (!i)
150                         sha1p = sha1pt;
151                 else if (sha1p != sha1pt)
152                         die("walking porcelain is /per/ cache slice; commits cannot be spread out amoung several");
153
154                 insert_by_date(commit, &work);
155         }
156
157         if (!sha1p)
158                 die("nothing to traverse!");
159
160         queue = 0;
161         qp = &queue;
162         commit = pop_commit(&work);
163         retval = traverse_cache_slice(&revs, sha1p, commit, &date, &slop, &qp, &work);
164         if (retval < 0)
165                 return retval;
166
167         fprintf(stderr, "queue:\n");
168         while ((commit = pop_commit(&queue)) != 0) {
169                 printf("%s\n", sha1_to_hex(commit->object.sha1));
170         }
171
172         fprintf(stderr, "work:\n");
173         while ((commit = pop_commit(&work)) != 0) {
174                 printf("%s\n", sha1_to_hex(commit->object.sha1));
175         }
176
177         fprintf(stderr, "pending:\n");
178         for (i = 0; i < revs.pending.nr; i++) {
179                 struct object *obj = revs.pending.objects[i].item;
180
181                 /* unfortunately, despite our careful generation, object duplication *is* a possibility...
182                  * (eg. same object introduced into two different branches) */
183                 if (obj->flags & SEEN)
184                         continue;
185
186                 printf("%s\n", sha1_to_hex(revs.pending.objects[i].item->sha1));
187                 obj->flags |= SEEN;
188         }
189
190         return 0;
191 }
192
193 static int handle_fuse(int argc, const char *argv[])
194 {
195         struct rev_info revs;
196         struct rev_cache_info rci;
197         const char *args[5];
198         int i, argn = 0;
199         char add_all = 0;
200
201         init_revisions(&revs, 0);
202         init_rev_cache_info(&rci);
203         args[argn++] = "rev-list";
204
205         for (i = 0; i < argc; i++) {
206                 if (!strcmp(argv[i], "--all")) {
207                         args[argn++] = "--all";
208                         setup_revisions(argn, args, &revs, 0);
209                         add_all = 1;
210                 } else if (!strcmp(argv[i], "--no-objects"))
211                         rci.objects = 0;
212                 else if (!strncmp(argv[i], "--ignore-size", 13)) {
213                         unsigned long sz;
214
215                         if (argv[i][13] == '=')
216                                 git_parse_ulong(argv[i] + 14, &sz);
217                         else
218                                 sz = default_ignore_size;
219
220                         rci.ignore_size = sz;
221                 } else
222                         continue;
223         }
224
225         if (!add_all)
226                 starts_from_slices(&revs, 0, 0, 0);
227
228         return fuse_cache_slices(&rci, &revs);
229 }
230
231 static int handle_index(int argc, const char *argv[])
232 {
233         return regenerate_cache_index(0);
234 }
235
236 static int handle_alt(int argc, const char *argv[])
237 {
238         if (argc < 1)
239                 return -1;
240
241         return make_cache_slice_pointer(0, argv[0]);
242 }
243
244 static int handle_help(void)
245 {
246         char *usage = "\
247 usage:\n\
248 git-rev-cache COMMAND [options] [<commit-id>...]\n\
249 commands:\n\
250   add    - add revisions to the cache.  reads commit ids from stdin, \n\
251            formatted as: START START ... --not END END ...\n\
252            options:\n\
253             --all                  use all branch heads as starts\n\
254             --fresh/--incremental  exclude everything already in a cache slice\n\
255             --stdin                also read commit ids from stdin (same form\n\
256                                    as cmd)\n\
257             --legs                 ensure branch is entirely self-contained\n\
258             --no-objects           don't add non-commit objects to slice\n\
259   walk   - walk a cache slice based on set of commits; formatted as add\n\
260            options:\n\
261            --objects               include non-commit objects in traversals\n\
262   fuse   - coalesce cache slices into a single cache.\n\
263            options:\n\
264             --all                  include all objects in repository\n\
265             --no-objects           don't add non-commit objects to slice\n\
266             --ignore-size[=N]      ignore slices of size >= N; defaults to ~5MB\n\
267   index  - regnerate the cache index.";
268
269         puts(usage);
270
271         return 0;
272 }
273
274 static int rev_cache_config(const char *k, const char *v, void *cb)
275 {
276         /* this could potentially be related to pack.windowmemory, but we want a max around 50mb,
277          * and .windowmemory is often >700mb, with *large* variations */
278         if (!strcmp(k, "revcache.ignoresize")) {
279                 int t;
280
281                 t = git_config_ulong(k, v);
282                 if (t)
283                         default_ignore_size = t;
284         }
285
286         return 0;
287 }
288
289 int cmd_rev_cache(int argc, const char *argv[], const char *prefix)
290 {
291         const char *arg;
292         int r;
293
294         git_config(git_default_config, NULL);
295         git_config(rev_cache_config, NULL);
296
297         if (argc > 1)
298                 arg = argv[1];
299         else
300                 arg = "";
301
302         argc -= 2;
303         argv += 2;
304         if (!strcmp(arg, "add"))
305                 r = handle_add(argc, argv);
306         else if (!strcmp(arg, "fuse"))
307                 r = handle_fuse(argc, argv);
308         else if (!strcmp(arg, "walk"))
309                 r = handle_walk(argc, argv);
310         else if (!strcmp(arg, "index"))
311                 r = handle_index(argc, argv);
312         else if (!strcmp(arg, "test"))
313                 r = test_rev_list(argc, argv);
314         else if (!strcmp(arg, "alt"))
315                 r = handle_alt(argc, argv);
316         else
317                 return handle_help();
318
319         fprintf(stderr, "final return value: %d\n", r);
320
321         return 0;
322 }