[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