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 ... import controldir
19
from ...commands import Command
20
from ...option import Option, RegistryOption
29
def _run(source, processor_factory, verbose=False, user_map=None, **kwargs):
30
"""Create and run a processor.
32
:param source: a filename or '-' for standard input. If the
33
filename ends in .gz, it will be opened as a gzip file and
34
the stream will be implicitly uncompressed
35
:param processor_factory: a callable for creating a processor
36
:param user_map: if not None, the file containing the user map.
38
from fastimport.errors import ParsingError
39
from ...errors import CommandError
40
from fastimport import parser
41
stream = _get_source_stream(source)
42
user_mapper = _get_user_mapper(user_map)
43
proc = processor_factory(verbose=verbose, **kwargs)
44
p = parser.ImportParser(stream, verbose=verbose, user_mapper=user_mapper)
46
return proc.process(p.iter_commands)
47
except ParsingError as e:
48
raise CommandError("%d: Parse error: %s" % (e.lineno, e))
51
def _get_source_stream(source):
52
if source == '-' or source is None:
54
stream = helpers.binary_stream(sys.stdin)
55
elif source.endswith('.gz'):
57
stream = gzip.open(source, "rb")
59
stream = open(source, "rb")
63
def _get_user_mapper(filename):
64
from . import user_mapper
70
return user_mapper.UserMapper(lines)
73
class cmd_fast_import(Command):
74
"""Backend for fast Bazaar data importers.
76
This command reads a mixed command/data stream and creates
77
branches in a Bazaar repository accordingly. The preferred
80
bzr fast-import project.fi project.bzr
82
Numerous commands are provided for generating a fast-import file
84
To specify standard input as the input stream, use a
85
source name of '-' (instead of project.fi). If the source name
86
ends in '.gz', it is assumed to be compressed in gzip format.
88
project.bzr will be created if it doesn't exist. If it exists
89
already, it should be empty or be an existing Bazaar repository
90
or branch. If not specified, the current directory is assumed.
92
fast-import will intelligently select the format to use when
93
creating a repository or branch. If you are running Bazaar 1.17
94
up to Bazaar 2.0, the default format for Bazaar 2.x ("2a") is used.
95
Otherwise, the current default format ("pack-0.92" for Bazaar 1.x)
96
is used. If you wish to specify a custom format, use the `--format`
101
To maintain backwards compatibility, fast-import lets you
102
create the target repository or standalone branch yourself.
103
It is recommended though that you let fast-import create
104
these for you instead.
106
:Branch mapping rules:
108
Git reference names are mapped to Bazaar branch names as follows:
110
* refs/heads/foo is mapped to foo
111
* refs/remotes/origin/foo is mapped to foo.remote
112
* refs/tags/foo is mapped to foo.tag
113
* */master is mapped to trunk, trunk.remote, etc.
114
* */trunk is mapped to git-trunk, git-trunk.remote, etc.
116
:Branch creation rules:
118
When a shared repository is created or found at the destination,
119
branches are created inside it. In the simple case of a single
120
branch (refs/heads/master) inside the input file, the branch is
123
When a standalone branch is found at the destination, the trunk
124
is imported there and warnings are output about any other branches
125
found in the input file.
127
When a branch in a shared repository is found at the destination,
128
that branch is made the trunk and other branches, if any, are
129
created in sister directories.
131
:Working tree updates:
133
The working tree is generated for the trunk branch. If multiple
134
branches are created, a message is output on completion explaining
135
how to create the working trees for other branches.
139
The fast-export-from-xxx commands typically call more advanced
140
xxx-fast-export scripts. You are welcome to use the advanced
141
scripts if you prefer.
143
If you wish to write a custom exporter for your project, see
144
http://bazaar-vcs.org/BzrFastImport for the detailed protocol
145
specification. In many cases, exporters can be written quite
146
quickly using whatever scripting/programming language you like.
150
Some source repositories store just the user name while Bazaar
151
prefers a full email address. You can adjust user-ids while
152
importing by using the --user-map option. The argument is a
153
text file with lines in the format::
157
Blank lines and lines beginning with # are ignored.
158
If old-id has the special value '@', then users without an
159
email address will get one created by using the matching new-id
160
as the domain, unless a more explicit address is given for them.
161
For example, given the user-map of::
164
bill = William Jones <bill@example.com>
166
then user-ids are mapped as follows::
168
maria => maria <maria@example.com>
169
bill => William Jones <bill@example.com>
173
User mapping is supported by both the fast-import and
174
fast-import-filter commands.
178
As some exporters (like git-fast-export) reuse blob data across
179
commits, fast-import makes two passes over the input file by
180
default. In the first pass, it collects data about what blobs are
181
used when, along with some other statistics (e.g. total number of
182
commits). In the second pass, it generates the repository and
187
The initial pass isn't done if the --info option is used
188
to explicitly pass in information about the input stream.
189
It also isn't done if the source is standard input. In the
190
latter case, memory consumption may be higher than otherwise
191
because some blobs may be kept in memory longer than necessary.
193
:Restarting an import:
195
At checkpoints and on completion, the commit-id -> revision-id
196
map is saved to a file called 'fastimport-id-map' in the control
197
directory for the repository (e.g. .bzr/repository). If the import
198
is interrupted or unexpectedly crashes, it can be started again
199
and this file will be used to skip over already loaded revisions.
200
As long as subsequent exports from the original source begin
201
with exactly the same revisions, you can use this feature to
202
maintain a mirror of a repository managed by a foreign tool.
203
If and when Bazaar is used to manage the repository, this file
204
can be safely deleted.
208
Import a Subversion repository into Bazaar::
210
svn-fast-export /svn/repo/path > project.fi
211
bzr fast-import project.fi project.bzr
213
Import a CVS repository into Bazaar::
215
cvs2git /cvs/repo/path > project.fi
216
bzr fast-import project.fi project.bzr
218
Import a Git repository into Bazaar::
221
git fast-export --all > project.fi
222
bzr fast-import project.fi project.bzr
224
Import a Mercurial repository into Bazaar::
227
hg fast-export > project.fi
228
bzr fast-import project.fi project.bzr
230
Import a Darcs repository into Bazaar::
233
darcs-fast-export > project.fi
234
bzr fast-import project.fi project.bzr
237
_see_also = ['fast-export', 'fast-import-filter', 'fast-import-info']
238
takes_args = ['source', 'destination?']
239
takes_options = ['verbose',
240
Option('user-map', type=str,
241
help="Path to file containing a map of user-ids.",
243
Option('info', type=str,
244
help="Path to file containing caching hints.",
247
help="Update all working trees, not just trunk's.",
249
Option('count', type=int,
250
help="Import this many revisions then exit.",
252
Option('checkpoint', type=int,
253
help="Checkpoint automatically every N revisions."
254
" The default is 10000.",
256
Option('autopack', type=int,
257
help="Pack every N checkpoints. The default is 4.",
259
Option('inv-cache', type=int,
260
help="Number of inventories to cache.",
262
RegistryOption.from_kwargs('mode',
263
'The import algorithm to use.',
264
title='Import Algorithm',
265
default='Use the preferred algorithm (inventory deltas).',
266
experimental="Enable experimental features.",
267
value_switches=True, enum_switch=False,
269
Option('import-marks', type=str,
270
help="Import marks from file."
272
Option('export-marks', type=str,
273
help="Export marks to file."
275
RegistryOption('format',
276
help='Specify a format for the created repository. See'
277
' "bzr help formats" for details.',
279
'breezy.controldir', 'format_registry'),
280
converter=lambda name: controldir.format_registry.make_controldir(
282
value_switches=False, title='Repository format'),
285
def run(self, source, destination='.', verbose=False, info=None,
286
trees=False, count=-1, checkpoint=10000, autopack=4, inv_cache=-1,
287
mode=None, import_marks=None, export_marks=None, format=None,
290
from .processors import generic_processor
291
from .helpers import (
292
open_destination_directory,
294
control = open_destination_directory(destination, format=format)
296
# If an information file was given and the source isn't stdin,
297
# generate the information by reading the source file as a first pass
298
if info is None and source != '-':
299
info = self._generate_info(source)
308
'checkpoint': checkpoint,
309
'autopack': autopack,
310
'inv-cache': inv_cache,
312
'import-marks': import_marks,
313
'export-marks': export_marks,
315
return _run(source, generic_processor.GenericProcessor,
316
bzrdir=control, params=params, verbose=verbose,
319
def _generate_info(self, source):
320
from io import StringIO
321
from fastimport import parser
322
from fastimport.errors import ParsingError
323
from ...errors import CommandError
324
from fastimport.processors import info_processor
325
stream = _get_source_stream(source)
328
proc = info_processor.InfoProcessor(verbose=True, outf=output)
329
p = parser.ImportParser(stream)
331
return_code = proc.process(p.iter_commands)
332
except ParsingError as e:
333
raise CommandError("%d: Parse error: %s" % (e.lineno, e))
334
lines = output.getvalue().splitlines()
341
class cmd_fast_export(Command):
342
"""Generate a fast-import stream from a Bazaar branch.
344
This program generates a stream from a Bazaar branch in fast-import
345
format used by tools such as bzr fast-import, git-fast-import and
348
It takes two optional arguments: the source bzr branch to export and
349
the destination to write the file to write the fastimport stream to.
351
If no source is specified, it will search for a branch in the
354
If no destination is given or the destination is '-', standard output
355
is used. Otherwise, the destination is the name of a file. If the
356
destination ends in '.gz', the output will be compressed into gzip
361
Recent versions of the fast-import specification support features
362
that allow effective round-tripping most of the metadata in Bazaar
363
branches. As such, fast-exporting a branch and fast-importing the data
364
produced will create a new repository with roughly equivalent history, i.e.
365
"bzr log -v -p --include-merges --forward" on the old branch and
366
new branch should produce similar, if not identical, results.
370
Be aware that the new repository may appear to have similar history
371
but internally it is quite different with new revision-ids and
372
file-ids assigned. As a consequence, the ability to easily merge
373
with branches based on the old repository is lost. Depending on your
374
reasons for producing a new repository, this may or may not be an
379
fast-export can use the following "extended features" to
380
produce a richer data stream:
382
* *multiple-authors* - if a commit has multiple authors (as commonly
383
occurs in pair-programming), all authors will be included in the
384
output, not just the first author
386
* *commit-properties* - custom metadata per commit that Bazaar stores
387
in revision properties (e.g. branch-nick and bugs fixed by this
388
change) will be included in the output.
390
* *empty-directories* - directories, even the empty ones, will be
391
included in the output.
393
To disable these features and produce output acceptable to git 1.6,
394
use the --plain option. To enable these features, use --no-plain.
395
Currently, --plain is the default but that will change in the near
396
future once the feature names and definitions are formally agreed
397
to by the broader fast-import developer community.
399
Git has stricter naming rules for tags and fast-export --plain
400
will skip tags which can't be imported into git. To replace characters
401
unsupported in git with an underscore instead, specify
406
It is sometimes convenient to simply truncate the revision history at a
407
certain point. The --baseline option, to be used in conjunction with -r,
408
emits a baseline commit containing the state of the entire source tree at
409
the first requested revision. This allows a user to produce a tree
410
identical to the original without munging multiple exports.
414
To produce data destined for import into Bazaar::
416
bzr fast-export --no-plain my-bzr-branch my.fi.gz
418
To produce data destined for Git 1.6::
420
bzr fast-export --plain my-bzr-branch my.fi
422
To import several unmerged but related branches into the same repository,
423
use the --{export,import}-marks options, and specify a name for the git
426
bzr fast-export --export-marks=marks.bzr project.dev |
427
GIT_DIR=project/.git git-fast-import --export-marks=marks.git
429
bzr fast-export --import-marks=marks.bzr -b other project.other |
430
GIT_DIR=project/.git git-fast-import --import-marks=marks.git
432
If you get a "Missing space after source" error from git-fast-import,
433
see the top of the commands.py module for a work-around.
435
Since bzr uses per-branch tags and git/hg use per-repo tags, the
436
way bzr fast-export presently emits tags (unconditional reset &
437
new ref) may result in clashes when several different branches
438
are imported into single git/hg repo. If this occurs, use the
439
bzr fast-export option --no-tags during the export of one or more
440
branches to avoid the issue.
443
_see_also = ['fast-import', 'fast-import-filter']
444
takes_args = ['source?', 'destination?']
445
takes_options = ['verbose', 'revision',
446
Option('git-branch', short_name='b', type=str,
448
help='Name of the git branch to create (default=master).'
450
Option('checkpoint', type=int, argname='N',
451
help="Checkpoint every N revisions (default=10000)."
453
Option('marks', type=str, argname='FILE',
454
help="Import marks from and export marks to file."
456
Option('import-marks', type=str, argname='FILE',
457
help="Import marks from file."
459
Option('export-marks', type=str, argname='FILE',
460
help="Export marks to file."
463
help="Exclude metadata to maximise interoperability."
465
Option('rewrite-tag-names',
466
help="Replace characters invalid in git with '_'"
467
" (plain mode only).",
470
help="Export an 'absolute' baseline commit prior to"
471
"the first relative commit",
474
help="Don't export tags"
477
encoding_type = 'exact'
479
def run(self, source=None, destination=None, verbose=False,
480
git_branch="master", checkpoint=10000, marks=None,
481
import_marks=None, export_marks=None, revision=None,
482
plain=True, rewrite_tag_names=False, no_tags=False, baseline=False):
484
from ...branch import Branch
485
from . import exporter
488
import_marks = export_marks = marks
493
branch = Branch.open_containing(source)[0]
494
outf = exporter._get_output_stream(destination)
495
exporter = exporter.BzrFastExporter(
497
outf=outf, ref=b"refs/heads/%s" % git_branch.encode('utf-8'),
498
checkpoint=checkpoint, import_marks_file=import_marks,
499
export_marks_file=export_marks, revision=revision, verbose=verbose,
500
plain_format=plain, rewrite_tags=rewrite_tag_names,
501
no_tags=no_tags, baseline=baseline)
502
return exporter.run()