~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-06-08 15:08:04 UTC
  • mfrom: (1740.3.10 commit_builder)
  • Revision ID: pqm@pqm.ubuntu.com-20060608150804-a186fa90bfede9a6
(jrv,rbc,jam) Refactor commit.Commit to use a Repository specific CommitBuilder, enables foreign branch commits

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
 
17
from binascii import hexlify
17
18
from copy import deepcopy
18
19
from cStringIO import StringIO
 
20
import re
 
21
import time
19
22
from unittest import TestSuite
20
23
 
21
24
import bzrlib.bzrdir as bzrdir
25
28
import bzrlib.gpg as gpg
26
29
from bzrlib.graph import Graph
27
30
from bzrlib.inter import InterObject
 
31
from bzrlib.inventory import Inventory
28
32
from bzrlib.knit import KnitVersionedFile, KnitPlainFactory
29
33
from bzrlib.lockable_files import LockableFiles, TransportLock
30
34
from bzrlib.lockdir import LockDir
31
 
from bzrlib.osutils import safe_unicode
32
 
from bzrlib.revision import NULL_REVISION
 
35
from bzrlib.osutils import (safe_unicode, rand_bytes, compact_date, 
 
36
                            local_time_offset)
 
37
from bzrlib.revision import NULL_REVISION, Revision
33
38
from bzrlib.store.versioned import VersionedFileStore, WeaveStore
34
39
from bzrlib.store.text import TextStore
35
40
from bzrlib.symbol_versioning import *
71
76
        inv_sha1 = bzrlib.osutils.sha_string(inv_text)
72
77
        inv_vf = self.control_weaves.get_weave('inventory',
73
78
                                               self.get_transaction())
74
 
        inv_vf.add_lines(revid, parents, bzrlib.osutils.split_lines(inv_text))
 
79
        self._inventory_add_lines(inv_vf, revid, parents, bzrlib.osutils.split_lines(inv_text))
75
80
        return inv_sha1
76
81
 
 
82
    def _inventory_add_lines(self, inv_vf, revid, parents, lines):
 
83
        final_parents = []
 
84
        for parent in parents:
 
85
            if parent in inv_vf:
 
86
                final_parents.append(parent)
 
87
 
 
88
        inv_vf.add_lines(revid, final_parents, lines)
 
89
 
77
90
    @needs_write_lock
78
91
    def add_revision(self, rev_id, rev, inv=None, config=None):
79
92
        """Add rev to the revision store as rev_id.
222
235
        return InterRepository.get(source, self).fetch(revision_id=revision_id,
223
236
                                                       pb=pb)
224
237
 
 
238
    def get_commit_builder(self, branch, parents, config, timestamp=None, 
 
239
                           timezone=None, committer=None, revprops=None, 
 
240
                           revision_id=None):
 
241
        """Obtain a CommitBuilder for this repository.
 
242
        
 
243
        :param branch: Branch to commit to.
 
244
        :param parents: Revision ids of the parents of the new revision.
 
245
        :param config: Configuration to use.
 
246
        :param timestamp: Optional timestamp recorded for commit.
 
247
        :param timezone: Optional timezone for timestamp.
 
248
        :param committer: Optional committer to set for commit.
 
249
        :param revprops: Optional dictionary of revision properties.
 
250
        :param revision_id: Optional revision id.
 
251
        """
 
252
        return CommitBuilder(self, parents, config, timestamp, timezone,
 
253
                             committer, revprops, revision_id)
 
254
 
225
255
    def unlock(self):
226
256
        self.control_files.unlock()
227
257
 
708
738
class KnitRepository(MetaDirRepository):
709
739
    """Knit format repository."""
710
740
 
 
741
    def _inventory_add_lines(self, inv_vf, revid, parents, lines):
 
742
        inv_vf.add_lines_with_ghosts(revid, parents, lines)
 
743
 
711
744
    @needs_read_lock
712
745
    def all_revision_ids(self):
713
746
        """See Repository.all_revision_ids()."""
1844
1877
        self.pb.update(message, self.count, self.total)
1845
1878
 
1846
1879
 
 
1880
class CommitBuilder(object):
 
1881
    """Provides an interface to build up a commit.
 
