~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to baz_import.py

  • Committer: Aaron Bentley
  • Date: 2005-10-18 20:17:59 UTC
  • Revision ID: abentley@panoramicfeedback.com-20051018201759-a5dfcbed504c3d22
Adopted Branch.open_containing's new format

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 by Aaron Bentley
 
1
# Copyright (C) 2005 by Aaron Bentley
2
2
 
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
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.bzrdir import BzrDir
20
 
import bzrlib.bzrdir as bzrdir
21
 
from bzrlib.errors import (BzrError,
22
 
                           NotBranchError,
23
 
                           NoWorkingTree,
24
 
                           BzrCommandError, 
25
 
                           NoSuchRevision,
26
 
                           NoRepositoryPresent,
27
 
                          )
28
16
from bzrlib.branch import Branch
29
 
from bzrlib.commit import Commit, NullCommitReporter
30
17
from bzrlib.commands import Command
31
 
from bzrlib.option import _global_option, Option
32
 
from bzrlib.merge import merge_inner
33
 
from bzrlib.revision import NULL_REVISION
34
 
from bzrlib.tree import EmptyTree
35
 
import bzrlib.ui
36
 
from bzrlib.workingtree import WorkingTree
37
18
from errors import NoPyBaz
38
19
try:
39
20
    import pybaz
40
21
    import pybaz.errors
41
 
    from pybaz import NameParser as NameParser
42
22
    from pybaz.backends.baz import null_cmd
43
23
except ImportError:
44
24
    raise NoPyBaz
45
 
from fai import iter_new_merges, direct_merges
46
25
import tempfile
47
26
import os
48
27
import os.path
49
28
import shutil
50
29
import bzrlib
 
30
from bzrlib.errors import BzrError
51
31
import bzrlib.trace
52
32
import bzrlib.merge
53
33
import bzrlib.inventory
56
36
import email.Utils
57
37
from progress import *
58
38
 
59
 
class ImportCommitReporter(NullCommitReporter):
60
 
    def __init__(self, pb):
61
 
        self.pb = pb
62
 
 
63
 
    def escaped(self, escape_count, message):
64
 
        self.pb.clear()
65
 
        bzrlib.trace.warning("replaced %d control characters in message" %
66
 
                             escape_count)
67
 
 
68
39
def add_id(files, id=None):
69
40
    """Adds an explicit id to a list of files.
70
41
 
79
50
    args.extend(files)
80
51
    return null_cmd(args)
81
52
 
82
 
saved_dir = None
83
 
 
84
 
def make_archive(name, location):
85
 
    pb_location = pybaz.ArchiveLocation(location)
86
 
    pb_location.create_master(pybaz.Archive(name), 
87
 
                              pybaz.ArchiveLocationParams())
88
 
 
89
53
def test_environ():
90
54
    """
91
55
    >>> q = test_environ()
97
61
    >>> os.path.exists(q)
98
62
    False
99
63
    """
100
 
    global saved_dir
101
 
    saved_dir = os.getcwdu()
102
64
    tdir = tempfile.mkdtemp(prefix="testdir-")
103
65
    os.environ["HOME"] = os.path.join(tdir, "home")
104
66
    os.mkdir(os.environ["HOME"])
105
67
    arch_dir = os.path.join(tdir, "archive_dir")
106
 
    make_archive("test@example.com", arch_dir)
 
68
    pybaz.make_archive("test@example.com", arch_dir)
107
69
    work_dir = os.path.join(tdir, "work_dir")
108
70
    os.mkdir(work_dir)
109
71
    os.chdir(work_dir)
142
104
    add_id([path], id)
143
105
 
144
106
def teardown_environ(tdir):
145
 
    os.chdir(saved_dir)
 
107
    os.chdir("/")
146
108
    shutil.rmtree(tdir)
147
109
 
148
110
def timport(tree, summary):
234
196
    """
235
197
    try:
236
198
        revision = version.iter_revisions(reverse=True).next()
237
 
    except StopIteration:
238
 
        return ()
239
199
    except:
240
 
        print version
241
200
        if not version.exists():
242
201
            raise NoSuchVersion(version)
243
202
        else:
253
212
    except NotArchRevision:
254
213
        raise UserError(
255
214
            "Directory \"%s\" already exists, and the last revision is not"
256
 
            " an Arch revision (%s)" % (branch.base, last_patch))
257
 
 
258
 
def do_branch(br_from, to_location, revision_id):
259
 
    """Derived from branch in builtins."""
260
 
    br_from.lock_read()
261
 
    try:
262
 
        try:
263
 
            os.mkdir(to_location)
264
 
        except OSError, e:
