Merge lp:~al-maisan/bzr-builddeb/merge-package into lp:~bzr-builddeb-hackers/bzr-builddeb/trunk-old

Proposed by Muharem Hrnjadovic
Status: Merged
Merged at revision: not available
Proposed branch: lp:~al-maisan/bzr-builddeb/merge-package
Merge into: lp:~bzr-builddeb-hackers/bzr-builddeb/trunk-old
Diff against target: None lines
To merge this branch: bzr merge lp:~al-maisan/bzr-builddeb/merge-package
Reviewer Review Type Date Requested Status
James Westby Approve
Review via email: mp+10369@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Muharem Hrnjadovic (al-maisan) wrote :

Hello James,

this branch implements the 'merge-package' command as specified here: http://pastebin.com/f7214e464.

Please take a look and let me know what you think.

391. By Muharem Hrnjadovic

More test clean-ups.

Revision history for this message
Muharem Hrnjadovic (al-maisan) wrote :

Hmm .. some more test code clean-ups .. please see the attached
incremental diff.

Best regards

--
Muharem Hrnjadovic <email address hidden>
Public key id : B2BBFCFC
Key fingerprint : A5A3 CC67 2B87 D641 103F 5602 219F 6B60 B2BB FCFC

=== modified file 'merge_package.py'
--- merge_package.py 2009-08-19 09:19:38 +0000
+++ merge_package.py 2009-08-19 10:06:11 +0000
@@ -89,7 +89,8 @@
89def _upstream_version_data(source, target):89def _upstream_version_data(source, target):
90 """Most recent upstream versions/revision IDs of the merge source/target.90 """Most recent upstream versions/revision IDs of the merge source/target.
9191
92 Please note: both packaing branches must have been read-locked beforehand.92 Please note: both packaging branches must have been read-locked
93 beforehand.
9394
94 :param source: The merge source branch.95 :param source: The merge source branch.
95 :param target: The merge target branch.96 :param target: The merge target branch.
9697
=== modified file 'tests/test_merge_package.py'
--- tests/test_merge_package.py 2009-08-19 09:44:38 +0000
+++ tests/test_merge_package.py 2009-08-19 10:08:00 +0000
@@ -19,13 +19,10 @@
19# along with bzr-builddeb; if not, write to the Free Software19# along with bzr-builddeb; if not, write to the Free Software
20# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA20# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2121
22import os
23import random
24import string22import string
25import unittest23import unittest
2624
27from bzrlib.errors import ConflictsInTree25from bzrlib.errors import ConflictsInTree
28from bzrlib.merge import WeaveMerger
29from bzrlib.tests import TestCaseWithTransport26from bzrlib.tests import TestCaseWithTransport
3027
31from bzrlib.plugins.builddeb import merge_package as MP28from bzrlib.plugins.builddeb import merge_package as MP
@@ -474,8 +471,7 @@
474 for version, paths, utree, urevid in vdata:471 for version, paths, utree, urevid in vdata:
475 msg = ''472 msg = ''
476 if utree is not None:473 if utree is not None:
477 tree.merge_from_branch(474 tree.merge_from_branch(utree.branch, to_revision=urevid)
478 utree.branch, to_revision=urevid, merge_type=WeaveMerger)
479 utree.branch.tags.merge_to(tree.branch.tags)475 utree.branch.tags.merge_to(tree.branch.tags)
480 if urevid is not None:476 if urevid is not None:
481 msg += 'Merged tree %s|%s. ' % (tree_nick(utree), urevid)477 msg += 'Merged tree %s|%s. ' % (tree_nick(utree), urevid)
@@ -491,7 +487,6 @@
491487
492488
493if __name__ == '__main__':489if __name__ == '__main__':
494 # unittest.main()
495 suite = unittest.TestLoader().loadTestsFromTestCase(MergePackageTests)490 suite = unittest.TestLoader().loadTestsFromTestCase(MergePackageTests)
496 unittest.TextTestRunner(verbosity=2).run(suite)491 unittest.TextTestRunner(verbosity=2).run(suite)
497492
Revision history for this message
James Westby (james-w) wrote :
Download full text (12.6 KiB)

> Hello James,
>
> this branch implements the 'merge-package' command as specified here:
> http://pastebin.com/f7214e464.
>
> Please take a look and let me know what you think.

=== modified file '__init__.py'
--- __init__.py 2009-07-26 15:51:02 +0000
+++ __init__.py 2009-08-06 09:59:34 +0000
@@ -39,7 +39,8 @@
         "merge_upstream": ["mu"],
         "import_dsc": [],
         "bd_do": [],
- "mark_uploaded": []
+ "mark_uploaded": [],
+ "merge_package": ["mp"]
         }

 for command, aliases in commands.iteritems():

I fear this alias will conflict with something in the future (merge-proposal
perhaps), perhaps it is better to leave it off for now and let people set
it as they like.

=== modified file 'cmds.py'
--- cmds.py 2009-07-26 18:21:49 +0000
+++ cmds.py 2009-08-19 09:06:47 +0000
@@ -870,6 +871,39 @@
             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.
+ tree.merge_from_branch(source_branch)
+
+
 class cmd_test_builddeb(Command):
     """Run the builddeb test suite"""

@@ -880,4 +914,3 @@
         passed = selftest(test_suite_factory=test_suite)
         # invert for shell exit code rules
         return not passed
-

=== modified file 'import_dsc.py'
--- import_dsc.py 2009-07-26 16:44:17 +0000
+++ import_dsc.py 2009-08-19 08:58:47 +0000
@@ -1570,7 +1570,7 @@
         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,
@@ -1582,6 +1582,13 @@
         self.upstream_tree = dir_to.open_workingtree()
         self.upstream_branch = self.upstream_tree.branch

+ def _extract_upstream_tree(self, upstream_tip, basedir):
+ # This method is now being used outside this module and hence
+ # not really private any longer.
+ # TODO: obsolete/remove this method and start using
+ # extract_upstream_tree() instead.
+ self.extract_upstre...

review: Needs Resubmitting
392. By Muharem Hrnjadovic

Enhancements stemming from James' review comments, round 1

393. By Muharem Hrnjadovic

Minor fix.

394. By Muharem Hrnjadovic

Enhancements stemming from James' review comments, round 2

395. By Muharem Hrnjadovic

Enhancements stemming from James' review comments, round 3

396. By Muharem Hrnjadovic

Enhancements stemming from James' review comments, round 4

397. By Muharem Hrnjadovic

Enhancements stemming from James' review comments, round 5

398. By Muharem Hrnjadovic

Enhancements stemming from James' review comments, round 6

399. By Muharem Hrnjadovic

Enhancements stemming from James' review comments, round 7

400. By Muharem Hrnjadovic

Minor fix.

401. By Muharem Hrnjadovic

Minor test code fix.

402. By Muharem Hrnjadovic

Added test that checks the ancestry pre- and post-fix.

403. By Muharem Hrnjadovic

Added a more stringent test that checks the ancestry pre- and post-fix.

Revision history for this message
Muharem Hrnjadovic (al-maisan) wrote :
Download full text (15.2 KiB)

James Westby wrote:

Hello James,

thank you very much for reviewing the branch at hand. I have revised it
to accommodate most of your suggestions.

Please have a look at the attached incremental/full diffs as well as at
my in-line responses below.

You are obviously free to track the changes made in the course of the
review here:
bzr+ssh://bazaar.launchpad.net/~al-maisan/bzr-builddeb/merge-package

> Review: Resubmit
>> Hello James,
>>
>> this branch implements the 'merge-package' command as specified here:
>> http://pastebin.com/f7214e464.
>>
>> Please take a look and let me know what you think.
>
>
> === modified file '__init__.py'
> --- __init__.py 2009-07-26 15:51:02 +0000
> +++ __init__.py 2009-08-06 09:59:34 +0000
> @@ -39,7 +39,8 @@
> "merge_upstream": ["mu"],
> "import_dsc": [],
> "bd_do": [],
> - "mark_uploaded": []
> + "mark_uploaded": [],
> + "merge_package": ["mp"]
> }
>
> for command, aliases in commands.iteritems():
>
>
> I fear this alias will conflict with something in the future (merge-proposal
> perhaps), perhaps it is better to leave it off for now and let people set
> it as they like.

Good point, alias removed.

