~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to baz_import.py

  • Committer: Aaron Bentley
  • Date: 2005-09-22 23:26:32 UTC
  • Revision ID: aaron.bentley@utoronto.ca-20050922232632-4fb8b331c9509a12
tweaked docs

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
 
import bzrlib.ui
35
 
import bzrlib.ui.text
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
 
 
61
 
    def escaped(self, escape_count, message):
62
 
        bzrlib.trace.warning("replaced %d control characters in message" %
63
 
                             escape_count)
64
 
 
65
39
def add_id(files, id=None):
66
40
    """Adds an explicit id to a list of files.
67
41
 
76
50
    args.extend(files)
77
51
    return null_cmd(args)
78
52
 
79
 
saved_dir = None
80
 
 
81
 
def make_archive(name, location):
82
 
    pb_location = pybaz.ArchiveLocation(location)
83
 
    pb_location.create_master(pybaz.Archive(name), 
84
 
                              pybaz.ArchiveLocationParams())
85
 
 
86
53
def test_environ():
87
54
    """
88
55
    >>> q = test_environ()
94
61
    >>> os.path.exists(q)
95
62
    False
96
63
    """
97
 
    global saved_dir
98
 
    saved_dir = os.getcwdu()
99
64
    tdir = tempfile.mkdtemp(prefix="testdir-")
100
65
    os.environ["HOME"] = os.path.join(tdir, "home")
101
66
    os.mkdir(os.environ["HOME"])
102
67
    arch_dir = os.path.join(tdir, "archive_dir")
103
 
    make_archive("test@example.com", arch_dir)
 
68
    pybaz.make_archive("test@example.com", arch_dir)
104
69
    work_dir = os.path.join(tdir, "work_dir")
105
70
    os.mkdir(work_dir)
106
71
    os.chdir(work_dir)
139
104
    add_id([path], id)
140
105
 
141
106
def teardown_environ(tdir):
142
 
    os.chdir(saved_dir)
 
107
    os.chdir("/")
143
108
    shutil.rmtree(tdir)
144
109
 
145
110
def timport(tree, summary):
231
196
    """
232
197
    try:
233
198
        revision = version.iter_revisions(reverse=True).next()
234
 
    except StopIteration:
235
 
        return ()
236
199
    except:
237
 
        print version
238
200
        if not version.exists():
239
201
            raise NoSuchVersion(version)
240
202
        else:
244
206
    return ancestors
245
207
 
246
208
def get_last_revision(branch):
247
 
    last_patch = branch.last_revision()
 
209
    last_patch = branch.last_patch()
248
210
    try:
249
211
        return arch_revision(last_patch)
250
212
    except NotArchRevision:
251
213
        raise UserError(
252
214
            "Directory \"%s\" already exists, and the last revision is not"
253
 
            " an Arch revision (%s)" % (branch.base, last_patch))
254
 
 
255
 
def do_branch(br_from, to_location, revision_id):
256
 
    """Derived from branch in builtins."""
257
 
    br_from.lock_read()
258
 
    try:
259
 
        try:
260
 
            os.mkdir(to_location)
261
 
        except OSError, e:
262
 
            if e.errno == errno.EEXIST:
263
 
                raise UserError('Target directory "%s" already'
264
 
                                      ' exists.' % to_location)
265
 
            if e.errno == errno.ENOENT:
266
 
                raise UserError('Parent of "%s" does not exist.' %
267
 
                                      to_location)
268
 
            else:
269
 
                raise
270
 
        try:
271
 
            br_from.bzrdir.clone(to_location, revision_id)
272
 
        except NoSuchRevision:
273
 
            rmtree(to_location)
274
 
            msg = "The branch %s has no revision %s." % (from_location, 
275
 
                                                         revision_id)
276
 
            raise UserError(msg)
277
 
    finally:
278
 
        br_from.unlock()
279
 
 
280
 
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):
281
219
    last_patch = None
282
220
    old_revno = None
283
 
    output_exists = os.path.exists(output_dir)
284
 
    if output_exists:
 
221
    if os.path.exists(output_dir):
285
222
        # We are starting from an existing directory, figure out what
286
223
        # the current version is
287
 
        branch = Branch.open(output_dir)
 
224
        branch = find_branch(output_dir)
288
225
        last_patch = get_last_revision(branch)
289
 
        if last_patch is None:
