~abentley/bzrtools/bzrtools.dev

147.1.65 by Aaron Bentley
Support creating repositories instead of standalone branches.
1
# Copyright (C) 2005, 2006 by Aaron Bentley
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
2
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
147.1.2 by Robert Collins
test empty import and tagged branches
16
147.4.10 by Robert Collins
missing an errno import
17
import errno
18
147.1.59 by Aaron Bentley
Reverted bzrtools.py to mainline version.
19
from bzrlib.bzrdir import BzrDir
147.1.64 by Aaron Bentley
Switched to using checkouts instead of branch copies
20
import bzrlib.bzrdir as bzrdir
147.1.65 by Aaron Bentley
Support creating repositories instead of standalone branches.
21
from bzrlib.errors import (BzrError,
22
                           NotBranchError,
23
                           NoWorkingTree,
24
                           BzrCommandError, 
25
                           NoSuchRevision,
26
                           NoRepositoryPresent,
27
                          )
115 by aaron.bentley at utoronto
Import fixes from magnus@therning.org
28
from bzrlib.branch import Branch
147.2.8 by Aaron Bentley
Quieten commits
29
from bzrlib.commit import Commit, NullCommitReporter
147.1.36 by Robert Collins
updates for bzr api changes
30
from bzrlib.commands import Command
147.1.66 by Aaron Bentley
Added prefixes param to specify branches to import
31
from bzrlib.option import _global_option, Option
147.1.64 by Aaron Bentley
Switched to using checkouts instead of branch copies
32
from bzrlib.merge import merge_inner
33
from bzrlib.revision import NULL_REVISION
34
from bzrlib.tree import EmptyTree
147.1.63 by Aaron Bentley
Killed progress bar in tests
35
import bzrlib.ui
147.4.19 by Robert Collins
Update for integration move of read_working_inventory from Branch to WorkingTree.
36
from bzrlib.workingtree import WorkingTree
105 by Aaron Bentley
Fixed NoPyBaz detection
37
from errors import NoPyBaz
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
38
try:
147.2.3 by Aaron Bentley
Fixed import issues w/ PyBaz
39
    import pybaz
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
40
    import pybaz.errors
147.1.14 by Robert Collins
implement a namespace mapper
41
    from pybaz import NameParser as NameParser
147.2.3 by Aaron Bentley
Fixed import issues w/ PyBaz
42
    from pybaz.backends.baz import null_cmd
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
43
except ImportError:
147.2.3 by Aaron Bentley
Fixed import issues w/ PyBaz
44
    raise NoPyBaz
147.2.2 by Aaron Bentley
Committed debugging changes
45
from fai import iter_new_merges, direct_merges
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
46
import tempfile
47
import os
48
import os.path
49
import shutil
50
import bzrlib
51
import bzrlib.trace
52
import bzrlib.merge
97 by aaron.bentley at utoronto
Added now-required imports
53
import bzrlib.inventory
54
import bzrlib.osutils
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
55
import sys
56
import email.Utils
57
from progress import *
58
147.2.8 by Aaron Bentley
Quieten commits
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
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
65
def add_id(files, id=None):
66
    """Adds an explicit id to a list of files.
67
68
    :param files: the name of the file to add an id to
69
    :type files: list of str
70
    :param id: tag one file using the specified id, instead of generating id
71
    :type id: str
72
    """
73
    args = ["add-id"]
74
    if id is not None:
75
        args.extend(["--id", id])
76
    args.extend(files)
77
    return null_cmd(args)
78
147.1.2 by Robert Collins
test empty import and tagged branches
79
saved_dir = None
80
147.1.60 by Aaron Bentley
Stopped using deprecated PyBaz functionality
81
def make_archive(name, location):
82
    pb_location = pybaz.ArchiveLocation(location)
83
    pb_location.create_master(pybaz.Archive(name), 
84
                              pybaz.ArchiveLocationParams())
85
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
86
def test_environ():
87
    """
88
    >>> q = test_environ()
89
    >>> os.path.exists(q)
90
    True
91
    >>> os.path.exists(os.path.join(q, "home", ".arch-params"))
92
    True
93
    >>> teardown_environ(q)
94
    >>> os.path.exists(q)
95
    False
96
    """
147.1.2 by Robert Collins
test empty import and tagged branches
97
    global saved_dir
98
    saved_dir = os.getcwdu()
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
99
    tdir = tempfile.mkdtemp(prefix="testdir-")
100
    os.environ["HOME"] = os.path.join(tdir, "home")
101
    os.mkdir(os.environ["HOME"])
102
    arch_dir = os.path.join(tdir, "archive_dir")
147.1.60 by Aaron Bentley
Stopped using deprecated PyBaz functionality
103
    make_archive("test@example.com", arch_dir)
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
104
    work_dir = os.path.join(tdir, "work_dir")
105
    os.mkdir(work_dir)
106
    os.chdir(work_dir)
107
    pybaz.init_tree(work_dir, "test@example.com/test--test--0")
108
    lib_dir = os.path.join(tdir, "lib_dir")
109
    os.mkdir(lib_dir)
110
    pybaz.register_revision_library(lib_dir)
111
    pybaz.set_my_id("Test User<test@example.org>")
112
    return tdir
