Merge branch 'ks/config-file-stdin'
[gitweb.git] / builtin / fast-export.c
index ad9d0c46e8e32471f7c2ede9620b66c0711a4de6..b8d8a3aaf9ee8294be9a78956a70b476d327ee05 100644 (file)
@@ -24,12 +24,13 @@ static const char *fast_export_usage[] = {
 };
 
 static int progress;
-static enum { ABORT, VERBATIM, WARN, STRIP } signed_tag_mode = ABORT;
+static enum { ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = ABORT;
 static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ERROR;
 static int fake_missing_tagger;
 static int use_done_feature;
 static int no_data;
 static int full_tree;
+static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
 
 static int parse_opt_signed_tag_mode(const struct option *opt,
                                     const char *arg, int unset)
@@ -40,6 +41,8 @@ static int parse_opt_signed_tag_mode(const struct option *opt,
                signed_tag_mode = VERBATIM;
        else if (!strcmp(arg, "warn"))
                signed_tag_mode = WARN;
+       else if (!strcmp(arg, "warn-strip"))
+               signed_tag_mode = WARN_STRIP;
        else if (!strcmp(arg, "strip"))
                signed_tag_mode = STRIP;
        else
@@ -113,12 +116,13 @@ static void show_progress(void)
                printf("progress %d objects\n", counter);
 }
 
-static void handle_object(const unsigned char *sha1)
+static void export_blob(const unsigned char *sha1)
 {
        unsigned long size;
        enum object_type type;
        char *buf;
        struct object *object;
+       int eaten;
 
        if (no_data)
                return;
@@ -126,16 +130,18 @@ static void handle_object(const unsigned char *sha1)
        if (is_null_sha1(sha1))
                return;
 
-       object = parse_object(sha1);
-       if (!object)
-               die ("Could not read blob %s", sha1_to_hex(sha1));
-
-       if (object->flags & SHOWN)
+       object = lookup_object(sha1);
+       if (object && object->flags & SHOWN)
                return;
 
        buf = read_sha1_file(sha1, &type, &size);
        if (!buf)
                die ("Could not read blob %s", sha1_to_hex(sha1));
+       if (check_sha1_signature(sha1, buf, size, typename(type)) < 0)
+               die("sha1 mismatch in blob %s", sha1_to_hex(sha1));
+       object = parse_object_buffer(sha1, type, size, buf, &eaten);
+       if (!object)
+               die("Could not read blob %s", sha1_to_hex(sha1));
 
        mark_next_object(object);
 
@@ -147,7 +153,8 @@ static void handle_object(const unsigned char *sha1)
        show_progress();
 
        object->flags |= SHOWN;
-       free(buf);
+       if (!eaten)
+               free(buf);
 }
 
 static int depth_first(const void *a_, const void *b_)
@@ -280,7 +287,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
 
        rev->diffopt.output_format = DIFF_FORMAT_CALLBACK;
 
-       parse_commit(commit);
+       parse_commit_or_die(commit);
        author = strstr(commit->buffer, "\nauthor ");
        if (!author)
                die ("Could not find author in commit %s",
@@ -301,7 +308,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
        if (commit->parents &&
            get_object_mark(&commit->parents->item->object) != 0 &&
            !full_tree) {
-               parse_commit(commit->parents->item);
+               parse_commit_or_die(commit->parents->item);
                diff_tree_sha1(commit->parents->item->tree->object.sha1,
                               commit->tree->object.sha1, "", &rev->diffopt);
        }
@@ -312,7 +319,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
        /* Export the referenced blobs, and remember the marks. */
        for (i = 0; i < diff_queued_diff.nr; i++)
                if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode))
-                       handle_object(diff_queued_diff.queue[i]->two->sha1);
+                       export_blob(diff_queued_diff.queue[i]->two->sha1);
 
        mark_next_object(&commit->object);
        if (!is_encoding_utf8(encoding))
@@ -373,7 +380,7 @@ static void handle_tag(const char *name, struct tag *tag)
        int tagged_mark;
        struct commit *p;
 
-       /* Trees have no identifer in fast-export output, thus we have no way
+       /* Trees have no identifier in fast-export output, thus we have no way
         * to output tags of trees, tags of tags of trees, etc.  Simply omit
         * such tags.
         */
