~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to baz_import.py

  • Committer: Aaron Bentley
  • Date: 2013-08-20 03:02:43 UTC
  • Revision ID: aaron@aaronbentley.com-20130820030243-r8v1xfbcnd8f10p4
Fix zap command for 2.6/7

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Aaron Bentley
2
 
 
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
 
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
 
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
from bzrlib.branch import Branch
17
 
from bzrlib.commands import Command
18
 
from errors import NoPyBaz
19
 
try:
20
 
    import pybaz
21
 
    import pybaz.errors
22
 
    from pybaz.backends.baz import null_cmd
23
 
except ImportError:
24
 
    raise NoPyBaz
25
 
import tempfile
26
 
import os
27
 
import os.path
28
 
import shutil
29
 
import bzrlib
30
 
from bzrlib.errors import BzrError, NotBranchError, BzrCommandError
31
 
import bzrlib.trace
32
 
import bzrlib.merge
33
 
import bzrlib.inventory
34
 
import bzrlib.osutils
35
 
import sys
36
 
import email.Utils
37
 
from progress import *
38
 
 
39
 
def add_id(files, id=None):
40
 
    """Adds an explicit id to a list of files.
41
 
 
42
 
    :param files: the name of the file to add an id to
43
 
    :type files: list of str
44
 
    :param id: tag one file using the specified id, instead of generating id
45
 
    :type id: str
46
 
    """
47
 
    args = ["add-id"]
48
 
    if id is not None:
49
 
        args.extend(["--id", id])
50
 
    args.extend(files)
51
 
    return null_cmd(args)
52
 
 
53
 
def test_environ():
54
 
    """
55
 
    >>> q = test_environ()
56
 
    >>> os.path.exists(q)
57
 
    True
58
 
    >>> os.path.exists(os.path.join(q, "home", ".arch-params"))
59
 
    True
60
 
    >>> teardown_environ(q)
61
 
    >>> os.path.exists(q)
62
 
    False
63
 
    """
64
 
    tdir = tempfile.mkdtemp(prefix="testdir-")
65
 
    os.environ["HOME"] = os.path.join(tdir, "home")
66
 
    os.mkdir(os.environ["HOME"])
67
 
    arch_dir = os.path.join(tdir, "archive_dir")
68
 
    pybaz.make_archive("test@example.com", arch_dir)
69
 
    work_dir = os.path.join(tdir, "work_dir")
70
 
    os.mkdir(work_dir)
71
 
    os.chdir(work_dir)
72
 
    pybaz.init_tree(work_dir, "test@example.com/test--test--0")
73
 
    lib_dir = os.path.join(tdir, "lib_dir")
74
 
    os.mkdir(lib_dir)
75
 
    pybaz.register_revision_library(lib_dir)
76
 
    pybaz.set_my_id("Test User<test@example.org>")
77
 
    return tdir
78
 
 
79
 
def add_file(path, text, id):
80
 
    """
81
 
    >>> q = test_environ()
82
 
    >>> add_file("path with space", "text", "lalala")
83
 
    >>> tree = pybaz.tree_root(".")
84
 
    >>> inv = list(tree.iter_inventory_ids(source=True, both=True))
85
 
    >>> ("x_lalala", "path with space") in inv
86
 
    True
87
 
    >>> teardown_environ(q)
88
 
    """
89
 
    file(path, "wb").write(text)
90
 
    add_id([path], id)
91
 
 
92
 
 
93
 
def add_dir(path, id):
94
 
    """
95
 
    >>> q = test_environ()
96
 
    >>> add_dir("path with\(sp) space", "lalala")
97
 
    >>> tree = pybaz.tree_root(".")
98
 
    >>> inv = list(tree.iter_inventory_ids(source=True, both=True))
99
 
    >>> ("x_lalala", "path with\(sp) space") in inv
100
 
    True
101
 
    >>> teardown_environ(q)
102
 
    """
103
 
    os.mkdir(path)
104
 
    add_id([path], id)
105
 
 
106
 
