]> git.ipfire.org Git - thirdparty/git.git/blobdiff - fast-import.c
Merge branch 'jn/maint-instaweb-plack-fix' into maint
[thirdparty/git.git] / fast-import.c
index 613623be14c4c647e11b20fefb382b3f7438fb3c..b3ba778924908946a0e20ebfdae9bc2058e8a55e 100644 (file)
@@ -132,14 +132,17 @@ Format of STDIN stream:
   ts    ::= # time since the epoch in seconds, ascii base10 notation;
   tz    ::= # GIT style timezone;
 
-     # note: comments 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.
+     # note: comments and cat 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).
      #
+  cat_blob ::= 'cat-blob' sp (hexsha1 | idnum) lf;
+
   comment ::= '#' not_lf* lf;
   not_lf  ::= # Any byte that is not ASCII newline (LF);
 */
@@ -156,14 +159,14 @@ Format of STDIN stream:
 #include "csum-file.h"
 #include "quote.h"
 #include "exec_cmd.h"
+#include "dir.h"
 
 #define PACK_ID_BITS 16
 #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
 #define DEPTH_BITS 13
 #define MAX_DEPTH ((1<<DEPTH_BITS)-1)
 
-struct object_entry
-{
+struct object_entry {
        struct pack_idx_entry idx;
        struct object_entry *next;
        uint32_t type : TYPE_BITS,
@@ -171,16 +174,14 @@ struct object_entry
                depth : DEPTH_BITS;
 };
 
-struct object_entry_pool
-{
+struct object_entry_pool {
        struct object_entry_pool *next_pool;
        struct object_entry *next_free;
        struct object_entry *end;
        struct object_entry entries[FLEX_ARRAY]; /* more */
 };
 
-struct mark_set
-{
+struct mark_set {
        union {
                struct object_entry *marked[1024];
                struct mark_set *sets[1024];
@@ -188,57 +189,49 @@ struct mark_set
        unsigned int shift;
 };
 
-struct last_object
-{
+struct last_object {
        struct strbuf data;
        off_t offset;
        unsigned int depth;
        unsigned no_swap : 1;
 };
 
-struct mem_pool
-{
+struct mem_pool {
        struct mem_pool *next_pool;
        char *next_free;
        char *end;
        uintmax_t space[FLEX_ARRAY]; /* more */
 };
 
-struct atom_str
-{
+struct atom_str {
        struct atom_str *next_atom;
        unsigned short str_len;
        char str_dat[FLEX_ARRAY]; /* more */
 };
 
 struct tree_content;
-struct tree_entry
-{
+struct tree_entry {
        struct tree_content *tree;
        struct atom_str *name;
-       struct tree_entry_ms
-       {
+       struct tree_entry_ms {
                uint16_t mode;
                unsigned char sha1[20];
        } versions[2];
 };
 
-struct tree_content
-{
+struct tree_content {
        unsigned int entry_capacity; /* must match avail_tree_content */
        unsigned int entry_count;
        unsigned int delta_depth;
        struct tree_entry *entries[FLEX_ARRAY]; /* more */
 };
 
-struct avail_tree_content
-{
+struct avail_tree_content {
        unsigned int entry_capacity; /* must match tree_content */
        struct avail_tree_content *next_avail;
 };
 
-struct branch
-{
+struct branch {
        struct branch *table_next_branch;
        struct branch *active_next_branch;
        const char *name;
@@ -250,16 +243,14 @@ struct branch
        unsigned char sha1[20];
 };
 
-struct tag
-{
+struct tag {
        struct tag *next_tag;
        const char *name;
        unsigned int pack_id;
        unsigned char sha1[20];
 };
 
-struct hash_list
-{
+struct hash_list {
        struct hash_list *next;
        unsigned char sha1[20];
 };
@@ -270,8 +261,7 @@ typedef enum {
        WHENSPEC_NOW
 } whenspec_type;
 
-struct recent_command
-{
+struct recent_command {
        struct recent_command *prev;
        struct recent_command *next;
        char *buf;
@@ -361,7 +351,14 @@ static uintmax_t next_mark;
 static struct strbuf new_data = STRBUF_INIT;
 static int seen_data_command;
 
+/* Signal handling */
+static volatile sig_atomic_t checkpoint_requested;
+
+/* Where to write output of cat-blob commands */
+static int cat_blob_fd = STDOUT_FILENO;
+
 static void parse_argv(void);
+static void parse_cat_blob(void);
 
 static void write_branch_report(FILE *rpt, struct branch *b)
 {
@@ -500,6 +497,32 @@ static NORETURN void die_nicely(const char *err, va_list params)
        exit(128);
 }
 
+#ifndef SIGUSR1        /* Windows, for example */
+
+static void set_checkpoint_signal(void)
+{
+}
+
+#else
+
+static void checkpoint_signal(int signo)
+{
+       checkpoint_requested = 1;
+}
+
+static void set_checkpoint_signal(void)
+{
+       struct sigaction sa;
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = checkpoint_signal;
+       sigemptyset(&sa.sa_mask);
+       sa.sa_flags = SA_RESTART;
+       sigaction(SIGUSR1, &sa, NULL);
+}
+
+#endif
+
 static void alloc_objects(unsigned int cnt)
 {
        struct object_entry_pool *b;
@@ -834,6 +857,7 @@ static void start_packfile(void)
        p = xcalloc(1, sizeof(*p) + strlen(tmpfile) + 2);
        strcpy(p->pack_name, tmpfile);
        p->pack_fd = pack_fd;
+       p->do_not_close = 1;
        pack_file = sha1fd(pack_fd, p->pack_name);
 
        hdr.hdr_signature = htonl(PACK_SIGNATURE);
@@ -1432,6 +1456,20 @@ static void store_tree(struct tree_entry *root)
        t->entry_count -= del;
 }
 
+static void tree_content_replace(
+       struct tree_entry *root,
+       const unsigned char *sha1,
+       const uint16_t mode,
+       struct tree_content *newtree)
+{
+       if (!S_ISDIR(mode))
+               die("Root cannot be a non-directory");
+       hashcpy(root->versions[1].sha1, sha1);
+       if (root->tree)
+               release_tree_content_recursive(root->tree);
+       root->tree = newtree;
+}
+
 static int tree_content_set(
        struct tree_entry *root,
        const char *p,
@@ -1439,7 +1477,7 @@ static int tree_content_set(
        const uint16_t mode,
        struct tree_content *subtree)
 {
-       struct tree_content *t = root->tree;
+       struct tree_content *t;
        const char *slash1;
        unsigned int i, n;
        struct tree_entry *e;
@@ -1454,9 +1492,12 @@ static int tree_content_set(
        if (!slash1 && !S_ISDIR(mode) && subtree)
                die("Non-directories cannot have subtrees");
 
+       if (!root->tree)
+               load_tree(root);
+       t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
-               if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
+               if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
                        if (!slash1) {
                                if (!S_ISDIR(mode)
                                                && e->versions[1].mode == mode
@@ -1509,7 +1550,7 @@ static int tree_content_remove(
        const char *p,
        struct tree_entry *backup_leaf)
 {
-       struct tree_content *t = root->tree;
+       struct tree_content *t;
        const char *slash1;
        unsigned int i, n;
        struct tree_entry *e;
@@ -1520,9 +1561,12 @@ static int tree_content_remove(
        else
                n = strlen(p);
 
+       if (!root->tree)
+               load_tree(root);
+       t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
-               if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
+               if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
                        if (slash1 && !S_ISDIR(e->versions[1].mode))
                                /*
                                 * If p names a file in some subdirectory, and a
@@ -1567,7 +1611,7 @@ static int tree_content_get(
        const char *p,
        struct tree_entry *leaf)
 {
-       struct tree_content *t = root->tree;
+       struct tree_content *t;
        const char *slash1;
        unsigned int i, n;
        struct tree_entry *e;
@@ -1578,9 +1622,12 @@ static int tree_content_get(
        else
                n = strlen(p);
 
+       if (!root->tree)
+               load_tree(root);
+       t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
-               if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
+               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))
@@ -1776,7 +1823,7 @@ static int read_next_command(void)
                return EOF;
        }
 
-       do {
+       for (;;) {
                if (unread_command_buf) {
                        unread_command_buf = 0;
                } else {
@@ -1809,9 +1856,14 @@ static int read_next_command(void)
                        rc->prev->next = rc;
                        cmd_tail = rc;
                }
-       } while (command_buf.buf[0] == '#');
-
-       return 0;
+               if (!prefixcmp(command_buf.buf, "cat-blob ")) {
+                       parse_cat_blob();
+                       continue;
+               }
+               if (command_buf.buf[0] == '#')
+                       continue;
+               return 0;
+       }
 }
 
 static void skip_optional_lf(void)
@@ -2166,6 +2218,12 @@ static void file_change_m(struct branch *b)
                p = uq.buf;
        }
 
+       /* 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);
+               return;
+       }
+
        if (S_ISGITLINK(mode)) {
                if (inline_data)
                        die("Git links cannot be specified 'inline': %s",
@@ -2204,6 +2262,10 @@ static void file_change_m(struct branch *b)
                                command_buf.buf);
        }
 
+       if (!*p) {
+               tree_content_replace(&b->branch_tree, sha1, mode, NULL);
+               return;
+       }
        tree_content_set(&b->branch_tree, p, sha1, mode, NULL);
 }
 
@@ -2262,6 +2324,13 @@ static void file_change_cr(struct branch *b, int rename)
                tree_content_get(&b->branch_tree, s, &leaf);
        if (!leaf.versions[1].mode)
                die("Path %s not in branch", s);
+       if (!*d) {      /* C "path/to/subdir" "" */
+               tree_content_replace(&b->branch_tree,
+                       leaf.versions[1].sha1,
+                       leaf.versions[1].mode,
+                       leaf.tree);
+               return;
+       }
        tree_content_set(&b->branch_tree, d,
                leaf.versions[1].sha1,
                leaf.versions[1].mode,
@@ -2675,14 +2744,95 @@ static void parse_reset_branch(void)
                unread_command_buf = 1;
 }
 
-static void parse_checkpoint(void)
+static void cat_blob_write(const char *buf, unsigned long size)
 {
+       if (write_in_full(cat_blob_fd, buf, size) != size)
+               die_errno("Write to frontend failed");
+}
+
+static void cat_blob(struct object_entry *oe, unsigned char sha1[20])
+{
+       struct strbuf line = STRBUF_INIT;
+       unsigned long size;
+       enum object_type type = 0;
+       char *buf;
+
+       if (!oe || oe->pack_id == MAX_PACK_ID) {
+               buf = read_sha1_file(sha1, &type, &size);
+       } else {
+               type = oe->type;
+               buf = gfi_unpack_entry(oe, &size);
+       }
+
+       /*
+        * Output based on batch_one_object() from cat-file.c.
+        */
+       if (type <= 0) {
+               strbuf_reset(&line);
+               strbuf_addf(&line, "%s missing\n", sha1_to_hex(sha1));
+               cat_blob_write(line.buf, line.len);
+               strbuf_release(&line);
+               free(buf);
+               return;
+       }
+       if (!buf)
+               die("Can't read object %s", sha1_to_hex(sha1));
+       if (type != OBJ_BLOB)
+               die("Object %s is a %s but a blob was expected.",
+                   sha1_to_hex(sha1), typename(type));
+       strbuf_reset(&line);
+       strbuf_addf(&line, "%s %s %lu\n", sha1_to_hex(sha1),
+                                               typename(type), size);
+       cat_blob_write(line.buf, line.len);
+       strbuf_release(&line);
+       cat_blob_write(buf, size);
+       cat_blob_write("\n", 1);
+       free(buf);
+}
+
+static void parse_cat_blob(void)
+{
+       const char *p;
+       struct object_entry *oe = oe;
+       unsigned char sha1[20];
+
+       /* 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);
+               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);
+               if (p[40])
+                       die("Garbage after SHA1: %s", command_buf.buf);
+               oe = find_object(sha1);
+       }
+
+       cat_blob(oe, sha1);
+}
+
+static void checkpoint(void)
+{
+       checkpoint_requested = 0;
        if (object_count) {
                cycle_packfile();
                dump_branches();
                dump_tags();
                dump_marks();
        }
+}
+
+static void parse_checkpoint(void)
+{
+       checkpoint_requested = 1;
        skip_optional_lf();
 }
 
@@ -2732,16 +2882,25 @@ static void option_date_format(const char *fmt)
                die("unknown --date-format argument %s", fmt);
 }
 
+static unsigned long ulong_arg(const char *option, const char *arg)
+{
+       char *endptr;
+       unsigned long rv = strtoul(arg, &endptr, 0);
+       if (strchr(arg, '-') || endptr == arg || *endptr)
+               die("%s: argument must be a non-negative integer", option);
+       return rv;
+}
+
 static void option_depth(const char *depth)
 {
-       max_depth = strtoul(depth, NULL, 0);
+       max_depth = ulong_arg("--depth", depth);
        if (max_depth > MAX_DEPTH)
                die("--depth cannot exceed %u", MAX_DEPTH);
 }
 
 static void option_active_branches(const char *branches)
 {
-       max_active_branches = strtoul(branches, NULL, 0);
+       max_active_branches = ulong_arg("--active-branches", branches);
 }
 
 static void option_export_marks(const char *marks)
@@ -2750,6 +2909,14 @@ static void option_export_marks(const char *marks)
        safe_create_leading_directories_const(export_marks_file);
 }
 
+static void option_cat_blob_fd(const char *fd)
+{
+       unsigned long n = ulong_arg("--cat-blob-fd", fd);
+       if (n > (unsigned long) INT_MAX)
+               die("--cat-blob-fd cannot exceed %d", INT_MAX);
+       cat_blob_fd = (int) n;
+}
+
 static void option_export_pack_edges(const char *edges)
 {
        if (pack_edges)
@@ -2803,12 +2970,16 @@ static int parse_one_feature(const char *feature, int from_stream)
                option_import_marks(feature + 13, from_stream);
        } else if (!prefixcmp(feature, "export-marks=")) {
                option_export_marks(feature + 13);
+       } else if (!strcmp(feature, "cat-blob")) {
+               ; /* Don't die - this feature is supported */
        } else if (!prefixcmp(feature, "relative-marks")) {
                relative_marks_paths = 1;
        } else if (!prefixcmp(feature, "no-relative-marks")) {
                relative_marks_paths = 0;
        } else if (!prefixcmp(feature, "force")) {
                force_update = 1;
+       } else if (!strcmp(feature, "notes")) {
+               ; /* do nothing; we have the feature */
        } else {
                return 0;
        }
@@ -2897,6 +3068,11 @@ static void parse_argv(void)
                if (parse_one_feature(a + 2, 0))
                        continue;
 
+               if (!prefixcmp(a + 2, "cat-blob-fd=")) {
+                       option_cat_blob_fd(a + 2 + strlen("cat-blob-fd="));
+                       continue;
+               }
+
                die("unknown option %s", a);
        }
        if (i != global_argc)
@@ -2939,6 +3115,7 @@ int main(int argc, const char **argv)
        prepare_packed_git();
        start_packfile();
        set_die_routine(die_nicely);
+       set_checkpoint_signal();
        while (read_next_command() != EOF) {
                if (!strcmp("blob", command_buf.buf))
                        parse_new_blob();
@@ -2960,6 +3137,9 @@ int main(int argc, const char **argv)
                        /* ignore non-git options*/;
                else
                        die("Unsupported command: %s", command_buf.buf);
+
+               if (checkpoint_requested)
+                       checkpoint();
        }
 
        /* argv hasn't been parsed yet, do so */