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 2018/03/22 15:01:07 UTC
svn commit: r1827503 - in /subversion/trunk/subversion: include/svn_client.h
libsvn_client/shelf.c svn/shelf-cmd.c tests/cmdline/shelf_tests.py
Author: julianfoad
Date: Thu Mar 22 15:01:07 2018
New Revision: 1827503
URL: http://svn.apache.org/viewvc?rev=1827503&view=rev
Log:
Shelving: refuse to unshelve if there are conflicts.
* subversion/include/svn_client.h,
subversion/libsvn_client/shelf.c
(svn_client_shelf_apply): Document better.
(patch_filter_baton_t, patch_filter,
patch_notify_baton_t, patch_notify,
svn_client_shelf_test_apply_file): New.
* subversion/svn/shelf-cmd.c
(test_apply): New.
(shelf_restore): Error if any path would have a conflict, unless forced.
* subversion/tests/cmdline/shelf_tests.py
(state_from_status): Move to top of file...
(shelve_unshelve_verify): ... and use to simplify here.
(unshelve_refuses_if_conflicts): New.
(test_list): Run it.
Modified:
subversion/trunk/subversion/include/svn_client.h
subversion/trunk/subversion/libsvn_client/shelf.c
subversion/trunk/subversion/svn/shelf-cmd.c
subversion/trunk/subversion/tests/cmdline/shelf_tests.py
Modified: subversion/trunk/subversion/include/svn_client.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_client.h?rev=1827503&r1=1827502&r2=1827503&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_client.h (original)
+++ subversion/trunk/subversion/include/svn_client.h Thu Mar 22 15:01:07 2018
@@ -7064,6 +7064,10 @@ svn_client_shelf_get_all_versions(apr_ar
/** Apply @a shelf_version to the WC.
*
+ * If @a dry_run is true, try applying the shelf-version to the WC and
+ * report the full set of notifications about successes and conflicts,
+ * but leave the WC untouched.
+ *
* @since New in 1.X.
* @warning EXPERIMENTAL.
*/
@@ -7073,6 +7077,24 @@ svn_client_shelf_apply(svn_client_shelf_
svn_boolean_t dry_run,
apr_pool_t *scratch_pool);
+/** Test whether we can successfully apply the patch for @a file_relpath
+ * in @a shelf_version to the WC.
+ *
+ * Try applying the shelf-version to the WC and set @a *conflict_p to
+ * true if any conflict occurs, else to false.
+ *
+ * Leave the WC untouched.
+ *
+ * @since New in 1.X.
+ * @warning EXPERIMENTAL.
+ */
+SVN_EXPERIMENTAL
+svn_error_t *
+svn_client_shelf_test_apply_file(svn_boolean_t *conflict_p,
+ svn_client_shelf_version_t *shelf_version,
+ const char *file_relpath,
+ apr_pool_t *scratch_pool);
+
/** Reverse-apply @a shelf_version to the WC.
*
* @since New in 1.X.
Modified: subversion/trunk/subversion/libsvn_client/shelf.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/shelf.c?rev=1827503&r1=1827502&r2=1827503&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/shelf.c (original)
+++ subversion/trunk/subversion/libsvn_client/shelf.c Thu Mar 22 15:01:07 2018
@@ -592,6 +592,82 @@ svn_client_shelf_paths_changed(apr_hash_
return SVN_NO_ERROR;
}
+/* A filter to only apply the patch to a particular file. */
+struct patch_filter_baton_t
+{
+ /* The single path to be selected for patching */
+ const char *path;
+};
+
+static svn_error_t *
+patch_filter(void *baton,
+ svn_boolean_t *filtered,
+ const char *canon_path_from_patchfile,
+ const char *patch_abspath,
+ const char *reject_abspath,
+ apr_pool_t *scratch_pool)
+{
+ struct patch_filter_baton_t *fb = baton;
+
+ *filtered = (strcmp(canon_path_from_patchfile, fb->path) != 0);
+ return SVN_NO_ERROR;
+}
+
+/* Intercept patch notifications to detect when there is a conflict */
+struct patch_notify_baton_t
+{
+ svn_boolean_t conflict;
+};
+
+/* Intercept patch notifications to detect when there is a conflict */
+static void
+patch_notify(void *baton,
+ const svn_wc_notify_t *notify,
+ apr_pool_t *pool)
+{
+ struct patch_notify_baton_t *nb = baton;
+
+ if (notify->action == svn_wc_notify_patch_rejected_hunk
+ || notify->action == svn_wc_notify_skip)
+ nb->conflict = TRUE;
+}
+
+svn_error_t *
+svn_client_shelf_test_apply_file(svn_boolean_t *conflict_p,
+ svn_client_shelf_version_t *shelf_version,
+ const char *file_relpath,
+ apr_pool_t *scratch_pool)
+{
+ svn_client_ctx_t *ctx = shelf_version->shelf->ctx;
+ svn_wc_notify_func2_t ctx_notify_func;
+ void *ctx_notify_baton;
+ struct patch_filter_baton_t fb;
+ struct patch_notify_baton_t nb;
+
+ fb.path = file_relpath;
+
+ nb.conflict = FALSE;
+ ctx_notify_func = ctx->notify_func2;
+ ctx_notify_baton = ctx->notify_baton2;
+ ctx->notify_func2 = patch_notify;
+ ctx->notify_baton2 = &nb;
+
+ SVN_ERR(svn_client_patch(shelf_version->patch_abspath,
+ shelf_version->shelf->wc_root_abspath,
+ TRUE /*dry_run*/, 0 /*strip*/,
+ FALSE /*reverse*/,
+ FALSE /*ignore_whitespace*/,
+ TRUE /*remove_tempfiles*/,
+ patch_filter, &fb,
+ shelf_version->shelf->ctx, scratch_pool));
+
+ ctx->notify_func2 = ctx_notify_func;
+ ctx->notify_baton2 = ctx_notify_baton;
+
+ *conflict_p = nb.conflict;
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
svn_client_shelf_apply(svn_client_shelf_version_t *shelf_version,
svn_boolean_t dry_run,
Modified: subversion/trunk/subversion/svn/shelf-cmd.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/shelf-cmd.c?rev=1827503&r1=1827502&r2=1827503&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/shelf-cmd.c (original)
+++ subversion/trunk/subversion/svn/shelf-cmd.c Thu Mar 22 15:01:07 2018
@@ -564,7 +564,34 @@ shelve(int *new_version_p,
return SVN_NO_ERROR;
}
-/* Throw an error if any paths affected by SHELF:VERSION are currently
+/* Throw an error if any path affected by SHELF_VERSION gives a conflict
+ * when applied (as a dry-run) to the WC. */
+static svn_error_t *
+test_apply(svn_client_shelf_version_t *shelf_version,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_t *paths;
+ apr_hash_index_t *hi;
+
+ SVN_ERR(svn_client_shelf_paths_changed(&paths, shelf_version,
+ scratch_pool, scratch_pool));
+ for (hi = apr_hash_first(scratch_pool, paths); hi; hi = apr_hash_next(hi))
+ {
+ const char *path = apr_hash_this_key(hi);
+ svn_boolean_t conflict;
+
+ SVN_ERR(svn_client_shelf_test_apply_file(&conflict, shelf_version, path,
+ scratch_pool));
+ if (conflict)
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Conflict in applying shelf '%s' path '%s'"),
+ shelf_version->shelf->name, path);
+ }
+ return SVN_NO_ERROR;
+}
+
+/* Throw an error if any paths affected by SHELF_VERSION are currently
* modified in the WC. */
static svn_error_t *
check_no_modified_paths(const char *paths_base_abspath,
@@ -642,13 +669,15 @@ patch_notify(void *baton,
* or the newest version is @a arg is null.
*
* If @a dry_run is true, don't actually do it.
+ *
+ * Error if any path would have a conflict, unless @a force_if_conflict.
*/
static svn_error_t *
shelf_restore(const char *name,
const char *arg,
svn_boolean_t dry_run,
svn_boolean_t quiet,
- svn_boolean_t force_already_modified,
+ svn_boolean_t force_if_conflict,
const char *local_abspath,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
@@ -685,10 +714,11 @@ shelf_restore(const char *name,
SVN_ERR(stats(shelf, version, shelf_version, time_now,
TRUE /*with_logmsg*/, scratch_pool));
}
- if (! force_already_modified)
+ if (! force_if_conflict)
{
- SVN_ERR(check_no_modified_paths(shelf->wc_root_abspath,
- shelf_version, quiet, ctx, scratch_pool));
+ SVN_ERR_W(test_apply(shelf_version, ctx, scratch_pool),
+ _("Cannot unshelve/restore, as at least one "
+ "path would conflict"));
}
b.rejects = FALSE;
Modified: subversion/trunk/subversion/tests/cmdline/shelf_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/shelf_tests.py?rev=1827503&r1=1827502&r2=1827503&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/shelf_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/shelf_tests.py Thu Mar 22 15:01:07 2018
@@ -44,6 +44,11 @@ Item = wc.StateItem
#----------------------------------------------------------------------
+def state_from_status(wc_dir):
+ _, output, _ = svntest.main.run_svn(None, 'status', '-v', '-u', '-q',
+ wc_dir)
+ return svntest.wc.State.from_status(output, wc_dir)
+
def shelve_unshelve_verify(sbox):
"""Round-trip: shelve; verify all changes are reverted;
unshelve; verify all changes are restored.
@@ -52,9 +57,7 @@ def shelve_unshelve_verify(sbox):
wc_dir = sbox.wc_dir
# Save the modified state
- _, output, _ = svntest.main.run_svn(None, 'status', '-v', '-u', '-q',
- wc_dir)
- modified_state = svntest.wc.State.from_status(output, wc_dir)
+ modified_state = state_from_status(wc_dir)
# Shelve; check there are no longer any modifications
svntest.actions.run_and_verify_svn(None, [],
@@ -184,11 +187,6 @@ def shelve_from_inner_path(sbox):
#----------------------------------------------------------------------
-def state_from_status(wc_dir):
- _, output, _ = svntest.main.run_svn(None, 'status', '-v', '-u', '-q',
- wc_dir)
- return svntest.wc.State.from_status(output, wc_dir)
-
def save_revert_restore(sbox, modifier1, modifier2):
"Save 2 checkpoints; revert; restore 1st"
@@ -255,6 +253,49 @@ def shelve_mergeinfo(sbox):
shelve_unshelve(sbox, modifier)
+#----------------------------------------------------------------------
+
+def unshelve_refuses_if_conflicts(sbox):
+ "unshelve refuses if conflicts"
+
+ def modifier1(sbox):
+ sbox.simple_append('alpha', 'A-mod1\nB\nC\nD\n', truncate=True)
+ sbox.simple_append('beta', 'A-mod1\nB\nC\nD\n', truncate=True)
+
+ def modifier2(sbox):
+ sbox.simple_append('beta', 'A-mod2\nB\nC\nD\n', truncate=True)
+
+ sbox.build(empty=True)
+ was_cwd = os.getcwd()
+ os.chdir(sbox.wc_dir)
+ sbox.wc_dir = ''
+ wc_dir = ''
+
+ sbox.simple_add_text('A\nB\nC\nD\n', 'alpha')
+ sbox.simple_add_text('A\nB\nC\nD\n', 'beta')
+ sbox.simple_commit()
+ initial_state = state_from_status(wc_dir)
+
+ # Make initial mods; remember this modified state
+ modifier1(sbox)
+ modified_state1 = state_from_status(wc_dir)
+ assert modified_state1 != initial_state
+
+ # Shelve; check there are no longer any local mods
+ svntest.actions.run_and_verify_svn(None, [],
+ 'shelve', 'foo')
+ svntest.actions.run_and_verify_status(wc_dir, initial_state)
+
+ # Make a different local mod that will conflict with the shelf
+ modifier2(sbox)
+ modified_state2 = state_from_status(wc_dir)
+
+ # Try to unshelve; check it fails with an error about a conflict
+ svntest.actions.run_and_verify_svn(None, '.*[Cc]onflict.*',
+ 'unshelve', 'foo')
+ # Check nothing changed in the attempt
+ svntest.actions.run_and_verify_status(wc_dir, modified_state2)
+
########################################################################
# Run the tests
@@ -270,6 +311,7 @@ test_list = [ None,
shelve_from_inner_path,
checkpoint_basic,
shelve_mergeinfo,
+ unshelve_refuses_if_conflicts,
]
if __name__ == '__main__':