1
# Copyright (C) 2008 Canonical Ltd
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.
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.
13
# You should have received a copy of the GNU General Public License
14
# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
"""Fastimport/fastexport commands."""
18
from __future__ import absolute_import
20
from ... import controldir
21
from ...commands import Command
22
from ...option import Option, RegistryOption
31
def _run(source, processor_factory, verbose=False, user_map=None, **kwargs):
32
"""Create and run a processor.
34
:param source: a filename or '-' for standard input. If the
35
filename ends in .gz, it will be opened as a gzip file and
36
the stream will be implicitly uncompressed
37
:param processor_factory: a callable for creating a processor
38
:param user_map: if not None, the file containing the user map.
40
from fastimport.errors import ParsingError
41
from ...errors import BzrCommandError
42
from fastimport import parser
43
stream = _get_source_stream(source)
44
user_mapper = _get_user_mapper(user_map)
45
proc = processor_factory(verbose=verbose, **kwargs)
46
p = parser.ImportParser(stream, verbose=verbose, user_mapper=user_mapper)
48
return proc.process(p.iter_commands)
49
except ParsingError as e:
50
raise BzrCommandError("%d: Parse error: %s" % (e.lineno, e))
53
def _get_source_stream(source):
54
if source == '-' or source is None:
56
stream = helpers.binary_stream(sys.stdin)
57
elif source.endswith('.gz'):
59
stream = gzip.open(source, "rb")
61
stream = open(source, "rb")
65
def _get_user_mapper(filename):
66
from . import user_mapper
72
return user_mapper.UserMapper(lines)
75
class cmd_fast_import(Command):
76
"""Backend for fast Bazaar data importers.
78
This command reads a mixed command/data stream and creates
79
branches in a Bazaar repository accordingly. The preferred
82
bzr fast-import project.fi project.bzr
84
Numerous commands are provided for generating a fast-import file
86
To specify standard input as the input stream, use a
87
source name of '-' (instead of project.fi). If the source name
88
ends in '.gz', it is assumed to be compressed in gzip format.
90
project.bzr will be created if it doesn't exist. If it exists
91
already, it should be empty or be an existing Bazaar repository
92
or branch. If not specified, the current directory is assumed.
94
fast-import will intelligently select the format to use when
95
creating a repository or branch. If you are running Bazaar 1.17
96
up to Bazaar 2.0, the default format for Bazaar 2.x ("2a") is used.
97
Otherwise, the current default format ("pack-0.92" for Bazaar 1.x)
98
is used. If you wish to specify a custom format, use the `--format`
103
To maintain backwards compatibility, fast-import lets you
104
create the target repository or standalone branch yourself.
105
It is recommended though that you let fast-import create
106
these for you instead.
108
:Branch mapping rules:
110
Git reference names are mapped to Bazaar branch names as follows:
112
* refs/heads/foo is mapped to foo
113
* refs/remotes/origin/foo is mapped to foo.remote
114
* refs/tags/foo is mapped to foo.tag
115
* */master is mapped to trunk, trunk.remote, etc.
116
* */trunk is mapped to git-trunk, git-trunk.remote, etc.
118
:Branch creation rules:
120
When a shared repository is created or found at the destination,
121
branches are created inside it. In the simple case of a single
122
branch (refs/heads/master) inside the input file, the branch is
125
When a standalone branch is found at the destination, the trunk
126
is imported there and warnings are output about any other branches
127
found in the input file.
129
When a branch in a shared repository is found at the destination,
130
that branch is made the trunk and other branches, if any, are
131
created in sister directories.
133
:Working tree updates:
135
The working tree is generated for the trunk branch. If multiple
136
branches are created, a message is output on completion explaining
137
how to create the working trees for other branches.
141
The fast-export-from-xxx commands typically call more advanced
142
xxx-fast-export scripts. You are welcome to use the advanced
143
scripts if you prefer.
145
If you wish to write a custom exporter for your project, see
146
http://bazaar-vcs.org/BzrFastImport for the detailed protocol
147
specification. In many cases, exporters can be written quite
148
quickly using whatever scripting/programming language you like.
152
Some source repositories store just the user name while Bazaar
153
prefers a full email address. You can adjust user-ids while
154
importing by using the --user-map option. The argument is a
155
text file with lines in the format::
159
Blank lines and lines beginning with # are ignored.
160
If old-id has the special value '@', then users without an
161
email address will get one created by using the matching new-id
162
as the domain, unless a more explicit address is given for them.
163
For example, given the user-map of::
166
bill = William Jones <bill@example.com>
168
then user-ids are mapped as follows::
170
maria => maria <maria@example.com>
171
bill => William Jones <bill@example.com>
175
User mapping is supported by both the fast-import and
176
fast-import-filter commands.
180
As some exporters (like git-fast-export) reuse blob data across
181
commits, fast-import makes two passes over the input file by
182
default. In the first pass, it collects data about what blobs are
183
used when, along with some other statistics (e.g. total number of
184
commits). In the second pass, it generates the repository and
189
The initial pass isn't done if the --info option is used
190
to explicitly pass in information about the input stream.
191
It also isn't done if the source is standard input. In the
192
latter case, memory consumption may be higher than otherwise
193
because some blobs may be kept in memory longer than necessary.
195
:Restarting an import:
197
At checkpoints and on completion, the commit-id -> revision-id
198
map is saved to a file called 'fastimport-id-map' in the control
199
directory for the repository (e.g. .bzr/repository). If the import
200
is interrupted or unexpectedly crashes, it can be started again
201
and this file will be used to skip over already loaded revisions.
202
As long as subsequent exports from the original source begin
203
with exactly the same revisions, you can use this feature to
204
maintain a mirror of a repository managed by a foreign tool.
205
If and when Bazaar is used to manage the repository, this file
206
can be safely deleted.
210
Import a Subversion repository into Bazaar::
212
svn-fast-export /svn/repo/path > project.fi
213
bzr fast-import project.fi project.bzr
215
Import a CVS repository into Bazaar::
217
cvs2git /cvs/repo/path > project.fi
218
bzr fast-import project.fi project.bzr
220
Import a Git repository into Bazaar::
223
git fast-export --all > project.fi
224
bzr fast-import project.fi project.bzr
226
Import a Mercurial repository into Bazaar::
229
hg fast-export > project.fi
230
bzr fast-import project.fi project.bzr
232
Import a Darcs repository into Bazaar::
235
darcs-fast-export > project.fi
236
bzr fast-import project.fi project.bzr
239
_see_also = ['fast-export', 'fast-import-filter', 'fast-import-info']
240
takes_args = ['source', 'destination?']
241
takes_options = ['verbose',
242
Option('user-map', type=str,
243
help="Path to file containing a map of user-ids.",
245
Option('info', type=str,
246
help="Path to file containing caching hints.",
249
help="Update all working trees, not just trunk's.",
251
Option('count', type=int,
252
help="Import this many revisions then exit.",
254
Option('checkpoint', type=int,
255
help="Checkpoint automatically every N revisions."
256
" The default is 10000.",
258
Option('autopack', type=int,
259
help="Pack every N checkpoints. The default is 4.",
261
Option('inv-cache', type=int,
262
help="Number of inventories to cache.",
264
RegistryOption.from_kwargs('mode',
265
'The import algorithm to use.',
266
title='Import Algorithm',
267
default='Use the preferred algorithm (inventory deltas).',
268
experimental="Enable experimental features.",
269
value_switches=True, enum_switch=False,
271
Option('import-marks', type=str,
272
help="Import marks from file."
274
Option('export-marks', type=str,
275
help="Export marks to file."
277
RegistryOption('format',
278
help='Specify a format for the created repository. See'
279
' "bzr help formats" for details.',
281
'breezy.controldir', 'format_registry'),
282
converter=lambda name: controldir.format_registry.make_controldir(
284
value_switches=False, title='Repository format'),
287
def run(self, source, destination='.', verbose=False, info=None,
288
trees=False, count=-1, checkpoint=10000, autopack=4, inv_cache=-1,
289
mode=None, import_marks=None, export_marks=None, format=None,
292
from .processors import generic_processor
293
from .helpers import (
294
open_destination_directory,
296
control = open_destination_directory(destination, format=format)
298
# If an information file was given and the source isn't stdin,
299
# generate the information by reading the source file as a first pass
300
if info is None and source != '-':
301
info = self._generate_info(source)
310
'checkpoint': checkpoint,
311
'autopack': autopack,
312
'inv-cache': inv_cache,
314
'import-marks': import_marks,
315
'export-marks': export_marks,
317
return _run(source, generic_processor.GenericProcessor,
318
bzrdir=control, params=params, verbose=verbose,
321
def _generate_info(self, source):
322
from io import StringIO
323
from fastimport import parser
324
from fastimport.errors import ParsingError
325
from ...errors import BzrCommandError
326
from fastimport.processors import info_processor
327
stream = _get_source_stream(source)
330
proc = info_processor.InfoProcessor(verbose=True, outf=output)
331
p = parser.ImportParser(stream)
333
return_code = proc.process(p.iter_commands)
334
except ParsingError as e:
335
raise BzrCommandError("%d: Parse error: %s" % (e.lineno, e))
336
lines = output.getvalue().splitlines()
343
class cmd_fast_export(Command):
344
"""Generate a fast-import stream from a Bazaar branch.
346
This program generates a stream from a Bazaar branch in fast-import
347
format used by tools such as bzr fast-import, git-fast-import and
350
It takes two optional arguments: the source bzr branch to export and
351
the destination to write the file to write the fastimport stream to.
353
If no source is specified, it will search for a branch in the
356
If no destination is given or the destination is '-', standard output
357
is used. Otherwise, the destination is the name of a file. If the
358
destination ends in '.gz', the output will be compressed into gzip
363
Recent versions of the fast-import specification support features
364
that allow effective round-tripping most of the metadata in Bazaar
365
branches. As such, fast-exporting a branch and fast-importing the data
366
produced will create a new repository with roughly equivalent history, i.e.
367
"bzr log -v -p --include-merges --forward" on the old branch and
368
new branch should produce similar, if not identical, results.
372
Be aware that the new repository may appear to have similar history
373
but internally it is quite different with new revision-ids and
374
file-ids assigned. As a consequence, the ability to easily merge
375
with branches based on the old repository is lost. Depending on your
376
reasons for producing a new repository, this may or may not be an
381
fast-export can use the following "extended features" to
382
produce a richer data stream:
384
* *multiple-authors* - if a commit has multiple authors (as commonly
385
occurs in pair-programming), all authors will be included in the
386
output, not just the first author
388
* *commit-properties* - custom metadata per commit that Bazaar stores
389
in revision properties (e.g. branch-nick and bugs fixed by this
390
change) will be included in the output.
392
* *empty-directories* - directories, even the empty ones, will be
393
included in the output.
395
To disable these features and produce output acceptable to git 1.6,
396
use the --plain option. To enable these features, use --no-plain.
397
Currently, --plain is the default but that will change in the near
398
future once the feature names and definitions are formally agreed
399
to by the broader fast-import developer community.
401
Git has stricter naming rules for tags and fast-export --plain
402
will skip tags which can't be imported into git. To replace characters
403
unsupported in git with an underscore instead, specify
408
It is sometimes convenient to simply truncate the revision history at a
409
certain point. The --baseline option, to be used in conjunction with -r,
410
emits a baseline commit containing the state of the entire source tree at
411
the first requested revision. This allows a user to produce a tree
412
identical to the original without munging multiple exports.
416
To produce data destined for import into Bazaar::
418
bzr fast-export --no-plain my-bzr-branch my.fi.gz
420
To produce data destined for Git 1.6::
422
bzr fast-export --plain my-bzr-branch my.fi
424
To import several unmerged but related branches into the same repository,
425
use the --{export,import}-marks options, and specify a name for the git
428
bzr fast-export --export-marks=marks.bzr project.dev |
429
GIT_DIR=project/.git git-fast-import --export-marks=marks.git
431
bzr fast-export --import-marks=marks.bzr -b other project.other |
432
GIT_DIR=project/.git git-fast-import --import-marks=marks.git
434
If you get a "Missing space after source" error from git-fast-import,
435
see the top of the commands.py module for a work-around.
437
Since bzr uses per-branch tags and git/hg use per-repo tags, the
438
way bzr fast-export presently emits tags (unconditional reset &
439
new ref) may result in clashes when several different branches
440
are imported into single git/hg repo. If this occurs, use the
441
bzr fast-export option --no-tags during the export of one or more
442
branches to avoid the issue.
445
_see_also = ['fast-import', 'fast-import-filter']
446
takes_args = ['source?', 'destination?']
447
takes_options = ['verbose', 'revision',
448
Option('git-branch', short_name='b', type=str,
450
help='Name of the git branch to create (default=master).'
452
Option('checkpoint', type=int, argname='N',
453
help="Checkpoint every N revisions (default=10000)."
455
Option('marks', type=str, argname='FILE',
456
help="Import marks from and export marks to file."
458
Option('import-marks', type=str, argname='FILE',
459
help="Import marks from file."
461
Option('export-marks', type=str, argname='FILE',
462
help="Export marks to file."
465
help="Exclude metadata to maximise interoperability."
467
Option('rewrite-tag-names',
468
help="Replace characters invalid in git with '_'"
469
" (plain mode only).",
472
help="Export an 'absolute' baseline commit prior to"
473
"the first relative commit",
476
help="Don't export tags"
479
encoding_type = 'exact'
481
def run(self, source=None, destination=None, verbose=False,
482
git_branch="master", checkpoint=10000, marks=None,
483
import_marks=None, export_marks=None, revision=None,
484
plain=True, rewrite_tag_names=False, no_tags=False, baseline=False):
486
from ...branch import Branch
487
from . import exporter
490
import_marks = export_marks = marks
495
branch = Branch.open_containing(source)[0]
496
outf = exporter._get_output_stream(destination)
497
exporter = exporter.BzrFastExporter(
499
outf=outf, ref=b"refs/heads/%s" % git_branch.encode('utf-8'),
500
checkpoint=checkpoint, import_marks_file=import_marks,
501
export_marks_file=export_marks, revision=revision, verbose=verbose,
502
plain_format=plain, rewrite_tags=rewrite_tag_names,
503
no_tags=no_tags, baseline=baseline)
504
return exporter.run()