]> asedeno.scripts.mit.edu Git - git.git/blobdiff - fast-import.c
Drop strbuf's 'eof' marker, and make read_line a first class citizen.
[git.git] / fast-import.c
index 4bc7f81bcb6e8d7dc927789caf0a36a7a383ae26..da045662ac16883ed28a3c783b4d7c6d10a130cc 100644 (file)
@@ -8,6 +8,7 @@ Format of STDIN stream:
         | new_tag
         | reset_branch
         | checkpoint
+        | progress
         ;
 
   new_blob ::= 'blob' lf
@@ -53,6 +54,9 @@ Format of STDIN stream:
   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
@@ -145,7 +149,6 @@ Format of STDIN stream:
 #include "pack.h"
 #include "refs.h"
 #include "csum-file.h"
-#include "strbuf.h"
 #include "quote.h"
 
 #define PACK_ID_BITS 16
@@ -265,6 +268,13 @@ typedef enum {
        WHENSPEC_NOW,
 } whenspec_type;
 
+struct recent_command
+{
+       struct recent_command *prev;
+       struct recent_command *next;
+       char *buf;
+};
+
 /* Configured limits on output */
 static unsigned long max_depth = 10;
 static off_t max_packsize = (1LL << 32) - 1;
@@ -329,11 +339,121 @@ static struct tag *last_tag;
 
 /* Input stream parsing */
 static whenspec_type whenspec = WHENSPEC_RAW;
-static struct strbuf command_buf;
+static struct strbuf command_buf = STRBUF_INIT;
 static int unread_command_buf;
+static struct recent_command cmd_hist = {&cmd_hist, &cmd_hist, NULL};
+static struct recent_command *cmd_tail = &cmd_hist;
+static struct recent_command *rc_free;
+static unsigned int cmd_save = 100;
 static uintmax_t next_mark;
 static struct dbuf new_data;
 
+static void write_branch_report(FILE *rpt, struct branch *b)
+{
+       fprintf(rpt, "%s:\n", b->name);
+
+       fprintf(rpt, "  status      :");
+       if (b->active)
+               fputs(" active", rpt);
+       if (b->branch_tree.tree)
+               fputs(" loaded", rpt);
+       if (is_null_sha1(b->branch_tree.versions[1].sha1))
+               fputs(" dirty", rpt);
+       fputc('\n', rpt);
+
+       fprintf(rpt, "  tip commit  : %s\n", sha1_to_hex(b->sha1));
+       fprintf(rpt, "  old tree    : %s\n", sha1_to_hex(b->branch_tree.versions[0].sha1));
+       fprintf(rpt, "  cur tree    : %s\n", sha1_to_hex(b->branch_tree.versions[1].sha1));
+       fprintf(rpt, "  commit clock: %" PRIuMAX "\n", b->last_commit);
+
+       fputs("  last pack   : ", rpt);
+       if (b->pack_id < MAX_PACK_ID)
+               fprintf(rpt, "%u", b->pack_id);
+       fputc('\n', rpt);
+
+       fputc('\n', rpt);
+}
+
+static void write_crash_report(const char *err)
+{
+       char *loc = git_path("fast_import_crash_%d", getpid());
+       FILE *rpt = fopen(loc, "w");
+       struct branch *b;
+       unsigned long lu;
+       struct recent_command *rc;
+
+       if (!rpt) {
+               error("can't write crash report %s: %s", loc, strerror(errno));
+               return;
+       }
+
+       fprintf(stderr, "fast-import: dumping crash report to %s\n", loc);
+
+       fprintf(rpt, "fast-import crash report:\n");
+       fprintf(rpt, "    fast-import process: %d\n", getpid());
+       fprintf(rpt, "    parent process     : %d\n", getppid());
+       fprintf(rpt, "    at %s\n", show_date(time(NULL), 0, DATE_LOCAL));
+       fputc('\n', rpt);
+
+       fputs("fatal: ", rpt);
+       fputs(err, rpt);
+       fputc('\n', rpt);
+
+       fputc('\n', rpt);
+       fputs("Most Recent Commands Before Crash\n", rpt);
+       fputs("---------------------------------\n", rpt);
+       for (rc = cmd_hist.next; rc != &cmd_hist; rc = rc->next) {
+               if (rc->next == &cmd_hist)
+                       fputs("* ", rpt);
+               else
+                       fputs("  ", rpt);
+               fputs(rc->buf, rpt);
+               fputc('\n', rpt);
+       }
+
+       fputc('\n', rpt);
+       fputs("Active Branch LRU\n", rpt);
+       fputs("-----------------\n", rpt);
+       fprintf(rpt, "    active_branches = %lu cur, %lu max\n",
+               cur_active_branches,
+               max_active_branches);
+       fputc('\n', rpt);
+       fputs("  pos  clock name\n", rpt);
+       fputs("  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", rpt);
+       for (b = active_branches, lu = 0; b; b = b->active_next_branch)
+               fprintf(rpt, "  %2lu) %6" PRIuMAX" %s\n",
+                       ++lu, b->last_commit, b->name);
+
+       fputc('\n', rpt);
+       fputs("Inactive Branches\n", rpt);
+       fputs("-----------------\n", rpt);
+       for (lu = 0; lu < branch_table_sz; lu++) {
+               for (b = branch_table[lu]; b; b = b->table_next_branch)
+                       write_branch_report(rpt, b);
+       }
+
+       fputc('\n', rpt);
+       fputs("-------------------\n", rpt);
+       fputs("END OF CRASH REPORT\n", rpt);
+       fclose(rpt);
+}
+
+static NORETURN void die_nicely(const char *err, va_list params)
+{
+       static int zombie;
+       char message[2 * PATH_MAX];
+
+       vsnprintf(message, sizeof(message), err, params);
+       fputs("fatal: ", stderr);
+       fputs(message, stderr);
+       fputc('\n', stderr);
+
+       if (!zombie) {
+               zombie = 1;
+               write_crash_report(message);
+       }
+       exit(128);
+}
 
 static void alloc_objects(unsigned int cnt)
 {
@@ -1464,17 +1584,48 @@ static void dump_marks(void)
                        mark_file, strerror(errno));
 }
 
