gitweb: Move static files into seperate subdirectory
[gitweb.git] / fast-import.c
index 2105310c5412ac5afea99840080406340e26552b..129a786832c2dc863e33ae2fc039a0212c1751d5 100644 (file)
@@ -164,12 +164,11 @@ Format of STDIN stream:
 
 struct object_entry
 {
+       struct pack_idx_entry idx;
        struct object_entry *next;
-       uint32_t offset;
        uint32_t type : TYPE_BITS,
                pack_id : PACK_ID_BITS,
                depth : DEPTH_BITS;
-       unsigned char sha1[20];
 };
 
 struct object_entry_pool
@@ -192,7 +191,7 @@ struct mark_set
 struct last_object
 {
        struct strbuf data;
-       uint32_t offset;
+       off_t offset;
        unsigned int depth;
        unsigned no_swap : 1;
 };
@@ -245,6 +244,7 @@ struct branch
        const char *name;
        struct tree_entry branch_tree;
        uintmax_t last_commit;
+       uintmax_t num_notes;
        unsigned active : 1;
        unsigned pack_id : PACK_ID_BITS;
        unsigned char sha1[20];
@@ -279,7 +279,7 @@ struct recent_command
 
 /* Configured limits on output */
 static unsigned long max_depth = 10;
-static off_t max_packsize = (1LL << 32) - 1;
+static off_t max_packsize;
 static uintmax_t big_file_threshold = 512 * 1024 * 1024;
 static int force_update;
 static int pack_compression_level = Z_DEFAULT_COMPRESSION;
@@ -296,6 +296,9 @@ static unsigned long branch_count;
 static unsigned long branch_load_count;
 static int failure;
 static FILE *pack_edges;
+static unsigned int show_stats = 1;
+static int global_argc;
+static const char **global_argv;
 
 /* Memory pools */
 static size_t mem_pool_alloc = 2*1024*1024 - sizeof(struct mem_pool);
@@ -309,16 +312,20 @@ static struct atom_str **atom_table;
 
 /* The .pack file being generated */
 static unsigned int pack_id;
+static struct sha1file *pack_file;
 static struct packed_git *pack_data;
 static struct packed_git **all_packs;
-static unsigned long pack_size;
+static off_t pack_size;
 
 /* Table of objects we've written. */
 static unsigned int object_entry_alloc = 5000;
 static struct object_entry_pool *blocks;
 static struct object_entry *object_table[1 << 16];
 static struct mark_set *marks;
-static const char *mark_file;
+static const char *export_marks_file;
+static const char *import_marks_file;
+static int import_marks_file_from_stream;
+static int relative_marks_paths;
 
 /* Our last blob */
 static struct last_object last_blob = { STRBUF_INIT, 0, 0, 0 };
@@ -352,6 +359,9 @@ static struct recent_command *rc_free;
 static unsigned int cmd_save = 100;
 static uintmax_t next_mark;
 static struct strbuf new_data = STRBUF_INIT;
+static int seen_data_command;
+
+static void parse_argv(void);
 
 static void write_branch_report(FILE *rpt, struct branch *b)
 {
@@ -455,8 +465,8 @@ static void write_crash_report(const char *err)
        fputc('\n', rpt);
        fputs("Marks\n", rpt);
        fputs("-----\n", rpt);
-       if (mark_file)
-               fprintf(rpt, "  exported to %s\n", mark_file);
+       if (export_marks_file)
+               fprintf(rpt, "  exported to %s\n", export_marks_file);
        else
                dump_marks_helper(rpt, 0, marks);
 
@@ -511,7 +521,7 @@ static struct object_entry *new_object(unsigned char *sha1)
                alloc_objects(object_entry_alloc);
 
        e = blocks->next_free++;
-       hashcpy(e->sha1, sha1);
+       hashcpy(e->idx.sha1, sha1);
        return e;
 }
 
@@ -520,7 +530,7 @@ static struct object_entry *find_object(unsigned char *sha1)
        unsigned int h = sha1[0] << 8 | sha1[1];
        struct object_entry *e;
        for (e = object_table[h]; e; e = e->next)
-               if (!hashcmp(sha1, e->sha1))
+               if (!hashcmp(sha1, e->idx.sha1))
                        return e;
        return NULL;
 }
@@ -532,7 +542,7 @@ static struct object_entry *insert_object(unsigned char *sha1)
        struct object_entry *p = NULL;
 
        while (e) {
-               if (!hashcmp(sha1, e->sha1))
+               if (!hashcmp(sha1, e->idx.sha1))
                        return e;
                p = e;
                e = e->next;
@@ -540,7 +550,7 @@ static struct object_entry *insert_object(unsigned char *sha1)
 
        e = new_object(sha1);
        e->next = NULL;
-       e->offset = 0;
+       e->idx.offset = 0;
        if (p)
                p->next = e;
        else
@@ -694,6 +704,7 @@ static struct branch *new_branch(const char *name)
        b->table_next_branch = branch_table[hc];
        b->branch_tree.versions[0].mode = S_IFDIR;
        b->branch_tree.versions[1].mode = S_IFDIR;
+       b->num_notes = 0;
        b->active = 0;
        b->pack_id = MAX_PACK_ID;
        branch_table[hc] = b;
@@ -828,11 +839,12 @@ static void start_packfile(void)
        p = xcalloc(1, sizeof(*p) + strlen(tmpfile) + 2);
        strcpy(p->pack_name, tmpfile);
        p->pack_fd = pack_fd;
+       pack_file = sha1fd(pack_fd, p->pack_name);
 
        hdr.hdr_signature = htonl(PACK_SIGNATURE);
        hdr.hdr_version = htonl(2);
        hdr.hdr_entries = 0;
-       write_or_die(p->pack_fd, &hdr, sizeof(hdr));
+       sha1write(pack_file, &hdr, sizeof(hdr));
 
        pack_data = p;
        pack_size = sizeof(hdr);
@@ -842,67 +854,30 @@ static void start_packfile(void)
        all_packs[pack_id] = p;
 }
 
