[Svnmerge] Re: [PATCH] Add 'svnmerge.py integrated' command
Daniel Rall
dlr at collab.net
Fri Mar 31 10:17:03 PST 2006
I've committed a slight variation of this patch to Subversion trunk in
r19110.
On Thu, 30 Mar 2006, Daniel Rall wrote:
>
> [[[
> Add a new 'svnmerge.py integrated' command, which tells you which
> change sets have been merged into a branch (the inverse of 'avail').
>
> This is a precursor to implementation of the 'rollback' command, which
> requires some of the functionality of 'integrated' command, and
> facilitates smoother usability (since you'll often run 'integrated' to
> figure out what revisions to 'rollback').
>
>
> * contrib/client-side/svnmerge.py
> (branch_props_to_revision_set): New conversion helper function
> factored out of main() which turns branch properties from
> get_merge_props() for a specific path into a RevisionSet object.
>
> (display_revisions): New function factored out of action_avail()
> which shows a set of revisions in the requested style (numerically,
> log format, or as diffs).
>
> (action_avail): Leverage display_revisions().
>
> (action_integrated): New function implementing the 'integrated'
> command. Execution of 'svn log' will be refactored by David James
> later on.
>
> (command_table): Add support for 'integrated' command.
>
> (command_table, main): Add handling for the 'integrated' command.
> Leverage branch_props_to_revision_set().
>
>
> * contrib/client-side/svnmerge_test.py
> (testNoWc, testCheckNoIntegrationInfo, testBasic,
> testMergeRecordOnly): Add testing of the new "integrated" command.
>
>
> Review by: djames
> ]]]
>
>
> Index: contrib/client-side/svnmerge.py
> ===================================================================
> --- contrib/client-side/svnmerge.py (revision 19105)
> +++ contrib/client-side/svnmerge.py (working copy)
> @@ -498,6 +498,15 @@
> revs.update(rs._revs)
> return RevisionSet(revs)
>
> +def branch_props_to_revision_set(branch_props, path):
> + """A converter which returns a RevisionSet instance containing the
> + revisions from PATH as known to BRANCH_PROPS. BRANCH_PROPS is a
> + dictionary of path -> revision set branch integration information
> + (as returned by get_merge_props())."""
> + if not branch_props.has_key(path):
> + error('no integration info available for repository path "%s"' % path)
> + return RevisionSet(branch_props[path])
> +
> def dict_from_revlist_prop(propvalue):
> """Given a property value as a string containing per-head revision
> lists, return a dictionary whose key is a relative path to a head
> @@ -873,6 +882,33 @@
> ret.reverse()
> return ret
>
> +def display_revisions(revs, display_style, head_url):
> + """Show REVS as dictated by DISPLAY_STYLE, either numerically, in
> + log format, or as diffs."""
> + if display_style == "revisions":
> + if revs:
> + report("available revisions to be merged are:")
> + print revs
> + elif display_style == "logs":
> + for start,end in revs.normalized():
> + svn_command('log --incremental -v -r %d:%d %s' % \
> + (start, end, head_url))
> + elif display_style == "diffs":
> + for start, end in revs.normalized():
> + print
> + if start == end:
> + print "%s: changes in revision %d follow" % (NAME, start)
> + else:
> + print "%s: changes in revisions %d-%d follow" % (NAME,
> + start, end)
> + print
> +
> + # Note: the starting revision number to 'svn diff' is
> + # NOT inclusive so we have to subtract one from ${START}.
> + svn_command("diff -r %d:%d %s" % (start - 1, end, head_url))
> + else:
> + assert False, "unhandled display style: %s" % display_style
> +
> def action_init(branch_dir, branch_props):
> """Initialize a branch for merges."""
> # Check branch directory is ready for being modified
> @@ -923,33 +959,43 @@
> if opts["revision"]:
> revs = revs & RevisionSet(opts["revision"])
>
> - # Show them, either numerically, in log format, or as diffs
> - if opts["avail-display"] == "revisions":
> - if revs:
> - report("available revisions to be merged are:")
> - print revs
> - elif opts["avail-display"] == "logs":
> - for start,end in revs.normalized():
> - svn_command('log --incremental -v -r %d:%d %s' % \
> - (start, end, opts["head-url"]))
> - elif opts["avail-display"] == "diffs":
> - for start,end in revs.normalized():
> - print
> - if start == end:
> - print "%s: changes in revision %d follow" % (NAME, start)
> - else:
> - print "%s: changes in revisions %d-%d follow" % (NAME,
> - start, end)
> - print
> + display_revisions(revs, opts["avail-display"], opts["head-url"])
>
> - # Note: the starting revision number to 'svn diff' is
> - # NOT inclusive so we have to subtract one from ${START}.
> - svn_command('diff -r %d:%d %s' % \
> - (start-1, end, opts["head-url"]))
> - else:
> - assert False, \
> - "unhandled 'avail' display type: %s" % opts["avail-display"]
> +def action_integrated(branch_dir, branch_props):
> + """Show change sets already merged. This set of revisions is
> + calculated from taking svnmerge-integrated property from the
> + branch, and subtracting any revision older than the branch
> + creation revision."""
> + # Extract the integration info for the branch_dir
> + branch_props = get_merge_props(branch_dir)
> + check_old_prop_version(branch_dir, branch_props)
> + revs = branch_props_to_revision_set(branch_props, opts["head-path"])
>
> + # Lookup the oldest revision on the branch path.
> + ### TODO: Refactor this to use a modified RevisionLog class.
> + oldest_branch_rev = -1
> + lines = None
> + try:
> + lines = launchsvn("log -r 1:HEAD --limit=1 -q " + branch_dir)
> + except LaunchError, e:
> + # Assume that --limit isn't supported by the installed 'svn'.
> + lines = launchsvn("log -r 1:HEAD -q " + branch_dir)
> + if lines and len(lines) > 1:
> + i = lines[1].find(" ")
> + if i != -1:
> + oldest_branch_rev = int(lines[1][1:i])
> + if oldest_branch_rev == -1:
> + error("unable to determine oldest branch revision")
> +
> + # Subtract any revisions which pre-date the branch.
> + revs = revs - range(1, oldest_branch_rev)
> +
> + # Limit to revisions specified by -r (if any)
> + if opts["revision"]:
> + revs = revs & RevisionSet(opts["revision"])
> +
> + display_revisions(revs, opts["integrated-display"], opts["head-url"])
> +
> def action_merge(branch_dir, branch_props):
> """Record merge meta data, and do the actual merge (if not
> requested otherwise via --record-only)."""
> @@ -1487,6 +1533,25 @@
> "-S",
> ]),
>
> + "integrated": (action_integrated,
> + "integrated [OPTION...] [PATH]",
> + """Show merged revisions available for PATH as a revision list.
> + If --revision is given, the revisions shown will be limited to
> + those also specified in the option.""",
> + [
> + Option("-d", "--diff",
> + dest="integrated-display",
> + value="diffs",
> + default="revisions",
> + help="show corresponding diff instead of revision list"),
> + Option("-l", "--log",
> + dest="integrated-display",
> + value="logs",
> + help="show corresponding log history instead of revision list"),
> + "-r",
> + "-S",
> + ]),
> +
> "merge": (action_merge,
> "merge [OPTION...] [PATH]",
> """Merge in revisions into PATH from its head. If --revision is omitted,
> @@ -1560,7 +1625,7 @@
> head = args[0]
> elif len(args) > 1:
> optsparser.error("wrong number of parameters", cmd)
> - elif str(cmd) in ["avail", "merge", "block", "unblock"]:
> + elif str(cmd) in ["avail", "integrated", "merge", "block", "unblock"]:
> if len(args) == 1:
> branch_dir = args[0]
> elif len(args) > 1:
> @@ -1614,13 +1679,9 @@
>
> # Get previously merged revisions (except when command is init)
> if str(cmd) != "init":
> - if not branch_props.has_key(opts["head-path"]):
> - error('no integration info available for repository path "%s"'
> - % opts["head-path"])
> + opts["merged-revs"] = branch_props_to_revision_set(branch_props,
> + opts["head-path"])
>
> - revs = branch_props[opts["head-path"]]
> - opts["merged-revs"] = RevisionSet(revs)
> -
> # Perform the action
> cmd(branch_dir, branch_props)
>
> Index: contrib/client-side/svnmerge_test.py
> ===================================================================
> --- contrib/client-side/svnmerge_test.py (revision 19099)
> +++ contrib/client-side/svnmerge_test.py (working copy)
> @@ -421,12 +421,14 @@
> os.chdir("foo")
> self.svnmerge("init", error=True, match="working dir")
> self.svnmerge("avail", error=True, match="working dir")
> + self.svnmerge("integrated", error=True, match="working dir")
> self.svnmerge("merge", error=True, match="working dir")
> self.svnmerge("block", error=True, match="working dir")
> self.svnmerge("unblock", error=True, match="working dir")
>
> def testCheckNoIntegrationInfo(self):
> self.svnmerge("avail", error=True, match="no integration")
> + self.svnmerge("integrated", error=True, match="no integration")
> self.svnmerge("merge", error=True, match="no integration")
> self.svnmerge("block", error=True, match="no integration")
> self.svnmerge("unblock", error=True, match="no integration")
> @@ -491,6 +493,10 @@
> out = self.svnmerge("avail --diff -r5")
> self.assertEqual(out.strip(), "")
>
> + self.svnmerge("integrated", match=r"^3-6$")
> + self.svnmerge("integrated --log -r5", match=r"| r5 ")
> + self.svnmerge("integrated --diff -r5", match=r"Index: test2")
> +
> def test_log_msg_suggest(self):
> self.svnmerge("init -vf commit-log.txt", match="wrote commit message")
> self.assert_(os.path.exists("commit-log.txt"))
> @@ -529,6 +535,8 @@
> nonmatch=r"svn merge -r 8:9")
> out = self.svnmerge("avail -r9")
> self.assertEqual(out.strip(), "")
> + self.svnmerge("integrated", match=r"^3-6,9$")
> + self.svnmerge("integrated -r9", match=r"^9$")
>
> def testBidirectionalMerges(self):
> """Check that reflected revisions are recognized properly for bidirectional merges."""
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url : /pipermail/svnmerge/attachments/20060331/315be0d2/attachment.pgp
More information about the Svnmerge
mailing list