Merge branch 'dk/blame-el' into pu
[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                 const char *name = revs.pending.objects[i].name;
181
182                 /* unfortunately, despite our careful generation, object duplication *is* a possibility...
183                  * (eg. same object introduced into two different branches) */
184                 if (obj->flags & SEEN)
185                         continue;
186
187                 printf("%s %s\n", sha1_to_hex(revs.pending.objects[i].item->sha1), name);
188                 obj->flags |= SEEN;
189         }
190
191         return 0;
192 }
193
194 static int handle_fuse(int argc, const char *argv[])
195 {
196         struct rev_info revs;
197         struct rev_cache_info rci;
198         const char *args[5];
199         int i, argn = 0;
200         char add_all = 0;
201
202         init_revisions(&revs, 0);
203         init_rev_cache_info(&rci);
204         args[argn++] = "rev-list";
205
206         for (i = 0; i < argc; i++) {
207                 if (!strcmp(argv[i], "--all")) {
208                         args[argn++] = "--all";
209                         setup_revisions(argn, args, &revs, 0);
210                         add_all = 1;
211                 } else if (!strcmp(argv[i], "--no-objects"))
212                         rci.objects = 0;
213                 else if (!strncmp(argv[i], "--ignore-size", 13)) {
214                         unsigned long sz;
215
216                         if (argv[i][13] == '=')
217                                 git_parse_ulong(argv[i] + 14, &sz);
218                         else
219                                 sz = default_ignore_size;
220
221                         rci.ignore_size = sz;
222                 } else
223                         continue;
224         }
225
226         if (!add_all)
227                 starts_from_slices(&revs, 0, 0, 0);
228
229         return fuse_cache_slices(&rci, &revs);
230 }
231
232 static int handle_index(int argc, const char *argv[])
233 {
234         return regenerate_cache_index(0);
235 }
236
237 static int handle_alt(int argc, const char *argv[])
238 {
239         if (argc < 1)
240                 return -1;
241
242         return make_cache_slice_pointer(0, argv[0]);
243 }
244
245 static int handle_help(void)
246 {
247         char *usage = "\
248 usage:\n\
249 git-rev-cache COMMAND [options] [<commit-id>...]\n\
250 commands:\n\
251   add    - add revisions to the cache.  reads commit ids from stdin, \n\
252            formatted as: START START ... --not END END ...\n\
253            options:\n\
254             --all                  use all branch heads as starts\n\
255             --fresh/--incremental  exclude everything already in a cache slice\n\
256             --stdin                also read commit ids from stdin (same form\n\
257                                    as cmd)\n\
258             --legs                 ensure branch is entirely self-contained\n\
259             --no-objects           don't add non-commit objects to slice\n\
260   walk   - walk a cache slice based on set of commits; formatted as add\n\
261            options:\n\
262            --objects               include non-commit objects in traversals\n\
263   fuse   - coalesce cache slices into a single cache.\n\
264            options:\n\
265             --all                  include all objects in repository\n\
266             --no-objects           don't add non-commit objects to slice\n\
267             --ignore-size[=N]      ignore slices of size >= N; defaults to ~5MB\n\
268   index  - regnerate the cache index.";
269
270         puts(usage);
271
272         return 0;
273 }
274
275 static int rev_cache_config(const char *k, const char *v, void *cb)
276 {
277         /* this could potentially be related to pack.windowmemory, but we want a max around 50mb,
278          * and .windowmemory is often >700mb, with *large* variations */
279         if (!strcmp(k, "revcache.ignoresize")) {
280                 int t;
281
282                 t = git_config_ulong(k, v);
283                 if (t)
284                         default_ignore_size = t;
285         }
286
287         return 0;
288 }
289
290 int cmd_rev_cache(int argc, const char *argv[], const char *prefix)
291 {
292         const char *arg;
293         int r;
294
295         git_config(git_default_config, NULL);
296         git_config(rev_cache_config, NULL);
297
298         if (argc > 1)
299                 arg = argv[1];
300         else
301                 arg = "";
302
303         argc -= 2;
304         argv += 2;
305         if (!strcmp(arg, "add"))
306                 r = handle_add(argc, argv);
307         else if (!strcmp(arg, "fuse"))
308                 r = handle_fuse(argc, argv);
309         else if (!strcmp(arg, "walk"))
310                 r = handle_walk(argc, argv);
311         else if (!strcmp(arg, "index"))
312                 r = handle_index(argc, argv);
313         else if (!strcmp(arg, "test"))
314                 r = test_rev_list(argc, argv);
315         else if (!strcmp(arg, "alt"))
316                 r = handle_alt(argc, argv);
317         else
318                 return handle_help();
319
320         fprintf(stderr, "final return value: %d\n", r);
321
322         return 0;
323 }