~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to baz_import.py

  • Committer: Aaron Bentley
  • Date: 2005-11-10 21:04:19 UTC
  • Revision ID: aaron.bentley@utoronto.ca-20051110210419-a402638d94693825
Handled whitespace branch names better

Show diffs side-by-side

added added

removed removed

Lines of Context:
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
import errno
18
 
 
19
 
from bzrlib.errors import BzrError
20
 
from bzrlib.errors import NotBranchError, BzrCommandError, NoSuchRevision
21
16
from bzrlib.branch import Branch
22
 
from bzrlib.clone import copy_branch
23
 
from bzrlib.commit import Commit, NullCommitReporter
24
17
from bzrlib.commands import Command
25
 
from bzrlib.option import _global_option
26
 
from bzrlib.workingtree import WorkingTree
27
18
from errors import NoPyBaz
28
19
try:
29
20
    import pybaz
30
21
    import pybaz.errors
31
 
    from pybaz import NameParser as NameParser
32
22
    from pybaz.backends.baz import null_cmd
33
23
except ImportError:
34
24
    raise NoPyBaz
35
 
from fai import iter_new_merges, direct_merges
36
25
import tempfile
37
26
import os
38
27
import os.path
39
28
import shutil
40
29
import bzrlib
 
30
from bzrlib.errors import BzrError, NotBranchError, BzrCommandError
41
31
import bzrlib.trace
42
32
import bzrlib.merge
43
33
import bzrlib.inventory
46
36
import email.Utils
47
37
from progress import *
48
38
 
49
 
class ImportCommitReporter(NullCommitReporter):
50
 
    def __init__(self, pb):
51
 
        self.pb = pb
52
 
 
53
 
    def escaped(self, escape_count, message):
54
 
        self.pb.clear()
55
 
        bzrlib.trace.warning("replaced %d control characters in message" %
56
 
                             escape_count)
57
 
 
58
39
def add_id(files, id=None):
59
40
    """Adds an explicit id to a list of files.
60
41
 
69
50
    args.extend(files)
70
51
    return null_cmd(args)
71
52
 
72
 
saved_dir = None
73
 
 
74
53
def test_environ():
75
54
    """
76
55
    >>> q = test_environ()
82
61
    >>> os.path.exists(q)
83
62
    False
84
63
    """
85
 
    global saved_dir
86
 
    saved_dir = os.getcwdu()
87
64
    tdir = tempfile.mkdtemp(prefix="testdir-")
88
65
    os.environ["HOME"] = os.path.join(tdir, "home")
89
66
    os.mkdir(os.environ["HOME"])
127
104
    add_id([path], id)
128
105
 
129
106
def teardown_environ(tdir):
130
 
    os.chdir(saved_dir)
 
107
    os.chdir("/")
131
108
    shutil.rmtree(tdir)
132
109
 
133
110
def timport(tree, summary):
219
196
    """
220
197
    try:
221
198
        revision = version.iter_revisions(reverse=True).next()
222
 
    except StopIteration:
223
 
        return ()
224
199
    except:
225
 
        print version
226
200
        if not version.exists():
227
201
            raise NoSuchVersion(version)
228
202
        else:
238
212
    except NotArchRevision:
239
213
        raise UserError(
240
214
            "Directory \"%s\" already exists, and the last revision is not"
241
 
            " an Arch revision (%s)" % (branch.base, last_patch))
242
 
 
243
 
def do_branch(br_from, to_location, revision_id):
244
 
    """Derived from branch in builtins."""
245
 
    br_from.lock_read()
246
 
    try:
247
 
        try:
248
 
            os.mkdir(to_location)
249
 
        except OSError, e:
250
 
            if e.errno == errno.EEXIST:
251
 
                raise UserError('Target directory "%s" already'
252
 
                                      ' exists.' % to_location)
253
 
            if e.errno == errno.ENOENT:
254
 
                raise UserError('Parent of "%s" does not exist.' %
255
 
                                      to_location)
256
 
            else:
257
 
                raise
258
 
        try:
259
 
            copy_branch(br_from, to_location, revision_id, None)
260
 
        except NoSuchRevision:
261
 
            rmtree(to_location)
262
 
            msg = "The branch %s has no revision %s." % (from_location, revision_id)
263
 
            raise UserError(msg)
264
 
    finally:
265
 
        br_from.unlock()
266
 
 
267
 
def get_remaining_revisions(output_dir, version, reuse_history_from=[]):
 
215
            " an Arch revision (%s)" % (output_dir, last_patch))
 
216
 
 
217
 
 
218
def get_remaining_revisions(output_dir, version):
268
219
    last_patch = None
269
220
    old_revno = None
270
 
    output_exists = os.path.exists(output_dir)
271
 
    if output_exists:
 
221
    if os.path.exists(output_dir):
272
222
        # We are starting from an existing directory, figure out what
273
223
        # the current version is
274
 
        branch = Branch.open(output_dir)
 
224
        branch = find_branch(output_dir)
275
225
        last_patch = get_last_revision(branch)
276
226
        if last_patch is None:
277
227
            raise NotPreviousImport(branch.base)
282
232
 
283
233
    try:
284
234
        ancestors = version_ancestry(version)
285
 
        if not output_exists and reuse_history_from != []:
286
 
            for ancestor in reversed(ancestors):
287
 
                if last_patch is not None:
288
 
                    # found something to copy
289
 
                    break
290
 
                # try to grab a copy of ancestor
291
 
                # note that is not optimised: we could look for namespace
292
 
                # transitions and only look for the past after the 
293
 
                # transition.
294
 
                for history_root in reuse_history_from:
295
 
                    possible_source = os.path.join(history_root,
296
 
                        map_namespace(ancestor.version))
297
 
                    try:
298
 
                        source = Branch.open(possible_source)
299
 
                        rev_id = revision_id(ancestor)
300
 
                        if rev_id in source.revision_history():
301
 
                            do_branch(source, output_dir, rev_id)
302
 
                            last_patch = ancestor
303
 
                            break
304
 
                    except NotBranchError:
305
 
                        pass
 
235
        if len(ancestors) > 0 and not ancestors[0].archive.is_registered():
 
236
            ancestors = ancestors[1:]
306
237
    except NoSuchVersion, e:
307
 
        raise UserError(str(e))
 
238
        raise UserError(e)
308
239
 
309
240
    if last_patch:
310
241
        for i in range(len(ancestors)):
317
248
        # Strip off all of the ancestors which are already present
318
249
        # And get a directory starting with the latest ancestor
319
250
        latest_ancestor = ancestors[i]
320
 
        old_revno = Branch.open(output_dir).revno()
 
251
        old_revno = find_branch(output_dir).revno()
321
252
        ancestors = ancestors[i+1:]
322
253
    return ancestors, old_revno
323
254
 
324
 
 
325
 
###class Importer(object):
326
 
###    """An importer.
327
 
###    
328
 
###    Currently this is used as a parameter object, though more behaviour is
329
 
###    possible later.
330
 
###    """
331
 
###
332
 
###    def __init__(self, output_dir, version, printer, fancy=True, fast=False,
333
 
###                 verbose=False, dry_run=False, max_count=None, 
334
 
###                   reuse_history_from=[]):
335
 
###        self.output_dir = output_dir
336
 
###        self.version = version
337
 
###        self.
338
 
 
339
 
 
340
 
def import_version(output_dir, version, printer, fancy=True, fast=False,
341
 
                   verbose=False, dry_run=False, max_count=None,
342
 
                   reuse_history_from=[]):
 
255
def import_version(output_dir, version, fancy=True, fast=False, verbose=False, 
 
256
                   dry_run=False, max_count=None, skip_symlinks=False):
