threads / discuss / 24173

[GSoC update] git-remote-svn: Week 8

Subject: [GSoC update] git-remote-svn: Week 8

## tl;dr

18 messages between Jun 23, 2010 and Jun 25, 2010.

replies: 17people: 4as markdown or json

Ramkumar Ramachandra· Jun 23, 2010, 16:22 UTC · lore
Hi,
I've been very productive over the last few days. The good news is
that I finally managed to a (badly-formatted, broken, abridged) dump
of the text and properties. The bad news is that the program only
seems to work by accident- it segfaults all over the place without
reason, I don't know what to make of the backtraces, and documentation
is thin as usual. I plan to go back in history to do a major cleanup/
refactor before proceeding, now that I've learnt how to trigger the
dumping.
 
I got this far mainly because of the help I got from Subversion
developers like Daniel- A big thank you to them. The plan is to finish
this, get it reviewed/ acked by Daniel and the other Subversion
developers, and test it with David's exporter before merging it into
git.git. Once it's merged, I plan to write a series for Subversion,
get it merged there, and remove it from git.git in favor of simply
linking to the feature. The reason for this roundabout dance is
simple- things are much easier to test in Git-land.

Another thing that I think we should give some thought- how do I get this merged into git.git? Should I post a single patch with the (probably 1000-line) dump editor? In my opinion, the revision history is very valuable- can we get a clean series of 6~8 patches merged instead?

-- Ram
Ramkumar Ramachandra (7):
  Add skeleton RA svnclient
  Add stripped dump editor
  Import dump_node to dump what changed and cleanup whitespace
  Replace deprecated svn_path_join
  Trigger dump_node in change_dir_prop
  Add file_baton and trigger dump_node in change_file_prop
  [WIP] Dump the text delta
 LICENSE        |   14 ++
 Makefile       |    4 +
 debug_editor.c |  413 +++++++++++++++++++++++++++++++++++
 debug_editor.h |    6 +
 dump_editor.c  |  652 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 dump_editor.h  |   10 +
 svnclient_ra.c |  138 ++++++++++++
 7 files changed, 1237 insertions(+), 0 deletions(-)
 create mode 100644 LICENSE
 create mode 100644 Makefile
 create mode 100644 debug_editor.c
 create mode 100644 debug_editor.h
 create mode 100644 dump_editor.c
 create mode 100644 dump_editor.h
 create mode 100644 svnclient_ra.c
Ramkumar Ramachandra· Jun 23, 2010, 16:22 UTC · re: Ramkumar Ramachandra · lore

[WIP PATCH 1/7] Add skeleton RA svnclient

