/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/git/tree.py

  • Committer: Jelmer Vernooij
  • Date: 2019-12-23 01:39:21 UTC
  • mfrom: (7424 work)
  • mto: This revision was merged to the branch mainline in revision 7425.
  • Revision ID: jelmer@jelmer.uk-20191223013921-2kzd0wlcoylgxksk
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
67
67
from .mapping import (
68
68
    mode_is_executable,
69
69
    mode_kind,
70
 
    GitFileIdMap,
71
70
    default_mapping,
72
71
    )
73
72
 
253
252
    def __init__(self, repository, revision_id):
254
253
        self._revision_id = revision_id
255
254
        self._repository = repository
 
255
        self._submodules = None
256
256
        self.store = repository._git.object_store
257
257
        if not isinstance(revision_id, bytes):
258
258
            raise TypeError(revision_id)
261
261
        if revision_id == NULL_REVISION:
262
262
            self.tree = None
263
263
            self.mapping = default_mapping
264
 
            self._fileid_map = GitFileIdMap(
265
 
                {},
266
 
                default_mapping)
267
264
        else:
268
265
            try:
269
266
                commit = self.store[self.commit_id]
270
267
            except KeyError:
271
268
                raise errors.NoSuchRevision(repository, revision_id)
272
269
            self.tree = commit.tree
273
 
            self._fileid_map = self.mapping.get_fileid_map(
274
 
                self.store.__getitem__, self.tree)
275
 
 
276
 
    def _get_nested_repository(self, path):
277
 
        nested_repo_transport = self._repository.user_transport.clone(path)
 
270
 
 
271
    def _submodule_info(self):
 
272
        if self._submodules is None:
 
273
            try:
 
274
                with self.get_file('.gitmodules') as f:
 
275
                    config = GitConfigFile.from_file(f)
 
276
                    self._submodules = {
 
277
                        path: (url, section)
 
278
                        for path, url, section in parse_submodules(config)}
 
279
            except errors.NoSuchFile:
 
280
                self._submodules = {}
 
281
        return self._submodules
 
282
 
 
283
    def _get_submodule_repository(self, relpath):
 
284
        if not isinstance(relpath, bytes):
 
285
            raise TypeError(relpath)
 
286
        try:
 
287
            info = self._submodule_info()[relpath]
 
288
        except KeyError:
 
289
            nested_repo_transport = self._repository.user_transport.clone(relpath.decode('utf-8'))
 
290
        else:
 
291
            nested_repo_transport = self._repository.control_transport.clone(
 
292
                posixpath.join('modules', info[0]))
278
293
        nested_controldir = _mod_controldir.ControlDir.open_from_transport(
279
294
            nested_repo_transport)
280
295
        return nested_controldir.find_repository()
281
296
 
 
297
    def get_nested_tree(self, path):
 
298
        encoded_path = path.encode('utf-8')
 
299
        nested_repo = self._get_submodule_repository(encoded_path)
 
300
        ref_rev = self.get_reference_revision(path)
 
301
        return nested_repo.revision_tree(ref_rev)
 
302
 
282
303
    def supports_rename_tracking(self):
283
304
        return False
284
305
 
304
325
 
305
326
    def id2path(self, file_id):
306
327
        try:
307
 
            path = self._fileid_map.lookup_path(file_id)
 
328
            path = self.mapping.parse_file_id(file_id)
308
329
        except ValueError:
309
330
            raise errors.NoSuchId(self, file_id)
310
331
        if self.is_versioned(path):
319
340
            return None
320
341
        if not self.is_versioned(path):
321
342
            return None
322
 
        return self._fileid_map.lookup_file_id(osutils.safe_unicode(path))
 
343
        return self.mapping.generate_file_id(osutils.safe_unicode(path))
323
344
 
324
345
    def all_file_ids(self):
325
346
        raise errors.UnsupportedOperation(self.all_file_ids, self)
339
360
                    todo.append((store, subpath, hexsha))
340
361
        return ret
341
362
 
342
 
    def get_root_id(self):
343
 
        if self.tree is None:
344
 
            return None
345
 
        return self.path2id("")
346
 
 
347
 
    def has_or_had_id(self, file_id):
348
 
        try:
349
 
            self.id2path(file_id)
350
 
        except errors.NoSuchId:
351
 
            return False
352
 
        return True
353
 
 
354
 
    def has_id(self, file_id):
355
 
        try:
356
 
            path = self.id2path(file_id)
