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