-static int oecmp (const void *a_, const void *b_)
+static const char *create_index(void)
 {
-       struct object_entry *a = *((struct object_entry**)a_);
-       struct object_entry *b = *((struct object_entry**)b_);
-       return hashcmp(a->sha1, b->sha1);
-}
-
-static char *create_index(void)
-{
-       static char tmpfile[PATH_MAX];
-       git_SHA_CTX ctx;
-       struct sha1file *f;
-       struct object_entry **idx, **c, **last, *e;
+       const char *tmpfile;
+       struct pack_idx_entry **idx, **c, **last;
+       struct object_entry *e;
        struct object_entry_pool *o;
-       uint32_t array[256];
-       int i, idx_fd;
 
-       /* Build the sorted table of object IDs. */
-       idx = xmalloc(object_count * sizeof(struct object_entry*));
+       /* Build the table of object IDs. */
+       idx = xmalloc(object_count * sizeof(*idx));
        c = idx;
        for (o = blocks; o; o = o->next_pool)
                for (e = o->next_free; e-- != o->entries;)
                        if (pack_id == e->pack_id)
-                               *c++ = e;
+                               *c++ = &e->idx;
        last = idx + object_count;
        if (c != last)
                die("internal consistency error creating the index");
-       qsort(idx, object_count, sizeof(struct object_entry*), oecmp);
 
-       /* Generate the fan-out array. */
-       c = idx;
-       for (i = 0; i < 256; i++) {
-               struct object_entry **next = c;
-               while (next < last) {
-                       if ((*next)->sha1[0] != i)
-                               break;
-                       next++;
-               }
-               array[i] = htonl(next - idx);
-               c = next;
-       }
-
-       idx_fd = odb_mkstemp(tmpfile, sizeof(tmpfile),
-                            "pack/tmp_idx_XXXXXX");
-       f = sha1fd(idx_fd, tmpfile);
-       sha1write(f, array, 256 * sizeof(int));
-       git_SHA1_Init(&ctx);
-       for (c = idx; c != last; c++) {
-               uint32_t offset = htonl((*c)->offset);
-               sha1write(f, &offset, 4);
-               sha1write(f, (*c)->sha1, sizeof((*c)->sha1));
-               git_SHA1_Update(&ctx, (*c)->sha1, 20);
-       }
-       sha1write(f, pack_data->sha1, sizeof(pack_data->sha1));
-       sha1close(f, NULL, CSUM_FSYNC);
+       tmpfile = write_idx_file(NULL, idx, object_count, pack_data->sha1);
        free(idx);
-       git_SHA1_Final(pack_data->sha1, &ctx);
        return tmpfile;
 }
 
-static char *keep_pack(char *curr_index_name)
+static char *keep_pack(const char *curr_index_name)
 {
        static char name[PATH_MAX];
        static const char *keep_msg = "fast-import";
@@ -924,6 +899,7 @@ static char *keep_pack(char *curr_index_name)
                 get_object_directory(), sha1_to_hex(pack_data->sha1));
        if (move_temp_to_file(curr_index_name, name))
                die("cannot store index file");
+       free((void *)curr_index_name);
        return name;
 }
 
@@ -946,15 +922,17 @@ static void end_packfile(void)
 
        clear_delta_base_cache();
        if (object_count) {
+               unsigned char cur_pack_sha1[20];
                char *idx_name;
                int i;
                struct branch *b;
                struct tag *t;
 
                close_pack_windows(pack_data);
+               sha1close(pack_file, cur_pack_sha1, 0);
                fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1,
                                    pack_data->pack_name, object_count,
-                                   NULL, 0);
+                                   cur_pack_sha1, pack_size);
                close(pack_data->pack_fd);
                idx_name = keep_pack(create_index());
 
@@ -1002,29 +980,6 @@ static void cycle_packfile(void)
        start_packfile();
 }
 