343
257
    """
344
258
    >>> q = test_environ()
345
259
    >>> result_path = os.path.join(q, "result")
346
260
    >>> commit_test_revisions()
347
261
    >>> version = pybaz.Version("test@example.com/test--test--0.1")
348
 
    >>> def printer(message): print message
349
 
    >>> import_version('/', version, printer, fancy=False, dry_run=True)
 
262
    >>> import_version('/', version, fancy=False, dry_run=True)
350
263
    Traceback (most recent call last):
351
 
    NotPreviousImport: / is not the location of a previous import.
352
 
    >>> import_version(result_path, version, printer, fancy=False, dry_run=True)
 
264
    UserError: / exists, but is not a bzr branch.
 
265
    >>> import_version(result_path, version, fancy=False, dry_run=True)
353
266
    Traceback (most recent call last):
354
267
    UserError: The version test@example.com/test--test--0.1 does not exist.
355
268
    >>> version = pybaz.Version("test@example.com/test--test--0")
356
 
    >>> import_version(result_path, version, printer, fancy=False, dry_run=True)
 
269
    >>> import_version(result_path, version, fancy=False, dry_run=True)
357
270
    not fancy
358
271
    ....
359
272
    Dry run, not modifying output_dir
360
273
    Cleaning up
361
 
    >>> import_version(result_path, version, printer, fancy=False)
 
274
    >>> import_version(result_path, version, fancy=False)
362
275
    not fancy
363
276
    ....
364
277
    Cleaning up
365
278
    Import complete.
366
 
    >>> import_version(result_path, version, printer, fancy=False)
 
279
    >>> import_version(result_path, version, fancy=False)
367
280
    Tree is up-to-date with test@example.com/test--test--0--patch-2
368
281
    >>> commit_more_test_revisions()
369
 
    >>> import_version(result_path, version, printer, fancy=False)
 
282
    >>> import_version(result_path, version, fancy=False)
370
283
    not fancy
371
284
    ..
372
285
    Cleaning up
374
287
    >>> teardown_environ(q)
375
288
    """
376
289
    try:
377
 
        ancestors, old_revno = get_remaining_revisions(output_dir, version,
378
 
                                                       reuse_history_from)
379
 
    except NotBranchError, e:
 
290
        ancestors, old_revno = get_remaining_revisions(output_dir, version)
 
291
    except NotInABranch, e:
380
292
        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
293
    if len(ancestors) == 0:
385
 
        last_revision = get_last_revision(Branch.open(output_dir))
 
294
        last_revision = get_last_revision(find_branch(output_dir))
386
295
        print 'Tree is up-to-date with %s' % last_revision
387
296
        return
388
297
 
394
303
            print "not fancy"
395
304
        try:
396
305
            for result in iter_import_version(output_dir, ancestors, tempdir,
397
 
                    progress_bar, fast=fast, verbose=verbose, dry_run=dry_run,
398
 
                    max_count=max_count):
 
306
                    fast=fast, verbose=verbose, dry_run=dry_run, 
 
307
                    max_count=max_count, skip_symlinks=skip_symlinks):
399
308
                if fancy:
400
309
                    show_progress(progress_bar, result)
401
310
                else:
411
320
            return
412
321
        if os.path.exists(output_dir):
413
322
            # 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), 
 
323
            tmp_bzr_dir = os.path.join(tempdir, '.bzr')
 
324
            
 
325
            bzr_dir = os.path.join(output_dir, '.bzr')
 
326
            new_bzr_dir = os.path.join(tempdir, "rd", '.bzr')
 
327
 
 
328
            os.rename(bzr_dir, tmp_bzr_dir) # Move the original bzr out of the way
 
329
            os.rename(new_bzr_dir, bzr_dir)
 
330
            try:
 
331
                bzrlib.merge.merge((output_dir, -1), (output_dir, old_revno), 
437
332
                                   check_clean=False, this_dir=output_dir, 
438
333
                                   ignore_zero=True)
 
334
            except:
 
335
                # If something failed, move back the original bzr directory
 
336
                os.rename(bzr_dir, new_bzr_dir)
 
337
                os.rename(tmp_bzr_dir, bzr_dir)
 
338
                raise
439
339
        else:
440
340
            revdir = os.path.join(tempdir, "rd")
441
341
            os.rename(revdir, output_dir)
442
342
 
443
343
    finally:
444
 
        printer('Cleaning up')
 
344
        print 'Cleaning up'
445
345
        shutil.rmtree(tempdir)
446
 
    printer("Import complete.")
 
346
    print "Import complete."
447
347
            
448
348
class UserError(BzrCommandError):
449
349
    def __init__(self, message):
468
368
    :param arch_revision: The Arch revision to generate an ID for.
469
369
 
470
370
    >>> revision_id(pybaz.Revision("you@example.com/cat--br--0--base-0"))
471
 
    'Arch-1:you@example.com%cat--br--0--base-0'
 
371
    'Arch-x:you@example.com%cat--br--0--base-0'
