~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to baz_import.py

  • Committer: Aaron Bentley
  • Date: 2008-11-11 15:55:32 UTC
  • Revision ID: aaron@aaronbentley.com-20081111155532-pjxa3kib17yggl9x
Update shelf tests to new command names

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
 
 
17
 
from bzrlib.errors import NotBranchError
18
 
from bzrlib.branch import Branch
19
 
from bzrlib.commands import Command
20
 
from errors import NoPyBaz
21
 
try:
22
 
    import pybaz
23
 
    import pybaz.errors
24
 
    from pybaz import NameParser as NameParser
25
 
    from pybaz.backends.baz import null_cmd
26
 
except ImportError:
27
 
    raise NoPyBaz
28
 
from fai import iter_new_merges, direct_merges
29
 
import tempfile
30
 
import os
31
 
import os.path
32
 
import shutil
33
 
import bzrlib
34
 
from bzrlib.errors import BzrError
35
 
import bzrlib.trace
36
 
import bzrlib.merge
37
 
import bzrlib.inventory
38
 
import bzrlib.osutils
39
 
import sys
40
 
import email.Utils
41
 
from progress import *
42
 
 
43
 
def add_id(files, id=None):
44
 
    """Adds an explicit id to a list of files.
45
 
 
46
 
    :param files: the name of the file to add an id to
47
 
    :type files: list of str
48
 
    :param id: tag one file using the specified id, instead of generating id
49
 
    :type id: str
50
 
    """
51
 
    args = ["add-id"]
52
 
    if id is not None:
53
 
        args.extend(["--id", id])
54
 
    args.extend(files)
55
 
    return null_cmd(args)
56
 
 
57
 
saved_dir = None
58
 
 
59
 
def test_environ():
60
 
    """
61
 
    >>> q = test_environ()
62
 
    >>> os.path.exists(q)
63
 
    True
64
 
    >>> os.path.exists(os.path.join(q, "home", ".arch-params"))
65
 
    True
66
 
    >>> teardown_environ(q)
67
 
    >>> os.path.exists(q)
68
 
    False
69
 
    """
70
 
    global saved_dir
71
 
    saved_dir = os.getcwdu()
72
 
    tdir = tempfile.mkdtemp(prefix="testdir-")
73
 
    os.environ["HOME"] = os.path.join(tdir, "home")
74
 
    os.mkdir(os.environ["HOME"])
75
 
    arch_dir = os.path.join(tdir, "archive_dir")
76
 
    pybaz.make_archive("test@example.com", arch_dir)
77
 
    work_dir = os.path.join(tdir, "work_dir")
78
 
    os.mkdir(work_dir)
79
 
    os.chdir(work_dir)
80
 
    pybaz.init_tree(work_dir, "test@example.com/test--test--0")
81
 
    lib_dir = os.path.join(tdir, "lib_dir")
82
 
    os.mkdir(lib_dir)
83
 
    pybaz.register_revision_library(lib_dir)
84
 
    pybaz.set_my_id("Test User<test@example.org>")
85
 
    return tdir
86
 
 
87
 
def add_file(path, text, id):
88
 
    """
89
 
    >>> q = test_environ()
90
 
    >>> add_file("path with space", "text", "lalala")
91
 
    >>> tree = pybaz.tree_root(".")
92
 
    >>> inv = list(tree.iter_inventory_ids(source=True, both=True))
93
 
    >>> ("x_lalala", "path with space") in inv
94
 
    True
95
 
    >>> teardown_environ(q)
96
 
    """
97
 
    file(path, "wb").write(text)
98
 
    add_id([path], id)
99
 
 
100
 
 
101
 
def add_dir(path, id):
102
 
    """
103
 
    >>> q = test_environ()
104
 
    >>> add_dir("path with\(sp) space", "lalala")
105
 
    >>> tree = pybaz.tree_root(".")
106
 
    >>> inv = list(tree.iter_inventory_ids(source=True, both=True))
107
 
    >>> ("x_lalala", "path with\(sp) space") in inv
108
 
    True
109
 
    >>> teardown_environ(q)
110
 
    """
111
 
    os.mkdir(path)
112
 
    add_id([path], id)
113
 
 
114
 
def teardown_environ(tdir):
115
 
    os.chdir(saved_dir)
116
 
    shutil.rmtree(tdir)
117
 
 
118
 
