server-info.con commit server-info.c: drop unused D lines. (6f42f89)
   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, path, 0);
  17                if (o)
  18                        fprintf(info_ref_fp, "%s        %s^{}\n",
  19                                sha1_to_hex(o->sha1), path);
  20        }
  21        return 0;
  22}
  23
  24static int update_info_refs(int force)
  25{
  26        char *path0 = strdup(git_path("info/refs"));
  27        int len = strlen(path0);
  28        char *path1 = xmalloc(len + 2);
  29
  30        strcpy(path1, path0);
  31        strcpy(path1 + len, "+");
  32
  33        safe_create_leading_directories(path0);
  34        info_ref_fp = fopen(path1, "w");
  35        if (!info_ref_fp)
  36                return error("unable to update %s", path0);
  37        for_each_ref(add_info_ref);
  38        fclose(info_ref_fp);
  39        rename(path1, path0);
  40        free(path0);
  41        free(path1);
  42        return 0;
  43}
  44
  45/* packs */
  46static struct pack_info {
  47        unsigned long latest;
  48        struct packed_git *p;
  49        int old_num;
  50        int new_num;
  51        int nr_alloc;
  52        int nr_heads;
  53        unsigned char (*head)[20];
  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                return 1;
 127        }
 128}
 129
 130/* Returns non-zero when we detect that the info in the old file is useless.
 131 */
 132static int parse_head_def(char *line)
 133{
 134        unsigned char sha1[20];
 135        unsigned long num;
 136        char *cp, *ep;
 137        struct pack_info *this;
 138        struct object *o;
 139
 140        cp = line + 2;
 141        num = strtoul(cp, &ep, 10);
 142        if (ep == cp || *ep++ != ' ')
 143                return error("invalid input ix %s", line);
 144        this = find_pack_by_old_num(num);
 145        if (!this)
 146                return 1; /* You know the drill. */
 147        if (get_sha1_hex(ep, sha1) || ep[40] != ' ')
 148                return error("invalid input sha1 %s (%s)", line, ep);
 149        if ((o = parse_object_cheap(sha1)) == NULL)
 150                return error("no such object: %s", line);
 151        return add_head_def(this, sha1);
 152}
 153
 154/* Returns non-zero when we detect that the info in the
 155 * old file is useless.
 156 */
 157static int read_pack_info_file(const char *infofile)
 158{
 159        FILE *fp;
 160        char line[1000];
 161        int old_cnt = 0;
 162
 163        fp = fopen(infofile, "r");
 164        if (!fp)
 165                return 1; /* nonexisting is not an error. */
 166
 167        while (fgets(line, sizeof(line), fp)) {
 168                int len = strlen(line);
 169                if (line[len-1] == '\n')
 170                        line[len-1] = 0;
 171
 172                switch (line[0]) {
 173                case 'P': /* P name */
 174                        if (parse_pack_def(line, old_cnt++))
 175                                goto out_stale;
 176                        break;
 177                case 'D': /* we used to emit D but that was misguided. */
 178                        goto out_stale;
 179                        break;
 180                case 'T': /* T ix sha1 type */
 181                        if (parse_head_def(line))
 182                                goto out_stale;
 183                        break;
 184                default:
 185                        error("unrecognized: %s", line);
 186                        break;
 187                }
 188        }
 189        fclose(fp);
 190        return 0;
 191 out_stale:
 192        fclose(fp);
 193        return 1;
 194}
 195
 196/* We sort the packs according to the date of the latest commit.  That
 197 * in turn indicates how young the pack is, and in general we would
 198 * want to depend on younger packs.
 199 */
 200static unsigned long get_latest_commit_date(struct packed_git *p)
 201{
 202        unsigned char sha1[20];
 203        struct object *o;
 204        int num = num_packed_objects(p);
 205        int i;
 206        unsigned long latest = 0;
 207
 208        for (i = 0; i < num; i++) {
 209                if (nth_packed_object_sha1(p, i, sha1))
 210                        die("corrupt pack file %s?", p->pack_name);
 211                if ((o = parse_object_cheap(sha1)) == NULL)
 212                        die("cannot parse %s", sha1_to_hex(sha1));
 213                if (o->type == commit_type) {
 214                        struct commit *commit = (struct commit *)o;
 215                        if (latest < commit->date)
 216                                latest = commit->date;
 217                }
 218        }
 219        return latest;
 220}
 221
 222static int compare_info(const void *a_, const void *b_)
 223{
 224        struct pack_info * const* a = a_;
 225        struct pack_info * const* b = b_;
 226
 227        if (0 <= (*a)->old_num && 0 <= (*b)->old_num)
 228                /* Keep the order in the original */
 229                return (*a)->old_num - (*b)->old_num;
 230        else if (0 <= (*a)->old_num)
 231                /* Only A existed in the original so B is obviously newer */
 232                return -1;
 233        else if (0 <= (*b)->old_num)
 234                /* The other way around. */
 235                return 1;
 236
 237        if ((*a)->latest < (*b)->latest)
 238                return -1;
 239        else if ((*a)->latest == (*b)->latest)
 240                return 0;
 241        else
 242                return 1;
 243}
 244
 245static void init_pack_info(const char *infofile, int force)
 246{
 247        struct packed_git *p;
 248        int stale;
 249        int i = 0;
 250
 251        objdir = get_object_directory();
 252        objdirlen = strlen(objdir);
 253
 254        prepare_packed_git();
 255        for (p = packed_git; p; p = p->next) {
 256                /* we ignore things on alternate path since they are
 257                 * not available to the pullers in general.
 258                 */
 259                if (strncmp(p->pack_name, objdir, objdirlen) ||
 260                    strncmp(p->pack_name + objdirlen, "/pack/", 6))
 261                        continue;
 262                i++;
 263        }
 264        num_pack = i;
 265        info = xcalloc(num_pack, sizeof(struct pack_info *));
 266        for (i = 0, p = packed_git; p; p = p->next) {
 267                if (strncmp(p->pack_name, objdir, objdirlen) ||
 268                    p->pack_name[objdirlen] != '/')
 269                        continue;
 270                info[i] = xcalloc(1, sizeof(struct pack_info));
 271                info[i]->p = p;
 272                info[i]->old_num = -1;
 273                i++;
 274        }
 275
 276        if (infofile && !force)
 277                stale = read_pack_info_file(infofile);
 278        else
 279                stale = 1;
 280
 281        for (i = 0; i < num_pack; i++) {
 282                if (stale) {
 283                        info[i]->old_num = -1;
 284                        info[i]->nr_heads = 0;
 285                }
 286                if (info[i]->old_num < 0)
 287                        info[i]->latest = get_latest_commit_date(info[i]->p);
 288        }
 289
 290        /* renumber them */
 291        qsort(info, num_pack, sizeof(info[0]), compare_info);
 292        for (i = 0; i < num_pack; i++)
 293                info[i]->new_num = i;
 294}
 295
 296static void write_pack_info_file(FILE *fp)
 297{
 298        int i, j;
 299        for (i = 0; i < num_pack; i++)
 300                fprintf(fp, "P %s\n", info[i]->p->pack_name + objdirlen + 6);
 301        for (i = 0; i < num_pack; i++) {
 302                struct pack_info *this = info[i];
 303                for (j = 0; j < this->nr_heads; j++) {
 304                        struct object *o = lookup_object(this->head[j]);
 305                        fprintf(fp, "T %1d %s %s\n",
 306                                i, sha1_to_hex(this->head[j]), o->type);
 307                }
 308        }
 309
 310}
 311
 312#define REFERENCED 01
 313#define EMITTED   04
 314
 315static void show(struct object *o, int pack_ix)
 316{
 317        /*
 318         * We are interested in objects that are not referenced,
 319         */
 320        if (o->flags & EMITTED)
 321                return;
 322
 323        if (!(o->flags & REFERENCED))
 324                add_head_def(info[pack_ix], o->sha1);
 325        o->flags |= EMITTED;
 326}
 327
 328static void find_pack_info_one(int pack_ix)
 329{
 330        unsigned char sha1[20];
 331        struct object *o;
 332        int i;
 333        struct packed_git *p = info[pack_ix]->p;
 334        int num = num_packed_objects(p);
 335
 336        /* Scan objects, clear flags from all the edge ones and
 337         * internal ones, possibly marked in the previous round.
 338         */
 339        for (i = 0; i < num; i++) {
 340                if (nth_packed_object_sha1(p, i, sha1))
 341                        die("corrupt pack file %s?", p->pack_name);
 342                if ((o = lookup_object(sha1)) == NULL)
 343                        die("cannot parse %s", sha1_to_hex(sha1));
 344                if (o->refs) {
 345                        struct object_refs *refs = o->refs;
 346                        int j;
 347                        for (j = 0; j < refs->count; j++)
 348                                refs->ref[j]->flags = 0;
 349                }
 350                o->flags = 0;
 351        }
 352
 353        /* Mark all the referenced ones */
 354        for (i = 0; i < num; i++) {
 355                if (nth_packed_object_sha1(p, i, sha1))
 356                        die("corrupt pack file %s?", p->pack_name);
 357                if ((o = lookup_object(sha1)) == NULL)
 358                        die("cannot find %s", sha1_to_hex(sha1));
 359                if (o->refs) {
 360                        struct object_refs *refs = o->refs;
 361                        int j;
 362                        for (j = 0; j < refs->count; j++)
 363                                refs->ref[j]->flags |= REFERENCED;
 364                }
 365        }
 366
 367        for (i = 0; i < num; i++) {
 368                if (nth_packed_object_sha1(p, i, sha1))
 369                        die("corrupt pack file %s?", p->pack_name);
 370                if ((o = lookup_object(sha1)) == NULL)
 371                        die("cannot find %s", sha1_to_hex(sha1));
 372                show(o, pack_ix);
 373        }
 374
 375}
 376
 377static void find_pack_info(void)
 378{
 379        int i;
 380        for (i = 0; i < num_pack; i++) {
 381                /* The packed objects are cast in stone, and a head
 382                 * in a pack will stay as head, so is the set of missing
 383                 * objects.  If the repo has been reorganized and we
 384                 * are missing some packs available back then, we have
 385                 * already discarded the info read from the file, so
 386                 * we will find (old_num < 0) in that case.
 387                 */
 388                if (0 <= info[i]->old_num)
 389                        continue;
 390                find_pack_info_one(i);
 391        }
 392}
 393
 394static int update_info_packs(int force)
 395{
 396        char infofile[PATH_MAX];
 397        char name[PATH_MAX];
 398        int namelen;
 399        FILE *fp;
 400
 401        namelen = sprintf(infofile, "%s/info/packs", get_object_directory());
 402        strcpy(name, infofile);
 403        strcpy(name + namelen, "+");
 404
 405        init_pack_info(infofile, force);
 406        find_pack_info();
 407
 408        safe_create_leading_directories(name);
 409        fp = fopen(name, "w");
 410        if (!fp)
 411                return error("cannot open %s", name);
 412        write_pack_info_file(fp);
 413        fclose(fp);
 414        rename(name, infofile);
 415        return 0;
 416}
 417
 418/* public */
 419int update_server_info(int force)
 420{
 421        /* We would add more dumb-server support files later,
 422         * including index of available pack files and their
 423         * intended audiences.
 424         */
 425        int errs = 0;
 426
 427        errs = errs | update_info_refs(force);
 428        errs = errs | update_info_packs(force);
 429
 430        /* remove leftover rev-cache file if there is any */
 431        unlink(git_path("info/rev-cache"));
 432
 433        return errs;
 434}