472
372
    """
473
 
    return "Arch-1:%s" % str(arch_revision).replace('/', '%')
 
373
    return "Arch-x:%s" % str(arch_revision).replace('/', '%')
474
374
 
475
375
class NotArchRevision(Exception):
476
376
    def __init__(self, revision_id):
480
380
 
481
381
def arch_revision(revision_id):
482
382
    """
483
 
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0"))
484
 
    Traceback (most recent call last):
485
 
    NotArchRevision: The revision id Arch-1:jrandom@example.com%test--test--0 does not look like it came from Arch.
486
 
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--base-5"))
487
 
    Traceback (most recent call last):
488
 
    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"))
 
383
    >>> str(arch_revision("Arch-x:jrandom@example.com%test--test--0"))
 
384
    Traceback (most recent call last):
 
385
    NotArchRevision: The revision id Arch-x:jrandom@example.com%test--test--0 does not look like it came from Arch.
 
386
    >>> str(arch_revision("Arch-x:jrandom@example.com%test--test--0--base-5"))
 
387
    Traceback (most recent call last):
 
388
    NotArchRevision: The revision id Arch-x:jrandom@example.com%test--test--0--base-5 does not look like it came from Arch.
 
389
    >>> str(arch_revision("Arch-x:jrandom@example.com%test--test--0--patch-5"))
490
390
    'jrandom@example.com/test--test--0--patch-5'
