~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to baz_import.py

Merge from Aaron.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 by Aaron Bentley
 
1
# Copyright (C) 2005 by Aaron Bentley
2
2
 
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
16
16
 
17
17
import errno
18
18
 
19
 
from bzrlib.bzrdir import BzrDir
20
 
import bzrlib.bzrdir as bzrdir
21
 
from bzrlib.errors import (BzrError,
22
 
                           NotBranchError,
23
 
                           NoWorkingTree,
24
 
                           BzrCommandError,
25
 
                           NoSuchRevision,
26
 
                           NoRepositoryPresent,
27
 
                          )
 
19
from bzrlib.errors import BzrError
 
20
from bzrlib.errors import NotBranchError, BzrCommandError, NoSuchRevision
28
21
from bzrlib.branch import Branch
 
22
from bzrlib.clone import copy_branch
29
23
from bzrlib.commit import Commit, NullCommitReporter
30
24
from bzrlib.commands import Command
31
 
from bzrlib.option import _global_option, Option
32
 
from bzrlib.merge import merge_inner
33
 
from bzrlib.revision import NULL_REVISION
34
 
import bzrlib.ui
35
 
import bzrlib.ui.text
 
25
from bzrlib.option import _global_option
36
26
from bzrlib.workingtree import WorkingTree
37
27
from errors import NoPyBaz
38
28
try:
56
46
import email.Utils
57
47
from progress import *
58
48
 
59
 
 
60
 
BAZ_IMPORT_ROOT = 'TREE_ROOT'
61
 
 
62
 
 
63
49
class ImportCommitReporter(NullCommitReporter):
 
50
    def __init__(self, pb):
 
51
        self.pb = pb
64
52
 
65
53
    def escaped(self, escape_count, message):
 
54
        self.pb.clear()
66
55
        bzrlib.trace.warning("replaced %d control characters in message" %
67
56
                             escape_count)
68
57
 
82
71
 
83
72
saved_dir = None
84
73
 
85
 
def make_archive(name, location):
86
 
    pb_location = pybaz.ArchiveLocation(location)
87
 
    pb_location.create_master(pybaz.Archive(name),
88
 
                              pybaz.ArchiveLocationParams())
89
 
 
90
74
def test_environ():
91
75
    """
92
76
    >>> q = test_environ()
104
88
    os.environ["HOME"] = os.path.join(tdir, "home")
105
89
    os.mkdir(os.environ["HOME"])
106
90
    arch_dir = os.path.join(tdir, "archive_dir")
107
 
    make_archive("test@example.com", arch_dir)
 
91
    pybaz.make_archive("test@example.com", arch_dir)
108
92
    work_dir = os.path.join(tdir, "work_dir")
109
93
    os.mkdir(work_dir)
110
94
    os.chdir(work_dir)
272
256
            else:
273
257
                raise
274
258
        try:
275
 
            br_from.bzrdir.clone(to_location, revision_id)
 
259
            copy_branch(br_from, to_location, revision_id, None)
276
260
        except NoSuchRevision:
277
261
            rmtree(to_location)
278
 
            msg = "The branch %s has no revision %s." % (from_location,
279
 
                                                         revision_id)
 
262
            msg = "The branch %s has no revision %s." % (from_location, revision_id)
280
263
            raise UserError(msg)
281
264
    finally:
282
265
        br_from.unlock()
283
266
 
284
 
def get_remaining_revisions(output_dir, version, encoding,
285
 
                            reuse_history_from=[]):
 
267
def get_remaining_revisions(output_dir, version, reuse_history_from=[]):
286
268
    last_patch = None
287
269
    old_revno = None
288
270
    output_exists = os.path.exists(output_dir)
290
272
        # We are starting from an existing directory, figure out what
291
273
        # the current version is
292
274
        branch = Branch.open(output_dir)
293
 
        last_patch, last_encoding = get_last_revision(branch)
294
 
        assert encoding == last_encoding
 
275
        last_patch = get_last_revision(branch)
295
276
        if last_patch is None:
296
 
            if branch.last_revision() != None:
297
 
                raise NotPreviousImport(branch.base)
298
 
        elif version is None:
 
277
            raise NotPreviousImport(branch.base)
 
278
        if version is None:
299
279
            version = last_patch.version
300
280
    elif version is None:
301
281
        raise UserError("No version specified, and directory does not exist.")
309
289
                    break
310
290
                # try to grab a copy of ancestor
311
291
                # note that is not optimised: we could look for namespace
312
 
                # transitions and only look for the past after the
 
292
                # transitions and only look for the past after the 
313
293
                # transition.
314
294
                for history_root in reuse_history_from:
315
295
                    possible_source = os.path.join(history_root,
316
296
                        map_namespace(ancestor.version))
317
297
                    try:
318
298
                        source = Branch.open(possible_source)
319
 
                        rev_id = revision_id(ancestor, encoding)
 
299
                        rev_id = revision_id(ancestor)
320
300
                        if rev_id in source.revision_history():
321
301
                            do_branch(source, output_dir, rev_id)
322
302
                            last_patch = ancestor
332
312
                break
333
313
        else:
334
314
            raise UserError("Directory \"%s\" already exists, and the last "
335
 
                "revision (%s) is not in the ancestry of %s" %
 
315
                "revision (%s) is not in the ancestry of %s" % 
336
316
                (output_dir, last_patch, version))
337
317
        # Strip off all of the ancestors which are already present
338
318
        # And get a directory starting with the latest ancestor
344
324
 
345
325
###class Importer(object):
346
326
###    """An importer.
347
 
