~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to baz_import.py

  • Committer: Aaron Bentley
  • Date: 2007-05-18 09:20:47 UTC
  • Revision ID: aaron.bentley@utoronto.ca-20070518092047-c25wfogkpw1c8yj1
Tags: release-0.17.0
Update version number to 0.17.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
from bzrlib.option import _global_option, Option
32
32
from bzrlib.merge import merge_inner
33
33
from bzrlib.revision import NULL_REVISION
34
 
from bzrlib.tree import EmptyTree
35
34
import bzrlib.ui
 
35
import bzrlib.ui.text
36
36
from bzrlib.workingtree import WorkingTree
37
37
from errors import NoPyBaz
38
38
try:
56
56
import email.Utils
57
57
from progress import *
58
58
 
 
59
 
 
60
BAZ_IMPORT_ROOT = 'TREE_ROOT'
 
61
 
 
62
 
59
63
class ImportCommitReporter(NullCommitReporter):
60
 
    def __init__(self, pb):
61
 
        self.pb = pb
62
64
 
63
65
    def escaped(self, escape_count, message):
64
 
        self.pb.clear()
65
66
        bzrlib.trace.warning("replaced %d control characters in message" %
66
67
                             escape_count)
67
68
 
280
281
    finally:
281
282
        br_from.unlock()
282
283
 
283
 
def get_remaining_revisions(output_dir, version, reuse_history_from=[]):
 
284
def get_remaining_revisions(output_dir, version, encoding, 
 
285
                            reuse_history_from=[]):
284
286
    last_patch = None
285
287
    old_revno = None
286
288
    output_exists = os.path.exists(output_dir)
288
290
        # We are starting from an existing directory, figure out what
289
291
        # the current version is
290
292
        branch = Branch.open(output_dir)
291
 
        last_patch = get_last_revision(branch)
 
293
        last_patch, last_encoding = get_last_revision(branch)
 
294
        assert encoding == last_encoding
292
295
        if last_patch is None:
293
 
            raise NotPreviousImport(branch.base)
294
 
        if version is None:
 
296
            if branch.last_revision() != None:
 
297
                raise NotPreviousImport(branch.base)
 
298
        elif version is None:
295
299
            version = last_patch.version
296
300
    elif version is None:
297
301
        raise UserError("No version specified, and directory does not exist.")
312
316
                        map_namespace(ancestor.version))
313
317
                    try:
314
318
                        source = Branch.open(possible_source)
315
 
                        rev_id = revision_id(ancestor)
 
319
                        rev_id = revision_id(ancestor, encoding)
316
320
                        if rev_id in source.revision_history():
317
321
                            do_branch(source, output_dir, rev_id)
318
322
                            last_patch = ancestor
345
349
###    possible later.
346
350
###    """
347
351
###
348
 
###    def __init__(self, output_dir, version, printer, fancy=True, fast=False,
 
352
###    def __init__(self, output_dir, version, fast=False,
349
353
###                 verbose=False, dry_run=False, max_count=None, 
350
354
###                   reuse_history_from=[]):
351
355
###        self.output_dir = output_dir
353
357
###        self.
354
358
 
355
359
 
356
 
def import_version(output_dir, version, printer, fancy=True, fast=False,
 
360
def import_version(output_dir, version, encoding, fast=False,
357
361
                   verbose=False, dry_run=False, max_count=None,
358
362
                   reuse_history_from=[], standalone=True):
359
363
    """
360
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
 
361
371
    >>> result_path = os.path.join(q, "result")
362
372
    >>> commit_test_revisions()
363
373
    >>> version = pybaz.Version("test@example.com/test--test--0.1")
364
 
    >>> def printer(message): print message
365
 
    >>> 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)
366
379
    Traceback (most recent call last):
367
380
    NotPreviousImport: / is not the location of a previous import.
368
 
    >>> import_version(result_path, version, printer, fancy=False, dry_run=True)
 
381
    >>> import_version(result_path, version, None, dry_run=True)
369
382
    Traceback (most recent call last):
370
383
    UserError: The version test@example.com/test--test--0.1 does not exist.
371
384
    >>> version = pybaz.Version("test@example.com/test--test--0")
372
 
    >>> import_version(result_path, version, printer, fancy=False, dry_run=True)
373
 
    not fancy
374
 
    ....
 
385
    >>> import_version(result_path, version, None, dry_run=True) #doctest: +ELLIPSIS
 
386
    importing test@example.com/test--test--0 into ...
 
387
    ...
 
388
    revisions: ..........................................
375
389
    Dry run, not modifying output_dir
376
390
    Cleaning up
377
 
    >>> import_version(result_path, version, printer, fancy=False)
378
 
    not fancy
379
 
    ....
 
391
    >>> import_version(result_path, version, None) #doctest: +ELLIPSIS
 
392
    importing test@example.com/test--test--0 into ...
 
393
    ...
 
394
    revisions: .....................................................................
380
395
    Cleaning up
381
396
    Import complete.
382
 
    >>> import_version(result_path, version, printer, fancy=False)
 
397
    >>> import_version(result_path, version, None) #doctest: +ELLIPSIS
383
398
    Tree is up-to-date with test@example.com/test--test--0--patch-2
384
399
    >>> commit_more_test_revisions()
385
 
    >>> import_version(result_path, version, printer, fancy=False)
386
 
    not fancy
387
 
    ..
 
400
    >>> import_version(result_path, version, None) #doctest: +ELLIPSIS
 
401
    importing test@example.com/test--test--0 into ...
 
402
    revisions: ....................................................
388
403
    Cleaning up
389
404
    Import complete.
 
405
    >>> bzrlib.ui.ui_factory = old_ui
 
406
    >>> sys.stderr = old_stderr
390
407
    >>> teardown_environ(q)
391
408
    """
392
 
    try:
393
 
        ancestors, old_revno = get_remaining_revisions(output_dir, version,
394
 
                                                       reuse_history_from)
395
 
    except NotBranchError, e:
396
 
        raise NotPreviousImport(e.path)
397
 
    if old_revno is None and len(ancestors) == 0:
398
 
        print 'Version %s has no revisions.' % version
399
 
        return
400
 
    if len(ancestors) == 0:
401
 
        last_revision = get_last_revision(Branch.open(output_dir))
402
 
        print 'Tree is up-to-date with %s' % last_revision
403
 
        return
404
 
 
405
409
    progress_bar = bzrlib.ui.ui_factory.nested_progress_bar()
406
 
    tempdir = tempfile.mkdtemp(prefix="baz2bzr-",
407
 
                               dir=os.path.dirname(output_dir))
408
 
    try:
409
 
        wt = WorkingTree.open(output_dir)
410
 
    except (NotBranchError, NoWorkingTree):
411
 
        wt = None
412
 
    if wt is None:
413
 
        old_basis = EmptyTree()
414
 
    else:
415
 
        old_basis = wt.basis_tree()
416
 
    try:
417
 
        if not fancy:
418
 
            print "not fancy"
 
410
    try:
419
411
        try:
420
 
            for result in iter_import_version(output_dir, ancestors, tempdir,
421
 
                    progress_bar, fast=fast, verbose=verbose, dry_run=dry_run,
422
 
                    max_count=max_count, standalone=standalone):
423
 
                if fancy:
424
 
                    show_progress(progress_bar, result)
425
 
                else:
426
 
                    sys.stdout.write('.')
427
 
        finally:
428
 
            if fancy:
429
 
                progress_bar.finished()
430
 
            else:
431
 
                sys.stdout.write('\n')
432
 
 
433
 
        if dry_run:
434
 
            print 'Dry run, not modifying output_dir'
435
 
            return
436
 
 
437
 
        # Update the working tree of the branch
 
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))
438
430
        try:
439
431
            wt = WorkingTree.open(output_dir)
440
 
        except NoWorkingTree:
 
432
        except (NotBranchError, NoWorkingTree):
441
433
            wt = None
442
 
        if wt is not None:
443
 
            wt.set_last_revision(wt.branch.last_revision())
444
 
            merge_inner(wt.branch, wt.basis_tree(), old_basis, 
445
 
                        ignore_zero=True, this_tree=wt)
446
 
            wt.revert([])
447
 
 
 
434
        try:
 
435
            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
    
 
454
        finally:
 
455
            
 
456
            progress_bar.note('Cleaning up')
 
457
            shutil.rmtree(tempdir)
 
458
        progress_bar.note("Import complete.")
448
459
    finally:
449
 
        printer('Cleaning up')
450
 
        shutil.rmtree(tempdir)
451
 
    printer("Import complete.")
 
460
        progress_bar.finished()
452
461
            
453
462
class UserError(BzrCommandError):
454
463
    def __init__(self, message):
464
473
                           % path)
465
474
 
466
475
 
467
 
def revision_id(arch_revision):
 
476
def revision_id(arch_revision, encoding):
468
477
    """
469
478
    Generate a Bzr revision id from an Arch revision id.  'x' in the id
470
479
    designates a revision imported with an experimental algorithm.  A number
472
481
 
473
482
    :param arch_revision: The Arch revision to generate an ID for.
474
483
 
475
 
    >>> 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)
476
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'
477
488
    """
478
 
    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('/', '%'))
479
494
 
480
495
class NotArchRevision(Exception):
481
496
    def __init__(self, revision_id):
491
506
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--base-5"))
492
507
    Traceback (most recent call last):
493
508
    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"))
495
 
    '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'
496
517
    """
497
518
    if revision_id is None:
498
 
        return None
499
 
    if revision_id[:7] != 'Arch-1:':
 
519
        return None, None
 
520
    if revision_id[:7] not in ('Arch-1:', 'Arch-1-'):
500
521
        raise NotArchRevision(revision_id)
501
522
    else:
502
523
        try:
503
 
            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
504
531
        except pybaz.errors.NamespaceError, e:
505
532
            raise NotArchRevision(revision_id)
506
533
 
525
552
    if revision_id is None:
526
553
        revision_id = source.last_revision()
527
554
    wt = create_checkout(source, to_location, NULL_REVISION)
528
 
    wt.set_last_revision(revision_id)
529
 
    wt._write_inventory(wt.basis_tree().inventory)
 
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()
530
568
    return wt
531
569
 
532
570
 
533
 
def iter_import_version(output_dir, ancestors, tempdir, pb, fast=False,
534
 
                        verbose=False, dry_run=False, max_count=None,
535
 
                        standalone=False):
 
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):
536
574
    revdir = None
 
575
    log_encoding = 'ascii'
 
576
    if encoding is not None:
 
577
        log_encoding = encoding
537
578
 
538
579
    # Uncomment this for testing, it basically just has baz2bzr only update
539
580
    # 5 patches at a time
568
609
 
569
610
    for i in range(len(ancestors)):
570
611
        revision = ancestors[i]
571
 
        rev_id = revision_id(revision)
 
612
        rev_id = revision_id(revision, encoding)
572
613
        direct_merges = []
573
614
        if verbose:
574
615
            version = str(revision.version)
575
616
            if version != previous_version:
576
 
                clear_progress_bar()
577
 
                print '\rOn version: %s' % version
 
617
                pb.note('On version: %s' % version)
578
618
            yield Progress(str(revision.patchlevel), i, len(ancestors))
579
619
            previous_version = version
580
620
        else:
592
632
                    raise
593
633
                missing_ancestor = revision
594
634
                revdir = None
595
 
                print ("unable to access ancestor %s, making into a merge."
 
635
                pb.note("unable to access ancestor %s, making into a merge."
596
636
                       % missing_ancestor)
597
637
                continue
598
638
            target_tree = create_checkout_metadata(target_branch, revdir)
626
666
        try:
627
667
            if missing_ancestor:
628
668
                # if we want it to be in revision-history, do that here.
629
 
                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)
630
672
                missing_ancestor = None
631
673
            for merged_rev in direct_merges:
632
 
                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()
633
678
            target_tree.set_inventory(baz_inv)
634
 
            commitobj = Commit(reporter=ImportCommitReporter(pb))
 
679
            commitobj = Commit(reporter=ImportCommitReporter())
635
680
            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,
639
 
                             revprops={})
 
681
                message=log_message.decode(log_encoding, 'replace'),
 
682
                verbose=False, committer=log_creator, timestamp=timestamp,
 
683
                timezone=0, rev_id=rev_id, revprops={})
640
684
        finally:
641
685
            target_tree.unlock()
642
686
            branch.unlock()
722
766
    bzr_inv.sort()
723
767
    return bzr_inv
724
768
 
725
 
_global_option('max-count', type = int)
726
 
class cmd_baz_import_branch(Command):
727
 
    """Import an Arch or Baz branch into a bzr branch.  <BZRTOOLS>"""
728
 
    takes_args = ['to_location', 'from_branch?', 'reuse_history*']
729
 
    takes_options = ['verbose', 'max-count']
730
 
 
731
 
    def printer(self, name):
732
 
        print name
733
 
 
734
 
    def run(self, to_location, from_branch=None, fast=False, max_count=None,
735
 
            verbose=False, dry_run=False, reuse_history_list=[]):
736
 
        to_location = os.path.realpath(str(to_location))
737
 
        if from_branch is not None:
738
 
            try:
739
 
                from_branch = pybaz.Version(from_branch)
740
 
            except pybaz.errors.NamespaceError:
741
 
                print "%s is not a valid Arch branch." % from_branch
742
 
                return 1
743
 
        if reuse_history_list is None:
744
 
            reuse_history_list = []
745
 
        import_version(to_location, from_branch, self.printer, 
746
 
                       max_count=max_count, 
747
 
                       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)
748
783
 
749
784
 
750
785
class NotInABranch(Exception):
753
788
        self.path = path
754
789
 
755
790
 
756
 
class cmd_baz_import(Command):
757
 
    """Import an Arch or Baz archive into bzr branches.  <BZRTOOLS>
758
 
 
759
 
    This command should be used on local archives (or mirrors) only.  It is
760
 
    quite slow on remote archives.
761
 
    
762
 
    reuse_history allows you to specify any previous imports you 
763
 
    have done of different archives, which this archive has branches
764
 
    tagged from. This will dramatically reduce the time to convert 
765
 
    the archive as it will not have to convert the history already
766
 
    converted in that other branch.
767
 
 
768
 
    If you specify prefixes, only branches whose names start with that prefix
769
 
    will be imported.  Skipped branches will be listed, so you can import any
770
 
    branches you missed by accident.  Here's an example of doing a partial
771
 
    import from thelove@canonical.com:
772
 
    bzr baz-import thelove thelove@canonical.com --prefixes dists:talloc-except
773
 
    """
774
 
    takes_args = ['to_root_dir', 'from_archive', 'reuse_history*']
775
 
    takes_options = ['verbose', Option('prefixes', type=str,
776
 
                     help="Prefixes of branches to import, colon-separated")]
777
 
 
778
 
    def printer(self, name):
779
 
        print name
780
 
 
781
 
    def run(self, to_root_dir, from_archive, verbose=False,
782
 
            reuse_history_list=[], prefixes=None):
783
 
        if reuse_history_list is None:
784
 
            reuse_history_list = []
785
 
        to_root = str(os.path.realpath(to_root_dir))
786
 
        if not os.path.exists(to_root):
787
 
            os.mkdir(to_root)
788
 
        if prefixes is not None:
789
 
            prefixes = prefixes.split(':')
790
 
        import_archive(to_root, from_archive, verbose, self.printer, 
791
 
                       reuse_history_list, prefixes=prefixes)
792
 
 
793
 
 
794
 
def import_archive(to_root, from_archive, verbose, printer,
795
 
                   reuse_history_from=[], standalone=False,
 
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,
796
807
                   prefixes=None):
797
808
    def selected(version):
798
809
        if prefixes is None:
813
824
        except NoRepositoryPresent:
814
825
            raise BzrCommandError("Can't create repository at existing branch.")
815
826
    versions = list(pybaz.Archive(str(from_archive)).iter_versions())
816
 
#    progress_bar = bzrlib.ui.ui_factory.nested_progress_bar()
 
827
    progress_bar = bzrlib.ui.ui_factory.nested_progress_bar()
817
828
    try:
818
829
        for num, version in enumerate(versions):
819
 
#            progress_bar.update("Branch", num, len(versions))
 
830
            progress_bar.update("Branch", num, len(versions))
820
831
            if not selected(version):
821
832
                print "Skipping %s" % version
822
833
                continue
823
834
            target = os.path.join(to_root, map_namespace(version))
824
 
            printer("importing %s into %s" % (version, target))
825
835
            if not os.path.exists(os.path.dirname(target)):
826
836
                os.makedirs(os.path.dirname(target))
827
837
            try:
828
 
                import_version(target, version, printer,
 
838
                import_version(target, version, encoding,
829
839
                               reuse_history_from=reuse_history_from, 
830
840
                               standalone=standalone)
831
841
            except pybaz.errors.ExecProblem,e:
832
842
                if str(e).find('The requested revision cannot be built.') != -1:
833
 
                    printer("Skipping version %s as it cannot be built due"
834
 
                            " to a missing parent archive." % version)
 
843
                    progress_bar.note(
 
844
                        "Skipping version %s as it cannot be built due"
 
845
                        " to a missing parent archive." % version)
835
846
                else:
836
847
                    raise
837
848
            except UserError, e:
838
849
                if str(e).find('already exists, and the last revision ') != -1:
839
 
                    printer("Skipping version %s as it has had commits made"
840
 
                            " since it was converted to bzr." % version)
 
850
                    progress_bar.note(
 
851
                        "Skipping version %s as it has had commits made"
 
852
                        " since it was converted to bzr." % version)
841
853
                else:
842
854
                    raise
843
855
    finally:
844
 
        pass
845
 
#        progress_bar.finished()
 
856
        progress_bar.finished()
 
857
 
846
858
 
847
859
def map_namespace(a_version):
848
860
    a_version = pybaz.Version("%s" % a_version)
856
868
        return "%s/%s" % (category, branch)
857
869
    return "%s/%s/%s" % (category, version, branch)
858
870
 
 
871
 
859
872
def map_file_id(file_id):
860
873
    """Convert a baz file id to a bzr one."""
861
874
    return file_id.replace('%', '%25').replace('/', '%2f')