290
 
            if branch.last_revision() != None:
291
 
                raise NotPreviousImport(branch.base)
292
 
        elif version is None:
 
226
        if version is None:
293
227
            version = last_patch.version
294
228
    elif version is None:
295
229
        raise UserError("No version specified, and directory does not exist.")
296
230
 
297
231
    try:
298
232
        ancestors = version_ancestry(version)
299
 
        if not output_exists and reuse_history_from != []:
300
 
            for ancestor in reversed(ancestors):
301
 
                if last_patch is not None:
302
 
                    # found something to copy
303
 
                    break
304
 
                # try to grab a copy of ancestor
305
 
                # note that is not optimised: we could look for namespace
306
 
                # transitions and only look for the past after the 
307
 
                # transition.
308
 
                for history_root in reuse_history_from:
309
 
                    possible_source = os.path.join(history_root,
310
 
                        map_namespace(ancestor.version))
311
 
                    try:
312
 
                        source = Branch.open(possible_source)
313
 
                        rev_id = revision_id(ancestor)
314
 
                        if rev_id in source.revision_history():
315
 
                            do_branch(source, output_dir, rev_id)
316
 
                            last_patch = ancestor
317
 
                            break
318
 
                    except NotBranchError:
319
 
                        pass
320
233
    except NoSuchVersion, e:
321
 
        raise UserError(str(e))
 
234
        raise UserError(e)
322
235
 
323
236
    if last_patch:
324
237
        for i in range(len(ancestors)):
331
244
        # Strip off all of the ancestors which are already present
332
245
        # And get a directory starting with the latest ancestor
333
246
        latest_ancestor = ancestors[i]
334
 
        old_revno = Branch.open(output_dir).revno()
 
247
        old_revno = find_branch(output_dir).revno()
335
248
        ancestors = ancestors[i+1:]
336
249
    return ancestors, old_revno
337
250
 
338
 
 
339
 
###class Importer(object):
340
 
###    """An importer.
341
 
###    
342
 
###    Currently this is used as a parameter object, though more behaviour is
343
 
###    possible later.
344
 
###    """
345
 
###
346
 
###    def __init__(self, output_dir, version, fast=False,
347
 
###                 verbose=False, dry_run=False, max_count=None, 
348
 
###                   reuse_history_from=[]):
349
 
###        self.output_dir = output_dir
350
 
###        self.version = version
351
 
###        self.
352
 
 
353
 
 
354
 
def import_version(output_dir, version, fast=False,
355
 
                   verbose=False, dry_run=False, max_count=None,
356
 
                   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):
357
253
    """
358
254
    >>> q = test_environ()
359
 
    
360
 
    Progress bars output to stderr, but doctest does not capture that.
361
 
 
362
 
    >>> old_stderr = sys.stderr
363
 
    >>> sys.stderr = sys.stdout
364
 
 
365
255
    >>> result_path = os.path.join(q, "result")
366
256
    >>> commit_test_revisions()
367
257
    >>> version = pybaz.Version("test@example.com/test--test--0.1")
368
 
    >>> old_ui = bzrlib.ui.ui_factory
369
 
    >>> bzrlib.ui.ui_factory = bzrlib.ui.text.TextUIFactory(
370
 
    ...     bar_type=bzrlib.progress.DotsProgressBar)
371
 
 
372
 
    >>> import_version('/', version, dry_run=True)
 
258
    >>> import_version('/', version, fancy=False, dry_run=True)
373
259
    Traceback (most recent call last):
374
 
    NotPreviousImport: / is not the location of a previous import.
375
 
    >>> import_version(result_path, version, dry_run=True)
 
260
    UserError: / exists, but is not a bzr branch.
 
261
    >>> import_version(result_path, version, fancy=False, dry_run=True)
376
262
    Traceback (most recent call last):
377
263
    UserError: The version test@example.com/test--test--0.1 does not exist.
378
264
    >>> version = pybaz.Version("test@example.com/test--test--0")
379
 
    >>> import_version(result_path, version, dry_run=True) #doctest: +ELLIPSIS
380
 
    importing test@example.com/test--test--0 into ...
381
 
    ...
382
 
    revisions: ..........................................
 
265
    >>> import_version(result_path, version, fancy=False, dry_run=True)
 
266
    not fancy
 
267
    ....
383
268
    Dry run, not modifying output_dir
384
269
    Cleaning up
385
 
    >>> import_version(result_path, version) #doctest: +ELLIPSIS
386
 
    importing test@example.com/test--test--0 into ...
387
 
    ...
388
 
    revisions: .....................................................................
 
270
    >>> import_version(result_path, version, fancy=False)
 
271
    not fancy
 
272
    ....
389
273
    Cleaning up
390
274
    Import complete.
391
 
    >>> import_version(result_path, version) #doctest: +ELLIPSIS
 
275
    >>> import_version(result_path, version, fancy=False)
392
276
    Tree is up-to-date with test@example.com/test--test--0--patch-2
393
277
    >>> commit_more_test_revisions()
394
 
    >>> import_version(result_path, version) #doctest: +ELLIPSIS
395
 
    importing test@example.com/test--test--0 into ...
396
 
    revisions: ....................................................
 
278
    >>> import_version(result_path, version, fancy=False)
 
279
    not fancy
 
280
    ..
397
281
    Cleaning up
398
282
    Import complete.
399
 
    >>> bzrlib.ui.ui_factory = old_ui
400
 
    >>> sys.stderr = old_stderr
401
283
    >>> teardown_environ(q)
402
284
    """