113
114
def add_file(path, text, id):
115
    """
116
    >>> q = test_environ()
117
    >>> add_file("path with space", "text", "lalala")
118
    >>> tree = pybaz.tree_root(".")
119
    >>> inv = list(tree.iter_inventory_ids(source=True, both=True))
120
    >>> ("x_lalala", "path with space") in inv
121
    True
122
    >>> teardown_environ(q)
123
    """
124
    file(path, "wb").write(text)
125
    add_id([path], id)
126
127
128
def add_dir(path, id):
129
    """
130
    >>> q = test_environ()
131
    >>> add_dir("path with\(sp) space", "lalala")
132
    >>> tree = pybaz.tree_root(".")
133
    >>> inv = list(tree.iter_inventory_ids(source=True, both=True))
134
    >>> ("x_lalala", "path with\(sp) space") in inv
135
    True
136
    >>> teardown_environ(q)
137
    """
138
    os.mkdir(path)
139
    add_id([path], id)
140
141
def teardown_environ(tdir):
147.1.2 by Robert Collins
test empty import and tagged branches
142
    os.chdir(saved_dir)
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
143
    shutil.rmtree(tdir)
144
145
def timport(tree, summary):
146
    msg = tree.log_message()
147
    msg["summary"] = summary
148
    tree.import_(msg)
149
150
def commit(tree, summary):
151
    """
152
    >>> q = test_environ()
153
    >>> tree = pybaz.tree_root(".")
154
    >>> timport(tree, "import")
155
    >>> commit(tree, "commit")
156
    >>> logs = [str(l.revision) for l in tree.iter_logs()]
157
    >>> len(logs)
158
    2
159
    >>> logs[0]
160
    'test@example.com/test--test--0--base-0'
161
    >>> logs[1]
162
    'test@example.com/test--test--0--patch-1'
163
    >>> teardown_environ(q)
164
    """
165
    msg = tree.log_message()
166
    msg["summary"] = summary
167
    tree.commit(msg)
168
169
def commit_test_revisions():
170
    """
171
    >>> q = test_environ()
172
    >>> commit_test_revisions()
173
    >>> a = pybaz.Archive("test@example.com")
174
    >>> revisions = list(a.iter_revisions("test--test--0"))
175
    >>> len(revisions)
176
    3
177
    >>> str(revisions[2])
178
    'test@example.com/test--test--0--base-0'
179
    >>> str(revisions[1])
180
    'test@example.com/test--test--0--patch-1'
181
    >>> str(revisions[0])
182
    'test@example.com/test--test--0--patch-2'
183
    >>> teardown_environ(q)
184
    """
185
    tree = pybaz.tree_root(".")
186
    add_file("mainfile", "void main(void){}", "mainfile by aaron")
187
    timport(tree, "Created mainfile")
188
    file("mainfile", "wb").write("or something like that")
189
    commit(tree, "altered mainfile")
190
    add_file("ofile", "this is another file", "ofile by aaron")
191
    commit(tree, "altered mainfile")
192
193
194
def commit_more_test_revisions():
195
    """
196
    >>> q = test_environ()
197
    >>> commit_test_revisions()
198
    >>> commit_more_test_revisions()
199
    >>> a = pybaz.Archive("test@example.com")
200
    >>> revisions = list(a.iter_revisions("test--test--0"))
201
    >>> len(revisions)
202
    4
203
    >>> str(revisions[0])
204
    'test@example.com/test--test--0--patch-3'
205
    >>> teardown_environ(q)
206
    """
207
    tree = pybaz.tree_root(".")
208
    add_file("trainfile", "void train(void){}", "trainfile by aaron")
209
    commit(tree, "altered trainfile")
210
211
class NoSuchVersion(Exception):
212
    def __init__(self, version):
213
        Exception.__init__(self, "The version %s does not exist." % version)
214
        self.version = version
215
216
def version_ancestry(version):
217
    """
218
    >>> q = test_environ()
219
    >>> commit_test_revisions()
220
    >>> version = pybaz.Version("test@example.com/test--test--0")
221
    >>> ancestors = version_ancestry(version)
222
    >>> str(ancestors[0])
223
    'test@example.com/test--test--0--base-0'
224
    >>> str(ancestors[1])
225
    'test@example.com/test--test--0--patch-1'
226
    >>> version = pybaz.Version("test@example.com/test--test--0.5")
227
    >>> ancestors = version_ancestry(version)
228
    Traceback (most recent call last):
229
    NoSuchVersion: The version test@example.com/test--test--0.5 does not exist.
230
    >>> teardown_environ(q)
231
    """
232
    try:
233
        revision = version.iter_revisions(reverse=True).next()
147.2.7 by Aaron Bentley
Handle empty versions correctly
234
    except StopIteration:
235
        return ()
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
236
    except:
147.1.2 by Robert Collins
test empty import and tagged branches
237
        print version
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
238
        if not version.exists():
239
            raise NoSuchVersion(version)
240
        else:
241
            raise
242
    ancestors = list(revision.iter_ancestors(metoo=True))
243
    ancestors.reverse()
244
    return ancestors
245
246
def get_last_revision(branch):
221 by abentley
bzrtools 0.1.1 (baz-import patch)
247
    last_patch = branch.last_revision()
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
248
    try:
249
        return arch_revision(last_patch)
250
    except NotArchRevision:
251
        raise UserError(
252
            "Directory \"%s\" already exists, and the last revision is not"
147.2.10 by Aaron Bentley
Improved error handling, esp when invoking on wrong branch
253
            " an Arch revision (%s)" % (branch.base, last_patch))
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
254
147.4.6 by Robert Collins
Fix continuation direct_merges output, and allow reusing history in a version import
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:
147.1.55 by Aaron Bentley
Got imports working, via a big hammer
271
            br_from.bzrdir.clone(to_location, revision_id)
147.4.6 by Robert Collins
Fix continuation direct_merges output, and allow reusing history in a version import
272
        except NoSuchRevision:
273
            rmtree(to_location)
147.1.57 by Aaron Bentley
Cleaned up baz_import and tests
274
            msg = "The branch %s has no revision %s." % (from_location, 
275
                                                         revision_id)
147.4.6 by Robert Collins
Fix continuation direct_merges output, and allow reusing history in a version import
276
            raise UserError(msg)
277
    finally:
278
        br_from.unlock()
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
279
147.4.6 by Robert Collins
Fix continuation direct_merges output, and allow reusing history in a version import
280
def get_remaining_revisions(output_dir, version, reuse_history_from=[]):
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
281
    last_patch = None
282
    old_revno = None
147.4.6 by Robert Collins
Fix continuation direct_merges output, and allow reusing history in a version import
283
    output_exists = os.path.exists(output_dir)
284
    if output_exists:
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
285
        # We are starting from an existing directory, figure out what
286
        # the current version is
147.1.29 by Robert Collins
update to latest bzr api
287
        branch = Branch.open(output_dir)
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
288
        last_patch = get_last_revision(branch)
278 by Aaron Bentley
Handled cases where user supplies empty directories or branches
289
        if last_patch is None:
147.4.35 by Robert Collins
Support importing into empty branches.
290
            if branch.last_revision() != None:
291
                raise NotPreviousImport(branch.base)
292
        elif version is None:
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
293
            version = last_patch.version
294
    elif version is None:
295
        raise UserError("No version specified, and directory does not exist.")
296
297
    try:
298
        ancestors = version_ancestry(version)
147.4.6 by Robert Collins
Fix continuation direct_merges output, and allow reusing history in a version import
299
        if not output_exists and reuse_history_from != []:
300
            for ancestor in reversed(ancestors):
147.4.11 by Robert Collins
tweak to fix reusing history with more than one candidate ancestor
301
                if last_patch is not None:
302
                    # found something to copy
303
                    break
147.4.6 by Robert Collins
Fix continuation direct_merges output, and allow reusing history in a version import
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
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
320
    except NoSuchVersion, e:
147.1.37 by Robert Collins
get all tests passing again, and disable importing the body of continuation log messages
321
        raise UserError(str(e))
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
322
323
    if last_patch:
324
        for i in range(len(ancestors)):
325
            if ancestors[i] == last_patch:
326
                break
327
        else:
328
            raise UserError("Directory \"%s\" already exists, and the last "
329
                "revision (%s) is not in the ancestry of %s" % 
330
                (output_dir, last_patch, version))
331
        # Strip off all of the ancestors which are already present
332
        # And get a directory starting with the latest ancestor
333
        latest_ancestor = ancestors[i]
147.1.29 by Robert Collins
update to latest bzr api
334
        old_revno = Branch.open(output_dir).revno()
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
335
        ancestors = ancestors[i+1:]
336
    return ancestors, old_revno
337
147.4.6 by Robert Collins
Fix continuation direct_merges output, and allow reusing history in a version import
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
###
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
346
###    def __init__(self, output_dir, version, fast=False,
147.4.6 by Robert Collins
Fix continuation direct_merges output, and allow reusing history in a version import
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
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
354
def import_version(output_dir, version, fast=False,
147.4.6 by Robert Collins
Fix continuation direct_merges output, and allow reusing history in a version import
355
                   verbose=False, dry_run=False, max_count=None,
147.1.65 by Aaron Bentley
Support creating repositories instead of standalone branches.
356
                   reuse_history_from=[], standalone=True):
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
357
    """
358
    >>> q = test_environ()
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
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
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
365
    >>> result_path = os.path.join(q, "result")
366
    >>> commit_test_revisions()
367
    >>> version = pybaz.Version("test@example.com/test--test--0.1")
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
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)
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
373
    Traceback (most recent call last):
147.1.45 by Aaron Bentley
Fixed test case
374
    NotPreviousImport: / is not the location of a previous import.
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
375
    >>> import_version(result_path, version, dry_run=True)
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
376
    Traceback (most recent call last):
377
    UserError: The version test@example.com/test--test--0.1 does not exist.
378
    >>> version = pybaz.Version("test@example.com/test--test--0")
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
379
    >>> import_version(result_path, version, dry_run=True) #doctest: +ELLIPSIS
380
    importing test@example.com/test--test--0 into ...
381
    ...
382
    revisions: ..........................................
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
383
    Dry run, not modifying output_dir
384
    Cleaning up
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
385
    >>> import_version(result_path, version) #doctest: +ELLIPSIS
386
    importing test@example.com/test--test--0 into ...
387
    ...
388
    revisions: .....................................................................
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
389
    Cleaning up
390
    Import complete.
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
391
    >>> import_version(result_path, version) #doctest: +ELLIPSIS
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
392
    Tree is up-to-date with test@example.com/test--test--0--patch-2
393
    >>> commit_more_test_revisions()
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
394
    >>> import_version(result_path, version) #doctest: +ELLIPSIS
395
    importing test@example.com/test--test--0 into ...
396
    revisions: ....................................................
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
397
    Cleaning up
398
    Import complete.
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
399
    >>> bzrlib.ui.ui_factory = old_ui
400
    >>> sys.stderr = old_stderr
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
401
    >>> teardown_environ(q)
402
    """
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
403
    progress_bar = bzrlib.ui.ui_factory.nested_progress_bar()
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
404
    try:
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
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
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
417
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
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
426
        if wt is None:
427
            old_basis = EmptyTree()
428
        else:
429
            old_basis = wt.basis_tree()
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
430
        try:
431
            for result in iter_import_version(output_dir, ancestors, tempdir,
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
432
                    pb=progress_bar,
433
                    fast=fast, verbose=verbose, dry_run=dry_run,
147.1.65 by Aaron Bentley
Support creating repositories instead of standalone branches.
434
                    max_count=max_count, standalone=standalone):
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
435
                show_progress(progress_bar, result)
436
            if dry_run:
437
                progress_bar.note('Dry run, not modifying output_dir')
438
                return
439
    
440
            # Update the working tree of the branch
441
            try:
442
                wt = WorkingTree.open(output_dir)
443
            except NoWorkingTree:
444
                wt = None
445
            if wt is not None:
446
                wt.set_last_revision(wt.branch.last_revision())
447
                merge_inner(wt.branch, wt.basis_tree(), old_basis, 
448
                            ignore_zero=True, this_tree=wt)
449
                wt.revert([])
450
    
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
451
        finally:
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
452
            
453
            progress_bar.note('Cleaning up')
454
            shutil.rmtree(tempdir)
455
        progress_bar.note("Import complete.")
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
456
    finally:
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
457
        progress_bar.finished()
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
458
            
278 by Aaron Bentley
Handled cases where user supplies empty directories or branches
459
class UserError(BzrCommandError):
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
460
    def __init__(self, message):
461
        """Exception to throw when a user makes an impossible request
462
        :param message: The message to emit when printing this exception
463
        :type message: string
464
        """
278 by Aaron Bentley
Handled cases where user supplies empty directories or branches
465
        BzrCommandError.__init__(self, message)
466
467
class NotPreviousImport(UserError):
468
    def __init__(self, path):
469
        UserError.__init__(self, "%s is not the location of a previous import."
470
                           % path)
471
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
472
473
def revision_id(arch_revision):
474
    """
475
    Generate a Bzr revision id from an Arch revision id.  'x' in the id
476
    designates a revision imported with an experimental algorithm.  A number
477
    would indicate a particular standardized version.
478
479
    :param arch_revision: The Arch revision to generate an ID for.
480
481
    >>> revision_id(pybaz.Revision("you@example.com/cat--br--0--base-0"))
147.1.2 by Robert Collins
test empty import and tagged branches
482
    'Arch-1:you@example.com%cat--br--0--base-0'
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
483
    """
147.1.2 by Robert Collins
test empty import and tagged branches
484
    return "Arch-1:%s" % str(arch_revision).replace('/', '%')
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
485
486
class NotArchRevision(Exception):
487
    def __init__(self, revision_id):
488
        msg = "The revision id %s does not look like it came from Arch."\
489
            % revision_id
490
        Exception.__init__(self, msg)
491
492
def arch_revision(revision_id):
493
    """
147.1.2 by Robert Collins
test empty import and tagged branches
494
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0"))
495
    Traceback (most recent call last):
496
    NotArchRevision: The revision id Arch-1:jrandom@example.com%test--test--0 does not look like it came from Arch.
497
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--base-5"))
498
    Traceback (most recent call last):
499
    NotArchRevision: The revision id Arch-1:jrandom@example.com%test--test--0--base-5 does not look like it came from Arch.
500
    >>> str(arch_revision("Arch-1:jrandom@example.com%test--test--0--patch-5"))
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
501
    'jrandom@example.com/test--test--0--patch-5'
502
    """
503
    if revision_id is None:
504
        return None
147.1.2 by Robert Collins
test empty import and tagged branches
505
    if revision_id[:7] != 'Arch-1:':
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
506
        raise NotArchRevision(revision_id)
507
    else:
508
        try:
509
            return pybaz.Revision(revision_id[7:].replace('%', '/'))
510
        except pybaz.errors.NamespaceError, e:
511
            raise NotArchRevision(revision_id)
147.1.64 by Aaron Bentley
Switched to using checkouts instead of branch copies
512
513
147.1.65 by Aaron Bentley
Support creating repositories instead of standalone branches.
514
def create_shared_repository(output_dir):
515
    bd = bzrdir.BzrDirMetaFormat1().initialize(output_dir)
516
    bd.create_repository(shared=True)
517
518
def create_branch(output_dir):
519
    os.mkdir(output_dir)
520
    bd = bzrdir.BzrDirMetaFormat1().initialize(output_dir)
521
    return bd.create_branch()
522
523
147.1.64 by Aaron Bentley
Switched to using checkouts instead of branch copies
524
def create_checkout(source, to_location, revision_id=None):
525
    checkout = bzrdir.BzrDirMetaFormat1().initialize(to_location)
526
    bzrlib.branch.BranchReferenceFormat().initialize(checkout, source)
527
    return checkout.create_workingtree(revision_id)
528
529
530
def create_checkout_metadata(source, to_location, revision_id=None):
531
    if revision_id is None:
532
        revision_id = source.last_revision()
533
    wt = create_checkout(source, to_location, NULL_REVISION)
534
    wt.set_last_revision(revision_id)
535
    wt._write_inventory(wt.basis_tree().inventory)
536
    return wt
537
538
147.2.8 by Aaron Bentley
Quieten commits
539
def iter_import_version(output_dir, ancestors, tempdir, pb, fast=False,
147.1.65 by Aaron Bentley
Support creating repositories instead of standalone branches.
540
                        verbose=False, dry_run=False, max_count=None,
541
                        standalone=False):
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
542
    revdir = None
543
544
    # Uncomment this for testing, it basically just has baz2bzr only update
545
    # 5 patches at a time
546
    if max_count:
547
        ancestors = ancestors[:max_count]
548
549
    # Not sure if I want this output. basically it tells you ahead of time
550
    # what it is going to do, but then later it tells you as it is doing it.
551
    # what probably would be best would be to collapse it into ranges, so that
552
    # this gives the simple view, and then later it gives the blow by blow.
553
    #if verbose:
554
    #    print 'Adding the following revisions:'
555
    #    for a in ancestors:
556
    #        print '\t%s' % a
557
558
    previous_version=None
147.1.20 by Robert Collins
handle missing ancestry
559
    missing_ancestor = None
147.1.64 by Aaron Bentley
Switched to using checkouts instead of branch copies
560
    if dry_run:
561
        dry_output_dir = os.path.join(tempdir, 'od')
562
        if os.path.exists(output_dir):
563
            shutil.copytree(output_dir, dry_output_dir)
564
        output_dir = dry_output_dir
565
566
    if os.path.exists(output_dir):
567
        target_branch = Branch.open(output_dir)
568
    else:
147.1.65 by Aaron Bentley
Support creating repositories instead of standalone branches.
569
        if standalone:
147.4.30 by Robert Collins
Merge from ab-baz2bzr
570
            wt = BzrDir.create_standalone_workingtree(output_dir)
147.1.65 by Aaron Bentley
Support creating repositories instead of standalone branches.
571
            target_branch = wt.branch
572
        else:
573
            target_branch = create_branch(output_dir)
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
574
575
    for i in range(len(ancestors)):
576
        revision = ancestors[i]
147.4.5 by Robert Collins
When an import finds a revision it needs already in the branch, just append it to the revision history
577
        rev_id = revision_id(revision)
147.1.3 by Robert Collins
test and deliver basic pending-merges into bzr so that merging is recorded
578
        direct_merges = []
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
579
        if verbose:
580
            version = str(revision.version)
581
            if version != previous_version:
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
582
                pb.note('On version: %s' % version)
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
583
            yield Progress(str(revision.patchlevel), i, len(ancestors))
584
            previous_version = version
585
        else:
586
            yield Progress("revisions", i, len(ancestors))
147.1.64 by Aaron Bentley
Switched to using checkouts instead of branch copies
587
588
        if target_branch.repository.has_revision(rev_id):
589
            target_branch.append_revision(rev_id)
590
            continue
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
591
        if revdir is None:
592
            revdir = os.path.join(tempdir, "rd")
147.1.20 by Robert Collins
handle missing ancestry
593
            try:
147.2.14 by abentley
Allouche: More efficient PyBaz use
594
                tree, baz_inv, log = get_revision(revdir, revision)
147.1.20 by Robert Collins
handle missing ancestry
595
            except pybaz.errors.ExecProblem, e:
596
                if ("%s" % e.args).find('could not connect') == -1:
597
                    raise
598
                missing_ancestor = revision
599
                revdir = None
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
600
                pb.note("unable to access ancestor %s, making into a merge."
147.1.20 by Robert Collins
handle missing ancestry
601
                       % missing_ancestor)
602
                continue
147.1.64 by Aaron Bentley
Switched to using checkouts instead of branch copies
603
            target_tree = create_checkout_metadata(target_branch, revdir)
604
            branch = target_tree.branch
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
605
        else:
606
            old = os.path.join(revdir, ".bzr")
607
            new = os.path.join(tempdir, ".bzr")
608
            os.rename(old, new)
147.2.14 by abentley
Allouche: More efficient PyBaz use
609
            baz_inv, log = apply_revision(tree, revision)
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
610
            os.rename(new, old)
147.1.61 by Aaron Bentley
Stopped using deprecated bzrlib functionality
611
            target_tree = WorkingTree.open(revdir)
612
            branch = target_tree.branch
147.4.2 by Robert Collins
use direct merge log always, whether its a new tree or a patch being applied
613
        # cached so we can delete the log
614
        log_date = log.date
615
        log_summary = log.summary
616
        log_description = log.description
617
        is_continuation = log.continuation_of is not None
618
        log_creator = log.creator
147.4.6 by Robert Collins
Fix continuation direct_merges output, and allow reusing history in a version import
619
        direct_merges = get_direct_merges(revdir, revision, log)
147.4.2 by Robert Collins
use direct merge log always, whether its a new tree or a patch being applied
620
147.1.3 by Robert Collins
test and deliver basic pending-merges into bzr so that merging is recorded
621
        timestamp = email.Utils.mktime_tz(log_date + (0,))
147.2.5 by Aaron Bentley
Handled empty summary lines
622
        if log_summary is None:
623
            log_summary = ""