357
 
        except errors.NoSuchId:
358
 
            return False
359
 
        return self.has_filename(path)
360
 
 
361
363
    def _lookup_path(self, path):
362
364
        if self.tree is None:
363
365
            raise errors.NoSuchFile(path)
401
403
            root_ie = self._get_dir_ie(b"", None)
402
404
        else:
403
405
            parent_path = posixpath.dirname(from_dir)
404
 
            parent_id = self._fileid_map.lookup_file_id(parent_path)
 
406
            parent_id = self.mapping.generate_file_id(parent_path)
405
407
            if mode_kind(mode) == 'directory':
406
408
                root_ie = self._get_dir_ie(from_dir.encode("utf-8"), parent_id)
407
409
            else:
441
443
        kind = mode_kind(mode)
442
444
        path = path.decode('utf-8')
443
445
        name = name.decode("utf-8")
444
 
        file_id = self._fileid_map.lookup_file_id(path)
 
446
        file_id = self.mapping.generate_file_id(path)
445
447
        ie = entry_factory[kind](file_id, name, parent_id)
446
448
        if kind == 'symlink':
447
449
            ie.symlink_target = store[hexsha].data.decode('utf-8')
457
459
 
458
460
    def _get_dir_ie(self, path, parent_id):
459
461
        path = path.decode('utf-8')
460
 
        file_id = self._fileid_map.lookup_file_id(path)
 
462
        file_id = self.mapping.generate_file_id(path)
461
463
        return GitTreeDirectory(file_id, posixpath.basename(path), parent_id)
462
464
 
463
465
    def iter_child_entries(self, path):
479
481
                yield self._get_file_ie(store, child_path, name, mode, hexsha,
480
482
                                        file_id)
481
483
 
482
 
    def iter_entries_by_dir(self, specific_files=None, yield_parents=False):
 
484
    def iter_entries_by_dir(self, specific_files=None):
483
485
        if self.tree is None:
484
486
            return
485
 
        if yield_parents:
486
 
            # TODO(jelmer): Support yield parents
487
 
            raise NotImplementedError
488
487
        if specific_files is not None:
489
488
            if specific_files in ([""], []):
490
489
                specific_files = None
491
490
            else:
492
491
                specific_files = set([p.encode('utf-8')
493
492
                                      for p in specific_files])
494
 
        todo = deque([(self.store, b"", self.tree, self.get_root_id())])
 
493
        todo = deque([(self.store, b"", self.tree, self.path2id(''))])
495
494
        if specific_files is None or u"" in specific_files:
496
495
            yield u"", self._get_dir_ie(b"", None)
497
496
        while todo:
524
523
        if self.supports_tree_reference():
525
524
            for path, entry in self.iter_entries_by_dir():
526
525
                if entry.kind == 'tree-reference':
527
 
                    yield path, self.mapping.generate_file_id(b'')
 
526
                    yield path
528
527
 
529
528
    def get_revision_id(self):
530
529
        """See RevisionTree.get_revision_id."""
565
564
        """See RevisionTree.get_symlink_target."""
566
565
        (store, mode, hexsha) = self._lookup_path(path)
567
566
        if S_ISGITLINK(mode):
568
 
            nested_repo = self._get_nested_repository(path)
 
567
            nested_repo = self._get_submodule_repository(path.encode('utf-8'))
569
568
            return nested_repo.lookup_foreign_revision_id(hexsha)
570
569
        else:
571
570
            return None
590
589
        elif kind == 'symlink':
591
590
            return (kind, None, None, store[hexsha].data.decode('utf-8'))
592
591
        elif kind == 'tree-reference':
593
 
            nested_repo = self._get_nested_repository(path)
 
592
            nested_repo = self._get_submodule_repository(path.encode('utf-8'))
594
593
            return (kind, None, None,
595
594
                    nested_repo.lookup_foreign_revision_id(hexsha))
596
595
        else:
666
665
            yield (path_decoded, parent_id), children
667
666
 
668
667
 
669
 