def teardown_environ(tdir):
107
 
    os.chdir("/")
108
 
    shutil.rmtree(tdir)
109
 
 
110
 
def timport(tree, summary):
111
 
    msg = tree.log_message()
112
 
    msg["summary"] = summary
113
 
    tree.import_(msg)
114
 
 
115
 
def commit(tree, summary):
116
 
    """
117
 
    >>> q = test_environ()
118
 
    >>> tree = pybaz.tree_root(".")
119
 
    >>> timport(tree, "import")
120
 
    >>> commit(tree, "commit")
121
 
    >>> logs = [str(l.revision) for l in tree.iter_logs()]
122
 
    >>> len(logs)
123
 
    2
124
 
    >>> logs[0]
125
 
    'test@example.com/test--test--0--base-0'
126
 
    >>> logs[1]
127
 
    'test@example.com/test--test--0--patch-1'
128
 
    >>> teardown_environ(q)
129
 
    """
130
 
    msg = tree.log_message()
131
 
    msg["summary"] = summary
132
 
    tree.commit(msg)
133
 
 
134
 
def commit_test_revisions():
135
 
    """
136
 
    >>> q = test_environ()
137
 
    >>> commit_test_revisions()
138
 
    >>> a = pybaz.Archive("test@example.com")
139
 
    >>> revisions = list(a.iter_revisions("test--test--0"))
140
 
    >>> len(revisions)
141
 
    3
142
 
    >>> str(revisions[2])
143
 
    'test@example.com/test--test--0--base-0'
144
 
    >>> str(revisions[1])
145
 
    'test@example.com/test--test--0--patch-1'
146
 
    >>> str(revisions[0])
147
 
    'test@example.com/test--test--0--patch-2'
148
 
    >>> teardown_environ(q)
149
 
    """
150
 
    tree = pybaz.tree_root(".")
151
 
    add_file("mainfile", "void main(void){}", "mainfile by aaron")
152
 
    timport(tree, "Created mainfile")
153
 
    file("mainfile", "wb").write("or something like that")
154
 
    commit(tree, "altered mainfile")
155
 
    add_file("ofile", "this is another file", "ofile by aaron")
156
 
    commit(tree, "altered mainfile")
157
 
 
158
 
 
159
 
def commit_more_test_revisions():
160
 
    """
161
 
    >>> q = test_environ()
162
 
    >>> commit_test_revisions()
163
 
    >>> commit_more_test_revisions()
164
 
    >>> a = pybaz.Archive("test@example.com")
165
 
    >>> revisions = list(a.iter_revisions("test--test--0"))
166
 
    >>> len(revisions)
167
 
    4
168
 
    >>> str(revisions[0])
169
 
    'test@example.com/test--test--0--patch-3'
170
 
    >>> teardown_environ(q)
171
 
    """
172
 
    tree = pybaz.tree_root(".")
173
 
    add_file("trainfile", "void train(void){}", "trainfile by aaron")
174
 
    commit(tree, "altered trainfile")
175
 
 
176
 
class NoSuchVersion(Exception):
177
 
    def __init__(self, version):
178
 
        Exception.__init__(self, "The version %s does not exist." % version)
179
 
        self.version = version
180
 
 
181
 
def version_ancestry(version):
182
 
    """
183
 
    >>> q = test_environ()
184
 
    >>> commit_test_revisions()
185
 
    >>> version = pybaz.Version("test@example.com/test--test--0")
186
 
    >>> ancestors = version_ancestry(version)
187
 
    >>> str(ancestors[0])
188
 
    'test@example.com/test--test--0--base-0'
189
 
    >>> str(ancestors[1])
190
 
    'test@example.com/test--test--0--patch-1'
191
 
    >>> version = pybaz.Version("test@example.com/test--test--0.5")
192
 
    >>> ancestors = version_ancestry(version)
193
 
    Traceback (most recent call last):
194
 
    NoSuchVersion: The version test@example.com/test--test--0.5 does not exist.
195
 
    >>> teardown_environ(q)
196
 
    """