###
 
327
###    
348
328
###    Currently this is used as a parameter object, though more behaviour is
349
329
###    possible later.
350
330
###    """
351
331
###
352
 
###    def __init__(self, output_dir, version, fast=False,
353
 
###                 verbose=False, dry_run=False, max_count=None,
 
332
###    def __init__(self, output_dir, version, printer, fancy=True, fast=False,
 
333
###                 verbose=False, dry_run=False, max_count=None, 
354
334
###                   reuse_history_from=[]):
355
335
###        self.output_dir = output_dir
356
336
###        self.version = version
357
337
###        self.
358
338
 
359
339
 
360
 
def import_version(output_dir, version, encoding, fast=False,
 
340
def import_version(output_dir, version, printer, fancy=True, fast=False,
361
341
                   verbose=False, dry_run=False, max_count=None,
362
 
                   reuse_history_from=[], standalone=True):
 
342
                   reuse_history_from=[]):
363
343
    """
364
344
    >>> q = test_environ()
365
 
 
366
 
    Progress bars output to stderr, but doctest does not capture that.
367
 
 
368
 
    >>> old_stderr = sys.stderr
369
 
    >>> sys.stderr = sys.stdout
370
 
 
371
345
    >>> result_path = os.path.join(q, "result")
372
346
    >>> commit_test_revisions()
373
347
    >>> version = pybaz.Version("test@example.com/test--test--0.1")
374
 
    >>> old_ui = bzrlib.ui.ui_factory
375
 
    >>> bzrlib.ui.ui_factory = bzrlib.ui.text.TextUIFactory(
376
 
    ...     bar_type=bzrlib.progress.DotsProgressBar)
377
 
 
378
 
    >>> import_version('/', version, None, dry_run=True)
 
348
    >>> def printer(message): print message
 
349
    >>> import_version('/', version, printer, fancy=False, dry_run=True)
379
350
    Traceback (most recent call last):
380
351
    NotPreviousImport: / is not the location of a previous import.
381
 
    >>> import_version(result_path, version, None, dry_run=True)
 
352
    >>> import_version(result_path, version, printer, fancy=False, dry_run=True)
382
353
    Traceback (most recent call last):
383
354
    UserError: The version test@example.com/test--test--0.1 does not exist.
384
355
    >>> version = pybaz.Version("test@example.com/test--test--0")
385
 
    >>> import_version(result_path, version, None, dry_run=True) #doctest: +ELLIPSIS
386
 
    importing test@example.com/test--test--0 into ...
387
 
    ...
388
 
    revisions: ..........................................
 
356
    >>> import_version(result_path, version, printer, fancy=False, dry_run=True)
 
357
    not fancy
 
358
    ....
389
359
    Dry run, not modifying output_dir
390
360
    Cleaning up
391
 
    >>> import_version(result_path, version, None) #doctest: +ELLIPSIS
392
 
    importing test@example.com/test--test--0 into ...
393
 
    ...
394
 
    revisions: .....................................................................
 
361
    >>> import_version(result_path, version, printer, fancy=False)
 
362
    not fancy
 
363
    ....
395
364
    Cleaning up
396
365
    Import complete.
397
 
    >>> import_version(result_path, version, None) #doctest: +ELLIPSIS
 
366
    >>> import_version(result_path, version, printer, fancy=False)
398
367
    Tree is up-to-date with test@example.com/test--test--0--patch-2
399
368
    >>> commit_more_test_revisions()
400
 
    >>> import_version(result_path, version, None) #doctest: +ELLIPSIS
401
 
    importing test@example.com/test--test--0 into ...
402
 
    revisions: ....................................................
 
369
    >>> import_version(result_path, version, printer, fancy=False)
 
370
    not fancy
 
371
    ..
403
372
    Cleaning up
404
373
    Import complete.
405
 
    >>> bzrlib.ui.ui_factory = old_ui
406
 
    >>> sys.stderr = old_stderr
407
374
    >>> teardown_environ(q)
408
375
    """
