sha1-file.c: mark more strings for translation
[gitweb.git] / builtin / fast-export.c
index 9aa4f7fce8f148e1d4cd2ebdd598c4dbad6cfcc6..83c482581b585116f5b78957b2f7a81a6b21a36e 100644 (file)
@@ -5,7 +5,9 @@
  */
 #include "builtin.h"
 #include "cache.h"
+#include "config.h"
 #include "refs.h"
+#include "refspec.h"
 #include "commit.h"
 #include "object.h"
 #include "tag.h"
@@ -20,6 +22,7 @@
 #include "quote.h"
 #include "remote.h"
 #include "blob.h"
+#include "commit-slab.h"
 
 static const char *fast_export_usage[] = {
        N_("git fast-export [rev-list-opts]"),
@@ -34,9 +37,9 @@ static int use_done_feature;
 static int no_data;
 static int full_tree;
 static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
-static struct refspec *refspecs;
-static int refspecs_nr;
+static struct refspec refspecs = REFSPEC_INIT_FETCH;
 static int anonymize;
+static struct revision_sources revision_sources;
 
 static int parse_opt_signed_tag_mode(const struct option *opt,
                                     const char *arg, int unset)
@@ -92,8 +95,9 @@ struct anonymized_entry {
        size_t anon_len;
 };
 
-static int anonymized_entry_cmp(const void *va, const void *vb,
-                               const void *data)
+static int anonymized_entry_cmp(const void *unused_cmp_data,
+                               const void *va, const void *vb,
+                               const void *unused_keydata)
 {
        const struct anonymized_entry *a = va, *b = vb;
        return a->orig_len != b->orig_len ||
@@ -112,7 +116,7 @@ static const void *anonymize_mem(struct hashmap *map,
        struct anonymized_entry key, *ret;
 
        if (!map->cmpfn)
-               hashmap_init(map, anonymized_entry_cmp, 0);
+               hashmap_init(map, anonymized_entry_cmp, NULL, 0);
 
        hashmap_entry_init(&key, memhash(orig, *len));
        key.orig = orig;
@@ -211,7 +215,7 @@ static char *anonymize_blob(unsigned long *size)
        return strbuf_detach(&out, NULL);
 }
 
-static void export_blob(const unsigned char *sha1)
+static void export_blob(const struct object_id *oid)
 {
        unsigned long size;
        enum object_type type;
@@ -222,34 +226,34 @@ static void export_blob(const unsigned char *sha1)
        if (no_data)
                return;
 
-       if (is_null_sha1(sha1))
+       if (is_null_oid(oid))
                return;
 
-       object = lookup_object(sha1);
+       object = lookup_object(oid->hash);
        if (object && object->flags & SHOWN)
                return;
 
        if (anonymize) {
                buf = anonymize_blob(&size);
-               object = (struct object *)lookup_blob(sha1);
+               object = (struct object *)lookup_blob(oid);
                eaten = 0;
        } else {
-               buf = read_sha1_file(sha1, &type, &size);
+               buf = read_object_file(oid, &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);
+                       die("could not read blob %s", oid_to_hex(oid));
+               if (check_object_signature(oid, buf, size, type_name(type)) < 0)
+                       die("sha1 mismatch in blob %s", oid_to_hex(oid));
+               object = parse_object_buffer(oid, type, size, buf, &eaten);
        }
 
        if (!object)
-               die("Could not read blob %s", sha1_to_hex(sha1));
+               die("Could not read blob %s", oid_to_hex(oid));
 
        mark_next_object(object);
 
        printf("blob\nmark :%"PRIu32"\ndata %lu\n", last_idnum, size);
        if (size && fwrite(buf, size, 1, stdout) != 1)
-               die_errno ("Could not write blob '%s'", sha1_to_hex(sha1));
+               die_errno("could not write blob '%s'", oid_to_hex(oid));
        printf("\n");
 
        show_progress();
@@ -322,31 +326,32 @@ static void print_path(const char *path)
        }
 }
 
-static void *generate_fake_sha1(const void *old, size_t *len)
+static void *generate_fake_oid(const void *old, size_t *len)
 {
        static uint32_t counter = 1; /* avoid null sha1 */
-       unsigned char *out = xcalloc(20, 1);
-       put_be32(out + 16, counter++);
+       unsigned char *out = xcalloc(GIT_SHA1_RAWSZ, 1);
+       put_be32(out + GIT_SHA1_RAWSZ - 4, counter++);
        return out;
 }
 
-static const unsigned char *anonymize_sha1(const unsigned char *sha1)
+static const unsigned char *anonymize_sha1(const struct object_id *oid)
 {
        static struct hashmap sha1s;
-       size_t len = 20;
-       return anonymize_mem(&sha1s, generate_fake_sha1, sha1, &len);
+       size_t len = GIT_SHA1_RAWSZ;
+       return anonymize_mem(&sha1s, generate_fake_oid, oid, &len);
 }
 
 static void show_filemodify(struct diff_queue_struct *q,
                            struct diff_options *options, void *data)
 {
        int i;
+       struct string_list *changed = data;
 
        /*
         * Handle files below a directory first, in case they are all deleted
         * and the directory changes to a file or symlink.
         */
-       qsort(q->queue, q->nr, sizeof(q->queue[0]), depth_first);
+       QSORT(q->queue, q->nr, depth_first);
 
        for (i = 0; i < q->nr; i++) {
                struct diff_filespec *ospec = q->queue[i]->one;
@@ -356,20 +361,31 @@ static void show_filemodify(struct diff_queue_struct *q,
                case DIFF_STATUS_DELETED:
                        printf("D ");
                        print_path(spec->path);
+                       string_list_insert(changed, spec->path);
                        putchar('\n');
                        break;
 
                case DIFF_STATUS_COPIED:
                case DIFF_STATUS_RENAMED:
-                       printf("%c ", q->queue[i]->status);
-                       print_path(ospec->path);
-                       putchar(' ');
-                       print_path(spec->path);
-                       putchar('\n');
-
-                       if (!oidcmp(&ospec->oid, &spec->oid) &&
-                           ospec->mode == spec->mode)
-                               break;
+                       /*
+                        * If a change in the file corresponding to ospec->path
+                        * has been observed, we cannot trust its contents
+                        * because the diff is calculated based on the prior
+                        * contents, not the current contents.  So, declare a
+                        * copy or rename only if there was no change observed.
+                        */
+                       if (!string_list_has_string(changed, ospec->path)) {
+                               printf("%c ", q->queue[i]->status);
+                               print_path(ospec->path);
+                               putchar(' ');
+                               print_path(spec->path);
+                               string_list_insert(changed, spec->path);
+                               putchar('\n');
+
+                               if (!oidcmp(&ospec->oid, &spec->oid) &&
+                                   ospec->mode == spec->mode)
+                                       break;
+                       }
                        /* fallthrough */
 
                case DIFF_STATUS_TYPE_CHANGED:
@@ -382,7 +398,7 @@ static void show_filemodify(struct diff_queue_struct *q,
                        if (no_data || S_ISGITLINK(spec->mode))
                                printf("M %06o %s ", spec->mode,
                                       sha1_to_hex(anonymize ?
-                                                  anonymize_sha1(spec->oid.hash) :
+                                                  anonymize_sha1(&spec->oid) :
                                                   spec->oid.hash));
                        else {
                                struct object *object = lookup_object(spec->oid.hash);
@@ -390,6 +406,7 @@ static void show_filemodify(struct diff_queue_struct *q,
                                       get_object_mark(object));
                        }
                        print_path(spec->path);
+                       string_list_insert(changed, spec->path);
                        putchar('\n');
                        break;
 
@@ -501,7 +518,7 @@ static void anonymize_ident_line(const char **beg, const char **end)
        /* skip "committer", "author", "tagger", etc */
        end_of_header = strchr(*beg, ' ');
        if (!end_of_header)
-               die("BUG: malformed line fed to anonymize_ident_line: %.*s",
+               BUG("malformed line fed to anonymize_ident_line: %.*s",
                    (int)(*end - *beg), *beg);
        end_of_header++;
        strbuf_add(out, *beg, end_of_header - *beg);
@@ -525,7 +542,8 @@ static void anonymize_ident_line(const char **beg, const char **end)
        *end = out->buf + out->len;
 }
 
-static void handle_commit(struct commit *commit, struct rev_info *rev)
+static void handle_commit(struct commit *commit, struct rev_info *rev,
+                         struct string_list *paths_of_changed_objects)
 {
        int saved_output_format = rev->diffopt.output_format;
        const char *commit_buffer;
@@ -542,14 +560,14 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
        commit_buffer = get_commit_buffer(commit, NULL);
        author = strstr(commit_buffer, "\nauthor ");
        if (!author)
-               die ("Could not find author in commit %s",
-                    oid_to_hex(&commit->object.oid));
+               die("could not find author in commit %s",
+                   oid_to_hex(&commit->object.oid));
        author++;
        author_end = strchrnul(author, '\n');
        committer = strstr(author_end, "\ncommitter ");
        if (!committer)
-               die ("Could not find committer in commit %s",
-                    oid_to_hex(&commit->object.oid));
+               die("could not find committer in commit %s",
+                   oid_to_hex(&commit->object.oid));
        committer++;
        committer_end = strchrnul(committer, '\n');
        message = strstr(committer_end, "\n\n");
@@ -561,19 +579,19 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
            get_object_mark(&commit->parents->item->object) != 0 &&
            !full_tree) {
                parse_commit_or_die(commit->parents->item);
-               diff_tree_sha1(commit->parents->item->tree->object.oid.hash,
-                              commit->tree->object.oid.hash, "", &rev->diffopt);
+               diff_tree_oid(get_commit_tree_oid(commit->parents->item),
+                             get_commit_tree_oid(commit), "", &rev->diffopt);
        }
        else
-               diff_root_tree_sha1(commit->tree->object.oid.hash,
-                                   "", &rev->diffopt);
+               diff_root_tree_oid(get_commit_tree_oid(commit),
+                                  "", &rev->diffopt);
 
        /* 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))
-                       export_blob(diff_queued_diff.queue[i]->two->oid.hash);
+                       export_blob(&diff_queued_diff.queue[i]->two->oid);
 
-       refname = commit->util;
+       refname = *revision_sources_at(&revision_sources, commit);
        if (anonymize) {
                refname = anonymize_refname(refname);
                anonymize_ident_line(&committer, &committer_end);
@@ -612,6 +630,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
        if (full_tree)
                printf("deleteall\n");
        log_tree_diff_flush(rev);
+       string_list_clear(paths_of_changed_objects, 0);
        rev->diffopt.output_format = saved_output_format;
 
        printf("\n");
@@ -627,15 +646,18 @@ static void *anonymize_tag(const void *old, size_t *len)
        return strbuf_detach(&out, len);
 }
 
-static void handle_tail(struct object_array *commits, struct rev_info *revs)
+static void handle_tail(struct object_array *commits, struct rev_info *revs,
+                       struct string_list *paths_of_changed_objects)
 {
        struct commit *commit;
        while (commits->nr) {
-               commit = (struct commit *)commits->objects[commits->nr - 1].item;
-               if (has_unshown_parent(commit))
+               commit = (struct commit *)object_array_pop(commits);
+               if (has_unshown_parent(commit)) {
+                       /* Queue again, to be handled later */
+                       add_object_array(&commit->object, NULL, commits);
                        return;
-               handle_commit(commit, revs);
-               commits->nr--;
+               }
+               handle_commit(commit, revs, paths_of_changed_objects);
        }
 }
 
@@ -664,9 +686,9 @@ static void handle_tag(const char *name, struct tag *tag)
                return;
        }
 
-       buf = read_sha1_file(tag->object.oid.hash, &type, &size);
+       buf = read_object_file(&tag->object.oid, &type, &size);
        if (!buf)
-               die ("Could not read tag %s", oid_to_hex(&tag->object.oid));
+               die("could not read tag %s", oid_to_hex(&tag->object.oid));
        message = memmem(buf, size, "\n\n", 2);
        if (message) {
                message += 2;
@@ -703,18 +725,18 @@ static void handle_tag(const char *name, struct tag *tag)
                if (signature)
                        switch(signed_tag_mode) {
                        case ABORT:
-                               die ("Encountered signed tag %s; use "
-                                    "--signed-tags=<mode> to handle it.",
-                                    oid_to_hex(&tag->object.oid));
+                               die("encountered signed tag %s; use "
+                                   "--signed-tags=<mode> to handle it",
+                                   oid_to_hex(&tag->object.oid));
                        case WARN:
-                               warning ("Exporting signed tag %s",
-                                        oid_to_hex(&tag->object.oid));
+                               warning("exporting signed tag %s",
+                                       oid_to_hex(&tag->object.oid));
                                /* fallthru */
                        case VERBATIM:
                                break;
                        case WARN_STRIP:
-                               warning ("Stripping signature from tag %s",
-                                        oid_to_hex(&tag->object.oid));
+                               warning("stripping signature from tag %s",
+                                       oid_to_hex(&tag->object.oid));
                                /* fallthru */
                        case STRIP:
                                message_size = signature + 1 - message;
@@ -728,17 +750,18 @@ static void handle_tag(const char *name, struct tag *tag)
        if (!tagged_mark) {
                switch(tag_of_filtered_mode) {
                case ABORT:
-                       die ("Tag %s tags unexported object; use "
-                            "--tag-of-filtered-object=<mode> to handle it.",
-                            oid_to_hex(&tag->object.oid));
+                       die("tag %s tags unexported object; use "
+                           "--tag-of-filtered-object=<mode> to handle it",
+                           oid_to_hex(&tag->object.oid));
                case DROP:
                        /* Ignore this tag altogether */
+                       free(buf);
                        return;
                case REWRITE:
                        if (tagged->type != OBJ_COMMIT) {
-                               die ("Tag %s tags unexported %s!",
-                                    oid_to_hex(&tag->object.oid),
-                                    typename(tagged->type));
+                               die("tag %s tags unexported %s!",
+                                   oid_to_hex(&tag->object.oid),
+                                   type_name(tagged->type));
                        }
                        p = (struct commit *)tagged;
                        for (;;) {
@@ -749,7 +772,7 @@ static void handle_tag(const char *name, struct tag *tag)
                                if (!(p->object.flags & TREESAME))
                                        break;
                                if (!p->parents)
-                                       die ("Can't find replacement commit for tag %s\n",
+                                       die("can't find replacement commit for tag %s",
                                             oid_to_hex(&tag->object.oid));
                                p = p->parents->item;
                        }
@@ -764,6 +787,7 @@ static void handle_tag(const char *name, struct tag *tag)
               (int)(tagger_end - tagger), tagger,
               tagger == tagger_end ? "" : "\n",
               (int)message_size, (int)message_size, message ? message : "");
+       free(buf);
 }
 
 static struct commit *get_commit(struct rev_cmdline_entry *e, char *full_name)
@@ -776,7 +800,7 @@ static struct commit *get_commit(struct rev_cmdline_entry *e, char *full_name)
 
                /* handle nested tags */
                while (tag && tag->object.type == OBJ_TAG) {
-                       parse_object(tag->object.oid.hash);
+                       parse_object(&tag->object.oid);
                        string_list_append(&extra_refs, full_name)->util = tag;
                        tag = (struct tag *)tag->tagged;
                }
@@ -796,19 +820,19 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
 
        for (i = 0; i < info->nr; i++) {
                struct rev_cmdline_entry *e = info->rev + i;
-               unsigned char sha1[20];
+               struct object_id oid;
                struct commit *commit;
                char *full_name;
 
                if (e->flags & UNINTERESTING)
                        continue;
 
-               if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1)
+               if (dwim_ref(e->name, strlen(e->name), &oid, &full_name) != 1)
                        continue;
 
-               if (refspecs) {
+               if (refspecs.nr) {
                        char *private;
-                       private = apply_refspecs(refspecs, refspecs_nr, full_name);
+                       private = apply_refspecs(&refspecs, full_name);
                        if (private) {
                                free(full_name);
                                full_name = private;
@@ -819,7 +843,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
                if (!commit) {
                        warning("%s: Unexpected object of type %s, skipping.",
                                e->name,
-                               typename(e->item->type));
+                               type_name(e->item->type));
                        continue;
                }
 
@@ -827,11 +851,11 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
                case OBJ_COMMIT:
                        break;
                case OBJ_BLOB:
-                       export_blob(commit->object.oid.hash);
+                       export_blob(&commit->object.oid);
                        continue;
                default: /* OBJ_TAG (nested tags) is already handled */
                        warning("Tag points to object of unexpected type %s, skipping.",
-                               typename(commit->object.type));
+                               type_name(commit->object.type));
                        continue;
                }
 
@@ -839,10 +863,11 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
                 * This ref will not be updated through a commit, lets make
                 * sure it gets properly updated eventually.
                 */
-               if (commit->util || commit->object.flags & SHOWN)
+               if (*revision_sources_at(&revision_sources, commit) ||
+                   commit->object.flags & SHOWN)
                        string_list_append(&extra_refs, full_name)->util = commit;
-               if (!commit->util)
-                       commit->util = full_name;
+               if (!*revision_sources_at(&revision_sources, commit))
+                       *revision_sources_at(&revision_sources, commit) = full_name;
        }
 }
 
@@ -875,7 +900,7 @@ static void export_marks(char *file)
 {
        unsigned int i;
        uint32_t mark;
-       struct object_decoration *deco = idnums.hash;
+       struct decoration_entry *deco = idnums.entries;
        FILE *f;
        int e = 0;
 
@@ -904,14 +929,12 @@ static void export_marks(char *file)
 static void import_marks(char *input_file)
 {
        char line[512];
-       FILE *f = fopen(input_file, "r");
-       if (!f)
-               die_errno("cannot read '%s'", input_file);
+       FILE *f = xfopen(input_file, "r");
 
        while (fgets(line, sizeof(line), f)) {
                uint32_t mark;
                char *line_end, *mark_end;
-               unsigned char sha1[20];
+               struct object_id oid;
                struct object *object;
                struct commit *commit;
                enum object_type type;
@@ -923,28 +946,28 @@ static void import_marks(char *input_file)
 
                mark = strtoumax(line + 1, &mark_end, 10);
                if (!mark || mark_end == line + 1
-                       || *mark_end != ' ' || get_sha1_hex(mark_end + 1, sha1))
+                       || *mark_end != ' ' || get_oid_hex(mark_end + 1, &oid))
                        die("corrupt mark line: %s", line);
 
                if (last_idnum < mark)
                        last_idnum = mark;
 
-               type = sha1_object_info(sha1, NULL);
+               type = oid_object_info(the_repository, &oid, NULL);
                if (type < 0)
-                       die("object not found: %s", sha1_to_hex(sha1));
+                       die("object not found: %s", oid_to_hex(&oid));
 
                if (type != OBJ_COMMIT)
                        /* only commits */
                        continue;
 
-               commit = lookup_commit(sha1);
+               commit = lookup_commit(&oid);
                if (!commit)
-                       die("not a commit? can't happen: %s", sha1_to_hex(sha1));
+                       die("not a commit? can't happen: %s", oid_to_hex(&oid));
 
                object = &commit->object;
 
                if (object->flags & SHOWN)
-                       error("Object %s already has a mark", sha1_to_hex(sha1));
+                       error("Object %s already has a mark", oid_to_hex(&oid));
 
                mark_object(object, mark);
 
@@ -956,8 +979,8 @@ static void import_marks(char *input_file)
 static void handle_deletes(void)
 {
        int i;
-       for (i = 0; i < refspecs_nr; i++) {
-               struct refspec *refspec = &refspecs[i];
+       for (i = 0; i < refspecs.nr; i++) {
+               struct refspec_item *refspec = &refspecs.items[i];
                if (*refspec->src)
                        continue;
 
@@ -974,6 +997,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        char *export_filename = NULL, *import_filename = NULL;
        uint32_t lastimportid;
        struct string_list refspecs_list = STRING_LIST_INIT_NODUP;
+       struct string_list paths_of_changed_objects = STRING_LIST_INIT_DUP;
        struct option options[] = {
                OPT_INTEGER(0, "progress", &progress,
                            N_("show progress after <n> objects")),
@@ -1007,8 +1031,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        git_config(git_default_config, NULL);
 
        init_revisions(&revs, prefix);
+       init_revision_sources(&revision_sources);
        revs.topo_order = 1;
-       revs.show_source = 1;
+       revs.sources = &revision_sources;
        revs.rewrite_parents = 1;
        argc = parse_options(argc, argv, prefix, options, fast_export_usage,
                        PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN);
@@ -1017,18 +1042,12 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
                usage_with_options (fast_export_usage, options);
 
        if (refspecs_list.nr) {
-               const char **refspecs_str;
                int i;
 
-               ALLOC_ARRAY(refspecs_str, refspecs_list.nr);
                for (i = 0; i < refspecs_list.nr; i++)
-                       refspecs_str[i] = refspecs_list.items[i].string;
-
-               refspecs_nr = refspecs_list.nr;
-               refspecs = parse_fetch_refspec(refspecs_nr, refspecs_str);
+                       refspec_append(&refspecs, refspecs_list.items[i].string);
 
                string_list_clear(&refspecs_list, 1);
-               free(refspecs_str);
        }
 
        if (use_done_feature)
@@ -1046,14 +1065,15 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        revs.diffopt.format_callback = show_filemodify;
-       DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
+       revs.diffopt.format_callback_data = &paths_of_changed_objects;
+       revs.diffopt.flags.recursive = 1;
        while ((commit = get_revision(&revs))) {
                if (has_unshown_parent(commit)) {
                        add_object_array(&commit->object, NULL, &commits);
                }
                else {
-                       handle_commit(commit, &revs);
-                       handle_tail(&commits, &revs);
+                       handle_commit(commit, &revs, &paths_of_changed_objects);
+                       handle_tail(&commits, &revs, &paths_of_changed_objects);
                }
        }
 
@@ -1066,7 +1086,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        if (use_done_feature)
                printf("done\n");
 
-       free_refspec(refspecs_nr, refspecs);
+       refspec_clear(&refspecs);
 
        return 0;
 }