~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to baz_import.py

  • Committer: Robert Collins
  • Date: 2005-09-13 12:39:26 UTC
  • mto: (147.2.6) (364.1.3 bzrtools)
  • mto: This revision was merged to the branch mainline in revision 324.
  • Revision ID: robertc@robertcollins.net-20050913123926-b72242bdacc1ae52
create the output directory

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