def tree_delta_from_git_changes(changes, mapping,
670
 
                                fileid_maps, specific_files=None,
 
668
def tree_delta_from_git_changes(changes, mappings,
 
669
                                specific_files=None,
671
670
                                require_versioned=False, include_root=False,
672
671
                                target_extras=None):
673
672
    """Create a TreeDelta from two git trees.
675
674
    source and target are iterators over tuples with:
676
675
        (filename, sha, mode)
677
676
    """
678
 
    (old_fileid_map, new_fileid_map) = fileid_maps
 
677
    (old_mapping, new_mapping) = mappings
679
678
    if target_extras is None:
680
679
        target_extras = set()
681
680
    ret = delta.TreeDelta()
683
682
    for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes:
684
683
        if newpath == b'' and not include_root:
685
684
            continue
686
 
        if oldpath is None:
 
685
        if oldpath is not None:
 
686
            oldpath_decoded = oldpath.decode('utf-8')
 
687
        else:
687
688
            oldpath_decoded = None
 
689
        if newpath is not None:
 
690
            newpath_decoded = newpath.decode('utf-8')
688
691
        else:
689
 
            oldpath_decoded = oldpath.decode('utf-8')
690
 
        if newpath is None:
691
692
            newpath_decoded = None
692
 
        else:
693
 
            newpath_decoded = newpath.decode('utf-8')
694
693
        if not (specific_files is None or
695
694
                (oldpath is not None and
696
695
                    osutils.is_inside_or_parent_of_any(
699
698
                    osutils.is_inside_or_parent_of_any(
700
699
                        specific_files, newpath_decoded))):
701
700
            continue
702
 
        if mapping.is_special_file(oldpath):
 
701
 
 
702
        if oldpath_decoded is None:
 
703
            fileid = new_mapping.generate_file_id(newpath_decoded)
 
704
            oldexe = None
 
705
            oldkind = None
 
706
            oldname = None
 
707
            oldparent = None
 
708
            oldversioned = False
 
709
        else:
 
710
            oldversioned = True
 
711
            if oldmode:
 
712
                oldexe = mode_is_executable(oldmode)
 
713
                oldkind = mode_kind(oldmode)
 
714
            else:
 
715
                oldexe = False
 
716
                oldkind = None
 
717
            if oldpath_decoded == u'':
 
718
                oldparent = None
 
719
                oldname = u''
 
720
            else:
 
721
                (oldparentpath, oldname) = osutils.split(oldpath_decoded)
 
722
                oldparent = old_mapping.generate_file_id(oldparentpath)
 
723
            fileid = old_mapping.generate_file_id(oldpath_decoded)
 
724
        if newpath_decoded is None:
 
725
            newexe = None
 
726
            newkind = None
 
727
            newname = None
 
728
            newparent = None
 
729
            newversioned = False
 
730
        else:
 
731
            newversioned = (newpath_decoded not in target_extras)
 
732
            if newmode:
 
733
                newexe = mode_is_executable(newmode)
 
734
                newkind = mode_kind(newmode)
 
735
            else:
 
736
                newexe = False
 
737
                newkind = None
 
738
            if newpath_decoded == u'':
 
739
                newparent = None
 
740
                newname = u''
 
741
            else:
 
742
                newparentpath, newname = osutils.split(newpath_decoded)
 
743
                newparent = new_mapping.generate_file_id(newparentpath)
 
744
        if old_mapping.is_special_file(oldpath):
703
745
            oldpath = None
704
 
        if mapping.is_special_file(newpath):
 
746
        if new_mapping.is_special_file(newpath):
705
747
            newpath = None
706
748
        if oldpath is None and newpath is None:
707
749
            continue
 
750
        change = _mod_tree.TreeChange(
 
751
            fileid, (oldpath_decoded, newpath_decoded), (oldsha != newsha),
 
752
            (oldversioned, newversioned),
 
753
            (oldparent, newparent), (oldname, newname),
 
754
            (oldkind, newkind), (oldexe, newexe))
708
755
        if oldpath is None:
709
 
            added.append((newpath, mode_kind(newmode)))
 
756
            added.append((newpath, newkind))
710
757
        elif newpath is None or newmode == 0:
711
 
            file_id = old_fileid_map.lookup_file_id(oldpath_decoded)
712
 
            ret.removed.append((oldpath_decoded, file_id, mode_kind(oldmode)))
 
758
            ret.removed.append(change)
713
759
        elif oldpath != newpath:
714
 
            file_id = old_fileid_map.lookup_file_id(oldpath_decoded)
715
 
            ret.renamed.append(
716
 
                (oldpath_decoded, newpath.decode('utf-8'), file_id,
717
 
                 mode_kind(newmode), (oldsha != newsha),
718
 
                 (oldmode != newmode)))
 
760
            ret.renamed.append(change)
719
761
        elif mode_kind(oldmode) != mode_kind(newmode):
720
 
            file_id = new_fileid_map.lookup_file_id(newpath_decoded)
721
 
            ret.kind_changed.append(
722
 
                (newpath_decoded, file_id, mode_kind(oldmode),
723
 
                 mode_kind(newmode)))
 
762
            ret.kind_changed.append(change)
724
763
        elif oldsha != newsha or oldmode != newmode:
725
764
            if stat.S_ISDIR(oldmode) and stat.S_ISDIR(newmode):
726
765
                continue
727
 
            file_id = new_fileid_map.lookup_file_id(newpath_decoded)
728
 
            ret.modified.append(
729
 
                (newpath_decoded, file_id, mode_kind(newmode),
730
 
                 (oldsha != newsha), (oldmode != newmode)))
 
766
            ret.modified.append(change)
731
767
        else:
732
 
            file_id = new_fileid_map.lookup_file_id(newpath_decoded)
733
 
            ret.unchanged.append(
734
 
                (newpath_decoded, file_id, mode_kind(newmode)))
 
768
            ret.unchanged.append(change)
735
769
 
736
770
    implicit_dirs = {b''}
737
771
    for path, kind in added:
743
777
        if kind == 'directory' and path not in implicit_dirs:
744
778
            continue
745
779
        path_decoded = osutils.normalized_filename(path)[0]
 
780
        parent_path, basename = osutils.split(path_decoded)
 
781
        parent_id = new_mapping.generate_file_id(parent_path)
746
782
        if path in target_extras:
747
 
            ret.unversioned.append((path_decoded, None, kind))
 
783
            ret.unversioned.append(_mod_tree.TreeChange(
 
784
                None, (None, path_decoded),
 
785
                True, (False, False), (None, parent_id),
 
786
                (None, basename), (None, kind), (None, False)))
748
787
        else:
749
 
            file_id = new_fileid_map.lookup_file_id(path_decoded)
750
 
            ret.added.append((path_decoded, file_id, kind))
 
788
            file_id = new_mapping.generate_file_id(path_decoded)
 
789
            ret.added.append(
 
790
                _mod_tree.TreeChange(
 
791
                    file_id, (None, path_decoded), True,
 
792
                    (False, True),
 
793
                    (None, parent_id),
 
794
                    (None, basename), (None, kind), (None, False)))
751
795
 
752
796
    return ret
753
797
 
828
872
            oldkind == 'directory' and newkind == 'directory' and
829
873
                oldpath_decoded == newpath_decoded):
830
874
            continue
831
 
        yield (fileid, (oldpath_decoded, newpath_decoded), (oldsha != newsha),
832
 
               (oldversioned, newversioned),
833
 
               (oldparent, newparent), (oldname, newname),
834
 
               (oldkind, newkind), (oldexe, newexe))
 
875
        yield _mod_tree.TreeChange(
 
876
            fileid, (oldpath_decoded, newpath_decoded), (oldsha != newsha),
 
877
            (oldversioned, newversioned),
 
878
            (oldparent, newparent), (oldname, newname),
 
879
            (oldkind, newkind), (oldexe, newexe))
835
880
 
836
881
 
837
882
class InterGitTrees(_mod_tree.InterTree):
856
901
                specific_files=specific_files,
857
902
                extra_trees=extra_trees,
858
903
                want_unversioned=want_unversioned)