@@ -424,6 +431,10 @@ static void handle_tag(const char *name, struct tag *tag)
                                /* fallthru */
                        case VERBATIM:
                                break;
+                       case WARN_STRIP:
+                               warning ("Stripping signature from tag %s",
+                                        sha1_to_hex(tag->object.sha1));
+                               /* fallthru */
                        case STRIP:
                                message_size = signature + 1 - message;
                                break;
@@ -465,7 +476,7 @@ static void handle_tag(const char *name, struct tag *tag)
                }
        }
 
-       if (!prefixcmp(name, "refs/tags/"))
+       if (starts_with(name, "refs/tags/"))
                name += 10;
        printf("tag %s\nfrom :%d\n%.*s%sdata %d\n%.*s\n",
               name, tagged_mark,
@@ -474,10 +485,32 @@ static void handle_tag(const char *name, struct tag *tag)
               (int)message_size, (int)message_size, message ? message : "");
 }
 
-static void get_tags_and_duplicates(struct rev_cmdline_info *info,
-                                   struct string_list *extra_refs)
+static struct commit *get_commit(struct rev_cmdline_entry *e, char *full_name)
+{
+       switch (e->item->type) {
+       case OBJ_COMMIT:
+               return (struct commit *)e->item;
+       case OBJ_TAG: {
+               struct tag *tag = (struct tag *)e->item;
+
+               /* handle nested tags */
+               while (tag && tag->object.type == OBJ_TAG) {
+                       parse_object(tag->object.sha1);
+                       string_list_append(&extra_refs, full_name)->util = tag;
+                       tag = (struct tag *)tag->tagged;
+               }
+               if (!tag)
+                       die("Tag %s points nowhere?", e->name);
+               return (struct commit *)tag;
+               break;
+       }
+       default:
+               return NULL;
+       }
+}
+
+static void get_tags_and_duplicates(struct rev_cmdline_info *info)
 {
-       struct tag *tag;
        int i;
 
        for (i = 0; i < info->nr; i++) {
@@ -492,60 +525,45 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info,
                if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1)
                        continue;
 
-               switch (e->item->type) {
-               case OBJ_COMMIT:
-                       commit = (struct commit *)e->item;
-                       break;
-               case OBJ_TAG:
-                       tag = (struct tag *)e->item;
-
-                       /* handle nested tags */
-                       while (tag && tag->object.type == OBJ_TAG) {
-                               parse_object(tag->object.sha1);
-                               string_list_append(extra_refs, full_name)->util = tag;
-                               tag = (struct tag *)tag->tagged;
-                       }
-                       if (!tag)
-                               die ("Tag %s points nowhere?", e->name);
-                       switch(tag->object.type) {
-                       case OBJ_COMMIT:
-                               commit = (struct commit *)tag;
-                               break;
-                       case OBJ_BLOB:
-                               handle_object(tag->object.sha1);
-                               continue;
-                       default: /* OBJ_TAG (nested tags) is already handled */
-                               warning("Tag points to object of unexpected type %s, skipping.",
-                                       typename(tag->object.type));
-                               continue;
-                       }
-                       break;
-               default:
+               commit = get_commit(e, full_name);
+               if (!commit) {
                        warning("%s: Unexpected object of type %s, skipping.",
                                e->name,
                                typename(e->item->type));
                        continue;
                }
 
+               switch(commit->object.type) {
+               case OBJ_COMMIT:
+                       break;
+               case OBJ_BLOB:
+                       export_blob(commit->object.sha1);
+                       continue;
+               default: /* OBJ_TAG (nested tags) is already handled */
+                       warning("Tag points to object of unexpected type %s, skipping.",
+                               typename(commit->object.type));
+                       continue;
+               }
+
                /*
                 * This ref will not be updated through a commit, lets make
                 * sure it gets properly updated eventually.
                 */
                if (commit->util || commit->object.flags & SHOWN)
-                       string_list_append(extra_refs, full_name)->util = commit;
+                       string_list_append(&extra_refs, full_name)->util = commit;
                if (!commit->util)
                        commit->util = full_name;
        }
 }
 
