Merge lp:~jelmer/bzr-builddeb/709263-previous-to-pocket into lp:~jelmer/bzr-builddeb/recommend-lplib

Proposed by Jelmer Vernooij
Status: Superseded
Proposed branch: lp:~jelmer/bzr-builddeb/709263-previous-to-pocket
Merge into: lp:~jelmer/bzr-builddeb/recommend-lplib
Diff against target: 9318 lines (+5874/-1295)
51 files modified
.bzrignore (+1/-0)
.testr.conf (+4/-0)
README (+4/-6)
__init__.py (+78/-23)
builder.py (+6/-13)
bzrtools_bzrtools.py (+30/-0)
bzrtools_import.py (+409/-0)
changes.py (+10/-6)
cmds.py (+595/-306)
config.py (+24/-1)
debian/changelog (+190/-3)
debian/control (+8/-9)
debian/doc-base (+1/-2)
dh_make.py (+128/-0)
directory.py (+19/-9)
doc/user_manual/normal.rst (+37/-1)
doc/user_manual/upstream_tarballs.rst (+3/-1)
errors.py (+46/-1)
hooks.py (+2/-2)
import_dsc.py (+491/-507)
info.py (+1/-1)
launchpad.py (+6/-5)
merge_changelog.py (+152/-0)
merge_package.py (+183/-0)
merge_upstream.py (+103/-27)
repack_tarball.py (+32/-26)
source_distiller.py (+4/-11)
tagging.py (+75/-0)
tests/__init__.py (+56/-32)
tests/blackbox/__init__.py (+3/-2)
tests/blackbox/test_builddeb.py (+35/-14)
tests/blackbox/test_do.py (+11/-4)
tests/blackbox/test_import_dsc.py (+40/-18)
tests/blackbox/test_import_upstream.py (+149/-0)
tests/blackbox/test_mark_uploaded.py (+7/-3)
tests/blackbox/test_merge_package.py (+207/-0)
tests/blackbox/test_merge_upstream.py (+143/-5)
tests/test_bzrtools_import.py (+175/-0)
tests/test_config.py (+7/-1)
tests/test_dh_make.py (+58/-0)
tests/test_import_dsc.py (+567/-41)
tests/test_merge_changelog.py (+252/-0)
tests/test_merge_package.py (+650/-0)
tests/test_merge_upstream.py (+37/-19)
tests/test_repack_tarball_extra.py (+21/-0)
tests/test_source_distiller.py (+5/-1)
tests/test_tagging.py (+62/-0)
tests/test_upstream.py (+6/-18)
tests/test_util.py (+251/-12)
upstream.py (+148/-111)
util.py (+342/-54)
To merge this branch: bzr merge lp:~jelmer/bzr-builddeb/709263-previous-to-pocket
Reviewer Review Type Date Requested Status
Bzr-builddeb-hackers Pending
Review via email: mp+47808@code.launchpad.net

This proposal has been superseded by a proposal from 2011-01-28.

Description of the change

Fix builds where the previous upload was to a pocket.

To post a comment you must log in.
Revision history for this message
James Westby (james-w) wrote :

Hi,

are you sure this is the proposal you intended to make? The diff is huge.

Thanks,

James

495. By Jelmer Vernooij

Merge support for building package where previous upload was to a non-release pocket.

496. By Jelmer Vernooij

Merge refactoring of get_export_upstream_revision to take an upstream version string rather than a Version object.

497. By Jelmer Vernooij

Merge rename of get_specific_version to fetch_tall and returning of tarball path.

498. By Jelmer Vernooij

Merge change of get_latest_version to return latest version string rather than fetching tarball.

499. By Jelmer Vernooij

Merge fix to allow version argument to be optional.

500. By Jelmer Vernooij

Merge support for using 'bzr merge-upstream' in merge mode.

501. By Jelmer Vernooij

Quick fix for merge-upstream command logic.

I'll work on more tests for this code.

502. By Jelmer Vernooij

Merge support for preserving epochs.

503. By Jelmer Vernooij

'bzr merge-upstream' now errors out if the upstream version has already been merged.

Previously it would happily update the changelog with newer versions when the
branch was being used in 'merge' mode.

504. By Jelmer Vernooij

Prevent merge of old upstream version in merge-upstream. LP: #567742

Unmerged revisions

504. By Jelmer Vernooij

Prevent merge of old upstream version in merge-upstream. LP: #567742

503. By Jelmer Vernooij

'bzr merge-upstream' now errors out if the upstream version has already been merged.

Previously it would happily update the changelog with newer versions when the
branch was being used in 'merge' mode.

502. By Jelmer Vernooij

Merge support for preserving epochs.

501. By Jelmer Vernooij

Quick fix for merge-upstream command logic.

I'll work on more tests for this code.

500. By Jelmer Vernooij

Merge support for using 'bzr merge-upstream' in merge mode.

499. By Jelmer Vernooij

Merge fix to allow version argument to be optional.

498. By Jelmer Vernooij

Merge change of get_latest_version to return latest version string rather than fetching tarball.

497. By Jelmer Vernooij

Merge rename of get_specific_version to fetch_tall and returning of tarball path.

496. By Jelmer Vernooij

Merge refactoring of get_export_upstream_revision to take an upstream version string rather than a Version object.

495. By Jelmer Vernooij

