Author: rhuijben
Date: Mon Oct 5 18:15:08 2015
New Revision: 1706893
URL: http://svn.apache.org/viewvc?rev=1706893&view=rev
Log:
Extend the 'svn patch' handling of moves to detect recorded moves that are
already applied.
* subversion/libsvn_client/patch.c
(init_patch_target): Verify if a move can be applied before opening the
target file. If the target is already moved to this exact location just
apply the patch to the target.
* subversion/tests/cmdline/patch_tests.py
(patch_git_rename,
patch_move_and_change): Retry applying the move patch.
Modified:
subversion/trunk/subversion/libsvn_client/patch.c
subversion/trunk/subversion/tests/cmdline/patch_tests.py
Modified: subversion/trunk/subversion/libsvn_client/patch.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/patch.c?rev=1706893&r1=1706892&r2=1706893&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/patch.c (original)
+++ subversion/trunk/subversion/libsvn_client/patch.c Mon Oct 5 18:15:08 2015
@@ -1039,7 +1039,8 @@ init_patch_target(patch_target_t **patch
target->operation = patch->operation;
if (patch->operation == svn_diff_op_added /* Allow replacing */
- || patch->operation == svn_diff_op_added)
+ || patch->operation == svn_diff_op_added
+ || patch->operation == svn_diff_op_moved)
{
follow_moves = FALSE;
}
@@ -1067,47 +1068,6 @@ init_patch_target(patch_target_t **patch
target->git_symlink_format = TRUE;
}
- /* Create a temporary file to write the patched result to.
- * Also grab various bits of information about the file. */
- if (target->is_symlink)
- {
- struct symlink_baton_t *sb = apr_pcalloc(result_pool, sizeof(*sb));
- content->existed = TRUE;
-
- sb->local_abspath = target->local_abspath;
-
- /* Wire up the read callbacks. */
- content->read_baton = sb;
-
- content->readline = target->git_symlink_format ? readline_symlink_git
- : readline_symlink;
- content->seek = seek_symlink;
- content->tell = tell_symlink;
- }
- else if (target->kind_on_disk == svn_node_file)
- {
- SVN_ERR(svn_io_file_open(&target->file, target->local_abspath,
- APR_READ | APR_BUFFERED,
- APR_OS_DEFAULT, result_pool));
- SVN_ERR(svn_io_is_file_executable(&target->executable,
- target->local_abspath,
- scratch_pool));
- SVN_ERR(obtain_eol_and_keywords_for_file(&content->keywords,
- &content->eol_style,
- &content->eol_str,
- wc_ctx,
- target->local_abspath,
- result_pool,
- scratch_pool));
- content->existed = TRUE;
-
- /* Wire up the read callbacks. */
- content->readline = readline_file;
- content->seek = seek_file;
- content->tell = tell_file;
- content->read_baton = target->file;
- }
-
/* ### Is it ok to set the operation of the target already here? Isn't
* ### the target supposed to be marked with an operation after we have
* ### determined that the changes will apply cleanly to the WC? Maybe
@@ -1123,6 +1083,7 @@ init_patch_target(patch_target_t **patch
const char *move_target_path;
const char *move_target_relpath;
svn_boolean_t under_root;
+ svn_boolean_t is_special;
svn_node_kind_t kind_on_disk;
svn_node_kind_t wc_kind;
@@ -1162,22 +1123,104 @@ init_patch_target(patch_target_t **patch
return SVN_NO_ERROR;
}
- SVN_ERR(svn_io_check_path(target->move_target_abspath,
- &kind_on_disk, scratch_pool));
+ SVN_ERR(svn_io_check_special_path(target->move_target_abspath,
+ &kind_on_disk, &is_special,
+ scratch_pool));
SVN_ERR(svn_wc_read_kind2(&wc_kind, wc_ctx,
target->move_target_abspath,
FALSE, FALSE, scratch_pool));
- if (kind_on_disk != svn_node_none || wc_kind != svn_node_none
+ if (wc_kind == svn_node_file || wc_kind == svn_node_dir)
+ {
+ /* The move target path already exists on disk. */
+ svn_error_t *err;
+ const char *moved_from_abspath;
+
+ err = svn_wc__node_was_moved_here(&moved_from_abspath, NULL,
+ wc_ctx,
+ target->move_target_abspath,
+ scratch_pool, scratch_pool);
+
+ if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ err = NULL;
+ moved_from_abspath = NULL;
+ }
+ else
+ SVN_ERR(err);
+
+ if (moved_from_abspath && (strcmp(moved_from_abspath,
+ target->local_abspath) == 0))
+ {
+ target->local_abspath = target->move_target_abspath;
+ target->move_target_abspath = NULL;
+ target->operation = svn_diff_op_modified;
+ target->locally_deleted = FALSE;
+ target->db_kind = wc_kind;
+ target->kind_on_disk = kind_on_disk;
+ target->is_special = is_special;
+
+ target->had_already_applied = TRUE; /* Make sure we notify */
+ }
+ else
+ {
+ target->skipped = TRUE;
+ target->move_target_abspath = NULL;
+ return SVN_NO_ERROR;
+ }
+
+ }
+ else if (kind_on_disk != svn_node_none
|| target_is_added(targets_info, target->move_target_abspath,
scratch_pool))
{
- /* The move target path already exists on disk. Skip target. */
- target->skipped = TRUE;
- target->move_target_abspath = NULL;
- return SVN_NO_ERROR;
+ target->skipped = TRUE;
+ target->move_target_abspath = NULL;
+ return SVN_NO_ERROR;
}
}
+ /* Create a temporary file to write the patched result to.
+ * Also grab various bits of information about the file. */
+ if (target->is_symlink)
+ {
+ struct symlink_baton_t *sb = apr_pcalloc(result_pool, sizeof(*sb));
+ content->existed = TRUE;
+
+ sb->local_abspath = target->local_abspath;
+
+ /* Wire up the read callbacks. */
+ content->read_baton = sb;
+
+ content->readline = target->git_symlink_format ? readline_symlink_git
+ : readline_symlink;
+ content->seek = seek_symlink;
+ content->tell = tell_symlink;
+ }
+ else if (target->kind_on_disk == svn_node_file)
+ {
+ SVN_ERR(svn_io_file_open(&target->file, target->local_abspath,
+ APR_READ | APR_BUFFERED,
+ APR_OS_DEFAULT, result_pool));
+ SVN_ERR(svn_io_is_file_executable(&target->executable,
+ target->local_abspath,
+ scratch_pool));
+ SVN_ERR(obtain_eol_and_keywords_for_file(&content->keywords,
+ &content->eol_style,
+ &content->eol_str,
+ wc_ctx,
+ target->local_abspath,
+ result_pool,
+ scratch_pool));
+ content->existed = TRUE;
+
+ /* Wire up the read callbacks. */
+ content->readline = readline_file;
+ content->seek = seek_file;
+ content->tell = tell_file;
+ content->read_baton = target->file;
+ }
+
/* Open a temporary file to write the patched result to. */
SVN_ERR(svn_io_open_unique_file3(&target->patched_file,
&target->patched_path, NULL,
Modified: subversion/trunk/subversion/tests/cmdline/patch_tests.py
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/patch_tests.py?rev=1706893&r1=1706892&r2=1706893&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/patch_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/patch_tests.py Mon Oct 5
18:15:08 2015
@@ -4817,14 +4817,19 @@ def patch_git_rename(sbox):
[], True, True)
# Retry
- #svntest.actions.run_and_verify_patch(wc_dir, patch_file_path,
- # expected_output, expected_disk,
- # expected_status, expected_skip,
- # [], True, True)
+ expected_output = wc.State(wc_dir, {
+ 'iota2' : Item(status='G ')
+ })
+ svntest.actions.run_and_verify_patch(wc_dir, patch_file_path,
+ expected_output, expected_disk,
+ expected_status, expected_skip,
+ [], True, True)
# Reverse
- expected_output.tweak('iota', status='A ')
- expected_output.tweak('iota2', status='D ')
+ expected_output = wc.State(wc_dir, {
+ 'iota2' : Item(status='D '),
+ 'iota' : Item(status='A '),
+ })
expected_disk.remove('iota2')
expected_disk.add({
'iota' : Item(contents="This is the file 'iota'.\n"),
@@ -7497,7 +7502,16 @@ def patch_move_and_change(sbox):
expected_status, expected_skip,
[], True, True)
- # Retry won't work yet, but let's try a reverse merge
+ # Retry
+ expected_output = svntest.wc.State(wc_dir, {
+ 'alpha' : Item(status='GG'),
+ })
+ svntest.actions.run_and_verify_patch(wc_dir, patch,
+ expected_output, expected_disk,
+ expected_status, expected_skip,
+ [], True, True)
+
+ # And now reverse
expected_output = wc.State(wc_dir, {
'alpha' : Item(status='D '),
'A/B/E/alpha' : Item(status='A '),