subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From stef...@apache.org
Subject svn commit: r1694191 - in /subversion/trunk/subversion: include/svn_repos.h libsvn_repos/load-fs-vtable.c svnadmin/svnadmin.c
Date Wed, 05 Aug 2015 12:01:07 GMT
Author: stefan2
Date: Wed Aug  5 12:01:06 2015
New Revision: 1694191

URL: http://svn.apache.org/r1694191
Log:
Implement the new 'svnadmin load-revprops' sub-command.  It takes a normal
dumpfile but applies the revprop data only, and only to existing revisions.

We simply use the existing dumpfile parser infrastructure and implement
two callbacks: Starting a revision record b/c we don't create a txn here
and closing revisions - which sets the revprops.  Revprop parser and magic
header callbacks can be reused from the standard dumpfile loader.

* subversion/include/svn_repos.h
  (svn_repos_notify_action_t): Add a new notification for writing revprops.
  (svn_repos_load_fs_revprops): Declare new API. This is a variant of the
                                existing svn_repos_load_fs5.  We don't
                                trigger hooks nor do we modify the UUID.

* subversion/libsvn_repos/load-fs-vtable.c
  (revprops_new_revision_record,
   revprops_close_revision): New callback implementations.
  (build_revprop_parser): New parser setup function. Based on the preexisting
                          svn_repos_get_fs_build_parser5.
  (svn_repos_load_fs_revprops): Implement new API.

* subversion/svnadmin/svnadmin.c
  (svn_opt_subcommand_t): Declare new sub-command.
  (cmd_table): Add UI spec for the new sub-command.
  (repos_notify_handler): Handle the new notification type.
  (get_load_range): New utility, factored out from ...
  (subcommand_load): ... this.
  (subcommand_load_revprops): Implement the new sub-command.  Code taken
                              mostly from subcommand_load.

Modified:
    subversion/trunk/subversion/include/svn_repos.h
    subversion/trunk/subversion/libsvn_repos/load-fs-vtable.c
    subversion/trunk/subversion/svnadmin/svnadmin.c

Modified: subversion/trunk/subversion/include/svn_repos.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_repos.h?rev=1694191&r1=1694190&r2=1694191&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_repos.h (original)
+++ subversion/trunk/subversion/include/svn_repos.h Wed Aug  5 12:01:06 2015
@@ -247,7 +247,10 @@ typedef enum svn_repos_notify_action_t
   svn_repos_notify_hotcopy_rev_range,
 
   /** The repository pack did not do anything. @since New in 1.10. */
