/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.64.128 by Ian Clatworthy
fix encoding issue in bzr_exporter (Teemu Likonen)
1
# -*- coding: utf-8 -*-
2
0.79.1 by Ian Clatworthy
turn bzr-fast-export into a fast-export command
3
# Copyright (C) 2008 Canonical Ltd
4
#
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
9
#
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
0.64.334 by Jelmer Vernooij
Remove old FSF address. Thanks Dan Callaghan.
16
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
0.79.1 by Ian Clatworthy
turn bzr-fast-export into a fast-export command
17
#
0.64.333 by Jelmer Vernooij
Inline bzr-fast-export license.
18
# Based on bzr-fast-export
19
# Copyright (c) 2008 Adeodato Simó
20
#
21
# Permission is hereby granted, free of charge, to any person obtaining
22
# a copy of this software and associated documentation files (the
23
# "Software"), to deal in the Software without restriction, including
24
# without limitation the rights to use, copy, modify, merge, publish,
25
# distribute, sublicense, and/or sell copies of the Software, and to
26
# permit persons to whom the Software is furnished to do so, subject to
27
# the following conditions:
28
#
29
# The above copyright notice and this permission notice shall be included
30
# in all copies or substantial portions of the Software.
31
#
32
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
34
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
35
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
36
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
37
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
38
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0.79.10 by Ian Clatworthy
documentation clean-ups
39
#
0.64.57 by Ian Clatworthy
integrate dato's bzr-fast-export
40
# vim: fileencoding=utf-8
0.79.1 by Ian Clatworthy
turn bzr-fast-export into a fast-export command
41
42
"""Core engine for the fast-export command."""
0.64.57 by Ian Clatworthy
integrate dato's bzr-fast-export
43
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
44
from __future__ import absolute_import
45
0.79.7 by Ian Clatworthy
trivial bzr_exporter clean-ups
46
# TODO: if a new_git_branch below gets merged repeatedly, the tip of the branch
0.64.57 by Ian Clatworthy
integrate dato's bzr-fast-export
47
# is not updated (because the parent of commit is already merged, so we don't
48
# set new_git_branch to the previously used name)
49
6791.2.4 by Jelmer Vernooij
Fix python3isms.
50
try:
51
    from email.utils import parseaddr
52
except ImportError:  # python < 3
53
    from email.Utils import parseaddr
7143.15.2 by Jelmer Vernooij
Run autopep8.
54
import sys
55
import time
56
import re
0.64.57 by Ian Clatworthy
integrate dato's bzr-fast-export
57
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
58
import breezy.branch
59
import breezy.revision
60
from ... import (
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
61
    builtins,
7463.3.2 by Jelmer Vernooij
Fix SyntaxError.
62
    errors,
6628.1.5 by Jelmer Vernooij
Consistently use fastimport feature.
63
    lazy_import,
7463.3.5 by Jelmer Vernooij
More batching.
64
    lru_cache,
0.64.237 by Ian Clatworthy
implicitly rename children on export when directory renamed
65
    osutils,
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
66
    progress,
67
    trace,
68
    )
7045.3.1 by Jelmer Vernooij
Fix another ~500 tests.
69
from ...sixish import (
70
    int2byte,
7045.4.18 by Jelmer Vernooij
Fix compatibility with older versions of fastimport.
71
    PY3,
7045.3.1 by Jelmer Vernooij
Fix another ~500 tests.
72
    viewitems,
73
    )
0.79.4 by Ian Clatworthy
use note and warning APIs
74
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
75
from . import (
0.64.284 by Jelmer Vernooij
Fix import of single_plural.
76
    helpers,
77
    marks_file,
78
    )
0.79.2 by Ian Clatworthy
extend & use marks_file API
79
6628.1.5 by Jelmer Vernooij
Consistently use fastimport feature.
80
lazy_import.lazy_import(globals(),
7143.15.2 by Jelmer Vernooij
Run autopep8.
81
                        """
0.123.8 by Jelmer Vernooij
Use modes for FileModifyCommand.
82
from fastimport import commands
6628.1.5 by Jelmer Vernooij
Consistently use fastimport feature.
83
""")
0.64.282 by Jelmer Vernooij
Fix output stream to stdout for bzr fast-export.
84
7463.3.3 by Jelmer Vernooij
Batch iter_revisions requests.
85
REVISIONS_CHUNK_SIZE = 1000
86
0.64.282 by Jelmer Vernooij
Fix output stream to stdout for bzr fast-export.
87
88
def _get_output_stream(destination):
89
    if destination is None or destination == '-':
7211.12.2 by Jelmer Vernooij
Unwrap sys.stdout.
90
        return helpers.binary_stream(getattr(sys.stdout, "buffer", sys.stdout))
7239.2.1 by Jelmer Vernooij
Require a dot in the extension for .gz
91
    elif destination.endswith('.gz'):
0.64.282 by Jelmer Vernooij
Fix output stream to stdout for bzr fast-export.
92
        import gzip
93
        return gzip.open(destination, 'wb')
94
    else:
95
        return open(destination, 'wb')
96
0.64.328 by Jelmer Vernooij
In "plain" mode, skip tags that contain characters not valid in Git.
97
# from dulwich.repo:
7143.15.2 by Jelmer Vernooij
Run autopep8.
98
99
0.64.328 by Jelmer Vernooij
In "plain" mode, skip tags that contain characters not valid in Git.
100
def check_ref_format(refname):
101
    """Check if a refname is correctly formatted.
102
103
    Implements all the same rules as git-check-ref-format[1].
104
105
    [1] http://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html
106
107
    :param refname: The refname to check
108
    :return: True if refname is valid, False otherwise
109
    """