265
 
            if e.errno == errno.EEXIST:
266
 
                raise UserError('Target directory "%s" already'
267
 
                                      ' exists.' % to_location)
268
 
            if e.errno == errno.ENOENT:
269
 
                raise UserError('Parent of "%s" does not exist.' %
270
 
                                      to_location)
271
 
            else:
272
 
                raise
273
 
        try:
274
 
            br_from.bzrdir.clone(to_location, revision_id)
275
 
        except NoSuchRevision:
276
 
            rmtree(to_location)
277
 
            msg = "The branch %s has no revision %s." % (from_location, 
278
 
                                                         revision_id)
279
 
            raise UserError(msg)
280
 
    finally:
281
 
        br_from.unlock()
282
 
 
283
 
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):
284
219
    last_patch = None
285
220
    old_revno = None
286
 
    output_exists = os.path.exists(output_dir)
287
 
    if output_exists:
 
221
    if os.path.exists(output_dir):
288
222
        # We are starting from an existing directory, figure out what
289
223
        # the current version is
290
 
        branch = Branch.open(output_dir)
 
224
        branch = find_branch(output_dir)
291
225
        last_patch = get_last_revision(branch)
292
 
        if last_patch is None:
293
 
            raise NotPreviousImport(branch.base)
294
226
        if version is None:
295
227
            version = last_patch.version
296
228
    elif version is None:
298
230
 
299
231
    try:
300
232
        ancestors = version_ancestry(version)
301
 
        if not output_exists and reuse_history_from != []:
302
 
            for ancestor in reversed(ancestors):
303
 
                if last_patch is not None:
304
 
                    # found something to copy
305
 
                    break
306
 
                # try to grab a copy of ancestor
307
 
                # note that is not optimised: we could look for namespace
308
 
                # transitions and only look for the past after the 
309
 
                # transition.
310
 
                for history_root in reuse_history_from:
311
 
                    possible_source = os.path.join(history_root,
312
 
                        map_namespace(ancestor.version))
313
 
                    try:
314
 
                        source = Branch.open(possible_source)
315
 
                        rev_id = revision_id(ancestor)
316
 
                        if rev_id in source.revision_history():
317
 
                            do_branch(source, output_dir, rev_id)
318
 
                            last_patch = ancestor
319
 
                            break
320
 
                    except NotBranchError:
321
 
                        pass
322
233
    except NoSuchVersion, e:
323
 
        raise UserError(str(e))
 
234
        raise UserError(e)
324
235
 
325
236
    if last_patch:
326
237
        for i in range(len(ancestors)):
333
244
        # Strip off all of the ancestors which are already present
334
245
        # And get a directory starting with the latest ancestor
335
246
        latest_ancestor = ancestors[i]
336
 
        old_revno = Branch.open(output_dir).revno()
 
247
        old_revno = find_branch(output_dir).revno()
337
248
        ancestors = ancestors[i+1:]
338
249
    return ancestors, old_revno
339
250
 
340
 
 
341
 
###class Importer(object):
342
 
###    """An importer.
343
 
###    
344
 
###    Currently this is used as a parameter object, though more behaviour is
345
 
###    possible later.
346
 
###    """
347
 
###
348
 
###    def __init__(self, output_dir, version, printer, fancy=True, fast=False,
349
 
###                 verbose=False, dry_run=False, max_count=None, 
350
 
###                   reuse_history_from=[]):
351
 
###        self.output_dir = output_dir
352
 
###        self.version = version
353
 
###        self.
354
 
 
355
 
 
356
 
def import_version(output_dir, version, printer, fancy=True, fast=False,
357
 
                   verbose=False, dry_run=False, max_count=None,
358
 
                   reuse_history_from=[], standalone=True):
 
251
def import_version(output_dir, version, fancy=True, fast=False, verbose=False, 
 
252
                   dry_run=False, max_count=None, skip_symlinks=False):