-  svn_repos_notify_pack_noop
+  svn_repos_notify_pack_noop,
+
+  /** The revision properties got set. @since New in 1.10. */
+  svn_repos_notify_load_revprop_set
 } svn_repos_notify_action_t;
 
 /** The type of warning occurring.
@@ -3222,6 +3225,52 @@ svn_repos_load_fs(svn_repos_t *repos,
                   void *cancel_baton,
                   apr_pool_t *pool);
 
+/**
+ * Read and parse dumpfile-formatted @a dumpstream, extracting the
+ * revision properties from it and apply them to the already-open
+ * @a repos.  Use @a scratch_pool for temporary allocations.
+ *
+ * If, after filtering by the @a start_rev and @a end_rev, the dumpstream
+ * contains revisions missing in @a repos, an error will be thrown.
+ *
+ * @a start_rev and @a end_rev act as filters, the lower and upper
+ * (inclusive) range values of revisions in @a dumpstream which will
+ * be loaded.  Either both of these values are #SVN_INVALID_REVNUM (in
+ * which case no revision-based filtering occurs at all), or both are
+ * valid revisions (where @a start_rev is older than or equivalent to
+ * @a end_rev).
+ *
+ * If @a validate_props is set, then validate Subversion revision
+ * properties (those in the svn: namespace) against established
+ * rules for those things.
+ *
+ * If @a ignore_dates is set, ignore any revision datestamps found in
+ * @a dumpstream, keeping whatever timestamps the revisions currently
+ * have.
+ *
+ * If non-NULL, use @a notify_func and @a notify_baton to send notification
+ * of events to the caller.
+ *
+ * If @a cancel_func is not @c NULL, it is called periodically with
+ * @a cancel_baton as argument to see if the client wishes to cancel
+ * the load.
+ *
+ * @remark No repository hooks will be triggered.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_repos_load_fs_revprops(svn_repos_t *repos,
+                           svn_stream_t *dumpstream,
+                           svn_revnum_t start_rev,
+                           svn_revnum_t end_rev,
+                           svn_boolean_t validate_props,
+                           svn_boolean_t ignore_dates,
+                           svn_repos_notify_func_t notify_func,
+                           void *notify_baton,
+                           svn_cancel_func_t cancel_func,
+                           void *cancel_baton,
+                           apr_pool_t *scratch_pool);
 
 /**
  * A vtable that is driven by svn_repos_parse_dumpstream3().

Modified: subversion/trunk/subversion/libsvn_repos/load-fs-vtable.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_repos/load-fs-vtable.c?rev=1694191&r1=1694190&r2=1694191&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_repos/load-fs-vtable.c (original)
+++ subversion/trunk/subversion/libsvn_repos/load-fs-vtable.c Wed Aug  5 12:01:06 2015
@@ -1271,3 +1271,215 @@ svn_repos_load_fs5(svn_repos_t *repos,
   return svn_repos_parse_dumpstream3(dumpstream, parser, parse_baton, FALSE,
                                      cancel_func, cancel_baton, pool);
 }
+
+/*----------------------------------------------------------------------*/
+
+/** The same functionality for revprops only **/
+
+/* Implement svn_repos_parse_fns3_t.new_revision_record.
+ *
+ * Because the revision is supposed to already exist, we don't need to
+ * start transactions etc. */
+static svn_error_t *
+revprops_new_revision_record(void **revision_baton,
+                             apr_hash_t *headers,
+                             void *parse_baton,
+                             apr_pool_t *pool)
+{
+  struct parse_baton *pb = parse_baton;
+  struct revision_baton *rb;
+
+  rb = make_revision_baton(headers, pb, pool);
+
+  /* If we're skipping this revision, try to notify someone. */
+  if (rb->skipped && pb->notify_func)
+    {
+      /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
+      svn_repos_notify_t *notify = svn_repos_notify_create(
+                                        svn_repos_notify_load_skipped_rev,
+                                        pb->notify_pool);
+
+      notify->old_revision = rb->rev;
+      pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
+      svn_pool_clear(pb->notify_pool);
+    }
+
+  /* If we're parsing revision 0, only the revision props are (possibly)
+     interesting to us: when loading the stream into an empty
+     filesystem, then we want new filesystem's revision 0 to have the
+     same props.  Otherwise, we just ignore revision 0 in the stream. */
+
+  *revision_baton = rb;
+  return SVN_NO_ERROR;
+}
+
+/* Implement svn_repos_parse_fns3_t.close_revision.
+ *
+ * Simply set the revprops we previously parsed and send notifications.
+ * This is the place where we will detect missing revisions. */
+static svn_error_t *
+revprops_close_revision(void *baton)
+{
+  struct revision_baton *rb = baton;
+  struct parse_baton *pb = rb->pb;
+  apr_hash_t *orig_props;
+  apr_hash_t *new_props;
+  apr_array_header_t *diff;
+  int i;
+
+  /* If we're skipping this revision we're done here. */
+  if (rb->skipped)
+    return SVN_NO_ERROR;
+
+  /* If the dumpstream doesn't have an 'svn:date' property and we
+     aren't ignoring the dates in the dumpstream altogether, remove
+     any 'svn:date' revision property that was set by FS layer when
+     the TXN was created.  */
+  if (! (pb->ignore_dates || rb->datestamp))
+    {
+      svn_prop_t *prop = &APR_ARRAY_PUSH(rb->revprops, svn_prop_t);
+      prop->name = SVN_PROP_REVISION_DATE;
+      prop->value = NULL;
+    }
+
+  SVN_ERR(svn_fs_revision_proplist(&orig_props, pb->fs, rb->rev, rb->pool));
+  new_props = svn_prop_array_to_hash(rb->revprops, rb->pool);
+  SVN_ERR(svn_prop_diffs(&diff, new_props, orig_props, rb->pool));
+
+  for (i = 0; i < diff->nelts; i++)
+    {
+      const svn_prop_t *prop = &APR_ARRAY_IDX(diff, i, svn_prop_t);
+
+      SVN_ERR(change_rev_prop(pb->repos, rb->rev, prop->name, prop->value,
+                              pb->validate_props, rb->pool));
+    }
+
+  if (pb->notify_func)
+    {
+      /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
+      svn_repos_notify_t *notify = svn_repos_notify_create(
+                                        svn_repos_notify_load_revprop_set,
+                                        pb->notify_pool);
+
+      notify->new_revision = rb->rev;
+      notify->old_revision = SVN_INVALID_REVNUM;
+      pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
+      svn_pool_clear(pb->notify_pool);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Set *CALLBACKS and *PARSE_BATON to a vtable parser which commits new
+ * revisions to the fs in REPOS.  Allocate the objects in RESULT_POOL.
+ *
+ * START_REV and END_REV act as filters, the lower and upper (inclusive)
+ * range values of revisions in DUMPSTREAM which will be loaded.  Either
+ * both of these values are #SVN_INVALID_REVNUM (in  which case no
+ * revision-based filtering occurs at all), or both are valid revisions
+ * (where START_REV is older than or equivalent to END_REV).
+ * 
+ * START_REV and END_REV act as filters, the lower and upper (inclusive)
+ * range values of revisions which will
+ * be loaded.  Either both of these values are #SVN_INVALID_REVNUM (in
+ * which case no revision-based filtering occurs at all), or both are
+ * valid revisions (where START_REV is older than or equivalent to
+ * END_REV).  They refer to dump stream revision numbers rather than
+ * committed revision numbers.
+ *
+ * If VALIDATE_PROPS is set, then validate Subversion revision properties
+ * (those in the svn: namespace) against established rules for those things.
+ *
+ * If IGNORE_DATES is set, ignore any revision datestamps found in
+ * DUMPSTREAM, keeping whatever timestamps the revisions currently have.
+ */
+static svn_error_t *
+build_revprop_parser(const svn_repos_parse_fns3_t **callbacks,
+                     void **parse_baton,
+                     svn_repos_t *repos,
+                     svn_revnum_t start_rev,
+                     svn_revnum_t end_rev,
+                     svn_boolean_t validate_props,
+                     svn_boolean_t ignore_dates,
+                     svn_repos_notify_func_t notify_func,
+                     void *notify_baton,
+                     apr_pool_t *result_pool)
+{
+  svn_repos_parse_fns3_t *parser = apr_pcalloc(result_pool, sizeof(*parser));
+  struct parse_baton *pb = apr_pcalloc(result_pool, sizeof(*pb));
+
+  SVN_ERR_ASSERT((SVN_IS_VALID_REVNUM(start_rev) &&
+                  SVN_IS_VALID_REVNUM(end_rev))
+                 || ((! SVN_IS_VALID_REVNUM(start_rev)) &&
+                     (! SVN_IS_VALID_REVNUM(end_rev))));
+  if (SVN_IS_VALID_REVNUM(start_rev))
+    SVN_ERR_ASSERT(start_rev <= end_rev);
+
+  parser->magic_header_record = magic_header_record;
+  parser->uuid_record = uuid_record;
+  parser->new_revision_record = revprops_new_revision_record;
+  parser->new_node_record = NULL;
+  parser->set_revision_property = set_revision_property;
+  parser->set_node_property = NULL;
+  parser->remove_node_props = NULL;
+  parser->set_fulltext = NULL;
+  parser->close_node = NULL;
+  parser->close_revision = revprops_close_revision;
+  parser->delete_node_property = NULL;
+  parser->apply_textdelta = NULL;
+
+  pb->repos = repos;
+  pb->fs = svn_repos_fs(repos);
+  pb->use_history = FALSE;
+  pb->validate_props = validate_props;
+  pb->notify_func = notify_func;
+  pb->notify_baton = notify_baton;
+  pb->uuid_action = svn_repos_load_uuid_ignore; /* Never touch the UUID. */
+  pb->parent_dir = NULL;
+  pb->pool = result_pool;
+  pb->notify_pool = svn_pool_create(result_pool);
+  pb->rev_map = NULL;
+  pb->oldest_dumpstream_rev = SVN_INVALID_REVNUM;
+  pb->last_rev_mapped = SVN_INVALID_REVNUM;
+  pb->start_rev = start_rev;
+  pb->end_rev = end_rev;
+  pb->use_pre_commit_hook = FALSE;
+  pb->use_post_commit_hook = FALSE;
+  pb->ignore_dates = ignore_dates;
+
+  *callbacks = parser;
+  *parse_baton = pb;
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_repos_load_fs_revprops(svn_repos_t *repos,
+                           svn_stream_t *dumpstream,
+                           svn_revnum_t start_rev,
+                           svn_revnum_t end_rev,
+                           svn_boolean_t validate_props,
+                           svn_boolean_t ignore_dates,
+                           svn_repos_notify_func_t notify_func,
+                           void *notify_baton,
+                           svn_cancel_func_t cancel_func,
+                           void *cancel_baton,
+                           apr_pool_t *scratch_pool)
+{
+  const svn_repos_parse_fns3_t *parser;
+  void *parse_baton;
+
+  /* This is really simple. */
+
+  SVN_ERR(build_revprop_parser(&parser, &parse_baton,
+                               repos,
+                               start_rev, end_rev,
+                               validate_props,
+                               ignore_dates,
+                               notify_func,
+                               notify_baton,
+                               scratch_pool));
+
+  return svn_repos_parse_dumpstream3(dumpstream, parser, parse_baton, FALSE,
+                                     cancel_func, cancel_baton, scratch_pool);
+}

Modified: subversion/trunk/subversion/svnadmin/svnadmin.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svnadmin/svnadmin.c?rev=1694191&r1=1694190&r2=1694191&view=diff
==============================================================================
--- subversion/trunk/subversion/svnadmin/svnadmin.c (original)
+++ subversion/trunk/subversion/svnadmin/svnadmin.c Wed Aug  5 12:01:06 2015
@@ -171,6 +171,7 @@ static svn_opt_subcommand_t
   subcommand_hotcopy,
   subcommand_info,
   subcommand_load,
+  subcommand_load_revprops,
   subcommand_list_dblogs,
   subcommand_list_unused_dblogs,
   subcommand_lock,
@@ -444,6 +445,15 @@ static const svn_opt_subcommand_desc2_t
     svnadmin__use_pre_commit_hook, svnadmin__use_post_commit_hook,
     svnadmin__parent_dir, svnadmin__bypass_prop_validation, 'M'} },
 
