Code

Merge branch 'db/strbufs-for-metadata' into db/svn-fe-code-purge
authorJonathan Nieder <jrnieder@gmail.com>
Tue, 22 Mar 2011 23:19:46 +0000 (18:19 -0500)
committerJonathan Nieder <jrnieder@gmail.com>
Tue, 22 Mar 2011 23:19:46 +0000 (18:19 -0500)
* db/strbufs-for-metadata:
  vcs-svn: use strbuf for author, UUID, and URL
  vcs-svn: use strbuf for revision log

Conflicts:
vcs-svn/fast_export.c
vcs-svn/fast_export.h
vcs-svn/repo_tree.c
vcs-svn/svndump.c

1  2 
vcs-svn/fast_export.c
vcs-svn/fast_export.h
vcs-svn/repo_tree.h
vcs-svn/svndump.c

diff --combined vcs-svn/fast_export.c
index 32f0c8cfcc98ad3104ee9eca01ce98909bcbfff8,a4d4d9993dac74f0e751009f30588aef62d3a573..a64a3c5633212fe5bd48f34d1ab568be6512df21
  #include "line_buffer.h"
  #include "repo_tree.h"
  #include "string_pool.h"
 +#include "strbuf.h"
  
  #define MAX_GITSVN_LINE_LEN 4096
  
  static uint32_t first_commit_done;
 +static struct line_buffer report_buffer = LINE_BUFFER_INIT;
  
 -void fast_export_delete(uint32_t depth, uint32_t *path)
 +void fast_export_init(int fd)
  {
 -      putchar('D');
 -      putchar(' ');
 -      pool_print_seq(depth, path, '/', stdout);
 -      putchar('\n');
 +      if (buffer_fdinit(&report_buffer, fd))
 +              die_errno("cannot read from file descriptor %d", fd);
  }
  
 -void fast_export_modify(uint32_t depth, uint32_t *path, uint32_t mode,
 -                      uint32_t mark)
 +void fast_export_deinit(void)
 +{
 +      if (buffer_deinit(&report_buffer))
 +              die_errno("error closing fast-import feedback stream");
 +}
 +
 +void fast_export_reset(void)
 +{
 +      buffer_reset(&report_buffer);
 +}
 +
 +void fast_export_delete(uint32_t depth, const uint32_t *path)
 +{
 +      printf("D \"");
 +      pool_print_seq_q(depth, path, '/', stdout);
 +      printf("\"\n");
 +}
 +
 +static void fast_export_truncate(uint32_t depth, const uint32_t *path, uint32_t mode)
 +{
 +      fast_export_modify(depth, path, mode, "inline");
 +      printf("data 0\n\n");
 +}
 +
 +void fast_export_modify(uint32_t depth, const uint32_t *path, uint32_t mode,
 +                      const char *dataref)
  {
        /* Mode must be 100644, 100755, 120000, or 160000. */
 -      printf("M %06"PRIo32" :%"PRIu32" ", mode, mark);
 -      pool_print_seq(depth, path, '/', stdout);
 -      putchar('\n');
 +      if (!dataref) {
 +              fast_export_truncate(depth, path, mode);
 +              return;
 +      }
 +      printf("M %06"PRIo32" %s \"", mode, dataref);
 +      pool_print_seq_q(depth, path, '/', stdout);
 +      printf("\"\n");
  }
  
  static char gitsvnline[MAX_GITSVN_LINE_LEN];
