687
def install_revision(repository, rev, revision_tree):
688
"""Install all revision data into a repository."""
691
for p_id in rev.parent_ids:
692
if repository.has_revision(p_id):
693
present_parents.append(p_id)
694
parent_trees[p_id] = repository.revision_tree(p_id)
696
parent_trees[p_id] = EmptyTree()
698
inv = revision_tree.inventory
700
# Add the texts that are not already present
701
for path, ie in inv.iter_entries():
702
w = repository.weave_store.get_weave_or_empty(ie.file_id,
703
repository.get_transaction())
704
if ie.revision not in w:
706
# FIXME: TODO: The following loop *may* be overlapping/duplicate
707
# with inventoryEntry.find_previous_heads(). if it is, then there
708
# is a latent bug here where the parents may have ancestors of each
710
for revision, tree in parent_trees.iteritems():
711
if ie.file_id not in tree:
713
parent_id = tree.inventory[ie.file_id].revision
714
if parent_id in text_parents:
716
text_parents.append(parent_id)
718
vfile = repository.weave_store.get_weave_or_empty(ie.file_id,
719
repository.get_transaction())
720
lines = revision_tree.get_file(ie.file_id).readlines()
721
vfile.add_lines(rev.revision_id, text_parents, lines)
723
# install the inventory
724
repository.add_inventory(rev.revision_id, inv, present_parents)
725
except errors.RevisionAlreadyPresent:
727
repository.add_revision(rev.revision_id, rev, inv)
653
730
class MetaDirRepository(Repository):
654
731
"""Repositories in the new meta-dir layout."""
1808
1888
self.pb.update(message, self.count, self.total)
1891
class CommitBuilder(object):
1892
"""Provides an interface to build up a commit.
1894
This allows describing a tree to be committed without needing to
1895
know the internals of the format of the repository.
1897
def __init__(self, repository, parents, config, timestamp=None,
1898
timezone=None, committer=None, revprops=None,
1900
"""Initiate a CommitBuilder.
1902
:param repository: Repository to commit to.
1903
:param parents: Revision ids of the parents of the new revision.
1904
:param config: Configuration to use.
1905
:param timestamp: Optional timestamp recorded for commit.
1906
:param timezone: Optional timezone for timestamp.
1907
:param committer: Optional committer to set for commit.
1908
:param revprops: Optional dictionary of revision properties.
1909
:param revision_id: Optional revision id.
1911
self._config = config
1913
if committer is None:
1914
self._committer = self._config.username()
1916
assert isinstance(committer, basestring), type(committer)
1917
self._committer = committer
1919
self.new_inventory = Inventory()
1920
self._new_revision_id = revision_id
1921
self.parents = parents
1922
self.repository = repository
1925
if revprops is not None:
1926
self._revprops.update(revprops)
1928
if timestamp is None:
1929
self._timestamp = time.time()
1931
self._timestamp = long(timestamp)
1933
if timezone is None:
1934
self._timezone = local_time_offset()
1936
self._timezone = int(timezone)
1938
self._generate_revision_if_needed()
1940
def commit(self, message):
1941
"""Make the actual commit.
1943
:return: The revision id of the recorded revision.
1945
rev = Revision(timestamp=self._timestamp,
1946
timezone=self._timezone,
1947
committer=self._committer,
1949
inventory_sha1=self.inv_sha1,
1950
revision_id=self._new_revision_id,
1951
properties=self._revprops)
1952
rev.parent_ids = self.parents
1953
self.repository.add_revision(self._new_revision_id, rev,
1954
self.new_inventory, self._config)
1955
return self._new_revision_id
1957
def finish_inventory(self):
1958
"""Tell the builder that the inventory is finished."""
1959
self.new_inventory.revision_id = self._new_revision_id
1960
self.inv_sha1 = self.repository.add_inventory(
1961
self._new_revision_id,
1966
def _gen_revision_id(self):
1967
"""Return new revision-id."""
1968
s = '%s-%s-' % (self._config.user_email(),
1969
compact_date(self._timestamp))
1970
s += hexlify(rand_bytes(8))
1973
def _generate_revision_if_needed(self):
1974
"""Create a revision id if None was supplied.
1976
If the repository can not support user-specified revision ids
1977
they should override this function and raise UnsupportedOperation
1978
if _new_revision_id is not None.
1980
:raises: UnsupportedOperation
1982
if self._new_revision_id is None:
1983
self._new_revision_id = self._gen_revision_id()
1985
def record_entry_contents(self, ie, parent_invs, path, tree):
1986
"""Record the content of ie from tree into the commit if needed.
1988
:param ie: An inventory entry present in the commit.
1989
:param parent_invs: The inventories of the parent revisions of the
1991
:param path: The path the entry is at in the tree.
1992
:param tree: The tree which contains this entry and should be used to
1995
self.new_inventory.add(ie)
1997
# ie.revision is always None if the InventoryEntry is considered
1998
# for committing. ie.snapshot will record the correct revision
1999
# which may be the sole parent if it is untouched.
2000
if ie.revision is not None:
2002
previous_entries = ie.find_previous_heads(
2004
self.repository.weave_store,
2005
self.repository.get_transaction())
2006
# we are creating a new revision for ie in the history store
2008
ie.snapshot(self._new_revision_id, path, previous_entries, tree, self)
2010
def modified_directory(self, file_id, file_parents):
2011
"""Record the presence of a symbolic link.
2013
:param file_id: The file_id of the link to record.
2014
:param file_parents: The per-file parent revision ids.
2016
self._add_text_to_weave(file_id, [], file_parents.keys())
2018
def modified_file_text(self, file_id, file_parents,
2019
get_content_byte_lines, text_sha1=None,
2021
"""Record the text of file file_id
2023
:param file_id: The file_id of the file to record the text of.
2024
:param file_parents: The per-file parent revision ids.
2025
:param get_content_byte_lines: A callable which will return the byte
2027
:param text_sha1: Optional SHA1 of the file contents.
2028
:param text_size: Optional size of the file contents.
2030
mutter('storing text of file {%s} in revision {%s} into %r',
2031
file_id, self._new_revision_id, self.repository.weave_store)
2032
# special case to avoid diffing on renames or
2034
if (len(file_parents) == 1
2035
and text_sha1 == file_parents.values()[0].text_sha1
2036
and text_size == file_parents.values()[0].text_size):
2037
previous_ie = file_parents.values()[0]
2038
versionedfile = self.repository.weave_store.get_weave(file_id,
2039
self.repository.get_transaction())
2040
versionedfile.clone_text(self._new_revision_id,
2041
previous_ie.revision, file_parents.keys())
2042
return text_sha1, text_size
2044
new_lines = get_content_byte_lines()
2045
# TODO: Rather than invoking sha_strings here, _add_text_to_weave
2046
# should return the SHA1 and size
2047
self._add_text_to_weave(file_id, new_lines, file_parents.keys())
2048
return bzrlib.osutils.sha_strings(new_lines), \
2049
sum(map(len, new_lines))
2051
def modified_link(self, file_id, file_parents, link_target):
2052
"""Record the presence of a symbolic link.
2054
:param file_id: The file_id of the link to record.
2055
:param file_parents: The per-file parent revision ids.
2056
:param link_target: Target location of this link.
2058
self._add_text_to_weave(file_id, [], file_parents.keys())
2060
def _add_text_to_weave(self, file_id, new_lines, parents):
2061
versionedfile = self.repository.weave_store.get_weave_or_empty(
2062
file_id, self.repository.get_transaction())
2063
versionedfile.add_lines(self._new_revision_id, parents, new_lines)
2064
versionedfile.clear_cache()
1811
2067
# Copied from xml.sax.saxutils
1812
2068
def _unescape_xml(data):
1813
2069
"""Unescape &, <, and > in a string of data.