[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

[PATCH] svn mergeinfo --elide

From: Stefan Sperling <stsp_at_elego.de>
Date: Sat, 10 Sep 2011 03:43:36 +0200

Users with lots of subtree mergeinfo may want to clean it up by
eliding subtree mergeinfo without actually performing a merge.
There seem to be scripts floating around that perform this task.
But there is no "official" solution yet, it seems.

Would it be useful to allow mergeinfo elision to take place outside
the context of a merge? The patch below makes it possible to trigger
mergeinfo elision by running 'svn mergeinfo --elide'.

It seems that the elision algorithm is very basic at the moment,
which prevents this from being really useful. Subversion only elides
mergeinfo which exactly matches the mergeinfo of a parent.
It doesn't perform elision if mergeinfo on the parent covers a
greater range of revisions than the mergeinfo on the child.

Is this deliberate?

For instance:

r3 changed '/branch/gamma'.
This is merged into '/trunk', as follows:

$ svn merge --reintegrate ^/branch/gamma gamma
--- Merging differences between repository URLs into 'gamma':
U gamma/delta
--- Recording mergeinfo for merge between repository URLs into 'gamma':
 U gamma
$

Nothing further happens on the branch.
Later, we merge the entire branch (a no-op merge):

$ svn merge --reintegrate ^/branch
--- Recording mergeinfo for merge between repository URLs into '.':
 U .
$

Note that no elision took place.
Now we have the following mergeinfo which will not elide:

Properties on '.':
  svn:mergeinfo
      /branch:2-4
Properties on 'gamma':
  svn:mergeinfo
      /branch:2-3

The following mergeinfo will elide:

Properties on '.':
  svn:mergeinfo
      /branch:2-4
Properties on 'gamma':
  svn:mergeinfo
      /branch:2-4

So the user is required to run no-op merges on subtrees in order
to elide mergeinfo from subtrees. This can be quite tedious.

It should be safe elide mergeinfo from 'gamma' to the root of the
working copy. The mergeinfo on the parent is inheritable and a
superset of the mergeinfo on 'gamma'.

If the elision algorithm supported elision of mergeinfo which
is a proper subset of the mergeinfo in a parent, cleaning up
subtree mergeinfo would become a lot more easier than it is now.

Would it be a good idea to commit this patch and wait for the
elision algorithm to improve? Having 'svn mergeinfo --elide'
might make it a bit easier to perform ad-hoc testing while
improving the algorithm.

This thread on users@ inspired the idea:
http://svn.haxx.se/users/archive-2011-09/0101.shtml

[[[
Teach the 'svn mergeinfo' command to elide mergeinfo.

In some situations it can be useful to remove redundant mergeinfo
without actually running a merge.

Add a new option --elide to 'svn mergeinfo' which causes it
to elide mergeinfo instead of displaying it. The new --elide
option is mutually exclusive with the --show-revs option.

* subversion/include/svn_client.h
  (svn_client_elide_subtree_mergeinfo): New.

* subversion/svn/cl.h
  (svn_cl__opt_state_t): New option ELIDE_MERGEINFO.

* subversion/svn/mergeinfo-cmd.c
  (svn_cl__mergeinfo): If the --elide option was given, elide
   mergeinfo within the specified working copy path instead
   of displaying mergeinfo, and default to depth 'infinity'.
   If the working copy path was not given, default to the
   current directory.

* subversion/svn/main.c
  (svn_cl__longopt_t, svn_cl__options): New option --elide.
  (svn_cl__cmd_table): Update help for 'svn mergeinfo'.
  (main): Handle new --elide option. Disallow --elide and
   --show-revs being used together.

* subversion/libsvn_client/mergeinfo.c
  (elide_subtree_mergeinfo_baton, elide_subtree_mergeinfo_cb,
   elide_subtree_mergeinfo_locked, svn_client_elide_subtree_mergeinfo): New.
]]]

Index: subversion/include/svn_client.h
===================================================================
--- subversion/include/svn_client.h (revision 1167379)
+++ subversion/include/svn_client.h (working copy)
@@ -3587,6 +3587,15 @@ svn_client_mergeinfo_log_eligible(const char *path
                                   svn_client_ctx_t *ctx,
                                   apr_pool_t *pool);
 
+/* Try to elide mergeinfo on working copy node @a local_abspath
+ * and children within the specified @a depth.
+ * Use @a scratch_pool for any temporary allocations. */
+svn_error_t *
+svn_client_elide_subtree_mergeinfo(const char *local_abspath,
+ svn_depth_t depth,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
 /** @} */
 
 /**
Index: subversion/svn/cl.h
===================================================================
--- subversion/svn/cl.h (revision 1167379)
+++ subversion/svn/cl.h (working copy)
@@ -230,6 +230,7 @@ typedef struct svn_cl__opt_state_t
   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 allow_mixed_rev; /* Allow operation on mixed-revision WC */
+ svn_boolean_t elide_mergeinfo; /* Elide mergeinfo. */
 } svn_cl__opt_state_t;
 
 
Index: subversion/svn/mergeinfo-cmd.c
===================================================================
--- subversion/svn/mergeinfo-cmd.c (revision 1167379)
+++ subversion/svn/mergeinfo-cmd.c (working copy)
@@ -30,6 +30,7 @@
 #include "svn_pools.h"
 #include "svn_client.h"
 #include "svn_cmdline.h"
+#include "svn_dirent_uri.h"
 #include "svn_path.h"
 #include "svn_error.h"
 #include "svn_error_codes.h"
@@ -66,14 +67,40 @@ svn_cl__mergeinfo(apr_getopt_t *os,
   apr_array_header_t *targets;
   const char *source, *target;
   svn_opt_revision_t src_peg_revision, tgt_peg_revision;
- /* Default to depth empty. */
- svn_depth_t depth = opt_state->depth == svn_depth_unknown
- ? svn_depth_empty : opt_state->depth;
-
+ svn_depth_t depth;
+
   SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
                                                       opt_state->targets,
                                                       ctx, FALSE, pool));
 