110
    # These could be combined into one big expression, but are listed separately
111
    # to parallel [1].
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
112
    if b'/.' in refname or refname.startswith(b'.'):
113
        return False
114
    if b'/' not in refname:
115
        return False
116
    if b'..' in refname:
117
        return False
118
    for i in range(len(refname)):
7143.15.2 by Jelmer Vernooij
Run autopep8.
119
        if ord(refname[i:i + 1]) < 0o40 or refname[i] in b'\177 ~^:?*[':
0.64.328 by Jelmer Vernooij
In "plain" mode, skip tags that contain characters not valid in Git.
120
            return False
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
121
    if refname[-1] in b'/.':
122
        return False
123
    if refname.endswith(b'.lock'):
124
        return False
125
    if b'@{' in refname:
126
        return False
127
    if b'\\' in refname:
0.64.328 by Jelmer Vernooij
In "plain" mode, skip tags that contain characters not valid in Git.
128
        return False
129
    return True
130
0.133.3 by Oleksandr Usov
Implement comments from patch review:
131
132
def sanitize_ref_name_for_git(refname):
0.133.1 by Oleksandr Usov
Add function to rewrite refnames & tests for it
133
    """Rewrite refname so that it will be accepted by git-fast-import.
134
    For the detailed rules see check_ref_format.
135
136
    By rewriting the refname we are breaking uniqueness guarantees provided by bzr
137
    so we have to manually
138
    verify that resulting ref names are unique.
139
140
    :param refname: refname to rewrite
141
    :return: new refname
142
    """
0.133.3 by Oleksandr Usov
Implement comments from patch review:
143
    new_refname = re.sub(
0.133.1 by Oleksandr Usov
Add function to rewrite refnames & tests for it
144
        # '/.' in refname or startswith '.'
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
145
        br"/\.|^\."
0.133.1 by Oleksandr Usov
Add function to rewrite refnames & tests for it
146
        # '..' in refname
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
147
        br"|\.\."
0.133.1 by Oleksandr Usov
Add function to rewrite refnames & tests for it
148
        # ord(c) < 040
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
149
        br"|[" + b"".join([int2byte(x) for x in range(0o40)]) + br"]"
0.133.1 by Oleksandr Usov
Add function to rewrite refnames & tests for it
150
        # c in '\177 ~^:?*['
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
151
        br"|[\177 ~^:?*[]"
0.133.1 by Oleksandr Usov
Add function to rewrite refnames & tests for it
152
        # last char in "/."
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
153
        br"|[/.]$"
0.133.1 by Oleksandr Usov
Add function to rewrite refnames & tests for it
154
        # endswith '.lock'
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
155
        br"|.lock$"
0.133.1 by Oleksandr Usov
Add function to rewrite refnames & tests for it
156
        # "@{" in refname
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
157
        br"|@{"
0.133.1 by Oleksandr Usov
Add function to rewrite refnames & tests for it
158
        # "\\" in refname
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
159
        br"|\\",
160
        b"_", refname)
0.133.3 by Oleksandr Usov
Implement comments from patch review:
161
    return new_refname
0.64.173 by Ian Clatworthy
add -r option to fast-export
162
0.64.339 by Jelmer Vernooij
Some refactoring of exporter.
163
0.79.1 by Ian Clatworthy
turn bzr-fast-export into a fast-export command
164
class BzrFastExporter(object):
0.68.1 by Pieter de Bie
Classify bzr-fast-export
165
0.64.350 by Jelmer Vernooij
Rename git_branch to ref.
166
    def __init__(self, source, outf, ref=None, checkpoint=-1,
7143.15.2 by Jelmer Vernooij
Run autopep8.
167
                 import_marks_file=None, export_marks_file=None, revision=None,
168
                 verbose=False, plain_format=False, rewrite_tags=False,
169
                 no_tags=False, baseline=False):
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
170
        """Export branch data in fast import format.
171
172
        :param plain_format: if True, 'classic' fast-import format is
0.64.337 by Jelmer Vernooij
Merge support for --rewrite-tag-names.
173
            used without any extended features; if False, the generated
174
            data is richer and includes information like multiple
175
            authors, revision properties, etc.
176
        :param rewrite_tags: if True and if plain_format is set, tag names
177
            will be rewritten to be git-compatible.
178
            Otherwise tags which aren't valid for git will be skipped if
179
            plain_format is set.
0.138.1 by Oleksandr Usov
Add --no-tags flag
180
        :param no_tags: if True tags won't be exported at all
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
181
        """
0.64.339 by Jelmer Vernooij
Some refactoring of exporter.
182
        self.branch = source
183
        self.outf = outf
0.64.350 by Jelmer Vernooij
Rename git_branch to ref.
184
        self.ref = ref
0.79.1 by Ian Clatworthy
turn bzr-fast-export into a fast-export command
185
        self.checkpoint = checkpoint
186
        self.import_marks_file = import_marks_file
187
        self.export_marks_file = export_marks_file
0.64.173 by Ian Clatworthy
add -r option to fast-export
188
        self.revision = revision
189
        self.excluded_revisions = set()
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
190
        self.plain_format = plain_format
0.133.2 by Oleksandr Usov
Rewrite tag names when exporting plain stream
191
        self.rewrite_tags = rewrite_tags
0.138.1 by Oleksandr Usov
Add --no-tags flag
192
        self.no_tags = no_tags
0.135.1 by Andy Grimm
Add --baseline option
193
        self.baseline = baseline
