]> git.ipfire.org Git - thirdparty/git.git/blobdiff - fast-import.c
Merge branch 'jk/fast-import-use-hashmap'
[thirdparty/git.git] / fast-import.c
index 7321ccca57adc6350f209aca0db160b412e04bfd..c98970274c4056178b0e9a37e3ba38b323707b50 100644 (file)
@@ -1,157 +1,3 @@
-/*
-(See Documentation/git-fast-import.txt for maintained documentation.)
-Format of STDIN stream:
-
-  stream ::= cmd*;
-
-  cmd ::= new_blob
-        | new_commit
-        | new_tag
-        | reset_branch
-        | checkpoint
-        | progress
-        ;
-
-  new_blob ::= 'blob' lf
-    mark?
-    file_content;
-  file_content ::= data;
-
-  new_commit ::= 'commit' sp ref_str lf
-    mark?
-    ('author' (sp name)? sp '<' email '>' sp when lf)?
-    'committer' (sp name)? sp '<' email '>' sp when lf
-    commit_msg
-    ('from' sp commit-ish lf)?
-    ('merge' sp commit-ish lf)*
-    (file_change | ls)*
-    lf?;
-  commit_msg ::= data;
-
-  ls ::= 'ls' sp '"' quoted(path) '"' lf;
-
-  file_change ::= file_clr
-    | file_del
-    | file_rnm
-    | file_cpy
-    | file_obm
-    | file_inm;
-  file_clr ::= 'deleteall' lf;
-  file_del ::= 'D' sp path_str lf;
-  file_rnm ::= 'R' sp path_str sp path_str lf;
-  file_cpy ::= 'C' sp path_str sp path_str lf;
-  file_obm ::= 'M' sp mode sp (hexsha1 | idnum) sp path_str lf;
-  file_inm ::= 'M' sp mode sp 'inline' sp path_str lf
-    data;
-  note_obm ::= 'N' sp (hexsha1 | idnum) sp commit-ish lf;
-  note_inm ::= 'N' sp 'inline' sp commit-ish lf
-    data;
-
-  new_tag ::= 'tag' sp tag_str lf
-    'from' sp commit-ish lf
-    ('tagger' (sp name)? sp '<' email '>' sp when lf)?
-    tag_msg;
-  tag_msg ::= data;
-
-  reset_branch ::= 'reset' sp ref_str lf
-    ('from' sp commit-ish lf)?
-    lf?;
-
-  checkpoint ::= 'checkpoint' lf
-    lf?;
-
-  progress ::= 'progress' sp not_lf* lf
-    lf?;
-
-     # note: the first idnum in a stream should be 1 and subsequent
-     # idnums should not have gaps between values as this will cause
-     # the stream parser to reserve space for the gapped values.  An
-     # idnum can be updated in the future to a new object by issuing
-     # a new mark directive with the old idnum.
-     #
-  mark ::= 'mark' sp idnum lf;
-  data ::= (delimited_data | exact_data)
-    lf?;
-
-    # note: delim may be any string but must not contain lf.
-    # data_line may contain any data but must not be exactly
-    # delim.
-  delimited_data ::= 'data' sp '<<' delim lf
-    (data_line lf)*
-    delim lf;
-
-     # note: declen indicates the length of binary_data in bytes.
-     # declen does not include the lf preceding the binary data.
-     #
-  exact_data ::= 'data' sp declen lf
-    binary_data;
-
-     # note: quoted strings are C-style quoting supporting \c for
-     # common escapes of 'c' (e..g \n, \t, \\, \") or \nnn where nnn
-     # is the signed byte value in octal.  Note that the only
-     # characters which must actually be escaped to protect the
-     # stream formatting is: \, " and LF.  Otherwise these values
-     # are UTF8.
-     #
-  commit-ish  ::= (ref_str | hexsha1 | sha1exp_str | idnum);
-  ref_str     ::= ref;
-  sha1exp_str ::= sha1exp;
-  tag_str     ::= tag;
-  path_str    ::= path    | '"' quoted(path)    '"' ;
-  mode        ::= '100644' | '644'
-                | '100755' | '755'
-                | '120000'
-                ;
-
-  declen ::= # unsigned 32 bit value, ascii base10 notation;
-  bigint ::= # unsigned integer value, ascii base10 notation;
-  binary_data ::= # file content, not interpreted;
-
-  when         ::= raw_when | rfc2822_when;
-  raw_when     ::= ts sp tz;
-  rfc2822_when ::= # Valid RFC 2822 date and time;
-
-  sp ::= # ASCII space character;
-  lf ::= # ASCII newline (LF) character;
-
-     # note: a colon (':') must precede the numerical value assigned to
-     # an idnum.  This is to distinguish it from a ref or tag name as
-     # GIT does not permit ':' in ref or tag strings.
-     #
-  idnum   ::= ':' bigint;
-  path    ::= # GIT style file path, e.g. "a/b/c";
-  ref     ::= # GIT ref name, e.g. "refs/heads/MOZ_GECKO_EXPERIMENT";
-  tag     ::= # GIT tag name, e.g. "FIREFOX_1_5";
-  sha1exp ::= # Any valid GIT SHA1 expression;
-  hexsha1 ::= # SHA1 in hexadecimal format;
-
-     # note: name and email are UTF8 strings, however name must not
-     # contain '<' or lf and email must not contain any of the
-     # following: '<', '>', lf.
-     #
-  name  ::= # valid GIT author/committer name;
-  email ::= # valid GIT author/committer email;
-  ts    ::= # time since the epoch in seconds, ascii base10 notation;
-  tz    ::= # GIT style timezone;
-
-     # note: comments, get-mark, ls-tree, and cat-blob requests may
-     # appear anywhere in the input, except within a data command. Any
-     # form of the data command always escapes the related input from
-     # comment processing.
-     #
-     # In case it is not clear, the '#' that starts the comment
-     # must be the first character on that line (an lf
-     # preceded it).
-     #
-
-  get_mark ::= 'get-mark' sp idnum lf;
-  cat_blob ::= 'cat-blob' sp (hexsha1 | idnum) lf;
-  ls_tree  ::= 'ls' sp (hexsha1 | idnum) sp path_str lf;
-
-  comment ::= '#' not_lf* lf;
-  not_lf  ::= # Any byte that is not ASCII newline (LF);
-*/
-
 #include "builtin.h"
 #include "cache.h"
 #include "repository.h"
@@ -171,6 +17,8 @@ Format of STDIN stream:
 #include "packfile.h"
 #include "object-store.h"
 #include "mem-pool.h"
+#include "commit-reach.h"
+#include "khash.h"
 
 #define PACK_ID_BITS 16
 #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
@@ -182,14 +30,37 @@ Format of STDIN stream:
  */
 #define NO_DELTA S_ISUID
 
