You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2010/03/18 12:58:09 UTC

svn commit: r924737 - in /subversion/trunk/subversion: include/svn_client.h libsvn_client/patch.c svn/cl.h svn/main.c svn/patch-cmd.c tests/cmdline/patch_tests.py

Author: stsp
Date: Thu Mar 18 11:58:08 2010
New Revision: 924737

URL: http://svn.apache.org/viewvc?rev=924737&view=rev
Log:
More work on issue #3434, which wasn't really fixed by r919460 alone.

For related discussion, see this and related messages:
  From: Stefan Sperling
  To: Julian Foad
  Cc: dev@subversion.apache.org
  Subject: Re: svn commit: r919460 - filtering svn patch targets
  Message-ID: <20...@jack.stsp.name>
  http://svn.haxx.se/dev/archive-2010-03/0236.shtml
  http://mail-archives.apache.org/mod_mbox/subversion-dev/201003.mbox/%3C20100310104635.GD14558@jack.stsp.name%3E

* subversion/include/svn_client.h
  (svn_client_patch): Add INCLUDE_PATTERNS paramter.

* subversion/libsvn_client/patch.c
  (init_patch_target): Add INCLUDE_PATTERNS parameter. If specified, only
   patch targets matching include patterns. Tweak scope of some variables.
  (apply_one_patch, apply_patches_baton_t, svn_client_patch): Add
   INCLUDE_PATTERNS parameter / member.

* subversion/svn/cl.h
  (svn_cl__opt_state_t): Add INCLUDE_PATTERNS member.

* subversion/svn/main.c
  (svn_cl__longopt_t, svn_cl__options): Add OPT_INCLUDE_PATTERN and
   corresponding --include-pattern option.
  (svn_cl__cmd_table, main): Make 'svn patch' accept --include-pattern.

* subversion/svn/patch-cmd.c
  (svn_cl__patch): Pass include patterns to svn_client_patch().

* subversion/tests/cmdline/patch_tests.py
  (patch_with_include_patterns): New test.
  (patch_with_exclude_patterns): Tweak test description.
  (patch_with_include_exclude_patterns): New test.
  (test_list): Add new tests.

Modified:
    subversion/trunk/subversion/include/svn_client.h
    subversion/trunk/subversion/libsvn_client/patch.c
    subversion/trunk/subversion/svn/cl.h
    subversion/trunk/subversion/svn/main.c
    subversion/trunk/subversion/svn/patch-cmd.c
    subversion/trunk/subversion/tests/cmdline/patch_tests.py