def timport(tree, summary):
119
 
    msg = tree.log_message()
120
 
    msg["summary"] = summary
121
 
    tree.import_(msg)
122
 
 
123
 
def commit(tree, summary):
124
 
    """
125
 
    >>> q = test_environ()
126
 
    >>> tree = pybaz.tree_root(".")
127
 
    >>> timport(tree, "import")
128
 
    >>> commit(tree, "commit")
129
 
    >>> logs = [str(l.revision) for l in tree.iter_logs()]
130
 
    >>> len(logs)
131
 
    2
132
 
    >>> logs[0]
133
 
    'test@example.com/test--test--0--base-0'
134
 
    >>> logs[1]
135
 
    'test@example.com/test--test--0--patch-1'
136
 
    >>> teardown_environ(q)
137
 
    """
138
 
    msg = tree.log_message()
139
 
    msg["summary"] = summary
140
 
    tree.commit(msg)
141
 
 
142
 
def commit_test_revisions():
143
 
    """
144
 
    >>> q = test_environ()
145
 
    >>> commit_test_revisions()
146
 
    >>> a = pybaz.Archive("test@example.com")
147
 
    >>> revisions = list(a.iter_revisions("test--test--0"))
148
 
    >>> len(revisions)
149
 
    3
150
 
    >>> str(revisions[2])
151
 
    'test@example.com/test--test--0--base-0'
152
 
    >>> str(revisions[1])
153
 
    'test@example.com/test--test--0--patch-1'
154
 
    >>> str(revisions[0])
155
 
    'test@example.com/test--test--0--patch-2'
156
 
    >>> teardown_environ(q)
157
 
    """
158
 
    tree = pybaz.tree_root(".")
159
 
    add_file("mainfile", "void main(void){}", "mainfile by aaron")
160
 
    timport(tree, "Created mainfile")
161
 
    file("mainfile", "wb").write("or something like that")
162
 
    commit(tree, "altered mainfile")
163
 
    add_file("ofile", "this is another file", "ofile by aaron")
164
 
    commit(tree, "altered mainfile")
165
 
 
166
 
 
167
 
def commit_more_test_revisions():
168
 
    """
169
 
    >>> q = test_environ()
170
 
    >>> commit_test_revisions()
171
 
    >>> commit_more_test_revisions()
172
 
    >>> a = pybaz.Archive("test@example.com")
173
 
    >>> revisions = list(a.iter_revisions("test--test--0"))
174
 
    >>> len(revisions)
175
 
    4
176
 
    >>> str(revisions[0])
177
 
    'test@example.com/test--test--0--patch-3'
178
 
    >>> teardown_environ(q)
179
 
    """
180
 
    tree = pybaz.tree_root(".")
181
 
    add_file("trainfile", "void train(void){}", "trainfile by aaron")
182
 
    commit(tree, "altered trainfile")
183
 
 
184
 
class NoSuchVersion(Exception):
185
 
    def __init__(self, version):
186
 
        Exception.__init__(self, "The version %s does not exist." % version)
187
 
        self.version = version
188
 
 
189
 
def version_ancestry(version):
190
 
    """
191
 
    >>> q = test_environ()
192
 
    >>> commit_test_revisions()
193
 
    >>> version = pybaz.Version("test@example.com/test--test--0")
194
 
    >>> ancestors = version_ancestry(version)
195
 
    >>> str(ancestors[0])
196
 
    'test@example.com/test--test--0--base-0'
197
 
    >>> str(ancestors[1])
198
 
    'test@example.com/test--test--0--patch-1'
199
 
    >>> version = pybaz.Version("test@example.com/test--test--0.5")
200
 
    >>> ancestors = version_ancestry(version)
201
 
    Traceback (most recent call last):
202
 
    NoSuchVersion: The version test@example.com/test--test--0.5 does not exist.
203
 
    >>> teardown_environ(q)
204
 
    """
205
 
    try:
206
 
        revision = version.iter_revisions(reverse=True).next()
207
 
    except:
208
 
        print version
209
 
        if not version.exists():
210
 
            raise NoSuchVersion(version)
211
 
        else:
212
 
            raise
213
 
    ancestors = list(revision.iter_ancestors(metoo=True))
214
 
    ancestors.reverse()
215
 
    return ancestors
216
 
 
217
 
def get_last_revision(branch):
218
 
    last_patch = branch.last_patch()
