You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2015/10/01 13:41:39 UTC

svn commit: r1706217 - in /subversion/trunk/subversion: libsvn_client/patch.c tests/cmdline/patch_tests.py

Author: rhuijben
Date: Thu Oct  1 11:41:39 2015
New Revision: 1706217

URL: http://svn.apache.org/viewvc?rev=1706217&view=rev
Log:
Record the patch dry run information in a way that even patches that replace
files can give acurate results.

* subversion/libsvn_client/patch.c
  (patch_target_info_t): Add boolean for adds.
  (target_is_added,
   target_is_deleted): New functions.
  (resolve_target_path): Handle self-deleted as not existing.
  (init_patch_target,
   apply_one_patch): Pass info.
  (create_missing_parents): Use target info instead of hashtable.
    Move cancel function up a bit.
  (install_patched_target): Pass info instead of hash.
  (apply_patches): Record info a bit later to allow processing just
    earlier items.

* subversion/tests/cmdline/patch_tests.py
  (patch_deletes_prop): Enable dry-run.
  (patch_git_symlink): New function.
  (test_list): Add patch_git_symlink.

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=1706217&r1=1706216&r2=1706217&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/patch.c (original)
+++ subversion/trunk/subversion/libsvn_client/patch.c Thu Oct  1 11:41:39 2015
@@ -273,8 +273,58 @@ typedef struct patch_target_t {
 typedef struct patch_target_info_t {
   const char *local_abspath;
   svn_boolean_t deleted;
+  svn_boolean_t added;
 } patch_target_info_t;
 
+/* Check if LOCAL_ABSPATH is recorded as added in TARGETS_INFO */
+static svn_boolean_t
+target_is_added(const apr_array_header_t *targets_info,
+                const char *local_abspath,
+                apr_pool_t *scratch_pool)
+{
+  int i;
+
+  for (i = targets_info->nelts - 1; i >= 0; i--)
+  {
+    const patch_target_info_t *target_info =
+      APR_ARRAY_IDX(targets_info, i, const patch_target_info_t *);
+
+    const char *info = svn_dirent_skip_ancestor(target_info->local_abspath,
+                                                local_abspath);
+
+    if (info && !*info)
+      return target_info->added;
+    else if (info)
+      return FALSE;
+  }
+
+  return FALSE;
+}
+
+/* Check if LOCAL_ABSPATH or an ancestor is recorded as deleted in
+   TARGETS_INFO */
+static svn_boolean_t
+target_is_deleted(const apr_array_header_t *targets_info,
+                  const char *local_abspath,
+                  apr_pool_t *scratch_pool)
+{
+  int i;
+
+  for (i = targets_info->nelts - 1; i >= 0; i--)
+  {
+    const patch_target_info_t *target_info =
+      APR_ARRAY_IDX(targets_info, i, const patch_target_info_t *);
+
+    const char *info = svn_dirent_skip_ancestor(target_info->local_abspath,
+                                                local_abspath);
+
+    if (info)
+      return target_info->deleted;
+  }
+
+  return FALSE;
+}
+
 
 /* Strip STRIP_COUNT components from the front of PATH, returning
  * the result in *RESULT, allocated in RESULT_POOL.
@@ -392,6 +442,7 @@ resolve_target_path(patch_target_t *targ
                     int strip_count,
                     svn_boolean_t has_text_changes,
                     svn_wc_context_t *wc_ctx,
+                    const apr_array_header_t *targets_info,
                     apr_pool_t *result_pool,
                     apr_pool_t *scratch_pool)
 {
@@ -454,6 +505,13 @@ resolve_target_path(patch_target_t *targ
       return SVN_NO_ERROR;
     }
 
+  if (target_is_deleted(targets_info, target->local_abspath, scratch_pool))
+    {
+      target->locally_deleted = TRUE;
+      target->db_kind = svn_node_none;
+      return SVN_NO_ERROR;
+    }
+
   /* Skip things we should not be messing with. */
   err = svn_wc_status3(&status, wc_ctx, target->local_abspath,
                        result_pool, scratch_pool);
@@ -979,6 +1037,7 @@ init_patch_target(patch_target_t **patch
                   const char *root_abspath,
                   svn_wc_context_t *wc_ctx, int strip_count,
                   svn_boolean_t remove_tempfiles,
+                  const apr_array_header_t *targets_info,
                   apr_pool_t *result_pool, apr_pool_t *scratch_pool)
 {
   patch_target_t *target;
@@ -1008,7 +1067,7 @@ init_patch_target(patch_target_t **patch
 
   SVN_ERR(resolve_target_path(target, choose_target_filename(patch),
                               root_abspath, strip_count, has_text_changes,
-                              wc_ctx, result_pool, scratch_pool));
+                              wc_ctx, targets_info, result_pool, scratch_pool));
   *patch_target = target;
   if (! target->skipped)
     {
@@ -2334,6 +2393,7 @@ apply_one_patch(patch_target_t **patch_t
                 int strip_count,
                 svn_boolean_t ignore_whitespace,
                 svn_boolean_t remove_tempfiles,
+                const apr_array_header_t *targets_info,
                 svn_client_patch_func_t patch_func,
                 void *patch_baton,
                 svn_cancel_func_t cancel_func,
@@ -2349,7 +2409,8 @@ apply_one_patch(patch_target_t **patch_t
   svn_boolean_t has_text_changes = FALSE;
 
   SVN_ERR(init_patch_target(&target, patch, abs_wc_path, wc_ctx, strip_count,
-                            remove_tempfiles, result_pool, scratch_pool));
+                            remove_tempfiles, targets_info,
+                            result_pool, scratch_pool));
   if (target->skipped)
     {
       *patch_target = target;
@@ -2821,7 +2882,7 @@ create_missing_parents(patch_target_t *t
                        const char *abs_wc_path,
                        svn_client_ctx_t *ctx,
                        svn_boolean_t dry_run,
-                       apr_hash_t *already_added,
+                       apr_array_header_t *targets_info,
                        apr_pool_t *scratch_pool)
 {
   const char *local_abspath;
@@ -2902,33 +2963,41 @@ create_missing_parents(patch_target_t *t
       for (i = present_components; i < components->nelts - 1; i++)
         {
           const char *component;
+          patch_target_info_t *pti;
 
           svn_pool_clear(iterpool);
 
+          if (ctx->cancel_func)
+            SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
+
           component = APR_ARRAY_IDX(components, i, const char *);
           local_abspath = svn_dirent_join(local_abspath, component,
-                                          scratch_pool);
+                                          iterpool);
+
+          if (target_is_added(targets_info, local_abspath, iterpool))
+            continue;
+
+          pti = apr_pcalloc(targets_info->pool, sizeof(*pti));
+
+          pti->local_abspath = apr_pstrdup(targets_info->pool,
+                                           local_abspath);
+          pti->added = TRUE;
+
+          APR_ARRAY_PUSH(targets_info, patch_target_info_t *) = pti;
+
           if (dry_run)
             {
-              if (!svn_hash_gets(already_added, local_abspath))
+              if (ctx->notify_func2)
                 {
-                  svn_hash_sets(already_added,
-                                apr_pstrdup(apr_hash_pool_get(already_added),
-                                            local_abspath),
-                                "");
-
-                  if (ctx->notify_func2)
-                    {
-                      /* Just do notification. */
-                      svn_wc_notify_t *notify;
-                      notify = svn_wc_create_notify(local_abspath,
-                                                    svn_wc_notify_add,
-                                                    iterpool);
-                      notify->kind = svn_node_dir;
-                      ctx->notify_func2(ctx->notify_baton2, notify,
-                                        iterpool);
-                    }
-              }
+                  /* Just do notification. */
+                  svn_wc_notify_t *notify;
+                  notify = svn_wc_create_notify(local_abspath,
+                                                svn_wc_notify_add,
+                                                iterpool);
+                  notify->kind = svn_node_dir;
+                  ctx->notify_func2(ctx->notify_baton2, notify,
+                                    iterpool);
+                }
             }
           else
             {
@@ -2936,10 +3005,6 @@ create_missing_parents(patch_target_t *t
                * to version control. Allow cancellation since we
                * have not modified the working copy yet for this
                * target. */
-
-              if (ctx->cancel_func)
-                SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
-
               SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, local_abspath,
                                             NULL /*props*/,
                                             FALSE /* skip checks */,
@@ -2965,7 +3030,8 @@ create_missing_parents(patch_target_t *t
 static svn_error_t *
 install_patched_target(patch_target_t *target, const char *abs_wc_path,
                        svn_client_ctx_t *ctx, svn_boolean_t dry_run,
-                       apr_hash_t *already_added, apr_pool_t *pool)
+                       apr_array_header_t *targets_info,
+                       apr_pool_t *pool)
 {
   if (target->deleted)
     {
@@ -3016,7 +3082,7 @@ install_patched_target(patch_target_t *t
             }
           else
             SVN_ERR(create_missing_parents(target, abs_wc_path, ctx,
-                                           dry_run, already_added, pool));
+                                           dry_run, targets_info, pool));
 
         }
       else
@@ -3415,7 +3481,6 @@ apply_patches(/* The path to the patch f
   apr_pool_t *iterpool;
   svn_patch_file_t *patch_file;
   apr_array_header_t *targets_info;
-  apr_hash_t *already_added = apr_hash_make(scratch_pool);
 
   /* Try to open the patch file. */
   SVN_ERR(svn_diff_open_patch_file(&patch_file, patch_abspath, scratch_pool));
@@ -3441,6 +3506,7 @@ apply_patches(/* The path to the patch f
           SVN_ERR(apply_one_patch(&target, patch, root_abspath,
                                   ctx->wc_ctx, strip_count,
                                   ignore_whitespace, remove_tempfiles,
+                                  targets_info,
                                   patch_func, patch_baton,
                                   ctx->cancel_func, ctx->cancel_baton,
                                   iterpool, iterpool));
@@ -3452,19 +3518,17 @@ apply_patches(/* The path to the patch f
               target_info->local_abspath = apr_pstrdup(scratch_pool,
                                                        target->local_abspath);
               target_info->deleted = target->deleted;
+              target_info->added = target->added;
 
               if (! target->skipped)
                 {
-                  APR_ARRAY_PUSH(targets_info,
-                                 patch_target_info_t *) = target_info;
-
                   if (target->has_text_changes
                       || target->added
                       || target->move_target_abspath
                       || target->deleted)
                     SVN_ERR(install_patched_target(target, root_abspath,
                                                    ctx, dry_run,
-                                                   already_added, iterpool));
+                                                   targets_info, iterpool));
 
                   if (target->has_prop_changes && (!target->deleted))
                     SVN_ERR(install_patched_prop_targets(target, ctx,
@@ -3472,12 +3536,9 @@ apply_patches(/* The path to the patch f
 
                   SVN_ERR(write_out_rejected_hunks(target, dry_run, iterpool));
 
-                  if (target->added)
-                    svn_hash_sets(already_added,
-                                  apr_pstrdup(scratch_pool,
-                                              target->local_abspath),
-                                  "");
-                }
+                  APR_ARRAY_PUSH(targets_info,
+                                 patch_target_info_t *) = target_info;
+              }
               SVN_ERR(send_patch_notification(target, ctx, iterpool));
 
               if (target->deleted && !target->skipped)

Modified: subversion/trunk/subversion/tests/cmdline/patch_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/patch_tests.py?rev=1706217&r1=1706216&r2=1706217&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/patch_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/patch_tests.py Thu Oct  1 11:41:39 2015
@@ -3711,7 +3711,7 @@ def patch_deletes_prop(sbox):
                                        expected_skip,
                                        None, # expected err
                                        1, # check-props
-                                       0) # dry-run
+                                       1) # dry-run
 
   # Revert any local mods, then try to reverse-apply a patch which
   # *adds* the property.
@@ -6730,6 +6730,119 @@ def patch_add_remove_executable(sbox):
                                        [], True, True,
                                        '--reverse-diff')
 
+def patch_git_symlink(sbox):
+  "patch a git symlink"
+
+  # ### Currently we completely ignore the symlink behavior via mode in
+  # ### Subversion but writing this test already found a few bugs in the
+  # ### patch code.
+
+  sbox.build(read_only = True)
+  wc_dir = sbox.wc_dir
+
+  patch_add = [
+    'diff --git a/link-to-iota b/link-to-iota\n',
+    'new file mode 120000\n',
+    'index 0000000..3ef26e4\n',
+    '--- /dev/null\n',
+    '+++ b/link-to-iota\n',
+    '@@ -0,0 +1 @@\n',
+    '+iota\n',
+    '\ No newline at end of file\n',
+  ]
+
+  patch_edit = [
+    'diff --git a/link-to-iota b/link-to-iota\n',
+    'index 3ef26e4..33e5b38 120000\n',
+    '--- a/link-to-iota\n',
+    '+++ b/link-to-iota\n',
+    '@@ -1 +1 @@\n',
+    '-iota\n',
+    '\ No newline at end of file\n',
+    '+A/mu\n',
+    '\ No newline at end of file\n',
+  ]
+
+  patch_to_file = [
+    'diff --git a/link-to-iota b/link-to-iota\n',
+    'deleted file mode 120000\n',
+    'index 33e5b38..0000000\n',
+    '--- a/link-to-iota\n',
+    '+++ /dev/null\n',
+    '@@ -1 +0,0 @@\n',
+    '-A/mu\n',
+    '\ No newline at end of file\n',
+    'diff --git a/link-to-iota b/link-to-iota\n',
+    'new file mode 100644\n',
+    'index 0000000..1b130bf\n',
+    '--- /dev/null\n',
+    '+++ b/link-to-iota\n',
+    '@@ -0,0 +1 @@\n',
+    '+This is a real file\n',
+  ]
+
+  add_patch = sbox.get_tempname('add.patch')
+  svntest.main.file_write(add_patch, ''.join(patch_add), mode='wb')
+
+  edit_patch = sbox.get_tempname('edit.patch')
+  svntest.main.file_write(edit_patch, ''.join(patch_edit), mode='wb')
+
+  to_file_patch = sbox.get_tempname('to_file.patch')
+  svntest.main.file_write(to_file_patch, ''.join(patch_to_file), mode='wb')
+
+
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.add({
+    'link-to-iota'      : Item(status='A ', wc_rev='-'),
+  })
+  expected_output = svntest.wc.State(wc_dir, {
+    'link-to-iota'      : Item(status='A '),
+  })
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.add({
+    'link-to-iota'      : Item(contents="iota"),
+  })
+  expected_skip = svntest.wc.State(wc_dir, {})
+
+  svntest.actions.run_and_verify_patch(wc_dir, add_patch,
+                                       expected_output, expected_disk,
+                                       expected_status, expected_skip,
+                                       [], True, True)
+
+  # And again
+  expected_output.tweak('link-to-iota', status='G ')
+  svntest.actions.run_and_verify_patch(wc_dir, add_patch,
+                                       expected_output, expected_disk,
+                                       expected_status, expected_skip,
+                                       [], True, True)
+
+  # Now tweak the link
+  expected_disk.tweak('link-to-iota', contents='A/mu')
+  svntest.actions.run_and_verify_patch(wc_dir, edit_patch,
+                                       expected_output, expected_disk,
+                                       expected_status, expected_skip,
+                                       [], True, True)
+
+  # And again
+  svntest.actions.run_and_verify_patch(wc_dir, edit_patch,
+                                       expected_output, expected_disk,
+                                       expected_status, expected_skip,
+                                       [], True, True)
+
+  # And replace the link with a file
+  expected_output.tweak('link-to-iota', status='A ', prev_status='D ')
+  expected_disk.tweak('link-to-iota', contents="This is a real file\n")
+  svntest.actions.run_and_verify_patch(wc_dir, to_file_patch,
+                                       expected_output, expected_disk,
+                                       expected_status, expected_skip,
+                                       [], True, True)
+
+  # And again
+  # svntest.actions.run_and_verify_patch(wc_dir, to_file_patch,
+  #                                      expected_output, expected_disk,
+  #                                      expected_status, expected_skip,
+  #                                      [], True, True)
+
 ########################################################################
 #Run the tests
 
@@ -6804,6 +6917,7 @@ test_list = [ None,
               patch_prop_madness,
               patch_empty_vs_delete,
               patch_add_remove_executable,
+              patch_git_symlink,
             ]
 
 if __name__ == '__main__':