409
 
    progress_bar = bzrlib.ui.ui_factory.nested_progress_bar()
410
 
    try:
411
 
        try:
412
 
            ancestors, old_revno = get_remaining_revisions(output_dir, version,
413
 
                                                           encoding,
414
 
                                                           reuse_history_from)
415
 
        except NotBranchError, e:
416
 
            raise NotPreviousImport(e.path)
417
 
        if old_revno is None and len(ancestors) == 0:
418
 
            progress_bar.note('Version %s has no revisions.' % version)
419
 
            return
420
 
        if len(ancestors) == 0:
421
 
            last_revision, last_encoding = \
422
 
                get_last_revision(Branch.open(output_dir))
423
 
            progress_bar.note('Tree is up-to-date with %s' % last_revision)
424
 
            return
425
 
 
426
 
        progress_bar.note("importing %s into %s" % (version, output_dir))
427
 
 
428
 
        tempdir = tempfile.mkdtemp(prefix="baz2bzr-",
429
 
                                   dir=os.path.dirname(output_dir))
430
 
        try:
431
 
            wt = WorkingTree.open(output_dir)
432
 
        except (NotBranchError, NoWorkingTree):
433
 
            wt = None
 
376
    try:
 
377
        ancestors, old_revno = get_remaining_revisions(output_dir, version,
 
378
                                                       reuse_history_from)
 
379
    except NotBranchError, e:
 
380
        raise NotPreviousImport(e.path)
 
381
    if old_revno is None and len(ancestors) == 0:
 
382
        print 'Version %s has no revisions.' % version
 
383
        return
 
384
    if len(ancestors) == 0:
 
385
        last_revision = get_last_revision(Branch.open(output_dir))
 
386
        print 'Tree is up-to-date with %s' % last_revision
 
387
        return
 
388
 
 
389
    progress_bar = ProgressBar()
 
390
    tempdir = tempfile.mkdtemp(prefix="baz2bzr-",
 
391
                               dir=os.path.dirname(output_dir))
 
392
    try:
 
393
        if not fancy:
 
394
            print "not fancy"
434
395
        try:
435
396
            for result in iter_import_version(output_dir, ancestors, tempdir,
436
 
                    pb=progress_bar, encoding=encoding, fast=fast,
437
 
                    verbose=verbose, dry_run=dry_run, max_count=max_count,
438
 
                    standalone=standalone):
439
 
                show_progress(progress_bar, result)
440
 
            if dry_run:
441
 
                progress_bar.note('Dry run, not modifying output_dir')
442
 
                return
443
 
 
444
 
            # Update the working tree of the branch
445
 
            try:
446
 
                wt = WorkingTree.open(output_dir)
447
 
            except NoWorkingTree:
448
 
                wt = None
449
 
            if wt is not None:
450
 
                wt.set_last_revision(wt.branch.last_revision())
451
 
                wt.set_root_id(BAZ_IMPORT_ROOT)
452
 
                wt.revert([])
453
 
 
 
397
                    progress_bar, fast=fast, verbose=verbose, dry_run=dry_run,
 
398
                    max_count=max_count):
 
399
                if fancy:
 
400
                    show_progress(progress_bar, result)
 
401
                else:
 
402
                    sys.stdout.write('.')
454
403
        finally:
455
 
 
456
 
            progress_bar.note('Cleaning up')
457
 
            shutil.rmtree(tempdir)
458
 
        progress_bar.note("Import complete.")
 
404
            if fancy:
 
405
                progress_bar.clear()
 
406
            else:
 
407
                sys.stdout.write('\n')
 
408
 
 
409
        if dry_run:
 
410
            print 'Dry run, not modifying output_dir'
 
411
            return
 
412
        if os.path.exists(output_dir):
 
413
            # Move the bzr control directory back, and update the working tree
 
414
            revdir = os.path.join(tempdir, "rd")
 
415
            if os.path.exists(revdir):
 
416
                # actual imports were done
 
417
                tmp_bzr_dir = os.path.join(tempdir, '.bzr')
 
