/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: Breezy landing bot
  • Author(s): Jelmer Vernooij
  • Date: 2020-06-24 02:46:36 UTC
  • mfrom: (7517.1.1 trunk-3.5)
  • Revision ID: breezy.the.bot@gmail.com-20200624024636-nea4994cfz3aymn8
Update installation instructions, explaining need for Python 3.5+ and optional dependencies.

Merged from https://code.launchpad.net/~jelmer/brz/trunk-3.5/+merge/386230

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
"""Git Trees."""
19
19
 
20
 
from __future__ import absolute_import
21
 
 
22
20
from collections import deque
23
21
import errno
24
22
from io import BytesIO
28
26
    parse_submodules,
29
27
    ConfigFile as GitConfigFile,
30
28
    )
31
 
from dulwich.diff_tree import tree_changes
 
29
from dulwich.diff_tree import tree_changes, RenameDetector
32
30
from dulwich.errors import NotTreeError
33
31
from dulwich.index import (
34
32
    blob_from_path_and_stat,
66
64
    CURRENT_REVISION,
67
65
    NULL_REVISION,
68
66
    )
69
 
from ..sixish import (
70
 
    text_type,
71
 
    viewitems,
72
 
    )
73
67
 
74
68
from .mapping import (
75
69
    mode_is_executable,
726
720
def tree_delta_from_git_changes(changes, mappings,
727
721
                                specific_files=None,
728
722
                                require_versioned=False, include_root=False,
729
 
                                target_extras=None):
 
723
                                source_extras=None, target_extras=None):