- void fast_export_begin_commit(uint32_t revision, uint32_t author, char *log,
-                       uint32_t uuid, uint32_t url,
 -void fast_export_commit(uint32_t revision, const char *author, char *log,
++void fast_export_begin_commit(uint32_t revision, const char *author, char *log,
+                       const char *uuid, const char *url,
                        unsigned long timestamp)
  {
        if (!log)
                log = "";
-       if (~uuid && ~url) {
+       if (*uuid && *url) {
                snprintf(gitsvnline, MAX_GITSVN_LINE_LEN,
                                "\n\ngit-svn-id: %s@%"PRIu32" %s\n",
-                                pool_fetch(url), revision, pool_fetch(uuid));
+                                url, revision, uuid);
        } else {
                *gitsvnline = '\0';
        }
        printf("commit refs/heads/master\n");
 +      printf("mark :%"PRIu32"\n", revision);
        printf("committer %s <%s@%s> %ld +0000\n",
-                  ~author ? pool_fetch(author) : "nobody",
-                  ~author ? pool_fetch(author) : "nobody",
-                  ~uuid ? pool_fetch(uuid) : "local", timestamp);
+                  *author ? author : "nobody",
+                  *author ? author : "nobody",
+                  *uuid ? uuid : "local", timestamp);
        printf("data %"PRIu32"\n%s%s\n",
                   (uint32_t) (strlen(log) + strlen(gitsvnline)),
                   log, gitsvnline);
        if (!first_commit_done) {
                if (revision > 1)
 -                      printf("from refs/heads/master^0\n");
 +                      printf("from :%"PRIu32"\n", revision - 1);
                first_commit_done = 1;
        }
 -      repo_diff(revision - 1, revision);
 -      fputc('\n', stdout);
 +}
  
 +void fast_export_end_commit(uint32_t revision)
 +{
        printf("progress Imported commit %"PRIu32".\n\n", revision);
  }
  
 +static void ls_from_rev(uint32_t rev, uint32_t depth, const uint32_t *path)
 +{
 +      /* ls :5 path/to/old/file */
 +      printf("ls :%"PRIu32" \"", rev);
 +      pool_print_seq_q(depth, path, '/', stdout);
 +      printf("\"\n");
 +      fflush(stdout);
 +}
 +
 +static void ls_from_active_commit(uint32_t depth, const uint32_t *path)
 +{
 +      /* ls "path/to/file" */
 +      printf("ls \"");
 +      pool_print_seq_q(depth, path, '/', stdout);
 +      printf("\"\n");
 +      fflush(stdout);
 +}
 +
 +static const char *get_response_line(void)
 +{
 +      const char *line = buffer_read_line(&report_buffer);
 +      if (line)
 +              return line;
 +      if (buffer_ferror(&report_buffer))
 +              die_errno("error reading from fast-import");
 +      die("unexpected end of fast-import feedback");
 +}
 +
  static void die_short_read(struct line_buffer *input)
  {
        if (buffer_ferror(input))
        die("invalid dump: unexpected end of file");
  }
  
 -void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len, struct line_buffer *input)
 +void fast_export_data(uint32_t mode, uint32_t len, struct line_buffer *input)
  {
        if (mode == REPO_MODE_LNK) {
                /* svn symlink blobs start with "link " */
                if (buffer_skip_bytes(input, 5) != 5)
                        die_short_read(input);
        }
 -      printf("blob\nmark :%"PRIu32"\ndata %"PRIu32"\n", mark, len);
 +      printf("data %"PRIu32"\n", len);
        if (buffer_copy_bytes(input, len) != len)
                die_short_read(input);
        fputc('\n', stdout);
  }
 +
 +static int parse_ls_response(const char *response, uint32_t *mode,
 +                                      struct strbuf *dataref)
 +{
 +      const char *tab;
 +      const char *response_end;
 +
 +      assert(response);
 +      response_end = response + strlen(response);
 +
 +      if (*response == 'm') { /* Missing. */
 +              errno = ENOENT;
 +              return -1;
 +      }
 +
 +      /* Mode. */
 +      if (response_end - response < strlen("100644") ||
 +          response[strlen("100644")] != ' ')
 +              die("invalid ls response: missing mode: %s", response);
 +      *mode = 0;
 +      for (; *response != ' '; response++) {
 +              char ch = *response;
 +              if (ch < '0' || ch > '7')
 +                      die("invalid ls response: mode is not octal: %s", response);
 +              *mode *= 8;
 +              *mode += ch - '0';
 +      }
 +
 +      /* ' blob ' or ' tree ' */
 +      if (response_end - response < strlen(" blob ") ||
 +          (response[1] != 'b' && response[1] != 't'))
 +              die("unexpected ls response: not a tree or blob: %s", response);
 +      response += strlen(" blob ");
 +
 +      /* Dataref. */
 +      tab = memchr(response, '\t', response_end - response);
 +      if (!tab)
 +              die("invalid ls response: missing tab: %s", response);
 +      strbuf_add(dataref, response, tab - response);
 +      return 0;
 +}
 +
 +int fast_export_ls_rev(uint32_t rev, uint32_t depth, const uint32_t *path,
 +                              uint32_t *mode, struct strbuf *dataref)
 +{
 +      ls_from_rev(rev, depth, path);
 +      return parse_ls_response(get_response_line(), mode, dataref);
 +}
 +
 +int fast_export_ls(uint32_t depth, const uint32_t *path,
 +                              uint32_t *mode, struct strbuf *dataref)
 +{
 +      ls_from_active_commit(depth, path);
 +      return parse_ls_response(get_response_line(), mode, dataref);
 +}
