446
444
parent_path = posixpath.dirname(from_dir)
447
445
parent_id = self.mapping.generate_file_id(parent_path)
448
446
if mode_kind(mode) == 'directory':
449
root_ie = self._get_dir_ie(encode_git_path(from_dir), parent_id)
447
root_ie = self._get_dir_ie(from_dir.encode("utf-8"), parent_id)
451
449
root_ie = self._get_file_ie(
452
store, encode_git_path(from_dir),
450
store, from_dir.encode("utf-8"),
453
451
posixpath.basename(from_dir), mode, hexsha)
455
453
yield (from_dir, "V", root_ie.kind, root_ie)
457
455
if root_ie.kind == 'directory':
458
todo.append((store, encode_git_path(from_dir),
456
todo.append((store, from_dir.encode("utf-8"),
459
457
b"", hexsha, root_ie.file_id))
461
459
(store, path, relpath, hexsha, parent_id) = todo.pop()
699
697
def walkdirs(self, prefix=u""):
700
698
(store, mode, hexsha) = self._lookup_path(prefix)
702
[(store, encode_git_path(prefix), hexsha, self.path2id(prefix))])
700
[(store, prefix.encode('utf-8'), hexsha, self.path2id(prefix))])
704
702
store, path, tree_sha, parent_id = todo.popleft()
705
path_decoded = decode_git_path(path)
703
path_decoded = path.decode('utf-8')
706
704
tree = store[tree_sha]
708
706
for name, mode, hexsha in tree.iteritems():
709
707
if self.mapping.is_special_file(name):
711
709
child_path = posixpath.join(path, name)
712
file_id = self.path2id(decode_git_path(child_path))
710
file_id = self.path2id(child_path.decode('utf-8'))
713
711
if stat.S_ISDIR(mode):
714
712
todo.append((store, child_path, hexsha, file_id))
716
(decode_git_path(child_path), decode_git_path(name),
714
(child_path.decode('utf-8'), name.decode('utf-8'),
717
715
mode_kind(mode), None,
718
716
file_id, mode_kind(mode)))
719
717
yield (path_decoded, parent_id), children
721
def preview_transform(self, pb=None):
722
from .transform import GitTransformPreview
723
return GitTransformPreview(self, pb=pb)
726
720
def tree_delta_from_git_changes(changes, mappings,
727
721
specific_files=None,
728
722
require_versioned=False, include_root=False,
729
source_extras=None, target_extras=None):
730
724
"""Create a TreeDelta from two git trees.
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()
738
if source_extras is None:
739
source_extras = set()
740
732
ret = delta.TreeDelta()
742
for (change_type, old, new) in changes:
743
(oldpath, oldmode, oldsha) = old
744
(newpath, newmode, newsha) = new
734
for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes:
745
735
if newpath == b'' and not include_root:
747
copied = (change_type == 'copy')
748
737
if oldpath is not None:
749
oldpath_decoded = decode_git_path(oldpath)
738
oldpath_decoded = oldpath.decode('utf-8')
751
740
oldpath_decoded = None
752
741
if newpath is not None:
753
newpath_decoded = decode_git_path(newpath)
742
newpath_decoded = newpath.decode('utf-8')
755
744
newpath_decoded = None
756
745
if not (specific_files is None or
818
803
fileid, (oldpath_decoded, newpath_decoded), (oldsha != newsha),
819
804
(oldversioned, newversioned),
820
805
(oldparent, newparent), (oldname, newname),
821
(oldkind, newkind), (oldexe, newexe),
823
if newpath is not None and not newversioned and newkind != 'directory':
824
change.file_id = None
825
ret.unversioned.append(change)
826
elif change_type == 'add':
806
(oldkind, newkind), (oldexe, newexe))
827
808
added.append((newpath, newkind))
828
809
elif newpath is None or newmode == 0:
829
810
ret.removed.append(change)
830
elif change_type == 'delete':
831
ret.removed.append(change)
832
elif change_type == 'copy':
833
if stat.S_ISDIR(oldmode) and stat.S_ISDIR(newmode):
835
ret.copied.append(change)
836
elif change_type == 'rename':
837
if stat.S_ISDIR(oldmode) and stat.S_ISDIR(newmode):
811
elif oldpath != newpath:
839
812
ret.renamed.append(change)
840
813
elif mode_kind(oldmode) != mode_kind(newmode):
841
814
ret.kind_changed.append(change)
855
828
for path, kind in added:
856
829
if kind == 'directory' and path not in implicit_dirs:
858
path_decoded = decode_git_path(path)
831
path_decoded = osutils.normalized_filename(path)[0]
859
832
parent_path, basename = osutils.split(path_decoded)
860
833
parent_id = new_mapping.generate_file_id(parent_path)
861
file_id = new_mapping.generate_file_id(path_decoded)
863
_mod_tree.TreeChange(
864
file_id, (None, path_decoded), True,
834
if path in target_extras:
835
ret.unversioned.append(_mod_tree.TreeChange(
836
None, (None, path_decoded),
837
True, (False, False), (None, parent_id),
867
838
(None, basename), (None, kind), (None, False)))
840
file_id = new_mapping.generate_file_id(path_decoded)
842
_mod_tree.TreeChange(
843
file_id, (None, path_decoded), True,
846
(None, basename), (None, kind), (None, False)))
872
851
def changes_from_git_changes(changes, mapping, specific_files=None,
873
include_unchanged=False, source_extras=None,
852
include_unchanged=False, target_extras=None):
875
853
"""Create a iter_changes-like generator from a git stream.
877
855
source and target are iterators over tuples with:
947
921
newparentpath, newname = osutils.split(newpath_decoded)
948
922
newparent = mapping.generate_file_id(newparentpath)
949
923
if (not include_unchanged and
950
oldkind == 'directory' and newkind == 'directory' and
924
oldkind == 'directory' and newkind == 'directory' and
951
925
oldpath_decoded == newpath_decoded):
953
if oldversioned and change_type != 'copy':
954
fileid = mapping.generate_file_id(oldpath_decoded)
956
fileid = mapping.generate_file_id(newpath_decoded)
959
927
yield _mod_tree.TreeChange(
960
928
fileid, (oldpath_decoded, newpath_decoded), (oldsha != newsha),
961
929
(oldversioned, newversioned),
962
930
(oldparent, newparent), (oldname, newname),
963
(oldkind, newkind), (oldexe, newexe),
964
copied=(change_type == 'copy'))
931
(oldkind, newkind), (oldexe, newexe))
967
934
class InterGitTrees(_mod_tree.InterTree):
989
956
return tree_delta_from_git_changes(
990
957
changes, (self.source.mapping, self.target.mapping),
991
958
specific_files=specific_files,
992
include_root=include_root,
993
source_extras=source_extras, target_extras=target_extras)
959
include_root=include_root, target_extras=target_extras)
995
961
def iter_changes(self, include_unchanged=False, specific_files=None,
996
962
pb=None, extra_trees=[], require_versioned=True,
997
963
want_unversioned=False):
998
964
with self.lock_read():
999
changes, source_extras, target_extras = self._iter_git_changes(
965
changes, target_extras = self._iter_git_changes(
1000
966
want_unchanged=include_unchanged,
1001
967
require_versioned=require_versioned,
1002
968
specific_files=specific_files,
1628
1583
def _live_entry(self, relpath):
1629
1584
raise NotImplementedError(self._live_entry)
1631
def transform(self, pb=None):
1632
from .transform import GitTreeTransform
1633
return GitTreeTransform(self, pb=pb)
1635
def preview_transform(self, pb=None):
1636
from .transform import GitTransformPreview
1637
return GitTransformPreview(self, pb=pb)
1640
class InterToIndexGitTree(InterGitTrees):
1586
def get_transform(self, pb=None):
1587
from ..transform import TreeTransform
1588
return TreeTransform(self, pb=pb)
1592
class InterIndexGitTree(InterGitTrees):
1641
1593
"""InterTree that works between a Git revision tree and an index."""
1643
1595
def __init__(self, source, target):
1644
super(InterToIndexGitTree, self).__init__(source, target)
1645
if self.source.store == self.target.store:
1646
self.store = self.source.store
1648
self.store = OverlayObjectStore(
1649
[self.source.store, self.target.store])
1650
self.rename_detector = RenameDetector(self.store)
1596
super(InterIndexGitTree, self).__init__(source, target)
1597
self._index = target.index
1653
1600
def is_compatible(cls, source, target):
1666
1613
require_versioned=require_versioned)
1667
1614
# TODO(jelmer): Restrict to specific_files, for performance reasons.
1668
1615
with self.lock_read():
1669
changes, target_extras = changes_between_git_tree_and_working_copy(
1616
return changes_between_git_tree_and_working_copy(
1670
1617
self.source.store, self.source.tree,
1671
1618
self.target, want_unchanged=want_unchanged,
1672
want_unversioned=want_unversioned,
1673
rename_detector=self.rename_detector,
1674
include_trees=include_trees)
1675
return changes, set(), target_extras
1678
_mod_tree.InterTree.register_optimiser(InterToIndexGitTree)
1681
class InterFromIndexGitTree(InterGitTrees):
1682
"""InterTree that works between a Git revision tree and an index."""
1684
def __init__(self, source, target):
1685
super(InterFromIndexGitTree, self).__init__(source, target)
1686
if self.source.store == self.target.store:
1687
self.store = self.source.store
1689
self.store = OverlayObjectStore(
1690
[self.source.store, self.target.store])
1691
self.rename_detector = RenameDetector(self.store)
1694
def is_compatible(cls, source, target):
1695
return (isinstance(target, GitRevisionTree) and
1696
isinstance(source, MutableGitIndexTree))
1698
def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1699
require_versioned=False, extra_trees=None,
1700
want_unversioned=False, include_trees=True):
1701
trees = [self.source]
1702
if extra_trees is not None:
1703
trees.extend(extra_trees)
1704
if specific_files is not None:
1705
specific_files = self.target.find_related_paths_across_trees(
1706
specific_files, trees,
1707
require_versioned=require_versioned)
1708
# TODO(jelmer): Restrict to specific_files, for performance reasons.
1709
with self.lock_read():
1710
from_tree_sha, extras = snapshot_workingtree(self.source, want_unversioned=want_unversioned)
1711
return tree_changes(
1712
self.store, from_tree_sha, self.target.tree,
1713
include_trees=include_trees,
1714
rename_detector=self.rename_detector,
1715
want_unchanged=want_unchanged, change_type_same=True), extras
1718
_mod_tree.InterTree.register_optimiser(InterFromIndexGitTree)
1721
class InterIndexGitTree(InterGitTrees):
1722
"""InterTree that works between a Git revision tree and an index."""
1724
def __init__(self, source, target):
1725
super(InterIndexGitTree, self).__init__(source, target)
1726
if self.source.store == self.target.store:
1727
self.store = self.source.store
1729
self.store = OverlayObjectStore(
1730
[self.source.store, self.target.store])
1731
self.rename_detector = RenameDetector(self.store)
1734
def is_compatible(cls, source, target):
1735
return (isinstance(target, MutableGitIndexTree) and
1736
isinstance(source, MutableGitIndexTree))
1738
def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1739
require_versioned=False, extra_trees=None,
1740
want_unversioned=False, include_trees=True):
1741
trees = [self.source]
1742
if extra_trees is not None:
1743
trees.extend(extra_trees)
1744
if specific_files is not None:
1745
specific_files = self.target.find_related_paths_across_trees(
1746
specific_files, trees,
1747
require_versioned=require_versioned)
1748
# TODO(jelmer): Restrict to specific_files, for performance reasons.
1749
with self.lock_read():
1750
from_tree_sha, from_extras = snapshot_workingtree(
1751
self.source, want_unversioned=want_unversioned)
1752
to_tree_sha, to_extras = snapshot_workingtree(
1753
self.target, want_unversioned=want_unversioned)
1754
changes = tree_changes(
1755
self.store, from_tree_sha, to_tree_sha,
1756
include_trees=include_trees,
1757
rename_detector=self.rename_detector,
1758
want_unchanged=want_unchanged, change_type_same=True)
1759
return changes, from_extras, to_extras
1619
want_unversioned=want_unversioned)
1762
1622
_mod_tree.InterTree.register_optimiser(InterIndexGitTree)
1765
def snapshot_workingtree(target, want_unversioned=False):
1625
def changes_between_git_tree_and_working_copy(store, from_tree_sha, target,
1626
want_unchanged=False,
1627
want_unversioned=False):
1628
"""Determine the changes between a git tree and a working tree with index.
1768
1633
# Report dirified directories to commit_tree first, so that they can be
1826
1675
target.abspath(e).encode(osutils._fs_enc), st)
1829
target.store.add_object(blob)
1678
store.add_object(blob)
1679
np = np.encode('utf-8')
1830
1680
blobs[np] = (blob.id, cleanup_mode(st.st_mode))
1833
target.store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()]), extras
1836
def changes_between_git_tree_and_working_copy(source_store, from_tree_sha, target,
1837
want_unchanged=False,
1838
want_unversioned=False,
1839
rename_detector=None,
1840
include_trees=True):
1841
"""Determine the changes between a git tree and a working tree with index.
1844
to_tree_sha, extras = snapshot_workingtree(target, want_unversioned=want_unversioned)
1845
store = OverlayObjectStore([source_store, target.store])
1846
return tree_changes(
1847
store, from_tree_sha, to_tree_sha, include_trees=include_trees,
1848
rename_detector=rename_detector,
1682
to_tree_sha = commit_tree(
1683
store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()])
1684
return store.tree_changes(
1685
from_tree_sha, to_tree_sha, include_trees=True,
1849
1686
want_unchanged=want_unchanged, change_type_same=True), extras