+  {"load-revprops", subcommand_load_revprops, {0}, N_
+   ("usage: svnadmin load-revprops REPOS_PATH\n\n"
+    "Read a 'dumpfile'-formatted stream from stdin, setting the revision\n"
+    "properties in the repository's filesystem.  Revisions not found in the\n"
+    "repository will cause an error.  Progress feedback is sent to stdout.\n"
+    "If --revision is specified, limit the loaded revisions to only those\n"
+    "in the dump stream whose revision numbers match the specified range.\n"),
+   {'q', 'r', svnadmin__force_uuid, svnadmin__bypass_prop_validation} },
+
   {"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"
@@ -1139,6 +1149,12 @@ repos_notify_handler(void *baton,
         }
       return;
 
+    case svn_repos_notify_load_revprop_set:
+      svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
+                        _("Properties set on revision %ld.\n"),
+                        notify->new_revision));
+      return;
+
     default:
       return;
   }
@@ -1374,43 +1390,57 @@ optrev_to_revnum(svn_revnum_t *revnum, c
   return SVN_NO_ERROR;
 }
 
-
-/* This implements `svn_opt_subcommand_t'. */
+/* Read the min / max revision from the OPT_STATE, verify them and return
+   them in *LOWER and *UPPER, respectively. */
 static svn_error_t *