7463.3.5 by Jelmer Vernooij
More batching.
194
        self.tree_cache = lru_cache.LRUCache(max_cache=20)
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
195
        self._multi_author_api_available = hasattr(breezy.revision.Revision,
7143.15.2 by Jelmer Vernooij
Run autopep8.
196
                                                   'get_apparent_authors')
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
197
        self.properties_to_exclude = ['authors', 'author']
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
198
199
        # Progress reporting stuff
200
        self.verbose = verbose
201
        if verbose:
202
            self.progress_every = 100
203
        else:
204
            self.progress_every = 1000
205
        self._start_time = time.time()
0.64.230 by Ian Clatworthy
Fix ghost handling and improve progress tracking in fast-export
206
        self._commit_total = 0
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
207
208
        # Load the marks and initialise things accordingly
0.68.1 by Pieter de Bie
Classify bzr-fast-export
209
        self.revid_to_mark = {}
210
        self.branch_names = {}
0.79.1 by Ian Clatworthy
turn bzr-fast-export into a fast-export command
211
        if self.import_marks_file:
0.79.2 by Ian Clatworthy
extend & use marks_file API
212
            marks_info = marks_file.import_marks(self.import_marks_file)
213
            if marks_info is not None:
0.64.134 by Ian Clatworthy
fix marks importing in fast-export
214
                self.revid_to_mark = dict((r, m) for m, r in
7143.15.2 by Jelmer Vernooij
Run autopep8.
215
                                          marks_info.items())
0.125.1 by Ian Clatworthy
Use the new marks file format (introduced in git 1.6 apparently)
216
                # These are no longer included in the marks file
217
                #self.branch_names = marks_info[1]
0.64.350 by Jelmer Vernooij
Rename git_branch to ref.
218
0.64.173 by Ian Clatworthy
add -r option to fast-export
219
    def interesting_history(self):
220
        if self.revision:
7463.3.2 by Jelmer Vernooij
Fix SyntaxError.
221
            rev1, rev2 = builtins._get_revision_range(
222
                self.revision, self.branch, "fast-export")
0.64.173 by Ian Clatworthy
add -r option to fast-export
223
            start_rev_id = rev1.rev_id
224
            end_rev_id = rev2.rev_id
225
        else:
226
            start_rev_id = None
227
            end_rev_id = None
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
228
        self.note("Calculating the revisions to include ...")
0.64.341 by Jelmer Vernooij
Fix test, clarify help description for 'bzr fast-export'.
229
        view_revisions = [rev_id for rev_id, _, _, _ in
7143.15.2 by Jelmer Vernooij
Run autopep8.
230
                          self.branch.iter_merge_sorted_revisions(end_rev_id, start_rev_id)]
0.64.341 by Jelmer Vernooij
Fix test, clarify help description for 'bzr fast-export'.
231
        view_revisions.reverse()
0.64.173 by Ian Clatworthy
add -r option to fast-export
232
        # If a starting point was given, we need to later check that we don't
233
        # start emitting revisions from before that point. Collect the
234
        # revisions to exclude now ...
235
        if start_rev_id is not None:
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
236
            self.note("Calculating the revisions to exclude ...")
7463.3.3 by Jelmer Vernooij
Batch iter_revisions requests.
237
            self.excluded_revisions = set(
238
                [rev_id for rev_id, _, _, _ in self.branch.iter_merge_sorted_revisions(start_rev_id)])
0.135.2 by Andy Grimm
fix --baseline bugs, and add a couple of tests
239
            if self.baseline:
240
                # needed so the first relative commit knows its parent
241
                self.excluded_revisions.remove(start_rev_id)
242
                view_revisions.insert(0, start_rev_id)
0.64.230 by Ian Clatworthy
Fix ghost handling and improve progress tracking in fast-export
243
        return list(view_revisions)
0.64.173 by Ian Clatworthy
add -r option to fast-export
244
7463.3.5 by Jelmer Vernooij
More batching.
245
    def emit_commits(self, interesting):
246
        if self.baseline:
247
            revobj = self.branch.repository.get_revision(interesting.pop(0))
248
            self.emit_baseline(revobj, self.ref)
249
        for i in range(0, len(interesting), REVISIONS_CHUNK_SIZE):
250
            chunk = interesting[i:i + REVISIONS_CHUNK_SIZE]
251
            history = dict(self.branch.repository.iter_revisions(chunk))
7463.3.6 by Jelmer Vernooij
Batch tree retrieval.
252
            trees_needed = set()
253
            trees = {}
254
            for revid in chunk:
255
                trees_needed.update(self.preprocess_commit(revid, history[revid], self.ref))
256
257
            for tree in self._get_revision_trees(trees_needed):
258
                trees[tree.get_revision_id()] = tree
259
260
            for revid in chunk:
261
                revobj = history[revid]
262
                if len(revobj.parent_ids) == 0:
263
                    parent = breezy.revision.NULL_REVISION
264
                else:
265
                    parent = revobj.parent_ids[0]
266
                self.emit_commit(revobj, self.ref, trees[parent], trees[revid])
7463.3.5 by Jelmer Vernooij
More batching.
267
0.79.1 by Ian Clatworthy
turn bzr-fast-export into a fast-export command
268
    def run(self):
269
        # Export the data
6754.8.4 by Jelmer Vernooij
Use new context stuff.
270
        with self.branch.repository.lock_read():
0.100.1 by Ian Clatworthy
Stop fast-export from exceeding the maximum recursion depth
271
            interesting = self.interesting_history()