418
                
 
419
                bzr_dir = os.path.join(output_dir, '.bzr')
 
420
                new_bzr_dir = os.path.join(tempdir, "rd", '.bzr')
 
421
    
 
422
                os.rename(bzr_dir, tmp_bzr_dir) # Move the original bzr out of the way
 
423
                os.rename(new_bzr_dir, bzr_dir)
 
424
                try:
 
425
                    bzrlib.merge.merge((output_dir, -1), (output_dir, None), # old_revno), 
 
426
                                       check_clean=False, this_dir=output_dir, 
 
427
                                       ignore_zero=True)
 
428
                except:
 
429
                    # If something failed, move back the original bzr directory
 
430
                    os.rename(bzr_dir, new_bzr_dir)
 
431
                    os.rename(tmp_bzr_dir, bzr_dir)
 
432
                    raise
 
433
            else:
 
434
                # no imports - perhaps just append_revisions
 
435
                # should not fail:
 
436
                bzrlib.merge.merge((output_dir, -1), (output_dir, None), # old_revno), 
 
437
                                   check_clean=False, this_dir=output_dir, 
 
438
                                   ignore_zero=True)
 
439
        else:
 
440
            revdir = os.path.join(tempdir, "rd")
 
441
            os.rename(revdir, output_dir)
 
442
 
459
443
    finally:
460
 
        progress_bar.finished()
461
 
 
 
444
        printer('Cleaning up')
 
445
        shutil.rmtree(tempdir)
 
446
    printer("Import complete.")
 
447
            
462
448
class UserError(BzrCommandError):
463
449
    def __init__(self, message):
464
450
        """Exception to throw when a user makes an impossible request
473
459
                           % path)
474
460
 
475
461
 
476
 
def revision_id(arch_revision, encoding):
 
462
def revision_id(arch_revision):
477
463
    """
478
464
    Generate a Bzr revision id from an Arch revision id.  'x' in the id
479
465
    designates a revision imported with an experimental algorithm.  A number
481
467
 
482
468
    :param arch_revision: The Arch revision to generate an ID for.
483
469
 
484
 
    >>> revision_id(pybaz.Revision("you@example.com/cat--br--0--base-0"), None)
 
470
    >>> revision_id(pybaz.Revision("you@example.com/cat--br--0--base-0"))
485
471
    'Arch-1:you@example.com%cat--br--0--base-0'
486
 
    >>> revision_id(pybaz.Revision("you@example.com/cat--br--0--base-0"), 'utf-8')
487
 
    'Arch-1-utf-8:you@example.com%cat--br--0--base-0'
488
472
    """
489
 
    if encoding is None:
490
 
        encoding = ''
491
 
    else:
492
 
        encoding = '-' + encoding
493
 
    return "Arch-1%s:%s" % (encoding, str(arch_revision).replace('/', '%'))
 
473
    return "Arch-1:%s" % str(arch_revision).replace('/', '%')
494
474
 
495
475
class NotArchRevision(Exception):
496
476
    def __init__(self, revision_id):
506
486
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--base-5"))
507
487
    Traceback (most recent call last):
508
488
    NotArchRevision: The revision id Arch-1:jrandom@example.com%test--test--0--base-5 does not look like it came from Arch.
509
 
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--patch-5")[0])
510
 
    'jrandom@example.com/test--test--0--patch-5'
511
 
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--patch-5")[0])
512
 
    'jrandom@example.com/test--test--0--patch-5'
513
 
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--patch-5")[1])
514
 
    'None'
515
 
    >>> str(arch_revision("Arch-1-utf-8:jrandom@example.com%test--test--0--patch-5")[1])
516
 
    'utf-8'
 
489
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--patch-5"))
 
490
    'jrandom@example.com/test--test--0--patch-5'
517
491
    """
518
492
    if revision_id is None:
519
 
        return None, None
520
 
    if revision_id[:7] not in ('Arch-1:', 'Arch-1-'):
 
493
        return None
 
494
    if revision_id[:7] != 'Arch-1:':
521
495
        raise NotArchRevision(revision_id)
522
496
    else:
523
497
        try:
524
 
            encoding, arch_name = revision_id[6:].split(':', 1)
525
 
            arch_name = arch_name.replace('%', '/')
526
 
            if encoding == '':
527
 
                encoding = None
528
 
            else:
529
 
                encoding = encoding[1:]
530
 
            return pybaz.Revision(arch_name), encoding
 
498
            return pybaz.Revision(revision_id[7:].replace('%', '/'))