+ /* If we're eliding mergeinfo we don't display any. */
+ if (opt_state->elide_mergeinfo)
+ {
+ if (targets->nelts > 1)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Too many arguments given"));
+
+ if (targets->nelts == 1)
+ target = APR_ARRAY_IDX(targets, 0, const char *);
+ else
+ target = "";
+
+ SVN_ERR(svn_dirent_get_absolute(&target, target, pool));
+
+ /* Default to depth infinity. */
+ depth = opt_state->depth == svn_depth_unknown
+ ? svn_depth_infinity : opt_state->depth;
+
+ SVN_ERR(svn_client_elide_subtree_mergeinfo(target, depth, ctx, pool));
+ return SVN_NO_ERROR;
+ }
+
+ /* Not eliding mergeinfo, so display mergeinfo. */
+
+ /* Default to depth empty. */
+ depth = opt_state->depth == svn_depth_unknown
+ ? svn_depth_empty : opt_state->depth;
+
   /* We expect a single source URL followed by a single target --
      nothing more, nothing less. */
   if (targets->nelts < 1)
Index: subversion/svn/main.c
===================================================================
--- subversion/svn/main.c (revision 1167379)
+++ subversion/svn/main.c (working copy)
@@ -125,6 +125,7 @@ typedef enum svn_cl__longopt_t {
   opt_internal_diff,
   opt_use_git_diff_format,
   opt_allow_mixed_revisions,
+ opt_elide,
 } svn_cl__longopt_t;
 
 
@@ -342,6 +343,7 @@ const apr_getopt_option_t svn_cl__options[] =
                        "Use of this option is not recommended!\n"
                        " "
                        "Please run 'svn update' instead.")},
+ {"elide", opt_elide, 0, N_("elide mergeinfo")},
 
   /* Long-opt Aliases
    *
@@ -926,16 +928,21 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table
      opt_allow_mixed_revisions} },
 
   { "mergeinfo", svn_cl__mergeinfo, {0}, N_
- ("Display merge-related information.\n"
- "usage: mergeinfo SOURCE[@REV] [TARGET[@REV]]\n"
+ ("Display or eliminate redundancy in merge-related information.\n"
+ "usage: 1. mergeinfo SOURCE[@REV] [TARGET[@REV]]\n"
+ " 2. mergeinfo --elide [PATH]\n"
      "\n"
- " Display information related to merges (or potential merges) between\n"
- " SOURCE and TARGET (default: '.'). Display the type of information\n"
- " specified by the --show-revs option. If --show-revs isn't passed,\n"
- " it defaults to --show-revs='merged'.\n"
+ " 1. Display information related to merges (or potential merges) between\n"
+ " SOURCE and TARGET (default: '.'). Display the type of information\n"
+ " specified by the --show-revs option. If --show-revs isn't passed,\n"
+ " it defaults to --show-revs='merged'.\n"
+ " The depth can be 'empty' or 'infinity'; the default is 'empty'.\n"
      "\n"
- " The depth can be 'empty' or 'infinity'; the default is 'empty'.\n"),
- {'r', 'R', opt_depth, opt_show_revs} },
+ " 2. Attempt to elide merge information within the subtree rooted at PATH\n"
+ " (default '.'). This removes redundant svn:mergeinfo properties from\n"
+ " children whose parents record a superset of the same merge information.\n"
+ " The depth can be any valid depth value; the default is 'infinity'.\n"),
+ {'r', 'R', opt_depth, opt_show_revs, opt_elide} },
 
   { "mkdir", svn_cl__mkdir, {0}, N_
     ("Create a new directory under version control.\n"
@@ -1525,7 +1532,7 @@ main(int argc, const char *argv[])
   opt_state.depth = svn_depth_unknown;
   opt_state.set_depth = svn_depth_unknown;
   opt_state.accept_which = svn_cl__accept_unspecified;
- opt_state.show_revs = svn_cl__show_revs_merged;
+ opt_state.show_revs = svn_cl__show_revs_invalid;
 
   /* No args? Show usage. */
   if (argc <= 1)
