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