-static size_t encode_header(
-       enum object_type type,
-       uintmax_t size,
-       unsigned char *hdr)
-{
-       int n = 1;
-       unsigned char c;
-
-       if (type < OBJ_COMMIT || type > OBJ_REF_DELTA)
-               die("bad type %d", type);
-
-       c = (type << 4) | (size & 15);
-       size >>= 4;
-       while (size) {
-               *hdr++ = c | 0x80;
-               c = size & 0x7f;
-               size >>= 7;
-               n++;
-       }
-       *hdr = c;
-       return n;
-}
-
 static int store_object(
        enum object_type type,
        struct strbuf *dat,
@@ -1052,25 +1007,21 @@ static int store_object(
        e = insert_object(sha1);
        if (mark)
                insert_mark(mark, e);
-       if (e->offset) {
+       if (e->idx.offset) {
                duplicate_count_by_type[type]++;
                return 1;
        } else if (find_sha1_pack(sha1, packed_git)) {
                e->type = type;
                e->pack_id = MAX_PACK_ID;
-               e->offset = 1; /* just not zero! */
+               e->idx.offset = 1; /* just not zero! */
                duplicate_count_by_type[type]++;
                return 1;
        }
 
-       if (last && last->data.buf && last->depth < max_depth) {
+       if (last && last->data.buf && last->depth < max_depth && dat->len > 20) {
                delta = diff_delta(last->data.buf, last->data.len,
                        dat->buf, dat->len,
-                       &deltalen, 0);
-               if (delta && deltalen >= dat->len) {
-                       free(delta);
-                       delta = NULL;
-               }
+                       &deltalen, dat->len - 20);
        } else
                delta = NULL;
 
@@ -1090,7 +1041,7 @@ static int store_object(
        deflateEnd(&s);
 
        /* Determine if we should auto-checkpoint. */
-       if ((pack_size + 60 + s.total_out) > max_packsize
+       if ((max_packsize && (pack_size + 60 + s.total_out) > max_packsize)
                || (pack_size + 60 + s.total_out) < pack_size) {
 
                /* This new object needs to *not* have the current pack_id. */
@@ -1116,36 +1067,40 @@ static int store_object(
 
        e->type = type;
        e->pack_id = pack_id;
-       e->offset = pack_size;
+       e->idx.offset = pack_size;
        object_count++;
        object_count_by_type[type]++;
 
+       crc32_begin(pack_file);
+
        if (delta) {
-               unsigned long ofs = e->offset - last->offset;
+               off_t ofs = e->idx.offset - last->offset;
                unsigned pos = sizeof(hdr) - 1;
 
                delta_count_by_type[type]++;
                e->depth = last->depth + 1;
 
-               hdrlen = encode_header(OBJ_OFS_DELTA, deltalen, hdr);
-               write_or_die(pack_data->pack_fd, hdr, hdrlen);
+               hdrlen = encode_in_pack_object_header(OBJ_OFS_DELTA, deltalen, hdr);
+               sha1write(pack_file, hdr, hdrlen);
                pack_size += hdrlen;
 
                hdr[pos] = ofs & 127;
                while (ofs >>= 7)
                        hdr[--pos] = 128 | (--ofs & 127);
-               write_or_die(pack_data->pack_fd, hdr + pos, sizeof(hdr) - pos);
+               sha1write(pack_file, hdr + pos, sizeof(hdr) - pos);
                pack_size += sizeof(hdr) - pos;
        } else {
                e->depth = 0;
-               hdrlen = encode_header(type, dat->len, hdr);
-               write_or_die(pack_data->pack_fd, hdr, hdrlen);
+               hdrlen = encode_in_pack_object_header(type, dat->len, hdr);
+               sha1write(pack_file, hdr, hdrlen);
                pack_size += hdrlen;
        }
 
-       write_or_die(pack_data->pack_fd, out, s.total_out);
+       sha1write(pack_file, out, s.total_out);
        pack_size += s.total_out;
 
+       e->idx.crc32 = crc32_end(pack_file);
+
        free(out);
        free(delta);
        if (last) {
@@ -1154,18 +1109,23 @@ static int store_object(
                } else {
                        strbuf_swap(&last->data, dat);
                }
-               last->offset = e->offset;
+               last->offset = e->idx.offset;
                last->depth = e->depth;
        }
        return 0;
 }
 
-static void truncate_pack(off_t to)
+static void truncate_pack(off_t to, git_SHA_CTX *ctx)
 {
        if (ftruncate(pack_data->pack_fd, to)
         || lseek(pack_data->pack_fd, to, SEEK_SET) != to)
                die_errno("cannot truncate pack to skip duplicate");
        pack_size = to;
+
+       /* yes this is a layering violation */
+       pack_file->total = to;
+       pack_file->offset = 0;
+       pack_file->ctx = *ctx;
 }
 
 static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
@@ -1178,16 +1138,21 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
        unsigned long hdrlen;
        off_t offset;
        git_SHA_CTX c;
+       git_SHA_CTX pack_file_ctx;
        z_stream s;
        int status = Z_OK;
 
        /* Determine if we should auto-checkpoint. */
-       if ((pack_size + 60 + len) > max_packsize
+       if ((max_packsize && (pack_size + 60 + len) > max_packsize)
                || (pack_size + 60 + len) < pack_size)
                cycle_packfile();
 
        offset = pack_size;
 
+       /* preserve the pack_file SHA1 ctx in case we have to truncate later */
+       sha1flush(pack_file);
+       pack_file_ctx = pack_file->ctx;
+
        hdrlen = snprintf((char *)out_buf, out_sz, "blob %" PRIuMAX, len) + 1;
        if (out_sz <= hdrlen)
                die("impossibly large object header");
@@ -1195,10 +1160,12 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
        git_SHA1_Init(&c);
        git_SHA1_Update(&c, out_buf, hdrlen);
 
+       crc32_begin(pack_file);
+
        memset(&s, 0, sizeof(s));
        deflateInit(&s, pack_compression_level);
 
-       hdrlen = encode_header(OBJ_BLOB, len, out_buf);
+       hdrlen = encode_in_pack_object_header(OBJ_BLOB, len, out_buf);
        if (out_sz <= hdrlen)
                die("impossibly large object header");
 
@@ -1222,7 +1189,7 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
 
                if (!s.avail_out || status == Z_STREAM_END) {
                        size_t n = s.next_out - out_buf;
-                       write_or_die(pack_data->pack_fd, out_buf, n);
+                       sha1write(pack_file, out_buf, n);
                        pack_size += n;
                        s.next_out = out_buf;
                        s.avail_out = out_sz;
@@ -1248,22 +1215,23 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
        if (mark)
                insert_mark(mark, e);
 
-       if (e->offset) {
+       if (e->idx.offset) {
                duplicate_count_by_type[OBJ_BLOB]++;
-               truncate_pack(offset);
+               truncate_pack(offset, &pack_file_ctx);
 
        } else if (find_sha1_pack(sha1, packed_git)) {
                e->type = OBJ_BLOB;
                e->pack_id = MAX_PACK_ID;
-               e->offset = 1; /* just not zero! */
+               e->idx.offset = 1; /* just not zero! */
                duplicate_count_by_type[OBJ_BLOB]++;
-               truncate_pack(offset);
+               truncate_pack(offset, &pack_file_ctx);
 
        } else {
                e->depth = 0;
                e->type = OBJ_BLOB;
                e->pack_id = pack_id;
-               e->offset = offset;
+               e->idx.offset = offset;
+               e->idx.crc32 = crc32_end(pack_file);
                object_count++;
                object_count_by_type[OBJ_BLOB]++;
        }
@@ -1306,6 +1274,7 @@ static void *gfi_unpack_entry(
                 * the newly written data.
                 */
                close_pack_windows(p);
+               sha1flush(pack_file);
 
                /* We have to offer 20 bytes additional on the end of
                 * the packfile as the core unpacker code assumes the
@@ -1315,7 +1284,7 @@ static void *gfi_unpack_entry(
                 */
                p->pack_size = pack_size + 20;
        }
-       return unpack_entry(p, oe->offset, &type, sizep);
+       return unpack_entry(p, oe->idx.offset, &type, sizep);
 }
 
 static const char *get_mode(const char *str, uint16_t *modep)
@@ -1446,7 +1415,7 @@ static void store_tree(struct tree_entry *root)
        if (S_ISDIR(root->versions[0].mode) && le && le->pack_id == pack_id) {
                mktree(t, 0, &old_tree);
                lo.data = old_tree;
-               lo.offset = le->offset;
+               lo.offset = le->idx.offset;
                lo.depth = t->delta_depth;
        }
 
@@ -1704,7 +1673,7 @@ static void dump_marks_helper(FILE *f,
                for (k = 0; k < 1024; k++) {
                        if (m->data.marked[k])
                                fprintf(f, ":%" PRIuMAX " %s\n", base + k,
-                                       sha1_to_hex(m->data.marked[k]->sha1));
+                                       sha1_to_hex(m->data.marked[k]->idx.sha1));
                }
        }
 }
@@ -1715,13 +1684,13 @@ static void dump_marks(void)
        int mark_fd;
        FILE *f;
 
-       if (!mark_file)
+       if (!export_marks_file)
                return;
 
-       mark_fd = hold_lock_file_for_update(&mark_lock, mark_file, 0);
+       mark_fd = hold_lock_file_for_update(&mark_lock, export_marks_file, 0);
        if (mark_fd < 0) {
                failure |= error("Unable to write marks file %s: %s",
-                       mark_file, strerror(errno));
+                       export_marks_file, strerror(errno));
                return;
        }
 
@@ -1730,7 +1699,7 @@ static void dump_marks(void)
                int saved_errno = errno;
                rollback_lock_file(&mark_lock);
                failure |= error("Unable to write marks file %s: %s",
-                       mark_file, strerror(saved_errno));
+                       export_marks_file, strerror(saved_errno));
                return;
        }
 
@@ -1746,7 +1715,7 @@ static void dump_marks(void)
                int saved_errno = errno;
                rollback_lock_file(&mark_lock);
                failure |= error("Unable to write marks file %s: %s",
-                       mark_file, strerror(saved_errno));
+                       export_marks_file, strerror(saved_errno));
                return;
        }
 
@@ -1754,11 +1723,47 @@ static void dump_marks(void)
                int saved_errno = errno;
                rollback_lock_file(&mark_lock);
                failure |= error("Unable to commit marks file %s: %s",
-                       mark_file, strerror(saved_errno));
+                       export_marks_file, strerror(saved_errno));
                return;
        }
 }
 