359
253
    """
360
254
    >>> q = test_environ()
361
255
    >>> result_path = os.path.join(q, "result")
362
256
    >>> commit_test_revisions()
363
257
    >>> version = pybaz.Version("test@example.com/test--test--0.1")
364
 
    >>> def printer(message): print message
365
 
    >>> import_version('/', version, printer, fancy=False, dry_run=True)
 
258
    >>> import_version('/', version, fancy=False, dry_run=True)
366
259
    Traceback (most recent call last):
367
 
    NotPreviousImport: / is not the location of a previous import.
368
 
    >>> import_version(result_path, version, printer, fancy=False, dry_run=True)
 
260
    UserError: / exists, but is not a bzr branch.
 
261
    >>> import_version(result_path, version, fancy=False, dry_run=True)
369
262
    Traceback (most recent call last):
370
263
    UserError: The version test@example.com/test--test--0.1 does not exist.
371
264
    >>> version = pybaz.Version("test@example.com/test--test--0")
372
 
    >>> import_version(result_path, version, printer, fancy=False, dry_run=True)
 
265
    >>> import_version(result_path, version, fancy=False, dry_run=True)
373
266
    not fancy
374
267
    ....
375
268
    Dry run, not modifying output_dir
376
269
    Cleaning up
377
 
    >>> import_version(result_path, version, printer, fancy=False)
 
270
    >>> import_version(result_path, version, fancy=False)
378
271
    not fancy
379
272
    ....
380
273
    Cleaning up
381
274
    Import complete.
382
 
    >>> import_version(result_path, version, printer, fancy=False)
 
275
    >>> import_version(result_path, version, fancy=False)
383
276
    Tree is up-to-date with test@example.com/test--test--0--patch-2
384
277
    >>> commit_more_test_revisions()
385
 
    >>> import_version(result_path, version, printer, fancy=False)
 
278
    >>> import_version(result_path, version, fancy=False)
386
279
    not fancy
387
280
    ..
388
281
    Cleaning up
390
283
    >>> teardown_environ(q)
391
284
    """
392
285
    try:
393
 
        ancestors, old_revno = get_remaining_revisions(output_dir, version,
394
 
                                                       reuse_history_from)
395
 
    except NotBranchError, e:
396
 
        raise NotPreviousImport(e.path)
397
 
    if old_revno is None and len(ancestors) == 0:
398
 
        print 'Version %s has no revisions.' % version
399
 
        return
 
286
        ancestors, old_revno = get_remaining_revisions(output_dir, version)
 
287
    except NotInABranch, e:
 
288
        raise UserError("%s exists, but is not a bzr branch." % e.path)
400
289
    if len(ancestors) == 0:
401
 
        last_revision = get_last_revision(Branch.open(output_dir))
 
290
        last_revision = get_last_revision(find_branch(output_dir))
402
291
        print 'Tree is up-to-date with %s' % last_revision
403
292
        return
404
293
 
405
 
    progress_bar = bzrlib.ui.ui_factory.nested_progress_bar()
 
294
    progress_bar = ProgressBar()
406
295
    tempdir = tempfile.mkdtemp(prefix="baz2bzr-",
407
296
                               dir=os.path.dirname(output_dir))
408
297
    try:
409
 
        wt = WorkingTree.open(output_dir)
410
 
    except (NotBranchError, NoWorkingTree):
411
 
        wt = None
412
 
    if wt is None:
413
 
        old_basis = EmptyTree()
414
 
    else:
415
 
        old_basis = wt.basis_tree()
416
 
    try:
417
298
        if not fancy:
418
299
            print "not fancy"
419
300
        try:
420
301
            for result in iter_import_version(output_dir, ancestors, tempdir,
421
 
                    progress_bar, fast=fast, verbose=verbose, dry_run=dry_run,
422
 
                    max_count=max_count, standalone=standalone):
 
302
                    fast=fast, verbose=verbose, dry_run=dry_run, 
 
303
                    max_count=max_count, skip_symlinks=skip_symlinks):
423
304
                if fancy:
424
305
                    show_progress(progress_bar, result)
425
306
                else:
426
307
                    sys.stdout.write('.')
427
308
        finally:
428
309
            if fancy:
429
 
                progress_bar.finished()
 
310
                progress_bar.clear()
430
311
            else:
431
312
                sys.stdout.write('\n')
432
313
 
433
314
        if dry_run:
434
315
            print 'Dry run, not modifying output_dir'
435
316
            return
 
317
        if os.path.exists(output_dir):
 
318
            # Move the bzr control directory back, and update the working tree
 
319
            tmp_bzr_dir = os.path.join(tempdir, '.bzr')
 
320
            
 
321
            bzr_dir = os.path.join(output_dir, '.bzr')
 
322
            new_bzr_dir = os.path.join(tempdir, "rd", '.bzr')
436
323
 
437
 
        # Update the working tree of the branch
438
 
        try:
439
 
            wt = WorkingTree.open(output_dir)
440
 
        except NoWorkingTree:
441
 
            wt = None
442
 
        if wt is not None:
443
 
            wt.set_last_revision(wt.branch.last_revision())
444
 
            merge_inner(wt.branch, wt.basis_tree(), old_basis, 
445
 
                        ignore_zero=True, this_tree=wt)
446
 
            wt.revert([])
 
324
            os.rename(bzr_dir, tmp_bzr_dir) # Move the original bzr out of the way
 