730
724
    """Create a TreeDelta from two git trees.
731
725
 
732
726
    source and target are iterators over tuples with:
735
729
    (old_mapping, new_mapping) = mappings
736
730
    if target_extras is None:
737
731
        target_extras = set()
 
732
    if source_extras is None:
 
733
        source_extras = set()
738
734
    ret = delta.TreeDelta()
739
735
    added = []
740
 
    for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes:
 
736
    for (change_type, old, new) in changes:
 
737
        (oldpath, oldmode, oldsha) = old
 
738
        (newpath, newmode, newsha) = new
741
739
        if newpath == b'' and not include_root:
742
740
            continue
 
741
        copied = (change_type == 'copy')
743
742
        if oldpath is not None:
744
743
            oldpath_decoded = oldpath.decode('utf-8')
745
744
        else:
757
756
                        specific_files, newpath_decoded))):
758
757
            continue
759
758
 
760
 
        if oldpath_decoded is None:
761
 
            fileid = new_mapping.generate_file_id(newpath_decoded)
 
759
        if oldpath is None:
762
760
            oldexe = None
763
761
            oldkind = None
764
762
            oldname = None
765
763
            oldparent = None
766
764
            oldversioned = False
767
765
        else:
768
 
            oldversioned = True
 
766
            oldversioned = (oldpath not in source_extras)
769
767
            if oldmode:
770
768
                oldexe = mode_is_executable(oldmode)
771
769
                oldkind = mode_kind(oldmode)
772
770
            else:
773
771
                oldexe = False
774
772
                oldkind = None
775
 
            if oldpath_decoded == u'':
 
773
            if oldpath == b'':
776
774
                oldparent = None
777
775
                oldname = u''
778
776
            else:
779
777
                (oldparentpath, oldname) = osutils.split(oldpath_decoded)
780
778
                oldparent = old_mapping.generate_file_id(oldparentpath)
 
779
        if newpath is None:
 
780
            newexe = None
 
781
            newkind = None
 
782
            newname = None
 
783
            newparent = None
 
784
            newversioned = False
 
785
        else:
 
786
            newversioned = (newpath not in target_extras)
 
787
            if newmode:
 
788
                newexe = mode_is_executable(newmode)
 
789
                newkind = mode_kind(newmode)
 
790
            else:
 
791
                newexe = False
 
792
                newkind = None
 
793
            if newpath_decoded == u'':
 
794
                newparent = None
 
795
                newname = u''
 
796
            else:
 
797
                newparentpath, newname = osutils.split(newpath_decoded)
 
798
                newparent = new_mapping.generate_file_id(newparentpath)
 
799
        if oldversioned and not copied:
781
800
            fileid = old_mapping.generate_file_id(oldpath_decoded)
782
 
        if newpath_decoded is None:
783
 
            newexe = None
784
 
            newkind = None
785
 
            newname = None
786
 
            newparent = None
787
 
            newversioned = False
 
801
        elif newversioned:
 
802
            fileid = new_mapping.generate_file_id(newpath_decoded)
788
803
        else:
789
 
            newversioned = (newpath_decoded not in target_extras)
790
 
            if newmode:
791
 
                newexe = mode_is_executable(newmode)
792
 
                newkind = mode_kind(newmode)
793
 
            else:
794
 
                newexe = False
795
 
                newkind = None
796
 
            if newpath_decoded == u'':
797
 
                newparent = None
798
 
                newname = u''
799
 
            else:
800
 
                newparentpath, newname = osutils.split(newpath_decoded)
801
 
                newparent = new_mapping.generate_file_id(newparentpath)
 
804
            fileid = None
802
805
        if old_mapping.is_special_file(oldpath):
803
806
            oldpath = None
804
807
        if new_mapping.is_special_file(newpath):
809
812
            fileid, (oldpath_decoded, newpath_decoded), (oldsha != newsha),
810
813
            (oldversioned, newversioned),
811
814
            (oldparent, newparent), (oldname, newname),
812
 
            (oldkind, newkind), (oldexe, newexe))
813
 
        if oldpath is None:
 
815
            (oldkind, newkind), (oldexe, newexe),
 
816
            copied=copied)
 
817
        if newpath is not None and not newversioned and newkind != 'directory':
 
818
            change.file_id = None
 
819
            ret.unversioned.append(change)
 
820
        elif change_type == 'add':
814
821
            added.append((newpath, newkind))
815
822
        elif newpath is None or newmode == 0:
816
823
            ret.removed.append(change)
817
 
        elif oldpath != newpath:
 
824
        elif change_type == 'delete':
 
825
            ret.removed.append(change)
 
826
        elif change_type == 'copy':
 
827
            if stat.S_ISDIR(oldmode) and stat.S_ISDIR(newmode):
 
828
                continue
 
829
            ret.copied.append(change)
 
830
        elif change_type == 'rename':
 
831
            if stat.S_ISDIR(oldmode) and stat.S_ISDIR(newmode):
 
832
                continue
818
833
            ret.renamed.append(change)
819
834
        elif mode_kind(oldmode) != mode_kind(newmode):
820
835
            ret.kind_changed.append(change)
837
852
        path_decoded = osutils.normalized_filename(path)[0]
838
853
        parent_path, basename = osutils.split(path_decoded)
839
854
        parent_id = new_mapping.generate_file_id(parent_path)
840
 
        if path in target_extras:
841
 
            ret.unversioned.append(_mod_tree.TreeChange(
842
 
                None, (None, path_decoded),
843
 
                True, (False, False), (None, parent_id),
 
855
        file_id = new_mapping.generate_file_id(path_decoded)
 
856
        ret.added.append(
 
857
            _mod_tree.TreeChange(
 
858
                file_id, (None, path_decoded), True,
 
859
                (False, True),
 
860
                (None, parent_id),
844
861
                (None, basename), (None, kind), (None, False)))
845
 
        else:
846
 
            file_id = new_mapping.generate_file_id(path_decoded)
847
 
            ret.added.append(
848
 
                _mod_tree.TreeChange(
849
 
                    file_id, (None, path_decoded), True,
850
 
                    (False, True),
851
 
                    (None, parent_id),
852
 
                    (None, basename), (None, kind), (None, False)))
853
862
 
854
863
    return ret
855
864
 
856
865
 
857
866
def changes_from_git_changes(changes, mapping, specific_files=None,
858
 
                             include_unchanged=False, target_extras=None):
 
867
                             include_unchanged=False, source_extras=None,
 
868
                             target_extras=None):
859
869
    """Create a iter_changes-like generator from a git stream.
860
870
 
861
871
    source and target are iterators over tuples with:
863
873
    """
864
874
    if target_extras is None:
865
875
        target_extras = set()
866
 
    for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes:
 
876
    if source_extras is None:
 
877
        source_extras = set()
 
878
    for (change_type, old, new) in changes:
 
879
        if change_type == 'unchanged' and not include_unchanged:
 
880
            continue
 
881
        (oldpath, oldmode, oldsha) = old
 
882
        (newpath, newmode, newsha) = new
867
883
        if oldpath is not None:
868
884
            oldpath_decoded = oldpath.decode('utf-8')
869
885
        else:
884
900
            continue
885
901
        if newpath is not None and mapping.is_special_file(newpath):
886
902
            continue
887
 
        if oldpath_decoded is None:
888
 
            fileid = mapping.generate_file_id(newpath_decoded)
 
903
        if oldpath is None:
889
904
            oldexe = None
890
905
            oldkind = None
891
906
            oldname = None
892
907
            oldparent = None
893
908
            oldversioned = False
894
909
        else:
895
 
            oldversioned = True
 
910
            oldversioned = (oldpath not in source_extras)
896
911
            if oldmode:
897
912
                oldexe = mode_is_executable(oldmode)
898
913
                oldkind = mode_kind(oldmode)
905
920
            else:
906
921
                (oldparentpath, oldname) = osutils.split(oldpath_decoded)
907
922
                oldparent = mapping.generate_file_id(oldparentpath)
908
 
            fileid = mapping.generate_file_id(oldpath_decoded)
909
 
        if newpath_decoded is None:
 
923
        if newpath is None:
910
924
            newexe = None
911
925
            newkind = None
912
926
            newname = None
913
927
            newparent = None
914
928
            newversioned = False
915
929
        else:
916
 
            newversioned = (newpath_decoded not in target_extras)
 
930
            newversioned = (newpath not in target_extras)
917
931
            if newmode:
918
932
                newexe = mode_is_executable(newmode)
919
933
                newkind = mode_kind(newmode)
927
941
                newparentpath, newname = osutils.split(newpath_decoded)
928
942
                newparent = mapping.generate_file_id(newparentpath)
929
943
        if (not include_unchanged and
930
 
            oldkind == 'directory' and newkind == 'directory' and
 
944
                oldkind == 'directory' and newkind == 'directory' and
931
945
                oldpath_decoded == newpath_decoded):
932
946
            continue
 
947
        if oldversioned and change_type != 'copy':
 
948
            fileid = mapping.generate_file_id(oldpath_decoded)
 
949
        elif newversioned:
 
950
            fileid = mapping.generate_file_id(newpath_decoded)
 
951
        else:
 
952
            fileid = None
933
953
        yield _mod_tree.TreeChange(
934
954
            fileid, (oldpath_decoded, newpath_decoded), (oldsha != newsha),
935
955
            (oldversioned, newversioned),
936
956
            (oldparent, newparent), (oldname, newname),
937
 
            (oldkind, newkind), (oldexe, newexe))
 
957
            (oldkind, newkind), (oldexe, newexe),
 
958
            copied=(change_type == 'copy'))
938
959
 
939
960
 
940
961
class InterGitTrees(_mod_tree.InterTree):
953
974
                extra_trees=None, require_versioned=False, include_root=False,
954
975
                want_unversioned=False):
955
976
        with self.lock_read():
956
 
            changes, target_extras = self._iter_git_changes(
 
977
            changes, source_extras, target_extras = self._iter_git_changes(
957
978
                want_unchanged=want_unchanged,
958
979
                require_versioned=require_versioned,
959
980
                specific_files=specific_files,
962
983
            return tree_delta_from_git_changes(
963
984
                changes, (self.source.mapping, self.target.mapping),
964
985
                specific_files=specific_files,
965
 
                include_root=include_root, target_extras=target_extras)
 
986
                include_root=include_root,
 
987
                source_extras=source_extras, target_extras=target_extras)
966
988
 
967
989
    def iter_changes(self, include_unchanged=False, specific_files=None,
968
990
                     pb=None, extra_trees=[], require_versioned=True,
969
991
                     want_unversioned=False):
970
992
        with self.lock_read():
971
 
            changes, target_extras = self._iter_git_changes(
 
993
            changes, source_extras, target_extras = self._iter_git_changes(
972
994
                want_unchanged=include_unchanged,
973
995
                require_versioned=require_versioned,
974
996
                specific_files=specific_files,
978
1000
                changes, self.target.mapping,
979
1001
                specific_files=specific_files,
980
1002
                include_unchanged=include_unchanged,
 
1003
                source_extras=source_extras,
981
1004
                target_extras=target_extras)
982
1005
 
983
1006
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
997
1020
        paths = set(paths)
998
1021
        ret = {}
999
1022
        changes = self._iter_git_changes(specific_files=paths)[0]
1000
 
        for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes:
 
1023
        for (change_type, old, new) in changes:
 
1024
            oldpath = old[0]
 
1025
            newpath = new[0]
1001
1026
            if oldpath in paths:
1002
1027
                ret[oldpath] = newpath
1003
1028
        for path in paths:
1015
1040
        paths = set(paths)
1016
1041
        ret = {}
1017
1042
        changes = self._iter_git_changes(specific_files=paths)[0]
1018
 
        for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes:
 
1043
        for (change_type, old, new) in changes:
 
1044
            oldpath = old[0]
 
1045
            newpath = new[0]
1019
1046
            if newpath in paths:
1020
1047
                ret[newpath] = oldpath
1021
1048
        for path in paths:
1060
1087
                    self.target._repository._git.object_store])
1061
1088
        else:
1062
1089
            store = self.source._repository._git.object_store
1063
 
        return store.tree_changes(
1064
 
            self.source.tree, self.target.tree, want_unchanged=want_unchanged,
1065
 
            include_trees=True, change_type_same=True), set()
 
1090
        rename_detector = RenameDetector(store)
 
1091
        changes = tree_changes(
 
1092
            store, self.source.tree, self.target.tree,
 
1093
            want_unchanged=want_unchanged, include_trees=True,
 
1094
            change_type_same=True, rename_detector=rename_detector)
 
1095
        return changes, set(), set()
1066
1096
 
1067
1097
 
1068
1098
_mod_tree.InterTree.register_optimiser(InterGitRevisionTrees)
1310
1340
                    key = (posixpath.dirname(path), path)
1311
1341
                    if key not in ret and self.is_versioned(path):
1312
1342
                        ret[key] = self._get_dir_ie(path, self.path2id(key[0]))
1313
 
            return ((path, ie) for ((_, path), ie) in sorted(viewitems(ret)))
 
1343
            return ((path, ie) for ((_, path), ie) in sorted(ret.items()))
1314
1344
 
1315
1345
    def iter_references(self):
1316
1346
        if self.supports_tree_reference():
1325
1355
                                posixpath.basename(path).strip("/"), parent_id)
1326
1356
 
1327
1357
    def _get_file_ie(self, name, path, value, parent_id):
1328
 
        if not isinstance(name, text_type):
 
1358
        if not isinstance(name, str):
1329
1359
            raise TypeError(name)
1330
 
        if not isinstance(path, text_type):
 
1360
        if not isinstance(path, str):
1331
1361
            raise TypeError(path)
1332
1362
        if not isinstance(value, tuple) or len(value) != 10:
1333
1363
            raise TypeError(value)
1601
1631
    def __init__(self, source, target):
1602
1632
        super(InterIndexGitTree, self).__init__(source, target)
1603
1633
        self._index = target.index
 
1634
        if self.source.store == self.target.store:
 
1635
            self.store = self.source.store
 
1636
        else:
 
1637
            self.store = OverlayObjectStore(
 
1638
                [self.source.store, self.target.store])
 
1639
        self.rename_detector = RenameDetector(self.store)
1604
1640
 
1605
1641
    @classmethod
1606
1642
    def is_compatible(cls, source, target):
1619
1655
                require_versioned=require_versioned)
1620
1656
        # TODO(jelmer): Restrict to specific_files, for performance reasons.
1621
1657
        with self.lock_read():
1622
 
            return changes_between_git_tree_and_working_copy(
 
1658
            changes, target_extras = changes_between_git_tree_and_working_copy(
1623
1659
                self.source.store, self.source.tree,
1624
1660
                self.target, want_unchanged=want_unchanged,
1625
 
                want_unversioned=want_unversioned)
 
1661
                want_unversioned=want_unversioned,
 
1662
                rename_detector=self.rename_detector)
 
1663
            return changes, set(), target_extras
1626
1664
 
1627
1665
 
1628
1666
_mod_tree.InterTree.register_optimiser(InterIndexGitTree)
1629
1667
 
1630
1668
 
1631
 
def changes_between_git_tree_and_working_copy(store, from_tree_sha, target,
 
1669
def changes_between_git_tree_and_working_copy(source_store, from_tree_sha, target,
1632
1670
                                              want_unchanged=False,
1633
 
                                              want_unversioned=False):
 
1671
                                              want_unversioned=False,
 
1672
                                              rename_detector=None):
1634
1673
    """Determine the changes between a git tree and a working tree with index.