+static void read_marks(void)
+{
+       char line[512];
+       FILE *f = fopen(import_marks_file, "r");
+       if (!f)
+               die_errno("cannot read '%s'", import_marks_file);
+       while (fgets(line, sizeof(line), f)) {
+               uintmax_t mark;
+               char *end;
+               unsigned char sha1[20];
+               struct object_entry *e;
+
+               end = strchr(line, '\n');
+               if (line[0] != ':' || !end)
+                       die("corrupt mark line: %s", line);
+               *end = 0;
+               mark = strtoumax(line + 1, &end, 10);
+               if (!mark || end == line + 1
+                       || *end != ' ' || get_sha1(end + 1, sha1))
+                       die("corrupt mark line: %s", line);
+               e = find_object(sha1);
+               if (!e) {
+                       enum object_type type = sha1_object_info(sha1, NULL);
+                       if (type < 0)
+                               die("object not found: %s", sha1_to_hex(sha1));
+                       e = insert_object(sha1);
+                       e->type = type;
+                       e->pack_id = MAX_PACK_ID;
+                       e->idx.offset = 1; /* just not zero! */
+               }
+               insert_mark(mark, e);
+       }
+       fclose(f);
+}
+
+
 static int read_next_command(void)
 {
        static int stdin_eof = 0;
@@ -1779,6 +1784,12 @@ static int read_next_command(void)
                        if (stdin_eof)
                                return EOF;
 
+                       if (!seen_data_command
+                               && prefixcmp(command_buf.buf, "feature ")
+                               && prefixcmp(command_buf.buf, "option ")) {
+                               parse_argv();
+                       }
+
                        rc = rc_free;
                        if (rc)
                                rc_free = rc->next;
@@ -1998,6 +2009,109 @@ static void load_branch(struct branch *b)
        }
 }
 