325
            os.rename(new_bzr_dir, bzr_dir)
 
326
            try:
 
327
                bzrlib.merge.merge((output_dir, -1), (output_dir, old_revno), 
 
328
                                   check_clean=False, this_dir=output_dir, 
 
329
                                   ignore_zero=True)
 
330
            except:
 
331
                # If something failed, move back the original bzr directory
 
332
                os.rename(bzr_dir, new_bzr_dir)
 
333
                os.rename(tmp_bzr_dir, bzr_dir)
 
334
                raise
 
335
        else:
 
336
            revdir = os.path.join(tempdir, "rd")
 
337
            os.rename(revdir, output_dir)
447
338
 
448
339
    finally:
449
 
        printer('Cleaning up')
 
340
        print 'Cleaning up'
450
341
        shutil.rmtree(tempdir)
451
 
    printer("Import complete.")
 
342
    print "Import complete."
452
343
            
453
 
class UserError(BzrCommandError):
 
344
class UserError(Exception):
454
345
    def __init__(self, message):
455
346
        """Exception to throw when a user makes an impossible request
456
347
        :param message: The message to emit when printing this exception
457
348
        :type message: string
458
349
        """
459
 
        BzrCommandError.__init__(self, message)
460
 
 
461
 
class NotPreviousImport(UserError):
462
 
    def __init__(self, path):
463
 
        UserError.__init__(self, "%s is not the location of a previous import."
464
 
                           % path)
465
 
 
 
350
        Exception.__init__(self, message)
466
351
 
467
352
def revision_id(arch_revision):
468
353
    """
473
358
    :param arch_revision: The Arch revision to generate an ID for.
474
359
 
475
360
    >>> revision_id(pybaz.Revision("you@example.com/cat--br--0--base-0"))
476
 
    'Arch-1:you@example.com%cat--br--0--base-0'
 
361
    'Arch-x:you@example.com%cat--br--0--base-0'
477
362
    """
478
 
    return "Arch-1:%s" % str(arch_revision).replace('/', '%')
 
363
    return "Arch-x:%s" % str(arch_revision).replace('/', '%')
479
364
 
480
365
class NotArchRevision(Exception):
481
366
    def __init__(self, revision_id):
485
370
 
486
371
def arch_revision(revision_id):
487
372
    """
488
 
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0"))
489
 
    Traceback (most recent call last):
490
 
    NotArchRevision: The revision id Arch-1:jrandom@example.com%test--test--0 does not look like it came from Arch.
491
 
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--base-5"))
492
 
    Traceback (most recent call last):
493
 
    NotArchRevision: The revision id Arch-1:jrandom@example.com%test--test--0--base-5 does not look like it came from Arch.
494
 
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--patch-5"))
 
373
    >>> str(arch_revision("Arch-x:jrandom@example.com%test--test--0"))
 
374
    Traceback (most recent call last):
 
375
    NotArchRevision: The revision id Arch-x:jrandom@example.com%test--test--0 does not look like it came from Arch.
 
376
    >>> str(arch_revision("Arch-x:jrandom@example.com%test--test--0--base-5"))
 
377
    Traceback (most recent call last):
 
378
    NotArchRevision: The revision id Arch-x:jrandom@example.com%test--test--0--base-5 does not look like it came from Arch.
 
379
    >>> str(arch_revision("Arch-x:jrandom@example.com%test--test--0--patch-5"))
495
380
    'jrandom@example.com/test--test--0--patch-5'
496
381
    """
497
382
    if revision_id is None:
498
383
        return None
499
 
    if revision_id[:7] != 'Arch-1:':
 
384
    if revision_id[:7] != 'Arch-x:':
500
385
        raise NotArchRevision(revision_id)
501
386
    else:
502
387
        try:
503
388
            return pybaz.Revision(revision_id[7:].replace('%', '/'))
504
389
        except pybaz.errors.NamespaceError, e:
505
390
            raise NotArchRevision(revision_id)
506
 
 
507
 
 
508
 
def create_shared_repository(output_dir):
509
 
    bd = bzrdir.BzrDirMetaFormat1().initialize(output_dir)
510
 
    bd.create_repository(shared=True)
511
 
 
512
 
def create_branch(output_dir):
513
 
    os.mkdir(output_dir)
514
 
    bd = bzrdir.BzrDirMetaFormat1().initialize(output_dir)
515
 
    return bd.create_branch()
516
 
 
517
 
 
518
 
def create_checkout(source, to_location, revision_id=None):
519
 
    checkout = bzrdir.BzrDirMetaFormat1().initialize(to_location)