+/*
+ * The amount of additional space required in order to write an object into the
+ * current pack. This is the hash lengths at the end of the pack, plus the
+ * length of one object ID.
+ */
+#define PACK_SIZE_THRESHOLD (the_hash_algo->rawsz * 3)
+
 struct object_entry {
        struct pack_idx_entry idx;
-       struct object_entry *next;
+       struct hashmap_entry ent;
        uint32_t type : TYPE_BITS,
                pack_id : PACK_ID_BITS,
                depth : DEPTH_BITS;
 };
 
+static int object_entry_hashcmp(const void *map_data,
+                               const struct hashmap_entry *eptr,
+                               const struct hashmap_entry *entry_or_key,
+                               const void *keydata)
+{
+       const struct object_id *oid = keydata;
+       const struct object_entry *e1, *e2;
+
+       e1 = container_of(eptr, const struct object_entry, ent);
+       if (oid)
+               return oidcmp(&e1->idx.oid, oid);
+
+       e2 = container_of(entry_or_key, const struct object_entry, ent);
+       return oidcmp(&e1->idx.oid, &e2->idx.oid);
+}
+
 struct object_entry_pool {
        struct object_entry_pool *next_pool;
        struct object_entry *next_free;
@@ -199,6 +70,7 @@ struct object_entry_pool {
 
 struct mark_set {
        union {
+               struct object_id *oids[1024];
                struct object_entry *marked[1024];
                struct mark_set *sets[1024];
        } data;
@@ -277,6 +149,9 @@ struct recent_command {
        char *buf;
 };
 
+typedef void (*mark_set_inserter_t)(struct mark_set *s, struct object_id *oid, uintmax_t mark);
+typedef void (*each_mark_fn_t)(uintmax_t mark, void *obj, void *cbp);
+
 /* Configured limits on output */
 static unsigned long max_depth = 50;
 static off_t max_packsize;
@@ -319,7 +194,7 @@ 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 hashmap object_table;
 static struct mark_set *marks;
 static const char *export_marks_file;
 static const char *import_marks_file;
@@ -368,6 +243,11 @@ static int allow_unsafe_features;
 /* Signal handling */
 static volatile sig_atomic_t checkpoint_requested;
 
+/* Submodule marks */
+static struct string_list sub_marks_from = STRING_LIST_INIT_DUP;
+static struct string_list sub_marks_to = STRING_LIST_INIT_DUP;
+static kh_oid_map_t *sub_oid_map;
+
 /* Where to write output of cat-blob commands */
 static int cat_blob_fd = STDOUT_FILENO;
 
@@ -376,6 +256,29 @@ static void parse_get_mark(const char *p);
 static void parse_cat_blob(const char *p);
 static void parse_ls(const char *p, struct branch *b);
 
+static void for_each_mark(struct mark_set *m, uintmax_t base, each_mark_fn_t callback, void *p)
+{
+       uintmax_t k;
+       if (m->shift) {
+               for (k = 0; k < 1024; k++) {
+                       if (m->data.sets[k])
+                               for_each_mark(m->data.sets[k], base + (k << m->shift), callback, p);
+               }
+       } else {
+               for (k = 0; k < 1024; k++) {
+                       if (m->data.marked[k])
+                               callback(base + k, m->data.marked[k], p);
+               }
+       }
+}
+
+static void dump_marks_fn(uintmax_t mark, void *object, void *cbp) {
+       struct object_entry *e = object;
+       FILE *f = cbp;
+
+       fprintf(f, ":%" PRIuMAX " %s\n", mark, oid_to_hex(&e->idx.oid));
+}
+
 static void write_branch_report(FILE *rpt, struct branch *b)
 {
        fprintf(rpt, "%s:\n", b->name);
@@ -404,8 +307,6 @@ static void write_branch_report(FILE *rpt, struct branch *b)
        fputc('\n', rpt);
 }
 
-static void dump_marks_helper(FILE *, uintmax_t, struct mark_set *);
-
 static void write_crash_report(const char *err)
 {
        char *loc = git_pathdup("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
@@ -484,7 +385,7 @@ static void write_crash_report(const char *err)
        if (export_marks_file)
                fprintf(rpt, "  exported to %s\n", export_marks_file);
        else
-               dump_marks_helper(rpt, 0, marks);
+               for_each_mark(marks, 0, dump_marks_fn, rpt);
 
        fputc('\n', rpt);
        fputs("-------------------\n", rpt);
@@ -570,44 +471,37 @@ static struct object_entry *new_object(struct object_id *oid)
 
 static struct object_entry *find_object(struct object_id *oid)
 {
-       unsigned int h = oid->hash[0] << 8 | oid->hash[1];
-       struct object_entry *e;
-       for (e = object_table[h]; e; e = e->next)
-               if (!oidcmp(oid, &e->idx.oid))
-                       return e;
-       return NULL;
+       return hashmap_get_entry_from_hash(&object_table, oidhash(oid), oid,
+                                          struct object_entry, ent);
 }
 
 static struct object_entry *insert_object(struct object_id *oid)
 {
-       unsigned int h = oid->hash[0] << 8 | oid->hash[1];
-       struct object_entry *e = object_table[h];
+       struct object_entry *e;
+       unsigned int hash = oidhash(oid);
 
-       while (e) {
-               if (!oidcmp(oid, &e->idx.oid))
-                       return e;
-               e = e->next;
+       e = hashmap_get_entry_from_hash(&object_table, hash, oid,
+                                       struct object_entry, ent);
+       if (!e) {
+               e = new_object(oid);
+               e->idx.offset = 0;
+               hashmap_entry_init(&e->ent, hash);
+               hashmap_add(&object_table, &e->ent);
        }
 
-       e = new_object(oid);
-       e->next = object_table[h];
-       e->idx.offset = 0;
-       object_table[h] = e;
        return e;
 }
 
 static void invalidate_pack_id(unsigned int id)
 {
-       unsigned int h;
        unsigned long lu;
        struct tag *t;
+       struct hashmap_iter iter;
+       struct object_entry *e;
 
-       for (h = 0; h < ARRAY_SIZE(object_table); h++) {
-               struct object_entry *e;
-
-               for (e = object_table[h]; e; e = e->next)
-                       if (e->pack_id == id)
-                               e->pack_id = MAX_PACK_ID;
+       hashmap_for_each_entry(&object_table, &iter, e, ent) {
+               if (e->pack_id == id)
+                       e->pack_id = MAX_PACK_ID;
        }
 
        for (lu = 0; lu < branch_table_sz; lu++) {
@@ -639,9 +533,8 @@ static char *pool_strdup(const char *s)
        return r;
 }
 
-static void insert_mark(uintmax_t idnum, struct object_entry *oe)
+static void insert_mark(struct mark_set *s, uintmax_t idnum, struct object_entry *oe)
 {
-       struct mark_set *s = marks;
        while ((idnum >> s->shift) >= 1024) {
                s = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct mark_set));
                s->shift = marks->shift + 10;
@@ -662,10 +555,9 @@ static void insert_mark(uintmax_t idnum, struct object_entry *oe)
        s->data.marked[idnum] = oe;
 }
 
-static struct object_entry *find_mark(uintmax_t idnum)
+static void *find_mark(struct mark_set *s, uintmax_t idnum)
 {
        uintmax_t orig_idnum = idnum;
-       struct mark_set *s = marks;
        struct object_entry *oe = NULL;
        if ((idnum >> s->shift) < 1024) {
                while (s && s->shift) {
@@ -791,7 +683,7 @@ static struct tree_content *grow_tree_content(
        struct tree_content *r = new_tree_content(t->entry_count + amt);
        r->entry_count = t->entry_count;
        r->delta_depth = t->delta_depth;
-       memcpy(r->entries,t->entries,t->entry_count*sizeof(t->entries[0]));
+       COPY_ARRAY(r->entries, t->entries, t->entry_count);
        release_tree_content(t);
        return r;
 }
@@ -896,7 +788,8 @@ static const char *create_index(void)
        if (c != last)
                die("internal consistency error creating the index");
 
-       tmpfile = write_idx_file(NULL, idx, object_count, &pack_idx_opts, pack_data->sha1);
+       tmpfile = write_idx_file(NULL, idx, object_count, &pack_idx_opts,
+                                pack_data->hash);
        free(idx);
        return tmpfile;
 }
@@ -907,7 +800,7 @@ static char *keep_pack(const char *curr_index_name)
        struct strbuf name = STRBUF_INIT;
        int keep_fd;
 
-       odb_pack_name(&name, pack_data->sha1, "keep");
+       odb_pack_name(&name, pack_data->hash, "keep");
        keep_fd = odb_pack_keep(name.buf);
        if (keep_fd < 0)
                die_errno("cannot create keep file");
@@ -915,11 +808,11 @@ static char *keep_pack(const char *curr_index_name)
        if (close(keep_fd))
                die_errno("failed to write keep file");
 
-       odb_pack_name(&name, pack_data->sha1, "pack");
+       odb_pack_name(&name, pack_data->hash, "pack");
        if (finalize_object_file(pack_data->pack_name, name.buf))
                die("cannot store pack file");
 
-       odb_pack_name(&name, pack_data->sha1, "idx");
+       odb_pack_name(&name, pack_data->hash, "idx");
        if (finalize_object_file(curr_index_name, name.buf))
                die("cannot store index file");
        free((void *)curr_index_name);
@@ -933,7 +826,7 @@ static void unkeep_all_packs(void)
 
        for (k = 0; k < pack_id; k++) {
                struct packed_git *p = all_packs[k];
-               odb_pack_name(&name, p->sha1, "keep");
+               odb_pack_name(&name, p->hash, "keep");
                unlink_or_warn(name.buf);
        }
        strbuf_release(&name);
@@ -975,9 +868,9 @@ static void end_packfile(void)
 
                close_pack_windows(pack_data);
                finalize_hashfile(pack_file, cur_pack_oid.hash, 0);
-               fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1,
-                                   pack_data->pack_name, object_count,
-                                   cur_pack_oid.hash, pack_size);
+               fixup_pack_header_footer(pack_data->pack_fd, pack_data->hash,
+                                        pack_data->pack_name, object_count,
+                                        cur_pack_oid.hash, pack_size);
 
                if (object_count <= unpack_limit) {
                        if (!loosen_small_pack(pack_data)) {
@@ -1064,12 +957,12 @@ static int store_object(
 
        e = insert_object(&oid);
        if (mark)
-               insert_mark(mark, e);
+               insert_mark(marks, mark, e);
        if (e->idx.offset) {
                duplicate_count_by_type[type]++;
                return 1;
        } else if (find_sha1_pack(oid.hash,
-                                 get_packed_git(the_repository))) {
+                                 get_all_packs(the_repository))) {
                e->type = type;
                e->pack_id = MAX_PACK_ID;
                e->idx.offset = 1; /* just not zero! */
@@ -1102,8 +995,9 @@ static int store_object(
        git_deflate_end(&s);
 
        /* Determine if we should auto-checkpoint. */
-       if ((max_packsize && (pack_size + 60 + s.total_out) > max_packsize)
-               || (pack_size + 60 + s.total_out) < pack_size) {
+       if ((max_packsize
+               && (pack_size + PACK_SIZE_THRESHOLD + s.total_out) > max_packsize)
+               || (pack_size + PACK_SIZE_THRESHOLD + s.total_out) < pack_size) {
 
                /* This new object needs to *not* have the current pack_id. */
                e->pack_id = pack_id + 1;
@@ -1198,8 +1092,9 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)
        int status = Z_OK;
 
        /* Determine if we should auto-checkpoint. */
-       if ((max_packsize && (pack_size + 60 + len) > max_packsize)
-               || (pack_size + 60 + len) < pack_size)
+       if ((max_packsize
+               && (pack_size + PACK_SIZE_THRESHOLD + len) > max_packsize)
+               || (pack_size + PACK_SIZE_THRESHOLD + len) < pack_size)
                cycle_packfile();
 
        hashfile_checkpoint(pack_file, &checkpoint);
@@ -1260,14 +1155,14 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)
        e = insert_object(&oid);
 
        if (mark)
-               insert_mark(mark, e);
+               insert_mark(marks, mark, e);
 
        if (e->idx.offset) {
                duplicate_count_by_type[OBJ_BLOB]++;
                truncate_pack(&checkpoint);
 
        } else if (find_sha1_pack(oid.hash,
-                                 get_packed_git(the_repository))) {
+                                 get_all_packs(the_repository))) {
                e->type = OBJ_BLOB;
                e->pack_id = MAX_PACK_ID;
                e->idx.offset = 1; /* just not zero! */
@@ -1394,7 +1289,7 @@ static void load_tree(struct tree_entry *root)
                c += e->name->str_len + 1;
                hashcpy(e->versions[0].oid.hash, (unsigned char *)c);
                hashcpy(e->versions[1].oid.hash, (unsigned char *)c);
-               c += GIT_SHA1_RAWSZ;
+               c += the_hash_algo->rawsz;
        }
        free(buf);
 }
@@ -1441,7 +1336,7 @@ static void mktree(struct tree_content *t, int v, struct strbuf *b)
                strbuf_addf(b, "%o %s%c",
                        (unsigned int)(e->versions[v].mode & ~NO_DELTA),
                        e->name->str_dat, '\0');
-               strbuf_add(b, e->versions[v].oid.hash, GIT_SHA1_RAWSZ);
+               strbuf_add(b, e->versions[v].oid.hash, the_hash_algo->rawsz);
        }
 }
 
@@ -1534,7 +1429,7 @@ static int tree_content_set(
                        if (!*slash1) {
                                if (!S_ISDIR(mode)
                                                && e->versions[1].mode == mode
-                                               && !oidcmp(&e->versions[1].oid, oid))
+                                               && oideq(&e->versions[1].oid, oid))
                                        return 0;
                                e->versions[1].mode = mode;
                                oidcpy(&e->versions[1].oid, oid);
@@ -1798,26 +1693,6 @@ static void dump_tags(void)
        strbuf_release(&err);
 }
 
-static void dump_marks_helper(FILE *f,
-       uintmax_t base,
-       struct mark_set *m)
-{
-       uintmax_t k;
-       if (m->shift) {
-               for (k = 0; k < 1024; k++) {
-                       if (m->data.sets[k])
-                               dump_marks_helper(f, base + (k << m->shift),
-                                       m->data.sets[k]);
-               }
-       } else {
-               for (k = 0; k < 1024; k++) {
-                       if (m->data.marked[k])
-                               fprintf(f, ":%" PRIuMAX " %s\n", base + k,
-                                       oid_to_hex(&m->data.marked[k]->idx.oid));
-               }
-       }
-}
-
 static void dump_marks(void)
 {
        struct lock_file mark_lock = LOCK_INIT;
@@ -1847,7 +1722,7 @@ static void dump_marks(void)
                return;
        }
 
-       dump_marks_helper(f, 0, marks);
+       for_each_mark(marks, 0, dump_marks_fn, f);
        if (commit_lock_file(&mark_lock)) {
                failure |= error_errno("Unable to write file %s",
                                       export_marks_file);
@@ -1855,21 +1730,38 @@ static void dump_marks(void)
        }
 }
 
-static void read_marks(void)
+static void insert_object_entry(struct mark_set *s, struct object_id *oid, uintmax_t mark)
+{
+       struct object_entry *e;
+       e = find_object(oid);
+       if (!e) {
+               enum object_type type = oid_object_info(the_repository,
+                                                       oid, NULL);
+               if (type < 0)
+                       die("object not found: %s", oid_to_hex(oid));
+               e = insert_object(oid);
+               e->type = type;
+               e->pack_id = MAX_PACK_ID;
+               e->idx.offset = 1; /* just not zero! */
+       }
+       insert_mark(s, mark, e);
+}
+
+static void insert_oid_entry(struct mark_set *s, struct object_id *oid, uintmax_t mark)
+{
+       insert_mark(s, mark, xmemdupz(oid, sizeof(*oid)));
+}
+
+static void read_mark_file(struct mark_set *s, FILE *f, mark_set_inserter_t inserter)
 {
        char line[512];
-       FILE *f = fopen(import_marks_file, "r");
-       if (f)
-               ;
-       else if (import_marks_file_ignore_missing && errno == ENOENT)
-               goto done; /* Marks file does not exist */
-       else
-               die_errno("cannot read '%s'", import_marks_file);
        while (fgets(line, sizeof(line), f)) {
                uintmax_t mark;
                char *end;
                struct object_id oid;
-               struct object_entry *e;
+
+               /* Ensure SHA-1 objects are padded with zeros. */
+               memset(oid.hash, 0, sizeof(oid.hash));
 
                end = strchr(line, '\n');
                if (line[0] != ':' || !end)
@@ -1877,21 +1769,23 @@ static void read_marks(void)
                *end = 0;
                mark = strtoumax(line + 1, &end, 10);
                if (!mark || end == line + 1
-                       || *end != ' ' || get_oid_hex(end + 1, &oid))
+                       || *end != ' '
+                       || get_oid_hex_any(end + 1, &oid) == GIT_HASH_UNKNOWN)
                        die("corrupt mark line: %s", line);
-               e = find_object(&oid);
-               if (!e) {
-                       enum object_type type = oid_object_info(the_repository,
-                                                               &oid, NULL);
-                       if (type < 0)
-                               die("object not found: %s", oid_to_hex(&oid));
-                       e = insert_object(&oid);
-                       e->type = type;
-                       e->pack_id = MAX_PACK_ID;
-                       e->idx.offset = 1; /* just not zero! */
-               }
-               insert_mark(mark, e);
+               inserter(s, &oid, mark);
        }
+}
+
+static void read_marks(void)
+{
+       FILE *f = fopen(import_marks_file, "r");
+       if (f)
+               ;
+       else if (import_marks_file_ignore_missing && errno == ENOENT)
+               goto done; /* Marks file does not exist */
+       else
+               die_errno("cannot read '%s'", import_marks_file);
+       read_mark_file(marks, f, insert_object_entry);
        fclose(f);
 done:
        import_marks_file_done = 1;
@@ -1908,14 +1802,11 @@ static int read_next_command(void)
        }
 
        for (;;) {
-               const char *p;
-
                if (unread_command_buf) {
                        unread_command_buf = 0;
                } else {
                        struct recent_command *rc;
 
-                       strbuf_detach(&command_buf, NULL);
                        stdin_eof = strbuf_getline_lf(&command_buf, stdin);
                        if (stdin_eof)
                                return EOF;
@@ -1936,20 +1827,12 @@ static int read_next_command(void)
                                free(rc->buf);
                        }
 
-                       rc->buf = command_buf.buf;
+                       rc->buf = xstrdup(command_buf.buf);
                        rc->prev = cmd_tail;
                        rc->next = cmd_hist.prev;
                        rc->prev->next = rc;
                        cmd_tail = rc;
                }
-               if (skip_prefix(command_buf.buf, "get-mark ", &p)) {
-                       parse_get_mark(p);
-                       continue;
-               }
-               if (skip_prefix(command_buf.buf, "cat-blob ", &p)) {
-                       parse_cat_blob(p);
-                       continue;
-               }
                if (command_buf.buf[0] == '#')
                        continue;
                return 0;
@@ -1974,6 +1857,13 @@ static void parse_mark(void)
                next_mark = 0;
 }
 
+static void parse_original_identifier(void)
+{
+       const char *v;
+       if (skip_prefix(command_buf.buf, "original-oid ", &v))
+               read_next_command();
+}
+
 static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
 {
        const char *data;
@@ -1986,7 +1876,6 @@ static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
                char *term = xstrdup(data);
                size_t term_len = command_buf.len - (data - command_buf.buf);
 
-               strbuf_detach(&command_buf, NULL);
                for (;;) {
                        if (strbuf_getline_lf(&command_buf, stdin) == EOF)
                                die("EOF in data (terminator '%s' not found)", term);
@@ -2116,6 +2005,7 @@ static void parse_new_blob(void)
 {
        read_next_command();
        parse_mark();
+       parse_original_identifier();
        parse_and_store_blob(&last_blob, NULL, next_mark);
 }
 
@@ -2198,7 +2088,9 @@ static uintmax_t do_change_note_fanout(
        unsigned int i, tmp_hex_oid_len, tmp_fullpath_len;
        uintmax_t num_notes = 0;
        struct object_id oid;
-       char realpath[60];
+       /* hex oid + '/' between each pair of hex digits + NUL */
+       char realpath[GIT_MAX_HEXSZ + ((GIT_MAX_HEXSZ / 2) - 1) + 1];
+       const unsigned hexsz = the_hash_algo->hexsz;
 
        if (!root->tree)
                load_tree(root);
@@ -2218,7 +2110,7 @@ static uintmax_t do_change_note_fanout(
                 * of 2 chars.
                 */
                if (!e->versions[1].mode ||
-                   tmp_hex_oid_len > GIT_SHA1_HEXSZ ||
+                   tmp_hex_oid_len > hexsz ||
                    e->name->str_len % 2)
                        continue;
 
@@ -2232,7 +2124,7 @@ static uintmax_t do_change_note_fanout(
                tmp_fullpath_len += e->name->str_len;
                fullpath[tmp_fullpath_len] = '\0';
 
-               if (tmp_hex_oid_len == GIT_SHA1_HEXSZ && !get_oid_hex(hex_oid, &oid)) {
+               if (tmp_hex_oid_len == hexsz && !get_oid_hex(hex_oid, &oid)) {
                        /* This is a note entry */
                        if (fanout == 0xff) {
                                /* Counting mode, no rename */
@@ -2279,6 +2171,30 @@ static uintmax_t change_note_fanout(struct tree_entry *root,
        return do_change_note_fanout(root, root, hex_oid, 0, path, 0, fanout);
 }
 
+static int parse_mapped_oid_hex(const char *hex, struct object_id *oid, const char **end)
+{
+       int algo;
+       khiter_t it;
+
+       /* Make SHA-1 object IDs have all-zero padding. */
+       memset(oid->hash, 0, sizeof(oid->hash));
+
+       algo = parse_oid_hex_any(hex, oid, end);
+       if (algo == GIT_HASH_UNKNOWN)
+               return -1;
+
+       it = kh_get_oid_map(sub_oid_map, *oid);
+       /* No such object? */
+       if (it == kh_end(sub_oid_map)) {
+               /* If we're using the same algorithm, pass it through. */
+               if (hash_algos[algo].format_id == the_hash_algo->format_id)
+                       return 0;
+               return -1;
+       }
+       oidcpy(oid, kh_value(sub_oid_map, it));
+       return 0;
+}
+
 /*
  * Given a pointer into a string, parse a mark reference:
  *
@@ -2359,13 +2275,13 @@ static void file_change_m(const char *p, struct branch *b)
        }
 
        if (*p == ':') {
-               oe = find_mark(parse_mark_ref_space(&p));
+               oe = find_mark(marks, parse_mark_ref_space(&p));
                oidcpy(&oid, &oe->idx.oid);
        } else if (skip_prefix(p, "inline ", &p)) {
                inline_data = 1;
                oe = NULL; /* not used with inline_data, but makes gcc happy */
        } else {
-               if (parse_oid_hex(p, &oid, &p))
+               if (parse_mapped_oid_hex(p, &oid, &p))
                        die("Invalid dataref: %s", command_buf.buf);
                oe = find_object(&oid);
                if (*p++ != ' ')
@@ -2406,8 +2322,15 @@ static void file_change_m(const char *p, struct branch *b)
                        strbuf_addstr(&uq, p);
                        p = uq.buf;
                }
-               read_next_command();
-               parse_and_store_blob(&last_blob, &oid, 0);
+               while (read_next_command() != EOF) {
+                       const char *v;
+                       if (skip_prefix(command_buf.buf, "cat-blob ", &v))
+                               parse_cat_blob(v);
+                       else {
+                               parse_and_store_blob(&last_blob, &oid, 0);
+                               break;
+                       }
+               }
        } else {
                enum object_type expected = S_ISDIR(mode) ?
                                                OBJ_TREE: OBJ_BLOB;
@@ -2503,7 +2426,7 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
        struct object_entry *oe;
        struct branch *s;
        struct object_id oid, commit_oid;
-       char path[60];
+       char path[GIT_MAX_RAWSZ * 3];
        uint16_t inline_data = 0;
        unsigned char new_fanout;
 
@@ -2526,13 +2449,13 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
        /* Now parse the notemodify command. */
        /* <dataref> or 'inline' */
        if (*p == ':') {
-               oe = find_mark(parse_mark_ref_space(&p));
+               oe = find_mark(marks, parse_mark_ref_space(&p));
                oidcpy(&oid, &oe->idx.oid);
        } else if (skip_prefix(p, "inline ", &p)) {
                inline_data = 1;
                oe = NULL; /* not used with inline_data, but makes gcc happy */
        } else {
-               if (parse_oid_hex(p, &oid, &p))
+               if (parse_mapped_oid_hex(p, &oid, &p))
                        die("Invalid dataref: %s", command_buf.buf);
                oe = find_object(&oid);
                if (*p++ != ' ')
@@ -2547,16 +2470,17 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
                oidcpy(&commit_oid, &s->oid);
        } else if (*p == ':') {
                uintmax_t commit_mark = parse_mark_ref_eol(p);
-               struct object_entry *commit_oe = find_mark(commit_mark);
+               struct object_entry *commit_oe = find_mark(marks, commit_mark);
                if (commit_oe->type != OBJ_COMMIT)
                        die("Mark :%" PRIuMAX " not a commit", commit_mark);
                oidcpy(&commit_oid, &commit_oe->idx.oid);
        } else if (!get_oid(p, &commit_oid)) {
                unsigned long size;
-               char *buf = read_object_with_reference(&commit_oid,
+               char *buf = read_object_with_reference(the_repository,
+                                                      &commit_oid,
                                                       commit_type, &size,
                                                       &commit_oid);
-               if (!buf || size < 46)
+               if (!buf || size < the_hash_algo->hexsz + 6)
                        die("Not a valid commit: %s", p);
                free(buf);
        } else
@@ -2607,7 +2531,7 @@ static void file_change_deleteall(struct branch *b)
 
 static void parse_from_commit(struct branch *b, char *buf, unsigned long size)
 {
-       if (!buf || size < GIT_SHA1_HEXSZ + 6)
+       if (!buf || size < the_hash_algo->hexsz + 6)
                die("Not a valid commit: %s", oid_to_hex(&b->oid));
        if (memcmp("tree ", buf, 5)
                || get_oid_hex(buf + 5, &b->branch_tree.versions[1].oid))
@@ -2625,25 +2549,22 @@ static void parse_from_existing(struct branch *b)
                unsigned long size;
                char *buf;
 
-               buf = read_object_with_reference(&b->oid, commit_type, &size,
+               buf = read_object_with_reference(the_repository,
+                                                &b->oid, commit_type, &size,
                                                 &b->oid);
                parse_from_commit(b, buf, size);
                free(buf);
        }
 }
 
-static int parse_from(struct branch *b)
+static int parse_objectish(struct branch *b, const char *objectish)
 {
-       const char *from;
        struct branch *s;
        struct object_id oid;
 
-       if (!skip_prefix(command_buf.buf, "from ", &from))
-               return 0;
-
        oidcpy(&oid, &b->branch_tree.versions[1].oid);
 
-       s = lookup_branch(from);
+       s = lookup_branch(objectish);
        if (b == s)
                die("Can't create a branch from itself: %s", b->name);
        else if (s) {
@@ -2651,12 +2572,12 @@ static int parse_from(struct branch *b)
                oidcpy(&b->oid, &s->oid);
                oidcpy(&b->branch_tree.versions[0].oid, t);
                oidcpy(&b->branch_tree.versions[1].oid, t);
-       } else if (*from == ':') {
-               uintmax_t idnum = parse_mark_ref_eol(from);
-               struct object_entry *oe = find_mark(idnum);
+       } else if (*objectish == ':') {
+               uintmax_t idnum = parse_mark_ref_eol(objectish);
+               struct object_entry *oe = find_mark(marks, idnum);
                if (oe->type != OBJ_COMMIT)
                        die("Mark :%" PRIuMAX " not a commit", idnum);
-               if (oidcmp(&b->oid, &oe->idx.oid)) {
+               if (!oideq(&b->oid, &oe->idx.oid)) {
                        oidcpy(&b->oid, &oe->idx.oid);
                        if (oe->pack_id != MAX_PACK_ID) {
                                unsigned long size;
@@ -2666,15 +2587,15 @@ static int parse_from(struct branch *b)
                        } else
                                parse_from_existing(b);
                }
-       } else if (!get_oid(from, &b->oid)) {
+       } else if (!get_oid(objectish, &b->oid)) {
                parse_from_existing(b);
                if (is_null_oid(&b->oid))
                        b->delete = 1;
        }
        else
-               die("Invalid ref name or SHA1 expression: %s", from);
+               die("Invalid ref name or SHA1 expression: %s", objectish);
 
-       if (b->branch_tree.tree && oidcmp(&oid, &b->branch_tree.versions[1].oid)) {
+       if (b->branch_tree.tree && !oideq(&oid, &b->branch_tree.versions[1].oid)) {
                release_tree_content_recursive(b->branch_tree.tree);
                b->branch_tree.tree = NULL;
        }
@@ -2683,6 +2604,26 @@ static int parse_from(struct branch *b)
        return 1;
 }
 
+static int parse_from(struct branch *b)
+{
+       const char *from;
+
+       if (!skip_prefix(command_buf.buf, "from ", &from))
+               return 0;
+
+       return parse_objectish(b, from);
+}
+
+static int parse_objectish_with_prefix(struct branch *b, const char *prefix)
+{
+       const char *base;
+
+       if (!skip_prefix(command_buf.buf, prefix, &base))
+               return 0;
+
+       return parse_objectish(b, base);
+}
+
 static struct hash_list *parse_merge(unsigned int *count)
 {
        struct hash_list *list = NULL, **tail = &list, *n;
@@ -2697,16 +2638,17 @@ static struct hash_list *parse_merge(unsigned int *count)
                        oidcpy(&n->oid, &s->oid);
                else if (*from == ':') {
                        uintmax_t idnum = parse_mark_ref_eol(from);
-                       struct object_entry *oe = find_mark(idnum);
+                       struct object_entry *oe = find_mark(marks, idnum);
                        if (oe->type != OBJ_COMMIT)
                                die("Mark :%" PRIuMAX " not a commit", idnum);
                        oidcpy(&n->oid, &oe->idx.oid);
                } else if (!get_oid(from, &n->oid)) {
                        unsigned long size;
-                       char *buf = read_object_with_reference(&n->oid,
+                       char *buf = read_object_with_reference(the_repository,
+                                                              &n->oid,
                                                               commit_type,
                                                               &size, &n->oid);
-                       if (!buf || size < 46)
+                       if (!buf || size < the_hash_algo->hexsz + 6)
                                die("Not a valid commit: %s", from);
                        free(buf);
                } else
@@ -2728,6 +2670,7 @@ static void parse_new_commit(const char *arg)
        struct branch *b;
        char *author = NULL;
        char *committer = NULL;
+       char *encoding = NULL;
        struct hash_list *merge_list = NULL;
        unsigned int merge_count;
        unsigned char prev_fanout, new_fanout;
@@ -2739,6 +2682,7 @@ static void parse_new_commit(const char *arg)
 
        read_next_command();
        parse_mark();
+       parse_original_identifier();
        if (skip_prefix(command_buf.buf, "author ", &v)) {
                author = parse_ident(v);
                read_next_command();
@@ -2749,6 +2693,10 @@ static void parse_new_commit(const char *arg)
        }
        if (!committer)
                die("Expected committer but didn't get one");
+       if (skip_prefix(command_buf.buf, "encoding ", &v)) {
+               encoding = xstrdup(v);
+               read_next_command();
+       }
        parse_data(&msg, 0, NULL);
        read_next_command();
        parse_from(b);
@@ -2778,6 +2726,8 @@ static void parse_new_commit(const char *arg)
                        file_change_deleteall(b);
                else if (skip_prefix(command_buf.buf, "ls ", &v))
                        parse_ls(v, b);
+               else if (skip_prefix(command_buf.buf, "cat-blob ", &v))
+                       parse_cat_blob(v);
                else {
                        unread_command_buf = 1;
                        break;
@@ -2810,12 +2760,17 @@ static void parse_new_commit(const char *arg)
        }
        strbuf_addf(&new_data,
                "author %s\n"
-               "committer %s\n"
-               "\n",
+               "committer %s\n",
                author ? author : committer, committer);
+       if (encoding)
+               strbuf_addf(&new_data,
+                       "encoding %s\n",
+                       encoding);
+       strbuf_addch(&new_data, '\n');
        strbuf_addbuf(&new_data, &msg);
        free(author);
        free(committer);
+       free(encoding);
 
        if (!store_object(OBJ_COMMIT, &new_data, NULL, &b->oid, next_mark))
                b->pack_id = pack_id;
@@ -2843,6 +2798,7 @@ static void parse_new_tag(const char *arg)
                first_tag = t;
        last_tag = t;
        read_next_command();
+       parse_mark();
 
        /* from ... */
        if (!skip_prefix(command_buf.buf, "from ", &from))
@@ -2856,7 +2812,7 @@ static void parse_new_tag(const char *arg)
        } else if (*from == ':') {
                struct object_entry *oe;
                from_mark = parse_mark_ref_eol(from);
-               oe = find_mark(from_mark);
+               oe = find_mark(marks, from_mark);
                type = oe->type;
                oidcpy(&oid, &oe->idx.oid);
        } else if (!get_oid(from, &oid)) {
@@ -2871,6 +2827,9 @@ static void parse_new_tag(const char *arg)
                die("Invalid ref name or SHA1 expression: %s", from);
        read_next_command();
 
+       /* original-oid ... */
+       parse_original_identifier();
+
        /* tagger ... */
        if (skip_prefix(command_buf.buf, "tagger ", &v)) {
                tagger = parse_ident(v);
@@ -2896,7 +2855,7 @@ static void parse_new_tag(const char *arg)
        strbuf_addbuf(&new_data, &msg);
        free(tagger);
 
-       if (store_object(OBJ_TAG, &new_data, NULL, &t->oid, 0))
+       if (store_object(OBJ_TAG, &new_data, NULL, &t->oid, next_mark))
                t->pack_id = MAX_PACK_ID;
        else
                t->pack_id = pack_id;
@@ -2905,6 +2864,7 @@ static void parse_new_tag(const char *arg)
 static void parse_reset_branch(const char *arg)
 {
        struct branch *b;
+       const char *tag_name;
 
        b = lookup_branch(arg);
        if (b) {
@@ -2920,6 +2880,32 @@ static void parse_reset_branch(const char *arg)
                b = new_branch(arg);
        read_next_command();
        parse_from(b);
+       if (b->delete && skip_prefix(b->name, "refs/tags/", &tag_name)) {
+               /*
+                * Elsewhere, we call dump_branches() before dump_tags(),
+                * and dump_branches() will handle ref deletions first, so
+                * in order to make sure the deletion actually takes effect,
+                * we need to remove the tag from our list of tags to update.
+                *
+                * NEEDSWORK: replace list of tags with hashmap for faster
+                * deletion?
+                */
+               struct tag *t, *prev = NULL;
+               for (t = first_tag; t; t = t->next_tag) {
+                       if (!strcmp(t->name, tag_name))
+                               break;
+                       prev = t;
+               }
+               if (t) {
+                       if (prev)
+                               prev->next_tag = t->next_tag;
+                       else
+                               first_tag = t->next_tag;
+                       if (!t->next_tag)
+                               last_tag = prev;
+                       /* There is no mem_pool_free(t) function to call. */
+               }
+       }
        if (command_buf.len > 0)
                unread_command_buf = 1;
 }
@@ -2961,8 +2947,8 @@ static void cat_blob(struct object_entry *oe, struct object_id *oid)
                die("Object %s is a %s but a blob was expected.",
                    oid_to_hex(oid), type_name(type));
        strbuf_reset(&line);
-       strbuf_addf(&line, "%s %s %lu\n", oid_to_hex(oid),
-                                               type_name(type), size);
+       strbuf_addf(&line, "%s %s %"PRIuMAX"\n", oid_to_hex(oid),
+                   type_name(type), (uintmax_t)size);
        cat_blob_write(line.buf, line.len);
        strbuf_release(&line);
        cat_blob_write(buf, size);
@@ -2984,12 +2970,12 @@ static void parse_get_mark(const char *p)
        if (*p != ':')
                die("Not a mark: %s", p);
 
-       oe = find_mark(parse_mark_ref_eol(p));
+       oe = find_mark(marks, parse_mark_ref_eol(p));
        if (!oe)
                die("Unknown mark: %s", command_buf.buf);
 
        xsnprintf(output, sizeof(output), "%s\n", oid_to_hex(&oe->idx.oid));
-       cat_blob_write(output, GIT_SHA1_HEXSZ + 1);
+       cat_blob_write(output, the_hash_algo->hexsz + 1);
 }
 
 static void parse_cat_blob(const char *p)
@@ -2999,12 +2985,12 @@ static void parse_cat_blob(const char *p)
 
        /* cat-blob SP <object> LF */
        if (*p == ':') {
-               oe = find_mark(parse_mark_ref_eol(p));
+               oe = find_mark(marks, parse_mark_ref_eol(p));
                if (!oe)
                        die("Unknown mark: %s", command_buf.buf);
                oidcpy(&oid, &oe->idx.oid);
        } else {
-               if (parse_oid_hex(p, &oid, &p))
+               if (parse_mapped_oid_hex(p, &oid, &p))
                        die("Invalid dataref: %s", command_buf.buf);
                if (*p)
                        die("Garbage after SHA1: %s", command_buf.buf);
@@ -3019,6 +3005,8 @@ static struct object_entry *dereference(struct object_entry *oe,
 {
        unsigned long size;
        char *buf = NULL;
+       const unsigned hexsz = the_hash_algo->hexsz;
+
        if (!oe) {
                enum object_type type = oid_object_info(the_repository, oid,
                                                        NULL);
@@ -3052,12 +3040,12 @@ static struct object_entry *dereference(struct object_entry *oe,
        /* Peel one layer. */
        switch (oe->type) {
        case OBJ_TAG:
-               if (size < GIT_SHA1_HEXSZ + strlen("object ") ||
+               if (size < hexsz + strlen("object ") ||
                    get_oid_hex(buf + strlen("object "), oid))
                        die("Invalid SHA1 in tag: %s", command_buf.buf);
                break;
        case OBJ_COMMIT:
-               if (size < GIT_SHA1_HEXSZ + strlen("tree ") ||
+               if (size < hexsz + strlen("tree ") ||
                    get_oid_hex(buf + strlen("tree "), oid))
                        die("Invalid SHA1 in commit: %s", command_buf.buf);
        }
@@ -3066,18 +3054,54 @@ static struct object_entry *dereference(struct object_entry *oe,
        return find_object(oid);
 }
 
+static void insert_mapped_mark(uintmax_t mark, void *object, void *cbp)
+{
+       struct object_id *fromoid = object;
+       struct object_id *tooid = find_mark(cbp, mark);
+       int ret;
+       khiter_t it;
+
+       it = kh_put_oid_map(sub_oid_map, *fromoid, &ret);
+       /* We've already seen this object. */
+       if (ret == 0)
+               return;
+       kh_value(sub_oid_map, it) = tooid;
+}
+
+static void build_mark_map_one(struct mark_set *from, struct mark_set *to)
+{
+       for_each_mark(from, 0, insert_mapped_mark, to);
+}
+
+static void build_mark_map(struct string_list *from, struct string_list *to)
+{
+       struct string_list_item *fromp, *top;
+
+       sub_oid_map = kh_init_oid_map();
+
+       for_each_string_list_item(fromp, from) {
+               top = string_list_lookup(to, fromp->string);
+               if (!fromp->util) {
+                       die(_("Missing from marks for submodule '%s'"), fromp->string);
+               } else if (!top || !top->util) {
+                       die(_("Missing to marks for submodule '%s'"), fromp->string);
+               }
+               build_mark_map_one(fromp->util, top->util);
+       }
+}
+
 static struct object_entry *parse_treeish_dataref(const char **p)
 {
        struct object_id oid;
        struct object_entry *e;
 
        if (**p == ':') {       /* <mark> */
-               e = find_mark(parse_mark_ref_space(p));
+               e = find_mark(marks, parse_mark_ref_space(p));
                if (!e)
                        die("Unknown mark: %s", command_buf.buf);
                oidcpy(&oid, &e->idx.oid);
        } else {        /* <sha1> */
-               if (parse_oid_hex(*p, &oid, p))
+               if (parse_mapped_oid_hex(*p, &oid, p))
                        die("Invalid dataref: %s", command_buf.buf);
                e = find_object(&oid);
                if (*(*p)++ != ' ')
@@ -3089,7 +3113,7 @@ static struct object_entry *parse_treeish_dataref(const char **p)
        return e;
 }
 
-static void print_ls(int mode, const unsigned char *sha1, const char *path)
+static void print_ls(int mode, const unsigned char *hash, const char *path)
 {
        static struct strbuf line = STRBUF_INIT;
 
@@ -3109,7 +3133,7 @@ static void print_ls(int mode, const unsigned char *sha1, const char *path)
                /* mode SP type SP object_name TAB path LF */
                strbuf_reset(&line);
                strbuf_addf(&line, "%06o %s %s\t",
-                               mode & ~NO_DELTA, type, sha1_to_hex(sha1));
+                               mode & ~NO_DELTA, type, hash_to_hex(hash));
                quote_c_style(path, &line, NULL, 0);
                strbuf_addch(&line, '\n');
        }
@@ -3184,6 +3208,28 @@ static void parse_progress(void)
        skip_optional_lf();
 }
 
+static void parse_alias(void)
+{
+       struct object_entry *e;
+       struct branch b;
+
+       skip_optional_lf();
+       read_next_command();
+
+       /* mark ... */
+       parse_mark();
+       if (!next_mark)
+               die(_("Expected 'mark' command, got %s"), command_buf.buf);
+
+       /* to ... */
+       memset(&b, 0, sizeof(b));
+       if (!parse_objectish_with_prefix(&b, "to "))
+               die(_("Expected 'to' command, got %s"), command_buf.buf);
+       e = find_object(&b.oid);
+       assert(e);
+       insert_mark(marks, next_mark, e);
+}
+
 static char* make_fast_import_path(const char *path)
 {
        if (!relative_marks_paths || is_absolute_path(path))
@@ -3261,6 +3307,26 @@ static void option_export_pack_edges(const char *edges)
        pack_edges = xfopen(edges, "a");
 }
 
+static void option_rewrite_submodules(const char *arg, struct string_list *list)
+{
+       struct mark_set *ms;
+       FILE *fp;
+       char *s = xstrdup(arg);
+       char *f = strchr(s, ':');
+       if (!f)
+               die(_("Expected format name:filename for submodule rewrite option"));
+       *f = '\0';
+       f++;
+       ms = xcalloc(1, sizeof(*ms));
+       string_list_insert(list, s)->util = ms;
+
+       fp = fopen(f, "r");
+       if (!fp)
+               die_errno("cannot read '%s'", f);
+       read_mark_file(ms, fp, insert_oid_entry);
+       fclose(fp);
+}
+
 static int parse_one_option(const char *option)
 {
        if (skip_prefix(option, "max-pack-size=", &option)) {
@@ -3321,6 +3387,13 @@ static int parse_one_feature(const char *feature, int from_stream)
        } else if (skip_prefix(feature, "export-marks=", &arg)) {
                check_unsafe_feature(feature, from_stream);
                option_export_marks(arg);
+       } else if (!strcmp(feature, "alias")) {
+               ; /* Don't die - this feature is supported */
+       } else if (skip_prefix(feature, "rewrite-submodules-to=", &arg)) {
+               option_rewrite_submodules(arg, &sub_marks_to);
+       } else if (skip_prefix(feature, "rewrite-submodules-from=", &arg)) {
+               option_rewrite_submodules(arg, &sub_marks_from);
+       } else if (skip_prefix(feature, "rewrite-submodules-from=", &arg)) {
        } else if (!strcmp(feature, "get-mark")) {
                ; /* Don't die - this feature is supported */
        } else if (!strcmp(feature, "cat-blob")) {
@@ -3426,6 +3499,7 @@ static void parse_argv(void)
        seen_data_command = 1;
        if (import_marks_file)
                read_marks();
+       build_mark_map(&sub_marks_from, &sub_marks_to);
 }
 
 int cmd_main(int argc, const char **argv)
@@ -3446,6 +3520,8 @@ int cmd_main(int argc, const char **argv)
        avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*));
        marks = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct mark_set));
 
+       hashmap_init(&object_table, object_entry_hashcmp, NULL, 0);
+
        /*
         * We don't parse most options until after we've seen the set of
         * "feature" lines at the start of the stream (which allows the command
@@ -3475,18 +3551,24 @@ int cmd_main(int argc, const char **argv)
                const char *v;
                if (!strcmp("blob", command_buf.buf))
                        parse_new_blob();
-               else if (skip_prefix(command_buf.buf, "ls ", &v))
-                       parse_ls(v, NULL);
                else if (skip_prefix(command_buf.buf, "commit ", &v))
                        parse_new_commit(v);
                else if (skip_prefix(command_buf.buf, "tag ", &v))
                        parse_new_tag(v);
                else if (skip_prefix(command_buf.buf, "reset ", &v))
                        parse_reset_branch(v);
+               else if (skip_prefix(command_buf.buf, "ls ", &v))
+                       parse_ls(v, NULL);
+               else if (skip_prefix(command_buf.buf, "cat-blob ", &v))
+                       parse_cat_blob(v);
+               else if (skip_prefix(command_buf.buf, "get-mark ", &v))
+                       parse_get_mark(v);
                else if (!strcmp("checkpoint", command_buf.buf))
                        parse_checkpoint();
                else if (!strcmp("done", command_buf.buf))
                        break;
+               else if (!strcmp("alias", command_buf.buf))
+                       parse_alias();
                else if (starts_with(command_buf.buf, "progress "))
                        parse_progress();
                else if (skip_prefix(command_buf.buf, "feature ", &v))