Merge lp:~jelmer/ubuntu/maverick/bzr/2.2.5 into lp:ubuntu/maverick-updates/bzr

Proposed by Jelmer Vernooij
Status: Merged
Merge reported by: Colin Watson
Merged at revision: not available
Proposed branch: lp:~jelmer/ubuntu/maverick/bzr/2.2.5
Merge into: lp:ubuntu/maverick-updates/bzr
Diff against target: 1694 lines (+1330/-32)
24 files modified
Makefile (+1/-1)
NEWS (+107/-1)
bzr (+2/-2)
bzrlib/__init__.py (+3/-3)
bzrlib/groupcompress.py (+2/-2)
bzrlib/help_topics/en/configuration.txt (+3/-1)
bzrlib/knit.py (+1/-1)
bzrlib/merge.py (+8/-2)
bzrlib/plugins/launchpad/__init__.py (+64/-4)
bzrlib/plugins/launchpad/lp_api_lite.py (+285/-0)
bzrlib/plugins/launchpad/test_lp_api_lite.py (+557/-0)
bzrlib/repofmt/groupcompress_repo.py (+13/-4)
bzrlib/tests/per_repository_reference/__init__.py (+1/-0)
bzrlib/tests/per_repository_reference/test_graph.py (+45/-0)
bzrlib/tests/test_config.py (+27/-1)
bzrlib/tests/test_conflicts.py (+64/-0)
bzrlib/tests/test_repository.py (+101/-1)
bzrlib/transport/http/_pycurl.py (+5/-5)
bzrlib/util/configobj/configobj.py (+4/-2)
bzrlib/versionedfile.py (+13/-0)
debian/changelog (+10/-0)
debian/watch (+1/-1)
doc/en/whats-new/whats-new-in-2.2.txt (+4/-1)
setup.py (+9/-0)
To merge this branch: bzr merge lp:~jelmer/ubuntu/maverick/bzr/2.2.5
Reviewer Review Type Date Requested Status
Colin Watson Approve
Ubuntu Sponsors Pending
Review via email: mp+77153@code.launchpad.net

This proposal supersedes a proposal from 2011-09-27.

Description of the change

bzr microrelease 2.2.5

