/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
7490.78.1 by Jelmer Vernooij
Fix marks file handling on Python 3.
344
        self.revid_to_mark[revobj.revision_id] = b"%d" % 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):
7490.78.1 by Jelmer Vernooij
Fix marks file handling on Python 3.
353
        if self.revid_to_mark.get(revid) or revid in self.excluded_revisions:
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!
7490.78.1 by Jelmer Vernooij
Fix marks file handling on Python 3.
357
            self.revid_to_mark[revid] = None
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
7490.78.1 by Jelmer Vernooij
Fix marks file handling on Python 3.
370
        self.revid_to_mark[revobj.revision_id] = b"%d" % (
371
            len(self.revid_to_mark) + 1)
7463.3.6 by Jelmer Vernooij
Batch tree retrieval.
372
        return [parent, revobj.revision_id]
373
374
    def emit_commit(self, revobj, ref, tree_old, tree_new):
0.430.1 by Oleksandr Usov
Emit 'reset' command for parentless & baseline commits.
375
        # For parentless commits we need to issue reset command first, otherwise
376
        # git-fast-import will assume previous commit was this one's parent
7463.3.6 by Jelmer Vernooij
Batch tree retrieval.
377
        if tree_old.get_revision_id() == breezy.revision.NULL_REVISION:
0.430.1 by Oleksandr Usov
Emit 'reset' command for parentless & baseline commits.
378
            self.print_cmd(commands.ResetCommand(ref, None))
379
7463.3.5 by Jelmer Vernooij
More batching.
380
        file_cmds = self._get_filecommands(tree_old, tree_new)
7463.3.6 by Jelmer Vernooij
Batch tree retrieval.
381
        mark = self.revid_to_mark[revobj.revision_id]
0.64.350 by Jelmer Vernooij
Rename git_branch to ref.
382
        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
383
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
384
        # Report progress and checkpoint if it's time for that
7463.3.6 by Jelmer Vernooij
Batch tree retrieval.
385
        ncommits = len(self.revid_to_mark)
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
386
        self.report_progress(ncommits)
7143.15.2 by Jelmer Vernooij
Run autopep8.
387
        if (self.checkpoint is not None and self.checkpoint > 0 and ncommits and
388
                ncommits % self.checkpoint == 0):
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
389
            self.note("Exported %i commits - adding checkpoint to output"
7143.15.2 by Jelmer Vernooij
Run autopep8.
390
                      % ncommits)
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
391
            self._save_marks()
392
            self.print_cmd(commands.CheckpointCommand())
393
0.102.16 by Ian Clatworthy
tweak author formatting to use same smart rule as used for committer
394
    def _get_name_email(self, user):
395
        if user.find('<') == -1:
0.64.177 by Ian Clatworthy
fix round-tripping of committer & author when name is an email
396
            # If the email isn't inside <>, we need to use it as the name
397
            # in order for things to round-trip correctly.
398
            # (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
399
            name = user
0.64.177 by Ian Clatworthy
fix round-tripping of committer & author when name is an email
400
            email = ''
401
        else:
0.102.16 by Ian Clatworthy
tweak author formatting to use same smart rule as used for committer
402
            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.
403
        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
404
405
    def _get_commit_command(self, git_ref, mark, revobj, file_cmds):
406
        # Get the committer and author info
407
        committer = revobj.committer
408
        name, email = self._get_name_email(committer)
0.79.6 by Ian Clatworthy
refactor bzr_exporter to use Command objects
409
        committer_info = (name, email, revobj.timestamp, revobj.timezone)
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
410
        if self._multi_author_api_available:
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
411
            more_authors = revobj.get_apparent_authors()
412
            author = more_authors.pop(0)
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
413
        else:
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
414
            more_authors = []
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
415
            author = revobj.get_apparent_author()
0.64.291 by Jelmer Vernooij
In plain mode, don't export multiple authors.
416
        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
417
            name, email = self._get_name_email(author)
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
418
            author_info = (name, email, revobj.timestamp, revobj.timezone)
419
            more_author_info = []
420
            for a in more_authors:
0.102.16 by Ian Clatworthy
tweak author formatting to use same smart rule as used for committer
421
                name, email = self._get_name_email(a)
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
422
                more_author_info.append(
423
                    (name, email, revobj.timestamp, revobj.timezone))
424
        elif author != committer:
0.102.16 by Ian Clatworthy
tweak author formatting to use same smart rule as used for committer
425
            name, email = self._get_name_email(author)
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
426
            author_info = (name, email, revobj.timestamp, revobj.timezone)
427
            more_author_info = None
0.79.6 by Ian Clatworthy
refactor bzr_exporter to use Command objects
428
        else:
429
            author_info = None
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
430
            more_author_info = None