219
 
    try:
220
 
        return arch_revision(last_patch)
221
 
    except NotArchRevision:
222
 
        raise UserError(
223
 
            "Directory \"%s\" already exists, and the last revision is not"
224
 
            " an Arch revision (%s)" % (output_dir, last_patch))
225
 
 
226
 
 
227
 
def get_remaining_revisions(output_dir, version):
228
 
    last_patch = None
229
 
    old_revno = None
230
 
    if os.path.exists(output_dir):
231
 
        # We are starting from an existing directory, figure out what
232
 
        # the current version is
233
 
        branch = Branch.open(output_dir)
234
 
        last_patch = get_last_revision(branch)
235
 
        if version is None:
236
 
            version = last_patch.version
237
 
    elif version is None:
238
 
        raise UserError("No version specified, and directory does not exist.")
239
 
 
240
 
    try:
241
 
        ancestors = version_ancestry(version)
242
 
    except NoSuchVersion, e:
243
 
        raise UserError(e)
244
 
 
245
 
    if last_patch:
246
 
        for i in range(len(ancestors)):
247
 
            if ancestors[i] == last_patch:
248
 
                break
249
 
        else:
250
 
            raise UserError("Directory \"%s\" already exists, and the last "
251
 
                "revision (%s) is not in the ancestry of %s" % 
252
 
                (output_dir, last_patch, version))
253
 
        # Strip off all of the ancestors which are already present
254
 
        # And get a directory starting with the latest ancestor
255
 
        latest_ancestor = ancestors[i]
256
 
        old_revno = Branch.open(output_dir).revno()
257
 
        ancestors = ancestors[i+1:]
258
 
    return ancestors, old_revno
259
 
 
260
 
def import_version(output_dir, version, printer, fancy=True, fast=False,
261
 
                   verbose=False, dry_run=False, max_count=None):
262
 
    """
263
 
    >>> q = test_environ()
264
 
    >>> result_path = os.path.join(q, "result")
265
 
    >>> commit_test_revisions()
266
 
    >>> version = pybaz.Version("test@example.com/test--test--0.1")
267
 
    >>> def printer(message): print message
268
 
    >>> import_version('/', version, printer, fancy=False, dry_run=True)
269
 
    Traceback (most recent call last):
270
 
    UserError: / exists, but is not a bzr branch.
271
 
    >>> import_version(result_path, version, printer, fancy=False, dry_run=True)
272
 
    Traceback (most recent call last):
273
 
    UserError: The version test@example.com/test--test--0.1 does not exist.
274
 
    >>> version = pybaz.Version("test@example.com/test--test--0")
275
 
    >>> import_version(result_path, version, printer, fancy=False, dry_run=True)
276
 
    not fancy
277
 
    ....
278
 
    Dry run, not modifying output_dir
279
 
    Cleaning up
280
 
    >>> import_version(result_path, version, printer, fancy=False)
281
 
    not fancy
282
 
    ....
283
 
    Cleaning up
284
 
    Import complete.
285
 
    >>> import_version(result_path, version, printer, fancy=False)
286
 
    Tree is up-to-date with test@example.com/test--test--0--patch-2
287
 
    >>> commit_more_test_revisions()
288
 
    >>> import_version(result_path, version, printer, fancy=False)
289
 
    not fancy
290
 
    ..
291
 
    Cleaning up
292
 
    Import complete.
293
 
    >>> teardown_environ(q)
294
 
    """
295
 
    try:
296
 
        ancestors, old_revno = get_remaining_revisions(output_dir, version)
297
 
    except NotBranchError, e:
298
 
        raise UserError("%s exists, but is not a bzr branch." % output_dir)
299
 
    if len(ancestors) == 0:
300
 
        last_revision = get_last_revision(Branch.open(output_dir))
301
 
        print 'Tree is up-to-date with %s' % last_revision
302
 
        return
303
 
 
304
 
    progress_bar = ProgressBar()
305
 
    tempdir = tempfile.mkdtemp(prefix="baz2bzr-",
306
 
                               dir=os.path.dirname(output_dir))
307
 
    try:
308
 
        if not fancy:
309
 
            print "not fancy"
310
 
        try:
311
 
            for result in iter_import_version(output_dir, ancestors, tempdir,
312
 
                    fast=fast, verbose=verbose, dry_run=dry_run, 
313
 
                    max_count=max_count):
