ewah: support platforms that require aligned reads
[gitweb.git] / fast-import.c
index e6f61fc7cb404248c7d742346573874b3ec22226..f4d9969e5c81f9c1415f0908f090bf62b156e179 100644 (file)
@@ -22,8 +22,8 @@ Format of STDIN stream:
     ('author' (sp name)? sp '<' email '>' sp when lf)?
     'committer' (sp name)? sp '<' email '>' sp when lf
     commit_msg
-    ('from' sp committish lf)?
-    ('merge' sp committish lf)*
+    ('from' sp commit-ish lf)?
+    ('merge' sp commit-ish lf)*
     (file_change | ls)*
     lf?;
   commit_msg ::= data;
@@ -43,18 +43,18 @@ Format of STDIN stream:
   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 committish lf;
-  note_inm ::= 'N' sp 'inline' sp committish lf
+  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 committish 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 committish lf)?
+    ('from' sp commit-ish lf)?
     lf?;
 
   checkpoint ::= 'checkpoint' lf
@@ -93,7 +93,7 @@ Format of STDIN stream:
      # stream formatting is: \, " and LF.  Otherwise these values
      # are UTF8.
      #
-  committish  ::= (ref_str | hexsha1 | sha1exp_str | idnum);
+  commit-ish  ::= (ref_str | hexsha1 | sha1exp_str | idnum);
   ref_str     ::= ref;
   sha1exp_str ::= sha1exp;
   tag_str     ::= tag;
@@ -297,7 +297,7 @@ static int failure;
 static FILE *pack_edges;
 static unsigned int show_stats = 1;
 static int global_argc;
-static const char **global_argv;
+static char **global_argv;
 
 /* Memory pools */
 static size_t mem_pool_alloc = 2*1024*1024 - sizeof(struct mem_pool);
