265
class DscImporter(object):
269
def __init__(self, dsc_files):
270
self.dsc_files = dsc_files
272
def import_orig(self, tree, origname, version, last_upstream=None,
273
transport=None, base_dir=None, dangling_tree=None):
274
f = open_file(origname, transport, base_dir=base_dir)[0]
276
if self.orig_target is not None:
277
# Make the orig dir and copy the orig tarball in to it
278
if not os.path.isdir(self.orig_target):
279
os.mkdir(self.orig_target)
280
new_filename = os.path.join(self.orig_target,
281
os.path.basename(origname))
282
new_f = open(new_filename, 'wb')
347
class DistributionBranchSet(object):
348
"""A collection of DistributionBranches with an ordering.
350
A DistributionBranchSet collects a group of DistributionBranches
351
and an order, and then can provide the branches with information
352
about their place in the relationship with other branches.
356
"""Create a DistributionBranchSet."""
357
self._branch_list = []
359
def add_branch(self, branch):
360
"""Adds a DistributionBranch to the end of the list.
362
Appends the passed distribution branch to the end of the list
363
that this DistributionBranchSet represents. It also provides
364
the distribution branch with a way to get the branches that
365
are before and after it in the list.
367
It will call branch.set_get_lesser_branches_callback() and
368
branch.set_get_greater_branches_callback(), passing it methods
369
that the DistributionBranch can call to get the list of branches
370
before it in the list and after it in the list respectively.
371
The passed methods take no arguments and return a list (possibly
372
empty) of the desired branches.
374
:param branch: the DistributionBranch to add.
376
self._branch_list.append(branch)
377
lesser_callback = self._make_lesser_callback(branch)
378
branch.set_get_lesser_branches_callback(lesser_callback)
379
greater_callback = self._make_greater_callback(branch)
380
branch.set_get_greater_branches_callback(greater_callback)
382
def _make_lesser_callback(self, branch):
383
return lambda: self.get_lesser_branches(branch)
385
def _make_greater_callback(self, branch):
386
return lambda: self.get_greater_branches(branch)
388
def get_lesser_branches(self, branch):
389
"""Return the list of branches less than the argument.
391
:param branch: The branch that all branches returned must be less
393
:return: a (possibly empty) list of all the branches that are
394
less than the argument. The list is sorted starting with the
397
index = self._branch_list.index(branch)
398
return self._branch_list[:index]
400
def get_greater_branches(self, branch):
401
"""Return the list of branches greater than the argument.
403
:param branch: The branch that all branches returned must be greater
405
:return: a (possibly empty) list of all the branches that are
406
greater than the argument. The list is sorted starting with the
409
index = self._branch_list.index(branch)
410
return self._branch_list[index+1:]
413
class DistributionBranch(object):
414
"""A DistributionBranch is a representation of one line of development.
416
It is a branch that is linked to a line of development, such as Debian
417
unstable. It also has associated branches, some of which are "lesser"
418
and some are "greater". A lesser branch is one that this branch
419
derives from. A greater branch is one that derives from this. For
420
instance Debian experimental would have unstable as a lesser branch,
421
and vice-versa. It is assumed that a group of DistributionBranches will
422
have a total ordering with respect to these relationships.
425
def __init__(self, branch, upstream_branch, tree=None,
427
"""Create a distribution branch.
429
You can only import packages on to the DistributionBranch
430
if both tree and upstream_tree are provided.
432
:param branch: the Branch for the packaging part.
433
:param upstream_branch: the Branch for the upstream part, if any.
434
:param tree: an optional tree for the branch.
435
:param upstream_tree: an optional upstream_tree for the
439
self.upstream_branch = upstream_branch
441
self.upstream_tree = upstream_tree
442
self.get_lesser_branches = None
443
self.get_greater_branches = None
445
def set_get_lesser_branches_callback(self, callback):
446
"""Set the callback to get the branches "lesser" than this.
448
The function passed to this method will be used to get the
449
list of branches that are "lesser" than this one. It is
450
expected to require no arguments, and to return the desired
451
(possibly empty) list of branches. The returned list should
452
be sorted starting with the least element.
454
:param callback: a function that is called to get the desired list
457
self.get_lesser_branches = callback
459
def set_get_greater_branches_callback(self, callback):
460
"""Set the callback to get the branches "greater" than this.
462
The function passed to this method will be used to get the
463
list of branches that are "greater" than this one. It is
464
expected to require no arguments, and to return the desired
465
(possibly empty) list of branches. The returned list should
466
be sorted starting with the least element.
468
:param callback: a function that is called to get the desired list
471
self.get_greater_branches = callback
473
def get_other_branches(self):
474
"""Return all the other branches in this set.
476
The returned list will be ordered, and will not contain this
479
:return: a list of all the other branches in this set (if any).
481
return self.get_lesser_branches() + self.get_greater_branches()
483
def tag_name(self, version):
484
"""Gets the name of the tag that is used for the version.
486
:param version: the Version object that the tag should refer to.
487
:return: a String with the name of the tag.
491
def upstream_tag_name(self, version, distro=None):
492
"""Gets the tag name for the upstream part of version.
494
:param version: the Version object to extract the upstream
495
part of the version number from.
496
:return: a String with the name of the tag.
498
assert isinstance(version, str)
499
tag_name = self.tag_name(version)
501
return "upstream-" + tag_name
502
return "upstream-%s-%s" % (distro, tag_name)
504
def _has_version(self, branch, tag_name, md5=None):
505
if branch.tags.has_tag(tag_name):
506
revid = branch.tags.lookup_tag(tag_name)
509
graph = branch.repository.get_graph()
510
if not graph.is_ancestor(revid, branch.last_revision()):
516
rev = branch.repository.get_revision(revid)
518
return rev.properties['deb-md5'] == md5
520
warning("tag %s present in branch, but there is no "
521
"associated 'deb-md5' property" % tag_name)
525
def has_version(self, version, md5=None):
526
"""Whether this branch contains the package version specified.
528
The version must be judged present by having the appropriate tag
529
in the branch. If the md5 argument is not None then the string
530
passed must the the md5sum that is associated with the revision
531
pointed to by the tag.
533
:param version: a Version object to look for in this branch.
534
:param md5: a string with the md5sum that if not None must be
535
associated with the revision.
536
:return: True if this branch contains the specified version of the
537
package. False otherwise.
539
tag_name = self.tag_name(version)
540
if self._has_version(self.branch, tag_name, md5=md5):
542
debian_tag_name = "debian-" + tag_name
543
if self._has_version(self.branch, debian_tag_name, md5=md5):
545
ubuntu_tag_name = "ubuntu-" + tag_name
546
if self._has_version(self.branch, ubuntu_tag_name, md5=md5):
550
def has_upstream_version(self, version, md5=None):
551
"""Whether this branch contains the upstream version specified.
553
The version must be judged present by having the appropriate tag
554
in the upstream branch. If the md5 argument is not None then the
555
string passed must the the md5sum that is associated with the
556
revision pointed to by the tag.
558
:param version: a upstream version number to look for in the upstream
560
:param md5: a string with the md5sum that if not None must be
561
associated with the revision.
562
:return: True if the upstream branch contains the specified upstream
563
version of the package. False otherwise.
565
tag_name = self.upstream_tag_name(version)
566
if self._has_version(self.upstream_branch, tag_name, md5=md5):
568
tag_name = self.upstream_tag_name(version, distro="debian")
569
if self._has_version(self.upstream_branch, tag_name, md5=md5):
571
tag_name = self.upstream_tag_name(version, distro="ubuntu")
572
if self._has_version(self.upstream_branch, tag_name, md5=md5):
576
def has_upstream_version_in_packaging_branch(self, version, md5=None):
577
assert isinstance(version, str)
578
tag_name = self.upstream_tag_name(version)
579
if self._has_version(self.branch, tag_name, md5=md5):
581
tag_name = self.upstream_tag_name(version, distro="debian")
582
if self._has_version(self.branch, tag_name, md5=md5):
584
tag_name = self.upstream_tag_name(version, distro="ubuntu")
585
if self._has_version(self.branch, tag_name, md5=md5):
589
def contained_versions(self, versions):
590
"""Splits a list of versions depending on presence in the branch.
592
Partitions the input list of versions depending on whether they
593
are present in the branch or not.
595
The two output lists will be sorted in the same order as the input
598
:param versions: a list of Version objects to look for in the
599
branch. May be an empty list.
600
:return: A tuple of two lists. The first list is the list of those
601
items from the input list that are present in the branch. The
602
second list is the list of those items from the input list that
603
are not present in the branch. The two lists will be disjoint
604
and cover the input list. Either list may be empty, or both if
605
the input list is empty.
607
#FIXME: should probably do an ancestory check to find all
608
# merged revisions. This will avoid adding an extra parent
610
# experimental 1-1~rc1
611
# unstable 1-1 1-1~rc1
612
# Ubuntu 1-1ubuntu1 1-1 1-1~rc1
613
# where only the first in each list is actually uploaded.
616
for version in versions:
617
if self.has_version(version):
618
contained.append(version)
620
not_contained.append(version)
621
return contained, not_contained
623
def missing_versions(self, versions):
624
"""Returns the versions from the list that the branch does not have.
626
Looks at all the versions specified and returns a list of the ones
627
that are earlier in the list that the last version that is
628
contained in this branch.
630
:param versions: a list of Version objects to look for in the branch.
631
May be an empty list.
632
:return: The subset of versions from the list that are not present
633
in this branch. May be an empty list.
635
last_contained = self.last_contained_version(versions)
636
if last_contained is None:
638
index = versions.index(last_contained)
639
return versions[:index]
641
def last_contained_version(self, versions):
642
"""Returns the highest version from the list present in this branch.
644
It assumes that the input list of versions is sorted with the
645
highest version first.
647
:param versions: a list of Version objects to look for in the branch.
648
Must be sorted with the highest version first. May be an empty
650
:return: the highest version that is contained in this branch, or
651
None if none of the versions are contained within the branch.
653
for version in versions:
654
if self.has_version(version):
658
def revid_of_version(self, version):
659
"""Returns the revision id corresponding to that version.
661
:param version: the Version object that you wish to retrieve the
662
revision id of. The Version must be present in the branch.
663
:return: the revision id corresponding to that version
665
tag_name = self.tag_name(version)
666
if self._has_version(self.branch, tag_name):
667
return self.branch.tags.lookup_tag(tag_name)
668
debian_tag_name = "debian-" + tag_name
669
if self._has_version(self.branch, debian_tag_name):
670
return self.branch.tags.lookup_tag(debian_tag_name)
671
ubuntu_tag_name = "ubuntu-" + tag_name
672
if self._has_version(self.branch, ubuntu_tag_name):
673
return self.branch.tags.lookup_tag(ubuntu_tag_name)
674
return self.branch.tags.lookup_tag(tag_name)
676
def revid_of_upstream_version(self, version):
677
"""Returns the revision id corresponding to the upstream version.
679
:param version: the Version object to extract the upstream version
680
from to retreive the revid of. The upstream version must be
681
present in the upstream branch.
682
:return: the revision id corresponding to the upstream portion
685
tag_name = self.upstream_tag_name(version)
686
if self._has_version(self.upstream_branch, tag_name):
687
return self.upstream_branch.tags.lookup_tag(tag_name)
688
tag_name = self.upstream_tag_name(version, distro="debian")
689
if self._has_version(self.upstream_branch, tag_name):
690
return self.upstream_branch.tags.lookup_tag(tag_name)
691
tag_name = self.upstream_tag_name(version, distro="ubuntu")
692
if self._has_version(self.upstream_branch, tag_name):
693
return self.upstream_branch.tags.lookup_tag(tag_name)
694
tag_name = self.upstream_tag_name(version)
695
return self.upstream_branch.tags.lookup_tag(tag_name)
697
def tag_version(self, version):
698
"""Tags the branch's last revision with the given version.
700
Sets a tag on the last revision of the branch with a tag that refers
701
to the version provided.
703
:param version: the Version object to derive the tag name from.
704
:return: Name of the tag set
706
tag_name = self.tag_name(version)
707
self.branch.tags.set_tag(tag_name,
708
self.branch.last_revision())
711
def tag_upstream_version(self, version):
712
"""Tags the upstream branch's last revision with an upstream version.
714
Sets a tag on the last revision of the upstream branch with a tag
715
that refers to the upstream part of the version provided.
717
:param version: the upstream part of the version number to derive the
720
assert isinstance(version, str)
721
tag_name = self.upstream_tag_name(version)
722
self.upstream_branch.tags.set_tag(tag_name,
723
self.upstream_branch.last_revision())
724
self.branch.tags.set_tag(tag_name,
725
self.upstream_branch.last_revision())
727
def _default_config_for_tree(self, tree):
728
# FIXME: shouldn't go to configobj directly
729
path = '.bzr-builddeb/default.conf'
730
c_fileid = tree.path2id(path)
732
if c_fileid is not None:
735
config = ConfigObj(tree.get_file(c_fileid, path))
739
config['BUILDDEB'] = {}
745
def _is_tree_native(self, tree):
746
config = self._default_config_for_tree(tree)
747
if config is not None:
749
current_value = config['BUILDDEB']['native']
751
current_value = False
752
return current_value == "True"
755
def is_version_native(self, version):
756
"""Determines whether the given version is native.
758
:param version: the Version object to test. Must be present in
760
:return: True if the version is was recorded as native when
761
imported, False otherwise.
763
revid = self.revid_of_version(version)
764
rev_tree = self.branch.repository.revision_tree(revid)
765
if self._is_tree_native(rev_tree):
767
rev = self.branch.repository.get_revision(revid)
284
new_f.write(f.read())
288
f = open(new_filename)
289
dangling_revid = None
290
if last_upstream is not None:
291
dangling_revid = tree.branch.last_revision()
292
dangling_tree = tree.branch.repository.revision_tree(dangling_revid)
293
old_upstream_revid = tree.branch.tags.lookup_tag(
294
make_upstream_tag(last_upstream))
296
tree.branch.repository.revision_tree(old_upstream_revid))
297
import_tar(tree, f, file_ids_from=dangling_tree)
298
if last_upstream is not None:
299
tree.set_parent_ids([old_upstream_revid])
300
revno = tree.branch.revision_id_to_revno(old_upstream_revid)
301
tree.branch.set_last_revision_info(revno, old_upstream_revid)
302
tree.commit('import upstream from %s' % (os.path.basename(origname)))
303
upstream_version = version.upstream_version
304
tree.branch.tags.set_tag(make_upstream_tag(upstream_version),
305
tree.branch.last_revision())
308
return dangling_revid
310
def import_native(self, tree, origname, version, last_upstream=None,
311
transport=None, base_dir=None):
312
f = open_file(origname, transport, base_dir=base_dir)[0]
314
dangling_revid = None
316
old_upstream_tree = None
317
if last_upstream is not None:
318
old_upstream_revid = tree.branch.tags.lookup_tag(
319
make_upstream_tag(last_upstream))
320
old_upstream_tree = tree.branch.repository.revision_tree(
322
if old_upstream_revid != tree.branch.last_revision():
323
dangling_revid = tree.branch.last_revision()
324
dangling_tree = tree.branch.repository.revision_tree(dangling_revid)
326
tree.branch.repository.revision_tree(old_upstream_revid))
327
import_tar(tree, f, file_ids_from=dangling_tree)
328
if last_upstream is not None:
329
tree.set_parent_ids([old_upstream_revid])
330
revno = tree.branch.revision_id_to_revno(old_upstream_revid)
331
tree.branch.set_last_revision_info(revno, old_upstream_revid)
332
if dangling_revid is not None:
333
tree.add_parent_tree_id(dangling_revid)
334
config_filename = '.bzr-builddeb/default.conf'
337
if not tree.has_filename(config_filename):
338
if not tree.has_filename(os.path.dirname(config_filename)):
339
os.mkdir(os.path.join(tree.basedir,
340
os.path.dirname(config_filename)))
342
conf = open(os.path.join(tree.basedir, config_filename), 'wb')
345
config_ = ConfigObj(os.path.join(tree.basedir, config_filename))
349
config_['BUILDDEB'] = {}
351
current_value = config_['BUILDDEB']['native']
353
current_value = False
354
if not current_value:
355
config_['BUILDDEB']['native'] = True
359
parent = os.path.dirname(config_filename)
360
if old_upstream_tree is not None:
361
file_id = old_upstream_tree.path2id(parent)
362
if file_id is not None:
363
tree.add([parent], [file_id])
368
if old_upstream_tree is not None:
369
file_id = old_upstream_tree.path2id(config_filename)
370
if file_id is not None:
371
tree.add([config_filename], [file_id])
373
tree.add([config_filename])
374
if os.path.isfile(os.path.join(tree.basedir, 'debian', 'rules')):
375
os.chmod(os.path.join(tree.basedir, 'debian', 'rules'),
376
(stat.S_IRWXU|stat.S_IRGRP|stat.S_IXGRP|stat.S_IROTH|
378
tree.commit('import package from %s' % (os.path.basename(origname)))
379
upstream_version = version.upstream_version
380
tree.branch.tags.set_tag(make_upstream_tag(upstream_version),
381
tree.branch.last_revision())
385
def _make_filter_proc(self):
386
"""Create a filterdiff subprocess."""
387
filter_cmd = ['filterdiff'] + ignore_arguments
388
filter_proc = Popen(filter_cmd, stdin=PIPE, stdout=PIPE)
391
def _patch_tree(self, patch, basedir):
392
"""Patch a tree located at basedir."""
393
filter_proc = self._make_filter_proc()
394
patch_cmd = ['patch', '-g', '0', '--strip', '1', '--quiet', '-f',
395
'--directory', basedir]
396
patch_proc = Popen(patch_cmd, stdin=filter_proc.stdout, close_fds=True)
398
filter_proc.stdin.write(line)
399
filter_proc.stdin.flush()
400
filter_proc.stdin.close()
401
r = patch_proc.wait()
403
raise BzrError('patch failed')
405
def _get_touched_paths(self, patch):
406
filter_proc = self._make_filter_proc()
407
cmd = ['lsdiff', '--strip', '1']
408
child_proc = Popen(cmd, stdin=filter_proc.stdout, stdout=PIPE,
412
filter_proc.stdin.write(line)
413
filter_proc.stdin.flush()
414
while select.select([child_proc.stdout], [], [], 0)[0]:
415
output += child_proc.stdout.read(1)
416
filter_proc.stdin.close()
417
output += child_proc.stdout.read()
419
for filename in output.split('\n'):
420
if filename.endswith('\n'):
421
filename = filename[:-1]
422
touched_paths.append(filename)
423
r = child_proc.wait()
425
raise BzrError('lsdiff failed')
428
def _add_implied_parents(self, tree, implied_parents, path,
430
parent = os.path.dirname(path)
433
if parent in implied_parents:
435
implied_parents.add(parent)
436
self._add_implied_parents(tree, implied_parents, parent,
437
file_ids_from=file_ids_from)
438
if file_ids_from is None:
441
file_id = file_ids_from.path2id(parent)
445
tree.add([parent], [file_id])
447
def _update_path_info(self, tree, touched_paths, other_parent, main_parent):
448
implied_parents = set()
449
for path in touched_paths:
450
if not tree.has_filename(path):
451
tree.remove([path], verbose=False)
452
elif not other_parent.has_filename(path):
453
self._add_implied_parents(tree, implied_parents, path,
454
file_ids_from=other_parent)
456
elif not (main_parent.has_filename(path) and
457
other_parent.has_filename(path)):
458
self._add_implied_parents(tree, implied_parents, path,
459
file_ids_from=other_parent)
460
file_id = other_parent.path2id(path)
464
tree.add([path], [file_id])
466
def _get_upstream_tree(self, version, tree):
467
upstream_version = version.upstream_version
468
up_revid = tree.branch.tags.lookup_tag(make_upstream_tag(upstream_version))
469
return tree.branch.repository.revision_tree(up_revid)
472
def import_diff(self, tree, diffname, version, dangling_revid=None,
473
transport=None, base_dir=None, no_add_extra_parent=False):
474
up_tree = self._get_upstream_tree(version, tree)
475
if dangling_revid is None:
476
current_revid = tree.branch.last_revision()
478
current_revid = dangling_revid
479
current_tree = tree.branch.repository.revision_tree(current_revid)
480
tree.revert(None, up_tree)
481
f = open_file(diffname, transport, base_dir=base_dir)[0]
482
f = gzip.GzipFile(fileobj=f)
484
self._patch_tree(f, tree.basedir)
485
if os.path.isfile(os.path.join(tree.basedir, 'debian', 'rules')):
486
os.chmod(os.path.join(tree.basedir, 'debian', 'rules'),
487
(stat.S_IRWXU|stat.S_IRGRP|stat.S_IXGRP|stat.S_IROTH|
490
touched_paths = self._get_touched_paths(f)
491
self._update_path_info(tree, touched_paths, current_tree, up_tree)
492
if (not no_add_extra_parent and dangling_revid is not None):
493
tree.add_parent_tree_id(dangling_revid)
494
tree.commit('merge packaging changes from %s' % \
495
(os.path.basename(diffname)))
499
def _add_to_safe(self, file, version, type, base, transport):
501
for safe_file in self.safe_files:
502
if file == safe_file[0]:
506
self.safe_files.append((file, version, type, base, transport))
508
def _check_orig_exists(self, version):
510
for safe_file in self.safe_files:
511
if safe_file[0].endswith("_%s.orig.tar.gz" % version.upstream_version):
515
raise ImportError("There is no upstream tarball corresponding to %s" % \
518
def _check_package_name(self, name):
519
if self.package_name is not None and name != self.package_name:
520
raise ImportError("The reported package name has changed from %s to "
521
"%s. I don't know what to do in this case. If this "
522
"case should be handled, please contact the author "
523
"with details of your case, and the expected outcome."
524
% (self.package_name, name))
525
self.package_name = name
527
def _decode_dsc(self, dsc, dscname, incremental=False):
531
self._check_package_name(dsc['Source'])
532
for file_details in dsc['files']:
533
name = file_details['name']
534
if name.endswith('.orig.tar.gz'):
535
if orig_file is not None:
536
raise ImportError("%s contains more than one .orig.tar.gz" % dscname)
538
elif name.endswith('.diff.gz'):
539
if diff_file is not None:
540
raise ImportError("%s contains more than one .diff.gz" % dscname)
542
elif name.endswith('.tar.gz'):
543
if native_file is not None:
544
raise ImportError("%s contains more than one .tar.gz" % dscname)
546
version = Version(dsc['Version'])
547
if self.transport is not None:
548
base_dir = urlutils.split(dscname)[0]
551
dsc_transport = self.cache.get_transport(dscname)
552
if native_file is not None:
553
if diff_file is not None or orig_file is not None:
554
raise ImportError("%s contains both a native package and a normal "
555
"package." % dscname)
556
self._add_to_safe(native_file, version, 'native', base_dir,
559
if diff_file is None:
560
raise ImportError("%s contains only a .orig.tar.gz, it must contain a "
561
".diff.gz as well" % dscname)
562
if orig_file is not None:
563
self._add_to_safe(orig_file, version, 'orig', base_dir, dsc_transport)
565
self._check_orig_exists(version)
566
self._add_to_safe(diff_file, version, 'diff', base_dir, dsc_transport)
568
def import_dsc(self, target_dir, orig_target=None):
569
if os.path.exists(target_dir):
570
raise FileExists(target_dir)
571
self.orig_target = orig_target
572
self.cache = DscCache(transport=self.transport)
573
self.dsc_files.sort(cmp=DscComp(self.cache).cmp)
575
self.package_name = None
576
for dscname in self.dsc_files:
577
dsc = self.cache.get_dsc(dscname)
578
self._decode_dsc(dsc, dscname)
580
format = bzrdir.format_registry.make_bzrdir('default')
581
branch = bzrdir.BzrDir.create_branch_convenience(target_dir,
583
tree = branch.bzrdir.open_workingtree()
587
dangling_revid = None
589
for (filename, version, type, base_dir, transport) in self.safe_files:
593
dangling_revid = self.import_orig(tree, filename, version,
594
last_upstream=last_upstream,
597
info("imported %s" % filename)
598
last_upstream = version.upstream_version
601
self.import_diff(tree, filename, version,
602
dangling_revid=dangling_revid,
603
transport=transport, base_dir=base_dir)
604
info("imported %s" % filename)
605
dangling_revid = None
607
elif type == 'native':
608
self.import_native(tree, filename, version,
609
last_upstream=last_upstream,
610
transport=transport, base_dir=base_dir)
611
last_upstream = version.upstream_version
613
info("imported %s" % filename)
617
def _find_last_upstream(self, tree, version):
619
previous_upstream = None
620
tag_dict = tree.branch.tags.get_reverse_tag_dict()
621
for rev in reversed(tree.branch.revision_history()):
623
for poss_tag in tag_dict[rev]:
624
poss_version = upstream_tag_to_version(poss_tag)
625
if poss_version is None:
627
if poss_version < version:
628
return poss_version, previous_upstream
629
previous_upstream = poss_version
630
return None, previous_upstream
632
def incremental_import_dsc(self, target, orig_target=None):
633
self.orig_target = orig_target
634
tree = WorkingTree.open_containing(target)[0]
635
if tree.changes_from(tree.basis_tree()).has_changed():
636
raise UncommittedChanges(tree)
637
self.cache = DscCache(transport=self.transport)
638
if len(self.dsc_files) > 1:
639
raise OnlyImportSingleDsc
641
self.package_name = None
642
dsc = self.cache.get_dsc(self.dsc_files[0])
643
self._decode_dsc(dsc, self.dsc_files[0], incremental=True)
646
current_rev_id = tree.branch.last_revision()
647
current_revno = tree.branch.revision_id_to_revno(current_rev_id)
648
dangling_revid = None
650
for (filename, version, type, base_dir, transport) in self.safe_files:
652
self.import_diff(tree, filename, version,
653
dangling_revid=dangling_revid,
654
transport=transport, base_dir=base_dir,
655
no_add_extra_parent=True)
656
info("imported %s" % filename)
657
dangling_revid = tree.branch.last_revision()
658
tree.pull(tree.branch, overwrite=True, stop_revision=current_rev_id)
659
tree.merge_from_branch(tree.branch, to_revision=dangling_revid,
660
from_revision=merge_base)
663
last_upstream, previous_upstream = \
664
self._find_last_upstream(tree, version)
665
if last_upstream is None:
666
dangling_revid = tree.branch.tags.lookup_tag(
667
make_upstream_tag(previous_upstream))
668
dangling_tree = tree.branch.repository.revision_tree(dangling_revid)
670
tree.branch.repository.revision_tree(NULL_REVISION),
672
tree.set_parent_ids([])
673
tree.branch.set_last_revision_info(0, NULL_REVISION)
674
dangling_revid = self.import_orig(tree, filename, version,
675
last_upstream=last_upstream,
677
dangling_tree=dangling_tree,
679
if last_upstream is None:
680
merge_base = NULL_REVISION
681
dangling_revid = current_rev_id
682
info("imported %s" % filename)
683
elif type == 'native':
684
assert False, "Native packages not yet supported"
689
class SourcesImporter(DscImporter):
690
"""For importing all the .dsc files from a Sources file."""
692
def __init__(self, base, sources_path, other_sources=[]):
693
"""Create a SourcesImporter.
695
:param base: the base URI from which all paths should be interpreted.
697
:param sources_path: the path to the Sources file to import the
698
packages from, relative to the base parameter.
701
self.base = urlutils.normalize_url(base)
702
if isinstance(sources_path, unicode):
703
sources_path = sources_path.encode('utf-8')
704
self.sources_path = sources_path
705
self.transport = get_transport(self.base)
706
sources_file = self.transport.get(self.sources_path)
707
if self.sources_path.endswith(".gz"):
708
sources_file = gzip.GzipFile(fileobj=sources_file)
710
for source in deb822.Sources.iter_paragraphs(sources_file):
711
base_dir = source['Directory']
712
if not self._check_basedir(base_dir):
714
for file_info in source['files']:
715
name = file_info['name']
716
if name.endswith('.dsc'):
717
dsc_files.append(urlutils.join(base_dir, name))
718
dsc_files += other_sources
719
super(SourcesImporter, self).__init__(dsc_files)
721
def _check_basedir(self, base_dir):
725
class SnapshotImporter(SourcesImporter):
726
"""Import all versions of a package recorded on snapshot.debian.net."""
728
def __init__(self, package_name, other_sources=[]):
729
base = 'http://snapshot.debian.net/archive/'
730
path = 'pool/%s/%s/source/Sources.gz' % (package_name[0], package_name)
731
super(SnapshotImporter, self).__init__(base, path, other_sources=other_sources)
732
warning("snapshot.debian.net has lost packages from before 12/03/2005, "
733
"only packages from after that date will be imported.")
735
def _check_basedir(self, base_dir):
737
match = re.match(r'(?P<year>\d\d\d\d)/(?P<month>\d\d)/(?P<day>\d\d)',
739
if match is not None:
740
year = int(match.group('year'))
744
month = int(match.group('month'))
748
day = int(match.group('day'))
769
prop = rev.properties["deb-native"]
770
return prop == "True"
753
# vim: ts=2 sts=2 sw=2
774
def branch_to_pull_version_from(self, version, md5):
775
"""Checks whether this upload is a pull from a lesser branch.
777
Looks in all the lesser branches for the given version/md5 pair
778
in a branch that has not diverged from this.
780
If it is present in another branch that has not diverged this
781
method will return the greatest branch that it is present in,
782
otherwise it will return None. If it returns a branch then it
783
indicates that a pull should be done from that branch, rather
784
than importing the version as a new revision in this branch.
786
:param version: the Version object to look for in the lesser
788
:param md5: a String containing the md5 associateed with the
790
:return: a DistributionBranch object to pull from if that is
791
what should be done, otherwise None.
793
assert md5 is not None, \
794
("It's not a good idea to use branch_to_pull_version_from with "
795
"md5 == None, as you may pull the wrong revision.")
796
self.branch.lock_read()
798
for branch in reversed(self.get_lesser_branches()):
799
if branch.has_version(version, md5=md5):
800
# Check that they haven't diverged
801
branch.branch.lock_read()
803
graph = branch.branch.repository.get_graph(
804
self.branch.repository)
805
other_revid = branch.revid_of_version(version)
806
if len(graph.heads([other_revid,
807
self.branch.last_revision()])) == 1:
810
branch.branch.unlock()
811
for branch in self.get_greater_branches():
812
if branch.has_version(version, md5=md5):
813
# Check that they haven't diverged
814
branch.branch.lock_read()
816
graph = branch.branch.repository.get_graph(
817
self.branch.repository)
818
other_revid = branch.revid_of_version(version)
819
if len(graph.heads([other_revid,
820
self.branch.last_revision()])) == 1:
823
branch.branch.unlock()
828
def branch_to_pull_upstream_from(self, version, md5):
829
"""Checks whether this upstream is a pull from a lesser branch.
831
Looks in all the other upstream branches for the given
832
version/md5 pair in a branch that has not diverged from this.
833
If it is present in a lower branch this method will return the
834
greatest branch that it is present in that has not diverged,
835
otherwise it will return None. If it returns a branch then it
836
indicates that a pull should be done from that branch, rather
837
than importing the upstream as a new revision in this branch.
839
:param version: the upstream version to use when searching in the
841
:param md5: a String containing the md5 associateed with the
843
:return: a DistributionBranch object to pull the upstream from
844
if that is what should be done, otherwise None.
846
assert isinstance(version, str)
847
assert md5 is not None, \
848
("It's not a good idea to use branch_to_pull_upstream_from with "
849
"md5 == None, as you may pull the wrong revision.")
850
up_branch = self.upstream_branch
851
up_branch.lock_read()
853
for branch in reversed(self.get_lesser_branches()):
854
if branch.has_upstream_version(version, md5=md5):
855
# Check for divergenge.
856
other_up_branch = branch.upstream_branch
857
other_up_branch.lock_read()
859
graph = other_up_branch.repository.get_graph(
860
up_branch.repository)
861
other_revid = branch.revid_of_upstream_version(
863
if len(graph.heads([other_revid,
864
up_branch.last_revision()])) == 1:
867
other_up_branch.unlock()
868
for branch in self.get_greater_branches():
869
if branch.has_upstream_version(version, md5=md5):
870
# Check for divergenge.
871
other_up_branch = branch.upstream_branch
872
other_up_branch.lock_read()
874
graph = other_up_branch.repository.get_graph(
875
up_branch.repository)
876
other_revid = branch.revid_of_upstream_version(
878
if len(graph.heads([other_revid,
879
up_branch.last_revision()])) == 1:
882
other_up_branch.unlock()
887
def get_parents(self, versions):
888
"""Return the list of parents for a specific version.
890
This method returns the list of revision ids that should be parents
891
for importing a specifc package version. The specific package version
892
is the first element of the list of versions passed.
894
The parents are determined by looking at the other versions in the
895
passed list and examining which of the branches (if any) they are
898
You should probably use get_parents_with_upstream rather than
901
:param versions: a list of Version objects, the first item of
902
which is the version of the package that is currently being
904
:return: a list of tuples of (DistributionBranch, version,
905
revision id). The revision ids should all be parents of the
906
revision that imports the specified version of the package.
907
The versions are the versions that correspond to that revision
908
id. The DistributionBranch is the branch that contains that
911
assert len(versions) > 0, "Need a version to import"
912
mutter("Getting parents of %s" % str(versions))
913
missing_versions = self.missing_versions(versions)
914
mutter("Versions we don't have are %s" % str(missing_versions))
915
last_contained_version = self.last_contained_version(versions)
917
if last_contained_version is not None:
918
assert last_contained_version != versions[0], \
919
"Reupload of a version?"
920
mutter("The last versions we do have is %s" \
921
% str(last_contained_version))
922
parents = [(self, last_contained_version,
923
self.revid_of_version(last_contained_version))]
925
mutter("We don't have any of those versions")
926
for branch in reversed(self.get_lesser_branches()):
927
merged, missing_versions = \
928
branch.contained_versions(missing_versions)
930
revid = branch.revid_of_version(merged[0])
931
parents.append((branch, merged[0], revid))
932
mutter("Adding merge from lesser of %s for version %s"
933
% (revid, str(merged[0])))
934
#FIXME: should this really be here?
935
branch.branch.tags.merge_to(self.branch.tags)
936
self.branch.fetch(branch.branch,
938
for branch in self.get_greater_branches():
939
merged, missing_versions = \
940
branch.contained_versions(missing_versions)
942
revid = branch.revid_of_version(merged[0])
943
parents.append((branch, merged[0], revid))
944
mutter("Adding merge from greater of %s for version %s"
945
% (revid, str(merged[0])))
946
#FIXME: should this really be here?
947
branch.branch.tags.merge_to(self.branch.tags)
948
self.branch.fetch(branch.branch,
952
def pull_upstream_from_branch(self, pull_branch, version):
953
"""Pulls an upstream version from a branch.
955
Given a DistributionBranch and a version number this method
956
will pull the upstream part of the given version from the
957
branch in to this. The upstream version must be present
958
in the DistributionBranch, and it is assumed that the md5
961
It sets the necessary tags so that the pulled version is
962
recognised as being part of this branch.
964
:param pull_branch: the DistributionBranch to pull from.
965
:param version: the upstream version string
967
assert isinstance(version, str)
968
pull_revision = pull_branch.revid_of_upstream_version(version)
969
mutter("Pulling upstream part of %s from revision %s" % \
970
(version, pull_revision))
971
up_pull_branch = pull_branch.upstream_branch
972
assert self.upstream_tree is not None, \
973
"Can't pull upstream with no tree"
974
self.upstream_tree.pull(up_pull_branch,
975
stop_revision=pull_revision)
976
self.tag_upstream_version(version)
977
self.branch.fetch(self.upstream_branch, last_revision=pull_revision)
978
self.upstream_branch.tags.merge_to(self.branch.tags)
980
def pull_version_from_branch(self, pull_branch, version, native=False):
981
"""Pull a version from a particular branch.
983
Given a DistributionBranch and a version number this method
984
will pull the given version from the branch in to this. The
985
version must be present in the DistributionBranch, and it
986
is assumed that the md5 matches.
988
It will also pull in any upstream part that is needed to
989
the upstream branch. It is assumed that the md5 matches
990
here as well. If the upstream version must be present in
991
at least one of the upstream branches.
993
It sets the necessary tags on the revisions so they are
994
recongnised in this branch as well.
996
:param pull_branch: the DistributionBranch to pull from.
997
:param version: the Version to pull.
998
:param native: whether it is a native version that is being
1001
pull_revision = pull_branch.revid_of_version(version)
1002
mutter("already has version %s so pulling from revision %s"
1003
% (str(version), pull_revision))
1004
assert self.tree is not None, "Can't pull branch with no tree"
1005
self.tree.pull(pull_branch.branch, stop_revision=pull_revision)
1006
self.tag_version(version)
1007
if not native and not self.has_upstream_version(version.upstream_version):
1008
if pull_branch.has_upstream_version(version.upstream_version):
1009
self.pull_upstream_from_branch(pull_branch,
1010
version.upstream_version)
1012
assert False, ("Can't find the needed upstream part "
1013
"for version %s" % version)
1014
if (native and self.upstream_branch.last_revision() == NULL_REVISION
1015
and pull_branch.upstream_branch.last_revision() != NULL_REVISION):
1016
# in case the package wasn't native before then we pull
1017
# the upstream. These checks may be a bit restrictive.
1018
self.upstream_tree.pull(pull_branch.upstream_branch)
1019
pull_branch.upstream_branch.tags.merge_to(self.upstream_branch.tags)
1021
mutter("Not checking for upstream as it is a native package")
1023
mutter("Not importing the upstream part as it is already "
1024
"present in the upstream branch")
1026
def get_parents_with_upstream(self, version, versions,
1027
force_upstream_parent=False):
1028
"""Get the list of parents including any upstream parents.
1030
Further to get_parents this method includes any upstream parents
1031
that are needed. An upstream parent is needed if none of
1032
the other parents include the upstream version. The needed
1033
upstream must already present in the upstream branch before
1034
calling this method.
1036
If force_upstream_parent is True then the upstream parent will
1037
be included, even if another parent is already using that
1038
upstream. This is for use in cases where the .orig.tar.gz
1039
is different in two ditributions.
1041
:param version: the Version that we are currently importing.
1042
:param versions: the list of Versions that are ancestors of
1043
version, including version itself. Sorted with the latest
1044
versions first, so version must be the first entry.
1045
:param force_upstream_parent: if True then an upstream parent
1046
will be added as the first parent, regardless of what the
1048
:return: a list of revision ids that should be the parents when
1049
importing the specified revision.
1051
assert version == versions[0], \
1052
"version is not the first entry of versions"
1053
parents = self.get_parents(versions)
1054
need_upstream_parent = True
1055
if not force_upstream_parent:
1056
for parent_pair in parents:
1057
if (parent_pair[1].upstream_version == \
1058
version.upstream_version):
1059
need_upstream_parent = False
1061
real_parents = [p[2] for p in parents]
1062
if need_upstream_parent:
1063
parent_revid = self.revid_of_upstream_version(version.upstream_version)
1064
if len(parents) > 0:
1065
real_parents.insert(1, parent_revid)
1067
real_parents = [parent_revid]
1070
def _fetch_upstream_to_branch(self, revid):
1071
"""Fetch the revision from the upstream branch in to the packaging one.
1073
# Make sure we see any revisions added by the upstream branch
1074
# since self.tree was locked.
1075
self.branch.repository.refresh_data()
1076
self.branch.fetch(self.upstream_branch, last_revision=revid)
1077
self.upstream_branch.tags.merge_to(self.branch.tags)
1079
def import_upstream(self, upstream_part, version, md5, upstream_parents,
1080
upstream_tarball=None, upstream_branch=None,
1081
upstream_revision=None, timestamp=None, author=None):
1082
"""Import an upstream part on to the upstream branch.
1084
This imports the upstream part of the code and places it on to
1085
the upstream branch, setting the necessary tags.
1087
:param upstream_part: the path of a directory containing the
1088
unpacked upstream part of the source package.
1089
:param version: upstream version that is being imported
1090
:param md5: the md5 of the upstream part.
1091
:param upstream_parents: the parents to give the upstream revision
1092
:param timestamp: a tuple of (timestamp, timezone) to use for
1093
the commit, or None to use the current time.
1095
# Should we just dump the upstream part on whatever is currently
1096
# there, or try and pull all of the other upstream versions
1097
# from lesser branches first? For now we'll just dump it on.
1098
# TODO: this method needs a lot of work for when we will make
1099
# the branches writeable by others.
1100
assert isinstance(version, str)
1101
mutter("Importing upstream version %s from %s with parents %s" \
1102
% (version, upstream_part, str(upstream_parents)))
1103
assert self.upstream_tree is not None, \
1104
"Can't import upstream with no tree"
1105
if len(upstream_parents) > 0:
1106
parent_revid = upstream_parents[0]
1108
parent_revid = NULL_REVISION
1109
self.upstream_tree.pull(self.upstream_tree.branch, overwrite=True,
1110
stop_revision=parent_revid)
1111
other_branches = self.get_other_branches()
1112
def get_last_revision_tree(br):
1113
return br.repository.revision_tree(br.last_revision())
1114
upstream_trees = [get_last_revision_tree(o.upstream_branch)
1115
for o in other_branches]
1116
if upstream_branch is not None:
1117
if upstream_revision is None:
1118
upstream_revision = upstream_branch.last_revision()
1119
self.upstream_branch.fetch(upstream_branch,
1120
last_revision=upstream_revision)
1121
upstream_branch.tags.merge_to(self.upstream_branch.tags)
1122
upstream_parents.append(upstream_revision)
1123
upstream_trees.insert(0,
1124
self.upstream_branch.repository.revision_tree(
1126
import_dir(self.upstream_tree, upstream_part,
1127
file_ids_from=upstream_trees + [self.tree])
1128
self.upstream_tree.set_parent_ids(upstream_parents)
1129
revprops = {"deb-md5": md5}
1130
if upstream_tarball is not None:
1131
delta = self.make_pristine_tar_delta(self.upstream_tree,
1133
uuencoded = standard_b64encode(delta)
1134
revprops["deb-pristine-delta"] = uuencoded
1135
if author is not None:
1136
revprops['authors'] = author
1138
if timestamp is not None:
1139
timezone = timestamp[1]
1140
timestamp = timestamp[0]
1141
revid = self.upstream_tree.commit("Import upstream version %s" \
1143
revprops=revprops, timestamp=timestamp, timezone=timezone)
1144
self.tag_upstream_version(version)
1147
def _mark_native_config(self, native):
1148
poss_native_tree = self.branch.repository.revision_tree(
1149
self.branch.last_revision())
1150
current_native = self._is_tree_native(poss_native_tree)
1151
current_config = self._default_config_for_tree(poss_native_tree)
1152
dirname = os.path.join(self.tree.basedir,
1154
if current_config is not None:
1155
# Add that back to the current tree
1156
if not os.path.exists(dirname):
1158
current_config.filename = os.path.join(dirname,
1160
current_config.write()
1161
dir_id = poss_native_tree.path2id('.bzr-builddeb')
1162
file_id = poss_native_tree.path2id(
1163
'.bzr-builddeb/default.conf')
1164
self.tree.add(['.bzr-builddeb/',
1165
'.bzr-builddeb/default.conf'],
1166
ids=[dir_id, file_id])
1167
if native != current_native:
1168
if current_config is None:
1171
current_config = ConfigObj()
1172
current_config['BUILDDEB'] = {}
1173
if current_config is not None:
1175
current_config['BUILDDEB']['native'] = True
1177
del current_config['BUILDDEB']['native']
1178
if len(current_config['BUILDDEB']) == 0:
1179
del current_config['BUILDDEB']
1180
if len(current_config) == 0:
1181
self.tree.remove(['.bzr-builddeb',
1182
'.bzr-builddeb/default.conf'],
1187
current_config.filename = os.path.join(dirname,
1189
current_config.write()
1191
self.tree.add(['.bzr-builddeb/',
1192
'.bzr-builddeb/default.conf'])
1194
def import_debian(self, debian_part, version, parents, md5,
1195
native=False, timestamp=None):
1196
"""Import the debian part of a source package.
1198
:param debian_part: the path of a directory containing the unpacked
1200
:param version: the Version of the source package.
1201
:param parents: a list of revision ids that should be the
1202
parents of the imported revision.
1203
:param md5: the md5 sum reported by the .dsc for
1204
the .diff.gz part of this source package.
1205
:param native: whether the package is native.
1206
:param timestamp: a tuple of (timestamp, timezone) to use for
1207
the commit, or None to use the current values.
1209
mutter("Importing debian part for version %s from %s, with parents "
1210
"%s" % (str(version), debian_part, str(parents)))
1211
assert self.tree is not None, "Can't import with no tree"
1212
# First we move the branch to the first parent
1214
if self.branch.last_revision() == NULL_REVISION:
1215
parent_revid = parents[0]
1216
self.tree.pull(self.tree.branch, overwrite=True,
1217
stop_revision=parent_revid)
1218
elif parents[0] != self.branch.last_revision():
1219
mutter("Adding current tip as parent: %s"
1220
% self.branch.last_revision())
1221
parents.insert(0, self.branch.last_revision())
1222
elif self.branch.last_revision() != NULL_REVISION:
1223
# We were told to import with no parents. That's not
1224
# right, so import with the current parent. Should
1225
# perhaps be fixed in the methods to determine the parents.
1226
mutter("Told to import with no parents. Adding current tip "
1227
"as the single parent")
1228
parents = [self.branch.last_revision()]
1229
other_branches = self.get_other_branches()
1230
def get_last_revision_tree(br):
1231
return br.repository.revision_tree(br.last_revision())
1232
debian_trees = [get_last_revision_tree(o.branch)
1233
for o in other_branches]
1235
for parent in parents:
1236
parent_trees.append(self.branch.repository.revision_tree(
1238
import_dir(self.tree, debian_part,
1239
file_ids_from=parent_trees + debian_trees)
1240
rules_path = os.path.join(self.tree.basedir, 'debian', 'rules')
1241
if os.path.isfile(rules_path):
1242
os.chmod(rules_path,
1243
(stat.S_IRWXU|stat.S_IRGRP|stat.S_IXGRP|
1244
stat.S_IROTH|stat.S_IXOTH))
1245
self.tree.set_parent_ids(parents)
1246
changelog_path = os.path.join(self.tree.basedir, 'debian',
1248
if os.path.exists(changelog_path):
1249
f = open(changelog_path)
1251
changelog_contents = f.read()
1254
changelog = Changelog(file=changelog_contents, max_blocks=1)
1255
message, authors, thanks, bugs = \
1256
get_commit_info_from_changelog(changelog, self.branch)
1258
message = 'Import packaging changes for version %s' % \
1260
revprops={"deb-md5":md5}
1262
revprops['deb-native'] = "True"
1264
revprops['authors'] = "\n".join(authors)
1266
revprops['deb-thanks'] = "\n".join(thanks)
1268
revprops['bugs'] = "\n".join(bugs)
1270
if timestamp is not None:
1271
timezone = timestamp[1]
1272
timestamp = timestamp[0]
1273
self._mark_native_config(native)
1274
self.tree.commit(message, revprops=revprops, timestamp=timestamp,
1276
self.tag_version(version)
1278
def _get_dsc_part(self, dsc, end):
1279
"""Get the path and md5 of a file ending with end in dsc."""
1280
files = dsc['files']
1281
for file_info in files:
1282
name = file_info['name']
1283
if name.endswith(end):
1285
md5 = file_info['md5sum']
1286
return (filename, md5)
1289
def get_upstream_part(self, dsc):
1290
"""Gets the information about the upstream part from the dsc.
1292
:param dsc: a deb822.Dsc object to take the information from.
1293
:return: a tuple (path, md5), both strings, the former being
1294
the path to the .orig.tar.gz, the latter being the md5
1295
reported for it. If there is no upstream part both will
1298
return self._get_dsc_part(dsc, ".orig.tar.gz")
1300
def get_diff_part(self, dsc):
1301
"""Gets the information about the diff part from the dsc.
1303
:param dsc: a deb822.Dsc object to take the information from.
1304
:return: a tuple (path, md5), both strings, the former being
1305
the path to the .diff.gz, the latter being the md5
1306
reported for it. If there is no diff part both will be
1309
return self._get_dsc_part(dsc, ".diff.gz")
1311
def get_native_part(self, dsc):
1312
"""Gets the information about the native part from the dsc.
1314
:param dsc: a deb822.Dsc object to take the information from.
1315
:return: a tuple (path, md5), both strings, the former being
1316
the path to the .tar.gz, the latter being the md5 reported
1317
for it. If there is not native part both will be None.
1319
(path, md5) = self._get_dsc_part(dsc, ".tar.gz")
1320
assert not path.endswith(".orig.tar.gz")
1323
def upstream_parents(self, versions, version):
1324
"""Get the parents for importing a new upstream.
1326
The upstream parents will be the last upstream version,
1327
except for some cases when the last version was native.
1329
:return: the list of revision ids to use as parents when
1330
importing the specified upstream version.
1333
first_parent = self.upstream_branch.last_revision()
1334
if first_parent != NULL_REVISION:
1335
parents = [first_parent]
1336
last_contained_version = self.last_contained_version(versions)
1337
if last_contained_version is not None:
1338
# If the last version was native, and was not from the same
1339
# upstream as a non-native version (i.e. it wasn't a mistaken
1340
# native -2 version), then we want to add an extra parent.
1341
if (self.is_version_native(last_contained_version)
1342
and not self.has_upstream_version(last_contained_version.upstream_version)):
1343
revid = self.revid_of_version(last_contained_version)
1344
parents.append(revid)
1345
self.upstream_branch.fetch(self.branch,
1346
last_revision=revid)
1347
pull_parents = self.get_parents(versions)
1348
if ((first_parent == NULL_REVISION and len(pull_parents) > 0)
1349
or len(pull_parents) > 1):
1350
if first_parent == NULL_REVISION:
1351
pull_branch = pull_parents[0][0]
1352
pull_version = pull_parents[0][1]
1354
pull_branch = pull_parents[1][0]
1355
pull_version = pull_parents[1][1]
1356
if not pull_branch.is_version_native(pull_version):
1358
pull_branch.revid_of_upstream_version(pull_version.upstream_version)
1359
mutter("Initialising upstream from %s, version %s" \
1360
% (str(pull_branch), str(pull_version)))
1361
parents.append(pull_revid)
1362
self.upstream_branch.fetch(
1363
pull_branch.upstream_branch,
1364
last_revision=pull_revid)
1365
pull_branch.upstream_branch.tags.merge_to(
1366
self.upstream_branch.tags)
1369
def get_changelog_from_source(self, dir):
1370
cl_filename = os.path.join(dir, "debian", "changelog")
1372
cl.parse_changelog(open(cl_filename).read(), strict=False)
1375
def extract_dsc(self, dsc_filename):
1376
"""Extract a dsc file in to a temporary directory."""
1377
tempdir = tempfile.mkdtemp()
1378
dsc_filename = os.path.abspath(dsc_filename)
1379
proc = Popen("dpkg-source -su -x %s" % (dsc_filename,), shell=True,
1380
cwd=tempdir, stdout=PIPE, stderr=PIPE,
1381
preexec_fn=subprocess_setup)
1382
(stdout, stderr) = proc.communicate()
1383
assert proc.returncode == 0, "dpkg-source -x failed, output:\n%s\n%s" % \
1387
def _do_import_package(self, version, versions, debian_part, md5,
1388
upstream_part, upstream_md5, upstream_tarball=None,
1389
timestamp=None, author=None):
1390
pull_branch = self.branch_to_pull_version_from(version, md5)
1391
if pull_branch is not None:
1392
if (self.branch_to_pull_upstream_from(version.upstream_version,
1396
if pull_branch is not None:
1397
self.pull_version_from_branch(pull_branch, version)
1399
# We need to import at least the diff, possibly upstream.
1400
# Work out if we need the upstream part first.
1401
imported_upstream = False
1402
if not self.has_upstream_version(version.upstream_version):
1404
self.branch_to_pull_upstream_from(version.upstream_version,
1406
if up_pull_branch is not None:
1407
self.pull_upstream_from_branch(up_pull_branch,
1408
version.upstream_version)
1410
imported_upstream = True
1411
# Check whether we should pull first if this initialises
1412
# from another branch:
1413
upstream_parents = self.upstream_parents(versions,
1414
version.upstream_version)
1415
new_revid = self.import_upstream(upstream_part,
1416
version.upstream_version,
1417
upstream_md5, upstream_parents,
1418
upstream_tarball=upstream_tarball,
1419
timestamp=timestamp, author=author)
1420
self._fetch_upstream_to_branch(new_revid)
1422
mutter("We already have the needed upstream part")
1423
parents = self.get_parents_with_upstream(version, versions,
1424
force_upstream_parent=imported_upstream)
1425
# Now we have the list of parents we need to import the .diff.gz
1426
self.import_debian(debian_part, version, parents, md5,
1427
timestamp=timestamp)
1429
def get_native_parents(self, version, versions):
1430
last_contained_version = self.last_contained_version(versions)
1431
if last_contained_version is None:
1434
parents = [self.revid_of_version(last_contained_version)]
1435
missing_versions = self.missing_versions(versions)
1436
for branch in reversed(self.get_lesser_branches()):
1437
merged, missing_versions = \
1438
branch.contained_versions(missing_versions)
1440
revid = branch.revid_of_version(merged[0])
1441
parents.append(revid)
1442
#FIXME: should this really be here?
1443
branch.branch.tags.merge_to(self.branch.tags)
1444
self.branch.fetch(branch.branch,
1445
last_revision=revid)
1446
if self.upstream_branch.last_revision() == NULL_REVISION:
1447
self.upstream_tree.pull(branch.upstream_branch)
1448
branch.upstream_branch.tags.merge_to(self.upstream_branch.tags)
1449
for branch in self.get_greater_branches():
1450
merged, missing_versions = \
1451
branch.contained_versions(missing_versions)
1453
revid = branch.revid_of_version(merged[0])
1454
parents.append(revid)
1455
#FIXME: should this really be here?
1456
branch.branch.tags.merge_to(self.branch.tags)
1457
self.branch.fetch(branch.branch,
1458
last_revision=revid)
1459
if self.upstream_branch.last_revision() == NULL_REVISION:
1460
self.upstream_tree.pull(branch.upstream_branch)
1461
branch.upstream_branch.tags.merge_to(self.upstream_branch.tags)
1462
if (self.branch.last_revision() != NULL_REVISION
1463
and not self.branch.last_revision() in parents):
1464
parents.insert(0, self.branch.last_revision())
1468
def _import_native_package(self, version, versions, debian_part, md5,
1470
pull_branch = self.branch_to_pull_version_from(version, md5)
1471
if pull_branch is not None:
1472
self.pull_version_from_branch(pull_branch, version, native=True)
1474
parents = self.get_native_parents(version, versions)
1475
self.import_debian(debian_part, version, parents, md5,
1476
native=True, timestamp=timestamp)
1478
def _get_safe_versions_from_changelog(self, cl):
1480
for block in cl._blocks:
1482
versions.append(block.version)
1483
except VersionError:
1487
def import_package(self, dsc_filename, use_time_from_changelog=True):
1488
"""Import a source package.
1490
:param dsc_filename: a path to a .dsc file for the version
1492
:param use_time_from_changelog: whether to use the current time or
1493
the one from the last changelog entry.
1495
base_path = osutils.dirname(dsc_filename)
1496
dsc = deb822.Dsc(open(dsc_filename).read())
1497
version = Version(dsc['Version'])
1498
name = dsc['Source']
1499
upstream_tarball = None
1500
for part in dsc['files']:
1501
if part['name'].endswith(".orig.tar.gz"):
1502
assert upstream_tarball is None, "Two .orig.tar.gz?"
1503
upstream_tarball = os.path.abspath(
1504
os.path.join(base_path, part['name']))
1505
tempdir = self.extract_dsc(dsc_filename)
1507
# TODO: make more robust against strange .dsc files.
1508
upstream_part = os.path.join(tempdir,
1509
"%s-%s.orig" % (name, str(version.upstream_version)))
1510
debian_part = os.path.join(tempdir,
1511
"%s-%s" % (name, str(version.upstream_version)))
1513
if not os.path.exists(upstream_part):
1514
mutter("It's a native package")
1516
(_, md5) = self.get_native_part(dsc)
1518
(_, upstream_md5) = self.get_upstream_part(dsc)
1519
(_, md5) = self.get_diff_part(dsc)
1520
cl = self.get_changelog_from_source(debian_part)
1523
if use_time_from_changelog and len(cl._blocks) > 0:
1524
raw_timestamp = cl.date
1526
time_tuple = rfc822.parsedate_tz(raw_timestamp)
1527
if time_tuple is not None:
1528
timestamp = (time.mktime(time_tuple[:9]), time_tuple[9])
1529
author = cl.author.decode("utf-8")
1530
versions = self._get_safe_versions_from_changelog(cl)
1531
assert not self.has_version(version), \
1532
"Trying to import version %s again" % str(version)
1533
#TODO: check that the versions list is correctly ordered,
1534
# as some methods assume that, and it's not clear what
1535
# should happen if it isn't.
1537
self._do_import_package(version, versions, debian_part, md5,
1538
upstream_part, upstream_md5,
1539
upstream_tarball=upstream_tarball,
1540
timestamp=timestamp, author=author)
1542
self._import_native_package(version, versions, debian_part,
1543
md5, timestamp=timestamp)
1545
shutil.rmtree(tempdir)
1547
def extract_upstream_tree(self, upstream_tip, basedir):
1548
# Extract that to a tempdir so we can get a working
1550
# TODO: should stack rather than trying to use the repository,
1551
# as that will be more efficient.
1552
# TODO: remove the _extract_upstream_tree alias below.
1553
to_location = os.path.join(basedir, "upstream")
1554
dir_to = self.branch.bzrdir.sprout(to_location,
1555
revision_id=upstream_tip,
1556
accelerator_tree=self.tree)
1558
self.upstream_tree = dir_to.open_workingtree()
1559
except NoWorkingTree:
1560
# Handle shared treeless repo's.
1561
self.upstream_tree = dir_to.create_workingtree()
1562
self.upstream_branch = self.upstream_tree.branch
1564
_extract_upstream_tree = extract_upstream_tree
1566
def _create_empty_upstream_tree(self, basedir):
1567
to_location = os.path.join(basedir, "upstream")
1568
to_transport = get_transport(to_location)
1569
to_transport.ensure_base()
1570
format = bzrdir.format_registry.make_bzrdir('default')
1572
existing_bzrdir = bzrdir.BzrDir.open_from_transport(
1574
except NotBranchError:
1575
# really a NotBzrDir error...
1576
create_branch = bzrdir.BzrDir.create_branch_convenience
1577
branch = create_branch(to_transport.base,
1579
possible_transports=[to_transport])
1581
if existing_bzrdir.has_branch():
1582
raise AlreadyBranchError(to_location)
1584
branch = existing_bzrdir.create_branch()
1585
existing_bzrdir.create_workingtree()
1586
self.upstream_branch = branch
1587
self.upstream_tree = branch.bzrdir.open_workingtree()
1588
self.upstream_tree.set_root_id(self.tree.path2id(""))
1590
def _extract_tarball_to_tempdir(self, tarball_filename):
1591
tempdir = tempfile.mkdtemp()
1593
proc = Popen(["/usr/bin/tar", "xzf", tarball_filename, "-C",
1594
tempdir, "--strip-components", "1"],
1595
preexec_fn=subprocess_setup)
1597
if proc.returncode != 0:
1598
raise TarFailed("extract", tarball_filename)
1601
shutil.rmtree(tempdir)
1604
def _revid_of_upstream_version_from_branch(self, version):
1605
"""The private method below will go away eventually."""
1606
return self.revid_of_upstream_version_from_branch(version)
1608
def revid_of_upstream_version_from_branch(self, version):
1609
# TODO: remove the _revid_of_upstream_version_from_branch alias below.
1610
assert isinstance(version, str)
1611
tag_name = self.upstream_tag_name(version)
1612
if self._has_version(self.branch, tag_name):
1613
return self.branch.tags.lookup_tag(tag_name)
1614
tag_name = self.upstream_tag_name(version, distro="debian")
1615
if self._has_version(self.branch, tag_name):
1616
return self.branch.tags.lookup_tag(tag_name)
1617
tag_name = self.upstream_tag_name(version, distro="ubuntu")
1618
if self._has_version(self.branch, tag_name):
1619
return self.branch.tags.lookup_tag(tag_name)
1620
tag_name = self.upstream_tag_name(version)
1621
return self.branch.tags.lookup_tag(tag_name)
1623
_revid_of_upstream_version_from_branch = revid_of_upstream_version_from_branch
1625
def merge_upstream(self, tarball_filename, version, previous_version,
1626
upstream_branch=None, upstream_revision=None, merge_type=None):
1627
assert self.upstream_branch is None, \
1628
"Should use self.upstream_branch if set"
1629
tempdir = tempfile.mkdtemp(dir=os.path.join(self.tree.basedir, '..'))
1631
if previous_version is not None:
1632
previous_upstream_revision = get_snapshot_revision(previous_version.upstream_version)
1633
if self.has_upstream_version_in_packaging_branch(
1634
previous_version.upstream_version):
1635
upstream_tip = self.revid_of_upstream_version_from_branch(
1636
previous_version.upstream_version)
1637
self.extract_upstream_tree(upstream_tip, tempdir)
1638
elif (upstream_branch is not None and
1639
previous_upstream_revision is not None):
1640
upstream_tip = RevisionSpec.from_string(previous_upstream_revision).as_revision_id(upstream_branch)
1641
assert isinstance(upstream_tip, str)
1642
self.extract_upstream_tree(upstream_tip, tempdir)
1644
raise BzrCommandError("Unable to find the tag for the "
1645
"previous upstream version, %s, in the branch: "
1647
previous_version.upstream_version,
1648
self.upstream_tag_name(
1649
previous_version.upstream_version)))
1651
self._create_empty_upstream_tree(tempdir)
1652
if self.has_upstream_version_in_packaging_branch(version.upstream_version):
1653
raise UpstreamAlreadyImported(version)
1655
if upstream_branch is not None:
1656
upstream_branch.lock_read()
1657
if upstream_revision is not None:
1658
upstream_revision = upstream_branch.last_revision()
1659
graph = self.branch.repository.get_graph(
1660
other_repository=upstream_branch.repository)
1661
if graph.is_ancestor(upstream_revision,
1662
self.branch.last_revision()):
1663
raise UpstreamBranchAlreadyMerged
1664
tarball_filename = os.path.abspath(tarball_filename)
1666
m.update(open(tarball_filename).read())
1667
md5sum = m.hexdigest()
1668
tarball_dir = self._extract_tarball_to_tempdir(tarball_filename)
1670
# FIXME: should use upstream_parents()?
1672
if self.upstream_branch.last_revision() != NULL_REVISION:
1673
parents = [self.upstream_branch.last_revision()]
1674
new_revid = self.import_upstream(tarball_dir,
1675
version.upstream_version,
1676
md5sum, parents, upstream_tarball=tarball_filename,
1677
upstream_branch=upstream_branch,
1678
upstream_revision=upstream_revision)
1679
self._fetch_upstream_to_branch(new_revid)
1681
shutil.rmtree(tarball_dir)
1682
if self.branch.last_revision() != NULL_REVISION:
1683
conflicts = self.tree.merge_from_branch(
1684
self.upstream_branch, merge_type=merge_type)
1686
# Pull so that merge-upstream allows you to start a branch
1687
# from upstream tarball.
1689
self.tree.pull(self.upstream_branch)
1690
self.upstream_branch.tags.merge_to(self.branch.tags)
1693
if upstream_branch is not None:
1694
upstream_branch.unlock()
1696
shutil.rmtree(tempdir)
1698
def has_pristine_tar_delta(self, revid):
1699
rev = self.branch.repository.get_revision(revid)
1700
return 'deb-pristine-delta' in rev.properties
1702
def pristine_tar_delta(self, revid):
1703
rev = self.branch.repository.get_revision(revid)
1704
uuencoded = rev.properties['deb-pristine-delta']
1705
delta = standard_b64decode(uuencoded)
1708
def reconstruct_pristine_tar(self, revid, package, version,
1710
"""Reconstruct a pristine-tar tarball from a bzr revision."""
1711
if not os.path.exists("/usr/bin/pristine-tar"):
1712
raise PristineTarError("/usr/bin/pristine-tar is not available")
1713
tree = self.branch.repository.revision_tree(revid)
1714
tmpdir = tempfile.mkdtemp(prefix="builddeb-pristine-")
1716
dest = os.path.join(tmpdir, "orig")
1717
export(tree, dest, format='dir')
1718
delta = self.pristine_tar_delta(revid)
1719
command = ["/usr/bin/pristine-tar", "gentar", "-",
1720
os.path.abspath(dest_filename)]
1721
proc = Popen(command, stdin=PIPE, cwd=dest,
1722
preexec_fn=subprocess_setup)
1723
(stdout, stderr) = proc.communicate(delta)
1724
if proc.returncode != 0:
1725
raise PristineTarError("Generating tar from delta failed: %s" % stderr)
1727
shutil.rmtree(tmpdir)
1729
def make_pristine_tar_delta(self, tree, tarball_path):
1730
if not os.path.exists("/usr/bin/pristine-tar"):
1731
raise PristineTarError("/usr/bin/pristine-tar is not available")
1732
tmpdir = tempfile.mkdtemp(prefix="builddeb-pristine-")
1734
dest = os.path.join(tmpdir, "orig")
1737
for (dp, ie) in tree.inventory.iter_entries():
1738
ie._read_tree_state(dp, tree)
1739
export(tree, dest, format='dir')
1742
command = ["/usr/bin/pristine-tar", "gendelta", tarball_path, "-"]
1743
note(" ".join(command))
1744
proc = Popen(command, stdout=PIPE, cwd=dest,
1745
preexec_fn=subprocess_setup)
1746
(stdout, stderr) = proc.communicate()
1747
if proc.returncode != 0:
1748
raise PristineTarError("Generating delta from tar failed: %s" % stderr)
1751
shutil.rmtree(tmpdir)