403
 
    progress_bar = bzrlib.ui.ui_factory.nested_progress_bar()
404
285
    try:
405
 
        try:
406
 
            ancestors, old_revno = get_remaining_revisions(output_dir, version,
407
 
                                                           reuse_history_from)
408
 
        except NotBranchError, e:
409
 
            raise NotPreviousImport(e.path)
410
 
        if old_revno is None and len(ancestors) == 0:
411
 
            progress_bar.note('Version %s has no revisions.' % version)
412
 
            return
413
 
        if len(ancestors) == 0:
414
 
            last_revision = get_last_revision(Branch.open(output_dir))
415
 
            progress_bar.note('Tree is up-to-date with %s' % last_revision)
416
 
            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)
 
289
    if len(ancestors) == 0:
 
290
        last_revision = get_last_revision(find_branch(output_dir))
 
291
        print 'Tree is up-to-date with %s' % last_revision
 
292
        return
417
293
 
418
 
        progress_bar.note("importing %s into %s" % (version, output_dir))
419
 
    
420
 
        tempdir = tempfile.mkdtemp(prefix="baz2bzr-",
421
 
                                   dir=os.path.dirname(output_dir))
422
 
        try:
423
 
            wt = WorkingTree.open(output_dir)
424
 
        except (NotBranchError, NoWorkingTree):
425
 
            wt = None
 
294
    progress_bar = ProgressBar()
 
295
    tempdir = tempfile.mkdtemp(prefix="baz2bzr-",
 
296
                               dir=os.path.dirname(output_dir))
 
297
    try:
 
298
        if not fancy:
 
299
            print "not fancy"
426
300
        try:
427
301
            for result in iter_import_version(output_dir, ancestors, tempdir,
428
 
                    pb=progress_bar,
429
 
                    fast=fast, verbose=verbose, dry_run=dry_run,
430
 
                    max_count=max_count, standalone=standalone):
431
 
                show_progress(progress_bar, result)
432
 
            if dry_run:
433
 
                progress_bar.note('Dry run, not modifying output_dir')
434
 
                return
435
 
    
436
 
            # Update the working tree of the branch
 
302
                    fast=fast, verbose=verbose, dry_run=dry_run, 
 
303
                    max_count=max_count, skip_symlinks=skip_symlinks):
 
304
                if fancy:
 
305
                    show_progress(progress_bar, result)
 
306
                else:
 
307
                    sys.stdout.write('.')
 
308
        finally:
 
309
            if fancy:
 
310
                progress_bar.clear()
 
311
            else:
 
312
                sys.stdout.write('\n')
 
313
 
 
314
        if dry_run:
 
315
            print 'Dry run, not modifying output_dir'
 
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')
 
323
 
 
324
            os.rename(bzr_dir, tmp_bzr_dir) # Move the original bzr out of the way
 
325
            os.rename(new_bzr_dir, bzr_dir)
437
326
            try:
438
 
                wt = WorkingTree.open(output_dir)
439
 
            except NoWorkingTree:
440
 
                wt = None
441
 
            if wt is not None:
442
 
                wt.set_last_revision(wt.branch.last_revision())
443
 
                wt.revert([])
