/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 bzrlib/merge.py

  • Committer: John Arbash Meinel
  • Date: 2010-08-05 16:27:35 UTC
  • mto: This revision was merged to the branch mainline in revision 5374.
  • Revision ID: john@arbash-meinel.com-20100805162735-172opvx34sr5gpbl
Find a case where we are wasting a bit of memory.

Specifically the 'build_details' tuple contains a lot of wasted references,
and we hold on to one of these for each record we are fetching.
And for something like 'bzr pack', that is all keys.

For just loading all text build details on my bzr+ repository, With:
locations = b.repository.texts._index.get_build_details(b.repository.texts.keys())
This drops the memory consumption from:
WorkingSize   77604KiB
 to
WorkingSize   64640KiB

Or around 10.6MB. I worked it out to a savings of about 80 bytes/record
on data that can have hundreds of thousands of records (in 32-bit).

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
import warnings
18
18
 
 
19
from bzrlib.lazy_import import lazy_import
 
20
lazy_import(globals(), """
19
21
from bzrlib import (
20
22
    branch as _mod_branch,
21
23
    conflicts as _mod_conflicts,
22
24
    debug,
23
 
    decorators,
24
 
    errors,
 
25
    generate_ids,
25
26
    graph as _mod_graph,
26
 
    hooks,
27
27
    merge3,
28
28
    osutils,
29
29
    patiencediff,
34
34
    tree as _mod_tree,
35
35
    tsort,
36
36
    ui,
37
 
    versionedfile
 
37
    versionedfile,
 
38
    workingtree,
38
39
    )
39
40
from bzrlib.cleanup import OperationWithCleanups
 
41
""")
 
42
from bzrlib import (
 
43
    decorators,
 
44
    errors,
 
45
    hooks,
 
46
    )
40
47
from bzrlib.symbol_versioning import (
41
48
    deprecated_in,
42
49
    deprecated_method,
421
428
        return self._cached_trees[revision_id]
422
429
 
423
430
    def _get_tree(self, treespec, possible_transports=None):
424
 
        from bzrlib import workingtree
425
431
        location, revno = treespec
426
432
        if revno is None:
427
433
            tree = workingtree.WorkingTree.open_containing(location)[0]
856
862
        finally:
857
863
            child_pb.finished()
858
864
        self.fix_root()
 
865
        self._finish_computing_transform()
 
866
 
 
867
    def _finish_computing_transform(self):
 
868
        """Finalize the transform and report the changes.
 
869
 
 
870
        This is the second half of _compute_transform.
 
871
        """
859
872
        child_pb = ui.ui_factory.nested_progress_bar()
860
873
        try:
861
874
            fs_conflicts = transform.resolve_conflicts(self.tt, child_pb,
1071
1084
                          ))
1072
1085
        return result
1073
1086
 
1074
 
 
1075
1087
    def fix_root(self):
1076
1088
        try:
1077
1089
            self.tt.final_kind(self.tt.root)
1455
1467
    def get_lines(self, tree, file_id):
1456
1468
        """Return the lines in a file, or an empty list."""
1457
1469
        if tree.has_id(file_id):
1458
 
            return tree.get_file(file_id).readlines()
 
1470
            return tree.get_file_lines(file_id)
1459
1471
        else:
1460
1472
            return []
1461
1473
 
1750
1762
            osutils.rmtree(temp_dir)
1751
1763
 
1752
1764
 
 
1765
class PathNotInTree(errors.BzrError):
 
1766
 
 
1767
    _fmt = """Merge-into failed because %(tree)s does not contain %(path)s."""
 
1768
 
 
1769
    def __init__(self, path, tree):
 
1770
        errors.BzrError.__init__(self, path=path, tree=tree)
 
1771
 
 
1772
 
 
1773
class MergeIntoMerger(Merger):
 
1774
    """Merger that understands other_tree will be merged into a subdir.
 
1775
 
 
1776
    This also changes the Merger api so that it uses real Branch, revision_id,
 
1777
    and RevisonTree objects, rather than using revision specs.
 
1778
    """
 
1779
 
 
1780
    def __init__(self, this_tree, other_branch, other_tree, target_subdir,
 
1781
            source_subpath, other_rev_id=None):
 
