[Pkg-bazaar-commits] ./bzr-builddeb/trunk r363: Merge merge-package command from Muharem.

James Westby james.westby at canonical.com
Mon Aug 24 17:33:44 UTC 2009


------------------------------------------------------------
revno: 363 [merge]
committer: James Westby <james.westby at canonical.com>
branch nick: trunk
timestamp: Mon 2009-08-24 18:33:44 +0100
message:
  Merge merge-package command from Muharem.
  
  merge-package allows you to merge another package branch, ensuring that
  there aren't spurious conflicts due to divergence in the upstream ancestry.
added:
  merge_package.py
  tests/test_merge_package.py
modified:
  __init__.py
  cmds.py
  errors.py
  import_dsc.py
  tests/__init__.py
  upstream.py
-------------- next part --------------
=== modified file '__init__.py'
--- a/__init__.py	2009-07-26 15:51:02 +0000
+++ b/__init__.py	2009-08-24 17:33:44 +0000
@@ -39,7 +39,8 @@
         "merge_upstream": ["mu"],
         "import_dsc": [],
         "bd_do": [],
-        "mark_uploaded": []
+        "mark_uploaded": [],
+        "merge_package": [],
         }
 
 for command, aliases in commands.iteritems():

=== modified file 'cmds.py'
--- a/cmds.py	2009-08-18 20:11:05 +0000
+++ b/cmds.py	2009-08-24 17:33:44 +0000
@@ -69,6 +69,7 @@
         DscCache,
         DscComp,
         )
+from bzrlib.plugins.builddeb.merge_package import fix_ancestry_as_needed
 from bzrlib.plugins.builddeb.source_distiller import (
         FullSourceDistiller,
         MergeModeDistiller,
@@ -722,9 +723,9 @@
                                 "the previous upstream version, %s, in the "
                                 "branch: %s" % (last_version,
                                     db.upstream_tag_name(last_version)))