0.102.15 by Ian Clatworthy
add revision count to 'Starting export ...' message
272
            self._commit_total = len(interesting)
273
            self.note("Starting export of %d revisions ..." %
7143.15.2 by Jelmer Vernooij
Run autopep8.
274
                      self._commit_total)
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
275
            if not self.plain_format:
276
                self.emit_features()
7463.3.5 by Jelmer Vernooij
More batching.
277
            self.emit_commits(interesting)
0.138.1 by Oleksandr Usov
Add --no-tags flag
278
            if self.branch.supports_tags() and not self.no_tags:
0.79.1 by Ian Clatworthy
turn bzr-fast-export into a fast-export command
279
                self.emit_tags()
0.68.1 by Pieter de Bie
Classify bzr-fast-export
280
0.79.1 by Ian Clatworthy
turn bzr-fast-export into a fast-export command
281
        # Save the marks if requested
0.79.2 by Ian Clatworthy
extend & use marks_file API
282
        self._save_marks()
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
283
        self.dump_stats()
284
285
    def note(self, msg, *args):
286
        """Output a note but timestamp it."""
287
        msg = "%s %s" % (self._time_of_day(), msg)
288
        trace.note(msg, *args)
289
290
    def warning(self, msg, *args):
291
        """Output a warning but timestamp it."""
292
        msg = "%s WARNING: %s" % (self._time_of_day(), msg)
293
        trace.warning(msg, *args)
294
295
    def _time_of_day(self):
296
        """Time of day as a string."""
297
        # Note: this is a separate method so tests can patch in a fixed value
298
        return time.strftime("%H:%M:%S")
299
300
    def report_progress(self, commit_count, details=''):
301
        if commit_count and commit_count % self.progress_every == 0:
0.64.230 by Ian Clatworthy
Fix ghost handling and improve progress tracking in fast-export
302
            if self._commit_total:
303
                counts = "%d/%d" % (commit_count, self._commit_total)
304
            else:
305
                counts = "%d" % (commit_count,)
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
306
            minutes = (time.time() - self._start_time) / 60
307
            rate = commit_count * 1.0 / minutes
308
            if rate > 10:
309
                rate_str = "at %.0f/minute " % rate
310
            else:
311
                rate_str = "at %.1f/minute " % rate
312
            self.note("%s commits exported %s%s" % (counts, rate_str, details))
313
314
    def dump_stats(self):
315
        time_required = progress.str_tdelta(time.time() - self._start_time)
316
        rc = len(self.revid_to_mark)
317
        self.note("Exported %d %s in %s",
7143.15.2 by Jelmer Vernooij
Run autopep8.
318
                  rc, helpers.single_plural(rc, "revision", "revisions"),
319
                  time_required)
0.79.2 by Ian Clatworthy
extend & use marks_file API
320
0.79.6 by Ian Clatworthy
refactor bzr_exporter to use Command objects
321
    def print_cmd(self, cmd):
7045.4.18 by Jelmer Vernooij
Fix compatibility with older versions of fastimport.
322
        if PY3:
323
            self.outf.write(b"%s\n" % cmd)
324
        else:
325
            self.outf.write(b"%r\n" % cmd)
0.79.6 by Ian Clatworthy
refactor bzr_exporter to use Command objects
326
0.79.2 by Ian Clatworthy
extend & use marks_file API
327
    def _save_marks(self):
328
        if self.export_marks_file:
0.64.134 by Ian Clatworthy
fix marks importing in fast-export
329
            revision_ids = dict((m, r) for r, m in self.revid_to_mark.items())
0.125.1 by Ian Clatworthy
Use the new marks file format (introduced in git 1.6 apparently)
330
            marks_file.export_marks(self.export_marks_file, revision_ids)
6656.1.1 by Martin
Apply 2to3 dict fixer and clean up resulting mess using view helpers
331
0.68.10 by Pieter de Bie
bzr-fast-export: Don't rename empty directories
332
    def is_empty_dir(self, tree, path):
6809.4.7 by Jelmer Vernooij
Swap arguments for get_symlink_target and kind/stored_kind.
333
        # Continue if path is not a directory
334
        try:
335
            if tree.kind(path) != 'directory':
336
                return False
7463.3.2 by Jelmer Vernooij
Fix SyntaxError.
337
        except errors.NoSuchFile:
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
338
            self.warning("Skipping empty_dir detection - no file_id for %s" %
7143.15.2 by Jelmer Vernooij
Run autopep8.
339
                         (path,))
0.68.10 by Pieter de Bie
bzr-fast-export: Don't rename empty directories
340
            return False
341
342
        # Use treewalk to find the contents of our directory
343
        contents = list(tree.walkdirs(prefix=path))[0]
344
        if len(contents[1]) == 0:
345
            return True
346
        else:
347
            return False
348
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
349
    def emit_features(self):
0.102.5 by Ian Clatworthy
Define feature names in one place
350
        for feature in sorted(commands.FEATURE_NAMES):
351
            self.print_cmd(commands.FeatureCommand(feature))
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
352
7463.3.5 by Jelmer Vernooij
More batching.
353
    def emit_baseline(self, revobj, ref):
0.135.1 by Andy Grimm
Add --baseline option
354
        # Emit a full source tree of the first commit's parent
355
        mark = 1
7463.3.5 by Jelmer Vernooij
More batching.
356
        self.revid_to_mark[revobj.revision_id] = mark
7463.3.6 by Jelmer Vernooij
Batch tree retrieval.
357
        tree_old = self.branch.repository.revision_tree(
358
            breezy.revision.NULL_REVISION)