diff --combined vcs-svn/fast_export.h
index 633d21944e83b795a170021be9290114e6e28f10,05cf97f3a7854e3ddf7060b361278ca82adc844b..fc142424201a94b5eb9da7efbf3cf52c2766cd49
@@@ -1,25 -1,15 +1,26 @@@
  #ifndef FAST_EXPORT_H_
  #define FAST_EXPORT_H_
  
 -#include "line_buffer.h"
 +struct strbuf;
 +struct line_buffer;
  
 -void fast_export_delete(uint32_t depth, uint32_t *path);
 -void fast_export_modify(uint32_t depth, uint32_t *path, uint32_t mode,
 -                      uint32_t mark);
 -void fast_export_commit(uint32_t revision, const char *author, char *log,
 +void fast_export_init(int fd);
 +void fast_export_deinit(void);
 +void fast_export_reset(void);
 +
 +void fast_export_delete(uint32_t depth, const uint32_t *path);
 +void fast_export_modify(uint32_t depth, const uint32_t *path,
 +                      uint32_t mode, const char *dataref);
- void fast_export_begin_commit(uint32_t revision, uint32_t author, char *log,
-                       uint32_t uuid, uint32_t url, unsigned long timestamp);
++void fast_export_begin_commit(uint32_t revision, const char *author, char *log,
+                       const char *uuid, const char *url,
+                       unsigned long timestamp);
 -void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len,
 -                    struct line_buffer *input);
 +void fast_export_end_commit(uint32_t revision);
 +void fast_export_data(uint32_t mode, uint32_t len, struct line_buffer *input);
 +
 +/* If there is no such file at that rev, returns -1, errno == ENOENT. */
 +int fast_export_ls_rev(uint32_t rev, uint32_t depth, const uint32_t *path,
 +                      uint32_t *mode_out, struct strbuf *dataref_out);
 +int fast_export_ls(uint32_t depth, const uint32_t *path,
 +                      uint32_t *mode_out, struct strbuf *dataref_out);
  
  #endif
diff --combined vcs-svn/repo_tree.h
index d690784fbbddd7743e9e97484cb81c2dbcf61327,a1b0e87651b7576cd4e1842261d745664331793a..29887f97656333688f2aeba90978f7f76388174c
  uint32_t next_blob_mark(void);
  void repo_copy(uint32_t revision, const uint32_t *src, const uint32_t *dst);
  void repo_add(uint32_t *path, uint32_t mode, uint32_t blob_mark);
 -uint32_t repo_read_path(const uint32_t *path);
 +const char *repo_read_path(const uint32_t *path);
  uint32_t repo_read_mode(const uint32_t *path);
  void repo_delete(uint32_t *path);
- void repo_commit(uint32_t revision, uint32_t author, char *log, uint32_t uuid,
-                uint32_t url, long unsigned timestamp);
+ void repo_commit(uint32_t revision, const char *author,
+               char *log, const char *uuid, const char *url,
+               long unsigned timestamp);
  void repo_diff(uint32_t r1, uint32_t r2);
  void repo_init(void);
  void repo_reset(void);