1782
        """Create a new MergeIntoMerger object.
 
1783
 
 
1784
        source_subpath in other_tree will be effectively copied to
 
1785
        target_subdir in this_tree.
 
1786
 
 
1787
        :param this_tree: The tree that we will be merging into.
 
1788
        :param other_branch: The Branch we will be merging from.
 
1789
        :param other_tree: The RevisionTree object we want to merge.
 
1790
        :param target_subdir: The relative path where we want to merge
 
1791
            other_tree into this_tree
 
1792
        :param source_subpath: The relative path specifying the subtree of
 
1793
            other_tree to merge into this_tree.
 
1794
        """
 
1795
        # It is assumed that we are merging a tree that is not in our current
 
1796
        # ancestry, which means we are using the "EmptyTree" as our basis.
 
1797
        null_ancestor_tree = this_tree.branch.repository.revision_tree(
 
1798
                                _mod_revision.NULL_REVISION)
 
1799
        super(MergeIntoMerger, self).__init__(
 
1800
            this_branch=this_tree.branch,
 
1801
            this_tree=this_tree,
 
1802
            other_tree=other_tree,
 
1803
            base_tree=null_ancestor_tree,
 
1804
            )
 
1805
        self._target_subdir = target_subdir
 
1806
        self._source_subpath = source_subpath
 
1807
        self.other_branch = other_branch
 
1808
        if other_rev_id is None:
 
1809
            other_rev_id = other_tree.get_revision_id()
 
1810
        self.other_rev_id = self.other_basis = other_rev_id
 
1811
        self.base_is_ancestor = True
 
1812
        self.backup_files = True
 
1813
        self.merge_type = Merge3Merger
 
1814
        self.show_base = False
 
1815
        self.reprocess = False
 
1816
        self.interesting_ids = None
 
1817
        self.merge_type = _MergeTypeParameterizer(MergeIntoMergeType,
 
1818
              target_subdir=self._target_subdir,
 
1819
              source_subpath=self._source_subpath)
 
1820
        if self._source_subpath != '':
 
1821
            # If this isn't a partial merge make sure the revisions will be
 
1822
            # present.
 
1823
            self._maybe_fetch(self.other_branch, self.this_branch,
 
1824
                self.other_basis)
 
1825
 
 
1826
    def set_pending(self):
 
1827
        if self._source_subpath != '':
 
1828
            return
 
1829
        Merger.set_pending(self)
 
1830
 
 
1831
 
 
1832
class _MergeTypeParameterizer(object):
 
1833
    """Wrap a merge-type class to provide extra parameters.
 
1834
    
 
1835
    This is hack used by MergeIntoMerger to pass some extra parameters to its
 
1836
    merge_type.  Merger.do_merge() sets up its own set of parameters to pass to
 
1837
    the 'merge_type' member.  It is difficult override do_merge without
 
1838
    re-writing the whole thing, so instead we create a wrapper which will pass
 
1839
    the extra parameters.
 
1840
    """
 
1841
 
 
1842
    def __init__(self, merge_type, **kwargs):
 
1843
        self._extra_kwargs = kwargs
 
1844
        self._merge_type = merge_type
 
1845
 
 
1846
    def __call__(self, *args, **kwargs):
 
1847
        kwargs.update(self._extra_kwargs)
 
1848
        return self._merge_type(*args, **kwargs)
 
1849
 
 
1850
    def __getattr__(self, name):
 
1851
        return getattr(self._merge_type, name)
 
1852
 
 
1853
 
 
1854
class MergeIntoMergeType(Merge3Merger):
 
1855
    """Merger that incorporates a tree (or part of a tree) into another."""
 
1856
 
 
1857
    def __init__(self, *args, **kwargs):
 
1858
        """Initialize the merger object.
 
1859
 
 
1860
        :param args: See Merge3Merger.__init__'s args.
 
1861
        :param kwargs: See Merge3Merger.__init__'s keyword args, except for
 
1862
            source_subpath and target_subdir.
 
1863
        :keyword source_subpath: The relative path specifying the subtree of
 
1864
            other_tree to merge into this_tree.
 
1865
        :keyword target_subdir: The relative path where we want to merge
 
1866
            other_tree into this_tree
 
1867
        """
 
1868
        # All of the interesting work happens during Merge3Merger.__init__(),
 