359
        [tree_new] = list(self._get_revision_trees([revobj.revision_id]))
7463.3.5 by Jelmer Vernooij
More batching.
360
        file_cmds = self._get_filecommands(tree_old, tree_new)
0.430.1 by Oleksandr Usov
Emit 'reset' command for parentless & baseline commits.
361
        self.print_cmd(commands.ResetCommand(ref, None))
0.64.350 by Jelmer Vernooij
Rename git_branch to ref.
362
        self.print_cmd(self._get_commit_command(ref, mark, revobj, file_cmds))
0.135.1 by Andy Grimm
Add --baseline option
363
7463.3.6 by Jelmer Vernooij
Batch tree retrieval.
364
    def preprocess_commit(self, revid, revobj, ref):
0.64.173 by Ian Clatworthy
add -r option to fast-export
365
        if revid in self.revid_to_mark or revid in self.excluded_revisions:
0.68.4 by Pieter de Bie
bzr-fast-export.py: Add support for ghost commits
366
            return
7463.3.3 by Jelmer Vernooij
Batch iter_revisions requests.
367
        if revobj is None:
0.68.4 by Pieter de Bie
bzr-fast-export.py: Add support for ghost commits
368
            # This is a ghost revision. Mark it as not found and next!
369
            self.revid_to_mark[revid] = -1
370
            return
0.79.6 by Ian Clatworthy
refactor bzr_exporter to use Command objects
371
        # Get the primary parent
0.100.1 by Ian Clatworthy
Stop fast-export from exceeding the maximum recursion depth
372
        # TODO: Consider the excluded revisions when deciding the parents.
373
        # Currently, a commit with parents that are excluded ought to be
0.64.350 by Jelmer Vernooij
Rename git_branch to ref.
374
        # triggering the ref calculation below (and it is not).
0.100.1 by Ian Clatworthy
Stop fast-export from exceeding the maximum recursion depth
375
        # IGC 20090824
7463.3.6 by Jelmer Vernooij
Batch tree retrieval.
376
        if len(revobj.parent_ids) == 0:
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
377
            parent = breezy.revision.NULL_REVISION
0.68.4 by Pieter de Bie
bzr-fast-export.py: Add support for ghost commits
378
        else:
379
            parent = revobj.parent_ids[0]
380
7463.3.6 by Jelmer Vernooij
Batch tree retrieval.
381
        # Print the commit
382
        mark = len(self.revid_to_mark) + 1
383
        self.revid_to_mark[revobj.revision_id] = mark
384
385
        return [parent, revobj.revision_id]
386
387
    def emit_commit(self, revobj, ref, tree_old, tree_new):
0.430.1 by Oleksandr Usov
Emit 'reset' command for parentless & baseline commits.
388
        # For parentless commits we need to issue reset command first, otherwise
389
        # git-fast-import will assume previous commit was this one's parent
7463.3.6 by Jelmer Vernooij
Batch tree retrieval.
390
        if tree_old.get_revision_id() == breezy.revision.NULL_REVISION:
0.430.1 by Oleksandr Usov
Emit 'reset' command for parentless & baseline commits.
391
            self.print_cmd(commands.ResetCommand(ref, None))
392
7463.3.5 by Jelmer Vernooij
More batching.
393
        file_cmds = self._get_filecommands(tree_old, tree_new)
7463.3.6 by Jelmer Vernooij
Batch tree retrieval.
394
        mark = self.revid_to_mark[revobj.revision_id]
0.64.350 by Jelmer Vernooij
Rename git_branch to ref.
395
        self.print_cmd(self._get_commit_command(ref, mark, revobj, file_cmds))
0.79.6 by Ian Clatworthy
refactor bzr_exporter to use Command objects
396
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
397
        # Report progress and checkpoint if it's time for that
7463.3.6 by Jelmer Vernooij
Batch tree retrieval.
398
        ncommits = len(self.revid_to_mark)
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
399
        self.report_progress(ncommits)
7143.15.2 by Jelmer Vernooij
Run autopep8.
400
        if (self.checkpoint is not None and self.checkpoint > 0 and ncommits and
401
                ncommits % self.checkpoint == 0):
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
402
            self.note("Exported %i commits - adding checkpoint to output"
7143.15.2 by Jelmer Vernooij
Run autopep8.
403
                      % ncommits)
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
404
            self._save_marks()
405
            self.print_cmd(commands.CheckpointCommand())
406
0.102.16 by Ian Clatworthy
tweak author formatting to use same smart rule as used for committer
407
    def _get_name_email(self, user):
408
        if user.find('<') == -1:
0.64.177 by Ian Clatworthy
fix round-tripping of committer & author when name is an email
409
            # If the email isn't inside <>, we need to use it as the name
410
            # in order for things to round-trip correctly.
411
            # (note: parseaddr('a@b.com') => name:'', email: 'a@b.com')
0.102.16 by Ian Clatworthy
tweak author formatting to use same smart rule as used for committer
412
            name = user
0.64.177 by Ian Clatworthy
fix round-tripping of committer & author when name is an email
413
            email = ''
414
        else:
0.102.16 by Ian Clatworthy
tweak author formatting to use same smart rule as used for committer
415
            name, email = parseaddr(user)
0.64.299 by Jelmer Vernooij
utf8 decode/encode paths and committer/author email/name, as python-fastimport no longer does so.
416
        return name.encode("utf-8"), email.encode("utf-8")
0.102.16 by Ian Clatworthy
tweak author formatting to use same smart rule as used for committer
417
418
    def _get_commit_command(self, git_ref, mark, revobj, file_cmds):