-static void read_next_command(void)
+static int read_next_command(void)
 {
+       static int stdin_eof = 0;
+
+       if (stdin_eof) {
+               unread_command_buf = 0;
+               return EOF;
+       }
+
        do {
-               if (unread_command_buf)
+               if (unread_command_buf) {
                        unread_command_buf = 0;
-               else
-                       read_line(&command_buf, stdin, '\n');
-       } while (!command_buf.eof && command_buf.buf[0] == '#');
+               } else {
+                       struct recent_command *rc;
+
+                       strbuf_detach(&command_buf);
+                       stdin_eof = strbuf_getline(&command_buf, stdin, '\n');
+                       if (stdin_eof)
+                               return EOF;
+
+                       rc = rc_free;
+                       if (rc)
+                               rc_free = rc->next;
+                       else {
+                               rc = cmd_hist.next;
+                               cmd_hist.next = rc->next;
+                               cmd_hist.next->prev = &cmd_hist;
+                               free(rc->buf);
+                       }
+
+                       rc->buf = command_buf.buf;
+                       rc->prev = cmd_tail;
+                       rc->next = cmd_hist.prev;
+                       rc->prev->next = rc;
+                       cmd_tail = rc;
+               }
+       } while (command_buf.buf[0] == '#');
+
+       return 0;
 }
 
