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 *