~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to baz_import.py

  • Committer: Aaron Bentley
  • Date: 2007-06-11 05:08:34 UTC
  • Revision ID: aaron.bentley@utoronto.ca-20070611050834-wcbta2pfitcuopku
fix long-line detection

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Aaron Bentley
 
1
# Copyright (C) 2005, 2006 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.errors import BzrError
20
 
from bzrlib.errors import NotBranchError, BzrCommandError, NoSuchRevision
 
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
                          )
21
28
from bzrlib.branch import Branch
22
29
from bzrlib.commit import Commit, NullCommitReporter
23
30
from bzrlib.commands import Command
24
 
from bzrlib.option import _global_option
 
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
36
from bzrlib.workingtree import WorkingTree
26
37
from errors import NoPyBaz
27
38
try:
37
48
import os.path
38
49
import shutil
39
50
import bzrlib
40
 
import bzrlib.builtins
41
51
import bzrlib.trace
42
52
import bzrlib.merge
43
53
import bzrlib.inventory
46
56
import email.Utils
47
57
from progress import *
48
58
 
 
59
 
 
60
BAZ_IMPORT_ROOT = 'TREE_ROOT'
 
61
 
 
62
 
49
63
class ImportCommitReporter(NullCommitReporter):
50
 
    def __init__(self, pb):
51
 
        self.pb = pb
52
64
 
53
65
    def escaped(self, escape_count, message):
54
 
        self.pb.clear()
55
66
        bzrlib.trace.warning("replaced %d control characters in message" %
56
67
                             escape_count)
57
68
 
71
82
 
72
83
saved_dir = None
73
84
 
 
85
def make_archive(name, location):
 
86
    pb_location = pybaz.ArchiveLocation(location)
 
87
    pb_location.create_master(pybaz.Archive(name),
 
88
                              pybaz.ArchiveLocationParams())
 
89
 
74
90
def test_environ():
75
91
    """
76
92
    >>> q = test_environ()
88
104
    os.environ["HOME"] = os.path.join(tdir, "home")
89
105
    os.mkdir(os.environ["HOME"])
90
106
    arch_dir = os.path.join(tdir, "archive_dir")
91
 
    pybaz.make_archive("test@example.com", arch_dir)
 
107
    make_archive("test@example.com", arch_dir)
92
108
    work_dir = os.path.join(tdir, "work_dir")
93
109
    os.mkdir(work_dir)
94
110
    os.chdir(work_dir)
257
273
                raise
258
274
        try:
259
275
            br_from.bzrdir.clone(to_location, revision_id)
260
 
#            copy_branch(br_from, to_location, revision_id, None)
261
276
        except NoSuchRevision:
262
277
            rmtree(to_location)
263
 
            msg = "The branch %s has no revision %s." % (from_location, revision_id)
 
278
            msg = "The branch %s has no revision %s." % (from_location,
 
279
                                                         revision_id)
264
280
            raise UserError(msg)
265
281
    finally:
266
282
        br_from.unlock()
267
283
 
268
 
def get_remaining_revisions(output_dir, version, reuse_history_from=[]):
 
284
def get_remaining_revisions(output_dir, version, encoding,
 
285
                            reuse_history_from=[]):
269
286
    last_patch = None
270
287
    old_revno = None
271
288
    output_exists = os.path.exists(output_dir)
273
290
        # We are starting from an existing directory, figure out what
274
291
        # the current version is
275
292
        branch = Branch.open(output_dir)
276
 
        last_patch = get_last_revision(branch)
 
293
        last_patch, last_encoding = get_last_revision(branch)
 
294
        assert encoding == last_encoding
277
295
        if last_patch is None:
278
 
            raise NotPreviousImport(branch.base)
279
 
        if version is None:
 
296
            if branch.last_revision() != None:
 
297
                raise NotPreviousImport(branch.base)
 
298
        elif version is None:
280
299
            version = last_patch.version
281
300
    elif version is None:
282
301
        raise UserError("No version specified, and directory does not exist.")
290
309
                    break
291
310
                # try to grab a copy of ancestor
292
311
                # note that is not optimised: we could look for namespace
293
 
                # transitions and only look for the past after the 
 
312
                # transitions and only look for the past after the
294
313
                # transition.
295
314
                for history_root in reuse_history_from:
296
315
                    possible_source = os.path.join(history_root,
297
316
                        map_namespace(ancestor.version))
298
317
                    try:
299
318
                        source = Branch.open(possible_source)
300
 
                        rev_id = revision_id(ancestor)
 
319
                        rev_id = revision_id(ancestor, encoding)
301
320
                        if rev_id in source.revision_history():
302
321
                            do_branch(source, output_dir, rev_id)
303
322
                            last_patch = ancestor
313
332
                break
314
333
        else:
315
334
            raise UserError("Directory \"%s\" already exists, and the last "
316
 
                "revision (%s) is not in the ancestry of %s" % 
 
335
                "revision (%s) is not in the ancestry of %s" %
317
336
                (output_dir, last_patch, version))
318
337
        # Strip off all of the ancestors which are already present
319
338
        # And get a directory starting with the latest ancestor
325
344
 
326
345
###class Importer(object):
327
346
###    """An importer.
328
 