@@ -1568,7 +1568,8 @@ static int tree_content_set(
 static int tree_content_remove(
        struct tree_entry *root,
        const char *p,
-       struct tree_entry *backup_leaf)
+       struct tree_entry *backup_leaf,
+       int allow_root)
 {
        struct tree_content *t;
        const char *slash1;
@@ -1583,6 +1584,12 @@ static int tree_content_remove(
 
        if (!root->tree)
                load_tree(root);
+
+       if (!*p && allow_root) {
+               e = root;
+               goto del_entry;
+       }
+
        t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
@@ -1599,7 +1606,7 @@ static int tree_content_remove(
                                goto del_entry;
                        if (!e->tree)
                                load_tree(e);
-                       if (tree_content_remove(e, slash1 + 1, backup_leaf)) {
+                       if (tree_content_remove(e, slash1 + 1, backup_leaf, 0)) {
                                for (n = 0; n < e->tree->entry_count; n++) {
                                        if (e->tree->entries[n]->versions[1].mode) {
                                                hashclr(root->versions[1].sha1);
@@ -1629,7 +1636,8 @@ static int tree_content_remove(
 static int tree_content_get(
        struct tree_entry *root,
        const char *p,
-       struct tree_entry *leaf)
+       struct tree_entry *leaf,
+       int allow_root)
 {
        struct tree_content *t;
        const char *slash1;
@@ -1641,31 +1649,39 @@ static int tree_content_get(
                n = slash1 - p;
        else
                n = strlen(p);
-       if (!n)
+       if (!n && !allow_root)
                die("Empty path component found in input");
 
        if (!root->tree)
                load_tree(root);
+
+       if (!n) {
+               e = root;
+               goto found_entry;
+       }
+
        t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
                if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
-                       if (!slash1) {
-                               memcpy(leaf, e, sizeof(*leaf));
-                               if (e->tree && is_null_sha1(e->versions[1].sha1))
-                                       leaf->tree = dup_tree_content(e->tree);
-                               else
-                                       leaf->tree = NULL;
-                               return 1;
-                       }
+                       if (!slash1)
+                               goto found_entry;
                        if (!S_ISDIR(e->versions[1].mode))
                                return 0;
                        if (!e->tree)
                                load_tree(e);
-                       return tree_content_get(e, slash1 + 1, leaf);
+                       return tree_content_get(e, slash1 + 1, leaf, 0);
                }
        }
        return 0;
+
+found_entry:
+       memcpy(leaf, e, sizeof(*leaf));
+       if (e->tree && is_null_sha1(e->versions[1].sha1))
+               leaf->tree = dup_tree_content(e->tree);
+       else
+               leaf->tree = NULL;
+       return 1;
 }
 
 static int update_branch(struct branch *b)
@@ -1678,7 +1694,7 @@ static int update_branch(struct branch *b)
                return 0;
        if (read_ref(b->name, old_sha1))
                hashclr(old_sha1);
-       lock = lock_any_ref_for_update(b->name, old_sha1, 0);
+       lock = lock_any_ref_for_update(b->name, old_sha1, 0, NULL);
        if (!lock)
                return error("Unable to lock %s", b->name);
        if (!force_update && !is_null_sha1(old_sha1)) {
@@ -1822,7 +1838,7 @@ static void read_marks(void)
                *end = 0;
                mark = strtoumax(line + 1, &end, 10);
                if (!mark || end == line + 1
-                       || *end != ' ' || get_sha1(end + 1, sha1))
+                       || *end != ' ' || get_sha1_hex(end + 1, sha1))
                        die("corrupt mark line: %s", line);
                e = find_object(sha1);
                if (!e) {
@@ -2179,7 +2195,7 @@ static uintmax_t do_change_note_fanout(
                        }
 
                        /* Rename fullpath to realpath */
-                       if (!tree_content_remove(orig_root, fullpath, &leaf))
+                       if (!tree_content_remove(orig_root, fullpath, &leaf, 0))
                                die("Failed to remove path %s", fullpath);
                        tree_content_set(orig_root, realpath,
                                leaf.versions[1].sha1,
@@ -2207,12 +2223,65 @@ static uintmax_t change_note_fanout(struct tree_entry *root,
        return do_change_note_fanout(root, root, hex_sha1, 0, path, 0, fanout);
 }
 
+/*
+ * Given a pointer into a string, parse a mark reference:
+ *
+ *   idnum ::= ':' bigint;
+ *
+ * Return the first character after the value in *endptr.
+ *
+ * Complain if the following character is not what is expected,
+ * either a space or end of the string.
+ */
+static uintmax_t parse_mark_ref(const char *p, char **endptr)
+{
+       uintmax_t mark;
+
+       assert(*p == ':');
+       p++;
+       mark = strtoumax(p, endptr, 10);
+       if (*endptr == p)
+               die("No value after ':' in mark: %s", command_buf.buf);
+       return mark;
+}
+
+/*
+ * Parse the mark reference, and complain if this is not the end of
+ * the string.
+ */
+static uintmax_t parse_mark_ref_eol(const char *p)
+{
+       char *end;
+       uintmax_t mark;
+
+       mark = parse_mark_ref(p, &end);
+       if (*end != '\0')
+               die("Garbage after mark: %s", command_buf.buf);
+       return mark;
+}
+
+/*
+ * Parse the mark reference, demanding a trailing space.  Return a
+ * pointer to the space.
+ */
+static uintmax_t parse_mark_ref_space(const char **p)
+{
+       uintmax_t mark;
+       char *end;
+
+       mark = parse_mark_ref(*p, &end);
+       if (*end != ' ')
+               die("Missing space after mark: %s", command_buf.buf);
+       *p = end;
+       return mark;
+}
+
 static void file_change_m(struct branch *b)
 {
        const char *p = command_buf.buf + 2;
        static struct strbuf uq = STRBUF_INIT;
        const char *endp;
-       struct object_entry *oe = oe;
+       struct object_entry *oe;
        unsigned char sha1[20];
        uint16_t mode, inline_data = 0;
 
@@ -2235,21 +2304,22 @@ static void file_change_m(struct branch *b)
        }
 
        if (*p == ':') {
-               char *x;
-               oe = find_mark(strtoumax(p + 1, &x, 10));
+               oe = find_mark(parse_mark_ref_space(&p));
                hashcpy(sha1, oe->idx.sha1);
-               p = x;
-       } else if (!prefixcmp(p, "inline")) {
+       } else if (!prefixcmp(p, "inline ")) {
                inline_data = 1;
-               p += 6;
+               oe = NULL; /* not used with inline_data, but makes gcc happy */
+               p += strlen("inline");  /* advance to space */
        } else {
                if (get_sha1_hex(p, sha1))
-                       die("Invalid SHA1: %s", command_buf.buf);
+                       die("Invalid dataref: %s", command_buf.buf);
                oe = find_object(sha1);
                p += 40;
+               if (*p != ' ')
+                       die("Missing space after SHA1: %s", command_buf.buf);
        }
-       if (*p++ != ' ')
-               die("Missing space after SHA1: %s", command_buf.buf);
+       assert(*p == ' ');
+       p++;  /* skip space */
 
        strbuf_reset(&uq);
        if (!unquote_c_style(&uq, p, &endp)) {
@@ -2260,7 +2330,7 @@ static void file_change_m(struct branch *b)
 
        /* Git does not track empty, non-toplevel directories. */
        if (S_ISDIR(mode) && !memcmp(sha1, EMPTY_TREE_SHA1_BIN, 20) && *p) {
-               tree_content_remove(&b->branch_tree, p, NULL);
+               tree_content_remove(&b->branch_tree, p, NULL, 0);
                return;
        }
 
@@ -2321,7 +2391,7 @@ static void file_change_d(struct branch *b)
                        die("Garbage after path in: %s", command_buf.buf);
                p = uq.buf;
        }
-       tree_content_remove(&b->branch_tree, p, NULL);
+       tree_content_remove(&b->branch_tree, p, NULL, 1);
 }
 
 static void file_change_cr(struct branch *b, int rename)
@@ -2359,9 +2429,9 @@ static void file_change_cr(struct branch *b, int rename)
 
        memset(&leaf, 0, sizeof(leaf));
        if (rename)
-               tree_content_remove(&b->branch_tree, s, &leaf);
+               tree_content_remove(&b->branch_tree, s, &leaf, 1);
        else
-               tree_content_get(&b->branch_tree, s, &leaf);
+               tree_content_get(&b->branch_tree, s, &leaf, 1);
        if (!leaf.versions[1].mode)
                die("Path %s not in branch", s);
        if (!*d) {      /* C "path/to/subdir" "" */
@@ -2381,7 +2451,7 @@ 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 object_entry *oe;
        struct branch *s;
        unsigned char sha1[20], commit_sha1[20];
        char path[60];
@@ -2407,30 +2477,31 @@ static void note_change_n(struct branch *b, unsigned char *old_fanout)
        /* Now parse the notemodify command. */
        /* <dataref> or 'inline' */
        if (*p == ':') {
-               char *x;
-               oe = find_mark(strtoumax(p + 1, &x, 10));
+               oe = find_mark(parse_mark_ref_space(&p));
                hashcpy(sha1, oe->idx.sha1);
-               p = x;
-       } else if (!prefixcmp(p, "inline")) {
+       } else if (!prefixcmp(p, "inline ")) {
                inline_data = 1;
-               p += 6;
+               oe = NULL; /* not used with inline_data, but makes gcc happy */
+               p += strlen("inline");  /* advance to space */
        } else {
                if (get_sha1_hex(p, sha1))
-                       die("Invalid SHA1: %s", command_buf.buf);
+                       die("Invalid dataref: %s", command_buf.buf);
                oe = find_object(sha1);
                p += 40;
+               if (*p != ' ')
+                       die("Missing space after SHA1: %s", command_buf.buf);
        }
-       if (*p++ != ' ')
-               die("Missing space after SHA1: %s", command_buf.buf);
+       assert(*p == ' ');
+       p++;  /* skip space */
 
-       /* <committish> */
+       /* <commit-ish> */
        s = lookup_branch(p);
        if (s) {
                if (is_null_sha1(s->sha1))
                        die("Can't add a note on empty branch.");
                hashcpy(commit_sha1, s->sha1);
        } else if (*p == ':') {
-               uintmax_t commit_mark = strtoumax(p + 1, NULL, 10);
+               uintmax_t commit_mark = parse_mark_ref_eol(p);
                struct object_entry *commit_oe = find_mark(commit_mark);
                if (commit_oe->type != OBJ_COMMIT)
                        die("Mark :%" PRIuMAX " not a commit", commit_mark);
@@ -2466,7 +2537,7 @@ static void note_change_n(struct branch *b, unsigned char *old_fanout)
        }
 
        construct_path_with_fanout(sha1_to_hex(commit_sha1), *old_fanout, path);
-       if (tree_content_remove(&b->branch_tree, path, NULL))
+       if (tree_content_remove(&b->branch_tree, path, NULL, 0))
                b->num_notes--;
 
        if (is_null_sha1(sha1))
@@ -2537,7 +2608,7 @@ static int parse_from(struct branch *b)
                hashcpy(b->branch_tree.versions[0].sha1, t);
                hashcpy(b->branch_tree.versions[1].sha1, t);
        } else if (*from == ':') {
-               uintmax_t idnum = strtoumax(from + 1, NULL, 10);
+               uintmax_t idnum = parse_mark_ref_eol(from);
                struct object_entry *oe = find_mark(idnum);
                if (oe->type != OBJ_COMMIT)
                        die("Mark :%" PRIuMAX " not a commit", idnum);
@@ -2560,7 +2631,7 @@ static int parse_from(struct branch *b)
 
 static struct hash_list *parse_merge(unsigned int *count)
 {
-       struct hash_list *list = NULL, *n, *e = e;
+       struct hash_list *list = NULL, **tail = &list, *n;
        const char *from;
        struct branch *s;
 
@@ -2572,7 +2643,7 @@ static struct hash_list *parse_merge(unsigned int *count)
                if (s)
                        hashcpy(n->sha1, s->sha1);
                else if (*from == ':') {
-                       uintmax_t idnum = strtoumax(from + 1, NULL, 10);
+                       uintmax_t idnum = parse_mark_ref_eol(from);
                        struct object_entry *oe = find_mark(idnum);
                        if (oe->type != OBJ_COMMIT)
                                die("Mark :%" PRIuMAX " not a commit", idnum);
@@ -2588,11 +2659,9 @@ static struct hash_list *parse_merge(unsigned int *count)
                        die("Invalid ref name or SHA1 expression: %s", from);
 
                n->next = NULL;
-               if (list)
-                       e->next = n;
-               else
-                       list = n;
-               e = n;
+               *tail = n;
+               tail = &n->next;
+
                (*count)++;
                read_next_command();
        }
@@ -2735,7 +2804,7 @@ static void parse_new_tag(void)
                type = OBJ_COMMIT;
        } else if (*from == ':') {
                struct object_entry *oe;
-               from_mark = strtoumax(from + 1, NULL, 10);
+               from_mark = parse_mark_ref_eol(from);
                oe = find_mark(from_mark);
                type = oe->type;
                hashcpy(sha1, oe->idx.sha1);
@@ -2867,18 +2936,13 @@ static void parse_cat_blob(void)
        /* cat-blob SP <object> LF */
        p = command_buf.buf + strlen("cat-blob ");
        if (*p == ':') {
-               char *x;
-               oe = find_mark(strtoumax(p + 1, &x, 10));
-               if (x == p + 1)
-                       die("Invalid mark: %s", command_buf.buf);
+               oe = find_mark(parse_mark_ref_eol(p));
                if (!oe)
                        die("Unknown mark: %s", command_buf.buf);
-               if (*x)
-                       die("Garbage after mark: %s", command_buf.buf);
                hashcpy(sha1, oe->idx.sha1);
        } else {
                if (get_sha1_hex(p, sha1))
-                       die("Invalid SHA1: %s", command_buf.buf);
+                       die("Invalid dataref: %s", command_buf.buf);
                if (p[40])
                        die("Garbage after SHA1: %s", command_buf.buf);
                oe = find_object(sha1);
@@ -2909,7 +2973,7 @@ static struct object_entry *dereference(struct object_entry *oe,
        case OBJ_TAG:
                break;
        default:
-               die("Not a treeish: %s", command_buf.buf);
+               die("Not a tree-ish: %s", command_buf.buf);
        }
 
        if (oe->pack_id != MAX_PACK_ID) {       /* in a pack being written */
@@ -2944,17 +3008,13 @@ static struct object_entry *parse_treeish_dataref(const char **p)
        struct object_entry *e;
 
        if (**p == ':') {       /* <mark> */
-               char *endptr;
-               e = find_mark(strtoumax(*p + 1, &endptr, 10));
-               if (endptr == *p + 1)
-                       die("Invalid mark: %s", command_buf.buf);
+               e = find_mark(parse_mark_ref_space(p));
                if (!e)
                        die("Unknown mark: %s", command_buf.buf);
-               *p = endptr;
                hashcpy(sha1, e->idx.sha1);
        } else {        /* <sha1> */
                if (get_sha1_hex(*p, sha1))
-                       die("Invalid SHA1: %s", command_buf.buf);
+                       die("Invalid dataref: %s", command_buf.buf);
                e = find_object(sha1);
                *p += 40;
        }
@@ -2997,7 +3057,7 @@ static void parse_ls(struct branch *b)
        struct tree_entry *root = NULL;
        struct tree_entry leaf = {NULL};
 
-       /* ls SP (<treeish> SP)? <path> */
+       /* ls SP (<tree-ish> SP)? <path> */
        p = command_buf.buf + strlen("ls ");
        if (*p == '"') {
                if (!b)
@@ -3007,6 +3067,8 @@ static void parse_ls(struct branch *b)
                struct object_entry *e = parse_treeish_dataref(&p);
                root = new_tree_entry();
                hashcpy(root->versions[1].sha1, e->idx.sha1);
+               if (!is_null_sha1(root->versions[1].sha1))
+                       root->versions[1].mode = S_IFDIR;
                load_tree(root);
                if (*p++ != ' ')
                        die("Missing space after tree-ish: %s", command_buf.buf);
@@ -3021,7 +3083,7 @@ static void parse_ls(struct branch *b)
                        die("Garbage after path in: %s", command_buf.buf);
                p = uq.buf;
        }
-       tree_content_get(root, p, &leaf);
+       tree_content_get(root, p, &leaf, 1);
        /*
         * A directory in preparation would have a sha1 of zero
         * until it is saved.  Save, for simplicity.
@@ -3303,7 +3365,7 @@ static void parse_argv(void)
                read_marks();
 }
 
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
 {
        unsigned int i;