Merge support for building package where previous upload was to a non-release pocket.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file '.bzrignore'
2--- .bzrignore 1970-01-01 00:00:00 +0000
3+++ .bzrignore 2011-01-28 14:24:44 +0000
4@@ -0,0 +1,1 @@
5+.testrepository
6
7=== added file '.testr.conf'
8--- .testr.conf 1970-01-01 00:00:00 +0000
9+++ .testr.conf 2011-01-28 14:24:44 +0000
10@@ -0,0 +1,4 @@
11+[DEFAULT]
12+# not quite ideal, because 'all tests' is too many, and 'just builddeb' is too few.
13+test_command=bzr selftest --subunit $IDOPTION
14+test_id_option=--load-list $IDFILE
15
16=== modified file 'README'
17--- README 2009-03-02 22:06:20 +0000
18+++ README 2011-01-28 14:24:44 +0000
19@@ -18,16 +18,14 @@
20 ------------
21
22 This plugin requires `python-debian`_ (at least version 0.1.11),
23-and a version of bzr at least 1.2. It also requires the
24-`bzrtools`_ plugin to be installed. These are available in Debian
25+and a version of bzr at least 2.1. These are available in Debian
26 (though maybe not at the required versions for a development version
27- of builddeb).
28+of builddeb).
29
30 .. _python-debian: http://bzr.debian.org/pkg-python-debian/trunk/
31-.. _bzrtools: https://launchpad.net/bzrtools
32
33 This plugin can be installed in two ways. As you are probably using a Debian
34-system you can probably just use the debian packages. The other way is to
35+system you can probably just use the Debian packages. The other way is to
36 branch it in to ``~/.bazaar/plugins/builddeb``, i.e::
37
38 bzr branch http://jameswestby.net/bzr/bzr-builddeb/ \
39@@ -262,7 +260,7 @@
40 $ bzr ci
41
42 There are two options when you want to build a Debian package, whether
43-it is a native package or not. Most packages are non-native so I will desribe
44+it is a native package or not. Most packages are non-native so I will describe
45 that first.
46
47 To create a non-native package you need an upstream tarball to build against.
48
49=== modified file '__init__.py'
50--- __init__.py 2009-07-26 15:51:02 +0000
51+++ __init__.py 2011-01-28 14:24:44 +0000
52@@ -25,21 +25,44 @@
53
54 import os
55
56-from bzrlib import msgeditor
57+import bzrlib
58+from bzrlib import (
59+ branch as _mod_branch,
60+ errors,
61+ merge,
62+ msgeditor,
63+ )
64 from bzrlib.commands import plugin_cmds
65+from bzrlib.config import config_dir
66 from bzrlib.directory_service import directories
67
68 from info import (
69 bzr_plugin_version as version_info,
70 )
71
72+
73+if getattr(merge, 'ConfigurableFileMerger', None) is None:
74+ raise ImportError(
75+ 'need at least bzr 2.1.0rc2 (you use %r)', bzrlib.version_info)
76+else:
77+ def changelog_merge_hook_factory(merger):
78+ from bzrlib.plugins.builddeb import merge_changelog
79+ return merge_changelog.ChangeLogFileMerge(merger)
80+
81+ merge.Merger.hooks.install_named_hook(
82+ 'merge_file_content', changelog_merge_hook_factory,
83+ 'Debian Changelog file merge')
84+
85+
86 commands = {
87- "test_builddeb": [],
88+ "bd_do": [],
89 "builddeb": ["bd"],
90+ "dh_make": ["dh_make"],
91+ "import_dsc": [],
92+ "import_upstream": [],
93+ "mark_uploaded": [],
94+ "merge_package": [],
95 "merge_upstream": ["mu"],
96- "import_dsc": [],
97- "bd_do": [],
98- "mark_uploaded": []
99 }
100
101 for command, aliases in commands.iteritems():
102@@ -48,7 +71,8 @@
103
104 builddeb_dir = '.bzr-builddeb'
105 default_conf = os.path.join(builddeb_dir, 'default.conf')
106-global_conf = os.path.expanduser('~/.bazaar/builddeb.conf')
107+def global_conf():
108+ return os.path.join(config_dir(), 'builddeb.conf')
109 local_conf = os.path.join(builddeb_dir, 'local.conf')
110
111 default_build_dir = '../build-area'
112@@ -94,7 +118,9 @@
113 for group in sequencematcher(None, old_text,
114 new_text).get_grouped_opcodes(0):
115 j1, j2 = group[0][3], group[-1][4]
116- changes += new_text[j1:j2]
117+ for line in new_text[j1:j2]:
118+ if line.startswith(" "):
119+ changes.append(line)
120 if not changes:
121 return start_message
122 from bzrlib.plugins.builddeb.util import strip_changelog_message
123@@ -108,26 +134,55 @@
124 "the commit message")
125
126
127+def debian_tag_name(branch, revid):
128+ from bzrlib.plugins.builddeb.config import BUILD_TYPE_MERGE
129+ from bzrlib.plugins.builddeb.errors import MissingChangelogError
130+ from bzrlib.plugins.builddeb.import_dsc import (DistributionBranch,
131+ DistributionBranchSet)
132+ from bzrlib.plugins.builddeb.util import (debuild_config, find_changelog)
133+ t = branch.repository.revision_tree(revid)
134+ config = debuild_config(t, False)
135+ try:
136+ (changelog, larstiq) = find_changelog(t, config.build_type == BUILD_TYPE_MERGE)
137+ except MissingChangelogError:
138+ # Not a debian package
139+ return None
140+ if changelog.distributions == 'UNRELEASED':
141+ # The changelog still targets 'UNRELEASED', so apparently hasn't been
142+ # uploaded. XXX: Give a warning of some sort here?
143+ return None
144+ db = DistributionBranch(branch, None)
145+ dbs = DistributionBranchSet()
146+ dbs.add_branch(db)
147+ return db.tag_name(changelog.version)
148+
149+
150+try:
151+ _mod_branch.Branch.hooks.install_named_hook("automatic_tag_name",
152+ debian_tag_name,
153+ "Automatically determine tag names from Debian version")
154+except errors.UnknownHook:
155+ pass # bzr < 2.2 doesn't have this hook.
156+
157+
158 try:
159 from bzrlib.revisionspec import revspec_registry
160- revspec_registry.register_lazy("package:", "bzrlib.plugins.builddeb.revspec", "RevisionSpec_package")
161+ revspec_registry.register_lazy("package:",
162+ "bzrlib.plugins.builddeb.revspec", "RevisionSpec_package")
163 except ImportError:
164 from bzrlib.revisionspec import SPEC_TYPES
165 from bzrlib.plugins.builddeb.revspec import RevisionSpec_package
166 SPEC_TYPES.append(RevisionSpec_package)
167
168-
169-def test_suite():
170- from unittest import TestSuite
171- from bzrlib.plugins.builddeb import tests
172- result = TestSuite()
173- result.addTest(tests.test_suite())
174- return result
175-
176-
177-if __name__ == '__main__':
178- print ("This is a Bazaar plugin. Copy this directory to ~/.bazaar/plugins "
179- "to use it.\n")
180- import unittest
181- runner = unittest.TextTestRunner()
182- runner.run(test_suite())
183+try:
184+ from bzrlib.tag import tag_sort_methods
185+except ImportError:
186+ pass # bzr tags --sort= can not be extended
187+else:
188+ tag_sort_methods.register_lazy("debversion",
189+ "bzrlib.plugins.builddeb.tagging", "sort_debversion",
190+ "Sort like Debian versions.")
191+
192+
193+def load_tests(standard_tests, module, loader):
194+ return loader.loadTestsFromModuleNames(['bzrlib.plugins.builddeb.tests'])
195
196=== modified file 'builder.py'
197--- builder.py 2009-07-15 20:48:37 +0000
198+++ builder.py 2011-01-28 14:24:44 +0000
199@@ -19,11 +19,10 @@
200 #
201
202 import shutil
203-import signal
204 import subprocess
205 import os
206
207-from bzrlib.trace import info
208+from bzrlib.trace import note
209
210 from bzrlib.plugins.builddeb.errors import (
211 NoSourceDirError,
212@@ -31,16 +30,10 @@
213 )
214 from bzrlib.plugins.builddeb.util import (
215 get_parent_dir,
216+ subprocess_setup,
217 )
218
219
220-def subprocess_setup():
221- # Python installs a SIGPIPE handler by default. This is usually not what
222- # non-Python subprocesses expect.
223- # Many, many thanks to Colin Watson
224- signal.signal(signal.SIGPIPE, signal.SIG_DFL)
225-
226-
227 class DebBuild(object):
228 """The object that does the building work."""
229
230@@ -69,10 +62,10 @@
231 os.makedirs(parent_dir)
232 if os.path.exists(self.target_dir):
233 if not self.use_existing:
234- info("Purging the build dir: %s", self.target_dir)
235+ note("Purging the build dir: %s", self.target_dir)
236 shutil.rmtree(self.target_dir)
237 else:
238- info("Not purging build dir as requested: %s",
239+ note("Not purging build dir as requested: %s",
240 self.target_dir)
241 else:
242 if self.use_existing:
243@@ -83,7 +76,7 @@
244
245 def build(self):
246 """This builds the package using the supplied command."""
247- info("Building the package in %s, using %s", self.target_dir,
248+ note("Building the package in %s, using %s", self.target_dir,
249 self.builder)
250 proc = subprocess.Popen(self.builder, shell=True, cwd=self.target_dir,
251 preexec_fn=subprocess_setup)
252@@ -93,5 +86,5 @@
253
254 def clean(self):
255 """This removes the build directory."""
256- info("Cleaning build dir: %s", self.target_dir)
257+ note("Cleaning build dir: %s", self.target_dir)
258 shutil.rmtree(self.target_dir)
259
260=== added file 'bzrtools_bzrtools.py'
261--- bzrtools_bzrtools.py 1970-01-01 00:00:00 +0000
262+++ bzrtools_bzrtools.py 2011-01-28 14:24:44 +0000
263@@ -0,0 +1,30 @@
264+# This file is a small part of bzrtools' own bzrtools.py
265+# The parts copied last changed in bzrtools 1.13.0.
266+
267+# Copyright (C) 2005, 2006, 2007 Aaron Bentley <aaron@aaronbentley.com>
268+# Copyright (C) 2007 John Arbash Meinel
269+#
270+# This program is free software; you can redistribute it and/or modify
271+# it under the terms of the GNU General Public License as published by
272+# the Free Software Foundation; either version 2 of the License, or
273+# (at your option) any later version.
274+#
275+# This program is distributed in the hope that it will be useful,
276+# but WITHOUT ANY WARRANTY; without even the implied warranty of
277+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
278+# GNU General Public License for more details.
279+#
280+# You should have received a copy of the GNU General Public License
281+# along with this program; if not, write to the Free Software
282+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
283+
284+from bzrlib import urlutils
285+from bzrlib.transport import get_transport
286+
287+
288+def open_from_url(location):
289+ location = urlutils.normalize_url(location)
290+ dirname, basename = urlutils.split(location)
291+ if location.endswith('/') and not basename.endswith('/'):
292+ basename += '/'
293+ return get_transport(dirname).get(basename)
294
295=== added file 'bzrtools_import.py'
296--- bzrtools_import.py 1970-01-01 00:00:00 +0000
297+++ bzrtools_import.py 2011-01-28 14:24:44 +0000
298@@ -0,0 +1,409 @@
299+# This file is a modified copy of bzrtools' upstream_import.py, last changed in
300+# bzrtools 1.14.0.
301+
302+"""Import upstream source into a branch"""
303+
304+import errno
305+import os
306+from StringIO import StringIO
307+import stat
308+import tarfile
309+import zipfile
310+
311+from bzrlib import generate_ids
312+from bzrlib.bzrdir import BzrDir
313+from bzrlib.errors import NoSuchFile, BzrCommandError, NotBranchError
314+from bzrlib.osutils import (pathjoin, isdir, file_iterator, basename,
315+ file_kind, splitpath, normpath)
316+from bzrlib.trace import warning
317+from bzrlib.transform import TreeTransform, resolve_conflicts, cook_conflicts
318+from bzrlib.workingtree import WorkingTree
319+from bzrlib.plugins.builddeb.bzrtools_bzrtools import open_from_url
320+from bzrlib.plugins.builddeb.errors import UnknownType
321+
322+class ZipFileWrapper(object):
323+
324+ def __init__(self, fileobj, mode):
325+ self.zipfile = zipfile.ZipFile(fileobj, mode)
326+
327+ def getmembers(self):
328+ for info in self.zipfile.infolist():
329+ yield ZipInfoWrapper(self.zipfile, info)
330+
331+ def extractfile(self, infowrapper):
332+ return StringIO(self.zipfile.read(infowrapper.name))
333+
334+ def add(self, filename):
335+ if isdir(filename):
336+ self.zipfile.writestr(filename+'/', '')
337+ else:
338+ self.zipfile.write(filename)
339+
340+ def close(self):
341+ self.zipfile.close()
342+
343+
344+class ZipInfoWrapper(object):
345+
346+ def __init__(self, zipfile, info):
347+ self.info = info
348+ self.type = None
349+ self.name = info.filename
350+ self.zipfile = zipfile
351+ self.mode = 0666
352+
353+ def isdir(self):
354+ # Really? Eeeew!
355+ return bool(self.name.endswith('/'))
356+
357+ def isreg(self):
358+ # Really? Eeeew!
359+ return not self.isdir()
360+
361+
362+files_to_ignore = set(
363+ ['.shelf', '.bzr', '.bzr.backup', '.bzrtags',
364+ '.bzr-builddeb'])
365+
366+
367+class DirWrapper(object):
368+ def __init__(self, fileobj, mode='r'):
369+ assert mode == 'r', mode
370+ self.root = os.path.realpath(fileobj.read())
371+
372+ def __repr__(self):
373+ return 'DirWrapper(%r)' % self.root
374+
375+ def getmembers(self, subdir=None):
376+ if subdir is not None:
377+ mydir = pathjoin(self.root, subdir)
378+ else:
379+ mydir = self.root
380+ for child in os.listdir(mydir):
381+ if subdir is not None:
382+ child = pathjoin(subdir, child)
383+ fi = FileInfo(self.root, child)
384+ yield fi
385+ if fi.isdir():
386+ for v in self.getmembers(child):
387+ yield v
388+
389+ def extractfile(self, member):
390+ return open(member.fullpath)
391+
392+
393+class FileInfo(object):
394+
395+ def __init__(self, root, filepath):
396+ self.fullpath = pathjoin(root, filepath)
397+ self.root = root
398+ if filepath != '':
399+ self.name = pathjoin(basename(root), filepath)
400+ else:
401+ self.name = basename(root)
402+ self.type = None
403+ stat = os.lstat(self.fullpath)
404+ self.mode = stat.st_mode
405+ if self.isdir():
406+ self.name += '/'
407+
408+ def __repr__(self):
409+ return 'FileInfo(%r)' % self.name
410+
411+ def isreg(self):
412+ return stat.S_ISREG(self.mode)
413+
414+ def isdir(self):
415+ return stat.S_ISDIR(self.mode)
416+
417+ def issym(self):
418+ if stat.S_ISLNK(self.mode):
419+ self.linkname = os.readlink(self.fullpath)
420+ return True
421+ else:
422+ return False
423+
424+
425+def top_path(path):
426+ """Return the top directory given in a path."""
427+ components = splitpath(normpath(path))
428+ if len(components) > 0:
429+ return components[0]
430+ else:
431+ return ''
432+
433+
434+def common_directory(names):
435+ """Determine a single directory prefix from a list of names"""
436+ prefixes = set()
437+ prefixes.update(map(top_path, names))
438+ if '' in prefixes:
439+ prefixes.remove('')
440+ if len(prefixes) != 1:
441+ return None
442+ prefix = prefixes.pop()
443+ if prefix == '':
444+ return None
445+ return prefix
446+
447+
448+def do_directory(tt, trans_id, tree, relative_path, path):
449+ if isdir(path) and tree.path2id(relative_path) is not None:
450+ tt.cancel_deletion(trans_id)
451+ else:
452+ tt.create_directory(trans_id)
453+
454+
455+def add_implied_parents(implied_parents, path):
456+ """Update the set of implied parents from a path"""
457+ parent = os.path.dirname(path)
458+ if parent in implied_parents:
459+ return
460+ implied_parents.add(parent)
461+ add_implied_parents(implied_parents, parent)
462+
463+
464+def names_of_files(tar_file):
465+ for member in tar_file.getmembers():
466+ if member.type != "g":
467+ yield member.name
468+
469+
470+def should_ignore(relative_path):
471+ parts = splitpath(relative_path)
472+ if not parts:
473+ return False
474+ for part in parts:
475+ if part in files_to_ignore:
476+ return True
477+ if part.endswith(',v'):
478+ return True
479+
480+
481+def import_tar(tree, tar_input, file_ids_from=None, target_tree=None):
482+ """Replace the contents of a working directory with tarfile contents.
483+ The tarfile may be a gzipped stream. File ids will be updated.
484+ """
485+ tar_file = tarfile.open('lala', 'r', tar_input)
486+ import_archive(tree, tar_file, file_ids_from=file_ids_from,
487+ target_tree=target_tree)
488+
489+def import_zip(tree, zip_input, file_ids_from=None, target_tree=None):
490+ zip_file = ZipFileWrapper(zip_input, 'r')
491+ import_archive(tree, zip_file, file_ids_from=file_ids_from,
492+ target_tree=target_tree)
493+
494+def import_dir(tree, dir, file_ids_from=None, target_tree=None):
495+ dir_input = StringIO(dir)
496+ dir_file = DirWrapper(dir_input)
497+ import_archive(tree, dir_file, file_ids_from=file_ids_from,
498+ target_tree=target_tree)
499+
500+def import_archive(tree, archive_file, file_ids_from=None, target_tree=None):
501+ if file_ids_from is None:
502+ file_ids_from = []
503+ for other_tree in file_ids_from:
504+ other_tree.lock_read()
505+ try:
506+ return _import_archive(tree, archive_file, file_ids_from,
507+ target_tree=target_tree)
508+ finally:
509+ for other_tree in file_ids_from:
510+ other_tree.unlock()
511+
512+def _get_paths_to_process(archive_file, prefix, implied_parents):
513+ to_process = set()
514+ for member in archive_file.getmembers():
515+ if member.type == 'g':
516+ # type 'g' is a header
517+ continue
518+ relative_path = member.name
519+ relative_path = normpath(relative_path)
520+ relative_path = relative_path.lstrip('/')
521+ if prefix is not None:
522+ relative_path = relative_path[len(prefix)+1:]
523+ relative_path = relative_path.rstrip('/')
524+ if relative_path == '' or relative_path == '.':
525+ continue
526+ if should_ignore(relative_path):
527+ continue
528+ add_implied_parents(implied_parents, relative_path)
529+ to_process.add((relative_path, member))
530+ return to_process
531+
532+
533+def _import_archive(tree, archive_file, file_ids_from, target_tree=None):
534+ prefix = common_directory(names_of_files(archive_file))
535+ tt = TreeTransform(tree)
536+ try:
537+ removed = set()
538+ for path, entry in tree.inventory.iter_entries():
539+ if entry.parent_id is None:
540+ continue
541+ trans_id = tt.trans_id_tree_path(path)
542+ tt.delete_contents(trans_id)
543+ removed.add(path)
544+
545+ added = set()
546+ implied_parents = set()
547+ seen = set()
548+ to_process = _get_paths_to_process(archive_file, prefix,
549+ implied_parents)
550+ renames = {}
551+
552+ # First we find the renames
553+ other_trees = file_ids_from[:]
554+ if target_tree is not None:
555+ other_trees.insert(0, target_tree)
556+ for other_tree in other_trees:
557+ for relative_path, member in to_process:
558+ trans_id = tt.trans_id_tree_path(relative_path)
559+ existing_file_id = tt.tree_file_id(trans_id)
560+ target_id = other_tree.path2id(relative_path)
561+ if (target_id is not None
562+ and target_id != existing_file_id
563+ and target_id not in renames):
564+ renames[target_id] = relative_path
565+
566+ # The we do the work
567+ for relative_path, member in to_process:
568+ trans_id = tt.trans_id_tree_path(relative_path)
569+ added.add(relative_path.rstrip('/'))
570+ # To handle renames, we need to not use the preserved file id, rather
571+ # we need to lookup the file id in target_tree, if there is one. If
572+ # there isn't, we should use the one in the current tree, and failing
573+ # that we will allocate one. In this importer we want the
574+ # target_tree to be authoritative about id2path, which is why we
575+ # consult it first.
576+ existing_file_id = tt.tree_file_id(trans_id)
577+ # If we find an id that we know we are going to assign to
578+ # different path as it has been renamed in one of the
579+ # file_ids_from trees then we ignore the one in this tree.
580+ if existing_file_id in renames:
581+ if relative_path != renames[existing_file_id]:
582+ existing_file_id = None
583+ found_file_id = None
584+ if target_tree is not None:
585+ found_file_id = target_tree.path2id(relative_path)
586+ if found_file_id in renames:
587+ if renames[found_file_id] != relative_path:
588+ found_file_id = None
589+ if found_file_id is None and existing_file_id is None:
590+ for other_tree in file_ids_from:
591+ found_file_id = other_tree.path2id(relative_path)
592+ if found_file_id is not None:
593+ if found_file_id in renames:
594+ if renames[found_file_id] != relative_path:
595+ found_file_id = None
596+ continue
597+ break
598+ if (found_file_id is not None
599+ and found_file_id != existing_file_id):
600+ # Found a specific file id in one of the source trees
601+ tt.version_file(found_file_id, trans_id)
602+ if existing_file_id is not None:
603+ # We need to remove the existing file so it can be
604+ # replaced by the file (and file id) from the
605+ # file_ids_from tree.
606+ tt.delete_versioned(trans_id)
607+ trans_id = tt.trans_id_file_id(found_file_id)
608+
609+ if not found_file_id and not existing_file_id:
610+ # No file_id in any of the source trees and no file id in the base
611+ # tree.
612+ name = basename(member.name.rstrip('/'))
613+ file_id = generate_ids.gen_file_id(name)
614+ tt.version_file(file_id, trans_id)
615+ path = tree.abspath(relative_path)
616+ if member.name in seen:
617+ if tt.final_kind(trans_id) == 'file':
618+ tt.set_executability(None, trans_id)
619+ tt.cancel_creation(trans_id)
620+ seen.add(member.name)
621+ if member.isreg():
622+ tt.create_file(file_iterator(archive_file.extractfile(member)),
623+ trans_id)
624+ executable = (member.mode & 0111) != 0
625+ tt.set_executability(executable, trans_id)
626+ elif member.isdir():
627+ do_directory(tt, trans_id, tree, relative_path, path)
628+ elif member.issym():
629+ tt.create_symlink(member.linkname, trans_id)
630+ else:
631+ raise UnknownType(relative_path)
632+
633+ for relative_path in implied_parents.difference(added):
634+ if relative_path == "":
635+ continue
636+ trans_id = tt.trans_id_tree_path(relative_path)
637+ path = tree.abspath(relative_path)
638+ do_directory(tt, trans_id, tree, relative_path, path)
639+ if tt.tree_file_id(trans_id) is None:
640+ found = False
641+ for other_tree in file_ids_from:
642+ other_tree.lock_read()
643+ try:
644+ if other_tree.has_filename(relative_path):
645+ file_id = other_tree.path2id(relative_path)
646+ if file_id is not None:
647+ tt.version_file(file_id, trans_id)
648+ found = True
649+ break
650+ finally:
651+ other_tree.unlock()
652+ if not found:
653+ # Should this really use the trans_id as the
654+ # file_id?
655+ tt.version_file(trans_id, trans_id)
656+ added.add(relative_path)
657+
658+ for path in removed.difference(added):
659+ tt.unversion_file(tt.trans_id_tree_path(path))
660+
661+ for conflict in cook_conflicts(resolve_conflicts(tt), tt):
662+ warning(conflict)
663+ tt.apply()
664+ finally:
665+ tt.finalize()
666+
667+
668+def do_import(source, tree_directory=None):
669+ """Implementation of import command. Intended for UI only"""
670+ if tree_directory is not None:
671+ try:
672+ tree = WorkingTree.open(tree_directory)
673+ except NotBranchError:
674+ if not os.path.exists(tree_directory):
675+ os.mkdir(tree_directory)
676+ branch = BzrDir.create_branch_convenience(tree_directory)
677+ tree = branch.bzrdir.open_workingtree()
678+ else:
679+ tree = WorkingTree.open_containing('.')[0]
680+ tree.lock_write()
681+ try:
682+ if tree.changes_from(tree.basis_tree()).has_changed():
683+ raise BzrCommandError("Working tree has uncommitted changes.")
684+
685+ if (source.endswith('.tar') or source.endswith('.tar.gz') or
686+ source.endswith('.tar.bz2')) or source.endswith('.tgz'):
687+ try:
688+ tar_input = open_from_url(source)
689+ if source.endswith('.bz2'):
690+ tar_input = StringIO(tar_input.read().decode('bz2'))
691+ except IOError, e:
692+ if e.errno == errno.ENOENT:
693+ raise NoSuchFile(source)
694+ try:
695+ import_tar(tree, tar_input)
696+ finally:
697+ tar_input.close()
698+ elif source.endswith('.zip'):
699+ import_zip(tree, open_from_url(source))
700+ elif file_kind(source) == 'directory':
701+ s = StringIO(source)
702+ s.seek(0)
703+ import_dir(tree, s)
704+ else:
705+ raise BzrCommandError('Unhandled import source')
706+ finally:
707+ tree.unlock()
708
709=== modified file 'changes.py'
710--- changes.py 2009-03-16 15:52:52 +0000
711+++ changes.py 2011-01-28 14:24:44 +0000
712@@ -21,7 +21,11 @@
713 import commands
714 import os
715
716-from debian_bundle import deb822
717+try:
718+ from debian import deb822
719+except ImportError:
720+ # Prior to 0.1.15 the debian module was called debian_bundle
721+ from debian_bundle import deb822
722
723 from bzrlib.trace import mutter
724
725@@ -37,15 +41,15 @@
726 >>> c = DebianChanges('bzr-builddeb', '0.1-1', file_dir, 'i386')
727 >>> fs = c.files()
728 >>> f = fs[0]
729- >>> f['name']
730+ >>> str(f['name'])
731 'bzr-builddeb_0.1-1.dsc'
732- >>> f['priority']
733+ >>> str(f['priority'])
734 'optional'
735- >>> f['section']
736+ >>> str(f['section'])
737 'devel'
738- >>> f['size']
739+ >>> str(f['size'])
740 '290'
741- >>> f['md5sum']
742+ >>> str(f['md5sum'])
743 'b4c9b646c741f531dd8349db83c77cae'
744 """
745 if arch is None:
746
747=== modified file 'cmds.py'
748--- cmds.py 2009-07-26 18:21:49 +0000
749+++ cmds.py 2011-01-28 14:24:44 +0000
750@@ -28,7 +28,11 @@
751 import tempfile
752 import urlparse
753
754-from debian_bundle.changelog import Version
755+try:
756+ from debian.changelog import Version
757+except ImportError:
758+ # Prior to 0.1.15 the debian module was called debian_bundle
759+ from debian_bundle.changelog import Version
760
761 from bzrlib import (
762 urlutils,
763@@ -36,32 +40,37 @@
764 from bzrlib.branch import Branch
765 from bzrlib.bzrdir import BzrDir
766 from bzrlib.commands import Command
767-from bzrlib.errors import (BzrCommandError,
768- NoWorkingTree,
769- NotBranchError,
770- FileExists,
771- )
772-from bzrlib.export import export
773+from bzrlib.errors import (
774+ BzrCommandError,
775+ FileExists,
776+ NotBranchError,
777+ NoWorkingTree,
778+ )
779 from bzrlib.option import Option
780 from bzrlib.revisionspec import RevisionSpec
781-from bzrlib.trace import info, warning
782-from bzrlib.transport import get_transport
783+from bzrlib.tag import _merge_tags_if_possible
784+from bzrlib.trace import note, warning
785 from bzrlib.workingtree import WorkingTree
786
787 from bzrlib.plugins.builddeb import (
788 default_build_dir,
789 default_orig_dir,
790 default_result_dir,
791- default_conf,
792- local_conf,
793- global_conf,
794- test_suite,
795+ dh_make,
796 )
797 from bzrlib.plugins.builddeb.builder import (
798 DebBuild,
799 )
800-from bzrlib.plugins.builddeb.config import DebBuildConfig
801-from bzrlib.plugins.builddeb.errors import BuildFailedError
802+from bzrlib.plugins.builddeb.config import (
803+ BUILD_TYPE_MERGE,
804+ BUILD_TYPE_NATIVE,
805+ BUILD_TYPE_NORMAL,
806+ BUILD_TYPE_SPLIT,
807+ )
808+from bzrlib.plugins.builddeb.errors import (
809+ BuildFailedError,
810+ NoPreviousUpload,
811+ )
812 from bzrlib.plugins.builddeb.hooks import run_hook
813 from bzrlib.plugins.builddeb.import_dsc import (
814 DistributionBranch,
815@@ -69,23 +78,38 @@
816 DscCache,
817 DscComp,
818 )
819+from bzrlib.plugins.builddeb.merge_package import fix_ancestry_as_needed
820 from bzrlib.plugins.builddeb.source_distiller import (
821 FullSourceDistiller,
822 MergeModeDistiller,
823 NativeSourceDistiller,
824 )
825+from bzrlib.plugins.builddeb.tagging import (
826+ is_upstream_tag,
827+ upstream_tag_version,
828+ )
829 from bzrlib.plugins.builddeb.upstream import (
830+ AptSource,
831+ GetOrigSourceSource,
832+ PristineTarSource,
833+ SelfSplitSource,
834+ UScanSource,
835 UpstreamProvider,
836 UpstreamBranchSource,
837- get_upstream_sources,
838 )
839-from bzrlib.plugins.builddeb.util import (find_changelog,
840+from bzrlib.plugins.builddeb.util import (
841+ debuild_config,
842+ dget_changes,
843+ find_changelog,
844+ find_last_distribution,
845+ find_previous_upload,
846 get_export_upstream_revision,
847- find_last_distribution,
848+ guess_build_type,
849 lookup_distribution,
850- suite_to_distribution,
851+ open_file,
852+ open_file_via_transport,
853 tarball_name,
854- dget_changes,
855+ tree_contains_upstream_source,
856 )
857
858 dont_purge_opt = Option('dont-purge',
859@@ -111,36 +135,6 @@
860 export_upstream_revision_opt = Option('export-upstream-revision',
861 help="Select the upstream revision that will be exported.",
862 type=str)
863-no_user_conf_opt = Option('no-user-config',
864- help="Stop builddeb from reading the user's config file. Used mainly "
865- "for tests.")
866-
867-
868-def debuild_config(tree, working_tree, no_user_config):
869- """Obtain the Debuild configuration object.
870-
871- :param tree: A Tree object, can be a WorkingTree or RevisionTree.
872- :param working_tree: Whether the tree is a working tree.
873- :param no_user_config: Whether to skip the user configuration
874- """
875- config_files = []
876- user_config = None
877- if (working_tree and tree.has_filename(local_conf)):
878- if tree.path2id(local_conf) is None:
879- config_files.append((tree.get_file_byname(local_conf), True,
880- "local.conf"))
881- else:
882- warning('Not using configuration from %s as it is versioned.')
883- if not no_user_config:
884- config_files.append((global_conf, True))
885- user_config = global_conf
886- if tree.path2id(default_conf):
887- config_files.append((tree.get_file(tree.path2id(default_conf)), False,
888- "default.conf"))
889- config = DebBuildConfig(config_files, tree=tree)
890- config.set_user_config(user_config)
891- return config
892-
893
894 class cmd_builddeb(Command):
895 """Builds a Debian package from a branch.
896@@ -200,6 +194,9 @@
897 short_name='S')
898 result_compat_opt = Option('result', help="Present only for compatibility "
899 "with bzr-builddeb <= 2.0. Use --result-dir instead.")
900+ package_merge_opt = Option('package-merge', help="Build using the "
901+ "appropriate -v and -sa options for merging in the changes from "
902+ "another source.")
903 takes_args = ['branch_or_build_options*']
904 aliases = ['bd']
905 takes_options = [working_tree_opt, export_only_opt,
906@@ -208,20 +205,18 @@
907 export_upstream_opt, export_upstream_revision_opt,
908 quick_opt, reuse_opt, native_opt,
909 source_opt, 'revision',
910- no_user_conf_opt, result_compat_opt]
911+ result_compat_opt, package_merge_opt]
912
913 def _get_tree_and_branch(self, location):
914 if location is None:
915 location = "."
916 is_local = urlparse.urlsplit(location)[0] in ('', 'file')
917- if is_local:
918- os.chdir(location)
919 tree, branch, relpath = BzrDir.open_containing_tree_or_branch(location)
920 return tree, branch, is_local
921
922 def _get_build_tree(self, revision, tree, branch):
923 if revision is None and tree is not None:
924- info("Building using working tree")
925+ note("Building using working tree")
926 working_tree = True
927 else:
928 if revision is None:
929@@ -231,30 +226,19 @@
930 else:
931 raise BzrCommandError('bzr builddeb --revision takes exactly one '
932 'revision specifier.')
933- info("Building branch from revision %s", revid)
934+ note("Building branch from revision %s", revid)
935 tree = branch.repository.revision_tree(revid)
936 working_tree = False
937 return tree, working_tree
938
939- def _build_type(self, config, merge, native, split):
940- if not merge:
941- merge = config.merge
942+ def _build_type(self, merge, native, split):
943 if merge:
944- info("Running in merge mode")
945- native = False
946- split = False
947- else:
948- if not native:
949- native = config.native
950- if native:
951- info("Running in native mode")
952- split = False
953- else:
954- if not split:
955- split = config.split
956- if split:
957- info("Running in split mode")
958- return merge, native, split
959+ return BUILD_TYPE_MERGE
960+ if native:
961+ return BUILD_TYPE_NATIVE
962+ if split:
963+ return BUILD_TYPE_SPLIT
964+ return None
965
966 def _get_build_command(self, config, builder, quick, build_options):
967 if builder is None:
968@@ -270,26 +254,26 @@
969 builder += " " + " ".join(build_options)
970 return builder
971
972- def _get_dirs(self, config, is_local, result_dir, result, build_dir, orig_dir):
973- if result_dir is None:
974- result_dir = result
975- if result_dir is None:
976- if is_local:
977- result_dir = config.result_dir
978- else:
979- result_dir = config.user_result_dir
980- if result_dir is not None:
981- result_dir = os.path.realpath(result_dir)
982- if build_dir is None:
983- if is_local:
984- build_dir = config.build_dir or default_build_dir
985- else:
986- build_dir = config.user_build_dir or 'build-area'
987- if orig_dir is None:
988- if is_local:
989- orig_dir = config.orig_dir or default_orig_dir
990- else:
991- orig_dir = config.user_orig_dir or 'build-area'
992+ def _get_dirs(self, config, branch, is_local, result_dir, build_dir, orig_dir):
993+ def _get_dir(supplied, if_local, if_not):
994+ if supplied is None:
995+ if is_local:
996+ supplied = if_local
997+ else:
998+ supplied = if_not
999+ if supplied is not None:
1000+ if is_local:
1001+ supplied = os.path.join(
1002+ urlutils.local_path_from_url(branch.base),
1003+ supplied)
1004+ supplied = os.path.realpath(supplied)
1005+ return supplied
1006+
1007+ result_dir = _get_dir(result_dir, config.result_dir, config.user_result_dir)
1008+ build_dir = _get_dir(build_dir, config.build_dir or default_build_dir,
1009+ config.user_build_dir or 'build-area')
1010+ orig_dir = _get_dir(orig_dir, config.orig_dir or default_orig_dir,
1011+ config.user_orig_dir or 'build-area')
1012 return result_dir, build_dir, orig_dir
1013
1014 def _branch_and_build_options(self, branch_or_build_options_list,
1015@@ -341,45 +325,81 @@
1016 def run(self, branch_or_build_options_list=None, verbose=False,
1017 working_tree=False,
1018 export_only=False, dont_purge=False, use_existing=False,
1019- result_dir=None, builder=None, merge=False, build_dir=None,
1020+ result_dir=None, builder=None, merge=None, build_dir=None,
1021 export_upstream=None, export_upstream_revision=None,
1022 orig_dir=None, split=None,
1023- quick=False, reuse=False, native=False,
1024- source=False, revision=None, no_user_config=False, result=None):
1025+ quick=False, reuse=False, native=None,
1026+ source=False, revision=None, result=None, package_merge=None):
1027 if result is not None:
1028 warning("--result is deprected, use --result-dir instead")
1029 branch, build_options, source = self._branch_and_build_options(
1030 branch_or_build_options_list, source)
1031 tree, branch, is_local = self._get_tree_and_branch(branch)
1032 tree, working_tree = self._get_build_tree(revision, tree, branch)
1033+
1034+ if len(tree.conflicts()) > 0:
1035+ raise BzrCommandError(
1036+ "There are conflicts in the working tree. "
1037+ "You must resolve these before building.")
1038+
1039 tree.lock_read()
1040 try:
1041- config = debuild_config(tree, working_tree, no_user_config)
1042+ config = debuild_config(tree, working_tree)
1043 if reuse:
1044- info("Reusing existing build dir")
1045+ note("Reusing existing build dir")
1046 dont_purge = True
1047 use_existing = True
1048- merge, native, split = self._build_type(config, merge, native, split)
1049- if (not merge and not native and not split and
1050- tree.inventory.root.children.keys() == ["debian"]):
1051- # Default to merge mode if there's only a debian/ directory
1052- merge = True
1053+ build_type = self._build_type(merge, native, split)
1054+ if build_type is None:
1055+ build_type = config.build_type
1056+ contains_upstream_source = tree_contains_upstream_source(tree)
1057+ (changelog, larstiq) = find_changelog(tree, not contains_upstream_source)
1058+ try:
1059+ prev_version = find_previous_upload(tree, not contains_upstream_source)
1060+ except NoPreviousUpload:
1061+ prev_version = None
1062+ if build_type is None:
1063+ build_type = guess_build_type(tree, changelog.version,
1064+ contains_upstream_source)
1065+
1066+ note("Building package in %s mode" % build_type)
1067+
1068+ if package_merge:
1069+ build_options.append("-v%s" % str(prev_version))
1070+ if (prev_version.upstream_version
1071+ != changelog.version.upstream_version
1072+ or prev_version.epoch != changelog.version.epoch):
1073+ build_options.append("-sa")
1074 build_cmd = self._get_build_command(config, builder, quick,
1075 build_options)
1076- (changelog, larstiq) = find_changelog(tree, merge)
1077- result_dir, build_dir, orig_dir = self._get_dirs(config, is_local,
1078- result_dir, result, build_dir, orig_dir)
1079-
1080- upstream_branch, upstream_revision = \
1081- self._get_upstream_branch(merge, export_upstream,
1082- export_upstream_revision, config,
1083- changelog.version)
1084-
1085- upstream_provider = UpstreamProvider(
1086- changelog.package, changelog.version,
1087- orig_dir, get_upstream_sources(tree, branch,
1088- larstiq=larstiq, upstream_branch=upstream_branch,
1089- upstream_revision=upstream_revision, allow_split=split))
1090+ result_dir, build_dir, orig_dir = self._get_dirs(config, branch,
1091+ is_local, result_dir or result, build_dir, orig_dir)
1092+
1093+ upstream_sources = [
1094+ PristineTarSource(tree, branch),
1095+ AptSource(),
1096+ ]
1097+ if merge:
1098+ upstream_branch, upstream_revision = self._get_upstream_branch(
1099+ merge, export_upstream, export_upstream_revision, config,
1100+ changelog.version)
1101+ if upstream_branch is not None:
1102+ upstream_sources.append(UpstreamBranchSource(
1103+ upstream_branch,
1104+ {changelog.version.upstream_version:
1105+ upstream_revision}))
1106+ elif not native and config.upstream_branch:
1107+ upstream_branch = Branch.open(config.upstream_branch)
1108+ upstream_sources.append(UpstreamBranchSource(upstream_branch))
1109+ upstream_sources.extend([
1110+ GetOrigSourceSource(tree, larstiq),
1111+ UScanSource(tree, larstiq),
1112+ ])
1113+ if split:
1114+ upstream_sources.append(SelfSplitSource(tree))
1115+
1116+ upstream_provider = UpstreamProvider(changelog.package,
1117+ changelog.version, orig_dir, upstream_sources)
1118
1119 if merge:
1120 distiller_cls = MergeModeDistiller
1121@@ -414,15 +434,24 @@
1122 'dpkg-architecture -qDEB_BUILD_ARCH')
1123 if status > 0:
1124 raise BzrCommandError("Could not find the build architecture")
1125+ non_epoch_version = changelog.version.upstream_version
1126+ if changelog.version.debian_version is not None:
1127+ non_epoch_version += "-%s" % changelog.version.debian_version
1128 changes = "%s_%s_%s.changes" % (changelog.package,
1129- str(changelog.version), arch)
1130+ non_epoch_version, arch)
1131 changes_path = os.path.join(build_dir, changes)
1132 if not os.path.exists(changes_path):
1133 if result_dir is not None:
1134 raise BzrCommandError("Could not find the .changes "
1135 "file from the build: %s" % changes_path)
1136 else:
1137- target_dir = result_dir or default_result_dir
1138+ if is_local:
1139+ target_dir = result_dir or default_result_dir
1140+ target_dir = os.path.join(
1141+ urlutils.local_path_from_url(branch.base),
1142+ target_dir)
1143+ else:
1144+ target_dir = "."
1145 if not os.path.exists(target_dir):
1146 os.makedirs(target_dir)
1147 dget_changes(changes_path, target_dir)
1148@@ -438,59 +467,225 @@
1149
1150 You must supply the source to import from, and the version number of the
1151 new release. The source can be a .tar.gz, .tar, .tar.bz2, .tgz or .zip
1152- archive, or a directory. The source may also be a remote file.
1153+ archive, or a directory. The source may also be a remote file described by
1154+ a URL.
1155
1156 You must supply the version number of the new upstream release
1157- using --version.
1158+ using --version, unless you're importing from an upstream branch, in which
1159+ case it can be guessed from that.
1160
1161 The distribution this version is targetted at can be specified with
1162- --distribution. This will be used to guess the version number, so you
1163- can always correct it in the changelog.
1164+ --distribution. This will be used to guess the version number suffix
1165+ that you want, but you can always correct it in the resulting
1166+ debian/changelog.
1167
1168 If there is no debian changelog in the branch to retrieve the package
1169 name from then you must pass the --package option. If this version
1170 will change the name of the source package then you can use this option
1171 to set the new name.
1172+
1173+ examples::
1174+
1175+ bzr merge-upstream --version 0.2 \
1176+ http://example.org/releases/scruff-0.2.tar.gz
1177+
1178+ If you are merging a branch as well as the tarball then you can
1179+ specify the branch after the tarball, along with -r to specify the
1180+ revision of that branch to take::
1181+
1182+ bzr merge-upstream --version 0.2 \
1183+ http://example.org/releases/scruff-0.2.tar.gz \
1184+ http://scruff.org/bzr/scruff.dev -r tag:0.2
1185+
1186+ If there is no upstream release tarball, and you want bzr-builddeb to
1187+ create the tarball for you::
1188+
1189+ bzr merge-upstream --version 0.2 http://scruff.org/bzr/scruff.dev
1190+
1191+ Note that the created tarball is just the same as the contents of
1192+ the branch at the specified revision. If you wish to have something
1193+ different, for instance the results of running "make dist", then you
1194+ should create the tarball first, and pass it to the command as in
1195+ the second example.
1196 """
1197 takes_args = ['location?', 'upstream_branch?']
1198 aliases = ['mu']
1199
1200 package_opt = Option('package', help="The name of the source package.",
1201 type=str)
1202- version_opt = Option('version', help="The version number of this release.",
1203- type=str)
1204+ version_opt = Option('version',
1205+ help="The upstream version number of this release, for example "
1206+ "\"0.2\".", type=str)
1207 distribution_opt = Option('distribution', help="The distribution that "
1208 "this release is targetted at.", type=str)
1209 directory_opt = Option('directory',
1210 help='Working tree into which to merge.',
1211 short_name='d', type=unicode)
1212-
1213- takes_options = [package_opt, no_user_conf_opt, version_opt,
1214- distribution_opt, directory_opt, 'revision', 'merge-type']
1215-
1216- def _update_changelog(self, tree, version, distribution_name, changelog,
1217- package):
1218- from bzrlib.plugins.builddeb.merge_upstream import package_version
1219- if "~bzr" in str(version) or "+bzr" in str(version):
1220- entry_description = "New upstream snapshot."
1221- else:
1222- entry_description = "New upstream release."
1223- proc = subprocess.Popen(["/usr/bin/dch", "-v",
1224- str(package_version(version, distribution_name)),
1225- "-D", "UNRELEASED", "--release-heuristic", "changelog",
1226- entry_description], cwd=tree.basedir)
1227- proc.wait()
1228- if proc.returncode != 0:
1229+ last_version_opt = Option('last-version',
1230+ help='The full version of the last time '
1231+ 'upstream was merged.', type=str)
1232+ force_opt = Option('force',
1233+ help=('Force a merge even if the upstream branch '
1234+ 'has not changed.'))
1235+ v3_opt = Option('v3', help='Use dpkg-source format v3.')
1236+
1237+
1238+ takes_options = [package_opt, version_opt,
1239+ distribution_opt, directory_opt, last_version_opt,
1240+ force_opt, v3_opt, 'revision', 'merge-type']
1241+
1242+
1243+ def _add_changelog_entry(self, tree, package, version, distribution_name,
1244+ changelog):
1245+ from bzrlib.plugins.builddeb.merge_upstream import (
1246+ changelog_add_new_version)
1247+ if not changelog_add_new_version(tree, version, distribution_name,
1248+ changelog, package):
1249 raise BzrCommandError('Adding a new changelog stanza after the '
1250- 'merge had completed failed. Add the new changelog entry '
1251- 'yourself, review the merge, and then commit.')
1252-
1253- def run(self, location=None, upstream_branch=None, version=None, distribution=None,
1254- package=None, no_user_config=None, directory=".", revision=None,
1255- merge_type=None):
1256- from bzrlib.plugins.builddeb.errors import MissingChangelogError
1257+ 'merge had completed failed. Add the new changelog '
1258+ 'entry yourself, review the merge, and then commit.')
1259+
1260+ def _do_merge(self, tree, tarball_filename, version, current_version,
1261+ upstream_branch, upstream_revision, merge_type, force):
1262+ db = DistributionBranch(tree.branch, None, tree=tree)
1263+ dbs = DistributionBranchSet()
1264+ dbs.add_branch(db)
1265+ conflicts = db.merge_upstream(tarball_filename, version,
1266+ current_version, upstream_branch=upstream_branch,
1267+ upstream_revision=upstream_revision,
1268+ merge_type=merge_type, force=force)
1269+ return conflicts
1270+
1271+ def _export_tarball(self, package, version, orig_dir, upstream_branch,
1272+ upstream_revision):
1273+ # TODO: a way to use bz2 on export
1274+ dest_name = tarball_name(package, version)
1275+ tarball_filename = os.path.join(orig_dir, dest_name)
1276+ upstream = UpstreamBranchSource(upstream_branch,
1277+ {version: upstream_revision})
1278+ upstream.get_specific_version(package, version, orig_dir)
1279+ return tarball_filename
1280+
1281+ def _fetch_tarball(self, package, version, orig_dir, location, v3):
1282 from bzrlib.plugins.builddeb.repack_tarball import repack_tarball
1283- from bzrlib.plugins.builddeb.merge_upstream import upstream_branch_version
1284+ format = None
1285+ if v3:
1286+ if (location.endswith(".tar.bz2")
1287+ or location.endswith(".tbz2")):
1288+ format = "bz2"
1289+ dest_name = tarball_name(package, version, format=format)
1290+ tarball_filename = os.path.join(orig_dir, dest_name)
1291+ try:
1292+ repack_tarball(location, dest_name, target_dir=orig_dir,
1293+ force_gz=not v3)
1294+ except FileExists:
1295+ raise BzrCommandError("The target file %s already exists, and is either "
1296+ "different to the new upstream tarball, or they "
1297+ "are of different formats. Either delete the target "
1298+ "file, or use it as the argument to import."
1299+ % dest_name)
1300+ return tarball_filename
1301+
1302+ def _get_tarball(self, config, tree, package, version, upstream_branch,
1303+ upstream_revision, no_tarball, v3, location):
1304+ orig_dir = config.orig_dir or default_orig_dir
1305+ orig_dir = os.path.join(tree.basedir, orig_dir)
1306+ if not os.path.exists(orig_dir):
1307+ os.makedirs(orig_dir)
1308+ if upstream_branch and no_tarball:
1309+ tarball_filename = self._export_tarball(package, version,
1310+ orig_dir, upstream_branch, upstream_revision)
1311+ else:
1312+ tarball_filename = self._fetch_tarball(package, version, orig_dir,
1313+ location, v3)
1314+ return tarball_filename
1315+
1316+ def _get_version(self, version, package, no_tarball, upstream_branch,
1317+ upstream_revision, current_version):
1318+ from bzrlib.plugins.builddeb.merge_upstream import (
1319+ upstream_branch_version)
1320+ if version is None:
1321+ if upstream_branch and no_tarball:
1322+ version = str(upstream_branch_version(upstream_branch,
1323+ upstream_revision, package,
1324+ current_version))
1325+ note("Using version string %s for upstream branch." % (version))
1326+ else:
1327+ raise BzrCommandError("You must specify the "
1328+ "version number using --version.")
1329+ return version
1330+
1331+ def _get_upstream_revision(self, upstream_branch, revision):
1332+ upstream_revision = None
1333+ if upstream_branch is not None:
1334+ if revision is not None:
1335+ if len(revision) > 1:
1336+ raise BzrCommandError("merge-upstream takes only a single --revision")
1337+ upstream_revspec = revision[0]
1338+ upstream_revision = upstream_revspec.as_revision_id(upstream_branch)
1339+ else:
1340+ upstream_revision = upstream_branch.last_revision()
1341+ return upstream_revision
1342+
1343+ def _get_upstream_branch(self, location, upstream_branch, revision):
1344+ no_tarball = False
1345+ if upstream_branch is None:
1346+ try:
1347+ upstream_branch = Branch.open(location)
1348+ no_tarball = True
1349+ except NotBranchError:
1350+ upstream_branch = None
1351+ if revision is not None:
1352+ raise BzrCommandError("--revision is not allowed when"
1353+ " merging only a tarball")
1354+ else:
1355+ upstream_branch = Branch.open(upstream_branch)
1356+ return no_tarball, upstream_branch
1357+
1358+ def _get_changelog_info(self, tree, last_version, package, distribution):
1359+ from bzrlib.plugins.builddeb.errors import MissingChangelogError
1360+ changelog = None
1361+ current_version = last_version
1362+ try:
1363+ changelog = find_changelog(tree, False, max_blocks=2)[0]
1364+ if last_version is None:
1365+ current_version = changelog.version.upstream_version
1366+ if package is None:
1367+ package = changelog.package
1368+ if distribution is None:
1369+ distribution = find_last_distribution(changelog)
1370+ if distribution is not None:
1371+ note("Using distribution %s" % distribution)
1372+ except MissingChangelogError:
1373+ pass
1374+ if distribution is None:
1375+ note("No distribution specified, and no changelog, "
1376+ "assuming 'debian'")
1377+ distribution = "debian"
1378+ if package is None:
1379+ raise BzrCommandError("You did not specify --package, and "
1380+ "there is no changelog from which to determine the "
1381+ "package name, which is needed to know the name to "
1382+ "give the .orig.tar.gz. Please specify --package.")
1383+ distribution = distribution.lower()
1384+ distribution_name = lookup_distribution(distribution)
1385+ if distribution_name is None:
1386+ raise BzrCommandError("Unknown target distribution: %s" \
1387+ % distribution)
1388+ return current_version, package, distribution, distribution_name, changelog
1389+
1390+ def _get_upstream_location(self, location, config):
1391+ if location is None:
1392+ if config.upstream_branch is not None:
1393+ location = config.upstream_branch
1394+ else:
1395+ raise BzrCommandError("No location specified to merge")
1396+ return location
1397+
1398+ def run(self, location=None, upstream_branch=None, version=None,
1399+ distribution=None, package=None,
1400+ directory=".", revision=None, merge_type=None,
1401+ last_version=None, force=None, v3=None):
1402 tree, _ = WorkingTree.open_containing(directory)
1403 tree.lock_write()
1404 try:
1405@@ -499,120 +694,39 @@
1406 raise BzrCommandError("There are uncommitted changes in the "
1407 "working tree. You must commit before using this "
1408 "command.")
1409- config = debuild_config(tree, tree, no_user_config)
1410- if config.merge:
1411+ config = debuild_config(tree, tree)
1412+ if config.build_type == BUILD_TYPE_MERGE:
1413 raise BzrCommandError("Merge upstream in merge mode is not "
1414 "yet supported.")
1415- if config.native:
1416+ if config.build_type == BUILD_TYPE_NATIVE:
1417 raise BzrCommandError("Merge upstream in native mode is not "
1418 "yet supported.")
1419
1420- if location is None:
1421- if config.upstream_branch is not None:
1422- location = config.upstream_branch
1423- else:
1424- raise BzrCommandError("No location specified to merge")
1425- changelog = None
1426- try:
1427- changelog = find_changelog(tree, False, max_blocks=2)[0]
1428- current_version = changelog.version
1429- if package is None:
1430- package = changelog.package
1431- if distribution is None:
1432- distribution = find_last_distribution(changelog)
1433- if distribution is not None:
1434- info("Using distribution %s" % distribution)
1435- except MissingChangelogError:
1436- current_version = None
1437- if distribution is None:
1438- info("No distribution specified, and no changelog, "
1439- "assuming 'debian'")
1440- distribution = "debian"
1441-
1442- if package is None:
1443- raise BzrCommandError("You did not specify --package, and "
1444- "there is no changelog from which to determine the "
1445- "package name, which is needed to know the name to "
1446- "give the .orig.tar.gz. Please specify --package.")
1447-
1448- no_tarball = False
1449- if upstream_branch is None:
1450- try:
1451- upstream_branch = Branch.open(location)
1452- no_tarball = True
1453- except NotBranchError:
1454- upstream_branch = None
1455- else:
1456- upstream_branch = Branch.open(upstream_branch)
1457-
1458- distribution = distribution.lower()
1459- distribution_name = lookup_distribution(distribution)
1460- if distribution_name is None:
1461- raise BzrCommandError("Unknown target distribution: %s" \
1462- % distribution)
1463-
1464- upstream_revision = None
1465- if upstream_branch:
1466- if revision is not None:
1467- if len(revision) > 1:
1468- raise BzrCommandError("merge-upstream takes only a single --revision")
1469- upstream_revspec = revision[0]
1470- upstream_revision = upstream_revspec.as_revision_id(upstream_branch)
1471- else:
1472- upstream_revision = upstream_branch.last_revision()
1473-
1474- if no_tarball and revision is not None:
1475- raise BzrCommandError("--revision is not allowed when merging a tarball")
1476-
1477- if version is None:
1478- if upstream_branch and no_tarball:
1479- version = upstream_branch_version(upstream_branch,
1480- upstream_revision, package,
1481- current_version.upstream_version)
1482- info("Using version string %s for upstream branch." % (version))
1483- else:
1484- raise BzrCommandError("You must specify the "
1485- "version number using --version.")
1486-
1487- version = Version(version)
1488- orig_dir = config.orig_dir or default_orig_dir
1489- orig_dir = os.path.join(tree.basedir, orig_dir)
1490- if not os.path.exists(orig_dir):
1491- os.makedirs(orig_dir)
1492- dest_name = tarball_name(package, version.upstream_version)
1493- tarball_filename = os.path.join(orig_dir, dest_name)
1494-
1495- if upstream_branch and no_tarball:
1496- upstream = UpstreamBranchSource(upstream_branch,
1497- upstream_revision)
1498- upstream.get_specific_version(package, version.upstream_version,
1499- orig_dir)
1500- else:
1501- try:
1502- repack_tarball(location, dest_name, target_dir=orig_dir)
1503- except FileExists:
1504- raise BzrCommandError("The target file %s already exists, and is either "
1505- "different to the new upstream tarball, or they "
1506- "are of different formats. Either delete the target "
1507- "file, or use it as the argument to import."
1508- % dest_name)
1509- db = DistributionBranch(tree.branch, None, tree=tree)
1510- dbs = DistributionBranchSet()
1511- dbs.add_branch(db)
1512- conflicts = db.merge_upstream(tarball_filename, version,
1513- current_version, upstream_branch=upstream_branch,
1514- upstream_revision=upstream_revision,
1515- merge_type=merge_type)
1516-
1517- self._update_changelog(tree, version, distribution_name, changelog,
1518- package)
1519+ location = self._get_upstream_location(location, config)
1520+ (current_version, package, distribution, distribution_name,
1521+ changelog) = self._get_changelog_info( tree, last_version,
1522+ package, distribution)
1523+ no_tarball, upstream_branch = self._get_upstream_branch(
1524+ location, upstream_branch, revision)
1525+ upstream_revision = self._get_upstream_revision(upstream_branch,
1526+ revision)
1527+ version = self._get_version(version, package, no_tarball,
1528+ upstream_branch, upstream_revision, current_version)
1529+ tarball_filename = self._get_tarball(config, tree, package,
1530+ version, upstream_branch, upstream_revision, no_tarball, v3,
1531+ location)
1532+ conflicts = self._do_merge(tree, tarball_filename, version,
1533+ current_version, upstream_branch, upstream_revision,
1534+ merge_type, force)
1535+ self._add_changelog_entry(tree, package, version,
1536+ distribution_name, changelog)
1537 finally:
1538 tree.unlock()
1539- info("The new upstream version has been imported.")
1540+ note("The new upstream version has been imported.")
1541 if conflicts:
1542- info("You should now resolve the conflicts, review the changes, and then commit.")
1543+ note("You should now resolve the conflicts, review the changes, and then commit.")
1544 else:
1545- info("You should now review the changes and then commit.")
1546+ note("You should now review the changes and then commit.")
1547
1548
1549 class cmd_import_dsc(Command):
1550@@ -641,8 +755,7 @@
1551 takes_args = ['files*']
1552
1553 filename_opt = Option('file', help="File containing URIs of source "
1554- "packages to import.", type=str, argname="filename",
1555- short_name='F')
1556+ "packages to import.", type=str, short_name='F')
1557
1558 takes_options = [filename_opt]
1559
1560@@ -654,7 +767,7 @@
1561 for dscname in files_list:
1562 dsc = cache.get_dsc(dscname)
1563 def get_dsc_part(from_transport, filename):
1564- from_f = from_transport.get(filename)
1565+ from_f = open_file_via_transport(filename, from_transport)
1566 contents = from_f.read()
1567 to_f = open(os.path.join(orig_target, filename), 'wb')
1568 try:
1569@@ -669,7 +782,7 @@
1570 get_dsc_part(from_transport, name)
1571 db.import_package(os.path.join(orig_target, filename))
1572
1573- def run(self, files_list, filename=None):
1574+ def run(self, files_list, file=None):
1575 from bzrlib.plugins.builddeb.errors import MissingChangelogError
1576 try:
1577 tree = WorkingTree.open_containing('.')[0]
1578@@ -683,20 +796,20 @@
1579 "command")
1580 if files_list is None:
1581 files_list = []
1582- if filename is not None:
1583- if isinstance(filename, unicode):
1584- filename = filename.encode('utf-8')
1585- base_dir, path = urlutils.split(filename)
1586- sources_file = get_transport(base_dir).get(path)
1587+ if file is not None:
1588+ if isinstance(file, unicode):
1589+ file = file.encode('utf-8')
1590+ sources_file = open_file(file)
1591 for line in sources_file:
1592- line.strip()
1593- files_list.append(line)
1594+ line = line.strip()
1595+ if len(line) > 0:
1596+ files_list.append(line)
1597 if len(files_list) < 1:
1598 raise BzrCommandError("You must give the location of at least one "
1599 "source package to install, or use the "
1600 "--file option.")
1601- config = debuild_config(tree, tree, False)
1602- if config.merge:
1603+ config = debuild_config(tree, tree)
1604+ if config.build_type == BUILD_TYPE_MERGE:
1605 raise BzrCommandError("import-dsc in merge mode is not "
1606 "yet supported.")
1607 orig_dir = config.orig_dir or default_orig_dir
1608@@ -715,13 +828,14 @@
1609 if last_version is not None:
1610 if not db.has_upstream_version_in_packaging_branch(
1611 last_version.upstream_version):
1612- raise BzrCommandError("Unable to find the tag for "
1613- "the previous upstream version, %s, in the "
1614- "branch: %s" % (last_version,
1615- db.upstream_tag_name(last_version)))
1616- upstream_tip = db._revid_of_upstream_version_from_branch(
1617- last_version)
1618- db._extract_upstream_tree(upstream_tip, tempdir)
1619+ raise BzrCommandError("Unable to find the tag for the "
1620+ "previous upstream version, %s, in the branch: %s."
1621+ " Consider importing it via import-dsc or "
1622+ "import-upstream." % (last_version,
1623+ db.upstream_tag_name(last_version.upstream_version)))
1624+ upstream_tip = db.revid_of_upstream_version_from_branch(
1625+ last_version.upstream_version)
1626+ db.extract_upstream_tree(upstream_tip, tempdir)
1627 else:
1628 db._create_empty_upstream_tree(tempdir)
1629 self.import_many(db, files_list, orig_target)
1630@@ -731,6 +845,96 @@
1631 tree.unlock()
1632
1633
1634+class cmd_import_upstream(Command):
1635+ """Imports an upstream tarball.
1636+
1637+ This will import an upstream tarball in to your branch, but not modify the
1638+ working tree. Use merge-upstream if you wish to directly merge the new
1639+ upstream version in to your tree.
1640+
1641+ The imported revision can be accessed using the tag name that will be
1642+ reported at the end of a successful operation. The revision will include
1643+ the pristine-tar data that will allow other commands to recreate the
1644+ tarball when needed.
1645+
1646+ For instance::
1647+
1648+ $ bzr import-upstream 1.2.3 ../package_1.2.3.orig.tar.gz
1649+
1650+ If upstream is packaged in bzr, you should provide the upstream branch
1651+ whose tip commit is the closest match to the tarball::
1652+
1653+ $ bzr import-upstream 1.2.3 ../package_1.2.3.orig.tar.gz ../upstream
1654+
1655+ After doing this, commands that assume there is an upstream tarball, like
1656+ 'bzr builddeb' will be able to recreate the one provided at import-upstream
1657+ time, meaning that you don't need to distribute the tarball in addition to
1658+ the branch.
1659+
1660+ If you want to manually merge with the imported upstream, you can do::
1661+
1662+ $ bzr merge . -r tag:upstream-1.2.3
1663+
1664+ The imported revision will have file ids taken from your branch, the
1665+ upstream branch, or previous tarball imports as necessary. In addition
1666+ the parents of the new revision will be the previous upstream tarball
1667+ import and the tip of the upstream branch if you supply one.
1668+ """
1669+
1670+ takes_options = ['revision']
1671+
1672+ takes_args = ['version', 'location', 'upstream_branch?']
1673+
1674+ def run(self, version, location, upstream_branch=None, revision=None):
1675+ # TODO: search for similarity etc.
1676+ version = version.encode('utf8')
1677+ branch, _ = Branch.open_containing('.')
1678+ if upstream_branch is None:
1679+ upstream = None
1680+ else:
1681+ upstream = Branch.open(upstream_branch)
1682+ branch.lock_write() # we will be adding a tag here.
1683+ self.add_cleanup(branch.unlock)
1684+ tempdir = tempfile.mkdtemp(
1685+ dir=branch.bzrdir.root_transport.clone('..').local_abspath('.'))
1686+ self.add_cleanup(shutil.rmtree, tempdir)
1687+ db = DistributionBranch(branch, upstream_branch=upstream)
1688+ if db.has_upstream_version_in_packaging_branch(version):
1689+ raise BzrCommandError("Version %s is already present." % version)
1690+ tagged_versions = {}
1691+ for tag_name, tag_revid in branch.tags.get_tag_dict().iteritems():
1692+ if not is_upstream_tag(tag_name):
1693+ continue
1694+ tag_version = Version(upstream_tag_version(tag_name))
1695+ tagged_versions[tag_version] = tag_revid
1696+ tag_order = sorted(tagged_versions.keys())
1697+ if tag_order:
1698+ parents = [tagged_versions[tag_order[-1]]]
1699+ else:
1700+ parents = []
1701+ if parents:
1702+ if upstream is not None:
1703+ # See bug lp:309682
1704+ upstream.repository.fetch(branch.repository, parents[0])
1705+ db.extract_upstream_tree(parents[0], tempdir)
1706+ else:
1707+ db._create_empty_upstream_tree(tempdir)
1708+ tree = db.get_branch_tip_revtree()
1709+ tree.lock_read()
1710+ dbs = DistributionBranchSet()
1711+ dbs.add_branch(db)
1712+ if revision is None:
1713+ upstream_revid = None
1714+ elif len(revision) == 1:
1715+ upstream_revid = revision[0].in_history(upstream).rev_id
1716+ else:
1717+ raise BzrCommandError('bzr import-upstream --revision takes exactly'
1718+ ' one revision specifier.')
1719+ tag_name, _ = db.import_upstream_tarball(location, version, parents,
1720+ upstream_branch=upstream, upstream_revision=upstream_revid)
1721+ self.outf.write('Imported %s as tag:%s.\n' % (location, tag_name))
1722+
1723+
1724 class cmd_bd_do(Command):
1725 """Run a command in an exported package, copying the result back.
1726
1727@@ -760,9 +964,8 @@
1728
1729 def run(self, command_list=None):
1730 t = WorkingTree.open_containing('.')[0]
1731- config = debuild_config(t, t, False)
1732-
1733- if not config.merge:
1734+ config = debuild_config(t, t)
1735+ if config.build_type != BUILD_TYPE_MERGE:
1736 raise BzrCommandError("This command only works for merge mode "
1737 "packages. See /usr/share/doc/bzr-builddeb"
1738 "/user_manual/merge.html for more information.")
1739@@ -781,9 +984,13 @@
1740 orig_dir = config.orig_dir
1741 if orig_dir is None:
1742 orig_dir = default_orig_dir
1743+
1744 upstream_provider = UpstreamProvider(changelog.package,
1745 changelog.version.upstream_version, orig_dir,
1746- get_upstream_sources(t, t.branch, larstiq=larstiq))
1747+ [PristineTarSource(t, t.branch),
1748+ AptSource(),
1749+ GetOrigSourceSource(t, larstiq),
1750+ UScanSource(t, larstiq) ])
1751
1752 distiller = MergeModeDistiller(t, upstream_provider,
1753 larstiq=larstiq)
1754@@ -797,16 +1004,16 @@
1755 builder.prepare()
1756 run_hook(t, 'pre-export', config)
1757 builder.export()
1758- info('Running "%s" in the exported directory.' % (command))
1759+ note('Running "%s" in the exported directory.' % (command))
1760 if give_instruction:
1761- info('If you want to cancel your changes then exit with a non-zero '
1762+ note('If you want to cancel your changes then exit with a non-zero '
1763 'exit code, e.g. run "exit 1".')
1764 try:
1765 builder.build()
1766 except BuildFailedError:
1767 raise BzrCommandError('Not updating the working tree as the '
1768 'command failed.')
1769- info("Copying debian/ back")
1770+ note("Copying debian/ back")
1771 if larstiq:
1772 destination = ''
1773 else:
1774@@ -821,13 +1028,13 @@
1775 if proc.returncode != 0:
1776 raise BzrCommandError('Copying back debian/ failed')
1777 builder.clean()
1778- info('If any files were added or removed you should run "bzr add" or '
1779+ note('If any files were added or removed you should run "bzr add" or '
1780 '"bzr rm" as appropriate.')
1781
1782
1783 class cmd_mark_uploaded(Command):
1784 """Mark that this branch has been uploaded, prior to pushing it.
1785-
1786+
1787 When a package has been uploaded we want to mark the revision
1788 that it was uploaded in. This command automates doing that
1789 by marking the current tip revision with the version indicated
1790@@ -836,9 +1043,9 @@
1791 force = Option('force', help="Mark the upload even if it is already "
1792 "marked.")
1793
1794- takes_options = [merge_opt, no_user_conf_opt, force]
1795+ takes_options = [merge_opt, force]
1796
1797- def run(self, merge=False, no_user_config=False, force=None):
1798+ def run(self, merge=None, force=None):
1799 t = WorkingTree.open_containing('.')[0]
1800 t.lock_write()
1801 try:
1802@@ -846,16 +1053,15 @@
1803 raise BzrCommandError("There are uncommitted changes in the "
1804 "working tree. You must commit before using this "
1805 "command")
1806- config = debuild_config(t, t, no_user_config)
1807- if not merge:
1808- merge = config.merge
1809+ config = debuild_config(t, t)
1810+ if merge is None:
1811+ merge = (config.build_type == BUILD_TYPE_MERGE)
1812 (changelog, larstiq) = find_changelog(t, merge)
1813- distributions = changelog.distributions.strip()
1814- target_dist = distributions.split()[0]
1815- distribution_name = suite_to_distribution(target_dist)
1816- if distribution_name is None:
1817- raise BzrCommandError("Unknown target distribution: %s" \
1818- % target_dist)
1819+ if changelog.distributions == 'UNRELEASED':
1820+ if not force:
1821+ raise BzrCommandError("The changelog still targets "
1822+ "'UNRELEASED', so apparently hasn't been "
1823+ "uploaded.")
1824 db = DistributionBranch(t.branch, None)
1825 dbs = DistributionBranchSet()
1826 dbs.add_branch(db)
1827@@ -870,14 +1076,97 @@
1828 t.unlock()
1829
1830
1831-class cmd_test_builddeb(Command):
1832- """Run the builddeb test suite"""
1833-
1834- hidden = True
1835-
1836- def run(self):
1837- from bzrlib.tests import selftest
1838- passed = selftest(test_suite_factory=test_suite)
1839- # invert for shell exit code rules
1840- return not passed
1841-
1842+class cmd_merge_package(Command):
1843+ """Merges source packaging branch into target packaging branch.
1844+
1845+ This will first check whether the upstream branches have diverged.
1846+
1847+ If that's the case an attempt will be made to fix the upstream ancestry
1848+ so that the user only needs to deal wth packaging branch merge issues.
1849+
1850+ In the opposite case a normal merge will be performed.
1851+ """
1852+ takes_args = ['source']
1853+
1854+ def run(self, source):
1855+ source_branch = None
1856+ # Get the target branch.
1857+ try:
1858+ tree = WorkingTree.open_containing('.')[0]
1859+ except (NotBranchError, NoWorkingTree):
1860+ raise BzrCommandError(
1861+ "There is no tree to merge the source branch in to")
1862+ # Get the source branch.
1863+ try:
1864+ source_branch = Branch.open(source)
1865+ except NotBranchError:
1866+ raise BzrCommandError("Invalid source branch URL?")
1867+
1868+ tree.lock_write()
1869+ self.add_cleanup(tree.unlock)
1870+ source_branch.lock_read()
1871+ self.add_cleanup(source_branch.unlock)
1872+ this_config = debuild_config(tree, tree)
1873+ that_config = debuild_config(source_branch.basis_tree(),
1874+ source_branch.basis_tree())
1875+ if not (this_config.build_type == BUILD_TYPE_NATIVE or
1876+ that_config.build_type == BUILD_TYPE_NATIVE):
1877+ fix_ancestry_as_needed(tree, source_branch)
1878+
1879+ # Merge source packaging branch in to the target packaging branch.
1880+ _merge_tags_if_possible(source_branch, tree.branch)
1881+ conflicts = tree.merge_from_branch(source_branch)
1882+ if conflicts > 0:
1883+ note('The merge resulted in %s conflicts. Please resolve these '
1884+ 'and commit the changes with "bzr commit".' % conflicts)
1885+ else:
1886+ note('The merge resulted in no conflicts. You may commit the '
1887+ 'changes by running "bzr commit".')
1888+
1889+
1890+class cmd_dh_make(Command):
1891+ """Helps you create a new package.
1892+
1893+ This code wraps dh_make to do the Bazaar setup for you, ensuring that
1894+ your branches have all the necessary information and are correctly
1895+ linked to the upstream branches where necessary.
1896+
1897+ The basic use case is satisfied by
1898+
1899+ bzr dh-make project 0.1 http://project.org/project-0.1.tar.gz
1900+
1901+ which will import the tarball with the correct tags etc. and then
1902+ run dh_make for you in order to start the packaging.
1903+
1904+ If there upstream is available in bzr then run the command from the
1905+ root of a branch of that corresponding to the 0.1 release.
1906+
1907+ If there is no upstream available in bzr then run the command from
1908+ outside a branch and it will create a branch for you in a directory
1909+ named the same as the package name you specify as the second argument.
1910+
1911+ If you do not wish to use dh_make, but just take advantage of the
1912+ Bazaar specific parts then use the --bzr-only option.
1913+ """
1914+
1915+ aliases = ['dh_make']
1916+
1917+ takes_args = ['package_name', 'version', 'tarball']
1918+
1919+ bzr_only_opt = Option('bzr-only', help="Don't run dh_make.")
1920+ v3_opt = Option('v3', help="Use dpkg-source format v3.")
1921+
1922+ takes_options = [bzr_only_opt, v3_opt]
1923+
1924+ def run(self, package_name, version, tarball, bzr_only=None, v3=None):
1925+ tree = dh_make.import_upstream(tarball, package_name,
1926+ version.encode("utf-8"), use_v3=v3)
1927+ if not bzr_only:
1928+ tree.lock_write()
1929+ try:
1930+ dh_make.run_dh_make(tree, package_name, version, use_v3=v3)
1931+ finally:
1932+ tree.unlock()
1933+ note('Package prepared in %s'
1934+ % urlutils.unescape_for_display(tree.basedir,
1935+ self.outf.encoding))
1936
1937=== modified file 'config.py'
1938--- config.py 2009-03-30 11:58:12 +0000
1939+++ config.py 2011-01-28 14:24:44 +0000
1940@@ -20,7 +20,16 @@
1941
1942 from bzrlib.config import ConfigObj, TreeConfig
1943 from bzrlib.trace import mutter, warning
1944-from bzrlib.util.configobj.configobj import ParseError
1945+try:
1946+ from bzrlib.util.configobj.configobj import ParseError
1947+except ImportError:
1948+ from configobj import ParseError
1949+
1950+
1951+BUILD_TYPE_NORMAL = "normal"
1952+BUILD_TYPE_NATIVE = "native"
1953+BUILD_TYPE_MERGE = "merge"
1954+BUILD_TYPE_SPLIT = "split"
1955
1956
1957 class SvnBuildPackageMappedConfig(object):
1958@@ -254,6 +263,17 @@
1959
1960 merge = _bool_property('merge', "Run in merge mode")
1961
1962+ @property
1963+ def build_type(self):
1964+ if self.merge:
1965+ return BUILD_TYPE_MERGE
1966+ elif self.native:
1967+ return BUILD_TYPE_NATIVE
1968+ elif self.split:
1969+ return BUILD_TYPE_SPLIT
1970+ else:
1971+ return BUILD_TYPE_NORMAL
1972+
1973 quick_builder = _opt_property('quick-builder',
1974 "A quick command to build with", True)
1975
1976@@ -267,6 +287,9 @@
1977 export_upstream = _opt_property('export-upstream',
1978 "Get the upstream source from another branch")
1979
1980+ export_upstream_revision = _opt_property('export-upstream-revision',
1981+ "The revision of the upstream source to use.")
1982+
1983
1984 def _test():
1985 import doctest
1986
1987=== modified file 'debian/changelog'
1988--- debian/changelog 2009-07-26 18:21:49 +0000
1989+++ debian/changelog 2011-01-28 14:24:44 +0000
1990@@ -1,9 +1,181 @@
1991-bzr-builddeb (2.2~ubuntu3) UNRELEASED; urgency=low
1992+bzr-builddeb (2.5.1) UNRELEASED; urgency=low
1993+
1994+ [ James Westby ]
1995+
1996+ * Don't fail if asked to use a .bz2 tarball that is already in the desired
1997+ location. LP: #616786
1998+ * Don't crash if we are asked to merge-upstream with an unrelated branch.
1999+ LP: #619614.
2000+ * Don't strip -n from the version we get in merge-upstream, as some
2001+ upstreams have this in there, and trying to support both means supporting
2002+ both badly. If you are used to doing "bzr merge-upstream --version
2003+ <package version>" then it will no longer work for you, use the
2004+ upstream version instead.
2005+ * Don't crash when doing merge-upstream with a branch that does a rename
2006+ and then ships another file with the old path in the tarball that isn't
2007+ in the branch.
2008+ * Accept None as a valid previous_version value in merge_upstream(). LP: #680945
2009+
2010+ [ Jelmer Vernooij ]
2011+
2012+ * Fix the auto-detection of merge mode.
2013+ * Don't crash on merge mode packages where there is no export-upstream
2014+ if we can't find the tarball.
2015+ * Determine Bazaar home directory using bzrlib to prevent test
2016+ isolation issues. LP: #614125
2017+ * Support 'bzr tags --sort=debversion'. Closes #701244.
2018+ * When adding a changelog entry, support git and svn snapshots.
2019+ * Automatically use debian/source/format if package is native. Closes:
2020+ #586617
2021+ * Fix "bzr builddeb" if last upload was not to a Ubuntu release pocket.
2022+ LP: #709263
2023+
2024+ -- Jelmer Vernooij <jelmer@debian.org> Fri, 28 Jan 2011 15:13:22 +0100
2025+
2026+bzr-builddeb (2.5) unstable; urgency=low
2027+
2028+ [ Colin Watson ]
2029+ * Consider a .dsc without a Format: to be Format: 1.0.
2030+
2031+ [ Jelmer Vernooij ]
2032+ * export now uses the timestamp of the last revision, making them more
2033+ deterministic, and so hopefully producing the same tarballs when it is
2034+ used for that.
2035+ * Fix use of getattr to have 3 arguments to avoid exception. (LP: #572093)
2036+ * Implement the automatic_tag_name hook so that "bzr tag" with no arguments
2037+ will tag based on the version in debian/changelog.
2038+ * Support upstream/VERSION tags, for compatibility with git-
2039+ buildpackage. LP: #551362
2040+ * Support upstream tarballs without a pristine tar delta.
2041+ * Support -r argument to import-upstream.
2042+
2043+ [ Robert Collins ]
2044+ * Add import-upstream command which imports an upstream - useful for
2045+ migrating existing packaging branches into pristine-tar using mode.
2046+ * Stop stripping .bzrignore from tarball imports. LP: #496907
2047+ * Make the upstream branch authoritative for file ids when importing a
2048+ tarball, stopping errors when files are renamed. LP: #588060
2049+
2050+ [ James Westby ]
2051+ * Add a --package-merge option to builddeb to build with the -v and -sa
2052+ appropriate when doing a merge from Debian or similar. LP: #576027
2053+ * Fixed a logic error that stops -r working in merge-upstream. LP: #594575
2054+
2055+ [ Jelmer Vernooij ]
2056+ * Determine Bazaar home directory using bzrlib to prevent test
2057+ isolation issues. Closes: #614125
2058+ * Bump standards version to 3.9.1 (no changes).
2059+
2060+ -- Jelmer Vernooij <jelmer@debian.org> Wed, 11 Aug 2010 18:23:54 +0200
2061+
2062+bzr-builddeb (2.4.2) unstable; urgency=low
2063+
2064+ [ Jelmer Vernooij ]
2065+ * Avoid AttributeError in the python-apt compatibility code.
2066+
2067+ [ James Westby ]
2068+ * Add 'maverick' as an Ubuntu release.
2069+
2070+ -- Jelmer Vernooij <jelmer@debian.org> Tue, 13 Apr 2010 21:37:53 +0200
2071+
2072+bzr-builddeb (2.4.1) unstable; urgency=low
2073+
2074+ [ Colin Watson ]
2075+ * Consider a .dsc without a Format: to be Format: 1.0.
2076+
2077+ [ Jelmer Vernooij ]
2078+ * Fix use of getattr to have 3 arguments to avoid exception. (LP: #572093)
2079+
2080+ [ James Westby ]
2081+ * Fix for Launchpad's multi-version support.
2082+
2083+ -- James Westby <james.westby@ubuntu.com> Thu, 18 Mar 2010 14:19:53 -0400
2084+
2085+bzr-builddeb (2.4) unstable; urgency=low
2086+
2087+ [ Jelmer Vernooij ]
2088+ * Switch section to vcs.
2089+ * Allow the directory service to work with older version of python-apt.
2090+ * Fix compatibility with versions of bzr that don't have
2091+ bzrlib.utils.configobj. (Closes: #572093)
2092+
2093+ [ James Westby ]
2094+ * Correct typo that causes packages with .orig.tar.bz2 to fail to build.
2095+ * Also merge tags in merge-package.
2096+ * Adapt to the python-apt 0.8 API. Thanks to Julian Andres Klode.
2097+ (Closes: #572093)
2098+ * Fix merge-upstream with just a branch. (LP: #528273)
2099+
2100+ [ John Arbash Meinel ]
2101+ * Improve the changelog merge hook to be smarter when both sides change.
2102+
2103+ [ Steve Langasek ]
2104+ * Make merge-upstream --v3 unpack .tar.bz2 with the correct arguments.
2105+ (LP: #529900)
2106+
2107+ -- Jelmer Vernooij <jelmer@debian.org> Sat, 13 Feb 2010 01:16:00 +0100
2108+
2109+bzr-builddeb (2.3) experimental; urgency=low
2110+
2111+ [ James Westby ]
2112+ * Some support for v3 source formats (Closes: #562991)
2113+ - Those that look quite a lot like v1 are supported well.
2114+ - .tar.bz2 tarballs are supported for import, building, merge-upstream,
2115+ etc., but only enabled for the latter with a --v3 switch for now.
2116+ - Multiple orig.tar.gz is not supported.
2117+ - .tar.lzma is not supported awaiting pristine-tar support.
2118+ * New "dh-make" command ("dh_make" alias) that allows you to start
2119+ packaging, either in an empty branch, or based on an upstream branch.
2120+ * Fix merge-package for native packages (LP: #476348)
2121+ * debian/changelog merge hook to reduce the manual conflict resolution
2122+ required there. Thanks to John Arbash Meinel and Andrew Bennetts
2123+ (LP: #501754).
2124+ - Requires newer bzr.
2125+ * Fix merge-package outside a shared repo (LP: #493462)
2126+ * Fix exporting of symlinks (LP: #364671)
2127+ * Add --force option to merge-upstream which may help certain people.
2128+ * Use system configobj if the bzr copy isn't available. Thanks Jelmer.
2129+ * Make merging multiple-root branches work. Thanks Robert Collins.
2130+ * Disentangle from bzrtools. Thanks Max Bowser.
2131+
2132+ [ Jelmer Vernooij ]
2133+ * Bump standards version to 3.8.4.
2134+ * Fix formatting in doc-base.
2135+
2136+ -- Jelmer Vernooij <jelmer@debian.org> Sat, 13 Feb 2010 00:44:03 +0100
2137+
2138+bzr-builddeb (2.2) unstable; urgency=low
2139+
2140+ * Upload to unstable.
2141+ * Bump standards version to 3.8.3.
2142+
2143+ -- Jelmer Vernooij <jelmer@debian.org> Mon, 18 Jan 2010 19:15:26 +1300
2144+
2145+bzr-builddeb (2.2~ubuntu3) karmic; urgency=low
2146
2147 [ Jelmer Vernooij ]
2148 * Automatically use merge mode if there's only a debian/ directory in
2149 the packaging branch. Closes: #529816.
2150
2151+ [ James Westby ]
2152+ * Merge merge-package command from Muharem Hrnjadovic to allow merging
2153+ another branch of the same package in a way that will avoid spurious
2154+ conflicts from divergent upstream histories. Thanks Muharem.
2155+ * Don't crash on merge-upstream due to API changes not being fully applied.
2156+ * Add plugin information as parsed by "bzr plugin-info".
2157+ * Improve the error when the upstream tag is missing during merge-upstream.
2158+ * Also query the config file for the revision to use in export-upstream
2159+ (LP: #415572)
2160+ * Don't populate the commit message editor with all lines added to
2161+ debian/changelog. Only use the stripped version of the "change" lines.
2162+ * Always use the date from debian/changelog during import-dsc. Thanks
2163+ Sveinung Kvilhaugsvik.
2164+ * pristine-tar errors are no longer fatal for building the package.
2165+ Thanks Muharem. (LP: #417153)
2166+ * Refuse to build a conflicted tree. Thanks Muharem. (LP: #381303)
2167+ * Don't crash if there are no deb-src lines when building. (LP: #375897)
2168+ * Make import-dsc work against redirected URIs. Thanks Muharem (LP: #337209)
2169+
2170 -- James Westby <james.westby@ubuntu.com> Sun, 26 Jul 2009 18:38:47 +0200
2171
2172 bzr-builddeb (2.2~ubuntu2) karmic; urgency=low
2173@@ -286,6 +458,7 @@
2174
2175 bzr-builddeb (0.92) unstable; urgency=low
2176
2177+ [ James Westby ]
2178 * Support incremental imports of normal mode packages from source packages
2179 for uploads done outside the VCS.
2180 * Also look for upstream tarballs in the archives. Do this in preference
2181@@ -299,8 +472,22 @@
2182 * Allow the last upstream not to be on the mainline during merge-upstream.
2183 * Don't complain when repacking the tarball if the target exists, but is the
2184 same as the source. Only .tar.gz can be considered identical.
2185-
2186- -- James Westby <jw+debian@jameswestby.net> Wed, 31 Oct 2007 21:06:58 +0000
2187+ * Bug fix: "bzr-builddeb: merge-upstream --version is a conflicting
2188+ optoin", thanks to Jamie Wilkinson (Closes: #449369).
2189+ * Close old bug, only parse first changelog entry (Closes: #429299)
2190+ * Bug fix: "bzr-builddeb: parse debian/watch if no URL specified for
2191+ merge-upstream", thanks to Jamie Wilkinson (Closes: #449362).
2192+ * Bug fix: "bzr-builddeb: can't do merge packages with
2193+ dh-make-pear", thanks to mah@everybody.org (Mark A. Hershberger)
2194+ (Closes: #440069).
2195+
2196+ [ Reinhard Tartler ]
2197+ * (Build-)Depend on python-apt
2198+ * bump dependency on bzrtools
2199+ * when running the testsuite, set home directory to effectively disable
2200+ any user installed plugins
2201+
2202+ -- Reinhard Tartler <siretart@tauware.de> Mon, 12 Nov 2007 16:39:43 +0100
2203
2204 bzr-builddeb (0.91) unstable; urgency=low
2205
2206
2207=== modified file 'debian/control'
2208--- debian/control 2009-07-26 18:35:25 +0000
2209+++ debian/control 2011-01-28 14:24:44 +0000
2210@@ -1,19 +1,18 @@
2211 Source: bzr-builddeb
2212-Section: devel
2213+Section: vcs
2214 Priority: optional
2215-Maintainer: Ubuntu MOTU Developers <ubuntu-motu@lists.ubuntu.com>
2216-XSBC-Original-Maintainer: Debian Bazaar Maintainers <pkg-bazaar-maint@lists.alioth.debian.org>
2217-Uploaders: Reinhard Tartler <siretart@tauware.de>, James Westby <james.westby@ubuntu.com>
2218+Maintainer: Debian Bazaar Maintainers <pkg-bazaar-maint@lists.alioth.debian.org>
2219+Uploaders: Reinhard Tartler <siretart@tauware.de>, James Westby <james.westby@ubuntu.com>, Jelmer Vernooij <jelmer@debian.org>
2220 Build-Depends: debhelper (>= 5.0.37.2), python-all (>= 2.3.5-11)
2221-Build-Depends-Indep: bzr (>= 1.10~), python-central (>= 0.5.8), python-docutils, python-debian (>= 0.1.11), python-apt, bzrtools (>= 1.2~), patchutils
2222-Vcs-Bzr: http://bazaar.launchpad.net/~james-w/bzr-builddeb/2.1/
2223-Vcs-Browser: http://bazaar.launchpad.net/~james-w/bzr-builddeb/2.1/
2224+Build-Depends-Indep: bzr (>= 2.1~), python-central (>= 0.5.8), python-docutils, python-debian (>= 0.1.11), python-apt, patchutils
2225+Vcs-Bzr: http://bzr.debian.org/pkg-bazaar/bzr-builddeb/unstable
2226+Vcs-Browser: http://bzr.debian.org/loggerhead/pkg-bazaar/bzr-builddeb/unstable
2227 XS-Python-Version: >= 2.4
2228-Standards-Version: 3.7.2
2229+Standards-Version: 3.9.1
2230
2231 Package: bzr-builddeb
2232 Architecture: all
2233-Depends: bzr (>= 1.10~), python-debian (>= 0.1.11), python-apt, ${python:Depends}, dpkg-dev, fakeroot, bzrtools (>= 1.2), devscripts, patchutils, pristine-tar
2234+Depends: bzr (>= 2.1~), python-debian (>= 0.1.11), python-apt, ${python:Depends}, dpkg-dev, fakeroot, devscripts, patchutils, pristine-tar, ${misc:Depends}
2235 Recommends: python-launchpadlib
2236 Suggests: bzr-svn (>= 0.4.10)
2237 Provides: bzr-buildpackage
2238
2239=== modified file 'debian/doc-base'
2240--- debian/doc-base 2007-07-10 20:52:45 +0000
2241+++ debian/doc-base 2011-01-28 14:24:44 +0000
2242@@ -3,9 +3,8 @@
2243 Author: James Westby
2244 Abstract: This user guide describes how to use bzr-builddeb to build Debian
2245 packages from Bazaar branches.
2246-Section: Apps/Programming
2247+Section: Programming
2248
2249 Format: HTML
2250 Index: /usr/share/doc/bzr-builddeb/user_manual/index.html
2251 Files: /usr/share/doc/bzr-builddeb/user_manual/*.html
2252-
2253
2254=== added file 'dh_make.py'
2255--- dh_make.py 1970-01-01 00:00:00 +0000
2256+++ dh_make.py 2011-01-28 14:24:44 +0000
2257@@ -0,0 +1,128 @@
2258+import os
2259+import sys
2260+import subprocess
2261+
2262+from bzrlib import (
2263+ bzrdir,
2264+ revision as mod_revision,
2265+ trace,
2266+ transport,
2267+ workingtree,
2268+ )
2269+from bzrlib import errors as bzr_errors
2270+
2271+from bzrlib.plugins.builddeb import (
2272+ default_orig_dir,
2273+ import_dsc,
2274+ upstream,
2275+ util,
2276+ )
2277+
2278+
2279+def _get_tree(package_name):
2280+ try:
2281+ tree = workingtree.WorkingTree.open(".")
2282+ except bzr_errors.NotBranchError:
2283+ if os.path.exists(package_name):
2284+ raise bzr_errors.BzrCommandError("Either run the command from an "
2285+ "existing branch of upstream, or move %s aside "
2286+ "and a new branch will be created there."
2287+ % package_name)
2288+ to_transport = transport.get_transport(package_name)
2289+ tree = to_transport.ensure_base()
2290+ try:
2291+ a_bzrdir = bzrdir.BzrDir.open_from_transport(to_transport)
2292+ except bzr_errors.NotBranchError:
2293+ # really a NotBzrDir error...
2294+ create_branch = bzrdir.BzrDir.create_branch_convenience
2295+ branch = create_branch(to_transport.base,
2296+ possible_transports=[to_transport])
2297+ a_bzrdir = branch.bzrdir
2298+ else:
2299+ if a_bzrdir.has_branch():
2300+ raise bzr_errors.AlreadyBranchError(package_name)
2301+ branch = a_bzrdir.create_branch()
2302+ a_bzrdir.create_workingtree()
2303+ try:
2304+ tree = a_bzrdir.open_workingtree()
2305+ except bzr_errors.NoWorkingTree:
2306+ tree = a_bzrdir.create_workingtree()
2307+ return tree
2308+
2309+
2310+def _get_tarball(tree, tarball, package_name, version, use_v3=False):
2311+ from bzrlib.plugins.builddeb.repack_tarball import repack_tarball
2312+ config = util.debuild_config(tree, tree)
2313+ orig_dir = config.orig_dir or default_orig_dir
2314+ orig_dir = os.path.join(tree.basedir, orig_dir)
2315+ if not os.path.exists(orig_dir):
2316+ os.makedirs(orig_dir)
2317+ format = None
2318+ if use_v3:
2319+ if tarball.endswith(".tar.bz2") or tarball.endswith(".tbz2"):
2320+ format = "bz2"
2321+ dest_name = util.tarball_name(package_name, version, format=format)
2322+ tarball_filename = os.path.join(orig_dir, dest_name)
2323+ trace.note("Fetching tarball")
2324+ repack_tarball(tarball, dest_name, target_dir=orig_dir,
2325+ force_gz=not use_v3)
2326+ provider = upstream.UpstreamProvider(package_name, "%s-1" % version,
2327+ orig_dir, [])
2328+ provider.provide(os.path.join(tree.basedir, ".."))
2329+ return tarball_filename, util.md5sum_filename(tarball_filename)
2330+
2331+
2332+def import_upstream(tarball, package_name, version, use_v3=False):
2333+ tree = _get_tree(package_name)
2334+ if tree.branch.last_revision() != mod_revision.NULL_REVISION:
2335+ parents = [tree.branch.last_revision()]
2336+ else:
2337+ parents = []
2338+ tarball_filename, md5sum = _get_tarball(tree, tarball,
2339+ package_name, version, use_v3=use_v3)
2340+ db = import_dsc.DistributionBranch(tree.branch, tree.branch, tree=tree,
2341+ upstream_tree=tree)
2342+ dbs = import_dsc.DistributionBranchSet()
2343+ dbs.add_branch(db)
2344+ db.import_upstream_tarball(tarball_filename, version, parents, md5sum=md5sum)
2345+ return tree
2346+
2347+
2348+def run_dh_make(tree, package_name, version, use_v3=False):
2349+ if not tree.has_filename("debian"):
2350+ tree.mkdir("debian")
2351+ # FIXME: give a nice error on 'debian is not a directory'
2352+ if tree.path2id("debian") is None:
2353+ tree.add("debian")
2354+ if use_v3:
2355+ if not tree.has_filename("debian/source"):
2356+ tree.mkdir("debian/source")
2357+ if tree.path2id("debian/source") is None:
2358+ tree.add("debian/source")
2359+ f = open("debian/source/format")
2360+ try:
2361+ f.write("3.0 (quilt)\n")
2362+ finally:
2363+ f.close()
2364+ if tree.path2id("debian/source/format") is None:
2365+ tree.add("debian/source/format")
2366+ command = ["dh_make", "--addmissing", "--packagename",
2367+ "%s_%s" % (package_name, version)]
2368+ if getattr(sys.stdin, 'fileno', None) is None:
2369+ # running in a test or something
2370+ stdin = subprocess.PIPE
2371+ input = "s\n\n"
2372+ else:
2373+ stdin = sys.stdin
2374+ input = None
2375+ proc = subprocess.Popen(command, cwd=tree.basedir,
2376+ preexec_fn=util.subprocess_setup, stdin=stdin)
2377+ if input is not None:
2378+ proc.stdin.write(input)
2379+ proc.stdin.close()
2380+ retcode = proc.wait()
2381+ if retcode != 0:
2382+ raise bzr_errors.BzrCommandError("dh_make failed.")
2383+ for fn in os.listdir(tree.abspath("debian")):
2384+ if not fn.endswith(".ex") and not fn.endswith(".EX"):
2385+ tree.add(os.path.join("debian", fn))
2386
2387=== modified file 'directory.py'
2388--- directory.py 2009-04-16 10:42:12 +0000
2389+++ directory.py 2011-01-28 14:24:44 +0000
2390@@ -19,7 +19,7 @@
2391 #
2392
2393 from bzrlib import errors
2394-from bzrlib.trace import info
2395+from bzrlib.trace import note
2396
2397 import apt_pkg
2398
2399@@ -35,11 +35,19 @@
2400
2401 apt_pkg.init()
2402
2403- sources = apt_pkg.GetPkgSrcRecords()
2404+ # Older versions of apt_pkg don't have SourceRecords,
2405+ # newer versions give a deprecation warning when using
2406+ # GetPkgSrcRecords.
2407+ try:
2408+ sources = apt_pkg.SourceRecords()
2409+ except AttributeError:
2410+ sources = apt_pkg.GetPkgSrcRecords()
2411
2412 urls = {}
2413- while sources.Lookup(name):
2414- for l in sources.Record.splitlines():
2415+ lookup = getattr(sources, 'lookup', getattr(sources, 'Lookup'))
2416+ while lookup(name):
2417+ record = getattr(sources, 'record', getattr(sources, 'Record'))
2418+ for l in record.splitlines():
2419 if not ": " in l:
2420 continue
2421 (field, value) = l.strip("\n").split(": ", 1)
2422@@ -55,13 +63,15 @@
2423
2424 if version is None:
2425 # Try the latest version
2426- version = sorted(urls,cmp=apt_pkg.VersionCompare)[0]
2427+ cmp = getattr(apt_pkg, 'version_compare',
2428+ getattr(apt_pkg, 'VersionCompare'))
2429+ version = sorted(urls,cmp=cmp)[0]
2430
2431 if not version in urls:
2432 raise errors.InvalidURL(path=url,
2433 extra='version %s not found' % version)
2434
2435- info("Retrieving Vcs locating from %s Debian version %s", name, version)
2436+ note("Retrieving Vcs locating from %s Debian version %s", name, version)
2437
2438 if "Bzr" in urls[version]:
2439 return urls[version]["Bzr"]
2440@@ -70,7 +80,7 @@
2441 try:
2442 import bzrlib.plugins.svn
2443 except ImportError:
2444- info("This package uses subversion. If you would like to "
2445+ note("This package uses subversion. If you would like to "
2446 "access it with bzr then please install bzr-svn "
2447 "and re-run the command.")
2448 else:
2449@@ -80,7 +90,7 @@
2450 try:
2451 import bzrlib.plugins.git
2452 except ImportError:
2453- info("This package uses git. If you would like to "
2454+ note("This package uses git. If you would like to "
2455 "access it with bzr then please install bzr-git "
2456 "and re-run the command.")
2457 else:
2458@@ -90,7 +100,7 @@
2459 try:
2460 import bzrlib.plugins.hg
2461 except ImportError:
2462- info("This package uses hg. If you would like to "
2463+ note("This package uses hg. If you would like to "
2464 "access it with bzr then please install bzr-hg"
2465 "and re-run the command.")
2466 else:
2467
2468=== modified file 'doc/user_manual/normal.rst'
2469--- doc/user_manual/normal.rst 2009-02-18 22:50:44 +0000
2470+++ doc/user_manual/normal.rst 2011-01-28 14:24:44 +0000
2471@@ -247,6 +247,42 @@
2472
2473 $ bzr merge-upstream --version 0.2 http://scruff.org/bzr/scruff.dev
2474
2475+Merging a package
2476+#################
2477+
2478+When merging a package you should use the ``merge-package`` command,
2479+which knows about packages in a way that ``merge`` does not. This
2480+knowledge allows it to reconcile deviations in the upstream
2481+ancestry so that they don't cause excess conflicts. (Note that the
2482+command works whether or not there are deviations in the upstream
2483+ancestry.)
2484+
2485+The command works in the same way as ``merge``. For example::
2486+
2487+ $ cd scruff-unstable/
2488+ $ bzr merge-package ../scruff-experimental
2489+
2490+will leave the branch in the same state as a normal merge allowing
2491+you to review the changes and commit.
2492+
2493+In a small number of cases, however, the source `upstream` and target
2494+`packaging` branches will have conflicts that cause the following error
2495+instead::
2496+
2497+ $ bzr merge-package ../scruff-highly-experimental
2498+ The upstream branches for the merge source and target have diverged.
2499+ Unfortunately, the attempt to fix this problem resulted in conflicts.
2500+ Please resolve these, commit and re-run the "merge-package" command to
2501+ finish.
2502+ Alternatively, until you commit you can use "bzr revert" to restore the
2503+ state of the unmerged branch.
2504+
2505+This will leave you in a conflicted tree, and you can deal with the conflicts
2506+and use ``resolve`` as normal. Once you have resolved all the conflicts you
2507+need to commit and then run the same ``merge-package`` command again to
2508+complete the operation. As with normal merges until you commit you can
2509+use ``revert`` to return you to the state before you started.
2510+
2511 Importing a source package from elsewhere
2512 #########################################
2513
2514@@ -266,7 +302,7 @@
2515 local path this can be any URI that Bazaar supports, for instance a
2516 ``http://`` URL. For instance::
2517
2518- $ bzr import-dsc --distribution debian ../scruff_0.2-1.1.dsc
2519+ $ bzr import-dsc ../scruff_0.2-1.1.dsc
2520
2521 The command will import the changes and then leave you with a tree that is
2522 the result of merging the changes in the source package in to the tip of
2523
2524=== modified file 'doc/user_manual/upstream_tarballs.rst'
2525--- doc/user_manual/upstream_tarballs.rst 2009-02-18 22:50:44 +0000
2526+++ doc/user_manual/upstream_tarballs.rst 2011-01-28 14:24:44 +0000
2527@@ -13,7 +13,9 @@
2528 If it can it will reconstruct the tarball from ``pristine-tar`` information
2529 stored in the branch. ``bzr-builddeb`` will store this information whenever
2530 it can, so using its commands such as ``merge-upstream`` and ``import-dsc``
2531-will lead to the best experience.
2532+will lead to the best experience. If you have an existing branch missing
2533+this information, you can use the ``import-upstream`` command to import a
2534+single tarball, after which the ``merge-upstream`` command should be used.
2535
2536 If there is no ``pristine-tar`` information then it will use apt to download
2537 the tarball from the archive if there is one of the correct version there.
2538
2539=== modified file 'errors.py'
2540--- errors.py 2009-04-16 09:30:49 +0000
2541+++ errors.py 2011-01-28 14:24:44 +0000
2542@@ -66,7 +66,7 @@
2543
2544
2545 class MissingChangelogError(BzrError):
2546- _fmt = 'Could not find changelog at %(locations)s.'
2547+ _fmt = 'Could not find changelog at %(location)s.'
2548
2549 def __init__(self, locations):
2550 BzrError.__init__(self, location=locations)
2551@@ -175,3 +175,48 @@
2552
2553 def __init__(self, error):
2554 BzrError.__init__(self, error=error)
2555+
2556+
2557+class SharedUpstreamConflictsWithTargetPackaging(BzrError):
2558+ _fmt = ('The upstream branches for the merge source and target have '
2559+ 'diverged. Unfortunately, the attempt to fix this problem '
2560+ 'resulted in conflicts. Please resolve these, commit and '
2561+ 're-run the "merge-package" command to finish. '
2562+ 'Alternatively, until you commit you can use "bzr revert" to '
2563+ 'restore the state of the unmerged branch.')
2564+
2565+
2566+class PerFileTimestampsNotSupported(BzrError):
2567+
2568+ _fmt = ("Per file timestamps are not supported by the "
2569+ "currently loaded version of bzrlib.")
2570+
2571+
2572+class NoPreviousUpload(BzrError):
2573+
2574+ _fmt = ("There was no previous upload to %(distribution)s.")
2575+
2576+ def __init__(self, distribution):
2577+ BzrError.__init__(self, distribution=distribution)
2578+
2579+
2580+class UnableToFindPreviousUpload(BzrError):
2581+
2582+ _fmt = ("Unable to determine the previous upload for --package-merge.")
2583+
2584+
2585+class InconsistentSourceFormatError(BzrError):
2586+
2587+ _fmt = ("Inconsistency between source format and version: version is "
2588+ "%(version_bool)snative, format is %(format_bool)snative.")
2589+
2590+ def __init__(self, version_native, format_native):
2591+ if version_native:
2592+ version_bool = ""
2593+ else:
2594+ version_bool = "not "
2595+ if format_native:
2596+ format_bool = ""
2597+ else:
2598+ format_bool = "not "
2599+ BzrError.__init__(self, version_bool=version_bool, format_bool=format_bool)
2600
2601=== modified file 'hooks.py'
2602--- hooks.py 2008-05-18 00:04:21 +0000
2603+++ hooks.py 2011-01-28 14:24:44 +0000
2604@@ -20,7 +20,7 @@
2605
2606 import subprocess
2607
2608-from bzrlib.trace import info
2609+from bzrlib.trace import note
2610
2611 from bzrlib.plugins.builddeb.errors import HookFailedError
2612
2613@@ -29,7 +29,7 @@
2614 hook = config.get_hook(hook_name)
2615 if hook is None:
2616 return
2617- info("Running %s as %s hook" % (hook, hook_name))
2618+ note("Running %s as %s hook" % (hook, hook_name))
2619 proc = subprocess.Popen(hook, shell=True,
2620 cwd=tree.abspath(wd))
2621 proc.wait()
2622
2623=== modified file 'import_dsc.py'
2624--- import_dsc.py 2009-07-26 16:44:17 +0000
2625+++ import_dsc.py 2011-01-28 14:24:44 +0000
2626@@ -29,290 +29,59 @@
2627 standard_b64decode,
2628 standard_b64encode,
2629 )
2630-try:
2631- import hashlib as md5
2632-except ImportError:
2633- import md5
2634+import errno
2635 import os
2636+import re
2637 import shutil
2638 import stat
2639-from subprocess import Popen, PIPE
2640-from StringIO import StringIO
2641+import subprocess
2642 import tempfile
2643
2644-from debian_bundle import deb822
2645-from debian_bundle.changelog import Version, Changelog, VersionError
2646+try:
2647+ from debian import deb822
2648+ from debian.changelog import Version, Changelog, VersionError
2649+except ImportError:
2650+ # Prior to 0.1.15 the debian module was called debian_bundle
2651+ from debian_bundle import deb822
2652+ from debian_bundle.changelog import Version, Changelog, VersionError
2653
2654 from bzrlib import (
2655 bzrdir,
2656- generate_ids,
2657 osutils,
2658- urlutils,
2659 )
2660 from bzrlib.config import ConfigObj
2661 from bzrlib.errors import (
2662+ AlreadyBranchError,
2663 BzrCommandError,
2664 NotBranchError,
2665- AlreadyBranchError,
2666+ NoSuchRevision,
2667+ NoWorkingTree,
2668+ UnrelatedBranches,
2669 )
2670-from bzrlib.export import export
2671-from bzrlib.osutils import file_iterator, isdir, basename, splitpath
2672 from bzrlib.revisionspec import RevisionSpec
2673 from bzrlib.revision import NULL_REVISION
2674-from bzrlib.trace import warning, info, mutter
2675-from bzrlib.transform import TreeTransform, cook_conflicts, resolve_conflicts
2676-from bzrlib.transport import get_transport
2677-
2678-from bzrlib.plugins.bzrtools.upstream_import import (
2679- names_of_files,
2680- add_implied_parents,
2681- )
2682-
2683+from bzrlib.trace import warning, mutter
2684+from bzrlib.transport import (
2685+ get_transport,
2686+ )
2687+
2688+from bzrlib.plugins.builddeb.bzrtools_import import import_dir
2689 from bzrlib.plugins.builddeb.errors import (
2690 PristineTarError,
2691- UnknownType,
2692+ TarFailed,
2693 UpstreamAlreadyImported,
2694 UpstreamBranchAlreadyMerged,
2695 )
2696-from bzrlib.plugins.builddeb.util import get_commit_info_from_changelog, get_snapshot_revision
2697-
2698-
2699-files_to_ignore = set(['.cvsignore', '.arch-inventory', '.bzrignore',
2700- '.gitignore', 'CVS', 'RCS', '.deps', '{arch}', '.arch-ids', '.svn',
2701- '.hg', '_darcs', '.git', '.shelf', '.bzr', '.bzr.backup', '.bzrtags',
2702- '.bzr-builddeb'])
2703-
2704-exclude_as_files = ['*/' + x for x in files_to_ignore]
2705-exclude_as_dirs = ['*/' + x + '/*' for x in files_to_ignore]
2706-exclude = exclude_as_files + exclude_as_dirs
2707-underscore_x = ['-x'] * len(exclude)
2708-ignore_arguments = []
2709-map(ignore_arguments.extend, zip(underscore_x, exclude))
2710-ignore_arguments = ignore_arguments + ['-x', '*,v']
2711-
2712-
2713-class DirWrapper(object):
2714- def __init__(self, fileobj, mode='r'):
2715- assert mode == 'r', mode
2716- self.root = os.path.realpath(fileobj.read())
2717-
2718- def __repr__(self):
2719- return 'DirWrapper(%r)' % self.root
2720-
2721- def getmembers(self, subdir=None):
2722- if subdir is not None:
2723- mydir = os.path.join(self.root, subdir)
2724- else:
2725- mydir = self.root
2726- for child in os.listdir(mydir):
2727- if subdir is not None:
2728- child = os.path.join(subdir, child)
2729- fi = FileInfo(self.root, child)
2730- yield fi
2731- if fi.isdir():
2732- for v in self.getmembers(child):
2733- yield v
2734-
2735- def extractfile(self, member):
2736- return open(member.fullpath)
2737-
2738-
2739-class FileInfo(object):
2740-
2741- def __init__(self, root, filepath):
2742- self.fullpath = os.path.join(root, filepath)
2743- self.root = root
2744- if filepath != '':
2745- self.name = os.path.join(basename(root), filepath)
2746- else:
2747- self.name = basename(root)
2748- self.type = None
2749- stat = os.lstat(self.fullpath)
2750- self.mode = stat.st_mode
2751- if self.isdir():
2752- self.name += '/'
2753-
2754- def __repr__(self):
2755- return 'FileInfo(%r)' % self.name
2756-
2757- def isreg(self):
2758- return stat.S_ISREG(self.mode)
2759-
2760- def isdir(self):
2761- return stat.S_ISDIR(self.mode)
2762-
2763- def issym(self):
2764- if stat.S_ISLNK(self.mode):
2765- self.linkname = os.readlink(self.fullpath)
2766- return True
2767- else:
2768- return False
2769-
2770- def islnk(self):
2771- # This could be accurate, but the use below seems like
2772- # it wouldn't really care
2773- return False
2774-
2775-
2776-def import_dir(tree, dir, file_ids_from=None):
2777- dir_input = StringIO(dir)
2778- dir_file = DirWrapper(dir_input)
2779- import_archive(tree, dir_file, file_ids_from=file_ids_from)
2780-
2781-
2782-def do_directory(tt, trans_id, tree, relative_path, path):
2783- if isdir(path) and tree.path2id(relative_path) is not None:
2784- tt.cancel_deletion(trans_id)
2785- else:
2786- tt.create_directory(trans_id)
2787-
2788-
2789-def should_ignore(relative_path):
2790- parts = splitpath(relative_path)
2791- if not parts:
2792- return False
2793- for part in parts:
2794- if part in files_to_ignore:
2795- return True
2796- if part.endswith(',v'):
2797- return True
2798-
2799-
2800-def top_directory(path):
2801- """Return the top directory given in a path."""
2802- parts = osutils.splitpath(osutils.normpath(path))
2803- if len(parts) > 0:
2804- return parts[0]
2805- return ''
2806-
2807-
2808-def common_directory(names):
2809- """Determine a single directory prefix from a list of names"""
2810- prefixes = set()
2811- prefixes.update(map(top_directory, names))
2812- if '' in prefixes:
2813- prefixes.remove('')
2814- if len(prefixes) != 1:
2815- return None
2816- prefix = prefixes.pop()
2817- if prefix == '':
2818- return None
2819- return prefix
2820-
2821-
2822-def import_archive(tree, archive_file, file_ids_from=None):
2823- prefix = common_directory(names_of_files(archive_file))
2824- tt = TreeTransform(tree)
2825-
2826- if file_ids_from is None:
2827- file_ids_from = []
2828-
2829- removed = set()
2830- for path, entry in tree.inventory.iter_entries():
2831- if entry.parent_id is None:
2832- continue
2833- trans_id = tt.trans_id_tree_path(path)
2834- tt.delete_contents(trans_id)
2835- removed.add(path)
2836-
2837- added = set()
2838- implied_parents = set()
2839- seen = set()
2840- for member in archive_file.getmembers():
2841- if member.type == 'g':
2842- # type 'g' is a header
2843- continue
2844- relative_path = member.name
2845- relative_path = osutils.normpath(relative_path)
2846- relative_path = relative_path.lstrip('/')
2847- if prefix is not None:
2848- relative_path = relative_path[len(prefix)+1:]
2849- if relative_path == '' or relative_path == '.':
2850- continue
2851- if should_ignore(relative_path):
2852- continue
2853- add_implied_parents(implied_parents, relative_path)
2854- trans_id = tt.trans_id_tree_path(relative_path)
2855- added.add(relative_path.rstrip('/'))
2856- path = tree.abspath(relative_path)
2857- if member.name in seen:
2858- if tt.final_kind(trans_id) == 'file':
2859- tt.set_executability(None, trans_id)
2860- tt.cancel_creation(trans_id)
2861- seen.add(member.name)
2862- if member.isreg() or member.islnk():
2863- tt.create_file(file_iterator(archive_file.extractfile(member)),
2864- trans_id)
2865- executable = (member.mode & 0111) != 0
2866- tt.set_executability(executable, trans_id)
2867- elif member.isdir():
2868- do_directory(tt, trans_id, tree, relative_path, path)
2869- elif member.issym():
2870- tt.create_symlink(member.linkname, trans_id)
2871- else:
2872- raise UnknownType(relative_path)
2873- if tt.tree_file_id(trans_id) is None:
2874- found = False
2875- for other_tree in file_ids_from:
2876- other_tree.lock_read()
2877- try:
2878- if other_tree.has_filename(relative_path):
2879- file_id = other_tree.path2id(relative_path)
2880- if file_id is not None:
2881- tt.version_file(file_id, trans_id)
2882- found = True
2883- break
2884- finally:
2885- other_tree.unlock()
2886- if not found:
2887- name = basename(member.name.rstrip('/'))
2888- file_id = generate_ids.gen_file_id(name)
2889- tt.version_file(file_id, trans_id)
2890-
2891- for relative_path in implied_parents.difference(added):
2892- if relative_path == "":
2893- continue
2894- trans_id = tt.trans_id_tree_path(relative_path)
2895- path = tree.abspath(relative_path)
2896- do_directory(tt, trans_id, tree, relative_path, path)
2897- if tt.tree_file_id(trans_id) is None:
2898- found = False
2899- for other_tree in file_ids_from:
2900- other_tree.lock_read()
2901- try:
2902- if other_tree.has_filename(relative_path):
2903- file_id = other_tree.path2id(relative_path)
2904- if file_id is not None:
2905- tt.version_file(file_id, trans_id)
2906- found = True
2907- break
2908- finally:
2909- other_tree.unlock()
2910- if not found:
2911- tt.version_file(trans_id, trans_id)
2912- added.add(relative_path)
2913-
2914- for path in removed.difference(added):
2915- tt.unversion_file(tt.trans_id_tree_path(path))
2916-
2917- for conflict in cook_conflicts(resolve_conflicts(tt), tt):
2918- warning(conflict)
2919- tt.apply()
2920-
2921-
2922-def open_file(path, transport, base_dir=None):
2923- """Open a file, possibly over a transport.
2924-
2925- Open the named path, using the transport if not None. If the transport and
2926- base_dir are not None, then path will be interpreted relative to base_dir.
2927- """
2928- if transport is None:
2929- base_dir, path = urlutils.split(path)
2930- transport = get_transport(base_dir)
2931- else:
2932- if base_dir is not None:
2933- path = urlutils.join(base_dir, path)
2934- return (transport.get(path), transport)
2935+from bzrlib.plugins.builddeb.util import (
2936+ export,
2937+ get_commit_info_from_changelog,
2938+ get_snapshot_revision,
2939+ md5sum_filename,
2940+ open_file_via_transport,
2941+ open_transport,
2942+ safe_decode,
2943+ subprocess_setup,
2944+ )
2945
2946
2947 class DscCache(object):
2948@@ -323,16 +92,20 @@
2949 self.transport = transport
2950
2951 def get_dsc(self, name):
2952+
2953 if name in self.cache:
2954 dsc1 = self.cache[name]
2955 else:
2956- (f1, transport) = open_file(name, self.transport)
2957+ # Obtain the dsc file, following any redirects as needed.
2958+ filename, transport = open_transport(name)
2959+ f1 = open_file_via_transport(filename, transport)
2960 try:
2961 dsc1 = deb822.Dsc(f1)
2962 finally:
2963 f1.close()
2964 self.cache[name] = dsc1
2965 self.transport_cache[name] = transport
2966+
2967 return dsc1
2968
2969 def get_transport(self, name):
2970@@ -355,6 +128,57 @@
2971 return -1
2972
2973
2974+
2975+def reconstruct_pristine_tar(dest, delta, dest_filename):
2976+ """Reconstruct a pristine tarball from a directory and a delta.
2977+
2978+ :param dest: Directory to pack
2979+ :param delta: pristine-tar delta
2980+ :param dest_filename: Destination filename
2981+ """
2982+ command = ["pristine-tar", "gentar", "-",
2983+ os.path.abspath(dest_filename)]
2984+ try:
2985+ proc = subprocess.Popen(command, stdin=subprocess.PIPE,
2986+ cwd=dest, preexec_fn=subprocess_setup,
2987+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
2988+ except OSError, e:
2989+ if e.errno == errno.ENOENT:
2990+ raise PristineTarError("pristine-tar is not installed")
2991+ else:
2992+ raise
2993+ (stdout, stderr) = proc.communicate(delta)
2994+ if proc.returncode != 0:
2995+ raise PristineTarError("Generating tar from delta failed: %s" % stdout)
2996+
2997+
2998+def make_pristine_tar_delta(dest, tarball_path):
2999+ """Create a pristine-tar delta for a tarball.
3000+
3001+ :param dest: Directory to generate pristine tar delta for
3002+ :param tarball_path: Path to the tarball
3003+ :return: pristine-tarball
3004+ """
3005+ # If tarball_path is relative, the cwd=dest parameter to Popen will make
3006+ # pristine-tar faaaail. pristine-tar doesn't use the VFS either, so we
3007+ # assume local paths.
3008+ tarball_path = osutils.abspath(tarball_path)
3009+ command = ["pristine-tar", "gendelta", tarball_path, "-"]
3010+ try:
3011+ proc = subprocess.Popen(command, stdout=subprocess.PIPE,
3012+ cwd=dest, preexec_fn=subprocess_setup,
3013+ stderr=subprocess.PIPE)
3014+ except OSError, e:
3015+ if e.errno == errno.ENOENT:
3016+ raise PristineTarError("pristine-tar is not installed")
3017+ else:
3018+ raise
3019+ (stdout, stderr) = proc.communicate()
3020+ if proc.returncode != 0:
3021+ raise PristineTarError("Generating delta from tar failed: %s" % stderr)
3022+ return stdout
3023+
3024+
3025 class DistributionBranchSet(object):
3026 """A collection of DistributionBranches with an ordering.
3027
3028@@ -573,28 +397,16 @@
3029 :return: True if the upstream branch contains the specified upstream
3030 version of the package. False otherwise.
3031 """
3032- tag_name = self.upstream_tag_name(version)
3033- if self._has_version(self.upstream_branch, tag_name, md5=md5):
3034- return True
3035- tag_name = self.upstream_tag_name(version, distro="debian")
3036- if self._has_version(self.upstream_branch, tag_name, md5=md5):
3037- return True
3038- tag_name = self.upstream_tag_name(version, distro="ubuntu")
3039- if self._has_version(self.upstream_branch, tag_name, md5=md5):
3040- return True
3041+ for tag_name in self.possible_upstream_tag_names(version):
3042+ if self._has_version(self.upstream_branch, tag_name, md5=md5):
3043+ return True
3044 return False
3045
3046 def has_upstream_version_in_packaging_branch(self, version, md5=None):
3047- assert isinstance(version, str)
3048- tag_name = self.upstream_tag_name(version)
3049- if self._has_version(self.branch, tag_name, md5=md5):
3050- return True
3051- tag_name = self.upstream_tag_name(version, distro="debian")
3052- if self._has_version(self.branch, tag_name, md5=md5):
3053- return True
3054- tag_name = self.upstream_tag_name(version, distro="ubuntu")
3055- if self._has_version(self.branch, tag_name, md5=md5):
3056- return True
3057+ assert isinstance(version, str), str(type(version))
3058+ for tag_name in self.possible_upstream_tag_names(version):
3059+ if self._has_version(self.branch, tag_name, md5=md5):
3060+ return True
3061 return False
3062
3063 def contained_versions(self, versions):
3064@@ -693,47 +505,62 @@
3065 :return: the revision id corresponding to the upstream portion
3066 of the version
3067 """
3068- tag_name = self.upstream_tag_name(version)
3069- if self._has_version(self.upstream_branch, tag_name):
3070- return self.upstream_branch.tags.lookup_tag(tag_name)
3071- tag_name = self.upstream_tag_name(version, distro="debian")
3072- if self._has_version(self.upstream_branch, tag_name):
3073- return self.upstream_branch.tags.lookup_tag(tag_name)
3074- tag_name = self.upstream_tag_name(version, distro="ubuntu")
3075- if self._has_version(self.upstream_branch, tag_name):
3076- return self.upstream_branch.tags.lookup_tag(tag_name)
3077+ for tag_name in self.possible_upstream_tag_names(version):
3078+ if self._has_version(self.upstream_branch, tag_name):
3079+ return self.upstream_branch.tags.lookup_tag(tag_name)
3080 tag_name = self.upstream_tag_name(version)
3081 return self.upstream_branch.tags.lookup_tag(tag_name)
3082
3083- def tag_version(self, version):
3084+ def possible_upstream_tag_names(self, version):
3085+ tags = [self.upstream_tag_name(version),
3086+ self.upstream_tag_name(version, distro="debian"),
3087+ self.upstream_tag_name(version, distro="ubuntu"),
3088+ "upstream/%s" % self.tag_name(version)]
3089+ return tags
3090+
3091+ def tag_version(self, version, revid=None):
3092 """Tags the branch's last revision with the given version.
3093
3094 Sets a tag on the last revision of the branch with a tag that refers
3095 to the version provided.
3096
3097 :param version: the Version object to derive the tag name from.
3098+ :param revid: the revid to associate the tag with, or None for the
3099+ tip of self.branch.
3100 :return: Name of the tag set
3101 """
3102 tag_name = self.tag_name(version)
3103- self.branch.tags.set_tag(tag_name,
3104- self.branch.last_revision())
3105+ if revid is None:
3106+ revid = self.branch.last_revision()
3107+ self.branch.tags.set_tag(tag_name, revid)
3108 return tag_name
3109
3110- def tag_upstream_version(self, version):
3111+ def tag_upstream_version(self, version, revid=None):
3112 """Tags the upstream branch's last revision with an upstream version.
3113
3114- Sets a tag on the last revision of the upstream branch with a tag
3115- that refers to the upstream part of the version provided.
3116+ Sets a tag on the last revision of the upstream branch and on the main
3117+ branch with a tag that refers to the upstream part of the version
3118+ provided.
3119
3120 :param version: the upstream part of the version number to derive the
3121 tag name from.
3122+ :param revid: the revid to associate the tag with, or None for the
3123+ tip of self.upstream_branch.
3124+ :return The tag name, revid of the added tag.
3125 """
3126 assert isinstance(version, str)
3127 tag_name = self.upstream_tag_name(version)
3128- self.upstream_branch.tags.set_tag(tag_name,
3129- self.upstream_branch.last_revision())
3130- self.branch.tags.set_tag(tag_name,
3131- self.upstream_branch.last_revision())
3132+ if revid is None:
3133+ revid = self.upstream_branch.last_revision()
3134+ self.upstream_branch.tags.set_tag(tag_name, revid)
3135+ try:
3136+ self.branch.repository.fetch(self.upstream_branch.repository,
3137+ revision_id=revid)
3138+ except NoSuchRevision:
3139+ # See bug lp:574223
3140+ pass
3141+ self.branch.tags.set_tag(tag_name, revid)
3142+ return tag_name, revid
3143
3144 def _default_config_for_tree(self, tree):
3145 # FIXME: shouldn't go to configobj directly
3146@@ -752,7 +579,6 @@
3147 tree.unlock()
3148 return config
3149
3150-
3151 def _is_tree_native(self, tree):
3152 config = self._default_config_for_tree(tree)
3153 if config is not None:
3154@@ -899,7 +725,7 @@
3155 """Return the list of parents for a specific version.
3156
3157 This method returns the list of revision ids that should be parents
3158- for importing a specifc package version. The specific package version
3159+ for importing a specific package version. The specific package version
3160 is the first element of the list of versions passed.
3161
3162 The parents are determined by looking at the other versions in the
3163@@ -984,7 +810,7 @@
3164 "Can't pull upstream with no tree"
3165 self.upstream_tree.pull(up_pull_branch,
3166 stop_revision=pull_revision)
3167- self.tag_upstream_version(version)
3168+ self.tag_upstream_version(version, revid=pull_revision)
3169 self.branch.fetch(self.upstream_branch, last_revision=pull_revision)
3170 self.upstream_branch.tags.merge_to(self.branch.tags)
3171
3172@@ -1014,7 +840,7 @@
3173 % (str(version), pull_revision))
3174 assert self.tree is not None, "Can't pull branch with no tree"
3175 self.tree.pull(pull_branch.branch, stop_revision=pull_revision)
3176- self.tag_version(version)
3177+ self.tag_version(version, revid=pull_revision)
3178 if not native and not self.has_upstream_version(version.upstream_version):
3179 if pull_branch.has_upstream_version(version.upstream_version):
3180 self.pull_upstream_from_branch(pull_branch,
3181@@ -1079,33 +905,18 @@
3182 return real_parents
3183
3184 def _fetch_upstream_to_branch(self, revid):
3185- """Fetch the revision from the upstream branch in to the pacakging one.
3186-
3187- This will unlock self.tree, then re-lock it and fetch. This is
3188- necessary as if the two branches share a repository the branch
3189- won't see any revisions added by the upstream branch since self.tree
3190- was locked.
3191-
3192- It will check that the last revision is the same before and after,
3193- and that there are no working tree changes, to prevent unexpected
3194- things happening if say a commit was done in this time.
3195+ """Fetch the revision from the upstream branch in to the packaging one.
3196 """
3197- if self.tree.is_locked():
3198- last_revision = self.branch.last_revision()
3199- # Unlock the tree and lock it again
3200- self.tree.unlock()
3201- self.tree.lock_write()
3202- assert self.branch.last_revision() == last_revision, \
3203- "Branch committed to while refreshing it. Not proceeding."
3204- assert not self.tree.changes_from(
3205- self.tree.basis_tree()).has_changed(), \
3206- "Treee altered while refreshing it. Not proceeding."
3207+ # Make sure we see any revisions added by the upstream branch
3208+ # since self.tree was locked.
3209+ self.branch.repository.refresh_data()
3210 self.branch.fetch(self.upstream_branch, last_revision=revid)
3211 self.upstream_branch.tags.merge_to(self.branch.tags)
3212
3213 def import_upstream(self, upstream_part, version, md5, upstream_parents,
3214 upstream_tarball=None, upstream_branch=None,
3215- upstream_revision=None, timestamp=None, author=None):
3216+ upstream_revision=None, timestamp=None, author=None,
3217+ file_ids_from=None):
3218 """Import an upstream part on to the upstream branch.
3219
3220 This imports the upstream part of the code and places it on to
3221@@ -1118,6 +929,7 @@
3222 :param upstream_parents: the parents to give the upstream revision
3223 :param timestamp: a tuple of (timestamp, timezone) to use for
3224 the commit, or None to use the current time.
3225+ :return: (tag_name, revision_id) of the imported tarball.
3226 """
3227 # Should we just dump the upstream part on whatever is currently
3228 # there, or try and pull all of the other upstream versions
3229@@ -1140,6 +952,7 @@
3230 return br.repository.revision_tree(br.last_revision())
3231 upstream_trees = [get_last_revision_tree(o.upstream_branch)
3232 for o in other_branches]
3233+ target_tree = None
3234 if upstream_branch is not None:
3235 if upstream_revision is None:
3236 upstream_revision = upstream_branch.last_revision()
3237@@ -1147,18 +960,32 @@
3238 last_revision=upstream_revision)
3239 upstream_branch.tags.merge_to(self.upstream_branch.tags)
3240 upstream_parents.append(upstream_revision)
3241- upstream_trees.insert(0,
3242- self.upstream_branch.repository.revision_tree(
3243- upstream_revision))
3244- import_dir(self.upstream_tree, upstream_part,
3245- file_ids_from=upstream_trees + [self.tree])
3246+ target_tree = self.upstream_branch.repository.revision_tree(
3247+ upstream_revision)
3248+ if file_ids_from is not None:
3249+ upstream_trees = file_ids_from + upstream_trees
3250+ if self.tree:
3251+ self_tree = self.tree
3252+ self_tree.lock_write() # might also be upstream tree for dh_make
3253+ else:
3254+ self_tree = self.get_branch_tip_revtree()
3255+ self_tree.lock_read()
3256+ try:
3257+ import_dir(self.upstream_tree, upstream_part,
3258+ file_ids_from=[self_tree] + upstream_trees,
3259+ target_tree=target_tree)
3260+ finally:
3261+ self_tree.unlock()
3262 self.upstream_tree.set_parent_ids(upstream_parents)
3263 revprops = {"deb-md5": md5}
3264 if upstream_tarball is not None:
3265 delta = self.make_pristine_tar_delta(self.upstream_tree,
3266 upstream_tarball)
3267 uuencoded = standard_b64encode(delta)
3268- revprops["deb-pristine-delta"] = uuencoded
3269+ if upstream_tarball.endswith(".tar.bz2"):
3270+ revprops["deb-pristine-delta-bz2"] = uuencoded
3271+ else:
3272+ revprops["deb-pristine-delta"] = uuencoded
3273 if author is not None:
3274 revprops['authors'] = author
3275 timezone=None
3276@@ -1168,12 +995,41 @@
3277 revid = self.upstream_tree.commit("Import upstream version %s" \
3278 % (version,),
3279 revprops=revprops, timestamp=timestamp, timezone=timezone)
3280- self.tag_upstream_version(version)
3281- return revid
3282+ tag_name, _ = self.tag_upstream_version(version, revid=revid)
3283+ return tag_name, revid
3284+
3285+ def import_upstream_tarball(self, tarball_filename, version, parents,
3286+ md5sum=None, upstream_branch=None, upstream_revision=None):
3287+ """Import an upstream part to the upstream branch.
3288+
3289+ :param tarball_filename: The tarball to import.
3290+ :param version: The upstream version to import.
3291+ :param parents: The tarball-branch parents to use for the import.
3292+ If an upstream branch is supplied, its automatically added to
3293+ parents.
3294+ :param upstream_branch: An upstream branch to associate with the
3295+ tarball.
3296+ :param upstream_revision: Upstream revision id
3297+ :param md5sum: hex digest of the md5sum of the tarball, if known.
3298+ :return: (tag_name, revision_id) of the imported tarball.
3299+ """
3300+ if not md5sum:
3301+ md5sum = md5sum_filename(tarball_filename)
3302+ tarball_dir = self._extract_tarball_to_tempdir(tarball_filename)
3303+ try:
3304+ return self.import_upstream(tarball_dir, version, md5sum, parents,
3305+ upstream_tarball=tarball_filename,
3306+ upstream_branch=upstream_branch,
3307+ upstream_revision=upstream_revision)
3308+ finally:
3309+ shutil.rmtree(tarball_dir)
3310+
3311+ def get_branch_tip_revtree(self):
3312+ return self.branch.repository.revision_tree(
3313+ self.branch.last_revision())
3314
3315 def _mark_native_config(self, native):
3316- poss_native_tree = self.branch.repository.revision_tree(
3317- self.branch.last_revision())
3318+ poss_native_tree = self.get_branch_tip_revtree()
3319 current_native = self._is_tree_native(poss_native_tree)
3320 current_config = self._default_config_for_tree(poss_native_tree)
3321 dirname = os.path.join(self.tree.basedir,
3322@@ -1219,7 +1075,7 @@
3323 '.bzr-builddeb/default.conf'])
3324
3325 def import_debian(self, debian_part, version, parents, md5,
3326- native=False, timestamp=None):
3327+ native=False, timestamp=None, file_ids_from=None):
3328 """Import the debian part of a source package.
3329
3330 :param debian_part: the path of a directory containing the unpacked
3331@@ -1259,6 +1115,8 @@
3332 debian_trees = [get_last_revision_tree(o.branch)
3333 for o in other_branches]
3334 parent_trees = []
3335+ if file_ids_from is not None:
3336+ parent_trees = file_ids_from[:]
3337 for parent in parents:
3338 parent_trees.append(self.branch.repository.revision_tree(
3339 parent))
3340@@ -1298,54 +1156,9 @@
3341 timezone = timestamp[1]
3342 timestamp = timestamp[0]
3343 self._mark_native_config(native)
3344- self.tree.commit(message, revprops=revprops, timestamp=timestamp,
3345+ revid = self.tree.commit(message, revprops=revprops, timestamp=timestamp,
3346 timezone=timezone)
3347- self.tag_version(version)
3348-
3349- def _get_dsc_part(self, dsc, end):
3350- """Get the path and md5 of a file ending with end in dsc."""
3351- files = dsc['files']
3352- for file_info in files:
3353- name = file_info['name']
3354- if name.endswith(end):
3355- filename = name
3356- md5 = file_info['md5sum']
3357- return (filename, md5)
3358- return (None, None)
3359-
3360- def get_upstream_part(self, dsc):
3361- """Gets the information about the upstream part from the dsc.
3362-
3363- :param dsc: a deb822.Dsc object to take the information from.
3364- :return: a tuple (path, md5), both strings, the former being
3365- the path to the .orig.tar.gz, the latter being the md5
3366- reported for it. If there is no upstream part both will
3367- be None.
3368- """
3369- return self._get_dsc_part(dsc, ".orig.tar.gz")
3370-
3371- def get_diff_part(self, dsc):
3372- """Gets the information about the diff part from the dsc.
3373-
3374- :param dsc: a deb822.Dsc object to take the information from.
3375- :return: a tuple (path, md5), both strings, the former being
3376- the path to the .diff.gz, the latter being the md5
3377- reported for it. If there is no diff part both will be
3378- None.
3379- """
3380- return self._get_dsc_part(dsc, ".diff.gz")
3381-
3382- def get_native_part(self, dsc):
3383- """Gets the information about the native part from the dsc.
3384-
3385- :param dsc: a deb822.Dsc object to take the information from.
3386- :return: a tuple (path, md5), both strings, the former being
3387- the path to the .tar.gz, the latter being the md5 reported
3388- for it. If there is not native part both will be None.
3389- """
3390- (path, md5) = self._get_dsc_part(dsc, ".tar.gz")
3391- assert not path.endswith(".orig.tar.gz")
3392- return (path, md5)
3393+ self.tag_version(version, revid=revid)
3394
3395 def upstream_parents(self, versions, version):
3396 """Get the parents for importing a new upstream.
3397@@ -1399,21 +1212,13 @@
3398 cl.parse_changelog(open(cl_filename).read(), strict=False)
3399 return cl
3400
3401- def extract_dsc(self, dsc_filename):
3402- """Extract a dsc file in to a temporary directory."""
3403- tempdir = tempfile.mkdtemp()
3404- dsc_filename = os.path.abspath(dsc_filename)
3405- proc = Popen("dpkg-source -su -x %s" % (dsc_filename,), shell=True,
3406- cwd=tempdir, stdout=PIPE, stderr=PIPE)
3407- (stdout, stderr) = proc.communicate()
3408- assert proc.returncode == 0, "dpkg-source -x failed, output:\n%s\n%s" % \
3409- (stdout, stderr)
3410- return tempdir
3411-
3412 def _do_import_package(self, version, versions, debian_part, md5,
3413 upstream_part, upstream_md5, upstream_tarball=None,
3414- timestamp=None, author=None):
3415- pull_branch = self.branch_to_pull_version_from(version, md5)
3416+ timestamp=None, author=None, file_ids_from=None,
3417+ pull_debian=True):
3418+ pull_branch = None
3419+ if pull_debian:
3420+ pull_branch = self.branch_to_pull_version_from(version, md5)
3421 if pull_branch is not None:
3422 if (self.branch_to_pull_upstream_from(version.upstream_version,
3423 upstream_md5)
3424@@ -1438,11 +1243,12 @@
3425 # from another branch:
3426 upstream_parents = self.upstream_parents(versions,
3427 version.upstream_version)
3428- new_revid = self.import_upstream(upstream_part,
3429+ _, new_revid = self.import_upstream(upstream_part,
3430 version.upstream_version,
3431 upstream_md5, upstream_parents,
3432 upstream_tarball=upstream_tarball,
3433- timestamp=timestamp, author=author)
3434+ timestamp=timestamp, author=author,
3435+ file_ids_from=file_ids_from)
3436 self._fetch_upstream_to_branch(new_revid)
3437 else:
3438 mutter("We already have the needed upstream part")
3439@@ -1450,7 +1256,7 @@
3440 force_upstream_parent=imported_upstream)
3441 # Now we have the list of parents we need to import the .diff.gz
3442 self.import_debian(debian_part, version, parents, md5,
3443- timestamp=timestamp)
3444+ timestamp=timestamp, file_ids_from=file_ids_from)
3445
3446 def get_native_parents(self, version, versions):
3447 last_contained_version = self.last_contained_version(versions)
3448@@ -1492,14 +1298,17 @@
3449
3450
3451 def _import_native_package(self, version, versions, debian_part, md5,
3452- timestamp=None):
3453- pull_branch = self.branch_to_pull_version_from(version, md5)
3454+ timestamp=None, file_ids_from=None, pull_debian=True):
3455+ pull_branch = None
3456+ if pull_debian:
3457+ pull_branch = self.branch_to_pull_version_from(version, md5)
3458 if pull_branch is not None:
3459 self.pull_version_from_branch(pull_branch, version, native=True)
3460 else:
3461 parents = self.get_native_parents(version, versions)
3462 self.import_debian(debian_part, version, parents, md5,
3463- native=True, timestamp=timestamp)
3464+ native=True, timestamp=timestamp,
3465+ file_ids_from=file_ids_from)
3466
3467 def _get_safe_versions_from_changelog(self, cl):
3468 versions = []
3469@@ -1510,7 +1319,8 @@
3470 break
3471 return versions
3472
3473- def import_package(self, dsc_filename, use_time_from_changelog=False):
3474+ def import_package(self, dsc_filename, use_time_from_changelog=True,
3475+ file_ids_from=None, pull_debian=True):
3476 """Import a source package.
3477
3478 :param dsc_filename: a path to a .dsc file for the version
3479@@ -1521,29 +1331,15 @@
3480 base_path = osutils.dirname(dsc_filename)
3481 dsc = deb822.Dsc(open(dsc_filename).read())
3482 version = Version(dsc['Version'])
3483- name = dsc['Source']
3484- upstream_tarball = None
3485- for part in dsc['files']:
3486- if part['name'].endswith(".orig.tar.gz"):
3487- assert upstream_tarball is None, "Two .orig.tar.gz?"
3488- upstream_tarball = os.path.abspath(
3489- os.path.join(base_path, part['name']))
3490- tempdir = self.extract_dsc(dsc_filename)
3491+ format = dsc.get('Format', '1.0').strip()
3492+ extractor_cls = SOURCE_EXTRACTORS.get(format)
3493+ if extractor_cls is None:
3494+ raise AssertionError("Don't know how to import source format %s yet"
3495+ % format)
3496+ extractor = extractor_cls(dsc_filename, dsc)
3497 try:
3498- # TODO: make more robust against strange .dsc files.
3499- upstream_part = os.path.join(tempdir,
3500- "%s-%s.orig" % (name, str(version.upstream_version)))
3501- debian_part = os.path.join(tempdir,
3502- "%s-%s" % (name, str(version.upstream_version)))
3503- native = False
3504- if not os.path.exists(upstream_part):
3505- mutter("It's a native package")
3506- native = True
3507- (_, md5) = self.get_native_part(dsc)
3508- else:
3509- (_, upstream_md5) = self.get_upstream_part(dsc)
3510- (_, md5) = self.get_diff_part(dsc)
3511- cl = self.get_changelog_from_source(debian_part)
3512+ extractor.extract()
3513+ cl = self.get_changelog_from_source(extractor.extracted_debianised)
3514 timestamp = None
3515 author = None
3516 if use_time_from_changelog and len(cl._blocks) > 0:
3517@@ -1552,36 +1348,52 @@
3518 time_tuple = rfc822.parsedate_tz(raw_timestamp)
3519 if time_tuple is not None:
3520 timestamp = (time.mktime(time_tuple[:9]), time_tuple[9])
3521- author = cl.author
3522+ author = safe_decode(cl.author)
3523 versions = self._get_safe_versions_from_changelog(cl)
3524 assert not self.has_version(version), \
3525 "Trying to import version %s again" % str(version)
3526 #TODO: check that the versions list is correctly ordered,
3527 # as some methods assume that, and it's not clear what
3528 # should happen if it isn't.
3529- if not native:
3530- self._do_import_package(version, versions, debian_part, md5,
3531- upstream_part, upstream_md5,
3532- upstream_tarball=upstream_tarball,
3533- timestamp=timestamp, author=author)
3534+ if extractor.extracted_upstream is not None:
3535+ self._do_import_package(version, versions,
3536+ extractor.extracted_debianised,
3537+ extractor.unextracted_debian_md5,
3538+ extractor.extracted_upstream,
3539+ extractor.unextracted_upstream_md5,
3540+ upstream_tarball=extractor.unextracted_upstream,
3541+ timestamp=timestamp, author=author,
3542+ file_ids_from=file_ids_from,
3543+ pull_debian=pull_debian)
3544 else:
3545- self._import_native_package(version, versions, debian_part,
3546- md5, timestamp=timestamp)
3547+ self._import_native_package(version, versions,
3548+ extractor.extracted_debianised,
3549+ extractor.unextracted_debian_md5,
3550+ timestamp=timestamp, file_ids_from=file_ids_from,
3551+ pull_debian=pull_debian)
3552 finally:
3553- shutil.rmtree(tempdir)
3554+ extractor.cleanup()
3555
3556- def _extract_upstream_tree(self, upstream_tip, basedir):
3557- # Extract that to a tempdir so we can get a working
3558- # tree for it.
3559+ def extract_upstream_tree(self, upstream_tip, basedir):
3560+ """Extract upstream_tip to a tempdir as a working tree."""
3561 # TODO: should stack rather than trying to use the repository,
3562 # as that will be more efficient.
3563+ # TODO: remove the _extract_upstream_tree alias below.
3564 to_location = os.path.join(basedir, "upstream")
3565- dir_to = self.branch.bzrdir.sprout(to_location,
3566+ # Use upstream_branch if it has been set, otherwise self.branch.
3567+ source_branch = self.upstream_branch or self.branch
3568+ dir_to = source_branch.bzrdir.sprout(to_location,
3569 revision_id=upstream_tip,
3570 accelerator_tree=self.tree)
3571- self.upstream_tree = dir_to.open_workingtree()
3572+ try:
3573+ self.upstream_tree = dir_to.open_workingtree()
3574+ except NoWorkingTree:
3575+ # Handle shared treeless repo's.
3576+ self.upstream_tree = dir_to.create_workingtree()
3577 self.upstream_branch = self.upstream_tree.branch
3578
3579+ _extract_upstream_tree = extract_upstream_tree
3580+
3581 def _create_empty_upstream_tree(self, basedir):
3582 to_location = os.path.join(basedir, "upstream")
3583 to_transport = get_transport(to_location)
3584@@ -1604,82 +1416,111 @@
3585 existing_bzrdir.create_workingtree()
3586 self.upstream_branch = branch
3587 self.upstream_tree = branch.bzrdir.open_workingtree()
3588+ if self.tree:
3589+ root_id = self.tree.path2id('')
3590+ else:
3591+ tip = self.get_branch_tip_revtree()
3592+ tip.lock_read()
3593+ try:
3594+ root_id = tip.path2id('')
3595+ finally:
3596+ tip.unlock()
3597+ if root_id:
3598+ self.upstream_tree.set_root_id(root_id)
3599
3600 def _extract_tarball_to_tempdir(self, tarball_filename):
3601 tempdir = tempfile.mkdtemp()
3602+ if tarball_filename.endswith(".tar.bz2"):
3603+ tar_args = 'xjf'
3604+ else:
3605+ tar_args = 'xzf'
3606 try:
3607- assert os.system("tar xzf %s -C %s --strip-components 1"
3608- % (tarball_filename, tempdir)) == 0
3609+ proc = subprocess.Popen(["tar", tar_args, tarball_filename, "-C",
3610+ tempdir, "--strip-components", "1"],
3611+ preexec_fn=subprocess_setup)
3612+ proc.communicate()
3613+ if proc.returncode != 0:
3614+ raise TarFailed("extract", tarball_filename)
3615 return tempdir
3616 except:
3617 shutil.rmtree(tempdir)
3618 raise
3619
3620 def _revid_of_upstream_version_from_branch(self, version):
3621+ """The private method below will go away eventually."""
3622+ return self.revid_of_upstream_version_from_branch(version)
3623+
3624+ def revid_of_upstream_version_from_branch(self, version):
3625+ # TODO: remove the _revid_of_upstream_version_from_branch alias below.
3626 assert isinstance(version, str)
3627- tag_name = self.upstream_tag_name(version)
3628- if self._has_version(self.branch, tag_name):
3629- return self.branch.tags.lookup_tag(tag_name)
3630- tag_name = self.upstream_tag_name(version, distro="debian")
3631- if self._has_version(self.branch, tag_name):
3632- return self.branch.tags.lookup_tag(tag_name)
3633- tag_name = self.upstream_tag_name(version, distro="ubuntu")
3634- if self._has_version(self.branch, tag_name):
3635- return self.branch.tags.lookup_tag(tag_name)
3636+ for tag_name in self.possible_upstream_tag_names(version):
3637+ if self._has_version(self.branch, tag_name):
3638+ return self.branch.tags.lookup_tag(tag_name)
3639 tag_name = self.upstream_tag_name(version)
3640 return self.branch.tags.lookup_tag(tag_name)
3641
3642+ _revid_of_upstream_version_from_branch = revid_of_upstream_version_from_branch
3643+
3644+ def _export_previous_upstream_tree(self, previous_version, tempdir, upstream_branch=None):
3645+ assert isinstance(previous_version, str), \
3646+ "Should pass upstream version as str, not Version."
3647+ previous_upstream_revision = get_snapshot_revision(previous_version)
3648+ if self.has_upstream_version_in_packaging_branch(previous_version):
3649+ upstream_tip = self.revid_of_upstream_version_from_branch(
3650+ previous_version)
3651+ self.extract_upstream_tree(upstream_tip, tempdir)
3652+ elif (upstream_branch is not None and
3653+ previous_upstream_revision is not None):
3654+ upstream_tip = RevisionSpec.from_string(previous_upstream_revision).as_revision_id(upstream_branch)
3655+ assert isinstance(upstream_tip, str)
3656+ self.extract_upstream_tree(upstream_tip, tempdir)
3657+ else:
3658+ raise BzrCommandError("Unable to find the tag for the "
3659+ "previous upstream version, %s, in the branch: "
3660+ "%s" % (
3661+ previous_version,
3662+ self.upstream_tag_name(previous_version)))
3663+
3664 def merge_upstream(self, tarball_filename, version, previous_version,
3665- upstream_branch=None, upstream_revision=None, merge_type=None):
3666+ upstream_branch=None, upstream_revision=None, merge_type=None,
3667+ force=False):
3668 assert self.upstream_branch is None, \
3669 "Should use self.upstream_branch if set"
3670+ assert isinstance(version, str), \
3671+ "Should pass version as str not %s" % str(type(version))
3672+ assert isinstance(previous_version, str) or previous_version is None, \
3673+ "Should pass previous_version as str not %s" % str(
3674+ type(previous_version))
3675 tempdir = tempfile.mkdtemp(dir=os.path.join(self.tree.basedir, '..'))
3676 try:
3677- previous_upstream_revision = get_snapshot_revision(previous_version.upstream_version)
3678 if previous_version is not None:
3679- if self.has_upstream_version_in_packaging_branch(
3680- previous_version.upstream_version):
3681- upstream_tip = self._revid_of_upstream_version_from_branch(
3682- previous_version.upstream_version)
3683- self._extract_upstream_tree(upstream_tip, tempdir)
3684- elif (upstream_branch is not None and
3685- previous_upstream_revision is not None):
3686- upstream_tip = RevisionSpec.from_string(previous_upstream_revision).as_revision_id(upstream_branch)
3687- assert isinstance(upstream_tip, str)
3688- self._extract_upstream_tree(upstream_tip, tempdir)
3689- else:
3690- raise BzrCommandError("Unable to find the tag for the "
3691- "previous upstream version, %s, in the branch: "
3692- "%s" % (
3693- previous_version.upstream_version,
3694- self.upstream_tag_name(
3695- previous_version.upstream_version)))
3696+ self._export_previous_upstream_tree(previous_version, tempdir,
3697+ upstream_branch)
3698 else:
3699 self._create_empty_upstream_tree(tempdir)
3700- if self.has_upstream_version_in_packaging_branch(version.upstream_version):
3701+ if self.has_upstream_version_in_packaging_branch(version):
3702 raise UpstreamAlreadyImported(version)
3703+ if upstream_branch is not None:
3704+ upstream_branch.lock_read()
3705 try:
3706 if upstream_branch is not None:
3707- upstream_branch.lock_read()
3708- if upstream_revision is not None:
3709+ if upstream_revision is None:
3710 upstream_revision = upstream_branch.last_revision()
3711 graph = self.branch.repository.get_graph(
3712 other_repository=upstream_branch.repository)
3713- if graph.is_ancestor(upstream_revision,
3714+ if not force and graph.is_ancestor(upstream_revision,
3715 self.branch.last_revision()):
3716 raise UpstreamBranchAlreadyMerged
3717 tarball_filename = os.path.abspath(tarball_filename)
3718- m = md5.md5()
3719- m.update(open(tarball_filename).read())
3720- md5sum = m.hexdigest()
3721+ md5sum = md5sum_filename(tarball_filename)
3722 tarball_dir = self._extract_tarball_to_tempdir(tarball_filename)
3723 try:
3724 # FIXME: should use upstream_parents()?
3725 parents = []
3726 if self.upstream_branch.last_revision() != NULL_REVISION:
3727 parents = [self.upstream_branch.last_revision()]
3728- new_revid = self.import_upstream(tarball_dir,
3729- version.upstream_version,
3730+ _, new_revid = self.import_upstream(tarball_dir,
3731+ version,
3732 md5sum, parents, upstream_tarball=tarball_filename,
3733 upstream_branch=upstream_branch,
3734 upstream_revision=upstream_revision)
3735@@ -1687,8 +1528,20 @@
3736 finally:
3737 shutil.rmtree(tarball_dir)
3738 if self.branch.last_revision() != NULL_REVISION:
3739- conflicts = self.tree.merge_from_branch(
3740- self.upstream_branch, merge_type=merge_type)
3741+ try:
3742+ conflicts = self.tree.merge_from_branch(
3743+ self.upstream_branch, merge_type=merge_type)
3744+ except UnrelatedBranches:
3745+ # Bug lp:515367 where the first upstream tarball is
3746+ # missing a proper history link and a criss-cross merge
3747+ # then recurses and finds no deeper ancestor.
3748+ if len(parents) != 2:
3749+ # Very first import... shouldn't be possible.
3750+ raise
3751+ # Use the previous upstream import as the from revision
3752+ conflicts = self.tree.merge_from_branch(
3753+ self.upstream_branch, merge_type=merge_type,
3754+ from_revision=parents[0])
3755 else:
3756 # Pull so that merge-upstream allows you to start a branch
3757 # from upstream tarball.
3758@@ -1702,39 +1555,49 @@
3759 finally:
3760 shutil.rmtree(tempdir)
3761
3762- def has_pristine_tar_delta(self, revid):
3763- rev = self.branch.repository.get_revision(revid)
3764- return 'deb-pristine-delta' in rev.properties
3765-
3766- def pristine_tar_delta(self, revid):
3767- rev = self.branch.repository.get_revision(revid)
3768- uuencoded = rev.properties['deb-pristine-delta']
3769+ def has_pristine_tar_delta(self, rev):
3770+ return ('deb-pristine-delta' in rev.properties
3771+ or 'deb-pristine-delta-bz2' in rev.properties)
3772+
3773+ def pristine_tar_format(self, rev):
3774+ if 'deb-pristine-delta' in rev.properties:
3775+ return 'gz'
3776+ elif 'deb-pristine-delta-bz2' in rev.properties:
3777+ return 'bz2'
3778+ assert self.has_pristine_tar_delta(rev)
3779+ raise AssertionError("Not handled new delta type in "
3780+ "pristine_tar_format")
3781+
3782+ def pristine_tar_delta(self, rev):
3783+ if 'deb-pristine-delta' in rev.properties:
3784+ uuencoded = rev.properties['deb-pristine-delta']
3785+ elif 'deb-pristine-delta-bz2' in rev.properties:
3786+ uuencoded = rev.properties['deb-pristine-delta-bz2']
3787+ else:
3788+ assert self.has_pristine_tar_delta(rev)
3789+ raise AssertionError("Not handled new delta type in "
3790+ "pristine_tar_delta")
3791 delta = standard_b64decode(uuencoded)
3792 return delta
3793
3794 def reconstruct_pristine_tar(self, revid, package, version,
3795 dest_filename):
3796 """Reconstruct a pristine-tar tarball from a bzr revision."""
3797- if not os.path.exists("/usr/bin/pristine-tar"):
3798- raise PristineTarError("/usr/bin/pristine-tar is not available")
3799 tree = self.branch.repository.revision_tree(revid)
3800 tmpdir = tempfile.mkdtemp(prefix="builddeb-pristine-")
3801 try:
3802 dest = os.path.join(tmpdir, "orig")
3803- export(tree, dest, format='dir')
3804- delta = self.pristine_tar_delta(revid)
3805- command = ["/usr/bin/pristine-tar", "gentar", "-",
3806- os.path.abspath(dest_filename)]
3807- proc = Popen(command, stdin=PIPE, cwd=dest)
3808- (stdout, stderr) = proc.communicate(delta)
3809- if proc.returncode != 0:
3810- raise PristineTarError("Generating tar from delta failed: %s" % stderr)
3811+ rev = self.branch.repository.get_revision(revid)
3812+ if self.has_pristine_tar_delta(rev):
3813+ export(tree, dest, format='dir')
3814+ delta = self.pristine_tar_delta(rev)
3815+ reconstruct_pristine_tar(dest, delta, dest_filename)
3816+ else:
3817+ export(tree, dest_filename, require_per_file_timestamps=True)
3818 finally:
3819 shutil.rmtree(tmpdir)
3820
3821 def make_pristine_tar_delta(self, tree, tarball_path):
3822- if not os.path.exists("/usr/bin/pristine-tar"):
3823- raise PristineTarError("/usr/bin/pristine-tar is not available")
3824 tmpdir = tempfile.mkdtemp(prefix="builddeb-pristine-")
3825 try:
3826 dest = os.path.join(tmpdir, "orig")
3827@@ -1745,12 +1608,133 @@
3828 export(tree, dest, format='dir')
3829 finally:
3830 tree.unlock()
3831- command = ["/usr/bin/pristine-tar", "gendelta", tarball_path, "-"]
3832- info(" ".join(command))
3833- proc = Popen(command, stdout=PIPE, cwd=dest)
3834- (stdout, stderr) = proc.communicate()
3835- if proc.returncode != 0:
3836- raise PristineTarError("Generating delta from tar failed: %s" % stderr)
3837- return stdout
3838+ return make_pristine_tar_delta(dest, tarball_path)
3839 finally:
3840 shutil.rmtree(tmpdir)
3841+
3842+
3843+class SourceExtractor(object):
3844+ """A class to extract a source package to its constituent parts"""
3845+
3846+ def __init__(self, dsc_path, dsc):
3847+ self.dsc_path = dsc_path
3848+ self.dsc = dsc
3849+ self.extracted_upstream = None
3850+ self.extracted_debianised = None
3851+ self.unextracted_upstream = None
3852+ self.unextracted_debian_md5 = None
3853+ self.unextracted_upstream_md5 = None
3854+
3855+ def extract(self):
3856+ """Extract the package to a new temporary directory."""
3857+ self.tempdir = tempfile.mkdtemp()
3858+ dsc_filename = os.path.abspath(self.dsc_path)
3859+ proc = subprocess.Popen("dpkg-source -su -x %s" % (dsc_filename,), shell=True,
3860+ cwd=self.tempdir, stdout=subprocess.PIPE,
3861+ stderr=subprocess.STDOUT, preexec_fn=subprocess_setup)
3862+ (stdout, _) = proc.communicate()
3863+ assert proc.returncode == 0, "dpkg-source -x failed, output:\n%s" % \
3864+ (stdout,)
3865+ name = self.dsc['Source']
3866+ version = Version(self.dsc['Version'])
3867+ self.extracted_upstream = os.path.join(self.tempdir,
3868+ "%s-%s.orig" % (name, str(version.upstream_version)))
3869+ self.extracted_debianised = os.path.join(self.tempdir,
3870+ "%s-%s" % (name, str(version.upstream_version)))
3871+ if not os.path.exists(self.extracted_upstream):
3872+ mutter("It's a native package")
3873+ self.extracted_upstream = None
3874+ for part in self.dsc['files']:
3875+ if self.extracted_upstream is None:
3876+ if part['name'].endswith(".tar.gz"):
3877+ self.unextracted_debian_md5 = part['md5sum']
3878+ else:
3879+ if part['name'].endswith(".orig.tar.gz"):
3880+ assert self.unextracted_upstream is None, "Two .orig.tar.gz?"
3881+ self.unextracted_upstream = os.path.abspath(
3882+ os.path.join(osutils.dirname(self.dsc_path),
3883+ part['name']))
3884+ self.unextracted_upstream_md5 = part['md5sum']
3885+ elif part['name'].endswith(".diff.gz"):
3886+ self.unextracted_debian_md5 = part['md5sum']
3887+
3888+ def cleanup(self):
3889+ if os.path.exists(self.tempdir):
3890+ shutil.rmtree(self.tempdir)
3891+
3892+
3893+class ThreeDotZeroNativeSourceExtractor(SourceExtractor):
3894+
3895+ def extract(self):
3896+ self.tempdir = tempfile.mkdtemp()
3897+ dsc_filename = os.path.abspath(self.dsc_path)
3898+ proc = subprocess.Popen("dpkg-source -x %s" % (dsc_filename,), shell=True,
3899+ cwd=self.tempdir, stdout=subprocess.PIPE,
3900+ stderr=subprocess.STDOUT, preexec_fn=subprocess_setup)
3901+ (stdout, _) = proc.communicate()
3902+ assert proc.returncode == 0, "dpkg-source -x failed, output:\n%s" % \
3903+ (stdout,)
3904+ name = self.dsc['Source']
3905+ version = Version(self.dsc['Version'])
3906+ self.extracted_debianised = os.path.join(self.tempdir,
3907+ "%s-%s" % (name, str(version.upstream_version)))
3908+ self.extracted_upstream = None
3909+ for part in self.dsc['files']:
3910+ if (part['name'].endswith(".tar.gz")
3911+ or part['name'].endswith(".tar.bz2")):
3912+ self.unextracted_debian_md5 = part['md5sum']
3913+
3914+
3915+class ThreeDotZeroQuiltSourceExtractor(SourceExtractor):
3916+
3917+ def extract(self):
3918+ self.tempdir = tempfile.mkdtemp()
3919+ dsc_filename = os.path.abspath(self.dsc_path)
3920+ proc = subprocess.Popen("dpkg-source --skip-debianization -x %s"
3921+ % (dsc_filename,), shell=True,
3922+ cwd=self.tempdir, stdout=subprocess.PIPE,
3923+ stderr=subprocess.STDOUT, preexec_fn=subprocess_setup)
3924+ (stdout, _) = proc.communicate()
3925+ assert proc.returncode == 0, "dpkg-source -x failed, output:\n%s" % \
3926+ (stdout,)
3927+ name = self.dsc['Source']
3928+ version = Version(self.dsc['Version'])
3929+ self.extracted_debianised = os.path.join(self.tempdir,
3930+ "%s-%s" % (name, str(version.upstream_version)))
3931+ self.extracted_upstream = self.extracted_debianised + ".orig"
3932+ os.rename(self.extracted_debianised, self.extracted_upstream)
3933+ proc = subprocess.Popen("dpkg-source -x %s" % (dsc_filename,), shell=True,
3934+ cwd=self.tempdir, stdout=subprocess.PIPE,
3935+ stderr=subprocess.STDOUT, preexec_fn=subprocess_setup)
3936+ (stdout, _) = proc.communicate()
3937+ assert proc.returncode == 0, "dpkg-source -x failed, output:\n%s" % \
3938+ (stdout,)
3939+ # Check that there are no unreadable files extracted.
3940+ subprocess.call(["find", self.extracted_upstream, "-perm",
3941+ "0000", "-exec", "chmod", "644", "{}", ";"])
3942+ subprocess.call(["find", self.extracted_debianised, "-perm",
3943+ "0000", "-exec", "chmod", "644", "{}", ";"])
3944+ for part in self.dsc['files']:
3945+ if (re.search("\.orig-[^.]+\.tar\.(gz|bz2|lzma)$", part['name'])):
3946+ raise AssertionError("Can't import packages with multiple "
3947+ "upstream tarballs yet")
3948+ if (part['name'].endswith(".orig.tar.gz")
3949+ or part['name'].endswith(".orig.tar.bz2")):
3950+ assert self.unextracted_upstream is None, "Two .orig.tar.(gz|bz2)?"
3951+ self.unextracted_upstream = os.path.abspath(
3952+ os.path.join(osutils.dirname(self.dsc_path),
3953+ part['name']))
3954+ self.unextracted_upstream_md5 = part['md5sum']
3955+ elif (part['name'].endswith(".debian.tar.gz")
3956+ or part['name'].endswith(".debian.tar.bz2")):
3957+ self.unextracted_debian_md5 = part['md5sum']
3958+ assert self.unextracted_upstream is not None, \
3959+ "Can't handle non gz|bz2 tarballs yet"
3960+ assert self.unextracted_debian_md5 is not None, \
3961+ "Can't handle non gz|bz2 tarballs yet"
3962+
3963+
3964+SOURCE_EXTRACTORS = {}
3965+SOURCE_EXTRACTORS["1.0"] = SourceExtractor
3966+SOURCE_EXTRACTORS["3.0 (native)"] = ThreeDotZeroNativeSourceExtractor
3967+SOURCE_EXTRACTORS["3.0 (quilt)"] = ThreeDotZeroQuiltSourceExtractor
3968
3969=== modified file 'info.py'
3970--- info.py 2009-07-26 15:51:02 +0000
3971+++ info.py 2011-01-28 14:24:44 +0000
3972@@ -18,7 +18,7 @@
3973
3974 bzr_plugin_name = 'builddeb'
3975
3976-bzr_plugin_version = (2, 1, 1, 'dev', 0)
3977+bzr_plugin_version = (2, 2, 0, 'final', 0)
3978
3979 bzr_commands = [
3980 "test_builddeb",
3981
3982=== modified file 'launchpad.py'
3983--- launchpad.py 2009-03-11 07:23:20 +0000
3984+++ launchpad.py 2011-01-28 14:24:44 +0000
3985@@ -20,18 +20,20 @@
3986
3987 import os
3988
3989+from bzrlib.config import config_dir
3990 from bzrlib.trace import mutter
3991
3992 try:
3993- from launchpadlib.launchpad import Launchpad, EDGE_SERVICE_ROOT
3994+ from launchpadlib.launchpad import Launchpad
3995 from launchpadlib.credentials import Credentials
3996+ from launchpadlib.uris import LPNET_SERVICE_ROOT
3997 HAVE_LPLIB = True
3998 except ImportError:
3999 HAVE_LPLIB = False
4000
4001
4002 def _get_launchpad():
4003- creds_path = os.path.expanduser("~/.bazaar/builddeb.lp_creds.txt")
4004+ creds_path = os.path.join(config_dir(), "builddeb.lp_creds.txt")
4005 if not os.path.exists(creds_path):
4006 return None
4007 creds = Credentials("bzr-builddeb")
4008@@ -40,7 +42,7 @@
4009 creds.load(f)
4010 finally:
4011 f.close()
4012- lp = Launchpad(creds, EDGE_SERVICE_ROOT)
4013+ lp = Launchpad(creds, service_root=LPNET_SERVICE_ROOT)
4014 return lp
4015
4016
4017@@ -51,8 +53,7 @@
4018 if lp is None:
4019 return []
4020 try:
4021- bug = lp.load("https://api.edge.launchpad.net/beta/bugs/"
4022- "bugtrackers/debbugs/%s")
4023+ bug = lp.load(str(lp._root_uri) + "bugs/bugtrackers/debbugs/%s")
4024 tasks = bug.bug_tasks
4025 for task in tasks:
4026 if task.bug_target_name.endswith("(Ubuntu)"):
4027
4028=== added file 'merge_changelog.py'
4029--- merge_changelog.py 1970-01-01 00:00:00 +0000
4030+++ merge_changelog.py 2011-01-28 14:24:44 +0000
4031@@ -0,0 +1,152 @@
4032+#!/usr/bin/env python
4033+# -*- coding: utf-8 -*-
4034+# Copyright ? 2008 Canonical Ltd.
4035+# Author: Scott James Remnant <scott@ubuntu.com>.
4036+# Hacked up by: Bryce Harrington <bryce@ubuntu.com>
4037+#
4038+# This program is free software: you can redistribute it and/or modify
4039+# it under the terms of version 3 of the GNU General Public License as
4040+# published by the Free Software Foundation.
4041+#
4042+# This program is distributed in the hope that it will be useful,
4043+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4044+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4045+# GNU General Public License for more details.
4046+#
4047+# You should have received a copy of the GNU General Public License
4048+# along with this program. If not, see <http://www.gnu.org/licenses/>.
4049+
4050+import re
4051+
4052+from bzrlib import (
4053+ merge,
4054+ )
4055+
4056+
4057+class ChangeLogFileMerge(merge.ConfigurableFileMerger):
4058+
4059+ name_prefix = 'deb_changelog'
4060+ default_files = ['debian/changelog']
4061+
4062+ def merge_text(self, params):
4063+ return merge_changelog(params.this_lines, params.other_lines,
4064+ params.base_lines)
4065+
4066+
4067+# Regular expression for top of debian/changelog
4068+CL_RE = re.compile(r'^(\w[-+0-9a-z.]*) \(([^\(\) \t]+)\)((\s+[-0-9a-z]+)+)\;',
4069+ re.IGNORECASE)
4070+
4071+def merge_changelog(this_lines, other_lines, base_lines=[]):
4072+ """Merge a changelog file."""
4073+ try:
4074+ from debian import changelog
4075+ except ImportError:
4076+ # Prior to 0.1.15 the debian module was called debian_bundle
4077+ from debian_bundle import changelog
4078+
4079+ try:
4080+ left_cl = read_changelog(this_lines)
4081+ right_cl = read_changelog(other_lines)
4082+ # BASE lines don't end up in the output, so we allow strict=False
4083+ base_cl = read_changelog(base_lines, strict=False)
4084+ except changelog.ChangelogParseError:
4085+ return ('not_applicable', None)
4086+
4087+ content = []
4088+ def step(iterator):
4089+ try:
4090+ return iterator.next()
4091+ except StopIteration:
4092+ return None
4093+ left_blocks = dict((b.version, b) for b in left_cl._blocks)
4094+ right_blocks = dict((b.version, b) for b in right_cl._blocks)
4095+ # Unfortunately, while version objects implement __eq__ they *don't*
4096+ # implement __hash__, which means we can't do dict lookups properly, so
4097+ # instead, we fall back on the version string instead of the object.
4098+ # Make sure never to try to use right_version in left_blocks because of
4099+ # this.
4100+ # We lazily parse the base data, in case we never need it
4101+ base_blocks = dict((b.version.full_version, b) for b in base_cl._blocks)
4102+ left_order = iter(sorted(left_blocks.keys(), reverse=True))
4103+ right_order = iter(sorted(right_blocks.keys(), reverse=True))
4104+ left_version = step(left_order)
4105+ right_version = step(right_order)
4106+
4107+ # TODO: Do we want to support the ability to delete a section? We could do
4108+ # a first-pass algorithm that checks the versions in base versus the
4109+ # versions in this and other, to determine what versions should be in
4110+ # the output. For now, we just assume that if a version is present in
4111+ # any of this or other, then we want it in the output.
4112+ conflict_status = 'success'
4113+
4114+ while left_version is not None or right_version is not None:
4115+ if (left_version is None or
4116+ (right_version is not None and right_version > left_version)):
4117+ next_content = str(right_blocks[right_version])
4118+ right_version = step(right_order)
4119+ elif (right_version is None or
4120+ (left_version is not None and left_version > right_version)):
4121+ next_content = str(left_blocks[left_version])
4122+ left_version = step(left_order)
4123+ else:
4124+ assert left_version == right_version
4125+ # Same version, step both
4126+ # TODO: Conflict if left_version != right
4127+ # Note: See above comment why we can't use
4128+ # right_blocks[left_version] even though they *should* be
4129+ # equivalent
4130+ left_content = str(left_blocks[left_version])
4131+ right_content = str(right_blocks[right_version])
4132+ if left_content == right_content:
4133+ # Identical content
4134+ next_content = left_content
4135+ else:
4136+ # Sides disagree, compare with base
4137+ base_content = str(base_blocks.get(left_version.full_version,
4138+ ''))
4139+ if left_content == base_content:
4140+ next_content = right_content
4141+ elif right_content == base_content:
4142+ next_content = left_content
4143+ else:
4144+ # TODO: We could use merge3.Merge3 to try a line-based
4145+ # textual merge on the content. However, for now I'm
4146+ # just going to conflict on the whole region
4147+ # Conflict names taken from merge.py
4148+ next_content = ('<<<<<<< TREE\n'
4149+ + left_content
4150+ + '=======\n'
4151+ + right_content
4152+ + '>>>>>>> MERGE-SOURCE\n'
4153+ )
4154+ conflict_status = 'conflicted'
4155+ next_block = left_blocks[left_version]
4156+ left_version = step(left_order)
4157+ right_version = step(right_order)
4158+ content.append(next_content)
4159+
4160+ return conflict_status, content
4161+
4162+
4163+def read_changelog(lines, strict=True):
4164+ """Return a parsed changelog file."""
4165+ try:
4166+ from debian import changelog
4167+ except ImportError:
4168+ # Prior to 0.1.15 the debian module was called debian_bundle
4169+ from debian_bundle import changelog
4170+ # Note: There appears to be a bug in Changelog if you pass it an iterable
4171+ # of lines (like a file obj, or a list of lines). Specifically, it
4172+ # does not strip trailing newlines, and it adds ones back in, so you
4173+ # get doubled blank lines... :(
4174+ # So we just ''.join() the lines and don't worry about it
4175+ # Note: There is also a bug that the Changelog constructor suppresses parse
4176+ # errors, so we want to always call parse_changelog separately
4177+ content = ''.join(lines)
4178+ cl = changelog.Changelog()
4179+ if content:
4180+ # We get a warning if we try to parse an empty changelog file, which in
4181+ # strict mode is an error, so only parse when we have content
4182+ cl.parse_changelog(content, strict=strict)
4183+ return cl
4184
4185=== added file 'merge_package.py'
4186--- merge_package.py 1970-01-01 00:00:00 +0000
4187+++ merge_package.py 2011-01-28 14:24:44 +0000
4188@@ -0,0 +1,183 @@
4189+# merge_package.py -- The plugin for bzr
4190+# Copyright (C) 2009 Canonical Ltd.
4191+#
4192+# :Author: Muharem Hrnjadovic <muharem@ubuntu.com>
4193+#
4194+# This file is part of bzr-builddeb.
4195+#
4196+# bzr-builddeb is free software; you can redistribute it and/or modify
4197+# it under the terms of the GNU General Public License as published by
4198+# the Free Software Foundation; either version 2 of the License, or
4199+# (at your option) any later version.
4200+#
4201+# bzr-builddeb is distributed in the hope that it will be useful,
4202+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4203+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4204+# GNU General Public License for more details.
4205+#
4206+# You should have received a copy of the GNU General Public License
4207+# along with bzr-builddeb; if not, write to the Free Software
4208+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
4209+#
4210+
4211+import os
4212+import shutil
4213+import tempfile
4214+
4215+try:
4216+ from debian.changelog import Version
4217+except ImportError:
4218+ # Prior to 0.1.15 the debian module was called debian_bundle
4219+ from debian_bundle.changelog import Version
4220+
4221+from bzrlib.plugins.builddeb.errors import (
4222+ SharedUpstreamConflictsWithTargetPackaging)
4223+from bzrlib.plugins.builddeb.import_dsc import DistributionBranch
4224+from bzrlib.plugins.builddeb.util import find_changelog
4225+
4226+
4227+def _latest_version(branch):
4228+ """Version of the most recent source package upload in the given `branch`.
4229+
4230+ :param branch: A Branch object containing the source upload of interest.
4231+ """
4232+ changelog, _ignore = find_changelog(branch.basis_tree(), False)
4233+
4234+ return changelog.version
4235+
4236+
4237+def _upstream_version_data(source, target):
4238+ """Most recent upstream versions/revision IDs of the merge source/target.
4239+
4240+ Please note: both packaging branches must have been read-locked
4241+ beforehand.
4242+
4243+ :param source: The merge source branch.
4244+ :param target: The merge target branch.
4245+ """
4246+ results = list()
4247+ for branch in (source, target):
4248+ db = DistributionBranch(branch, branch)
4249+ uver = _latest_version(branch).upstream_version
4250+ results.append((Version(uver),
4251+ db.revid_of_upstream_version_from_branch(uver)))
4252+
4253+ return results
4254+
4255+
4256+def fix_ancestry_as_needed(tree, source):
4257+ """Manipulate the merge target's ancestry to avoid upstream conflicts.
4258+
4259+ Merging J->I given the following ancestry tree is likely to result in
4260+ upstream merge conflicts:
4261+
4262+ debian-upstream ,------------------H
4263+ A-----------B \
4264+ ubuntu-upstream \ \`-------G \
4265+ \ \ \ \
4266+ debian-packaging \ ,---------D--------\-----------J
4267+ C \ \
4268+ ubuntu-packaging `----E------F--------I
4269+
4270+ Here there was a new upstream release (G) that Ubuntu packaged (I), and
4271+ then another one that Debian packaged, skipping G, at H and J.
4272+
4273+ Now, the way to solve this is to introduce the missing link.
4274+
4275+ debian-upstream ,------------------H------.
4276+ A-----------B \ \
4277+ ubuntu-upstream \ \`-------G-----------\------K
4278+ \ \ \ \
4279+ debian-packaging \ ,---------D--------\-----------J
4280+ C \ \
4281+ ubuntu-packaging `----E------F--------I
4282+
4283+ at K, which isn't a real merge, as we just use the tree from H, but add
4284+ G as a parent and then we merge that in to Ubuntu.
4285+
4286+ debian-upstream ,------------------H------.
4287+ A-----------B \ \
4288+ ubuntu-upstream \ \`-------G-----------\------K
4289+ \ \ \ \ \
4290+ debian-packaging \ ,---------D--------\-----------J \
4291+ C \ \ \
4292+ ubuntu-packaging `----E------F--------I------------------L
4293+
4294+ At this point we can merge J->L to merge the Debian and Ubuntu changes.
4295+
4296+ :param tree: The `WorkingTree` of the merge target branch.
4297+ :param source: The merge source (packaging) branch.
4298+ """
4299+ upstreams_diverged = False
4300+ t_upstream_reverted = False
4301+ target = tree.branch
4302+
4303+ source.lock_read()
4304+ try:
4305+ tree.lock_write()
4306+ try:
4307+ # "Unpack" the upstream versions and revision ids for the merge
4308+ # source and target branch respectively.
4309+ [(us_ver, us_revid), (ut_ver, ut_revid)] = _upstream_version_data(source, target)
4310+
4311+ # Did the upstream branches of the merge source/target diverge?
4312+ graph = source.repository.get_graph(target.repository)
4313+ upstreams_diverged = (len(graph.heads([us_revid, ut_revid])) > 1)
4314+
4315+ # No, we're done!
4316+ if not upstreams_diverged:
4317+ return (upstreams_diverged, t_upstream_reverted)
4318+
4319+ # Instantiate a `DistributionBranch` object for the merge target
4320+ # (packaging) branch.
4321+ db = DistributionBranch(tree.branch, tree.branch)
4322+ tempdir = tempfile.mkdtemp(dir=os.path.join(tree.basedir, '..'))
4323+
4324+ try:
4325+ # Extract the merge target's upstream tree into a temporary
4326+ # directory.
4327+ db.extract_upstream_tree(ut_revid, tempdir)
4328+ tmp_target_utree = db.upstream_tree
4329+
4330+ # Merge upstream branch tips to obtain a shared upstream parent.
4331+ # This will add revision K (see graph above) to a temporary merge
4332+ # target upstream tree.
4333+ tmp_target_utree.lock_write()
4334+ try:
4335+ if us_ver > ut_ver:
4336+ # The source upstream tree is more recent and the
4337+ # temporary target tree needs to be reshaped to match it.
4338+ tmp_target_utree.revert(
4339+ None, source.repository.revision_tree(us_revid))
4340+ t_upstream_reverted = True
4341+
4342+ tmp_target_utree.set_parent_ids((ut_revid, us_revid))
4343+ new_revid = tmp_target_utree.commit(
4344+ 'Prepared upstream tree for merging into target branch.')
4345+
4346+ # Repository updates during a held lock are not visible,
4347+ # hence the call to refresh the data in the /target/ repo.
4348+ tree.branch.repository.refresh_data()
4349+
4350+ tree.branch.fetch(source, last_revision=us_revid)
4351+ tree.branch.fetch(tmp_target_utree.branch,
4352+ last_revision=new_revid)
4353+
4354+ # Merge shared upstream parent into the target merge branch. This
4355+ # creates revison L in the digram above.
4356+ conflicts = tree.merge_from_branch(tmp_target_utree.branch)
4357+ if conflicts > 0:
4358+ raise SharedUpstreamConflictsWithTargetPackaging()
4359+ else:
4360+ tree.commit('Merging shared upstream rev into target branch.')
4361+
4362+ finally:
4363+ tmp_target_utree.unlock()
4364+ finally:
4365+ shutil.rmtree(tempdir)
4366+ finally:
4367+ tree.unlock()
4368+ finally:
4369+ source.unlock()
4370+
4371+ return (upstreams_diverged, t_upstream_reverted)
4372
4373=== modified file 'merge_upstream.py'
4374--- merge_upstream.py 2009-03-10 01:57:05 +0000
4375+++ merge_upstream.py 2011-01-28 14:24:44 +0000
4376@@ -27,9 +27,15 @@
4377 #
4378
4379 import itertools
4380-
4381-from debian_bundle.changelog import Version
4382-
4383+import subprocess
4384+
4385+try:
4386+ from debian.changelog import Version
4387+except ImportError:
4388+ # Prior to 0.1.15 the debian module was called debian_bundle
4389+ from debian_bundle.changelog import Version
4390+
4391+from bzrlib.errors import InvalidRevisionId
4392 from bzrlib.revisionspec import RevisionSpec
4393
4394 from bzrlib.plugins.builddeb.util import get_snapshot_revision
4395@@ -38,6 +44,27 @@
4396 TAG_PREFIX = "upstream-"
4397
4398
4399+def extract_svn_revno(rev):
4400+ """Extract the Subversion number of a revision from a revision.
4401+
4402+ :param rev: Revision object
4403+ :return: Revision number, None if this was not a Subversion revision or
4404+ if the revision number could not be determined (bzr-svn not available).
4405+ """
4406+ try:
4407+ from bzrlib.plugins.svn import extract_svn_foreign_revid
4408+ except ImportError:
4409+ # No svn support
4410+ return None
4411+ else:
4412+ try:
4413+ (svn_uuid, branch_path, svn_revno) = extract_svn_foreign_revid(rev)
4414+ except InvalidRevisionId:
4415+ return None
4416+ else:
4417+ return svn_revno
4418+
4419+
4420 def upstream_version_add_revision(upstream_branch, version_string, revid):
4421 """Update the revision in a upstream version string.
4422
4423@@ -46,7 +73,7 @@
4424 :param revid: Revision id of the revision
4425 """
4426 revno = upstream_branch.revision_id_to_revno(revid)
4427-
4428+
4429 if "+bzr" in version_string:
4430 return "%s+bzr%d" % (version_string[:version_string.rfind("+bzr")], revno)
4431
4432@@ -54,17 +81,18 @@
4433 return "%s~bzr%d" % (version_string[:version_string.rfind("~bzr")], revno)
4434
4435 rev = upstream_branch.repository.get_revision(revid)
4436- svn_revmeta = getattr(rev, "svn_meta", None)
4437- if svn_revmeta is not None:
4438- svn_revno = svn_revmeta.revnum
4439-
4440- if "+svn" in version_string:
4441- return "%s+svn%d" % (version_string[:version_string.rfind("+svn")], svn_revno)
4442- if "~svn" in version_string:
4443- return "%s~svn%d" % (version_string[:version_string.rfind("~svn")], svn_revno)
4444+ svn_revno = extract_svn_revno(rev)
4445+
4446+ # FIXME: Raise error if +svn/~svn is present and svn_revno is not set?
4447+ if "+svn" in version_string and svn_revno:
4448+ return "%s+svn%d" % (version_string[:version_string.rfind("+svn")], svn_revno)
4449+ if "~svn" in version_string and svn_revno:
4450+ return "%s~svn%d" % (version_string[:version_string.rfind("~svn")], svn_revno)
4451+
4452+ if svn_revno:
4453 return "%s+svn%d" % (version_string, svn_revno)
4454-
4455- return "%s+bzr%d" % (version_string, revno)
4456+ else:
4457+ return "%s+bzr%d" % (version_string, revno)
4458
4459
4460 def _upstream_branch_version(revhistory, reverse_tag_dict, package,
4461@@ -107,6 +135,14 @@
4462
4463 def upstream_branch_version(upstream_branch, upstream_revision, package,
4464 previous_version):
4465+ """Determine the version string for a revision in an upstream branch.
4466+
4467+ :param upstream_branch: The upstream branch object
4468+ :param upstream_revision: The revision id of the upstream revision
4469+ :param package: The name of the package
4470+ :param previous_version: The previous upstream version string
4471+ :return: Upstream version string for `upstream_revision`.
4472+ """
4473 dotted_revno = upstream_branch.revision_id_to_dotted_revno(upstream_revision)
4474 if len(dotted_revno) > 1:
4475 revno = -2
4476@@ -117,7 +153,7 @@
4477 if previous_revision is not None:
4478 previous_revspec = RevisionSpec.from_string(previous_revision)
4479 previous_revno, _ = previous_revspec.in_history(upstream_branch)
4480- # Trim revision history - we don't care about any revisions
4481+ # Trim revision history - we don't care about any revisions
4482 # before the revision of the previous version
4483 else:
4484 previous_revno = 0
4485@@ -145,21 +181,61 @@
4486 return None
4487
4488
4489-def package_version(merged_version, distribution_name):
4490- """Determine the package version from the merged version.
4491+def package_version(upstream_version, distribution_name):
4492+ """Determine the package version for a new upstream.
4493
4494- :param merged_version: Merged version string
4495+ :param upstream_version: Upstream version string
4496 :param distribution_name: Distribution the package is for
4497 """
4498- ret = Version(merged_version)
4499- if merged_version.debian_version is not None:
4500- prev_packaging_revnum = int("".join(itertools.takewhile(
4501- lambda x: x.isdigit(),
4502- merged_version.debian_version)))
4503- else:
4504- prev_packaging_revnum = 0
4505+ assert isinstance(upstream_version, str), \
4506+ "upstream_version should be a str, not %s" % str(
4507+ type(upstream_version))
4508 if distribution_name == "ubuntu":
4509- ret.debian_version = "%dubuntu1" % prev_packaging_revnum
4510+ ret = Version("%s-0ubuntu1" % upstream_version)
4511 else:
4512- ret.debian_version = "%d" % (prev_packaging_revnum+1)
4513+ ret = Version("%s-1" % upstream_version)
4514 return ret
4515+
4516+
4517+def upstream_merge_changelog_line(upstream_version):
4518+ """Describe that a new upstream revision was merged.
4519+
4520+ This will either describe that a new upstream release or a new upstream snapshot
4521+ was merged.
4522+
4523+ :param upstream_version: Upstream version string
4524+ :return: Line string for use in changelog
4525+ """
4526+ vcs_suffixes = ["~bzr", "+bzr", "~svn", "+svn", "~git", "+git"]
4527+ for vcs_suffix in vcs_suffixes:
4528+ if vcs_suffix in str(upstream_version):
4529+ entry_description = "New upstream snapshot."
4530+ break
4531+ else:
4532+ entry_description = "New upstream release."
4533+ return entry_description
4534+
4535+
4536+def changelog_add_new_version(tree, upstream_version, distribution_name,
4537+ changelog, package):
4538+ """Add an entry to the changelog for a new version.
4539+
4540+ :param tree: WorkingTree in which the package lives
4541+ :param upstream_version: Upstream version to add
4542+ :param distribution_name: Distribution name (debian, ubuntu, ...)
4543+ :param changelog: Changelog object
4544+ :param package: Package name
4545+ :return: Whether an entry was successfully added
4546+ """
4547+ assert isinstance(upstream_version, str), \
4548+ "upstream_version should be a str, not %s" % str(
4549+ type(upstream_version))
4550+ entry_description = upstream_merge_changelog_line(upstream_version)
4551+ proc = subprocess.Popen(["dch", "-v",
4552+ str(package_version(upstream_version, distribution_name)),
4553+ "-D", "UNRELEASED", "--release-heuristic", "changelog", entry_description],
4554+ cwd=tree.basedir)
4555+ proc.wait()
4556+ # FIXME: Raise insightful exception here rather than just checking
4557+ # return code.
4558+ return proc.returncode == 0
4559
4560=== modified file 'repack_tarball.py'
4561--- repack_tarball.py 2009-03-08 23:28:22 +0000
4562+++ repack_tarball.py 2011-01-28 14:24:44 +0000
4563@@ -39,9 +39,9 @@
4564 FileExists,
4565 )
4566 from bzrlib.transport import get_transport
4567-from bzrlib import urlutils
4568
4569 from bzrlib.plugins.builddeb.errors import UnsupportedRepackFormat
4570+from bzrlib.plugins.builddeb.util import open_file, open_file_via_transport
4571
4572
4573 class TgzRepacker(object):
4574@@ -123,36 +123,39 @@
4575 zip.close()
4576
4577
4578-def get_repacker_class(source_filename):
4579+def get_filetype(filename):
4580+ types = [".tar.gz", ".tgz", ".tar.bz2", ".tbz2", ".tar", ".zip"]
4581+ for filetype in types:
4582+ if filename.endswith(filetype):
4583+ return filetype
4584+
4585+
4586+def get_repacker_class(source_filename, force_gz=True):
4587 """Return the appropriate repacker based on the file extension."""
4588- if (source_filename.endswith(".tar.gz")
4589- or source_filename.endswith(".tgz")):
4590- return TgzTgzRepacker
4591- if (source_filename.endswith(".tar.bz2")
4592- or source_filename.endswith(".tbz2")):
4593- return Tbz2TgzRepacker
4594- if source_filename.endswith(".tar"):
4595+ filetype = get_filetype(source_filename)
4596+ if (filetype == ".tar.gz" or filetype == ".tgz"):
4597+ return TgzTgzRepacker
4598+ if (filetype == ".tar.bz2" or filetype == ".tbz2"):
4599+ if force_gz:
4600+ return Tbz2TgzRepacker
4601+ return TgzTgzRepacker
4602+ if filetype == ".tar":
4603 return TarTgzRepacker
4604- if source_filename.endswith(".zip"):
4605+ if filetype == ".zip":
4606 return ZipTgzRepacker
4607 return None
4608
4609
4610-def _get_file_from_location(location):
4611- base_dir, path = urlutils.split(location)
4612- transport = get_transport(base_dir)
4613- return transport.get(path)
4614-
4615-
4616-def _error_if_exists(target_transport, new_name, source_name):
4617- if not source_name.endswith('.tar.gz'):
4618+def _error_if_exists(target_transport, new_name, source_name, force_gz=True):
4619+ source_filetype = get_filetype(source_name)
4620+ if force_gz and source_filetype != ".tar.gz":
4621 raise FileExists(new_name)
4622- source_f = _get_file_from_location(source_name)
4623+ source_f = open_file(source_name)
4624 try:
4625 source_sha = new_sha(source_f.read()).hexdigest()
4626 finally:
4627 source_f.close()
4628- target_f = target_transport.get(new_name)
4629+ target_f = open_file_via_transport(new_name, target_transport)
4630 try:
4631 target_sha = new_sha(target_f.read()).hexdigest()
4632 finally:
4633@@ -174,14 +177,14 @@
4634 target_f.close()
4635
4636
4637-def _repack_other(target_transport, new_name, source_name):
4638- repacker_cls = get_repacker_class(source_name)
4639+def _repack_other(target_transport, new_name, source_name, force_gz=True):
4640+ repacker_cls = get_repacker_class(source_name, force_gz=force_gz)
4641 if repacker_cls is None:
4642 raise UnsupportedRepackFormat(source_name)
4643 target_transport.ensure_base()
4644 target_f = target_transport.open_write_stream(new_name)
4645 try:
4646- source_f = _get_file_from_location(source_name)
4647+ source_f = open_file(source_name)
4648 try:
4649 repacker = repacker_cls(source_f)
4650 repacker.repack(target_f)
4651@@ -191,7 +194,7 @@
4652 target_f.close()
4653
4654
4655-def repack_tarball(source_name, new_name, target_dir=None):
4656+def repack_tarball(source_name, new_name, target_dir=None, force_gz=True):
4657 """Repack the file/dir named to a .tar.gz with the chosen name.
4658
4659 This function takes a named file of either .tar.gz, .tar .tgz .tar.bz2
4660@@ -212,6 +215,7 @@
4661 :keyword target_dir: the directory to consider new_name relative to, and
4662 will be created if non-existant.
4663 :type target_dir: string
4664+ :param force_gz: whether to repack other .tar files to .tar.gz.
4665 :return: None
4666 :throws NoSuchFile: if source_name doesn't exist.
4667 :throws FileExists: if the target filename (after considering target_dir)
4668@@ -229,9 +233,11 @@
4669 extra, new_name = os.path.split(new_name)
4670 target_transport = get_transport(os.path.join(target_dir, extra))
4671 if target_transport.has(new_name):
4672- _error_if_exists(target_transport, new_name, source_name)
4673+ _error_if_exists(target_transport, new_name, source_name,
4674+ force_gz=force_gz)
4675 return
4676 if os.path.isdir(source_name):
4677 _repack_directory(target_transport, new_name, source_name)
4678 else:
4679- _repack_other(target_transport, new_name, source_name)
4680+ _repack_other(target_transport, new_name, source_name,
4681+ force_gz=force_gz)
4682
4683=== modified file 'source_distiller.py'
4684--- source_distiller.py 2009-07-15 12:02:25 +0000
4685+++ source_distiller.py 2011-01-28 14:24:44 +0000
4686@@ -20,29 +20,22 @@
4687 import glob
4688 import os
4689 import shutil
4690-import signal
4691 import subprocess
4692 import tempfile
4693
4694 from bzrlib import errors as bzr_errors
4695-from bzrlib.export import export
4696
4697 from bzrlib.plugins.builddeb.errors import (
4698 TarFailed,
4699 )
4700 from bzrlib.plugins.builddeb.util import (
4701+ export,
4702 get_parent_dir,
4703 recursive_copy,
4704+ subprocess_setup,
4705 )
4706
4707
4708-def subprocess_setup():
4709- # Python installs a SIGPIPE handler by default. This is usually not what
4710- # non-Python subprocesses expect.
4711- # Many, many thanks to Colin Watson
4712- signal.signal(signal.SIGPIPE, signal.SIG_DFL)
4713-
4714-
4715 class SourceDistiller(object):
4716 """A source distiller extracts the source to build from a location.
4717
4718@@ -115,7 +108,7 @@
4719 self.upstream_provider.provide(parent_dir)
4720 if self.is_working_tree:
4721 self._prepare_working_tree()
4722- export(self.tree, target, None, None)
4723+ export(self.tree, target)
4724
4725
4726 class MergeModeDistiller(SourceDistiller):
4727@@ -159,7 +152,7 @@
4728 export_dir = tempdir
4729 if self.is_working_tree:
4730 self._prepare_working_tree()
4731- export(self.tree,export_dir,None,None)
4732+ export(self.tree, export_dir)
4733 # Remove any upstream debian dir, or from previous export with
4734 # use_existing
4735 if os.path.exists(os.path.join(target, 'debian')):
4736
4737=== added file 'tagging.py'
4738--- tagging.py 1970-01-01 00:00:00 +0000
4739+++ tagging.py 2011-01-28 14:24:44 +0000
4740@@ -0,0 +1,75 @@
4741+# Copyright (C) 2010 Canonical Limited
4742+# vim: ts=4 sts=4 sw=4
4743+#
4744+# This file is part of bzr-builddeb.
4745+#
4746+# bzr-builddeb is free software; you can redistribute it and/or modify
4747+# it under the terms of the GNU General Public License as published by
4748+# the Free Software Foundation; either version 2 of the License, or
4749+# (at your option) any later version.
4750+#
4751+# bzr-builddeb is distributed in the hope that it will be useful,
4752+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4753+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4754+# GNU General Public License for more details.
4755+#
4756+# You should have received a copy of the GNU General Public License
4757+# along with bzr-builddeb; if not, write to the Free Software
4758+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
4759+
4760+"""Tagging related functions for bzr-builddeb."""
4761+
4762+__all__ = ['is_upstream_tag', 'upstream_tag_version']
4763+
4764+
4765+try:
4766+ from debian.changelog import Version
4767+except ImportError:
4768+ # Prior to 0.1.15 the debian module was called debian_bundle
4769+ from debian_bundle.changelog import Version
4770+
4771+
4772+def is_upstream_tag(tag):
4773+ """Return true if tag is an upstream tag.
4774+
4775+ :param tag: The string name of the tag.
4776+ :return: True if the tag name is one generated by upstream tag operations.
4777+
4778+ >>> is_upstream_tag('2.1')
4779+ False
4780+ >>> is_upstream_tag('upstream-2.1')
4781+ True
4782+ """
4783+ return tag.startswith('upstream-') or tag.startswith('upstream/')
4784+
4785+
4786+def upstream_tag_version(tag):
4787+ """Return the upstream version portion of an upstream tag name.
4788+
4789+ :param tag: The string name of the tag.
4790+ :return: The version portion of the tag.
4791+
4792+ >>> upstream_tag_version('upstream-2.1')
4793+ '2.1'
4794+ """
4795+ assert is_upstream_tag(tag), "Not an upstream tag: %s" % tag
4796+ if tag.startswith('upstream/'):
4797+ tag = tag[len('upstream/'):]
4798+ elif tag.startswith('upstream-'):
4799+ tag = tag[len('upstream-'):]
4800+ if tag.startswith('debian-'):
4801+ tag = tag[len('debian-'):]
4802+ elif tag.startswith('ubuntu-'):
4803+ tag = tag[len('ubuntu-'):]
4804+ return tag
4805+
4806+
4807+def sort_debversion(branch, tags):
4808+ """Sort tags using Debian version in-place.
4809+
4810+ :param branch: Branch to use
4811+ :param tags: List of tuples with name and version.
4812+ """
4813+ def debversion_key((version, revid)):
4814+ return Version(version)
4815+ tags.sort(key=debversion_key)
4816
4817=== modified file 'tests/__init__.py'
4818--- tests/__init__.py 2009-07-04 20:45:01 +0000
4819+++ tests/__init__.py 2011-01-28 14:24:44 +0000
4820@@ -28,12 +28,14 @@
4821 import os
4822 from unittest import TestSuite
4823
4824-from debian_bundle.changelog import Version, Changelog
4825+try:
4826+ from debian.changelog import Version, Changelog
4827+except ImportError:
4828+ # Prior to 0.1.15 the debian module was called debian_bundle
4829+ from debian_bundle.changelog import Version, Changelog
4830
4831 from bzrlib.tests import TestUtil, multiply_tests, TestCaseWithTransport
4832
4833-from bzrlib.plugins.builddeb.tests import blackbox
4834-
4835
4836 def make_new_upstream_dir(source, dest):
4837 os.rename(source, dest)
4838@@ -109,21 +111,26 @@
4839 return result
4840
4841
4842-def test_suite():
4843- loader = TestUtil.TestLoader()
4844- suite = TestSuite()
4845+def load_tests(standard_tests, module, loader):
4846+ suite = loader.suiteClass()
4847 testmod_names = [
4848+ 'blackbox',
4849 'test_builder',
4850+ 'test_bzrtools_import',
4851 'test_commit_message',
4852 'test_config',
4853+ 'test_dh_make',
4854 'test_hooks',
4855 'test_import_dsc',
4856+ 'test_merge_changelog',
4857+ 'test_merge_package',
4858 'test_merge_upstream',
4859 'test_repack_tarball_extra',
4860 'test_revspec',
4861 'test_source_distiller',
4862 'test_upstream',
4863 'test_util',
4864+ 'test_tagging',
4865 ]
4866 suite.addTest(loader.loadTestsFromModuleNames(["%s.%s" % (__name__, i)
4867 for i in testmod_names]))
4868@@ -148,15 +155,9 @@
4869 old_tarball='../package-0.2.tar')),
4870 ]
4871 suite = multiply_tests(repack_tarball_tests, scenarios, suite)
4872- packages_to_test = [
4873- blackbox,
4874- ]
4875-
4876- for package in packages_to_test:
4877- suite.addTest(package.test_suite())
4878-
4879 return suite
4880
4881+
4882 class BuilddebTestCase(TestCaseWithTransport):
4883
4884 package_name = 'test'
4885@@ -238,12 +239,13 @@
4886 >>> builder.dsc_name()
4887 """
4888
4889- def __init__(self, name, version, native=False):
4890+ def __init__(self, name, version, native=False, version3=False):
4891 self.upstream_files = {}
4892 self.upstream_symlinks = {}
4893 self.debian_files = {}
4894 self.name = name
4895 self.native = native
4896+ self.version3 = version3
4897 self._cl = Changelog()
4898 self.new_version(version)
4899
4900@@ -327,6 +329,10 @@
4901 def basedir(self):
4902 return self.name + "-" + str(self._cl.version.upstream_version)
4903
4904+ def write_debian_files(self, basedir):
4905+ self._make_files(self.debian_files, basedir)
4906+ self._make_files({"debian/changelog": str(self._cl)}, basedir)
4907+
4908 def _make_base(self):
4909 basedir = self.basedir()
4910 os.mkdir(basedir)
4911@@ -334,28 +340,46 @@
4912 self._make_symlinks(self.upstream_symlinks, basedir)
4913 return basedir
4914
4915- def build(self):
4916+ def build(self, tar_format=None):
4917+ if tar_format is None:
4918+ tar_format = 'gz'
4919 basedir = self._make_base()
4920- if not self.native:
4921- orig_basedir = basedir + ".orig"
4922- shutil.copytree(basedir, orig_basedir, symlinks=True)
4923- cmd = "dpkg-source -sa -b %s" % (basedir)
4924- if os.path.exists("%s_%s.orig.tar.gz"
4925- % (self.name, self._cl.version.upstream_version)):
4926- cmd = "dpkg-source -ss -b %s" % (basedir)
4927+ if not self.version3:
4928+ if not self.native:
4929+ orig_basedir = basedir + ".orig"
4930+ shutil.copytree(basedir, orig_basedir, symlinks=True)
4931+ cmd = ["dpkg-source", "-sa", "-b", basedir]
4932+ if os.path.exists("%s_%s.orig.tar.gz"
4933+ % (self.name, self._cl.version.upstream_version)):
4934+ cmd = ["dpkg-source", "-ss", "-b", basedir]
4935+ else:
4936+ cmd = ["dpkg-source", "-sn", "-b", basedir]
4937 else:
4938- cmd = "dpkg-source -sn -b %s" % (basedir)
4939- self._make_files(self.debian_files, basedir)
4940- self._make_files({"debian/changelog": str(self._cl)}, basedir)
4941- proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
4942- stderr=subprocess.PIPE)
4943+ if not self.native:
4944+ tar_path = "%s_%s.orig.tar.%s" % (self.name,
4945+ self._cl.version.upstream_version, tar_format)
4946+ if os.path.exists(tar_path):
4947+ os.unlink(tar_path)
4948+ tar = tarfile.open(tar_path, 'w:%s' % tar_format)
4949+ try:
4950+ tar.add(basedir)
4951+ finally:
4952+ tar.close()
4953+ cmd = ["dpkg-source", "--format=3.0 (quilt)", "-b",
4954+ basedir]
4955+ else:
4956+ cmd = ["dpkg-source", "--format=3.0 (native)", "-b",
4957+ basedir]
4958+ self.write_debian_files(basedir)
4959+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
4960+ stderr=subprocess.STDOUT)
4961 ret = proc.wait()
4962- assert ret == 0, "dpkg-source failed, output:\n%s\n%s" % \
4963- (proc.stdout.read(), proc.stderr.read())
4964+ assert ret == 0, "dpkg-source failed, output:\n%s" % \
4965+ (proc.stdout.read(),)
4966 cmd = "dpkg-genchanges -S > ../%s" % self.changes_name()
4967 proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
4968- stderr=subprocess.PIPE, cwd=basedir)
4969+ stderr=subprocess.STDOUT, cwd=basedir)
4970 ret = proc.wait()
4971- assert ret == 0, "dpkg-genchanges failed, output:\n%s\n%s" % \
4972- (proc.stdout.read(), proc.stderr.read())
4973+ assert ret == 0, "dpkg-genchanges failed, output:\n%s" % \
4974+ (proc.stdout.read(),)
4975 shutil.rmtree(basedir)
4976
4977=== modified file 'tests/blackbox/__init__.py'
4978--- tests/blackbox/__init__.py 2008-08-27 11:57:30 +0000
4979+++ tests/blackbox/__init__.py 2011-01-28 14:24:44 +0000
4980@@ -21,15 +21,16 @@
4981 from bzrlib.tests import TestUtil
4982
4983
4984-def test_suite():
4985+def load_tests(standard_tests, module, loader):
4986 testmod_names = [
4987 'test_builddeb',
4988 'test_do',
4989 'test_import_dsc',
4990+ 'test_import_upstream',
4991 'test_mark_uploaded',
4992+ 'test_merge_package',
4993 'test_merge_upstream',
4994 ]
4995- loader = TestUtil.TestLoader()
4996 suite = loader.loadTestsFromModuleNames(["%s.%s" % (__name__, i)
4997 for i in testmod_names])
4998 return suite
4999
5000=== modified file 'tests/blackbox/test_builddeb.py'
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: