You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2011/10/19 18:06:59 UTC
svn commit: r1186293 -
/subversion/branches/moves-scan-log/subversion/libsvn_client/update.c
Author: stsp
Date: Wed Oct 19 16:06:59 2011
New Revision: 1186293
URL: http://svn.apache.org/viewvc?rev=1186293&view=rev
Log:
On the moves-scan-log branch, add a simple heuristic that allows 'svn update'
to obtain information about server-side moves from the revision log.
Work in progress, review if you're interested, else ignore.
* subversion/libsvn_client/update.c
(scan_moves_log_recevier, scan_moves_log_receiver_baton): New.
(scan_for_server_side_moves): New helper function that scans the
revision log for server-side moves before running the update.
Currently prints results it finds via SVN_DBG, but nothing uses them.
(update_internal): Call scan_for_server_side_moves().
Modified:
subversion/branches/moves-scan-log/subversion/libsvn_client/update.c
Modified: subversion/branches/moves-scan-log/subversion/libsvn_client/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/moves-scan-log/subversion/libsvn_client/update.c?rev=1186293&r1=1186292&r2=1186293&view=diff
==============================================================================
--- subversion/branches/moves-scan-log/subversion/libsvn_client/update.c (original)
+++ subversion/branches/moves-scan-log/subversion/libsvn_client/update.c Wed Oct 19 16:06:59 2011
@@ -160,6 +160,169 @@ is_empty_wc(svn_boolean_t *clean_checkou
return svn_io_dir_close(dir);
}
+struct scan_moves_log_receiver_baton {
+ /* The moved nodes hash to be populated.
+ * Maps moved-from path to moved-to path. */
+ apr_hash_t *moved_nodes;
+} scan_moves_log_receiver_baton;
+
+static svn_error_t *
+scan_moves_log_recevier(void *baton,
+ svn_log_entry_t *log_entry,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_index_t *hi;
+ apr_hash_t *copied_paths = apr_hash_make(scratch_pool); /* copyfrom->path */
+ apr_array_header_t *deleted_paths = apr_array_make(scratch_pool, 0,
+ sizeof(const char *));
+ struct scan_moves_log_receiver_baton *b = baton;
+ int i;
+
+ /* Scan for copied and deleted nodes in this revision. */
+ for (hi = apr_hash_first(scratch_pool, log_entry->changed_paths2);
+ hi; hi = apr_hash_next(hi))
+ {
+ const char *path = svn__apr_hash_index_key(hi);
+ svn_log_changed_path2_t *data = svn__apr_hash_index_val(hi);
+
+ if (data->action == 'A' && data->copyfrom_path)
+ {
+ if (apr_hash_get(copied_paths, data->copyfrom_path,
+ APR_HASH_KEY_STRING))
+ {
+ /* The same copyfrom path appears multiple times. In this
+ * limited and simplified client-side heuristic, this means
+ * that the node was not moved.
+ * Mark this entry as invalid by setting the value to "".
+ * ### Extend this check to ignore copies crossing branch-roots?
+ */
+ apr_hash_set(copied_paths, data->copyfrom_path,
+ APR_HASH_KEY_STRING, "");
+ }
+ else
+ apr_hash_set(copied_paths, data->copyfrom_path,
+ APR_HASH_KEY_STRING, path);
+ }
+ else if (data->action == 'D')
+ APR_ARRAY_PUSH(deleted_paths, const char *) = path;
+ }
+
+ /* If a node was deleted at one location and copied from the deleted
+ * location to a new location within the same revision, put the node
+ * on the moved-nodes list. */
+ for (i = 0; i < deleted_paths->nelts; i++)
+ {
+ const char *deleted_path = APR_ARRAY_IDX(deleted_paths, i, const char *);
+ const char *copied_path = apr_hash_get(copied_paths, deleted_path,
+ APR_HASH_KEY_STRING);
+ if (copied_path && copied_path[0] != '\0')
+ {
+ apr_hash_index_t *hi2;
+ svn_boolean_t first_move = TRUE;
+
+ /* We found a single deleted node which matches the copyfrom
+ * of single a copied node. This is a non-ambiguous move.
+ * In case we previously found a move of the same node,
+ * update the existing entry to point to the node's current
+ * location. */
+ for (hi2 = apr_hash_first(scratch_pool, b->moved_nodes);
+ hi2; hi2 = apr_hash_next(hi2))
+ {
+ const char *moved_from = svn__apr_hash_index_key(hi2);
+ const char *moved_to = svn__apr_hash_index_key(hi2);
+
+ if (strcmp(moved_to, deleted_path))
+ {
+ first_move = FALSE;
+ apr_hash_set(b->moved_nodes, moved_from,
+ APR_HASH_KEY_STRING,
+ apr_pstrdup(apr_hash_pool_get(b->moved_nodes),
+ copied_path));
+ }
+ }
+
+ /* If we haven't seen this node being moved before, add it. */
+ if (first_move)
+ apr_hash_set(b->moved_nodes,
+ apr_pstrdup(apr_hash_pool_get(b->moved_nodes),
+ deleted_path),
+ APR_HASH_KEY_STRING,
+ apr_pstrdup(apr_hash_pool_get(b->moved_nodes),
+ copied_path));
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+scan_for_server_side_moves(apr_hash_t **server_side_moves,
+ const char *anchor_abspath,
+ svn_ra_session_t *ra_session,
+ svn_revnum_t target_rev,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_revnum_t local_min_rev;
+ svn_revnum_t local_max_rev;
+ svn_revnum_t start;
+ svn_revnum_t end;
+ struct scan_moves_log_receiver_baton b;
+ svn_boolean_t adjust_moves_list = FALSE;
+
+ /* Determine the min/max revisions of the working copy. */
+ SVN_ERR(svn_wc__min_max_revisions(&local_min_rev, &local_max_rev,
+ ctx->wc_ctx, anchor_abspath,
+ FALSE, scratch_pool));
+
+ /* Determine the range of revisions we'll have to scan for moves.
+ * We always scan the log forwards, and adjust the resulting moves
+ * map if the target revision is below or within the local min:max range. */
+ if (target_rev == SVN_INVALID_REVNUM || target_rev > local_max_rev)
+ {
+ /* We're updating to HEAD for some other revision newer than
+ * the local max. */
+ start = local_min_rev;
+ end = target_rev;
+ }
+ else if (target_rev < local_min_rev)
+ {
+ /* We're updating down to a revision older than the local max. */
+ start = target_rev;
+ end = local_max_rev;
+ adjust_moves_list = TRUE;
+ }
+ else if (target_rev >= local_min_rev && target_rev <= local_max_rev)
+ {
+ /* We're updating to a revision within the local min:max. */
+ start = local_min_rev;
+ end = local_max_rev;
+ adjust_moves_list = TRUE;
+ }
+
+ b.moved_nodes = apr_hash_make(result_pool);
+ SVN_ERR(svn_ra_get_log2(ra_session, NULL, start, end, 0, TRUE, FALSE, FALSE,
+ apr_array_make(scratch_pool, 0,
+ sizeof(const char *)),
+ scan_moves_log_recevier, &b, scratch_pool));
+#ifdef SVN_DEBUG
+ {
+ apr_hash_index_t *hi;
+ for (hi = apr_hash_first(scratch_pool, b.moved_nodes);
+ hi; hi = apr_hash_next(hi))
+ {
+ const char *moved_from = svn__apr_hash_index_key(hi);
+ const char *moved_to = svn__apr_hash_index_val(hi);
+ SVN_DBG(("found server-side move: '%s' -> '%s'\n",
+ moved_from, moved_to));
+ }
+ }
+#endif
+
+ return SVN_NO_ERROR;
+}
+
/* This is a helper for svn_client__update_internal(), which see for
an explanation of most of these parameters. Some stuff that's
unique is as follows:
@@ -368,6 +531,12 @@ update_internal(svn_revnum_t *result_rev
dfb.target_revision = revnum;
dfb.anchor_url = anchor_url;
+ /* Scan the log within the min:target-revision range for moves which
+ * happened on the server within this range. */
+ SVN_ERR(scan_for_server_side_moves(NULL /* ### TODO */, anchor_abspath,
+ ra_session, revnum, ctx,
+ pool, pool));
+
/* Fetch the update editor. If REVISION is invalid, that's okay;
the RA driver will call editor->set_target_revision later on. */
SVN_ERR(svn_wc_get_update_editor4(&update_editor, &update_edit_baton,