1012
1037
def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1013
1038
require_versioned=False, extra_trees=None,
1014
1039
want_unversioned=False, include_trees=True):
1015
raise NotImplementedError(self._iter_git_changes)
1040
trees = [self.source]
1041
if extra_trees is not None:
1042
trees.extend(extra_trees)
1043
if specific_files is not None:
1044
specific_files = self.target.find_related_paths_across_trees(
1045
specific_files, trees,
1046
require_versioned=require_versioned)
1047
# TODO(jelmer): Restrict to specific_files, for performance reasons.
1048
with self.lock_read():
1049
from_tree_sha, from_extras = self.source.git_snapshot(
1050
want_unversioned=want_unversioned)
1051
to_tree_sha, to_extras = self.target.git_snapshot(
1052
want_unversioned=want_unversioned)
1053
changes = tree_changes(
1054
self.store, from_tree_sha, to_tree_sha,
1055
include_trees=include_trees,
1056
rename_detector=self.rename_detector,
1057
want_unchanged=want_unchanged, change_type_same=True)
1058
return changes, from_extras, to_extras
1017
1060
def find_target_path(self, path, recurse='none'):
1018
1061
ret = self.find_target_paths([path], recurse=recurse)
1070
class InterGitRevisionTrees(InterGitTrees):
1071
"""InterTree that works between two git revision trees."""
1073
_matching_from_tree_format = None
1074
_matching_to_tree_format = None
1075
_test_mutable_trees_to_test_trees = None
1078
def is_compatible(cls, source, target):
1079
return (isinstance(source, GitRevisionTree) and
1080
isinstance(target, GitRevisionTree))
1082
def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1083
require_versioned=True, extra_trees=None,
1084
want_unversioned=False, include_trees=True):
1085
trees = [self.source]
1086
if extra_trees is not None:
1087
trees.extend(extra_trees)
1088
if specific_files is not None:
1089
specific_files = self.target.find_related_paths_across_trees(
1090
specific_files, trees,
1091
require_versioned=require_versioned)
1093
if (self.source._repository._git.object_store !=
1094
self.target._repository._git.object_store):
1095
store = OverlayObjectStore(
1096
[self.source._repository._git.object_store,
1097
self.target._repository._git.object_store])
1099
store = self.source._repository._git.object_store
1100
rename_detector = RenameDetector(store)
1101
changes = tree_changes(
1102
store, self.source.tree, self.target.tree,
1103
want_unchanged=want_unchanged, include_trees=include_trees,
1104
change_type_same=True, rename_detector=rename_detector)
1105
return changes, set(), set()
1108
_mod_tree.InterTree.register_optimiser(InterGitRevisionTrees)
1111
class MutableGitIndexTree(mutabletree.MutableTree):
1113
_mod_tree.InterTree.register_optimiser(InterGitTrees)
1116
class MutableGitIndexTree(mutabletree.MutableTree, GitTree):
1113
1118
def __init__(self):
1114
1119
self._lock_mode = None
1636
1638
from .transform import GitTransformPreview
1637
1639
return GitTransformPreview(self, pb=pb)
1640
class InterToIndexGitTree(InterGitTrees):
1641
"""InterTree that works between a Git revision tree and an index."""
1643
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)
1653
def is_compatible(cls, source, target):
1654
return (isinstance(source, GitRevisionTree) and
1655
isinstance(target, MutableGitIndexTree))
1657
def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1658
require_versioned=False, extra_trees=None,
1659
want_unversioned=False, include_trees=True):
1660
trees = [self.source]
1661
if extra_trees is not None:
1662
trees.extend(extra_trees)
1663
if specific_files is not None:
1664
specific_files = self.target.find_related_paths_across_trees(
1665
specific_files, trees,
1666
require_versioned=require_versioned)
1667
# TODO(jelmer): Restrict to specific_files, for performance reasons.
1668
with self.lock_read():
1669
changes, target_extras = changes_between_git_tree_and_working_copy(
1670
self.source.store, self.source.tree,
1671
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
1762
_mod_tree.InterTree.register_optimiser(InterIndexGitTree)
1641
def has_changes(self, _from_tree=None):
1642
"""Quickly check that the tree contains at least one commitable change.
1644
:param _from_tree: tree to compare against to find changes (default to
1645
the basis tree and is intended to be used by tests).
1647
:return: True if a change is found. False otherwise
1649
with self.lock_read():
1650
# Check pending merges
1651
if len(self.get_parent_ids()) > 1:
1653
if _from_tree is None:
1654
_from_tree = self.basis_tree()
1655
changes = self.iter_changes(_from_tree)
1656
if self.supports_symlinks():
1657
# Fast path for has_changes.
1659
change = next(changes)
1660
if change.path[1] == '':
1663
except StopIteration:
1667
# Slow path for has_changes.
1668
# Handle platforms that do not support symlinks in the
1669
# conditional below. This is slower than the try/except
1670
# approach below that but we don't have a choice as we
1671
# need to be sure that all symlinks are removed from the
1672
# entire changeset. This is because in platforms that
1673
# do not support symlinks, they show up as None in the
1674
# working copy as compared to the repository.
1675
# Also, exclude root as mention in the above fast path.
1677
lambda c: c[6][0] != 'symlink' and c[4] != (None, None),
1681
except StopIteration:
1765
1686
def snapshot_workingtree(target, want_unversioned=False):