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