197
 
    try:
198
 
        revision = version.iter_revisions(reverse=True).next()
199
 
    except:
200
 
        if not version.exists():
201
 
            raise NoSuchVersion(version)
202
 
        else:
203
 
            raise
204
 
    ancestors = list(revision.iter_ancestors(metoo=True))
205
 
    ancestors.reverse()
206
 
    return ancestors
207
 
 
208
 
def get_last_revision(branch):
209
 
    last_patch = branch.last_revision()
210
 
    try:
211
 
        return arch_revision(last_patch)
212
 
    except NotArchRevision:
213
 
        raise UserError(
214
 
            "Directory \"%s\" already exists, and the last revision is not"
215
 
            " an Arch revision (%s)" % (output_dir, last_patch))
216
 
 
217
 
 
218
 
def get_remaining_revisions(output_dir, version):
219
 
    last_patch = None
220
 
    old_revno = None
221
 
    if os.path.exists(output_dir):
222
 
        # We are starting from an existing directory, figure out what
223
 
        # the current version is
224
 
        branch = find_branch(output_dir)
225
 
        last_patch = get_last_revision(branch)
226
 
        if last_patch is None:
227
 
            raise NotPreviousImport(branch.base)
228
 
        if version is None:
229
 
            version = last_patch.version
230
 
    elif version is None:
231
 
        raise UserError("No version specified, and directory does not exist.")
232
 
 
233
 
    try:
234
 
        ancestors = version_ancestry(version)
235
 
        if len(ancestors) > 0 and not ancestors[0].archive.is_registered():
236
 
            ancestors = ancestors[1:]
237
 
    except NoSuchVersion, e:
238
 
        raise UserError(e)
239
 
 
240
 
    if last_patch:
241
 
        for i in range(len(ancestors)):
242
 
            if ancestors[i] == last_patch:
243
 
                break
244
 
        else:
245
 
            raise UserError("Directory \"%s\" already exists, and the last "
246
 
                "revision (%s) is not in the ancestry of %s" % 
247
 
                (output_dir, last_patch, version))
248
 
        # Strip off all of the ancestors which are already present
249
 
        # And get a directory starting with the latest ancestor
250
 
        latest_ancestor = ancestors[i]
251
 
        old_revno = find_branch(output_dir).revno()
252
 
        ancestors = ancestors[i+1:]
253
 
    return ancestors, old_revno
254
 
 
255
 
def import_version(output_dir, version, fancy=True, fast=False, verbose=False, 
256
 
                   dry_run=False, max_count=None, skip_symlinks=False):
257
 
    """
258
 
    >>> q = test_environ()
259
 
    >>> result_path = os.path.join(q, "result")
260
 
    >>> commit_test_revisions()
261
 
    >>> version = pybaz.Version("test@example.com/test--test--0.1")
262
 
    >>> import_version('/', version, fancy=False, dry_run=True)
263
 
    Traceback (most recent call last):
264
 
    UserError: / exists, but is not a bzr branch.
265
 
    >>> import_version(result_path, version, fancy=False, dry_run=True)
266
 
    Traceback (most recent call last):
267
 
    UserError: The version test@example.com/test--test--0.1 does not exist.
268
 
    >>> version = pybaz.Version("test@example.com/test--test--0")
269
 
    >>> import_version(result_path, version, fancy=False, dry_run=True)
270
 
    not fancy
271
 
    ....
272
 
    Dry run, not modifying output_dir
273
 
    Cleaning up
274
 
    >>> import_version(result_path, version, fancy=False)
275
 
    not fancy
276
 
    ....
277
 
    Cleaning up
278
 
    Import complete.
279
 
    >>> import_version(result_path, version, fancy=False)
280
 
    Tree is up-to-date with test@example.com/test--test--0--patch-2
281
 
    >>> commit_more_test_revisions()
282
 
    >>> import_version(result_path, version, fancy=False)
283
 
    not fancy
284
 
    ..
285
 
    Cleaning up
286
 
    Import complete.
287
 
    >>> teardown_environ(q)
288
 
    """
