~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to baz_import.py

  • Committer: Robert Collins
  • Date: 2005-09-14 08:04:32 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-20050914080431-dd50e57903065542
bugfix - baz-import-branch was broken

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
42
25
    from pybaz.backends.baz import null_cmd
43
26
except ImportError:
44
27
    raise NoPyBaz
45
 
from fai import iter_new_merges, direct_merges
46
28
import tempfile
47
29
import os
48
30
import os.path
49
31
import shutil
50
32
import bzrlib
 
33
from bzrlib.errors import BzrError
51
34
import bzrlib.trace
52
35
import bzrlib.merge
53
36
import bzrlib.inventory
56
39
import email.Utils
57
40
from progress import *
58
41
 
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
42
def add_id(files, id=None):
70
43
    """Adds an explicit id to a list of files.
71
44
 
82
55
 
83
56
saved_dir = None
84
57
 
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
58
def test_environ():
91
59
    """
92
60
    >>> q = test_environ()
104
72
    os.environ["HOME"] = os.path.join(tdir, "home")
105
73
    os.mkdir(os.environ["HOME"])
106
74
    arch_dir = os.path.join(tdir, "archive_dir")
107
 
    make_archive("test@example.com", arch_dir)
 
75
    pybaz.make_archive("test@example.com", arch_dir)
108
76
    work_dir = os.path.join(tdir, "work_dir")
109
77
    os.mkdir(work_dir)
110
78
    os.chdir(work_dir)
235
203
    """
236
204
    try:
237
205
        revision = version.iter_revisions(reverse=True).next()
238
 
    except StopIteration:
239
 
        return ()
240
206
    except:
241
207
        print version
242
208
        if not version.exists():
248
214
    return ancestors
249
215
 
250
216
def get_last_revision(branch):
251
 
    last_patch = branch.last_revision()
 
217
    last_patch = branch.last_patch()
252
218
    try:
253
219
        return arch_revision(last_patch)
254
220
    except NotArchRevision:
255
221
        raise UserError(
256
222
            "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=[]):
 
223
            " an Arch revision (%s)" % (output_dir, last_patch))
 
224
 
 
225
 
 
226
def get_remaining_revisions(output_dir, version):
286
227
    last_patch = None
287
228
    old_revno = None
288
 
    output_exists = os.path.exists(output_dir)
289
 
    if output_exists:
 
229
    if os.path.exists(output_dir):
290
230
        # We are starting from an existing directory, figure out what
291
231
        # 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:
 
232
        branch = find_branch(output_dir, find_root=False)
 
233
        last_patch = get_last_revision(branch)
 
234
        if version is None:
299
235
            version = last_patch.version
300
236
    elif version is None:
301
237
        raise UserError("No version specified, and directory does not exist.")
302
238
 
303
239
    try:
304
240
        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
241
    except NoSuchVersion, e:
327
 
        raise UserError(str(e))
 
242
        raise UserError(e)
328
243
 
329
244
    if last_patch:
330
245
        for i in range(len(ancestors)):
337
252
        # Strip off all of the ancestors which are already present
338
253
        # And get a directory starting with the latest ancestor
339
254
        latest_ancestor = ancestors[i]
340
 
        old_revno = Branch.open(output_dir).revno()
 
255
        old_revno = find_branch(output_dir, find_root=False).revno()
341
256
        ancestors = ancestors[i+1:]
342
257
    return ancestors, old_revno
343
258
 
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):
 
259
def import_version(output_dir, version, printer, fancy=True, fast=False,
 
260
                   verbose=False, dry_run=False, max_count=None):
363
261
    """
364
262
    >>> 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
263
    >>> result_path = os.path.join(q, "result")
372
264
    >>> commit_test_revisions()
373
265
    >>> 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)
 
266
    >>> def printer(message): print message
 
267
    >>> import_version('/', version, printer, fancy=False, dry_run=True)
379
268
    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)
 
269
    UserError: / exists, but is not a bzr branch.
 
270
    >>> import_version(result_path, version, printer, fancy=False, dry_run=True)
382
271
    Traceback (most recent call last):
383
272
    UserError: The version test@example.com/test--test--0.1 does not exist.
384
273
    >>> 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: ..........................................
 
274
    >>> import_version(result_path, version, printer, fancy=False, dry_run=True)
 
275
    not fancy
 
276
    ....
