{"thread":{"id":"26379","subject":"[PATCH v3 0/3] Towards a Git-to-SVN bridge","startedAt":"2011-02-01T14:26:40Z","lastAt":"2011-02-02T12:43:06Z","messageCount":13,"participants":["Ramkumar Ramachandra","Erik Faye-Lund","Jonathan Nieder","Junio C Hamano"],"isPatch":true,"patchVersion":3,"patchTotal":3},"messages":[{"id":"160168","messageId":"1296570403-9082-1-git-send-email-artagnon@gmail.com","threadId":"26379","inReplyTo":null,"subject":"[PATCH v3 0/3] Towards a Git-to-SVN bridge","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-02-01T14:26:40Z","receivedAt":"2011-02-01T14:26:40Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nHere's another attempt. I've omitted 3 patches in this round:\n1. The --inline-blobs patch\n2. The time_to_tm patch\n3. The contrib/svn-fe and test-svn-fi patch\n(since those haven't changed from last time)\n\nChanges since last time:\n1. Bugfixes and testsuite. All tests pass.\n2. Style fixes after Junio's review:\n<7v39olok4l.fsf@alter.siamese.dyndns.org>\n3. Implemented an experimetal dispatch table suggested by Jonathan:\n<20110122191801.GB13023@burratino>\n\nThanks for reading.\n\nRamkumar Ramachandra (3):\n  vcs-svn: Introduce svnload, a dumpfile producer\n  t9010-svn-fi: Add tests for svn-fi\n  vcs-svn: Refactor dump_export code into dispatch table\n\n .gitignore            |    1 +\n Makefile              |    9 +-\n t/t9010-svn-fi.sh     |  303 ++++++++++++++++++++++++++++++++++++++++++++++\n test-svn-fi.c         |   20 +++\n vcs-svn/dir_cache.c   |   40 ++++++\n vcs-svn/dir_cache.h   |   12 ++\n vcs-svn/dump_export.c |  150 +++++++++++++++++++++++\n vcs-svn/dump_export.h |   35 ++++++\n vcs-svn/svnload.c     |  322 +++++++++++++++++++++++++++++++++++++++++++++++++\n vcs-svn/svnload.h     |   10 ++\n 10 files changed, 899 insertions(+), 3 deletions(-)\n create mode 100644 t/t9010-svn-fi.sh\n create mode 100644 test-svn-fi.c\n create mode 100644 vcs-svn/dir_cache.c\n create mode 100644 vcs-svn/dir_cache.h\n create mode 100644 vcs-svn/dump_export.c\n create mode 100644 vcs-svn/dump_export.h\n create mode 100644 vcs-svn/svnload.c\n create mode 100644 vcs-svn/svnload.h\n\n-- \n1.7.4.rc1.7.g2cf08.dirty\n"},{"id":"160171","messageId":"1296570403-9082-2-git-send-email-artagnon@gmail.com","threadId":"26379","inReplyTo":"1296570403-9082-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 1/3] vcs-svn: Introduce svnload, a dumpfile producer","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-02-01T14:26:41Z","receivedAt":"2011-02-01T14:26:41Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Design-wise, svnload resembles svndump. Include a Makefile rule to\nbuild it into vcs-svn/lib.a.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n Makefile              |    4 +-\n vcs-svn/dir_cache.c   |   40 ++++++\n vcs-svn/dir_cache.h   |   12 ++\n vcs-svn/dump_export.c |  149 +++++++++++++++++++++++\n vcs-svn/dump_export.h |   33 +++++\n vcs-svn/svnload.c     |  322 +++++++++++++++++++++++++++++++++++++++++++++++++\n vcs-svn/svnload.h     |   10 ++\n 7 files changed, 568 insertions(+), 2 deletions(-)\n create mode 100644 vcs-svn/dir_cache.c\n create mode 100644 vcs-svn/dir_cache.h\n create mode 100644 vcs-svn/dump_export.c\n create mode 100644 vcs-svn/dump_export.h\n create mode 100644 vcs-svn/svnload.c\n create mode 100644 vcs-svn/svnload.h\n\ndiff --git a/Makefile b/Makefile\nindex 1345c38..d9c2442 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1834,9 +1834,9 @@ ifndef NO_CURL\n endif\n XDIFF_OBJS = xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \\\n \txdiff/xmerge.o xdiff/xpatience.o\n-VCSSVN_OBJS = vcs-svn/line_buffer.o \\\n+VCSSVN_OBJS = vcs-svn/line_buffer.o vcs-svn/svnload.o vcs-svn/dump_export.o \\\n \tvcs-svn/repo_tree.o vcs-svn/fast_export.o vcs-svn/sliding_window.o \\\n-\tvcs-svn/svndiff.o vcs-svn/svndump.o\n+\tvcs-svn/svndiff.o vcs-svn/svndump.o vcs-svn/dir_cache.o\n VCSSVN_TEST_OBJS = test-obj-pool.o \\\n \ttest-line-buffer.o test-treap.o test-svn-fe.o\n OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS) $(VCSSVN_OBJS)\ndiff --git a/vcs-svn/dir_cache.c b/vcs-svn/dir_cache.c\nnew file mode 100644\nindex 0000000..9a608ce\n--- /dev/null\n+++ b/vcs-svn/dir_cache.c\n@@ -0,0 +1,40 @@\n+/*\n+ * Licensed under a two-clause BSD-style license.\n+ * See LICENSE for details.\n+ */\n+\n+#include \"git-compat-util.h\"\n+#include \"string-list.h\"\n+#include \"line_buffer.h\"\n+#include \"dump_export.h\"\n+\n+static struct string_list dirents = STRING_LIST_INIT_DUP;\n+static struct string_list_item *dir = NULL;\n+\n+void dir_cache_add(const char *path, enum node_kind kind) {\n+\tdir = string_list_insert(&dirents, path);\n+\tdir->util = malloc(sizeof(enum node_kind));\n+\t*((enum node_kind *)(dir->util)) = kind;\n+}\n+\n+void dir_cache_remove(const char *path) {\n+\tdir = string_list_lookup(&dirents, path);\n+\tif (dir)\n+\t\t*((enum node_kind *)(dir->util)) = NODE_KIND_UNKNOWN;\n+}\n+\n+enum node_kind dir_cache_lookup(const char *path) {\n+\tdir = string_list_lookup(&dirents, path);\n+\tif (dir)\n+\t\treturn *((enum node_kind *)(dir->util));\n+\telse\n+\t\treturn NODE_KIND_UNKNOWN;\n+}\n+\n+void dir_cache_init() {\n+\treturn;\n+}\n+\n+void dir_cache_deinit() {\n+\tstring_list_clear(&dirents, 1);\n+}\ndiff --git a/vcs-svn/dir_cache.h b/vcs-svn/dir_cache.h\nnew file mode 100644\nindex 0000000..43c3797\n--- /dev/null\n+++ b/vcs-svn/dir_cache.h\n@@ -0,0 +1,12 @@\n+#ifndef DIR_CACHE_H_\n+#define DIR_CACHE_H_\n+\n+#include \"dump_export.h\"\n+\n+void dir_cache_add(const char *path, enum node_kind kind);\n+void dir_cache_remove(const char *path);\n+enum node_kind dir_cache_lookup(const char *path);\n+void dir_cache_init();\n+void dir_cache_deinit();\n+\n+#endif\ndiff --git a/vcs-svn/dump_export.c b/vcs-svn/dump_export.c\nnew file mode 100644\nindex 0000000..2b23f77\n--- /dev/null\n+++ b/vcs-svn/dump_export.c\n@@ -0,0 +1,149 @@\n+/*\n+ * Licensed under a two-clause BSD-style license.\n+ * See LICENSE for details.\n+ */\n+\n+#include \"git-compat-util.h\"\n+#include \"strbuf.h\"\n+#include \"line_buffer.h\"\n+#include \"dump_export.h\"\n+#include \"dir_cache.h\"\n+\n+static struct strbuf props;\n+\n+void dump_export_begin_rev(int revision, const char *revprops,\n+\t\t\tint prop_len)\n+{\n+\tprintf(\"Revision-number: %d\\n\", revision);\n+\tprintf(\"Prop-content-length: %d\\n\", prop_len);\n+\tprintf(\"Content-length: %d\\n\\n\", prop_len);\n+\tprintf(\"%s\\n\", revprops);\n+}\n+\n+void dump_export_node(const char *path, enum node_kind kind,\n+\t\tenum node_action action, unsigned long text_len,\n+\t\tunsigned long copyfrom_rev, const char *copyfrom_path)\n+{\n+\tint dump_props = 1; /* Boolean */\n+\tstrbuf_reset(&props);\n+\tprintf(\"Node-path: %s\\n\", path);\n+\tswitch (kind) {\n+\tcase NODE_KIND_NORMAL:\n+\t\tprintf(\"Node-kind: file\\n\");\n+\t\tbreak;\n+\tcase NODE_KIND_EXECUTABLE:\n+\t\tprintf(\"Node-kind: file\\n\");\n+\t\tstrbuf_addf(&props, \"K 14\\nsvn:executable\\nV 1\\n*\\n\");\n+\t\tbreak;\n+\tcase NODE_KIND_SYMLINK:\n+\t\tprintf(\"Node-kind: file\\n\");\n+\t\tstrbuf_addf(&props, \"K 11\\nsvn:special\\nV 1\\n*\\n\");\n+\t\tbreak;\n+\tcase NODE_KIND_GITLINK:\n+\t\tprintf(\"Node-kind: file\\n\");\n+\t\tbreak;\n+\tcase NODE_KIND_DIR:\n+\t\tprintf(\"Node-kind: dir\\n\");\n+\t\tbreak;\n+\tcase NODE_KIND_SUBDIR:\n+\t\tdie(\"Unsupported: subdirectory\");\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\tstrbuf_add(&props, \"PROPS-END\\n\", 10);\n+\n+\tswitch (action) {\n+\tcase NODE_ACTION_CHANGE:\n+\t\tprintf(\"Node-action: change\\n\");\n+\t\tbreak;\n+\tcase NODE_ACTION_ADD:\n+\t\tprintf(\"Node-action: add\\n\");\n+\t\tbreak;\n+\tcase NODE_ACTION_REPLACE:\n+\t\tprintf(\"Node-action: replace\\n\");\n+\t\tbreak;\n+\tcase NODE_ACTION_DELETE:\n+\t\tprintf(\"Node-action: delete\\n\");\n+\t\tdump_props = 0;\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\tif (copyfrom_rev != SVN_INVALID_REV) {\n+\t\tprintf(\"Node-copyfrom-rev: %lu\\n\", copyfrom_rev);\n+\t\tprintf(\"Node-copyfrom-path: %s\\n\", copyfrom_path);\n+\t}\n+\tif (dump_props) {\n+\t\tprintf(\"Prop-delta: false\\n\");\n+\t\tprintf(\"Prop-content-length: %lu\\n\", props.len);\n+\t}\n+\tif (text_len) {\n+\t\tprintf(\"Text-delta: false\\n\");\t\t\n+\t\tprintf(\"Text-content-length: %lu\\n\", text_len);\n+\t}\n+\tif (text_len || dump_props) {\n+\t\tprintf(\"Content-length: %lu\\n\\n\", text_len + props.len);\n+\t\tprintf(\"%s\", props.buf);\n+\t}\n+\tif (!text_len)\n+\t\tprintf(\"\\n\");\n+}\n+\n+void dump_export_node_r(const char *path, enum node_kind kind,\n+\t\t\tenum node_action action, unsigned long text_len,\n+\t\t\tunsigned long copyfrom_rev, const char *copyfrom_path)\n+{\n+\tchar *start, *t;\n+\tstart = (char *) path;\n+\n+\twhile ((t = strchr(start, '/'))) {\n+\t\t\t*t = '\\0';\n+\t\t\tif (dir_cache_lookup(path) == NODE_KIND_UNKNOWN) {\n+\t\t\t\tdir_cache_add(path, NODE_KIND_NORMAL);\n+\t\t\t\tdump_export_node(path, NODE_KIND_DIR,\n+\t\t\t\t\t\tNODE_ACTION_ADD, 0,\n+\t\t\t\t\t\tSVN_INVALID_REV, NULL);\n+\t\t\t}\n+\t\t\t*t = '/';   /* Change it back */\n+\t\t\tstart = t + 1;\n+\t}\n+\tswitch (dir_cache_lookup(path)) {\n+\tcase NODE_KIND_UNKNOWN:\n+\t\taction = NODE_ACTION_ADD;\n+\t\tbreak;\n+\tcase NODE_KIND_SYMLINK:\n+\t\tdir_cache_remove(path);\n+\t\tdump_export_node(path, NODE_KIND_UNKNOWN,\n+\t\t\t\tNODE_ACTION_DELETE,\n+\t\t\t\t0, SVN_INVALID_REV, NULL);\n+\t\taction = NODE_ACTION_ADD;\n+\t\tbreak;\n+\tcase NODE_KIND_DIR:\n+\t\tdie(\"File was previously a directory?\");\n+\t\tbreak;\n+\tcase NODE_KIND_SUBDIR:\n+\t\tdie(\"Subdirectories unsupported\");\n+\t\tbreak;\n+\tdefault:\n+\t\taction = NODE_ACTION_CHANGE;\n+\t\tbreak;\n+\t}\n+\tdir_cache_add(path, kind);\n+\tdump_export_node(path, kind, action, text_len, copyfrom_rev, copyfrom_path);\n+}\n+\n+void dump_export_text(struct line_buffer *data, off_t len)\n+{\n+\tbuffer_copy_bytes(data, len);\n+}\n+\n+void dump_export_init()\n+{\n+\tstrbuf_init(&props, MAX_GITSVN_LINE_LEN);\n+\tprintf(\"SVN-fs-dump-format-version: 3\\n\\n\");\n+}\n+\n+void dump_export_deinit()\n+{\n+\tstrbuf_release(&props);\n+}\ndiff --git a/vcs-svn/dump_export.h b/vcs-svn/dump_export.h\nnew file mode 100644\nindex 0000000..e9f51a3\n--- /dev/null\n+++ b/vcs-svn/dump_export.h\n@@ -0,0 +1,33 @@\n+#ifndef DUMP_EXPORT_H_\n+#define DUMP_EXPORT_H_\n+\n+#define MAX_GITSVN_LINE_LEN 4096\n+#define SVN_INVALID_REV 0\n+\n+enum node_action {\n+\tNODE_ACTION_UNKNOWN,\n+\tNODE_ACTION_CHANGE,\n+\tNODE_ACTION_ADD,\n+\tNODE_ACTION_DELETE,\n+\tNODE_ACTION_REPLACE\n+};\n+\n+enum node_kind {\n+\tNODE_KIND_UNKNOWN,       /* Missing node */\n+\tNODE_KIND_NORMAL,\n+\tNODE_KIND_EXECUTABLE,\n+\tNODE_KIND_SYMLINK,\n+\tNODE_KIND_GITLINK,\n+\tNODE_KIND_DIR,           /* SVN-specific */\n+\tNODE_KIND_SUBDIR\n+};\n+\n+void dump_export_begin_rev(int revision, const char *revprops, int prop_len);\n+void dump_export_text(struct line_buffer *data, off_t len);\n+void dump_export_node_r(const char *path, enum node_kind kind,\n+\t\t\tenum node_action action, unsigned long text_len,\n+\t\t\tunsigned long copyfrom_rev, const char *copyfrom_path);\n+void dump_export_init();\n+void dump_export_deinit();\n+\n+#endif\ndiff --git a/vcs-svn/svnload.c b/vcs-svn/svnload.c\nnew file mode 100644\nindex 0000000..40fc1db\n--- /dev/null\n+++ b/vcs-svn/svnload.c\n@@ -0,0 +1,322 @@\n+/*\n+ * Produce a dumpfile v3 from a fast-import stream.\n+ * Load the dump into the SVN repository with:\n+ * svnrdump load <URL> <dumpfile\n+ *\n+ * Licensed under a two-clause BSD-style license.\n+ * See LICENSE for details.\n+ */\n+\n+#include \"cache.h\"\n+#include \"git-compat-util.h\"\n+#include \"line_buffer.h\"\n+#include \"dump_export.h\"\n+#include \"dir_cache.h\"\n+\n+#define SVN_DATE_FORMAT \"%Y-%m-%dT%H:%M:%S.000000Z\"\n+#define SVN_DATE_LEN 27\n+#define LENGTH_UNKNOWN (~0)\n+\n+static struct line_buffer input = LINE_BUFFER_INIT;\n+\n+static struct {\n+\tunsigned long prop_len, text_len, copyfrom_rev;\n+\tint text_delta, prop_delta; /* false=0, true=1, unknown=-1 */\n+\tenum node_action action;\n+\tenum node_kind kind;\n+\tstruct strbuf copyfrom_path, path;\n+} node_ctx;\n+\n+static struct {\n+\tint rev, text_len;\n+\tstruct strbuf props, log;\n+\tstruct strbuf svn_author, author, committer;\n+\tstruct strbuf author_date, committer_date;\n+\tstruct strbuf author_email, committer_email;\n+} rev_ctx;\n+\n+static enum {\n+\tUNKNOWN_CTX,\n+\tCOMMIT_CTX,\n+\tBLOB_CTX\n+} active_ctx;\n+\n+static void populate_revprops(struct strbuf *props, size_t author_len,\n+\t\t\tconst char *author, size_t log_len, const char *log,\n+\t\t\tsize_t date_len, const char *date)\n+{\n+\tstrbuf_reset(props);\n+\tstrbuf_addf(props, \"K 10\\nsvn:author\\nV %lu\\n%s\\n\", author_len, author);\n+\tstrbuf_addf(props, \"K 7\\nsvn:log\\nV %lu\\n%s\\n\", log_len, log);\n+\tif (date_len)\n+\t\t/* SVN doesn't like an empty svn:date value */\n+\t\tstrbuf_addf(props, \"K 8\\nsvn:date\\nV %lu\\n%s\\n\", date_len, date);\n+\tstrbuf_add(props, \"PROPS-END\\n\", 10);\n+}\n+\n+static void parse_author_line(char *val, struct strbuf *name,\n+\t\t\tstruct strbuf *email, struct strbuf *date)\n+{\n+\tchar *t, *tz_off;\n+\tchar time_buf[SVN_DATE_LEN + 1];\n+\tint tz_off_buf;\n+\tconst struct tm *tm_time;\n+\n+\t/* Author Name <author@email.com> 1170199019 +0530 */\n+\tstrbuf_reset(name);\n+\tstrbuf_reset(email);\n+\tstrbuf_reset(date);\n+\tif (!val) die(\"Malformed author line\");\n+\tif (!(tz_off = strrchr(val, ' '))) goto error;\n+\t*tz_off++ = '\\0';\n+\tif (!(t = strrchr(val, ' '))) goto error;\n+\t*(t - 1) = '\\0'; /* Ignore '>' from email */\n+\tt++;\n+\ttz_off_buf = atoi(tz_off);\n+\tif (tz_off_buf > 1200 || tz_off_buf  < -1200) goto error;\n+\ttm_time = time_to_tm(strtoul(t, NULL, 10), tz_off_buf);\n+\tstrftime(time_buf, SVN_DATE_LEN + 1, SVN_DATE_FORMAT, tm_time);\n+\tstrbuf_add(date, time_buf, SVN_DATE_LEN);\n+\tif (!(t = strchr(val, '<'))) goto error;\n+\t*(t - 1) = '\\0'; /* Ignore ' <' from email */\n+\tt++;\n+\tstrbuf_add(email, t, strlen(t));\n+\tstrbuf_add(name, val, strlen(val));\n+\treturn;\n+error:\n+\tdie(\"Malformed author line: %s\", val);\n+}\n+\n+void build_svn_author(struct strbuf *svn_author)\n+{\n+\tchar *t, *email;\n+\n+\tstrbuf_reset(svn_author);\n+\temail = rev_ctx.author_email.buf;\n+\tif (!(t = strchr(email, '@')))\n+\t\tgoto error;\n+\tstrbuf_add(svn_author, email, t - email);\n+\treturn;\n+error:\n+\tdie(\"Malformed email: %s\", email);\n+}\n+\n+int parse_filemodify_mode(char *val)\n+{\n+\tchar *t;\n+\n+\tif (!(t = strchr(val, ' '))) goto error;\n+\tswitch (t - val) {\n+\tcase 6:\n+\t\tif (!memcmp(val, \"100644\", 6))\n+\t\t\tnode_ctx.kind = NODE_KIND_NORMAL;\n+\t\telse if (!memcmp(val, \"100755\", 6))\n+\t\t\tnode_ctx.kind = NODE_KIND_EXECUTABLE;\n+\t\telse if (!memcmp(val, \"120000\", 6))\n+\t\t\tnode_ctx.kind = NODE_KIND_SYMLINK;\n+\t\telse if (!memcmp(val, \"160000\", 6))\n+\t\t\tnode_ctx.kind = NODE_KIND_GITLINK;\n+\t\telse if (!memcmp(val, \"040000\", 6))\n+\t\t\tnode_ctx.kind = NODE_KIND_SUBDIR;\n+\t\telse\n+\t\t\tgoto error;\n+\t\tbreak;\n+\tcase 3:\n+\t\tif (!memcmp(val, \"755\", 3))\n+\t\t\tnode_ctx.kind = NODE_KIND_EXECUTABLE;\n+\t\telse if (!memcmp(val, \"644\", 3))\n+\t\t\tnode_ctx.kind = NODE_KIND_NORMAL;\n+\t\telse\n+\t\t\tgoto error;\n+\t\tbreak;\n+\tdefault:\n+\t\tgoto error;\n+\t}\n+\treturn t - val + 1;\n+error:\n+\tdie(\"Unrecognized mode: %s\", val);\n+}\n+\n+void svnload_read(void)\n+{\n+\tchar *t, *val;\n+\tint len;\n+\n+\twhile ((t = buffer_read_line(&input))) {\n+\t\tif ((val = strchr(t, ' ')))\n+\t\t\t*val++ = '\\0';\n+\t\tlen = (val ? val - t - 1 : strlen(t));\n+\n+\t\tswitch (len) {\n+\t\tcase 1:\n+\t\t\tif (!memcmp(t, \"D\", 1)) {\n+\t\t\t\tnode_ctx.action = NODE_ACTION_DELETE;\n+\t\t\t} else if (!memcmp(t, \"C\", 1)) {\n+\t\t\t\tnode_ctx.action = NODE_ACTION_ADD;\n+\t\t\t} else if (!memcmp(t, \"R\", 1)) {\n+\t\t\t\tnode_ctx.action = NODE_ACTION_REPLACE;\n+\t\t\t} else if (!memcmp(t, \"M\", 1)) {\n+\t\t\t\tif (!val) goto error;\n+\t\t\t\tnode_ctx.action = NODE_ACTION_CHANGE;\n+\t\t\t\tval += parse_filemodify_mode(val);\n+\t\t\t\tif (!(t = strchr(val, ' '))) goto error;\n+\t\t\t\t*t++ = '\\0';\n+\t\t\t\tstrbuf_reset(&node_ctx.path);\n+\t\t\t\tstrbuf_add(&node_ctx.path, t, strlen(t));\n+\t\t\t\tif (!strncmp(val, \"inline\", 6))\n+\t\t\t\t\tactive_ctx = BLOB_CTX;\n+\t\t\t\telse if (*val == ':')\n+\t\t\t\t\tdie(\"Unsupported dataref: marks\");\n+\t\t\t\telse {\n+\t\t\t\terror:\n+\t\t\t\t\tdie(\"Malformed filemodify line: %s\", t);\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tbreak;\n+\t\tcase 2:\n+\t\t\tif (!memcmp(t, \"ls\", 2))\n+\t\t\t\tdie(\"ls not supported\");\n+\t\tcase 3:\n+\t\t\tif (!memcmp(t, \"tag\", 3))\n+\t\t\t\tcontinue;\n+\t\t\tbreak;\n+\t\tcase 4:\n+\t\t\tif (!memcmp(t, \"blob\", 4))\n+\t\t\t\tcontinue;\n+\t\t\telse if (!memcmp(t, \"mark\", 4))\n+\t\t\t\tcontinue;\n+\t\t\telse if (!memcmp(t, \"from\", 4))\n+\t\t\t\tcontinue;\n+\t\t\telse if (!memcmp(t, \"data\", 4)) {\n+\t\t\t\tswitch (active_ctx) {\n+\t\t\t\tcase COMMIT_CTX:\n+\t\t\t\t\tstrbuf_reset(&rev_ctx.log);\n+\t\t\t\t\tbuffer_read_binary(&input,\n+\t\t\t\t\t\t\t&rev_ctx.log,\n+\t\t\t\t\t\t\tstrtoul(val, NULL, 10));\n+\t\t\t\t\tpopulate_revprops(&rev_ctx.props,\n+\t\t\t\t\t\t\trev_ctx.svn_author.len,\n+\t\t\t\t\t\t\trev_ctx.svn_author.buf,\n+\t\t\t\t\t\t\trev_ctx.log.len,\n+\t\t\t\t\t\t\trev_ctx.log.buf,\n+\t\t\t\t\t\t\trev_ctx.author_date.len,\n+\t\t\t\t\t\t\trev_ctx.author_date.buf);\n+\t\t\t\t\tdump_export_begin_rev(rev_ctx.rev,\n+\t\t\t\t\t\t\trev_ctx.props.buf,\n+\t\t\t\t\t\t\trev_ctx.props.len);\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase BLOB_CTX:\n+\t\t\t\t\tnode_ctx.text_len = strtoul(val, NULL, 10);\n+\t\t\t\t\tdump_export_node_r(node_ctx.path.buf, node_ctx.kind,\n+\t\t\t\t\t\t\tnode_ctx.action, node_ctx.text_len,\n+\t\t\t\t\t\t\tSVN_INVALID_REV, NULL);\n+\t\t\t\t\tbuffer_copy_bytes(&input, node_ctx.text_len);\n+\t\t\t\t\tbreak;\n+\t\t\t\tdefault:\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tbreak;\n+\t\tcase 5:\n+\t\t\tif (!memcmp(t, \"reset\", 5))\n+\t\t\t\tcontinue;\n+\t\t\tif (!memcmp(t, \"merge\", 5))\n+\t\t\t\tcontinue;\n+\t\t\tbreak;\n+\t\tcase 6:\n+\t\t\tif (!memcmp(t, \"author\", 6)) {\n+\t\t\t\tparse_author_line(val, &rev_ctx.author,\n+\t\t\t\t\t\t&rev_ctx.author_email,\n+\t\t\t\t\t\t&rev_ctx.author_date);\n+\t\t\t\tbuild_svn_author(&rev_ctx.svn_author);\n+\t\t\t} else if (!memcmp(t, \"commit\", 6)) {\n+\t\t\t\trev_ctx.rev++;\n+\t\t\t\tactive_ctx = COMMIT_CTX;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\tcase 8:\n+\t\t\tif (!memcmp(t, \"cat-blob\", 8))\n+\t\t\t\tdie(\"cat-blob unsupported\");\n+\t\t\tbreak;\n+\t\tcase 9:\n+\t\t\tif (!memcmp(t, \"deleteall\", 9))\n+\t\t\t\tcontinue;\n+\t\t\telse if (!memcmp(t, \"committer\", 9))\n+\t\t\t\tparse_author_line(val, &rev_ctx.committer,\n+\t\t\t\t\t\t&rev_ctx.committer_email,\n+\t\t\t\t\t\t&rev_ctx.committer_date);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+}\n+\n+static void reset_rev_ctx(int revision)\n+{\n+\trev_ctx.rev = revision;\n+\tstrbuf_reset(&rev_ctx.props);\n+\tstrbuf_reset(&rev_ctx.log);\n+\tstrbuf_reset(&rev_ctx.svn_author);\n+\tstrbuf_reset(&rev_ctx.author);\n+\tstrbuf_reset(&rev_ctx.committer);\n+\tstrbuf_reset(&rev_ctx.author_date);\n+\tstrbuf_reset(&rev_ctx.committer_date);\n+\tstrbuf_reset(&rev_ctx.author_email);\n+\tstrbuf_reset(&rev_ctx.committer_email);\n+}\n+\n+static void reset_node_ctx(void)\n+{\n+\tnode_ctx.prop_len = LENGTH_UNKNOWN;\n+\tnode_ctx.text_len = LENGTH_UNKNOWN;\n+\tnode_ctx.copyfrom_rev = SVN_INVALID_REV;\n+\tnode_ctx.text_delta = -1;\n+\tnode_ctx.prop_delta = -1;\n+\tstrbuf_reset(&node_ctx.copyfrom_path);\n+\tstrbuf_reset(&node_ctx.path);\n+}\n+\n+int svnload_init(const char *filename)\n+{\n+\tif (buffer_init(&input, filename))\n+\t\treturn error(\"cannot open %s: %s\", filename, strerror(errno));\n+\tactive_ctx = UNKNOWN_CTX;\n+\tstrbuf_init(&rev_ctx.props, MAX_GITSVN_LINE_LEN);\n+\tstrbuf_init(&rev_ctx.log, MAX_GITSVN_LINE_LEN);\n+\tstrbuf_init(&rev_ctx.author, MAX_GITSVN_LINE_LEN);\n+\tstrbuf_init(&rev_ctx.committer, MAX_GITSVN_LINE_LEN);\n+\tstrbuf_init(&rev_ctx.svn_author, MAX_GITSVN_LINE_LEN);\n+\tstrbuf_init(&rev_ctx.author_date, MAX_GITSVN_LINE_LEN);\n+\tstrbuf_init(&rev_ctx.committer_date, MAX_GITSVN_LINE_LEN);\n+\tstrbuf_init(&rev_ctx.author_email, MAX_GITSVN_LINE_LEN);\n+\tstrbuf_init(&rev_ctx.committer_email, MAX_GITSVN_LINE_LEN);\n+\tstrbuf_init(&node_ctx.path, MAX_GITSVN_LINE_LEN);\n+\tstrbuf_init(&node_ctx.copyfrom_path, MAX_GITSVN_LINE_LEN);\n+\tdump_export_init();\n+\tdir_cache_init();\n+\treturn 0;\n+}\n+\n+void svnload_deinit(void)\n+{\n+\treset_rev_ctx(0);\n+\treset_node_ctx();\n+\tstrbuf_release(&rev_ctx.props);\n+\tstrbuf_release(&rev_ctx.log);\n+\tstrbuf_release(&rev_ctx.author);\n+\tstrbuf_release(&rev_ctx.committer);\n+\tstrbuf_release(&rev_ctx.svn_author);\n+\tstrbuf_release(&rev_ctx.author_date);\n+\tstrbuf_release(&rev_ctx.committer_date);\n+\tstrbuf_release(&rev_ctx.author_email);\n+\tstrbuf_release(&rev_ctx.committer_email);\n+\tstrbuf_release(&node_ctx.path);\n+\tstrbuf_release(&node_ctx.copyfrom_path);\n+\tdump_export_deinit();\n+\tdir_cache_deinit();\n+\tif (buffer_deinit(&input))\n+\t\tfprintf(stderr, \"Input error\\n\");\n+\tif (ferror(stdout))\n+\t\tfprintf(stderr, \"Output error\\n\");\n+}\ndiff --git a/vcs-svn/svnload.h b/vcs-svn/svnload.h\nnew file mode 100644\nindex 0000000..0c8fe8b\n--- /dev/null\n+++ b/vcs-svn/svnload.h\n@@ -0,0 +1,10 @@\n+#ifndef SVNLOAD_H_\n+#define SVNLOAD_H_\n+\n+#define SVN_INVALID_REV -1\n+\n+int svnload_init(const char *filename);\n+void svnload_deinit(void);\n+void svnload_read(void);\n+\n+#endif\n-- \n1.7.4.rc1.7.g2cf08.dirty\n"},{"id":"160170","messageId":"1296570403-9082-3-git-send-email-artagnon@gmail.com","threadId":"26379","inReplyTo":"1296570403-9082-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 2/3] t9010-svn-fi: Add tests for svn-fi","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-02-01T14:26:42Z","receivedAt":"2011-02-01T14:26:42Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Create a test-svn-fi in toplevel directory, add rules to build it, and\nadd some basic tests.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n .gitignore        |    1 +\n Makefile          |    5 +-\n t/t9010-svn-fi.sh |  303 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n test-svn-fi.c     |   20 ++++\n 4 files changed, 328 insertions(+), 1 deletions(-)\n create mode 100644 t/t9010-svn-fi.sh\n create mode 100644 test-svn-fi.c\n\ndiff --git a/.gitignore b/.gitignore\nindex b48d1ee..c8c8a17 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -177,6 +177,7 @@\n /test-sigchain\n /test-subprocess\n /test-svn-fe\n+/test-svn-fi\n /common-cmds.h\n *.tar.gz\n *.dsc\ndiff --git a/Makefile b/Makefile\nindex d9c2442..cb21b78 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -431,6 +431,7 @@ TEST_PROGRAMS_NEED_X += test-sha1\n TEST_PROGRAMS_NEED_X += test-sigchain\n TEST_PROGRAMS_NEED_X += test-subprocess\n TEST_PROGRAMS_NEED_X += test-svn-fe\n+TEST_PROGRAMS_NEED_X += test-svn-fi\n TEST_PROGRAMS_NEED_X += test-index-version\n \n TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))\n@@ -1838,7 +1839,7 @@ VCSSVN_OBJS = vcs-svn/line_buffer.o vcs-svn/svnload.o vcs-svn/dump_export.o \\\n \tvcs-svn/repo_tree.o vcs-svn/fast_export.o vcs-svn/sliding_window.o \\\n \tvcs-svn/svndiff.o vcs-svn/svndump.o vcs-svn/dir_cache.o\n VCSSVN_TEST_OBJS = test-obj-pool.o \\\n-\ttest-line-buffer.o test-treap.o test-svn-fe.o\n+\ttest-line-buffer.o test-treap.o test-svn-fe.o test-svn-fi.o\n OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS) $(VCSSVN_OBJS)\n \n dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)\n@@ -2129,6 +2130,8 @@ test-parse-options$X: parse-options.o\n \n test-svn-fe$X: vcs-svn/lib.a\n \n+test-svn-fi$X: vcs-svn/lib.a\n+\n .PRECIOUS: $(TEST_OBJS)\n \n test-%$X: test-%.o $(GITLIBS)\ndiff --git a/t/t9010-svn-fi.sh b/t/t9010-svn-fi.sh\nnew file mode 100644\nindex 0000000..676c7fc\n--- /dev/null\n+++ b/t/t9010-svn-fi.sh\n@@ -0,0 +1,303 @@\n+#!/bin/sh\n+\n+test_description='check svn dumpfile exporter'\n+\n+. ./test-lib.sh\n+\n+if ! svnadmin -h >/dev/null 2>&1\n+then\n+\tskip_all='skipping svn-fi tests, svn not available'\n+\ttest_done\n+fi\n+\n+svnrepo=\"testsvn\"\n+\n+reinit_svn () {\n+\trm -rf \"$svnrepo\" &&\n+\trm -f stream &&\n+\tsvnadmin create \"$svnrepo\" &&\n+\tprintf \"#!/bin/sh\" > \"$svnrepo\"/hooks/pre-revprop-change &&\n+\tchmod +x \"$svnrepo\"/hooks/pre-revprop-change &&\n+\tmkfifo stream\n+}\n+\n+svn_look () {\n+\tsubcommand=$1 &&\n+\tshift &&\n+\tsvnlook \"$subcommand\" \"$svnrepo\" \"$@\"\n+}\n+\n+try_load () {\n+\tinput=$1 &&\n+\tmaybe_fail=${2:+test_$2} &&\n+\n+\t{\n+\t\t$maybe_fail test-svn-fi \"$input\" >stream &\n+\t} &&\n+\tsvnadmin load \"$svnrepo\" <stream &&\n+\twait $!\n+}\n+\n+test_expect_success 'normal empty files' '\n+\treinit_svn &&\n+\tcat >expect.tree <<-\\EOF &&\n+\t/\n+\t foo\n+\t bar\n+\tEOF\n+\tcat >input <<-\\EOF &&\n+\treset refs/heads/master\n+\tcommit refs/heads/master\n+\tmark :1\n+\tauthor nobody <nobody@localhost> 1170199019 +0100\n+\tcommitter nobody <nobody@localhost> 1170199019 +0100\n+\tdata 0\n+\tM 100644 inline foo\n+\tdata 0\n+\tM 644 inline bar\n+\tdata 0\n+\n+\tEOF\n+\ttry_load input &&\n+\tsvn_look tree >actual.tree &&\n+\ttest_cmp expect.tree actual.tree\n+'\n+\n+# TODO: How to test date? Need to convert from local timestamp\n+test_expect_success 'svn:author and svn:log' '\n+\treinit_svn &&\n+\techo \"nothing\" >expect.log &&\n+\techo \"nobody\" >expect.author &&\n+\tcat >input <<-\\EOF &&\n+\treset refs/heads/master\n+\tcommit refs/heads/master\n+\tmark :1\n+\tauthor nobody <nobody@localhost> 1170199019 +0100\n+\tcommitter nobody <nobody@localhost> 1170199019 +0100\n+\tdata 7\n+\tnothing\n+\tM 100644 inline foo\n+\tdata 0\n+\n+\tEOF\n+\ttry_load input &&\n+\tsvn_look log >actual.log &&\n+\tsvn_look author >actual.author &&\n+\ttest_cmp expect.log actual.log &&\n+\ttest_cmp expect.author actual.author\n+'\n+\n+test_expect_success 'missing author line' '\n+\treinit_svn &&\n+\tcat >expect.tree <<-\\EOF &&\n+\t/\n+\t foo\n+\tEOF\n+\tcat >input <<-\\EOF &&\n+\treset refs/heads/master\n+\tcommit refs/heads/master\n+\tmark :1\n+\tcommitter nobody <nobody@localhost> 1170199019 +0100\n+\tdata 0\n+\tM 100644 inline foo\n+\tdata 0\n+\n+\tEOF\n+\ttry_load input &&\n+\tsvn_look tree >actual.tree &&\n+\ttest_cmp expect.tree actual.tree\n+'\n+\n+test_expect_success 'blob marks unsupported' '\n+\treinit_svn &&\n+\tcat >input <<-\\EOF &&\n+\tblob\n+\tmark :1\n+\tdata 0\n+\n+\treset refs/heads/master\n+\tcommit refs/heads/master\n+\tmark :2\n+\tauthor nobody <nobody@localhost> 1170199019 +0100\n+\tcommitter nobody <nobody@localhost> 1170199019 +0100\n+\tdata 0\n+\tM 100644 :1 foo\n+\n+\tEOF\n+\ttry_load input must_fail\n+'\n+\n+test_expect_success 'malformed fast-import stream: filemodify' '\n+\treinit_svn &&\n+\tcat >input <<-\\EOF &&\n+\treset refs/heads/master\n+\tcommit refs/heads/master\n+\tmark :1\n+\tauthor nobody <nobody@localhost> 1170199019 +0100\n+\tcommitter nobody <nobody@localhost> 1170199019 +0100\n+\tdata 0\n+\tM 100644 inline\n+\n+\tEOF\n+\ttry_load input must_fail\n+'\n+\n+test_expect_success 'malformed fast-import stream: author' '\n+\treinit_svn &&\n+\tcat >input <<-\\EOF &&\n+\treset refs/heads/master\n+\tcommit refs/heads/master\n+\tmark :1\n+\tauthor 2d3%*s&f#k|\n+\tcommitter nobody <nobody@localhost> 1170199019 +0100\n+\tdata 0\n+\tM 100644 inline foo\n+\tdata 0\n+\n+\tEOF\n+\ttry_load input must_fail\n+'\n+\n+test_expect_success 'malformed fast-import stream: author 2' '\n+\treinit_svn &&\n+\tcat >input <<-\\EOF &&\n+\treset refs/heads/master\n+\tcommit refs/heads/master\n+\tmark :1\n+\tauthor nobody <localhost> 1170199019 +0100\n+\tcommitter nobody <nobody@localhost> 1170199019 +0100\n+\tdata 0\n+\tM 100644 inline foo\n+\tdata 0\n+\n+\tEOF\n+\ttry_load input must_fail\n+'\n+\n+test_expect_success 'malformed fast-import stream: data length' '\n+\treinit_svn &&\n+\tcat >input <<-\\EOF &&\n+\treset refs/heads/master\n+\tcommit refs/heads/master\n+\tmark :1\n+\tauthor nobody <nobody@localhost> 1170199019 +0100\n+\tcommitter nobody <nobody@localhost> 1170199019 +0100\n+\tdata 0\n+\tM 100644 inline foo\n+\tdata 14238\n+\n+\tEOF\n+\ttest_must_fail try_load input\n+'\n+\n+test_expect_success 'recursive directory creation' '\n+\treinit_svn &&\n+\tcat >expect.tree <<-\\EOF &&\n+\t/\n+\t alpha/\n+\t  beta/\n+\t   gamma\n+\tEOF\n+\tcat >input <<-\\EOF &&\n+\treset refs/heads/master\n+\tcommit refs/heads/master\n+\tmark :1\n+\tauthor nobody <nobody@localhost> 1170199019 +0100\n+\tcommitter nobody <nobody@localhost> 1170199019 +0100\n+\tdata 7\n+\tnothing\n+\tM 100644 inline alpha/beta/gamma\n+\tdata 12\n+\tsome content\n+\n+\tEOF\n+\ttry_load input &&\n+\tsvn_look tree >actual.tree &&\n+\ttest_cmp expect.tree actual.tree\n+'\n+\n+test_expect_success 'svn:special and svn:executable' '\n+\treinit_svn &&\n+\tcat >input <<-\\EOF &&\n+\treset refs/heads/master\n+\tcommit refs/heads/master\n+\tmark :1\n+\tauthor nobody <nobody@localhost> 1170199019 +0100\n+\tcommitter nobody <nobody@localhost> 1170199019 +0100\n+\tdata 7\n+\tnothing\n+\tM 100755 inline foo\n+\tdata 0\n+\tM 755 inline moo\n+\tdata 0\n+\tM 120000 inline bar\n+\tdata 0\n+\n+\tEOF\n+\ttry_load input &&\n+\tsvn_look propget svn:executable foo &&\n+\tsvn_look propget svn:executable moo &&\n+\tsvn_look propget svn:special bar\n+'\n+\n+test_expect_success 'replace symlink with normal file' '\n+\treinit_svn &&\n+\tcat >expect.tree <<-\\EOF &&\n+\t/\n+\t alpha/\n+\t  beta/\n+\t   gamma\n+\tEOF\n+\tcat >input <<-\\EOF &&\n+\treset refs/heads/master\n+\tcommit refs/heads/master\n+\tmark :1\n+\tauthor nobody <nobody@localhost> 1170199019 +0100\n+\tcommitter nobody <nobody@localhost> 1170199019 +0100\n+\tdata 7\n+\tnothing\n+\tM 120000 inline alpha/beta/gamma\n+\tdata 0\n+\tcommit refs/heads/master\n+\tmark :1\n+\tauthor nobody <nobody@localhost> 1170199019 +0100\n+\tcommitter nobody <nobody@localhost> 1170199019 +0100\n+\tdata 7\n+\tnothing\n+\tM 100644 inline alpha/beta/gamma\n+\tdata 0\n+\n+\tEOF\n+\ttry_load input &&\n+\tsvn_look tree -r1 >actual.tree1 &&\n+\tsvn_look tree -r2 >actual.tree2 &&\n+\ttest_cmp expect.tree actual.tree1 &&\n+\ttest_cmp expect.tree actual.tree2\n+'\n+\n+test_expect_success 'path includes symlink' '\n+\treinit_svn &&\n+\tcat >input <<-\\EOF &&\n+\treset refs/heads/master\n+\tcommit refs/heads/master\n+\tmark :1\n+\tauthor nobody <nobody@localhost> 1170199019 +0100\n+\tcommitter nobody <nobody@localhost> 1170199019 +0100\n+\tdata 7\n+\tnothing\n+\tM 120000 inline alpha/beta/gamma\n+\tdata 0\n+\tcommit refs/heads/master\n+\tmark :1\n+\tauthor nobody <nobody@localhost> 1170199019 +0100\n+\tcommitter nobody <nobody@localhost> 1170199019 +0100\n+\tdata 7\n+\tnothing\n+\tM 100644 inline alpha/beta/gamma/bar\n+\tdata 0\n+\n+\tEOF\n+\ttest_must_fail try_load input\n+'\n+\n+test_done\ndiff --git a/test-svn-fi.c b/test-svn-fi.c\nnew file mode 100644\nindex 0000000..b0605fe\n--- /dev/null\n+++ b/test-svn-fi.c\n@@ -0,0 +1,20 @@\n+/*\n+ * test-svn-fe: Code to exercise the svn import lib\n+ */\n+\n+#include \"git-compat-util.h\"\n+#include \"vcs-svn/svnload.h\"\n+\n+int main(int argc, char *argv[])\n+{\n+\tstatic const char test_svnfe_usage[] =\n+\t\t\"test-svn-fe (<dumpfile>\";\n+\tif (argc == 2) {\n+\t\tif (svnload_init(argv[1]))\n+\t\t\treturn 1;\n+\t\tsvnload_read();\n+\t\tsvnload_deinit();\n+\t\treturn 0;\n+\t}\n+\tusage(test_svnfe_usage);\n+}\n-- \n1.7.4.rc1.7.g2cf08.dirty\n"},{"id":"160169","messageId":"1296570403-9082-4-git-send-email-artagnon@gmail.com","threadId":"26379","inReplyTo":"1296570403-9082-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 3/3] vcs-svn: Refactor dump_export code into dispatch table","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-02-01T14:26:43Z","receivedAt":"2011-02-01T14:26:43Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Suggested-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n vcs-svn/dump_export.c |   89 +++++++++++++++++++++++++------------------------\n vcs-svn/dump_export.h |    6 ++-\n 2 files changed, 49 insertions(+), 46 deletions(-)\n\ndiff --git a/vcs-svn/dump_export.c b/vcs-svn/dump_export.c\nindex 2b23f77..a8331fd 100644\n--- a/vcs-svn/dump_export.c\n+++ b/vcs-svn/dump_export.c\n@@ -11,6 +11,48 @@\n \n static struct strbuf props;\n \n+typedef void state_fn(void);\n+\n+static void Kfile   (void) { printf(\"Node-kind: file\\n\"); }\n+static void Kdir    (void) { printf(\"Node-kind: dir\\n\"); }\n+static void Achange (void) { printf(\"Node-action: change\\n\"); }\n+static void Aadd    (void) { printf(\"Node-action: add\\n\"); }\n+static void Adelete (void) { printf(\"Node-action: delete\\n\"); }\n+static void Areplace(void) { printf(\"Node-action: replace\\n\"); }\n+static void Pexec   (void) { strbuf_addf(&props, \"K 14\\nsvn:executable\\nV 1\\n*\\n\"); }\n+static void Psym    (void) { strbuf_addf(&props, \"K 11\\nsvn:special\\nV 1\\n*\\n\"); }\n+static void Pend    (void) { strbuf_add(&props, \"PROPS-END\\n\", 10); }\n+\n+static void Nchange (void) { Kfile(); Achange(); }\n+static void Nadd    (void) { Kfile(); Aadd(); }\n+static void Nreplace(void) { Kfile(); Areplace(); }\n+static void Echange (void) { Pexec(); Pend(); Kfile(); Achange(); }\n+static void Eadd    (void) { Pexec(); Pend(); Kfile(); Aadd(); }\n+static void Ereplace(void) { Pexec(); Pend(); Kfile(); Areplace(); }\n+static void Schange (void) { Psym(); Pend(); Kfile(); Achange(); }\n+static void Sadd    (void) { Psym(); Pend(); Kfile(); Aadd(); }\n+static void Sreplace(void) { Psym(); Pend(); Kfile(); Areplace(); }\n+static void Dchange (void) { Kdir(); Achange(); }\n+static void Dadd    (void) { Kdir(); Aadd(); }\n+static void Dreplace(void) { Kdir(); Areplace(); }\n+\n+static state_fn *const dispatch_table[NODE_KIND_COUNT][NODE_ACTION_COUNT] = {\n+\t/* NODE_KIND_UNKNOWN */\n+\t{abort, abort, abort, Adelete, abort},\n+\t/* NODE_KIND_NORMAL */\n+\t{abort, Nchange, Nadd, Adelete, Nreplace},\n+\t/* NODE_KIND_EXECUTABLE */\n+\t{abort, Echange, Eadd, Adelete, Ereplace},\n+\t/* NODE_KIND_SYMLINK */\n+\t{abort, Schange, Sadd, Adelete, Sreplace},\n+\t/* NODE_KIND_GITLINK */\n+\t{abort, abort, abort, abort, abort},\n+\t/* NODE_KIND_DIR */\n+\t{abort, Dchange, Dadd, Adelete, Dreplace},\n+\t/* NODE_KIND_SUBDIR */\n+\t{abort, abort, abort, abort, abort}\n+};\n+\n void dump_export_begin_rev(int revision, const char *revprops,\n \t\t\tint prop_len)\n {\n@@ -24,56 +66,15 @@ void dump_export_node(const char *path, enum node_kind kind,\n \t\tenum node_action action, unsigned long text_len,\n \t\tunsigned long copyfrom_rev, const char *copyfrom_path)\n {\n-\tint dump_props = 1; /* Boolean */\n \tstrbuf_reset(&props);\n \tprintf(\"Node-path: %s\\n\", path);\n-\tswitch (kind) {\n-\tcase NODE_KIND_NORMAL:\n-\t\tprintf(\"Node-kind: file\\n\");\n-\t\tbreak;\n-\tcase NODE_KIND_EXECUTABLE:\n-\t\tprintf(\"Node-kind: file\\n\");\n-\t\tstrbuf_addf(&props, \"K 14\\nsvn:executable\\nV 1\\n*\\n\");\n-\t\tbreak;\n-\tcase NODE_KIND_SYMLINK:\n-\t\tprintf(\"Node-kind: file\\n\");\n-\t\tstrbuf_addf(&props, \"K 11\\nsvn:special\\nV 1\\n*\\n\");\n-\t\tbreak;\n-\tcase NODE_KIND_GITLINK:\n-\t\tprintf(\"Node-kind: file\\n\");\n-\t\tbreak;\n-\tcase NODE_KIND_DIR:\n-\t\tprintf(\"Node-kind: dir\\n\");\n-\t\tbreak;\n-\tcase NODE_KIND_SUBDIR:\n-\t\tdie(\"Unsupported: subdirectory\");\n-\tdefault:\n-\t\tbreak;\n-\t}\n-\tstrbuf_add(&props, \"PROPS-END\\n\", 10);\n+\tdispatch_table[kind][action]();\n \n-\tswitch (action) {\n-\tcase NODE_ACTION_CHANGE:\n-\t\tprintf(\"Node-action: change\\n\");\n-\t\tbreak;\n-\tcase NODE_ACTION_ADD:\n-\t\tprintf(\"Node-action: add\\n\");\n-\t\tbreak;\n-\tcase NODE_ACTION_REPLACE:\n-\t\tprintf(\"Node-action: replace\\n\");\n-\t\tbreak;\n-\tcase NODE_ACTION_DELETE:\n-\t\tprintf(\"Node-action: delete\\n\");\n-\t\tdump_props = 0;\n-\t\tbreak;\n-\tdefault:\n-\t\tbreak;\n-\t}\n \tif (copyfrom_rev != SVN_INVALID_REV) {\n \t\tprintf(\"Node-copyfrom-rev: %lu\\n\", copyfrom_rev);\n \t\tprintf(\"Node-copyfrom-path: %s\\n\", copyfrom_path);\n \t}\n-\tif (dump_props) {\n+\tif (props.len) {\n \t\tprintf(\"Prop-delta: false\\n\");\n \t\tprintf(\"Prop-content-length: %lu\\n\", props.len);\n \t}\n@@ -81,7 +82,7 @@ void dump_export_node(const char *path, enum node_kind kind,\n \t\tprintf(\"Text-delta: false\\n\");\t\t\n \t\tprintf(\"Text-content-length: %lu\\n\", text_len);\n \t}\n-\tif (text_len || dump_props) {\n+\tif (text_len || props.len) {\n \t\tprintf(\"Content-length: %lu\\n\\n\", text_len + props.len);\n \t\tprintf(\"%s\", props.buf);\n \t}\ndiff --git a/vcs-svn/dump_export.h b/vcs-svn/dump_export.h\nindex e9f51a3..8265170 100644\n--- a/vcs-svn/dump_export.h\n+++ b/vcs-svn/dump_export.h\n@@ -9,7 +9,8 @@ enum node_action {\n \tNODE_ACTION_CHANGE,\n \tNODE_ACTION_ADD,\n \tNODE_ACTION_DELETE,\n-\tNODE_ACTION_REPLACE\n+\tNODE_ACTION_REPLACE,\n+\tNODE_ACTION_COUNT\n };\n \n enum node_kind {\n@@ -19,7 +20,8 @@ enum node_kind {\n \tNODE_KIND_SYMLINK,\n \tNODE_KIND_GITLINK,\n \tNODE_KIND_DIR,           /* SVN-specific */\n-\tNODE_KIND_SUBDIR\n+\tNODE_KIND_SUBDIR,\n+\tNODE_KIND_COUNT\n };\n \n void dump_export_begin_rev(int revision, const char *revprops, int prop_len);\n-- \n1.7.4.rc1.7.g2cf08.dirty\n"},{"id":"160173","messageId":"AANLkTikUUojgj_H0r0L=XBi7H714xUvAS=vPsgMRNNjr@mail.gmail.com","threadId":"26379","inReplyTo":"1296570403-9082-2-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 1/3] vcs-svn: Introduce svnload, a dumpfile producer","fromName":"Erik Faye-Lund","fromEmail":"kusmabite@gmail.com","sentAt":"2011-02-01T14:46:25Z","receivedAt":"2011-02-01T14:46:25Z","isPatch":true,"sender":{"key":"kusmabite@gmail.com","avatar":"https://avatars.githubusercontent.com/u/47073?v=4"},"body":"A very superficial review, because I don't have much time, and don't\nknow the surrounding code well. Sorry about that.\n\nOn Tue, Feb 1, 2011 at 3:26 PM, Ramkumar Ramachandra <artagnon@gmail.com> wrote:\n> diff --git a/vcs-svn/dir_cache.c b/vcs-svn/dir_cache.c\n> new file mode 100644\n> index 0000000..9a608ce\n> --- /dev/null\n> +++ b/vcs-svn/dir_cache.c\n> @@ -0,0 +1,40 @@\n> +/*\n> + * Licensed under a two-clause BSD-style license.\n> + * See LICENSE for details.\n> + */\n> +\n> +#include \"git-compat-util.h\"\n> +#include \"string-list.h\"\n> +#include \"line_buffer.h\"\n> +#include \"dump_export.h\"\n> +\n> +static struct string_list dirents = STRING_LIST_INIT_DUP;\n> +static struct string_list_item *dir = NULL;\n> +\n> +void dir_cache_add(const char *path, enum node_kind kind) {\n\nStyle: we put the opening bracket of functions on the next line.\n\n> +       dir = string_list_insert(&dirents, path);\n> +       dir->util = malloc(sizeof(enum node_kind));\n> +       *((enum node_kind *)(dir->util)) = kind;\n\nUnchecked malloc; perhaps you should use xmalloc instead?\n\n> +}\n> +\n> +void dir_cache_remove(const char *path) {\n\nSame style-violation as above.\n\n> +       dir = string_list_lookup(&dirents, path);\n> +       if (dir)\n> +               *((enum node_kind *)(dir->util)) = NODE_KIND_UNKNOWN;\n> +}\n> +\n> +enum node_kind dir_cache_lookup(const char *path) {\n> +       dir = string_list_lookup(&dirents, path);\n> +       if (dir)\n> +               return *((enum node_kind *)(dir->util));\n> +       else\n> +               return NODE_KIND_UNKNOWN;\n> +}\n> +\n> +void dir_cache_init() {\n> +       return;\n> +}\n> +\n> +void dir_cache_deinit() {\n> +       string_list_clear(&dirents, 1);\n> +}\n\nThree more.\n\n> +static void populate_revprops(struct strbuf *props, size_t author_len,\n> +                       const char *author, size_t log_len, const char *log,\n> +                       size_t date_len, const char *date)\n> +{\n> +       strbuf_reset(props);\n> +       strbuf_addf(props, \"K 10\\nsvn:author\\nV %lu\\n%s\\n\", author_len, author);\n> +       strbuf_addf(props, \"K 7\\nsvn:log\\nV %lu\\n%s\\n\", log_len, log);\n> +       if (date_len)\n> +               /* SVN doesn't like an empty svn:date value */\n> +               strbuf_addf(props, \"K 8\\nsvn:date\\nV %lu\\n%s\\n\", date_len, date);\n\nPerhaps a scope around here will make this a bit easier to read. At\nfirst glance it looked like a missing scope to me, due to the indented\ncomment...\n\n> +       if (!val) die(\"Malformed author line\");\n> +       if (!(tz_off = strrchr(val, ' '))) goto error;\n> +       *tz_off++ = '\\0';\n> +       if (!(t = strrchr(val, ' '))) goto error;\n\nstyle: use\n\"if (x)\n\tdo_stuff();\"\ninstead of\n\"if (x) do_stuff();\"\n\n\n> +       *(t - 1) = '\\0'; /* Ignore '>' from email */\n> +       t++;\n> +       tz_off_buf = atoi(tz_off);\n> +       if (tz_off_buf > 1200 || tz_off_buf  < -1200) goto error;\n\nsame\n\n> +       tm_time = time_to_tm(strtoul(t, NULL, 10), tz_off_buf);\n> +       strftime(time_buf, SVN_DATE_LEN + 1, SVN_DATE_FORMAT, tm_time);\n> +       strbuf_add(date, time_buf, SVN_DATE_LEN);\n> +       if (!(t = strchr(val, '<'))) goto error;\n\nsame\n\n> +int parse_filemodify_mode(char *val)\n> +{\n> +       char *t;\n> +\n> +       if (!(t = strchr(val, ' '))) goto error;\n\nsame\n\n> +void svnload_read(void)\n> +{\n> +       char *t, *val;\n> +       int len;\n> +\n> +       while ((t = buffer_read_line(&input))) {\n> +               if ((val = strchr(t, ' ')))\n> +                       *val++ = '\\0';\n> +               len = (val ? val - t - 1 : strlen(t));\n> +\n> +               switch (len) {\n> +               case 1:\n> +                       if (!memcmp(t, \"D\", 1)) {\n> +                               node_ctx.action = NODE_ACTION_DELETE;\n> +                       } else if (!memcmp(t, \"C\", 1)) {\n> +                               node_ctx.action = NODE_ACTION_ADD;\n> +                       } else if (!memcmp(t, \"R\", 1)) {\n> +                               node_ctx.action = NODE_ACTION_REPLACE;\n> +                       } else if (!memcmp(t, \"M\", 1)) {\n> +                               if (!val) goto error;\n> +                               node_ctx.action = NODE_ACTION_CHANGE;\n> +                               val += parse_filemodify_mode(val);\n> +                               if (!(t = strchr(val, ' '))) goto error;\n\nsame\n"},{"id":"160195","messageId":"20110201174241.GB3771@burratino","threadId":"26379","inReplyTo":"1296570403-9082-4-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 3/3] vcs-svn: Refactor dump_export code into dispatch table","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-02-01T17:42:41Z","receivedAt":"2011-02-01T17:42:41Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> +++ b/vcs-svn/dump_export.c\n> @@ -11,6 +11,48 @@\n[...]\n> +static state_fn *const dispatch_table[NODE_KIND_COUNT][NODE_ACTION_COUNT] = {\n> +\t/* NODE_KIND_UNKNOWN */\n> +\t{abort, abort, abort, Adelete, abort},\n> +\t/* NODE_KIND_NORMAL */\n> +\t{abort, Nchange, Nadd, Adelete, Nreplace},\n> +\t/* NODE_KIND_EXECUTABLE */\n> +\t{abort, Echange, Eadd, Adelete, Ereplace},\n> +\t/* NODE_KIND_SYMLINK */\n> +\t{abort, Schange, Sadd, Adelete, Sreplace},\n> +\t/* NODE_KIND_GITLINK */\n> +\t{abort, abort, abort, abort, abort},\n> +\t/* NODE_KIND_DIR */\n> +\t{abort, Dchange, Dadd, Adelete, Dreplace},\n> +\t/* NODE_KIND_SUBDIR */\n> +\t{abort, abort, abort, abort, abort}\n> +};\n\nHeh.  I think that Junio was suggesting making the _parser_\ntable-driven, meaning something like\n\n\t... node_kinds[] = {\n\t\t{ \"100644\", sizeof(\"100644\"), \"file\" },\n\t\t{ \"100755\", sizeof(\"100755\"), \"file\", \"svn:executable\" },\n\t\t{ \"120000\", sizeof(\"120000\"), \"file\", \"svn:special\" },\n\t\t{ \"160000\", sizeof(\"160000\"), \"file\" },\t/* NEEDSWORK: seems wrong\" */\n\t\t{ \"040000\", sizeof(\"040000\"), \"dir\" }\n\t};\n\n(Side note: remember that 644 and 755 are permitted synonyms for\n100644 and 100755!)\n\nI personally think that simple state machines tend to be easier to\nfollow if the current state is represented by the instruction pointer\nrather than a variable, as in the current fast-import.c.  But maybe\nthat's a matter of taste?\n\nAnyway, my other complaints about this dispatch_table are that the\nfunction names leave me rubbing my head and I can't keep the list\nof states you're transitioning between straight in my head.  I guess\n\"Adelete\" is an abbreviation for print_delete_node_action?  Is a\ncallback needed at all (rather than just a string) for such actions?\n\nHope that helps,\nJonathan\n"},{"id":"160207","messageId":"20110201185817.GG3771@burratino","threadId":"26379","inReplyTo":"1296570403-9082-3-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 2/3] t9010-svn-fi: Add tests for svn-fi","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-02-01T18:58:17Z","receivedAt":"2011-02-01T18:58:17Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> Create a test-svn-fi in toplevel directory, add rules to build it, and\n> add some basic tests.\n\nThanks.  Probably this should be squashed with patch #1?\n\n> --- /dev/null\n> +++ b/t/t9010-svn-fi.sh\n\nTest number's already taken.  9012 is free, though.\n\n> @@ -0,0 +1,303 @@\n> +#!/bin/sh\n> +\n> +test_description='check svn dumpfile exporter'\n> +\n> +. ./test-lib.sh\n> +\n> +if ! svnadmin -h >/dev/null 2>&1\n> +then\n> +\tskip_all='skipping svn-fi tests, svn not available'\n> +\ttest_done\n> +fi\n\nIs it impossible to test without svn present or otherwise speed\nthese up?  Some random ideas:\n\n - feed svn-fi output into svn-fe and compare git repos\n - check if \"svn-fi after svn-fe\" is idempotent (should it be?)\n - check for individual salient features from svn-fi output, using\n   \"grep\", \"sed\", or \"awk\" to ignore the rest\n - use svnrdump in place of \"svnadmin load\" if available (is it\n   faster?)\n - find the bottleneck in svnadmin and fix it\n\nOf course at least one test of the \"svn-fe | svnadmin load\"\npipeline seems worthwhile; I'm just thinking we should avoid\nhabits that slow down the test suite too much.\n\n> +svn_look () {\n> +\tsubcommand=$1 &&\n> +\tshift &&\n> +\tsvnlook \"$subcommand\" \"$svnrepo\" \"$@\"\n> +}\n\nNeeded?  The svn_cmd function exists to point to a configuration\ndirectory.\n\n> +test_expect_success 'normal empty files' '\n> +\treinit_svn &&\n> +\tcat >expect.tree <<-\\EOF &&\n> +\t/\n> +\t foo\n> +\t bar\n> +\tEOF\n> +\tcat >input <<-\\EOF &&\n> +\treset refs/heads/master\n> +\tcommit refs/heads/master\n> +\tmark :1\n> +\tauthor nobody <nobody@localhost> 1170199019 +0100\n> +\tcommitter nobody <nobody@localhost> 1170199019 +0100\n\nWhere do the dates come from?  Why should I (the reader) expect\nthem?\n\n> +# TODO: How to test date? Need to convert from local timestamp\n\nAh, you saw. :)\n\nOne possibility is to fuzz away whatever is unimportant when\ncomparing results (and using tools like \"awk\" to extract the\ndate to perform whatever tests on it are appropriate where it\nmatters).\n\n> +test_expect_success 'malformed fast-import stream: author 2' '\n> +\treinit_svn &&\n> +\tcat >input <<-\\EOF &&\n> +\treset refs/heads/master\n> +\tcommit refs/heads/master\n> +\tmark :1\n> +\tauthor nobody <localhost> 1170199019 +0100\n> +\tcommitter nobody <nobody@localhost> 1170199019 +0100\n> +\tdata 0\n> +\tM 100644 inline foo\n> +\tdata 0\n> +\n> +\tEOF\n> +\ttry_load input must_fail\n\nWhat's wrong with this stream?\n\n> +test_expect_success 'svn:special and svn:executable' '\n> +\treinit_svn &&\n> +\tcat >input <<-\\EOF &&\n> +\treset refs/heads/master\n> +\tcommit refs/heads/master\n> +\tmark :1\n> +\tauthor nobody <nobody@localhost> 1170199019 +0100\n> +\tcommitter nobody <nobody@localhost> 1170199019 +0100\n> +\tdata 7\n> +\tnothing\n> +\tM 100755 inline foo\n> +\tdata 0\n> +\tM 755 inline moo\n> +\tdata 0\n> +\tM 120000 inline bar\n> +\tdata 0\n> +\n> +\tEOF\n> +\ttry_load input &&\n> +\tsvn_look propget svn:executable foo &&\n> +\tsvn_look propget svn:executable moo &&\n> +\tsvn_look propget svn:special bar\n\nMaybe worth checking for the absence of svn:executable +\nsvn:special from an ordinary file, too?\n\n> +test_expect_success 'replace symlink with normal file' '\n\nNice.  \"replace symlink with executable\" and \"replace symlink\nwith directory\" might also be interesting.\n\nRegards,\nJonathan\n"},{"id":"160223","messageId":"7vd3nbzbob.fsf@alter.siamese.dyndns.org","threadId":"26379","inReplyTo":"20110201174241.GB3771@burratino","subject":"Re: [PATCH 3/3] vcs-svn: Refactor dump_export code into dispatch table","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-02-01T21:29:08Z","receivedAt":"2011-02-01T21:29:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan Nieder <jrnieder@gmail.com> writes:\n\n> Heh.  I think that Junio was suggesting making the _parser_\n> table-driven, meaning something like\n>\n> \t... node_kinds[] = {\n> \t\t{ \"100644\", sizeof(\"100644\"), \"file\" },\n> ...\n> \t};\n\nYes, and thanks.\n"},{"id":"160249","messageId":"20110202024944.GA11339@kytes","threadId":"26379","inReplyTo":"20110201185817.GG3771@burratino","subject":"Re: [PATCH 2/3] t9010-svn-fi: Add tests for svn-fi","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-02-02T02:49:49Z","receivedAt":"2011-02-02T02:49:49Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Jonathan,\n\nJonathan Nieder writes:\n> Ramkumar Ramachandra wrote:\n> \n> > Create a test-svn-fi in toplevel directory, add rules to build it, and\n> > add some basic tests.\n> \n> Thanks.  Probably this should be squashed with patch #1?\n\nRight. Will do.\n\n> > --- /dev/null\n> > +++ b/t/t9010-svn-fi.sh\n> \n> Test number's already taken.  9012 is free, though.\n\nOk. Will rename in the next iteration.\n\n> > @@ -0,0 +1,303 @@\n> > +#!/bin/sh\n> > +\n> > +test_description='check svn dumpfile exporter'\n> > +\n> > +. ./test-lib.sh\n> > +\n> > +if ! svnadmin -h >/dev/null 2>&1\n> > +then\n> > +\tskip_all='skipping svn-fi tests, svn not available'\n> > +\ttest_done\n> > +fi\n> \n> Is it impossible to test without svn present or otherwise speed\n> these up?  Some random ideas:\n> \n>  - feed svn-fi output into svn-fe and compare git repos\n>  - check if \"svn-fi after svn-fe\" is idempotent (should it be?)\n>  - check for individual salient features from svn-fi output, using\n>    \"grep\", \"sed\", or \"awk\" to ignore the rest\n>  - use svnrdump in place of \"svnadmin load\" if available (is it\n>    faster?)\n>  - find the bottleneck in svnadmin and fix it\n> \n> Of course at least one test of the \"svn-fe | svnadmin load\"\n> pipeline seems worthwhile; I'm just thinking we should avoid\n> habits that slow down the test suite too much.\n\nHm, I'm not entirely convinced -- the final verdict is always dictated\nby whether or not the emitted dumpstream loads. Many streams that look\nalright to the eye don't actually load because of small intricacies\nlike missing newlines. It'll be awfully complicated reverse-engineer\nall these intricate rules and write them in terms of sed/ awk\ncommands. Yes, svnrdump will be faster, but it'll only be available\nwith the later versions of Subversion (same problem with fixing\nsvnadmin bottlenecks).\n\n> > +svn_look () {\n> > +\tsubcommand=$1 &&\n> > +\tshift &&\n> > +\tsvnlook \"$subcommand\" \"$svnrepo\" \"$@\"\n> > +}\n> \n> Needed?  The svn_cmd function exists to point to a configuration\n> directory.\n\nJust a convinience: I am using svn_look to look at the properties of\nnodes in several tests. If I don't use this in enough tests, I'll\nfactor it out.\n\n> > +test_expect_success 'normal empty files' '\n> > +\treinit_svn &&\n> > +\tcat >expect.tree <<-\\EOF &&\n> > +\t/\n> > +\t foo\n> > +\t bar\n> > +\tEOF\n> > +\tcat >input <<-\\EOF &&\n> > +\treset refs/heads/master\n> > +\tcommit refs/heads/master\n> > +\tmark :1\n> > +\tauthor nobody <nobody@localhost> 1170199019 +0100\n> > +\tcommitter nobody <nobody@localhost> 1170199019 +0100\n> \n> Where do the dates come from?  Why should I (the reader) expect\n> them?\n\nWhat do you suggest? I need some valid \"generic\" timestamp + offset.\n\n> > +# TODO: How to test date? Need to convert from local timestamp\n> \n> Ah, you saw. :)\n> \n> One possibility is to fuzz away whatever is unimportant when\n> comparing results (and using tools like \"awk\" to extract the\n> date to perform whatever tests on it are appropriate where it\n> matters).\n\nOkay, I'll try this. Is there no other way? Can we whip up a shell\nscript to convert the timestamp?\n\n> > +test_expect_success 'malformed fast-import stream: author 2' '\n> > +\treinit_svn &&\n> > +\tcat >input <<-\\EOF &&\n> > +\treset refs/heads/master\n> > +\tcommit refs/heads/master\n> > +\tmark :1\n> > +\tauthor nobody <localhost> 1170199019 +0100\n> > +\tcommitter nobody <nobody@localhost> 1170199019 +0100\n> > +\tdata 0\n> > +\tM 100644 inline foo\n> > +\tdata 0\n> > +\n> > +\tEOF\n> > +\ttry_load input must_fail\n> \n> What's wrong with this stream?\n\nauthor nobody <localhost>\n                        ^\nParse error there, since build_svn_author tries to use the part of the\nemail address appearing before the '@'. This is just a temporary test\n-- we should make the svn_author generation logic configurable.\n\n> > +test_expect_success 'svn:special and svn:executable' '\n> > +\treinit_svn &&\n> > +\tcat >input <<-\\EOF &&\n> > +\treset refs/heads/master\n> > +\tcommit refs/heads/master\n> > +\tmark :1\n> > +\tauthor nobody <nobody@localhost> 1170199019 +0100\n> > +\tcommitter nobody <nobody@localhost> 1170199019 +0100\n> > +\tdata 7\n> > +\tnothing\n> > +\tM 100755 inline foo\n> > +\tdata 0\n> > +\tM 755 inline moo\n> > +\tdata 0\n> > +\tM 120000 inline bar\n> > +\tdata 0\n> > +\n> > +\tEOF\n> > +\ttry_load input &&\n> > +\tsvn_look propget svn:executable foo &&\n> > +\tsvn_look propget svn:executable moo &&\n> > +\tsvn_look propget svn:special bar\n> \n> Maybe worth checking for the absence of svn:executable +\n> svn:special from an ordinary file, too?\n\nGood idea.\n\n> > +test_expect_success 'replace symlink with normal file' '\n> \n> Nice.  \"replace symlink with executable\" and \"replace symlink\n> with directory\" might also be interesting.\n\nGood idea. Will do.\n\n-- Ram\n"},{"id":"160250","messageId":"20110202025320.GB11339@kytes","threadId":"26379","inReplyTo":"AANLkTikUUojgj_H0r0L=XBi7H714xUvAS=vPsgMRNNjr@mail.gmail.com","subject":"Re: [PATCH 1/3] vcs-svn: Introduce svnload, a dumpfile producer","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-02-02T02:53:24Z","receivedAt":"2011-02-02T02:53:24Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Erik,\n\nErik Faye-Lund writes:\n> A very superficial review, because I don't have much time, and don't\n> know the surrounding code well. Sorry about that.\n\nThanks. I have to often switch back and fourth between GNU-style (for\nSubversion) and Linux-style (for Git), so please bear with my silly\nstyle errors.\n\n> On Tue, Feb 1, 2011 at 3:26 PM, Ramkumar Ramachandra <artagnon@gmail.com> wrote:\n> > +       if (!val) die(\"Malformed author line\");\n> > +       if (!(tz_off = strrchr(val, ' '))) goto error;\n> > +       *tz_off++ = '\\0';\n> > +       if (!(t = strrchr(val, ' '))) goto error;\n> \n> style: use\n> \"if (x)\n> \tdo_stuff();\"\n> instead of\n> \"if (x) do_stuff();\"\n\nThis was intentional -- I'm only using it when do_stuff() is 'goto\nerror'. I thought it looked nicer that way.\n\n-- Ram\n"},{"id":"160251","messageId":"20110202025626.GC11339@kytes","threadId":"26379","inReplyTo":"20110201174241.GB3771@burratino","subject":"Re: [PATCH 3/3] vcs-svn: Refactor dump_export code into dispatch table","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-02-02T02:56:31Z","receivedAt":"2011-02-02T02:56:31Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Jonathan,\n\nJonathan Nieder writes:\n> Ramkumar Ramachandra wrote:\n> \n> > +++ b/vcs-svn/dump_export.c\n> > @@ -11,6 +11,48 @@\n> [...]\n> > +static state_fn *const dispatch_table[NODE_KIND_COUNT][NODE_ACTION_COUNT] = {\n> > +\t/* NODE_KIND_UNKNOWN */\n> > +\t{abort, abort, abort, Adelete, abort},\n> > +\t/* NODE_KIND_NORMAL */\n> > +\t{abort, Nchange, Nadd, Adelete, Nreplace},\n> > +\t/* NODE_KIND_EXECUTABLE */\n> > +\t{abort, Echange, Eadd, Adelete, Ereplace},\n> > +\t/* NODE_KIND_SYMLINK */\n> > +\t{abort, Schange, Sadd, Adelete, Sreplace},\n> > +\t/* NODE_KIND_GITLINK */\n> > +\t{abort, abort, abort, abort, abort},\n> > +\t/* NODE_KIND_DIR */\n> > +\t{abort, Dchange, Dadd, Adelete, Dreplace},\n> > +\t/* NODE_KIND_SUBDIR */\n> > +\t{abort, abort, abort, abort, abort}\n> > +};\n> \n> Heh.  I think that Junio was suggesting making the _parser_\n> table-driven, meaning something like\n\nOops :p I'll fix this in the next round.\n\n> \t... node_kinds[] = {\n> \t\t{ \"100644\", sizeof(\"100644\"), \"file\" },\n> \t\t{ \"100755\", sizeof(\"100755\"), \"file\", \"svn:executable\" },\n> \t\t{ \"120000\", sizeof(\"120000\"), \"file\", \"svn:special\" },\n> \t\t{ \"160000\", sizeof(\"160000\"), \"file\" },\t/* NEEDSWORK: seems wrong\" */\n> \t\t{ \"040000\", sizeof(\"040000\"), \"dir\" }\n> \t};\n> \n> (Side note: remember that 644 and 755 are permitted synonyms for\n> 100644 and 100755!)\n> \n> I personally think that simple state machines tend to be easier to\n> follow if the current state is represented by the instruction pointer\n> rather than a variable, as in the current fast-import.c.  But maybe\n> that's a matter of taste?\n> \n> Anyway, my other complaints about this dispatch_table are that the\n> function names leave me rubbing my head and I can't keep the list\n> of states you're transitioning between straight in my head.  I guess\n> \"Adelete\" is an abbreviation for print_delete_node_action?  Is a\n> callback needed at all (rather than just a string) for such actions?\n\nTrue -- there are many intermediate layers where all kinds of\nvariables are getting set. It'll probably be a good idea to remove all\nthese abstraction layers and map fast-import commands directly to\ndumpstream strings. I'll do this in the next iteration.\n\n-- Ram\n"},{"id":"160252","messageId":"20110202031812.GB15285@elie","threadId":"26379","inReplyTo":"20110202024944.GA11339@kytes","subject":"Re: [PATCH 2/3] t9010-svn-fi: Add tests for svn-fi","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-02-02T03:18:12Z","receivedAt":"2011-02-02T03:18:12Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n> Jonathan Nieder writes:\n>> Ramkumar Ramachandra wrote:\n\n>> Is it impossible to test without svn present or otherwise speed\n>> these up?  Some random ideas:\n[...]\n> Hm, I'm not entirely convinced -- the final verdict is always dictated\n> by whether or not the emitted dumpstream loads. Many streams that look\n> alright to the eye don't actually load because of small intricacies\n> like missing newlines. It'll be awfully complicated reverse-engineer\n> all these intricate rules and write them in terms of sed/ awk\n> commands. Yes, svnrdump will be faster, but it'll only be available\n> with the later versions of Subversion (same problem with fixing\n> svnadmin bottlenecks).\n\nIf svnrdump is fast enough, wouldn't something like\n\n if svnrdump --help >/dev/null 2>&1\n then\n\tload_dump () {\n\t\tsvnrdump load \"file://$1\"\n\t}\n\ttest_set_prereq SVN\n elif svnadmin --help >/dev/null 2>&1\n then\n\tload_dump () {\n\t\tsvnadmin load \"$1\"\n\t}\n\ttest_set_prereq SVN\n else\n\t: no usable svn installation\n fi\n\ndo it?\n\n>>> +svn_look () {\n>>> +\tsubcommand=$1 &&\n>>> +\tshift &&\n>>> +\tsvnlook \"$subcommand\" \"$svnrepo\" \"$@\"\n>>> +}\n[...]\n> Just a convinience: I am using svn_look to look at the properties of\n> nodes in several tests.\n\nAh, I missed the implicit $svnrepo argument.  Makes sense then (might\nbe good to have a comment to explain the purpose, though).\n\n>>> +test_expect_success 'normal empty files' '\n>>> +\treinit_svn &&\n>>> +\tcat >expect.tree <<-\\EOF &&\n>>> +\t/\n>>> +\t foo\n>>> +\t bar\n>>> +\tEOF\n>>> +\tcat >input <<-\\EOF &&\n>>> +\treset refs/heads/master\n>>> +\tcommit refs/heads/master\n>>> +\tmark :1\n>>> +\tauthor nobody <nobody@localhost> 1170199019 +0100\n>>> +\tcommitter nobody <nobody@localhost> 1170199019 +0100\n>>\n>> Where do the dates come from?  Why should I (the reader) expect\n>> them?\n>\n> What do you suggest? I need some valid \"generic\" timestamp + offset.\n\n\ttest_tick\n\t...\n\tauthor nobody <nobody@localhost> $GIT_AUTHOR_DATE\n\n[...]\n> Okay, I'll try this. Is there no other way? Can we whip up a shell\n> script to convert the timestamp?\n\nMaybe the standard \"date\" utility can help with conversions?\n\nI'm not sure what kinds of validation you're doing on the dates, hence\nthe vague answers.  For examples of fuzzing out the unimportant bits,\n\n git grep fuzz t/*.sh\n\ncan help.\n\n>>> +test_expect_success 'malformed fast-import stream: author 2' '\n[...]\n>>> +\ttry_load input must_fail\n>> \n>> What's wrong with this stream?\n>\n> author nobody <localhost>\n>                         ^\n> Parse error there, since build_svn_author tries to use the part of the\n> email address appearing before the '@'. This is just a temporary test\n> -- we should make the svn_author generation logic configurable.\n\nSo the stream is not malformed?  In that case, I guess the\nintent is\n\n test_expect_failure 'does something reasonable with email address without @' '\n\t...\n\ttry_load input\n '\n\nThanks again.\nJonathan\n"},{"id":"160267","messageId":"AANLkTi=sQtaWc0C5tqjgmAqCoLupKg0fPhEd+-Y5kx=x@mail.gmail.com","threadId":"26379","inReplyTo":"20110202025320.GB11339@kytes","subject":"Re: [PATCH 1/3] vcs-svn: Introduce svnload, a dumpfile producer","fromName":"Erik Faye-Lund","fromEmail":"kusmabite@gmail.com","sentAt":"2011-02-02T12:43:06Z","receivedAt":"2011-02-02T12:43:06Z","isPatch":true,"sender":{"key":"kusmabite@gmail.com","avatar":"https://avatars.githubusercontent.com/u/47073?v=4"},"body":"On Wed, Feb 2, 2011 at 3:53 AM, Ramkumar Ramachandra <artagnon@gmail.com> wrote:\n> Hi Erik,\n>\n> Erik Faye-Lund writes:\n>> A very superficial review, because I don't have much time, and don't\n>> know the surrounding code well. Sorry about that.\n>\n> Thanks. I have to often switch back and fourth between GNU-style (for\n> Subversion) and Linux-style (for Git), so please bear with my silly\n> style errors.\n>\n\nThat's completely fair :)\n\n>> On Tue, Feb 1, 2011 at 3:26 PM, Ramkumar Ramachandra <artagnon@gmail.com> wrote:\n>> > +       if (!val) die(\"Malformed author line\");\n>> > +       if (!(tz_off = strrchr(val, ' '))) goto error;\n>> > +       *tz_off++ = '\\0';\n>> > +       if (!(t = strrchr(val, ' '))) goto error;\n>>\n>> style: use\n>> \"if (x)\n>>       do_stuff();\"\n>> instead of\n>> \"if (x) do_stuff();\"\n>\n> This was intentional -- I'm only using it when do_stuff() is 'goto\n> error'. I thought it looked nicer that way.\n>\n\nThis is still not the style we use in git:\n\n$ git grep \"if (\" | grep goto | wc -l\n      0\n$ git grep -B1 \"if (\" | grep goto | wc -l\n     33\n"}]}