289
 
    try:
290
 
        ancestors, old_revno = get_remaining_revisions(output_dir, version)
291
 
    except NotInABranch, e:
292
 
        raise NotPreviousImport(e.path)
293
 
    if len(ancestors) == 0:
294
 
        last_revision = get_last_revision(find_branch(output_dir))
295
 
        print 'Tree is up-to-date with %s' % last_revision
296
 
        return
297
 
 
298
 
    progress_bar = ProgressBar()
299
 
    tempdir = tempfile.mkdtemp(prefix="baz2bzr-",
300
 
                               dir=os.path.dirname(output_dir))
301
 
    try:
302
 
        if not fancy:
303
 
            print "not fancy"
304
 
        try:
305
 
            for result in iter_import_version(output_dir, ancestors, tempdir,
306
 
                    fast=fast, verbose=verbose, dry_run=dry_run, 
307
 
                    max_count=max_count, skip_symlinks=skip_symlinks):
308
 
                if fancy:
309
 
                    show_progress(progress_bar, result)
310
 
                else:
311
 
                    sys.stdout.write('.')
312
 
        finally:
313
 
            if fancy:
314
 
                progress_bar.clear()
315
 
            else:
316
 
                sys.stdout.write('\n')
317
 
 
318
 
        if dry_run:
319
 
            print 'Dry run, not modifying output_dir'
320
 
            return
321
 
        if os.path.exists(output_dir):
322
 
            # Move the bzr control directory back, and update the working tree
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), 
332
 
                                   check_clean=False, this_dir=output_dir, 
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
339
 
        else:
340
 
            revdir = os.path.join(tempdir, "rd")
341
 
            os.rename(revdir, output_dir)
342
 
 
343
 
    finally:
344
 
        print 'Cleaning up'
345
 
        shutil.rmtree(tempdir)
346
 
    print "Import complete."
347
 
            
348
 
class UserError(BzrCommandError):
349
 
    def __init__(self, message):
350
 
        """Exception to throw when a user makes an impossible request
351
 
        :param message: The message to emit when printing this exception
352
 
        :type message: string
353
 
        """
354
 
        BzrCommandError.__init__(self, message)
355
 
 
356
 
class NotPreviousImport(UserError):
357
 
    def __init__(self, path):
358
 
        UserError.__init__(self, "%s is not the location of a previous import."
359
 
                           % path)
360
 
 
361
 
 
362
 
def revision_id(arch_revision):
363
 
    """
364
 
    Generate a Bzr revision id from an Arch revision id.  'x' in the id
365
 
    designates a revision imported with an experimental algorithm.  A number
366
 
    would indicate a particular standardized version.
367
 
 
368
 
    :param arch_revision: The Arch revision to generate an ID for.
369
 
 
370
 
    >>> revision_id(pybaz.Revision("you@example.com/cat--br--0--base-0"))
371
 
    'Arch-x:you@example.com%cat--br--0--base-0'
372
 
    """
373
 
    return "Arch-x:%s" % str(arch_revision).replace('/', '%')
374
 
 
375
 
class NotArchRevision(Exception):
376
 
    def __init__(self, revision_id):
377
 
        msg = "The revision id %s does not look like it came from Arch."\
378
 
            % revision_id
379
 
        Exception.__init__(self, msg)
380
 
 
381
 
def arch_revision(revision_id):
382
 
    """
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"))
390
 
    'jrandom@example.com/test--test--0--patch-5'
391
 
    """
392
 
    if revision_id is None:
393
 
        return None
394
 
    if revision_id[:7] != 'Arch-x:':
395
 
        raise NotArchRevision(revision_id)
396
 
    else:
397
 
        try:
398
 
            return pybaz.Revision(revision_id[7:].replace('%', '/'))
399
 
        except pybaz.errors.NamespaceError, e:
400
 
            raise NotArchRevision(revision_id)
401
 
            
402
 