389
277
    Dry run, not modifying output_dir
390
278
    Cleaning up
391
 
    >>> import_version(result_path, version, None) #doctest: +ELLIPSIS
392
 
    importing test@example.com/test--test--0 into ...
393
 
    ...
394
 
    revisions: .....................................................................
 
279
    >>> import_version(result_path, version, printer, fancy=False)
 
280
    not fancy
 
281
    ....
395
282
    Cleaning up
396
283
    Import complete.
397
 
    >>> import_version(result_path, version, None) #doctest: +ELLIPSIS
 
284
    >>> import_version(result_path, version, printer, fancy=False)
398
285
    Tree is up-to-date with test@example.com/test--test--0--patch-2
399
286
    >>> commit_more_test_revisions()
400
 
    >>> import_version(result_path, version, None) #doctest: +ELLIPSIS
401
 
    importing test@example.com/test--test--0 into ...
402
 
    revisions: ....................................................
 
287
    >>> import_version(result_path, version, printer, fancy=False)
 
288
    not fancy
 
289
    ..
403
290
    Cleaning up
404
291
    Import complete.
405
 
    >>> bzrlib.ui.ui_factory = old_ui
406
 
    >>> sys.stderr = old_stderr
407
292
    >>> teardown_environ(q)
408
293
    """
409
 
    progress_bar = bzrlib.ui.ui_factory.nested_progress_bar()
410
294
    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
 
295
        ancestors, old_revno = get_remaining_revisions(output_dir, version)
 
296
    except NotBranchError, e:
 
297
        raise UserError("%s exists, but is not a bzr branch." % output_dir)
 
298
    if len(ancestors) == 0:
 
299
        last_revision = get_last_revision(find_branch(output_dir, find_root=False))
 
300
        print 'Tree is up-to-date with %s' % last_revision
 
301
        return
425
302
 
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
 
303
    progress_bar = ProgressBar()
 
304
    tempdir = tempfile.mkdtemp(prefix="baz2bzr-",
 
305
                               dir=os.path.dirname(output_dir))
 
306
    try:
 
307
        if not fancy:
 
308
            print "not fancy"
434
309
        try:
435
310
            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
 
311
                    fast=fast, verbose=verbose, dry_run=dry_run, 
 
312
                    max_count=max_count):
 
313
                if fancy:
 
314
                    show_progress(progress_bar, result)
 
315
                else:
 
316
                    sys.stdout.write('.')
 
317
        finally:
 
318
            if fancy:
 
319
                progress_bar.clear()
 
320
            else:
 
321
                sys.stdout.write('\n')
 
322
 
 
323
        if dry_run:
 
324
            print 'Dry run, not modifying output_dir'
 
325
            return
 
326
        if os.path.exists(output_dir):
 
327
            # Move the bzr control directory back, and update the working tree
 
328
            tmp_bzr_dir = os.path.join(tempdir, '.bzr')
 
329
            
 
330
            bzr_dir = os.path.join(output_dir, '.bzr')
 
331
            new_bzr_dir = os.path.join(tempdir, "rd", '.bzr')
 
332
 
 
333
            os.rename(bzr_dir, tmp_bzr_dir) # Move the original bzr out of the way
 
334
            os.rename(new_bzr_dir, bzr_dir)
445
335
            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.")
 
336
                bzrlib.merge.merge((output_dir, -1), (output_dir, old_revno), 
 
337
                                   check_clean=False, this_dir=output_dir, 
 
338
                                   ignore_zero=True)
 
339
            except:
 
340
                # If something failed, move back the original bzr directory
 
341
                os.rename(bzr_dir, new_bzr_dir)
 
342
                os.rename(tmp_bzr_dir, bzr_dir)
 
343
                raise
 
344
        else:
 
345
            revdir = os.path.join(tempdir, "rd")
 
346
            os.rename(revdir, output_dir)
 
347
 
459
348
    finally:
460
 
        progress_bar.finished()
 
349
        printer('Cleaning up')
 
350
        shutil.rmtree(tempdir)
 
351
    printer("Import complete.")
461
352
            
462
 
class UserError(BzrCommandError):
 
353
class UserError(Exception):
463
354
    def __init__(self, message):
464
355
        """Exception to throw when a user makes an impossible request
465
356
        :param message: The message to emit when printing this exception
466
357
        :type message: string