-static void skip_optional_lf()
+static void skip_optional_lf(void)
 {
        int term_char = fgetc(stdin);
        if (term_char != '\n' && term_char != EOF)
@@ -1493,39 +1644,34 @@ static void cmd_mark(void)
 
 static void *cmd_data (size_t *size)
 {
-       size_t length;
-       char *buffer;
+       struct strbuf buffer;
 
+       strbuf_init(&buffer, 0);
        if (prefixcmp(command_buf.buf, "data "))
                die("Expected 'data n' command, found: %s", command_buf.buf);
 
        if (!prefixcmp(command_buf.buf + 5, "<<")) {
                char *term = xstrdup(command_buf.buf + 5 + 2);
-               size_t sz = 8192, term_len = command_buf.len - 5 - 2;
-               length = 0;
-               buffer = xmalloc(sz);
+               size_t term_len = command_buf.len - 5 - 2;
+
                for (;;) {
-                       read_line(&command_buf, stdin, '\n');
-                       if (command_buf.eof)
+                       if (strbuf_getline(&command_buf, stdin, '\n') == EOF)
                                die("EOF in data (terminator '%s' not found)", term);
                        if (term_len == command_buf.len
                                && !strcmp(term, command_buf.buf))
                                break;
-                       ALLOC_GROW(buffer, length + command_buf.len, sz);
-                       memcpy(buffer + length,
-                               command_buf.buf,
-                               command_buf.len - 1);
-                       length += command_buf.len - 1;
-                       buffer[length++] = '\n';
+                       strbuf_addbuf(&buffer, &command_buf);
+                       strbuf_addch(&buffer, '\n');
                }
                free(term);
        }
        else {
-               size_t n = 0;
+               size_t n = 0, length;
+
                length = strtoul(command_buf.buf + 5, NULL, 10);
-               buffer = xmalloc(length);
+
                while (n < length) {
-                       size_t s = fread(buffer + n, 1, length - n, stdin);
+                       size_t s = strbuf_fread(&buffer, length - n, stdin);
                        if (!s && feof(stdin))
                                die("EOF in data (%lu bytes remaining)",
                                        (unsigned long)(length - n));
@@ -1534,8 +1680,8 @@ static void *cmd_data (size_t *size)
        }
 
        skip_optional_lf();
-       *size = length;
-       return buffer;
+       *size = buffer.len;
+       return strbuf_detach(&buffer);
 }
 
 static int validate_raw_date(const char *src, char *result, int maxlen)
@@ -1955,7 +2101,7 @@ static void cmd_new_commit(void)
        }
 
        /* file_change* */
-       while (!command_buf.eof && command_buf.len > 1) {
+       while (command_buf.len > 0) {
                if (!prefixcmp(command_buf.buf, "M "))
                        file_change_m(b);
                else if (!prefixcmp(command_buf.buf, "D "))
@@ -1970,7 +2116,8 @@ static void cmd_new_commit(void)
                        unread_command_buf = 1;
                        break;
                }
-               read_next_command();
+               if (read_next_command() == EOF)
+                       break;
        }
 
        /* build the tree and the commit */
@@ -2110,7 +2257,7 @@ static void cmd_reset_branch(void)
        else
                b = new_branch(sp);
        read_next_command();
-       if (!cmd_from(b) && command_buf.len > 1)
+       if (!cmd_from(b) && command_buf.len > 0)
                unread_command_buf = 1;
 }
 
@@ -2125,6 +2272,14 @@ static void cmd_checkpoint(void)
        skip_optional_lf();
 }
 
+static void cmd_progress(void)
+{
+       fwrite(command_buf.buf, 1, command_buf.len, stdout);
+       fputc('\n', stdout);
+       fflush(stdout);
+       skip_optional_lf();
+}
+
 static void import_marks(const char *input_file)
 {
        char line[512];
@@ -2165,11 +2320,11 @@ static const char fast_import_usage[] =
 
 int main(int argc, const char **argv)
 {
-       int i, show_stats = 1;
+       unsigned int i, show_stats = 1;
 
        git_config(git_default_config);
        alloc_objects(object_entry_alloc);
-       strbuf_init(&command_buf);
+       strbuf_init(&command_buf, 0);
        atom_table = xcalloc(atom_table_sz, sizeof(struct atom_str*));
        branch_table = xcalloc(branch_table_sz, sizeof(struct branch*));
        avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*));
@@ -2219,13 +2374,16 @@ int main(int argc, const char **argv)
        if (i != argc)
                usage(fast_import_usage);
 
+       rc_free = pool_alloc(cmd_save * sizeof(*rc_free));
+       for (i = 0; i < (cmd_save - 1); i++)
+               rc_free[i].next = &rc_free[i + 1];
+       rc_free[cmd_save - 1].next = NULL;
+
        prepare_packed_git();
        start_packfile();
-       for (;;) {
-               read_next_command();
-               if (command_buf.eof)
-                       break;
-               else if (!strcmp("blob", command_buf.buf))
+       set_die_routine(die_nicely);
+       while (read_next_command() != EOF) {
+               if (!strcmp("blob", command_buf.buf))
                        cmd_new_blob();
                else if (!prefixcmp(command_buf.buf, "commit "))
                        cmd_new_commit();
@@ -2235,6 +2393,8 @@ int main(int argc, const char **argv)
                        cmd_reset_branch();
                else if (!strcmp("checkpoint", command_buf.buf))
                        cmd_checkpoint();
+               else if (!prefixcmp(command_buf.buf, "progress "))
+                       cmd_progress();
                else
                        die("Unsupported command: %s", command_buf.buf);
        }