def iter_import_version(output_dir, ancestors, tempdir, fast=False,
403
 
                        verbose=False, dry_run=False, max_count=None,
404
 
                        skip_symlinks=False):
405
 
    revdir = None
406
 
 
407
 
    # Uncomment this for testing, it basically just has baz2bzr only update
408
 
    # 5 patches at a time
409
 
    if max_count:
410
 
        ancestors = ancestors[:max_count]
411
 
 
412
 
    # Not sure if I want this output. basically it tells you ahead of time
413
 
    # what it is going to do, but then later it tells you as it is doing it.
414
 
    # what probably would be best would be to collapse it into ranges, so that
415
 
    # this gives the simple view, and then later it gives the blow by blow.
416
 
    #if verbose:
417
 
    #    print 'Adding the following revisions:'
418
 
    #    for a in ancestors:
419
 
    #        print '\t%s' % a
420
 
 
421
 
    previous_version=None
422
 
 
423
 
    for i in range(len(ancestors)):
424
 
        revision = ancestors[i]
425
 
        if verbose:
426
 
            version = str(revision.version)
427
 
            if version != previous_version:
428
 
                clear_progress_bar()
429
 
                print '\rOn version: %s' % version
430
 
            yield Progress(str(revision.patchlevel), i, len(ancestors))
431
 
            previous_version = version
432
 
        else:
433
 
            yield Progress("revisions", i, len(ancestors))
434
 
        if revdir is None:
435
 
            revdir = os.path.join(tempdir, "rd")
436
 
            baz_inv, log = get_revision(revdir, revision, 
437
 
                                        skip_symlinks=skip_symlinks)
438
 
            if os.path.exists(output_dir):
439
 
                bzr_dir = os.path.join(output_dir, '.bzr')
440
 
                new_bzr_dir = os.path.join(tempdir, "rd", '.bzr')
441
 
                # This would be much faster with a simple os.rename(), but if
442
 
                # we fail, we have corrupted the original .bzr directory.  Is
443
 
                # that a big problem, as we can just back out the last
444
 
                # revisions in .bzr/revision_history I don't really know
445
 
                shutil.copytree(bzr_dir, new_bzr_dir)
446
 
                # Now revdir should have a tree with the latest .bzr, and the
447
 
                # next revision of the baz tree
448
 
                branch = find_branch(revdir)
449
 
            else:
450
 
                branch = Branch.initialize(revdir)
451
 
        else:
452
 
            old = os.path.join(revdir, ".bzr")
453
 
            new = os.path.join(tempdir, ".bzr")
454
 
            os.rename(old, new)
455
 
            baz_inv, log = apply_revision(revdir, revision, 
456
 
                                          skip_symlinks=skip_symlinks)
457
 
            os.rename(new, old)
458
 
            branch = find_branch(revdir)
459
 
        timestamp = email.Utils.mktime_tz(log.date + (0,))
460
 
        rev_id = revision_id(revision)
461
 
        branch.lock_write()
462
 
        try:
463
 
            branch.working_tree().set_inventory(baz_inv)
464
 
            bzrlib.trace.silent = True
465
 
            wt = branch.working_tree()
466
 
            wt.commit(log.summary, verbose=False, committer=log.creator,
467
 
                      timestamp=timestamp, timezone=0, rev_id=rev_id)
468
 
        finally:
469
 
            bzrlib.trace.silent = False   
470
 
            branch.unlock()
471
 
    yield Progress("revisions", len(ancestors), len(ancestors))
472
 
    unlink_unversioned(branch, revdir)
473
 
 
474
 
def unlink_unversioned(branch, revdir):
475
 
    for unversioned in branch.working_tree().extras():
476
 
        path = os.path.join(revdir, unversioned)
477
 
        if os.path.isdir(path):
478
 
            shutil.rmtree(path)
479
 
        else:
480
 
            os.unlink(path)
481
 
 
482
 
def get_log(tree, revision):
483
 
    log = tree.iter_logs(version=revision.version, reverse=True).next()
484
 
    assert str(log.revision) == str(revision), (log.revision, revision)