diff --combined vcs-svn/svndump.c
index ff985fe9e69f10f17918ee72cd49c0e07b96f132,7ac74877fa6fcba86adef0a6b204db496dfd4b49..f5de49cbeb35c5b146db1280de4322ce2fab4031
  #include "repo_tree.h"
  #include "fast_export.h"
  #include "line_buffer.h"
- #include "obj_pool.h"
  #include "string_pool.h"
+ #include "strbuf.h"
  
 +#define REPORT_FILENO 3
 +
 +/*
 + * Compare start of string to literal of equal length;
 + * must be guarded by length test.
 + */
 +#define constcmp(s, ref) memcmp(s, ref, sizeof(ref) - 1)
 +
  #define NODEACT_REPLACE 4
  #define NODEACT_DELETE 3
  #define NODEACT_ADD 2
  #define NODEACT_CHANGE 1
  #define NODEACT_UNKNOWN 0
  
 -#define DUMP_CTX 0
 -#define REV_CTX  1
 -#define NODE_CTX 2
 +/* States: */
 +#define DUMP_CTX 0    /* dump metadata */
 +#define REV_CTX  1    /* revision metadata */
 +#define NODE_CTX 2    /* node metadata */
 +#define INTERNODE_CTX 3       /* between nodes */
  
  #define LENGTH_UNKNOWN (~0)
  #define DATE_RFC2822_LEN 31
  
- /* Create memory pool for log messages */
- obj_pool_gen(log, char, 4096)
  static struct line_buffer input = LINE_BUFFER_INIT;
  