-static void handle_tags_and_duplicates(struct string_list *extra_refs)
+static void handle_tags_and_duplicates(void)
 {
        struct commit *commit;
        int i;
 
-       for (i = extra_refs->nr - 1; i >= 0; i--) {
-               const char *name = extra_refs->items[i].string;
-               struct object *object = extra_refs->items[i].util;
+       for (i = extra_refs.nr - 1; i >= 0; i--) {
+               const char *name = extra_refs.items[i].string;
+               struct object *object = extra_refs.items[i].util;
                switch (object->type) {
                case OBJ_TAG:
                        handle_tag(name, (struct tag *)object);
@@ -603,6 +621,8 @@ static void import_marks(char *input_file)
                char *line_end, *mark_end;
                unsigned char sha1[20];
                struct object *object;
+               struct commit *commit;
+               enum object_type type;
 
                line_end = strchr(line, '\n');
                if (line[0] != ':' || !line_end)
@@ -611,23 +631,30 @@ static void import_marks(char *input_file)
 
                mark = strtoumax(line + 1, &mark_end, 10);
                if (!mark || mark_end == line + 1
-                       || *mark_end != ' ' || get_sha1(mark_end + 1, sha1))
+                       || *mark_end != ' ' || get_sha1_hex(mark_end + 1, sha1))
                        die("corrupt mark line: %s", line);
 
-               object = parse_object(sha1);
-               if (!object)
-                       die ("Could not read blob %s", sha1_to_hex(sha1));
+               if (last_idnum < mark)
+                       last_idnum = mark;
 
-               if (object->flags & SHOWN)
-                       error("Object %s already has a mark", sha1_to_hex(sha1));
+               type = sha1_object_info(sha1, NULL);
+               if (type < 0)
+                       die("object not found: %s", sha1_to_hex(sha1));
 
-               if (object->type != OBJ_COMMIT)
+               if (type != OBJ_COMMIT)
                        /* only commits */
                        continue;
 
+               commit = lookup_commit(sha1);
+               if (!commit)
+                       die("not a commit? can't happen: %s", sha1_to_hex(sha1));
+
+               object = &commit->object;
+
+               if (object->flags & SHOWN)
+                       error("Object %s already has a mark", sha1_to_hex(sha1));
+
                mark_object(object, mark);
-               if (last_idnum < mark)
-                       last_idnum = mark;
 
                object->flags |= SHOWN;
        }
@@ -638,9 +665,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
        struct object_array commits = OBJECT_ARRAY_INIT;
-       struct string_list extra_refs = STRING_LIST_INIT_NODUP;
        struct commit *commit;
        char *export_filename = NULL, *import_filename = NULL;
+       uint32_t lastimportid;
        struct option options[] = {
                OPT_INTEGER(0, "progress", &progress,
                            N_("show progress after <n> objects")),
@@ -654,11 +681,11 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
                             N_("Dump marks to this file")),
                OPT_STRING(0, "import-marks", &import_filename, N_("file"),
                             N_("Import marks from this file")),
-               OPT_BOOLEAN(0, "fake-missing-tagger", &fake_missing_tagger,
-                            N_("Fake a tagger when tags lack one")),
-               OPT_BOOLEAN(0, "full-tree", &full_tree,
-                            N_("Output full tree for each commit")),
-               OPT_BOOLEAN(0, "use-done-feature", &use_done_feature,
+               OPT_BOOL(0, "fake-missing-tagger", &fake_missing_tagger,
+                        N_("Fake a tagger when tags lack one")),
+               OPT_BOOL(0, "full-tree", &full_tree,
+                        N_("Output full tree for each commit")),
+               OPT_BOOL(0, "use-done-feature", &use_done_feature,
                             N_("Use the done feature to terminate the stream")),
                OPT_BOOL(0, "no-data", &no_data, N_("Skip output of blob data")),
                OPT_END()
@@ -684,11 +711,12 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
 
        if (import_filename)
                import_marks(import_filename);
+       lastimportid = last_idnum;
 
        if (import_filename && revs.prune_data.nr)
                full_tree = 1;
 
-       get_tags_and_duplicates(&revs.cmdline, &extra_refs);
+       get_tags_and_duplicates(&revs.cmdline);
 
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
@@ -704,9 +732,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
                }
        }
 
-       handle_tags_and_duplicates(&extra_refs);
+       handle_tags_and_duplicates();
 
-       if (export_filename)
+       if (export_filename && lastimportid != last_idnum)
                export_marks(export_filename);
 
        if (use_done_feature)