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 2017/10/09 11:26:08 UTC

svn commit: r1811552 [1/5] - in /subversion/branches/shelve: ./ subversion/include/ subversion/include/private/ subversion/libsvn_client/ subversion/libsvn_ra_serf/ subversion/libsvn_repos/ subversion/libsvn_subr/ subversion/libsvn_subr/lz4/ subversion...

Author: julianfoad
Date: Mon Oct  9 11:26:07 2017
New Revision: 1811552

URL: http://svn.apache.org/viewvc?rev=1811552&view=rev
Log:
On the 'shelve' branch: Catch up with trunk@1811551.

Added:
    subversion/branches/shelve/subversion/libsvn_subr/utf8proc/LICENSE.md
      - copied unchanged from r1811551, subversion/trunk/subversion/libsvn_subr/utf8proc/LICENSE.md
    subversion/branches/shelve/subversion/libsvn_subr/utf8proc/NEWS.md
      - copied unchanged from r1811551, subversion/trunk/subversion/libsvn_subr/utf8proc/NEWS.md
    subversion/branches/shelve/subversion/libsvn_subr/utf8proc/README.md
      - copied unchanged from r1811551, subversion/trunk/subversion/libsvn_subr/utf8proc/README.md
    subversion/branches/shelve/subversion/libsvn_subr/utf8proc/lump.md
      - copied unchanged from r1811551, subversion/trunk/subversion/libsvn_subr/utf8proc/lump.md
Removed:
    subversion/branches/shelve/subversion/libsvn_subr/utf8proc/LICENSE
    subversion/branches/shelve/subversion/libsvn_subr/utf8proc/README
    subversion/branches/shelve/subversion/tests/cmdline/upgrade_tests_data/format_12a.tar.bz2
    subversion/branches/shelve/subversion/tests/cmdline/upgrade_tests_data/format_12b.tar.bz2
    subversion/branches/shelve/subversion/tests/cmdline/upgrade_tests_data/format_13.tar.bz2
    subversion/branches/shelve/subversion/tests/cmdline/upgrade_tests_data/format_14.tar.bz2
    subversion/branches/shelve/subversion/tests/cmdline/upgrade_tests_data/format_15.tar.bz2
    subversion/branches/shelve/subversion/tests/cmdline/upgrade_tests_data/format_16.tar.bz2
    subversion/branches/shelve/subversion/tests/cmdline/upgrade_tests_data/format_28.tar.bz2
    subversion/branches/shelve/subversion/tests/cmdline/upgrade_tests_data/upgrade_tc.tar.bz2
Modified:
    subversion/branches/shelve/   (props changed)
    subversion/branches/shelve/CHANGES
    subversion/branches/shelve/configure.ac
    subversion/branches/shelve/subversion/include/private/svn_repos_private.h
    subversion/branches/shelve/subversion/include/private/svn_subr_private.h
    subversion/branches/shelve/subversion/include/private/svn_utf_private.h
    subversion/branches/shelve/subversion/include/private/svn_wc_private.h
    subversion/branches/shelve/subversion/include/svn_delta.h
    subversion/branches/shelve/subversion/include/svn_error_codes.h
    subversion/branches/shelve/subversion/include/svn_repos.h
    subversion/branches/shelve/subversion/include/svn_string.h
    subversion/branches/shelve/subversion/libsvn_client/conflicts.c
    subversion/branches/shelve/subversion/libsvn_client/list.c
    subversion/branches/shelve/subversion/libsvn_ra_serf/merge.c
    subversion/branches/shelve/subversion/libsvn_repos/deprecated.c
    subversion/branches/shelve/subversion/libsvn_repos/fs-wrap.c
    subversion/branches/shelve/subversion/libsvn_repos/hooks.c
    subversion/branches/shelve/subversion/libsvn_repos/list.c
    subversion/branches/shelve/subversion/libsvn_repos/load-fs-vtable.c
    subversion/branches/shelve/subversion/libsvn_subr/config_win.c
    subversion/branches/shelve/subversion/libsvn_subr/deprecated.c
    subversion/branches/shelve/subversion/libsvn_subr/io.c
    subversion/branches/shelve/subversion/libsvn_subr/lz4/lz4.c
    subversion/branches/shelve/subversion/libsvn_subr/spillbuf.c
    subversion/branches/shelve/subversion/libsvn_subr/sqlite3wrapper.c
    subversion/branches/shelve/subversion/libsvn_subr/string.c
    subversion/branches/shelve/subversion/libsvn_subr/utf8proc/   (props changed)
    subversion/branches/shelve/subversion/libsvn_subr/utf8proc.c
    subversion/branches/shelve/subversion/libsvn_subr/utf8proc/utf8proc.c
    subversion/branches/shelve/subversion/libsvn_subr/utf8proc/utf8proc_data.c
    subversion/branches/shelve/subversion/libsvn_subr/utf8proc/utf8proc_internal.h
    subversion/branches/shelve/subversion/libsvn_subr/version.c
    subversion/branches/shelve/subversion/libsvn_subr/win32_crashrpt.c
    subversion/branches/shelve/subversion/libsvn_subr/win32_crypto.c
    subversion/branches/shelve/subversion/libsvn_subr/win32_xlate.c
    subversion/branches/shelve/subversion/libsvn_subr/x509parse.c
    subversion/branches/shelve/subversion/libsvn_wc/conflicts.c
    subversion/branches/shelve/subversion/libsvn_wc/upgrade.c
    subversion/branches/shelve/subversion/libsvn_wc/wc-metadata.sql
    subversion/branches/shelve/subversion/libsvn_wc/wc-queries.sql
    subversion/branches/shelve/subversion/libsvn_wc/wc_db.c
    subversion/branches/shelve/subversion/libsvn_wc/wc_db.h
    subversion/branches/shelve/subversion/mod_dav_svn/merge.c
    subversion/branches/shelve/subversion/mod_dav_svn/mod_dav_svn.c
    subversion/branches/shelve/subversion/svn/svn.c
    subversion/branches/shelve/subversion/svnadmin/svnadmin.c
    subversion/branches/shelve/subversion/svnrdump/load_editor.c
    subversion/branches/shelve/subversion/svnrdump/svnrdump.h
    subversion/branches/shelve/subversion/svnrdump/util.c
    subversion/branches/shelve/subversion/tests/cmdline/basic_tests.py
    subversion/branches/shelve/subversion/tests/cmdline/davautocheck.sh
    subversion/branches/shelve/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout
    subversion/branches/shelve/subversion/tests/cmdline/svnadmin_tests.py
    subversion/branches/shelve/subversion/tests/cmdline/svntest/actions.py
    subversion/branches/shelve/subversion/tests/cmdline/upgrade_tests.py
    subversion/branches/shelve/subversion/tests/libsvn_client/conflicts-test.c
    subversion/branches/shelve/subversion/tests/libsvn_repos/dump-load-test.c
    subversion/branches/shelve/subversion/tests/libsvn_subr/string-test.c
    subversion/branches/shelve/subversion/tests/libsvn_wc/utils.c
    subversion/branches/shelve/subversion/tests/libsvn_wc/wc-queries-test.c
    subversion/branches/shelve/tools/buildbot/slaves/svn-x64-macosx/mkramdisk.sh
    subversion/branches/shelve/tools/buildbot/slaves/svn-x64-macosx/setenv.sh
    subversion/branches/shelve/tools/buildbot/slaves/svn-x64-macosx/svncheck.sh
    subversion/branches/shelve/tools/client-side/bash_completion
    subversion/branches/shelve/tools/dev/svnmover/svnmover.c
    subversion/branches/shelve/tools/dist/templates/rc-release-ann.ezt

Propchange: subversion/branches/shelve/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Oct  9 11:26:07 2017
@@ -98,4 +98,4 @@
 /subversion/branches/verify-at-commit:1462039-1462408
 /subversion/branches/verify-keep-going:1439280-1546110
 /subversion/branches/wc-collate-path:1402685-1480384
-/subversion/trunk:1801593-1806000
+/subversion/trunk:1801593-1811551

Modified: subversion/branches/shelve/CHANGES
URL: http://svn.apache.org/viewvc/subversion/branches/shelve/CHANGES?rev=1811552&r1=1811551&r2=1811552&view=diff
==============================================================================
--- subversion/branches/shelve/CHANGES (original)
+++ subversion/branches/shelve/CHANGES Mon Oct  9 11:26:07 2017
@@ -20,6 +20,7 @@ the 1.9 release:  https://subversion.apa
     * New '--max-request-size' option for svnserve (r1714330)
     * New '--max-response-size' option for svnserve (r1714333)
     * New '-rN' option for 'svnadmin lstxns' (r1703699)
+    * New '--search' option for fast 'svn ls' searches (r1767186 et al)
     * Add '--search' option support to 'svnbench null-list' (r1767202)
     * New '-M' option for 'svnlook tree' (r1708222)
     * New '--skip-unchanged' option for 'svnsync copy-revprops' (r1692655)

Modified: subversion/branches/shelve/configure.ac
URL: http://svn.apache.org/viewvc/subversion/branches/shelve/configure.ac?rev=1811552&r1=1811551&r2=1811552&view=diff
==============================================================================
--- subversion/branches/shelve/configure.ac (original)
+++ subversion/branches/shelve/configure.ac Mon Oct  9 11:26:07 2017
@@ -1312,7 +1312,7 @@ AC_PATH_PROG(PERL, perl, none)
 if test -n "$RUBY"; then
   AC_PATH_PROG(RUBY, "$RUBY", none)
 else
-  AC_PATH_PROGS(RUBY, ruby ruby1.8 ruby18 ruby1.9 ruby1 ruby1.9.3 ruby193 ruby2.0 ruby2.1, none)
+  AC_PATH_PROGS(RUBY, ruby ruby1 ruby1.8 ruby18 ruby1.9 ruby19 ruby1.9.3 ruby193 ruby2 ruby2.0 ruby20 ruby2.1 ruby21 ruby2.2 ruby22 ruby2.3 ruby23 ruby2.4 ruby24, none)
 fi
 if test "$RUBY" != "none"; then
   AC_MSG_CHECKING([rb_hash_foreach])
@@ -1321,7 +1321,7 @@ if test "$RUBY" != "none"; then
     if test -n "$RDOC"; then
       AC_PATH_PROG(RDOC, "$RDOC", none)
     else
