subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ar...@apache.org
Subject svn commit: r1367002 [17/21] - in /subversion/branches/svn-bisect: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ contrib/client-side/emacs/ contrib/server-side/mod_dontdothat/ notes/ notes/api-errata/1.7/ notes/http-and-webdav/...
Date Mon, 30 Jul 2012 06:39:38 GMT
Modified: subversion/branches/svn-bisect/subversion/svn/cl.h
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/svn/cl.h?rev=1367002&r1=1367001&r2=1367002&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/svn/cl.h (original)
+++ subversion/branches/svn-bisect/subversion/svn/cl.h Mon Jul 30 06:39:28 2012
@@ -184,6 +184,7 @@ typedef struct svn_cl__opt_state_t
   svn_boolean_t no_ignore;       /* disregard default ignores & svn:ignore's */
   svn_boolean_t no_auth_cache;   /* do not cache authentication information */
   svn_boolean_t no_diff_deleted; /* do not show diffs for deleted files */
+  svn_boolean_t ignore_props;    /* ignore properties */
   svn_boolean_t show_copies_as_adds; /* do not diff copies with their source */
   svn_boolean_t notice_ancestry; /* notice ancestry for diff-y operations */
   svn_boolean_t ignore_ancestry; /* ignore ancestry for merge-y operations */
@@ -229,6 +230,7 @@ typedef struct svn_cl__opt_state_t
   svn_boolean_t show_diff;        /* produce diff output (maps to --diff) */
   svn_boolean_t internal_diff;    /* override diff_cmd in config file */
   svn_boolean_t use_git_diff_format; /* Use git's extended diff format */
+  svn_boolean_t use_patch_diff_format; /* Output compatible with GNU patch */
   svn_boolean_t allow_mixed_rev; /* Allow operation on mixed-revision WC */
   svn_boolean_t include_externals; /* Recurses (in)to file & dir externals */
 } svn_cl__opt_state_t;
@@ -823,6 +825,23 @@ svn_cl__local_style_skip_ancestor(const 
                                   const char *path,
                                   apr_pool_t *pool);
 