This fixes a couple of bugs that might be affecting users:

 * stacking is not fully transitive (715000)
 * merge failing with NoFinalPath (#805809)

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Makefile'
--- Makefile 2010-08-07 00:54:52 +0000
+++ Makefile 2011-09-27 11:59:22 +0000
@@ -40,7 +40,7 @@
4040
41check-nodocs: extensions41check-nodocs: extensions
42 # Generate a stream for PQM to watch.42 # Generate a stream for PQM to watch.
43 $(PYTHON) -Werror -O ./bzr selftest --subunit $(tests) | tee selftest.log43 $(PYTHON) -Werror -Wignore::ImportWarning -O ./bzr selftest --subunit $(tests) | tee selftest.log
44 # Check that there were no errors reported.44 # Check that there were no errors reported.
45 subunit-stats < selftest.log45 subunit-stats < selftest.log
4646
4747
=== modified file 'NEWS'
--- NEWS 2011-04-07 15:30:17 +0000
+++ NEWS 2011-09-27 11:59:22 +0000
@@ -5,6 +5,98 @@
5.. contents:: List of Releases5.. contents:: List of Releases
6 :depth: 16 :depth: 1
77
8This is a bugfix release. One regression introduced in 2.2b1 has been fixed
9for some rare conflict resolutions. Also a warning is now emmitted when
10branching an out-of-date ubuntu packaging branch. Upgrading is recommended
11for all users on earlier 2.2 releases.
12
13bzr 2.2.5
14#########
15
16:2.2.5: 2011-09-01
17
18Compatibility Breaks
19********************
20
21None.
22
23New Features
24************
25
26None.
27
28Bug Fixes
29*********
30
31* Correctly handle ``bzr log`` and `get_known_graph_ancestry` on a
32 doubly-stacked branch.
33 (James Westby, Martin Pool, #715000)
34
35* Don't crash while merging and encountering obscure path conflicts
36 involving different root-ids. (Vincent Ladeuil, #805809)
37
38Internals
39*********
40
41* Fixed bug in the bundled copy of ConfigObj with quoting of triple quotes
42 in the value string. Fix suggested by ConfigObj's author Michael Foord.
43 (Alexander Belchenko, #710410)
44
45bzr 2.1.5
46#########
47
48:2.1.5: NOT RELEASED YET
49
50Compatibility Breaks
51********************
52
53New Features
54************
55
56Bug Fixes
57*********
58
59* Accessing a packaging branch on Launchpad (eg, ``lp:ubuntu/bzr``) now
60 checks to see if the most recent published source package version for
61 that project is present in the branch tags. This should help developers
62 trust whether the packaging branch is up-to-date and can be used for new
63 changes. The level of verbosity is controlled by the config item
64 ``launchpad.packaging_verbosity``. It can be set to one of
65
66 off
67 disable all checks
68
69
70 minimal
71 only display if the branch is out-of-date
72
73 short
74 also display single-line up-to-date and missing,
75
76
77 all
78 (default) display multi-line content for all states
79
80
81 (John Arbash Meinel, #609187, #812928)
82
83
84Improvements
85************
86
87Documentation
88*************
89
90API Changes
91***********
92
93Internals
94*********
95
96Testing
97*******
98
99
8bzr 2.2.4100bzr 2.2.4
9#########101#########
10102
@@ -251,7 +343,12 @@
251bzr 2.1.4343bzr 2.1.4
252#########344#########
253345
254:2.1.4: NOT RELEASED YET346:2.1.4: 2011-05-16
347
348The fourth release in our 2.1 series addresses some user-inconvenience bugs.
349None are critical, but upgrading is recommended for all users on earlier 2.1
350releases.
351
255352
256Compatibility Breaks353Compatibility Breaks
257********************354********************
@@ -265,6 +362,8 @@
265New Features362New Features
266************363************
267364
365None.
366
268Bug Fixes367Bug Fixes
269*********368*********
270369
@@ -403,6 +502,13 @@
403* Avoid UnicodeDecodeError in ``bzr add`` with multiple files under a non-ascii502* Avoid UnicodeDecodeError in ``bzr add`` with multiple files under a non-ascii
404 path on windows from symlink support addition. (Martin [gz], #686611)503 path on windows from symlink support addition. (Martin [gz], #686611)
405504
505* Avoid spurious ValueErrors when autopacking a subset of the repository,
506 and seeing a revision without its related inventory
507 (John Arbash Meinel, #437003)
508
509* Fix activity reporting for pycurl when using https with some
510 implementations of curl. (Vincent Ladeuil, #614713)
511
406Improvements512Improvements
407************513************
408514
409515
=== modified file 'bzr'
--- bzr 2011-04-07 15:30:17 +0000
+++ bzr 2011-09-27 11:59:22 +0000
@@ -1,6 +1,6 @@
1#! /usr/bin/env python1#! /usr/bin/env python
22
3# Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Canonical Ltd3# Copyright (C) 2005-2011 Canonical Ltd
4#4#
5# This program is free software; you can redistribute it and/or modify5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by6# it under the terms of the GNU General Public License as published by
@@ -23,7 +23,7 @@
23import warnings23import warnings
2424
25# update this on each release25# update this on each release
26_script_version = (2, 2, 4)26_script_version = (2, 2, 5)
2727
28try:28try:
29 version_info = sys.version_info29 version_info = sys.version_info
3030
=== modified file 'bzrlib/__init__.py'
--- bzrlib/__init__.py 2011-04-07 15:30:17 +0000
+++ bzrlib/__init__.py 2011-09-27 11:59:22 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2005-2010 Canonical Ltd1# Copyright (C) 2005-2011 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -43,7 +43,7 @@
43IGNORE_FILENAME = ".bzrignore"43IGNORE_FILENAME = ".bzrignore"
4444
4545
46__copyright__ = "Copyright 2005-2010 Canonical Ltd."46__copyright__ = "Copyright 2005-2011 Canonical Ltd."
4747
48# same format as sys.version_info: "A tuple containing the five components of48# same format as sys.version_info: "A tuple containing the five components of
49# the version number: major, minor, micro, releaselevel, and serial. All49# the version number: major, minor, micro, releaselevel, and serial. All
@@ -52,7 +52,7 @@
52# Python version 2.0 is (2, 0, 0, 'final', 0)." Additionally we use a52# Python version 2.0 is (2, 0, 0, 'final', 0)." Additionally we use a
53# releaselevel of 'dev' for unreleased under-development code.53# releaselevel of 'dev' for unreleased under-development code.
5454
55version_info = (2, 2, 4, 'final', 0)55version_info = (2, 2, 5, 'final', 0)
5656
57# API compatibility version57# API compatibility version
58api_minimum_version = (2, 2, 0)58api_minimum_version = (2, 2, 0)
5959
=== modified file 'bzrlib/groupcompress.py'
--- bzrlib/groupcompress.py 2010-05-27 21:58:49 +0000
+++ bzrlib/groupcompress.py 2011-09-27 11:59:22 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2008, 2009, 2010 Canonical Ltd1# Copyright (C) 2008-2011 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -1293,7 +1293,7 @@
1293 # KnitVersionedFiles.get_known_graph_ancestry, but they don't share1293 # KnitVersionedFiles.get_known_graph_ancestry, but they don't share
1294 # ancestry.1294 # ancestry.
1295 parent_map, missing_keys = self._index.find_ancestry(keys)1295 parent_map, missing_keys = self._index.find_ancestry(keys)
1296 for fallback in self._fallback_vfs:1296 for fallback in self._transitive_fallbacks():
1297 if not missing_keys:1297 if not missing_keys:
1298 break1298 break
1299 (f_parent_map, f_missing_keys) = fallback._index.find_ancestry(1299 (f_parent_map, f_missing_keys) = fallback._index.find_ancestry(
13001300
=== modified file 'bzrlib/help_topics/en/configuration.txt'
--- bzrlib/help_topics/en/configuration.txt 2010-08-07 00:54:52 +0000
+++ bzrlib/help_topics/en/configuration.txt 2011-09-27 11:59:22 +0000
@@ -19,7 +19,9 @@
19BZR_PROGRESS_BAR19BZR_PROGRESS_BAR
20~~~~~~~~~~~~~~~~20~~~~~~~~~~~~~~~~
2121
22Override the progress display. Possible values are "none", "dots", "tty"22Override the progress display. Possible values are "none" or "text". If
23the value is "none" then no progress bar is displayed. The value "text" draws
24the ordinary command line progress bar.
2325
24BZR_SIGQUIT_PDB26BZR_SIGQUIT_PDB
25~~~~~~~~~~~~~~~27~~~~~~~~~~~~~~~
2628
=== modified file 'bzrlib/knit.py'
--- bzrlib/knit.py 2010-08-07 00:54:52 +0000
+++ bzrlib/knit.py 2011-09-27 11:59:22 +0000
@@ -1195,7 +1195,7 @@
1195 def get_known_graph_ancestry(self, keys):1195 def get_known_graph_ancestry(self, keys):
1196 """Get a KnownGraph instance with the ancestry of keys."""1196 """Get a KnownGraph instance with the ancestry of keys."""
1197 parent_map, missing_keys = self._index.find_ancestry(keys)1197 parent_map, missing_keys = self._index.find_ancestry(keys)
1198 for fallback in self._fallback_vfs:1198 for fallback in self._transitive_fallbacks():
1199 if not missing_keys:1199 if not missing_keys:
1200 break1200 break
1201 (f_parent_map, f_missing_keys) = fallback._index.find_ancestry(1201 (f_parent_map, f_missing_keys) = fallback._index.find_ancestry(
12021202
=== modified file 'bzrlib/merge.py'
--- bzrlib/merge.py 2010-08-07 00:54:52 +0000
+++ bzrlib/merge.py 2011-09-27 11:59:22 +0000
@@ -1624,8 +1624,14 @@
1624 if other_parent is None or other_name is None:1624 if other_parent is None or other_name is None:
1625 other_path = '<deleted>'1625 other_path = '<deleted>'
1626 else:1626 else:
1627 parent_path = fp.get_path(1627 if other_parent == self.other_tree.get_root_id():
1628 self.tt.trans_id_file_id(other_parent))1628 # The tree transform doesn't know about the other root,
1629 # so we special case here to avoid a NoFinalPath
1630 # exception
1631 parent_path = ''
1632 else:
1633 parent_path = fp.get_path(
1634 self.tt.trans_id_file_id(other_parent))
1629 other_path = osutils.pathjoin(parent_path, other_name)1635 other_path = osutils.pathjoin(parent_path, other_name)
1630 c = _mod_conflicts.Conflict.factory(1636 c = _mod_conflicts.Conflict.factory(
1631 'path conflict', path=this_path,1637 'path conflict', path=this_path,
16321638
=== modified file 'bzrlib/plugins/launchpad/__init__.py'
--- bzrlib/plugins/launchpad/__init__.py 2010-08-07 00:54:52 +0000
+++ bzrlib/plugins/launchpad/__init__.py 2011-09-27 11:59:22 +0000
@@ -21,13 +21,9 @@
2121
22# see http://bazaar-vcs.org/Specs/BranchRegistrationTool22# see http://bazaar-vcs.org/Specs/BranchRegistrationTool
2323
24# Since we are a built-in plugin we share the bzrlib version
25from bzrlib import version_info
26
27from bzrlib.lazy_import import lazy_import24from bzrlib.lazy_import import lazy_import
28lazy_import(globals(), """25lazy_import(globals(), """
29from bzrlib import (26from bzrlib import (
30 branch as _mod_branch,
31 trace,27 trace,
32 )28 )
33""")29""")
@@ -37,6 +33,12 @@
37 Command,33 Command,
38 register_command,34 register_command,
39)35)
36from bzrlib import (
37 branch as _mod_branch,
38 lazy_regex,
39 # Since we are a built-in plugin we share the bzrlib version
40 version_info,
41 )
40from bzrlib.directory_service import directories42from bzrlib.directory_service import directories
41from bzrlib.errors import (43from bzrlib.errors import (
42 BzrCommandError,44 BzrCommandError,
@@ -354,12 +356,70 @@
354 'Launchpad-based directory service',)356 'Launchpad-based directory service',)
355_register_directory()357_register_directory()
356358
359# This is kept in __init__ so that we don't load lp_api_lite unless the branch
360# actually matches. That way we can avoid importing extra dependencies like
361# json.
362_package_branch = lazy_regex.lazy_compile(
363 r'bazaar.launchpad.net.*?/'
364 r'(?P<user>~[^/]+/)?(?P<archive>ubuntu|debian)/(?P<series>[^/]+/)?'
365 r'(?P<project>[^/]+)(?P<branch>/[^/]+)?'
366 )
367
368def _get_package_branch_info(url):
369 """Determine the packaging information for this URL.
370
371 :return: If this isn't a packaging branch, return None. If it is, return
372 (archive, series, project)
373 """
374 if url is None:
375 return None
376 m = _package_branch.search(url)
377 if m is None:
378 return None
379 archive, series, project, user = m.group('archive', 'series',
380 'project', 'user')
381 if series is not None:
382 # series is optional, so the regex includes the extra '/', we don't
383 # want to send that on (it causes Internal Server Errors.)
384 series = series.strip('/')
385 if user is not None:
386 user = user.strip('~/')
387 if user != 'ubuntu-branches':
388 return None
389 return archive, series, project
390
391
392def _check_is_up_to_date(the_branch):
393 info = _get_package_branch_info(the_branch.base)
394 if info is None:
395 return
396 c = the_branch.get_config()
397 verbosity = c.get_user_option('launchpad.packaging_verbosity')
398 if verbosity is not None:
399 verbosity = verbosity.lower()
400 if verbosity == 'off':
401 trace.mutter('not checking %s because verbosity is turned off'
402 % (the_branch.base,))
403 return
404 archive, series, project = info
405 from bzrlib.plugins.launchpad import lp_api_lite
406 latest_pub = lp_api_lite.LatestPublication(archive, series, project)
407 lp_api_lite.report_freshness(the_branch, verbosity, latest_pub)
408
409
410def _register_hooks():
411 _mod_branch.Branch.hooks.install_named_hook('open',
412 _check_is_up_to_date, 'package-branch-up-to-date')
413
414
415_register_hooks()
357416
358def load_tests(basic_tests, module, loader):417def load_tests(basic_tests, module, loader):
359 testmod_names = [418 testmod_names = [
360 'test_account',419 'test_account',
361 'test_register',420 'test_register',
362 'test_lp_api',421 'test_lp_api',
422 'test_lp_api_lite',
363 'test_lp_directory',423 'test_lp_directory',
364 'test_lp_login',424 'test_lp_login',
365 'test_lp_open',425 'test_lp_open',
366426
=== added file 'bzrlib/plugins/launchpad/lp_api_lite.py'
--- bzrlib/plugins/launchpad/lp_api_lite.py 1970-01-01 00:00:00 +0000
+++ bzrlib/plugins/launchpad/lp_api_lite.py 2011-09-27 11:59:22 +0000
@@ -0,0 +1,285 @@
1# Copyright (C) 2011 Canonical Ltd
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17"""Tools for dealing with the Launchpad API without using launchpadlib.
18
19The api itself is a RESTful interface, so we can make HTTP queries directly.
20loading launchpadlib itself has a fairly high overhead (just calling
21Launchpad.login_anonymously() takes a 500ms once the WADL is cached, and 5+s to
22get the WADL.
23"""
24
25try:
26 # Use simplejson if available, much faster, and can be easily installed in
27 # older versions of python
28 import simplejson as json
29except ImportError:
30 # Is present since python 2.6
31 try:
32 import json
33 except ImportError:
34 json = None
35
36import time
37import urllib
38import urllib2
39
40from bzrlib import (
41 revision,
42 trace,
43 )
44
45
46class LatestPublication(object):
47 """Encapsulate how to find the latest publication for a given project."""
48
49 LP_API_ROOT = 'https://api.launchpad.net/1.0'
50
51 def __init__(self, archive, series, project):
52 self._archive = archive
53 self._project = project
54 self._setup_series_and_pocket(series)
55
56 def _setup_series_and_pocket(self, series):
57 """Parse the 'series' info into a series and a pocket.
58
59 eg::
60 _setup_series_and_pocket('natty-proposed')
61 => _series == 'natty'
62 _pocket == 'Proposed'
63 """
64 self._series = series
65 self._pocket = None
66 if self._series is not None and '-' in self._series:
67 self._series, self._pocket = self._series.split('-', 1)
68 self._pocket = self._pocket.title()
69 else:
70 self._pocket = 'Release'
71
72 def _archive_URL(self):
73 """Return the Launchpad 'Archive' URL that we will query.
74 This is everything in the URL except the query parameters.
75 """
76 return '%s/%s/+archive/primary' % (self.LP_API_ROOT, self._archive)
77
78 def _publication_status(self):
79 """Handle the 'status' field.
80 It seems that Launchpad tracks all 'debian' packages as 'Pending', while
81 for 'ubuntu' we care about the 'Published' packages.
82 """
83 if self._archive == 'debian':
84 # Launchpad only tracks debian packages as "Pending", it doesn't mark
85 # them Published
86 return 'Pending'
87 return 'Published'
88
89 def _query_params(self):
90 """Get the parameters defining our query.
91 This defines the actions we are making against the archive.
92 :return: A dict of query parameters.
93 """
94 params = {'ws.op': 'getPublishedSources',
95 'exact_match': 'true',
96 # If we need to use "" shouldn't we quote the project somehow?
97 'source_name': '"%s"' % (self._project,),
98 'status': self._publication_status(),
99 # We only need the latest one, the results seem to be properly
100 # most-recent-debian-version sorted
101 'ws.size': '1',
102 }
103 if self._series is not None:
104 params['distro_series'] = '/%s/%s' % (self._archive, self._series)
105 if self._pocket is not None:
106 params['pocket'] = self._pocket
107 return params
108
109 def _query_URL(self):
110 """Create the full URL that we need to query, including parameters."""
111 params = self._query_params()
112 # We sort to give deterministic results for testing
113 encoded = urllib.urlencode(sorted(params.items()))
114 return '%s?%s' % (self._archive_URL(), encoded)
115
116 def _get_lp_info(self):
117 """Place an actual HTTP query against the Launchpad service."""
118 if json is None:
119 return None
120 query_URL = self._query_URL()
121 try:
122 req = urllib2.Request(query_URL)
123 response = urllib2.urlopen(req)
124 json_info = response.read()
125 # TODO: We haven't tested the HTTPError
126 except (urllib2.URLError, urllib2.HTTPError), e:
127 trace.mutter('failed to place query to %r' % (query_URL,))
128 trace.log_exception_quietly()
129 return None
130 return json_info
131
132 def _parse_json_info(self, json_info):
133 """Parse the json response from Launchpad into objects."""
134 if json is None:
135 return None
136 try:
137 return json.loads(json_info)
138 except Exception:
139 trace.mutter('Failed to parse json info: %r' % (json_info,))
140 trace.log_exception_quietly()
141 return None
142
143 def get_latest_version(self):
144 """Get the latest published version for the given package."""
145 json_info = self._get_lp_info()
146 if json_info is None:
147 return None
148 info = self._parse_json_info(json_info)
149 if info is None:
150 return None
151 try:
152 entries = info['entries']
153 if len(entries) == 0:
154 return None
155 return entries[0]['source_package_version']
156 except KeyError:
157 trace.log_exception_quietly()
158 return None
159
160 def place(self):
161 """Text-form for what location this represents.
162
163 Example::
164 ubuntu, natty => Ubuntu Natty
165 ubuntu, natty-proposed => Ubuntu Natty Proposed
166 :return: A string representing the location we are checking.
167 """
168 place = self._archive
169 if self._series is not None:
170 place = '%s %s' % (place, self._series)
171 if self._pocket is not None and self._pocket != 'Release':
172 place = '%s %s' % (place, self._pocket)
173 return place.title()
174
175
176def get_latest_publication(archive, series, project):
177 """Get the most recent publication for a given project.
178
179 :param archive: Either 'ubuntu' or 'debian'
180 :param series: Something like 'natty', 'sid', etc. Can be set as None. Can
181 also include a pocket such as 'natty-proposed'.
182 :param project: Something like 'bzr'
183 :return: A version string indicating the most-recent version published in
184 Launchpad. Might return None if there is an error.
185 """
186 lp = LatestPublication(archive, series, project)
187 return lp.get_latest_version()
188
189
190def get_most_recent_tag(tag_dict, the_branch):
191 """Get the most recent revision that has been tagged."""
192 # Note: this assumes that a given rev won't get tagged multiple times. But
193 # it should be valid for the package importer branches that we care
194 # about
195 reverse_dict = dict((rev, tag) for tag, rev in tag_dict.iteritems())
196 the_branch.lock_read()
197 try:
198 history = the_branch.repository.iter_reverse_revision_history(
199 the_branch.last_revision())
200 for rev_id in history:
201 if rev_id in reverse_dict:
202 return reverse_dict[rev_id]
203 finally:
204 the_branch.unlock()
205
206
207def _get_newest_versions(the_branch, latest_pub):
208 """Get information about how 'fresh' this packaging branch is.
209
210 :param the_branch: The Branch to check
211 :param latest_pub: The LatestPublication used to check most recent
212 published version.
213 :return: (latest_ver, branch_latest_ver)
214 """
215 t = time.time()
216 latest_ver = latest_pub.get_latest_version()
217 t_latest_ver = time.time() - t
218 trace.mutter('LatestPublication.get_latest_version took: %.3fs'
219 % (t_latest_ver,))
220 if latest_ver is None:
221 return None, None
222 t = time.time()
223 tags = the_branch.tags.get_tag_dict()
224 t_tag_dict = time.time() - t
225 trace.mutter('LatestPublication.get_tag_dict took: %.3fs' % (t_tag_dict,))
226 if latest_ver in tags:
227 # branch might have a newer tag, but we don't really care
228 return latest_ver, latest_ver
229 else:
230 best_tag = get_most_recent_tag(tags, the_branch)
231 return latest_ver, best_tag
232
233
234def _report_freshness(latest_ver, branch_latest_ver, place, verbosity,
235 report_func):
236 """Report if the branch is up-to-date."""
237 if latest_ver is None:
238 if verbosity == 'all':
239 report_func('Most recent %s version: MISSING' % (place,))
240 elif verbosity == 'short':
241 report_func('%s is MISSING a version' % (place,))
242 return
243 elif latest_ver == branch_latest_ver:
244 if verbosity == 'minimal':
245 return
246 elif verbosity == 'short':
247 report_func('%s is CURRENT in %s' % (latest_ver, place))
248 else:
249 report_func('Most recent %s version: %s\n'
250 'Packaging branch status: CURRENT'
251 % (place, latest_ver))
252 else:
253 if verbosity in ('minimal', 'short'):
254 if branch_latest_ver is None:
255 branch_latest_ver = 'Branch'
256 report_func('%s is OUT-OF-DATE, %s has %s'
257 % (branch_latest_ver, place, latest_ver))
258 else:
259 report_func('Most recent %s version: %s\n'
260 'Packaging branch version: %s\n'
261 'Packaging branch status: OUT-OF-DATE'
262 % (place, latest_ver, branch_latest_ver))
263
264
265def report_freshness(the_branch, verbosity, latest_pub):
266 """Report to the user how up-to-date the packaging branch is.
267
268 :param the_branch: A Branch object
269 :param verbosity: Can be one of:
270 off: Do not print anything, and skip all checks.
271 all: Print all information that we have in a verbose manner, this
272 includes misses, etc.
273 short: Print information, but only one-line summaries
274 minimal: Only print a one-line summary when the package branch is
275 out-of-date
276 :param latest_pub: A LatestPublication instance
277 """
278 if verbosity == 'off':
279 return
280 if verbosity is None:
281 verbosity = 'all'
282 latest_ver, branch_ver = _get_newest_versions(the_branch, latest_pub)
283 place = latest_pub.place()
284 _report_freshness(latest_ver, branch_ver, place, verbosity,
285 trace.note)
0286
=== added file 'bzrlib/plugins/launchpad/test_lp_api_lite.py'
--- bzrlib/plugins/launchpad/test_lp_api_lite.py 1970-01-01 00:00:00 +0000
+++ bzrlib/plugins/launchpad/test_lp_api_lite.py 2011-09-27 11:59:22 +0000
@@ -0,0 +1,557 @@
1# Copyright (C) 2011 Canonical Ltd
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17"""Tools for dealing with the Launchpad API without using launchpadlib.
18"""
19
20import doctest
21import socket
22
23from bzrlib import tests
24from bzrlib.plugins import launchpad
25from bzrlib.plugins.launchpad import lp_api_lite
26
27from testtools.matchers import DocTestMatches
28
29
30class _JSONParserFeature(tests.Feature):
31
32 def _probe(self):
33 return lp_api_lite.json is not None
34
35 def feature_name(self):
36 return 'simplejson or json'
37
38JSONParserFeature = _JSONParserFeature()
39
40_example_response = r"""
41{
42 "total_size": 2,
43 "start": 0,
44 "next_collection_link": "https://api.launchpad.net/1.0/ubuntu/+archive/primary?distro_series=%2Fubuntu%2Flucid&exact_match=true&source_name=%22bzr%22&status=Published&ws.op=getPublishedSources&ws.start=1&ws.size=1",
45 "entries": [
46 {
47 "package_creator_link": "https://api.launchpad.net/1.0/~maxb",
48 "package_signer_link": "https://api.launchpad.net/1.0/~jelmer",
49 "source_package_name": "bzr",
50 "removal_comment": null,
51 "display_name": "bzr 2.1.4-0ubuntu1 in lucid",
52 "date_made_pending": null,
53 "source_package_version": "2.1.4-0ubuntu1",
54 "date_superseded": null,
55 "http_etag": "\"9ba966152dec474dc0fe1629d0bbce2452efaf3b-5f4c3fbb3eaf26d502db4089777a9b6a0537ffab\"",
56 "self_link": "https://api.launchpad.net/1.0/ubuntu/+archive/primary/+sourcepub/1750327",
57 "distro_series_link": "https://api.launchpad.net/1.0/ubuntu/lucid",
58 "component_name": "main",
59 "status": "Published",
60 "date_removed": null,
61 "pocket": "Updates",
62 "date_published": "2011-05-30T06:09:58.653984+00:00",
63 "removed_by_link": null,
64 "section_name": "devel",
65 "resource_type_link": "https://api.launchpad.net/1.0/#source_package_publishing_history",
66 "archive_link": "https://api.launchpad.net/1.0/ubuntu/+archive/primary",
67 "package_maintainer_link": "https://api.launchpad.net/1.0/~ubuntu-devel-discuss-lists",
68 "date_created": "2011-05-30T05:19:12.233621+00:00",
69 "scheduled_deletion_date": null
70 }
71 ]
72}"""
73
74_no_versions_response = '{"total_size": 0, "start": 0, "entries": []}'
75
76
77class TestLatestPublication(tests.TestCase):
78
79 def make_latest_publication(self, archive='ubuntu', series='natty',
80 project='bzr'):
81 return lp_api_lite.LatestPublication(archive, series, project)
82
83 def assertPlace(self, place, archive, series, project):
84 lp = lp_api_lite.LatestPublication(archive, series, project)
85 self.assertEqual(place, lp.place())
86
87 def test_init(self):
88 latest_pub = self.make_latest_publication()
89 self.assertEqual('ubuntu', latest_pub._archive)
90 self.assertEqual('natty', latest_pub._series)
91 self.assertEqual('bzr', latest_pub._project)
92 self.assertEqual('Release', latest_pub._pocket)
93
94 def test__archive_URL(self):
95 latest_pub = self.make_latest_publication()
96 self.assertEqual(
97 'https://api.launchpad.net/1.0/ubuntu/+archive/primary',
98 latest_pub._archive_URL())
99
100 def test__publication_status_for_ubuntu(self):
101 latest_pub = self.make_latest_publication()
102 self.assertEqual('Published', latest_pub._publication_status())
103
104 def test__publication_status_for_debian(self):
105 latest_pub = self.make_latest_publication(archive='debian')
106 self.assertEqual('Pending', latest_pub._publication_status())
107
108 def test_pocket(self):
109 latest_pub = self.make_latest_publication(series='natty-proposed')
110 self.assertEqual('natty', latest_pub._series)
111 self.assertEqual('Proposed', latest_pub._pocket)
112
113 def test_series_None(self):
114 latest_pub = self.make_latest_publication(series=None)
115 self.assertEqual('ubuntu', latest_pub._archive)
116 self.assertEqual(None, latest_pub._series)
117 self.assertEqual('bzr', latest_pub._project)
118 self.assertEqual('Release', latest_pub._pocket)
119
120 def test__query_params(self):
121 latest_pub = self.make_latest_publication()
122 self.assertEqual({'ws.op': 'getPublishedSources',
123 'exact_match': 'true',
124 'source_name': '"bzr"',
125 'status': 'Published',
126 'ws.size': '1',
127 'distro_series': '/ubuntu/natty',
128 'pocket': 'Release',
129 }, latest_pub._query_params())
130
131 def test__query_params_no_series(self):
132 latest_pub = self.make_latest_publication(series=None)
133 self.assertEqual({'ws.op': 'getPublishedSources',
134 'exact_match': 'true',
135 'source_name': '"bzr"',
136 'status': 'Published',
137 'ws.size': '1',
138 'pocket': 'Release',
139 }, latest_pub._query_params())
140
141 def test__query_params_pocket(self):
142 latest_pub = self.make_latest_publication(series='natty-proposed')
143 self.assertEqual({'ws.op': 'getPublishedSources',
144 'exact_match': 'true',
145 'source_name': '"bzr"',
146 'status': 'Published',
147 'ws.size': '1',
148 'distro_series': '/ubuntu/natty',
149 'pocket': 'Proposed',
150 }, latest_pub._query_params())
151
152 def test__query_URL(self):
153 latest_pub = self.make_latest_publication()
154 # we explicitly sort params, so we can be sure this URL matches exactly
155 self.assertEqual(
156 'https://api.launchpad.net/1.0/ubuntu/+archive/primary'
157 '?distro_series=%2Fubuntu%2Fnatty&exact_match=true'
158 '&pocket=Release&source_name=%22bzr%22&status=Published'
159 '&ws.op=getPublishedSources&ws.size=1',
160 latest_pub._query_URL())
161
162 def DONT_test__gracefully_handle_failed_rpc_connection(self):
163 # TODO: This test kind of sucks. We intentionally create an arbitrary
164 # port and don't listen to it, because we want the request to fail.
165 # However, it seems to take 1s for it to timeout. Is there a way
166 # to make it fail faster?
167 latest_pub = self.make_latest_publication()
168 s = socket.socket()
169 s.bind(('127.0.0.1', 0))
170 addr, port = s.getsockname()
171 latest_pub.LP_API_ROOT = 'http://%s:%s/' % (addr, port)
172 s.close()
173 self.assertIs(None, latest_pub._get_lp_info())
174
175 def DONT_test__query_launchpad(self):
176 # TODO: This is a test that we are making a valid request against
177 # launchpad. This seems important, but it is slow, requires net
178 # access, and requires launchpad to be up and running. So for
179 # now, it is commented out for production tests.
180 latest_pub = self.make_latest_publication()
181 json_txt = latest_pub._get_lp_info()
182 self.assertIsNot(None, json_txt)
183 if lp_api_lite.json is None:
184 # We don't have a way to parse the text
185 return
186 # The content should be a valid json result
187 content = lp_api_lite.json.loads(json_txt)
188 entries = content['entries'] # It should have an 'entries' field.
189 # ws.size should mean we get 0 or 1, and there should be something
190 self.assertEqual(1, len(entries))
191 entry = entries[0]
192 self.assertEqual('bzr', entry['source_package_name'])
193 version = entry['source_package_version']
194 self.assertIsNot(None, version)
195
196 def disableJSON(self):
197 orig = lp_api_lite.json
198 def cleanup():
199 lp_api_lite.json = orig
200 self.addCleanup(cleanup)
201 lp_api_lite.json = None
202
203 def test__get_lp_info_no_json(self):
204 # If we can't parse the json, we don't make the query.
205 self.disableJSON()
206 latest_pub = self.make_latest_publication()
207 self.assertIs(None, latest_pub._get_lp_info())
208
209 def test__parse_json_info_no_module(self):
210 # If a json parsing module isn't available, we just return None here.
211 self.disableJSON()
212 latest_pub = self.make_latest_publication()
213 self.assertIs(None, latest_pub._parse_json_info(_example_response))
214
215 def test__parse_json_example_response(self):
216 self.requireFeature(JSONParserFeature)
217 latest_pub = self.make_latest_publication()
218 content = latest_pub._parse_json_info(_example_response)
219 self.assertIsNot(None, content)
220 self.assertEqual(2, content['total_size'])
221 entries = content['entries']
222 self.assertEqual(1, len(entries))
223 entry = entries[0]
224 self.assertEqual('bzr', entry['source_package_name'])
225 self.assertEqual("2.1.4-0ubuntu1", entry["source_package_version"])
226
227 def test__parse_json_not_json(self):
228 self.requireFeature(JSONParserFeature)
229 latest_pub = self.make_latest_publication()
230 self.assertIs(None, latest_pub._parse_json_info('Not_valid_json'))
231
232 def test_get_latest_version_no_response(self):
233 latest_pub = self.make_latest_publication()
234 latest_pub._get_lp_info = lambda: None
235 self.assertEqual(None, latest_pub.get_latest_version())
236
237 def test_get_latest_version_no_json(self):
238 self.disableJSON()
239 latest_pub = self.make_latest_publication()
240 self.assertEqual(None, latest_pub.get_latest_version())
241
242 def test_get_latest_version_invalid_json(self):
243 self.requireFeature(JSONParserFeature)
244 latest_pub = self.make_latest_publication()
245 latest_pub._get_lp_info = lambda: "not json"
246 self.assertEqual(None, latest_pub.get_latest_version())
247
248 def test_get_latest_version_no_versions(self):
249 self.requireFeature(JSONParserFeature)
250 latest_pub = self.make_latest_publication()
251 latest_pub._get_lp_info = lambda: _no_versions_response
252 self.assertEqual(None, latest_pub.get_latest_version())
253
254 def test_get_latest_version_missing_entries(self):
255 # Launchpad's no-entries response does have an empty entries value.
256 # However, lets test that we handle other failures without tracebacks
257 self.requireFeature(JSONParserFeature)
258 latest_pub = self.make_latest_publication()
259 latest_pub._get_lp_info = lambda: '{}'
260 self.assertEqual(None, latest_pub.get_latest_version())
261
262 def test_get_latest_version_invalid_entries(self):
263 # Make sure we sanely handle a json response we don't understand
264 self.requireFeature(JSONParserFeature)
265 latest_pub = self.make_latest_publication()
266 latest_pub._get_lp_info = lambda: '{"entries": {"a": 1}}'
267 self.assertEqual(None, latest_pub.get_latest_version())
268
269 def test_get_latest_version_example(self):
270 self.requireFeature(JSONParserFeature)
271 latest_pub = self.make_latest_publication()
272 latest_pub._get_lp_info = lambda: _example_response
273 self.assertEqual("2.1.4-0ubuntu1", latest_pub.get_latest_version())
274
275 def DONT_test_get_latest_version_from_launchpad(self):
276 self.requireFeature(JSONParserFeature)
277 latest_pub = self.make_latest_publication()
278 self.assertIsNot(None, latest_pub.get_latest_version())
279
280 def test_place(self):
281 self.assertPlace('Ubuntu', 'ubuntu', None, 'bzr')
282 self.assertPlace('Ubuntu Natty', 'ubuntu', 'natty', 'bzr')
283 self.assertPlace('Ubuntu Natty Proposed', 'ubuntu', 'natty-proposed',
284 'bzr')
285 self.assertPlace('Debian', 'debian', None, 'bzr')
286 self.assertPlace('Debian Sid', 'debian', 'sid', 'bzr')
287
288
289class TestIsUpToDate(tests.TestCase):
290
291 def assertPackageBranchRe(self, url, user, archive, series, project):
292 m = launchpad._package_branch.search(url)
293 if m is None:
294 self.fail('package_branch regex did not match url: %s' % (url,))
295 self.assertEqual(
296 (user, archive, series, project),
297 m.group('user', 'archive', 'series', 'project'))
298
299 def assertNotPackageBranch(self, url):
300 self.assertIs(None, launchpad._get_package_branch_info(url))
301
302 def assertBranchInfo(self, url, archive, series, project):
303 self.assertEqual((archive, series, project),
304 launchpad._get_package_branch_info(url))
305
306 def test_package_branch_regex(self):
307 self.assertPackageBranchRe(
308 'http://bazaar.launchpad.net/+branch/ubuntu/foo',
309 None, 'ubuntu', None, 'foo')
310 self.assertPackageBranchRe(
311 'bzr+ssh://bazaar.launchpad.net/+branch/ubuntu/natty/foo',
312 None, 'ubuntu', 'natty/', 'foo')
313 self.assertPackageBranchRe(
314 'sftp://bazaar.launchpad.net/+branch/debian/foo',
315 None, 'debian', None, 'foo')
316 self.assertPackageBranchRe(
317 'http://bazaar.launchpad.net/+branch/debian/sid/foo',
318 None, 'debian', 'sid/', 'foo')
319 self.assertPackageBranchRe(
320 'http://bazaar.launchpad.net/+branch'
321 '/~ubuntu-branches/ubuntu/natty/foo/natty',
322 '~ubuntu-branches/', 'ubuntu', 'natty/', 'foo')
323 self.assertPackageBranchRe(
324 'http://bazaar.launchpad.net/+branch'
325 '/~user/ubuntu/natty/foo/test',
326 '~user/', 'ubuntu', 'natty/', 'foo')
327
328 def test_package_branch_doesnt_match(self):
329 self.assertNotPackageBranch('http://example.com/ubuntu/foo')
330 self.assertNotPackageBranch(
331 'http://bazaar.launchpad.net/+branch/bzr')
332 self.assertNotPackageBranch(
333 'http://bazaar.launchpad.net/+branch/~bzr-pqm/bzr/bzr.dev')
334 # Not a packaging branch because ~user isn't ~ubuntu-branches
335 self.assertNotPackageBranch(
336 'http://bazaar.launchpad.net/+branch'
337 '/~user/ubuntu/natty/foo/natty')
338 # Older versions of bzr-svn/hg/git did not set Branch.base until after
339 # they called Branch.__init__().
340 self.assertNotPackageBranch(None)
341
342 def test__get_package_branch_info(self):
343 self.assertBranchInfo(
344 'bzr+ssh://bazaar.launchpad.net/+branch/ubuntu/natty/foo',
345 'ubuntu', 'natty', 'foo')
346 self.assertBranchInfo(
347 'bzr+ssh://bazaar.launchpad.net/+branch'
348 '/~ubuntu-branches/ubuntu/natty/foo/natty',
349 'ubuntu', 'natty', 'foo')
350 self.assertBranchInfo(
351 'http://bazaar.launchpad.net/+branch'
352 '/~ubuntu-branches/debian/sid/foo/sid',
353 'debian', 'sid', 'foo')
354
355
356class TestGetMostRecentTag(tests.TestCaseWithMemoryTransport):
357
358 def make_simple_builder(self):
359 builder = self.make_branch_builder('tip')
360 builder.build_snapshot('A', None, [
361 ('add', ('', 'root-id', 'directory', None))])
362 b = builder.get_branch()
363 b.tags.set_tag('tip-1.0', 'A')
364 return builder, b, b.tags.get_tag_dict()
365
366 def test_get_most_recent_tag_tip(self):
367 builder, b, tag_dict = self.make_simple_builder()
368 self.assertEqual('tip-1.0',
369 lp_api_lite.get_most_recent_tag(tag_dict, b))
370
371 def test_get_most_recent_tag_older(self):
372 builder, b, tag_dict = self.make_simple_builder()
373 builder.build_snapshot('B', ['A'], [])
374 self.assertEqual('B', b.last_revision())
375 self.assertEqual('tip-1.0',
376 lp_api_lite.get_most_recent_tag(tag_dict, b))
377
378
379class StubLatestPublication(object):
380
381 def __init__(self, latest):
382 self.called = False
383 self.latest = latest
384
385 def get_latest_version(self):
386 self.called = True
387 return self.latest
388
389 def place(self):
390 return 'Ubuntu Natty'
391
392
393class TestReportFreshness(tests.TestCaseWithMemoryTransport):
394
395 def setUp(self):
396 super(TestReportFreshness, self).setUp()
397 builder = self.make_branch_builder('tip')
398 builder.build_snapshot('A', None, [
399 ('add', ('', 'root-id', 'directory', None))])
400 self.branch = builder.get_branch()
401
402 def assertFreshnessReports(self, verbosity, latest_version, content):
403 """Assert that lp_api_lite.report_freshness reports the given content.
404
405 :param verbosity: The reporting level
406 :param latest_version: The version reported by StubLatestPublication
407 :param content: The expected content. This should be in DocTest form.
408 """
409 orig_log_len = len(self.get_log())
410 lp_api_lite.report_freshness(self.branch, verbosity,
411 StubLatestPublication(latest_version))
412 new_content = self.get_log()[orig_log_len:]
413 # Strip out lines that have LatestPublication.get_* because those are
414 # timing related lines. While interesting to log for now, they aren't
415 # something we want to be testing
416 new_content = new_content.split('\n')
417 for i in range(2):
418 if (len(new_content) > 0
419 and 'LatestPublication.get_' in new_content[0]):
420 new_content = new_content[1:]
421 new_content = '\n'.join(new_content)
422 self.assertThat(new_content,
423 DocTestMatches(content,
424 doctest.ELLIPSIS | doctest.REPORT_UDIFF))
425
426 def test_verbosity_off_skips_check(self):
427 # We force _get_package_branch_info so that we know it would otherwise
428 # try to connect to launcphad
429 orig_gpbi = launchpad._get_package_branch_info
430 orig_lp = lp_api_lite.LatestPublication
431 def cleanup():
432 launchpad._get_package_branch_info = orig_gpbi
433 lp_api_lite.LatestPublication = orig_lp
434 self.addCleanup(cleanup)
435 launchpad._get_package_branch_info = lambda x: ('ubuntu', 'natty', 'bzr')
436 lp_api_lite.LatestPublication = lambda *args: self.fail('Tried to query launchpad')
437 c = self.branch.get_config()
438 c.set_user_option('launchpad.packaging_verbosity', 'off')
439 orig_log_len = len(self.get_log())
440 launchpad._check_is_up_to_date(self.branch)
441 new_content = self.get_log()[orig_log_len:]
442 self.assertContainsRe(new_content,
443 'not checking memory.*/tip/ because verbosity is turned off')
444
445 def test_verbosity_off(self):
446 latest_pub = StubLatestPublication('1.0-1ubuntu2')
447 lp_api_lite.report_freshness(self.branch, 'off', latest_pub)
448 self.assertFalse(latest_pub.called)
449
450 def test_verbosity_all_out_of_date_smoke(self):
451 self.branch.tags.set_tag('1.0-1ubuntu1', 'A')
452 self.assertFreshnessReports('all', '1.0-1ubuntu2',
453 ' INFO Most recent Ubuntu Natty version: 1.0-1ubuntu2\n'
454 'Packaging branch version: 1.0-1ubuntu1\n'
455 'Packaging branch status: OUT-OF-DATE\n')
456
457
458class Test_GetNewestVersions(tests.TestCaseWithMemoryTransport):
459
460 def setUp(self):
461 super(Test_GetNewestVersions, self).setUp()
462 builder = self.make_branch_builder('tip')
463 builder.build_snapshot('A', None, [
464 ('add', ('', 'root-id', 'directory', None))])
465 self.branch = builder.get_branch()
466
467 def assertLatestVersions(self, latest_branch_version, pub_version):
468 if latest_branch_version is not None:
469 self.branch.tags.set_tag(latest_branch_version, 'A')
470 latest_pub = StubLatestPublication(pub_version)
471 self.assertEqual((pub_version, latest_branch_version),
472 lp_api_lite._get_newest_versions(self.branch, latest_pub))
473
474 def test_no_tags(self):
475 self.assertLatestVersions(None, '1.0-1ubuntu2')
476
477 def test_out_of_date(self):
478 self.assertLatestVersions('1.0-1ubuntu1', '1.0-1ubuntu2')
479
480 def test_up_to_date(self):
481 self.assertLatestVersions('1.0-1ubuntu2', '1.0-1ubuntu2')
482
483 def test_missing(self):
484 self.assertLatestVersions(None, None)
485
486
487class Test_ReportFreshness(tests.TestCase):
488
489 def assertReportedFreshness(self, verbosity, latest_ver, branch_latest_ver,
490 content, place='Ubuntu Natty'):
491 """Assert that lp_api_lite.report_freshness reports the given content.
492 """
493 reported = []
494 def report_func(value):
495 reported.append(value)
496 lp_api_lite._report_freshness(latest_ver, branch_latest_ver, place,
497 verbosity, report_func)
498 new_content = '\n'.join(reported)
499 self.assertThat(new_content,
500 DocTestMatches(content,
501 doctest.ELLIPSIS | doctest.REPORT_UDIFF))
502
503 def test_verbosity_minimal_no_tags(self):
504 self.assertReportedFreshness('minimal', '1.0-1ubuntu2', None,
505 'Branch is OUT-OF-DATE, Ubuntu Natty has 1.0-1ubuntu2\n')
506
507 def test_verbosity_minimal_out_of_date(self):
508 self.assertReportedFreshness('minimal', '1.0-1ubuntu2', '1.0-1ubuntu1',
509 '1.0-1ubuntu1 is OUT-OF-DATE,'
510 ' Ubuntu Natty has 1.0-1ubuntu2\n')
511
512 def test_verbosity_minimal_up_to_date(self):
513 self.assertReportedFreshness('minimal', '1.0-1ubuntu2', '1.0-1ubuntu2',
514 '')
515
516 def test_verbosity_minimal_missing(self):
517 self.assertReportedFreshness('minimal', None, None,
518 '')
519
520 def test_verbosity_short_out_of_date(self):
521 self.assertReportedFreshness('short', '1.0-1ubuntu2', '1.0-1ubuntu1',
522 '1.0-1ubuntu1 is OUT-OF-DATE,'
523 ' Ubuntu Natty has 1.0-1ubuntu2\n')
524
525 def test_verbosity_short_up_to_date(self):
526 self.assertReportedFreshness('short', '1.0-1ubuntu2', '1.0-1ubuntu2',
527 '1.0-1ubuntu2 is CURRENT in Ubuntu Natty')
528
529 def test_verbosity_short_missing(self):
530 self.assertReportedFreshness('short', None, None,
531 'Ubuntu Natty is MISSING a version')
532
533 def test_verbosity_all_no_tags(self):
534 self.assertReportedFreshness('all', '1.0-1ubuntu2', None,
535 'Most recent Ubuntu Natty version: 1.0-1ubuntu2\n'
536 'Packaging branch version: None\n'
537 'Packaging branch status: OUT-OF-DATE\n')
538
539 def test_verbosity_all_out_of_date(self):
540 self.assertReportedFreshness('all', '1.0-1ubuntu2', '1.0-1ubuntu1',
541 'Most recent Ubuntu Natty version: 1.0-1ubuntu2\n'
542 'Packaging branch version: 1.0-1ubuntu1\n'
543 'Packaging branch status: OUT-OF-DATE\n')
544
545 def test_verbosity_all_up_to_date(self):
546 self.assertReportedFreshness('all', '1.0-1ubuntu2', '1.0-1ubuntu2',
547 'Most recent Ubuntu Natty version: 1.0-1ubuntu2\n'
548 'Packaging branch status: CURRENT\n')
549
550 def test_verbosity_all_missing(self):
551 self.assertReportedFreshness('all', None, None,
552 'Most recent Ubuntu Natty version: MISSING\n')
553
554 def test_verbosity_None_is_all(self):
555 self.assertReportedFreshness(None, '1.0-1ubuntu2', '1.0-1ubuntu2',
556 'Most recent Ubuntu Natty version: 1.0-1ubuntu2\n'
557 'Packaging branch status: CURRENT\n')
0558
=== modified file 'bzrlib/repofmt/groupcompress_repo.py'
--- bzrlib/repofmt/groupcompress_repo.py 2010-08-07 00:54:52 +0000
+++ bzrlib/repofmt/groupcompress_repo.py 2011-09-27 11:59:22 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2008, 2009, 2010 Canonical Ltd1# Copyright (C) 2008-2011 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -419,9 +419,18 @@
419 inventory_keys = source_vf.keys()419 inventory_keys = source_vf.keys()
420 missing_inventories = set(self.revision_keys).difference(inventory_keys)420 missing_inventories = set(self.revision_keys).difference(inventory_keys)
421 if missing_inventories:421 if missing_inventories:
422 missing_inventories = sorted(missing_inventories)422 # Go back to the original repo, to see if these are really missing
423 raise ValueError('We are missing inventories for revisions: %s'423 # https://bugs.launchpad.net/bzr/+bug/437003
424 % (missing_inventories,))424 # If we are packing a subset of the repo, it is fine to just have
425 # the data in another Pack file, which is not included in this pack
426 # operation.
427 inv_index = self._pack_collection.repo.inventories._index
428 pmap = inv_index.get_parent_map(missing_inventories)
429 really_missing = missing_inventories.difference(pmap)
430 if really_missing:
431 missing_inventories = sorted(really_missing)
432 raise ValueError('We are missing inventories for revisions: %s'
433 % (missing_inventories,))
425 self._copy_stream(source_vf, target_vf, inventory_keys,434 self._copy_stream(source_vf, target_vf, inventory_keys,
426 'inventories', self._get_filtered_inv_stream, 2)435 'inventories', self._get_filtered_inv_stream, 2)
427436
428437
=== modified file 'bzrlib/tests/per_repository_reference/__init__.py'
--- bzrlib/tests/per_repository_reference/__init__.py 2010-08-07 00:54:52 +0000
+++ bzrlib/tests/per_repository_reference/__init__.py 2011-09-27 11:59:22 +0000
@@ -117,6 +117,7 @@
117 'bzrlib.tests.per_repository_reference.test_fetch',117 'bzrlib.tests.per_repository_reference.test_fetch',
118 'bzrlib.tests.per_repository_reference.test_get_record_stream',118 'bzrlib.tests.per_repository_reference.test_get_record_stream',
119 'bzrlib.tests.per_repository_reference.test_get_rev_id_for_revno',119 'bzrlib.tests.per_repository_reference.test_get_rev_id_for_revno',
120 'bzrlib.tests.per_repository_reference.test_graph',
120 'bzrlib.tests.per_repository_reference.test_initialize',121 'bzrlib.tests.per_repository_reference.test_initialize',
121 'bzrlib.tests.per_repository_reference.test_unlock',122 'bzrlib.tests.per_repository_reference.test_unlock',
122 ]123 ]
123124
=== added file 'bzrlib/tests/per_repository_reference/test_graph.py'
--- bzrlib/tests/per_repository_reference/test_graph.py 1970-01-01 00:00:00 +0000
+++ bzrlib/tests/per_repository_reference/test_graph.py 2011-09-27 11:59:22 +0000
@@ -0,0 +1,45 @@
1# Copyright (C) 2011 Canonical Ltd
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17
18"""Tests for graph operations on stacked repositories."""
19
20
21from bzrlib.tests.per_repository import TestCaseWithRepository
22
23
24class TestGraph(TestCaseWithRepository):
25
26 def test_get_known_graph_ancestry_stacked(self):
27 """get_known_graph_ancestry works correctly on stacking.
28
29 See <https://bugs.launchpad.net/bugs/715000>.
30 """
31 branch_a, branch_b, branch_c, revid_1 = self.make_double_stacked_branches()
32 for br in [branch_c]:
33 self.assertEquals(
34 [revid_1],
35 br.repository.get_known_graph_ancestry([revid_1]).topo_sort())
36
37 def make_double_stacked_branches(self):
38 wt_a = self.make_branch_and_tree('a')
39 branch_a = wt_a.branch
40 branch_b = self.make_branch('b')
41 branch_b.set_stacked_on_url('../a')
42 branch_c = self.make_branch('c')
43 branch_c.set_stacked_on_url('../b')
44 revid_1 = wt_a.commit('first commit')
45 return branch_a, branch_b, branch_c, revid_1
046
=== modified file 'bzrlib/tests/test_config.py'
--- bzrlib/tests/test_config.py 2010-08-07 00:54:52 +0000
+++ bzrlib/tests/test_config.py 2011-09-27 11:59:22 +0000
@@ -241,11 +241,37 @@
241 """241 """
242 co = config.ConfigObj()242 co = config.ConfigObj()
243 co['test'] = 'foo#bar'243 co['test'] = 'foo#bar'
244 lines = co.write()244 outfile = StringIO()
245 co.write(outfile=outfile)
246 lines = outfile.getvalue().splitlines()
245 self.assertEqual(lines, ['test = "foo#bar"'])247 self.assertEqual(lines, ['test = "foo#bar"'])
246 co2 = config.ConfigObj(lines)248 co2 = config.ConfigObj(lines)
247 self.assertEqual(co2['test'], 'foo#bar')249 self.assertEqual(co2['test'], 'foo#bar')
248250
251 def test_triple_quotes(self):
252 # Bug #710410: if the value string has triple quotes
253 # then ConfigObj versions up to 4.7.2 will quote them wrong
254 # and won't able to read them back
255 triple_quotes_value = '''spam
256""" that's my spam """
257eggs'''
258 co = config.ConfigObj()
259 co['test'] = triple_quotes_value
260 # While writing this test another bug in ConfigObj has been found:
261 # method co.write() without arguments produces list of lines
262 # one option per line, and multiline values are not split
263 # across multiple lines,
264 # and that breaks the parsing these lines back by ConfigObj.
265 # This issue only affects test, but it's better to avoid
266 # `co.write()` construct at all.
267 # [bialix 20110222] bug report sent to ConfigObj's author
268 outfile = StringIO()
269 co.write(outfile=outfile)
270 output = outfile.getvalue()
271 # now we're trying to read it back
272 co2 = config.ConfigObj(StringIO(output))
273 self.assertEquals(triple_quotes_value, co2['test'])
274
249275
250erroneous_config = """[section] # line 1276erroneous_config = """[section] # line 1
251good=good # line 2277good=good # line 2
252278
=== modified file 'bzrlib/tests/test_conflicts.py'
--- bzrlib/tests/test_conflicts.py 2011-04-07 15:30:17 +0000
+++ bzrlib/tests/test_conflicts.py 2011-09-27 11:59:22 +0000
@@ -1049,6 +1049,70 @@
1049""")1049""")
10501050
10511051
1052class TestNoFinalPath(script.TestCaseWithTransportAndScript):
1053
1054 def test_bug_805809(self):
1055 self.run_script("""
1056$ bzr init trunk
1057Created a standalone tree (format: 2a)
1058$ cd trunk
1059$ echo trunk >file
1060$ bzr add
1061adding file
1062$ bzr commit -m 'create file on trunk'
10632>Committing to: .../trunk/
10642>added file
10652>Committed revision 1.
1066# Create a debian branch based on trunk
1067$ cd ..
1068$ bzr branch trunk -r 1 debian
10692>Branched 1 revision(s).
1070$ cd debian
1071$ mkdir dir
1072$ bzr add
1073adding dir
1074$ bzr mv file dir
1075file => dir/file
1076$ bzr commit -m 'rename file to dir/file for debian'
10772>Committing to: .../debian/
10782>added dir
10792>renamed file => dir/file
10802>Committed revision 2.
1081# Create an experimental branch with a new root-id
1082$ cd ..
1083$ bzr init experimental
1084$ cd experimental
1085# merge debian even without a common ancestor
1086$ bzr merge ../debian -r0..2
10872>+N dir/
10882>+N dir/file
10892>All changes applied successfully.
1090$ bzr commit -m 'merging debian into experimental'
10912>Committing to: .../experimental/
10922>deleted
10932>modified dir
10942>Committed revision 1.
1095# Create an ubuntu branch with yet another root-id
1096$ cd ..
1097$ bzr init ubuntu
1098$ cd ubuntu
1099# Also merge debian
1100$ bzr merge ../debian -r0..2
11012>+N dir/
11022>+N dir/file
11032>All changes applied successfully.
1104$ bzr commit -m 'merging debian'
11052>Committing to: .../ubuntu/
11062>deleted
11072>modified dir
11082>Committed revision 1.
1109# Now try to merge experimental
1110$ bzr merge ../experimental
11112>Path conflict: dir / dir
11122>1 conflicts encountered.
1113""")
1114
1115
1052class TestResolveActionOption(tests.TestCase):1116class TestResolveActionOption(tests.TestCase):
10531117
1054 def setUp(self):1118 def setUp(self):
10551119
=== modified file 'bzrlib/tests/test_repository.py'
--- bzrlib/tests/test_repository.py 2010-08-07 00:54:52 +0000
+++ bzrlib/tests/test_repository.py 2011-09-27 11:59:22 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2006-2010 Canonical Ltd1# Copyright (C) 2006-2011 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -1622,6 +1622,106 @@
1622 self.assertTrue(new_pack.signature_index._optimize_for_size)1622 self.assertTrue(new_pack.signature_index._optimize_for_size)
16231623
16241624
1625class TestGCCHKPacker(TestCaseWithTransport):
1626
1627 def make_abc_branch(self):
1628 builder = self.make_branch_builder('source')
1629 builder.start_series()
1630 builder.build_snapshot('A', None, [
1631 ('add', ('', 'root-id', 'directory', None)),
1632 ('add', ('file', 'file-id', 'file', 'content\n')),
1633 ])
1634 builder.build_snapshot('B', ['A'], [
1635 ('add', ('dir', 'dir-id', 'directory', None))])
1636 builder.build_snapshot('C', ['B'], [
1637 ('modify', ('file-id', 'new content\n'))])
1638 builder.finish_series()
1639 return builder.get_branch()
1640
1641 def make_branch_with_disjoint_inventory_and_revision(self):
1642 """a repo with separate packs for a revisions Revision and Inventory.
1643
1644 There will be one pack file that holds the Revision content, and one
1645 for the Inventory content.
1646
1647 :return: (repository,
1648 pack_name_with_rev_A_Revision,
1649 pack_name_with_rev_A_Inventory,
1650 pack_name_with_rev_C_content)
1651 """
1652 b_source = self.make_abc_branch()
1653 b_base = b_source.bzrdir.sprout('base', revision_id='A').open_branch()
1654 b_stacked = b_base.bzrdir.sprout('stacked', stacked=True).open_branch()
1655 b_stacked.lock_write()
1656 self.addCleanup(b_stacked.unlock)
1657 b_stacked.fetch(b_source, 'B')
1658 # Now re-open the stacked repo directly (no fallbacks) so that we can
1659 # fill in the A rev.
1660 repo_not_stacked = b_stacked.bzrdir.open_repository()
1661 repo_not_stacked.lock_write()
1662 self.addCleanup(repo_not_stacked.unlock)
1663 # Now we should have a pack file with A's inventory, but not its
1664 # Revision
1665 self.assertEqual([('A',), ('B',)],
1666 sorted(repo_not_stacked.inventories.keys()))
1667 self.assertEqual([('B',)],
1668 sorted(repo_not_stacked.revisions.keys()))
1669 stacked_pack_names = repo_not_stacked._pack_collection.names()
1670 # We have a couple names here, figure out which has A's inventory
1671 for name in stacked_pack_names:
1672 pack = repo_not_stacked._pack_collection.get_pack_by_name(name)
1673 keys = [n[1] for n in pack.inventory_index.iter_all_entries()]
1674 if ('A',) in keys:
1675 inv_a_pack_name = name
1676 break
1677 else:
1678 self.fail('Could not find pack containing A\'s inventory')
1679 repo_not_stacked.fetch(b_source.repository, 'A')
1680 self.assertEqual([('A',), ('B',)],
1681 sorted(repo_not_stacked.revisions.keys()))
1682 new_pack_names = set(repo_not_stacked._pack_collection.names())
1683 rev_a_pack_names = new_pack_names.difference(stacked_pack_names)
1684 self.assertEqual(1, len(rev_a_pack_names))
1685 rev_a_pack_name = list(rev_a_pack_names)[0]
1686 # Now fetch 'C', so we have a couple pack files to join
1687 repo_not_stacked.fetch(b_source.repository, 'C')
1688 rev_c_pack_names = set(repo_not_stacked._pack_collection.names())
1689 rev_c_pack_names = rev_c_pack_names.difference(new_pack_names)
1690 self.assertEqual(1, len(rev_c_pack_names))
1691 rev_c_pack_name = list(rev_c_pack_names)[0]
1692 return (repo_not_stacked, rev_a_pack_name, inv_a_pack_name,
1693 rev_c_pack_name)
1694
1695 def test_pack_with_distant_inventories(self):
1696 # See https://bugs.launchpad.net/bzr/+bug/437003
1697 # When repacking, it is possible to have an inventory in a different
1698 # pack file than the associated revision. An autopack can then come
1699 # along, and miss that inventory, and complain.
1700 (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
1701 ) = self.make_branch_with_disjoint_inventory_and_revision()
1702 a_pack = repo._pack_collection.get_pack_by_name(rev_a_pack_name)
1703 c_pack = repo._pack_collection.get_pack_by_name(rev_c_pack_name)
1704 packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
1705 [a_pack, c_pack], '.test-pack')
1706 # This would raise ValueError in bug #437003, but should not raise an
1707 # error once fixed.
1708 packer.pack()
1709
1710 def test_pack_with_missing_inventory(self):
1711 # Similar to test_pack_with_missing_inventory, but this time, we force
1712 # the A inventory to actually be gone from the repository.
1713 (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
1714 ) = self.make_branch_with_disjoint_inventory_and_revision()
1715 inv_a_pack = repo._pack_collection.get_pack_by_name(inv_a_pack_name)
1716 repo._pack_collection._remove_pack_from_memory(inv_a_pack)
1717 packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
1718 repo._pack_collection.all_packs(), '.test-pack')
1719 e = self.assertRaises(ValueError, packer.pack)
1720 packer.new_pack.abort()
1721 self.assertContainsRe(str(e),
1722 r"We are missing inventories for revisions: .*'A'")
1723
1724
1625class TestCrossFormatPacks(TestCaseWithTransport):1725class TestCrossFormatPacks(TestCaseWithTransport):
16261726
1627 def log_pack(self, hint=None):1727 def log_pack(self, hint=None):
16281728
=== modified file 'bzrlib/transport/http/_pycurl.py'
--- bzrlib/transport/http/_pycurl.py 2011-04-07 15:30:17 +0000
+++ bzrlib/transport/http/_pycurl.py 2011-09-27 11:59:22 +0000
@@ -305,19 +305,19 @@
305 url, 'Unable to handle http code %d%s' % (code,msg))305 url, 'Unable to handle http code %d%s' % (code,msg))
306306
307 def _debug_cb(self, kind, text):307 def _debug_cb(self, kind, text):
308 if kind in (pycurl.INFOTYPE_HEADER_IN, pycurl.INFOTYPE_DATA_IN,308 if kind in (pycurl.INFOTYPE_HEADER_IN, pycurl.INFOTYPE_DATA_IN):
309 pycurl.INFOTYPE_SSL_DATA_IN):
310 self._report_activity(len(text), 'read')309 self._report_activity(len(text), 'read')
311 if (kind == pycurl.INFOTYPE_HEADER_IN310 if (kind == pycurl.INFOTYPE_HEADER_IN
312 and 'http' in debug.debug_flags):311 and 'http' in debug.debug_flags):
313 mutter('< %s' % text)312 mutter('< %s' % text)
314 elif kind in (pycurl.INFOTYPE_HEADER_OUT, pycurl.INFOTYPE_DATA_OUT,313 elif kind in (pycurl.INFOTYPE_HEADER_OUT, pycurl.INFOTYPE_DATA_OUT):
315 pycurl.INFOTYPE_SSL_DATA_OUT):
316 self._report_activity(len(text), 'write')314 self._report_activity(len(text), 'write')
317 if (kind == pycurl.INFOTYPE_HEADER_OUT315 if (kind == pycurl.INFOTYPE_HEADER_OUT
318 and 'http' in debug.debug_flags):316 and 'http' in debug.debug_flags):
319 mutter('> %s' % text)317 mutter('> %s' % text)
320 elif kind == pycurl.INFOTYPE_TEXT and 'http' in debug.debug_flags:318 elif (kind in (pycurl.INFOTYPE_TEXT, pycurl.INFOTYPE_SSL_DATA_IN,
319 pycurl.INFOTYPE_SSL_DATA_OUT)
320 and 'http' in debug.debug_flags):
321 mutter('* %s' % text)321 mutter('* %s' % text)
322322
323 def _set_curl_options(self, curl):323 def _set_curl_options(self, curl):
324324
=== modified file 'bzrlib/util/configobj/configobj.py'
--- bzrlib/util/configobj/configobj.py 2009-05-16 13:51:08 +0000
+++ bzrlib/util/configobj/configobj.py 2011-09-27 11:59:22 +0000
@@ -1794,10 +1794,12 @@
1794 def _get_triple_quote(self, value):1794 def _get_triple_quote(self, value):
1795 if (value.find('"""') != -1) and (value.find("'''") != -1):1795 if (value.find('"""') != -1) and (value.find("'''") != -1):
1796 raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)1796 raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1797 # upstream version (up to version 4.7.2) has the bug with incorrect quoting;
1798 # fixed in our copy based on the suggestion of ConfigObj's author
1797 if value.find('"""') == -1:1799 if value.find('"""') == -1:
1800 quot = tsquot
1801 else:
1798 quot = tdquot1802 quot = tdquot
1799 else:
1800 quot = tsquot
1801 return quot1803 return quot
18021804
18031805
18041806
=== modified file 'bzrlib/versionedfile.py'
--- bzrlib/versionedfile.py 2010-08-07 00:54:52 +0000
+++ bzrlib/versionedfile.py 2011-09-27 11:59:22 +0000
@@ -1090,6 +1090,19 @@
1090 def _extract_blocks(self, version_id, source, target):1090 def _extract_blocks(self, version_id, source, target):
1091 return None1091 return None
10921092
1093 def _transitive_fallbacks(self):
1094 """Return the whole stack of fallback versionedfiles.
1095
1096 This VersionedFiles may have a list of fallbacks, but it doesn't
1097 necessarily know about the whole stack going down, and it can't know
1098 at open time because they may change after the objects are opened.
1099 """
1100 all_fallbacks = []
1101 for a_vfs in self._fallback_vfs:
1102 all_fallbacks.append(a_vfs)
1103 all_fallbacks.extend(a_vfs._transitive_fallbacks())
1104 return all_fallbacks
1105
10931106
1094class ThunkedVersionedFiles(VersionedFiles):1107class ThunkedVersionedFiles(VersionedFiles):
1095 """Storage for many versioned files thunked onto a 'VersionedFile' class.1108 """Storage for many versioned files thunked onto a 'VersionedFile' class.
10961109
=== modified file 'debian/changelog'
--- debian/changelog 2011-04-07 15:30:17 +0000
+++ debian/changelog 2011-09-27 11:59:22 +0000
@@ -1,3 +1,13 @@
1bzr (2.2.5-0ubuntu1) maverick-proposed; urgency=low
2
3 * New upstream release.
4 + Fixes merge failing with NoFinalPath. LP: #805809
5 + Warns users when brancing from UDD branches that are out of date.
6 LP: #609187
7 + stacking is now fully transitive. LP: #715000
8
9 -- Jelmer Vernooij <jelmer@ubuntu.com> Tue, 27 Sep 2011 10:30:17 +0200
10
1bzr (2.2.4-0ubuntu1) maverick-proposed; urgency=low11bzr (2.2.4-0ubuntu1) maverick-proposed; urgency=low
212
3 [ Jelmer Vernooij ]13 [ Jelmer Vernooij ]
414
=== modified file 'debian/watch'
--- debian/watch 2011-04-07 15:30:17 +0000
+++ debian/watch 2011-09-27 11:59:22 +0000
@@ -1,3 +1,3 @@
1version=31version=3
2opts="uversionmangle=s/rc/~rc/;s/b/~b/;s/^/2.2./" \2opts="uversionmangle=s/rc/~rc/;s/b/~b/;s/^/2.2./" \
3https://launchpad.net/bzr/+download http://launchpad.net/bzr/.*/bzr-2.2.(.+).tar.gz3https://launchpad.net/bzr/2.2 http://launchpad.net/bzr/.*/bzr-2.2.(.+).tar.gz
44
=== modified file 'doc/en/whats-new/whats-new-in-2.2.txt'
--- doc/en/whats-new/whats-new-in-2.2.txt 2011-04-07 15:30:17 +0000
+++ doc/en/whats-new/whats-new-in-2.2.txt 2011-09-27 11:59:22 +0000
@@ -36,7 +36,10 @@
36server and python-2.7 compatibility.36server and python-2.7 compatibility.
3737
38Bazaar 2.2.4 fixed a regression for some interactions with the launchpad38Bazaar 2.2.4 fixed a regression for some interactions with the launchpad
39server .39server.
40
41Bazaar 2.2.5 fixed a regression in some rare conflict resolutions and warns
42when branching an out-of-date ubuntu packaging branch.
4043
41See the :doc:`../release-notes/index` for details.44See the :doc:`../release-notes/index` for details.
4245
4346
=== modified file 'setup.py'
--- setup.py 2011-04-07 15:30:17 +0000
+++ setup.py 2011-09-27 11:59:22 +0000
@@ -468,6 +468,12 @@
468 packages.append('sqlite3')468 packages.append('sqlite3')
469469
470470
471def get_fastimport_py2exe_info(includes, excludes, packages):
472 # This is the python-fastimport package, not to be confused with the
473 # bzr-fastimport plugin.
474 packages.append('fastimport')
475
476
471if 'bdist_wininst' in sys.argv:477if 'bdist_wininst' in sys.argv:
472 def find_docs():478 def find_docs():
473 docs = []479 docs = []
@@ -660,6 +666,9 @@
660 if 'svn' in plugins:666 if 'svn' in plugins:
661 get_svn_py2exe_info(includes, excludes, packages)667 get_svn_py2exe_info(includes, excludes, packages)
662668
669 if 'fastimport' in plugins:
670 get_fastimport_py2exe_info(includes, excludes, packages)
671
663 if "TBZR" in os.environ:672 if "TBZR" in os.environ:
664 # TORTOISE_OVERLAYS_MSI_WIN32 must be set to the location of the673 # TORTOISE_OVERLAYS_MSI_WIN32 must be set to the location of the
665 # TortoiseOverlays MSI installer file. It is in the TSVN svn repo and674 # TortoiseOverlays MSI installer file. It is in the TSVN svn repo and

Subscribers

People subscribed via source and target branches