Konstantin Kolinko wrote on Thu, Oct 02, 2014 at 03:40:51 +0400:
> 2014-10-02 2:59 GMT+04:00 Daniel Shahaf <d...@daniel.shahaf.name>:
> > Julian Foad wrote on Wed, Oct 01, 2014 at 10:52:42 +0100:
> >> Daniel Shahaf wrote in the thread "No no-op changes":
> >> > Should we provide an "official" way to create an empty revision?  That
> >> > is, a revision whose changed-paths list is empty?
> >> >
> >> > Use-cases:
> >> >
> >> > 1. Suppose last backup is r100 and revisions r101:r105 were lost; then
> >> > after restoring the backup, the admin would create 5 empty revisions.
> >> >
> >> > 2. Force an empty revision for whatever reason, such as to make the
> >> > revnums sync to something:
> >> > 2.1. See r3 of the regression test merge_tests.py#125 svnmucc_abuse_1().
> >> > 2.2. W hen loading our repository to the ASF repository, if Joe had
> >> > created 26 empty revisions, then The Offset would have been 840100
> >> > rather than 840074, which would make our mental math easier.
> >>
> >> What should the author and log message be on the empty revs? I suppose
> >> these need to be optionally specified, defaulting to blank?
> >>
> 
> My thought:
> 
> svnadmin bump -m "message" REPOS_PATH
> svnrdump bump -m "message" URL
> 
> The command creates 1 empty revision and thus bumps the repository
> revision number. It can be repeated in a loop as necessary.
> 

Two proof-of-concept patches implementing this are attached.  They work,
but some bells and whistles are missing (e.g., setting the log message
from 'svnadmin bump' to a boilerplate value or to a user-provided value).

I'm not going to commit them just yet --- I'd rather let the idea soak
for a bit.  (In part because the 1.9 branch point is around the corner.)

> > The author of a revision created by svnadmin is "the administrator"; we
> > have not defined a way to represent this value in an svn:author revprop.
> >
> 
> If dependent tools can deal with missing svn:author property then it
> is OK to do not have one.
> 
> Looking at authors.txt file used to configure svn-git mirroring for
> ASF [1], there is the following line at the end of the file:
> 
> (no author) = No Author <dev-n...@apache.org>
> 
> So I think git-svn can deal with revisions that do not have svn:author
> property, and thus it is safe to create such revisions.
> 

And even if git-svn didn't implement this, it would _still_ be okay to
create revisions with no svn:author revprops, since we stipulated that
was permissible a decade+ ago when we defined our API.  Naturally, if
that were the case, we'd heads-up the git-svn folks and work with them
to minimize the impact on users.
>From 4016382c6801028c0cabee63dbf89f885e198b74 Mon Sep 17 00:00:00 2001
From: Daniel Shahaf <d...@daniel.shahaf.name>
Date: Thu, 2 Oct 2014 03:29:07 +0000
Subject: [PATCH 1/3] New feature: 'svnmucc bump'.

This partially addresses issue #4521.

* subversion/svnmucc/svnmucc.c
  (action_code_t.ACTION_BUMP): New enumerator.
  (sub_main): Parse 'bump'.
  (execute): Handle 'bump' (as a no-op).

* subversion/libsvn_client/mtcc.c
  (svn_client__mtcc_commit): Permit the "no changes" case.

* subversion/tests/cmdline/svnmucc_tests.py
  (bump): New unit test.
---
 subversion/libsvn_client/mtcc.c           |    7 -------
 subversion/svnmucc/svnmucc.c              |   13 +++++++++++--
 subversion/tests/cmdline/svnmucc_tests.py |   14 ++++++++++++++
 3 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/subversion/libsvn_client/mtcc.c b/subversion/libsvn_client/mtcc.c