419
        # Get the committer and author info
420
        committer = revobj.committer
421
        name, email = self._get_name_email(committer)
0.79.6 by Ian Clatworthy
refactor bzr_exporter to use Command objects
422
        committer_info = (name, email, revobj.timestamp, revobj.timezone)
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
423
        if self._multi_author_api_available:
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
424
            more_authors = revobj.get_apparent_authors()
425
            author = more_authors.pop(0)
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
426
        else:
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
427
            more_authors = []
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
428
            author = revobj.get_apparent_author()
0.64.291 by Jelmer Vernooij
In plain mode, don't export multiple authors.
429
        if not self.plain_format and more_authors:
0.102.16 by Ian Clatworthy
tweak author formatting to use same smart rule as used for committer
430
            name, email = self._get_name_email(author)
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
431
            author_info = (name, email, revobj.timestamp, revobj.timezone)
432
            more_author_info = []
433
            for a in more_authors:
0.102.16 by Ian Clatworthy
tweak author formatting to use same smart rule as used for committer
434
                name, email = self._get_name_email(a)
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
435
                more_author_info.append(
436
                    (name, email, revobj.timestamp, revobj.timezone))
437
        elif author != committer:
0.102.16 by Ian Clatworthy
tweak author formatting to use same smart rule as used for committer
438
            name, email = self._get_name_email(author)
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
439
            author_info = (name, email, revobj.timestamp, revobj.timezone)
440
            more_author_info = None
0.79.6 by Ian Clatworthy
refactor bzr_exporter to use Command objects
441
        else:
442
            author_info = None
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
443
            more_author_info = None
0.79.6 by Ian Clatworthy
refactor bzr_exporter to use Command objects
444
445
        # Get the parents in terms of marks
446
        non_ghost_parents = []
0.68.4 by Pieter de Bie
bzr-fast-export.py: Add support for ghost commits
447
        for p in revobj.parent_ids:
0.64.173 by Ian Clatworthy
add -r option to fast-export
448
            if p in self.excluded_revisions:
449
                continue
0.64.230 by Ian Clatworthy
Fix ghost handling and improve progress tracking in fast-export
450
            try:
451
                parent_mark = self.revid_to_mark[p]
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
452
                non_ghost_parents.append(b":%d" % parent_mark)
0.64.230 by Ian Clatworthy
Fix ghost handling and improve progress tracking in fast-export
453
            except KeyError:
454
                # ghost - ignore
455
                continue
0.79.6 by Ian Clatworthy
refactor bzr_exporter to use Command objects
456
        if non_ghost_parents:
457
            from_ = non_ghost_parents[0]
458
            merges = non_ghost_parents[1:]
459
        else:
460
            from_ = None
461
            merges = None
462
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
463
        # Filter the revision properties. Some metadata (like the
464
        # author information) is already exposed in other ways so
465
        # don't repeat it here.
466
        if self.plain_format:
467
            properties = None
468
        else:
469
            properties = revobj.properties
470
            for prop in self.properties_to_exclude:
471
                try:
472
                    del properties[prop]
473
                except KeyError:
474
                    pass
475
0.79.6 by Ian Clatworthy
refactor bzr_exporter to use Command objects
476
        # Build and return the result
7463.3.5 by Jelmer Vernooij
More batching.
477
        return commands.CommitCommand(
478
            git_ref, mark, author_info, committer_info,
479
            revobj.message.encode("utf-8"), from_, merges, file_cmds,
480
            more_authors=more_author_info, properties=properties)
481
482
    def _get_revision_trees(self, revids):
483
        missing = []
484
        by_revid = {}
485
        for revid in revids:
486
            if revid == breezy.revision.NULL_REVISION:
487
                by_revid[revid] = self.branch.repository.revision_tree(revid)
488
            elif revid not in self.tree_cache:
489
                missing.append(revid)
490
491
        for tree in self.branch.repository.revision_trees(missing):
492
            by_revid[tree.get_revision_id()] = tree
493
494
        for revid in revids:
495
            try:
496
                yield self.tree_cache[revid]
497
            except KeyError:
498
                yield by_revid[revid]
499
500
        for revid, tree in by_revid.items():
501
            self.tree_cache[revid] = tree
502
503
    def _get_filecommands(self, tree_old, tree_new):
0.79.6 by Ian Clatworthy
refactor bzr_exporter to use Command objects
504
        """Get the list of FileCommands for the changes between two revisions."""
0.64.166 by Ian Clatworthy
graceful handling of faulty revisions (David Reitter)
505
        changes = tree_new.changes_from(tree_old)
506
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
507
        my_modified = list(changes.modified)
0.64.166 by Ian Clatworthy
graceful handling of faulty revisions (David Reitter)
508
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
509
        # The potential interaction between renames and deletes is messy.
510
        # Handle it here ...
511
        file_cmds, rd_modifies, renamed = self._process_renames_and_deletes(
7463.3.5 by Jelmer Vernooij
More batching.
512
            changes.renamed, changes.removed, tree_new.get_revision_id(), tree_old)
0.64.166 by Ian Clatworthy
graceful handling of faulty revisions (David Reitter)
513
7222.1.1 by Jelmer Vernooij
Fix a spurious test.
514
        for cmd in file_cmds:
515
            yield cmd
516
0.64.166 by Ian Clatworthy
graceful handling of faulty revisions (David Reitter)
517
        # Map kind changes to a delete followed by an add
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
518
        for change in changes.kind_changed:
7463.3.5 by Jelmer Vernooij
More batching.
519
            path = self._adjust_path_for_renames(
520
                path, renamed, tree_new.get_revision_id())