###    
 
347
###
329
348
###    Currently this is used as a parameter object, though more behaviour is
330
349
###    possible later.
331
350
###    """
332
351
###
333
 
###    def __init__(self, output_dir, version, printer, fancy=True, fast=False,
334
 
###                 verbose=False, dry_run=False, max_count=None, 
 
352
###    def __init__(self, output_dir, version, fast=False,
 
353
###                 verbose=False, dry_run=False, max_count=None,
335
354
###                   reuse_history_from=[]):
336
355
###        self.output_dir = output_dir
337
356
###        self.version = version
338
357
###        self.
339
358
 
340
359
 
341
 
def import_version(output_dir, version, printer, fancy=True, fast=False,
 
360
def import_version(output_dir, version, encoding, fast=False,
342
361
                   verbose=False, dry_run=False, max_count=None,
343
 
                   reuse_history_from=[]):
 
362
                   reuse_history_from=[], standalone=True):
344
363
    """
345
364
    >>> 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
 
346
371
    >>> result_path = os.path.join(q, "result")
347
372
    >>> commit_test_revisions()
348
373
    >>> version = pybaz.Version("test@example.com/test--test--0.1")
349
 
    >>> def printer(message): print message
350
 
    >>> import_version('/', version, printer, fancy=False, dry_run=True)
 
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)
351
379
    Traceback (most recent call last):
352
380
    NotPreviousImport: / is not the location of a previous import.
353
 
    >>> import_version(result_path, version, printer, fancy=False, dry_run=True)
 
381
    >>> import_version(result_path, version, None, dry_run=True)
354
382
    Traceback (most recent call last):
355
383
    UserError: The version test@example.com/test--test--0.1 does not exist.
356
384
    >>> version = pybaz.Version("test@example.com/test--test--0")
357
 
    >>> import_version(result_path, version, printer, fancy=False, dry_run=True)
358
 
    not fancy
359
 
    ....
 
385
    >>> import_version(result_path, version, None, dry_run=True) #doctest: +ELLIPSIS
 
386
    importing test@example.com/test--test--0 into ...
 
387
    ...
 
388
    revisions: ..........................................
360
389
    Dry run, not modifying output_dir
361
390
    Cleaning up
362
 
    >>> import_version(result_path, version, printer, fancy=False)
363
 
    not fancy
364
 
    ....
 
391
    >>> import_version(result_path, version, None) #doctest: +ELLIPSIS
 
392
    importing test@example.com/test--test--0 into ...
 
393
    ...
 
394
    revisions: .....................................................................
365
395
    Cleaning up
366
396
    Import complete.
367
 
    >>> import_version(result_path, version, printer, fancy=False)
 
397
    >>> import_version(result_path, version, None) #doctest: +ELLIPSIS
368
398
    Tree is up-to-date with test@example.com/test--test--0--patch-2
369
399
    >>> commit_more_test_revisions()
370
 
    >>> import_version(result_path, version, printer, fancy=False)
371
 
    not fancy
372
 
    ..
 
400
    >>> import_version(result_path, version, None) #doctest: +ELLIPSIS
 
401
    importing test@example.com/test--test--0 into ...
 
402
    revisions: ....................................................
373
403
    Cleaning up
374
404
    Import complete.
 
405
    >>> bzrlib.ui.ui_factory = old_ui
 
406
    >>> sys.stderr = old_stderr
375
407
    >>> teardown_environ(q)
376
408
    """
377
 
    try:
378
 
        ancestors, old_revno = get_remaining_revisions(output_dir, version,
379
 
                                                       reuse_history_from)
380
 
    except NotBranchError, e:
381
 
        raise NotPreviousImport(e.path)
382
 
    if old_revno is None and len(ancestors) == 0:
383
 
        print 'Version %s has no revisions.' % version
384
 
        return
385
 
    if len(ancestors) == 0:
386
 
        last_revision = get_last_revision(Branch.open(output_dir))
387
 
        print 'Tree is up-to-date with %s' % last_revision
388
 
        return
389
 
 
390
 
    progress_bar = ProgressBar()
391
 
    tempdir = tempfile.mkdtemp(prefix="baz2bzr-",
392
 
                               dir=os.path.dirname(output_dir))
393
 
    try:
394
 
        if not fancy:
395
 
            print "not fancy"
 
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
396
434
        try:
397
435
            for result in iter_import_version(output_dir, ancestors, tempdir,
398
 
                    progress_bar, fast=fast, verbose=verbose, dry_run=dry_run,
399
 
                    max_count=max_count):
400
 
                if fancy:
401
 
                    show_progress(progress_bar, result)
402
 
                else:
403
 
                    sys.stdout.write('.')
 
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
 
404
454
        finally:
405
 
            if fancy:
406
 
                progress_bar.clear()
407
 
            else:
408
 
                sys.stdout.write('\n')
409
 
 
410
 
        if dry_run:
411
 
            print 'Dry run, not modifying output_dir'
412
 
            return
413
 
        if os.path.exists(output_dir):
414
 
            # Move the bzr control directory back, and update the working tree
415
 
            revdir = os.path.join(tempdir, "rd")
416
 
            if os.path.exists(revdir):
417
 
                # actual imports were done
418
 
                tmp_bzr_dir = os.path.join(tempdir, '.bzr')
419
 
                
420
 
                bzr_dir = os.path.join(output_dir, '.bzr')
421
 
                new_bzr_dir = os.path.join(tempdir, "rd", '.bzr')
422
 
    
423
 
                os.rename(bzr_dir, tmp_bzr_dir) # Move the original bzr out of the way
424
 
                os.rename(new_bzr_dir, bzr_dir)
425
 
                try:
426
 
                    WorkingTree.open_containing(output_dir)[0].revert([])
427
 
                except:
428
 
                    # If something failed, move back the original bzr directory
429
 
                    os.rename(bzr_dir, new_bzr_dir)
430
 
                    os.rename(tmp_bzr_dir, bzr_dir)
431
 
                    raise
432
 
            else:
433
 
                # no imports - perhaps just append_revisions
434
 
                # should not fail:
435
 
                WorkingTree.open_containing(output_dir)[0].revert([])
436
 
                #bzrlib.builtins.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
 
 
 
455
 
 
456
            progress_bar.note('Cleaning up')
 
457
            shutil.rmtree(tempdir)
 
458
        progress_bar.note("Import complete.")
443
459
    finally:
444
 
        printer('Cleaning up')
445
 
        shutil.rmtree(tempdir)
446
 
    printer("Import complete.")
447
 
            
 
460
        progress_bar.finished()
 
461
 
448
462
class UserError(BzrCommandError):
449
463
    def __init__(self, message):
450
464
        """Exception to throw when a user makes an impossible request
459
473
                           % path)
460
474
 
461
475
 
462
 
def revision_id(arch_revision):
 
476
def revision_id(arch_revision, encoding):
463
477
    """
464
478
    Generate a Bzr revision id from an Arch revision id.  'x' in the id
465
479
    designates a revision imported with an experimental algorithm.  A number
467
481
 
468
482
    :param arch_revision: The Arch revision to generate an ID for.
469
483
 
470
 
    >>> revision_id(pybaz.Revision("you@example.com/cat--br--0--base-0"))
 
484
    >>> revision_id(pybaz.Revision("you@example.com/cat--br--0--base-0"), None)
471
485
    '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'
472
488
    """
473
 
    return "Arch-1:%s" % str(arch_revision).replace('/', '%')
 
489
    if encoding is None:
 
490
        encoding = ''
 
491
    else:
 
492
        encoding = '-' + encoding
 
493
    return "Arch-1%s:%s" % (encoding, str(arch_revision).replace('/', '%'))
474
494
 
475
495
class NotArchRevision(Exception):
476
496
    def __init__(self, revision_id):
486
506
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--base-5"))
487
507
    Traceback (most recent call last):
488
508
    NotArchRevision: The revision id Arch-1:jrandom@example.com%test--test--0--base-5 does not look like it came from Arch.
489
 
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--patch-5"))
490
 
    'jrandom@example.com/test--test--0--patch-5'
 
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'
491
517
    """
492
518
    if revision_id is None:
493
 
        return None
494
 
    if revision_id[:7] != 'Arch-1:':
 
519
        return None, None
 
520
    if revision_id[:7] not in ('Arch-1:', 'Arch-1-'):
495
521
        raise NotArchRevision(revision_id)
496
522
    else:
497
523
        try:
498
 
            return pybaz.Revision(revision_id[7:].replace('%', '/'))
 
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
499
531
        except pybaz.errors.NamespaceError, e:
500
532
            raise NotArchRevision(revision_id)
501
 
            
502
 
def iter_import_version(output_dir, ancestors, tempdir, pb, fast=False,
503
 
                        verbose=False, dry_run=False, max_count=None):
 
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):
504
574
    revdir = None
 
575
    log_encoding = 'ascii'
 
576
    if encoding is not None:
 
577
        log_encoding = encoding
505
578
 
506
579
    # Uncomment this for testing, it basically just has baz2bzr only update
507
580
    # 5 patches at a time
519
592
 
520
593
    previous_version=None
521
594
    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)
522
609
 
523
610
    for i in range(len(ancestors)):
524
611
        revision = ancestors[i]
525
 
        rev_id = revision_id(revision)
 
612
        rev_id = revision_id(revision, encoding)
526
613
        direct_merges = []
527
614
        if verbose:
528
615
            version = str(revision.version)
529
616
            if version != previous_version:
530
 
                clear_progress_bar()
531
 
                print '\rOn version: %s' % version
 
617
                pb.note('On version: %s' % version)
532
618
            yield Progress(str(revision.patchlevel), i, len(ancestors))
533
619
            previous_version = version
534
620
        else:
535
621
            yield Progress("revisions", i, len(ancestors))
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.repository.has_revision(rev_id):
540
 
                branch.append_revision(rev_id)
541
 
                continue
 
622
 
 
623
        if target_branch.repository.has_revision(rev_id):
 
624
            target_branch.append_revision(rev_id)
 
625
            continue
542
626
        if revdir is None:
543
627
            revdir = os.path.join(tempdir, "rd")
544
628
            try:
548
632
                    raise
549
633
                missing_ancestor = revision
550
634
                revdir = None
551
 
                print ("unable to access ancestor %s, making into a merge."
 
635
                pb.note("unable to access ancestor %s, making into a merge."
552
636
                       % missing_ancestor)
553
637
                continue
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)
 
638
            target_tree = create_checkout_metadata(target_branch, revdir)
 
639
            branch = target_tree.branch
569
640
        else:
570
641
            old = os.path.join(revdir, ".bzr")
571
642
            new = os.path.join(tempdir, ".bzr")
572
643
            os.rename(old, new)
573
644
            baz_inv, log = apply_revision(tree, revision)
574
645
            os.rename(new, old)
575
 
            branch = Branch.open(revdir)
 
646
            target_tree = WorkingTree.open(revdir)
 
647
            branch = target_tree.branch
576
648
        # cached so we can delete the log
577
649
        log_date = log.date
578
650
        log_summary = log.summary
589
661
            log_message = "\n".join((log_summary, log_description))
590
662
        else:
591
663
            log_message = log_summary
 
664
        target_tree.lock_write()
592
665
        branch.lock_write()
593
 
        target_tree = WorkingTree(revdir ,branch=branch)
594
 
        target_tree.lock_write()
595
666
        try:
596
667
            if missing_ancestor:
597
668
                # if we want it to be in revision-history, do that here.
598
 
                target_tree.add_pending_merge(revision_id(missing_ancestor))
 