+static unsigned char convert_num_notes_to_fanout(uintmax_t num_notes)
+{
+       unsigned char fanout = 0;
+       while ((num_notes >>= 8))
+               fanout++;
+       return fanout;
+}
+
+static void construct_path_with_fanout(const char *hex_sha1,
+               unsigned char fanout, char *path)
+{
+       unsigned int i = 0, j = 0;
+       if (fanout >= 20)
+               die("Too large fanout (%u)", fanout);
+       while (fanout) {
+               path[i++] = hex_sha1[j++];
+               path[i++] = hex_sha1[j++];
+               path[i++] = '/';
+               fanout--;
+       }
+       memcpy(path + i, hex_sha1 + j, 40 - j);
+       path[i + 40 - j] = '\0';
+}
+
+static uintmax_t do_change_note_fanout(
+               struct tree_entry *orig_root, struct tree_entry *root,
+               char *hex_sha1, unsigned int hex_sha1_len,
+               char *fullpath, unsigned int fullpath_len,
+               unsigned char fanout)
+{
+       struct tree_content *t = root->tree;
+       struct tree_entry *e, leaf;
+       unsigned int i, tmp_hex_sha1_len, tmp_fullpath_len;
+       uintmax_t num_notes = 0;
+       unsigned char sha1[20];
+       char realpath[60];
+
+       for (i = 0; t && i < t->entry_count; i++) {
+               e = t->entries[i];
+               tmp_hex_sha1_len = hex_sha1_len + e->name->str_len;
+               tmp_fullpath_len = fullpath_len;
+
+               /*
+                * We're interested in EITHER existing note entries (entries
+                * with exactly 40 hex chars in path, not including directory
+                * separators), OR directory entries that may contain note
+                * entries (with < 40 hex chars in path).
+                * Also, each path component in a note entry must be a multiple
+                * of 2 chars.
+                */
+               if (!e->versions[1].mode ||
+                   tmp_hex_sha1_len > 40 ||
+                   e->name->str_len % 2)
+                       continue;
+
+               /* This _may_ be a note entry, or a subdir containing notes */
+               memcpy(hex_sha1 + hex_sha1_len, e->name->str_dat,
+                      e->name->str_len);
+               if (tmp_fullpath_len)
+                       fullpath[tmp_fullpath_len++] = '/';
+               memcpy(fullpath + tmp_fullpath_len, e->name->str_dat,
+                      e->name->str_len);
+               tmp_fullpath_len += e->name->str_len;
+               fullpath[tmp_fullpath_len] = '\0';
+
+               if (tmp_hex_sha1_len == 40 && !get_sha1_hex(hex_sha1, sha1)) {
+                       /* This is a note entry */
+                       construct_path_with_fanout(hex_sha1, fanout, realpath);
+                       if (!strcmp(fullpath, realpath)) {
+                               /* Note entry is in correct location */
+                               num_notes++;
+                               continue;
+                       }
+
+                       /* Rename fullpath to realpath */
+                       if (!tree_content_remove(orig_root, fullpath, &leaf))
+                               die("Failed to remove path %s", fullpath);
+                       tree_content_set(orig_root, realpath,
+                               leaf.versions[1].sha1,
+                               leaf.versions[1].mode,
+                               leaf.tree);
+               } else if (S_ISDIR(e->versions[1].mode)) {
+                       /* This is a subdir that may contain note entries */
+                       if (!e->tree)
+                               load_tree(e);
+                       num_notes += do_change_note_fanout(orig_root, e,
+                               hex_sha1, tmp_hex_sha1_len,
+                               fullpath, tmp_fullpath_len, fanout);
+               }
+
+               /* The above may have reallocated the current tree_content */
+               t = root->tree;
+       }
+       return num_notes;
+}
+
+static uintmax_t change_note_fanout(struct tree_entry *root,
+               unsigned char fanout)
+{
+       char hex_sha1[40], path[60];
+       return do_change_note_fanout(root, root, hex_sha1, 0, path, 0, fanout);
+}
+
 static void file_change_m(struct branch *b)
 {
        const char *p = command_buf.buf + 2;
@@ -2027,7 +2141,7 @@ static void file_change_m(struct branch *b)
        if (*p == ':') {
                char *x;
                oe = find_mark(strtoumax(p + 1, &x, 10));
-               hashcpy(sha1, oe->sha1);
+               hashcpy(sha1, oe->idx.sha1);
                p = x;
        } else if (!prefixcmp(p, "inline")) {
                inline_data = 1;
@@ -2145,20 +2259,22 @@ static void file_change_cr(struct branch *b, int rename)
                leaf.tree);
 }
 
