1014
1037
def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1015
1038
require_versioned=False, extra_trees=None,
1016
1039
want_unversioned=False, include_trees=True):
1017
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
1019
1060
def find_target_path(self, path, recurse='none'):
1020
1061
ret = self.find_target_paths([path], recurse=recurse)
1072
class InterGitRevisionTrees(InterGitTrees):
1073
"""InterTree that works between two git revision trees."""
1075
_matching_from_tree_format = None
1076
_matching_to_tree_format = None
1077
_test_mutable_trees_to_test_trees = None
1080
def is_compatible(cls, source, target):
1081
return (isinstance(source, GitRevisionTree) and
1082
isinstance(target, GitRevisionTree))
1084
def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1085
require_versioned=True, extra_trees=None,
1086
want_unversioned=False, include_trees=True):
1087
trees = [self.source]
1088
if extra_trees is not None:
1089
trees.extend(extra_trees)
1090
if specific_files is not None:
1091
specific_files = self.target.find_related_paths_across_trees(
1092
specific_files, trees,
1093
require_versioned=require_versioned)
1095
if (self.source._repository._git.object_store !=
1096
self.target._repository._git.object_store):
1097
store = OverlayObjectStore(
1098
[self.source._repository._git.object_store,
1099
self.target._repository._git.object_store])
1101
store = self.source._repository._git.object_store
1102
rename_detector = RenameDetector(store)
1103
changes = tree_changes(
1104
store, self.source.tree, self.target.tree,
1105
want_unchanged=want_unchanged, include_trees=include_trees,
1106
change_type_same=True, rename_detector=rename_detector)
1107
return changes, set(), set()
1110
_mod_tree.InterTree.register_optimiser(InterGitRevisionTrees)
1113
class MutableGitIndexTree(mutabletree.MutableTree):
1113
_mod_tree.InterTree.register_optimiser(InterGitTrees)
1116
class MutableGitIndexTree(mutabletree.MutableTree, GitTree):
1115
1118
def __init__(self):
1116
1119
self._lock_mode = None
1631
1631
raise NotImplementedError(self._live_entry)
1633
1633
def transform(self, pb=None):
1634
from ..transform import TreeTransform
1635
return TreeTransform(self, pb=pb)
1639
class InterToIndexGitTree(InterGitTrees):
1640
"""InterTree that works between a Git revision tree and an index."""
1642
def __init__(self, source, target):
1643
super(InterToIndexGitTree, self).__init__(source, target)
1644
if self.source.store == self.target.store:
1645
self.store = self.source.store
1647
self.store = OverlayObjectStore(
1648
[self.source.store, self.target.store])
1649
self.rename_detector = RenameDetector(self.store)
1652
def is_compatible(cls, source, target):
1653
return (isinstance(source, GitRevisionTree) and
1654
isinstance(target, MutableGitIndexTree))
1656
def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1657
require_versioned=False, extra_trees=None,
1658
want_unversioned=False, include_trees=True):
1659
trees = [self.source]
1660
if extra_trees is not None:
1661
trees.extend(extra_trees)
1662
if specific_files is not None:
1663
specific_files = self.target.find_related_paths_across_trees(
1664
specific_files, trees,
1665
require_versioned=require_versioned)
1666
# TODO(jelmer): Restrict to specific_files, for performance reasons.
1667
with self.lock_read():
1668
changes, target_extras = changes_between_git_tree_and_working_copy(
1669
self.source.store, self.source.tree,
1670
self.target, want_unchanged=want_unchanged,
1671
want_unversioned=want_unversioned,
1672
rename_detector=self.rename_detector,
1673
include_trees=include_trees)
1674
return changes, set(), target_extras
1677
_mod_tree.InterTree.register_optimiser(InterToIndexGitTree)
1680
class InterFromIndexGitTree(InterGitTrees):
1681
"""InterTree that works between a Git revision tree and an index."""
1683
def __init__(self, source, target):
1684
super(InterFromIndexGitTree, self).__init__(source, target)
1685
if self.source.store == self.target.store:
1686
self.store = self.source.store
1688
self.store = OverlayObjectStore(
1689
[self.source.store, self.target.store])
1690
self.rename_detector = RenameDetector(self.store)
1693
def is_compatible(cls, source, target):
1694
return (isinstance(target, GitRevisionTree) and
1695
isinstance(source, MutableGitIndexTree))
1697
def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1698
require_versioned=False, extra_trees=None,
1699
want_unversioned=False, include_trees=True):
1700
trees = [self.source]
1701
if extra_trees is not None:
1702
trees.extend(extra_trees)
1703
if specific_files is not None:
1704
specific_files = self.target.find_related_paths_across_trees(
1705
specific_files, trees,
1706
require_versioned=require_versioned)
1707
# TODO(jelmer): Restrict to specific_files, for performance reasons.
1708
with self.lock_read():
1709
from_tree_sha, extras = snapshot_workingtree(self.source, want_unversioned=want_unversioned)
1710
return tree_changes(
1711
self.store, from_tree_sha, self.target.tree,
1712
include_trees=include_trees,
1713
rename_detector=self.rename_detector,
1714
want_unchanged=want_unchanged, change_type_same=True), extras
1717
_mod_tree.InterTree.register_optimiser(InterFromIndexGitTree)
1720
class InterIndexGitTree(InterGitTrees):
1721
"""InterTree that works between a Git revision tree and an index."""
1723
def __init__(self, source, target):
1724
super(InterIndexGitTree, self).__init__(source, target)
1725
if self.source.store == self.target.store:
1726
self.store = self.source.store
1728
self.store = OverlayObjectStore(
1729
[self.source.store, self.target.store])
1730
self.rename_detector = RenameDetector(self.store)
1733
def is_compatible(cls, source, target):
1734
return (isinstance(target, MutableGitIndexTree) and
1735
isinstance(source, MutableGitIndexTree))
1737
def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1738
require_versioned=False, extra_trees=None,
1739
want_unversioned=False, include_trees=True):
1740
trees = [self.source]
1741
if extra_trees is not None:
1742
trees.extend(extra_trees)
1743
if specific_files is not None:
1744
specific_files = self.target.find_related_paths_across_trees(
1745
specific_files, trees,
1746
require_versioned=require_versioned)
1747
# TODO(jelmer): Restrict to specific_files, for performance reasons.
1748
with self.lock_read():
1749
from_tree_sha, from_extras = snapshot_workingtree(
1750
self.source, want_unversioned=want_unversioned)
1751
to_tree_sha, to_extras = snapshot_workingtree(
1752
self.target, want_unversioned=want_unversioned)
1753
changes = tree_changes(
1754
self.store, from_tree_sha, to_tree_sha,
1755
include_trees=include_trees,
1756
rename_detector=self.rename_detector,
1757
want_unchanged=want_unchanged, change_type_same=True)
1758
return changes, from_extras, to_extras
1761
_mod_tree.InterTree.register_optimiser(InterIndexGitTree)
1634
from .transform import GitTreeTransform
1635
return GitTreeTransform(self, pb=pb)
1637
def preview_transform(self, pb=None):
1638
from .transform import GitTransformPreview
1639
return GitTransformPreview(self, pb=pb)
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:
1764
1686
def snapshot_workingtree(target, want_unversioned=False):