859
 
            source_fileid_map = self.source._fileid_map
860
 
            target_fileid_map = self.target._fileid_map
861
904
            return tree_delta_from_git_changes(
862
 
                changes, self.target.mapping,
863
 
                (source_fileid_map, target_fileid_map),
 
905
                changes, (self.source.mapping, self.target.mapping),
864
906
                specific_files=specific_files,
865
907
                include_root=include_root, target_extras=target_extras)
866
908
 
968
1010
        with self.lock_read():
969
1011
            path = path.rstrip('/')
970
1012
            if self.is_versioned(path.rstrip('/')):
971
 
                return self._fileid_map.lookup_file_id(
 
1013
                return self.mapping.generate_file_id(
972
1014
                    osutils.safe_unicode(path))
973
1015
            return None
974
1016
 
975
 
    def has_id(self, file_id):
976
 
        try:
977
 
            self.id2path(file_id)
978
 
        except errors.NoSuchId:
979
 
            return False
980
 
        else:
981
 
            return True
982
 
 
983
1017
    def id2path(self, file_id):
984
1018
        if file_id is None:
985
1019
            return ''
987
1021
            raise TypeError(file_id)
988
1022
        with self.lock_read():
989
1023
            try:
990
 
                path = self._fileid_map.lookup_path(file_id)
 
1024
                path = self.mapping.parse_file_id(file_id)
991
1025
            except ValueError:
992
1026
                raise errors.NoSuchId(self, file_id)
993
1027
            if self.is_versioned(path):
997
1031
    def _set_root_id(self, file_id):
998
1032
        raise errors.UnsupportedOperation(self._set_root_id, self)
999
1033
 
1000
 
    def get_root_id(self):
1001
 
        return self.path2id(u"")
1002
 
 
1003
1034
    def _add(self, files, ids, kinds):
1004
1035
        for (path, file_id, kind) in zip(files, ids, kinds):
1005
1036
            if file_id is not None:
1099
1130
                if S_ISGITLINK(mode):
1100
1131
                    pass  # TODO(jelmer): dive into submodule
1101
1132
 
1102
 
    def iter_entries_by_dir(self, specific_files=None, yield_parents=False):
1103
 
        if yield_parents:
1104
 
            raise NotImplementedError(self.iter_entries_by_dir)
 
1133
    def iter_entries_by_dir(self, specific_files=None):
1105
1134
        with self.lock_read():
1106
1135
            if specific_files is not None:
1107
1136
                specific_files = set(specific_files)
1123
1152
                    file_ie = self._get_file_ie(name, path, value, None)
1124
1153
                except errors.NoSuchFile:
1125
1154
                    continue
1126
 
                if yield_parents or specific_files is None:
 
1155
                if specific_files is None:
1127
1156
                    for (dir_path, dir_ie) in self._add_missing_parent_ids(
1128
1157
                            parent, dir_ids):
1129
1158
                        ret[(posixpath.dirname(dir_path), dir_path)] = dir_ie
1130
1159
                file_ie.parent_id = self.path2id(parent)
1131
1160
                ret[(posixpath.dirname(path), path)] = file_ie
 
1161
            # Special casing for directories
 
1162
            if specific_files:
 
1163
                for path in specific_files:
 
1164
                    key = (posixpath.dirname(path), path)
 
1165
                    if key not in ret and self.is_versioned(path):
 
1166
                        ret[key] = self._get_dir_ie(path, self.path2id(key[0]))
1132
1167
            return ((path, ie) for ((_, path), ie) in sorted(viewitems(ret)))
1133
1168
 
1134
1169
    def iter_references(self):
1135
1170
        # TODO(jelmer): Implement a more efficient version of this
1136
1171
        for path, entry in self.iter_entries_by_dir():
1137
1172
            if entry.kind == 'tree-reference':
1138
 
                yield path, self.mapping.generate_file_id(b'')
 
1173
                yield path
1139
1174
 
1140
1175
    def _get_dir_ie(self, path, parent_id):
1141
1176
        file_id = self.path2id(path)
1402
1437
    def _live_entry(self, relpath):
1403
1438
        raise NotImplementedError(self._live_entry)
1404
1439
 
 
1440
    def get_transform(self, pb=None):
 
1441
        from ..transform import TreeTransform
 
1442
        return TreeTransform(self, pb=pb)
 
1443
 
 
1444
 
1405
1445
 
1406
1446
class InterIndexGitTree(InterGitTrees):
1407
1447
    """InterTree that works between a Git revision tree and an index."""
1447
1487
    # Report dirified directories to commit_tree first, so that they can be
1448
1488
    # replaced with non-empty directories if they have contents.
1449
1489
    dirified = []
 
1490
    trust_executable = target._supports_executable()
1450
1491
    for path, index_entry in target._recurse_index_entries():
1451
1492
        try:
1452
1493
            live_entry = target._live_entry(path)
1472
1513
                dirified.append((path, Tree().id, stat.S_IFDIR))
1473
1514
                store.add_object(Tree())
1474
1515
            else:
 
1516
                mode = live_entry.mode
 
1517
                if not trust_executable:
 
1518
                    if mode_is_executable(index_entry.mode):
 
1519
                        mode |= 0o111
 
1520
                    else:
 
1521
                        mode &= ~0o111
1475
1522
                blobs[path] = (live_entry.sha, cleanup_mode(live_entry.mode))
1476
1523
    if want_unversioned:
1477
1524
        for e in target.extras():