314
 
                if fancy:
315
 
                    show_progress(progress_bar, result)
316
 
                else:
317
 
                    sys.stdout.write('.')
318
 
        finally:
319
 
            if fancy:
320
 
                progress_bar.clear()
321
 
            else:
322
 
                sys.stdout.write('\n')
323
 
 
324
 
        if dry_run:
325
 
            print 'Dry run, not modifying output_dir'
326
 
            return
327
 
        if os.path.exists(output_dir):
328
 
            # Move the bzr control directory back, and update the working tree
329
 
            tmp_bzr_dir = os.path.join(tempdir, '.bzr')
330
 
            
331
 
            bzr_dir = os.path.join(output_dir, '.bzr')
332
 
            new_bzr_dir = os.path.join(tempdir, "rd", '.bzr')
333
 
 
334
 
            os.rename(bzr_dir, tmp_bzr_dir) # Move the original bzr out of the way
335
 
            os.rename(new_bzr_dir, bzr_dir)
336
 
            try:
337
 
                bzrlib.merge.merge((output_dir, -1), (output_dir, None), # old_revno), 
338
 
                                   check_clean=False, this_dir=output_dir, 
339
 
                                   ignore_zero=True)
340
 
            except:
341
 
                # If something failed, move back the original bzr directory
342
 
                os.rename(bzr_dir, new_bzr_dir)
343
 
                os.rename(tmp_bzr_dir, bzr_dir)
344
 
                raise
345
 
        else:
346
 
            revdir = os.path.join(tempdir, "rd")
347
 
            os.rename(revdir, output_dir)
348
 
 
349
 
    finally:
350
 
        printer('Cleaning up')
351
 
        shutil.rmtree(tempdir)
352
 
    printer("Import complete.")
353
 
            
354
 
class UserError(Exception):
355
 
    def __init__(self, message):
356
 
        """Exception to throw when a user makes an impossible request
357
 
        :param message: The message to emit when printing this exception
358
 
        :type message: string
359
 
        """