- #define REPORT_FILENO 3
- static char *log_copy(uint32_t length, const char *log)
- {
-       char *buffer;
-       log_free(log_pool.size);
-       buffer = log_pointer(log_alloc(length));
-       strncpy(buffer, log, length);
-       return buffer;
- }
  static struct {
        uint32_t action, propLength, textLength, srcRev, type;
        uint32_t src[REPO_MAX_PATH_DEPTH], dst[REPO_MAX_PATH_DEPTH];
  } node_ctx;
  
  static struct {
-       uint32_t revision, author;
+       uint32_t revision;
        unsigned long timestamp;
-       char *log;
+       struct strbuf log, author;
  } rev_ctx;
  
  static struct {
-       uint32_t version, uuid, url;
+       uint32_t version;
+       struct strbuf uuid, url;
  } dump_ctx;
  
 -static struct {
 -      uint32_t svn_log, svn_author, svn_date, svn_executable, svn_special, uuid,
 -              revision_number, node_path, node_kind, node_action,
 -              node_copyfrom_path, node_copyfrom_rev, text_content_length,
 -              prop_content_length, content_length, svn_fs_dump_format_version,
 -              /* version 3 format */
 -              text_delta, prop_delta;
 -} keys;
 -
  static void reset_node_ctx(char *fname)
  {
        node_ctx.type = 0;
@@@ -86,54 -72,60 +73,58 @@@ static void reset_rev_ctx(uint32_t revi
  {
        rev_ctx.revision = revision;
        rev_ctx.timestamp = 0;
-       rev_ctx.log = NULL;
-       rev_ctx.author = ~0;
+       strbuf_reset(&rev_ctx.log);
+       strbuf_reset(&rev_ctx.author);
  }
  
- static void reset_dump_ctx(uint32_t url)
+ static void reset_dump_ctx(const char *url)
  {
-       dump_ctx.url = url;
+       strbuf_reset(&dump_ctx.url);
+       if (url)
+               strbuf_addstr(&dump_ctx.url, url);
        dump_ctx.version = 1;
-       dump_ctx.uuid = ~0;
+       strbuf_reset(&dump_ctx.uuid);
  }
  
 -static void init_keys(void)
 -{
 -      keys.svn_log = pool_intern("svn:log");
 -      keys.svn_author = pool_intern("svn:author");
 -      keys.svn_date = pool_intern("svn:date");
 -      keys.svn_executable = pool_intern("svn:executable");
 -      keys.svn_special = pool_intern("svn:special");
 -      keys.uuid = pool_intern("UUID");
 -      keys.revision_number = pool_intern("Revision-number");
 -      keys.node_path = pool_intern("Node-path");
 -      keys.node_kind = pool_intern("Node-kind");
 -      keys.node_action = pool_intern("Node-action");
 -      keys.node_copyfrom_path = pool_intern("Node-copyfrom-path");
 -      keys.node_copyfrom_rev = pool_intern("Node-copyfrom-rev");
 -      keys.text_content_length = pool_intern("Text-content-length");
 -      keys.prop_content_length = pool_intern("Prop-content-length");
 -      keys.content_length = pool_intern("Content-length");
 -      keys.svn_fs_dump_format_version = pool_intern("SVN-fs-dump-format-version");
 -      /* version 3 format (Subversion 1.1.0) */
 -      keys.text_delta = pool_intern("Text-delta");
 -      keys.prop_delta = pool_intern("Prop-delta");
 -}
 -
 -static void handle_property(uint32_t key, const char *val, uint32_t len,
 +static void handle_property(const struct strbuf *key_buf,
 +                              const char *val, uint32_t len,
                                uint32_t *type_set)
  {
 -      if (key == keys.svn_log) {
 +      const char *key = key_buf->buf;
 +      size_t keylen = key_buf->len;
 +
 +      switch (keylen + 1) {
 +      case sizeof("svn:log"):
 +              if (constcmp(key, "svn:log"))
 +                      break;
                if (!val)
                        die("invalid dump: unsets svn:log");
-               /* Value length excludes terminating nul. */
-               rev_ctx.log = log_copy(len + 1, val);
+               strbuf_reset(&rev_ctx.log);
+               strbuf_add(&rev_ctx.log, val, len);
 -      } else if (key == keys.svn_author) {
 +              break;
 +      case sizeof("svn:author"):
 +              if (constcmp(key, "svn:author"))
 +                      break;
-               rev_ctx.author = pool_intern(val);
+               strbuf_reset(&rev_ctx.author);
+               if (val)
+                       strbuf_add(&rev_ctx.author, val, len);
 -      } else if (key == keys.svn_date) {
 +              break;
 +      case sizeof("svn:date"):
 +              if (constcmp(key, "svn:date"))
 +                      break;
                if (!val)
                        die("invalid dump: unsets svn:date");
                if (parse_date_basic(val, &rev_ctx.timestamp, NULL))
                        warning("invalid timestamp: %s", val);
 -      } else if (key == keys.svn_executable || key == keys.svn_special) {
 +              break;
 +      case sizeof("svn:executable"):
 +      case sizeof("svn:special"):
 +              if (keylen == strlen("svn:executable") &&
 +                  constcmp(key, "svn:executable"))
 +                      break;
 +              if (keylen == strlen("svn:special") &&
 +                  constcmp(key, "svn:special"))
 +                      break;
                if (*type_set) {
                        if (!val)
                                return;
                        return;
                }
                *type_set = 1;
 -              node_ctx.type = key == keys.svn_executable ?
 +              node_ctx.type = keylen == strlen("svn:executable") ?
                                REPO_MODE_EXE :
                                REPO_MODE_LNK;
        }
@@@ -159,7 -151,7 +150,7 @@@ static void die_short_read(void
  
  static void read_props(void)
  {
 -      uint32_t key = ~0;
 +      static struct strbuf key = STRBUF_INIT;
        const char *t;
        /*
         * NEEDSWORK: to support simple mode changes like
  
                switch (type) {
                case 'K':
 -                      key = pool_intern(val);
 -                      continue;
                case 'D':
 -                      key = pool_intern(val);
 +                      strbuf_reset(&key);
 +                      if (val)
 +                              strbuf_add(&key, val, len);
 +                      if (type == 'K')
 +                              continue;
 +                      assert(type == 'D');
                        val = NULL;
                        len = 0;
                        /* fall through */
                case 'V':
 -                      handle_property(key, val, len, &type_set);
 -                      key = ~0;
 +                      handle_property(&key, val, len, &type_set);
 +                      strbuf_reset(&key);
                        continue;
                default:
                        die("invalid property line: %s\n", t);
  
  static void handle_node(void)
  {
 -      uint32_t mark = 0;
        const uint32_t type = node_ctx.type;
        const int have_props = node_ctx.propLength != LENGTH_UNKNOWN;
        const int have_text = node_ctx.textLength != LENGTH_UNKNOWN;
 +      /*
 +       * Old text for this node:
 +       *  NULL        - directory or bug
 +       *  empty_blob  - empty
 +       *  "<dataref>" - data retrievable from fast-import
 +       */
 +      static const char *const empty_blob = "::empty::";
 +      const char *old_data = NULL;
  
        if (node_ctx.text_delta)
                die("text deltas not supported");
 -      if (have_text)
 -              mark = next_blob_mark();
 +
        if (node_ctx.action == NODEACT_DELETE) {
                if (have_text || have_props || node_ctx.srcRev)
                        die("invalid dump: deletion node has "
                die("invalid dump: directories cannot have text attached");
  
        /*
 -       * Decide on the new content (mark) and mode (node_ctx.type).
 +       * Find old content (old_data) and decide on the new mode.
         */
        if (node_ctx.action == NODEACT_CHANGE && !~*node_ctx.dst) {
                if (type != REPO_MODE_DIR)
                        die("invalid dump: root of tree is not a regular file");
 +              old_data = NULL;
        } else if (node_ctx.action == NODEACT_CHANGE) {
                uint32_t mode;
 -              if (!have_text)
 -                      mark = repo_read_path(node_ctx.dst);
 +              old_data = repo_read_path(node_ctx.dst);
                mode = repo_read_mode(node_ctx.dst);
                if (mode == REPO_MODE_DIR && type != REPO_MODE_DIR)
                        die("invalid dump: cannot modify a directory into a file");
                        die("invalid dump: cannot modify a file into a directory");
                node_ctx.type = mode;
        } else if (node_ctx.action == NODEACT_ADD) {
 -              if (!have_text && type != REPO_MODE_DIR)
 +              if (type == REPO_MODE_DIR)
 +                      old_data = NULL;
 +              else if (have_text)
 +                      old_data = empty_blob;
 +              else
                        die("invalid dump: adds node without text");
        } else {
                die("invalid dump: Node-path block lacks Node-action");
        /*
         * Save the result.
         */
 -      repo_add(node_ctx.dst, node_ctx.type, mark);
 -      if (have_text)
 -              fast_export_blob(node_ctx.type, mark,
 -                               node_ctx.textLength, &input);
 +      if (type == REPO_MODE_DIR)      /* directories are not tracked. */
 +              return;
 +      assert(old_data);
 +      if (old_data == empty_blob)
 +              /* For the fast_export_* functions, NULL means empty. */
 +              old_data = NULL;
 +      if (!have_text) {
 +              fast_export_modify(REPO_MAX_PATH_DEPTH, node_ctx.dst,
 +                                      node_ctx.type, old_data);
 +              return;
 +      }
 +      fast_export_modify(REPO_MAX_PATH_DEPTH, node_ctx.dst,
 +                              node_ctx.type, "inline");
 +      fast_export_data(node_ctx.type, node_ctx.textLength, &input);
 +}
 +
 +static void begin_revision(void)
 +{
 +      if (!rev_ctx.revision)  /* revision 0 gets no git commit. */
 +              return;
-       fast_export_begin_commit(rev_ctx.revision, rev_ctx.author, rev_ctx.log,
-               dump_ctx.uuid, dump_ctx.url, rev_ctx.timestamp);
++      fast_export_begin_commit(rev_ctx.revision, rev_ctx.author.buf,
++              rev_ctx.log.buf, dump_ctx.uuid.buf, dump_ctx.url.buf,
++              rev_ctx.timestamp);
  }
  
 -static void handle_revision(void)
 +static void end_revision(void)
  {
        if (rev_ctx.revision)
 -              repo_commit(rev_ctx.revision, rev_ctx.author.buf,
 -                      rev_ctx.log.buf, dump_ctx.uuid.buf, dump_ctx.url.buf,
 -                      rev_ctx.timestamp);
 +              fast_export_end_commit(rev_ctx.revision);
  }
  
  void svndump_read(const char *url)
        char *t;
        uint32_t active_ctx = DUMP_CTX;
        uint32_t len;
 -      uint32_t key;
  
-       reset_dump_ctx(pool_intern(url));
+       reset_dump_ctx(url);
        while ((t = buffer_read_line(&input))) {
                val = strstr(t, ": ");
                if (!val)
                        continue;
 -              *val++ = '\0';
 -              *val++ = '\0';
 -              key = pool_intern(t);
 +              val += 2;
  
 -              if (key == keys.svn_fs_dump_format_version) {
 +              /* strlen(key) + 1 */
 +              switch (val - t - 1) {
 +              case sizeof("SVN-fs-dump-format-version"):
 +                      if (constcmp(t, "SVN-fs-dump-format-version"))
 +                              continue;
                        dump_ctx.version = atoi(val);
                        if (dump_ctx.version > 3)
                                die("expected svn dump format version <= 3, found %"PRIu32,
                                    dump_ctx.version);
 -              } else if (key == keys.uuid) {
 +                      break;
 +              case sizeof("UUID"):
 +                      if (constcmp(t, "UUID"))
 +                              continue;
-                       dump_ctx.uuid = pool_intern(val);
+                       strbuf_reset(&dump_ctx.uuid);
+                       strbuf_addstr(&dump_ctx.uuid, val);
 -              } else if (key == keys.revision_number) {
 +                      break;
 +              case sizeof("Revision-number"):
 +                      if (constcmp(t, "Revision-number"))
 +                              continue;
                        if (active_ctx == NODE_CTX)
                                handle_node();
 +                      if (active_ctx == REV_CTX)
 +                              begin_revision();
                        if (active_ctx != DUMP_CTX)
 -                              handle_revision();
 +                              end_revision();
                        active_ctx = REV_CTX;
                        reset_rev_ctx(atoi(val));
 -              } else if (key == keys.node_path) {
 -                      if (active_ctx == NODE_CTX)
 -                              handle_node();
 -                      active_ctx = NODE_CTX;
 -                      reset_node_ctx(val);
 -              } else if (key == keys.node_kind) {
 +                      break;
 +              case sizeof("Node-path"):
 +                      if (prefixcmp(t, "Node-"))
 +                              continue;
 +                      if (!constcmp(t + strlen("Node-"), "path")) {
 +                              if (active_ctx == NODE_CTX)
 +                                      handle_node();
 +                              if (active_ctx == REV_CTX)
 +                                      begin_revision();
 +                              active_ctx = NODE_CTX;
 +                              reset_node_ctx(val);
 +                              break;
 +                      }
 +                      if (constcmp(t + strlen("Node-"), "kind"))
 +                              continue;
                        if (!strcmp(val, "dir"))
                                node_ctx.type = REPO_MODE_DIR;
                        else if (!strcmp(val, "file"))
                                node_ctx.type = REPO_MODE_BLB;
                        else
                                fprintf(stderr, "Unknown node-kind: %s\n", val);
 -              } else if (key == keys.node_action) {
 +                      break;
 +              case sizeof("Node-action"):
 +                      if (constcmp(t, "Node-action"))
 +                              continue;
                        if (!strcmp(val, "delete")) {
                                node_ctx.action = NODEACT_DELETE;
                        } else if (!strcmp(val, "add")) {
                                fprintf(stderr, "Unknown node-action: %s\n", val);
                                node_ctx.action = NODEACT_UNKNOWN;
                        }
 -              } else if (key == keys.node_copyfrom_path) {
 +                      break;
 +              case sizeof("Node-copyfrom-path"):
 +                      if (constcmp(t, "Node-copyfrom-path"))
 +                              continue;
                        pool_tok_seq(REPO_MAX_PATH_DEPTH, node_ctx.src, "/", val);
 -              } else if (key == keys.node_copyfrom_rev) {
 +                      break;
 +              case sizeof("Node-copyfrom-rev"):
 +                      if (constcmp(t, "Node-copyfrom-rev"))
 +                              continue;
                        node_ctx.srcRev = atoi(val);
 -              } else if (key == keys.text_content_length) {
 -                      node_ctx.textLength = atoi(val);
 -              } else if (key == keys.prop_content_length) {
 +                      break;
 +              case sizeof("Text-content-length"):
 +                      if (!constcmp(t, "Text-content-length")) {
 +                              node_ctx.textLength = atoi(val);
 +                              break;
 +                      }
 +                      if (constcmp(t, "Prop-content-length"))
 +                              continue;
                        node_ctx.propLength = atoi(val);
 -              } else if (key == keys.text_delta) {
 -                      node_ctx.text_delta = !strcmp(val, "true");
 -              } else if (key == keys.prop_delta) {
 +                      break;
 +              case sizeof("Text-delta"):
 +                      if (!constcmp(t, "Text-delta")) {
 +                              node_ctx.text_delta = !strcmp(val, "true");
 +                              break;
 +                      }
 +                      if (constcmp(t, "Prop-delta"))
 +                              continue;
                        node_ctx.prop_delta = !strcmp(val, "true");
 -              } else if (key == keys.content_length) {
 +                      break;
 +              case sizeof("Content-length"):
 +                      if (constcmp(t, "Content-length"))
 +                              continue;
                        len = atoi(val);
                        t = buffer_read_line(&input);
                        if (!t)
                                read_props();
                        } else if (active_ctx == NODE_CTX) {
                                handle_node();
 -                              active_ctx = REV_CTX;
 +                              active_ctx = INTERNODE_CTX;
                        } else {
                                fprintf(stderr, "Unexpected content length header: %"PRIu32"\n", len);
                                if (buffer_skip_bytes(&input, len) != len)
                die_short_read();
        if (active_ctx == NODE_CTX)
                handle_node();
 +      if (active_ctx == REV_CTX)
 +              begin_revision();
        if (active_ctx != DUMP_CTX)
 -              handle_revision();
 +              end_revision();
  }
  
  int svndump_init(const char *filename)
  {
        if (buffer_init(&input, filename))
                return error("cannot open %s: %s", filename, strerror(errno));
 -      repo_init();
 +      fast_export_init(REPORT_FILENO);
-       reset_dump_ctx(~0);
+       strbuf_init(&dump_ctx.uuid, 4096);
+       strbuf_init(&dump_ctx.url, 4096);
+       strbuf_init(&rev_ctx.log, 4096);
+       strbuf_init(&rev_ctx.author, 4096);
+       reset_dump_ctx(NULL);
        reset_rev_ctx(0);
        reset_node_ctx(NULL);
 -      init_keys();
        return 0;
  }
  
  void svndump_deinit(void)
  {
-       log_reset();
 -      repo_reset();
 +      fast_export_deinit();
-       reset_dump_ctx(~0);
+       reset_dump_ctx(NULL);
        reset_rev_ctx(0);
        reset_node_ctx(NULL);
+       strbuf_release(&rev_ctx.log);
        if (buffer_deinit(&input))
                fprintf(stderr, "Input error\n");
        if (ferror(stdout))
  
  void svndump_reset(void)
  {
-       log_reset();
 +      fast_export_reset();
        buffer_reset(&input);
-       reset_dump_ctx(~0);
-       reset_rev_ctx(0);
-       reset_node_ctx(NULL);
 -      repo_reset();
+       strbuf_release(&dump_ctx.uuid);
+       strbuf_release(&dump_ctx.url);
+       strbuf_release(&rev_ctx.log);
+       strbuf_release(&rev_ctx.author);
  }