-subcommand_load(apr_getopt_t *os, void *baton, apr_pool_t *pool)
+get_load_range(svn_revnum_t *lower,
+               svn_revnum_t *upper,
+               struct svnadmin_opt_state *opt_state)
 {
-  svn_error_t *err;
-  struct svnadmin_opt_state *opt_state = baton;
-  svn_repos_t *repos;
-  svn_revnum_t lower = SVN_INVALID_REVNUM, upper = SVN_INVALID_REVNUM;
-  svn_stream_t *stdin_stream;
-  svn_stream_t *feedback_stream = NULL;
-
-  /* Expect no more arguments. */
-  SVN_ERR(parse_args(NULL, os, 0, 0, pool));
-
   /* Find the revision numbers at which to start and end.  We only
      support a limited set of revision kinds: number and unspecified. */
-  SVN_ERR(optrev_to_revnum(&lower, &opt_state->start_revision));
-  SVN_ERR(optrev_to_revnum(&upper, &opt_state->end_revision));
+  SVN_ERR(optrev_to_revnum(lower, &opt_state->start_revision));
+  SVN_ERR(optrev_to_revnum(upper, &opt_state->end_revision));
 
   /* Fill in implied revisions if necessary. */
-  if ((upper == SVN_INVALID_REVNUM) && (lower != SVN_INVALID_REVNUM))
+  if ((*upper == SVN_INVALID_REVNUM) && (*lower != SVN_INVALID_REVNUM))
     {
-      upper = lower;
+      *upper = *lower;
     }
-  else if ((upper != SVN_INVALID_REVNUM) && (lower == SVN_INVALID_REVNUM))
+  else if ((*upper != SVN_INVALID_REVNUM) && (*lower == SVN_INVALID_REVNUM))
     {
-      lower = upper;
+      *lower = *upper;
     }
 
   /* Ensure correct range ordering. */
-  if (lower > upper)
+  if (*lower > *upper)
     {
       return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
                               _("First revision cannot be higher than second"));
     }
 
