]> git.ipfire.org Git - thirdparty/git.git/blame_incremental - server-info.c
[PATCH] Add update-server-info.
[thirdparty/git.git] / server-info.c
... / ...
CommitLineData
1#include "cache.h"
2#include "refs.h"
3#include "object.h"
4#include "commit.h"
5#include "rev-cache.h"
6
7/* refs */
8static FILE *info_ref_fp;
9static unsigned long info_ref_time;
10static int info_ref_is_stale = 0;
11
12static int stat_ref(const char *path, const unsigned char *sha1)
13{
14 struct stat st;
15 if (!stat(path, &st) && info_ref_time < st.st_mtime)
16 info_ref_is_stale = 1;
17 return 0;
18}
19
20static int add_info_ref(const char *path, const unsigned char *sha1)
21{
22 fprintf(info_ref_fp, "%s %s\n", sha1_to_hex(sha1), path);
23 return 0;
24}
25
26static int update_info_refs(int force)
27{
28 struct stat st;
29 char *path0 = strdup(git_path("info/refs"));
30 int len = strlen(path0);
31 char *path1 = xmalloc(len + 2);
32
33 strcpy(path1, path0);
34 strcpy(path1 + len, "+");
35
36 if (!force) {
37 if (stat(path0, &st)) {
38 if (errno == ENOENT)
39 info_ref_is_stale = 1;
40 else
41 return error("cannot stat %s", path0);
42 }
43 else {
44 info_ref_time = st.st_mtime;
45 for_each_ref(stat_ref);
46 }
47 if (!info_ref_is_stale)
48 return 0;
49 }
50
51 safe_create_leading_directories(path0);
52 info_ref_fp = fopen(path1, "w");
53 if (!info_ref_fp)
54 return error("unable to update %s", path0);
55 for_each_ref(add_info_ref);
56 fclose(info_ref_fp);
57 rename(path1, path0);
58 free(path0);
59 free(path1);
60 return 0;
61}
62
63/* packs */
64struct pack_info {
65 unsigned long latest;
66 struct packed_git *p;
67 int old_num;
68 int new_num;
69 int nr_alloc;
70 int nr_heads;
71 unsigned char (*head)[20];
72 char dep[0]; /* more */
73} **info;
74static int num_pack;
75static const char *objdir;
76static int objdirlen;
77
78static struct object *parse_object_cheap(const unsigned char *sha1)
79{
80 struct object *o;
81
82 if ((o = parse_object(sha1)) == NULL)
83 return NULL;
84 if (o->type == commit_type) {
85 struct commit *commit = (struct commit *)o;
86 free(commit->buffer);
87 commit->buffer = NULL;
88 }
89 return o;
90}
91
92static struct pack_info *find_pack_by_name(const char *name)
93{
94 int i;
95 for (i = 0; i < num_pack; i++) {
96 struct packed_git *p = info[i]->p;
97 /* skip "/pack/" after ".git/objects" */
98 if (!strcmp(p->pack_name + objdirlen + 6, name))
99 return info[i];
100 }
101 return NULL;
102}
103
104static struct pack_info *find_pack_by_old_num(int old_num)
105{
106 int i;
107 for (i = 0; i < num_pack; i++)
108 if (info[i]->old_num == old_num)
109 return info[i];
110 return NULL;
111}
112
113static int add_head_def(struct pack_info *this, unsigned char *sha1)
114{
115 if (this->nr_alloc <= this->nr_heads) {
116 this->nr_alloc = alloc_nr(this->nr_alloc);
117 this->head = xrealloc(this->head, this->nr_alloc * 20);
118 }
119 memcpy(this->head[this->nr_heads++], sha1, 20);
120 return 0;
121}
122
123/* Returns non-zero when we detect that the info in the
124 * old file is useless.
125 */
126static int parse_pack_def(const char *line, int old_cnt)
127{
128 struct pack_info *i = find_pack_by_name(line + 2);
129 if (i) {
130 i->old_num = old_cnt;
131 return 0;
132 }
133 else {
134 /* The file describes a pack that is no longer here;
135 * dependencies between packs needs to be recalculated.
136 */
137 return 1;
138 }
139}
140
141/* Returns non-zero when we detect that the info in the
142 * old file is useless.
143 */
144static int parse_depend_def(char *line)
145{
146 unsigned long num;
147 char *cp, *ep;
148 struct pack_info *this, *that;
149
150 cp = line + 2;
151 num = strtoul(cp, &ep, 10);
152 if (ep == cp)
153 return error("invalid input %s", line);
154 this = find_pack_by_old_num(num);
155 if (!this)
156 return 0;
157 while (ep && *(cp = ep)) {
158 num = strtoul(cp, &ep, 10);
159 if (ep == cp)
160 break;
161 that = find_pack_by_old_num(num);
162 if (!that)
163 /* The pack this one depends on does not
164 * exist; this should not happen because
165 * we write out the list of packs first and
166 * then dependency information, but it means
167 * the file is useless anyway.
168 */
169 return 1;
170 this->dep[that->new_num] = 1;
171 }
172 return 0;
173}
174
175/* Returns non-zero when we detect that the info in the
176 * old file is useless.
177 */
178static int parse_head_def(char *line)
179{
180 unsigned char sha1[20];
181 unsigned long num;
182 char *cp, *ep;
183 struct pack_info *this;
184 struct object *o;
185
186 cp = line + 2;
187 num = strtoul(cp, &ep, 10);
188 if (ep == cp || *ep++ != ' ')
189 return error("invalid input ix %s", line);
190 this = find_pack_by_old_num(num);
191 if (!this)
192 return 1; /* You know the drill. */
193 if (get_sha1_hex(ep, sha1) || ep[40] != ' ')
194 return error("invalid input sha1 %s (%s)", line, ep);
195 if ((o = parse_object_cheap(sha1)) == NULL)
196 return error("no such object: %s", line);
197 return add_head_def(this, sha1);
198}
199
200/* Returns non-zero when we detect that the info in the
201 * old file is useless.
202 */
203static int read_pack_info_file(const char *infofile)
204{
205 FILE *fp;
206 char line[1000];
207 int old_cnt = 0;
208
209 fp = fopen(infofile, "r");
210 if (!fp)
211 return 1; /* nonexisting is not an error. */
212
213 while (fgets(line, sizeof(line), fp)) {
214 int len = strlen(line);
215 if (line[len-1] == '\n')
216 line[len-1] = 0;
217
218 switch (line[0]) {
219 case 'P': /* P name */
220 if (parse_pack_def(line, old_cnt++))
221 goto out_stale;
222 break;
223 case 'D': /* D ix dep-ix1 dep-ix2... */
224 if (parse_depend_def(line))
225 goto out_stale;
226 break;
227 case 'T': /* T ix sha1 type */
228 if (parse_head_def(line))
229 goto out_stale;
230 break;
231 default:
232 error("unrecognized: %s", line);
233 break;
234 }
235 }
236 fclose(fp);
237 return 0;
238 out_stale:
239 fclose(fp);
240 return 1;
241}
242
243/* We sort the packs according to the date of the latest commit. That
244 * in turn indicates how young the pack is, and in general we would
245 * want to depend on younger packs.
246 */
247static unsigned long get_latest_commit_date(struct packed_git *p)
248{
249 unsigned char sha1[20];
250 struct object *o;
251 int num = num_packed_objects(p);
252 int i;
253 unsigned long latest = 0;
254
255 for (i = 0; i < num; i++) {
256 if (nth_packed_object_sha1(p, i, sha1))
257 die("corrupt pack file %s?", p->pack_name);
258 if ((o = parse_object_cheap(sha1)) == NULL)
259 die("cannot parse %s", sha1_to_hex(sha1));
260 if (o->type == commit_type) {
261 struct commit *commit = (struct commit *)o;
262 if (latest < commit->date)
263 latest = commit->date;
264 }
265 }
266 return latest;
267}
268
269static int compare_info(const void *a_, const void *b_)
270{
271 struct pack_info * const* a = a_;
272 struct pack_info * const* b = b_;
273
274 if (0 <= (*a)->old_num && 0 <= (*b)->old_num)
275 /* Keep the order in the original */
276 return (*a)->old_num - (*b)->old_num;
277 else if (0 <= (*a)->old_num)
278 /* Only A existed in the original so B is obviously newer */
279 return -1;
280 else if (0 <= (*b)->old_num)
281 /* The other way around. */
282 return 1;
283
284 if ((*a)->latest < (*b)->latest)
285 return -1;
286 else if ((*a)->latest == (*b)->latest)
287 return 0;
288 else
289 return 1;
290}
291
292static void init_pack_info(const char *infofile, int force)
293{
294 struct packed_git *p;
295 int stale;
296 int i = 0;
297 char *dep_temp;
298
299 objdir = get_object_directory();
300 objdirlen = strlen(objdir);
301
302 prepare_packed_git();
303 for (p = packed_git; p; p = p->next) {
304 /* we ignore things on alternate path since they are
305 * not available to the pullers in general.
306 */
307 if (strncmp(p->pack_name, objdir, objdirlen) ||
308 strncmp(p->pack_name + objdirlen, "/pack/", 6))
309 continue;
310 i++;
311 }
312 num_pack = i;
313 info = xcalloc(num_pack, sizeof(struct pack_info *));
314 for (i = 0, p = packed_git; p; p = p->next) {
315 if (strncmp(p->pack_name, objdir, objdirlen) ||
316 p->pack_name[objdirlen] != '/')
317 continue;
318 info[i] = xcalloc(1, sizeof(struct pack_info) + num_pack);
319 info[i]->p = p;
320 info[i]->old_num = -1;
321 i++;
322 }
323
324 if (infofile && !force)
325 stale = read_pack_info_file(infofile);
326 else
327 stale = 1;
328
329 for (i = 0; i < num_pack; i++) {
330 if (stale) {
331 info[i]->old_num = -1;
332 memset(info[i]->dep, 0, num_pack);
333 info[i]->nr_heads = 0;
334 }
335 if (info[i]->old_num < 0)
336 info[i]->latest = get_latest_commit_date(info[i]->p);
337 }
338
339 qsort(info, num_pack, sizeof(info[0]), compare_info);
340 for (i = 0; i < num_pack; i++)
341 info[i]->new_num = i;
342
343 /* we need to fix up the dependency information
344 * for the old ones.
345 */
346 dep_temp = NULL;
347 for (i = 0; i < num_pack; i++) {
348 int old;
349
350 if (info[i]->old_num < 0)
351 continue;
352 if (! dep_temp)
353 dep_temp = xmalloc(num_pack);
354 memset(dep_temp, 0, num_pack);
355 for (old = 0; old < num_pack; old++) {
356 struct pack_info *base;
357 if (!info[i]->dep[old])
358 continue;
359 base = find_pack_by_old_num(old);
360 if (!base)
361 die("internal error renumbering");
362 dep_temp[base->new_num] = 1;
363 }
364 memcpy(info[i]->dep, dep_temp, num_pack);
365 }
366 free(dep_temp);
367}
368
369static void write_pack_info_file(FILE *fp)
370{
371 int i, j;
372 for (i = 0; i < num_pack; i++)
373 fprintf(fp, "P %s\n", info[i]->p->pack_name + objdirlen + 6);
374
375 for (i = 0; i < num_pack; i++) {
376 fprintf(fp, "D %1d", i);
377 for (j = 0; j < num_pack; j++) {
378 if ((i == j) || !(info[i]->dep[j]))
379 continue;
380 fprintf(fp, " %1d", j);
381 }
382 fputc('\n', fp);
383 }
384
385 for (i = 0; i < num_pack; i++) {
386 struct pack_info *this = info[i];
387 for (j = 0; j < this->nr_heads; j++) {
388 struct object *o = lookup_object(this->head[j]);
389 fprintf(fp, "T %1d %s %s\n",
390 i, sha1_to_hex(this->head[j]), o->type);
391 }
392 }
393
394}
395
396#define REFERENCED 01
397#define INTERNAL 02
398#define EMITTED 04
399
400static void show(struct object *o, int pack_ix)
401{
402 /*
403 * We are interested in objects that are not referenced,
404 * and objects that are referenced but not internal.
405 */
406 if (o->flags & EMITTED)
407 return;
408
409 if (!(o->flags & REFERENCED))
410 add_head_def(info[pack_ix], o->sha1);
411 else if ((o->flags & REFERENCED) && !(o->flags & INTERNAL)) {
412 int i;
413
414 /* Which pack contains this object? That is what
415 * pack_ix can depend on. We earlier sorted info
416 * array from youngest to oldest, so try newer packs
417 * first to favor them here.
418 */
419 for (i = num_pack - 1; 0 <= i; i--) {
420 struct packed_git *p = info[i]->p;
421 struct pack_entry ent;
422 if (find_pack_entry_one(o->sha1, &ent, p)) {
423 info[pack_ix]->dep[i] = 1;
424 break;
425 }
426 }
427 }
428 o->flags |= EMITTED;
429}
430
431static void find_pack_info_one(int pack_ix)
432{
433 unsigned char sha1[20];
434 struct object *o;
435 struct object_list *ref;
436 int i;
437 struct packed_git *p = info[pack_ix]->p;
438 int num = num_packed_objects(p);
439
440 /* Scan objects, clear flags from all the edge ones and
441 * internal ones, possibly marked in the previous round.
442 */
443 for (i = 0; i < num; i++) {
444 if (nth_packed_object_sha1(p, i, sha1))
445 die("corrupt pack file %s?", p->pack_name);
446 if ((o = lookup_object(sha1)) == NULL)
447 die("cannot parse %s", sha1_to_hex(sha1));
448 for (ref = o->refs; ref; ref = ref->next)
449 ref->item->flags = 0;
450 o->flags = 0;
451 }
452
453 /* Mark all the internal ones */
454 for (i = 0; i < num; i++) {
455 if (nth_packed_object_sha1(p, i, sha1))
456 die("corrupt pack file %s?", p->pack_name);
457 if ((o = lookup_object(sha1)) == NULL)
458 die("cannot find %s", sha1_to_hex(sha1));
459 for (ref = o->refs; ref; ref = ref->next)
460 ref->item->flags |= REFERENCED;
461 o->flags |= INTERNAL;
462 }
463
464 for (i = 0; i < num; i++) {
465 if (nth_packed_object_sha1(p, i, sha1))
466 die("corrupt pack file %s?", p->pack_name);
467 if ((o = lookup_object(sha1)) == NULL)
468 die("cannot find %s", sha1_to_hex(sha1));
469
470 show(o, pack_ix);
471 for (ref = o->refs; ref; ref = ref->next)
472 show(ref->item, pack_ix);
473 }
474
475}
476
477static void find_pack_info(void)
478{
479 int i;
480 for (i = 0; i < num_pack; i++) {
481 /* The packed objects are cast in stone, and a head
482 * in a pack will stay as head, so is the set of missing
483 * objects. If the repo has been reorganized and we
484 * are missing some packs available back then, we have
485 * already discarded the info read from the file, so
486 * we will find (old_num < 0) in that case.
487 */
488 if (0 <= info[i]->old_num)
489 continue;
490 find_pack_info_one(i);
491 }
492}
493
494static int update_info_packs(int force)
495{
496 char infofile[PATH_MAX];
497 char name[PATH_MAX];
498 int namelen;
499 FILE *fp;
500
501 namelen = sprintf(infofile, "%s/info/packs", get_object_directory());
502 strcpy(name, infofile);
503 strcpy(name + namelen, "+");
504
505 init_pack_info(infofile, force);
506 find_pack_info();
507
508 safe_create_leading_directories(name);
509 fp = fopen(name, "w");
510 if (!fp)
511 return error("cannot open %s", name);
512 write_pack_info_file(fp);
513 fclose(fp);
514 rename(name, infofile);
515 return 0;
516}
517
518/* rev-cache */
519static int record_rev_cache_ref(const char *path, const unsigned char *sha1)
520{
521 struct commit *commit;
522 if (!(commit = lookup_commit_reference(sha1)))
523 return error("not a commit: %s", sha1_to_hex(sha1));
524 return record_rev_cache(commit->object.sha1, NULL);
525}
526
527static int update_info_revs(int force)
528{
529 char *path0 = strdup(git_path("info/rev-cache"));
530 int len = strlen(path0);
531 char *path1 = xmalloc(len + 2);
532
533 strcpy(path1, path0);
534 strcpy(path1 + len, "+");
535
536 /* read existing rev-cache */
537 if (!force)
538 read_rev_cache(path0, NULL, 0);
539 safe_create_leading_directories(path0);
540
541 for_each_ref(record_rev_cache_ref);
542
543 /* update the rev-cache database */
544 write_rev_cache(path1, force ? "/dev/null" : path0);
545 rename(path1, path0);
546 free(path1);
547 free(path0);
548 return 0;
549}
550
551/* public */
552int update_server_info(int force)
553{
554 /* We would add more dumb-server support files later,
555 * including index of available pack files and their
556 * intended audiences.
557 */
558 int errs = 0;
559
560 errs = errs | update_info_refs(force);
561 errs = errs | update_info_packs(force);
562 errs = errs | update_info_revs(force);
563
564 return errs;
565}