520
 
    bzrlib.branch.BranchReferenceFormat().initialize(checkout, source)
521
 
    return checkout.create_workingtree(revision_id)
522
 
 
523
 
 
524
 
def create_checkout_metadata(source, to_location, revision_id=None):
525
 
    if revision_id is None:
526
 
        revision_id = source.last_revision()
527
 
    wt = create_checkout(source, to_location, NULL_REVISION)
528
 
    wt.set_last_revision(revision_id)
529
 
    wt._write_inventory(wt.basis_tree().inventory)
530
 
    return wt
531
 
 
532
 
 
533
 
def iter_import_version(output_dir, ancestors, tempdir, pb, fast=False,
 
391
            
 
392
def iter_import_version(output_dir, ancestors, tempdir, fast=False,
534
393
                        verbose=False, dry_run=False, max_count=None,
535
 
                        standalone=False):
 
394
                        skip_symlinks=False):
536
395
    revdir = None
537
396
 
538
397
    # Uncomment this for testing, it basically just has baz2bzr only update
550
409
    #        print '\t%s' % a
551
410
 
552
411
    previous_version=None
553
 
    missing_ancestor = None
554
 
    if dry_run:
555
 
        dry_output_dir = os.path.join(tempdir, 'od')
556
 
        if os.path.exists(output_dir):
557
 
            shutil.copytree(output_dir, dry_output_dir)
558
 
        output_dir = dry_output_dir
559
 
 
560
 
    if os.path.exists(output_dir):
561
 
        target_branch = Branch.open(output_dir)
562
 
    else:
563
 
        if standalone:
564
 
            wt = BzrDir.create_standalone_workingtree(output_dir)
565
 
            target_branch = wt.branch
566
 
        else:
567
 
            target_branch = create_branch(output_dir)
568
412
 
569
413
    for i in range(len(ancestors)):
570
414
        revision = ancestors[i]
571
 
        rev_id = revision_id(revision)
572
 
        direct_merges = []
573
415
        if verbose:
574
416
            version = str(revision.version)
575
417
            if version != previous_version:
579
421
            previous_version = version
580
422
        else:
581
423
            yield Progress("revisions", i, len(ancestors))
582
 
 
583
 
        if target_branch.repository.has_revision(rev_id):
584
 
            target_branch.append_revision(rev_id)
585
 
            continue
586
424
        if revdir is None:
587
425
            revdir = os.path.join(tempdir, "rd")
588
 
            try:
589
 
                tree, baz_inv, log = get_revision(revdir, revision)
590
 
            except pybaz.errors.ExecProblem, e:
591
 
                if ("%s" % e.args).find('could not connect') == -1:
592
 
                    raise
593
 
                missing_ancestor = revision
594
 
                revdir = None
595
 
                print ("unable to access ancestor %s, making into a merge."
596
 
                       % missing_ancestor)
597
 
                continue
598
 
            target_tree = create_checkout_metadata(target_branch, revdir)
599
 
            branch = target_tree.branch
 
426
            baz_inv, log = get_revision(revdir, revision, 
 
427
                                        skip_symlinks=skip_symlinks)
 
428
            if os.path.exists(output_dir):
 
429
                bzr_dir = os.path.join(output_dir, '.bzr')
 
430
                new_bzr_dir = os.path.join(tempdir, "rd", '.bzr')
 
431
                # This would be much faster with a simple os.rename(), but if
 
432
                # we fail, we have corrupted the original .bzr directory.  Is
 
433
                # that a big problem, as we can just back out the last
 
434
                # revisions in .bzr/revision_history I don't really know
 
435
                shutil.copytree(bzr_dir, new_bzr_dir)
 
436
                # Now revdir should have a tree with the latest .bzr, and the
 
437
                # next revision of the baz tree
 
438
                branch = find_branch(revdir)
 
439
            else:
 
440
                branch = Branch.initialize(revdir)
600
441
        else:
601
442
            old = os.path.join(revdir, ".bzr")
602
443
            new = os.path.join(tempdir, ".bzr")
603
444
            os.rename(old, new)
604
 
            baz_inv, log = apply_revision(tree, revision)
 
445
            baz_inv, log = apply_revision(revdir, revision, 
 
446
                                          skip_symlinks=skip_symlinks)
605
447
            os.rename(new, old)
606
 
            target_tree = WorkingTree.open(revdir)
607
 
            branch = target_tree.branch
608
 
        # cached so we can delete the log
609
 
        log_date = log.date
610
 
        log_summary = log.summary
611
 
        log_description = log.description
612
 
        is_continuation = log.continuation_of is not None
613
 
        log_creator = log.creator
614
 
        direct_merges = get_direct_merges(revdir, revision, log)
