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