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