615
 
 
616
 
        timestamp = email.Utils.mktime_tz(log_date + (0,))
617
 
        if log_summary is None:
618
 
            log_summary = ""
619
 
        # log_descriptions of None and "" are ignored.
620
 
        if not is_continuation and log_description:
621
 
            log_message = "\n".join((log_summary, log_description))
622
 
        else:
623
 
            log_message = log_summary
624
 
        target_tree.lock_write()
 
448
            branch = find_branch(revdir)
 
449
        timestamp = email.Utils.mktime_tz(log.date + (0,))
 
450
        rev_id = revision_id(revision)
625
451
        branch.lock_write()
626
452
        try:
627
 
            if missing_ancestor:
628
 
                # if we want it to be in revision-history, do that here.
629
 
                target_tree.add_pending_merge(revision_id(missing_ancestor))
630
 
                missing_ancestor = None
631
 
            for merged_rev in direct_merges:
632
 
                target_tree.add_pending_merge(revision_id(merged_rev))
633
 
            target_tree.set_inventory(baz_inv)
634
 
            commitobj = Commit(reporter=ImportCommitReporter(pb))
635
 
            commitobj.commit(working_tree=target_tree,
636
 
                             message=log_message.decode('ascii', 'replace'), 
637
 
                             verbose=False, committer=log_creator,
638
 
                             timestamp=timestamp, timezone=0, rev_id=rev_id,
639
 
                             revprops={})
 
453
            branch.set_inventory(baz_inv)
 
454
            bzrlib.trace.silent = True
 
455
            branch.commit(log.summary, verbose=False, committer=log.creator,
 
456
                          timestamp=timestamp, timezone=0, rev_id=rev_id)
640
457
        finally:
641
 
            target_tree.unlock()
 
458
            bzrlib.trace.silent = False   
642
459
            branch.unlock()
643
460
    yield Progress("revisions", len(ancestors), len(ancestors))
644
 
 
645
 
def get_direct_merges(revdir, revision, log):
646
 
    continuation = log.continuation_of
647
 
    previous_version = revision.version
648
 
    if pybaz.WorkingTree(revdir).tree_version != previous_version:
649
 
        pybaz.WorkingTree(revdir).set_tree_version(previous_version)
650
 
    log_path = "%s/{arch}/%s/%s/%s/%s/patch-log/%s" % (revdir, 
651
 
        revision.category.nonarch, revision.branch.nonarch, 
652
 
        revision.version.nonarch, revision.archive, revision.patchlevel)
653
 
    temp_path = tempfile.mktemp(dir=os.path.dirname(revdir))
654
 
    os.rename(log_path, temp_path)
655
 
    merges = list(iter_new_merges(revdir, revision.version))
656
 
    direct = direct_merges(merges, [continuation])
657
 
    os.rename(temp_path, log_path)
658
 
    return direct
659
 
 
660
 
def unlink_unversioned(wt):
661
 
    for unversioned in wt.extras():
662
 
        path = wt.abspath(unversioned)
 
461
    unlink_unversioned(branch, revdir)
 
462
 
 
463
def unlink_unversioned(branch, revdir):
 
464
    for unversioned in branch.working_tree().extras():
 
465
        path = os.path.join(revdir, unversioned)
663
466
        if os.path.isdir(path):
664
467
            shutil.rmtree(path)
665
468
        else:
666
469
            os.unlink(path)
667
470
 
668
471
def get_log(tree, revision):
669
 
    log = pybaz.Patchlog(revision, tree=tree)
 
472
    log = tree.iter_logs(version=revision.version, reverse=True).next()
670
473
    assert str(log.revision) == str(revision), (log.revision, revision)
671
474
    return log
672
475
 
673
 
def get_revision(revdir, revision):
674
 
    tree = revision.get(revdir)
 
476
def get_revision(revdir, revision, skip_symlinks=False):
 
477
    revision.get(revdir)
 
478
    tree = pybaz.tree_root(revdir)
675
479
    log = get_log(tree, revision)
676
480
    try:
677
 
        return tree, bzr_inventory_data(tree), log 
 
481
        return bzr_inventory_data(tree, skip_symlinks=skip_symlinks), log 
678
482
    except BadFileKind, e:
679
 
        raise UserError("Cannot convert %s because %s is a %s" % 
680
 
                        (revision,e.path, e.kind))
681
 
 
682
 
 
683
 
def apply_revision(tree, revision):
 
483
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
 
484
 
 
485
 
 
486
def apply_revision(revdir, revision, skip_symlinks=False):
 
487
    tree = pybaz.tree_root(revdir)
684
488
    revision.apply(tree)