+  return SVN_NO_ERROR;
+}
+
+
+/* This implements `svn_opt_subcommand_t'. */
+static svn_error_t *
+subcommand_load(apr_getopt_t *os, void *baton, apr_pool_t *pool)
+{
+  svn_error_t *err;
+  struct svnadmin_opt_state *opt_state = baton;
+  svn_repos_t *repos;
+  svn_revnum_t lower, upper;
+  svn_stream_t *stdin_stream;
+  svn_stream_t *feedback_stream = NULL;
+
+  /* Expect no more arguments. */
+  SVN_ERR(parse_args(NULL, os, 0, 0, pool));
+
+  /* Find the revision numbers at which to start and end.  We only
+     support a limited set of revision kinds: number and unspecified. */
+  SVN_ERR(get_load_range(&lower, &upper, opt_state));
+
   SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
 
   /* Read the stream from STDIN.  Users can redirect a file. */
@@ -1437,6 +1467,47 @@ subcommand_load(apr_getopt_t *os, void *
   return err;
 }
 
+static svn_error_t *
+subcommand_load_revprops(apr_getopt_t *os, void *baton, apr_pool_t *pool)
+{
+  svn_error_t *err;
+  struct svnadmin_opt_state *opt_state = baton;
+  svn_repos_t *repos;
+  svn_revnum_t lower, upper;
+  svn_stream_t *stdin_stream;
+
+  svn_stream_t *feedback_stream = NULL;
+
+  /* Expect no more arguments. */
+  SVN_ERR(parse_args(NULL, os, 0, 0, pool));
+
+  /* Find the revision numbers at which to start and end.  We only
+     support a limited set of revision kinds: number and unspecified. */
+  SVN_ERR(get_load_range(&lower, &upper, opt_state));
+
+  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
+
+  /* Read the stream from STDIN.  Users can redirect a file. */
+  SVN_ERR(svn_stream_for_stdin(&stdin_stream, pool));
+
+  /* Progress feedback goes to STDOUT, unless they asked to suppress it. */
+  if (! opt_state->quiet)
+    feedback_stream = recode_stream_create(stdout, pool);
+
+  err = svn_repos_load_fs_revprops(repos, stdin_stream, lower, upper,
+                                   !opt_state->bypass_prop_validation,
+                                   opt_state->ignore_dates,
+                                   opt_state->quiet ? NULL
+                                                    : repos_notify_handler,
+                                   feedback_stream, check_cancel, NULL, pool);
+  if (err && err->apr_err == SVN_ERR_BAD_PROPERTY_VALUE)
+    return svn_error_quick_wrap(err,
+                                _("Invalid property value found in "
+                                  "dumpstream; consider repairing the source "
+                                  "or using --bypass-prop-validation while "
+                                  "loading."));
+  return err;
+}
 
 /* This implements `svn_opt_subcommand_t'. */
 static svn_error_t *



Mime
View raw message