@@ -2030,6 +2037,9 @@ main(int argc, const char *argv[])
       case opt_allow_mixed_revisions:
         opt_state.allow_mixed_rev = TRUE;
         break;
+ case opt_elide:
+ opt_state.elide_mergeinfo = TRUE;
+ break;
       default:
         /* Hmmm. Perhaps this would be a good place to squirrel away
            opts that commands like svn diff might need. Hmmm indeed. */
@@ -2222,6 +2232,21 @@ main(int argc, const char *argv[])
       return svn_cmdline_handle_exit_error(err, pool, "svn: ");
     }
 
+ /* Disallow simultaneous use of both --show-revs and --elide. */
+ if (opt_state.elide_mergeinfo &&
+ opt_state.show_revs != svn_cl__show_revs_invalid)
+ {
+ err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
+ _("--show-revs and --elide are mutually "
+ "exclusive"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ else if (opt_state.show_revs == svn_cl__show_revs_invalid)
+ {
+ /* Set the default value of --show-revs. */
+ opt_state.show_revs = svn_cl__show_revs_merged;
+ }
+
   /* Ensure that 'revision_ranges' has at least one item, and make
      'start_revision' and 'end_revision' match that item. */
   if (opt_state.revision_ranges->nelts == 0)
Index: subversion/libsvn_client/mergeinfo.c
===================================================================
--- subversion/libsvn_client/mergeinfo.c (revision 1167379)
+++ subversion/libsvn_client/mergeinfo.c (working copy)
@@ -2255,3 +2255,64 @@ svn_client__mergeinfo_status(svn_boolean_t *mergei
 
   return SVN_NO_ERROR;
 }
+
+/* Baton for elide_subtree_mergeinfo_cb() and
+ * elide_subtree_mergeinfo_locked(). */
+struct elide_subtree_mergeinfo_baton {
+ const char *local_abspath;
+ svn_client_ctx_t *ctx;
+ svn_depth_t depth;
+};
+
+/* Implements svn_wc__proplist_receiver_t. */
+static svn_error_t *
+elide_subtree_mergeinfo_cb(void *baton,
+ const char *local_abspath,
+ apr_hash_t *props,
+ apr_pool_t *scratch_pool)
+{
+ struct elide_subtree_mergeinfo_baton *b = baton;
+
+ SVN_ERR(svn_client__elide_mergeinfo(local_abspath,
+ b->local_abspath,
+ b->ctx, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+elide_subtree_mergeinfo_locked(void *baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct elide_subtree_mergeinfo_baton *b = baton;
+
+ /* Use proplist to find nodes with mergeinfo, and try to elide
+ * mergeinfo on each. */
+ SVN_ERR(svn_wc__prop_list_recursive(b->ctx->wc_ctx, b->local_abspath,
+ SVN_PROP_MERGEINFO,
+ b->depth, FALSE, FALSE, NULL,
+ elide_subtree_mergeinfo_cb, baton,
+ b->ctx->cancel_func,
+ b->ctx->cancel_baton,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_elide_subtree_mergeinfo(const char *local_abspath,
+ svn_depth_t depth,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ struct elide_subtree_mergeinfo_baton baton;
+
+ baton.local_abspath = local_abspath;
+ baton.depth = depth;
+ baton.ctx = ctx;
+
+ SVN_ERR(svn_wc__call_with_write_lock(elide_subtree_mergeinfo_locked,
+ &baton, ctx->wc_ctx,
+ local_abspath, FALSE,
+ scratch_pool, scratch_pool));
+ return SVN_NO_ERROR;
+}
Received on 2011-09-10 03:44:16 CEST

This is an archived mail posted to the Subversion Dev mailing list.