server-info.con commit server-info: stop sorting packs by latest date. (d5eac49)
   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        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} **info;
  54static int num_pack;
  55static const char *objdir;
  56static int objdirlen;
  57
  58static struct object *parse_object_cheap(const unsigned char *sha1)
  59{
  60        struct object *o;
  61
  62        if ((o = parse_object(sha1)) == NULL)
  63                return NULL;
  64        if (o->type == commit_type) {
  65                struct commit *commit = (struct commit *)o;
  66                free(commit->buffer);
  67                commit->buffer = NULL;
  68        } else if (o->type == tree_type) {
  69                struct tree *tree = (struct tree *)o;
  70                struct tree_entry_list *e, *n;
  71                for (e = tree->entries; e; e = n) {
  72                        free(e->name);
  73                        e->name = NULL;
  74                        n = e->next;
  75                        free(e);
  76                }
  77                tree->entries = NULL;
  78        }
  79        return o;
  80}
  81
  82static struct pack_info *find_pack_by_name(const char *name)
  83{
  84        int i;
  85        for (i = 0; i < num_pack; i++) {
  86                struct packed_git *p = info[i]->p;
  87                /* skip "/pack/" after ".git/objects" */
  88                if (!strcmp(p->pack_name + objdirlen + 6, name))
  89                        return info[i];
  90        }
  91        return NULL;
  92}
  93
  94static struct pack_info *find_pack_by_old_num(int old_num)
  95{
  96        int i;
  97        for (i = 0; i < num_pack; i++)
  98                if (info[i]->old_num == old_num)
  99                        return info[i];
 100        return NULL;
 101}
 102
 103static int add_head_def(struct pack_info *this, unsigned char *sha1)
 104{
 105        if (this->nr_alloc <= this->nr_heads) {
 106                this->nr_alloc = alloc_nr(this->nr_alloc);
 107                this->head = xrealloc(this->head, this->nr_alloc * 20);
 108        }
 109        memcpy(this->head[this->nr_heads++], sha1, 20);
 110        return 0;
 111}
 112
 113/* Returns non-zero when we detect that the info in the
 114 * old file is useless.
 115 */
 116static int parse_pack_def(const char *line, int old_cnt)
 117{
 118        struct pack_info *i = find_pack_by_name(line + 2);
 119        if (i) {
 120                i->old_num = old_cnt;
 121                return 0;
 122        }
 123        else {
 124                /* The file describes a pack that is no longer here */
 125                return 1;
 126        }
 127}
 128
 129/* Returns non-zero when we detect that the info in the old file is useless.
 130 */
 131static int parse_head_def(char *line)
 132{
 133        unsigned char sha1[20];
 134        unsigned long num;
 135        char *cp, *ep;
 136        struct pack_info *this;
 137        struct object *o;
 138
 139        cp = line + 2;
 140        num = strtoul(cp, &ep, 10);
 141        if (ep == cp || *ep++ != ' ')
 142                return error("invalid input ix %s", line);
 143        this = find_pack_by_old_num(num);
 144        if (!this)
 145                return 1; /* You know the drill. */
 146        if (get_sha1_hex(ep, sha1) || ep[40] != ' ')
 147                return error("invalid input sha1 %s (%s)", line, ep);
 148        if ((o = parse_object_cheap(sha1)) == NULL)
 149                return error("no such object: %s", line);
 150        return add_head_def(this, sha1);
 151}
 152
 153/* Returns non-zero when we detect that the info in the
 154 * old file is useless.
 155 */
 156static int read_pack_info_file(const char *infofile)
 157{
 158        FILE *fp;
 159        char line[1000];
 160        int old_cnt = 0;
 161
 162        fp = fopen(infofile, "r");
 163        if (!fp)
 164                return 1; /* nonexisting is not an error. */
 165
 166        while (fgets(line, sizeof(line), fp)) {
 167                int len = strlen(line);
 168                if (line[len-1] == '\n')
 169                        line[len-1] = 0;
 170
 171                switch (line[0]) {
 172                case 'P': /* P name */
 173                        if (parse_pack_def(line, old_cnt++))
 174                                goto out_stale;
 175                        break;
 176                case 'D': /* we used to emit D but that was misguided. */
 177                        goto out_stale;
 178                        break;
 179                case 'T': /* T ix sha1 type */
 180                        if (parse_head_def(line))
 181                                goto out_stale;
 182                        break;
 183                default:
 184                        error("unrecognized: %s", line);
 185                        break;
 186                }
 187        }
 188        fclose(fp);
 189        return 0;
 190 out_stale:
 191        fclose(fp);
 192        return 1;
 193}
 194
 195static int compare_info(const void *a_, const void *b_)
 196{
 197        struct pack_info * const* a = a_;
 198        struct pack_info * const* b = b_;
 199
 200        if (0 <= (*a)->old_num && 0 <= (*b)->old_num)
 201                /* Keep the order in the original */
 202                return (*a)->old_num - (*b)->old_num;
 203        else if (0 <= (*a)->old_num)
 204                /* Only A existed in the original so B is obviously newer */
 205                return -1;
 206        else if (0 <= (*b)->old_num)
 207                /* The other way around. */
 208                return 1;
 209
 210        /* then it does not matter but at least keep the comparison stable */
 211        return (*a)->p - (*b)->p;
 212}
 213
 214static void init_pack_info(const char *infofile, int force)
 215{
 216        struct packed_git *p;
 217        int stale;
 218        int i = 0;
 219
 220        objdir = get_object_directory();
 221        objdirlen = strlen(objdir);
 222
 223        prepare_packed_git();
 224        for (p = packed_git; p; p = p->next) {
 225                /* we ignore things on alternate path since they are
 226                 * not available to the pullers in general.
 227                 */
 228                if (strncmp(p->pack_name, objdir, objdirlen) ||
 229                    strncmp(p->pack_name + objdirlen, "/pack/", 6))
 230                        continue;
 231                i++;
 232        }
 233        num_pack = i;
 234        info = xcalloc(num_pack, sizeof(struct pack_info *));
 235        for (i = 0, p = packed_git; p; p = p->next) {
 236                if (strncmp(p->pack_name, objdir, objdirlen) ||
 237                    p->pack_name[objdirlen] != '/')
 238                        continue;
 239                info[i] = xcalloc(1, sizeof(struct pack_info));
 240                info[i]->p = p;
 241                info[i]->old_num = -1;
 242                i++;
 243        }
 244
 245        if (infofile && !force)
 246                stale = read_pack_info_file(infofile);
 247        else
 248                stale = 1;
 249
 250        for (i = 0; i < num_pack; i++) {
 251                if (stale) {
 252                        info[i]->old_num = -1;
 253                        info[i]->nr_heads = 0;
 254                }
 255        }
 256
 257        /* renumber them */
 258        qsort(info, num_pack, sizeof(info[0]), compare_info);
 259        for (i = 0; i < num_pack; i++)
 260                info[i]->new_num = i;
 261}
 262
 263static void write_pack_info_file(FILE *fp)
 264{
 265        int i, j;
 266        for (i = 0; i < num_pack; i++)
 267                fprintf(fp, "P %s\n", info[i]->p->pack_name + objdirlen + 6);
 268        for (i = 0; i < num_pack; i++) {
 269                struct pack_info *this = info[i];
 270                for (j = 0; j < this->nr_heads; j++) {
 271                        struct object *o = lookup_object(this->head[j]);
 272                        fprintf(fp, "T %1d %s %s\n",
 273                                i, sha1_to_hex(this->head[j]), o->type);
 274                }
 275        }
 276
 277}
 278
 279#define REFERENCED 01
 280#define EMITTED   04
 281
 282static void show(struct object *o, int pack_ix)
 283{
 284        /*
 285         * We are interested in objects that are not referenced,
 286         */
 287        if (o->flags & EMITTED)
 288                return;
 289
 290        if (!(o->flags & REFERENCED))
 291                add_head_def(info[pack_ix], o->sha1);
 292        o->flags |= EMITTED;
 293}
 294
 295static void find_pack_info_one(int pack_ix)
 296{
 297        unsigned char sha1[20];
 298        struct object *o;
 299        int i;
 300        struct packed_git *p = info[pack_ix]->p;
 301        int num = num_packed_objects(p);
 302
 303        /* Scan objects, clear flags from all the edge ones and
 304         * internal ones, possibly marked in the previous round.
 305         */
 306        for (i = 0; i < num; i++) {
 307                if (nth_packed_object_sha1(p, i, sha1))
 308                        die("corrupt pack file %s?", p->pack_name);
 309                if ((o = parse_object_cheap(sha1)) == NULL)
 310                        die("cannot parse %s", sha1_to_hex(sha1));
 311                if (o->refs) {
 312                        struct object_refs *refs = o->refs;
 313                        int j;
 314                        for (j = 0; j < refs->count; j++)
 315                                refs->ref[j]->flags = 0;
 316                }
 317                o->flags = 0;
 318        }
 319
 320        /* Mark all the referenced ones */
 321        for (i = 0; i < num; i++) {
 322                if (nth_packed_object_sha1(p, i, sha1))
 323                        die("corrupt pack file %s?", p->pack_name);
 324                if ((o = lookup_object(sha1)) == NULL)
 325                        die("cannot find %s", sha1_to_hex(sha1));
 326                if (o->refs) {
 327                        struct object_refs *refs = o->refs;
 328                        int j;
 329                        for (j = 0; j < refs->count; j++)
 330                                refs->ref[j]->flags |= REFERENCED;
 331                }
 332        }
 333
 334        for (i = 0; i < num; i++) {
 335                if (nth_packed_object_sha1(p, i, sha1))
 336                        die("corrupt pack file %s?", p->pack_name);
 337                if ((o = lookup_object(sha1)) == NULL)
 338                        die("cannot find %s", sha1_to_hex(sha1));
 339                show(o, pack_ix);
 340        }
 341
 342}
 343
 344static void find_pack_info(void)
 345{
 346        int i;
 347        for (i = 0; i < num_pack; i++) {
 348                /* The packed objects are cast in stone, and a head
 349                 * in a pack will stay as head, so is the set of missing
 350                 * objects.  If the repo has been reorganized and we
 351                 * are missing some packs available back then, we have
 352                 * already discarded the info read from the file, so
 353                 * we will find (old_num < 0) in that case.
 354                 */
 355                if (0 <= info[i]->old_num)
 356                        continue;
 357                find_pack_info_one(i);
 358        }
 359}
 360
 361static int update_info_packs(int force)
 362{
 363        char infofile[PATH_MAX];
 364        char name[PATH_MAX];
 365        int namelen;
 366        FILE *fp;
 367
 368        namelen = sprintf(infofile, "%s/info/packs", get_object_directory());
 369        strcpy(name, infofile);
 370        strcpy(name + namelen, "+");
 371
 372        init_pack_info(infofile, force);
 373        find_pack_info();
 374
 375        safe_create_leading_directories(name);
 376        fp = fopen(name, "w");
 377        if (!fp)
 378                return error("cannot open %s", name);
 379        write_pack_info_file(fp);
 380        fclose(fp);
 381        rename(name, infofile);
 382        return 0;
 383}
 384
 385/* public */
 386int update_server_info(int force)
 387{
 388        /* We would add more dumb-server support files later,
 389         * including index of available pack files and their
 390         * intended audiences.
 391         */
 392        int errs = 0;
 393
 394        errs = errs | update_info_refs(force);
 395        errs = errs | update_info_packs(force);
 396
 397        /* remove leftover rev-cache file if there is any */
 398        unlink(git_path("info/rev-cache"));
 399
 400        return errs;
 401}