-static void note_change_n(struct branch *b)
+static void note_change_n(struct branch *b, unsigned char old_fanout)
 {
        const char *p = command_buf.buf + 2;
        static struct strbuf uq = STRBUF_INIT;
        struct object_entry *oe = oe;
        struct branch *s;
        unsigned char sha1[20], commit_sha1[20];
+       char path[60];
        uint16_t inline_data = 0;
+       unsigned char new_fanout;
 
        /* <dataref> or 'inline' */
        if (*p == ':') {
                char *x;
                oe = find_mark(strtoumax(p + 1, &x, 10));
-               hashcpy(sha1, oe->sha1);
+               hashcpy(sha1, oe->idx.sha1);
                p = x;
        } else if (!prefixcmp(p, "inline")) {
                inline_data = 1;
@@ -2181,7 +2297,7 @@ static void note_change_n(struct branch *b)
                struct object_entry *commit_oe = find_mark(commit_mark);
                if (commit_oe->type != OBJ_COMMIT)
                        die("Mark :%" PRIuMAX " not a commit", commit_mark);
-               hashcpy(commit_sha1, commit_oe->sha1);
+               hashcpy(commit_sha1, commit_oe->idx.sha1);
        } else if (!get_sha1(p, commit_sha1)) {
                unsigned long size;
                char *buf = read_object_with_reference(commit_sha1,
@@ -2203,7 +2319,7 @@ static void note_change_n(struct branch *b)
                if (oe->type != OBJ_BLOB)
                        die("Not a blob (actually a %s): %s",
                                typename(oe->type), command_buf.buf);
-       } else {
+       } else if (!is_null_sha1(sha1)) {
                enum object_type type = sha1_object_info(sha1, NULL);
                if (type < 0)
                        die("Blob not found: %s", command_buf.buf);
@@ -2212,8 +2328,17 @@ static void note_change_n(struct branch *b)
                            typename(type), command_buf.buf);
        }
 
-       tree_content_set(&b->branch_tree, sha1_to_hex(commit_sha1), sha1,
-               S_IFREG | 0644, NULL);
+       construct_path_with_fanout(sha1_to_hex(commit_sha1), old_fanout, path);
+       if (tree_content_remove(&b->branch_tree, path, NULL))
+               b->num_notes--;
+
+       if (is_null_sha1(sha1))
+               return; /* nothing to insert */
+
+       b->num_notes++;
+       new_fanout = convert_num_notes_to_fanout(b->num_notes);
+       construct_path_with_fanout(sha1_to_hex(commit_sha1), new_fanout, path);
+       tree_content_set(&b->branch_tree, path, sha1, S_IFREG | 0644, NULL);
 }
 
 static void file_change_deleteall(struct branch *b)
@@ -2222,6 +2347,7 @@ static void file_change_deleteall(struct branch *b)
        hashclr(b->branch_tree.versions[0].sha1);
        hashclr(b->branch_tree.versions[1].sha1);
        load_tree(&b->branch_tree);
+       b->num_notes = 0;
 }
 
 static void parse_from_commit(struct branch *b, char *buf, unsigned long size)
@@ -2278,7 +2404,7 @@ static int parse_from(struct branch *b)
                struct object_entry *oe = find_mark(idnum);
                if (oe->type != OBJ_COMMIT)
                        die("Mark :%" PRIuMAX " not a commit", idnum);