491
391
    """
492
392
    if revision_id is None:
493
393
        return None
494
 
    if revision_id[:7] != 'Arch-1:':
 
394
    if revision_id[:7] != 'Arch-x:':
495
395
        raise NotArchRevision(revision_id)
496
396
    else:
497
397
        try:
499
399
        except pybaz.errors.NamespaceError, e:
500
400
            raise NotArchRevision(revision_id)
501
401
            
502
 
def iter_import_version(output_dir, ancestors, tempdir, pb, fast=False,
503
 
                        verbose=False, dry_run=False, max_count=None):
 
402
def iter_import_version(output_dir, ancestors, tempdir, fast=False,
 
403
                        verbose=False, dry_run=False, max_count=None,
 
404
                        skip_symlinks=False):
504
405
    revdir = None
505
406
 
506
407
    # Uncomment this for testing, it basically just has baz2bzr only update
518
419
    #        print '\t%s' % a
519
420
 
520
421
    previous_version=None
521
 
    missing_ancestor = None
522
422
 
523
423
    for i in range(len(ancestors)):
524
424
        revision = ancestors[i]
525
 
        rev_id = revision_id(revision)
526
 
        direct_merges = []
527
425
        if verbose:
528
426
            version = str(revision.version)
529
427
            if version != previous_version:
533
431
            previous_version = version
534
432
        else:
535
433
            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.has_revision(rev_id):
540
 
                branch.append_revision(rev_id)
541
 
                continue
542
434
        if revdir is None:
543
435
            revdir = os.path.join(tempdir, "rd")
544
 
            try:
545
 
                tree, baz_inv, log = get_revision(revdir, revision)
546
 
            except pybaz.errors.ExecProblem, e:
547
 
                if ("%s" % e.args).find('could not connect') == -1:
548
 
                    raise
549
 
                missing_ancestor = revision
550
 
                revdir = None
551
 
                print ("unable to access ancestor %s, making into a merge."
552
 
                       % missing_ancestor)
553
 
                continue
 
436
            baz_inv, log = get_revision(revdir, revision, 
 
437
                                        skip_symlinks=skip_symlinks)
554
438
            if os.path.exists(output_dir):
555
439
                bzr_dir = os.path.join(output_dir, '.bzr')
556
440
                new_bzr_dir = os.path.join(tempdir, "rd", '.bzr')
558
442
                # we fail, we have corrupted the original .bzr directory.  Is
559
443
                # that a big problem, as we can just back out the last
560
444
                # 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
445
                shutil.copytree(bzr_dir, new_bzr_dir)
564
446
                # Now revdir should have a tree with the latest .bzr, and the
565
447
                # next revision of the baz tree
566
 
                branch = Branch.open(revdir)
 
448
                branch = find_branch(revdir)
567
449
            else:
568
450
                branch = Branch.initialize(revdir)
569
451
        else:
570
452
            old = os.path.join(revdir, ".bzr")
571
453
            new = os.path.join(tempdir, ".bzr")
572
454
            os.rename(old, new)
573
 
            baz_inv, log = apply_revision(tree, revision)
 
455
            baz_inv, log = apply_revision(revdir, revision, 
 
456
                                          skip_symlinks=skip_symlinks)
574
457
            os.rename(new, old)
575
 
            branch = Branch.open(revdir)
576
 
        # cached so we can delete the log
577
 
        log_date = log.date
578
 
        log_summary = log.summary
579
 
        log_description = log.description
580
 
        is_continuation = log.continuation_of is not None
581
 
        log_creator = log.creator
582
 
        direct_merges = get_direct_merges(revdir, revision, log)
583
 
 
584
 
        timestamp = email.Utils.mktime_tz(log_date + (0,))
585
 
        if log_summary is None:
586
 
            log_summary = ""
587
 
        # log_descriptions of None and "" are ignored.
588
 
        if not is_continuation and log_description:
589
 
            log_message = "\n".join((log_summary, log_description))
590
 
        else:
591
 
            log_message = log_summary
 
458
            branch = find_branch(revdir)
 
459
        timestamp = email.Utils.mktime_tz(log.date + (0,))
 
460
        rev_id = revision_id(revision)
592
461
        branch.lock_write()
593
 
        target_tree = WorkingTree(revdir ,branch=branch)
594
 
        target_tree.lock_write()
595
462
        try:
596
 
            if missing_ancestor:
597
 
                # if we want it to be in revision-history, do that here.
598
 
                target_tree.add_pending_merge(revision_id(missing_ancestor))
599
 
                missing_ancestor = None
600
 
            for merged_rev in direct_merges:
601
 
                target_tree.add_pending_merge(revision_id(merged_rev))
602
 
            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)
 
463
            branch.working_tree().set_inventory(baz_inv)
 
464
            bzrlib.trace.silent = True
 
465
            branch.commit(log.summary, verbose=False, committer=log.creator,
 
466
                          timestamp=timestamp, timezone=0, rev_id=rev_id)
607
467
        finally:
608
 
            target_tree.unlock()
 
468
            bzrlib.trace.silent = False   
609
469
            branch.unlock()
610
470
    yield Progress("revisions", len(ancestors), len(ancestors))
611
471
    unlink_unversioned(branch, revdir)
612
472
 
613
 
def get_direct_merges(revdir, revision, log):
614
 
    continuation = log.continuation_of
615
 
    previous_version = revision.version
616
 
    if pybaz.WorkingTree(revdir).tree_version != previous_version:
617
 
        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, 
620
 
        revision.version.nonarch, revision.archive, revision.patchlevel)
621
 
    temp_path = tempfile.mktemp(dir=os.path.dirname(revdir))
622
 
    os.rename(log_path, temp_path)
623
 
    merges = list(iter_new_merges(revdir, revision.version))
624
 
    direct = direct_merges(merges, [continuation])
625
 
    os.rename(temp_path, log_path)
626
 
    return direct
627
 
 
628
473
def unlink_unversioned(branch, revdir):
629
474
    for unversioned in branch.working_tree().extras():
630
475
        path = os.path.join(revdir, unversioned)
634
479
            os.unlink(path)
635
480
 
636
481
def get_log(tree, revision):
637
 
    log = pybaz.Patchlog(revision, tree=tree)
 
482
    log = tree.iter_logs(version=revision.version, reverse=True).next()
638
483
    assert str(log.revision) == str(revision), (log.revision, revision)
639
484
    return log
640
485
 
641
 
def get_revision(revdir, revision):
642
 
    tree = revision.get(revdir)
 
486
def get_revision(revdir, revision, skip_symlinks=False):
 
487
    revision.get(revdir)
 
488
    tree = pybaz.tree_root(revdir)
643
489
    log = get_log(tree, revision)
644
490
    try:
645
 
        return tree, bzr_inventory_data(tree), log 
 
491
        return bzr_inventory_data(tree, skip_symlinks=skip_symlinks), log 
646
492
    except BadFileKind, e:
647
493
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
648
494
 
649
495
 
650
 
def apply_revision(tree, revision):
 
496
def apply_revision(revdir, revision, skip_symlinks=False):
 
497
    tree = pybaz.tree_root(revdir)
651
498
    revision.apply(tree)
652
499
    log = get_log(tree, revision)
653
500
    try:
654
 
        return bzr_inventory_data(tree), log
 
501
        return bzr_inventory_data(tree, skip_symlinks=skip_symlinks), log
655
502
    except BadFileKind, e:
656
503
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
657
504
 
658
505
 
 
506
 
 
507
 
659
508
class BadFileKind(Exception):
660
509
    """The file kind is not permitted in bzr inventories"""
661
510
    def __init__(self, tree_root, path, kind):
665
514
        Exception.__init__(self, "File %s is of forbidden type %s" %
666
515
                           (os.path.join(tree_root, path), kind))
667
516
 
668
 
 
669
 
def bzr_inventory_data(tree):
 
517
def bzr_inventory_data(tree, skip_symlinks=False):
670
518
    inv_iter = tree.iter_inventory_ids(source=True, both=True)
671
519
    inv_map = {}
672
520
    for arch_id, path in inv_iter:
673
 
        bzr_file_id = map_file_id(arch_id)
 
521
        bzr_file_id = arch_id.replace('%', '%25').replace('/', '%2f')
674
522
        inv_map[path] = bzr_file_id 
675
523
 
676
524
    bzr_inv = []
677
525
    for path, file_id in inv_map.iteritems():
678
526
        full_path = os.path.join(tree, path)
679
527
        kind = bzrlib.osutils.file_kind(full_path)
680
 
        if kind not in ("file", "directory", "symlink"):
 
528
        if skip_symlinks and kind == "symlink":
 
529
            continue
 
530
        if kind not in ("file", "directory"):
681
531
            raise BadFileKind(tree, path, kind)
682
532
        parent_dir = os.path.dirname(path)
683
533
        if parent_dir != "":
688
538
    bzr_inv.sort()
689
539
    return bzr_inv
690
540
 
691
 
_global_option('max-count', type = int)
692
 
class cmd_baz_import_branch(Command):
 
541
class NotInABranch(Exception):
 
542
    def __init__(self, path):
 
543
        Exception.__init__(self, "%s is not in a branch." % path)
 
544
        self.path = path
 
545
 
 
546
 
 
547
def find_branch(path):
 
548
    """
 
