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

Re: [SVNMERGE][PATCH] svnmerge rollback

From: Madan U Sreenivasan <madan_at_collab.net>
Date: 2006-05-21 22:05:18 CEST

On Fri, 19 May 2006 22:20:15 +0530, Daniel Rall <dlr@collab.net> wrote:

> On Fri, 19 May 2006, Madan S. wrote:
> ...
>> Before I fix this and send another version of the patch, do you see
>> anything else that could be improved in the second version of the patch
>> that I just sent? I would like to wait for more comments before giving
>> one
>> final patch.
>
> It's ready.

Okay. I have implemented all the necessary feedback changes in the patch
attached herewith. This patch also takes into account the introduction of
the uninit command and handles the conflict that could arise.
I request the committers to do the honors!

[[[
Implement `svnmerge rollback'.
The `rollback' sub-command can be used to rollback previously integrated
revisions.

Review by: David James <djames@collab.net>
            Daniel Rall <dlr@collab.net>
            Giovanni Bajo <rasky@develer.com>

* contrib/client-side/svnmerge_test.py
   (TestCase_TestRepo.testRollbackWithoutInit):
   (TestCase_TestRepo.testRollbackOutsidePossibleRange):
   (TestCase_TestRepo.testRollbackWithoutRevisionOpt):
   (TestCase_TestRepo.testInitAndRollbackRecordOnly):
   (TestCase_TestRepo.testInitAndRollback):
   (TestCase_TestRepo.testMergeAndRollbackEmptyRevisionRange):
   (TestCase_TestRepo.testBlockMergeAndRollback):
   (TestCase_TestRepo.testMergeAndRollback):
   (TestCase_TestRepo.testBlockMergeAndRollback): New tests for rollback
    functionality.

* contrib/client-side/svnmerge.py
   (warn): New function to print warning message to stdout.
   (action_rollback): New function. Used to rollback the merges between
    the given revision numbers.
   (command_table): New entry for `rollback'.
]]]

Regards,
Madan.

Index: contrib/client-side/svnmerge_test.py
===================================================================
--- contrib/client-side/svnmerge_test.py (revision 19729)
+++ contrib/client-side/svnmerge_test.py (working copy)
@@ -857,6 +857,169 @@
 
         self.svnmerge("integrated", match=r"^3-20$")
 
+ def testRollbackWithoutInit(self):
+ """Rollback should error out if invoked prior to init"""
+
+ self.svnmerge("rollback -vv --head ../trunk",
+ error = True,
+ match = r"no integration info available for repository path")
+
+ def testRollbackOutsidePossibleRange(self):
+ """`svnmerge rollback' should error out if range contains revisions prior to
+ SOURCE creation date."""
+
+ # Initialize svnmerge
+ self.svnmerge2(["init", self.test_repo_url + "/trunk"])
+ self.launch("svn commit -F svnmerge-commit-message.txt",
+ match = r"Committed revision 14")
+ os.remove("svnmerge-commit-message.txt")
+
+ expected_error = r"""Specified revision range falls out of the rollback range."""
+ self.svnmerge("rollback -vv --head ../trunk -r 2-14",
+ error = True,
+ match = expected_error)
+
+ def testRollbackWithoutRevisionOpt(self):
+ """`svnmerge rollback' should error out if -r option is not given"""
+
+ # Initialize svnmerge
+ self.svnmerge2(["init", self.test_repo_url + "/trunk"])
+ self.launch("svn commit -F svnmerge-commit-message.txt",
+ match=r"Committed revision 14")
+ os.remove("svnmerge-commit-message.txt")
+
+ self.svnmerge("rollback -vv --head ../trunk",
+ error = True,
+ match = r"The '-r' option is mandatory for rollback")
+
+ def testInitAndRollbackRecordOnly(self):
+ """Init svnmerge, modify source head, merge, rollback --record-only."""
+
+ # Initialize svnmerge
+ self.svnmerge2(["init", self.test_repo_url + "/trunk"])
+ self.launch("svn commit -F svnmerge-commit-message.txt",
+ match = r"Committed revision 14")
+ os.remove("svnmerge-commit-message.txt")
+
+ # Rollback record-only
+ expected_output = r"property 'svnmerge-integrated' set on '.'"
+ detested_output = r"""
+D test2
+D test3"""
+ self.svnmerge("rollback -vv --record-only --head ../trunk -r5-7",
+ match = expected_output,
+ nonmatch = detested_output)
+
+ def testInitAndRollback(self):
+ """Init svnmerge, modify source head, merge, rollback."""
+
+ # Initialize svnmerge
+ self.svnmerge2(["init", self.test_repo_url + "/trunk"])
+ self.launch("svn commit -F svnmerge-commit-message.txt",
+ match = r"Committed revision 14")
+ os.remove("svnmerge-commit-message.txt")
+
+ # Svnmerge rollback r5-7
+ expected_output = r"""
+D test2
+D test3"""
+ self.svnmerge("rollback -vv --head ../trunk -r5-7",
+ match = expected_output)
+
+ def testMergeAndRollbackEmptyRevisionRange(self):
+ """Init svnmerge, modify source head, merge, rollback where no merge
+ occured."""
+
+ # Initialize svnmerge
+ self.svnmerge2(["init", self.test_repo_url + "/trunk"])
+ self.launch("svn commit -F svnmerge-commit-message.txt",
+ match = r"Committed revision 14")
+ os.remove("svnmerge-commit-message.txt")
+
+ # Make changes to trunk
+ os.chdir("../trunk")
+ open("newfile", "w").close()
+ self.launch("svn add newfile")
+ self.launch("svn commit -m 'Adding newfile'", match=r"Committed revision 15")
+ open("anothernewfile", "w").close()
+ self.launch("svn add anothernewfile")
+ self.launch("svn commit -m 'Adding anothernewfile'", match=r"Committed revision 16")
+
+ # Svnmerge block r15,16
+ os.chdir("../test-branch")
+ self.launch("svn up ..",
+ error = False)
+ self.svnmerge("block -r 15,16 --head ../trunk")
+ self.launch("svn commit -F svnmerge-commit-message.txt",
+ match = r"Committed revision 17")
+ self.svnmerge("merge --head ../trunk")
+ self.launch("svn commit -F svnmerge-commit-message.txt")
+
+ # Svnmerge rollback r15-16
+ self.svnmerge("rollback -vv --head ../trunk -r15-16",
+ error = False,
+ match = r"Nothing to rollback in revision range r15-16")
+
+ def testMergeAndRollback(self):
+ """Init svnmerge, modify source head, merge, rollback."""
+
+ # Initialize svnmerge
+ self.svnmerge2(["init", self.test_repo_url + "/trunk"])
+ self.launch("svn commit -F svnmerge-commit-message.txt",
+ match = r"Committed revision 14")
+ os.remove("svnmerge-commit-message.txt")
+
+ # Make changes to trunk
+ os.chdir("../trunk")
+ open("newfile", "w").close()
+ self.launch("svn add newfile")
+ self.launch("svn commit -m 'Adding newfile'", match=r"Committed revision 15")
+
+ # Svnmerge merge r15
+ os.chdir("../test-branch")
+ self.svnmerge("merge -r 15 --head ../trunk")
+ self.launch("svn commit -F svnmerge-commit-message.txt",
+ match = r"Committed revision 16")
+
+ # Svnmerge rollback r15
+ self.svnmerge("rollback -vv --head ../trunk -r15",
+ match = r"-r 15:14")
+
+ def testBlockMergeAndRollback(self):
+ """Init svnmerge, block, modify head, merge, rollback."""
+
+ # Initialize svnmerge
+ self.svnmerge2(["init", self.test_repo_url + "/trunk"])
+ self.launch("svn commit -F svnmerge-commit-message.txt",
+ match = r"Committed revision 14")
+ os.remove("svnmerge-commit-message.txt")
+
+ # Make changes to trunk
+ os.chdir("../trunk")
+ open("newfile", "w").close()
+ self.launch("svn add newfile")
+ self.launch("svn commit -m 'Adding newfile'", match=r"Committed revision 15")
+ open("anothernewfile", "w").close()
+ self.launch("svn add anothernewfile")
+ self.launch("svn commit -m 'Adding anothernewfile'", match=r"Committed revision 16")
+
+ # Svnmerge block r16, merge r15
+ os.chdir("../test-branch")
+ self.launch("svn up ..",
+ error = False)
+ self.svnmerge("block -r 16 --head ../trunk")
+ self.launch("svn commit -F svnmerge-commit-message.txt",
+ match = r"Committed revision 17")
+ self.svnmerge("merge --head ../trunk",
+ nonmatch = r"A anothernewfile",
+ match = r"A newfile")
+ self.launch("svn commit -F svnmerge-commit-message.txt",
+ match = r"Committed revision 18")
+
+ # Svnmerge rollback revision range 15-18 (in effect only 15,17)
+ self.svnmerge("rollback -vv --head ../trunk -r15-18",
+ nonmatch = r"D anothernewfile")
+
 if __name__ == "__main__":
     # If an existing template repository and working copy for testing
     # exists, then always remove it. This prevents any problems if
Index: contrib/client-side/svnmerge.py
===================================================================
--- contrib/client-side/svnmerge.py (revision 19729)
+++ contrib/client-side/svnmerge.py (working copy)
@@ -1205,6 +1205,83 @@
         f.close()
         report('wrote commit message to "%s"' % opts["commit-file"])
 
+def action_rollback(branch_dir, branch_props):
+ """Rollback previously integrated revisions."""
+
+ # Make sure the revision arguments are present
+ if not opts["revision"]:
+ error("The '-r' option is mandatory for rollback")
+
+ # Check branch directory is ready for being modified
+ check_dir_clean(branch_dir)
+
+ # Extract the integration info for the branch_dir
+ branch_props = get_merge_props(branch_dir)
+ check_old_prop_version(branch_dir, branch_props)
+ # Get the list of all revisions already merged into this source-path.
+ merged_revs = merge_props_to_revision_set(branch_props, opts["head-path"])
+
+ # At which revision was the src created?
+ oldest_src_rev = get_created_rev(opts["head-url"])
+ src_pre_exist_range = RevisionSet("1-%d" % oldest_src_rev)
+
+ # Limit to revisions specified by -r (if any)
+ revs = merged_revs & RevisionSet(opts["revision"])
+
+ # make sure theres some revision to rollback
+ if not revs:
+ report("Nothing to rollback in revision range r%s" % opts["revision"])
+ return
+
+ # If even one specified revision lies outside the lifetime of the
+ # source head, error out.
+ if revs & src_pre_exist_range:
+ err_str = "Specified revision range falls out of the rollback range.\n"
+ err_str += "%s was created at r%d" % (opts["head-path"], oldest_src_rev)
+ error(err_str)
+
+ record_only = opts["record-only"]
+
+ if record_only:
+ report('recording rollback of revision(s) %s from "%s"' %
+ (revs, opts["head-url"]))
+ else:
+ report('rollback of revision(s) %s from "%s"' %
+ (revs, opts["head-url"]))
+
+ # Do the reverse merge(s). Note: the starting revision number
+ # to 'svn merge' is NOT inclusive so we have to subtract one from start.
+ # We try to keep the number of merge operations as low as possible,
+ # because it is faster and reduces the number of conflicts.
+ rollback_intervals = minimal_merge_intervals(revs, [])
+ # rollback in the reverse order of merge
+ rollback_intervals.reverse()
+ for start, end in rollback_intervals:
+ if not record_only:
+ # Do the merge
+ svn_command("merge -r %d:%d %s %s" % \
+ (end, start - 1, opts["head-url"], branch_dir))
+
+ # Write out commit message if desired
+ # calculate the phantom revs first
+ if opts["commit-file"]:
+ f = open(opts["commit-file"], "w")
+ if record_only:
+ print >>f, 'Recorded rollback of revisions %s via %s from ' % \
+ (revs , NAME)
+ else:
+ print >>f, 'Rolled back revisions %s via %s from ' % \
+ (revs , NAME)
+ print >>f, '%s' % opts["head-url"]
+
+ f.close()
+ report('wrote commit message to "%s"' % opts["commit-file"])
+
+ # Update the set of merged revisions.
+ merged_revs = merged_revs - revs
+ branch_props[opts["head-path"]] = str(merged_revs)
+ set_merge_props(branch_dir, branch_props)
+
 def action_uninit(branch_dir, branch_props):
     """Uninit HEAD URL."""
     # Check branch directory is ready for being modified
@@ -1637,6 +1714,21 @@
         "-S",
     ]),
 
+ "rollback": (action_rollback,
+ "rollback [OPTION...] [PATH]",
+ """Rollback previously merged in revisions from PATH. The
+ --revision option is mandatory, and specifies which revisions
+ will be rolled back. Only the previously integrated merges
+ will be rolled back.
+
+ When manually rolling back changes, --record-only can be used to
+ instruct %s that a manual rollback of a certain revision
+ already happened, so that it can record it and offer that
+ revision for merge henceforth.""" % (NAME),
+ [
+ "-f", "-r", "-S", "-M", # import common opts
+ ]),
+
     "merge": (action_merge,
     "merge [OPTION...] [PATH]",
     """Merge in revisions into PATH from its head. If --revision is omitted,

Implement `svnmerge rollback'.
The `rollback' sub-command can be used to rollback previously integrated
revisions.

Review by: David James <djames@collab.net>
           Daniel Rall <dlr@collab.net>
           Giovanni Bajo <rasky@develer.com>

* contrib/client-side/svnmerge_test.py
  (TestCase_TestRepo.testRollbackWithoutInit):
  (TestCase_TestRepo.testRollbackOutsidePossibleRange):
  (TestCase_TestRepo.testRollbackWithoutRevisionOpt):
  (TestCase_TestRepo.testInitAndRollbackRecordOnly):
  (TestCase_TestRepo.testInitAndRollback):
  (TestCase_TestRepo.testMergeAndRollbackEmptyRevisionRange):
  (TestCase_TestRepo.testBlockMergeAndRollback):
  (TestCase_TestRepo.testMergeAndRollback):
  (TestCase_TestRepo.testBlockMergeAndRollback): New tests for rollback
   functionality.

* contrib/client-side/svnmerge.py
  (warn): New function to print warning message to stdout.
  (action_rollback): New function. Used to rollback the merges between
   the given revision numbers.
  (command_table): New entry for `rollback'.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Sun May 21 21:34:55 2006

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