Author: stefan2
Date: Sun Sep 22 21:13:03 2013
New Revision: 1525448

URL: http://svn.apache.org/r1525448
Log:
Add flexible, backward compatible move support to svn_fs_paths_changed.

We introduce a new enum type, svn_move_behavior_t, that will allow us
to specify which changes shall be reported / treated as moves.  For now,
we use it with the log APIs but it is also applicable to merge-related
functionality.

* subversion/include/svn_types.h
  (svn_move_behavior_t): declare new enum type

* subversion/include/svn_fs.h
  (svn_fs_paths_changed3): bumped API with additional move_behavior option
  (svn_fs_paths_changed2): deprecate

* subversion/libsvn_fs/fs-loader.c
  (is_deletion,
   turn_moves_into_copies,
   turn_unique_copies_into_moves): new utility functions for the new
                                   svn_fs_paths_changed3 modes
  (svn_fs_paths_changed3): implement bumped API
  (svn_fs_paths_changed2): implement in terms of the new API

Modified:
    subversion/trunk/subversion/include/svn_fs.h
    subversion/trunk/subversion/include/svn_types.h
    subversion/trunk/subversion/libsvn_fs/fs-loader.c

Modified: subversion/trunk/subversion/include/svn_fs.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_fs.h?rev=1525448&r1=1525447&r2=1525448&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_fs.h (original)
+++ subversion/trunk/subversion/include/svn_fs.h Sun Sep 22 21:13:03 2013
@@ -1399,6 +1399,8 @@ svn_fs_path_change2_create(const svn_fs_
  * Allocate and return a hash @a *changed_paths2_p containing descriptions
  * of the paths changed under @a root.  The hash is keyed with
  * <tt>const char *</tt> paths, and has #svn_fs_path_change2_t * values.
+ * @a move_behavior determines how moves are being detected and reported;
+ * see #svn_fs_move_behavior_t for the various options.
  *
  * Callers can assume that this function takes time proportional to
  * the amount of data output, and does not need to do tree crawls;
@@ -1408,8 +1410,22 @@ svn_fs_path_change2_create(const svn_fs_
  *
  * Use @a pool for all allocations, including the hash and its values.
  *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_fs_paths_changed3(apr_hash_t **changed_paths2_p,
+                      svn_fs_root_t *root,
+                      svn_move_behavior_t move_behavior,
+                      apr_pool_t *pool);
+
+
+/** Same as svn_fs_paths_changed3 but with @a move_behavior set to
+ * #svn_fs_move_behavior_no_moves.
+ *
+ * @deprecated Provided for backward compatibility with the 1.8 API.
  * @since New in 1.6.
  */
+SVN_DEPRECATED
 svn_error_t *
 svn_fs_paths_changed2(apr_hash_t **changed_paths2_p,
                       svn_fs_root_t *root,

Modified: subversion/trunk/subversion/include/svn_types.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_types.h?rev=1525448&r1=1525447&r2=1525448&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_types.h (original)
+++ subversion/trunk/subversion/include/svn_types.h Sun Sep 22 21:13:03 2013
@@ -1001,6 +1001,28 @@ typedef svn_error_t *(*svn_log_message_r
   const char *message,
   apr_pool_t *pool);
 
+/**
+ * This enumeration contains the various options how SVN shall report
+ * and process explicit MOVes as well as ADD+DEL pairs.
+ *
+ * @since New in 1.9.
+ */
+typedef enum svn_move_behavior_t
+{
+  /* report all moves as ADD with history.
+     This also provides backward compatibility with 1.8 clients. */
+  svn_move_behavior_no_moves,
+
+  /* report all changes, including moves, as they were reported.
+     This is option with the least overhead. */
+  svn_move_behavior_explicit_moves,
+
+  /* in addition to explicit moves, try to find matching DEL + ADD pairs
+     and report the ADD in those as moves as well.  Which of the eligible
+     DEL + ADD pairs will be detected is implementation-dependent. */
+  svn_move_behavior_auto_moves
+} svn_move_behavior_t;
+
 
 
 /** Callback function type for commits.

Modified: subversion/trunk/subversion/libsvn_fs/fs-loader.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs/fs-loader.c?rev=1525448&r1=1525447&r2=1525448&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs/fs-loader.c (original)
+++ subversion/trunk/subversion/libsvn_fs/fs-loader.c Sun Sep 22 21:13:03 2013
@@ -42,6 +42,7 @@
 #include "svn_xml.h"
 #include "svn_pools.h"
 #include "svn_string.h"
+#include "svn_sorts.h"
 
 #include "private/svn_fs_private.h"
 #include "private/svn_fs_util.h"
@@ -988,11 +989,177 @@ svn_fs_revision_root_revision(svn_fs_roo
   return root->is_txn_root ? SVN_INVALID_REVNUM : root->rev;
 }
 
+/* Return TRUE, if CHANGE deleted the node previously found at its target
+   path. */
+static svn_boolean_t
+is_deletion(svn_fs_path_change2_t *change)
+{
+  return change->change_kind == svn_fs_path_change_movereplace
+      || change->change_kind == svn_fs_path_change_replace
+      || change->change_kind == svn_fs_path_change_delete;
+}
+
+/* Change all moves in CHANGES to ADD.  Use POOL for temporary allocations.
+ */
+static void
+turn_moves_into_copies(apr_hash_t *changes,
+                       apr_pool_t *pool)
+{
+  apr_hash_index_t *hi;
+  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
+    {
+      const char *key;
+      apr_ssize_t klen;
+      svn_fs_path_change2_t *change;
+      apr_hash_this(hi, (const void **)&key, &klen, (void**)&change);
+
+      switch (change->change_kind)
+        {
+          case svn_fs_path_change_move:
+            change->change_kind = svn_fs_path_change_add;
+            break;
+
+          case svn_fs_path_change_movereplace:
+            change->change_kind = svn_fs_path_change_replace;
+            break;
+
+          default:
+            break;
+        }
+    }
+}
+
+/* Replace ADDs with MOVes, if they are unique, have a matching deletion
+ * and if the copy-from revision is REVISION-1.  Use POOL for temporary
+ * allocations.
+ */
+static void
+turn_unique_copies_into_moves(apr_hash_t *changes,
+                              svn_revnum_t revision,
+                              apr_pool_t *pool)
+{
+  apr_hash_index_t *hi;
+  apr_hash_t *unique_copy_sources;
+  const char **sources;
+  int i;
+
+  /* find all copy-from paths (ADD and MOV alike) */
+
+  svn_boolean_t any_deletion = FALSE;
+  apr_array_header_t *copy_sources
+    = apr_array_make(pool, apr_hash_count(changes), sizeof(const char*));
+
+  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
+    {
+      svn_fs_path_change2_t *change;
+      apr_hash_this(hi, NULL, NULL, (void**)&change);
+
+      if (change->copyfrom_path && change->copyfrom_rev == revision-1)
+        APR_ARRAY_PUSH(copy_sources, const char *)
+          = change->copyfrom_path;
+
+      any_deletion |= is_deletion(change);
+    }
+
+  /* no suitable copy-from or no deletion -> no moves */
+
+  if (!copy_sources->nelts || !any_deletion)
+    return;
+
+  /* identify copy-from paths that have been mentioned exactly once */
+
+  sources = (const char **)copy_sources->elts;
+  qsort(sources, copy_sources->nelts, copy_sources->elt_size,
+        (int (*)(const void *, const void *))svn_sort_compare_paths);
+
+  unique_copy_sources = apr_hash_make(pool);
+  for (i = 0; i < copy_sources->nelts; ++i)
+    if (   (i == 0 || strcmp(sources[i-1], sources[i]))
+        && (i == copy_sources->nelts-1 || strcmp(sources[i+1], sources[i])))
+      {
+        apr_hash_set(unique_copy_sources, sources[i],
+                     APR_HASH_KEY_STRING, sources[i]);
+      }
+
+  /* no unique copy-from paths -> no moves */
+
+  if (!apr_hash_count(unique_copy_sources))
+    return;
+
+  /* Replace all additions, replacements with a unique copy-from path,
+     the correct copy-from rev and a matching deletion in this revision,
+     with moves and move-replacements, respectively. */
+
+  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
+    {
+      const char *key;
+      apr_ssize_t klen;
+      svn_fs_path_change2_t *change, *copy_from_change;
+
+      apr_hash_this(hi, (const void **)&key, &klen, (void**)&change);
+      if (   change->copyfrom_rev != revision-1
+          || !change->copyfrom_path
+          || !apr_hash_get(unique_copy_sources, change->copyfrom_path,
+                           APR_HASH_KEY_STRING))
+        continue;
+
+      copy_from_change = apr_hash_get(changes, change->copyfrom_path,
+                                      APR_HASH_KEY_STRING);
+      if (!copy_from_change || !is_deletion(copy_from_change))
+        continue;
+
+      /* There is a deletion of the ADD's copy-from path in *REVISION*.
+         This can either be the same as in REVISION-1 (o.k.) or must have
+         been replaced by some other node.  However, that would imply that
+         it still got deleted as part of the replacement, i.e. both cases
+         are o.k. */
+
+      switch (change->change_kind)
+        {
+          case svn_fs_path_change_add:
+            change->change_kind = svn_fs_path_change_move;
+            break;
+
+          case svn_fs_path_change_replace:
+            change->change_kind = svn_fs_path_change_movereplace;
+            break;
+
+          default:
+            break;
+        }
+    }
+}
+
+svn_error_t *
+svn_fs_paths_changed3(apr_hash_t **changed_paths_p,
+                      svn_fs_root_t *root,
+                      svn_move_behavior_t move_behavior,
+                      apr_pool_t *pool)
+{
+  SVN_ERR(root->vtable->paths_changed(changed_paths_p, root, pool));
+  switch(move_behavior)
+    {
+      case svn_move_behavior_no_moves:
+        turn_moves_into_copies(*changed_paths_p, pool);
+        break;
+
+      case svn_move_behavior_auto_moves:
+        turn_unique_copies_into_moves(*changed_paths_p, root->rev, pool);
+        break;
+
+      default:
+        break;
+    }
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_fs_paths_changed2(apr_hash_t **changed_paths_p, svn_fs_root_t *root,
                       apr_pool_t *pool)
 {
-  return root->vtable->paths_changed(changed_paths_p, root, pool);
+  return svn_fs_paths_changed3(changed_paths_p, root,
+                               svn_move_behavior_no_moves, pool);
 }
 
 svn_error_t *


Reply via email to