685
489
    log = get_log(tree, revision)
686
490
    try:
687
 
        return bzr_inventory_data(tree), log
 
491
        return bzr_inventory_data(tree, skip_symlinks=skip_symlinks), log
688
492
    except BadFileKind, e:
689
 
        raise UserError("Cannot convert %s because %s is a %s" % 
690
 
                        (revision,e.path, e.kind))
 
493
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
 
494
 
 
495
 
691
496
 
692
497
 
693
498
class BadFileKind(Exception):
699
504
        Exception.__init__(self, "File %s is of forbidden type %s" %
700
505
                           (os.path.join(tree_root, path), kind))
701
506
 
702
 
 
703
 
def bzr_inventory_data(tree):
 
507
def bzr_inventory_data(tree, skip_symlinks=False):
704
508
    inv_iter = tree.iter_inventory_ids(source=True, both=True)
705
509
    inv_map = {}
706
510
    for arch_id, path in inv_iter:
707
 
        bzr_file_id = map_file_id(arch_id)
 
511
        bzr_file_id = arch_id.replace('%', '%25').replace('/', '%2f')
708
512
        inv_map[path] = bzr_file_id 
709
513
 
710
514
    bzr_inv = []
711
515
    for path, file_id in inv_map.iteritems():
712
516
        full_path = os.path.join(tree, path)
713
517
        kind = bzrlib.osutils.file_kind(full_path)
714
 
        if kind not in ("file", "directory", "symlink"):
 
518
        if skip_symlinks and kind == "symlink":
 
519
            continue
 
520
        if kind not in ("file", "directory"):
715
521
            raise BadFileKind(tree, path, kind)
716
522
        parent_dir = os.path.dirname(path)
717
523
        if parent_dir != "":
722
528
    bzr_inv.sort()
723
529
    return bzr_inv
724
530
 
725
 
_global_option('max-count', type = int)
726
 
class cmd_baz_import_branch(Command):
727
 
    """Import an Arch or Baz branch into a bzr branch.  <BZRTOOLS>"""
728
 
    takes_args = ['to_location', 'from_branch?', 'reuse_history*']
729
 
    takes_options = ['verbose', 'max-count']
730
 
 
731
 
    def printer(self, name):
732
 
        print name
733
 
 
734
 
    def run(self, to_location, from_branch=None, fast=False, max_count=None,
735
 
            verbose=False, dry_run=False, reuse_history_list=[]):
 
531
class NotInABranch(Exception):
 
532
    def __init__(self, path):
 
533
        Exception.__init__(self, "%s is not in a branch." % path)
 
534
        self.path = path
 
535
 
 
536
 
 
537
def find_branch(path):
 
538
    """
 
539
    >>> find_branch('/')
 
540
    Traceback (most recent call last):
 
541
    NotInABranch: / is not in a branch.
 
542
    >>> sb = bzrlib.ScratchBranch()
 
543
    >>> isinstance(find_branch(sb.base), Branch)
 
544
    True
 
545
    """
 
546
    try:
 
547
        return Branch.open(path)
 
548
    except BzrError, e:
 
549
        if e.args[0].endswith("' is not in a branch"):
 
550
            raise NotInABranch(path)
 
551
 
 
552
class cmd_baz_import(Command):
 
553
    """Import an Arch or Baz branch into a bzr branch"""
 
554
    takes_args = ['to_location', 'from_branch?']
 
555
    takes_options = ['verbose']
 
556
 
 
557
    def run(self, to_location, from_branch=None, skip_symlinks=False, 
 
558
            fast=False, max_count=None, verbose=False, dry_run=False):
736
559
        to_location = os.path.realpath(str(to_location))
737
560
        if from_branch is not None:
738
561
            try:
740
563
            except pybaz.errors.NamespaceError:
741
564
                print "%s is not a valid Arch branch." % from_branch
742
565
                return 1
743
 
        if reuse_history_list is None:
744
 
            reuse_history_list = []
745
 
        import_version(to_location, from_branch, self.printer, 
746
 
                       max_count=max_count, 
747
 
                       reuse_history_from=reuse_history_list)
748
 
 
749
 
 
750
 
class NotInABranch(Exception):
751
 
    def __init__(self, path):
752
 
        Exception.__init__(self, "%s is not in a branch." % path)
753
 
        self.path = path
754
 
 
755
 
 
756
 
