Author: julianfoad
Date: Tue Nov 11 17:54:22 2014
New Revision: 1638225

URL: http://svn.apache.org/r1638225
Log:
On the 'move-tracking-2' branch: Make copying work in 'svnmover'.

The main change is in compat3.c, translating a branch state change expressed
as element mappings into a sequence of Ev1 operations that includes copies.

This makes svnmover_tests.py 2 pass, and svnmover_tests.py 1 get quite a
long way.

* subversion/include/private/svn_editor3.h,
  subversion/libsvn_delta/editor3.c
  (svn_editor3_peg_path_equal): New.
  (svn_editor3_copy_one,
   svn_editor3_copy_tree,
   svn_editor3_cb_copy_one_t,
   svn_editor3_cb_copy_tree_t,
   wrap_copy_one,
   wrap_copy_tree):
    Correct the copy source argument to use a type that makes sense.
  (svn_branch_el_rev_id_create): New.

* subversion/libsvn_delta/compat3.c
  Many changes.

* subversion/svnmover/svnmover.c
  (VERIFY_REV_SPECIFIED): Correct the sense of the test.
  (execute): Track the change to svn_editor3_copy_tree().
  (usage,
   sub_main): Revert to taking the copy-from rev number as a separate
    argument.

* subversion/tests/cmdline/svnmover_tests.py
  (basic_svnmover): Avoid file names containing spaces. Give file names in
    plain text, not URI-encoded.

Modified:
    subversion/branches/move-tracking-2/subversion/include/private/svn_editor3.h
    subversion/branches/move-tracking-2/subversion/libsvn_delta/compat3.c
    subversion/branches/move-tracking-2/subversion/libsvn_delta/editor3.c
    subversion/branches/move-tracking-2/subversion/svnmover/svnmover.c
    
subversion/branches/move-tracking-2/subversion/tests/cmdline/svnmover_tests.py

Modified: 
subversion/branches/move-tracking-2/subversion/include/private/svn_editor3.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/include/private/svn_editor3.h?rev=1638225&r1=1638224&r2=1638225&view=diff
==============================================================================
--- 
subversion/branches/move-tracking-2/subversion/include/private/svn_editor3.h 
(original)
+++ 
subversion/branches/move-tracking-2/subversion/include/private/svn_editor3.h 
Tue Nov 11 17:54:22 2014
@@ -540,6 +540,12 @@ svn_editor3_peg_path_t
 svn_editor3_peg_path_dup(svn_editor3_peg_path_t old,
                          apr_pool_t *result_pool);
 
+/* Return true iff PEG_PATH1 and PEG_PATH2 are both the same location.
+ */
+svn_boolean_t
+svn_editor3_peg_path_equal(svn_editor3_peg_path_t *peg_path1,
+                           svn_editor3_peg_path_t *peg_path2);
+
 /** A reference to a node in a txn.
  *
  * @a peg gives a pegged location and @a peg.rev shall not be
@@ -579,6 +585,8 @@ typedef int svn_editor3_eid_t;
  */
 typedef struct svn_editor3_node_content_t svn_editor3_node_content_t;
 
+struct svn_branch_el_rev_id_t;
+
 /** The kind of the checksum to be used throughout the #svn_editor3_t APIs.
  */
 #define SVN_EDITOR3_CHECKSUM_KIND svn_checksum_sha1
