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 2016/10/13 15:25:17 UTC
svn commit: r1764707 [2/8] - in /subversion/branches/authzperf: ./
build/ac-macros/ build/generator/ build/win32/ notes/meetings/
subversion/bindings/swig/ruby/test/ subversion/include/
subversion/include/private/ subversion/libsvn_client/ subversion/l...
Modified: subversion/branches/authzperf/subversion/libsvn_client/conflicts.c
URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_client/conflicts.c?rev=1764707&r1=1764706&r2=1764707&view=diff
==============================================================================
--- subversion/branches/authzperf/subversion/libsvn_client/conflicts.c (original)
+++ subversion/branches/authzperf/subversion/libsvn_client/conflicts.c Thu Oct 13 15:25:15 2016
@@ -38,6 +38,9 @@
#include "svn_hash.h"
#include "svn_sorts.h"
#include "client.h"
+
+#include "private/svn_diff_tree.h"
+#include "private/svn_ra_private.h"
#include "private/svn_sorts_private.h"
#include "private/svn_token.h"
#include "private/svn_wc_private.h"
@@ -53,6 +56,7 @@
typedef svn_error_t *(*tree_conflict_get_description_func_t)(
const char **change_description,
svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
@@ -60,16 +64,16 @@ typedef svn_error_t *(*tree_conflict_get
* This function may contact the repository. */
typedef svn_error_t *(*tree_conflict_get_details_func_t)(
svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool);
struct svn_client_conflict_t
{
const char *local_abspath;
- svn_client_ctx_t *ctx;
apr_hash_t *prop_conflicts;
/* Indicate which options were chosen to resolve a text or tree conflict
- * on the conflited node. */
+ * on the conflicted node. */
svn_client_conflict_option_id_t resolution_text;
svn_client_conflict_option_id_t resolution_tree;
@@ -112,6 +116,7 @@ struct svn_client_conflict_t
typedef svn_error_t *(*conflict_option_resolve_func_t)(
svn_client_conflict_option_t *option,
svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool);
struct svn_client_conflict_option_t
@@ -122,6 +127,9 @@ struct svn_client_conflict_option_t
svn_client_conflict_t *conflict;
conflict_option_resolve_func_t do_resolve_func;
+ /* The pool this option was allocated from. */
+ apr_pool_t *pool;
+
/* Data which is specific to particular conflicts and options. */
union {
struct {
@@ -235,49 +243,60 @@ static const svn_token_map_t map_conflic
};
/* Describes a server-side move (really a copy+delete within the same
- * revision) which was identified by scanning the revision log. */
+ * revision) which was identified by scanning the revision log.
+ * This structure can represent one or more "chains" of moves, i.e.
+ * multiple move operations which occurred across a range of revisions. */
struct repos_move_info {
- /* The repository relpath the node was moved from. */
- const char *moved_from_repos_relpath;
-
- /* The repository relpath the node was moved to. */
- const char *moved_to_repos_relpath;
-
/* The revision in which this move was committed. */
svn_revnum_t rev;
/* The author who commited the revision in which this move was committed. */
const char *rev_author;
+ /* The repository relpath the node was moved from in this revision. */
+ const char *moved_from_repos_relpath;
+
+ /* The repository relpath the node was moved to in this revision. */
+ const char *moved_to_repos_relpath;
+
/* The copyfrom revision of the moved-to path. */
svn_revnum_t copyfrom_rev;
- /* Prev and next pointers. NULL if no prior or next move exists. */
+ /* Prev pointer. NULL if no prior move exists in the chain. */
struct repos_move_info *prev;
- struct repos_move_info *next;
+
+ /* An array of struct repos_move_info * elements, each representing
+ * a possible way forward in the move chain. NULL if no next move
+ * exists in this chain. If the deleted node was copied only once in
+ * this revision, then this array has only one element and the move
+ * chain does not fork. But if this revision contains multiple copies of
+ * the deleted node, each of these copies appears as an element of this
+ * array, and each element represents a different path the next move
+ * might have taken. */
+ apr_array_header_t *next;
};
-/* Set *RELATED to true if the deleted node at repository relpath
- * DELETED_REPOS_RELPATH@DELETED_REV is ancestrally related to the node at
- * repository relpath COPYFROM_PATH@COPYFROM_REV, else set it to false. */
+/* Set *RELATED to true if the deleted node DELETED_REPOS_RELPATH@DELETED_REV
+ * is an ancestor of the copied node COPYFROM_PATH@COPYFROM_REV.
+ * If CHECK_LAST_CHANGED_REV is non-zero, also ensure that the copied node
+ * is a copy of the deleted node's last-changed revision's content, rather
+ * than a copy of some older content. If it's not, set *RELATED to false. */
static svn_error_t *
check_move_ancestry(svn_boolean_t *related,
+ svn_ra_session_t *ra_session,
const char *repos_root_url,
const char *deleted_repos_relpath,
svn_revnum_t deleted_rev,
const char *copyfrom_path,
svn_revnum_t copyfrom_rev,
- svn_client_ctx_t *ctx,
+ svn_boolean_t check_last_changed_rev,
apr_pool_t *scratch_pool)
{
apr_hash_t *locations;
const char *deleted_url;
const char *deleted_location;
- svn_ra_session_t *ra_session;
- const char *corrected_url;
apr_array_header_t *location_revisions;
-
- *related = FALSE;
+ const char *old_session_url;
location_revisions = apr_array_make(scratch_pool, 1, sizeof(svn_revnum_t));
APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = copyfrom_rev;
@@ -286,11 +305,8 @@ check_move_ancestry(svn_boolean_t *relat
deleted_repos_relpath,
NULL),
scratch_pool);
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url,
- deleted_url, NULL,
- NULL, FALSE, FALSE,
- ctx, scratch_pool,
- scratch_pool));
+ SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session,
+ deleted_url, scratch_pool));
SVN_ERR(svn_ra_get_locations(ra_session, &locations, "",
deleted_rev - 1, location_revisions,
scratch_pool));
@@ -301,9 +317,34 @@ check_move_ancestry(svn_boolean_t *relat
{
if (deleted_location[0] == '/')
deleted_location++;
- *related = (strcmp(deleted_location, copyfrom_path) == 0);
+ if (strcmp(deleted_location, copyfrom_path) != 0)
+ {
+ *related = FALSE;
+ return SVN_NO_ERROR;
+ }
+ }
+ else
+ {
+ *related = FALSE;
+ return SVN_NO_ERROR;
+ }
+
+ if (check_last_changed_rev)
+ {
+ svn_dirent_t *dirent;
+
+ /* Verify that copyfrom_rev >= last-changed revision of the
+ * deleted node. */
+ SVN_ERR(svn_ra_stat(ra_session, "", deleted_rev - 1, &dirent,
+ scratch_pool));
+ if (dirent == NULL || copyfrom_rev < dirent->created_rev)
+ {
+ *related = FALSE;
+ return SVN_NO_ERROR;
+ }
}
+ *related = TRUE;
return SVN_NO_ERROR;
}
@@ -314,15 +355,16 @@ struct copy_info {
};
/* Update MOVES_TABLE and MOVED_PATHS based on information from
- * revision data in LOG_ENTRY, COPIES, and DELETED_PATHS. */
+ * revision data in LOG_ENTRY, COPIES, and DELETED_PATHS.
+ * Use RA_SESSION to perform the necessary requests. */
static svn_error_t *
-find_moves_in_revision(apr_hash_t *moves_table,
+find_moves_in_revision(svn_ra_session_t *ra_session,
+ apr_hash_t *moves_table,
apr_hash_t *moved_paths,
svn_log_entry_t *log_entry,
apr_hash_t *copies,
apr_array_header_t *deleted_paths,
const char *repos_root_url,
- svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
@@ -364,20 +406,15 @@ find_moves_in_revision(apr_hash_t *moves
* from revision log_entry->revision-1 (where the deleted node is
* guaranteed to exist) to the copyfrom-revision, we must end up
* at the copyfrom-path. */
- SVN_ERR(check_move_ancestry(&related, repos_root_url,
+ SVN_ERR(check_move_ancestry(&related, ra_session, repos_root_url,
deleted_repos_relpath,
log_entry->revision,
copy->copyfrom_path,
copy->copyfrom_rev,
- ctx, iterpool));
+ TRUE, iterpool));
if (!related)
continue;
- /* ### TODO:
- * If the node was not copied from the most recent last-changed
- * revision of the deleted node, this is not a move but a
- * "copy from the past + delete". */
-
/* Remember details of this move. */
move = apr_pcalloc(result_pool, sizeof(*move));
move->moved_from_repos_relpath = apr_pstrdup(result_pool,
@@ -398,18 +435,23 @@ find_moves_in_revision(apr_hash_t *moves
/* Tracing back history of the delete-half of the next move
* to the copyfrom-revision of the prior move we must end up
* at the delete-half of the prior move. */
- SVN_ERR(check_move_ancestry(&related, repos_root_url,
+ SVN_ERR(check_move_ancestry(&related, ra_session, repos_root_url,
next_move->moved_from_repos_relpath,
next_move->rev,
move->moved_from_repos_relpath,
move->copyfrom_rev,
- ctx, iterpool));
+ FALSE, iterpool));
if (related)
{
SVN_ERR_ASSERT(move->rev < next_move->rev);
/* Prepend this move to the linked list. */
- move->next = next_move;
+ if (move->next == NULL)
+ move->next = apr_array_make(
+ result_pool, 1,
+ sizeof (struct repos_move_info *));
+ APR_ARRAY_PUSH(move->next,
+ struct repos_move_info *) = next_move;
next_move->prev = move;
}
}
@@ -445,6 +487,7 @@ struct find_deleted_rev_baton
const char *repos_root_url;
const char *repos_uuid;
svn_client_ctx_t *ctx;
+ const char *victim_abspath; /* for notifications */
/* Variables below are results for the caller of svn_ra_get_log2(). */
svn_revnum_t deleted_rev;
@@ -500,14 +543,50 @@ struct find_deleted_rev_baton
/* Temporary map of moved paths to struct repos_move_info.
* Used to link multiple moves of the same node across revisions. */
apr_hash_t *moved_paths;
+
+ /* Extra RA session that can be used to make additional requests. */
+ svn_ra_session_t *extra_ra_session;
};
+/* Find the youngest common ancestor of REPOS_RELPATH1@PEG_REV1 and
+ * REPOS_RELPATH2@PEG_REV2. Return the result in *YCA_LOC.
+ * Set *YCA_LOC to NULL if no common ancestor exists. */
+static svn_error_t *
+find_yca(svn_client__pathrev_t **yca_loc,
+ const char *repos_relpath1,
+ svn_revnum_t peg_rev1,
+ const char *repos_relpath2,
+ svn_revnum_t peg_rev2,
+ const char *repos_root_url,
+ const char *repos_uuid,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_client__pathrev_t *loc1;
+ svn_client__pathrev_t *loc2;
+
+ *yca_loc = NULL;
+
+ loc1 = svn_client__pathrev_create_with_relpath(repos_root_url, repos_uuid,
+ peg_rev1, repos_relpath1,
+ scratch_pool);
+ loc2 = svn_client__pathrev_create_with_relpath(repos_root_url, repos_uuid,
+ peg_rev2, repos_relpath2,
+ scratch_pool);
+ SVN_ERR(svn_client__get_youngest_common_ancestor(yca_loc, loc1, loc2, NULL,
+ ctx, result_pool,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
/* Implements svn_log_entry_receiver_t.
*
* Find the revision in which a node, optionally ancestrally related to the
* node specified via find_deleted_rev_baton, was deleted, When the revision
* was found, store it in BATON->DELETED_REV and abort the log operation
- * by raising SVN_ERR_CANCELLED.
+ * by raising SVN_ERR_CEASE_INVOCATION.
*
* If no such revision can be found, leave BATON->DELETED_REV and
* BATON->REPLACING_NODE_KIND alone.
@@ -520,7 +599,7 @@ struct find_deleted_rev_baton
* works in cases where we do not already know a revision in which the deleted
* node once used to exist.
*
- * If the node node was moved, rather than deleted, return move information
+ * If the node was moved, rather than deleted, return move information
* in BATON->MOVE.
*/
static svn_error_t *
@@ -535,6 +614,18 @@ find_deleted_rev(void *baton,
apr_array_header_t *deleted_paths;
apr_hash_t *copies;
+ if (b->ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+
+ notify = svn_wc_create_notify(
+ b->victim_abspath,
+ svn_wc_notify_tree_conflict_details_progress,
+ scratch_pool),
+ notify->revision = log_entry->revision;
+ b->ctx->notify_func2(b->ctx->notify_baton2, notify, scratch_pool);
+ }
+
/* No paths were changed in this revision. Nothing to do. */
if (! log_entry->changed_paths2)
return SVN_NO_ERROR;
@@ -606,27 +697,32 @@ find_deleted_rev(void *baton,
if (b->related_repos_relpath != NULL &&
b->related_repos_peg_rev != SVN_INVALID_REVNUM)
{
- svn_client__pathrev_t *yca_loc = NULL;
- svn_client__pathrev_t *loc1;
- svn_client__pathrev_t *loc2;
+ svn_client__pathrev_t *yca_loc;
+ svn_error_t *err;
/* We found a deleted node which occupies the correct path.
* To be certain that this is the deleted node we're looking for,
* we must establish whether it is ancestrally related to the
* "related node" specified in our baton. */
- loc1 = svn_client__pathrev_create_with_relpath(
- b->repos_root_url, b->repos_uuid,
- b->related_repos_peg_rev,
- b->related_repos_relpath, iterpool);
- loc2 = svn_client__pathrev_create_with_relpath(
- b->repos_root_url, b->repos_uuid,
- log_entry->revision - 1,
- b->deleted_repos_relpath, iterpool);
- SVN_ERR(svn_client__get_youngest_common_ancestor(&yca_loc,
- loc1, loc2,
- NULL, b->ctx,
- iterpool,
- iterpool));
+ err = find_yca(&yca_loc,
+ b->related_repos_relpath,
+ b->related_repos_peg_rev,
+ b->deleted_repos_relpath,
+ log_entry->revision - 1,
+ b->repos_root_url, b->repos_uuid,
+ b->ctx, iterpool, iterpool);
+ if (err)
+ {
+ /* ### Happens for moves within other moves and copies. */
+ if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ yca_loc = NULL;
+ }
+ else
+ return svn_error_trace(err);
+ }
+
deleted_node_found = (yca_loc != NULL);
}
@@ -649,14 +745,15 @@ find_deleted_rev(void *baton,
svn_pool_destroy(iterpool);
/* Check for moves in this revision */
- SVN_ERR(find_moves_in_revision(b->moves_table, b->moved_paths,
+ SVN_ERR(find_moves_in_revision(b->extra_ra_session,
+ b->moves_table, b->moved_paths,
log_entry, copies, deleted_paths,
- b->repos_root_url, b->ctx,
+ b->repos_root_url,
b->result_pool, scratch_pool));
if (deleted_node_found)
{
/* We're done. Abort the log operation. */
- return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL);
+ return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
}
return SVN_NO_ERROR;
@@ -667,6 +764,7 @@ find_deleted_rev(void *baton,
static svn_error_t *
describe_local_file_node_change(const char **description,
svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
@@ -726,7 +824,7 @@ describe_local_file_node_change(const ch
const char *moved_to_abspath;
SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
- conflict->ctx->wc_ctx,
+ ctx->wc_ctx,
conflict->local_abspath,
scratch_pool,
scratch_pool));
@@ -745,7 +843,7 @@ describe_local_file_node_change(const ch
const char *wcroot_abspath;
SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath,
- conflict->ctx->wc_ctx,
+ ctx->wc_ctx,
conflict->local_abspath,
scratch_pool,
scratch_pool));
@@ -778,7 +876,7 @@ describe_local_file_node_change(const ch
const char *wcroot_abspath;
SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath,
- conflict->ctx->wc_ctx,
+ ctx->wc_ctx,
conflict->local_abspath,
scratch_pool,
scratch_pool));
@@ -800,7 +898,7 @@ describe_local_file_node_change(const ch
const char *moved_from_abspath;
SVN_ERR(svn_wc__node_was_moved_here(&moved_from_abspath, NULL,
- conflict->ctx->wc_ctx,
+ ctx->wc_ctx,
conflict->local_abspath,
scratch_pool,
scratch_pool));
@@ -819,7 +917,7 @@ describe_local_file_node_change(const ch
const char *wcroot_abspath;
SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath,
- conflict->ctx->wc_ctx,
+ ctx->wc_ctx,
conflict->local_abspath,
scratch_pool,
scratch_pool));
@@ -851,7 +949,7 @@ describe_local_file_node_change(const ch
const char *wcroot_abspath;
SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath,
- conflict->ctx->wc_ctx,
+ ctx->wc_ctx,
conflict->local_abspath,
scratch_pool,
scratch_pool));
@@ -879,6 +977,7 @@ describe_local_file_node_change(const ch
static svn_error_t *
describe_local_dir_node_change(const char **description,
svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
@@ -915,8 +1014,7 @@ describe_local_dir_node_change(const cha
case svn_wc_conflict_reason_missing:
if (operation == svn_wc_operation_update ||
operation == svn_wc_operation_switch)
- *description = _("No such directory was found in the working "
- "copy.");
+ *description = _("No such directory was found in the working copy.");
else if (operation == svn_wc_operation_merge)
{
/* ### display deleted revision */
@@ -940,7 +1038,7 @@ describe_local_dir_node_change(const cha
const char *moved_to_abspath;
SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
- conflict->ctx->wc_ctx,
+ ctx->wc_ctx,
conflict->local_abspath,
scratch_pool,
scratch_pool));
@@ -959,7 +1057,7 @@ describe_local_dir_node_change(const cha
const char *wcroot_abspath;
SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath,
- conflict->ctx->wc_ctx,
+ ctx->wc_ctx,
conflict->local_abspath,
scratch_pool,
scratch_pool));
@@ -992,7 +1090,7 @@ describe_local_dir_node_change(const cha
const char *wcroot_abspath;
SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath,
- conflict->ctx->wc_ctx,
+ ctx->wc_ctx,
conflict->local_abspath,
scratch_pool,
scratch_pool));
@@ -1014,7 +1112,7 @@ describe_local_dir_node_change(const cha
const char *moved_from_abspath;
SVN_ERR(svn_wc__node_was_moved_here(&moved_from_abspath, NULL,
- conflict->ctx->wc_ctx,
+ ctx->wc_ctx,
conflict->local_abspath,
scratch_pool,
scratch_pool));
@@ -1033,7 +1131,7 @@ describe_local_dir_node_change(const cha
const char *wcroot_abspath;
SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath,
- conflict->ctx->wc_ctx,
+ ctx->wc_ctx,
conflict->local_abspath,
scratch_pool,
scratch_pool));
@@ -1066,7 +1164,7 @@ describe_local_dir_node_change(const cha
const char *wcroot_abspath;
SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath,
- conflict->ctx->wc_ctx,
+ ctx->wc_ctx,
conflict->local_abspath,
scratch_pool,
scratch_pool));
@@ -1095,14 +1193,14 @@ describe_local_dir_node_change(const cha
* If the node was replaced rather than deleted, set *REPLACING_NODE_KIND to
* the node kind of the replacing node. Else, set it to svn_node_unknown.
* Only request the log for revisions up to END_REV from the server.
- * If the deleted node was moved, provide move information in *MOVE.
- * If the node was not moved,set *MOVE to NULL.
+ * If the deleted node was moved, provide heads of move chains in *MOVES.
+ * If the node was not moved,set *MOVES to NULL.
*/
static svn_error_t *
find_revision_for_suspected_deletion(svn_revnum_t *deleted_rev,
const char **deleted_rev_author,
svn_node_kind_t *replacing_node_kind,
- struct repos_move_info **move,
+ struct apr_array_header_t **moves,
svn_client_conflict_t *conflict,
const char *deleted_basename,
const char *parent_repos_relpath,
@@ -1110,6 +1208,7 @@ find_revision_for_suspected_deletion(svn
svn_revnum_t end_rev,
const char *related_repos_relpath,
svn_revnum_t related_peg_rev,
+ svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
@@ -1121,8 +1220,11 @@ find_revision_for_suspected_deletion(svn
const char *repos_root_url;
const char *repos_uuid;
struct find_deleted_rev_baton b;
+ const char *victim_abspath;
svn_error_t *err;
+ SVN_ERR_ASSERT(start_rev > end_rev);
+
SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, &repos_uuid,
conflict, scratch_pool,
scratch_pool));
@@ -1130,7 +1232,7 @@ find_revision_for_suspected_deletion(svn
scratch_pool);
SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url,
url, NULL, NULL, FALSE, FALSE,
- conflict->ctx, scratch_pool,
+ ctx, scratch_pool,
scratch_pool));
paths = apr_array_make(scratch_pool, 1, sizeof(const char *));
@@ -1139,6 +1241,8 @@ find_revision_for_suspected_deletion(svn
revprops = apr_array_make(scratch_pool, 1, sizeof(const char *));
APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_AUTHOR;
+ victim_abspath = svn_client_conflict_get_local_abspath(conflict);
+ b.victim_abspath = victim_abspath;
b.deleted_repos_relpath = svn_relpath_join(parent_repos_relpath,
deleted_basename, scratch_pool);
b.related_repos_relpath = related_repos_relpath;
@@ -1147,10 +1251,12 @@ find_revision_for_suspected_deletion(svn
b.replacing_node_kind = svn_node_unknown;
b.repos_root_url = repos_root_url;
b.repos_uuid = repos_uuid;
- b.ctx = conflict->ctx;
+ b.ctx = ctx;
b.moves_table = apr_hash_make(result_pool);
b.moved_paths = apr_hash_make(scratch_pool);
b.result_pool = result_pool;
+ SVN_ERR(svn_ra__dup_session(&b.extra_ra_session, ra_session, NULL,
+ scratch_pool, scratch_pool));
err = svn_ra_get_log2(ra_session, paths, start_rev, end_rev,
0, /* no limit */
@@ -1162,8 +1268,9 @@ find_revision_for_suspected_deletion(svn
scratch_pool);
if (err)
{
- if (err->apr_err == SVN_ERR_CANCELLED &&
+ if (err->apr_err == SVN_ERR_CEASE_INVOCATION &&
b.deleted_rev != SVN_INVALID_REVNUM)
+
{
/* Log operation was aborted because we found deleted rev. */
svn_error_clear(err);
@@ -1179,41 +1286,46 @@ find_revision_for_suspected_deletion(svn
*deleted_rev = SVN_INVALID_REVNUM;
*deleted_rev_author = NULL;
*replacing_node_kind = svn_node_unknown;
- *move = NULL;
+ *moves = NULL;
return SVN_NO_ERROR;
}
else
{
- apr_array_header_t *moves;
+ apr_array_header_t *all_moves_in_deleted_rev;
*deleted_rev = b.deleted_rev;
*deleted_rev_author = b.deleted_rev_author;
*replacing_node_kind = b.replacing_node_kind;
- /* Look for a move which affects the deleted node. */
- moves = apr_hash_get(b.moves_table, &b.deleted_rev,
- sizeof(svn_revnum_t));
- if (moves)
+ /* Look for a moves which affect the deleted node. */
+ all_moves_in_deleted_rev = apr_hash_get(b.moves_table, &b.deleted_rev,
+ sizeof(svn_revnum_t));
+ if (all_moves_in_deleted_rev)
{
int i;
- for (i = 0; i < moves->nelts; i++)
+ *moves = apr_array_make(result_pool, 1, sizeof(const char *));
+ for (i = 0; i < all_moves_in_deleted_rev->nelts; i++)
{
struct repos_move_info *this_move;
- this_move = APR_ARRAY_IDX(moves, i, struct repos_move_info *);
+ this_move = APR_ARRAY_IDX(all_moves_in_deleted_rev, i,
+ struct repos_move_info *);
if (strcmp(b.deleted_repos_relpath,
this_move->moved_from_repos_relpath) == 0)
{
/* Because b->moves_table lives in result_pool
* there is no need to deep-copy here. */
- *move = this_move;
- break;
+ APR_ARRAY_PUSH(*moves, struct repos_move_info *) = this_move;
}
}
+
+ /* If we didn't find any applicable moves, return NULL. */
+ if ((*moves)->nelts == 0)
+ *moves = NULL;
}
else
- *move = NULL;
+ *moves = NULL;
}
return SVN_NO_ERROR;
@@ -1228,14 +1340,19 @@ struct conflict_tree_local_missing_detai
/* Author who committed DELETED_REV. */
const char *deleted_rev_author;
- /* Move information. If not NULL, the first move happened in DELETED_REV.
- * Follow MOVE->NEXT for subsequent moves in later revisions. */
- struct repos_move_info *move;
+ /* The path which was deleted relative to the repository root. */
+ const char *deleted_repos_relpath;
+
+ /* Move information. If not NULL, this is an array of repos_move_info *
+ * elements. Each element is the head of a move chain which starts in
+ * DELETED_REV. */
+ apr_array_header_t *moves;
};
/* Implements tree_conflict_get_details_func_t. */
static svn_error_t *
conflict_tree_get_details_local_missing(svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
const char *old_repos_relpath;
@@ -1248,7 +1365,9 @@ conflict_tree_get_details_local_missing(
svn_node_kind_t replacing_node_kind;
const char *deleted_basename;
struct conflict_tree_local_missing_details *details;
- struct repos_move_info *move;
+ apr_array_header_t *moves;
+ const char *related_repos_relpath;
+ svn_revnum_t related_peg_rev;
/* We only handle merges here. */
if (svn_client_conflict_get_operation(conflict) != svn_wc_operation_merge)
@@ -1268,18 +1387,95 @@ conflict_tree_get_details_local_missing(
scratch_pool);
SVN_ERR(svn_wc__node_get_repos_info(NULL, &parent_repos_relpath,
NULL, NULL,
- conflict->ctx->wc_ctx,
+ ctx->wc_ctx,
svn_dirent_dirname(
conflict->local_abspath,
scratch_pool),
scratch_pool,
scratch_pool));
+
+ /* Pick the younger incoming node as our 'related node' which helps
+ * pin-pointing the deleted conflict victim in history. */
+ related_repos_relpath =
+ (old_rev < new_rev ? new_repos_relpath : old_repos_relpath);
+ related_peg_rev = (old_rev < new_rev ? new_rev : old_rev);
+
+ /* Make sure we're going to search the related node in a revision where
+ * it exists. The younger incoming node might have been deleted in HEAD. */
+ if (related_repos_relpath != NULL && related_peg_rev != SVN_INVALID_REVNUM)
+ {
+ const char *repos_root_url;
+ const char *repos_uuid;
+ const char *related_url;
+ const char *corrected_url;
+ svn_node_kind_t related_node_kind;
+ svn_ra_session_t *ra_session;
+
+ SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url,
+ &repos_uuid,
+ conflict,
+ scratch_pool, scratch_pool));
+ related_url = svn_path_url_add_component2(repos_root_url,
+ related_repos_relpath,
+ scratch_pool);
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session,
+ &corrected_url,
+ related_url, NULL,
+ NULL,
+ FALSE,
+ FALSE,
+ ctx,
+ scratch_pool,
+ scratch_pool));
+ SVN_ERR(svn_ra_check_path(ra_session, "", related_peg_rev,
+ &related_node_kind, scratch_pool));
+ if (related_node_kind == svn_node_none)
+ {
+ svn_revnum_t related_deleted_rev;
+ const char *related_deleted_rev_author;
+ svn_node_kind_t related_replacing_node_kind;
+ const char *related_basename;
+ const char *related_parent_repos_relpath;
+ apr_array_header_t *related_moves;
+ const char *older_incoming_repos_relpath;
+ svn_revnum_t older_incoming_peg_rev;
+
+ /* Looks like the younger incoming node, which we'd like to use as
+ * our 'related node', was also deleted. Try to find its deleted
+ * revision so we can calculate a peg revision at which it exists.
+ * The younger incoming node is related to the older incoming node,
+ * so we can use the older incoming node to guide us in our search. */
+ related_basename = svn_relpath_basename(related_repos_relpath,
+ scratch_pool);
+ related_parent_repos_relpath =
+ svn_relpath_dirname(related_repos_relpath, scratch_pool);
+ older_incoming_repos_relpath =
+ (old_rev < new_rev ? old_repos_relpath : new_repos_relpath);
+ older_incoming_peg_rev = (old_rev < new_rev ? old_rev : new_rev);
+ SVN_ERR(find_revision_for_suspected_deletion(
+ &related_deleted_rev, &related_deleted_rev_author,
+ &related_replacing_node_kind, &related_moves,
+ conflict, related_basename,
+ related_parent_repos_relpath,
+ old_rev < new_rev ? new_rev : old_rev, 0,
+ older_incoming_repos_relpath, older_incoming_peg_rev,
+ ctx, conflict->pool, scratch_pool));
+
+ /* If we can't find a related node, bail. */
+ if (related_deleted_rev == SVN_INVALID_REVNUM)
+ return SVN_NO_ERROR;
+
+ /* The node should exist in the revision before it was deleted. */
+ related_peg_rev = related_deleted_rev - 1;
+ }
+ }
+
SVN_ERR(find_revision_for_suspected_deletion(
- &deleted_rev, &deleted_rev_author, &replacing_node_kind, &move,
+ &deleted_rev, &deleted_rev_author, &replacing_node_kind, &moves,
conflict, deleted_basename, parent_repos_relpath,
old_rev < new_rev ? new_rev : old_rev, 0,
- old_rev < new_rev ? new_repos_relpath : old_repos_relpath,
- old_rev < new_rev ? new_rev : old_rev,
+ related_repos_relpath,
+ related_peg_rev, ctx,
conflict->pool, scratch_pool));
if (deleted_rev == SVN_INVALID_REVNUM)
@@ -1288,7 +1484,10 @@ conflict_tree_get_details_local_missing(
details = apr_pcalloc(conflict->pool, sizeof(*details));
details->deleted_rev = deleted_rev;
details->deleted_rev_author = deleted_rev_author;
- details->move = move;
+ details->deleted_repos_relpath = svn_relpath_join(parent_repos_relpath,
+ deleted_basename,
+ conflict->pool);
+ details->moves = moves;
conflict->tree_conflict_local_details = details;
@@ -1359,24 +1558,31 @@ describe_local_none_node_change(const ch
return SVN_NO_ERROR;
}
-/* Append a description of all moves in the MOVE chain to DESCRIPTION. */
+/* Append a description of a move chain beginning at NEXT to DESCRIPTION. */
static const char *
append_moved_to_chain_description(const char *description,
- struct repos_move_info *move,
+ apr_array_header_t *next,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- if (move == NULL)
+ if (next == NULL)
return description;
- while (move)
+ while (next)
{
+ struct repos_move_info *move;
+
+ /* Describe the first possible move chain only. Adding multiple chains
+ * to the description would just be confusing. The user may select a
+ * different move destination while resolving the conflict. */
+ move = APR_ARRAY_IDX(next, 0, struct repos_move_info *);
+
description = apr_psprintf(scratch_pool,
_("%s\nAnd then moved away to '^/%s' by "
"%s in r%ld."),
description, move->moved_to_repos_relpath,
move->rev_author, move->rev);
- move = move->next;
+ next = move->next;
}
return apr_pstrdup(result_pool, description);
@@ -1386,6 +1592,7 @@ append_moved_to_chain_description(const
static svn_error_t *
conflict_tree_get_local_description_generic(const char **description,
svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
@@ -1399,11 +1606,11 @@ conflict_tree_get_local_description_gene
{
case svn_node_file:
case svn_node_symlink:
- SVN_ERR(describe_local_file_node_change(description, conflict,
+ SVN_ERR(describe_local_file_node_change(description, conflict, ctx,
result_pool, scratch_pool));
break;
case svn_node_dir:
- SVN_ERR(describe_local_dir_node_change(description, conflict,
+ SVN_ERR(describe_local_dir_node_change(description, conflict, ctx,
result_pool, scratch_pool));
break;
case svn_node_none:
@@ -1420,6 +1627,7 @@ conflict_tree_get_local_description_gene
static svn_error_t *
conflict_tree_get_description_local_missing(const char **description,
svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
@@ -1428,19 +1636,23 @@ conflict_tree_get_description_local_miss
details = conflict->tree_conflict_local_details;
if (details == NULL)
return svn_error_trace(conflict_tree_get_local_description_generic(
- description, conflict, result_pool, scratch_pool));
+ description, conflict, ctx,
+ result_pool, scratch_pool));
- if (details->move)
+ if (details->moves)
{
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
*description = apr_psprintf(
result_pool,
_("No such file or directory was found in the "
"merge target working copy.\nThe item was "
"moved away to '^/%s' in r%ld by %s."),
- details->move->moved_to_repos_relpath,
- details->move->rev, details->move->rev_author);
+ move->moved_to_repos_relpath,
+ move->rev, move->rev_author);
*description = append_moved_to_chain_description(*description,
- details->move->next,
+ move->next,
result_pool,
scratch_pool);
}
@@ -1448,8 +1660,9 @@ conflict_tree_get_description_local_miss
*description = apr_psprintf(
result_pool,
_("No such file or directory was found in the "
- "merge target working copy.\nThe item was "
- "deleted in r%ld by %s."),
+ "merge target working copy.\n'^/%s' was deleted "
+ "in r%ld by %s."),
+ details->deleted_repos_relpath,
details->deleted_rev, details->deleted_rev_author);
return SVN_NO_ERROR;
@@ -1693,6 +1906,7 @@ static svn_error_t *
conflict_tree_get_incoming_description_generic(
const char **incoming_change_description,
svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
@@ -1700,11 +1914,9 @@ conflict_tree_get_incoming_description_g
svn_node_kind_t incoming_kind;
svn_wc_conflict_action_t conflict_action;
svn_wc_operation_t conflict_operation;
- svn_node_kind_t conflict_node_kind;
conflict_action = svn_client_conflict_get_incoming_change(conflict);
conflict_operation = svn_client_conflict_get_operation(conflict);
- conflict_node_kind = svn_client_conflict_tree_get_victim_node_kind(conflict);
/* Determine the node kind of the incoming change. */
incoming_kind = svn_node_unknown;
@@ -1767,10 +1979,36 @@ struct conflict_tree_incoming_delete_det
/* New node kind for a replaced node. This is svn_node_none for deletions. */
svn_node_kind_t replacing_node_kind;
- /* Move information. If not NULL, the first move happened in DELETED_REV
- * or in ADDED_REV (in which case moves should be interpreted in reverse).
- * Follow MOVE->NEXT for subsequent moves in later revisions. */
- struct repos_move_info *move;
+ /* Move information. If not NULL, this is an array of repos_move_info *
+ * elements. Each element is the head of a move chain which starts in
+ * DELETED_REV or in ADDED_REV (in which case moves should be interpreted
+ * in reverse). */
+ apr_array_header_t *moves;
+
+ /* A map of repos_relpaths and working copy nodes for an incoming move.
+ *
+ * Each key is a "const char *" repository relpath corresponding to a
+ * possible repository-side move destination node in the revision which
+ * is the target revision in case of update and switch, or the merge-right
+ * revision in case of a merge.
+ *
+ * Each value is an apr_array_header_t *.
+ * Each array consists of "const char *" absolute paths to working copy
+ * nodes which correspond to the repository node selected by the map key.
+ * Each such working copy node is a potential local move target which can
+ * be chosen to "follow" the incoming move when resolving a tree conflict.
+ *
+ * This may be an empty hash map in case if there is no move target path
+ * in the working copy. */
+ apr_hash_t *wc_move_targets;
+
+ /* The preferred move target repository relpath. This is our key into
+ * the WC_MOVE_TARGETS map above (can be overridden by the user). */
+ const char *move_target_repos_relpath;
+
+ /* The current index into the list of working copy nodes corresponding to
+ * MOVE_TARGET_REPOS_REPLATH (can be overridden by the user). */
+ int wc_move_target_idx;
};
static const char *
@@ -1786,62 +2024,180 @@ describe_incoming_deletion_upon_update(
details->replacing_node_kind == svn_node_symlink)
{
if (victim_node_kind == svn_node_dir)
- return apr_psprintf(result_pool,
- _("Directory updated from r%ld to r%ld was "
- "replaced with a file by %s in r%ld."),
- old_rev, new_rev,
- details->rev_author, details->deleted_rev);
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Directory updated from r%ld to r%ld was "
+ "replaced with a file by %s in r%ld."),
+ old_rev, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced directory was moved to "
+ "'^/%s'."), description,
+ move->moved_to_repos_relpath);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
else if (victim_node_kind == svn_node_file ||
victim_node_kind == svn_node_symlink)
- return apr_psprintf(result_pool,
- _("File updated from r%ld to r%ld was replaced "
- "with a file from another line of history by "
- "%s in r%ld."),
- old_rev, new_rev,
- details->rev_author, details->deleted_rev);
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("File updated from r%ld to r%ld was replaced "
+ "with a file from another line of history by "
+ "%s in r%ld."),
+ old_rev, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced file was moved to '^/%s'."),
+ description,
+ move->moved_to_repos_relpath);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
else
- return apr_psprintf(result_pool,
- _("Item updated from r%ld to r%ld was replaced "
- "with a file by %s in r%ld."), old_rev, new_rev,
- details->rev_author, details->deleted_rev);
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Item updated from r%ld to r%ld was replaced "
+ "with a file by %s in r%ld."), old_rev, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced item was moved to '^/%s'."),
+ description,
+ move->moved_to_repos_relpath);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
}
else if (details->replacing_node_kind == svn_node_dir)
{
if (victim_node_kind == svn_node_dir)
- return apr_psprintf(result_pool,
- _("Directory updated from r%ld to r%ld was "
- "replaced with a directory from another line "
- "of history by %s in r%ld."),
- old_rev, new_rev,
- details->rev_author, details->deleted_rev);
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Directory updated from r%ld to r%ld was "
+ "replaced with a directory from another line "
+ "of history by %s in r%ld."),
+ old_rev, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced directory was moved to "
+ "'^/%s'."), description,
+ move->moved_to_repos_relpath);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
else if (victim_node_kind == svn_node_file ||
victim_node_kind == svn_node_symlink)
- return apr_psprintf(result_pool,
- _("Directory updated from r%ld to r%ld was "
- "replaced with a file by %s in r%ld."),
- old_rev, new_rev,
- details->rev_author, details->deleted_rev);
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("File updated from r%ld to r%ld was "
+ "replaced with a directory by %s in r%ld."),
+ old_rev, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced file was moved to '^/%s'."),
+ description,
+ move->moved_to_repos_relpath);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
else
- return apr_psprintf(result_pool,
- _("Item updated from r%ld to r%ld was replaced "
- "by %s in r%ld."), old_rev, new_rev,
- details->rev_author, details->deleted_rev);
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Item updated from r%ld to r%ld was replaced "
+ "by %s in r%ld."), old_rev, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced item was moved to '^/%s'."),
+ description,
+ move->moved_to_repos_relpath);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
}
else
{
if (victim_node_kind == svn_node_dir)
{
- if (details->move)
+ if (details->moves)
{
- const char *description =
+ const char *description;
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
apr_psprintf(result_pool,
_("Directory updated from r%ld to r%ld was "
"moved to '^/%s' by %s in r%ld."),
old_rev, new_rev,
- details->move->moved_to_repos_relpath,
+ move->moved_to_repos_relpath,
details->rev_author, details->deleted_rev);
return append_moved_to_chain_description(description,
- details->move->next,
+ move->next,
result_pool,
scratch_pool);
}
@@ -1855,16 +2211,20 @@ describe_incoming_deletion_upon_update(
else if (victim_node_kind == svn_node_file ||
victim_node_kind == svn_node_symlink)
{
- if (details->move)
+ if (details->moves)
{
- const char *description =
+ struct repos_move_info *move;
+ const char *description;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
apr_psprintf(result_pool,
_("File updated from r%ld to r%ld was moved "
"to '^/%s' by %s in r%ld."), old_rev, new_rev,
- details->move->moved_to_repos_relpath,
+ move->moved_to_repos_relpath,
details->rev_author, details->deleted_rev);
return append_moved_to_chain_description(description,
- details->move->next,
+ move->next,
result_pool,
scratch_pool);
}
@@ -1876,16 +2236,20 @@ describe_incoming_deletion_upon_update(
}
else
{
- if (details->move)
+ if (details->moves)
{
- const char *description =
+ const char *description;
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
apr_psprintf(result_pool,
_("Item updated from r%ld to r%ld was moved "
"to '^/%s' by %s in r%ld."), old_rev, new_rev,
- details->move->moved_to_repos_relpath,
+ move->moved_to_repos_relpath,
details->rev_author, details->deleted_rev);
return append_moved_to_chain_description(description,
- details->move->next,
+ move->next,
result_pool,
scratch_pool);
}
@@ -1992,78 +2356,196 @@ describe_incoming_deletion_upon_switch(
details->replacing_node_kind == svn_node_symlink)
{
if (victim_node_kind == svn_node_dir)
- return apr_psprintf(result_pool,
- _("Directory switched from\n"
- "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
- "was replaced with a file by %s in r%ld."),
- old_repos_relpath, old_rev,
- new_repos_relpath, new_rev,
- details->rev_author, details->deleted_rev);
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Directory switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was replaced with a file by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced directory was moved "
+ "to '^/%s'."), description,
+ move->moved_to_repos_relpath);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
else if (victim_node_kind == svn_node_file ||
victim_node_kind == svn_node_symlink)
- return apr_psprintf(result_pool,
- _("File switched from\n"
- "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
- "replaced with a file from another line of "
- "history by %s in r%ld."),
- old_repos_relpath, old_rev,
- new_repos_relpath, new_rev,
- details->rev_author, details->deleted_rev);
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("File switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "replaced with a file from another line of "
+ "history by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced file was moved to '^/%s'."),
+ description,
+ move->moved_to_repos_relpath);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
else
- return apr_psprintf(result_pool,
- _("Item switched from\n"
- "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
- "replaced with a file by %s in r%ld."),
- old_repos_relpath, old_rev,
- new_repos_relpath, new_rev,
- details->rev_author, details->deleted_rev);
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Item switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "replaced with a file by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced item was moved to '^/%s'."),
+ description,
+ move->moved_to_repos_relpath);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
}
else if (details->replacing_node_kind == svn_node_dir)
{
if (victim_node_kind == svn_node_dir)
- return apr_psprintf(result_pool,
- _("Directory switched from\n"
- "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
- "was replaced with a directory from another "
- "line of history by %s in r%ld."),
- old_repos_relpath, old_rev,
- new_repos_relpath, new_rev,
- details->rev_author, details->deleted_rev);
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Directory switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was replaced with a directory from another "
+ "line of history by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced directory was moved to "
+ "'^/%s'."), description,
+ move->moved_to_repos_relpath);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
else if (victim_node_kind == svn_node_file ||
victim_node_kind == svn_node_symlink)
- return apr_psprintf(result_pool,
- _("File switched from\n"
- "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
- "was replaced with a directory by %s in r%ld."),
- old_repos_relpath, old_rev,
- new_repos_relpath, new_rev,
- details->rev_author, details->deleted_rev);
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("File switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was replaced with a directory by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced file was moved to '^/%s'."),
+ description,
+ move->moved_to_repos_relpath);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
else
- return apr_psprintf(result_pool,
- _("Item switched from\n"
- "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
- "replaced with a directory by %s in r%ld."),
- old_repos_relpath, old_rev,
- new_repos_relpath, new_rev,
- details->rev_author, details->deleted_rev);
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Item switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "replaced with a directory by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced item was moved to '^/%s'."),
+ description,
+ move->moved_to_repos_relpath);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
}
else
{
if (victim_node_kind == svn_node_dir)
{
- if (details->move)
+ if (details->moves)
{
- const char *description =
+ struct repos_move_info *move;
+ const char *description;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
apr_psprintf(result_pool,
_("Directory switched from\n"
"'^/%s@%ld'\nto\n'^/%s@%ld'\n"
"was moved to '^/%s' by %s in r%ld."),
old_repos_relpath, old_rev,
new_repos_relpath, new_rev,
- details->move->moved_to_repos_relpath,
+ move->moved_to_repos_relpath,
details->rev_author, details->deleted_rev);
return append_moved_to_chain_description(description,
- details->move->next,
+ move->next,
result_pool,
scratch_pool);
}
@@ -2079,19 +2561,23 @@ describe_incoming_deletion_upon_switch(
else if (victim_node_kind == svn_node_file ||
victim_node_kind == svn_node_symlink)
{
- if (details->move)
+ if (details->moves)
{
- const char *description =
+ struct repos_move_info *move;
+ const char *description;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
apr_psprintf(result_pool,
_("File switched from\n"
"'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
"moved to '^/%s' by %s in r%ld."),
old_repos_relpath, old_rev,
new_repos_relpath, new_rev,
- details->move->moved_to_repos_relpath,
+ move->moved_to_repos_relpath,
details->rev_author, details->deleted_rev);
return append_moved_to_chain_description(description,
- details->move->next,
+ move->next,
result_pool,
scratch_pool);
}
@@ -2106,19 +2592,23 @@ describe_incoming_deletion_upon_switch(
}
else
{
- if (details->move)
+ if (details->moves)
{
- const char *description =
+ struct repos_move_info *move;
+ const char *description;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
apr_psprintf(result_pool,
_("Item switched from\n"
"'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
"moved to '^/%s' by %s in r%ld."),
old_repos_relpath, old_rev,
new_repos_relpath, new_rev,
- details->move->moved_to_repos_relpath,
+ move->moved_to_repos_relpath,
details->rev_author, details->deleted_rev);
return append_moved_to_chain_description(description,
- details->move->next,
+ move->next,
result_pool,
scratch_pool);
}
@@ -2253,23 +2743,61 @@ describe_incoming_deletion_upon_merge(
details->replacing_node_kind == svn_node_symlink)
{
if (victim_node_kind == svn_node_dir)
- return apr_psprintf(result_pool,
- _("Directory merged from\n"
- "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
- "was replaced with a file by %s in r%ld."),
- old_repos_relpath, old_rev,
- new_repos_relpath, new_rev,
- details->rev_author, details->deleted_rev);
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Directory merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was replaced with a file by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced directory was moved to "
+ "'^/%s'."), description,
+ move->moved_to_repos_relpath);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
else if (victim_node_kind == svn_node_file ||
victim_node_kind == svn_node_symlink)
- return apr_psprintf(result_pool,
- _("File merged from\n"
- "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
- "replaced with a file from another line of "
- "history by %s in r%ld."),
- old_repos_relpath, old_rev,
- new_repos_relpath, new_rev,
- details->rev_author, details->deleted_rev);
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("File merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "replaced with a file from another line of "
+ "history by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced file was moved to '^/%s'."),
+ description,
+ move->moved_to_repos_relpath);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
else
return apr_psprintf(result_pool,
_("Item merged from\n"
@@ -2282,49 +2810,110 @@ describe_incoming_deletion_upon_merge(
else if (details->replacing_node_kind == svn_node_dir)
{
if (victim_node_kind == svn_node_dir)
- return apr_psprintf(result_pool,
- _("Directory merged from\n"
- "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
- "was replaced with a directory from another "
- "line of history by %s in r%ld."),
- old_repos_relpath, old_rev,
- new_repos_relpath, new_rev,
- details->rev_author, details->deleted_rev);
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Directory merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was replaced with a directory from another "
+ "line of history by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced directory was moved to "
+ "'^/%s'."), description,
+ move->moved_to_repos_relpath);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
else if (victim_node_kind == svn_node_file ||
victim_node_kind == svn_node_symlink)
- return apr_psprintf(result_pool,
- _("File merged from\n"
- "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
- "was replaced with a directory by %s in r%ld."),
- old_repos_relpath, old_rev,
- new_repos_relpath, new_rev,
- details->rev_author, details->deleted_rev);
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("File merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was replaced with a directory by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced file was moved to '^/%s'."),
+ description,
+ move->moved_to_repos_relpath);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
else
- return apr_psprintf(result_pool,
- _("Item merged from\n"
- "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
- "replaced with a directory by %s in r%ld."),
- old_repos_relpath, old_rev,
- new_repos_relpath, new_rev,
- details->rev_author, details->deleted_rev);
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Item merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "replaced with a directory by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced item was moved to '^/%s'."),
+ description,
+ move->moved_to_repos_relpath);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
}
else
{
if (victim_node_kind == svn_node_dir)
{
- if (details->move)
+ if (details->moves)
{
- const char *description =
+ struct repos_move_info *move;
+ const char *description;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
apr_psprintf(result_pool,
_("Directory merged from\n"
"'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
"moved to '^/%s' by %s in r%ld."),
old_repos_relpath, old_rev,
new_repos_relpath, new_rev,
- details->move->moved_to_repos_relpath,
+ move->moved_to_repos_relpath,
details->rev_author, details->deleted_rev);
return append_moved_to_chain_description(description,
- details->move->next,
+ move->next,
result_pool,
scratch_pool);
}
@@ -2340,19 +2929,23 @@ describe_incoming_deletion_upon_merge(
else if (victim_node_kind == svn_node_file ||
victim_node_kind == svn_node_symlink)
{
- if (details->move)
+ if (details->moves)
{
- const char *description =
+ struct repos_move_info *move;
+ const char *description;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
apr_psprintf(result_pool,
_("File merged from\n"
"'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
"moved to '^/%s' by %s in r%ld."),
old_repos_relpath, old_rev,
new_repos_relpath, new_rev,
- details->move->moved_to_repos_relpath,
+ move->moved_to_repos_relpath,
details->rev_author, details->deleted_rev);
return append_moved_to_chain_description(description,
- details->move->next,
+ move->next,
result_pool,
scratch_pool);
}
@@ -2367,19 +2960,23 @@ describe_incoming_deletion_upon_merge(
}
else
{
- if (details->move)
+ if (details->moves)
{
- const char *description =
+ struct repos_move_info *move;
+ const char *description;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
apr_psprintf(result_pool,
_("Item merged from\n"
"'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
"moved to '^/%s' by %s in r%ld."),
old_repos_relpath, old_rev,
new_repos_relpath, new_rev,
- details->move->moved_to_repos_relpath,
+ move->moved_to_repos_relpath,
details->rev_author, details->deleted_rev);
return append_moved_to_chain_description(description,
- details->move->next,
+ move->next,
result_pool,
scratch_pool);
}
@@ -2501,6 +3098,7 @@ static svn_error_t *
conflict_tree_get_description_incoming_delete(
const char **incoming_change_description,
svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
@@ -2516,7 +3114,7 @@ conflict_tree_get_description_incoming_d
if (conflict->tree_conflict_incoming_details == NULL)
return svn_error_trace(conflict_tree_get_incoming_description_generic(
incoming_change_description,
- conflict, result_pool, scratch_pool));
+ conflict, ctx, result_pool, scratch_pool));
conflict_operation = svn_client_conflict_get_operation(conflict);
victim_node_kind = svn_client_conflict_tree_get_victim_node_kind(conflict);
@@ -2599,6 +3197,8 @@ conflict_tree_get_description_incoming_d
/* Baton for find_added_rev(). */
struct find_added_rev_baton
{
+ const char *victim_abspath;
+ svn_client_ctx_t *ctx;
svn_revnum_t added_rev;
const char *repos_relpath;
const char *parent_repos_relpath;
@@ -2617,6 +3217,18 @@ find_added_rev(svn_location_segment_t *s
{
struct find_added_rev_baton *b = baton;
+ if (b->ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+
+ notify = svn_wc_create_notify(
+ b->victim_abspath,
+ svn_wc_notify_tree_conflict_details_progress,
+ scratch_pool),
+ notify->revision = segment->range_start;
+ b->ctx->notify_func2(b->ctx->notify_baton2, notify, scratch_pool);
+ }
+
if (segment->path) /* not interested in gaps */
{
if (b->parent_repos_relpath == NULL ||
@@ -2641,6 +3253,7 @@ get_incoming_delete_details_for_reverse_
svn_revnum_t old_rev,
svn_revnum_t new_rev,
svn_client_ctx_t *ctx,
+ const char *victim_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
@@ -2662,15 +3275,19 @@ get_incoming_delete_details_for_reverse_
scratch_pool));
*details = apr_pcalloc(result_pool, sizeof(**details));
+ b.ctx = ctx;
+ b.victim_abspath = victim_abspath;
b.added_rev = SVN_INVALID_REVNUM;
b.repos_relpath = NULL;
b.parent_repos_relpath = NULL;
b.pool = scratch_pool;
+
/* Figure out when this node was added. */
SVN_ERR(svn_ra_get_location_segments(ra_session, "", old_rev,
old_rev, new_rev,
find_added_rev, &b,
scratch_pool));
+
SVN_ERR(svn_ra_rev_prop(ra_session, b.added_rev,
SVN_PROP_REVISION_AUTHOR,
&author_revprop, scratch_pool));
@@ -2700,6 +3317,7 @@ get_incoming_delete_details_for_reverse_
* Find the revision in which the victim was deleted in the repository. */
static svn_error_t *
conflict_tree_get_details_incoming_delete(svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
const char *old_repos_relpath;
[... 4054 lines stripped ...]