444
 
    
445
 
        finally:
446
 
            
447
 
            progress_bar.note('Cleaning up')
448
 
            shutil.rmtree(tempdir)
449
 
        progress_bar.note("Import complete.")
 
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)
 
338
 
450
339
    finally:
451
 
        progress_bar.finished()
 
340
        print 'Cleaning up'
 
341
        shutil.rmtree(tempdir)
 
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:
576
 
                pb.note('On version: %s' % version)
 
418
                clear_progress_bar()
 
419
                print '\rOn version: %s' % version
577
420
            yield Progress(str(revision.patchlevel), i, len(ancestors))
578
421
            previous_version = version
579
422
        else:
580
423
            yield Progress("revisions", i, len(ancestors))
581
 
 
582
 
        if target_branch.repository.has_revision(rev_id):
583
 
            target_branch.append_revision(rev_id)
584
 
            continue
585
424
        if revdir is None:
586
425
            revdir = os.path.join(tempdir, "rd")
587
 
            try:
588
 
                tree, baz_inv, log = get_revision(revdir, revision)
589
 
            except pybaz.errors.ExecProblem, e:
590
 
                if ("%s" % e.args).find('could not connect') == -1:
591
 
                    raise
592
 
                missing_ancestor = revision
593
 
                revdir = None
594
 
                pb.note("unable to access ancestor %s, making into a merge."
595
 
                       % missing_ancestor)
596
 
                continue
597
 
            target_tree = create_checkout_metadata(target_branch, revdir)
598
 
            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)
599
441
        else:
600
442
            old = os.path.join(revdir, ".bzr")
601
443
            new = os.path.join(tempdir, ".bzr")
602
444
            os.rename(old, new)
603
 
            baz_inv, log = apply_revision(tree, revision)
 
445
            baz_inv, log = apply_revision(revdir, revision, 
 
446
                                          skip_symlinks=skip_symlinks)
604
447
            os.rename(new, old)
605
 
            target_tree = WorkingTree.open(revdir)
606
 
            branch = target_tree.branch
607
 
        # cached so we can delete the log
608
 
        log_date = log.date
609
 
        log_summary = log.summary
610
 
        log_description = log.description
611
 
        is_continuation = log.continuation_of is not None
612
 
        log_creator = log.creator
613
 
        direct_merges = get_direct_merges(revdir, revision, log)
614
 
 
615
 
        timestamp = email.Utils.mktime_tz(log_date + (0,))
616
 
        if log_summary is None:
617
 
            log_summary = ""
618
 
        # log_descriptions of None and "" are ignored.
619
 
        if not is_continuation and log_description:
620
 
            log_message = "\n".join((log_summary, log_description))
621
 
        else:
622
 
            log_message = log_summary
623
 
        target_tree.lock_write()
 
448
            branch = find_branch(revdir)
 
449
        timestamp = email.Utils.mktime_tz(log.date + (0,))
 
450
        rev_id = revision_id(revision)
624
451
        branch.lock_write()
625
452
        try:
626
 
            if missing_ancestor:
627
 
                # if we want it to be in revision-history, do that here.
628
 
                target_tree.add_pending_merge(revision_id(missing_ancestor))
629
 
                missing_ancestor = None
630
 
            for merged_rev in direct_merges:
631
 
                target_tree.add_pending_merge(revision_id(merged_rev))
632
 
            target_tree.set_inventory(baz_inv)
633
 
            commitobj = Commit(reporter=ImportCommitReporter())
634
 
            commitobj.commit(working_tree=target_tree,
635
 
                             message=log_message.decode('ascii', 'replace'), 
636
 
                             verbose=False, committer=log_creator,
637
 
                             timestamp=timestamp, timezone=0, rev_id=rev_id,
638
 
                             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)
639
457
        finally:
640
 
            target_tree.unlock()
 
458
            bzrlib.trace.silent = False   
641
459
            branch.unlock()
642
460
    yield Progress("revisions", len(ancestors), len(ancestors))
643
 
 
644
 
def get_direct_merges(revdir, revision, log):
645
 
    continuation = log.continuation_of
646
 
    previous_version = revision.version
647
 
    if pybaz.WorkingTree(revdir).tree_version != previous_version:
648
 
        pybaz.WorkingTree(revdir).set_tree_version(previous_version)