467
358
        """
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):
 
359
        Exception.__init__(self, message)
 
360
 
 
361
def revision_id(arch_revision):
477
362
    """
478
363
    Generate a Bzr revision id from an Arch revision id.  'x' in the id
479
364
    designates a revision imported with an experimental algorithm.  A number
481
366
 
482
367
    :param arch_revision: The Arch revision to generate an ID for.
483
368
 
484
 
    >>> revision_id(pybaz.Revision("you@example.com/cat--br--0--base-0"), None)
 
369
    >>> revision_id(pybaz.Revision("you@example.com/cat--br--0--base-0"))
485
370
    '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
371
    """
489
 
    if encoding is None:
490
 
        encoding = ''
491
 
    else:
492
 
        encoding = '-' + encoding
493
 
    return "Arch-1%s:%s" % (encoding, str(arch_revision).replace('/', '%'))
 
372
    return "Arch-1:%s" % str(arch_revision).replace('/', '%')
494
373
 
495
374
class NotArchRevision(Exception):
496
375
    def __init__(self, revision_id):
506
385
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--base-5"))
507
386
    Traceback (most recent call last):
508
387
    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'
 
388
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--patch-5"))
 
389
    'jrandom@example.com/test--test--0--patch-5'
517
390
    """
518
391
    if revision_id is None:
519
 
        return None, None
520
 
    if revision_id[:7] not in ('Arch-1:', 'Arch-1-'):
 
392
        return None
 
393
    if revision_id[:7] != 'Arch-1:':
521
394
        raise NotArchRevision(revision_id)
522
395
    else:
523
396
        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
 
397
            return pybaz.Revision(revision_id[7:].replace('%', '/'))
531
398
        except pybaz.errors.NamespaceError, e:
532
399
            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.set_last_revision(revision_id)
556
 
    if revision_id not in (NULL_REVISION, None):
557
 
        wt._write_inventory(wt.basis_tree().inventory)
558
 
    return wt
559
 
 
560
 
 
561
 
def iter_import_version(output_dir, ancestors, tempdir, pb, encoding, 
562
 
                        fast=False, verbose=False, dry_run=False,
563
 
                        max_count=None, standalone=False):
 
400
            
 
401
def iter_import_version(output_dir, ancestors, tempdir, fast=False,
 
402
                        verbose=False, dry_run=False, max_count=None):
564
403
    revdir = None
565
 
    log_encoding = 'ascii'
566
 
    if encoding is not None:
567
 
        log_encoding = encoding
568
404
 
569
405
    # Uncomment this for testing, it basically just has baz2bzr only update
570
406
    # 5 patches at a time
582
418
 
583
419
    previous_version=None
584
420
    missing_ancestor = None
585
 
    if dry_run:
586
 
        dry_output_dir = os.path.join(tempdir, 'od')
587
 
        if os.path.exists(output_dir):
588
 
            shutil.copytree(output_dir, dry_output_dir)
589
 
        output_dir = dry_output_dir
590
 
 
591
 
    if os.path.exists(output_dir):
592
 
        target_branch = Branch.open(output_dir)
593
 
    else:
594
 
        if standalone:
595
 
            wt = BzrDir.create_standalone_workingtree(output_dir)
596
 
            target_branch = wt.branch
597
 
        else:
598
 
            target_branch = create_branch(output_dir)
599
421
 
600
422
    for i in range(len(ancestors)):
601
423
        revision = ancestors[i]
602
 
        rev_id = revision_id(revision, encoding)
603
424
        direct_merges = []
604
425
        if verbose:
605
426
            version = str(revision.version)
606
427
            if version != previous_version:
607
 
                pb.note('On version: %s' % version)
 
428
                clear_progress_bar()
 
429
                print '\rOn version: %s' % version
608
430
            yield Progress(str(revision.patchlevel), i, len(ancestors))
609
431
            previous_version = version
610
432
        else:
611
433
            yield Progress("revisions", i, len(ancestors))
612
 
 
613
 
        if target_branch.repository.has_revision(rev_id):
614
 
            target_branch.append_revision(rev_id)
615
 
            continue
616
434
        if revdir is None:
617
435
            revdir = os.path.join(tempdir, "rd")
618
436
            try:
619
 
                tree, baz_inv, log = get_revision(revdir, revision)
 
437
                baz_inv, log = get_revision(revdir, revision)
