/*
 * op-depth-test.c :  test layered tree changes
 *
 * ====================================================================
 *    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.
 * ====================================================================
 */

/* To avoid warnings... */
#define SVN_DEPRECATED

#include <apr_pools.h>
#include <apr_general.h>

#include "svn_types.h"
#include "svn_hash.h"
#include "svn_io.h"
#include "svn_dirent_uri.h"
#include "svn_pools.h"
#include "svn_repos.h"
#include "svn_wc.h"
#include "svn_client.h"
#include "svn_hash.h"
#include "svn_sorts.h"

#include "utils.h"

#include "private/svn_dep_compat.h"
#include "private/svn_sorts_private.h"
#include "private/svn_sqlite.h"
#include "private/svn_wc_private.h"
#include "../../libsvn_wc/wc.h"
#include "../../libsvn_wc/wc_db.h"
#include "../../libsvn_wc/workqueue.h"
#include "../../libsvn_wc/conflicts.h"
#define SVN_WC__I_AM_WC_DB
#include "../../libsvn_wc/wc_db_private.h"

#include "../svn_test.h"

#include "wc-test-queries.h"

WC_TEST_QUERIES_SQL_DECLARE_STATEMENTS(op_depth_statements);

/* Compare strings, like strcmp but either or both may be NULL which
 * compares equal to NULL and not equal to any non-NULL string. */
static int
strcmp_null(const char *s1, const char *s2)
{
  if (s1 && s2)
    return strcmp(s1, s2);
  else if (s1 || s2)
    return 1;
  else
    return 0;
}


/* ---------------------------------------------------------------------- */
/* Reading the WC DB */

static svn_error_t *
open_wc_db(svn_sqlite__db_t **sdb,
           const char *wc_root_abspath,
           apr_pool_t *result_pool,
           apr_pool_t *scratch_pool)
{
  SVN_ERR(svn_wc__db_util_open_db(sdb, wc_root_abspath, "wc.db",
                                  svn_sqlite__mode_readwrite,
                                  FALSE /* exclusive */, 0 /* timeout */,
                                  op_depth_statements,
                                  result_pool, scratch_pool));
  return SVN_NO_ERROR;
}


/* ---------------------------------------------------------------------- */
/* Functions for comparing expected and found WC DB data. */

/* Some of the fields from a NODES table row. */
typedef struct nodes_row_t {
    int op_depth;
    const char *local_relpath;
    const char *presence;
    svn_revnum_t repo_revnum;
    const char *repo_relpath;
    svn_boolean_t file_external;
    const char *moved_to;
    svn_boolean_t moved_here;
    const char *props;  /* comma-separated list of prop names */
} nodes_row_t;

/* Tree conflict details */
typedef struct tree_conflict_info
{
  svn_wc_conflict_action_t action;
  svn_wc_conflict_reason_t reason;
  const char *delete_path;
  svn_boolean_t conflicted_fb; /* fallback for reason, action and path 0 */
} tree_conflict_info;

/* What conflicts are on a path. */
typedef struct conflict_info_t {
    const char *local_relpath;
    svn_boolean_t text_conflicted;
    svn_boolean_t prop_conflicted;

    tree_conflict_info tc;
} conflict_info_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, FALSE
#define MOVED_HERE FALSE, NULL, TRUE
#define NOT_MOVED  FALSE, NULL, FALSE
#define FILE_EXTERNAL TRUE

/* Return a comma-separated list of the prop names in PROPS, in lexically
 * ascending order, or NULL if PROPS is empty or NULL.  (Here, we don't
 * care about the difference between 'has no props' and 'can't have props',
 * and we choose to represent both of those as NULL.) */
static const char *
props_hash_to_text(apr_hash_t *props, apr_pool_t *pool)
{
  apr_array_header_t *props_sorted;
  svn_stringbuf_t *str;
  int i;

  if (! props)
    return NULL;

  str = svn_stringbuf_create_empty(pool);
  props_sorted = svn_sort__hash(props, svn_sort_compare_items_lexically, pool);
  for (i = 0; i < props_sorted->nelts; i++)
    {
      const svn_sort__item_t *item
        = &APR_ARRAY_IDX(props_sorted, i, svn_sort__item_t);

      if (str->len)
        svn_stringbuf_appendbyte(str, ',');
      svn_stringbuf_appendcstr(str, item->key);
    }
  return str->len ? str->data : NULL;
}

/* Return a human-readable string representing ROW. With a tiny bit of editting
   this can be used to create expected results */
static const char *
print_row(const nodes_row_t *row,
          apr_pool_t *result_pool)
{
  const char *relpath_str, *presence_str;
  const char *file_external_str, *moved_here_str, *moved_to_str, *props;

  if (row == NULL)
    return "(null)";

  relpath_str = apr_psprintf(result_pool, "\"%s\",", row->local_relpath);
  presence_str = apr_psprintf(result_pool, "\"%s\",", row->presence);
  if (row->moved_to)
    moved_to_str = apr_psprintf(result_pool, ", \"%s\"", row->moved_to);
  else
    moved_to_str = "";

  if (row->moved_here && !row->file_external && !row->moved_to)
    moved_here_str = ", MOVED_HERE";
  else if (row->moved_to)
    moved_here_str = ", TRUE";
  else
    moved_here_str = "";

  if (row->file_external)
    file_external_str = ", FILE_EXTERNAL";
  else if (row->moved_to || row->props)
    file_external_str = ", FALSE";
  else
    file_external_str = "";

  if (row->props)
    props = apr_psprintf(result_pool, ", p=(%s)", row->props);
  else
    props = "";

  if (row->repo_revnum == SVN_INVALID_REVNUM)
    return apr_psprintf(result_pool, "%d, %-20s%-15s NO_COPY_FROM%s%s%s%s",
                        row->op_depth, relpath_str, presence_str,
                        file_external_str, moved_here_str, moved_to_str,
                        props);
  else
    return apr_psprintf(result_pool, "%d, %-20s%-15s %d, \"%s\"%s%s%s%s",
                        row->op_depth, relpath_str, presence_str,
                        (int)row->repo_revnum, row->repo_relpath,
                        file_external_str, moved_here_str, moved_to_str,
                        props);
}
/* A baton to pass through svn_hash_diff() to compare_nodes_rows(). */
typedef struct comparison_baton_t {
    apr_hash_t *expected_hash;  /* Maps "OP_DEPTH PATH" to nodes_row_t. */
    apr_hash_t *found_hash;     /* Maps "OP_DEPTH PATH" to nodes_row_t. */
    apr_pool_t *scratch_pool;
    svn_error_t *errors;        /* Chain of errors found in comparison. */
} comparison_baton_t;

/* Compare two hash entries indexed by KEY, in the two hashes in BATON.
 * Append an error message to BATON->errors if they differ or are not both
 * present.
 *
 * Implements svn_hash_diff_func_t. */
static svn_error_t *
compare_nodes_rows(const void *key, apr_ssize_t klen,
                   enum svn_hash_diff_key_status status,
                   void *baton)
{
  comparison_baton_t *b = baton;
  nodes_row_t *expected = apr_hash_get(b->expected_hash, key, klen);
  nodes_row_t *found = apr_hash_get(b->found_hash, key, klen);

  if (! expected)
    {
      b->errors = svn_error_createf(
                    SVN_ERR_TEST_FAILED, b->errors,
                    "found   {%s}",
                    print_row(found, b->scratch_pool));
    }
  else if (! found)
    {
      b->errors = svn_error_createf(
                    SVN_ERR_TEST_FAILED, b->errors,
                    "expected {%s}",
                    print_row(expected, b->scratch_pool));
    }
  else if (expected->repo_revnum != found->repo_revnum
           || (strcmp_null(expected->repo_relpath, found->repo_relpath) != 0)
           || (strcmp_null(expected->presence, found->presence) != 0)
           || (expected->file_external != found->file_external)
           || (expected->moved_here != found->moved_here)
           || (expected->moved_to && !found->moved_to)
           || (!expected->moved_to && found->moved_to)
           || (expected->moved_to
               && strcmp(expected->moved_to, found->moved_to))
           || (expected->props != NULL
               && strcmp_null(expected->props, found->props) != 0))
    {
      b->errors = svn_error_createf(
                    SVN_ERR_TEST_FAILED, b->errors,
                    "expected {%s}; found {%s}",
                    print_row(expected, b->scratch_pool),
                    print_row(found, b->scratch_pool));
    }

  /* Don't terminate the comparison: accumulate all differences. */
  return SVN_NO_ERROR;
}


/* Examine the WC DB for paths ROOT_PATH and below, and check that their
 * rows in the 'NODES' table (only those at op_depth > 0) match EXPECTED_ROWS
 * (which is terminated by a row of null fields).
 *
 * Return a chain of errors describing any and all mismatches. */
static svn_error_t *
check_db_rows(svn_test__sandbox_t *b,
              const char *root_path,
              const nodes_row_t *expected_rows)
{
  svn_sqlite__db_t *sdb;
  int i;
  svn_sqlite__stmt_t *stmt;

  svn_boolean_t have_row;
  apr_hash_t *found_hash = apr_hash_make(b->pool);
  apr_hash_t *expected_hash = apr_hash_make(b->pool);
  comparison_baton_t comparison_baton;

  comparison_baton.expected_hash = expected_hash;
  comparison_baton.found_hash = found_hash;
  comparison_baton.scratch_pool = b->pool;
  comparison_baton.errors = NULL;

  /* Fill ACTUAL_HASH with data from the WC DB. */
  SVN_ERR(open_wc_db(&sdb, b->wc_abspath, b->pool, b->pool));
  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_NODES_INFO));
  SVN_ERR(svn_sqlite__bindf(stmt, "is", (apr_int64_t)1 /* wc_id */,
                                        root_path));
  SVN_ERR(svn_sqlite__step(&have_row, stmt));
  while (have_row)
    {
      const char *key;
      nodes_row_t *row = apr_palloc(b->pool, sizeof(*row));
      apr_hash_t *props_hash;

      row->op_depth = svn_sqlite__column_int(stmt, 0);
      row->presence = svn_sqlite__column_text(stmt, 1, b->pool);
      row->local_relpath = svn_sqlite__column_text(stmt, 2, b->pool);
      row->repo_revnum = svn_sqlite__column_revnum(stmt, 3);
      row->repo_relpath = svn_sqlite__column_text(stmt, 4, b->pool);
      row->file_external = !svn_sqlite__column_is_null(stmt, 5);
      row->moved_to = svn_sqlite__column_text(stmt, 7, b->pool);
      row->moved_here = svn_sqlite__column_boolean(stmt, 8);
      SVN_ERR(svn_sqlite__column_properties(&props_hash, stmt, 9,
                                            b->pool, b->pool));
      row->props = props_hash_to_text(props_hash, b->pool);

      if (row->file_external && svn_sqlite__column_is_null(stmt, 6))
        comparison_baton.errors
          = svn_error_createf(SVN_ERR_TEST_FAILED, comparison_baton.errors,
                              "incomplete {%s}", print_row(row, b->pool));

      key = apr_psprintf(b->pool, "%d %s", row->op_depth, row->local_relpath);
      apr_hash_set(found_hash, key, APR_HASH_KEY_STRING, row);

      SVN_ERR(svn_sqlite__step(&have_row, stmt));
    }
  SVN_ERR(svn_sqlite__reset(stmt));

  /* Fill EXPECTED_HASH with data from EXPECTED_ROWS. */
  for (i = 0; expected_rows[i].local_relpath != NULL; i++)
    {
      const char *key;
      const nodes_row_t *row = &expected_rows[i];

      key = apr_psprintf(b->pool, "%d %s", row->op_depth, row->local_relpath);
      apr_hash_set(expected_hash, key, APR_HASH_KEY_STRING, row);
    }

  /* Compare EXPECTED_HASH with ACTUAL_HASH and return any errors. */
  SVN_ERR(svn_hash_diff(expected_hash, found_hash,
                        compare_nodes_rows, &comparison_baton, b->pool));
  SVN_ERR(svn_sqlite__close(sdb));
  return comparison_baton.errors;
}

#define EDIT_EDIT_TC {svn_wc_conflict_reason_edited, \
                      svn_wc_conflict_action_edit, \
                      NULL, TRUE}
#define NO_TC { 0 }
static const char *
print_conflict(const conflict_info_t *row,
               apr_pool_t *result_pool)
{
  const char *tc_text;

  if (!row->tc.reason && !row->tc.action && !row->tc.delete_path)
    {
      if (row->tc.conflicted_fb)
        tc_text = "EDIT_EDIT_TC";
      else
        tc_text = "NO_TC";
    }
  else
    {
      const char *action;
      const char *reason;
      const char *path;

#define CASE_ENUM_STRVAL(x, y) case y: x = #y; break
      switch(row->tc.action)
        {
          CASE_ENUM_STRVAL(action, svn_wc_conflict_action_edit);
          CASE_ENUM_STRVAL(action, svn_wc_conflict_action_add);
          CASE_ENUM_STRVAL(action, svn_wc_conflict_action_delete);
          CASE_ENUM_STRVAL(action, svn_wc_conflict_action_replace);
          default:
            SVN_ERR_MALFUNCTION_NO_RETURN();
        }
      switch(row->tc.reason)
        {
          CASE_ENUM_STRVAL(reason, svn_wc_conflict_reason_edited);
          CASE_ENUM_STRVAL(reason, svn_wc_conflict_reason_obstructed);
          CASE_ENUM_STRVAL(reason, svn_wc_conflict_reason_deleted);
          CASE_ENUM_STRVAL(reason, svn_wc_conflict_reason_missing);
          CASE_ENUM_STRVAL(reason, svn_wc_conflict_reason_unversioned);
          CASE_ENUM_STRVAL(reason, svn_wc_conflict_reason_added);
          CASE_ENUM_STRVAL(reason, svn_wc_conflict_reason_replaced);
          CASE_ENUM_STRVAL(reason, svn_wc_conflict_reason_moved_away);
          CASE_ENUM_STRVAL(reason, svn_wc_conflict_reason_moved_here);
          default:
            SVN_ERR_MALFUNCTION_NO_RETURN();
        }

      if (row->tc.delete_path)
        path = apr_psprintf(result_pool, ", \"%s\"", row->tc.delete_path);
      else
        path = "";

      tc_text = apr_psprintf(result_pool, "{%s, %s%s}", action,
                             reason, path);
    }

  return apr_psprintf(result_pool, "\"%s\", %s, %s, %s",
                      row->local_relpath,
                      row->text_conflicted ? "TRUE" : "FALSE",
                      row->prop_conflicted ? "TRUE" : "FALSE",
                      tc_text);
}

static svn_boolean_t
tree_conflicts_match(const tree_conflict_info *expected,
                     const tree_conflict_info *actual)
{
    if (expected->action != actual->action)
      return FALSE;
    else if (expected->reason != actual->reason)
      return FALSE;
    else if (strcmp_null(expected->delete_path, actual->delete_path) != 0)
      return FALSE;
    else if (expected->conflicted_fb != actual->conflicted_fb)
      return FALSE;

    return TRUE;
}

static svn_error_t *
compare_conflict_info(const void *key, apr_ssize_t klen,
                      enum svn_hash_diff_key_status status,
                      void *baton)
{
  comparison_baton_t *b = baton;
  conflict_info_t *expected = apr_hash_get(b->expected_hash, key, klen);
  conflict_info_t *found = apr_hash_get(b->found_hash, key, klen);

  if (! expected)
    {
      b->errors = svn_error_createf(
                    SVN_ERR_TEST_FAILED, b->errors,
                    "found   {%s}",
                    print_conflict(found, b->scratch_pool));
    }
  else if (! found)
    {
      b->errors = svn_error_createf(
                    SVN_ERR_TEST_FAILED, b->errors,
                    "expected {%s}",
                    print_conflict(expected, b->scratch_pool));
    }
  else if (expected->text_conflicted != found->text_conflicted
           || expected->prop_conflicted != found->prop_conflicted
           || !tree_conflicts_match(&expected->tc, &found->tc))
    {
      b->errors = svn_error_createf(
                    SVN_ERR_TEST_FAILED, b->errors,
                    "expected {%s}; found {%s}",
                    print_conflict(expected, b->scratch_pool),
                    print_conflict(found, b->scratch_pool));
    }

  /* Don't terminate the comparison: accumulate all differences. */
  return SVN_NO_ERROR;
}

static svn_error_t *
check_db_conflicts(svn_test__sandbox_t *b,
                   const char *root_path,
                   const conflict_info_t *expected_conflicts)
{
  svn_sqlite__db_t *sdb;
  int i;
  svn_sqlite__stmt_t *stmt;

  svn_boolean_t have_row;
  apr_hash_t *found_hash = apr_hash_make(b->pool);
  apr_hash_t *expected_hash = apr_hash_make(b->pool);
  apr_pool_t *iterpool = svn_pool_create(b->pool);
  apr_hash_index_t *hi;
  comparison_baton_t comparison_baton;

  comparison_baton.expected_hash = expected_hash;
  comparison_baton.found_hash = found_hash;
  comparison_baton.scratch_pool = b->pool;
  comparison_baton.errors = NULL;

  /* Fill ACTUAL_HASH with data from the WC DB. */
  SVN_ERR(open_wc_db(&sdb, b->wc_abspath, b->pool, b->pool));
  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_ACTUAL_INFO));
  SVN_ERR(svn_sqlite__bindf(stmt, "is", (apr_int64_t)1 /* wc_id */,
                                        root_path));
  SVN_ERR(svn_sqlite__step(&have_row, stmt));
  while (have_row)
    {
      conflict_info_t *row = apr_pcalloc(b->pool, sizeof(*row));

      row->local_relpath = svn_sqlite__column_text(stmt, 0, b->pool);

      svn_hash_sets(found_hash, row->local_relpath, row);

      SVN_ERR(svn_sqlite__step(&have_row, stmt));
    }
  SVN_ERR(svn_sqlite__reset(stmt));
  SVN_ERR(svn_sqlite__close(sdb));

  for (hi = apr_hash_first(b->pool, found_hash); hi; hi = apr_hash_next(hi))
    {
      svn_skel_t *conflict;
      conflict_info_t *info = apr_hash_this_val(hi);
      const char *local_abspath;
      svn_boolean_t tree_conflicted;

      svn_pool_clear(iterpool);

      local_abspath = svn_dirent_join(b->wc_abspath, info->local_relpath,
                                      iterpool);

      SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL,
                                       b->wc_ctx->db, local_abspath,
                                       iterpool, iterpool));

      SVN_TEST_ASSERT(conflict != NULL);

      SVN_ERR(svn_wc__conflict_read_info(NULL, NULL,
                                         &info->text_conflicted,
                                         &info->prop_conflicted,
                                         &tree_conflicted,
                                         b->wc_ctx->db, local_abspath,
                                         conflict,
                                         iterpool, iterpool));

      if (tree_conflicted)
        {
          const char *move_src_abspath;
          SVN_ERR(svn_wc__conflict_read_tree_conflict(&info->tc.reason,
                                                      &info->tc.action,
                                                      &move_src_abspath,
                                                      NULL, b->wc_ctx->db,
                                                      local_abspath,
                                                      conflict,
                                                      b->pool, iterpool));

          if (move_src_abspath)
            info->tc.delete_path =
                svn_dirent_skip_ancestor(b->wc_abspath, move_src_abspath);

          if (!info->tc.reason
              && !info->tc.action
              && !info->tc.delete_path)
            {
              info->tc.conflicted_fb = TRUE;
            }
        }
    }

  /* Fill EXPECTED_HASH with data from EXPECTED_ROWS. */
  if (expected_conflicts)
    for (i = 0; expected_conflicts[i].local_relpath != NULL; i++)
      {
        const conflict_info_t *row = &expected_conflicts[i];

        svn_hash_sets(expected_hash, row->local_relpath, row);
      }

  /* Compare EXPECTED_HASH with ACTUAL_HASH and return any errors. */
  SVN_ERR(svn_hash_diff(expected_hash, found_hash,
                        compare_conflict_info, &comparison_baton, b->pool));
  return comparison_baton.errors;
}

static svn_error_t *
verify_db_callback(void *baton,
                   const char *wc_abspath,
                   const char *local_relpath,
                   int op_depth,
                   int id,
                   const char *msg,
                   apr_pool_t *scratch_pool)
{
  if (op_depth >= 0)
    return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
                "Verify: %s: %s (%d): SV%04d %s",
                wc_abspath, local_relpath, op_depth, id, msg);
  else
    return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
                "DB-VRFY: %s: %s: SV%04d %s",
                wc_abspath, local_relpath, id, msg);
}

static svn_error_t *
verify_db(svn_test__sandbox_t *b)
{
  SVN_ERR(svn_wc__db_verify_db_full(b->wc_ctx->db, b->wc_abspath,
                                    verify_db_callback, NULL, b->pool));

  return SVN_NO_ERROR;
}


/* ---------------------------------------------------------------------- */
/* The test functions */

/* Definition of a copy sub-test and its expected results. */
struct copy_subtest_t
{
  /* WC-relative or repo-relative source and destination paths. */
  const char *from_path;
  const char *to_path;
  /* All the expected nodes table rows within the destination sub-tree.
   * Terminated by an all-zero row. */
  nodes_row_t expected[20];
};

#define source_everything   "A/B"

#define source_base_file    "A/B/lambda"
#define source_base_dir     "A/B/E"

#define source_added_file   "A/B/file-added"
#define source_added_dir    "A/B/D-added"
#define source_added_dir2   "A/B/D-added/D2"

#define source_copied_file  "A/B/lambda-copied"
#define source_copied_dir   "A/B/E-copied"

/* Check that all kinds of WC-to-WC copies give correct op_depth results:
 * create a Greek tree, make copies in it, and check the resulting DB rows. */
static svn_error_t *
wc_wc_copies(svn_test__sandbox_t *b)
{
  SVN_ERR(sbox_add_and_commit_greek_tree(b));

  /* Create the various kinds of source node which will be copied */

  SVN_ERR(sbox_file_write(b, source_added_file, "New file"));
  SVN_ERR(sbox_wc_add(b, source_added_file));
  SVN_ERR(sbox_wc_mkdir(b, source_added_dir));
  SVN_ERR(sbox_wc_mkdir(b, source_added_dir2));

  SVN_ERR(sbox_wc_copy(b, source_base_file, source_copied_file));
  SVN_ERR(sbox_wc_copy(b, source_base_dir, source_copied_dir));

  /* Delete some nodes so that we can test copying onto these paths */

  SVN_ERR(sbox_wc_delete(b, "A/D/gamma"));
  SVN_ERR(sbox_wc_delete(b, "A/D/G"));

  /* Test copying various things */

  {
    struct copy_subtest_t subtests[] =
      {
        /* base file */
        { source_base_file, "A/C/copy1", {
            { 3, "",                "normal",   1, source_base_file }
          } },

        /* base dir */
        { source_base_dir, "A/C/copy2", {
            { 3, "",                "normal",   1, source_base_dir },
            { 3, "alpha",           "normal",   1, "A/B/E/alpha" },
            { 3, "beta",            "normal",   1, "A/B/E/beta" }
          } },

        /* added file */
        { source_added_file, "A/C/copy3", {
            { 3, "",                "normal",   NO_COPY_FROM }
          } },

        /* added dir */
        { source_added_dir, "A/C/copy4", {
            { 3, "",                "normal",   NO_COPY_FROM },
            { 4, "D2",              "normal",   NO_COPY_FROM }
          } },

        /* copied file */
        { source_copied_file, "A/C/copy5", {
            { 3, "",                "normal",   1, source_base_file }
          } },

        /* copied dir */
        { source_copied_dir, "A/C/copy6", {
            { 3, "",                "normal",   1, source_base_dir },
            { 3, "alpha",           "normal",   1, "A/B/E/alpha" },
            { 3, "beta",            "normal",   1, "A/B/E/beta" }
          } },

        /* copied tree with everything in it */
        { source_everything, "A/C/copy7", {
            { 3, "",                "normal",   1, source_everything },
            { 3, "lambda",          "normal",   1, "A/B/lambda" },
            { 3, "E",               "normal",   1, "A/B/E" },
            { 3, "E/alpha",         "normal",   1, "A/B/E/alpha" },
            { 3, "E/beta",          "normal",   1, "A/B/E/beta" },
            { 3, "F",               "normal",   1, "A/B/F" },
            /* Each add is an op_root */
            { 4, "file-added",      "normal",   NO_COPY_FROM },
            { 4, "D-added",         "normal",   NO_COPY_FROM },
            { 5, "D-added/D2",      "normal",   NO_COPY_FROM },
            /* Each copied-copy subtree is an op_root */
            { 4, "lambda-copied",   "normal",   1, source_base_file },
            { 4, "E-copied",        "normal",   1, source_base_dir },
            { 4, "E-copied/alpha",  "normal",   1, "A/B/E/alpha" },
            { 4, "E-copied/beta",   "normal",   1, "A/B/E/beta" }
          } },

        /* dir onto a schedule-delete file */
        { source_base_dir, "A/D/gamma", {
            { 0, "",                "normal",   1, "A/D/gamma" },
            { 3, "",                "normal",   1, source_base_dir },
            { 3, "alpha",           "normal",   1, "A/B/E/alpha" },
            { 3, "beta",            "normal",   1, "A/B/E/beta" }
          } },

        /* file onto a schedule-delete dir */
        { source_base_file, "A/D/G", {
            { 0, "",                "normal",   1, "A/D/G" },
            { 0, "pi",              "normal",   1, "A/D/G/pi" },
            { 0, "rho",             "normal",   1, "A/D/G/rho" },
            { 0, "tau",             "normal",   1, "A/D/G/tau" },
            { 3, "",                "normal",   1, source_base_file },
            { 3, "pi",              "base-deleted",   NO_COPY_FROM },
            { 3, "rho",             "base-deleted",   NO_COPY_FROM },
            { 3, "tau",             "base-deleted",   NO_COPY_FROM }
          } },

        { 0 }
      };
    struct copy_subtest_t *subtest;

    /* Fix up the expected->local_relpath fields in the subtest data to be
     * relative to the WC root rather than to the copy destination dir. */
    for (subtest = subtests; subtest->from_path; subtest++)
      {
        nodes_row_t *row;
        for (row = &subtest->expected[0]; row->local_relpath; row++)
          row->local_relpath = svn_dirent_join(subtest->to_path,
                                               row->local_relpath, b->pool);
      }

    /* Perform each subtest in turn. */
    for (subtest = subtests; subtest->from_path; subtest++)
      {
        SVN_ERR(sbox_wc_copy(b, subtest->from_path, subtest->to_path));
        SVN_ERR(check_db_rows(b, subtest->to_path, subtest->expected));
      }
  }

  return SVN_NO_ERROR;
}

/* Check that all kinds of repo-to-WC copies give correct op_depth results:
 * create a Greek tree, make copies in it, and check the resulting DB rows. */
static svn_error_t *
repo_wc_copies(svn_test__sandbox_t *b)
{
  SVN_ERR(sbox_add_and_commit_greek_tree(b));

  /* Delete some nodes so that we can test copying onto these paths */

  SVN_ERR(sbox_wc_delete(b, "A/B/lambda"));
  SVN_ERR(sbox_wc_delete(b, "A/D/gamma"));
  SVN_ERR(sbox_wc_delete(b, "A/D/G"));
  SVN_ERR(sbox_wc_delete(b, "A/D/H"));

  /* Test copying various things */

  {
    struct copy_subtest_t subtests[] =
      {
        /* file onto nothing */
        { "iota", "A/C/copy1", {
            { 3, "",                "normal",       1, "iota" },
          } },

        /* dir onto nothing */
        { "A/B/E", "A/C/copy2", {
            { 3, "",                "normal",       1, "A/B/E" },
            { 3, "alpha",           "normal",       1, "A/B/E/alpha" },
            { 3, "beta",            "normal",       1, "A/B/E/beta" },
          } },

        /* file onto a schedule-delete file */
        { "iota", "A/B/lambda", {
            { 0, "",                "normal",       1, "A/B/lambda" },
            { 3, "",                "normal",       1, "iota" },
          } },

        /* dir onto a schedule-delete dir */
        { "A/B/E", "A/D/G", {
            { 0, "",                "normal",       1, "A/D/G" },
            { 0, "pi",              "normal",       1, "A/D/G/pi" },
            { 0, "rho",             "normal",       1, "A/D/G/rho" },
            { 0, "tau",             "normal",       1, "A/D/G/tau" },
            { 3, "",                "normal",       1, "A/B/E" },
            { 3, "pi",              "base-deleted", NO_COPY_FROM },
            { 3, "rho",             "base-deleted", NO_COPY_FROM },
            { 3, "tau",             "base-deleted", NO_COPY_FROM },
            { 3, "alpha",           "normal",       1, "A/B/E/alpha" },
            { 3, "beta",            "normal",       1, "A/B/E/beta" },
          } },

        /* dir onto a schedule-delete file */
        { "A/B/E", "A/D/gamma", {
            { 0, "",                "normal",       1, "A/D/gamma" },
            { 3, "",                "normal",       1, "A/B/E" },
            { 3, "alpha",           "normal",       1, "A/B/E/alpha" },
            { 3, "beta",            "normal",       1, "A/B/E/beta" },
          } },

        /* file onto a schedule-delete dir */
        { "iota", "A/D/H", {
            { 0, "",                "normal",       1, "A/D/H" },
            { 0, "chi",             "normal",       1, "A/D/H/chi" },
            { 0, "psi",             "normal",       1, "A/D/H/psi" },
            { 0, "omega",           "normal",       1, "A/D/H/omega" },
            { 3, "",                "normal",       1, "iota" },
            { 3, "chi",             "base-deleted", NO_COPY_FROM },
            { 3, "psi",             "base-deleted", NO_COPY_FROM },
            { 3, "omega",           "base-deleted", NO_COPY_FROM },
          } },

        { 0 }
      };
    struct copy_subtest_t *subtest;
    svn_client_ctx_t *ctx;

    /* Fix up the expected->local_relpath fields in the subtest data to be
     * relative to the WC root rather than to the copy destination dir. */
    for (subtest = subtests; subtest->from_path; subtest++)
      {
        nodes_row_t *row;
        for (row = &subtest->expected[0]; row->local_relpath; row++)
          row->local_relpath = svn_dirent_join(subtest->to_path,
                                               row->local_relpath, b->pool);
      }

    /* Perform each copy. */
    SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
    for (subtest = subtests; subtest->from_path; subtest++)
      {
        svn_opt_revision_t rev = { svn_opt_revision_number, { 1 } };
        svn_client_copy_source_t source;
        apr_array_header_t *sources
          = apr_array_make(b->pool, 0, sizeof(svn_client_copy_source_t *));

        source.path = svn_path_url_add_component2(b->repos_url,
                                                  subtest->from_path,
                                                  b->pool);
        source.revision = &rev;
        source.peg_revision = &rev;
        APR_ARRAY_PUSH(sources, svn_client_copy_source_t *) = &source;
        SVN_ERR(svn_client_copy6(sources,
                                 sbox_wc_path(b, subtest->to_path),
                                 FALSE, FALSE, FALSE,
                                 NULL, NULL, NULL, ctx, b->pool));
      }

    /* Check each result. */
    for (subtest = subtests; subtest->from_path; subtest++)
      {
        SVN_ERR(check_db_rows(b, subtest->to_path, subtest->expected));
      }
  }

  return SVN_NO_ERROR;
}


static svn_error_t *
test_wc_wc_copies(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "wc_wc_copies", opts, pool));

  return wc_wc_copies(&b);
}

static svn_error_t *
test_reverts(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;
  nodes_row_t no_node_rows_expected[] = { { 0 } };

  SVN_ERR(svn_test__sandbox_create(&b, "reverts", opts, pool));

  SVN_ERR(wc_wc_copies(&b));


    /* Implement revert tests below, now that we have a wc with lots of
     copy-changes */

  SVN_ERR(sbox_wc_revert(&b, "A/B/D-added", svn_depth_infinity));
  SVN_ERR(check_db_rows(&b, "A/B/D-added", no_node_rows_expected));

  return SVN_NO_ERROR;
}

static svn_error_t *
test_deletes(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "deletes", opts, pool));
  SVN_ERR(sbox_add_and_commit_greek_tree(&b));

  SVN_ERR(sbox_file_write(&b,     "A/B/E/new-file", "New file"));
  SVN_ERR(sbox_wc_add(&b, "A/B/E/new-file"));
  {
    nodes_row_t rows[] = {
      { 4, "A/B/E/new-file", "normal", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A/B/E/new-file", rows));
  }

  SVN_ERR(sbox_wc_delete(&b, "A/B/E/alpha"));
  {
    nodes_row_t rows[] = {
      { 0, "A/B/E/alpha", "normal",       1, "A/B/E/alpha" },
      { 4, "A/B/E/alpha", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A/B/E/alpha", rows));
  }

  SVN_ERR(sbox_wc_delete(&b, "A/B/F"));
  {
    nodes_row_t rows[] = {
      { 0, "A/B/F", "normal",       1, "A/B/F" },
      { 3, "A/B/F", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A/B/F", rows));
  }

  SVN_ERR(sbox_wc_delete(&b, "A/B"));
  {
    nodes_row_t rows[] = {
      { 0, "A/B",         "normal",       1, "A/B",        },
      { 2, "A/B/lambda",  "base-deleted", NO_COPY_FROM },
      { 0, "A/B/lambda",  "normal",       1, "A/B/lambda", },
      { 2, "A/B",         "base-deleted", NO_COPY_FROM },
      { 0, "A/B/E",       "normal",       1, "A/B/E",      },
      { 2, "A/B/E",       "base-deleted", NO_COPY_FROM },
      { 0, "A/B/E/alpha", "normal",       1, "A/B/E/alpha" },
      { 2, "A/B/E/alpha", "base-deleted", NO_COPY_FROM },
      { 0, "A/B/E/beta",  "normal",       1, "A/B/E/beta" },
      { 2, "A/B/E/beta",  "base-deleted", NO_COPY_FROM },
      { 0, "A/B/F",       "normal",       1, "A/B/F",      },
      { 2, "A/B/F",       "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A/B", rows));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
test_adds(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "adds", opts, pool));
  SVN_ERR(sbox_add_and_commit_greek_tree(&b));

  /* add file */
  SVN_ERR(sbox_file_write(&b, "new-file", "New file"));
  SVN_ERR(sbox_wc_add(&b, "new-file"));
  {
    nodes_row_t rows[] = {
      { 1, "new-file",    "normal",       NO_COPY_FROM     },
      { 0 } };
    SVN_ERR(check_db_rows(&b, "new-file", rows));
  }

  /* add dir */
  SVN_ERR(sbox_wc_mkdir(&b, "new-dir"));
  SVN_ERR(sbox_wc_mkdir(&b, "new-dir/D2"));
  {
    nodes_row_t rows[] = {
      { 1, "new-dir",     "normal",       NO_COPY_FROM     },
      { 2, "new-dir/D2",  "normal",       NO_COPY_FROM     },
      { 0 } };
    SVN_ERR(check_db_rows(&b, "new-dir", rows));
  }

  /* replace file */
  SVN_ERR(sbox_wc_delete(&b, "iota"));
  SVN_ERR(sbox_file_write(&b, "iota", "New iota file"));
  SVN_ERR(sbox_wc_add(&b, "iota"));
  {
    nodes_row_t rows[] = {
      { 0, "iota",        "normal",       1, "iota"        },
      { 1, "iota",        "normal",       NO_COPY_FROM     },
      { 0 } };
    SVN_ERR(check_db_rows(&b, "iota", rows));
  }

  /* replace dir */
  SVN_ERR(sbox_wc_delete(&b, "A/B/E"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/E"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/E/D2"));
  {
    nodes_row_t rows[] = {
      { 0, "A/B/E",       "normal",       1, "A/B/E"       },
      { 0, "A/B/E/alpha", "normal",       1, "A/B/E/alpha" },
      { 0, "A/B/E/beta",  "normal",       1, "A/B/E/beta"  },
      { 3, "A/B/E",       "normal",       NO_COPY_FROM     },
      { 4, "A/B/E/D2",    "normal",       NO_COPY_FROM     },
      { 3, "A/B/E/alpha", "base-deleted", NO_COPY_FROM     },
      { 3, "A/B/E/beta",  "base-deleted", NO_COPY_FROM     },
      { 0 } };
    SVN_ERR(check_db_rows(&b, "A/B/E", rows));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
test_adds_change_kind(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "test_adds_change_kind", opts, pool));
  SVN_ERR(sbox_add_and_commit_greek_tree(&b));

  /* replace dir with file */
  SVN_ERR(sbox_wc_delete(&b, "A/B/E"));
  SVN_ERR(sbox_file_write(&b, "A/B/E", "New E file"));
  SVN_ERR(sbox_wc_add(&b, "A/B/E"));
  {
    nodes_row_t rows[] = {
      { 0, "A/B/E",       "normal",       1, "A/B/E"       },
      { 0, "A/B/E/alpha", "normal",       1, "A/B/E/alpha" },
      { 0, "A/B/E/beta",  "normal",       1, "A/B/E/beta"  },
      { 3, "A/B/E",       "normal",       NO_COPY_FROM     },
      { 3, "A/B/E/alpha", "base-deleted", NO_COPY_FROM     },
      { 3, "A/B/E/beta",  "base-deleted", NO_COPY_FROM     },
      { 0 } };
    SVN_ERR(check_db_rows(&b, "A/B/E", rows));
  }

  /* replace file with dir */
  SVN_ERR(sbox_wc_delete(&b, "iota"));
  SVN_ERR(sbox_wc_mkdir(&b, "iota"));
  SVN_ERR(sbox_wc_mkdir(&b, "iota/D2"));
  {
    nodes_row_t rows[] = {
      { 0, "iota",        "normal",       1, "iota"        },
      { 1, "iota",        "normal",       NO_COPY_FROM     },
      { 2, "iota/D2",     "normal",       NO_COPY_FROM     },
      { 0 } };
    SVN_ERR(check_db_rows(&b, "iota", rows));
  }

  return SVN_NO_ERROR;
}


static svn_error_t *
test_delete_of_copies(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "deletes_of_copies", opts, pool));
  SVN_ERR(sbox_add_and_commit_greek_tree(&b));
  SVN_ERR(sbox_wc_copy(&b, "A/B", "A/B-copied"));

  SVN_ERR(sbox_wc_delete(&b, "A/B-copied/E"));
  {
    nodes_row_t rows[] = {
      { 2, "A/B-copied/E",       "normal",       1, "A/B/E" },
      { 2, "A/B-copied/E/alpha", "normal",       1, "A/B/E/alpha" },
      { 2, "A/B-copied/E/beta",  "normal",       1, "A/B/E/beta" },
      { 3, "A/B-copied/E",       "base-deleted", NO_COPY_FROM },
      { 3, "A/B-copied/E/alpha", "base-deleted", NO_COPY_FROM },
      { 3, "A/B-copied/E/beta",  "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A/B-copied/E", rows));
  }

  SVN_ERR(sbox_wc_copy(&b, "A/D/G", "A/B-copied/E"));
  {
    nodes_row_t rows[] = {
      { 2, "A/B-copied/E",       "normal",       1, "A/B/E" },
      { 2, "A/B-copied/E/alpha", "normal",       1, "A/B/E/alpha" },
      { 2, "A/B-copied/E/beta",  "normal",       1, "A/B/E/beta" },
      { 3, "A/B-copied/E",       "normal",       1, "A/D/G" },
      { 3, "A/B-copied/E/alpha", "base-deleted", NO_COPY_FROM },
      { 3, "A/B-copied/E/beta",  "base-deleted", NO_COPY_FROM },
      { 3, "A/B-copied/E/pi",    "normal",       1, "A/D/G/pi" },
      { 3, "A/B-copied/E/rho",   "normal",       1, "A/D/G/rho" },
      { 3, "A/B-copied/E/tau",   "normal",       1, "A/D/G/tau" },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A/B-copied/E", rows));
  }

  SVN_ERR(sbox_wc_delete(&b, "A/B-copied/E/rho"));
  {
    nodes_row_t rows[] = {
      { 2, "A/B-copied/E",       "normal",       1, "A/B/E" },
      { 2, "A/B-copied/E/alpha", "normal",       1, "A/B/E/alpha" },
      { 2, "A/B-copied/E/beta",  "normal",       1, "A/B/E/beta" },
      { 3, "A/B-copied/E",       "normal",       1, "A/D/G" },
      { 3, "A/B-copied/E/alpha", "base-deleted", NO_COPY_FROM },
      { 3, "A/B-copied/E/beta",  "base-deleted", NO_COPY_FROM },
      { 3, "A/B-copied/E/pi",    "normal",       1, "A/D/G/pi" },
      { 3, "A/B-copied/E/rho",   "normal",       1, "A/D/G/rho" },
      { 3, "A/B-copied/E/tau",   "normal",       1, "A/D/G/tau" },
      { 4, "A/B-copied/E/rho",   "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A/B-copied/E", rows));
  }

  SVN_ERR(sbox_wc_delete(&b, "A/B-copied/E"));
  {
    nodes_row_t rows[] = {
      { 2, "A/B-copied/E",       "normal",       1, "A/B/E" },
      { 2, "A/B-copied/E/alpha", "normal",       1, "A/B/E/alpha" },
      { 2, "A/B-copied/E/beta",  "normal",       1, "A/B/E/beta" },
      { 3, "A/B-copied/E",       "base-deleted", NO_COPY_FROM },
      { 3, "A/B-copied/E/alpha", "base-deleted", NO_COPY_FROM },
      { 3, "A/B-copied/E/beta",  "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A/B-copied/E", rows));
  }

  SVN_ERR(sbox_wc_copy(&b, "A/B", "A/B-copied/E"));

  SVN_ERR(sbox_wc_delete(&b, "A/B-copied/E/F"));
  {
    nodes_row_t rows[] = {
      { 3, "A/B-copied/E/F", "normal",       1, "A/B/F" },
      { 4, "A/B-copied/E/F", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A/B-copied/E/F", rows));
  }

  SVN_ERR(sbox_wc_delete(&b, "A/B-copied"));
  {
    nodes_row_t rows[] = { { 0 } };
    SVN_ERR(check_db_rows(&b, "A/B-copied", rows));
  }

  return SVN_NO_ERROR;
}


static svn_error_t *
test_delete_with_base(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "deletes_with_base", opts, pool));
  SVN_ERR(sbox_add_and_commit_greek_tree(&b));
  SVN_ERR(sbox_wc_delete(&b, "A/B/E/beta"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_delete(&b, "A/B/E"));
  {
    nodes_row_t rows[] = {
      { 0, "A/B/E",       "normal",        1, "A/B/E"},
      { 0, "A/B/E/alpha", "normal",        1, "A/B/E/alpha"},
      { 0, "A/B/E/beta",  "not-present",   2, "A/B/E/beta"},
      { 3, "A/B/E",       "base-deleted",  NO_COPY_FROM},
      { 3, "A/B/E/alpha", "base-deleted",  NO_COPY_FROM},
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A/B/E", rows));
  }

  SVN_ERR(sbox_wc_copy(&b, "A/B/F", "A/B/E"));
  SVN_ERR(sbox_wc_copy(&b, "A/mu",  "A/B/E/alpha"));
  SVN_ERR(sbox_wc_copy(&b, "A/mu",  "A/B/E/beta"));
  {
    nodes_row_t rows[] = {
      { 0, "A/B/E",       "normal",        1, "A/B/E"},
      { 0, "A/B/E/alpha", "normal",        1, "A/B/E/alpha"},
      { 0, "A/B/E/beta",  "not-present",   2, "A/B/E/beta"},
      { 3, "A/B/E",       "base-deleted",  NO_COPY_FROM},
      { 3, "A/B/E/alpha", "base-deleted",  NO_COPY_FROM},
      { 3, "A/B/E",       "normal",        1, "A/B/F"},
      { 4, "A/B/E/alpha", "normal",        1, "A/mu"},
      { 4, "A/B/E/beta",  "normal",        1, "A/mu"},
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A/B/E", rows));
  }

  SVN_ERR(sbox_wc_delete(&b, "A/B/E"));
  {
    nodes_row_t rows[] = {
      { 0, "A/B/E",       "normal",        1, "A/B/E"},
      { 0, "A/B/E/alpha", "normal",        1, "A/B/E/alpha"},
      { 0, "A/B/E/beta",  "not-present",   2, "A/B/E/beta"},
      { 3, "A/B/E",       "base-deleted",  NO_COPY_FROM},
      { 3, "A/B/E/alpha", "base-deleted",  NO_COPY_FROM},
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A/B/E", rows));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
test_repo_wc_copies(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "repo_wc_copies", opts, pool));

  return repo_wc_copies(&b);
}

static svn_error_t *
test_delete_with_update(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "delete_with_update", opts, pool));
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_delete(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  {
    nodes_row_t rows[] = {
      { 0, "A",       "normal",        1, "A"},
      { 1, "A",       "normal",        NO_COPY_FROM},
      { 2, "A/B",     "normal",        NO_COPY_FROM},
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A", rows));
  }
  SVN_ERR(sbox_wc_update(&b, "", 2));
  {
    nodes_row_t rows[] = {
      { 0, "A",       "normal",        2, "A"},
      { 0, "A/B",     "normal",        2, "A/B"},
      { 0, "A/B/C",   "normal",        2, "A/B/C"},
      { 1, "A",       "normal",        NO_COPY_FROM},
      { 1, "A/B",     "base-deleted",  NO_COPY_FROM},
      { 1, "A/B/C",   "base-deleted",  NO_COPY_FROM},
      { 2, "A/B",     "normal",        NO_COPY_FROM},
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A", rows));
  }
  SVN_ERR(sbox_wc_resolved(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  {
    nodes_row_t rows[] = {
      { 0, "A",       "normal",        1, "A"},
      { 1, "A",       "normal",        NO_COPY_FROM},
      { 2, "A/B",     "normal",        NO_COPY_FROM},
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A", rows));
  }

  return SVN_NO_ERROR;
}


static svn_error_t *
insert_dirs(svn_test__sandbox_t *b,
            nodes_row_t *nodes)
{
  svn_sqlite__db_t *sdb;
  svn_sqlite__stmt_t *stmt;

  SVN_ERR(open_wc_db(&sdb, b->wc_abspath, b->pool, b->pool));

  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_DELETE_NODES));
  SVN_ERR(svn_sqlite__step_done(stmt));

  while(nodes->local_relpath)
    {
      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
      SVN_ERR(svn_sqlite__bindf(stmt, "sdssrs",
                                nodes->local_relpath,
                                nodes->op_depth,
                                nodes->presence,
                                nodes->repo_relpath,
                                nodes->repo_revnum,
                                nodes->local_relpath[0]
                                  ? svn_relpath_dirname(nodes->local_relpath,
                                                        b->pool)
                                  : NULL));

      if (nodes->moved_to)
        SVN_ERR(svn_sqlite__bind_text(stmt, 7, nodes->moved_to));
      if (nodes->moved_here)
        SVN_ERR(svn_sqlite__bind_int(stmt, 8, 1));
      if (nodes->props)
        {
          int i;
          apr_hash_t *props = apr_hash_make(b->pool);
          apr_array_header_t *names = svn_cstring_split(nodes->props, ",",
                                                        TRUE, b->pool);

          for (i = 0; i < names->nelts; i++)
            {
              const char *name = APR_ARRAY_IDX(names, i, const char *);
              svn_hash_sets(props, name, svn_string_create(name, b->pool));
            }

          SVN_ERR(svn_sqlite__bind_properties(stmt, 9, props, b->pool));
        }
      else if (nodes->repo_relpath
               && strcmp(nodes->presence, "normal") == 0)
        {
          SVN_ERR(svn_sqlite__bind_text(stmt, 9, "()"));
        }

      /* File externals? */

      SVN_ERR(svn_sqlite__step_done(stmt));
      ++nodes;
    }

  SVN_ERR(svn_sqlite__close(sdb));

  return SVN_NO_ERROR;
}

static apr_int64_t count_rows(nodes_row_t *rows)
{
  nodes_row_t *first = rows;
  while(rows->local_relpath)
    ++rows;
  return rows - first;
}

static svn_error_t *
base_dir_insert_remove(svn_test__sandbox_t *b,
                       const char *local_relpath,
                       svn_revnum_t revision,
                       nodes_row_t *before,
                       nodes_row_t *added)
{
  nodes_row_t *after;
  const char *dir_abspath = sbox_wc_path(b, local_relpath);
  int i;
  apr_int64_t num_before = count_rows(before), num_added = count_rows(added);

  SVN_ERR(insert_dirs(b, before));

  SVN_ERR(svn_wc__db_base_add_directory(b->wc_ctx->db, dir_abspath,
                                        dir_abspath,
                                        local_relpath, b->repos_url,
                                        "not-even-a-uuid", revision,
                                        apr_hash_make(b->pool), revision,
                                        0, NULL, NULL, svn_depth_infinity,
                                        NULL, FALSE, NULL, NULL, NULL, NULL,
                                        b->pool));

  after = apr_palloc(b->pool, sizeof(*after) * (apr_size_t)(num_before + num_added + 1));
  for (i = 0; i < num_before; ++i)
    after[i] = before[i];
  for (i = 0; i < num_added; ++i)
    after[num_before+i] = added[i];
  after[num_before+num_added].local_relpath = NULL;

  SVN_ERR(check_db_rows(b, "", after));

  SVN_ERR(svn_wc__db_base_remove(b->wc_ctx->db, dir_abspath,
                                 FALSE, FALSE, FALSE,
                                 SVN_INVALID_REVNUM,
                                 NULL, NULL, b->pool));
  SVN_ERR(svn_wc__wq_run(b->wc_ctx->db, dir_abspath,
                         NULL, NULL, b->pool));

  SVN_ERR(check_db_rows(b, "", before));

  return SVN_NO_ERROR;
}

static svn_error_t *
test_base_dir_insert_remove(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "base_dir_insert_remove", opts, pool));

  {
    /* /  normal                     /    normal
       A  normal                     A    normal
                                     A/B  normal
    */
    nodes_row_t before[] = {
      { 0, "",  "normal", 2, "" },
      { 0, "A", "normal", 2, "A" },
      { 0 }
    };
    nodes_row_t added[] = {
      { 0, "A/B", "normal", 2, "A/B" },
      { 0 }
    };
    SVN_ERR(base_dir_insert_remove(&b, "A/B", 2, before, added));
  }
  {
    /* /  normal                      /    normal
       A  normal  base-del            A    normal  base-del
                                      A/B  normal  base-del
    */
    nodes_row_t before[] = {
      { 0, "",  "normal",       2, "" },
      { 0, "A", "normal",       2, "A" },
      { 1, "A", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    nodes_row_t added[] = {
      { 0, "A/B", "normal",       2, "A/B" },
      { 1, "A/B", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(base_dir_insert_remove(&b, "A/B", 2, before, added));
  }
  {
    /* /  normal                       /    normal
       A  normal  normal               A    normal  normal
                                       A/B  normal  base-del
     */
    nodes_row_t before[] = {
      { 0, "",  "normal", 2, "" },
      { 0, "A", "normal", 2, "A" },
      { 1, "A", "normal", 1, "X" },
      { 0 }
    };
    nodes_row_t added[] = {
      { 0, "A/B", "normal",       2, "A/B" },
      { 1, "A/B", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(base_dir_insert_remove(&b, "A/B", 2, before, added));
  }
  {
    /* /    normal                     /      normal
       A    normal  normal             A      normal  normal
       A/B  normal  not-pres           A/B    normal  not-pres
                                       A/B/C  normal  base-del
     */
    nodes_row_t before[] = {
      { 0, "",    "normal",      2, "" },
      { 0, "A",   "normal",      2, "A" },
      { 0, "A/B", "normal",      2, "A/B" },
      { 1, "A",   "normal",      1, "X" },
      { 1, "A/B", "not-present", NO_COPY_FROM },
      { 0 }
    };
    nodes_row_t added[] = {
      { 0, "A/B/C", "normal",       2, "A/B/C" },
      { 1, "A/B/C", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(base_dir_insert_remove(&b, "A/B/C", 2, before, added));
  }
  {
    /* /    normal                      /    normal
       A    normal  normal              A    normal  normal
       A/B          normal              A/B  normal  normal
     */
    nodes_row_t before[] = {
      { 0, "",    "normal", 2, "" },
      { 0, "A",   "normal", 2, "A" },
      { 1, "A",   "normal", 1, "X" },
      { 1, "A/B", "normal", NO_COPY_FROM },
      { 0 }
    };
    nodes_row_t added[] = {
      { 0, "A/B", "normal",       2, "A/B" },
      { 0 }
    };
    SVN_ERR(base_dir_insert_remove(&b, "A/B", 2, before, added));
  }
  {
    /* /    normal                       /    normal
       A    normal  normal               A    normal  normal
       A/B          not-pres             A/B  normal  not-pres
     */
    nodes_row_t before[] = {
      { 0, "",    "normal",      2, "" },
      { 0, "A",   "normal",      2, "A" },
      { 1, "A",   "normal",      1, "X" },
      { 1, "A/B", "not-present", NO_COPY_FROM },
      { 0 }
    };
    nodes_row_t added[] = {
      { 0, "A/B", "normal",       2, "A/B" },
      { 0 }
    };
    SVN_ERR(base_dir_insert_remove(&b, "A/B", 2, before, added));
  }
  {
    /* /    normal                       /    normal
       A    normal  normal               A    normal  normal
       A/B                  normal       A/B  normal  base-del  normal
     */
    nodes_row_t before[] = {
      { 0, "",    "normal",      2, "" },
      { 0, "A",   "normal",      2, "A" },
      { 1, "A",   "normal",      1, "X" },
      { 2, "A/B", "normal",      1, "Y" },
      { 0 }
    };
    nodes_row_t added[] = {
      { 0, "A/B", "normal",       2, "A/B" },
      { 1, "A/B", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(base_dir_insert_remove(&b, "A/B", 2, before, added));
  }
  {
    /* /      normal                          /      normal
       A      normal  normal                  A      normal  normal
       A/B    normal  base-del  normal        A/B    normal  base-del  normal
       A/B/C                    normal        A/B/C  normal  base-del  normal
     */
    nodes_row_t before[] = {
      { 0, "",    "normal",       2, "" },
      { 0, "A",   "normal",       2, "A" },
      { 0, "A/B", "normal",       2, "A/B" },
      { 1, "A",   "normal",       1, "X" },
      { 1, "A/B", "base-deleted", NO_COPY_FROM },
      { 2, "A/B", "normal",       1, "Y" },
      { 0 }
    };
    nodes_row_t added[] = {
      { 0, "A/B/C", "normal",       2, "A/B/C" },
      { 1, "A/B/C", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(base_dir_insert_remove(&b, "A/B/C", 2, before, added));
  }
  {
    /* /      normal                          /      normal
       A      normal  normal                  A      normal  normal
       A/B    normal  not-pres  normal        A/B    normal  not-pres  normal
       A/B/C                    normal        A/B/C  normal  base-del  normal
     */
    nodes_row_t before[] = {
      { 0, "",      "normal",      2, "" },
      { 0, "A",     "normal",      2, "A" },
      { 0, "A/B",   "normal",      2, "A/B" },
      { 1, "A",     "normal",      1, "X" },
      { 1, "A/B",   "not-present", NO_COPY_FROM },
      { 2, "A/B",   "normal",      1, "Y" },
      { 2, "A/B/C", "normal",      NO_COPY_FROM },
      { 0 }
    };
    nodes_row_t added[] = {
      { 0, "A/B/C", "normal",       2, "A/B/C" },
      { 1, "A/B/C", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(base_dir_insert_remove(&b, "A/B/C", 2, before, added));
  }
  {
    /*  /      normal                         /
        A      normal  normal                 A      normal  normal
        A/B    normal  base-del  normal       A/B    normal  base-del  normal
        A/B/C                    not-pres     A/B/C  normal  base-del  not-pres
     */
    nodes_row_t before[] = {
      { 0, "",      "normal",       2, "" },
      { 0, "A",     "normal",       2, "A" },
      { 0, "A/B",   "normal",       2, "A/B" },
      { 1, "A",     "normal",       1, "X" },
      { 1, "A/B",   "base-deleted", NO_COPY_FROM },
      { 2, "A/B",   "normal",       1, "Y" },
      { 2, "A/B/C", "not-present",  NO_COPY_FROM },
      { 0 }
    };
    nodes_row_t added[] = {
      { 0, "A/B/C", "normal",       2, "A/B/C" },
      { 1, "A/B/C", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(base_dir_insert_remove(&b, "A/B/C", 2, before, added));
  }
  {
    /*  /      normal                         /
        A      normal  normal                 A      normal  normal
        A/B    normal  not-pres  normal       A/B    normal  not-pres  normal
        A/B/C                    not-pres     A/B/C  normal  base-del  not-pres
     */
    nodes_row_t before[] = {
      { 0, "",      "normal",      2, "" },
      { 0, "A",     "normal",      2, "A" },
      { 0, "A/B",   "normal",      2, "A/B" },
      { 1, "A",     "normal",      1, "X" },
      { 1, "A/B",   "not-present", NO_COPY_FROM },
      { 2, "A/B",   "normal",      1, "Y" },
      { 2, "A/B/C", "not-present", NO_COPY_FROM },
      { 0 }
    };
    nodes_row_t added[] = {
      { 0, "A/B/C", "normal",       2, "A/B/C" },
      { 1, "A/B/C", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(base_dir_insert_remove(&b, "A/B/C", 2, before, added));
  }
  {
    /*  /      norm                       /
        A      norm  norm                 A      norm  norm
        A/B    norm  not-p  norm          A/B    norm  not-p  norm
        A/B/C                     norm    A/B/C  norm  b-del        norm
     */
    nodes_row_t before[] = {
      { 0, "",      "normal",      2, "" },
      { 0, "A",     "normal",      2, "A" },
      { 0, "A/B",   "normal",      2, "A/B" },
      { 1, "A",     "normal",      1, "X" },
      { 1, "A/B",   "not-present", NO_COPY_FROM },
      { 2, "A/B",   "normal",      1, "Y" },
      { 3, "A/B/C", "normal",      NO_COPY_FROM },
      { 0 }
    };
    nodes_row_t added[] = {
      { 0, "A/B/C", "normal",       2, "A/B/C" },
      { 1, "A/B/C", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(base_dir_insert_remove(&b, "A/B/C", 2, before, added));
  }
  {
    /* /      norm                     /        norm
       A      norm                     A        norm
       A/B    norm                     A/B      norm
       A/B/C  norm  -  -  norm         A/B/C    norm   -  -  norm
                                       A/B/C/D  norm   -  -  b-del
    */
    nodes_row_t before[] = {
      { 0, "",      "normal", 2, "" },
      { 0, "A",     "normal", 2, "A" },
      { 0, "A/B",   "normal", 2, "A/B" },
      { 0, "A/B/C", "normal", 2, "A/B/C" },
      { 3, "A/B/C", "normal", NO_COPY_FROM },
      { 0 }
    };
    nodes_row_t added[] = {
      { 0, "A/B/C/D", "normal",       2, "A/B/C/D" },
      { 3, "A/B/C/D", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(base_dir_insert_remove(&b, "A/B/C/D", 2, before, added));
  }
  {
    /* /      norm                     /        norm
       A      norm                     A        norm
       A/B    norm                     A/B      norm
       A/B/C  norm  -  -  norm         A/B/C    norm   -  -  norm
       A/B/C/D                  norm   A/B/C/D  norm   -  -  b-del  norm
    */
    nodes_row_t before[] = {
      { 0, "",        "normal", 2, "" },
      { 0, "A",       "normal", 2, "A" },
      { 0, "A/B",     "normal", 2, "A/B" },
      { 0, "A/B/C",   "normal", 2, "A/B/C" },
      { 3, "A/B/C",   "normal", NO_COPY_FROM },
      { 4, "A/B/C/D", "normal", NO_COPY_FROM },
      { 0 }
    };
    nodes_row_t added[] = {
      { 0, "A/B/C/D", "normal",       2, "A/B/C/D" },
      { 3, "A/B/C/D", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(base_dir_insert_remove(&b, "A/B/C/D", 2, before, added));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
test_db_make_copy(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "make_copy", opts, pool));

  {
    /*  /           norm        -
        A           norm        -
        A/B         norm        -       norm
        A/B/C       norm        -       base-del    norm
        A/F         norm        -       norm
        A/F/G       norm        -       norm
        A/F/H       norm        -       not-pres
        A/F/E       norm        -       base-del
        A/X         norm        -
        A/X/Y       incomplete  -
    */
    nodes_row_t before[] = {
      { 0, "",      "normal",       2, "" },
      { 0, "A",     "normal",       2, "A" },
      { 0, "A/B",   "normal",       2, "A/B" },
      { 0, "A/B/C", "normal",       2, "A/B/C" },
      { 0, "A/F",   "normal",       2, "A/F" },
      { 0, "A/F/G", "normal",       2, "A/F/G" },
      { 0, "A/F/H", "normal",       2, "A/F/H" },
      { 0, "A/F/E", "normal",       2, "A/F/E" },
      { 0, "A/X",   "normal",       2, "A/X" },
      { 0, "A/X/Y", "incomplete",   2, "A/X/Y" },
      { 2, "A/B",   "normal",       NO_COPY_FROM },
      { 2, "A/B/C", "base-deleted", NO_COPY_FROM },
      { 3, "A/B/C", "normal",       NO_COPY_FROM },
      { 2, "A/F",   "normal",       1, "S2" },
      { 2, "A/F/G", "normal",       1, "S2/G" },
      { 2, "A/F/H", "not-present",  1, "S2/H" },
      { 2, "A/F/E", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    /*  /           norm        -
        A           norm        norm
        A/B         norm        norm        norm
        A/B/C       norm        norm        base-del    norm
        A/F         norm        norm        norm
        A/F/G       norm        norm        norm
        A/F/H       norm        norm        not-pres
        A/F/E       norm        norm        base-del
        A/X         norm        norm
        A/X/Y       incomplete  incomplete
    */
    nodes_row_t after[] = {
      { 0, "",      "normal",       2, "" },
      { 0, "A",     "normal",       2, "A" },
      { 0, "A/B",   "normal",       2, "A/B" },
      { 0, "A/B/C", "normal",       2, "A/B/C" },
      { 0, "A/F",   "normal",       2, "A/F" },
      { 0, "A/F/G", "normal",       2, "A/F/G" },
      { 0, "A/F/H", "normal",       2, "A/F/H" },
      { 0, "A/F/E", "normal",       2, "A/F/E" },
      { 0, "A/X",   "normal",       2, "A/X" },
      { 0, "A/X/Y", "incomplete",   2, "A/X/Y" },
      { 1, "A",     "normal",       2, "A" },
      { 1, "A/B",   "normal",       2, "A/B" },
      { 1, "A/B/C", "normal",       2, "A/B/C" },
      { 1, "A/F",   "normal",       2, "A/F" },
      { 1, "A/F/G", "normal",       2, "A/F/G" },
      { 1, "A/F/H", "normal",       2, "A/F/H" },
      { 1, "A/F/E", "normal",       2, "A/F/E" },
      { 1, "A/X",   "normal",       2, "A/X" },
      { 1, "A/X/Y", "incomplete",   2, "A/X/Y" },
      { 2, "A/B",   "normal",       NO_COPY_FROM },
      { 2, "A/B",   "normal",       NO_COPY_FROM },
      { 2, "A/B/C", "base-deleted", NO_COPY_FROM },
      { 2, "A/F",   "normal",       1, "S2" },
      { 2, "A/F/E", "base-deleted", NO_COPY_FROM },
      { 2, "A/F/G", "normal",       1, "S2/G" },
      { 2, "A/F/H", "not-present",  1, "S2/H" },
      { 3, "A/B/C", "normal",       NO_COPY_FROM },
      { 0 }
    };

    SVN_ERR(insert_dirs(&b, before));
    SVN_ERR(svn_wc__db_op_make_copy(b.wc_ctx->db, sbox_wc_path(&b, "A"),
                                    NULL, NULL, pool));

    SVN_ERR(check_db_rows(&b, "", after));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
test_wc_move(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "wc_move", opts, pool));
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A/B/C", "A/B/C-move"));
  {
    nodes_row_t rows[] = {
      { 0, "",           "normal",       1, "" },
      { 0, "A",          "normal",       1, "A" },
      { 0, "A/B",        "normal",       1, "A/B" },
      { 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 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  SVN_ERR(sbox_wc_move(&b, "A/B", "A/B-move"));
  {
    nodes_row_t rows[] = {
      { 0, "",                "normal",       1, "" },
      { 0, "A",               "normal",       1, "A" },
      { 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, "A/B-move/C-move" },
      { 3, "A/B-move/C-move", "normal",       1, "A/B/C", MOVED_HERE },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
test_mixed_rev_copy(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "mixed_rev_copy", opts, pool));
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_copy(&b, "A", "X"));
  {
    nodes_row_t rows[] = {
      { 1, "X",     "normal",       1, "A" },
      { 1, "X/B",   "not-present",  2, "A/B" },
      { 2, "X/B",   "normal",       2, "A/B" },
      { 2, "X/B/C", "not-present",  3, "A/B/C" },
      { 3, "X/B/C", "normal",       3, "A/B/C" },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "X", rows));
  }

  SVN_ERR(sbox_wc_copy(&b, "A/B", "X/Y"));
  {
    nodes_row_t rows[] = {
      { 1, "X",     "normal",       1, "A" },
      { 1, "X/B",   "not-present",  2, "A/B" },
      { 2, "X/B",   "normal",       2, "A/B" },
      { 2, "X/B/C", "not-present",  3, "A/B/C" },
      { 3, "X/B/C", "normal",       3, "A/B/C" },
      { 2, "X/Y",   "normal",       2, "A/B" },
      { 2, "X/Y/C", "not-present",  3, "A/B/C" },
      { 3, "X/Y/C", "normal",       3, "A/B/C" },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "X", rows));
  }

  SVN_ERR(sbox_wc_delete(&b, "X/B/C"));
  {
    nodes_row_t rows[] = {
      { 1, "X",     "normal",       1, "A" },
      { 1, "X/B",   "not-present",  2, "A/B" },
      { 2, "X/B",   "normal",       2, "A/B" },
      { 2, "X/B/C", "not-present",  3, "A/B/C" },
      { 2, "X/Y",   "normal",       2, "A/B" },
      { 2, "X/Y/C", "not-present",  3, "A/B/C" },
      { 3, "X/Y/C", "normal",       3, "A/B/C" },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "X", rows));
  }

  SVN_ERR(sbox_wc_delete(&b, "X"));
  SVN_ERR(sbox_wc_update(&b, "A/B/C", 0));
  {
    nodes_row_t rows[] = {
      { 0, "",      "normal",       0, "" },
      { 0, "A",     "normal",       1, "A" },
      { 0, "A/B",   "normal",       2, "A/B" },
      { 0, "A/B/C", "not-present",  0, "A/B/C" },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  SVN_ERR(sbox_wc_copy(&b, "A", "X"));
  {
    nodes_row_t rows[] = {
      { 1, "X",     "normal",       1, "A" },
      { 1, "X/B",   "not-present",  2, "A/B" },
      { 2, "X/B",   "normal",       2, "A/B" },
      { 2, "X/B/C", "not-present",  0, "A/B/C" },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "X", rows));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
test_delete_of_replace(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "delete_of_replace", opts, pool));
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/F"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/F/K"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/G"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/G/K"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_copy(&b, "A", "X"));
  SVN_ERR(sbox_wc_move(&b, "X/B/C/F", "X/B/C/H"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 2));

  SVN_ERR(sbox_wc_delete(&b, "A/B"));
  SVN_ERR(sbox_wc_copy(&b, "X/B", "A/B"));
  {
    nodes_row_t rows[] = {
      { 0, "A",         "normal",       2, "A" },
      { 0, "A/B",       "normal",       2, "A/B" },
      { 0, "A/B/C",     "normal",       2, "A/B/C" },
      { 0, "A/B/C/F",   "normal",       2, "A/B/C/F" },
      { 0, "A/B/C/F/K", "normal",       2, "A/B/C/F/K" },
      { 0, "A/B/C/G",   "normal",       2, "A/B/C/G" },
      { 0, "A/B/C/G/K", "normal",       2, "A/B/C/G/K" },
      { 2, "A/B",       "normal",       2, "X/B" },
      { 2, "A/B/C",     "normal",       2, "X/B/C" },
      { 2, "A/B/C/F",   "base-deleted", NO_COPY_FROM },
      { 2, "A/B/C/F/K", "base-deleted", NO_COPY_FROM },
      { 2, "A/B/C/G",   "normal",       2, "X/B/C/G" },
      { 2, "A/B/C/G/K", "normal",       2, "X/B/C/G/K" },
      { 2, "A/B/C/H",   "normal",       2, "X/B/C/H" },
      { 2, "A/B/C/H/K", "normal",       2, "X/B/C/H/K" },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A", rows));
  }

  SVN_ERR(sbox_wc_delete(&b, "A/B"));
  {
    nodes_row_t rows[] = {
      { 0, "A",         "normal",       2, "A" },
      { 0, "A/B",       "normal",       2, "A/B" },
      { 0, "A/B/C",     "normal",       2, "A/B/C" },
      { 0, "A/B/C/F",   "normal",       2, "A/B/C/F" },
      { 0, "A/B/C/F/K", "normal",       2, "A/B/C/F/K" },
      { 0, "A/B/C/G",   "normal",       2, "A/B/C/G" },
      { 0, "A/B/C/G/K", "normal",       2, "A/B/C/G/K" },
      { 2, "A/B",       "base-deleted", NO_COPY_FROM },
      { 2, "A/B/C",     "base-deleted", NO_COPY_FROM },
      { 2, "A/B/C/F",   "base-deleted", NO_COPY_FROM },
      { 2, "A/B/C/F/K", "base-deleted", NO_COPY_FROM },
      { 2, "A/B/C/G",   "base-deleted", NO_COPY_FROM },
      { 2, "A/B/C/G/K", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A", rows));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
test_del_replace_not_present(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "del_replace_not_present", opts, pool));
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/X"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/Y"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/Z"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_copy(&b, "A", "X"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/B/W"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_update(&b, "", 2));
  SVN_ERR(sbox_wc_update(&b, "A/B/X", 0));
  SVN_ERR(sbox_wc_update(&b, "A/B/Y", 0));
  SVN_ERR(sbox_wc_update(&b, "X/B/W", 0));
  SVN_ERR(sbox_wc_update(&b, "X/B/Y", 0));
  SVN_ERR(sbox_wc_update(&b, "X/B/Z", 0));

  SVN_ERR(sbox_wc_delete(&b, "A"));
  {
    nodes_row_t rows[] = {
      { 0, "A",         "normal",       2, "A" },
      { 0, "A/B",       "normal",       2, "A/B" },
      { 0, "A/B/X",     "not-present",  0, "A/B/X" },
      { 0, "A/B/Y",     "not-present",  0, "A/B/Y" },
      { 0, "A/B/Z",     "normal",       2, "A/B/Z" },
      { 1, "A",         "base-deleted", NO_COPY_FROM },
      { 1, "A/B",       "base-deleted", NO_COPY_FROM },
      { 1, "A/B/Z",     "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A", rows));
  }

  SVN_ERR(sbox_wc_copy(&b, "X", "A"));
  {
    nodes_row_t rows[] = {
      { 0, "A",         "normal",       2, "A" },
      { 0, "A/B",       "normal",       2, "A/B" },
      { 0, "A/B/X",     "not-present",  0, "A/B/X" },
      { 0, "A/B/Y",     "not-present",  0, "A/B/Y" },
      { 0, "A/B/Z",     "normal",       2, "A/B/Z" },
      { 1, "A",         "normal",       2, "X" },
      { 1, "A/B",       "normal",       2, "X/B" },
      { 1, "A/B/W",     "not-present",  0, "X/B/W" },
      { 1, "A/B/X",     "normal",       2, "X/B/X" },
      { 1, "A/B/Y",     "not-present",  0, "X/B/Y" },
      { 1, "A/B/Z",     "not-present",  0, "X/B/Z" },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A", rows));
  }

  SVN_ERR(sbox_wc_delete(&b, "A"));
  {
    nodes_row_t rows[] = {
      { 0, "A",         "normal",       2, "A" },
      { 0, "A/B",       "normal",       2, "A/B" },
      { 0, "A/B/X",     "not-present",  0, "A/B/X" },
      { 0, "A/B/Y",     "not-present",  0, "A/B/Y" },
      { 0, "A/B/Z",     "normal",       2, "A/B/Z" },
      { 1, "A",         "base-deleted", NO_COPY_FROM },
      { 1, "A/B",       "base-deleted", NO_COPY_FROM },
      { 1, "A/B/Z",     "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A", rows));
  }

  return SVN_NO_ERROR;
}

typedef struct actual_row_t {
  const char *local_relpath;
  const char *changelist;
} actual_row_t;

static svn_error_t *
insert_actual(svn_test__sandbox_t *b,
              actual_row_t *actual)
{
  svn_sqlite__db_t *sdb;
  svn_sqlite__stmt_t *stmt;

  if (!actual)
    return SVN_NO_ERROR;

  SVN_ERR(open_wc_db(&sdb, b->wc_abspath, b->pool, b->pool));

  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_DELETE_ACTUAL));
  SVN_ERR(svn_sqlite__step_done(stmt));

  while(actual->local_relpath)
    {
      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_ACTUAL));
      SVN_ERR(svn_sqlite__bindf(stmt, "sss",
                                actual->local_relpath,
                                actual->local_relpath[0]
                                  ? svn_relpath_dirname(actual->local_relpath,
                                                        b->pool)
                                  : NULL,
                                actual->changelist));
      SVN_ERR(svn_sqlite__step_done(stmt));
      if (actual->changelist)
        {
          SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
                                            STMT_ENSURE_EMPTY_PRISTINE));
          SVN_ERR(svn_sqlite__step_done(stmt));
          SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_NODES_SET_FILE));
          SVN_ERR(svn_sqlite__bindf(stmt, "s", actual->local_relpath));
          SVN_ERR(svn_sqlite__step_done(stmt));
        }
      ++actual;
    }
  SVN_ERR(svn_sqlite__close(sdb));

  return SVN_NO_ERROR;
}

static svn_error_t *
check_db_actual(svn_test__sandbox_t* b, actual_row_t *rows)
{
  svn_sqlite__db_t *sdb;
  svn_sqlite__stmt_t *stmt;
  svn_boolean_t have_row;
  apr_hash_t *path_hash = apr_hash_make(b->pool);

  if (!rows)
    return SVN_NO_ERROR;

  while(rows->local_relpath)
    {
      apr_hash_set(path_hash, rows->local_relpath, APR_HASH_KEY_STRING,
                   (void*)1);
      ++rows;
    }

  SVN_ERR(open_wc_db(&sdb, b->wc_abspath, b->pool, b->pool));

  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_ALL_ACTUAL));
  SVN_ERR(svn_sqlite__step(&have_row, stmt));
  while (have_row)
    {
      const char *local_relpath = svn_sqlite__column_text(stmt, 0, b->pool);
      if (!apr_hash_get(path_hash, local_relpath, APR_HASH_KEY_STRING))
        return svn_error_createf(SVN_ERR_TEST_FAILED, svn_sqlite__reset(stmt),
                                 "actual '%s' unexpected", local_relpath);
      apr_hash_set(path_hash, local_relpath, APR_HASH_KEY_STRING, NULL);
      SVN_ERR(svn_sqlite__step(&have_row, stmt));
    }

  if (apr_hash_count(path_hash))
    {
      const char *local_relpath
        = apr_hash_this_key(apr_hash_first(b->pool, path_hash));
      return svn_error_createf(SVN_ERR_TEST_FAILED, svn_sqlite__reset(stmt),
                               "actual '%s' expected", local_relpath);
    }

  SVN_ERR(svn_sqlite__reset(stmt));
  SVN_ERR(svn_sqlite__close(sdb));

  return SVN_NO_ERROR;
}

static svn_error_t *
revert(svn_test__sandbox_t *b,
       const char *local_relpath,
       svn_depth_t depth,
       nodes_row_t *before_nodes,
       nodes_row_t *after_nodes,
       actual_row_t *before_actual,
       actual_row_t *after_actual)
{
  const char *local_abspath = sbox_wc_path(b, local_relpath);
  svn_error_t *err;

  if (!before_actual)
    {
      actual_row_t actual[] = { { 0 } };
      SVN_ERR(insert_actual(b, actual));
    }

  SVN_ERR(insert_dirs(b, before_nodes));
  SVN_ERR(insert_actual(b, before_actual));
  SVN_ERR(check_db_rows(b, "", before_nodes));
  SVN_ERR(check_db_actual(b, before_actual));
  err = svn_wc__db_op_revert(b->wc_ctx->db, local_abspath, depth, FALSE,
                             b->pool, b->pool);
  if (err)
    {
      /* If db_op_revert returns an error the DB should be unchanged so
         verify and return a verification error if a change is detected
         or the revert error if unchanged. */
      err = svn_error_compose_create(check_db_rows(b, "", before_nodes), err);
      err = svn_error_compose_create(check_db_actual(b, before_actual), err);
      return err;
    }
  SVN_ERR(check_db_rows(b, "", after_nodes));
  SVN_ERR(check_db_actual(b, after_actual));

  return SVN_NO_ERROR;
}

static svn_error_t *
test_op_revert(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;
  svn_error_t *err;

  SVN_ERR(svn_test__sandbox_create(&b, "test_op_revert", opts, pool));

  {
    nodes_row_t before[] = {
      { 0, "",    "normal", 4, "" },
      { 0, "A",   "normal", 4, "A" },
      { 2, "A/B", "normal", NO_COPY_FROM },
      { 0 },
    };
    nodes_row_t after[] = {
      { 0, "",    "normal", 4, "" },
      { 0, "A",   "normal", 4, "A" },
      { 0 },
    };
    actual_row_t before_actual1[] = {
      { "A", NULL },
      { "A/B", NULL },
      { 0 }
    };
    actual_row_t after_actual1[] = {
      { "A", NULL },
      { 0 }
    };
    actual_row_t before_actual2[] = {
      { "A/B", NULL },
      { "A/B/C", NULL },
      { 0 }
    };
    actual_row_t after_actual2[] = {
      { "A/B", NULL },
      { 0 }
    };
    actual_row_t before_actual3[] = {
      { "", NULL },
      { "A", NULL },
      { "A/B", NULL },
      { 0 }
    };
    actual_row_t after_actual3[] = {
      { "", NULL },
      { "A/B", NULL },
      { 0 }
    };
    actual_row_t before_actual4[] = {
      { "", NULL },
      { "A/B", NULL },
      { 0 }
    };
    actual_row_t after_actual4[] = {
      { "A/B", NULL },
      { 0 }
    };
    actual_row_t common_actual5[] = {
      { "A/B", NULL },
      { "A/B/C", NULL },
      { 0 }
    };
    actual_row_t common_actual6[] = {
      { "A/B", NULL },
      { "A/B/C", NULL },
      { "A/B/C/D", NULL },
      { 0 }
    };
    SVN_ERR(revert(&b, "A/B", svn_depth_empty,
                   before, after, NULL, NULL));
    SVN_ERR(revert(&b, "A/B", svn_depth_empty,
                   before, after, before_actual1, after_actual1));
    SVN_ERR(revert(&b, "A/B/C", svn_depth_empty,
                   before, before, before_actual2, after_actual2));
    SVN_ERR(revert(&b, "A", svn_depth_empty,
                   before, before, before_actual3, after_actual3));
    SVN_ERR(revert(&b, "", svn_depth_empty,
                   before, before, before_actual4, after_actual4));
    err = revert(&b, "A/B", svn_depth_empty,
                 before, before, common_actual5, common_actual5);
    SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH);
    err = revert(&b, "A/B/C", svn_depth_empty,
                 before, before, common_actual6, common_actual6);
    SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH);
  }

  {
    nodes_row_t common[] = {
      { 0, "",        "normal", 4, "" },
      { 0, "A",       "normal", 4, "A" },
      { 0, "P",       "normal", 4, "P" },
      { 0, "P/Q",     "normal", 4, "P/Q" },
      { 1, "P",       "normal", 3, "V" },
      { 1, "P/Q",     "normal", 3, "V/Q" },
      { 2, "A/B",     "normal", 2, "X/B" },
      { 2, "A/B/C",   "normal", 2, "X/B/C" },
      { 2, "A/B/C/D", "normal", 2, "X/B/C/D" },
      { 1, "X",       "normal", NO_COPY_FROM },
      { 2, "X/Y",     "normal", NO_COPY_FROM },
      { 0 },
    };
    actual_row_t common_actual[] = {
      { "A/B/C/D", NULL },
      { "A/B/C", NULL },
      { "A/B", NULL },
      { "P", NULL },
      { "X", NULL },
      { 0 }
    };
    actual_row_t actual1[] = {
      { "A/B/C", NULL },
      { "A/B", NULL },
      { "P", NULL },
      { "X", NULL },
      { 0 }
    };
    actual_row_t actual2[] = {
      { "A/B/C/D", NULL },
      { "A/B", NULL },
      { "P", NULL },
      { "X", NULL },
      { 0 }
    };

    SVN_ERR(revert(&b, "A/B/C/D", svn_depth_empty,
                   common, common, NULL, NULL));
    SVN_ERR(revert(&b, "A/B/C/D", svn_depth_empty,
                   common, common, common_actual, actual1));

    SVN_ERR(revert(&b, "A/B/C", svn_depth_empty,
                   common, common, NULL, NULL));
    SVN_ERR(revert(&b, "A/B/C", svn_depth_empty,
                   common, common, common_actual, actual2));

    err = revert(&b, "A/B", svn_depth_empty,
                 common, common, NULL, NULL);
    SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH);
    err = revert(&b, "A/B", svn_depth_empty,
                 common, common, common_actual, common_actual);
    SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH);

    err = revert(&b, "P", svn_depth_empty,
                 common, common, NULL, NULL);
    SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH);
    err = revert(&b, "P", svn_depth_empty,
                 common, common, common_actual, common_actual);
    SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH);

    err = revert(&b, "X", svn_depth_empty,
                 common, common, NULL, NULL);
    SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH);
    err = revert(&b, "X", svn_depth_empty,
                 common, common, common_actual, common_actual);
    SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH);
  }

  {
    nodes_row_t before[] = {
      { 0, "",        "normal", 4, "" },
      { 0, "A",       "normal", 4, "A" },
      { 0, "A/B",     "normal", 4, "A/B" },
      { 0, "A/B/C",   "normal", 4, "A/B/C" },
      { 3, "A/B/C",   "base-deleted", NO_COPY_FROM },
      { 0 },
    };
    nodes_row_t after[] = {
      { 0, "",        "normal", 4, "" },
      { 0, "A",       "normal", 4, "A" },
      { 0, "A/B",     "normal", 4, "A/B" },
      { 0, "A/B/C",   "normal", 4, "A/B/C" },
      { 0 },
    };
    actual_row_t before_actual[] = {
      { "A/B", NULL },
      { "A/B/C", NULL },
      { 0 }
    };
    actual_row_t after_actual[] = {
      { "A/B", NULL },
      { 0 }
    };
    SVN_ERR(revert(&b, "A/B/C", svn_depth_empty,
                   before, after, NULL, NULL));
    SVN_ERR(revert(&b, "A/B/C", svn_depth_empty,
                   before, after, before_actual, after_actual));
  }

  {
    nodes_row_t before[] = {
      { 0, "",    "normal", 4, "" },
      { 1, "A",   "normal", 2, "X" },
      { 1, "A/B", "normal", 2, "X/B" },
      { 2, "A/B", "base-deleted", NO_COPY_FROM },
      { 0 },
    };
    nodes_row_t after[] = {
      { 0, "",    "normal", 4, "" },
      { 1, "A",   "normal", 2, "X" },
      { 1, "A/B", "normal", 2, "X/B" },
      { 0 },
    };
    actual_row_t before_actual[] = {
      { "A", NULL },
      { "A/B", NULL },
      { 0 }
    };
    actual_row_t after_actual[] = {
      { "A", NULL },
      { 0 }
    };
    SVN_ERR(revert(&b, "A/B", svn_depth_empty,
                   before, after, NULL, NULL));
    SVN_ERR(revert(&b, "A/B", svn_depth_empty,
                   before, after, before_actual, after_actual));
  }

  {
    nodes_row_t before[] = {
      { 0, "",    "normal", 4, "" },
      { 0, "A",   "normal", 4, "A" },
      { 0, "A/B", "normal", 4, "A/B" },
      { 1, "A",   "normal", 2, "X" },
      { 1, "A/B", "normal", 2, "X/B" },
      { 2, "A/B", "base-deleted", NO_COPY_FROM },
      { 0 },
    };
    nodes_row_t after[] = {
      { 0, "",    "normal", 4, "" },
      { 0, "A",   "normal", 4, "A" },
      { 0, "A/B", "normal", 4, "A/B" },
      { 1, "A",   "normal", 2, "X" },
      { 1, "A/B", "normal", 2, "X/B" },
      { 0 },
    };
    actual_row_t before_actual[] = {
      { "A", NULL },
      { "A/B", NULL },
      { 0 },
    };
    actual_row_t after_actual[] = {
      { "A", NULL },
      { 0 },
    };
    SVN_ERR(revert(&b, "A/B", svn_depth_empty,
                   before, after, NULL, NULL));
    SVN_ERR(revert(&b, "A/B", svn_depth_empty,
                   before, after, before_actual, after_actual));
  }

  {
    nodes_row_t before[] = {
      { 0, "",        "normal", 4, "" },
      { 0, "A",       "normal", 4, "A" },
      { 0, "A/B",     "normal", 4, "A/B" },
      { 0, "A/B/C",   "normal", 4, "A/B/C" },
      { 2, "A/B",     "base-deleted", NO_COPY_FROM },
      { 2, "A/B/C",   "base-deleted", NO_COPY_FROM },
      { 2, "A/B/C/D", "base-deleted", NO_COPY_FROM },
      { 0 },
    };
    nodes_row_t after[] = {
      { 0, "",        "normal", 4, "" },
      { 0, "A",       "normal", 4, "A" },
      { 0, "A/B",     "normal", 4, "A/B" },
      { 0, "A/B/C",   "normal", 4, "A/B/C" },
      { 3, "A/B/C",   "base-deleted", NO_COPY_FROM },
      { 3, "A/B/C/D", "base-deleted", NO_COPY_FROM },
      { 0 },
    };
    SVN_ERR(revert(&b, "A/B", svn_depth_empty,
                   before, after, NULL, NULL));
  }

  {
    nodes_row_t before[] = {
      { 0, "",        "normal", 4, "" },
      { 0, "A",       "normal", 4, "A" },
      { 0, "A/B",     "normal", 4, "A/B" },
      { 0, "A/B/C",   "normal", 4, "A/B/C" },
      { 1, "A",       "normal", NO_COPY_FROM },
      { 1, "A/B",     "base-deleted", NO_COPY_FROM },
      { 1, "A/B/C",   "base-deleted", NO_COPY_FROM },
      { 2, "A/B",     "normal", NO_COPY_FROM },
      { 3, "A/B/C",   "normal", NO_COPY_FROM },
      { 0 },
    };
    nodes_row_t after1[] = {
      { 0, "",        "normal", 4, "" },
      { 0, "A",       "normal", 4, "A" },
      { 0, "A/B",     "normal", 4, "A/B" },
      { 0, "A/B/C",   "normal", 4, "A/B/C" },
      { 1, "A",       "normal", NO_COPY_FROM },
      { 1, "A/B",     "base-deleted", NO_COPY_FROM },
      { 1, "A/B/C",   "base-deleted", NO_COPY_FROM },
      { 2, "A/B",     "normal", NO_COPY_FROM },
      { 0 },
    };
    nodes_row_t after2[] = {
      { 0, "",        "normal", 4, "" },
      { 0, "A",       "normal", 4, "A" },
      { 0, "A/B",     "normal", 4, "A/B" },
      { 0, "A/B/C",   "normal", 4, "A/B/C" },
      { 1, "A",       "normal", NO_COPY_FROM },
      { 1, "A/B",     "base-deleted", NO_COPY_FROM },
      { 1, "A/B/C",   "base-deleted", NO_COPY_FROM },
      { 0 },
    };
    SVN_ERR(revert(&b, "A/B/C", svn_depth_empty,
                   before, after1, NULL, NULL));
    SVN_ERR(revert(&b, "A/B", svn_depth_empty,
                   after1, after2, NULL, NULL));
  }

  {
    nodes_row_t before[] = {
      { 0, "",        "normal", 4, "" },
      { 0, "A",       "normal", 4, "A" },
      { 0, "A/B",     "normal", 4, "A/B" },
      { 0, "A/B/C",   "normal", 4, "A/B/C" },
      { 0, "A/B/C/D", "normal", 4, "A/B/C/D" },
      { 2, "A/B",     "normal", NO_COPY_FROM },
      { 2, "A/B/C",   "base-deleted", NO_COPY_FROM },
      { 2, "A/B/C/D", "base-deleted", NO_COPY_FROM },
      { 0 },
    };
    nodes_row_t after[] = {
      { 0, "",        "normal", 4, "" },
      { 0, "A",       "normal", 4, "A" },
      { 0, "A/B",     "normal", 4, "A/B" },
      { 0, "A/B/C",   "normal", 4, "A/B/C" },
      { 0, "A/B/C/D", "normal", 4, "A/B/C/D" },
      { 3, "A/B/C",   "base-deleted", NO_COPY_FROM },
      { 3, "A/B/C/D", "base-deleted", NO_COPY_FROM },
      { 0 },
    };
    SVN_ERR(revert(&b, "A/B", svn_depth_empty,
                   before, after, NULL, NULL));
  }

  {
    nodes_row_t common[] = {
      { 0, "",        "normal", 4, "" },
      { 0, "A",       "normal", 4, "A" },
      { 0, "A/B",     "normal", 4, "A/B" },
      { 0, "A/B/C",   "normal", 4, "A/B/C" },
      { 0, "A/B/C/D", "normal", 4, "A/B/C/D" },
      { 1, "A",       "normal", 2, "X/Y" },
      { 1, "A/B",     "normal", 2, "X/Y/B" },
      { 1, "A/B/C",   "base-deleted", NO_COPY_FROM },
      { 1, "A/B/C/D", "base-deleted", NO_COPY_FROM },
      { 0 },
    };
    err = revert(&b, "A", svn_depth_empty,
                 common, common, NULL, NULL);
    SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH);
  }

  {
    nodes_row_t before[] = {
      { 0, "",        "normal", 4, "" },
      { 0, "A",       "normal", 4, "A" },
      { 0, "A/B",     "normal", 4, "A/B" },
      { 0, "A/B/C",   "normal", 4, "A/B/C" },
      { 1, "A",       "normal", NO_COPY_FROM },
      { 1, "A/B",     "base-deleted", NO_COPY_FROM },
      { 1, "A/B/C",   "base-deleted", NO_COPY_FROM },
      { 2, "A/B",     "normal", NO_COPY_FROM },
      { 0 },
    };
    nodes_row_t after1[] = {
      { 0, "",        "normal", 4, "" },
      { 0, "A",       "normal", 4, "A" },
      { 0, "A/B",     "normal", 4, "A/B" },
      { 0, "A/B/C",   "normal", 4, "A/B/C" },
      { 0 },
    };
    nodes_row_t after2[] = {
      { 0, "",        "normal", 4, "" },
      { 0, "A",       "normal", 4, "A" },
      { 0, "A/B",     "normal", 4, "A/B" },
      { 0, "A/B/C",   "normal", 4, "A/B/C" },
      { 1, "A",       "normal", NO_COPY_FROM },
      { 1, "A/B",     "base-deleted", NO_COPY_FROM },
      { 1, "A/B/C",   "base-deleted", NO_COPY_FROM },
      { 0 },
    };
    SVN_ERR(revert(&b, "", svn_depth_infinity,
                   before, after1, NULL, NULL));
    SVN_ERR(revert(&b, "A", svn_depth_infinity,
                   before, after1, NULL, NULL));
    SVN_ERR(revert(&b, "A/B", svn_depth_infinity,
                   before, after2, NULL, NULL));
    SVN_ERR(revert(&b, "A/B/C", svn_depth_empty,
                   before, before, NULL, NULL));
  }

  {
    nodes_row_t before[] = {
      { 0, "",      "normal", 4, "" },
      { 0, "A",     "normal", 4, "A" },
      { 0, "A/B",   "normal", 4, "A/B" },
      { 1, "A",     "normal", 2, "X" },
      { 1, "A/B",   "normal", 2, "X/B" },
      { 1, "A/B/C", "normal", 2, "X/B/C" },
      { 2, "A/B",   "base-deleted", NO_COPY_FROM },
      { 2, "A/B/C", "base-deleted", NO_COPY_FROM },
      { 0 },
    };
    nodes_row_t after1[] = {
      { 0, "",      "normal", 4, "" },
      { 0, "A",     "normal", 4, "A" },
      { 0, "A/B",   "normal", 4, "A/B" },
      { 1, "A",     "normal", 2, "X" },
      { 1, "A/B",   "normal", 2, "X/B" },
      { 1, "A/B/C", "normal", 2, "X/B/C" },
      { 3, "A/B/C", "base-deleted", NO_COPY_FROM },
      { 0 },
    };
    nodes_row_t after2[] = {
      { 0, "",      "normal", 4, "" },
      { 0, "A",     "normal", 4, "A" },
      { 0, "A/B",   "normal", 4, "A/B" },
      { 1, "A",     "normal", 2, "X" },
      { 1, "A/B",   "normal", 2, "X/B" },
      { 1, "A/B/C", "normal", 2, "X/B/C" },
      { 0 },
    };
    SVN_ERR(revert(&b, "A/B", svn_depth_empty,
                   before, after1, NULL, NULL));
    SVN_ERR(revert(&b, "A/B", svn_depth_infinity,
                   before, after2, NULL, NULL));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
test_op_revert_changelist(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "test_op_revert_changelist", opts, pool));

  {
    nodes_row_t before[] = {
      { 0, "",    "normal", 4, "" },
      { 0, "A",   "normal", 4, "A" },
      { 2, "A/f", "normal", NO_COPY_FROM },
      { 0 },
    };
    nodes_row_t after[] = {
      { 0, "",    "normal", 4, "" },
      { 0, "A",   "normal", 4, "A" },
      { 0 },
    };
    actual_row_t before_actual[] = {
      { "A/f", "qq" },
      { 0 },
    };
    actual_row_t after_actual[] = {
      { 0 },
    };
    SVN_ERR(revert(&b, "A/f", svn_depth_empty,
                   before, after, before_actual, after_actual));
    SVN_ERR(revert(&b, "A/f", svn_depth_infinity,
                   before, after, before_actual, after_actual));
    SVN_ERR(revert(&b, "", svn_depth_infinity,
                   before, after, before_actual, after_actual));
  }

  {
    nodes_row_t before[] = {
      { 0, "",    "normal", 4, "" },
      { 0, "A",   "normal", 4, "A" },
      { 0, "A/f", "normal", 4, "A/f" },
      { 2, "A/f", "base-deleted", NO_COPY_FROM },
      { 0 },
    };
    nodes_row_t after[] = {
      { 0, "",    "normal", 4, "" },
      { 0, "A",   "normal", 4, "A" },
      { 0, "A/f", "normal", 4, "A/f" },
      { 0 },
    };
    actual_row_t common_actual[] = {
      { "A/f", "qq" },
      { 0 },
    };
    SVN_ERR(revert(&b, "A/f", svn_depth_empty,
                   before, after, common_actual, common_actual));
    SVN_ERR(revert(&b, "A/f", svn_depth_infinity,
                   before, after, common_actual, common_actual));
    SVN_ERR(revert(&b, "", svn_depth_infinity,
                   before, after, common_actual, common_actual));
  }

  {
    nodes_row_t before[] = {
      { 0, "",    "normal", 4, "" },
      { 0, "A",   "normal", 4, "A" },
      { 0, "A/f", "normal", 4, "A/f" },
      { 0 },
    };
    nodes_row_t after[] = {
      { 0, "",    "normal", 4, "" },
      { 0, "A",   "normal", 4, "A" },
      { 0, "A/f", "normal", 4, "A/f" },
      { 0 },
    };
    actual_row_t common_actual[] = {
      { "A/f", "qq" },
      { 0 },
    };
    SVN_ERR(revert(&b, "A/f", svn_depth_empty,
                   before, after, common_actual, common_actual));
    SVN_ERR(revert(&b, "A/f", svn_depth_infinity,
                   before, after, common_actual, common_actual));
    SVN_ERR(revert(&b, "", svn_depth_infinity,
                   before, after, common_actual, common_actual));
  }

  return SVN_NO_ERROR;
}

/* Check that the (const char *) keys of HASH are exactly the
 * EXPECTED_NUM strings in EXPECTED_STRINGS.  Return an error if not. */
static svn_error_t *
check_hash_keys(apr_hash_t *hash,
                int expected_num,
                const char **expected_strings,
                apr_pool_t *scratch_pool)
{
  svn_error_t *err = SVN_NO_ERROR;
  int i;
  apr_hash_index_t *hi;

  for (i = 0; i < expected_num; i++)
    {
      const char *name = expected_strings[i];

      if (apr_hash_get(hash, name, APR_HASH_KEY_STRING))
        apr_hash_set(hash, name, APR_HASH_KEY_STRING, NULL);
      else
        err = svn_error_compose_create(
                err, svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
                                       _("Expected, not found: '%s'"), name));
    }
  for (hi = apr_hash_first(scratch_pool, hash); hi;
       hi = apr_hash_next(hi))
    {
      const char *name = apr_hash_this_key(hi);
      err = svn_error_compose_create(
              err, svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
                                     _("Found, not expected: '%s'"), name));
    }
  return err;
}

/* Check that the (const char *) keys of APR_HASH are exactly the
 * strings in (const char *[]) C_ARRAY.  Return an error if not. */
#define CHECK_HASH(apr_hash, c_array, scratch_pool) \
  check_hash_keys(apr_hash, sizeof(c_array) / sizeof(c_array[0]), \
                  c_array, scratch_pool)

/* Check that the basenames of the (const char *) paths in ARRAY are exactly
 * the EXPECTED_NUM strings in EXPECTED_STRINGS.  Return an error if not. */
static svn_error_t *
check_array_strings(const apr_array_header_t *array,
                    int expected_num,
                    const char **expected_strings,
                    apr_pool_t *scratch_pool)
{
  int i;
  apr_hash_t *hash = apr_hash_make(scratch_pool);

  for (i = 0; i < array->nelts; i++)
    {
      const char *path = APR_ARRAY_IDX(array, i, const char *);

      apr_hash_set(hash, svn_path_basename(path, scratch_pool),
                   APR_HASH_KEY_STRING, "");
    }

  return check_hash_keys(hash, expected_num, expected_strings, scratch_pool);
}

/* Check that the basenames of the (const char *) paths in APR_ARRAY are
 * exactly the strings in (const char *[]) C_ARRAY. Return an error if not. */
#define CHECK_ARRAY(apr_array, c_array, scratch_pool) \
  check_array_strings(apr_array, sizeof(c_array) / sizeof(c_array[0]), \
                      c_array, scratch_pool)


/* The purpose of this test is to check whether a child of a deleted-and-
 * replaced directory is reported by various "list the children" APIs. */
static svn_error_t *
test_children_of_replaced_dir(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;
  const apr_array_header_t *children_array;
  apr_hash_t *children_hash, *conflicts_hash;
  const char *A_abspath;
  const char *working_children_exc_hidden[] = { "G", "H", "I", "J", "K", "L" };
  const char *working_children_inc_hidden[] = { "G", "H", "I", "J", "K", "L" };
  const char *all_children_inc_hidden[] = { "F", "G", "H", "I", "J", "K", "L" };

  /*
   * F - base only
   * G - base, working (from copy of X; schedule-delete)
   * H - base, working (from copy of X)
   * I - working only (from copy of X)
   * J - working only (schedule-add)
   * K - working only (from copy of X; schedule-delete)
   * L - base, working (not in copy; schedule-add)
   */

  SVN_ERR(svn_test__sandbox_create(&b, "children_of_replaced_dir", opts, pool));
  A_abspath = svn_dirent_join(b.wc_abspath, "A", pool);

  /* Set up the base state as revision 1. */
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/F"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/G"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/H"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/L"));
  SVN_ERR(sbox_wc_mkdir(&b, "X"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/G"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/H"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/I"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/K"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  /* Replace A with a copy of X. */
  SVN_ERR(sbox_wc_delete(&b, "A"));
  SVN_ERR(sbox_wc_copy(&b, "X", "A"));

  /* Make other local mods. */
  SVN_ERR(sbox_wc_delete(&b, "A/G"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/J"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/L"));

  /* Test several variants of "list the children of 'A'". */

  SVN_ERR(svn_wc__db_read_children(&children_array, b.wc_ctx->db, A_abspath,
                                   pool, pool));
  SVN_ERR(CHECK_ARRAY(children_array, all_children_inc_hidden, pool));

  SVN_ERR(svn_wc__db_read_children_of_working_node(
            &children_array, b.wc_ctx->db, A_abspath, pool, pool));
  SVN_ERR(CHECK_ARRAY(children_array, working_children_inc_hidden, pool));

  SVN_ERR(svn_wc__db_read_children(&children_array, b.wc_ctx->db, A_abspath,
                                   pool, pool));
  SVN_ERR(CHECK_ARRAY(children_array, all_children_inc_hidden, pool));

  /* I am not testing svn_wc__node_get_children(show_hidden=FALSE) because
   * I'm not sure what result we should expect if a certain child path is a
   * child of a deleted-and-replaced dir (so should be included) and is also
   * a 'hidden' child of the working dir (so should be excluded). */

  SVN_ERR(svn_wc__node_get_children_of_working_node(
            &children_array, b.wc_ctx, A_abspath,
            pool, pool));
  SVN_ERR(CHECK_ARRAY(children_array, working_children_exc_hidden, pool));

  SVN_ERR(svn_wc__db_read_children_info(&children_hash, &conflicts_hash,
                                        b.wc_ctx->db, A_abspath,
                                        FALSE /* base_tree_only */,
                                        pool, pool));
  SVN_ERR(CHECK_HASH(children_hash, all_children_inc_hidden, pool));

  /* We don't yet have a svn_wc__db_read_children_info2() to test. */

  return SVN_NO_ERROR;
}

static svn_error_t *
do_delete(svn_test__sandbox_t *b,
          const char *local_relpath,
          nodes_row_t *before,
          nodes_row_t *after,
          actual_row_t *actual_before,
          actual_row_t *actual_after)
{
  const char *local_abspath = sbox_wc_path(b, local_relpath);

  SVN_ERR(insert_dirs(b, before));
  SVN_ERR(insert_actual(b, actual_before));
  SVN_ERR(check_db_rows(b, "", before));
  SVN_ERR(check_db_actual(b, actual_before));
  SVN_ERR(svn_wc__db_op_delete(b->wc_ctx->db, local_abspath, NULL,
                               TRUE /* delete_dir_externals */,
                               NULL /* conflict */, NULL /* work_item */,
                               NULL, NULL /* cancellation */,
                               NULL, NULL /* notification */,
                               b->pool));
  SVN_ERR(check_db_rows(b, "", after));
  SVN_ERR(check_db_actual(b, actual_after));

  return SVN_NO_ERROR;
}

static svn_error_t *
test_op_delete(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;
  SVN_ERR(svn_test__sandbox_create(&b, "op_delete", opts, pool));

  {
    nodes_row_t before1[] = {
      { 0, "",    "normal",       5, "" },
      { 0, "A",   "normal",       5, "A" },
      { 0, "A/B", "normal",       5, "A/B" },
      { 0 }
    };
    nodes_row_t before2[] = {
      { 0, "",    "normal",       5, "" },
      { 0, "A",   "normal",       5, "A" },
      { 0, "A/B", "normal",       5, "A/B" },
      { 1, "A",   "normal",       NO_COPY_FROM },
      { 2, "A/B", "normal",       NO_COPY_FROM },
      { 0 }
    };
    nodes_row_t after[] = {
      { 0, "",    "normal",       5, "" },
      { 0, "A",   "normal",       5, "A" },
      { 0, "A/B", "normal",       5, "A/B" },
      { 1, "A",   "base-deleted", NO_COPY_FROM },
      { 1, "A/B", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(do_delete(&b, "A", before1, after, NULL, NULL));
    SVN_ERR(do_delete(&b, "A", before2, after, NULL, NULL));
  }

  {
    nodes_row_t before[] = {
      { 0, "",      "normal",       5, "" },
      { 0, "A",     "normal",       5, "A" },
      { 2, "A/B",   "normal",       3, "X/B" },
      { 2, "A/B/C", "normal",       3, "X/B/C" },
      { 0 }
    };
    nodes_row_t after[] = {
      { 0, "",    "normal",       5, "" },
      { 0, "A",   "normal",       5, "A" },
      { 0 }
    };
    SVN_ERR(do_delete(&b, "A/B", before, after, NULL, NULL));
  }

  {
    nodes_row_t before[] = {
      { 0, "",      "normal",       5, "" },
      { 0, "A",     "normal",       5, "A" },
      { 0, "A/B",   "normal",       5, "A/B" },
      { 0, "A/B/C", "normal",       5, "A/B/C" },
      { 1, "A",     "normal",       3, "X" },
      { 1, "A/B",   "normal",       3, "X/B" },
      { 1, "A/B/C", "base-deleted", NO_COPY_FROM },
      { 1, "A/B/D", "normal",       3, "X/B/D" },
      { 0 }
    };
    nodes_row_t after1[] = {
      { 0, "",      "normal",       5, "" },
      { 0, "A",     "normal",       5, "A" },
      { 0, "A/B",   "normal",       5, "A/B" },
      { 0, "A/B/C", "normal",       5, "A/B/C" },
      { 1, "A",     "normal",       3, "X" },
      { 1, "A/B",   "normal",       3, "X/B" },
      { 1, "A/B/C", "base-deleted", NO_COPY_FROM },
      { 1, "A/B/D", "normal",       3, "X/B/D" },
      { 2, "A/B",   "base-deleted", NO_COPY_FROM },
      { 2, "A/B/D", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    nodes_row_t after2[] = {
      { 0, "",      "normal",       5, "" },
      { 0, "A",     "normal",       5, "A" },
      { 0, "A/B",   "normal",       5, "A/B" },
      { 0, "A/B/C", "normal",       5, "A/B/C" },
      { 1, "A",     "base-deleted", NO_COPY_FROM },
      { 1, "A/B",   "base-deleted", NO_COPY_FROM },
      { 1, "A/B/C", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(do_delete(&b, "A/B", before, after1, NULL, NULL));
    SVN_ERR(do_delete(&b, "A", before, after2, NULL, NULL));
  }

  {
    nodes_row_t before[] = {
      { 0, "",        "normal",       5, "" },
      { 0, "A",       "normal",       5, "A" },
      { 0, "A/B",     "normal",       5, "A/B" },
      { 0, "A/B/C",   "normal",       5, "A/B/C" },
      { 3, "A/B/C",   "normal",       3, "X" },
      { 3, "A/B/C/D", "normal",       3, "X/D" },
      { 4, "A/B/C/D", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    nodes_row_t after[] = {
      { 0, "",        "normal",       5, "" },
      { 0, "A",       "normal",       5, "A" },
      { 0, "A/B",     "normal",       5, "A/B" },
      { 0, "A/B/C",   "normal",       5, "A/B/C" },
      { 1, "A",       "base-deleted", NO_COPY_FROM },
      { 1, "A/B",     "base-deleted", NO_COPY_FROM },
      { 1, "A/B/C",   "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(do_delete(&b, "A", before, after, NULL, NULL));
  }

  {
    nodes_row_t state1[] = {
      { 0, "",        "normal", 5, "" },
      { 0, "A",       "normal", 5, "A" },
      { 0, "A/B",     "normal", 5, "A/B" },
      { 0, "A/B/C",   "normal", 5, "A/B/C" },
      { 0, "A/B/C/D", "normal", 5, "A/B/C" },
      { 4, "A/B/C/X", "normal", NO_COPY_FROM },
      { 0 }
    };
    nodes_row_t state2[] = {
      { 0, "",        "normal",       5, "" },
      { 0, "A",       "normal",       5, "A" },
      { 0, "A/B",     "normal",       5, "A/B" },
      { 0, "A/B/C",   "normal",       5, "A/B/C" },
      { 0, "A/B/C/D", "normal",       5, "A/B/C" },
      { 4, "A/B/C/D", "base-deleted", NO_COPY_FROM },
      { 4, "A/B/C/X", "normal",       NO_COPY_FROM },
      { 0 }
    };
    nodes_row_t state3[] = {
      { 0, "",        "normal",       5, "" },
      { 0, "A",       "normal",       5, "A" },
      { 0, "A/B",     "normal",       5, "A/B" },
      { 0, "A/B/C",   "normal",       5, "A/B/C" },
      { 0, "A/B/C/D", "normal",       5, "A/B/C" },
      { 2, "A/B",     "base-deleted", NO_COPY_FROM },
      { 2, "A/B/C",   "base-deleted", NO_COPY_FROM },
      { 2, "A/B/C/D", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    nodes_row_t state4[] = {
      { 0, "",        "normal",       5, "" },
      { 0, "A",       "normal",       5, "A" },
      { 0, "A/B",     "normal",       5, "A/B" },
      { 0, "A/B/C",   "normal",       5, "A/B/C" },
      { 0, "A/B/C/D", "normal",       5, "A/B/C" },
      { 1, "A",       "base-deleted", NO_COPY_FROM },
      { 1, "A/B",     "base-deleted", NO_COPY_FROM },
      { 1, "A/B/C",   "base-deleted", NO_COPY_FROM },
      { 1, "A/B/C/D", "base-deleted", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(do_delete(&b, "A/B/C/D", state1, state2, NULL, NULL));
    SVN_ERR(do_delete(&b, "A/B", state2, state3, NULL, NULL));
    SVN_ERR(do_delete(&b, "A", state3, state4, NULL, NULL));
  }

  {
    nodes_row_t before[] = {
      { 0, "",    "normal", 5, "" },
      { 0, "A",   "normal", 5, "" },
      { 0, "A/f", "normal", 5, "" },
      { 2, "A/B", "normal", 5, "" },
      { 0 }
    };
    nodes_row_t after[] = {
      { 0, "",    "normal", 5, "" },
      { 0, "A",   "normal", 5, "" },
      { 0, "A/f", "normal", 5, "" },
      { 1, "A",   "base-deleted", NO_COPY_FROM},
      { 1, "A/f", "base-deleted", NO_COPY_FROM},
      { 0 }
    };
    actual_row_t before_actual[] = {
      { "A",     NULL },
      { "A/f",   "qq" },
      { "A/B",   NULL },
      { "A/B/C", NULL },
      { 0 },
    };
    actual_row_t after_actual[] = {
      { "A/f", "qq" },
      { 0 },
    };
    SVN_ERR(do_delete(&b, "A", before, after, before_actual, after_actual));
  }

  {
    nodes_row_t before[] = {
      { 0, "",      "normal",       5, "" },
      { 0, "A",     "normal",       5, "A" },
      { 0, "A/B",   "normal",       5, "A/B" },
      { 0, "A/B/f", "normal",       5, "A/B/f" },
      { 0, "A/B/g", "normal",       5, "A/B/g" },
      { 1, "A",     "normal",       4, "A" },
      { 1, "A/B",   "normal",       4, "A/B" },
      { 1, "A/B/f", "normal",       4, "A/B/f" },
      { 1, "A/B/g", "base-deleted", NO_COPY_FROM},
      { 0 }
    };
    nodes_row_t after[] = {
      { 0, "",      "normal",       5, "" },
      { 0, "A",     "normal",       5, "A" },
      { 0, "A/B",   "normal",       5, "A/B" },
      { 0, "A/B/f", "normal",       5, "A/B/f" },
      { 0, "A/B/g", "normal",       5, "A/B/g" },
      { 1, "A",     "normal",       4, "A" },
      { 1, "A/B",   "normal",       4, "A/B" },
      { 1, "A/B/f", "normal",       4, "A/B/f" },
      { 1, "A/B/g", "base-deleted", NO_COPY_FROM},
      { 2, "A/B",   "base-deleted", NO_COPY_FROM},
      { 2, "A/B/f", "base-deleted", NO_COPY_FROM},
      { 0 }
    };
    SVN_ERR(do_delete(&b, "A/B", before, after, NULL, NULL));
  }

  return SVN_NO_ERROR;
}

/* The purpose of this test is to check what happens if a deleted child is
   replaced by the same nodes. */
static svn_error_t *
test_child_replace_with_same_origin(const svn_test_opts_t *opts,
                                    apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "child_replace_with_same", opts, pool));

  /* Set up the base state as revision 1. */
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_copy(&b, "A", "X"));

  {
    nodes_row_t rows[] = {
      {1, "X",       "normal",           1, "A"},
      {1, "X/B",     "normal",           1, "A/B"},
      {1, "X/B/C",   "normal",           1, "A/B/C"},
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "X", rows));
  }

  SVN_ERR(sbox_wc_delete(&b, "X/B"));
  {
    nodes_row_t rows[] = {
      {1, "X",       "normal",           1, "A"},
      {1, "X/B",     "normal",           1, "A/B"},
      {1, "X/B/C",   "normal",           1, "A/B/C"},

      {2, "X/B",     "base-deleted",     NO_COPY_FROM },
      {2, "X/B/C",   "base-deleted",     NO_COPY_FROM },

      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "X", rows));
  }

  SVN_ERR(sbox_wc_copy(&b, "A/B", "X/B"));
  {
    /* The revisions match what was here, so for an optimal commit
       this should have exactly the same behavior as reverting X/B.

       Another copy would be fine, as that is really what the user
       did. */
    nodes_row_t rows[] = {
      {1, "X",       "normal",           1, "A"},
      {1, "X/B",     "normal",           1, "A/B"},
      {1, "X/B/C",   "normal",           1, "A/B/C"},

      /* We either expect this */
      {2, "X/B",     "normal",           1, "A/B" },
      {2, "X/B/C",   "normal",           1, "A/B/C" },

      /* Or we expect that op_depth 2 does not exist */

      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "X", rows));
  }

  return SVN_NO_ERROR;
}

/* The purpose of this test is to check what happens below a shadowed update,
   in a few scenarios */
static svn_error_t *
test_shadowed_update(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "shadowed_update", opts, pool));

  /* Set up the base state as revision 1. */
  SVN_ERR(sbox_file_write(&b, "iota", "This is iota"));
  SVN_ERR(sbox_wc_add(&b, "iota"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  /* And create two trees in r2 */
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));

  SVN_ERR(sbox_wc_mkdir(&b, "K"));
  SVN_ERR(sbox_wc_mkdir(&b, "K/L"));
  SVN_ERR(sbox_wc_mkdir(&b, "K/L/M"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  /* And change something in r3 */
  SVN_ERR(sbox_file_write(&b, "iota", "This is a new iota"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  /* And delete C & M */
  SVN_ERR(sbox_wc_delete(&b, "A/B/C"));
  SVN_ERR(sbox_wc_delete(&b, "K/L/M"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  /* And now create the shadowed situation */
  SVN_ERR(sbox_wc_update(&b, "", 2));
  SVN_ERR(sbox_wc_copy(&b, "A", "A_tmp"));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_move(&b, "A_tmp", "A"));

  SVN_ERR(sbox_wc_mkdir(&b, "K"));
  SVN_ERR(sbox_wc_mkdir(&b, "K/L"));
  SVN_ERR(sbox_wc_mkdir(&b, "K/L/M"));

  /* Verify situation before update */
  {
    nodes_row_t rows[] = {
      {0, "",        "normal",           1, ""},
      {0, "iota",    "normal",           1, "iota"},

      {1, "A",       "normal",           2, "A"},
      {1, "A/B",     "normal",           2, "A/B"},
      {1, "A/B/C",   "normal",           2, "A/B/C"},

      {1, "K",       "normal",           NO_COPY_FROM},
      {2, "K/L",     "normal",           NO_COPY_FROM},
      {3, "K/L/M",   "normal",           NO_COPY_FROM},
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  /* And now bring in A and K below the local information */
  SVN_ERR(sbox_wc_update(&b, "", 3));

  {
    nodes_row_t rows[] = {

      {0, "",        "normal",           3, ""},
      {0, "iota",    "normal",           3, "iota"},

      {0, "A",       "normal",           3, "A"},
      {0, "A/B",     "normal",           3, "A/B"},
      {0, "A/B/C",   "normal",           3, "A/B/C"},

      {1, "A",       "normal",           2, "A"},
      {1, "A/B",     "normal",           2, "A/B"},
      {1, "A/B/C",   "normal",           2, "A/B/C"},

      {0, "K",       "normal",           3, "K"},
      {0, "K/L",     "normal",           3, "K/L"},
      {0, "K/L/M",   "normal",           3, "K/L/M"},

      {1, "K",       "normal",           NO_COPY_FROM},
      {1, "K/L",     "base-deleted",     NO_COPY_FROM},
      {1, "K/L/M",   "base-deleted",     NO_COPY_FROM},

      {2, "K/L",     "normal",           NO_COPY_FROM},
      {3, "K/L/M",   "normal",           NO_COPY_FROM},

      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  /* Update again to remove C and M */
  SVN_ERR(sbox_wc_resolved(&b, "A"));
  SVN_ERR(sbox_wc_resolved(&b, "K"));
  SVN_ERR(sbox_wc_update(&b, "", 4));

  {
    nodes_row_t rows[] = {

      {0, "",        "normal",           4, ""},
      {0, "iota",    "normal",           4, "iota"},

      {0, "A",       "normal",           4, "A"},
      {0, "A/B",     "normal",           4, "A/B"},

      {1, "A",       "normal",           2, "A"},
      {1, "A/B",     "normal",           2, "A/B"},
      {1, "A/B/C",   "normal",           2, "A/B/C"},

      {0, "K",       "normal",           4, "K"},
      {0, "K/L",     "normal",           4, "K/L"},

      {1, "K",       "normal",           NO_COPY_FROM},
      {1, "K/L",     "base-deleted",     NO_COPY_FROM},

      {2, "K/L",     "normal",           NO_COPY_FROM},
      {3, "K/L/M",   "normal",           NO_COPY_FROM},

      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  /* Update again to bring C and M back */
  SVN_ERR(sbox_wc_resolved(&b, "A"));
  SVN_ERR(sbox_wc_resolved(&b, "K"));
  SVN_ERR(sbox_wc_update(&b, "", 3));

  SVN_ERR(sbox_wc_delete(&b, "K/L/M"));
  {
    nodes_row_t rows[] = {
      {0, "",        "normal",           3, ""},
      {0, "iota",    "normal",           3, "iota"},

      {0, "A",       "normal",           3, "A"},
      {0, "A/B",     "normal",           3, "A/B"},
      {0, "A/B/C",   "normal",           3, "A/B/C"},

      {1, "A",       "normal",           2, "A"},
      {1, "A/B",     "normal",           2, "A/B"},
      {1, "A/B/C",   "normal",           2, "A/B/C"},

      {0, "K",       "normal",           3, "K"},
      {0, "K/L",     "normal",           3, "K/L"},
      {0, "K/L/M",   "normal",           3, "K/L/M"},

      {1, "K",       "normal",           NO_COPY_FROM},
      {1, "K/L",     "base-deleted",     NO_COPY_FROM},
      {1, "K/L/M",   "base-deleted",     NO_COPY_FROM},

      {2, "K/L",     "normal",           NO_COPY_FROM},

      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  /* Resolve conflict on K and go back to r1 */
  SVN_ERR(sbox_wc_revert(&b, "K", svn_depth_infinity));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_mkdir(&b, "K"));
  SVN_ERR(sbox_wc_mkdir(&b, "K/L"));

  SVN_ERR(sbox_wc_update(&b, "", 3));

  {
    nodes_row_t rows[] = {

      {0, "K",       "normal",           3, "K"},
      {0, "K/L",     "normal",           3, "K/L"},
      {0, "K/L/M",   "normal",           3, "K/L/M"},

      {1, "K",       "normal",           NO_COPY_FROM},
      {1, "K/L",     "base-deleted",     NO_COPY_FROM},
      {1, "K/L/M",   "base-deleted",     NO_COPY_FROM},

      {2, "K/L",     "normal",           NO_COPY_FROM},

      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "K", rows));
  }

  /* Update the shadowed K/L/M to r4 where they do not exit */
  SVN_ERR(sbox_wc_resolved(&b, "K"));
  SVN_ERR(sbox_wc_update(&b, "K/L/M", 4));
  SVN_ERR(sbox_wc_resolved(&b, "A"));
  SVN_ERR(sbox_wc_update(&b, "A/B/C", 4));

  {
    nodes_row_t rows[] = {

      {0, "",        "normal",           3, ""},
      {0, "iota",    "normal",           3, "iota"},

      {0, "A",       "normal",           3, "A"},
      {0, "A/B",     "normal",           3, "A/B"},
      {0, "A/B/C",   "not-present",      4, "A/B/C"},

      {1, "A",       "normal",           2, "A"},
      {1, "A/B",     "normal",           2, "A/B"},
      {1, "A/B/C",   "normal",           2, "A/B/C"},

      {0, "K",       "normal",           3, "K"},
      {0, "K/L",     "normal",           3, "K/L"},
      {0, "K/L/M",   "not-present",      4, "K/L/M"},

      {1, "K",       "normal",           NO_COPY_FROM},
      {1, "K/L",     "base-deleted",     NO_COPY_FROM},

      {2, "K/L",     "normal",           NO_COPY_FROM},

      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }


  return SVN_NO_ERROR;
}

/* The purpose of this test is to check what happens below a shadowed update,
   in a few scenarios */
static svn_error_t *
test_copy_of_deleted(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "copy_of_deleted", opts, pool));
  SVN_ERR(sbox_add_and_commit_greek_tree(&b));

  /* Recreate the test scenario from copy_tests.py copy_wc_url_with_server_excluded */

  /* Delete A/B */
  SVN_ERR(sbox_wc_delete(&b, "A/B"));

  /* A/no not-present but in HEAD */
  SVN_ERR(sbox_wc_copy(&b, "A/mu", "A/no"));
  SVN_ERR(sbox_wc_commit(&b, "A/no"));
  SVN_ERR(sbox_wc_update(&b, "A/no", 1));

  /* A/mu not-present and not in HEAD */
  SVN_ERR(sbox_wc_delete(&b, "A/mu"));
  SVN_ERR(sbox_wc_commit(&b, "A/mu"));

  /* A/D excluded */
  SVN_ERR(sbox_wc_exclude(&b, "A/D"));

  /* This should have created this structure */
  {
    nodes_row_t rows[] = {

      {0, "A",           "normal",           1, "A"},
      {0, "A/B",         "normal",           1, "A/B"},
      {0, "A/B/E",       "normal",           1, "A/B/E"},
      {0, "A/B/E/alpha", "normal",           1, "A/B/E/alpha"},
      {0, "A/B/E/beta",  "normal",           1, "A/B/E/beta"},
      {0, "A/B/F",       "normal",           1, "A/B/F"},
      {0, "A/B/lambda",  "normal",           1, "A/B/lambda"},
      {0, "A/C",         "normal",           1, "A/C"},
      {0, "A/D",         "excluded",         1, "A/D"},
      {0, "A/mu",        "not-present",      3, "A/mu"},
      {0, "A/no",        "not-present",      1, "A/no"},

      {2, "A/B",         "base-deleted",     NO_COPY_FROM},
      {2, "A/B/E",       "base-deleted",     NO_COPY_FROM},
      {2, "A/B/E/alpha", "base-deleted",     NO_COPY_FROM},
      {2, "A/B/E/beta",  "base-deleted",     NO_COPY_FROM},
      {2, "A/B/lambda",  "base-deleted",     NO_COPY_FROM},
      {2, "A/B/F",       "base-deleted",     NO_COPY_FROM},

      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A", rows));
  }

  SVN_ERR(sbox_wc_copy(&b, "A", "A_copied"));

  /* I would expect this behavior, as this copies all layers where possible
     instead of just constructing a top level layer with not-present nodes
     whenever we find a deletion. */
  {
    nodes_row_t rows[] = {

      {1, "A_copied",           "normal",           1, "A"},
      {1, "A_copied/B",         "normal",           1, "A/B"},
      {1, "A_copied/B/E",       "normal",           1, "A/B/E"},
      {1, "A_copied/B/E/alpha", "normal",           1, "A/B/E/alpha"},
      {1, "A_copied/B/E/beta",  "normal",           1, "A/B/E/beta"},
      {1, "A_copied/B/F",       "normal",           1, "A/B/F"},
      {1, "A_copied/B/lambda",  "normal",           1, "A/B/lambda"},
      {1, "A_copied/C",         "normal",           1, "A/C"},
      {1, "A_copied/D",         "excluded",         1, "A/D"},
      {1, "A_copied/mu",        "not-present",      3, "A/mu"},
      {1, "A_copied/no",        "not-present",      1, "A/no"},

      {2, "A_copied/B",         "base-deleted",     NO_COPY_FROM},
      {2, "A_copied/B/E",       "base-deleted",     NO_COPY_FROM},
      {2, "A_copied/B/E/alpha", "base-deleted",     NO_COPY_FROM},
      {2, "A_copied/B/E/beta",  "base-deleted",     NO_COPY_FROM},
      {2, "A_copied/B/lambda",  "base-deleted",     NO_COPY_FROM},
      {2, "A_copied/B/F",       "base-deleted",     NO_COPY_FROM},

      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "A_copied", rows));
  }

  return SVN_NO_ERROR;
}

/* Part of issue #3702, #3865 */
static svn_error_t *
test_case_rename(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;
  apr_hash_t *dirents;

  SVN_ERR(svn_test__sandbox_create(&b, "case_rename", opts, pool));
  SVN_ERR(sbox_add_and_commit_greek_tree(&b));

  SVN_ERR(sbox_wc_move(&b, "A", "a"));
  SVN_ERR(sbox_wc_move(&b, "iota", "iotA"));

  SVN_ERR(svn_io_get_dirents3(&dirents, sbox_wc_path(&b, ""), TRUE, pool, pool));

  /* A shouldn't be there, but a should */
  SVN_TEST_ASSERT(apr_hash_get(dirents, "a", APR_HASH_KEY_STRING));
  SVN_TEST_ASSERT(apr_hash_get(dirents, "A", APR_HASH_KEY_STRING) == NULL);
  /* iota shouldn't be there, but iotA should */
  SVN_TEST_ASSERT(apr_hash_get(dirents, "iotA", APR_HASH_KEY_STRING));
  SVN_TEST_ASSERT(apr_hash_get(dirents, "iota", APR_HASH_KEY_STRING) == NULL);

  return SVN_NO_ERROR;
}

static svn_error_t *
commit_file_external(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "commit_file_external", opts, pool));
  SVN_ERR(sbox_file_write(&b, "f", "this is f\n"));
  SVN_ERR(sbox_wc_add(&b, "f"));
  SVN_ERR(sbox_wc_propset(&b, "svn:externals", "^/f g", ""));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_file_write(&b, "g", "this is f\nmodified via g\n"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 2));

  {
    nodes_row_t rows[] = {
      { 0, "",  "normal",       2, "" },
      { 0, "f", "normal",       2, "f" },
      { 0, "g", "normal",       2, "f", TRUE },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
revert_file_externals(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "revert_file_externals", opts, pool));
  SVN_ERR(sbox_file_write(&b, "f", "this is f\n"));
  SVN_ERR(sbox_wc_add(&b, "f"));
  SVN_ERR(sbox_wc_propset(&b, "svn:externals", "^/f g", ""));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_propset(&b, "svn:externals", "^/f h", ""));
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_propset(&b, "svn:externals", "^/f g", "A"));
  {
    nodes_row_t rows[] = {
      { 0, "",    "normal", 1, "" },
      { 0, "f",   "normal", 1, "f" },
      { 0, "g",   "normal", 1, "f", TRUE },
      { 1, "A",   "normal", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  SVN_ERR(sbox_wc_update(&b, "", 1));
  {
    nodes_row_t rows[] = {
      { 0, "",    "normal",      1, "" },
      { 0, "f",   "normal",      1, "f" },
      { 1, "A",   "normal",      NO_COPY_FROM },
      { 0, "h",   "normal",      1, "f", TRUE },
      { 0, "A/g", "normal",      1, "f", TRUE },

      { 0, "g",   "not-present", 0, "g"},
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  SVN_ERR(sbox_wc_revert(&b, "", svn_depth_infinity));
  {
    nodes_row_t rows[] = {
      { 0, "",    "normal",      1, "" },
      { 0, "f",   "normal",      1, "f" },
      { 0, "h",   "normal",      1, "f", TRUE },
      { 0, "A/g", "normal",      1, "f", TRUE },

      { 0, "g",   "not-present", 0, "g"},
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  SVN_ERR(sbox_wc_update(&b, "", 1));
  {
    nodes_row_t rows[] = {
      { 0, "",    "normal",      1, "" },
      { 0, "f",   "normal",      1, "f" },
      { 0, "g",   "normal",      1, "f", TRUE },

      { 0, "h",   "not-present", 0, "h"},
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  SVN_ERR(sbox_wc_update(&b, "", 1));
  {
    nodes_row_t rows[] = {
      { 0, "",    "normal",      1, "" },
      { 0, "f",   "normal",      1, "f" },
      { 0, "g",   "normal",      1, "f", TRUE },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
copy_file_externals(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "copy_file_externals", opts, pool));
  SVN_ERR(sbox_file_write(&b, "f", "this is f\n"));
  SVN_ERR(sbox_wc_add(&b, "f"));
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_propset(&b, "svn:externals", "^/f g", "A"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_propset(&b, "svn:externals", "^/f g", "A/B"));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  {
    nodes_row_t rows[] = {
      { 0, "",      "normal", 1, "" },
      { 0, "f",     "normal", 1, "f" },
      { 0, "A",     "normal", 1, "A" },
      { 2, "A/B",   "normal", NO_COPY_FROM },
      { 0, "A/g",   "normal", 1, "f", TRUE },
      { 0, "A/B/g", "normal", 1, "f", TRUE },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  SVN_ERR(sbox_wc_copy(&b, "A", "X"));
  {
    nodes_row_t rows[] = {
      { 0, "",      "normal", 1, "" },
      { 0, "f",     "normal", 1, "f" },
      { 0, "A",     "normal", 1, "A" },
      { 2, "A/B",   "normal", NO_COPY_FROM },
      { 0, "A/g",   "normal", 1, "f", TRUE },
      { 0, "A/B/g", "normal", 1, "f", TRUE },
      { 1, "X",     "normal", 1, "A" },
      { 2, "X/B",   "normal", NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  SVN_ERR(sbox_wc_update(&b, "", 1));
  {
    nodes_row_t rows[] = {
      { 0, "",      "normal", 1, "" },
      { 0, "f",     "normal", 1, "f" },
      { 0, "A",     "normal", 1, "A" },
      { 2, "A/B",   "normal", NO_COPY_FROM },
      { 0, "A/g",   "normal", 1, "f", TRUE },
      { 0, "A/B/g", "normal", 1, "f", TRUE },
      { 1, "X",     "normal", 1, "A" },
      { 2, "X/B",   "normal", NO_COPY_FROM },
      { 0, "X/g",   "normal", 1, "f", TRUE },
      { 0, "X/B/g", "normal", 1, "f", TRUE },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  SVN_ERR(sbox_wc_delete(&b, "X"));
  {
    nodes_row_t rows[] = {
      { 0, "",      "normal", 1, "" },
      { 0, "f",     "normal", 1, "f" },
      { 0, "A",     "normal", 1, "A" },
      { 2, "A/B",   "normal", NO_COPY_FROM },
      { 0, "A/g",   "normal", 1, "f", TRUE },
      { 0, "A/B/g", "normal", 1, "f", TRUE },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  SVN_ERR(sbox_wc_update(&b, "", 1));
  {
    nodes_row_t rows[] = {
      { 0, "",      "normal", 1, "" },
      { 0, "f",     "normal", 1, "f" },
      { 0, "A",     "normal", 1, "A" },
      { 2, "A/B",   "normal", NO_COPY_FROM },
      { 0, "A/g",   "normal", 1, "f", TRUE },
      { 0, "A/B/g", "normal", 1, "f", TRUE },
      { 0 }
    };
    SVN_ERR(check_db_rows(&b, "", rows));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
copy_wc_wc_server_excluded(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;
  nodes_row_t before[] = {
    {0, "",      "normal",  1, ""},
    {0, "A",     "normal",  1, "A"},
    {0, "A/B",   "normal",  1, "A/B"},
    {0, "A/B/E", "server-excluded",  1, "A/B/E"},
    {0}
  };
  nodes_row_t after[] = {
    {0, "",      "normal",  1, ""},
    {0, "A",     "normal",      1, "A"},
    {0, "A/B",   "normal",      1, "A/B"},
    {0, "A/B/E", "server-excluded",      1, "A/B/E"},
    {1, "X",     "normal",      1, "A"},
    {1, "X/B",   "normal",      1, "A/B"},
    {1, "X/B/E", "incomplete",  1, "A/B/E"},
    {0}
  };
  svn_error_t *err;

  SVN_ERR(svn_test__sandbox_create(&b, "copy_wc_wc_server_excluded", opts, pool));
  SVN_ERR(insert_dirs(&b, before));
  SVN_ERR(check_db_rows(&b, "", before));
  SVN_ERR(sbox_disk_mkdir(&b, "A"));
  err = sbox_wc_copy(&b, "A", "X");
  SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_PATH_UNEXPECTED_STATUS);
  SVN_ERR(check_db_rows(&b, "", after));

  return SVN_NO_ERROR;
}

/* Issue 4040 */
static svn_error_t *
incomplete_switch(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "incomplete_switch", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_copy(&b, "A", "X"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_copy(&b, "A", "X/A"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_delete(&b, "X/A"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  {
    /* Interrupted switch from A@1 to X@3 */
    nodes_row_t before[] = {
      {0, "",      "incomplete", 3, "X"},
      {0, "A",     "incomplete", 3, "X/A"},
      {0, "A/B",   "incomplete", 3, "X/A/B"},
      {0, "A/B/C", "incomplete", 3, "X/A/B/C"},
      {0, "B",     "normal",     1, "A/B"},
      {0, "B/C",   "normal",     1, "A/B/C"},
      {0, "B/C/D", "normal",     1, "A/B/C/D"},
      {0}
    };

    nodes_row_t after_update[] = {
      {0, "",      "normal", 4, "X"},
      {0, "B",     "normal", 4, "A/B"},
      {0, "B/C",   "normal", 4, "A/B/C"},
      {0, "B/C/D", "normal", 4, "A/B/C/D"},
      {0}
    };

    SVN_ERR(insert_dirs(&b, before));
    SVN_ERR(svn_io_remove_dir2(sbox_wc_path(&b, "A/B/C/D"), FALSE,
                               NULL, NULL, pool));
    SVN_ERR(check_db_rows(&b, "", before));
    SVN_ERR(sbox_wc_update(&b, "", 4));
    SVN_ERR(check_db_rows(&b, "", after_update));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
nested_moves_child_first(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "nested_moves_child_first", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  {
    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}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }
  SVN_ERR(sbox_wc_move(&b, "A/B/C", "A/B/C2"));
  {
    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"},
      {3, "A/B/C",  "base-deleted", NO_COPY_FROM, "A/B/C2"},
      {3, "A/B/C2", "normal",       1, "A/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }
  SVN_ERR(sbox_wc_move(&b, "A/B", "A/B2"));
  {
    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"},
      {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, "A/B2/C2"},
      {3, "A/B2/C2", "normal",       1, "A/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }
  SVN_ERR(sbox_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, "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, "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, "A2/B2/C2"},
      {3, "A2/B2/C2","normal",       1, "A/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Revert should leave the A to A2 move */
  SVN_ERR(sbox_wc_revert(&b, "A2/B2", svn_depth_infinity));
  SVN_ERR(sbox_wc_revert(&b, "A2/B", svn_depth_infinity));
  {
    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"},
      {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},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
nested_moves_child_last(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "nested_moves_child_last", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  {
    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}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }
  SVN_ERR(sbox_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, "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},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }
  SVN_ERR(sbox_wc_move(&b, "A2/B", "A2/B2"));
  {
    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"},
      {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, "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},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }
  SVN_ERR(sbox_wc_move(&b, "A2/B2/C", "A2/B2/C2"));
  {
    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"},
      {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, "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, "A2/B2/C2"},
      {3, "A2/B2/C2","normal",       1, "A/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Revert should leave the A to A2 move */
  SVN_ERR(sbox_wc_revert(&b, "A2/B2", svn_depth_infinity));
  SVN_ERR(sbox_wc_revert(&b, "A2/B", svn_depth_infinity));
  {
    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"},
      {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},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_in_copy(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_in_copy", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_copy(&b, "A", "A2"));

  {
    nodes_row_t nodes[] = {
      {0, "",     "normal", 1, ""},
      {0, "A",    "normal", 1, "A"},
      {0, "A/B",  "normal", 1, "A/B"},
      {1, "A2",   "normal", 1, "A"},
      {1, "A2/B", "normal", 1, "A/B"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }
  SVN_ERR(sbox_wc_move(&b, "A2/B", "A2/B2"));
  {
    nodes_row_t nodes[] = {
      {0, "",      "normal",       1, ""},
      {0, "A",     "normal",       1, "A"},
      {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, "A2/B2"},
      {2, "A2/B2", "normal",       1, "A/B", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_in_replace(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_in_replace", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "X"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/B"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_delete(&b, "A"));
  SVN_ERR(sbox_wc_copy(&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/B", "normal", 1, "X/B"},
      {1, "A",   "normal", 1, "X"},
      {1, "A/B", "normal", 1, "X/B"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }
  SVN_ERR(sbox_wc_move(&b, "A/B", "A/B2"));
  {
    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/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, "A/B2"},
      {2, "A/B2", "normal",       1, "X/B", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
copy_a_move(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "copy_a_move", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  {
    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}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }
  SVN_ERR(sbox_wc_move(&b, "A/B/C", "A/C2"));
  {
    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"},
      {2, "A/C2",  "normal",       1, "A/B/C", MOVED_HERE},
      {3, "A/B/C", "base-deleted", NO_COPY_FROM, "A/C2"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }
  /* Copying a move doesn't copy any moved-to/here artifacts, which
     means that moving inside a copy is not the same as copying
     something that contains a move?  Is this behaviour correct? */
  SVN_ERR(sbox_wc_copy(&b, "A", "A2"));
  {
    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"},
      {2, "A/C2",   "normal",       1, "A/B/C", MOVED_HERE},
      {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"},   /* MOVED_HERE? */
      {3, "A2/B/C", "base-deleted", NO_COPY_FROM}, /* "A2/C2"? */
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_to_swap(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_to_swap", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "X"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/Y"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A/B", "X/B"));
  SVN_ERR(sbox_wc_move(&b, "X/Y", "A/Y"));

  {
    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"},
      {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, "A/Y"},
      {2, "X/B", "normal",       1, "A/B", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_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(sbox_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(sbox_wc_move(&b, "A2", "X"));

  {
    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, "X", TRUE},
      {1, "A/Y", "normal",       1, "X/Y", MOVED_HERE},
      {1, "A/B", "base-deleted", NO_COPY_FROM},
      {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, "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}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Revert and try in different order */
  SVN_ERR(sbox_wc_revert(&b, "", svn_depth_infinity));

  SVN_ERR(sbox_wc_move(&b, "A", "A2"));
  SVN_ERR(sbox_wc_move(&b, "X", "A"));
  SVN_ERR(sbox_wc_move(&b, "A2", "X"));

  {
    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, "X", TRUE},
      {1, "A/Y", "normal",       1, "X/Y", MOVED_HERE},
      {1, "A/B", "base-deleted", NO_COPY_FROM},
      {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}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_move(&b, "A/Y", "X/Y"));
  SVN_ERR(sbox_wc_move(&b, "X/B", "A/B"));

  {
    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, "X", TRUE},
      {1, "A/Y", "normal",       1, "X/Y", MOVED_HERE},
      {1, "A/B", "base-deleted", NO_COPY_FROM},
      {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, "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}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* And move this last bit back and check if the db state is restored */
  SVN_ERR(sbox_wc_move(&b, "A/B", "X/B"));
  SVN_ERR(sbox_wc_move(&b, "X/Y", "A/Y"));

  {
    /* Exact the same as before the initial moves */
    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, "X", TRUE},
      {1, "A/Y", "normal",       1, "X/Y", MOVED_HERE},
      {1, "A/B", "base-deleted", NO_COPY_FROM},
      {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}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* And try to undo the rest */
  SVN_ERR(sbox_wc_move(&b, "A", "A2"));
  SVN_ERR(sbox_wc_move(&b, "X", "A"));
  SVN_ERR(sbox_wc_move(&b, "A2", "X"));

  {
    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"},

      {0}
    };

    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
revert_nested_move(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;
  nodes_row_t nodes_A_moved[] = {
    {0, "",       "normal",       1, ""},
    {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},
    {0}
  };
  nodes_row_t nodes_AB_moved[] = {
    {0, "",        "normal",       1, ""},
    {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, "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},
    {0}
  };
  nodes_row_t nodes_ABC_moved[] = {
    {0, "",         "normal",       1, ""},
    {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, "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, "A2/B2/C2"},
    {3, "A2/B2/C2", "normal",       1, "A/B/C", MOVED_HERE},
    {0}
  };
  nodes_row_t nodes_AB_moved_C_copied[] = {
    {0, "",         "normal",       1, ""},
    {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, "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/C2", "normal",       1, "A/B/C"},
    {0}
  };
  nodes_row_t nodes_AC_moved_B_copied[] = {
    {0, "",         "normal",       1, ""},
    {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/B2",    "normal",       1, "A/B"},
    {2, "A2/B2/C",  "normal",       1, "A/B/C"},
    {3, "A2/B2/C",  "base-deleted", NO_COPY_FROM, "A2/B2/C2"},
    {3, "A2/B2/C2", "normal",       1, "A/B/C", MOVED_HERE},
    {0}
  };

  SVN_ERR(svn_test__sandbox_create(&b, "revert_nested_move", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A", "A2"));
  SVN_ERR(check_db_rows(&b, "", nodes_A_moved));

  SVN_ERR(sbox_wc_move(&b, "A2/B", "A2/B2"));
  SVN_ERR(check_db_rows(&b, "", nodes_AB_moved));

  SVN_ERR(sbox_wc_move(&b, "A2/B2/C", "A2/B2/C2"));
  SVN_ERR(check_db_rows(&b, "", nodes_ABC_moved));

  SVN_ERR(sbox_wc_revert(&b, "A2/B", svn_depth_infinity));
  SVN_ERR(sbox_wc_revert(&b, "A2/B2", svn_depth_infinity));
  SVN_ERR(check_db_rows(&b, "", nodes_A_moved));

  SVN_ERR(sbox_wc_move(&b, "A2/B", "A2/B2"));
  SVN_ERR(sbox_wc_move(&b, "A2/B2/C", "A2/B2/C2"));
  SVN_ERR(check_db_rows(&b, "", nodes_ABC_moved));

  SVN_ERR(sbox_wc_revert(&b, "A2/B2/C", svn_depth_empty));
  SVN_ERR(check_db_rows(&b, "", nodes_AB_moved_C_copied));
  SVN_ERR(sbox_wc_revert(&b, "A2/B2/C2", svn_depth_infinity));
  SVN_ERR(check_db_rows(&b, "", nodes_AB_moved));

  SVN_ERR(sbox_wc_move(&b, "A2/B2/C", "A2/B2/C2"));
  SVN_ERR(check_db_rows(&b, "", nodes_ABC_moved));

  SVN_ERR(sbox_wc_revert(&b, "A2/B2/C", svn_depth_infinity));
  SVN_ERR(check_db_rows(&b, "", nodes_AB_moved_C_copied));
  SVN_ERR(sbox_wc_revert(&b, "A2/B2/C2", svn_depth_infinity));
  SVN_ERR(check_db_rows(&b, "", nodes_AB_moved));

  SVN_ERR(sbox_wc_revert(&b, "A2/B", svn_depth_infinity));
  SVN_ERR(sbox_wc_revert(&b, "A2/B2", svn_depth_infinity));
  SVN_ERR(check_db_rows(&b, "", nodes_A_moved));

  /* Check moves in reverse order */
  SVN_ERR(sbox_wc_revert(&b, "", svn_depth_infinity));
  SVN_ERR(sbox_wc_move(&b, "A/B/C", "A/B/C2"));
  SVN_ERR(sbox_wc_move(&b, "A/B", "A/B2"));
  SVN_ERR(sbox_wc_move(&b, "A", "A2"));
  SVN_ERR(check_db_rows(&b, "", nodes_ABC_moved));

  SVN_ERR(sbox_wc_revert(&b, "A2/B", svn_depth_infinity));
  SVN_ERR(check_db_rows(&b, "", nodes_AC_moved_B_copied));

  return SVN_NO_ERROR;
}

static svn_error_t *
move_on_move(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_on_move", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "X"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/B"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A/B", "B2"));
  SVN_ERR(sbox_wc_delete(&b, "A"));
  SVN_ERR(sbox_wc_copy(&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/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", FALSE, "B2"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_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"},
      {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", FALSE, "B2"},
      {2, "A/B",      "base-deleted", NO_COPY_FROM, "B3"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_on_move2(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_on_move2", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "X"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/B"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A", "A2"));
  SVN_ERR(sbox_wc_delete(&b, "A"));
  SVN_ERR(sbox_wc_copy(&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/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", FALSE, "A2"},
      {1, "A/B",      "normal",       1, "X/B"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_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"},
      {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", 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));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_added(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_added", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_move(&b, "A", "A2"));
  SVN_ERR(sbox_wc_mkdir(&b, "A2/B/C2"));

  /* Both A2/B/C and A2/B/C2 are simple adds inside the move.  It
     doesn't seem right for A2/B/C to be marked moved_here. */
  {
    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},
      {3, "A2/B/C",   "normal",       NO_COPY_FROM},
      {3, "A2/B/C2",  "normal",       NO_COPY_FROM},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

/* Test the result of 'update' when the incoming changes are inside a
 * directory that is locally moved. */
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));

  /* r1: Create files 'f', 'h' */
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_file_write(&b, "A/B/f", "r1 content\n"));
  SVN_ERR(sbox_file_write(&b, "A/B/h", "r1 content\n"));
  SVN_ERR(sbox_wc_add(&b, "A/B/f"));
  SVN_ERR(sbox_wc_add(&b, "A/B/h"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  /* r2: Modify 'f' */
  SVN_ERR(sbox_file_write(&b, "A/B/f", "r1 content\nr2 content\n"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  /* r3: Delete 'h', add 'g' */
  SVN_ERR(sbox_file_write(&b, "A/B/g", "r3 content\n"));
  SVN_ERR(sbox_wc_add(&b, "A/B/g"));
  SVN_ERR(sbox_wc_delete(&b, "A/B/h"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  /* r4: Add a new subtree 'X' */
  SVN_ERR(sbox_wc_mkdir(&b, "X"));
  SVN_ERR(sbox_file_write(&b, "X/f", "r4 content\n"));
  SVN_ERR(sbox_file_write(&b, "X/g", "r4 content\n"));
  SVN_ERR(sbox_file_write(&b, "X/h", "r4 content\n"));
  SVN_ERR(sbox_wc_add(&b, "X/f"));
  SVN_ERR(sbox_wc_add(&b, "X/g"));
  SVN_ERR(sbox_wc_add(&b, "X/h"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  /* r5: Add a subtree 'A/B/C' */
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_update(&b, "", 1));

  /* A is single-revision so A2 is a single-revision copy */
  SVN_ERR(sbox_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, "A/B/f",    "normal",       1, "A/B/f"},
      {0, "A/B/h",    "normal",       1, "A/B/h"},
      {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
      {1, "A/B",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/f",    "base-deleted", NO_COPY_FROM},
      {1, "A/B/h",    "base-deleted", NO_COPY_FROM},
      {1, "A2",       "normal",       1, "A", MOVED_HERE},
      {1, "A2/B",     "normal",       1, "A/B", MOVED_HERE},
      {1, "A2/B/f",   "normal",       1, "A/B/f", MOVED_HERE},
      {1, "A2/B/h",   "normal",       1, "A/B/h", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Update causes a tree-conflict on A due to incoming text-change. */
  SVN_ERR(sbox_wc_update(&b, "", 2));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       2, ""},
      {0, "A",        "normal",       2, "A"},
      {0, "A/B",      "normal",       2, "A/B"},
      {0, "A/B/f",    "normal",       2, "A/B/f"},
      {0, "A/B/h",    "normal",       2, "A/B/h"},
      {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
      {1, "A/B",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/f",    "base-deleted", NO_COPY_FROM},
      {1, "A/B/h",    "base-deleted", NO_COPY_FROM},
      {1, "A2",       "normal",       1, "A", MOVED_HERE},
      {1, "A2/B",     "normal",       1, "A/B", MOVED_HERE},
      {1, "A2/B/f",   "normal",       1, "A/B/f", MOVED_HERE},
      {1, "A2/B/h",   "normal",       1, "A/B/h", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Resolve should update the move. */
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       2, ""},
      {0, "A",        "normal",       2, "A"},
      {0, "A/B",      "normal",       2, "A/B"},
      {0, "A/B/f",    "normal",       2, "A/B/f"},
      {0, "A/B/h",    "normal",       2, "A/B/h"},
      {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
      {1, "A/B",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/f",    "base-deleted", NO_COPY_FROM},
      {1, "A/B/h",    "base-deleted", NO_COPY_FROM},
      {1, "A2",       "normal",       2, "A", MOVED_HERE},
      {1, "A2/B",     "normal",       2, "A/B", MOVED_HERE},
      {1, "A2/B/f",   "normal",       2, "A/B/f", MOVED_HERE},
      {1, "A2/B/h",   "normal",       2, "A/B/h", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Update causes a tree-conflict on due to incoming add. */
  SVN_ERR(sbox_wc_update(&b, "", 3));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       3, ""},
      {0, "A",        "normal",       3, "A"},
      {0, "A/B",      "normal",       3, "A/B"},
      {0, "A/B/f",    "normal",       3, "A/B/f"},
      {0, "A/B/g",    "normal",       3, "A/B/g"},
      {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
      {1, "A/B",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/f",    "base-deleted", NO_COPY_FROM},
      {1, "A/B/g",    "base-deleted", NO_COPY_FROM},
      {1, "A2",       "normal",       2, "A", MOVED_HERE},
      {1, "A2/B",     "normal",       2, "A/B", MOVED_HERE},
      {1, "A2/B/f",   "normal",       2, "A/B/f", MOVED_HERE},
      {1, "A2/B/h",   "normal",       2, "A/B/h", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       3, ""},
      {0, "A",        "normal",       3, "A"},
      {0, "A/B",      "normal",       3, "A/B"},
      {0, "A/B/f",    "normal",       3, "A/B/f"},
      {0, "A/B/g",    "normal",       3, "A/B/g"},
      {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
      {1, "A/B",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/f",    "base-deleted", NO_COPY_FROM},
      {1, "A/B/g",    "base-deleted", NO_COPY_FROM},
      {1, "A2",       "normal",       3, "A", MOVED_HERE},
      {1, "A2/B",     "normal",       3, "A/B", MOVED_HERE},
      {1, "A2/B/f",   "normal",       3, "A/B/f", MOVED_HERE},
      {1, "A2/B/g",   "normal",       3, "A/B/g", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_delete(&b, "A2/B"));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       3, ""},
      {0, "A",        "normal",       3, "A"},
      {0, "A/B",      "normal",       3, "A/B"},
      {0, "A/B/f",    "normal",       3, "A/B/f"},
      {0, "A/B/g",    "normal",       3, "A/B/g"},
      {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
      {1, "A/B",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/f",    "base-deleted", NO_COPY_FROM},
      {1, "A/B/g",    "base-deleted", NO_COPY_FROM},
      {1, "A2",       "normal",       3, "A", MOVED_HERE},
      {1, "A2/B",     "normal",       3, "A/B", MOVED_HERE},
      {1, "A2/B/f",   "normal",       3, "A/B/f", MOVED_HERE},
      {1, "A2/B/g",   "normal",       3, "A/B/g", MOVED_HERE},
      {2, "A2/B",     "base-deleted", NO_COPY_FROM},
      {2, "A2/B/f",   "base-deleted", NO_COPY_FROM},
      {2, "A2/B/g",   "base-deleted", NO_COPY_FROM},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_update(&b, "", 2));
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       2, ""},
      {0, "A",        "normal",       2, "A"},
      {0, "A/B",      "normal",       2, "A/B"},
      {0, "A/B/f",    "normal",       2, "A/B/f"},
      {0, "A/B/h",    "normal",       2, "A/B/h"},
      {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
      {1, "A/B",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/f",    "base-deleted", NO_COPY_FROM},
      {1, "A/B/h",    "base-deleted", NO_COPY_FROM},
      {1, "A2",       "normal",       2, "A", MOVED_HERE},
      {1, "A2/B",     "normal",       2, "A/B", MOVED_HERE},
      {1, "A2/B/f",   "normal",       2, "A/B/f", MOVED_HERE},
      {1, "A2/B/h",   "normal",       2, "A/B/h", MOVED_HERE},
      {2, "A2/B",     "base-deleted", NO_COPY_FROM},
      {2, "A2/B/f",   "base-deleted", NO_COPY_FROM},
      {2, "A2/B/h",   "base-deleted", NO_COPY_FROM},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_update(&b, "X", 4));
  SVN_ERR(sbox_wc_copy(&b, "X", "A2/B"));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       2, ""},
      {0, "A",        "normal",       2, "A"},
      {0, "A/B",      "normal",       2, "A/B"},
      {0, "A/B/f",    "normal",       2, "A/B/f"},
      {0, "A/B/h",    "normal",       2, "A/B/h"},
      {0, "X",        "normal",       4, "X"},
      {0, "X/f",      "normal",       4, "X/f"},
      {0, "X/g",      "normal",       4, "X/g"},
      {0, "X/h",      "normal",       4, "X/h"},
      {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
      {1, "A/B",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/f",    "base-deleted", NO_COPY_FROM},
      {1, "A/B/h",    "base-deleted", NO_COPY_FROM},
      {1, "A2",       "normal",       2, "A", MOVED_HERE},
      {1, "A2/B",     "normal",       2, "A/B", MOVED_HERE},
      {1, "A2/B/f",   "normal",       2, "A/B/f", MOVED_HERE},
      {1, "A2/B/h",   "normal",       2, "A/B/h", MOVED_HERE},
      {2, "A2/B",     "normal",       4, "X"},
      {2, "A2/B/f",   "normal",       4, "X/f"},
      {2, "A2/B/g",   "normal",       4, "X/g"},
      {2, "A2/B/h",   "normal",       4, "X/h"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_update(&b, "", 4));
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       4, ""},
      {0, "A",        "normal",       4, "A"},
      {0, "A/B",      "normal",       4, "A/B"},
      {0, "A/B/f",    "normal",       4, "A/B/f"},
      {0, "A/B/g",    "normal",       4, "A/B/g"},
      {0, "X",        "normal",       4, "X"},
      {0, "X/f",      "normal",       4, "X/f"},
      {0, "X/g",      "normal",       4, "X/g"},
      {0, "X/h",      "normal",       4, "X/h"},
      {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
      {1, "A/B",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/f",    "base-deleted", NO_COPY_FROM},
      {1, "A/B/g",    "base-deleted", NO_COPY_FROM},
      {1, "A2",       "normal",       4, "A", MOVED_HERE},
      {1, "A2/B",     "normal",       4, "A/B", MOVED_HERE},
      {1, "A2/B/f",   "normal",       4, "A/B/f", MOVED_HERE},
      {1, "A2/B/g",   "normal",       4, "A/B/g", MOVED_HERE},
      {2, "A2/B",     "normal",       4, "X"},
      {2, "A2/B/f",   "normal",       4, "X/f"},
      {2, "A2/B/g",   "normal",       4, "X/g"},
      {2, "A2/B/h",   "normal",       4, "X/h"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_update(&b, "", 5));
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       5, ""},
      {0, "A",        "normal",       5, "A"},
      {0, "A/B",      "normal",       5, "A/B"},
      {0, "A/B/f",    "normal",       5, "A/B/f"},
      {0, "A/B/g",    "normal",       5, "A/B/g"},
      {0, "A/B/C",    "normal",       5, "A/B/C"},
      {0, "X",        "normal",       5, "X"},
      {0, "X/f",      "normal",       5, "X/f"},
      {0, "X/g",      "normal",       5, "X/g"},
      {0, "X/h",      "normal",       5, "X/h"},
      {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
      {1, "A/B",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/f",    "base-deleted", NO_COPY_FROM},
      {1, "A/B/g",    "base-deleted", NO_COPY_FROM},
      {1, "A/B/C",    "base-deleted", NO_COPY_FROM},
      {1, "A2",       "normal",       5, "A", MOVED_HERE},
      {1, "A2/B",     "normal",       5, "A/B", MOVED_HERE},
      {1, "A2/B/f",   "normal",       5, "A/B/f", MOVED_HERE},
      {1, "A2/B/g",   "normal",       5, "A/B/g", MOVED_HERE},
      {1, "A2/B/C",   "normal",       5, "A/B/C", MOVED_HERE},
      {2, "A2/B",     "normal",       4, "X"},
      {2, "A2/B/f",   "normal",       4, "X/f"},
      {2, "A2/B/g",   "normal",       4, "X/g"},
      {2, "A2/B/h",   "normal",       4, "X/h"},
      {2, "A2/B/C",   "base-deleted", NO_COPY_FROM},
      {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(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "A2"));
  SVN_ERR(sbox_wc_mkdir(&b, "A2/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "C2"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A2", "X"));
  SVN_ERR(sbox_wc_move(&b, "X/B", "Z"));
  SVN_ERR(sbox_wc_move(&b, "A/B", "X/B"));
  SVN_ERR(sbox_wc_move(&b, "X/B/C", "Y"));
  SVN_ERR(sbox_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, sbox_wc_path(&b, "C2"),
                                   pool, pool));
  SVN_TEST_STRING_ASSERT(moved_to_abspath, sbox_wc_path(&b, "X/B/C"));
  SVN_TEST_STRING_ASSERT(moved_to_op_root_abspath, sbox_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, sbox_wc_path(&b, "A/B"),
                                   pool, pool));
  SVN_TEST_STRING_ASSERT(moved_to_abspath, sbox_wc_path(&b, "X/B"));
  SVN_TEST_STRING_ASSERT(moved_to_op_root_abspath, sbox_wc_path(&b, "X/B"));

  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
                                     sbox_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, sbox_wc_path(&b, "A/B/C"),
                                   pool, pool));
  SVN_TEST_STRING_ASSERT(moved_to_abspath, sbox_wc_path(&b, "X/B/C"));
  SVN_TEST_STRING_ASSERT(moved_to_op_root_abspath, sbox_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, sbox_wc_path(&b, "A2"),
                                   pool, pool));
  SVN_TEST_STRING_ASSERT(moved_to_abspath, sbox_wc_path(&b, "X"));
  SVN_TEST_STRING_ASSERT(moved_to_op_root_abspath, sbox_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, sbox_wc_path(&b, "A2/B"),
                                   pool, pool));
  SVN_TEST_STRING_ASSERT(moved_to_abspath, sbox_wc_path(&b, "X/B"));
  SVN_TEST_STRING_ASSERT(moved_to_op_root_abspath, sbox_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(sbox_wc_mkdir(&b, "A1"));
  SVN_ERR(sbox_wc_mkdir(&b, "A1/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A1/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "A1/B/C/D"));
  SVN_ERR(sbox_wc_mkdir(&b, "A1/B/C/D/E"));
  SVN_ERR(sbox_wc_mkdir(&b, "A2"));
  SVN_ERR(sbox_wc_mkdir(&b, "A2/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A2/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "A2/B/C/D"));
  SVN_ERR(sbox_wc_mkdir(&b, "A2/B/C/D/E"));
  SVN_ERR(sbox_wc_mkdir(&b, "A3"));
  SVN_ERR(sbox_wc_mkdir(&b, "A3/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A3/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "A3/B/C/D"));
  SVN_ERR(sbox_wc_mkdir(&b, "A3/B/C/D/E"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_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(sbox_wc_move(&b, "A1", "X"));
  SVN_ERR(sbox_wc_move(&b, "A2", "A1"));
  SVN_ERR(sbox_wc_move(&b, "A3", "A2"));
  SVN_ERR(sbox_wc_move(&b, "X", "A3"));
  SVN_ERR(sbox_wc_move(&b, "A1/B", "X"));
  SVN_ERR(sbox_wc_move(&b, "A2/B", "A1/B"));
  SVN_ERR(sbox_wc_move(&b, "A3/B", "A2/B"));
  SVN_ERR(sbox_wc_move(&b, "X", "A3/B"));
  SVN_ERR(sbox_wc_move(&b, "A1/B/C/D", "X"));
  SVN_ERR(sbox_wc_move(&b, "A2/B/C/D", "A1/B/C/D"));
  SVN_ERR(sbox_wc_move(&b, "A3/B/C/D", "A2/B/C/D"));
  SVN_ERR(sbox_wc_move(&b, "X", "A3/B/C/D"));
  SVN_ERR(sbox_wc_move(&b, "A1/B/C/D/E", "X"));
  SVN_ERR(sbox_wc_move(&b, "A2/B/C/D/E", "A1/B/C/D/E"));
  SVN_ERR(sbox_wc_move(&b, "A3/B/C/D/E", "A2/B/C/D/E"));
  SVN_ERR(sbox_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,
                                     sbox_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,
                                     sbox_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,
                                     sbox_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,
                                     sbox_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,
                                     sbox_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);

  SVN_ERR(sbox_wc_delete(&b, "A3/B/C/D/E"));
  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
                                     sbox_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_TEST_ASSERT(moved_tos->nelts == 3);

  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(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/D"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/D/E"));
  SVN_ERR(sbox_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, "A/D",   "normal", 2, "A/D"},
      {0, "A/D/E", "normal", 3, "A/D/E"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* We don't allow mixed-rev move in 1.8 and the command line client
     will return an error, but for compatibility with 1.7 move has an
     allow_mixed_revisions=TRUE flag which is being used here so the
     move transforms automatically into copy+delete.  This test was
     written before that transforming was implemented so still expects
     some move information. */
  SVN_ERR(sbox_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"},
      {0, "A/D",   "normal",       2, "A/D"},
      {0, "A/D/E", "normal",       3, "A/D/E"},
      {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, "A/D",   "base-deleted", NO_COPY_FROM},
      {1, "A/D/E", "base-deleted", NO_COPY_FROM},
      {1, "X",     "normal",       1, "A", MOVED_HERE},
      {1, "X/B",   "not-present",  2, "A/B"},
      {1, "X/D",   "not-present",  2, "A/D"},
      {2, "X/B",   "normal",       2, "A/B"},
      {2, "X/B/C", "not-present",  3, "A/B/C"},
      {2, "X/D",   "normal",       2, "A/D"},
      {2, "X/D/E", "not-present",  3, "A/D/E"},
      {3, "X/B/C", "normal",       3, "A/B/C"},
      {3, "X/D/E", "normal",       3, "A/D/E"},

      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* ### These values PASS but I'm not sure they are correct. */
  /* A/B/C doesn't exist as X/B/C at op depth 1, but is reported */
  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
                                     sbox_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,
                                     sbox_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,
                                     sbox_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(sbox_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"},
      {0, "A/D",   "normal",       2, "A/D"},
      {0, "A/D/E", "normal",       3, "A/D/E"},
      {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, "A/D",   "base-deleted", NO_COPY_FROM},
      {1, "A/D/E", "base-deleted", NO_COPY_FROM},
      {1, "X",     "normal",       1, "A", MOVED_HERE},
      {1, "X/B",   "not-present",  2, "A/B"},
      {1, "X/D",   "not-present",  2, "A/D"},
      {2, "X/D",   "normal",       2, "A/D"},
      {2, "X/D/E", "not-present",  3, "A/D/E"},
      {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"},
      {3, "X/D/E", "normal",       3, "A/D/E"},

      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* A/B/C still doesn't exist as X/B/C at op depth 1 */
  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
                                     sbox_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);

  /* A/B doesn't exist exist as X/B and the move to Y can't be tracked in
     the current scheme */
  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
                                     sbox_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,
                                     sbox_wc_path(&b, "A"), pool, pool));
  SVN_ERR(check_moved_to(moved_tos, 0, 1, "X"));
  SVN_TEST_ASSERT(moved_tos->nelts == 1);


  SVN_ERR(sbox_wc_mkdir(&b, "Z"));
  SVN_ERR(sbox_wc_commit(&b, "Z")); /* r4 */

  SVN_ERR(sbox_wc_update(&b, "", 4));

  {
    nodes_row_t nodes[] = {
      {0, "",      "normal",       4, ""},
      {0, "A",     "normal",       4, "A"},
      {0, "A/B",   "normal",       4, "A/B"},
      {0, "A/B/C", "normal",       4, "A/B/C"},
      {0, "A/D",   "normal",       4, "A/D"},
      {0, "A/D/E", "normal",       4, "A/D/E"},
      {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, "A/D",   "base-deleted", NO_COPY_FROM},
      {1, "A/D/E", "base-deleted", NO_COPY_FROM},
      /* X is expanded on update. The not-present nodes are now here */
      {1, "X",     "normal",       4, "A", MOVED_HERE},
      {1, "X/B",   "normal",       4, "A/B", MOVED_HERE},
      {1, "X/B/C", "normal",       4, "A/B/C", MOVED_HERE},
      {1, "X/D",   "normal",       4, "A/D", MOVED_HERE},
      {1, "X/D/E", "normal",       4, "A/D/E", MOVED_HERE},
      {2, "X/D",   "normal",       2, "A/D"},
      {2, "X/D/E", "not-present",  3, "A/D/E"},
      {2, "X/Y",   "normal",       2, "A/B"},
      {2, "X/Y/C", "not-present",  NO_COPY_FROM},
      {3, "X/D/E", "normal",       3, "A/D/E"},
      {3, "X/Y/C", "normal",       3, "A/B/C"},

      {0, "Z",     "normal",       4, "Z"},
      {0}
    };

    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db,
                                     sbox_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,
                                     sbox_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,
                                     sbox_wc_path(&b, "A"), pool, pool));
  SVN_ERR(check_moved_to(moved_tos, 0, 1, "X"));
  SVN_TEST_ASSERT(moved_tos->nelts == 1);

  {
    conflict_info_t conflicts[] = {
      { "X/D", FALSE, FALSE, {0 /* ### Needs fixing */} },
      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }

  return SVN_NO_ERROR;
}

/* Test the result of 'update' when the incoming changes are inside a
 * directory that is locally moved. */
static svn_error_t *
update_prop_mod_into_moved(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "update_prop_mod_into_moved", opts, pool));

  /* r1: Create files 'f', 'h' */
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_file_write(&b, "A/B/f", "r1 content\n"));
  SVN_ERR(sbox_file_write(&b, "A/B/h", "r1 content\n"));
  SVN_ERR(sbox_wc_add(&b, "A/B/f"));
  SVN_ERR(sbox_wc_add(&b, "A/B/h"));
  SVN_ERR(sbox_wc_propset(&b, "pd", "f1", "A/B/f"));
  SVN_ERR(sbox_wc_propset(&b, "pn", "f1", "A/B/f"));
  SVN_ERR(sbox_wc_propset(&b, "pm", "f1", "A/B/f"));
  SVN_ERR(sbox_wc_propset(&b, "p", "h1", "A/B/h"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  /* r2: Modify 'f'. Delete prop 'pd', modify prop 'pm', add prop 'pa',
   * leave prop 'pn' unchanged. */
  SVN_ERR(sbox_file_write(&b, "A/B/f", "r1 content\nr2 content\n"));
  SVN_ERR(sbox_wc_propset(&b, "pd", NULL, "A/B/f"));
  SVN_ERR(sbox_wc_propset(&b, "pm", "f2", "A/B/f"));
  SVN_ERR(sbox_wc_propset(&b, "pa", "f2", "A/B/f"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  /* r3: Delete 'h', add 'g' */
  SVN_ERR(sbox_file_write(&b, "A/B/g", "r3 content\n"));
  SVN_ERR(sbox_wc_add(&b, "A/B/g"));
  SVN_ERR(sbox_wc_propset(&b, "p", "g3", "A/B/g"));
  SVN_ERR(sbox_wc_delete(&b, "A/B/h"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_update(&b, "", 1));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       1, ""},
      {0, "A",        "normal",       1, "A"},
      {0, "A/B",      "normal",       1, "A/B"},
      {0, "A/B/f",    "normal",       1, "A/B/f", NOT_MOVED,  "pd,pm,pn"},
      {0, "A/B/h",    "normal",       1, "A/B/h", NOT_MOVED,  "p"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* A is single-revision so A2 is a single-revision copy */
  SVN_ERR(sbox_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, "A/B/f",    "normal",       1, "A/B/f", NOT_MOVED,  "pd,pm,pn"},
      {0, "A/B/h",    "normal",       1, "A/B/h", NOT_MOVED,  "p"},
      {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
      {1, "A/B",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/f",    "base-deleted", NO_COPY_FROM},
      {1, "A/B/h",    "base-deleted", NO_COPY_FROM},
      {1, "A2",       "normal",       1, "A", MOVED_HERE},
      {1, "A2/B",     "normal",       1, "A/B", MOVED_HERE},
      {1, "A2/B/f",   "normal",       1, "A/B/f", MOVED_HERE, "pd,pm,pn"},
      {1, "A2/B/h",   "normal",       1, "A/B/h", MOVED_HERE, "p"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Update causes a tree-conflict on A due to incoming text-change. */
  SVN_ERR(sbox_wc_update(&b, "", 2));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       2, ""},
      {0, "A",        "normal",       2, "A"},
      {0, "A/B",      "normal",       2, "A/B"},
      {0, "A/B/f",    "normal",       2, "A/B/f", NOT_MOVED,  "pa,pm,pn"},
      {0, "A/B/h",    "normal",       2, "A/B/h", NOT_MOVED,  "p"},
      {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
      {1, "A/B",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/f",    "base-deleted", NO_COPY_FROM},
      {1, "A/B/h",    "base-deleted", NO_COPY_FROM},
      {1, "A2",       "normal",       1, "A", MOVED_HERE},
      {1, "A2/B",     "normal",       1, "A/B", MOVED_HERE},
      {1, "A2/B/f",   "normal",       1, "A/B/f", MOVED_HERE, "pd,pm,pn"},
      {1, "A2/B/h",   "normal",       1, "A/B/h", MOVED_HERE, "p"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  {
    conflict_info_t conflicts[] = {
      { "A", FALSE, FALSE, {svn_wc_conflict_action_edit,
                            svn_wc_conflict_reason_moved_away, "A"}},
      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }

  /* Resolve should update the move. */
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));

  SVN_ERR(check_db_conflicts(&b, "", NULL));

  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       2, ""},
      {0, "A",        "normal",       2, "A"},
      {0, "A/B",      "normal",       2, "A/B"},
      {0, "A/B/f",    "normal",       2, "A/B/f", NOT_MOVED,  "pa,pm,pn"},
      {0, "A/B/h",    "normal",       2, "A/B/h", NOT_MOVED,  "p"},
      {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
      {1, "A/B",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/f",    "base-deleted", NO_COPY_FROM},
      {1, "A/B/h",    "base-deleted", NO_COPY_FROM},
      {1, "A2",       "normal",       2, "A", MOVED_HERE},
      {1, "A2/B",     "normal",       2, "A/B", MOVED_HERE},
      {1, "A2/B/f",   "normal",       2, "A/B/f", MOVED_HERE, "pa,pm,pn"},
      {1, "A2/B/h",   "normal",       2, "A/B/h", MOVED_HERE, "p"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
nested_move_update(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "nested_move_update", opts, pool));

  /* r1: Create file 'f' */
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_file_write(&b, "A/B/C/f", "r1 content\n"));
  SVN_ERR(sbox_wc_add(&b, "A/B/C/f"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  /* r2: Modify 'f' */
  SVN_ERR(sbox_file_write(&b, "A/B/C/f", "r1 content\nr2 content\n"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  /* r3: Create 'X' */
  SVN_ERR(sbox_wc_mkdir(&b, "X"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A", "A2"));
  SVN_ERR(sbox_wc_move(&b, "A2/B/C", "A2/B/C2"));
  {
    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, "A/B/C/f",   "normal",       1, "A/B/C/f"},
      {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, "A/B/C/f",   "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},
      {1, "A2/B/C/f",  "normal",       1, "A/B/C/f", MOVED_HERE},
      {3, "A2/B/C",    "base-deleted", NO_COPY_FROM, "A2/B/C2"},
      {3, "A2/B/C/f",  "base-deleted", NO_COPY_FROM},
      {3, "A2/B/C2",   "normal",       1, "A/B/C", MOVED_HERE},
      {3, "A2/B/C2/f", "normal",       1, "A/B/C/f", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_update(&b, "", 2));

  {
    nodes_row_t nodes[] = {
      {0, "",          "normal",       2, ""},
      {0, "A",         "normal",       2, "A"},
      {0, "A/B",       "normal",       2, "A/B"},
      {0, "A/B/C",     "normal",       2, "A/B/C"},
      {0, "A/B/C/f",   "normal",       2, "A/B/C/f"},
      {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, "A/B/C/f",   "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},
      {1, "A2/B/C/f",  "normal",       1, "A/B/C/f", MOVED_HERE},
      {3, "A2/B/C",    "base-deleted", NO_COPY_FROM, "A2/B/C2"},
      {3, "A2/B/C/f",  "base-deleted", NO_COPY_FROM},
      {3, "A2/B/C2",   "normal",       1, "A/B/C", MOVED_HERE},
      {3, "A2/B/C2/f", "normal",       1, "A/B/C/f", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Following the A->A2 move should raise a tree-conflict on A2/B/C,
     resolving that may require an explicit resolve. */
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  SVN_ERR(sbox_wc_resolve(&b, "A2/B/C", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  SVN_ERR(check_db_conflicts(&b, "", NULL /* no conflicts */));
  {
    nodes_row_t nodes[] = {
      {0, "",          "normal",       2, ""},
      {0, "A",         "normal",       2, "A"},
      {0, "A/B",       "normal",       2, "A/B"},
      {0, "A/B/C",     "normal",       2, "A/B/C"},
      {0, "A/B/C/f",   "normal",       2, "A/B/C/f"},
      {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, "A/B/C/f",   "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},
      {1, "A2/B/C/f",  "normal",       2, "A/B/C/f", MOVED_HERE},
      {3, "A2/B/C",    "base-deleted", NO_COPY_FROM, "A2/B/C2"},
      {3, "A2/B/C/f",  "base-deleted", NO_COPY_FROM},
      {3, "A2/B/C2",   "normal",       2, "A/B/C", MOVED_HERE},
      {3, "A2/B/C2/f", "normal",       2, "A/B/C/f", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Update A to r3 brings no changes but updates the revisions. */
  SVN_ERR(sbox_wc_update(&b, "A", 3));
  SVN_ERR(check_db_conflicts(&b, "", NULL /* no conflicts */));

  {
    nodes_row_t nodes[] = {
      {0, "",          "normal",       2, ""},
      {0, "A",         "normal",       3, "A"},
      {0, "A/B",       "normal",       3, "A/B"},
      {0, "A/B/C",     "normal",       3, "A/B/C"},
      {0, "A/B/C/f",   "normal",       3, "A/B/C/f"},
      {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, "A/B/C/f",   "base-deleted", NO_COPY_FROM},
      {1, "A2",        "normal",       3, "A", MOVED_HERE},
      {1, "A2/B",      "normal",       3, "A/B", MOVED_HERE},
      {1, "A2/B/C",    "normal",       3, "A/B/C", MOVED_HERE},
      {1, "A2/B/C/f",  "normal",       3, "A/B/C/f", MOVED_HERE},
      {3, "A2/B/C",    "base-deleted", NO_COPY_FROM, "A2/B/C2"},
      {3, "A2/B/C/f",  "base-deleted", NO_COPY_FROM},
      {3, "A2/B/C2",   "normal",       3, "A/B/C", MOVED_HERE},
      {3, "A2/B/C2/f", "normal",       3, "A/B/C/f", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
nested_move_commit(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "nested_move_commit", opts, pool));

  /* r1: Create file 'f' */
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_file_write(&b, "A/B/C/f", "r1 content\n"));
  SVN_ERR(sbox_wc_add(&b, "A/B/C/f"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A/B/C", "C2"));

  {
    const char *moved_to;
    const char *expected_to;
    SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, NULL, &moved_to,
                                     b.wc_ctx->db, sbox_wc_path(&b, "A/B/C"),
                                     pool, pool));

    expected_to = sbox_wc_path(&b, "C2");

    if (strcmp(moved_to, expected_to) != 0)
        return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
                                 "Expected moved to %s, but was %s",
                                 expected_to, moved_to);
  }
  {
    const char *moved_from;
    const char *expected_from;
    SVN_ERR(svn_wc__db_scan_moved(&moved_from, NULL, NULL, NULL,
                                  b.wc_ctx->db, sbox_wc_path(&b, "C2"),
                                  pool, pool));

    expected_from = sbox_wc_path(&b, "A/B/C");

    if (strcmp(moved_from, expected_from) != 0)
        return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
                                 "Expected moved from %s, but was %s",
                                 expected_from, moved_from);
  }

  SVN_ERR(sbox_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, "A/B/C",     "normal",       1, "A/B/C"},
      {0, "A/B/C/f",   "normal",       1, "A/B/C/f"},
      {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, "A/B/C/f",   "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},
      {1, "A2/B/C/f",  "normal",       1, "A/B/C/f", MOVED_HERE},
      {3, "A2/B/C",    "base-deleted", NO_COPY_FROM, "C2"},
      {3, "A2/B/C/f",  "base-deleted", NO_COPY_FROM},
      {1, "C2",        "normal",       1, "A/B/C", MOVED_HERE},
      {1, "C2/f",      "normal",       1, "A/B/C/f", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  {
    const char *moved_to;
    const char *expected_to;
    SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, NULL, &moved_to,
                                     b.wc_ctx->db, sbox_wc_path(&b, "A/B/C"),
                                     pool, pool));

    /* A/B/C is part of the A->A2 move. */
    expected_to = sbox_wc_path(&b, "A2");
    if (strcmp(moved_to, expected_to) != 0)
        return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
                                 "Expected moved to %s, but was %s",
                                 expected_to, moved_to);

    SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, NULL, &moved_to,
                                     b.wc_ctx->db, sbox_wc_path(&b, "A2/B/C"),
                                     pool, pool));

    /* A2/B/C is the A2/B/C->C2 move. */
    expected_to = sbox_wc_path(&b, "C2");
    if (strcmp(moved_to, expected_to) != 0)
        return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
                                 "Expected moved to %s, but was %s",
                                 expected_to, moved_to);
  }
  {
    const char *moved_from;
    const char *expected_from;
    SVN_ERR(svn_wc__db_scan_moved(&moved_from, NULL, NULL, NULL,
                                  b.wc_ctx->db, sbox_wc_path(&b, "C2"),
                                  pool, pool));

    /* C2 is the A2/B/C->C2 move. */
    expected_from = sbox_wc_path(&b, "A2/B/C");
    if (strcmp(moved_from, expected_from) != 0)
        return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
                                 "Expected moved from %s, but was %s",
                                 expected_from, moved_from);
  }

  {
    apr_array_header_t *targets = apr_array_make(pool, 2, sizeof(const char *));

    APR_ARRAY_PUSH(targets, const char *) = sbox_wc_path(&b, "A");
    APR_ARRAY_PUSH(targets, const char *) = sbox_wc_path(&b, "A2");

    SVN_ERR(sbox_wc_commit_ex(&b, targets, svn_depth_empty));
  }

  {
    nodes_row_t nodes[] = {
      {0, "",          "normal",       1, ""},
      {0, "A",         "not-present",  2, "A"},
      {0, "A2",        "normal",       2, "A2"},
      {0, "A2/B",      "normal",       2, "A2/B"},
      {0, "A2/B/C",    "normal",       2, "A2/B/C"},
      {0, "A2/B/C/f",  "normal",       2, "A2/B/C/f"},
      {3, "A2/B/C",    "base-deleted", NO_COPY_FROM, "C2"},
      {3, "A2/B/C/f",  "base-deleted", NO_COPY_FROM},

      /* These need to have their copyfrom information updated */
      {1, "C2",        "normal",       2, "A2/B/C", MOVED_HERE},
      {1, "C2/f",      "normal",       2, "A2/B/C/f", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  {
    const char *moved_to;
    const char *expected_to;
    SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, NULL, &moved_to,
                                     b.wc_ctx->db, sbox_wc_path(&b, "A2/B/C"),
                                     pool, pool));

    expected_to = sbox_wc_path(&b, "C2");

    if (strcmp(moved_to, expected_to) != 0)
        return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
                                 "Expected moved to %s, but was %s",
                                 expected_to, moved_to);
  }

  {
    const char *moved_from;
    const char *expected_from;
    SVN_ERR(svn_wc__db_scan_moved(&moved_from, NULL, NULL, NULL,
                                  b.wc_ctx->db, sbox_wc_path(&b, "C2"),
                                  pool, pool));

    expected_from = sbox_wc_path(&b, "A2/B/C");

    if (strcmp(moved_from, expected_from) != 0)
        return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
                                 "Expected moved from %s, but was %s",
                                 expected_from, moved_from);
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
nested_move_update2(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "nested_move_update2", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "P"));
  SVN_ERR(sbox_wc_mkdir(&b, "P/Q"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "X"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_move(&b, "A", "A2"));
  SVN_ERR(sbox_wc_move(&b, "P", "A"));
  SVN_ERR(sbox_wc_move(&b, "A2", "P"));
  SVN_ERR(sbox_wc_move(&b, "A/Q", "A/Q2"));
  SVN_ERR(sbox_wc_move(&b, "P/B", "P/B2"));

  {
    nodes_row_t nodes[] = {
      {0, "",          "normal",       1, ""},
      {0, "A",         "normal",       1, "A"},
      {0, "A/B",       "normal",       1, "A/B"},
      {0, "P",         "normal",       1, "P"},
      {0, "P/Q",       "normal",       1, "P/Q"},
      {1, "A",         "normal",       1, "P", FALSE, "P", TRUE},
      {1, "A/B",       "base-deleted", NO_COPY_FROM},
      {1, "A/Q",       "normal",       1, "P/Q", MOVED_HERE},
      {1, "P",         "normal",       1, "A", FALSE, "A", TRUE},
      {1, "P/Q",       "base-deleted", NO_COPY_FROM},
      {1, "P/B",       "normal",       1, "A/B", MOVED_HERE},
      {2, "A/Q",       "base-deleted", NO_COPY_FROM, "A/Q2"},
      {2, "A/Q2",      "normal",       1, "P/Q", MOVED_HERE},
      {2, "P/B",       "base-deleted", NO_COPY_FROM, "P/B2"},
      {2, "P/B2",      "normal",       1, "A/B", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Update A bumps revisions but only for moves originating in A.  In
     particular A/Q to A/Q2 does not get bumped. */
  SVN_ERR(sbox_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, "P",         "normal",       1, "P"},
      {0, "P/Q",       "normal",       1, "P/Q"},
      {1, "A",         "normal",       1, "P", FALSE, "P", TRUE},
      {1, "A/B",       "base-deleted", NO_COPY_FROM},
      {1, "A/Q",       "normal",       1, "P/Q", MOVED_HERE},
      {1, "P",         "normal",       2, "A", FALSE, "A", TRUE},
      {1, "P/Q",       "base-deleted", NO_COPY_FROM},
      {1, "P/B",       "normal",       2, "A/B", MOVED_HERE},
      {2, "A/Q",       "base-deleted", NO_COPY_FROM, "A/Q2"},
      {2, "A/Q2",      "normal",       1, "P/Q", MOVED_HERE},
      {2, "P/B",       "base-deleted", NO_COPY_FROM, "P/B2"},
      {2, "P/B2",      "normal",       2, "A/B", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
check_tree_conflict_repos_path(svn_test__sandbox_t *b,
                               const char *wc_path,
                               const char *repos_path1,
                               const char *repos_path2)
{
  svn_skel_t *conflict;
  svn_wc_operation_t operation;
  const apr_array_header_t *locations;
  svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted;

  SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL,
                                   b->wc_ctx->db, sbox_wc_path(b, wc_path),
                                   b->pool, b->pool));

  SVN_TEST_ASSERT(conflict != NULL);

  SVN_ERR(svn_wc__conflict_read_info(&operation, &locations,
                                     &text_conflicted, &prop_conflicted,
                                     &tree_conflicted,
                                     b->wc_ctx->db,  b->wc_abspath,
                                     conflict, b->pool, b->pool));

  SVN_ERR_ASSERT(tree_conflicted);

  if (repos_path1)
    {
      svn_wc_conflict_version_t *version
        = APR_ARRAY_IDX(locations, 0, svn_wc_conflict_version_t *);

      SVN_TEST_ASSERT(version != NULL);

      SVN_TEST_STRING_ASSERT(version->path_in_repos, repos_path1);
    }

  if (repos_path2)
    {
      svn_wc_conflict_version_t *version
        = APR_ARRAY_IDX(locations, 1, svn_wc_conflict_version_t *);

      SVN_TEST_ASSERT(version != NULL);

      SVN_TEST_STRING_ASSERT(version->path_in_repos, repos_path2);
    }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_update_conflicts(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_update_conflicts", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "X"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/A/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/A/B/C/D"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "X/A/B/C/D/E"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/A/B/F"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_switch(&b, "", "/X", svn_depth_infinity));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_move(&b, "A", "A2"));
  SVN_ERR(sbox_wc_move(&b, "A2/B/C", "A2/B/C2"));
  SVN_ERR(sbox_file_write(&b, "A2/B/F", "obstruction\n"));

  {
    nodes_row_t nodes[] = {
      {0, "",          "normal",       1, "X"},
      {0, "A",         "normal",       1, "X/A"},
      {0, "A/B",       "normal",       1, "X/A/B"},
      {0, "A/B/C",     "normal",       1, "X/A/B/C"},
      {0, "A/B/C/D",   "normal",       1, "X/A/B/C/D"},
      {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, "A/B/C/D",   "base-deleted", NO_COPY_FROM},
      {1, "A2",        "normal",       1, "X/A", MOVED_HERE},
      {1, "A2/B",      "normal",       1, "X/A/B", MOVED_HERE},
      {1, "A2/B/C",    "normal",       1, "X/A/B/C", MOVED_HERE},
      {1, "A2/B/C/D",  "normal",       1, "X/A/B/C/D", MOVED_HERE},
      {3, "A2/B/C",    "base-deleted", NO_COPY_FROM, "A2/B/C2"},
      {3, "A2/B/C/D",  "base-deleted", NO_COPY_FROM},
      {3, "A2/B/C2",   "normal",       1, "X/A/B/C", MOVED_HERE},
      {3, "A2/B/C2/D", "normal",       1, "X/A/B/C/D", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_update(&b, "A", 2));
  SVN_ERR(check_tree_conflict_repos_path(&b, "A", "X/A", "X/A"));
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    nodes_row_t nodes[] = {
      {0, "",           "normal",       1, "X"},
      {0, "A",          "normal",       2, "X/A"},
      {0, "A/B",        "normal",       2, "X/A/B"},
      {0, "A/B/C",      "normal",       2, "X/A/B/C"},
      {0, "A/B/C/D",    "normal",       2, "X/A/B/C/D"},
      {0, "A/B/C/D/E",  "normal",       2, "X/A/B/C/D/E"},
      {0, "A/B/F",      "normal",       2, "X/A/B/F"},
      {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, "A/B/C/D",    "base-deleted", NO_COPY_FROM},
      {1, "A/B/C/D/E",  "base-deleted", NO_COPY_FROM},
      {1, "A/B/F",      "base-deleted", NO_COPY_FROM},
      {1, "A2",         "normal",       2, "X/A", MOVED_HERE},
      {1, "A2/B",       "normal",       2, "X/A/B", MOVED_HERE},
      {1, "A2/B/C",     "normal",       2, "X/A/B/C", MOVED_HERE},
      {1, "A2/B/C/D",   "normal",       2, "X/A/B/C/D", MOVED_HERE},
      {1, "A2/B/C/D/E", "normal",       2, "X/A/B/C/D/E", MOVED_HERE},
      {1, "A2/B/F",     "normal",       2, "X/A/B/F", MOVED_HERE},
      {3, "A2/B/C",     "base-deleted", NO_COPY_FROM, "A2/B/C2"},
      {3, "A2/B/C/D",   "base-deleted", NO_COPY_FROM},
      {3, "A2/B/C/D/E", "base-deleted", NO_COPY_FROM},
      {3, "A2/B/C2",    "normal",       1, "X/A/B/C", MOVED_HERE},
      {3, "A2/B/C2/D",  "normal",       1, "X/A/B/C/D", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(check_tree_conflict_repos_path(&b, "A2/B/C", "X/A/B/C", "X/A/B/C"));
  SVN_ERR(check_tree_conflict_repos_path(&b, "A2/B/F", NULL, "X/A/B/F"));

  return SVN_NO_ERROR;
}

static svn_error_t *
move_update_delete_mods(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_update_delete_mods", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/D"));
  SVN_ERR(sbox_file_write(&b, "A/B/C/f", "r1 content\n"));
  SVN_ERR(sbox_wc_add(&b, "A/B/C/f"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_delete(&b, "A/B/C"));
  SVN_ERR(sbox_wc_delete(&b, "A/B/D"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A/B", "B2"));
  SVN_ERR(sbox_file_write(&b, "B2/C/f", "modified content\n"));
  SVN_ERR(sbox_wc_delete(&b, "B2/D"));
  {
    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, "A/B/C/f", "normal",       1, "A/B/C/f"},
      {0, "A/B/D",   "normal",       1, "A/B/D"},
      {2, "A/B",     "base-deleted",  NO_COPY_FROM, "B2"},
      {2, "A/B/C",   "base-deleted",  NO_COPY_FROM},
      {2, "A/B/C/f", "base-deleted",  NO_COPY_FROM},
      {2, "A/B/D",   "base-deleted",  NO_COPY_FROM},
      {1, "B2",      "normal",       1, "A/B", MOVED_HERE},
      {1, "B2/C",    "normal",       1, "A/B/C", MOVED_HERE},
      {1, "B2/C/f",  "normal",       1, "A/B/C/f", MOVED_HERE},
      {1, "B2/D",    "normal",       1, "A/B/D", MOVED_HERE},
      {2, "B2/D",    "base-deleted", NO_COPY_FROM},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_update(&b, "A", 2));
  SVN_ERR(sbox_wc_resolve(&b, "A/B", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       1, ""},
      {0, "A",       "normal",       2, "A"},
      {0, "A/B",     "normal",       2, "A/B"},
      {2, "A/B",     "base-deleted",  NO_COPY_FROM, "B2"},
      {1, "B2",      "normal",       2, "A/B", MOVED_HERE},
      {2, "B2/C",    "normal",       1, "A/B/C"},
      {2, "B2/C/f",  "normal",       1, "A/B/C/f"},
      {0}
    };
    conflict_info_t conflicts[] = {
      {"B2/C", FALSE, FALSE, {svn_wc_conflict_action_delete,
                              svn_wc_conflict_reason_edited}},
      {"B2/D", FALSE, FALSE, {svn_wc_conflict_action_delete,
                              svn_wc_conflict_reason_deleted}},
      { 0 }
    };

    SVN_ERR(check_db_rows(&b, "", nodes));
    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }

  SVN_ERR(check_tree_conflict_repos_path(&b, "B2/C", "A/B/C", "A/B/C"));
  SVN_ERR(check_tree_conflict_repos_path(&b, "B2/D", "A/B/D", "A/B/D"));

  return SVN_NO_ERROR;
}

static svn_error_t *
nested_moves2(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "nested_moves2", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A/A/A"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "X"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_move(&b, "A/A/A/A/A/A", "C"));
  SVN_ERR(sbox_wc_move(&b, "A/A/A/A", "D"));
  SVN_ERR(sbox_wc_move(&b, "A/A", "E"));

  {
    nodes_row_t nodes[] = {
      {0, "",            "normal",       0, ""},
      {0, "X",           "normal",       2, "X"},
      {0, "A",           "normal",       1, "A"},
      {0, "A/A",         "normal",       1, "A/A"},
      {0, "A/A/A",       "normal",       1, "A/A/A"},
      {0, "A/A/A/A",     "normal",       1, "A/A/A/A"},
      {0, "A/A/A/A/A",   "normal",       1, "A/A/A/A/A"},
      {0, "A/A/A/A/A/A", "normal",       1, "A/A/A/A/A/A"},
      {2, "A/A",         "base-deleted", NO_COPY_FROM, "E"},
      {2, "A/A/A",       "base-deleted", NO_COPY_FROM},
      {2, "A/A/A/A",     "base-deleted", NO_COPY_FROM},
      {2, "A/A/A/A/A",   "base-deleted", NO_COPY_FROM},
      {2, "A/A/A/A/A/A", "base-deleted", NO_COPY_FROM},
      {1, "E",           "normal",       1, "A/A", MOVED_HERE},
      {1, "E/A",         "normal",       1, "A/A/A", MOVED_HERE},
      {1, "E/A/A",       "normal",       1, "A/A/A/A", MOVED_HERE},
      {1, "E/A/A/A",     "normal",       1, "A/A/A/A/A", MOVED_HERE},
      {1, "E/A/A/A/A",   "normal",       1, "A/A/A/A/A/A", MOVED_HERE},
      {3, "E/A/A",       "base-deleted", NO_COPY_FROM, "D"},
      {3, "E/A/A/A",     "base-deleted", NO_COPY_FROM},
      {3, "E/A/A/A/A",   "base-deleted", NO_COPY_FROM},
      {1, "D",           "normal",       1, "A/A/A/A", MOVED_HERE},
      {1, "D/A",         "normal",       1, "A/A/A/A/A", MOVED_HERE},
      {1, "D/A/A",       "normal",       1, "A/A/A/A/A/A", MOVED_HERE},
      {3, "D/A/A",       "base-deleted", NO_COPY_FROM, "C"},
      {1, "C",           "normal",       1, "A/A/A/A/A/A", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_update(&b, "A", 2));
  {
    nodes_row_t nodes[] = {
      {0, "",            "normal",       0, ""},
      {0, "X",           "normal",       2, "X"},
      {0, "A",           "normal",       2, "A"},
      {0, "A/A",         "normal",       2, "A/A"},
      {0, "A/A/A",       "normal",       2, "A/A/A"},
      {0, "A/A/A/A",     "normal",       2, "A/A/A/A"},
      {0, "A/A/A/A/A",   "normal",       2, "A/A/A/A/A"},
      {0, "A/A/A/A/A/A", "normal",       2, "A/A/A/A/A/A"},
      {2, "A/A",         "base-deleted", NO_COPY_FROM, "E"},
      {2, "A/A/A",       "base-deleted", NO_COPY_FROM},
      {2, "A/A/A/A",     "base-deleted", NO_COPY_FROM},
      {2, "A/A/A/A/A",   "base-deleted", NO_COPY_FROM},
      {2, "A/A/A/A/A/A", "base-deleted", NO_COPY_FROM},
      {1, "E",           "normal",       2, "A/A", MOVED_HERE},
      {1, "E/A",         "normal",       2, "A/A/A", MOVED_HERE},
      {1, "E/A/A",       "normal",       2, "A/A/A/A", MOVED_HERE},
      {1, "E/A/A/A",     "normal",       2, "A/A/A/A/A", MOVED_HERE},
      {1, "E/A/A/A/A",   "normal",       2, "A/A/A/A/A/A", MOVED_HERE},
      {3, "E/A/A",       "base-deleted", NO_COPY_FROM, "D"},
      {3, "E/A/A/A",     "base-deleted", NO_COPY_FROM},
      {3, "E/A/A/A/A",   "base-deleted", NO_COPY_FROM},
      {1, "D",           "normal",       2, "A/A/A/A", MOVED_HERE},
      {1, "D/A",         "normal",       2, "A/A/A/A/A", MOVED_HERE},
      {1, "D/A/A",       "normal",       2, "A/A/A/A/A/A", MOVED_HERE},
      {3, "D/A/A",       "base-deleted", NO_COPY_FROM, "C"},
      {1, "C",           "normal",       2, "A/A/A/A/A/A", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_in_delete(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_in_delete", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D/E"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "X"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A/B/C", "C2"));
  SVN_ERR(sbox_wc_delete(&b, "A/B"));
  {
    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"},
      {2, "A/B",     "base-deleted", NO_COPY_FROM},
      {2, "A/B/C",   "base-deleted", NO_COPY_FROM, "C2"},
      {1, "C2",      "normal",       1, "A/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_update(&b, "", 2));
  SVN_ERR(sbox_wc_resolve(&b, "A/B", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  SVN_ERR(sbox_wc_resolve(&b, "A/B/C", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       2, ""},
      {0, "A",       "normal",       2, "A"},
      {0, "A/B",     "normal",       2, "A/B"},
      {0, "A/B/C",   "normal",       2, "A/B/C"},
      {0, "A/B/C/D", "normal",       2, "A/B/C/D"},
      {2, "A/B",     "base-deleted", NO_COPY_FROM},
      {2, "A/B/C",   "base-deleted", NO_COPY_FROM, "C2"},
      {2, "A/B/C/D", "base-deleted", NO_COPY_FROM},
      {1, "C2",      "normal",       2, "A/B/C", MOVED_HERE},
      {1, "C2/D",    "normal",       2, "A/B/C/D", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_update(&b, "", 3));
  {
    nodes_row_t nodes[] = {
      {0, "",          "normal",       3, ""},
      {0, "A",         "normal",       3, "A"},
      {0, "A/B",       "normal",       3, "A/B"},
      {0, "A/B/C",     "normal",       3, "A/B/C"},
      {0, "A/B/C/D",   "normal",       3, "A/B/C/D"},
      {0, "A/B/C/D/E", "normal",       3, "A/B/C/D/E"},

      {1, "C2",        "normal",       2, "A/B/C", MOVED_HERE},
      {1, "C2/D",      "normal",       2, "A/B/C/D", MOVED_HERE},

      {2, "A/B",              "base-deleted", NO_COPY_FROM},
      {2, "A/B/C",            "base-deleted", NO_COPY_FROM, "C2"},
      {2, "A/B/C/D",          "base-deleted", NO_COPY_FROM},
      {2, "A/B/C/D/E",        "base-deleted", NO_COPY_FROM},

      {0}
    };
    conflict_info_t conflicts[] = {
      {"A/B", FALSE, FALSE, {svn_wc_conflict_action_edit,
                             svn_wc_conflict_reason_deleted}},
      {0}
    };

    SVN_ERR(check_db_rows(&b, "", nodes));
    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  SVN_ERR(sbox_wc_revert(&b, "A/B", svn_depth_empty));
  {
    nodes_row_t nodes[] = {
      {0, "",          "normal",       3, ""},
      {0, "A",         "normal",       3, "A"},
      {0, "A/B",       "normal",       3, "A/B"},
      {0, "A/B/C",     "normal",       3, "A/B/C"},
      {0, "A/B/C/D",   "normal",       3, "A/B/C/D"},
      {0, "A/B/C/D/E", "normal",       3, "A/B/C/D/E"},
      {3, "A/B/C",     "base-deleted", NO_COPY_FROM, "C2"},
      {3, "A/B/C/D",   "base-deleted", NO_COPY_FROM},
      {3, "A/B/C/D/E", "base-deleted", NO_COPY_FROM},
      {1, "C2",        "normal",       2, "A/B/C", MOVED_HERE},
      {1, "C2/D",      "normal",       2, "A/B/C/D", MOVED_HERE},
      {0}
    };
    conflict_info_t conflicts[] = {
      {"A/B/C", FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "A/B/C"}},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
    /* Where did this conflict come from? */
    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }

  /* Revert should have left a tree-conflict (or broken the move). */
  SVN_ERR(sbox_wc_resolve(&b, "A/B/C", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    nodes_row_t nodes[] = {
      {0, "",          "normal",       3, ""},
      {0, "A",         "normal",       3, "A"},
      {0, "A/B",       "normal",       3, "A/B"},
      {0, "A/B/C",     "normal",       3, "A/B/C"},
      {0, "A/B/C/D",   "normal",       3, "A/B/C/D"},
      {0, "A/B/C/D/E", "normal",       3, "A/B/C/D/E"},
      {3, "A/B/C",     "base-deleted", NO_COPY_FROM, "C2"},
      {3, "A/B/C/D",   "base-deleted", NO_COPY_FROM},
      {3, "A/B/C/D/E", "base-deleted", NO_COPY_FROM},
      {1, "C2",        "normal",       3, "A/B/C", MOVED_HERE},
      {1, "C2/D",      "normal",       3, "A/B/C/D", MOVED_HERE},
      {1, "C2/D/E",    "normal",       3, "A/B/C/D/E", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
    SVN_ERR(check_db_conflicts(&b, "", NULL));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
switch_move(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "switch_move", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/D"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/D/E"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_copy(&b, "A", "X"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "X/B/D/E/F"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_switch(&b, "", "/A", svn_depth_infinity));
  SVN_ERR(sbox_wc_update(&b, "", 2));

  SVN_ERR(sbox_wc_move(&b, "B/C", "C2"));
  SVN_ERR(sbox_wc_move(&b, "B/D", "D2"));
  SVN_ERR(sbox_wc_move(&b, "D2/E", "D2/E2"));
  {
    nodes_row_t nodes[] = {
      {0, "",      "normal",       2, "A"},
      {0, "B",     "normal",       2, "A/B"},
      {0, "B/C",   "normal",       2, "A/B/C"},
      {0, "B/D",   "normal",       2, "A/B/D"},
      {0, "B/D/E", "normal",       2, "A/B/D/E"},
      {2, "B/C",   "base-deleted", NO_COPY_FROM, "C2"},
      {2, "B/D",   "base-deleted", NO_COPY_FROM, "D2"},
      {2, "B/D/E", "base-deleted", NO_COPY_FROM},
      {1, "C2",    "normal",       2, "A/B/C", MOVED_HERE},
      {1, "D2",    "normal",       2, "A/B/D", MOVED_HERE},
      {1, "D2/E",  "normal",       2, "A/B/D/E", MOVED_HERE},
      {2, "D2/E",  "base-deleted", NO_COPY_FROM, "D2/E2"},
      {2, "D2/E2", "normal",       2, "A/B/D/E", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Switch "bumps" revisions and paths and raises conflicts just like
     update. */
  SVN_ERR(sbox_wc_switch(&b, "", "/X", svn_depth_infinity));
  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       3, "X"},
      {0, "B",       "normal",       3, "X/B"},
      {0, "B/C",     "normal",       3, "X/B/C"},
      {0, "B/D",     "normal",       3, "X/B/D"},
      {0, "B/D/E",   "normal",       3, "X/B/D/E"},
      {0, "B/D/E/F", "normal",       3, "X/B/D/E/F"},
      {2, "B/C",     "base-deleted", NO_COPY_FROM, "C2"},
      {2, "B/D",     "base-deleted", NO_COPY_FROM, "D2"},
      {2, "B/D/E",   "base-deleted", NO_COPY_FROM},
      {2, "B/D/E/F", "base-deleted", NO_COPY_FROM},
      {1, "C2",      "normal",       3, "X/B/C", MOVED_HERE},
      {1, "D2",      "normal",       2, "A/B/D", MOVED_HERE},
      {1, "D2/E",    "normal",       2, "A/B/D/E", MOVED_HERE},
      {2, "D2/E",    "base-deleted", NO_COPY_FROM, "D2/E2"},
      {2, "D2/E2",   "normal",       2, "A/B/D/E", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Conflicts from switch are resolved just like those from update. */
  SVN_ERR(sbox_wc_resolve(&b, "B/D", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       3, "X"},
      {0, "B",       "normal",       3, "X/B"},
      {0, "B/C",     "normal",       3, "X/B/C"},
      {0, "B/D",     "normal",       3, "X/B/D"},
      {0, "B/D/E",   "normal",       3, "X/B/D/E"},
      {0, "B/D/E/F", "normal",       3, "X/B/D/E/F"},
      {2, "B/C",     "base-deleted", NO_COPY_FROM, "C2"},
      {2, "B/D",     "base-deleted", NO_COPY_FROM, "D2"},
      {2, "B/D/E",   "base-deleted", NO_COPY_FROM},
      {2, "B/D/E/F", "base-deleted", NO_COPY_FROM},
      {1, "C2",      "normal",       3, "X/B/C", MOVED_HERE},
      {1, "D2",      "normal",       3, "X/B/D", MOVED_HERE},
      {1, "D2/E",    "normal",       3, "X/B/D/E", MOVED_HERE},
      {1, "D2/E/F",  "normal",       3, "X/B/D/E/F", MOVED_HERE},
      {2, "D2/E",    "base-deleted", NO_COPY_FROM, "D2/E2"},
      {2, "D2/E/F",  "base-deleted", NO_COPY_FROM},
      {2, "D2/E2",   "normal",       2, "A/B/D/E", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_resolve(&b, "D2/E", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       3, "X"},
      {0, "B",       "normal",       3, "X/B"},
      {0, "B/C",     "normal",       3, "X/B/C"},
      {0, "B/D",     "normal",       3, "X/B/D"},
      {0, "B/D/E",   "normal",       3, "X/B/D/E"},
      {0, "B/D/E/F", "normal",       3, "X/B/D/E/F"},
      {2, "B/C",     "base-deleted", NO_COPY_FROM, "C2"},
      {2, "B/D",     "base-deleted", NO_COPY_FROM, "D2"},
      {2, "B/D/E",   "base-deleted", NO_COPY_FROM},
      {2, "B/D/E/F", "base-deleted", NO_COPY_FROM},
      {1, "C2",      "normal",       3, "X/B/C", MOVED_HERE},
      {1, "D2",      "normal",       3, "X/B/D", MOVED_HERE},
      {1, "D2/E",    "normal",       3, "X/B/D/E", MOVED_HERE},
      {1, "D2/E/F",  "normal",       3, "X/B/D/E/F", MOVED_HERE},
      {2, "D2/E",    "base-deleted", NO_COPY_FROM, "D2/E2"},
      {2, "D2/E/F",  "base-deleted", NO_COPY_FROM},
      {2, "D2/E2",   "normal",       3, "X/B/D/E", MOVED_HERE},
      {2, "D2/E2/F", "normal",       3, "X/B/D/E/F", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_replace(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_replace", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "B"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "B/X"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A", "X"));
  SVN_ERR(sbox_wc_move(&b, "B", "A"));
  SVN_ERR(sbox_wc_move(&b, "X", "B"));
  {
    nodes_row_t nodes[] = {
      {0, "",  "normal",       1, ""},
      {0, "A", "normal",       1, "A"},
      {0, "B", "normal",       1, "B"},
      {1, "A", "normal",       1, "B", FALSE, "B", TRUE},
      {1, "B", "normal",       1, "A", FALSE, "A", TRUE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_update(&b, "", 2));
  {
    nodes_row_t nodes[] = {
      {0, "",    "normal",       2, ""},
      {0, "A",   "normal",       2, "A"},
      {0, "B",   "normal",       2, "B"},
      {0, "B/X", "normal",       2, "B/X"},
      {1, "A",   "normal",       1, "B", FALSE, "B", TRUE},
      {1, "B",   "normal",       2, "A", FALSE, "A", TRUE},
      {1, "B/X", "base-deleted", NO_COPY_FROM},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_resolve(&b, "B", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    nodes_row_t nodes[] = {
      {0, "",    "normal",       2, ""},
      {0, "A",   "normal",       2, "A"},
      {0, "B",   "normal",       2, "B"},
      {0, "B/X", "normal",       2, "B/X"},
      {1, "A",   "normal",       2, "B", FALSE, "B", TRUE},
      {1, "A/X", "normal",       2, "B/X", MOVED_HERE},
      {1, "B",   "normal",       2, "A", FALSE, "A", TRUE},
      {1, "B/X", "base-deleted", NO_COPY_FROM},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
layered_moved_to(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;
  svn_error_t *err;

  SVN_ERR(svn_test__sandbox_create(&b, "layered_moved_to", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D/E"));
  SVN_ERR(sbox_wc_mkdir(&b, "C"));
  SVN_ERR(sbox_wc_mkdir(&b, "C/D"));
  SVN_ERR(sbox_wc_mkdir(&b, "C/D/E"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_propset(&b, "property", "value", "A/B/C/D/E"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_propset(&b, "property", "value", "C/D/E"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "P"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_propset(&b, "property2", "value", "A/B/C/D/E"));
  SVN_ERR(sbox_wc_propset(&b, "property2", "value", "C/D/E"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A", "X"));
  SVN_ERR(sbox_wc_move(&b, "X/B/C/D/E", "E2"));
  SVN_ERR(sbox_wc_delete(&b, "X/B/C"));
  SVN_ERR(sbox_wc_move(&b, "C", "X/B/C"));
  SVN_ERR(sbox_wc_move(&b, "X/B/C/D/E", "E3"));
  {
    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, "A/B/C/D",     "normal",       1, "A/B/C/D"},
      {0, "A/B/C/D/E",   "normal",       1, "A/B/C/D/E"},
      {0, "C",           "normal",       1, "C"},
      {0, "C/D",         "normal",       1, "C/D"},
      {0, "C/D/E",       "normal",       1, "C/D/E"},
      {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, "A/B/C/D",     "base-deleted", NO_COPY_FROM},
      {1, "A/B/C/D/E",   "base-deleted", NO_COPY_FROM},
      {1, "C",           "base-deleted", NO_COPY_FROM, "X/B/C"},
      {1, "C/D",         "base-deleted", NO_COPY_FROM},
      {1, "C/D/E",       "base-deleted", NO_COPY_FROM},
      {1, "X",           "normal",       1, "A", MOVED_HERE},
      {1, "X/B",         "normal",       1, "A/B", MOVED_HERE},
      {1, "X/B/C",       "normal",       1, "A/B/C", MOVED_HERE},
      {1, "X/B/C/D",     "normal",       1, "A/B/C/D", MOVED_HERE},
      {1, "X/B/C/D/E",   "normal",       1, "A/B/C/D/E", MOVED_HERE},
      {3, "X/B/C",       "normal",       1, "C", MOVED_HERE},
      {3, "X/B/C/D",     "normal",       1, "C/D", MOVED_HERE},
      {3, "X/B/C/D/E",   "normal",       1, "C/D/E", FALSE, "E2", TRUE},
      {5, "X/B/C/D/E",   "base-deleted", NO_COPY_FROM, "E3"},
      {1, "E2",          "normal",       1, "A/B/C/D/E", MOVED_HERE},
      {1, "E3",          "normal",       1, "C/D/E", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_update(&b, "A", 2));
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  SVN_ERR(sbox_wc_resolve(&b, "X/B/C", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  SVN_ERR(sbox_wc_resolve(&b, "X/B/C/D/E", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    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"},
      {0, "A/B/C/D",     "normal",       2, "A/B/C/D"},
      {0, "A/B/C/D/E",   "normal",       2, "A/B/C/D/E"},
      {0, "C",           "normal",       1, "C"},
      {0, "C/D",         "normal",       1, "C/D"},
      {0, "C/D/E",       "normal",       1, "C/D/E"},
      {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, "A/B/C/D",     "base-deleted", NO_COPY_FROM},
      {1, "A/B/C/D/E",   "base-deleted", NO_COPY_FROM},
      {1, "C",           "base-deleted", NO_COPY_FROM, "X/B/C"},
      {1, "C/D",         "base-deleted", NO_COPY_FROM},
      {1, "C/D/E",       "base-deleted", NO_COPY_FROM},
      {1, "X",           "normal",       2, "A", MOVED_HERE},
      {1, "X/B",         "normal",       2, "A/B", MOVED_HERE},
      {1, "X/B/C",       "normal",       2, "A/B/C", MOVED_HERE},
      {1, "X/B/C/D",     "normal",       2, "A/B/C/D", MOVED_HERE},
      {1, "X/B/C/D/E",   "normal",       2, "A/B/C/D/E", MOVED_HERE},
      {3, "X/B/C",       "normal",       1, "C", MOVED_HERE},
      {3, "X/B/C/D",     "normal",       1, "C/D", MOVED_HERE},
      {3, "X/B/C/D/E",   "normal",       1, "C/D/E", FALSE, "E2", TRUE},
      {5, "X/B/C/D/E",   "base-deleted", NO_COPY_FROM, "E3"},
      {1, "E2",          "normal",       2, "A/B/C/D/E", MOVED_HERE},
      {1, "E3",          "normal",       1, "C/D/E", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_update(&b, "C", 3));
  SVN_ERR(sbox_wc_resolve(&b, "C", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  SVN_ERR(sbox_wc_resolve(&b, "X/B/C/D/E", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    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"},
      {0, "A/B/C/D",     "normal",       2, "A/B/C/D"},
      {0, "A/B/C/D/E",   "normal",       2, "A/B/C/D/E"},
      {0, "C",           "normal",       3, "C"},
      {0, "C/D",         "normal",       3, "C/D"},
      {0, "C/D/E",       "normal",       3, "C/D/E"},
      {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, "A/B/C/D",     "base-deleted", NO_COPY_FROM},
      {1, "A/B/C/D/E",   "base-deleted", NO_COPY_FROM},
      {1, "C",           "base-deleted", NO_COPY_FROM, "X/B/C"},
      {1, "C/D",         "base-deleted", NO_COPY_FROM},
      {1, "C/D/E",       "base-deleted", NO_COPY_FROM},
      {1, "X",           "normal",       2, "A", MOVED_HERE},
      {1, "X/B",         "normal",       2, "A/B", MOVED_HERE},
      {1, "X/B/C",       "normal",       2, "A/B/C", MOVED_HERE},
      {1, "X/B/C/D",     "normal",       2, "A/B/C/D", MOVED_HERE},
      {1, "X/B/C/D/E",   "normal",       2, "A/B/C/D/E", MOVED_HERE},
      {3, "X/B/C",       "normal",       3, "C", MOVED_HERE},
      {3, "X/B/C/D",     "normal",       3, "C/D", MOVED_HERE},
      {3, "X/B/C/D/E",   "normal",       3, "C/D/E", FALSE, "E2", TRUE},
      {5, "X/B/C/D/E",   "base-deleted", NO_COPY_FROM, "E3"},
      {1, "E2",          "normal",       2, "A/B/C/D/E", MOVED_HERE},
      {1, "E3",          "normal",       3, "C/D/E", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* An update with no text/property/tree changes in A, just a revision bump. */
  SVN_ERR(sbox_wc_update(&b, "A", 4));
  {
    nodes_row_t nodes[] = {
      {0, "",            "normal",       1, ""},
      {0, "A",           "normal",       4, "A"},
      {0, "A/B",         "normal",       4, "A/B"},
      {0, "A/B/C",       "normal",       4, "A/B/C"},
      {0, "A/B/C/D",     "normal",       4, "A/B/C/D"},
      {0, "A/B/C/D/E",   "normal",       4, "A/B/C/D/E"},
      {0, "C",           "normal",       3, "C"},
      {0, "C/D",         "normal",       3, "C/D"},
      {0, "C/D/E",       "normal",       3, "C/D/E"},
      {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, "A/B/C/D",     "base-deleted", NO_COPY_FROM},
      {1, "A/B/C/D/E",   "base-deleted", NO_COPY_FROM},
      {1, "C",           "base-deleted", NO_COPY_FROM, "X/B/C"},
      {1, "C/D",         "base-deleted", NO_COPY_FROM},
      {1, "C/D/E",       "base-deleted", NO_COPY_FROM},
      {1, "X",           "normal",       4, "A", MOVED_HERE},
      {1, "X/B",         "normal",       4, "A/B", MOVED_HERE},
      {1, "X/B/C",       "normal",       4, "A/B/C", MOVED_HERE},
      {1, "X/B/C/D",     "normal",       4, "A/B/C/D", MOVED_HERE},
      {1, "X/B/C/D/E",   "normal",       4, "A/B/C/D/E", MOVED_HERE},
      {3, "X/B/C",       "normal",       3, "C", MOVED_HERE},
      {3, "X/B/C/D",     "normal",       3, "C/D", MOVED_HERE},
      {3, "X/B/C/D/E",   "normal",       3, "C/D/E", FALSE, "E2", TRUE},
      {5, "X/B/C/D/E",   "base-deleted", NO_COPY_FROM, "E3"},
      {1, "E2",          "normal",       4, "A/B/C/D/E", MOVED_HERE},
      {1, "E3",          "normal",       3, "C/D/E", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Update for conflicts on A and C */
  SVN_ERR(sbox_wc_update(&b, "", 5));
  {
    nodes_row_t nodes[] = {
      {0, "",            "normal",       5, ""},
      {0, "A",           "normal",       5, "A"},
      {0, "A/B",         "normal",       5, "A/B"},
      {0, "A/B/C",       "normal",       5, "A/B/C"},
      {0, "A/B/C/D",     "normal",       5, "A/B/C/D"},
      {0, "A/B/C/D/E",   "normal",       5, "A/B/C/D/E"},
      {0, "P",           "normal",       5, "P"},
      {0, "C",           "normal",       5, "C"},
      {0, "C/D",         "normal",       5, "C/D"},
      {0, "C/D/E",       "normal",       5, "C/D/E"},
      {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, "A/B/C/D",     "base-deleted", NO_COPY_FROM},
      {1, "A/B/C/D/E",   "base-deleted", NO_COPY_FROM},
      {1, "C",           "base-deleted", NO_COPY_FROM, "X/B/C"},
      {1, "C/D",         "base-deleted", NO_COPY_FROM},
      {1, "C/D/E",       "base-deleted", NO_COPY_FROM},
      {1, "X",           "normal",       4, "A", MOVED_HERE},
      {1, "X/B",         "normal",       4, "A/B", MOVED_HERE},
      {1, "X/B/C",       "normal",       4, "A/B/C", MOVED_HERE},
      {1, "X/B/C/D",     "normal",       4, "A/B/C/D", MOVED_HERE},
      {1, "X/B/C/D/E",   "normal",       4, "A/B/C/D/E", MOVED_HERE},
      {3, "X/B/C",       "normal",       3, "C", MOVED_HERE},
      {3, "X/B/C/D",     "normal",       3, "C/D", MOVED_HERE},
      {3, "X/B/C/D/E",   "normal",       3, "C/D/E", FALSE, "E2", TRUE},
      {5, "X/B/C/D/E",   "base-deleted", NO_COPY_FROM, "E3"},
      {1, "E2",          "normal",       4, "A/B/C/D/E", MOVED_HERE},
      {1, "E3",          "normal",       3, "C/D/E", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Partially resolve A */
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  SVN_ERR(sbox_wc_resolve(&b, "X/B/C", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));

  /* Cannot resolve C */
  err = sbox_wc_resolve(&b, "C", svn_depth_empty,
                        svn_wc_conflict_choose_mine_conflict);
  SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE);

  /* Complete resolving A and then resolve C */
  SVN_ERR(sbox_wc_resolve(&b, "X/B/C/D/E", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  SVN_ERR(sbox_wc_resolve(&b, "C", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));

  {
    nodes_row_t nodes[] = {
      {0, "",            "normal",       5, ""},
      {0, "A",           "normal",       5, "A"},
      {0, "A/B",         "normal",       5, "A/B"},
      {0, "A/B/C",       "normal",       5, "A/B/C"},
      {0, "A/B/C/D",     "normal",       5, "A/B/C/D"},
      {0, "A/B/C/D/E",   "normal",       5, "A/B/C/D/E"},
      {0, "P",           "normal",       5, "P"},
      {0, "C",           "normal",       5, "C"},
      {0, "C/D",         "normal",       5, "C/D"},
      {0, "C/D/E",       "normal",       5, "C/D/E"},
      {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, "A/B/C/D",     "base-deleted", NO_COPY_FROM},
      {1, "A/B/C/D/E",   "base-deleted", NO_COPY_FROM},
      {1, "C",           "base-deleted", NO_COPY_FROM, "X/B/C"},
      {1, "C/D",         "base-deleted", NO_COPY_FROM},
      {1, "C/D/E",       "base-deleted", NO_COPY_FROM},
      {1, "X",           "normal",       5, "A", MOVED_HERE},
      {1, "X/B",         "normal",       5, "A/B", MOVED_HERE},
      {1, "X/B/C",       "normal",       5, "A/B/C", MOVED_HERE},
      {1, "X/B/C/D",     "normal",       5, "A/B/C/D", MOVED_HERE},
      {1, "X/B/C/D/E",   "normal",       5, "A/B/C/D/E", MOVED_HERE},
      {3, "X/B/C",       "normal",       5, "C", MOVED_HERE},
      {3, "X/B/C/D",     "normal",       5, "C/D", MOVED_HERE},
      {3, "X/B/C/D/E",   "normal",       5, "C/D/E", FALSE, "E2", TRUE},
      {5, "X/B/C/D/E",   "base-deleted", NO_COPY_FROM, "E3"},
      {1, "E2",          "normal",       5, "A/B/C/D/E", MOVED_HERE},
      {1, "E3",          "normal",       3, "C/D/E", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
update_within_move(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;
  svn_error_t *err;

  SVN_ERR(svn_test__sandbox_create(&b, "update_within_move", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A", "X"));
  SVN_ERR(sbox_wc_update(&b, "A/B/C", 2));
  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       1, ""},
      {0, "A",       "normal",       1, "A"},
      {0, "A/B",     "normal",       1, "A/B"},
      {0, "A/B/C",   "normal",       2, "A/B/C"},
      {0, "A/B/C/D", "normal",       2, "A/B/C/D"},
      {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, "A/B/C/D", "base-deleted", NO_COPY_FROM},
      {1, "X",       "normal",       1, "A", MOVED_HERE},
      {1, "X/B",     "normal",       1, "A/B", MOVED_HERE},
      {1, "X/B/C",   "normal",       1, "A/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Can't resolve mixed-revision source to mine-conflict. */
  err = sbox_wc_resolve(&b, "A", svn_depth_empty,
                        svn_wc_conflict_choose_mine_conflict);
  SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE);

  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_merged));
  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       1, ""},
      {0, "A",       "normal",       1, "A"},
      {0, "A/B",     "normal",       1, "A/B"},
      {0, "A/B/C",   "normal",       2, "A/B/C"},
      {0, "A/B/C/D", "normal",       2, "A/B/C/D"},
      {1, "A",       "base-deleted", NO_COPY_FROM},
      {1, "A/B",     "base-deleted", NO_COPY_FROM},
      {1, "A/B/C",   "base-deleted", NO_COPY_FROM},
      {1, "A/B/C/D", "base-deleted", NO_COPY_FROM},
      {1, "X",       "normal",       1, "A"},
      {1, "X/B",     "normal",       1, "A/B"},
      {1, "X/B/C",   "normal",       1, "A/B/C"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }


  return SVN_NO_ERROR;
}

static svn_error_t *
commit_moved_descendant(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;
  SVN_ERR(svn_test__sandbox_create(&b, "commit_moved_descendant", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A/A/A"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_copy(&b, "A", "A_copied"));
  SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_moved"));
  SVN_ERR(sbox_wc_delete(&b, "A/A"));
  SVN_ERR(sbox_wc_copy(&b, "A_copied/A", "A/A"));

  /* And now I want to commit AAA_moved (the entire move), but not
     the replacement of A/A */

  /* For now, just start committing directly */
  /* ### This fails, because A/A/A is not collected by the commit
         harvester (it doesn't need committing, but our move filter
         blocks on it) */
  SVN_ERR(sbox_wc_commit(&b, ""));

  /* It would be nicer if we could just do a: */
  /* SVN_ERR(sbox_wc_commit(&b, "AAA_moved")); */
  /* Which then includes the delete half of the move, when it is
     shadowed, like in this case. The commit processing doesn't
     support this yet though*/

   {
    nodes_row_t nodes[] = {
      {0, "",                 "normal",       0, ""},
      {0, "A",                "normal",       1, "A"},
      {0, "A/A",              "normal",       2, "A/A"},
      {0, "A/A/A",            "normal",       2, "A/A/A"},
      {0, "A/A/A/A",          "normal",       2, "A/A/A/A"},
      {0, "A/A/A/A/A",        "normal",       2, "A/A/A/A/A"},
      {0, "A/A/A/A/A/A",      "normal",       2, "A/A/A/A/A/A"},
      {0, "A_copied",         "normal",       2, "A_copied"},
      {0, "A_copied/A",       "normal",       2, "A_copied/A"},
      {0, "A_copied/A/A",     "normal",       2, "A_copied/A/A"},
      {0, "A_copied/A/A/A",   "normal",       2, "A_copied/A/A/A"},
      {0, "A_copied/A/A/A/A", "normal",       2, "A_copied/A/A/A/A"},
      {0, "A_copied/A/A/A/A/A","normal",       2, "A_copied/A/A/A/A/A"},
      {0, "AAA_moved",        "normal",       2, "AAA_moved"},
      {0, "AAA_moved/A",      "normal",       2, "AAA_moved/A"},
      {0, "AAA_moved/A/A",    "normal",       2, "AAA_moved/A/A"},
      {0, "AAA_moved/A/A/A",  "normal",       2, "AAA_moved/A/A/A"},

      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
commit_moved_away_descendant(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;
  SVN_ERR(svn_test__sandbox_create(&b, "commit_moved_away_descendant",
                                   opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A/A/A"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_copy(&b, "A", "A_copied"));
  SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_moved"));
  SVN_ERR(sbox_wc_delete(&b, "A/A"));
  SVN_ERR(sbox_wc_copy(&b, "A_copied/A", "A/A"));

  {
    nodes_row_t nodes[] = {
      {0, "",                   "normal",       0, ""},
      {0, "A",                  "normal",       1, "A"},
      {0, "A/A",                "normal",       1, "A/A"},
      {0, "A/A/A",              "normal",       1, "A/A/A"},
      {0, "A/A/A/A",            "normal",       1, "A/A/A/A"},
      {0, "A/A/A/A/A",          "normal",       1, "A/A/A/A/A"},
      {0, "A/A/A/A/A/A",        "normal",       1, "A/A/A/A/A/A"},
      {1, "A_copied",           "normal",       1, "A"},
      {1, "A_copied/A",         "normal",       1, "A/A"},
      {1, "A_copied/A/A",       "normal",       1, "A/A/A"},
      {1, "A_copied/A/A/A",     "normal",       1, "A/A/A/A"},
      {1, "A_copied/A/A/A/A",   "normal",       1, "A/A/A/A/A"},
      {1, "A_copied/A/A/A/A/A", "normal",       1, "A/A/A/A/A/A"},
      {1, "AAA_moved",          "normal",       1, "A/A/A", MOVED_HERE},
      {1, "AAA_moved/A",        "normal",       1, "A/A/A/A", MOVED_HERE},
      {1, "AAA_moved/A/A",      "normal",       1, "A/A/A/A/A", MOVED_HERE},
      {1, "AAA_moved/A/A/A",    "normal",       1, "A/A/A/A/A/A", MOVED_HERE},
      {2, "A/A",                "normal",       1, "A/A"},
      {2, "A/A/A",              "normal",       1, "A/A/A", FALSE, "AAA_moved"},
      {2, "A/A/A/A",            "normal",       1, "A/A/A/A"},
      {2, "A/A/A/A/A",          "normal",       1, "A/A/A/A/A"},
      {2, "A/A/A/A/A/A",        "normal",       1, "A/A/A/A/A/A"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* And now I want to make sure that I can't commit A, without also
     committing AAA_moved, as that would break the move*/
  SVN_ERR(sbox_wc_commit(&b, "A"));

  {
    nodes_row_t nodes[] = {
      {0, "",                   "normal",       0, ""},
      {0, "A",                  "normal",       1, "A"},
      {0, "A/A",                "normal",       2, "A/A"},
      {0, "A/A/A",              "normal",       2, "A/A/A"},
      {0, "A/A/A/A",            "normal",       2, "A/A/A/A"},
      {0, "A/A/A/A/A",          "normal",       2, "A/A/A/A/A"},
      {0, "A/A/A/A/A/A",        "normal",       2, "A/A/A/A/A/A"},
      {1, "A_copied",           "normal",       1, "A"},
      {1, "A_copied/A",         "normal",       1, "A/A"},
      {1, "A_copied/A/A",       "normal",       1, "A/A/A"},
      {1, "A_copied/A/A/A",     "normal",       1, "A/A/A/A"},
      {1, "A_copied/A/A/A/A",   "normal",       1, "A/A/A/A/A"},
      {1, "A_copied/A/A/A/A/A", "normal",       1, "A/A/A/A/A/A"},
      {1, "AAA_moved",          "normal",       1, "A/A/A"},
      {1, "AAA_moved/A",        "normal",       1, "A/A/A/A"},
      {1, "AAA_moved/A/A",      "normal",       1, "A/A/A/A/A"},
      {1, "AAA_moved/A/A/A",    "normal",       1, "A/A/A/A/A/A"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
                          "The commit should have failed");

  /*return SVN_NO_ERROR;*/
}

static svn_error_t *
finite_move_update_bump(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;
  svn_error_t *err;
  SVN_ERR(svn_test__sandbox_create(&b, "finite_move_update_bump",
                                   opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "P"));
  SVN_ERR(sbox_wc_mkdir(&b, "P/Q"));
  SVN_ERR(sbox_file_write(&b, "P/Q/f", "r1 content\n"));
  SVN_ERR(sbox_wc_add(&b, "P/Q/f"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "X"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A/B", "B2"));
  SVN_ERR(sbox_wc_update(&b, "A/B/C", 2));
  SVN_ERR(check_tree_conflict_repos_path(&b, "A/B", NULL, NULL));
  err = sbox_wc_resolve(&b, "A/B", svn_depth_empty,
                        svn_wc_conflict_choose_mine_conflict);
  SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE);
  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       1, ""},
      {0, "A",       "normal",       1, "A"},
      {0, "A/B",     "normal",       1, "A/B"},
      {0, "A/B/C",   "normal",       2, "A/B/C"},
      {0, "P",       "normal",       1, "P"},
      {0, "P/Q",     "normal",       1, "P/Q"},
      {0, "P/Q/f",   "normal",       1, "P/Q/f"},
      {2, "A/B",     "base-deleted", NO_COPY_FROM, "B2"},
      {2, "A/B/C",   "base-deleted", NO_COPY_FROM},
      {1, "B2",      "normal",       1, "A/B", MOVED_HERE},
      {1, "B2/C",    "normal",       1, "A/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_revert(&b, "", svn_depth_infinity));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_move(&b, "A/B", "B2"));
  SVN_ERR(sbox_wc_move(&b, "P/Q", "Q2"));
  SVN_ERR(sbox_wc_update_depth(&b, "A/B", 2, svn_depth_files, FALSE));
  SVN_ERR(sbox_wc_update_depth(&b, "P/Q", 2, svn_depth_files, FALSE));
  {
    conflict_info_t conflicts[] = {
      {"A/B", FALSE, FALSE, {svn_wc_conflict_action_edit,
                             svn_wc_conflict_reason_moved_away, "A/B"}},
      {"P/Q", FALSE, FALSE, {svn_wc_conflict_action_edit,
                             svn_wc_conflict_reason_moved_away, "P/Q"}},
      {0}
    };
    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }

  SVN_ERR(check_tree_conflict_repos_path(&b, "A/B", "A/B", "A/B"));
  SVN_ERR(check_tree_conflict_repos_path(&b, "P/Q", "P/Q", "P/Q"));
  err = sbox_wc_resolve(&b, "A/B", svn_depth_empty,
                        svn_wc_conflict_choose_mine_conflict);
  SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE);

  /* sbox_wc_resolve() obtains a lock on the target path, so now it
     will apply the change on the target */
  SVN_ERR(sbox_wc_resolve(&b, "P/Q", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       1, ""},
      {0, "A",       "normal",       1, "A"},
      {0, "A/B",     "normal",       2, "A/B"},
      {0, "A/B/C",   "normal",       1, "A/B/C"},
      {0, "P",       "normal",       1, "P"},
      {0, "P/Q",     "normal",       2, "P/Q"},
      {0, "P/Q/f",   "normal",       2, "P/Q/f"},
      {2, "A/B",     "base-deleted", NO_COPY_FROM, "B2"},
      {2, "A/B/C",   "base-deleted", NO_COPY_FROM},
      {2, "P/Q",     "base-deleted", NO_COPY_FROM, "Q2"},
      {2, "P/Q/f",   "base-deleted", NO_COPY_FROM},
      {1, "B2",      "normal",       1, "A/B", MOVED_HERE},
      {1, "B2/C",    "normal",       1, "A/B/C", MOVED_HERE},
      {1, "Q2",      "normal",       2, "P/Q", MOVED_HERE},
      {1, "Q2/f",    "normal",       2, "P/Q/f", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_revert(&b, "", svn_depth_infinity));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_move(&b, "A/B", "B2"));
  SVN_ERR(sbox_wc_move(&b, "P", "P2"));
  SVN_ERR(sbox_wc_update_depth(&b, "A/B", 2, svn_depth_immediates, FALSE));
  SVN_ERR(sbox_wc_update_depth(&b, "P", 2, svn_depth_immediates, FALSE));
  {
    conflict_info_t conflicts[] = {
      {"A/B", FALSE, FALSE, {svn_wc_conflict_action_edit,
                             svn_wc_conflict_reason_moved_away, "A/B"}},
      {"P",   FALSE, FALSE, {svn_wc_conflict_action_edit,
                             svn_wc_conflict_reason_moved_away, "P"}},
      {0}
    };
    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  SVN_ERR(check_tree_conflict_repos_path(&b, "P", "P", "P"));
  SVN_ERR(check_tree_conflict_repos_path(&b, "A/B", "A/B", "A/B"));
  err = sbox_wc_resolve(&b, "P", svn_depth_empty,
                        svn_wc_conflict_choose_mine_conflict);
  SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE);
  SVN_ERR(sbox_wc_resolve(&b, "A/B", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    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"},
      {0, "P",       "normal",       2, "P"},
      {0, "P/Q",     "normal",       2, "P/Q"},
      {0, "P/Q/f",   "normal",       1, "P/Q/f"},
      {2, "A/B",     "base-deleted", NO_COPY_FROM, "B2"},
      {2, "A/B/C",   "base-deleted", NO_COPY_FROM},
      {1, "P",       "base-deleted", NO_COPY_FROM, "P2"},
      {1, "P/Q",     "base-deleted", NO_COPY_FROM},
      {1, "P/Q/f",   "base-deleted", NO_COPY_FROM},
      {1, "B2",      "normal",       2, "A/B", MOVED_HERE},
      {1, "B2/C",    "normal",       2, "A/B/C", MOVED_HERE},
      {1, "P2",      "normal",       1, "P", MOVED_HERE},
      {1, "P2/Q",    "normal",       1, "P/Q", MOVED_HERE},
      {1, "P2/Q/f",  "normal",       1, "P/Q/f", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_revert(&b, "", svn_depth_infinity));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_move(&b, "A/B/C", "C2"));
  SVN_ERR(sbox_wc_move(&b, "P/Q", "Q2"));
  SVN_ERR(sbox_wc_update_depth(&b, "A/B/C", 2, svn_depth_empty, FALSE));
  SVN_ERR(sbox_wc_update_depth(&b, "P/Q", 2, svn_depth_empty, FALSE));
  {
    conflict_info_t conflicts[] = {
      {"A/B/C", FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "A/B/C"}},
      {"P/Q",   FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "P/Q"}},

      {0}
    };
    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  SVN_ERR(check_tree_conflict_repos_path(&b, "A/B/C", "A/B/C", "A/B/C"));
  SVN_ERR(check_tree_conflict_repos_path(&b, "P/Q", "P/Q", "P/Q"));
  err = sbox_wc_resolve(&b, "P/Q", svn_depth_empty,
                        svn_wc_conflict_choose_mine_conflict);
  SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE);
  SVN_ERR(sbox_wc_resolve(&b, "A/B/C", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       1, ""},
      {0, "A",       "normal",       1, "A"},
      {0, "A/B",     "normal",       1, "A/B"},
      {0, "A/B/C",   "normal",       2, "A/B/C"},
      {0, "P",       "normal",       1, "P"},
      {0, "P/Q",     "normal",       2, "P/Q"},
      {0, "P/Q/f",   "normal",       1, "P/Q/f"},
      {3, "A/B/C",   "base-deleted", NO_COPY_FROM, "C2"},
      {2, "P/Q",     "base-deleted", NO_COPY_FROM, "Q2"},
      {2, "P/Q/f",   "base-deleted", NO_COPY_FROM},
      {1, "C2",      "normal",       2, "A/B/C", MOVED_HERE},
      {1, "Q2",      "normal",       1, "P/Q", MOVED_HERE},
      {1, "Q2/f",    "normal",       1, "P/Q/f", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_away_delete_update(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;
  SVN_ERR(svn_test__sandbox_create(&b, "move_away_delete_update",
                                   opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "P"));
  SVN_ERR(sbox_wc_mkdir(&b, "P/Q"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_delete(&b, "A/B"));
  SVN_ERR(sbox_wc_delete(&b, "P/Q"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_move(&b, "A/B/C", "C2"));
  SVN_ERR(sbox_wc_move(&b, "P/Q", "Q2"));

  /* Update to r2 removes the move sources and clears moved_here from
     the move destinations. */
  SVN_ERR(sbox_wc_update(&b, "", 2));
  {
    nodes_row_t nodes[] = {
      {0, "",   "normal", 2, ""},
      {0, "A",  "normal", 2, "A"},
      {0, "P",  "normal", 2, "P"},
      {1, "C2", "normal", 1, "A/B/C", MOVED_HERE},
      {1, "Q2", "normal", 1, "P/Q"},

      {2, "A/B",              "normal",       1, "A/B"},
      {2, "A/B/C",            "normal",       1, "A/B/C"},
      {3, "A/B/C",            "base-deleted", NO_COPY_FROM, "C2"},
      {0}
    };
    conflict_info_t conflicts[] = {
      {"A/B", FALSE, FALSE, {svn_wc_conflict_action_delete,
                             svn_wc_conflict_reason_edited}},
      {"P/Q", FALSE, FALSE, {svn_wc_conflict_action_delete,
                             svn_wc_conflict_reason_moved_away, "P/Q"}},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_not_present_variants(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;
  SVN_ERR(svn_test__sandbox_create(&b, "move_not_present_variants",
                                   opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "B"));
  SVN_ERR(sbox_wc_mkdir(&b, "B/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "C"));
  SVN_ERR(sbox_wc_mkdir(&b, "C/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "D"));
  SVN_ERR(sbox_wc_mkdir(&b, "D/B"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_delete(&b, "A/B"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_delete(&b, "B/B"));
  SVN_ERR(sbox_wc_update(&b, "C/B", 0));
  SVN_ERR(sbox_wc_exclude(&b, "D/B"));

  SVN_ERR(sbox_wc_copy(&b, "A", "cA"));
  SVN_ERR(sbox_wc_copy(&b, "B", "cB"));
  SVN_ERR(sbox_wc_copy(&b, "C", "cC"));
  SVN_ERR(sbox_wc_copy(&b, "D", "cD"));

  SVN_ERR(sbox_wc_copy(&b, "cA", "ccA"));
  SVN_ERR(sbox_wc_copy(&b, "cB", "ccB"));
  SVN_ERR(sbox_wc_copy(&b, "cC", "ccC"));
  SVN_ERR(sbox_wc_copy(&b, "cD", "ccD"));

  {
    nodes_row_t nodes[] = {
      {0, "",           "normal",       1, ""},

      /* Copy of a deleted + committed node */
      {0, "A",          "normal",       1, "A"},
      {0, "A/B",        "not-present",  2, "A/B"},

      {1, "cA",         "normal",       1, "A"},
      {1, "cA/B",       "not-present",  2, "A/B"},

      {1, "ccA",        "normal",       1, "A"},
      {1, "ccA/B",      "not-present",  2, "A/B"},

      /* Copy of a local deleted node */
      {0, "B",          "normal",       1, "B"},
      {0, "B/B",        "normal",       1, "B/B"},
      {2, "B/B",        "base-deleted", NO_COPY_FROM},

      {1, "cB",         "normal",       1, "B",},
      {1, "cB/B",       "normal",       1, "B/B"},
      {2, "cB/B",       "base-deleted", NO_COPY_FROM},

      {1, "ccB",        "normal",       1, "B"},
      {1, "ccB/B",      "normal",       1, "B/B"},
      {2, "ccB/B",      "base-deleted", NO_COPY_FROM},

      /* Copy of a to r0 updated node */
      {0, "C",          "normal",       1, "C"},
      {0, "C/B",        "not-present",  0, "C/B"},

      {1, "cC",         "normal",       1, "C"},
      {1, "cC/B",       "not-present",  0, "C/B"},

      {1, "ccC",        "normal",       1, "C"},
      {1, "ccC/B",      "not-present",  0, "C/B"},

      /* Copy of an excluded node */
      {0, "D",          "normal",       1, "D"},
      {0, "D/B",        "excluded",     1, "D/B"},

      {1, "cD",         "normal",       1, "D"},
      {1, "cD/B",       "excluded",     1, "D/B"},

      {1, "ccD",        "normal",       1, "D"},
      {1, "ccD/B",      "excluded",     1, "D/B"},

      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_revert(&b, "", svn_depth_infinity));
  SVN_ERR(sbox_wc_delete(&b, "B/B"));

  /* And now do the same thing with moves */

  SVN_ERR(sbox_wc_move(&b, "A", "mA"));
  SVN_ERR(sbox_wc_move(&b, "B", "mB"));
  SVN_ERR(sbox_wc_move(&b, "C", "mC"));
  SVN_ERR(sbox_wc_move(&b, "D", "mD"));

  SVN_ERR(sbox_wc_move(&b, "mA", "mmA"));
  SVN_ERR(sbox_wc_move(&b, "mB", "mmB"));
  SVN_ERR(sbox_wc_move(&b, "mC", "mmC"));
  SVN_ERR(sbox_wc_move(&b, "mD", "mmD"));

  {
    nodes_row_t nodes[] = {
      {0, "",           "normal",       1, ""},

      /* Move of a deleted + committed node */
      {0, "A",          "normal",       1, "A"},
      {0, "A/B",        "not-present",  2, "A/B"},
      {1, "A",          "base-deleted", NO_COPY_FROM, "mmA"},

      {1, "mmA",        "normal",       1, "A", MOVED_HERE},
      {1, "mmA/B",      "not-present",  2, "A/B", MOVED_HERE},


      /* Move of a local deleted node */
      {0, "B",          "normal",       1, "B"},
      {0, "B/B",        "normal",       1, "B/B"},
      {1, "B",          "base-deleted", NO_COPY_FROM, "mmB"},
      {1, "B/B",        "base-deleted", NO_COPY_FROM},

      {1, "mmB",        "normal",       1, "B", MOVED_HERE},
      {1, "mmB/B",      "normal",       1, "B/B", MOVED_HERE},
      {2, "mmB/B",      "base-deleted", NO_COPY_FROM},

      /* Move of a to r0 updated node */
      {0, "C",          "normal",       1, "C"},
      {0, "C/B",        "not-present",  0, "C/B"},
      {1, "C",          "base-deleted", NO_COPY_FROM, "mmC"},

      {1, "mmC",        "normal",       1, "C", MOVED_HERE},
      {1, "mmC/B",      "not-present",  0, "C/B", MOVED_HERE},

      /* Move of an excluded node */
      {0, "D",          "normal",       1, "D",},
      {0, "D/B",        "excluded",     1, "D/B", },
      {1, "D",          "base-deleted", NO_COPY_FROM, "mmD"},

      {1, "mmD",        "normal",       1, "D", MOVED_HERE},
      {1, "mmD/B",      "excluded",     1, "D/B", MOVED_HERE},

      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* And move everything back */
  SVN_ERR(sbox_wc_move(&b, "mmA", "A"));
  SVN_ERR(sbox_wc_move(&b, "mmB", "B"));
  SVN_ERR(sbox_wc_move(&b, "mmC", "C"));
  SVN_ERR(sbox_wc_move(&b, "mmD", "D"));

  {
    nodes_row_t nodes[] = {
      {0, "",           "normal",       1, ""},

      /* deleted + committed node */
      {0, "A",          "normal",       1, "A"},
      {0, "A/B",        "not-present",  2, "A/B"},

      /* local deleted node */
      {0, "B",          "normal",       1, "B"},
      {0, "B/B",        "normal",       1, "B/B"},
      {2, "B/B",        "base-deleted", NO_COPY_FROM},

      /* To r0 updated node */
      {0, "C",          "normal",       1, "C"},
      {0, "C/B",        "not-present",  0, "C/B"},

      /* Move of an excluded node */
      {0, "D",          "normal",       1, "D",},
      {0, "D/B",        "excluded",     1, "D/B", },

      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
update_child_under_add(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;
  svn_error_t *err;
  nodes_row_t nodes[] = {
    {0, "",        "normal",      1, ""},
    {0, "A",       "normal",      1, "A"},
    {0, "A/B",     "not-present", 0, "A/B"},
    {2, "A/B",     "normal",      NO_COPY_FROM},
    {3, "A/B/C",   "normal",      NO_COPY_FROM},
    {4, "A/B/C/D", "normal",      NO_COPY_FROM},
    {0}
  };

  SVN_ERR(svn_test__sandbox_create(&b, "update_child_under_add",
                                   opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_update(&b, "A/B", 0));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D"));
  SVN_ERR(check_db_rows(&b, "", nodes));

  /* A/B/C/D is skipped as it has no base node parent */
  SVN_ERR(sbox_wc_update(&b, "A/B/C/D", 1));
  SVN_ERR(check_db_rows(&b, "", nodes));

  /* A/B/C should be skipped as it has a not-present base node parent */
  err = sbox_wc_update(&b, "A/B/C", 1);
  svn_error_clear(err); /* Allow any error and always check NODES. */
  SVN_ERR(check_db_rows(&b, "", nodes));

  return SVN_NO_ERROR;
}

static svn_error_t *
delete_over_moved_away(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "delete_over_moved_away",
                                   opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));

  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A/B", "B"));
  SVN_ERR(sbox_wc_delete(&b, "A"));

  {
    nodes_row_t nodes[] = {
      {0, "",       "normal",         1, ""},

      {0, "A",      "normal",         1, "A"},
      {1, "A",      "base-deleted",   NO_COPY_FROM},
      {0, "A/B",    "normal",         1, "A/B"},
      {1, "A/B",    "base-deleted",   NO_COPY_FROM, "B"},
      {0, "A/B/C",  "normal",         1, "A/B/C"},
      {1, "A/B/C",  "base-deleted",   NO_COPY_FROM},

      {1, "B",      "normal",         1, "A/B", MOVED_HERE},
      {1, "B/C",    "normal",         1, "A/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Now replace A with a similar tree */
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));

  {
    nodes_row_t nodes[] = {
      {0, "",       "normal",         1, ""},

      {0, "A",      "normal",         1, "A"},
      {1, "A",      "normal",         NO_COPY_FROM},
      {0, "A/B",    "normal",         1, "A/B"},
      {1, "A/B",    "base-deleted",   NO_COPY_FROM, "B"},
      {2, "A/B",    "normal",         NO_COPY_FROM},
      {0, "A/B/C",  "normal",         1, "A/B/C"},
      {1, "A/B/C",  "base-deleted",   NO_COPY_FROM},
      {3, "A/B/C",  "normal",         NO_COPY_FROM},

      {1, "B",      "normal",         1, "A/B", MOVED_HERE},
      {1, "B/C",    "normal",         1, "A/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* And delete the new A */
  SVN_ERR(sbox_wc_delete(&b, "A"));

  {
    nodes_row_t nodes[] = {
      {0, "",       "normal",         1, ""},

      {0, "A",      "normal",         1, "A"},
      {1, "A",      "base-deleted",   NO_COPY_FROM},
      {0, "A/B",    "normal",         1, "A/B"},
      /* And here the moved-to information is lost */
      {1, "A/B",    "base-deleted",   NO_COPY_FROM, "B"},
      {0, "A/B/C",  "normal",         1, "A/B/C"},
      {1, "A/B/C",  "base-deleted",   NO_COPY_FROM},

      /* But the moved-here is still there */
      {1, "B",      "normal",         1, "A/B", MOVED_HERE},
      {1, "B/C",    "normal",         1, "A/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
movedto_opdepth(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "movedto_opdepth",
                                   opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A/B/C", "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"},
      {3, "A/B/C",  "base-deleted",   NO_COPY_FROM, "C"},

      {1, "C",      "normal",         1, "A/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* And now the moved_to information has to switch op-depths */
  SVN_ERR(sbox_wc_delete(&b, "A/B"));
  {
    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"},

      {2, "A/B",    "base-deleted",   NO_COPY_FROM},
      {2, "A/B/C",  "base-deleted",   NO_COPY_FROM, "C"},

      {1, "C",      "normal",         1, "A/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* And again */
  SVN_ERR(sbox_wc_delete(&b, "A"));
  {
    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"},

      {1, "A",      "base-deleted",   NO_COPY_FROM},
      {1, "A/B",    "base-deleted",   NO_COPY_FROM},
      {1, "A/B/C",  "base-deleted",   NO_COPY_FROM, "C"},

      {1, "C",      "normal",         1, "A/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* And now stay at the depth of A */
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));

  {
    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"},

      {1, "A",      "normal",         NO_COPY_FROM},
      {1, "A/B",    "base-deleted",   NO_COPY_FROM},
      {1, "A/B/C",  "base-deleted",   NO_COPY_FROM, "C"},

      {2, "A/B",    "normal",         NO_COPY_FROM},

      {1, "C",      "normal",         1, "A/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* And see if it can jump back to B again? */
  SVN_ERR(sbox_wc_delete(&b, "A"));
  SVN_ERR(sbox_wc_revert(&b, "A", svn_depth_empty));

  {
    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"},

      {2, "A/B",    "base-deleted",   NO_COPY_FROM},
      {2, "A/B/C",  "base-deleted",   NO_COPY_FROM, "C"},

      {1, "C",      "normal",         1, "A/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* And can we bump it back to C itself? */
  SVN_ERR(sbox_wc_revert(&b, "A", svn_depth_immediates));
  {
    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"},

      {3, "A/B/C",  "base-deleted",   NO_COPY_FROM, "C"},

      {1, "C",      "normal",         1, "A/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
new_basemove(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "new_basemove",
                                   opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  /* We keep track of moved children of copies */
  SVN_ERR(sbox_wc_copy(&b, "A", "Copy"));
  SVN_ERR(sbox_wc_move(&b, "Copy/B/C", "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"},

      {1, "Copy",     "normal",         1, "A"},
      {1, "Copy/B",   "normal",         1, "A/B"},
      {1, "Copy/B/C", "normal",         1, "A/B/C"},

      {3, "Copy/B/C", "base-deleted",   NO_COPY_FROM, "C"},

      /* C is a copy of A/B/C */
      {1, "C",        "normal",         1, "A/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  {
    apr_array_header_t *targets = apr_array_make(pool, 1, sizeof(char *));
    APR_ARRAY_PUSH(targets, const char*) = sbox_wc_path(&b, "Copy");

    SVN_ERR(sbox_wc_commit_ex(&b, targets, svn_depth_empty));
  }

  {
    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, "Copy",     "normal",         2, "Copy"},
      {0, "Copy/B",   "normal",         2, "Copy/B"},
      {0, "Copy/B/C", "normal",         2, "Copy/B/C"},

      {3, "Copy/B/C", "base-deleted",   NO_COPY_FROM, "C"},

      /* And this node is now a copy of Copy/B/C at r2 */
      {1, "C",        "normal",         2, "Copy/B/C", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_back(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_back", opts, pool));

  /* X just so we don't always test with local_relpath == repos_path */
  SVN_ERR(sbox_wc_mkdir(&b, "X"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/A/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/A/B/D"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/E"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_switch(&b, "", "/X", svn_depth_infinity));

  SVN_ERR(sbox_wc_move(&b, "A/B", "A/B2"));
  {
    nodes_row_t nodes[] = {
      {0, "",       "normal",       1, "X"},
      {0, "A",      "normal",       1, "X/A"},
      {0, "A/B",    "normal",       1, "X/A/B"},
      {0, "A/B/C",  "normal",       1, "X/A/B/C"},
      {0, "A/B/D",  "normal",       1, "X/A/B/D"},
      {0, "E",      "normal",       1, "X/E"},
      {2, "A/B",    "base-deleted", NO_COPY_FROM, "A/B2"},
      {2, "A/B/C",  "base-deleted", NO_COPY_FROM},
      {2, "A/B/D",  "base-deleted", NO_COPY_FROM},
      {2, "A/B2",   "normal",       1, "X/A/B", MOVED_HERE},
      {2, "A/B2/C", "normal",       1, "X/A/B/C", MOVED_HERE},
      {2, "A/B2/D", "normal",       1, "X/A/B/D", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }
  SVN_ERR(sbox_wc_move(&b, "A/B2", "A/B"));
  {
    nodes_row_t nodes[] = {
      {0, "",       "normal",       1, "X"},
      {0, "A",      "normal",       1, "X/A"},
      {0, "A/B",    "normal",       1, "X/A/B"},
      {0, "A/B/C",  "normal",       1, "X/A/B/C"},
      {0, "A/B/D",  "normal",       1, "X/A/B/D"},
      {0, "E",      "normal",       1, "X/E"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_move(&b, "A/B", "A/B2"));
  SVN_ERR(sbox_wc_move(&b, "A/B2/C", "A/B2/C2"));
  SVN_ERR(sbox_wc_move(&b, "A/B2/D", "D2"));
  SVN_ERR(sbox_wc_move(&b, "E", "A/B2/E2"));
  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       1, "X"},
      {0, "A",       "normal",       1, "X/A"},
      {0, "A/B",     "normal",       1, "X/A/B"},
      {0, "A/B/C",   "normal",       1, "X/A/B/C"},
      {0, "A/B/D",   "normal",       1, "X/A/B/D"},
      {0, "E",       "normal",       1, "X/E"},
      {1, "D2",      "normal",       1, "X/A/B/D", MOVED_HERE},
      {1, "E",       "base-deleted", NO_COPY_FROM, "A/B2/E2"},
      {2, "A/B",     "base-deleted", NO_COPY_FROM, "A/B2"},
      {2, "A/B/C",   "base-deleted", NO_COPY_FROM},
      {2, "A/B/D",   "base-deleted", NO_COPY_FROM},
      {2, "A/B2",    "normal",       1, "X/A/B", MOVED_HERE},
      {2, "A/B2/C",  "normal",       1, "X/A/B/C", MOVED_HERE},
      {2, "A/B2/D",  "normal",       1, "X/A/B/D", MOVED_HERE},
      {3, "A/B2/C",  "base-deleted", NO_COPY_FROM, "A/B2/C2"},
      {3, "A/B2/D",  "base-deleted", NO_COPY_FROM, "D2"},
      {3, "A/B2/C2", "normal",       1, "X/A/B/C", MOVED_HERE},
      {3, "A/B2/E2", "normal",       1, "X/E", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }
  SVN_ERR(sbox_wc_move(&b, "A/B2", "A/B"));
  {
    nodes_row_t nodes[] = {
      {0, "",       "normal",       1, "X"},
      {0, "A",      "normal",       1, "X/A"},
      {0, "A/B",    "normal",       1, "X/A/B"},
      {0, "A/B/C",  "normal",       1, "X/A/B/C"},
      {0, "A/B/D",  "normal",       1, "X/A/B/D"},
      {0, "E",      "normal",       1, "X/E"},
      {1, "D2",     "normal",       1, "X/A/B/D", MOVED_HERE},
      {1, "E",      "base-deleted", NO_COPY_FROM, "A/B/E2"},
      {3, "A/B/C",  "base-deleted", NO_COPY_FROM, "A/B/C2"},
      {3, "A/B/D",  "base-deleted", NO_COPY_FROM, "D2"},
      {3, "A/B/C2", "normal",       1, "X/A/B/C", MOVED_HERE},
      {3, "A/B/E2", "normal",       1, "X/E", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_update_subtree(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;
  svn_error_t *err;

  SVN_ERR(svn_test__sandbox_create(&b, "move_update_subtree", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "X"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 3));
  SVN_ERR(sbox_wc_copy(&b, "A", "P"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  /* Subtree update is like an interrupted update, it leaves a
     mixed-revision move source. */
  SVN_ERR(sbox_wc_move(&b, "A/B", "A/B2"));
  SVN_ERR(sbox_wc_update(&b, "A/B/C", 2));
  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       1, ""},
      {0, "A",       "normal",       1, "A"},
      {0, "A/B",     "normal",       1, "A/B"},
      {0, "A/B/C",   "normal",       2, "A/B/C"},
      {0, "A/B/C/D", "normal",       2, "A/B/C/D"},
      {2, "A/B",     "base-deleted", NO_COPY_FROM, "A/B2"},
      {2, "A/B/C",   "base-deleted", NO_COPY_FROM},
      {2, "A/B/C/D", "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},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Resolve fails because of the mixed-revision. */
  err = sbox_wc_resolve(&b, "A/B", svn_depth_empty,
                        svn_wc_conflict_choose_mine_conflict);
  SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE);

  /* Update to single-revision to allow resolve; this requires update
     while the tree-conflict on A/B is present. */
  SVN_ERR(sbox_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"},
      {0, "A/B/C/D", "normal",       2, "A/B/C/D"},
      {2, "A/B",     "base-deleted", NO_COPY_FROM, "A/B2"},
      {2, "A/B/C",   "base-deleted", NO_COPY_FROM},
      {2, "A/B/C/D", "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},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Now resolve is possible. */
  SVN_ERR(sbox_wc_resolve(&b, "A/B", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    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"},
      {0, "A/B/C/D",  "normal",       2, "A/B/C/D"},
      {2, "A/B",      "base-deleted", NO_COPY_FROM, "A/B2"},
      {2, "A/B/C",    "base-deleted", NO_COPY_FROM},
      {2, "A/B/C/D",  "base-deleted", NO_COPY_FROM},
      {2, "A/B2",     "normal",       2, "A/B", MOVED_HERE},
      {2, "A/B2/C",   "normal",       2, "A/B/C", MOVED_HERE},
      {2, "A/B2/C/D", "normal",       2, "A/B/C/D", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Subtree update that only bumps. */
  SVN_ERR(sbox_wc_update(&b, "A/B/C", 3));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       1, ""},
      {0, "A",        "normal",       1, "A"},
      {0, "A/B",      "normal",       2, "A/B"},
      {0, "A/B/C",    "normal",       3, "A/B/C"},
      {0, "A/B/C/D",  "normal",       3, "A/B/C/D"},
      {2, "A/B",      "base-deleted", NO_COPY_FROM, "A/B2"},
      {2, "A/B/C",    "base-deleted", NO_COPY_FROM},
      {2, "A/B/C/D",  "base-deleted", NO_COPY_FROM},
      {2, "A/B2",     "normal",       2, "A/B", MOVED_HERE},
      {2, "A/B2/C",   "normal",       2, "A/B/C", MOVED_HERE},
      {2, "A/B2/C/D", "normal",       2, "A/B/C/D", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Resolve fails because of the mixed-revision. */
  err = sbox_wc_resolve(&b, "A/B", svn_depth_empty,
                        svn_wc_conflict_choose_mine_conflict);
  SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE);

  /* Update allowed while tree-conflict is present. */
  SVN_ERR(sbox_wc_update(&b, "A/B", 3));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       1, ""},
      {0, "A",        "normal",       1, "A"},
      {0, "A/B",      "normal",       3, "A/B"},
      {0, "A/B/C",    "normal",       3, "A/B/C"},
      {0, "A/B/C/D",  "normal",       3, "A/B/C/D"},
      {2, "A/B",      "base-deleted", NO_COPY_FROM, "A/B2"},
      {2, "A/B/C",    "base-deleted", NO_COPY_FROM},
      {2, "A/B/C/D",  "base-deleted", NO_COPY_FROM},
      {2, "A/B2",     "normal",       2, "A/B", MOVED_HERE},
      {2, "A/B2/C",   "normal",       2, "A/B/C", MOVED_HERE},
      {2, "A/B2/C/D", "normal",       2, "A/B/C/D", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Now resolve works. */
  SVN_ERR(sbox_wc_resolve(&b, "A/B", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       1, ""},
      {0, "A",        "normal",       1, "A"},
      {0, "A/B",      "normal",       3, "A/B"},
      {0, "A/B/C",    "normal",       3, "A/B/C"},
      {0, "A/B/C/D",  "normal",       3, "A/B/C/D"},
      {2, "A/B",      "base-deleted", NO_COPY_FROM, "A/B2"},
      {2, "A/B/C",    "base-deleted", NO_COPY_FROM},
      {2, "A/B/C/D",  "base-deleted", NO_COPY_FROM},
      {2, "A/B2",     "normal",       3, "A/B", MOVED_HERE},
      {2, "A/B2/C",   "normal",       3, "A/B/C", MOVED_HERE},
      {2, "A/B2/C/D", "normal",       3, "A/B/C/D", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Partial switch of source. */
  SVN_ERR(sbox_wc_switch(&b, "A", "/P", svn_depth_immediates));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       1, ""},
      {0, "A",        "normal",       4, "P"},
      {0, "A/B",      "normal",       4, "P/B"},
      {0, "A/B/C",    "normal",       3, "A/B/C"},
      {0, "A/B/C/D",  "normal",       3, "A/B/C/D"},
      {2, "A/B",      "base-deleted", NO_COPY_FROM, "A/B2"},
      {2, "A/B/C",    "base-deleted", NO_COPY_FROM},
      {2, "A/B/C/D",  "base-deleted", NO_COPY_FROM},
      {2, "A/B2",     "normal",       3, "A/B", MOVED_HERE},
      {2, "A/B2/C",   "normal",       3, "A/B/C", MOVED_HERE},
      {2, "A/B2/C/D", "normal",       3, "A/B/C/D", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Resolve fails because of the subtree-switch. */
  err = sbox_wc_resolve(&b, "A/B", svn_depth_empty,
                        svn_wc_conflict_choose_mine_conflict);
  SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE);

  /* Switch works while tree-conflict is present. */
  SVN_ERR(sbox_wc_switch(&b, "A", "/P", svn_depth_infinity));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       1, ""},
      {0, "A",        "normal",       4, "P"},
      {0, "A/B",      "normal",       4, "P/B"},
      {0, "A/B/C",    "normal",       4, "P/B/C"},
      {0, "A/B/C/D",  "normal",       4, "P/B/C/D"},
      {2, "A/B",      "base-deleted", NO_COPY_FROM, "A/B2"},
      {2, "A/B/C",    "base-deleted", NO_COPY_FROM},
      {2, "A/B/C/D",  "base-deleted", NO_COPY_FROM},
      {2, "A/B2",     "normal",       3, "A/B", MOVED_HERE},
      {2, "A/B2/C",   "normal",       3, "A/B/C", MOVED_HERE},
      {2, "A/B2/C/D", "normal",       3, "A/B/C/D", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Now resolve works. */
  SVN_ERR(sbox_wc_resolve(&b, "A/B", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       1, ""},
      {0, "A",        "normal",       4, "P"},
      {0, "A/B",      "normal",       4, "P/B"},
      {0, "A/B/C",    "normal",       4, "P/B/C"},
      {0, "A/B/C/D",  "normal",       4, "P/B/C/D"},
      {2, "A/B",      "base-deleted", NO_COPY_FROM, "A/B2"},
      {2, "A/B/C",    "base-deleted", NO_COPY_FROM},
      {2, "A/B/C/D",  "base-deleted", NO_COPY_FROM},
      {2, "A/B2",     "normal",       4, "P/B", MOVED_HERE},
      {2, "A/B2/C",   "normal",       4, "P/B/C", MOVED_HERE},
      {2, "A/B2/C/D", "normal",       4, "P/B/C/D", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_parent_into_child(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_parent_into_child", opts, pool));
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A/B", "B2"));
  SVN_ERR(sbox_wc_move(&b, "A", "B2/A"));
  {
    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, "B2/A"},
      {1, "A/B",    "base-deleted", NO_COPY_FROM},
      {1, "B2",     "normal",       1, "A/B", MOVED_HERE},
      {2, "B2/A",   "normal",       1, "A", MOVED_HERE},
      {2, "B2/A/B", "normal",       1, "A/B", MOVED_HERE},
      {3, "B2/A/B", "base-deleted", NO_COPY_FROM, "B2"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_move(&b, "B2", "A"));
  {
    nodes_row_t nodes[] = {
      {0, "",      "normal",       1, ""},
      {0, "A",     "normal",       1, "A"},
      {0, "A/B",   "normal",       1, "A/B"},
      {1, "A",     "normal",       1, "A/B", FALSE, "A/A", TRUE},
      {1, "A/B",   "base-deleted", NO_COPY_FROM},
      {2, "A/A",   "normal",       1, "A", MOVED_HERE},
      {2, "A/A/B", "normal",       1, "A/B", MOVED_HERE},
      {3, "A/A/B", "base-deleted", NO_COPY_FROM, "A"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_move(&b, "A/A", "A/B"));
  {
    nodes_row_t nodes[] = {
      {0, "",      "normal",       1, ""},
      {0, "A",     "normal",       1, "A"},
      {0, "A/B",   "normal",       1, "A/B"},
      {1, "A",     "normal",       1, "A/B", FALSE, "A/B", TRUE},
      {1, "A/B",   "base-deleted", NO_COPY_FROM},
      {2, "A/B",   "normal",       1, "A", MOVED_HERE},
      {2, "A/B/B", "normal",       1, "A/B", MOVED_HERE},
      {3, "A/B/B", "base-deleted", NO_COPY_FROM, "A"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_depth_expand(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_depth_expand", opts, pool));
  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/A/A"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 0));

  SVN_ERR(sbox_wc_update_depth(&b, "", 1, svn_depth_immediates, TRUE));
  SVN_ERR(sbox_wc_update_depth(&b, "A", 1, svn_depth_immediates, TRUE));
  /* Make A/B not present */
  SVN_ERR(sbox_wc_update_depth(&b, "A/B", 0, svn_depth_immediates, TRUE));

  SVN_ERR(sbox_wc_move(&b, "A", "C"));
  SVN_ERR(sbox_wc_mkdir(&b, "C/A/A")); /* Local addition obstruction */
  SVN_ERR(sbox_wc_copy(&b, "C/A", "C/B")); /* Copied obstruction */

  {
    nodes_row_t nodes[] = {
      {0, "",       "normal",       1, "" },

      {0, "A",      "normal",       1, "A" },
      {1, "A",      "base-deleted", NO_COPY_FROM, "C" },
      {0, "A/A",    "normal",       1, "A/A" },
      {1, "A/A",    "base-deleted", NO_COPY_FROM },
      {0, "A/B",    "not-present",  0, "A/B" },

      {1, "C",      "normal",       1, "A", MOVED_HERE },

      {1, "C/A",    "normal",       1, "A/A", MOVED_HERE },
      {3, "C/A/A",  "normal",       NO_COPY_FROM },

      {1, "C/B",    "not-present",  0, "A/B", MOVED_HERE},
      {2, "C/B",    "normal",       1, "A/A" },
      {3, "C/B/A",  "normal",       NO_COPY_FROM },
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_update_depth(&b, "", 1, svn_depth_infinity, TRUE));

  /* And now verify that there are no not-present nodes left and a
     consistent working copy */
  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       1, "" },

      {0, "A",       "normal",       1, "A" },
      {0, "A/A",     "normal",       1, "A/A" },
      {0, "A/A/A",   "normal",       1, "A/A/A" },
      {0, "A/A/A/A", "normal",       1, "A/A/A/A" },
      {0, "A/B",     "normal",       1, "A/B" },
      {0, "A/B/A",   "normal",       1, "A/B/A" },
      {0, "A/B/A/A", "normal",       1, "A/B/A/A" },

      {1, "A",       "base-deleted", NO_COPY_FROM, "C" },
      {1, "A/A",     "base-deleted", NO_COPY_FROM },
      {1, "A/A/A",   "base-deleted", NO_COPY_FROM },
      {1, "A/B",     "base-deleted", NO_COPY_FROM },
      {1, "A/B/A",   "base-deleted", NO_COPY_FROM },
      {1, "A/B/A/A", "base-deleted", NO_COPY_FROM },
      {1, "A/A/A/A", "base-deleted", NO_COPY_FROM },

      {1, "C",       "normal",       1, "A", MOVED_HERE },
      {1, "C/A",     "normal",       1, "A/A", MOVED_HERE },
      {1, "C/B",     "not-present",  0, "A/B", MOVED_HERE},

      {2, "C/B",     "normal",       1, "A/A" },

      {3, "C/A/A",   "normal",       NO_COPY_FROM },
      {3, "C/B/A",   "normal",       NO_COPY_FROM },

      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* This used to cause a segfault. Then it asserted in a different place */
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));

  /* And now verify that there are no not-present nodes left and a
     consistent working copy */
  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       1, "" },

      {0, "A",       "normal",       1, "A" },
      {0, "A/A",     "normal",       1, "A/A" },
      {0, "A/A/A",   "normal",       1, "A/A/A" },
      {0, "A/A/A/A", "normal",       1, "A/A/A/A" },
      {0, "A/B",     "normal",       1, "A/B" },
      {0, "A/B/A",   "normal",       1, "A/B/A" },
      {0, "A/B/A/A", "normal",       1, "A/B/A/A" },

      {1, "A",       "base-deleted", NO_COPY_FROM, "C" },
      {1, "A/A",     "base-deleted", NO_COPY_FROM },
      {1, "A/A/A",   "base-deleted", NO_COPY_FROM },
      {1, "A/B",     "base-deleted", NO_COPY_FROM },
      {1, "A/B/A",   "base-deleted", NO_COPY_FROM },
      {1, "A/B/A/A", "base-deleted", NO_COPY_FROM },
      {1, "A/A/A/A", "base-deleted", NO_COPY_FROM },


      {1, "C",       "normal",       1, "A", MOVED_HERE },
      {1, "C/A",     "normal",       1, "A/A", MOVED_HERE },
      {1, "C/A/A",   "normal",       1, "A/A/A", MOVED_HERE },
      {1, "C/A/A/A", "normal",       1, "A/A/A/A", MOVED_HERE },
      {1, "C/B",     "normal",       1, "A/B", MOVED_HERE },
      {1, "C/B/A",   "normal",       1, "A/B/A", MOVED_HERE },
      {1, "C/B/A/A", "normal",       1, "A/B/A/A", MOVED_HERE },

      {2, "C/B",     "normal",       1, "A/A" },
      {2, "C/B/A",   "base-deleted", NO_COPY_FROM },
      {2, "C/B/A/A", "base-deleted", NO_COPY_FROM },

      {3, "C/A/A",   "normal",       NO_COPY_FROM },
      {3, "C/A/A/A", "base-deleted", NO_COPY_FROM },
      {3, "C/B/A",   "normal",       NO_COPY_FROM },

      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_retract(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_retract", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/D"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/A/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/A/D"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_delete(&b, "A/A/A/A"));
  SVN_ERR(sbox_wc_delete(&b, "A/A/A/C"));
  SVN_ERR(sbox_wc_delete(&b, "A/A/A/D"));
  SVN_ERR(sbox_wc_delete(&b, "A/B/A/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_move(&b, "A/B/A/D", "D"));
  SVN_ERR(sbox_wc_delete(&b, "A/B"));
  SVN_ERR(sbox_wc_move(&b, "A/A", "A/B"));

  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       1, "" },

      {0, "A",       "normal",       1, "A" },
      {0, "A/A",     "normal",       1, "A/A" },
      {0, "A/A/A",   "normal",       1, "A/A/A" },
      {0, "A/A/A/A", "normal",       1, "A/A/A/A" },
      {0, "A/A/A/C", "normal",       1, "A/A/A/C" },
      {0, "A/A/A/D", "normal",       1, "A/A/A/D" },

      {2, "A/A",     "base-deleted", NO_COPY_FROM, "A/B"},
      {2, "A/A/A",   "base-deleted", NO_COPY_FROM },
      {2, "A/A/A/A", "base-deleted", NO_COPY_FROM },
      {2, "A/A/A/C", "base-deleted", NO_COPY_FROM },
      {2, "A/A/A/D", "base-deleted", NO_COPY_FROM },

      {0, "A/B",     "normal",       1, "A/B" },
      {0, "A/B/A",   "normal",       1, "A/B/A" },
      {0, "A/B/A/A", "normal",       1, "A/B/A/A" },
      {0, "A/B/A/C", "normal",       1, "A/B/A/C" },
      {0, "A/B/A/D", "normal",       1, "A/B/A/D" },

      {2, "A/B",     "normal",       1, "A/A", MOVED_HERE },
      {2, "A/B/A",   "normal",       1, "A/A/A", MOVED_HERE },
      {2, "A/B/A/A", "normal",       1, "A/A/A/A", MOVED_HERE },
      {2, "A/B/A/C", "normal",       1, "A/A/A/C", MOVED_HERE },
      {2, "A/B/A/D", "normal",       1, "A/A/A/D", FALSE, "D", TRUE },

      {1, "D",       "normal",       1, "A/B/A/D", MOVED_HERE },

      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_update(&b, "", 2));

  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       2, "" },

      {0, "A",       "normal",       2, "A" },
      {0, "A/A",     "normal",       2, "A/A" },
      {0, "A/A/A",   "normal",       2, "A/A/A" },

      {2, "A/A",     "base-deleted", NO_COPY_FROM, "A/B"},
      {2, "A/A/A",   "base-deleted", NO_COPY_FROM },

      {0, "A/B",     "normal",       2, "A/B" },
      {0, "A/B/A",   "normal",       2, "A/B/A" },
      {0, "A/B/A/A", "normal",       2, "A/B/A/A" },
      {0, "A/B/A/D", "normal",       2, "A/B/A/D" },

      {2, "A/B",     "normal",       1, "A/A", MOVED_HERE },
      {2, "A/B/A",   "normal",       1, "A/A/A", MOVED_HERE },
      {2, "A/B/A/A", "normal",       1, "A/A/A/A", MOVED_HERE },
      {2, "A/B/A/C", "normal",       1, "A/A/A/C", MOVED_HERE },
      {2, "A/B/A/D", "normal",       1, "A/A/A/D", FALSE, "D", TRUE },

      {1, "D",       "normal",       1, "A/B/A/D", MOVED_HERE },

      {0}
    };
    conflict_info_t conflicts[] = {
      {"A/A", FALSE, FALSE, {svn_wc_conflict_action_edit,
                             svn_wc_conflict_reason_moved_away, "A/A"}},
      {"A/B", FALSE, FALSE, {svn_wc_conflict_action_edit,
                             svn_wc_conflict_reason_replaced}},
      { 0 },
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }


  SVN_ERR(sbox_wc_resolve(&b, "A/A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));

  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       2, "" },

      {0, "A",       "normal",       2, "A" },
      {0, "A/A",     "normal",       2, "A/A" },
      {0, "A/A/A",   "normal",       2, "A/A/A" },

      {0, "A/B",     "normal",       2, "A/B" },
      {0, "A/B/A",   "normal",       2, "A/B/A" },
      {0, "A/B/A/A", "normal",       2, "A/B/A/A" },
      {0, "A/B/A/D", "normal",       2, "A/B/A/D" },

      {2, "A/A",     "base-deleted", NO_COPY_FROM, "A/B"},
      {2, "A/A/A",   "base-deleted", NO_COPY_FROM },

      {2, "A/B",     "normal",       2, "A/A", MOVED_HERE },
      {2, "A/B/A",   "normal",       2, "A/A/A", MOVED_HERE },
      {2, "A/B/A/A", "base-deleted", NO_COPY_FROM }, /* ### MISSING! */
      {2, "A/B/A/D", "base-deleted", NO_COPY_FROM, "D" }, /* ### MISSING! */

      /* Still conflicted */
      {1, "D",       "normal",       1, "A/B/A/D", MOVED_HERE },

      {4, "A/B/A/C", "normal",       1, "A/A/A/C"},


      {0}
    };
    conflict_info_t conflicts[] = {
      {"A/B",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                                 svn_wc_conflict_reason_replaced}},
      {"A/B/A/C", FALSE, FALSE, {svn_wc_conflict_action_delete,
                                 svn_wc_conflict_reason_edited}},
      {0}
    };

    SVN_ERR(check_db_rows(&b, "", nodes));
    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }

  /* ### TODO: Resolve via which specific target? */
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity,
                          svn_wc_conflict_choose_mine_conflict));

  {
    nodes_row_t nodes[] = {

      {1, "D",       "normal",       1, "A/B/A/D", MOVED_HERE },

      {0}
    };
    SVN_ERR(check_db_rows(&b, "D", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_delete_file_externals(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_delete_file_externals", opts,
                                   pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_file_write(&b, "f", "New file"));
  SVN_ERR(sbox_wc_add(&b, "f"));
  SVN_ERR(sbox_wc_propset(&b, "svn:externals", "^/f B/P/g", "A"));
  SVN_ERR(sbox_wc_propset(&b, "svn:externals", "^/f Q/g\n^/f g", "A/B"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       1, ""},
      {0, "A",       "normal",       1, "A"},
      {0, "A/B",     "normal",       1, "A/B"},
      {0, "f",       "normal",       1, "f"},
      {0, "A/B/g",   "normal",       1, "f", TRUE},
      {0, "A/B/P/g", "normal",       1, "f", TRUE},
      {0, "A/B/Q/g", "normal",       1, "f", TRUE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Delete removes the file external rows. */
  SVN_ERR(sbox_wc_delete(&b, "A"));
  {
    nodes_row_t nodes[] = {
      {0, "",    "normal",       1, ""},
      {0, "A",   "normal",       1, "A"},
      {0, "A/B", "normal",       1, "A/B"},
      {0, "f",   "normal",       1, "f"},
      {1, "A",   "base-deleted", NO_COPY_FROM},
      {1, "A/B", "base-deleted", NO_COPY_FROM},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Revert doesn't restore the file external rows... */
  SVN_ERR(sbox_wc_revert(&b, "A", svn_depth_infinity));
  {
    nodes_row_t nodes[] = {
      {0, "",    "normal",       1, ""},
      {0, "A",   "normal",       1, "A"},
      {0, "A/B", "normal",       1, "A/B"},
      {0, "f",   "normal",       1, "f"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }
  /* ... but update does. */
  SVN_ERR(sbox_wc_update(&b, "", 1));
  {
    nodes_row_t nodes[] = {
      {0, "",        "normal",       1, ""},
      {0, "A",       "normal",       1, "A"},
      {0, "A/B",     "normal",       1, "A/B"},
      {0, "f",       "normal",       1, "f"},
      {0, "A/B/g",   "normal",       1, "f", TRUE},
      {0, "A/B/P/g", "normal",       1, "f", TRUE},
      {0, "A/B/Q/g", "normal",       1, "f", TRUE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Move removes the file external rows. */
  SVN_ERR(sbox_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, "f",    "normal",       1, "f"},
      {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 adds file external rows to the copy. */
  SVN_ERR(sbox_wc_update(&b, "", 1));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       1, ""},
      {0, "A",        "normal",       1, "A"},
      {0, "A/B",      "normal",       1, "A/B"},
      {0, "f",        "normal",       1, "f"},
      {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, "A2/B/g",   "normal",       1, "f", TRUE},
      {0, "A2/B/P/g", "normal",       1, "f", TRUE},
      {0, "A2/B/Q/g", "normal",       1, "f", TRUE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
update_with_tree_conflict(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "update_with_tree_conflict", opts,
                                   pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_move(&b, "A", "A2"));

  SVN_ERR(sbox_wc_update_depth(&b, "A", 2, svn_depth_empty, FALSE));
  {
    nodes_row_t nodes[] = {
      {0, "",       "normal",       1, ""},
      {0, "A",      "normal",       2, "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));
  }

  SVN_ERR(sbox_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",       1, "A", MOVED_HERE},
      {1, "A2/B",   "normal",       1, "A/B", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Second update overwrote the existing tree-conflict and that
     causes the move-update to assert. */
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_infinity,
                          svn_wc_conflict_choose_mine_conflict));
  {
    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));
  }

  /* Same again but second update is A/B rather than A which triggers
     the problem through open_root rather than open_directory. */
  SVN_ERR(sbox_wc_revert(&b, "", svn_depth_infinity));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_move(&b, "A", "A2"));
  SVN_ERR(sbox_wc_update_depth(&b, "A", 2, svn_depth_empty, FALSE));
  SVN_ERR(sbox_wc_update(&b, "A/B", 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",       1, "A", MOVED_HERE},
      {1, "A2/B",   "normal",       1, "A/B", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_infinity,
                          svn_wc_conflict_choose_mine_conflict));
  {
    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 *
move_update_parent_replace(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_update_parent_replace", opts,
                                   pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_delete(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_move(&b, "A/B/C", "A/C"));

  /* Update breaks the move and leaves a conflict. */
  SVN_ERR(sbox_wc_update(&b, "", 2));
  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       2, ""},
      {0, "A",        "normal",       2, "A"},
      {0, "A/B",      "normal",       2, "A/B"},

      {2, "A/C",      "normal",       1, "A/B/C", MOVED_HERE},

      {2, "A/B",      "normal",       1, "A/B"},
      {2, "A/B/C",    "normal",       1, "A/B/C", FALSE},

      {3, "A/B/C",    "base-deleted", NO_COPY_FROM, "A/C"},

      {0}
    };
    conflict_info_t conflicts[] = {
      {"A/B", FALSE, FALSE, {svn_wc_conflict_action_replace,
                             svn_wc_conflict_reason_edited}},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }

  SVN_ERR(sbox_wc_resolve(&b, "A/B", svn_depth_infinity,
                          svn_wc_conflict_choose_merged));

  {
    nodes_row_t nodes[] = {
      {0, "",         "normal",       2, ""},
      {0, "A",        "normal",       2, "A"},
      {0, "A/B",      "normal",       2, "A/B"},
      {2, "A/C",      "normal",       1, "A/B/C", MOVED_HERE},
      {2, "A/B",      "normal",       1, "A/B"},
      {2, "A/B/C",    "normal",       1, "A/B/C", FALSE},
      {3, "A/B/C",    "base-deleted", NO_COPY_FROM, "A/C"},

      {0}
    };

    SVN_ERR(check_db_rows(&b, "", nodes));
    SVN_ERR(check_db_conflicts(&b, "", NULL));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
copy_mixed_rev_mods(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "copy_mixed_rev_mods", opts,
                                   pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_update(&b, "A/B", 2));
  SVN_ERR(sbox_wc_delete(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));

  {
    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"},
      {2, "A/B",   "normal",       NO_COPY_FROM},
      {2, "A/B/C", "base-deleted", NO_COPY_FROM},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_copy(&b, "A", "X"));
  {
    nodes_row_t nodes[] = {
      {1, "X",   "normal",      1, "A"},
      {1, "X/B", "not-present", 2, "A/B"},
      {2, "X/B", "normal",      NO_COPY_FROM},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "X", nodes));
  }

  SVN_ERR(sbox_wc_commit(&b, "X"));
  {
    nodes_row_t nodes[] = {
      {0, "X",   "normal", 3, "X"},
      {0, "X/B", "normal", 3, "X/B"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "X", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_child_to_parent_revert(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_child_to_parent_revert", opts,
                                   pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_commit(&b, ""));


  SVN_ERR(sbox_wc_move(&b, "A/B", "B"));
  SVN_ERR(sbox_wc_delete(&b, "A"));

  /* Verify that the move is still recorded correctly */
  {
    nodes_row_t nodes[] = {
      {0, "",    "normal", 0, ""},
      {0, "A",   "normal", 1, "A"},
      {0, "A/B", "normal", 1, "A/B"},

      {1, "A",   "base-deleted", NO_COPY_FROM},
      {1, "A/B", "base-deleted", NO_COPY_FROM, "B"},

      {1, "B", "normal", 1, "A/B", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_revert(&b, "A", svn_depth_infinity));

  /* Verify that the move is now just a copy */
  {
    nodes_row_t nodes[] = {
      {0, "",    "normal", 0, ""},
      {0, "A",   "normal", 1, "A"},
      {0, "A/B", "normal", 1, "A/B"},

      {1, "B", "normal", 1, "A/B"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }


  return SVN_NO_ERROR;
}

static svn_error_t *
move_delete_intermediate(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_delete_intermediate", opts,
                                   pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "B"));
  SVN_ERR(sbox_wc_mkdir(&b, "B/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "B/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "B/A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "C"));
  SVN_ERR(sbox_wc_mkdir(&b, "C/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "C/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "C/A/A/A"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_1"));

  SVN_ERR(sbox_wc_delete(&b, "A"));
  SVN_ERR(sbox_wc_move(&b, "B", "A"));

  SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_2"));

  SVN_ERR(sbox_wc_delete(&b, "A/A"));
  SVN_ERR(sbox_wc_move(&b, "C/A", "A/A"));

  SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_3"));

  /* Verify that the move is still recorded correctly */
  {
    nodes_row_t nodes[] = {

      {0, "",           "normal",       0, ""},

      {1, "AAA_1",      "normal",       1, "A/A/A",             MOVED_HERE},
      {1, "AAA_1/A",    "normal",       1, "A/A/A/A",           MOVED_HERE},
      {1, "AAA_2",      "normal",       1, "B/A/A",             MOVED_HERE},
      {1, "AAA_2/A",    "normal",       1, "B/A/A/A",           MOVED_HERE},
      {1, "AAA_3",      "normal",       1, "C/A/A",             MOVED_HERE},
      {1, "AAA_3/A",    "normal",       1, "C/A/A/A",           MOVED_HERE},

      {0, "A",          "normal",       1, "A"},
      {0, "A/A",        "normal",       1, "A/A"},
      {0, "A/A/A",      "normal",       1, "A/A/A"},
      {0, "A/A/A/A",    "normal",       1, "A/A/A/A"},

      {1, "A",          "normal",       1, "B",                 MOVED_HERE},
      {1, "A/A",        "normal",       1, "B/A",               MOVED_HERE},
      {1, "A/A/A",      "normal",       1, "B/A/A", FALSE, "AAA_1",   TRUE},
      {1, "A/A/A/A",    "normal",       1, "B/A/A/A",           MOVED_HERE},

      {2, "A/A",        "normal",       1, "C/A", MOVED_HERE},
      {2, "A/A/A",      "normal",       1, "C/A/A", FALSE, "AAA_2",   TRUE},
      {2, "A/A/A/A",    "normal",       1, "C/A/A/A",           MOVED_HERE},

      {3, "A/A/A",      "base-deleted", NO_COPY_FROM,      "AAA_3"},
      {3, "A/A/A/A",    "base-deleted", NO_COPY_FROM},

      {0, "B",          "normal",       1, "B"},
      {0, "B/A",        "normal",       1, "B/A"},
      {0, "B/A/A",      "normal",       1, "B/A/A"},
      {0, "B/A/A/A",    "normal",       1, "B/A/A/A"},

      {1, "B",          "base-deleted", NO_COPY_FROM, "A"},
      {1, "B/A",        "base-deleted", NO_COPY_FROM},
      {1, "B/A/A",      "base-deleted", NO_COPY_FROM},
      {1, "B/A/A/A",    "base-deleted", NO_COPY_FROM},

      {0, "C",          "normal",       1, "C"},
      {0, "C/A",        "normal",       1, "C/A"},
      {0, "C/A/A",      "normal",       1, "C/A/A"},
      {0, "C/A/A/A",    "normal",       1, "C/A/A/A"},

      {2, "C/A",        "base-deleted", NO_COPY_FROM, "A/A"},
      {2, "C/A/A",      "base-deleted", NO_COPY_FROM},
      {2, "C/A/A/A",    "base-deleted", NO_COPY_FROM},

      {0},
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Ok, now we are in the very ugly case where A/A/A is moved away 3 times */

  /* Let's delete A */
  SVN_ERR(sbox_wc_delete(&b, "A"));

  /* AAA_1, AAA_2 and AAA_3 should still be moves after deleting A */
  {
    nodes_row_t nodes[] = {

      {0, "",           "normal",       0, ""},

      {1, "AAA_1",      "normal",       1, "A/A/A",             MOVED_HERE},
      {1, "AAA_1/A",    "normal",       1, "A/A/A/A",           MOVED_HERE},

      {1, "AAA_2",      "normal",       1, "B/A/A",             MOVED_HERE},
      {1, "AAA_2/A",    "normal",       1, "B/A/A/A",           MOVED_HERE},

      {1, "AAA_3",      "normal",       1, "C/A/A",             MOVED_HERE},
      {1, "AAA_3/A",    "normal",       1, "C/A/A/A",           MOVED_HERE},

      {0, "A",          "normal",       1, "A"},
      {0, "A/A",        "normal",       1, "A/A"},
      {0, "A/A/A",      "normal",       1, "A/A/A"},
      {0, "A/A/A/A",    "normal",       1, "A/A/A/A"},

      {1, "A",          "base-deleted", NO_COPY_FROM},
      {1, "A/A",        "base-deleted", NO_COPY_FROM},
      {1, "A/A/A",      "base-deleted", NO_COPY_FROM, "AAA_1"},
      {1, "A/A/A/A",    "base-deleted", NO_COPY_FROM},

      {0, "B",          "normal",       1, "B"},
      {0, "B/A",        "normal",       1, "B/A"},
      {0, "B/A/A",      "normal",       1, "B/A/A"},
      {0, "B/A/A/A",    "normal",       1, "B/A/A/A"},

      {1, "B",          "base-deleted", NO_COPY_FROM},
      {1, "B/A",        "base-deleted", NO_COPY_FROM},
      {1, "B/A/A",      "base-deleted", NO_COPY_FROM, "AAA_2"},
      {1, "B/A/A/A",    "base-deleted", NO_COPY_FROM},

      {0, "C",          "normal",       1, "C"},
      {0, "C/A",        "normal",       1, "C/A"},
      {0, "C/A/A",      "normal",       1, "C/A/A"},
      {0, "C/A/A/A",    "normal",       1, "C/A/A/A"},

      {2, "C/A",        "base-deleted", NO_COPY_FROM},
      {2, "C/A/A",      "base-deleted", NO_COPY_FROM, "AAA_3"},
      {2, "C/A/A/A",    "base-deleted", NO_COPY_FROM},

      {0},
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_revert_intermediate(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_revert_intermediate", opts,
                                   pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "B"));
  SVN_ERR(sbox_wc_mkdir(&b, "B/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "B/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "B/A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "C"));
  SVN_ERR(sbox_wc_mkdir(&b, "C/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "C/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "C/A/A/A"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_1"));

  SVN_ERR(sbox_wc_delete(&b, "A"));
  SVN_ERR(sbox_wc_move(&b, "B", "A"));

  SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_2"));

  SVN_ERR(sbox_wc_delete(&b, "A/A"));
  SVN_ERR(sbox_wc_move(&b, "C/A", "A/A"));

  SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_3"));

  /* Verify that the move is still recorded correctly */
  {
    nodes_row_t nodes[] = {

      {0, "",           "normal",       0, ""},

      {1, "AAA_1",      "normal",       1, "A/A/A",             MOVED_HERE},
      {1, "AAA_1/A",    "normal",       1, "A/A/A/A",           MOVED_HERE},
      {1, "AAA_2",      "normal",       1, "B/A/A",             MOVED_HERE},
      {1, "AAA_2/A",    "normal",       1, "B/A/A/A",           MOVED_HERE},
      {1, "AAA_3",      "normal",       1, "C/A/A",             MOVED_HERE},
      {1, "AAA_3/A",    "normal",       1, "C/A/A/A",           MOVED_HERE},

      {0, "A",          "normal",       1, "A"},
      {0, "A/A",        "normal",       1, "A/A"},
      {0, "A/A/A",      "normal",       1, "A/A/A"},
      {0, "A/A/A/A",    "normal",       1, "A/A/A/A"},

      {1, "A",          "normal",       1, "B",                 MOVED_HERE},
      {1, "A/A",        "normal",       1, "B/A",               MOVED_HERE},
      {1, "A/A/A",      "normal",       1, "B/A/A", FALSE, "AAA_1",   TRUE},
      {1, "A/A/A/A",    "normal",       1, "B/A/A/A",           MOVED_HERE},

      {2, "A/A",        "normal",       1, "C/A", MOVED_HERE},
      {2, "A/A/A",      "normal",       1, "C/A/A", FALSE, "AAA_2",   TRUE},
      {2, "A/A/A/A",    "normal",       1, "C/A/A/A",           MOVED_HERE},

      {3, "A/A/A",      "base-deleted", NO_COPY_FROM,      "AAA_3"},
      {3, "A/A/A/A",    "base-deleted", NO_COPY_FROM},

      {0, "B",          "normal",       1, "B"},
      {0, "B/A",        "normal",       1, "B/A"},
      {0, "B/A/A",      "normal",       1, "B/A/A"},
      {0, "B/A/A/A",    "normal",       1, "B/A/A/A"},

      {1, "B",          "base-deleted", NO_COPY_FROM, "A"},
      {1, "B/A",        "base-deleted", NO_COPY_FROM},
      {1, "B/A/A",      "base-deleted", NO_COPY_FROM},
      {1, "B/A/A/A",    "base-deleted", NO_COPY_FROM},

      {0, "C",          "normal",       1, "C"},
      {0, "C/A",        "normal",       1, "C/A"},
      {0, "C/A/A",      "normal",       1, "C/A/A"},
      {0, "C/A/A/A",    "normal",       1, "C/A/A/A"},

      {2, "C/A",        "base-deleted", NO_COPY_FROM, "A/A"},
      {2, "C/A/A",      "base-deleted", NO_COPY_FROM},
      {2, "C/A/A/A",    "base-deleted", NO_COPY_FROM},

      {0},
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Ok, now we are in the very ugly case where A/A/A is moved away 3 times */

  /* Let's revert A */
  SVN_ERR(sbox_wc_revert(&b, "A", svn_depth_infinity));

  /* AAA_1 should now be a copy, but AAA_2 and AAA_3 should still be moves,
     but now from the original location instead of from "A/A/A" */
  {
    nodes_row_t nodes[] = {

      {0, "",           "normal",       0, ""},

      {1, "AAA_1",      "normal",       1, "A/A/A",},
      {1, "AAA_1/A",    "normal",       1, "A/A/A/A"},
      {1, "AAA_2",      "normal",       1, "B/A/A",             MOVED_HERE},
      {1, "AAA_2/A",    "normal",       1, "B/A/A/A",           MOVED_HERE},
      {1, "AAA_3",      "normal",       1, "C/A/A",             MOVED_HERE},
      {1, "AAA_3/A",    "normal",       1, "C/A/A/A",           MOVED_HERE},

      {0, "A",          "normal",       1, "A"},
      {0, "A/A",        "normal",       1, "A/A"},
      {0, "A/A/A",      "normal",       1, "A/A/A"},
      {0, "A/A/A/A",    "normal",       1, "A/A/A/A"},

      {0, "B",          "normal",       1, "B"},
      {0, "B/A",        "normal",       1, "B/A"},
      {0, "B/A/A",      "normal",       1, "B/A/A"},
      {0, "B/A/A/A",    "normal",       1, "B/A/A/A"},

      {1, "B",          "base-deleted", NO_COPY_FROM},
      {1, "B/A",        "base-deleted", NO_COPY_FROM},
      {1, "B/A/A",      "base-deleted", NO_COPY_FROM, "AAA_2"},
      {1, "B/A/A/A",    "base-deleted", NO_COPY_FROM},

      {0, "C",          "normal",       1, "C"},
      {0, "C/A",        "normal",       1, "C/A"},
      {0, "C/A/A",      "normal",       1, "C/A/A"},
      {0, "C/A/A/A",    "normal",       1, "C/A/A/A"},

      {2, "C/A",        "base-deleted", NO_COPY_FROM},
      {2, "C/A/A",      "base-deleted", NO_COPY_FROM, "AAA_3"},
      {2, "C/A/A/A",    "base-deleted", NO_COPY_FROM},

      {0},
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_replace_ancestor_with_child(const svn_test_opts_t *opts, apr_pool_t *pool)
{
    svn_test__sandbox_t b;

    SVN_ERR(svn_test__sandbox_create(&b, "move_replace_ancestor_with_child", opts,
        pool));

    SVN_ERR(sbox_wc_mkdir(&b, "A"));
    SVN_ERR(sbox_wc_mkdir(&b, "A/A"));

    SVN_ERR(sbox_wc_commit(&b, ""));
    SVN_ERR(sbox_wc_update(&b, "", 1));

    SVN_ERR(sbox_wc_move(&b, "A", "A2"));

    {
      nodes_row_t nodes[] = {

        { 0, "",            "normal",       1, "" },

        { 0, "A",           "normal",       1, "A"},
        { 0, "A/A",         "normal",       1, "A/A" },

        { 1, "A",           "base-deleted", NO_COPY_FROM , "A2"},
        { 1, "A/A",         "base-deleted", NO_COPY_FROM },

        { 1, "A2",          "normal",       1, "A",     MOVED_HERE },
        { 1, "A2/A",        "normal",       1, "A/A",   MOVED_HERE },

        { 0 },
      };
      SVN_ERR(check_db_rows(&b, "", nodes));
    }

    SVN_ERR(sbox_wc_move(&b, "A2/A", "A"));

    {
      nodes_row_t nodes[] = {
        { 0, "",            "normal",       1, "" },

        { 0, "A",           "normal",       1, "A"},
        { 0, "A/A",         "normal",       1, "A/A" },

        { 1, "A",           "normal",       1, "A/A", FALSE, "A2", TRUE },
        { 1, "A/A",         "base-deleted", NO_COPY_FROM },

        { 1, "A2",          "normal",       1, "A",     MOVED_HERE },
        { 1, "A2/A",        "normal",       1, "A/A",   MOVED_HERE },

        { 2, "A2/A",        "base-deleted", NO_COPY_FROM, "A" },
        { 0 },
      };
      SVN_ERR(check_db_rows(&b, "", nodes));
    }

    /* ### This currently fails with an assertion in maintainer mode */
    SVN_ERR(sbox_wc_delete(&b, "A2"));

    {
      nodes_row_t nodes[] = {
        { 0, "",            "normal",       1, "" },

        { 0, "A",           "normal",       1, "A"},
        { 0, "A/A",         "normal",       1, "A/A" },

        { 1, "A",           "normal",       1, "A/A", MOVED_HERE },
        { 1, "A/A",         "base-deleted", NO_COPY_FROM, "A" },

        { 0 },
      };
      SVN_ERR(check_db_rows(&b, "", nodes));
    }

    SVN_ERR(sbox_wc_commit(&b, "A"));

    return SVN_NO_ERROR;
}

static svn_error_t *
move_twice_within_delete(const svn_test_opts_t *opts, apr_pool_t *pool)
{
    svn_test__sandbox_t b;

    SVN_ERR(svn_test__sandbox_create(&b, "move_twice_within_delete", opts,
        pool));

    SVN_ERR(sbox_wc_mkdir(&b, "A"));
    SVN_ERR(sbox_wc_mkdir(&b, "A/A"));
    SVN_ERR(sbox_wc_mkdir(&b, "A/A/A"));

    SVN_ERR(sbox_wc_commit(&b, ""));
    SVN_ERR(sbox_wc_update(&b, "", 1));

    SVN_ERR(sbox_wc_mkdir(&b, "B"));
    SVN_ERR(sbox_wc_move(&b, "A", "B/A"));
    SVN_ERR(sbox_wc_move(&b, "B/A/A", "B/AA"));
    SVN_ERR(sbox_wc_move(&b, "B/AA/A", "AA"));

    {
      nodes_row_t nodes[] = {

        { 0, "",          "normal",       1, "" },

        { 0, "A",         "normal",       1, "A" },
        { 0, "A/A",       "normal",       1, "A/A" },
        { 0, "A/A/A",     "normal",       1, "A/A/A" },

        { 1, "A",         "base-deleted", NO_COPY_FROM, "B/A" },
        { 1, "A/A",       "base-deleted", NO_COPY_FROM },
        { 1, "A/A/A",     "base-deleted", NO_COPY_FROM },

        { 1, "AA",        "normal",       1, "A/A/A", MOVED_HERE },

        { 1, "B",         "normal",       NO_COPY_FROM },
        { 2, "B/A",       "normal",       1, "A",       MOVED_HERE },
        { 2, "B/A/A",     "normal",       1, "A/A",     MOVED_HERE },
        { 2, "B/A/A/A",   "normal",       1, "A/A/A",   MOVED_HERE },

        { 3, "B/A/A",     "base-deleted", NO_COPY_FROM, "B/AA" },
        { 3, "B/A/A/A",   "base-deleted", NO_COPY_FROM },

        { 2, "B/AA",      "normal",       1, "A/A", MOVED_HERE},
        { 2, "B/AA/A",    "normal",       1, "A/A/A", MOVED_HERE },

        { 3, "B/AA/A",    "base-deleted", NO_COPY_FROM, "AA" },

        { 0 },
      };
      SVN_ERR(check_db_rows(&b, "", nodes));
    }

    SVN_ERR(sbox_wc_delete(&b, "B"));

    {
      nodes_row_t nodes[] = {
        { 0, "",        "normal", 1, "" },

        { 0, "A",       "normal", 1, "A" },
        { 0, "A/A",     "normal", 1, "A/A" },
        { 0, "A/A/A",   "normal", 1, "A/A/A" },

        { 1, "A",       "base-deleted", NO_COPY_FROM },
        { 1, "A/A",     "base-deleted", NO_COPY_FROM },
        { 1, "A/A/A",   "base-deleted", NO_COPY_FROM, "AA" },

        { 1, "AA", "normal", 1, "A/A/A", MOVED_HERE },

        { 0 },
      };
        SVN_ERR(check_db_rows(&b, "", nodes));
    }

    return SVN_NO_ERROR;
}

/* Helper function for 4 move4 tests */
static svn_error_t *
init_move4(svn_test__sandbox_t *sandbox,
           const char *test_name,
           const svn_test_opts_t *opts,
           svn_boolean_t move_away,
           apr_pool_t *pool)
{
  SVN_ERR(svn_test__sandbox_create(sandbox, test_name, opts, pool));

  SVN_ERR(sbox_wc_mkdir(sandbox, "A"));
  SVN_ERR(sbox_wc_mkdir(sandbox, "A/A"));
  SVN_ERR(sbox_wc_mkdir(sandbox, "A/A/A"));
  SVN_ERR(sbox_wc_mkdir(sandbox, "A/A/A/A"));

  SVN_ERR(sbox_wc_mkdir(sandbox, "B"));
  SVN_ERR(sbox_wc_mkdir(sandbox, "B/A"));
  SVN_ERR(sbox_wc_mkdir(sandbox, "B/A/A"));
  SVN_ERR(sbox_wc_mkdir(sandbox, "B/A/A/A"));

  SVN_ERR(sbox_wc_mkdir(sandbox, "C"));
  SVN_ERR(sbox_wc_mkdir(sandbox, "C/A"));
  SVN_ERR(sbox_wc_mkdir(sandbox, "C/A/A"));
  SVN_ERR(sbox_wc_mkdir(sandbox, "C/A/A/A"));

  SVN_ERR(sbox_wc_mkdir(sandbox, "D"));
  SVN_ERR(sbox_wc_mkdir(sandbox, "D/A"));
  SVN_ERR(sbox_wc_mkdir(sandbox, "D/A/A"));
  SVN_ERR(sbox_wc_mkdir(sandbox, "D/A/A/A"));

  SVN_ERR(sbox_wc_commit(sandbox, "")); /* r1 */

  if (strstr(test_name, "_edit_"))
    {
      SVN_ERR(sbox_wc_propset(sandbox, "key", "value", "A/A/A"));
      SVN_ERR(sbox_wc_propset(sandbox, "key", "value", "B/A/A"));
      SVN_ERR(sbox_wc_propset(sandbox, "key", "value", "C/A/A"));
      SVN_ERR(sbox_wc_propset(sandbox, "key", "value", "D/A/A"));
      SVN_ERR(sbox_wc_propset(sandbox, "key", "value", "A/A/A/A"));
      SVN_ERR(sbox_wc_propset(sandbox, "key", "value", "B/A/A/A"));
      SVN_ERR(sbox_wc_propset(sandbox, "key", "value", "C/A/A/A"));
      SVN_ERR(sbox_wc_propset(sandbox, "key", "value", "D/A/A/A"));
    }
  else if (strstr(test_name, "_delete_"))
    {
      SVN_ERR(sbox_wc_delete(sandbox, "A/A/A/A"));
      SVN_ERR(sbox_wc_delete(sandbox, "B/A/A/A"));
      SVN_ERR(sbox_wc_delete(sandbox, "C/A/A/A"));
      SVN_ERR(sbox_wc_delete(sandbox, "D/A/A/A"));
    }
  else if (strstr(test_name, "_add_"))
    {
      SVN_ERR(sbox_wc_mkdir(sandbox, "A/A/A/NEW"));
      SVN_ERR(sbox_wc_mkdir(sandbox, "B/A/A/NEW"));
      SVN_ERR(sbox_wc_mkdir(sandbox, "C/A/A/NEW"));
      SVN_ERR(sbox_wc_mkdir(sandbox, "D/A/A/NEW"));
    }
  else if (strstr(test_name, "_replace_"))
    {
      SVN_ERR(sbox_wc_delete(sandbox, "A/A/A/A"));
      SVN_ERR(sbox_wc_delete(sandbox, "B/A/A/A"));
      SVN_ERR(sbox_wc_delete(sandbox, "C/A/A/A"));
      SVN_ERR(sbox_wc_delete(sandbox, "D/A/A/A"));
      SVN_ERR(sbox_file_write(sandbox, "A/A/A/A", "A"));
      SVN_ERR(sbox_file_write(sandbox, "B/A/A/A", "A"));
      SVN_ERR(sbox_file_write(sandbox, "C/A/A/A", "A"));
      SVN_ERR(sbox_file_write(sandbox, "D/A/A/A", "A"));
      SVN_ERR(sbox_wc_add(sandbox, "A/A/A/A"));
      SVN_ERR(sbox_wc_add(sandbox, "B/A/A/A"));
      SVN_ERR(sbox_wc_add(sandbox, "C/A/A/A"));
      SVN_ERR(sbox_wc_add(sandbox, "D/A/A/A"));
    }
  else if (strstr(test_name, "_delself_"))
    {
      SVN_ERR(sbox_wc_delete(sandbox, "A/A/A"));
      SVN_ERR(sbox_wc_delete(sandbox, "B/A/A"));
      SVN_ERR(sbox_wc_delete(sandbox, "C/A/A"));
      SVN_ERR(sbox_wc_delete(sandbox, "D/A/A"));
    }
  else if (strstr(test_name, "_replaceself_"))
    {
      SVN_ERR(sbox_wc_delete(sandbox, "A/A/A"));
      SVN_ERR(sbox_wc_delete(sandbox, "B/A/A"));
      SVN_ERR(sbox_wc_delete(sandbox, "C/A/A"));
      SVN_ERR(sbox_wc_delete(sandbox, "D/A/A"));
      SVN_ERR(sbox_file_write(sandbox, "A/A/A", "A"));
      SVN_ERR(sbox_file_write(sandbox, "B/A/A", "A"));
      SVN_ERR(sbox_file_write(sandbox, "C/A/A", "A"));
      SVN_ERR(sbox_file_write(sandbox, "D/A/A", "A"));
      SVN_ERR(sbox_wc_add(sandbox, "A/A/A"));
      SVN_ERR(sbox_wc_add(sandbox, "B/A/A"));
      SVN_ERR(sbox_wc_add(sandbox, "C/A/A"));
      SVN_ERR(sbox_wc_add(sandbox, "D/A/A"));
    }

  SVN_ERR(sbox_wc_commit(sandbox, ""));
  SVN_ERR(sbox_wc_update(sandbox, "", 1));

  SVN_ERR(sbox_wc_move(sandbox, "A/A/A", "AAA_1"));

  if (move_away)
    SVN_ERR(sbox_wc_move(sandbox, "A", "A_moved"));
  else
    SVN_ERR(sbox_wc_delete(sandbox, "A"));

  SVN_ERR(sbox_wc_move(sandbox, "B", "A"));

  SVN_ERR(sbox_wc_move(sandbox, "A/A/A", "AAA_2"));

  if (move_away)
    SVN_ERR(sbox_wc_move(sandbox, "A/A", "BA_moved"));
  else
    SVN_ERR(sbox_wc_delete(sandbox, "A/A"));

  SVN_ERR(sbox_wc_move(sandbox, "C/A", "A/A"));

  SVN_ERR(sbox_wc_move(sandbox, "A/A/A", "AAA_3"));

  SVN_ERR(sbox_wc_move(sandbox, "D/A/A", "A/A/A"));

  return SVN_NO_ERROR;
}

static svn_error_t *
del4_update_edit_AAA(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(init_move4(&b, "del4_update_edit_AAA", opts, FALSE, pool));

  {
    nodes_row_t nodes[] = {

      { 0, "A/A/A",     "normal",    1, "A/A/A" },
      { 1, "A/A/A",     "normal",    1, "B/A/A", FALSE, "AAA_1", TRUE },
      { 2, "A/A/A",     "normal",    1, "C/A/A", FALSE, "AAA_2", TRUE },
      { 3, "A/A/A",     "normal",    1, "D/A/A", FALSE, "AAA_3", TRUE },

      { 0, "A/A/A/A",   "normal",    1, "A/A/A/A" },
      { 1, "A/A/A/A",   "normal",    1, "B/A/A/A", FALSE, NULL, TRUE },
      { 2, "A/A/A/A",   "normal",    1, "C/A/A/A", FALSE, NULL, TRUE },
      { 3, "A/A/A/A",   "normal",    1, "D/A/A/A", FALSE, NULL, TRUE },

      { 0 },
    };

    SVN_ERR(check_db_rows(&b, "A/A/A", nodes));
  }

  /* Update and resolve via mine strategy */
  SVN_ERR(sbox_wc_update(&b, "", 2));
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));

  {
    nodes_row_t nodes[] = {
      {0, "",                 "normal",       2, ""},
      {0, "A",                "normal",       2, "A"},
      {0, "A/A",              "normal",       2, "A/A"},
      {0, "A/A/A",            "normal",       2, "A/A/A", NOT_MOVED, "key"},
      {0, "A/A/A/A",          "normal",       2, "A/A/A/A", NOT_MOVED, "key"},
      {0, "B",                "normal",       2, "B"},
      {0, "B/A",              "normal",       2, "B/A"},
      {0, "B/A/A",            "normal",       2, "B/A/A", NOT_MOVED, "key"},
      {0, "B/A/A/A",          "normal",       2, "B/A/A/A", NOT_MOVED, "key"},
      {0, "C",                "normal",       2, "C"},
      {0, "C/A",              "normal",       2, "C/A"},
      {0, "C/A/A",            "normal",       2, "C/A/A", NOT_MOVED, "key"},
      {0, "C/A/A/A",          "normal",       2, "C/A/A/A", NOT_MOVED, "key"},
      {0, "D",                "normal",       2, "D"},
      {0, "D/A",              "normal",       2, "D/A"},
      {0, "D/A/A",            "normal",       2, "D/A/A", NOT_MOVED, "key"},
      {0, "D/A/A/A",          "normal",       2, "D/A/A/A", NOT_MOVED, "key"},

      {1, "A",                "normal",       2, "B", MOVED_HERE},
      {1, "A/A",              "normal",       2, "B/A", MOVED_HERE},
      {1, "A/A/A",            "normal",       2, "B/A/A", FALSE, "AAA_1", TRUE, "key"},
      {1, "A/A/A/A",          "normal",       2, "B/A/A/A", FALSE, NULL, TRUE, "key"},
      {1, "AAA_1",            "normal",       2, "A/A/A", MOVED_HERE, "key"},
      {1, "AAA_1/A",          "normal",       2, "A/A/A/A", MOVED_HERE, "key"},
      {1, "AAA_2",            "normal",       2, "B/A/A", MOVED_HERE, "key"},
      {1, "AAA_2/A",          "normal",       2, "B/A/A/A", MOVED_HERE, "key"},
      {1, "AAA_3",            "normal",       2, "C/A/A", MOVED_HERE, "key"},
      {1, "AAA_3/A",          "normal",       2, "C/A/A/A", MOVED_HERE, "key"},
      {1, "B",                "base-deleted", NO_COPY_FROM, "A"},
      {1, "B/A",              "base-deleted", NO_COPY_FROM},
      {1, "B/A/A",            "base-deleted", NO_COPY_FROM},
      {1, "B/A/A/A",          "base-deleted", NO_COPY_FROM},

      {2, "A/A",              "normal",       2, "C/A", MOVED_HERE},
      {2, "A/A/A",            "normal",       2, "C/A/A", FALSE, "AAA_2", TRUE, "key"},
      {2, "A/A/A/A",          "normal",       2, "C/A/A/A", FALSE, NULL, TRUE,  "key"},
      {2, "C/A",              "base-deleted", NO_COPY_FROM, "A/A"},
      {2, "C/A/A",            "base-deleted", NO_COPY_FROM},
      {2, "C/A/A/A",          "base-deleted", NO_COPY_FROM},

      {3, "A/A/A",            "normal",       2, "D/A/A", FALSE, "AAA_3", TRUE, "key"},
      {3, "A/A/A/A",          "normal",       2, "D/A/A/A", FALSE, NULL, TRUE,  "key"},
      {3, "D/A/A",            "base-deleted", NO_COPY_FROM, "A/A/A"},
      {3, "D/A/A/A",          "base-deleted", NO_COPY_FROM},

      { 0 },
    };

    SVN_ERR(check_db_rows(&b, "", nodes));
    SVN_ERR(check_db_conflicts(&b, "", NULL));
  }

  /* Go back to start position */
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));

  {
    nodes_row_t nodes[] = {
      {0, "",                 "normal",       1, ""},
      {0, "A",                "normal",       1, "A"},
      {0, "A/A",              "normal",       1, "A/A"},
      {0, "A/A/A",            "normal",       1, "A/A/A", NOT_MOVED},
      {0, "A/A/A/A",          "normal",       1, "A/A/A/A", NOT_MOVED},
      {0, "B",                "normal",       1, "B"},
      {0, "B/A",              "normal",       1, "B/A"},
      {0, "B/A/A",            "normal",       1, "B/A/A", NOT_MOVED},
      {0, "B/A/A/A",          "normal",       1, "B/A/A/A", NOT_MOVED},
      {0, "C",                "normal",       1, "C"},
      {0, "C/A",              "normal",       1, "C/A"},
      {0, "C/A/A",            "normal",       1, "C/A/A", NOT_MOVED},
      {0, "C/A/A/A",          "normal",       1, "C/A/A/A", NOT_MOVED},
      {0, "D",                "normal",       1, "D"},
      {0, "D/A",              "normal",       1, "D/A"},
      {0, "D/A/A",            "normal",       1, "D/A/A", NOT_MOVED},
      {0, "D/A/A/A",          "normal",       1, "D/A/A/A", NOT_MOVED},

      {1, "A",                "normal",       1, "B", MOVED_HERE},
      {1, "A/A",              "normal",       1, "B/A", MOVED_HERE},
      {1, "A/A/A",            "normal",       1, "B/A/A", FALSE, "AAA_1", TRUE},
      {1, "A/A/A/A",          "normal",       1, "B/A/A/A", FALSE, NULL, TRUE},
      {1, "AAA_1",            "normal",       1, "A/A/A", MOVED_HERE},
      {1, "AAA_1/A",          "normal",       1, "A/A/A/A", MOVED_HERE},
      {1, "AAA_2",            "normal",       1, "B/A/A", MOVED_HERE},
      {1, "AAA_2/A",          "normal",       1, "B/A/A/A", MOVED_HERE},
      {1, "AAA_3",            "normal",       1, "C/A/A", MOVED_HERE},
      {1, "AAA_3/A",          "normal",       1, "C/A/A/A", MOVED_HERE},
      {1, "B",                "base-deleted", NO_COPY_FROM, "A"},
      {1, "B/A",              "base-deleted", NO_COPY_FROM},
      {1, "B/A/A",            "base-deleted", NO_COPY_FROM},
      {1, "B/A/A/A",          "base-deleted", NO_COPY_FROM},

      {2, "A/A",              "normal",       1, "C/A", MOVED_HERE},
      {2, "A/A/A",            "normal",       1, "C/A/A", FALSE, "AAA_2", TRUE},
      {2, "A/A/A/A",          "normal",       1, "C/A/A/A", FALSE, NULL, TRUE},
      {2, "C/A",              "base-deleted", NO_COPY_FROM, "A/A"},
      {2, "C/A/A",            "base-deleted", NO_COPY_FROM},
      {2, "C/A/A/A",          "base-deleted", NO_COPY_FROM},

      {3, "A/A/A",            "normal",       1, "D/A/A", FALSE, "AAA_3", TRUE},
      {3, "A/A/A/A",          "normal",       1, "D/A/A/A", FALSE, NULL, TRUE},
      {3, "D/A/A",            "base-deleted", NO_COPY_FROM, "A/A/A"},
      {3, "D/A/A/A",          "base-deleted", NO_COPY_FROM},

      { 0 },
    };

    SVN_ERR(check_db_rows(&b, "", nodes));
    SVN_ERR(check_db_conflicts(&b, "", NULL));
  }

  /* Update and resolve via their strategy */
  SVN_ERR(sbox_wc_update(&b, "", 2));
  {
    conflict_info_t conflicts[] = {
      {"A",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_replaced}},
      {"B",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "B"}},
      {"C/A",   FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "C/A"}},
      {"D/A/A", FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "D/A/A"}},
      {0},
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  /* This breaks the move A/A/A -> AAA_1 */
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty, svn_wc_conflict_choose_merged));
  /* This breaks the move B -> A */
  SVN_ERR(sbox_wc_resolve(&b, "B", svn_depth_empty, svn_wc_conflict_choose_merged));
  /* This breaks the move C/A/A -> A/A */
  SVN_ERR(sbox_wc_resolve(&b, "C/A", svn_depth_empty, svn_wc_conflict_choose_merged));
  /* This breaks the move from D/A/A -> A/A/A */
  SVN_ERR(sbox_wc_resolve(&b, "D/A/A", svn_depth_empty, svn_wc_conflict_choose_merged));

  {
    nodes_row_t nodes[] = {
      {0, "",          "normal",       2, ""},
      {0, "A",         "normal",       2, "A"},
      {0, "A/A",       "normal",       2, "A/A"},
      {0, "A/A/A",     "normal",       2, "A/A/A", NOT_MOVED, "key"},
      {0, "A/A/A/A",   "normal",       2, "A/A/A/A", NOT_MOVED, "key"},
      {0, "B",         "normal",       2, "B"},
      {0, "B/A",       "normal",       2, "B/A"},
      {0, "B/A/A",     "normal",       2, "B/A/A", NOT_MOVED, "key"},
      {0, "B/A/A/A",   "normal",       2, "B/A/A/A", NOT_MOVED, "key"},
      {0, "C",         "normal",       2, "C"},
      {0, "C/A",       "normal",       2, "C/A"},
      {0, "C/A/A",     "normal",       2, "C/A/A", NOT_MOVED, "key"},
      {0, "C/A/A/A",   "normal",       2, "C/A/A/A", NOT_MOVED, "key"},
      {0, "D",         "normal",       2, "D"},
      {0, "D/A",       "normal",       2, "D/A"},
      {0, "D/A/A",     "normal",       2, "D/A/A", NOT_MOVED, "key"},
      {0, "D/A/A/A",   "normal",       2, "D/A/A/A", NOT_MOVED, "key"},
      {1, "A",         "normal",       1, "B"},
      {1, "A/A",       "normal",       1, "B/A"},
      {1, "A/A/A",     "normal",       1, "B/A/A", FALSE},
      {1, "A/A/A/A",   "normal",       1, "B/A/A/A"},
      {1, "AAA_1",     "normal",       1, "A/A/A"},
      {1, "AAA_1/A",   "normal",       1, "A/A/A/A"},
      {1, "AAA_2",     "normal",       1, "B/A/A", MOVED_HERE},
      {1, "AAA_2/A",   "normal",       1, "B/A/A/A", MOVED_HERE},
      {1, "AAA_3",     "normal",       1, "C/A/A", MOVED_HERE},
      {1, "AAA_3/A",   "normal",       1, "C/A/A/A", MOVED_HERE},
      {1, "B",         "base-deleted", NO_COPY_FROM},
      {1, "B/A",       "base-deleted", NO_COPY_FROM},
      {1, "B/A/A",     "base-deleted", NO_COPY_FROM},
      {1, "B/A/A/A",   "base-deleted", NO_COPY_FROM},
      {2, "A/A",       "normal",       1, "C/A"},
      {2, "A/A/A",     "normal",       1, "C/A/A", FALSE, "AAA_2"},
      {2, "A/A/A/A",   "normal",       1, "C/A/A/A"},
      {2, "C/A",       "base-deleted", NO_COPY_FROM},
      {2, "C/A/A",     "base-deleted", NO_COPY_FROM},
      {2, "C/A/A/A",   "base-deleted", NO_COPY_FROM},
      {3, "A/A/A",     "normal",       1, "D/A/A", FALSE, "AAA_3"},
      {3, "A/A/A/A",   "normal",       1, "D/A/A/A"},
      {3, "D/A/A",     "base-deleted", NO_COPY_FROM},
      {3, "D/A/A/A",   "base-deleted", NO_COPY_FROM},

      { 0 },
    };

    SVN_ERR(check_db_rows(&b, "", nodes));
  }


  return SVN_NO_ERROR;
}

static svn_error_t *
del4_update_delete_AAA(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(init_move4(&b, "del4_update_delete_AAA", opts, FALSE, pool));

  /* Update and resolve via mine strategy */
  SVN_ERR(sbox_wc_update(&b, "", 2));
  {
    conflict_info_t conflicts[] = {
      {"A",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_replaced}},
      {"B",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "B"}},
      {"C/A",   FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "C/A"}},
      {"D/A/A", FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "D/A/A"}},
      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
  /* Go back to start position */
  SVN_ERR(sbox_wc_update(&b, "", 1));
  {
    conflict_info_t conflicts[] = {
      {"A",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_replaced}},
      {"B",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "B"}},
      {"C/A",   FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "C/A"}},
      {"D/A/A", FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "D/A/A"}},
      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
  /* Update and resolve via their strategy */
  SVN_ERR(sbox_wc_update(&b, "", 2));
  {
    conflict_info_t conflicts[] = {
      {"A", FALSE, FALSE, {svn_wc_conflict_action_edit, svn_wc_conflict_reason_replaced}},
      {"B", FALSE, FALSE, {svn_wc_conflict_action_edit, svn_wc_conflict_reason_moved_away, "B"}},
      {"C/A", FALSE, FALSE, {svn_wc_conflict_action_edit, svn_wc_conflict_reason_moved_away, "C/A"}},
      {"D/A/A", FALSE, FALSE, {svn_wc_conflict_action_edit, svn_wc_conflict_reason_moved_away, "D/A/A"}},
      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_merged));

  return SVN_NO_ERROR;
}

static svn_error_t *
del4_update_add_AAA(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(init_move4(&b, "del4_update_add_AAA", opts, FALSE, pool));

  /* Update and resolve via mine strategy */
  SVN_ERR(sbox_wc_update(&b, "", 2));
  {
    conflict_info_t conflicts[] = {
      {"A", FALSE, FALSE, {svn_wc_conflict_action_edit, svn_wc_conflict_reason_replaced}},
      {"B", FALSE, FALSE, {svn_wc_conflict_action_edit, svn_wc_conflict_reason_moved_away, "B"}},
      {"C/A", FALSE, FALSE, {svn_wc_conflict_action_edit, svn_wc_conflict_reason_moved_away, "C/A"}},
      {"D/A/A", FALSE, FALSE, {svn_wc_conflict_action_edit, svn_wc_conflict_reason_moved_away, "D/A/A"}},
      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
  /* Go back to start position */
  SVN_ERR(sbox_wc_update(&b, "", 1));
  {
    conflict_info_t conflicts[] = {
      {"A",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_replaced}},
      {"B",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "B"}},
      {"C/A",   FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "C/A"}},
      {"D/A/A", FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "D/A/A"}},
      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
  /* Update and resolve via their strategy */
  SVN_ERR(sbox_wc_update(&b, "", 2));
  {
    conflict_info_t conflicts[] = {
      {"A",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_replaced}},
      {"B",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "B"}},
      {"C/A",   FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "C/A"}},
      {"D/A/A", FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "D/A/A"}},
      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_merged));

  return SVN_NO_ERROR;
}

static svn_error_t *
del4_update_replace_AAA(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(init_move4(&b, "del4_update_replace_AAA", opts, FALSE, pool));

  /* Update and resolve via mine strategy */
  SVN_ERR(sbox_wc_update(&b, "", 2));
  {
    conflict_info_t conflicts[] = {
      {"A",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_replaced}},
      {"B",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "B"}},
      {"C/A",   FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "C/A"}},
      {"D/A/A", FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "D/A/A"}},
      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
  /* Go back to start position */
  SVN_ERR(sbox_wc_update(&b, "", 1));
  {
    conflict_info_t conflicts[] = {
      {"A",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_replaced}},
      {"B",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "B"}},
      {"C/A",   FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "C/A"}},
      {"D/A/A", FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "D/A/A"}},
      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
  /* Update and resolve via their strategy */
  SVN_ERR(sbox_wc_update(&b, "", 2));
  {
    conflict_info_t conflicts[] = {
      {"A",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_replaced}},
      {"B",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "B"}},
      {"C/A",   FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "C/A"}},
      {"D/A/A", FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "D/A/A"}},
      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_merged));

  return SVN_NO_ERROR;
}

static svn_error_t *
del4_update_delself_AAA(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(init_move4(&b, "del4_update_delself_AAA", opts, FALSE, pool));

  /* Update and resolve via mine strategy */
  SVN_ERR(sbox_wc_update(&b, "", 2));

  {
    conflict_info_t conflicts[] = {
      {"A",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_replaced}},
      {"B",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "B"}},
      {"C/A",   FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "C/A"}},
      {"D/A/A", FALSE, FALSE, {svn_wc_conflict_action_delete,
                               svn_wc_conflict_reason_moved_away, "D/A/A"}},
      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }


  /* Resolve a few conflicts manually */
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  SVN_ERR(sbox_wc_resolve(&b, "B", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  SVN_ERR(sbox_wc_resolve(&b, "C/A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));

  {
    nodes_row_t nodes[] = {
      {0, "",           "normal",       2, ""},
      {0, "A",          "normal",       2, "A"},
      {0, "A/A",        "normal",       2, "A/A"},
      {0, "B",          "normal",       2, "B"},
      {0, "B/A",        "normal",       2, "B/A"},
      {0, "C",          "normal",       2, "C"},
      {0, "C/A",        "normal",       2, "C/A"},
      {0, "D",          "normal",       2, "D"},
      {0, "D/A",        "normal",       2, "D/A"},
      {1, "A",          "normal",       2, "B", MOVED_HERE},
      {1, "A/A",        "normal",       2, "B/A", MOVED_HERE},
      {1, "AAA_1",      "normal",       1, "A/A/A"},
      {1, "AAA_1/A",    "normal",       1, "A/A/A/A"},
      {1, "AAA_2",      "normal",       1, "B/A/A"},
      {1, "AAA_2/A",    "normal",       1, "B/A/A/A"},
      {1, "AAA_3",      "normal",       1, "C/A/A"},
      {1, "AAA_3/A",    "normal",       1, "C/A/A/A"},
      {1, "B",          "base-deleted", NO_COPY_FROM, "A"},
      {1, "B/A",        "base-deleted", NO_COPY_FROM},
      {2, "A/A",        "normal",       2, "C/A", MOVED_HERE},
      {2, "C/A",        "base-deleted", NO_COPY_FROM, "A/A"},
      {3, "A/A/A",      "normal",       1, "D/A/A"},
      {3, "A/A/A/A",    "normal",       1, "D/A/A/A"},

      { 0 },
    };

    SVN_ERR(check_db_rows(&b, "", nodes));
  }
  {
    conflict_info_t conflicts[] = {
      /* Not resolved yet */
      {"D/A/A", FALSE, FALSE, {svn_wc_conflict_action_delete,
                               svn_wc_conflict_reason_moved_away, "D/A/A"}},

      /* New */
      {"A/A",   FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_deleted}},
      {"A/A/A", FALSE, FALSE, {svn_wc_conflict_action_delete,
                               svn_wc_conflict_reason_moved_away, "A/A/A"}},

      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }

  /* These can only be resolved to merged, as the merge is already broken
     (because the move source is gone): incoming delete on moved_away */
  SVN_ERR(sbox_wc_resolve(&b, "D/A/A", svn_depth_empty,
                          svn_wc_conflict_choose_merged));
  SVN_ERR(sbox_wc_resolve(&b, "A/A/A", svn_depth_empty,
                          svn_wc_conflict_choose_merged));

  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
  /* Go back to start position */
  SVN_ERR(sbox_wc_update(&b, "", 1));
  {
    conflict_info_t conflicts[] = {
      {"A", FALSE, FALSE,   { svn_wc_conflict_action_edit,
                              svn_wc_conflict_reason_replaced}},
      {"B", FALSE, FALSE,   { svn_wc_conflict_action_edit,
                              svn_wc_conflict_reason_moved_away, "B"}},
      {"C/A", FALSE, FALSE, { svn_wc_conflict_action_edit,
                              svn_wc_conflict_reason_moved_away, "C/A"}},
      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  SVN_ERR(sbox_wc_resolve(&b, "B", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  SVN_ERR(sbox_wc_resolve(&b, "C/A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    conflict_info_t conflicts[] = {
      {"A/A",  FALSE, FALSE,  { svn_wc_conflict_action_edit,
                                svn_wc_conflict_reason_deleted}},
      {"A/A/A", FALSE, FALSE, { svn_wc_conflict_action_add,
                                svn_wc_conflict_reason_added}},
      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  SVN_ERR(sbox_wc_resolve(&b, "A/A/A", svn_depth_empty,
                          svn_wc_conflict_choose_merged));
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity,
                          svn_wc_conflict_choose_mine_conflict));
  /* Update and resolve via their strategy */
  SVN_ERR(sbox_wc_update(&b, "", 2));
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_merged));

  return SVN_NO_ERROR;
}

static svn_error_t *
del4_update_replaceself_AAA(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(init_move4(&b, "del4_update_replaceself_AAA", opts, FALSE, pool));

  /* Update and resolve via mine strategy */
  SVN_ERR(sbox_wc_update(&b, "", 2));
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
  /* Go back to start position */
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
  /* Update and resolve via their strategy */
  SVN_ERR(sbox_wc_update(&b, "", 2));
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_merged));

  return SVN_NO_ERROR;
}


static svn_error_t *
move4_update_edit_AAA(const svn_test_opts_t *opts, apr_pool_t *pool)
{
    svn_test__sandbox_t b;

    SVN_ERR(init_move4(&b, "move4_update_edit_AAA", opts, TRUE, pool));

    {
      nodes_row_t nodes[] = {

        { 0, "A/A/A",     "normal",    1, "A/A/A" },
        { 1, "A/A/A",     "normal",    1, "B/A/A", FALSE, NULL /*"AAA_1"*/, TRUE },
        { 2, "A/A/A",     "normal",    1, "C/A/A", FALSE, NULL /*"AAA_2"*/, TRUE },
        { 3, "A/A/A",     "normal",    1, "D/A/A", FALSE, "AAA_3", TRUE },

        { 0, "A/A/A/A",   "normal",    1, "A/A/A/A" },
        { 1, "A/A/A/A",   "normal",    1, "B/A/A/A", FALSE, NULL, TRUE },
        { 2, "A/A/A/A",   "normal",    1, "C/A/A/A", FALSE, NULL, TRUE },
        { 3, "A/A/A/A",   "normal",    1, "D/A/A/A", FALSE, NULL, TRUE },


        { 0 },
      };

      SVN_ERR(check_db_rows(&b, "A/A/A", nodes));
    }

    /* Update and resolve via mine strategy */
    SVN_ERR(sbox_wc_update(&b, "", 2));
    SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));

    {
      nodes_row_t nodes[] = {

        { 0, "A/A/A",     "normal",  2, "A/A/A", NOT_MOVED,            "key" },
        { 1, "A/A/A",     "normal",  2, "B/A/A", FALSE, NULL /*"AAA_1"*/, TRUE, "key" },
        { 2, "A/A/A",     "normal",  2, "C/A/A", FALSE, NULL /*"AAA_2"*/, TRUE, "key" },
        { 3, "A/A/A",     "normal",  2, "D/A/A", FALSE, "AAA_3", TRUE, "key" },

        { 0, "A/A/A/A",   "normal",  2, "A/A/A/A", NOT_MOVED,          "key" },
        { 1, "A/A/A/A",   "normal",  2, "B/A/A/A", FALSE, NULL,  TRUE, "key" },
        { 2, "A/A/A/A",   "normal",  2, "C/A/A/A", FALSE, NULL,  TRUE, "key" },
        { 3, "A/A/A/A",   "normal",  2, "D/A/A/A", FALSE, NULL,  TRUE, "key" },

        { 0 },
      };
        SVN_ERR(check_db_rows(&b, "A/A/A", nodes));
    }

    /* Go back to start position */
    SVN_ERR(sbox_wc_update(&b, "", 1));
    SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
    /* Update and resolve via their strategy */
    SVN_ERR(sbox_wc_update(&b, "", 2));
    SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_merged));

    {
      nodes_row_t nodes[] = {

        { 0, "A/A/A",     "normal",       2, "A/A/A", NOT_MOVED, "key" },
        { 1, "A/A/A",     "normal",       1, "B/A/A" },
        { 2, "A/A/A",     "normal",       1, "C/A/A" },
        { 3, "A/A/A",     "normal",       1, "D/A/A", FALSE, "AAA_3"},

        { 0, "A/A/A/A",   "normal",       2, "A/A/A/A", NOT_MOVED, "key" },
        { 1, "A/A/A/A",   "normal",       1, "B/A/A/A" },
        { 2, "A/A/A/A",   "normal",       1, "C/A/A/A" },
        { 3, "A/A/A/A",   "normal",       1, "D/A/A/A" },

        { 0 },
      };

      SVN_ERR(check_db_rows(&b, "A/A/A", nodes));
    }

    return SVN_NO_ERROR;
}

static svn_error_t *
move4_update_delete_AAA(const svn_test_opts_t *opts, apr_pool_t *pool)
{
    svn_test__sandbox_t b;

    SVN_ERR(init_move4(&b, "move4_update_delete_AAA", opts, TRUE, pool));

    /* Update and resolve via mine strategy */
    SVN_ERR(sbox_wc_update(&b, "", 2));
    SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
    /* Go back to start position */
    SVN_ERR(sbox_wc_update(&b, "", 1));
    SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
    /* Update and resolve via their strategy */
    SVN_ERR(sbox_wc_update(&b, "", 2));
    SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_merged));

    return SVN_NO_ERROR;
}

static svn_error_t *
move4_update_add_AAA(const svn_test_opts_t *opts, apr_pool_t *pool)
{
    svn_test__sandbox_t b;

    SVN_ERR(init_move4(&b, "move4_update_add_AAA", opts, TRUE, pool));

    /* Update and resolve via mine strategy */
    SVN_ERR(sbox_wc_update(&b, "", 2));
    SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
    /* Go back to start position */
    SVN_ERR(sbox_wc_update(&b, "", 1));
    SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
    /* Update and resolve via their strategy */
    SVN_ERR(sbox_wc_update(&b, "", 2));
    SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_merged));

    return SVN_NO_ERROR;
}

static svn_error_t *
move4_update_replace_AAA(const svn_test_opts_t *opts, apr_pool_t *pool)
{
    svn_test__sandbox_t b;

    SVN_ERR(init_move4(&b, "move4_update_replace_AAA", opts, TRUE, pool));

    /* Update and resolve via mine strategy */
    SVN_ERR(sbox_wc_update(&b, "", 2));
    SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
    /* Go back to start position */
    SVN_ERR(sbox_wc_update(&b, "", 1));
    SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
    /* Update and resolve via their strategy */
    SVN_ERR(sbox_wc_update(&b, "", 2));
    SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_merged));

    return SVN_NO_ERROR;
}

static svn_error_t *
move4_update_delself_AAA(const svn_test_opts_t *opts, apr_pool_t *pool)
{
    svn_test__sandbox_t b;

    SVN_ERR(init_move4(&b, "move4_update_delself_AAA", opts, TRUE, pool));

    /* Update and resolve via mine strategy */
    SVN_ERR(sbox_wc_update(&b, "", 2));

    {
      nodes_row_t nodes[] = {

        {1, "A_moved",        "normal",       1, "A", MOVED_HERE},
        {1, "A_moved/A",      "normal",       1, "A/A", MOVED_HERE},
        {1, "A_moved/A/A",    "normal",       1, "A/A/A", MOVED_HERE},
        {3, "A_moved/A/A",    "base-deleted", NO_COPY_FROM, "AAA_1"},
        {1, "A_moved/A/A/A",  "normal",       1, "A/A/A/A", MOVED_HERE},
        {3, "A_moved/A/A/A",  "base-deleted", NO_COPY_FROM},

        { 0 },
      };
        SVN_ERR(check_db_rows(&b, "A_moved", nodes));
    }

    /* Resolve a few conflicts manually */
    SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
        svn_wc_conflict_choose_mine_conflict));

    {
      nodes_row_t nodes[] = {
        {0, "",               "normal",       2, ""},
        {0, "A",              "normal",       2, "A"},
        {0, "A/A",            "normal",       2, "A/A"},
        {0, "B",              "normal",       2, "B"},
        {0, "B/A",            "normal",       2, "B/A"},
        {0, "C",              "normal",       2, "C"},
        {0, "C/A",            "normal",       2, "C/A"},
        {0, "D",              "normal",       2, "D"},
        {0, "D/A",            "normal",       2, "D/A"},
        {1, "A",              "normal",       1, "B", FALSE, "A_moved", TRUE},
        {1, "A/A",            "normal",       1, "B/A", MOVED_HERE},
        {1, "A/A/A",          "normal",       1, "B/A/A", MOVED_HERE},
        {1, "A/A/A/A",        "normal",       1, "B/A/A/A", MOVED_HERE},
        {1, "AAA_1",          "normal",       1, "A/A/A"},
        {1, "AAA_1/A",        "normal",       1, "A/A/A/A"},
        {1, "AAA_2",          "normal",       1, "B/A/A", MOVED_HERE},
        {1, "AAA_2/A",        "normal",       1, "B/A/A/A", MOVED_HERE},
        {1, "AAA_3",          "normal",       1, "C/A/A", MOVED_HERE},
        {1, "AAA_3/A",        "normal",       1, "C/A/A/A", MOVED_HERE},
        {1, "A_moved",        "normal",       2, "A", MOVED_HERE},
        {1, "A_moved/A",      "normal",       2, "A/A", MOVED_HERE},
        {1, "B",              "base-deleted", NO_COPY_FROM, "A"},
        {1, "B/A",            "base-deleted", NO_COPY_FROM},
        {1, "BA_moved",       "normal",       1, "B/A", MOVED_HERE},
        {1, "BA_moved/A",     "normal",       1, "B/A/A", MOVED_HERE},
        {1, "BA_moved/A/A",   "normal",       1, "B/A/A/A", MOVED_HERE},
        {2, "A/A",            "normal",       1, "C/A", FALSE, "BA_moved", TRUE},
        {2, "A/A/A",          "normal",       1, "C/A/A", MOVED_HERE},
        {2, "A/A/A/A",        "normal",       1, "C/A/A/A", MOVED_HERE},
        {2, "BA_moved/A",     "base-deleted", NO_COPY_FROM, "AAA_2"},
        {2, "BA_moved/A/A",   "base-deleted", NO_COPY_FROM},
        {2, "C/A",            "base-deleted", NO_COPY_FROM, "A/A"},
        {3, "A/A/A",          "normal",       1, "D/A/A", FALSE, "AAA_3"},
        {3, "A/A/A/A",        "normal",       1, "D/A/A/A"},

        { 0 },
      };
      conflict_info_t conflicts[] = {
        {"A_moved/A/A", FALSE, FALSE, {svn_wc_conflict_action_delete,
                                       svn_wc_conflict_reason_moved_away, "A_moved/A/A"}},
        {"B",           FALSE, FALSE, {svn_wc_conflict_action_edit,
                                       svn_wc_conflict_reason_moved_away, "B"}},
        {"C/A",         FALSE, FALSE, {svn_wc_conflict_action_edit,
                                       svn_wc_conflict_reason_moved_away, "C/A"}},
        {"D/A/A",       FALSE, FALSE, {svn_wc_conflict_action_delete,
                                       svn_wc_conflict_reason_moved_away, "D/A/A"}},

        { 0 },
      };

      SVN_ERR(check_db_rows(&b, "", nodes));
      SVN_ERR(check_db_conflicts(&b, "", conflicts));
    }

    SVN_ERR(sbox_wc_resolve(&b, "B", svn_depth_empty,
        svn_wc_conflict_choose_mine_conflict));
    SVN_ERR(sbox_wc_resolve(&b, "C/A", svn_depth_empty,
        svn_wc_conflict_choose_mine_conflict));


    /* ### These can currently only be resolved to merged ???? */
    SVN_ERR(sbox_wc_resolve(&b, "D/A/A", svn_depth_empty,
                            svn_wc_conflict_choose_merged));
    SVN_ERR(sbox_wc_resolve(&b, "A/A/A", svn_depth_empty,
                            svn_wc_conflict_choose_merged));
    SVN_ERR(sbox_wc_resolve(&b, "A_moved/A/A", svn_depth_empty,
                            svn_wc_conflict_choose_merged));
    SVN_ERR(sbox_wc_resolve(&b, "A/A", svn_depth_empty,
                            svn_wc_conflict_choose_mine_conflict));
    SVN_ERR(sbox_wc_resolve(&b, "BA_moved/A", svn_depth_empty,
                            svn_wc_conflict_choose_merged));
    SVN_ERR(check_db_conflicts(&b, "", NULL));
    /* Go back to start position */
    SVN_ERR(sbox_wc_update(&b, "", 1));
    {
      nodes_row_t nodes[] = {
        {0, "",               "normal",       1, ""},
        {0, "A",              "normal",       1, "A"},
        {0, "A/A",            "normal",       1, "A/A"},
        {0, "A/A/A",          "normal",       1, "A/A/A"},
        {0, "A/A/A/A",        "normal",       1, "A/A/A/A"},
        {0, "B",              "normal",       1, "B"},
        {0, "B/A",            "normal",       1, "B/A"},
        {0, "B/A/A",          "normal",       1, "B/A/A"},
        {0, "B/A/A/A",        "normal",       1, "B/A/A/A"},
        {0, "C",              "normal",       1, "C"},
        {0, "C/A",            "normal",       1, "C/A"},
        {0, "C/A/A",          "normal",       1, "C/A/A"},
        {0, "C/A/A/A",        "normal",       1, "C/A/A/A"},
        {0, "D",              "normal",       1, "D"},
        {0, "D/A",            "normal",       1, "D/A"},
        {0, "D/A/A",          "normal",       1, "D/A/A"},
        {0, "D/A/A/A",        "normal",       1, "D/A/A/A"},
        {1, "A",              "normal",       2, "B", FALSE, "A_moved", TRUE},
        {1, "A/A",            "normal",       2, "B/A", MOVED_HERE},
        {1, "A/A/A",          "base-deleted", NO_COPY_FROM},
        {1, "A/A/A/A",        "base-deleted", NO_COPY_FROM},
        {1, "A_moved",        "normal",       2, "A", MOVED_HERE},
        {1, "A_moved/A",      "normal",       2, "A/A", MOVED_HERE},
        {1, "AAA_1",          "normal",       1, "A/A/A"},
        {1, "AAA_1/A",        "normal",       1, "A/A/A/A"},
        {1, "AAA_2",          "normal",       1, "B/A/A"},
        {1, "AAA_2/A",        "normal",       1, "B/A/A/A"},
        {1, "AAA_3",          "normal",       1, "C/A/A"},
        {1, "AAA_3/A",        "normal",       1, "C/A/A/A"},
        {1, "B",              "base-deleted", NO_COPY_FROM, "A"},
        {1, "B/A",            "base-deleted", NO_COPY_FROM},
        {1, "B/A/A",          "base-deleted", NO_COPY_FROM},
        {1, "B/A/A/A",        "base-deleted", NO_COPY_FROM},
        {1, "BA_moved",       "normal",       2, "B/A", MOVED_HERE},
        {2, "A/A",            "normal",       2, "C/A", FALSE, "BA_moved", TRUE},
        {2, "C/A",            "base-deleted", NO_COPY_FROM, "A/A"},
        {2, "C/A/A",          "base-deleted", NO_COPY_FROM},
        {2, "C/A/A/A",        "base-deleted", NO_COPY_FROM},
        {3, "A/A/A",          "normal",       1, "D/A/A"},
        {3, "A/A/A/A",        "normal",       1, "D/A/A/A"},

        { 0 },
      };

      conflict_info_t conflicts[] = {
        {"A", FALSE, FALSE,   { svn_wc_conflict_action_edit,
                                svn_wc_conflict_reason_replaced}},
        {"B", FALSE, FALSE,   { svn_wc_conflict_action_edit,
                                svn_wc_conflict_reason_moved_away, "B"}},
        {"C/A", FALSE, FALSE, { svn_wc_conflict_action_edit,
                                svn_wc_conflict_reason_moved_away, "C/A"}},
        {0}
      };

      SVN_ERR(check_db_rows(&b, "", nodes));
      SVN_ERR(check_db_conflicts(&b, "", conflicts));
    }

  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  SVN_ERR(sbox_wc_resolve(&b, "B", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  SVN_ERR(sbox_wc_resolve(&b, "C/A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    conflict_info_t conflicts[] = {
      {"A/A",  FALSE, FALSE,  { svn_wc_conflict_action_edit,
                                svn_wc_conflict_reason_moved_away, "A/A"}},
      {"A/A/A", FALSE, FALSE, { svn_wc_conflict_action_add,
                                svn_wc_conflict_reason_added}},
      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  SVN_ERR(sbox_wc_resolve(&b, "A/A/A", svn_depth_empty,
                          svn_wc_conflict_choose_merged));
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
  /* Update and resolve via their strategy */
  SVN_ERR(sbox_wc_update(&b, "", 2));
  SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_merged));

  return SVN_NO_ERROR;
}

static svn_error_t *
move4_update_replaceself_AAA(const svn_test_opts_t *opts, apr_pool_t *pool)
{
    svn_test__sandbox_t b;

    SVN_ERR(init_move4(&b, "move4_update_replaceself_AAA", opts, TRUE, pool));

    /* Update and resolve via mine strategy */
    SVN_ERR(sbox_wc_update(&b, "", 2));
    SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
    /* Go back to start position */
    SVN_ERR(sbox_wc_update(&b, "", 1));
    SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
    /* Update and resolve via their strategy */
    SVN_ERR(sbox_wc_update(&b, "", 2));
    SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_merged));

    return SVN_NO_ERROR;
}

static svn_error_t *
simple_move_bump(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "simple_move_bump", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));

  SVN_ERR(sbox_wc_propset(&b, "old_A", "val", "A"));
  SVN_ERR(sbox_wc_propset(&b, "old_B", "val", "A/B"));

  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_propset(&b, "new_A", "val", "A"));
  SVN_ERR(sbox_wc_propset(&b, "new_B", "val", "A/B"));

  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A/B", "A/B_mv"));
  SVN_ERR(sbox_wc_move(&b, "A", "A_mv"));

  {
    nodes_row_t nodes[] = {

      { 0, "",          "normal",       1, ""},
      { 0, "A",         "normal",       1, "A", NOT_MOVED, "old_A"},
      { 0, "A/B",       "normal",       1, "A/B", NOT_MOVED, "old_B"},

      { 1, "A",         "base-deleted", NO_COPY_FROM, "A_mv"},
      { 1, "A/B",       "base-deleted", NO_COPY_FROM},

      { 1, "A_mv",      "normal",       1, "A", MOVED_HERE, "old_A" },
      { 1, "A_mv/B",    "normal",       1, "A/B", MOVED_HERE, "old_B" },

      { 2, "A_mv/B",    "base-deleted", NO_COPY_FROM, "A_mv/B_mv" },
      { 2, "A_mv/B_mv", "normal",       1, "A/B", FALSE, NULL, TRUE, "old_B" },

      { 0 },
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_update(&b, "", 2));

  /* Expect the A tree to be updated */
  {
    nodes_row_t nodes[] = {

      { 0, "",          "normal",       2, ""},
      { 0, "A",         "normal",       2, "A", NOT_MOVED, "new_A,old_A"},
      { 0, "A/B",       "normal",       2, "A/B", NOT_MOVED, "new_B,old_B"},

      { 1, "A",         "base-deleted", NO_COPY_FROM, "A_mv"},
      { 1, "A/B",       "base-deleted", NO_COPY_FROM},

      { 1, "A_mv",      "normal",       1, "A", MOVED_HERE, "old_A" },
      { 1, "A_mv/B",    "normal",       1, "A/B", MOVED_HERE, "old_B" },

      { 2, "A_mv/B",    "base-deleted", NO_COPY_FROM, "A_mv/B_mv" },
      { 2, "A_mv/B_mv", "normal",       1, "A/B", FALSE, NULL, TRUE, "old_B" },

      { 0 },
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));

  {
    nodes_row_t nodes[] = {

      { 0, "",          "normal",       2, ""},
      { 0, "A",         "normal",       2, "A", NOT_MOVED, "new_A,old_A"},
      { 0, "A/B",       "normal",       2, "A/B", NOT_MOVED, "new_B,old_B"},

      { 1, "A",         "base-deleted", NO_COPY_FROM, "A_mv"},
      { 1, "A/B",       "base-deleted", NO_COPY_FROM},

      { 1, "A_mv",      "normal",       2, "A", MOVED_HERE, "new_A,old_A" },
      { 1, "A_mv/B",    "normal",       2, "A/B", MOVED_HERE, "new_B,old_B" },

      { 2, "A_mv/B",    "base-deleted", NO_COPY_FROM, "A_mv/B_mv" },
      { 2, "A_mv/B_mv", "normal",       1, "A/B", FALSE, NULL, TRUE, "old_B" },

      { 0 },
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_resolve(&b, "A_mv/B", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));

  {
    nodes_row_t nodes[] = {

      { 0, "",          "normal",       2, ""},
      { 0, "A",         "normal",       2, "A", NOT_MOVED, "new_A,old_A"},
      { 0, "A/B",       "normal",       2, "A/B", NOT_MOVED, "new_B,old_B"},

      { 1, "A",         "base-deleted", NO_COPY_FROM, "A_mv"},
      { 1, "A/B",       "base-deleted", NO_COPY_FROM},

      { 1, "A_mv",      "normal",       2, "A", MOVED_HERE, "new_A,old_A" },
      { 1, "A_mv/B",    "normal",       2, "A/B", MOVED_HERE, "new_B,old_B" },

      { 2, "A_mv/B",    "base-deleted", NO_COPY_FROM, "A_mv/B_mv" },
      { 2, "A_mv/B_mv", "normal",       2, "A/B", FALSE, NULL, TRUE, "new_B,old_B" },

      { 0 },
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
movedhere_extract_retract(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "movedhere_extract_retract",
                                   opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B1"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B2"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B3"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/C1"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/C2"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/C3"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/D1"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/D2"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/D3"));

  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_propset(&b, "k", "v", "A/B1"));
  SVN_ERR(sbox_wc_propset(&b, "k", "v", "A/B2"));
  SVN_ERR(sbox_wc_propset(&b, "k", "v", "A/B3"));
  SVN_ERR(sbox_wc_delete(&b, "A/C1"));
  SVN_ERR(sbox_wc_delete(&b, "A/C2"));
  SVN_ERR(sbox_wc_delete(&b, "A/C3"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/E1"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/E2"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/E3"));

  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "A", "Z"));

  SVN_ERR(sbox_wc_delete(&b, "Z/B1"));
  SVN_ERR(sbox_wc_delete(&b, "Z/C1"));
  SVN_ERR(sbox_wc_delete(&b, "Z/D1"));

  SVN_ERR(sbox_wc_move(&b, "Z/B2", "B2"));
  SVN_ERR(sbox_wc_move(&b, "Z/C2", "C2"));
  SVN_ERR(sbox_wc_move(&b, "Z/D2", "D2"));

  SVN_ERR(sbox_wc_mkdir(&b, "Z/B2"));
  SVN_ERR(sbox_wc_mkdir(&b, "Z/C2"));
  SVN_ERR(sbox_wc_mkdir(&b, "Z/D2"));
  SVN_ERR(sbox_wc_mkdir(&b, "Z/E2"));

  SVN_ERR(sbox_wc_update(&b, "", 2));
  {
    conflict_info_t conflicts[] = {
      {"A",    FALSE, FALSE, {svn_wc_conflict_action_edit,
                              svn_wc_conflict_reason_moved_away, "A"}},
      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  {
    conflict_info_t conflicts[] = {
      {"Z/B1", FALSE, FALSE, {svn_wc_conflict_action_edit,
                              svn_wc_conflict_reason_deleted}},
      {"Z/B2", FALSE, FALSE, {svn_wc_conflict_action_edit,
                              svn_wc_conflict_reason_moved_away, "Z/B2"}},
      {"Z/C1", FALSE, FALSE, {svn_wc_conflict_action_delete,
                              svn_wc_conflict_reason_deleted}},
      {"Z/C2", FALSE, FALSE, {svn_wc_conflict_action_delete,
                              svn_wc_conflict_reason_moved_away, "Z/C2"}},
      {"Z/E2", FALSE, FALSE, {svn_wc_conflict_action_add,
                              svn_wc_conflict_reason_added}},

      {0}
    };

    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }
  SVN_ERR(sbox_wc_resolve(&b, "Z/B1", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  SVN_ERR(sbox_wc_resolve(&b, "Z/B2", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));

  SVN_ERR(sbox_wc_resolve(&b, "Z/C1", svn_depth_empty,
                          svn_wc_conflict_choose_merged));
  SVN_ERR(sbox_wc_resolve(&b, "Z/C2", svn_depth_empty,
                          svn_wc_conflict_choose_merged));

  SVN_ERR(sbox_wc_resolve(&b, "Z/E2", svn_depth_empty,
                          svn_wc_conflict_choose_merged));

  SVN_ERR(check_db_conflicts(&b, "", NULL));
  {
    nodes_row_t nodes[] = {

      {0, "",       "normal",       2, ""},
      {0, "A",      "normal",       2, "A"},
      {0, "A/B1",   "normal",       2, "A/B1", FALSE, NULL, FALSE, "k"},
      {0, "A/B2",   "normal",       2, "A/B2", FALSE, NULL, FALSE, "k"},
      {0, "A/B3",   "normal",       2, "A/B3", FALSE, NULL, FALSE, "k"},
      {0, "A/D1",   "normal",       2, "A/D1"},
      {0, "A/D2",   "normal",       2, "A/D2"},
      {0, "A/D3",   "normal",       2, "A/D3"},
      {0, "A/E1",   "normal",       2, "A/E1"},
      {0, "A/E2",   "normal",       2, "A/E2"},
      {0, "A/E3",   "normal",       2, "A/E3"},

      {1, "A",      "base-deleted", NO_COPY_FROM, "Z"},
      {1, "A/B1",   "base-deleted", NO_COPY_FROM},
      {1, "A/B2",   "base-deleted", NO_COPY_FROM},
      {1, "A/B3",   "base-deleted", NO_COPY_FROM},

      {1, "A/D1",   "base-deleted", NO_COPY_FROM},
      {1, "A/D2",   "base-deleted", NO_COPY_FROM},
      {1, "A/D3",   "base-deleted", NO_COPY_FROM},

      {1, "A/E1",   "base-deleted", NO_COPY_FROM},
      {1, "A/E2",   "base-deleted", NO_COPY_FROM},
      {1, "A/E3",   "base-deleted", NO_COPY_FROM},

      {1, "B2",     "normal",       2, "A/B2", MOVED_HERE, "k"},
      {1, "C2",     "normal",       1, "A/C2"},
      {1, "D2",     "normal",       1, "A/D2", MOVED_HERE},

      {1, "Z",      "normal",       2, "A", MOVED_HERE},
      {1, "Z/B1",   "normal",       2, "A/B1", MOVED_HERE, "k"},
      {1, "Z/B2",   "normal",       2, "A/B2", MOVED_HERE, "k"},
      {1, "Z/B3",   "normal",       2, "A/B3", MOVED_HERE, "k"},
      {1, "Z/D1",   "normal",       2, "A/D1", MOVED_HERE},
      {1, "Z/D2",   "normal",       2, "A/D2", MOVED_HERE},
      {1, "Z/D3",   "normal",       2, "A/D3", MOVED_HERE},
      {1, "Z/E1",   "normal",       2, "A/E1", MOVED_HERE},
      {1, "Z/E2",   "normal",       2, "A/E2", MOVED_HERE},
      {1, "Z/E3",   "normal",       2, "A/E3", MOVED_HERE},

      {2, "Z/B2",   "normal",       NO_COPY_FROM, "B2"},
      {2, "Z/C2",   "normal",       NO_COPY_FROM},
      {2, "Z/D2",   "normal",       NO_COPY_FROM, "D2"},
      {2, "Z/E2",   "normal",       NO_COPY_FROM},

      {2, "Z/B1",   "base-deleted", NO_COPY_FROM},
      {2, "Z/D1",   "base-deleted", NO_COPY_FROM},

      { 0 },
    };
    SVN_ERR(check_db_rows(&b, "", nodes));

    SVN_ERR(check_db_conflicts(&b, "", NULL));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
repo_wc_copy(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;
  const char *new_repos_dir;
  const char *new_repos_url;

  SVN_ERR(svn_test__sandbox_create(&b, "repo_wc_copy",
                                   opts, pool));
  SVN_ERR(sbox_add_and_commit_greek_tree(&b));

  SVN_ERR(sbox_wc_copy_url(&b,
                           svn_path_url_add_component2(b.repos_url, "A/B",
                                                       pool),
                           -1, "AA"));

  {
    nodes_row_t nodes[] = {

      {1, "AA/lambda",   "normal", 1, "A/B/lambda"},
      {1, "AA",          "normal", 1, "A/B"},
      {1, "AA/E/beta",   "normal", 1, "A/B/E/beta"},
      {1, "AA/E/alpha",  "normal", 1, "A/B/E/alpha"},
      {1, "AA/F",        "normal", 1, "A/B/F"},
      {1, "AA/E",        "normal", 1, "A/B/E"},

      { 0 },
    };
    SVN_ERR(check_db_rows(&b, "AA", nodes));
  }

  new_repos_dir = apr_pstrcat(pool, b.repos_dir, "-2", SVN_VA_NULL);
  new_repos_url = apr_pstrcat(pool, b.repos_url, "-2", SVN_VA_NULL);

  svn_test_add_dir_cleanup(new_repos_dir);

  SVN_ERR(svn_io_remove_dir2(new_repos_dir, TRUE, NULL, NULL, pool));
  SVN_ERR(svn_io_copy_dir_recursively(b.repos_dir,
                                      svn_dirent_dirname(new_repos_dir, pool),
                                      svn_dirent_basename(new_repos_dir, pool),
                                      FALSE, NULL, NULL, pool));

  SVN_ERR(sbox_wc_relocate(&b, new_repos_url));

  /* This produced an invalid copy in Subversion <= 1.8.8.
     Status would show all descendants as incomplete */
  SVN_ERR(sbox_wc_copy_url(&b,
                           svn_path_url_add_component2(b.repos_url, "A/B",
                                                       pool),
                           -1, "BB"));

  {
    nodes_row_t nodes[] = {

      {1, "BB/lambda",   "normal", 1, "A/B/lambda"},
      {1, "BB",          "normal", 1, "A/B"},
      {1, "BB/E/beta",   "normal", 1, "A/B/E/beta"},
      {1, "BB/E/alpha",  "normal", 1, "A/B/E/alpha"},
      {1, "BB/F",        "normal", 1, "A/B/F"},
      {1, "BB/E",        "normal", 1, "A/B/E"},

      { 0 },
    };
    SVN_ERR(check_db_rows(&b, "BB", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
break_move_in_delete(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "break_move_in_delete", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "X"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/Y"));
  SVN_ERR(sbox_wc_mkdir(&b, "X/Y/Z"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_propset(&b, "key", "value", "X/Y/Z"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "X/Y/Z", "A/Z"));
  SVN_ERR(sbox_wc_delete(&b, "X"));
  {
    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"},
      {0, "X/Y/Z", "normal",       1, "X/Y/Z"},
      {1, "X",     "base-deleted", NO_COPY_FROM},
      {1, "X/Y",   "base-deleted", NO_COPY_FROM},
      {1, "X/Y/Z", "base-deleted", NO_COPY_FROM, "A/Z"},
      {2, "A/Z",   "normal",       1, "X/Y/Z", MOVED_HERE},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(sbox_wc_update(&b, "", 2));
  {
    nodes_row_t nodes[] = {
      {0, "",      "normal",       2, ""},
      {0, "A",     "normal",       2, "A"},
      {0, "A/B",   "normal",       2, "A/B"},
      {0, "X",     "normal",       2, "X"},
      {0, "X/Y",   "normal",       2, "X/Y"},
      {0, "X/Y/Z", "normal",       2, "X/Y/Z"},
      {1, "X",     "base-deleted", NO_COPY_FROM},
      {1, "X/Y",   "base-deleted", NO_COPY_FROM},
      {1, "X/Y/Z", "base-deleted", NO_COPY_FROM, "A/Z"},
      {2, "A/Z",   "normal",       1, "X/Y/Z", MOVED_HERE},
      {0}
    };
    conflict_info_t conflicts1[] = {
      {"X",     FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_deleted}},
      {0}
    };
    conflict_info_t conflicts2[] = {
      {"X/Y/Z", FALSE, FALSE, {svn_wc_conflict_action_edit,
                               svn_wc_conflict_reason_moved_away, "X"}},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
    SVN_ERR(check_db_conflicts(&b, "", conflicts1));
    SVN_ERR(sbox_wc_resolve(&b, "X", svn_depth_empty,
                            svn_wc_conflict_choose_mine_conflict));
    SVN_ERR(check_db_rows(&b, "", nodes));
    SVN_ERR(check_db_conflicts(&b, "", conflicts2));
  }

  SVN_ERR(sbox_wc_resolved(&b, "X/Y/Z"));
  {
    nodes_row_t nodes[] = {
      {0, "",      "normal",       2, ""},
      {0, "A",     "normal",       2, "A"},
      {0, "A/B",   "normal",       2, "A/B"},
      {0, "X",     "normal",       2, "X"},
      {0, "X/Y",   "normal",       2, "X/Y"},
      {0, "X/Y/Z", "normal",       2, "X/Y/Z"},
      {1, "X",     "base-deleted", NO_COPY_FROM},
      {1, "X/Y",   "base-deleted", NO_COPY_FROM},
      {1, "X/Y/Z", "base-deleted", NO_COPY_FROM},
      {2, "A/Z",   "normal",       1, "X/Y/Z"},
      {0}
    };
    SVN_ERR(check_db_rows(&b, "", nodes));
    SVN_ERR(check_db_conflicts(&b, "", NULL));
  }

  return SVN_NO_ERROR;
}


static svn_error_t *
nested_move_delete(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "nested_move_delete", opts, pool));

  SVN_ERR(sbox_add_and_commit_greek_tree(&b));

  SVN_ERR(sbox_wc_mkdir(&b, "A/Z"));
  SVN_ERR(sbox_wc_move(&b, "A/B/lambda", "A/Z/lambda"));

  SVN_ERR(sbox_wc_delete(&b, "A/B"));

  {
    nodes_row_t nodes_AB[] = {
      {0, "A/B",          "normal",       1, "A/B"},
      {2, "A/B",          "base-deleted", NO_COPY_FROM},
      {0, "A/B/E",        "normal",       1, "A/B/E"},
      {2, "A/B/E",        "base-deleted", NO_COPY_FROM},
      {0, "A/B/E/alpha",  "normal",       1, "A/B/E/alpha"},
      {2, "A/B/E/alpha",  "base-deleted", NO_COPY_FROM},
      {0, "A/B/E/beta",   "normal",       1, "A/B/E/beta"},
      {2, "A/B/E/beta",   "base-deleted", NO_COPY_FROM},
      {0, "A/B/F",        "normal",       1, "A/B/F"},
      {2, "A/B/F",        "base-deleted", NO_COPY_FROM},
      {0, "A/B/lambda",   "normal",       1, "A/B/lambda"},
      {2, "A/B/lambda",   "base-deleted", NO_COPY_FROM, "A/Z/lambda"},
      {0}
    };
    nodes_row_t nodes_AZ[] = {
      {2, "A/Z",          "normal",       NO_COPY_FROM},
      {3, "A/Z/lambda",   "normal",       1, "A/B/lambda", MOVED_HERE },
      {0}
    };

    SVN_ERR(check_db_rows(&b, "A/B", nodes_AB));
    SVN_ERR(check_db_rows(&b, "A/Z", nodes_AZ));
  }

  SVN_ERR(sbox_wc_move(&b, "A", "A_moved"));

  {
    nodes_row_t nodes[] = {
      {0, "",                   "normal",       0, ""},
      {0, "A",                  "normal",       1, "A"},
      {1, "A",                  "base-deleted", NO_COPY_FROM, "A_moved"},
      {0, "A/B",                "normal",       1, "A/B"},
      {1, "A/B",                "base-deleted", NO_COPY_FROM},
      {0, "A/B/E",              "normal",       1, "A/B/E"},
      {1, "A/B/E",              "base-deleted", NO_COPY_FROM},
      {0, "A/B/E/alpha",        "normal",       1, "A/B/E/alpha"},
      {1, "A/B/E/alpha",        "base-deleted", NO_COPY_FROM},
      {0, "A/B/E/beta",         "normal",       1, "A/B/E/beta"},
      {1, "A/B/E/beta",         "base-deleted", NO_COPY_FROM},
      {0, "A/B/F",              "normal",       1, "A/B/F"},
      {1, "A/B/F",              "base-deleted", NO_COPY_FROM},
      {0, "A/B/lambda",         "normal",       1, "A/B/lambda"},
      {1, "A/B/lambda",         "base-deleted", NO_COPY_FROM},
      {0, "A/C",                "normal",       1, "A/C"},
      {1, "A/C",                "base-deleted", NO_COPY_FROM},
      {0, "A/D",                "normal",       1, "A/D"},
      {1, "A/D",                "base-deleted", NO_COPY_FROM},
      {0, "A/D/G",              "normal",       1, "A/D/G"},
      {1, "A/D/G",              "base-deleted", NO_COPY_FROM},
      {0, "A/D/G/pi",           "normal",       1, "A/D/G/pi"},
      {1, "A/D/G/pi",           "base-deleted", NO_COPY_FROM},
      {0, "A/D/G/rho",          "normal",       1, "A/D/G/rho"},
      {1, "A/D/G/rho",          "base-deleted", NO_COPY_FROM},
      {0, "A/D/G/tau",          "normal",       1, "A/D/G/tau"},
      {1, "A/D/G/tau",          "base-deleted", NO_COPY_FROM},
      {0, "A/D/H",              "normal",       1, "A/D/H"},
      {1, "A/D/H",              "base-deleted", NO_COPY_FROM},
      {0, "A/D/H/chi",          "normal",       1, "A/D/H/chi"},
      {1, "A/D/H/chi",          "base-deleted", NO_COPY_FROM},
      {0, "A/D/H/omega",        "normal",       1, "A/D/H/omega"},
      {1, "A/D/H/omega",        "base-deleted", NO_COPY_FROM},
      {0, "A/D/H/psi",          "normal",       1, "A/D/H/psi"},
      {1, "A/D/H/psi",          "base-deleted", NO_COPY_FROM},
      {0, "A/D/gamma",          "normal",       1, "A/D/gamma"},
      {1, "A/D/gamma",          "base-deleted", NO_COPY_FROM},
      {0, "A/mu",               "normal",       1, "A/mu"},
      {1, "A/mu",               "base-deleted", NO_COPY_FROM},
      {1, "A_moved",            "normal",       1, "A", MOVED_HERE},
      {1, "A_moved/B",          "normal",       1, "A/B", MOVED_HERE},
      {2, "A_moved/B",          "base-deleted", NO_COPY_FROM},
      {2, "A_moved/B/E",        "base-deleted", NO_COPY_FROM},
      {1, "A_moved/B/E",        "normal",       1, "A/B/E", MOVED_HERE},
      {1, "A_moved/B/E/alpha",  "normal",       1, "A/B/E/alpha", MOVED_HERE},
      {2, "A_moved/B/E/alpha",  "base-deleted", NO_COPY_FROM},
      {1, "A_moved/B/E/beta",   "normal",       1, "A/B/E/beta", MOVED_HERE},
      {2, "A_moved/B/E/beta",   "base-deleted", NO_COPY_FROM},
      {1, "A_moved/B/F",        "normal",       1, "A/B/F", MOVED_HERE},
      {2, "A_moved/B/F",        "base-deleted", NO_COPY_FROM},
      {1, "A_moved/B/lambda",   "normal",       1, "A/B/lambda", MOVED_HERE},
      {2, "A_moved/B/lambda",   "base-deleted", NO_COPY_FROM, "A_moved/Z/lambda"},
      {1, "A_moved/C",          "normal",       1, "A/C", MOVED_HERE},
      {1, "A_moved/D",          "normal",       1, "A/D", MOVED_HERE},
      {1, "A_moved/D/G",        "normal",       1, "A/D/G", MOVED_HERE},
      {1, "A_moved/D/G/pi",     "normal",       1, "A/D/G/pi", MOVED_HERE},
      {1, "A_moved/D/G/rho",    "normal",       1, "A/D/G/rho", MOVED_HERE},
      {1, "A_moved/D/G/tau",    "normal",       1, "A/D/G/tau", MOVED_HERE},
      {1, "A_moved/D/H",        "normal",       1, "A/D/H", MOVED_HERE},
      {1, "A_moved/D/H/chi",    "normal",       1, "A/D/H/chi", MOVED_HERE},
      {1, "A_moved/D/H/omega",  "normal",       1, "A/D/H/omega", MOVED_HERE},
      {1, "A_moved/D/H/psi",    "normal",       1, "A/D/H/psi", MOVED_HERE},
      {1, "A_moved/D/gamma",    "normal",       1, "A/D/gamma", MOVED_HERE},
      {2, "A_moved/Z",          "normal",       NO_COPY_FROM},
      {3, "A_moved/Z/lambda",   "normal",       1, "A/B/lambda", MOVED_HERE},
      {1, "A_moved/mu",         "normal",       1, "A/mu", MOVED_HERE},
      {0, "iota", "normal",  1, "iota"},

      {0}
    };

    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_within_mixed_move(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_within_mixed_move", opts, pool));

  SVN_ERR(sbox_add_and_commit_greek_tree(&b));

  SVN_ERR(sbox_wc_delete(&b, "iota"));
  SVN_ERR(sbox_wc_commit(&b, ""));

  /* Make A mixed revision */
  SVN_ERR(sbox_wc_update(&b, "A/B/E", 2));

  /* Single rev moves.. ok */
  SVN_ERR(sbox_wc_move(&b, "A/D", "A/D_mv"));
  SVN_ERR(sbox_wc_move(&b, "A/C", "C_mv"));

  {
    nodes_row_t nodes[] = {
      {0, "",                 "normal",       0, ""},
      {0, "A",                "normal",       1, "A"},
      {0, "A/B",              "normal",       1, "A/B"},
      {0, "A/B/E",            "normal",       2, "A/B/E"},
      {0, "A/B/E/alpha",      "normal",       2, "A/B/E/alpha"},
      {0, "A/B/E/beta",       "normal",       2, "A/B/E/beta"},
      {0, "A/B/F",            "normal",       1, "A/B/F"},
      {0, "A/B/lambda",       "normal",       1, "A/B/lambda"},
      {0, "A/C",              "normal",       1, "A/C"},
      {0, "A/D",              "normal",       1, "A/D"},
      {0, "A/D/G",            "normal",       1, "A/D/G"},
      {0, "A/D/G/pi",         "normal",       1, "A/D/G/pi"},
      {0, "A/D/G/rho",        "normal",       1, "A/D/G/rho"},
      {0, "A/D/G/tau",        "normal",       1, "A/D/G/tau"},
      {0, "A/D/gamma",        "normal",       1, "A/D/gamma"},
      {0, "A/D/H",            "normal",       1, "A/D/H"},
      {0, "A/D/H/chi",        "normal",       1, "A/D/H/chi"},
      {0, "A/D/H/omega",      "normal",       1, "A/D/H/omega"},
      {0, "A/D/H/psi",        "normal",       1, "A/D/H/psi"},
      {0, "A/mu",             "normal",       1, "A/mu"},
      {0, "iota",             "not-present",  2, "iota"},
      {1, "C_mv",             "normal",       1, "A/C", MOVED_HERE},
      {2, "A/C",              "base-deleted", NO_COPY_FROM, "C_mv"},
      {2, "A/D",              "base-deleted", NO_COPY_FROM, "A/D_mv"},
      {2, "A/D/G",            "base-deleted", NO_COPY_FROM},
      {2, "A/D/G/pi",         "base-deleted", NO_COPY_FROM},
      {2, "A/D/G/rho",        "base-deleted", NO_COPY_FROM},
      {2, "A/D/G/tau",        "base-deleted", NO_COPY_FROM},
      {2, "A/D/gamma",        "base-deleted", NO_COPY_FROM},
      {2, "A/D/H",            "base-deleted", NO_COPY_FROM},
      {2, "A/D/H/chi",        "base-deleted", NO_COPY_FROM},
      {2, "A/D/H/omega",      "base-deleted", NO_COPY_FROM},
      {2, "A/D/H/psi",        "base-deleted", NO_COPY_FROM},
      {2, "A/D_mv",           "normal",       1, "A/D", MOVED_HERE},
      {2, "A/D_mv/G",         "normal",       1, "A/D/G", MOVED_HERE},
      {2, "A/D_mv/G/pi",      "normal",       1, "A/D/G/pi", MOVED_HERE},
      {2, "A/D_mv/G/rho",     "normal",       1, "A/D/G/rho", MOVED_HERE},
      {2, "A/D_mv/G/tau",     "normal",       1, "A/D/G/tau", MOVED_HERE},
      {2, "A/D_mv/gamma",     "normal",       1, "A/D/gamma", MOVED_HERE},
      {2, "A/D_mv/H",         "normal",       1, "A/D/H", MOVED_HERE},
      {2, "A/D_mv/H/chi",     "normal",       1, "A/D/H/chi", MOVED_HERE},
      {2, "A/D_mv/H/omega",   "normal",       1, "A/D/H/omega", MOVED_HERE},
      {2, "A/D_mv/H/psi",     "normal",       1, "A/D/H/psi", MOVED_HERE},
      {0}
    };

    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  /* Mixed rev move... breaks recordings "A/D" -> "A/D_mv"  */
  SVN_ERR(sbox_wc_move(&b, "A", "A_mv"));

  {
    nodes_row_t nodes[] = {
      {0, "",                 "normal",       0, ""},
      {0, "A",                "normal",       1, "A"},
      {0, "A/B",              "normal",       1, "A/B"},
      {0, "A/B/E",            "normal",       2, "A/B/E"},
      {0, "A/B/E/alpha",      "normal",       2, "A/B/E/alpha"},
      {0, "A/B/E/beta",       "normal",       2, "A/B/E/beta"},
      {0, "A/B/F",            "normal",       1, "A/B/F"},
      {0, "A/B/lambda",       "normal",       1, "A/B/lambda"},
      {0, "A/C",              "normal",       1, "A/C"},
      {0, "A/D",              "normal",       1, "A/D"},
      {0, "A/D/G",            "normal",       1, "A/D/G"},
      {0, "A/D/G/pi",         "normal",       1, "A/D/G/pi"},
      {0, "A/D/G/rho",        "normal",       1, "A/D/G/rho"},
      {0, "A/D/G/tau",        "normal",       1, "A/D/G/tau"},
      {0, "A/D/gamma",        "normal",       1, "A/D/gamma"},
      {0, "A/D/H",            "normal",       1, "A/D/H"},
      {0, "A/D/H/chi",        "normal",       1, "A/D/H/chi"},
      {0, "A/D/H/omega",      "normal",       1, "A/D/H/omega"},
      {0, "A/D/H/psi",        "normal",       1, "A/D/H/psi"},
      {0, "A/mu",             "normal",       1, "A/mu"},
      {0, "iota",             "not-present",  2, "iota"},
      {1, "A",                "base-deleted", NO_COPY_FROM },
      {1, "A/B",              "base-deleted", NO_COPY_FROM },
      {1, "A/B/E",            "base-deleted", NO_COPY_FROM },
      {1, "A/B/E/alpha",      "base-deleted", NO_COPY_FROM },
      {1, "A/B/E/beta",       "base-deleted", NO_COPY_FROM },
      {1, "A/B/F",            "base-deleted", NO_COPY_FROM },
      {1, "A/B/lambda",       "base-deleted", NO_COPY_FROM },
      {1, "A/C",              "base-deleted", NO_COPY_FROM, "C_mv"},
      {1, "A/D",              "base-deleted", NO_COPY_FROM, "A/D_mv" },
      {1, "A/D/G",            "base-deleted", NO_COPY_FROM },
      {1, "A/D/G/pi",         "base-deleted", NO_COPY_FROM },
      {1, "A/D/G/rho",        "base-deleted", NO_COPY_FROM },
      {1, "A/D/G/tau",        "base-deleted", NO_COPY_FROM },
      {1, "A/D/gamma",        "base-deleted", NO_COPY_FROM },
      {1, "A/D/H",            "base-deleted", NO_COPY_FROM },
      {1, "A/D/H/chi",        "base-deleted", NO_COPY_FROM },
      {1, "A/D/H/omega",      "base-deleted", NO_COPY_FROM },
      {1, "A/D/H/psi",        "base-deleted", NO_COPY_FROM },
      {1, "A/mu",             "base-deleted", NO_COPY_FROM },
      {1, "A_mv",             "normal",       1, "A"},
      {1, "A_mv/B",           "normal",       1, "A/B"},
      {1, "A_mv/B/E",         "not-present",  2, "A/B/E"},
      {1, "A_mv/B/F",         "normal",       1, "A/B/F"},
      {1, "A_mv/B/lambda",    "normal",       1, "A/B/lambda"},
      {1, "A_mv/C",           "normal",       1, "A/C"},
      {1, "A_mv/D",           "normal",       1, "A/D"},
      {1, "A_mv/D/G",         "normal",       1, "A/D/G"},
      {1, "A_mv/D/G/pi",      "normal",       1, "A/D/G/pi"},
      {1, "A_mv/D/G/rho",     "normal",       1, "A/D/G/rho"},
      {1, "A_mv/D/G/tau",     "normal",       1, "A/D/G/tau"},
      {1, "A_mv/D/gamma",     "normal",       1, "A/D/gamma"},
      {1, "A_mv/D/H",         "normal",       1, "A/D/H"},
      {1, "A_mv/D/H/chi",     "normal",       1, "A/D/H/chi"},
      {1, "A_mv/D/H/omega",   "normal",       1, "A/D/H/omega"},
      {1, "A_mv/D/H/psi",     "normal",       1, "A/D/H/psi"},
      {1, "A_mv/mu",          "normal",       1, "A/mu"},
      {1, "C_mv",             "normal",       1, "A/C", MOVED_HERE},
      {2, "A_mv/C",           "base-deleted", NO_COPY_FROM },
      {2, "A_mv/D",           "base-deleted", NO_COPY_FROM },
      {2, "A_mv/D/G",         "base-deleted", NO_COPY_FROM },
      {2, "A_mv/D/G/pi",      "base-deleted", NO_COPY_FROM },
      {2, "A_mv/D/G/rho",     "base-deleted", NO_COPY_FROM },
      {2, "A_mv/D/G/tau",     "base-deleted", NO_COPY_FROM },
      {2, "A_mv/D/gamma",     "base-deleted", NO_COPY_FROM },
      {2, "A_mv/D/H",         "base-deleted", NO_COPY_FROM },
      {2, "A_mv/D/H/chi",     "base-deleted", NO_COPY_FROM },
      {2, "A_mv/D/H/omega",   "base-deleted", NO_COPY_FROM },
      {2, "A_mv/D/H/psi",     "base-deleted", NO_COPY_FROM },
      {2, "A_mv/D_mv",        "normal",       1, "A/D", MOVED_HERE},
      {2, "A_mv/D_mv/G",      "normal",       1, "A/D/G", MOVED_HERE},
      {2, "A_mv/D_mv/G/pi",   "normal",       1, "A/D/G/pi", MOVED_HERE},
      {2, "A_mv/D_mv/G/rho",  "normal",       1, "A/D/G/rho", MOVED_HERE},
      {2, "A_mv/D_mv/G/tau",  "normal",       1, "A/D/G/tau", MOVED_HERE},
      {2, "A_mv/D_mv/gamma",  "normal",       1, "A/D/gamma", MOVED_HERE},
      {2, "A_mv/D_mv/H",      "normal",       1, "A/D/H", MOVED_HERE},
      {2, "A_mv/D_mv/H/chi",  "normal",       1, "A/D/H/chi", MOVED_HERE},
      {2, "A_mv/D_mv/H/omega","normal",       1, "A/D/H/omega", MOVED_HERE},
      {2, "A_mv/D_mv/H/psi",  "normal",       1, "A/D/H/psi", MOVED_HERE},
      {3, "A_mv/B/E",         "normal",       2, "A/B/E"},
      {3, "A_mv/B/E/alpha",   "normal",       2, "A/B/E/alpha"},
      {3, "A_mv/B/E/beta",    "normal",       2, "A/B/E/beta"},

      {0}
    };

    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_edit_obstruction(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_edit_obstruction", opts, pool));

  SVN_ERR(sbox_add_and_commit_greek_tree(&b));

  SVN_ERR(sbox_file_write(&b, "A/B/E/alpha", "Update alpha"));
  SVN_ERR(sbox_wc_propset(&b, "a", "b", "A/B/F"));
  SVN_ERR(sbox_wc_commit(&b, "")); /* r2 */

  SVN_ERR(sbox_wc_update(&b, "", 1));

  /* Simple move */
  SVN_ERR(sbox_wc_move(&b, "A", "A_mv"));
  SVN_ERR(sbox_wc_update(&b, "", 2));
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));

  {
    nodes_row_t nodes[] = {
      {1, "A_mv",             "normal",       2, "A", MOVED_HERE},
      {1, "A_mv/B",           "normal",       2, "A/B", MOVED_HERE},
      {1, "A_mv/B/E",         "normal",       2, "A/B/E", MOVED_HERE},
      {1, "A_mv/B/E/alpha",   "normal",       2, "A/B/E/alpha", MOVED_HERE},
      {1, "A_mv/B/E/beta",    "normal",       2, "A/B/E/beta", MOVED_HERE},
      {1, "A_mv/B/F",         "normal",       2, "A/B/F", MOVED_HERE, "a"},
      {1, "A_mv/B/lambda",    "normal",       2, "A/B/lambda", MOVED_HERE},
      {1, "A_mv/C",           "normal",       2, "A/C", MOVED_HERE},
      {1, "A_mv/D",           "normal",       2, "A/D", MOVED_HERE},
      {1, "A_mv/D/G",         "normal",       2, "A/D/G", MOVED_HERE},
      {1, "A_mv/D/G/pi",      "normal",       2, "A/D/G/pi", MOVED_HERE},
      {1, "A_mv/D/G/rho",     "normal",       2, "A/D/G/rho", MOVED_HERE},
      {1, "A_mv/D/G/tau",     "normal",       2, "A/D/G/tau", MOVED_HERE},
      {1, "A_mv/D/gamma",     "normal",       2, "A/D/gamma", MOVED_HERE},
      {1, "A_mv/D/H",         "normal",       2, "A/D/H", MOVED_HERE},
      {1, "A_mv/D/H/chi",     "normal",       2, "A/D/H/chi", MOVED_HERE},
      {1, "A_mv/D/H/omega",   "normal",       2, "A/D/H/omega", MOVED_HERE},
      {1, "A_mv/D/H/psi",     "normal",       2, "A/D/H/psi", MOVED_HERE},
      {1, "A_mv/mu",          "normal",       2, "A/mu", MOVED_HERE},
      {0}
    };

    SVN_ERR(check_db_rows(&b, "A_mv", nodes));
    SVN_ERR(check_db_conflicts(&b, "", NULL));
  }

  /* Now do the same thing with local obstructions on the edited nodes */
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_revert(&b, "", svn_depth_infinity));
  SVN_ERR(sbox_wc_move(&b, "A", "A_mv"));

  SVN_ERR(svn_io_remove_file2(sbox_wc_path(&b, "A_mv/B/E/alpha"), FALSE, pool));
  SVN_ERR(svn_io_dir_make(sbox_wc_path(&b, "A_mv/B/E/alpha"), APR_OS_DEFAULT,
                          pool));
  SVN_ERR(svn_io_dir_remove_nonrecursive(sbox_wc_path(&b, "A_mv/B/F"), pool));
  SVN_ERR(sbox_file_write(&b, "A_mv/B/F", "F file"));

  SVN_ERR(sbox_wc_update(&b, "", 2));
  SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));

  {
    nodes_row_t nodes[] = {
      {1, "A_mv",             "normal",       2, "A", MOVED_HERE},
      {1, "A_mv/B",           "normal",       2, "A/B", MOVED_HERE},
      {1, "A_mv/B/E",         "normal",       2, "A/B/E", MOVED_HERE},
      {1, "A_mv/B/E/alpha",   "normal",       2, "A/B/E/alpha", MOVED_HERE},
      {1, "A_mv/B/E/beta",    "normal",       2, "A/B/E/beta", MOVED_HERE},
      {1, "A_mv/B/F",         "normal",       2, "A/B/F", MOVED_HERE, "a"},
      {1, "A_mv/B/lambda",    "normal",       2, "A/B/lambda", MOVED_HERE},
      {1, "A_mv/C",           "normal",       2, "A/C", MOVED_HERE},
      {1, "A_mv/D",           "normal",       2, "A/D", MOVED_HERE},
      {1, "A_mv/D/G",         "normal",       2, "A/D/G", MOVED_HERE},
      {1, "A_mv/D/G/pi",      "normal",       2, "A/D/G/pi", MOVED_HERE},
      {1, "A_mv/D/G/rho",     "normal",       2, "A/D/G/rho", MOVED_HERE},
      {1, "A_mv/D/G/tau",     "normal",       2, "A/D/G/tau", MOVED_HERE},
      {1, "A_mv/D/gamma",     "normal",       2, "A/D/gamma", MOVED_HERE},
      {1, "A_mv/D/H",         "normal",       2, "A/D/H", MOVED_HERE},
      {1, "A_mv/D/H/chi",     "normal",       2, "A/D/H/chi", MOVED_HERE},
      {1, "A_mv/D/H/omega",   "normal",       2, "A/D/H/omega", MOVED_HERE},
      {1, "A_mv/D/H/psi",     "normal",       2, "A/D/H/psi", MOVED_HERE},
      {1, "A_mv/mu",          "normal",       2, "A/mu", MOVED_HERE},
      {0}
    };
    conflict_info_t conflicts[] = {
      {"A_mv/B/E/alpha", FALSE, FALSE, {svn_wc_conflict_action_edit,
                                        svn_wc_conflict_reason_obstructed}},
      {"A_mv/B/F",       FALSE, FALSE, {svn_wc_conflict_action_edit,
                                        svn_wc_conflict_reason_obstructed}},
      {0}
    };

    SVN_ERR(check_db_rows(&b, "A_mv", nodes));
    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
move_deep_bump(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "move_deep_bump", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "B"));
  SVN_ERR(sbox_wc_mkdir(&b, "B/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "B/B/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "B/B/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "B/B/A/A/A"));
  SVN_ERR(sbox_wc_mkdir(&b, "C"));
  SVN_ERR(sbox_wc_mkdir(&b, "C/C"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "Z"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_mkdir(&b, "B/B/A/A/A/A"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));

  SVN_ERR(sbox_wc_move(&b, "B/B/A", "B/B/B"));
  SVN_ERR(sbox_wc_move(&b, "B/B/B/A", "C/C/A"));

  /* This can't bump C/C/A as that is outside the lock range
     so we expect a tree conflict.

     This used to cause a node not found during bumping
     because B/B/B/A doesn't have a BASE node */
  SVN_ERR(sbox_wc_update(&b, "B/B", 2));

  {
    nodes_row_t nodes[] = {
      {0, "",                 "normal",       1, ""},
      {0, "B",                "normal",       1, "B"},
      {0, "B/B",              "normal",       2, "B/B"},
      {0, "B/B/A",            "normal",       2, "B/B/A"},
      {0, "B/B/A/A",          "normal",       2, "B/B/A/A"},
      {0, "B/B/A/A/A",        "normal",       2, "B/B/A/A/A"},
      {0, "C",                "normal",       1, "C"},
      {0, "C/C",              "normal",       1, "C/C"},
      {3, "B/B/A",            "base-deleted", NO_COPY_FROM, "B/B/B"},
      {3, "B/B/A/A",          "base-deleted", NO_COPY_FROM},
      {3, "B/B/A/A/A",        "base-deleted", NO_COPY_FROM},
      {3, "B/B/B",            "normal",       2, "B/B/A", MOVED_HERE},
      {3, "B/B/B/A",          "normal",       2, "B/B/A/A", MOVED_HERE},
      {3, "B/B/B/A/A",        "normal",       2, "B/B/A/A/A", MOVED_HERE},
      {3, "C/C/A",            "normal",       1, "B/B/A/A", MOVED_HERE},
      {3, "C/C/A/A",          "normal",       1, "B/B/A/A/A", MOVED_HERE},
      {4, "B/B/B/A",          "base-deleted", NO_COPY_FROM, "C/C/A"},
      {4, "B/B/B/A/A",        "base-deleted", NO_COPY_FROM},
      {0}
    };
    conflict_info_t conflicts[] = {
      {"B/B/B/A", FALSE, FALSE, {svn_wc_conflict_action_edit,
                                 svn_wc_conflict_reason_moved_away, "B/B/B/A"}},
      {0}
    };

    SVN_ERR(check_db_rows(&b, "", nodes));
    SVN_ERR(check_db_conflicts(&b, "", conflicts));
  }

  SVN_ERR(sbox_wc_resolve(&b, "B/B/B/A", svn_depth_empty,
                          svn_wc_conflict_choose_mine_conflict));
  SVN_ERR(check_db_conflicts(&b, "", NULL));

  return SVN_NO_ERROR;
}

static svn_error_t *
make_copy_mixed(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "make_copy_mixed", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/E"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/F"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/H"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/I"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/J"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/K"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/K/L"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/K/M"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/N"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/N/O"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/N/P"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/N/Q"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/R"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/R/S"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/R/S/T"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_propset(&b, "k", "r2", ""));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_propset(&b, "k", "r3", ""));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_propset(&b, "k", "r4", ""));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_propset(&b, "k", "r5", ""));
  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_update(&b, "", 5));
  SVN_ERR(sbox_wc_update(&b, "A", 4));
  SVN_ERR(sbox_wc_update(&b, "A/B", 3));
  SVN_ERR(sbox_wc_update(&b, "A/B/C", 2));
  SVN_ERR(sbox_wc_update(&b, "A/B/K", 1));
  SVN_ERR(sbox_wc_update(&b, "A/N/O", 3));

  SVN_ERR(sbox_wc_delete(&b, "A/B/C/F"));
  SVN_ERR(sbox_wc_delete(&b, "A/B/G/J"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/J"));

  SVN_ERR(sbox_wc_update(&b, "A/N/P", 1));
  SVN_ERR(sbox_wc_update(&b, "A/N/Q", 1));
  SVN_ERR(sbox_wc_delete(&b, "A/N/P"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/N/P"));
  SVN_ERR(sbox_wc_move(&b, "A/N/Q", "Q"));
  SVN_ERR(sbox_wc_move(&b, "A/B/G/H", "H"));

  /* And something that can't be represented */
  SVN_ERR(sbox_wc_update(&b, "A/B/C/E", 1));
  SVN_ERR(sbox_wc_move(&b, "A/B/C/E", "E"));

  {
    nodes_row_t nodes[] = {
      {0, "",             "normal",       5, "", NOT_MOVED, "k"},
      {0, "A",            "normal",       4, "A"},
      {0, "A/B",          "normal",       3, "A/B"},
      {0, "A/B/C",        "normal",       2, "A/B/C"},
      {0, "A/B/C/D",      "normal",       2, "A/B/C/D"},
      {0, "A/B/C/E",      "normal",       1, "A/B/C/E"},
      {0, "A/B/C/F",      "normal",       2, "A/B/C/F"},
      {0, "A/B/G",        "normal",       3, "A/B/G"},
      {0, "A/B/G/H",      "normal",       3, "A/B/G/H"},
      {0, "A/B/G/I",      "normal",       3, "A/B/G/I"},
      {0, "A/B/G/J",      "normal",       3, "A/B/G/J"},
      {0, "A/B/K",        "normal",       1, "A/B/K"},
      {0, "A/B/K/L",      "normal",       1, "A/B/K/L"},
      {0, "A/B/K/M",      "normal",       1, "A/B/K/M"},
      {0, "A/N",          "normal",       4, "A/N"},
      {0, "A/N/O",        "normal",       3, "A/N/O"},
      {0, "A/N/P",        "normal",       1, "A/N/P"},
      {0, "A/N/Q",        "normal",       1, "A/N/Q"},
      {0, "A/R",          "normal",       4, "A/R"},
      {0, "A/R/S",        "normal",       4, "A/R/S"},
      {0, "A/R/S/T",      "normal",       4, "A/R/S/T"},
      {1, "E",            "normal",       1, "A/B/C/E", MOVED_HERE},
      {1, "H",            "normal",       3, "A/B/G/H", MOVED_HERE},
      {1, "Q",            "normal",       1, "A/N/Q", MOVED_HERE},
      {3, "A/N/P",        "normal",       NO_COPY_FROM},
      {3, "A/N/Q",        "base-deleted", NO_COPY_FROM, "Q"},
      {4, "A/B/C/E",      "base-deleted", NO_COPY_FROM, "E"},
      {4, "A/B/C/F",      "base-deleted", NO_COPY_FROM},
      {4, "A/B/G/H",      "base-deleted", NO_COPY_FROM, "H"},
      {4, "A/B/G/J",      "normal",       NO_COPY_FROM},

      {0}
    };

    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(svn_wc__db_op_make_copy(b.wc_ctx->db, sbox_wc_path(&b, "A"),
                                  NULL, NULL, pool));

  {
    nodes_row_t nodes[] = {
      {0, "",             "normal",       5, "", NOT_MOVED, "k"},
      {0, "A",            "normal",       4, "A"},
      {0, "A/B",          "normal",       3, "A/B"},
      {0, "A/B/C",        "normal",       2, "A/B/C"},
      {0, "A/B/C/D",      "normal",       2, "A/B/C/D"},
      {0, "A/B/C/E",      "normal",       1, "A/B/C/E"},
      {0, "A/B/C/F",      "normal",       2, "A/B/C/F"},
      {0, "A/B/G",        "normal",       3, "A/B/G"},
      {0, "A/B/G/H",      "normal",       3, "A/B/G/H"},
      {0, "A/B/G/I",      "normal",       3, "A/B/G/I"},
      {0, "A/B/G/J",      "normal",       3, "A/B/G/J"},
      {0, "A/B/K",        "normal",       1, "A/B/K"},
      {0, "A/B/K/L",      "normal",       1, "A/B/K/L"},
      {0, "A/B/K/M",      "normal",       1, "A/B/K/M"},
      {0, "A/N",          "normal",       4, "A/N"},
      {0, "A/N/O",        "normal",       3, "A/N/O"},
      {0, "A/N/P",        "normal",       1, "A/N/P"},
      {0, "A/N/Q",        "normal",       1, "A/N/Q"},
      {0, "A/R",          "normal",       4, "A/R"},
      {0, "A/R/S",        "normal",       4, "A/R/S"},
      {0, "A/R/S/T",      "normal",       4, "A/R/S/T"},
      {1, "A",            "normal",       4, "A"},
      {1, "A/B",          "not-present",  3, "A/B"},
      {1, "A/B/C",        "base-deleted", NO_COPY_FROM},
      {1, "A/B/C/D",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/C/E",      "base-deleted", NO_COPY_FROM, "E"},
      {1, "A/B/C/F",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/G",        "base-deleted", NO_COPY_FROM},
      {1, "A/B/G/H",      "base-deleted", NO_COPY_FROM, "H"},
      {1, "A/B/G/I",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/G/J",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/K",        "base-deleted", NO_COPY_FROM},
      {1, "A/B/K/L",      "base-deleted", NO_COPY_FROM},
      {1, "A/B/K/M",      "base-deleted", NO_COPY_FROM},
      {1, "A/N",          "normal",       4, "A/N"},
      {1, "A/N/O",        "not-present",  3, "A/N/O"},
      {1, "A/N/P",        "not-present",  1, "A/N/P"},
      {1, "A/N/Q",        "not-present",  1, "A/N/Q", FALSE, "Q"},
      {1, "A/R",          "normal",       4, "A/R"},
      {1, "A/R/S",        "normal",       4, "A/R/S"},
      {1, "A/R/S/T",      "normal",       4, "A/R/S/T"},
      {1, "E",            "normal",       1, "A/B/C/E", MOVED_HERE},
      {1, "H",            "normal",       3, "A/B/G/H", MOVED_HERE},
      {1, "Q",            "normal",       1, "A/N/Q", MOVED_HERE},
      {2, "A/B",          "normal",       3, "A/B"},
      {2, "A/B/C",        "not-present",  2, "A/B/C"},
      {2, "A/B/G",        "normal",       3, "A/B/G"},
      {2, "A/B/G/H",      "normal",       3, "A/B/G/H"},
      {2, "A/B/G/I",      "normal",       3, "A/B/G/I"},
      {2, "A/B/G/J",      "normal",       3, "A/B/G/J"},
      {2, "A/B/K",        "not-present",  1, "A/B/K"},
      {3, "A/B/C",        "normal",       2, "A/B/C"},
      {3, "A/B/C/D",      "normal",       2, "A/B/C/D"},
      {3, "A/B/C/E",      "not-present",  1, "A/B/C/E"},
      {3, "A/B/C/F",      "normal",       2, "A/B/C/F"},
      {3, "A/B/K",        "normal",       1, "A/B/K"},
      {3, "A/B/K/L",      "normal",       1, "A/B/K/L"},
      {3, "A/B/K/M",      "normal",       1, "A/B/K/M"},
      {3, "A/N/O",        "normal",       3, "A/N/O"},
      {3, "A/N/P",        "normal",       NO_COPY_FROM},
      {4, "A/B/C/F",      "base-deleted", NO_COPY_FROM},
      {4, "A/B/G/H",      "base-deleted", NO_COPY_FROM},
      {4, "A/B/G/J",      "normal",       NO_COPY_FROM},

      {0}
    };

    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(verify_db(&b));

  return SVN_NO_ERROR;
}

static svn_error_t *
make_copy_and_delete_mixed(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "make_copy_and_del_mixed", opts, pool));

  SVN_ERR(sbox_wc_mkdir(&b, "A"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/E"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/F"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/H"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/I"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/J"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/K"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/K/L"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/K/M"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/N"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/N/O"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/N/P"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/N/Q"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/R"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/R/S"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/R/S/T"));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_update(&b, "", 1));
  SVN_ERR(sbox_wc_propset(&b, "k", "r2", ""));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_propset(&b, "k", "r3", ""));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_propset(&b, "k", "r4", ""));
  SVN_ERR(sbox_wc_commit(&b, ""));
  SVN_ERR(sbox_wc_propset(&b, "k", "r5", ""));
  SVN_ERR(sbox_wc_commit(&b, ""));

  SVN_ERR(sbox_wc_update(&b, "", 5));
  SVN_ERR(sbox_wc_update(&b, "A", 4));
  SVN_ERR(sbox_wc_update(&b, "A/B", 3));
  SVN_ERR(sbox_wc_update(&b, "A/B/C", 2));
  SVN_ERR(sbox_wc_update(&b, "A/B/K", 1));
  SVN_ERR(sbox_wc_update(&b, "A/N/O", 3));

  SVN_ERR(sbox_wc_delete(&b, "A/B/C/F"));
  SVN_ERR(sbox_wc_delete(&b, "A/B/G/J"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/J"));

  SVN_ERR(sbox_wc_update(&b, "A/N/P", 1));
  SVN_ERR(sbox_wc_update(&b, "A/N/Q", 1));
  SVN_ERR(sbox_wc_delete(&b, "A/N/P"));
  SVN_ERR(sbox_wc_mkdir(&b, "A/N/P"));
  SVN_ERR(sbox_wc_move(&b, "A/N/Q", "Q"));
  SVN_ERR(sbox_wc_move(&b, "A/B/G/H", "H"));

  /* And something that can't be represented */
  SVN_ERR(sbox_wc_update(&b, "A/B/C/E", 1));
  SVN_ERR(sbox_wc_move(&b, "A/B/C/E", "E"));

  {
    nodes_row_t nodes[] = {
      {0, "",             "normal",       5, "", NOT_MOVED, "k"},
      {0, "A",            "normal",       4, "A"},
      {0, "A/B",          "normal",       3, "A/B"},
      {0, "A/B/C",        "normal",       2, "A/B/C"},
      {0, "A/B/C/D",      "normal",       2, "A/B/C/D"},
      {0, "A/B/C/E",      "normal",       1, "A/B/C/E"},
      {0, "A/B/C/F",      "normal",       2, "A/B/C/F"},
      {0, "A/B/G",        "normal",       3, "A/B/G"},
      {0, "A/B/G/H",      "normal",       3, "A/B/G/H"},
      {0, "A/B/G/I",      "normal",       3, "A/B/G/I"},
      {0, "A/B/G/J",      "normal",       3, "A/B/G/J"},
      {0, "A/B/K",        "normal",       1, "A/B/K"},
      {0, "A/B/K/L",      "normal",       1, "A/B/K/L"},
      {0, "A/B/K/M",      "normal",       1, "A/B/K/M"},
      {0, "A/N",          "normal",       4, "A/N"},
      {0, "A/N/O",        "normal",       3, "A/N/O"},
      {0, "A/N/P",        "normal",       1, "A/N/P"},
      {0, "A/N/Q",        "normal",       1, "A/N/Q"},
      {0, "A/R",          "normal",       4, "A/R"},
      {0, "A/R/S",        "normal",       4, "A/R/S"},
      {0, "A/R/S/T",      "normal",       4, "A/R/S/T"},
      {1, "E",            "normal",       1, "A/B/C/E", MOVED_HERE},
      {1, "H",            "normal",       3, "A/B/G/H", MOVED_HERE},
      {1, "Q",            "normal",       1, "A/N/Q", MOVED_HERE},
      {3, "A/N/P",        "normal",       NO_COPY_FROM},
      {3, "A/N/Q",        "base-deleted", NO_COPY_FROM, "Q"},
      {4, "A/B/C/E",      "base-deleted", NO_COPY_FROM, "E"},
      {4, "A/B/C/F",      "base-deleted", NO_COPY_FROM},
      {4, "A/B/G/H",      "base-deleted", NO_COPY_FROM, "H"},
      {4, "A/B/G/J",      "normal",       NO_COPY_FROM},

      {0}
    };

    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(svn_wc__db_base_remove(b.wc_ctx->db, sbox_wc_path(&b, "A"),
                                 TRUE, TRUE, FALSE, 99,
                                 NULL, NULL, pool));

  {
    nodes_row_t nodes[] = {
      {0, "",             "normal",       5, "", NOT_MOVED, "k"},
      {0, "A",            "not-present",  99, "A"},
      {1, "A",            "normal",       4, "A"},
      {1, "A/B",          "not-present",  3, "A/B"},
      {1, "A/N",          "normal",       4, "A/N"},
      {1, "A/N/O",        "not-present",  3, "A/N/O"},
      {1, "A/N/P",        "not-present",  1, "A/N/P"},
      {1, "A/N/Q",        "not-present",  1, "A/N/Q", FALSE},
      {1, "A/R",          "normal",       4, "A/R"},
      {1, "A/R/S",        "normal",       4, "A/R/S"},
      {1, "A/R/S/T",      "normal",       4, "A/R/S/T"},
      {1, "E",            "normal",       1, "A/B/C/E"},
      {1, "H",            "normal",       3, "A/B/G/H", MOVED_HERE},
      {1, "Q",            "normal",       1, "A/N/Q"},
      {2, "A/B",          "normal",       3, "A/B"},
      {2, "A/B/C",        "not-present",  2, "A/B/C"},
      {2, "A/B/G",        "normal",       3, "A/B/G"},
      {2, "A/B/G/H",      "normal",       3, "A/B/G/H"},
      {2, "A/B/G/I",      "normal",       3, "A/B/G/I"},
      {2, "A/B/G/J",      "normal",       3, "A/B/G/J"},
      {3, "A/B/C",        "normal",       2, "A/B/C"},
      {3, "A/B/C/D",      "normal",       2, "A/B/C/D"},
      {3, "A/B/C/E",      "not-present",  1, "A/B/C/E"},
      {3, "A/B/C/F",      "normal",       2, "A/B/C/F"},
      {2, "A/B/K",        "not-present",  1, "A/B/K"},
      {3, "A/B/K",        "normal",       1, "A/B/K"},
      {3, "A/B/K/L",      "normal",       1, "A/B/K/L"},
      {3, "A/B/K/M",      "normal",       1, "A/B/K/M"},
      {3, "A/N/O",        "normal",       3, "A/N/O"},
      {3, "A/N/P",        "normal",       NO_COPY_FROM},
      {4, "A/B/C/F",      "base-deleted", NO_COPY_FROM},
      {4, "A/B/G/H",      "base-deleted", NO_COPY_FROM, "H"},
      {4, "A/B/G/J",      "normal",       NO_COPY_FROM},

      {0}
    };

    /* This currently fails because Q and E are still marked as moved,
       while there is nothing to be moved. */
    SVN_ERR(check_db_rows(&b, "", nodes));
  }

  SVN_ERR(verify_db(&b));

  return SVN_NO_ERROR;
}

static svn_error_t *
test_global_commit(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "global_commit", opts, pool));

  {
    nodes_row_t before[] = {
      { 0, "",        "normal",       2, "" },
      { 0, "A",       "normal",       2, "A" },
      { 0, "A/B",     "normal",       2, "A/B" },
      { 0, "A/B/C",   "normal",       2, "A/B/C" },
      { 0, "A/B/D",   "normal",       2, "A/B/D" },
      { 0, "A/B/D/E", "normal",       2, "A/B/D/E" },
      { 0, "A/F",     "normal",       2, "A/F" },
      { 0, "A/F/G",   "normal",       2, "A/F/G" },
      { 0, "A/F/H",   "normal",       2, "A/F/H" },
      { 0, "A/F/E",   "normal",       2, "A/F/E" },
      { 0, "A/X",     "normal",       2, "A/X" },
      { 0, "A/X/Y",   "incomplete",   2, "A/X/Y" },
      { 1, "C",       "normal",       2, "A/B/C", MOVED_HERE},
      { 1, "E",       "normal",       2, "A/B/D/E", MOVED_HERE},
      { 2, "A/B",     "normal",       3, "some", MOVED_HERE },
      { 2, "A/B/C",   "base-deleted", NO_COPY_FROM, "C" },
      { 2, "A/B/D",   "normal",       3, "some/D", MOVED_HERE},
      { 2, "A/B/D/E", "not-present",  3, "some/D/E", FALSE, "E", TRUE},
      { 3, "A/B/C",   "normal",       NO_COPY_FROM},
      { 2, "A/F",     "normal",       1, "S2" },
      { 2, "A/F/G",   "normal",       1, "S2/G" },
      { 2, "A/F/H",   "not-present",  1, "S2/H" },
      { 2, "A/F/E",   "base-deleted", NO_COPY_FROM },
      { 1, "some",    "normal",       3, "some", FALSE, "A/B"},
      { 0 }
    };
    SVN_ERR(insert_dirs(&b, before));
    SVN_ERR(check_db_rows(&b, "", before)); /* Check move insertion logic */
    SVN_ERR(verify_db(&b));
  }

  /* This should break the moves */
  SVN_ERR(svn_wc__db_global_commit(b.wc_ctx->db,
                                   sbox_wc_path(&b, "A/B"),
                                   5, 5, 700, "me", NULL, NULL,
                                   FALSE, FALSE, NULL, pool));
  {
    nodes_row_t after[] = {
      { 0, "",        "normal",       2, "" },
      { 0, "A",       "normal",       2, "A" },
      { 0, "A/B",     "normal",       5, "A/B" },
      { 0, "A/B/D",   "normal",       5, "A/B/D"},
      { 0, "A/B/D/E", "not-present",  5, "A/B/D/E"},
      { 0, "A/F",     "normal",       2, "A/F" },
      { 0, "A/F/G",   "normal",       2, "A/F/G" },
      { 0, "A/F/H",   "normal",       2, "A/F/H" },
      { 0, "A/F/E",   "normal",       2, "A/F/E" },
      { 0, "A/X",     "normal",       2, "A/X" },
      { 0, "A/X/Y",   "incomplete",   2, "A/X/Y" },
      { 1, "C",       "normal",       2, "A/B/C"},
      { 1, "E",       "normal",       2, "A/B/D/E"},
      { 1, "some",    "normal",       3, "some"},
      { 3, "A/B/C",   "normal",       NO_COPY_FROM},
      { 2, "A/F",     "normal",       1, "S2" },
      { 2, "A/F/G",   "normal",       1, "S2/G" },
      { 2, "A/F/H",   "not-present",  1, "S2/H" },
      { 2, "A/F/E",   "base-deleted", NO_COPY_FROM },
      { 0 }
    };

    SVN_ERR(check_db_rows(&b, "", after));
    SVN_ERR(verify_db(&b));
  }

  SVN_ERR(svn_wc__db_global_commit(b.wc_ctx->db,
                                   sbox_wc_path(&b, "A/F"),
                                   6, 6, 800, "me", NULL, NULL,
                                   FALSE, FALSE, NULL, pool));

  {
    nodes_row_t after[] = {
      { 0, "",        "normal",       2, "" },
      { 0, "A",       "normal",       2, "A" },
      { 0, "A/B",     "normal",       5, "A/B" },
      { 0, "A/B/D",   "normal",       5, "A/B/D"},
      { 0, "A/B/D/E", "not-present",  5, "A/B/D/E"},
      { 0, "A/F",     "normal",       6, "A/F" },
      { 0, "A/F/G",   "normal",       6, "A/F/G" },
      { 0, "A/F/H",   "not-present",  6, "A/F/H" },
      { 0, "A/X",     "normal",       2, "A/X" },
      { 0, "A/X/Y",   "incomplete",   2, "A/X/Y" },
      { 1, "C",       "normal",       2, "A/B/C"},
      { 1, "E",       "normal",       2, "A/B/D/E"},
      { 1, "some",    "normal",       3, "some"},
      { 3, "A/B/C",   "normal",       NO_COPY_FROM },
      { 0 }
    };

    SVN_ERR(check_db_rows(&b, "", after));
    SVN_ERR(verify_db(&b));
  }

  SVN_ERR(svn_wc__db_global_commit(b.wc_ctx->db,
                                   sbox_wc_path(&b, "A/B/C"),
                                   7, 7, 900, "me", NULL, NULL,
                                   FALSE, FALSE, NULL, pool));

  {
    nodes_row_t after[] = {
      { 0, "",        "normal",       2, "" },
      { 0, "A",       "normal",       2, "A" },
      { 0, "A/B",     "normal",       5, "A/B" },
      { 0, "A/B/C",   "normal",       7, "A/B/C"},
      { 0, "A/B/D",   "normal",       5, "A/B/D"},
      { 0, "A/B/D/E", "not-present",  5, "A/B/D/E"},
      { 0, "A/F",     "normal",       6, "A/F" },
      { 0, "A/F/G",   "normal",       6, "A/F/G" },
      { 0, "A/F/H",   "not-present",  6, "A/F/H" },
      { 0, "A/X",     "normal",       2, "A/X" },
      { 0, "A/X/Y",   "incomplete",   2, "A/X/Y" },
      { 1, "some",    "normal",       3, "some"},
      { 1, "E",       "normal",       2, "A/B/D/E"},
      { 1, "C",       "normal",       2, "A/B/C"},
      { 0 }
    };

    SVN_ERR(check_db_rows(&b, "", after));
    SVN_ERR(verify_db(&b));
  }

  return SVN_NO_ERROR;
}

static svn_error_t *
test_global_commit_switched(const svn_test_opts_t *opts, apr_pool_t *pool)
{
  svn_test__sandbox_t b;

  SVN_ERR(svn_test__sandbox_create(&b, "global_commit_switched", opts, pool));
  {
    nodes_row_t before[] = {
      { 0, "",          "normal",       2, "" },
      { 0, "A",         "normal",       2, "A" },
      /* A/B is switched... The libsvn_client layer tries to prevent this,
                             because it has such an unexpected behavior. */
      { 0, "A/B",       "normal",       2, "N/B" },
      { 0, "A/B/C",     "normal",       2, "N/B/C" },
      { 0, "A/B/C/D",   "normal",       2, "N/B/C/D" },
      { 0, "A/B/C/E",   "normal",       2, "N/B/C/E" },
      { 2, "A/B",       "normal",       3, "Z/B" },
      { 2, "A/B/C",     "normal",       3, "Z/B/C" },
      { 2, "A/B/C/D",   "normal",       3, "Z/B/C/D" },
      { 2, "A/B/C/E",   "base-deleted", NO_COPY_FROM },
      /* not-present nodes have an 'uninteresting path',
         which doesn't have to be as implied by ancestor at same depth */
      { 2, "A/B/C/F",   "not-present",  3, "ZZ-Z-Z_ZZ_Z_Z" },
      { 2, "A/B/C/G",   "normal",       3, "Z/B/C/G" },
      { 2, "A/B/C/G/H", "normal",       3, "Z/B/C/G/H" },

      { 3, "A/B/C",     "normal",       4, "Q/C" },
      { 3, "A/B/C/D",   "base-deleted", NO_COPY_FROM },
      { 3, "A/B/C/G",   "normal",       4, "Q/C/G" },
      { 3, "A/B/C/G/H", "base-deleted", NO_COPY_FROM },

      { 4, "A/B/C/F",   "normal",       NO_COPY_FROM },
      { 5, "A/B/C/G/H", "normal",       NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(insert_dirs(&b, before));
    SVN_ERR(verify_db(&b));
  }

  SVN_ERR(svn_wc__db_global_commit(b.wc_ctx->db,
                                   sbox_wc_path(&b, "A/B"),
                                   7, 7, 12, "me", NULL, NULL,
                                   FALSE, FALSE, NULL, pool));

  {
    nodes_row_t after[] = {
      { 0, "",          "normal",       2, "" },
      { 0, "A",         "normal",       2, "A" },
      /* The commit is applied as A/B, because the path is calculated from A,
         and not the shadowed node at A/B. (Fixed in r1663991) */
      { 0, "A/B",       "normal",       7, "A/B" },
      { 0, "A/B/C",     "normal",       7, "A/B/C" },
      { 0, "A/B/C/D",   "normal",       7, "A/B/C/D" },
      /* Even calculated path of not-present is fixed */
      { 0, "A/B/C/F",   "not-present",  7, "A/B/C/F" },
      { 0, "A/B/C/G",   "normal",       7, "A/B/C/G" },
      { 0, "A/B/C/G/H", "normal",       7, "A/B/C/G/H" },

      /* The higher layers are unaffected */
      { 3, "A/B/C",     "normal",       4, "Q/C" },
      { 3, "A/B/C/D",   "base-deleted", NO_COPY_FROM },
      { 3, "A/B/C/G",   "normal",       4, "Q/C/G" },
      { 3, "A/B/C/G/H", "base-deleted", NO_COPY_FROM },

      { 4, "A/B/C/F",   "normal",       NO_COPY_FROM },
      { 5, "A/B/C/G/H", "normal",       NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(verify_db(&b));
    SVN_ERR(check_db_rows(&b, "", after));
  }

  SVN_ERR(svn_wc__db_global_commit(b.wc_ctx->db,
                                   sbox_wc_path(&b, "A/B/C"),
                                   8, 8, 12, "me", NULL, NULL,
                                   FALSE, FALSE, NULL, pool));

  {
    nodes_row_t after[] = {
      { 0, "",          "normal",       2, "" },
      { 0, "A",         "normal",       2, "A" },
      { 0, "A/B",       "normal",       7, "A/B" },
      /* Base deleted and not-present are now gone */
      { 0, "A/B/C",     "normal",       8, "A/B/C" },
      { 0, "A/B/C/G",   "normal",       8, "A/B/C/G" },

      { 4, "A/B/C/F",   "normal",       NO_COPY_FROM },
      { 5, "A/B/C/G/H", "normal",       NO_COPY_FROM },
      { 0 }
    };
    SVN_ERR(verify_db(&b));
    SVN_ERR(check_db_rows(&b, "", after));
  }

  return SVN_NO_ERROR;
}

/* ---------------------------------------------------------------------- */
/* The list of test functions */

static int max_threads = 4;

static struct svn_test_descriptor_t test_funcs[] =
  {
    SVN_TEST_NULL,
    SVN_TEST_OPTS_PASS(test_wc_wc_copies,
                       "test_wc_wc_copies"),
    SVN_TEST_OPTS_PASS(test_reverts,
                       "test_reverts"),
    SVN_TEST_OPTS_PASS(test_deletes,
                       "test_deletes"),
    SVN_TEST_OPTS_PASS(test_delete_of_copies,
                       "test_delete_of_copies"),
    SVN_TEST_OPTS_PASS(test_delete_with_base,
                       "test_delete_with_base"),
    SVN_TEST_OPTS_PASS(test_adds,
                       "test_adds"),
    SVN_TEST_OPTS_PASS(test_repo_wc_copies,
                       "test_repo_wc_copies"),
    SVN_TEST_OPTS_PASS(test_delete_with_update,
                       "test_delete_with_update"),
    SVN_TEST_OPTS_PASS(test_adds_change_kind,
                       "test_adds_change_kind"),
    SVN_TEST_OPTS_PASS(test_base_dir_insert_remove,
                       "test_base_dir_insert_remove"),
    SVN_TEST_OPTS_PASS(test_db_make_copy,
                       "test_db_make_copy"),
    SVN_TEST_OPTS_PASS(test_wc_move,
                       "test_wc_move"),
    SVN_TEST_OPTS_PASS(test_mixed_rev_copy,
                        "test_mixed_rev_copy"),
    SVN_TEST_OPTS_PASS(test_delete_of_replace,
                       "test_delete_of_replace"),
    SVN_TEST_OPTS_PASS(test_del_replace_not_present,
                       "test_del_replace_not_present"),
    SVN_TEST_OPTS_PASS(test_op_revert,
                       "test_op_revert"),
    SVN_TEST_OPTS_PASS(test_op_revert_changelist,
                       "test_op_revert_changelist"),
    SVN_TEST_OPTS_PASS(test_children_of_replaced_dir,
                       "test_children_of_replaced_dir"),
    SVN_TEST_OPTS_PASS(test_op_delete,
                       "test_op_delete"),
    SVN_TEST_OPTS_PASS(test_child_replace_with_same_origin,
                       "test_child_replace_with_same"),
    SVN_TEST_OPTS_PASS(test_shadowed_update,
                       "test_shadowed_update"),
    SVN_TEST_OPTS_PASS(test_copy_of_deleted,
                       "test_copy_of_deleted (issue #3873)"),
#ifndef DARWIN
    SVN_TEST_OPTS_PASS(test_case_rename,
                       "test_case_rename on case (in)sensitive system"),
#else
    /* apr doesn't implement APR_FILEPATH_TRUENAME for MAC OS yet */
    SVN_TEST_OPTS_XFAIL(test_case_rename,
                        "test_case_rename on case (in)sensitive system"),
#endif
    SVN_TEST_OPTS_PASS(commit_file_external,
                       "commit_file_external (issue #4002)"),
    SVN_TEST_OPTS_PASS(revert_file_externals,
                       "revert_file_externals"),
    SVN_TEST_OPTS_PASS(copy_file_externals,
                       "copy_file_externals"),
    SVN_TEST_OPTS_PASS(copy_wc_wc_server_excluded,
                       "test_wc_wc_copy_server_excluded"),
    SVN_TEST_OPTS_PASS(incomplete_switch,
                       "incomplete_switch (issue 4040)"),
    SVN_TEST_OPTS_PASS(nested_moves_child_first,
                       "nested_moves_child_first"),
    SVN_TEST_OPTS_PASS(nested_moves_child_last,
                       "nested_moves_child_last"),
    SVN_TEST_OPTS_PASS(move_in_copy,
                       "move_in_copy"),
    SVN_TEST_OPTS_PASS(move_in_replace,
                       "move_in_replace"),
    SVN_TEST_OPTS_PASS(copy_a_move,
                       "copy_a_move"),
    SVN_TEST_OPTS_PASS(move_to_swap,
                       "move_to_swap"),
    SVN_TEST_OPTS_PASS(revert_nested_move,
                       "revert_nested_move"),
    SVN_TEST_OPTS_PASS(move_on_move,
                       "move_on_move"),
    SVN_TEST_OPTS_PASS(move_on_move2,
                       "move_on_move2"),
    SVN_TEST_OPTS_PASS(move_added,
                       "move_added"),
    SVN_TEST_OPTS_PASS(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_WIMP(mixed_rev_move,
                       "mixed_rev_move",
                       "needs different libsvn_wc entry point"),
    SVN_TEST_OPTS_PASS(update_prop_mod_into_moved,
                       "update_prop_mod_into_moved"),
    SVN_TEST_OPTS_PASS(nested_move_update,
                       "nested_move_update"),
    SVN_TEST_OPTS_PASS(nested_move_commit,
                       "nested_move_commit (issue 4291)"),
    SVN_TEST_OPTS_PASS(nested_move_update2,
                       "nested_move_update2"),
    SVN_TEST_OPTS_PASS(move_update_conflicts,
                       "move_update_conflicts"),
    SVN_TEST_OPTS_PASS(move_update_delete_mods,
                       "move_update_delete_mods"),
    SVN_TEST_OPTS_PASS(nested_moves2,
                       "nested_moves2"),
    SVN_TEST_OPTS_PASS(move_in_delete,
                       "move_in_delete (issue 4303)"),
    SVN_TEST_OPTS_PASS(switch_move,
                       "switch_move"),
    SVN_TEST_OPTS_PASS(move_replace,
                       "move_replace"),
    SVN_TEST_OPTS_PASS(layered_moved_to,
                       "layered_moved_to"),
    SVN_TEST_OPTS_PASS(update_within_move,
                       "update_within_move"),
    SVN_TEST_OPTS_PASS(commit_moved_descendant,
                       "commit_moved_descendant"),
    SVN_TEST_OPTS_XFAIL(commit_moved_away_descendant,
                        "commit_moved_away_descendant"),
    SVN_TEST_OPTS_PASS(finite_move_update_bump,
                       "finite_move_update_bump"),
    SVN_TEST_OPTS_PASS(move_away_delete_update,
                       "move_away_delete_update"),
    SVN_TEST_OPTS_PASS(move_not_present_variants,
                       "move_not_present_variants"),
    SVN_TEST_OPTS_PASS(update_child_under_add,
                       "update_child_under_add (issue 4111)"),
    SVN_TEST_OPTS_PASS(delete_over_moved_away,
                       "delete_over_moved_away"),
    SVN_TEST_OPTS_PASS(movedto_opdepth,
                       "moved_to op_depth"),
    SVN_TEST_OPTS_PASS(new_basemove,
                       "new_basemove"),
    SVN_TEST_OPTS_PASS(move_back,
                       "move_back (issue 4302)"),
    SVN_TEST_OPTS_PASS(move_update_subtree,
                       "move_update_subtree (issue 4232)"),
    SVN_TEST_OPTS_PASS(move_parent_into_child,
                       "move_parent_into_child (issue 4333)"),
    SVN_TEST_OPTS_PASS(move_depth_expand,
                       "move depth expansion"),
    SVN_TEST_OPTS_XFAIL(move_retract,
                       "move retract (issue 4336)"),
    SVN_TEST_OPTS_XFAIL(move_delete_file_externals,
                       "move/delete file externals (issue 4293)"),
    SVN_TEST_OPTS_PASS(update_with_tree_conflict,
                       "update with tree conflict (issue 4347)"),
    SVN_TEST_OPTS_PASS(move_update_parent_replace,
                       "move update with replaced parent (issue 4388)"),
    SVN_TEST_OPTS_PASS(copy_mixed_rev_mods,
                       "copy mixed-rev with mods"),
    SVN_TEST_OPTS_PASS(move_child_to_parent_revert,
                       "move child to parent and revert (issue 4436)"),
    SVN_TEST_OPTS_PASS(move_delete_intermediate,
                       "move more than once, delete intermediate"),
    SVN_TEST_OPTS_XFAIL(move_revert_intermediate,
                       "move more than once, revert intermediate"),
    SVN_TEST_OPTS_PASS(move_replace_ancestor_with_child,
                       "move replace ancestor with child"),
    SVN_TEST_OPTS_PASS(move_twice_within_delete,
                       "move twice and then delete"),
    SVN_TEST_OPTS_PASS(del4_update_edit_AAA,
                       "del4: edit AAA"),
    SVN_TEST_OPTS_XFAIL(del4_update_delete_AAA,
                       "del4: delete AAA"),
    SVN_TEST_OPTS_XFAIL(del4_update_add_AAA,
                       "del4: add AAA"),
    SVN_TEST_OPTS_XFAIL(del4_update_replace_AAA,
                       "del4: replace AAA"),
    SVN_TEST_OPTS_PASS(del4_update_delself_AAA,
                       "del4: delete self AAA"),
    SVN_TEST_OPTS_XFAIL(del4_update_replaceself_AAA,
                       "del4: replace self AAA"),
    SVN_TEST_OPTS_PASS(move4_update_edit_AAA,
                       "move4: edit AAA"),
    SVN_TEST_OPTS_XFAIL(move4_update_delete_AAA,
                       "move4: delete AAA"),
    SVN_TEST_OPTS_XFAIL(move4_update_add_AAA,
                       "move4: add AAA"),
    SVN_TEST_OPTS_XFAIL(move4_update_replace_AAA,
                       "move4: replace AAA"),
    SVN_TEST_OPTS_PASS(move4_update_delself_AAA,
                       "move4: delete self AAA"),
    SVN_TEST_OPTS_XFAIL(move4_update_replaceself_AAA,
                       "move4: replace self AAA"),
    SVN_TEST_OPTS_PASS(simple_move_bump,
                       "simple move bump"),
    SVN_TEST_OPTS_PASS(movedhere_extract_retract,
                       "movedhere extract retract"),
    SVN_TEST_OPTS_PASS(repo_wc_copy,
                       "repo_wc_copy"),
    SVN_TEST_OPTS_PASS(break_move_in_delete,
                       "break move in delete (issue 4491)"),
    SVN_TEST_OPTS_PASS(nested_move_delete,
                       "nested move delete"),
    SVN_TEST_OPTS_XFAIL(move_within_mixed_move,
                        "move within mixed move"),
    SVN_TEST_OPTS_PASS(move_edit_obstruction,
                       "move edit obstruction"),
    SVN_TEST_OPTS_PASS(move_deep_bump,
                       "move deep bump"),
    SVN_TEST_OPTS_PASS(make_copy_mixed,
                       "make a copy of a mixed revision tree"),
    SVN_TEST_OPTS_PASS(make_copy_and_delete_mixed,
                       "make a copy of a mixed revision tree and del"),
    SVN_TEST_OPTS_PASS(test_global_commit,
                       "test global commit"),
    SVN_TEST_OPTS_PASS(test_global_commit_switched,
                       "test global commit switched"),
    SVN_TEST_NULL
  };

SVN_TEST_MAIN