> === modified file 'cmds.py'
> --- cmds.py 2009-07-26 18:21:49 +0000
> +++ cmds.py 2009-08-19 09:06:47 +0000
> @@ -870,6 +871,39 @@
> 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.
> + tree.merge_from_branch(source_branch)
> +
> +
> class cmd_test_builddeb(Command):
> """Run the builddeb test suite"""
>
> @@ -880,4 +914,3 @@
> passed = selftest(test_suite_factory=test_suite)
> # invert for shell exit code rules
> return not passed
> -
>
> === modified file 'import_dsc.py'
> --- import_dsc.py 2009-07-26 16:44:17 +0000
> +++ import_dsc.py 2009-08-19 08:58:47 +0000
> @@ -1570,7 +1570,7 @@
> finally:
> shutil.rmtree(tempdir)
>
> - def _extract_upstream_tree(self, upstream_tip, basedir):
> + def extract_upstream_tree(self, upstream_tip, base...

=== modified file '__init__.py'
--- __init__.py 2009-08-06 09:59:34 +0000
+++ __init__.py 2009-08-19 11:39:16 +0000
@@ -40,7 +40,7 @@
40 "import_dsc": [],40 "import_dsc": [],
41 "bd_do": [],41 "bd_do": [],
42 "mark_uploaded": [],42 "mark_uploaded": [],
43 "merge_package": ["mp"]43 "merge_package": []
44 }44 }
4545
46for command, aliases in commands.iteritems():46for command, aliases in commands.iteritems():
4747
=== modified file 'errors.py'
--- errors.py 2009-04-16 09:30:49 +0000
+++ errors.py 2009-08-19 14:08:39 +0000
@@ -175,3 +175,22 @@
175175
176 def __init__(self, error):176 def __init__(self, error):
177 BzrError.__init__(self, error=error)177 BzrError.__init__(self, error=error)
178
179
180class SharedUpstreamConflictsWithTargetPackaging(BzrError):
181 _fmt = ('''\
182 The "merge-package" command has detected diverged upstream
183 branches for the merge source and target. A shared upstream
184 revision was constructed to remedy the problem.
185
186 However, merging the shared upstream revision into the merge
187 target resulted in conflicts.
188
189 Please proceed as follows:
190
191 1 - Resolve the current merge conflicts in the merge target
192 directory and commit the changes.
193 2 - Perform a plain "bzr merge <source-packaging-branch>"
194 command, resolve any ensuing packaging branch conflicts
195 and commit once satisfied with the changes.
196 ''')
178197
=== modified file 'import_dsc.py'
--- import_dsc.py 2009-08-19 08:58:47 +0000
+++ import_dsc.py 2009-08-19 11:43:09 +0000
@@ -1575,6 +1575,7 @@
1575 # tree for it.1575 # tree for it.
1576 # TODO: should stack rather than trying to use the repository,1576 # TODO: should stack rather than trying to use the repository,
1577 # as that will be more efficient.1577 # as that will be more efficient.
1578 # TODO: remove the _extract_upstream_tree alias below.
1578 to_location = os.path.join(basedir, "upstream")1579 to_location = os.path.join(basedir, "upstream")
1579 dir_to = self.branch.bzrdir.sprout(to_location,1580 dir_to = self.branch.bzrdir.sprout(to_location,
1580 revision_id=upstream_tip,1581 revision_id=upstream_tip,
@@ -1582,12 +1583,7 @@
1582 self.upstream_tree = dir_to.open_workingtree()1583 self.upstream_tree = dir_to.open_workingtree()
1583 self.upstream_branch = self.upstream_tree.branch1584 self.upstream_branch = self.upstream_tree.branch
15841585
1585 def _extract_upstream_tree(self, upstream_tip, basedir):1586 _extract_upstream_tree = extract_upstream_tree
1586 # This method is now being used outside this module and hence
1587 # not really private any longer.
1588 # TODO: obsolete/remove this method and start using
1589 # extract_upstream_tree() instead.
1590 self.extract_upstream_tree(upstream_tip, basedir)
15911587
1592 def _create_empty_upstream_tree(self, basedir):1588 def _create_empty_upstream_tree(self, basedir):
1593 to_location = os.path.join(basedir, "upstream")1589 to_location = os.path.join(basedir, "upstream")
@@ -1622,11 +1618,11 @@
1622 shutil.rmtree(tempdir)1618 shutil.rmtree(tempdir)
1623 raise1619 raise
16241620
1625 def _revid_of_upstream_version_from_branch(self, version):
1626 """The private method below will go away eventually."""1621 """The private method below will go away eventually."""
1627 return self.revid_of_upstream_version_from_branch(version)1622 return self.revid_of_upstream_version_from_branch(version)
16281623
1629 def revid_of_upstream_version_from_branch(self, version):1624 def revid_of_upstream_version_from_branch(self, version):
1625 # TODO: remove the _revid_of_upstream_version_from_branch alias below.
1630 assert isinstance(version, str)1626 assert isinstance(version, str)
1631 tag_name = self.upstream_tag_name(version)1627 tag_name = self.upstream_tag_name(version)
1632 if self._has_version(self.branch, tag_name):1628 if self._has_version(self.branch, tag_name):
@@ -1640,6 +1636,8 @@
1640 tag_name = self.upstream_tag_name(version)1636 tag_name = self.upstream_tag_name(version)
1641 return self.branch.tags.lookup_tag(tag_name)1637 return self.branch.tags.lookup_tag(tag_name)
16421638
1639 _revid_of_upstream_version_from_branch = revid_of_upstream_version_from_branch
1640
1643 def merge_upstream(self, tarball_filename, version, previous_version,1641 def merge_upstream(self, tarball_filename, version, previous_version,
1644 upstream_branch=None, upstream_revision=None, merge_type=None):1642 upstream_branch=None, upstream_revision=None, merge_type=None):
1645 assert self.upstream_branch is None, \1643 assert self.upstream_branch is None, \
16461644
=== modified file 'merge_package.py'
--- merge_package.py 2009-08-19 09:19:38 +0000
+++ merge_package.py 2009-08-19 16:31:25 +0000
@@ -30,37 +30,7 @@
30from bzrlib import errors30from bzrlib import errors
3131
32from bzrlib.plugins.builddeb.import_dsc import DistributionBranch32from bzrlib.plugins.builddeb.import_dsc import DistributionBranch
3333from bzrlib.plugins.builddeb.util import find_changelog
34
35class WrongBranchType(errors.BzrError):
36 _fmt = "The merge target is not a packaging branch."
37
38
39class InvalidChangelogFormat(errors.BzrError):
40 _fmt = "The debian/changelog is empty or not in valid format."
41
42
43class SourceUpstreamConflictsWithTargetPackaging(errors.BzrError):
44 _fmt = (
45 "The source upstream branch conflicts with "
46 "the target packaging branch")
47
48
49def _read_file(branch, path):
50 """Get content of file for given `branch` and `path.
51
52 :param branch: A Branch object containing the file of interest.
53 :param path: The path of the file to read.
54 """
55 try:
56 tree = branch.basis_tree()
57 tree.lock_read()
58 content = tree.get_file_text(tree.path2id(path))
59 tree.unlock()
60 except errors.NoSuchId:
61 raise WrongBranchType()
62
63 return content
6434
6535
66def _latest_version(branch):36def _latest_version(branch):
@@ -68,28 +38,16 @@
68 38
69 :param branch: A Branch object containing the source upload of interest.39 :param branch: A Branch object containing the source upload of interest.
70 """40 """
71 upload_version = ''41 changelog, _ignore = find_changelog(branch.basis_tree(), False)
72 changelog = _read_file(branch, "debian/changelog")42
7343 return changelog.version
74 for line in changelog.splitlines():
75 # Look for the top-level changelog stanza, extract the
76 # upload version from it and break on success.
77 match = re.search('^.+\(([^)]+)\).*$', line)
78 if match is not None:
79 (upload_version,) = match.groups(1)
80 break
81
82 upload_version = upload_version.strip()
83 if len(upload_version) <= 0:
84 raise InvalidChangelogFormat()
85
86 return Version(upload_version)
8744
8845
89def _upstream_version_data(source, target):46def _upstream_version_data(source, target):
90 """Most recent upstream versions/revision IDs of the merge source/target.47 """Most recent upstream versions/revision IDs of the merge source/target.
9148
92 Please note: both packaing branches must have been read-locked beforehand.49 Please note: both packaging branches must have been read-locked
50 beforehand.
9351
94 :param source: The merge source branch.52 :param source: The merge source branch.
95 :param target: The merge target branch.53 :param target: The merge target branch.
@@ -150,49 +108,48 @@
150 t_upstream_reverted = False108 t_upstream_reverted = False
151 target = tree.branch109 target = tree.branch
152110
111 source.lock_read()
153 try:112 try:
154 source.lock_read()113 tree.lock_read()
155 target.lock_read()114 try:
156 upstream_vdata = _upstream_version_data(source, target)115 # "Unpack" the upstream versions and revision ids for the merge
157 # Did the upstream branches of the merge source and target diverge?116 # source and target branch respectively.
158 revids = [vdata[1] for vdata in upstream_vdata]117 [(us_ver, us_revid), (ut_ver, ut_revid)] = _upstream_version_data(source, target)
159 graph = source.repository.get_graph(target.repository)118
160 upstreams_diverged = (len(graph.heads(revids)) > 1)119 # Did the upstream branches of the merge source/target diverge?
120 graph = source.repository.get_graph(target.repository)
121 upstreams_diverged = (len(graph.heads([us_revid, ut_revid])) > 1)
122 finally:
123 tree.unlock()
161 finally:124 finally:
162 source.unlock()125 source.unlock()
163 target.unlock()
164126
165 if not upstreams_diverged:127 if not upstreams_diverged:
166 return (upstreams_diverged, t_upstream_reverted)128 return (upstreams_diverged, t_upstream_reverted)
167129
168 # "Unpack" the upstream versions and revision ids for the merge source and
169 # target branch respectively.
170 [(usource_v, usource_revid), (utarget_v, utarget_revid)] = upstream_vdata
171
172 # Instantiate a `DistributionBranch` object for the merge target130 # Instantiate a `DistributionBranch` object for the merge target
173 # (packaging) branch.131 # (packaging) branch.
174 db = DistributionBranch(tree.branch, tree.branch)132 db = DistributionBranch(tree.branch, tree.branch)
175 tempdir = tempfile.mkdtemp(dir=os.path.join(tree.basedir, '..'))133 tempdir = tempfile.mkdtemp(dir=os.path.join(tree.basedir, '..'))
176134
177 # Extract the merge target's upstream tree into a temporary directory.135 # Extract the merge target's upstream tree into a temporary directory.
178 db.extract_upstream_tree(utarget_revid, tempdir)136 db.extract_upstream_tree(ut_revid, tempdir)
179 tmp_target_upstream_tree = db.upstream_tree137 tmp_target_upstream_tree = db.upstream_tree
180138
181 # Merge upstream branch tips to obtain a shared upstream parent. This139 # Merge upstream branch tips to obtain a shared upstream parent. This
182 # will add revision K (see graph above) to a temporary merge target140 # will add revision K (see graph above) to a temporary merge target
183 # upstream tree.141 # upstream tree.
142 tmp_target_upstream_tree.lock_write()
184 try:143 try:
185 tmp_target_upstream_tree.lock_write()144 if us_ver > ut_ver:
186
187 if usource_v > utarget_v:
188 # The source upstream tree is more recent and the temporary145 # The source upstream tree is more recent and the temporary
189 # target tree needs to be reshaped to match it.146 # target tree needs to be reshaped to match it.
190 tmp_target_upstream_tree.revert(147 tmp_target_upstream_tree.revert(
191 None, source.repository.revision_tree(usource_revid))148 None, source.repository.revision_tree(us_revid))
192 t_upstream_reverted = True149 t_upstream_reverted = True
193150
194 tmp_target_upstream_tree.set_parent_ids(151 tmp_target_upstream_tree.set_parent_ids(
195 (utarget_revid, usource_revid))152 (ut_revid, us_revid))
196153
197 tmp_target_upstream_tree.commit(154 tmp_target_upstream_tree.commit(
198 'Consolidated upstream tree for merging into target branch')155 'Consolidated upstream tree for merging into target branch')
@@ -201,13 +158,13 @@
201158
202 # Merge shared upstream parent into the target merge branch. This creates159 # Merge shared upstream parent into the target merge branch. This creates
203 # revison L in the digram above.160 # revison L in the digram above.
161 tree.lock_write()
204 try:162 try:
205 tree.lock_write()163 conflicts = tree.merge_from_branch(tmp_target_upstream_tree.branch)
206 try:164 if conflicts > 0:
207 tree.merge_from_branch(tmp_target_upstream_tree.branch)165 raise errors.SharedUpstreamConflictsWithTargetPackaging()
208 tree.commit('Merging source packaging branch in to target.')166 else:
209 except ConflictsInTree:167 tree.commit('Merging shared upstream rev in to target branch.')
210 raise SourceUpstreamConflictsWithTargetPackaging()
211 finally:168 finally:
212 tree.unlock()169 tree.unlock()
213170
214171
=== modified file 'tests/test_merge_package.py'
--- tests/test_merge_package.py 2009-08-19 09:44:38 +0000
+++ tests/test_merge_package.py 2009-08-21 12:40:48 +0000
@@ -19,16 +19,16 @@
19# along with bzr-builddeb; if not, write to the Free Software19# along with bzr-builddeb; if not, write to the Free Software
20# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA20# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2121
22import os
23import random
24import string22import string
25import unittest23import unittest
2624
25from debian_bundle.changelog import Version
26
27from bzrlib.errors import ConflictsInTree27from bzrlib.errors import ConflictsInTree
28from bzrlib.merge import WeaveMerger
29from bzrlib.tests import TestCaseWithTransport28from bzrlib.tests import TestCaseWithTransport
3029
31from bzrlib.plugins.builddeb import merge_package as MP30from bzrlib.plugins.builddeb import merge_package as MP
31from bzrlib.plugins.builddeb.import_dsc import DistributionBranch
3232
33_Debian_changelog = '''\33_Debian_changelog = '''\
34ipsec-tools (%s) unstable; urgency=high34ipsec-tools (%s) unstable; urgency=high
@@ -52,29 +52,31 @@
52def _prepend_log(text, path):52def _prepend_log(text, path):
53 content = open(path).read()53 content = open(path).read()
54 fh = open(path, 'wb')54 fh = open(path, 'wb')
55 fh.write(text+content)55 try:
56 fh.close()56 fh.write(text+content)
57 finally:
58 fh.close()
5759
5860
59class MergePackageTests(TestCaseWithTransport):61class MergePackageTests(TestCaseWithTransport):
6062
61 def test_latest_upstream_versions(self):63 def test_latest_upstream_versions(self):
62 """Check correctness of upstream version computation."""64 """Check correctness of upstream version computation."""
63 ubup_o, debp_n = self._setup_debian_upstrem_newer()65 ubup_o, debp_n, _ubuu, _debu = self._setup_debian_upstream_newer()
64 # Ubuntu upstream.66 # Ubuntu upstream.
65 self.assertEquals(67 self.assertEquals(
66 MP._latest_version(ubup_o.branch).upstream_version, '1.1.2')68 MP._latest_version(ubup_o).upstream_version, '1.1.2')
67 # Debian upstream.69 # Debian upstream.
68 self.assertEquals(70 self.assertEquals(
69 MP._latest_version(debp_n.branch).upstream_version, '2.0')71 MP._latest_version(debp_n).upstream_version, '2.0')
7072
71 ubuntup, debianp = self._setup_upstreams_not_diverged()73 ubuntup, debianp = self._setup_upstreams_not_diverged()
72 # Ubuntu upstream.74 # Ubuntu upstream.
73 self.assertEquals(75 self.assertEquals(
74 MP._latest_version(ubuntup.branch).upstream_version, '1.4')76 MP._latest_version(ubuntup).upstream_version, '1.4')
75 # Debian upstream.77 # Debian upstream.
76 self.assertEquals(78 self.assertEquals(
77 MP._latest_version(debianp.branch).upstream_version, '2.2')79 MP._latest_version(debianp).upstream_version, '2.2')
7880
79 def test_debian_upstream_newer(self):81 def test_debian_upstream_newer(self):
80 """Diverging upstreams (debian newer) don't cause merge conflicts.82 """Diverging upstreams (debian newer) don't cause merge conflicts.
@@ -88,23 +90,39 @@
88 The upstream conflict will be resolved by fix_ancestry_as_needed().90 The upstream conflict will be resolved by fix_ancestry_as_needed().
89 Please note that the debian ancestry is more recent.91 Please note that the debian ancestry is more recent.
90 """92 """
91 ubup_o, debp_n = self._setup_debian_upstrem_newer()93 ubup, debp, ubuu, debu = self._setup_debian_upstream_newer()
9294
93 # Attempt a plain merge first.95 # Attempt a plain merge first.
94 conflicts = ubup_o.merge_from_branch(96 conflicts = ubup.merge_from_branch(
95 debp_n.branch, to_revision=self.revid_debp_n_C)97 debp.branch, to_revision=self.revid_debp_n_C)
9698
97 # There are two conflicts in the 'c' and the 'debian/changelog' files99 # There are two conflicts in the 'c' and the 'debian/changelog' files
98 # respectively.100 # respectively.
99 self.assertEquals(conflicts, 2)101 self.assertEquals(conflicts, 2)
100 conflict_paths = sorted([c.path for c in ubup_o.conflicts()])102 conflict_paths = sorted([c.path for c in ubup.conflicts()])
101 self.assertEquals(conflict_paths, [u'c.moved', u'debian/changelog'])103 self.assertEquals(conflict_paths, [u'c.moved', u'debian/changelog'])
102104
103 # Undo the failed merge.105 # Undo the failed merge.
104 ubup_o.revert()106 ubup.revert()
107
108 # Check the versions present in the tree with the fixed ancestry.
109 v3 = "1.1.2"
110 v4 = "2.0"
111 db1 = DistributionBranch(ubup.branch, ubup.branch)
112 self.assertEqual(db1.has_upstream_version(v3), True)
113 # This version is in the diverged debian upstream tree and will
114 # hence not be present in the target ubuntu packaging branch.
115 self.assertEqual(db1.has_upstream_version(v4), False)
116
117 # The ubuntu upstream branch tip.
118 ubuu_tip = ubuu.branch.revision_history()[-1]
119 # The debian upstream branch tip.
120 debu_tip = debu.branch.revision_history()[-1]
121 # The ubuntu packaging branch tip.
122 ubup_tip_pre_fix = ubup.branch.revision_history()[-1]
105123
106 # The first conflict is resolved by calling fix_ancestry_as_needed().124 # The first conflict is resolved by calling fix_ancestry_as_needed().
107 upstreams_diverged, t_upstream_reverted = MP.fix_ancestry_as_needed(ubup_o, debp_n.branch)125 upstreams_diverged, t_upstream_reverted = MP.fix_ancestry_as_needed(ubup, debp.branch)
108126
109 # The ancestry did diverge and needed to be fixed.127 # The ancestry did diverge and needed to be fixed.
110 self.assertEquals(upstreams_diverged, True)128 self.assertEquals(upstreams_diverged, True)
@@ -112,13 +130,35 @@
112 # source upstream branch since the latter was more recent.130 # source upstream branch since the latter was more recent.
113 self.assertEquals(t_upstream_reverted, True)131 self.assertEquals(t_upstream_reverted, True)
114132
133 # Check the versions present in the tree with the fixed ancestry.
134 db2 = DistributionBranch(ubup.branch, ubup.branch)
135 self.assertEqual(db2.has_upstream_version(v3), True)
136 # The ancestry has been fixed and the missing debian upstream
137 # version should now be present in the target ubuntu packaging
138 # branch.
139 self.assertEqual(db2.has_upstream_version(v4), True)
140
141 # Now let's take a look at the fixed ubuntu packaging branch.
142 ubup_tip_post_fix = ubup.branch.revision_history()[-1]
143 ubup_parents_post_fix = ubup.branch.repository.revision_tree(ubup_tip_post_fix).get_parent_ids()
144
145 # The tip of the fixed ubuntu packaging branch has 2 parents.
146 self.assertEquals(len(ubup_parents_post_fix), 2)
147
148 # The left parent is the packaging branch tip before fixing.
149 self.assertEquals(ubup_parents_post_fix[0], ubup_tip_pre_fix)
150
151 # The right parent is derived from a merge
152 ubup_parents_sharedupstream = ubup.branch.repository.revision_tree(ubup_parents_post_fix[1]).get_parent_ids()
153 self.assertEquals(ubup_parents_sharedupstream, [ubuu_tip, debu_tip])
154
115 # Try merging again.155 # Try merging again.
116 conflicts = ubup_o.merge_from_branch(156 conflicts = ubup.merge_from_branch(
117 debp_n.branch, to_revision=self.revid_debp_n_C)157 debp.branch, to_revision=self.revid_debp_n_C)
118158
119 # And, voila, only the packaging branch conflict remains.159 # And, voila, only the packaging branch conflict remains.
120 self.assertEquals(conflicts, 1)160 self.assertEquals(conflicts, 1)
121 conflict_paths = sorted([c.path for c in ubup_o.conflicts()])161 conflict_paths = sorted([c.path for c in ubup.conflicts()])
122 self.assertEquals(conflict_paths, [u'debian/changelog'])162 self.assertEquals(conflict_paths, [u'debian/changelog'])
123163
124 def test_debian_upstream_older(self):164 def test_debian_upstream_older(self):
@@ -208,9 +248,9 @@
208 conflict_paths = sorted([c.path for c in ubuntup.conflicts()])248 conflict_paths = sorted([c.path for c in ubuntup.conflicts()])
209 self.assertEquals(conflict_paths, [u'debian/changelog'])249 self.assertEquals(conflict_paths, [u'debian/changelog'])
210250
211 def _setup_debian_upstrem_newer(self):251 def _setup_debian_upstream_newer(self):
212 """252 """
213 Set up the following test configuration (debian upstrem newer).253 Set up the following test configuration (debian upstream newer).
214254
215 debian-upstream ,------------------H255 debian-upstream ,------------------H
216 A-----------B \256 A-----------B \
@@ -280,11 +320,11 @@
280 self._setup_branch(name, vdata, ubup_o, 'u')320 self._setup_branch(name, vdata, ubup_o, 'u')
281321
282 # Return the ubuntu and the debian packaging branches.322 # Return the ubuntu and the debian packaging branches.
283 return (ubup_o, debp_n)323 return (ubup_o, debp_n, ubuu_o, debu_n)
284324
285 def _setup_debian_upstream_older(self):325 def _setup_debian_upstream_older(self):
286 """326 """
287 Set up the following test configuration (debian upstrem older).327 Set up the following test configuration (debian upstream older).
288328
289 debian-upstream ,----H-------------.329 debian-upstream ,----H-------------.
290 A-----------B \330 A-----------B \
@@ -434,6 +474,9 @@
434 if tree is None:474 if tree is None:
435 tree = self.make_branch_and_tree(name)475 tree = self.make_branch_and_tree(name)
436476
477 tree.lock_write()
478 self.addCleanup(tree.unlock)
479
437 def revid_name(vid):480 def revid_name(vid):
438 return 'revid_%s_%s' % (name.replace('-', '_'), vid)481 return 'revid_%s_%s' % (name.replace('-', '_'), vid)
439482
@@ -464,7 +507,7 @@
464 cle = changelog(version, vid)507 cle = changelog(version, vid)
465 p = '%s/work/%s/debian/changelog' % (self.test_base_dir, name)508 p = '%s/work/%s/debian/changelog' % (self.test_base_dir, name)
466 _prepend_log(cle, p)509 _prepend_log(cle, p)
467 revid = tree.commit('%s: %s' % (vid, msg), rev_id='%s-%s' % (name, vid))510 revid = tree.commit('%s: %s' % (vid, msg))
468 setattr(self, revid_name(vid), revid)511 setattr(self, revid_name(vid), revid)
469 tree.branch.tags.set_tag(version, revid)512 tree.branch.tags.set_tag(version, revid)
470513
@@ -474,8 +517,7 @@
474 for version, paths, utree, urevid in vdata:517 for version, paths, utree, urevid in vdata:
475 msg = ''518 msg = ''
476 if utree is not None:519 if utree is not None:
477 tree.merge_from_branch(520 tree.merge_from_branch(utree.branch, to_revision=urevid)
478 utree.branch, to_revision=urevid, merge_type=WeaveMerger)
479 utree.branch.tags.merge_to(tree.branch.tags)521 utree.branch.tags.merge_to(tree.branch.tags)
480 if urevid is not None:522 if urevid is not None:
481 msg += 'Merged tree %s|%s. ' % (tree_nick(utree), urevid)523 msg += 'Merged tree %s|%s. ' % (tree_nick(utree), urevid)
@@ -491,7 +533,5 @@
491533
492534
493if __name__ == '__main__':535if __name__ == '__main__':
494 # unittest.main()
495 suite = unittest.TestLoader().loadTestsFromTestCase(MergePackageTests)536 suite = unittest.TestLoader().loadTestsFromTestCase(MergePackageTests)
496 unittest.TextTestRunner(verbosity=2).run(suite)537 unittest.TextTestRunner(verbosity=2).run(suite)
497
=== modified file '__init__.py'
--- __init__.py 2009-07-26 15:51:02 +0000
+++ __init__.py 2009-08-19 11:39:16 +0000
@@ -39,7 +39,8 @@
39 "merge_upstream": ["mu"],39 "merge_upstream": ["mu"],
40 "import_dsc": [],40 "import_dsc": [],
41 "bd_do": [],41 "bd_do": [],
42 "mark_uploaded": []42 "mark_uploaded": [],
43 "merge_package": []
43 }44 }
4445
45for command, aliases in commands.iteritems():46for command, aliases in commands.iteritems():
4647
=== modified file 'cmds.py'
--- cmds.py 2009-07-26 18:21:49 +0000
+++ cmds.py 2009-08-19 09:01:30 +0000
@@ -69,6 +69,7 @@
69 DscCache,69 DscCache,
70 DscComp,70 DscComp,
71 )71 )
72from bzrlib.plugins.builddeb.merge_package import fix_ancestry_as_needed
72from bzrlib.plugins.builddeb.source_distiller import (73from bzrlib.plugins.builddeb.source_distiller import (
73 FullSourceDistiller,74 FullSourceDistiller,
74 MergeModeDistiller,75 MergeModeDistiller,
@@ -719,9 +720,9 @@
719 "the previous upstream version, %s, in the "720 "the previous upstream version, %s, in the "
720 "branch: %s" % (last_version,721 "branch: %s" % (last_version,
721 db.upstream_tag_name(last_version)))722 db.upstream_tag_name(last_version)))
722 upstream_tip = db._revid_of_upstream_version_from_branch(723 upstream_tip = db.revid_of_upstream_version_from_branch(
723 last_version)724 last_version)
724 db._extract_upstream_tree(upstream_tip, tempdir)725 db.extract_upstream_tree(upstream_tip, tempdir)
725 else:726 else:
726 db._create_empty_upstream_tree(tempdir)727 db._create_empty_upstream_tree(tempdir)
727 self.import_many(db, files_list, orig_target)728 self.import_many(db, files_list, orig_target)
@@ -870,6 +871,39 @@
870 t.unlock()871 t.unlock()
871872
872873
874class cmd_merge_package(Command):
875 """Merges source packaging branch into target packaging branch.
876
877 This will first check whether the upstream branches have diverged.
878
879 If that's the case an attempt will be made to fix the upstream ancestry
880 so that the user only needs to deal wth packaging branch merge issues.
881
882 In the opposite case a normal merge will be performed.
883 """
884 takes_args = ['source']
885
886 def run(self, source):
887 source_branch = target_branch = None
888 # Get the target branch.
889 try:
890 tree = WorkingTree.open_containing('.')[0]
891 target_branch = tree.branch
892 except NotBranchError:
893 raise BzrCommandError(
894 "There is no tree to merge the source branch in to")
895 # Get the source branch.
896 try:
897 source_branch = Branch.open(source)
898 except NotBranchError:
899 raise BzrCommandError("Invalid source branch URL?")
900
901 fix_ancestry_as_needed(tree, source_branch)
902
903 # Merge source packaging branch in to the target packaging branch.
904 tree.merge_from_branch(source_branch)
905
906
873class cmd_test_builddeb(Command):907class cmd_test_builddeb(Command):
874 """Run the builddeb test suite"""908 """Run the builddeb test suite"""
875909
@@ -880,4 +914,3 @@
880 passed = selftest(test_suite_factory=test_suite)914 passed = selftest(test_suite_factory=test_suite)
881 # invert for shell exit code rules915 # invert for shell exit code rules
882 return not passed916 return not passed
883
884917
=== modified file 'errors.py'
--- errors.py 2009-04-16 09:30:49 +0000
+++ errors.py 2009-08-19 14:08:39 +0000
@@ -175,3 +175,22 @@
175175
176 def __init__(self, error):176 def __init__(self, error):
177 BzrError.__init__(self, error=error)177 BzrError.__init__(self, error=error)
178
179
180class SharedUpstreamConflictsWithTargetPackaging(BzrError):
181 _fmt = ('''\
182 The "merge-package" command has detected diverged upstream
183 branches for the merge source and target. A shared upstream
184 revision was constructed to remedy the problem.
185
186 However, merging the shared upstream revision into the merge
187 target resulted in conflicts.
188
189 Please proceed as follows:
190
191 1 - Resolve the current merge conflicts in the merge target
192 directory and commit the changes.
193 2 - Perform a plain "bzr merge <source-packaging-branch>"
194 command, resolve any ensuing packaging branch conflicts
195 and commit once satisfied with the changes.
196 ''')
178197
=== modified file 'import_dsc.py'
--- import_dsc.py 2009-07-26 16:44:17 +0000
+++ import_dsc.py 2009-08-19 11:43:09 +0000
@@ -1570,11 +1570,12 @@
1570 finally:1570 finally:
1571 shutil.rmtree(tempdir)1571 shutil.rmtree(tempdir)
15721572
1573 def _extract_upstream_tree(self, upstream_tip, basedir):1573 def extract_upstream_tree(self, upstream_tip, basedir):
1574 # Extract that to a tempdir so we can get a working1574 # Extract that to a tempdir so we can get a working
1575 # tree for it.1575 # tree for it.
1576 # TODO: should stack rather than trying to use the repository,1576 # TODO: should stack rather than trying to use the repository,
1577 # as that will be more efficient.1577 # as that will be more efficient.
1578 # TODO: remove the _extract_upstream_tree alias below.
1578 to_location = os.path.join(basedir, "upstream")1579 to_location = os.path.join(basedir, "upstream")
1579 dir_to = self.branch.bzrdir.sprout(to_location,1580 dir_to = self.branch.bzrdir.sprout(to_location,
1580 revision_id=upstream_tip,1581 revision_id=upstream_tip,
@@ -1582,6 +1583,8 @@
1582 self.upstream_tree = dir_to.open_workingtree()1583 self.upstream_tree = dir_to.open_workingtree()
1583 self.upstream_branch = self.upstream_tree.branch1584 self.upstream_branch = self.upstream_tree.branch
15841585
1586 _extract_upstream_tree = extract_upstream_tree
1587
1585 def _create_empty_upstream_tree(self, basedir):1588 def _create_empty_upstream_tree(self, basedir):
1586 to_location = os.path.join(basedir, "upstream")1589 to_location = os.path.join(basedir, "upstream")
1587 to_transport = get_transport(to_location)1590 to_transport = get_transport(to_location)
@@ -1615,7 +1618,11 @@
1615 shutil.rmtree(tempdir)1618 shutil.rmtree(tempdir)
1616 raise1619 raise
16171620
1618 def _revid_of_upstream_version_from_branch(self, version):1621 """The private method below will go away eventually."""
1622 return self.revid_of_upstream_version_from_branch(version)
1623
1624 def revid_of_upstream_version_from_branch(self, version):
1625 # TODO: remove the _revid_of_upstream_version_from_branch alias below.
1619 assert isinstance(version, str)1626 assert isinstance(version, str)
1620 tag_name = self.upstream_tag_name(version)1627 tag_name = self.upstream_tag_name(version)
1621 if self._has_version(self.branch, tag_name):1628 if self._has_version(self.branch, tag_name):
@@ -1629,6 +1636,8 @@
1629 tag_name = self.upstream_tag_name(version)1636 tag_name = self.upstream_tag_name(version)
1630 return self.branch.tags.lookup_tag(tag_name)1637 return self.branch.tags.lookup_tag(tag_name)
16311638
1639 _revid_of_upstream_version_from_branch = revid_of_upstream_version_from_branch
1640
1632 def merge_upstream(self, tarball_filename, version, previous_version,1641 def merge_upstream(self, tarball_filename, version, previous_version,
1633 upstream_branch=None, upstream_revision=None, merge_type=None):1642 upstream_branch=None, upstream_revision=None, merge_type=None):
1634 assert self.upstream_branch is None, \1643 assert self.upstream_branch is None, \
@@ -1639,14 +1648,14 @@
1639 if previous_version is not None:1648 if previous_version is not None:
1640 if self.has_upstream_version_in_packaging_branch(1649 if self.has_upstream_version_in_packaging_branch(
1641 previous_version.upstream_version):1650 previous_version.upstream_version):
1642 upstream_tip = self._revid_of_upstream_version_from_branch(1651 upstream_tip = self.revid_of_upstream_version_from_branch(
1643 previous_version.upstream_version)1652 previous_version.upstream_version)
1644 self._extract_upstream_tree(upstream_tip, tempdir)1653 self.extract_upstream_tree(upstream_tip, tempdir)
1645 elif (upstream_branch is not None and 1654 elif (upstream_branch is not None and
1646 previous_upstream_revision is not None):1655 previous_upstream_revision is not None):
1647 upstream_tip = RevisionSpec.from_string(previous_upstream_revision).as_revision_id(upstream_branch)1656 upstream_tip = RevisionSpec.from_string(previous_upstream_revision).as_revision_id(upstream_branch)
1648 assert isinstance(upstream_tip, str)1657 assert isinstance(upstream_tip, str)
1649 self._extract_upstream_tree(upstream_tip, tempdir)1658 self.extract_upstream_tree(upstream_tip, tempdir)
1650 else:1659 else:
1651 raise BzrCommandError("Unable to find the tag for the "1660 raise BzrCommandError("Unable to find the tag for the "
1652 "previous upstream version, %s, in the branch: "1661 "previous upstream version, %s, in the branch: "
16531662
=== added file 'merge_package.py'
--- merge_package.py 1970-01-01 00:00:00 +0000
+++ merge_package.py 2009-08-19 16:31:25 +0000
@@ -0,0 +1,171 @@
1# merge_package.py -- The plugin for bzr
2# Copyright (C) 2009 Canonical Ltd.
3#
4# :Author: Muharem Hrnjadovic <muharem@ubuntu.com>
5#
6# This file is part of bzr-builddeb.
7#
8# bzr-builddeb is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13# bzr-builddeb is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with bzr-builddeb; if not, write to the Free Software
20# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21#
22
23import os
24import re
25import sys
26import tempfile
27
28from debian_bundle.changelog import Version
29
30from bzrlib import errors
31
32from bzrlib.plugins.builddeb.import_dsc import DistributionBranch
33from bzrlib.plugins.builddeb.util import find_changelog
34
35
36def _latest_version(branch):
37 """Version of the most recent source package upload in the given `branch`.
38
39 :param branch: A Branch object containing the source upload of interest.
40 """
41 changelog, _ignore = find_changelog(branch.basis_tree(), False)
42
43 return changelog.version
44
45
46def _upstream_version_data(source, target):
47 """Most recent upstream versions/revision IDs of the merge source/target.
48
49 Please note: both packaging branches must have been read-locked
50 beforehand.
51
52 :param source: The merge source branch.
53 :param target: The merge target branch.
54 """
55 results = list()
56 for branch in (source, target):
57 db = DistributionBranch(branch, branch)
58 uver = _latest_version(branch).upstream_version
59 results.append((uver, db.revid_of_upstream_version_from_branch(uver)))
60
61 return results
62
63
64def fix_ancestry_as_needed(tree, source):
65 """Manipulate the merge target's ancestry to avoid upstream conflicts.
66
67 Merging J->I given the following ancestry tree is likely to result in
68 upstream merge conflicts:
69
70 debian-upstream ,------------------H
71 A-----------B \
72 ubuntu-upstream \ \`-------G \
73 \ \ \ \
74 debian-packaging \ ,---------D--------\-----------J
75 C \ \
76 ubuntu-packaging `----E------F--------I
77
78 Here there was a new upstream release (G) that Ubuntu packaged (I), and
79 then another one that Debian packaged, skipping G, at H and J.
80
81 Now, the way to solve this is to introduce the missing link.
82
83 debian-upstream ,------------------H------.
84 A-----------B \ \
85 ubuntu-upstream \ \`-------G-----------\------K
86 \ \ \ \
87 debian-packaging \ ,---------D--------\-----------J
88 C \ \
89 ubuntu-packaging `----E------F--------I
90
91 at K, which isn't a real merge, as we just use the tree from H, but add
92 G as a parent and then we merge that in to Ubuntu.
93
94 debian-upstream ,------------------H------.
95 A-----------B \ \
96 ubuntu-upstream \ \`-------G-----------\------K
97 \ \ \ \ \
98 debian-packaging \ ,---------D--------\-----------J \
99 C \ \ \
100 ubuntu-packaging `----E------F--------I------------------L
101
102 At this point we can merge J->L to merge the Debian and Ubuntu changes.
103
104 :param tree: The `WorkingTree` of the merge target branch.
105 :param source: The merge source (packaging) branch.
106 """
107 upstreams_diverged = False
108 t_upstream_reverted = False
109 target = tree.branch
110
111 source.lock_read()
112 try:
113 tree.lock_read()
114 try:
115 # "Unpack" the upstream versions and revision ids for the merge
116 # source and target branch respectively.
117 [(us_ver, us_revid), (ut_ver, ut_revid)] = _upstream_version_data(source, target)
118
119 # Did the upstream branches of the merge source/target diverge?
120 graph = source.repository.get_graph(target.repository)
121 upstreams_diverged = (len(graph.heads([us_revid, ut_revid])) > 1)
122 finally:
123 tree.unlock()
124 finally:
125 source.unlock()
126
127 if not upstreams_diverged:
128 return (upstreams_diverged, t_upstream_reverted)
129
130 # Instantiate a `DistributionBranch` object for the merge target
131 # (packaging) branch.
132 db = DistributionBranch(tree.branch, tree.branch)
133 tempdir = tempfile.mkdtemp(dir=os.path.join(tree.basedir, '..'))
134
135 # Extract the merge target's upstream tree into a temporary directory.
136 db.extract_upstream_tree(ut_revid, tempdir)
137 tmp_target_upstream_tree = db.upstream_tree
138
139 # Merge upstream branch tips to obtain a shared upstream parent. This
140 # will add revision K (see graph above) to a temporary merge target
141 # upstream tree.
142 tmp_target_upstream_tree.lock_write()
143 try:
144 if us_ver > ut_ver:
145 # The source upstream tree is more recent and the temporary
146 # target tree needs to be reshaped to match it.
147 tmp_target_upstream_tree.revert(
148 None, source.repository.revision_tree(us_revid))
149 t_upstream_reverted = True
150
151 tmp_target_upstream_tree.set_parent_ids(
152 (ut_revid, us_revid))
153
154 tmp_target_upstream_tree.commit(
155 'Consolidated upstream tree for merging into target branch')
156 finally:
157 tmp_target_upstream_tree.unlock()
158
159 # Merge shared upstream parent into the target merge branch. This creates
160 # revison L in the digram above.
161 tree.lock_write()
162 try:
163 conflicts = tree.merge_from_branch(tmp_target_upstream_tree.branch)
164 if conflicts > 0:
165 raise errors.SharedUpstreamConflictsWithTargetPackaging()
166 else:
167 tree.commit('Merging shared upstream rev in to target branch.')
168 finally:
169 tree.unlock()
170
171 return (upstreams_diverged, t_upstream_reverted)
0172
=== modified file 'tests/__init__.py'
--- tests/__init__.py 2009-07-04 20:45:01 +0000
+++ tests/__init__.py 2009-08-19 09:55:06 +0000
@@ -118,6 +118,7 @@
118 'test_config',118 'test_config',
119 'test_hooks',119 'test_hooks',
120 'test_import_dsc',120 'test_import_dsc',
121 'test_merge_package',
121 'test_merge_upstream',122 'test_merge_upstream',
122 'test_repack_tarball_extra',123 'test_repack_tarball_extra',
123 'test_revspec',124 'test_revspec',
124125
=== added file 'tests/test_merge_package.py'
--- tests/test_merge_package.py 1970-01-01 00:00:00 +0000
+++ tests/test_merge_package.py 2009-08-21 12:40:48 +0000
@@ -0,0 +1,537 @@
1#!/usr/bin/env python
2# -*- coding: iso-8859-15 -*-
3# test_merge_package.py -- Merge packaging branches, fix ancestry as needed.
4# Copyright (C) 2008 Canonical Ltd.
5#
6# This file is part of bzr-builddeb.
7#
8# bzr-builddeb is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13# bzr-builddeb is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with bzr-builddeb; if not, write to the Free Software
20# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
22import string
23import unittest
24
25from debian_bundle.changelog import Version
26
27from bzrlib.errors import ConflictsInTree
28from bzrlib.tests import TestCaseWithTransport
29
30from bzrlib.plugins.builddeb import merge_package as MP
31from bzrlib.plugins.builddeb.import_dsc import DistributionBranch
32
33_Debian_changelog = '''\
34ipsec-tools (%s) unstable; urgency=high
35
36 * debian packaging -- %s
37
38 -- Nico Golde <nion@debian.org> Tue, %02d May 2009 13:26:14 +0200
39
40'''
41
42_Ubuntu_changelog = '''\
43ipsec-tools (%s) karmic; urgency=low
44
45 * ubuntu packaging -- %s
46
47 -- Jamie Strandboge <jamie@ubuntu.com> Fri, %02d Jul 2009 13:24:17 -0500
48
49'''
50
51
52def _prepend_log(text, path):
53 content = open(path).read()
54 fh = open(path, 'wb')
55 try:
56 fh.write(text+content)
57 finally:
58 fh.close()
59
60
61class MergePackageTests(TestCaseWithTransport):
62
63 def test_latest_upstream_versions(self):
64 """Check correctness of upstream version computation."""
65 ubup_o, debp_n, _ubuu, _debu = self._setup_debian_upstream_newer()
66 # Ubuntu upstream.
67 self.assertEquals(
68 MP._latest_version(ubup_o).upstream_version, '1.1.2')
69 # Debian upstream.
70 self.assertEquals(
71 MP._latest_version(debp_n).upstream_version, '2.0')
72
73 ubuntup, debianp = self._setup_upstreams_not_diverged()
74 # Ubuntu upstream.
75 self.assertEquals(
76 MP._latest_version(ubuntup).upstream_version, '1.4')
77 # Debian upstream.
78 self.assertEquals(
79 MP._latest_version(debianp).upstream_version, '2.2')
80
81 def test_debian_upstream_newer(self):
82 """Diverging upstreams (debian newer) don't cause merge conflicts.
83
84 The debian and ubuntu upstream branches will differ with regard to
85 the content of the file 'c'.
86
87 Furthermore the respective packaging branches will have a text
88 conflict in 'debian/changelog'.
89
90 The upstream conflict will be resolved by fix_ancestry_as_needed().
91 Please note that the debian ancestry is more recent.
92 """
93 ubup, debp, ubuu, debu = self._setup_debian_upstream_newer()
94
95 # Attempt a plain merge first.
96 conflicts = ubup.merge_from_branch(
97 debp.branch, to_revision=self.revid_debp_n_C)
98
99 # There are two conflicts in the 'c' and the 'debian/changelog' files
100 # respectively.
101 self.assertEquals(conflicts, 2)
102 conflict_paths = sorted([c.path for c in ubup.conflicts()])
103 self.assertEquals(conflict_paths, [u'c.moved', u'debian/changelog'])
104
105 # Undo the failed merge.
106 ubup.revert()
107
108 # Check the versions present in the tree with the fixed ancestry.
109 v3 = "1.1.2"
110 v4 = "2.0"
111 db1 = DistributionBranch(ubup.branch, ubup.branch)
112 self.assertEqual(db1.has_upstream_version(v3), True)
113 # This version is in the diverged debian upstream tree and will
114 # hence not be present in the target ubuntu packaging branch.
115 self.assertEqual(db1.has_upstream_version(v4), False)
116
117 # The ubuntu upstream branch tip.
118 ubuu_tip = ubuu.branch.revision_history()[-1]
119 # The debian upstream branch tip.
120 debu_tip = debu.branch.revision_history()[-1]
121 # The ubuntu packaging branch tip.
122 ubup_tip_pre_fix = ubup.branch.revision_history()[-1]
123
124 # The first conflict is resolved by calling fix_ancestry_as_needed().
125 upstreams_diverged, t_upstream_reverted = MP.fix_ancestry_as_needed(ubup, debp.branch)
126
127 # The ancestry did diverge and needed to be fixed.
128 self.assertEquals(upstreams_diverged, True)
129 # The (temporary) target upstream branch had to be reverted to the
130 # source upstream branch since the latter was more recent.
131 self.assertEquals(t_upstream_reverted, True)
132
133 # Check the versions present in the tree with the fixed ancestry.
134 db2 = DistributionBranch(ubup.branch, ubup.branch)
135 self.assertEqual(db2.has_upstream_version(v3), True)
136 # The ancestry has been fixed and the missing debian upstream
137 # version should now be present in the target ubuntu packaging
138 # branch.
139 self.assertEqual(db2.has_upstream_version(v4), True)
140
141 # Now let's take a look at the fixed ubuntu packaging branch.
142 ubup_tip_post_fix = ubup.branch.revision_history()[-1]
143 ubup_parents_post_fix = ubup.branch.repository.revision_tree(ubup_tip_post_fix).get_parent_ids()
144
145 # The tip of the fixed ubuntu packaging branch has 2 parents.
146 self.assertEquals(len(ubup_parents_post_fix), 2)
147
148 # The left parent is the packaging branch tip before fixing.
149 self.assertEquals(ubup_parents_post_fix[0], ubup_tip_pre_fix)
150
151 # The right parent is derived from a merge
152 ubup_parents_sharedupstream = ubup.branch.repository.revision_tree(ubup_parents_post_fix[1]).get_parent_ids()
153 self.assertEquals(ubup_parents_sharedupstream, [ubuu_tip, debu_tip])
154
155 # Try merging again.
156 conflicts = ubup.merge_from_branch(
157 debp.branch, to_revision=self.revid_debp_n_C)
158
159 # And, voila, only the packaging branch conflict remains.
160 self.assertEquals(conflicts, 1)
161 conflict_paths = sorted([c.path for c in ubup.conflicts()])
162 self.assertEquals(conflict_paths, [u'debian/changelog'])
163
164 def test_debian_upstream_older(self):
165 """Diverging upstreams (debian older) don't cause merge conflicts.
166
167 The debian and ubuntu upstream branches will differ with regard to
168 the content of the file 'c'.
169
170 Furthermore the respective packaging branches will have a text
171 conflict in 'debian/changelog'.
172
173 The upstream conflict will be resolved by fix_ancestry_as_needed().
174 Please note that the debian ancestry is older in this case.
175 """
176 ubup_n, debp_o = self._setup_debian_upstream_older()
177
178 # Attempt a plain merge first.
179 conflicts = ubup_n.merge_from_branch(
180 debp_o.branch, to_revision=self.revid_debp_o_C)
181
182 # There are two conflicts in the 'c' and the 'debian/changelog' files
183 # respectively.
184 self.assertEquals(conflicts, 2)
185 conflict_paths = sorted([c.path for c in ubup_n.conflicts()])
186 self.assertEquals(conflict_paths, [u'c.moved', u'debian/changelog'])
187
188 # Undo the failed merge.
189 ubup_n.revert()
190
191 # The first conflict is resolved by calling fix_ancestry_as_needed().
192 upstreams_diverged, t_upstream_reverted = MP.fix_ancestry_as_needed(ubup_n, debp_o.branch)
193
194 # The ancestry did diverge and needed to be fixed.
195 self.assertEquals(upstreams_diverged, True)
196 # The target upstream branch was more recent in this case and hence
197 # was not reverted to the source upstream branch.
198 self.assertEquals(t_upstream_reverted, False)
199
200 # Try merging again.
201 conflicts = ubup_n.merge_from_branch(
202 debp_o.branch, to_revision=self.revid_debp_o_C)
203
204 # And, voila, only the packaging branch conflict remains.
205 self.assertEquals(conflicts, 1)
206 conflict_paths = sorted([c.path for c in ubup_n.conflicts()])
207 self.assertEquals(conflict_paths, [u'debian/changelog'])
208
209 def test_upstreams_not_diverged(self):
210 """Non-diverging upstreams result in a normal merge.
211
212 The debian and ubuntu upstream branches will not have diverged
213 this time.
214
215 The packaging branches will have a conflict in 'debian/changelog'.
216 fix_ancestry_as_needed() will return as soon as establishing that
217 the upstreams have not diverged.
218 """
219 ubuntup, debianp = self._setup_upstreams_not_diverged()
220
221 # Attempt a plain merge first.
222 conflicts = ubuntup.merge_from_branch(
223 debianp.branch, to_revision=self.revid_debianp_C)
224
225 # There is only a conflict in the 'debian/changelog' file.
226 self.assertEquals(conflicts, 1)
227 conflict_paths = sorted([c.path for c in ubuntup.conflicts()])
228 self.assertEquals(conflict_paths, [u'debian/changelog'])
229
230 # Undo the failed merge.
231 ubuntup.revert()
232
233 # The conflict is *not* resolved by calling fix_ancestry_as_needed().
234 upstreams_diverged, t_upstream_reverted = MP.fix_ancestry_as_needed(ubuntup, debianp.branch)
235
236 # The ancestry did *not* diverge.
237 self.assertEquals(upstreams_diverged, False)
238 # The upstreams have not diverged, hence no need to fix/revert
239 # either of them.
240 self.assertEquals(t_upstream_reverted, False)
241
242 # Try merging again.
243 conflicts = ubuntup.merge_from_branch(
244 debianp.branch, to_revision=self.revid_debianp_C)
245
246 # The packaging branch conflict we saw above is still there.
247 self.assertEquals(conflicts, 1)
248 conflict_paths = sorted([c.path for c in ubuntup.conflicts()])
249 self.assertEquals(conflict_paths, [u'debian/changelog'])
250
251 def _setup_debian_upstream_newer(self):
252 """
253 Set up the following test configuration (debian upstream newer).
254
255 debian-upstream ,------------------H
256 A-----------B \
257 ubuntu-upstream \ \`-------G \
258 \ \ \ \
259 debian-packaging \ ,---------D--------\-----------J
260 C \
261 ubuntu-packaging `----E---------------I
262
263 where:
264 - A = 1.0
265 - B = 1.1
266 - H = 2.0
267
268 - G = 1.1.2
269
270 - C = 1.0-1
271 - D = 1.1-1
272 - J = 2.0-1
273
274 - E = 1.0-1ubuntu1
275 - I = 1.1.2-0ubuntu1
276
277 Please note that the debian and ubuntu branches will have a conflict
278 with respect to the file 'c'.
279 """
280 # Set up the debian upstream branch.
281 name = 'debu-n'
282 vdata = [
283 ('upstream-1.0', ('a',), None, None),
284 ('upstream-1.1', ('b',), None, None),
285 ('upstream-2.0', ('c',), None, None),
286 ]
287 debu_n = self._setup_branch(name, vdata)
288
289 # Set up the debian packaging branch.
290 name = 'debp-n'
291 debp_n = self.make_branch_and_tree(name)
292 debp_n.pull(debu_n.branch, stop_revision=self.revid_debu_n_A)
293
294 vdata = [
295 ('1.0-1', ('debian/', 'debian/changelog'), None, None),
296 ('1.1-1', ('o',), debu_n, self.revid_debu_n_B),
297 ('2.0-1', ('p',), debu_n, self.revid_debu_n_C),
298 ]
299 self._setup_branch(name, vdata, debp_n, 'd')
300
301 # Set up the ubuntu upstream branch.
302 name = 'ubuu-o'
303 ubuu_o = debu_n.bzrdir.sprout(
304 name, revision_id=self.revid_debu_n_B).open_workingtree()
305
306 vdata = [
307 ('upstream-1.1.2', ('c',), None, None),
308 ]
309 self._setup_branch(name, vdata, ubuu_o)
310
311 # Set up the ubuntu packaging branch.
312 name = 'ubup-o'
313 ubup_o = debu_n.bzrdir.sprout(
314 name, revision_id=self.revid_debu_n_A).open_workingtree()
315
316 vdata = [
317 ('1.0-1ubuntu1', (), debp_n, self.revid_debp_n_A),
318 ('1.1.2-0ubuntu1', (), ubuu_o, self.revid_ubuu_o_A),
319 ]
320 self._setup_branch(name, vdata, ubup_o, 'u')
321
322 # Return the ubuntu and the debian packaging branches.
323 return (ubup_o, debp_n, ubuu_o, debu_n)
324
325 def _setup_debian_upstream_older(self):
326 """
327 Set up the following test configuration (debian upstream older).
328
329 debian-upstream ,----H-------------.
330 A-----------B \
331 ubuntu-upstream \ \`-----------G \
332 \ \ \ \
333 debian-packaging \ ,---------D------------\-------J
334 C \
335 ubuntu-packaging `----E-------------------I
336
337 where:
338 - A = 1.0
339 - B = 1.1
340 - H = 1.1.3
341
342 - G = 2.1
343
344 - C = 1.0-1
345 - D = 1.1-1
346 - J = 1.1.3-1
347
348 - E = 1.0-1ubuntu1
349 - I = 2.1-0ubuntu1
350
351 Please note that the debian and ubuntu branches will have a conflict
352 with respect to the file 'c'.
353 """
354 # Set up the debian upstream branch.
355 name = 'debu-o'
356 vdata = [
357 ('upstream-1.0', ('a',), None, None),
358 ('upstream-1.1', ('b',), None, None),
359 ('upstream-1.1.3', ('c',), None, None),
360 ]
361 debu_o = self._setup_branch(name, vdata)
362
363 # Set up the debian packaging branch.
364 name = 'debp-o'
365 debp_o = self.make_branch_and_tree(name)
366 debp_o.pull(debu_o.branch, stop_revision=self.revid_debu_o_A)
367
368 vdata = [
369 ('1.0-1', ('debian/', 'debian/changelog'), None, None),
370 ('1.1-1', ('o',), debu_o, self.revid_debu_o_B),
371 ('1.1.3-1', ('p',), debu_o, self.revid_debu_o_C),
372 ]
373 self._setup_branch(name, vdata, debp_o, 'd')
374
375 # Set up the ubuntu upstream branch.
376 name = 'ubuu-n'
377 ubuu_n = debu_o.bzrdir.sprout(
378 name, revision_id=self.revid_debu_o_B).open_workingtree()
379
380 vdata = [
381 ('upstream-2.1', ('c',), None, None),
382 ]
383 self._setup_branch(name, vdata, ubuu_n)
384
385 # Set up the ubuntu packaging branch.
386 name = 'ubup-n'
387 ubup_n = debu_o.bzrdir.sprout(
388 name, revision_id=self.revid_debu_o_A).open_workingtree()
389
390 vdata = [
391 ('1.0-1ubuntu1', (), debp_o, self.revid_debp_o_A),
392 ('2.1-0ubuntu1', (), ubuu_n, self.revid_ubuu_n_A),
393 ]
394 self._setup_branch(name, vdata, ubup_n, 'u')
395
396 # Return the ubuntu and the debian packaging branches.
397 return (ubup_n, debp_o)
398
399 def _setup_upstreams_not_diverged(self):
400 """
401 Set up a test configuration where the usptreams have not diverged.
402
403 debian-upstream .-----G
404 A-----------B-----H \
405 ubuntu-upstream \ \ \ \
406 \ \ \ \
407 debian-packaging \ ,---------D-----\-------J
408 C \
409 ubuntu-packaging `----E------------I
410
411 where:
412 - A = 1.0
413 - B = 1.1
414 - H = 1.4
415
416 - G = 2.2
417
418 - C = 1.0-1
419 - D = 1.1-1
420 - J = 2.2-1
421
422 - E = 1.0-1ubuntu1
423 - I = 1.4-0ubuntu1
424
425 Please note that there's only one shared upstream branch in this case.
426 """
427 # Set up the upstream branch.
428 name = 'upstream'
429 vdata = [
430 ('upstream-1.0', ('a',), None, None),
431 ('upstream-1.1', ('b',), None, None),
432 ('upstream-1.4', ('c',), None, None),
433 ]
434 upstream = self._setup_branch(name, vdata)
435
436 # Set up the debian upstream branch.
437 name = 'dupstream'
438 dupstream = upstream.bzrdir.sprout(name).open_workingtree()
439 vdata = [
440 ('upstream-2.2', (), None, None),
441 ]
442 dupstream = self._setup_branch(name, vdata, dupstream)
443
444 # Set up the debian packaging branch.
445 name = 'debianp'
446 debianp = self.make_branch_and_tree(name)
447 debianp.pull(dupstream.branch, stop_revision=self.revid_upstream_A)
448
449 vdata = [
450 ('1.0-1', ('debian/', 'debian/changelog'), None, None),
451 ('1.1-1', ('o',), dupstream, self.revid_upstream_B),
452 ('2.2-1', ('p',), dupstream, self.revid_dupstream_A),
453 ]
454 self._setup_branch(name, vdata, debianp, 'd')
455
456 # Set up the ubuntu packaging branch.
457 name = 'ubuntup'
458 ubuntup = upstream.bzrdir.sprout(
459 name, revision_id=self.revid_upstream_A).open_workingtree()
460
461 vdata = [
462 ('1.0-1ubuntu1', (), debianp, self.revid_debianp_A),
463 ('1.4-0ubuntu1', (), upstream, self.revid_upstream_C),
464 ]
465 self._setup_branch(name, vdata, ubuntup, 'u')
466
467 # Return the ubuntu and the debian packaging branches.
468 return (ubuntup, debianp)
469
470 def _setup_branch(self, name, vdata, tree=None, log_format=None):
471 vids = list(string.ascii_uppercase)
472 days = range(len(string.ascii_uppercase))
473
474 if tree is None:
475 tree = self.make_branch_and_tree(name)
476
477 tree.lock_write()
478 self.addCleanup(tree.unlock)
479
480 def revid_name(vid):
481 return 'revid_%s_%s' % (name.replace('-', '_'), vid)
482
483 def add_paths(paths):
484 qpaths = ['%s/%s' % (name, path) for path in paths]
485 self.build_tree(qpaths)
486 tree.add(paths)
487
488 def changelog(vdata, vid):
489 result = ''
490 day = days.pop(0)
491 if isinstance(vdata, tuple):
492 uver, dver = vdata[:2]
493 ucle = _Ubuntu_changelog % (uver, vid, day)
494 dcle = _Debian_changelog % (dver, vid, day)
495 result = ucle + dcle
496 else:
497 if log_format == 'u':
498 result = _Ubuntu_changelog % (vdata, vid, day)
499 elif log_format == 'd':
500 result = _Debian_changelog % (vdata, vid, day)
501
502 return result
503
504 def commit(msg, version):
505 vid = vids.pop(0)
506 if log_format is not None:
507 cle = changelog(version, vid)
508 p = '%s/work/%s/debian/changelog' % (self.test_base_dir, name)
509 _prepend_log(cle, p)
510 revid = tree.commit('%s: %s' % (vid, msg))
511 setattr(self, revid_name(vid), revid)
512 tree.branch.tags.set_tag(version, revid)
513
514 def tree_nick(tree):
515 return str(tree)[1:-1].split('/')[-1]
516
517 for version, paths, utree, urevid in vdata:
518 msg = ''
519 if utree is not None:
520 tree.merge_from_branch(utree.branch, to_revision=urevid)
521 utree.branch.tags.merge_to(tree.branch.tags)
522 if urevid is not None:
523 msg += 'Merged tree %s|%s. ' % (tree_nick(utree), urevid)
524 else:
525 msg += 'Merged tree %s. ' % utree
526 if paths is not None:
527 add_paths(paths)
528 msg += 'Added paths: %s. ' % str(paths)
529
530 commit(msg, version)
531
532 return tree
533
534
535if __name__ == '__main__':
536 suite = unittest.TestLoader().loadTestsFromTestCase(MergePackageTests)
537 unittest.TextTestRunner(verbosity=2).run(suite)
0538
=== modified file 'upstream.py'
--- upstream.py 2009-07-26 18:21:49 +0000
+++ upstream.py 2009-08-06 08:40:05 +0000
@@ -79,7 +79,7 @@
79 db = DistributionBranch(self.branch, None, tree=self.tree)79 db = DistributionBranch(self.branch, None, tree=self.tree)
80 if not db.has_upstream_version_in_packaging_branch(version):80 if not db.has_upstream_version_in_packaging_branch(version):
81 raise PackageVersionNotPresent(package, version, self)81 raise PackageVersionNotPresent(package, version, self)
82 revid = db._revid_of_upstream_version_from_branch(version)82 revid = db.revid_of_upstream_version_from_branch(version)
83 if not db.has_pristine_tar_delta(revid):83 if not db.has_pristine_tar_delta(revid):
84 raise PackageVersionNotPresent(package, version, self)84 raise PackageVersionNotPresent(package, version, self)
85 info("Using pristine-tar to reconstruct the needed tarball.")85 info("Using pristine-tar to reconstruct the needed tarball.")
Revision history for this message
James Westby (james-w) wrote :

Excerpts from Muharem Hrnjadovic's message of Fri Aug 21 12:51:08 UTC 2009:
> > This is the big question I have left though. How does the user continue
> > after this? There should be a clear way to continue once they have committed,
> > and the exception should tell them how to proceed.
>
> How about this?
>
> {{{
> The "merge-package" command has detected diverged upstream
> branches for the merge source and target. A shared upstream
> revision was constructed to remedy the problem.
>
> However, merging the shared upstream revision into the merge
> target resulted in conflicts.
>
> Please proceed as follows:
>
> 1 - Resolve the current merge conflicts in the merge target
> directory and commit the changes.
> 2 - Perform a plain "bzr merge <source-packaging-branch>"
> command, resolve any ensuing packaging branch conflicts
> and commit once satisfied with the changes.
> }}}

For starters this is bit verbose, we can't really format it like that
in an error message, so it needs to be a couple of sentences.

I don't think they need to know that a new "shared upstream" was
created, you can just say that there are conflicts but they are
not finished.

Should we direct them to "bzr merge-package" again? It becomes
equivalent, but I think it's better to not confuse them with
when they can use "bzr merge" and when they can't.

You don't need to tell them what to do after they've run that again,
it can just tell them when it is done.

Would it be worth saying that if they don't like what they see
they can just "bzr revert" to get back to where they started?

Looking at the incremental diff the major issue I see remaining
is that you lock and unlock the tree, only to lock it again.
Lock it once, and hold that lock until you don't reference the
tree or anything that makes use of it any more.

Writing this mail also interested me in whether you ever get any
conflicts in the case when merging the faked upstream merge
in to the packaging branch in the case where your upstream
version was newer than the other upstream version.

Thanks,

James

404. By Muharem Hrnjadovic

Target branch is now being locked once only; James' review comments, round 7

405. By Muharem Hrnjadovic

Enhanced error message for case where merging the shared upstreams revision results in conflicts; James' review comments, round 8

406. By Muharem Hrnjadovic

Minor fix.

407. By Muharem Hrnjadovic

Slightly cleaned-up test.

Revision history for this message
Muharem Hrnjadovic (al-maisan) wrote :

James Westby wrote:
> Excerpts from Muharem Hrnjadovic's message of Fri Aug 21 12:51:08 UTC 2009:
>>> This is the big question I have left though. How does the user continue
>>> after this? There should be a clear way to continue once they have committed,
>>> and the exception should tell them how to proceed.
>> How about this?
>>
>> {{{
>> The "merge-package" command has detected diverged upstream
>> branches for the merge source and target. A shared upstream
>> revision was constructed to remedy the problem.
>>
>> However, merging the shared upstream revision into the merge
>> target resulted in conflicts.
>>
>> Please proceed as follows:
>>
>> 1 - Resolve the current merge conflicts in the merge target
>> directory and commit the changes.
>> 2 - Perform a plain "bzr merge <source-packaging-branch>"
>> command, resolve any ensuing packaging branch conflicts
>> and commit once satisfied with the changes.
>> }}}
>
> For starters this is bit verbose, we can't really format it like that
> in an error message, so it needs to be a couple of sentences.
>
> I don't think they need to know that a new "shared upstream" was
> created, you can just say that there are conflicts but they are
> not finished.
>
> Should we direct them to "bzr merge-package" again? It becomes
> equivalent, but I think it's better to not confuse them with
> when they can use "bzr merge" and when they can't.
>
> You don't need to tell them what to do after they've run that again,
> it can just tell them when it is done.
>
> Would it be worth saying that if they don't like what they see
> they can just "bzr revert" to get back to where they started?

Hello James,

thank you very much for your suggestions above. They are all good. I
hope the following text is more suitable.

{{{
The upstream branches for the merge source and target have diverged.
Unfortunately, the attempt to fix this problem resulted in conflicts.
Please resolve these and re-run the "merge-package" command to finish.
Alternatively, you can restore the original merge target state by
running "bzr revert".
}}}

>
> Looking at the incremental diff the major issue I see remaining
> is that you lock and unlock the tree, only to lock it again.
> Lock it once, and hold that lock until you don't reference the
> tree or anything that makes use of it any more.

Done.

> Writing this mail also interested me in whether you ever get any
> conflicts in the case when merging the faked upstream merge
> in to the packaging branch in the case where your upstream
> version was newer than the other upstream version.

That would be the test_debian_upstream_older() test, and, no, I did not
get any conflicts.

Please find the incremental diff enclosed.

Best regards

--
Muharem Hrnjadovic <email address hidden>
Public key id : B2BBFCFC
Key fingerprint : A5A3 CC67 2B87 D641 103F 5602 219F 6B60 B2BB FCFC

=== modified file 'cmds.py'
--- cmds.py 2009-08-19 09:06:47 +0000
+++ cmds.py 2009-08-24 10:28:15 +0000
@@ -901,7 +901,13 @@
901 fix_ancestry_as_needed(tree, source_branch)901 fix_ancestry_as_needed(tree, source_branch)
902902
903 # Merge source packaging branch in to the target packaging branch.903 # Merge source packaging branch in to the target packaging branch.
904 tree.merge_from_branch(source_branch)904 conflicts = tree.merge_from_branch(source_branch)
905 if conflicts > 0:
906 info('The merge resulted in %s conflicts. Please resolve these '
907 'and commit the changes with "bzr commit".' % conflicts)
908 else:
909 info('The merge resulted in no conflicts. You may commit the '
910 'changes by running "bzr commit".')
905911
906912
907class cmd_test_builddeb(Command):913class cmd_test_builddeb(Command):
908914
=== modified file 'errors.py'
--- errors.py 2009-08-19 14:09:53 +0000
+++ errors.py 2009-08-24 10:36:04 +0000
@@ -179,18 +179,5 @@
179179
180class SharedUpstreamConflictsWithTargetPackaging(BzrError):180class SharedUpstreamConflictsWithTargetPackaging(BzrError):
181 _fmt = ('''\181 _fmt = ('''\
182 The "merge-package" command has detected diverged upstream182The upstream branches for the merge source and target have diverged. Unfortunately, the attempt to fix this problem resulted in conflicts. Please resolve these and re-run the "merge-package" command to finish.
183 branches for the merge source and target. A shared upstream183Alternatively, you can restore the original merge target state by running "bzr revert".''')
184 revision was constructed to remedy the problem.
185
186 However, merging the shared upstream revision into the merge
187 target resulted in conflicts.
188
189 Please proceed as follows:
190
191 1 - Resolve the current merge conflicts in the merge target
192 directory and commit the changes.
193 2 - Perform a plain "bzr merge <source-packaging-branch>"
194 command, resolve any ensuing packaging branch conflicts
195 and commit once satisfied with the changes.
196 ''')
197184
=== modified file 'merge_package.py'
--- merge_package.py 2009-08-20 07:39:39 +0000
+++ merge_package.py 2009-08-24 10:22:13 +0000
@@ -110,7 +110,7 @@
110110
111 source.lock_read()111 source.lock_read()
112 try:112 try:
113 tree.lock_read()113 tree.lock_write()
114 try:114 try:
115 # "Unpack" the upstream versions and revision ids for the merge115 # "Unpack" the upstream versions and revision ids for the merge
116 # source and target branch respectively.116 # source and target branch respectively.
@@ -119,53 +119,50 @@
119 # Did the upstream branches of the merge source/target diverge?119 # Did the upstream branches of the merge source/target diverge?
120 graph = source.repository.get_graph(target.repository)120 graph = source.repository.get_graph(target.repository)
121 upstreams_diverged = (len(graph.heads([us_revid, ut_revid])) > 1)121 upstreams_diverged = (len(graph.heads([us_revid, ut_revid])) > 1)
122
123 # No, we're done!
124 if not upstreams_diverged:
125 return (upstreams_diverged, t_upstream_reverted)
126
127 # Instantiate a `DistributionBranch` object for the merge target
128 # (packaging) branch.
129 db = DistributionBranch(tree.branch, tree.branch)
130 tempdir = tempfile.mkdtemp(dir=os.path.join(tree.basedir, '..'))
131
132 # Extract the merge target's upstream tree into a temporary
133 # directory.
134 db.extract_upstream_tree(ut_revid, tempdir)
135 tmp_target_utree = db.upstream_tree
136
137 # Merge upstream branch tips to obtain a shared upstream parent.
138 # This will add revision K (see graph above) to a temporary merge
139 # target upstream tree.
140 tmp_target_utree.lock_write()
141 try:
142 if us_ver > ut_ver:
143 # The source upstream tree is more recent and the
144 # temporary target tree needs to be reshaped to match it.
145 tmp_target_utree.revert(
146 None, source.repository.revision_tree(us_revid))
147 t_upstream_reverted = True
148
149 tmp_target_utree.set_parent_ids((ut_revid, us_revid))
150 tmp_target_utree.commit(
151 'Prepared upstream tree for merging into target branch.')
152 finally:
153 tmp_target_utree.unlock()
154
155 # Merge shared upstream parent into the target merge branch. This
156 # creates revison L in the digram above.
157 conflicts = tree.merge_from_branch(tmp_target_utree.branch)
158 if conflicts > 0:
159 raise errors.SharedUpstreamConflictsWithTargetPackaging()
160 else:
161 tree.commit('Merging shared upstream rev into target branch.')
162
122 finally:163 finally:
123 tree.unlock()164 tree.unlock()
124 finally:165 finally:
125 source.unlock()166 source.unlock()
126167
127 if not upstreams_diverged:
128 return (upstreams_diverged, t_upstream_reverted)
129
130 # Instantiate a `DistributionBranch` object for the merge target
131 # (packaging) branch.
132 db = DistributionBranch(tree.branch, tree.branch)
133 tempdir = tempfile.mkdtemp(dir=os.path.join(tree.basedir, '..'))
134
135 # Extract the merge target's upstream tree into a temporary directory.
136 db.extract_upstream_tree(ut_revid, tempdir)
137 tmp_target_upstream_tree = db.upstream_tree
138
139 # Merge upstream branch tips to obtain a shared upstream parent. This
140 # will add revision K (see graph above) to a temporary merge target
141 # upstream tree.
142 tmp_target_upstream_tree.lock_write()
143 try:
144 if us_ver > ut_ver:
145 # The source upstream tree is more recent and the temporary
146 # target tree needs to be reshaped to match it.
147 tmp_target_upstream_tree.revert(
148 None, source.repository.revision_tree(us_revid))
149 t_upstream_reverted = True
150
151 tmp_target_upstream_tree.set_parent_ids(
152 (ut_revid, us_revid))
153
154 tmp_target_upstream_tree.commit(
155 'Consolidated upstream tree for merging into target branch')
156 finally:
157 tmp_target_upstream_tree.unlock()
158
159 # Merge shared upstream parent into the target merge branch. This creates
160 # revison L in the digram above.
161 tree.lock_write()
162 try:
163 conflicts = tree.merge_from_branch(tmp_target_upstream_tree.branch)
164 if conflicts > 0:
165 raise errors.SharedUpstreamConflictsWithTargetPackaging()
166 else:
167 tree.commit('Merging shared upstream rev in to target branch.')
168 finally:
169 tree.unlock()
170
171 return (upstreams_diverged, t_upstream_reverted)168 return (upstreams_diverged, t_upstream_reverted)
172169
=== modified file 'tests/test_merge_package.py'
--- tests/test_merge_package.py 2009-08-21 12:41:34 +0000
+++ tests/test_merge_package.py 2009-08-24 10:44:31 +0000
@@ -173,23 +173,23 @@
173 The upstream conflict will be resolved by fix_ancestry_as_needed().173 The upstream conflict will be resolved by fix_ancestry_as_needed().
174 Please note that the debian ancestry is older in this case.174 Please note that the debian ancestry is older in this case.
175 """175 """
176 ubup_n, debp_o = self._setup_debian_upstream_older()176 ubup, debp, _ubuu, _debu = self._setup_debian_upstream_older()
177177
178 # Attempt a plain merge first.178 # Attempt a plain merge first.
179 conflicts = ubup_n.merge_from_branch(179 conflicts = ubup.merge_from_branch(
180 debp_o.branch, to_revision=self.revid_debp_o_C)180 debp.branch, to_revision=self.revid_debp_o_C)
181181
182 # There are two conflicts in the 'c' and the 'debian/changelog' files182 # There are two conflicts in the 'c' and the 'debian/changelog' files
183 # respectively.183 # respectively.
184 self.assertEquals(conflicts, 2)184 self.assertEquals(conflicts, 2)
185 conflict_paths = sorted([c.path for c in ubup_n.conflicts()])185 conflict_paths = sorted([c.path for c in ubup.conflicts()])
186 self.assertEquals(conflict_paths, [u'c.moved', u'debian/changelog'])186 self.assertEquals(conflict_paths, [u'c.moved', u'debian/changelog'])
187187
188 # Undo the failed merge.188 # Undo the failed merge.
189 ubup_n.revert()189 ubup.revert()
190190
191 # The first conflict is resolved by calling fix_ancestry_as_needed().191 # The first conflict is resolved by calling fix_ancestry_as_needed().
192 upstreams_diverged, t_upstream_reverted = MP.fix_ancestry_as_needed(ubup_n, debp_o.branch)192 upstreams_diverged, t_upstream_reverted = MP.fix_ancestry_as_needed(ubup, debp.branch)
193193
194 # The ancestry did diverge and needed to be fixed.194 # The ancestry did diverge and needed to be fixed.
195 self.assertEquals(upstreams_diverged, True)195 self.assertEquals(upstreams_diverged, True)
@@ -198,12 +198,12 @@
198 self.assertEquals(t_upstream_reverted, False)198 self.assertEquals(t_upstream_reverted, False)
199199
200 # Try merging again.200 # Try merging again.
201 conflicts = ubup_n.merge_from_branch(201 conflicts = ubup.merge_from_branch(
202 debp_o.branch, to_revision=self.revid_debp_o_C)202 debp.branch, to_revision=self.revid_debp_o_C)
203203
204 # And, voila, only the packaging branch conflict remains.204 # And, voila, only the packaging branch conflict remains.
205 self.assertEquals(conflicts, 1)205 self.assertEquals(conflicts, 1)
206 conflict_paths = sorted([c.path for c in ubup_n.conflicts()])206 conflict_paths = sorted([c.path for c in ubup.conflicts()])
207 self.assertEquals(conflict_paths, [u'debian/changelog'])207 self.assertEquals(conflict_paths, [u'debian/changelog'])
208208
209 def test_upstreams_not_diverged(self):209 def test_upstreams_not_diverged(self):
@@ -394,7 +394,7 @@
394 self._setup_branch(name, vdata, ubup_n, 'u')394 self._setup_branch(name, vdata, ubup_n, 'u')
395395
396 # Return the ubuntu and the debian packaging branches.396 # Return the ubuntu and the debian packaging branches.
397 return (ubup_n, debp_o)397 return (ubup_n, debp_o, ubuu_n, debu_o)
398398
399 def _setup_upstreams_not_diverged(self):399 def _setup_upstreams_not_diverged(self):
400 """400 """
Revision history for this message
James Westby (james-w) wrote :

Excerpts from Muharem Hrnjadovic's message of Mon Aug 24 10:57:08 UTC 2009:
> thank you very much for your suggestions above. They are all good. I
> hope the following text is more suitable.
>
> {{{
> The upstream branches for the merge source and target have diverged.
> Unfortunately, the attempt to fix this problem resulted in conflicts.
> Please resolve these and re-run the "merge-package" command to finish.
> Alternatively, you can restore the original merge target state by
> running "bzr revert".
> }}}

That's better. I think "resolve these and commit" is more explicit, and
I would prefer something other than "original merge target state", as
that sounds a bit jargonish, perhaps 'Alternatively, until you commit
you can use "bzr revert" to restore the state of the unmerged branch.'

It's not as clear what is going on, but I think that makes it easier
to understand :-)

> > Writing this mail also interested me in whether you ever get any
> > conflicts in the case when merging the faked upstream merge
> > in to the packaging branch in the case where your upstream
> > version was newer than the other upstream version.
>
> That would be the test_debian_upstream_older() test, and, no, I did not
> get any conflicts.

Sure, there's one test, and I wouldn't expect any conflicts in that
test, I was thinking more of testing other situations though. Not
as unit tests, but one-off testing to investigate whether you can
provoke conflicts. If it is possible then we may want to investigate
ways to avoid that.

> Please find the incremental diff enclosed.

I'll review this shortly.

Thanks,

James

408. By Muharem Hrnjadovic

Clarified error message in accordance with James' suggestion.

Revision history for this message
James Westby (james-w) wrote :

Excerpts from Muharem Hrnjadovic's message of Mon Aug 24 10:57:08 UTC 2009:
> Please find the incremental diff enclosed.

My only quibble with this is the wrapping of the exception message. Please
make it wrap at <80 chars. You can make it resolve to a single string though.

   _fmt = ('...............................................'
           '...............................................'
           '...............................................')

Then I think we should be good to go.

There is documentation needed, but we can get this landed first.

Thanks,

James

409. By Muharem Hrnjadovic

Better formating of error string.

Revision history for this message
Muharem Hrnjadovic (al-maisan) wrote :

James Westby wrote:
> Excerpts from Muharem Hrnjadovic's message of Mon Aug 24 10:57:08 UTC 2009:
>> Please find the incremental diff enclosed.
>
> My only quibble with this is the wrapping of the exception message. Please
> make it wrap at <80 chars. You can make it resolve to a single string though.
>
> _fmt = ('...............................................'
> '...............................................'
> '...............................................')
>
> Then I think we should be good to go.

done.

> There is documentation needed, but we can get this landed first.

I see. Please point me to an example if available.

Best regards

--
Muharem Hrnjadovic <email address hidden>
Public key id : B2BBFCFC
Key fingerprint : A5A3 CC67 2B87 D641 103F 5602 219F 6B60 B2BB FCFC

Revision history for this message
Muharem Hrnjadovic (al-maisan) wrote :

James Westby wrote:
> Excerpts from Muharem Hrnjadovic's message of Mon Aug 24 10:57:08 UTC 2009:
>> thank you very much for your suggestions above. They are all good. I
>> hope the following text is more suitable.
>>
>> {{{
>> The upstream branches for the merge source and target have diverged.
>> Unfortunately, the attempt to fix this problem resulted in conflicts.
>> Please resolve these and re-run the "merge-package" command to finish.
>> Alternatively, you can restore the original merge target state by
>> running "bzr revert".
>> }}}
>
> That's better. I think "resolve these and commit" is more explicit, and
> I would prefer something other than "original merge target state", as
> that sounds a bit jargonish, perhaps 'Alternatively, until you commit
> you can use "bzr revert" to restore the state of the unmerged branch.'
>
> It's not as clear what is going on, but I think that makes it easier
> to understand :-)

Thank you very much for your help with this :)

>>> Writing this mail also interested me in whether you ever get any
>>> conflicts in the case when merging the faked upstream merge
>>> in to the packaging branch in the case where your upstream
>>> version was newer than the other upstream version.
>> That would be the test_debian_upstream_older() test, and, no, I did not
>> get any conflicts.
>
> Sure, there's one test, and I wouldn't expect any conflicts in that
> test, I was thinking more of testing other situations though. Not
> as unit tests, but one-off testing to investigate whether you can
> provoke conflicts. If it is possible then we may want to investigate
> ways to avoid that.

I can think of a way to set up such a situation. There would need to
be a file that's shared and modified in all four branches: source
upstream, source packaging, target upstream and target packaging.
Furthermore the target upstream branch would have to be more recent one.

I will try to come up with this.

[..]

Best regards

--
Muharem Hrnjadovic <email address hidden>
Public key id : B2BBFCFC
Key fingerprint : A5A3 CC67 2B87 D641 103F 5602 219F 6B60 B2BB FCFC

Revision history for this message
James Westby (james-w) wrote :

On Mon Aug 24 16:30:19 UTC 2009 Muharem Hrnjadovic wrote:
> > There is documentation needed, but we can get this landed first.
>
> I see. Please point me to an example if available.

Check the doc/user_manual/ directory. We will also want to put a quick
recipe type thing on https://wiki.ubuntu.com/DistributedDevelopment/Documentation
as well.

One thing jumped to mind as well, perhaps there are some options of "merge"
we should support in "merge-package"? We can add these later if needed.

Thanks,

James

Revision history for this message
Muharem Hrnjadovic (al-maisan) wrote :

James Westby wrote:
> On Mon Aug 24 16:30:19 UTC 2009 Muharem Hrnjadovic wrote:
>>> There is documentation needed, but we can get this landed first.
>> I see. Please point me to an example if available.
>
> Check the doc/user_manual/ directory. We will also want to put a quick
> recipe type thing on https://wiki.ubuntu.com/DistributedDevelopment/Documentation
> as well.

Thanks, I will take a look.

> One thing jumped to mind as well, perhaps there are some options of "merge"
> we should support in "merge-package"? We can add these later if needed.

Hey, that's a good idea :)

Best regards

--
Muharem Hrnjadovic <email address hidden>
Public key id : B2BBFCFC
Key fingerprint : A5A3 CC67 2B87 D641 103F 5602 219F 6B60 B2BB FCFC

Revision history for this message
James Westby (james-w) wrote :

Looks good, thanks for all your work.

James

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '__init__.py'
--- __init__.py 2009-07-26 15:51:02 +0000
+++ __init__.py 2009-08-06 09:59:34 +0000
@@ -39,7 +39,8 @@
39 "merge_upstream": ["mu"],39 "merge_upstream": ["mu"],
40 "import_dsc": [],40 "import_dsc": [],
41 "bd_do": [],41 "bd_do": [],
42 "mark_uploaded": []42 "mark_uploaded": [],
43 "merge_package": ["mp"]
43 }44 }
4445
45for command, aliases in commands.iteritems():46for command, aliases in commands.iteritems():
4647
=== modified file 'cmds.py'
--- cmds.py 2009-07-26 18:21:49 +0000
+++ cmds.py 2009-08-19 09:06:47 +0000
@@ -69,6 +69,7 @@
69 DscCache,69 DscCache,
70 DscComp,70 DscComp,
71 )71 )
72from bzrlib.plugins.builddeb.merge_package import fix_ancestry_as_needed
72from bzrlib.plugins.builddeb.source_distiller import (73from bzrlib.plugins.builddeb.source_distiller import (
73 FullSourceDistiller,74 FullSourceDistiller,
74 MergeModeDistiller,75 MergeModeDistiller,
@@ -719,9 +720,9 @@
719 "the previous upstream version, %s, in the "720 "the previous upstream version, %s, in the "
720 "branch: %s" % (last_version,721 "branch: %s" % (last_version,
721 db.upstream_tag_name(last_version)))722 db.upstream_tag_name(last_version)))
722 upstream_tip = db._revid_of_upstream_version_from_branch(723 upstream_tip = db.revid_of_upstream_version_from_branch(
723 last_version)724 last_version)
724 db._extract_upstream_tree(upstream_tip, tempdir)725 db.extract_upstream_tree(upstream_tip, tempdir)
725 else:726 else:
726 db._create_empty_upstream_tree(tempdir)727 db._create_empty_upstream_tree(tempdir)
727 self.import_many(db, files_list, orig_target)728 self.import_many(db, files_list, orig_target)
@@ -870,6 +871,39 @@
870 t.unlock()871 t.unlock()
871872
872873
874class cmd_merge_package(Command):
875 """Merges source packaging branch into target packaging branch.
876
877 This will first check whether the upstream branches have diverged.
878
879 If that's the case an attempt will be made to fix the upstream ancestry
880 so that the user only needs to deal wth packaging branch merge issues.
881
882 In the opposite case a normal merge will be performed.
883 """
884 takes_args = ['source']
885
886 def run(self, source):
887 source_branch = target_branch = None
888 # Get the target branch.
889 try:
890 tree = WorkingTree.open_containing('.')[0]
891 target_branch = tree.branch
892 except NotBranchError:
893 raise BzrCommandError(
894 "There is no tree to merge the source branch in to")
895 # Get the source branch.
896 try:
897 source_branch = Branch.open(source)
898 except NotBranchError:
899 raise BzrCommandError("Invalid source branch URL?")
900
901 fix_ancestry_as_needed(tree, source_branch)
902
903 # Merge source packaging branch in to the target packaging branch.
904 tree.merge_from_branch(source_branch)
905
906
873class cmd_test_builddeb(Command):907class cmd_test_builddeb(Command):
874 """Run the builddeb test suite"""908 """Run the builddeb test suite"""
875909
@@ -880,4 +914,3 @@
880 passed = selftest(test_suite_factory=test_suite)914 passed = selftest(test_suite_factory=test_suite)
881 # invert for shell exit code rules915 # invert for shell exit code rules
882 return not passed916 return not passed
883
884917
=== modified file 'import_dsc.py'
--- import_dsc.py 2009-07-26 16:44:17 +0000
+++ import_dsc.py 2009-08-19 08:58:47 +0000
@@ -1570,7 +1570,7 @@
1570 finally:1570 finally:
1571 shutil.rmtree(tempdir)1571 shutil.rmtree(tempdir)
15721572
1573 def _extract_upstream_tree(self, upstream_tip, basedir):1573 def extract_upstream_tree(self, upstream_tip, basedir):
1574 # Extract that to a tempdir so we can get a working1574 # Extract that to a tempdir so we can get a working
1575 # tree for it.1575 # tree for it.
1576 # TODO: should stack rather than trying to use the repository,1576 # TODO: should stack rather than trying to use the repository,
@@ -1582,6 +1582,13 @@
1582 self.upstream_tree = dir_to.open_workingtree()1582 self.upstream_tree = dir_to.open_workingtree()
1583 self.upstream_branch = self.upstream_tree.branch1583 self.upstream_branch = self.upstream_tree.branch
15841584
1585 def _extract_upstream_tree(self, upstream_tip, basedir):
1586 # This method is now being used outside this module and hence
1587 # not really private any longer.
1588 # TODO: obsolete/remove this method and start using
1589 # extract_upstream_tree() instead.
1590 self.extract_upstream_tree(upstream_tip, basedir)
1591
1585 def _create_empty_upstream_tree(self, basedir):1592 def _create_empty_upstream_tree(self, basedir):
1586 to_location = os.path.join(basedir, "upstream")1593 to_location = os.path.join(basedir, "upstream")
1587 to_transport = get_transport(to_location)1594 to_transport = get_transport(to_location)
@@ -1616,6 +1623,10 @@
1616 raise1623 raise
16171624
1618 def _revid_of_upstream_version_from_branch(self, version):1625 def _revid_of_upstream_version_from_branch(self, version):
1626 """The private method below will go away eventually."""
1627 return self.revid_of_upstream_version_from_branch(version)
1628
1629 def revid_of_upstream_version_from_branch(self, version):
1619 assert isinstance(version, str)1630 assert isinstance(version, str)
1620 tag_name = self.upstream_tag_name(version)1631 tag_name = self.upstream_tag_name(version)
1621 if self._has_version(self.branch, tag_name):1632 if self._has_version(self.branch, tag_name):
@@ -1639,14 +1650,14 @@
1639 if previous_version is not None:1650 if previous_version is not None:
1640 if self.has_upstream_version_in_packaging_branch(1651 if self.has_upstream_version_in_packaging_branch(
1641 previous_version.upstream_version):1652 previous_version.upstream_version):
1642 upstream_tip = self._revid_of_upstream_version_from_branch(1653 upstream_tip = self.revid_of_upstream_version_from_branch(
1643 previous_version.upstream_version)1654 previous_version.upstream_version)
1644 self._extract_upstream_tree(upstream_tip, tempdir)1655 self.extract_upstream_tree(upstream_tip, tempdir)
1645 elif (upstream_branch is not None and 1656 elif (upstream_branch is not None and
1646 previous_upstream_revision is not None):1657 previous_upstream_revision is not None):
1647 upstream_tip = RevisionSpec.from_string(previous_upstream_revision).as_revision_id(upstream_branch)1658 upstream_tip = RevisionSpec.from_string(previous_upstream_revision).as_revision_id(upstream_branch)
1648 assert isinstance(upstream_tip, str)1659 assert isinstance(upstream_tip, str)
1649 self._extract_upstream_tree(upstream_tip, tempdir)1660 self.extract_upstream_tree(upstream_tip, tempdir)
1650 else:1661 else:
1651 raise BzrCommandError("Unable to find the tag for the "1662 raise BzrCommandError("Unable to find the tag for the "
1652 "previous upstream version, %s, in the branch: "1663 "previous upstream version, %s, in the branch: "
16531664
=== added file 'merge_package.py'
--- merge_package.py 1970-01-01 00:00:00 +0000
+++ merge_package.py 2009-08-19 09:19:38 +0000
@@ -0,0 +1,214 @@
1# merge_package.py -- The plugin for bzr
2# Copyright (C) 2009 Canonical Ltd.
3#
4# :Author: Muharem Hrnjadovic <muharem@ubuntu.com>
5#
6# This file is part of bzr-builddeb.
7#
8# bzr-builddeb is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13# bzr-builddeb is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with bzr-builddeb; if not, write to the Free Software
20# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21#
22
23import os
24import re
25import sys
26import tempfile
27
28from debian_bundle.changelog import Version
29
30from bzrlib import errors
31
32from bzrlib.plugins.builddeb.import_dsc import DistributionBranch
33
34
35class WrongBranchType(errors.BzrError):
36 _fmt = "The merge target is not a packaging branch."
37
38
39class InvalidChangelogFormat(errors.BzrError):
40 _fmt = "The debian/changelog is empty or not in valid format."
41
42
43class SourceUpstreamConflictsWithTargetPackaging(errors.BzrError):
44 _fmt = (
45 "The source upstream branch conflicts with "
46 "the target packaging branch")
47
48
49def _read_file(branch, path):
50 """Get content of file for given `branch` and `path.
51
52 :param branch: A Branch object containing the file of interest.
53 :param path: The path of the file to read.
54 """
55 try:
56 tree = branch.basis_tree()
57 tree.lock_read()
58 content = tree.get_file_text(tree.path2id(path))
59 tree.unlock()
60 except errors.NoSuchId:
61 raise WrongBranchType()
62
63 return content
64
65
66def _latest_version(branch):
67 """Version of the most recent source package upload in the given `branch`.
68
69 :param branch: A Branch object containing the source upload of interest.
70 """
71 upload_version = ''
72 changelog = _read_file(branch, "debian/changelog")
73
74 for line in changelog.splitlines():
75 # Look for the top-level changelog stanza, extract the
76 # upload version from it and break on success.
77 match = re.search('^.+\(([^)]+)\).*$', line)
78 if match is not None:
79 (upload_version,) = match.groups(1)
80 break
81
82 upload_version = upload_version.strip()
83 if len(upload_version) <= 0:
84 raise InvalidChangelogFormat()
85
86 return Version(upload_version)
87
88
89def _upstream_version_data(source, target):
90 """Most recent upstream versions/revision IDs of the merge source/target.
91
92 Please note: both packaing branches must have been read-locked beforehand.
93
94 :param source: The merge source branch.
95 :param target: The merge target branch.
96 """
97 results = list()
98 for branch in (source, target):
99 db = DistributionBranch(branch, branch)
100 uver = _latest_version(branch).upstream_version
101 results.append((uver, db.revid_of_upstream_version_from_branch(uver)))
102
103 return results
104
105
106def fix_ancestry_as_needed(tree, source):
107 """Manipulate the merge target's ancestry to avoid upstream conflicts.
108
109 Merging J->I given the following ancestry tree is likely to result in
110 upstream merge conflicts:
111
112 debian-upstream ,------------------H
113 A-----------B \
114 ubuntu-upstream \ \`-------G \
115 \ \ \ \
116 debian-packaging \ ,---------D--------\-----------J
117 C \ \
118 ubuntu-packaging `----E------F--------I
119
120 Here there was a new upstream release (G) that Ubuntu packaged (I), and
121 then another one that Debian packaged, skipping G, at H and J.
122
123 Now, the way to solve this is to introduce the missing link.
124
125 debian-upstream ,------------------H------.
126 A-----------B \ \
127 ubuntu-upstream \ \`-------G-----------\------K
128 \ \ \ \
129 debian-packaging \ ,---------D--------\-----------J
130 C \ \
131 ubuntu-packaging `----E------F--------I
132
133 at K, which isn't a real merge, as we just use the tree from H, but add
134 G as a parent and then we merge that in to Ubuntu.
135
136 debian-upstream ,------------------H------.
137 A-----------B \ \
138 ubuntu-upstream \ \`-------G-----------\------K
139 \ \ \ \ \
140 debian-packaging \ ,---------D--------\-----------J \
141 C \ \ \
142 ubuntu-packaging `----E------F--------I------------------L
143
144 At this point we can merge J->L to merge the Debian and Ubuntu changes.
145
146 :param tree: The `WorkingTree` of the merge target branch.
147 :param source: The merge source (packaging) branch.
148 """
149 upstreams_diverged = False
150 t_upstream_reverted = False
151 target = tree.branch
152
153 try:
154 source.lock_read()
155 target.lock_read()
156 upstream_vdata = _upstream_version_data(source, target)
157 # Did the upstream branches of the merge source and target diverge?
158 revids = [vdata[1] for vdata in upstream_vdata]
159 graph = source.repository.get_graph(target.repository)
160 upstreams_diverged = (len(graph.heads(revids)) > 1)
161 finally:
162 source.unlock()
163 target.unlock()
164
165 if not upstreams_diverged:
166 return (upstreams_diverged, t_upstream_reverted)
167
168 # "Unpack" the upstream versions and revision ids for the merge source and
169 # target branch respectively.
170 [(usource_v, usource_revid), (utarget_v, utarget_revid)] = upstream_vdata
171
172 # Instantiate a `DistributionBranch` object for the merge target
173 # (packaging) branch.
174 db = DistributionBranch(tree.branch, tree.branch)
175 tempdir = tempfile.mkdtemp(dir=os.path.join(tree.basedir, '..'))
176
177 # Extract the merge target's upstream tree into a temporary directory.
178 db.extract_upstream_tree(utarget_revid, tempdir)
179 tmp_target_upstream_tree = db.upstream_tree
180
181 # Merge upstream branch tips to obtain a shared upstream parent. This
182 # will add revision K (see graph above) to a temporary merge target
183 # upstream tree.
184 try:
185 tmp_target_upstream_tree.lock_write()
186
187 if usource_v > utarget_v:
188 # The source upstream tree is more recent and the temporary
189 # target tree needs to be reshaped to match it.
190 tmp_target_upstream_tree.revert(
191 None, source.repository.revision_tree(usource_revid))
192 t_upstream_reverted = True
193
194 tmp_target_upstream_tree.set_parent_ids(
195 (utarget_revid, usource_revid))
196
197 tmp_target_upstream_tree.commit(
198 'Consolidated upstream tree for merging into target branch')
199 finally:
200 tmp_target_upstream_tree.unlock()
201
202 # Merge shared upstream parent into the target merge branch. This creates
203 # revison L in the digram above.
204 try:
205 tree.lock_write()
206 try:
207 tree.merge_from_branch(tmp_target_upstream_tree.branch)
208 tree.commit('Merging source packaging branch in to target.')
209 except ConflictsInTree:
210 raise SourceUpstreamConflictsWithTargetPackaging()
211 finally:
212 tree.unlock()
213
214 return (upstreams_diverged, t_upstream_reverted)
0215
=== modified file 'tests/__init__.py'
--- tests/__init__.py 2009-07-04 20:45:01 +0000
+++ tests/__init__.py 2009-08-19 09:56:50 +0000
@@ -118,6 +118,7 @@
118 'test_config',118 'test_config',
119 'test_hooks',119 'test_hooks',
120 'test_import_dsc',120 'test_import_dsc',
121 'test_merge_package',
121 'test_merge_upstream',122 'test_merge_upstream',
122 'test_repack_tarball_extra',123 'test_repack_tarball_extra',
123 'test_revspec',124 'test_revspec',
124125
=== added file 'tests/test_merge_package.py'
--- tests/test_merge_package.py 1970-01-01 00:00:00 +0000
+++ tests/test_merge_package.py 2009-08-19 09:44:38 +0000
@@ -0,0 +1,497 @@
1#!/usr/bin/env python
2# -*- coding: iso-8859-15 -*-
3# test_merge_package.py -- Merge packaging branches, fix ancestry as needed.
4# Copyright (C) 2008 Canonical Ltd.
5#
6# This file is part of bzr-builddeb.
7#
8# bzr-builddeb is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13# bzr-builddeb is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with bzr-builddeb; if not, write to the Free Software
20# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
22import os
23import random
24import string
25import unittest
26
27from bzrlib.errors import ConflictsInTree
28from bzrlib.merge import WeaveMerger
29from bzrlib.tests import TestCaseWithTransport
30
31from bzrlib.plugins.builddeb import merge_package as MP
32
33_Debian_changelog = '''\
34ipsec-tools (%s) unstable; urgency=high
35
36 * debian packaging -- %s
37
38 -- Nico Golde <nion@debian.org> Tue, %02d May 2009 13:26:14 +0200
39
40'''
41
42_Ubuntu_changelog = '''\
43ipsec-tools (%s) karmic; urgency=low
44
45 * ubuntu packaging -- %s
46
47 -- Jamie Strandboge <jamie@ubuntu.com> Fri, %02d Jul 2009 13:24:17 -0500
48
49'''
50
51
52def _prepend_log(text, path):
53 content = open(path).read()
54 fh = open(path, 'wb')
55 fh.write(text+content)
56 fh.close()
57
58
59class MergePackageTests(TestCaseWithTransport):
60
61 def test_latest_upstream_versions(self):
62 """Check correctness of upstream version computation."""
63 ubup_o, debp_n = self._setup_debian_upstrem_newer()
64 # Ubuntu upstream.
65 self.assertEquals(
66 MP._latest_version(ubup_o.branch).upstream_version, '1.1.2')
67 # Debian upstream.
68 self.assertEquals(
69 MP._latest_version(debp_n.branch).upstream_version, '2.0')
70
71 ubuntup, debianp = self._setup_upstreams_not_diverged()
72 # Ubuntu upstream.
73 self.assertEquals(
74 MP._latest_version(ubuntup.branch).upstream_version, '1.4')
75 # Debian upstream.
76 self.assertEquals(
77 MP._latest_version(debianp.branch).upstream_version, '2.2')
78
79 def test_debian_upstream_newer(self):
80 """Diverging upstreams (debian newer) don't cause merge conflicts.
81
82 The debian and ubuntu upstream branches will differ with regard to
83 the content of the file 'c'.
84
85 Furthermore the respective packaging branches will have a text
86 conflict in 'debian/changelog'.
87
88 The upstream conflict will be resolved by fix_ancestry_as_needed().
89 Please note that the debian ancestry is more recent.
90 """
91 ubup_o, debp_n = self._setup_debian_upstrem_newer()
92
93 # Attempt a plain merge first.
94 conflicts = ubup_o.merge_from_branch(
95 debp_n.branch, to_revision=self.revid_debp_n_C)
96
97 # There are two conflicts in the 'c' and the 'debian/changelog' files
98 # respectively.
99 self.assertEquals(conflicts, 2)
100 conflict_paths = sorted([c.path for c in ubup_o.conflicts()])
101 self.assertEquals(conflict_paths, [u'c.moved', u'debian/changelog'])
102
103 # Undo the failed merge.
104 ubup_o.revert()
105
106 # The first conflict is resolved by calling fix_ancestry_as_needed().
107 upstreams_diverged, t_upstream_reverted = MP.fix_ancestry_as_needed(ubup_o, debp_n.branch)
108
109 # The ancestry did diverge and needed to be fixed.
110 self.assertEquals(upstreams_diverged, True)
111 # The (temporary) target upstream branch had to be reverted to the
112 # source upstream branch since the latter was more recent.
113 self.assertEquals(t_upstream_reverted, True)
114
115 # Try merging again.
116 conflicts = ubup_o.merge_from_branch(
117 debp_n.branch, to_revision=self.revid_debp_n_C)
118
119 # And, voila, only the packaging branch conflict remains.
120 self.assertEquals(conflicts, 1)
121 conflict_paths = sorted([c.path for c in ubup_o.conflicts()])
122 self.assertEquals(conflict_paths, [u'debian/changelog'])
123
124 def test_debian_upstream_older(self):
125 """Diverging upstreams (debian older) don't cause merge conflicts.
126
127 The debian and ubuntu upstream branches will differ with regard to
128 the content of the file 'c'.
129
130 Furthermore the respective packaging branches will have a text
131 conflict in 'debian/changelog'.
132
133 The upstream conflict will be resolved by fix_ancestry_as_needed().
134 Please note that the debian ancestry is older in this case.
135 """
136 ubup_n, debp_o = self._setup_debian_upstream_older()
137
138 # Attempt a plain merge first.
139 conflicts = ubup_n.merge_from_branch(
140 debp_o.branch, to_revision=self.revid_debp_o_C)
141
142 # There are two conflicts in the 'c' and the 'debian/changelog' files
143 # respectively.
144 self.assertEquals(conflicts, 2)
145 conflict_paths = sorted([c.path for c in ubup_n.conflicts()])
146 self.assertEquals(conflict_paths, [u'c.moved', u'debian/changelog'])
147
148 # Undo the failed merge.
149 ubup_n.revert()
150
151 # The first conflict is resolved by calling fix_ancestry_as_needed().
152 upstreams_diverged, t_upstream_reverted = MP.fix_ancestry_as_needed(ubup_n, debp_o.branch)
153
154 # The ancestry did diverge and needed to be fixed.
155 self.assertEquals(upstreams_diverged, True)
156 # The target upstream branch was more recent in this case and hence
157 # was not reverted to the source upstream branch.
158 self.assertEquals(t_upstream_reverted, False)
159
160 # Try merging again.
161 conflicts = ubup_n.merge_from_branch(
162 debp_o.branch, to_revision=self.revid_debp_o_C)
163
164 # And, voila, only the packaging branch conflict remains.
165 self.assertEquals(conflicts, 1)
166 conflict_paths = sorted([c.path for c in ubup_n.conflicts()])
167 self.assertEquals(conflict_paths, [u'debian/changelog'])
168
169 def test_upstreams_not_diverged(self):
170 """Non-diverging upstreams result in a normal merge.
171
172 The debian and ubuntu upstream branches will not have diverged
173 this time.
174
175 The packaging branches will have a conflict in 'debian/changelog'.
176 fix_ancestry_as_needed() will return as soon as establishing that
177 the upstreams have not diverged.
178 """
179 ubuntup, debianp = self._setup_upstreams_not_diverged()
180
181 # Attempt a plain merge first.
182 conflicts = ubuntup.merge_from_branch(
183 debianp.branch, to_revision=self.revid_debianp_C)
184
185 # There is only a conflict in the 'debian/changelog' file.
186 self.assertEquals(conflicts, 1)
187 conflict_paths = sorted([c.path for c in ubuntup.conflicts()])
188 self.assertEquals(conflict_paths, [u'debian/changelog'])
189
190 # Undo the failed merge.
191 ubuntup.revert()
192
193 # The conflict is *not* resolved by calling fix_ancestry_as_needed().
194 upstreams_diverged, t_upstream_reverted = MP.fix_ancestry_as_needed(ubuntup, debianp.branch)
195
196 # The ancestry did *not* diverge.
197 self.assertEquals(upstreams_diverged, False)
198 # The upstreams have not diverged, hence no need to fix/revert
199 # either of them.
200 self.assertEquals(t_upstream_reverted, False)
201
202 # Try merging again.
203 conflicts = ubuntup.merge_from_branch(
204 debianp.branch, to_revision=self.revid_debianp_C)
205
206 # The packaging branch conflict we saw above is still there.
207 self.assertEquals(conflicts, 1)
208 conflict_paths = sorted([c.path for c in ubuntup.conflicts()])
209 self.assertEquals(conflict_paths, [u'debian/changelog'])
210
211 def _setup_debian_upstrem_newer(self):
212 """
213 Set up the following test configuration (debian upstrem newer).
214
215 debian-upstream ,------------------H
216 A-----------B \
217 ubuntu-upstream \ \`-------G \
218 \ \ \ \
219 debian-packaging \ ,---------D--------\-----------J
220 C \
221 ubuntu-packaging `----E---------------I
222
223 where:
224 - A = 1.0
225 - B = 1.1
226 - H = 2.0
227
228 - G = 1.1.2
229
230 - C = 1.0-1
231 - D = 1.1-1
232 - J = 2.0-1
233
234 - E = 1.0-1ubuntu1
235 - I = 1.1.2-0ubuntu1
236
237 Please note that the debian and ubuntu branches will have a conflict
238 with respect to the file 'c'.
239 """
240 # Set up the debian upstream branch.
241 name = 'debu-n'
242 vdata = [
243 ('upstream-1.0', ('a',), None, None),
244 ('upstream-1.1', ('b',), None, None),
245 ('upstream-2.0', ('c',), None, None),
246 ]
247 debu_n = self._setup_branch(name, vdata)
248
249 # Set up the debian packaging branch.
250 name = 'debp-n'
251 debp_n = self.make_branch_and_tree(name)
252 debp_n.pull(debu_n.branch, stop_revision=self.revid_debu_n_A)
253
254 vdata = [
255 ('1.0-1', ('debian/', 'debian/changelog'), None, None),
256 ('1.1-1', ('o',), debu_n, self.revid_debu_n_B),
257 ('2.0-1', ('p',), debu_n, self.revid_debu_n_C),
258 ]
259 self._setup_branch(name, vdata, debp_n, 'd')
260
261 # Set up the ubuntu upstream branch.
262 name = 'ubuu-o'
263 ubuu_o = debu_n.bzrdir.sprout(
264 name, revision_id=self.revid_debu_n_B).open_workingtree()
265
266 vdata = [
267 ('upstream-1.1.2', ('c',), None, None),
268 ]
269 self._setup_branch(name, vdata, ubuu_o)
270
271 # Set up the ubuntu packaging branch.
272 name = 'ubup-o'
273 ubup_o = debu_n.bzrdir.sprout(
274 name, revision_id=self.revid_debu_n_A).open_workingtree()
275
276 vdata = [
277 ('1.0-1ubuntu1', (), debp_n, self.revid_debp_n_A),
278 ('1.1.2-0ubuntu1', (), ubuu_o, self.revid_ubuu_o_A),
279 ]
280 self._setup_branch(name, vdata, ubup_o, 'u')
281
282 # Return the ubuntu and the debian packaging branches.
283 return (ubup_o, debp_n)
284
285 def _setup_debian_upstream_older(self):
286 """
287 Set up the following test configuration (debian upstrem older).
288
289 debian-upstream ,----H-------------.
290 A-----------B \
291 ubuntu-upstream \ \`-----------G \
292 \ \ \ \
293 debian-packaging \ ,---------D------------\-------J
294 C \
295 ubuntu-packaging `----E-------------------I
296
297 where:
298 - A = 1.0
299 - B = 1.1
300 - H = 1.1.3
301
302 - G = 2.1
303
304 - C = 1.0-1
305 - D = 1.1-1
306 - J = 1.1.3-1
307
308 - E = 1.0-1ubuntu1
309 - I = 2.1-0ubuntu1
310
311 Please note that the debian and ubuntu branches will have a conflict
312 with respect to the file 'c'.
313 """
314 # Set up the debian upstream branch.
315 name = 'debu-o'
316 vdata = [
317 ('upstream-1.0', ('a',), None, None),
318 ('upstream-1.1', ('b',), None, None),
319 ('upstream-1.1.3', ('c',), None, None),
320 ]
321 debu_o = self._setup_branch(name, vdata)
322
323 # Set up the debian packaging branch.
324 name = 'debp-o'
325 debp_o = self.make_branch_and_tree(name)
326 debp_o.pull(debu_o.branch, stop_revision=self.revid_debu_o_A)
327
328 vdata = [
329 ('1.0-1', ('debian/', 'debian/changelog'), None, None),
330 ('1.1-1', ('o',), debu_o, self.revid_debu_o_B),
331 ('1.1.3-1', ('p',), debu_o, self.revid_debu_o_C),
332 ]
333 self._setup_branch(name, vdata, debp_o, 'd')
334
335 # Set up the ubuntu upstream branch.
336 name = 'ubuu-n'
337 ubuu_n = debu_o.bzrdir.sprout(
338 name, revision_id=self.revid_debu_o_B).open_workingtree()
339
340 vdata = [
341 ('upstream-2.1', ('c',), None, None),
342 ]
343 self._setup_branch(name, vdata, ubuu_n)
344
345 # Set up the ubuntu packaging branch.
346 name = 'ubup-n'
347 ubup_n = debu_o.bzrdir.sprout(
348 name, revision_id=self.revid_debu_o_A).open_workingtree()
349
350 vdata = [
351 ('1.0-1ubuntu1', (), debp_o, self.revid_debp_o_A),
352 ('2.1-0ubuntu1', (), ubuu_n, self.revid_ubuu_n_A),
353 ]
354 self._setup_branch(name, vdata, ubup_n, 'u')
355
356 # Return the ubuntu and the debian packaging branches.
357 return (ubup_n, debp_o)
358
359 def _setup_upstreams_not_diverged(self):
360 """
361 Set up a test configuration where the usptreams have not diverged.
362
363 debian-upstream .-----G
364 A-----------B-----H \
365 ubuntu-upstream \ \ \ \
366 \ \ \ \
367 debian-packaging \ ,---------D-----\-------J
368 C \
369 ubuntu-packaging `----E------------I
370
371 where:
372 - A = 1.0
373 - B = 1.1
374 - H = 1.4
375
376 - G = 2.2
377
378 - C = 1.0-1
379 - D = 1.1-1
380 - J = 2.2-1
381
382 - E = 1.0-1ubuntu1
383 - I = 1.4-0ubuntu1
384
385 Please note that there's only one shared upstream branch in this case.
386 """
387 # Set up the upstream branch.
388 name = 'upstream'
389 vdata = [
390 ('upstream-1.0', ('a',), None, None),
391 ('upstream-1.1', ('b',), None, None),
392 ('upstream-1.4', ('c',), None, None),
393 ]
394 upstream = self._setup_branch(name, vdata)
395
396 # Set up the debian upstream branch.
397 name = 'dupstream'
398 dupstream = upstream.bzrdir.sprout(name).open_workingtree()
399 vdata = [
400 ('upstream-2.2', (), None, None),
401 ]
402 dupstream = self._setup_branch(name, vdata, dupstream)
403
404 # Set up the debian packaging branch.
405 name = 'debianp'
406 debianp = self.make_branch_and_tree(name)
407 debianp.pull(dupstream.branch, stop_revision=self.revid_upstream_A)
408
409 vdata = [
410 ('1.0-1', ('debian/', 'debian/changelog'), None, None),
411 ('1.1-1', ('o',), dupstream, self.revid_upstream_B),
412 ('2.2-1', ('p',), dupstream, self.revid_dupstream_A),
413 ]
414 self._setup_branch(name, vdata, debianp, 'd')
415
416 # Set up the ubuntu packaging branch.
417 name = 'ubuntup'
418 ubuntup = upstream.bzrdir.sprout(
419 name, revision_id=self.revid_upstream_A).open_workingtree()
420
421 vdata = [
422 ('1.0-1ubuntu1', (), debianp, self.revid_debianp_A),
423 ('1.4-0ubuntu1', (), upstream, self.revid_upstream_C),
424 ]
425 self._setup_branch(name, vdata, ubuntup, 'u')
426
427 # Return the ubuntu and the debian packaging branches.
428 return (ubuntup, debianp)
429
430 def _setup_branch(self, name, vdata, tree=None, log_format=None):
431 vids = list(string.ascii_uppercase)
432 days = range(len(string.ascii_uppercase))
433
434 if tree is None:
435 tree = self.make_branch_and_tree(name)
436
437 def revid_name(vid):
438 return 'revid_%s_%s' % (name.replace('-', '_'), vid)
439
440 def add_paths(paths):
441 qpaths = ['%s/%s' % (name, path) for path in paths]
442 self.build_tree(qpaths)
443 tree.add(paths)
444
445 def changelog(vdata, vid):
446 result = ''
447 day = days.pop(0)
448 if isinstance(vdata, tuple):
449 uver, dver = vdata[:2]
450 ucle = _Ubuntu_changelog % (uver, vid, day)
451 dcle = _Debian_changelog % (dver, vid, day)
452 result = ucle + dcle
453 else:
454 if log_format == 'u':
455 result = _Ubuntu_changelog % (vdata, vid, day)
456 elif log_format == 'd':
457 result = _Debian_changelog % (vdata, vid, day)
458
459 return result
460
461 def commit(msg, version):
462 vid = vids.pop(0)
463 if log_format is not None:
464 cle = changelog(version, vid)
465 p = '%s/work/%s/debian/changelog' % (self.test_base_dir, name)
466 _prepend_log(cle, p)
467 revid = tree.commit('%s: %s' % (vid, msg), rev_id='%s-%s' % (name, vid))
468 setattr(self, revid_name(vid), revid)
469 tree.branch.tags.set_tag(version, revid)
470
471 def tree_nick(tree):
472 return str(tree)[1:-1].split('/')[-1]
473
474 for version, paths, utree, urevid in vdata:
475 msg = ''
476 if utree is not None:
477 tree.merge_from_branch(
478 utree.branch, to_revision=urevid, merge_type=WeaveMerger)
479 utree.branch.tags.merge_to(tree.branch.tags)
480 if urevid is not None:
481 msg += 'Merged tree %s|%s. ' % (tree_nick(utree), urevid)
482 else:
483 msg += 'Merged tree %s. ' % utree
484 if paths is not None:
485 add_paths(paths)
486 msg += 'Added paths: %s. ' % str(paths)
487
488 commit(msg, version)
489
490 return tree
491
492
493if __name__ == '__main__':
494 # unittest.main()
495 suite = unittest.TestLoader().loadTestsFromTestCase(MergePackageTests)
496 unittest.TextTestRunner(verbosity=2).run(suite)
497
0498
=== modified file 'upstream.py'
--- upstream.py 2009-07-26 18:21:49 +0000
+++ upstream.py 2009-08-06 08:41:23 +0000
@@ -79,7 +79,7 @@
79 db = DistributionBranch(self.branch, None, tree=self.tree)79 db = DistributionBranch(self.branch, None, tree=self.tree)
80 if not db.has_upstream_version_in_packaging_branch(version):80 if not db.has_upstream_version_in_packaging_branch(version):
81 raise PackageVersionNotPresent(package, version, self)81 raise PackageVersionNotPresent(package, version, self)
82 revid = db._revid_of_upstream_version_from_branch(version)82 revid = db.revid_of_upstream_version_from_branch(version)
83 if not db.has_pristine_tar_delta(revid):83 if not db.has_pristine_tar_delta(revid):
84 raise PackageVersionNotPresent(package, version, self)84 raise PackageVersionNotPresent(package, version, self)
85 info("Using pristine-tar to reconstruct the needed tarball.")85 info("Using pristine-tar to reconstruct the needed tarball.")

Subscribers

People subscribed via source and target branches