-                    upstream_tip = db._revid_of_upstream_version_from_branch(
+                    upstream_tip = db.revid_of_upstream_version_from_branch(
                             last_version)
-                    db._extract_upstream_tree(upstream_tip, tempdir)
+                    db.extract_upstream_tree(upstream_tip, tempdir)
                 else:
                     db._create_empty_upstream_tree(tempdir)
                 self.import_many(db, files_list, orig_target)
@@ -873,6 +874,45 @@
             t.unlock()
 
 
+class cmd_merge_package(Command):
+    """Merges source packaging branch into target packaging branch.
+
+    This will first check whether the upstream branches have diverged.
+
+    If that's the case an attempt will be made to fix the upstream ancestry
+    so that the user only needs to deal wth packaging branch merge issues.
+
+    In the opposite case a normal merge will be performed.
+    """
+    takes_args = ['source']
+
+    def run(self, source):
+        source_branch = target_branch = None
+        # Get the target branch.
+        try:
+            tree = WorkingTree.open_containing('.')[0]
+            target_branch = tree.branch
+        except NotBranchError:
+            raise BzrCommandError(
+                "There is no tree to merge the source branch in to")
+        # Get the source branch.
+        try:
+            source_branch = Branch.open(source)
+        except NotBranchError:
+            raise BzrCommandError("Invalid source branch URL?")
+
+        fix_ancestry_as_needed(tree, source_branch)
+
+        # Merge source packaging branch in to the target packaging branch.
+        conflicts = tree.merge_from_branch(source_branch)
+        if conflicts > 0:
+            info('The merge resulted in %s conflicts. Please resolve these '
+                 'and commit the changes with "bzr commit".' % conflicts)
+        else:
+            info('The merge resulted in no conflicts. You may commit the '
+            'changes by running "bzr commit".')
+
+
 class cmd_test_builddeb(Command):
     """Run the builddeb test suite"""
 
@@ -883,4 +923,3 @@
         passed = selftest(test_suite_factory=test_suite)
         # invert for shell exit code rules
         return not passed
-

=== modified file 'errors.py'
--- a/errors.py	2009-04-16 09:30:49 +0000
+++ b/errors.py	2009-08-24 16:27:40 +0000
@@ -175,3 +175,12 @@
 
     def __init__(self, error):
         BzrError.__init__(self, error=error)
+
+
+class SharedUpstreamConflictsWithTargetPackaging(BzrError):
+    _fmt = ('The upstream branches for the merge source and target have '
+            'diverged. Unfortunately, the attempt to fix this problem '
+            'resulted in conflicts. Please resolve these, commit and '
+            're-run the "merge-package" command to finish. '
+            'Alternatively, until you commit you can use "bzr revert" to '
+            'restore the state of the unmerged branch.')

=== modified file 'import_dsc.py'
--- a/import_dsc.py	2009-07-26 16:44:17 +0000
+++ b/import_dsc.py	2009-08-19 12:22:29 +0000
@@ -1570,11 +1570,12 @@
         finally:
             shutil.rmtree(tempdir)
 
-    def _extract_upstream_tree(self, upstream_tip, basedir):
+    def extract_upstream_tree(self, upstream_tip, basedir):
         # Extract that to a tempdir so we can get a working
         # tree for it.
         # TODO: should stack rather than trying to use the repository,
         # as that will be more efficient.
+        # TODO: remove the _extract_upstream_tree alias below.
         to_location = os.path.join(basedir, "upstream")
         dir_to = self.branch.bzrdir.sprout(to_location,
                 revision_id=upstream_tip,
@@ -1582,6 +1583,8 @@
         self.upstream_tree = dir_to.open_workingtree()
         self.upstream_branch = self.upstream_tree.branch
 
+    _extract_upstream_tree = extract_upstream_tree
+
     def _create_empty_upstream_tree(self, basedir):
         to_location = os.path.join(basedir, "upstream")
         to_transport = get_transport(to_location)
@@ -1615,7 +1618,11 @@
             shutil.rmtree(tempdir)
             raise
 
-    def _revid_of_upstream_version_from_branch(self, version):
+        """The private method below will go away eventually."""
+        return self.revid_of_upstream_version_from_branch(version)
+
+    def revid_of_upstream_version_from_branch(self, version):
+        # TODO: remove the _revid_of_upstream_version_from_branch alias below.
         assert isinstance(version, str)
         tag_name = self.upstream_tag_name(version)
         if self._has_version(self.branch, tag_name):
@@ -1629,6 +1636,8 @@
         tag_name = self.upstream_tag_name(version)
         return self.branch.tags.lookup_tag(tag_name)
 
+    _revid_of_upstream_version_from_branch = revid_of_upstream_version_from_branch
+
     def merge_upstream(self, tarball_filename, version, previous_version,
             upstream_branch=None, upstream_revision=None, merge_type=None):
         assert self.upstream_branch is None, \
@@ -1639,14 +1648,14 @@
             if previous_version is not None:
                 if self.has_upstream_version_in_packaging_branch(
                         previous_version.upstream_version):
-                    upstream_tip = self._revid_of_upstream_version_from_branch(
+                    upstream_tip = self.revid_of_upstream_version_from_branch(
                             previous_version.upstream_version)
-                    self._extract_upstream_tree(upstream_tip, tempdir)
+                    self.extract_upstream_tree(upstream_tip, tempdir)
                 elif (upstream_branch is not None and 
                       previous_upstream_revision is not None):
                     upstream_tip = RevisionSpec.from_string(previous_upstream_revision).as_revision_id(upstream_branch)
                     assert isinstance(upstream_tip, str)
-                    self._extract_upstream_tree(upstream_tip, tempdir)
+                    self.extract_upstream_tree(upstream_tip, tempdir)
                 else:
                     raise BzrCommandError("Unable to find the tag for the "
                             "previous upstream version, %s, in the branch: "

=== added file 'merge_package.py'
--- a/merge_package.py	1970-01-01 00:00:00 +0000
+++ b/merge_package.py	2009-08-24 10:08:59 +0000
@@ -0,0 +1,168 @@
+#    merge_package.py -- The plugin for bzr
+#    Copyright (C) 2009 Canonical Ltd.
+#
+#    :Author: Muharem Hrnjadovic <muharem at ubuntu.com>
+#
+#    This file is part of bzr-builddeb.
+#
+#    bzr-builddeb is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    bzr-builddeb is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with bzr-builddeb; if not, write to the Free Software
+#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#
+
+import os
+import re
+import sys
+import tempfile
+
+from debian_bundle.changelog import Version
+
+from bzrlib import errors
+
+from bzrlib.plugins.builddeb.import_dsc import DistributionBranch
+from bzrlib.plugins.builddeb.util import find_changelog
+
+
+def _latest_version(branch):
+    """Version of the most recent source package upload in the given `branch`.
+    
+    :param branch: A Branch object containing the source upload of interest.
+    """
+    changelog, _ignore = find_changelog(branch.basis_tree(), False)
+
+    return changelog.version
+
+
+def _upstream_version_data(source, target):
+    """Most recent upstream versions/revision IDs of the merge source/target.
+
+    Please note: both packaging branches must have been read-locked
+    beforehand.
+
+    :param source: The merge source branch.
+    :param target: The merge target branch.
+    """
+    results = list()
+    for branch in (source, target):
+        db = DistributionBranch(branch, branch)
+        uver = _latest_version(branch).upstream_version
+        results.append((uver, db.revid_of_upstream_version_from_branch(uver)))
+
+    return results
+
+
+def fix_ancestry_as_needed(tree, source):
+    """Manipulate the merge target's ancestry to avoid upstream conflicts.
+
+    Merging J->I given the following ancestry tree is likely to result in
+    upstream merge conflicts:
+
+    debian-upstream                 ,------------------H
+                       A-----------B                    \
+    ubuntu-upstream     \           \`-------G           \
+                         \           \        \           \
+    debian-packaging      \ ,---------D--------\-----------J
+                           C           \        \
+    ubuntu-packaging        `----E------F--------I
+
+    Here there was a new upstream release (G) that Ubuntu packaged (I), and
+    then another one that Debian packaged, skipping G, at H and J.
+
+    Now, the way to solve this is to introduce the missing link.
+
+    debian-upstream                 ,------------------H------.
+                       A-----------B                    \      \
+    ubuntu-upstream     \           \`-------G-----------\------K
+                         \           \        \           \
+    debian-packaging      \ ,---------D--------\-----------J
+                           C           \        \
+    ubuntu-packaging        `----E------F--------I
+
+    at K, which isn't a real merge, as we just use the tree from H, but add
+    G as a parent and then we merge that in to Ubuntu.
+
+    debian-upstream                 ,------------------H------.
+                       A-----------B                    \      \
+    ubuntu-upstream     \           \`-------G-----------\------K
+                         \           \        \           \      \
+    debian-packaging      \ ,---------D--------\-----------J      \
+                           C           \        \                  \
+    ubuntu-packaging        `----E------F--------I------------------L
+
+    At this point we can merge J->L to merge the Debian and Ubuntu changes.
+
+    :param tree: The `WorkingTree` of the merge target branch.
+    :param source: The merge source (packaging) branch.
+    """
+    upstreams_diverged = False
+    t_upstream_reverted = False
+    target = tree.branch
+
+    source.lock_read()
+    try:
+        tree.lock_write()
+        try:
+            # "Unpack" the upstream versions and revision ids for the merge
+            # source and target branch respectively.
+            [(us_ver, us_revid), (ut_ver, ut_revid)] = _upstream_version_data(source, target)
+
+            # Did the upstream branches of the merge source/target diverge?
+            graph = source.repository.get_graph(target.repository)
+            upstreams_diverged = (len(graph.heads([us_revid, ut_revid])) > 1)
+
+            # No, we're done!
+            if not upstreams_diverged:
+                return (upstreams_diverged, t_upstream_reverted)
+
+            # Instantiate a `DistributionBranch` object for the merge target
+            # (packaging) branch.
+            db = DistributionBranch(tree.branch, tree.branch)
+            tempdir = tempfile.mkdtemp(dir=os.path.join(tree.basedir, '..'))
+
+            # Extract the merge target's upstream tree into a temporary
+            # directory.
+            db.extract_upstream_tree(ut_revid, tempdir)
+            tmp_target_utree = db.upstream_tree
+
+            # Merge upstream branch tips to obtain a shared upstream parent.
+            # This will add revision K (see graph above) to a temporary merge
+            # target upstream tree.
+            tmp_target_utree.lock_write()
+            try:
+                if us_ver > ut_ver:
+                    # The source upstream tree is more recent and the
+                    # temporary target tree needs to be reshaped to match it.
+                    tmp_target_utree.revert(
+                        None, source.repository.revision_tree(us_revid))
+                    t_upstream_reverted = True
+
+                tmp_target_utree.set_parent_ids((ut_revid, us_revid))
+                tmp_target_utree.commit(
+                    'Prepared upstream tree for merging into target branch.')
+            finally:
+                tmp_target_utree.unlock()
+
+            # Merge shared upstream parent into the target merge branch. This
+            # creates revison L in the digram above.
+            conflicts = tree.merge_from_branch(tmp_target_utree.branch)
+            if conflicts > 0:
+                raise errors.SharedUpstreamConflictsWithTargetPackaging()
+            else:
+                tree.commit('Merging shared upstream rev into target branch.')
+
+        finally:
+            tree.unlock()
+    finally:
+        source.unlock()
+
+    return (upstreams_diverged, t_upstream_reverted)

=== modified file 'tests/__init__.py'
--- a/tests/__init__.py	2009-07-04 20:45:01 +0000
+++ b/tests/__init__.py	2009-08-19 09:56:50 +0000
@@ -118,6 +118,7 @@
             'test_config',
             'test_hooks',
             'test_import_dsc',
+            'test_merge_package',
             'test_merge_upstream',
             'test_repack_tarball_extra',
             'test_revspec',

=== added file 'tests/test_merge_package.py'
--- a/tests/test_merge_package.py	1970-01-01 00:00:00 +0000
+++ b/tests/test_merge_package.py	2009-08-24 10:47:13 +0000
@@ -0,0 +1,537 @@
+#!/usr/bin/env python
+# -*- coding: iso-8859-15 -*-
+#    test_merge_package.py -- Merge packaging branches, fix ancestry as needed.
+#    Copyright (C) 2008 Canonical Ltd.
+#
+#    This file is part of bzr-builddeb.
+#
+#    bzr-builddeb is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    bzr-builddeb is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with bzr-builddeb; if not, write to the Free Software
+#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import string
+import unittest
+
+from debian_bundle.changelog import Version
+
+from bzrlib.errors import ConflictsInTree
+from bzrlib.tests import TestCaseWithTransport
+
+from bzrlib.plugins.builddeb import merge_package as MP
+from bzrlib.plugins.builddeb.import_dsc import DistributionBranch
+
+_Debian_changelog = '''\
+ipsec-tools (%s) unstable; urgency=high
+
+  * debian packaging -- %s
+
+ -- Nico Golde <nion at debian.org>  Tue, %02d May 2009 13:26:14 +0200
+
+'''
+
+_Ubuntu_changelog = '''\
+ipsec-tools (%s) karmic; urgency=low
+
+  * ubuntu packaging -- %s
+
+ -- Jamie Strandboge <jamie at ubuntu.com>  Fri, %02d Jul 2009 13:24:17 -0500
+
+'''
+
+
+def _prepend_log(text, path):
+    content = open(path).read()
+    fh = open(path, 'wb')
+    try:
+        fh.write(text+content)
+    finally:
+        fh.close()
+
+
+class MergePackageTests(TestCaseWithTransport):
+
+    def test_latest_upstream_versions(self):
+        """Check correctness of upstream version computation."""
+        ubup_o, debp_n, _ubuu, _debu = self._setup_debian_upstream_newer()
+        # Ubuntu upstream.
+        self.assertEquals(
+            MP._latest_version(ubup_o).upstream_version, '1.1.2')
+        # Debian upstream.
+        self.assertEquals(
+            MP._latest_version(debp_n).upstream_version, '2.0')
+
+        ubuntup, debianp = self._setup_upstreams_not_diverged()
+        # Ubuntu upstream.
+        self.assertEquals(
+            MP._latest_version(ubuntup).upstream_version, '1.4')
+        # Debian upstream.
+        self.assertEquals(
+            MP._latest_version(debianp).upstream_version, '2.2')
+
+    def test_debian_upstream_newer(self):
+        """Diverging upstreams (debian newer) don't cause merge conflicts.
+
+        The debian and ubuntu upstream branches will differ with regard to
+        the content of the file 'c'.
+
+        Furthermore the respective packaging branches will have a text
+        conflict in 'debian/changelog'.
+
+        The upstream conflict will be resolved by fix_ancestry_as_needed().
+        Please note that the debian ancestry is more recent.
+        """
+        ubup, debp, ubuu, debu = self._setup_debian_upstream_newer()
+
+        # Attempt a plain merge first.
+        conflicts = ubup.merge_from_branch(
+            debp.branch, to_revision=self.revid_debp_n_C)
+
+        # There are two conflicts in the 'c' and the 'debian/changelog' files
+        # respectively.
+        self.assertEquals(conflicts, 2)
+        conflict_paths = sorted([c.path for c in ubup.conflicts()])
+        self.assertEquals(conflict_paths, [u'c.moved', u'debian/changelog'])
+
+        # Undo the failed merge.
+        ubup.revert()
+
+        # Check the versions present in the tree with the fixed ancestry.
+        v3 = "1.1.2"
+        v4 = "2.0"
+        db1 = DistributionBranch(ubup.branch, ubup.branch)
+        self.assertEqual(db1.has_upstream_version(v3), True)
+        # This version is in the diverged debian upstream tree and will
+        # hence not be present in the target ubuntu packaging branch.
+        self.assertEqual(db1.has_upstream_version(v4), False)
+
+        # The ubuntu upstream branch tip.
+        ubuu_tip = ubuu.branch.revision_history()[-1]
+        # The debian upstream branch tip.
+        debu_tip = debu.branch.revision_history()[-1]
+        # The ubuntu packaging branch tip.
+        ubup_tip_pre_fix = ubup.branch.revision_history()[-1]
+
+        # The first conflict is resolved by calling fix_ancestry_as_needed().
+        upstreams_diverged, t_upstream_reverted = MP.fix_ancestry_as_needed(ubup, debp.branch)
+
+        # The ancestry did diverge and needed to be fixed.
+        self.assertEquals(upstreams_diverged, True)
+        # The (temporary) target upstream branch had to be reverted to the
+        # source upstream branch since the latter was more recent.
+        self.assertEquals(t_upstream_reverted, True)
+
+        # Check the versions present in the tree with the fixed ancestry.
+        db2 = DistributionBranch(ubup.branch, ubup.branch)
+        self.assertEqual(db2.has_upstream_version(v3), True)
+        # The ancestry has been fixed and the missing debian upstream
+        # version should now be present in the target ubuntu packaging
+        # branch.
+        self.assertEqual(db2.has_upstream_version(v4), True)
+
+        # Now let's take a look at the fixed ubuntu packaging branch.
+        ubup_tip_post_fix = ubup.branch.revision_history()[-1]
+        ubup_parents_post_fix = ubup.branch.repository.revision_tree(ubup_tip_post_fix).get_parent_ids()
+
+        # The tip of the fixed ubuntu packaging branch has 2 parents.
+        self.assertEquals(len(ubup_parents_post_fix), 2)
+
+        # The left parent is the packaging branch tip before fixing.
+        self.assertEquals(ubup_parents_post_fix[0], ubup_tip_pre_fix)
+
+        # The right parent is derived from a merge 
+        ubup_parents_sharedupstream = ubup.branch.repository.revision_tree(ubup_parents_post_fix[1]).get_parent_ids()
+        self.assertEquals(ubup_parents_sharedupstream, [ubuu_tip, debu_tip])
+
+        # Try merging again.
+        conflicts = ubup.merge_from_branch(
+            debp.branch, to_revision=self.revid_debp_n_C)
+
+        # And, voila, only the packaging branch conflict remains.
+        self.assertEquals(conflicts, 1)
+        conflict_paths = sorted([c.path for c in ubup.conflicts()])
+        self.assertEquals(conflict_paths, [u'debian/changelog'])
+
+    def test_debian_upstream_older(self):
+        """Diverging upstreams (debian older) don't cause merge conflicts.
+
+        The debian and ubuntu upstream branches will differ with regard to
+        the content of the file 'c'.
+
+        Furthermore the respective packaging branches will have a text
+        conflict in 'debian/changelog'.
+
+        The upstream conflict will be resolved by fix_ancestry_as_needed().
+        Please note that the debian ancestry is older in this case.
+        """
+        ubup, debp, _ubuu, _debu = self._setup_debian_upstream_older()
+
+        # Attempt a plain merge first.
+        conflicts = ubup.merge_from_branch(
+            debp.branch, to_revision=self.revid_debp_o_C)
+
+        # There are two conflicts in the 'c' and the 'debian/changelog' files
+        # respectively.
+        self.assertEquals(conflicts, 2)
+        conflict_paths = sorted([c.path for c in ubup.conflicts()])
+        self.assertEquals(conflict_paths, [u'c.moved', u'debian/changelog'])
+
+        # Undo the failed merge.
+        ubup.revert()
+
+        # The first conflict is resolved by calling fix_ancestry_as_needed().
+        upstreams_diverged, t_upstream_reverted = MP.fix_ancestry_as_needed(ubup, debp.branch)
+
+        # The ancestry did diverge and needed to be fixed.
+        self.assertEquals(upstreams_diverged, True)
+        # The target upstream branch was more recent in this case and hence
+        # was not reverted to the source upstream branch.
+        self.assertEquals(t_upstream_reverted, False)
+
+        # Try merging again.
+        conflicts = ubup.merge_from_branch(
+            debp.branch, to_revision=self.revid_debp_o_C)
+
+        # And, voila, only the packaging branch conflict remains.
+        self.assertEquals(conflicts, 1)
+        conflict_paths = sorted([c.path for c in ubup.conflicts()])
+        self.assertEquals(conflict_paths, [u'debian/changelog'])
+
+    def test_upstreams_not_diverged(self):
+        """Non-diverging upstreams result in a normal merge.
+
+        The debian and ubuntu upstream branches will not have diverged
+        this time.
+
+        The packaging branches will have a conflict in 'debian/changelog'.
+        fix_ancestry_as_needed() will return as soon as establishing that
+        the upstreams have not diverged.
+        """
+        ubuntup, debianp = self._setup_upstreams_not_diverged()
+
+        # Attempt a plain merge first.
+        conflicts = ubuntup.merge_from_branch(
+            debianp.branch, to_revision=self.revid_debianp_C)
+
+        # There is only a conflict in the 'debian/changelog' file.
+        self.assertEquals(conflicts, 1)
+        conflict_paths = sorted([c.path for c in ubuntup.conflicts()])
+        self.assertEquals(conflict_paths, [u'debian/changelog'])
+
+        # Undo the failed merge.
+        ubuntup.revert()
+
+        # The conflict is *not* resolved by calling fix_ancestry_as_needed().
+        upstreams_diverged, t_upstream_reverted = MP.fix_ancestry_as_needed(ubuntup, debianp.branch)
+
+        # The ancestry did *not* diverge.
+        self.assertEquals(upstreams_diverged, False)
+        # The upstreams have not diverged, hence no need to fix/revert 
+        # either of them.
+        self.assertEquals(t_upstream_reverted, False)
+
+        # Try merging again.
+        conflicts = ubuntup.merge_from_branch(
+            debianp.branch, to_revision=self.revid_debianp_C)
+
+        # The packaging branch conflict we saw above is still there.
+        self.assertEquals(conflicts, 1)
+        conflict_paths = sorted([c.path for c in ubuntup.conflicts()])
+        self.assertEquals(conflict_paths, [u'debian/changelog'])
+
+    def _setup_debian_upstream_newer(self):
+        """
+        Set up the following test configuration (debian upstream newer).
+
+        debian-upstream                 ,------------------H
+                           A-----------B                    \
+        ubuntu-upstream     \           \`-------G           \
+                             \           \        \           \
+        debian-packaging      \ ,---------D--------\-----------J
+                               C                    \
+        ubuntu-packaging        `----E---------------I
+
+        where:
+             - A = 1.0
+             - B = 1.1
+             - H = 2.0
+
+             - G = 1.1.2
+
+             - C = 1.0-1
+             - D = 1.1-1
+             - J = 2.0-1
+
+             - E = 1.0-1ubuntu1
+             - I = 1.1.2-0ubuntu1
+
+        Please note that the debian and ubuntu branches will have a conflict
+        with respect to the file 'c'.
+        """
+        # Set up the debian upstream branch.
+        name = 'debu-n'
+        vdata = [
+            ('upstream-1.0', ('a',), None, None),
+            ('upstream-1.1', ('b',), None, None),
+            ('upstream-2.0', ('c',), None, None),
+            ]
+        debu_n = self._setup_branch(name, vdata)
+
+        # Set up the debian packaging branch.
+        name = 'debp-n'
+        debp_n = self.make_branch_and_tree(name)
+        debp_n.pull(debu_n.branch, stop_revision=self.revid_debu_n_A)
+
+        vdata = [
+            ('1.0-1', ('debian/', 'debian/changelog'), None, None),
+            ('1.1-1', ('o',), debu_n, self.revid_debu_n_B),
+            ('2.0-1', ('p',), debu_n, self.revid_debu_n_C),
+            ]
+        self._setup_branch(name, vdata, debp_n, 'd')
+
+        # Set up the ubuntu upstream branch.
+        name = 'ubuu-o'
+        ubuu_o = debu_n.bzrdir.sprout(
+            name, revision_id=self.revid_debu_n_B).open_workingtree()
+
+        vdata = [
+            ('upstream-1.1.2', ('c',), None, None),
+            ]
+        self._setup_branch(name, vdata, ubuu_o)
+
+        # Set up the ubuntu packaging branch.
+        name = 'ubup-o'
+        ubup_o = debu_n.bzrdir.sprout(
+            name, revision_id=self.revid_debu_n_A).open_workingtree()
+
+        vdata = [
+            ('1.0-1ubuntu1', (), debp_n, self.revid_debp_n_A),
+            ('1.1.2-0ubuntu1', (), ubuu_o, self.revid_ubuu_o_A),
+            ]
+        self._setup_branch(name, vdata, ubup_o, 'u')
+
+        # Return the ubuntu and the debian packaging branches.
+        return (ubup_o, debp_n, ubuu_o, debu_n)
+
+    def _setup_debian_upstream_older(self):
+        """
+        Set up the following test configuration (debian upstream older).
+
+        debian-upstream                 ,----H-------------.
+                           A-----------B                    \
+        ubuntu-upstream     \           \`-----------G       \
+                             \           \            \       \
+        debian-packaging      \ ,---------D------------\-------J
+                               C                        \
+        ubuntu-packaging        `----E-------------------I
+
+        where:
+             - A = 1.0
+             - B = 1.1
+             - H = 1.1.3
+
+             - G = 2.1
+
+             - C = 1.0-1
+             - D = 1.1-1
+             - J = 1.1.3-1
+
+             - E = 1.0-1ubuntu1
+             - I = 2.1-0ubuntu1
+
+        Please note that the debian and ubuntu branches will have a conflict
+        with respect to the file 'c'.
+        """
+        # Set up the debian upstream branch.
+        name = 'debu-o'
+        vdata = [
+            ('upstream-1.0', ('a',), None, None),
+            ('upstream-1.1', ('b',), None, None),
+            ('upstream-1.1.3', ('c',), None, None),
+            ]
+        debu_o = self._setup_branch(name, vdata)
+
+        # Set up the debian packaging branch.
+        name = 'debp-o'
+        debp_o = self.make_branch_and_tree(name)
+        debp_o.pull(debu_o.branch, stop_revision=self.revid_debu_o_A)
+
+        vdata = [
+            ('1.0-1', ('debian/', 'debian/changelog'), None, None),
+            ('1.1-1', ('o',), debu_o, self.revid_debu_o_B),
+            ('1.1.3-1', ('p',), debu_o, self.revid_debu_o_C),
+            ]
+        self._setup_branch(name, vdata, debp_o, 'd')
+
+        # Set up the ubuntu upstream branch.
+        name = 'ubuu-n'
+        ubuu_n = debu_o.bzrdir.sprout(
+            name, revision_id=self.revid_debu_o_B).open_workingtree()
+
+        vdata = [
+            ('upstream-2.1', ('c',), None, None),
+            ]
+        self._setup_branch(name, vdata, ubuu_n)
+
+        # Set up the ubuntu packaging branch.
+        name = 'ubup-n'
+        ubup_n = debu_o.bzrdir.sprout(
+            name, revision_id=self.revid_debu_o_A).open_workingtree()
+
+        vdata = [
+            ('1.0-1ubuntu1', (), debp_o, self.revid_debp_o_A),
+            ('2.1-0ubuntu1', (), ubuu_n, self.revid_ubuu_n_A),
+            ]
+        self._setup_branch(name, vdata, ubup_n, 'u')
+
+        # Return the ubuntu and the debian packaging branches.
+        return (ubup_n, debp_o, ubuu_n, debu_o)
+
+    def _setup_upstreams_not_diverged(self):
+        """
+        Set up a test configuration where the usptreams have not diverged.
+
+        debian-upstream                       .-----G
+                           A-----------B-----H       \
+        ubuntu-upstream     \           \     \       \
+                             \           \     \       \
+        debian-packaging      \ ,---------D-----\-------J
+                               C                 \ 
+        ubuntu-packaging        `----E------------I
+
+        where:
+             - A = 1.0
+             - B = 1.1
+             - H = 1.4
+
+             - G = 2.2
+
+             - C = 1.0-1
+             - D = 1.1-1
+             - J = 2.2-1
+
+             - E = 1.0-1ubuntu1
+             - I = 1.4-0ubuntu1
+
+        Please note that there's only one shared upstream branch in this case.
+        """
+        # Set up the upstream branch.
+        name = 'upstream'
+        vdata = [
+            ('upstream-1.0', ('a',), None, None),
+            ('upstream-1.1', ('b',), None, None),
+            ('upstream-1.4', ('c',), None, None),
+            ]
+        upstream = self._setup_branch(name, vdata)
+
+        # Set up the debian upstream branch.
+        name = 'dupstream'
+        dupstream = upstream.bzrdir.sprout(name).open_workingtree()
+        vdata = [
+            ('upstream-2.2', (), None, None),
+            ]
+        dupstream = self._setup_branch(name, vdata, dupstream)
+
+        # Set up the debian packaging branch.
+        name = 'debianp'
+        debianp = self.make_branch_and_tree(name)
+        debianp.pull(dupstream.branch, stop_revision=self.revid_upstream_A)
+
+        vdata = [
+            ('1.0-1', ('debian/', 'debian/changelog'), None, None),
+            ('1.1-1', ('o',), dupstream, self.revid_upstream_B),
+            ('2.2-1', ('p',), dupstream, self.revid_dupstream_A),
+            ]
+        self._setup_branch(name, vdata, debianp, 'd')
+
+        # Set up the ubuntu packaging branch.
+        name = 'ubuntup'
+        ubuntup = upstream.bzrdir.sprout(
+            name, revision_id=self.revid_upstream_A).open_workingtree()
+
+        vdata = [
+            ('1.0-1ubuntu1', (), debianp, self.revid_debianp_A),
+            ('1.4-0ubuntu1', (), upstream, self.revid_upstream_C),
+            ]
+        self._setup_branch(name, vdata, ubuntup, 'u')
+
+        # Return the ubuntu and the debian packaging branches.
+        return (ubuntup, debianp)
+
+    def _setup_branch(self, name, vdata, tree=None, log_format=None):
+        vids = list(string.ascii_uppercase)
+        days = range(len(string.ascii_uppercase))
+
+        if tree is None:
+            tree = self.make_branch_and_tree(name)
+
+        tree.lock_write()
+        self.addCleanup(tree.unlock)
+
+        def revid_name(vid):
+            return 'revid_%s_%s' % (name.replace('-', '_'), vid)
+
+        def add_paths(paths):
+            qpaths = ['%s/%s' % (name, path) for path in paths]
+            self.build_tree(qpaths)
+            tree.add(paths)
+
+        def changelog(vdata, vid):
+            result = ''
+            day = days.pop(0)
+            if isinstance(vdata, tuple):
+                uver, dver = vdata[:2]
+                ucle = _Ubuntu_changelog % (uver, vid, day)
+                dcle = _Debian_changelog % (dver, vid, day)
+                result = ucle + dcle
+            else:
+                if log_format == 'u':
+                    result = _Ubuntu_changelog % (vdata, vid, day)
+                elif log_format == 'd':
+                    result = _Debian_changelog % (vdata, vid, day)
+
+            return result
+
+        def commit(msg, version):
+            vid = vids.pop(0)
+            if log_format is not None:
+                cle = changelog(version, vid)
+                p = '%s/work/%s/debian/changelog' % (self.test_base_dir, name)
+                _prepend_log(cle, p)
+            revid = tree.commit('%s: %s' % (vid, msg))
+            setattr(self, revid_name(vid), revid)
+            tree.branch.tags.set_tag(version, revid)
+
+        def tree_nick(tree):
+            return str(tree)[1:-1].split('/')[-1]
+            
+        for version, paths, utree, urevid in vdata:
+            msg = ''
+            if utree is not None:
+                tree.merge_from_branch(utree.branch, to_revision=urevid)
+                utree.branch.tags.merge_to(tree.branch.tags)
+                if urevid is not None:
+                    msg += 'Merged tree %s|%s. ' % (tree_nick(utree), urevid)
+                else:
+                    msg += 'Merged tree %s. ' % utree
+            if paths is not None:
+                add_paths(paths)
+                msg += 'Added paths: %s. ' % str(paths)
+
+            commit(msg, version)
+                
+        return tree
+
+
+if __name__ == '__main__':
+    suite = unittest.TestLoader().loadTestsFromTestCase(MergePackageTests)
+    unittest.TextTestRunner(verbosity=2).run(suite)

=== modified file 'upstream.py'
--- a/upstream.py	2009-08-14 10:52:10 +0000
+++ b/upstream.py	2009-08-24 17:33:44 +0000
@@ -79,7 +79,7 @@
         db = DistributionBranch(self.branch, None, tree=self.tree)
         if not db.has_upstream_version_in_packaging_branch(version):
             raise PackageVersionNotPresent(package, version, self)
-        revid = db._revid_of_upstream_version_from_branch(version)
+        revid = db.revid_of_upstream_version_from_branch(version)
         if not db.has_pristine_tar_delta(revid):
             raise PackageVersionNotPresent(package, version, self)
         info("Using pristine-tar to reconstruct the needed tarball.")



More information about the Pkg-bazaar-commits mailing list