@@ -932,7 +940,9 @@ svn_editor3_put(svn_editor3_t *editor,
  *     single-element copy supports arbitrary copy-and-modify operations,
  *     and tree-copy can be used for any unmodified subtrees therein.
  *     There is no need to reference the root element of a tree-copy again
- *     within the same edit, and so no id is provided.
+ *     within the same edit, and so no id is provided. [### Or maybe there
+ *     is such a need, when performing the same copy in multiple branches;
+ *     but in that case the caller would need to specify the new eids.]
  */
 
 /** Create a new element (versioned object) of kind @a new_kind.
@@ -982,9 +992,9 @@ svn_editor3_instantiate(svn_editor3_t *e
  * Assign the target element a locally unique element-id, @a local_eid,
  * with which it can be referenced within this edit.
  *
- * Copy from the source element at @a src_revision, @a src_eid.
+ * Copy from the source element at @a src_el_rev.
  * <SVN_EDITOR3_WITH_COPY_FROM_THIS_REV>
- * If @a src_revision is #SVN_INVALID_REVNUM, it means copy from within
+ * If @a src_el_rev->rev is #SVN_INVALID_REVNUM, it means copy from within
  * the new revision being described.
  *   ### See note on copy_tree().
  * </SVN_EDITOR3_WITH_COPY_FROM_THIS_REV>
@@ -1005,8 +1015,7 @@ svn_editor3_instantiate(svn_editor3_t *e
 svn_error_t *
 svn_editor3_copy_one(svn_editor3_t *editor,
                      svn_editor3_eid_t local_eid,
-                     svn_revnum_t src_revision,
-                     svn_editor3_eid_t src_eid,
+                     const struct svn_branch_el_rev_id_t *src_el_rev,
                      svn_editor3_eid_t new_parent_eid,
                      const char *new_name,
                      const svn_editor3_node_content_t *new_content);
@@ -1022,9 +1031,9 @@ svn_editor3_copy_one(svn_editor3_t *edit
  * Set the target root element's parent and name to @a new_parent_eid and
  * @a new_name.
  *
- * Copy from the source element at @a src_revision, @a src_eid.
+ * Copy from the source subtree at @a src_el_rev.
  * <SVN_EDITOR3_WITH_COPY_FROM_THIS_REV>
- * If @a src_revision is #SVN_INVALID_REVNUM, it means copy from within
+ * If @a src_el_rev->rev is #SVN_INVALID_REVNUM, it means copy from within
  * the new revision being described. In this case the subtree copied is
  * the FINAL subtree as committed, regardless of the order in which the
  * edit operations are described.
@@ -1043,8 +1052,7 @@ svn_editor3_copy_one(svn_editor3_t *edit
  */
 svn_error_t *
 svn_editor3_copy_tree(svn_editor3_t *editor,
-                      svn_revnum_t src_revision,
-                      svn_editor3_eid_t src_eid,
+                      const struct svn_branch_el_rev_id_t *src_el_rev,
                       svn_editor3_eid_t new_parent_eid,
                       const char *new_name);
 
@@ -1251,8 +1259,7 @@ typedef svn_error_t *(*svn_editor3_cb_in
 typedef svn_error_t *(*svn_editor3_cb_copy_one_t)(
   void *baton,
   svn_editor3_eid_t local_eid,
-  svn_revnum_t src_revision,
-  svn_editor3_eid_t src_eid,
+  const struct svn_branch_el_rev_id_t *src_el_rev,
   svn_editor3_eid_t new_parent_eid,
   const char *new_name,
   const svn_editor3_node_content_t *new_content,
@@ -1262,8 +1269,7 @@ typedef svn_error_t *(*svn_editor3_cb_co
  */
 typedef svn_error_t *(*svn_editor3_cb_copy_tree_t)(
   void *baton,
-  svn_revnum_t src_revision,
-  svn_editor3_eid_t src_eid,
+  const struct svn_branch_el_rev_id_t *src_el_rev,
   svn_editor3_eid_t new_parent_eid,
   const char *new_name,
   apr_pool_t *scratch_pool);
@@ -1692,6 +1698,15 @@ typedef struct svn_branch_el_rev_id_t
 
 } svn_branch_el_rev_id_t;
 
+/* Return a new el_rev_id object constructed with *shallow* copies of BRANCH,
+ * EID and REV, allocated in RESULT_POOL.
+ */
+svn_branch_el_rev_id_t *
+svn_branch_el_rev_id_create(svn_branch_instance_t *branch,
+                            int eid,
+                            svn_revnum_t rev,
+                            apr_pool_t *result_pool);
+
 /* The content (parent, name and node-content) of an element-revision.
  * In other words, an el-rev node in a (mixed-rev) directory-tree.
  */
@@ -1716,8 +1731,7 @@ svn_branch_el_rev_content_create(svn_edi
                                  const svn_editor3_node_content_t 
*node_content,
                                  apr_pool_t *result_pool);
 
-/* Return a new content object constructed with a deep copy of OLD,
- * allocated in RESULT_POOL.
+/* Return a deep copy of OLD, allocated in RESULT_POOL.
  */
 svn_branch_el_rev_content_t *
 svn_branch_el_rev_content_dup(const svn_branch_el_rev_content_t *old,

Modified: subversion/branches/move-tracking-2/subversion/libsvn_delta/compat3.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_delta/compat3.c?rev=1638225&r1=1638224&r2=1638225&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_delta/compat3.c 
(original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_delta/compat3.c Tue 
Nov 11 17:54:22 2014
@@ -41,6 +41,17 @@
 /* Verify EXPR is true; raise an error if not. */
 #define VERIFY(expr) SVN_ERR_ASSERT(expr)
 
+#ifdef SVN_DEBUG
+/* Return a human-readable string representation of LOC. */
+static const char *
+peg_path_str(svn_editor3_peg_path_t loc,
+             apr_pool_t *result_pool)
+{
+  return apr_psprintf(result_pool, "%s@%ld",
+                      loc.relpath, loc.rev);
+}
+#endif
+
 /*
  * ========================================================================
  * Configuration Options
@@ -1858,6 +1869,20 @@ svn_branch_instance_create(svn_branch_si
   return b;
 }
 
+svn_branch_el_rev_id_t *
+svn_branch_el_rev_id_create(svn_branch_instance_t *branch,
+                            int eid,
+                            svn_revnum_t rev,
+                            apr_pool_t *result_pool)
+{
+  svn_branch_el_rev_id_t *id = apr_palloc(result_pool, sizeof(*id));
+
+  id->branch = branch;
+  id->eid = eid;
+  id->rev = rev;
+  return id;
+}
+
 svn_branch_el_rev_content_t *
 svn_branch_el_rev_content_create(svn_editor3_eid_t parent_eid,
                                  const char *name,
@@ -3017,45 +3042,50 @@ branch_branch_subtree_r(svn_branch_insta
 }
 
 /* Adjust TO_BRANCH and its subbranches (recursively), to reflect a copy
- * of a subtree from FROM_BRANCH:FROM_EID to TO_PARENT_EID:TO_NAME.
+ * of a subtree from FROM_EL_REV to TO_PARENT_EID:TO_NAME.
+ *
+ * FROM_EL_REV must be an existing element. (It may be a branch root.)
  *
- * FROM_EID must be an existing element of FROM_BRANCH. (It may be the root.)
- * If FROM_EID is the root of a subbranch and/or contains nested
- * subbranches, also copy them (by branching).
+ * ### TODO:
+ * If FROM_EL_REV is the root of a subbranch and/or contains nested
+ * subbranches, also copy them ...
+ * ### What shall we do with a subbranch? Make plain copies of its raw
+ *     elements; make a subbranch by branching the source subbranch in
+ *     cases where possible; make a subbranch in a new family?
  *
  * TO_PARENT_EID must be a directory element in TO_BRANCH, and TO_NAME a
  * non-existing path in it.
  */
 static svn_error_t *
-branch_copy_subtree_r(svn_branch_instance_t *from_branch,
-                      svn_editor3_eid_t from_eid,
+branch_copy_subtree_r(const svn_branch_el_rev_id_t *from_el_rev,
                       svn_branch_instance_t *to_branch,
                       svn_editor3_eid_t to_parent_eid,
                       const char *to_name,
                       apr_pool_t *scratch_pool)
 {
+  int to_eid;
+  svn_branch_el_rev_content_t *old_content;
+
   /* Copy the root element */
-  {
-    int to_eid = family_add_new_element(to_branch->sibling_defn->family);
-    svn_branch_el_rev_content_t *old_content = branch_map_get(from_branch, 
from_eid);
+  to_eid = family_add_new_element(to_branch->sibling_defn->family);
+  old_content = branch_map_get(from_el_rev->branch, from_el_rev->eid);
 
-    /* ### If this element is a subbranch root, need to call
-           branch_map_update_as_subbranch_root() instead. */
-    branch_map_update(to_branch, to_eid,
-                      to_parent_eid, to_name, old_content->content);
-  }
+  /* ### If this element is a subbranch root, need to call
+         branch_map_update_as_subbranch_root() instead. */
+  branch_map_update(to_branch, to_eid,
+                    to_parent_eid, to_name, old_content->content);
 
   /* Copy the children within this branch */
-  SVN_ERR(branch_map_copy_children(from_branch, from_eid,
-                                   to_branch, to_parent_eid,
+  SVN_ERR(branch_map_copy_children(from_el_rev->branch, from_el_rev->eid,
+                                   to_branch, to_eid,
                                    scratch_pool));
 
   /* handle any subbranches under FROM_BRANCH:FROM_EID */
   /* ### Later. */
-  /* ### What shall we do with a subbranch? Make plain copies of its raw
-         elements; make a subbranch by branching the source subbranch in
-         cases where possible; make a subbranch in a new family? */
 
+  SVN_DBG(("cp subtree from e%d (%d/%s) to e%d (%d/%s)",
+           from_el_rev->eid, old_content->parent_eid, old_content->name,
+           to_eid, to_parent_eid, to_name));
   return SVN_NO_ERROR;
 }
 
@@ -3889,6 +3919,33 @@ editor3_put(void *baton,
  * ========================================================================
  */
 
+/*  */
+static svn_error_t *
+content_fetch(svn_editor3_node_content_t **content_p,
+              apr_hash_t **children_names,
+              ev3_from_delta_baton_t *eb,
+              const svn_editor3_peg_path_t *path_rev,
+              apr_pool_t *result_pool,
+              apr_pool_t *scratch_pool)
+{
+  svn_editor3_node_content_t *content
+    = apr_pcalloc(result_pool, sizeof (*content));
+
+  SVN_ERR(eb->fetch_func(&content->kind,
+                         &content->props,
+                         &content->text,
+                         children_names,
+                         eb->fetch_baton,
+                         path_rev->relpath, path_rev->rev,
+                         result_pool, scratch_pool));
+
+  SVN_ERR_ASSERT(content->kind == svn_node_dir
+                 || content->kind == svn_node_file);
+  if (content_p)
+    *content_p = content;
+  return SVN_NO_ERROR;
+}
+
 /* Get the content of BRANCH:EID, as fully resolved content (not as a
  * reference). BRANCH:EID must not be a subbranch root.
  *
@@ -3914,32 +3971,24 @@ svn_branch_el_rev_get(svn_branch_el_rev_
   /* If content is by reference, fetch full content. */
   if (node && (node->content->ref.relpath))
     {
-      svn_editor3_node_content_t *content
-        = apr_pcalloc(result_pool, sizeof(*content));
-
-      SVN_ERR(eb->fetch_func(&content->kind,
-                             &content->props,
-                             &content->text, NULL,
-                             eb->fetch_baton,
-                             node->content->ref.relpath, 
node->content->ref.rev,
-                             result_pool, scratch_pool));
-      node->content = content;
+      SVN_ERR(content_fetch(&node->content, NULL,
+                            eb, &node->content->ref,
+                            result_pool, scratch_pool));
     }
 
   *node_p = node;
   return SVN_NO_ERROR;
 }
 
-svn_error_t *
-svn_editor3_find_el_rev_by_path_rev(svn_branch_el_rev_id_t **el_rev_p,
-                                    svn_editor3_t *editor,
-                                    const char *rrpath,
-                                    svn_revnum_t revnum,
-                                    apr_pool_t *result_pool,
-                                    apr_pool_t *scratch_pool)
+/*  */
+static svn_error_t *
+repos_find_el_rev_by_path_rev(svn_branch_el_rev_id_t **el_rev_p,
+                              const char *rrpath,
+                              svn_revnum_t revnum,
+                              const svn_branch_repos_t *repos,
+                              apr_pool_t *result_pool,
+                              apr_pool_t *scratch_pool)
 {
-  ev3_from_delta_baton_t *eb = svn_editor3__get_baton(editor);
-  const svn_branch_repos_t *repos = eb->edited_rev_root->repos;
   svn_branch_el_rev_id_t *el_rev = apr_palloc(result_pool, sizeof(*el_rev));
   const svn_branch_revision_root_t *rev_root;
 
@@ -3957,6 +4006,22 @@ svn_editor3_find_el_rev_by_path_rev(svn_
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_editor3_find_el_rev_by_path_rev(svn_branch_el_rev_id_t **el_rev_p,
+                                    svn_editor3_t *editor,
+                                    const char *rrpath,
+                                    svn_revnum_t revnum,
+                                    apr_pool_t *result_pool,
+                                    apr_pool_t *scratch_pool)
+{
+  ev3_from_delta_baton_t *eb = svn_editor3__get_baton(editor);
+  const svn_branch_repos_t *repos = eb->edited_rev_root->repos;
+
+  SVN_ERR(repos_find_el_rev_by_path_rev(el_rev_p, rrpath, revnum, repos,
+                                        result_pool, scratch_pool));
+  return SVN_NO_ERROR;
+}
+
 void
 svn_editor3_find_branch_element_by_rrpath(svn_branch_instance_t **branch_p,
                                           int *eid_p,
@@ -4102,8 +4167,7 @@ editor3_instantiate(void *baton,
 static svn_error_t *
 editor3_copy_one(void *baton,
                  svn_editor3_eid_t eid,
-                 svn_revnum_t src_revision,
-                 svn_editor3_eid_t src_eid,
+                 const struct svn_branch_el_rev_id_t *src_el_rev,
                  svn_editor3_eid_t new_parent_eid,
                  const char *new_name,
                  const svn_editor3_node_content_t *new_content,
@@ -4122,25 +4186,20 @@ editor3_copy_one(void *baton,
 /* An #svn_editor3_t method. */
 static svn_error_t *
 editor3_copy_tree(void *baton,
-                  svn_revnum_t src_revision,
-                  svn_editor3_eid_t src_eid,
+                  const svn_branch_el_rev_id_t *src_el_rev,
                   svn_editor3_eid_t new_parent_eid,
                   const char *new_name,
                   apr_pool_t *scratch_pool)
 {
-#if 0
   ev3_from_delta_baton_t *eb = baton;
-  svn_branch_instance_t *src_branch /* = ### */;
   svn_branch_instance_t *to_branch = eb->edited_branch;
 
   SVN_DBG(("copy_tree(e%d -> e%d/%s)",
-           /*branch->sibling_defn->bid,*/
-           src_eid, new_parent_eid, new_name));
+           src_el_rev->eid, new_parent_eid, new_name));
 
-  SVN_ERR(branch_copy_subtree_r(src_branch, src_eid,
+  SVN_ERR(branch_copy_subtree_r(src_el_rev,
                                 to_branch, new_parent_eid, new_name,
                                 scratch_pool));
-#endif
 
   return SVN_NO_ERROR;
 }
@@ -4195,23 +4254,12 @@ editor3_alter(void *baton,
   return SVN_NO_ERROR;
 }
 
-/*  */
-typedef struct initial_final_t
-{
-  svn_branch_instance_t *branch;
-  int eid;
-  svn_revnum_t rev;
-  svn_branch_el_rev_content_t *node;
-} initial_final_t;
-
-/* Update *PATHS, a hash of (rrpath -> initial_final_t[2]), creating or
- * filling in entries for all elements in BRANCH, at index [I] in the
- * initial_final_t array.
+/* Update *PATHS, a hash of (rrpath -> svn_branch_el_rev_id_t),
+ * creating or filling in entries for all elements in BRANCH.
  */
 static void
 convert_branch_to_paths(apr_hash_t *paths,
                         svn_branch_instance_t *branch,
-                        int i,
                         apr_pool_t *result_pool,
                         apr_pool_t *scratch_pool)
 {
@@ -4222,53 +4270,43 @@ convert_branch_to_paths(apr_hash_t *path
        hi; hi = apr_hash_next(hi))
     {
       int eid = *(const int *)apr_hash_this_key(hi);
-      svn_branch_el_rev_content_t *node = apr_hash_this_val(hi);
       const char *relpath = branch_map_get_path_by_eid(branch, eid, 
result_pool);
       const char *rrpath = svn_relpath_join(svn_branch_get_root_rrpath(branch),
                                             relpath, result_pool);
-      initial_final_t *ba = svn_hash_gets(paths, rrpath);
-
-      if (! ba)
-        {
-          ba = apr_pcalloc(result_pool, 2 * sizeof(*ba));
-          ba[0].eid = -1;
-          ba[1].eid = -1;
-          ba[0].rev = SVN_INVALID_REVNUM;
-          ba[1].rev = SVN_INVALID_REVNUM;
-          svn_hash_sets(paths, rrpath, ba);
-        }
+      svn_branch_el_rev_id_t *ba = svn_hash_gets(paths, rrpath);
 
       /* Fill in the details. If it's already been filled in, then let a
          branch-root element override a sub-branch element of an outer
          branch, because the branch-root element is the one that should
          be specifying the element's content.
        */
-      if (! ba[i].branch
+      if (! ba
           || eid == branch->sibling_defn->root_eid)
         {
-          ba[i].branch = branch;
-          ba[i].eid = eid;
-          ba[i].rev = branch->rev_root->rev;
-          ba[i].node = node;
+          ba = svn_branch_el_rev_id_create(branch, eid, branch->rev_root->rev,
+                                           result_pool);
+          svn_hash_sets(paths, rrpath, ba);
           /*SVN_DBG(("branch-to-path[%d]: b%d e%d -> %s",
                    i, branch->sibling_defn->bid, eid, rrpath));*/
         }
       else
         {
-          /*SVN_DBG(("branch-to-path[%d]: b%d e%d -> <already present; not 
overwriting> (%s)",
-                   i, branch->sibling_defn->bid, eid, rrpath));*/
+          SVN_DBG(("branch-to-path: b%d e%d -> <already present; not 
overwriting> (%s)",
+                   branch->sibling_defn->bid, eid, rrpath));
         }
     }
 }
 
-/* Update *PATHS_UNION, a hash of (rrpath -> initial_final_t[2]), creating or
- * filling in entries for all elements in all branches at and under BRANCH,
- * recursively, at index [I] in the initial_final_t array.
+/* Produce a mapping from paths to element ids, covering all elements in
+ * BRANCH and all its sub-branches, recursively.
+ *
+ * Update *PATHS_UNION, a hash of (rrpath -> svn_branch_el_rev_id_t),
+ * creating or filling in entries for all elements in all branches at and
+ * under BRANCH, recursively.
  */
 static void
 convert_branch_to_paths_r(apr_hash_t *paths_union,
                           svn_branch_instance_t *branch,
-                          int idx,
                           apr_pool_t *result_pool,
                           apr_pool_t *scratch_pool)
 {
@@ -4277,7 +4315,7 @@ convert_branch_to_paths_r(apr_hash_t *pa
 
   /*SVN_DBG(("[%d] branch={b%de%d at '%s'}", idx,
            branch->sibling_defn->bid, branch->sibling_defn->root_eid, 
branch->branch_root_rrpath));*/
-  convert_branch_to_paths(paths_union, branch, idx,
+  convert_branch_to_paths(paths_union, branch,
                           result_pool, scratch_pool);
 
   /* Rercurse into sub-branches */
@@ -4287,149 +4325,380 @@ convert_branch_to_paths_r(apr_hash_t *pa
     {
       svn_branch_instance_t *b = APR_ARRAY_IDX(sub_branches, i, void *);
 
-      convert_branch_to_paths_r(paths_union, b, idx, result_pool, 
scratch_pool);
+      convert_branch_to_paths_r(paths_union, b, result_pool, scratch_pool);
     }
 }
 
-/*
- * Drive svn_delta_editor_t (actions: add/copy/delete/modify) from
- * a before-and-after element mapping.
+/* Return TRUE iff INITIAL_CONTENT and FINAL_CONTENT are both non-null
+ * and have the same properties.
+ */
+static svn_boolean_t
+props_equal(svn_editor3_node_content_t *initial_content,
+            svn_editor3_node_content_t *final_content,
+            apr_pool_t *scratch_pool)
+{
+  apr_array_header_t *prop_diffs;
+
+  if (!initial_content || !final_content)
+    return FALSE;
+
+  svn_error_clear(svn_prop_diffs(&prop_diffs,
+                                 initial_content->props,
+                                 final_content->props,
+                                 scratch_pool));
+  return (prop_diffs->nelts == 0);
+}
+
+/* Return TRUE iff INITIAL_CONTENT and FINAL_CONTENT are both file content
+ * and have the same text.
+ */
+static svn_boolean_t
+text_equal(svn_editor3_node_content_t *initial_content,
+           svn_editor3_node_content_t *final_content)
+{
+  if (!initial_content || !final_content
+      || initial_content->kind != svn_node_file
+      || final_content->kind != svn_node_file)
+    {
+      return FALSE;
+    }
+
+  return svn_stringbuf_compare(initial_content->text,
+                               final_content->text);
+}
+
+/* Return the copy-from location to be used if this is to be a copy;
+ * otherwise return NULL.
+ *
+ * ### Currently this is indicated by content-by-reference, which is
+ *     an inadequate indication.
+ */
+static svn_editor3_peg_path_t *
+get_copy_from(svn_editor3_node_content_t *final_content)
+{
+  if (final_content->ref.relpath)
+    {
+      return &final_content->ref;
+    }
+  return NULL;
+}
+
+/* Return a hash whose keys are the names of the immediate children of
+ * RRPATH in PATHS.
+ */
+static apr_hash_t *
+get_immediate_children_names(apr_hash_t *paths,
+                             const char *parent_rrpath,
+                             apr_pool_t *result_pool,
+                             apr_pool_t *scratch_pool)
+{
+  apr_hash_t *children = apr_hash_make(result_pool);
+  apr_hash_index_t *hi;
+
+  for (hi = apr_hash_first(scratch_pool, paths); hi; hi = apr_hash_next(hi))
+    {
+      const char *this_rrpath = apr_hash_this_key(hi);
+
+      if (this_rrpath[0]
+          && strcmp(parent_rrpath, svn_relpath_dirname(this_rrpath,
+                                                       scratch_pool)) == 0)
+        {
+          svn_hash_sets(children,
+                        svn_relpath_basename(this_rrpath, result_pool), "");
+        }
+    }
+
+  return children;
+}
+
+/* Return true iff EL_REV1 and EL_REV2 identify the same branch-family
+ * and element.
+ */
+static svn_boolean_t
+same_family_and_element(const svn_branch_el_rev_id_t *el_rev1,
+                        const svn_branch_el_rev_id_t *el_rev2)
+{
+  if (el_rev1->branch->sibling_defn->family->fid
+      != el_rev2->branch->sibling_defn->family->fid)
+    return FALSE;
+  if (el_rev1->eid != el_rev2->eid)
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Generate Ev1 instructions to edit from a current state to a final state
+ * at RRPATH, recursing for child paths of RRPATH.
+ *
+ * The current state at RRPATH might not be the initial state because,
+ * although neither RRPATH nor any sub-paths have been explicitly visited
+ * before, the current state at RRPATH and its sub-paths might be the
+ * result of a copy.
+ *
+ * PRED_LOC is the predecessor location of the node currently at RRPATH in
+ * the Ev1 transaction, or NULL if there is no node currently at RRPATH.
+ * If the node is copied, including a child of a copy, this is its copy-from
+ * location, otherwise this is its location in the txn base revision.
+ * (The current node cannot be a plain added node on entry to this function,
+ * as the function must be called only once for each path and there is no
+ * recursive add operation.) PRED_LOC identifies the node content that the
+ * that the Ev1 edit needs to delete, replace, update or leave unchanged.
+ *
  */
 static svn_error_t *
-drive_changes_branch(ev3_from_delta_baton_t *eb,
-                     apr_pool_t *scratch_pool)
+drive_changes_r(const char *rrpath,
+                svn_editor3_peg_path_t *pred_loc,
+                apr_hash_t *paths_final,
+                ev3_from_delta_baton_t *eb,
+                apr_pool_t *scratch_pool)
 {
-  svn_branch_revision_root_t *base_rev_root;
-  apr_hash_t *paths_union;
-  apr_array_header_t *sorted_paths;
-  const apr_array_header_t *paths;
-  int i;
+  /* The el-rev-id of the element that will finally exist at RRPATH. */
+  svn_branch_el_rev_id_t *final_el_rev = svn_hash_gets(paths_final, rrpath);
+  svn_editor3_node_content_t *final_content;
+  svn_editor3_peg_path_t *final_copy_from;
+  svn_boolean_t succession;
+
+  SVN_DBG(("rrpath '%s' current=%s, final=e%d)",
+           rrpath,
+           pred_loc ? peg_path_str(*pred_loc, scratch_pool) : "<nil>",
+           final_el_rev ? final_el_rev->eid : -1));
+
+  SVN_ERR_ASSERT(!pred_loc || (pred_loc->relpath && 
SVN_IS_VALID_REVNUM(pred_loc->rev)));
+  /* A non-null FINAL address means an element exists there. */
+  SVN_ERR_ASSERT(!final_el_rev || branch_map_get(final_el_rev->branch, 
final_el_rev->eid));
+
+  if (final_el_rev)
+    {
+      final_content
+        = branch_map_get(final_el_rev->branch, final_el_rev->eid)->content;
+
+      /* Decide whether the state at this path should be a copy (incl. a
+         copy-child) */
+      final_copy_from = get_copy_from(final_content);
+      /* It doesn't make sense to have a non-copy inside a copy */
+      /*SVN_ERR_ASSERT(!(parent_is_copy && !final_copy_from));*/
+   }
+  else
+    {
+      final_content = NULL;
+      final_copy_from = NULL;
+    }
 
-  /* Find the initial branching state.
-     ### For now, assume single-rev state based on youngest known rev. */
-  {
-    int base_rev = eb->edited_rev_root->repos->rev_roots->nelts - 1;
-    base_rev_root = APR_ARRAY_IDX(eb->edited_rev_root->repos->rev_roots,
-                                  base_rev, void *);
-  }
+  /* Succession means:
+       for a copy (inc. child)  -- copy-from same place as natural predecessor
+       otherwise                -- it's succession if it's the same element
+                                   (which also implies the same kind) */
+  if (pred_loc && final_copy_from)
+    {
+      succession = svn_editor3_peg_path_equal(pred_loc, final_copy_from);
+    }
+  else if (pred_loc && final_el_rev)
+    {
+      svn_branch_el_rev_id_t *pred_el_rev;
 
-  /* Convert the element mappings to an svn_delta_editor_t traversal.
+      SVN_ERR(repos_find_el_rev_by_path_rev(&pred_el_rev,
+                                            pred_loc->relpath, pred_loc->rev,
+                                            eb->edited_rev_root->repos,
+                                            scratch_pool, scratch_pool));
 
-        1. find union of paths in initial and final states, across all 
branches.
-        2. traverse paths in depth-first order.
-        3. modify/delete/add/replace as needed at each path.
-   */
-  paths_union = apr_hash_make(scratch_pool);
-  convert_branch_to_paths_r(paths_union, base_rev_root->root_branch, 0,
-                            scratch_pool, scratch_pool);
-  convert_branch_to_paths_r(paths_union, eb->edited_rev_root->root_branch, 1,
-                            scratch_pool, scratch_pool);
+      succession = same_family_and_element(pred_el_rev, final_el_rev);
+    }
+  else
+    {
+      succession = FALSE;
+    }
 
-  sorted_paths = svn_sort__hash(paths_union, svn_sort_compare_items_as_paths,
-                                scratch_pool);
-  for (i = 0; i < sorted_paths->nelts; i++)
-    {
-      svn_sort__item_t *item = &APR_ARRAY_IDX(sorted_paths, i, 
svn_sort__item_t);
-      const char *rrpath = item->key;
-      initial_final_t *ba = item->value, *initial = &ba[0], *final = &ba[1];
-
-      /* If there's an initial node that isn't also going to be the final
-         node, it's being deleted or replaced: delete it. */
-      if (initial->node
-          && (! final->node || final->eid != initial->eid))
-        {
-          /* Issue an Ev1 delete unless this path is inside a path at which
-             we've already issued a delete. */
-          if (check_existence(eb->changes, rrpath) != svn_tristate_false)
-            {
-              /*SVN_DBG(("ev1:del(%s)", rrpath));*/
-              SVN_ERR(delete_subtree(eb->changes, rrpath, initial->rev));
-            }
-          /*else
-            SVN_DBG(("ev1:del(%s): parent is already deleted", rrpath));*/
+  /* If there's an initial node that isn't also going to be the final
+     node at this path, then it's being deleted or replaced: delete it. */
+  if (pred_loc && !succession)
+    {
+      /* Issue an Ev1 delete unless this path is inside a path at which
+         we've already issued a delete. */
+      if (check_existence(eb->changes, rrpath) != svn_tristate_false)
+        {
+          SVN_DBG(("ev1:del(%s)", rrpath));
+          /* ### We don't need "delete_subtree", we only need to insert a
+             single delete operation, as we know we haven't
+             inserted any changes inside this subtree. */
+          SVN_ERR(delete_subtree(eb->changes, rrpath, pred_loc->rev));
+        }
+      else
+        SVN_DBG(("ev1:del(%s): parent is already deleted", rrpath));
+    }
+
+  /* If there's a final node, it's being added or modified.
+     Or it's unchanged -- we do nothing in that case. */
+  if (final_el_rev)
+    {
+      svn_editor3_node_content_t *current_content = NULL;
+      apr_hash_t *current_children = NULL;
+      change_node_t *change = NULL;
+
+      /* Get the full content of the final node. If we have
+         only a reference to the content, fetch it in full. */
+      SVN_ERR_ASSERT(final_content);
+      if (final_content->ref.relpath)
+        {
+          /* Get content by reference. */
+          SVN_ERR(content_fetch(&final_content, NULL,
+                                eb, &final_content->ref,
+                                scratch_pool, scratch_pool));
         }
 
-      /* If there's a final node, it's being added or modified.
-         ### Or it's unchanged -- we should do nothing in that case. */
-      if (final->node)
+      /* If the final node was also the initial node, it's being
+         modified, otherwise it's being added (perhaps a replacement). */
+      if (succession)
         {
-          svn_editor3_node_content_t *initial_content = NULL;
-          svn_editor3_node_content_t *final_content = final->node->content;
-          change_node_t *change;
+          /* Get full content of the current node */
+          SVN_ERR(content_fetch(&current_content, &current_children,
+                                eb, pred_loc,
+                                scratch_pool, scratch_pool));
 
-          /* Get the full content of the initial node, if there was one. */
-          initial_content = initial->node ? initial->node->content : NULL;
-          if (initial_content && initial_content->ref.relpath)
+          /* If no changes to make, then skip this path */
+          if (svn_editor3_node_content_equal(current_content,
+                                             final_content, scratch_pool))
             {
-              /* Get content by reference. */
-              SVN_ERR(eb->fetch_func(&initial_content->kind,
-                                     &initial_content->props,
-                                     &initial_content->text, NULL,
-                                     eb->fetch_baton,
-                                     initial_content->ref.relpath,
-                                     initial_content->ref.rev,
-                                     scratch_pool, scratch_pool));
-              SVN_ERR_ASSERT(initial_content->kind == svn_node_dir
-                             || initial_content->kind == svn_node_file);
-              initial_content->ref.relpath = NULL;
+              SVN_DBG(("ev1:no-op(%s)", rrpath));
             }
-          /* Get the full content of the final node. If we have
-             only a reference to the content, fetch it in full. */
-          SVN_ERR_ASSERT(final_content);
-          if (final_content->ref.relpath)
-            {
-              /* Get content by reference. */
-              SVN_ERR(eb->fetch_func(&final_content->kind,
-                                     &final_content->props,
-                                     &final_content->text, NULL,
-                                     eb->fetch_baton,
-                                     final_content->ref.relpath,
-                                     final_content->ref.rev,
-                                     scratch_pool, scratch_pool));
-              SVN_ERR_ASSERT(final_content->kind == svn_node_dir
-                             || final_content->kind == svn_node_file);
-              final_content->ref.relpath = NULL;
-            }
-
-          /* If the final node was also the initial node, it's being
-             modified, otherwise it's being added (perhaps a replacement). */
-          if (initial->node
-              /* && same branch family */
-              && (final->eid == initial->eid))
+          else
             {
-              /* If no changes to make, then skip this path */
-              if (svn_editor3_node_content_equal(initial->node->content,
-                                                 final_content, scratch_pool))
-                {
-                  /*SVN_DBG(("ev1:no-op(%s)", rrpath));*/
-                  continue;
-                }
-
-              /*SVN_DBG(("ev1:mod(%s)", rrpath));*/
+              SVN_DBG(("ev1:mod(%s)", rrpath));
               SVN_ERR(insert_change(&change, eb->changes, rrpath,
                                     RESTRUCTURE_NONE));
-              change->changing_rev = initial->rev;
+              change->changing_rev = pred_loc->rev;
             }
-          else
+        }
+      else /* add or copy/move */
+        {
+          SVN_DBG(("ev1:add(%s)", rrpath));
+          SVN_ERR(insert_change(&change, eb->changes, rrpath,
+                                RESTRUCTURE_ADD));
+
+          /* If content is to be copied (and possibly modified) ... */
+          if (final_copy_from)
             {
-              /*SVN_DBG(("ev1:add(%s)", rrpath));*/
-              SVN_ERR(insert_change(&change, eb->changes, rrpath,
-                                    RESTRUCTURE_ADD));
-              /* ### TODO: copy from */
+              change->copyfrom_rev = final_copy_from->rev;
+              change->copyfrom_path = final_copy_from->relpath;
+
+              /* Get full content of the copy source node */
+              SVN_ERR(content_fetch(&current_content, &current_children,
+                                    eb, final_copy_from,
+                                    scratch_pool, scratch_pool));
             }
+        }
 
-          /* Copy the required content into the change record. */
+      if (change)
+        {
+          /* Copy the required content into the change record. Avoid no-op
+             changes of props / text, not least to minimize clutter when
+             debugging Ev1 operations. */
           SVN_ERR_ASSERT(final_content->kind == svn_node_dir
                          || final_content->kind == svn_node_file);
           change->kind = final_content->kind;
-          change->props = final_content->props;
-          if (final_content->kind == svn_node_file)
+          if (!props_equal(current_content, final_content, scratch_pool))
+            {
+              change->props = final_content->props;
+            }
+          if (final_content->kind == svn_node_file
+              && !text_equal(current_content, final_content))
             {
               change->contents_text = final_content->text;
             }
         }
+
+      /* Recurse to process this directory's children */
+      if (final_content->kind == svn_node_dir)
+        {
+          apr_hash_t *final_children;
+          apr_hash_t *union_children;
+          apr_hash_index_t *hi;
+
+          final_children = get_immediate_children_names(paths_final, rrpath,
+                                                        scratch_pool,
+                                                        scratch_pool);
+          union_children = (current_children
+                            ? apr_hash_overlay(scratch_pool, current_children,
+                                               final_children)
+                            : final_children);
+          for (hi = apr_hash_first(scratch_pool, union_children);
+               hi; hi = apr_hash_next(hi))
+            {
+              const char *name = apr_hash_this_key(hi);
+              const char *this_rrpath = svn_relpath_join(rrpath, name,
+                                                         scratch_pool);
+              svn_boolean_t child_in_current
+                = current_children && svn_hash_gets(current_children, name);
+              svn_editor3_peg_path_t *child_pred = NULL;
+
+              if (child_in_current)
+                {
+                 /* If the parent dir is copied, then this child has been
+                    copied along with it: predecessor is parent's copy-from
+                    location extended by the child's name. */
+                  child_pred = apr_palloc(scratch_pool, sizeof(*child_pred));
+                  if (final_copy_from)
+                    {
+                      child_pred->rev = final_copy_from->rev;
+                      child_pred->relpath
+                        = svn_relpath_join(final_copy_from->relpath, name,
+                                           scratch_pool);
+                    }
+                  else
+                    {
+                      child_pred->rev = pred_loc->rev;
+                      child_pred->relpath = this_rrpath;
+                    }
+               }
+              SVN_DBG(("child '%s' current=%s final? %d%s",
+                       name,
+                       child_pred ? peg_path_str(*child_pred, scratch_pool)
+                                  : "<nil>",
+                       (svn_hash_gets(final_children, name) != NULL),
+                       final_copy_from
+                         ? apr_psprintf(scratch_pool, " parent-cp-from=%s@%ld",
+                                        final_copy_from->relpath,
+                                        final_copy_from->rev) : ""));
+
+              SVN_ERR(drive_changes_r(this_rrpath,
+                                      child_pred,
+                                      paths_final, eb, scratch_pool));
+            }
+        }
     }
 
+  return SVN_NO_ERROR;
+}
+
+/*
+ * Drive svn_delta_editor_t (actions: add/copy/delete/modify) from
+ * a before-and-after element mapping.
+ */
+static svn_error_t *
+drive_changes_branch(ev3_from_delta_baton_t *eb,
+                     apr_pool_t *scratch_pool)
+{
+  apr_hash_t *paths_final;
+  const apr_array_header_t *paths;
+
+  /* Convert the element mappings to an svn_delta_editor_t traversal.
+
+        1. find union of paths in initial and final states, across all 
branches.
+        2. traverse paths in depth-first order.
+        3. modify/delete/add/replace as needed at each path.
+   */
+  paths_final = apr_hash_make(scratch_pool);
+  convert_branch_to_paths_r(paths_final, eb->edited_rev_root->root_branch,
+                            scratch_pool, scratch_pool);
+
+  {
+    svn_editor3_peg_path_t current = { -1, "" };
+
+    /* ### For now, assume based on youngest known rev. */
+    current.rev = eb->edited_rev_root->repos->rev_roots->nelts - 1;
+    SVN_ERR(drive_changes_r("", &current,
+                            paths_final, eb, scratch_pool));
+  }
+
   /* If the driver has not explicitly opened the root directory via the
      start_edit (aka open_root) callback, do so now. */
   if (eb->ev1_root_dir_baton == NULL)

Modified: subversion/branches/move-tracking-2/subversion/libsvn_delta/editor3.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_delta/editor3.c?rev=1638225&r1=1638224&r2=1638225&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_delta/editor3.c 
(original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_delta/editor3.c Tue 
Nov 11 17:54:22 2014
@@ -270,6 +270,7 @@ svn_editor3_put(svn_editor3_t *editor,
 #define VALID_EID(eid) ((eid) >= 0)
 #define VALID_NAME(name) ((name) && (name)[0] && 
svn_relpath_is_canonical(name))
 #define VALID_CONTENT(content) ((content) && VALID_NODE_KIND((content)->kind))
+#define VALID_EL_REV_ID(el_rev) (el_rev && el_rev->branch && 
VALID_EID(el_rev->eid))
 
 svn_error_t *
 svn_editor3_add(svn_editor3_t *editor,
@@ -324,22 +325,20 @@ svn_editor3_instantiate(svn_editor3_t *e
 svn_error_t *
 svn_editor3_copy_one(svn_editor3_t *editor,
                      svn_editor3_eid_t local_eid,
-                     svn_revnum_t src_revision,
-                     svn_editor3_eid_t src_eid,
+                     const struct svn_branch_el_rev_id_t *src_el_rev,
                      svn_editor3_eid_t new_parent_eid,
                      const char *new_name,
                      const svn_editor3_node_content_t *new_content)
 {
   SVN_ERR_ASSERT(VALID_EID(local_eid));
-  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(src_revision));
-  SVN_ERR_ASSERT(VALID_EID(src_eid));
+  SVN_ERR_ASSERT(VALID_EL_REV_ID(src_el_rev));
   SVN_ERR_ASSERT(VALID_EID(new_parent_eid));
   SVN_ERR_ASSERT(VALID_NAME(new_name));
   SVN_ERR_ASSERT(! new_content || VALID_CONTENT(new_content));
 
   DO_CALLBACK(editor, cb_copy_one,
-              6(local_eid,
-                src_revision, src_eid,
+              5(local_eid,
+                src_el_rev,
                 new_parent_eid, new_name,
                 new_content));
 
@@ -348,18 +347,16 @@ svn_editor3_copy_one(svn_editor3_t *edit
 
 svn_error_t *
 svn_editor3_copy_tree(svn_editor3_t *editor,
-                      svn_revnum_t src_revision,
-                      svn_editor3_eid_t src_eid,
+                      const svn_branch_el_rev_id_t *src_el_rev,
                       svn_editor3_eid_t new_parent_eid,
                       const char *new_name)
 {
-  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(src_revision));
-  SVN_ERR_ASSERT(VALID_EID(src_eid));
+  SVN_ERR_ASSERT(VALID_EL_REV_ID(src_el_rev));
   SVN_ERR_ASSERT(VALID_EID(new_parent_eid));
   SVN_ERR_ASSERT(VALID_NAME(new_name));
 
   DO_CALLBACK(editor, cb_copy_tree,
-              4(src_revision, src_eid,
+              3(src_el_rev,
                 new_parent_eid, new_name));
 
   return SVN_NO_ERROR;
@@ -573,6 +570,18 @@ svn_editor3_peg_path_dup(svn_editor3_peg
   return p;
 }
 
+svn_boolean_t
+svn_editor3_peg_path_equal(svn_editor3_peg_path_t *peg_path1,
+                           svn_editor3_peg_path_t *peg_path2)
+{
+  if (peg_path1->rev != peg_path2->rev)
+    return FALSE;
+  if (strcmp(peg_path1->relpath, peg_path2->relpath) != 0)
+    return FALSE;
+
+  return TRUE;
+}
+
 svn_editor3_txn_path_t
 svn_editor3_txn_path_dup(svn_editor3_txn_path_t p,
                          apr_pool_t *result_pool)
@@ -643,6 +652,15 @@ txn_path_str(svn_editor3_txn_path_t loc,
                       peg_path_str(loc.peg, result_pool), loc.relpath);
 }
 
+/* Return a human-readable string representation of EL_REV. */
+static const char *
+el_rev_str(const svn_branch_el_rev_id_t *el_rev,
+           apr_pool_t *result_pool)
+{
+  return apr_psprintf(result_pool, "r%ldb%de%d",
+                      el_rev->rev, el_rev->branch->sibling_defn->bid, 
el_rev->eid);
+}
+
 /* Return a human-readable string representation of EID. */
 static const char *
 eid_str(svn_editor3_eid_t eid,
@@ -796,8 +814,7 @@ wrap_instantiate(void *baton,
 static svn_error_t *
 wrap_copy_one(void *baton,
               svn_editor3_eid_t local_eid,
-              svn_revnum_t src_revision,
-              svn_editor3_eid_t src_eid,
+              const struct svn_branch_el_rev_id_t *src_el_rev,
               svn_editor3_eid_t new_parent_eid,
               const char *new_name,
               const svn_editor3_node_content_t *new_content,
@@ -806,18 +823,17 @@ wrap_copy_one(void *baton,
   wrapper_baton_t *eb = baton;
 
   dbg(eb, scratch_pool, "%s : copy_one(f=%s, p=%s, n=%s, c=...)",
-      eid_str(local_eid, scratch_pool), eid_str(src_eid, scratch_pool),
+      eid_str(local_eid, scratch_pool), el_rev_str(src_el_rev, scratch_pool),
       eid_str(new_parent_eid, scratch_pool), new_name);
   SVN_ERR(svn_editor3_copy_one(eb->wrapped_editor,
-                               local_eid, src_revision, src_eid,
+                               local_eid, src_el_rev,
                                new_parent_eid, new_name, new_content));
   return SVN_NO_ERROR;
 }
 
 static svn_error_t *
 wrap_copy_tree(void *baton,
-               svn_revnum_t src_revision,
-               svn_editor3_eid_t src_eid,
+               const svn_branch_el_rev_id_t *src_el_rev,
                svn_editor3_eid_t new_parent_eid,
                const char *new_name,
                apr_pool_t *scratch_pool)
@@ -825,10 +841,10 @@ wrap_copy_tree(void *baton,
   wrapper_baton_t *eb = baton;
 
   dbg(eb, scratch_pool, "... : copy_tree(f=%s, p=%s, n=%s)",
-      eid_str(src_eid, scratch_pool),
+      el_rev_str(src_el_rev, scratch_pool),
       eid_str(new_parent_eid, scratch_pool), new_name);
   SVN_ERR(svn_editor3_copy_tree(eb->wrapped_editor,
-                                src_revision, src_eid,
+                                src_el_rev,
                                 new_parent_eid, new_name));
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/move-tracking-2/subversion/svnmover/svnmover.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/svnmover/svnmover.c?rev=1638225&r1=1638224&r2=1638225&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/svnmover/svnmover.c 
(original)
+++ subversion/branches/move-tracking-2/subversion/svnmover/svnmover.c Tue Nov 
11 17:54:22 2014
@@ -859,7 +859,7 @@ svn_branch_diff(svn_editor3_t *editor,
 }
 
 #define VERIFY_REV_SPECIFIED(op, i)                                     \
-  if (el_rev[i]->rev != SVN_INVALID_REVNUM)                             \
+  if (el_rev[i]->rev == SVN_INVALID_REVNUM)                             \
     return svn_error_createf(SVN_ERR_BRANCHING, NULL,                   \
                              _("%s: '%s': revision number required"),   \
                              op, action->path[i]);
@@ -1063,7 +1063,7 @@ execute(const apr_array_header_t *action
           VERIFY_REV_UNSPECIFIED("cp", 1);
           VERIFY_EID_NONEXISTENT("cp", 1);
           SVN_ERR(svn_editor3_copy_tree(editor,
-                                        el_rev[0]->rev, el_rev[0]->eid,
+                                        el_rev[0],
                                         parent_el_rev[1]->eid, path_name[1]));
           made_changes = TRUE;
           break;
@@ -1194,7 +1194,7 @@ usage(FILE *stream, apr_pool_t *pool)
       "  diff-e LEFT RIGHT      : diff LEFT to RIGHT (element-focused 
output)\n"
       "  merge FROM TO YCA@REV  : merge changes YCA->FROM and YCA->TO into 
TO\n"
       "                           (FROM and YCA are relative to repo, not to 
root-URL)\n"
-      "  cp SRC-URL@REV DST-URL : copy SRC-URL@REV to DST-URL\n"
+      "  cp REV SRC-URL DST-URL : copy SRC-URL@REV to DST-URL\n"
       "  mv SRC-URL DST-URL     : move SRC-URL to DST-URL\n"
       "  rm URL                 : delete URL\n"
       "  mkdir URL              : create new directory URL\n"
@@ -1607,6 +1607,7 @@ sub_main(int *exit_code, int argc, const
       int j, num_url_args;
       const char *action_string = APR_ARRAY_IDX(action_args, i, const char *);
       struct action *action = apr_pcalloc(pool, sizeof(*action));
+      const char *cp_from_rev = NULL;
 
       /* First, parse the action. */
       if (! strcmp(action_string, "diff"))
@@ -1628,7 +1629,14 @@ sub_main(int *exit_code, int argc, const
       else if (! strcmp(action_string, "mv"))
         action->action = ACTION_MV;
       else if (! strcmp(action_string, "cp"))
-        action->action = ACTION_CP;
+        {
+          action->action = ACTION_CP;
+
+          /* next argument is the copy source revision */
+          if (++i == action_args->nelts)
+            return svn_error_trace(insufficient());
+          cp_from_rev = APR_ARRAY_IDX(action_args, i, const char *);
+        }
       else if (! strcmp(action_string, "mkdir"))
         action->action = ACTION_MKDIR;
       else if (! strcmp(action_string, "put"))
@@ -1669,6 +1677,11 @@ sub_main(int *exit_code, int argc, const
             return svn_error_trace(insufficient());
           path = APR_ARRAY_IDX(action_args, i, const char *);
 
+          if (cp_from_rev && j == 0)
+            {
+              path = apr_psprintf(pool, "%s@%s", path, cp_from_rev);
+            }
+
           SVN_ERR(svn_opt_parse_path(&action->rev_spec[j], &path, path, pool));
 
           /* If there's a ROOT_URL, we expect URL to be a path

Modified: 
subversion/branches/move-tracking-2/subversion/tests/cmdline/svnmover_tests.py
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/tests/cmdline/svnmover_tests.py?rev=1638225&r1=1638224&r2=1638225&view=diff
==============================================================================
--- 
subversion/branches/move-tracking-2/subversion/tests/cmdline/svnmover_tests.py 
(original)
+++ 
subversion/branches/move-tracking-2/subversion/tests/cmdline/svnmover_tests.py 
Tue Nov 11 17:54:22 2014
@@ -161,24 +161,24 @@ def basic_svnmover(sbox):
   # revision 7
   test_svnmover(sbox.repo_url,
                 ['D /zig/zag/bar/y.c',
-                 'A /zig/zag/bar/y y.c (from /zig/zag/bar/y.c:6)',
+                 'A /zig/zag/bar/y_y.c (from /zig/zag/bar/y.c:6)',
                  'A /zig/zag/bar/y%20y.c (from /zig/zag/bar/y.c:6)',
                  ], # ---------
                 '-m', 'log msg',
-                'mv',         'zig/zag/bar/y.c', 'zig/zag/bar/y%20y.c',
-                'cp', 'HEAD', 'zig/zag/bar/y.c', 'zig/zag/bar/y%2520y.c')
+                'mv',         'zig/zag/bar/y.c', 'zig/zag/bar/y_y.c',
+                'cp', 'HEAD', 'zig/zag/bar/y.c', 'zig/zag/bar/y%20y.c')
 
   # revision 8
   test_svnmover(sbox.repo_url,
-                ['D /zig/zag/bar/y y.c',
-                 'A /zig/zag/bar/z z1.c (from /zig/zag/bar/y y.c:7)',
+                ['D /zig/zag/bar/y_y.c',
+                 'A /zig/zag/bar/z_z1.c (from /zig/zag/bar/y_y.c:7)',
                  'A /zig/zag/bar/z%20z.c (from /zig/zag/bar/y%20y.c:7)',
-                 'A /zig/zag/bar/z z2.c (from /zig/zag/bar/y y.c:7)',
+                 'A /zig/zag/bar/z_z2.c (from /zig/zag/bar/y_y.c:7)',
                  ], #---------
                 '-m', 'log msg',
-                'mv',         'zig/zag/bar/y%20y.c',   'zig/zag/bar/z z1.c',
-                'cp', 'HEAD', 'zig/zag/bar/y%2520y.c', 'zig/zag/bar/z%2520z.c',
-                'cp', 'HEAD', 'zig/zag/bar/y y.c',     'zig/zag/bar/z z2.c')
+                'mv',         'zig/zag/bar/y_y.c',   'zig/zag/bar/z_z1.c',
+                'cp', 'HEAD', 'zig/zag/bar/y%20y.c', 'zig/zag/bar/z%20z.c',
+                'cp', 'HEAD', 'zig/zag/bar/y_y.c',   'zig/zag/bar/z_z2.c')
 
 
   # revision 9
@@ -186,15 +186,15 @@ def basic_svnmover(sbox):
                 ['D /zig/zag',
                  'A /zig/foo (from /zig/zag:8)',
                  'D /zig/foo/bar/z%20z.c',
-                 'D /zig/foo/bar/z z2.c',
-                 'R /zig/foo/bar/z z1.c (from /zig/zag/bar/x.c:6)',
+                 'D /zig/foo/bar/z_z2.c',
+                 'R /zig/foo/bar/z_z1.c (from /zig/zag/bar/x.c:6)',
                  ], #---------
                 '-m', 'log msg',
                 'mv',      'zig/zag',         'zig/foo',
-                'rm',                         'zig/foo/bar/z z1.c',
-                'rm',                         'zig/foo/bar/z%20z2.c',
-                'rm',                         'zig/foo/bar/z%2520z.c',
-                'cp', '6', 'zig/zag/bar/x.c', 'zig/foo/bar/z%20z1.c')
+                'rm',                         'zig/foo/bar/z_z1.c',
+                'rm',                         'zig/foo/bar/z_z2.c',
+                'rm',                         'zig/foo/bar/z%20z.c',
+                'cp', '6', 'zig/zag/bar/x.c', 'zig/foo/bar/z_z1.c')
 
   # revision 10
   test_svnmover(sbox.repo_url,
@@ -207,12 +207,12 @@ def basic_svnmover(sbox):
   # revision 11
   test_svnmover(sbox.repo_url,
                 ['R /zig/foo/bar (from /zig/foo/bar:9)',
-                 'D /zig/foo/bar/z z1.c',
+                 'D /zig/foo/bar/z_z1.c',
                  ], #---------
                 '-m', 'log msg',
                 'rm',                     'zig/foo/bar',
                 'cp', '9', 'zig/foo/bar', 'zig/foo/bar',
-                'rm',                     'zig/foo/bar/z%20z1.c')
+                'rm',                     'zig/foo/bar/z_z1.c')
 
   # revision 12
   test_svnmover(sbox.repo_url,


Reply via email to