class cmd_baz_import(Command):
757
 
    """Import an Arch or Baz archive into bzr branches.  <BZRTOOLS>
758
 
 
759
 
    This command should be used on local archives (or mirrors) only.  It is
760
 
    quite slow on remote archives.
761
 
    
762
 
    reuse_history allows you to specify any previous imports you 
763
 
    have done of different archives, which this archive has branches
764
 
    tagged from. This will dramatically reduce the time to convert 
765
 
    the archive as it will not have to convert the history already
766
 
    converted in that other branch.
767
 
 
768
 
    If you specify prefixes, only branches whose names start with that prefix
769
 
    will be imported.  Skipped branches will be listed, so you can import any
770
 
    branches you missed by accident.  Here's an example of doing a partial
771
 
    import from thelove@canonical.com:
772
 
    bzr baz-import thelove thelove@canonical.com --prefixes dists:talloc-except
773
 
    """
774
 
    takes_args = ['to_root_dir', 'from_archive', 'reuse_history*']
775
 
    takes_options = ['verbose', Option('prefixes', type=str,
776
 
                     help="Prefixes of branches to import, colon-separated")]
777
 
 
778
 
    def printer(self, name):
779
 
        print name
780
 
 
781
 
    def run(self, to_root_dir, from_archive, verbose=False,
782
 
            reuse_history_list=[], prefixes=None):
783
 
        if reuse_history_list is None:
784
 
            reuse_history_list = []
785
 
        to_root = str(os.path.realpath(to_root_dir))
786
 
        if not os.path.exists(to_root):
787
 
            os.mkdir(to_root)
788
 
        if prefixes is not None:
789
 
            prefixes = prefixes.split(':')
790
 
        import_archive(to_root, from_archive, verbose, self.printer, 
791
 
                       reuse_history_list, prefixes=prefixes)
792
 
 
793
 
 
794
 
def import_archive(to_root, from_archive, verbose, printer,
795
 
                   reuse_history_from=[], standalone=False,
796
 
                   prefixes=None):
797
 
    def selected(version):
798
 
        if prefixes is None:
799
 
            return True
800
 
        else:
801
 
            for prefix in prefixes:
802
 
                if version.nonarch.startswith(prefix):
803
 
                    return True
804
 
            return False
805
 
    real_to = os.path.realpath(to_root)
806
 
    history_locations = [real_to] + reuse_history_from
807
 
    if standalone is False:
808
 
        try:
809
 
            bd = BzrDir.open(to_root)
810
 
            bd.find_repository()
811
 
        except NotBranchError:
812
 
            create_shared_repository(to_root)
813
 
        except NoRepositoryPresent:
814
 
            raise BzrCommandError("Can't create repository at existing branch.")
815
 
    versions = list(pybaz.Archive(str(from_archive)).iter_versions())
816
 
#    progress_bar = bzrlib.ui.ui_factory.nested_progress_bar()
817
 
    try:
818
 
        for num, version in enumerate(versions):
819
 
#            progress_bar.update("Branch", num, len(versions))
820
 
            if not selected(version):
821
 
                print "Skipping %s" % version
822
 
                continue
823
 
            target = os.path.join(to_root, map_namespace(version))
824
 
            printer("importing %s into %s" % (version, target))
825
 
            if not os.path.exists(os.path.dirname(target)):
826
 
                os.makedirs(os.path.dirname(target))
827
 
            try:
828
 
                import_version(target, version, printer,
829
 
                               reuse_history_from=reuse_history_from, 
830
 
                               standalone=standalone)
831
 
            except pybaz.errors.ExecProblem,e:
832
 
                if str(e).find('The requested revision cannot be built.') != -1:
833
 
                    printer("Skipping version %s as it cannot be built due"
834
 
                            " to a missing parent archive." % version)
835
 
                else:
836
 
                    raise
837
 
            except UserError, e:
838
 
                if str(e).find('already exists, and the last revision ') != -1:
839
 
                    printer("Skipping version %s as it has had commits made"
840
 
                            " since it was converted to bzr." % version)
841
 
                else:
842
 
                    raise
843
 
    finally:
844
 
        pass
845
 
#        progress_bar.finished()
846
 
 
847
 
def map_namespace(a_version):
848
 
    a_version = pybaz.Version("%s" % a_version)
849
 
    parser = NameParser(a_version)
850
 
    version = parser.get_version()
851
 
    branch = parser.get_branch()
852
 
    category = parser.get_category()
853
 
    if branch is None or branch == '':
854
 
        branch = "+trunk"
855
 
    if version == '0':
856
 
        return "%s/%s" % (category, branch)
857
 
    return "%s/%s/%s" % (category, version, branch)
858
 
 
859
 
def map_file_id(file_id):
860
 
    """Convert a baz file id to a bzr one."""
861
 
    return file_id.replace('%', '%25').replace('/', '%2f')
 
566
        import_version(to_location, from_branch)