649
 
    log_path = "%s/{arch}/%s/%s/%s/%s/patch-log/%s" % (revdir, 
650
 
        revision.category.nonarch, revision.branch.nonarch, 
651
 
        revision.version.nonarch, revision.archive, revision.patchlevel)
652
 
    temp_path = tempfile.mktemp(dir=os.path.dirname(revdir))
653
 
    os.rename(log_path, temp_path)
654
 
    merges = list(iter_new_merges(revdir, revision.version))
655
 
    direct = direct_merges(merges, [continuation])
656
 
    os.rename(temp_path, log_path)
657
 
    return direct
658
 
 
659
 
def unlink_unversioned(wt):
660
 
    for unversioned in wt.extras():
661
 
        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)
662
466
        if os.path.isdir(path):
663
467
            shutil.rmtree(path)
664
468
        else:
665
469
            os.unlink(path)
666
470
 
667
471
def get_log(tree, revision):
668
 
    log = pybaz.Patchlog(revision, tree=tree)
 
472
    log = tree.iter_logs(version=revision.version, reverse=True).next()
669
473
    assert str(log.revision) == str(revision), (log.revision, revision)
670
474
    return log
671
475
 
672
 
def get_revision(revdir, revision):
673
 
    tree = revision.get(revdir)
 
476
def get_revision(revdir, revision, skip_symlinks=False):
 
477
    revision.get(revdir)
 
478
    tree = pybaz.tree_root(revdir)
674
479
    log = get_log(tree, revision)
675
480
    try:
676
 
        return tree, bzr_inventory_data(tree), log 
 
481
        return bzr_inventory_data(tree, skip_symlinks=skip_symlinks), log 
677
482
    except BadFileKind, e:
678
 
        raise UserError("Cannot convert %s because %s is a %s" % 
679
 
                        (revision,e.path, e.kind))
680
 
 
681
 
 
682
 
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)
683
488
    revision.apply(tree)
684
489
    log = get_log(tree, revision)
685
490
    try:
686
 
        return bzr_inventory_data(tree), log
 
491
        return bzr_inventory_data(tree, skip_symlinks=skip_symlinks), log
687
492
    except BadFileKind, e:
688
 
        raise UserError("Cannot convert %s because %s is a %s" % 
689
 
                        (revision,e.path, e.kind))
 
493
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
 
494
 
 
495
 
690
496
 
691
497
 
692
498
class BadFileKind(Exception):
698
504
        Exception.__init__(self, "File %s is of forbidden type %s" %
699
505
                           (os.path.join(tree_root, path), kind))
700
506
 
701
 
 
702
 
def bzr_inventory_data(tree):
 
507
def bzr_inventory_data(tree, skip_symlinks=False):
703
508
    inv_iter = tree.iter_inventory_ids(source=True, both=True)
704
509
    inv_map = {}
705
510
    for arch_id, path in inv_iter:
706
 
        bzr_file_id = map_file_id(arch_id)
 
511
        bzr_file_id = arch_id.replace('%', '%25').replace('/', '%2f')
707
512
        inv_map[path] = bzr_file_id 
708
513
 
709
514
    bzr_inv = []
710
515
    for path, file_id in inv_map.iteritems():
711
516
        full_path = os.path.join(tree, path)
712
517
        kind = bzrlib.osutils.file_kind(full_path)
713
 
        if kind not in ("file", "directory", "symlink"):
 
518
        if skip_symlinks and kind == "symlink":
 
519
            continue
 
520
        if kind not in ("file", "directory"):
714
521
            raise BadFileKind(tree, path, kind)
715
522
        parent_dir = os.path.dirname(path)
716
523
        if parent_dir != "":
721
528
    bzr_inv.sort()
722
529
    return bzr_inv
723
530
 
724
 
 
725
 
def baz_import_branch(to_location, from_branch, fast, max_count, verbose, 
726
 
                      dry_run, reuse_history_list):
727
 
    to_location = os.path.realpath(str(to_location))
728
 
    if from_branch is not None:
729
 
        try:
730
 
            from_branch = pybaz.Version(from_branch)
731
 
        except pybaz.errors.NamespaceError:
732
 
            print "%s is not a valid Arch branch." % from_branch
733
 
            return 1
734
 
    if reuse_history_list is None:
735
 
        reuse_history_list = []