0.64.174 by Ian Clatworthy
fix rename adjustment & kind change logic in fast-export
521
            # IGC: I don't understand why a delete is needed here.
522
            # In fact, it seems harmful? If you uncomment this line,
523
            # please file a bug explaining why you needed to.
7222.1.1 by Jelmer Vernooij
Fix a spurious test.
524
            # yield commands.FileDeleteCommand(path)
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
525
            my_modified.append(change)
0.64.166 by Ian Clatworthy
graceful handling of faulty revisions (David Reitter)
526
527
        # Record modifications
7211.5.1 by Jelmer Vernooij
Batch up requests for iter_files_bytes.
528
        files_to_get = []
7358.17.2 by Jelmer Vernooij
Fix some tests.
529
        for change in changes.added + changes.copied + my_modified + rd_modifies:
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
530
            if change.kind[1] == 'file':
7211.5.2 by Jelmer Vernooij
Actually remove unnecessary get_file_text call.
531
                files_to_get.append(
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
532
                    (change.path[1],
533
                     (change.path[1], helpers.kind_to_mode(
534
                         'file', change.executable[1]))))
535
            elif change.kind[1] == 'symlink':
7222.1.1 by Jelmer Vernooij
Fix a spurious test.
536
                yield commands.FileModifyCommand(
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
537
                    change.path[1].encode("utf-8"),
0.123.8 by Jelmer Vernooij
Use modes for FileModifyCommand.
538
                    helpers.kind_to_mode('symlink', False),
7427.2.1 by Jelmer Vernooij
merge lp:brz/3.0.
539
                    None, tree_new.get_symlink_target(
540
                        change.path[1]).encode('utf-8'))
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
541
            elif change.kind[1] == 'directory':
0.105.1 by John Whitley
Don't emit directory info when plain format is specified.
542
                if not self.plain_format:
7222.1.1 by Jelmer Vernooij
Fix a spurious test.
543
                    yield commands.FileModifyCommand(
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
544
                        change.path[1].encode("utf-8"),
7222.1.1 by Jelmer Vernooij
Fix a spurious test.
545
                        helpers.kind_to_mode('directory', False), None,
546
                        None)
0.64.166 by Ian Clatworthy
graceful handling of faulty revisions (David Reitter)
547
            else:
0.102.14 by Ian Clatworthy
export and import empty directories
548
                self.warning("cannot export '%s' of kind %s yet - ignoring" %
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
549
                             (change.path[1], change.kind[1]))
7463.3.5 by Jelmer Vernooij
More batching.
550
7463.3.7 by Jelmer Vernooij
Add TODO.
551
        # TODO(jelmer): Improve performance on remote repositories
552
        # by using Repository.iter_files_bytes for bzr repositories here.
7463.3.2 by Jelmer Vernooij
Fix SyntaxError.
553
        for (path, mode), chunks in tree_new.iter_files_bytes(files_to_get):
7222.1.1 by Jelmer Vernooij
Fix a spurious test.
554
            yield commands.FileModifyCommand(
555
                path.encode("utf-8"), mode, None, b''.join(chunks))
0.64.166 by Ian Clatworthy
graceful handling of faulty revisions (David Reitter)
556
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
557
    def _process_renames_and_deletes(self, renames, deletes,
7143.15.2 by Jelmer Vernooij
Run autopep8.
558
                                     revision_id, tree_old):
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
559
        file_cmds = []
560
        modifies = []
561
        renamed = []
562
563
        # See https://bugs.edge.launchpad.net/bzr-fastimport/+bug/268933.
564
        # In a nutshell, there are several nasty cases:
565
        #
566
        # 1) bzr rm a; bzr mv b a; bzr commit
567
        # 2) bzr mv x/y z; bzr rm x; commmit
568
        #
569
        # The first must come out with the delete first like this:
570
        #
571
        # D a
572
        # R b a
573
        #
574
        # The second case must come out with the rename first like this:
575
        #
576
        # R x/y z
577
        # D x
578
        #
579
        # So outputting all deletes first or all renames first won't work.
580
        # Instead, we need to make multiple passes over the various lists to
581
        # get the ordering right.
582
0.64.237 by Ian Clatworthy
implicitly rename children on export when directory renamed
583
        must_be_renamed = {}
584
        old_to_new = {}
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
585
        deleted_paths = set([change.path[0] for change in deletes])
586
        for change in renames:
587
            emit = change.kind[1] != 'directory' or not self.plain_format
588
            if change.path[1] in deleted_paths:
0.106.2 by Harry Hirsch
Don't emit directory info for renames operations when using plain format
589
                if emit:
7143.15.2 by Jelmer Vernooij
Run autopep8.
590
                    file_cmds.append(commands.FileDeleteCommand(
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
591
                        change.path[1].encode("utf-8")))
592
                deleted_paths.remove(change.path[1])
593
            if (self.is_empty_dir(tree_old, change.path[0])):
594
                self.note("Skipping empty dir %s in rev %s" % (change.path[0],
7143.15.2 by Jelmer Vernooij
Run autopep8.
595
                                                               revision_id))
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
596
                continue
7143.15.2 by Jelmer Vernooij
Run autopep8.
597
            # oldpath = self._adjust_path_for_renames(oldpath, renamed,
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
598
            #    revision_id)
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
599
            renamed.append(change.path)
600
            old_to_new[change.path[0]] = change.path[1]
0.106.2 by Harry Hirsch
Don't emit directory info for renames operations when using plain format
601
            if emit:
0.64.299 by Jelmer Vernooij
utf8 decode/encode paths and committer/author email/name, as python-fastimport no longer does so.
602
                file_cmds.append(
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
603
                    commands.FileRenameCommand(change.path[0].encode("utf-8"), change.path[1].encode("utf-8")))
604
            if change.changed_content or change.meta_modified():
605
                modifies.append(change)
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
606
0.64.237 by Ian Clatworthy
implicitly rename children on export when directory renamed
607
            # Renaming a directory implies all children must be renamed.
608
            # Note: changes_from() doesn't handle this
7463.3.2 by Jelmer Vernooij
Fix SyntaxError.
609
            if change.kind == ('directory', 'directory'):
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
610
                for p, e in tree_old.iter_entries_by_dir(specific_files=[change.path[0]]):
0.106.2 by Harry Hirsch
Don't emit directory info for renames operations when using plain format
611
                    if e.kind == 'directory' and self.plain_format:
612
                        continue
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
613
                    old_child_path = osutils.pathjoin(change.path[0], p)
614
                    new_child_path = osutils.pathjoin(change.path[1], p)
0.64.237 by Ian Clatworthy
implicitly rename children on export when directory renamed
615
                    must_be_renamed[old_child_path] = new_child_path
616
617
        # Add children not already renamed
618
        if must_be_renamed:
619
            renamed_already = set(old_to_new.keys())
620
            still_to_be_renamed = set(must_be_renamed.keys()) - renamed_already
621
            for old_child_path in sorted(still_to_be_renamed):
622
                new_child_path = must_be_renamed[old_child_path]
623
                if self.verbose:
624
                    self.note("implicitly renaming %s => %s" % (old_child_path,
7143.15.2 by Jelmer Vernooij
Run autopep8.
625
                                                                new_child_path))
0.64.299 by Jelmer Vernooij
utf8 decode/encode paths and committer/author email/name, as python-fastimport no longer does so.
626
                file_cmds.append(commands.FileRenameCommand(old_child_path.encode("utf-8"),
7143.15.2 by Jelmer Vernooij
Run autopep8.
627
                                                            new_child_path.encode("utf-8")))
0.64.237 by Ian Clatworthy
implicitly rename children on export when directory renamed
628
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
629
        # Record remaining deletes
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
630
        for change in deletes:
631
            if change.path[0] not in deleted_paths:
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
632
                continue
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
633
            if change.kind[0] == 'directory' and self.plain_format:
0.106.2 by Harry Hirsch
Don't emit directory info for renames operations when using plain format
634
                continue
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
635
            #path = self._adjust_path_for_renames(path, renamed, revision_id)
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
636
            file_cmds.append(commands.FileDeleteCommand(change.path[0].encode("utf-8")))
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
637
        return file_cmds, modifies, renamed
638
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
639
    def _adjust_path_for_renames(self, path, renamed, revision_id):
0.64.174 by Ian Clatworthy
fix rename adjustment & kind change logic in fast-export
640
        # If a previous rename is found, we should adjust the path
641
        for old, new in renamed:
642
            if path == old:
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
643
                self.note("Changing path %s given rename to %s in revision %s"
7143.15.2 by Jelmer Vernooij
Run autopep8.
644
                          % (path, new, revision_id))
0.64.174 by Ian Clatworthy
fix rename adjustment & kind change logic in fast-export
645
                path = new
646
            elif path.startswith(old + '/'):
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
647
                self.note(
648
                    "Adjusting path %s given rename of %s to %s in revision %s"
649
                    % (path, old, new, revision_id))
0.64.174 by Ian Clatworthy
fix rename adjustment & kind change logic in fast-export
650
                path = path.replace(old + "/", new + "/")
651
        return path
652
0.68.1 by Pieter de Bie
Classify bzr-fast-export
653
    def emit_tags(self):
7045.3.1 by Jelmer Vernooij
Fix another ~500 tests.
654
        for tag, revid in viewitems(self.branch.tags.get_tag_dict()):
0.68.1 by Pieter de Bie
Classify bzr-fast-export
655
            try:
656
                mark = self.revid_to_mark[revid]
657
            except KeyError:
0.79.4 by Ian Clatworthy
use note and warning APIs
658
                self.warning('not creating tag %r pointing to non-existent '
7143.15.2 by Jelmer Vernooij
Run autopep8.
659
                             'revision %s' % (tag, revid))
0.68.1 by Pieter de Bie
Classify bzr-fast-export
660
            else:
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
661
                git_ref = b'refs/tags/%s' % tag.encode("utf-8")
0.64.328 by Jelmer Vernooij
In "plain" mode, skip tags that contain characters not valid in Git.
662
                if self.plain_format and not check_ref_format(git_ref):
0.133.2 by Oleksandr Usov
Rewrite tag names when exporting plain stream
663
                    if self.rewrite_tags:
0.133.3 by Oleksandr Usov
Implement comments from patch review:
664
                        new_ref = sanitize_ref_name_for_git(git_ref)
0.133.2 by Oleksandr Usov
Rewrite tag names when exporting plain stream
665
                        self.warning('tag %r is exported as %r to be valid in git.',
666
                                     git_ref, new_ref)
667
                        git_ref = new_ref
0.133.3 by Oleksandr Usov
Implement comments from patch review:
668
                    else:
0.133.2 by Oleksandr Usov
Rewrite tag names when exporting plain stream
669
                        self.warning('not creating tag %r as its name would not be '
670
                                     'valid in git.', git_ref)
671
                        continue
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
672
                self.print_cmd(commands.ResetCommand(git_ref, b":%d" % mark))