convert-cache.con commit [PATCH] checkout-cache: add "-n" option (32718b6)
   1#define _XOPEN_SOURCE /* glibc2 needs this */
   2#include <time.h>
   3#include <ctype.h>
   4#include "cache.h"
   5
   6struct entry {
   7        unsigned char old_sha1[20];
   8        unsigned char new_sha1[20];
   9        int converted;
  10};
  11
  12#define MAXOBJECTS (1000000)
  13
  14static struct entry *convert[MAXOBJECTS];
  15static int nr_convert;
  16
  17static struct entry * convert_entry(unsigned char *sha1);
  18
  19static struct entry *insert_new(unsigned char *sha1, int pos)
  20{
  21        struct entry *new = malloc(sizeof(struct entry));
  22
  23        memset(new, 0, sizeof(*new));
  24        memcpy(new->old_sha1, sha1, 20);
  25        memmove(convert + pos + 1, convert + pos, (nr_convert - pos) * sizeof(struct entry *));
  26        convert[pos] = new;
  27        nr_convert++;
  28        if (nr_convert == MAXOBJECTS)
  29                die("you're kidding me - hit maximum object limit");
  30        return new;
  31}
  32
  33static struct entry *lookup_entry(unsigned char *sha1)
  34{
  35        int low = 0, high = nr_convert;
  36
  37        while (low < high) {
  38                int next = (low + high) / 2;
  39                struct entry *n = convert[next];
  40                int cmp = memcmp(sha1, n->old_sha1, 20);
  41                if (!cmp)
  42                        return n;
  43                if (cmp < 0) {
  44                        high = next;
  45                        continue;
  46                }
  47                low = next+1;
  48        }
  49        return insert_new(sha1, low);
  50}
  51
  52static void convert_binary_sha1(void *buffer)
  53{
  54        struct entry *entry = convert_entry(buffer);
  55        memcpy(buffer, entry->new_sha1, 20);
  56}
  57
  58static void convert_ascii_sha1(void *buffer)
  59{
  60        unsigned char sha1[20];
  61        struct entry *entry;
  62
  63        if (get_sha1_hex(buffer, sha1))
  64                die("bad sha1");
  65        entry = convert_entry(sha1);
  66        memcpy(buffer, sha1_to_hex(entry->new_sha1), 40);
  67}
  68
  69#define ORIG_OFFSET (40)
  70
  71static int prepend_integer(char *buffer, unsigned val, int i)
  72{
  73        buffer[--i] = '\0';
  74        do {
  75                buffer[--i] = '0' + (val % 10);
  76                val /= 10;
  77        } while (val);
  78        return i;
  79}
  80
  81
  82static int write_subdirectory(void *buffer, unsigned long size, const char *base, int baselen, unsigned char *result_sha1)
  83{
  84        char *new = malloc(size + ORIG_OFFSET);
  85        unsigned long newlen = ORIG_OFFSET;
  86        unsigned long used;
  87        int i;
  88
  89        used = 0;
  90        while (size) {
  91                int len = 21 + strlen(buffer);
  92                char *path = strchr(buffer, ' ');
  93                unsigned char *sha1;
  94                unsigned int mode;
  95                char *slash, *origpath;
  96
  97                if (!path || sscanf(buffer, "%o", &mode) != 1)
  98                        die("bad tree conversion");
  99                path++;
 100                if (memcmp(path, base, baselen))
 101                        break;
 102                origpath = path;
 103                path += baselen;
 104                slash = strchr(path, '/');
 105                if (!slash) {
 106                        newlen += sprintf(new + newlen, "%o %s", mode, path);
 107                        new[newlen++] = '\0';
 108                        memcpy(new + newlen, buffer + len - 20, 20);
 109                        newlen += 20;
 110
 111                        used += len;
 112                        size -= len;
 113                        buffer += len;
 114                        continue;
 115                }
 116
 117                newlen += sprintf(new + newlen, "%o %.*s", S_IFDIR, slash - path, path);
 118                new[newlen++] = 0;
 119                sha1 = (unsigned char *)(new + newlen);
 120                newlen += 20;
 121
 122                len = write_subdirectory(buffer, size, origpath, slash-origpath+1, sha1);
 123
 124                used += len;
 125                size -= len;
 126                buffer += len;
 127        }
 128
 129        i = prepend_integer(new, newlen - ORIG_OFFSET, ORIG_OFFSET);
 130        i -= 5;
 131        memcpy(new + i, "tree ", 5);
 132
 133        write_sha1_file(new + i, newlen - i, result_sha1);
 134        free(new);
 135        return used;
 136}
 137
 138static void convert_tree(void *buffer, unsigned long size, unsigned char *result_sha1)
 139{
 140        void *orig_buffer = buffer;
 141        unsigned long orig_size = size;
 142
 143        while (size) {
 144                int len = 1+strlen(buffer);
 145
 146                convert_binary_sha1(buffer + len);
 147
 148                len += 20;
 149                if (len > size)
 150                        die("corrupt tree object");
 151                size -= len;
 152                buffer += len;
 153        }
 154
 155        write_subdirectory(orig_buffer, orig_size, "", 0, result_sha1);
 156}
 157
 158static unsigned long parse_oldstyle_date(const char *buf)
 159{
 160        char c, *p;
 161        char buffer[100];
 162        struct tm tm;
 163        const char *formats[] = {
 164                "%c",
 165                "%a %b %d %T",
 166                "%Z",
 167                "%Y",
 168                " %Y",
 169                NULL
 170        };
 171        /* We only ever did two timezones in the bad old format .. */
 172        const char *timezones[] = {
 173                "PDT", "PST", NULL
 174        };
 175        const char **fmt = formats;
 176
 177        p = buffer;
 178        while (isspace(c = *buf))
 179                buf++;
 180        while ((c = *buf++) != '\n')
 181                *p++ = c;
 182        *p++ = 0;
 183        buf = buffer;
 184        memset(&tm, 0, sizeof(tm));
 185        do {
 186                const char *next = strptime(buf, *fmt, &tm);
 187                if (next) {
 188                        if (!*next)
 189                                return mktime(&tm);
 190                        buf = next;
 191                } else {
 192                        const char **p = timezones;
 193                        while (isspace(*buf))
 194                                buf++;
 195                        while (*p) {
 196                                if (!memcmp(buf, *p, strlen(*p))) {
 197                                        buf += strlen(*p);
 198                                        break;
 199                                }
 200                                p++;
 201                        }
 202                }
 203                fmt++;
 204        } while (*buf && *fmt);
 205        printf("left: %s\n", buf);
 206        return mktime(&tm);                             
 207}
 208
 209static int convert_date_line(char *dst, void **buf, unsigned long *sp)
 210{
 211        unsigned long size = *sp;
 212        char *line = *buf;
 213        char *next = strchr(line, '\n');
 214        char *date = strchr(line, '>');
 215        int len;
 216
 217        if (!next || !date)
 218                die("missing or bad author/committer line %s", line);
 219        next++; date += 2;
 220
 221        *buf = next;
 222        *sp = size - (next - line);
 223
 224        len = date - line;
 225        memcpy(dst, line, len);
 226        dst += len;
 227
 228        /* Is it already in new format? */
 229        if (isdigit(*date)) {
 230                int datelen = next - date;
 231                memcpy(dst, date, datelen);
 232                printf("new format date '%.*s'?\n", datelen-1, date);
 233                return len + datelen;
 234        }
 235
 236        /*
 237         * Hacky hacky: one of the sparse old-style commits does not have
 238         * any date at all, but we can fake it by using the committer date.
 239         */
 240        if (*date == '\n' && strchr(next, '>'))
 241                date = strchr(next, '>')+2;
 242
 243        return len + sprintf(dst, "%lu -0700\n", parse_oldstyle_date(date));
 244}
 245
 246static void convert_date(void *buffer, unsigned long size, unsigned char *result_sha1)
 247{
 248        char *new = malloc(size + ORIG_OFFSET + 100);
 249        unsigned long newlen = ORIG_OFFSET;
 250        int i;
 251
 252        // "tree <sha1>\n"
 253        memcpy(new + newlen, buffer, 46);
 254        newlen += 46;
 255        buffer += 46;
 256        size -= 46;
 257
 258        // "parent <sha1>\n"
 259        while (!memcmp(buffer, "parent ", 7)) {
 260                memcpy(new + newlen, buffer, 48);
 261                newlen += 48;
 262                buffer += 48;
 263                size -= 48;
 264        }
 265
 266        // "author xyz <xyz> date"
 267        newlen += convert_date_line(new + newlen, &buffer, &size);
 268        // "committer xyz <xyz> date"
 269        newlen += convert_date_line(new + newlen, &buffer, &size);
 270
 271        // Rest
 272        memcpy(new + newlen, buffer, size);
 273        newlen += size;
 274
 275        i = prepend_integer(new, newlen - ORIG_OFFSET, ORIG_OFFSET);
 276        i -= 7;
 277        memcpy(new + i, "commit ", 7);
 278
 279        write_sha1_file(new + i, newlen - i, result_sha1);
 280        free(new);      
 281}
 282
 283static void convert_commit(void *buffer, unsigned long size, unsigned char *result_sha1)
 284{
 285        void *orig_buffer = buffer;
 286        unsigned long orig_size = size;
 287
 288        convert_ascii_sha1(buffer+5);
 289        buffer += 46;    /* "tree " + "hex sha1" + "\n" */
 290        while (!memcmp(buffer, "parent ", 7)) {
 291                convert_ascii_sha1(buffer+7);
 292                buffer += 48;
 293        }
 294        convert_date(orig_buffer, orig_size, result_sha1);
 295}
 296
 297static struct entry * convert_entry(unsigned char *sha1)
 298{
 299        struct entry *entry = lookup_entry(sha1);
 300        char type[20];
 301        void *buffer, *data;
 302        unsigned long size, offset;
 303
 304        if (entry->converted)
 305                return entry;
 306        data = read_sha1_file(sha1, type, &size);
 307        if (!data)
 308                die("unable to read object %s", sha1_to_hex(sha1));
 309
 310        buffer = malloc(size + 100);
 311        offset = sprintf(buffer, "%s %lu", type, size)+1;
 312        memcpy(buffer + offset, data, size);
 313        
 314        if (!strcmp(type, "blob")) {
 315                write_sha1_file(buffer, size + offset, entry->new_sha1);
 316        } else if (!strcmp(type, "tree"))
 317                convert_tree(buffer + offset, size, entry->new_sha1);
 318        else if (!strcmp(type, "commit"))
 319                convert_commit(buffer + offset, size, entry->new_sha1);
 320        else
 321                die("unknown object type '%s' in %s", type, sha1_to_hex(sha1));
 322        entry->converted = 1;
 323        free(buffer);
 324        return entry;
 325}
 326
 327int main(int argc, char **argv)
 328{
 329        unsigned char sha1[20];
 330        struct entry *entry;
 331
 332        if (argc != 2 || get_sha1_hex(argv[1], sha1))
 333                usage("convert-cache <sha1>");
 334
 335        entry = convert_entry(sha1);
 336        printf("new sha1: %s\n", sha1_to_hex(entry->new_sha1));
 337        return 0;
 338}