0.79.6 by Ian Clatworthy
refactor bzr_exporter to use Command objects
431
432
        # Get the parents in terms of marks
433
        non_ghost_parents = []
0.68.4 by Pieter de Bie
bzr-fast-export.py: Add support for ghost commits
434
        for p in revobj.parent_ids:
0.64.173 by Ian Clatworthy
add -r option to fast-export
435
            if p in self.excluded_revisions:
436
                continue
0.64.230 by Ian Clatworthy
Fix ghost handling and improve progress tracking in fast-export
437
            try:
438
                parent_mark = self.revid_to_mark[p]
7490.78.1 by Jelmer Vernooij
Fix marks file handling on Python 3.
439
                non_ghost_parents.append(b":%s" % parent_mark)
0.64.230 by Ian Clatworthy
Fix ghost handling and improve progress tracking in fast-export
440
            except KeyError:
441
                # ghost - ignore
442
                continue
0.79.6 by Ian Clatworthy
refactor bzr_exporter to use Command objects
443
        if non_ghost_parents:
444
            from_ = non_ghost_parents[0]
445
            merges = non_ghost_parents[1:]
446
        else:
447
            from_ = None
448
            merges = None
449
0.102.3 by Ian Clatworthy
First cut at exporting additional metadata via 'features'
450
        # Filter the revision properties. Some metadata (like the
451
        # author information) is already exposed in other ways so
452
        # don't repeat it here.
453
        if self.plain_format:
454
            properties = None
455
        else:
456
            properties = revobj.properties
457
            for prop in self.properties_to_exclude:
458
                try:
459
                    del properties[prop]
460
                except KeyError:
461
                    pass
462
0.79.6 by Ian Clatworthy
refactor bzr_exporter to use Command objects
463
        # Build and return the result
7463.3.5 by Jelmer Vernooij
More batching.
464
        return commands.CommitCommand(
465
            git_ref, mark, author_info, committer_info,
466
            revobj.message.encode("utf-8"), from_, merges, file_cmds,
467
            more_authors=more_author_info, properties=properties)
468
469
    def _get_revision_trees(self, revids):
470
        missing = []
471
        by_revid = {}
472
        for revid in revids:
473
            if revid == breezy.revision.NULL_REVISION:
474
                by_revid[revid] = self.branch.repository.revision_tree(revid)
475
            elif revid not in self.tree_cache:
476
                missing.append(revid)
477
478
        for tree in self.branch.repository.revision_trees(missing):
479
            by_revid[tree.get_revision_id()] = tree
480
481
        for revid in revids:
482
            try:
483
                yield self.tree_cache[revid]
484
            except KeyError:
485
                yield by_revid[revid]
486
487
        for revid, tree in by_revid.items():
488
            self.tree_cache[revid] = tree
489
490
    def _get_filecommands(self, tree_old, tree_new):
0.79.6 by Ian Clatworthy
refactor bzr_exporter to use Command objects
491
        """Get the list of FileCommands for the changes between two revisions."""
0.64.166 by Ian Clatworthy
graceful handling of faulty revisions (David Reitter)
492
        changes = tree_new.changes_from(tree_old)
493
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
494
        my_modified = list(changes.modified)
0.64.166 by Ian Clatworthy
graceful handling of faulty revisions (David Reitter)
495
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
496
        # The potential interaction between renames and deletes is messy.
497
        # Handle it here ...
498
        file_cmds, rd_modifies, renamed = self._process_renames_and_deletes(
7463.3.5 by Jelmer Vernooij
More batching.
499
            changes.renamed, changes.removed, tree_new.get_revision_id(), tree_old)
0.64.166 by Ian Clatworthy
graceful handling of faulty revisions (David Reitter)
500
7222.1.1 by Jelmer Vernooij
Fix a spurious test.
501
        for cmd in file_cmds:
502
            yield cmd
503
0.64.166 by Ian Clatworthy
graceful handling of faulty revisions (David Reitter)
504
        # 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.
505
        for change in changes.kind_changed:
7463.3.5 by Jelmer Vernooij
More batching.
506
            path = self._adjust_path_for_renames(
507
                path, renamed, tree_new.get_revision_id())
0.64.174 by Ian Clatworthy
fix rename adjustment & kind change logic in fast-export
508
            # IGC: I don't understand why a delete is needed here.
509
            # In fact, it seems harmful? If you uncomment this line,
510
            # please file a bug explaining why you needed to.
7222.1.1 by Jelmer Vernooij
Fix a spurious test.
511
            # yield commands.FileDeleteCommand(path)
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
512
            my_modified.append(change)
0.64.166 by Ian Clatworthy
graceful handling of faulty revisions (David Reitter)
513
514
        # Record modifications
