{"thread":{"id":"24388","subject":"[PATCH 0/9] Get svnrdump merged into git.git","startedAt":"2010-07-13T23:36:07Z","lastAt":"2010-08-07T02:51:25Z","messageCount":26,"participants":["Ramkumar Ramachandra","Jonathan Nieder","Daniel Shahaf","Stefan Sperling","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":9},"messages":[{"id":"145488","messageId":"1279064176-6645-1-git-send-email-artagnon@gmail.com","threadId":"24388","inReplyTo":null,"subject":"[PATCH 0/9] Get svnrdump merged into git.git","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-07-13T23:36:07Z","receivedAt":"2010-07-13T23:36:07Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nFor the convinience of fellow Git developers, here's a series to get\nsvnrdump merged into git.git. Although the series is in excellent\nshape (thanks to reviewers), I'd like to make a special request to\nrelax conformance guidelines a little bit for this series. I'll be\ngetting commit access to the ASF repository soon; I will commit it\nthere and make subsequent improvements in Subversion-land. As you can\nimagine, due to whitespace and style differences, it's too painful to\nbackport the changes here. Once there's a release of Subversion with\nsvnrdump merged in, I will remove it from git.git.\n\nThe series incorporates suggestions by Jonathan, Bert, and\nDaniel. However, it does not address the recent review by Stefan, and\ndoes not attempt to; Stefan's review will be addressed in future\ncommits I make to svnrdump in the ASF repository.\n\nAgain, I'd request reviewers not to hold up this series for git.git:\nsuggestions will be incorporated as commits to the ASF. If something\ndoes not work, I'll quickly post fixup patches while the series is\nstill in `pu`.\n\nThanks for reading.\n\n-- Ram\n\nRamkumar Ramachandra (8):\n  Add LICENSE\n  Add skeleton SVN client and Makefile\n  Add debug editor from Subversion trunk\n  Drive the debug editor\n  Dump the revprops at the start of every revision\n  Implement directory-related functions\n  Implement file-related functions\n  Implement close_file\n\nWill Palmer (1):\n  Add a validation script\n\n vcs-svn/LICENSE        |   26 ++\n vcs-svn/Makefile       |    8 +\n vcs-svn/debug_editor.c |  421 +++++++++++++++++++++++++++++++\n vcs-svn/debug_editor.h |   10 +\n vcs-svn/dump_editor.c  |  652 ++++++++++++++++++++++++++++++++++++++++++++++++\n vcs-svn/dump_editor.h  |    8 +\n vcs-svn/dumpr_util.c   |  112 +++++++++\n vcs-svn/dumpr_util.h   |   92 +++++++\n vcs-svn/svnrdump.c     |  185 ++++++++++++++\n vcs-svn/validate.sh    |  226 +++++++++++++++++\n 10 files changed, 1740 insertions(+), 0 deletions(-)\n create mode 100644 vcs-svn/LICENSE\n create mode 100644 vcs-svn/Makefile\n create mode 100644 vcs-svn/debug_editor.c\n create mode 100644 vcs-svn/debug_editor.h\n create mode 100644 vcs-svn/dump_editor.c\n create mode 100644 vcs-svn/dump_editor.h\n create mode 100644 vcs-svn/dumpr_util.c\n create mode 100644 vcs-svn/dumpr_util.h\n create mode 100644 vcs-svn/svnrdump.c\n create mode 100755 vcs-svn/validate.sh\n"},{"id":"145489","messageId":"1279064176-6645-2-git-send-email-artagnon@gmail.com","threadId":"24388","inReplyTo":"1279064176-6645-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 1/9] Add LICENSE","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-07-13T23:36:08Z","receivedAt":"2010-07-13T23:36:08Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"License the project under a two-clause BSD-style license. A dual\nlicense will be required later when attempting to merge into\nSubversion.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n LICENSE |   26 ++++++++++++++++++++++++++\n 1 files changed, 26 insertions(+), 0 deletions(-)\n create mode 100644 LICENSE\n\ndiff --git a/LICENSE b/LICENSE\nnew file mode 100644\nindex 0000000..4367b7c\n--- /dev/null\n+++ b/LICENSE\n@@ -0,0 +1,26 @@\n+Copyright (C) 2010 Ramkumar Ramachandra\n+All rights reserved.\n+\n+Redistribution and use in source and binary forms, with or without\n+modification, are permitted provided that the following conditions\n+are met:\n+1. Redistributions of source code must retain the above copyright\n+   notice(s), this list of conditions and the following disclaimer\n+   unmodified other than the allowable addition of one or more\n+   copyright notices.\n+2. Redistributions in binary form must reproduce the above copyright\n+   notice(s), this list of conditions and the following disclaimer in\n+   the documentation and/or other materials provided with the\n+   distribution.\n+\n+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY\n+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n+PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE\n+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\n+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\n+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n-- \n1.7.1\n"},{"id":"145490","messageId":"1279064176-6645-3-git-send-email-artagnon@gmail.com","threadId":"24388","inReplyTo":"1279064176-6645-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 2/9] Add skeleton SVN client and Makefile","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-07-13T23:36:09Z","receivedAt":"2010-07-13T23:36:09Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Add a basic SVN command-line client along with a Makefile that does\njust enough to establish a connection with the ASF subversion server;\nit initializes a memory pool, sees that configuration files are in\norder, builds up a context object, sets up an authentication baton,\nand finally opens a session to the subversion server.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n Makefile   |    8 +++++\n svnrdump.c |  102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 110 insertions(+), 0 deletions(-)\n create mode 100644 Makefile\n create mode 100644 svnrdump.c\n\ndiff --git a/Makefile b/Makefile\nnew file mode 100644\nindex 0000000..55f28e5\n--- /dev/null\n+++ b/Makefile\n@@ -0,0 +1,8 @@\n+svnrdump: *.c\n+\t$(CC) -Wall -Werror -DAPR_POOL_DEBUG -ggdb3 -O0 -o $@ svnrdump.c -lsvn_client-1 -I. -I/usr/include/subversion-1 -I/usr/include/apr-1.0\n+\n+svnrdump_bench: *.c\n+\t$(CC) -O2 -o $@ svnrdump.c -lsvn_client-1 -I. -I/usr/include/subversion-1 -I/usr/include/apr-1.0\n+\n+clean:\n+\t$(RM) svnrdump svnrdump_bench\ndiff --git a/svnrdump.c b/svnrdump.c\nnew file mode 100644\nindex 0000000..35c1a73\n--- /dev/null\n+++ b/svnrdump.c\n@@ -0,0 +1,102 @@\n+/* Licensed under a two-clause BSD-style license.\n+ * See LICENSE for details.\n+ */\n+\n+#include \"svn_pools.h\"\n+#include \"svn_cmdline.h\"\n+#include \"svn_client.h\"\n+#include \"svn_ra.h\"\n+#include \"svn_repos.h\"\n+#include \"svn_path.h\"\n+\n+static int verbose = 0;\n+static apr_pool_t *pool = NULL;\n+static svn_client_ctx_t *ctx = NULL;\n+static svn_ra_session_t *session = NULL;\n+\n+static svn_error_t *open_connection(const char *url)\n+{\n+\tSVN_ERR(svn_config_ensure (NULL, pool));\n+\tSVN_ERR(svn_client_create_context (&ctx, pool));\n+\tSVN_ERR(svn_ra_initialize(pool));\n+\n+\tSVN_ERR(svn_config_get_config(&(ctx->config), NULL, pool));\n+\n+\t/* Default authentication providers for non-interactive use */\n+\tSVN_ERR(svn_cmdline_create_auth_baton(&(ctx->auth_baton), TRUE,\n+\t\t\t\t\t      NULL, NULL, NULL, FALSE,\n+\t\t\t\t\t      FALSE, NULL, NULL, NULL,\n+\t\t\t\t\t      pool));\n+\tSVN_ERR(svn_client_open_ra_session(&session, url, ctx, pool));\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *replay_range(svn_revnum_t start_revision, svn_revnum_t end_revision)\n+{\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *usage(FILE *out_stream)\n+{\n+\tfprintf(out_stream,\n+\t\t\"usage: svnrdump URL [-r LOWER[:UPPER]]\\n\\n\"\n+\t\t\"Dump the contents of repository at remote URL to stdout in a 'dumpfile'\\n\"\n+\t\t\"v3 portable format.  Dump revisions LOWER rev through UPPER rev.\\n\"\n+\t\t\"LOWER defaults to 1 and UPPER defaults to the highest possible revision\\n\"\n+\t\t\"if omitted.\\n\");\n+\treturn SVN_NO_ERROR;\n+}\n+\n+\n+int main(int argc, const char **argv)\n+{\n+\tint i;\n+\tconst char *url = NULL;\n+\tchar *revision_cut = NULL;\n+\tsvn_revnum_t start_revision = svn_opt_revision_unspecified;\n+\tsvn_revnum_t end_revision = svn_opt_revision_unspecified;\n+\n+\tif (svn_cmdline_init (\"svnrdump\", stderr) != EXIT_SUCCESS)\n+\t\treturn EXIT_FAILURE;\n+\n+\tpool = svn_pool_create(NULL);\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tif (!strncmp(\"-r\", argv[i], 2)) {\n+\t\t\trevision_cut = strchr(argv[i] + 2, ':');\n+\t\t\tif (revision_cut) {\n+\t\t\t\tstart_revision = (svn_revnum_t) strtoul(argv[i] + 2, &revision_cut, 10);\n+\t\t\t\tend_revision = (svn_revnum_t) strtoul(revision_cut + 1, NULL, 10);\n+\t\t\t}\n+\t\t\telse\n+\t\t\t\tstart_revision = (svn_revnum_t) strtoul(argv[i] + 2, NULL, 10);\n+\t\t} else if (!strcmp(\"-v\", argv[i]) || !strcmp(\"--verbose\", argv[i])) {\n+\t\t\tverbose = 1;\n+\t\t} else if (!strcmp(\"help\", argv[i]) || !strcmp(\"--help\", argv[i])) {\n+\t\t\tSVN_INT_ERR(usage(stdout));\n+\t\t\treturn EXIT_SUCCESS;\n+\t\t} else if (*argv[i] == '-' || url) {\n+\t\t\tSVN_INT_ERR(usage(stderr));\n+\t\t\treturn EXIT_FAILURE;\n+\t\t} else\n+\t\t\turl = argv[i];\n+\t}\n+\n+\tif (!url || !svn_path_is_url(url)) {\n+\t\tusage(stderr);\n+\t\treturn EXIT_FAILURE;\n+\t}\n+\tSVN_INT_ERR(open_connection(url));\n+\n+\t/* Have sane start_revision and end_revision defaults if unspecified */\n+\tif (start_revision == svn_opt_revision_unspecified)\n+\t\tstart_revision = 1;\n+\tif (end_revision == svn_opt_revision_unspecified)\n+\t\tSVN_INT_ERR(svn_ra_get_latest_revnum(session, &end_revision, pool));\n+\n+\tSVN_INT_ERR(replay_range(start_revision, end_revision));\n+\n+\tsvn_pool_destroy(pool);\n+\t\n+\treturn 0;\n+}\n-- \n1.7.1\n"},{"id":"145494","messageId":"1279064176-6645-4-git-send-email-artagnon@gmail.com","threadId":"24388","inReplyTo":"1279064176-6645-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 3/9] Add debug editor from Subversion trunk","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-07-13T23:36:10Z","receivedAt":"2010-07-13T23:36:10Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Add the debug editor from subversion/libsvn_delta/debug_editor.c along\nwith a header to expose the svn_delta__get_debug_editor function. It\nis used for wrapping other editors, and prints the operations it does\nto stderr making debugging easier.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n Makefile       |    8 +-\n debug_editor.c |  421 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n debug_editor.h |   10 ++\n dump_editor.c  |  144 +++++++++++++++++++\n dump_editor.h  |    8 +\n dumpr_util.h   |    9 ++\n 6 files changed, 596 insertions(+), 4 deletions(-)\n create mode 100644 debug_editor.c\n create mode 100644 debug_editor.h\n create mode 100644 dump_editor.c\n create mode 100644 dump_editor.h\n create mode 100644 dumpr_util.h\n\ndiff --git a/Makefile b/Makefile\nindex 55f28e5..3561909 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1,8 +1,8 @@\n-svnrdump: *.c\n-\t$(CC) -Wall -Werror -DAPR_POOL_DEBUG -ggdb3 -O0 -o $@ svnrdump.c -lsvn_client-1 -I. -I/usr/include/subversion-1 -I/usr/include/apr-1.0\n+svnrdump: *.c *.h\n+\t$(CC) -Wall -Werror -DAPR_POOL_DEBUG -ggdb3 -O0 -o $@ svnrdump.c debug_editor.c dump_editor.c -lsvn_client-1 -I. -I/usr/include/subversion-1 -I/usr/include/apr-1.0\n \n-svnrdump_bench: *.c\n-\t$(CC) -O2 -o $@ svnrdump.c -lsvn_client-1 -I. -I/usr/include/subversion-1 -I/usr/include/apr-1.0\n+svnrdump_bench: *.c *.h\n+\t$(CC) -O2 -o $@ svnrdump.c debug_editor.c dump_editor.c -lsvn_client-1 -I. -I/usr/include/subversion-1 -I/usr/include/apr-1.0\n \n clean:\n \t$(RM) svnrdump svnrdump_bench\ndiff --git a/debug_editor.c b/debug_editor.c\nnew file mode 100644\nindex 0000000..9271c7a\n--- /dev/null\n+++ b/debug_editor.c\n@@ -0,0 +1,421 @@\n+/*\n+ * debug_editor.c :  An editor that writes the operations it does to stderr.\n+ *\n+ * ====================================================================\n+ *    Licensed to the Apache Software Foundation (ASF) under one\n+ *    or more contributor license agreements.  See the NOTICE file\n+ *    distributed with this work for additional information\n+ *    regarding copyright ownership.  The ASF licenses this file\n+ *    to you under the Apache License, Version 2.0 (the\n+ *    \"License\"); you may not use this file except in compliance\n+ *    with the License.  You may obtain a copy of the License at\n+ *\n+ *      http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ *    Unless required by applicable law or agreed to in writing,\n+ *    software distributed under the License is distributed on an\n+ *    \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n+ *    KIND, either express or implied.  See the License for the\n+ *    specific language governing permissions and limitations\n+ *    under the License.\n+ * ====================================================================\n+ */\n+\n+#include \"svn_pools.h\"\n+#include \"svn_cmdline.h\"\n+#include \"svn_client.h\"\n+#include \"svn_ra.h\"\n+\n+#include \"debug_editor.h\"\n+\n+struct edit_baton\n+{\n+\tconst svn_delta_editor_t *wrapped_editor;\n+\tvoid *wrapped_edit_baton;\n+\n+\tint indent_level;\n+\n+\tsvn_stream_t *out;\n+};\n+\n+struct dir_baton\n+{\n+\tvoid *edit_baton;\n+\tvoid *wrapped_dir_baton;\n+};\n+\n+struct file_baton\n+{\n+\tvoid *edit_baton;\n+\tvoid *wrapped_file_baton;\n+};\n+\n+static svn_error_t *write_indent(struct edit_baton *eb, apr_pool_t *pool)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < eb->indent_level; ++i)\n+\t\tSVN_ERR(svn_stream_printf(eb->out, pool, \" \"));\n+\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *set_target_revision(void *edit_baton,\n+\t\t\t\t\tsvn_revnum_t target_revision,\n+\t\t\t\t\tapr_pool_t *pool)\n+{\n+\tstruct edit_baton *eb = edit_baton;\n+\n+\tSVN_ERR(write_indent(eb, pool));\n+\tSVN_ERR(svn_stream_printf(eb->out, pool, \"set_target_revision : %ld\\n\",\n+\t\t\t\t  target_revision));\n+\n+\treturn eb->wrapped_editor->set_target_revision(eb->wrapped_edit_baton,\n+\t\t\t\t\t\t       target_revision,\n+\t\t\t\t\t\t       pool);\n+}\n+\n+static svn_error_t *open_root(void *edit_baton,\n+\t\t\t      svn_revnum_t base_revision,\n+\t\t\t      apr_pool_t *pool,\n+\t\t\t      void **root_baton)\n+{\n+\tstruct edit_baton *eb = edit_baton;\n+\tstruct dir_baton *dir_baton = apr_palloc(pool, sizeof(*dir_baton));\n+\n+\tSVN_ERR(write_indent(eb, pool));\n+\tSVN_ERR(svn_stream_printf(eb->out, pool, \"open_root : %ld\\n\",\n+\t\t\t\t  base_revision));\n+\teb->indent_level++;\n+\n+\tSVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton,\n+\t\t\t\t\t      base_revision,\n+\t\t\t\t\t      pool,\n+\t\t\t\t\t      &dir_baton->wrapped_dir_baton));\n+\n+\tdir_baton->edit_baton = edit_baton;\n+\n+\t*root_baton = dir_baton;\n+\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *delete_entry(const char *path,\n+\t\t\t\t svn_revnum_t base_revision,\n+\t\t\t\t void *parent_baton,\n+\t\t\t\t apr_pool_t *pool)\n+{\n+\tstruct dir_baton *pb = parent_baton;\n+\tstruct edit_baton *eb = pb->edit_baton;\n+\n+\tSVN_ERR(write_indent(eb, pool));\n+\tSVN_ERR(svn_stream_printf(eb->out, pool, \"delete_entry : %s:%ld\\n\",\n+\t\t\t\t  path, base_revision));\n+\n+\treturn eb->wrapped_editor->delete_entry(path,\n+\t\t\t\t\t\tbase_revision,\n+\t\t\t\t\t\tpb->wrapped_dir_baton,\n+\t\t\t\t\t\tpool);\n+}\n+\n+static svn_error_t *add_directory(const char *path,\n+\t\t\t\t  void *parent_baton,\n+\t\t\t\t  const char *copyfrom_path,\n+\t\t\t\t  svn_revnum_t copyfrom_revision,\n+\t\t\t\t  apr_pool_t *pool,\n+\t\t\t\t  void **child_baton)\n+{\n+\tstruct dir_baton *pb = parent_baton;\n+\tstruct edit_baton *eb = pb->edit_baton;\n+\tstruct dir_baton *b = apr_palloc(pool, sizeof(*b));\n+\n+\tSVN_ERR(write_indent(eb, pool));\n+\tSVN_ERR(svn_stream_printf(eb->out, pool,\n+\t\t\t\t  \"add_directory : '%s' [from '%s':%ld]\\n\",\n+\t\t\t\t  path, copyfrom_path, copyfrom_revision));\n+\teb->indent_level++;\n+\n+\tSVN_ERR(eb->wrapped_editor->add_directory(path,\n+\t\t\t\t\t\t  pb->wrapped_dir_baton,\n+\t\t\t\t\t\t  copyfrom_path,\n+\t\t\t\t\t\t  copyfrom_revision,\n+\t\t\t\t\t\t  pool,\n+\t\t\t\t\t\t  &b->wrapped_dir_baton));\n+\n+\tb->edit_baton = eb;\n+\t*child_baton = b;\n+\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *open_directory(const char *path,\n+\t\t\t\t   void *parent_baton,\n+\t\t\t\t   svn_revnum_t base_revision,\n+\t\t\t\t   apr_pool_t *pool,\n+\t\t\t\t   void **child_baton)\n+{\n+\tstruct dir_baton *pb = parent_baton;\n+\tstruct edit_baton *eb = pb->edit_baton;\n+\tstruct dir_baton *db = apr_palloc(pool, sizeof(*db));\n+\n+\tSVN_ERR(write_indent(eb, pool));\n+\tSVN_ERR(svn_stream_printf(eb->out, pool, \"open_directory : '%s':%ld\\n\",\n+\t\t\t\t  path, base_revision));\n+\teb->indent_level++;\n+\n+\tSVN_ERR(eb->wrapped_editor->open_directory(path,\n+\t\t\t\t\t\t   pb->wrapped_dir_baton,\n+\t\t\t\t\t\t   base_revision,\n+\t\t\t\t\t\t   pool,\n+\t\t\t\t\t\t   &db->wrapped_dir_baton));\n+\n+\tdb->edit_baton = eb;\n+\t*child_baton = db;\n+\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *add_file(const char *path,\n+\t\t\t     void *parent_baton,\n+\t\t\t     const char *copyfrom_path,\n+\t\t\t     svn_revnum_t copyfrom_revision,\n+\t\t\t     apr_pool_t *pool,\n+\t\t\t     void **file_baton)\n+{\n+\tstruct dir_baton *pb = parent_baton;\n+\tstruct edit_baton *eb = pb->edit_baton;\n+\tstruct file_baton *fb = apr_palloc(pool, sizeof(*fb));\n+\n+\tSVN_ERR(write_indent(eb, pool));\n+\tSVN_ERR(svn_stream_printf(eb->out, pool,\n+\t\t\t\t  \"add_file : '%s' [from '%s':%ld]\\n\",\n+\t\t\t\t  path, copyfrom_path, copyfrom_revision));\n+\n+\teb->indent_level++;\n+\n+\tSVN_ERR(eb->wrapped_editor->add_file(path,\n+\t\t\t\t\t     pb->wrapped_dir_baton,\n+\t\t\t\t\t     copyfrom_path,\n+\t\t\t\t\t     copyfrom_revision,\n+\t\t\t\t\t     pool,\n+\t\t\t\t\t     &fb->wrapped_file_baton));\n+\n+\tfb->edit_baton = eb;\n+\t*file_baton = fb;\n+\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *open_file(const char *path,\n+\t\t\t      void *parent_baton,\n+\t\t\t      svn_revnum_t base_revision,\n+\t\t\t      apr_pool_t *pool,\n+\t\t\t      void **file_baton)\n+{\n+\tstruct dir_baton *pb = parent_baton;\n+\tstruct edit_baton *eb = pb->edit_baton;\n+\tstruct file_baton *fb = apr_palloc(pool, sizeof(*fb));\n+\n+\tSVN_ERR(write_indent(eb, pool));\n+\tSVN_ERR(svn_stream_printf(eb->out, pool, \"open_file : '%s':%ld\\n\",\n+\t\t\t\t  path, base_revision));\n+\n+\teb->indent_level++;\n+\n+\tSVN_ERR(eb->wrapped_editor->open_file(path,\n+\t\t\t\t\t      pb->wrapped_dir_baton,\n+\t\t\t\t\t      base_revision,\n+\t\t\t\t\t      pool,\n+\t\t\t\t\t      &fb->wrapped_file_baton));\n+\n+\tfb->edit_baton = eb;\n+\t*file_baton = fb;\n+\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *apply_textdelta(void *file_baton,\n+\t\t\t\t    const char *base_checksum,\n+\t\t\t\t    apr_pool_t *pool,\n+\t\t\t\t    svn_txdelta_window_handler_t *handler,\n+\t\t\t\t    void **handler_baton)\n+{\n+\tstruct file_baton *fb = file_baton;\n+\tstruct edit_baton *eb = fb->edit_baton;\n+\n+\tSVN_ERR(write_indent(eb, pool));\n+\tSVN_ERR(svn_stream_printf(eb->out, pool, \"apply_textdelta : %s\\n\",\n+\t\t\t\t  base_checksum));\n+\n+\tSVN_ERR(eb->wrapped_editor->apply_textdelta(fb->wrapped_file_baton,\n+\t\t\t\t\t\t    base_checksum,\n+\t\t\t\t\t\t    pool,\n+\t\t\t\t\t\t    handler,\n+\t\t\t\t\t\t    handler_baton));\n+\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *close_file(void *file_baton,\n+\t\t\t       const char *text_checksum,\n+\t\t\t       apr_pool_t *pool)\n+{\n+\tstruct file_baton *fb = file_baton;\n+\tstruct edit_baton *eb = fb->edit_baton;\n+\n+\teb->indent_level--;\n+\n+\tSVN_ERR(write_indent(eb, pool));\n+\tSVN_ERR(svn_stream_printf(eb->out, pool, \"close_file : %s\\n\",\n+\t\t\t\t  text_checksum));\n+\n+\tSVN_ERR(eb->wrapped_editor->close_file(fb->wrapped_file_baton,\n+\t\t\t\t\t       text_checksum, pool));\n+\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *absent_file(const char *path,\n+\t\t\t\tvoid *file_baton,\n+\t\t\t\tapr_pool_t *pool)\n+{\n+\tstruct file_baton *fb = file_baton;\n+\tstruct edit_baton *eb = fb->edit_baton;\n+\n+\tSVN_ERR(write_indent(eb, pool));\n+\tSVN_ERR(svn_stream_printf(eb->out, pool, \"absent_file : %s\\n\", path));\n+\n+\tSVN_ERR(eb->wrapped_editor->absent_file(path, fb->wrapped_file_baton,\n+\t\t\t\t\t\tpool));\n+\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *close_directory(void *dir_baton,\n+\t\t\t\t    apr_pool_t *pool)\n+{\n+\tstruct dir_baton *db = dir_baton;\n+\tstruct edit_baton *eb = db->edit_baton;\n+\n+\teb->indent_level--;\n+\tSVN_ERR(write_indent(eb, pool));\n+\tSVN_ERR(svn_stream_printf(eb->out, pool, \"close_directory\\n\"));\n+\n+\tSVN_ERR(eb->wrapped_editor->close_directory(db->wrapped_dir_baton,\n+\t\t\t\t\t\t    pool));\n+\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *absent_directory(const char *path,\n+\t\t\t\t     void *dir_baton,\n+\t\t\t\t     apr_pool_t *pool)\n+{\n+\tstruct dir_baton *db = dir_baton;\n+\tstruct edit_baton *eb = db->edit_baton;\n+\n+\tSVN_ERR(write_indent(eb, pool));\n+\tSVN_ERR(svn_stream_printf(eb->out, pool, \"absent_directory : %s\\n\",\n+\t\t\t\t  path));\n+\n+\tSVN_ERR(eb->wrapped_editor->absent_directory(path, db->wrapped_dir_baton,\n+\t\t\t\t\t\t     pool));\n+\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *change_file_prop(void *file_baton,\n+\t\t\t\t     const char *name,\n+\t\t\t\t     const svn_string_t *value,\n+\t\t\t\t     apr_pool_t *pool)\n+{\n+\tstruct file_baton *fb = file_baton;\n+\tstruct edit_baton *eb = fb->edit_baton;\n+\n+\tSVN_ERR(write_indent(eb, pool));\n+\tSVN_ERR(svn_stream_printf(eb->out, pool, \"change_file_prop : %s\\n\",\n+\t\t\t\t  name));\n+\n+\tSVN_ERR(eb->wrapped_editor->change_file_prop(fb->wrapped_file_baton,\n+\t\t\t\t\t\t     name,\n+\t\t\t\t\t\t     value,\n+\t\t\t\t\t\t     pool));\n+\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *change_dir_prop(void *dir_baton,\n+\t\t\t\t    const char *name,\n+\t\t\t\t    const svn_string_t *value,\n+\t\t\t\t    apr_pool_t *pool)\n+{\n+\tstruct dir_baton *db = dir_baton;\n+\tstruct edit_baton *eb = db->edit_baton;\n+\n+\tSVN_ERR(write_indent(eb, pool));\n+\tSVN_ERR(svn_stream_printf(eb->out, pool, \"change_dir_prop : %s\\n\", name));\n+\n+\tSVN_ERR(eb->wrapped_editor->change_dir_prop(db->wrapped_dir_baton,\n+\t\t\t\t\t\t    name,\n+\t\t\t\t\t\t    value,\n+\t\t\t\t\t\t    pool));\n+\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *close_edit(void *edit_baton,\n+\t\t\t       apr_pool_t *pool)\n+{\n+\tstruct edit_baton *eb = edit_baton;\n+\n+\tSVN_ERR(write_indent(eb, pool));\n+\tSVN_ERR(svn_stream_printf(eb->out, pool, \"close_edit\\n\"));\n+\n+\tSVN_ERR(eb->wrapped_editor->close_edit(eb->wrapped_edit_baton, pool));\n+\n+\treturn SVN_NO_ERROR;\n+}\n+\n+svn_error_t *svn_delta__get_debug_editor(const svn_delta_editor_t **editor,\n+\t\t\t\t\t void **edit_baton,\n+\t\t\t\t\t const svn_delta_editor_t *wrapped_editor,\n+\t\t\t\t\t void *wrapped_edit_baton,\n+\t\t\t\t\t apr_pool_t *pool)\n+{\n+\tsvn_delta_editor_t *tree_editor = svn_delta_default_editor(pool);\n+\tstruct edit_baton *eb = apr_palloc(pool, sizeof(*eb));\n+\tapr_file_t *errfp;\n+\tsvn_stream_t *out;\n+\n+\tapr_status_t apr_err = apr_file_open_stderr(&errfp, pool);\n+\tif (apr_err)\n+\t\treturn svn_error_wrap_apr(apr_err, \"Problem opening stderr\");\n+\n+\tout = svn_stream_from_aprfile2(errfp, TRUE, pool);\n+\n+\ttree_editor->set_target_revision = set_target_revision;\n+\ttree_editor->open_root = open_root;\n+\ttree_editor->delete_entry = delete_entry;\n+\ttree_editor->add_directory = add_directory;\n+\ttree_editor->open_directory = open_directory;\n+\ttree_editor->change_dir_prop = change_dir_prop;\n+\ttree_editor->close_directory = close_directory;\n+\ttree_editor->absent_directory = absent_directory;\n+\ttree_editor->add_file = add_file;\n+\ttree_editor->open_file = open_file;\n+\ttree_editor->apply_textdelta = apply_textdelta;\n+\ttree_editor->change_file_prop = change_file_prop;\n+\ttree_editor->close_file = close_file;\n+\ttree_editor->absent_file = absent_file;\n+\ttree_editor->close_edit = close_edit;\n+\n+\teb->wrapped_editor = wrapped_editor;\n+\teb->wrapped_edit_baton = wrapped_edit_baton;\n+\teb->out = out;\n+\teb->indent_level = 0;\n+\n+\t*editor = tree_editor;\n+\t*edit_baton = eb;\n+\n+\treturn SVN_NO_ERROR;\n+}\ndiff --git a/debug_editor.h b/debug_editor.h\nnew file mode 100644\nindex 0000000..a0d412a\n--- /dev/null\n+++ b/debug_editor.h\n@@ -0,0 +1,10 @@\n+#ifndef DEBUG_EDITOR_H_\n+#define DEBUG_EDITOR_H_\n+\n+svn_error_t *svn_delta__get_debug_editor(const svn_delta_editor_t **editor,\n+\t\t\t\t\t void **edit_baton,\n+\t\t\t\t\t const svn_delta_editor_t *wrapped_editor,\n+\t\t\t\t\t void *wrapped_edit_baton,\n+\t\t\t\t\t apr_pool_t *pool);\n+\n+#endif\ndiff --git a/dump_editor.c b/dump_editor.c\nnew file mode 100644\nindex 0000000..2db3f7e\n--- /dev/null\n+++ b/dump_editor.c\n@@ -0,0 +1,144 @@\n+/* Licensed under a two-clause BSD-style license.\n+ * See LICENSE for details.\n+ */\n+\n+#include \"svn_pools.h\"\n+#include \"svn_repos.h\"\n+#include \"svn_path.h\"\n+#include \"svn_props.h\"\n+#include \"svn_dirent_uri.h\"\n+\n+#include \"dumpr_util.h\"\n+\n+static svn_error_t *open_root(void *edit_baton,\n+\t\t\t      svn_revnum_t base_revision,\n+\t\t\t      apr_pool_t *pool,\n+\t\t\t      void **root_baton)\n+{\n+\t*root_baton = NULL;\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *delete_entry(const char *path,\n+\t\t\t\t svn_revnum_t revision,\n+\t\t\t\t void *parent_baton,\n+\t\t\t\t apr_pool_t *pool)\n+{\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *add_directory(const char *path,\n+\t\t\t\t  void *parent_baton,\n+\t\t\t\t  const char *copyfrom_path,\n+\t\t\t\t  svn_revnum_t copyfrom_rev,\n+\t\t\t\t  apr_pool_t *pool,\n+\t\t\t\t  void **child_baton)\n+{\n+\t*child_baton = NULL;\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *open_directory(const char *path,\n+\t\t\t\t   void *parent_baton,\n+\t\t\t\t   svn_revnum_t base_revision,\n+\t\t\t\t   apr_pool_t *pool,\n+\t\t\t\t   void **child_baton)\n+{\n+\t*child_baton = NULL;\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *close_directory(void *dir_baton,\n+\t\t\t\t    apr_pool_t *pool)\n+{\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *add_file(const char *path,\n+\t\t\t     void *parent_baton,\n+\t\t\t     const char *copyfrom_path,\n+\t\t\t     svn_revnum_t copyfrom_rev,\n+\t\t\t     apr_pool_t *pool,\n+\t\t\t     void **file_baton)\n+{\n+\t*file_baton = NULL;\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *open_file(const char *path,\n+\t\t\t      void *parent_baton,\n+\t\t\t      svn_revnum_t ancestor_revision,\n+\t\t\t      apr_pool_t *pool,\n+\t\t\t      void **file_baton)\n+{\n+\t*file_baton = NULL;\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *change_dir_prop(void *parent_baton,\n+\t\t\t\t    const char *name,\n+\t\t\t\t    const svn_string_t *value,\n+\t\t\t\t    apr_pool_t *pool)\n+{\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *change_file_prop(void *file_baton,\n+\t\t\t\t     const char *name,\n+\t\t\t\t     const svn_string_t *value,\n+\t\t\t\t     apr_pool_t *pool)\n+{\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *apply_textdelta(void *file_baton, const char *base_checksum,\n+\t\t\t\t    apr_pool_t *pool,\n+\t\t\t\t    svn_txdelta_window_handler_t *handler,\n+\t\t\t\t    void **handler_baton)\n+{\n+\t*handler = svn_delta_noop_window_handler;\n+\t*handler_baton = NULL;\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *close_file(void *file_baton,\n+\t\t\t       const char *text_checksum,\n+\t\t\t       apr_pool_t *pool)\n+{\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *close_edit(void *edit_baton, apr_pool_t *pool)\n+{\n+\treturn SVN_NO_ERROR;\n+}\n+\n+svn_error_t *get_dump_editor(const svn_delta_editor_t **editor,\n+                             void **edit_baton,\n+                             svn_revnum_t from_rev,\n+                             apr_pool_t *pool)\n+{\n+\tstruct dump_edit_baton *eb = apr_pcalloc(pool, sizeof(struct dump_edit_baton));\n+\teb->current_rev = from_rev;\n+\tSVN_ERR(svn_stream_for_stdout(&(eb->stream), pool));\n+\tsvn_delta_editor_t *de = svn_delta_default_editor(pool);\n+\t\n+\tde->open_root = open_root;\n+\tde->delete_entry = delete_entry;\n+\tde->add_directory = add_directory;\n+\tde->open_directory = open_directory;\n+\tde->close_directory = close_directory;\n+\tde->change_dir_prop = change_dir_prop;\n+\tde->change_file_prop = change_file_prop;\n+\tde->apply_textdelta = apply_textdelta;\n+\tde->add_file = add_file;\n+\tde->open_file = open_file;\n+\tde->close_file = close_file;\n+\tde->close_edit = close_edit;\n+\n+\t/* Set the edit_baton and editor */\n+\t*edit_baton = eb;\n+\t*editor = de;\n+\n+\treturn SVN_NO_ERROR;\n+}\ndiff --git a/dump_editor.h b/dump_editor.h\nnew file mode 100644\nindex 0000000..9c70b74\n--- /dev/null\n+++ b/dump_editor.h\n@@ -0,0 +1,8 @@\n+#ifndef DUMP_EDITOR_H_\n+#define DUMP_EDITOR_H_\n+\n+svn_error_t *get_dump_editor(const svn_delta_editor_t **editor,\n+\t\t\t     void **edit_baton,\n+\t\t\t     svn_revnum_t to_rev,\n+\t\t\t     apr_pool_t *pool);\n+#endif\ndiff --git a/dumpr_util.h b/dumpr_util.h\nnew file mode 100644\nindex 0000000..199c9c4\n--- /dev/null\n+++ b/dumpr_util.h\n@@ -0,0 +1,9 @@\n+#ifndef DUMPR_UTIL_H_\n+#define DUMPR_UTIL_H_\n+\n+struct dump_edit_baton {\n+\tsvn_stream_t *stream;\n+\tsvn_revnum_t current_rev;\n+};\n+\n+#endif\n-- \n1.7.1\n"},{"id":"145491","messageId":"1279064176-6645-5-git-send-email-artagnon@gmail.com","threadId":"24388","inReplyTo":"1279064176-6645-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 4/9] Drive the debug editor","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-07-13T23:36:11Z","receivedAt":"2010-07-13T23:36:11Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Wrap the dummy dump editor in the debug editor and drive the debug\neditor to print out all the actions that occur during the replay to\nstderr. In replay_revstart, extract and set editor/ edit_baton from\nthe replay_baton; they must persist across all the callback functions\nwhile replaying a revision.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n dumpr_util.h |    5 +++++\n svnrdump.c   |   47 +++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 52 insertions(+), 0 deletions(-)\n\ndiff --git a/dumpr_util.h b/dumpr_util.h\nindex 199c9c4..8f494c1 100644\n--- a/dumpr_util.h\n+++ b/dumpr_util.h\n@@ -1,6 +1,11 @@\n #ifndef DUMPR_UTIL_H_\n #define DUMPR_UTIL_H_\n \n+struct replay_baton {\n+\tconst svn_delta_editor_t *editor;\n+\tvoid *edit_baton;\n+};\n+\n struct dump_edit_baton {\n \tsvn_stream_t *stream;\n \tsvn_revnum_t current_rev;\ndiff --git a/svnrdump.c b/svnrdump.c\nindex 35c1a73..e184fee 100644\n--- a/svnrdump.c\n+++ b/svnrdump.c\n@@ -9,11 +9,39 @@\n #include \"svn_repos.h\"\n #include \"svn_path.h\"\n \n+#include \"debug_editor.h\"\n+#include \"dump_editor.h\"\n+#include \"dumpr_util.h\"\n+\n static int verbose = 0;\n static apr_pool_t *pool = NULL;\n static svn_client_ctx_t *ctx = NULL;\n static svn_ra_session_t *session = NULL;\n \n+static svn_error_t *replay_revstart(svn_revnum_t revision,\n+                                    void *replay_baton,\n+                                    const svn_delta_editor_t **editor,\n+                                    void **edit_baton,\n+                                    apr_hash_t *rev_props,\n+                                    apr_pool_t *pool)\n+{\n+\tstruct replay_baton *rb = replay_baton;\n+\t*editor = rb->editor;\n+\t*edit_baton = rb->edit_baton;\n+\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *replay_revend(svn_revnum_t revision,\n+                                  void *replay_baton,\n+                                  const svn_delta_editor_t *editor,\n+                                  void *edit_baton,\n+                                  apr_hash_t *rev_props,\n+                                  apr_pool_t *pool)\n+{\n+\treturn SVN_NO_ERROR;\n+}\n+\n static svn_error_t *open_connection(const char *url)\n {\n \tSVN_ERR(svn_config_ensure (NULL, pool));\n@@ -33,6 +61,25 @@ static svn_error_t *open_connection(const char *url)\n \n static svn_error_t *replay_range(svn_revnum_t start_revision, svn_revnum_t end_revision)\n {\n+\tconst svn_delta_editor_t *dump_editor, *debug_editor;\n+\tvoid *debug_baton, *dump_baton;\n+\n+\tSVN_ERR(get_dump_editor(&dump_editor,\n+\t                        &dump_baton, start_revision, pool));\n+\n+\tSVN_ERR(svn_delta__get_debug_editor(&debug_editor,\n+\t                                    &debug_baton,\n+\t                                    dump_editor,\n+\t                                    dump_baton, pool));\n+\n+\tstruct replay_baton *replay_baton = apr_palloc(pool, sizeof(struct replay_baton));\n+\treplay_baton->editor = debug_editor;\n+\treplay_baton->edit_baton = debug_baton;\n+\tSVN_ERR(svn_cmdline_printf(pool, SVN_REPOS_DUMPFILE_MAGIC_HEADER \": %d\\n\",\n+\t\t\t\t   SVN_REPOS_DUMPFILE_FORMAT_VERSION));\n+\tSVN_ERR(svn_ra_replay_range(session, start_revision, end_revision,\n+\t                            0, TRUE, replay_revstart, replay_revend,\n+\t                            replay_baton, pool));\n \treturn SVN_NO_ERROR;\n }\n \n-- \n1.7.1\n"},{"id":"145493","messageId":"1279064176-6645-6-git-send-email-artagnon@gmail.com","threadId":"24388","inReplyTo":"1279064176-6645-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 5/9] Dump the revprops at the start of every revision","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-07-13T23:36:12Z","receivedAt":"2010-07-13T23:36:12Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Fill in replay_revstart to dump the revprops at the start of every\nrevision. Add an additional write_hash_to_stringbuf helper function.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n Makefile      |    4 +-\n dump_editor.c |  207 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n dumpr_util.c  |  112 +++++++++++++++++++++++++++++++\n dumpr_util.h  |   63 +++++++++++++++++\n svnrdump.c    |   40 +++++++++++-\n 5 files changed, 421 insertions(+), 5 deletions(-)\n create mode 100644 dumpr_util.c\n\ndiff --git a/Makefile b/Makefile\nindex 3561909..6a5a101 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1,8 +1,8 @@\n svnrdump: *.c *.h\n-\t$(CC) -Wall -Werror -DAPR_POOL_DEBUG -ggdb3 -O0 -o $@ svnrdump.c debug_editor.c dump_editor.c -lsvn_client-1 -I. -I/usr/include/subversion-1 -I/usr/include/apr-1.0\n+\t$(CC) -Wall -Werror -DAPR_POOL_DEBUG -ggdb3 -O0 -o $@ svnrdump.c debug_editor.c dump_editor.c dumpr_util.c -lsvn_client-1 -I. -I/usr/include/subversion-1 -I/usr/include/apr-1.0\n \n svnrdump_bench: *.c *.h\n-\t$(CC) -O2 -o $@ svnrdump.c debug_editor.c dump_editor.c -lsvn_client-1 -I. -I/usr/include/subversion-1 -I/usr/include/apr-1.0\n+\t$(CC) -O2 -o $@ svnrdump.c debug_editor.c dump_editor.c dumpr_util.c -lsvn_client-1 -I. -I/usr/include/subversion-1 -I/usr/include/apr-1.0\n \n clean:\n \t$(RM) svnrdump svnrdump_bench\ndiff --git a/dump_editor.c b/dump_editor.c\nindex 2db3f7e..0506966 100644\n--- a/dump_editor.c\n+++ b/dump_editor.c\n@@ -10,12 +10,213 @@\n \n #include \"dumpr_util.h\"\n \n+#define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))\n+\n+/* Make a directory baton to represent the directory was path\n+   (relative to EDIT_BATON's path) is PATH.\n+\n+   CMP_PATH/CMP_REV are the path/revision against which this directory\n+   should be compared for changes.  If either is omitted (NULL for the\n+   path, SVN_INVALID_REVNUM for the rev), just compare this directory\n+   PATH against itself in the previous revision.\n+\n+   PARENT_DIR_BATON is the directory baton of this directory's parent,\n+   or NULL if this is the top-level directory of the edit.  ADDED\n+   indicated if this directory is newly added in this revision.\n+   Perform all allocations in POOL.  */\n+static struct dir_baton *make_dir_baton(const char *path,\n+\t\t\t\t\tconst char *cmp_path,\n+\t\t\t\t\tsvn_revnum_t cmp_rev,\n+\t\t\t\t\tvoid *edit_baton,\n+\t\t\t\t\tvoid *parent_dir_baton,\n+\t\t\t\t\tsvn_boolean_t added,\n+\t\t\t\t\tapr_pool_t *pool)\n+{\n+\tstruct dump_edit_baton *eb = edit_baton;\n+\tstruct dir_baton *pb = parent_dir_baton;\n+\tstruct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db));\n+\tconst char *full_path;\n+\tapr_array_header_t *compose_path = apr_array_make(pool, 2, sizeof(const char *));\n+\n+\t/* A path relative to nothing?  I don't think so. */\n+\tSVN_ERR_ASSERT_NO_RETURN(!path || pb);\n+\n+\t/* Construct the full path of this node. */\n+\tif (pb) {\n+\t\tAPR_ARRAY_PUSH(compose_path, const char *) = \"/\";\n+\t\tAPR_ARRAY_PUSH(compose_path, const char *) = path;\n+\t\tfull_path = svn_path_compose(compose_path, pool);\n+\t}\n+\telse\n+\t\tfull_path = apr_pstrdup(pool, \"/\");\n+\n+\t/* Remove leading slashes from copyfrom paths. */\n+\tif (cmp_path)\n+\t\tcmp_path = ((*cmp_path == '/') ? cmp_path + 1 : cmp_path);\n+\n+\tnew_db->eb = eb;\n+\tnew_db->parent_dir_baton = pb;\n+\tnew_db->path = full_path;\n+\tnew_db->cmp_path = cmp_path ? apr_pstrdup(pool, cmp_path) : NULL;\n+\tnew_db->cmp_rev = cmp_rev;\n+\tnew_db->added = added;\n+\tnew_db->written_out = FALSE;\n+\tnew_db->deleted_entries = apr_hash_make(pool);\n+\tnew_db->pool = pool;\n+\n+\treturn new_db;\n+}\n+\n+/*\n+ * Write out a node record for PATH of type KIND under EB->FS_ROOT.\n+ * ACTION describes what is happening to the node (see enum svn_node_action).\n+ * Write record to writable EB->STREAM, using EB->BUFFER to write in chunks.\n+ *\n+ * If the node was itself copied, IS_COPY is TRUE and the\n+ * path/revision of the copy source are in CMP_PATH/CMP_REV.  If\n+ * IS_COPY is FALSE, yet CMP_PATH/CMP_REV are valid, this node is part\n+ * of a copied subtree.\n+ */\n+static svn_error_t *dump_node(struct dump_edit_baton *eb,\n+\t\t\t      const char *path,    /* an absolute path. */\n+\t\t\t      svn_node_kind_t kind,\n+\t\t\t      enum svn_node_action action,\n+\t\t\t      const char *cmp_path,\n+\t\t\t      svn_revnum_t cmp_rev,\n+\t\t\t      apr_pool_t *pool)\n+{\n+\t/* Write out metadata headers for this file node. */\n+\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t\t\t  SVN_REPOS_DUMPFILE_NODE_PATH \": %s\\n\",\n+\t\t\t\t  (*path == '/') ? path + 1 : path));\n+\n+\tif (kind == svn_node_file)\n+\t\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t                          SVN_REPOS_DUMPFILE_NODE_KIND \": file\\n\"));\n+\telse if (kind == svn_node_dir)\n+\t\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t                          SVN_REPOS_DUMPFILE_NODE_KIND \": dir\\n\"));\n+\n+\t/* Remove leading slashes from copyfrom paths. */\n+\tif (cmp_path)\n+\t\tcmp_path = ((*cmp_path == '/') ? cmp_path + 1 : cmp_path);\n+\n+\tswitch (action) {\n+\t\t/* Appropriately handle the four svn_node_action actions */\n+\n+\tcase svn_node_action_change:\n+\t\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t                          SVN_REPOS_DUMPFILE_NODE_ACTION\n+\t\t                          \": change\\n\"));\n+\t\tbreak;\n+\n+\tcase svn_node_action_replace:\n+\t\tif (!eb->is_copy) {\n+\t\t\t/* a simple delete+add, implied by a single 'replace' action. */\n+\t\t\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t\t                          SVN_REPOS_DUMPFILE_NODE_ACTION\n+\t\t\t                          \": replace\\n\"));\n+\n+\t\t\teb->dump_props_pending = TRUE;\n+\t\t\tbreak;\n+\t\t}\n+\t\t/* More complex case: eb->is_copy is true, and\n+\t\t   cmp_path/ cmp_rev are present: delete the original,\n+\t\t   and then re-add it */\n+\n+\t\t/* the path & kind headers have already been printed;  just\n+\t\t   add a delete action, and end the current record.*/\n+\t\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t                          SVN_REPOS_DUMPFILE_NODE_ACTION\n+\t\t                          \": delete\\n\\n\"));\n+\n+\t\t/* recurse:  print an additional add-with-history record. */\n+\t\tSVN_ERR(dump_node(eb, path, kind, svn_node_action_add,\n+\t\t                  cmp_path, cmp_rev, pool));\n+\n+\t\t/* we can leave this routine quietly now, don't need to dump\n+\t\t   any content;  that was already done in the second record. */\n+\t\teb->must_dump_props = FALSE;\n+\t\teb->is_copy = FALSE;\n+\t\tbreak;\n+\n+\tcase svn_node_action_delete:\n+\t\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t                          SVN_REPOS_DUMPFILE_NODE_ACTION\n+\t\t                          \": delete\\n\"));\n+\n+\t\t/* we can leave this routine quietly now, don't need to dump\n+\t\t   any content. */\n+\t\tSVN_ERR(svn_stream_printf(eb->stream, pool, \"\\n\\n\"));\n+\t\teb->must_dump_props = FALSE;\n+\t\tbreak;\n+\n+\tcase svn_node_action_add:\n+\t\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t                          SVN_REPOS_DUMPFILE_NODE_ACTION \": add\\n\"));\n+\n+\t\tif (!eb->is_copy) {\n+\t\t\t/* eb->dump_props_pending for files is handled in\n+\t\t\t   close_file which is called immediately.\n+\t\t\t   However, directories are not closed until\n+\t\t\t   all the work inside them have been done;\n+\t\t\t   eb->dump_props_pending for directories is\n+\t\t\t   handled in all the functions that can\n+\t\t\t   possibly be called after add_directory:\n+\t\t\t   add_directory, open_directory,\n+\t\t\t   delete_entry, close_directory, add_file,\n+\t\t\t   open_file and change_dir_prop;\n+\t\t\t   change_dir_prop is a special case\n+\t\t\t   ofcourse */\n+\n+\t\t\teb->dump_props_pending = TRUE;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t                          SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV\n+\t\t                          \": %ld\\n\"\n+\t\t                          SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH\n+\t\t                          \": %s\\n\",\n+\t\t                          cmp_rev, cmp_path));\n+\n+\t\t/* Dump the text only if apply_textdelta sets\n+\t\t   eb->must_dump_text */\n+\n+\t\t/* UGLY hack: If a directory was copied from a\n+\t\t   previous revision, nothing else can be done, and\n+\t\t   close_file won't be called to write two blank\n+\t\t   lines; write them here */\n+\t\tif (kind == svn_node_dir)\n+\t\t\tSVN_ERR(svn_stream_printf(eb->stream, pool, \"\\n\\n\"));\n+\n+\t\teb->is_copy = FALSE;\n+\n+\t\tbreak;\n+\t}\n+\n+\t/* Dump property headers */\n+\tSVN_ERR(dump_props(eb, &(eb->must_dump_props), FALSE, pool));\n+\n+\treturn SVN_NO_ERROR;\n+}\n+\n static svn_error_t *open_root(void *edit_baton,\n \t\t\t      svn_revnum_t base_revision,\n \t\t\t      apr_pool_t *pool,\n \t\t\t      void **root_baton)\n {\n-\t*root_baton = NULL;\n+\t/* Allocate a special pool for the edit_baton to avoid pool\n+\t   lifetime issues */\n+\tstruct dump_edit_baton *eb = edit_baton;\n+\teb->pool = svn_pool_create(pool);\n+\teb->properties = apr_hash_make(eb->pool);\n+\teb->del_properties = apr_hash_make(eb->pool);\n+\teb->propstring = svn_stringbuf_create(\"\", eb->pool);\n+\teb->is_copy = FALSE;\n+\n+\t*root_baton = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,\n+\t                             edit_baton, NULL, FALSE, pool);\n \treturn SVN_NO_ERROR;\n }\n \n@@ -110,6 +311,10 @@ static svn_error_t *close_file(void *file_baton,\n \n static svn_error_t *close_edit(void *edit_baton, apr_pool_t *pool)\n {\n+\tstruct dump_edit_baton *eb = edit_baton;\n+\tsvn_pool_destroy(eb->pool);\n+\t(eb->current_rev) ++;\n+\n \treturn SVN_NO_ERROR;\n }\n \ndiff --git a/dumpr_util.c b/dumpr_util.c\nnew file mode 100644\nindex 0000000..33dc74a\n--- /dev/null\n+++ b/dumpr_util.c\n@@ -0,0 +1,112 @@\n+/* Licensed under a two-clause BSD-style license.\n+ * See LICENSE for details.\n+ */\n+\n+#include \"svn_pools.h\"\n+#include \"svn_cmdline.h\"\n+#include \"svn_client.h\"\n+#include \"svn_ra.h\"\n+#include \"svn_repos.h\"\n+\n+#include \"dumpr_util.h\"\n+\n+void write_hash_to_stringbuf(apr_hash_t *properties,\n+\t\t\t     svn_boolean_t deleted,\n+\t\t\t     svn_stringbuf_t **strbuf,\n+\t\t\t     apr_pool_t *pool)\n+{\n+\tapr_hash_index_t *this;\n+\tconst void *key;\n+\tvoid *val;\n+\tapr_ssize_t keylen;\n+\tsvn_string_t *value;\n+\n+\tif (!deleted) {\n+\t\tfor (this = apr_hash_first(pool, properties); this;\n+\t\t     this = apr_hash_next(this)) {\n+\t\t\t/* Get this key and val. */\n+\t\t\tapr_hash_this(this, &key, &keylen, &val);\n+\t\t\tvalue = val;\n+\n+\t\t\t/* Output name length, then name. */\n+\t\t\tsvn_stringbuf_appendcstr(*strbuf,\n+\t\t\t\t\t\t apr_psprintf(pool, \"K %\" APR_SSIZE_T_FMT \"\\n\",\n+\t\t\t\t\t\t\t      keylen));\n+\n+\t\t\tsvn_stringbuf_appendbytes(*strbuf, (const char *) key, keylen);\n+\t\t\tsvn_stringbuf_appendbytes(*strbuf, \"\\n\", 1);\n+\n+\t\t\t/* Output value length, then value. */\n+\t\t\tsvn_stringbuf_appendcstr(*strbuf,\n+\t\t\t\t\t\t apr_psprintf(pool, \"V %\" APR_SIZE_T_FMT \"\\n\",\n+\t\t\t\t\t\t\t      value->len));\n+\n+\t\t\tsvn_stringbuf_appendbytes(*strbuf, value->data, value->len);\n+\t\t\tsvn_stringbuf_appendbytes(*strbuf, \"\\n\", 1);\n+\t\t}\n+\t}\n+\telse {\n+\t\t/* Output a \"D \" entry for each deleted property */\n+\t\tfor (this = apr_hash_first(pool, properties); this;\n+\t\t     this = apr_hash_next(this)) {\n+\t\t\t/* Get this key */\n+\t\t\tapr_hash_this(this, &key, &keylen, NULL);\n+\n+\t\t\t/* Output name length, then name */\n+\t\t\tsvn_stringbuf_appendcstr(*strbuf,\n+\t\t\t\t\t\t apr_psprintf(pool, \"D %\" APR_SSIZE_T_FMT \"\\n\",\n+\t\t\t\t\t\t\t      keylen));\n+\n+\t\t\tsvn_stringbuf_appendbytes(*strbuf, (const char *) key, keylen);\n+\t\t\tsvn_stringbuf_appendbytes(*strbuf, \"\\n\", 1);\n+\t\t}\n+\t}\n+}\n+\n+svn_error_t *dump_props(struct dump_edit_baton *eb,\n+\t\t\tsvn_boolean_t *trigger_var,\n+\t\t\tsvn_boolean_t dump_data_too,\n+\t\t\tapr_pool_t *pool)\n+{\n+\tif (trigger_var && !*trigger_var)\n+\t\treturn SVN_NO_ERROR;\n+\n+\t/* Build a propstring to print */\n+\tsvn_stringbuf_setempty(eb->propstring);\n+\twrite_hash_to_stringbuf(eb->properties,\n+\t\t\t\tFALSE,\n+\t\t\t\t&(eb->propstring), eb->pool);\n+\twrite_hash_to_stringbuf(eb->del_properties,\n+\t\t\t\tTRUE,\n+\t\t\t\t&(eb->propstring), eb->pool);\n+\tsvn_stringbuf_appendbytes(eb->propstring, \"PROPS-END\\n\", 10);\n+\n+\t/* prop-delta header */\n+\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t\t\t  SVN_REPOS_DUMPFILE_PROP_DELTA\n+\t\t\t\t  \": true\\n\"));\n+\n+\t/* prop-content-length header */\n+\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t\t\t  SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH\n+\t\t\t\t  \": %\" APR_SIZE_T_FMT \"\\n\", eb->propstring->len));\n+\n+\tif (dump_data_too) {\n+\t\t/* content-length header */\n+\t\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t\t\t\t  SVN_REPOS_DUMPFILE_CONTENT_LENGTH\n+\t\t\t\t\t  \": %\" APR_SIZE_T_FMT \"\\n\\n\",\n+\t\t\t\t\t  eb->propstring->len));\n+\n+\t\t/* the properties themselves */\n+\t\tSVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,\n+\t\t\t\t\t &(eb->propstring->len)));\n+\n+\t\t/* Cleanup so that data is never dumped twice */\n+\t\tapr_hash_clear(eb->properties);\n+\t\tapr_hash_clear(eb->del_properties);\n+\t\tif (trigger_var)\n+\t\t\t*trigger_var = FALSE;\n+\t}\n+\treturn SVN_NO_ERROR;\n+}\ndiff --git a/dumpr_util.h b/dumpr_util.h\nindex 8f494c1..4b0d5b0 100644\n--- a/dumpr_util.h\n+++ b/dumpr_util.h\n@@ -9,6 +9,69 @@ struct replay_baton {\n struct dump_edit_baton {\n \tsvn_stream_t *stream;\n \tsvn_revnum_t current_rev;\n+\n+\t/* pool is for per-edit-session allocations */\n+\tapr_pool_t *pool;\n+\n+\t/* Store the properties that changed */\n+\tapr_hash_t *properties;\n+\tapr_hash_t *del_properties; /* Value is always 0x1 */\n+\tsvn_stringbuf_t *propstring;\n+\n+\t/* Was a copy command issued? */\n+\tsvn_boolean_t is_copy;\n+\n+\t/* Path of changed file */\n+\tconst char *changed_path;\n+\n+\t/* Temporary file to write delta to along with its checksum */\n+\tchar *temp_filepath;\n+\tsvn_checksum_t *checksum;\n+\n+\t/* Flags to trigger dumping props and text */\n+\tsvn_boolean_t must_dump_props;\n+\tsvn_boolean_t must_dump_text;\n+\tsvn_boolean_t dump_props_pending;\n+};\n+\n+struct dir_baton {\n+\tstruct dump_edit_baton *eb;\n+\tstruct dir_baton *parent_dir_baton;\n+\n+\t/* is this directory a new addition to this revision? */\n+\tsvn_boolean_t added;\n+\n+\t/* has this directory been written to the output stream? */\n+\tsvn_boolean_t written_out;\n+\n+\t/* the absolute path to this directory */\n+\tconst char *path;\n+\n+\t/* the comparison path and revision of this directory.  if both of\n+\t   these are valid, use them as a source against which to compare\n+\t   the directory instead of the default comparison source of PATH in\n+\t   the previous revision. */\n+\tconst char *cmp_path;\n+\tsvn_revnum_t cmp_rev;\n+\n+\t/* hash of paths that need to be deleted, though some -might- be\n+\t   replaced.  maps const char * paths to this dir_baton.  (they're\n+\t   full paths, because that's what the editor driver gives us.  but\n+\t   really, they're all within this directory.) */\n+\tapr_hash_t *deleted_entries;\n+\n+\t/* pool to be used for deleting the hash items */\n+\tapr_pool_t *pool;\n };\n \n+void write_hash_to_stringbuf(apr_hash_t *properties,\n+\t\t\t     svn_boolean_t deleted,\n+\t\t\t     svn_stringbuf_t **strbuf,\n+\t\t\t     apr_pool_t *pool);\n+\n+svn_error_t *dump_props(struct dump_edit_baton *eb,\n+\t\t\tsvn_boolean_t *trigger_var,\n+\t\t\tsvn_boolean_t dump_data_too,\n+\t\t\tapr_pool_t *pool);\n+\n #endif\ndiff --git a/svnrdump.c b/svnrdump.c\nindex e184fee..d7c1eea 100644\n--- a/svnrdump.c\n+++ b/svnrdump.c\n@@ -25,6 +25,39 @@ static svn_error_t *replay_revstart(svn_revnum_t revision,\n                                     apr_hash_t *rev_props,\n                                     apr_pool_t *pool)\n {\n+\t/* Editing this revision has just started; dump the revprops\n+\t   before invoking the editor callbacks */\n+\tsvn_stringbuf_t *propstring = svn_stringbuf_create(\"\", pool);\n+\tsvn_stream_t *stdout_stream;\n+\n+\t/* Create an stdout stream */\n+\tsvn_stream_for_stdout(&stdout_stream, pool);\n+\n+        /* Print revision number and prepare the propstring */\n+\tSVN_ERR(svn_stream_printf(stdout_stream, pool,\n+\t\t\t\t  SVN_REPOS_DUMPFILE_REVISION_NUMBER\n+\t\t\t\t  \": %ld\\n\", revision));\n+\twrite_hash_to_stringbuf(rev_props, FALSE, &propstring, pool);\n+\tsvn_stringbuf_appendbytes(propstring, \"PROPS-END\\n\", 10);\n+\n+\t/* prop-content-length header */\n+\tSVN_ERR(svn_stream_printf(stdout_stream, pool,\n+\t\t\t\t  SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH\n+\t\t\t\t  \": %\" APR_SIZE_T_FMT \"\\n\", propstring->len));\n+\n+\t/* content-length header */\n+\tSVN_ERR(svn_stream_printf(stdout_stream, pool,\n+\t\t\t\t  SVN_REPOS_DUMPFILE_CONTENT_LENGTH\n+\t\t\t\t  \": %\" APR_SIZE_T_FMT \"\\n\\n\", propstring->len));\n+\n+\t/* Print the revprops now */\n+\tSVN_ERR(svn_stream_write(stdout_stream, propstring->data,\n+\t\t\t\t &(propstring->len)));\n+\n+\tsvn_stream_close(stdout_stream);\n+\n+\t/* Extract editor and editor_baton from the replay_baton and\n+\t   set them so that the editor callbacks can use them */\n \tstruct replay_baton *rb = replay_baton;\n \t*editor = rb->editor;\n \t*edit_baton = rb->edit_baton;\n@@ -39,6 +72,9 @@ static svn_error_t *replay_revend(svn_revnum_t revision,\n                                   apr_hash_t *rev_props,\n                                   apr_pool_t *pool)\n {\n+\t/* Editor has finished for this revision and close_edit has\n+\t   been called; do nothing: just continue to the next\n+\t   revision */\n \treturn SVN_NO_ERROR;\n }\n \n@@ -73,8 +109,8 @@ static svn_error_t *replay_range(svn_revnum_t start_revision, svn_revnum_t end_r\n \t                                    dump_baton, pool));\n \n \tstruct replay_baton *replay_baton = apr_palloc(pool, sizeof(struct replay_baton));\n-\treplay_baton->editor = debug_editor;\n-\treplay_baton->edit_baton = debug_baton;\n+\treplay_baton->editor = dump_editor;\n+\treplay_baton->edit_baton = dump_baton;\n \tSVN_ERR(svn_cmdline_printf(pool, SVN_REPOS_DUMPFILE_MAGIC_HEADER \": %d\\n\",\n \t\t\t\t   SVN_REPOS_DUMPFILE_FORMAT_VERSION));\n \tSVN_ERR(svn_ra_replay_range(session, start_revision, end_revision,\n-- \n1.7.1\n"},{"id":"145492","messageId":"1279064176-6645-7-git-send-email-artagnon@gmail.com","threadId":"24388","inReplyTo":"1279064176-6645-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 6/9] Implement directory-related functions","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-07-13T23:36:13Z","receivedAt":"2010-07-13T23:36:13Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Implement open_directory, add_directory, change_dir_prop and\nclose_directory. All of them involve adding and removing entries from\nthe directory_baton and dumping the related node information. Note\nthat open_directory doesn't need any corresponding node information to\nbe printed.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n dump_editor.c |  112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 files changed, 110 insertions(+), 2 deletions(-)\n\ndiff --git a/dump_editor.c b/dump_editor.c\nindex 0506966..7fafa8b 100644\n--- a/dump_editor.c\n+++ b/dump_editor.c\n@@ -225,6 +225,15 @@ static svn_error_t *delete_entry(const char *path,\n \t\t\t\t void *parent_baton,\n \t\t\t\t apr_pool_t *pool)\n {\n+\tstruct dir_baton *pb = parent_baton;\n+\tconst char *mypath = apr_pstrdup(pb->pool, path);\n+\n+\t/* Some pending properties to dump? */\n+\tSVN_ERR(dump_props(pb->eb, &(pb->eb->dump_props_pending), TRUE, pool));\n+\n+\t/* remember this path needs to be deleted */\n+\tapr_hash_set(pb->deleted_entries, mypath, APR_HASH_KEY_STRING, pb);\n+\n \treturn SVN_NO_ERROR;\n }\n \n@@ -235,7 +244,35 @@ static svn_error_t *add_directory(const char *path,\n \t\t\t\t  apr_pool_t *pool,\n \t\t\t\t  void **child_baton)\n {\n-\t*child_baton = NULL;\n+\tstruct dir_baton *pb = parent_baton;\n+\tvoid *val;\n+\tstruct dir_baton *new_db\n+\t\t= make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb, TRUE, pool);\n+\n+\t/* Some pending properties to dump? */\n+\tSVN_ERR(dump_props(pb->eb, &(pb->eb->dump_props_pending), TRUE, pool));\n+\n+\t/* This might be a replacement -- is the path already deleted? */\n+\tval = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);\n+\n+\t/* Detect an add-with-history */\n+\tpb->eb->is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);\n+\n+\t/* Dump the node */\n+\tSVN_ERR(dump_node(pb->eb, path,\n+\t                  svn_node_dir,\n+\t                  val ? svn_node_action_replace : svn_node_action_add,\n+\t                  pb->eb->is_copy ? copyfrom_path : NULL,\n+\t                  pb->eb->is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,\n+\t                  pool));\n+\n+\tif (val)\n+\t\t/* Delete the path, it's now been dumped */\n+\t\tapr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);\n+\n+\tnew_db->written_out = TRUE;\n+\n+\t*child_baton = new_db;\n \treturn SVN_NO_ERROR;\n }\n \n@@ -245,13 +282,56 @@ static svn_error_t *open_directory(const char *path,\n \t\t\t\t   apr_pool_t *pool,\n \t\t\t\t   void **child_baton)\n {\n-\t*child_baton = NULL;\n+\tstruct dir_baton *pb = parent_baton;\n+\tstruct dir_baton *new_db;\n+\tconst char *cmp_path = NULL;\n+\tsvn_revnum_t cmp_rev = SVN_INVALID_REVNUM;\n+\tapr_array_header_t *compose_path = apr_array_make(pool, 2, sizeof(const char *));\n+\n+\t/* Some pending properties to dump? */\n+\tSVN_ERR(dump_props(pb->eb, &(pb->eb->dump_props_pending), TRUE, pool));\n+\n+\t/* If the parent directory has explicit comparison path and rev,\n+\t   record the same for this one. */\n+\tif (pb && ARE_VALID_COPY_ARGS(pb->cmp_path, pb->cmp_rev)) {\n+\t\tAPR_ARRAY_PUSH(compose_path, const char *) = pb->cmp_path;\n+\t\tAPR_ARRAY_PUSH(compose_path, const char *) = svn_relpath_basename(path, pool);\n+\t\tcmp_path = svn_path_compose(compose_path, pool);\n+\t\tcmp_rev = pb->cmp_rev;\n+\t}\n+\n+\tnew_db = make_dir_baton(path, cmp_path, cmp_rev, pb->eb, pb, FALSE, pool);\n+\t*child_baton = new_db;\n \treturn SVN_NO_ERROR;\n }\n \n static svn_error_t *close_directory(void *dir_baton,\n \t\t\t\t    apr_pool_t *pool)\n {\n+\tstruct dir_baton *db = dir_baton;\n+\tstruct dump_edit_baton *eb = db->eb;\n+\tapr_hash_index_t *hi;\n+\tapr_pool_t *subpool = svn_pool_create(pool);\n+\n+\t/* Some pending properties to dump? */\n+\tSVN_ERR(dump_props(eb, &(eb->dump_props_pending), TRUE, pool));\n+\n+\t/* Dump the directory entries */\n+\tfor (hi = apr_hash_first(pool, db->deleted_entries); hi;\n+\t     hi = apr_hash_next(hi)) {\n+\t\tconst void *key;\n+\t\tconst char *path;\n+\t\tapr_hash_this(hi, &key, NULL, NULL);\n+\t\tpath = key;\n+\n+\t\tsvn_pool_clear(subpool);\n+\n+\t\tSVN_ERR(dump_node(db->eb, path,\n+\t\t                  svn_node_unknown, svn_node_action_delete,\n+\t\t                  NULL, SVN_INVALID_REVNUM, subpool));\n+\t}\n+\n+\tsvn_pool_destroy(subpool);\n \treturn SVN_NO_ERROR;\n }\n \n@@ -281,6 +361,34 @@ static svn_error_t *change_dir_prop(void *parent_baton,\n \t\t\t\t    const svn_string_t *value,\n \t\t\t\t    apr_pool_t *pool)\n {\n+\tstruct dir_baton *db = parent_baton;\n+\n+\tif (svn_property_kind(NULL, name) != svn_prop_regular_kind)\n+\t\treturn SVN_NO_ERROR;\n+\n+\tvalue ? apr_hash_set(db->eb->properties, apr_pstrdup(pool, name),\n+\t                     APR_HASH_KEY_STRING, svn_string_dup(value, pool)) :\n+\t\tapr_hash_set(db->eb->del_properties, apr_pstrdup(pool, name),\n+\t\t             APR_HASH_KEY_STRING, (void *)0x1);\n+\n+\t/* This function is what distinguishes between a directory that is\n+\t   opened to merely get somewhere, vs. one that is opened because it\n+\t   actually changed by itself  */\n+\tif (! db->written_out) {\n+\t\t/* If eb->dump_props_pending was set, it means that the\n+\t\t   node information corresponding to add_directory has already\n+\t\t   been written; just don't unset it and dump_node will dump\n+\t\t   the properties before doing anything else. If it wasn't\n+\t\t   set, node information hasn't been written yet: so dump the\n+\t\t   node itself before dumping the props */\n+\n+\t\tSVN_ERR(dump_node(db->eb, db->path,\n+\t\t                  svn_node_dir, svn_node_action_change,\n+\t\t                  db->cmp_path, db->cmp_rev, pool));\n+\n+\t\tSVN_ERR(dump_props(db->eb, NULL, TRUE, pool));\n+\t\tdb->written_out = TRUE;\n+\t}\n \treturn SVN_NO_ERROR;\n }\n \n-- \n1.7.1\n"},{"id":"145496","messageId":"1279064176-6645-8-git-send-email-artagnon@gmail.com","threadId":"24388","inReplyTo":"1279064176-6645-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 7/9] Implement file-related functions","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-07-13T23:36:14Z","receivedAt":"2010-07-13T23:36:14Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Implement add_file, open_file and change_file_prop. All of them\ninvolve dumping the corresponding node information and setting up the\nfile_baton for apply_textdelta and close_file to use.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n dump_editor.c |  127 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--\n dumpr_util.h  |   15 +++++++\n 2 files changed, 138 insertions(+), 4 deletions(-)\n\ndiff --git a/dump_editor.c b/dump_editor.c\nindex 7fafa8b..8b0a830 100644\n--- a/dump_editor.c\n+++ b/dump_editor.c\n@@ -342,7 +342,34 @@ static svn_error_t *add_file(const char *path,\n \t\t\t     apr_pool_t *pool,\n \t\t\t     void **file_baton)\n {\n-\t*file_baton = NULL;\n+\tstruct dir_baton *pb = parent_baton;\n+\tvoid *val;\n+\n+\t/* Some pending properties to dump? */\n+\tSVN_ERR(dump_props(pb->eb, &(pb->eb->dump_props_pending), TRUE, pool));\n+\n+\t/* This might be a replacement -- is the path already deleted? */\n+\tval = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);\n+\n+\t/* Detect add-with-history. */\n+\tpb->eb->is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);\n+\n+\t/* Dump the node. */\n+\tSVN_ERR(dump_node(pb->eb, path,\n+\t                  svn_node_file,\n+\t                  val ? svn_node_action_replace : svn_node_action_add,\n+\t                  pb->eb->is_copy ? copyfrom_path : NULL,\n+\t                  pb->eb->is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,\n+\t                  pool));\n+\n+\tif (val)\n+\t\t/* delete the path, it's now been dumped. */\n+\t\tapr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);\n+\n+\t/* Build a nice file baton to pass to change_file_prop and apply_textdelta */\n+\tpb->eb->changed_path = path;\n+\t*file_baton = pb->eb;\n+\n \treturn SVN_NO_ERROR;\n }\n \n@@ -352,7 +379,31 @@ static svn_error_t *open_file(const char *path,\n \t\t\t      apr_pool_t *pool,\n \t\t\t      void **file_baton)\n {\n-\t*file_baton = NULL;\n+\tstruct dir_baton *pb = parent_baton;\n+\tconst char *cmp_path = NULL;\n+\tsvn_revnum_t cmp_rev = SVN_INVALID_REVNUM;\n+\n+\t/* Some pending properties to dump? */\n+\tSVN_ERR(dump_props(pb->eb, &(pb->eb->dump_props_pending), TRUE, pool));\n+\n+\tapr_array_header_t *compose_path = apr_array_make(pool, 2, sizeof(const char *));\n+\t/* If the parent directory has explicit comparison path and rev,\n+\t   record the same for this one. */\n+\tif (pb && ARE_VALID_COPY_ARGS(pb->cmp_path, pb->cmp_rev)) {\n+\t\tAPR_ARRAY_PUSH(compose_path, const char *) = pb->cmp_path;\n+\t\tAPR_ARRAY_PUSH(compose_path, const char *) = svn_relpath_basename(path, pool);\n+\t\tcmp_path = svn_path_compose(compose_path, pool);\n+\t\tcmp_rev = pb->cmp_rev;\n+\t}\n+\n+\tSVN_ERR(dump_node(pb->eb, path,\n+\t                  svn_node_file, svn_node_action_change,\n+\t                  cmp_path, cmp_rev, pool));\n+\n+\t/* Build a nice file baton to pass to change_file_prop and apply_textdelta */\n+\tpb->eb->changed_path = path;\n+\t*file_baton = pb->eb;\n+\n \treturn SVN_NO_ERROR;\n }\n \n@@ -397,6 +448,44 @@ static svn_error_t *change_file_prop(void *file_baton,\n \t\t\t\t     const svn_string_t *value,\n \t\t\t\t     apr_pool_t *pool)\n {\n+\tstruct dump_edit_baton *eb = file_baton;\n+\n+\tif (svn_property_kind(NULL, name) != svn_prop_regular_kind)\n+\t\treturn SVN_NO_ERROR;\n+\n+\tapr_hash_set(eb->properties, apr_pstrdup(pool, name),\n+\t\t     APR_HASH_KEY_STRING, value ?\n+\t\t     svn_string_dup(value, pool): (void *)0x1);\n+\n+\t/* Dump the property headers and wait; close_file might need\n+\t   to write text headers too depending on whether\n+\t   apply_textdelta is called */\n+\teb->dump_props_pending = TRUE;\n+\n+\treturn SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *window_handler(svn_txdelta_window_t *window, void *baton)\n+{\n+\tstruct handler_baton *hb = baton;\n+\tstruct dump_edit_baton *eb = hb->eb;\n+\tstatic svn_error_t *err;\n+\n+\terr = hb->apply_handler(window, hb->apply_baton);\n+\tif (window != NULL && !err)\n+\t\treturn SVN_NO_ERROR;\n+\n+\tif (err)\n+\t\tSVN_ERR(err);\n+\n+\t/* Write information about the filepath to hb->eb */\n+\teb->temp_filepath = apr_pstrdup(eb->pool,\n+\t\t\t\t\thb->temp_filepath);\n+\n+\t/* Cleanup */\n+\tSVN_ERR(svn_io_file_close(hb->temp_file, hb->pool));\n+\tSVN_ERR(svn_stream_close(hb->temp_filestream));\n+\tsvn_pool_destroy(hb->pool);\n \treturn SVN_NO_ERROR;\n }\n \n@@ -405,8 +494,38 @@ static svn_error_t *apply_textdelta(void *file_baton, const char *base_checksum,\n \t\t\t\t    svn_txdelta_window_handler_t *handler,\n \t\t\t\t    void **handler_baton)\n {\n-\t*handler = svn_delta_noop_window_handler;\n-\t*handler_baton = NULL;\n+\tstruct dump_edit_baton *eb = file_baton;\n+\tapr_status_t apr_err;\n+\tconst char *tempdir;\n+\n+\t/* Custom handler_baton allocated in a separate pool */\n+\tapr_pool_t *handler_pool = svn_pool_create(pool);\n+\tstruct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));\n+\thb->pool = handler_pool;\n+\thb->eb = eb;\n+\n+\t/* Use a temporary file to measure the text-content-length */\n+\tSVN_ERR(svn_io_temp_dir(&tempdir, hb->pool));\n+\n+\thb->temp_filepath = svn_dirent_join(tempdir, \"XXXXXX\", hb->pool);\n+\tapr_err = apr_file_mktemp(&(hb->temp_file), hb->temp_filepath,\n+\t\t\t\t  APR_CREATE | APR_READ | APR_WRITE | APR_EXCL,\n+\t\t\t\t  hb->pool);\n+\tif (apr_err != APR_SUCCESS)\n+\t\tSVN_ERR(svn_error_wrap_apr(apr_err, NULL));\n+\n+\thb->temp_filestream = svn_stream_from_aprfile2(hb->temp_file, TRUE, hb->pool);\n+\n+\t/* Prepare to write the delta to the temporary file */\n+\tsvn_txdelta_to_svndiff2(&(hb->apply_handler), &(hb->apply_baton),\n+\t                        hb->temp_filestream, 0, hb->pool);\n+\teb->must_dump_text = TRUE;\n+\n+\t/* The actual writing takes place when this function has finished */\n+\t/* Set the handler and handler_baton */\n+\t*handler = window_handler;\n+\t*handler_baton = hb;\n+\n \treturn SVN_NO_ERROR;\n }\n \ndiff --git a/dumpr_util.h b/dumpr_util.h\nindex 4b0d5b0..9870b0d 100644\n--- a/dumpr_util.h\n+++ b/dumpr_util.h\n@@ -64,6 +64,21 @@ struct dir_baton {\n \tapr_pool_t *pool;\n };\n \n+struct handler_baton\n+{\n+\tsvn_txdelta_window_handler_t apply_handler;\n+\tvoid *apply_baton;\n+\tapr_pool_t *pool;\n+\n+\t/* Information about the path of the tempoarary file used */\n+\tchar *temp_filepath;\n+\tapr_file_t *temp_file;\n+\tsvn_stream_t *temp_filestream;\n+\n+\t/* To fill in the edit baton fields */\n+\tstruct dump_edit_baton *eb;\n+};\n+\n void write_hash_to_stringbuf(apr_hash_t *properties,\n \t\t\t     svn_boolean_t deleted,\n \t\t\t     svn_stringbuf_t **strbuf,\n-- \n1.7.1\n"},{"id":"145495","messageId":"1279064176-6645-9-git-send-email-artagnon@gmail.com","threadId":"24388","inReplyTo":"1279064176-6645-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 8/9] Implement close_file","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-07-13T23:36:15Z","receivedAt":"2010-07-13T23:36:15Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"close_file measures the length of the temporary file to write text\nheaders and full text before cleaning up the temporary file. It also\nwrites props and prop deltas if necessary.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n dump_editor.c |   76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 76 insertions(+), 0 deletions(-)\n\ndiff --git a/dump_editor.c b/dump_editor.c\nindex 8b0a830..b2fd3d6 100644\n--- a/dump_editor.c\n+++ b/dump_editor.c\n@@ -533,7 +533,82 @@ static svn_error_t *close_file(void *file_baton,\n \t\t\t       const char *text_checksum,\n \t\t\t       apr_pool_t *pool)\n {\n+\tstruct dump_edit_baton *eb = file_baton;\n+\tapr_file_t *temp_file;\n+\tsvn_stream_t *temp_filestream;\n+\tapr_finfo_t *info = apr_pcalloc(pool, sizeof(apr_finfo_t));\n+\n+\t/* We didn't write the property headers because we were\n+\t   waiting for file_prop_change; write them now */\n+\tSVN_ERR(dump_props(eb, &(eb->dump_props_pending), FALSE, pool));\n+\n+\t/* The prop headers have already been dumped in dump_node */\n+\t/* Dump the text headers */\n+\tif (eb->must_dump_text) {\n+\t\t/* text-delta header */\n+\t\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t\t\t\t  SVN_REPOS_DUMPFILE_TEXT_DELTA\n+\t\t\t\t\t  \": true\\n\"));\n+\n+\t\t/* Measure the length */\n+\t\tSVN_ERR(svn_io_stat(info, eb->temp_filepath, APR_FINFO_SIZE, pool));\n+\n+\t\t/* text-content-length header */\n+\t\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t\t\t\t  SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH\n+\t\t\t\t\t  \": %lu\\n\",\n+\t\t\t\t\t  (unsigned long)info->size));\n+\t\t/* text-content-md5 header */\n+\t\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t\t\t\t  SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5\n+\t\t\t\t\t  \": %s\\n\",\n+\t\t\t\t\t  text_checksum));\n+\t}\n+\n+\t/* content-length header: if both text and props are absent,\n+\t   skip this block */\n+\tif (eb->must_dump_props || eb->dump_props_pending)\n+\t\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t\t\t\t  SVN_REPOS_DUMPFILE_CONTENT_LENGTH\n+\t\t\t\t\t  \": %ld\\n\\n\",\n+\t\t\t\t\t  (unsigned long)info->size + eb->propstring->len));\n+\telse if (eb->must_dump_text)\n+\t\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t\t\t\t  SVN_REPOS_DUMPFILE_CONTENT_LENGTH\n+\t\t\t\t\t  \": %ld\\n\\n\",\n+\t\t\t\t\t  (unsigned long)info->size));\n+\n+\t/* Dump the props; the propstring should have already been\n+\t   written in dump_node or above */\n+\tif (eb->must_dump_props || eb->dump_props_pending) {\n+\t\tSVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,\n+\t\t\t\t\t &(eb->propstring->len)));\n+\n+\t\t/* Cleanup */\n+\t\teb->must_dump_props = eb->dump_props_pending = FALSE;\n+\t\tapr_hash_clear(eb->properties);\n+\t\tapr_hash_clear(eb->del_properties);\n+\t}\n+\n+\t/* Dump the text */\n+\tif (eb->must_dump_text) {\n+\n+\t\t/* Open the temporary file, map it to a stream, copy\n+\t\t   the stream to eb->stream, close and delete the\n+\t\t   file */\n+\t\tSVN_ERR(svn_io_file_open(&temp_file, eb->temp_filepath, APR_READ, 0600, pool));\n+\t\ttemp_filestream = svn_stream_from_aprfile2(temp_file, TRUE, pool);\n+\t\tSVN_ERR(svn_stream_copy3(temp_filestream, eb->stream, NULL, NULL, pool));\n+\n+\t\t/* Cleanup */\n+\t\tSVN_ERR(svn_io_file_close(temp_file, pool));\n+\t\tSVN_ERR(svn_stream_close(temp_filestream));\n+\t\tSVN_ERR(svn_io_remove_file2(eb->temp_filepath, TRUE, pool));\n+\t\teb->must_dump_text = FALSE;\n+\t}\n+\n+\tSVN_ERR(svn_stream_printf(eb->stream, pool, \"\\n\\n\"));\n+\n \treturn SVN_NO_ERROR;\n }\n \n-- \n1.7.1\n"},{"id":"145497","messageId":"1279064176-6645-10-git-send-email-artagnon@gmail.com","threadId":"24388","inReplyTo":"1279064176-6645-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 9/9] Add a validation script","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-07-13T23:36:16Z","receivedAt":"2010-07-13T23:36:16Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"From: Will Palmer <wpalmer@gmail.com>\n\nAdd a validation script. Using an existing dump known to be correct\n(possibly generated using `svnsync` and `svnadmin dump --deltas`), it\ncompares the outputs produced by `svnadmin load` when fed with this\ndump and the dump from the program.\n\nThe arguments added are:\n    --svnadmin-dump=<file>      existing \"svnadmin dump\"\n    --svndumpr-dump=<file>      existing \"svndumpr\" dump\n    --repos=<url-or-path>       SVN repos URL, or local repos\n    -r<revision>                end revision to dump/cut\n    --ignore-existing           regenerate svnadmin-dump even if\n                                it already exists\n    --make                      run \"make svndumpr\" prior to svnrdump\n    generate                    as with the old parameter, but now\n                                implied by default\n    validate                    as with the old parameter, but now\n                                implied by default\n\nThe end result is that the only step technically needed to test\nvalidation is:\n\n./validate.sh --make --repos=/path/to/local/repos\n\nSigned-off-by: Will Palmer <wpalmer@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n validate.sh |  226 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 226 insertions(+), 0 deletions(-)\n create mode 100755 validate.sh\n\ndiff --git a/validate.sh b/validate.sh\nnew file mode 100755\nindex 0000000..6bdb427\n--- /dev/null\n+++ b/validate.sh\n@@ -0,0 +1,226 @@\n+#!/bin/sh\n+usage(){\n+\tsed 's/..//' <<USAGE\n+\t\tUsage: validate.sh [--svnadmin-dump=<file>] [--svnrdump-dump=<file>]\n+\t\t                   [--repos=<url-or-path>] [-r<revision>]\n+\t\t                   [--ignore-existing-dump] [--make]\n+\t\t                   [generate] [validate]\n+USAGE\n+}\n+\n+svnadmin_dump=\n+svnrdump_dump=\n+repos_url=\n+repos_path=\n+end_rev=\n+do_make=\n+do_both=1\n+do_generate=1\n+do_validate=1\n+do_ignore_existing_svnadmin_dump=\n+while test \"$#\" -gt 0; do\n+\tcase \"$1\" in\n+\t--svnadmin-dump=*)\n+\t\tsvnadmin_dump=\"${1#*=}\"\n+\t\t;;\n+\t--svnrdump-dump=*)\n+\t\tsvnrdump_dump=\"${1#*=}\"\n+\t\t;;\n+\t--ignore-existing|--ignore-existing-dump)\n+\t\tdo_ignore_existing_svnadmin_dump=1\n+\t\t;;\n+\t--repos=*)\n+\t\trepos_url=\"${1#*=}\"\n+\t\trepos_protocol=\"${repos_url%://*}\"\n+\t\trepos_path=\"${repos_url#file://}\"\n+\t\tif test \"$repos_protocol\" = \"$repos_path\"; then\n+\t\t\trepos_protocol=\n+\t\t\trepos_url=\n+\t\telif test ! \"$repos_protocol\" = 'file'; then\n+\t\t\trepos_path=\n+\t\tfi\n+\t\t;;\n+\t-r*)\n+\t\tend_rev=\"${1#-r}\"\n+\t\t;;\n+\t--make)\n+\t\tdo_make=1\n+\t\t;;\n+\tgenerate)\n+\t\tdo_generate=1\n+\t\ttest -n \"$do_both\" && do_validate=\n+\t\tdo_both=\n+\t\t;;\n+\tvalidate)\n+\t\tdo_validate=1\n+\t\ttest -n \"$do_both\" && do_generate=\n+\t\tdo_both=\n+\t\t;;\n+\t-h|--help)\n+\t\tusage\n+\t\texit\n+\t\t;;\n+\t*)\n+\t\techo \"unknown option $1\" >&2\n+\t\tusage >&2\n+\t\texit 1\n+\tesac\n+\tshift\n+done\n+\n+if test -z \"$svnrdump_dump\"; then\n+\tif test -z \"$repos_url\"; then\n+\t\tif test -n \"$repos_path\"; then\n+\t\t\trepos_url=\"file://$(readlink -f \"$repos_path\")\"\n+\t\t\tif test $? -ne 0; then\n+\t\t\t\techo \"error: unable to derive repos url from path\" >&2\n+\t\t\t\techo \"--svnrdump_dump=<file> or a local (file://) --repos=<url> is required\" >&2\n+\t\t\t\texit 1\n+\t\t\tfi\n+\t\telse\n+\t\t\t\techo \"--svnrdump_dump=<file> or a local (file://) --repos=<url> is required\" >&2\n+\t\t\t\texit 1\n+\t\tfi\n+\tfi\n+fi\n+\n+svnadmin_dump_cut=\"t/svnadmin-$end_rev.dump\"\n+svnrdump_dump_cut=\"t/svnrdump-$end_rev.dump\"\n+mkdir t 2>/dev/null\n+\n+if test -z \"$svnadmin_dump\"; then\n+\tif test -z \"$repos_path\"; then\n+\t\techo \"--svnadmin_dump=<file> or a local (file://) --repos=<url> is required\" >&2\n+\t\tusage >&2\n+\t\texit 1\n+\tfi\n+\n+\tsvnadmin_dump=\"$svnadmin_dump_cut\"\n+\tif test -z \"$do_ignore_existing_svnadmin_dump\" && test -r \"$svnadmin_dump\"; then\n+\t\techo \"Using existing $svnadmin_dump\"\n+\telse\n+\t\techo \"Generating $svnadmin_dump ...\"\n+\n+\t\tr=\n+\t\tif test -n \"$end_rev\"; then\n+\t\t\tr=\"-r0:$end_rev\"\n+\t\telse\n+\t\t\tr=\"-r0:HEAD\"\n+\t\tfi\n+\n+\t\tsvnadmin dump --deltas $r \"$repos_path\" > \"$svnadmin_dump\"\n+\t\tif test $? -ne 0; then\n+\t\t\techo \"error: failed to create canonical dump for comparison\" >&2\n+\t\t\texit 1\n+\t\tfi\n+\tfi\n+else\n+\techo \"Using specified $svnadmin_dump\"\n+fi\n+\n+if test -z \"$svnrdump_dump\"; then\n+\tsvnrdump_dump=\"$svnrdump_dump_cut\"\n+\n+\tif test -n \"$do_make\"; then\n+\t\tmake svnrdump > /dev/null;\n+\t\tif test $? -ne 0; then\n+\t\t\techo \"error: Make failed. Check the program.\" >&2\n+\t\t\texit 1;\n+\t\tfi\n+\tfi\n+\n+\techo \"Generating $svnrdump_dump ...\"\n+\n+\tr=\n+\ttest -n \"$end_rev\" && r=\"-r0:$end_rev\"\n+\n+\t./svnrdump -v $r \"$repos_url\" > \"$svnrdump_dump\"\n+\tif test $? -ne 0; then\n+\t\techo \"error: failed to create dump for validation\" >&2\n+\t\texit 1\n+\tfi\n+else\n+\techo \"Using specified $svnrdump_dump\"\n+fi\n+\n+cut_dump(){\n+\tr=\"$1\"\n+\ttest -z \"$r\" && r=-1\n+\n+\tgawk '\n+\t\tBEGIN {\n+\t\t\tmax='\"$r\"'\n+\t\t\thit_max=0\n+\t\t}\n+\t\t/^Revision-number: [0-9][0-9]*$/ {\n+\t\trev=$2\n+\t\t\tif (rev == max) {\n+\t\t\t\thit_max=1\n+\t\t\t} else if ( hit_max ) {\n+\t\t\t\texit\n+\t\t\t}\n+\t\t}\n+\t\t{ print $0 }\n+\t\tEND {\n+\t\t\tif (max == -1 || hit_max) {\n+\t\t\t\texit 0\n+\t\t\t}\n+\t\t\texit 1\n+\t\t}'\n+}\n+\n+if test ! \"$svnadmin_dump\" = \"$svnadmin_dump_cut\"; then\n+\tcut_dump \"$end_rev\" <\"$svnadmin_dump\" >\"$svnadmin_dump_cut\"\n+\tif test $? -ne 0; then\n+\t\techo \"error: failed to cut canonical dump $svnadmin_dump for comparison\" >&2\n+\t\texit 1\n+\tfi\n+\techo \"Successfully generated cut canonical dump $svnadmin_dump_cut for comparison\" >&2\n+fi\n+\n+echo \"Comparing canonical and svnrdump-based dumps...\"\n+diff -au \"$svnadmin_dump_cut\" \"$svnrdump_dump\" > t/dump-diff.error\n+gawk \\\n+\t'$0 !~ \"Prop-delta: true|Text-delta-base-|sha1|Text-copy-source-|^-$\" && $0 ~ \"^+|^-\" { print; }' \\\n+\tt/dump-diff.error >t/dump-diff-filtered.error;\n+\n+if test -n \"$do_generate\"; then\n+\techo \"Generating canonical import logs...\"\n+\n+\trm -rf t/repo;\n+\tmkdir t/repo;\n+\tsvnadmin create t/repo;\n+\n+\tsvnadmin load t/repo < \"$svnadmin_dump_cut\" \\\n+\t    1>\"$svnadmin_dump_cut.import.log\" 2>\"$svnadmin_dump_cut.import.error\";\n+\tif test $? -ne 0; then\n+\t\techo \"error: Load $end_rev failed. See $svnadmin_dump_cut.import.* for details\" >&2\n+\t\texit 1\n+\tfi\n+\techo \"Successfully generated canonical repository for comparison.\"\n+fi\n+\n+if test -n \"$do_validate\"; then\n+\techo \"Generating svnrdump-based import logs...\"\n+\n+\tlog_prefix=t/svnrdump-$end_rev\n+\n+\trm -rf t/repo;\n+\tmkdir t/repo;\n+\tsvnadmin create t/repo;\n+\n+\tsvnadmin load t/repo < \"$svnrdump_dump\" 1>\"$log_prefix.import.log\" 2>\"$log_prefix.import.error\";\n+\tif test $? -ne 0; then\n+\t\techo \"error: Load $end_rev failed. See $log_prefix.import.* for details\" >&2\n+\t\texit 1\n+\tfi\n+\techo \"Successfully loaded svnrdump-based repository for validation\"\n+\n+\techo \"Comparing canonical and svnrdump-based import logs...\"\n+\tdiff -au \"$svnadmin_dump_cut.import.log\" \"$log_prefix.import.log\" > t/import-diff.error;\n+\tif test $? -ne 0; then\n+\t\techo \"Validation failed. See t/import-diff.error for details.\" >&2\n+\t\texit 1\n+\tfi\n+\techo \"Validation successful\"\n+fi\n-- \n1.7.1\n"},{"id":"145498","messageId":"20100713235825.GC12639@debian","threadId":"24388","inReplyTo":"1279064176-6645-1-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 0/9] Get svnrdump merged into git.git","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-07-13T23:58:25Z","receivedAt":"2010-07-13T23:58:25Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nJust to clarify again, I'm doing this roundabout dance because a lot\nof other work in the git-remote-svn toolchain (svn-fe, a new dumpfile\nparser I'm writing, and a future branch/ tag mapper that some people\nare working on) is dependent on svnrdump; compiling the Subversion\ntrunk just for svnrdump is both inconvinient and non-trivial. It's\nalright if the git.git version of svnrdump rots for a few months\n(before a Subversion release includes it), as long as it works and\ndoesn't halt future work.\n\nThanks.\n\n-- Ram\n"},{"id":"145499","messageId":"20100714001530.GB2308@burratino","threadId":"24388","inReplyTo":"20100713235825.GC12639@debian","subject":"Re: [PATCH 0/9] Get svnrdump merged into git.git","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-07-14T00:15:30Z","receivedAt":"2010-07-14T00:15:30Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi Ram,\n\nSorry I missed this message before.\n\nRamkumar Ramachandra wrote:\n\n> It's\n> alright if the git.git version of svnrdump rots for a few months\n> (before a Subversion release includes it), as long as it works and\n> doesn't halt future work.\n\nTo clarify: is that because of API differences between svn 1.6 and\ntrunk?\n\nIf we are including a convenience copy of files that are (soon to be)\nin svn trunk, I don’t see much reason to diverge from the svn\nversion except for API differences and the overhead of updating too\nfrequently.\n"},{"id":"145500","messageId":"20100714002235.GF12639@debian","threadId":"24388","inReplyTo":"20100714001530.GB2308@burratino","subject":"Re: [PATCH 0/9] Get svnrdump merged into git.git","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-07-14T00:22:35Z","receivedAt":"2010-07-14T00:22:35Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Jonathan,\n\nJonathan Nieder writes:\n> If we are including a convenience copy of files that are (soon to be)\n> in svn trunk, I don’t see much reason to diverge from the svn\n> version except for API differences and the overhead of updating too\n> frequently.\n\nIs licensing an issue? I won't be the copyright holder or the sole\ncontributor to svnrdump when it's in ASF. Can I just copy-paste from\nthere into the git.git tree?\n\n-- Ram\n"},{"id":"145501","messageId":"20100714002843.GC2308@burratino","threadId":"24388","inReplyTo":"20100714002235.GF12639@debian","subject":"Re: [PATCH 0/9] Get svnrdump merged into git.git","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-07-14T00:28:43Z","receivedAt":"2010-07-14T00:28:43Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> Is licensing an issue? I won't be the copyright holder or the sole\n> contributor to svnrdump when it's in ASF. Can I just copy-paste from\n> there into the git.git tree?\n\nAs long as the relevant licenses permit that.  svnrdump only links to\nlibsvnclient, right?  So copy-pasting the code along with\n\n - a copy of the Apache license\n - prominent notices in any files that differ from the svn version\n - the notices from the Subversion NOTICE file\n\nshould be okay.\n"},{"id":"145502","messageId":"20100714004959.GG12639@debian","threadId":"24388","inReplyTo":"20100714002843.GC2308@burratino","subject":"Re: [PATCH 0/9] Get svnrdump merged into git.git","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-07-14T00:49:59Z","receivedAt":"2010-07-14T00:49:59Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nJonathan Nieder writes:\n> As long as the relevant licenses permit that.  svnrdump only links to\n> libsvnclient, right?\n\nI can see another problem coming: things are likely to move around\nquite a bit after the code gets into the ASF. For instance, Stefan's\nrecent review points out that write_hash_to_stringbuf should probably\nbe in a reusable library. If these changes happen soon, svnrdump will\nbe tightly integrated into Subversion, and it'll be hard to separate\nout an independent program. Then again, we don't know how soon this\nwill happen.\n\nIf you get this series merged into git.git, Git people will be able to\nread and review it easily and it'll be guaranteed to compile against\nSVN 1.6. In other words, it won't be a moving part. The downside being\nthat it's too painful to backport changes from the ASF version, so\nit'll remain outdated atleast until the next Subversion release, and\nwe don't know how soon that'll happen and be widely available.\n\nIf the former option is preferred, I'll send one large patch to the\nlist for mirroring the version in ASF. Also, could the Subversion\npeople confirm that licensing isn't an issue?\n\n-- Ram\n"},{"id":"145511","messageId":"20100714044712.GB3081@daniel3.local","threadId":"24388","inReplyTo":"1279064176-6645-2-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 1/9] Add LICENSE","fromName":"Daniel Shahaf","fromEmail":"d.s@daniel.shahaf.name","sentAt":"2010-07-14T04:47:12Z","receivedAt":"2010-07-14T04:47:12Z","isPatch":true,"sender":{"key":"d.s@daniel.shahaf.name","avatar":null},"body":"[ are all of these CCs really necessary?  that's another thing I almost never\nsee over at dev@svn ]\n\nRamkumar Ramachandra wrote on Wed, Jul 14, 2010 at 01:36:08 +0200:\n> License the project under a two-clause BSD-style license. A dual\n> license will be required later when attempting to merge into\n> Subversion.\n> \n\nIf it ends in svn trunk as subversion/svnrdump/ (and not as a tools/ utility),\nit's going to have to be licensed under the Apache License v2.\n\nDaniel\n(AFAIK)\n\n> Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n> ---\n>  LICENSE |   26 ++++++++++++++++++++++++++\n>  1 files changed, 26 insertions(+), 0 deletions(-)\n>  create mode 100644 LICENSE\n> \n> diff --git a/LICENSE b/LICENSE\n> new file mode 100644\n> index 0000000..4367b7c\n> --- /dev/null\n> +++ b/LICENSE\n> @@ -0,0 +1,26 @@\n> +Copyright (C) 2010 Ramkumar Ramachandra\n> +All rights reserved.\n> +\n> +Redistribution and use in source and binary forms, with or without\n> +modification, are permitted provided that the following conditions\n> +are met:\n> +1. Redistributions of source code must retain the above copyright\n> +   notice(s), this list of conditions and the following disclaimer\n> +   unmodified other than the allowable addition of one or more\n> +   copyright notices.\n> +2. Redistributions in binary form must reproduce the above copyright\n> +   notice(s), this list of conditions and the following disclaimer in\n> +   the documentation and/or other materials provided with the\n> +   distribution.\n> +\n> +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY\n> +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n> +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n> +PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE\n> +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n> +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n> +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\n> +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n> +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\n> +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n> +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n> -- \n> 1.7.1\n> \n"},{"id":"145518","messageId":"20100714070323.GB25630@jack.stsp.name","threadId":"24388","inReplyTo":"20100714004959.GG12639@debian","subject":"Re: [PATCH 0/9] Get svnrdump merged into git.git","fromName":"Stefan Sperling","fromEmail":"stsp@elego.de","sentAt":"2010-07-14T07:03:23Z","receivedAt":"2010-07-14T07:03:23Z","isPatch":true,"sender":{"key":"stsp@elego.de","avatar":"https://avatars.githubusercontent.com/u/9281333?v=4"},"body":"On Wed, Jul 14, 2010 at 02:49:59AM +0200, Ramkumar Ramachandra wrote:\n> Hi,\n> \n> Jonathan Nieder writes:\n> > As long as the relevant licenses permit that.  svnrdump only links to\n> > libsvnclient, right?\n\nIt links against a couple of Subversion libraries and against APR.\n\n> I can see another problem coming: things are likely to move around\n> quite a bit after the code gets into the ASF. For instance, Stefan's\n> recent review points out that write_hash_to_stringbuf should probably\n> be in a reusable library. If these changes happen soon, svnrdump will\n> be tightly integrated into Subversion, and it'll be hard to separate\n> out an independent program. Then again, we don't know how soon this\n> will happen.\n> \n> If you get this series merged into git.git, Git people will be able to\n> read and review it easily and it'll be guaranteed to compile against\n> SVN 1.6. In other words, it won't be a moving part. The downside being\n> that it's too painful to backport changes from the ASF version, so\n> it'll remain outdated atleast until the next Subversion release, and\n> we don't know how soon that'll happen and be widely available.\n> \n> If the former option is preferred, I'll send one large patch to the\n> list for mirroring the version in ASF.\n\nGit devs will want a version of svnrdump that compiles against\nthe 1.6.x libraries. We'll want a version that compiles against\nthe trunk (so we can release it with 1.7).\n\nSo depending on which APIs svnrdump is using, it's likely that\nyou'll have to maintain 2 slightly differing versions anyway,\nuntil 1.7 is released. (The current ETA for 1.7 is autumn, BTW).\n\n> Also, could the Subversion\n> people confirm that licensing isn't an issue?\n\nI didn't catch any licensing discussion. What's the issue?\n\nStefan\n"},{"id":"145526","messageId":"20100714112342.GB3496@debian","threadId":"24388","inReplyTo":"20100714044712.GB3081@daniel3.local","subject":"Re: [PATCH 1/9] Add LICENSE","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-07-14T11:23:42Z","receivedAt":"2010-07-14T11:23:42Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Daniel,\n\nDaniel Shahaf writes:\n> [ are all of these CCs really necessary?  that's another thing I almost never\n> see over at dev@svn ]\n\nIt's Git culture. Since our list is so high volume, many people aren't\nsubscribed to the list. Even those who are subscribed often cannot\nread all the emails. When I CC someone on a list email, I'm implicitly\ntelling them \"Please review this patch\" or \"Please comment on this\" or\nsimply \"You were involved with this work; just FYI, this is where it's\ngoing\". Most email clients have convinient markers to tell people if\nthey're marked on the To or Cc fields, or are simply receiving it\nbecause they're subscribed to a list.\n\n> If it ends in svn trunk as subversion/svnrdump/ (and not as a tools/ utility),\n> it's going to have to be licensed under the Apache License v2.\n\nYes, I know. PATCH v2 is licensed appropriately. My intial idea was to\ndual-license it: two-clause BSD for the git.git version and Apache for\nthe ASF version, but it seems like too much work now.\n\n-- Ram\n"},{"id":"145527","messageId":"20100714112620.GC3496@debian","threadId":"24388","inReplyTo":"20100714070323.GB25630@jack.stsp.name","subject":"Re: [PATCH 0/9] Get svnrdump merged into git.git","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-07-14T11:26:20Z","receivedAt":"2010-07-14T11:26:20Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Stefan,\n\nStefan Sperling writes:\n> Git devs will want a version of svnrdump that compiles against\n> the 1.6.x libraries. We'll want a version that compiles against\n> the trunk (so we can release it with 1.7).\n> \n> So depending on which APIs svnrdump is using, it's likely that\n> you'll have to maintain 2 slightly differing versions anyway,\n> until 1.7 is released. (The current ETA for 1.7 is autumn, BTW).\n\nRight, that's alright. I'll just have a commit that I'll keep `rebase\n--onto`ing to have the 1.6-specific changes.\n\n> > Also, could the Subversion\n> > people confirm that licensing isn't an issue?\n> \n> I didn't catch any licensing discussion. What's the issue?\n\nI guess there's no issue then- sorry, I know close to nothing about\nlicensing.\n\n-- Ram\n"},{"id":"145533","messageId":"20100714125505.GF25630@jack.stsp.name","threadId":"24388","inReplyTo":"20100714112620.GC3496@debian","subject":"Re: [PATCH 0/9] Get svnrdump merged into git.git","fromName":"Stefan Sperling","fromEmail":"stsp@elego.de","sentAt":"2010-07-14T12:55:05Z","receivedAt":"2010-07-14T12:55:05Z","isPatch":true,"sender":{"key":"stsp@elego.de","avatar":"https://avatars.githubusercontent.com/u/9281333?v=4"},"body":"On Wed, Jul 14, 2010 at 01:26:20PM +0200, Ramkumar Ramachandra wrote:\n> Stefan Sperling writes:\n> > I didn't catch any licensing discussion. What's the issue?\n> \n> I guess there's no issue then- sorry, I know close to nothing about\n> licensing.\n\nWell, the only theoretical issue I can see is that the FSF says that\nthe GPLv2 was incompatible with the Apache 2.0 licence.\nSee http://www.apache.org/licenses/GPL-compatibility.html\n\nSo if git distributed their own version of svnrdump licensed under GPLv2,\ndepending on who you believe, distributors of Git binaries would violate\nthe GPLv2 by linking svnrdump to the Subversion libraries.\n\nMercurial recently switched to GPLv3 for this reason. They have code\nthat uses Subversion's Python bindings. They made this switch on their\nown accord, however, after consulting the Software Freedom Law Center.\nThe Subversion project itself was not involved in that decision.\n\nPossible workarounds are simply ignoring the FSF, or distributing all\ncopies of svnrdump under Apache 2.0 or GPLv3 (but the svnrdump included\nin Subversion itself must be licensed under Apache 2.0).\n\nStefan\n"},{"id":"145540","messageId":"7vsk3myuyo.fsf@alter.siamese.dyndns.org","threadId":"24388","inReplyTo":"1279064176-6645-1-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 0/9] Get svnrdump merged into git.git","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-07-14T14:54:55Z","receivedAt":"2010-07-14T14:54:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ramkumar Ramachandra <artagnon@gmail.com> writes:\n\n> For the convinience of fellow Git developers, here's a series to get\n> svnrdump merged into git.git.\n\nGiven you'll be maintaining a variant that can link with 1.6 subversion\nlibraries anyway, I'd rather see you host this as an independent, related\nproject, perhaps at repo.or.cz and/or github.com, and add a link to point\nat it at https://git.wiki.kernel.org/index.php/InterfacesFrontendsAndTools\n\nThanks.\n"},{"id":"145618","messageId":"20100715105527.GC22574@debian","threadId":"24388","inReplyTo":"7vsk3myuyo.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 0/9] Get svnrdump merged into git.git","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-07-15T10:55:27Z","receivedAt":"2010-07-15T10:55:27Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Junio,\n\nJunio C Hamano writes:\n> Given you'll be maintaining a variant that can link with 1.6 subversion\n> libraries anyway, I'd rather see you host this as an independent, related\n> project, perhaps at repo.or.cz and/or github.com, and add a link to point\n> at it at https://git.wiki.kernel.org/index.php/InterfacesFrontendsAndTools\n\nI think that sorts it out then. Thanks. I've merged this series into a\nnew project on my GitHub [1]. I'll soon convert whitespace/ style and\nadd some instructions, and maintain a branch that compiles against\nlibsvn 1.6. Until svnrdump is in a re-usable library in the ASF, I'm\nafraid the git-remote-svn toolchain will simply have to spawn the\nexecutable and read the output from a pipe. Instructions can then be\nsimplified to: compile `svnrdump` and place the executable anywhere in\nyour $PATH.\n\n[1]: http://github.com/artagnon/svnrdump\n\n-- Ram\n"},{"id":"147332","messageId":"20100806183736.GA2985@burratino","threadId":"24388","inReplyTo":"20100806175709.GA2683@burratino","subject":"Re: [PATCH svnrdump-standalone] Sync with upstream","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-06T18:37:36Z","receivedAt":"2010-08-06T18:37:36Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Jonathan Nieder wrote:\n\n> Delete all files and copy in newer versions from Subversion\n> upstream (http://svn.apache.org/repos/asf/subversion/trunk@982862).\n\nHere’s the “while at it” part:\n\n> While at it, port for an out-of-tree build against svn 1.6:\n> \n>  - include a stripped-down version of the autoconf-generated\n>    Makefile for Linux and a .gitignore file listing build\n>    products;\n> \n>  - use apr_hash_clear directly instead of svn_hash__clear\n>    (since the latter’s signature has changed);\n> \n>  - build and use an svn17_compat module for functions used\n>    that were added in Subversion 1.7.\n> \n> In theory, the svn17_compat module could shrink over time\n> as simpler compatibility shims are devised.\n>\n> The result builds and all dump tests pass.\n>\n> Caveats: Most load tests do not pass.  Chances are this will only\n> build against libsvn 1.6, not 1.7, since I did not do the appropriate\n> identifier-renaming dance.\n\nMost of this patch is Subversion code.\n\nThe rest, which is my own doing, I place in the public domain.\nYou may freely use, modify, distribute, and relicense it.\n\n .gitignore      |    5 +\n Makefile        |   35 +++\n dump_editor.c   |    9 +-\n svn17_compat.c  |  842 +++++++++++++++++++++++++++++++++++++++++++++++++++++++\n svn17_compat.h  |  179 ++++++++++++\n svnrdump.c      |    5 +-\n svntest/main.py |   17 +-\n 7 files changed, 1076 insertions(+), 16 deletions(-)\n create mode 100644 .gitignore\n create mode 100644 Makefile\n create mode 100644 svn17_compat.c\n create mode 100644 svn17_compat.h\n\ndiff --git a/.gitignore b/.gitignore\nnew file mode 100644\nindex 0000000..af251b1\n--- /dev/null\n+++ b/.gitignore\n@@ -0,0 +1,5 @@\n+*.lo\n+*.o\n+*.pyc\n+/svn-test-work/\n+/svnrdump\ndiff --git a/Makefile b/Makefile\nnew file mode 100644\nindex 0000000..b022411\n--- /dev/null\n+++ b/Makefile\n@@ -0,0 +1,35 @@\n+CC=gcc\n+PYTHON=python\n+LIBTOOL=libtool\n+LTFLAGS=--tag=CC --silent\n+CFLAGS=-g -O2 -pthread -Wall -Werror=implicit-function-declaration\n+EXEEXT=\n+CPPFLAGS=-DLINUX=2 -D_REENTRANT -D_GNU_SOURCE -D_LARGEFILE64_SOURCE\n+LDFLAGS=\n+COMPILE=$(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDES)\n+LINK=$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) $(CFLAGS)\n+LT_COMPILE=$(LIBTOOL) $(LTFLAGS) --mode=compile $(COMPILE) $(CFLAGS)\n+\n+INCLUDES=-I/usr/include/subversion-1 -I/usr/include/apr-1.0\n+LIBS=-lsvn_client-1 -lsvn_ra-1 -lsvn_repos-1 -lsvn_delta-1 -lsvn_subr-1 -lapr-1\n+OBJECTS=dump_editor.lo load_editor.lo svnrdump.lo svn17_compat.lo\n+\n+.SUFFIXES: .c .lo\n+\n+svnrdump$(EXEEXT): $(OBJECTS)\n+\t$(LINK) $(LDFLAGS) -o svnrdump$(EXEEXT) $(OBJECTS) $(LIBS)\n+\n+.c.lo:\n+\t$(LT_COMPILE) -o $@ -c $<\n+\n+dump_editor.lo: dump_editor.c dump_editor.h svn17_compat.h\n+load_editor.lo: load_editor.c load_editor.h\n+svnrdump.lo: svnrdump.c dump_editor.h load_editor.h svn17_compat.h\n+svn17_compat.lo: svn17_compat.c svn17_compat.h\n+\n+check: svnrdump$(EXEEXT) svnrdump_tests.py\n+\t$(PYTHON) svnrdump_tests.py\n+\n+clean:\n+\t$(RM) svnrdump$(EXEEXT)\n+\t$(RM) *.lo *.o\ndiff --git a/dump_editor.c b/dump_editor.c\nindex dac9d15..72ad04f 100644\n--- a/dump_editor.c\n+++ b/dump_editor.c\n@@ -29,6 +29,7 @@\n #include \"svn_props.h\"\n #include \"svn_dirent_uri.h\"\n \n+#include \"svn17_compat.h\"\n #include \"dump_editor.h\"\n \n #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))\n@@ -166,8 +167,8 @@ dump_props(struct dump_edit_baton *eb,\n       SVN_ERR(svn_stream_printf(eb->stream, pool, \"\\n\\n\"));\n \n       /* Cleanup so that data is never dumped twice. */\n-      svn_hash__clear(eb->props, pool);\n-      svn_hash__clear(eb->deleted_props, pool);\n+      apr_hash_clear(eb->props);\n+      apr_hash_clear(eb->deleted_props);\n       if (trigger_var)\n         *trigger_var = FALSE;\n     }\n@@ -711,8 +712,8 @@ close_file(void *file_baton,\n \n       /* Cleanup */\n       eb->dump_props = eb->dump_props_pending = FALSE;\n-      svn_hash__clear(eb->props, pool);\n-      svn_hash__clear(eb->deleted_props, pool);\n+      apr_hash_clear(eb->props);\n+      apr_hash_clear(eb->deleted_props);\n     }\n \n   /* Dump the text */\ndiff --git a/svn17_compat.c b/svn17_compat.c\nnew file mode 100644\nindex 0000000..e6d8774\n--- /dev/null\n+++ b/svn17_compat.c\n@@ -0,0 +1,842 @@\n+/*\n+ * svn17_compat.c :   a library to make Subversion 1.6 look like 1.7.\n+ *\n+ * ====================================================================\n+ *    This file is derived from code licensed to the Apache\n+ *    Software Foundation (ASF) under one or more contributor\n+ *    license agreements.  See the NOTICE file distributed with\n+ *    this file for additional information regarding copyright\n+ *    ownership.  The ASF licenses those portions to you under\n+ *    the Apache License, Version 2.0 (the \"License\"); you may\n+ *    not use those portions except in compliance with the\n+ *    License.  You may obtain a copy of the License at\n+ *\n+ *       http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ *    Unless required by applicable law or agreed to in writing,\n+ *    software distributed under the License is distributed on an\n+ *    \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n+ *    KIND, either express or implied.  See the License for the\n+ *    specific language governing permissions and limitations\n+ *    under the License.\n+ *\n+ *    Any code in this file not licensed from the ASF is\n+ *    original code in the public domain.  You may freely use,\n+ *    modify, distribute, and relicense such code.\n+ * ====================================================================\n+ */\n+\f\n+#include <stdlib.h>\n+#include <string.h>\n+#include <assert.h>\n+\n+#include <apr.h>\n+#include <apr_strings.h>\n+#include <apr_hash.h>\n+#include <apr_lib.h>\n+#include <apr_errno.h>\n+#include <apr_pools.h>\n+\n+#include <svn_error.h>\n+#include <svn_config.h>\n+#include <svn_io.h>\n+#include <svn_dirent_uri.h>\n+#include <svn_path.h>\n+#include \"svn17_compat.h\"\n+\n+/* TRUE if s is the canonical empty path, FALSE otherwise\n+   From libsvn_subr/dirent_uri.c. */\n+#define SVN_PATH_IS_EMPTY(s) ((s)[0] == '\\0')\n+\n+/* Path type definition. Used only by internal functions.\n+   From libsvn_subr/dirent_uri.c. */\n+typedef enum {\n+  type_uri,\n+  type_dirent,\n+  type_relpath\n+} path_type_t;\n+\n+/* Here is the BNF for path components in a URI. \"pchar\" is a\n+   character in a path component.\n+\n+      pchar       = unreserved | escaped |\n+                    \":\" | \"@\" | \"&\" | \"=\" | \"+\" | \"$\" | \",\"\n+      unreserved  = alphanum | mark\n+      mark        = \"-\" | \"_\" | \".\" | \"!\" | \"~\" | \"*\" | \"'\" | \"(\" | \")\"\n+\n+   Note that \"escaped\" doesn't really apply to what users can put in\n+   their paths, so that really means the set of characters is:\n+\n+      alphanum | mark | \":\" | \"@\" | \"&\" | \"=\" | \"+\" | \"$\" | \",\"\n+\n+   From libsvn_subr/path.c. */\n+static const char svn_uri__char_validity[256] = {\n+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,\n+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,\n+  0, 1, 0, 0, 1, 0, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,\n+  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 0, 0, 1, 0, 0,\n+\n+  /* 64 */\n+  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,\n+  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 0, 0, 0, 0, 1,\n+  0, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,\n+  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 0, 0, 0, 1, 0,\n+\n+  /* 128 */\n+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,\n+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,\n+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,\n+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,\n+\n+  /* 192 */\n+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,\n+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,\n+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,\n+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,\n+};\n+\n+/* From libsvn_subr/dirent_uri.c. */\n+static svn_boolean_t\n+svn_uri_is_canonical(const char *uri, apr_pool_t *pool)\n+{\n+  const char *ptr = uri, *seg = uri;\n+  const char *schema_data = NULL;\n+\n+  /* URI is canonical if it has:\n+   *  - no '.' segments\n+   *  - no closing '/', unless for the root path '/' itself\n+   *  - no '//'\n+   *  - lowercase URL scheme\n+   *  - lowercase URL hostname\n+   */\n+\n+  if (*uri == '\\0')\n+    return TRUE;\n+\n+  /* Maybe parse hostname and scheme. */\n+  if (*ptr != '/')\n+    {\n+      while (*ptr && (*ptr != '/') && (*ptr != ':'))\n+        ptr++;\n+\n+      if (*ptr == ':' && *(ptr+1) == '/' && *(ptr+2) == '/')\n+        {\n+          /* Found a scheme, check that it's all lowercase. */\n+          ptr = uri;\n+          while (*ptr != ':')\n+            {\n+              if (*ptr >= 'A' && *ptr <= 'Z')\n+                return FALSE;\n+              ptr++;\n+            }\n+          /* Skip :// */\n+          ptr += 3;\n+\n+          /* This might be the hostname */\n+          seg = ptr;\n+          while (*ptr && (*ptr != '/') && (*ptr != '@'))\n+            ptr++;\n+\n+          if (! *ptr)\n+            return TRUE;\n+\n+          if (*ptr == '@')\n+            seg = ptr + 1;\n+\n+          /* Found a hostname, check that it's all lowercase. */\n+          ptr = seg;\n+          while (*ptr && *ptr != '/')\n+            {\n+              if (*ptr >= 'A' && *ptr <= 'Z')\n+                return FALSE;\n+              ptr++;\n+            }\n+\n+          schema_data = ptr;\n+        }\n+      else\n+        {\n+          /* Didn't find a scheme; finish the segment. */\n+          while (*ptr && *ptr != '/')\n+            ptr++;\n+        }\n+    }\n+\n+#ifdef SVN_USE_DOS_PATHS\n+  if (schema_data && *ptr == '/')\n+    {\n+      /* If this is a file url, ptr now points to the third '/' in\n+         file:///C:/path. Check that if we have such a URL the drive\n+         letter is in uppercase. */\n+      if (strncmp(uri, \"file:\", 5) == 0 &&\n+          ! (*(ptr+1) >= 'A' && *(ptr+1) <= 'Z') &&\n+          *(ptr+2) == ':')\n+        return FALSE;\n+    }\n+#endif /* SVN_USE_DOS_PATHS */\n+\n+  /* Now validate the rest of the URI. */\n+  while(1)\n+    {\n+      apr_size_t seglen = ptr - seg;\n+\n+      if (seglen == 1 && *seg == '.')\n+        return FALSE;  /*  /./   */\n+\n+      if (*ptr == '/' && *(ptr+1) == '/')\n+        return FALSE;  /*  //    */\n+\n+      if (! *ptr && *(ptr - 1) == '/' && ptr - 1 != uri)\n+        return FALSE;  /* foo/  */\n+\n+      if (! *ptr)\n+        break;\n+\n+      if (*ptr == '/')\n+        ptr++;\n+      seg = ptr;\n+\n+\n+      while (*ptr && (*ptr != '/'))\n+        ptr++;\n+    }\n+\n+  if (schema_data)\n+    {\n+      ptr = schema_data;\n+\n+      while (*ptr)\n+        {\n+          if (*ptr == '%')\n+            {\n+              char digitz[3];\n+              int val;\n+\n+              /* Can't use apr_isxdigit() because lower case letters are\n+                 not in our canonical format */\n+              if (((*(ptr+1) < '0' || (*ptr+1) > '9'))\n+                  && (*(ptr+1) < 'A' || (*ptr+1) > 'F'))\n+                return FALSE;\n+              else if (((*(ptr+2) < '0' || (*ptr+2) > '9'))\n+                  && (*(ptr+2) < 'A' || (*ptr+2) > 'F'))\n+                return FALSE;\n+\n+              digitz[0] = *(++ptr);\n+              digitz[1] = *(++ptr);\n+              digitz[2] = '\\0';\n+              val = (int)strtol(digitz, NULL, 16);\n+\n+              if (svn_uri__char_validity[val])\n+                return FALSE; /* Should not have been escaped */\n+            }\n+          else if (*ptr != '/' && !svn_uri__char_validity[(unsigned char)*ptr])\n+            return FALSE; /* Character should have been escaped */\n+          ptr++;\n+        }\n+    }\n+\n+  return TRUE;\n+}\n+\n+/* From libsvn_subr/dirent_uri.c. */\n+static svn_boolean_t\n+svn_uri_is_absolute(const char *uri)\n+{\n+  /* uri is absolute if it starts with '/' */\n+  if (uri && uri[0] == '/')\n+    return TRUE;\n+\n+  /* URLs are absolute. */\n+  return svn_path_is_url(uri);\n+}\n+\n+/* Calculates the length occupied by the schema defined root of URI\n+   From libsvn_subr/dirent_uri.c. */\n+static apr_size_t\n+uri_schema_root_length(const char *uri, apr_size_t len)\n+{\n+  apr_size_t i;\n+\n+  for (i = 0; i < len; i++)\n+    {\n+      if (uri[i] == '/')\n+        {\n+          if (i > 0 && uri[i-1] == ':' && i < len-1 && uri[i+1] == '/')\n+            {\n+              /* We have an absolute uri */\n+              if (i == 5 && strncmp(\"file\", uri, 4) == 0)\n+                return 7; /* file:// */\n+              else\n+                {\n+                  for (i += 2; i < len; i++)\n+                    if (uri[i] == '/')\n+                      return i;\n+\n+                  return len; /* Only a hostname is found */\n+                }\n+            }\n+          else\n+            return 0;\n+        }\n+    }\n+\n+  return 0;\n+}\n+\n+/* From libsvn_subr/dirent_uri.c. */\n+char *\n+svn_uri_join(const char *base, const char *component, apr_pool_t *pool)\n+{\n+  apr_size_t blen = strlen(base);\n+  apr_size_t clen = strlen(component);\n+  char *path;\n+\n+  assert(svn_uri_is_canonical(base, pool));\n+  assert(svn_uri_is_canonical(component, pool));\n+\n+  /* If either is empty return the other */\n+  if (SVN_PATH_IS_EMPTY(base))\n+    return apr_pmemdup(pool, component, clen + 1);\n+  if (SVN_PATH_IS_EMPTY(component))\n+    return apr_pmemdup(pool, base, blen + 1);\n+\n+  /* If the component is absolute, then return it.  */\n+  if (svn_uri_is_absolute(component))\n+    {\n+      if (*component != '/')\n+        return apr_pmemdup(pool, component, clen + 1);\n+      else\n+        {\n+          /* The uri is not absolute enough; use only the root from base */\n+          apr_size_t n = uri_schema_root_length(base, blen);\n+\n+          path = apr_palloc(pool, n + clen + 1);\n+\n+          if (n > 0)\n+            memcpy(path, base, n);\n+\n+          memcpy(path + n, component, clen + 1); /* Include '\\0' */\n+\n+          return path;\n+        }\n+    }\n+\n+  if (blen == 1 && base[0] == '/')\n+    blen = 0; /* Ignore base, just return separator + component */\n+\n+  /* Construct the new, combined path. */\n+  path = apr_palloc(pool, blen + 1 + clen + 1);\n+  memcpy(path, base, blen);\n+  path[blen] = '/';\n+  memcpy(path + blen + 1, component, clen + 1);\n+\n+  return path;\n+}\n+\n+/* From libsvn_subr/dirent_uri.c. */\n+static svn_boolean_t\n+svn_relpath_is_canonical(const char *relpath,\n+                         apr_pool_t *pool)\n+{\n+  const char *ptr = relpath, *seg = relpath;\n+\n+  /* RELPATH is canonical if it has:\n+   *  - no '.' segments\n+   *  - no start and closing '/'\n+   *  - no '//'\n+   */\n+\n+  if (*relpath == '\\0')\n+    return TRUE;\n+\n+  if (*ptr == '/')\n+    return FALSE;\n+\n+  /* Now validate the rest of the path. */\n+  while(1)\n+    {\n+      apr_size_t seglen = ptr - seg;\n+\n+      if (seglen == 1 && *seg == '.')\n+        return FALSE;  /*  /./   */\n+\n+      if (*ptr == '/' && *(ptr+1) == '/')\n+        return FALSE;  /*  //    */\n+\n+      if (! *ptr && *(ptr - 1) == '/')\n+        return FALSE;  /* foo/  */\n+\n+      if (! *ptr)\n+        break;\n+\n+      if (*ptr == '/')\n+        ptr++;\n+      seg = ptr;\n+\n+      while (*ptr && (*ptr != '/'))\n+        ptr++;\n+    }\n+\n+  return TRUE;\n+}\n+\n+/* From libsvn_subr/dirent_uri.c. */\n+const char *\n+svn_relpath_basename(const char *relpath,\n+                     apr_pool_t *pool)\n+{\n+  apr_size_t len = strlen(relpath);\n+  apr_size_t start;\n+\n+  assert(!pool || svn_relpath_is_canonical(relpath, pool));\n+\n+  start = len;\n+  while (start > 0 && relpath[start - 1] != '/')\n+    --start;\n+\n+  if (pool)\n+    return apr_pstrmemdup(pool, relpath + start, len - start);\n+  else\n+    return relpath + start;\n+}\n+\n+/* From libsvn_subr/dirent_uri.c. */\n+const char *\n+svn_dirent_basename(const char *dirent, apr_pool_t *pool)\n+{\n+  apr_size_t len = strlen(dirent);\n+  apr_size_t start;\n+\n+  assert(!pool || svn_dirent_is_canonical(dirent, pool));\n+\n+  if (svn_dirent_is_root(dirent, len))\n+    return \"\";\n+  else\n+    {\n+      start = len;\n+      while (start > 0 && dirent[start - 1] != '/'\n+#ifdef SVN_USE_DOS_PATHS\n+             && dirent[start - 1] != ':'\n+#endif\n+            )\n+        --start;\n+    }\n+\n+  if (pool)\n+    return apr_pstrmemdup(pool, dirent + start, len - start);\n+  else\n+    return dirent + start;\n+}\n+\n+/* Locale insensitive tolower() for converting parts of dirents and urls\n+   while canonicalizing.  From libsvn_subr/dirent_uri.c. */\n+static char\n+canonicalize_to_lower(char c)\n+{\n+  if (c < 'A' || c > 'Z')\n+    return c;\n+  else\n+    return c - 'A' + 'a';\n+}\n+\n+/* Locale insensitive toupper() for converting parts of dirents and urls\n+   while canonicalizing.  From libsvn_subr/dirent_uri.c. */\n+static char\n+canonicalize_to_upper(char c)\n+{\n+  if (c < 'a' || c > 'z')\n+    return c;\n+  else\n+    return c - 'a' + 'A';\n+}\n+\n+\n+/* Return the canonicalized version of PATH, of type TYPE, allocated in\n+ * POOL.  From libsvn_subr/dirent_uri.c.\n+ */\n+static const char *\n+canonicalize(path_type_t type, const char *path, apr_pool_t *pool)\n+{\n+  char *canon, *dst;\n+  const char *src;\n+  apr_size_t seglen;\n+  apr_size_t schemelen = 0;\n+  apr_size_t canon_segments = 0;\n+  svn_boolean_t url = FALSE;\n+  char *schema_data = NULL;\n+\n+  /* \"\" is already canonical, so just return it; note that later code\n+     depends on path not being zero-length.  */\n+  if (SVN_PATH_IS_EMPTY(path))\n+    return \"\";\n+\n+  dst = canon = apr_pcalloc(pool, strlen(path) + 1);\n+\n+  /* If this is supposed to be an URI and it starts with \"scheme://\", then\n+     copy the scheme, host name, etc. to DST and set URL = TRUE. */\n+  src = path;\n+  if (type == type_uri && *src != '/')\n+    {\n+      while (*src && (*src != '/') && (*src != ':'))\n+        src++;\n+\n+      if (*src == ':' && *(src+1) == '/' && *(src+2) == '/')\n+        {\n+          const char *seg;\n+\n+          url = TRUE;\n+\n+          /* Found a scheme, convert to lowercase and copy to dst. */\n+          src = path;\n+          while (*src != ':')\n+            {\n+              *(dst++) = canonicalize_to_lower((*src++));\n+              schemelen++;\n+            }\n+          *(dst++) = ':';\n+          *(dst++) = '/';\n+          *(dst++) = '/';\n+          src += 3;\n+          schemelen += 3;\n+\n+          /* This might be the hostname */\n+          seg = src;\n+          while (*src && (*src != '/') && (*src != '@'))\n+            src++;\n+\n+          if (*src == '@')\n+            {\n+              /* Copy the username & password. */\n+              seglen = src - seg + 1;\n+              memcpy(dst, seg, seglen);\n+              dst += seglen;\n+              src++;\n+            }\n+          else\n+            src = seg;\n+\n+          /* Found a hostname, convert to lowercase and copy to dst. */\n+          while (*src && (*src != '/'))\n+            *(dst++) = canonicalize_to_lower((*src++));\n+\n+          /* Copy trailing slash, or null-terminator. */\n+          *(dst) = *(src);\n+\n+          /* Move src and dst forward only if we are not\n+           * at null-terminator yet. */\n+          if (*src)\n+            {\n+              src++;\n+              dst++;\n+              schema_data = dst;\n+            }\n+\n+          canon_segments = 1;\n+        }\n+    }\n+\n+  /* Copy to DST any separator or drive letter that must come before the\n+     first regular path segment. */\n+  if (! url && type != type_relpath)\n+    {\n+      src = path;\n+      /* If this is an absolute path, then just copy over the initial\n+         separator character. */\n+      if (*src == '/')\n+        {\n+          *(dst++) = *(src++);\n+\n+#ifdef SVN_USE_DOS_PATHS\n+          /* On Windows permit two leading separator characters which means an\n+           * UNC path. */\n+          if ((type == type_dirent) && *src == '/')\n+            *(dst++) = *(src++);\n+#endif /* SVN_USE_DOS_PATHS */\n+        }\n+#ifdef SVN_USE_DOS_PATHS\n+      /* On Windows the first segment can be a drive letter, which we normalize\n+         to upper case. */\n+      else if (type == type_dirent &&\n+               ((*src >= 'a' && *src <= 'z') ||\n+                (*src >= 'A' && *src <= 'Z')) &&\n+               (src[1] == ':'))\n+        {\n+          *(dst++) = canonicalize_to_upper(*(src++));\n+          /* Leave the ':' to be processed as (or as part of) a path segment\n+             by the following code block, so we need not care whether it has\n+             a slash after it. */\n+        }\n+#endif /* SVN_USE_DOS_PATHS */\n+    }\n+\n+  while (*src)\n+    {\n+      /* Parse each segment, find the closing '/' */\n+      const char *next = src;\n+      while (*next && (*next != '/'))\n+        ++next;\n+\n+      seglen = next - src;\n+\n+      if (seglen == 0 || (seglen == 1 && src[0] == '.'))\n+        {\n+          /* Noop segment, so do nothing. */\n+        }\n+#ifdef SVN_USE_DOS_PATHS\n+      /* If this is the first path segment of a file:// URI and it contains a\n+         windows drive letter, convert the drive letter to upper case. */\n+      else if (url && canon_segments == 1 && seglen == 2 &&\n+               (strncmp(canon, \"file:\", 5) == 0) &&\n+               src[0] >= 'a' && src[0] <= 'z' && src[1] == ':')\n+        {\n+          *(dst++) = canonicalize_to_upper(src[0]);\n+          *(dst++) = ':';\n+          if (*next)\n+            *(dst++) = *next;\n+          canon_segments++;\n+        }\n+#endif /* SVN_USE_DOS_PATHS */\n+      else\n+        {\n+          /* An actual segment, append it to the destination path */\n+          if (*next)\n+            seglen++;\n+          memcpy(dst, src, seglen);\n+          dst += seglen;\n+          canon_segments++;\n+        }\n+\n+      /* Skip over trailing slash to the next segment. */\n+      src = next;\n+      if (*src)\n+        src++;\n+    }\n+\n+  /* Remove the trailing slash if there was at least one\n+   * canonical segment and the last segment ends with a slash.\n+   *\n+   * But keep in mind that, for URLs, the scheme counts as a\n+   * canonical segment -- so if path is ONLY a scheme (such\n+   * as \"https://\") we should NOT remove the trailing slash. */\n+  if ((canon_segments > 0 && *(dst - 1) == '/')\n+      && ! (url && path[schemelen] == '\\0'))\n+    {\n+      dst --;\n+    }\n+\n+  *dst = '\\0';\n+\n+#ifdef SVN_USE_DOS_PATHS\n+  /* Skip leading double slashes when there are less than 2\n+   * canon segments. UNC paths *MUST* have two segments. */\n+  if ((type == type_dirent) && canon[0] == '/' && canon[1] == '/')\n+    {\n+      if (canon_segments < 2)\n+        return canon + 1;\n+      else\n+        {\n+          /* Now we're sure this is a valid UNC path, convert the server name\n+             (the first path segment) to lowercase as Windows treats it as case\n+             insensitive.\n+             Note: normally the share name is treated as case insensitive too,\n+             but it seems to be possible to configure Samba to treat those as\n+             case sensitive, so better leave that alone. */\n+          dst = canon + 2;\n+          while (*dst && *dst != '/')\n+            *(dst++) = canonicalize_to_lower(*dst);\n+        }\n+    }\n+#endif /* SVN_USE_DOS_PATHS */\n+\n+  /* Check the normalization of characters in a uri */\n+  if (schema_data)\n+    {\n+      int need_extra = 0;\n+      src = schema_data;\n+\n+      while (*src)\n+        {\n+          switch (*src)\n+            {\n+              case '/':\n+                break;\n+              case '%':\n+                if (!apr_isxdigit(*(src+1)) || !apr_isxdigit(*(src+2)))\n+                  need_extra += 2;\n+                else\n+                  src += 2;\n+                break;\n+              default:\n+                if (!svn_uri__char_validity[(unsigned char)*src])\n+                  need_extra += 2;\n+                break;\n+            }\n+          src++;\n+        }\n+\n+      if (need_extra > 0)\n+        {\n+          apr_size_t pre_schema_size = (apr_size_t)(schema_data - canon);\n+\n+          dst = apr_palloc(pool, (apr_size_t)(src - canon) + need_extra + 1);\n+          memcpy(dst, canon, pre_schema_size);\n+          canon = dst;\n+\n+          dst += pre_schema_size;\n+        }\n+      else\n+        dst = schema_data;\n+\n+      src = schema_data;\n+\n+      while (*src)\n+        {\n+          switch (*src)\n+            {\n+              case '/':\n+                *(dst++) = '/';\n+                break;\n+              case '%':\n+                if (!apr_isxdigit(*(src+1)) || !apr_isxdigit(*(src+2)))\n+                  {\n+                    *(dst++) = '%';\n+                    *(dst++) = '2';\n+                    *(dst++) = '5';\n+                  }\n+                else\n+                  {\n+                    char digitz[3];\n+                    int val;\n+\n+                    digitz[0] = *(++src);\n+                    digitz[1] = *(++src);\n+                    digitz[2] = 0;\n+\n+                    val = (int)strtol(digitz, NULL, 16);\n+\n+                    if (svn_uri__char_validity[(unsigned char)val])\n+                      *(dst++) = (char)val;\n+                    else\n+                      {\n+                        *(dst++) = '%';\n+                        *(dst++) = canonicalize_to_upper(digitz[0]);\n+                        *(dst++) = canonicalize_to_upper(digitz[1]);\n+                      }\n+                  }\n+                break;\n+              default:\n+                if (!svn_uri__char_validity[(unsigned char)*src])\n+                  {\n+                    apr_snprintf(dst, 4, \"%%%02X\", (unsigned char)*src);\n+                    dst += 3;\n+                  }\n+                else\n+                  *(dst++) = *src;\n+                break;\n+            }\n+          src++;\n+        }\n+      *dst = '\\0';\n+    }\n+\n+  return canon;\n+}\n+\n+/* From libsvn_subr/dirent_uri.c. */\n+const char *\n+svn_uri_canonicalize(const char *uri, apr_pool_t *pool)\n+{\n+  return canonicalize(type_uri, uri, pool);\n+}\n+\n+/* New code (public domain). */\n+svn_error_t *\n+svn_io_remove_file2(const char *path,\n+                   svn_boolean_t ignore_enoent,\n+                   apr_pool_t *scratch_pool)\n+{\n+  svn_error_t *err = svn_io_remove_file(path, scratch_pool);\n+  if (ignore_enoent && err && APR_STATUS_IS_ENOENT(err->apr_err))\n+    {\n+      svn_error_clear(err);\n+      return SVN_NO_ERROR;\n+    }\n+  return err;\n+}\n+\n+/* From libsvn_subr/cmdline.c. */\n+svn_error_t *\n+svn_cmdline__apply_config_options(apr_hash_t *config,\n+                                  const apr_array_header_t *config_options,\n+                                  const char *prefix,\n+                                  const char *argument_name)\n+{\n+  int i;\n+\n+  for (i = 0; i < config_options->nelts; i++)\n+   {\n+     svn_config_t *cfg;\n+     svn_cmdline__config_argument_t *arg =\n+                          APR_ARRAY_IDX(config_options, i,\n+                                        svn_cmdline__config_argument_t *);\n+\n+     cfg = apr_hash_get(config, arg->file, APR_HASH_KEY_STRING);\n+\n+     if (cfg)\n+       {\n+         svn_config_set(cfg, arg->section, arg->option, arg->value);\n+       }\n+     else\n+       {\n+         svn_error_t *err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,\n+             _(\"Unrecognized file in argument of %s\"), argument_name);\n+\n+         svn_handle_warning2(stderr, err, prefix);\n+         svn_error_clear(err);\n+       }\n+    }\n+\n+  return SVN_NO_ERROR;\n+}\n+\n+/* From libsvn_subr/cmdline.c. */\n+svn_error_t *\n+svn_cmdline__parse_config_option(apr_array_header_t *config_options,\n+                                 const char *opt_arg,\n+                                 apr_pool_t *pool)\n+{\n+  svn_cmdline__config_argument_t *config_option;\n+  const char *first_colon, *second_colon, *equals_sign;\n+  apr_size_t len = strlen(opt_arg);\n+  if ((first_colon = strchr(opt_arg, ':')) && (first_colon != opt_arg))\n+    {\n+      if ((second_colon = strchr(first_colon + 1, ':')) &&\n+          (second_colon != first_colon + 1))\n+        {\n+          if ((equals_sign = strchr(second_colon + 1, '=')) &&\n+              (equals_sign != second_colon + 1))\n+            {\n+              config_option = apr_pcalloc(pool, sizeof(*config_option));\n+              config_option->file = apr_pstrndup(pool, opt_arg,\n+                                                 first_colon - opt_arg);\n+              config_option->section = apr_pstrndup(pool, first_colon + 1,\n+                                                    second_colon - first_colon - 1);\n+              config_option->option = apr_pstrndup(pool, second_colon + 1,\n+                                                   equals_sign - second_colon -\n+1);\n+\n+              if (! (strchr(config_option->option, ':')))\n+                {\n+                  config_option->value = apr_pstrndup(pool, equals_sign + 1,\n+                                                      opt_arg + len - equals_sign - 1);\n+                  APR_ARRAY_PUSH(config_options, svn_cmdline__config_argument_t\n+*)\n+                                       = config_option;\n+                  return SVN_NO_ERROR;\n+                }\n+            }\n+        }\n+    }\n+  return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,\n+                          _(\"Invalid syntax of argument of --config-option\"));\n+}\ndiff --git a/svn17_compat.h b/svn17_compat.h\nnew file mode 100644\nindex 0000000..2c48cd6\n--- /dev/null\n+++ b/svn17_compat.h\n@@ -0,0 +1,179 @@\n+/**\n+ * @copyright\n+ * ====================================================================\n+ *    This file is derived from code licensed to the Apache\n+ *    Software Foundation (ASF) under one or more contributor\n+ *    license agreements.  See the NOTICE file distributed with\n+ *    this file for additional information regarding copyright\n+ *    ownership.  The ASF licenses those portions to you under\n+ *    the Apache License, Version 2.0 (the \"License\"); you may\n+ *    not use those portions except in compliance with the\n+ *    License.  You may obtain a copy of the License at\n+ *\n+ *       http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ *    Unless required by applicable law or agreed to in writing,\n+ *    software distributed under the License is distributed on an\n+ *    \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n+ *    KIND, either express or implied.  See the License for the\n+ *    specific language governing permissions and limitations\n+ *    under the License.\n+ *\n+ *    Any code in this file not licensed from the ASF is\n+ *    original code in the public domain.  You may freely use,\n+ *    modify, distribute, and relicense such code.\n+ * ====================================================================\n+ * @endcopyright\n+ *\n+ * @file compat.h\n+ * @brief SVN 1.7 compatibility routines.\n+ */\n+\n+#ifndef SVN17_COMPAT_H\n+#define SVN17_COMPAT_H\n+\n+#include <apr.h>\n+#include <apr_pools.h>\n+\n+#include <svn_types.h>\n+\n+/** Join a valid base uri (@a base) with a relative path or uri\n+ * (@a component), allocating the result in @a pool. @a component need\n+ * not be a single component: it can be a relative path or a '/'\n+ * prefixed relative path to join component to the root path of @a base.\n+ *\n+ * If @a component is the empty path, then @a base will be copied and\n+ * returned.\n+ *\n+ * If the @a component is an absolute uri, then it is copied and returned.\n+ *\n+ * If @a component starts with a '/' and @a base contains a scheme, the\n+ * scheme defined joining rules are applied.\n+ *\n+ * From svn_dirent_uri.h.\n+ */\n+char *\n+svn_uri_join(const char *base,\n+             const char *component,\n+             apr_pool_t *pool);\n+\n+/** Get the basename of the specified canonicalized @a relpath.  The\n+ * basename is defined as the last component of the relpath.  If the @a\n+ * relpath has only one component then that is returned. The returned\n+ * value will have no slashes in it.\n+ *\n+ * Example: svn_relpath_basename(\"/trunk/foo/bar\") -> \"bar\"\n+ *\n+ * The returned basename will be allocated in @a pool. If @a\n+ * pool is NULL a pointer to the basename in @a relpath is returned.\n+ *\n+ * @note If an empty string is passed, then an empty string will be returned.\n+ *\n+ * From svn_dirent_uri.h.\n+ */\n+const char *\n+svn_relpath_basename(const char *uri,\n+                     apr_pool_t *pool);\n+\n+/** Gets the name of the specified canonicalized @a dirent as it is known\n+ * within its parent directory. If the @a dirent is root, return \"\". The\n+ * returned value will not have slashes in it.\n+ *\n+ * Example: svn_dirent_basename(\"/foo/bar\") -> \"bar\"\n+ *\n+ * The returned basename will be allocated in @a pool. If @a pool is NULL\n+ * a pointer to the basename in @a dirent is returned.\n+ *\n+ * @note If an empty string is passed, then an empty string will be returned.\n+ *\n+ * From svn_dirent_uri.h.\n+ */\n+const char *\n+svn_dirent_basename(const char *dirent,\n+                    apr_pool_t *pool);\n+\n+/** Return a new uri like @a uri, but transformed such that some types\n+ * of uri specification redundancies are removed.\n+ *\n+ * This involves collapsing redundant \"/./\" elements, removing\n+ * multiple adjacent separator characters, removing trailing\n+ * separator characters, and possibly other semantically inoperative\n+ * transformations.\n+ *\n+ * If @a uri starts with a schema, this function also normalizes the\n+ * escaping of the path component by unescaping characters that don't\n+ * need escaping and escaping characters that do need escaping but\n+ * weren't.\n+ *\n+ * This functions supports URLs.\n+ *\n+ * The returned uri may be statically allocated or allocated from @a pool.\n+ *\n+ * From svn_dirent_uri.h.\n+ */\n+const char *\n+svn_uri_canonicalize(const char *uri,\n+                     apr_pool_t *pool);\n+\n+/** Remove file @a path, a utf8-encoded path.  This wraps apr_file_remove(),\n+ * converting any error to a Subversion error. If @a ignore_enoent is TRUE, and\n+ * the file is not present (APR_STATUS_IS_ENOENT returns TRUE), then no\n+ * error will be returned.\n+ *\n+ * From svn_io.h.\n+ */\n+svn_error_t *\n+svn_io_remove_file2(const char *path,\n+                   svn_boolean_t ignore_enoent,\n+                   apr_pool_t *scratch_pool);\n+\n+/* From svn_private_config.h.\n+ */\n+#define PACKAGE_NAME \"subversion\"\n+#define N_(x) x\n+#include <locale.h>\n+#include <libintl.h>\n+#define _(x) dgettext(PACKAGE_NAME, x)\n+\n+/** Sets the config options in @a config_options, an apr array containing\n+ * svn_cmdline__config_argument_t* elements to the configuration in @a cfg,\n+ * a hash mapping of <tt>const char *</tt> configuration file names to\n+ * @c svn_config_t *'s. Write warnings to stderr.\n+ *\n+ * Use @a prefix as prefix and @a argument_name in warning messages.\n+ *\n+ * From private/svn_cmdline_private.h.\n+ */\n+svn_error_t *\n+svn_cmdline__apply_config_options(apr_hash_t *config,\n+                                  const apr_array_header_t *config_options,\n+                                  const char *prefix,\n+                                  const char *argument_name);\n+\n+/** Container for config options parsed with svn_cmdline__parse_config_option\n+ *\n+ * From private/svn_cmdline_private.h.\n+ */\n+typedef struct svn_cmdline__config_argument_t\n+{\n+  const char *file;\n+  const char *section;\n+  const char *option;\n+  const char *value;\n+} svn_cmdline__config_argument_t;\n+\n+/** Parser for 'FILE:SECTION:OPTION=[VALUE]'-style option arguments.\n+ *\n+ * Parses @a opt_arg and places its value in @a config_options, an apr array\n+ * containing svn_cmdline__config_argument_t* elements, allocating the option\n+ * data in @a pool\n+ *\n+ * From private/svn_cmdline_private.h.\n+ */\n+svn_error_t *\n+svn_cmdline__parse_config_option(apr_array_header_t *config_options,\n+                                 const char *opt_arg,\n+                                 apr_pool_t *pool);\n+\n+\n+#endif /* SVN17_COMPAT_H */\ndiff --git a/svnrdump.c b/svnrdump.c\nindex daf826e..a320b24 100644\n--- a/svnrdump.c\n+++ b/svnrdump.c\n@@ -30,15 +30,14 @@\n #include \"svn_repos.h\"\n #include \"svn_path.h\"\n #include \"svn_utf.h\"\n-#include \"svn_private_config.h\"\n #include \"svn_string.h\"\n #include \"svn_props.h\"\n+#include \"svn_dirent_uri.h\"\n \n+#include \"svn17_compat.h\"\n #include \"dump_editor.h\"\n #include \"load_editor.h\"\n \n-#include \"private/svn_cmdline_private.h\"\n-\n static svn_opt_subcommand_t dump_cmd, load_cmd;\n \n enum svn_svnrdump__longopt_t\ndiff --git a/svntest/main.py b/svntest/main.py\nindex 2c59101..3e938d6 100644\n--- a/svntest/main.py\n+++ b/svntest/main.py\n@@ -149,15 +149,14 @@ def url2pathname(path):\n # The locations of the svn, svnadmin and svnlook binaries, relative to\n # the only scripts that import this file right now (they live in ../).\n # Use --bin to override these defaults.\n-svn_binary = os.path.abspath('../../svn/svn' + _exe)\n-svnadmin_binary = os.path.abspath('../../svnadmin/svnadmin' + _exe)\n-svnlook_binary = os.path.abspath('../../svnlook/svnlook' + _exe)\n-svnrdump_binary = os.path.abspath('../../svnrdump/svnrdump' + _exe)\n-svnsync_binary = os.path.abspath('../../svnsync/svnsync' + _exe)\n-svnversion_binary = os.path.abspath('../../svnversion/svnversion' + _exe)\n-svndumpfilter_binary = os.path.abspath('../../svndumpfilter/svndumpfilter' + \\\n-                                       _exe)\n-entriesdump_binary = os.path.abspath('entries-dump' + _exe)\n+svn_binary = 'svn'\n+svnadmin_binary = 'svnadmin'\n+svnlook_binary = 'svnlook'\n+svnrdump_binary = os.path.abspath('./svnrdump' + _exe)\n+svnsync_binary = 'svnsync'\n+svnversion_binary = 'svnversion'\n+svndumpfilter_binary = 'svndumpfilter'\n+entriesdump_binary = 'entries-dump'\n \n # Location to the pristine repository, will be calculated from test_area_url\n # when we know what the user specified for --url.\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147366","messageId":"20100807023026.GA3111@burratino","threadId":"24388","inReplyTo":"20100806175709.GA2683@burratino","subject":"Re: [PATCH svnrdump-standalone] Sync with upstream","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-07T02:30:26Z","receivedAt":"2010-08-07T02:30:26Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Jonathan Nieder wrote:\n\n> Caveats: Most load tests do not pass.\n\nThey pass now.  Thanks for the help!\n\nYou can\n\n  git clone git://repo.or.cz/svnrdump/jrn.git\n\nfor the tree.  The todo branch documents how to revive the\ngit-svn branches.\n\nHave fun,\nJonathan\n"},{"id":"147368","messageId":"20100807024703.GB15410@kytes","threadId":"24388","inReplyTo":"20100807023026.GA3111@burratino","subject":"Re: [PATCH svnrdump-standalone] Sync with upstream","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-08-07T02:47:05Z","receivedAt":"2010-08-07T02:47:05Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Jonathan,\n\nJonathan Nieder writes:\n> Jonathan Nieder wrote:\n> \n> > Caveats: Most load tests do not pass.\n> \n> They pass now.  Thanks for the help!\n\n:)\n\n> You can\n> \n>   git clone git://repo.or.cz/svnrdump/jrn.git\n> \n> for the tree.  The todo branch documents how to revive the\n> git-svn branches.\n\nThanks for this- I can easily maintain an out-of-trunk version that\ncompiles against 1.6 now. Much nicer than your massive patches :)\n\n-- Ram\n"},{"id":"147369","messageId":"20100807025123.GC15410@kytes","threadId":"24388","inReplyTo":"20100807023026.GA3111@burratino","subject":"Re: [PATCH svnrdump-standalone] Sync with upstream","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-08-07T02:51:25Z","receivedAt":"2010-08-07T02:51:25Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Jonathan,\n\nJonathan Nieder writes:\n> for the tree.  The todo branch documents how to revive the\n> git-svn branches.\n\nThanks especially for taking the effort to import the history from SVN\nand write a nice NOTES file :)\n\n-- Ram\n"}]}