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)
{