1635
1674
 
1636
1675
    """
1657
1696
                    blobs[path] = (index_entry.sha, index_entry.mode)
1658
1697
                else:
1659
1698
                    dirified.append((path, Tree().id, stat.S_IFDIR))
1660
 
                    store.add_object(Tree())
 
1699
                    target.store.add_object(Tree())
1661
1700
            else:
1662
1701
                mode = live_entry.mode
1663
1702
                if not trust_executable:
1665
1704
                        mode |= 0o111
1666
1705
                    else:
1667
1706
                        mode &= ~0o111
 
1707
                if live_entry.sha != index_entry.sha:
 
1708
                    rp = path.decode('utf-8')
 
1709
                    if stat.S_ISREG(live_entry.mode):
 
1710
                        blob = Blob()
 
1711
                        with target.get_file(rp) as f:
 
1712
                            blob.data = f.read()
 
1713
                    elif stat.S_ISLNK(live_entry.mode):
 
1714
                        blob = Blob()
 
1715
                        blob.data = target.get_symlink_target(rp).encode(osutils._fs_enc)
 
1716
                    else:
 
1717
                        blob = None
 
1718
                    if blob is not None:
 
1719
                        target.store.add_object(blob)
1668
1720
                blobs[path] = (live_entry.sha, cleanup_mode(live_entry.mode))
1669
1721
    if want_unversioned:
1670
 
        for e in target.extras():
1671
 
            st = target._lstat(e)
 
1722
        for e in target._iter_files_recursive(include_dirs=False):
1672
1723
            try:
1673
 
                np, accessible = osutils.normalized_filename(e)
 
1724
                e, accessible = osutils.normalized_filename(e)
1674
1725
            except UnicodeDecodeError:
1675
1726
                raise errors.BadFilenameEncoding(
1676
1727
                    e, osutils._fs_enc)
 
1728
            np = e.encode('utf-8')
 
1729
            if np in blobs:
 
1730
                continue
 
1731
            st = target._lstat(e)
1677
1732
            if stat.S_ISDIR(st.st_mode):
1678
1733
                blob = Tree()
1679
 
            else:
 
1734
            elif stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode):
1680
1735
                blob = blob_from_path_and_stat(
1681
1736
                    target.abspath(e).encode(osutils._fs_enc), st)
1682
 
            store.add_object(blob)
1683
 
            np = np.encode('utf-8')
 
1737
            else:
 
1738
                continue
 
1739
            target.store.add_object(blob)
1684
1740
            blobs[np] = (blob.id, cleanup_mode(st.st_mode))
1685
1741
            extras.add(np)
1686
1742
    to_tree_sha = commit_tree(
1687
 
        store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()])
1688
 
    return store.tree_changes(
1689
 
        from_tree_sha, to_tree_sha, include_trees=True,
 
1743
        target.store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()])
 
1744
    store = OverlayObjectStore([source_store, target.store])
 
1745
    return tree_changes(
 
1746
        store, from_tree_sha, to_tree_sha, include_trees=True,
 
1747
        rename_detector=rename_detector,
1690
1748
        want_unchanged=want_unchanged, change_type_same=True), extras