index 2fa40fb..2b3bdb2 100644
--- a/subversion/libsvn_client/mtcc.c
+++ b/subversion/libsvn_client/mtcc.c
@@ -1312,13 +1312,6 @@ svn_client__mtcc_commit(apr_hash_t *revprop_table,
   const char *session_url;
   const char *log_msg;
 
-  if (MTCC_UNMODIFIED(mtcc))
-    {
-      /* No changes -> no revision. Easy out */
-      svn_pool_destroy(mtcc->pool);
-      return SVN_NO_ERROR;
-    }
-
   SVN_ERR(svn_ra_get_session_url(mtcc->ra_session, &session_url, scratch_pool));
 
   if (mtcc->root_op->kind != OP_OPEN_DIR)
diff --git a/subversion/svnmucc/svnmucc.c b/subversion/svnmucc/svnmucc.c
index 1c4326b..a2df51a 100644
--- a/subversion/svnmucc/svnmucc.c
+++ b/subversion/svnmucc/svnmucc.c
@@ -88,6 +88,7 @@ commit_callback(const svn_commit_info_t *commit_info,
 }
 
 typedef enum action_code_t {
+  ACTION_BUMP,
   ACTION_MV,
   ACTION_MKDIR,
   ACTION_CP,
@@ -212,6 +213,9 @@ execute(const apr_array_header_t *actions,
                                                action->prop_value, FALSE,
                                                mtcc, iterpool));
           break;
+        case ACTION_BUMP:
+          /* Noop. */
+          break;
         case ACTION_PROPSETF:
         default:
           SVN_ERR_MALFUNCTION_NO_RETURN();
@@ -280,6 +284,8 @@ help(FILE *stream, apr_pool_t *pool)
       "  propset NAME VALUE URL : set property NAME on URL to VALUE\n"
       "  propsetf NAME FILE URL : set property NAME on URL to value read from FILE\n"
       "  propdel NAME URL       : delete property NAME from URL\n"
+      "  bump URL               : force a new commit to URL's repository, even if\n"
+      "                         : no files or directories would be changed\n"
       "\n"
       "Valid options:\n"
       "  -h, -? [--help]        : display this text\n"
@@ -733,6 +739,8 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
         action->action = ACTION_PROPSETF;
       else if (! strcmp(action_string, "propdel"))
         action->action = ACTION_PROPDEL;
+      else if (! strcmp(action_string, "bump"))
+        action->action = ACTION_BUMP;
       else if (! strcmp(action_string, "?") || ! strcmp(action_string, "h")
                || ! strcmp(action_string, "help"))
         {
@@ -836,6 +844,7 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
 
       /* How many URLs does this action expect? */
       if (action->action == ACTION_RM
+          || action->action == ACTION_BUMP
           || action->action == ACTION_MKDIR
           || action->action == ACTION_PUT
           || action->action == ACTION_PROPSET
@@ -869,9 +878,9 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
           url = sanitize_url(url, pool);
           action->path[j] = url;
 
-          /* The first URL arguments to 'cp', 'pd', 'ps' could be the anchor,
-             but the other URLs should be children of the anchor. */
+          /* Most URLs should be children of the anchor, except for: */
           if (! (action->action == ACTION_CP && j == 0)
+              && action->action != ACTION_BUMP
               && action->action != ACTION_PROPDEL
               && action->action != ACTION_PROPSET
               && action->action != ACTION_PROPSETF)
diff --git a/subversion/tests/cmdline/svnmucc_tests.py b/subversion/tests/cmdline/svnmucc_tests.py
index b24ada0..627fae9 100755
--- a/subversion/tests/cmdline/svnmucc_tests.py
+++ b/subversion/tests/cmdline/svnmucc_tests.py
@@ -452,6 +452,19 @@ rm A/B/C/Y
   svntest.actions.run_and_verify_svn(None, expected_output, [],
                                      'log', '-qvr3', repo_url)
 
+@Issues(4521)
+def bump(sbox):
+  "svnmucc bump"
+
+  sbox.build(create_wc=False)
+  svntest.actions.run_and_verify_svnmucc(None, None, [],
+                           '-U', sbox.repo_url, '-m', "log message",
+                           'bump', "")
+
+  # Verify that the changed-paths list is empty.
+  expected_output = []
+  svntest.actions.run_and_verify_svnlook(None, expected_output, [],
+                           "changed", sbox.repo_dir)
 
 ######################################################################
 
@@ -462,6 +475,7 @@ test_list = [ None,
               too_many_log_messages,
               no_log_msg_non_interactive,
               nested_replaces,
+              bump,
             ]
 
 if __name__ == '__main__':
-- 
1.7.10.4

>From bc755c6f20cd6cd31d193ee5e32abf0ef2ffbf14 Mon Sep 17 00:00:00 2001
From: Daniel Shahaf <d...@daniel.shahaf.name>
Date: Thu, 2 Oct 2014 10:07:33 +0000
Subject: [PATCH 2/3] New feature: 'svnadmin bump'.

This partially addresses issue #4521.

* subversion/svnadmin/svnadmin.c
  (subcommand_bump, cmd_table."bump"): New subcommand.
  (bump_commit_callback): New helper function.

* subversion/tests/cmdline/svnadmin_tests.py
  (bump): New unit test.
---
 subversion/svnadmin/svnadmin.c             |   76 ++++++++++++++++++++++++++++
 subversion/tests/cmdline/svnadmin_tests.py |   14 +++++
 2 files changed, 90 insertions(+)

diff --git a/subversion/svnadmin/svnadmin.c b/subversion/svnadmin/svnadmin.c
index 9391bac..1385b25 100644
--- a/subversion/svnadmin/svnadmin.c
+++ b/subversion/svnadmin/svnadmin.c
@@ -161,6 +161,7 @@ check_lib_versions(void)
 /** Subcommands. **/
 
 static svn_opt_subcommand_t
+  subcommand_bump,
   subcommand_crashtest,
   subcommand_create,
   subcommand_delrevprop,
@@ -342,6 +343,12 @@ static const apr_getopt_option_t options_table[] =
  */
 static const svn_opt_subcommand_desc2_t cmd_table[] =
 {
+  {"bump", subcommand_bump, {0}, N_
+   ("usage: svnadmin bump REPOS_PATH\n\n"
+    "Commit an empty revision to REPOS_PATH.\n"),
+   /* ### TODO: svnadmin__bypass_hooks, 'q', 'F', */
+   {0} },
+
   {"crashtest", subcommand_crashtest, {0}, N_
    ("usage: svnadmin crashtest REPOS_PATH\n\n"
     "Open the repository at REPOS_PATH, then abort, thus simulating\n"
@@ -679,6 +686,75 @@ crashtest_malfunction_handler(svn_boolean_t can_return,
   return SVN_NO_ERROR; /* Not reached. */
 }
 
+/* This implements 'svn_error_malfunction_handler_t. */
+static svn_error_t *
+bump_commit_callback(const svn_commit_info_t *commit_info,
+                     void *baton,
+                     apr_pool_t *pool)
+{
+  /* We print the post-commit error and revnum only. */
+
+  if (commit_info->post_commit_err)
+    SVN_ERR(svn_cmdline_fprintf(stderr, pool,
+                                _("%swarning: Post-commit hook error: %s\n"),
+                                "svnadmin: ",
+                                commit_info->post_commit_err));
+
+  SVN_ERR(svn_cmdline_printf(pool, _("Committed empty revision r%ld.\n"),
+                             commit_info->revision));
+
+  return SVN_NO_ERROR;
+}
+
+/* This implements `svn_opt_subcommand_t'. */
+static svn_error_t *
+subcommand_bump(apr_getopt_t *os, void *baton, apr_pool_t *pool)
+{
+  struct svnadmin_opt_state *opt_state = baton;
+  svn_repos_t *repos;
+
+  /* Expect no more arguments. */
+  SVN_ERR(parse_args(NULL, os, 0, 0, pool));
+
+  SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
+
+  /* Do the commit. */
+  {
+    svn_fs_t *fs = svn_repos_fs(repos);
+    svn_fs_txn_t *txn;
+    svn_revnum_t youngest;
+    apr_hash_t *revprop_table = apr_hash_make(pool);
+    const svn_delta_editor_t *editor;
+    void *edit_baton;
+    void *root_baton;
+    svn_error_t *err;
+
+    SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool));
+    /* ### What should SVN_PROP_REVISION_AUTHOR be set to? */
+    /* ### Invent and set an SVN_PROP_REVISION_EMPTY revprop. */
+    /* Note that we don't override SVN_PROP_REVISION_DATE. */
+    SVN_ERR(svn_repos_fs_begin_txn_for_commit2(&txn, repos, youngest,
+                                               revprop_table, pool));
+    SVN_ERR(svn_repos_get_commit_editor5(&editor, &edit_baton, repos, txn,
+                                         "" /* don't check copyfrom */,
+                                         "" /* ignored */,
+                                         revprop_table,
+                                         bump_commit_callback, NULL,
+                                         NULL, NULL, /* no authz */
+                                         pool));
+
+    err = editor->open_root(edit_baton, youngest, pool, &root_baton);
+    if (!err)
+      err = editor->close_edit(edit_baton, pool);
+    if (err)
+      return svn_error_compose_create(
+               editor->abort_edit(edit_baton, pool),
+               err);
+  }
+
+  return SVN_NO_ERROR;
+}
+
 /* This implements `svn_opt_subcommand_t'. */
 static svn_error_t *
 subcommand_crashtest(apr_getopt_t *os, void *baton, apr_pool_t *pool)
diff --git a/subversion/tests/cmdline/svnadmin_tests.py b/subversion/tests/cmdline/svnadmin_tests.py
index 5d97058..bee3111 100755
--- a/subversion/tests/cmdline/svnadmin_tests.py
+++ b/subversion/tests/cmdline/svnadmin_tests.py
@@ -2941,6 +2941,19 @@ def upgrade(sbox):
                                      '-m', svntest.main.make_log_msg(),
                                      sbox.repo_url + '/dir')
 
+@Issues(4521)
+def bump(sbox):
+  "svnadmin bump"
+
+  sbox.build(create_wc=False)
+  expected_output = ["Committed empty revision r2.\n"]
+  svntest.actions.run_and_verify_svnadmin(None, expected_output, [],
+                           'bump', sbox.repo_dir)
+
+  # Verify that the changed-paths list is empty.
+  expected_output = []
+  svntest.actions.run_and_verify_svnlook(None, expected_output, [],
+                           "changed", sbox.repo_dir)
 
 ########################################################################
 # Run the tests
@@ -2995,6 +3008,7 @@ test_list = [ None,
               fsfs_hotcopy_progress_old,
               freeze_same_uuid,
               upgrade,
+              bump,
              ]
 
 if __name__ == '__main__':
-- 
1.7.10.4

Reply via email to