/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/plugins/fastimport/exporter.py

  • Committer: Jelmer Vernooij
  • Date: 2018-11-17 00:47:52 UTC
  • mfrom: (7182 work)
  • mto: This revision was merged to the branch mainline in revision 7305.
  • Revision ID: jelmer@jelmer.uk-20181117004752-6ywampe5pfywlby4
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
51
51
    from email.utils import parseaddr
52
52
except ImportError:  # python < 3
53
53
    from email.Utils import parseaddr
54
 
import sys, time, re
 
54
import sys
 
55
import time
 
56
import re
55
57
 
56
58
import breezy.branch
57
59
import breezy.revision
75
77
    )
76
78
 
77
79
lazy_import.lazy_import(globals(),
78
 
"""
 
80
                        """
79
81
from fastimport import commands
80
82
""")
81
83
 
90
92
        return open(destination, 'wb')
91
93
 
92
94
# from dulwich.repo:
 
95
 
 
96
 
93
97
def check_ref_format(refname):
94
98
    """Check if a refname is correctly formatted.
95
99
 
109
113
    if b'..' in refname:
110
114
        return False
111
115
    for i in range(len(refname)):
112
 
        if ord(refname[i:i+1]) < 0o40 or refname[i] in b'\177 ~^:?*[':
 
116
        if ord(refname[i:i + 1]) < 0o40 or refname[i] in b'\177 ~^:?*[':
113
117
            return False
114
118
    if refname[-1] in b'/.':
115
119
        return False
157
161
class BzrFastExporter(object):
158
162
 
159
163
    def __init__(self, source, outf, ref=None, checkpoint=-1,
160
 
        import_marks_file=None, export_marks_file=None, revision=None,
161
 
        verbose=False, plain_format=False, rewrite_tags=False,
162
 
        no_tags=False, baseline=False):
 
164
                 import_marks_file=None, export_marks_file=None, revision=None,
 
165
                 verbose=False, plain_format=False, rewrite_tags=False,
 
166
                 no_tags=False, baseline=False):