1882
 
 
1883
    This allows describing a tree to be committed without needing to 
 
1884
    know the internals of the format of the repository.
 
1885
    """
 
1886
    def __init__(self, repository, parents, config, timestamp=None, 
 
1887
                 timezone=None, committer=None, revprops=None, 
 
1888
                 revision_id=None):
 
1889
        """Initiate a CommitBuilder.
 
1890
 
 
1891
        :param repository: Repository to commit to.
 
1892
        :param parents: Revision ids of the parents of the new revision.
 
1893
        :param config: Configuration to use.
 
1894
        :param timestamp: Optional timestamp recorded for commit.
 
1895
        :param timezone: Optional timezone for timestamp.
 
1896
        :param committer: Optional committer to set for commit.
 
1897
        :param revprops: Optional dictionary of revision properties.
 
1898
        :param revision_id: Optional revision id.
 
1899
        """
 
1900
        self._config = config
 
1901
 
 
1902
        if committer is None:
 
1903
            self._committer = self._config.username()
 
1904
        else:
 
1905
            assert isinstance(committer, basestring), type(committer)
 
1906
            self._committer = committer
 
1907
 
 
1908
        self.new_inventory = Inventory()
 
1909
        self._new_revision_id = revision_id
 
1910
        self.parents = parents
 
1911
        self.repository = repository
 
1912
 
 
1913
        self._revprops = {}
 
1914
        if revprops is not None:
 
1915
            self._revprops.update(revprops)
 
1916
 
 
1917
        if timestamp is None:
 
1918
            self._timestamp = time.time()
 
1919
        else:
 
1920
            self._timestamp = long(timestamp)
 
1921
 
 
1922
        if timezone is None:
 
1923
            self._timezone = local_time_offset()
 
1924
        else:
 
1925
            self._timezone = int(timezone)
 
1926
 
 
1927
        self._generate_revision_if_needed()
 
1928
 
 
1929
    def commit(self, message):
 
1930
        """Make the actual commit.
 
1931
 
 
1932
        :return: The revision id of the recorded revision.
 
1933
        """
 
1934
        rev = Revision(timestamp=self._timestamp,
 
1935
                       timezone=self._timezone,
 
1936
                       committer=self._committer,
 
1937
                       message=message,
 
1938
                       inventory_sha1=self.inv_sha1,
 
1939
                       revision_id=self._new_revision_id,
 
1940
                       properties=self._revprops)
 
1941
        rev.parent_ids = self.parents
 
1942
        self.repository.add_revision(self._new_revision_id, rev, 
 
1943
            self.new_inventory, self._config)
 
1944
        return self._new_revision_id
 
1945
 
 
1946
    def finish_inventory(self):
 
1947
        """Tell the builder that the inventory is finished."""
 
1948
        self.new_inventory.revision = self._new_revision_id
 
1949
        self.inv_sha1 = self.repository.add_inventory(
 
1950
            self._new_revision_id,
 
1951
            self.new_inventory,
 
1952
            self.parents
 
1953
            )
 
1954
 
 
1955
    def _gen_revision_id(self):
 
1956
        """Return new revision-id."""
 
1957
        s = '%s-%s-' % (self._config.user_email(), 
 
1958
                        compact_date(self._timestamp))
 
1959
        s += hexlify(rand_bytes(8))
 
1960
        return s
 
1961
 
 
1962
    def _generate_revision_if_needed(self):
 
1963
        """Create a revision id if None was supplied.
 
1964
        
 
1965
        If the repository can not support user-specified revision ids
 
1966
        they should override this function and raise UnsupportedOperation
 
1967
        if _new_revision_id is not None.
 
1968
 
 
1969
        :raises: UnsupportedOperation
 
1970
        """
 
1971
        if self._new_revision_id is None:
 
1972
            self._new_revision_id = self._gen_revision_id()
 
1973
 
 
1974
    def record_entry_contents(self, ie, parent_invs, path, tree):
 
1975
        """Record the content of ie from tree into the commit if needed.
 
1976
 
 
1977
        :param ie: An inventory entry present in the commit.
 
