You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2012/05/16 22:32:54 UTC

svn commit: r1339349 [35/37] - in /subversion/branches/fix-rdump-editor: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ contrib/client-side/vim/ contrib/server-side/ notes/ notes/api-errat...

Modified: subversion/branches/fix-rdump-editor/subversion/tests/libsvn_subr/spillbuf-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/subversion/tests/libsvn_subr/spillbuf-test.c?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/subversion/tests/libsvn_subr/spillbuf-test.c (original)
+++ subversion/branches/fix-rdump-editor/subversion/tests/libsvn_subr/spillbuf-test.c Wed May 16 20:32:43 2012
@@ -33,42 +33,55 @@ static const char basic_data[] = ("abcde
                                   "0123456789");
 
 
+/* Validate that BUF is STARTING_SIZE in length. Then read some data from
+   the buffer, which should match EXPECTED. The EXPECTED value must be
+   NUL-terminated, but the NUL is not part of the expected/verified value.  */
+#define CHECK_READ(b, s, e, p) SVN_ERR(check_read(b, s, e, p))
+static svn_error_t *
+check_read(svn_spillbuf_t *buf,
+           svn_filesize_t starting_size,
+           const char *expected,
+           apr_pool_t *scratch_pool)
+{
+  apr_size_t expected_len = strlen(expected);
+  const char *readptr;
+  apr_size_t readlen;
+
+  SVN_TEST_ASSERT(svn_spillbuf__get_size(buf) == starting_size);
+  SVN_ERR(svn_spillbuf__read(&readptr, &readlen, buf, scratch_pool));
+  SVN_TEST_ASSERT(readptr != NULL
+                  && readlen == expected_len
+                  && memcmp(readptr, expected, expected_len) == 0);
+  return SVN_NO_ERROR;
+}
+
+
 static svn_error_t *
 test_spillbuf_basic(apr_pool_t *pool)
 {
-  svn_spillbuf_t *buf = svn_spillbuf__create(
-                          sizeof(basic_data) /* blocksize */,
-                          10 * sizeof(basic_data) /* maxsize */,
-                          pool);
+  apr_size_t len = strlen(basic_data);  /* Don't include basic_data's NUL  */
+  svn_spillbuf_t *buf = svn_spillbuf__create(len, 10 * len, pool);
   int i;
+  const char *readptr;
+  apr_size_t readlen;
 
   /* It starts empty.  */
   SVN_TEST_ASSERT(svn_spillbuf__get_size(buf) == 0);
 
   /* Place enough data into the buffer to cause a spill to disk.  */
   for (i = 20; i--; )
-    SVN_ERR(svn_spillbuf__write(buf, basic_data, sizeof(basic_data), pool));
+    SVN_ERR(svn_spillbuf__write(buf, basic_data, len, pool));
 
   /* And now has content.  */
   SVN_TEST_ASSERT(svn_spillbuf__get_size(buf) > 0);
 
-  while (TRUE)
-    {
-      const char *readptr;
-      apr_size_t readlen;
-
-      SVN_ERR(svn_spillbuf__read(&readptr, &readlen, buf, pool));
-      if (readptr == NULL)
-        break;
-
-      /* We happen to know that the spill buffer reads data in lengths
-         of BLOCKSIZE.  */
-      SVN_TEST_ASSERT(readlen == sizeof(basic_data));
-
-      /* And it should match each block of data we put in.  */
-      SVN_TEST_ASSERT(memcmp(readptr, basic_data, readlen) == 0);
-    }
+  /* Verify that we can read 20 copies of basic_data from the buffer.  */
+  for (i = 20; i--; )
+    CHECK_READ(buf, (i + 1) * len, basic_data, pool);
 
+  /* And after precisely 20 reads, it should be empty.  */
+  SVN_ERR(svn_spillbuf__read(&readptr, &readlen, buf, pool));
+  SVN_TEST_ASSERT(readptr == NULL);
   SVN_TEST_ASSERT(svn_spillbuf__get_size(buf) == 0);
 
   return SVN_NO_ERROR;
@@ -197,48 +210,29 @@ test_spillbuf_interleaving(apr_pool_t *p
   svn_spillbuf_t *buf = svn_spillbuf__create(8 /* blocksize */,
                                              15 /* maxsize */,
                                              pool);
-  const char *readptr;
-  apr_size_t readlen;
 
   SVN_ERR(svn_spillbuf__write(buf, "abcdef", 6, pool));
   SVN_ERR(svn_spillbuf__write(buf, "ghijkl", 6, pool));
-  /* now: two blocks of 8 and 4 bytes  */
+  /* now: two blocks: 8 and 4 bytes  */
 
-  SVN_ERR(svn_spillbuf__read(&readptr, &readlen, buf, pool));
-  SVN_TEST_ASSERT(readptr != NULL
-                  && readlen == 8
-                  && memcmp(readptr, "abcdefgh", 8) == 0);
-  /* now: one block of 4 bytes  */
+  CHECK_READ(buf, 12, "abcdefgh", pool);
+  /* now: one block: 4 bytes  */
 
   SVN_ERR(svn_spillbuf__write(buf, "mnopqr", 6, pool));
-  /* now: two blocks of 8 and 2 bytes  */
+  /* now: two blocks: 8 and 2 bytes  */
 
-  SVN_ERR(svn_spillbuf__read(&readptr, &readlen, buf, pool));
-  SVN_TEST_ASSERT(readptr != NULL
-                  && readlen == 8
-                  && memcmp(readptr, "ijklmnop", 8) == 0);
-  /* now: one block of 2 bytes  */
+  CHECK_READ(buf, 10, "ijklmnop", pool);
+  /* now: one block: 2 bytes  */
 
   SVN_ERR(svn_spillbuf__write(buf, "stuvwx", 6, pool));
   SVN_ERR(svn_spillbuf__write(buf, "ABCDEF", 6, pool));
   SVN_ERR(svn_spillbuf__write(buf, "GHIJKL", 6, pool));
-  /* now: two blocks of 8 and 6 bytes, and 6 bytes spilled to a file  */
+  /* now: two blocks: 8 and 6 bytes, and 6 bytes spilled to a file  */
+
+  CHECK_READ(buf, 20, "qrstuvwx", pool);
+  CHECK_READ(buf, 12, "ABCDEF", pool);
+  CHECK_READ(buf, 6, "GHIJKL", pool);
 
-  SVN_TEST_ASSERT(svn_spillbuf__get_size(buf) > 0);
-  SVN_ERR(svn_spillbuf__read(&readptr, &readlen, buf, pool));
-  SVN_TEST_ASSERT(readptr != NULL
-                  && readlen == 8
-                  && memcmp(readptr, "qrstuvwx", 8) == 0);
-  SVN_TEST_ASSERT(svn_spillbuf__get_size(buf) > 0);
-  SVN_ERR(svn_spillbuf__read(&readptr, &readlen, buf, pool));
-  SVN_TEST_ASSERT(readptr != NULL
-                  && readlen == 6
-                  && memcmp(readptr, "ABCDEF", 6) == 0);
-  SVN_TEST_ASSERT(svn_spillbuf__get_size(buf) > 0);
-  SVN_ERR(svn_spillbuf__read(&readptr, &readlen, buf, pool));
-  SVN_TEST_ASSERT(readptr != NULL
-                  && readlen == 6
-                  && memcmp(readptr, "GHIJKL", 6) == 0);
   SVN_TEST_ASSERT(svn_spillbuf__get_size(buf) == 0);
 
   return SVN_NO_ERROR;
@@ -290,26 +284,26 @@ test_spillbuf_stream(apr_pool_t *pool)
   writelen = 6;
   SVN_ERR(svn_stream_write(stream, "abcdef", &writelen));
   SVN_ERR(svn_stream_write(stream, "ghijkl", &writelen));
-  /* now: two blocks of 8 and 4 bytes  */
+  /* now: two blocks: 8 and 4 bytes  */
 
   readlen = 8;
   SVN_ERR(svn_stream_read(stream, readbuf, &readlen));
   SVN_TEST_ASSERT(readlen == 8
                   && memcmp(readbuf, "abcdefgh", 8) == 0);
-  /* now: one block of 4 bytes  */
+  /* now: one block: 4 bytes  */
 
   SVN_ERR(svn_stream_write(stream, "mnopqr", &writelen));
-  /* now: two blocks of 8 and 2 bytes  */
+  /* now: two blocks: 8 and 2 bytes  */
 
   SVN_ERR(svn_stream_read(stream, readbuf, &readlen));
   SVN_TEST_ASSERT(readlen == 8
                   && memcmp(readbuf, "ijklmnop", 8) == 0);
-  /* now: one block of 2 bytes  */
+  /* now: one block: 2 bytes  */
 
   SVN_ERR(svn_stream_write(stream, "stuvwx", &writelen));
   SVN_ERR(svn_stream_write(stream, "ABCDEF", &writelen));
   SVN_ERR(svn_stream_write(stream, "GHIJKL", &writelen));
-  /* now: two blocks of 8 and 6 bytes, and 6 bytes spilled to a file  */
+  /* now: two blocks: 8 and 6 bytes, and 6 bytes spilled to a file  */
 
   SVN_ERR(svn_stream_read(stream, readbuf, &readlen));
   SVN_TEST_ASSERT(readlen == 8
@@ -326,6 +320,102 @@ test_spillbuf_stream(apr_pool_t *pool)
 }
 
 
+static svn_error_t *
+test_spillbuf_rwfile(apr_pool_t *pool)
+{
+  svn_spillbuf_t *buf = svn_spillbuf__create(4 /* blocksize */,
+                                             10 /* maxsize */,
+                                             pool);
+
+  SVN_ERR(svn_spillbuf__write(buf, "abcdef", 6, pool));
+  SVN_ERR(svn_spillbuf__write(buf, "ghijkl", 6, pool));
+  SVN_ERR(svn_spillbuf__write(buf, "mnopqr", 6, pool));
+  /* now: two blocks: 4 and 2 bytes, and 12 bytes in spill file.  */
+
+  CHECK_READ(buf, 18, "abcd", pool);
+  /* now: one block: 2 bytes, and 12 bytes in spill file.  */
+
+  CHECK_READ(buf, 14, "ef", pool);
+  /* now: no blocks, and 12 bytes in spill file.  */
+
+  CHECK_READ(buf, 12, "ghij", pool);
+  /* now: no blocks, and 8 bytes in spill file.  */
+
+  /* Write more data. It should be appended to the spill file.  */
+  SVN_ERR(svn_spillbuf__write(buf, "stuvwx", 6, pool));
+  /* now: no blocks, and 14 bytes in spill file.  */
+
+  CHECK_READ(buf, 14, "klmn", pool);
+  /* now: no blocks, and 10 bytes in spill file.  */
+
+  CHECK_READ(buf, 10, "opqr", pool);
+  /* now: no blocks, and 6 bytes in spill file.  */
+
+  CHECK_READ(buf, 6, "stuv", pool);
+  /* now: no blocks, and 2 bytes in spill file.  */
+
+  CHECK_READ(buf, 2, "wx", pool);
+  /* now: no blocks, and no spill file.  */
+
+  return SVN_NO_ERROR;
+}
+
+
+static svn_error_t *
+test_spillbuf_eof(apr_pool_t *pool)
+{
+  svn_spillbuf_t *buf = svn_spillbuf__create(4 /* blocksize */,
+                                             10 /* maxsize */,
+                                             pool);
+
+  SVN_ERR(svn_spillbuf__write(buf, "abcdef", 6, pool));
+  SVN_ERR(svn_spillbuf__write(buf, "ghijkl", 6, pool));
+  /* now: two blocks: 4 and 2 bytes, and 6 bytes in spill file.  */
+
+  CHECK_READ(buf, 12, "abcd", pool);
+  CHECK_READ(buf, 8, "ef", pool);
+  CHECK_READ(buf, 6, "ghij", pool);
+  CHECK_READ(buf, 2, "kl", pool);
+  /* The spill file should have been emptied and forgotten.  */
+
+  /* Assuming the spill file has been forgotten, this should result in
+     precisely the same behavior. Specifically: the initial write should
+     create two blocks, and the second write should be spilled. If there
+     *was* a spill file, then this written data would go into the file.  */
+  SVN_ERR(svn_spillbuf__write(buf, "abcdef", 6, pool));
+  SVN_ERR(svn_spillbuf__write(buf, "ghijkl", 6, pool));
+  CHECK_READ(buf, 12, "abcd", pool);
+  CHECK_READ(buf, 8, "ef", pool);
+  CHECK_READ(buf, 6, "ghij", pool);
+  CHECK_READ(buf, 2, "kl", pool);
+  /* The spill file should have been emptied and forgotten.  */
+
+  /* Now, let's do a sequence where we arrange to hit EOF precisely on
+     a block-sized read. Note: the second write must be more than 4 bytes,
+     or it will not cause a spill. We use 8 to get the right boundary.  */
+  SVN_ERR(svn_spillbuf__write(buf, "abcdef", 6, pool));
+  SVN_ERR(svn_spillbuf__write(buf, "ghijklmn", 8, pool));
+  CHECK_READ(buf, 14, "abcd", pool);
+  CHECK_READ(buf, 10, "ef", pool);
+  CHECK_READ(buf, 8, "ghij", pool);
+  CHECK_READ(buf, 4, "klmn", pool);
+  /* We discard the spill file when we know it has no data, rather than
+     upon hitting EOF (upon a read attempt). Thus, the spill file should
+     be gone.  */
+
+  /* Verify the forgotten spill file.  */
+  SVN_ERR(svn_spillbuf__write(buf, "abcdef", 6, pool));
+  SVN_ERR(svn_spillbuf__write(buf, "ghijkl", 6, pool));
+  CHECK_READ(buf, 12, "abcd", pool);
+  CHECK_READ(buf, 8, "ef", pool);
+  CHECK_READ(buf, 6, "ghij", pool);
+  /* Two unread bytes remaining in the spill file.  */
+  SVN_TEST_ASSERT(svn_spillbuf__get_size(buf) == 2);
+
+  return SVN_NO_ERROR;
+}
+
+
 /* The test table.  */
 struct svn_test_descriptor_t test_funcs[] =
   {
@@ -337,5 +427,7 @@ struct svn_test_descriptor_t test_funcs[
                    "interleaving reads and writes"),
     SVN_TEST_PASS2(test_spillbuf_reader, "spill buffer reader test"),
     SVN_TEST_PASS2(test_spillbuf_stream, "spill buffer stream test"),
+    SVN_TEST_PASS2(test_spillbuf_rwfile, "read/write spill file"),
+    SVN_TEST_PASS2(test_spillbuf_eof, "validate reaching EOF of spill file"),
     SVN_TEST_NULL
   };

Propchange: subversion/branches/fix-rdump-editor/subversion/tests/libsvn_subr/spillbuf-test.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/fix-rdump-editor/subversion/tests/libsvn_subr/string-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/subversion/tests/libsvn_subr/string-test.c?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/subversion/tests/libsvn_subr/string-test.c (original)
+++ subversion/branches/fix-rdump-editor/subversion/tests/libsvn_subr/string-test.c Wed May 16 20:32:43 2012
@@ -39,7 +39,7 @@
 #include "svn_io.h"
 #include "svn_error.h"
 #include "svn_string.h"   /* This includes <apr_*.h> */
-
+#include "private/svn_string_private.h"
 
 /* A quick way to create error messages.  */
 static svn_error_t *
@@ -511,6 +511,35 @@ test23(apr_pool_t *pool)
   return test_stringbuf_unequal("abc", "abb", pool);
 }
 
+static svn_error_t *
+test24(apr_pool_t *pool)
+{
+  char buffer[SVN_INT64_BUFFER_SIZE];
+  apr_size_t length;
+
+  length = svn__i64toa(buffer, 0);
+  SVN_TEST_ASSERT(length == 1);
+  SVN_TEST_STRING_ASSERT(buffer, "0");
+
+  length = svn__i64toa(buffer, 0x8000000000000000ll);
+  SVN_TEST_ASSERT(length == 20);
+  SVN_TEST_STRING_ASSERT(buffer, "-9223372036854775808");
+
+  length = svn__i64toa(buffer, 0x7fffffffffffffffll);
+  SVN_TEST_ASSERT(length == 19);
+  SVN_TEST_STRING_ASSERT(buffer, "9223372036854775807");
+
+  length = svn__ui64toa(buffer, 0);
+  SVN_TEST_ASSERT(length == 1);
+  SVN_TEST_STRING_ASSERT(buffer, "0");
+
+  length = svn__ui64toa(buffer, 0xffffffffffffffffll);
+  SVN_TEST_ASSERT(length == 20);
+  SVN_TEST_STRING_ASSERT(buffer, "18446744073709551615");
+
+  return test_stringbuf_unequal("abc", "abb", pool);
+}
+
 /*
    ====================================================================
    If you add a new test to this file, update this array.
@@ -568,5 +597,7 @@ struct svn_test_descriptor_t test_funcs[
                    "compare stringbufs; different lengths"),
     SVN_TEST_PASS2(test23,
                    "compare stringbufs; same length, different content"),
+    SVN_TEST_PASS2(test24,
+                   "verify i64toa"),
     SVN_TEST_NULL
   };

Modified: subversion/branches/fix-rdump-editor/subversion/tests/libsvn_wc/db-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/subversion/tests/libsvn_wc/db-test.c?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/subversion/tests/libsvn_wc/db-test.c (original)
+++ subversion/branches/fix-rdump-editor/subversion/tests/libsvn_wc/db-test.c Wed May 16 20:32:43 2012
@@ -228,8 +228,12 @@ static const char * const TESTING_DATA =
   "  1, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 2, " TIME_2s ", '" AUTHOR_2 "',"
   "  10, null, null, null);"
   "insert into nodes values ("
-  "  1, 'moved/file', 0, 'moved', 2, 'moved/file', 2, 'base-deleted',"
-  "  0, 'J/J-d', 'file', '()', null, '$sha1$" SHA1_1 "', null, 2, " TIME_2s ", '" AUTHOR_2 "',"
+  "  1, 'moved/file', 0, 'moved', 2, 'moved/file', 2, 'normal',"
+  "  0, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 2, " TIME_2s ", '" AUTHOR_2 "',"
+  "  10, null, null, null);"
+  "insert into nodes values ("
+  "  1, 'moved/file', 2, 'moved', 2, 'moved/file', 2, 'base-deleted',"
+  "  0, 'J/J-d', 'file', '()', null, null, null, null, null, null,"
   "  10, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e', 1, 'J', null, null, null, 'normal',"

Modified: subversion/branches/fix-rdump-editor/subversion/tests/libsvn_wc/op-depth-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/subversion/tests/libsvn_wc/op-depth-test.c?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/subversion/tests/libsvn_wc/op-depth-test.c (original)
+++ subversion/branches/fix-rdump-editor/subversion/tests/libsvn_wc/op-depth-test.c Wed May 16 20:32:43 2012
@@ -328,7 +328,7 @@ typedef struct nodes_row_t {
 
 /* Macro for filling in the REPO_* fields of a non-base NODES_ROW_T
  * that has no copy-from info. */
-#define NO_COPY_FROM SVN_INVALID_REVNUM, NULL
+#define NO_COPY_FROM SVN_INVALID_REVNUM, NULL, FALSE
 #define MOVED_HERE FALSE, NULL, TRUE
 
 /* Return a human-readable string representing ROW. */
@@ -342,12 +342,12 @@ print_row(const nodes_row_t *row,
     return "(null)";
 
   if (row->moved_to)
-    moved_to_str = apr_psprintf(result_pool, ", to %s", row->moved_to);
+    moved_to_str = apr_psprintf(result_pool, ", moved-to %s", row->moved_to);
   else
     moved_to_str = "";
 
   if (row->moved_here)
-    moved_here_str = ", here";
+    moved_here_str = ", moved-here";
   else
     moved_here_str = "";
 
@@ -362,8 +362,9 @@ print_row(const nodes_row_t *row,
                         moved_here_str, moved_to_str,
                         file_external_str);
   else
-    return apr_psprintf(result_pool, "%d, %s, %s, from ^/%s@%d%s%s%s",
+    return apr_psprintf(result_pool, "%d, %s, %s, %s ^/%s@%d%s%s%s",
                         row->op_depth, row->local_relpath, row->presence,
+                        row->op_depth == 0 ? "base" : "copyfrom",
                         row->repo_relpath, (int)row->repo_revnum,
                         moved_here_str, moved_to_str,
                         file_external_str);
@@ -1216,9 +1217,9 @@ insert_dirs(svn_test__sandbox_t *b,
       if (nodes->local_relpath[0])
         {
           SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 2));
-          SVN_ERR(svn_sqlite__bindf(stmt, "sissrs",
+          SVN_ERR(svn_sqlite__bindf(stmt, "sdssrs",
                                     nodes->local_relpath,
-                                    (apr_int64_t)nodes->op_depth,
+                                    nodes->op_depth,
                                     nodes->presence,
                                     nodes->repo_relpath,
                                     nodes->repo_revnum,
@@ -1228,9 +1229,9 @@ insert_dirs(svn_test__sandbox_t *b,
       else
         {
           SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 1));
-          SVN_ERR(svn_sqlite__bindf(stmt, "sissr",
+          SVN_ERR(svn_sqlite__bindf(stmt, "sdssr",
                                     nodes->local_relpath,
-                                    (apr_int64_t)nodes->op_depth,
+                                    nodes->op_depth,
                                     nodes->presence,
                                     nodes->repo_relpath,
                                     nodes->repo_revnum));
@@ -1713,8 +1714,8 @@ test_wc_move(const svn_test_opts_t *opts
       { 0, "",           "normal",       1, "" },
       { 0, "A",          "normal",       1, "A" },
       { 0, "A/B",        "normal",       1, "A/B" },
-      { 0, "A/B/C",      "normal",       1, "A/B/C", FALSE, "A/B/C-move" },
-      { 3, "A/B/C",      "base-deleted", NO_COPY_FROM },
+      { 0, "A/B/C",      "normal",       1, "A/B/C"},
+      { 3, "A/B/C",      "base-deleted", NO_COPY_FROM, "A/B/C-move" },
       { 3, "A/B/C-move", "normal",       1, "A/B/C", MOVED_HERE },
       { 0 }
     };
@@ -1726,13 +1727,13 @@ test_wc_move(const svn_test_opts_t *opts
     nodes_row_t rows[] = {
       { 0, "",                "normal",       1, "" },
       { 0, "A",               "normal",       1, "A" },
-      { 0, "A/B",             "normal",       1, "A/B", FALSE, "A/B-move" },
-      { 0, "A/B/C",           "normal",       1, "A/B/C", FALSE, "A/B-move/C-move" },
-      { 2, "A/B",             "base-deleted", NO_COPY_FROM },
-      { 2, "A/B/C",           "base-deleted", NO_COPY_FROM },
+      { 0, "A/B",             "normal",       1, "A/B"},
+      { 0, "A/B/C",           "normal",       1, "A/B/C"},
+      { 2, "A/B",             "base-deleted", NO_COPY_FROM, "A/B-move" },
+      { 2, "A/B/C",           "base-deleted", NO_COPY_FROM},
       { 2, "A/B-move",        "normal",       1, "A/B", MOVED_HERE },
       { 2, "A/B-move/C",      "normal",       1, "A/B/C", MOVED_HERE },
-      { 3, "A/B-move/C",      "base-deleted", NO_COPY_FROM },
+      { 3, "A/B-move/C",      "base-deleted", NO_COPY_FROM, "A/B-move/C-move" },
       { 3, "A/B-move/C-move", "normal",       1, "A/B/C", MOVED_HERE },
       { 0 }
     };
@@ -3168,9 +3169,7 @@ test_shadowed_update(const svn_test_opts
   SVN_ERR(wc_update(&b, "", 2));
   SVN_ERR(wc_copy(&b, "A", "A_tmp"));
   SVN_ERR(wc_update(&b, "", 1));
-  SVN_ERR(wc_move(&b, "A_tmp", "A")); /* ### XFAIL: sets moved-here on
-                                         A but A_tmp is removed and so
-                                         does not have moved-to. */
+  SVN_ERR(wc_move(&b, "A_tmp", "A"));
 
   SVN_ERR(wc_mkdir(&b, "K"));
   SVN_ERR(wc_mkdir(&b, "K/L"));
@@ -3773,8 +3772,8 @@ nested_moves_child_first(const svn_test_
       {0, "",       "normal",       1, ""},
       {0, "A",      "normal",       1, "A"},
       {0, "A/B",    "normal",       1, "A/B"},
-      {0, "A/B/C",  "normal",       1, "A/B/C", FALSE, "A/B/C2"},
-      {3, "A/B/C",  "base-deleted", NO_COPY_FROM},
+      {0, "A/B/C",  "normal",       1, "A/B/C"},
+      {3, "A/B/C",  "base-deleted", NO_COPY_FROM, "A/B/C2"},
       {3, "A/B/C2", "normal",       1, "A/B/C", MOVED_HERE},
       {0}
     };
@@ -3785,13 +3784,13 @@ nested_moves_child_first(const svn_test_
     nodes_row_t nodes[] = {
       {0, "",        "normal",       1, ""},
       {0, "A",       "normal",       1, "A"},
-      {0, "A/B",     "normal",       1, "A/B",   FALSE, "A/B2"},
-      {0, "A/B/C",   "normal",       1, "A/B/C", FALSE, "A/B2/C2"},
-      {2, "A/B",     "base-deleted", NO_COPY_FROM},
+      {0, "A/B",     "normal",       1, "A/B"},
+      {0, "A/B/C",   "normal",       1, "A/B/C"},
+      {2, "A/B",     "base-deleted", NO_COPY_FROM, "A/B2"},
       {2, "A/B/C",   "base-deleted", NO_COPY_FROM},
       {2, "A/B2",    "normal",       1, "A/B",   MOVED_HERE},
       {2, "A/B2/C",  "normal",       1, "A/B/C", MOVED_HERE},
-      {3, "A/B2/C",  "base-deleted", NO_COPY_FROM},
+      {3, "A/B2/C",  "base-deleted", NO_COPY_FROM, "A/B2/C2"},
       {3, "A/B2/C2", "normal",       1, "A/B/C", MOVED_HERE},
       {0}
     };
@@ -3801,20 +3800,20 @@ nested_moves_child_first(const svn_test_
   {
     nodes_row_t nodes[] = {
       {0, "",        "normal",       1, ""},
-      {0, "A",       "normal",       1, "A",     FALSE, "A2"},
-      {0, "A/B",     "normal",       1, "A/B",   FALSE, "A2/B2"},
-      {0, "A/B/C",   "normal",       1, "A/B/C", FALSE, "A2/B2/C2"},
-      {1, "A",       "base-deleted", NO_COPY_FROM},
+      {0, "A",       "normal",       1, "A"},
+      {0, "A/B",     "normal",       1, "A/B"},
+      {0, "A/B/C",   "normal",       1, "A/B/C"},
+      {1, "A",       "base-deleted", NO_COPY_FROM, "A2"},
       {1, "A/B",     "base-deleted", NO_COPY_FROM},
       {1, "A/B/C",   "base-deleted", NO_COPY_FROM},
       {1, "A2",      "normal",       1, "A",     MOVED_HERE},
       {1, "A2/B",    "normal",       1, "A/B",   MOVED_HERE},
       {1, "A2/B/C",  "normal",       1, "A/B/C", MOVED_HERE},
-      {2, "A2/B",    "base-deleted", NO_COPY_FROM},
+      {2, "A2/B",    "base-deleted", NO_COPY_FROM, "A2/B2"},
       {2, "A2/B/C",  "base-deleted", NO_COPY_FROM},
       {2, "A2/B2",   "normal",       1, "A/B",   MOVED_HERE},
       {2, "A2/B2/C", "normal",       1, "A/B/C", MOVED_HERE},
-      {3, "A2/B2/C", "base-deleted", NO_COPY_FROM},
+      {3, "A2/B2/C", "base-deleted", NO_COPY_FROM, "A2/B2/C2"},
       {3, "A2/B2/C2","normal",       1, "A/B/C", MOVED_HERE},
       {0}
     };
@@ -3827,10 +3826,10 @@ nested_moves_child_first(const svn_test_
   {
     nodes_row_t nodes[] = {
       {0, "",        "normal",       1, ""},
-      {0, "A",       "normal",       1, "A",     FALSE, "A2"},
+      {0, "A",       "normal",       1, "A"},
       {0, "A/B",     "normal",       1, "A/B"},
       {0, "A/B/C",   "normal",       1, "A/B/C"},
-      {1, "A",       "base-deleted", NO_COPY_FROM},
+      {1, "A",       "base-deleted", NO_COPY_FROM, "A2"},
       {1, "A/B",     "base-deleted", NO_COPY_FROM},
       {1, "A/B/C",   "base-deleted", NO_COPY_FROM},
       {1, "A2",      "normal",       1, "A",     MOVED_HERE},
@@ -3871,10 +3870,10 @@ nested_moves_child_last(const svn_test_o
   {
     nodes_row_t nodes[] = {
       {0, "",        "normal",       1, ""},
-      {0, "A",       "normal",       1, "A",     FALSE, "A2"},
+      {0, "A",       "normal",       1, "A"},
       {0, "A/B",     "normal",       1, "A/B"},
       {0, "A/B/C",   "normal",       1, "A/B/C"},
-      {1, "A",       "base-deleted", NO_COPY_FROM},
+      {1, "A",       "base-deleted", NO_COPY_FROM, "A2"},
       {1, "A/B",     "base-deleted", NO_COPY_FROM},
       {1, "A/B/C",   "base-deleted", NO_COPY_FROM},
       {1, "A2",      "normal",       1, "A",     MOVED_HERE},
@@ -3888,16 +3887,16 @@ nested_moves_child_last(const svn_test_o
   {
     nodes_row_t nodes[] = {
       {0, "",        "normal",       1, ""},
-      {0, "A",       "normal",       1, "A",     FALSE, "A2"},
-      {0, "A/B",     "normal",       1, "A/B",   FALSE, "A2/B2"},
+      {0, "A",       "normal",       1, "A"},
+      {0, "A/B",     "normal",       1, "A/B"},
       {0, "A/B/C",   "normal",       1, "A/B/C"},
-      {1, "A",       "base-deleted", NO_COPY_FROM},
+      {1, "A",       "base-deleted", NO_COPY_FROM, "A2"},
       {1, "A/B",     "base-deleted", NO_COPY_FROM},
       {1, "A/B/C",   "base-deleted", NO_COPY_FROM},
       {1, "A2",      "normal",       1, "A",     MOVED_HERE},
       {1, "A2/B",    "normal",       1, "A/B",   MOVED_HERE},
       {1, "A2/B/C",  "normal",       1, "A/B/C", MOVED_HERE},
-      {2, "A2/B",    "base-deleted", NO_COPY_FROM},
+      {2, "A2/B",    "base-deleted", NO_COPY_FROM, "A2/B2"},
       {2, "A2/B/C",  "base-deleted", NO_COPY_FROM},
       {2, "A2/B2",   "normal",       1, "A/B",   MOVED_HERE},
       {2, "A2/B2/C", "normal",       1, "A/B/C", MOVED_HERE},
@@ -3909,20 +3908,20 @@ nested_moves_child_last(const svn_test_o
   {
     nodes_row_t nodes[] = {
       {0, "",        "normal",       1, ""},
-      {0, "A",       "normal",       1, "A",     FALSE, "A2"},
-      {0, "A/B",     "normal",       1, "A/B",   FALSE, "A2/B2"},
-      {0, "A/B/C",   "normal",       1, "A/B/C", FALSE, "A2/B2/C2"},
-      {1, "A",       "base-deleted", NO_COPY_FROM},
+      {0, "A",       "normal",       1, "A"},
+      {0, "A/B",     "normal",       1, "A/B"},
+      {0, "A/B/C",   "normal",       1, "A/B/C"},
+      {1, "A",       "base-deleted", NO_COPY_FROM, "A2"},
       {1, "A/B",     "base-deleted", NO_COPY_FROM},
       {1, "A/B/C",   "base-deleted", NO_COPY_FROM},
       {1, "A2",      "normal",       1, "A",     MOVED_HERE},
       {1, "A2/B",    "normal",       1, "A/B",   MOVED_HERE},
       {1, "A2/B/C",  "normal",       1, "A/B/C", MOVED_HERE},
-      {2, "A2/B",    "base-deleted", NO_COPY_FROM},
+      {2, "A2/B",    "base-deleted", NO_COPY_FROM, "A2/B2"},
       {2, "A2/B/C",  "base-deleted", NO_COPY_FROM},
       {2, "A2/B2",   "normal",       1, "A/B",   MOVED_HERE},
       {2, "A2/B2/C", "normal",       1, "A/B/C", MOVED_HERE},
-      {3, "A2/B2/C", "base-deleted", NO_COPY_FROM},
+      {3, "A2/B2/C", "base-deleted", NO_COPY_FROM, "A2/B2/C2"},
       {3, "A2/B2/C2","normal",       1, "A/B/C", MOVED_HERE},
       {0}
     };
@@ -3935,10 +3934,10 @@ nested_moves_child_last(const svn_test_o
   {
     nodes_row_t nodes[] = {
       {0, "",        "normal",       1, ""},
-      {0, "A",       "normal",       1, "A",     FALSE, "A2"},
+      {0, "A",       "normal",       1, "A"},
       {0, "A/B",     "normal",       1, "A/B"},
       {0, "A/B/C",   "normal",       1, "A/B/C"},
-      {1, "A",       "base-deleted", NO_COPY_FROM},
+      {1, "A",       "base-deleted", NO_COPY_FROM, "A2"},
       {1, "A/B",     "base-deleted", NO_COPY_FROM},
       {1, "A/B/C",   "base-deleted", NO_COPY_FROM},
       {1, "A2",      "normal",       1, "A",     MOVED_HERE},
@@ -3976,8 +3975,7 @@ move_in_copy(const svn_test_opts_t *opts
     };
     SVN_ERR(check_db_rows(&b, "", nodes));
   }
-  SVN_ERR(wc_move(&b, "A2/B", "A2/B2")); /* ### Moved-here gets recorded, but
-                                            not moved-to. */
+  SVN_ERR(wc_move(&b, "A2/B", "A2/B2"));
   {
     nodes_row_t nodes[] = {
       {0, "",      "normal",       1, ""},
@@ -3985,8 +3983,8 @@ move_in_copy(const svn_test_opts_t *opts
       {0, "A/B",   "normal",       1, "A/B"},
       {1, "A2",    "normal",       1, "A"},
       {1, "A2/B",  "normal",       1, "A/B"},
-      {2, "A2/B",  "base-deleted", NO_COPY_FROM},
-      {2, "A2/B2", "normal",       1, "A/B"},
+      {2, "A2/B",  "base-deleted", NO_COPY_FROM, "A2/B2"},
+      {2, "A2/B2", "normal",       1, "A/B", MOVED_HERE},
       {0}
     };
     SVN_ERR(check_db_rows(&b, "", nodes));
@@ -4024,9 +4022,7 @@ move_in_replace(const svn_test_opts_t *o
     };
     SVN_ERR(check_db_rows(&b, "", nodes));
   }
-  SVN_ERR(wc_move(&b, "A/B", "A/B2")); /* ### Moved-to gets recorded on A/B
-                                          at op-depth=0, that's not the node
-                                          that got moved. */
+  SVN_ERR(wc_move(&b, "A/B", "A/B2"));
   {
     nodes_row_t nodes[] = {
       {0, "",     "normal",       1, ""},
@@ -4036,7 +4032,7 @@ move_in_replace(const svn_test_opts_t *o
       {0, "X/B",  "normal",       1, "X/B"},
       {1, "A",    "normal",       1, "X"},
       {1, "A/B",  "normal",       1, "X/B"},
-      {2, "A/B",  "base-deleted", NO_COPY_FROM},
+      {2, "A/B",  "base-deleted", NO_COPY_FROM, "A/B2"},
       {2, "A/B2", "normal",       1, "X/B", MOVED_HERE},
       {0}
     };
@@ -4075,9 +4071,9 @@ copy_a_move(const svn_test_opts_t *opts,
       {0, "",      "normal",       1, ""},
       {0, "A",     "normal",       1, "A"},
       {0, "A/B",   "normal",       1, "A/B"},
-      {0, "A/B/C", "normal",       1, "A/B/C", FALSE, "A/C2"},
+      {0, "A/B/C", "normal",       1, "A/B/C"},
       {2, "A/C2",  "normal",       1, "A/B/C", MOVED_HERE},
-      {3, "A/B/C", "base-deleted", NO_COPY_FROM},
+      {3, "A/B/C", "base-deleted", NO_COPY_FROM, "A/C2"},
       {0}
     };
     SVN_ERR(check_db_rows(&b, "", nodes));
@@ -4091,14 +4087,14 @@ copy_a_move(const svn_test_opts_t *opts,
       {0, "",       "normal",       1, ""},
       {0, "A",      "normal",       1, "A"},
       {0, "A/B",    "normal",       1, "A/B"},
-      {0, "A/B/C",  "normal",       1, "A/B/C", FALSE, "A/C2"},
+      {0, "A/B/C",  "normal",       1, "A/B/C"},
       {2, "A/C2",   "normal",       1, "A/B/C", MOVED_HERE},
-      {3, "A/B/C",  "base-deleted", NO_COPY_FROM},
+      {3, "A/B/C",  "base-deleted", NO_COPY_FROM, "A/C2"},
       {1, "A2",     "normal",       1, "A"},
       {1, "A2/B",   "normal",       1, "A/B"},
       {1, "A2/B/C", "normal",       1, "A/B/C"},
-      {2, "A2/C2",  "normal",       1, "A/B/C"},
-      {3, "A2/B/C", "base-deleted", NO_COPY_FROM},
+      {2, "A2/C2",  "normal",       1, "A/B/C"},   /* MOVED_HERE? */
+      {3, "A2/B/C", "base-deleted", NO_COPY_FROM}, /* "A2/C2"? */
       {0}
     };
     SVN_ERR(check_db_rows(&b, "", nodes));
@@ -4128,12 +4124,12 @@ move_to_swap(const svn_test_opts_t *opts
     nodes_row_t nodes[] = {
       {0, "",    "normal",       1, ""},
       {0, "A",   "normal",       1, "A"},
-      {0, "A/B", "normal",       1, "A/B", FALSE, "X/B"},
+      {0, "A/B", "normal",       1, "A/B"},
       {0, "X",   "normal",       1, "X"},
-      {0, "X/Y", "normal",       1, "X/Y", FALSE, "A/Y"},
-      {2, "A/B", "base-deleted", NO_COPY_FROM},
+      {0, "X/Y", "normal",       1, "X/Y"},
+      {2, "A/B", "base-deleted", NO_COPY_FROM, "X/B"},
       {2, "A/Y", "normal",       1, "X/Y", MOVED_HERE},
-      {2, "X/Y", "base-deleted", NO_COPY_FROM},
+      {2, "X/Y", "base-deleted", NO_COPY_FROM, "A/Y"},
       {2, "X/B", "normal",       1, "A/B", MOVED_HERE},
       {0}
     };
@@ -4141,26 +4137,69 @@ move_to_swap(const svn_test_opts_t *opts
   }
 
   SVN_ERR(wc_move(&b, "A", "A2"));
+
+  {
+    nodes_row_t nodes[] = {
+      {0, "",     "normal",       1, ""},
+      {0, "A",    "normal",       1, "A"},
+      {0, "A/B",  "normal",       1, "A/B"},
+      {0, "X",    "normal",       1, "X"},
+      {0, "X/Y",  "normal",       1, "X/Y"},
+      {1, "A",    "base-deleted", NO_COPY_FROM, "A2"},
+      {1, "A/B",  "base-deleted", NO_COPY_FROM},
+      {1, "A2",   "normal",       1, "A", MOVED_HERE},
+      {1, "A2/B", "normal",       1, "A/B", MOVED_HERE},
+      {2, "A2/B", "base-deleted", NO_COPY_FROM, "X/B"},
+      {2, "A2/Y", "normal",       1, "X/Y", MOVED_HERE},
+      {2, "X/Y",  "base-deleted", NO_COPY_FROM, "A2/Y"},
+      {2, "X/B",  "normal",       1, "A/B", MOVED_HERE},
+      {0}
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
   SVN_ERR(wc_move(&b, "X", "A"));
+
+  {
+    nodes_row_t nodes[] = {
+      {0, "",     "normal",       1, ""},
+      {0, "A",    "normal",       1, "A"},
+      {0, "A/B",  "normal",       1, "A/B"},
+      {0, "X",    "normal",       1, "X"},
+      {0, "X/Y",  "normal",       1, "X/Y"},
+      {1, "A",    "normal",       1, "X", FALSE, "A2", TRUE},
+      {1, "A/B",  "base-deleted", NO_COPY_FROM},
+      {1, "A/Y",  "normal",       1, "X/Y", MOVED_HERE},
+      {1, "A2",   "normal",       1, "A", MOVED_HERE},
+      {1, "A2/B", "normal",       1, "A/B", MOVED_HERE},
+      {1, "X",    "base-deleted", NO_COPY_FROM, "A"},
+      {1, "X/Y",  "base-deleted", NO_COPY_FROM},
+      {2, "A/B",  "normal",       1, "A/B", MOVED_HERE},
+      {2, "A/Y",  "base-deleted", NO_COPY_FROM, "A2/Y"},
+      {2, "A2/B", "base-deleted", NO_COPY_FROM, "A/B"},
+      {2, "A2/Y", "normal",       1, "X/Y", MOVED_HERE},
+      {0}
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
   SVN_ERR(wc_move(&b, "A2", "X"));
 
-  /* Is this correct or should A/Y and X/B at op-depth=1 be marked
-     moved-here? */
   {
     nodes_row_t nodes[] = {
       {0, "",    "normal",       1, ""},
-      {0, "A",   "normal",       1, "A",   FALSE, "X"},
-      {0, "A/B", "normal",       1, "A/B", FALSE, "A/B"},
-      {0, "X",   "normal",       1, "X",   FALSE, "A"},
-      {0, "X/Y", "normal",       1, "X/Y", FALSE, "X/Y"},
-      {1, "A",   "normal",       1, "X",   MOVED_HERE},
+      {0, "A",   "normal",       1, "A"},
+      {0, "A/B", "normal",       1, "A/B"},
+      {0, "X",   "normal",       1, "X"},
+      {0, "X/Y", "normal",       1, "X/Y"},
+      {1, "A",   "normal",       1, "X",   FALSE, "X", TRUE},
       {1, "A/Y", "normal",       1, "X/Y", MOVED_HERE},
       {1, "A/B", "base-deleted", NO_COPY_FROM},
-      {1, "X",   "normal",       1, "A",   MOVED_HERE},
+      {1, "X",   "normal",       1, "A",   FALSE, "A", TRUE},
       {1, "X/B", "normal",       1, "A/B", MOVED_HERE},
       {1, "X/Y", "base-deleted", NO_COPY_FROM},
-      {2, "A/Y", "base-deleted", NO_COPY_FROM},
-      {2, "X/B", "base-deleted", NO_COPY_FROM},
+      {2, "A/Y", "base-deleted", NO_COPY_FROM, "X/Y"},
+      {2, "X/B", "base-deleted", NO_COPY_FROM, "A/B"},
       {2, "A/B", "normal",       1, "A/B", MOVED_HERE},
       {2, "X/Y", "normal",       1, "X/Y", MOVED_HERE},
       {0}
@@ -4178,14 +4217,14 @@ move_to_swap(const svn_test_opts_t *opts
   {
     nodes_row_t nodes[] = {
       {0, "",    "normal",       1, ""},
-      {0, "A",   "normal",       1, "A",   FALSE, "X"},
+      {0, "A",   "normal",       1, "A"},
       {0, "A/B", "normal",       1, "A/B"},
-      {0, "X",   "normal",       1, "X",   FALSE, "A"},
+      {0, "X",   "normal",       1, "X"},
       {0, "X/Y", "normal",       1, "X/Y"},
-      {1, "A",   "normal",       1, "X",   MOVED_HERE},
+      {1, "A",   "normal",       1, "X",   FALSE, "X", TRUE},
       {1, "A/Y", "normal",       1, "X/Y", MOVED_HERE},
       {1, "A/B", "base-deleted", NO_COPY_FROM},
-      {1, "X",   "normal",       1, "A",   MOVED_HERE},
+      {1, "X",   "normal",       1, "A",   FALSE, "A", TRUE},
       {1, "X/B", "normal",       1, "A/B", MOVED_HERE},
       {1, "X/Y", "base-deleted", NO_COPY_FROM},
       {0}
@@ -4199,18 +4238,18 @@ move_to_swap(const svn_test_opts_t *opts
   {
     nodes_row_t nodes[] = {
       {0, "",    "normal",       1, ""},
-      {0, "A",   "normal",       1, "A",   FALSE, "X"},
-      {0, "A/B", "normal",       1, "A/B", FALSE, "A/B"},
-      {0, "X",   "normal",       1, "X",   FALSE, "A"},
-      {0, "X/Y", "normal",       1, "X/Y", FALSE, "X/Y"},
-      {1, "A",   "normal",       1, "X",   MOVED_HERE},
+      {0, "A",   "normal",       1, "A"},
+      {0, "A/B", "normal",       1, "A/B"},
+      {0, "X",   "normal",       1, "X"},
+      {0, "X/Y", "normal",       1, "X/Y"},
+      {1, "A",   "normal",       1, "X",   FALSE, "X", TRUE},
       {1, "A/Y", "normal",       1, "X/Y", MOVED_HERE},
       {1, "A/B", "base-deleted", NO_COPY_FROM},
-      {1, "X",   "normal",       1, "A",   MOVED_HERE},
+      {1, "X",   "normal",       1, "A",   FALSE, "A", TRUE},
       {1, "X/B", "normal",       1, "A/B", MOVED_HERE},
       {1, "X/Y", "base-deleted", NO_COPY_FROM},
-      {2, "A/Y", "base-deleted", NO_COPY_FROM},
-      {2, "X/B", "base-deleted", NO_COPY_FROM},
+      {2, "A/Y", "base-deleted", NO_COPY_FROM, "X/Y"},
+      {2, "X/B", "base-deleted", NO_COPY_FROM, "A/B"},
       {2, "A/B", "normal",       1, "A/B", MOVED_HERE},
       {2, "X/Y", "normal",       1, "X/Y", MOVED_HERE},
       {0}
@@ -4227,10 +4266,10 @@ revert_nested_move(const svn_test_opts_t
   svn_test__sandbox_t b;
   nodes_row_t nodes_A_moved[] = {
     {0, "",       "normal",       1, ""},
-    {0, "A",      "normal",       1, "A",     FALSE, "A2"},
+    {0, "A",      "normal",       1, "A"},
     {0, "A/B",    "normal",       1, "A/B"},
     {0, "A/B/C",  "normal",       1, "A/B/C"},
-    {1, "A",      "base-deleted", NO_COPY_FROM},
+    {1, "A",      "base-deleted", NO_COPY_FROM, "A2"},
     {1, "A/B",    "base-deleted", NO_COPY_FROM},
     {1, "A/B/C",  "base-deleted", NO_COPY_FROM},
     {1, "A2",     "normal",       1, "A",     MOVED_HERE},
@@ -4240,16 +4279,16 @@ revert_nested_move(const svn_test_opts_t
   };
   nodes_row_t nodes_AB_moved[] = {
     {0, "",        "normal",       1, ""},
-    {0, "A",       "normal",       1, "A",     FALSE, "A2"},
-    {0, "A/B",     "normal",       1, "A/B",   FALSE, "A2/B2"},
+    {0, "A",       "normal",       1, "A"},
+    {0, "A/B",     "normal",       1, "A/B"},
     {0, "A/B/C",   "normal",       1, "A/B/C"},
-    {1, "A",       "base-deleted", NO_COPY_FROM},
+    {1, "A",       "base-deleted", NO_COPY_FROM, "A2"},
     {1, "A/B",     "base-deleted", NO_COPY_FROM},
     {1, "A/B/C",   "base-deleted", NO_COPY_FROM},
     {1, "A2",      "normal",       1, "A",     MOVED_HERE},
     {1, "A2/B",    "normal",       1, "A/B",   MOVED_HERE},
     {1, "A2/B/C",  "normal",       1, "A/B/C", MOVED_HERE},
-    {2, "A2/B",    "base-deleted", NO_COPY_FROM},
+    {2, "A2/B",    "base-deleted", NO_COPY_FROM, "A2/B2"},
     {2, "A2/B/C",  "base-deleted", NO_COPY_FROM},
     {2, "A2/B2",   "normal",       1, "A/B",   MOVED_HERE},
     {2, "A2/B2/C", "normal",       1, "A/B/C", MOVED_HERE},
@@ -4257,20 +4296,20 @@ revert_nested_move(const svn_test_opts_t
   };
   nodes_row_t nodes_ABC_moved[] = {
     {0, "",         "normal",       1, ""},
-    {0, "A",        "normal",       1, "A",     FALSE, "A2"},
-    {0, "A/B",      "normal",       1, "A/B",   FALSE, "A2/B2"},
-    {0, "A/B/C",    "normal",       1, "A/B/C", FALSE, "A2/B2/C2"},
-    {1, "A",        "base-deleted", NO_COPY_FROM},
+    {0, "A",        "normal",       1, "A"},
+    {0, "A/B",      "normal",       1, "A/B"},
+    {0, "A/B/C",    "normal",       1, "A/B/C"},
+    {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
     {1, "A/B",      "base-deleted", NO_COPY_FROM},
     {1, "A/B/C",    "base-deleted", NO_COPY_FROM},
     {1, "A2",       "normal",       1, "A",     MOVED_HERE},
     {1, "A2/B",     "normal",       1, "A/B",   MOVED_HERE},
     {1, "A2/B/C",   "normal",       1, "A/B/C", MOVED_HERE},
-    {2, "A2/B",     "base-deleted", NO_COPY_FROM},
+    {2, "A2/B",     "base-deleted", NO_COPY_FROM, "A2/B2"},
     {2, "A2/B/C",   "base-deleted", NO_COPY_FROM},
     {2, "A2/B2",    "normal",       1, "A/B",   MOVED_HERE},
     {2, "A2/B2/C",  "normal",       1, "A/B/C", MOVED_HERE},
-    {3, "A2/B2/C",  "base-deleted", NO_COPY_FROM},
+    {3, "A2/B2/C",  "base-deleted", NO_COPY_FROM, "A2/B2/C2"},
     {3, "A2/B2/C2", "normal",       1, "A/B/C", MOVED_HERE},
     {0}
   };
@@ -4308,6 +4347,13 @@ revert_nested_move(const svn_test_opts_t
   SVN_ERR(wc_revert(&b, "A2/B2", svn_depth_infinity));
   SVN_ERR(check_db_rows(&b, "", nodes_A_moved));
 
+  /* Check moves in reverse order */
+  SVN_ERR(wc_revert(&b, "", svn_depth_infinity));
+  SVN_ERR(wc_move(&b, "A/B/C", "A/B/C2"));
+  SVN_ERR(wc_move(&b, "A/B", "A/B2"));
+  SVN_ERR(wc_move(&b, "A", "A2"));
+  SVN_ERR(check_db_rows(&b, "", nodes_ABC_moved));
+
   return SVN_NO_ERROR;
 }
 
@@ -4333,34 +4379,30 @@ move_on_move(const svn_test_opts_t *opts
     nodes_row_t nodes[] = {
       {0, "",         "normal",       1, ""},
       {0, "A",        "normal",       1, "A"},
-      {0, "A/B",      "normal",       1, "A/B", FALSE, "B2"},
+      {0, "A/B",      "normal",       1, "A/B"},
       {0, "X",        "normal",       1, "X"},
       {0, "X/B",      "normal",       1, "X/B"},
       {1, "B2",       "normal",       1, "A/B", MOVED_HERE},
       {1, "A",        "normal",       1, "X"},
-      {1, "A/B",      "normal",       1, "X/B"},
+      {1, "A/B",      "normal",       1, "X/B", FALSE, "B2"},
       {0}
     };
     SVN_ERR(check_db_rows(&b, "", nodes));
   }
 
-  /* A/B to B2 is already recorded in A/B but the copy has given us
-     another A/B that we can move.  A second move overwites the first
-     move stored in A/B even though it's a different node being moved,
-     and that breaks the recording of the move to B2. */
   SVN_ERR(wc_move(&b, "A/B", "B3"));
   {
     nodes_row_t nodes[] = {
       {0, "",         "normal",       1, ""},
       {0, "A",        "normal",       1, "A"},
-      {0, "A/B",      "normal",       1, "A/B",   FALSE, "B2"}, /* XFAIL */
+      {0, "A/B",      "normal",       1, "A/B"},
       {0, "X",        "normal",       1, "X"},
       {0, "X/B",      "normal",       1, "X/B"},
       {1, "B2",       "normal",       1, "A/B",   MOVED_HERE},
       {1, "B3",       "normal",       1, "X/B",   MOVED_HERE},
       {1, "A",        "normal",       1, "X"},
-      {1, "A/B",      "normal",       1, "X/B"},         /* moved_to=B3? */
-      {2, "A/B",      "base-deleted", NO_COPY_FROM},
+      {1, "A/B",      "normal",       1, "X/B", FALSE, "B2"},
+      {2, "A/B",      "base-deleted", NO_COPY_FROM, "B3"},
       {0}
     };
     SVN_ERR(check_db_rows(&b, "", nodes));
@@ -4390,37 +4432,33 @@ move_on_move2(const svn_test_opts_t *opt
   {
     nodes_row_t nodes[] = {
       {0, "",         "normal",       1, ""},
-      {0, "A",        "normal",       1, "A",   FALSE, "A2"},
+      {0, "A",        "normal",       1, "A"},
       {0, "A/B",      "normal",       1, "A/B"},
       {0, "X",        "normal",       1, "X"},
       {0, "X/B",      "normal",       1, "X/B"},
       {1, "A2",       "normal",       1, "A",   MOVED_HERE},
       {1, "A2/B",     "normal",       1, "A/B", MOVED_HERE},
-      {1, "A",        "normal",       1, "X"},
+      {1, "A",        "normal",       1, "X", FALSE, "A2"},
       {1, "A/B",      "normal",       1, "X/B"},
       {0}
     };
     SVN_ERR(check_db_rows(&b, "", nodes));
   }
 
-  /* A/B is already moved to A2/B but there is no explicit moved_to,
-     we derive it from A.  The copy has given us another A/B that we
-     can move doing so stores explicit moved_to in A/B that breaks the
-     recording of the first move to A2/B. */
   SVN_ERR(wc_move(&b, "A/B", "B3"));
   {
     nodes_row_t nodes[] = {
       {0, "",         "normal",       1, ""},
-      {0, "A",        "normal",       1, "A",   FALSE, "A2"},
-      {0, "A/B",      "normal",       1, "A/B"},               /* XFAIL */
+      {0, "A",        "normal",       1, "A"},
+      {0, "A/B",      "normal",       1, "A/B"},
       {0, "X",        "normal",       1, "X"},
       {0, "X/B",      "normal",       1, "X/B"},
       {1, "A2",       "normal",       1, "A",   MOVED_HERE},
       {1, "A2/B",     "normal",       1, "A/B", MOVED_HERE},
       {1, "B3",       "normal",       1, "X/B", MOVED_HERE},
-      {1, "A",        "normal",       1, "X"},
-      {1, "A/B",      "normal",       1, "X/B"},           /* moved_to=B3? */
-      {2, "A/B",      "base-deleted", NO_COPY_FROM},
+      {1, "A",        "normal",       1, "X", FALSE, "A2"},
+      {1, "A/B",      "normal",       1, "X/B"},
+      {2, "A/B",      "base-deleted", NO_COPY_FROM, "B3"},
       {0}
     };
     SVN_ERR(check_db_rows(&b, "", nodes));
@@ -4450,13 +4488,13 @@ move_added(const svn_test_opts_t *opts, 
   {
     nodes_row_t nodes[] = {
       {0, "",         "normal",       1, ""},
-      {0, "A",        "normal",       1, "A",   FALSE, "A2"},
+      {0, "A",        "normal",       1, "A"},
       {0, "A/B",      "normal",       1, "A/B"},
-      {1, "A",        "base-deleted", NO_COPY_FROM},
+      {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
       {1, "A/B",      "base-deleted", NO_COPY_FROM},
       {1, "A2",       "normal",       1, "A",   MOVED_HERE},
       {1, "A2/B",     "normal",       1, "A/B", MOVED_HERE},
-      {3, "A2/B/C",   "normal",       NO_COPY_FROM},          /* XFAIL */
+      {3, "A2/B/C",   "normal",       NO_COPY_FROM},
       {3, "A2/B/C2",  "normal",       NO_COPY_FROM},
       {0}
     };
@@ -4466,6 +4504,468 @@ move_added(const svn_test_opts_t *opts, 
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+move_update(const svn_test_opts_t *opts, apr_pool_t *pool)
+{
+  svn_test__sandbox_t b;
+
+  SVN_ERR(svn_test__sandbox_create(&b, "move_update", opts, pool));
+
+  SVN_ERR(wc_mkdir(&b, "A"));
+  SVN_ERR(wc_mkdir(&b, "A/B"));
+  SVN_ERR(wc_commit(&b, ""));
+  SVN_ERR(wc_mkdir(&b, "A/B/C"));
+  SVN_ERR(wc_commit(&b, ""));
+  SVN_ERR(wc_update(&b, "", 1));
+
+  /* A is single-revision so A2 is a single-revision copy */
+  SVN_ERR(wc_move(&b, "A", "A2"));
+  {
+    nodes_row_t nodes[] = {
+      {0, "",         "normal",       1, ""},
+      {0, "A",        "normal",       1, "A"},
+      {0, "A/B",      "normal",       1, "A/B"},
+      {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
+      {1, "A/B",      "base-deleted", NO_COPY_FROM},
+      {1, "A2",       "normal",       1, "A", MOVED_HERE},
+      {1, "A2/B",     "normal",       1, "A/B", MOVED_HERE},
+      {0}
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
+  /* Update A/B makes A2 a mixed-revision copy */
+  SVN_ERR(wc_update(&b, "A/B", 2));
+  {
+    nodes_row_t nodes[] = {
+      {0, "",         "normal",       1, ""},
+      {0, "A",        "normal",       1, "A"},
+      {0, "A/B",      "normal",       2, "A/B"},
+      {0, "A/B/C",    "normal",       2, "A/B/C"},
+      {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
+      {1, "A/B",      "base-deleted", NO_COPY_FROM},
+      {1, "A/B/C",    "base-deleted", NO_COPY_FROM},
+      {1, "A2",       "normal",       1, "A", MOVED_HERE},
+      {1, "A2/B",     "not-present",  2, "A/B"},                 /* XFAIL */
+      {2, "A2/B",     "normal",       2, "A/B", MOVED_HERE},
+      {2, "A2/B/C",   "normal",       2, "A/B/C", MOVED_HERE},
+      {0}
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
+  /* Update A makes A2 back into a single-revision copy */
+  SVN_ERR(wc_update(&b, "A", 2));
+  {
+    nodes_row_t nodes[] = {
+      {0, "",         "normal",       1, ""},
+      {0, "A",        "normal",       2, "A"},
+      {0, "A/B",      "normal",       2, "A/B"},
+      {0, "A/B/C",    "normal",       2, "A/B/C"},
+      {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
+      {1, "A/B",      "base-deleted", NO_COPY_FROM},
+      {1, "A/B/C",    "base-deleted", NO_COPY_FROM},
+      {1, "A2",       "normal",       2, "A", MOVED_HERE},
+      {1, "A2/B",     "normal",       2, "A/B", MOVED_HERE},
+      {1, "A2/B/C",   "normal",       2, "A/B/C", MOVED_HERE},
+      {0}
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+check_moved_to(apr_array_header_t *moved_tos,
+               int i,
+               int op_depth,
+               const char *local_relpath)
+{
+  struct svn_wc__db_moved_to_t *moved_to;
+
+  if (i >= moved_tos->nelts)
+    return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
+                             "moved-to %d not found", i);
+
+  moved_to = APR_ARRAY_IDX(moved_tos, i, struct svn_wc__db_moved_to_t *);
+
+  if (moved_to->op_depth != op_depth
+      || strcmp(moved_to->local_relpath, local_relpath))
+    return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
+                             "expected: {%d '%s'} found[%d]: {%d '%s'}",
+                             op_depth, local_relpath, i,
+                             moved_to->op_depth, moved_to->local_relpath);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_scan_delete(const svn_test_opts_t *opts, apr_pool_t *pool)
+{
+  svn_test__sandbox_t b;
+  const char *moved_to_abspath, *moved_to_op_root_abspath;
+  apr_array_header_t *moved_tos;
+
+  SVN_ERR(svn_test__sandbox_create(&b, "scan_delete", opts, pool));
+
+  SVN_ERR(wc_mkdir(&b, "A"));
+  SVN_ERR(wc_mkdir(&b, "A/B"));
+  SVN_ERR(wc_mkdir(&b, "A/B/C"));
+  SVN_ERR(wc_mkdir(&b, "A2"));
+  SVN_ERR(wc_mkdir(&b, "A2/B"));
+  SVN_ERR(wc_mkdir(&b, "C2"));
+  SVN_ERR(wc_commit(&b, ""));
+  SVN_ERR(wc_update(&b, "", 1));
+
+  SVN_ERR(wc_move(&b, "A2", "X"));
+  SVN_ERR(wc_move(&b, "X/B", "Z"));
+  SVN_ERR(wc_move(&b, "A/B", "X/B"));
+  SVN_ERR(wc_move(&b, "X/B/C", "Y"));
+  SVN_ERR(wc_move(&b, "C2", "X/B/C"));
+
+  {
+    nodes_row_t nodes[] = {
+      {0, "",         "normal",       1, ""},
+      {0, "A",        "normal",       1, "A"},
+      {0, "A/B",      "normal",       1, "A/B"},
+      {0, "A/B/C",    "normal",       1, "A/B/C"},
+      {0, "A2",       "normal",       1, "A2"},
+      {0, "A2/B",     "normal",       1, "A2/B"},
+      {0, "C2",       "normal",       1, "C2"},
+      {1, "A2",       "base-deleted", NO_COPY_FROM, "X"},
+      {1, "A2/B",     "base-deleted", NO_COPY_FROM},
+      {1, "Z",        "normal",       1, "A2/B", MOVED_HERE},
+      {1, "X",        "normal",       1, "A2", MOVED_HERE},
+      {1, "X/B",      "normal",       1, "A2/B", MOVED_HERE},
+      {2, "A/B",      "base-deleted", NO_COPY_FROM, "X/B"},
+      {2, "A/B/C",    "base-deleted", NO_COPY_FROM},
+      {2, "X/B",      "normal",       1, "A/B", FALSE, "Z", TRUE},
+      {2, "X/B/C",    "normal",       1, "A/B/C", MOVED_HERE},
+      {1, "Y",        "normal",       1, "A/B/C", MOVED_HERE},
+      {1, "C2",       "base-deleted", NO_COPY_FROM, "X/B/C"},
+      {3, "X/B/C",    "normal",       1, "C2", FALSE, "Y", TRUE},
+      {0}
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
+  SVN_ERR(svn_wc__db_scan_deletion(NULL, &moved_to_abspath,
+                                   NULL, &moved_to_op_root_abspath,
+                                   b.wc_ctx->db, wc_path(&b, "C2"),
+                                   pool, pool));
+  SVN_TEST_STRING_ASSERT(moved_to_abspath, wc_path(&b, "X/B/C"));
+  SVN_TEST_STRING_ASSERT(moved_to_op_root_abspath, wc_path(&b, "X/B/C"));
+
+  SVN_ERR(svn_wc__db_scan_deletion(NULL, &moved_to_abspath,
+                                   NULL, &moved_to_op_root_abspath,
+                                   b.wc_ctx->db, wc_path(&b, "A/B"),
+                                   pool, pool));
+  SVN_TEST_STRING_ASSERT(moved_to_abspath, wc_path(&b, "X/B"));
+  SVN_TEST_STRING_ASSERT(moved_to_op_root_abspath, wc_path(&b, "X/B"));
+
+  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
+                                     wc_path(&b, "A/B/C"), pool, pool));
+  SVN_ERR(check_moved_to(moved_tos, 0, 2, "X/B/C"));
+  SVN_ERR(check_moved_to(moved_tos, 1, 3, "Y"));
+  SVN_TEST_ASSERT(moved_tos->nelts == 2);
+
+  SVN_ERR(svn_wc__db_scan_deletion(NULL, &moved_to_abspath,
+                                   NULL, &moved_to_op_root_abspath,
+                                   b.wc_ctx->db, wc_path(&b, "A/B/C"),
+                                   pool, pool));
+  SVN_TEST_STRING_ASSERT(moved_to_abspath, wc_path(&b, "X/B/C"));
+  SVN_TEST_STRING_ASSERT(moved_to_op_root_abspath, wc_path(&b, "X/B"));
+
+  SVN_ERR(svn_wc__db_scan_deletion(NULL, &moved_to_abspath,
+                                   NULL, &moved_to_op_root_abspath,
+                                   b.wc_ctx->db, wc_path(&b, "A2"),
+                                   pool, pool));
+  SVN_TEST_STRING_ASSERT(moved_to_abspath, wc_path(&b, "X"));
+  SVN_TEST_STRING_ASSERT(moved_to_op_root_abspath, wc_path(&b, "X"));
+
+  SVN_ERR(svn_wc__db_scan_deletion(NULL, &moved_to_abspath,
+                                   NULL, &moved_to_op_root_abspath,
+                                   b.wc_ctx->db, wc_path(&b, "A2/B"),
+                                   pool, pool));
+  SVN_TEST_STRING_ASSERT(moved_to_abspath, wc_path(&b, "X/B"));
+  SVN_TEST_STRING_ASSERT(moved_to_op_root_abspath, wc_path(&b, "X"));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_follow_moved_to(const svn_test_opts_t *opts, apr_pool_t *pool)
+{
+  svn_test__sandbox_t b;
+  apr_array_header_t *moved_tos;
+
+  SVN_ERR(svn_test__sandbox_create(&b, "follow_moved_to", opts, pool));
+
+  SVN_ERR(wc_mkdir(&b, "A1"));
+  SVN_ERR(wc_mkdir(&b, "A1/B"));
+  SVN_ERR(wc_mkdir(&b, "A1/B/C"));
+  SVN_ERR(wc_mkdir(&b, "A1/B/C/D"));
+  SVN_ERR(wc_mkdir(&b, "A1/B/C/D/E"));
+  SVN_ERR(wc_mkdir(&b, "A2"));
+  SVN_ERR(wc_mkdir(&b, "A2/B"));
+  SVN_ERR(wc_mkdir(&b, "A2/B/C"));
+  SVN_ERR(wc_mkdir(&b, "A2/B/C/D"));
+  SVN_ERR(wc_mkdir(&b, "A2/B/C/D/E"));
+  SVN_ERR(wc_mkdir(&b, "A3"));
+  SVN_ERR(wc_mkdir(&b, "A3/B"));
+  SVN_ERR(wc_mkdir(&b, "A3/B/C"));
+  SVN_ERR(wc_mkdir(&b, "A3/B/C/D"));
+  SVN_ERR(wc_mkdir(&b, "A3/B/C/D/E"));
+  SVN_ERR(wc_commit(&b, ""));
+  SVN_ERR(wc_update(&b, "", 1));
+
+  {
+    nodes_row_t nodes[] = {
+      {0, "",         "normal", 1, ""},
+      {0, "A1",       "normal", 1, "A1"},
+      {0, "A1/B",     "normal", 1, "A1/B"},
+      {0, "A1/B/C",   "normal", 1, "A1/B/C"},
+      {0, "A1/B/C/D", "normal", 1, "A1/B/C/D"},
+      {0, "A1/B/C/D/E", "normal", 1, "A1/B/C/D/E"},
+      {0, "A2",       "normal", 1, "A2"},
+      {0, "A2/B",     "normal", 1, "A2/B"},
+      {0, "A2/B/C",   "normal", 1, "A2/B/C"},
+      {0, "A2/B/C/D", "normal", 1, "A2/B/C/D"},
+      {0, "A2/B/C/D/E", "normal", 1, "A2/B/C/D/E"},
+      {0, "A3",       "normal", 1, "A3"},
+      {0, "A3/B",     "normal", 1, "A3/B"},
+      {0, "A3/B/C",   "normal", 1, "A3/B/C"},
+      {0, "A3/B/C/D", "normal", 1, "A3/B/C/D"},
+      {0, "A3/B/C/D/E", "normal", 1, "A3/B/C/D/E"},
+      {0}
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
+  SVN_ERR(wc_move(&b, "A1", "X"));
+  SVN_ERR(wc_move(&b, "A2", "A1"));
+  SVN_ERR(wc_move(&b, "A3", "A2"));
+  SVN_ERR(wc_move(&b, "X", "A3"));
+  SVN_ERR(wc_move(&b, "A1/B", "X"));
+  SVN_ERR(wc_move(&b, "A2/B", "A1/B"));
+  SVN_ERR(wc_move(&b, "A3/B", "A2/B"));
+  SVN_ERR(wc_move(&b, "X", "A3/B"));
+  SVN_ERR(wc_move(&b, "A1/B/C/D", "X"));
+  SVN_ERR(wc_move(&b, "A2/B/C/D", "A1/B/C/D"));
+  SVN_ERR(wc_move(&b, "A3/B/C/D", "A2/B/C/D"));
+  SVN_ERR(wc_move(&b, "X", "A3/B/C/D"));
+  SVN_ERR(wc_move(&b, "A1/B/C/D/E", "X"));
+  SVN_ERR(wc_move(&b, "A2/B/C/D/E", "A1/B/C/D/E"));
+  SVN_ERR(wc_move(&b, "A3/B/C/D/E", "A2/B/C/D/E"));
+  SVN_ERR(wc_move(&b, "X", "A3/B/C/D/E"));
+
+  {
+    nodes_row_t nodes[] = {
+      {0, "",         "normal", 1, ""},
+      {0, "A1",       "normal", 1, "A1"},
+      {0, "A1/B",     "normal", 1, "A1/B"},
+      {0, "A1/B/C",   "normal", 1, "A1/B/C"},
+      {0, "A1/B/C/D", "normal", 1, "A1/B/C/D"},
+      {0, "A1/B/C/D/E", "normal", 1, "A1/B/C/D/E"},
+      {0, "A2",       "normal", 1, "A2"},
+      {0, "A2/B",     "normal", 1, "A2/B"},
+      {0, "A2/B/C",   "normal", 1, "A2/B/C"},
+      {0, "A2/B/C/D", "normal", 1, "A2/B/C/D"},
+      {0, "A2/B/C/D/E", "normal", 1, "A2/B/C/D/E"},
+      {0, "A3",       "normal", 1, "A3"},
+      {0, "A3/B",     "normal", 1, "A3/B"},
+      {0, "A3/B/C",   "normal", 1, "A3/B/C"},
+      {0, "A3/B/C/D", "normal", 1, "A3/B/C/D"},
+      {0, "A3/B/C/D/E", "normal", 1, "A3/B/C/D/E"},
+
+      {1, "A1",       "normal", 1, "A2", FALSE, "A3", TRUE},
+      {1, "A1/B",     "normal", 1, "A2/B", MOVED_HERE},
+      {1, "A1/B/C",   "normal", 1, "A2/B/C", MOVED_HERE},
+      {1, "A1/B/C/D", "normal", 1, "A2/B/C/D", MOVED_HERE},
+      {1, "A1/B/C/D/E", "normal", 1, "A2/B/C/D/E", MOVED_HERE},
+
+      {1, "A2",       "normal", 1, "A3", FALSE, "A1", TRUE},
+      {1, "A2/B",     "normal", 1, "A3/B", MOVED_HERE},
+      {1, "A2/B/C",   "normal", 1, "A3/B/C", MOVED_HERE},
+      {1, "A2/B/C/D", "normal", 1, "A3/B/C/D", MOVED_HERE},
+      {1, "A2/B/C/D/E", "normal", 1, "A3/B/C/D/E", MOVED_HERE},
+
+      {1, "A3",       "normal", 1, "A1", FALSE, "A2", TRUE},
+      {1, "A3/B",     "normal", 1, "A1/B", MOVED_HERE},
+      {1, "A3/B/C",   "normal", 1, "A1/B/C", MOVED_HERE},
+      {1, "A3/B/C/D", "normal", 1, "A1/B/C/D", MOVED_HERE},
+      {1, "A3/B/C/D/E", "normal", 1, "A1/B/C/D/E", MOVED_HERE},
+
+      {2, "A1/B",     "normal", 1, "A3/B", FALSE, "A3/B", TRUE},
+      {2, "A1/B/C",   "normal", 1, "A3/B/C", MOVED_HERE},
+      {2, "A1/B/C/D", "normal", 1, "A3/B/C/D", MOVED_HERE},
+      {2, "A1/B/C/D/E", "normal", 1, "A3/B/C/D/E", MOVED_HERE},
+
+      {2, "A2/B",     "normal", 1, "A1/B", FALSE, "A1/B", TRUE},
+      {2, "A2/B/C",   "normal", 1, "A1/B/C", MOVED_HERE},
+      {2, "A2/B/C/D", "normal", 1, "A1/B/C/D", MOVED_HERE},
+      {2, "A2/B/C/D/E", "normal", 1, "A1/B/C/D/E", MOVED_HERE},
+
+      {2, "A3/B",     "normal", 1, "A2/B", FALSE, "A2/B", TRUE},
+      {2, "A3/B/C",   "normal", 1, "A2/B/C", MOVED_HERE},
+      {2, "A3/B/C/D", "normal", 1, "A2/B/C/D", MOVED_HERE},
+      {2, "A3/B/C/D/E", "normal", 1, "A2/B/C/D/E", MOVED_HERE},
+
+      {4, "A1/B/C/D",   "normal", 1, "A1/B/C/D", FALSE, "A3/B/C/D", TRUE},
+      {4, "A1/B/C/D/E", "normal", 1, "A1/B/C/D/E", MOVED_HERE},
+
+      {4, "A2/B/C/D",   "normal", 1, "A2/B/C/D", FALSE, "A1/B/C/D", TRUE},
+      {4, "A2/B/C/D/E", "normal", 1, "A2/B/C/D/E", MOVED_HERE},
+
+      {4, "A3/B/C/D",   "normal", 1, "A3/B/C/D", FALSE, "A2/B/C/D", TRUE},
+      {4, "A3/B/C/D/E", "normal", 1, "A3/B/C/D/E", MOVED_HERE},
+
+      {5, "A1/B/C/D/E", "normal", 1, "A2/B/C/D/E", FALSE, "A3/B/C/D/E", TRUE},
+      {5, "A2/B/C/D/E", "normal", 1, "A3/B/C/D/E", FALSE, "A1/B/C/D/E", TRUE},
+      {5, "A3/B/C/D/E", "normal", 1, "A1/B/C/D/E", FALSE, "A2/B/C/D/E", TRUE},
+      {0}
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
+  /* A1->A3, A3/B->A2/B, A2/B/C/D->A1/B/C/D, A1/B/C/D/E->A3/B/C/D/E */
+  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
+                                     wc_path(&b, "A1"), pool, pool));
+  SVN_ERR(check_moved_to(moved_tos, 0, 1, "A3"));
+  SVN_TEST_ASSERT(moved_tos->nelts == 1);
+
+  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
+                                     wc_path(&b, "A1/B"), pool, pool));
+  SVN_ERR(check_moved_to(moved_tos, 0, 1, "A3/B"));
+  SVN_ERR(check_moved_to(moved_tos, 1, 2, "A2/B"));
+  SVN_TEST_ASSERT(moved_tos->nelts == 2);
+
+  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
+                                     wc_path(&b, "A1/B/C"), pool, pool));
+  SVN_ERR(check_moved_to(moved_tos, 0, 1, "A3/B/C"));
+  SVN_ERR(check_moved_to(moved_tos, 1, 2, "A2/B/C"));
+  SVN_TEST_ASSERT(moved_tos->nelts == 2);
+
+  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
+                                     wc_path(&b, "A1/B/C/D"), pool, pool));
+  SVN_ERR(check_moved_to(moved_tos, 0, 1, "A3/B/C/D"));
+  SVN_ERR(check_moved_to(moved_tos, 1, 2, "A2/B/C/D"));
+  SVN_ERR(check_moved_to(moved_tos, 2, 4, "A1/B/C/D"));
+  SVN_TEST_ASSERT(moved_tos->nelts == 3);
+
+  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
+                                     wc_path(&b, "A1/B/C/D/E"), pool, pool));
+  SVN_ERR(check_moved_to(moved_tos, 0, 1, "A3/B/C/D/E"));
+  SVN_ERR(check_moved_to(moved_tos, 1, 2, "A2/B/C/D/E"));
+  SVN_ERR(check_moved_to(moved_tos, 2, 4, "A1/B/C/D/E"));
+  SVN_ERR(check_moved_to(moved_tos, 3, 5, "A3/B/C/D/E"));
+  SVN_TEST_ASSERT(moved_tos->nelts == 4);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+mixed_rev_move(const svn_test_opts_t *opts, apr_pool_t *pool)
+{
+  svn_test__sandbox_t b;
+  apr_array_header_t *moved_tos;
+
+  SVN_ERR(svn_test__sandbox_create(&b, "mixed_rev_move", opts, pool));
+
+  SVN_ERR(wc_mkdir(&b, "A"));
+  SVN_ERR(wc_commit(&b, ""));
+  SVN_ERR(wc_mkdir(&b, "A/B"));
+  SVN_ERR(wc_commit(&b, ""));
+  SVN_ERR(wc_mkdir(&b, "A/B/C"));
+  SVN_ERR(wc_commit(&b, ""));
+
+  {
+    nodes_row_t nodes[] = {
+      {0, "",      "normal", 0, ""},
+      {0, "A",     "normal", 1, "A"},
+      {0, "A/B",   "normal", 2, "A/B"},
+      {0, "A/B/C", "normal", 3, "A/B/C"},
+      {0}
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
+  SVN_ERR(wc_move(&b, "A", "X"));
+
+  {
+    nodes_row_t nodes[] = {
+      {0, "",      "normal",       0, ""},
+      {0, "A",     "normal",       1, "A"},
+      {0, "A/B",   "normal",       2, "A/B"},
+      {0, "A/B/C", "normal",       3, "A/B/C"},
+      {1, "A",     "base-deleted", NO_COPY_FROM, "X"},
+      {1, "A/B",   "base-deleted", NO_COPY_FROM},
+      {1, "A/B/C", "base-deleted", NO_COPY_FROM},
+      {1, "X",     "normal",       1, "A", MOVED_HERE},
+      {1, "X/B",   "not-present",  2, "A/B"},
+      {2, "X/B",   "normal",       2, "A/B", MOVED_HERE},
+      {2, "X/B/C", "not-present",  3, "A/B/C"},
+      {3, "X/B/C", "normal",       3, "A/B/C", MOVED_HERE},
+      {0}
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
+  /* ### These values PASS but I'm not sure they are correct. */
+  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
+                                     wc_path(&b, "A/B/C"), pool, pool));
+  SVN_ERR(check_moved_to(moved_tos, 0, 1, "X/B/C"));
+  SVN_TEST_ASSERT(moved_tos->nelts == 1);
+
+  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
+                                     wc_path(&b, "A/B"), pool, pool));
+  SVN_ERR(check_moved_to(moved_tos, 0, 1, "X/B"));
+  SVN_TEST_ASSERT(moved_tos->nelts == 1);
+
+  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
+                                     wc_path(&b, "A"), pool, pool));
+  SVN_ERR(check_moved_to(moved_tos, 0, 1, "X"));
+  SVN_TEST_ASSERT(moved_tos->nelts == 1);
+
+
+  /* This move doesn't record moved-to */
+  SVN_ERR(wc_move(&b, "X/B", "X/Y"));
+
+  {
+    nodes_row_t nodes[] = {
+      {0, "",      "normal",       0, ""},
+      {0, "A",     "normal",       1, "A"},
+      {0, "A/B",   "normal",       2, "A/B"},
+      {0, "A/B/C", "normal",       3, "A/B/C"},
+      {1, "A",     "base-deleted", NO_COPY_FROM, "X"},
+      {1, "A/B",   "base-deleted", NO_COPY_FROM},
+      {1, "A/B/C", "base-deleted", NO_COPY_FROM},
+      {1, "X",     "normal",       1, "A", MOVED_HERE},
+      {1, "X/B",   "not-present",  2, "A/B"},
+      {2, "X/Y",   "normal",       2, "A/B"},
+      {2, "X/Y/C", "not-present",  NO_COPY_FROM},
+      {3, "X/Y/C", "normal",       3, "A/B/C"},
+      {0}
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
+  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
+                                     wc_path(&b, "A/B/C"), pool, pool));
+  SVN_TEST_ASSERT(moved_tos->nelts == 0);
+
+  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
+                                     wc_path(&b, "A/B"), pool, pool));
+  SVN_TEST_ASSERT(moved_tos->nelts == 0);
+
+  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
+                                     wc_path(&b, "A"), pool, pool));
+  SVN_ERR(check_moved_to(moved_tos, 0, 1, "X"));
+  SVN_TEST_ASSERT(moved_tos->nelts == 1);
+
+  return SVN_NO_ERROR;
+}
 
 
 /* ---------------------------------------------------------------------- */
@@ -4514,7 +5014,7 @@ struct svn_test_descriptor_t test_funcs[
                        "test_op_delete"),
     SVN_TEST_OPTS_PASS(test_child_replace_with_same_origin,
                        "test_child_replace_with_same"),
-    SVN_TEST_OPTS_XFAIL(test_shadowed_update,
+    SVN_TEST_OPTS_PASS(test_shadowed_update,
                        "test_shadowed_update"),
     SVN_TEST_OPTS_PASS(test_copy_of_deleted,
                        "test_copy_of_deleted (issue #3873)"),
@@ -4540,9 +5040,9 @@ struct svn_test_descriptor_t test_funcs[
                        "nested_moves_child_first"),
     SVN_TEST_OPTS_PASS(nested_moves_child_last,
                        "nested_moves_child_last"),
-    SVN_TEST_OPTS_XFAIL(move_in_copy,
+    SVN_TEST_OPTS_PASS(move_in_copy,
                        "move_in_copy"),
-    SVN_TEST_OPTS_XFAIL(move_in_replace,
+    SVN_TEST_OPTS_PASS(move_in_replace,
                        "move_in_replace"),
     SVN_TEST_OPTS_PASS(copy_a_move,
                        "copy_a_move"),
@@ -4550,11 +5050,19 @@ struct svn_test_descriptor_t test_funcs[
                        "move_to_swap"),
     SVN_TEST_OPTS_PASS(revert_nested_move,
                        "revert_nested_move"),
-    SVN_TEST_OPTS_XFAIL(move_on_move,
+    SVN_TEST_OPTS_PASS(move_on_move,
                        "move_on_move"),
-    SVN_TEST_OPTS_XFAIL(move_on_move2,
+    SVN_TEST_OPTS_PASS(move_on_move2,
                        "move_on_move2"),
-    SVN_TEST_OPTS_XFAIL(move_added,
+    SVN_TEST_OPTS_PASS(move_added,
                        "move_added"),
+    SVN_TEST_OPTS_XFAIL(move_update,
+                       "move_update"),
+    SVN_TEST_OPTS_PASS(test_scan_delete,
+                       "scan_delete"),
+    SVN_TEST_OPTS_PASS(test_follow_moved_to,
+                       "follow_moved_to"),
+    SVN_TEST_OPTS_PASS(mixed_rev_move,
+                       "mixed_rev_move"),
     SVN_TEST_NULL
   };

Propchange: subversion/branches/fix-rdump-editor/subversion/tests/libsvn_wc/wc-incomplete-tester.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/fix-rdump-editor/subversion/tests/svn_test.h
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/subversion/tests/svn_test.h?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/subversion/tests/svn_test.h (original)
+++ subversion/branches/fix-rdump-editor/subversion/tests/svn_test.h Wed May 16 20:32:43 2012
@@ -54,7 +54,9 @@ extern "C" {
   } while (0)
 
 /** Handy macro for testing an expected svn_error_t return value.
- * EXPECTED must be a real error (neither SVN_NO_ERROR nor APR_SUCCESS). */
+ * EXPECTED must be a real error (neither SVN_NO_ERROR nor APR_SUCCESS).
+ * The error returned by EXPR will be cleared.
+ */
 #define SVN_TEST_ASSERT_ERROR(expr, expected)                             \
   do {                                                                    \
     svn_error_t *err__ = (expr);                                          \
@@ -68,9 +70,13 @@ extern "C" {
                                         "Expected error %d but got %s",   \
                                         (expected),                       \
                                         "SVN_NO_ERROR");                  \
+    svn_error_clear(err__);                                               \
   } while (0)
 
 /** Handy macro for testing string equality.
+ *
+ * EXPR and/or EXPECTED_EXPR may be NULL which compares equal to NULL and
+ * not equal to any non-NULL string.
  */
 #define SVN_TEST_STRING_ASSERT(expr, expected_expr)                 \
   do {                                                              \
@@ -79,8 +85,8 @@ extern "C" {
                                                                     \
     if (tst_str2 == NULL && tst_str1 == NULL)                       \
       break;                                                        \
-    if (   (tst_str2 != NULL && tst_str1 == NULL)                   \
-        || (strcmp(tst_str2, tst_str1) != 0)  )                     \
+    if ((tst_str1 == NULL) || (tst_str2 == NULL)                    \
+        || (strcmp(tst_str2, tst_str1) != 0))                       \
       return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,           \
           "Strings not equal\n  Expected: '%s'\n  Found:    '%s'"   \
           "\n  at %s:%d",                                           \

Modified: subversion/branches/fix-rdump-editor/subversion/tests/svn_test_main.c
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/subversion/tests/svn_test_main.c?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/subversion/tests/svn_test_main.c (original)
+++ subversion/branches/fix-rdump-editor/subversion/tests/svn_test_main.c Wed May 16 20:32:43 2012
@@ -345,7 +345,6 @@ main(int argc, const char *argv[])
   const char *prog_name;
   int i;
   svn_boolean_t got_error = FALSE;
-  apr_allocator_t *allocator;
   apr_pool_t *pool, *test_pool;
   svn_boolean_t ran_a_test = FALSE;
   svn_boolean_t list_mode = FALSE;
@@ -368,16 +367,10 @@ main(int argc, const char *argv[])
       exit(1);
     }
 
-  /* set up the global pool.  Use a separate mutexless allocator,
-   * given this application is single threaded.
+  /* set up the global pool.  Use a separate allocator to limit memory
+   * usage but make it thread-safe to allow for multi-threaded tests.
    */
-  if (apr_allocator_create(&allocator))
-    return EXIT_FAILURE;
-
-  apr_allocator_max_free_set(allocator, SVN_ALLOCATOR_RECOMMENDED_MAX_FREE);
-
-  pool = svn_pool_create_ex(NULL, allocator);
-  apr_allocator_owner_set(allocator, pool);
+  pool = apr_allocator_owner_get(svn_pool_create_allocator(TRUE));
 
   /* Remember the command line */
   test_argc = argc;
@@ -461,7 +454,7 @@ main(int argc, const char *argv[])
         case server_minor_version_opt:
           {
             char *end;
-            opts.server_minor_version = strtol(opt_arg, &end, 10);
+            opts.server_minor_version = (int) strtol(opt_arg, &end, 10);
             if (end == opt_arg || *end != '\0')
               {
                 fprintf(stderr, "FAIL: Non-numeric minor version given\n");

Modified: subversion/branches/fix-rdump-editor/tools/buildbot/slaves/bb-openbsd/svnbuild.sh
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/buildbot/slaves/bb-openbsd/svnbuild.sh?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/buildbot/slaves/bb-openbsd/svnbuild.sh (original)
+++ subversion/branches/fix-rdump-editor/tools/buildbot/slaves/bb-openbsd/svnbuild.sh Wed May 16 20:32:43 2012
@@ -23,4 +23,4 @@ set -e
 set -x
 
 branch="$(basename $(svn info . | grep ^URL  | cut -d' ' -f2))"
-(cd .. && gmake BRANCH="$branch")
+(cd .. && gmake BRANCH="$branch" THREADING="no")

Modified: subversion/branches/fix-rdump-editor/tools/buildbot/slaves/win32-SharpSvn/svntest-bindings.cmd
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/buildbot/slaves/win32-SharpSvn/svntest-bindings.cmd?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/buildbot/slaves/win32-SharpSvn/svntest-bindings.cmd (original)
+++ subversion/branches/fix-rdump-editor/tools/buildbot/slaves/win32-SharpSvn/svntest-bindings.cmd Wed May 16 20:32:43 2012
@@ -72,7 +72,7 @@ pushd subversion\bindings\swig\perl\nati
 perl -MExtUtils::Command::MM -e test_harness() t\*.t
 IF ERRORLEVEL 1 (
   echo [Perl reported error %ERRORLEVEL%]
-  REM SET result=1
+  SET result=1
 )
 popd
 

Modified: subversion/branches/fix-rdump-editor/tools/buildbot/slaves/win32-SharpSvn/svntest-cleanup.cmd
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/buildbot/slaves/win32-SharpSvn/svntest-cleanup.cmd?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/buildbot/slaves/win32-SharpSvn/svntest-cleanup.cmd (original)
+++ subversion/branches/fix-rdump-editor/tools/buildbot/slaves/win32-SharpSvn/svntest-cleanup.cmd Wed May 16 20:32:43 2012
@@ -56,6 +56,7 @@ taskkill /im svnserve.exe /f 2> nul:
 taskkill /im svnrdump.exe /f 2> nul:
 taskkill /im svnsync.exe /f 2> nul:
 taskkill /im httpd.exe /f 2> nul:
+taskkill /im fs-test.exe /f 2> nul:
 taskkill /im op-depth-test.exe /f 2> nul:
 IF EXIST "%TESTDIR%\tests\subversion\tests\cmdline\httpd\" (
   rmdir /s /q  "%TESTDIR%\tests\subversion\tests\cmdline\httpd"

Modified: subversion/branches/fix-rdump-editor/tools/client-side/mergeinfo-sanitizer.py
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/client-side/mergeinfo-sanitizer.py?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/client-side/mergeinfo-sanitizer.py (original)
+++ subversion/branches/fix-rdump-editor/tools/client-side/mergeinfo-sanitizer.py Wed May 16 20:32:43 2012
@@ -25,7 +25,6 @@ import getopt
 import hashlib
 import pickle
 import getpass
-import re
 from svn import client, core, ra, wc
 
 ## This script first fetches the mergeinfo of the working copy and tries
@@ -42,7 +41,7 @@ except AttributeError:
 mergeinfo = {}
 
 def usage():
-  sys.stderr.write(""" Usage: %s WCPATH
+  sys.stderr.write(""" Usage: %s WCPATH [OPTION]
 
 Analyze the mergeinfo property of the given WCPATH.
 Look for the existence of merge_source's locations at their recorded

Propchange: subversion/branches/fix-rdump-editor/tools/client-side/mergeinfo-sanitizer.py
------------------------------------------------------------------------------
    svn:executable = *

Modified: subversion/branches/fix-rdump-editor/tools/dev/gdb-py/svndbg/printers.py
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/dev/gdb-py/svndbg/printers.py?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/dev/gdb-py/svndbg/printers.py (original)
+++ subversion/branches/fix-rdump-editor/tools/dev/gdb-py/svndbg/printers.py Wed May 16 20:32:43 2012
@@ -24,8 +24,168 @@ import gdb
 import re
 
 import gdb.printing
+from gdb.printing import RegexpCollectionPrettyPrinter
 
 
+class TypedefRegexCollectionPrettyPrinter(RegexpCollectionPrettyPrinter):
+    """Class for implementing a collection of regular-expression based
+       pretty-printers, matching on the type name at the point of use, such
+       as (but not necessarily) a 'typedef' name, ignoring 'const' or
+       'volatile' qualifiers.
+
+       This is modeled on RegexpCollectionPrettyPrinter, which (in GDB 7.3)
+       matches on the base type's tag name and can't match a pointer type or
+       any other type that doesn't have a tag name."""
+
+    def __init__(self, name):
+        super(TypedefRegexCollectionPrettyPrinter, self).__init__(name)
+
+    def __call__(self, val):
+        """Lookup the pretty-printer for the provided value."""
+
+        # Get the type name, without 'const' or 'volatile' qualifiers.
+        typename = str(val.type.unqualified())
+        if not typename:
+            return None
+
+        # Iterate over table of type regexps to find an enabled printer for
+        # that type.  Return an instantiation of the printer if found.
+        for printer in self.subprinters:
+            if printer.enabled and printer.compiled_re.search(typename):
+                return printer.gen_printer(val)
+
+        # Cannot find a pretty printer.  Return None.
+        return None
+
+class InferiorFunction:
+    """A class whose instances are callable functions on the inferior
+       process."""
+    def __init__(self, function_name):
+        self.function_name = function_name
+        self.func = None
+
+    def __call__(self, *args):
+        if not self.func:
+            self.func = gdb.parse_and_eval(self.function_name)
+        return self.func(*args)
+
+def children_as_map(children_iterator):
+    """Convert an iteration of (key, value) pairs into the form required for
+       a pretty-printer 'children' method when the display-hint is 'map'."""
+    for k, v in children_iterator:
+        yield 'key', k
+        yield 'val', v
+
+
+########################################################################
+
+# Pretty-printing for APR library types.
+
+# Some useful gdb.Type instances that can be initialized before any object
+# files are loaded.
+pvoidType = gdb.lookup_type('void').pointer()
+cstringType = gdb.lookup_type('char').pointer()
+
+# Some functions that resolve to calls into the inferior process.
+apr_hash_count = InferiorFunction('apr_hash_count')
+apr_hash_first = InferiorFunction('apr_hash_first')
+apr_hash_next = InferiorFunction('apr_hash_next')
+svn__apr_hash_index_key = InferiorFunction('svn__apr_hash_index_key')
+svn__apr_hash_index_val = InferiorFunction('svn__apr_hash_index_val')
+
+def children_of_apr_hash(hash_p, value_type=None):
+    """Iterate over an 'apr_hash_t *' GDB value, in the way required for a
+       pretty-printer 'children' method when the display-hint is 'array'.
+       Cast the value pointers to VALUE_TYPE, or return values as '...' if
+       VALUE_TYPE is None."""
+    hi = apr_hash_first(0, hash_p)
+    while (hi):
+        k = svn__apr_hash_index_key(hi).reinterpret_cast(cstringType)
+        if value_type:
+            val = svn__apr_hash_index_val(hi).reinterpret_cast(value_type)
+        else:
+            val = '...'
+        try:
+            key = k.string()
+        except:
+            key = '<unreadable>'
+        yield key, val
+        hi = apr_hash_next(hi)
+
+class AprHashPrinter:
+    """for 'apr_hash_t' of 'char *' keys and unknown values"""
+    def __init__(self, val):
+        self.hash_p = val.address
+
+    def to_string(self):
+        """Return a string to be displayed before children are displayed, or
+           return None if we don't want any such."""
+        return 'hash of ' + str(apr_hash_count(self.hash_p)) + ' items'
+
+    def children(self):
+        if not self.hash_p:
+            return []
+        return children_as_map(children_of_apr_hash(self.hash_p))
+
+    def display_hint(self):
+        return 'map'
+
+class PtrAprHashPrinter(AprHashPrinter):
+    """for pointer to 'apr_hash_t' of 'char *' keys and unknown values"""
+    def __init__(self, val):
+        self.hash_p = val
+
+    def to_string(self):
+        if not self.hash_p:
+            return 'NULL'
+        return AprHashPrinter.to_string(self)
+
+    def children(self):
+        if not self.hash_p:
+            return []
+        return AprHashPrinter.children(self)
+
+class AprArrayPrinter:
+    """for 'apr_array_header_t' of unknown elements"""
+    def __init__(self, val):
+        self.array = val
+
+    def to_string(self):
+        if not self.array:
+            return 'NULL'
+        nelts = self.array['nelts']
+        return 'array of ' + str(int(nelts)) + ' items'
+
+    def children(self):
+        # We can't display the children as we don't know their type.
+        return []
+
+    def display_hint(self):
+        return 'array'
+
+class PtrAprArrayPrinter(AprArrayPrinter):
+    """for pointer to 'apr_array_header_t' of unknown elements"""
+    def __init__(self, val):
+        if not val:
+            self.array = None
+        else:
+            self.array = val.dereference()
+
+    def to_string(self):
+        if not self.array:
+            return 'NULL'
+        return AprArrayPrinter.to_string(self)
+
+    def children(self):
+        if not self.array:
+            return []
+        return AprArrayPrinter.children(self)
+
+
+########################################################################
+
+# Pretty-printing for Subversion library types.
+
 class SvnStringPrinter:
     def __init__(self, val):
         self.val = val
@@ -42,22 +202,86 @@ class SvnStringPrinter:
     def display_hint(self):
         return 'string'
 
+class SvnMergeinfoCatalogPrinter:
+    """for svn_mergeinfo_catalog_t"""
+    def __init__(self, val):
+        self.hash_p = val
+
+    def to_string(self):
+        if self.hash_p == 0:
+            return 'NULL'
+        return 'mergeinfo catalog of ' + str(apr_hash_count(self.hash_p)) + ' items'
+
+    def children(self):
+        if self.hash_p == 0:
+            return None
+        mergeinfoType = gdb.lookup_type('svn_mergeinfo_t')
+        return children_as_map(children_of_apr_hash(self.hash_p, mergeinfoType))
+
+    def display_hint(self):
+        return 'map'
 
-def build_libsvn_printer():
-    global libsvn_printer
 
-    libsvn_printer = gdb.printing.RegexpCollectionPrettyPrinter("libsvn")
+########################################################################
+
+libapr_printer = None
+libapr_printer2 = None
+libsvn_printer = None
+libsvn_printer2 = None
 
-    libsvn_printer.add_printer('svn_string_t', '^svn_string_t$',
+def build_libsvn_printers():
+    """Construct the pretty-printer objects."""
+
+    global libapr_printer, libapr_printer2, libsvn_printer, libsvn_printer2
+
+    # These sub-printers match a struct's (or union)'s tag name,
+    # after stripping typedefs, references and const/volatile qualifiers.
+    libapr_printer = RegexpCollectionPrettyPrinter("libapr")
+    libapr_printer.add_printer('apr_hash_t', r'^apr_hash_t$',
+                               AprHashPrinter)
+    libapr_printer.add_printer('apr_array_header_t', r'^apr_array_header_t$',
+                               AprArrayPrinter)
+
+    # These sub-printers match a type name at the point of use,
+    # after stripping const/volatile qualifiers.
+    #
+    # TODO: The "apr_foo_t *" entries are in this collection merely because
+    #       the collection above can't match them, but ideally we'd fix that
+    #       matching and move these entries to there so that they get used
+    #       for any typedef that doesn't have its own specific pretty-printer
+    #       registered.
+    libapr_printer2 = TypedefRegexCollectionPrettyPrinter("libapr2")
+    libapr_printer2.add_printer('apr_hash_t *', r'^apr_hash_t \*$',
+                                PtrAprHashPrinter)
+    libapr_printer2.add_printer('apr_array_header_t *', r'^apr_array_header_t \*$',
+                                PtrAprArrayPrinter)
+
+    # These sub-printers match a struct's (or union)'s tag name,
+    # after stripping typedefs, references and const/volatile qualifiers.
+    libsvn_printer = RegexpCollectionPrettyPrinter("libsvn")
+    libsvn_printer.add_printer('svn_string_t', r'^svn_string_t$',
                                SvnStringPrinter)
 
+    # These sub-printers match a type name at the point of use,
+    # after stripping const/volatile qualifiers.
+    libsvn_printer2 = TypedefRegexCollectionPrettyPrinter("libsvn2")
+    libsvn_printer2.add_printer('svn_mergeinfo_catalog_t', r'^svn_mergeinfo_catalog_t$',
+                                SvnMergeinfoCatalogPrinter)
 
-libsvn_printer = None
 
 def register_libsvn_printers(obj):
-    global libsvn_printer
+    """Register the pretty-printers for the object file OBJ."""
+
+    global libapr_printer, libapr_printer2, libsvn_printer, libsvn_printer2
 
+    # Printers registered later take precedence.
+    gdb.printing.register_pretty_printer(obj, libapr_printer)
+    gdb.printing.register_pretty_printer(obj, libapr_printer2)
     gdb.printing.register_pretty_printer(obj, libsvn_printer)
+    gdb.printing.register_pretty_printer(obj, libsvn_printer2)
 
 
-build_libsvn_printer()
+# Construct the pretty-printer objects, once, at GDB start-up time when this
+# Python module is loaded.  (Registration happens later, once per object
+# file.)
+build_libsvn_printers()

Modified: subversion/branches/fix-rdump-editor/tools/dev/gen-py-errors.py
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/dev/gen-py-errors.py?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/dev/gen-py-errors.py (original)
+++ subversion/branches/fix-rdump-editor/tools/dev/gen-py-errors.py Wed May 16 20:32:43 2012
@@ -23,44 +23,87 @@
 # ====================================================================
 #
 #
-#  Meant to be run from the root of a Subversion working copy.  If anybody
-#  wants to do some path magic to improve that use, feel free.
-
-import sys, os
-sys.path.append(os.path.join('subversion', 'bindings', 'swig',
-                             'python', 'tests'))
-
+# Locates svn_error_codes.h based on its relative location to this script.
+#
+# Generates to STDOUT. Typically, redirect this into svntest/err.py
+#
 
-import setup_path
+import sys
+import os
+import re
 
-header = '''#!/usr/bin/env python
+HEADER = '''#!/usr/bin/env python
 ### This file automatically generated by tools/dev/gen-py-error.py,
 ### which see for more information
 ###
 ### It is versioned for convenience.
-
 '''
 
+# Established by svn 1.0. May as well hard-code these.
+APR_OS_START_ERROR = 20000
+APR_OS_START_USERERR = APR_OS_START_ERROR + 50000 * 2
+SVN_ERR_CATEGORY_SIZE = 5000
+
+RE_CAT_NAME = re.compile(r'SVN_ERR_([A-Z_]+)_CATEG')
+RE_CAT_VALUE = re.compile(r'\d+')
+
+RE_DEF_NAME = re.compile(r'SVN_ERRDEF\(([A-Z0-9_]+)')
+RE_DEF_VALUE = re.compile(r'SVN_ERR_([A-Z_]+)_CATEG[^0-9]*([0-9]+)')
+
+
+def write_output(codes):
+  print HEADER
+
+  for name, value in codes:
+    # skip SVN_ERR_ on the name
+    print '%s = %d' % (name[8:], value)
+
+
+def main(codes_fname):
+  categ = { }
+  codes = [ ]
+
+  f = open(codes_fname)
+
+  # Parse all the category start values
+  while True:
+    line = f.next()
+    m = RE_CAT_NAME.search(line)
+    if m:
+      name = m.group(1)
+      m = RE_CAT_VALUE.search(f.next())
+      assert m
+      value = int(m.group(0))
+      categ[name] = APR_OS_START_USERERR + value * SVN_ERR_CATEGORY_SIZE
+
+    elif line.strip() == 'SVN_ERROR_START':
+      break
+
+  # Parse each of the error values
+  while True:
+    line = f.next()
+    m = RE_DEF_NAME.search(line)
+    if m:
+      name = m.group(1)
+      line = f.next()
+      m = RE_DEF_VALUE.search(line)
+      if not m:
+        # SVN_ERR_WC_NOT_DIRECTORY is defined as equal to NOT_WORKING_COPY
+        # rather than relative to SVN_ERR_WC_CATEGORY_START
+        #print 'SKIP:', line
+        continue
+      cat = m.group(1)
+      value = int(m.group(2))
+      codes.append((name, categ[cat] + value))
 
-def write_output(errs, filename):
-  out = open(filename, 'w')
-  out.write(header)
-
-  for name, val in errs:
-    out.write('%s = %d\n' % (name, val))
-
-  out.close()
-
-
-def main(output_filename):
-  import core
+    elif line.strip() == 'SVN_ERROR_END':
+      break
 
-  errs = [e for e in dir(core.svn.core) if e.startswith('SVN_ERR_')]
-  codes = []
-  for e in errs:
-    codes.append((e[8:], getattr(core.svn.core, e)))
-  write_output(codes, output_filename)
+  write_output(sorted(codes))
 
 
 if __name__ == '__main__':
-  main(os.path.join('subversion', 'tests', 'cmdline', 'svntest', 'err.py'))
+  this_dir = os.path.dirname(os.path.abspath(__file__))
+  codes_fname = os.path.join(this_dir, os.path.pardir, os.path.pardir,
+                             'subversion', 'include', 'svn_error_codes.h')
+  main(codes_fname)