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)
619
730
class MetaDirRepository(Repository):
620
731
"""Repositories in the new meta-dir layout."""
1797
1913
self.pb.update(message, self.count, self.total)
1916
class CommitBuilder(object):
1917
"""Provides an interface to build up a commit.
1919
This allows describing a tree to be committed without needing to
1920
know the internals of the format of the repository.
1922
def __init__(self, repository, parents, config, timestamp=None,
1923
timezone=None, committer=None, revprops=None,
1925
"""Initiate a CommitBuilder.
1927
:param repository: Repository to commit to.
1928
:param parents: Revision ids of the parents of the new revision.
1929
:param config: Configuration to use.
1930
:param timestamp: Optional timestamp recorded for commit.
1931
:param timezone: Optional timezone for timestamp.
1932
:param committer: Optional committer to set for commit.
1933
:param revprops: Optional dictionary of revision properties.
1934
:param revision_id: Optional revision id.
1936
self._config = config
1938
if committer is None:
1939
self._committer = self._config.username()
1941
assert isinstance(committer, basestring), type(committer)
1942
self._committer = committer
1944
self.new_inventory = Inventory()
1945
self._new_revision_id = revision_id
1946
self.parents = parents
1947
self.repository = repository
1950
if revprops is not None:
1951
self._revprops.update(revprops)
1953
if timestamp is None:
1954
self._timestamp = time.time()
1956
self._timestamp = long(timestamp)
1958
if timezone is None:
1959
self._timezone = local_time_offset()
1961
self._timezone = int(timezone)
1963
self._generate_revision_if_needed()
1965
def commit(self, message):
1966
"""Make the actual commit.
1968
:return: The revision id of the recorded revision.
1970
rev = Revision(timestamp=self._timestamp,
1971
timezone=self._timezone,
1972
committer=self._committer,
1974
inventory_sha1=self.inv_sha1,
1975
revision_id=self._new_revision_id,
1976
properties=self._revprops)
1977
rev.parent_ids = self.parents
1978
self.repository.add_revision(self._new_revision_id, rev,
1979
self.new_inventory, self._config)
1980
return self._new_revision_id
1982
def finish_inventory(self):
1983
"""Tell the builder that the inventory is finished."""
1984
self.new_inventory.revision_id = self._new_revision_id
1985
self.inv_sha1 = self.repository.add_inventory(
1986
self._new_revision_id,
1991
def _gen_revision_id(self):
1992
"""Return new revision-id."""
1993
s = '%s-%s-' % (self._config.user_email(),
1994
compact_date(self._timestamp))
1995
s += hexlify(rand_bytes(8))
1998
def _generate_revision_if_needed(self):
1999
"""Create a revision id if None was supplied.
2001
If the repository can not support user-specified revision ids
2002
they should override this function and raise UnsupportedOperation
2003
if _new_revision_id is not None.
2005
:raises: UnsupportedOperation
2007
if self._new_revision_id is None:
2008
self._new_revision_id = self._gen_revision_id()
2010
def record_entry_contents(self, ie, parent_invs, path, tree):
2011
"""Record the content of ie from tree into the commit if needed.
2013
:param ie: An inventory entry present in the commit.
2014
:param parent_invs: The inventories of the parent revisions of the
2016
:param path: The path the entry is at in the tree.
2017
:param tree: The tree which contains this entry and should be used to
2020
self.new_inventory.add(ie)
2022
# ie.revision is always None if the InventoryEntry is considered
2023
# for committing. ie.snapshot will record the correct revision
2024
# which may be the sole parent if it is untouched.
2025
if ie.revision is not None:
2027
previous_entries = ie.find_previous_heads(
2029
self.repository.weave_store,
2030
self.repository.get_transaction())
2031
# we are creating a new revision for ie in the history store
2033
ie.snapshot(self._new_revision_id, path, previous_entries, tree, self)
2035
def modified_directory(self, file_id, file_parents):
2036
"""Record the presence of a symbolic link.
2038
:param file_id: The file_id of the link to record.
2039
:param file_parents: The per-file parent revision ids.
2041
self._add_text_to_weave(file_id, [], file_parents.keys())
2043
def modified_file_text(self, file_id, file_parents,
2044
get_content_byte_lines, text_sha1=None,
2046
"""Record the text of file file_id
2048
:param file_id: The file_id of the file to record the text of.
2049
:param file_parents: The per-file parent revision ids.
2050
:param get_content_byte_lines: A callable which will return the byte
2052
:param text_sha1: Optional SHA1 of the file contents.
2053
:param text_size: Optional size of the file contents.
2055
mutter('storing text of file {%s} in revision {%s} into %r',
2056
file_id, self._new_revision_id, self.repository.weave_store)
2057
# special case to avoid diffing on renames or
2059
if (len(file_parents) == 1
2060
and text_sha1 == file_parents.values()[0].text_sha1
2061
and text_size == file_parents.values()[0].text_size):
2062
previous_ie = file_parents.values()[0]
2063
versionedfile = self.repository.weave_store.get_weave(file_id,
2064
self.repository.get_transaction())
2065
versionedfile.clone_text(self._new_revision_id,
2066
previous_ie.revision, file_parents.keys())
2067
return text_sha1, text_size
2069
new_lines = get_content_byte_lines()
2070
# TODO: Rather than invoking sha_strings here, _add_text_to_weave
2071
# should return the SHA1 and size
2072
self._add_text_to_weave(file_id, new_lines, file_parents.keys())
2073
return bzrlib.osutils.sha_strings(new_lines), \
2074
sum(map(len, new_lines))
2076
def modified_link(self, file_id, file_parents, link_target):
2077
"""Record the presence of a symbolic link.
2079
:param file_id: The file_id of the link to record.
2080
:param file_parents: The per-file parent revision ids.
2081
:param link_target: Target location of this link.
2083
self._add_text_to_weave(file_id, [], file_parents.keys())
2085
def _add_text_to_weave(self, file_id, new_lines, parents):
2086
versionedfile = self.repository.weave_store.get_weave_or_empty(
2087
file_id, self.repository.get_transaction())
2088
versionedfile.add_lines(self._new_revision_id, parents, new_lines)
2089
versionedfile.clear_cache()
1800
2092
# Copied from xml.sax.saxutils
1801
2093
def _unescape_xml(data):
1802
2094
"""Unescape &, <, and > in a string of data.