-      AC_PATH_PROGS(RDOC, rdoc rdoc1.8 rdoc18 rdoc1.9 rdoc19 rdoc1.9.3 rdoc193 rdoc2.0 rdoc2.1, none)
+      AC_PATH_PROGS(RUBY, rdoc rdoc1 rdoc1.8 rdoc18 rdoc1.9 rdoc19 rdoc1.9.3 rdoc193 rdoc2 rdoc2.0 rdoc20 rdoc2.1 rdoc21 rdoc2.2 rdoc22 rdoc2.3 rdoc23 rdoc2.4 rdoc24, none)
     fi
     AC_CACHE_CHECK([for Ruby major version], [svn_cv_ruby_major],[
     svn_cv_ruby_major="`$RUBY -rrbconfig -e 'print RbConfig::CONFIG.fetch(%q(MAJOR))'`"
@@ -1345,7 +1345,7 @@ if test "$RUBY" != "none"; then
       # Disallow Ruby between 1.8.7 and 1.9.3
       RUBY="none"
       AC_MSG_WARN([The detected Ruby is between 1.9 and 1.9.3])
-      AC_MSG_WARN([Only 1.8.x and 1.9.3 releases are supported at this time])
+      AC_MSG_WARN([Only 1.8.x and 1.9.3 or later are supported at this time])
     fi
   else
     AC_MSG_RESULT([no])

Modified: subversion/branches/shelve/subversion/include/private/svn_repos_private.h
URL: http://svn.apache.org/viewvc/subversion/branches/shelve/subversion/include/private/svn_repos_private.h?rev=1811552&r1=1811551&r2=1811552&view=diff
==============================================================================
--- subversion/branches/shelve/subversion/include/private/svn_repos_private.h (original)
+++ subversion/branches/shelve/subversion/include/private/svn_repos_private.h Mon Oct  9 11:26:07 2017
@@ -75,6 +75,30 @@ svn_repos__validate_prop(const char *nam
                          const svn_string_t *value,
                          apr_pool_t *pool);
 
+/* Attempt to normalize a Subversion property if it "needs translation"
+ * (according to svn_prop_needs_translation(), currently all svn:* props).
+ *
+ * At this time, the only performed normalization is translation of
+ * the line endings of the property value so that it would only contain
+ * LF (\n) characters. "\r" characters found mid-line are replaced with "\n".
+ * "\r\n" sequences are replaced with "\n".
+ *
+ * NAME is used to check that VALUE should be normalized, and if this
+ * is the case, VALUE is then normalized, allocated from RESULT_POOL.
+ * If no normalization is required, VALUE will be copied to RESULT_POOL
+ * unchanged.  If NORMALIZED_P is not NULL, and the normalization
+ * happened, set *NORMALIZED_P to non-zero.  If the property is returned
+ * unchanged and NORMALIZED_P is not NULL, then *NORMALIZED_P will be
+ * set to zero.  SCRATCH_POOL will be used for temporary allocations.
+ */
+svn_error_t *
+svn_repos__normalize_prop(const svn_string_t **result_p,
+                          svn_boolean_t *normalized_p,
+                          const char *name,
+                          const svn_string_t *value,
+                          apr_pool_t *result_pool,
+                          apr_pool_t *scratch_pool);
+
 /**
  * Given the error @a err from svn_repos_fs_commit_txn(), return an
  * string containing either or both of the svn_fs_commit_txn() error

Modified: subversion/branches/shelve/subversion/include/private/svn_subr_private.h
URL: http://svn.apache.org/viewvc/subversion/branches/shelve/subversion/include/private/svn_subr_private.h?rev=1811552&r1=1811551&r2=1811552&view=diff
==============================================================================
--- subversion/branches/shelve/subversion/include/private/svn_subr_private.h (original)
+++ subversion/branches/shelve/subversion/include/private/svn_subr_private.h Mon Oct  9 11:26:07 2017
@@ -112,12 +112,12 @@ svn_spillbuf__get_size(const svn_spillbu
 svn_filesize_t
 svn_spillbuf__get_memory_size(const svn_spillbuf_t *buf);
 
-/* Retrieve the name of the spill file. The returned value can be NULL
-   if the file has not been created yet. */
+/* Retrieve the name of the spill file. The returned value will be
+   NULL if the file has not been created yet. */
 const char *
 svn_spillbuf__get_filename(const svn_spillbuf_t *buf);
 
-/* Retrieve the handle of the spill file. The returned value can be
+/* Retrieve the handle of the spill file. The returned value will be
    NULL if the file has not been created yet. */
 apr_file_t *
 svn_spillbuf__get_file(const svn_spillbuf_t *buf);
@@ -133,8 +133,8 @@ svn_spillbuf__write(svn_spillbuf_t *buf,
 /* Read a block of memory from the spill buffer. @a *data will be set to
    NULL if no content remains. Otherwise, @a data and @a len will point to
    data that must be fully-consumed by the caller. This data will remain
-   valid until another call to svn_spillbuf_write(), svn_spillbuf_read(),
-   or svn_spillbuf_process(), or if the spill buffer's pool is cleared.  */
+   valid until another call to svn_spillbuf__write(), svn_spillbuf__read(),
+   or svn_spillbuf__process(), or if the spill buffer's pool is cleared.  */
 svn_error_t *
 svn_spillbuf__read(const char **data,
                    apr_size_t *len,
@@ -143,7 +143,7 @@ svn_spillbuf__read(const char **data,
 
 
 /* Callback for reading content out of the spill buffer. Set @a stop if
-   you want to stop the processing (and will call svn_spillbuf_process
+   you want to stop the processing (and will call svn_spillbuf__process
    again, at a later time).  */
 typedef svn_error_t * (*svn_spillbuf_read_t)(svn_boolean_t *stop,
                                              void *baton,
@@ -472,7 +472,7 @@ svn_version__parse_version_string(svn_ve
  * @since New in 1.8.
  */
 svn_boolean_t
-svn_version__at_least(svn_version_t *version,
+svn_version__at_least(const svn_version_t *version,
                       int major,
                       int minor,
                       int patch);

Modified: subversion/branches/shelve/subversion/include/private/svn_utf_private.h
URL: http://svn.apache.org/viewvc/subversion/branches/shelve/subversion/include/private/svn_utf_private.h?rev=1811552&r1=1811551&r2=1811552&view=diff
==============================================================================
--- subversion/branches/shelve/subversion/include/private/svn_utf_private.h (original)
+++ subversion/branches/shelve/subversion/include/private/svn_utf_private.h Mon Oct  9 11:26:07 2017
@@ -172,6 +172,18 @@ svn_utf__xfrm(const char **result,
               svn_boolean_t accent_insensitive,
               svn_membuf_t *buf);
 
+/* Return TRUE if S matches any of the const char * glob patterns in
+ * PATTERNS.
+ *
+ * S will internally be normalized to lower-case and accents removed
+ * using svn_utf__xfrm.  To get a match, the PATTERNS must have been
+ * normalized accordingly before calling this function.
+ */
+svn_boolean_t
+svn_utf__fuzzy_glob_match(const char *str,
+                          const apr_array_header_t *patterns,
+                          svn_membuf_t *buf);
+
 /* Check if STRING is a valid, NFC-normalized UTF-8 string.  Note that
  * a FALSE return value may indicate that STRING is not valid UTF-8 at
  * all.

Modified: subversion/branches/shelve/subversion/include/private/svn_wc_private.h
URL: http://svn.apache.org/viewvc/subversion/branches/shelve/subversion/include/private/svn_wc_private.h?rev=1811552&r1=1811551&r2=1811552&view=diff
==============================================================================
--- subversion/branches/shelve/subversion/include/private/svn_wc_private.h (original)
+++ subversion/branches/shelve/subversion/include/private/svn_wc_private.h Mon Oct  9 11:26:07 2017
@@ -1933,7 +1933,7 @@ svn_wc__conflict_tree_update_local_add(s
                                        apr_pool_t *scratch_pool);
 
 /* Find nodes in the working copy which corresponds to the new location
- * MOVED_TO_REPOS_RELPATH@REV of the tree conflict victim at VICTIM_ABSPATH.
+ * MOVED_TO_REPOS_RELPATH of the tree conflict victim at VICTIM_ABSPATH.
  * The nodes must be of the same node kind as VICTIM_NODE_KIND.
  * If no such node can be found, set *POSSIBLE_TARGETS to an empty array.
  *
@@ -1943,10 +1943,10 @@ svn_wc__conflict_tree_update_local_add(s
  * to the implementation of this function.
  * Note that this function may not necessarily return a node which was
  * actually moved. The only hard guarantee is that the node corresponds to
- * the repository node MOVED_TO_REPOS_RELPATH@REV specified by the caller.
- * In many cases, this will be a moved node if the caller's parameters are
- * correct. Users should be able to perform a sanity check on the results
- * returned from this function.
+ * the repository relpath MOVED_TO_REPOS_RELPATH specified by the caller.
+ * Users should perform a sanity check on the results returned from this
+ * function, e.g. establish whether the MOVED_TO_REPOS_RELPATH at its
+ * current checked-out revision shares ancestry with the conflict victim.
  */
 svn_error_t *
 svn_wc__guess_incoming_move_target_nodes(apr_array_header_t **possible_targets,
@@ -1954,7 +1954,6 @@ svn_wc__guess_incoming_move_target_nodes
                                          const char *victim_abspath,
                                          svn_node_kind_t victim_node_kind,
                                          const char *moved_to_repos_relpath,
-                                         svn_revnum_t rev,
                                          apr_pool_t *result_pool,
                                          apr_pool_t *scratch_pool);
 

Modified: subversion/branches/shelve/subversion/include/svn_delta.h
URL: http://svn.apache.org/viewvc/subversion/branches/shelve/subversion/include/svn_delta.h?rev=1811552&r1=1811551&r2=1811552&view=diff
==============================================================================
--- subversion/branches/shelve/subversion/include/svn_delta.h (original)
+++ subversion/branches/shelve/subversion/include/svn_delta.h Mon Oct  9 11:26:07 2017
@@ -693,9 +693,12 @@ svn_txdelta_skip_svndiff_window(apr_file
 /** A structure full of callback functions the delta source will invoke
  * as it produces the delta.
  *
- * @note Don't try to allocate one of these yourself.  Instead, always
- * use svn_delta_default_editor() or some other constructor, to ensure
- * that unused slots are filled in with no-op functions.
+ * @note Fields may be added to the end of this structure in future
+ * versions.  Therefore, users shouldn't allocate structures of this
+ * type, to preserve binary compatibility.
+ *
+ * @note It is recommended to use svn_delta_default_editor() or some other
+ * constructor, to ensure that unused slots are filled in with no-op functions.
  *
  * <h3>Function Usage</h3>
  *
@@ -1157,7 +1160,7 @@ typedef struct svn_delta_editor_t
    * be ignored even if not NULL.  If it is not ignored, it must match
    * the checksum of the base text against which svndiff data is being
    * applied; if it does not, @c apply_textdelta_stream call which detects
-   * the mismatch will return the error SVN_ERR_CHECKSUM_MISMATCH
+   * the mismatch will return the error #SVN_ERR_CHECKSUM_MISMATCH
    * (if there is no base text, there may still be an error if
    * @a base_checksum is neither NULL nor the hex MD5 checksum of the
    * empty string).

Modified: subversion/branches/shelve/subversion/include/svn_error_codes.h
URL: http://svn.apache.org/viewvc/subversion/branches/shelve/subversion/include/svn_error_codes.h?rev=1811552&r1=1811551&r2=1811552&view=diff
==============================================================================
--- subversion/branches/shelve/subversion/include/svn_error_codes.h (original)
+++ subversion/branches/shelve/subversion/include/svn_error_codes.h Mon Oct  9 11:26:07 2017
@@ -240,6 +240,11 @@ SVN_ERROR_START
              SVN_ERR_BAD_CATEGORY_START + 16,
              "Invalid compression method")
 
+  /** @since New in 1.10. */
+  SVN_ERRDEF(SVN_ERR_BAD_PROPERTY_VALUE_EOL,
+             SVN_ERR_BAD_CATEGORY_START + 17,
+             "Unexpected line ending in the property value")
+
   /* xml errors */
 
   SVN_ERRDEF(SVN_ERR_XML_ATTRIB_NOT_FOUND,

Modified: subversion/branches/shelve/subversion/include/svn_repos.h
URL: http://svn.apache.org/viewvc/subversion/branches/shelve/subversion/include/svn_repos.h?rev=1811552&r1=1811551&r2=1811552&view=diff
==============================================================================
--- subversion/branches/shelve/subversion/include/svn_repos.h (original)
+++ subversion/branches/shelve/subversion/include/svn_repos.h Mon Oct  9 11:26:07 2017
@@ -3436,6 +3436,15 @@ svn_repos_dump_fs(svn_repos_t *repos,
  * to be stamped as if they were newly created via the normal commit
  * process.
  *
+ * If @a normalize_props is set, attempt to normalize invalid Subversion
+ * revision and node properties (those in the svn: namespace) so that
+ * their values would follow the established rules for them.  For example,
+ * for such properties, typically the value must be in UTF-8 with LF
+ * line endings.
+ *
+ * @note The details or the performed normalizations are deliberately
+ * left unspecified and may change in the future.
+ *
  * If non-NULL, use @a notify_func and @a notify_baton to send notification
  * of events to the caller.
  *
@@ -3443,8 +3452,34 @@ svn_repos_dump_fs(svn_repos_t *repos,
  * @a cancel_baton as argument to see if the client wishes to cancel
  * the load.
  *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_repos_load_fs6(svn_repos_t *repos,
+                   svn_stream_t *dumpstream,
+                   svn_revnum_t start_rev,
+                   svn_revnum_t end_rev,
+                   enum svn_repos_load_uuid uuid_action,
+                   const char *parent_dir,
+                   svn_boolean_t use_pre_commit_hook,
+                   svn_boolean_t use_post_commit_hook,
+                   svn_boolean_t validate_props,
+                   svn_boolean_t ignore_dates,
+                   svn_boolean_t normalize_props,
+                   svn_repos_notify_func_t notify_func,
+                   void *notify_baton,
+                   svn_cancel_func_t cancel_func,
+                   void *cancel_baton,
+                   apr_pool_t *pool);
+
+/**
+ * Similar to svn_repos_load_fs6(), but with the @a normalize_props
+ * parameter always set to @c FALSE.
+ *
  * @since New in 1.9.
+ * @deprecated Provided for backward compatibility with the 1.9 API.
  */
+SVN_DEPRECATED
 svn_error_t *
 svn_repos_load_fs5(svn_repos_t *repos,
                    svn_stream_t *dumpstream,
@@ -3567,6 +3602,15 @@ svn_repos_load_fs(svn_repos_t *repos,
  * @a dumpstream, keeping whatever timestamps the revisions currently
  * have.
  *
+ * If @a normalize_props is set, attempt to normalize invalid Subversion
+ * revision and node properties (those in the svn: namespace) so that
+ * their values would follow the established rules for them.  For example,
+ * for such properties, typically the value must be in UTF-8 with LF
+ * line endings.
+ *
+ * @note The details or the performed normalizations are deliberately
+ * left unspecified and may change in the future.
+ *
  * If non-NULL, use @a notify_func and @a notify_baton to send notification
  * of events to the caller.
  *
@@ -3585,6 +3629,7 @@ svn_repos_load_fs_revprops(svn_repos_t *
                            svn_revnum_t end_rev,
                            svn_boolean_t validate_props,
                            svn_boolean_t ignore_dates,
+                           svn_boolean_t normalize_props,
                            svn_repos_notify_func_t notify_func,
                            void *notify_baton,
                            svn_cancel_func_t cancel_func,
@@ -3788,12 +3833,47 @@ svn_repos_parse_dumpstream3(svn_stream_t
  * to be stamped as if they were newly created via the normal commit
  * process.
  *
+ * If @a normalize_props is set, attempt to normalize invalid Subversion
+ * revision and node properties (those in the svn: namespace) so that
+ * their values would follow the established rules for them.  For example,
+ * for such properties, typically the value must be in UTF-8 with LF
+ * line endings.
+ *
+ * @note The details or the performed normalizations are deliberately
+ * left unspecified and may change in the future.
+ *
  * If @a parent_dir is not NULL, then the parser will reparent all the
  * loaded nodes, from root to @a parent_dir.  The directory @a parent_dir
  * must be an existing directory in the repository.
  *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_repos_get_fs_build_parser6(const svn_repos_parse_fns3_t **parser,
+                               void **parse_baton,
+                               svn_repos_t *repos,
+                               svn_revnum_t start_rev,
+                               svn_revnum_t end_rev,
+                               svn_boolean_t use_history,
+                               svn_boolean_t validate_props,
+                               enum svn_repos_load_uuid uuid_action,
+                               const char *parent_dir,
+                               svn_boolean_t use_pre_commit_hook,
+                               svn_boolean_t use_post_commit_hook,
+                               svn_boolean_t ignore_dates,
+                               svn_boolean_t normalize_props,
+                               svn_repos_notify_func_t notify_func,
+                               void *notify_baton,
+                               apr_pool_t *pool);
+
+/**
+ * Similar to svn_repos_get_fs_build_parser6(), but with the
+ * @a normalize_props parameter always set to @c FALSE.
+ *
  * @since New in 1.9.
+ * @deprecated Provided for backward compatibility with the 1.9 API.
  */
+SVN_DEPRECATED
 svn_error_t *
 svn_repos_get_fs_build_parser5(const svn_repos_parse_fns3_t **parser,
                                void **parse_baton,

Modified: subversion/branches/shelve/subversion/include/svn_string.h
URL: http://svn.apache.org/viewvc/subversion/branches/shelve/subversion/include/svn_string.h?rev=1811552&r1=1811551&r2=1811552&view=diff
==============================================================================
--- subversion/branches/shelve/subversion/include/svn_string.h (original)
+++ subversion/branches/shelve/subversion/include/svn_string.h Mon Oct  9 11:26:07 2017
@@ -533,9 +533,25 @@ svn_cstring_count_newlines(const char *m
  * of char *) each followed by @a separator (that is, @a separator
  * will also end the resulting string).  Allocate the result in @a pool.
  * If @a strings is empty, then return the empty string.
+ * If @a trailing_separator is non-zero, also append the separator
+ * after the last joined element.
+ *
+ * @since New in 1.10.
+ */
+char *
+svn_cstring_join2(const apr_array_header_t *strings,
+                  const char *separator,
+                  svn_boolean_t trailing_separator,
+                  apr_pool_t *pool);
+
+/**
+ * Similar to svn_cstring_join2(), but always includes the trailing
+ * separator.
  *
  * @since New in 1.2.
+ * @deprecated Provided for backwards compatibility with the 1.9 API.
  */
+SVN_DEPRECATED
 char *
 svn_cstring_join(const apr_array_header_t *strings,
                  const char *separator,

Modified: subversion/branches/shelve/subversion/libsvn_client/conflicts.c
URL: http://svn.apache.org/viewvc/subversion/branches/shelve/subversion/libsvn_client/conflicts.c?rev=1811552&r1=1811551&r2=1811552&view=diff
==============================================================================
--- subversion/branches/shelve/subversion/libsvn_client/conflicts.c (original)
+++ subversion/branches/shelve/subversion/libsvn_client/conflicts.c Mon Oct  9 11:26:07 2017
@@ -267,6 +267,9 @@ struct repos_move_info {
   /* The copyfrom revision of the moved-to path. */
   svn_revnum_t copyfrom_rev;
 
+  /* The node kind of the item being moved. */
+  svn_node_kind_t node_kind;
+
   /* Prev pointer. NULL if no prior move exists in the chain. */
   struct repos_move_info *prev;
 
@@ -366,6 +369,7 @@ struct copy_info {
   const char *copyto_path;
   const char *copyfrom_path;
   svn_revnum_t copyfrom_rev;
+  svn_node_kind_t node_kind;
 };
 
 /* Allocate and return a NEW_MOVE, and update MOVED_PATHS with this new move. */
@@ -374,6 +378,7 @@ add_new_move(struct repos_move_info **ne
              const char *deleted_repos_relpath,
              const char *copyto_path,
              svn_revnum_t copyfrom_rev,
+             svn_node_kind_t node_kind,
              svn_revnum_t revision,
              const char *author,
              apr_hash_t *moved_paths,
@@ -392,6 +397,7 @@ add_new_move(struct repos_move_info **ne
   move->rev = revision;
   move->rev_author = apr_pstrdup(result_pool, author);
   move->copyfrom_rev = copyfrom_rev;
+  move->node_kind = node_kind;
 
   /* Link together multiple moves of the same node.
    * Note that we're traversing history backwards, so moves already
@@ -430,6 +436,269 @@ add_new_move(struct repos_move_info **ne
   return SVN_NO_ERROR;
 }
 
+/* Push a MOVE into the MOVES_TABLE. */
+static void
+push_move(struct repos_move_info *move, apr_hash_t *moves_table,
+          apr_pool_t *result_pool)
+{
+  apr_array_header_t *moves;
+
+  /* Add this move to the list of moves in the revision. */
+  moves = apr_hash_get(moves_table, &move->rev, sizeof(svn_revnum_t));
+  if (moves == NULL)
+    {
+      /* It is the first move in this revision. Create the list. */
+      moves = apr_array_make(result_pool, 1, sizeof(struct repos_move_info *));
+      apr_hash_set(moves_table, &move->rev, sizeof(svn_revnum_t), moves);
+    }
+  APR_ARRAY_PUSH(moves, struct repos_move_info *) = move;
+}
+
+/* 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_ra_session_t *ra_session,
+         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,
+                                                   ra_session, ctx,
+                                                   result_pool, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Like find_yca, expect that a YCA could also be found via a brute-force
+ * search of parents of REPOS_RELPATH1 and REPOS_RELPATH2, if no "direct"
+ * YCA exists. An implicit assumption is that some parent of REPOS_RELPATH1
+ * is a branch of some parent of REPOS_RELPATH2.
+ *
+ * This function can guess a "good enough" YCA for 'missing nodes' which do
+ * not exist in the working copy, e.g. when a file edit is merged to a path
+ * which does not exist in the working copy.
+ */
+static svn_error_t *
+find_nearest_yca(svn_client__pathrev_t **yca_locp,
+                 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_ra_session_t *ra_session,
+                 svn_client_ctx_t *ctx,
+                 apr_pool_t *result_pool,
+                 apr_pool_t *scratch_pool)
+{
+  svn_client__pathrev_t *yca_loc;
+  svn_error_t *err;
+  apr_pool_t *iterpool;
+  const char *p1, *p2;
+  apr_size_t c1, c2;
+
+  *yca_locp = NULL;
+
+  iterpool = svn_pool_create(scratch_pool);
+
+  p1 = repos_relpath1;
+  c1 = svn_path_component_count(repos_relpath1);
+  while (c1--)
+    {
+      svn_pool_clear(iterpool);
+
+      p2 = repos_relpath2;
+      c2 = svn_path_component_count(repos_relpath2);
+      while (c2--)
+        {
+          err = find_yca(&yca_loc, p1, peg_rev1, p2, peg_rev2,
+                         repos_root_url, repos_uuid, ra_session, ctx,
+                         result_pool, iterpool);
+          if (err)
+            {
+              if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
+                {
+                  svn_error_clear(err);
+                  yca_loc = NULL;
+                }
+              else
+                return svn_error_trace(err);
+            }
+
+          if (yca_loc)
+            {
+              *yca_locp = yca_loc;
+              svn_pool_destroy(iterpool);
+              return SVN_NO_ERROR;
+            }
+
+          p2 = svn_relpath_dirname(p2, scratch_pool);
+        }
+
+      p1 = svn_relpath_dirname(p1, scratch_pool);
+    }
+
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+/* Check if the copied node described by COPY and the DELETED_PATH@DELETED_REV
+ * share a common ancestor. If so, return new repos_move_info in *MOVE which
+ * describes a move from the deleted path to that copy's destination. */
+static svn_error_t *
+find_related_move(struct repos_move_info **move,
+                  struct copy_info *copy,
+                  const char *deleted_repos_relpath,
+                  svn_revnum_t deleted_rev,
+                  const char *author,
+                  apr_hash_t *moved_paths,
+                  const char *repos_root_url,
+                  const char *repos_uuid,
+                  svn_client_ctx_t *ctx,
+                  svn_ra_session_t *ra_session,
+                  apr_pool_t *result_pool,
+                  apr_pool_t *scratch_pool)
+{
+  svn_client__pathrev_t *yca_loc;
+  svn_error_t *err;
+
+  *move = NULL;
+  err = find_yca(&yca_loc, copy->copyfrom_path, copy->copyfrom_rev,
+                 deleted_repos_relpath, rev_below(deleted_rev),
+                 repos_root_url, repos_uuid, ra_session, ctx,
+                 scratch_pool, scratch_pool);
+  if (err)
+    {
+      if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
+        {
+          svn_error_clear(err);
+          yca_loc = NULL;
+        }
+      else
+        return svn_error_trace(err);
+    }
+
+  if (yca_loc)
+    SVN_ERR(add_new_move(move, deleted_repos_relpath,
+                         copy->copyto_path, copy->copyfrom_rev,
+                         copy->node_kind, deleted_rev, author,
+                         moved_paths, ra_session, repos_root_url,
+                         result_pool, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Detect moves by matching DELETED_REPOS_RELPATH@DELETED_REV to the copies
+ * in COPIES. Add any moves found to MOVES_TABLE and update MOVED_PATHS. */
+static svn_error_t *
+match_copies_to_deletion(const char *deleted_repos_relpath,
+                         svn_revnum_t deleted_rev,
+                         const char *author,
+                         apr_hash_t *copies,
+                         apr_hash_t *moves_table,
+                         apr_hash_t *moved_paths,
+                         const char *repos_root_url,
+                         const char *repos_uuid,
+                         svn_ra_session_t *ra_session,
+                         svn_client_ctx_t *ctx,
+                         apr_pool_t *result_pool,
+                         apr_pool_t *scratch_pool)
+{
+  apr_hash_index_t *hi;
+  apr_pool_t *iterpool;
+
+  iterpool = svn_pool_create(scratch_pool);
+  for (hi = apr_hash_first(scratch_pool, copies);
+       hi != NULL;
+       hi = apr_hash_next(hi))
+    {
+      const char *copyfrom_path = apr_hash_this_key(hi);
+      apr_array_header_t *copies_with_same_source_path;
+      int i;
+
+      svn_pool_clear(iterpool);
+
+      copies_with_same_source_path = apr_hash_this_val(hi);
+
+      if (strcmp(copyfrom_path, deleted_repos_relpath) == 0)
+        {
+          /* We found a copyfrom path which matches a deleted node.
+           * Check if the deleted node is an ancestor of the copied node. */
+          for (i = 0; i < copies_with_same_source_path->nelts; i++)
+            {
+              struct copy_info *copy;
+              svn_boolean_t related;
+              struct repos_move_info *move;
+
+              copy = APR_ARRAY_IDX(copies_with_same_source_path, i,
+                                   struct copy_info *);
+              SVN_ERR(check_move_ancestry(&related,
+                                          ra_session, repos_root_url,
+                                          deleted_repos_relpath,
+                                          deleted_rev,
+                                          copy->copyfrom_path,
+                                          copy->copyfrom_rev,
+                                          TRUE, iterpool));
+              if (!related)
+                continue;
+              
+              /* Remember details of this move. */
+              SVN_ERR(add_new_move(&move, deleted_repos_relpath,
+                                   copy->copyto_path, copy->copyfrom_rev,
+                                   copy->node_kind, deleted_rev, author,
+                                   moved_paths, ra_session, repos_root_url,
+                                   result_pool, iterpool));
+              push_move(move, moves_table, result_pool);
+            } 
+        }
+      else
+        {
+          /* Check if this deleted node is related to any copies in this
+           * revision. These could be moves of the deleted node which
+           * were merged here from other lines of history. */
+          for (i = 0; i < copies_with_same_source_path->nelts; i++)
+            {
+              struct copy_info *copy;
+              struct repos_move_info *move = NULL;
+
+              copy = APR_ARRAY_IDX(copies_with_same_source_path, i,
+                                   struct copy_info *);
+              SVN_ERR(find_related_move(&move, copy, deleted_repos_relpath,
+                                        deleted_rev, author,
+                                        moved_paths,
+                                        repos_root_url, repos_uuid,
+                                        ctx, ra_session,
+                                        result_pool, iterpool));
+              if (move)
+                push_move(move, moves_table, result_pool);
+            }
+        }
+    }
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
 /* Update MOVES_TABLE and MOVED_PATHS based on information from
  * revision data in LOG_ENTRY, COPIES, and DELETED_PATHS.
  * Use RA_SESSION to perform the necessary requests. */
@@ -441,73 +710,31 @@ find_moves_in_revision(svn_ra_session_t
                        apr_hash_t *copies,
                        apr_array_header_t *deleted_paths,
                        const char *repos_root_url,
+                       const char *repos_uuid,
+                       svn_client_ctx_t *ctx,
                        apr_pool_t *result_pool,
                        apr_pool_t *scratch_pool)
 {
   apr_pool_t *iterpool;
-  svn_boolean_t related;
   int i;
+  const svn_string_t *author;
 
+  author = svn_hash_gets(log_entry->revprops, SVN_PROP_REVISION_AUTHOR);
   iterpool = svn_pool_create(scratch_pool);
   for (i = 0; i < deleted_paths->nelts; i++)
     {
       const char *deleted_repos_relpath;
-      struct repos_move_info *move;
-      apr_array_header_t *moves;
-      apr_array_header_t *copies_with_same_source_path;
-      int j;
 
       svn_pool_clear(iterpool);
 
       deleted_repos_relpath = APR_ARRAY_IDX(deleted_paths, i, const char *);
-
-      copies_with_same_source_path = svn_hash_gets(copies,
-                                                   deleted_repos_relpath);
-      if (copies_with_same_source_path == NULL)
-        continue; /* Not a move, or a nested move we handle later on. */
-
-      for (j = 0; j < copies_with_same_source_path->nelts; j++)
-        {
-          struct copy_info *copy;
-
-          /* We found a copy with a copyfrom path which matches a deleted node.
-           * Verify that the deleted node is an ancestor of the copied node. */
-          copy = APR_ARRAY_IDX(copies_with_same_source_path, j,
-                               struct copy_info *);
-          SVN_ERR(check_move_ancestry(&related, ra_session, repos_root_url,
-                                      deleted_repos_relpath,
-                                      log_entry->revision,
-                                      copy->copyfrom_path,
-                                      copy->copyfrom_rev,
-                                      TRUE, iterpool));
-          if (related)
-            {
-              const svn_string_t *author;
-              
-              author = svn_hash_gets(log_entry->revprops,
-                                     SVN_PROP_REVISION_AUTHOR);
-              /* Remember details of this move. */
-              SVN_ERR(add_new_move(&move, deleted_repos_relpath,
-                                   copy->copyto_path, copy->copyfrom_rev,
-                                   log_entry->revision,
-                                   author ? author->data : _("unknown author"),
-                                   moved_paths, ra_session, repos_root_url,
-                                   result_pool, iterpool));
-
-              /* Add this move to the list of moves in this revision. */
-              moves = apr_hash_get(moves_table, &move->rev,
-                                   sizeof(svn_revnum_t));
-              if (moves == NULL)
-                {
-                  /* It is the first move in this revision. Create the list. */
-                  moves = apr_array_make(result_pool, 1,
-                                         sizeof(struct repos_move_info *));
-                  apr_hash_set(moves_table, &move->rev, sizeof(svn_revnum_t),
-                               moves);
-                }
-              APR_ARRAY_PUSH(moves, struct repos_move_info *) = move;
-            }
-        }
+      SVN_ERR(match_copies_to_deletion(deleted_repos_relpath,
+                                       log_entry->revision,
+                                       author ? author->data
+                                              : _("unknown author"),
+                                       copies, moves_table, moved_paths,
+                                       repos_root_url, repos_uuid, ra_session,
+                                       ctx, result_pool, iterpool));
     }
   svn_pool_destroy(iterpool);
 
@@ -520,7 +747,7 @@ struct find_deleted_rev_baton
    * svn_ra_get_log2(). */
   const char *deleted_repos_relpath;
   const char *related_repos_relpath;
-  svn_revnum_t related_repos_peg_rev;
+  svn_revnum_t related_peg_rev;
   const char *repos_root_url;
   const char *repos_uuid;
   svn_client_ctx_t *ctx;
@@ -539,40 +766,6 @@ struct find_deleted_rev_baton
   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_ra_session_t *ra_session,
-         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,
-                                                   ra_session, ctx,
-                                                   result_pool, scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-
 /* If DELETED_RELPATH matches the moved-from path of a move in MOVES,
  * or if DELETED_RELPATH is a child of a moved-to path in MOVES, return
  * a struct move_info for the corresponding move. Else, return NULL. */
@@ -708,6 +901,7 @@ find_nested_moves(apr_array_header_t *mo
               /* Remember details of this move. */
               SVN_ERR(add_new_move(&nested_move, moved_along_repos_relpath,
                                    copy->copyto_path, copy->copyfrom_rev,
+                                   copy->node_kind,
                                    revision, author, moved_paths,
                                    ra_session, repos_root_url,
                                    result_pool, iterpool));
@@ -726,6 +920,34 @@ find_nested_moves(apr_array_header_t *mo
   return SVN_NO_ERROR;
 }
 
+/* Make a shallow copy of the copied LOG_ITEM in COPIES. */
+static void
+cache_copied_item(apr_hash_t *copies, const char *changed_path,
+                  svn_log_changed_path2_t *log_item)
+{
+  apr_pool_t *result_pool = apr_hash_pool_get(copies);
+  struct copy_info *copy = apr_palloc(result_pool, sizeof(*copy));
+  apr_array_header_t *copies_with_same_source_path;
+
+  copy->copyfrom_path = log_item->copyfrom_path;
+  if (log_item->copyfrom_path[0] == '/')
+    copy->copyfrom_path++;
+  copy->copyto_path = changed_path;
+  copy->copyfrom_rev = log_item->copyfrom_rev;
+  copy->node_kind = log_item->node_kind;
+
+  copies_with_same_source_path = apr_hash_get(copies, copy->copyfrom_path,
+                                              APR_HASH_KEY_STRING);
+  if (copies_with_same_source_path == NULL)
+    {
+      copies_with_same_source_path = apr_array_make(result_pool, 1,
+                                                    sizeof(struct copy_info *));
+      apr_hash_set(copies, copy->copyfrom_path, APR_HASH_KEY_STRING,
+                   copies_with_same_source_path);
+    }
+  APR_ARRAY_PUSH(copies_with_same_source_path, struct copy_info *) = copy;
+}
+
 /* Implements svn_log_entry_receiver_t.
  *
  * Find the revision in which a node, optionally ancestrally related to the
@@ -796,7 +1018,7 @@ find_deleted_rev(void *baton,
           deleted_node_found = TRUE;
 
           if (b->related_repos_relpath != NULL &&
-              b->related_repos_peg_rev != SVN_INVALID_REVNUM)
+              b->related_peg_rev != SVN_INVALID_REVNUM)
             {
               svn_client__pathrev_t *yca_loc;
               svn_error_t *err;
@@ -807,7 +1029,7 @@ find_deleted_rev(void *baton,
                * "related node" specified in our baton. */
               err = find_yca(&yca_loc,
                              b->related_repos_relpath,
-                             b->related_repos_peg_rev,
+                             b->related_peg_rev,
                              b->deleted_repos_relpath,
                              rev_below(log_entry->revision),
                              b->repos_root_url, b->repos_uuid,
@@ -1438,31 +1660,7 @@ find_moves(void *baton, svn_log_entry_t
 
       /* For move detection, scan for copied nodes in this revision. */
       if (log_item->action == 'A' && log_item->copyfrom_path)
-        {
-          struct copy_info *copy;
-          apr_array_header_t *copies_with_same_source_path;
-
-          if (log_item->copyfrom_path[0] == '/')
-            log_item->copyfrom_path++;
-
-          copy = apr_palloc(scratch_pool, sizeof(*copy));
-          copy->copyto_path = changed_path;
-          copy->copyfrom_path = log_item->copyfrom_path;
-          copy->copyfrom_rev = log_item->copyfrom_rev;
-          copies_with_same_source_path = apr_hash_get(copies,
-                                                      log_item->copyfrom_path,
-                                                      APR_HASH_KEY_STRING);
-          if (copies_with_same_source_path == NULL)
-            {
-              copies_with_same_source_path = apr_array_make(
-                                               scratch_pool, 1,
-                                               sizeof(struct copy_info *));
-              apr_hash_set(copies, copy->copyfrom_path, APR_HASH_KEY_STRING,
-                           copies_with_same_source_path);
-            }
-          APR_ARRAY_PUSH(copies_with_same_source_path,
-                         struct copy_info *) = copy;
-        }
+        cache_copied_item(copies, changed_path, log_item);
 
       /* For move detection, store all deleted_paths. */
       if (log_item->action == 'D' || log_item->action == 'R')
@@ -1475,8 +1673,8 @@ find_moves(void *baton, svn_log_entry_t
   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->result_pool, scratch_pool));
+                                 b->repos_root_url, b->repos_uuid,
+                                 b->ctx, b->result_pool, scratch_pool));
 
   moves = apr_hash_get(b->moves_table, &log_entry->revision,
                        sizeof(svn_revnum_t));
@@ -1503,7 +1701,9 @@ find_moves(void *baton, svn_log_entry_t
 static svn_error_t *
 find_moves_in_revision_range(struct apr_hash_t **moves_table,
                              const char *repos_relpath,
-                             svn_client_conflict_t *conflict,
+                             const char *repos_root_url,
+                             const char *repos_uuid,
+                             const char *victim_abspath,
                              svn_revnum_t start_rev,
                              svn_revnum_t end_rev,
                              svn_client_ctx_t *ctx,
@@ -1515,15 +1715,10 @@ find_moves_in_revision_range(struct apr_
   const char *corrected_url;
   apr_array_header_t *paths;
   apr_array_header_t *revprops;
-  const char *repos_root_url;
-  const char *repos_uuid;
   struct find_moves_baton b = { 0 };
 
   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));
   url = svn_path_url_add_component2(repos_root_url, repos_relpath,
                                     scratch_pool);
   SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url,
@@ -1540,7 +1735,7 @@ find_moves_in_revision_range(struct apr_
   b.repos_root_url = repos_root_url;
   b.repos_uuid = repos_uuid;
   b.ctx = ctx;
-  b.victim_abspath = conflict->local_abspath;
+  b.victim_abspath = victim_abspath;
   b.moves_table = apr_hash_make(result_pool);
   b.moved_paths = apr_hash_make(scratch_pool);
   b.result_pool = result_pool;
@@ -1562,12 +1757,14 @@ find_moves_in_revision_range(struct apr_
 }
 
 /* Return new move information for a moved-along child MOVED_ALONG_RELPATH.
+ * Set MOVE->NODE_KIND to MOVED_ALONG_NODE_KIND.
  * Do not copy MOVE->NEXT and MOVE-PREV.
  * If MOVED_ALONG_RELPATH is empty, this effectively copies MOVE to
  * RESULT_POOL with NEXT and PREV pointers cleared. */
 static struct repos_move_info *
 new_path_adjusted_move(struct repos_move_info *move,
                        const char *moved_along_relpath,
+                       svn_node_kind_t moved_along_node_kind,
                        apr_pool_t *result_pool)
 {
   struct repos_move_info *new_move;
@@ -1582,6 +1779,7 @@ new_path_adjusted_move(struct repos_move
   new_move->rev = move->rev;
   new_move->rev_author = apr_pstrdup(result_pool, move->rev_author);
   new_move->copyfrom_rev = move->copyfrom_rev;
+  new_move->node_kind = moved_along_node_kind;
   /* Ignore prev and next pointers. Caller will set them if needed. */
 
   return new_move;
@@ -1644,7 +1842,8 @@ find_next_moves_in_revision(apr_array_he
           struct repos_move_info *new_move;
 
           /* We have a winner. */
-          new_move = new_path_adjusted_move(move, relpath, result_pool);
+          new_move = new_path_adjusted_move(move, relpath, prev_move->node_kind,
+                                            result_pool);
           if (*next_moves == NULL)
             *next_moves = apr_array_make(result_pool, 1,
                                          sizeof(struct repos_move_info *));
@@ -1743,6 +1942,188 @@ trace_moved_node(apr_hash_t *moves_table
   return SVN_NO_ERROR;
 }
 
+/* Given a list of MOVES_IN_REVISION, figure out which of these moves
+ * move the node which was later on moved by NEXT_MOVE. */
+static svn_error_t *
+find_prev_move_in_revision(struct repos_move_info **prev_move,
+                           apr_array_header_t *moves_in_revision,
+                           struct repos_move_info *next_move,
+                           svn_ra_session_t *ra_session,
+                           const char *repos_root_url,
+                           apr_pool_t *result_pool,
+                           apr_pool_t *scratch_pool)
+{
+  int i;
+  apr_pool_t *iterpool;
+
+  *prev_move = NULL;
+
+  iterpool = svn_pool_create(scratch_pool);
+  for (i = 0; i < moves_in_revision->nelts; i++)
+    {
+      struct repos_move_info *move;
+      const char *relpath;
+      const char *deleted_repos_relpath;
+      svn_boolean_t related;
+      svn_error_t *err;
+
+      svn_pool_clear(iterpool);
+
+      /* Check if this move affects the current known path of our node. */
+      move = APR_ARRAY_IDX(moves_in_revision, i, struct repos_move_info *);
+      relpath = svn_relpath_skip_ancestor(next_move->moved_from_repos_relpath,
+                                          move->moved_to_repos_relpath);
+      if (relpath == NULL)
+        continue;
+
+      /* It does. So our node must have been deleted. */
+      deleted_repos_relpath = svn_relpath_join(
+                                next_move->moved_from_repos_relpath,
+                                relpath, iterpool);
+
+      /* 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. */
+      err = check_move_ancestry(&related, ra_session, repos_root_url,
+                                deleted_repos_relpath, next_move->rev,
+                                move->moved_from_repos_relpath,
+                                move->copyfrom_rev,
+                                FALSE, scratch_pool);
+      if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
+        {
+          svn_error_clear(err);
+          continue;
+        }
+      else
+        SVN_ERR(err);
+
+      if (related)
+        {
+          /* We have a winner. */
+          *prev_move = new_path_adjusted_move(move, relpath,
+                                              next_move->node_kind,
+                                              result_pool);
+          break;
+        }
+    }
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+static int
+compare_items_as_revs_reverse(const svn_sort__item_t *a,
+                              const svn_sort__item_t *b)
+{
+  int c = svn_sort_compare_revisions(a->key, b->key);
+  if (c < 0)
+    return 1;
+  if (c > 0)
+    return -1;
+  return c;
+}
+
+/* Starting at MOVE->REV, loop over past revisions which contain moves,
+ * and look for a matching previous move in each. Once found, return
+ * it in *PREV_MOVE */
+static svn_error_t *
+find_prev_move(struct repos_move_info **prev_move,
+               apr_hash_t *moves_table,
+               struct repos_move_info *move,
+               svn_ra_session_t *ra_session,
+               const char *repos_root_url,
+               apr_pool_t *result_pool,
+               apr_pool_t *scratch_pool)
+{
+  apr_array_header_t *moves;
+  apr_array_header_t *revisions;
+  apr_pool_t *iterpool;
+  int i;
+
+  *prev_move = NULL;
+  revisions = svn_sort__hash(moves_table, compare_items_as_revs_reverse,
+                             scratch_pool);
+  iterpool = svn_pool_create(scratch_pool);
+  for (i = 0; i < revisions->nelts; i++)
+    {
+      svn_sort__item_t item = APR_ARRAY_IDX(revisions, i, svn_sort__item_t);
+      svn_revnum_t rev = *(svn_revnum_t *)item.key;
+
+      svn_pool_clear(iterpool);
+
+      if (rev >= move->rev)
+        continue;
+
+      moves = apr_hash_get(moves_table, &rev, sizeof(rev));
+      SVN_ERR(find_prev_move_in_revision(prev_move, moves, move,
+                                         ra_session, repos_root_url,
+                                         result_pool, iterpool));
+      if (*prev_move)
+        break;
+    }
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+
+/* Trace all past moves of the node moved by MOVE.
+ * Update MOVE->PREV and MOVE->NEXT accordingly. */
+static svn_error_t *
+trace_moved_node_backwards(apr_hash_t *moves_table,
+                           struct repos_move_info *move,
+                           svn_ra_session_t *ra_session,
+                           const char *repos_root_url,
+                           apr_pool_t *result_pool,
+                           apr_pool_t *scratch_pool)
+{
+  struct repos_move_info *prev_move;
+
+  SVN_ERR(find_prev_move(&prev_move, moves_table, move,
+                         ra_session, repos_root_url,
+                         result_pool, scratch_pool));
+  if (prev_move)
+    {
+      move->prev = prev_move;
+      prev_move->next = apr_array_make(result_pool, 1,
+                                       sizeof(struct repos_move_info *));
+      APR_ARRAY_PUSH(prev_move->next, struct repos_move_info *) = move;
+
+      SVN_ERR(trace_moved_node_backwards(moves_table, prev_move,
+                                         ra_session, repos_root_url,
+                                         result_pool, scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+reparent_session_and_fetch_node_kind(svn_node_kind_t *node_kind,
+                                     svn_ra_session_t *ra_session,
+                                     const char *url,
+                                     svn_revnum_t peg_rev,
+                                     apr_pool_t *scratch_pool)
+{
+  svn_error_t *err;
+
+  err = svn_ra_reparent(ra_session, url, scratch_pool);
+  if (err)
+    {
+      if (err->apr_err == SVN_ERR_RA_ILLEGAL_URL)
+        {
+          svn_error_clear(err);
+          *node_kind = svn_node_unknown;
+          return SVN_NO_ERROR;
+        }
+    
+      return svn_error_trace(err);
+    }
+
+  SVN_ERR(svn_ra_check_path(ra_session, "", peg_rev, node_kind, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
 /* Scan MOVES_TABLE for moves which affect a particular deleted node, and
  * build a set of new move information for this node.
  * Return heads of all possible move chains in *MOVES.
@@ -1766,6 +2147,7 @@ find_operative_moves(apr_array_header_t
   apr_array_header_t *moves_in_deleted_rev;
   int i;
   apr_pool_t *iterpool;
+  const char *session_url, *url = NULL;
 
   moves_in_deleted_rev = apr_hash_get(moves_table, &deleted_rev,
                                       sizeof(deleted_rev));
@@ -1775,6 +2157,8 @@ find_operative_moves(apr_array_header_t
       return SVN_NO_ERROR;
     }
 
+  SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, scratch_pool));
+
   /* Look for operative moves in the revision where the node was deleted. */
   *moves = apr_array_make(scratch_pool, 0, sizeof(struct repos_move_info *));
   iterpool = svn_pool_create(scratch_pool);
@@ -1788,15 +2172,25 @@ find_operative_moves(apr_array_header_t
       move = APR_ARRAY_IDX(moves_in_deleted_rev, i, struct repos_move_info *);
       relpath = svn_relpath_skip_ancestor(move->moved_from_repos_relpath,
                                           deleted_repos_relpath);
-      if (relpath)
+      if (relpath && relpath[0] != '\0')
         {
-          struct repos_move_info *new_move;
+          svn_node_kind_t node_kind;
 
-          new_move = new_path_adjusted_move(move, relpath, result_pool);
-          APR_ARRAY_PUSH(*moves, struct repos_move_info *) = new_move;
+          url = svn_path_url_add_component2(repos_root_url,
+                                            deleted_repos_relpath,
+                                            iterpool);
+          SVN_ERR(reparent_session_and_fetch_node_kind(&node_kind,
+                                                       ra_session, url,
+                                                       rev_below(deleted_rev),
+                                                       iterpool));
+          move = new_path_adjusted_move(move, relpath, node_kind, result_pool);
         }
+      APR_ARRAY_PUSH(*moves, struct repos_move_info *) = move;
     }
 
+  if (url != NULL)
+    SVN_ERR(svn_ra_reparent(ra_session, session_url, scratch_pool));
+
   /* If we didn't find any applicable moves, return NULL. */
   if ((*moves)->nelts == 0)
     {
@@ -1862,13 +2256,16 @@ find_revision_for_suspected_deletion(svn
 
   SVN_ERR_ASSERT(start_rev > end_rev);
 
-  SVN_ERR(find_moves_in_revision_range(&moves_table, parent_repos_relpath,
-                                       conflict, start_rev, end_rev,
-                                       ctx, result_pool, scratch_pool));
-
   SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, &repos_uuid,
                                              conflict, scratch_pool,
                                              scratch_pool));
+  victim_abspath = svn_client_conflict_get_local_abspath(conflict);
+
+  SVN_ERR(find_moves_in_revision_range(&moves_table, parent_repos_relpath,
+                                       repos_root_url, repos_uuid,
+                                       victim_abspath, start_rev, end_rev,
+                                       ctx, result_pool, scratch_pool));
+
   url = svn_path_url_add_component2(repos_root_url, parent_repos_relpath,
                                     scratch_pool);
   SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url,
@@ -1882,12 +2279,11 @@ 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;
-  b.related_repos_peg_rev = related_peg_rev;
+  b.related_peg_rev = related_peg_rev;
   b.deleted_rev = SVN_INVALID_REVNUM;
   b.replacing_node_kind = svn_node_unknown;
   b.repos_root_url = repos_root_url;
@@ -1929,7 +2325,7 @@ find_revision_for_suspected_deletion(svn
           *deleted_rev_author = move->rev_author;
           *replacing_node_kind = b.replacing_node_kind;
           SVN_ERR(find_operative_moves(moves, moves_table,
-                                       move->moved_from_repos_relpath,
+                                       b.deleted_repos_relpath,
                                        move->rev,
                                        ra_session, repos_root_url,
                                        result_pool, scratch_pool));
@@ -1954,7 +2350,6 @@ find_revision_for_suspected_deletion(svn
                                    b.deleted_repos_relpath, b.deleted_rev,
                                    ra_session, repos_root_url,
                                    result_pool, scratch_pool));
-
     }
 
   return SVN_NO_ERROR;
@@ -1972,11 +2367,20 @@ struct conflict_tree_local_missing_detai
   /* 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. */
+  /* Move information about the conflict victim. 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;
 
+  /* Move information about siblings. Siblings are nodes which share
+   * a youngest common ancestor with the conflict victim. E.g. in case
+   * of a merge operation they are part of the merge source branch.
+   * If not NULL, this is an array of repos_move_info elements.
+   * Each element is the head of a move chain, which starts at some
+   * point in history after siblings and conflict victim forked off
+   * their common ancestor. */
+  apr_array_header_t *sibling_moves;
+
   /* If not NULL, this is the move target abspath. */
   const char *moved_to_abspath;
 };
@@ -2063,6 +2467,140 @@ find_related_node(const char **related_r
   return SVN_NO_ERROR;
 }
 
+/* Determine if REPOS_RELPATH@PEG_REV was moved at some point in its history.
+ * History's range of interest ends at END_REV which must be older than PEG_REV.
+ *
+ * VICTIM_ABSPATH is the abspath of a conflict victim in the working copy and
+ * will be used in notifications.
+ *
+ * Return any applicable move chain heads in *MOVES.
+ * If no moves can be found, set *MOVES to NULL. */
+static svn_error_t *
+find_moves_in_natural_history(apr_array_header_t **moves,
+                              const char *repos_relpath,
+                              svn_revnum_t peg_rev,
+                              svn_node_kind_t node_kind,
+                              svn_revnum_t end_rev,
+                              const char *victim_abspath,
+                              const char *repos_root_url,
+                              const char *repos_uuid,
+                              svn_ra_session_t *ra_session,
+                              svn_client_ctx_t *ctx,
+                              apr_pool_t *result_pool,
+                              apr_pool_t *scratch_pool)
+{
+  apr_hash_t *moves_table;
+  apr_array_header_t *revs;
+  apr_array_header_t *most_recent_moves = NULL;
+  int i;
+  apr_pool_t *iterpool;
+
+  *moves = NULL;
+
+  SVN_ERR(find_moves_in_revision_range(&moves_table, repos_relpath,
+                                       repos_root_url, repos_uuid,
+                                       victim_abspath, peg_rev, end_rev,
+                                       ctx, scratch_pool, scratch_pool));
+
+  iterpool = svn_pool_create(scratch_pool);
+
+  /* Scan the moves table for applicable moves. */
+  revs = svn_sort__hash(moves_table, compare_items_as_revs, scratch_pool);
+  for (i = revs->nelts - 1; i >= 0; i--)
+    {
+      svn_sort__item_t item = APR_ARRAY_IDX(revs, i, svn_sort__item_t);
+      apr_array_header_t *moves_in_rev = apr_hash_get(moves_table, item.key,
+                                                      sizeof(svn_revnum_t));
+      int j;
+
+      svn_pool_clear(iterpool);
+
+      /* Was repos relpath moved to its location in this revision? */
+      for (j = 0; j < moves_in_rev->nelts; j++)
+        {
+          struct repos_move_info *move;
+          const char *relpath;
+
+          move = APR_ARRAY_IDX(moves_in_rev, j, struct repos_move_info *);
+          relpath = svn_relpath_skip_ancestor(move->moved_to_repos_relpath,
+                                              repos_relpath);
+          if (relpath)
+            {
+              /* If the move did not happen in our peg revision, make
+               * sure this move happened on the same line of history. */
+              if (move->rev != peg_rev)
+                {
+                  svn_client__pathrev_t *yca_loc;
+                  svn_error_t *err;
+
+                  err = find_yca(&yca_loc, repos_relpath, peg_rev,
+                                 repos_relpath, move->rev,
+                                 repos_root_url, repos_uuid,
+                                 NULL, ctx, iterpool, iterpool);
+                  if (err)
+                    {
+                      if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
+                        {
+                          svn_error_clear(err);
+                          yca_loc = NULL;
+                        }
+                      else
+                        return svn_error_trace(err);
+                    }
+
+                  if (yca_loc == NULL || yca_loc->rev != move->rev)
+                    continue;
+                }
+
+              if (most_recent_moves == NULL)
+                most_recent_moves =
+                  apr_array_make(result_pool, 1,
+                                 sizeof(struct repos_move_info *));
+
+              /* Copy the move to result pool (even if relpath is ""). */
+              move = new_path_adjusted_move(move, relpath, node_kind,
+                                            result_pool);
+              APR_ARRAY_PUSH(most_recent_moves,
+                             struct repos_move_info *) = move;
+            }
+        }
+
+      /* If we found one move, or several ambiguous moves, we're done. */
+      if (most_recent_moves)
+        break;
+    }
+
+  if (most_recent_moves && most_recent_moves->nelts > 0)
+    {
+      *moves = apr_array_make(result_pool, 1,
+                              sizeof(struct repos_move_info *));
+
+      /* Figure out what happened to the most recent moves in prior
+       * revisions and build move chains. */
+      for (i = 0; i < most_recent_moves->nelts; i++)
+        {
+          struct repos_move_info *move;
+
+          svn_pool_clear(iterpool);
+
+          move = APR_ARRAY_IDX(most_recent_moves, i, struct repos_move_info *);
+          SVN_ERR(trace_moved_node_backwards(moves_table, move,
+                                             ra_session, repos_root_url,
+                                             result_pool, iterpool));
+          /* Follow the move chain backwards. */
+          while (move->prev)
+            move = move->prev;
+
+          /* Return move heads. */
+          APR_ARRAY_PUSH(*moves, struct repos_move_info *) = move;
+        }
+    }
+
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
 /* Implements tree_conflict_get_details_func_t. */
 static svn_error_t *
 conflict_tree_get_details_local_missing(svn_client_conflict_t *conflict,
@@ -2080,9 +2618,12 @@ conflict_tree_get_details_local_missing(
   svn_node_kind_t replacing_node_kind;
   const char *deleted_basename;
   struct conflict_tree_local_missing_details *details;
-  apr_array_header_t *moves;
+  apr_array_header_t *moves = NULL;
+  apr_array_header_t *sibling_moves = NULL;
   const char *related_repos_relpath;
   svn_revnum_t related_peg_rev;
+  const char *repos_root_url;
+  const char *repos_uuid;
 
   SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
             &old_repos_relpath, &old_rev, NULL, conflict,
@@ -2096,7 +2637,7 @@ conflict_tree_get_details_local_missing(
   deleted_basename = svn_dirent_basename(conflict->local_abspath,
                                          scratch_pool);
   SVN_ERR(svn_wc__node_get_repos_info(&parent_peg_rev, &parent_repos_relpath,
-                                      NULL, NULL,
+                                      &repos_root_url, &repos_uuid,
                                       ctx->wc_ctx,
                                       svn_dirent_dirname(
                                         conflict->local_abspath,
@@ -2126,16 +2667,72 @@ conflict_tree_get_details_local_missing(
             parent_peg_rev, 0, related_repos_relpath, related_peg_rev,
             ctx, conflict->pool, scratch_pool));
 
+  /* If the victim was not deleted then check if the related path was moved. */
   if (deleted_rev == SVN_INVALID_REVNUM)
-    return SVN_NO_ERROR;
+    {
+      const char *victim_abspath;
+      svn_ra_session_t *ra_session;
+      const char *url, *corrected_url;
+      svn_client__pathrev_t *yca_loc;
+      svn_revnum_t end_rev;
+      svn_node_kind_t related_node_kind;
+
+      /* ### The following describes all moves in terms of forward-merges,
+       * should do we something else for reverse-merges? */
+
+      victim_abspath = svn_client_conflict_get_local_abspath(conflict);
+      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,
+                                                   url, NULL, NULL,
+                                                   FALSE,
+                                                   FALSE,
+                                                   ctx,
+                                                   scratch_pool,
+                                                   scratch_pool));
+
+      /* Set END_REV to our best guess of the nearest YCA revision. */
+      SVN_ERR(find_nearest_yca(&yca_loc, related_repos_relpath, related_peg_rev,
+                               parent_repos_relpath, parent_peg_rev,
+                               repos_root_url, repos_uuid, ra_session, ctx,
+                               scratch_pool, scratch_pool));
+      if (yca_loc == NULL)
+        return SVN_NO_ERROR;
+      end_rev = yca_loc->rev;
+
+      /* END_REV must be smaller than RELATED_PEG_REV, else the call
+         to find_moves_in_natural_history() below will error out. */
+      if (end_rev >= related_peg_rev)
+        end_rev = related_peg_rev > 0 ? related_peg_rev - 1 : 0;
+
+      SVN_ERR(svn_ra_check_path(ra_session, "", related_peg_rev,
+                                &related_node_kind, scratch_pool));
+      SVN_ERR(find_moves_in_natural_history(&sibling_moves,
+                                            related_repos_relpath,
+                                            related_peg_rev,
+                                            related_node_kind,
+                                            end_rev,
+                                            victim_abspath,
+                                            repos_root_url, repos_uuid,
+                                            ra_session, ctx,
+                                            conflict->pool, scratch_pool));
+
+      if (sibling_moves == NULL)
+        return SVN_NO_ERROR;
+
+      /* ## TODO: Find the missing node in the WC. */
+    }
 
   details = apr_pcalloc(conflict->pool, sizeof(*details));
   details->deleted_rev = deleted_rev;
   details->deleted_rev_author = deleted_rev_author;
-  details->deleted_repos_relpath = svn_relpath_join(parent_repos_relpath,
-                                                    deleted_basename,
-                                                    conflict->pool); 
+  if (deleted_rev != SVN_INVALID_REVNUM)
+    details->deleted_repos_relpath = svn_relpath_join(parent_repos_relpath,
+                                                      deleted_basename,
+                                                      conflict->pool); 
   details->moves = moves;
+  details->sibling_moves = sibling_moves;
                                          
   conflict->tree_conflict_local_details = details;
 
@@ -2287,22 +2884,74 @@ conflict_tree_get_description_local_miss
                              description, conflict, ctx,
                              result_pool, scratch_pool));
 
-  if (details->moves)
+  if (details->moves || details->sibling_moves)
     {
       struct repos_move_info *move;
+      
+      *description = _("No such file or directory was found in the "
+                       "merge target working copy.\n");
 
-      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."),
-                       move->moved_to_repos_relpath,
-                       move->rev, move->rev_author);
-      *description = append_moved_to_chain_description(*description,
-                                                       move->next,
-                                                       result_pool,
-                                                       scratch_pool);
+      if (details->moves)
+        {
+          move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+          if (move->node_kind == svn_node_file)
+            *description = apr_psprintf(
+                             result_pool,
+                             _("%sThe file was moved to '^/%s' in r%ld by %s."),
+                             *description, move->moved_to_repos_relpath,
+                             move->rev, move->rev_author);
+          else if (move->node_kind == svn_node_dir)
+            *description = apr_psprintf(
+                             result_pool,
+                             _("%sThe directory was moved to '^/%s' in "
+                               "r%ld by %s."),
+                             *description, move->moved_to_repos_relpath,
+                             move->rev, move->rev_author);
+          else
+            *description = apr_psprintf(
+                             result_pool,
+                             _("%sThe item was moved to '^/%s' in r%ld by %s."),
+                             *description, move->moved_to_repos_relpath,
+                             move->rev, move->rev_author);
+          *description = append_moved_to_chain_description(*description,
+                                                           move->next,
+                                                           result_pool,
+                                                           scratch_pool);
+        }
+
+      if (details->sibling_moves)
+        {
+          move = APR_ARRAY_IDX(details->sibling_moves, 0,
+                               struct repos_move_info *);
+          if (move->node_kind == svn_node_file)
+            *description = apr_psprintf(
+                             result_pool,
+                             _("%sThe file '^/%s' was moved to '^/%s' "
+                               "in r%ld by %s."),
+                             *description, move->moved_from_repos_relpath,
+                             move->moved_to_repos_relpath,
+                             move->rev, move->rev_author);
+          else if (move->node_kind == svn_node_dir)
+            *description = apr_psprintf(
+                             result_pool,
+                             _("%sThe directory '^/%s' was moved to '^/%s' "
+                               "in r%ld by %s."),
+                             *description, move->moved_from_repos_relpath,
+                             move->moved_to_repos_relpath,
+                             move->rev, move->rev_author);
+          else
+            *description = apr_psprintf(
+                             result_pool,
+                             _("%sThe item '^/%s' was moved to '^/%s' "
+                               "in r%ld by %s."),
+                             *description, move->moved_from_repos_relpath,
+                             move->moved_to_repos_relpath,
+                             move->rev, move->rev_author);
+          *description = append_moved_to_chain_description(*description,
+                                                           move->next,
+                                                           result_pool,
+                                                           scratch_pool);
+        }
     }
   else
     *description = apr_psprintf(
@@ -3994,7 +4643,8 @@ get_incoming_delete_details_for_reverse_
 }
 
 /* Follow each move chain starting a MOVE all the way to the end to find
- * the possible working copy locations for VICTIM_ABSPATH at PEG_REVISION.
+ * the possible working copy locations for VICTIM_ABSPATH which corresponds
+ * to VICTIM_REPOS_REPLATH@VICTIM_REVISION.
  * Add each such location to the WC_MOVE_TARGETS hash table, keyed on the
  * repos_relpath which is the corresponding move destination in the repository.
  * This function is recursive. */
@@ -4004,7 +4654,8 @@ follow_move_chains(apr_hash_t *wc_move_t
                    svn_client_ctx_t *ctx,
                    const char *victim_abspath,
                    svn_node_kind_t victim_node_kind,
-                   svn_revnum_t peg_revision,
+                   const char *victim_repos_relpath,
+                   svn_revnum_t victim_revision,
                    apr_pool_t *result_pool,
                    apr_pool_t *scratch_pool)
 {
@@ -4012,17 +4663,85 @@ follow_move_chains(apr_hash_t *wc_move_t
    * the working copy and add them to our collection if found. */
   if (move->next == NULL)
     {
-      apr_array_header_t *moved_to_abspaths;
+      apr_array_header_t *candidate_abspaths;
 
-      /* Gather nodes which represent this moved_to_repos_relpath. */
+      /* Gather candidate nodes which represent this moved_to_repos_relpath. */
       SVN_ERR(svn_wc__guess_incoming_move_target_nodes(
-                &moved_to_abspaths, ctx->wc_ctx,
+                &candidate_abspaths, ctx->wc_ctx,
                 victim_abspath, victim_node_kind,
                 move->moved_to_repos_relpath,
-                peg_revision, result_pool, scratch_pool));
-      if (moved_to_abspaths->nelts > 0)
-        svn_hash_sets(wc_move_targets, move->moved_to_repos_relpath,
-                      moved_to_abspaths);
+                scratch_pool, scratch_pool));
+      if (candidate_abspaths->nelts > 0)
+        {
+          apr_array_header_t *moved_to_abspaths;
+          int i;
+          apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+          moved_to_abspaths = apr_array_make(result_pool, 1,
+                                             sizeof (const char *));
+
+          for (i = 0; i < candidate_abspaths->nelts; i++)
+            {
+              const char *candidate_abspath;
+              const char *repos_root_url;
+              const char *repos_uuid;
+              const char *candidate_repos_relpath;
+              svn_revnum_t candidate_revision;
+
+              svn_pool_clear(iterpool);
+
+              candidate_abspath = APR_ARRAY_IDX(candidate_abspaths, i,
+                                                const char *);
+              SVN_ERR(svn_wc__node_get_origin(NULL, &candidate_revision,
+                                              &candidate_repos_relpath,
+                                              &repos_root_url,
+                                              &repos_uuid,
+                                              NULL, NULL,
+                                              ctx->wc_ctx,
+                                              candidate_abspath,
+                                              FALSE,
+                                              iterpool, iterpool));
+
+              if (candidate_revision == SVN_INVALID_REVNUM)
+                continue;
+
+              /* If the conflict victim and the move target candidate
+               * are not from the same revision we must ensure that
+               * they are related. */
+               if (candidate_revision != victim_revision)
+                {
+                  svn_client__pathrev_t *yca_loc;
+                  svn_error_t *err;
+
+                  err = find_yca(&yca_loc, victim_repos_relpath,
+                                 victim_revision,
+                                 candidate_repos_relpath,
+                                 candidate_revision,
+                                 repos_root_url, repos_uuid,
+                                 NULL, ctx, iterpool, iterpool);
+                  if (err)
+                    {
+                      if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
+                        {
+                          svn_error_clear(err);
+                          yca_loc = NULL;
+                        }
+                      else
+                        return svn_error_trace(err);
+                    }
+
+                  if (yca_loc == NULL)
+                    continue;
+                }
+
+              APR_ARRAY_PUSH(moved_to_abspaths, const char *) =
+                apr_pstrdup(result_pool, candidate_abspath);
+            }
+          svn_pool_destroy(iterpool);
+
+          svn_hash_sets(wc_move_targets, move->moved_to_repos_relpath,
+                        moved_to_abspaths);
+        }
     }
   else
     {
@@ -4040,7 +4759,8 @@ follow_move_chains(apr_hash_t *wc_move_t
           next_move = APR_ARRAY_IDX(move->next, i, struct repos_move_info *);
           SVN_ERR(follow_move_chains(wc_move_targets, next_move,
                                      ctx, victim_abspath, victim_node_kind,
-                                     peg_revision, result_pool, iterpool));
+                                     victim_repos_relpath, victim_revision,
+                                     result_pool, iterpool));
                                         
         }
       svn_pool_destroy(iterpool);
@@ -4058,14 +4778,17 @@ init_wc_move_targets(struct conflict_tre
   int i;
   const char *victim_abspath;
   svn_node_kind_t victim_node_kind;
+  const char *incoming_new_repos_relpath;
   svn_revnum_t incoming_new_pegrev;
   svn_wc_operation_t operation;
 
   victim_abspath = svn_client_conflict_get_local_abspath(conflict);
   victim_node_kind = svn_client_conflict_tree_get_victim_node_kind(conflict);
   operation = svn_client_conflict_get_operation(conflict);
+  /* ### Should we get the old location in case of reverse-merges? */
   SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(
-            NULL, &incoming_new_pegrev, NULL, conflict,
+            &incoming_new_repos_relpath, &incoming_new_pegrev,
+            NULL, conflict,
             scratch_pool, scratch_pool));
   details->wc_move_targets = apr_hash_make(conflict->pool);
   for (i = 0; i < details->moves->nelts; i++)
@@ -4076,6 +4799,7 @@ init_wc_move_targets(struct conflict_tre
       SVN_ERR(follow_move_chains(details->wc_move_targets, move,
                                  ctx, victim_abspath,
                                  victim_node_kind,
+                                 incoming_new_repos_relpath,
                                  incoming_new_pegrev,
                                  conflict->pool, scratch_pool));
     }
@@ -9264,6 +9988,7 @@ configure_option_local_move_file_merge(s
               SVN_ERR(follow_move_chains(wc_move_targets, move, ctx,
                                          conflict->local_abspath,
                                          svn_node_file,
+                                         incoming_new_repos_relpath,
                                          incoming_new_pegrev,
                                          scratch_pool, iterpool));
             }
@@ -9603,6 +10328,22 @@ svn_client_conflict_tree_get_resolution_
   return SVN_NO_ERROR;
 }
 
+/* Swallow authz failures and return SVN_NO_ERROR in that case.
+ * Otherwise, return ERR unchanged. */
+static svn_error_t *
+ignore_authz_failures(svn_error_t *err)
+{
+  if (err && (   svn_error_find_cause(err, SVN_ERR_AUTHZ_UNREADABLE)
+              || svn_error_find_cause(err, SVN_ERR_RA_NOT_AUTHORIZED)
+              || svn_error_find_cause(err, SVN_ERR_RA_DAV_FORBIDDEN)))
+    {
+      svn_error_clear(err);
+      err = SVN_NO_ERROR;
+    }
+
+  return err;
+}
+
 svn_error_t *
 svn_client_conflict_tree_get_details(svn_client_conflict_t *conflict,
                                      svn_client_ctx_t *ctx,
@@ -9622,13 +10363,18 @@ svn_client_conflict_tree_get_details(svn
                                   scratch_pool);
     }
 
+  /* Collecting conflict details may fail due to insufficient access rights.
+   * This is not a failure but simply restricts our future options. */
   if (conflict->tree_conflict_get_incoming_details_func)
-    SVN_ERR(conflict->tree_conflict_get_incoming_details_func(conflict, ctx,
-                                                              scratch_pool));
+    SVN_ERR(ignore_authz_failures(
+      conflict->tree_conflict_get_incoming_details_func(conflict, ctx,
+                                                        scratch_pool)));
+
 
   if (conflict->tree_conflict_get_local_details_func)
-    SVN_ERR(conflict->tree_conflict_get_local_details_func(conflict, ctx,
-                                                           scratch_pool));
+    SVN_ERR(ignore_authz_failures(
+      conflict->tree_conflict_get_local_details_func(conflict, ctx,
+                                                    scratch_pool)));
 
   if (ctx->notify_func2)
     {