The SVN client uses the RA API to connect to a remote server and replay revisions. Currently, it wraps a debug editor to print out some tree information. In future, it will dump the data in every revision to stdout in dumpfile format (hopefully) without resorting to the FS API.

Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 Makefile       |    4 +
 debug_editor.c |  413 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 debug_editor.h |    6 +
 svnclient_ra.c |  131 ++++++++++++++++++
 4 files changed, 554 insertions(+), 0 deletions(-)
 create mode 100644 Makefile
 create mode 100644 debug_editor.c
 create mode 100644 debug_editor.h
 create mode 100644 svnclient_ra.c
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c3c695c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,4 @@
+svnclient_ra: *.c *.h
+	$(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
+clean:
+	$(RM) svnclient_ra
diff --git a/debug_editor.c b/debug_editor.c
new file mode 100644
index 0000000..ef77e44
--- /dev/null
+++ b/debug_editor.c
@@ -0,0 +1,413 @@
+#include "svn_pools.h"
+#include "svn_cmdline.h"
+#include "svn_client.h"
+#include "svn_ra.h"
+
+struct edit_baton
+{
+  const svn_delta_editor_t *wrapped_editor;
+  void *wrapped_edit_baton;
+
+  int indent_level;
+
+  svn_stream_t *out;
+};
+
+struct dir_baton
+{
+  void *edit_baton;
+  void *wrapped_dir_baton;
+};
+
+struct file_baton
+{
+  void *edit_baton;
+  void *wrapped_file_baton;
+};
+
+static svn_error_t *
+write_indent(struct edit_baton *eb, apr_pool_t *pool)
+{
+  int i;
+
+  for (i = 0; i < eb->indent_level; ++i)
+    SVN_ERR(svn_stream_printf(eb->out, pool, " "));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+set_target_revision(void *edit_baton,
+                    svn_revnum_t target_revision,
+                    apr_pool_t *pool)
+{
+  struct edit_baton *eb = edit_baton;
+
+  SVN_ERR(write_indent(eb, pool));
+  SVN_ERR(svn_stream_printf(eb->out, pool, "set_target_revision : %ld\n",
+                            target_revision));
+
+  return eb->wrapped_editor->set_target_revision(eb->wrapped_edit_baton,
+                                                 target_revision,
+                                                 pool);
+}
+
+static svn_error_t *
+open_root(void *edit_baton,
+          svn_revnum_t base_revision,
+          apr_pool_t *pool,
+          void **root_baton)
+{
+  struct edit_baton *eb = edit_baton;
+  struct dir_baton *dir_baton = apr_palloc(pool, sizeof(*dir_baton));
+
+  SVN_ERR(write_indent(eb, pool));
+  SVN_ERR(svn_stream_printf(eb->out, pool, "open_root : %ld\n",
+                            base_revision));
+  eb->indent_level++;
+
+  SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton,
+                                        base_revision,
+                                        pool,
+                                        &dir_baton->wrapped_dir_baton));
+
+  dir_baton->edit_baton = edit_baton;
+
+  *root_baton = dir_baton;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+delete_entry(const char *path,
+             svn_revnum_t base_revision,
+             void *parent_baton,
+             apr_pool_t *pool)
+{
+  struct dir_baton *pb = parent_baton;
+  struct edit_baton *eb = pb->edit_baton;
+
+  SVN_ERR(write_indent(eb, pool));
+  SVN_ERR(svn_stream_printf(eb->out, pool, "delete_entry : %s:%ld\n",
+                            path, base_revision));
+
+  return eb->wrapped_editor->delete_entry(path,
+                                          base_revision,
+                                          pb->wrapped_dir_baton,
+                                          pool);
+}
+
+static svn_error_t *
+add_directory(const char *path,
+              void *parent_baton,
+              const char *copyfrom_path,
+              svn_revnum_t copyfrom_revision,
+              apr_pool_t *pool,
+              void **child_baton)
+{
+  struct dir_baton *pb = parent_baton;
+  struct edit_baton *eb = pb->edit_baton;
+  struct dir_baton *b = apr_palloc(pool, sizeof(*b));
+
+  SVN_ERR(write_indent(eb, pool));
+  SVN_ERR(svn_stream_printf(eb->out, pool,
+                            "add_directory : '%s' [from '%s':%ld]\n",
+                            path, copyfrom_path, copyfrom_revision));
+  eb->indent_level++;
+
+  SVN_ERR(eb->wrapped_editor->add_directory(path,
+                                            pb->wrapped_dir_baton,
+                                            copyfrom_path,
+                                            copyfrom_revision,
+                                            pool,
+                                            &b->wrapped_dir_baton));
+
+  b->edit_baton = eb;
+  *child_baton = b;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+open_directory(const char *path,
+               void *parent_baton,
+               svn_revnum_t base_revision,
+               apr_pool_t *pool,
+               void **child_baton)
+{
+  struct dir_baton *pb = parent_baton;
+  struct edit_baton *eb = pb->edit_baton;
+  struct dir_baton *db = apr_palloc(pool, sizeof(*db));
+
+  SVN_ERR(write_indent(eb, pool));
+  SVN_ERR(svn_stream_printf(eb->out, pool, "open_directory : '%s':%ld\n",
+                            path, base_revision));
+  eb->indent_level++;
+
+  SVN_ERR(eb->wrapped_editor->open_directory(path,
+                                             pb->wrapped_dir_baton,
+                                             base_revision,
+                                             pool,
+                                             &db->wrapped_dir_baton));
+
+  db->edit_baton = eb;
+  *child_baton = db;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+add_file(const char *path,
+         void *parent_baton,
+         const char *copyfrom_path,
+         svn_revnum_t copyfrom_revision,
+         apr_pool_t *pool,
+         void **file_baton)
+{
+  struct dir_baton *pb = parent_baton;
+  struct edit_baton *eb = pb->edit_baton;
+  struct file_baton *fb = apr_palloc(pool, sizeof(*fb));
+
+  SVN_ERR(write_indent(eb, pool));
+  SVN_ERR(svn_stream_printf(eb->out, pool,
+                            "add_file : '%s' [from '%s':%ld]\n",
+                            path, copyfrom_path, copyfrom_revision));
+
+  eb->indent_level++;
+
+  SVN_ERR(eb->wrapped_editor->add_file(path,
+                                       pb->wrapped_dir_baton,
+                                       copyfrom_path,
+                                       copyfrom_revision,
+                                       pool,
+                                       &fb->wrapped_file_baton));
+
+  fb->edit_baton = eb;
+  *file_baton = fb;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+open_file(const char *path,
+          void *parent_baton,
+          svn_revnum_t base_revision,
+          apr_pool_t *pool,
+          void **file_baton)
+{
+  struct dir_baton *pb = parent_baton;
+  struct edit_baton *eb = pb->edit_baton;
+  struct file_baton *fb = apr_palloc(pool, sizeof(*fb));
+
+  SVN_ERR(write_indent(eb, pool));
+  SVN_ERR(svn_stream_printf(eb->out, pool, "open_file : '%s':%ld\n",
+                            path, base_revision));
+
+  eb->indent_level++;
+
+  SVN_ERR(eb->wrapped_editor->open_file(path,
+                                        pb->wrapped_dir_baton,
+                                        base_revision,
+                                        pool,
+                                        &fb->wrapped_file_baton));
+
+  fb->edit_baton = eb;
+  *file_baton = fb;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+apply_textdelta(void *file_baton,
+                const char *base_checksum,
+                apr_pool_t *pool,
+                svn_txdelta_window_handler_t *handler,
+                void **handler_baton)
+{
+  struct file_baton *fb = file_baton;
+  struct edit_baton *eb = fb->edit_baton;
+
+  SVN_ERR(write_indent(eb, pool));
+  SVN_ERR(svn_stream_printf(eb->out, pool, "apply_textdelta : %s\n",
+                            base_checksum));
+
+  SVN_ERR(eb->wrapped_editor->apply_textdelta(fb->wrapped_file_baton,
+                                              base_checksum,
+                                              pool,
+                                              handler,
+                                              handler_baton));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+close_file(void *file_baton,
+           const char *text_checksum,
+           apr_pool_t *pool)
+{
+  struct file_baton *fb = file_baton;
+  struct edit_baton *eb = fb->edit_baton;
+
+  eb->indent_level--;
+
+  SVN_ERR(write_indent(eb, pool));
+  SVN_ERR(svn_stream_printf(eb->out, pool, "close_file : %s\n",
+                            text_checksum));
+
+  SVN_ERR(eb->wrapped_editor->close_file(fb->wrapped_file_baton,
+                                         text_checksum, pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+absent_file(const char *path,
+            void *file_baton,
+            apr_pool_t *pool)
+{
+  struct file_baton *fb = file_baton;
+  struct edit_baton *eb = fb->edit_baton;
+
+  SVN_ERR(write_indent(eb, pool));
+  SVN_ERR(svn_stream_printf(eb->out, pool, "absent_file : %s\n", path));
+
+  SVN_ERR(eb->wrapped_editor->absent_file(path, fb->wrapped_file_baton,
+                                          pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+close_directory(void *dir_baton,
+                apr_pool_t *pool)
+{
+  struct dir_baton *db = dir_baton;
+  struct edit_baton *eb = db->edit_baton;
+
+  eb->indent_level--;
+  SVN_ERR(write_indent(eb, pool));
+  SVN_ERR(svn_stream_printf(eb->out, pool, "close_directory\n"));
+
+  SVN_ERR(eb->wrapped_editor->close_directory(db->wrapped_dir_baton,
+                                              pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+absent_directory(const char *path,
+                 void *dir_baton,
+                 apr_pool_t *pool)
+{
+  struct dir_baton *db = dir_baton;
+  struct edit_baton *eb = db->edit_baton;
+
+  SVN_ERR(write_indent(eb, pool));
+  SVN_ERR(svn_stream_printf(eb->out, pool, "absent_directory : %s\n",
+                            path));
+
+  SVN_ERR(eb->wrapped_editor->absent_directory(path, db->wrapped_dir_baton,
+                                               pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+change_file_prop(void *file_baton,
+                 const char *name,
+                 const svn_string_t *value,
+                 apr_pool_t *pool)
+{
+  struct file_baton *fb = file_baton;
+  struct edit_baton *eb = fb->edit_baton;
+
+  SVN_ERR(write_indent(eb, pool));
+  SVN_ERR(svn_stream_printf(eb->out, pool, "change_file_prop : %s\n",
+                            name));
+
+  SVN_ERR(eb->wrapped_editor->change_file_prop(fb->wrapped_file_baton,
+                                               name,
+                                               value,
+                                               pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+change_dir_prop(void *dir_baton,
+                const char *name,
+                const svn_string_t *value,
+                apr_pool_t *pool)
+{
+  struct dir_baton *db = dir_baton;
+  struct edit_baton *eb = db->edit_baton;
+
+  SVN_ERR(write_indent(eb, pool));
+  SVN_ERR(svn_stream_printf(eb->out, pool, "change_dir_prop : %s\n", name));
+
+  SVN_ERR(eb->wrapped_editor->change_dir_prop(db->wrapped_dir_baton,
+                                              name,
+                                              value,
+                                              pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+close_edit(void *edit_baton,
+           apr_pool_t *pool)
+{
+  struct edit_baton *eb = edit_baton;
+
+  SVN_ERR(write_indent(eb, pool));
+  SVN_ERR(svn_stream_printf(eb->out, pool, "close_edit\n"));
+
+  SVN_ERR(eb->wrapped_editor->close_edit(eb->wrapped_edit_baton, pool));
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_delta__get_debug_editor(const svn_delta_editor_t **editor,
+                            void **edit_baton,
+                            const svn_delta_editor_t *wrapped_editor,
+                            void *wrapped_edit_baton,
+                            apr_pool_t *pool)
+{
+  svn_delta_editor_t *tree_editor = svn_delta_default_editor(pool);
+  struct edit_baton *eb = apr_palloc(pool, sizeof(*eb));
+  apr_file_t *errfp;
+  svn_stream_t *out;
+
+  apr_status_t apr_err = apr_file_open_stderr(&errfp, pool);
+  if (apr_err)
+    return svn_error_wrap_apr(apr_err, "Problem opening stderr");
+
+  out = svn_stream_from_aprfile2(errfp, TRUE, pool);
+
+  tree_editor->set_target_revision = set_target_revision;
+  tree_editor->open_root = open_root;
+  tree_editor->delete_entry = delete_entry;
+  tree_editor->add_directory = add_directory;
+  tree_editor->open_directory = open_directory;
+  tree_editor->change_dir_prop = change_dir_prop;
+  tree_editor->close_directory = close_directory;
+  tree_editor->absent_directory = absent_directory;
+  tree_editor->add_file = add_file;
+  tree_editor->open_file = open_file;
+  tree_editor->apply_textdelta = apply_textdelta;
+  tree_editor->change_file_prop = change_file_prop;
+  tree_editor->close_file = close_file;
+  tree_editor->absent_file = absent_file;
+  tree_editor->close_edit = close_edit;
+
+  eb->wrapped_editor = wrapped_editor;
+  eb->wrapped_edit_baton = wrapped_edit_baton;
+  eb->out = out;
+  eb->indent_level = 0;
+
+  *editor = tree_editor;
+  *edit_baton = eb;
+
+  return SVN_NO_ERROR;
+}
diff --git a/debug_editor.h b/debug_editor.h
new file mode 100644
index 0000000..ae87c36
--- /dev/null
+++ b/debug_editor.h
@@ -0,0 +1,6 @@
+svn_error_t *
+svn_delta__get_debug_editor(const svn_delta_editor_t **editor,
+                            void **edit_baton,
+                            const svn_delta_editor_t *wrapped_editor,
+                            void *wrapped_edit_baton,
+                            apr_pool_t *pool);
diff --git a/svnclient_ra.c b/svnclient_ra.c
new file mode 100644
index 0000000..2ed04af
--- /dev/null
+++ b/svnclient_ra.c
@@ -0,0 +1,131 @@
+#include "svn_pools.h"
+#include "svn_cmdline.h"
+#include "svn_client.h"
+#include "svn_ra.h"
+
+#include "debug_editor.h"
+
+static apr_pool_t *pool = NULL;
+static svn_client_ctx_t *ctx = NULL;
+static svn_ra_session_t *session = NULL;
+
+typedef struct {
+	void *editor, *baton;
+} replay_baton_t;
+
+static svn_error_t *replay_revstart(svn_revnum_t revision,
+                                    void *replay_baton,
+                                    const svn_delta_editor_t **editor,
+                                    void **edit_baton,
+                                    apr_hash_t *rev_props,
+                                    apr_pool_t *pool)
+{
+	*editor = ((replay_baton_t *) replay_baton)->editor;
+	*edit_baton = ((replay_baton_t *) replay_baton)->baton;
+	return SVN_NO_ERROR;
+}
+
+static svn_error_t *replay_revend(svn_revnum_t revision,
+                                  void *replay_baton,
+                                  const svn_delta_editor_t *editor,
+                                  void *edit_baton,
+                                  apr_hash_t *rev_props,
+                                  apr_pool_t *pool)
+{
+	SVN_ERR(editor->close_edit(edit_baton, pool));
+	return SVN_NO_ERROR;
+}
+
+svn_error_t *build_auth_baton()
+{
+	svn_auth_provider_object_t *provider;
+	apr_array_header_t *providers
+		= apr_array_make (pool, 4, sizeof (svn_auth_provider_object_t *));
+
+	svn_auth_get_simple_prompt_provider (&provider,
+	                                     NULL,
+	                                     NULL,
+	                                     2,
+	                                     pool);
+	APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider;
+
+	svn_auth_get_username_prompt_provider (&provider,
+	                                       NULL,
+	                                       NULL,
+	                                       2,
+	                                       pool);
+	APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider;
+
+	svn_auth_open (&ctx->auth_baton, providers, pool);
+	return SVN_NO_ERROR;
+}
+
+svn_error_t *populate_context()
+{
+	const char *http_library;
+	
+	SVN_ERR(svn_config_get_config(&(ctx->config), NULL, pool));
+	
+	http_library = getenv("SVN_HTTP_LIBRARY");
+	if (http_library)
+		svn_config_set(apr_hash_get(ctx->config, "servers", APR_HASH_KEY_STRING),
+		               "global", "http-library", http_library);
+	return SVN_NO_ERROR;
+}
+
+svn_error_t *open_connection(const char *url)
+{
+	SVN_ERR(svn_config_ensure (NULL, pool));
+	SVN_ERR(svn_client_create_context (&ctx, pool));
+	SVN_ERR(svn_ra_initialize(pool));
+
+#if defined(WIN32) || defined(__CYGWIN__)
+	if (getenv("SVN_ASP_DOT_NET_HACK"))
+		SVN_ERR(svn_wc_set_adm_dir("_svn", pool));
+#endif
+
+	SVN_ERR(populate_context());
+	SVN_ERR(build_auth_baton());
+	SVN_ERR(svn_client_open_ra_session(&session, url, ctx, pool));
+	return SVN_NO_ERROR;
+}
+
+svn_error_t *replay_range(svn_revnum_t start_revision, svn_revnum_t end_revision)
+{
+	svn_revnum_t latest_revision;
+	svn_delta_editor_t *editor, *debug_editor;
+	replay_baton_t *replay_baton = apr_palloc(pool, sizeof(replay_baton_t));
+	void *debug_baton;
+	SVN_ERR(svn_ra_get_latest_revnum(session, &latest_revision, pool));
+	printf("%ld\n", latest_revision);
+	editor = svn_delta_default_editor(pool);
+	SVN_ERR(svn_delta__get_debug_editor((const svn_delta_editor_t **)&debug_editor,
+	                                    &debug_baton, editor,
+	                                    NULL, pool));
+	replay_baton->editor = debug_editor;
+	replay_baton->baton = debug_baton;
+	SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision,
+	                            0, TRUE, replay_revstart, replay_revend,
+	                            replay_baton, pool));
+	return SVN_NO_ERROR;
+}
+
+void close_connection()
+{
+	svn_pool_destroy(pool);
+}
+
+int main()
+{
+	const char url[] = "http://svn.apache.org/repos/asf";
+	svn_revnum_t start_revision = 1, end_revision = 5;
+	if (svn_cmdline_init ("svnclient_ra", stderr) != EXIT_SUCCESS)
+		return 1;
+	pool = svn_pool_create(NULL);
+
+	SVN_INT_ERR(open_connection(url));
+	SVN_INT_ERR(replay_range(start_revision, end_revision));
+
+	close_connection();
+	return 0;
+}
-- 
1.7.1
Jonathan Nieder· Jun 25, 2010, 00:14 UTC · re: Ramkumar Ramachandra · lore

Re: [WIP PATCH 1/7] Add skeleton RA svnclient

Ramkumar Ramachandra wrote:
> In future, it will dump the data in every revision
> to stdout in dumpfile format (hopefully) without resorting to the FS
> API.

For now, just some naïve questions. Warning: I know nothing about svn internals.

I assume this corresponds to the ra-svn branch of <http://github.com/artagnon/svn-dump-fast-export.git>. Has the relevant code changed much since you sent it?

Show 9 quoted lines
> --- /dev/null
> +++ b/debug_editor.c
> @@ -0,0 +1,413 @@
> +#include "svn_pools.h"
> +#include "svn_cmdline.h"
> +#include "svn_client.h"
> +#include "svn_ra.h"
> +
> +struct edit_baton
What is a baton?
[...]
> +  void *wrapped_edit_baton;
[...]
> +  void *edit_baton;
> +  void *wrapped_dir_baton;
[...]
> +  void *edit_baton;
> +  void *wrapped_file_baton;
Are these opaque types necessary?
Show 11 quoted lines
> +
> +static svn_error_t *
> +write_indent(struct edit_baton *eb, apr_pool_t *pool)
> +{
> +  int i;
> +
> +  for (i = 0; i < eb->indent_level; ++i)
> +    SVN_ERR(svn_stream_printf(eb->out, pool, " "));
> +
> +  return SVN_NO_ERROR;
> +}
What does this do?  Is SVN_ERR for debugging?  Where does the output go?
> +static svn_error_t *
> +set_target_revision(void *edit_baton,
> +                    svn_revnum_t target_revision,
> +                    apr_pool_t *pool)
[...]
> +static svn_error_t *
> +open_root(void *edit_baton,
[...]
> +static svn_error_t *
> +close_edit(void *edit_baton,
[...]

I take it these are callbacks? Is there overview documentation for them somewhere?

Show 33 quoted lines
> +svn_error_t *
> +svn_delta__get_debug_editor(const svn_delta_editor_t **editor,
> +                            void **edit_baton,
> +                            const svn_delta_editor_t *wrapped_editor,
> +                            void *wrapped_edit_baton,
> +                            apr_pool_t *pool)
> +{
> +  svn_delta_editor_t *tree_editor = svn_delta_default_editor(pool);
> +  struct edit_baton *eb = apr_palloc(pool, sizeof(*eb));
> +  apr_file_t *errfp;
> +  svn_stream_t *out;
> +
> +  apr_status_t apr_err = apr_file_open_stderr(&errfp, pool);
> +  if (apr_err)
> +    return svn_error_wrap_apr(apr_err, "Problem opening stderr");
> +
> +  out = svn_stream_from_aprfile2(errfp, TRUE, pool);
> +
> +  tree_editor->set_target_revision = set_target_revision;
> +  tree_editor->open_root = open_root;
> +  tree_editor->delete_entry = delete_entry;
> +  tree_editor->add_directory = add_directory;
> +  tree_editor->open_directory = open_directory;
> +  tree_editor->change_dir_prop = change_dir_prop;
> +  tree_editor->close_directory = close_directory;
> +  tree_editor->absent_directory = absent_directory;
> +  tree_editor->add_file = add_file;
> +  tree_editor->open_file = open_file;
> +  tree_editor->apply_textdelta = apply_textdelta;
> +  tree_editor->change_file_prop = change_file_prop;
> +  tree_editor->close_file = close_file;
> +  tree_editor->absent_file = absent_file;
> +  tree_editor->close_edit = close_edit;

I take it that the fields of svn_delta_editor_t do not have a well-defined order? Ugh.

In any case, I suspect this would be easier to read rearranged a little:
 1. declarations for callbacks
 2. get_debug_editor implementation
 3. definitions of types not needed in get_debug_editor()
 4. implementations of callbacks

That way, a person reading straight through can figure out what’s going on a little earlier.

> --- /dev/null
> +++ b/svnclient_ra.c
[...]
Show 14 quoted lines
> +int main()
> +{
> +	const char url[] = "http://svn.apache.org/repos/asf";
> +	svn_revnum_t start_revision = 1, end_revision = 5;
> +	if (svn_cmdline_init ("svnclient_ra", stderr) != EXIT_SUCCESS)
> +		return 1;
> +	pool = svn_pool_create(NULL);
> +
> +	SVN_INT_ERR(open_connection(url));
> +	SVN_INT_ERR(replay_range(start_revision, end_revision));
> +
> +	close_connection();
> +	return 0;
> +}

What is svn_cmdline_init? Is this code destined for inclusion in svn upstream, and if so, where can one find the surrounding code this should fit in with?

Jonathan
Daniel Shahaf· Jun 25, 2010, 09:07 UTC · re: Jonathan Nieder · lore

Re: [WIP PATCH 1/7] Add skeleton RA svnclient

Jonathan Nieder wrote on Fri, 25 Jun 2010 at 03:14 -0000:
Show 25 quoted lines
> Ramkumar Ramachandra wrote:
> 
> > In future, it will dump the data in every revision
> > to stdout in dumpfile format (hopefully) without resorting to the FS
> > API.
> 
> For now, just some naïve questions.  Warning: I know nothing about
> svn internals.
> 
> I assume this corresponds to the ra-svn branch of
> <http://github.com/artagnon/svn-dump-fast-export.git>.  Has the
> relevant code changed much since you sent it?
> 
> > --- /dev/null
> > +++ b/debug_editor.c
> > @@ -0,0 +1,413 @@
> > +#include "svn_pools.h"
> > +#include "svn_cmdline.h"
> > +#include "svn_client.h"
> > +#include "svn_ra.h"
> > +
> > +struct edit_baton
> 
> What is a baton?
> 
The context object for a callback.
You call:
    some_function(your_callback_function, your_baton)
which then calls:
    your_callback_function(your_baton, other_arguments)
Show 11 quoted lines
> [...]
> > +  void *wrapped_edit_baton;
> [...]
> > +  void *edit_baton;
> > +  void *wrapped_dir_baton;
> [...]
> > +  void *edit_baton;
> > +  void *wrapped_file_baton;
> 
> Are these opaque types necessary?
> 

The convention in Subversion's code is to convert the void * to a concrete_baton_t * only inside the callback. If you wish to declare these, e.g., as

    debug_editor_baton_t *wrapped_baton;
You can probably do that too.
Show 13 quoted lines
> > +
> > +static svn_error_t *
> > +write_indent(struct edit_baton *eb, apr_pool_t *pool)
> > +{
> > +  int i;
> > +
> > +  for (i = 0; i < eb->indent_level; ++i)
> > +    SVN_ERR(svn_stream_printf(eb->out, pool, " "));
> > +
> > +  return SVN_NO_ERROR;
> > +}
> 
> What does this do?  Is SVN_ERR for debugging?

That's how we implement exception throwing in C. SVN_ERR means "if this returned a non-NULL svn_error_t *, then return that error to our caller".

The other pattern does
    svn_error_t *err = svn_stream_printf();

and then inspects err and err->apr_err to decide whether to ignore the error or return it (possibly wrapped).

> Where does the output go?
> 
SVN_ERR does not print anything.  It may return(), though.
Show 15 quoted lines
> > +static svn_error_t *
> > +set_target_revision(void *edit_baton,
> > +                    svn_revnum_t target_revision,
> > +                    apr_pool_t *pool)
> [...]
> > +static svn_error_t *
> > +open_root(void *edit_baton,
> [...]
> > +static svn_error_t *
> > +close_edit(void *edit_baton,
> [...]
> 
> I take it these are callbacks?  Is there overview documentation for
> them somewhere?
> 

svn_delta_editor_t in http://svn.apache.org/repos/asf/subversion/trunk/subversion/include/svn_delta.h

Show 37 quoted lines
> > +svn_error_t *
> > +svn_delta__get_debug_editor(const svn_delta_editor_t **editor,
> > +                            void **edit_baton,
> > +                            const svn_delta_editor_t *wrapped_editor,
> > +                            void *wrapped_edit_baton,
> > +                            apr_pool_t *pool)
> > +{
> > +  svn_delta_editor_t *tree_editor = svn_delta_default_editor(pool);
> > +  struct edit_baton *eb = apr_palloc(pool, sizeof(*eb));
> > +  apr_file_t *errfp;
> > +  svn_stream_t *out;
> > +
> > +  apr_status_t apr_err = apr_file_open_stderr(&errfp, pool);
> > +  if (apr_err)
> > +    return svn_error_wrap_apr(apr_err, "Problem opening stderr");
> > +
> > +  out = svn_stream_from_aprfile2(errfp, TRUE, pool);
> > +
> > +  tree_editor->set_target_revision = set_target_revision;
> > +  tree_editor->open_root = open_root;
> > +  tree_editor->delete_entry = delete_entry;
> > +  tree_editor->add_directory = add_directory;
> > +  tree_editor->open_directory = open_directory;
> > +  tree_editor->change_dir_prop = change_dir_prop;
> > +  tree_editor->close_directory = close_directory;
> > +  tree_editor->absent_directory = absent_directory;
> > +  tree_editor->add_file = add_file;
> > +  tree_editor->open_file = open_file;
> > +  tree_editor->apply_textdelta = apply_textdelta;
> > +  tree_editor->change_file_prop = change_file_prop;
> > +  tree_editor->close_file = close_file;
> > +  tree_editor->absent_file = absent_file;
> > +  tree_editor->close_edit = close_edit;
> 
> I take it that the fields of svn_delta_editor_t do not have a
> well-defined order?  Ugh.
> 

I don't understand. It seems that the fields here appear in the same order as in the definition of struct svn_delta_editor_t.

Show 29 quoted lines
> In any case, I suspect this would be easier to read rearranged a little:
> 
>  1. declarations for callbacks
>  2. get_debug_editor implementation
>  3. definitions of types not needed in get_debug_editor()
>  4. implementations of callbacks
> 
> That way, a person reading straight through can figure out what’s
> going on a little earlier.
> 
> > --- /dev/null
> > +++ b/svnclient_ra.c
> [...]
> > +int main()
> > +{
> > +	const char url[] = "http://svn.apache.org/repos/asf";
> > +	svn_revnum_t start_revision = 1, end_revision = 5;
> > +	if (svn_cmdline_init ("svnclient_ra", stderr) != EXIT_SUCCESS)
> > +		return 1;
> > +	pool = svn_pool_create(NULL);
> > +
> > +	SVN_INT_ERR(open_connection(url));
> > +	SVN_INT_ERR(replay_range(start_revision, end_revision));
> > +
> > +	close_connection();
> > +	return 0;
> > +}
> 
> What is svn_cmdline_init?

A Subversion API that does some necessary initializations (e.g., calls apr_initialize()). See subversion/include/svn_cmdline.h for docs (and subversion/libsvn_subr/cmdline.c for the implementation).

Show 5 quoted lines
> Is this code destined for inclusion in svn upstream, and if so, where
> can one find the surrounding code this should fit in with?
> 
> Jonathan
> 
Ramkumar Ramachandra· Jun 25, 2010, 11:07 UTC · re: Daniel Shahaf · lore

Re: [WIP PATCH 1/7] Add skeleton RA svnclient

Hi Jonathan and Daniel,
Jonathan: First, thanks for bringing up these questions- they will
definitely help future reviewers. I'll now sprinkle Daniel's replies
with a few of my observations.
Daniel: Thanks for responding to the questions- your insights as a
core Subversion developer are most appreciated. I'll add a few notes
to your answers now.
Daniel Shahaf wrote:
> Jonathan Nieder wrote on Fri, 25 Jun 2010 at 03:14 -0000:
>> For now, just some naïve questions.  Warning: I know nothing about
>> svn internals.

That's alright. I highly encourage naïve questions- I'm new to SVN myself. I also highly recommend reading the API documentation and going through the code for answers to "why is it like THIS" questions as I haven't manged to clean out the Subversion style yet. Hopefully, I'll have some good notes that I can attach with my next series to put in the trunk.

>> I assume this corresponds to the ra-svn branch of
>> <http://github.com/artagnon/svn-dump-fast-export.git>.  Has the
>> relevant code changed much since you sent it?

Yes. I've managed to fix the deltified dump, and have now started working on a full text dump. Due to my low familiarity with the libsvn API, the cleanups are mixed with my code in the history; it needs a thorough line-by-line scrubbing.

Show 12 quoted lines
>> What is a baton?
>>
>
> The context object for a callback.
>
> You call:
>
>    some_function(your_callback_function, your_baton)
>
> which then calls:
>
>    your_callback_function(your_baton, other_arguments)

In general, I've found that batons are void * objects in which you can stuff anything you like and pass around from function to function: I've abused them quite heavily in my code by stuffing all kinds of things into them: see fb->eb->* in my current code for an example of this.

Show 19 quoted lines
>> [...]
>> > +  void *wrapped_edit_baton;
>> [...]
>> > +  void *edit_baton;
>> > +  void *wrapped_dir_baton;
>> [...]
>> > +  void *edit_baton;
>> > +  void *wrapped_file_baton;
>>
>> Are these opaque types necessary?
>>
>
> The convention in Subversion's code is to convert the void * to
> a concrete_baton_t * only inside the callback.  If you wish to declare
> these, e.g., as
>
>    debug_editor_baton_t *wrapped_baton;
>
> You can probably do that too.

The function prototypes in libsvn contain void * parameters corresponding to batons, so I'd have to typecast explicitly to avoid any warnings. I think Jonathan's also referring to the absence of the "struct" keyword everywhere, as that is against Git policy. Unfortunately, everything is typedef'ed in libsvn, and we cannot do much about that.

Show 12 quoted lines
>> What does this do?  Is SVN_ERR for debugging?
>
> That's how we implement exception throwing in C.  SVN_ERR means "if this
> returned a non-NULL svn_error_t *, then return that error to our
> caller".
>
> The other pattern does
>
>    svn_error_t *err = svn_stream_printf();
>
> and then inspects err and err->apr_err to decide whether to ignore the
> error or return it (possibly wrapped).
>> Where does the output go?
>>
>
> SVN_ERR does not print anything.  It may return(), though.

Embarrassingly enough, write_indent does exactly what it says it does: It writes some spaces (or indent) to eb->out, which you'll find is actually stderr in svn_delta__get_debug. In the cleanup, I should probably get rid of this. As far as the error handling is concerned, to be terse, SVN_ERR and SVN_INT_ERR are cpp macros. They are defined as follows in svn_error.h (with line numbers from the trunk file). Note however, that even with all this error handling, the most common type of error I get by far is the segfault: I'll make an effort to document the pitfalls.

00284 #define SVN_ERR(expr) \ 00285 do { \ 00286 svn_error_t *svn_err__temp = (expr); \ 00287 if (svn_err__temp) \ 00288 return svn_error_return(svn_err__temp); \ 00289 } while (0)

00336 #define SVN_INT_ERR(expr) \ 00337 do { \ 00338 svn_error_t *svn_err__temp = (expr); \ 00339 if (svn_err__temp) { \ 00340 svn_handle_error2(svn_err__temp, stderr, FALSE, "svn: "); \ 00341 svn_error_clear(svn_err__temp); \ 00342 return EXIT_FAILURE; } \ 00343 } while (0)

Show 6 quoted lines
>> I take it these are callbacks?  Is there overview documentation for
>> them somewhere?
>>
>
> svn_delta_editor_t in
> http://svn.apache.org/repos/asf/subversion/trunk/subversion/include/svn_delta.h

Yes, they are callbacks that are fired automatically by the editor. In svn_delta.h, look at struct svn_delta_editor_t (and the corresponding doxygen-style comments).

>> I take it that the fields of svn_delta_editor_t do not have a
>> well-defined order?  Ugh.

No, they don't. I've tried to stick to the order in the struct svn_delta_editor_t.

Show 9 quoted lines
>> In any case, I suspect this would be easier to read rearranged a little:
>>
>>  1. declarations for callbacks
>>  2. get_debug_editor implementation
>>  3. definitions of types not needed in get_debug_editor()
>>  4. implementations of callbacks
>>
>> That way, a person reading straight through can figure out what’s
>> going on a little earlier.

Agreed. I'm still struggling to clean up the Subversion-style code. We can probably discuss this at length on IRC?

Show 5 quoted lines
>> What is svn_cmdline_init?
>
> A Subversion API that does some necessary initializations (e.g., calls
> apr_initialize()).  See subversion/include/svn_cmdline.h for docs
> (and subversion/libsvn_subr/cmdline.c for the implementation).

These svn_cmdline functions are actually shortcuts- they do all the initializations required for a "typical" command line SVN client. It saves me the trouble of having to figure out what I missed initializing: I'll be using more of them in future; to eliminate the auth baton creation by hand, for example.

>> Is this code destined for inclusion in svn upstream, and if so, where
>> can one find the surrounding code this should fit in with?

Yes, but with a lot of style transformations. In svnsync/main.c. Atleast that's the plan, as per the discussion on #svn-dev

-- Ram
Daniel Shahaf· Jun 25, 2010, 11:30 UTC · re: Ramkumar Ramachandra · lore

Re: [WIP PATCH 1/7] Add skeleton RA svnclient

Ramkumar Ramachandra wrote on Fri, 25 Jun 2010 at 14:07 -0000:
Show 21 quoted lines
> Daniel Shahaf wrote:
> > Jonathan Nieder wrote on Fri, 25 Jun 2010 at 03:14 -0000:
> >> What is a baton?
> >>
> >
> > The context object for a callback.
> >
> > You call:
> >
> >    some_function(your_callback_function, your_baton)
> >
> > which then calls:
> >
> >    your_callback_function(your_baton, other_arguments)
> 
> In general, I've found that batons are void * objects in which you can
> stuff anything you like and pass around from function to function:
> I've abused them quite heavily in my code by stuffing all kinds of
> things into them: see fb->eb->* in my current code for an example of
> this.
> 
We do that too sometimes:

% pwd $trunk_wc/subversion/libsvn_client/ % grep -- "->eb->" *.c | head commit_util.c: return (*db->eb->real_editor->add_file)(path, db->real_baton, commit_util.c: return (*db->eb->real_editor->delete_entry)(path, revision, commit_util.c: return (*db->eb->real_editor->open_file)(path, db->real_baton, commit_util.c: return (*fb->eb->real_editor->close_file)(fb->real_baton, commit_util.c: return (*fb->eb->real_editor->change_file_prop)(fb->real_baton, commit_util.c: return (*fb->eb->real_editor->apply_textdelta)(fb->real_baton, ...

Show 23 quoted lines
> >> [...]
> >> > +  void *wrapped_edit_baton;
> >> [...]
> >> > +  void *edit_baton;
> >> > +  void *wrapped_dir_baton;
> >> [...]
> >> > +  void *edit_baton;
> >> > +  void *wrapped_file_baton;
> >>
> >> Are these opaque types necessary?
> >>
> >
> > The convention in Subversion's code is to convert the void * to
> > a concrete_baton_t * only inside the callback.  If you wish to declare
> > these, e.g., as
> >
> >    debug_editor_baton_t *wrapped_baton;
> >
> > You can probably do that too.
> 
> The function prototypes in libsvn contain void * parameters
> corresponding to batons, so I'd have to typecast explicitly to avoid
> any warnings.

By the way, a common pitfall is to mis-treat the void *baton argument as the wrong type of baton. That is, code of the form

    f(void *baton) {
        foo_baton_t *fb = baton;
    }
instead of
    f(void *baton) {
        bar_baton_t *bb = baton;
    }
Show 5 quoted lines
> I think Jonathan's also referring to the absence of the
> "struct" keyword everywhere, as that is against Git policy.
> Unfortunately, everything is typedef'ed in libsvn, and we cannot do
> much about that.
> 
Usually we do
    typedef struct svn_error_t svn_error_t;
so you can add the 'struct' back if you want.
Show 20 quoted lines
> >> What does this do?  Is SVN_ERR for debugging?
> >
> > That's how we implement exception throwing in C.  SVN_ERR means "if this
> > returned a non-NULL svn_error_t *, then return that error to our
> > caller".
> >
> > The other pattern does
> >
> >    svn_error_t *err = svn_stream_printf();
> >
> > and then inspects err and err->apr_err to decide whether to ignore the
> > error or return it (possibly wrapped).
> 
> >> Where does the output go?
> >>
> >
> > SVN_ERR does not print anything.  It may return(), though.
> 
> Embarrassingly enough, write_indent does exactly what it says it does:
> It writes some spaces (or indent) to eb->out
To represent nesting.
> Note however, that even with all this error handling, the most common
> type of error I get by far is the segfault: I'll make an effort to
> document the pitfalls.
> 

Note there are several kinds of errors that can cause a segfault. (@Ram, you've encountered some of them already, I know.)

Show 11 quoted lines
> >> I take it these are callbacks?  Is there overview documentation for
> >> them somewhere?
> >>
> >
> > svn_delta_editor_t in
> > http://svn.apache.org/repos/asf/subversion/trunk/subversion/include/svn_delta.h
> 
> Yes, they are callbacks that are fired automatically by the editor. In
> svn_delta.h, look at struct svn_delta_editor_t (and the corresponding
> doxygen-style comments).
> 

The compiled doxygen docs are available on <http://subversion.apache.org/docs/#api>. Personally I always just read the header file directly (with :set filetype=c.doxygen).

Jonathan Nieder· Jun 25, 2010, 15:30 UTC · re: Daniel Shahaf · lore

OT: typesafe callbacks in C (Re: [WIP PATCH 1/7] Add skeleton RA svnclient)

Daniel Shahaf wrote:
Show 12 quoted lines
> By the way, a common pitfall is to mis-treat the void *baton argument as
> the wrong type of baton.  That is, code of the form
> 
>     f(void *baton) {
>         foo_baton_t *fb = baton;
>     }
> 
> instead of
> 
>     f(void *baton) {
>         bar_baton_t *bb = baton;
>     }
Just for kicks, I should mention
  http://ccan.ozlabs.org/info/typesafe_cb.html
which makes code like this cause warnings with gcc.
Maybe some version of Subversion years from now could use it.
Jonathan Nieder· Jun 25, 2010, 14:45 UTC · re: Ramkumar Ramachandra · lore

Re: [WIP PATCH 1/7] Add skeleton RA svnclient

Hi Ram,
Ramkumar Ramachandra wrote:
> I also highly recommend reading the API documentation and
> going through the code for answers to "why is it like THIS" questions
> as I haven't manged to clean out the Subversion style yet.

If this code is ultimately destined for the svnsync tree (as it seems it is), it might be possible to save time by just sticking to the svn style. I think this would be a typical place where we relax the git style guidelines a bit.

On the other hand, during development, if you want to use some other style (or if you want to write in Smalltalk for that matter), that’s fine by me. I don’t want to dictate your development style. I am only mentioning style now because I suspect it would be easier to review code that follows a single convention (which could be Subversion’s, or Git’s, or some well defined hybrid, or something else entirely, or ...).

>> SVN_ERR does not print anything.  It may return(), though.
Ah, so it is an exception handling mechanism.
Show 5 quoted lines
> These svn_cmdline functions are actually shortcuts- they do all the
> initializations required for a "typical" command line SVN client. It
> saves me the trouble of having to figure out what I missed
> initializing: I'll be using more of them in future; to eliminate the
> auth baton creation by hand, for example.
Thanks again for the explanations.
Jonathan
Jonathan Nieder· Jun 25, 2010, 13:34 UTC · re: Daniel Shahaf · lore

Re: [WIP PATCH 1/7] Add skeleton RA svnclient

Daniel Shahaf wrote:
> I don't understand.  It seems that the fields here appear in the same
> order as in the definition of struct svn_delta_editor_t.
Embarrassingly, I was just confused.  Thanks for the answers!
Jonathan
Ramkumar Ramachandra· Jun 23, 2010, 16:22 UTC · re: Ramkumar Ramachandra · lore

[WIP PATCH 2/7] Add stripped dump editor

Replace the default editor with a dump editor from libsvn_repos/dump.c after stripping off filesystem dependencies. Currently, the dump editor does nothing, but will dump some revision information to stdout in future.

Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 Makefile       |    2 +-
 dump_editor.c  |  365 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 dump_editor.h  |   10 ++
 svnclient_ra.c |   19 ++-
 4 files changed, 389 insertions(+), 7 deletions(-)
 create mode 100644 dump_editor.c
 create mode 100644 dump_editor.h
diff --git a/Makefile b/Makefile
index c3c695c..269c406 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
 svnclient_ra: *.c *.h
-	$(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
+	$(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
 clean:
 	$(RM) svnclient_ra
diff --git a/dump_editor.c b/dump_editor.c
new file mode 100644
index 0000000..f5353b0
--- /dev/null
+++ b/dump_editor.c
@@ -0,0 +1,365 @@
+#include "svn_pools.h"
+#include "svn_error.h"
+#include "svn_iter.h"
+#include "svn_repos.h"
+#include "svn_string.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_time.h"
+#include "svn_checksum.h"
+#include "svn_props.h"
+
+#define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
+/*----------------------------------------------------------------------*/
+/** An editor which dumps node-data in 'dumpfile format' to a file. **/
+
+/* Look, mom!  No file batons! */
+
+struct edit_baton
+{
+	/* The stream to dump to: stdout */
+	svn_stream_t *stream;
+	
+	svn_revnum_t current_rev;
+	
+	/* reusable buffer for writing file contents */
+	char buffer[SVN__STREAM_CHUNK_SIZE];
+	apr_size_t bufsize;
+};
+
+struct dir_baton
+{
+	struct edit_baton *edit_baton;
+	struct dir_baton *parent_dir_baton;
+
+	/* is this directory a new addition to this revision? */
+	svn_boolean_t added;
+
+	/* has this directory been written to the output stream? */
+	svn_boolean_t written_out;
+
+	/* the absolute path to this directory */
+	const char *path;
+
+	/* the comparison path and revision of this directory.  if both of
+	   these are valid, use them as a source against which to compare
+	   the directory instead of the default comparison source of PATH in
+	   the previous revision. */
+	const char *cmp_path;
+	svn_revnum_t cmp_rev;
+
+	/* hash of paths that need to be deleted, though some -might- be
+	   replaced.  maps const char * paths to this dir_baton.  (they're
+	   full paths, because that's what the editor driver gives us.  but
+	   really, they're all within this directory.) */
+	apr_hash_t *deleted_entries;
+
+	/* pool to be used for deleting the hash items */
+	apr_pool_t *pool;
+};
+
+
+/* Make a directory baton to represent the directory was path
+   (relative to EDIT_BATON's path) is PATH.
+
+   CMP_PATH/CMP_REV are the path/revision against which this directory
+   should be compared for changes.  If either is omitted (NULL for the
+   path, SVN_INVALID_REVNUM for the rev), just compare this directory
+   PATH against itself in the previous revision.
+
+   PARENT_DIR_BATON is the directory baton of this directory's parent,
+   or NULL if this is the top-level directory of the edit.  ADDED
+   indicated if this directory is newly added in this revision.
+   Perform all allocations in POOL.  */
+struct dir_baton *
+make_dir_baton(const char *path,
+               const char *cmp_path,
+               svn_revnum_t cmp_rev,
+               void *edit_baton,
+               void *parent_dir_baton,
+               svn_boolean_t added,
+               apr_pool_t *pool)
+{
+	struct edit_baton *eb = edit_baton;
+	struct dir_baton *pb = parent_dir_baton;
+	struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db));
+	const char *full_path;
+
+	/* A path relative to nothing?  I don't think so. */
+	SVN_ERR_ASSERT_NO_RETURN(!path || pb);
+
+	/* Construct the full path of this node. */
+	if (pb)
+		full_path = svn_path_join("/", path, pool);
+	else
+		full_path = apr_pstrdup(pool, "/");
+
+	/* Remove leading slashes from copyfrom paths. */
+	if (cmp_path)
+		cmp_path = ((*cmp_path == '/') ? cmp_path + 1 : cmp_path);
+
+	new_db->edit_baton = eb;
+	new_db->parent_dir_baton = pb;
+	new_db->path = full_path;
+	new_db->cmp_path = cmp_path ? apr_pstrdup(pool, cmp_path) : NULL;
+	new_db->cmp_rev = cmp_rev;
+	new_db->added = added;
+	new_db->written_out = FALSE;
+	new_db->deleted_entries = apr_hash_make(pool);
+	new_db->pool = pool;
+
+	return new_db;
+}
+
+
+svn_error_t *
+open_root(void *edit_baton,
+          svn_revnum_t base_revision,
+          apr_pool_t *pool,
+          void **root_baton)
+{
+	*root_baton = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
+	                             edit_baton, NULL, FALSE, pool);
+	return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+delete_entry(const char *path,
+             svn_revnum_t revision,
+             void *parent_baton,
+             apr_pool_t *pool)
+{
+	struct dir_baton *pb = parent_baton;
+	const char *mypath = apr_pstrdup(pb->pool, path);
+
+	/* remember this path needs to be deleted. */
+	apr_hash_set(pb->deleted_entries, mypath, APR_HASH_KEY_STRING, pb);
+
+	return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+add_directory(const char *path,
+              void *parent_baton,
+              const char *copyfrom_path,
+              svn_revnum_t copyfrom_rev,
+              apr_pool_t *pool,
+              void **child_baton)
+{
+	struct dir_baton *pb = parent_baton;
+	struct edit_baton *eb = pb->edit_baton;
+	void *val;
+	svn_boolean_t is_copy = FALSE;
+	struct dir_baton *new_db
+		= make_dir_baton(path, copyfrom_path, copyfrom_rev, eb, pb, TRUE, pool);
+
+	/* This might be a replacement -- is the path already deleted? */
+	val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
+
+	/* Detect an add-with-history. */
+	is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
+
+	/* Dump the node. */
+	/* SVN_ERR(dump_node(eb, path, */
+	/*                   svn_node_dir, */
+	/*                   val ? svn_node_action_replace : svn_node_action_add, */
+	/*                   is_copy, */
+	/*                   is_copy ? copyfrom_path : NULL, */
+	/*                   is_copy ? copyfrom_rev : SVN_INVALID_REVNUM, */
+	/*                   pool)); */
+
+	if (val)
+		/* Delete the path, it's now been dumped. */
+		apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
+
+	new_db->written_out = TRUE;
+
+	*child_baton = new_db;
+	return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+open_directory(const char *path,
+               void *parent_baton,
+               svn_revnum_t base_revision,
+               apr_pool_t *pool,
+               void **child_baton)
+{
+	struct dir_baton *pb = parent_baton;
+	struct edit_baton *eb = pb->edit_baton;
+	struct dir_baton *new_db;
+	const char *cmp_path = NULL;
+	svn_revnum_t cmp_rev = SVN_INVALID_REVNUM;
+
+	/* If the parent directory has explicit comparison path and rev,
+	   record the same for this one. */
+	if (pb && ARE_VALID_COPY_ARGS(pb->cmp_path, pb->cmp_rev))
+	{
+		cmp_path = svn_path_join(pb->cmp_path,
+		                         svn_dirent_basename(path, pool), pool);
+		cmp_rev = pb->cmp_rev;
+	}
+
+	new_db = make_dir_baton(path, cmp_path, cmp_rev, eb, pb, FALSE, pool);
+	*child_baton = new_db;
+	return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+close_directory(void *dir_baton,
+                apr_pool_t *pool)
+{
+	struct dir_baton *db = dir_baton;
+	struct edit_baton *eb = db->edit_baton;
+	apr_hash_index_t *hi;
+	apr_pool_t *subpool = svn_pool_create(pool);
+
+	for (hi = apr_hash_first(pool, db->deleted_entries);
+	     hi;
+	     hi = apr_hash_next(hi))
+	{
+		const void *key;
+		const char *path;
+		apr_hash_this(hi, &key, NULL, NULL);
+		path = key;
+
+		svn_pool_clear(subpool);
+
+		/* By sending 'svn_node_unknown', the Node-kind: header simply won't
+		   be written out.  No big deal at all, really.  The loader
+		   shouldn't care.  */
+		/* SVN_ERR(dump_node(eb, path, */
+		/*                   svn_node_unknown, svn_node_action_delete, */
+		/*                   FALSE, NULL, SVN_INVALID_REVNUM, subpool)); */
+	}
+
+	svn_pool_destroy(subpool);
+	return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+add_file(const char *path,
+         void *parent_baton,
+         const char *copyfrom_path,
+         svn_revnum_t copyfrom_rev,
+         apr_pool_t *pool,
+         void **file_baton)
+{
+	struct dir_baton *pb = parent_baton;
+	struct edit_baton *eb = pb->edit_baton;
+	void *val;
+	svn_boolean_t is_copy = FALSE;
+
+	/* This might be a replacement -- is the path already deleted? */
+	val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
+
+	/* Detect add-with-history. */
+	is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
+
+	/* Dump the node. */
+	/* SVN_ERR(dump_node(eb, path, */
+	/*                   svn_node_file, */
+	/*                   val ? svn_node_action_replace : svn_node_action_add, */
+	/*                   is_copy, */
+	/*                   is_copy ? copyfrom_path : NULL, */
+	/*                   is_copy ? copyfrom_rev : SVN_INVALID_REVNUM, */
+	/*                   pool)); */
+
+	if (val)
+		/* delete the path, it's now been dumped. */
+		apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
+
+	*file_baton = NULL;  /* muhahahaha */
+	return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+open_file(const char *path,
+          void *parent_baton,
+          svn_revnum_t ancestor_revision,
+          apr_pool_t *pool,
+          void **file_baton)
+{
+	struct dir_baton *pb = parent_baton;
+	struct edit_baton *eb = pb->edit_baton;
+	const char *cmp_path = NULL;
+	svn_revnum_t cmp_rev = SVN_INVALID_REVNUM;
+
+	/* If the parent directory has explicit comparison path and rev,
+	   record the same for this one. */
+	if (pb && ARE_VALID_COPY_ARGS(pb->cmp_path, pb->cmp_rev))
+	{
+		cmp_path = svn_path_join(pb->cmp_path,
+		                         svn_dirent_basename(path, pool), pool);
+		cmp_rev = pb->cmp_rev;
+	}
+
+	/* SVN_ERR(dump_node(eb, path, */
+	/*                   svn_node_file, svn_node_action_change, */
+	/*                   FALSE, cmp_path, cmp_rev, pool)); */
+
+	*file_baton = NULL;  /* muhahahaha again */
+	return SVN_NO_ERROR;
+}
+
+svn_error_t *
+change_dir_prop(void *parent_baton,
+                const char *name,
+                const svn_string_t *value,
+                apr_pool_t *pool)
+{
+	struct dir_baton *db = parent_baton;
+	struct edit_baton *eb = db->edit_baton;
+
+	/* This function is what distinguishes between a directory that is
+	   opened to merely get somewhere, vs. one that is opened because it
+	   *actually* changed by itself.  */
+	if (! db->written_out)
+	{
+		/* SVN_ERR(dump_node(eb, db->path, */
+		/*                   svn_node_dir, svn_node_action_change, */
+		/*                   FALSE, db->cmp_path, db->cmp_rev, pool)); */
+		db->written_out = TRUE;
+	}
+	return SVN_NO_ERROR;
+}
+
+svn_error_t *
+get_dump_editor(const svn_delta_editor_t **editor,
+                void **edit_baton,
+                svn_revnum_t to_rev,
+                apr_pool_t *pool)
+{
+	/* Allocate an edit baton to be stored in every directory baton.
+	   Set it up for the directory baton we create here, which is the
+	   root baton. */
+	struct edit_baton *eb = apr_pcalloc(pool, sizeof(*eb));
+	svn_delta_editor_t *dump_editor = svn_delta_default_editor(pool);
+
+	/* Set up the edit baton. */
+	svn_stream_for_stdout(&(eb->stream), pool);
+	eb->bufsize = sizeof(eb->buffer);
+	eb->current_rev = to_rev;
+
+
+	/* Set up the editor. */
+	dump_editor->open_root = open_root;
+	dump_editor->delete_entry = delete_entry;
+	dump_editor->add_directory = add_directory;
+	dump_editor->open_directory = open_directory;
+	dump_editor->close_directory = close_directory;
+	dump_editor->change_dir_prop = change_dir_prop;
+	dump_editor->add_file = add_file;
+	dump_editor->open_file = open_file;
+
+	*edit_baton = eb;
+	*editor = dump_editor;
+
+	return SVN_NO_ERROR;
+}
diff --git a/dump_editor.h b/dump_editor.h
new file mode 100644
index 0000000..5e84223
--- /dev/null
+++ b/dump_editor.h
@@ -0,0 +1,10 @@
+#include "svn_pools.h"
+#include "svn_cmdline.h"
+#include "svn_client.h"
+#include "svn_ra.h"
+
+svn_error_t *
+get_dump_editor(const svn_delta_editor_t **editor,
+                void **edit_baton,
+                svn_revnum_t to_rev,
+                apr_pool_t *pool);
diff --git a/svnclient_ra.c b/svnclient_ra.c
index 2ed04af..24d99cb 100644
--- a/svnclient_ra.c
+++ b/svnclient_ra.c
@@ -4,6 +4,7 @@
 #include "svn_ra.h"
 
 #include "debug_editor.h"
+#include "dump_editor.h"
 
 static apr_pool_t *pool = NULL;
 static svn_client_ctx_t *ctx = NULL;
@@ -92,16 +93,22 @@ svn_error_t *open_connection(const char *url)
 
 svn_error_t *replay_range(svn_revnum_t start_revision, svn_revnum_t end_revision)
 {
+	svn_delta_editor_t *dump_editor, *debug_editor;
+	void *debug_baton, *dump_baton;
+	
 	svn_revnum_t latest_revision;
-	svn_delta_editor_t *editor, *debug_editor;
-	replay_baton_t *replay_baton = apr_palloc(pool, sizeof(replay_baton_t));
-	void *debug_baton;
 	SVN_ERR(svn_ra_get_latest_revnum(session, &latest_revision, pool));
 	printf("%ld\n", latest_revision);
-	editor = svn_delta_default_editor(pool);
+	
+	SVN_ERR(get_dump_editor((const svn_delta_editor_t **)&dump_editor,
+	                        &dump_baton, end_revision, pool));
+
 	SVN_ERR(svn_delta__get_debug_editor((const svn_delta_editor_t **)&debug_editor,
-	                                    &debug_baton, editor,
-	                                    NULL, pool));
+	                                    &debug_baton,
+	                                    (const svn_delta_editor_t *)dump_editor,
+	                                    dump_baton, pool));
+	
+	replay_baton_t *replay_baton = apr_palloc(pool, sizeof(replay_baton_t));
 	replay_baton->editor = debug_editor;
 	replay_baton->baton = debug_baton;
 	SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision,
-- 
1.7.1
Ramkumar Ramachandra· Jun 23, 2010, 16:22 UTC · re: Ramkumar Ramachandra · lore

[WIP PATCH 3/7] Import dump_node to dump what changed and cleanup whitespace

Import dump_node from dump.c after stripping it off filesystem backing to dump which files/ directories were added or removed without actually dumping the delta. Modify svnclient_ra to use just the dump editor, and not the debug editor. Also cleanup whitespace to conform to Git style. Add LICENSE file.

Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 LICENSE        |   14 +++
 dump_editor.c  |  344 ++++++++++++++++++++++++++++++++++++++------------------
 svnclient_ra.c |    6 +-
 3 files changed, 251 insertions(+), 113 deletions(-)
 create mode 100644 LICENSE
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c8f39ff
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,14 @@
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to you under the Apache License, Version
+2.0 (the "License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+implied.  See the License for the specific language governing
+permissions and limitations under the License.
diff --git a/dump_editor.c b/dump_editor.c
index f5353b0..ba0630f 100644
--- a/dump_editor.c
+++ b/dump_editor.c
@@ -1,3 +1,7 @@
+/* Licensed under Apache license.
+ * See LICENSE for details.
+ */
+
 #include "svn_pools.h"
 #include "svn_error.h"
 #include "svn_iter.h"
@@ -10,13 +14,8 @@
 #include "svn_props.h"
 
 #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
-/*----------------------------------------------------------------------*/
-/** An editor which dumps node-data in 'dumpfile format' to a file. **/
-
-/* Look, mom!  No file batons! */
 
-struct edit_baton
-{
+struct edit_baton {
 	/* The stream to dump to: stdout */
 	svn_stream_t *stream;
 	
@@ -27,8 +26,7 @@ struct edit_baton
 	apr_size_t bufsize;
 };
 
-struct dir_baton
-{
+struct dir_baton {
 	struct edit_baton *edit_baton;
 	struct dir_baton *parent_dir_baton;
 
@@ -71,20 +69,19 @@ struct dir_baton
    or NULL if this is the top-level directory of the edit.  ADDED
    indicated if this directory is newly added in this revision.
    Perform all allocations in POOL.  */
-struct dir_baton *
-make_dir_baton(const char *path,
-               const char *cmp_path,
-               svn_revnum_t cmp_rev,
-               void *edit_baton,
-               void *parent_dir_baton,
-               svn_boolean_t added,
-               apr_pool_t *pool)
-{
+struct dir_baton *make_dir_baton(const char *path,
+                                 const char *cmp_path,
+                                 svn_revnum_t cmp_rev,
+                                 void *edit_baton,
+                                 void *parent_dir_baton,
+                                 svn_boolean_t added,
+                                 apr_pool_t *pool) {
 	struct edit_baton *eb = edit_baton;
 	struct dir_baton *pb = parent_dir_baton;
 	struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db));
 	const char *full_path;
 
+
 	/* A path relative to nothing?  I don't think so. */
 	SVN_ERR_ASSERT_NO_RETURN(!path || pb);
 
@@ -111,24 +108,170 @@ make_dir_baton(const char *path,
 	return new_db;
 }
 
+static svn_error_t *dump_node(struct edit_baton *eb,
+                              const char *path,    /* an absolute path. */
+                              svn_node_kind_t kind,
+                              enum svn_node_action action,
+                              svn_boolean_t is_copy,
+                              const char *cmp_path,
+                              svn_revnum_t cmp_rev,
+                              apr_pool_t *pool)
+{
+	apr_size_t len;
+	svn_boolean_t must_dump_text = TRUE, must_dump_props = TRUE;
+	const char *compare_path = path;
+	svn_revnum_t compare_rev = eb->current_rev - 1;
+
+	/* Write out metadata headers for this file node. */
+	SVN_ERR(svn_stream_printf(eb->stream, pool,
+	                          SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n",
+	                          (*path == '/') ? path + 1 : path));
+	if (kind == svn_node_file)
+		SVN_ERR(svn_stream_printf(eb->stream, pool,
+		                          SVN_REPOS_DUMPFILE_NODE_KIND ": file\n"));
+	else if (kind == svn_node_dir)
+		SVN_ERR(svn_stream_printf(eb->stream, pool,
+		                          SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
+
+	/* Remove leading slashes from copyfrom paths. */
+	if (cmp_path)
+		cmp_path = ((*cmp_path == '/') ? cmp_path + 1 : cmp_path);
 
-svn_error_t *
-open_root(void *edit_baton,
-          svn_revnum_t base_revision,
-          apr_pool_t *pool,
-          void **root_baton)
+	/* Validate the comparison path/rev. */
+	if (ARE_VALID_COPY_ARGS(cmp_path, cmp_rev)) {
+		compare_path = cmp_path;
+		compare_rev = cmp_rev;
+	}
+
+	switch (action) {
+		/* Appropriately handle the four svn_node_action actions */
+
+	case svn_node_action_change:
+		SVN_ERR(svn_stream_printf(eb->stream, pool,
+		                          SVN_REPOS_DUMPFILE_NODE_ACTION
+		                          ": change\n"));
+
+		/* either the text or props changed, or possibly both. */
+		/* SVN_ERR(svn_props_changed(&must_dump_props, */
+		/*                           compare_path, path, pool)); */
+		if (kind == svn_node_file)
+			/* SVN_ERR(svn_contents_changed(&must_dump_text, */
+			/*                              compare_path, path, pool)); */
+		break;
+      
+	case svn_node_action_replace:
+		if (!is_copy) {
+			/* a simple delete+add, implied by a single 'replace' action. */
+			SVN_ERR(svn_stream_printf(eb->stream, pool,
+			                          SVN_REPOS_DUMPFILE_NODE_ACTION
+			                          ": replace\n"));
+
+			/* definitely need to dump all content for a replace. */
+			if (kind == svn_node_file)
+				must_dump_text = TRUE;
+			must_dump_props = TRUE;
+			break;
+		}
+		/* more complex:  delete original, then add-with-history.  */
+
+		/* the path & kind headers have already been printed;  just
+		   add a delete action, and end the current record.*/
+		SVN_ERR(svn_stream_printf(eb->stream, pool,
+		                          SVN_REPOS_DUMPFILE_NODE_ACTION
+		                          ": delete\n\n"));
+
+		/* recurse:  print an additional add-with-history record. */
+		SVN_ERR(dump_node(eb, path, kind, svn_node_action_add,
+		                  is_copy, compare_path, compare_rev, pool));
+
+		/* we can leave this routine quietly now, don't need to dump
+		   any content;  that was already done in the second record. */
+		must_dump_text = FALSE;
+		must_dump_props = FALSE;
+		break;
+
+	case svn_node_action_delete:
+		SVN_ERR(svn_stream_printf(eb->stream, pool,
+		                          SVN_REPOS_DUMPFILE_NODE_ACTION
+		                          ": delete\n"));
+
+		/* we can leave this routine quietly now, don't need to dump
+		   any content. */
+		must_dump_text = FALSE;
+		must_dump_props = FALSE;
+		break;
+
+	case svn_node_action_add:
+		SVN_ERR(svn_stream_printf(eb->stream, pool,
+		                          SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
+
+		if (!is_copy) {
+			/* Dump all contents for a simple 'add'. */
+			if (kind == svn_node_file)
+				must_dump_text = TRUE;
+			must_dump_props = TRUE;
+			break;
+		}
+
+		SVN_ERR(svn_stream_printf(eb->stream, pool,
+		                          SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV
+		                          ": %ld\n"
+		                          SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH
+		                          ": %s\n",
+		                          cmp_rev, cmp_path));
+
+		/* Need to decide if the copied node had any extra textual or
+		   property mods as well.  */
+		/* SVN_ERR(svn_fs_props_changed(&must_dump_props, */
+		/*                              compare_path, path, pool)); */
+		/* if (kind == svn_node_file) */
+		/* { */
+		/* 	svn_checksum_t *checksum; */
+		/* 	const char *hex_digest; */
+		/* 	SVN_ERR(svn_fs_contents_changed(&must_dump_text, */
+		/* 	                                compare_root, compare_path, */
+		/* 	                                eb->fs_root, path, pool)); */
+
+		/* 	SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, */
+		/* 	                             compare_root, compare_path, */
+		/* 	                             TRUE, pool)); */
+		/* 	hex_digest = svn_checksum_to_cstring(checksum, pool); */
+		/* 	if (hex_digest) */
+		/* 		SVN_ERR(svn_stream_printf(eb->stream, pool, */
+		/* 		                          SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_MD5 */
+		/* 		                          ": %s\n", hex_digest)); */
+
+		/* 	SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1, */
+		/* 	                             compare_root, compare_path, */
+		/* 	                             TRUE, pool)); */
+		/* 	hex_digest = svn_checksum_to_cstring(checksum, pool); */
+		/* 	if (hex_digest) */
+		/* 		SVN_ERR(svn_stream_printf(eb->stream, pool, */
+		/* 		                          SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_SHA1 */
+		/* 		                          ": %s\n", hex_digest)); */
+		/* } */
+		break;
+	}
+	if (!must_dump_text && !must_dump_props) {
+		len = 2;
+		return svn_stream_write(eb->stream, "\n\n", &len); /* ### needed? */
+	}
+	return SVN_NO_ERROR;
+}
+svn_error_t *open_root(void *edit_baton,
+                       svn_revnum_t base_revision,
+                       apr_pool_t *pool,
+                       void **root_baton)
 {
 	*root_baton = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
 	                             edit_baton, NULL, FALSE, pool);
 	return SVN_NO_ERROR;
 }
 
-
-svn_error_t *
-delete_entry(const char *path,
-             svn_revnum_t revision,
-             void *parent_baton,
-             apr_pool_t *pool)
+svn_error_t *delete_entry(const char *path,
+                          svn_revnum_t revision,
+                          void *parent_baton,
+                          apr_pool_t *pool)
 {
 	struct dir_baton *pb = parent_baton;
 	const char *mypath = apr_pstrdup(pb->pool, path);
@@ -139,14 +282,12 @@ delete_entry(const char *path,
 	return SVN_NO_ERROR;
 }
 
-
-svn_error_t *
-add_directory(const char *path,
-              void *parent_baton,
-              const char *copyfrom_path,
-              svn_revnum_t copyfrom_rev,
-              apr_pool_t *pool,
-              void **child_baton)
+svn_error_t *add_directory(const char *path,
+                           void *parent_baton,
+                           const char *copyfrom_path,
+                           svn_revnum_t copyfrom_rev,
+                           apr_pool_t *pool,
+                           void **child_baton)
 {
 	struct dir_baton *pb = parent_baton;
 	struct edit_baton *eb = pb->edit_baton;
@@ -162,13 +303,13 @@ add_directory(const char *path,
 	is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
 
 	/* Dump the node. */
-	/* SVN_ERR(dump_node(eb, path, */
-	/*                   svn_node_dir, */
-	/*                   val ? svn_node_action_replace : svn_node_action_add, */
-	/*                   is_copy, */
-	/*                   is_copy ? copyfrom_path : NULL, */
-	/*                   is_copy ? copyfrom_rev : SVN_INVALID_REVNUM, */
-	/*                   pool)); */
+	SVN_ERR(dump_node(eb, path,
+	                  svn_node_dir,
+	                  val ? svn_node_action_replace : svn_node_action_add,
+	                  is_copy,
+	                  is_copy ? copyfrom_path : NULL,
+	                  is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
+	                  pool));
 
 	if (val)
 		/* Delete the path, it's now been dumped. */
@@ -180,13 +321,11 @@ add_directory(const char *path,
 	return SVN_NO_ERROR;
 }
 
-
-svn_error_t *
-open_directory(const char *path,
-               void *parent_baton,
-               svn_revnum_t base_revision,
-               apr_pool_t *pool,
-               void **child_baton)
+svn_error_t *open_directory(const char *path,
+                            void *parent_baton,
+                            svn_revnum_t base_revision,
+                            apr_pool_t *pool,
+                            void **child_baton)
 {
 	struct dir_baton *pb = parent_baton;
 	struct edit_baton *eb = pb->edit_baton;
@@ -196,8 +335,7 @@ open_directory(const char *path,
 
 	/* If the parent directory has explicit comparison path and rev,
 	   record the same for this one. */
-	if (pb && ARE_VALID_COPY_ARGS(pb->cmp_path, pb->cmp_rev))
-	{
+	if (pb && ARE_VALID_COPY_ARGS(pb->cmp_path, pb->cmp_rev)) {
 		cmp_path = svn_path_join(pb->cmp_path,
 		                         svn_dirent_basename(path, pool), pool);
 		cmp_rev = pb->cmp_rev;
@@ -208,10 +346,8 @@ open_directory(const char *path,
 	return SVN_NO_ERROR;
 }
 
-
-svn_error_t *
-close_directory(void *dir_baton,
-                apr_pool_t *pool)
+svn_error_t *close_directory(void *dir_baton,
+                             apr_pool_t *pool)
 {
 	struct dir_baton *db = dir_baton;
 	struct edit_baton *eb = db->edit_baton;
@@ -220,8 +356,7 @@ close_directory(void *dir_baton,
 
 	for (hi = apr_hash_first(pool, db->deleted_entries);
 	     hi;
-	     hi = apr_hash_next(hi))
-	{
+	     hi = apr_hash_next(hi)) {
 		const void *key;
 		const char *path;
 		apr_hash_this(hi, &key, NULL, NULL);
@@ -232,23 +367,21 @@ close_directory(void *dir_baton,
 		/* By sending 'svn_node_unknown', the Node-kind: header simply won't
 		   be written out.  No big deal at all, really.  The loader
 		   shouldn't care.  */
-		/* SVN_ERR(dump_node(eb, path, */
-		/*                   svn_node_unknown, svn_node_action_delete, */
-		/*                   FALSE, NULL, SVN_INVALID_REVNUM, subpool)); */
+		SVN_ERR(dump_node(eb, path,
+		                  svn_node_unknown, svn_node_action_delete,
+		                  FALSE, NULL, SVN_INVALID_REVNUM, subpool));
 	}
 
 	svn_pool_destroy(subpool);
 	return SVN_NO_ERROR;
 }
 
-
-svn_error_t *
-add_file(const char *path,
-         void *parent_baton,
-         const char *copyfrom_path,
-         svn_revnum_t copyfrom_rev,
-         apr_pool_t *pool,
-         void **file_baton)
+svn_error_t *add_file(const char *path,
+                      void *parent_baton,
+                      const char *copyfrom_path,
+                      svn_revnum_t copyfrom_rev,
+                      apr_pool_t *pool,
+                      void **file_baton)
 {
 	struct dir_baton *pb = parent_baton;
 	struct edit_baton *eb = pb->edit_baton;
@@ -262,29 +395,29 @@ add_file(const char *path,
 	is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
 
 	/* Dump the node. */
-	/* SVN_ERR(dump_node(eb, path, */
-	/*                   svn_node_file, */
-	/*                   val ? svn_node_action_replace : svn_node_action_add, */
-	/*                   is_copy, */
-	/*                   is_copy ? copyfrom_path : NULL, */
-	/*                   is_copy ? copyfrom_rev : SVN_INVALID_REVNUM, */
-	/*                   pool)); */
+	SVN_ERR(dump_node(eb, path,
+	                  svn_node_file,
+	                  val ? svn_node_action_replace : svn_node_action_add,
+	                  is_copy,
+	                  is_copy ? copyfrom_path : NULL,
+	                  is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
+	                  pool));
 
 	if (val)
 		/* delete the path, it's now been dumped. */
 		apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
 
-	*file_baton = NULL;  /* muhahahaha */
+	/* TODO: Store the delta in file_baton */
+	*file_baton = NULL;
 	return SVN_NO_ERROR;
 }
 
 
-svn_error_t *
-open_file(const char *path,
-          void *parent_baton,
-          svn_revnum_t ancestor_revision,
-          apr_pool_t *pool,
-          void **file_baton)
+svn_error_t *open_file(const char *path,
+                       void *parent_baton,
+                       svn_revnum_t ancestor_revision,
+                       apr_pool_t *pool,
+                       void **file_baton)
 {
 	struct dir_baton *pb = parent_baton;
 	struct edit_baton *eb = pb->edit_baton;
@@ -293,26 +426,25 @@ open_file(const char *path,
 
 	/* If the parent directory has explicit comparison path and rev,
 	   record the same for this one. */
-	if (pb && ARE_VALID_COPY_ARGS(pb->cmp_path, pb->cmp_rev))
-	{
+	if (pb && ARE_VALID_COPY_ARGS(pb->cmp_path, pb->cmp_rev)) {
 		cmp_path = svn_path_join(pb->cmp_path,
 		                         svn_dirent_basename(path, pool), pool);
 		cmp_rev = pb->cmp_rev;
 	}
 
-	/* SVN_ERR(dump_node(eb, path, */
-	/*                   svn_node_file, svn_node_action_change, */
-	/*                   FALSE, cmp_path, cmp_rev, pool)); */
+	SVN_ERR(dump_node(eb, path,
+	                  svn_node_file, svn_node_action_change,
+	                  FALSE, cmp_path, cmp_rev, pool));
 
-	*file_baton = NULL;  /* muhahahaha again */
+	/* TODO: Store the delta in file_baton */
+	*file_baton = NULL;
 	return SVN_NO_ERROR;
 }
 
-svn_error_t *
-change_dir_prop(void *parent_baton,
-                const char *name,
-                const svn_string_t *value,
-                apr_pool_t *pool)
+svn_error_t *change_dir_prop(void *parent_baton,
+                             const char *name,
+                             const svn_string_t *value,
+                             apr_pool_t *pool)
 {
 	struct dir_baton *db = parent_baton;
 	struct edit_baton *eb = db->edit_baton;
@@ -320,35 +452,27 @@ change_dir_prop(void *parent_baton,
 	/* This function is what distinguishes between a directory that is
 	   opened to merely get somewhere, vs. one that is opened because it
 	   *actually* changed by itself.  */
-	if (! db->written_out)
-	{
-		/* SVN_ERR(dump_node(eb, db->path, */
-		/*                   svn_node_dir, svn_node_action_change, */
-		/*                   FALSE, db->cmp_path, db->cmp_rev, pool)); */
+	if (! db->written_out) {
+		SVN_ERR(dump_node(eb, db->path,
+		                  svn_node_dir, svn_node_action_change,
+		                  FALSE, db->cmp_path, db->cmp_rev, pool));
 		db->written_out = TRUE;
 	}
 	return SVN_NO_ERROR;
 }
 
-svn_error_t *
-get_dump_editor(const svn_delta_editor_t **editor,
-                void **edit_baton,
-                svn_revnum_t to_rev,
-                apr_pool_t *pool)
+svn_error_t *get_dump_editor(const svn_delta_editor_t **editor,
+                             void **edit_baton,
+                             svn_revnum_t to_rev,
+                             apr_pool_t *pool)
 {
-	/* Allocate an edit baton to be stored in every directory baton.
-	   Set it up for the directory baton we create here, which is the
-	   root baton. */
 	struct edit_baton *eb = apr_pcalloc(pool, sizeof(*eb));
 	svn_delta_editor_t *dump_editor = svn_delta_default_editor(pool);
 
-	/* Set up the edit baton. */
 	svn_stream_for_stdout(&(eb->stream), pool);
 	eb->bufsize = sizeof(eb->buffer);
 	eb->current_rev = to_rev;
 
-
-	/* Set up the editor. */
 	dump_editor->open_root = open_root;
 	dump_editor->delete_entry = delete_entry;
 	dump_editor->add_directory = add_directory;
diff --git a/svnclient_ra.c b/svnclient_ra.c
index 24d99cb..0b9e002 100644
--- a/svnclient_ra.c
+++ b/svnclient_ra.c
@@ -109,8 +109,8 @@ svn_error_t *replay_range(svn_revnum_t start_revision, svn_revnum_t end_revision
 	                                    dump_baton, pool));
 	
 	replay_baton_t *replay_baton = apr_palloc(pool, sizeof(replay_baton_t));
-	replay_baton->editor = debug_editor;
-	replay_baton->baton = debug_baton;
+	replay_baton->editor = dump_editor;
+	replay_baton->baton = dump_baton;
 	SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision,
 	                            0, TRUE, replay_revstart, replay_revend,
 	                            replay_baton, pool));
@@ -125,7 +125,7 @@ void close_connection()
 int main()
 {
 	const char url[] = "http://svn.apache.org/repos/asf";
-	svn_revnum_t start_revision = 1, end_revision = 5;
+	svn_revnum_t start_revision = 1, end_revision = 50;
 	if (svn_cmdline_init ("svnclient_ra", stderr) != EXIT_SUCCESS)
 		return 1;
 	pool = svn_pool_create(NULL);
-- 
1.7.1
Ramkumar Ramachandra· Jun 23, 2010, 17:05 UTC · re: Ramkumar Ramachandra · lore

Re: [WIP PATCH 3/7] Import dump_node to dump what changed and cleanup whitespace

Hi,
Ramkumar Ramachandra wrote:
Show 6 quoted lines
> +Licensed to the Apache Software Foundation (ASF) under one or more
> +contributor license agreements.  See the NOTICE file distributed with
> +this work for additional information regarding copyright ownership.
> +The ASF licenses this file to you under the Apache License, Version
> +2.0 (the "License"); you may not use this file except in compliance
> +with the License.  You may obtain a copy of the License at

Daniel pointed out that my code isn't licensed to the ASF before it's merged back into Subversion; the dump.c I've taken directly from the Subversion trunk is though. I'll find out the exact semantics and correct this.

-- Ram
Ramkumar Ramachandra· Jun 23, 2010, 16:22 UTC · re: Ramkumar Ramachandra · lore

[WIP PATCH 4/7] Replace deprecated svn_path_join

Strip the deprecated svn_path_join API, using svn_path_compose instead. Re-add -Werror to the Makefile.

Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 Makefile      |    2 +-
 dump_editor.c |   21 ++++++++++++++-------
 2 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/Makefile b/Makefile
index 269c406..cf7fef7 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
 svnclient_ra: *.c *.h
-	$(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
+	$(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
 clean:
 	$(RM) svnclient_ra
diff --git a/dump_editor.c b/dump_editor.c
index ba0630f..00c838a 100644
--- a/dump_editor.c
+++ b/dump_editor.c
@@ -80,14 +80,17 @@ struct dir_baton *make_dir_baton(const char *path,
 	struct dir_baton *pb = parent_dir_baton;
 	struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db));
 	const char *full_path;
-
+	apr_array_header_t *compose_path = apr_array_make(pool, 2, sizeof(const char *));
 
 	/* A path relative to nothing?  I don't think so. */
 	SVN_ERR_ASSERT_NO_RETURN(!path || pb);
 
 	/* Construct the full path of this node. */
-	if (pb)
-		full_path = svn_path_join("/", path, pool);
+	if (pb) {
+		APR_ARRAY_PUSH(compose_path, const char *) = "/";
+		APR_ARRAY_PUSH(compose_path, const char *) = path;
+		full_path = svn_path_compose(compose_path, pool);
+	}
 	else
 		full_path = apr_pstrdup(pool, "/");
 
@@ -332,12 +335,14 @@ svn_error_t *open_directory(const char *path,
 	struct dir_baton *new_db;
 	const char *cmp_path = NULL;
 	svn_revnum_t cmp_rev = SVN_INVALID_REVNUM;
+	apr_array_header_t *compose_path = apr_array_make(pool, 2, sizeof(const char *));
 
 	/* If the parent directory has explicit comparison path and rev,
 	   record the same for this one. */
 	if (pb && ARE_VALID_COPY_ARGS(pb->cmp_path, pb->cmp_rev)) {
-		cmp_path = svn_path_join(pb->cmp_path,
-		                         svn_dirent_basename(path, pool), pool);
+		APR_ARRAY_PUSH(compose_path, const char *) = pb->cmp_path;
+		APR_ARRAY_PUSH(compose_path, const char *) = svn_dirent_basename(path, pool);
+		cmp_path = svn_path_compose(compose_path, pool);
 		cmp_rev = pb->cmp_rev;
 	}
 
@@ -424,11 +429,13 @@ svn_error_t *open_file(const char *path,
 	const char *cmp_path = NULL;
 	svn_revnum_t cmp_rev = SVN_INVALID_REVNUM;
 
+	apr_array_header_t *compose_path = apr_array_make(pool, 2, sizeof(const char *));
 	/* If the parent directory has explicit comparison path and rev,
 	   record the same for this one. */
 	if (pb && ARE_VALID_COPY_ARGS(pb->cmp_path, pb->cmp_rev)) {
-		cmp_path = svn_path_join(pb->cmp_path,
-		                         svn_dirent_basename(path, pool), pool);
+		APR_ARRAY_PUSH(compose_path, const char *) = pb->cmp_path;
+		APR_ARRAY_PUSH(compose_path, const char *) = svn_dirent_basename(path, pool);
+		cmp_path = svn_path_compose(compose_path, pool);
 		cmp_rev = pb->cmp_rev;
 	}
 
-- 
1.7.1
Ramkumar Ramachandra· Jun 23, 2010, 16:22 UTC · re: Ramkumar Ramachandra · lore

[WIP PATCH 5/7] Trigger dump_node in change_dir_prop

Trigger dump_node to dump property length in change_dir_prop. Use the edit_baton to store the changed property before dumping it.

Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 dump_editor.c |  111 +++++++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 89 insertions(+), 22 deletions(-)
diff --git a/dump_editor.c b/dump_editor.c
index 00c838a..a80d711 100644
--- a/dump_editor.c
+++ b/dump_editor.c
@@ -15,12 +15,22 @@
 
 #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
 
+static svn_boolean_t must_dump_text = FALSE, must_dump_props = FALSE;
+static svn_boolean_t text_changed = FALSE, props_changed = FALSE;
+
 struct edit_baton {
 	/* The stream to dump to: stdout */
 	svn_stream_t *stream;
 	
 	svn_revnum_t current_rev;
 	
+	/* Store the properties that changed */
+	apr_hash_t *properties;
+	apr_hash_t *del_properties; /* Value is always 0x1 */
+
+	/* Store the text delta */
+	const char *delta_text;
+
 	/* reusable buffer for writing file contents */
 	char buffer[SVN__STREAM_CHUNK_SIZE];
 	apr_size_t bufsize;
@@ -56,6 +66,41 @@ struct dir_baton {
 	apr_pool_t *pool;
 };
 
+static void write_hash_to_stringbuf(apr_hash_t *hash,
+                                    svn_stringbuf_t **strbuf,
+                                    apr_pool_t *pool) {
+	apr_hash_index_t *this;
+	*strbuf = svn_stringbuf_create("", pool);
+
+	for (this = apr_hash_first(pool, hash); this; this = apr_hash_next(this))
+	{
+		const void *key;
+		void *val;
+		apr_ssize_t keylen;
+		svn_string_t *value;
+
+		/* Get this key and val. */
+		apr_hash_this(this, &key, &keylen, &val);
+		value = val;
+
+		/* Output name length, then name. */
+		svn_stringbuf_appendcstr(*strbuf,
+		                         apr_psprintf(pool, "K %" APR_SSIZE_T_FMT "\n",
+		                                      keylen));
+
+		svn_stringbuf_appendbytes(*strbuf, (const char *) key, keylen);
+		svn_stringbuf_appendbytes(*strbuf, "\n", 1);
+
+		/* Output value length, then value. */
+		svn_stringbuf_appendcstr(*strbuf,
+		                         apr_psprintf(pool, "V %" APR_SIZE_T_FMT "\n",
+		                                      value->len));
+
+		svn_stringbuf_appendbytes(*strbuf, value->data, value->len);
+		svn_stringbuf_appendbytes(*strbuf, "\n", 1);
+	}
+	svn_stringbuf_appendbytes(*strbuf, "PROPS-END\n", 10);
+}
 
 /* Make a directory baton to represent the directory was path
    (relative to EDIT_BATON's path) is PATH.
@@ -120,10 +165,11 @@ static svn_error_t *dump_node(struct edit_baton *eb,
                               svn_revnum_t cmp_rev,
                               apr_pool_t *pool)
 {
-	apr_size_t len;
-	svn_boolean_t must_dump_text = TRUE, must_dump_props = TRUE;
+	apr_size_t len, proplen;
 	const char *compare_path = path;
 	svn_revnum_t compare_rev = eb->current_rev - 1;
+	svn_stringbuf_t *propstring;
+	svn_filesize_t content_length = 0;
 
 	/* Write out metadata headers for this file node. */
 	SVN_ERR(svn_stream_printf(eb->stream, pool,
@@ -153,13 +199,10 @@ static svn_error_t *dump_node(struct edit_baton *eb,
 		SVN_ERR(svn_stream_printf(eb->stream, pool,
 		                          SVN_REPOS_DUMPFILE_NODE_ACTION
 		                          ": change\n"));
-
-		/* either the text or props changed, or possibly both. */
-		/* SVN_ERR(svn_props_changed(&must_dump_props, */
-		/*                           compare_path, path, pool)); */
+		
+		must_dump_props = props_changed;
 		if (kind == svn_node_file)
-			/* SVN_ERR(svn_contents_changed(&must_dump_text, */
-			/*                              compare_path, path, pool)); */
+			must_dump_text = text_changed;
 		break;
       
 	case svn_node_action_replace:
@@ -223,28 +266,24 @@ static svn_error_t *dump_node(struct edit_baton *eb,
 		                          ": %s\n",
 		                          cmp_rev, cmp_path));
 
-		/* Need to decide if the copied node had any extra textual or
-		   property mods as well.  */
-		/* SVN_ERR(svn_fs_props_changed(&must_dump_props, */
-		/*                              compare_path, path, pool)); */
-		/* if (kind == svn_node_file) */
-		/* { */
+		must_dump_props = props_changed;
+		/* if (kind == svn_node_file) { */
 		/* 	svn_checksum_t *checksum; */
 		/* 	const char *hex_digest; */
-		/* 	SVN_ERR(svn_fs_contents_changed(&must_dump_text, */
-		/* 	                                compare_root, compare_path, */
-		/* 	                                eb->fs_root, path, pool)); */
+		/* 	must_dump_text = text_changed; */
 
-		/* 	SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, */
-		/* 	                             compare_root, compare_path, */
-		/* 	                             TRUE, pool)); */
+		/* 	/\* MD5 checksum *\/ */
+		/* 	SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, */
+		/* 	                     compare_root, compare_path, */
+		/* 	                     TRUE, pool)); */
 		/* 	hex_digest = svn_checksum_to_cstring(checksum, pool); */
 		/* 	if (hex_digest) */
 		/* 		SVN_ERR(svn_stream_printf(eb->stream, pool, */
 		/* 		                          SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_MD5 */
 		/* 		                          ": %s\n", hex_digest)); */
 
-		/* 	SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1, */
+		/* 	/\* SHA1 checksum *\/ */
+		/* 	SVN_ERR(svn_checksum(&checksum, svn_checksum_sha1, */
 		/* 	                             compare_root, compare_path, */
 		/* 	                             TRUE, pool)); */
 		/* 	hex_digest = svn_checksum_to_cstring(checksum, pool); */
@@ -257,7 +296,23 @@ static svn_error_t *dump_node(struct edit_baton *eb,
 	}
 	if (!must_dump_text && !must_dump_props) {
 		len = 2;
-		return svn_stream_write(eb->stream, "\n\n", &len); /* ### needed? */
+		return svn_stream_write(eb->stream, "\n\n", &len);
+	}
+
+	/* If we are supposed to dump properties, write out a property
+	   length header and generate a stringbuf that contains those
+	   property values here. */
+	if (must_dump_props) {
+		write_hash_to_stringbuf(eb->properties, &propstring, pool);
+		proplen = propstring->len;
+		content_length += proplen;
+		SVN_ERR(svn_stream_printf(eb->stream, pool,
+		                          SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
+		                          ": %" APR_SIZE_T_FMT "\n", proplen));
+	}
+	if (must_dump_text && (kind == svn_node_file)) {
+		/* svn_stream_t *contents; */
+		;
 	}
 	return SVN_NO_ERROR;
 }
@@ -456,6 +511,15 @@ svn_error_t *change_dir_prop(void *parent_baton,
 	struct dir_baton *db = parent_baton;
 	struct edit_baton *eb = db->edit_baton;
 
+	if (svn_property_kind(NULL, name) != svn_prop_regular_kind)
+		return SVN_NO_ERROR;
+
+	value ? apr_hash_set(eb->properties, apr_pstrdup(pool, name),
+	                     APR_HASH_KEY_STRING, svn_string_dup(value, pool)) :
+		apr_hash_set(eb->del_properties, apr_pstrdup(pool, name),
+		             APR_HASH_KEY_STRING, (void *)0x1);
+	props_changed = TRUE;
+	
 	/* This function is what distinguishes between a directory that is
 	   opened to merely get somewhere, vs. one that is opened because it
 	   *actually* changed by itself.  */
@@ -479,6 +543,9 @@ svn_error_t *get_dump_editor(const svn_delta_editor_t **editor,
 	svn_stream_for_stdout(&(eb->stream), pool);
 	eb->bufsize = sizeof(eb->buffer);
 	eb->current_rev = to_rev;
+	eb->properties = apr_hash_make(pool);
+	eb->del_properties = apr_hash_make(pool);
+	eb->delta_text = apr_pcalloc(pool, sizeof(const char *));
 
 	dump_editor->open_root = open_root;
 	dump_editor->delete_entry = delete_entry;
-- 
1.7.1
Ramkumar Ramachandra· Jun 23, 2010, 16:22 UTC · re: Ramkumar Ramachandra · lore

[WIP PATCH 6/7] Add file_baton and trigger dump_node in change_file_prop

Add a file_baton structure, and fill it in in open_file and add_file functions. It is to be used by apply_textdelta and change_file_prop functions. Trigger dump_node in change_file_prop. Dump the actual properties along with the property lengths.

Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 dump_editor.c |  100 ++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 89 insertions(+), 11 deletions(-)
diff --git a/dump_editor.c b/dump_editor.c
index a80d711..7bd00a4 100644
--- a/dump_editor.c
+++ b/dump_editor.c
@@ -15,7 +15,6 @@
 
 #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
 
-static svn_boolean_t must_dump_text = FALSE, must_dump_props = FALSE;
 static svn_boolean_t text_changed = FALSE, props_changed = FALSE;
 
 struct edit_baton {
@@ -66,9 +65,16 @@ struct dir_baton {
 	apr_pool_t *pool;
 };
 
+struct file_baton {
+	/* Store the path of the file */
+	const char *path;
+	struct edit_baton *eb;
+};
+
 static void write_hash_to_stringbuf(apr_hash_t *hash,
                                     svn_stringbuf_t **strbuf,
-                                    apr_pool_t *pool) {
+                                    apr_pool_t *pool)
+{
 	apr_hash_index_t *this;
 	*strbuf = svn_stringbuf_create("", pool);
 
@@ -120,7 +126,8 @@ struct dir_baton *make_dir_baton(const char *path,
                                  void *edit_baton,
                                  void *parent_dir_baton,
                                  svn_boolean_t added,
-                                 apr_pool_t *pool) {
+                                 apr_pool_t *pool)
+{
 	struct edit_baton *eb = edit_baton;
 	struct dir_baton *pb = parent_dir_baton;
 	struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db));
@@ -156,6 +163,18 @@ struct dir_baton *make_dir_baton(const char *path,
 	return new_db;
 }
 
+struct file_baton *make_file_baton(struct edit_baton *eb,
+                                   const char *path,
+                                   apr_pool_t *pool)
+{
+	struct file_baton *new_fb = apr_pcalloc(pool, sizeof(struct file_baton));
+	new_fb->path = apr_pcalloc(pool, sizeof(const char *));
+	new_fb->path = path;
+	new_fb->eb = eb;
+
+	return new_fb;
+}
+
 static svn_error_t *dump_node(struct edit_baton *eb,
                               const char *path,    /* an absolute path. */
                               svn_node_kind_t kind,
@@ -170,6 +189,7 @@ static svn_error_t *dump_node(struct edit_baton *eb,
 	svn_revnum_t compare_rev = eb->current_rev - 1;
 	svn_stringbuf_t *propstring;
 	svn_filesize_t content_length = 0;
+	svn_boolean_t must_dump_text = FALSE, must_dump_props = FALSE;
 
 	/* Write out metadata headers for this file node. */
 	SVN_ERR(svn_stream_printf(eb->stream, pool,
@@ -310,10 +330,44 @@ static svn_error_t *dump_node(struct edit_baton *eb,
 		                          SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
 		                          ": %" APR_SIZE_T_FMT "\n", proplen));
 	}
+
+	/* If we are supposed to dump text, write out a text length header
+	   here, and an MD5 checksum (if available). */
 	if (must_dump_text && (kind == svn_node_file)) {
-		/* svn_stream_t *contents; */
 		;
 	}
+
+	/* 'Content-length:' is the last header before we dump the content,
+	   and is the sum of the text and prop contents lengths.  We write
+	   this only for the benefit of non-Subversion RFC-822 parsers. */
+	SVN_ERR(svn_stream_printf(eb->stream, pool,
+	                          SVN_REPOS_DUMPFILE_CONTENT_LENGTH
+	                          ": %" SVN_FILESIZE_T_FMT "\n\n",
+	                          content_length));
+
+	/* Now dump the text and properties */
+	if (must_dump_props)
+	{
+		len = propstring->len;
+		SVN_ERR(svn_stream_write(eb->stream, propstring->data, &len));
+	}
+	/* if (must_dump_text && (kind == svn_node_file)) */
+	/* { */
+	/* 	svn_stream_t *contents; */
+
+	/* 	if (delta_file) */
+	/* 	{ */
+	/* 		/\* Make sure to close the underlying file when the stream is */
+	/* 		   closed. *\/ */
+	/* 		contents = svn_stream_from_aprfile2(delta_file, FALSE, pool); */
+	/* 	} */
+	/* 	else */
+	/* 		SVN_ERR(svn_fs_file_contents(&contents, eb->fs_root, path, pool)); */
+
+	/* 	SVN_ERR(svn_stream_copy3(contents, svn_stream_disown(eb->stream, pool), */
+	/* 	                         NULL, NULL, pool)); */
+	/* } */
+
 	return SVN_NO_ERROR;
 }
 svn_error_t *open_root(void *edit_baton,
@@ -467,12 +521,12 @@ svn_error_t *add_file(const char *path,
 		/* delete the path, it's now been dumped. */
 		apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
 
-	/* TODO: Store the delta in file_baton */
-	*file_baton = NULL;
+	/* Build a nice file baton to pass to change_file_prop and apply_textdelta */
+	*file_baton = make_file_baton(eb, path, pool);
+
 	return SVN_NO_ERROR;
 }
 
-
 svn_error_t *open_file(const char *path,
                        void *parent_baton,
                        svn_revnum_t ancestor_revision,
@@ -497,9 +551,10 @@ svn_error_t *open_file(const char *path,
 	SVN_ERR(dump_node(eb, path,
 	                  svn_node_file, svn_node_action_change,
 	                  FALSE, cmp_path, cmp_rev, pool));
-
-	/* TODO: Store the delta in file_baton */
-	*file_baton = NULL;
+	
+	/* Build a nice file baton to pass to change_file_prop and apply_textdelta */
+	*file_baton = make_file_baton(eb, path, pool);
+	
 	return SVN_NO_ERROR;
 }
 
@@ -532,6 +587,29 @@ svn_error_t *change_dir_prop(void *parent_baton,
 	return SVN_NO_ERROR;
 }
 
+svn_error_t *change_file_prop(void *file_baton,
+                              const char *name,
+                              const svn_string_t *value,
+                              apr_pool_t *pool)
+{
+	struct file_baton *fb = file_baton;
+	
+	if (svn_property_kind(NULL, name) != svn_prop_regular_kind)
+		return SVN_NO_ERROR;
+
+	value ? apr_hash_set(fb->eb->properties, apr_pstrdup(pool, name),
+	                     APR_HASH_KEY_STRING, svn_string_dup(value, pool)) :
+		apr_hash_set(fb->eb->del_properties, apr_pstrdup(pool, name),
+		             APR_HASH_KEY_STRING, (void *)0x1);
+	props_changed = TRUE;
+	
+	SVN_ERR(dump_node(fb->eb, fb->path,
+	                  svn_node_dir, svn_node_action_change,
+	                  FALSE, NULL, 0, pool));
+
+	return SVN_NO_ERROR;
+}
+
 svn_error_t *get_dump_editor(const svn_delta_editor_t **editor,
                              void **edit_baton,
                              svn_revnum_t to_rev,
@@ -545,7 +623,6 @@ svn_error_t *get_dump_editor(const svn_delta_editor_t **editor,
 	eb->current_rev = to_rev;
 	eb->properties = apr_hash_make(pool);
 	eb->del_properties = apr_hash_make(pool);
-	eb->delta_text = apr_pcalloc(pool, sizeof(const char *));
 
 	dump_editor->open_root = open_root;
 	dump_editor->delete_entry = delete_entry;
@@ -553,6 +630,7 @@ svn_error_t *get_dump_editor(const svn_delta_editor_t **editor,
 	dump_editor->open_directory = open_directory;
 	dump_editor->close_directory = close_directory;
 	dump_editor->change_dir_prop = change_dir_prop;
+	dump_editor->change_file_prop = change_file_prop;	
 	dump_editor->add_file = add_file;
 	dump_editor->open_file = open_file;
 
-- 
1.7.1
Ramkumar Ramachandra· Jun 23, 2010, 16:22 UTC · re: Ramkumar Ramachandra · lore

[WIP PATCH 7/7] Dump the text delta

Add a handler and handler_baton to handle text deltas in the editor baton. Currently, program exits with code 01 because delta source ends unexpectedly.

Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 dump_editor.c |   51 +++++++++++++++++++++++++++++++--------------------
 1 files changed, 31 insertions(+), 20 deletions(-)
diff --git a/dump_editor.c b/dump_editor.c
index 7bd00a4..2192177 100644
--- a/dump_editor.c
+++ b/dump_editor.c
@@ -27,12 +27,13 @@ struct edit_baton {
 	apr_hash_t *properties;
 	apr_hash_t *del_properties; /* Value is always 0x1 */
 
-	/* Store the text delta */
-	const char *delta_text;
-
 	/* reusable buffer for writing file contents */
 	char buffer[SVN__STREAM_CHUNK_SIZE];
 	apr_size_t bufsize;
+
+	/* The txdelta handler and handler baton */
+	svn_txdelta_window_handler_t *handler;
+	void **handler_baton;
 };
 
 struct dir_baton {
@@ -351,22 +352,9 @@ static svn_error_t *dump_node(struct edit_baton *eb,
 		len = propstring->len;
 		SVN_ERR(svn_stream_write(eb->stream, propstring->data, &len));
 	}
-	/* if (must_dump_text && (kind == svn_node_file)) */
-	/* { */
-	/* 	svn_stream_t *contents; */
-
-	/* 	if (delta_file) */
-	/* 	{ */
-	/* 		/\* Make sure to close the underlying file when the stream is */
-	/* 		   closed. *\/ */
-	/* 		contents = svn_stream_from_aprfile2(delta_file, FALSE, pool); */
-	/* 	} */
-	/* 	else */
-	/* 		SVN_ERR(svn_fs_file_contents(&contents, eb->fs_root, path, pool)); */
-
-	/* 	SVN_ERR(svn_stream_copy3(contents, svn_stream_disown(eb->stream, pool), */
-	/* 	                         NULL, NULL, pool)); */
-	/* } */
+	if (must_dump_text && (kind == svn_node_file) && eb->handler) {
+		svn_txdelta_to_svndiff2(eb->handler, eb->handler_baton, eb->stream, 0, pool);
+	}
 
 	return SVN_NO_ERROR;
 }
@@ -610,6 +598,28 @@ svn_error_t *change_file_prop(void *file_baton,
 	return SVN_NO_ERROR;
 }
 
+svn_error_t *apply_textdelta(void *file_baton, const char *base_checksum,
+                             apr_pool_t *pool,
+                             svn_txdelta_window_handler_t *handler,
+                             void **handler_baton)
+{
+	struct file_baton *fb = file_baton;
+	svn_stream_t *source_stream = svn_stream_empty(pool);
+	svn_txdelta_apply(source_stream, fb->eb->stream,
+	                  NULL, fb->path, pool, handler,
+	                  handler_baton);
+	fb->eb->handler = handler;
+	fb->eb->handler_baton = handler_baton;
+
+	text_changed = TRUE;
+
+	SVN_ERR(dump_node(fb->eb, fb->path,
+	                  svn_node_dir, svn_node_action_change,
+	                  FALSE, NULL, 0, pool));
+
+	return SVN_NO_ERROR;
+}
+
 svn_error_t *get_dump_editor(const svn_delta_editor_t **editor,
                              void **edit_baton,
                              svn_revnum_t to_rev,
@@ -630,7 +640,8 @@ svn_error_t *get_dump_editor(const svn_delta_editor_t **editor,
 	dump_editor->open_directory = open_directory;
 	dump_editor->close_directory = close_directory;
 	dump_editor->change_dir_prop = change_dir_prop;
-	dump_editor->change_file_prop = change_file_prop;	
+	dump_editor->change_file_prop = change_file_prop;
+	dump_editor->apply_textdelta = apply_textdelta;
 	dump_editor->add_file = add_file;
 	dump_editor->open_file = open_file;
 
-- 
1.7.1
Ramkumar Ramachandra· Jun 23, 2010, 17:18 UTC · re: Ramkumar Ramachandra · lore

Re: [GSoC update] git-remote-svn: Week 8

Hi,

I have another doubt. If the 6~8 part series is preferred, shall I also include one patch that imports dump.c from the Subversion trunk directly and another to strip off the unnecessary parts, clean up whitespace and style? In other words, would you prefer to review the dump.c taken from the Subversion trunk (and some style patches from me) or my cleaned up version?

-- Ram
Jonathan Nieder· Jun 25, 2010, 00:42 UTC · re: Ramkumar Ramachandra · lore

Re: [GSoC update] git-remote-svn: Week 8

Ramkumar Ramachandra wrote:
> shall I
> also include one patch that imports dump.c from the Subversion trunk
> directly and another to strip off the unnecessary parts, clean up
> whitespace and style?

This is just my preference, but I would prefer your cleaned up version (with a link to the pristine version in trunk I can diff against, of course).

Jonathan

← back to recent threads