You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2015/11/26 15:48:49 UTC

svn commit: r1716710 - in /subversion/trunk/tools/dev/svnmover: svnmover.c svnmover.h

Author: julianfoad
Date: Thu Nov 26 14:48:49 2015
New Revision: 1716710

URL: http://svn.apache.org/viewvc?rev=1716710&view=rev
Log:
Teach 'svnmover' to support a mixed-revision WC.

The WC storage structure now supports a different base revision for each
element. The 'info-wc' command now shows minimum and maximum base revisions
(if they differ). There is not yet a way to see the base revision of a
specific element.

Commit will now produce a mixed-rev WC: it no longer updates the whole WC.

In order to fully identify a base element with an older revision number, we
will need a way to trace the branch back through history, following any
changes in the branch id.

Bug: The committed revision doesn't get attached to the WC's 'repos' object
(only to the server's 'repos' object) so we then can't access it: e.g.
'update .@HEAD' fails with 'No such revision'.

* tools/dev/svnmover/svnmover.c
  (svnmover_wc_set_base_rev,
   svnmover_wc_get_base_rev,
   svnmover_wc_set_base_revs,
   svnmover_wc_get_base_revs): New.
  (wc_checkout): Initialize the base revision numbers.
  (update_wc_eids,
   update_wc_base): New.
  (wc_commit): Update the WC base to reflect the commit.
  (find_el_rev_by_rrpath_rev): Allow looking up an element in revision
    'BASE'.
  (subtree_diff_r,
   branch_diff_r): Stop reporting a (single) revision number for each side
    of the diff.
  (commit): Tweak a comment.
  (do_commit): Don't update the whole WC after commit.
  (arg_t): Remove the 'revnum' field.
  (execute): Expand the 'info-wc' display to report the minimum and maximum
    base revision numbers. Track the other changes. Add a comment.

* tools/dev/svnmover/svnmover.h
  (svnmover_wc_t): Add storage for the base revision numbers.

Modified:
    subversion/trunk/tools/dev/svnmover/svnmover.c
    subversion/trunk/tools/dev/svnmover/svnmover.h

Modified: subversion/trunk/tools/dev/svnmover/svnmover.c
URL: http://svn.apache.org/viewvc/subversion/trunk/tools/dev/svnmover/svnmover.c?rev=1716710&r1=1716709&r2=1716710&view=diff
==============================================================================
--- subversion/trunk/tools/dev/svnmover/svnmover.c (original)
+++ subversion/trunk/tools/dev/svnmover/svnmover.c Thu Nov 26 14:48:49 2015
@@ -185,6 +185,91 @@ svnmover_notify_v(const char *fmt,
 
 /* ====================================================================== */
 
+/* Set the WC base revision of element EID to BASE_REV.
+ */
+static void
+svnmover_wc_set_base_rev(svnmover_wc_t *wc, int eid, svn_revnum_t base_rev)
+{
+  void *val = apr_pmemdup(wc->pool, &base_rev, sizeof(base_rev));
+
+  svn_eid__hash_set(wc->base_revs, eid, val);
+}
+
+/* Get the WC base revision of element EID, or SVN_INVALID_REVNUM if
+ * element EID is not present in the WC base.
+ */
+static svn_revnum_t
+svnmover_wc_get_base_rev(svnmover_wc_t *wc, int eid, apr_pool_t *scratch_pool)
+{
+  svn_error_t *err;
+  svn_element__content_t *element;
+
+  err = svn_branch__state_get_element(wc->base->branch, &element, eid,
+                                      scratch_pool);
+  if (err || !element)
+    {
+      svn_error_clear(err);
+      return SVN_INVALID_REVNUM;
+    }
+
+  return *(svn_revnum_t *)svn_eid__hash_get(wc->base_revs, eid);
+}
+
+/* Set the WC base revision of each element in ELEMENTS to BASE_REV.
+ */
+static svn_error_t *
+svnmover_wc_set_base_revs(svnmover_wc_t *wc,
+                          svn_element__tree_t *elements,
+                          svn_revnum_t base_rev,
+                          apr_pool_t *scratch_pool)
+{
+  apr_hash_index_t *hi;
+
+  wc->base_revs = apr_hash_make(wc->pool);
+  for (hi = apr_hash_first(scratch_pool, elements->e_map);
+       hi; hi = apr_hash_next(hi))
+    {
+      int eid = svn_eid__hash_this_key(hi);
+
+      svnmover_wc_set_base_rev(wc, eid, base_rev);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Get the lowest and highest base revision numbers in WC.
+ */
+static svn_error_t *
+svnmover_wc_get_base_revs(svnmover_wc_t *wc,
+                          svn_revnum_t *base_rev_min,
+                          svn_revnum_t *base_rev_max,
+                          apr_pool_t *scratch_pool)
+{
+  svn_element__tree_t *base_elements;
+  apr_hash_index_t *hi;
+
+  SVN_ERR(svn_branch__state_get_elements(wc->base->branch, &base_elements,
+                                         scratch_pool));
+
+  *base_rev_min = SVN_INVALID_REVNUM;
+  *base_rev_max = SVN_INVALID_REVNUM;
+  for (hi = apr_hash_first(scratch_pool, base_elements->e_map);
+       hi; hi = apr_hash_next(hi))
+    {
+      int eid = svn_eid__hash_this_key(hi);
+      svn_revnum_t rev = svnmover_wc_get_base_rev(wc, eid, scratch_pool);
+
+      if (*base_rev_min == SVN_INVALID_REVNUM
+          || rev < *base_rev_min)
+        *base_rev_min = rev;
+      if (*base_rev_max == SVN_INVALID_REVNUM
+          || rev > *base_rev_max)
+        *base_rev_max = rev;
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /* Update the WC to revision BASE_REVISION (SVN_INVALID_REVNUM means HEAD).
  *
  * Requires these fields in WC:
@@ -214,6 +299,7 @@ wc_checkout(svnmover_wc_t *wc,
   svn_branch__compat_fetch_func_t fetch_func;
   void *fetch_baton;
   svn_branch__txn_t *base_txn;
+  svn_element__tree_t *base_elements;
 
   /* Validate and store the new base revision number */
   if (! SVN_IS_VALID_REVNUM(base_revision))
@@ -255,6 +341,10 @@ wc_checkout(svnmover_wc_t *wc,
     return svn_error_createf(SVN_BRANCH__ERR, NULL,
                              "Cannot check out WC: branch %s not found in r%ld",
                              base_branch_id, base_revision);
+  SVN_ERR(svn_branch__state_get_elements(wc->base->branch, &base_elements,
+                                         scratch_pool));
+  SVN_ERR(svnmover_wc_set_base_revs(wc, base_elements,
+                                    base_revision, scratch_pool));
 
   wc->working = apr_pcalloc(wc->pool, sizeof(*wc->working));
   wc->working->revision = SVN_INVALID_REVNUM;
@@ -733,9 +823,74 @@ allocate_eids(svn_branch__txn_t *new_txn
   return SVN_NO_ERROR;
 }
 
+/* Update the EIDs, given that a commit has translated all new EIDs
+ * (negative numbers) to regular EIDs (positive numbers).
+ *
+ * ### TODO: This will need to take and use a new-EID-translation rule
+ *     that must be returned by the commit, as we must not guess (as we
+ *     presently do) what translation the server performed. This guess
+ *     will fail once the server does rebasing on commit.
+ */
+static svn_error_t *
+update_wc_eids(svnmover_wc_t *wc,
+               apr_pool_t *scratch_pool)
+{
+  SVN_ERR(allocate_eids(wc->base->branch->txn, wc->working->branch->txn,
+                        scratch_pool));
+  SVN_ERR(svn_branch__txn_finalize_eids(wc->base->branch->txn, scratch_pool));
+  SVN_ERR(svn_branch__txn_finalize_eids(wc->working->branch->txn, scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+/* Update the WC base value of each committed element to match the
+ * corresponding WC working element value.
+ * Update the WC base revision for each committed element to NEW_REV.
+ *
+ * The committed elements are determined by diffing base against working.
+ * ### TODO: When we allow committing a subset of the WC, we'll need to
+ *     pass in a list of the committed elements.
+ */
+static svn_error_t *
+update_wc_base(svnmover_wc_t *wc,
+               svn_revnum_t new_rev,
+               apr_pool_t *scratch_pool)
+{
+  svn_element__tree_t *base_elements, *working_elements;
+  apr_hash_t *committed_elements;
+  apr_hash_index_t *hi;
+
+  SVN_ERR(svn_branch__state_get_elements(wc->base->branch, &base_elements,
+                                         scratch_pool));
+  SVN_ERR(svn_branch__state_get_elements(wc->working->branch, &working_elements,
+                                         scratch_pool));
+  SVN_ERR(svnmover_element_differences(&committed_elements,
+                                       base_elements, working_elements,
+                                       scratch_pool, scratch_pool));
+
+  for (hi = apr_hash_first(scratch_pool, committed_elements);
+       hi; hi = apr_hash_next(hi))
+    {
+      int eid = svn_eid__hash_this_key(hi);
+      svn_element__content_t *content;
+
+      SVN_ERR(svn_branch__state_get_element(wc->working->branch, &content,
+                                            eid, scratch_pool));
+      if (content)
+        SVN_ERR(svn_branch__state_alter_one(wc->base->branch, eid,
+                                            content->parent_eid, content->name,
+                                            content->payload, scratch_pool));
+      else
+        SVN_ERR(svn_branch__state_delete_one(wc->base->branch, eid, scratch_pool));
+      svnmover_wc_set_base_rev(wc, eid, new_rev);
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /* Commit the changes from WC into the repository.
  *
  * Open a new commit txn to the repo. Replay the changes from WC into it.
+ * Update the WC base for the committed elements.
  *
  * Set WC->head_revision and *NEW_REV_P to the committed revision number.
  *
@@ -833,6 +988,8 @@ wc_commit(svn_revnum_t *new_rev_p,
   ccbb.wc_commit_branch_id = edit_root_branch_id;
 
   SVN_ERR(svn_branch__txn_complete(commit_txn, scratch_pool));
+  SVN_ERR(update_wc_eids(wc, scratch_pool));
+  SVN_ERR(update_wc_base(wc, ccbb.revision, scratch_pool));
   SVN_ERR(display_diff_of_commit(&ccbb, scratch_pool));
 
   wc->head_revision = ccbb.revision;
@@ -989,14 +1146,18 @@ typedef struct action_t {
 static svn_error_t *
 find_el_rev_by_rrpath_rev(svn_branch__el_rev_id_t **el_rev_p,
                           svnmover_wc_t *wc,
-                          svn_revnum_t revnum,
+                          const svn_opt_revision_t *rev_spec,
                           const char *branch_id,
                           const char *relpath,
                           apr_pool_t *result_pool,
                           apr_pool_t *scratch_pool)
 {
-  if (SVN_IS_VALID_REVNUM(revnum))
+  if (rev_spec->kind == svn_opt_revision_number
+      || rev_spec->kind == svn_opt_revision_head)
     {
+      svn_revnum_t revnum
+        = (rev_spec->kind == svn_opt_revision_number)
+           ? rev_spec->value.number : wc->head_revision;
       const svn_branch__repos_t *repos = wc->working->branch->txn->repos;
 
       if (! branch_id)
@@ -1008,7 +1169,10 @@ find_el_rev_by_rrpath_rev(svn_branch__el
                                                         result_pool,
                                                         scratch_pool));
     }
-  else
+  else if (rev_spec->kind == svn_opt_revision_unspecified
+           || rev_spec->kind == svn_opt_revision_working
+           || rev_spec->kind == svn_opt_revision_base
+           || rev_spec->kind == svn_opt_revision_committed)
     {
       svn_branch__state_t *branch
         = branch_id ? svn_branch__txn_get_branch_by_id(
@@ -1023,9 +1187,25 @@ find_el_rev_by_rrpath_rev(svn_branch__el
       SVN_ERR(svn_branch__find_nested_branch_element_by_relpath(
         &el_rev->branch, &el_rev->eid,
         branch, relpath, scratch_pool));
-      el_rev->rev = SVN_INVALID_REVNUM;
+      if (rev_spec->kind == svn_opt_revision_unspecified
+          || rev_spec->kind == svn_opt_revision_working)
+        {
+          el_rev->rev = SVN_INVALID_REVNUM;
+        }
+      else
+        {
+          el_rev->rev = svnmover_wc_get_base_rev(wc, el_rev->eid, scratch_pool);
+        }
       *el_rev_p = el_rev;
     }
+  else
+    {
+      return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
+                               "'%s@...': revision specifier "
+                               "must be a number or 'head', 'base' "
+                               "or 'committed'",
+                               relpath);
+    }
   SVN_ERR_ASSERT(*el_rev_p);
   return SVN_NO_ERROR;
 }
@@ -1730,11 +1910,9 @@ svn_branch__diff_func_t(svn_branch__subt
  */
 static svn_error_t *
 subtree_diff_r(svn_branch__subtree_t *left,
-               svn_revnum_t left_rev,
                const char *left_bid,
                const char *left_rrpath,
                svn_branch__subtree_t *right,
-               svn_revnum_t right_rev,
                const char *right_bid,
                const char *right_rrpath,
                svn_branch__diff_func_t diff_func,
@@ -1742,12 +1920,12 @@ subtree_diff_r(svn_branch__subtree_t *le
                apr_pool_t *scratch_pool)
 {
   const char *left_str
-    = left ? apr_psprintf(scratch_pool, "r%ld:%s:e%d at /%s",
-                          left_rev, left_bid, left->tree->root_eid, left_rrpath)
+    = left ? apr_psprintf(scratch_pool, "%s:e%d at /%s",
+                          left_bid, left->tree->root_eid, left_rrpath)
            : NULL;
   const char *right_str
-    = right ? apr_psprintf(scratch_pool, "r%ld:%s:e%d at /%s",
-                           right_rev, right_bid, right->tree->root_eid, right_rrpath)
+    = right ? apr_psprintf(scratch_pool, "%s:e%d at /%s",
+                           right_bid, right->tree->root_eid, right_rrpath)
             : NULL;
   const char *header;
   apr_hash_t *subbranches_l, *subbranches_r, *subbranches_all;
@@ -1828,8 +2006,8 @@ subtree_diff_r(svn_branch__subtree_t *le
                                                   scratch_pool);
             }
         }
-      SVN_ERR(subtree_diff_r(sub_left, left_rev, sub_left_bid, sub_left_rrpath,
-                             sub_right, right_rev, sub_right_bid, sub_right_rrpath,
+      SVN_ERR(subtree_diff_r(sub_left, sub_left_bid, sub_left_rrpath,
+                             sub_right, sub_right_bid, sub_right_rrpath,
                              diff_func, prefix, scratch_pool));
     }
   return SVN_NO_ERROR;
@@ -1869,11 +2047,9 @@ branch_diff_r(svn_branch__el_rev_id_t *l
                                   scratch_pool));
 
   SVN_ERR(subtree_diff_r(s_left,
-                         left->rev,
                          svn_branch__get_id(left->branch, scratch_pool),
                          svn_branch__get_root_rrpath(left->branch, scratch_pool),
                          s_right,
-                         right->rev,
                          svn_branch__get_id(right->branch, scratch_pool),
                          svn_branch__get_root_rrpath(right->branch, scratch_pool),
                          diff_func, prefix, scratch_pool));
@@ -2574,7 +2750,7 @@ commit(svn_revnum_t *new_rev_p,
                                  "unresolved conflicts"));
     }
 
-  /* Complete the old edit drive (into the 'WC') */
+  /* Complete the old edit drive (editing the WC working state) */
   SVN_ERR(svn_branch__txn_sequence_point(wc->edit_txn, scratch_pool));
 
   /* Just as in execute() the pool must be a subpool of wc->pool. */
@@ -2583,13 +2759,13 @@ commit(svn_revnum_t *new_rev_p,
   return SVN_NO_ERROR;
 }
 
-/* Commit and update WC.
+/* Commit.
  *
- * Set *NEW_REV_P to the committed revision number, and update the WC to
- * that revision.
+ * Set *NEW_REV_P to the committed revision number. Update the WC base of
+ * each committed element to that revision.
  *
  * If there are no changes to commit, set *NEW_REV_P to SVN_INVALID_REVNUM
- * and do not make a commit and do not update the WC.
+ * and do not make a commit.
  *
  * NEW_REV_P may be null if not wanted.
  */
@@ -2603,12 +2779,6 @@ do_commit(svn_revnum_t *new_rev_p,
 
   SVN_ERR(commit(&new_rev, wc, revprops, scratch_pool));
 
-  /* Check out a new WC.
-     (Instead, we could perhaps just get a new WC editor.) */
-  SVN_ERR(wc_checkout(wc, SVN_IS_VALID_REVNUM(new_rev) ? new_rev
-                                                       : wc->base->revision,
-                      wc->working->branch->bid, scratch_pool));
-
   if (new_rev_p)
     *new_rev_p = new_rev;
   return SVN_NO_ERROR;
@@ -2752,7 +2922,6 @@ do_migrate(svnmover_wc_t *wc,
 typedef struct arg_t
 {
   const char *path_name;
-  svn_revnum_t revnum;
   svn_branch__el_rev_id_t *el_rev, *parent_el_rev;
 } arg_t;
 
@@ -2865,37 +3034,18 @@ execute(svnmover_wc_t *wc,
               const char *rrpath, *parent_rrpath;
 
               arg[j] = apr_palloc(iterpool, sizeof(*arg[j]));
-              if (action->rev_spec[j].kind == svn_opt_revision_unspecified)
-                arg[j]->revnum = SVN_INVALID_REVNUM;
-              else if (action->rev_spec[j].kind == svn_opt_revision_number)
-                arg[j]->revnum = action->rev_spec[j].value.number;
-              else if (action->rev_spec[j].kind == svn_opt_revision_head)
-                {
-                  arg[j]->revnum = wc->head_revision;
-                }
-              else if (action->rev_spec[j].kind == svn_opt_revision_base
-                       || action->rev_spec[j].kind == svn_opt_revision_committed)
-                {
-                  arg[j]->revnum = wc->base->revision;
-                }
-              else
-                return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
-                                         "'%s@...': revision specifier "
-                                         "must be a number or 'head', 'base' "
-                                         "or 'committed'",
-                                         action->relpath[j]);
 
               rrpath = svn_relpath_join(base_relpath, action->relpath[j], iterpool);
               parent_rrpath = svn_relpath_dirname(rrpath, iterpool);
 
               arg[j]->path_name = svn_relpath_basename(rrpath, NULL);
               SVN_ERR(find_el_rev_by_rrpath_rev(&arg[j]->el_rev, wc,
-                                                arg[j]->revnum,
+                                                &action->rev_spec[j],
                                                 action->branch_id[j],
                                                 rrpath,
                                                 iterpool, iterpool));
               SVN_ERR(find_el_rev_by_rrpath_rev(&arg[j]->parent_el_rev, wc,
-                                                arg[j]->revnum,
+                                                &action->rev_spec[j],
                                                 action->branch_id[j],
                                                 parent_rrpath,
                                                 iterpool, iterpool));
@@ -2907,12 +3057,22 @@ execute(svnmover_wc_t *wc,
         case ACTION_INFO_WC:
           {
             svn_boolean_t is_modified;
+            svn_revnum_t base_rev_min, base_rev_max;
             svn_branch__rev_bid_t *merge_ancestor;
 
             SVN_ERR(txn_is_changed(wc->working->branch->txn, &is_modified,
                                    iterpool));
+            SVN_ERR(svnmover_wc_get_base_revs(wc, &base_rev_min, &base_rev_max,
+                                              iterpool));
+            SVN_ERR(svn_branch__state_get_merge_ancestor(
+                      wc->working->branch, &merge_ancestor, iterpool));
+
             svnmover_notify("Repository Root: %s", wc->repos_root_url);
-            svnmover_notify("Base Revision: %ld", wc->base->revision);
+            if (base_rev_min == base_rev_max)
+              svnmover_notify("Base Revision: %ld", base_rev_min);
+            else
+              svnmover_notify("Base Revisions: %ld to %ld",
+                              base_rev_min, base_rev_max);
             svnmover_notify("Base Branch:    %s", wc->base->branch->bid);
             SVN_ERR(svn_branch__state_get_merge_ancestor(
                       wc->base->branch, &merge_ancestor, iterpool));
@@ -2921,8 +3081,6 @@ execute(svnmover_wc_t *wc,
                               merge_ancestor->rev, merge_ancestor->bid);
             svnmover_notify("Working Branch: %s", wc->working->branch->bid);
             svnmover_notify("Modified:       %s", is_modified ? "yes" : "no");
-            SVN_ERR(svn_branch__state_get_merge_ancestor(
-                      wc->working->branch, &merge_ancestor, iterpool));
             if (merge_ancestor)
               svnmover_notify("  merge ancestor: %ld.%s",
                               merge_ancestor->rev, merge_ancestor->bid);
@@ -2971,7 +3129,7 @@ execute(svnmover_wc_t *wc,
 
             from = svn_branch__el_rev_id_create(wc->base->branch,
                                                 svn_branch__root_eid(wc->base->branch),
-                                                wc->base->revision, iterpool);
+                                                SVN_INVALID_REVNUM, iterpool);
             to = svn_branch__el_rev_id_create(wc->working->branch,
                                               svn_branch__root_eid(wc->working->branch),
                                               SVN_INVALID_REVNUM, iterpool);
@@ -3302,9 +3460,12 @@ execute(svnmover_wc_t *wc,
              Presently it would try to update to a state of nonexistence. */
           /* path (or eid) is currently required for syntax, but ignored */
           VERIFY_EID_EXISTS("update", 0);
+          /* We require a rev to be specified because an unspecified rev
+             currently always means 'working version', whereas we would
+             want it to mean 'head' for this subcommand. */
           VERIFY_REV_SPECIFIED("update", 0);
           {
-            SVN_ERR(do_switch(wc, arg[0]->revnum, wc->base->branch,
+            SVN_ERR(do_switch(wc, arg[0]->el_rev->rev, wc->base->branch,
                               iterpool));
           }
           break;
@@ -3312,7 +3473,7 @@ execute(svnmover_wc_t *wc,
         case ACTION_SWITCH:
           VERIFY_EID_EXISTS("switch", 0);
           {
-            SVN_ERR(do_switch(wc, arg[0]->revnum, arg[0]->el_rev->branch,
+            SVN_ERR(do_switch(wc, arg[0]->el_rev->rev, arg[0]->el_rev->branch,
                               iterpool));
           }
           break;
@@ -3329,7 +3490,7 @@ execute(svnmover_wc_t *wc,
           VERIFY_REV_SPECIFIED("migrate", 0);
           {
             SVN_ERR(do_migrate(wc,
-                               arg[0]->revnum, arg[0]->revnum,
+                               arg[0]->el_rev->rev, arg[0]->el_rev->rev,
                                iterpool));
           }
           break;

Modified: subversion/trunk/tools/dev/svnmover/svnmover.h
URL: http://svn.apache.org/viewvc/subversion/trunk/tools/dev/svnmover/svnmover.h?rev=1716710&r1=1716709&r2=1716710&view=diff
==============================================================================
--- subversion/trunk/tools/dev/svnmover/svnmover.h (original)
+++ subversion/trunk/tools/dev/svnmover/svnmover.h Thu Nov 26 14:48:49 2015
@@ -120,6 +120,7 @@ typedef struct svnmover_wc_t
   conflict_storage_t *conflicts;
 
   /* Base and working versions. */
+  apr_hash_t *base_revs;  /* eid -> revnum, for (at least) all eids in BASE */
   svnmover_wc_version_t *base, *working;
 
   /* Textual list of commands the commands that were executed, suitable