360
 
        Exception.__init__(self, message)
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-1:you@example.com%cat--br--0--base-0'
372
 
    """
373
 
    return "Arch-1:%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-1:jrandom@example.com%test--test--0"))
384
 
    Traceback (most recent call last):
385
 
    NotArchRevision: The revision id Arch-1:jrandom@example.com%test--test--0 does not look like it came from Arch.
386
 
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--base-5"))
387
 
    Traceback (most recent call last):
388
 
    NotArchRevision: The revision id Arch-1:jrandom@example.com%test--test--0--base-5 does not look like it came from Arch.
389
 
    >>> str(arch_revision("Arch-1: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-1:':
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
 
    revdir = None
405
 
 
406
 
    # Uncomment this for testing, it basically just has baz2bzr only update
407
 
    # 5 patches at a time
408
 
    if max_count:
409
 
        ancestors = ancestors[:max_count]
410
 
 
411
 
    # Not sure if I want this output. basically it tells you ahead of time
412
 
    # what it is going to do, but then later it tells you as it is doing it.
413
 
    # what probably would be best would be to collapse it into ranges, so that
414
 
    # this gives the simple view, and then later it gives the blow by blow.
415
 
    #if verbose:
416
 
    #    print 'Adding the following revisions:'
417
 
    #    for a in ancestors:
418
 
    #        print '\t%s' % a
419
 
 
420
 
    previous_version=None
421
 
    missing_ancestor = None
422
 
 
423
 
    for i in range(len(ancestors)):
424
 
        revision = ancestors[i]
425
 
        direct_merges = []
426
 
        if verbose:
427
 
            version = str(revision.version)
428
 
            if version != previous_version:
429
 
                clear_progress_bar()
430
 
                print '\rOn version: %s' % version
431
 
            yield Progress(str(revision.patchlevel), i, len(ancestors))
432
 
            previous_version = version
433
 
        else:
434
 
            yield Progress("revisions", i, len(ancestors))
435
 
        if revdir is None:
436
 
            revdir = os.path.join(tempdir, "rd")
437
 
            try:
438
 
                baz_inv, log = get_revision(revdir, revision)
439
 
            except pybaz.errors.ExecProblem, e:
440
 
                if ("%s" % e.args).find('could not connect') == -1:
441
 
                    raise
442
 
                missing_ancestor = revision
443
 
                revdir = None
444
 
                print ("unable to access ancestor %s, making into a merge."
445
 
                       % missing_ancestor)
446
 
                continue
447
 
            # cached so we can delete the log
448
 
            log_date = log.date
449
 
            log_summary = log.summary
450
 
            log_creator = log.creator
451
 
            if os.path.exists(output_dir):
452
 
                bzr_dir = os.path.join(output_dir, '.bzr')
453
 
                new_bzr_dir = os.path.join(tempdir, "rd", '.bzr')
454
 
                # This would be much faster with a simple os.rename(), but if
455
 
                # we fail, we have corrupted the original .bzr directory.  Is
456
 
                # that a big problem, as we can just back out the last
457
 
                # revisions in .bzr/revision_history I don't really know
458
 
                shutil.copytree(bzr_dir, new_bzr_dir)
459
 
                # Now revdir should have a tree with the latest .bzr, and the
460
 
                # next revision of the baz tree
461
 
                branch = Branch.open(revdir)
462
 
            else:
463
 
                branch = Branch.initialize(revdir)
464
 
        else:
465
 
            old = os.path.join(revdir, ".bzr")
466
 
            new = os.path.join(tempdir, ".bzr")
467
 
            os.rename(old, new)
468
 
            baz_inv, log = apply_revision(revdir, revision)
469
 
            log_date = log.date
470
 
            log_summary = log.summary
471
 
            log_creator = log.creator
472
 
            direct_merges = get_direct_merges(revdir, revision)
473
 
            os.rename(new, old)
474
 
            branch = Branch.open(revdir)
475
 
        timestamp = email.Utils.mktime_tz(log_date + (0,))
476
 
        rev_id = revision_id(revision)
477
 
        if log_summary is None:
478
 
            log_summary = ""
479
 
        branch.lock_write()
480
 
        try:
481
 
            if missing_ancestor:
482
 
                # if we want it to be in revision-history, do that here.
483
 
                branch.add_pending_merge(revision_id(missing_ancestor))
484
 
                missing_ancestor = None
485
 
            for merge in direct_merges:
486
 
                branch.add_pending_merge(revision_id(merge.revision))
487
 
            branch.set_inventory(baz_inv)
488
 
            bzrlib.trace.silent = True
489
 
            branch.commit(log_summary, verbose=False, committer=log_creator,
490
 
                          timestamp=timestamp, timezone=0, rev_id=rev_id)
491
 
        finally:
492
 
            bzrlib.trace.silent = False   
493
 
            branch.unlock()
494
 
    yield Progress("revisions", len(ancestors), len(ancestors))
495
 
    unlink_unversioned(branch, revdir)
496
 
 
497
 
def get_direct_merges(revdir, revision):
498
 
    if pybaz.WorkingTree(revdir).tree_version != revision.version:
499
 
        pybaz.WorkingTree(revdir).set_tree_version(revision.version)
500
 
    log_path = "%s/{arch}/%s/%s/%s/%s/patch-log/%s" % (revdir, 
501
 
        revision.category.nonarch, revision.branch.nonarch, 
502
 
        revision.version.nonarch, revision.archive, revision.patchlevel)
503
 
    temp_path = tempfile.mktemp()
504
 
    os.rename(log_path, temp_path)
505
 
    merges = list(iter_new_merges(revdir, revision.version))
506
 
    direct = direct_merges (merges)
507
 
    os.rename(temp_path, log_path)
508
 
    return direct
509
 
 
510
 
def unlink_unversioned(branch, revdir):
511
 
    for unversioned in branch.working_tree().extras():
512
 
        path = os.path.join(revdir, unversioned)
513
 
        if os.path.isdir(path):
514
 
            shutil.rmtree(path)
515
 
        else:
516
 
            os.unlink(path)
517
 
 
518
 
def get_log(tree, revision):
519
 
    log = tree.iter_logs(version=revision.version, reverse=True).next()
520
 
    assert str(log.revision) == str(revision), (log.revision, revision)
521
 
    return log
522
 
 
523
 
def get_revision(revdir, revision):
524
 
    revision.get(revdir)
525
 
    tree = pybaz.tree_root(revdir)
526
 
    log = get_log(tree, revision)
527
 
    try:
528
 
        return bzr_inventory_data(tree), log 
529
 
    except BadFileKind, e:
530
 
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
531
 
 
532
 
 
533
 
def apply_revision(revdir, revision):
534
 
    tree = pybaz.tree_root(revdir)
535
 
    revision.apply(tree)
536
 
    log = get_log(tree, revision)
537
 
    try:
538
 
        return bzr_inventory_data(tree), log
539
 
    except BadFileKind, e:
540
 
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
541
 
 
542
 
 
543
 
class BadFileKind(Exception):
544
 
    """The file kind is not permitted in bzr inventories"""
545
 
    def __init__(self, tree_root, path, kind):
546
 
        self.tree_root = tree_root
547
 
        self.path = path
548
 
        self.kind = kind
549
 
        Exception.__init__(self, "File %s is of forbidden type %s" %
550
 
                           (os.path.join(tree_root, path), kind))
551
 
 
552
 
 
553
 
def bzr_inventory_data(tree):
554
 
    inv_iter = tree.iter_inventory_ids(source=True, both=True)
555
 
    inv_map = {}
556
 
    for arch_id, path in inv_iter:
557
 
        bzr_file_id = arch_id.replace('%', '%25').replace('/', '%2f')
558
 
        inv_map[path] = bzr_file_id 
559
 
 
560
 
    bzr_inv = []
561
 
    for path, file_id in inv_map.iteritems():
562
 
        full_path = os.path.join(tree, path)
563
 
        kind = bzrlib.osutils.file_kind(full_path)
564
 
        if kind not in ("file", "directory", "symlink"):
565
 
            raise BadFileKind(tree, path, kind)
566
 
        parent_dir = os.path.dirname(path)
567
 
        if parent_dir != "":
568
 
            parent_id = inv_map[parent_dir]
569
 
        else:
570
 
            parent_id = bzrlib.inventory.ROOT_ID
571
 
        bzr_inv.append((path, file_id, parent_id, kind))
572
 
    bzr_inv.sort()
573
 
    return bzr_inv
574
 
 
575
 
 
576
 
class cmd_baz_import_branch(Command):
577
 
    """Import an Arch or Baz branch into a bzr branch"""
578
 
    takes_args = ['to_location', 'from_branch?']
579
 
    takes_options = ['verbose']
580
 
 
581
 
    def printer(self, name):
582
 
        print name
583
 
 
584
 
    def run(self, to_location, from_branch=None, fast=False, max_count=None,
585
 
            verbose=False, dry_run=False):
586
 
        to_location = os.path.realpath(str(to_location))
587
 
        if from_branch is not None:
588
 
            try:
589
 
                from_branch = pybaz.Version(from_branch)
590
 
            except pybaz.errors.NamespaceError:
591
 
                print "%s is not a valid Arch branch." % from_branch
592
 
                return 1
593
 
        import_version(to_location, from_branch, self.printer)
594
 
 
595
 
 
596
 
class cmd_baz_import(Command):
597
 
    """Import an Arch or Baz archive into bzr branches."""
598
 
    takes_args = ['to_root_dir', 'from_archive']
599
 
    takes_options = ['verbose']
600
 
 
601
 
    def printer(self, name):
602
 
        print name
603
 
 
604
 
    def run(self, to_root_dir, from_archive, verbose=False):
605
 
        to_root = str(os.path.realpath(to_root_dir))
606
 
        if not os.path.exists(to_root):
607
 
            os.mkdir(to_root)
608
 
        import_archive(to_root, from_archive, verbose, self.printer)
609
 
 
610
 
 
611
 
def import_archive(to_root, from_archive, verbose, printer):
612
 
    for version in pybaz.Archive(str(from_archive)).iter_versions():
613
 
        target = os.path.join(to_root, map_namespace(version))
614
 
        printer("importing %s into %s" % (version, target))
615
 
        if not os.path.exists(os.path.dirname(target)):
616
 
            os.makedirs(os.path.dirname(target))
617
 
        import_version(target, version, printer)
618
 
 
619
 
 
620
 
def map_namespace(a_version):
621
 
    a_version = pybaz.Version("%s" % a_version)
622
 
    parser = NameParser(a_version)
623
 
    version = parser.get_version()
624
 
    branch = parser.get_branch()
625
 
    category = parser.get_category()
626
 
    if branch is None or branch == '':
627
 
        branch = "+trunk"
628
 
    if version == '0':
629
 
        return "%s/%s" % (category, branch)
630
 
    return "%s/%s/%s" % (category, version, branch)