620
438
            except pybaz.errors.ExecProblem, e:
621
439
                if ("%s" % e.args).find('could not connect') == -1:
622
440
                    raise
623
441
                missing_ancestor = revision
624
442
                revdir = None
625
 
                pb.note("unable to access ancestor %s, making into a merge."
 
443
                print ("unable to access ancestor %s, making into a merge."
626
444
                       % missing_ancestor)
627
445
                continue
628
 
            target_tree = create_checkout_metadata(target_branch, revdir)
629
 
            branch = target_tree.branch
 
446
            # cached so we can delete the log
 
447
            log_date = log.date
 
448
            log_summary = log.summary
 
449
            log_creator = log.creator
 
450
            if os.path.exists(output_dir):
 
451
                bzr_dir = os.path.join(output_dir, '.bzr')
 
452
                new_bzr_dir = os.path.join(tempdir, "rd", '.bzr')
 
453
                # This would be much faster with a simple os.rename(), but if
 
454
                # we fail, we have corrupted the original .bzr directory.  Is
 
455
                # that a big problem, as we can just back out the last
 
456
                # revisions in .bzr/revision_history I don't really know
 
457
                shutil.copytree(bzr_dir, new_bzr_dir)
 
458
                # Now revdir should have a tree with the latest .bzr, and the
 
459
                # next revision of the baz tree
 
460
                branch = find_branch(revdir, find_root=False)
 
461
            else:
 
462
                branch = Branch(revdir, init=True)
630
463
        else:
631
464
            old = os.path.join(revdir, ".bzr")
632
465
            new = os.path.join(tempdir, ".bzr")
633
466
            os.rename(old, new)
634
 
            baz_inv, log = apply_revision(tree, revision)
 
467
            baz_inv, log = apply_revision(revdir, revision)
 
468
            log_date = log.date
 
469
            log_summary = log.summary
 
470
            log_creator = log.creator
 
471
            direct_merges = get_direct_merges(revdir, revision)
635
472
            os.rename(new, old)
636
 
            target_tree = WorkingTree.open(revdir)
637
 
            branch = target_tree.branch
638
 
        # cached so we can delete the log
639
 
        log_date = log.date
640
 
        log_summary = log.summary
641
 
        log_description = log.description
642
 
        is_continuation = log.continuation_of is not None
643
 
        log_creator = log.creator
644
 
        direct_merges = get_direct_merges(revdir, revision, log)
645
 
 
 
473
            branch = find_branch(revdir, find_root=False)
646
474
        timestamp = email.Utils.mktime_tz(log_date + (0,))
647
 
        if log_summary is None:
648
 
            log_summary = ""
649
 
        # log_descriptions of None and "" are ignored.
650
 
        if not is_continuation and log_description:
651
 
            log_message = "\n".join((log_summary, log_description))
652
 
        else:
653
 
            log_message = log_summary
654
 
        target_tree.lock_write()
 
475
        rev_id = revision_id(revision)
655
476
        branch.lock_write()
656
477
        try:
657
478
            if missing_ancestor:
658
479
                # if we want it to be in revision-history, do that here.
659
 
                target_tree.set_parent_ids(
660
 
                    [revision_id(missing_ancestor, encoding)],
661
 
                    allow_leftmost_as_ghost=True)
 
480
                branch.add_pending_merge(revision_id(missing_ancestor))
662
481
                missing_ancestor = None
663
 
            for merged_rev in direct_merges:
664
 
                target_tree.add_pending_merge(revision_id(merged_rev, 
665
 
                                                          encoding))
666
 
            target_tree.set_root_id(BAZ_IMPORT_ROOT)
667
 
            target_tree.set_inventory(baz_inv)
668
 
            commitobj = Commit(reporter=ImportCommitReporter())
669
 
            commitobj.commit(working_tree=target_tree,
670
 
                message=log_message.decode(log_encoding, 'replace'),
671
 
                verbose=False, committer=log_creator, timestamp=timestamp,
672
 
                timezone=0, rev_id=rev_id, revprops={})
 
482
            for merge in direct_merges:
 
483
                branch.add_pending_merge(revision_id(merge.revision))
 
484
            branch.set_inventory(baz_inv)
 
485
            bzrlib.trace.silent = True
 
486
            branch.commit(log_summary, verbose=False, committer=log_creator,
 
487
                          timestamp=timestamp, timezone=0, rev_id=rev_id)
673
488
        finally:
674
 
            target_tree.unlock()
 
489
            bzrlib.trace.silent = False   
675
490
            branch.unlock()
676
491
    yield Progress("revisions", len(ancestors), len(ancestors))
 
492
    unlink_unversioned(branch, revdir)
677
493
 
678
 
def get_direct_merges(revdir, revision, log):
679
 
    continuation = log.continuation_of
680
 
    previous_version = revision.version
681
 
    if pybaz.WorkingTree(revdir).tree_version != previous_version:
682
 
        pybaz.WorkingTree(revdir).set_tree_version(previous_version)
 
494
def get_direct_merges(revdir, revision):
 
495
    from fai.cmdutil import pylon, direct_merges
 
496
    if pybaz.WorkingTree(revdir).tree_version != revision.version:
 
497
        pybaz.WorkingTree(revdir).set_tree_version(revision.version)
683
498
    log_path = "%s/{arch}/%s/%s/%s/%s/patch-log/%s" % (revdir, 
684
499
        revision.category.nonarch, revision.branch.nonarch, 
685
500
        revision.version.nonarch, revision.archive, revision.patchlevel)
686
 
    temp_path = tempfile.mktemp(dir=os.path.dirname(revdir))
 
501
    temp_path = tempfile.mktemp()
687
502
    os.rename(log_path, temp_path)
688
 
    merges = list(iter_new_merges(revdir, revision.version))
689
 
    direct = direct_merges(merges, [continuation])
 
503
    merges = list(pylon.iter_new_merges(revdir, revision.version))
 
504
    direct = direct_merges (merges)
690
505
    os.rename(temp_path, log_path)
691
506
    return direct
692
507
 
693
 
def unlink_unversioned(wt):
694
 
    for unversioned in wt.extras():
695
 
        path = wt.abspath(unversioned)
 
508
def unlink_unversioned(branch, revdir):
 
509
    for unversioned in branch.working_tree().extras():
 
510
        path = os.path.join(revdir, unversioned)
696
511
        if os.path.isdir(path):
697
512
            shutil.rmtree(path)
698
513
        else:
699
514
            os.unlink(path)
700
515
 
701
516
def get_log(tree, revision):
702
 
    log = pybaz.Patchlog(revision, tree=tree)
 
517
    log = tree.iter_logs(version=revision.version, reverse=True).next()
703
518
    assert str(log.revision) == str(revision), (log.revision, revision)
704
519
    return log
705
520
 
706
521
def get_revision(revdir, revision):
707
 
    tree = revision.get(revdir)
 
522
    revision.get(revdir)
 
523
    tree = pybaz.tree_root(revdir)
708
524
    log = get_log(tree, revision)
709
525
    try:
710
 
        return tree, bzr_inventory_data(tree), log 
 
526
        return bzr_inventory_data(tree), log 
711
527
    except BadFileKind, e:
712
 
        raise UserError("Cannot convert %s because %s is a %s" % 
713
 
                        (revision,e.path, e.kind))
714
 
 
715
 
 
716
 
def apply_revision(tree, revision):
 
528
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
 
529
 
 
530
 
 
531
def apply_revision(revdir, revision):
 
532
    tree = pybaz.tree_root(revdir)
717
533
    revision.apply(tree)
718
534
    log = get_log(tree, revision)
719
535
    try:
720
536
        return bzr_inventory_data(tree), log
721
537
    except BadFileKind, e:
722
 
        raise UserError("Cannot convert %s because %s is a %s" % 
723
 
                        (revision,e.path, e.kind))
 
538
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
 
539
 
 
540
 
724
541
 
725
542
 
726
543
class BadFileKind(Exception):
732
549
        Exception.__init__(self, "File %s is of forbidden type %s" %
733
550
                           (os.path.join(tree_root, path), kind))
734
551
 
735
 
 
736
552
def bzr_inventory_data(tree):
737
553
    inv_iter = tree.iter_inventory_ids(source=True, both=True)
738
554
    inv_map = {}
739
555
    for arch_id, path in inv_iter:
740
 
        bzr_file_id = map_file_id(arch_id)
 
556
        bzr_file_id = arch_id.replace('%', '%25').replace('/', '%2f')
741
557
        inv_map[path] = bzr_file_id 
742
558
 
743
559
    bzr_inv = []
756
572
    return bzr_inv
757
573
 
758
574
 
759
 
def baz_import_branch(to_location, from_branch, fast, max_count, verbose, 
760
 
                      encoding, dry_run, reuse_history_list):
761
 
    to_location = os.path.realpath(str(to_location))
762
 
    if from_branch is not None:
763
 
        try:
764
 
            from_branch = pybaz.Version(from_branch)
765
 
        except pybaz.errors.NamespaceError:
766
 
            print "%s is not a valid Arch branch." % from_branch
767
 
            return 1
768
 
    if reuse_history_list is None:
769
 
        reuse_history_list = []
770
 
    import_version(to_location, from_branch, encoding, max_count=max_count, 
771
 
                   reuse_history_from=reuse_history_list)
772
 
 
773
 
 
774
 
class NotInABranch(Exception):
775
 
    def __init__(self, path):
776
 
        Exception.__init__(self, "%s is not in a branch." % path)
777
 
        self.path = path
778
 
 
779
 
 
780
 
 
781
 
def baz_import(to_root_dir, from_archive, encoding, 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, encoding,
791
 
                   reuse_history_list, prefixes=prefixes)
792
 
 
793
 
 
794
 
def import_archive(to_root, from_archive, verbose,
795
 
                   encoding, 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
 
            if not os.path.exists(os.path.dirname(target)):
825
 
                os.makedirs(os.path.dirname(target))
 
575
class cmd_baz_import_branch(Command):
 
576
    """Import an Arch or Baz branch into a bzr branch"""
 
577
    takes_args = ['to_location', 'from_branch?']
 
578
    takes_options = ['verbose']
 
579
 
 
580
    def printer(self, name):
 
581
        print name
 
582
 
 
583
    def run(self, to_location, from_branch=None, fast=False, max_count=None,
 
584
            verbose=False, dry_run=False):
 
585
        to_location = os.path.realpath(str(to_location))
 
586
        if from_branch is not None:
826
587
            try:
827
 
                import_version(target, version, encoding,
828
 
                               reuse_history_from=reuse_history_from, 
829
 
                               standalone=standalone)
830
 
            except pybaz.errors.ExecProblem,e:
831
 
                if str(e).find('The requested revision cannot be built.') != -1:
832
 
                    progress_bar.note(
833
 
                        "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
 
                    progress_bar.note(
840
 
                        "Skipping version %s as it has had commits made"
841
 
                        " since it was converted to bzr." % version)
842
 
                else:
843
 
                    raise
844
 
    finally:
845
 
        progress_bar.finished()
846
 
 
 
588
                from_branch = pybaz.Version(from_branch)
 
589
            except pybaz.errors.NamespaceError:
 
590
                print "%s is not a valid Arch branch." % from_branch
 
591
                return 1
 
592
        import_version(to_location, from_branch, self.printer)
 
593
 
 
594
class cmd_baz_import(Command):
 
595
    """Import an Arch or Baz archive into bzr branches."""
 
596
    takes_args = ['to_root_dir', 'from_archive']
 
597
    takes_options = ['verbose']
 
598
 
 
599
    def printer(self, name):
 
600
        print name
 
601
 
 
602
    def run(self, to_root_dir, from_archive, verbose=False):
 
603
        to_root = str(os.path.realpath(to_root_dir))
 
604
        if not os.path.exists(to_root):
 
605
            os.mkdir(to_root)
 
606
        import_archive(to_root, from_archive, verbose, self.printer)
 
607
 
 
608
def import_archive(to_root, from_archive, verbose, printer):
 
609
    for version in pybaz.Archive(str(from_archive)).iter_versions():
 
610
        target = os.path.join(to_root, map_namespace(version))
 
611
        printer("importing %s into %s" % (version, target))
 
612
        if not os.path.exists(os.path.dirname(target)):
 
613
            os.makedirs(os.path.dirname(target))
 
614
        import_version(target, version, printer)
847
615
 
848
616
def map_namespace(a_version):
849
617
    a_version = pybaz.Version("%s" % a_version)
856
624
    if version == '0':
857
625
        return "%s/%s" % (category, branch)
858
626
    return "%s/%s/%s" % (category, version, branch)
859
 
 
860
 
 
861
 
def map_file_id(file_id):
862
 
    """Convert a baz file id to a bzr one."""
863
 
    return file_id.replace('%', '%25').replace('/', '%2f')