7211.5.1 by Jelmer Vernooij
Batch up requests for iter_files_bytes.
515
        files_to_get = []
7358.17.2 by Jelmer Vernooij
Fix some tests.
516
        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.
517
            if change.kind[1] == 'file':
7211.5.2 by Jelmer Vernooij
Actually remove unnecessary get_file_text call.
518
                files_to_get.append(
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
519
                    (change.path[1],
520
                     (change.path[1], helpers.kind_to_mode(
521
                         'file', change.executable[1]))))
522
            elif change.kind[1] == 'symlink':
7222.1.1 by Jelmer Vernooij
Fix a spurious test.
523
                yield commands.FileModifyCommand(
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
524
                    change.path[1].encode("utf-8"),
0.123.8 by Jelmer Vernooij
Use modes for FileModifyCommand.
525
                    helpers.kind_to_mode('symlink', False),
7427.2.1 by Jelmer Vernooij
merge lp:brz/3.0.
526
                    None, tree_new.get_symlink_target(
527
                        change.path[1]).encode('utf-8'))
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
528
            elif change.kind[1] == 'directory':
0.105.1 by John Whitley
Don't emit directory info when plain format is specified.
529
                if not self.plain_format:
7222.1.1 by Jelmer Vernooij
Fix a spurious test.
530
                    yield commands.FileModifyCommand(
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
531
                        change.path[1].encode("utf-8"),
7222.1.1 by Jelmer Vernooij
Fix a spurious test.
532
                        helpers.kind_to_mode('directory', False), None,
533
                        None)
0.64.166 by Ian Clatworthy
graceful handling of faulty revisions (David Reitter)
534
            else:
0.102.14 by Ian Clatworthy
export and import empty directories
535
                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.
536
                             (change.path[1], change.kind[1]))
7463.3.5 by Jelmer Vernooij
More batching.
537
7463.3.7 by Jelmer Vernooij
Add TODO.
538
        # TODO(jelmer): Improve performance on remote repositories
539
        # by using Repository.iter_files_bytes for bzr repositories here.
7463.3.2 by Jelmer Vernooij
Fix SyntaxError.
540
        for (path, mode), chunks in tree_new.iter_files_bytes(files_to_get):
7222.1.1 by Jelmer Vernooij
Fix a spurious test.
541
            yield commands.FileModifyCommand(
542
                path.encode("utf-8"), mode, None, b''.join(chunks))
0.64.166 by Ian Clatworthy
graceful handling of faulty revisions (David Reitter)
543
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
544
    def _process_renames_and_deletes(self, renames, deletes,
7143.15.2 by Jelmer Vernooij
Run autopep8.
545
                                     revision_id, tree_old):
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
546
        file_cmds = []
547
        modifies = []
548
        renamed = []
549
550
        # See https://bugs.edge.launchpad.net/bzr-fastimport/+bug/268933.
551
        # In a nutshell, there are several nasty cases:
552
        #
553
        # 1) bzr rm a; bzr mv b a; bzr commit
554
        # 2) bzr mv x/y z; bzr rm x; commmit
555
        #
556
        # The first must come out with the delete first like this:
557
        #
558
        # D a
559
        # R b a
560
        #
561
        # The second case must come out with the rename first like this:
562
        #
563
        # R x/y z
564
        # D x
565
        #
566
        # So outputting all deletes first or all renames first won't work.
567
        # Instead, we need to make multiple passes over the various lists to
568
        # get the ordering right.
569
0.64.237 by Ian Clatworthy
implicitly rename children on export when directory renamed
570
        must_be_renamed = {}
571
        old_to_new = {}
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
572
        deleted_paths = set([change.path[0] for change in deletes])
573
        for change in renames:
574
            emit = change.kind[1] != 'directory' or not self.plain_format
575
            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
576
                if emit:
7143.15.2 by Jelmer Vernooij
Run autopep8.
577
                    file_cmds.append(commands.FileDeleteCommand(
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
578
                        change.path[1].encode("utf-8")))
579
                deleted_paths.remove(change.path[1])
580
            if (self.is_empty_dir(tree_old, change.path[0])):
581
                self.note("Skipping empty dir %s in rev %s" % (change.path[0],
7143.15.2 by Jelmer Vernooij
Run autopep8.
582
                                                               revision_id))
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
583
                continue
7143.15.2 by Jelmer Vernooij
Run autopep8.
584
            # oldpath = self._adjust_path_for_renames(oldpath, renamed,
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
585
            #    revision_id)
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
586
            renamed.append(change.path)
587
            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
588
            if emit:
0.64.299 by Jelmer Vernooij
utf8 decode/encode paths and committer/author email/name, as python-fastimport no longer does so.
589
                file_cmds.append(
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
590
                    commands.FileRenameCommand(change.path[0].encode("utf-8"), change.path[1].encode("utf-8")))
