Reintegrate the 1.7.x-issue3976 branch to 1.7.x:

 * r1161683, r1161721
   Fix issue #3976 "record-only merges which add new subtree mergeinfo don't
   record mergeinfo describing merge".
   Justification:
      Avoid repeat merges and possible spurious conflicts in merges subsequent
      to a --record-only merge which adds mergeinfo to a subtree which had no
      explicit mergeinfo prior to the merge.  Ok, that's a mouthful, to put it
      more succinctly: A --record-only merge of revision X should block future
      merges of revision X, but that wasn't happening here.
   Notes:
     r1161683 is a new test for this issue and r1161721 is the fix.
   Branch:
     ^/subversion/branches/1.7.x-issue3976
   Votes:
     +1: pburba, stsp, philip


git-svn-id: https://svn.apache.org/repos/asf/subversion/branches/1.7.x@1165398 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/CHANGES b/CHANGES
index b8df254..5012b4d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -41,6 +41,7 @@
     * make server-side network data compression rate configurable (r1072288)
     * added support for auto-detecting mime-types with libmagic (r1131120)
     * 'svn rm url1 url2 url3' uses single txn per repo (issue #1199)
+    * don't leave unversioned files when reverting copies (issue #3101)
 
   - Client-side bugfixes:
     * 'svn cp A B; svn mv B C' is equivalent to 'svn cp A C' (issue #756)
@@ -82,6 +83,9 @@
     * 'svn delete --force' removes tree conflicts (issue #3805)
     * don't throw an error when skipping tree conflicts in update (issue #3329)
     * don't break commits of wc->wc copies with file externals (issue #3589)
+    * allow 'svn info' to work on symlinks to working copies (issue #2305)
+    * allow 'svn st --show-updates' to work across symlinks (issue #3117)
+    * 'svn revert' shouldn't loop on symlinks (issue #3972)
     * fixed: wc-to-wc copy of a switch source (issue #1802)
     * fixed: 'svn st' reports symlinks as obstructed items (issue #2284)
     * fixed: 'cd e:\; svn up e:\' fails (issue #2556)
@@ -123,6 +127,9 @@
     * fixed: spurious prop conflict with 'merge --reintegrate' (issue #3919)
     * fixed: 'svn --version' fails with non-existant $HOME (issue #3947)
     * fixed: unforced export silently overwites existing file (issue #3799)
+    * fixed: reverse merge which adds subtree mergeinfo fails (issue #3978)
+    * fixed: 'svn up -r{R>HEAD}' hangs client over ra_svn (issue #3963)
+    * fixed: 'svn up' updates file externals in target siblings (issue #3819)
     * many other minor bugfixes, optimizations, plugs of memory leaks, etc
   
   - Server-side bugfixes:
@@ -143,6 +150,7 @@
     * svnserve can now force usernames to upper/lower case (issue #3726)
     * reduce duplicate log messages in 'log -g' (issue #3650)
     * don't crash on shutdown with SASL in inetd mode (issue #3664)
+    * disallow arbitrary HTTP headers from committers (issue #2872)
     * many other minor bugfixes too numerous to list here
 
   - Other tool improvements and bugfixes:
@@ -172,7 +180,9 @@
     * notifications sent when mergeinfo changes (r877588)
     * add information on text and property mods in log APIs (r877688)
     * fixed: svn_ra_local__get_file() leaks file descriptors (issue #3290)
+    * svn_ra_neon__get_dir() returns correct dir set for URLs (issue #3093)
     * swig-py: always set ChangedPath.path (issue #2630)
+    * improve conflict resolver API for a specific direction (issue #3049)
 
   - Bindings:
     * New JavaHL package: org.apache.subversion
diff --git a/STATUS b/STATUS
index 390046c..a35f282 100644
--- a/STATUS
+++ b/STATUS
@@ -40,6 +40,7 @@
    Votes:
      +1: arfrever
      -0: stsp (we can backport these when we need to)
+     -0: gstein
 
  * r1154278, r1154379, r1154382
    Make 'svn ls' capable of listing Subversion 1.0-1.3 repositories using
@@ -58,12 +59,92 @@
    Votes:
      +1: rhuijben, cmpilato
 
- * r1159400
-   Document a neat trick to better handle patch rejects in 'svn help patch'.
+ * r1147299, r1162974, r1162995
+   Fix issue #3867 'reintegrate merges create mergeinfo for non-existent
+   paths' and issue #3961 'reintegrate merge of replaced source produces
+   inaccurate mergeinfo'.
    Justification:
-     Might save a poor soul from lots of manual copy-paste/editing some day.
+      Avoid potential confusion which can occur when a reintegrate merge
+      sets mergeinfo (to describe the merge) which contains non-existent
+      path-revs, other lines of history, and/or valid path-revs which were
+      never merged.
+   Notes:
+     r1147299 and r1162995 is the addition and tweak of a test for
+     issue #3961.  A test already existed for issue #3867 prior to branching
+     1.7.x. r1162974 is the fix for both issues.
    Votes:
-     +1: stsp, rhuijben
+     +1: pburba, stsp
+
+ * r1163243, r1163383
+   Fix issue #3791 'svn mergeinfo shows natural history of added subtrees
+   as eligible'.
+   Justification:
+     The 'svn mergeinfo [ -R | --depth [ files | immediates | infinity ] ]'
+     subcommand gives incorrect answers in some use cases.
+   Notes:
+     r1163243 is a test for the issue, r1163383 is the fix.
+   Votes:
+     +1: pburba
+
+ * r1163557
+   Relax wcng consistency checks for file externals.
+   Justification:
+      Debug build claims wcng DB is inconsistent when it is OK.
+   Branch:
+     1.7.x-r1163557
+   Votes:
+     +1: philip
+     
+ * r1164386
+   Avoid an expensive database operation in 'svn info'.
+   Justification:
+     Improves performance of 'svn info' on a single unmodified node by 20-30%, by
+     moving an in most cases unneeded database call in the error handling.
+   Votes:
+     +1: rhuijben
+     +1: gstein (for 1.7.1)
+
+ * r1164426,r1164614
+   Fix STMT_SELECT_NODE_CHILDREN_WALKER_INFO for correctness and speed.
+   Justification:
+     Recursive info and propset don't scale well with large working copies
+     and may possibly act on the wrong nodes.
+     On a Linux machine with a Subversion trunk working copy on a local disk
+     running 'svn info --depth infinity' shows it to be 2 or 3 times faster.
+     On a larger working copy it's orders of magnitude faster.
+   Votes:
+     +1: philip
+     +0: peters, gstein (looks correct, but I didn't test, and while
+                         performance should be better, I wonder if that's
+                         been tested at all)
+
+ * r1164645
+   Fix issue #3999 'svn merge with bad option or target segfaults'.
+   Justification:
+      Command line merge with too many targets and/or a bad option
+      (no leading '-') results in a segfault without this fix.
+   Votes:
+     +1: pburba, philip
+
+ * branches/1.7.x-issue4000
+   Fix issue #4000 'WC-WC-copy of file external creates first-class file node'
+   and don't break file external state when committing a file external.
+   Justification:
+     Inconsistency. Can create collisions between externals definitions and
+     versioned nodes.
+     r1164027 is included since it inadvertently fixes the re-appearance of
+     issue #4000 on a file external that has been committed.
+   Notes:
+     Since r1164027 prevents the NODES table's FILE_EXTERNAL column from
+     being blanked out during commit bumping, it probably fixes a number of
+     other problems with committed file externals that I'm not aware of ATM.
+   Conflicts:
+     C  externals_tests.py
+     branches/1.7.x-issue4000@    1164785 1164786 1164792
+     are clean versions of trunk@ 1164027 1164760 1164765
+     and are the:    corner case fix^  main fix^    ^test
+   Votes:
+     +1: neels
 
 Veto-blocked changes:
 =====================
diff --git a/subversion/libsvn_client/merge.c b/subversion/libsvn_client/merge.c
index efc5f42..513cbf9 100644
--- a/subversion/libsvn_client/merge.c
+++ b/subversion/libsvn_client/merge.c
@@ -7705,6 +7705,7 @@
               && (!merge_b->record_only || merge_b->reintegrate_merge)
               && (!is_rollback))
             {
+              svn_error_t *err;
               svn_opt_revision_t peg_revision;
               svn_mergeinfo_t subtree_history_as_mergeinfo;
               apr_array_header_t *child_merge_src_rangelist;
@@ -7727,26 +7728,40 @@
                                                         merge_b->ra_session2,
                                                         subtree_mergeinfo_url,
                                                         iterpool));
-              SVN_ERR(svn_client__get_history_as_mergeinfo(
+              err = svn_client__get_history_as_mergeinfo(
                 &subtree_history_as_mergeinfo, NULL,
                 subtree_mergeinfo_url, &peg_revision,
                 MAX(merged_range->start, merged_range->end),
                 MIN(merged_range->start, merged_range->end),
-                merge_b->ra_session2, merge_b->ctx, iterpool));
+                merge_b->ra_session2, merge_b->ctx, iterpool);
+
+              /* If CHILD is a subtree it may have been deleted prior to
+                 MERGED_RANGE->END so the above call to get its history
+                 will fail. */
+              if (err)
+                {
+                  if (err->apr_err != SVN_ERR_FS_NOT_FOUND)
+                      return svn_error_trace(err);
+                  svn_error_clear(err);
+                }
+              else
+                {
+                  child_merge_src_rangelist = apr_hash_get(
+                    subtree_history_as_mergeinfo,
+                    child_merge_src_canon_path,
+                    APR_HASH_KEY_STRING);
+                  SVN_ERR(svn_rangelist_intersect(&child_merge_rangelist,
+                                                  child_merge_rangelist,
+                                                  child_merge_src_rangelist,
+                                                  FALSE, iterpool));
+                  if (!rangelist_inheritance)
+                    svn_rangelist__set_inheritance(child_merge_rangelist,
+                                                   FALSE);
+                }
 
               if (old_session_url)
                 SVN_ERR(svn_ra_reparent(merge_b->ra_session2,
                                         old_session_url, iterpool));
-              child_merge_src_rangelist = apr_hash_get(
-                subtree_history_as_mergeinfo,
-                child_merge_src_canon_path,
-                APR_HASH_KEY_STRING);
-              SVN_ERR(svn_rangelist_intersect(&child_merge_rangelist,
-                                              child_merge_rangelist,
-                                              child_merge_src_rangelist,
-                                              FALSE, iterpool));
-              if (!rangelist_inheritance)
-                svn_rangelist__set_inheritance(child_merge_rangelist, FALSE);
             }
 
           apr_hash_set(child_merges, child->abspath, APR_HASH_KEY_STRING,
diff --git a/subversion/libsvn_client/patch.c b/subversion/libsvn_client/patch.c
index 989caa6..445952d 100644
--- a/subversion/libsvn_client/patch.c
+++ b/subversion/libsvn_client/patch.c
@@ -2471,8 +2471,15 @@
           target->added = TRUE;
         }
 
-      /* Attempt to set the property, and reject all hunks if this fails. */
-      prop_val = svn_stringbuf__morph_into_string(prop_target->patched_value);
+      /* Attempt to set the property, and reject all hunks if this
+         fails.  If the property had a non-empty value, but now has
+         an empty one, we'll just delete the property altogether.  */
+      if (prop_target->value && prop_target->value->len
+          && prop_target->patched_value && !prop_target->patched_value->len)
+        prop_val = NULL;
+      else
+        prop_val = svn_stringbuf__morph_into_string(prop_target->patched_value);
+
       if (dry_run)
         {
           const svn_string_t *canon_propval;
@@ -2891,7 +2898,7 @@
                                                    btn->ctx, btn->dry_run,
                                                    iterpool));
 
-                  if (target->has_prop_changes)
+                  if (target->has_prop_changes && (!target->deleted))
                     SVN_ERR(install_patched_prop_targets(target, btn->ctx,
                                                          btn->dry_run,
                                                          iterpool));
diff --git a/subversion/libsvn_ra_serf/update.c b/subversion/libsvn_ra_serf/update.c
index 07c33f1..da1c71b 100644
--- a/subversion/libsvn_ra_serf/update.c
+++ b/subversion/libsvn_ra_serf/update.c
@@ -1604,9 +1604,11 @@
 
       SVN_ERR(open_dir(info->dir));
 
-      SVN_ERR(ctx->update_editor->absent_directory(file_name,
-                                                   info->dir->dir_baton,
-                                                   info->dir->pool));
+      SVN_ERR(ctx->update_editor->absent_directory(
+                                        svn_relpath_join(info->name, file_name,
+                                                         info->dir->pool),
+                                        info->dir->dir_baton,
+                                        info->dir->pool));
     }
   else if ((state == OPEN_DIR || state == ADD_DIR) &&
            strcmp(name.name, "absent-file") == 0)
@@ -1627,9 +1629,11 @@
 
       SVN_ERR(open_dir(info->dir));
 
-      SVN_ERR(ctx->update_editor->absent_file(file_name,
-                                              info->dir->dir_baton,
-                                              info->dir->pool));
+      SVN_ERR(ctx->update_editor->absent_file(
+                                        svn_relpath_join(info->name, file_name,
+                                                         info->dir->pool),
+                                        info->dir->dir_baton,
+                                        info->dir->pool));
     }
   else if (state == OPEN_DIR || state == ADD_DIR)
     {
diff --git a/subversion/libsvn_repos/dump.c b/subversion/libsvn_repos/dump.c
index f8b07ad..ca23809 100644
--- a/subversion/libsvn_repos/dump.c
+++ b/subversion/libsvn_repos/dump.c
@@ -385,7 +385,7 @@
                      pool,
                      _("Referencing data in revision %ld,"
                        " which is older than the oldest"
-                       " dumped revision (%ld).  Loading this dump"
+                       " dumped revision (r%ld).  Loading this dump"
                        " into an empty repository"
                        " will fail."),
                      cmp_rev, eb->oldest_dumped_rev);
@@ -489,7 +489,7 @@
                   notify->warning_str = apr_psprintf(
                     pool,
                     _("Mergeinfo referencing revision(s) prior "
-                      "to the oldest dumped revision (%ld). "
+                      "to the oldest dumped revision (r%ld). "
                       "Loading this dump may result in invalid "
                       "mergeinfo."),
                     eb->oldest_dumped_rev);
diff --git a/subversion/libsvn_wc/entries.c b/subversion/libsvn_wc/entries.c
index 1d842b0..9ef1ec1 100644
--- a/subversion/libsvn_wc/entries.c
+++ b/subversion/libsvn_wc/entries.c
@@ -892,12 +892,15 @@
           switch (cd->kind)
             {
             case svn_wc_conflict_kind_text:
-              entry->conflict_old = svn_dirent_basename(cd->base_abspath,
-                                                        result_pool);
-              entry->conflict_new = svn_dirent_basename(cd->their_abspath,
-                                                        result_pool);
-              entry->conflict_wrk = svn_dirent_basename(cd->my_abspath,
-                                                        result_pool);
+              if (cd->base_abspath)
+                entry->conflict_old = svn_dirent_basename(cd->base_abspath,
+                                                          result_pool);
+              if (cd->their_abspath)
+                entry->conflict_new = svn_dirent_basename(cd->their_abspath,
+                                                          result_pool);
+              if (cd->my_abspath)
+                entry->conflict_wrk = svn_dirent_basename(cd->my_abspath,
+                                                          result_pool);
               break;
             case svn_wc_conflict_kind_property:
               entry->prejfile = svn_dirent_basename(cd->their_abspath,
diff --git a/subversion/libsvn_wc/merge.c b/subversion/libsvn_wc/merge.c
index 17fb554..3f48896 100644
--- a/subversion/libsvn_wc/merge.c
+++ b/subversion/libsvn_wc/merge.c
@@ -1037,7 +1037,7 @@
         }
 
       if (*merge_outcome == svn_wc_merge_merged)
-        return SVN_NO_ERROR;
+        goto done;
     }
   else if (contains_conflicts && dry_run)
       *merge_outcome = svn_wc_merge_conflict;
@@ -1074,6 +1074,7 @@
       *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
     }
 
+done:
   /* Remove the tempfile after use */
   SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
                                        mt->db, result_target,
diff --git a/subversion/svn/info-cmd.c b/subversion/svn/info-cmd.c
index 263e09d..14d0db6 100644
--- a/subversion/svn/info-cmd.c
+++ b/subversion/svn/info-cmd.c
@@ -417,20 +417,26 @@
               switch (conflict->kind)
                 {
                   case svn_wc_conflict_kind_text:
-                    SVN_ERR(svn_cmdline_printf(pool,
-                              _("Conflict Previous Base File: %s\n"),
-                              svn_dirent_local_style(conflict->base_abspath,
-                                                     pool)));
+                    if (conflict->base_abspath)
+                      SVN_ERR(svn_cmdline_printf(pool,
+                                _("Conflict Previous Base File: %s\n"),
+                                svn_cl__local_style_skip_ancestor(
+                                        path_prefix, conflict->base_abspath,
+                                        pool)));
 
-                    SVN_ERR(svn_cmdline_printf(pool,
-                              _("Conflict Previous Working File: %s\n"),
-                              svn_dirent_local_style(conflict->my_abspath,
-                                                     pool)));
+                    if (conflict->my_abspath)
+                      SVN_ERR(svn_cmdline_printf(pool,
+                                _("Conflict Previous Working File: %s\n"),
+                                svn_cl__local_style_skip_ancestor(
+                                        path_prefix, conflict->my_abspath,
+                                        pool)));
 
-                    SVN_ERR(svn_cmdline_printf(pool,
-                              _("Conflict Current Base File: %s\n"),
-                              svn_dirent_local_style(conflict->their_abspath,
-                                                     pool)));
+                    if (conflict->their_abspath)
+                      SVN_ERR(svn_cmdline_printf(pool,
+                                _("Conflict Current Base File: %s\n"),
+                                svn_cl__local_style_skip_ancestor(
+                                        path_prefix, conflict->their_abspath,
+                                        pool)));
                   break;
 
                   case svn_wc_conflict_kind_property:
diff --git a/subversion/svn/main.c b/subversion/svn/main.c
index f1cc45a..c17240a 100644
--- a/subversion/svn/main.c
+++ b/subversion/svn/main.c
@@ -1006,6 +1006,13 @@
      "  for deletion. If the patch creates a new file, that file is scheduled\n"
      "  for addition. Use 'svn revert' to undo deletions and additions you\n"
      "  do not agree with.\n"
+     "\n"
+     "  Hint: If the patch file was created with Subversion, it will contain\n"
+     "        the number of a revision N the patch will cleanly apply to\n"
+     "        (look for lines like \"--- foo/bar.txt        (revision N)\").\n"
+     "        To avoid rejects, first update to the revision N using \n"
+     "        'svn update -r N', apply the patch, and then update back to the\n"
+     "        HEAD revision. This way, conflicts can be resolved interactively.\n"
      ),
     {'q', opt_dry_run, opt_strip, opt_reverse_diff,
      opt_ignore_whitespace} },
@@ -1384,7 +1391,7 @@
 
   { "upgrade", svn_cl__upgrade, {0}, N_
     ("Upgrade the metadata storage format for a working copy.\n"
-     "usage: upgrade WCPATH...\n"
+     "usage: upgrade [WCPATH...]\n"
      "\n"
      "  Local modifications are preserved.\n"),
     { 'q' } },
diff --git a/subversion/tests/cmdline/info_tests.py b/subversion/tests/cmdline/info_tests.py
index 86a56bb..aa1c924 100755
--- a/subversion/tests/cmdline/info_tests.py
+++ b/subversion/tests/cmdline/info_tests.py
@@ -482,6 +482,42 @@
        'svn: E200009: Could not display info for all targets.*',
         'info', iota)
 
+@Issue(3998)
+def binary_tree_conflict(sbox):
+  "svn info shouldn't crash on conflict"
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  sbox.simple_propset('svn:mime-type', 'binary/octet-stream', 'iota')
+  sbox.simple_commit()
+
+  iota = sbox.ospath('iota')
+
+  svntest.main.file_write(iota, 'something-else')
+  sbox.simple_commit()
+
+  svntest.main.file_write(iota, 'third')
+
+  expected_output = svntest.wc.State(wc_dir, {
+    'iota' : Item(status='C '),
+    })
+  expected_status = svntest.wc.State(iota, {
+    '' : Item(status='C ', wc_rev='2')
+  })
+  svntest.actions.run_and_verify_update(iota,
+                                        expected_output, None, expected_status,
+                                        None, None, None, None, None, False,
+                                        iota, '-r', '2')
+
+  expected_info = [{
+      'Path' : '%s' % re.escape(iota),
+      'Conflict Previous Base File' : re.escape(iota + '.r3'),
+      'Conflict Current Base File' : re.escape(iota + '.r2'),
+  }]
+  svntest.actions.run_and_verify_info(expected_info, iota)
+
+
+
 
 ########################################################################
 # Run the tests
@@ -496,6 +532,7 @@
               info_multiple_targets,
               info_repos_root_url,
               info_show_exclude,
+              binary_tree_conflict,
              ]
 
 if __name__ == '__main__':
diff --git a/subversion/tests/cmdline/patch_tests.py b/subversion/tests/cmdline/patch_tests.py
index 2c9a588..29a4583 100755
--- a/subversion/tests/cmdline/patch_tests.py
+++ b/subversion/tests/cmdline/patch_tests.py
@@ -3485,6 +3485,193 @@
                                        1) # dry-run
 
 
+@Issue(4003)
+def patch_deletes_prop(sbox):
+  "patch deletes prop, directly and via reversed add"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  patch_file_path = make_patch_path(sbox)
+  iota_path = os.path.join(wc_dir, 'iota')
+
+  svntest.main.run_svn(None, 'propset', 'propname', 'propvalue',
+                       iota_path)
+  expected_output = svntest.wc.State(wc_dir, {
+    'iota' : Item(verb='Sending'),
+    })
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.tweak('iota', wc_rev=2)
+  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
+                                        expected_status, None, wc_dir)
+
+  # Apply patch
+  unidiff_patch = [
+    "Index: iota\n",
+    "===================================================================\n",
+    "--- iota\t(revision 1)\n",
+    "+++ iota\t(working copy)\n",
+    "\n",
+    "Property changes on: iota\n",
+    "___________________________________________________________________\n",
+    "Deleted: propname\n",
+    "## -1 +0,0 ##\n",
+    "-propvalue\n",
+    ]
+  svntest.main.file_write(patch_file_path, ''.join(unidiff_patch))
+
+  # Expect the original state of the working copy in r1, exception
+  # that iota is at r2 now.
+  expected_disk = svntest.main.greek_state.copy()
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.tweak('iota', status=' M')
+  expected_status.tweak('iota', wc_rev=2)
+  expected_skip = wc.State('', { })
+  expected_output = [
+    ' U        %s\n' % os.path.join(wc_dir, 'iota'),
+  ]
+  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
+                                       0) # dry-run
+
+  # Revert any local mods, then try to reverse-apply a patch which
+  # *adds* the property.
+  svntest.main.run_svn(None, 'revert', iota_path)
+
+  # Apply patch 
+  unidiff_patch = [
+    "Index: iota\n",
+    "===================================================================\n",
+    "--- iota\t(revision 1)\n",
+    "+++ iota\t(working copy)\n",
+    "\n",
+    "Property changes on: iota\n",
+    "___________________________________________________________________\n",
+    "Added: propname\n",
+    "## -0,0 +1 ##\n",
+    "+propvalue\n",
+    ]
+  svntest.main.file_write(patch_file_path, ''.join(unidiff_patch))
+
+  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
+                                       0, # dry-run
+                                       '--reverse-diff') 
+
+@Issue(4004)
+def patch_reversed_add_with_props(sbox):
+  "reverse patch new file+props atop uncommitted"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+  patch_file_path = make_patch_path(sbox)
+
+  # Add a new file which also has props set on it.
+  newfile_path = os.path.join(wc_dir, 'newfile')
+  newfile_contents = ["This is the file 'newfile'.\n"]
+  svntest.main.file_write(newfile_path, ''.join(newfile_contents))
+  svntest.main.run_svn(None, 'add', newfile_path)
+  svntest.main.run_svn(None, 'propset', 'propname', 'propvalue',
+                       newfile_path)
+
+  # Generate a patch file from our current diff (rooted at the working
+  # copy root).
+  cwd = os.getcwd()
+  try:
+    os.chdir(wc_dir)
+    exit_code, diff_output, err_output = svntest.main.run_svn(None, 'diff')
+  finally:
+    os.chdir(cwd)
+  svntest.main.file_write(patch_file_path, ''.join(diff_output))
+
+  # Okay, now commit up.
+  expected_output = svntest.wc.State(wc_dir, {
+    'newfile' : Item(verb='Adding'),
+    })
+
+  # Now, we'll try to reverse-apply the very diff we just created.  We
+  # expect the original state of the working copy in r1.
+  expected_disk = svntest.main.greek_state.copy()
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_skip = wc.State('', { })
+  expected_output = [
+    'D         %s\n' % newfile_path,
+  ]
+  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
+                                       0, # dry-run
+                                       '--reverse-diff') 
+
+@Issue(4004)
+def patch_reversed_add_with_props2(sbox):
+  "reverse patch new file+props"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+  patch_file_path = make_patch_path(sbox)
+
+  # Add a new file which also has props set on it.
+  newfile_path = os.path.join(wc_dir, 'newfile')
+  newfile_contents = ["This is the file 'newfile'.\n"]
+  svntest.main.file_write(newfile_path, ''.join(newfile_contents))
+  svntest.main.run_svn(None, 'add', newfile_path)
+  svntest.main.run_svn(None, 'propset', 'propname', 'propvalue',
+                       newfile_path)
+
+  # Generate a patch file from our current diff (rooted at the working
+  # copy root).
+  cwd = os.getcwd()
+  try:
+    os.chdir(wc_dir)
+    exit_code, diff_output, err_output = svntest.main.run_svn(None, 'diff')
+  finally:
+    os.chdir(cwd)
+  svntest.main.file_write(patch_file_path, ''.join(diff_output))
+
+  # Okay, now commit up.
+  expected_output = svntest.wc.State(wc_dir, {
+    'newfile' : Item(verb='Adding'),
+    })
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.add({'newfile' : Item(wc_rev=2, status='  ')})
+  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
+                                        expected_status, None, wc_dir)
+
+  # Now, we'll try to reverse-apply the very diff we just created.  We
+  # expect the original state of the working copy in r1 plus 'newfile'
+  # scheduled for deletion.
+  expected_disk = svntest.main.greek_state.copy()
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.add({'newfile' : Item(status='D ', wc_rev=2)})
+  expected_skip = wc.State('', { })
+  expected_output = [
+    'D         %s\n' % newfile_path,
+  ]
+  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
+                                       0, # dry-run
+                                       '--reverse-diff') 
+
 ########################################################################
 #Run the tests
 
@@ -3520,6 +3707,9 @@
               patch_strip_cwd,
               patch_set_prop_no_eol,
               patch_add_symlink,
+              patch_deletes_prop,
+              patch_reversed_add_with_props,
+              patch_reversed_add_with_props2,
             ]
 
 if __name__ == '__main__':