{"thread":{"id":"24173","subject":"[GSoC update] git-remote-svn: Week 8","startedAt":"2010-06-23T16:22:13Z","lastAt":"2010-06-25T15:30:32Z","messageCount":18,"participants":["Ramkumar Ramachandra","Jonathan Nieder","Daniel Shahaf"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"144068","messageId":"1277310140-16891-1-git-send-email-artagnon@gmail.com","threadId":"24173","inReplyTo":null,"subject":"[GSoC update] git-remote-svn: Week 8","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-06-23T16:22:13Z","receivedAt":"2010-06-23T16:22:13Z","isPatch":false,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nI've been very productive over the last few days. The good news is\nthat I finally managed to a (badly-formatted, broken, abridged) dump\nof the text and properties. The bad news is that the program only\nseems to work by accident- it segfaults all over the place without\nreason, I don't know what to make of the backtraces, and documentation\nis thin as usual. I plan to go back in history to do a major cleanup/\nrefactor before proceeding, now that I've learnt how to trigger the\ndumping.\n \nI got this far mainly because of the help I got from Subversion\ndevelopers like Daniel- A big thank you to them. The plan is to finish\nthis, get it reviewed/ acked by Daniel and the other Subversion\ndevelopers, and test it with David's exporter before merging it into\ngit.git. Once it's merged, I plan to write a series for Subversion,\nget it merged there, and remove it from git.git in favor of simply\nlinking to the feature. The reason for this roundabout dance is\nsimple- things are much easier to test in Git-land.\n\nAnother thing that I think we should give some thought- how do I get\nthis merged into git.git? Should I post a single patch with the\n(probably 1000-line) dump editor? In my opinion, the revision history\nis very valuable- can we get a clean series of 6~8 patches merged\ninstead?\n\n-- Ram\n\nRamkumar Ramachandra (7):\n  Add skeleton RA svnclient\n  Add stripped dump editor\n  Import dump_node to dump what changed and cleanup whitespace\n  Replace deprecated svn_path_join\n  Trigger dump_node in change_dir_prop\n  Add file_baton and trigger dump_node in change_file_prop\n  [WIP] Dump the text delta\n\n LICENSE        |   14 ++\n Makefile       |    4 +\n debug_editor.c |  413 +++++++++++++++++++++++++++++++++++\n debug_editor.h |    6 +\n dump_editor.c  |  652 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n dump_editor.h  |   10 +\n svnclient_ra.c |  138 ++++++++++++\n 7 files changed, 1237 insertions(+), 0 deletions(-)\n create mode 100644 LICENSE\n create mode 100644 Makefile\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 svnclient_ra.c\n"},{"id":"144073","messageId":"1277310140-16891-2-git-send-email-artagnon@gmail.com","threadId":"24173","inReplyTo":"1277310140-16891-1-git-send-email-artagnon@gmail.com","subject":"[WIP PATCH 1/7] Add skeleton RA svnclient","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-06-23T16:22:14Z","receivedAt":"2010-06-23T16:22:14Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"The SVN client uses the RA API to connect to a remote server and\nreplay revisions. Currently, it wraps a debug editor to print out some\ntree information. In future, it will dump the data in every revision\nto stdout in dumpfile format (hopefully) without resorting to the FS\nAPI.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n Makefile       |    4 +\n debug_editor.c |  413 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n debug_editor.h |    6 +\n svnclient_ra.c |  131 ++++++++++++++++++\n 4 files changed, 554 insertions(+), 0 deletions(-)\n create mode 100644 Makefile\n create mode 100644 debug_editor.c\n create mode 100644 debug_editor.h\n create mode 100644 svnclient_ra.c\n\ndiff --git a/Makefile b/Makefile\nnew file mode 100644\nindex 0000000..c3c695c\n--- /dev/null\n+++ b/Makefile\n@@ -0,0 +1,4 @@\n+svnclient_ra: *.c *.h\n+\t$(CC) -Wall -Werror -ggdb3 -O0 -o $@ svnclient_ra.c debug_editor.c -lsvn_client-1 -I. -I/usr/local/include/subversion-1 -I/usr/include/apr-1.0\n+clean:\n+\t$(RM) svnclient_ra\ndiff --git a/debug_editor.c b/debug_editor.c\nnew file mode 100644\nindex 0000000..ef77e44\n--- /dev/null\n+++ b/debug_editor.c\n@@ -0,0 +1,413 @@\n+#include \"svn_pools.h\"\n+#include \"svn_cmdline.h\"\n+#include \"svn_client.h\"\n+#include \"svn_ra.h\"\n+\n+struct edit_baton\n+{\n+  const svn_delta_editor_t *wrapped_editor;\n+  void *wrapped_edit_baton;\n+\n+  int indent_level;\n+\n+  svn_stream_t *out;\n+};\n+\n+struct dir_baton\n+{\n+  void *edit_baton;\n+  void *wrapped_dir_baton;\n+};\n+\n+struct file_baton\n+{\n+  void *edit_baton;\n+  void *wrapped_file_baton;\n+};\n+\n+static svn_error_t *\n+write_indent(struct edit_baton *eb, apr_pool_t *pool)\n+{\n+  int i;\n+\n+  for (i = 0; i < eb->indent_level; ++i)\n+    SVN_ERR(svn_stream_printf(eb->out, pool, \" \"));\n+\n+  return SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *\n+set_target_revision(void *edit_baton,\n+                    svn_revnum_t target_revision,\n+                    apr_pool_t *pool)\n+{\n+  struct edit_baton *eb = edit_baton;\n+\n+  SVN_ERR(write_indent(eb, pool));\n+  SVN_ERR(svn_stream_printf(eb->out, pool, \"set_target_revision : %ld\\n\",\n+                            target_revision));\n+\n+  return eb->wrapped_editor->set_target_revision(eb->wrapped_edit_baton,\n+                                                 target_revision,\n+                                                 pool);\n+}\n+\n+static svn_error_t *\n+open_root(void *edit_baton,\n+          svn_revnum_t base_revision,\n+          apr_pool_t *pool,\n+          void **root_baton)\n+{\n+  struct edit_baton *eb = edit_baton;\n+  struct dir_baton *dir_baton = apr_palloc(pool, sizeof(*dir_baton));\n+\n+  SVN_ERR(write_indent(eb, pool));\n+  SVN_ERR(svn_stream_printf(eb->out, pool, \"open_root : %ld\\n\",\n+                            base_revision));\n+  eb->indent_level++;\n+\n+  SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton,\n+                                        base_revision,\n+                                        pool,\n+                                        &dir_baton->wrapped_dir_baton));\n+\n+  dir_baton->edit_baton = edit_baton;\n+\n+  *root_baton = dir_baton;\n+\n+  return SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *\n+delete_entry(const char *path,\n+             svn_revnum_t base_revision,\n+             void *parent_baton,\n+             apr_pool_t *pool)\n+{\n+  struct dir_baton *pb = parent_baton;\n+  struct edit_baton *eb = pb->edit_baton;\n+\n+  SVN_ERR(write_indent(eb, pool));\n+  SVN_ERR(svn_stream_printf(eb->out, pool, \"delete_entry : %s:%ld\\n\",\n+                            path, base_revision));\n+\n+  return eb->wrapped_editor->delete_entry(path,\n+                                          base_revision,\n+                                          pb->wrapped_dir_baton,\n+                                          pool);\n+}\n+\n+static svn_error_t *\n+add_directory(const char *path,\n+              void *parent_baton,\n+              const char *copyfrom_path,\n+              svn_revnum_t copyfrom_revision,\n+              apr_pool_t *pool,\n+              void **child_baton)\n+{\n+  struct dir_baton *pb = parent_baton;\n+  struct edit_baton *eb = pb->edit_baton;\n+  struct dir_baton *b = apr_palloc(pool, sizeof(*b));\n+\n+  SVN_ERR(write_indent(eb, pool));\n+  SVN_ERR(svn_stream_printf(eb->out, pool,\n+                            \"add_directory : '%s' [from '%s':%ld]\\n\",\n+                            path, copyfrom_path, copyfrom_revision));\n+  eb->indent_level++;\n+\n+  SVN_ERR(eb->wrapped_editor->add_directory(path,\n+                                            pb->wrapped_dir_baton,\n+                                            copyfrom_path,\n+                                            copyfrom_revision,\n+                                            pool,\n+                                            &b->wrapped_dir_baton));\n+\n+  b->edit_baton = eb;\n+  *child_baton = b;\n+\n+  return SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *\n+open_directory(const char *path,\n+               void *parent_baton,\n+               svn_revnum_t base_revision,\n+               apr_pool_t *pool,\n+               void **child_baton)\n+{\n+  struct dir_baton *pb = parent_baton;\n+  struct edit_baton *eb = pb->edit_baton;\n+  struct dir_baton *db = apr_palloc(pool, sizeof(*db));\n+\n+  SVN_ERR(write_indent(eb, pool));\n+  SVN_ERR(svn_stream_printf(eb->out, pool, \"open_directory : '%s':%ld\\n\",\n+                            path, base_revision));\n+  eb->indent_level++;\n+\n+  SVN_ERR(eb->wrapped_editor->open_directory(path,\n+                                             pb->wrapped_dir_baton,\n+                                             base_revision,\n+                                             pool,\n+                                             &db->wrapped_dir_baton));\n+\n+  db->edit_baton = eb;\n+  *child_baton = db;\n+\n+  return SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *\n+add_file(const char *path,\n+         void *parent_baton,\n+         const char *copyfrom_path,\n+         svn_revnum_t copyfrom_revision,\n+         apr_pool_t *pool,\n+         void **file_baton)\n+{\n+  struct dir_baton *pb = parent_baton;\n+  struct edit_baton *eb = pb->edit_baton;\n+  struct file_baton *fb = apr_palloc(pool, sizeof(*fb));\n+\n+  SVN_ERR(write_indent(eb, pool));\n+  SVN_ERR(svn_stream_printf(eb->out, pool,\n+                            \"add_file : '%s' [from '%s':%ld]\\n\",\n+                            path, copyfrom_path, copyfrom_revision));\n+\n+  eb->indent_level++;\n+\n+  SVN_ERR(eb->wrapped_editor->add_file(path,\n+                                       pb->wrapped_dir_baton,\n+                                       copyfrom_path,\n+                                       copyfrom_revision,\n+                                       pool,\n+                                       &fb->wrapped_file_baton));\n+\n+  fb->edit_baton = eb;\n+  *file_baton = fb;\n+\n+  return SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *\n+open_file(const char *path,\n+          void *parent_baton,\n+          svn_revnum_t base_revision,\n+          apr_pool_t *pool,\n+          void **file_baton)\n+{\n+  struct dir_baton *pb = parent_baton;\n+  struct edit_baton *eb = pb->edit_baton;\n+  struct file_baton *fb = apr_palloc(pool, sizeof(*fb));\n+\n+  SVN_ERR(write_indent(eb, pool));\n+  SVN_ERR(svn_stream_printf(eb->out, pool, \"open_file : '%s':%ld\\n\",\n+                            path, base_revision));\n+\n+  eb->indent_level++;\n+\n+  SVN_ERR(eb->wrapped_editor->open_file(path,\n+                                        pb->wrapped_dir_baton,\n+                                        base_revision,\n+                                        pool,\n+                                        &fb->wrapped_file_baton));\n+\n+  fb->edit_baton = eb;\n+  *file_baton = fb;\n+\n+  return SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *\n+apply_textdelta(void *file_baton,\n+                const char *base_checksum,\n+                apr_pool_t *pool,\n+                svn_txdelta_window_handler_t *handler,\n+                void **handler_baton)\n+{\n+  struct file_baton *fb = file_baton;\n+  struct edit_baton *eb = fb->edit_baton;\n+\n+  SVN_ERR(write_indent(eb, pool));\n+  SVN_ERR(svn_stream_printf(eb->out, pool, \"apply_textdelta : %s\\n\",\n+                            base_checksum));\n+\n+  SVN_ERR(eb->wrapped_editor->apply_textdelta(fb->wrapped_file_baton,\n+                                              base_checksum,\n+                                              pool,\n+                                              handler,\n+                                              handler_baton));\n+\n+  return SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *\n+close_file(void *file_baton,\n+           const char *text_checksum,\n+           apr_pool_t *pool)\n+{\n+  struct file_baton *fb = file_baton;\n+  struct edit_baton *eb = fb->edit_baton;\n+\n+  eb->indent_level--;\n+\n+  SVN_ERR(write_indent(eb, pool));\n+  SVN_ERR(svn_stream_printf(eb->out, pool, \"close_file : %s\\n\",\n+                            text_checksum));\n+\n+  SVN_ERR(eb->wrapped_editor->close_file(fb->wrapped_file_baton,\n+                                         text_checksum, pool));\n+\n+  return SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *\n+absent_file(const char *path,\n+            void *file_baton,\n+            apr_pool_t *pool)\n+{\n+  struct file_baton *fb = file_baton;\n+  struct edit_baton *eb = fb->edit_baton;\n+\n+  SVN_ERR(write_indent(eb, pool));\n+  SVN_ERR(svn_stream_printf(eb->out, pool, \"absent_file : %s\\n\", path));\n+\n+  SVN_ERR(eb->wrapped_editor->absent_file(path, fb->wrapped_file_baton,\n+                                          pool));\n+\n+  return SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *\n+close_directory(void *dir_baton,\n+                apr_pool_t *pool)\n+{\n+  struct dir_baton *db = dir_baton;\n+  struct edit_baton *eb = db->edit_baton;\n+\n+  eb->indent_level--;\n+  SVN_ERR(write_indent(eb, pool));\n+  SVN_ERR(svn_stream_printf(eb->out, pool, \"close_directory\\n\"));\n+\n+  SVN_ERR(eb->wrapped_editor->close_directory(db->wrapped_dir_baton,\n+                                              pool));\n+\n+  return SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *\n+absent_directory(const char *path,\n+                 void *dir_baton,\n+                 apr_pool_t *pool)\n+{\n+  struct dir_baton *db = dir_baton;\n+  struct edit_baton *eb = db->edit_baton;\n+\n+  SVN_ERR(write_indent(eb, pool));\n+  SVN_ERR(svn_stream_printf(eb->out, pool, \"absent_directory : %s\\n\",\n+                            path));\n+\n+  SVN_ERR(eb->wrapped_editor->absent_directory(path, db->wrapped_dir_baton,\n+                                               pool));\n+\n+  return SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *\n+change_file_prop(void *file_baton,\n+                 const char *name,\n+                 const svn_string_t *value,\n+                 apr_pool_t *pool)\n+{\n+  struct file_baton *fb = file_baton;\n+  struct edit_baton *eb = fb->edit_baton;\n+\n+  SVN_ERR(write_indent(eb, pool));\n+  SVN_ERR(svn_stream_printf(eb->out, pool, \"change_file_prop : %s\\n\",\n+                            name));\n+\n+  SVN_ERR(eb->wrapped_editor->change_file_prop(fb->wrapped_file_baton,\n+                                               name,\n+                                               value,\n+                                               pool));\n+\n+  return SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *\n+change_dir_prop(void *dir_baton,\n+                const char *name,\n+                const svn_string_t *value,\n+                apr_pool_t *pool)\n+{\n+  struct dir_baton *db = dir_baton;\n+  struct edit_baton *eb = db->edit_baton;\n+\n+  SVN_ERR(write_indent(eb, pool));\n+  SVN_ERR(svn_stream_printf(eb->out, pool, \"change_dir_prop : %s\\n\", name));\n+\n+  SVN_ERR(eb->wrapped_editor->change_dir_prop(db->wrapped_dir_baton,\n+                                              name,\n+                                              value,\n+                                              pool));\n+\n+  return SVN_NO_ERROR;\n+}\n+\n+static svn_error_t *\n+close_edit(void *edit_baton,\n+           apr_pool_t *pool)\n+{\n+  struct edit_baton *eb = edit_baton;\n+\n+  SVN_ERR(write_indent(eb, pool));\n+  SVN_ERR(svn_stream_printf(eb->out, pool, \"close_edit\\n\"));\n+\n+  SVN_ERR(eb->wrapped_editor->close_edit(eb->wrapped_edit_baton, pool));\n+\n+  return SVN_NO_ERROR;\n+}\n+\n+svn_error_t *\n+svn_delta__get_debug_editor(const svn_delta_editor_t **editor,\n+                            void **edit_baton,\n+                            const svn_delta_editor_t *wrapped_editor,\n+                            void *wrapped_edit_baton,\n+                            apr_pool_t *pool)\n+{\n+  svn_delta_editor_t *tree_editor = svn_delta_default_editor(pool);\n+  struct edit_baton *eb = apr_palloc(pool, sizeof(*eb));\n+  apr_file_t *errfp;\n+  svn_stream_t *out;\n+\n+  apr_status_t apr_err = apr_file_open_stderr(&errfp, pool);\n+  if (apr_err)\n+    return svn_error_wrap_apr(apr_err, \"Problem opening stderr\");\n+\n+  out = svn_stream_from_aprfile2(errfp, TRUE, pool);\n+\n+  tree_editor->set_target_revision = set_target_revision;\n+  tree_editor->open_root = open_root;\n+  tree_editor->delete_entry = delete_entry;\n+  tree_editor->add_directory = add_directory;\n+  tree_editor->open_directory = open_directory;\n+  tree_editor->change_dir_prop = change_dir_prop;\n+  tree_editor->close_directory = close_directory;\n+  tree_editor->absent_directory = absent_directory;\n+  tree_editor->add_file = add_file;\n+  tree_editor->open_file = open_file;\n+  tree_editor->apply_textdelta = apply_textdelta;\n+  tree_editor->change_file_prop = change_file_prop;\n+  tree_editor->close_file = close_file;\n+  tree_editor->absent_file = absent_file;\n+  tree_editor->close_edit = close_edit;\n+\n+  eb->wrapped_editor = wrapped_editor;\n+  eb->wrapped_edit_baton = wrapped_edit_baton;\n+  eb->out = out;\n+  eb->indent_level = 0;\n+\n+  *editor = tree_editor;\n+  *edit_baton = eb;\n+\n+  return SVN_NO_ERROR;\n+}\ndiff --git a/debug_editor.h b/debug_editor.h\nnew file mode 100644\nindex 0000000..ae87c36\n--- /dev/null\n+++ b/debug_editor.h\n@@ -0,0 +1,6 @@\n+svn_error_t *\n+svn_delta__get_debug_editor(const svn_delta_editor_t **editor,\n+                            void **edit_baton,\n+                            const svn_delta_editor_t *wrapped_editor,\n+                            void *wrapped_edit_baton,\n+                            apr_pool_t *pool);\ndiff --git a/svnclient_ra.c b/svnclient_ra.c\nnew file mode 100644\nindex 0000000..2ed04af\n--- /dev/null\n+++ b/svnclient_ra.c\n@@ -0,0 +1,131 @@\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+static apr_pool_t *pool = NULL;\n+static svn_client_ctx_t *ctx = NULL;\n+static svn_ra_session_t *session = NULL;\n+\n+typedef struct {\n+\tvoid *editor, *baton;\n+} replay_baton_t;\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+\t*editor = ((replay_baton_t *) replay_baton)->editor;\n+\t*edit_baton = ((replay_baton_t *) replay_baton)->baton;\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+\tSVN_ERR(editor->close_edit(edit_baton, pool));\n+\treturn SVN_NO_ERROR;\n+}\n+\n+svn_error_t *build_auth_baton()\n+{\n+\tsvn_auth_provider_object_t *provider;\n+\tapr_array_header_t *providers\n+\t\t= apr_array_make (pool, 4, sizeof (svn_auth_provider_object_t *));\n+\n+\tsvn_auth_get_simple_prompt_provider (&provider,\n+\t                                     NULL,\n+\t                                     NULL,\n+\t                                     2,\n+\t                                     pool);\n+\tAPR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider;\n+\n+\tsvn_auth_get_username_prompt_provider (&provider,\n+\t                                       NULL,\n+\t                                       NULL,\n+\t                                       2,\n+\t                                       pool);\n+\tAPR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider;\n+\n+\tsvn_auth_open (&ctx->auth_baton, providers, pool);\n+\treturn SVN_NO_ERROR;\n+}\n+\n+svn_error_t *populate_context()\n+{\n+\tconst char *http_library;\n+\t\n+\tSVN_ERR(svn_config_get_config(&(ctx->config), NULL, pool));\n+\t\n+\thttp_library = getenv(\"SVN_HTTP_LIBRARY\");\n+\tif (http_library)\n+\t\tsvn_config_set(apr_hash_get(ctx->config, \"servers\", APR_HASH_KEY_STRING),\n+\t\t               \"global\", \"http-library\", http_library);\n+\treturn SVN_NO_ERROR;\n+}\n+\n+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+#if defined(WIN32) || defined(__CYGWIN__)\n+\tif (getenv(\"SVN_ASP_DOT_NET_HACK\"))\n+\t\tSVN_ERR(svn_wc_set_adm_dir(\"_svn\", pool));\n+#endif\n+\n+\tSVN_ERR(populate_context());\n+\tSVN_ERR(build_auth_baton());\n+\tSVN_ERR(svn_client_open_ra_session(&session, url, ctx, pool));\n+\treturn SVN_NO_ERROR;\n+}\n+\n+svn_error_t *replay_range(svn_revnum_t start_revision, svn_revnum_t end_revision)\n+{\n+\tsvn_revnum_t latest_revision;\n+\tsvn_delta_editor_t *editor, *debug_editor;\n+\treplay_baton_t *replay_baton = apr_palloc(pool, sizeof(replay_baton_t));\n+\tvoid *debug_baton;\n+\tSVN_ERR(svn_ra_get_latest_revnum(session, &latest_revision, pool));\n+\tprintf(\"%ld\\n\", latest_revision);\n+\teditor = svn_delta_default_editor(pool);\n+\tSVN_ERR(svn_delta__get_debug_editor((const svn_delta_editor_t **)&debug_editor,\n+\t                                    &debug_baton, editor,\n+\t                                    NULL, pool));\n+\treplay_baton->editor = debug_editor;\n+\treplay_baton->baton = debug_baton;\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+void close_connection()\n+{\n+\tsvn_pool_destroy(pool);\n+}\n+\n+int main()\n+{\n+\tconst char url[] = \"http://svn.apache.org/repos/asf\";\n+\tsvn_revnum_t start_revision = 1, end_revision = 5;\n+\tif (svn_cmdline_init (\"svnclient_ra\", stderr) != EXIT_SUCCESS)\n+\t\treturn 1;\n+\tpool = svn_pool_create(NULL);\n+\n+\tSVN_INT_ERR(open_connection(url));\n+\tSVN_INT_ERR(replay_range(start_revision, end_revision));\n+\n+\tclose_connection();\n+\treturn 0;\n+}\n-- \n1.7.1\n"},{"id":"144070","messageId":"1277310140-16891-3-git-send-email-artagnon@gmail.com","threadId":"24173","inReplyTo":"1277310140-16891-1-git-send-email-artagnon@gmail.com","subject":"[WIP PATCH 2/7] Add stripped dump editor","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-06-23T16:22:15Z","receivedAt":"2010-06-23T16:22:15Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Replace the default editor with a dump editor from libsvn_repos/dump.c\nafter stripping off filesystem dependencies. Currently, the dump\neditor does nothing, but will dump some revision information to stdout\nin future.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n Makefile       |    2 +-\n dump_editor.c  |  365 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n dump_editor.h  |   10 ++\n svnclient_ra.c |   19 ++-\n 4 files changed, 389 insertions(+), 7 deletions(-)\n create mode 100644 dump_editor.c\n create mode 100644 dump_editor.h\n\ndiff --git a/Makefile b/Makefile\nindex c3c695c..269c406 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1,4 +1,4 @@\n svnclient_ra: *.c *.h\n-\t$(CC) -Wall -Werror -ggdb3 -O0 -o $@ svnclient_ra.c debug_editor.c -lsvn_client-1 -I. -I/usr/local/include/subversion-1 -I/usr/include/apr-1.0\n+\t$(CC) -Wall -ggdb3 -O0 -o $@ svnclient_ra.c debug_editor.c dump_editor.c -lsvn_client-1 -I. -I/usr/local/include/subversion-1 -I/usr/include/apr-1.0\n clean:\n \t$(RM) svnclient_ra\ndiff --git a/dump_editor.c b/dump_editor.c\nnew file mode 100644\nindex 0000000..f5353b0\n--- /dev/null\n+++ b/dump_editor.c\n@@ -0,0 +1,365 @@\n+#include \"svn_pools.h\"\n+#include \"svn_error.h\"\n+#include \"svn_iter.h\"\n+#include \"svn_repos.h\"\n+#include \"svn_string.h\"\n+#include \"svn_dirent_uri.h\"\n+#include \"svn_path.h\"\n+#include \"svn_time.h\"\n+#include \"svn_checksum.h\"\n+#include \"svn_props.h\"\n+\n+#define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))\n+/*----------------------------------------------------------------------*/\n+/** An editor which dumps node-data in 'dumpfile format' to a file. **/\n+\n+/* Look, mom!  No file batons! */\n+\n+struct edit_baton\n+{\n+\t/* The stream to dump to: stdout */\n+\tsvn_stream_t *stream;\n+\t\n+\tsvn_revnum_t current_rev;\n+\t\n+\t/* reusable buffer for writing file contents */\n+\tchar buffer[SVN__STREAM_CHUNK_SIZE];\n+\tapr_size_t bufsize;\n+};\n+\n+struct dir_baton\n+{\n+\tstruct edit_baton *edit_baton;\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+\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+struct dir_baton *\n+make_dir_baton(const char *path,\n+               const char *cmp_path,\n+               svn_revnum_t cmp_rev,\n+               void *edit_baton,\n+               void *parent_dir_baton,\n+               svn_boolean_t added,\n+               apr_pool_t *pool)\n+{\n+\tstruct 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+\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\tfull_path = svn_path_join(\"/\", path, pool);\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->edit_baton = 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+svn_error_t *\n+open_root(void *edit_baton,\n+          svn_revnum_t base_revision,\n+          apr_pool_t *pool,\n+          void **root_baton)\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+\n+svn_error_t *\n+delete_entry(const char *path,\n+             svn_revnum_t revision,\n+             void *parent_baton,\n+             apr_pool_t *pool)\n+{\n+\tstruct dir_baton *pb = parent_baton;\n+\tconst char *mypath = apr_pstrdup(pb->pool, path);\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+\n+svn_error_t *\n+add_directory(const char *path,\n+              void *parent_baton,\n+              const char *copyfrom_path,\n+              svn_revnum_t copyfrom_rev,\n+              apr_pool_t *pool,\n+              void **child_baton)\n+{\n+\tstruct dir_baton *pb = parent_baton;\n+\tstruct edit_baton *eb = pb->edit_baton;\n+\tvoid *val;\n+\tsvn_boolean_t is_copy = FALSE;\n+\tstruct dir_baton *new_db\n+\t\t= make_dir_baton(path, copyfrom_path, copyfrom_rev, eb, pb, 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+\tis_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);\n+\n+\t/* Dump the node. */\n+\t/* SVN_ERR(dump_node(eb, path, */\n+\t/*                   svn_node_dir, */\n+\t/*                   val ? svn_node_action_replace : svn_node_action_add, */\n+\t/*                   is_copy, */\n+\t/*                   is_copy ? copyfrom_path : NULL, */\n+\t/*                   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+\n+svn_error_t *\n+open_directory(const char *path,\n+               void *parent_baton,\n+               svn_revnum_t base_revision,\n+               apr_pool_t *pool,\n+               void **child_baton)\n+{\n+\tstruct dir_baton *pb = parent_baton;\n+\tstruct edit_baton *eb = pb->edit_baton;\n+\tstruct dir_baton *new_db;\n+\tconst char *cmp_path = NULL;\n+\tsvn_revnum_t cmp_rev = SVN_INVALID_REVNUM;\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{\n+\t\tcmp_path = svn_path_join(pb->cmp_path,\n+\t\t                         svn_dirent_basename(path, pool), pool);\n+\t\tcmp_rev = pb->cmp_rev;\n+\t}\n+\n+\tnew_db = make_dir_baton(path, cmp_path, cmp_rev, eb, pb, FALSE, pool);\n+\t*child_baton = new_db;\n+\treturn SVN_NO_ERROR;\n+}\n+\n+\n+svn_error_t *\n+close_directory(void *dir_baton,\n+                apr_pool_t *pool)\n+{\n+\tstruct dir_baton *db = dir_baton;\n+\tstruct edit_baton *eb = db->edit_baton;\n+\tapr_hash_index_t *hi;\n+\tapr_pool_t *subpool = svn_pool_create(pool);\n+\n+\tfor (hi = apr_hash_first(pool, db->deleted_entries);\n+\t     hi;\n+\t     hi = apr_hash_next(hi))\n+\t{\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\t/* By sending 'svn_node_unknown', the Node-kind: header simply won't\n+\t\t   be written out.  No big deal at all, really.  The loader\n+\t\t   shouldn't care.  */\n+\t\t/* SVN_ERR(dump_node(eb, path, */\n+\t\t/*                   svn_node_unknown, svn_node_action_delete, */\n+\t\t/*                   FALSE, NULL, SVN_INVALID_REVNUM, subpool)); */\n+\t}\n+\n+\tsvn_pool_destroy(subpool);\n+\treturn SVN_NO_ERROR;\n+}\n+\n+\n+svn_error_t *\n+add_file(const char *path,\n+         void *parent_baton,\n+         const char *copyfrom_path,\n+         svn_revnum_t copyfrom_rev,\n+         apr_pool_t *pool,\n+         void **file_baton)\n+{\n+\tstruct dir_baton *pb = parent_baton;\n+\tstruct edit_baton *eb = pb->edit_baton;\n+\tvoid *val;\n+\tsvn_boolean_t is_copy = FALSE;\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+\tis_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);\n+\n+\t/* Dump the node. */\n+\t/* SVN_ERR(dump_node(eb, path, */\n+\t/*                   svn_node_file, */\n+\t/*                   val ? svn_node_action_replace : svn_node_action_add, */\n+\t/*                   is_copy, */\n+\t/*                   is_copy ? copyfrom_path : NULL, */\n+\t/*                   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*file_baton = NULL;  /* muhahahaha */\n+\treturn SVN_NO_ERROR;\n+}\n+\n+\n+svn_error_t *\n+open_file(const char *path,\n+          void *parent_baton,\n+          svn_revnum_t ancestor_revision,\n+          apr_pool_t *pool,\n+          void **file_baton)\n+{\n+\tstruct dir_baton *pb = parent_baton;\n+\tstruct edit_baton *eb = pb->edit_baton;\n+\tconst char *cmp_path = NULL;\n+\tsvn_revnum_t cmp_rev = SVN_INVALID_REVNUM;\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{\n+\t\tcmp_path = svn_path_join(pb->cmp_path,\n+\t\t                         svn_dirent_basename(path, pool), pool);\n+\t\tcmp_rev = pb->cmp_rev;\n+\t}\n+\n+\t/* SVN_ERR(dump_node(eb, path, */\n+\t/*                   svn_node_file, svn_node_action_change, */\n+\t/*                   FALSE, cmp_path, cmp_rev, pool)); */\n+\n+\t*file_baton = NULL;  /* muhahahaha again */\n+\treturn SVN_NO_ERROR;\n+}\n+\n+svn_error_t *\n+change_dir_prop(void *parent_baton,\n+                const char *name,\n+                const svn_string_t *value,\n+                apr_pool_t *pool)\n+{\n+\tstruct dir_baton *db = parent_baton;\n+\tstruct edit_baton *eb = db->edit_baton;\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{\n+\t\t/* SVN_ERR(dump_node(eb, db->path, */\n+\t\t/*                   svn_node_dir, svn_node_action_change, */\n+\t\t/*                   FALSE, db->cmp_path, db->cmp_rev, pool)); */\n+\t\tdb->written_out = TRUE;\n+\t}\n+\treturn SVN_NO_ERROR;\n+}\n+\n+svn_error_t *\n+get_dump_editor(const svn_delta_editor_t **editor,\n+                void **edit_baton,\n+                svn_revnum_t to_rev,\n+                apr_pool_t *pool)\n+{\n+\t/* Allocate an edit baton to be stored in every directory baton.\n+\t   Set it up for the directory baton we create here, which is the\n+\t   root baton. */\n+\tstruct edit_baton *eb = apr_pcalloc(pool, sizeof(*eb));\n+\tsvn_delta_editor_t *dump_editor = svn_delta_default_editor(pool);\n+\n+\t/* Set up the edit baton. */\n+\tsvn_stream_for_stdout(&(eb->stream), pool);\n+\teb->bufsize = sizeof(eb->buffer);\n+\teb->current_rev = to_rev;\n+\n+\n+\t/* Set up the editor. */\n+\tdump_editor->open_root = open_root;\n+\tdump_editor->delete_entry = delete_entry;\n+\tdump_editor->add_directory = add_directory;\n+\tdump_editor->open_directory = open_directory;\n+\tdump_editor->close_directory = close_directory;\n+\tdump_editor->change_dir_prop = change_dir_prop;\n+\tdump_editor->add_file = add_file;\n+\tdump_editor->open_file = open_file;\n+\n+\t*edit_baton = eb;\n+\t*editor = dump_editor;\n+\n+\treturn SVN_NO_ERROR;\n+}\ndiff --git a/dump_editor.h b/dump_editor.h\nnew file mode 100644\nindex 0000000..5e84223\n--- /dev/null\n+++ b/dump_editor.h\n@@ -0,0 +1,10 @@\n+#include \"svn_pools.h\"\n+#include \"svn_cmdline.h\"\n+#include \"svn_client.h\"\n+#include \"svn_ra.h\"\n+\n+svn_error_t *\n+get_dump_editor(const svn_delta_editor_t **editor,\n+                void **edit_baton,\n+                svn_revnum_t to_rev,\n+                apr_pool_t *pool);\ndiff --git a/svnclient_ra.c b/svnclient_ra.c\nindex 2ed04af..24d99cb 100644\n--- a/svnclient_ra.c\n+++ b/svnclient_ra.c\n@@ -4,6 +4,7 @@\n #include \"svn_ra.h\"\n \n #include \"debug_editor.h\"\n+#include \"dump_editor.h\"\n \n static apr_pool_t *pool = NULL;\n static svn_client_ctx_t *ctx = NULL;\n@@ -92,16 +93,22 @@ svn_error_t *open_connection(const char *url)\n \n svn_error_t *replay_range(svn_revnum_t start_revision, svn_revnum_t end_revision)\n {\n+\tsvn_delta_editor_t *dump_editor, *debug_editor;\n+\tvoid *debug_baton, *dump_baton;\n+\t\n \tsvn_revnum_t latest_revision;\n-\tsvn_delta_editor_t *editor, *debug_editor;\n-\treplay_baton_t *replay_baton = apr_palloc(pool, sizeof(replay_baton_t));\n-\tvoid *debug_baton;\n \tSVN_ERR(svn_ra_get_latest_revnum(session, &latest_revision, pool));\n \tprintf(\"%ld\\n\", latest_revision);\n-\teditor = svn_delta_default_editor(pool);\n+\t\n+\tSVN_ERR(get_dump_editor((const svn_delta_editor_t **)&dump_editor,\n+\t                        &dump_baton, end_revision, pool));\n+\n \tSVN_ERR(svn_delta__get_debug_editor((const svn_delta_editor_t **)&debug_editor,\n-\t                                    &debug_baton, editor,\n-\t                                    NULL, pool));\n+\t                                    &debug_baton,\n+\t                                    (const svn_delta_editor_t *)dump_editor,\n+\t                                    dump_baton, pool));\n+\t\n+\treplay_baton_t *replay_baton = apr_palloc(pool, sizeof(replay_baton_t));\n \treplay_baton->editor = debug_editor;\n \treplay_baton->baton = debug_baton;\n \tSVN_ERR(svn_ra_replay_range(session, start_revision, end_revision,\n-- \n1.7.1\n"},{"id":"144071","messageId":"1277310140-16891-4-git-send-email-artagnon@gmail.com","threadId":"24173","inReplyTo":"1277310140-16891-1-git-send-email-artagnon@gmail.com","subject":"[WIP PATCH 3/7] Import dump_node to dump what changed and cleanup whitespace","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-06-23T16:22:16Z","receivedAt":"2010-06-23T16:22:16Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Import dump_node from dump.c after stripping it off filesystem backing\nto dump which files/ directories were added or removed without\nactually dumping the delta. Modify svnclient_ra to use just the dump\neditor, and not the debug editor. Also cleanup whitespace to conform\nto Git style. Add LICENSE file.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n LICENSE        |   14 +++\n dump_editor.c  |  344 ++++++++++++++++++++++++++++++++++++++------------------\n svnclient_ra.c |    6 +-\n 3 files changed, 251 insertions(+), 113 deletions(-)\n create mode 100644 LICENSE\n\ndiff --git a/LICENSE b/LICENSE\nnew file mode 100644\nindex 0000000..c8f39ff\n--- /dev/null\n+++ b/LICENSE\n@@ -0,0 +1,14 @@\n+Licensed to the Apache Software Foundation (ASF) under one or more\n+contributor license agreements.  See the NOTICE file distributed with\n+this work for additional information regarding copyright ownership.\n+The ASF licenses this file to you under the Apache License, Version\n+2.0 (the \"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, software\n+distributed under the License is distributed on an \"AS IS\" BASIS,\n+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n+implied.  See the License for the specific language governing\n+permissions and limitations under the License.\ndiff --git a/dump_editor.c b/dump_editor.c\nindex f5353b0..ba0630f 100644\n--- a/dump_editor.c\n+++ b/dump_editor.c\n@@ -1,3 +1,7 @@\n+/* Licensed under Apache license.\n+ * See LICENSE for details.\n+ */\n+\n #include \"svn_pools.h\"\n #include \"svn_error.h\"\n #include \"svn_iter.h\"\n@@ -10,13 +14,8 @@\n #include \"svn_props.h\"\n \n #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))\n-/*----------------------------------------------------------------------*/\n-/** An editor which dumps node-data in 'dumpfile format' to a file. **/\n-\n-/* Look, mom!  No file batons! */\n \n-struct edit_baton\n-{\n+struct edit_baton {\n \t/* The stream to dump to: stdout */\n \tsvn_stream_t *stream;\n \t\n@@ -27,8 +26,7 @@ struct edit_baton\n \tapr_size_t bufsize;\n };\n \n-struct dir_baton\n-{\n+struct dir_baton {\n \tstruct edit_baton *edit_baton;\n \tstruct dir_baton *parent_dir_baton;\n \n@@ -71,20 +69,19 @@ struct dir_baton\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-struct dir_baton *\n-make_dir_baton(const char *path,\n-               const char *cmp_path,\n-               svn_revnum_t cmp_rev,\n-               void *edit_baton,\n-               void *parent_dir_baton,\n-               svn_boolean_t added,\n-               apr_pool_t *pool)\n-{\n+struct dir_baton *make_dir_baton(const char *path,\n+                                 const char *cmp_path,\n+                                 svn_revnum_t cmp_rev,\n+                                 void *edit_baton,\n+                                 void *parent_dir_baton,\n+                                 svn_boolean_t added,\n+                                 apr_pool_t *pool) {\n \tstruct 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 \n+\n \t/* A path relative to nothing?  I don't think so. */\n \tSVN_ERR_ASSERT_NO_RETURN(!path || pb);\n \n@@ -111,24 +108,170 @@ make_dir_baton(const char *path,\n \treturn new_db;\n }\n \n+static svn_error_t *dump_node(struct edit_baton *eb,\n+                              const char *path,    /* an absolute path. */\n+                              svn_node_kind_t kind,\n+                              enum svn_node_action action,\n+                              svn_boolean_t is_copy,\n+                              const char *cmp_path,\n+                              svn_revnum_t cmp_rev,\n+                              apr_pool_t *pool)\n+{\n+\tapr_size_t len;\n+\tsvn_boolean_t must_dump_text = TRUE, must_dump_props = TRUE;\n+\tconst char *compare_path = path;\n+\tsvn_revnum_t compare_rev = eb->current_rev - 1;\n+\n+\t/* Write out metadata headers for this file node. */\n+\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t                          SVN_REPOS_DUMPFILE_NODE_PATH \": %s\\n\",\n+\t                          (*path == '/') ? path + 1 : path));\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-svn_error_t *\n-open_root(void *edit_baton,\n-          svn_revnum_t base_revision,\n-          apr_pool_t *pool,\n-          void **root_baton)\n+\t/* Validate the comparison path/rev. */\n+\tif (ARE_VALID_COPY_ARGS(cmp_path, cmp_rev)) {\n+\t\tcompare_path = cmp_path;\n+\t\tcompare_rev = cmp_rev;\n+\t}\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+\n+\t\t/* either the text or props changed, or possibly both. */\n+\t\t/* SVN_ERR(svn_props_changed(&must_dump_props, */\n+\t\t/*                           compare_path, path, pool)); */\n+\t\tif (kind == svn_node_file)\n+\t\t\t/* SVN_ERR(svn_contents_changed(&must_dump_text, */\n+\t\t\t/*                              compare_path, path, pool)); */\n+\t\tbreak;\n+      \n+\tcase svn_node_action_replace:\n+\t\tif (!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\t/* definitely need to dump all content for a replace. */\n+\t\t\tif (kind == svn_node_file)\n+\t\t\t\tmust_dump_text = TRUE;\n+\t\t\tmust_dump_props = TRUE;\n+\t\t\tbreak;\n+\t\t}\n+\t\t/* more complex:  delete original, then add-with-history.  */\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                  is_copy, compare_path, compare_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\tmust_dump_text = FALSE;\n+\t\tmust_dump_props = 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\tmust_dump_text = FALSE;\n+\t\tmust_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 (!is_copy) {\n+\t\t\t/* Dump all contents for a simple 'add'. */\n+\t\t\tif (kind == svn_node_file)\n+\t\t\t\tmust_dump_text = TRUE;\n+\t\t\tmust_dump_props = 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/* Need to decide if the copied node had any extra textual or\n+\t\t   property mods as well.  */\n+\t\t/* SVN_ERR(svn_fs_props_changed(&must_dump_props, */\n+\t\t/*                              compare_path, path, pool)); */\n+\t\t/* if (kind == svn_node_file) */\n+\t\t/* { */\n+\t\t/* \tsvn_checksum_t *checksum; */\n+\t\t/* \tconst char *hex_digest; */\n+\t\t/* \tSVN_ERR(svn_fs_contents_changed(&must_dump_text, */\n+\t\t/* \t                                compare_root, compare_path, */\n+\t\t/* \t                                eb->fs_root, path, pool)); */\n+\n+\t\t/* \tSVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, */\n+\t\t/* \t                             compare_root, compare_path, */\n+\t\t/* \t                             TRUE, pool)); */\n+\t\t/* \thex_digest = svn_checksum_to_cstring(checksum, pool); */\n+\t\t/* \tif (hex_digest) */\n+\t\t/* \t\tSVN_ERR(svn_stream_printf(eb->stream, pool, */\n+\t\t/* \t\t                          SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_MD5 */\n+\t\t/* \t\t                          \": %s\\n\", hex_digest)); */\n+\n+\t\t/* \tSVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1, */\n+\t\t/* \t                             compare_root, compare_path, */\n+\t\t/* \t                             TRUE, pool)); */\n+\t\t/* \thex_digest = svn_checksum_to_cstring(checksum, pool); */\n+\t\t/* \tif (hex_digest) */\n+\t\t/* \t\tSVN_ERR(svn_stream_printf(eb->stream, pool, */\n+\t\t/* \t\t                          SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_SHA1 */\n+\t\t/* \t\t                          \": %s\\n\", hex_digest)); */\n+\t\t/* } */\n+\t\tbreak;\n+\t}\n+\tif (!must_dump_text && !must_dump_props) {\n+\t\tlen = 2;\n+\t\treturn svn_stream_write(eb->stream, \"\\n\\n\", &len); /* ### needed? */\n+\t}\n+\treturn SVN_NO_ERROR;\n+}\n+svn_error_t *open_root(void *edit_baton,\n+                       svn_revnum_t base_revision,\n+                       apr_pool_t *pool,\n+                       void **root_baton)\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-\n-svn_error_t *\n-delete_entry(const char *path,\n-             svn_revnum_t revision,\n-             void *parent_baton,\n-             apr_pool_t *pool)\n+svn_error_t *delete_entry(const char *path,\n+                          svn_revnum_t revision,\n+                          void *parent_baton,\n+                          apr_pool_t *pool)\n {\n \tstruct dir_baton *pb = parent_baton;\n \tconst char *mypath = apr_pstrdup(pb->pool, path);\n@@ -139,14 +282,12 @@ delete_entry(const char *path,\n \treturn SVN_NO_ERROR;\n }\n \n-\n-svn_error_t *\n-add_directory(const char *path,\n-              void *parent_baton,\n-              const char *copyfrom_path,\n-              svn_revnum_t copyfrom_rev,\n-              apr_pool_t *pool,\n-              void **child_baton)\n+svn_error_t *add_directory(const char *path,\n+                           void *parent_baton,\n+                           const char *copyfrom_path,\n+                           svn_revnum_t copyfrom_rev,\n+                           apr_pool_t *pool,\n+                           void **child_baton)\n {\n \tstruct dir_baton *pb = parent_baton;\n \tstruct edit_baton *eb = pb->edit_baton;\n@@ -162,13 +303,13 @@ add_directory(const char *path,\n \tis_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);\n \n \t/* Dump the node. */\n-\t/* SVN_ERR(dump_node(eb, path, */\n-\t/*                   svn_node_dir, */\n-\t/*                   val ? svn_node_action_replace : svn_node_action_add, */\n-\t/*                   is_copy, */\n-\t/*                   is_copy ? copyfrom_path : NULL, */\n-\t/*                   is_copy ? copyfrom_rev : SVN_INVALID_REVNUM, */\n-\t/*                   pool)); */\n+\tSVN_ERR(dump_node(eb, path,\n+\t                  svn_node_dir,\n+\t                  val ? svn_node_action_replace : svn_node_action_add,\n+\t                  is_copy,\n+\t                  is_copy ? copyfrom_path : NULL,\n+\t                  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@@ -180,13 +321,11 @@ add_directory(const char *path,\n \treturn SVN_NO_ERROR;\n }\n \n-\n-svn_error_t *\n-open_directory(const char *path,\n-               void *parent_baton,\n-               svn_revnum_t base_revision,\n-               apr_pool_t *pool,\n-               void **child_baton)\n+svn_error_t *open_directory(const char *path,\n+                            void *parent_baton,\n+                            svn_revnum_t base_revision,\n+                            apr_pool_t *pool,\n+                            void **child_baton)\n {\n \tstruct dir_baton *pb = parent_baton;\n \tstruct edit_baton *eb = pb->edit_baton;\n@@ -196,8 +335,7 @@ open_directory(const char *path,\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{\n+\tif (pb && ARE_VALID_COPY_ARGS(pb->cmp_path, pb->cmp_rev)) {\n \t\tcmp_path = svn_path_join(pb->cmp_path,\n \t\t                         svn_dirent_basename(path, pool), pool);\n \t\tcmp_rev = pb->cmp_rev;\n@@ -208,10 +346,8 @@ open_directory(const char *path,\n \treturn SVN_NO_ERROR;\n }\n \n-\n-svn_error_t *\n-close_directory(void *dir_baton,\n-                apr_pool_t *pool)\n+svn_error_t *close_directory(void *dir_baton,\n+                             apr_pool_t *pool)\n {\n \tstruct dir_baton *db = dir_baton;\n \tstruct edit_baton *eb = db->edit_baton;\n@@ -220,8 +356,7 @@ close_directory(void *dir_baton,\n \n \tfor (hi = apr_hash_first(pool, db->deleted_entries);\n \t     hi;\n-\t     hi = apr_hash_next(hi))\n-\t{\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@@ -232,23 +367,21 @@ close_directory(void *dir_baton,\n \t\t/* By sending 'svn_node_unknown', the Node-kind: header simply won't\n \t\t   be written out.  No big deal at all, really.  The loader\n \t\t   shouldn't care.  */\n-\t\t/* SVN_ERR(dump_node(eb, path, */\n-\t\t/*                   svn_node_unknown, svn_node_action_delete, */\n-\t\t/*                   FALSE, NULL, SVN_INVALID_REVNUM, subpool)); */\n+\t\tSVN_ERR(dump_node(eb, path,\n+\t\t                  svn_node_unknown, svn_node_action_delete,\n+\t\t                  FALSE, NULL, SVN_INVALID_REVNUM, subpool));\n \t}\n \n \tsvn_pool_destroy(subpool);\n \treturn SVN_NO_ERROR;\n }\n \n-\n-svn_error_t *\n-add_file(const char *path,\n-         void *parent_baton,\n-         const char *copyfrom_path,\n-         svn_revnum_t copyfrom_rev,\n-         apr_pool_t *pool,\n-         void **file_baton)\n+svn_error_t *add_file(const char *path,\n+                      void *parent_baton,\n+                      const char *copyfrom_path,\n+                      svn_revnum_t copyfrom_rev,\n+                      apr_pool_t *pool,\n+                      void **file_baton)\n {\n \tstruct dir_baton *pb = parent_baton;\n \tstruct edit_baton *eb = pb->edit_baton;\n@@ -262,29 +395,29 @@ add_file(const char *path,\n \tis_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);\n \n \t/* Dump the node. */\n-\t/* SVN_ERR(dump_node(eb, path, */\n-\t/*                   svn_node_file, */\n-\t/*                   val ? svn_node_action_replace : svn_node_action_add, */\n-\t/*                   is_copy, */\n-\t/*                   is_copy ? copyfrom_path : NULL, */\n-\t/*                   is_copy ? copyfrom_rev : SVN_INVALID_REVNUM, */\n-\t/*                   pool)); */\n+\tSVN_ERR(dump_node(eb, path,\n+\t                  svn_node_file,\n+\t                  val ? svn_node_action_replace : svn_node_action_add,\n+\t                  is_copy,\n+\t                  is_copy ? copyfrom_path : NULL,\n+\t                  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*file_baton = NULL;  /* muhahahaha */\n+\t/* TODO: Store the delta in file_baton */\n+\t*file_baton = NULL;\n \treturn SVN_NO_ERROR;\n }\n \n \n-svn_error_t *\n-open_file(const char *path,\n-          void *parent_baton,\n-          svn_revnum_t ancestor_revision,\n-          apr_pool_t *pool,\n-          void **file_baton)\n+svn_error_t *open_file(const char *path,\n+                       void *parent_baton,\n+                       svn_revnum_t ancestor_revision,\n+                       apr_pool_t *pool,\n+                       void **file_baton)\n {\n \tstruct dir_baton *pb = parent_baton;\n \tstruct edit_baton *eb = pb->edit_baton;\n@@ -293,26 +426,25 @@ open_file(const char *path,\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{\n+\tif (pb && ARE_VALID_COPY_ARGS(pb->cmp_path, pb->cmp_rev)) {\n \t\tcmp_path = svn_path_join(pb->cmp_path,\n \t\t                         svn_dirent_basename(path, pool), pool);\n \t\tcmp_rev = pb->cmp_rev;\n \t}\n \n-\t/* SVN_ERR(dump_node(eb, path, */\n-\t/*                   svn_node_file, svn_node_action_change, */\n-\t/*                   FALSE, cmp_path, cmp_rev, pool)); */\n+\tSVN_ERR(dump_node(eb, path,\n+\t                  svn_node_file, svn_node_action_change,\n+\t                  FALSE, cmp_path, cmp_rev, pool));\n \n-\t*file_baton = NULL;  /* muhahahaha again */\n+\t/* TODO: Store the delta in file_baton */\n+\t*file_baton = NULL;\n \treturn SVN_NO_ERROR;\n }\n \n-svn_error_t *\n-change_dir_prop(void *parent_baton,\n-                const char *name,\n-                const svn_string_t *value,\n-                apr_pool_t *pool)\n+svn_error_t *change_dir_prop(void *parent_baton,\n+                             const char *name,\n+                             const svn_string_t *value,\n+                             apr_pool_t *pool)\n {\n \tstruct dir_baton *db = parent_baton;\n \tstruct edit_baton *eb = db->edit_baton;\n@@ -320,35 +452,27 @@ change_dir_prop(void *parent_baton,\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{\n-\t\t/* SVN_ERR(dump_node(eb, db->path, */\n-\t\t/*                   svn_node_dir, svn_node_action_change, */\n-\t\t/*                   FALSE, db->cmp_path, db->cmp_rev, pool)); */\n+\tif (! db->written_out) {\n+\t\tSVN_ERR(dump_node(eb, db->path,\n+\t\t                  svn_node_dir, svn_node_action_change,\n+\t\t                  FALSE, db->cmp_path, db->cmp_rev, pool));\n \t\tdb->written_out = TRUE;\n \t}\n \treturn SVN_NO_ERROR;\n }\n \n-svn_error_t *\n-get_dump_editor(const svn_delta_editor_t **editor,\n-                void **edit_baton,\n-                svn_revnum_t to_rev,\n-                apr_pool_t *pool)\n+svn_error_t *get_dump_editor(const svn_delta_editor_t **editor,\n+                             void **edit_baton,\n+                             svn_revnum_t to_rev,\n+                             apr_pool_t *pool)\n {\n-\t/* Allocate an edit baton to be stored in every directory baton.\n-\t   Set it up for the directory baton we create here, which is the\n-\t   root baton. */\n \tstruct edit_baton *eb = apr_pcalloc(pool, sizeof(*eb));\n \tsvn_delta_editor_t *dump_editor = svn_delta_default_editor(pool);\n \n-\t/* Set up the edit baton. */\n \tsvn_stream_for_stdout(&(eb->stream), pool);\n \teb->bufsize = sizeof(eb->buffer);\n \teb->current_rev = to_rev;\n \n-\n-\t/* Set up the editor. */\n \tdump_editor->open_root = open_root;\n \tdump_editor->delete_entry = delete_entry;\n \tdump_editor->add_directory = add_directory;\ndiff --git a/svnclient_ra.c b/svnclient_ra.c\nindex 24d99cb..0b9e002 100644\n--- a/svnclient_ra.c\n+++ b/svnclient_ra.c\n@@ -109,8 +109,8 @@ svn_error_t *replay_range(svn_revnum_t start_revision, svn_revnum_t end_revision\n \t                                    dump_baton, pool));\n \t\n \treplay_baton_t *replay_baton = apr_palloc(pool, sizeof(replay_baton_t));\n-\treplay_baton->editor = debug_editor;\n-\treplay_baton->baton = debug_baton;\n+\treplay_baton->editor = dump_editor;\n+\treplay_baton->baton = dump_baton;\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@@ -125,7 +125,7 @@ void close_connection()\n int main()\n {\n \tconst char url[] = \"http://svn.apache.org/repos/asf\";\n-\tsvn_revnum_t start_revision = 1, end_revision = 5;\n+\tsvn_revnum_t start_revision = 1, end_revision = 50;\n \tif (svn_cmdline_init (\"svnclient_ra\", stderr) != EXIT_SUCCESS)\n \t\treturn 1;\n \tpool = svn_pool_create(NULL);\n-- \n1.7.1\n"},{"id":"144072","messageId":"1277310140-16891-5-git-send-email-artagnon@gmail.com","threadId":"24173","inReplyTo":"1277310140-16891-1-git-send-email-artagnon@gmail.com","subject":"[WIP PATCH 4/7] Replace deprecated svn_path_join","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-06-23T16:22:17Z","receivedAt":"2010-06-23T16:22:17Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Strip the deprecated svn_path_join API, using svn_path_compose\ninstead. Re-add -Werror to the Makefile.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n Makefile      |    2 +-\n dump_editor.c |   21 ++++++++++++++-------\n 2 files changed, 15 insertions(+), 8 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 269c406..cf7fef7 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1,4 +1,4 @@\n svnclient_ra: *.c *.h\n-\t$(CC) -Wall -ggdb3 -O0 -o $@ svnclient_ra.c debug_editor.c dump_editor.c -lsvn_client-1 -I. -I/usr/local/include/subversion-1 -I/usr/include/apr-1.0\n+\t$(CC) -Wall -Werror -ggdb3 -O0 -o $@ svnclient_ra.c debug_editor.c dump_editor.c -lsvn_client-1 -I. -I/usr/local/include/subversion-1 -I/usr/include/apr-1.0\n clean:\n \t$(RM) svnclient_ra\ndiff --git a/dump_editor.c b/dump_editor.c\nindex ba0630f..00c838a 100644\n--- a/dump_editor.c\n+++ b/dump_editor.c\n@@ -80,14 +80,17 @@ struct dir_baton *make_dir_baton(const char *path,\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-\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\tfull_path = svn_path_join(\"/\", path, pool);\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@@ -332,12 +335,14 @@ svn_error_t *open_directory(const char *path,\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/* 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\tcmp_path = svn_path_join(pb->cmp_path,\n-\t\t                         svn_dirent_basename(path, pool), pool);\n+\t\tAPR_ARRAY_PUSH(compose_path, const char *) = pb->cmp_path;\n+\t\tAPR_ARRAY_PUSH(compose_path, const char *) = svn_dirent_basename(path, pool);\n+\t\tcmp_path = svn_path_compose(compose_path, pool);\n \t\tcmp_rev = pb->cmp_rev;\n \t}\n \n@@ -424,11 +429,13 @@ svn_error_t *open_file(const char *path,\n \tconst char *cmp_path = NULL;\n \tsvn_revnum_t cmp_rev = SVN_INVALID_REVNUM;\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\tcmp_path = svn_path_join(pb->cmp_path,\n-\t\t                         svn_dirent_basename(path, pool), pool);\n+\t\tAPR_ARRAY_PUSH(compose_path, const char *) = pb->cmp_path;\n+\t\tAPR_ARRAY_PUSH(compose_path, const char *) = svn_dirent_basename(path, pool);\n+\t\tcmp_path = svn_path_compose(compose_path, pool);\n \t\tcmp_rev = pb->cmp_rev;\n \t}\n \n-- \n1.7.1\n"},{"id":"144069","messageId":"1277310140-16891-6-git-send-email-artagnon@gmail.com","threadId":"24173","inReplyTo":"1277310140-16891-1-git-send-email-artagnon@gmail.com","subject":"[WIP PATCH 5/7] Trigger dump_node in change_dir_prop","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-06-23T16:22:18Z","receivedAt":"2010-06-23T16:22:18Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Trigger dump_node to dump property length in change_dir_prop. Use the\nedit_baton to store the changed property before dumping it.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n dump_editor.c |  111 +++++++++++++++++++++++++++++++++++++++++++++-----------\n 1 files changed, 89 insertions(+), 22 deletions(-)\n\ndiff --git a/dump_editor.c b/dump_editor.c\nindex 00c838a..a80d711 100644\n--- a/dump_editor.c\n+++ b/dump_editor.c\n@@ -15,12 +15,22 @@\n \n #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))\n \n+static svn_boolean_t must_dump_text = FALSE, must_dump_props = FALSE;\n+static svn_boolean_t text_changed = FALSE, props_changed = FALSE;\n+\n struct edit_baton {\n \t/* The stream to dump to: stdout */\n \tsvn_stream_t *stream;\n \t\n \tsvn_revnum_t current_rev;\n \t\n+\t/* Store the properties that changed */\n+\tapr_hash_t *properties;\n+\tapr_hash_t *del_properties; /* Value is always 0x1 */\n+\n+\t/* Store the text delta */\n+\tconst char *delta_text;\n+\n \t/* reusable buffer for writing file contents */\n \tchar buffer[SVN__STREAM_CHUNK_SIZE];\n \tapr_size_t bufsize;\n@@ -56,6 +66,41 @@ struct dir_baton {\n \tapr_pool_t *pool;\n };\n \n+static void write_hash_to_stringbuf(apr_hash_t *hash,\n+                                    svn_stringbuf_t **strbuf,\n+                                    apr_pool_t *pool) {\n+\tapr_hash_index_t *this;\n+\t*strbuf = svn_stringbuf_create(\"\", pool);\n+\n+\tfor (this = apr_hash_first(pool, hash); this; this = apr_hash_next(this))\n+\t{\n+\t\tconst void *key;\n+\t\tvoid *val;\n+\t\tapr_ssize_t keylen;\n+\t\tsvn_string_t *value;\n+\n+\t\t/* Get this key and val. */\n+\t\tapr_hash_this(this, &key, &keylen, &val);\n+\t\tvalue = val;\n+\n+\t\t/* Output name length, then name. */\n+\t\tsvn_stringbuf_appendcstr(*strbuf,\n+\t\t                         apr_psprintf(pool, \"K %\" APR_SSIZE_T_FMT \"\\n\",\n+\t\t                                      keylen));\n+\n+\t\tsvn_stringbuf_appendbytes(*strbuf, (const char *) key, keylen);\n+\t\tsvn_stringbuf_appendbytes(*strbuf, \"\\n\", 1);\n+\n+\t\t/* Output value length, then value. */\n+\t\tsvn_stringbuf_appendcstr(*strbuf,\n+\t\t                         apr_psprintf(pool, \"V %\" APR_SIZE_T_FMT \"\\n\",\n+\t\t                                      value->len));\n+\n+\t\tsvn_stringbuf_appendbytes(*strbuf, value->data, value->len);\n+\t\tsvn_stringbuf_appendbytes(*strbuf, \"\\n\", 1);\n+\t}\n+\tsvn_stringbuf_appendbytes(*strbuf, \"PROPS-END\\n\", 10);\n+}\n \n /* Make a directory baton to represent the directory was path\n    (relative to EDIT_BATON's path) is PATH.\n@@ -120,10 +165,11 @@ static svn_error_t *dump_node(struct edit_baton *eb,\n                               svn_revnum_t cmp_rev,\n                               apr_pool_t *pool)\n {\n-\tapr_size_t len;\n-\tsvn_boolean_t must_dump_text = TRUE, must_dump_props = TRUE;\n+\tapr_size_t len, proplen;\n \tconst char *compare_path = path;\n \tsvn_revnum_t compare_rev = eb->current_rev - 1;\n+\tsvn_stringbuf_t *propstring;\n+\tsvn_filesize_t content_length = 0;\n \n \t/* Write out metadata headers for this file node. */\n \tSVN_ERR(svn_stream_printf(eb->stream, pool,\n@@ -153,13 +199,10 @@ static svn_error_t *dump_node(struct edit_baton *eb,\n \t\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n \t\t                          SVN_REPOS_DUMPFILE_NODE_ACTION\n \t\t                          \": change\\n\"));\n-\n-\t\t/* either the text or props changed, or possibly both. */\n-\t\t/* SVN_ERR(svn_props_changed(&must_dump_props, */\n-\t\t/*                           compare_path, path, pool)); */\n+\t\t\n+\t\tmust_dump_props = props_changed;\n \t\tif (kind == svn_node_file)\n-\t\t\t/* SVN_ERR(svn_contents_changed(&must_dump_text, */\n-\t\t\t/*                              compare_path, path, pool)); */\n+\t\t\tmust_dump_text = text_changed;\n \t\tbreak;\n       \n \tcase svn_node_action_replace:\n@@ -223,28 +266,24 @@ static svn_error_t *dump_node(struct edit_baton *eb,\n \t\t                          \": %s\\n\",\n \t\t                          cmp_rev, cmp_path));\n \n-\t\t/* Need to decide if the copied node had any extra textual or\n-\t\t   property mods as well.  */\n-\t\t/* SVN_ERR(svn_fs_props_changed(&must_dump_props, */\n-\t\t/*                              compare_path, path, pool)); */\n-\t\t/* if (kind == svn_node_file) */\n-\t\t/* { */\n+\t\tmust_dump_props = props_changed;\n+\t\t/* if (kind == svn_node_file) { */\n \t\t/* \tsvn_checksum_t *checksum; */\n \t\t/* \tconst char *hex_digest; */\n-\t\t/* \tSVN_ERR(svn_fs_contents_changed(&must_dump_text, */\n-\t\t/* \t                                compare_root, compare_path, */\n-\t\t/* \t                                eb->fs_root, path, pool)); */\n+\t\t/* \tmust_dump_text = text_changed; */\n \n-\t\t/* \tSVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, */\n-\t\t/* \t                             compare_root, compare_path, */\n-\t\t/* \t                             TRUE, pool)); */\n+\t\t/* \t/\\* MD5 checksum *\\/ */\n+\t\t/* \tSVN_ERR(svn_checksum(&checksum, svn_checksum_md5, */\n+\t\t/* \t                     compare_root, compare_path, */\n+\t\t/* \t                     TRUE, pool)); */\n \t\t/* \thex_digest = svn_checksum_to_cstring(checksum, pool); */\n \t\t/* \tif (hex_digest) */\n \t\t/* \t\tSVN_ERR(svn_stream_printf(eb->stream, pool, */\n \t\t/* \t\t                          SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_MD5 */\n \t\t/* \t\t                          \": %s\\n\", hex_digest)); */\n \n-\t\t/* \tSVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1, */\n+\t\t/* \t/\\* SHA1 checksum *\\/ */\n+\t\t/* \tSVN_ERR(svn_checksum(&checksum, svn_checksum_sha1, */\n \t\t/* \t                             compare_root, compare_path, */\n \t\t/* \t                             TRUE, pool)); */\n \t\t/* \thex_digest = svn_checksum_to_cstring(checksum, pool); */\n@@ -257,7 +296,23 @@ static svn_error_t *dump_node(struct edit_baton *eb,\n \t}\n \tif (!must_dump_text && !must_dump_props) {\n \t\tlen = 2;\n-\t\treturn svn_stream_write(eb->stream, \"\\n\\n\", &len); /* ### needed? */\n+\t\treturn svn_stream_write(eb->stream, \"\\n\\n\", &len);\n+\t}\n+\n+\t/* If we are supposed to dump properties, write out a property\n+\t   length header and generate a stringbuf that contains those\n+\t   property values here. */\n+\tif (must_dump_props) {\n+\t\twrite_hash_to_stringbuf(eb->properties, &propstring, pool);\n+\t\tproplen = propstring->len;\n+\t\tcontent_length += proplen;\n+\t\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t\t                          SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH\n+\t\t                          \": %\" APR_SIZE_T_FMT \"\\n\", proplen));\n+\t}\n+\tif (must_dump_text && (kind == svn_node_file)) {\n+\t\t/* svn_stream_t *contents; */\n+\t\t;\n \t}\n \treturn SVN_NO_ERROR;\n }\n@@ -456,6 +511,15 @@ svn_error_t *change_dir_prop(void *parent_baton,\n \tstruct dir_baton *db = parent_baton;\n \tstruct edit_baton *eb = db->edit_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(eb->properties, apr_pstrdup(pool, name),\n+\t                     APR_HASH_KEY_STRING, svn_string_dup(value, pool)) :\n+\t\tapr_hash_set(eb->del_properties, apr_pstrdup(pool, name),\n+\t\t             APR_HASH_KEY_STRING, (void *)0x1);\n+\tprops_changed = TRUE;\n+\t\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@@ -479,6 +543,9 @@ svn_error_t *get_dump_editor(const svn_delta_editor_t **editor,\n \tsvn_stream_for_stdout(&(eb->stream), pool);\n \teb->bufsize = sizeof(eb->buffer);\n \teb->current_rev = to_rev;\n+\teb->properties = apr_hash_make(pool);\n+\teb->del_properties = apr_hash_make(pool);\n+\teb->delta_text = apr_pcalloc(pool, sizeof(const char *));\n \n \tdump_editor->open_root = open_root;\n \tdump_editor->delete_entry = delete_entry;\n-- \n1.7.1\n"},{"id":"144075","messageId":"1277310140-16891-7-git-send-email-artagnon@gmail.com","threadId":"24173","inReplyTo":"1277310140-16891-1-git-send-email-artagnon@gmail.com","subject":"[WIP PATCH 6/7] Add file_baton and trigger dump_node in change_file_prop","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-06-23T16:22:19Z","receivedAt":"2010-06-23T16:22:19Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Add a file_baton structure, and fill it in in open_file and add_file\nfunctions. It is to be used by apply_textdelta and change_file_prop\nfunctions. Trigger dump_node in change_file_prop. Dump the actual\nproperties along with the property lengths.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n dump_editor.c |  100 ++++++++++++++++++++++++++++++++++++++++++++++++++------\n 1 files changed, 89 insertions(+), 11 deletions(-)\n\ndiff --git a/dump_editor.c b/dump_editor.c\nindex a80d711..7bd00a4 100644\n--- a/dump_editor.c\n+++ b/dump_editor.c\n@@ -15,7 +15,6 @@\n \n #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))\n \n-static svn_boolean_t must_dump_text = FALSE, must_dump_props = FALSE;\n static svn_boolean_t text_changed = FALSE, props_changed = FALSE;\n \n struct edit_baton {\n@@ -66,9 +65,16 @@ struct dir_baton {\n \tapr_pool_t *pool;\n };\n \n+struct file_baton {\n+\t/* Store the path of the file */\n+\tconst char *path;\n+\tstruct edit_baton *eb;\n+};\n+\n static void write_hash_to_stringbuf(apr_hash_t *hash,\n                                     svn_stringbuf_t **strbuf,\n-                                    apr_pool_t *pool) {\n+                                    apr_pool_t *pool)\n+{\n \tapr_hash_index_t *this;\n \t*strbuf = svn_stringbuf_create(\"\", pool);\n \n@@ -120,7 +126,8 @@ struct dir_baton *make_dir_baton(const char *path,\n                                  void *edit_baton,\n                                  void *parent_dir_baton,\n                                  svn_boolean_t added,\n-                                 apr_pool_t *pool) {\n+                                 apr_pool_t *pool)\n+{\n \tstruct 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@@ -156,6 +163,18 @@ struct dir_baton *make_dir_baton(const char *path,\n \treturn new_db;\n }\n \n+struct file_baton *make_file_baton(struct edit_baton *eb,\n+                                   const char *path,\n+                                   apr_pool_t *pool)\n+{\n+\tstruct file_baton *new_fb = apr_pcalloc(pool, sizeof(struct file_baton));\n+\tnew_fb->path = apr_pcalloc(pool, sizeof(const char *));\n+\tnew_fb->path = path;\n+\tnew_fb->eb = eb;\n+\n+\treturn new_fb;\n+}\n+\n static svn_error_t *dump_node(struct edit_baton *eb,\n                               const char *path,    /* an absolute path. */\n                               svn_node_kind_t kind,\n@@ -170,6 +189,7 @@ static svn_error_t *dump_node(struct edit_baton *eb,\n \tsvn_revnum_t compare_rev = eb->current_rev - 1;\n \tsvn_stringbuf_t *propstring;\n \tsvn_filesize_t content_length = 0;\n+\tsvn_boolean_t must_dump_text = FALSE, must_dump_props = FALSE;\n \n \t/* Write out metadata headers for this file node. */\n \tSVN_ERR(svn_stream_printf(eb->stream, pool,\n@@ -310,10 +330,44 @@ static svn_error_t *dump_node(struct edit_baton *eb,\n \t\t                          SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH\n \t\t                          \": %\" APR_SIZE_T_FMT \"\\n\", proplen));\n \t}\n+\n+\t/* If we are supposed to dump text, write out a text length header\n+\t   here, and an MD5 checksum (if available). */\n \tif (must_dump_text && (kind == svn_node_file)) {\n-\t\t/* svn_stream_t *contents; */\n \t\t;\n \t}\n+\n+\t/* 'Content-length:' is the last header before we dump the content,\n+\t   and is the sum of the text and prop contents lengths.  We write\n+\t   this only for the benefit of non-Subversion RFC-822 parsers. */\n+\tSVN_ERR(svn_stream_printf(eb->stream, pool,\n+\t                          SVN_REPOS_DUMPFILE_CONTENT_LENGTH\n+\t                          \": %\" SVN_FILESIZE_T_FMT \"\\n\\n\",\n+\t                          content_length));\n+\n+\t/* Now dump the text and properties */\n+\tif (must_dump_props)\n+\t{\n+\t\tlen = propstring->len;\n+\t\tSVN_ERR(svn_stream_write(eb->stream, propstring->data, &len));\n+\t}\n+\t/* if (must_dump_text && (kind == svn_node_file)) */\n+\t/* { */\n+\t/* \tsvn_stream_t *contents; */\n+\n+\t/* \tif (delta_file) */\n+\t/* \t{ */\n+\t/* \t\t/\\* Make sure to close the underlying file when the stream is */\n+\t/* \t\t   closed. *\\/ */\n+\t/* \t\tcontents = svn_stream_from_aprfile2(delta_file, FALSE, pool); */\n+\t/* \t} */\n+\t/* \telse */\n+\t/* \t\tSVN_ERR(svn_fs_file_contents(&contents, eb->fs_root, path, pool)); */\n+\n+\t/* \tSVN_ERR(svn_stream_copy3(contents, svn_stream_disown(eb->stream, pool), */\n+\t/* \t                         NULL, NULL, pool)); */\n+\t/* } */\n+\n \treturn SVN_NO_ERROR;\n }\n svn_error_t *open_root(void *edit_baton,\n@@ -467,12 +521,12 @@ svn_error_t *add_file(const char *path,\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/* TODO: Store the delta in file_baton */\n-\t*file_baton = NULL;\n+\t/* Build a nice file baton to pass to change_file_prop and apply_textdelta */\n+\t*file_baton = make_file_baton(eb, path, pool);\n+\n \treturn SVN_NO_ERROR;\n }\n \n-\n svn_error_t *open_file(const char *path,\n                        void *parent_baton,\n                        svn_revnum_t ancestor_revision,\n@@ -497,9 +551,10 @@ svn_error_t *open_file(const char *path,\n \tSVN_ERR(dump_node(eb, path,\n \t                  svn_node_file, svn_node_action_change,\n \t                  FALSE, cmp_path, cmp_rev, pool));\n-\n-\t/* TODO: Store the delta in file_baton */\n-\t*file_baton = NULL;\n+\t\n+\t/* Build a nice file baton to pass to change_file_prop and apply_textdelta */\n+\t*file_baton = make_file_baton(eb, path, pool);\n+\t\n \treturn SVN_NO_ERROR;\n }\n \n@@ -532,6 +587,29 @@ svn_error_t *change_dir_prop(void *parent_baton,\n \treturn SVN_NO_ERROR;\n }\n \n+svn_error_t *change_file_prop(void *file_baton,\n+                              const char *name,\n+                              const svn_string_t *value,\n+                              apr_pool_t *pool)\n+{\n+\tstruct file_baton *fb = file_baton;\n+\t\n+\tif (svn_property_kind(NULL, name) != svn_prop_regular_kind)\n+\t\treturn SVN_NO_ERROR;\n+\n+\tvalue ? apr_hash_set(fb->eb->properties, apr_pstrdup(pool, name),\n+\t                     APR_HASH_KEY_STRING, svn_string_dup(value, pool)) :\n+\t\tapr_hash_set(fb->eb->del_properties, apr_pstrdup(pool, name),\n+\t\t             APR_HASH_KEY_STRING, (void *)0x1);\n+\tprops_changed = TRUE;\n+\t\n+\tSVN_ERR(dump_node(fb->eb, fb->path,\n+\t                  svn_node_dir, svn_node_action_change,\n+\t                  FALSE, NULL, 0, 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 to_rev,\n@@ -545,7 +623,6 @@ svn_error_t *get_dump_editor(const svn_delta_editor_t **editor,\n \teb->current_rev = to_rev;\n \teb->properties = apr_hash_make(pool);\n \teb->del_properties = apr_hash_make(pool);\n-\teb->delta_text = apr_pcalloc(pool, sizeof(const char *));\n \n \tdump_editor->open_root = open_root;\n \tdump_editor->delete_entry = delete_entry;\n@@ -553,6 +630,7 @@ svn_error_t *get_dump_editor(const svn_delta_editor_t **editor,\n \tdump_editor->open_directory = open_directory;\n \tdump_editor->close_directory = close_directory;\n \tdump_editor->change_dir_prop = change_dir_prop;\n+\tdump_editor->change_file_prop = change_file_prop;\t\n \tdump_editor->add_file = add_file;\n \tdump_editor->open_file = open_file;\n \n-- \n1.7.1\n"},{"id":"144074","messageId":"1277310140-16891-8-git-send-email-artagnon@gmail.com","threadId":"24173","inReplyTo":"1277310140-16891-1-git-send-email-artagnon@gmail.com","subject":"[WIP PATCH 7/7] Dump the text delta","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-06-23T16:22:20Z","receivedAt":"2010-06-23T16:22:20Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Add a handler and handler_baton to handle text deltas in the editor\nbaton. Currently, program exits with code 01 because delta source ends\nunexpectedly.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n dump_editor.c |   51 +++++++++++++++++++++++++++++++--------------------\n 1 files changed, 31 insertions(+), 20 deletions(-)\n\ndiff --git a/dump_editor.c b/dump_editor.c\nindex 7bd00a4..2192177 100644\n--- a/dump_editor.c\n+++ b/dump_editor.c\n@@ -27,12 +27,13 @@ struct edit_baton {\n \tapr_hash_t *properties;\n \tapr_hash_t *del_properties; /* Value is always 0x1 */\n \n-\t/* Store the text delta */\n-\tconst char *delta_text;\n-\n \t/* reusable buffer for writing file contents */\n \tchar buffer[SVN__STREAM_CHUNK_SIZE];\n \tapr_size_t bufsize;\n+\n+\t/* The txdelta handler and handler baton */\n+\tsvn_txdelta_window_handler_t *handler;\n+\tvoid **handler_baton;\n };\n \n struct dir_baton {\n@@ -351,22 +352,9 @@ static svn_error_t *dump_node(struct edit_baton *eb,\n \t\tlen = propstring->len;\n \t\tSVN_ERR(svn_stream_write(eb->stream, propstring->data, &len));\n \t}\n-\t/* if (must_dump_text && (kind == svn_node_file)) */\n-\t/* { */\n-\t/* \tsvn_stream_t *contents; */\n-\n-\t/* \tif (delta_file) */\n-\t/* \t{ */\n-\t/* \t\t/\\* Make sure to close the underlying file when the stream is */\n-\t/* \t\t   closed. *\\/ */\n-\t/* \t\tcontents = svn_stream_from_aprfile2(delta_file, FALSE, pool); */\n-\t/* \t} */\n-\t/* \telse */\n-\t/* \t\tSVN_ERR(svn_fs_file_contents(&contents, eb->fs_root, path, pool)); */\n-\n-\t/* \tSVN_ERR(svn_stream_copy3(contents, svn_stream_disown(eb->stream, pool), */\n-\t/* \t                         NULL, NULL, pool)); */\n-\t/* } */\n+\tif (must_dump_text && (kind == svn_node_file) && eb->handler) {\n+\t\tsvn_txdelta_to_svndiff2(eb->handler, eb->handler_baton, eb->stream, 0, pool);\n+\t}\n \n \treturn SVN_NO_ERROR;\n }\n@@ -610,6 +598,28 @@ svn_error_t *change_file_prop(void *file_baton,\n \treturn SVN_NO_ERROR;\n }\n \n+svn_error_t *apply_textdelta(void *file_baton, const char *base_checksum,\n+                             apr_pool_t *pool,\n+                             svn_txdelta_window_handler_t *handler,\n+                             void **handler_baton)\n+{\n+\tstruct file_baton *fb = file_baton;\n+\tsvn_stream_t *source_stream = svn_stream_empty(pool);\n+\tsvn_txdelta_apply(source_stream, fb->eb->stream,\n+\t                  NULL, fb->path, pool, handler,\n+\t                  handler_baton);\n+\tfb->eb->handler = handler;\n+\tfb->eb->handler_baton = handler_baton;\n+\n+\ttext_changed = TRUE;\n+\n+\tSVN_ERR(dump_node(fb->eb, fb->path,\n+\t                  svn_node_dir, svn_node_action_change,\n+\t                  FALSE, NULL, 0, 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 to_rev,\n@@ -630,7 +640,8 @@ svn_error_t *get_dump_editor(const svn_delta_editor_t **editor,\n \tdump_editor->open_directory = open_directory;\n \tdump_editor->close_directory = close_directory;\n \tdump_editor->change_dir_prop = change_dir_prop;\n-\tdump_editor->change_file_prop = change_file_prop;\t\n+\tdump_editor->change_file_prop = change_file_prop;\n+\tdump_editor->apply_textdelta = apply_textdelta;\n \tdump_editor->add_file = add_file;\n \tdump_editor->open_file = open_file;\n \n-- \n1.7.1\n"},{"id":"144077","messageId":"AANLkTilNcL5sKVPo9nGytHoSh8qPwsi1ldKr2hu6oHpU@mail.gmail.com","threadId":"24173","inReplyTo":"1277310140-16891-4-git-send-email-artagnon@gmail.com","subject":"Re: [WIP PATCH 3/7] Import dump_node to dump what changed and cleanup whitespace","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-06-23T17:05:22Z","receivedAt":"2010-06-23T17:05:22Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nRamkumar Ramachandra wrote:\n> +Licensed to the Apache Software Foundation (ASF) under one or more\n> +contributor license agreements.  See the NOTICE file distributed with\n> +this work for additional information regarding copyright ownership.\n> +The ASF licenses this file to you under the Apache License, Version\n> +2.0 (the \"License\"); you may not use this file except in compliance\n> +with the License.  You may obtain a copy of the License at\n\nDaniel pointed out that my code isn't licensed to the ASF before it's\nmerged back into Subversion; the dump.c I've taken directly from the\nSubversion trunk is though. I'll find out the exact semantics and\ncorrect this.\n\n-- Ram\n"},{"id":"144078","messageId":"AANLkTik1fV1yYZL5mKbVfGKOao7VjzMsg2D_7FkL8nR2@mail.gmail.com","threadId":"24173","inReplyTo":"1277310140-16891-1-git-send-email-artagnon@gmail.com","subject":"Re: [GSoC update] git-remote-svn: Week 8","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-06-23T17:18:09Z","receivedAt":"2010-06-23T17:18:09Z","isPatch":false,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nI have another doubt. If the 6~8 part series is preferred, shall I\nalso include one patch that imports dump.c from the Subversion trunk\ndirectly and another to strip off the unnecessary parts, clean up\nwhitespace and style? In other words, would you prefer to review the\ndump.c taken from the Subversion trunk (and some style patches from\nme) or my cleaned up version?\n\n-- Ram\n"},{"id":"144217","messageId":"20100625001427.GA4683@burratino","threadId":"24173","inReplyTo":"1277310140-16891-2-git-send-email-artagnon@gmail.com","subject":"Re: [WIP PATCH 1/7] Add skeleton RA svnclient","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-06-25T00:14:27Z","receivedAt":"2010-06-25T00:14:27Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> In future, it will dump the data in every revision\n> to stdout in dumpfile format (hopefully) without resorting to the FS\n> API.\n\nFor now, just some naïve questions.  Warning: I know nothing about\nsvn internals.\n\nI assume this corresponds to the ra-svn branch of\n<http://github.com/artagnon/svn-dump-fast-export.git>.  Has the\nrelevant code changed much since you sent it?\n\n> --- /dev/null\n> +++ b/debug_editor.c\n> @@ -0,0 +1,413 @@\n> +#include \"svn_pools.h\"\n> +#include \"svn_cmdline.h\"\n> +#include \"svn_client.h\"\n> +#include \"svn_ra.h\"\n> +\n> +struct edit_baton\n\nWhat is a baton?\n\n[...]\n> +  void *wrapped_edit_baton;\n[...]\n> +  void *edit_baton;\n> +  void *wrapped_dir_baton;\n[...]\n> +  void *edit_baton;\n> +  void *wrapped_file_baton;\n\nAre these opaque types necessary?\n\n> +\n> +static svn_error_t *\n> +write_indent(struct edit_baton *eb, apr_pool_t *pool)\n> +{\n> +  int i;\n> +\n> +  for (i = 0; i < eb->indent_level; ++i)\n> +    SVN_ERR(svn_stream_printf(eb->out, pool, \" \"));\n> +\n> +  return SVN_NO_ERROR;\n> +}\n\nWhat does this do?  Is SVN_ERR for debugging?  Where does the output go?\n\n> +static svn_error_t *\n> +set_target_revision(void *edit_baton,\n> +                    svn_revnum_t target_revision,\n> +                    apr_pool_t *pool)\n[...]\n> +static svn_error_t *\n> +open_root(void *edit_baton,\n[...]\n> +static svn_error_t *\n> +close_edit(void *edit_baton,\n[...]\n\nI take it these are callbacks?  Is there overview documentation for\nthem somewhere?\n\n> +svn_error_t *\n> +svn_delta__get_debug_editor(const svn_delta_editor_t **editor,\n> +                            void **edit_baton,\n> +                            const svn_delta_editor_t *wrapped_editor,\n> +                            void *wrapped_edit_baton,\n> +                            apr_pool_t *pool)\n> +{\n> +  svn_delta_editor_t *tree_editor = svn_delta_default_editor(pool);\n> +  struct edit_baton *eb = apr_palloc(pool, sizeof(*eb));\n> +  apr_file_t *errfp;\n> +  svn_stream_t *out;\n> +\n> +  apr_status_t apr_err = apr_file_open_stderr(&errfp, pool);\n> +  if (apr_err)\n> +    return svn_error_wrap_apr(apr_err, \"Problem opening stderr\");\n> +\n> +  out = svn_stream_from_aprfile2(errfp, TRUE, pool);\n> +\n> +  tree_editor->set_target_revision = set_target_revision;\n> +  tree_editor->open_root = open_root;\n> +  tree_editor->delete_entry = delete_entry;\n> +  tree_editor->add_directory = add_directory;\n> +  tree_editor->open_directory = open_directory;\n> +  tree_editor->change_dir_prop = change_dir_prop;\n> +  tree_editor->close_directory = close_directory;\n> +  tree_editor->absent_directory = absent_directory;\n> +  tree_editor->add_file = add_file;\n> +  tree_editor->open_file = open_file;\n> +  tree_editor->apply_textdelta = apply_textdelta;\n> +  tree_editor->change_file_prop = change_file_prop;\n> +  tree_editor->close_file = close_file;\n> +  tree_editor->absent_file = absent_file;\n> +  tree_editor->close_edit = close_edit;\n\nI take it that the fields of svn_delta_editor_t do not have a\nwell-defined order?  Ugh.\n\nIn any case, I suspect this would be easier to read rearranged a little:\n\n 1. declarations for callbacks\n 2. get_debug_editor implementation\n 3. definitions of types not needed in get_debug_editor()\n 4. implementations of callbacks\n\nThat way, a person reading straight through can figure out what’s\ngoing on a little earlier.\n\n> --- /dev/null\n> +++ b/svnclient_ra.c\n[...]\n> +int main()\n> +{\n> +\tconst char url[] = \"http://svn.apache.org/repos/asf\";\n> +\tsvn_revnum_t start_revision = 1, end_revision = 5;\n> +\tif (svn_cmdline_init (\"svnclient_ra\", stderr) != EXIT_SUCCESS)\n> +\t\treturn 1;\n> +\tpool = svn_pool_create(NULL);\n> +\n> +\tSVN_INT_ERR(open_connection(url));\n> +\tSVN_INT_ERR(replay_range(start_revision, end_revision));\n> +\n> +\tclose_connection();\n> +\treturn 0;\n> +}\n\nWhat is svn_cmdline_init?  Is this code destined for inclusion in svn\nupstream, and if so, where can one find the surrounding code this\nshould fit in with?\n\nJonathan\n"},{"id":"144218","messageId":"20100625004240.GB4762@burratino","threadId":"24173","inReplyTo":"AANLkTik1fV1yYZL5mKbVfGKOao7VjzMsg2D_7FkL8nR2@mail.gmail.com","subject":"Re: [GSoC update] git-remote-svn: Week 8","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-06-25T00:42:40Z","receivedAt":"2010-06-25T00:42:40Z","isPatch":false,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> shall I\n> also include one patch that imports dump.c from the Subversion trunk\n> directly and another to strip off the unnecessary parts, clean up\n> whitespace and style?\n\nThis is just my preference, but I would prefer your cleaned up version\n(with a link to the pristine version in trunk I can diff against, of\ncourse).\n\nJonathan\n"},{"id":"144233","messageId":"alpine.561.2.00.1006251156180.2064@daniel2.local","threadId":"24173","inReplyTo":"20100625001427.GA4683@burratino","subject":"Re: [WIP PATCH 1/7] Add skeleton RA svnclient","fromName":"Daniel Shahaf","fromEmail":"daniel@shahaf.name","sentAt":"2010-06-25T09:07:31Z","receivedAt":"2010-06-25T09:07:31Z","isPatch":true,"sender":{"key":"daniel@shahaf.name","avatar":null},"body":"Jonathan Nieder wrote on Fri, 25 Jun 2010 at 03:14 -0000:\n> Ramkumar Ramachandra wrote:\n> \n> > In future, it will dump the data in every revision\n> > to stdout in dumpfile format (hopefully) without resorting to the FS\n> > API.\n> \n> For now, just some naïve questions.  Warning: I know nothing about\n> svn internals.\n> \n> I assume this corresponds to the ra-svn branch of\n> <http://github.com/artagnon/svn-dump-fast-export.git>.  Has the\n> relevant code changed much since you sent it?\n> \n> > --- /dev/null\n> > +++ b/debug_editor.c\n> > @@ -0,0 +1,413 @@\n> > +#include \"svn_pools.h\"\n> > +#include \"svn_cmdline.h\"\n> > +#include \"svn_client.h\"\n> > +#include \"svn_ra.h\"\n> > +\n> > +struct edit_baton\n> \n> What is a baton?\n> \n\nThe context object for a callback.\n\nYou call:\n\n    some_function(your_callback_function, your_baton)\n\nwhich then calls:\n\n    your_callback_function(your_baton, other_arguments)\n\n\n> [...]\n> > +  void *wrapped_edit_baton;\n> [...]\n> > +  void *edit_baton;\n> > +  void *wrapped_dir_baton;\n> [...]\n> > +  void *edit_baton;\n> > +  void *wrapped_file_baton;\n> \n> Are these opaque types necessary?\n> \n\nThe convention in Subversion's code is to convert the void * to\na concrete_baton_t * only inside the callback.  If you wish to declare\nthese, e.g., as\n\n    debug_editor_baton_t *wrapped_baton;\n\nYou can probably do that too.\n\n> > +\n> > +static svn_error_t *\n> > +write_indent(struct edit_baton *eb, apr_pool_t *pool)\n> > +{\n> > +  int i;\n> > +\n> > +  for (i = 0; i < eb->indent_level; ++i)\n> > +    SVN_ERR(svn_stream_printf(eb->out, pool, \" \"));\n> > +\n> > +  return SVN_NO_ERROR;\n> > +}\n> \n> What does this do?  Is SVN_ERR for debugging?\n\nThat's how we implement exception throwing in C.  SVN_ERR means \"if this\nreturned a non-NULL svn_error_t *, then return that error to our\ncaller\".\n\nThe other pattern does\n\n    svn_error_t *err = svn_stream_printf();\n\nand then inspects err and err->apr_err to decide whether to ignore the\nerror or return it (possibly wrapped).\n\n> Where does the output go?\n> \n\nSVN_ERR does not print anything.  It may return(), though.\n\n> > +static svn_error_t *\n> > +set_target_revision(void *edit_baton,\n> > +                    svn_revnum_t target_revision,\n> > +                    apr_pool_t *pool)\n> [...]\n> > +static svn_error_t *\n> > +open_root(void *edit_baton,\n> [...]\n> > +static svn_error_t *\n> > +close_edit(void *edit_baton,\n> [...]\n> \n> I take it these are callbacks?  Is there overview documentation for\n> them somewhere?\n> \n\nsvn_delta_editor_t in\nhttp://svn.apache.org/repos/asf/subversion/trunk/subversion/include/svn_delta.h\n\n> > +svn_error_t *\n> > +svn_delta__get_debug_editor(const svn_delta_editor_t **editor,\n> > +                            void **edit_baton,\n> > +                            const svn_delta_editor_t *wrapped_editor,\n> > +                            void *wrapped_edit_baton,\n> > +                            apr_pool_t *pool)\n> > +{\n> > +  svn_delta_editor_t *tree_editor = svn_delta_default_editor(pool);\n> > +  struct edit_baton *eb = apr_palloc(pool, sizeof(*eb));\n> > +  apr_file_t *errfp;\n> > +  svn_stream_t *out;\n> > +\n> > +  apr_status_t apr_err = apr_file_open_stderr(&errfp, pool);\n> > +  if (apr_err)\n> > +    return svn_error_wrap_apr(apr_err, \"Problem opening stderr\");\n> > +\n> > +  out = svn_stream_from_aprfile2(errfp, TRUE, pool);\n> > +\n> > +  tree_editor->set_target_revision = set_target_revision;\n> > +  tree_editor->open_root = open_root;\n> > +  tree_editor->delete_entry = delete_entry;\n> > +  tree_editor->add_directory = add_directory;\n> > +  tree_editor->open_directory = open_directory;\n> > +  tree_editor->change_dir_prop = change_dir_prop;\n> > +  tree_editor->close_directory = close_directory;\n> > +  tree_editor->absent_directory = absent_directory;\n> > +  tree_editor->add_file = add_file;\n> > +  tree_editor->open_file = open_file;\n> > +  tree_editor->apply_textdelta = apply_textdelta;\n> > +  tree_editor->change_file_prop = change_file_prop;\n> > +  tree_editor->close_file = close_file;\n> > +  tree_editor->absent_file = absent_file;\n> > +  tree_editor->close_edit = close_edit;\n> \n> I take it that the fields of svn_delta_editor_t do not have a\n> well-defined order?  Ugh.\n> \n\nI don't understand.  It seems that the fields here appear in the same\norder as in the definition of struct svn_delta_editor_t.\n\n> In any case, I suspect this would be easier to read rearranged a little:\n> \n>  1. declarations for callbacks\n>  2. get_debug_editor implementation\n>  3. definitions of types not needed in get_debug_editor()\n>  4. implementations of callbacks\n> \n> That way, a person reading straight through can figure out what’s\n> going on a little earlier.\n> \n> > --- /dev/null\n> > +++ b/svnclient_ra.c\n> [...]\n> > +int main()\n> > +{\n> > +\tconst char url[] = \"http://svn.apache.org/repos/asf\";\n> > +\tsvn_revnum_t start_revision = 1, end_revision = 5;\n> > +\tif (svn_cmdline_init (\"svnclient_ra\", stderr) != EXIT_SUCCESS)\n> > +\t\treturn 1;\n> > +\tpool = svn_pool_create(NULL);\n> > +\n> > +\tSVN_INT_ERR(open_connection(url));\n> > +\tSVN_INT_ERR(replay_range(start_revision, end_revision));\n> > +\n> > +\tclose_connection();\n> > +\treturn 0;\n> > +}\n> \n> What is svn_cmdline_init?\n\nA Subversion API that does some necessary initializations (e.g., calls\napr_initialize()).  See subversion/include/svn_cmdline.h for docs\n(and subversion/libsvn_subr/cmdline.c for the implementation).\n\n> Is this code destined for inclusion in svn upstream, and if so, where\n> can one find the surrounding code this should fit in with?\n> \n> Jonathan\n> "},{"id":"144236","messageId":"AANLkTimirObq-HBrB4rQdAprN8mfd92rfFKSegJKwMsc@mail.gmail.com","threadId":"24173","inReplyTo":"alpine.561.2.00.1006251156180.2064@daniel2.local","subject":"Re: [WIP PATCH 1/7] Add skeleton RA svnclient","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-06-25T11:07:17Z","receivedAt":"2010-06-25T11:07:17Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Jonathan and Daniel,\n\nJonathan: First, thanks for bringing up these questions- they will\ndefinitely help future reviewers. I'll now sprinkle Daniel's replies\nwith a few of my observations.\nDaniel: Thanks for responding to the questions- your insights as a\ncore Subversion developer are most appreciated. I'll add a few notes\nto your answers now.\n\nDaniel Shahaf wrote:\n> Jonathan Nieder wrote on Fri, 25 Jun 2010 at 03:14 -0000:\n>> For now, just some naïve questions.  Warning: I know nothing about\n>> svn internals.\n\nThat's alright. I highly encourage naïve questions- I'm new to SVN\nmyself. I also highly recommend reading the API documentation and\ngoing through the code for answers to \"why is it like THIS\" questions\nas I haven't manged to clean out the Subversion style yet. Hopefully,\nI'll have some good notes that I can attach with my next series to put\nin the trunk.\n\n>> I assume this corresponds to the ra-svn branch of\n>> <http://github.com/artagnon/svn-dump-fast-export.git>.  Has the\n>> relevant code changed much since you sent it?\n\nYes. I've managed to fix the deltified dump, and have now started\nworking on a full text dump. Due to my low familiarity with the libsvn\nAPI, the cleanups are mixed with my code in the history; it needs a\nthorough line-by-line scrubbing.\n\n>> What is a baton?\n>>\n>\n> The context object for a callback.\n>\n> You call:\n>\n>    some_function(your_callback_function, your_baton)\n>\n> which then calls:\n>\n>    your_callback_function(your_baton, other_arguments)\n\nIn general, I've found that batons are void * objects in which you can\nstuff anything you like and pass around from function to function:\nI've abused them quite heavily in my code by stuffing all kinds of\nthings into them: see fb->eb->* in my current code for an example of\nthis.\n\n>> [...]\n>> > +  void *wrapped_edit_baton;\n>> [...]\n>> > +  void *edit_baton;\n>> > +  void *wrapped_dir_baton;\n>> [...]\n>> > +  void *edit_baton;\n>> > +  void *wrapped_file_baton;\n>>\n>> Are these opaque types necessary?\n>>\n>\n> The convention in Subversion's code is to convert the void * to\n> a concrete_baton_t * only inside the callback.  If you wish to declare\n> these, e.g., as\n>\n>    debug_editor_baton_t *wrapped_baton;\n>\n> You can probably do that too.\n\nThe function prototypes in libsvn contain void * parameters\ncorresponding to batons, so I'd have to typecast explicitly to avoid\nany warnings. I think Jonathan's also referring to the absence of the\n\"struct\" keyword everywhere, as that is against Git policy.\nUnfortunately, everything is typedef'ed in libsvn, and we cannot do\nmuch about that.\n\n>> What does this do?  Is SVN_ERR for debugging?\n>\n> That's how we implement exception throwing in C.  SVN_ERR means \"if this\n> returned a non-NULL svn_error_t *, then return that error to our\n> caller\".\n>\n> The other pattern does\n>\n>    svn_error_t *err = svn_stream_printf();\n>\n> and then inspects err and err->apr_err to decide whether to ignore the\n> error or return it (possibly wrapped).\n\n>> Where does the output go?\n>>\n>\n> SVN_ERR does not print anything.  It may return(), though.\n\nEmbarrassingly enough, write_indent does exactly what it says it does:\nIt writes some spaces (or indent) to eb->out, which you'll find is\nactually stderr in svn_delta__get_debug. In the cleanup, I should\nprobably get rid of this. As far as the error handling is concerned,\nto be terse, SVN_ERR and SVN_INT_ERR are cpp macros. They are defined\nas follows in svn_error.h (with line numbers from the trunk file).\nNote however, that even with all this error handling, the most common\ntype of error I get by far is the segfault: I'll make an effort to\ndocument the pitfalls.\n\n00284 #define SVN_ERR(expr)                           \\\n00285   do {                                          \\\n00286     svn_error_t *svn_err__temp = (expr);        \\\n00287     if (svn_err__temp)                          \\\n00288       return svn_error_return(svn_err__temp);   \\\n00289   } while (0)\n\n00336 #define SVN_INT_ERR(expr)                                        \\\n00337   do {                                                           \\\n00338     svn_error_t *svn_err__temp = (expr);                         \\\n00339     if (svn_err__temp) {                                         \\\n00340       svn_handle_error2(svn_err__temp, stderr, FALSE, \"svn: \");  \\\n00341       svn_error_clear(svn_err__temp);                            \\\n00342       return EXIT_FAILURE; }                                     \\\n00343   } while (0)\n\n>> I take it these are callbacks?  Is there overview documentation for\n>> them somewhere?\n>>\n>\n> svn_delta_editor_t in\n> http://svn.apache.org/repos/asf/subversion/trunk/subversion/include/svn_delta.h\n\nYes, they are callbacks that are fired automatically by the editor. In\nsvn_delta.h, look at struct svn_delta_editor_t (and the corresponding\ndoxygen-style comments).\n\n>> I take it that the fields of svn_delta_editor_t do not have a\n>> well-defined order?  Ugh.\n\nNo, they don't. I've tried to stick to the order in the struct\nsvn_delta_editor_t.\n\n>> In any case, I suspect this would be easier to read rearranged a little:\n>>\n>>  1. declarations for callbacks\n>>  2. get_debug_editor implementation\n>>  3. definitions of types not needed in get_debug_editor()\n>>  4. implementations of callbacks\n>>\n>> That way, a person reading straight through can figure out what’s\n>> going on a little earlier.\n\nAgreed. I'm still struggling to clean up the Subversion-style code. We\ncan probably discuss this at length on IRC?\n\n>> What is svn_cmdline_init?\n>\n> A Subversion API that does some necessary initializations (e.g., calls\n> apr_initialize()).  See subversion/include/svn_cmdline.h for docs\n> (and subversion/libsvn_subr/cmdline.c for the implementation).\n\nThese svn_cmdline functions are actually shortcuts- they do all the\ninitializations required for a \"typical\" command line SVN client. It\nsaves me the trouble of having to figure out what I missed\ninitializing: I'll be using more of them in future; to eliminate the\nauth baton creation by hand, for example.\n\n>> Is this code destined for inclusion in svn upstream, and if so, where\n>> can one find the surrounding code this should fit in with?\n\nYes, but with a lot of style transformations. In svnsync/main.c.\nAtleast that's the plan, as per the discussion on #svn-dev\n\n-- Ram\n"},{"id":"144237","messageId":"alpine.561.2.00.1006251412170.2144@daniel2.local","threadId":"24173","inReplyTo":"AANLkTimirObq-HBrB4rQdAprN8mfd92rfFKSegJKwMsc@mail.gmail.com","subject":"Re: [WIP PATCH 1/7] Add skeleton RA svnclient","fromName":"Daniel Shahaf","fromEmail":"d.s@daniel.shahaf.name","sentAt":"2010-06-25T11:30:57Z","receivedAt":"2010-06-25T11:30:57Z","isPatch":true,"sender":{"key":"d.s@daniel.shahaf.name","avatar":null},"body":"Ramkumar Ramachandra wrote on Fri, 25 Jun 2010 at 14:07 -0000:\n> Daniel Shahaf wrote:\n> > Jonathan Nieder wrote on Fri, 25 Jun 2010 at 03:14 -0000:\n> >> What is a baton?\n> >>\n> >\n> > The context object for a callback.\n> >\n> > You call:\n> >\n> >    some_function(your_callback_function, your_baton)\n> >\n> > which then calls:\n> >\n> >    your_callback_function(your_baton, other_arguments)\n> \n> In general, I've found that batons are void * objects in which you can\n> stuff anything you like and pass around from function to function:\n> I've abused them quite heavily in my code by stuffing all kinds of\n> things into them: see fb->eb->* in my current code for an example of\n> this.\n> \n\nWe do that too sometimes:\n\n% pwd\n$trunk_wc/subversion/libsvn_client/\n% grep -- \"->eb->\" *.c | head\ncommit_util.c:  return (*db->eb->real_editor->add_file)(path, db->real_baton,\ncommit_util.c:  return (*db->eb->real_editor->delete_entry)(path, revision,\ncommit_util.c:  return (*db->eb->real_editor->open_file)(path, db->real_baton,\ncommit_util.c:  return (*fb->eb->real_editor->close_file)(fb->real_baton,\ncommit_util.c:  return (*fb->eb->real_editor->change_file_prop)(fb->real_baton,\ncommit_util.c:  return (*fb->eb->real_editor->apply_textdelta)(fb->real_baton,\n...\n\n\n> >> [...]\n> >> > +  void *wrapped_edit_baton;\n> >> [...]\n> >> > +  void *edit_baton;\n> >> > +  void *wrapped_dir_baton;\n> >> [...]\n> >> > +  void *edit_baton;\n> >> > +  void *wrapped_file_baton;\n> >>\n> >> Are these opaque types necessary?\n> >>\n> >\n> > The convention in Subversion's code is to convert the void * to\n> > a concrete_baton_t * only inside the callback.  If you wish to declare\n> > these, e.g., as\n> >\n> >    debug_editor_baton_t *wrapped_baton;\n> >\n> > You can probably do that too.\n> \n> The function prototypes in libsvn contain void * parameters\n> corresponding to batons, so I'd have to typecast explicitly to avoid\n> any warnings.\n\nBy the way, a common pitfall is to mis-treat the void *baton argument as\nthe wrong type of baton.  That is, code of the form\n\n    f(void *baton) {\n        foo_baton_t *fb = baton;\n    }\n\ninstead of\n\n    f(void *baton) {\n        bar_baton_t *bb = baton;\n    }\n\n> I think Jonathan's also referring to the absence of the\n> \"struct\" keyword everywhere, as that is against Git policy.\n> Unfortunately, everything is typedef'ed in libsvn, and we cannot do\n> much about that.\n> \n\nUsually we do\n\n    typedef struct svn_error_t svn_error_t;\n\nso you can add the 'struct' back if you want.\n\n> >> What does this do?  Is SVN_ERR for debugging?\n> >\n> > That's how we implement exception throwing in C.  SVN_ERR means \"if this\n> > returned a non-NULL svn_error_t *, then return that error to our\n> > caller\".\n> >\n> > The other pattern does\n> >\n> >    svn_error_t *err = svn_stream_printf();\n> >\n> > and then inspects err and err->apr_err to decide whether to ignore the\n> > error or return it (possibly wrapped).\n> \n> >> Where does the output go?\n> >>\n> >\n> > SVN_ERR does not print anything.  It may return(), though.\n> \n> Embarrassingly enough, write_indent does exactly what it says it does:\n> It writes some spaces (or indent) to eb->out\n\nTo represent nesting.\n\n> Note however, that even with all this error handling, the most common\n> type of error I get by far is the segfault: I'll make an effort to\n> document the pitfalls.\n> \n\nNote there are several kinds of errors that can cause a segfault.\n(@Ram, you've encountered some of them already, I know.)\n\n> >> I take it these are callbacks?  Is there overview documentation for\n> >> them somewhere?\n> >>\n> >\n> > svn_delta_editor_t in\n> > http://svn.apache.org/repos/asf/subversion/trunk/subversion/include/svn_delta.h\n> \n> Yes, they are callbacks that are fired automatically by the editor. In\n> svn_delta.h, look at struct svn_delta_editor_t (and the corresponding\n> doxygen-style comments).\n> \n\nThe compiled doxygen docs are available on\n<http://subversion.apache.org/docs/#api>.  Personally I always\njust read the header file directly (with :set filetype=c.doxygen)."},{"id":"144241","messageId":"20100625133456.GA1495@burratino","threadId":"24173","inReplyTo":"alpine.561.2.00.1006251156180.2064@daniel2.local","subject":"Re: [WIP PATCH 1/7] Add skeleton RA svnclient","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-06-25T13:34:56Z","receivedAt":"2010-06-25T13:34:56Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Daniel Shahaf wrote:\n\n> I don't understand.  It seems that the fields here appear in the same\n> order as in the definition of struct svn_delta_editor_t.\n\nEmbarrassingly, I was just confused.  Thanks for the answers!\n\nJonathan\n"},{"id":"144246","messageId":"20100625144544.GA7710@burratino","threadId":"24173","inReplyTo":"AANLkTimirObq-HBrB4rQdAprN8mfd92rfFKSegJKwMsc@mail.gmail.com","subject":"Re: [WIP PATCH 1/7] Add skeleton RA svnclient","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-06-25T14:45:44Z","receivedAt":"2010-06-25T14:45:44Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi Ram,\n\nRamkumar Ramachandra wrote:\n\n> I also highly recommend reading the API documentation and\n> going through the code for answers to \"why is it like THIS\" questions\n> as I haven't manged to clean out the Subversion style yet.\n\nIf this code is ultimately destined for the svnsync tree (as it seems\nit is), it might be possible to save time by just sticking to the svn\nstyle.  I think this would be a typical place where we relax the git\nstyle guidelines a bit.\n\nOn the other hand, during development, if you want to use some other\nstyle (or if you want to write in Smalltalk for that matter), that’s\nfine by me.  I don’t want to dictate your development style.  I am\nonly mentioning style now because I suspect it would be easier to\nreview code that follows a single convention (which could be\nSubversion’s, or Git’s, or some well defined hybrid, or something else\nentirely, or ...).\n\n>> SVN_ERR does not print anything.  It may return(), though.\n\nAh, so it is an exception handling mechanism.\n\n> These svn_cmdline functions are actually shortcuts- they do all the\n> initializations required for a \"typical\" command line SVN client. It\n> saves me the trouble of having to figure out what I missed\n> initializing: I'll be using more of them in future; to eliminate the\n> auth baton creation by hand, for example.\n\nThanks again for the explanations.\n\nJonathan\n"},{"id":"144251","messageId":"20100625153031.GA9702@burratino","threadId":"24173","inReplyTo":"alpine.561.2.00.1006251412170.2144@daniel2.local","subject":"OT: typesafe callbacks in C (Re: [WIP PATCH 1/7] Add skeleton RA svnclient)","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-06-25T15:30:32Z","receivedAt":"2010-06-25T15:30:32Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Daniel Shahaf wrote:\n\n> By the way, a common pitfall is to mis-treat the void *baton argument as\n> the wrong type of baton.  That is, code of the form\n> \n>     f(void *baton) {\n>         foo_baton_t *fb = baton;\n>     }\n> \n> instead of\n> \n>     f(void *baton) {\n>         bar_baton_t *bb = baton;\n>     }\n\nJust for kicks, I should mention\n\n  http://ccan.ozlabs.org/info/typesafe_cb.html\n\nwhich makes code like this cause warnings with gcc.\n\nMaybe some version of Subversion years from now could use it.\n"}]}