531
499
        except pybaz.errors.NamespaceError, e:
532
500
            raise NotArchRevision(revision_id)
533
 
 
534
 
 
535
 
def create_shared_repository(output_dir):
536
 
    bd = bzrdir.BzrDirMetaFormat1().initialize(output_dir)
537
 
    bd.create_repository(shared=True)
538
 
 
539
 
def create_branch(output_dir):
540
 
    os.mkdir(output_dir)
541
 
    bd = bzrdir.BzrDirMetaFormat1().initialize(output_dir)
542
 
    return bd.create_branch()
543
 
 
544
 
 
545
 
def create_checkout(source, to_location, revision_id=None):
546
 
    checkout = bzrdir.BzrDirMetaFormat1().initialize(to_location)
547
 
    bzrlib.branch.BranchReferenceFormat().initialize(checkout, source)
548
 
    return checkout.create_workingtree(revision_id)
549
 
 
550
 
 
551
 
def create_checkout_metadata(source, to_location, revision_id=None):
552
 
    if revision_id is None:
553
 
        revision_id = source.last_revision()
554
 
    wt = create_checkout(source, to_location, NULL_REVISION)
555
 
    wt.lock_write()
556
 
    try:
557
 
        wt.set_last_revision(revision_id)
558
 
        wt.flush()
559
 
        if revision_id not in (NULL_REVISION, None):
560
 
            basis = wt.basis_tree()
561
 
            basis.lock_read()
562
 
            try:
563
 
                wt._write_inventory(basis.inventory)
564
 
            finally:
565
 
                basis.unlock()
566
 
    finally:
567
 
        wt.unlock()
568
 
    return wt
569
 
 
570
 
 
571
 
def iter_import_version(output_dir, ancestors, tempdir, pb, encoding,
572
 
                        fast=False, verbose=False, dry_run=False,
573
 
                        max_count=None, standalone=False):
 
501
            
 
502
def iter_import_version(output_dir, ancestors, tempdir, pb, fast=False,
 
503
                        verbose=False, dry_run=False, max_count=None):
574
504
    revdir = None
575
 
    log_encoding = 'ascii'
576
 
    if encoding is not None:
577
 
        log_encoding = encoding
578
505
 
579
506
    # Uncomment this for testing, it basically just has baz2bzr only update
580
507
    # 5 patches at a time
592
519
 
593
520
    previous_version=None
594
521
    missing_ancestor = None
595
 
    if dry_run:
596
 
        dry_output_dir = os.path.join(tempdir, 'od')
597
 
        if os.path.exists(output_dir):
598
 
            shutil.copytree(output_dir, dry_output_dir)
599
 
        output_dir = dry_output_dir
600
 
 
601
 
    if os.path.exists(output_dir):
602
 
        target_branch = Branch.open(output_dir)
603
 
    else:
604
 
        if standalone:
605
 
            wt = BzrDir.create_standalone_workingtree(output_dir)
606
 
            target_branch = wt.branch
607
 
        else:
608
 
            target_branch = create_branch(output_dir)
609
522
 
610
523
    for i in range(len(ancestors)):
611
524
        revision = ancestors[i]
612
 
        rev_id = revision_id(revision, encoding)
 
525
        rev_id = revision_id(revision)
613
526
        direct_merges = []
614
527
        if verbose:
615
528
            version = str(revision.version)
616
529
            if version != previous_version:
617
 
                pb.note('On version: %s' % version)
 
530
                clear_progress_bar()
 
531
                print '\rOn version: %s' % version
618
532
            yield Progress(str(revision.patchlevel), i, len(ancestors))
619
533
            previous_version = version
620
534
        else:
621
535
            yield Progress("revisions", i, len(ancestors))
622
 
 
623
 
        if target_branch.repository.has_revision(rev_id):
624
 
            target_branch.append_revision(rev_id)
625
 
            continue
 
536
        if revdir is None and os.path.exists(output_dir):
 
537
            # check for imported revisions and if present just append immediately
 
538
            branch = Branch.open(output_dir)
 
539
            if branch.has_revision(rev_id):
 
540
                branch.append_revision(rev_id)
 
541
                continue
626
542
        if revdir is None:
627
543
            revdir = os.path.join(tempdir, "rd")
628
544
            try:
632
548
                    raise
633
549
                missing_ancestor = revision
634
550
                revdir = None