-               hashcpy(b->sha1, oe->sha1);
+               hashcpy(b->sha1, oe->idx.sha1);
                if (oe->pack_id != MAX_PACK_ID) {
                        unsigned long size;
                        char *buf = gfi_unpack_entry(oe, &size);
@@ -2313,7 +2439,7 @@ static struct hash_list *parse_merge(unsigned int *count)
                        struct object_entry *oe = find_mark(idnum);
                        if (oe->type != OBJ_COMMIT)
                                die("Mark :%" PRIuMAX " not a commit", idnum);
-                       hashcpy(n->sha1, oe->sha1);
+                       hashcpy(n->sha1, oe->idx.sha1);
                } else if (!get_sha1(from, n->sha1)) {
                        unsigned long size;
                        char *buf = read_object_with_reference(n->sha1,
@@ -2345,6 +2471,7 @@ static void parse_new_commit(void)
        char *committer = NULL;
        struct hash_list *merge_list = NULL;
        unsigned int merge_count;
+       unsigned char prev_fanout, new_fanout;
 
        /* Obtain the branch name from the rest of our command */
        sp = strchr(command_buf.buf, ' ') + 1;
@@ -2375,6 +2502,8 @@ static void parse_new_commit(void)
                load_branch(b);
        }
 
+       prev_fanout = convert_num_notes_to_fanout(b->num_notes);
+
        /* file_change* */
        while (command_buf.len > 0) {
                if (!prefixcmp(command_buf.buf, "M "))
@@ -2386,7 +2515,7 @@ static void parse_new_commit(void)
                else if (!prefixcmp(command_buf.buf, "C "))
                        file_change_cr(b, 0);
                else if (!prefixcmp(command_buf.buf, "N "))
-                       note_change_n(b);
+                       note_change_n(b, prev_fanout);
                else if (!strcmp("deleteall", command_buf.buf))
                        file_change_deleteall(b);
                else {
@@ -2397,6 +2526,10 @@ static void parse_new_commit(void)
                        break;
        }
 
+       new_fanout = convert_num_notes_to_fanout(b->num_notes);
+       if (new_fanout != prev_fanout)
+               b->num_notes = change_note_fanout(&b->branch_tree, new_fanout);
+
        /* build the tree and the commit */
        store_tree(&b->branch_tree);
        hashcpy(b->branch_tree.versions[0].sha1,
@@ -2464,7 +2597,7 @@ static void parse_new_tag(void)
                from_mark = strtoumax(from + 1, NULL, 10);
                oe = find_mark(from_mark);
                type = oe->type;
-               hashcpy(sha1, oe->sha1);
+               hashcpy(sha1, oe->idx.sha1);
        } else if (!get_sha1(from, sha1)) {
                unsigned long size;
                char *buf;
@@ -2552,39 +2685,152 @@ static void parse_progress(void)
        skip_optional_lf();
 }
 
-static void import_marks(const char *input_file)
+static char* make_fast_import_path(const char *path)
 {
-       char line[512];
-       FILE *f = fopen(input_file, "r");
-       if (!f)
-               die_errno("cannot read '%s'", input_file);
-       while (fgets(line, sizeof(line), f)) {
-               uintmax_t mark;
-               char *end;
-               unsigned char sha1[20];
-               struct object_entry *e;
+       struct strbuf abs_path = STRBUF_INIT;
 
-               end = strchr(line, '\n');
-               if (line[0] != ':' || !end)
-                       die("corrupt mark line: %s", line);
-               *end = 0;
-               mark = strtoumax(line + 1, &end, 10);
-               if (!mark || end == line + 1
-                       || *end != ' ' || get_sha1(end + 1, sha1))
-                       die("corrupt mark line: %s", line);
-               e = find_object(sha1);
-               if (!e) {
-                       enum object_type type = sha1_object_info(sha1, NULL);
-                       if (type < 0)
-                               die("object not found: %s", sha1_to_hex(sha1));
-                       e = insert_object(sha1);
-                       e->type = type;
-                       e->pack_id = MAX_PACK_ID;
-                       e->offset = 1; /* just not zero! */
+       if (!relative_marks_paths || is_absolute_path(path))
+               return xstrdup(path);
+       strbuf_addf(&abs_path, "%s/info/fast-import/%s", get_git_dir(), path);
+       return strbuf_detach(&abs_path, NULL);
+}
+
+static void option_import_marks(const char *marks, int from_stream)
+{
+       if (import_marks_file) {
+               if (from_stream)
+                       die("Only one import-marks command allowed per stream");
+
+               /* read previous mark file */
+               if(!import_marks_file_from_stream)
+                       read_marks();
+       }
+
+       import_marks_file = make_fast_import_path(marks);
+       safe_create_leading_directories_const(import_marks_file);
+       import_marks_file_from_stream = from_stream;
+}
+
+static void option_date_format(const char *fmt)
+{
+       if (!strcmp(fmt, "raw"))
+               whenspec = WHENSPEC_RAW;
+       else if (!strcmp(fmt, "rfc2822"))
+               whenspec = WHENSPEC_RFC2822;
+       else if (!strcmp(fmt, "now"))
+               whenspec = WHENSPEC_NOW;
+       else
+               die("unknown --date-format argument %s", fmt);
+}
+
+static void option_depth(const char *depth)
+{
+       max_depth = strtoul(depth, NULL, 0);
+       if (max_depth > MAX_DEPTH)
+               die("--depth cannot exceed %u", MAX_DEPTH);
+}
+
+static void option_active_branches(const char *branches)
+{
+       max_active_branches = strtoul(branches, NULL, 0);
+}
+
+static void option_export_marks(const char *marks)
+{
+       export_marks_file = make_fast_import_path(marks);
+       safe_create_leading_directories_const(export_marks_file);
+}
+
+static void option_export_pack_edges(const char *edges)
+{
+       if (pack_edges)
+               fclose(pack_edges);
+       pack_edges = fopen(edges, "a");
+       if (!pack_edges)
+               die_errno("Cannot open '%s'", edges);
+}
+
+static int parse_one_option(const char *option)
+{
+       if (!prefixcmp(option, "max-pack-size=")) {
+               unsigned long v;
+               if (!git_parse_ulong(option + 14, &v))
+                       return 0;
+               if (v < 8192) {
+                       warning("max-pack-size is now in bytes, assuming --max-pack-size=%lum", v);
+                       v *= 1024 * 1024;
+               } else if (v < 1024 * 1024) {
+                       warning("minimum max-pack-size is 1 MiB");
+                       v = 1024 * 1024;
                }
-               insert_mark(mark, e);
+               max_packsize = v;
+       } else if (!prefixcmp(option, "big-file-threshold=")) {
+               unsigned long v;
+               if (!git_parse_ulong(option + 19, &v))
+                       return 0;
+               big_file_threshold = v;
+       } else if (!prefixcmp(option, "depth=")) {
+               option_depth(option + 6);
+       } else if (!prefixcmp(option, "active-branches=")) {
+               option_active_branches(option + 16);
+       } else if (!prefixcmp(option, "export-pack-edges=")) {
+               option_export_pack_edges(option + 18);
+       } else if (!prefixcmp(option, "quiet")) {
+               show_stats = 0;
+       } else if (!prefixcmp(option, "stats")) {
+               show_stats = 1;
+       } else {
+               return 0;
        }
-       fclose(f);
+
+       return 1;
+}
+
+static int parse_one_feature(const char *feature, int from_stream)
+{
+       if (!prefixcmp(feature, "date-format=")) {
+               option_date_format(feature + 12);
+       } else if (!prefixcmp(feature, "import-marks=")) {
+               option_import_marks(feature + 13, from_stream);
+       } else if (!prefixcmp(feature, "export-marks=")) {
+               option_export_marks(feature + 13);
+       } else if (!prefixcmp(feature, "relative-marks")) {
+               relative_marks_paths = 1;
+       } else if (!prefixcmp(feature, "no-relative-marks")) {
+               relative_marks_paths = 0;
+       } else if (!prefixcmp(feature, "force")) {
+               force_update = 1;
+       } else {
+               return 0;
+       }
+
+       return 1;
+}
+
+static void parse_feature(void)
+{
+       char *feature = command_buf.buf + 8;
+
+       if (seen_data_command)
+               die("Got feature command '%s' after data command", feature);
+
+       if (parse_one_feature(feature, 1))
+               return;
+
+       die("This version of fast-import does not support feature %s.", feature);
+}
+
+static void parse_option(void)
+{
+       char *option = command_buf.buf + 11;
+
+       if (seen_data_command)
+               die("Got option command '%s' after data command", option);
+
+       if (parse_one_option(option))
+               return;
+
+       die("This version of fast-import does not support option: %s", option);
 }
 
 static int git_pack_config(const char *k, const char *v, void *cb)
@@ -2605,6 +2851,17 @@ static int git_pack_config(const char *k, const char *v, void *cb)
                pack_compression_seen = 1;
                return 0;
        }
+       if (!strcmp(k, "pack.indexversion")) {
+               pack_idx_default_version = git_config_int(k, v);
+               if (pack_idx_default_version > 2)
+                       die("bad pack.indexversion=%"PRIu32,
+                           pack_idx_default_version);
+               return 0;
+       }
+       if (!strcmp(k, "pack.packsizelimit")) {
+               max_packsize = git_config_ulong(k, v);
+               return 0;
+       }
        if (!strcmp(k, "core.bigfilethreshold")) {
                long n = git_config_int(k, v);
                big_file_threshold = 0 < n ? n : 0;
@@ -2615,9 +2872,35 @@ static int git_pack_config(const char *k, const char *v, void *cb)
 static const char fast_import_usage[] =
 "git fast-import [--date-format=f] [--max-pack-size=n] [--big-file-threshold=n] [--depth=n] [--active-branches=n] [--export-marks=marks.file]";
 
+static void parse_argv(void)
+{
+       unsigned int i;
+
+       for (i = 1; i < global_argc; i++) {
+               const char *a = global_argv[i];
+
+               if (*a != '-' || !strcmp(a, "--"))
+                       break;
+
+               if (parse_one_option(a + 2))
+                       continue;
+
+               if (parse_one_feature(a + 2, 0))
+                       continue;
+
+               die("unknown option %s", a);
+       }
+       if (i != global_argc)
+               usage(fast_import_usage);
+
+       seen_data_command = 1;
+       if (import_marks_file)
+               read_marks();
+}
+
 int main(int argc, const char **argv)
 {
-       unsigned int i, show_stats = 1;
+       unsigned int i;
 
        git_extract_argv0_path(argv[0]);
 
@@ -2636,57 +2919,8 @@ int main(int argc, const char **argv)
        avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*));
        marks = pool_calloc(1, sizeof(struct mark_set));
 
-       for (i = 1; i < argc; i++) {
-               const char *a = argv[i];
-
-               if (*a != '-' || !strcmp(a, "--"))
-                       break;
-               else if (!prefixcmp(a, "--date-format=")) {
-                       const char *fmt = a + 14;
-                       if (!strcmp(fmt, "raw"))
-                               whenspec = WHENSPEC_RAW;
-                       else if (!strcmp(fmt, "rfc2822"))
-                               whenspec = WHENSPEC_RFC2822;
-                       else if (!strcmp(fmt, "now"))
-                               whenspec = WHENSPEC_NOW;
-                       else
-                               die("unknown --date-format argument %s", fmt);
-               }
-               else if (!prefixcmp(a, "--max-pack-size="))
-                       max_packsize = strtoumax(a + 16, NULL, 0) * 1024 * 1024;
-               else if (!prefixcmp(a, "--big-file-threshold=")) {
-                       unsigned long v;
-                       if (!git_parse_ulong(a + 21, &v))
-                               usage(fast_import_usage);
-                       big_file_threshold = v;
-               } else if (!prefixcmp(a, "--depth=")) {
-                       max_depth = strtoul(a + 8, NULL, 0);
-                       if (max_depth > MAX_DEPTH)
-                               die("--depth cannot exceed %u", MAX_DEPTH);
-               }
-               else if (!prefixcmp(a, "--active-branches="))
-                       max_active_branches = strtoul(a + 18, NULL, 0);
-               else if (!prefixcmp(a, "--import-marks="))
-                       import_marks(a + 15);
-               else if (!prefixcmp(a, "--export-marks="))
-                       mark_file = a + 15;
-               else if (!prefixcmp(a, "--export-pack-edges=")) {
-                       if (pack_edges)
-                               fclose(pack_edges);
-                       pack_edges = fopen(a + 20, "a");
-                       if (!pack_edges)
-                               die_errno("Cannot open '%s'", a + 20);
-               } else if (!strcmp(a, "--force"))
-                       force_update = 1;
-               else if (!strcmp(a, "--quiet"))
-                       show_stats = 0;
-               else if (!strcmp(a, "--stats"))
-                       show_stats = 1;
-               else
-                       die("unknown option %s", a);
-       }
-       if (i != argc)
-               usage(fast_import_usage);
+       global_argc = argc;
+       global_argv = argv;
 
        rc_free = pool_alloc(cmd_save * sizeof(*rc_free));
        for (i = 0; i < (cmd_save - 1); i++)
@@ -2709,9 +2943,20 @@ int main(int argc, const char **argv)
                        parse_checkpoint();
                else if (!prefixcmp(command_buf.buf, "progress "))
                        parse_progress();
+               else if (!prefixcmp(command_buf.buf, "feature "))
+                       parse_feature();
+               else if (!prefixcmp(command_buf.buf, "option git "))
+                       parse_option();
+               else if (!prefixcmp(command_buf.buf, "option "))
+                       /* ignore non-git options*/;
                else
                        die("Unsupported command: %s", command_buf.buf);
        }
+
+       /* argv hasn't been parsed yet, do so */
+       if (!seen_data_command)
+               parse_argv();
+
        end_packfile();
 
        dump_branches();