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