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 '),


Reply via email to