+/* Check that PATH_OR_URL1@REVISION1 is related to PATH_OR_URL2@REVISION2.
+ * Raise an error if not.
+ *
+ * ### Ideally we would also check that they are on different lines of
+ * history.  That is easy in common cases, but to give a correct answer in
+ * general we need to know the operative revision(s) as well.  For example,
+ * when one location is the branch point from which the other branch was
+ * copied.
+ */
+svn_error_t *
+svn_cl__check_related_source_and_target(const char *path_or_url1,
+                                        const svn_opt_revision_t *revision1,
+                                        const char *path_or_url2,
+                                        const svn_opt_revision_t *revision2,
+                                        svn_client_ctx_t *ctx,
+                                        apr_pool_t *pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

Modified: subversion/branches/svn-bisect/subversion/svn/diff-cmd.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/svn/diff-cmd.c?rev=1367002&r1=1367001&r2=1367002&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/svn/diff-cmd.c (original)
+++ subversion/branches/svn-bisect/subversion/svn/diff-cmd.c Mon Jul 30 06:39:28 2012
@@ -171,6 +171,10 @@ svn_cl__diff(apr_getopt_t *os,
   const char *old_target, *new_target;
   apr_pool_t *iterpool;
   svn_boolean_t pegged_diff = FALSE;
+  svn_boolean_t show_copies_as_adds =
+    opt_state->use_patch_diff_format ? TRUE : opt_state->show_copies_as_adds;
+  svn_boolean_t ignore_prop_diff =
+    opt_state->use_patch_diff_format ? TRUE : opt_state->ignore_props;
   int i;
   const svn_client_diff_summarize_func_t summarize_func =
     (opt_state->xml ? summarize_xml : summarize_regular);
@@ -361,8 +365,9 @@ svn_cl__diff(apr_getopt_t *os,
                      opt_state->depth,
                      ! opt_state->notice_ancestry,
                      opt_state->no_diff_deleted,
-                     opt_state->show_copies_as_adds,
+                     show_copies_as_adds,
                      opt_state->force,
+                     ignore_prop_diff,
                      opt_state->use_git_diff_format,
                      svn_cmdline_output_encoding(pool),
                      outstream,
@@ -406,8 +411,9 @@ svn_cl__diff(apr_getopt_t *os,
                      opt_state->depth,
                      ! opt_state->notice_ancestry,
                      opt_state->no_diff_deleted,
-                     opt_state->show_copies_as_adds,
+                     show_copies_as_adds,
                      opt_state->force,
+                     ignore_prop_diff,
                      opt_state->use_git_diff_format,
                      svn_cmdline_output_encoding(pool),
                      outstream,

Modified: subversion/branches/svn-bisect/subversion/svn/log-cmd.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/svn/log-cmd.c?rev=1367002&r1=1367001&r2=1367002&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/svn/log-cmd.c (original)
+++ subversion/branches/svn-bisect/subversion/svn/log-cmd.c Mon Jul 30 06:39:28 2012
@@ -51,8 +51,9 @@ struct log_receiver_baton
   /* Client context. */
   svn_client_ctx_t *ctx;
 
-  /* The URL target of the log operation. */
-  const char *target_url;
+  /* The target of the log operation. */
+  const char *target_path_or_url;
+  svn_opt_revision_t target_peg_revision;
 
   /* Don't print log message body nor its line count. */
   svn_boolean_t omit_log_message;
@@ -273,6 +274,9 @@ log_entry_receiver(void *baton,
       SVN_ERR(svn_cmdline_printf(pool, "\n%s\n", message));
     }
 
+  SVN_ERR(svn_cmdline_fflush(stdout));
+  SVN_ERR(svn_cmdline_fflush(stderr));
+
   /* Print a diff if requested. */
   if (lb->show_diff)
     {
@@ -297,30 +301,29 @@ log_entry_receiver(void *baton,
       end_revision.kind = svn_opt_revision_number;
       end_revision.value.number = log_entry->revision;
 
-      SVN_ERR(svn_cmdline_printf(pool, _("\n")));
-      SVN_ERR(svn_client_diff6(diff_options,
-                               lb->target_url,
-                               &start_revision,
-                               lb->target_url,
-                               &end_revision,
-                               NULL,
-                               lb->depth,
-                               FALSE, /* ignore ancestry */
-                               TRUE, /* no diff deleted */
-                               FALSE, /* show copies as adds */
-                               FALSE, /* ignore content type */
-                               FALSE, /* use git diff format */
-                               svn_cmdline_output_encoding(pool),
-                               outstream,
-                               errstream,
-                               NULL,
-                               lb->ctx, pool));
-      SVN_ERR(svn_cmdline_printf(pool, _("\n")));
+      SVN_ERR(svn_stream_printf(outstream, pool, _("\n")));
+      SVN_ERR(svn_client_diff_peg6(diff_options,
+                                   lb->target_path_or_url,
+                                   &lb->target_peg_revision,
+                                   &start_revision, &end_revision,
+                                   NULL,
+                                   lb->depth,
+                                   FALSE, /* ignore ancestry */
+                                   TRUE, /* no diff deleted */
+                                   FALSE, /* show copies as adds */
+                                   FALSE, /* ignore content type */
+                                   FALSE, /* ignore prop diff */
+                                   FALSE, /* use git diff format */
+                                   svn_cmdline_output_encoding(pool),
+                                   outstream,
+                                   errstream,
+                                   NULL,
+                                   lb->ctx, pool));
+      SVN_ERR(svn_stream_printf(outstream, pool, _("\n")));
+      SVN_ERR(svn_stream_close(outstream));
+      SVN_ERR(svn_stream_close(errstream));
     }
 
-  SVN_ERR(svn_cmdline_fflush(stdout));
-  SVN_ERR(svn_cmdline_fflush(stderr));
-
   if (log_entry->has_children)
     APR_ARRAY_PUSH(lb->merge_stack, svn_revnum_t) = log_entry->revision;
 
@@ -507,8 +510,6 @@ svn_cl__log(apr_getopt_t *os,
   struct log_receiver_baton lb;
   const char *target;
   int i;
-  svn_opt_revision_t peg_revision;
-  const char *true_path;
   apr_array_header_t *revprops;
 
   if (!opt_state->xml)
@@ -562,8 +563,6 @@ svn_cl__log(apr_getopt_t *os,
   /* Add "." if user passed 0 arguments */
   svn_opt_push_implicit_dot_target(targets, pool);
 
-  target = APR_ARRAY_IDX(targets, 0, const char *);
-
   /* Determine if they really want a two-revision range. */
   if (opt_state->used_change_arg)
     {
@@ -585,9 +584,15 @@ svn_cl__log(apr_getopt_t *os,
         }
     }
 
-  /* Strip peg revision. */
-  SVN_ERR(svn_opt_parse_path(&peg_revision, &true_path, target, pool));
-  APR_ARRAY_IDX(targets, 0, const char *) = true_path;
+  /* Parse the first target into path-or-url and peg revision. */
+  target = APR_ARRAY_IDX(targets, 0, const char *);
+  SVN_ERR(svn_opt_parse_path(&lb.target_peg_revision, &lb.target_path_or_url,
+                             target, pool));
+  if (lb.target_peg_revision.kind == svn_opt_revision_unspecified)
+    lb.target_peg_revision.kind = (svn_path_is_url(target)
+                                     ? svn_opt_revision_head
+                                     : svn_opt_revision_working);
+  APR_ARRAY_IDX(targets, 0, const char *) = lb.target_path_or_url;
 
   if (svn_path_is_url(target))
     {
@@ -606,8 +611,6 @@ svn_cl__log(apr_getopt_t *os,
 
   lb.ctx = ctx;
   lb.omit_log_message = opt_state->quiet;
-  SVN_ERR(svn_client_url_from_path2(&lb.target_url, true_path, ctx,
-                                    pool, pool));
   lb.show_diff = opt_state->show_diff;
   lb.depth = opt_state->depth == svn_depth_unknown ? svn_depth_infinity
                                                    : opt_state->depth;
@@ -658,7 +661,7 @@ svn_cl__log(apr_getopt_t *os,
             APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG;
         }
       SVN_ERR(svn_client_log5(targets,
-                              &peg_revision,
+                              &lb.target_peg_revision,
                               opt_state->revision_ranges,
                               opt_state->limit,
                               opt_state->verbose,
@@ -681,7 +684,7 @@ svn_cl__log(apr_getopt_t *os,
       if (!opt_state->quiet)
         APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG;
       SVN_ERR(svn_client_log5(targets,
-                              &peg_revision,
+                              &lb.target_peg_revision,
                               opt_state->revision_ranges,
                               opt_state->limit,
                               opt_state->verbose,

Modified: subversion/branches/svn-bisect/subversion/svn/main.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/svn/main.c?rev=1367002&r1=1367001&r2=1367002&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/svn/main.c (original)
+++ subversion/branches/svn-bisect/subversion/svn/main.c Mon Jul 30 06:39:28 2012
@@ -55,7 +55,6 @@
 #include "cl.h"
 
 #include "private/svn_opt_private.h"
-#include "private/svn_wc_private.h"
 #include "private/svn_cmdline_private.h"
 
 #include "svn_private_config.h"
@@ -90,6 +89,7 @@ typedef enum svn_cl__longopt_t {
   opt_no_auth_cache,
   opt_no_autoprops,
   opt_no_diff_deleted,
+  opt_ignore_props,
   opt_no_ignore,
   opt_no_unlock,
   opt_non_interactive,
@@ -124,6 +124,7 @@ typedef enum svn_cl__longopt_t {
   opt_diff,
   opt_internal_diff,
   opt_use_git_diff_format,
+  opt_use_patch_diff_format,
   opt_allow_mixed_revisions,
   opt_include_externals,
 } svn_cl__longopt_t;
@@ -240,6 +241,8 @@ const apr_getopt_option_t svn_cl__option
                     N_("try operation but make no changes")},
   {"no-diff-deleted", opt_no_diff_deleted, 0,
                     N_("do not print differences for deleted files")},
+  {"ignore-properties", opt_ignore_props, 0,
+                    N_("ignore properties during the operation")},
   {"notice-ancestry", opt_notice_ancestry, 0,
                     N_("notice ancestry when calculating differences")},
   {"ignore-ancestry", opt_ignore_ancestry, 0,
@@ -301,11 +304,14 @@ const apr_getopt_option_t svn_cl__option
   {"accept",        opt_accept, 1,
                     N_("specify automatic conflict resolution action\n"
                        "                             "
-                       "('postpone', 'base', 'mine-conflict',\n"
+                       "('postpone', 'working', 'base', 'mine-conflict',\n"
                        "                             "
                        "'theirs-conflict', 'mine-full', 'theirs-full',\n"
                        "                             "
-                       "'edit', 'launch')")},
+                       "'edit', 'launch')\n"
+                       "                             "
+                       "(shorthand: 'p', 'mc', 'tc', 'mf', 'tf', 'e', 'l')"
+                       )},
   {"show-revs",     opt_show_revs, 1,
                     N_("specify which collection of revisions to display\n"
                        "                             "
@@ -341,6 +347,13 @@ const apr_getopt_option_t svn_cl__option
                        N_("override diff-cmd specified in config file")},
   {"git", opt_use_git_diff_format, 0,
                        N_("use git's extended diff format")},
+  {"patch-compatible", opt_use_patch_diff_format, 0,
+                       N_("generate diff suitable for generic third-party\n"
+                       "                             "
+                       "patch tools; currently the same as\n"
+                       "                             "
+                       "--show-copies-as-adds --ignore-properties\n"
+                       )},
   {"allow-mixed-revisions", opt_allow_mixed_revisions, 0,
                        N_("Allow merge into mixed-revision working copy.\n"
                        "                             "
@@ -542,9 +555,9 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "\n"
      "  Use just 'svn diff' to display local modifications in a working copy.\n"),
     {'r', 'c', opt_old_cmd, opt_new_cmd, 'N', opt_depth, opt_diff_cmd,
-     opt_internal_diff, 'x', opt_no_diff_deleted, opt_show_copies_as_adds,
-     opt_notice_ancestry, opt_summarize, opt_changelist, opt_force, opt_xml,
-     opt_use_git_diff_format} },
+     opt_internal_diff, 'x', opt_no_diff_deleted, opt_ignore_props,
+     opt_show_copies_as_adds, opt_notice_ancestry, opt_summarize, opt_changelist,
+     opt_force, opt_xml, opt_use_git_diff_format, opt_use_patch_diff_format} },
   { "export", svn_cl__export, {0}, N_
     ("Create an unversioned copy of a tree.\n"
      "usage: 1. export [-r REV] URL[@PEGREV] [PATH]\n"
@@ -669,7 +682,11 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "    svn log bar.c@42\n"
      "    svn log http://www.example.com/repo/project/foo.c\n"
      "    svn log http://www.example.com/repo/project foo.c bar.c\n"
-     "    svn log http://www.example.com/repo/project@50 foo.c bar.c\n"),
+     "    svn log http://www.example.com/repo/project@50 foo.c bar.c\n"
+     "\n"
+     "    This command shows the log entry for the revision the branch\n"
+     "    ^/branches/foo was created in:\n"
+     "      svn log --stop-on-copy --limit 1 -r0:HEAD ^/branches/foo\n"),
     {'r', 'q', 'v', 'g', 'c', opt_targets, opt_stop_on_copy, opt_incremental,
      opt_xml, 'l', opt_with_all_revprops, opt_with_no_revprops, opt_with_revprop,
      opt_depth, opt_diff, opt_diff_cmd, opt_internal_diff, 'x'},
@@ -677,294 +694,330 @@ const svn_opt_subcommand_desc2_t svn_cl_
      {'c', N_("the change made in revision ARG")}} },
 
   { "merge", svn_cl__merge, {0}, N_
-    ("Merge changes into a working copy.\n"
-     "usage: 0. merge SOURCE[@REV] [TARGET_WCPATH]\n"
-     "          (the 'sync' merge)\n"
-     "       1. merge [-c M[,N...] | -r N:M ...] SOURCE[@REV] [TARGET_WCPATH]\n"
-     "          (the 'cherry-pick' merge)\n"
-     "       2. merge --reintegrate SOURCE[@REV] [TARGET_WCPATH]\n"
-     "          (the 'reintegrate' merge)\n"
-     "       3. merge SOURCE1[@N] SOURCE2[@M] [TARGET_WCPATH]\n"
-     "          (the '2-URL' merge)\n"
-     "\n"
-     "  0. This form is called a 'sync' (aka 'catch-up') merge:\n"
-     "\n"
-     "       svn merge SOURCE[@REV] [TARGET_WCPATH]\n"
-     "\n"
-     "     A sync merge is used to merge into a branch any unmerged changes\n"
-     "     made on its immediate ancestor branch. This uses merge tracking\n"
-     "     to find which changes need to be merged.\n"
-     "\n"
-     "     SOURCE specifies the branch from where the changes will be pulled,\n"
-     "     and TARGET_WCPATH specifies the working copy of the target branch,\n"
-     "     into which the changes will be applied. Normally SOURCE and\n"
-     "     TARGET_WCPATH should each point to the root of a branch. If changes\n"
-     "     need to be merged from and to only a subtree of the branch, then\n"
-     "     the path to that subtree must be included in both SOURCE and\n"
-     "     TARGET_WCPATH.\n"
-     "\n"
-     "     SOURCE is usually a URL. The optional '@REV' specifies both the peg\n"
-     "     revision of the URL and the latest revision that will be considered\n"
-     "     for merging; the default value is the HEAD revision. If SOURCE is a\n"
-     "     working copy path, the corresponding URL of the path is used, and\n"
-     "     the default value of 'REV' is the base revision.\n"
-     "\n"
-     "     TARGET_WCPATH is a working copy path; the default is '.'.\n"
-     "\n"
-     "       - Sync Merge Example -\n"
-     "\n"
-     "     A feature is being developed on a branch called 'feature'. The\n"
-     "     feature branch is regularly synced with trunk to keep up with the\n"
-     "     changes made there. The previous sync merges are not shown on this\n"
-     "     diagram, and the last of them was done when HEAD was r100, and now\n"
-     "     HEAD is r200.\n"
-     "\n"
-     "                 feature  +------------------------o-----\n"
-     "                         /                         ^\n"
-     "                        /                         /\n"
-     "                       /          .............../\n"
-     "         trunk ------+------------L--------------R------\n"
-     "                                r100           r200\n"
-     "\n"
-     "     Subversion will locate all the changes on 'trunk' that have not yet\n"
-     "     been merged into the target. In this case that is a single range,\n"
-     "     r100:200. In the diagram above, L marks the 'left' side of the\n"
-     "     range (trunk@100) and R marks the 'right' side of the range\n"
-     "     (trunk@200). The difference between L and R will be merged into\n"
-     "     the target.\n"
-     "\n"
-     "     To perform this sync merge, check out a working copy of the feature\n"
-     "     branch and run the following command in the top-level directory\n"
-     "     of the working copy:\n"
-     "\n"
-     "         svn merge ^/trunk\n"
-     "\n"
-     "\n"
-     "  1. This form is called a 'cherry-pick' merge:\n"
-     "\n"
-     "       svn merge [-c M[,N...] | -r N:M ...] SOURCE[@REV] [TARGET_WCPATH]\n"
-     "\n"
-     "     A cherry-picking merge is used to merge specific revisions (or\n"
-     "     revision ranges) from one branch to another. By default, this form\n"
-     "     of merge uses merge tracking to avoid re-merging any of the specified\n"
-     "     revisions that have already been merged. If the --ignore-ancestry\n"
-     "     option is given then that does not happen.\n"
-     "\n"
-     "     The SOURCE and TARGET_WCPATH arguments are the same as for a 'sync'\n"
-     "     merge, except that REV acts only as a peg and does not specify the\n"
-     "     latest revision that will be considered for merging.\n"
-     "\n"
-     "     The revision ranges to be merged are specified by the '-r' and/or\n"
-     "     '-c' options. '-r N:M' refers to the difference in the history of\n"
-     "     the source branch between revision N and revision M. '-c M' is\n"
-     "     equivalent to '-r <M-1>:M'. Each such difference is merged into\n"
-     "     TARGET_WCPATH.\n"
-     "\n"
-     "     If mergeinfo within TARGET_WCPATH indicates that revisions within\n"
-     "     the range were already merged, changes made in those revisions\n"
-     "     are not merged again. If needed, the range is broken into multiple\n"
-     "     sub-ranges, and each sub-range is merged separately.\n"
-     "\n"
-     "     If N is greater than M in '-r N:M', or with '-c -M' which is\n"
-     "     equivalent to '-r M:<M-1>', the range is a 'reverse range'.\n"
-     "     A reverse range can be used to undo changes made to SOURCE\n"
-     "     between revisions N and M.\n"
-     "\n"
-     "     Multiple '-c' and/or '-r' options may be specified and mixing of\n"
-     "     forward and reverse ranges is allowed.\n"
-     "\n"
-     "       - Cherry-picking Merge Example -\n"
-     "\n"
-     "     A bug has been fixed on trunk on revision 50. This fix needs to\n"
-     "     be merged from the trunk into the release branch.\n"
-     "\n"
-     "            1.x-release  +-----------------------o-----\n"
-     "                        /                        ^\n"
-     "                       /                         |\n"
-     "                      /                          |\n"
-     "         trunk ------+--------------------------LR-----\n"
-     "                                                r50\n"
-     "\n"
-     "     In the above diagram, L marks the left side of the merge (trunk@49)\n"
-     "     and R marks the right side of the merge (trunk@50). The difference\n"
-     "     between the left and right side is merged into the target.\n"
-     "\n"
-     "     To perform the merge, check out a working copy of the release\n"
-     "     branch and run the following command in the top-level directory\n"
-     "     of the working copy:\n"
-     "\n"
-     "         svn merge -c50 ^/trunk\n"
-     "\n"
-     "     If several commits to trunk were related to the fix, multiple\n"
-     "     revisions can be merged:\n"
-     "\n"
-     "         svn merge -c50,54,60 ^/trunk\n"
-     "\n"
-     "\n"
-     "  2. This form is called a 'reintegrate merge':\n"
-     "\n"
-     "       svn merge --reintegrate SOURCE[@REV] [TARGET_WCPATH]\n"
-     "\n"
-     "     SOURCE is the URL of a branch to be merged back into (usually) its\n"
-     "     immediate ancestor branch.  If REV is specified, it is used a\n"
-     "     the peg revision for SOURCE, i.e. SOURCE is looked up in the\n"
-     "     repository at revision REV.  If REV is not specified, the HEAD\n"
-     "     revision is assumed.\n"
-     "\n"
-     "     TARGET_WCPATH is a working copy of the branch the changes will\n"
-     "     be applied to.\n"
-     "\n"
-     "       - Reintegrate Merge Example -\n"
-     "\n"
-     "     A feature has been developed on a branch called 'feature'.\n"
-     "     The feature branch started as a copy of trunk@W. Work on the\n"
-     "     feature has completed and it should be merged back into the trunk.\n"
-     "\n"
-     "     The feature branch was last synced with its immediate ancestor,\n"
-     "     the trunk, in revision X. So the difference between trunk@X and\n"
-     "     feature@HEAD contains the complete set of changes that implement\n"
-     "     the feature, and no other changes. These changes are applied to\n"
-     "     the trunk.\n"
-     "\n"
-     "                 feature  +-------------------------------R\n"
-     "                         /                               . \\\n"
-     "                        /                  ..............   \\\n"
-     "                       /                  .                  v\n"
-     "         trunk ------+--------------------L------------------o\n"
-     "                    rW                   rX\n"
-     "\n"
-     "     In the diagram above, L marks the left side of the merge (trunk@X),\n"
-     "     and R marks the right side of the merge (feature@HEAD). The difference\n"
-     "     between the left and right side is merged into the target.\n"
-     "\n"
-     "     To perform the merge, check out a working copy of the trunk, and run\n"
-     "     the following command in the top-level directory of the working copy:\n"
-     "\n"
-     "         svn merge --reintegrate ^/feature\n"
-     "\n"
-     "     To prevent unnecessary merge conflicts, reintegrate merges require\n"
-     "     that TARGET_WCPATH is not a mixed-revision working copy, has no\n"
-     "     local modifications, and has no switched subtrees.\n"
-     "\n"
-     "     Reintegrate merges also require that the reintegrate source be fully\n"
-     "     synced with the target since their common branch point.\n"
-     "     In the above example this means that all of the changes made\n"
-     "     on trunk between revision W and revision X are fully merged to\n"
-     "     the feature branch before it can be reintegrated back to trunk.\n"
-     "\n"
-     "     After the reintegrate merge, the feature branch cannot be synced\n"
-     "     to the trunk again without merge conflicts. If further work must\n"
-     "     be done on the feature branch, it should be deleted and then re-created.\n"
-     "\n"
-     "\n"
-     "  3. This form is called a '2-URL merge':\n"
-     "\n"
-     "       svn merge SOURCE1[@N] SOURCE2[@M] [TARGET_WCPATH]\n"
-     "\n"
-     "     Two source URLs are specified, together with two revisions N and M.\n"
-     "     The two sources to be compared at the specified revisions, and the\n"
-     "     difference is applied to TARGET_WCPATH, which is a path to a working\n"
-     "     copy of another branch.\n"
-     "\n"
-     "     The revisions default to HEAD if omitted.\n"
-     "\n"
-     "     If TARGET_WCPATH is omitted, a default value of '.' is assumed,\n"
-     "     unless the sources have identical basenames that match a file\n"
-     "     within '.'; In which case, the differences will be applied to\n"
-     "     that file.\n"
-     "\n"
-     "     The sources can also be specified as working copy paths, in which\n"
-     "     case the URLs of the merge sources are derived from the working copies.\n"
-     "\n"
-     "     This is the most flexible type of merge, but also the most difficult\n"
-     "     to use. It can be used to merge the differences between two (possibly\n"
-     "     ancestrally unrelated) branches into a working copy of another branch.\n"
-     "     This type of merge should be used very carefully because the probability\n"
-     "     of merge conflicts is quite high. In most use cases, a sync, cherry-pick,\n"
-     "     or reintegrate merge is sufficient and reduces the chances of mistakes.\n"
-     "\n"
-     "       - 2-URL Merge Example -\n"
-     "\n"
-     "     A feature has been developed on a branch called 'feature'.\n"
-     "     Development for the upcoming 3.0 release has happened in parallel on\n"
-     "     the '3.x-release' branch. The work on the feature branch must be\n"
-     "     merged to the 3.x-release branch. However, the feature branch and\n"
-     "     the 3.x-release branch are not directly related, so a 2-URL merge\n"
-     "     is needed.\n"
-     "     The feature branch was last synced with its immediate ancestor,\n"
-     "     the trunk, up to revision 500. So the difference between trunk@500\n"
-     "     and feature@HEAD contains the complete set of changes related to\n"
-     "     the feature, and no other changes. These changes are applied to\n"
-     "     the 3.x-release branch.\n"
-     "\n"
-     "                   3.x-release  +-----------------------------------o\n"
-     "                               /                                    ^\n"
-     "                              /                                    /\n"
-     "                             /              r500                  /\n"
-     "         trunk ------+------+-----------------L--------->        /\n"
-     "                      \\                         .               /\n"
-     "                       \\                         ...........   /\n"
-     "                        \\                                   . /\n"
-     "                feature  +-----------------------------------R\n"
-     "\n"
-     "     In the diagram above, L marks the left side of the merge (trunk@500),\n"
-     "     and R marks the right side of the merge is (feature@HEAD).\n"
-     "     The difference between the left and right side is merged into the target.\n"
-     "\n"
-     "     To perform the merge, check out a working copy of the 3.x-release\n"
-     "     branch and run the following command in the top-level directory\n"
-     "     of the working copy:\n"
-     "\n"
-     "         svn merge ^/trunk@500 ^/feature\n"
-     "\n"
-     "     Before performing a 2-URL merge, it is a good idea to preview the\n"
-     "     changes which will be merged, because there is no guarantee that\n"
-     "     the merge will be free of conflicts. The preview can be done with\n"
-     "     the svn diff command:\n"
-     "\n"
-     "         svn diff ^/trunk@500 ^/feature@HEAD\n"
-     "\n"
-     "     Note that a 2-URL merge can also merge from foreign repositories.\n"
-     "     While SOURCE1 and SOURCE2 must both come from the same repository,\n"
-     "     TARGET_WCPATH may come from a different repository than the sources.\n"
-     "     However, there are some caveats. Most notably, copies made in the\n"
-     "     merge source will be transformed into plain additions in the merge\n"
-     "     target. Also, merge-tracking is not supported.\n"
-     "\n"
-     "\n"
-     "  The following applies to all types of merges:\n"
-     "\n"
-     "  To prevent unnecessary merge conflicts, svn merge requires that\n"
-     "  TARGET_WCPATH is not a mixed-revision working copy.\n"
-     "  Running 'svn update' before starting a merge ensures that all\n"
-     "  items in the working copy are based on the same revision.\n"
-     "\n"
-     "  For each merged item a line will be printed with characters reporting\n"
-     "  the action taken. These characters have the following meaning:\n"
-     "\n"
-     "    A  Added\n"
-     "    D  Deleted\n"
-     "    U  Updated\n"
-     "    C  Conflict\n"
-     "    G  Merged\n"
-     "    E  Existed\n"
-     "    R  Replaced\n"
-     "\n"
-     "  Characters in the first column report about the item itself.\n"
-     "  Characters in the second column report about properties of the item.\n"
-     "  A 'C' in the third column indicates a tree conflict, while a 'C' in\n"
-     "  the first and second columns indicate textual conflicts in files\n"
-     "  and in property values, respectively.\n"
-     "\n"
-     "  NOTE:  Subversion uses the svn:mergeinfo property to track merge\n"
-     "  history.  This property is considered at the start of a merge to\n"
-     "  determine what to merge and it is updated at the conclusion of the\n"
-     "  merge to describe the merge that took place.  Mergeinfo is used only\n"
-     "  if the two sources are on the same line of history -- if the first\n"
-     "  source is an ancestor of the second, or vice-versa.  This is guaranteed\n"
-     "  to be the case when using sync merges and reintegrate merges.\n"
-     "  The --ignore-ancestry option prevents merge tracking and thus\n"
-     "  ignores mergeinfo, neither considering it nor recording it.\n"),
+    ( /* For this large section, let's keep it unindented for easier
+       * viewing/editing. It has been vim-treated with a textwidth=75 and 'gw'
+       * (with quotes and newlines removed). */
+"Merge changes into a working copy.\n"
+"usage: 1. merge SOURCE[@REV] [TARGET_WCPATH]\n"
+"          (the 'sync' merge)\n"
+"       2. merge [-c M[,N...] | -r N:M ...] SOURCE[@REV] [TARGET_WCPATH]\n"
+"          (the 'cherry-pick' merge)\n"
+"       3. merge --reintegrate SOURCE[@REV] [TARGET_WCPATH]\n"
+"          (the 'reintegrate' merge)\n"
+"       4. merge SOURCE1[@N] SOURCE2[@M] [TARGET_WCPATH]\n"
+"          (the '2-URL' merge)\n"
+"\n"
+"  1. This form is called a 'sync' (or 'catch-up') merge:\n"
+"\n"
+"       svn merge SOURCE[@REV] [TARGET_WCPATH]\n"
+"\n"
+"     A sync merge is used to fetch all the latest changes made on a parent\n"
+"     branch. In other words, the target branch has originally been created\n"
+"     by copying the source branch, and any changes committed on the source\n"
+"     branch since branching are applied to the target branch. This uses\n"
+"     merge tracking to skip all those revisions that have already been\n"
+"     merged, so a sync merge can be repeated periodically to stay up-to-\n"
+"     date with the source branch.\n"
+"\n"
+"     SOURCE specifies the branch from where the changes will be pulled, and\n"
+"     TARGET_WCPATH specifies a working copy of the target branch to which\n"
+"     the changes will be applied. Normally SOURCE and TARGET_WCPATH should\n"
+"     each correspond to the root of a branch. (If you want to merge only a\n"
+"     subtree, then the subtree path must be included in both SOURCE and\n"
+"     TARGET_WCPATH; this is discouraged, to avoid subtree mergeinfo.)\n"
+"\n"
+"     SOURCE is usually a URL. The optional '@REV' specifies both the peg\n"
+"     revision of the URL and the latest revision that will be considered\n"
+"     for merging; if REV is not specified, the HEAD revision is assumed. If\n"
+"     SOURCE is a working copy path, the corresponding URL of the path is\n"
+"     used, and the default value of 'REV' is the base revision (usually the\n"
+"     revision last updated to).\n"
+"\n"
+"     TARGET_WCPATH is a working copy path; if omitted, '.' is assumed.\n"
+"\n"
+"       - Sync Merge Example -\n"
+"\n"
+"     A feature is being developed on a branch called 'feature', which has\n"
+"     originally been a copy of trunk. The feature branch has been regularly\n"
+"     synced with trunk to keep up with the changes made there. The previous\n"
+"     sync merges are not shown on this diagram, and the last of them was\n"
+"     done when HEAD was r100. Currently, HEAD is r200.\n"
+"\n"
+"                feature  +------------------------o-----\n"
+"                        /                         ^\n"
+"                       /            ............  |\n"
+"                      /            .            . /\n"
+"         trunk ------+------------L--------------R------\n"
+"                                r100           r200\n"
+"\n"
+"     Subversion will locate all the changes on 'trunk' that have not yet\n"
+"     been merged into the 'feature' branch. In this case that is a single\n"
+"     range, r100:200. In the diagram above, L marks the left side\n"
+"     (trunk@100) and R marks the right side (trunk@200) of the merge. The\n"
+"     difference between L and R will be applied to the target working copy\n"
+"     path. In this case, the working copy is a clean checkout of the entire\n"
+"     'feature' branch.\n"
+"\n"
+"     To perform this sync merge, have a clean working copy of the feature\n"
+"     branch and run the following command in its top-level directory:\n"
+"\n"
+"         svn merge ^/trunk\n"
+"\n"
+"     Note that the merge is now only in your local working copy and still\n"
+"     needs to be committed to the repository so that it can be seen by\n"
+"     others. You can review the changes and you may have to resolve\n"
+"     conflicts before you commit the merge.\n"
+"\n"
+"\n"
+"  2. This form is called a 'cherry-pick' merge:\n"
+"\n"
+"       svn merge [-c M[,N...] | -r N:M ...] SOURCE[@REV] [TARGET_WCPATH]\n"
+"\n"
+"     A cherry-pick merge is used to merge specific revisions (or revision\n"
+"     ranges) from one branch to another. By default, this uses merge\n"
+"     tracking to automatically skip any revisions that have already been\n"
+"     merged to the target; you can use the --ignore-ancestry option to\n"
+"     disable such skipping.\n"
+"\n"
+"     SOURCE is usually a URL. The optional '@REV' specifies only the peg\n"
+"     revision of the URL and does not affect the merge range; if REV is not\n"
+"     specified, the HEAD revision is assumed. If SOURCE is a working copy\n"
+"     path, the corresponding URL of the path is used, and the default value\n"
+"     of 'REV' is the base revision (usually the revision last updated to).\n"
+"\n"
+"     TARGET_WCPATH is a working copy path; if omitted, '.' is assumed.\n"
+"\n"
+"     The revision ranges to be merged are specified by the '-r' and/or '-c'\n"
+"     options. '-r N:M' refers to the difference in the history of the\n"
+"     source branch between revisions N and M. You can use '-c M' to merge\n"
+"     single revisions: '-c M' is equivalent to '-r <M-1>:M'. Each such\n"
+"     difference is applied to TARGET_WCPATH.\n"
+"\n"
+"     If the mergeinfo in TARGET_WCPATH indicates that revisions within the\n"
+"     range were already merged, changes made in those revisions are not\n"
+"     merged again. If needed, the range is broken into multiple sub-ranges,\n"
+"     and each sub-range is merged separately.\n"
+"\n"
+"     A 'reverse range' can be used to undo changes. For example, when\n"
+"     source and target refer to the same branch, a previously committed\n"
+"     revision can be 'undone'. In a reverse range, N is greater than M in\n"
+"     '-r N:M', or the '-c' option is used with a negative number: '-c -M'\n"
+"     is equivalent to '-r M:<M-1>'.\n"
+"\n"
+"     Multiple '-c' and/or '-r' options may be specified and mixing of\n"
+"     forward and reverse ranges is allowed.\n"
+"\n"
+"       - Cherry-pick Merge Example -\n"
+"\n"
+"     A bug has been fixed on trunk in revision 50. This fix needs to\n"
+"     be merged from trunk onto the release branch.\n"
+"\n"
+"            1.x-release  +-----------------------o-----\n"
+"                        /                        ^\n"
+"                       /                         |\n"
+"                      /                          |\n"
+"         trunk ------+--------------------------LR-----\n"
+"                                                r50\n"
+"\n"
+"     In the above diagram, L marks the left side (trunk@49) and R marks the\n"
+"     right side (trunk@50) of the merge. The difference between the left\n"
+"     and right side is applied to the target working copy path.\n"
+"\n"
+"     Note that the difference between revision 49 and 50 is exactly those\n"
+"     changes that were committed in revision 50, not including changes\n"
+"     committed in revision 49.\n"
+"\n"
+"     To perform the merge, have a clean working copy of the release branch\n"
+"     and run the following command in its top-level directory; remember\n"
+"     that the default target is '.':\n"
+"\n"
+"         svn merge -c50 ^/trunk\n"
+"\n"
+"     You can also cherry-pick several revisions and/or revision ranges:\n"
+"\n"
+"         svn merge -c50,54,60 -r65:68 ^/trunk\n"
+"\n"
+"\n"
+"  3. This form is called a 'reintegrate merge':\n"
+"\n"
+"       svn merge --reintegrate SOURCE[@REV] [TARGET_WCPATH]\n"
+"\n"
+"     In a reintegrate merge, an (e.g. feature) branch is merged back to its\n"
+"     originating branch. In other words, the source branch has originally\n"
+"     been created by copying the target branch, development has concluded\n"
+"     on the source branch and it should now be merged back into the target\n"
+"     branch.\n"
+"     \n"
+"     SOURCE is the URL of a branch to be merged back. If REV is specified,\n"
+"     it is used as the peg revision for SOURCE; if REV is not specified,\n"
+"     the HEAD revision is assumed.\n"
+"\n"
+"     TARGET_WCPATH is a working copy of the branch the changes will be\n"
+"     applied to.\n"
+"\n"
+"       - Reintegrate Merge Example -\n"
+"\n"
+"     A feature has been developed on a branch called 'feature'. The feature\n"
+"     branch started as a copy of trunk@W. Work on the feature has completed\n"
+"     and it should be merged back into the trunk.\n"
+"\n"
+"     The feature branch was last synced with trunk up to revision X. So the\n"
+"     difference between trunk@X and feature@HEAD contains the complete set\n"
+"     of changes that implement the feature, and no other changes. These\n"
+"     changes are applied to trunk.\n"
+"\n"
+"                feature  +--------------------------------R\n"
+"                        /                                . \\\n"
+"                       /                    .............   \\\n"
+"                      /                    .                 v\n"
+"         trunk ------+--------------------L------------------o\n"
+"                    rW                   rX\n"
+"\n"
+"     In the diagram above, L marks the left side (trunk@X) and R marks the\n"
+"     right side (feature@HEAD) of the merge. The difference between the\n"
+"     left and right side is merged into trunk, the target.\n"
+"\n"
+"     To perform the merge, have a clean working copy of trunk and run the\n"
+"     following command in its top-level directory:\n"
+"\n"
+"         svn merge --reintegrate ^/feature\n"
+"\n"
+"     To prevent unnecessary merge conflicts, a reintegrate merge requires\n"
+"     that TARGET_WCPATH is not a mixed-revision working copy, has no local\n"
+"     modifications, and has no switched subtrees.\n"
+"\n"
+"     A reintegrate merge also requires that the source branch is coherently\n"
+"     synced with the target -- in the above example, this means that all\n"
+"     revisions between the branch point W and the last merged revision X\n"
+"     are merged to the feature branch, so that there are no unmerged\n"
+"     revisions in-between.\n"
+"\n"
+"     After the reintegrate merge, the feature branch cannot be synced to\n"
+"     the trunk again without merge conflicts. If further work must be done\n"
+"     on the feature branch, it should be deleted and then re-created.\n"
+"\n"
+"\n"
+"  4. This form is called a '2-URL merge':\n"
+"\n"
+"       svn merge SOURCE1[@N] SOURCE2[@M] [TARGET_WCPATH]\n"
+"\n"
+"     Two source URLs are specified, together with two revisions N and M.\n"
+"     The two sources are compared at the specified revisions, and the\n"
+"     difference is applied to TARGET_WCPATH, which is a path to a working\n"
+"     copy of another branch. The three branches involved can be completely\n"
+"     unrelated.\n"
+"\n"
+"     You should use this merge variant only if the other variants do not\n"
+"     apply to your situation, as this variant can be quite complex to\n"
+"     master.\n"
+"\n"
+"     If TARGET_WCPATH is omitted, a default value of '.' is assumed.\n"
+"     However, in the special case where both sources refer to a file node\n"
+"     with the same basename and a similarly named file is also found within\n"
+"     '.', the differences will be applied to that local file.  The source\n"
+"     revisions default to HEAD if omitted.\n"
+"\n"
+"     The sources can also be specified as working copy paths, in which case\n"
+"     the URLs of the merge sources are derived from the working copies.\n"
+"\n"
+"       - 2-URL Merge Example -\n"
+"\n"
+"     Two features have been developed on separate branches called 'foo' and\n"
+"     'bar'. It has since become clear that 'bar' should be combined with\n"
+"     the 'foo' branch for further development before reintegration.\n"
+"\n"
+"     Although both feature branches originate from trunk, they are not\n"
+"     directly related -- one is not a direct copy of the other. A 2-URL\n"
+"     merge is necessary.\n"
+"\n"
+"     The 'bar' branch has been synced with trunk up to revision 500.\n"
+"     (If this revision number is not known, it can be located using the\n"
+"     'svn log' and/or 'svn mergeinfo' commands.)\n"
+"     The difference between trunk@500 and bar@HEAD contains the complete\n"
+"     set of changes related to feature 'bar', and no other changes. These\n"
+"     changes are applied to the 'foo' branch.\n"
+"\n"
+"                           foo  +-----------------------------------o\n"
+"                               /                                    ^\n"
+"                              /                                    /\n"
+"                             /              r500                  /\n"
+"         trunk ------+------+-----------------L--------->        /\n"
+"                      \\                        .                /\n"
+"                       \\                        ............   /\n"
+"                        \\                                   . /\n"
+"                    bar  +-----------------------------------R\n"
+"\n"
+"     In the diagram above, L marks the left side (trunk@500) and R marks\n"
+"     the right side (bar@HEAD) of the merge. The difference between the\n"
+"     left and right side is applied to the target working copy path, in\n"
+"     this case a working copy of the 'foo' branch.\n"
+"\n"
+"     To perform the merge, have a clean working copy of the 'foo' branch\n"
+"     and run the following command in its top-level directory:\n"
+"\n"
+"         svn merge ^/trunk@500 ^/bar\n"
+"\n"
+"     The exact changes applied by a 2-URL merge can be previewed with svn's\n"
+"     diff command, which is a good idea to verify if you do not have the\n"
+"     luxury of a clean working copy to merge to. In this case:\n"
+"\n"
+"         svn diff ^/trunk@500 ^/bar@HEAD\n"
+"\n"
+"\n"
+"  The following applies to all types of merges:\n"
+"\n"
+"  To prevent unnecessary merge conflicts, svn merge requires that\n"
+"  TARGET_WCPATH is not a mixed-revision working copy. Running 'svn update'\n"
+"  before starting a merge ensures that all items in the working copy are\n"
+"  based on the same revision.\n"
+"\n"
+"  If possible, you should have no local modifications in the merge's target\n"
+"  working copy prior to the merge, to keep things simpler. It will be\n"
+"  easier to revert the merge and to understand the branch's history.\n"
+"\n"
+"  Switched sub-paths should also be avoided during merging, as they may\n"
+"  cause incomplete merges and create subtree mergeinfo.\n"
+"\n"
+"  For each merged item a line will be printed with characters reporting the\n"
+"  action taken. These characters have the following meaning:\n"
+"\n"
+"    A  Added\n"
+"    D  Deleted\n"
+"    U  Updated\n"
+"    C  Conflict\n"
+"    G  Merged\n"
+"    E  Existed\n"
+"    R  Replaced\n"
+"\n"
+"  Characters in the first column report about the item itself.\n"
+"  Characters in the second column report about properties of the item.\n"
+"  A 'C' in the third column indicates a tree conflict, while a 'C' in\n"
+"  the first and second columns indicate textual conflicts in files\n"
+"  and in property values, respectively.\n"
+"\n"
+"    - Merge Tracking -\n"
+"\n"
+"  Subversion uses the svn:mergeinfo property to track merge history. This\n"
+"  property is considered at the start of a merge to determine what to merge\n"
+"  and it is updated at the conclusion of the merge to describe the merge\n"
+"  that took place. Mergeinfo is used only if the two sources are on the\n"
+"  same line of history -- if the first source is an ancestor of the second,\n"
+"  or vice-versa (i.e. if one has originally been created by copying the\n"
+"  other). This is verified and enforced when using sync merges and\n"
+"  reintegrate merges.\n"
+"\n"
+"  The --ignore-ancestry option prevents merge tracking and thus ignores\n"
+"  mergeinfo, neither considering it nor recording it.\n"
+"\n"
+"    - Merging from foreign repositories -\n"
+"\n"
+"  Subversion does support merging from foreign repositories.\n"
+"  While all merge source URLs must point to the same repository, the merge\n"
+"  target working copy may come from a different repository than the source.\n"
+"  However, there are some caveats. Most notably, copies made in the\n"
+"  merge source will be transformed into plain additions in the merge\n"
+"  target. Also, merge-tracking is not supported for merges from foreign\n"
+"  repositories.\n"),
     {'r', 'c', 'N', opt_depth, 'q', opt_force, opt_dry_run, opt_merge_cmd,
      opt_record_only, 'x', opt_ignore_ancestry, opt_accept, opt_reintegrate,
      opt_allow_mixed_revisions} },
@@ -1200,7 +1253,7 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "     complete old and new URLs if you wish.)  Use 'svn info' to determine\n"
      "     the current working copy URL.\n"
      "\n"
-     "  2. TO-URL is the (complete) new repository URL to use for for PATH.\n"
+     "  2. TO-URL is the (complete) new repository URL to use for PATH.\n"
      "\n"
      "  Examples:\n"
      "    svn relocate http:// svn:// project1 project2\n"
@@ -1872,6 +1925,9 @@ main(int argc, const char *argv[])
       case opt_no_diff_deleted:
         opt_state.no_diff_deleted = TRUE;
         break;
+      case opt_ignore_props:
+        opt_state.ignore_props = TRUE;
+        break;
       case opt_show_copies_as_adds:
         opt_state.show_copies_as_adds = TRUE;
         break;
@@ -2052,6 +2108,9 @@ main(int argc, const char *argv[])
       case opt_internal_diff:
         opt_state.internal_diff = TRUE;
         break;
+      case opt_use_patch_diff_format:
+        opt_state.use_patch_diff_format = TRUE;
+        break;
       case opt_use_git_diff_format:
         opt_state.use_git_diff_format = TRUE;
         break;
@@ -2654,6 +2713,15 @@ main(int argc, const char *argv[])
                                      _("Please see the 'svn upgrade' command"));
         }
 
+      /* Tell the user about 'svn cleanup' if any error on the stack
+         was about locked working copies. */
+      if (svn_error_find_cause(err, SVN_ERR_WC_LOCKED))
+        {
+          err = svn_error_quick_wrap(
+                  err, _("Run 'svn cleanup' to remove locks "
+                         "(type 'svn help cleanup' for details)"));
+        }
+
       /* Issue #3014:
        * Don't print anything on broken pipes. The pipe was likely
        * closed by the process at the other end. We expect that
@@ -2664,14 +2732,6 @@ main(int argc, const char *argv[])
       if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
         svn_handle_error2(err, stderr, FALSE, "svn: ");
 
-      /* Tell the user about 'svn cleanup' if any error on the stack
-         was about locked working copies. */
-      if (svn_error_find_cause(err, SVN_ERR_WC_LOCKED))
-        svn_error_clear(svn_cmdline_fputs(_("svn: run 'svn cleanup' to "
-                                            "remove locks (type 'svn help "
-                                            "cleanup' for details)\n"),
-                                          stderr, pool));
-
       svn_error_clear(err);
       svn_pool_destroy(pool);
       return EXIT_FAILURE;

Modified: subversion/branches/svn-bisect/subversion/svn/merge-cmd.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/svn/merge-cmd.c?rev=1367002&r1=1367001&r2=1367002&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/svn/merge-cmd.c (original)
+++ subversion/branches/svn-bisect/subversion/svn/merge-cmd.c Mon Jul 30 06:39:28 2012
@@ -39,6 +39,63 @@
 
 /*** Code. ***/
 
+/* Do a reintegrate merge from SOURCE_PATH_OR_URL@SOURCE_PEG_REVISION into
+ * TARGET_WCPATH.  Do it with a WC write lock unless DRY_RUN is true. */
+static svn_error_t *
+merge_reintegrate(const char *source_path_or_url,
+                  const svn_opt_revision_t *source_peg_revision,
+                  const char *target_wcpath,
+                  svn_boolean_t dry_run,
+                  const apr_array_header_t *merge_options,
+                  svn_client_ctx_t *ctx,
+                  apr_pool_t *scratch_pool)
+{
+  const char *url1, *url2;
+  svn_revnum_t rev1, rev2;
+
+  SVN_ERR(svn_client_find_reintegrate_merge(
+            &url1, &rev1, &url2, &rev2,
+            source_path_or_url, source_peg_revision, target_wcpath,
+            ctx, scratch_pool, scratch_pool));
+
+  if (url1)
+    {
+      svn_opt_revision_t revision1 = { svn_opt_revision_number, { rev1 } };
+      svn_opt_revision_t revision2 = { svn_opt_revision_number, { rev2 } };
+
+      /* Do the merge.  Set 'allow_mixed_rev' to true, not because we want
+       * to allow a mixed-rev WC but simply to bypass the check, as it was
+       * already checked in svn_client_find_reintegrate_merge(). */
+      SVN_ERR(svn_client_merge4(url1, &revision1, url2, &revision2,
+                                target_wcpath, svn_depth_infinity,
+                                FALSE /* ignore_ancestry */,
+                                FALSE /* force */,
+                                FALSE /* record_only */,
+                                dry_run, TRUE /* allow_mixed_rev */,
+                                merge_options, ctx, scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Throw an error if PATH_OR_URL is a path and REVISION isn't a repository
+ * revision. */
+static svn_error_t *
+ensure_wc_path_has_repo_revision(const char *path_or_url,
+                                 const svn_opt_revision_t *revision,
+                                 apr_pool_t *scratch_pool)
+{
+  if (revision->kind != svn_opt_revision_number
+      && revision->kind != svn_opt_revision_date
+      && revision->kind != svn_opt_revision_head
+      && ! svn_path_is_url(path_or_url))
+    return svn_error_createf(
+      SVN_ERR_CLIENT_BAD_REVISION, NULL,
+      _("Invalid merge source '%s'; a working copy path can only be "
+        "used with a repository revision (a number, a date, or head)"),
+      svn_dirent_local_style(path_or_url, scratch_pool));
+  return SVN_NO_ERROR;
+}
 
 /* This implements the `svn_opt_subcommand_t' interface. */
 svn_error_t *
@@ -55,6 +112,7 @@ svn_cl__merge(apr_getopt_t *os,
   svn_opt_revision_t first_range_start, first_range_end, peg_revision1,
     peg_revision2;
   apr_array_header_t *options, *ranges_to_merge = opt_state->revision_ranges;
+  svn_opt_revision_t unspecified = { svn_opt_revision_unspecified, { 0 } };
 
   /* Merge doesn't support specifying a revision or revision range
      when using --reintegrate. */
@@ -205,16 +263,13 @@ svn_cl__merge(apr_getopt_t *os,
 
       /* Catch 'svn merge wc_path1 wc_path2 [target]' without explicit
          revisions--since it ignores local modifications it may not do what
-         the user expects.  Forcing the user to specify a repository
+         the user expects.  That is, it doesn't read from the WC itself, it
+         reads from the WC's URL.  Forcing the user to specify a repository
          revision should avoid any confusion. */
-      if ((first_range_start.kind == svn_opt_revision_unspecified
-           && ! svn_path_is_url(sourcepath1))
-          ||
-          (first_range_end.kind == svn_opt_revision_unspecified
-           && ! svn_path_is_url(sourcepath2)))
-        return svn_error_create
-          (SVN_ERR_CLIENT_BAD_REVISION, 0,
-           _("A working copy merge source needs an explicit revision"));
+      SVN_ERR(ensure_wc_path_has_repo_revision(sourcepath1, &first_range_start,
+                                               pool));
+      SVN_ERR(ensure_wc_path_has_repo_revision(sourcepath2, &first_range_end,
+                                               pool));
 
       /* Default peg revisions to each URL's youngest revision. */
       if (first_range_start.kind == svn_opt_revision_unspecified)
@@ -254,11 +309,11 @@ svn_cl__merge(apr_getopt_t *os,
       else if (strcmp(sourcepath1, sourcepath2) == 0)
         {
           svn_node_kind_t kind;
-          const char *decoded_path = svn_path_uri_decode(sourcepath1, pool);
-          SVN_ERR(svn_io_check_path(decoded_path, &kind, pool));
+
+          SVN_ERR(svn_io_check_path(sourcepath1, &kind, pool));
           if (kind == svn_node_file)
             {
-              targetpath = decoded_path;
+              targetpath = sourcepath1;
             }
         }
     }
@@ -293,11 +348,13 @@ svn_cl__merge(apr_getopt_t *os,
 
   if (opt_state->reintegrate)
     {
-      err = svn_client_merge_reintegrate(sourcepath1,
-                                         &peg_revision1,
-                                         targetpath,
-                                         opt_state->dry_run,
-                                         options, ctx, pool);
+      SVN_ERR_W(svn_cl__check_related_source_and_target(
+                  sourcepath1, &peg_revision1, targetpath, &unspecified,
+                  ctx, pool),
+                _("Source and target must be different but related branches"));
+
+      err = merge_reintegrate(sourcepath1, &peg_revision1, targetpath,
+                              opt_state->dry_run, options, ctx, pool);
     }
   else if (! two_sources_specified)
     {
@@ -313,6 +370,12 @@ svn_cl__merge(apr_getopt_t *os,
           range->start.value.number = 1;
           range->end = peg_revision1;
           APR_ARRAY_PUSH(ranges_to_merge, svn_opt_revision_range_t *) = range;
+
+          /* This must be a 'sync' merge so check branch relationship. */
+          SVN_ERR_W(svn_cl__check_related_source_and_target(
+                      sourcepath1, &peg_revision1, targetpath, &unspecified,
+                      ctx, pool),
+                _("Source and target must be different but related branches"));
         }
 
       err = svn_client_merge_peg4(sourcepath1,

Modified: subversion/branches/svn-bisect/subversion/svn/mergeinfo-cmd.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/svn/mergeinfo-cmd.c?rev=1367002&r1=1367001&r2=1367002&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/svn/mergeinfo-cmd.c (original)
+++ subversion/branches/svn-bisect/subversion/svn/mergeinfo-cmd.c Mon Jul 30 06:39:28 2012
@@ -114,6 +114,11 @@ svn_cl__mergeinfo(apr_getopt_t *os,
         tgt_peg_revision.kind = svn_opt_revision_base;
     }
 
+  SVN_ERR_W(svn_cl__check_related_source_and_target(source, &src_peg_revision,
+                                                    target, &tgt_peg_revision,
+                                                    ctx, pool),
+            _("Source and target must be different but related branches"));
+
   /* Do the real work, depending on the requested data flavor. */
   if (opt_state->show_revs == svn_cl__show_revs_merged)
     {

Modified: subversion/branches/svn-bisect/subversion/svn/propedit-cmd.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/svn/propedit-cmd.c?rev=1367002&r1=1367001&r2=1367002&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/svn/propedit-cmd.c (original)
+++ subversion/branches/svn-bisect/subversion/svn/propedit-cmd.c Mon Jul 30 06:39:28 2012
@@ -39,8 +39,6 @@
 #include "svn_props.h"
 #include "cl.h"
 
-#include "private/svn_wc_private.h"
-
 #include "svn_private_config.h"
 
 

Modified: subversion/branches/svn-bisect/subversion/svn/status.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/svn/status.c?rev=1367002&r1=1367001&r2=1367002&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/svn/status.c (original)
+++ subversion/branches/svn-bisect/subversion/svn/status.c Mon Jul 30 06:39:28 2012
@@ -432,7 +432,8 @@ svn_cl__print_status_xml(const char *cwd
   const char *local_abspath = status->local_abspath;
   svn_boolean_t tree_conflicted = FALSE;
 
-  if (status->node_status == svn_wc_status_none)
+  if (status->node_status == svn_wc_status_none
+      && status->repos_node_status == svn_wc_status_none)
     return SVN_NO_ERROR;
 
   if (status->conflicted)

Modified: subversion/branches/svn-bisect/subversion/svn/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/svn/util.c?rev=1367002&r1=1367001&r2=1367002&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/svn/util.c (original)
+++ subversion/branches/svn-bisect/subversion/svn/util.c Mon Jul 30 06:39:28 2012
@@ -947,7 +947,7 @@ svn_cl__try(svn_error_t *err,
       va_list ap;
 
       va_start(ap, quiet);
-      while ((apr_err = va_arg(ap, apr_status_t)) != SVN_NO_ERROR)
+      while ((apr_err = va_arg(ap, apr_status_t)) != APR_SUCCESS)
         {
           if (errors_seen)
             {
@@ -1423,3 +1423,43 @@ svn_cl__local_style_skip_ancestor(const 
 
   return svn_dirent_local_style(relpath ? relpath : path, pool);
 }
+
+/* Return a string of the form "PATH_OR_URL@REVISION". */
+static const char *
+path_for_display(const char *path_or_url,
+                 const svn_opt_revision_t *revision,
+                 apr_pool_t *pool)
+{
+  const char *rev_str = svn_opt__revision_to_string(revision, pool);
+
+  if (! svn_path_is_url(path_or_url))
+    path_or_url = svn_dirent_local_style(path_or_url, pool);
+  return apr_psprintf(pool, "%s@%s", path_or_url, rev_str);
+}
+
+svn_error_t *
+svn_cl__check_related_source_and_target(const char *path_or_url1,
+                                        const svn_opt_revision_t *revision1,
+                                        const char *path_or_url2,
+                                        const svn_opt_revision_t *revision2,
+                                        svn_client_ctx_t *ctx,
+                                        apr_pool_t *pool)
+{
+  const char *ancestor_url;
+  svn_revnum_t ancestor_rev;
+
+  SVN_ERR(svn_client__youngest_common_ancestor(
+            &ancestor_url, &ancestor_rev,
+            path_or_url1, revision1, path_or_url2, revision2,
+            ctx, pool, pool));
+
+  if (ancestor_url == NULL)
+    {
+      return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                               _("Source and target have no common ancestor: "
+                                 "'%s' and '%s'"),
+                               path_for_display(path_or_url1, revision1, pool),
+                               path_for_display(path_or_url2, revision2, pool));
+    }
+  return SVN_NO_ERROR;
+}

Modified: subversion/branches/svn-bisect/subversion/svnadmin/main.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/svnadmin/main.c?rev=1367002&r1=1367001&r2=1367002&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/svnadmin/main.c (original)
+++ subversion/branches/svn-bisect/subversion/svnadmin/main.c Mon Jul 30 06:39:28 2012
@@ -40,6 +40,7 @@
 #include "svn_props.h"
 #include "svn_time.h"
 #include "svn_user.h"
+#include "svn_xml.h"
 
 #include "private/svn_opt_private.h"
 
@@ -152,6 +153,7 @@ static svn_opt_subcommand_t
   subcommand_load,
   subcommand_list_dblogs,
   subcommand_list_unused_dblogs,
+  subcommand_lock,
   subcommand_lslocks,
   subcommand_lstxns,
   subcommand_pack,
@@ -161,6 +163,7 @@ static svn_opt_subcommand_t
   subcommand_setlog,
   subcommand_setrevprop,
   subcommand_setuuid,
+  subcommand_unlock,
   subcommand_upgrade,
   subcommand_verify;
 
@@ -208,7 +211,7 @@ static const apr_getopt_option_t options
      N_("specify revision number ARG (or X:Y range)")},
 
     {"incremental",   svnadmin__incremental, 0,
-     N_("dump incrementally")},
+     N_("dump or hotcopy incrementally")},
 
     {"deltas",        svnadmin__deltas, 0,
      N_("use deltas in dump output")},
@@ -332,8 +335,10 @@ static const svn_opt_subcommand_desc2_t 
 
   {"hotcopy", subcommand_hotcopy, {0}, N_
    ("usage: svnadmin hotcopy REPOS_PATH NEW_REPOS_PATH\n\n"
-    "Makes a hot copy of a repository.\n"),
-   {svnadmin__clean_logs} },
+    "Makes a hot copy of a repository.\n"
+    "If --incremental is passed, data which already exists at the destination\n"
+    "is not copied again.  Incremental mode is implemented for FSFS repositories.\n"),
+   {svnadmin__clean_logs, svnadmin__incremental} },
 
   {"list-dblogs", subcommand_list_dblogs, {0}, N_
    ("usage: svnadmin list-dblogs REPOS_PATH\n\n"
@@ -358,6 +363,13 @@ static const svn_opt_subcommand_desc2_t 
    {'q', 'r', svnadmin__ignore_uuid, svnadmin__force_uuid,
     svnadmin__use_pre_commit_hook, svnadmin__use_post_commit_hook,
     svnadmin__parent_dir, svnadmin__bypass_prop_validation, 'M'} },
+  
+  {"lock", subcommand_lock, {0}, N_
+   ("usage: svnadmin lock REPOS_PATH PATH USERNAME COMMENT-FILE [TOKEN]\n\n"
+    "Lock PATH by USERNAME setting comments from COMMENT-FILE.\n"
+    "If provided, use TOKEN as lock token.  Use --bypass-hooks to avoid\n"
+    "triggering the pre-lock and post-lock hook scripts.\n"),
+  {svnadmin__bypass_hooks} },
 
   {"lslocks", subcommand_lslocks, {0}, N_
    ("usage: svnadmin lslocks REPOS_PATH [PATH-IN-REPOS]\n\n"
@@ -424,6 +436,13 @@ static const svn_opt_subcommand_desc2_t 
     "generate a brand new UUID for the repository.\n"),
    {0} },
 
+  {"unlock", subcommand_unlock, {0}, N_
+   ("usage: svnadmin unlock REPOS_PATH LOCKED_PATH USERNAME TOKEN\n\n"
+    "Unlocked LOCKED_PATH (as USERNAME) after verifying that the token\n"
+    "associated with the lock matches TOKEN.  Use --bypass-hooks to avoid\n"
+    "triggering the pre-unlock and post-unlock hook scripts.\n"),
+   {svnadmin__bypass_hooks} },
+
   {"upgrade", subcommand_upgrade, {0}, N_
    ("usage: svnadmin upgrade REPOS_PATH\n\n"
     "Upgrade the repository located at REPOS_PATH to the latest supported\n"
@@ -1431,10 +1450,74 @@ subcommand_hotcopy(apr_getopt_t *os, voi
   new_repos_path = APR_ARRAY_IDX(targets, 0, const char *);
   SVN_ERR(target_arg_to_dirent(&new_repos_path, new_repos_path, pool));
 
-  return svn_repos_hotcopy(opt_state->repository_path, new_repos_path,
-                           opt_state->clean_logs, pool);
+  return svn_repos_hotcopy2(opt_state->repository_path, new_repos_path,
+                            opt_state->clean_logs, opt_state->incremental,
+                            check_cancel, NULL, pool);
 }
 
+/* This implements `svn_opt_subcommand_t'. */
+static svn_error_t *
+subcommand_lock(apr_getopt_t *os, void *baton, apr_pool_t *pool)
+{
+  struct svnadmin_opt_state *opt_state = baton;
+  svn_repos_t *repos;
+  svn_fs_t *fs;
+  svn_fs_access_t *access;
+  apr_array_header_t *args;
+  const char *username;
+  const char *lock_path;
+  const char *comment_file_name;
+  svn_stringbuf_t *file_contents;
+  const char *lock_path_utf8;
+  svn_lock_t *lock;
+  const char *lock_token = NULL;
+
+  /* Expect three more arguments: PATH USERNAME COMMENT-FILE */
+  SVN_ERR(parse_args(&args, os, 3, 4, pool));
+  lock_path = APR_ARRAY_IDX(args, 0, const char *);
+  username = APR_ARRAY_IDX(args, 1, const char *);
+  comment_file_name = APR_ARRAY_IDX(args, 2, const char *);
+
+  /* Expect one more optional argument: TOKEN */
+  if (args->nelts == 4)
+    lock_token = APR_ARRAY_IDX(args, 3, const char *);
+
+  SVN_ERR(target_arg_to_dirent(&comment_file_name, comment_file_name, pool));
+  
+  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
+  fs = svn_repos_fs(repos);
+
+  /* Create an access context describing the user. */
+  SVN_ERR(svn_fs_create_access(&access, username, pool));
+
+  /* Attach the access context to the filesystem. */
+  SVN_ERR(svn_fs_set_access(fs, access));
+
+  SVN_ERR(svn_stringbuf_from_file2(&file_contents, comment_file_name, pool));
+
+  SVN_ERR(svn_utf_cstring_to_utf8(&lock_path_utf8, lock_path, pool));
+  
+  if (opt_state->bypass_hooks)
+    SVN_ERR(svn_fs_lock(&lock, fs, lock_path_utf8,
+                        lock_token,          
+                        file_contents->data, /* comment */
+                        0,                   /* is_dav_comment */
+                        0,                   /* no expiration time. */
+                        SVN_INVALID_REVNUM,
+                        FALSE, pool));
+  else
+    SVN_ERR(svn_repos_fs_lock(&lock, repos, lock_path_utf8,
+                              lock_token,          
+                              file_contents->data, /* comment */
+                              0,                   /* is_dav_comment */
+                              0,                   /* no expiration time. */
+                              SVN_INVALID_REVNUM,
+                              FALSE, pool));
+
+  SVN_ERR(svn_cmdline_printf(pool, _("'%s' locked by user '%s'.\n"),
+                             lock_path, username));
+  return SVN_NO_ERROR;
+}
 
 static svn_error_t *
 subcommand_lslocks(apr_getopt_t *os, void *baton, apr_pool_t *pool)
@@ -1579,6 +1662,47 @@ subcommand_rmlocks(apr_getopt_t *os, voi
 
 /* This implements `svn_opt_subcommand_t'. */
 static svn_error_t *
+subcommand_unlock(apr_getopt_t *os, void *baton, apr_pool_t *pool)
+{
+  struct svnadmin_opt_state *opt_state = baton;
+  svn_repos_t *repos;
+  svn_fs_t *fs;
+  svn_fs_access_t *access;
+  apr_array_header_t *args;
+  const char *username;
+  const char *lock_path;
+  const char *lock_path_utf8;
+  const char *lock_token = NULL;
+
+  /* Expect three more arguments: PATH USERNAME TOKEN */
+  SVN_ERR(parse_args(&args, os, 3, 3, pool));
+  lock_path = APR_ARRAY_IDX(args, 0, const char *);
+  username = APR_ARRAY_IDX(args, 1, const char *);
+  lock_token = APR_ARRAY_IDX(args, 2, const char *);
+
+  /* Open the repos/FS, and associate an access context containing
+     USERNAME. */
+  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
+  fs = svn_repos_fs(repos);
+  SVN_ERR(svn_fs_create_access(&access, username, pool));
+  SVN_ERR(svn_fs_set_access(fs, access));
+
+  SVN_ERR(svn_utf_cstring_to_utf8(&lock_path_utf8, lock_path, pool));
+  if (opt_state->bypass_hooks)
+    SVN_ERR(svn_fs_unlock(fs, lock_path_utf8, lock_token,
+                          FALSE, pool));
+  else
+    SVN_ERR(svn_repos_fs_unlock(repos, lock_path_utf8, lock_token,
+                                FALSE, pool));
+
+  SVN_ERR(svn_cmdline_printf(pool, _("'%s' unlocked by user '%s'.\n"),
+                             lock_path, username));
+  return SVN_NO_ERROR;
+}
+
+
+/* This implements `svn_opt_subcommand_t'. */
+static svn_error_t *
 subcommand_upgrade(apr_getopt_t *os, void *baton, apr_pool_t *pool)
 {
   svn_error_t *err;

Modified: subversion/branches/svn-bisect/subversion/svndumpfilter/main.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/svndumpfilter/main.c?rev=1367002&r1=1367001&r2=1367002&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/svndumpfilter/main.c (original)
+++ subversion/branches/svn-bisect/subversion/svndumpfilter/main.c Mon Jul 30 06:39:28 2012
@@ -110,7 +110,10 @@ write_prop_to_stringbuf(svn_stringbuf_t 
 }
 
 
-/* Prefix matching function to compare node-path with set of prefixes. */
+/* Compare the node-path PATH with the (const char *) prefixes in PFXLIST.
+ * Return TRUE if any prefix is a prefix of PATH (matching whole path
+ * components); FALSE otherwise.
+ * PATH starts with a '/', as do the (const char *) paths in PREFIXES. */
 static svn_boolean_t
 ary_prefix_match(const apr_array_header_t *pfxlist, const char *path)
 {
@@ -125,7 +128,7 @@ ary_prefix_match(const apr_array_header_
       if (path_len < pfx_len)
         continue;
       if (strncmp(path, pfx, pfx_len) == 0
-          && (path[pfx_len] == '\0' || path[pfx_len] == '/'))
+          && (pfx_len == 1 || path[pfx_len] == '\0' || path[pfx_len] == '/'))
         return TRUE;
     }
 
@@ -134,7 +137,8 @@ ary_prefix_match(const apr_array_header_
 
 
 /* Check whether we need to skip this PATH based on its presence in
-   the PREFIXES list, and the DO_EXCLUDE option. */
+   the PREFIXES list, and the DO_EXCLUDE option.
+   PATH starts with a '/', as do the (const char *) paths in PREFIXES. */
 static APR_INLINE svn_boolean_t
 skip_path(const char *path, const apr_array_header_t *prefixes,
           svn_boolean_t do_exclude, svn_boolean_t glob)

Modified: subversion/branches/svn-bisect/subversion/svnlook/main.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/svnlook/main.c?rev=1367002&r1=1367001&r2=1367002&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/svnlook/main.c (original)
+++ subversion/branches/svn-bisect/subversion/svnlook/main.c Mon Jul 30 06:39:28 2012
@@ -784,18 +784,25 @@ generate_label(const char **label,
    If TOKEN is empty, or is already terminated by an EOL marker,
    return TOKEN unmodified. Else, return a new string consisting
    of the concatenation of TOKEN and the system's default EOL marker.
-   The new string is allocated from POOL. */
+   The new string is allocated from POOL.
+   If HAD_EOL is not NULL, indicate in *HAD_EOL if the token had a EOL. */
 static const svn_string_t *
-maybe_append_eol(const svn_string_t *token, apr_pool_t *pool)
+maybe_append_eol(const svn_string_t *token, svn_boolean_t *had_eol,
+                 apr_pool_t *pool)
 {
   const char *curp;
 
+  if (had_eol)
+    *had_eol = FALSE;
+
   if (token->len == 0)
     return token;
 
   curp = token->data + token->len - 1;
   if (*curp == '\r')
     {
+      if (had_eol)
+        *had_eol = TRUE;
       return token;
     }
   else if (*curp != '\n')
@@ -804,6 +811,8 @@ maybe_append_eol(const svn_string_t *tok
     }
   else
     {
+      if (had_eol)
+        *had_eol = TRUE;
       return token;
     }
 }
@@ -860,19 +869,20 @@ display_prop_diffs(const apr_array_heade
         const svn_string_t *tmp;
         const svn_string_t *orig;
         const svn_string_t *val;
+        svn_boolean_t val_has_eol;
 
         SVN_ERR(svn_stream_for_stdout(&out, pool));
 
         /* The last character in a property is often not a newline.
-           Since the diff is not useful anyway for patching properties an
-           eol character is appended when needed to remove those pescious
-           ' \ No newline at end of file' lines. */
+           An eol character is appended to prevent the diff API to add a
+           ' \ No newline at end of file' line. We add 
+           ' \ No newline at end of property' manually if needed. */
         tmp = orig_value ? orig_value : svn_string_create_empty(pool);
-        orig = maybe_append_eol(tmp, pool);
+        orig = maybe_append_eol(tmp, NULL, pool);
 
         tmp = pc->value ? pc->value :
                                   svn_string_create_empty(pool);
-        val = maybe_append_eol(tmp, pool);
+        val = maybe_append_eol(tmp, &val_has_eol, pool);
 
         SVN_ERR(svn_diff_mem_string_diff(&diff, orig, val, &options, pool));
 
@@ -888,6 +898,12 @@ display_prop_diffs(const apr_array_heade
                                            svn_dirent_local_style(path, pool),
                                            svn_cmdline_output_encoding(pool),
                                            orig, val, pool));
+        if (!val_has_eol)
+          {
+            const char *s = "\\ No newline at end of property" APR_EOL_STR;
+            apr_size_t len = strlen(s);
+            SVN_ERR(svn_stream_write(out, s, &len));
+          }
       }
     }
   return svn_cmdline_fflush(stdout);

Modified: subversion/branches/svn-bisect/subversion/svnrdump/dump_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/svnrdump/dump_editor.c?rev=1367002&r1=1367001&r2=1367002&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/svnrdump/dump_editor.c (original)
+++ subversion/branches/svn-bisect/subversion/svnrdump/dump_editor.c Mon Jul 30 06:39:28 2012
@@ -81,6 +81,9 @@ struct dump_edit_baton {
   /* The output stream we write the dumpfile to */
   svn_stream_t *stream;
 
+  /* A backdoor ra session to fetch additional information during the edit. */
+  svn_ra_session_t *ra_session;
+
   /* Pool for per-revision allocations */
   apr_pool_t *pool;
 
@@ -109,6 +112,9 @@ struct dump_edit_baton {
   svn_boolean_t dump_text;
   svn_boolean_t dump_props;
   svn_boolean_t dump_newlines;
+
+  /* The revision we're currently dumping. */
+  svn_revnum_t current_revision;
 };
 
 /* Make a directory baton to represent the directory at PATH (relative
@@ -163,58 +169,90 @@ make_dir_baton(const char *path,
   return new_db;
 }
 
+/* Return in *HEADER and *CONTENT the headers and content for PROPS. */
+static svn_error_t *
+get_props_content(svn_stringbuf_t **header,
+                  svn_stringbuf_t **content,
+                  apr_hash_t *props,
+                  apr_hash_t *deleted_props,
+                  apr_pool_t *result_pool,
+                  apr_pool_t *scratch_pool)
+{
+  svn_stream_t *content_stream;
+  apr_hash_t *normal_props;
+  const char *buf;
+  
+  *content = svn_stringbuf_create_empty(result_pool);
+  *header = svn_stringbuf_create_empty(result_pool);
+
+  content_stream = svn_stream_from_stringbuf(*content, scratch_pool);
+
+  SVN_ERR(svn_rdump__normalize_props(&normal_props, props, scratch_pool));
+  SVN_ERR(svn_hash_write_incremental(normal_props, deleted_props,
+                                     content_stream, "PROPS-END",
+                                     scratch_pool));
+  SVN_ERR(svn_stream_close(content_stream));
+
+  /* Prop-delta: true */
+  *header = svn_stringbuf_createf(result_pool, SVN_REPOS_DUMPFILE_PROP_DELTA
+                                  ": true\n");
+
+  /* Prop-content-length: 193 */
+  buf = apr_psprintf(scratch_pool, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
+                     ": %" APR_SIZE_T_FMT "\n", (*content)->len);
+  svn_stringbuf_appendcstr(*header, buf);
+
+  return SVN_NO_ERROR;
+}
+
 /* Extract and dump properties stored in edit baton EB, using POOL for
  * any temporary allocations. If TRIGGER_VAR is not NULL, it is set to FALSE.
  * Unless DUMP_DATA_TOO is set, only property headers are dumped.
  */
 static svn_error_t *
-do_dump_props(struct dump_edit_baton *eb,
+do_dump_props(svn_stringbuf_t **propstring,
+              svn_stream_t *stream,
+              apr_hash_t *props,
+              apr_hash_t *deleted_props,
               svn_boolean_t *trigger_var,
               svn_boolean_t dump_data_too,
-              apr_pool_t *pool)
+              apr_pool_t *result_pool,
+              apr_pool_t *scratch_pool)
 {
-  svn_stream_t *propstream;
-  apr_hash_t *normal_props;
+  svn_stringbuf_t *header;
+  svn_stringbuf_t *content;
+  apr_size_t len;
 
   if (trigger_var && !*trigger_var)
     return SVN_NO_ERROR;
 
-  SVN_ERR(svn_rdump__normalize_props(&normal_props, eb->props, eb->pool));
-  svn_stringbuf_setempty(eb->propstring);
-  propstream = svn_stream_from_stringbuf(eb->propstring, eb->pool);
-  SVN_ERR(svn_hash_write_incremental(normal_props, eb->deleted_props,
-                                     propstream, "PROPS-END", pool));
-  SVN_ERR(svn_stream_close(propstream));
+  SVN_ERR(get_props_content(&header, &content, props, deleted_props,
+                            result_pool, scratch_pool));
 
-  /* Prop-delta: true */
-  SVN_ERR(svn_stream_printf(eb->stream, pool,
-                            SVN_REPOS_DUMPFILE_PROP_DELTA
-                            ": true\n"));
+  /* This is a wacky side-effect of this function. */
+  *propstring = content;
 
-  /* Prop-content-length: 193 */
-  SVN_ERR(svn_stream_printf(eb->stream, pool,
-                            SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
-                            ": %" APR_SIZE_T_FMT "\n", eb->propstring->len));
+  len = header->len;
+  SVN_ERR(svn_stream_write(stream, header->data, &len));
 
   if (dump_data_too)
     {
       /* Content-length: 14 */
-      SVN_ERR(svn_stream_printf(eb->stream, pool,
+      SVN_ERR(svn_stream_printf(stream, scratch_pool,
                                 SVN_REPOS_DUMPFILE_CONTENT_LENGTH
                                 ": %" APR_SIZE_T_FMT "\n\n",
-                                eb->propstring->len));
+                                content->len));
 
-      /* The properties. */
-      SVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,
-                               &(eb->propstring->len)));
+      len = content->len;
+      SVN_ERR(svn_stream_write(stream, content->data, &len));
 
       /* No text is going to be dumped. Write a couple of newlines and
          wait for the next node/ revision. */
-      SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
+      SVN_ERR(svn_stream_printf(stream, scratch_pool, "\n\n"));
 
       /* Cleanup so that data is never dumped twice. */
-      SVN_ERR(svn_hash__clear(eb->props, eb->pool));
-      SVN_ERR(svn_hash__clear(eb->deleted_props, eb->pool));
+      SVN_ERR(svn_hash__clear(props, scratch_pool));
+      SVN_ERR(svn_hash__clear(deleted_props, scratch_pool));
       if (trigger_var)
         *trigger_var = FALSE;
     }
@@ -403,7 +441,9 @@ delete_entry(const char *path,
   LDR_DBG(("delete_entry %s\n", path));
 
   /* Some pending properties to dump? */
-  SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
+  SVN_ERR(do_dump_props(&pb->eb->propstring, pb->eb->stream,
+                        pb->eb->props, pb->eb->deleted_props,
+                        &(pb->eb->dump_props), TRUE, pool, pool));
 
   /* Some pending newlines to dump? */
   SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
@@ -435,7 +475,9 @@ add_directory(const char *path,
                           pb, TRUE, pb->eb->pool);
 
   /* Some pending properties to dump? */
-  SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
+  SVN_ERR(do_dump_props(&pb->eb->propstring, pb->eb->stream,
+                        pb->eb->props, pb->eb->deleted_props,
+                        &(pb->eb->dump_props), TRUE, pool, pool));
 
   /* Some pending newlines to dump? */
   SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
@@ -480,7 +522,9 @@ open_directory(const char *path,
   LDR_DBG(("open_directory %s\n", path));
 
   /* Some pending properties to dump? */
-  SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
+  SVN_ERR(do_dump_props(&pb->eb->propstring, pb->eb->stream,
+                        pb->eb->props, pb->eb->deleted_props,
+                        &(pb->eb->dump_props), TRUE, pool, pool));
 
   /* Some pending newlines to dump? */
   SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
@@ -512,7 +556,9 @@ close_directory(void *dir_baton,
   LDR_DBG(("close_directory %p\n", dir_baton));
 
   /* Some pending properties to dump? */
-  SVN_ERR(do_dump_props(eb, &(eb->dump_props), TRUE, pool));
+  SVN_ERR(do_dump_props(&eb->propstring, eb->stream,
+                        eb->props, eb->deleted_props,
+                        &(eb->dump_props), TRUE, pool, pool));
 
   /* Some pending newlines to dump? */
   SVN_ERR(do_dump_newlines(eb, &(eb->dump_newlines), pool));
@@ -546,7 +592,9 @@ add_file(const char *path,
   LDR_DBG(("add_file %s\n", path));
 
   /* Some pending properties to dump? */
-  SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
+  SVN_ERR(do_dump_props(&pb->eb->propstring, pb->eb->stream,
+                        pb->eb->props, pb->eb->deleted_props,
+                        &(pb->eb->dump_props), TRUE, pool, pool));
 
   /* Some pending newlines to dump? */
   SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
@@ -591,7 +639,9 @@ open_file(const char *path,
   LDR_DBG(("open_file %s\n", path));
 
   /* Some pending properties to dump? */
-  SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
+  SVN_ERR(do_dump_props(&pb->eb->propstring, pb->eb->stream,
+                        pb->eb->props, pb->eb->deleted_props,
+                        &(pb->eb->dump_props), TRUE, pool, pool));
 
   /* Some pending newlines to dump? */
   SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
@@ -751,7 +801,9 @@ close_file(void *file_baton,
 
   /* Some pending properties to dump? Dump just the headers- dump the
      props only after dumping the text headers too (if present) */
-  SVN_ERR(do_dump_props(eb, &(eb->dump_props), FALSE, pool));
+  SVN_ERR(do_dump_props(&eb->propstring, eb->stream,
+                        eb->props, eb->deleted_props,
+                        &(eb->dump_props), FALSE, pool, pool));
 
   /* Dump the text headers */
   if (eb->dump_text)
@@ -846,10 +898,120 @@ close_edit(void *edit_baton, apr_pool_t 
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+fetch_base_func(const char **filename,
+                void *baton,
+                const char *path,
+                svn_revnum_t base_revision,
+                apr_pool_t *result_pool,
+                apr_pool_t *scratch_pool)
+{
+  struct dump_edit_baton *eb = baton;
+  svn_stream_t *fstream;
+  svn_error_t *err;
+
+  if (path[0] == '/')
+    path += 1;
+
+  if (! SVN_IS_VALID_REVNUM(base_revision))
+    base_revision = eb->current_revision - 1;
+
+  SVN_ERR(svn_stream_open_unique(&fstream, filename, NULL,
+                                 svn_io_file_del_on_pool_cleanup,
+                                 result_pool, scratch_pool));
+
+  err = svn_ra_get_file(eb->ra_session, path, base_revision,
+                        fstream, NULL, NULL, scratch_pool);
+  if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
+    {
+      svn_error_clear(err);
+      SVN_ERR(svn_stream_close(fstream));
+
+      *filename = NULL;
+      return SVN_NO_ERROR;
+    }
+  else if (err)
+    return svn_error_trace(err);
+  
+  SVN_ERR(svn_stream_close(fstream));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+fetch_props_func(apr_hash_t **props,
+                 void *baton,
+                 const char *path,
+                 svn_revnum_t base_revision,
+                 apr_pool_t *result_pool,
+                 apr_pool_t *scratch_pool)
+{
+  struct dump_edit_baton *eb = baton;
+  svn_node_kind_t node_kind;
+
+  if (path[0] == '/')
+    path += 1;
+
+  if (! SVN_IS_VALID_REVNUM(base_revision))
+    base_revision = eb->current_revision - 1;
+
+  SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, &node_kind,
+                            scratch_pool));
+
+  if (node_kind == svn_node_file)
+    {
+      SVN_ERR(svn_ra_get_file(eb->ra_session, path, base_revision,
+                              NULL, NULL, props, result_pool));
+    }
+  else if (node_kind == svn_node_dir)
+    {
+      apr_array_header_t *tmp_props;
+
+      SVN_ERR(svn_ra_get_dir2(eb->ra_session, NULL, NULL, props, path,
+                              base_revision, 0 /* Dirent fields */,
+                              result_pool));
+      tmp_props = svn_prop_hash_to_array(*props, result_pool);
+      SVN_ERR(svn_categorize_props(tmp_props, NULL, NULL, &tmp_props,
+                                   result_pool));
+      *props = svn_prop_array_to_hash(tmp_props, result_pool);
+    }
+  else
+    {
+      *props = apr_hash_make(result_pool);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+fetch_kind_func(svn_kind_t *kind,
+                void *baton,
+                const char *path,
+                svn_revnum_t base_revision,
+                apr_pool_t *scratch_pool)
+{
+  struct dump_edit_baton *eb = baton;
+  svn_node_kind_t node_kind;
+
+  if (path[0] == '/')
+    path += 1;
+
+  if (! SVN_IS_VALID_REVNUM(base_revision))
+    base_revision = eb->current_revision - 1;
+
+  SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, &node_kind,
+                            scratch_pool));
+
+  *kind = svn__kind_from_node_kind(node_kind, FALSE);
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_rdump__get_dump_editor(const svn_delta_editor_t **editor,
                            void **edit_baton,
+                           svn_revnum_t revision,
                            svn_stream_t *stream,
+                           svn_ra_session_t *ra_session,
                            svn_cancel_func_t cancel_func,
                            void *cancel_baton,
                            apr_pool_t *pool)
@@ -861,6 +1023,8 @@ svn_rdump__get_dump_editor(const svn_del
 
   eb = apr_pcalloc(pool, sizeof(struct dump_edit_baton));
   eb->stream = stream;
+  eb->ra_session = ra_session;
+  eb->current_revision = revision;
 
   /* Create a special per-revision pool */
   eb->pool = svn_pool_create(pool);
@@ -893,6 +1057,11 @@ svn_rdump__get_dump_editor(const svn_del
   SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
                                             de, eb, editor, edit_baton, pool));
 
+  shim_callbacks->fetch_base_func = fetch_base_func;
+  shim_callbacks->fetch_props_func = fetch_props_func;
+  shim_callbacks->fetch_kind_func = fetch_kind_func;
+  shim_callbacks->fetch_baton = eb;
+
   SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
                                    shim_callbacks, pool, pool));
 



Mime
View raw message