208
246
def get_last_revision(branch):
209
last_patch = branch.last_patch()
247
last_patch = branch.last_revision()
211
249
return arch_revision(last_patch)
212
250
except NotArchRevision:
214
252
"Directory \"%s\" already exists, and the last revision is not"
215
" an Arch revision (%s)" % (output_dir, last_patch))
218
def get_remaining_revisions(output_dir, version):
253
" an Arch revision (%s)" % (branch.base, last_patch))
255
def do_branch(br_from, to_location, revision_id):
256
"""Derived from branch in builtins."""
260
os.mkdir(to_location)
262
if e.errno == errno.EEXIST:
263
raise UserError('Target directory "%s" already'
264
' exists.' % to_location)
265
if e.errno == errno.ENOENT:
266
raise UserError('Parent of "%s" does not exist.' %
271
br_from.bzrdir.clone(to_location, revision_id)
272
except NoSuchRevision:
274
msg = "The branch %s has no revision %s." % (from_location,
280
def get_remaining_revisions(output_dir, version, reuse_history_from=[]):
219
281
last_patch = None
221
if os.path.exists(output_dir):
283
output_exists = os.path.exists(output_dir)
222
285
# We are starting from an existing directory, figure out what
223
286
# the current version is
224
branch = find_branch(output_dir)
287
branch = Branch.open(output_dir)
225
288
last_patch = get_last_revision(branch)
289
if last_patch is None:
290
if branch.last_revision() != None:
291
raise NotPreviousImport(branch.base)
292
elif version is None:
227
293
version = last_patch.version
228
294
elif version is None:
229
295
raise UserError("No version specified, and directory does not exist.")
232
298
ancestors = version_ancestry(version)
299
if not output_exists and reuse_history_from != []:
300
for ancestor in reversed(ancestors):
301
if last_patch is not None:
302
# found something to copy
304
# try to grab a copy of ancestor
305
# note that is not optimised: we could look for namespace
306
# transitions and only look for the past after the
308
for history_root in reuse_history_from:
309
possible_source = os.path.join(history_root,
310
map_namespace(ancestor.version))
312
source = Branch.open(possible_source)
313
rev_id = revision_id(ancestor)
314
if rev_id in source.revision_history():
315
do_branch(source, output_dir, rev_id)
316
last_patch = ancestor
318
except NotBranchError:
233
320
except NoSuchVersion, e:
321
raise UserError(str(e))
237
324
for i in range(len(ancestors)):
244
331
# Strip off all of the ancestors which are already present
245
332
# And get a directory starting with the latest ancestor
246
333
latest_ancestor = ancestors[i]
247
old_revno = find_branch(output_dir).revno()
334
old_revno = Branch.open(output_dir).revno()
248
335
ancestors = ancestors[i+1:]
249
336
return ancestors, old_revno
251
def import_version(output_dir, version, fancy=True, fast=False, verbose=False,
252
dry_run=False, max_count=None, skip_symlinks=False):
339
###class Importer(object):
342
### Currently this is used as a parameter object, though more behaviour is
346
### def __init__(self, output_dir, version, fast=False,
347
### verbose=False, dry_run=False, max_count=None,
348
### reuse_history_from=[]):
349
### self.output_dir = output_dir
350
### self.version = version
354
def import_version(output_dir, version, fast=False,
355
verbose=False, dry_run=False, max_count=None,
356
reuse_history_from=[], standalone=True):
254
358
>>> q = test_environ()
360
Progress bars output to stderr, but doctest does not capture that.
362
>>> old_stderr = sys.stderr
363
>>> sys.stderr = sys.stdout
255
365
>>> result_path = os.path.join(q, "result")
256
366
>>> commit_test_revisions()
257
367
>>> version = pybaz.Version("test@example.com/test--test--0.1")
258
>>> import_version('/', version, fancy=False, dry_run=True)
368
>>> old_ui = bzrlib.ui.ui_factory
369
>>> bzrlib.ui.ui_factory = bzrlib.ui.text.TextUIFactory(
370
... bar_type=bzrlib.progress.DotsProgressBar)
372
>>> import_version('/', version, dry_run=True)
259
373
Traceback (most recent call last):
260
UserError: / exists, but is not a bzr branch.
261
>>> import_version(result_path, version, fancy=False, dry_run=True)
374
NotPreviousImport: / is not the location of a previous import.
375
>>> import_version(result_path, version, dry_run=True)
262
376
Traceback (most recent call last):
263
377
UserError: The version test@example.com/test--test--0.1 does not exist.
264
378
>>> version = pybaz.Version("test@example.com/test--test--0")
265
>>> import_version(result_path, version, fancy=False, dry_run=True)
379
>>> import_version(result_path, version, dry_run=True) #doctest: +ELLIPSIS
380
importing test@example.com/test--test--0 into ...
382
revisions: ..........................................
268
383
Dry run, not modifying output_dir
270
>>> import_version(result_path, version, fancy=False)
385
>>> import_version(result_path, version) #doctest: +ELLIPSIS
386
importing test@example.com/test--test--0 into ...
388
revisions: .....................................................................
275
>>> import_version(result_path, version, fancy=False)
391
>>> import_version(result_path, version) #doctest: +ELLIPSIS
276
392
Tree is up-to-date with test@example.com/test--test--0--patch-2
277
393
>>> commit_more_test_revisions()
278
>>> import_version(result_path, version, fancy=False)
394
>>> import_version(result_path, version) #doctest: +ELLIPSIS
395
importing test@example.com/test--test--0 into ...
396
revisions: ....................................................
399
>>> bzrlib.ui.ui_factory = old_ui
400
>>> sys.stderr = old_stderr
283
401
>>> teardown_environ(q)
403
progress_bar = bzrlib.ui.ui_factory.nested_progress_bar()
286
ancestors, old_revno = get_remaining_revisions(output_dir, version)
287
except NotInABranch, e:
288
raise UserError("%s exists, but is not a bzr branch." % e.path)
289
if len(ancestors) == 0:
290
last_revision = get_last_revision(find_branch(output_dir))
291
print 'Tree is up-to-date with %s' % last_revision
406
ancestors, old_revno = get_remaining_revisions(output_dir, version,
408
except NotBranchError, e:
409
raise NotPreviousImport(e.path)
410
if old_revno is None and len(ancestors) == 0:
411
progress_bar.note('Version %s has no revisions.' % version)
413
if len(ancestors) == 0:
414
last_revision = get_last_revision(Branch.open(output_dir))
415
progress_bar.note('Tree is up-to-date with %s' % last_revision)
294
progress_bar = ProgressBar()
295
tempdir = tempfile.mkdtemp(prefix="baz2bzr-",
296
dir=os.path.dirname(output_dir))
418
progress_bar.note("importing %s into %s" % (version, output_dir))
420
tempdir = tempfile.mkdtemp(prefix="baz2bzr-",
421
dir=os.path.dirname(output_dir))
423
wt = WorkingTree.open(output_dir)
424
except (NotBranchError, NoWorkingTree):
301
427
for result in iter_import_version(output_dir, ancestors, tempdir,
302
fast=fast, verbose=verbose, dry_run=dry_run,
303
max_count=max_count, skip_symlinks=skip_symlinks):
305
show_progress(progress_bar, result)
307
sys.stdout.write('.')
429
fast=fast, verbose=verbose, dry_run=dry_run,
430
max_count=max_count, standalone=standalone):
431
show_progress(progress_bar, result)
433
progress_bar.note('Dry run, not modifying output_dir')
436
# Update the working tree of the branch
438
wt = WorkingTree.open(output_dir)
439
except NoWorkingTree:
442
wt.set_last_revision(wt.branch.last_revision())
312
sys.stdout.write('\n')
315
print 'Dry run, not modifying output_dir'
317
if os.path.exists(output_dir):
318
# Move the bzr control directory back, and update the working tree
319
tmp_bzr_dir = os.path.join(tempdir, '.bzr')
321
bzr_dir = os.path.join(output_dir, '.bzr')
322
new_bzr_dir = os.path.join(tempdir, "rd", '.bzr')
324
os.rename(bzr_dir, tmp_bzr_dir) # Move the original bzr out of the way
325
os.rename(new_bzr_dir, bzr_dir)
327
bzrlib.merge.merge((output_dir, -1), (output_dir, old_revno),
328
check_clean=False, this_dir=output_dir,
331
# If something failed, move back the original bzr directory
332
os.rename(bzr_dir, new_bzr_dir)
333
os.rename(tmp_bzr_dir, bzr_dir)
336
revdir = os.path.join(tempdir, "rd")
337
os.rename(revdir, output_dir)
447
progress_bar.note('Cleaning up')
448
shutil.rmtree(tempdir)
449
progress_bar.note("Import complete.")
341
shutil.rmtree(tempdir)
342
print "Import complete."
451
progress_bar.finished()
344
class UserError(Exception):
453
class UserError(BzrCommandError):
345
454
def __init__(self, message):
346
455
"""Exception to throw when a user makes an impossible request
347
456
:param message: The message to emit when printing this exception
348
457
:type message: string
350
Exception.__init__(self, message)
459
BzrCommandError.__init__(self, message)
461
class NotPreviousImport(UserError):
462
def __init__(self, path):
463
UserError.__init__(self, "%s is not the location of a previous import."
352
467
def revision_id(arch_revision):
371
486
def arch_revision(revision_id):
373
>>> str(arch_revision("Arch-x:jrandom@example.com%test--test--0"))
374
Traceback (most recent call last):
375
NotArchRevision: The revision id Arch-x:jrandom@example.com%test--test--0 does not look like it came from Arch.
376
>>> str(arch_revision("Arch-x:jrandom@example.com%test--test--0--base-5"))
377
Traceback (most recent call last):
378
NotArchRevision: The revision id Arch-x:jrandom@example.com%test--test--0--base-5 does not look like it came from Arch.
379
>>> str(arch_revision("Arch-x:jrandom@example.com%test--test--0--patch-5"))
488
>>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0"))
489
Traceback (most recent call last):
490
NotArchRevision: The revision id Arch-1:jrandom@example.com%test--test--0 does not look like it came from Arch.
491
>>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--base-5"))
492
Traceback (most recent call last):
493
NotArchRevision: The revision id Arch-1:jrandom@example.com%test--test--0--base-5 does not look like it came from Arch.
494
>>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--patch-5"))
380
495
'jrandom@example.com/test--test--0--patch-5'
382
497
if revision_id is None:
384
if revision_id[:7] != 'Arch-x:':
499
if revision_id[:7] != 'Arch-1:':
385
500
raise NotArchRevision(revision_id)
388
503
return pybaz.Revision(revision_id[7:].replace('%', '/'))
389
504
except pybaz.errors.NamespaceError, e:
390
505
raise NotArchRevision(revision_id)
392
def iter_import_version(output_dir, ancestors, tempdir, fast=False,
508
def create_shared_repository(output_dir):
509
bd = bzrdir.BzrDirMetaFormat1().initialize(output_dir)
510
bd.create_repository(shared=True)
512
def create_branch(output_dir):
514
bd = bzrdir.BzrDirMetaFormat1().initialize(output_dir)
515
return bd.create_branch()
518
def create_checkout(source, to_location, revision_id=None):
519
checkout = bzrdir.BzrDirMetaFormat1().initialize(to_location)
520
bzrlib.branch.BranchReferenceFormat().initialize(checkout, source)
521
return checkout.create_workingtree(revision_id)
524
def create_checkout_metadata(source, to_location, revision_id=None):
525
if revision_id is None:
526
revision_id = source.last_revision()
527
wt = create_checkout(source, to_location, NULL_REVISION)
528
wt.set_last_revision(revision_id)
529
wt._write_inventory(wt.basis_tree().inventory)
533
def iter_import_version(output_dir, ancestors, tempdir, pb, fast=False,
393
534
verbose=False, dry_run=False, max_count=None,
394
skip_symlinks=False):
397
538
# Uncomment this for testing, it basically just has baz2bzr only update
409
550
# print '\t%s' % a
411
552
previous_version=None
553
missing_ancestor = None
555
dry_output_dir = os.path.join(tempdir, 'od')
556
if os.path.exists(output_dir):
557
shutil.copytree(output_dir, dry_output_dir)
558
output_dir = dry_output_dir
560
if os.path.exists(output_dir):
561
target_branch = Branch.open(output_dir)
564
wt = BzrDir.create_standalone_workingtree(output_dir)
565
target_branch = wt.branch
567
target_branch = create_branch(output_dir)
413
569
for i in range(len(ancestors)):
414
570
revision = ancestors[i]
571
rev_id = revision_id(revision)
416
574
version = str(revision.version)
417
575
if version != previous_version:
419
print '\rOn version: %s' % version
576
pb.note('On version: %s' % version)
420
577
yield Progress(str(revision.patchlevel), i, len(ancestors))
421
578
previous_version = version
423
580
yield Progress("revisions", i, len(ancestors))
582
if target_branch.repository.has_revision(rev_id):
583
target_branch.append_revision(rev_id)
424
585
if revdir is None:
425
586
revdir = os.path.join(tempdir, "rd")
426
baz_inv, log = get_revision(revdir, revision,
427
skip_symlinks=skip_symlinks)
428
if os.path.exists(output_dir):
429
bzr_dir = os.path.join(output_dir, '.bzr')
430
new_bzr_dir = os.path.join(tempdir, "rd", '.bzr')
431
# This would be much faster with a simple os.rename(), but if
432
# we fail, we have corrupted the original .bzr directory. Is
433
# that a big problem, as we can just back out the last
434
# revisions in .bzr/revision_history I don't really know
435
shutil.copytree(bzr_dir, new_bzr_dir)
436
# Now revdir should have a tree with the latest .bzr, and the
437
# next revision of the baz tree
438
branch = find_branch(revdir)
440
branch = Branch(revdir, init=True)
588
tree, baz_inv, log = get_revision(revdir, revision)
589
except pybaz.errors.ExecProblem, e:
590
if ("%s" % e.args).find('could not connect') == -1:
592
missing_ancestor = revision
594
pb.note("unable to access ancestor %s, making into a merge."
597
target_tree = create_checkout_metadata(target_branch, revdir)
598
branch = target_tree.branch
442
600
old = os.path.join(revdir, ".bzr")
443
601
new = os.path.join(tempdir, ".bzr")
444
602
os.rename(old, new)
445
baz_inv, log = apply_revision(revdir, revision,
446
skip_symlinks=skip_symlinks)
603
baz_inv, log = apply_revision(tree, revision)
447
604
os.rename(new, old)
448
branch = find_branch(revdir)
449
timestamp = email.Utils.mktime_tz(log.date + (0,))
450
rev_id = revision_id(revision)
605
target_tree = WorkingTree.open(revdir)
606
branch = target_tree.branch
607
# cached so we can delete the log
609
log_summary = log.summary
610
log_description = log.description
611
is_continuation = log.continuation_of is not None
612
log_creator = log.creator
613
direct_merges = get_direct_merges(revdir, revision, log)
615
timestamp = email.Utils.mktime_tz(log_date + (0,))
616
if log_summary is None:
618
# log_descriptions of None and "" are ignored.
619
if not is_continuation and log_description:
620
log_message = "\n".join((log_summary, log_description))
622
log_message = log_summary
623
target_tree.lock_write()
451
624
branch.lock_write()
453
branch.set_inventory(baz_inv)
454
bzrlib.trace.silent = True
455
branch.commit(log.summary, verbose=False, committer=log.creator,
456
timestamp=timestamp, timezone=0, rev_id=rev_id)
627
# if we want it to be in revision-history, do that here.
628
target_tree.set_parent_ids([revision_id(missing_ancestor)],
629
allow_leftmost_as_ghost=True)
630
missing_ancestor = None
631
for merged_rev in direct_merges:
632
target_tree.add_pending_merge(revision_id(merged_rev))
633
target_tree.set_inventory(baz_inv)
634
commitobj = Commit(reporter=ImportCommitReporter())
635
commitobj.commit(working_tree=target_tree,
636
message=log_message.decode('ascii', 'replace'),
637
verbose=False, committer=log_creator,
638
timestamp=timestamp, timezone=0, rev_id=rev_id,
458
bzrlib.trace.silent = False
460
643
yield Progress("revisions", len(ancestors), len(ancestors))
461
unlink_unversioned(branch, revdir)
463
def unlink_unversioned(branch, revdir):
464
for unversioned in branch.working_tree().extras():
465
path = os.path.join(revdir, unversioned)
645
def get_direct_merges(revdir, revision, log):
646
continuation = log.continuation_of
647
previous_version = revision.version
648
if pybaz.WorkingTree(revdir).tree_version != previous_version:
649
pybaz.WorkingTree(revdir).set_tree_version(previous_version)
650
log_path = "%s/{arch}/%s/%s/%s/%s/patch-log/%s" % (revdir,
651
revision.category.nonarch, revision.branch.nonarch,
652
revision.version.nonarch, revision.archive, revision.patchlevel)
653
temp_path = tempfile.mktemp(dir=os.path.dirname(revdir))
654
os.rename(log_path, temp_path)
655
merges = list(iter_new_merges(revdir, revision.version))
656
direct = direct_merges(merges, [continuation])
657
os.rename(temp_path, log_path)
660
def unlink_unversioned(wt):
661
for unversioned in wt.extras():
662
path = wt.abspath(unversioned)
466
663
if os.path.isdir(path):
467
664
shutil.rmtree(path)
471
668
def get_log(tree, revision):
472
log = tree.iter_logs(version=revision.version, reverse=True).next()
473
assert log.revision == revision
669
log = pybaz.Patchlog(revision, tree=tree)
670
assert str(log.revision) == str(revision), (log.revision, revision)
476
def get_revision(revdir, revision, skip_symlinks=False):
478
tree = pybaz.tree_root(revdir)
673
def get_revision(revdir, revision):
674
tree = revision.get(revdir)
479
675
log = get_log(tree, revision)
481
return bzr_inventory_data(tree, skip_symlinks=skip_symlinks), log
677
return tree, bzr_inventory_data(tree), log
482
678
except BadFileKind, e:
483
raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
486
def apply_revision(revdir, revision, skip_symlinks=False):
487
tree = pybaz.tree_root(revdir)
679
raise UserError("Cannot convert %s because %s is a %s" %
680
(revision,e.path, e.kind))
683
def apply_revision(tree, revision):
488
684
revision.apply(tree)
489
685
log = get_log(tree, revision)
491
return bzr_inventory_data(tree, skip_symlinks=skip_symlinks), log
687
return bzr_inventory_data(tree), log
492
688
except BadFileKind, e:
493
raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
689
raise UserError("Cannot convert %s because %s is a %s" %
690
(revision,e.path, e.kind))
498
693
class BadFileKind(Exception):
726
def baz_import_branch(to_location, from_branch, fast, max_count, verbose,
727
dry_run, reuse_history_list):
728
to_location = os.path.realpath(str(to_location))
729
if from_branch is not None:
731
from_branch = pybaz.Version(from_branch)
732
except pybaz.errors.NamespaceError:
733
print "%s is not a valid Arch branch." % from_branch
735
if reuse_history_list is None:
736
reuse_history_list = []
737
import_version(to_location, from_branch,
739
reuse_history_from=reuse_history_list)
531
742
class NotInABranch(Exception):
532
743
def __init__(self, path):
533
744
Exception.__init__(self, "%s is not in a branch." % path)
537
def find_branch(path):
540
Traceback (most recent call last):
541
NotInABranch: / is not in a branch.
542
>>> sb = bzrlib.ScratchBranch()
543
>>> isinstance(find_branch(sb.base), Branch)
749
def baz_import(to_root_dir, from_archive, verbose=False, reuse_history_list=[],
751
if reuse_history_list is None:
752
reuse_history_list = []
753
to_root = str(os.path.realpath(to_root_dir))
754
if not os.path.exists(to_root):
756
if prefixes is not None:
757
prefixes = prefixes.split(':')
758
import_archive(to_root, from_archive, verbose,
759
reuse_history_list, prefixes=prefixes)
762
def import_archive(to_root, from_archive, verbose,
763
reuse_history_from=[], standalone=False,
765
def selected(version):
769
for prefix in prefixes:
770
if version.nonarch.startswith(prefix):
773
real_to = os.path.realpath(to_root)
774
history_locations = [real_to] + reuse_history_from
775
if standalone is False:
777
bd = BzrDir.open(to_root)
779
except NotBranchError:
780
create_shared_repository(to_root)
781
except NoRepositoryPresent:
782
raise BzrCommandError("Can't create repository at existing branch.")
783
versions = list(pybaz.Archive(str(from_archive)).iter_versions())
784
progress_bar = bzrlib.ui.ui_factory.nested_progress_bar()
549
if e.args[0].endswith("' is not in a branch"):
550
raise NotInABranch(path)
552
class cmd_baz_import(Command):
553
"""Import an Arch or Baz branch into a bzr branch"""
554
takes_args = ['to_location', 'from_branch?']
555
takes_options = ['verbose']
557
def run(self, to_location, from_branch=None, skip_symlinks=False,
558
fast=False, max_count=None, verbose=False, dry_run=False):
559
to_location = os.path.realpath(str(to_location))
560
if from_branch is not None:
786
for num, version in enumerate(versions):
787
progress_bar.update("Branch", num, len(versions))
788
if not selected(version):
789
print "Skipping %s" % version
791
target = os.path.join(to_root, map_namespace(version))
792
if not os.path.exists(os.path.dirname(target)):
793
os.makedirs(os.path.dirname(target))
562
from_branch = pybaz.Version(from_branch)
563
except pybaz.errors.NamespaceError:
564
print "%s is not a valid Arch branch." % from_branch
566
import_version(to_location, from_branch)
795
import_version(target, version,
796
reuse_history_from=reuse_history_from,
797
standalone=standalone)
798
except pybaz.errors.ExecProblem,e:
799
if str(e).find('The requested revision cannot be built.') != -1:
801
"Skipping version %s as it cannot be built due"
802
" to a missing parent archive." % version)
806
if str(e).find('already exists, and the last revision ') != -1:
808
"Skipping version %s as it has had commits made"
809
" since it was converted to bzr." % version)
813
progress_bar.finished()
816
def map_namespace(a_version):
817
a_version = pybaz.Version("%s" % a_version)
818
parser = NameParser(a_version)
819
version = parser.get_version()
820
branch = parser.get_branch()
821
category = parser.get_category()
822
if branch is None or branch == '':
825
return "%s/%s" % (category, branch)
826
return "%s/%s/%s" % (category, version, branch)
829
def map_file_id(file_id):
830
"""Convert a baz file id to a bzr one."""
831
return file_id.replace('%', '%25').replace('/', '%2f')