591
            if change.changed_content or change.meta_modified():
592
                modifies.append(change)
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
593
0.64.237 by Ian Clatworthy
implicitly rename children on export when directory renamed
594
            # Renaming a directory implies all children must be renamed.
595
            # Note: changes_from() doesn't handle this
7463.3.2 by Jelmer Vernooij
Fix SyntaxError.
596
            if change.kind == ('directory', 'directory'):
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
597
                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
598
                    if e.kind == 'directory' and self.plain_format:
599
                        continue
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
600
                    old_child_path = osutils.pathjoin(change.path[0], p)
601
                    new_child_path = osutils.pathjoin(change.path[1], p)
0.64.237 by Ian Clatworthy
implicitly rename children on export when directory renamed
602
                    must_be_renamed[old_child_path] = new_child_path
603
604
        # Add children not already renamed
605
        if must_be_renamed:
606
            renamed_already = set(old_to_new.keys())
607
            still_to_be_renamed = set(must_be_renamed.keys()) - renamed_already
608
            for old_child_path in sorted(still_to_be_renamed):
609
                new_child_path = must_be_renamed[old_child_path]
610
                if self.verbose:
611
                    self.note("implicitly renaming %s => %s" % (old_child_path,
7143.15.2 by Jelmer Vernooij
Run autopep8.
612
                                                                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.
613
                file_cmds.append(commands.FileRenameCommand(old_child_path.encode("utf-8"),
7143.15.2 by Jelmer Vernooij
Run autopep8.
614
                                                            new_child_path.encode("utf-8")))
0.64.237 by Ian Clatworthy
implicitly rename children on export when directory renamed
615
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
616
        # Record remaining deletes
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
617
        for change in deletes:
618
            if change.path[0] not in deleted_paths:
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
619
                continue
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
620
            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
621
                continue
0.64.178 by Ian Clatworthy
improve fast-export's handling of rename+delete combinations
622
            #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.
623
            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
624
        return file_cmds, modifies, renamed
625
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
626
    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
627
        # If a previous rename is found, we should adjust the path
628
        for old, new in renamed:
629
            if path == old:
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
630
                self.note("Changing path %s given rename to %s in revision %s"
7143.15.2 by Jelmer Vernooij
Run autopep8.
631
                          % (path, new, revision_id))
0.64.174 by Ian Clatworthy
fix rename adjustment & kind change logic in fast-export
632
                path = new
633
            elif path.startswith(old + '/'):
0.64.176 by Ian Clatworthy
faster export of revision range & improved diagnostics in fast-export
634
                self.note(
635
                    "Adjusting path %s given rename of %s to %s in revision %s"
636
                    % (path, old, new, revision_id))
0.64.174 by Ian Clatworthy
fix rename adjustment & kind change logic in fast-export
637
                path = path.replace(old + "/", new + "/")
638
        return path
639
0.68.1 by Pieter de Bie
Classify bzr-fast-export
640
    def emit_tags(self):
7479.2.1 by Jelmer Vernooij
Drop python2 support.
641
        for tag, revid in self.branch.tags.get_tag_dict().items():
0.68.1 by Pieter de Bie
Classify bzr-fast-export
642
            try:
643
                mark = self.revid_to_mark[revid]
644
            except KeyError:
0.79.4 by Ian Clatworthy
use note and warning APIs
645
                self.warning('not creating tag %r pointing to non-existent '
7143.15.2 by Jelmer Vernooij
Run autopep8.
646
                             'revision %s' % (tag, revid))
0.68.1 by Pieter de Bie
Classify bzr-fast-export
647
            else:
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
648
                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.
649
                if self.plain_format and not check_ref_format(git_ref):
0.133.2 by Oleksandr Usov
Rewrite tag names when exporting plain stream
650
                    if self.rewrite_tags:
0.133.3 by Oleksandr Usov
Implement comments from patch review:
651
                        new_ref = sanitize_ref_name_for_git(git_ref)
0.133.2 by Oleksandr Usov
Rewrite tag names when exporting plain stream
652
                        self.warning('tag %r is exported as %r to be valid in git.',
653
                                     git_ref, new_ref)
654
                        git_ref = new_ref
0.133.3 by Oleksandr Usov
Implement comments from patch review:
655
                    else:
0.133.2 by Oleksandr Usov
Rewrite tag names when exporting plain stream
656
                        self.warning('not creating tag %r as its name would not be '
657
                                     'valid in git.', git_ref)
658
                        continue
7490.78.1 by Jelmer Vernooij
Fix marks file handling on Python 3.
659
                self.print_cmd(commands.ResetCommand(git_ref, b":%s" % mark))