147.1.37 by Robert Collins
get all tests passing again, and disable importing the body of continuation log messages
624
        # log_descriptions of None and "" are ignored.
625
        if not is_continuation and log_description:
147.2.9 by Aaron Bentley
Fixed log conversion
626
            log_message = "\n".join((log_summary, log_description))
627
        else:
628
            log_message = log_summary
147.1.61 by Aaron Bentley
Stopped using deprecated bzrlib functionality
629
        target_tree.lock_write()
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
630
        branch.lock_write()
631
        try:
147.1.20 by Robert Collins
handle missing ancestry
632
            if missing_ancestor:
633
                # if we want it to be in revision-history, do that here.
147.4.20 by Robert Collins
Branch.commit->WorkingTree.commit
634
                target_tree.add_pending_merge(revision_id(missing_ancestor))
147.1.20 by Robert Collins
handle missing ancestry
635
                missing_ancestor = None
147.4.6 by Robert Collins
Fix continuation direct_merges output, and allow reusing history in a version import
636
            for merged_rev in direct_merges:
147.4.20 by Robert Collins
Branch.commit->WorkingTree.commit
637
                target_tree.add_pending_merge(revision_id(merged_rev))
147.4.19 by Robert Collins
Update for integration move of read_working_inventory from Branch to WorkingTree.
638
            target_tree.set_inventory(baz_inv)
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
639
            commitobj = Commit(reporter=ImportCommitReporter())
147.1.61 by Aaron Bentley
Stopped using deprecated bzrlib functionality
640
            commitobj.commit(working_tree=target_tree,
641
                             message=log_message.decode('ascii', 'replace'), 
147.2.9 by Aaron Bentley
Fixed log conversion
642
                             verbose=False, committer=log_creator,
147.1.64 by Aaron Bentley
Switched to using checkouts instead of branch copies
643
                             timestamp=timestamp, timezone=0, rev_id=rev_id,
147.1.70 by Aaron Bentley
Switched to blacnk branch-nicks
644
                             revprops={})
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
645
        finally:
147.4.19 by Robert Collins
Update for integration move of read_working_inventory from Branch to WorkingTree.
646
            target_tree.unlock()
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
647
            branch.unlock()
648
    yield Progress("revisions", len(ancestors), len(ancestors))
649
147.4.6 by Robert Collins
Fix continuation direct_merges output, and allow reusing history in a version import
650
def get_direct_merges(revdir, revision, log):
651
    continuation = log.continuation_of
652
    previous_version = revision.version
653
    if pybaz.WorkingTree(revdir).tree_version != previous_version:
654
        pybaz.WorkingTree(revdir).set_tree_version(previous_version)
147.1.3 by Robert Collins
test and deliver basic pending-merges into bzr so that merging is recorded
655
    log_path = "%s/{arch}/%s/%s/%s/%s/patch-log/%s" % (revdir, 
656
        revision.category.nonarch, revision.branch.nonarch, 
657
        revision.version.nonarch, revision.archive, revision.patchlevel)
147.2.13 by abentley
Allouche: always use a temp dir on the same filesystem
658
    temp_path = tempfile.mktemp(dir=os.path.dirname(revdir))
147.1.3 by Robert Collins
test and deliver basic pending-merges into bzr so that merging is recorded
659
    os.rename(log_path, temp_path)
147.1.24 by Robert Collins
trim fai cribbage
660
    merges = list(iter_new_merges(revdir, revision.version))
147.4.6 by Robert Collins
Fix continuation direct_merges output, and allow reusing history in a version import
661
    direct = direct_merges(merges, [continuation])
147.1.3 by Robert Collins
test and deliver basic pending-merges into bzr so that merging is recorded
662
    os.rename(temp_path, log_path)
663
    return direct
664
147.1.61 by Aaron Bentley
Stopped using deprecated bzrlib functionality
665
def unlink_unversioned(wt):
666
    for unversioned in wt.extras():
667
        path = wt.abspath(unversioned)
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
668
        if os.path.isdir(path):
669
            shutil.rmtree(path)
670
        else:
671
            os.unlink(path)
672
673
def get_log(tree, revision):
147.2.14 by abentley
Allouche: More efficient PyBaz use
674
    log = pybaz.Patchlog(revision, tree=tree)
133 by Aaron Bentley
Weakened check so baz-import works
675
    assert str(log.revision) == str(revision), (log.revision, revision)
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
676
    return log
677
147.1.6 by Robert Collins
import symlinks (needs symlink enabled bzr)
678
def get_revision(revdir, revision):
147.2.14 by abentley
Allouche: More efficient PyBaz use
679
    tree = revision.get(revdir)
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
680
    log = get_log(tree, revision)
681
    try:
147.2.14 by abentley
Allouche: More efficient PyBaz use
682
        return tree, bzr_inventory_data(tree), log 
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
683
    except BadFileKind, e:
147.1.57 by Aaron Bentley
Cleaned up baz_import and tests
684
        raise UserError("Cannot convert %s because %s is a %s" % 
685
                        (revision,e.path, e.kind))
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
686
687
147.2.14 by abentley
Allouche: More efficient PyBaz use
688
def apply_revision(tree, revision):
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
689
    revision.apply(tree)
690
    log = get_log(tree, revision)
691
    try:
147.1.6 by Robert Collins
import symlinks (needs symlink enabled bzr)
692
        return bzr_inventory_data(tree), log
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
693
    except BadFileKind, e:
147.1.57 by Aaron Bentley
Cleaned up baz_import and tests
694
        raise UserError("Cannot convert %s because %s is a %s" % 
695
                        (revision,e.path, e.kind))
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
696
697
698
class BadFileKind(Exception):
699
    """The file kind is not permitted in bzr inventories"""
700
    def __init__(self, tree_root, path, kind):
701
        self.tree_root = tree_root
702
        self.path = path
703
        self.kind = kind
704
        Exception.__init__(self, "File %s is of forbidden type %s" %
705
                           (os.path.join(tree_root, path), kind))
706
147.1.29 by Robert Collins
update to latest bzr api
707
147.1.6 by Robert Collins
import symlinks (needs symlink enabled bzr)
708
def bzr_inventory_data(tree):
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
709
    inv_iter = tree.iter_inventory_ids(source=True, both=True)
710
    inv_map = {}
106 by Aaron Bentley
Used limited url-encoding for file ids
711
    for arch_id, path in inv_iter:
147.4.1 by Robert Collins
test escaping of file ids is working
712
        bzr_file_id = map_file_id(arch_id)
106 by Aaron Bentley
Used limited url-encoding for file ids
713
        inv_map[path] = bzr_file_id 
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
714
715
    bzr_inv = []
716
    for path, file_id in inv_map.iteritems():
717
        full_path = os.path.join(tree, path)
718
        kind = bzrlib.osutils.file_kind(full_path)
147.1.6 by Robert Collins
import symlinks (needs symlink enabled bzr)
719
        if kind not in ("file", "directory", "symlink"):
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
720
            raise BadFileKind(tree, path, kind)
721
        parent_dir = os.path.dirname(path)
722
        if parent_dir != "":
723
            parent_id = inv_map[parent_dir]
724
        else:
725
            parent_id = bzrlib.inventory.ROOT_ID
726
        bzr_inv.append((path, file_id, parent_id, kind))
727
    bzr_inv.sort()
728
    return bzr_inv
729
147.1.36 by Robert Collins
updates for bzr api changes
730
_global_option('max-count', type = int)
147.1.11 by Robert Collins
move baz-import to baz-import-branch
731
class cmd_baz_import_branch(Command):
342 by Aaron Bentley
Tagged BZRTOOLS commands to reduce confusion
732
    """Import an Arch or Baz branch into a bzr branch.  <BZRTOOLS>"""
147.4.13 by Robert Collins
add a reuse_history option to the baz-import-branch command too.
733
    takes_args = ['to_location', 'from_branch?', 'reuse_history*']
147.2.10 by Aaron Bentley
Improved error handling, esp when invoking on wrong branch
734
    takes_options = ['verbose', 'max-count']
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
735
147.1.6 by Robert Collins
import symlinks (needs symlink enabled bzr)
736
    def run(self, to_location, from_branch=None, fast=False, max_count=None,
147.4.13 by Robert Collins
add a reuse_history option to the baz-import-branch command too.
737
            verbose=False, dry_run=False, reuse_history_list=[]):
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
738
        to_location = os.path.realpath(str(to_location))
739
        if from_branch is not None:
740
            try:
100 by Aaron Bentley
Fixed up the baz-import plugin
741
                from_branch = pybaz.Version(from_branch)
83 by Aaron Bentley
Moved most baz2bzr code to baz_import, added Python plugin
742
            except pybaz.errors.NamespaceError:
743
                print "%s is not a valid Arch branch." % from_branch
744
                return 1
147.4.13 by Robert Collins
add a reuse_history option to the baz-import-branch command too.
745
        if reuse_history_list is None:
746
            reuse_history_list = []
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
747
        import_version(to_location, from_branch, 
147.1.44 by Aaron Bentley
Merged the bzrtools mainline
748
                       max_count=max_count, 
749
                       reuse_history_from=reuse_history_list)
750
751
752
class NotInABranch(Exception):
753
    def __init__(self, path):
754
        Exception.__init__(self, "%s is not in a branch." % path)
755
        self.path = path
147.1.11 by Robert Collins
move baz-import to baz-import-branch
756
147.1.29 by Robert Collins
update to latest bzr api
757
147.1.11 by Robert Collins
move baz-import to baz-import-branch
758
class cmd_baz_import(Command):
364 by Aaron Bentley
Tweaked descriptions
759
    """Import an Arch or Baz archive into a bzr repository.  <BZRTOOLS>
147.1.66 by Aaron Bentley
Added prefixes param to specify branches to import
760
761
    This command should be used on local archives (or mirrors) only.  It is
762
    quite slow on remote archives.
147.4.8 by Robert Collins
Enable reuse of history on archive imports, just append the history locations to the command line
763
    
764
    reuse_history allows you to specify any previous imports you 
765
    have done of different archives, which this archive has branches
766
    tagged from. This will dramatically reduce the time to convert 
767
    the archive as it will not have to convert the history already
768
    converted in that other branch.
147.1.66 by Aaron Bentley
Added prefixes param to specify branches to import
769
770
    If you specify prefixes, only branches whose names start with that prefix
771
    will be imported.  Skipped branches will be listed, so you can import any
772
    branches you missed by accident.  Here's an example of doing a partial
773
    import from thelove@canonical.com:
774
    bzr baz-import thelove thelove@canonical.com --prefixes dists:talloc-except
147.4.8 by Robert Collins
Enable reuse of history on archive imports, just append the history locations to the command line
775
    """
