~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to baz_import.py

  • Committer: Aaron Bentley
  • Date: 2005-11-10 20:15:27 UTC
  • Revision ID: aaron.bentley@utoronto.ca-20051110201527-8caa84ae529a1871
Added Daniel Silverstone to credits

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
            branch.commit(log.summary, verbose=False, committer=log.creator,
 
466
                          timestamp=timestamp, timezone=0, rev_id=rev_id)
 
467
        finally:
 
468
            bzrlib.trace.silent = False   
 
469
            branch.unlock()
 
470
    yield Progress("revisions", len(ancestors), len(ancestors))
 
471
    unlink_unversioned(branch, revdir)
 
472
 
 
473
def unlink_unversioned(branch, revdir):
 
474
    for unversioned in branch.working_tree().extras():
 
475
        path = os.path.join(revdir, unversioned)
 
476
        if os.path.isdir(path):
 
477
            shutil.rmtree(path)
 
478
        else:
 
479
            os.unlink(path)
 
480
 
 
481
def get_log(tree, revision):
 
482
    log = tree.iter_logs(version=revision.version, reverse=True).next()
 
483
    assert str(log.revision) == str(revision), (log.revision, revision)
 
484
    return log
 
485
 
 
486
def get_revision(revdir, revision, skip_symlinks=False):
 
487
    revision.get(revdir)
 
488
    tree = pybaz.tree_root(revdir)
 
489
    log = get_log(tree, revision)
 
490
    try:
 
491
        return bzr_inventory_data(tree, skip_symlinks=skip_symlinks), log 
 
492
    except BadFileKind, e:
 
493
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
 
494
 
 
495
 
 
496
def apply_revision(revdir, revision, skip_symlinks=False):
 
497
    tree = pybaz.tree_root(revdir)
 
498
    revision.apply(tree)
 
499
    log = get_log(tree, revision)
 
500
    try:
 
501
        return bzr_inventory_data(tree, skip_symlinks=skip_symlinks), log
 
502
    except BadFileKind, e:
 
503
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
 
504
 
 
505
 
 
506
 
 
507
 
 
508
class BadFileKind(Exception):
 
509
    """The file kind is not permitted in bzr inventories"""
 
510
    def __init__(self, tree_root, path, kind):
 
511
        self.tree_root = tree_root
 
512
        self.path = path
 
513
        self.kind = kind
 
514
        Exception.__init__(self, "File %s is of forbidden type %s" %
 
515
                           (os.path.join(tree_root, path), kind))
 
516
 
 
517
def bzr_inventory_data(tree, skip_symlinks=False):
 
518
    inv_iter = tree.iter_inventory_ids(source=True, both=True)
 
519
    inv_map = {}
 
520
    for arch_id, path in inv_iter:
 
521
        bzr_file_id = arch_id.replace('%', '%25').replace('/', '%2f')
 
522
        inv_map[path] = bzr_file_id 
 
523
 
 
524
    bzr_inv = []
 
525
    for path, file_id in inv_map.iteritems():
 
526
        full_path = os.path.join(tree, path)
 
527
        kind = bzrlib.osutils.file_kind(full_path)
 
528
        if skip_symlinks and kind == "symlink":
 
529
            continue
 
530
        if kind not in ("file", "directory"):
 
531
            raise BadFileKind(tree, path, kind)
 
532
        parent_dir = os.path.dirname(path)
 
533
        if parent_dir != "":
 
534
            parent_id = inv_map[parent_dir]
 
535
        else:
 
536
            parent_id = bzrlib.inventory.ROOT_ID
 
537
        bzr_inv.append((path, file_id, parent_id, kind))
 
538
    bzr_inv.sort()
 
539
    return bzr_inv
 
540
 
 
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):
 
562
    """Import an Arch or Baz branch into a bzr branch"""
 
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):
 
568
        to_location = os.path.realpath(str(to_location))
 
569
        if from_branch is not None:
 
570
            try:
 
571
                from_branch = pybaz.Version(from_branch)
 
572
            except pybaz.errors.NamespaceError:
 
573
                print "%s is not a valid Arch branch." % from_branch
 
574
                return 1
 
575
        import_version(to_location, from_branch)