1978
        :param parent_invs: The inventories of the parent revisions of the
 
1979
            commit.
 
1980
        :param path: The path the entry is at in the tree.
 
1981
        :param tree: The tree which contains this entry and should be used to 
 
1982
        obtain content.
 
1983
        """
 
1984
        self.new_inventory.add(ie)
 
1985
 
 
1986
        # ie.revision is always None if the InventoryEntry is considered
 
1987
        # for committing. ie.snapshot will record the correct revision 
 
1988
        # which may be the sole parent if it is untouched.
 
1989
        if ie.revision is not None:
 
1990
            return
 
1991
        previous_entries = ie.find_previous_heads(
 
1992
            parent_invs,
 
1993
            self.repository.weave_store,
 
1994
            self.repository.get_transaction())
 
1995
        # we are creating a new revision for ie in the history store
 
1996
        # and inventory.
 
1997
        ie.snapshot(self._new_revision_id, path, previous_entries, tree, self)
 
1998
 
 
1999
    def modified_directory(self, file_id, file_parents):
 
2000
        """Record the presence of a symbolic link.
 
2001
 
 
2002
        :param file_id: The file_id of the link to record.
 
2003
        :param file_parents: The per-file parent revision ids.
 
2004
        """
 
2005
        self._add_text_to_weave(file_id, [], file_parents.keys())
 
2006
    
 
2007
    def modified_file_text(self, file_id, file_parents,
 
2008
                           get_content_byte_lines, text_sha1=None,
 
2009
                           text_size=None):
 
2010
        """Record the text of file file_id
 
2011
 
 
2012
        :param file_id: The file_id of the file to record the text of.
 
2013
        :param file_parents: The per-file parent revision ids.
 
2014
        :param get_content_byte_lines: A callable which will return the byte
 
2015
            lines for the file.
 
2016
        :param text_sha1: Optional SHA1 of the file contents.
 
2017
        :param text_size: Optional size of the file contents.
 
2018
        """
 
2019
        mutter('storing text of file {%s} in revision {%s} into %r',
 
2020
               file_id, self._new_revision_id, self.repository.weave_store)
 
2021
        # special case to avoid diffing on renames or 
 
2022
        # reparenting
 
2023
        if (len(file_parents) == 1
 
2024
            and text_sha1 == file_parents.values()[0].text_sha1
 
2025
            and text_size == file_parents.values()[0].text_size):
 
2026
            previous_ie = file_parents.values()[0]
 
2027
            versionedfile = self.repository.weave_store.get_weave(file_id, 
 
2028
                self.repository.get_transaction())
 
2029
            versionedfile.clone_text(self._new_revision_id, 
 
2030
                previous_ie.revision, file_parents.keys())
 
2031
            return text_sha1, text_size
 
2032
        else:
 
2033
            new_lines = get_content_byte_lines()
 
2034
            # TODO: Rather than invoking sha_strings here, _add_text_to_weave
 
2035
            # should return the SHA1 and size
 
2036
            self._add_text_to_weave(file_id, new_lines, file_parents.keys())
 
2037
            return bzrlib.osutils.sha_strings(new_lines), \
 
2038
                sum(map(len, new_lines))
 
2039
 
 
2040
    def modified_link(self, file_id, file_parents, link_target):
 
2041
        """Record the presence of a symbolic link.
 
2042
 
 
2043
        :param file_id: The file_id of the link to record.
 
2044
        :param file_parents: The per-file parent revision ids.
 
2045
        :param link_target: Target location of this link.
 
2046
        """
 
2047
        self._add_text_to_weave(file_id, [], file_parents.keys())
 
2048
 
 
2049
    def _add_text_to_weave(self, file_id, new_lines, parents):
 
2050
        versionedfile = self.repository.weave_store.get_weave_or_empty(
 
2051
            file_id, self.repository.get_transaction())
 
2052
        versionedfile.add_lines(self._new_revision_id, parents, new_lines)
 
2053
        versionedfile.clear_cache()
 
2054
 
 
2055
 
1847
2056
# Copied from xml.sax.saxutils
1848
2057
def _unescape_xml(data):
1849
2058
    """Unescape &, <, and > in a string of data.