549
    >>> find_branch('/')
 
550
    Traceback (most recent call last):
 
551
    NotInABranch: / is not in a branch.
 
552
    >>> sb = bzrlib.ScratchBranch()
 
553
    >>> isinstance(find_branch(sb.base), Branch)
 
554
    True
 
555
    """
 
556
    try:
 
557
        return Branch.open(path)
 
558
    except NotBranchError, e:
 
559
        raise NotInABranch(path)
 
560
 
 
561
class cmd_baz_import(Command):
693
562
    """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=[]):
 
563
    takes_args = ['to_location', 'from_branch?']
 
564
    takes_options = ['verbose']
 
565
 
 
566
    def run(self, to_location, from_branch=None, skip_symlinks=False, 
 
567
            fast=False, max_count=None, verbose=False, dry_run=False):
702
568
        to_location = os.path.realpath(str(to_location))
703
569
        if from_branch is not None:
704
570
            try:
706
572
            except pybaz.errors.NamespaceError:
707
573
                print "%s is not a valid Arch branch." % from_branch
708
574
                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)
714
 
 
715
 
 
716
 
class NotInABranch(Exception):
717
 
    def __init__(self, path):
718
 
        Exception.__init__(self, "%s is not in a branch." % path)
719
 
        self.path = path
720
 
 
721
 
 
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=[]):
750
 
    real_to = os.path.realpath(to_root)
751
 
    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))
757
 
        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"
763
 
                        " 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"
769
 
                        " since it was converted to bzr." % version)
770
 
            else:
771
 
                raise
772
 
 
773
 
 
774
 
def map_namespace(a_version):
775
 
    a_version = pybaz.Version("%s" % a_version)
776
 
    parser = NameParser(a_version)
777
 
    version = parser.get_version()
778
 
    branch = parser.get_branch()
779
 
    category = parser.get_category()
780
 
    if branch is None or branch == '':
781
 
        branch = "+trunk"
782
 
    if version == '0':
783
 
        return "%s/%s" % (category, branch)
784
 
    return "%s/%s/%s" % (category, version, branch)
785
 
 
786
 
def map_file_id(file_id):
787
 
    """Convert a baz file id to a bzr one."""
788
 
    return file_id.replace('%', '%25').replace('/', '%2f')
 
575
        import_version(to_location, from_branch)