Modified: subversion/trunk/subversion/include/svn_client.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_client.h?rev=924737&r1=924736&r2=924737&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_client.h (original)
+++ subversion/trunk/subversion/include/svn_client.h Thu Mar 18 11:58:08 2010
@@ -4849,10 +4849,17 @@ svn_client_info(const char *path_or_url,
  * original and modified files swapped due to human error.
  *
  * Excluding patch targets from the patching process is possible by passing
- * an @a exclude_patterns array containing elements of type const char *.
+ * @a include_patterns and/or @a exclude_patterns arrays containing
+ * elements of type const char *.
+ * If @a include_patterns is not NULL, patch targets not matching any glob
+ * pattern in @a include_patterns will not be patched.
  * If @a exclude_patterns is not NULL, patch targets matching any glob pattern
- * in @a exclude_patterns will not be patched. The match is performed on the
- * target path as parsed from the patch file, after canonicalization.
+ * in @a exclude_patterns will not be patched
+ * The match is performed on the target path as parsed from the patch file,
+ * after canonicalization.
+ * If both @a include_patterns and @a exclude_patterns are specified,
+ * the @a include_patterns are applied first, i.e. the @a exclude_patterns
+ * are applied to all targets which matched one of the @a include_patterns.
  *
  * If @a ctx->notify_func2 is non-NULL, invoke @a ctx->notify_func2 with
  * @a ctx->notify_baton2 as patching progresses.
@@ -4868,6 +4875,7 @@ svn_client_patch(const char *abs_patch_p
                  svn_boolean_t dry_run,
                  int strip_count,
                  svn_boolean_t reverse,
+                 const apr_array_header_t *include_patterns,
                  const apr_array_header_t *exclude_patterns,
                  svn_client_ctx_t *ctx,
                  apr_pool_t *pool);

Modified: subversion/trunk/subversion/libsvn_client/patch.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/patch.c?rev=924737&r1=924736&r2=924737&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/patch.c (original)
+++ subversion/trunk/subversion/libsvn_client/patch.c Thu Mar 18 11:58:08 2010
@@ -325,6 +325,7 @@ resolve_target_path(patch_target_t *targ
  * which should be stripped from target paths in the patch.
  * Upon success, allocate the patch target structure in RESULT_POOL.
  * Else, set *target to NULL.
+ * If a target does not match a glob in INCLUDE_PATTERNS, mark it as filtered.
  * If a target matches a glob in EXCLUDE_PATTERNS, mark it as filtered.
  * Use SCRATCH_POOL for all other allocations. */
 static svn_error_t *
@@ -332,11 +333,11 @@ init_patch_target(patch_target_t **patch
                   const svn_patch_t *patch,
                   const char *base_dir,
                   svn_wc_context_t *wc_ctx, int strip_count,
+                  const apr_array_header_t *include_patterns,
                   const apr_array_header_t *exclude_patterns,
                   apr_pool_t *result_pool, apr_pool_t *scratch_pool)
 {
   patch_target_t *target;
-  int i;
 
   target = apr_pcalloc(result_pool, sizeof(*target));
 
@@ -345,12 +346,36 @@ init_patch_target(patch_target_t **patch
                               result_pool, scratch_pool));
 
   target->filtered = FALSE;
+  if (include_patterns)
+    {
+      int i;
+      const char *glob;
+      svn_boolean_t match;
+
+      match = FALSE;
+      for (i = 0; i < include_patterns->nelts; i++)
+        {
+          glob = APR_ARRAY_IDX(include_patterns, i, const char *);
+          match = (apr_fnmatch(glob, target->canon_path_from_patchfile,
+                               APR_FNM_CASE_BLIND) == APR_SUCCESS);
+          if (match)
+            break;
+        }
+
+      if (! match)
+        {
+          target->filtered = TRUE;
+          *patch_target = target;
+          return SVN_NO_ERROR;
+        }
+    }
   if (exclude_patterns)
     {
+      int i;
+      const char *glob;
+
       for (i = 0; i < exclude_patterns->nelts; i++)
         {
-          const char *glob;
-
           glob = APR_ARRAY_IDX(exclude_patterns, i, const char *);
           target->filtered = (apr_fnmatch(glob,
                                           target->canon_path_from_patchfile,
@@ -1055,12 +1080,15 @@ send_patch_notification(const patch_targ
  * in RESULT_POOL. Use WC_CTX as the working copy context.
  * STRIP_COUNT specifies the number of leading path components
  * which should be stripped from target paths in the patch.
+ * If a target does not match a glob in INCLUDE_PATTERNS, mark it as filtered.
  * If a target matches a glob in EXCLUDE_PATTERNS, mark it as filtered.
  * Do temporary allocations in SCRATCH_POOL. */
 static svn_error_t *
 apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch,
                 const char *abs_wc_path, svn_wc_context_t *wc_ctx,
-                int strip_count, const apr_array_header_t *exclude_patterns,
+                int strip_count,
+                const apr_array_header_t *include_patterns,
+                const apr_array_header_t *exclude_patterns,
                 apr_pool_t *result_pool, apr_pool_t *scratch_pool)
 {
   patch_target_t *target;
@@ -1069,7 +1097,8 @@ apply_one_patch(patch_target_t **patch_t
   static const int MAX_FUZZ = 2;
 
   SVN_ERR(init_patch_target(&target, patch, abs_wc_path, wc_ctx, strip_count,
-                            exclude_patterns, result_pool, scratch_pool));
+                            include_patterns, exclude_patterns,
+                            result_pool, scratch_pool));
 
   if (target->skipped || target->filtered)
     {
@@ -1378,7 +1407,10 @@ typedef struct {
   /* Whether to apply the patch in reverse. */
   svn_boolean_t reverse;
 
-  /* Glob patterns. Files matching any of these patterns won't be patched. */
+  /* Files not matching any of these patterns won't be patched. */
+  const apr_array_header_t *include_patterns;
+
+  /* Files matching any of these patterns won't be patched. */
   const apr_array_header_t *exclude_patterns;
 
   /* The client context. */
@@ -1433,7 +1465,8 @@ apply_patches(void *baton,
 
           SVN_ERR(apply_one_patch(&target, patch, btn->abs_wc_path,
                                   btn->ctx->wc_ctx, btn->strip_count,
-                                  btn->exclude_patterns, scratch_pool, iterpool));
+                                  btn->include_patterns, btn->exclude_patterns,
+                                  scratch_pool, iterpool));
           if (target->filtered)
             SVN_ERR(svn_diff__close_patch(patch));
           else
@@ -1473,6 +1506,7 @@ svn_client_patch(const char *abs_patch_p
                  svn_boolean_t dry_run,
                  int strip_count,
                  svn_boolean_t reverse,
+                 const apr_array_header_t *include_patterns,
                  const apr_array_header_t *exclude_patterns,
                  svn_client_ctx_t *ctx,
                  apr_pool_t *pool)
@@ -1489,6 +1523,7 @@ svn_client_patch(const char *abs_patch_p
   baton.ctx = ctx;
   baton.strip_count = strip_count;
   baton.reverse = reverse;
+  baton.include_patterns = include_patterns;
   baton.exclude_patterns = exclude_patterns;
 
   SVN_ERR(svn_wc__call_with_write_lock(apply_patches, &baton,

Modified: subversion/trunk/subversion/svn/cl.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/cl.h?rev=924737&r1=924736&r2=924737&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/cl.h (original)
+++ subversion/trunk/subversion/svn/cl.h Thu Mar 18 11:58:08 2010
@@ -224,6 +224,7 @@ typedef struct svn_cl__opt_state_t
   int strip_count; /* number of leading path components to strip */
   svn_boolean_t ignore_keywords;  /* do not expand keywords */
   svn_boolean_t reverse_diff;     /* reverse a diff (e.g. when patching) */
+  apr_array_header_t *include_patterns; /* targets to include in operation */
   apr_array_header_t *exclude_patterns; /* targets to exclude from operation */
 } svn_cl__opt_state_t;
 

Modified: subversion/trunk/subversion/svn/main.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/main.c?rev=924737&r1=924736&r2=924737&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/main.c (original)
+++ subversion/trunk/subversion/svn/main.c Thu Mar 18 11:58:08 2010
@@ -117,6 +117,7 @@ typedef enum {
   opt_show_copies_as_adds,
   opt_ignore_keywords,
   opt_reverse_diff,
+  opt_include_pattern,
   opt_exclude_pattern,
 } svn_cl__longopt_t;
 
@@ -348,6 +349,24 @@ const apr_getopt_option_t svn_cl__option
                     N_("apply the unidiff in reverse\n"
                        "                             "
                        "[alias: --rd]")},
+  {"include-pattern", opt_include_pattern, 1,
+                    N_("operate only on targets matching ARG,\n"
+                       "                             "
+                       "which may be a glob pattern such as '*.txt'.\n"
+                       "                             "
+                       "If this option is specified multiple times,\n"
+                       "                             "
+                       "all patterns are matched in turn.\n"
+                       "                             "
+                       "If both --include-pattern and --exclude-pattern\n"
+                       "                             "
+                       "options are specified include patterns are applied\n"
+                       "                             "
+                       "first, i.e. exclude patterns are applied to all\n"
+                       "                             "
+                       "targets which match an include pattern.\n"
+                       "                             "
+                       "[alias: --ip]")},
   {"exclude-pattern", opt_exclude_pattern, 1,
                     N_("do not operate on targets matching ARG,\n"
                        "                             "
@@ -357,6 +376,8 @@ const apr_getopt_option_t svn_cl__option
                        "                             "
                        "all patterns are matched in turn.\n"
                        "                             "
+                       "See also the --include-pattern option.\n"
+                       "                             "
                        "[alias: --ep]")},
   /* Long-opt Aliases
    *
@@ -382,6 +403,7 @@ const apr_getopt_option_t svn_cl__option
   {"ri",            opt_reintegrate, 0, NULL},
   {"sca",           opt_show_copies_as_adds, 0, NULL},
   {"ik",            opt_ignore_keywords, 0, NULL},
+  {"ip",            opt_include_pattern, 1, NULL},
   {"ep",            opt_exclude_pattern, 1, NULL},
 
   {0,               0, 0, 0},
@@ -824,7 +846,8 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "  for addition. Use 'svn revert' to undo deletions and additions you\n"
      "  do not agree with.\n"
      ),
-    {'q', opt_dry_run, 'p', opt_reverse_diff, opt_exclude_pattern} },
+    {'q', opt_dry_run, 'p', opt_reverse_diff, opt_include_pattern,
+     opt_exclude_pattern} },
 
   { "propdel", svn_cl__propdel, {"pdel", "pd"}, N_
     ("Remove a property from files, dirs, or revisions.\n"
@@ -1739,6 +1762,12 @@ main(int argc, const char *argv[])
       case opt_reverse_diff:
         opt_state.reverse_diff = TRUE;
         break;
+      case opt_include_pattern:
+        if (opt_state.include_patterns == NULL)
+          opt_state.include_patterns = apr_array_make(pool, 1,
+                                                      sizeof (const char *));
+        APR_ARRAY_PUSH(opt_state.include_patterns, const char *) = opt_arg;
+        break;
       case opt_exclude_pattern:
         if (opt_state.exclude_patterns == NULL)
           opt_state.exclude_patterns = apr_array_make(pool, 1,

Modified: subversion/trunk/subversion/svn/patch-cmd.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/patch-cmd.c?rev=924737&r1=924736&r2=924737&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/patch-cmd.c (original)
+++ subversion/trunk/subversion/svn/patch-cmd.c Thu Mar 18 11:58:08 2010
@@ -79,6 +79,7 @@ svn_cl__patch(apr_getopt_t *os,
   SVN_ERR(svn_client_patch(abs_patch_path, abs_target_path,
                            opt_state->dry_run, opt_state->strip_count,
                            opt_state->reverse_diff,
+                           opt_state->include_patterns,
                            opt_state->exclude_patterns, ctx, pool));
 
   if (! opt_state->quiet)

Modified: subversion/trunk/subversion/tests/cmdline/patch_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/patch_tests.py?rev=924737&r1=924736&r2=924737&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/patch_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/patch_tests.py Thu Mar 18 11:58:08 2010
@@ -1677,8 +1677,166 @@ def patch_with_svn_eol_style_uncommitted
       expected_output = ["Reverted '" + mu_path + "'\n"]
       svntest.actions.run_and_verify_svn(None, expected_output, [], 'revert', '-R', wc_dir)
 
+def patch_with_include_patterns(sbox):
+  "patch with include-patterns"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  patch_file_path = tempfile.mkstemp(dir=os.path.abspath(svntest.main.temp_dir))[1]
+  mu_path = os.path.join(wc_dir, 'A', 'mu')
+
+  mu_contents = [
+    "Dear internet user,\n",
+    "\n",
+    "We wish to congratulate you over your email success in our computer\n",
+    "Balloting. This is a Millennium Scientific Electronic Computer Draw\n",
+    "in which email addresses were used. All participants were selected\n",
+    "through a computer ballot system drawn from over 100,000 company\n",
+    "and 50,000,000 individual email addresses from all over the world.\n",
+    "\n",
+    "Your email address drew and have won the sum of  750,000 Euros\n",
+    "( Seven Hundred and Fifty Thousand Euros) in cash credited to\n",
+    "file with\n",
+    "    REFERENCE NUMBER: ESP/WIN/008/05/10/MA;\n",
+    "    WINNING NUMBER : 14-17-24-34-37-45-16\n",
+    "    BATCH NUMBERS :\n",
+    "    EULO/1007/444/606/08;\n",
+    "    SERIAL NUMBER: 45327\n",
+    "and PROMOTION DATE: 13th June. 2009\n",
+    "\n",
+    "To claim your winning prize, you are to contact the appointed\n",
+    "agent below as soon as possible for the immediate release of your\n",
+    "winnings with the below details.\n",
+    "\n",
+    "Again, we wish to congratulate you over your email success in our\n"
+    "computer Balloting.\n"
+  ]
+
+  # Set mu contents
+  svntest.main.file_write(mu_path, ''.join(mu_contents))
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/mu'       : Item(verb='Sending'),
+    })
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.tweak('A/mu', wc_rev=2)
+  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
+                                        expected_status, None, wc_dir)
+
+  # Apply patch
+
+  unidiff_patch = [
+    "Index: A/D/gamma\n",
+    "===================================================================\n",
+    "--- A/D/gamma\t(revision 1)\n",
+    "+++ A/D/gamma\t(working copy)\n",
+    "@@ -1 +1 @@\n",
+    "-This is the file 'gamma'.\n",
+    "+It is the file 'gamma'.\n",
+    "Index: iota\n",
+    "===================================================================\n",
+    "--- iota\t(revision 1)\n",
+    "+++ iota\t(working copy)\n",
+    "@@ -1 +1,2 @@\n",
+    " This is the file 'iota'.\n",
+    "+Some more bytes\n",
+    "\n",
+    "Index: new\n",
+    "===================================================================\n",
+    "--- new	(revision 0)\n",
+    "+++ new	(revision 0)\n",
+    "@@ -0,0 +1 @@\n",
+    "+new\n",
+    "\n",
+    "--- A/mu.orig	2009-06-24 15:23:55.000000000 +0100\n",
+    "+++ A/mu	2009-06-24 15:21:23.000000000 +0100\n",
+    "@@ -6,6 +6,9 @@\n",
+    " through a computer ballot system drawn from over 100,000 company\n",
+    " and 50,000,000 individual email addresses from all over the world.\n",
+    " \n",
+    "+It is a promotional program aimed at encouraging internet users;\n",
+    "+therefore you do not need to buy ticket to enter for it.\n",
+    "+\n",
+    " Your email address drew and have won the sum of  750,000 Euros\n",
+    " ( Seven Hundred and Fifty Thousand Euros) in cash credited to\n",
+    " file with\n",
+    "@@ -14,11 +17,8 @@\n",
+    "     BATCH NUMBERS :\n",
+    "     EULO/1007/444/606/08;\n",
+    "     SERIAL NUMBER: 45327\n",
+    "-and PROMOTION DATE: 13th June. 2009\n",
+    "+and PROMOTION DATE: 14th June. 2009\n",
+    " \n",
+    " To claim your winning prize, you are to contact the appointed\n",
+    " agent below as soon as possible for the immediate release of your\n",
+    " winnings with the below details.\n",
+    "-\n",
+    "-Again, we wish to congratulate you over your email success in our\n",
+    "-computer Balloting.\n",
+    "Index: A/B/E/beta\n",
+    "===================================================================\n",
+    "--- A/B/E/beta	(revision 1)\n",
+    "+++ A/B/E/beta	(working copy)\n",
+    "@@ -1 +0,0 @@\n",
+    "-This is the file 'beta'.\n",
+  ]
+
+  svntest.main.file_write(patch_file_path, ''.join(unidiff_patch))
+
+  gamma_contents = "It is the file 'gamma'.\n"
+  iota_contents = "This is the file 'iota'.\nSome more bytes\n"
+  new_contents = "new\n"
+  mu_contents = [
+    "Dear internet user,\n",
+    "\n",
+    "We wish to congratulate you over your email success in our computer\n",
+    "Balloting. This is a Millennium Scientific Electronic Computer Draw\n",
+    "in which email addresses were used. All participants were selected\n",
+    "through a computer ballot system drawn from over 100,000 company\n",
+    "and 50,000,000 individual email addresses from all over the world.\n",
+    "\n",
+    "It is a promotional program aimed at encouraging internet users;\n",
+    "therefore you do not need to buy ticket to enter for it.\n",
+    "\n",
+    "Your email address drew and have won the sum of  750,000 Euros\n",
+    "( Seven Hundred and Fifty Thousand Euros) in cash credited to\n",
+    "file with\n",
+    "    REFERENCE NUMBER: ESP/WIN/008/05/10/MA;\n",
+    "    WINNING NUMBER : 14-17-24-34-37-45-16\n",
+    "    BATCH NUMBERS :\n",
+    "    EULO/1007/444/606/08;\n",
+    "    SERIAL NUMBER: 45327\n",
+    "and PROMOTION DATE: 14th June. 2009\n",
+    "\n",
+    "To claim your winning prize, you are to contact the appointed\n",
+    "agent below as soon as possible for the immediate release of your\n",
+    "winnings with the below details.\n",
+  ]
+
+  expected_output = [
+    'U         %s\n' % os.path.join(wc_dir, 'A', 'mu'),
+  ]
+
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.tweak('A/mu', contents=''.join(mu_contents))
+
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.tweak('A/mu', status='M ', wc_rev=2)
+
+  expected_skip = wc.State('', { })
+
+  svntest.actions.run_and_verify_patch(wc_dir, os.path.abspath(patch_file_path),
+                                       expected_output,
+                                       expected_disk,
+                                       expected_status,
+                                       expected_skip,
+                                       None, # expected err
+                                       1, # check-props
+                                       1, # dry-run
+                                       "--include-pattern", "A/mu")
+
 def patch_with_exclude_patterns(sbox):
-  "patch with --exclude-patterns"
+  "patch with exclude-patterns"
 
   sbox.build()
   wc_dir = sbox.wc_dir
@@ -1837,6 +1995,172 @@ def patch_with_exclude_patterns(sbox):
                                        "--exclude-pattern", "new",
                                        "--exclude-pattern", "*a")
 
+def patch_with_include_exclude_patterns(sbox):
+  "patch with include-patterns and exclude-patterns"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  patch_file_path = tempfile.mkstemp(dir=os.path.abspath(svntest.main.temp_dir))[1]
+  mu_path = os.path.join(wc_dir, 'A', 'mu')
+
+  mu_contents = [
+    "Dear internet user,\n",
+    "\n",
+    "We wish to congratulate you over your email success in our computer\n",
+    "Balloting. This is a Millennium Scientific Electronic Computer Draw\n",
+    "in which email addresses were used. All participants were selected\n",
+    "through a computer ballot system drawn from over 100,000 company\n",
+    "and 50,000,000 individual email addresses from all over the world.\n",
+    "\n",
+    "Your email address drew and have won the sum of  750,000 Euros\n",
+    "( Seven Hundred and Fifty Thousand Euros) in cash credited to\n",
+    "file with\n",
+    "    REFERENCE NUMBER: ESP/WIN/008/05/10/MA;\n",
+    "    WINNING NUMBER : 14-17-24-34-37-45-16\n",
+    "    BATCH NUMBERS :\n",
+    "    EULO/1007/444/606/08;\n",
+    "    SERIAL NUMBER: 45327\n",
+    "and PROMOTION DATE: 13th June. 2009\n",
+    "\n",
+    "To claim your winning prize, you are to contact the appointed\n",
+    "agent below as soon as possible for the immediate release of your\n",
+    "winnings with the below details.\n",
+    "\n",
+    "Again, we wish to congratulate you over your email success in our\n"
+    "computer Balloting.\n"
+  ]
+
+  # Set mu contents
+  svntest.main.file_write(mu_path, ''.join(mu_contents))
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/mu'       : Item(verb='Sending'),
+    })
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.tweak('A/mu', wc_rev=2)
+  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
+                                        expected_status, None, wc_dir)
+
+  # Apply patch
+
+  unidiff_patch = [
+    "Index: A/D/gamma\n",
+    "===================================================================\n",
+    "--- A/D/gamma\t(revision 1)\n",
+    "+++ A/D/gamma\t(working copy)\n",
+    "@@ -1 +1 @@\n",
+    "-This is the file 'gamma'.\n",
+    "+It is the file 'gamma'.\n",
+    "Index: iota\n",
+    "===================================================================\n",
+    "--- iota\t(revision 1)\n",
+    "+++ iota\t(working copy)\n",
+    "@@ -1 +1,2 @@\n",
+    " This is the file 'iota'.\n",
+    "+Some more bytes\n",
+    "\n",
+    "Index: new\n",
+    "===================================================================\n",
+    "--- new	(revision 0)\n",
+    "+++ new	(revision 0)\n",
+    "@@ -0,0 +1 @@\n",
+    "+new\n",
+    "\n",
+    "--- A/mu.orig	2009-06-24 15:23:55.000000000 +0100\n",
+    "+++ A/mu	2009-06-24 15:21:23.000000000 +0100\n",
+    "@@ -6,6 +6,9 @@\n",
+    " through a computer ballot system drawn from over 100,000 company\n",
+    " and 50,000,000 individual email addresses from all over the world.\n",
+    " \n",
+    "+It is a promotional program aimed at encouraging internet users;\n",
+    "+therefore you do not need to buy ticket to enter for it.\n",
+    "+\n",
+    " Your email address drew and have won the sum of  750,000 Euros\n",
+    " ( Seven Hundred and Fifty Thousand Euros) in cash credited to\n",
+    " file with\n",
+    "@@ -14,11 +17,8 @@\n",
+    "     BATCH NUMBERS :\n",
+    "     EULO/1007/444/606/08;\n",
+    "     SERIAL NUMBER: 45327\n",
+    "-and PROMOTION DATE: 13th June. 2009\n",
+    "+and PROMOTION DATE: 14th June. 2009\n",
+    " \n",
+    " To claim your winning prize, you are to contact the appointed\n",
+    " agent below as soon as possible for the immediate release of your\n",
+    " winnings with the below details.\n",
+    "-\n",
+    "-Again, we wish to congratulate you over your email success in our\n",
+    "-computer Balloting.\n",
+    "Index: A/B/E/beta\n",
+    "===================================================================\n",
+    "--- A/B/E/beta	(revision 1)\n",
+    "+++ A/B/E/beta	(working copy)\n",
+    "@@ -1 +0,0 @@\n",
+    "-This is the file 'beta'.\n",
+  ]
+
+  svntest.main.file_write(patch_file_path, ''.join(unidiff_patch))
+
+  gamma_contents = "It is the file 'gamma'.\n"
+  iota_contents = "This is the file 'iota'.\nSome more bytes\n"
+  new_contents = "new\n"
+  mu_contents = [
+    "Dear internet user,\n",
+    "\n",
+    "We wish to congratulate you over your email success in our computer\n",
+    "Balloting. This is a Millennium Scientific Electronic Computer Draw\n",
+    "in which email addresses were used. All participants were selected\n",
+    "through a computer ballot system drawn from over 100,000 company\n",
+    "and 50,000,000 individual email addresses from all over the world.\n",
+    "\n",
+    "It is a promotional program aimed at encouraging internet users;\n",
+    "therefore you do not need to buy ticket to enter for it.\n",
+    "\n",
+    "Your email address drew and have won the sum of  750,000 Euros\n",
+    "( Seven Hundred and Fifty Thousand Euros) in cash credited to\n",
+    "file with\n",
+    "    REFERENCE NUMBER: ESP/WIN/008/05/10/MA;\n",
+    "    WINNING NUMBER : 14-17-24-34-37-45-16\n",
+    "    BATCH NUMBERS :\n",
+    "    EULO/1007/444/606/08;\n",
+    "    SERIAL NUMBER: 45327\n",
+    "and PROMOTION DATE: 14th June. 2009\n",
+    "\n",
+    "To claim your winning prize, you are to contact the appointed\n",
+    "agent below as soon as possible for the immediate release of your\n",
+    "winnings with the below details.\n",
+  ]
+
+  expected_output = [
+    'U         %s\n' % os.path.join(wc_dir, 'iota'),
+    'U         %s\n' % os.path.join(wc_dir, 'A', 'mu'),
+    'D         %s\n' % os.path.join(wc_dir, 'A', 'B', 'E', 'beta'),
+  ]
+
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.tweak('A/mu', contents=''.join(mu_contents))
+  expected_disk.tweak('iota', contents=''.join(iota_contents))
+  expected_disk.remove('A/B/E/beta')
+
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.tweak('A/mu', status='M ', wc_rev=2)
+  expected_status.tweak('iota', status='M ')
+  expected_status.tweak('A/B/E/beta', status='D ')
+
+  expected_skip = wc.State('', { })
+
+  svntest.actions.run_and_verify_patch(wc_dir, os.path.abspath(patch_file_path),
+                                       expected_output,
+                                       expected_disk,
+                                       expected_status,
+                                       expected_skip,
+                                       None, # expected err
+                                       1, # check-props
+                                       1, # dry-run
+                                       "--include-pattern", "A/mu",
+                                       "--include-pattern", "*a",
+                                       "--exclude-pattern", "A/*/gamma")
+
 ########################################################################
 #Run the tests
 
@@ -1855,7 +2179,9 @@ test_list = [ None,
               patch_no_svn_eol_style,
               patch_with_svn_eol_style,
               patch_with_svn_eol_style_uncommitted,
+              patch_with_include_patterns,
               patch_with_exclude_patterns,
+              patch_with_include_exclude_patterns,
             ]
 
 if __name__ == '__main__':