669
                target_tree.set_parent_ids(
 
670
                    [revision_id(missing_ancestor, encoding)],
 
671
                    allow_leftmost_as_ghost=True)
599
672
                missing_ancestor = None
600
673
            for merged_rev in direct_merges:
601
 
                target_tree.add_pending_merge(revision_id(merged_rev))
 
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()
602
678
            target_tree.set_inventory(baz_inv)
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)
 
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={})
607
684
        finally:
608
685
            target_tree.unlock()
609
686
            branch.unlock()
610
687
    yield Progress("revisions", len(ancestors), len(ancestors))
611
 
    unlink_unversioned(branch, revdir)
612
688
 
613
689
def get_direct_merges(revdir, revision, log):
614
690
    continuation = log.continuation_of
615
691
    previous_version = revision.version
616
692
    if pybaz.WorkingTree(revdir).tree_version != previous_version:
617
693
        pybaz.WorkingTree(revdir).set_tree_version(previous_version)
618
 
    log_path = "%s/{arch}/%s/%s/%s/%s/patch-log/%s" % (revdir, 
619
 
        revision.category.nonarch, revision.branch.nonarch, 
 
694
    log_path = "%s/{arch}/%s/%s/%s/%s/patch-log/%s" % (revdir,
 
695
        revision.category.nonarch, revision.branch.nonarch,
620
696
        revision.version.nonarch, revision.archive, revision.patchlevel)
621
697
    temp_path = tempfile.mktemp(dir=os.path.dirname(revdir))
622
698
    os.rename(log_path, temp_path)
625
701
    os.rename(temp_path, log_path)
626
702
    return direct
627
703
 
628
 
def unlink_unversioned(branch, revdir):
629
 
    for unversioned in branch.working_tree().extras():
630
 
        path = os.path.join(revdir, unversioned)
 
704
def unlink_unversioned(wt):
 
705
    for unversioned in wt.extras():
 
706
        path = wt.abspath(unversioned)
631
707
        if os.path.isdir(path):
632
708
            shutil.rmtree(path)
633
709
        else:
642
718
    tree = revision.get(revdir)
643
719
    log = get_log(tree, revision)
644
720
    try:
645
 
        return tree, bzr_inventory_data(tree), log 
 
721
        return tree, bzr_inventory_data(tree), log
646
722
    except BadFileKind, e:
647
 
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
 
723
        raise UserError("Cannot convert %s because %s is a %s" %
 
724
                        (revision,e.path, e.kind))
648
725
 
649
726
 
650
727
def apply_revision(tree, revision):
653
730
    try:
654
731
        return bzr_inventory_data(tree), log
655
732
    except BadFileKind, e:
656
 
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
 
733
        raise UserError("Cannot convert %s because %s is a %s" %
 
734
                        (revision,e.path, e.kind))
657
735
 
658
736
 
659
737
class BadFileKind(Exception):
671
749
    inv_map = {}
672
750
    for arch_id, path in inv_iter:
673
751
        bzr_file_id = map_file_id(arch_id)
674
 
        inv_map[path] = bzr_file_id 
 
752
        inv_map[path] = bzr_file_id
675
753
 
676
754
    bzr_inv = []
677
755
    for path, file_id in inv_map.iteritems():
688
766
    bzr_inv.sort()
689
767
    return bzr_inv
690
768
 
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)
 
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)
714
783
 
715
784
 
716
785
class NotInABranch(Exception):
719
788
        self.path = path
720
789
 
721
790
 
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=[]):
 
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
750
816
    real_to = os.path.realpath(to_root)
751
817
    history_locations = [real_to] + reuse_history_from
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))
 
818
    if standalone is False:
757
819
        try:
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"
 
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"
763
845
                        " to a missing parent archive." % version)
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"
 
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"
769
852
                        " since it was converted to bzr." % version)
770
 
            else:
771
 
                raise
 
853
                else:
 
854
                    raise
 
855
    finally:
 
856
        progress_bar.finished()
772
857
 
773
858
 
774
859
def map_namespace(a_version):
783
868
        return "%s/%s" % (category, branch)
784
869
    return "%s/%s/%s" % (category, version, branch)
785
870
 
 
871
 
786
872
def map_file_id(file_id):
787
873
    """Convert a baz file id to a bzr one."""
788
874
    return file_id.replace('%', '%25').replace('/', '%2f')