485
 
    return log
486
 
 
487
 
def get_revision(revdir, revision, skip_symlinks=False):
488
 
    revision.get(revdir)
489
 
    tree = pybaz.tree_root(revdir)
490
 
    log = get_log(tree, revision)
491
 
    try:
492
 
        return bzr_inventory_data(tree, skip_symlinks=skip_symlinks), log 
493
 
    except BadFileKind, e:
494
 
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
495
 
 
496
 
 
497
 
def apply_revision(revdir, revision, skip_symlinks=False):
498
 
    tree = pybaz.tree_root(revdir)
499
 
    revision.apply(tree)
500
 
    log = get_log(tree, revision)
501
 
    try:
502
 
        return bzr_inventory_data(tree, skip_symlinks=skip_symlinks), log
503
 
    except BadFileKind, e:
504
 
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
505
 
 
506
 
 
507
 
 
508
 
 
509
 
class BadFileKind(Exception):
510
 
    """The file kind is not permitted in bzr inventories"""
511
 
    def __init__(self, tree_root, path, kind):
512
 
        self.tree_root = tree_root
513
 
        self.path = path
514
 
        self.kind = kind
515
 
        Exception.__init__(self, "File %s is of forbidden type %s" %
516
 
                           (os.path.join(tree_root, path), kind))
517
 
 
518
 
def bzr_inventory_data(tree, skip_symlinks=False):
519
 
    inv_iter = tree.iter_inventory_ids(source=True, both=True)
520
 
    inv_map = {}
521
 
    for arch_id, path in inv_iter:
522
 
        bzr_file_id = arch_id.replace('%', '%25').replace('/', '%2f')
523
 
        inv_map[path] = bzr_file_id 
524
 
 
525
 
    bzr_inv = []
526
 
    for path, file_id in inv_map.iteritems():
527
 
        full_path = os.path.join(tree, path)
528
 
        kind = bzrlib.osutils.file_kind(full_path)
529
 
        if skip_symlinks and kind == "symlink":
530
 
            continue
531
 
        if kind not in ("file", "directory"):
532
 
            raise BadFileKind(tree, path, kind)
533
 
        parent_dir = os.path.dirname(path)
534
 
        if parent_dir != "":
535
 
            parent_id = inv_map[parent_dir]
536
 
        else:
537
 
            parent_id = bzrlib.inventory.ROOT_ID
538
 
        bzr_inv.append((path, file_id, parent_id, kind))
539
 
    bzr_inv.sort()
540
 
    return bzr_inv
541
 
 
542
 
class NotInABranch(Exception):
543
 
    def __init__(self, path):
544
 
        Exception.__init__(self, "%s is not in a branch." % path)
545
 
        self.path = path
546
 
 
547
 
 
548
 
def find_branch(path):
549
 
    """
550
 
    >>> find_branch('/')
551
 
    Traceback (most recent call last):
552
 
    NotInABranch: / is not in a branch.
553
 
    >>> sb = bzrlib.ScratchBranch()
554
 
    >>> isinstance(find_branch(sb.base), Branch)
555
 
    True
556
 
    """
557
 
    try:
558
 
        return Branch.open(path)
559
 
    except NotBranchError, e:
560
 
        raise NotInABranch(path)
561
 
 
562
 
class cmd_baz_import(Command):
563
 
    """Import an Arch or Baz branch into a bzr branch"""
564
 
    takes_args = ['to_location', 'from_branch?']
565
 
    takes_options = ['verbose']
566
 
 
567
 
    def run(self, to_location, from_branch=None, skip_symlinks=False, 
568
 
            fast=False, max_count=None, verbose=False, dry_run=False):
569
 
        to_location = os.path.realpath(str(to_location))
570
 
        if from_branch is not None:
571
 
            try:
572
 
                from_branch = pybaz.Version(from_branch)
573
 
            except pybaz.errors.NamespaceError:
574
 
                print "%s is not a valid Arch branch." % from_branch
575
 
                return 1
576
 
        import_version(to_location, from_branch)