776
    takes_args = ['to_root_dir', 'from_archive', 'reuse_history*']
147.1.66 by Aaron Bentley
Added prefixes param to specify branches to import
777
    takes_options = ['verbose', Option('prefixes', type=str,
778
                     help="Prefixes of branches to import, colon-separated")]
147.1.11 by Robert Collins
move baz-import to baz-import-branch
779
147.4.8 by Robert Collins
Enable reuse of history on archive imports, just append the history locations to the command line
780
    def run(self, to_root_dir, from_archive, verbose=False,
147.1.66 by Aaron Bentley
Added prefixes param to specify branches to import
781
            reuse_history_list=[], prefixes=None):
147.4.9 by Robert Collins
But do not require a history location.
782
        if reuse_history_list is None:
783
            reuse_history_list = []
147.1.18 by Robert Collins
baz-import twice should work
784
        to_root = str(os.path.realpath(to_root_dir))
147.1.12 by Robert Collins
create the output directory
785
        if not os.path.exists(to_root):
786
            os.mkdir(to_root)
147.1.66 by Aaron Bentley
Added prefixes param to specify branches to import
787
        if prefixes is not None:
788
            prefixes = prefixes.split(':')
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
789
        import_archive(to_root, from_archive, verbose,
147.1.66 by Aaron Bentley
Added prefixes param to specify branches to import
790
                       reuse_history_list, prefixes=prefixes)
147.4.8 by Robert Collins
Enable reuse of history on archive imports, just append the history locations to the command line
791
792
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
793
def import_archive(to_root, from_archive, verbose,
147.1.66 by Aaron Bentley
Added prefixes param to specify branches to import
794
                   reuse_history_from=[], standalone=False,
795
                   prefixes=None):
796
    def selected(version):
797
        if prefixes is None:
798
            return True
799
        else:
800
            for prefix in prefixes:
801
                if version.nonarch.startswith(prefix):
802
                    return True
803
            return False
147.4.8 by Robert Collins
Enable reuse of history on archive imports, just append the history locations to the command line
804
    real_to = os.path.realpath(to_root)
805
    history_locations = [real_to] + reuse_history_from
147.1.65 by Aaron Bentley
Support creating repositories instead of standalone branches.
806
    if standalone is False:
807
        try:
808
            bd = BzrDir.open(to_root)
809
            bd.find_repository()
810
        except NotBranchError:
811
            create_shared_repository(to_root)
812
        except NoRepositoryPresent:
813
            raise BzrCommandError("Can't create repository at existing branch.")
326 by Aaron Bentley
Initial nested progressbar work. (Need console fix)
814
    versions = list(pybaz.Archive(str(from_archive)).iter_versions())
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
815
    progress_bar = bzrlib.ui.ui_factory.nested_progress_bar()
326 by Aaron Bentley
Initial nested progressbar work. (Need console fix)
816
    try:
817
        for num, version in enumerate(versions):
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
818
            progress_bar.update("Branch", num, len(versions))
326 by Aaron Bentley
Initial nested progressbar work. (Need console fix)
819
            if not selected(version):
820
                print "Skipping %s" % version
821
                continue
822
            target = os.path.join(to_root, map_namespace(version))
823
            if not os.path.exists(os.path.dirname(target)):
824
                os.makedirs(os.path.dirname(target))
825
            try:
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
826
                import_version(target, version,
326 by Aaron Bentley
Initial nested progressbar work. (Need console fix)
827
                               reuse_history_from=reuse_history_from, 
828
                               standalone=standalone)
829
            except pybaz.errors.ExecProblem,e:
830
                if str(e).find('The requested revision cannot be built.') != -1:
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
831
                    progress_bar.note(
832
                        "Skipping version %s as it cannot be built due"
833
                        " to a missing parent archive." % version)
326 by Aaron Bentley
Initial nested progressbar work. (Need console fix)
834
                else:
835
                    raise
836
            except UserError, e:
837
                if str(e).find('already exists, and the last revision ') != -1:
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
838
                    progress_bar.note(
839
                        "Skipping version %s as it has had commits made"
840
                        " since it was converted to bzr." % version)
326 by Aaron Bentley
Initial nested progressbar work. (Need console fix)
841
                else:
842
                    raise
843
    finally:
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
844
        progress_bar.finished()
845
147.1.29 by Robert Collins
update to latest bzr api
846
147.1.13 by Robert Collins
create the output directory
847
def map_namespace(a_version):
147.1.14 by Robert Collins
implement a namespace mapper
848
    a_version = pybaz.Version("%s" % a_version)
849
    parser = NameParser(a_version)
850
    version = parser.get_version()
851
    branch = parser.get_branch()
852
    category = parser.get_category()
853
    if branch is None or branch == '':
854
        branch = "+trunk"
855
    if version == '0':
856
        return "%s/%s" % (category, branch)
857
    return "%s/%s/%s" % (category, version, branch)
147.4.1 by Robert Collins
test escaping of file ids is working
858
147.4.34 by Robert Collins
Use nested progress bars for baz-import.
859
147.4.1 by Robert Collins
test escaping of file ids is working
860
def map_file_id(file_id):
861
    """Convert a baz file id to a bzr one."""
862
    return file_id.replace('%', '%25').replace('/', '%2f')