736
 
    import_version(to_location, from_branch, 
737
 
                   max_count=max_count, 
738
 
                   reuse_history_from=reuse_history_list)
739
 
 
740
 
 
741
531
class NotInABranch(Exception):
742
532
    def __init__(self, path):
743
533
        Exception.__init__(self, "%s is not in a branch." % path)
744
534
        self.path = path
745
535
 
746
536
 
747
 
 
748
 
def baz_import(to_root_dir, from_archive, verbose=False, reuse_history_list=[],
749
 
               prefixes=None):
750
 
    if reuse_history_list is None:
751
 
        reuse_history_list = []
752
 
    to_root = str(os.path.realpath(to_root_dir))
753
 
    if not os.path.exists(to_root):
754
 
        os.mkdir(to_root)
755
 
    if prefixes is not None:
756
 
        prefixes = prefixes.split(':')
757
 
    import_archive(to_root, from_archive, verbose,
758
 
                   reuse_history_list, prefixes=prefixes)
759
 
 
760
 
 
761
 
def import_archive(to_root, from_archive, verbose,
762
 
                   reuse_history_from=[], standalone=False,
763
 
                   prefixes=None):
764
 
    def selected(version):
765
 
        if prefixes is None:
766
 
            return True
767
 
        else:
768
 
            for prefix in prefixes:
769
 
                if version.nonarch.startswith(prefix):
770
 
                    return True
771
 
            return False
772
 
    real_to = os.path.realpath(to_root)
773
 
    history_locations = [real_to] + reuse_history_from
774
 
    if standalone is False:
775
 
        try:
776
 
            bd = BzrDir.open(to_root)
777
 
            bd.find_repository()
778
 
        except NotBranchError:
779
 
            create_shared_repository(to_root)
780
 
        except NoRepositoryPresent:
781
 
            raise BzrCommandError("Can't create repository at existing branch.")
782
 
    versions = list(pybaz.Archive(str(from_archive)).iter_versions())
783
 
    progress_bar = bzrlib.ui.ui_factory.nested_progress_bar()
 
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
    """
784
546
    try:
785
 
        for num, version in enumerate(versions):
786
 
            progress_bar.update("Branch", num, len(versions))
787
 
            if not selected(version):
788
 
                print "Skipping %s" % version
789
 
                continue
790
 
            target = os.path.join(to_root, map_namespace(version))
791
 
            if not os.path.exists(os.path.dirname(target)):
792
 
                os.makedirs(os.path.dirname(target))
 
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):
 
559
        to_location = os.path.realpath(str(to_location))
 
560
        if from_branch is not None:
793
561
            try:
794
 
                import_version(target, version,
795
 
                               reuse_history_from=reuse_history_from, 
796
 
                               standalone=standalone)
797
 
            except pybaz.errors.ExecProblem,e:
798
 
                if str(e).find('The requested revision cannot be built.') != -1:
799
 
                    progress_bar.note(
800
 
                        "Skipping version %s as it cannot be built due"
801
 
                        " to a missing parent archive." % version)
802
 
                else:
803
 
                    raise
804
 
            except UserError, e:
805
 
                if str(e).find('already exists, and the last revision ') != -1:
806
 
                    progress_bar.note(
807
 
                        "Skipping version %s as it has had commits made"
808
 
                        " since it was converted to bzr." % version)
809
 
                else:
810
 
                    raise
811
 
    finally:
812
 
        progress_bar.finished()
813
 
 
814
 
 
815
 
def map_namespace(a_version):
816
 
    a_version = pybaz.Version("%s" % a_version)
817
 
    parser = NameParser(a_version)
818
 
    version = parser.get_version()
819
 
    branch = parser.get_branch()
820
 
    category = parser.get_category()
821
 
    if branch is None or branch == '':
822
 
        branch = "+trunk"
823
 
    if version == '0':
824
 
        return "%s/%s" % (category, branch)
825
 
    return "%s/%s/%s" % (category, version, branch)
826
 
 
827
 
 
828
 
def map_file_id(file_id):
829
 
    """Convert a baz file id to a bzr one."""
830
 
    return file_id.replace('%', '%25').replace('/', '%2f')
 
562
                from_branch = pybaz.Version(from_branch)
 
563
            except pybaz.errors.NamespaceError:
 
564
                print "%s is not a valid Arch branch." % from_branch
 
565
                return 1
 
566
        import_version(to_location, from_branch)