635
 
                pb.note("unable to access ancestor %s, making into a merge."
 
551
                print ("unable to access ancestor %s, making into a merge."
636
552
                       % missing_ancestor)
637
553
                continue
638
 
            target_tree = create_checkout_metadata(target_branch, revdir)
639
 
            branch = target_tree.branch
 
554
            if os.path.exists(output_dir):
 
555
                bzr_dir = os.path.join(output_dir, '.bzr')
 
556
                new_bzr_dir = os.path.join(tempdir, "rd", '.bzr')
 
557
                # This would be much faster with a simple os.rename(), but if
 
558
                # we fail, we have corrupted the original .bzr directory.  Is
 
559
                # that a big problem, as we can just back out the last
 
560
                # revisions in .bzr/revision_history I don't really know
 
561
                # RBC20051024 - yes, it would be a problem as we could not then
 
562
                # apply the corrupted revision.
 
563
                shutil.copytree(bzr_dir, new_bzr_dir)
 
564
                # Now revdir should have a tree with the latest .bzr, and the
 
565
                # next revision of the baz tree
 
566
                branch = Branch.open(revdir)
 
567
            else:
 
568
                branch = Branch.initialize(revdir)
640
569
        else:
641
570
            old = os.path.join(revdir, ".bzr")
642
571
            new = os.path.join(tempdir, ".bzr")
643
572
            os.rename(old, new)
644
573
            baz_inv, log = apply_revision(tree, revision)
645
574
            os.rename(new, old)
646
 
            target_tree = WorkingTree.open(revdir)
647
 
            branch = target_tree.branch
 
575
            branch = Branch.open(revdir)
648
576
        # cached so we can delete the log
649
577
        log_date = log.date
650
578
        log_summary = log.summary
661
589
            log_message = "\n".join((log_summary, log_description))
662
590
        else:
663
591
            log_message = log_summary
 
592
        branch.lock_write()
 
593
        target_tree = WorkingTree(revdir ,branch=branch)
664
594
        target_tree.lock_write()
665
 
        branch.lock_write()
666
595
        try:
667
596
            if missing_ancestor:
668
597
                # if we want it to be in revision-history, do that here.
669
 
                target_tree.set_parent_ids(
670
 
                    [revision_id(missing_ancestor, encoding)],
671
 
                    allow_leftmost_as_ghost=True)
 
598
                target_tree.add_pending_merge(revision_id(missing_ancestor))
672
599
                missing_ancestor = None
673
600
            for merged_rev in direct_merges:
674
 
                target_tree.add_pending_merge(revision_id(merged_rev,
675
 
                                                          encoding))
676
 
            target_tree.set_root_id(BAZ_IMPORT_ROOT)
677
 
            target_tree.flush()
 
601
                target_tree.add_pending_merge(revision_id(merged_rev))
678
602
            target_tree.set_inventory(baz_inv)
679
 
            commitobj = Commit(reporter=ImportCommitReporter())
680
 
            commitobj.commit(working_tree=target_tree,
681
 
                message=log_message.decode(log_encoding, 'replace'),
682
 
                verbose=False, committer=log_creator, timestamp=timestamp,
683
 
                timezone=0, rev_id=rev_id, revprops={})
 
603
            commitobj = Commit(reporter=ImportCommitReporter(pb))
 
604
            commitobj.commit(branch, log_message.decode('ascii', 'replace'), 
 
605
                             verbose=False, committer=log_creator,
 
606
                             timestamp=timestamp, timezone=0, rev_id=rev_id)
684
607
        finally:
685
608
            target_tree.unlock()
686
609
            branch.unlock()
687
610
    yield Progress("revisions", len(ancestors), len(ancestors))
 
611
    unlink_unversioned(branch, revdir)
688
612
 
689
613
def get_direct_merges(revdir, revision, log):
690
614
    continuation = log.continuation_of
691
615
    previous_version = revision.version
692
616
    if pybaz.WorkingTree(revdir).tree_version != previous_version:
693
617
        pybaz.WorkingTree(revdir).set_tree_version(previous_version)
694
 
    log_path = "%s/{arch}/%s/%s/%s/%s/patch-log/%s" % (revdir,
695
 
        revision.category.nonarch, revision.branch.nonarch,
 
618
    log_path = "%s/{arch}/%s/%s/%s/%s/patch-log/%s" % (revdir, 
 
619
        revision.category.nonarch, revision.branch.nonarch, 
696
620
        revision.version.nonarch, revision.archive, revision.patchlevel)
697
621
    temp_path = tempfile.mktemp(dir=os.path.dirname(revdir))
698
622
    os.rename(log_path, temp_path)
701
625
    os.rename(temp_path, log_path)
702
626
    return direct
703
627
 
704
 
def unlink_unversioned(wt):
705
 
    for unversioned in wt.extras():
706
 
        path = wt.abspath(unversioned)
 
628
def unlink_unversioned(branch, revdir):
 
629
    for unversioned in branch.working_tree().extras():
 
630
        path = os.path.join(revdir, unversioned)
707
631
        if os.path.isdir(path):
708
632
            shutil.rmtree(path)
709
633
        else:
718
642
    tree = revision.get(revdir)
719
643
    log = get_log(tree, revision)
720
644
    try:
721
 
        return tree, bzr_inventory_data(tree), log
 
645
        return tree, bzr_inventory_data(tree), log 
722
646
    except BadFileKind, e:
723
 
        raise UserError("Cannot convert %s because %s is a %s" %
724
 
                        (revision,e.path, e.kind))
 
647
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
725
648
 
726
649
 
727
650
def apply_revision(tree, revision):
730
653
    try:
731
654
        return bzr_inventory_data(tree), log
732
655
    except BadFileKind, e:
733
 
        raise UserError("Cannot convert %s because %s is a %s" %
734
 
                        (revision,e.path, e.kind))
 
656
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
735
657
 
736
658
 
737
659
class BadFileKind(Exception):
749
671
    inv_map = {}
750
672
    for arch_id, path in inv_iter:
751
673
        bzr_file_id = map_file_id(arch_id)
752
 
        inv_map[path] = bzr_file_id
 
674
        inv_map[path] = bzr_file_id 
753
675
 
754
676
    bzr_inv = []
755
677
    for path, file_id in inv_map.iteritems():
766
688
    bzr_inv.sort()
767
689
    return bzr_inv
768
690
 
769
 
 
770
 
def baz_import_branch(to_location, from_branch, fast, max_count, verbose,
771
 
                      encoding, dry_run, reuse_history_list):
772
 
    to_location = os.path.realpath(str(to_location))
773
 
    if from_branch is not None:
774
 
        try:
775
 
            from_branch = pybaz.Version(from_branch)
776
 
        except pybaz.errors.NamespaceError:
777
 
            print "%s is not a valid Arch branch." % from_branch
778
 
            return 1
779
 
    if reuse_history_list is None:
780
 
        reuse_history_list = []
781
 
    import_version(to_location, from_branch, encoding, max_count=max_count,
782
 
                   reuse_history_from=reuse_history_list)
 
691
_global_option('max-count', type = int)
 
692
class cmd_baz_import_branch(Command):
 
693
    """Import an Arch or Baz branch into a bzr branch"""
 
694
    takes_args = ['to_location', 'from_branch?', 'reuse_history*']
 
695
    takes_options = ['verbose', 'max-count']
 
696
 
 
697
    def printer(self, name):
 
698
        print name
 
699
 
 
700
    def run(self, to_location, from_branch=None, fast=False, max_count=None,
 
701
            verbose=False, dry_run=False, reuse_history_list=[]):
 
702
        to_location = os.path.realpath(str(to_location))
 
703
        if from_branch is not None:
 
704
            try:
 
705
                from_branch = pybaz.Version(from_branch)
 
706
            except pybaz.errors.NamespaceError:
 
707
                print "%s is not a valid Arch branch." % from_branch
 
708
                return 1
 
709
        if reuse_history_list is None:
 
710
            reuse_history_list = []
 
711
        import_version(to_location, from_branch, self.printer, 
 
712
                       max_count=max_count, 
 
713
                       reuse_history_from=reuse_history_list)
783
714
 
784
715
 
785
716
class NotInABranch(Exception):
788
719
        self.path = path
789
720
 
790
721
 
791
 
 
792
 
def baz_import(to_root_dir, from_archive, encoding, verbose=False,
793
 
               reuse_history_list=[], prefixes=None):
794
 
    if reuse_history_list is None:
795
 
        reuse_history_list = []
796
 
    to_root = str(os.path.realpath(to_root_dir))
797
 
    if not os.path.exists(to_root):
798
 
        os.mkdir(to_root)
799
 
    if prefixes is not None:
800
 
        prefixes = prefixes.split(':')
801
 
    import_archive(to_root, from_archive, verbose, encoding,
802
 
                   reuse_history_list, prefixes=prefixes)
803
 
 
804
 
 
805
 
def import_archive(to_root, from_archive, verbose,
806
 
                   encoding, reuse_history_from=[], standalone=False,
807
 
                   prefixes=None):
808
 
    def selected(version):
809
 
        if prefixes is None:
810
 
            return True
811
 
        else:
812
 
            for prefix in prefixes:
813
 
                if version.nonarch.startswith(prefix):
814
 
                    return True
815
 
            return False
 
722
class cmd_baz_import(Command):
 
723
    """Import an Arch or Baz archive into bzr branches.
 
724
    
 
725
    reuse_history allows you to specify any previous imports you 
 
726
    have done of different archives, which this archive has branches
 
727
    tagged from. This will dramatically reduce the time to convert 
 
728
    the archive as it will not have to convert the history already
 
729
    converted in that other branch.
 
730
    """
 
731
    takes_args = ['to_root_dir', 'from_archive', 'reuse_history*']
 
732
    takes_options = ['verbose']
 
733
 
 
734
    def printer(self, name):
 
735
        print name
 
736
 
 
737
    def run(self, to_root_dir, from_archive, verbose=False,
 
738
            reuse_history_list=[]):
 
739
        if reuse_history_list is None:
 
740
            reuse_history_list = []
 
741
        to_root = str(os.path.realpath(to_root_dir))
 
742
        if not os.path.exists(to_root):
 
743
            os.mkdir(to_root)
 
744
        import_archive(to_root, from_archive, verbose, self.printer, 
 
745
                       reuse_history_list)
 
746
 
 
747
 
 
748
def import_archive(to_root, from_archive, verbose, printer,
 
749
                   reuse_history_from=[]):
816
750
    real_to = os.path.realpath(to_root)
817
751
    history_locations = [real_to] + reuse_history_from
818
 
    if standalone is False:
 
752
    for version in pybaz.Archive(str(from_archive)).iter_versions():
 
753
        target = os.path.join(to_root, map_namespace(version))
 
754
        printer("importing %s into %s" % (version, target))
 
755
        if not os.path.exists(os.path.dirname(target)):
 
756
            os.makedirs(os.path.dirname(target))
819
757
        try:
820
 
            bd = BzrDir.open(to_root)
821
 
            bd.find_repository()
822
 
        except NotBranchError:
823
 
            create_shared_repository(to_root)
824
 
        except NoRepositoryPresent:
825
 
            raise BzrCommandError("Can't create repository at existing branch.")
826
 
    versions = list(pybaz.Archive(str(from_archive)).iter_versions())
827
 
    progress_bar = bzrlib.ui.ui_factory.nested_progress_bar()
828
 
    try:
829
 
        for num, version in enumerate(versions):
830
 
            progress_bar.update("Branch", num, len(versions))
831
 
            if not selected(version):
832
 
                print "Skipping %s" % version
833
 
                continue
834
 
            target = os.path.join(to_root, map_namespace(version))
835
 
            if not os.path.exists(os.path.dirname(target)):
836
 
                os.makedirs(os.path.dirname(target))
837
 
            try:
838
 
                import_version(target, version, encoding,
839
 
                               reuse_history_from=reuse_history_from,
840
 
                               standalone=standalone)
841
 
            except pybaz.errors.ExecProblem,e:
842
 
                if str(e).find('The requested revision cannot be built.') != -1:
843
 
                    progress_bar.note(
844
 
                        "Skipping version %s as it cannot be built due"
 
758
            import_version(target, version, printer,
 
759
                           reuse_history_from=reuse_history_from)
 
760
        except pybaz.errors.ExecProblem,e:
 
761
            if str(e).find('The requested revision cannot be built.') != -1:
 
762
                printer("Skipping version %s as it cannot be built due"
845
763
                        " to a missing parent archive." % version)
846
 
                else:
847
 
                    raise
848
 
            except UserError, e:
849
 
                if str(e).find('already exists, and the last revision ') != -1:
850
 
                    progress_bar.note(
851
 
                        "Skipping version %s as it has had commits made"
 
764
            else:
 
765
                raise
 
766
        except UserError, e:
 
767
            if str(e).find('already exists, and the last revision ') != -1:
 
768
                printer("Skipping version %s as it has had commits made"
852
769
                        " since it was converted to bzr." % version)
853
 
                else:
854
 
                    raise
855
 
    finally:
856
 
        progress_bar.finished()
 
770
            else:
 
771
                raise
857
772
 
858
773
 
859
774
def map_namespace(a_version):
868
783
        return "%s/%s" % (category, branch)
869
784
    return "%s/%s/%s" % (category, version, branch)
870
785
 
871
 
 
872
786
def map_file_id(file_id):
873
787
    """Convert a baz file id to a bzr one."""
874
788
    return file_id.replace('%', '%25').replace('/', '%2f')