1869
        # so we have have to hack in to get our extra parameters set.
 
1870
        self._source_subpath = kwargs.pop('source_subpath')
 
1871
        self._target_subdir = kwargs.pop('target_subdir')
 
1872
        super(MergeIntoMergeType, self).__init__(*args, **kwargs)
 
1873
 
 
1874
    def _compute_transform(self):
 
1875
        child_pb = ui.ui_factory.nested_progress_bar()
 
1876
        try:
 
1877
            entries = self._entries_to_incorporate()
 
1878
            entries = list(entries)
 
1879
            for num, (entry, parent_id) in enumerate(entries):
 
1880
                child_pb.update('Preparing file merge', num, len(entries))
 
1881
                parent_trans_id = self.tt.trans_id_file_id(parent_id)
 
1882
                trans_id = transform.new_by_entry(self.tt, entry,
 
1883
                    parent_trans_id, self.other_tree)
 
1884
        finally:
 
1885
            child_pb.finished()
 
1886
        self._finish_computing_transform()
 
1887
 
 
1888
    def _entries_to_incorporate(self):
 
1889
        """Yields pairs of (inventory_entry, new_parent)."""
 
1890
        other_inv = self.other_tree.inventory
 
1891
        subdir_id = other_inv.path2id(self._source_subpath)
 
1892
        if subdir_id is None:
 
1893
            # XXX: The error would be clearer if it gave the URL of the source
 
1894
            # branch, but we don't have a reference to that here.
 
1895
            raise PathNotInTree(self._source_subpath, "Source tree")
 
1896
        subdir = other_inv[subdir_id]
 
1897
        parent_in_target = osutils.dirname(self._target_subdir)
 
1898
        target_id = self.this_tree.inventory.path2id(parent_in_target)
 
1899
        if target_id is None:
 
1900
            raise PathNotInTree(self._target_subdir, "Target tree")
 
1901
        name_in_target = osutils.basename(self._target_subdir)
 
1902
        merge_into_root = subdir.copy()
 
1903
        merge_into_root.name = name_in_target
 
1904
        if merge_into_root.file_id in self.this_tree.inventory:
 
1905
            # Give the root a new file-id.
 
1906
            # This can happen fairly easily if the directory we are
 
1907
            # incorporating is the root, and both trees have 'TREE_ROOT' as
 
1908
            # their root_id.  Users will expect this to Just Work, so we
 
1909
            # change the file-id here.
 
1910
            # Non-root file-ids could potentially conflict too.  That's really
 
1911
            # an edge case, so we don't do anything special for those.  We let
 
1912
            # them cause conflicts.
 
1913
            merge_into_root.file_id = generate_ids.gen_file_id(name_in_target)
 
1914
        yield (merge_into_root, target_id)
 
1915
        if subdir.kind != 'directory':
 
1916
            # No children, so we are done.
 
1917
            return
 
1918
        for ignored_path, entry in other_inv.iter_entries_by_dir(subdir_id):
 
1919
            parent_id = entry.parent_id
 
1920
            if parent_id == subdir.file_id:
 
1921
                # The root's parent ID has changed, so make sure children of
 
1922
                # the root refer to the new ID.
 
1923
                parent_id = merge_into_root.file_id
 
1924
            yield (entry, parent_id)
 
1925
 
 
1926
 
1753
1927
def merge_inner(this_branch, other_tree, base_tree, ignore_zero=False,
1754
1928
                backup_files=False,
1755
1929
                merge_type=Merge3Merger,
1763
1937
                change_reporter=None):
1764
1938
    """Primary interface for merging.
1765
1939
 
1766
 
        typical use is probably
1767
 
        'merge_inner(branch, branch.get_revision_tree(other_revision),
1768
 
                     branch.get_revision_tree(base_revision))'
1769
 
        """
 
1940
    Typical use is probably::
 
1941
 
 
1942
        merge_inner(branch, branch.get_revision_tree(other_revision),
 
1943
                    branch.get_revision_tree(base_revision))
 
1944
    """
1770
1945
    if this_tree is None:
1771
1946
        raise errors.BzrError("bzrlib.merge.merge_inner requires a this_tree "
1772
1947
                              "parameter as of bzrlib version 0.8.")