163
167
        """Export branch data in fast import format.
164
168
 
165
169
        :param plain_format: if True, 'classic' fast-import format is
185
189
        self.no_tags = no_tags
186
190
        self.baseline = baseline
187
191
        self._multi_author_api_available = hasattr(breezy.revision.Revision,
188
 
            'get_apparent_authors')
 
192
                                                   'get_apparent_authors')
189
193
        self.properties_to_exclude = ['authors', 'author']
190
194
 
191
195
        # Progress reporting stuff
204
208
            marks_info = marks_file.import_marks(self.import_marks_file)
205
209
            if marks_info is not None:
206
210
                self.revid_to_mark = dict((r, m) for m, r in
207
 
                    marks_info.items())
 
211
                                          marks_info.items())
208
212
                # These are no longer included in the marks file
209
213
                #self.branch_names = marks_info[1]
210
214
 
211
215
    def interesting_history(self):
212
216
        if self.revision:
213
217
            rev1, rev2 = builtins._get_revision_range(self.revision,
214
 
                self.branch, "fast-export")
 
218
                                                      self.branch, "fast-export")
215
219
            start_rev_id = rev1.rev_id
216
220
            end_rev_id = rev2.rev_id
217
221
        else:
219
223
            end_rev_id = None
220
224
        self.note("Calculating the revisions to include ...")
221
225
        view_revisions = [rev_id for rev_id, _, _, _ in
222
 
            self.branch.iter_merge_sorted_revisions(end_rev_id, start_rev_id)]
 
226
                          self.branch.iter_merge_sorted_revisions(end_rev_id, start_rev_id)]
223
227
        view_revisions.reverse()
224
228
        # If a starting point was given, we need to later check that we don't
225
229
        # start emitting revisions from before that point. Collect the
227
231
        if start_rev_id is not None:
228
232
            self.note("Calculating the revisions to exclude ...")
229
233
            self.excluded_revisions = set([rev_id for rev_id, _, _, _ in
230
 
                self.branch.iter_merge_sorted_revisions(start_rev_id)])
 
234
                                           self.branch.iter_merge_sorted_revisions(start_rev_id)])
231
235
            if self.baseline:
232
236
                # needed so the first relative commit knows its parent
233
237
                self.excluded_revisions.remove(start_rev_id)
240
244
            interesting = self.interesting_history()
241
245
            self._commit_total = len(interesting)
242
246
            self.note("Starting export of %d revisions ..." %
243
 
                self._commit_total)
 
247
                      self._commit_total)
244
248
            if not self.plain_format:
245
249
                self.emit_features()
246
250
            if self.baseline:
287
291
        time_required = progress.str_tdelta(time.time() - self._start_time)
288
292
        rc = len(self.revid_to_mark)
289
293
        self.note("Exported %d %s in %s",
290
 
            rc, helpers.single_plural(rc, "revision", "revisions"),
291
 
            time_required)
 
294
                  rc, helpers.single_plural(rc, "revision", "revisions"),
 
295
                  time_required)
292
296
 
293
297
    def print_cmd(self, cmd):
294
298
        if PY3:
308
312
                return False
309
313
        except bazErrors.NoSuchFile:
310
314
            self.warning("Skipping empty_dir detection - no file_id for %s" %
311
 
                (path,))
 
315
                         (path,))
312
316
            return False
313
317
 
314
318
        # Use treewalk to find the contents of our directory
327
331
        revobj = self.branch.repository.get_revision(revid)
328
332
        mark = 1
329
333
        self.revid_to_mark[revid] = mark
330
 
        file_cmds = self._get_filecommands(breezy.revision.NULL_REVISION, revid)
 
334
        file_cmds = self._get_filecommands(
 
335
            breezy.revision.NULL_REVISION, revid)
331
336
        self.print_cmd(self._get_commit_command(ref, mark, revobj, file_cmds))
332
337
 
333
338
    def emit_commit(self, revid, ref):
341
346
            # This is a ghost revision. Mark it as not found and next!
342
347
            self.revid_to_mark[revid] = -1
343
348
            return
344
 
 
 
349
 
345
350
        # Get the primary parent
346
351
        # TODO: Consider the excluded revisions when deciding the parents.
347
352
        # Currently, a commit with parents that are excluded ought to be
368
373
 
369
374
        # Report progress and checkpoint if it's time for that
370
375
        self.report_progress(ncommits)
371
 
        if (self.checkpoint is not None and self.checkpoint > 0 and ncommits
372
 
            and ncommits % self.checkpoint == 0):
 
376
        if (self.checkpoint is not None and self.checkpoint > 0 and ncommits and
 
377
                ncommits % self.checkpoint == 0):
373
378
            self.note("Exported %i commits - adding checkpoint to output"
374
 
                % ncommits)
 
379
                      % ncommits)
375
380
            self._save_marks()
376
381
            self.print_cmd(commands.CheckpointCommand())
377
382
 
446
451
 
447
452
        # Build and return the result
448
453
        return commands.CommitCommand(git_ref, mark, author_info,
449
 
            committer_info, revobj.message.encode("utf-8"), from_, merges, iter(file_cmds),
450
 
            more_authors=more_author_info, properties=properties)
 
454
                                      committer_info, revobj.message.encode(
 
455
                                          "utf-8"), from_, merges, iter(file_cmds),
 
456
                                      more_authors=more_author_info, properties=properties)
451
457
 
452
458
    def _get_revision_trees(self, parent, revision_id):
453
459
        try:
454
460
            tree_old = self.branch.repository.revision_tree(parent)
455
461
        except bazErrors.UnexpectedInventoryFormat:
456
 
            self.warning("Parent is malformed - diffing against previous parent")
 
462
            self.warning(
 
463
                "Parent is malformed - diffing against previous parent")
457
464
            # We can't find the old parent. Let's diff against his parent
458
465
            pp = self.branch.repository.get_revision(parent)
459
466
            tree_old = self.branch.repository.revision_tree(pp.parent_ids[0])
475
482
        changes = tree_new.changes_from(tree_old)
476
483
 
477
484
        # Make "modified" have 3-tuples, as added does
478
 
        my_modified = [ x[0:3] for x in changes.modified ]
 
485
        my_modified = [x[0:3] for x in changes.modified]
479
486
 
480
487
        # The potential interaction between renames and deletes is messy.
481
488
        # Handle it here ...
488
495
            # IGC: I don't understand why a delete is needed here.
489
496
            # In fact, it seems harmful? If you uncomment this line,
490
497
            # please file a bug explaining why you needed to.
491
 
            #file_cmds.append(commands.FileDeleteCommand(path))
 
498
            # file_cmds.append(commands.FileDeleteCommand(path))
492
499
            my_modified.append((path, id_, kind2))
493
500
 
494
501
        # Record modifications
495
502
        for path, id_, kind in changes.added + my_modified + rd_modifies:
496
503
            if kind == 'file':
497
 
                text = tree_new.get_file_text(path, id_)
498
 
                file_cmds.append(commands.FileModifyCommand(path.encode("utf-8"),
499
 
                    helpers.kind_to_mode(
500
 
                        'file', tree_new.is_executable(path, id_)),
 
504
                text = tree_new.get_file_text(path)
 
505
                file_cmds.append(commands.FileModifyCommand(
 
506
                    path.encode("utf-8"),
 
507
                    helpers.kind_to_mode('file', tree_new.is_executable(path)),
501
508
                    None, text))
502
509
            elif kind == 'symlink':
503
 
                file_cmds.append(commands.FileModifyCommand(path.encode("utf-8"),
 
510
                file_cmds.append(commands.FileModifyCommand(
 
511
                    path.encode("utf-8"),
504
512
                    helpers.kind_to_mode('symlink', False),
505
 
                    None, tree_new.get_symlink_target(path, id_)))
 
513
                    None, tree_new.get_symlink_target(path)))
506
514
            elif kind == 'directory':
507
515
                if not self.plain_format:
508
516
                    file_cmds.append(
509
 
                            commands.FileModifyCommand(path.encode("utf-8"),
510
 
                                helpers.kind_to_mode('directory', False), None,
511
 
                                None))
 
517
                        commands.FileModifyCommand(
 
518
                            path.encode("utf-8"),
 
519
                            helpers.kind_to_mode('directory', False), None,
 
520
                            None))
512
521
            else:
513
522
                self.warning("cannot export '%s' of kind %s yet - ignoring" %
514
 
                    (path, kind))
 
523
                             (path, kind))
515
524
        return file_cmds
516
525
 
517
526
    def _process_renames_and_deletes(self, renames, deletes,
518
 
        revision_id, tree_old):
 
527
                                     revision_id, tree_old):
519
528
        file_cmds = []
520
529
        modifies = []
521
530
        renamed = []
548
557
            emit = kind != 'directory' or not self.plain_format
549
558
            if newpath in deleted_paths:
550
559
                if emit:
551
 
                    file_cmds.append(commands.FileDeleteCommand(newpath.encode("utf-8")))
 
560
                    file_cmds.append(commands.FileDeleteCommand(
 
561
                        newpath.encode("utf-8")))
552
562
                deleted_paths.remove(newpath)
553
563
            if (self.is_empty_dir(tree_old, oldpath)):
554
564
                self.note("Skipping empty dir %s in rev %s" % (oldpath,
555
 
                    revision_id))
 
565
                                                               revision_id))
556
566
                continue
557
 
            #oldpath = self._adjust_path_for_renames(oldpath, renamed,
 
567
            # oldpath = self._adjust_path_for_renames(oldpath, renamed,
558
568
            #    revision_id)
559
569
            renamed.append([oldpath, newpath])
560
570
            old_to_new[oldpath] = newpath
582
592
                new_child_path = must_be_renamed[old_child_path]
583
593
                if self.verbose:
584
594
                    self.note("implicitly renaming %s => %s" % (old_child_path,
585
 
                        new_child_path))
 
595
                                                                new_child_path))
586
596
                file_cmds.append(commands.FileRenameCommand(old_child_path.encode("utf-8"),
587
 
                    new_child_path.encode("utf-8")))
 
597
                                                            new_child_path.encode("utf-8")))
588
598
 
589
599
        # Record remaining deletes
590
600
        for path, id_, kind in deletes:
601
611
        for old, new in renamed:
602
612
            if path == old:
603
613
                self.note("Changing path %s given rename to %s in revision %s"
604
 
                    % (path, new, revision_id))
 
614
                          % (path, new, revision_id))
605
615
                path = new
606
616
            elif path.startswith(old + '/'):
607
617
                self.note(
616
626
                mark = self.revid_to_mark[revid]
617
627
            except KeyError:
618
628
                self.warning('not creating tag %r pointing to non-existent '
619
 
                    'revision %s' % (tag, revid))
 
629
                             'revision %s' % (tag, revid))
620
630
            else:
621
631
                git_ref = b'refs/tags/%s' % tag.encode("utf-8")
622
632
                if self.plain_format and not check_ref_format(git_ref):