[patch] storage refactoring ready to merge?
Martin Pool
mbp at sourcefrog.net
Thu Jan 12 08:59:58 GMT 2006
Here's an updated version of the "storage" branch, which many people
have had a hand in by now. I've reviewed it today and made some small
changes and will merge it someone else would like to contribute a +1.
The changes (most from other people) include:
* add LockableFiles grouping
* store history in a Repository object rather than in the Branch itself
* remove bzrlib.clone
* split @needs_read_lock, @needs_write_lock into bzrlib.decorators
* add IterableFile
* remove many interfaces from Branch that don't (or no longer)
properly belong there
--
Martin
-------------- next part --------------
=== removed file 'bzrlib/clone.py'
--- bzrlib/clone.py
+++ /dev/null
@@ -1,162 +0,0 @@
-# Copyright (C) 2004, 2005 by Canonical Ltd
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-"""Make a copy of an entire branch and all its history.
-
-This is the underlying function for the branch/get/clone commands."""
-
-# TODO: This could be done *much* more efficiently by just copying
-# all the whole weaves and revisions, rather than getting one
-# revision at a time.
-
-# TODO: Optionally, after copying, discard any irrelevant information from
-# the destination, such as revisions committed after the last one we're interested
-# in. This needs to apply a weave prune operation (not written yet) to each
-# weave one by one.
-
-# Copying must be done in a way that supports http transports, where we
-# can't list a directory, and therefore have to rely on information
-# retrieved from top-level objects whose names we do know.
-#
-# In practice this means we first fetch the revision history and ancestry.
-# These give us a list of all the revisions that need to be fetched. We
-# also get the inventory weave. We then just need to get a list of all
-# file-ids ever referenced by this tree. (It might be nice to keep a list
-# of them directly.) This is done by walking over the inventories of all
-# copied revisions and accumulating a list of file ids.
-#
-# For local branches it is possible to optimize this considerably in two
-# ways. One is to hardlink the files (if possible and requested), rather
-# than copying them. Another is to simply list the directory rather than
-# walking through the inventories to find out what files are present -- but
-# there it may be better to just be consistent with remote branches.
-
-import os
-import sys
-
-import bzrlib
-from bzrlib.merge import build_working_dir
-from bzrlib.branch import Branch
-from bzrlib.trace import mutter, note
-from bzrlib.store import copy_all
-from bzrlib.errors import InvalidRevisionId
-
-def copy_branch(branch_from, to_location, revision=None, basis_branch=None):
- """Copy branch_from into the existing directory to_location.
-
- Returns the newly created branch object.
-
- revision
- If not None, only revisions up to this point will be copied.
- The head of the new branch will be that revision. Must be a
- revid or None.
-
- to_location -- The destination directory; must either exist and be
- empty, or not exist, in which case it is created.
-
- basis_branch
- A local branch to copy revisions from, related to branch_from.
- This is used when branching from a remote (slow) branch, and we have
- a local branch that might contain some relevant revisions.
- """
- assert isinstance(branch_from, Branch)
- assert isinstance(to_location, basestring)
- if basis_branch is not None:
- note("basis_branch is not supported for fast weave copy yet.")
- branch_from.lock_read()
- try:
- if not (branch_from.weave_store.listable()
- and branch_from.revision_store.listable()):
- return copy_branch_slower(branch_from, to_location, revision,
- basis_branch)
- history = _get_truncated_history(branch_from, revision)
- if not bzrlib.osutils.lexists(to_location):
- os.mkdir(to_location)
- branch_to = Branch.initialize(to_location)
- mutter("copy branch from %s to %s", branch_from, branch_to)
- branch_to.working_tree().set_root_id(branch_from.get_root_id())
- _copy_control_weaves(branch_from, branch_to)
- _copy_text_weaves(branch_from, branch_to)
- _copy_revision_store(branch_from, branch_to)
- branch_to.set_parent(branch_from.base)
- # must be done *after* history is copied across
- branch_to.append_revision(*history)
- build_working_dir(to_location)
- mutter("copied")
- return branch_to
- finally:
- branch_from.unlock()
-
-
-def _get_truncated_history(branch_from, revision_id):
- history = branch_from.revision_history()
- if revision_id is None:
- return history
- try:
- idx = history.index(revision_id)
- except ValueError:
- raise InvalidRevisionId(revision_id=revision, branch=branch_from)
- return history[:idx+1]
-
-def _copy_text_weaves(branch_from, branch_to):
- copy_all(branch_from.weave_store, branch_to.weave_store)
-
-
-def _copy_revision_store(branch_from, branch_to):
- copy_all(branch_from.revision_store, branch_to.revision_store)
-
-
-def _copy_control_weaves(branch_from, branch_to):
- to_control = branch_to.control_weaves
- from_control = branch_from.control_weaves
- to_control.copy_multi(from_control, ['inventory'])
-
-
-def copy_branch_slower(branch_from, to_location, revision=None, basis_branch=None):
- """Copy branch_from into the existing directory to_location.
-
- revision
- If not None, only revisions up to this point will be copied.
- The head of the new branch will be that revision. Must be a
- revid or None.
-
- to_location -- The destination directory; must either exist and be
- empty, or not exist, in which case it is created.
-
- revno
- The revision to copy up to
-
- basis_branch
- A local branch to copy revisions from, related to branch_from.
- This is used when branching from a remote (slow) branch, and we have
- a local branch that might contain some relevant revisions.
- """
- assert isinstance(branch_from, Branch)
- assert isinstance(to_location, basestring)
- if not bzrlib.osutils.lexists(to_location):
- os.mkdir(to_location)
- br_to = Branch.initialize(to_location)
- mutter("copy branch from %s to %s", branch_from, br_to)
- if basis_branch is not None:
- basis_branch.push_stores(br_to)
- br_to.working_tree().set_root_id(branch_from.get_root_id())
- if revision is None:
- revision = branch_from.last_revision()
- br_to.update_revisions(branch_from, stop_revision=revision)
- build_working_dir(to_location)
- br_to.set_parent(branch_from.base)
- mutter("copied")
- return br_to
=== added file 'bzrlib/decorators.py'
--- /dev/null
+++ bzrlib/decorators.py
@@ -0,0 +1,49 @@
+# Copyright (C) 2005 Canonical Ltd
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+def needs_read_lock(unbound):
+ """Decorate unbound to take out and release a read lock.
+
+ This decorator can be applied to methods of any class with lock_read() and
+ unlock() methods.
+
+ Typical usage:
+
+ class Branch(...):
+ @needs_read_lock
+ def branch_method(self, ...):
+ stuff
+ """
+ def decorated(self, *args, **kwargs):
+ self.lock_read()
+ try:
+ return unbound(self, *args, **kwargs)
+ finally:
+ self.unlock()
+ return decorated
+
+
+def needs_write_lock(unbound):
+ """Decorate unbound to take out and release a write lock."""
+ def decorated(self, *args, **kwargs):
+ self.lock_write()
+ try:
+ return unbound(self, *args, **kwargs)
+ finally:
+ self.unlock()
+ return decorated
+
=== added file 'bzrlib/iterablefile.py'
--- /dev/null
+++ bzrlib/iterablefile.py
@@ -0,0 +1,256 @@
+# Copyright (C) 2005 Aaron Bentley
+# <aaron.bentley at utoronto.ca>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import doctest
+
+class IterableFileBase(object):
+ """Create a file-like object from any iterable"""
+ def __init__(self, iterable):
+ object.__init__(self)
+ self._iter = iterable.__iter__()
+ self._buffer = ""
+ self.done = False
+
+ def read_n(self, length):
+ """
+ >>> IterableFileBase(['This ', 'is ', 'a ', 'test.']).read_n(8)
+ 'This is '
+ """
+ def test_length(result):
+ if len(result) >= length:
+ return length
+ else:
+ return None
+ return self._read(test_length)
+
+ def read_to(self, sequence, length=None):
+ """
+ >>> f = IterableFileBase(['Th\\nis ', 'is \\n', 'a ', 'te\\nst.'])
+ >>> f.read_to('\\n')
+ 'Th\\n'
+ >>> f.read_to('\\n')
+ 'is is \\n'
+ """
+ def test_contents(result):
+ if length is not None:
+ if len(result) >= length:
+ return length
+ try:
+ return result.index(sequence)+len(sequence)
+ except ValueError:
+ return None
+ return self._read(test_contents)
+
+ def _read(self, result_length):
+ """
+ Read data until result satisfies the condition result_length.
+ result_length is a callable that returns None until the condition
+ is satisfied, and returns the length of the result to use when
+ the condition is satisfied. (i.e. it returns the length of the
+ subset of the first condition match.)
+ """
+ result = self._buffer
+ while result_length(result) is None:
+ try:
+ result += self._iter.next()
+ except StopIteration:
+ self.done = True
+ self._buffer = ""
+ return result
+ output_length = result_length(result)
+ self._buffer = result[output_length:]
+ return result[:output_length]
+
+ def read_all(self):
+ """
+ >>> IterableFileBase(['This ', 'is ', 'a ', 'test.']).read_all()
+ 'This is a test.'
+ """
+ def no_stop(result):
+ return None
+ return self._read(no_stop)
+
+
+ def push_back(self, contents):
+ """
+ >>> f = IterableFileBase(['Th\\nis ', 'is \\n', 'a ', 'te\\nst.'])
+ >>> f.read_to('\\n')
+ 'Th\\n'
+ >>> f.push_back("Sh")
+ >>> f.read_all()
+ 'Shis is \\na te\\nst.'
+ """
+ self._buffer = contents + self._buffer
+
+
+class IterableFile(object):
+ """This class supplies all File methods that can be implemented cheaply."""
+ def __init__(self, iterable):
+ object.__init__(self)
+ self._file_base = IterableFileBase(iterable)
+ self._iter = self._make_iterator()
+ self._closed = False
+ self.softspace = 0
+
+ def _make_iterator(self):
+ while not self._file_base.done:
+ self._check_closed()
+ result = self._file_base.read_to('\n')
+ if result != '':
+ yield result
+
+ def _check_closed(self):
+ if self.closed:
+ raise ValueError("File is closed.")
+
+ def close(self):
+ """
+ >>> f = IterableFile(['This ', 'is ', 'a ', 'test.'])
+ >>> f.closed
+ False
+ >>> f.close()
+ >>> f.closed
+ True
+ """
+ self._file_base.done = True
+ self._closed = True
+
+ closed = property(lambda x: x._closed)
+
+ def flush(self):
+ """No-op for standard compliance.
+ >>> f = IterableFile([])
+ >>> f.close()
+ >>> f.flush()
+ Traceback (most recent call last):
+ ValueError: File is closed.
+ """
+ self._check_closed()
+
+ def next(self):
+ """Implementation of the iterator protocol's next()
+
+ >>> f = IterableFile(['This \\n', 'is ', 'a ', 'test.'])
+ >>> f.next()
+ 'This \\n'
+ >>> f.close()
+ >>> f.next()
+ Traceback (most recent call last):
+ ValueError: File is closed.
+ >>> f = IterableFile(['This \\n', 'is ', 'a ', 'test.\\n'])
+ >>> f.next()
+ 'This \\n'
+ >>> f.next()
+ 'is a test.\\n'
+ >>> f.next()
+ Traceback (most recent call last):
+ StopIteration
+ """
+ self._check_closed()
+ return self._iter.next()
+
+ def __iter__(self):
+ """
+ >>> list(IterableFile(['Th\\nis ', 'is \\n', 'a ', 'te\\nst.']))
+ ['Th\\n', 'is is \\n', 'a te\\n', 'st.']
+ >>> f = IterableFile(['Th\\nis ', 'is \\n', 'a ', 'te\\nst.'])
+ >>> f.close()
+ >>> list(f)
+ Traceback (most recent call last):
+ ValueError: File is closed.
+ """
+ return self
+
+ def read(self, length=None):
+ """
+ >>> IterableFile(['This ', 'is ', 'a ', 'test.']).read()
+ 'This is a test.'
+ >>> f = IterableFile(['This ', 'is ', 'a ', 'test.'])
+ >>> f.read(10)
+ 'This is a '
+ >>> f = IterableFile(['This ', 'is ', 'a ', 'test.'])
+ >>> f.close()
+ >>> f.read(10)
+ Traceback (most recent call last):
+ ValueError: File is closed.
+ """
+ self._check_closed()
+ if length is None:
+ return self._file_base.read_all()
+ else:
+ return self._file_base.read_n(length)
+
+ def read_to(self, sequence, size=None):
+ """
+ Read characters until a sequence is found, with optional max size.
+ The specified sequence, if found, will be included in the result
+
+ >>> f = IterableFile(['Th\\nis ', 'is \\n', 'a ', 'te\\nst.'])
+ >>> f.read_to('i')
+ 'Th\\ni'
+ >>> f.read_to('i')
+ 's i'
+ >>> f.close()
+ >>> f.read_to('i')
+ Traceback (most recent call last):
+ ValueError: File is closed.
+ """
+ self._check_closed()
+ return self._file_base.read_to(sequence, size)
+
+ def readline(self, size=None):
+ """
+ >>> f = IterableFile(['Th\\nis ', 'is \\n', 'a ', 'te\\nst.'])
+ >>> f.readline()
+ 'Th\\n'
+ >>> f.readline(4)
+ 'is i'
+ >>> f.close()
+ >>> f.readline()
+ Traceback (most recent call last):
+ ValueError: File is closed.
+ """
+ return self.read_to('\n', size)
+
+ def readlines(self, sizehint=None):
+ """
+ >>> f = IterableFile(['Th\\nis ', 'is \\n', 'a ', 'te\\nst.'])
+ >>> f.readlines()
+ ['Th\\n', 'is is \\n', 'a te\\n', 'st.']
+ >>> f = IterableFile(['Th\\nis ', 'is \\n', 'a ', 'te\\nst.'])
+ >>> f.close()
+ >>> f.readlines()
+ Traceback (most recent call last):
+ ValueError: File is closed.
+ """
+ lines = []
+ while True:
+ line = self.readline()
+ if line == "":
+ return lines
+ if sizehint is None:
+ lines.append(line)
+ elif len(line) < sizehint:
+ lines.append(line)
+ sizehint -= len(line)
+ else:
+ self._file_base.push_back(line)
+ return lines
+
+
+if __name__ == "__main__":
+ doctest.testmod()
=== added file 'bzrlib/lockable_files.py'
--- /dev/null
+++ bzrlib/lockable_files.py
@@ -0,0 +1,233 @@
+# Copyright (C) 2005 Canonical Ltd
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+from osutils import file_iterator
+
+import bzrlib
+import bzrlib.errors as errors
+from bzrlib.errors import LockError, ReadOnlyError
+from bzrlib.trace import mutter
+import bzrlib.transactions as transactions
+
+class LockableFiles(object):
+ """Object representing a set of files locked within the same scope
+
+ _lock_mode
+ None, or 'r' or 'w'
+
+ _lock_count
+ If _lock_mode is true, a positive count of the number of times the
+ lock has been taken *by this process*. Others may have compatible
+ read locks.
+
+ _lock
+ Lock object from bzrlib.lock.
+ """
+
+ _lock_mode = None
+ _lock_count = None
+ _lock = None
+ # If set to False (by a plugin, etc) BzrBranch will not set the
+ # mode on created files or directories
+ _set_file_mode = True
+ _set_dir_mode = True
+
+ def __init__(self, transport, lock_name):
+ object.__init__(self)
+ self._transport = transport
+ self.lock_name = lock_name
+ self._transaction = None
+ self._find_modes()
+
+ def __del__(self):
+ if self._lock_mode or self._lock:
+ # XXX: This should show something every time, and be suitable for
+ # headless operation and embedding
+ from warnings import warn
+ warn("file group %r was not explicitly unlocked" % self)
+ self._lock.unlock()
+
+ def _escape(self, file_or_path):
+ if not isinstance(file_or_path, basestring):
+ file_or_path = '/'.join(file_or_path)
+ if file_or_path == '':
+ return u''
+ return bzrlib.transport.urlescape(unicode(file_or_path))
+
+ def _find_modes(self):
+ """Determine the appropriate modes for files and directories."""
+ try:
+ try:
+ st = self._transport.stat(u'.')
+ except errors.NoSuchFile:
+ # The .bzr/ directory doesn't exist, try to
+ # inherit the permissions from the parent directory
+ # but only try 1 level up
+ temp_transport = self._transport.clone('..')
+ st = temp_transport.stat(u'.')
+ except (errors.TransportNotPossible, errors.NoSuchFile):
+ self._dir_mode = 0755
+ self._file_mode = 0644
+ else:
+ self._dir_mode = st.st_mode & 07777
+ # Remove the sticky and execute bits for files
+ self._file_mode = self._dir_mode & ~07111
+ if not self._set_dir_mode:
+ self._dir_mode = None
+ if not self._set_file_mode:
+ self._file_mode = None
+
+ def controlfilename(self, file_or_path):
+ """Return location relative to branch."""
+ return self._transport.abspath(self._escape(file_or_path))
+
+ def controlfile(self, file_or_path, mode='r'):
+ """Open a control file for this branch.
+
+ There are two classes of file in the control directory: text
+ and binary. binary files are untranslated byte streams. Text
+ control files are stored with Unix newlines and in UTF-8, even
+ if the platform or locale defaults are different.
+
+ Controlfiles should almost never be opened in write mode but
+ rather should be atomically copied and replaced using atomicfile.
+ """
+ import codecs
+
+ relpath = self._escape(file_or_path)
+ #TODO: codecs.open() buffers linewise, so it was overloaded with
+ # a much larger buffer, do we need to do the same for getreader/getwriter?
+ if mode == 'rb':
+ return self._transport.get(relpath)
+ elif mode == 'wb':
+ raise BzrError("Branch.controlfile(mode='wb') is not supported, use put[_utf8]")
+ elif mode == 'r':
+ # XXX: Do we really want errors='replace'? Perhaps it should be
+ # an error, or at least reported, if there's incorrectly-encoded
+ # data inside a file.
+ # <https://launchpad.net/products/bzr/+bug/3823>
+ return codecs.getreader('utf-8')(self._transport.get(relpath), errors='replace')
+ elif mode == 'w':
+ raise BzrError("Branch.controlfile(mode='w') is not supported, use put[_utf8]")
+ else:
+ raise BzrError("invalid controlfile mode %r" % mode)
+
+ def put(self, path, file):
+ """Write a file.
+
+ :param path: The path to put the file, relative to the .bzr control
+ directory
+ :param f: A file-like or string object whose contents should be copied.
+ """
+ if not self._lock_mode == 'w':
+ raise ReadOnlyError()
+ self._transport.put(self._escape(path), file, mode=self._file_mode)
+
+ def put_utf8(self, path, file, mode=None):
+ """Write a file, encoding as utf-8.
+
+ :param path: The path to put the file, relative to the .bzr control
+ directory
+ :param f: A file-like or string object whose contents should be copied.
+ """
+ import codecs
+ from iterablefile import IterableFile
+ ctrl_files = []
+ if hasattr(file, 'read'):
+ iterator = file_iterator(file)
+ else:
+ iterator = file
+ # IterableFile would not be needed if Transport.put took iterables
+ # instead of files. ADHB 2005-12-25
+ # RBC 20060103 surely its not needed anyway, with codecs transcode
+ # file support ?
+ # JAM 20060103 We definitely don't want encode(..., 'replace')
+ # these are valuable files which should have exact contents.
+ encoded_file = IterableFile(b.encode('utf-8') for b in
+ iterator)
+ self.put(path, encoded_file)
+
+ def lock_write(self):
+ mutter("lock write: %s (%s)", self, self._lock_count)
+ # TODO: Upgrade locking to support using a Transport,
+ # and potentially a remote locking protocol
+ if self._lock_mode:
+ if self._lock_mode != 'w':
+ raise LockError("can't upgrade to a write lock from %r" %
+ self._lock_mode)
+ self._lock_count += 1
+ else:
+ self._lock = self._transport.lock_write(
+ self._escape(self.lock_name))
+ self._lock_mode = 'w'
+ self._lock_count = 1
+ self._set_transaction(transactions.PassThroughTransaction())
+
+ def lock_read(self):
+ mutter("lock read: %s (%s)", self, self._lock_count)
+ if self._lock_mode:
+ assert self._lock_mode in ('r', 'w'), \
+ "invalid lock mode %r" % self._lock_mode
+ self._lock_count += 1
+ else:
+ self._lock = self._transport.lock_read(
+ self._escape(self.lock_name))
+ self._lock_mode = 'r'
+ self._lock_count = 1
+ self._set_transaction(transactions.ReadOnlyTransaction())
+ # 5K may be excessive, but hey, its a knob.
+ self.get_transaction().set_cache_size(5000)
+
+ def unlock(self):
+ mutter("unlock: %s (%s)", self, self._lock_count)
+ if not self._lock_mode:
+ raise LockError('branch %r is not locked' % (self))
+
+ if self._lock_count > 1:
+ self._lock_count -= 1
+ else:
+ self._finish_transaction()
+ self._lock.unlock()
+ self._lock = None
+ self._lock_mode = self._lock_count = None
+
+ def get_transaction(self):
+ """Return the current active transaction.
+
+ If no transaction is active, this returns a passthrough object
+ for which all data is immediately flushed and no caching happens.
+ """
+ if self._transaction is None:
+ return transactions.PassThroughTransaction()
+ else:
+ return self._transaction
+
+ def _set_transaction(self, new_transaction):
+ """Set a new active transaction."""
+ if self._transaction is not None:
+ raise errors.LockError('Branch %s is in a transaction already.' %
+ self)
+ self._transaction = new_transaction
+
+ def _finish_transaction(self):
+ """Exit the current transaction."""
+ if self._transaction is None:
+ raise errors.LockError('Branch %s is not in a transaction' %
+ self)
+ transaction = self._transaction
+ self._transaction = None
+ transaction.finish()
=== added file 'bzrlib/repository.py'
--- /dev/null
+++ bzrlib/repository.py
@@ -0,0 +1,253 @@
+# Copyright (C) 2005 Canonical Ltd
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from cStringIO import StringIO
+
+from bzrlib.lockable_files import LockableFiles
+from bzrlib.tree import EmptyTree
+from bzrlib.revision import NULL_REVISION
+from bzrlib.store import copy_all
+from bzrlib.store.weave import WeaveStore
+from bzrlib.store.text import TextStore
+import bzrlib.xml5
+from bzrlib.tree import RevisionTree
+from bzrlib.errors import InvalidRevisionId
+from bzrlib.testament import Testament
+from bzrlib.decorators import needs_read_lock, needs_write_lock
+
+
+
+class Repository(object):
+ """Repository holding history for one or more branches.
+
+ The repository holds and retrieves historical information including
+ revisions and file history. It's normally accessed only by the Branch,
+ which views a particular line of development through that history.
+
+ The Repository builds on top of Stores and a Transport, which respectively
+ describe the disk data format and the way of accessing the (possibly
+ remote) disk.
+ """
+
+ def __init__(self, transport, branch_format):
+ object.__init__(self)
+ self.control_files = LockableFiles(transport.clone(bzrlib.BZRDIR), 'README')
+
+ dir_mode = self.control_files._dir_mode
+ file_mode = self.control_files._file_mode
+
+ def get_weave(name, prefixed=False):
+ if name:
+ name = bzrlib.BZRDIR + '/' + unicode(name)
+ else:
+ name = bzrlib.BZRDIR
+ relpath = self.control_files._escape(name)
+ weave_transport = transport.clone(relpath)
+ ws = WeaveStore(weave_transport, prefixed=prefixed,
+ dir_mode=dir_mode,
+ file_mode=file_mode)
+ if self.control_files._transport.should_cache():
+ ws.enable_cache = True
+ return ws
+
+ def get_store(name, compressed=True, prefixed=False):
+ # FIXME: This approach of assuming stores are all entirely compressed
+ # or entirely uncompressed is tidy, but breaks upgrade from
+ # some existing branches where there's a mixture; we probably
+ # still want the option to look for both.
+ if name:
+ name = bzrlib.BZRDIR + '/' + unicode(name)
+ else:
+ name = bzrlib.BZRDIR
+ relpath = self.control_files._escape(name)
+ store = TextStore(transport.clone(relpath),
+ prefixed=prefixed, compressed=compressed,
+ dir_mode=dir_mode,
+ file_mode=file_mode)
+ #if self._transport.should_cache():
+ # cache_path = os.path.join(self.cache_root, name)
+ # os.mkdir(cache_path)
+ # store = bzrlib.store.CachedStore(store, cache_path)
+ return store
+
+ if branch_format == 4:
+ self.inventory_store = get_store('inventory-store')
+ self.text_store = get_store('text-store')
+ self.revision_store = get_store('revision-store')
+ elif branch_format == 5:
+ self.control_weaves = get_weave('')
+ self.weave_store = get_weave('weaves')
+ self.revision_store = get_store('revision-store', compressed=False)
+ elif branch_format == 6:
+ self.control_weaves = get_weave('')
+ self.weave_store = get_weave('weaves', prefixed=True)
+ self.revision_store = get_store('revision-store', compressed=False,
+ prefixed=True)
+ self.revision_store.register_suffix('sig')
+
+ def lock_write(self):
+ self.control_files.lock_write()
+
+ def lock_read(self):
+ self.control_files.lock_read()
+
+ def unlock(self):
+ self.control_files.unlock()
+
+ def copy(self, destination):
+ destination.control_weaves.copy_multi(self.control_weaves,
+ ['inventory'])
+ copy_all(self.weave_store, destination.weave_store)
+ copy_all(self.revision_store, destination.revision_store)
+
+ def has_revision(self, revision_id):
+ """True if this branch has a copy of the revision.
+
+ This does not necessarily imply the revision is merge
+ or on the mainline."""
+ return (revision_id is None
+ or self.revision_store.has_id(revision_id))
+
+ @needs_read_lock
+ def get_revision_xml_file(self, revision_id):
+ """Return XML file object for revision object."""
+ if not revision_id or not isinstance(revision_id, basestring):
+ raise InvalidRevisionId(revision_id=revision_id, branch=self)
+ try:
+ return self.revision_store.get(revision_id)
+ except (IndexError, KeyError):
+ raise bzrlib.errors.NoSuchRevision(self, revision_id)
+
+ def get_revision_xml(self, revision_id):
+ return self.get_revision_xml_file(revision_id).read()
+
+ def get_revision(self, revision_id):
+ """Return the Revision object for a named revision"""
+ xml_file = self.get_revision_xml_file(revision_id)
+
+ try:
+ r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
+ except SyntaxError, e:
+ raise bzrlib.errors.BzrError('failed to unpack revision_xml',
+ [revision_id,
+ str(e)])
+
+ assert r.revision_id == revision_id
+ return r
+
+ def get_revision_sha1(self, revision_id):
+ """Hash the stored value of a revision, and return it."""
+ # In the future, revision entries will be signed. At that
+ # point, it is probably best *not* to include the signature
+ # in the revision hash. Because that lets you re-sign
+ # the revision, (add signatures/remove signatures) and still
+ # have all hash pointers stay consistent.
+ # But for now, just hash the contents.
+ return bzrlib.osutils.sha_file(self.get_revision_xml_file(revision_id))
+
+ @needs_write_lock
+ def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
+ self.revision_store.add(StringIO(gpg_strategy.sign(plaintext)),
+ revision_id, "sig")
+
+ def get_inventory_weave(self):
+ return self.control_weaves.get_weave('inventory',
+ self.get_transaction())
+
+ def get_inventory(self, revision_id):
+ """Get Inventory object by hash."""
+ xml = self.get_inventory_xml(revision_id)
+ return bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
+
+ def get_inventory_xml(self, revision_id):
+ """Get inventory XML as a file object."""
+ try:
+ assert isinstance(revision_id, basestring), type(revision_id)
+ iw = self.get_inventory_weave()
+ return iw.get_text(iw.lookup(revision_id))
+ except IndexError:
+ raise bzrlib.errors.HistoryMissing(self, 'inventory', revision_id)
+
+ def get_inventory_sha1(self, revision_id):
+ """Return the sha1 hash of the inventory entry
+ """
+ return self.get_revision(revision_id).inventory_sha1
+
+ def get_revision_inventory(self, revision_id):
+ """Return inventory of a past revision."""
+ # TODO: Unify this with get_inventory()
+ # bzr 0.0.6 and later imposes the constraint that the inventory_id
+ # must be the same as its revision, so this is trivial.
+ if revision_id == None:
+ # This does not make sense: if there is no revision,
+ # then it is the current tree inventory surely ?!
+ # and thus get_root_id() is something that looks at the last
+ # commit on the branch, and the get_root_id is an inventory check.
+ raise NotImplementedError
+ # return Inventory(self.get_root_id())
+ else:
+ return self.get_inventory(revision_id)
+
+ def revision_tree(self, revision_id):
+ """Return Tree for a revision on this branch.
+
+ `revision_id` may be None for the null revision, in which case
+ an `EmptyTree` is returned."""
+ # TODO: refactor this to use an existing revision object
+ # so we don't need to read it in twice.
+ if revision_id == None or revision_id == NULL_REVISION:
+ return EmptyTree()
+ else:
+ inv = self.get_revision_inventory(revision_id)
+ return RevisionTree(self, inv, revision_id)
+
+ def get_ancestry(self, revision_id):
+ """Return a list of revision-ids integrated by a revision.
+
+ This is topologically sorted.
+ """
+ if revision_id is None:
+ return [None]
+ w = self.get_inventory_weave()
+ return [None] + map(w.idx_to_name,
+ w.inclusions([w.lookup(revision_id)]))
+
+ @needs_read_lock
+ def print_file(self, file, revision_id):
+ """Print `file` to stdout."""
+ tree = self.revision_tree(revision_id)
+ # use inventory as it was in that revision
+ file_id = tree.inventory.path2id(file)
+ if not file_id:
+ raise BzrError("%r is not present in revision %s" % (file, revno))
+ try:
+ revno = self.revision_id_to_revno(revision_id)
+ except errors.NoSuchRevision:
+ # TODO: This should not be BzrError,
+ # but NoSuchFile doesn't fit either
+ raise BzrError('%r is not present in revision %s'
+ % (file, revision_id))
+ else:
+ raise BzrError('%r is not present in revision %s'
+ % (file, revno))
+ tree.print_file(file_id)
+
+ def get_transaction(self):
+ return self.control_files.get_transaction()
+
+ def sign_revision(self, revision_id, gpg_strategy):
+ plaintext = Testament.from_revision(self, revision_id).as_short_text()
+ self.store_revision_signature(gpg_strategy, plaintext, revision_id)
=== added file 'bzrlib/tests/test_lockable_files.py'
--- /dev/null
+++ bzrlib/tests/test_lockable_files.py
@@ -0,0 +1,85 @@
+# Copyright (C) 2005 by Canonical Ltd
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from StringIO import StringIO
+
+from bzrlib.branch import Branch
+from bzrlib.errors import NoSuchFile, ReadOnlyError
+from bzrlib.lockable_files import LockableFiles
+from bzrlib.tests import TestCaseInTempDir
+from bzrlib.transactions import PassThroughTransaction, ReadOnlyTransaction
+from bzrlib.transport import get_transport
+
+class TestLockableFiles(TestCaseInTempDir):
+
+ def setUp(self):
+ super(TestLockableFiles, self).setUp()
+ transport = get_transport('.')
+ transport.mkdir('.bzr')
+ transport.put('.bzr/my-lock', StringIO(''))
+ self.lockable = LockableFiles(transport.clone('.bzr'), 'my-lock')
+
+ def test_read_write(self):
+ self.assertRaises(NoSuchFile, self.lockable.controlfile, 'foo')
+ self.lockable.lock_write()
+ try:
+ unicode_string = u'bar\u1234'
+ self.assertEqual(4, len(unicode_string))
+ byte_string = unicode_string.encode('utf-8')
+ self.assertEqual(6, len(byte_string))
+ self.assertRaises(UnicodeEncodeError, self.lockable.put, 'foo',
+ StringIO(unicode_string))
+ self.lockable.put('foo', StringIO(byte_string))
+ self.assertEqual(byte_string,
+ self.lockable.controlfile('foo', 'rb').read())
+ self.assertEqual(unicode_string,
+ self.lockable.controlfile('foo', 'r').read())
+ self.lockable.put_utf8('bar', StringIO(unicode_string))
+ self.assertEqual(unicode_string,
+ self.lockable.controlfile('bar', 'r').read())
+ self.assertEqual(byte_string,
+ self.lockable.controlfile('bar', 'rb').read())
+ finally:
+ self.lockable.unlock()
+
+ def test_locks(self):
+ self.lockable.lock_read()
+ self.lockable.unlock()
+ self.assertRaises(ReadOnlyError, self.lockable.put, 'foo',
+ StringIO('bar\u1234'))
+
+ def test_transactions(self):
+ self.assertIs(self.lockable.get_transaction().__class__,
+ PassThroughTransaction)
+ self.lockable.lock_read()
+ try:
+ self.assertIs(self.lockable.get_transaction().__class__,
+ ReadOnlyTransaction)
+ finally:
+ self.lockable.unlock()
+ self.assertIs(self.lockable.get_transaction().__class__,
+ PassThroughTransaction)
+ self.lockable.lock_write()
+ self.assertIs(self.lockable.get_transaction().__class__,
+ PassThroughTransaction)
+ self.lockable.unlock()
+
+ def test__escape(self):
+ self.assertEqual('%25', self.lockable._escape('%'))
+
+ def test__escape_empty(self):
+ self.assertEqual('', self.lockable._escape(''))
+
=== modified file 'BRANCH.TODO'
--- BRANCH.TODO
+++ BRANCH.TODO
@@ -1,3 +1,14 @@
# This file is for listing TODOs for branches that are being worked on.
# It should ALWAYS be empty in the mainline or in integration branches.
-#
+
+The purpose of this branch is to separate the storage of history within a
+branch from the logical identity of the branch itself (being the tip
+pointer and revision-history).
+
+The major refactoring here is that historical data (revisions, file
+weaves, etc) are stored not in the Branch itself but in
+`branch.repository`, which is a `Repository` object.
+
+All the immediately accessible history for a Branch is stored in its
+repository, but the repository might hold information on multiple
+branches, to implement what's called "shared storage".
=== modified file 'NEWS'
--- NEWS
+++ NEWS
@@ -274,6 +274,12 @@
* Simplify handling of DivergedBranches in cmd_pull().
(Michael Ellerman)
+
+ * Branch.controlfile* logic has moved to lockablefiles.LockableFiles, which
+ is exposed as Branch().control_files. Also this has been altered with the
+ controlfile pre/suffix replaced by simple method names like 'get' and
+ 'put'. (Aaron Bentley, Robert Collins).
+
* Deprecated functions and methods can now be marked as such using the
bzrlib.symbol_versioning module. Marked method have their docstring
updated and will issue a DeprecationWarning using the warnings module
=== modified file 'bzrlib/annotate.py'
--- bzrlib/annotate.py
+++ bzrlib/annotate.py
@@ -55,7 +55,8 @@
def _annotate_file(branch, rev_id, file_id ):
rh = branch.revision_history()
- w = branch.weave_store.get_weave(file_id, branch.get_transaction())
+ w = branch.repository.weave_store.get_weave(file_id,
+ branch.repository.get_transaction())
last_origin = None
for origin, text in w.annotate_iter(rev_id):
text = text.rstrip('\r\n')
@@ -64,14 +65,14 @@
else:
last_origin = origin
line_rev_id = w.idx_to_name(origin)
- if not branch.has_revision(line_rev_id):
+ if not branch.repository.has_revision(line_rev_id):
(revno_str, author, date_str) = ('?','?','?')
else:
if line_rev_id in rh:
revno_str = str(rh.index(line_rev_id) + 1)
else:
revno_str = 'merge'
- rev = branch.get_revision(line_rev_id)
+ rev = branch.repository.get_revision(line_rev_id)
tz = rev.timezone or 0
date_str = time.strftime('%Y%m%d',
time.gmtime(rev.timestamp + tz))
=== modified file 'bzrlib/branch.py'
--- bzrlib/branch.py
+++ bzrlib/branch.py
@@ -24,7 +24,6 @@
import bzrlib
-import bzrlib.inventory as inventory
from bzrlib.trace import mutter, note
from bzrlib.osutils import (isdir, quotefn,
rename, splitpath, sha_file,
@@ -36,60 +35,43 @@
UnlistableBranch, NoSuchFile, NotVersionedError,
NoWorkingTree)
from bzrlib.textui import show_status
-from bzrlib.revision import (Revision, is_ancestor, get_intervening_revisions,
- NULL_REVISION)
-
+from bzrlib.config import TreeConfig
from bzrlib.delta import compare_trees
-from bzrlib.tree import EmptyTree, RevisionTree
+import bzrlib.inventory as inventory
from bzrlib.inventory import Inventory
+from bzrlib.lockable_files import LockableFiles
+from bzrlib.revision import (Revision, is_ancestor, get_intervening_revisions)
+from bzrlib.repository import Repository
from bzrlib.store import copy_all
-from bzrlib.store.text import TextStore
-from bzrlib.store.weave import WeaveStore
-from bzrlib.testament import Testament
import bzrlib.transactions as transactions
from bzrlib.transport import Transport, get_transport
+from bzrlib.tree import EmptyTree, RevisionTree
+import bzrlib.ui
import bzrlib.xml5
-import bzrlib.ui
-from config import TreeConfig
+from bzrlib.decorators import needs_read_lock, needs_write_lock
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
BZR_BRANCH_FORMAT_6 = "Bazaar-NG branch, format 6\n"
-## TODO: Maybe include checks for common corruption of newlines, etc?
-
+
+
+# TODO: Maybe include checks for common corruption of newlines, etc?
# TODO: Some operations like log might retrieve the same revisions
# repeatedly to calculate deltas. We could perhaps have a weakref
# cache in memory to make this faster. In general anything can be
# cached in memory between lock and unlock operations.
+# FIXME: At the moment locking the Branch locks both the repository and the
+# control files, representing the two aspects currently controlled by one
+# object. However, they currently both map to the same lockfile.
+
def find_branch(*ignored, **ignored_too):
# XXX: leave this here for about one release, then remove it
raise NotImplementedError('find_branch() is not supported anymore, '
'please use one of the new branch constructors')
-
-def needs_read_lock(unbound):
- """Decorate unbound to take out and release a read lock."""
- def decorated(self, *args, **kwargs):
- self.lock_read()
- try:
- return unbound(self, *args, **kwargs)
- finally:
- self.unlock()
- return decorated
-
-
-def needs_write_lock(unbound):
- """Decorate unbound to take out and release a write lock."""
- def decorated(self, *args, **kwargs):
- self.lock_write()
- try:
- return unbound(self, *args, **kwargs)
- finally:
- self.unlock()
- return decorated
######################################################################
# branch objects
@@ -144,7 +126,7 @@
@staticmethod
def initialize(base):
"""Create a new branch, rooted at 'base' (url)"""
- t = get_transport(base)
+ t = get_transport(unicode(base))
return BzrBranch(t, init=True)
def setup_caching(self, cache_root):
@@ -168,14 +150,6 @@
"""Copy the content of this branches store to branch_to."""
raise NotImplementedError('push_stores is abstract')
- def get_transaction(self):
- """Return the current active transaction.
-
- If no transaction is active, this returns a passthrough object
- for which all data is immediately flushed and no caching happens.
- """
- raise NotImplementedError('get_transaction is abstract')
-
def lock_write(self):
raise NotImplementedError('lock_write is abstract')
@@ -185,6 +159,10 @@
def unlock(self):
raise NotImplementedError('unlock is abstract')
+ def peek_lock_mode(self):
+ """Return lock mode for the Branch: 'r', 'w' or None"""
+ raise NotImplementedError(self.is_locked)
+
def abspath(self, name):
"""Return absolute filename for something in the branch
@@ -192,50 +170,11 @@
method and not a tree method.
"""
raise NotImplementedError('abspath is abstract')
-
- def controlfilename(self, file_or_path):
- """Return location relative to branch."""
- raise NotImplementedError('controlfilename is abstract')
-
- def controlfile(self, file_or_path, mode='r'):
- """Open a control file for this branch.
-
- There are two classes of file in the control directory: text
- and binary. binary files are untranslated byte streams. Text
- control files are stored with Unix newlines and in UTF-8, even
- if the platform or locale defaults are different.
-
- Controlfiles should almost never be opened in write mode but
- rather should be atomically copied and replaced using atomicfile.
- """
- raise NotImplementedError('controlfile is abstract')
-
- def put_controlfile(self, path, f, encode=True):
- """Write an entry as a controlfile.
-
- :param path: The path to put the file, relative to the .bzr control
- directory
- :param f: A file-like or string object whose contents should be copied.
- :param encode: If true, encode the contents as utf-8
- """
- raise NotImplementedError('put_controlfile is abstract')
-
- def put_controlfiles(self, files, encode=True):
- """Write several entries as controlfiles.
-
- :param files: A list of [(path, file)] pairs, where the path is the directory
- underneath the bzr control directory
- :param encode: If true, encode the contents as utf-8
- """
- raise NotImplementedError('put_controlfiles is abstract')
def get_root_id(self):
"""Return the id of this branches root"""
raise NotImplementedError('get_root_id is abstract')
- def set_root_id(self, file_id):
- raise NotImplementedError('set_root_id is abstract')
-
def print_file(self, file, revision_id):
"""Print `file` to stdout."""
raise NotImplementedError('print_file is abstract')
@@ -245,69 +184,6 @@
def set_revision_history(self, rev_history):
raise NotImplementedError('set_revision_history is abstract')
-
- def has_revision(self, revision_id):
- """True if this branch has a copy of the revision.
-
- This does not necessarily imply the revision is merge
- or on the mainline."""
- raise NotImplementedError('has_revision is abstract')
-
- def get_revision_xml(self, revision_id):
- raise NotImplementedError('get_revision_xml is abstract')
-
- def get_revision(self, revision_id):
- """Return the Revision object for a named revision"""
- raise NotImplementedError('get_revision is abstract')
-
- def get_revision_delta(self, revno):
- """Return the delta for one revision.
-
- The delta is relative to its mainline predecessor, or the
- empty tree for revision 1.
- """
- assert isinstance(revno, int)
- rh = self.revision_history()
- if not (1 <= revno <= len(rh)):
- raise InvalidRevisionNumber(revno)
-
- # revno is 1-based; list is 0-based
-
- new_tree = self.revision_tree(rh[revno-1])
- if revno == 1:
- old_tree = EmptyTree()
- else:
- old_tree = self.revision_tree(rh[revno-2])
-
- return compare_trees(old_tree, new_tree)
-
- def get_revision_sha1(self, revision_id):
- """Hash the stored value of a revision, and return it."""
- raise NotImplementedError('get_revision_sha1 is abstract')
-
- def get_ancestry(self, revision_id):
- """Return a list of revision-ids integrated by a revision.
-
- This currently returns a list, but the ordering is not guaranteed:
- treat it as a set.
- """
- raise NotImplementedError('get_ancestry is abstract')
-
- def get_inventory(self, revision_id):
- """Get Inventory object by hash."""
- raise NotImplementedError('get_inventory is abstract')
-
- def get_inventory_xml(self, revision_id):
- """Get inventory XML as a file object."""
- raise NotImplementedError('get_inventory_xml is abstract')
-
- def get_inventory_sha1(self, revision_id):
- """Return the sha1 hash of the inventory entry."""
- raise NotImplementedError('get_inventory_sha1 is abstract')
-
- def get_revision_inventory(self, revision_id):
- """Return inventory of a past revision."""
- raise NotImplementedError('get_revision_inventory is abstract')
def revision_history(self):
"""Return sequence of revision hashes on to this branch."""
@@ -373,6 +249,7 @@
if stop_revision > other_len:
raise bzrlib.errors.NoSuchRevision(self, stop_revision)
return other_history[self_len:stop_revision]
+
def update_revisions(self, other, stop_revision=None):
"""Pull in new perfect-fit revisions."""
@@ -401,13 +278,6 @@
raise bzrlib.errors.NoSuchRevision(self, revno)
return history[revno - 1]
- def revision_tree(self, revision_id):
- """Return Tree for a revision on this branch.
-
- `revision_id` may be None for the null revision, in which case
- an `EmptyTree` is returned."""
- raise NotImplementedError('revision_tree is abstract')
-
def working_tree(self):
"""Return a `Tree` for the working copy if this is a local branch."""
raise NotImplementedError('working_tree is abstract')
@@ -420,7 +290,7 @@
If there are no revisions yet, return an `EmptyTree`.
"""
- return self.revision_tree(self.last_revision())
+ return self.repository.revision_tree(self.last_revision())
def rename_one(self, from_rel, to_rel):
"""Rename one file.
@@ -487,6 +357,47 @@
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
raise NotImplementedError('store_revision_signature is abstract')
+ def clone(self, to_location, revision=None, basis_branch=None, to_branch_type=None):
+ """Copy this branch into the existing directory to_location.
+
+ Returns the newly created branch object.
+
+ revision
+ If not None, only revisions up to this point will be copied.
+ The head of the new branch will be that revision. Must be a
+ revid or None.
+
+ to_location -- The destination directory; must either exist and be
+ empty, or not exist, in which case it is created.
+
+ basis_branch
+ A local branch to copy revisions from, related to this branch.
+ This is used when branching from a remote (slow) branch, and we have
+ a local branch that might contain some relevant revisions.
+
+ to_branch_type
+ Branch type of destination branch
+ """
+ assert isinstance(to_location, basestring)
+ if not bzrlib.osutils.lexists(to_location):
+ os.mkdir(to_location)
+ if to_branch_type is None:
+ to_branch_type = BzrBranch
+ br_to = to_branch_type.initialize(to_location)
+ mutter("copy branch from %s to %s", self, br_to)
+ if basis_branch is not None:
+ basis_branch.push_stores(br_to)
+ br_to.working_tree().set_root_id(self.get_root_id())
+ if revision is None:
+ revision = self.last_revision()
+ br_to.update_revisions(self, stop_revision=revision)
+ br_to.set_parent(self.base)
+ # circular import protection
+ from bzrlib.merge import build_working_dir
+ build_working_dir(to_location)
+ mutter("copied")
+ return br_to
+
class BzrBranch(Branch):
"""A branch stored in the actual filesystem.
@@ -494,28 +405,12 @@
really matter if it's on an nfs/smb/afs/coda/... share, as long as
it's writable, and can be accessed via the normal filesystem API.
- _lock_mode
- None, or 'r' or 'w'
-
- _lock_count
- If _lock_mode is true, a positive count of the number of times the
- lock has been taken.
-
- _lock
- Lock object from bzrlib.lock.
"""
# We actually expect this class to be somewhat short-lived; part of its
# purpose is to try to isolate what bits of the branch logic are tied to
# filesystem access, so that in a later step, we can extricate them to
# a separarte ("storage") class.
- _lock_mode = None
- _lock_count = None
- _lock = None
_inventory_weave = None
- # If set to False (by a plugin, etc) BzrBranch will not set the
- # mode on created files or directories
- _set_file_mode = True
- _set_dir_mode = True
# Map some sort of prefix into a namespace
# stuff like "revno:10", "revid:", etc.
@@ -562,59 +457,22 @@
"""
assert isinstance(transport, Transport), \
"%r is not a Transport" % transport
- self._transport = transport
+ # TODO: jam 20060103 We create a clone of this transport at .bzr/
+ # and then we forget about it, should we keep a handle to it?
+ self._base = transport.base
+ self.control_files = LockableFiles(transport.clone(bzrlib.BZRDIR),
+ 'branch-lock')
if init:
self._make_control()
self._check_format(relax_version_check)
- self._find_modes()
-
- def get_store(name, compressed=True, prefixed=False):
- relpath = self._rel_controlfilename(unicode(name))
- store = TextStore(self._transport.clone(relpath),
- dir_mode=self._dir_mode,
- file_mode=self._file_mode,
- prefixed=prefixed,
- compressed=compressed)
- return store
-
- def get_weave(name, prefixed=False):
- relpath = self._rel_controlfilename(unicode(name))
- ws = WeaveStore(self._transport.clone(relpath),
- prefixed=prefixed,
- dir_mode=self._dir_mode,
- file_mode=self._file_mode)
- if self._transport.should_cache():
- ws.enable_cache = True
- return ws
-
- if self._branch_format == 4:
- self.inventory_store = get_store('inventory-store')
- self.text_store = get_store('text-store')
- self.revision_store = get_store('revision-store')
- elif self._branch_format == 5:
- self.control_weaves = get_weave(u'')
- self.weave_store = get_weave(u'weaves')
- self.revision_store = get_store(u'revision-store', compressed=False)
- elif self._branch_format == 6:
- self.control_weaves = get_weave(u'')
- self.weave_store = get_weave(u'weaves', prefixed=True)
- self.revision_store = get_store(u'revision-store', compressed=False,
- prefixed=True)
- self.revision_store.register_suffix('sig')
- self._transaction = None
+ self.repository = Repository(transport, self._branch_format)
def __str__(self):
- return '%s(%r)' % (self.__class__.__name__, self._transport.base)
+ return '%s(%r)' % (self.__class__.__name__, self.base)
__repr__ = __str__
def __del__(self):
- if self._lock_mode or self._lock:
- # XXX: This should show something every time, and be suitable for
- # headless operation and embedding
- warn("branch %r was not explicitly unlocked" % self)
- self._lock.unlock()
-
# TODO: It might be best to do this somewhere else,
# but it is nice for a Branch object to automatically
# cache it's information.
@@ -629,151 +487,36 @@
self.cache_root = None
def _get_base(self):
- if self._transport:
- return self._transport.base
- return None
+ return self._base
base = property(_get_base, doc="The URL for the root of this branch.")
def _finish_transaction(self):
"""Exit the current transaction."""
- if self._transaction is None:
- raise errors.LockError('Branch %s is not in a transaction' %
- self)
- transaction = self._transaction
- self._transaction = None
- transaction.finish()
+ return self.control_files._finish_transaction()
def get_transaction(self):
- """See Branch.get_transaction."""
- if self._transaction is None:
- return transactions.PassThroughTransaction()
- else:
- return self._transaction
-
- def _set_transaction(self, new_transaction):
+ """Return the current active transaction.
+
+ If no transaction is active, this returns a passthrough object
+ for which all data is immediately flushed and no caching happens.
+ """
+ # this is an explicit function so that we can do tricky stuff
+ # when the storage in rev_storage is elsewhere.
+ # we probably need to hook the two 'lock a location' and
+ # 'have a transaction' together more delicately, so that
+ # we can have two locks (branch and storage) and one transaction
+ # ... and finishing the transaction unlocks both, but unlocking
+ # does not. - RBC 20051121
+ return self.control_files.get_transaction()
+
+ def _set_transaction(self, transaction):
"""Set a new active transaction."""
- if self._transaction is not None:
- raise errors.LockError('Branch %s is in a transaction already.' %
- self)
- self._transaction = new_transaction
-
- def lock_write(self):
- #mutter("lock write: %s (%s)", self, self._lock_count)
- # TODO: Upgrade locking to support using a Transport,
- # and potentially a remote locking protocol
- if self._lock_mode:
- if self._lock_mode != 'w':
- raise LockError("can't upgrade to a write lock from %r" %
- self._lock_mode)
- self._lock_count += 1
- else:
- self._lock = self._transport.lock_write(
- self._rel_controlfilename('branch-lock'))
- self._lock_mode = 'w'
- self._lock_count = 1
- self._set_transaction(transactions.PassThroughTransaction())
-
- def lock_read(self):
- #mutter("lock read: %s (%s)", self, self._lock_count)
- if self._lock_mode:
- assert self._lock_mode in ('r', 'w'), \
- "invalid lock mode %r" % self._lock_mode
- self._lock_count += 1
- else:
- self._lock = self._transport.lock_read(
- self._rel_controlfilename('branch-lock'))
- self._lock_mode = 'r'
- self._lock_count = 1
- self._set_transaction(transactions.ReadOnlyTransaction())
- # 5K may be excessive, but hey, its a knob.
- self.get_transaction().set_cache_size(5000)
-
- def unlock(self):
- #mutter("unlock: %s (%s)", self, self._lock_count)
- if not self._lock_mode:
- raise LockError('branch %r is not locked' % (self))
-
- if self._lock_count > 1:
- self._lock_count -= 1
- else:
- self._finish_transaction()
- self._lock.unlock()
- self._lock = None
- self._lock_mode = self._lock_count = None
+ return self.control_files._set_transaction(transaction)
def abspath(self, name):
"""See Branch.abspath."""
- return self._transport.abspath(name)
-
- def _rel_controlfilename(self, file_or_path):
- if not isinstance(file_or_path, basestring):
- file_or_path = u'/'.join(file_or_path)
- if file_or_path == '':
- return bzrlib.BZRDIR
- return bzrlib.transport.urlescape(bzrlib.BZRDIR + u'/' + file_or_path)
-
- def controlfilename(self, file_or_path):
- """See Branch.controlfilename."""
- return self._transport.abspath(self._rel_controlfilename(file_or_path))
-
- def controlfile(self, file_or_path, mode='r'):
- """See Branch.controlfile."""
- import codecs
-
- relpath = self._rel_controlfilename(file_or_path)
- #TODO: codecs.open() buffers linewise, so it was overloaded with
- # a much larger buffer, do we need to do the same for getreader/getwriter?
- if mode == 'rb':
- return self._transport.get(relpath)
- elif mode == 'wb':
- raise BzrError("Branch.controlfile(mode='wb') is not supported, use put_controlfiles")
- elif mode == 'r':
- # XXX: Do we really want errors='replace'? Perhaps it should be
- # an error, or at least reported, if there's incorrectly-encoded
- # data inside a file.
- # <https://launchpad.net/products/bzr/+bug/3823>
- return codecs.getreader('utf-8')(self._transport.get(relpath), errors='replace')
- elif mode == 'w':
- raise BzrError("Branch.controlfile(mode='w') is not supported, use put_controlfiles")
- else:
- raise BzrError("invalid controlfile mode %r" % mode)
-
- def put_controlfile(self, path, f, encode=True):
- """See Branch.put_controlfile."""
- self.put_controlfiles([(path, f)], encode=encode)
-
- def put_controlfiles(self, files, encode=True):
- """See Branch.put_controlfiles."""
- import codecs
- ctrl_files = []
- for path, f in files:
- if encode:
- if isinstance(f, basestring):
- f = f.encode('utf-8', 'replace')
- else:
- f = codecs.getwriter('utf-8')(f, errors='replace')
- path = self._rel_controlfilename(path)
- ctrl_files.append((path, f))
- self._transport.put_multi(ctrl_files, mode=self._file_mode)
-
- def _find_modes(self, path=None):
- """Determine the appropriate modes for files and directories."""
- try:
- if path is None:
- path = self._rel_controlfilename('')
- st = self._transport.stat(path)
- except errors.TransportNotPossible:
- self._dir_mode = 0755
- self._file_mode = 0644
- else:
- self._dir_mode = st.st_mode & 07777
- # Remove the sticky and execute bits for files
- self._file_mode = self._dir_mode & ~07111
- if not self._set_dir_mode:
- self._dir_mode = None
- if not self._set_file_mode:
- self._file_mode = None
+ return self.control_files._transport.abspath(name)
def _make_control(self):
from bzrlib.inventory import Inventory
@@ -790,11 +533,6 @@
sio = StringIO()
bzrlib.weavefile.write_weave_v5(Weave(), sio)
empty_weave = sio.getvalue()
-
- cfn = self._rel_controlfilename
- # Since we don't have a .bzr directory, inherit the
- # mode from the root directory
- self._find_modes(u'.')
dirs = ['', 'revision-store', 'weaves']
files = [('README',
@@ -809,9 +547,16 @@
('inventory.weave', empty_weave),
('ancestry.weave', empty_weave)
]
- self._transport.mkdir_multi([cfn(d) for d in dirs], mode=self._dir_mode)
- self.put_controlfiles(files)
- mutter('created control directory in ' + self._transport.base)
+ cfe = self.control_files._escape
+ self.control_files._transport.mkdir_multi([cfe(d) for d in dirs],
+ mode=self.control_files._dir_mode)
+ self.control_files.lock_write()
+ try:
+ for file, content in files:
+ self.control_files.put_utf8(file, content)
+ mutter('created control directory in ' + self.base)
+ finally:
+ self.control_files.unlock()
def _check_format(self, relax_version_check):
"""Check this branch format is supported.
@@ -823,7 +568,7 @@
classes to support downlevel branches. But not yet.
"""
try:
- fmt = self.controlfile('branch-format', 'r').read()
+ fmt = self.control_files.controlfile('branch-format', 'r').read()
except NoSuchFile:
raise NotBranchError(path=self.base)
mutter("got branch format %r", fmt)
@@ -845,27 +590,34 @@
@needs_read_lock
def get_root_id(self):
"""See Branch.get_root_id."""
- inv = self.get_inventory(self.last_revision())
+ inv = self.repository.get_inventory(self.last_revision())
return inv.root.file_id
+
+ def lock_write(self):
+ # TODO: test for failed two phase locks. This is known broken.
+ self.control_files.lock_write()
+ self.repository.lock_write()
+
+ def lock_read(self):
+ # TODO: test for failed two phase locks. This is known broken.
+ self.control_files.lock_read()
+ self.repository.lock_read()
+
+ def unlock(self):
+ # TODO: test for failed two phase locks. This is known broken.
+ self.repository.unlock()
+ self.control_files.unlock()
+
+ def peek_lock_mode(self):
+ if self.control_files._lock_count == 0:
+ return None
+ else:
+ return self.control_files._lock_mode
@needs_read_lock
def print_file(self, file, revision_id):
"""See Branch.print_file."""
- tree = self.revision_tree(revision_id)
- # use inventory as it was in that revision
- file_id = tree.inventory.path2id(file)
- if not file_id:
- try:
- revno = self.revision_id_to_revno(revision_id)
- except errors.NoSuchRevision:
- # TODO: This should not be BzrError,
- # but NoSuchFile doesn't fit either
- raise BzrError('%r is not present in revision %s'
- % (file, revision_id))
- else:
- raise BzrError('%r is not present in revision %s'
- % (file, revno))
- tree.print_file(file_id)
+ return self.repository.print_file(file, revision_id)
@needs_write_lock
def append_revision(self, *revision_ids):
@@ -881,109 +633,47 @@
"""See Branch.set_revision_history."""
old_revision = self.last_revision()
new_revision = rev_history[-1]
- self.put_controlfile('revision-history', '\n'.join(rev_history))
+ self.control_files.put_utf8(
+ 'revision-history', '\n'.join(rev_history))
try:
+ # FIXME: RBC 20051207 this smells wrong, last_revision in the
+ # working tree may be != to last_revision in the branch - so
+ # why is this passing in the branches last_revision ?
self.working_tree().set_last_revision(new_revision, old_revision)
except NoWorkingTree:
mutter('Unable to set_last_revision without a working tree.')
- def has_revision(self, revision_id):
- """See Branch.has_revision."""
- return (revision_id is None
- or self.revision_store.has_id(revision_id))
-
- @needs_read_lock
- def _get_revision_xml_file(self, revision_id):
- if not revision_id or not isinstance(revision_id, basestring):
- raise InvalidRevisionId(revision_id=revision_id, branch=self)
- try:
- return self.revision_store.get(revision_id)
- except (IndexError, KeyError):
- raise bzrlib.errors.NoSuchRevision(self, revision_id)
-
- def get_revision_xml(self, revision_id):
- """See Branch.get_revision_xml."""
- return self._get_revision_xml_file(revision_id).read()
-
- def get_revision(self, revision_id):
- """See Branch.get_revision."""
- xml_file = self._get_revision_xml_file(revision_id)
-
- try:
- r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
- except SyntaxError, e:
- raise bzrlib.errors.BzrError('failed to unpack revision_xml',
- [revision_id,
- str(e)])
-
- assert r.revision_id == revision_id
- return r
-
- def get_revision_sha1(self, revision_id):
- """See Branch.get_revision_sha1."""
- # In the future, revision entries will be signed. At that
- # point, it is probably best *not* to include the signature
- # in the revision hash. Because that lets you re-sign
- # the revision, (add signatures/remove signatures) and still
- # have all hash pointers stay consistent.
- # But for now, just hash the contents.
- return bzrlib.osutils.sha_file(self.get_revision_xml_file(revision_id))
-
- def get_ancestry(self, revision_id):
- """See Branch.get_ancestry."""
- if revision_id is None:
- return [None]
- w = self._get_inventory_weave()
- return [None] + map(w.idx_to_name,
- w.inclusions([w.lookup(revision_id)]))
-
- def _get_inventory_weave(self):
- return self.control_weaves.get_weave('inventory',
- self.get_transaction())
-
- def get_inventory(self, revision_id):
- """See Branch.get_inventory."""
- xml = self.get_inventory_xml(revision_id)
- return bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
-
- def get_inventory_xml(self, revision_id):
- """See Branch.get_inventory_xml."""
- try:
- assert isinstance(revision_id, basestring), type(revision_id)
- iw = self._get_inventory_weave()
- return iw.get_text(iw.lookup(revision_id))
- except IndexError:
- raise bzrlib.errors.HistoryMissing(self, 'inventory', revision_id)
-
- def get_inventory_sha1(self, revision_id):
- """See Branch.get_inventory_sha1."""
- return self.get_revision(revision_id).inventory_sha1
-
- def get_revision_inventory(self, revision_id):
- """See Branch.get_revision_inventory."""
- # TODO: Unify this with get_inventory()
- # bzr 0.0.6 and later imposes the constraint that the inventory_id
- # must be the same as its revision, so this is trivial.
- if revision_id == None:
- # This does not make sense: if there is no revision,
- # then it is the current tree inventory surely ?!
- # and thus get_root_id() is something that looks at the last
- # commit on the branch, and the get_root_id is an inventory check.
- raise NotImplementedError
- # return Inventory(self.get_root_id())
+ def get_revision_delta(self, revno):
+ """Return the delta for one revision.
+
+ The delta is relative to its mainline predecessor, or the
+ empty tree for revision 1.
+ """
+ assert isinstance(revno, int)
+ rh = self.revision_history()
+ if not (1 <= revno <= len(rh)):
+ raise InvalidRevisionNumber(revno)
+
+ # revno is 1-based; list is 0-based
+
+ new_tree = self.repository.revision_tree(rh[revno-1])
+ if revno == 1:
+ old_tree = EmptyTree()
else:
- return self.get_inventory(revision_id)
+ old_tree = self.repository.revision_tree(rh[revno-2])
+ return compare_trees(old_tree, new_tree)
@needs_read_lock
def revision_history(self):
"""See Branch.revision_history."""
+ # FIXME are transactions bound to control files ? RBC 20051121
transaction = self.get_transaction()
history = transaction.map.find_revision_history()
if history is not None:
mutter("cache hit for revision-history in %s", self)
return list(history)
history = [l.rstrip('\r\n') for l in
- self.controlfile('revision-history', 'r').readlines()]
+ self.control_files.controlfile('revision-history', 'r').readlines()]
transaction.map.add_revision_history(history)
# this call is disabled because revision_history is
# not really an object yet, and the transaction is for objects.
@@ -1013,7 +703,8 @@
except DivergedBranches, e:
try:
pullable_revs = get_intervening_revisions(self.last_revision(),
- stop_revision, self)
+ stop_revision,
+ self.repository)
assert self.last_revision() not in pullable_revs
return pullable_revs
except bzrlib.errors.NotAncestor:
@@ -1022,30 +713,24 @@
else:
raise e
- def revision_tree(self, revision_id):
- """See Branch.revision_tree."""
- # TODO: refactor this to use an existing revision object
- # so we don't need to read it in twice.
- if revision_id == None or revision_id == NULL_REVISION:
- return EmptyTree()
- else:
- inv = self.get_revision_inventory(revision_id)
- return RevisionTree(self, inv, revision_id)
-
def basis_tree(self):
"""See Branch.basis_tree."""
try:
revision_id = self.revision_history()[-1]
+ # FIXME: This is an abstraction violation, the basis tree
+ # here as defined is on the working tree, the method should
+ # be too. The basis tree for a branch can be different than
+ # that for a working tree. RBC 20051207
xml = self.working_tree().read_basis_inventory(revision_id)
inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
- return RevisionTree(self, inv, revision_id)
+ return RevisionTree(self.repository, inv, revision_id)
except (IndexError, NoSuchFile, NoWorkingTree), e:
- return self.revision_tree(self.last_revision())
+ return self.repository.revision_tree(self.last_revision())
def working_tree(self):
"""See Branch.working_tree."""
from bzrlib.workingtree import WorkingTree
- if self._transport.base.find('://') != -1:
+ if self.base.find('://') != -1:
raise NoWorkingTree(self.base)
return WorkingTree(self.base, branch=self)
@@ -1073,7 +758,7 @@
_locs = ['parent', 'pull', 'x-pull']
for l in _locs:
try:
- return self.controlfile(l, 'r').read().strip('\n')
+ return self.control_files.controlfile(l, 'r').read().strip('\n')
except NoSuchFile:
pass
return None
@@ -1094,7 +779,7 @@
"""See Branch.set_parent."""
# TODO: Maybe delete old location files?
from bzrlib.atomicfile import AtomicFile
- f = AtomicFile(self.controlfilename('parent'))
+ f = AtomicFile(self.control_files.controlfilename('parent'))
try:
f.write(url + '\n')
f.commit()
@@ -1104,16 +789,52 @@
def tree_config(self):
return TreeConfig(self)
- def sign_revision(self, revision_id, gpg_strategy):
- """See Branch.sign_revision."""
- plaintext = Testament.from_revision(self, revision_id).as_short_text()
- self.store_revision_signature(gpg_strategy, plaintext, revision_id)
-
- @needs_write_lock
- def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
- """See Branch.store_revision_signature."""
- self.revision_store.add(StringIO(gpg_strategy.sign(plaintext)),
- revision_id, "sig")
+ def _get_truncated_history(self, revision_id):
+ history = self.revision_history()
+ if revision_id is None:
+ return history
+ try:
+ idx = history.index(revision_id)
+ except ValueError:
+ raise InvalidRevisionId(revision_id=revision, branch=self)
+ return history[:idx+1]
+
+ @needs_read_lock
+ def _clone_weave(self, to_location, revision=None, basis_branch=None):
+ assert isinstance(to_location, basestring)
+ if basis_branch is not None:
+ note("basis_branch is not supported for fast weave copy yet.")
+
+ history = self._get_truncated_history(revision)
+ if not bzrlib.osutils.lexists(to_location):
+ os.mkdir(to_location)
+ branch_to = Branch.initialize(to_location)
+ mutter("copy branch from %s to %s", self, branch_to)
+ branch_to.working_tree().set_root_id(self.get_root_id())
+
+ self.repository.copy(branch_to.repository)
+
+ # must be done *after* history is copied across
+ # FIXME duplicate code with base .clone().
+ # .. would template method be useful here. RBC 20051207
+ branch_to.set_parent(self.base)
+ branch_to.append_revision(*history)
+ # circular import protection
+ from bzrlib.merge import build_working_dir
+ build_working_dir(to_location)
+ mutter("copied")
+ return branch_to
+
+ def clone(self, to_location, revision=None, basis_branch=None, to_branch_type=None):
+ if to_branch_type is None:
+ to_branch_type = BzrBranch
+
+ if to_branch_type == BzrBranch \
+ and self.repository.weave_store.listable() \
+ and self.repository.revision_store.listable():
+ return self._clone_weave(to_location, revision, basis_branch)
+
+ return Branch.clone(self, to_location, revision, basis_branch, to_branch_type)
class ScratchBranch(BzrBranch):
@@ -1141,16 +862,23 @@
else:
super(ScratchBranch, self).__init__(transport)
+ # BzrBranch creates a clone to .bzr and then forgets about the
+ # original transport. A ScratchTransport() deletes itself and
+ # everything underneath it when it goes away, so we need to
+ # grab a local copy to prevent that from happening
+ self._transport = transport
+
for d in dirs:
self._transport.mkdir(d)
for f in files:
self._transport.put(f, 'content of %s' % f)
-
def clone(self):
"""
>>> orig = ScratchBranch(files=["file1", "file2"])
+ >>> os.listdir(orig.base)
+ [u'.bzr', u'file1', u'file2']
>>> clone = orig.clone()
>>> if os.name != 'nt':
... os.path.samefile(orig.base, clone.base)
@@ -1158,8 +886,8 @@
... orig.base == clone.base
...
False
- >>> os.path.isfile(pathjoin(clone.base, "file1"))
- True
+ >>> os.listdir(clone.base)
+ [u'.bzr', u'file1', u'file2']
"""
from shutil import copytree
from bzrlib.osutils import mkdtemp
=== modified file 'bzrlib/builtins.py'
--- bzrlib/builtins.py
+++ bzrlib/builtins.py
@@ -148,13 +148,13 @@
raise BzrCommandError('You must supply either --revision or a revision_id')
b = WorkingTree.open_containing(u'.')[0].branch
if revision_id is not None:
- sys.stdout.write(b.get_revision_xml(revision_id))
+ sys.stdout.write(b.repository.get_revision_xml(revision_id))
elif revision is not None:
for rev in revision:
if rev is None:
raise BzrCommandError('You cannot specify a NULL revision.')
revno, rev_id = rev.in_history(b)
- sys.stdout.write(b.get_revision_xml(rev_id))
+ sys.stdout.write(b.repository.get_revision_xml(rev_id))
class cmd_revno(Command):
@@ -298,7 +298,7 @@
if len(revision) > 1:
raise BzrCommandError('bzr inventory --revision takes'
' exactly one revision identifier')
- inv = tree.branch.get_revision_inventory(
+ inv = tree.branch.repository.get_revision_inventory(
revision[0].in_history(tree.branch).rev_id)
for path, entry in inv.entries():
@@ -548,7 +548,6 @@
aliases = ['get', 'clone']
def run(self, from_location, to_location=None, revision=None, basis=None):
- from bzrlib.clone import copy_branch
import errno
from shutil import rmtree
if revision is None:
@@ -591,7 +590,7 @@
else:
raise
try:
- copy_branch(br_from, to_location, revision_id, basis_branch)
+ br_from.clone(to_location, revision_id, basis_branch)
except bzrlib.errors.NoSuchRevision:
rmtree(to_location)
msg = "The branch %s has no revision %s." % (from_location, revision[0])
@@ -603,7 +602,12 @@
branch = Branch.open(to_location)
if name:
name = StringIO(name)
- branch.put_controlfile('branch-name', name)
+ branch.lock_write()
+ try:
+ branch.control_files.put_utf8('branch-name', name)
+ finally:
+ branch.unlock()
+
note('Branched %d revision(s).' % branch.revno())
finally:
br_from.unlock()
@@ -927,7 +931,7 @@
if tree is None:
b, fp = Branch.open_containing(filename)
if fp != '':
- inv = b.get_inventory(b.last_revision())
+ inv = b.repository.get_inventory(b.last_revision())
if fp != '':
file_id = inv.path2id(fp)
else:
@@ -1036,7 +1040,7 @@
elif relpath:
relpath += '/'
if revision is not None:
- tree = tree.branch.revision_tree(
+ tree = tree.branch.repository.revision_tree(
revision[0].in_history(tree.branch).rev_id)
for fp, fc, kind, fid, entry in tree.list_files():
if fp.startswith(relpath):
@@ -1194,7 +1198,7 @@
if len(revision) != 1:
raise BzrError('bzr export --revision takes exactly 1 argument')
rev_id = revision[0].in_history(b).rev_id
- t = b.revision_tree(rev_id)
+ t = b.repository.revision_tree(rev_id)
try:
export(t, dest, format, root)
except errors.NoSuchExportFormat, e:
@@ -1651,10 +1655,11 @@
raise BzrCommandError("Sorry, remerge only works after normal"
+ " merges. Not cherrypicking or"
+ "multi-merges.")
+ repository = tree.branch.repository
base_revision = common_ancestor(tree.branch.last_revision(),
- pending_merges[0], tree.branch)
- base_tree = tree.branch.revision_tree(base_revision)
- other_tree = tree.branch.revision_tree(pending_merges[0])
+ pending_merges[0], repository)
+ base_tree = repository.revision_tree(base_revision)
+ other_tree = repository.revision_tree(pending_merges[0])
interesting_ids = None
if file_list is not None:
interesting_ids = set()
@@ -1718,8 +1723,8 @@
else:
tree, file_list = tree_files(file_list)
rev_id = revision[0].in_history(tree.branch).rev_id
- tree.revert(file_list, tree.branch.revision_tree(rev_id),
- not no_backup)
+ tree.revert(file_list, tree.branch.repository.revision_tree(rev_id),
+ not no_backup)
class cmd_assert_fail(Command):
@@ -1821,7 +1826,8 @@
remote_extra.reverse()
if local_extra and not theirs_only:
print "You have %d extra revision(s):" % len(local_extra)
- for data in iter_log_data(local_extra, local_branch, verbose):
+ for data in iter_log_data(local_extra, local_branch.repository,
+ verbose):
lf.show(*data)
printed_local = True
else:
@@ -1830,7 +1836,8 @@
if printed_local is True:
print "\n\n"
print "You are missing %d revision(s):" % len(remote_extra)
- for data in iter_log_data(remote_extra, remote_branch, verbose):
+ for data in iter_log_data(remote_extra, remote_branch.repository,
+ verbose):
lf.show(*data)
if not remote_extra and not local_extra:
status_code = 0
@@ -1876,7 +1883,7 @@
rev_id = b.last_revision()
else:
rev_id = revision[0].in_history(b).rev_id
- t = Testament.from_revision(b, rev_id)
+ t = Testament.from_revision(b.repository, rev_id)
if long:
sys.stdout.writelines(t.as_text_lines())
else:
@@ -1912,7 +1919,7 @@
branch.lock_read()
try:
file_id = tree.inventory.path2id(relpath)
- tree = branch.revision_tree(branch.last_revision())
+ tree = branch.repository.revision_tree(branch.last_revision())
file_version = tree.inventory[file_id].revision
annotate_file(branch, file_version, file_id, long, all, sys.stdout)
finally:
@@ -1937,11 +1944,11 @@
b = WorkingTree.open_containing(u'.')[0].branch
gpg_strategy = gpg.GPGStrategy(config.BranchConfig(b))
if revision_id is not None:
- b.sign_revision(revision_id, gpg_strategy)
+ b.repository.sign_revision(revision_id, gpg_strategy)
elif revision is not None:
if len(revision) == 1:
revno, rev_id = revision[0].in_history(b)
- b.sign_revision(rev_id, gpg_strategy)
+ b.repository.sign_revision(rev_id, gpg_strategy)
elif len(revision) == 2:
# are they both on rh- if so we can walk between them
# might be nice to have a range helper for arbitrary
@@ -1953,7 +1960,8 @@
if from_revno is None or to_revno is None:
raise BzrCommandError('Cannot sign a range of non-revision-history revisions')
for revno in range(from_revno, to_revno + 1):
- b.sign_revision(b.get_rev_id(revno), gpg_strategy)
+ b.repository.sign_revision(b.get_rev_id(revno),
+ gpg_strategy)
else:
raise BzrCommandError('Please supply either one revision, or a range.')
@@ -2001,7 +2009,7 @@
for r in range(revno, b.revno()+1):
rev_id = b.get_rev_id(r)
lf = log_formatter('short', to_file=sys.stdout,show_timezone='original')
- lf.show(r, b.get_revision(rev_id), None)
+ lf.show(r, b.repository.get_revision(rev_id), None)
if dry_run:
print 'Dry-run, pretending to remove the above revisions.'
=== modified file 'bzrlib/check.py'
--- bzrlib/check.py
+++ bzrlib/check.py
@@ -41,6 +41,7 @@
def __init__(self, branch):
self.branch = branch
+ self.repository = branch.repository
self.checked_text_cnt = 0
self.checked_rev_cnt = 0
self.ghosts = []
@@ -59,7 +60,7 @@
self.progress.update('retrieving inventory', 0, 0)
# do not put in init, as it should be done with progess,
# and inside the lock.
- self.inventory_weave = self.branch._get_inventory_weave()
+ self.inventory_weave = self.branch.repository.get_inventory_weave()
self.history = self.branch.revision_history()
if not len(self.history):
# nothing to see here
@@ -78,8 +79,9 @@
self.branch.unlock()
def plan_revisions(self):
- if not self.branch.revision_store.listable():
- self.planned_revisions = self.branch.get_ancestry(self.history[-1])
+ repository = self.branch.repository
+ if not repository.revision_store.listable():
+ self.planned_revisions = repository.get_ancestry(self.history[-1])
self.planned_revisions.remove(None)
# FIXME progress bars should support this more nicely.
self.progress.clear()
@@ -87,7 +89,7 @@
" for a complete check use a local branch.")
return
- self.planned_revisions = set(self.branch.revision_store)
+ self.planned_revisions = set(repository.revision_store)
inventoried = set(self.inventory_weave.names())
awol = self.planned_revisions - inventoried
if len(awol) > 0:
@@ -140,11 +142,11 @@
rev_history_position = None
last_rev_id = None
if rev_history_position:
- rev = branch.get_revision(rev_id)
+ rev = branch.repository.get_revision(rev_id)
if rev_history_position > 0:
last_rev_id = self.history[rev_history_position - 1]
else:
- rev = branch.get_revision(rev_id)
+ rev = branch.repository.get_revision(rev_id)
if rev.revision_id != rev_id:
raise BzrCheckError('wrong internal revision id in revision {%s}'
@@ -168,7 +170,7 @@
# list based so somewhat slow,
# TODO have a planned_revisions list and set.
if self.branch.has_revision(parent):
- missing_ancestry = self.branch.get_ancestry(parent)
+ missing_ancestry = self.repository.get_ancestry(parent)
for missing in missing_ancestry:
if (missing is not None
and missing not in self.planned_revisions):
@@ -181,7 +183,7 @@
% (rev_id, last_rev_id))
if rev.inventory_sha1:
- inv_sha1 = branch.get_inventory_sha1(rev_id)
+ inv_sha1 = branch.repository.get_inventory_sha1(rev_id)
if inv_sha1 != rev.inventory_sha1:
raise BzrCheckError('Inventory sha1 hash doesn\'t match'
' value in revision {%s}' % rev_id)
@@ -196,21 +198,21 @@
"""
n_weaves = 1
weave_ids = []
- if self.branch.weave_store.listable():
- weave_ids = list(self.branch.weave_store)
+ if self.branch.repository.weave_store.listable():
+ weave_ids = list(self.branch.repository.weave_store)
n_weaves = len(weave_ids)
self.progress.update('checking weave', 0, n_weaves)
self.inventory_weave.check(progress_bar=self.progress)
for i, weave_id in enumerate(weave_ids):
self.progress.update('checking weave', i, n_weaves)
- w = self.branch.weave_store.get_weave(weave_id,
- self.branch.get_transaction())
+ w = self.branch.repository.weave_store.get_weave(weave_id,
+ self.branch.repository.get_transaction())
# No progress here, because it looks ugly.
w.check()
self.checked_weaves[weave_id] = True
def _check_revision_tree(self, rev_id):
- tree = self.branch.revision_tree(rev_id)
+ tree = self.branch.repository.revision_tree(rev_id)
inv = tree.inventory
seen_ids = {}
for file_id in inv:
=== modified file 'bzrlib/commit.py'
--- bzrlib/commit.py
+++ bzrlib/commit.py
@@ -202,7 +202,7 @@
mutter('preparing to commit')
self.branch = branch
- self.weave_store = branch.weave_store
+ self.weave_store = branch.repository.weave_store
self.rev_id = rev_id
self.specific_files = specific_files
self.allow_pointless = allow_pointless
@@ -290,7 +290,7 @@
"""Store the inventory for the new revision."""
inv_text = serializer_v5.write_inventory_to_string(self.new_inv)
self.inv_sha1 = sha_string(inv_text)
- s = self.branch.control_weaves
+ s = self.branch.repository.control_weaves
s.add_text('inventory', self.rev_id,
split_lines(inv_text), self.present_parents,
self.branch.get_transaction())
@@ -319,14 +319,15 @@
self.parents.append(precursor_id)
self.parents += pending_merges
for revision in self.parents:
- if self.branch.has_revision(revision):
- self.parent_invs.append(self.branch.get_inventory(revision))
+ if self.branch.repository.has_revision(revision):
+ inventory = self.branch.repository.get_inventory(revision)
+ self.parent_invs.append(inventory)
self.present_parents.append(revision)
def _check_parents_present(self):
for parent_id in self.parents:
mutter('commit parent revision {%s}', parent_id)
- if not self.branch.has_revision(parent_id):
+ if not self.branch.repository.has_revision(parent_id):
if parent_id == self.branch.last_revision():
warning("parent is missing %r", parent_id)
raise HistoryMissing(self.branch, 'revision', parent_id)
@@ -348,9 +349,9 @@
rev_tmp.seek(0)
if self.config.signature_needed():
plaintext = Testament(self.rev, self.new_inv).as_short_text()
- self.branch.store_revision_signature(gpg.GPGStrategy(self.config),
- plaintext, self.rev_id)
- self.branch.revision_store.add(rev_tmp, self.rev_id)
+ self.branch.repository.store_revision_signature(
+ gpg.GPGStrategy(self.config), plaintext, self.rev_id)
+ self.branch.repository.revision_store.add(rev_tmp, self.rev_id)
mutter('new revision_id is {%s}', self.rev_id)
def _remove_deleted(self):
=== modified file 'bzrlib/config.py'
--- bzrlib/config.py
+++ bzrlib/config.py
@@ -379,7 +379,7 @@
This is looked up in the email controlfile for the branch.
"""
try:
- return (self.branch.controlfile("email", "r")
+ return (self.branch.control_files.controlfile("email", "r")
.read()
.decode(bzrlib.user_encoding)
.rstrip("\r\n"))
@@ -520,7 +520,7 @@
def _get_config(self):
try:
- obj = ConfigObj(self.branch.controlfile('branch.conf',
+ obj = ConfigObj(self.branch.control_files.controlfile('branch.conf',
'rb').readlines())
obj.decode('UTF-8')
except errors.NoSuchFile:
@@ -558,6 +558,6 @@
cfg_obj.encode('UTF-8')
out_file = StringIO(''.join([l+'\n' for l in cfg_obj.write()]))
out_file.seek(0)
- self.branch.put_controlfile('branch.conf', out_file, encode=False)
+ self.branch.control_files.put('branch.conf', out_file)
finally:
self.branch.unlock()
=== modified file 'bzrlib/diff.py'
--- bzrlib/diff.py
+++ bzrlib/diff.py
@@ -164,7 +164,7 @@
else:
old_tree = b.working_tree()
else:
- old_tree = b.revision_tree(from_spec.in_history(b).rev_id)
+ old_tree = b.repository.revision_tree(from_spec.in_history(b).rev_id)
if revision2 is None:
if b2 is None:
@@ -172,7 +172,7 @@
else:
new_tree = b2.working_tree()
else:
- new_tree = b.revision_tree(revision2.in_history(b).rev_id)
+ new_tree = b.repository.revision_tree(revision2.in_history(b).rev_id)
return show_diff_trees(old_tree, new_tree, output, specific_files,
external_diff_options)
=== modified file 'bzrlib/fetch.py'
--- bzrlib/fetch.py
+++ bzrlib/fetch.py
@@ -91,11 +91,13 @@
if to_branch == from_branch:
raise Exception("can't fetch from a branch to itself")
self.to_branch = to_branch
- self.to_weaves = to_branch.weave_store
- self.to_control = to_branch.control_weaves
+ self.to_repository = to_branch.repository
+ self.to_weaves = self.to_repository.weave_store
+ self.to_control = self.to_repository.control_weaves
self.from_branch = from_branch
- self.from_weaves = from_branch.weave_store
- self.from_control = from_branch.control_weaves
+ self.from_repository = from_branch.repository
+ self.from_weaves = self.from_repository.weave_store
+ self.from_control = self.from_repository.control_weaves
self.failed_revisions = []
self.count_copied = 0
self.count_total = 0
@@ -117,7 +119,7 @@
self.last_revision = self._find_last_revision(last_revision)
mutter('fetch up to rev {%s}', self.last_revision)
if (self.last_revision is not None and
- self.to_branch.has_revision(self.last_revision)):
+ self.to_repository.has_revision(self.last_revision)):
return
try:
revs_to_fetch = self._compare_ancestries()
@@ -150,12 +152,14 @@
That is, every revision that's in the ancestry of the source
branch and not in the destination branch."""
self.pb.update('get source ancestry')
- self.from_ancestry = self.from_branch.get_ancestry(self.last_revision)
+ from_repository = self.from_branch.repository
+ self.from_ancestry = from_repository.get_ancestry(self.last_revision)
dest_last_rev = self.to_branch.last_revision()
self.pb.update('get destination ancestry')
if dest_last_rev:
- dest_ancestry = self.to_branch.get_ancestry(dest_last_rev)
+ to_repository = self.to_branch.repository
+ dest_ancestry = to_repository.get_ancestry(dest_last_rev)
else:
dest_ancestry = []
ss = set(dest_ancestry)
@@ -174,7 +178,7 @@
i += 1
if rev_id is None:
continue
- if self.to_branch.has_revision(rev_id):
+ if self.to_repository.has_revision(rev_id):
continue
self.pb.update('copy revision', i, self.count_total)
self._copy_one_revision(rev_id)
@@ -184,8 +188,8 @@
def _copy_one_revision(self, rev_id):
"""Copy revision and everything referenced by it."""
mutter('copying revision {%s}', rev_id)
- rev_xml = self.from_branch.get_revision_xml(rev_id)
- inv_xml = self.from_branch.get_inventory_xml(rev_id)
+ rev_xml = self.from_repository.get_revision_xml(rev_id)
+ inv_xml = self.from_repository.get_inventory_xml(rev_id)
rev = serializer_v5.read_revision_from_string(rev_xml)
inv = serializer_v5.read_inventory_from_string(inv_xml)
assert rev.revision_id == rev_id
@@ -197,16 +201,16 @@
parents = rev.parent_ids
new_parents = copy(parents)
for parent in parents:
- if not self.to_branch.has_revision(parent):
+ if not self.to_repository.has_revision(parent):
new_parents.pop(new_parents.index(parent))
self._copy_inventory(rev_id, inv_xml, new_parents)
- self.to_branch.revision_store.add(StringIO(rev_xml), rev_id)
+ self.to_repository.revision_store.add(StringIO(rev_xml), rev_id)
mutter('copied revision %s', rev_id)
def _copy_inventory(self, rev_id, inv_xml, parent_ids):
self.to_control.add_text('inventory', rev_id,
split_lines(inv_xml), parent_ids,
- self.to_branch.get_transaction())
+ self.to_repository.get_transaction())
def _copy_new_texts(self, rev_id, inv):
"""Copy any new texts occuring in this revision."""
@@ -223,13 +227,13 @@
text_revision in self.file_ids_names[file_id]:
return
to_weave = self.to_weaves.get_weave_or_empty(file_id,
- self.to_branch.get_transaction())
+ self.to_repository.get_transaction())
if not file_id in self.file_ids_names.keys( ):
self.file_ids_names[file_id] = to_weave.names( )
if text_revision in to_weave:
return
from_weave = self.from_weaves.get_weave(file_id,
- self.from_branch.get_transaction())
+ self.from_branch.repository.get_transaction())
if text_revision not in from_weave:
raise MissingText(self.from_branch, text_revision, file_id)
mutter('copy file {%s} modified in {%s}', file_id, rev_id)
@@ -244,7 +248,7 @@
# destination is empty, just replace it
to_weave = from_weave.copy( )
self.to_weaves.put_weave(file_id, to_weave,
- self.to_branch.get_transaction())
+ self.to_repository.get_transaction())
self.count_weaves += 1
self.copied_file_ids.add(file_id)
self.file_ids_names[file_id] = to_weave.names()
=== modified file 'bzrlib/info.py'
--- bzrlib/info.py
+++ bzrlib/info.py
@@ -33,7 +33,8 @@
def show_info(b):
import diff
- print 'branch format:', b.controlfile('branch-format', 'r').readline().rstrip('\n')
+ print 'branch format:', b.control_files.controlfile(
+ 'branch-format', 'r').readline().rstrip('\n')
def plural(n, base='', pl=None):
if n == 1:
@@ -83,16 +84,16 @@
print ' %8d revision%s' % (revno, plural(revno))
committers = {}
for rev in history:
- committers[b.get_revision(rev).committer] = True
+ committers[b.repository.get_revision(rev).committer] = True
print ' %8d committer%s' % (len(committers), plural(len(committers)))
if revno > 0:
- firstrev = b.get_revision(history[0])
+ firstrev = b.repository.get_revision(history[0])
age = int((time.time() - firstrev.timestamp) / 3600 / 24)
print ' %8d day%s old' % (age, plural(age))
print ' first revision: %s' % format_date(firstrev.timestamp,
firstrev.timezone)
- lastrev = b.get_revision(history[-1])
+ lastrev = b.repository.get_revision(history[-1])
print ' latest revision: %s' % format_date(lastrev.timestamp,
lastrev.timezone)
@@ -104,7 +105,7 @@
print
print 'revision store:'
- c, t = b.revision_store.total_size()
+ c, t = b.repository.revision_store.total_size()
print ' %8d revisions' % c
print ' %8d kB' % (t/1024)
=== modified file 'bzrlib/log.py'
--- bzrlib/log.py
+++ bzrlib/log.py
@@ -74,7 +74,7 @@
last_path = None
revno = 1
for revision_id in branch.revision_history():
- this_inv = branch.get_revision_inventory(revision_id)
+ this_inv = branch.repository.get_revision_inventory(revision_id)
if file_id in this_inv:
this_ie = this_inv[file_id]
this_path = this_inv.id2path(file_id)
@@ -223,7 +223,7 @@
# although we calculated it, throw it away without display
delta = None
- rev = branch.get_revision(rev_id)
+ rev = branch.repository.get_revision(rev_id)
if searchRE:
if not searchRE.search(rev.message):
@@ -235,7 +235,9 @@
excludes = set()
else:
# revno is 1 based, so -2 to get back 1 less.
- excludes = set(branch.get_ancestry(revision_history[revno - 2]))
+ repository = branch.repository
+ excludes = repository.get_ancestry(revision_history[revno - 2])
+ excludes = set(excludes)
pending = list(rev.parent_ids)
while pending:
rev_id = pending.pop()
@@ -244,7 +246,7 @@
# prevent showing merged revs twice if they multi-path.
excludes.add(rev_id)
try:
- rev = branch.get_revision(rev_id)
+ rev = branch.repository.get_revision(rev_id)
except errors.NoSuchRevision:
continue
pending.extend(rev.parent_ids)
@@ -510,7 +512,7 @@
to_file.write('*'*60)
to_file.write('\nRemoved Revisions:\n')
for i in range(base_idx, len(old_rh)):
- rev = branch.get_revision(old_rh[i])
+ rev = branch.repository.get_revision(old_rh[i])
lf.show(i+1, rev, None)
to_file.write('*'*60)
to_file.write('\n\n')
=== modified file 'bzrlib/merge.py'
--- bzrlib/merge.py
+++ bzrlib/merge.py
@@ -245,9 +245,9 @@
else:
if local_branch is not None:
greedy_fetch(local_branch, branch, revision)
- base_tree = local_branch.revision_tree(revision)
+ base_tree = local_branch.repository.revision_tree(revision)
else:
- base_tree = branch.revision_tree(revision)
+ base_tree = branch.repository.revision_tree(revision)
return base_tree
@@ -389,11 +389,11 @@
other_tree)
def revision_tree(self, revision_id):
- return self.this_branch.revision_tree(revision_id)
+ return self.this_branch.repository.revision_tree(revision_id)
def ensure_revision_trees(self):
if self.this_revision_tree is None:
- self.this_basis_tree = self.this_branch.revision_tree(
+ self.this_basis_tree = self.this_branch.repository.revision_tree(
self.this_basis)
if self.this_basis == self.this_rev_id:
self.this_revision_tree = self.this_basis_tree
@@ -489,7 +489,8 @@
return
if self.other_rev_id is None:
return
- if self.other_rev_id in self.this_branch.get_ancestry(self.this_basis):
+ ancestry = self.this_branch.repository.get_ancestry(self.this_basis)
+ if self.other_rev_id in ancestry:
return
self.this_branch.working_tree().add_pending_merge(self.other_rev_id)
@@ -518,7 +519,7 @@
try:
self.base_rev_id = common_ancestor(self.this_basis,
self.other_basis,
- self.this_branch)
+ self.this_branch.repository)
except NoCommonAncestor:
raise UnrelatedBranches()
self.base_tree = get_revid_tree(self.this_branch, self.base_rev_id,
=== modified file 'bzrlib/missing.py'
--- bzrlib/missing.py
+++ bzrlib/missing.py
@@ -10,6 +10,7 @@
for revno, rev_id in revisions:
rev = revision_source.get_revision(rev_id)
if verbose:
+ remote_tree = revision_source.revision_tree(rev_id)
parent_rev_id = rev.parent_ids[0]
if last_rev_id == parent_rev_id:
parent_tree = last_tree
@@ -43,10 +44,10 @@
remote_rev_history_map)
return local_extra, remote_extra
- local_ancestry = _get_ancestry(local_branch, progress, "local",
- 2, local_rev_history)
- remote_ancestry = _get_ancestry(remote_branch, progress, "remote",
- 3, remote_rev_history)
+ local_ancestry = _get_ancestry(local_branch.repository, progress,
+ "local", 2, local_rev_history)
+ remote_ancestry = _get_ancestry(remote_branch.repository, progress,
+ "remote", 3, remote_rev_history)
progress.update('pondering', 4, 5)
extras = local_ancestry.symmetric_difference(remote_ancestry)
local_extra = extras.intersection(set(local_rev_history))
@@ -87,10 +88,10 @@
for rev in rev_history])
return rev_history, rev_history_map
-def _get_ancestry(branch, progress, label, step, rev_history):
+def _get_ancestry(repository, progress, label, step, rev_history):
progress.update('%s ancestry' % label, step, 5)
if len(rev_history) > 0:
- ancestry = set(branch.get_ancestry(rev_history[-1]))
+ ancestry = set(repository.get_ancestry(rev_history[-1]))
else:
ancestry = set()
return ancestry
=== modified file 'bzrlib/osutils.py'
--- bzrlib/osutils.py
+++ bzrlib/osutils.py
@@ -336,6 +336,14 @@
tofile.write(b)
+def file_iterator(input_file, readsize=32768):
+ while True:
+ b = input_file.read(readsize)
+ if len(b) == 0:
+ break
+ yield b
+
+
def sha_file(f):
if hasattr(f, 'tell'):
assert f.tell() == 0
=== modified file 'bzrlib/revision.py'
--- bzrlib/revision.py
+++ bzrlib/revision.py
@@ -88,7 +88,7 @@
revisions_source is an object supporting a get_revision operation that
behaves like Branch's.
"""
- return candidate_id in branch.get_ancestry(revision_id)
+ return candidate_id in branch.repository.get_ancestry(revision_id)
def iter_ancestors(revision_id, revision_source, only_present=False):
=== modified file 'bzrlib/revisionspec.py'
--- bzrlib/revisionspec.py
+++ bzrlib/revisionspec.py
@@ -57,7 +57,7 @@
# TODO: otherwise, it should depend on how I was built -
# if it's in_history(branch), then check revision_history(),
# if it's in_store(branch), do the check below
- return self.branch.revision_store.has_id(self.rev_id)
+ return self.branch.repository.has_revision(self.rev_id)
def __len__(self):
return 2
@@ -68,7 +68,7 @@
raise IndexError(index)
def get(self):
- return self.branch.get_revision(self.rev_id)
+ return self.branch.repository.get_revision(self.rev_id)
def __eq__(self, other):
if type(other) not in (tuple, list, type(self)):
@@ -297,7 +297,7 @@
hour=hour, minute=minute, second=second)
first = dt
for i in range(len(revs)):
- r = branch.get_revision(revs[i])
+ r = branch.repository.get_revision(revs[i])
# TODO: Handle timezone.
dt = datetime.datetime.fromtimestamp(r.timestamp)
if first <= dt:
@@ -319,7 +319,8 @@
for r, b in ((revision_a, branch), (revision_b, other_branch)):
if r is None:
raise NoCommits(b)
- revision_source = MultipleRevisionSources(branch, other_branch)
+ revision_source = MultipleRevisionSources(branch.repository,
+ other_branch.repository)
rev_id = common_ancestor(revision_a, revision_b, revision_source)
try:
revno = branch.revision_id_to_revno(rev_id)
=== modified file 'bzrlib/status.py'
--- bzrlib/status.py
+++ bzrlib/status.py
@@ -71,13 +71,13 @@
elif len(revision) > 0:
try:
rev_id = revision[0].in_history(branch).rev_id
- old = branch.revision_tree(rev_id)
+ old = branch.repository.revision_tree(rev_id)
except NoSuchRevision, e:
raise BzrCommandError(str(e))
if len(revision) > 1:
try:
rev_id = revision[1].in_history(branch).rev_id
- new = branch.revision_tree(rev_id)
+ new = branch.repository.revision_tree(rev_id)
new_is_working_tree = False
except NoSuchRevision, e:
raise BzrCommandError(str(e))
@@ -109,20 +109,20 @@
print >>to_file, 'pending merges:'
last_revision = branch.last_revision()
if last_revision is not None:
- ignore = set(branch.get_ancestry(last_revision))
+ ignore = set(branch.repository.get_ancestry(last_revision))
else:
ignore = set()
for merge in new.pending_merges():
ignore.add(merge)
try:
- m_revision = branch.get_revision(merge)
+ m_revision = branch.repository.get_revision(merge)
print >> to_file, ' ', line_log(m_revision, 77)
- inner_merges = branch.get_ancestry(merge)
+ inner_merges = branch.repository.get_ancestry(merge)
inner_merges.reverse()
for mmerge in inner_merges:
if mmerge in ignore:
continue
- mm_revision = branch.get_revision(mmerge)
+ mm_revision = branch.repository.get_revision(mmerge)
print >> to_file, ' ', line_log(mm_revision, 75)
ignore.add(mmerge)
except NoSuchRevision:
=== modified file 'bzrlib/store/__init__.py'
--- bzrlib/store/__init__.py
+++ bzrlib/store/__init__.py
@@ -181,6 +181,7 @@
else:
fn = self._relpath(fileid)
+ # FIXME RBC 20051128 this belongs in TextStore.
fn_gz = fn + '.gz'
if self._compressed:
return fn_gz, fn
@@ -226,6 +227,7 @@
super(TransportStore, self).__init__()
self._transport = a_transport
self._prefixed = prefixed
+ # FIXME RBC 20051128 this belongs in TextStore.
self._compressed = compressed
self._suffixes = set()
=== modified file 'bzrlib/testament.py'
--- bzrlib/testament.py
+++ bzrlib/testament.py
@@ -88,10 +88,10 @@
"""
@classmethod
- def from_revision(cls, branch, revision_id):
+ def from_revision(cls, repository, revision_id):
"""Produce a new testament from a historical revision"""
- rev = branch.get_revision(revision_id)
- inventory = branch.get_inventory(revision_id)
+ rev = repository.get_revision(revision_id)
+ inventory = repository.get_inventory(revision_id)
return cls(rev, inventory)
def __init__(self, rev, inventory):
=== modified file 'bzrlib/tests/__init__.py'
--- bzrlib/tests/__init__.py
+++ bzrlib/tests/__init__.py
@@ -39,6 +39,7 @@
import bzrlib.commands
from bzrlib.errors import BzrError
import bzrlib.inventory
+import bzrlib.iterablefile
import bzrlib.merge3
import bzrlib.osutils
import bzrlib.osutils as osutils
@@ -56,6 +57,7 @@
bzrlib.commands,
bzrlib.errors,
bzrlib.inventory,
+ bzrlib.iterablefile,
bzrlib.merge3,
bzrlib.osutils,
bzrlib.store,
@@ -290,6 +292,10 @@
if len(missing) > 0:
raise AssertionError("value(s) %r not present in container %r" %
(missing, superlist))
+
+ def assertIs(self, left, right):
+ if not (left is right):
+ raise AssertionError("%r is not %r." % (left, right))
def assertTransportMode(self, transport, path, mode):
"""Fail if a path does not have mode mode.
@@ -693,6 +699,7 @@
'bzrlib.tests.test_http',
'bzrlib.tests.test_identitymap',
'bzrlib.tests.test_inv',
+ 'bzrlib.tests.test_lockable_files',
'bzrlib.tests.test_log',
'bzrlib.tests.test_merge',
'bzrlib.tests.test_merge3',
=== modified file 'bzrlib/tests/blackbox/test_revision_info.py'
--- bzrlib/tests/blackbox/test_revision_info.py
+++ bzrlib/tests/blackbox/test_revision_info.py
@@ -92,9 +92,9 @@
b.working_tree().commit('Commit three', rev_id='a at r-0-3')
revs = {
- 1:b.get_revision_xml('a at r-0-1'),
- 2:b.get_revision_xml('a at r-0-2'),
- 3:b.get_revision_xml('a at r-0-3')
+ 1:b.repository.get_revision_xml('a at r-0-1'),
+ 2:b.repository.get_revision_xml('a at r-0-2'),
+ 3:b.repository.get_revision_xml('a at r-0-3'),
}
self.check_output(revs[1], 'cat-revision', 'a at r-0-1')
=== modified file 'bzrlib/tests/blackbox/test_too_much.py'
--- bzrlib/tests/blackbox/test_too_much.py
+++ bzrlib/tests/blackbox/test_too_much.py
@@ -41,7 +41,6 @@
import sys
from bzrlib.branch import Branch
-from bzrlib.clone import copy_branch
from bzrlib.errors import BzrCommandError
from bzrlib.osutils import has_symlinks, pathjoin
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
@@ -364,7 +363,7 @@
branch = Branch.initialize('branch1')
branch.working_tree().add(['file'])
branch.working_tree().commit('add file')
- copy_branch(branch, 'branch2')
+ branch.clone('branch2')
print >> open('branch2/file', 'wb'), 'new content'
branch2 = Branch.open('branch2')
branch2.working_tree().commit('update file')
@@ -439,7 +438,7 @@
# Merging a branch pulls its revision into the tree
a = Branch.open('.')
b = Branch.open('../b')
- a.get_revision_xml(b.last_revision())
+ a.repository.get_revision_xml(b.last_revision())
self.log('pending merges: %s', a.working_tree().pending_merges())
self.assertEquals(a.working_tree().pending_merges(),
[b.last_revision()])
@@ -598,6 +597,13 @@
'subdir/b\n'
, '--versioned')
+ def test_cat(self):
+ self.runbzr('init')
+ file("myfile", "wb").write("My contents\n")
+ self.runbzr('add')
+ self.runbzr('commit -m myfile')
+ self.run_bzr_captured('cat -r 1 myfile'.split(' '))
+
def test_pull_verbose(self):
"""Pull changes from one branch to another and watch the output."""
@@ -825,6 +831,21 @@
self.runbzr('commit -m done',)
self.runbzr('remerge', retcode=3)
+ def test_status(self):
+ os.mkdir('branch1')
+ os.chdir('branch1')
+ self.runbzr('init')
+ self.runbzr('commit --unchanged --message f')
+ self.runbzr('branch . ../branch2')
+ self.runbzr('branch . ../branch3')
+ self.runbzr('commit --unchanged --message peter')
+ os.chdir('../branch2')
+ self.runbzr('merge ../branch1')
+ self.runbzr('commit --unchanged --message pumpkin')
+ os.chdir('../branch3')
+ self.runbzr('merge ../branch2')
+ message = self.capture('status')
+
def test_conflicts(self):
"""Handling of merge conflicts"""
@@ -866,8 +887,10 @@
from bzrlib.testament import Testament
bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
self.runbzr('re-sign -r revid:A')
- self.assertEqual(Testament.from_revision(branch,'A').as_short_text(),
- branch.revision_store.get('A', 'sig').read())
+ self.assertEqual(Testament.from_revision(branch.repository,
+ 'A').as_short_text(),
+ branch.repository.revision_store.get('A',
+ 'sig').read())
finally:
bzrlib.gpg.GPGStrategy = oldstrategy
@@ -883,12 +906,16 @@
from bzrlib.testament import Testament
bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
self.runbzr('re-sign -r 1..')
- self.assertEqual(Testament.from_revision(branch,'A').as_short_text(),
- branch.revision_store.get('A', 'sig').read())
- self.assertEqual(Testament.from_revision(branch,'B').as_short_text(),
- branch.revision_store.get('B', 'sig').read())
- self.assertEqual(Testament.from_revision(branch,'C').as_short_text(),
- branch.revision_store.get('C', 'sig').read())
+ self.assertEqual(
+ Testament.from_revision(branch.repository,'A').as_short_text(),
+ branch.repository.revision_store.get('A', 'sig').read())
+ self.assertEqual(
+ Testament.from_revision(branch.repository,'B').as_short_text(),
+ branch.repository.revision_store.get('B', 'sig').read())
+ self.assertEqual(Testament.from_revision(branch.repository,
+ 'C').as_short_text(),
+ branch.repository.revision_store.get('C',
+ 'sig').read())
finally:
bzrlib.gpg.GPGStrategy = oldstrategy
@@ -1287,6 +1314,14 @@
url = self.get_remote_url('branch/file')
output = self.capture('log %s' % url)
self.assertEqual(8, len(output.split('\n')))
+ # FIXME: rbc 20051128 what is the remainder of this test testing?
+ # - it does not seem to be http specific.
+ copy = branch.clone('branch2')
+ branch.working_tree().commit(message='empty commit')
+ os.chdir('branch2')
+ self.run_bzr('merge', '../branch')
+ copy.working_tree().commit(message='merge')
+ output = self.capture('log')
def test_check(self):
self.build_tree(['branch/', 'branch/file'])
=== modified file 'bzrlib/tests/blackbox/test_versioning.py'
--- bzrlib/tests/blackbox/test_versioning.py
+++ bzrlib/tests/blackbox/test_versioning.py
@@ -171,8 +171,8 @@
mutter('start selective subdir commit')
run_bzr('commit', 'a', '-m', 'commit a only')
- old = b.revision_tree(b.get_rev_id(1))
- new = b.revision_tree(b.get_rev_id(2))
+ old = b.repository.revision_tree(b.get_rev_id(1))
+ new = b.repository.revision_tree(b.get_rev_id(2))
eq(new.get_file_by_path('b/two').read(), 'old contents')
eq(new.get_file_by_path('top').read(), 'old contents')
@@ -181,14 +181,14 @@
os.chdir('a')
# commit from here should do nothing
run_bzr('commit', '.', '-m', 'commit subdir only', '--unchanged')
- v3 = b.revision_tree(b.get_rev_id(3))
+ v3 = b.repository.revision_tree(b.get_rev_id(3))
eq(v3.get_file_by_path('b/two').read(), 'old contents')
eq(v3.get_file_by_path('top').read(), 'old contents')
eq(v3.get_file_by_path('a/one').read(), 'new contents')
# commit in subdirectory commits whole tree
run_bzr('commit', '-m', 'commit whole tree from subdir')
- v4 = b.revision_tree(b.get_rev_id(4))
+ v4 = b.repository.revision_tree(b.get_rev_id(4))
eq(v4.get_file_by_path('b/two').read(), 'new contents')
eq(v4.get_file_by_path('top').read(), 'new contents')
=== modified file 'bzrlib/tests/test_ancestry.py'
--- bzrlib/tests/test_ancestry.py
+++ bzrlib/tests/test_ancestry.py
@@ -39,10 +39,10 @@
allow_pointless=True,
rev_id='tester at foo--2')
- ancs = b.get_ancestry('tester at foo--2')
+ ancs = b.repository.get_ancestry('tester at foo--2')
self.assertEqual([None, 'tester at foo--1', 'tester at foo--2'], ancs)
self.assertEqual([None, 'tester at foo--1'],
- b.get_ancestry('tester at foo--1'))
+ b.repository.get_ancestry('tester at foo--1'))
def test_none_is_always_an_ancestor(self):
b = Branch.initialize(u'.')
=== modified file 'bzrlib/tests/test_annotate.py'
--- bzrlib/tests/test_annotate.py
+++ bzrlib/tests/test_annotate.py
@@ -31,7 +31,6 @@
import os
from bzrlib.branch import Branch
-from bzrlib.clone import copy_branch
from bzrlib.errors import BzrCommandError
from bzrlib.osutils import has_symlinks
from bzrlib.tests import TestCaseInTempDir, BzrTestBase
=== modified file 'bzrlib/tests/test_basis_inventory.py'
--- bzrlib/tests/test_basis_inventory.py
+++ bzrlib/tests/test_basis_inventory.py
@@ -25,7 +25,7 @@
def test_create(self):
# Make sure the basis file is created by a commit
- b = Branch.initialize(u'.')
+ b = Branch.initialize('.')
t = b.working_tree()
open('a', 'wb').write('a\n')
t.add('a')
@@ -37,7 +37,7 @@
basis_inv = serializer_v5.read_inventory_from_string(basis_inv_txt)
#self.assertEquals('r1', basis_inv.revision_id)
- store_inv = b.get_inventory('r1')
+ store_inv = b.repository.get_inventory('r1')
self.assertEquals(store_inv._byid, basis_inv._byid)
open('b', 'wb').write('b\n')
@@ -49,7 +49,7 @@
basis_inv_txt = t.read_basis_inventory('r2')
basis_inv = serializer_v5.read_inventory_from_string(basis_inv_txt)
- store_inv = b.get_inventory('r2')
+ store_inv = b.repository.get_inventory('r2')
self.assertEquals(store_inv._byid, basis_inv._byid)
=== modified file 'bzrlib/tests/test_branch.py'
--- bzrlib/tests/test_branch.py
+++ bzrlib/tests/test_branch.py
@@ -18,7 +18,6 @@
import sys
from bzrlib.branch import Branch, needs_read_lock, needs_write_lock
-from bzrlib.clone import copy_branch
from bzrlib.commit import commit
import bzrlib.errors as errors
from bzrlib.errors import NoSuchRevision, UnlistableBranch, NotBranchError
@@ -61,17 +60,17 @@
eq(f.count_copied, 1)
eq(f.last_revision, 'revision-1')
- rev = b2.get_revision('revision-1')
- tree = b2.revision_tree('revision-1')
+ rev = b2.repository.get_revision('revision-1')
+ tree = b2.repository.revision_tree('revision-1')
eq(tree.get_file_text('foo-id'), 'hello')
def test_revision_tree(self):
b1 = Branch.initialize(u'.')
b1.working_tree().commit('lala!', rev_id='revision-1', allow_pointless=True)
- tree = b1.revision_tree('revision-1')
- tree = b1.revision_tree(None)
+ tree = b1.repository.revision_tree('revision-1')
+ tree = b1.repository.revision_tree(None)
self.assertEqual(len(tree.list_files()), 0)
- tree = b1.revision_tree(NULL_REVISION)
+ tree = b1.repository.revision_tree(NULL_REVISION)
self.assertEqual(len(tree.list_files()), 0)
def get_unbalanced_branch_pair(self):
@@ -95,26 +94,26 @@
"""Copy the stores from one branch to another"""
br_a, br_b = self.get_unbalanced_branch_pair()
# ensure the revision is missing.
- self.assertRaises(NoSuchRevision, br_b.get_revision,
+ self.assertRaises(NoSuchRevision, br_b.repository.get_revision,
br_a.revision_history()[0])
br_a.push_stores(br_b)
# check that b now has all the data from a's first commit.
- rev = br_b.get_revision(br_a.revision_history()[0])
- tree = br_b.revision_tree(br_a.revision_history()[0])
+ rev = br_b.repository.get_revision(br_a.revision_history()[0])
+ tree = br_b.repository.revision_tree(br_a.revision_history()[0])
for file_id in tree:
if tree.inventory[file_id].kind == "file":
tree.get_file(file_id).read()
return br_a, br_b
- def test_copy_branch(self):
+ def test_clone_branch(self):
"""Copy the stores from one branch to another"""
br_a, br_b = self.get_balanced_branch_pair()
commit(br_b, "silly commit")
os.mkdir('c')
- br_c = copy_branch(br_a, 'c', basis_branch=br_b)
+ br_c = br_a.clone('c', basis_branch=br_b)
self.assertEqual(br_a.revision_history(), br_c.revision_history())
- def test_copy_partial(self):
+ def test_clone_partial(self):
"""Copy only part of the history of a branch."""
self.build_tree(['a/', 'a/one'])
br_a = Branch.initialize('a')
@@ -123,7 +122,7 @@
self.build_tree(['a/two'])
br_a.working_tree().add(['two'])
br_a.working_tree().commit('commit two', rev_id='u at d-2')
- br_b = copy_branch(br_a, 'b', revision='u at d-1')
+ br_b = br_a.clone('b', revision='u at d-1')
self.assertEqual(br_b.last_revision(), 'u at d-1')
self.assertTrue(os.path.exists('b/one'))
self.assertFalse(os.path.exists('b/two'))
@@ -133,15 +132,16 @@
branch = Branch.initialize(u'.')
branch.working_tree().add_pending_merge('non:existent at rev--ision--0--2')
branch.working_tree().commit('pretend to merge nonexistent-revision', rev_id='first')
- rev = branch.get_revision(branch.last_revision())
+ rev = branch.repository.get_revision(branch.last_revision())
self.assertEqual(len(rev.parent_ids), 1)
# parent_sha1s is not populated now, WTF. rbc 20051003
self.assertEqual(len(rev.parent_sha1s), 0)
self.assertEqual(rev.parent_ids[0], 'non:existent at rev--ision--0--2')
def test_bad_revision(self):
- branch = Branch.initialize(u'.')
- self.assertRaises(errors.InvalidRevisionId, branch.get_revision, None)
+ branch = Branch.initialize('.')
+ self.assertRaises(errors.InvalidRevisionId,
+ branch.repository.get_revision, None)
# TODO 20051003 RBC:
# compare the gpg-to-sign info for a commit with a ghost and
@@ -162,7 +162,7 @@
['foo at azkhazan-123123-abcabc',
'wibble at fofof--20050401--1928390812'])
b.working_tree().commit("commit from base with two merges")
- rev = b.get_revision(b.revision_history()[0])
+ rev = b.repository.get_revision(b.revision_history()[0])
self.assertEquals(len(rev.parent_ids), 2)
self.assertEquals(rev.parent_ids[0],
'foo at azkhazan-123123-abcabc')
@@ -175,23 +175,20 @@
branch = Branch.initialize(u'.')
branch.working_tree().commit("base", allow_pointless=True, rev_id='A')
from bzrlib.testament import Testament
- branch.sign_revision('A', bzrlib.gpg.LoopbackGPGStrategy(None))
- self.assertEqual(Testament.from_revision(branch, 'A').as_short_text(),
- branch.revision_store.get('A', 'sig').read())
+ strategy = bzrlib.gpg.LoopbackGPGStrategy(None)
+ branch.repository.sign_revision('A', strategy)
+ self.assertEqual(Testament.from_revision(branch.repository,
+ 'A').as_short_text(),
+ branch.repository.revision_store.get('A',
+ 'sig').read())
def test_store_signature(self):
- branch = Branch.initialize(u'.')
- branch.store_revision_signature(bzrlib.gpg.LoopbackGPGStrategy(None),
- 'FOO', 'A')
- self.assertEqual('FOO', branch.revision_store.get('A', 'sig').read())
-
- def test__relcontrolfilename(self):
- branch = Branch.initialize(u'.')
- self.assertEqual('.bzr/%25', branch._rel_controlfilename('%'))
-
- def test__relcontrolfilename_empty(self):
- branch = Branch.initialize(u'.')
- self.assertEqual('.bzr', branch._rel_controlfilename(''))
+ branch = Branch.initialize('.')
+ branch.repository.store_revision_signature(
+ bzrlib.gpg.LoopbackGPGStrategy(None), 'FOO', 'A')
+ self.assertEqual('FOO',
+ branch.repository.revision_store.get('A',
+ 'sig').read())
def test_nicks(self):
"""Branch nicknames"""
@@ -203,7 +200,7 @@
self.assertEqual(branch.nick, 'bzr.ab')
branch.nick = "Aaron's branch"
branch.nick = "Aaron's branch"
- self.failUnless(os.path.exists(branch.controlfilename("branch.conf")))
+ self.failUnlessExists(branch.control_files.controlfilename("branch.conf"))
self.assertEqual(branch.nick, "Aaron's branch")
os.rename('bzr.ab', 'integration')
branch = Branch.open('integration')
@@ -217,7 +214,7 @@
branch = Branch.initialize('bzr.dev')
branch.nick = "My happy branch"
branch.working_tree().commit('My commit respect da nick.')
- committed = branch.get_revision(branch.last_revision())
+ committed = branch.repository.get_revision(branch.last_revision())
self.assertEqual(committed.properties["branch-nick"],
"My happy branch")
@@ -321,7 +318,7 @@
def setUp(self):
super(TestBranchTransaction, self).setUp()
- self.branch = Branch.initialize(u'.')
+ self.branch = Branch.initialize('.')
def test_default_get_transaction(self):
"""branch.get_transaction on a new branch should give a PassThrough."""
@@ -343,12 +340,12 @@
def test_finish_readonly_transaction_works(self):
self.branch._set_transaction(transactions.ReadOnlyTransaction())
self.branch._finish_transaction()
- self.assertEqual(None, self.branch._transaction)
+ self.assertEqual(None, self.branch.control_files._transaction)
def test_unlock_calls_finish(self):
self.branch.lock_read()
transaction = InstrumentedTransaction()
- self.branch._transaction = transaction
+ self.branch.control_files._transaction = transaction
self.branch.unlock()
self.assertEqual(['finish'], transaction.calls)
@@ -361,7 +358,7 @@
def test_lock_write_acquires_passthrough_transaction(self):
self.branch.lock_write()
# cannot use get_transaction as its magic
- self.failUnless(isinstance(self.branch._transaction,
+ self.failUnless(isinstance(self.branch.control_files._transaction,
transactions.PassThroughTransaction))
self.branch.unlock()
=== modified file 'bzrlib/tests/test_commit.py'
--- bzrlib/tests/test_commit.py
+++ bzrlib/tests/test_commit.py
@@ -59,14 +59,14 @@
eq = self.assertEquals
eq(b.revno(), 2)
rh = b.revision_history()
- rev = b.get_revision(rh[0])
+ rev = b.repository.get_revision(rh[0])
eq(rev.message, 'add hello')
- tree1 = b.revision_tree(rh[0])
+ tree1 = b.repository.revision_tree(rh[0])
text = tree1.get_file_text(file_id)
eq(text, 'hello world')
- tree2 = b.revision_tree(rh[1])
+ tree2 = b.repository.revision_tree(rh[1])
eq(tree2.get_file_text(file_id), 'version 2')
def test_delete_commit(self):
@@ -79,7 +79,7 @@
os.remove('hello')
b.working_tree().commit('removed hello', rev_id='rev2')
- tree = b.revision_tree('rev2')
+ tree = b.repository.revision_tree('rev2')
self.assertFalse(tree.has_id('hello-id'))
def test_pointless_commit(self):
@@ -132,12 +132,12 @@
eq = self.assertEquals
eq(b.revno(), 3)
- tree2 = b.revision_tree('test at rev-2')
+ tree2 = b.repository.revision_tree('test at rev-2')
self.assertTrue(tree2.has_filename('hello'))
self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
- tree3 = b.revision_tree('test at rev-3')
+ tree3 = b.repository.revision_tree('test at rev-3')
self.assertFalse(tree3.has_filename('hello'))
self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
@@ -154,7 +154,7 @@
tree.commit(message='renamed', rev_id='test at rev-2', allow_pointless=False)
eq = self.assertEquals
- tree1 = b.revision_tree('test at rev-1')
+ tree1 = b.repository.revision_tree('test at rev-1')
eq(tree1.id2path('hello-id'), 'hello')
eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
self.assertFalse(tree1.has_filename('fruity'))
@@ -162,7 +162,7 @@
ie = tree1.inventory['hello-id']
eq(ie.revision, 'test at rev-1')
- tree2 = b.revision_tree('test at rev-2')
+ tree2 = b.repository.revision_tree('test at rev-2')
eq(tree2.id2path('hello-id'), 'fruity')
eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
self.check_inventory_shape(tree2.inventory, ['fruity'])
@@ -198,7 +198,7 @@
b.working_tree().commit('three', rev_id=r3, allow_pointless=False)
self.check_inventory_shape(b.working_tree().read_working_inventory(),
['a', 'a/hello', 'a/b'])
- self.check_inventory_shape(b.get_revision_inventory(r3),
+ self.check_inventory_shape(b.repository.get_revision_inventory(r3),
['a', 'a/hello', 'a/b'])
b.working_tree().move(['a/hello'], 'a/b')
@@ -207,7 +207,7 @@
self.check_inventory_shape(b.working_tree().read_working_inventory(),
['a', 'a/b/hello', 'a/b'])
- inv = b.get_revision_inventory(r4)
+ inv = b.repository.get_revision_inventory(r4)
eq(inv['hello-id'].revision, r4)
eq(inv['a-id'].revision, r1)
eq(inv['b-id'].revision, r3)
@@ -224,7 +224,7 @@
wt.remove('hello')
b.working_tree().commit('removed hello', rev_id='rev2')
- tree = b.revision_tree('rev2')
+ tree = b.repository.revision_tree('rev2')
self.assertFalse(tree.has_id('hello-id'))
@@ -243,7 +243,7 @@
eq = self.assertEquals
eq(b.revision_history(), rev_ids)
for i in range(4):
- anc = b.get_ancestry(rev_ids[i])
+ anc = b.repository.get_ancestry(rev_ids[i])
eq(anc, [None] + rev_ids[:i+1])
def test_commit_new_subdir_child_selective(self):
@@ -252,7 +252,7 @@
b.working_tree().add(['dir', 'dir/file1', 'dir/file2'],
['dirid', 'file1id', 'file2id'])
b.working_tree().commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
- inv = b.get_inventory('1')
+ inv = b.repository.get_inventory('1')
self.assertEqual('1', inv['dirid'].revision)
self.assertEqual('1', inv['file1id'].revision)
# FIXME: This should raise a KeyError I think, rbc20051006
@@ -299,7 +299,7 @@
oldstrategy = bzrlib.gpg.GPGStrategy
branch = Branch.initialize(u'.')
branch.working_tree().commit("base", allow_pointless=True, rev_id='A')
- self.failIf(branch.revision_store.has_id('A', 'sig'))
+ self.failIf(branch.repository.revision_store.has_id('A', 'sig'))
try:
from bzrlib.testament import Testament
# monkey patch gpg signing mechanism
@@ -307,8 +307,10 @@
commit.Commit(config=MustSignConfig(branch)).commit(branch, "base",
allow_pointless=True,
rev_id='B')
- self.assertEqual(Testament.from_revision(branch,'B').as_short_text(),
- branch.revision_store.get('B', 'sig').read())
+ self.assertEqual(Testament.from_revision(branch.repository,
+ 'B').as_short_text(),
+ branch.repository.revision_store.get('B',
+ 'sig').read())
finally:
bzrlib.gpg.GPGStrategy = oldstrategy
@@ -318,7 +320,7 @@
oldstrategy = bzrlib.gpg.GPGStrategy
branch = Branch.initialize(u'.')
branch.working_tree().commit("base", allow_pointless=True, rev_id='A')
- self.failIf(branch.revision_store.has_id('A', 'sig'))
+ self.failIf(branch.repository.revision_store.has_id('A', 'sig'))
try:
from bzrlib.testament import Testament
# monkey patch gpg signing mechanism
@@ -331,7 +333,7 @@
rev_id='B')
branch = Branch.open(u'.')
self.assertEqual(branch.revision_history(), ['A'])
- self.failIf(branch.revision_store.has_id('B'))
+ self.failIf(branch.repository.revision_store.has_id('B'))
finally:
bzrlib.gpg.GPGStrategy = oldstrategy
=== modified file 'bzrlib/tests/test_commit_merge.py'
--- bzrlib/tests/test_commit_merge.py
+++ bzrlib/tests/test_commit_merge.py
@@ -58,7 +58,7 @@
self.assertEquals(by.revno(), 2)
self.assertEquals(list(by.revision_history()),
['y at u-0-1', 'y at u-0-2'])
- rev = by.get_revision('y at u-0-2')
+ rev = by.repository.get_revision('y at u-0-2')
self.assertEquals(rev.parent_ids,
['y at u-0-1', 'x at u-0-1'])
@@ -89,7 +89,7 @@
specific_files=['ecks'])
commit(by, 'merge from x', rev_id='y at u-0-2', allow_pointless=False)
- tree = by.revision_tree('y at u-0-2')
+ tree = by.repository.revision_tree('y at u-0-2')
inv = tree.inventory
self.assertEquals(inv['ecks-id'].revision, 'x at u-0-1')
self.assertEquals(inv['why-id'].revision, 'y at u-0-1')
=== modified file 'bzrlib/tests/test_config.py'
--- bzrlib/tests/test_config.py
+++ bzrlib/tests/test_config.py
@@ -95,6 +95,12 @@
def __init__(self):
self.base = "http://example.com/branches/demo"
+ self.control_files = FakeControlFiles()
+
+
+class FakeControlFiles(object):
+
+ def __init__(self):
self.email = 'Robert Collins <robertc at example.net>\n'
def controlfile(self, filename, mode):
@@ -541,19 +547,19 @@
my_config = config.BranchConfig(branch)
self.assertEqual("Robert Collins <robertc at example.net>",
my_config._get_user_id())
- branch.email = "John"
+ branch.control_files.email = "John"
self.assertEqual("John", my_config._get_user_id())
def test_not_set_in_branch(self):
branch = FakeBranch()
my_config = config.BranchConfig(branch)
- branch.email = None
+ branch.control_files.email = None
config_file = StringIO(sample_config_text)
(my_config._get_location_config().
_get_global_config()._get_parser(config_file))
self.assertEqual("Robert Collins <robertc at example.com>",
my_config._get_user_id())
- branch.email = "John"
+ branch.control_files.email = "John"
self.assertEqual("John", my_config._get_user_id())
def test_BZREMAIL_OVERRIDES(self):
=== modified file 'bzrlib/tests/test_fetch.py'
--- bzrlib/tests/test_fetch.py
+++ bzrlib/tests/test_fetch.py
@@ -23,7 +23,6 @@
from bzrlib.branch import Branch
from bzrlib.fetch import greedy_fetch
from bzrlib.merge import merge
-from bzrlib.clone import copy_branch
from bzrlib.tests import TestCaseInTempDir
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
@@ -31,7 +30,7 @@
def has_revision(branch, revision_id):
try:
- branch.get_revision_xml(revision_id)
+ branch.repository.get_revision_xml_file(revision_id)
return True
except bzrlib.errors.NoSuchRevision:
return False
@@ -128,7 +127,7 @@
os.mkdir('br1')
br1 = Branch.initialize('br1')
br1.working_tree().commit(message='rev 1-1', rev_id='1-1')
- copy_branch(br1, 'br2')
+ br1.clone('br2')
br2 = Branch.open('br2')
br1.working_tree().commit(message='rev 1-2', rev_id='1-2')
br2.working_tree().commit(message='rev 2-1', rev_id='2-1')
@@ -138,10 +137,10 @@
def _check_revs_present(self, br2):
for rev_id in '1-1', '1-2', '2-1':
- self.assertTrue(br2.has_revision(rev_id))
- rev = br2.get_revision(rev_id)
+ self.assertTrue(br2.repository.has_revision(rev_id))
+ rev = br2.repository.get_revision(rev_id)
self.assertEqual(rev.revision_id, rev_id)
- self.assertTrue(br2.get_inventory(rev_id))
+ self.assertTrue(br2.repository.get_inventory(rev_id))
@@ -153,7 +152,7 @@
self.build_tree_contents([('br1/file', 'original contents\n')])
br1.working_tree().add(['file'], ['this-file-id'])
br1.working_tree().commit(message='rev 1-1', rev_id='1-1')
- copy_branch(br1, 'br2')
+ br1.clone('br2')
br2 = Branch.open('br2')
self.build_tree_contents([('br1/file', 'original from 1\n')])
br1.working_tree().commit(message='rev 1-2', rev_id='1-2')
@@ -173,8 +172,9 @@
('1-3', 'agreement\n'),
('2-1', 'contents in 2\n'),
('2-2', 'agreement\n')]:
- self.assertEqualDiff(br2.revision_tree(rev_id).get_file_text('this-file-id'),
- text)
+ self.assertEqualDiff(
+ br2.repository.revision_tree(
+ rev_id).get_file_text('this-file-id'), text)
@@ -184,7 +184,7 @@
def test_fetch(self):
#highest indices a: 5, b: 7
br_a, br_b = make_branches(self)
- br_rem_a = Branch.open(self.get_remote_url(br_a._transport.base))
+ br_rem_a = Branch.open(self.get_remote_url(br_a.base))
fetch_steps(self, br_rem_a, br_b, br_a)
def test_weaves_are_retrieved_once(self):
=== modified file 'bzrlib/tests/test_inv.py'
--- bzrlib/tests/test_inv.py
+++ bzrlib/tests/test_inv.py
@@ -18,7 +18,6 @@
import os
from bzrlib.branch import Branch
-from bzrlib.clone import copy_branch
import bzrlib.errors as errors
from bzrlib.diff import internal_diff
from bzrlib.inventory import Inventory, ROOT_ID
@@ -149,8 +148,8 @@
if has_symlinks():
os.unlink('symlink')
os.symlink('target2', 'symlink')
- self.tree_1 = self.branch.revision_tree('1')
- self.inv_1 = self.branch.get_inventory('1')
+ self.tree_1 = self.branch.repository.revision_tree('1')
+ self.inv_1 = self.branch.repository.get_inventory('1')
self.file_1 = self.inv_1['fileid']
self.tree_2 = self.branch.working_tree()
self.inv_2 = self.tree_2.read_working_inventory()
@@ -248,8 +247,8 @@
pass
self.wt = self.branch.working_tree()
self.wt.commit('message_1', rev_id = '1')
- self.tree_1 = self.branch.revision_tree('1')
- self.inv_1 = self.branch.get_inventory('1')
+ self.tree_1 = self.branch.repository.revision_tree('1')
+ self.inv_1 = self.branch.repository.get_inventory('1')
self.file_1 = self.inv_1['fileid']
self.work_tree = self.branch.working_tree()
self.file_active = self.work_tree.inventory['fileid']
@@ -258,14 +257,14 @@
# This tests that a simple commit with no parents makes a new
# revision value in the inventory entry
self.file_active.snapshot('2', 'subdir/file', {}, self.work_tree,
- self.branch.weave_store,
+ self.branch.repository.weave_store,
self.branch.get_transaction())
# expected outcome - file_1 has a revision id of '2', and we can get
# its text of 'file contents' out of the weave.
self.assertEqual(self.file_1.revision, '1')
self.assertEqual(self.file_active.revision, '2')
# this should be a separate test probably, but lets check it once..
- lines = self.branch.weave_store.get_lines('fileid','2',
+ lines = self.branch.repository.weave_store.get_lines('fileid','2',
self.branch.get_transaction())
self.assertEqual(lines, ['contents of subdir/file\n'])
@@ -273,13 +272,14 @@
#This tests that a simple commit does not make a new entry for
# an unchanged inventory entry
self.file_active.snapshot('2', 'subdir/file', {'1':self.file_1},
- self.work_tree, self.branch.weave_store,
+ self.work_tree,
+ self.branch.repository.weave_store,
self.branch.get_transaction())
self.assertEqual(self.file_1.revision, '1')
self.assertEqual(self.file_active.revision, '1')
self.assertRaises(errors.WeaveError,
- self.branch.weave_store.get_lines, 'fileid', '2',
- self.branch.get_transaction())
+ self.branch.repository.weave_store.get_lines,
+ 'fileid', '2', self.branch.get_transaction())
def test_snapshot_merge_identical_different_revid(self):
# This tests that a commit with two identical parents, one of which has
@@ -293,11 +293,12 @@
self.assertEqual(self.file_1, other_ie)
other_ie.revision = 'other'
self.assertNotEqual(self.file_1, other_ie)
- self.branch.weave_store.add_identical_text('fileid', '1', 'other', ['1'],
- self.branch.get_transaction())
+ self.branch.repository.weave_store.add_identical_text('fileid', '1',
+ 'other', ['1'], self.branch.get_transaction())
self.file_active.snapshot('2', 'subdir/file',
{'1':self.file_1, 'other':other_ie},
- self.work_tree, self.branch.weave_store,
+ self.work_tree,
+ self.branch.repository.weave_store,
self.branch.get_transaction())
self.assertEqual(self.file_active.revision, '2')
@@ -308,7 +309,7 @@
rename('subdir/file', 'subdir/newname')
self.file_active.snapshot('2', 'subdir/newname', {'1':self.file_1},
self.work_tree,
- self.branch.weave_store,
+ self.branch.repository.weave_store,
self.branch.get_transaction())
# expected outcome - file_1 has a revision id of '2'
self.assertEqual(self.file_active.revision, '2')
@@ -330,19 +331,23 @@
self.branch = Branch.initialize(u'.')
self.wt = self.branch.working_tree()
self.wt.commit('new branch', allow_pointless=True, rev_id='A')
- self.inv_A = self.branch.get_inventory('A')
- self.branch.working_tree().add(['file'], ['fileid'])
+ self.inv_A = self.branch.repository.get_inventory('A')
+ self.wt.add(['file'], ['fileid'])
self.wt.commit('add file', rev_id='B')
- self.inv_B = self.branch.get_inventory('B')
- self.branch.put_controlfile('revision-history', 'A\n')
+ self.inv_B = self.branch.repository.get_inventory('B')
+ self.branch.lock_write()
+ try:
+ self.branch.control_files.put_utf8('revision-history', 'A\n')
+ finally:
+ self.branch.unlock()
self.assertEqual(self.branch.revision_history(), ['A'])
self.wt.commit('another add of file', rev_id='C')
- self.inv_C = self.branch.get_inventory('C')
+ self.inv_C = self.branch.repository.get_inventory('C')
self.wt.add_pending_merge('B')
self.wt.commit('merge in B', rev_id='D')
- self.inv_D = self.branch.get_inventory('D')
- self.file_active = self.wt.inventory['fileid']
- self.weave = self.branch.weave_store.get_weave('fileid',
+ self.inv_D = self.branch.repository.get_inventory('D')
+ self.file_active = self.branch.working_tree().inventory['fileid']
+ self.weave = self.branch.repository.weave_store.get_weave('fileid',
self.branch.get_transaction())
def get_previous_heads(self, inventories):
@@ -404,7 +409,7 @@
t.commit('adding a,b', rev_id='r1')
- rev_tree = b.revision_tree('r1')
+ rev_tree = b.repository.revision_tree('r1')
self.failUnless(rev_tree.is_executable(a_id), "'a' lost the execute bit")
self.failIf(rev_tree.is_executable(b_id), "'b' gained an execute bit")
@@ -458,8 +463,7 @@
# Now make sure that 'bzr branch' also preserves the
# executable bit
# TODO: Maybe this should be a blackbox test
- from bzrlib.clone import copy_branch
- copy_branch(b, 'b2', revision='r1')
+ b.clone('b2', revision='r1')
b2 = Branch.open('b2')
self.assertEquals('r1', b2.last_revision())
t2 = b2.working_tree()
=== modified file 'bzrlib/tests/test_merge.py'
--- bzrlib/tests/test_merge.py
+++ bzrlib/tests/test_merge.py
@@ -52,7 +52,7 @@
br1, br2 = self.test_pending_with_null()
commit(br1, "blah")
last = br1.last_revision()
- self.assertEquals(common_ancestor(last, last, br1), last)
+ self.assertEquals(common_ancestor(last, last, br1.repository), last)
def test_create_rename(self):
"""Rename an inventory entry while creating the file"""
=== modified file 'bzrlib/tests/test_merge_core.py'
--- bzrlib/tests/test_merge_core.py
+++ bzrlib/tests/test_merge_core.py
@@ -15,7 +15,6 @@
BackupBeforeChange, ExecFlagMerge, WeaveMerge)
from bzrlib.changeset import Inventory, apply_changeset, invert_dict, \
get_contents, ReplaceContents, ChangeExecFlag
-from bzrlib.clone import copy_branch
from bzrlib.merge import merge
from bzrlib.workingtree import WorkingTree
@@ -537,7 +536,6 @@
def test_trivial_star_merge(self):
"""Test that merges in a star shape Just Work."""
from bzrlib.add import smart_add_tree
- from bzrlib.clone import copy_branch
from bzrlib.merge import merge
# John starts a branch
self.build_tree(("original/", "original/file1", "original/file2"))
@@ -547,7 +545,7 @@
tree.commit("start branch.", verbose=False)
# Mary branches it.
self.build_tree(("mary/",))
- copy_branch(branch, "mary")
+ branch.clone("mary")
# Now John commits a change
file = open("original/file1", "wt")
file.write("John\n")
@@ -577,7 +575,7 @@
file('a/file', 'wb').write('contents\n')
a.working_tree().add('file')
a.working_tree().commit('base revision', allow_pointless=False)
- b = copy_branch(a, 'b')
+ b = a.clone('b')
file('a/file', 'wb').write('other contents\n')
a.working_tree().commit('other revision', allow_pointless=False)
file('b/file', 'wb').write('this contents contents\n')
@@ -659,7 +657,7 @@
a_wt = a.working_tree()
a_wt.add('file')
a_wt.commit('r0')
- copy_branch(a, 'b')
+ a.clone('b')
b = Branch.open('b')
b_wt = b.working_tree()
os.chmod('b/file', 0755)
@@ -680,12 +678,12 @@
a_wt.add('un')
a_wt.add('deux')
a_wt.commit('r0')
- copy_branch(a,'b')
+ a.clone('b')
b = Branch.open('b')
b_wt = b.working_tree()
- b_wt.rename_one('un','tmp')
- b_wt.rename_one('deux','un')
- b_wt.rename_one('tmp','deux')
+ b_wt.rename_one('un', 'tmp')
+ b_wt.rename_one('deux', 'un')
+ b_wt.rename_one('tmp', 'deux')
b_wt.commit('r1')
merge(['b', -1],['b', 1],this_dir='a')
self.assert_(os.path.exists('a/un'))
@@ -701,7 +699,7 @@
a_wt = a.working_tree()
a_wt.add('file')
a_wt.commit('r0')
- copy_branch(a, 'b')
+ a.clone('b')
b = Branch.open('b')
b_wt = b.working_tree()
os.remove('b/file')
@@ -733,7 +731,7 @@
a_wt = a.working_tree()
a_wt.add('foo')
a_wt.commit('added foo')
- copy_branch(a, 'b')
+ a.clone('b')
b = Branch.open('b')
b_wt = b.working_tree()
b_wt.rename_one('foo', 'bar')
@@ -763,7 +761,7 @@
a_wt = a.working_tree()
a_wt.add('foo')
a_wt.commit('added foo')
- copy_branch(a, 'b')
+ a.clone('b')
b = Branch.open('b')
b_wt = b.working_tree()
os.mkdir('b/bar')
@@ -795,7 +793,7 @@
a_wt.add('foo')
a_wt.add('foo/bar')
a_wt.commit('added foo/bar')
- copy_branch(a, 'b')
+ a.clone('b')
b = Branch.open('b')
b_wt = b.working_tree()
b_wt.rename_one('foo/bar', 'bar')
@@ -827,7 +825,7 @@
a_wt.add('foo')
a_wt.add('bar')
a_wt.commit('added foo and bar')
- copy_branch(a, 'b')
+ a.clone('b')
b = Branch.open('b')
b_wt = b.working_tree()
os.unlink('b/foo')
=== modified file 'bzrlib/tests/test_parent.py'
--- bzrlib/tests/test_parent.py
+++ bzrlib/tests/test_parent.py
@@ -16,9 +16,8 @@
import os
+from bzrlib.branch import Branch
from bzrlib.tests import TestCaseInTempDir
-from bzrlib.branch import Branch
-from bzrlib.clone import copy_branch
from bzrlib.osutils import abspath, realpath
@@ -26,12 +25,12 @@
class TestParent(TestCaseInTempDir):
+
def test_no_default_parent(self):
"""Branches should have no parent by default"""
b = Branch.initialize(u'.')
self.assertEquals(b.get_parent(), None)
-
def test_set_get_parent(self):
"""Set and then re-get the parent"""
b = Branch.initialize(u'.')
@@ -50,7 +49,7 @@
branch_from.working_tree().commit('initial commit')
os.mkdir('to')
- copy_branch(branch_from, 'to', None)
+ branch_from.clone('to', None)
branch_to = Branch.open('to')
self.assertEquals(branch_to.get_parent(), branch_from.base)
=== modified file 'bzrlib/tests/test_permissions.py'
--- bzrlib/tests/test_permissions.py
+++ bzrlib/tests/test_permissions.py
@@ -23,19 +23,23 @@
In the future, when we have Repository/Branch/Checkout information, the
permissions should be inherited individually, rather than all be the same.
-
-TODO: jam 20051215 There are no tests for ftp yet, because we have no ftp server
-TODO: jam 20051215 Currently the default behavior for 'bzr branch' is just
- defined by the local umask. This isn't terrible, is it
- the truly desired behavior?
"""
+# TODO: jam 20051215 There are no tests for ftp yet, because we have no ftp server
+# TODO: jam 20051215 Currently the default behavior for 'bzr branch' is just
+# defined by the local umask. This isn't terrible, is it
+# the truly desired behavior?
+
import os
import sys
import stat
+from StringIO import StringIO
from bzrlib.branch import Branch
+from bzrlib.lockable_files import LockableFiles
from bzrlib.tests import TestCaseInTempDir, TestSkipped
+from bzrlib.transport import get_transport
+
from bzrlib.tests.test_sftp_transport import TestCaseWithSFTPServer
from bzrlib.transport import get_transport
@@ -99,8 +103,8 @@
b = Branch.open('.')
t = b.working_tree()
- assertEqualMode(self, 0755, b._dir_mode)
- assertEqualMode(self, 0644, b._file_mode)
+ assertEqualMode(self, 0755, b.control_files._dir_mode)
+ assertEqualMode(self, 0644, b.control_files._file_mode)
# Modifying a file shouldn't break the permissions
open('a', 'wb').write('foo2\n')
@@ -120,8 +124,8 @@
check_mode_r(self, '.bzr', 0664, 0775)
b = Branch.open('.')
t = b.working_tree()
- assertEqualMode(self, 0775, b._dir_mode)
- assertEqualMode(self, 0664, b._file_mode)
+ assertEqualMode(self, 0775, b.control_files._dir_mode)
+ assertEqualMode(self, 0664, b.control_files._file_mode)
open('a', 'wb').write('foo3\n')
t.commit('foo3')
@@ -139,8 +143,8 @@
check_mode_r(self, '.bzr', 0664, 02775)
b = Branch.open('.')
t = b.working_tree()
- assertEqualMode(self, 02775, b._dir_mode)
- assertEqualMode(self, 0664, b._file_mode)
+ assertEqualMode(self, 02775, b.control_files._dir_mode)
+ assertEqualMode(self, 0664, b.control_files._file_mode)
open('a', 'wb').write('foo4\n')
t.commit('foo4')
@@ -154,64 +158,71 @@
def test_disable_set_mode(self):
# TODO: jam 20051215 Ultimately, this test should probably test that
# extra chmod calls aren't being made
- import bzrlib.branch
try:
- b = Branch.initialize(u'.')
- self.assertNotEqual(None, b._dir_mode)
- self.assertNotEqual(None, b._file_mode)
-
- bzrlib.branch.BzrBranch._set_dir_mode = False
- b = Branch.open(u'.')
- self.assertEqual(None, b._dir_mode)
- self.assertNotEqual(None, b._file_mode)
-
- bzrlib.branch.BzrBranch._set_file_mode = False
- b = Branch.open(u'.')
- self.assertEqual(None, b._dir_mode)
- self.assertEqual(None, b._file_mode)
-
- bzrlib.branch.BzrBranch._set_dir_mode = True
- b = Branch.open(u'.')
- self.assertNotEqual(None, b._dir_mode)
- self.assertEqual(None, b._file_mode)
-
- bzrlib.branch.BzrBranch._set_file_mode = True
- b = Branch.open(u'.')
- self.assertNotEqual(None, b._dir_mode)
- self.assertNotEqual(None, b._file_mode)
+ transport = get_transport('.')
+ transport.put('my-lock', StringIO(''))
+ lockable = LockableFiles(transport, 'my-lock')
+ self.assertNotEqual(None, lockable._dir_mode)
+ self.assertNotEqual(None, lockable._file_mode)
+
+ LockableFiles._set_dir_mode = False
+ transport = get_transport('.')
+ lockable = LockableFiles(transport, 'my-lock')
+ self.assertEqual(None, lockable._dir_mode)
+ self.assertNotEqual(None, lockable._file_mode)
+
+ LockableFiles._set_file_mode = False
+ transport = get_transport('.')
+ lockable = LockableFiles(transport, 'my-lock')
+ self.assertEqual(None, lockable._dir_mode)
+ self.assertEqual(None, lockable._file_mode)
+
+ LockableFiles._set_dir_mode = True
+ transport = get_transport('.')
+ lockable = LockableFiles(transport, 'my-lock')
+ self.assertNotEqual(None, lockable._dir_mode)
+ self.assertEqual(None, lockable._file_mode)
+
+ LockableFiles._set_file_mode = True
+ transport = get_transport('.')
+ lockable = LockableFiles(transport, 'my-lock')
+ self.assertNotEqual(None, lockable._dir_mode)
+ self.assertNotEqual(None, lockable._file_mode)
finally:
- bzrlib.branch.BzrBranch._set_dir_mode = True
- bzrlib.branch.BzrBranch._set_file_mode = True
+ LockableFiles._set_dir_mode = True
+ LockableFiles._set_file_mode = True
def test_new_branch(self):
if sys.platform == 'win32':
raise TestSkipped('chmod has no effect on win32')
-
+ #FIXME RBC 20060105 should test branch and repository
+ # permissions ?
+ # also, these are BzrBranch format specific things..
os.mkdir('a')
mode = stat.S_IMODE(os.stat('a').st_mode)
b = Branch.initialize('a')
- assertEqualMode(self, mode, b._dir_mode)
- assertEqualMode(self, mode & ~07111, b._file_mode)
+ assertEqualMode(self, mode, b.control_files._dir_mode)
+ assertEqualMode(self, mode & ~07111, b.control_files._file_mode)
os.mkdir('b')
os.chmod('b', 02777)
b = Branch.initialize('b')
- assertEqualMode(self, 02777, b._dir_mode)
- assertEqualMode(self, 00666, b._file_mode)
+ assertEqualMode(self, 02777, b.control_files._dir_mode)
+ assertEqualMode(self, 00666, b.control_files._file_mode)
check_mode_r(self, 'b/.bzr', 00666, 02777)
os.mkdir('c')
os.chmod('c', 02750)
b = Branch.initialize('c')
- assertEqualMode(self, 02750, b._dir_mode)
- assertEqualMode(self, 00640, b._file_mode)
+ assertEqualMode(self, 02750, b.control_files._dir_mode)
+ assertEqualMode(self, 00640, b.control_files._file_mode)
check_mode_r(self, 'c/.bzr', 00640, 02750)
os.mkdir('d')
os.chmod('d', 0700)
b = Branch.initialize('d')
- assertEqualMode(self, 0700, b._dir_mode)
- assertEqualMode(self, 0600, b._file_mode)
+ assertEqualMode(self, 0700, b.control_files._dir_mode)
+ assertEqualMode(self, 0600, b.control_files._file_mode)
check_mode_r(self, 'd/.bzr', 00600, 0700)
@@ -244,8 +255,8 @@
b_local = Branch.open(u'local')
t_local = b_local.working_tree()
- assertEqualMode(self, 0755, b_local._dir_mode)
- assertEqualMode(self, 0644, b_local._file_mode)
+ assertEqualMode(self, 0755, b_local.control_files._dir_mode)
+ assertEqualMode(self, 0644, b_local.control_files._file_mode)
os.mkdir('sftp')
sftp_url = self.get_remote_url('sftp')
@@ -257,8 +268,8 @@
check_mode_r(self, 'sftp/.bzr', 0644, 0755)
b_sftp = Branch.open(sftp_url)
- assertEqualMode(self, 0755, b_sftp._dir_mode)
- assertEqualMode(self, 0644, b_sftp._file_mode)
+ assertEqualMode(self, 0755, b_sftp.control_files._dir_mode)
+ assertEqualMode(self, 0644, b_sftp.control_files._file_mode)
open('local/a', 'wb').write('foo2\n')
t_local.commit('foo2')
@@ -278,8 +289,8 @@
check_mode_r(self, 'sftp/.bzr', 0664, 0775)
b_sftp = Branch.open(sftp_url)
- assertEqualMode(self, 0775, b_sftp._dir_mode)
- assertEqualMode(self, 0664, b_sftp._file_mode)
+ assertEqualMode(self, 0775, b_sftp.control_files._dir_mode)
+ assertEqualMode(self, 0664, b_sftp.control_files._file_mode)
open('local/a', 'wb').write('foo3\n')
t_local.commit('foo3')
=== modified file 'bzrlib/tests/test_revision.py'
--- bzrlib/tests/test_revision.py
+++ bzrlib/tests/test_revision.py
@@ -112,9 +112,9 @@
if rev_id in br2_only and not branch is br2:
continue
mutter('ancestry of {%s}: %r',
- rev_id, branch.get_ancestry(rev_id))
- self.assertEquals(sorted(branch.get_ancestry(rev_id)),
- [None] + sorted(anc))
+ rev_id, branch.repository.get_ancestry(rev_id))
+ result = sorted(branch.repository.get_ancestry(rev_id))
+ self.assertEquals(result, [None] + sorted(anc))
def test_is_ancestor(self):
@@ -157,7 +157,8 @@
self.br2.working_tree().commit("Commit fifteen", rev_id="b at u-0-10")
from bzrlib.revision import MultipleRevisionSources
- self.sources = MultipleRevisionSources(self.br1, self.br2)
+ self.sources = MultipleRevisionSources(self.br1.repository,
+ self.br2.repository)
def intervene(self, ancestor, revision, revision_history=None):
from bzrlib.revision import get_intervening_revisions
@@ -214,7 +215,7 @@
br1, br2 = make_branches(self)
revisions = br1.revision_history()
revisions_2 = br2.revision_history()
- sources = br1
+ sources = br1.repository
expected_ancestors_list = {revisions[3]:(0, 0),
revisions[2]:(1, 1),
@@ -253,7 +254,7 @@
br1, br2 = make_branches(self)
revisions = br1.revision_history()
revisions_2 = br2.revision_history()
- sources = MultipleRevisionSources(br1, br2)
+ sources = MultipleRevisionSources(br1.repository, br2.repository)
expected_ancestors_list = {revisions[3]:(0, 0),
revisions[2]:(1, 1),
revisions_2[4]:(2, 1),
@@ -288,7 +289,7 @@
Ensure it's not order-sensitive
"""
br1, br2 = make_branches(self)
- source = MultipleRevisionSources(br1, br2)
+ source = MultipleRevisionSources(br1.repository, br2.repository)
combined_1 = combined_graph(br1.last_revision(),
br2.last_revision(), source)
combined_2 = combined_graph(br2.last_revision(),
=== modified file 'bzrlib/tests/test_revisionnamespaces.py'
--- bzrlib/tests/test_revisionnamespaces.py
+++ bzrlib/tests/test_revisionnamespaces.py
@@ -21,7 +21,6 @@
from bzrlib.tests import TestCaseInTempDir
from bzrlib.errors import NoCommonAncestor, NoCommits
from bzrlib.errors import NoSuchRevision
-from bzrlib.clone import copy_branch
from bzrlib.merge import merge
from bzrlib.revisionspec import RevisionSpec
@@ -69,7 +68,7 @@
self.assertRaises(NoCommits, RevisionSpec('ancestor:.').in_history, b2)
os.mkdir('copy')
- b3 = copy_branch(b, 'copy')
+ b3 = b.clone('copy')
b3.working_tree().commit('Commit four', rev_id='b at r-0-4')
self.assertEquals(RevisionSpec('ancestor:.').in_history(b3).rev_id,
'a at r-0-3')
@@ -90,7 +89,7 @@
branch = Branch.initialize('branch1')
branch.working_tree().add(['file'])
branch.working_tree().commit('add file')
- copy_branch(branch, 'branch2')
+ branch.clone('branch2')
print >> open('branch2/file', 'w'), 'new content'
branch2 = Branch.open('branch2')
branch2.working_tree().commit('update file', rev_id='A')
=== modified file 'bzrlib/tests/test_revprops.py'
--- bzrlib/tests/test_revprops.py
+++ bzrlib/tests/test_revprops.py
@@ -16,7 +16,7 @@
revprops=props,
allow_pointless=True,
rev_id='test at user-1')
- rev = b.get_revision('test at user-1')
+ rev = b.repository.get_revision('test at user-1')
self.assertTrue('flavor' in rev.properties)
self.assertEquals(rev.properties['flavor'], 'choc-mint')
self.assertEquals(sorted(rev.properties.items()),
=== modified file 'bzrlib/tests/test_status.py'
--- bzrlib/tests/test_status.py
+++ bzrlib/tests/test_status.py
@@ -31,7 +31,6 @@
from bzrlib.merge import merge
from bzrlib.status import show_status
from bzrlib.branch import Branch
-from bzrlib.clone import copy_branch
class BranchStatus(TestCaseInTempDir):
@@ -111,7 +110,7 @@
mkdir("./branch")
b = Branch.initialize('./branch')
b.working_tree().commit("Empty commit 1")
- b_2 = copy_branch(b, './copy')
+ b_2 = b.clone('./copy')
b.working_tree().commit(u"\N{TIBETAN DIGIT TWO} Empty commit 2")
merge(["./branch", -1], [None, None], this_dir = './copy')
message = self.status_string(b_2)
=== modified file 'bzrlib/tests/test_testament.py'
--- bzrlib/tests/test_testament.py
+++ bzrlib/tests/test_testament.py
@@ -53,7 +53,7 @@
def test_null_testament(self):
"""Testament for a revision with no contents."""
- t = Testament.from_revision(self.b, 'test at user-1')
+ t = Testament.from_revision(self.b.repository, 'test at user-1')
ass = self.assertTrue
eq = self.assertEqual
ass(isinstance(t, Testament))
@@ -64,14 +64,14 @@
def test_testment_text_form(self):
"""Conversion of testament to canonical text form."""
- t = Testament.from_revision(self.b, 'test at user-1')
+ t = Testament.from_revision(self.b.repository, 'test at user-1')
text_form = t.as_text()
self.log('testament text form:\n' + text_form)
self.assertEqual(text_form, REV_1_TESTAMENT)
def test_testament_with_contents(self):
"""Testament containing a file and a directory."""
- t = Testament.from_revision(self.b, 'test at user-2')
+ t = Testament.from_revision(self.b.repository, 'test at user-2')
text_form = t.as_text()
self.log('testament text form:\n' + text_form)
self.assertEqualDiff(text_form, REV_2_TESTAMENT)
@@ -101,7 +101,7 @@
timezone=36000,
rev_id='test at user-3',
committer='test at user')
- t = Testament.from_revision(self.b, 'test at user-3')
+ t = Testament.from_revision(self.b.repository, 'test at user-3')
self.assertEqualDiff(t.as_text(), REV_3_TESTAMENT)
def test_testament_revprops(self):
@@ -114,14 +114,14 @@
rev_id='test at user-3',
committer='test at user',
revprops=props)
- t = Testament.from_revision(self.b, 'test at user-3')
+ t = Testament.from_revision(self.b.repository, 'test at user-3')
self.assertEqualDiff(t.as_text(), REV_PROPS_TESTAMENT)
def test___init__(self):
- revision = self.b.get_revision('test at user-2')
- inventory = self.b.get_inventory('test at user-2')
+ revision = self.b.repository.get_revision('test at user-2')
+ inventory = self.b.repository.get_inventory('test at user-2')
testament_1 = Testament(revision, inventory).as_short_text()
- testament_2 = Testament.from_revision(self.b,
+ testament_2 = Testament.from_revision(self.b.repository,
'test at user-2').as_short_text()
self.assertEqual(testament_1, testament_2)
=== modified file 'bzrlib/tests/test_upgrade.py'
--- bzrlib/tests/test_upgrade.py
+++ bzrlib/tests/test_upgrade.py
@@ -48,28 +48,28 @@
eq(rh,
['mbp at sourcefrog.net-20051004035611-176b16534b086b3c',
'mbp at sourcefrog.net-20051004035756-235f2b7dcdddd8dd'])
- t = b.revision_tree(rh[0])
+ t = b.repository.revision_tree(rh[0])
foo_id = 'foo-20051004035605-91e788d1875603ae'
eq(t.get_file_text(foo_id), 'initial contents\n')
- t = b.revision_tree(rh[1])
+ t = b.repository.revision_tree(rh[1])
eq(t.get_file_text(foo_id), 'new contents\n')
def test_upgrade_with_ghosts(self):
"""Upgrade v0.0.4 tree containing ghost references.
That is, some of the parents of revisions mentioned in the branch
- aren't present in the branches storage.
+ aren't present in the branch's storage.
This shouldn't normally happen in branches created entirely in
- bzr but can happen in imports from baz and arch, or from other
- systems, where the importer knows about a revision but not
+ bzr, but can happen in branches imported from baz and arch, or from
+ other systems, where the importer knows about a revision but not
its contents."""
eq = self.assertEquals
self.build_tree_contents(_ghost_template)
upgrade(u'.')
b = Branch.open(u'.')
revision_id = b.revision_history()[1]
- rev = b.get_revision(revision_id)
+ rev = b.repository.get_revision(revision_id)
eq(len(rev.parent_ids), 2)
eq(rev.parent_ids[1], 'wibble at wobble-2')
=== modified file 'bzrlib/tests/test_whitebox.py'
--- bzrlib/tests/test_whitebox.py
+++ bzrlib/tests/test_whitebox.py
@@ -54,7 +54,7 @@
revid = b.revision_history()[0]
self.log('first revision_id is {%s}' % revid)
- inv = b.get_revision_inventory(revid)
+ inv = b.repository.get_revision_inventory(revid)
self.log('contents of inventory: %r' % inv.entries())
self.check_inventory_shape(inv,
=== modified file 'bzrlib/tests/test_workingtree.py'
--- bzrlib/tests/test_workingtree.py
+++ bzrlib/tests/test_workingtree.py
@@ -102,15 +102,13 @@
branch = Branch.initialize(u'.')
tree = WorkingTree(branch.base)
tree.lock_read()
- self.assertEqual(1, tree.branch._lock_count)
- self.assertEqual('r', tree.branch._lock_mode)
+ self.assertEqual('r', tree.branch.peek_lock_mode())
tree.unlock()
- self.assertEqual(None, tree.branch._lock_count)
+ self.assertEqual(None, tree.branch.peek_lock_mode())
tree.lock_write()
- self.assertEqual(1, tree.branch._lock_count)
- self.assertEqual('w', tree.branch._lock_mode)
+ self.assertEqual('w', tree.branch.peek_lock_mode())
tree.unlock()
- self.assertEqual(None, tree.branch._lock_count)
+ self.assertEqual(None, tree.branch.peek_lock_mode())
def get_pullable_branches(self):
self.build_tree(['from/', 'from/file', 'to/'])
@@ -124,7 +122,7 @@
def test_pull(self):
br_a, br_b = self.get_pullable_branches()
br_b.working_tree().pull(br_a)
- self.failUnless(br_b.has_revision('A'))
+ self.failUnless(br_b.repository.has_revision('A'))
self.assertEqual(['A'], br_b.revision_history())
def test_pull_overwrites(self):
@@ -132,8 +130,8 @@
br_b.working_tree().commit('foo', rev_id='B')
self.assertEqual(['B'], br_b.revision_history())
br_b.working_tree().pull(br_a, overwrite=True)
- self.failUnless(br_b.has_revision('A'))
- self.failUnless(br_b.has_revision('B'))
+ self.failUnless(br_b.repository.has_revision('A'))
+ self.failUnless(br_b.repository.has_revision('B'))
self.assertEqual(['A'], br_b.revision_history())
def test_revert(self):
=== modified file 'bzrlib/transport/__init__.py'
--- bzrlib/transport/__init__.py
+++ bzrlib/transport/__init__.py
@@ -431,6 +431,10 @@
def get_transport(base):
+ """Open a transport to access a URL or directory.
+
+ base is either a URL or a directory name.
+ """
global _protocol_handlers
if base is None:
base = u'.'
=== modified file 'bzrlib/uncommit.py'
--- bzrlib/uncommit.py
+++ bzrlib/uncommit.py
@@ -27,16 +27,16 @@
revno = len(rh)
files_to_remove = []
- new_rev_history = AtomicFile(branch.controlfilename('revision-history'))
+ new_rev_history = AtomicFile(branch.control_files.controlfilename('revision-history'))
for r in range(revno-1, len(rh)):
rev_id = rh.pop()
if verbose:
print 'Removing revno %d: %s' % (len(rh)+1, rev_id)
- rev = branch.get_revision(rev_id)
- inv = branch.get_revision_inventory(rev_id)
+ rev = branch.repository.get_revision(rev_id)
+ inv = branch.repository.get_revision_inventory(rev_id)
inv_prev = []
for p in rev.parent_ids:
- inv_prev.append(branch.get_revision_inventory(p))
+ inv_prev.append(branch.repository.get_revision_inventory(p))
if remove_files:
# Figure out what text-store entries are new
=== modified file 'bzrlib/upgrade.py'
--- bzrlib/upgrade.py
+++ bzrlib/upgrade.py
@@ -102,11 +102,19 @@
self.pb = ui_factory.progress_bar()
if self.old_format == 4:
note('starting upgrade from format 4 to 5')
- self._convert_to_weaves()
+ self.branch.lock_write()
+ try:
+ self._convert_to_weaves()
+ finally:
+ self.branch.unlock()
self._open_branch()
if self.old_format == 5:
note('starting upgrade from format 5 to 6')
- self._convert_to_prefixed()
+ self.branch.lock_write()
+ try:
+ self._convert_to_prefixed()
+ finally:
+ self.branch.unlock()
self._open_branch()
cache = hashcache.HashCache(abspath(self.base))
cache.clear()
@@ -139,7 +147,7 @@
self.inv_weave = Weave('inventory')
# holds in-memory weaves for all files
self.text_weaves = {}
- os.remove(self.branch.controlfilename('branch-format'))
+ os.remove(self.branch.control_files.controlfilename('branch-format'))
self._convert_working_inv()
rev_history = self.branch.revision_history()
# to_read is a stack holding the revisions we still need to process;
@@ -178,21 +186,18 @@
self.branch._branch_format)
return True
-
def _set_new_format(self, format):
- self.branch.put_controlfile('branch-format', format)
-
+ self.branch.control_files.put_utf8('branch-format', format)
def _cleanup_spare_files(self):
for n in 'merged-patches', 'pending-merged-patches':
- p = self.branch.controlfilename(n)
+ p = self.branch.control_files.controlfilename(n)
if not os.path.exists(p):
continue
## assert os.path.getsize(p) == 0
os.remove(p)
shutil.rmtree(self.base + '/.bzr/inventory-store')
shutil.rmtree(self.base + '/.bzr/text-store')
-
def _backup_control_dir(self):
orig = self.base + '/.bzr'
@@ -203,14 +208,11 @@
note('if conversion fails, you can move this directory back to .bzr')
note('if it succeeds, you can remove this directory if you wish')
-
def _convert_working_inv(self):
branch = self.branch
- inv = serializer_v4.read_inventory(branch.controlfile('inventory', 'rb'))
+ inv = serializer_v4.read_inventory(branch.control_files.controlfile('inventory', 'rb'))
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
- branch.put_controlfile('inventory', new_inv_xml)
-
-
+ branch.control_files.put_utf8('inventory', new_inv_xml)
def _write_all_weaves(self):
write_a_weave(self.inv_weave, self.base + '/.bzr/inventory.weave')
@@ -248,14 +250,14 @@
self.pb.update('loading revision',
len(self.revisions),
len(self.known_revisions))
- if not self.branch.revision_store.has_id(rev_id):
+ if not self.branch.repository.revision_store.has_id(rev_id):
self.pb.clear()
note('revision {%s} not present in branch; '
'will be converted as a ghost',
rev_id)
self.absent_revisions.add(rev_id)
else:
- rev_xml = self.branch.revision_store.get(rev_id).read()
+ rev_xml = self.branch.repository.revision_store.get(rev_id).read()
rev = serializer_v4.read_revision_from_string(rev_xml)
for parent_id in rev.parent_ids:
self.known_revisions.add(parent_id)
@@ -265,7 +267,7 @@
def _load_old_inventory(self, rev_id):
assert rev_id not in self.converted_revs
- old_inv_xml = self.branch.inventory_store.get(rev_id).read()
+ old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
inv = serializer_v4.read_inventory_from_string(old_inv_xml)
rev = self.revisions[rev_id]
if rev.inventory_sha1:
@@ -360,7 +362,8 @@
return
parent_indexes = map(w.lookup, previous_revisions)
if ie.has_text():
- file_lines = self.branch.text_store.get(ie.text_id).readlines()
+ text = self.branch.repository.text_store.get(ie.text_id)
+ file_lines = text.readlines()
assert sha_strings(file_lines) == ie.text_sha1
assert sum(map(len, file_lines)) == ie.text_size
w.add(rev_id, parent_indexes, file_lines, ie.text_sha1)
=== modified file 'bzrlib/workingtree.py'
--- bzrlib/workingtree.py
+++ bzrlib/workingtree.py
@@ -49,14 +49,13 @@
from bzrlib.branch import (Branch,
is_control_file,
- needs_read_lock,
- needs_write_lock,
quotefn)
from bzrlib.errors import (BzrCheckError,
BzrError,
DivergedBranches,
WeaveRevisionNotPresent,
NotBranchError,
+ NoSuchFile,
NotVersionedError)
from bzrlib.inventory import InventoryEntry
from bzrlib.osutils import (appendpath,
@@ -77,6 +76,7 @@
import bzrlib.tree
from bzrlib.trace import mutter
import bzrlib.xml5
+from bzrlib.decorators import needs_read_lock, needs_write_lock
def gen_file_id(name):
@@ -410,23 +410,25 @@
if updated:
self.set_pending_merges(p)
+ @needs_read_lock
def pending_merges(self):
"""Return a list of pending merges.
These are revisions that have been merged into the working
directory but not yet committed.
"""
- cfn = self.branch._rel_controlfilename('pending-merges')
- if not self.branch._transport.has(cfn):
+ try:
+ f = self.branch.control_files.controlfile('pending-merges', 'r')
+ except NoSuchFile:
return []
p = []
- for l in self.branch.controlfile('pending-merges', 'r').readlines():
+ for l in f.readlines():
p.append(l.rstrip('\n'))
return p
@needs_write_lock
def set_pending_merges(self, rev_list):
- self.branch.put_controlfile('pending-merges', '\n'.join(rev_list))
+ self.branch.control_files.put_utf8('pending-merges', '\n'.join(rev_list))
def get_symlink_target(self, file_id):
return os.readlink(self.id2abspath(file_id))
@@ -675,9 +677,10 @@
other_revision = old_revision_history[-1]
else:
other_revision = None
+ repository = self.branch.repository
merge_inner(self.branch,
self.branch.basis_tree(),
- self.branch.revision_tree(other_revision))
+ repository.revision_tree(other_revision))
return count
finally:
source.unlock()
@@ -788,31 +791,31 @@
return 'basis-inventory.%s' % revision_id
def set_last_revision(self, new_revision, old_revision=None):
- if old_revision:
+ if old_revision is not None:
try:
path = self._basis_inventory_name(old_revision)
- path = self.branch._rel_controlfilename(path)
- self.branch._transport.delete(path)
- except:
+ path = self.branch.control_files._escape(path)
+ self.branch.control_files._transport.delete(path)
+ except NoSuchFile:
pass
try:
- xml = self.branch.get_inventory_xml(new_revision)
+ xml = self.branch.repository.get_inventory_xml(new_revision)
path = self._basis_inventory_name(new_revision)
- self.branch.put_controlfile(path, xml)
+ self.branch.control_files.put_utf8(path, xml)
except WeaveRevisionNotPresent:
pass
def read_basis_inventory(self, revision_id):
"""Read the cached basis inventory."""
path = self._basis_inventory_name(revision_id)
- return self.branch.controlfile(path, 'r').read()
+ return self.branch.control_files.controlfile(path, 'r').read()
@needs_read_lock
def read_working_inventory(self):
"""Read the working inventory."""
# ElementTree does its own conversion from UTF-8, so open in
# binary.
- f = self.branch.controlfile('inventory', 'rb')
+ f = self.branch.control_files.controlfile('inventory', 'rb')
return bzrlib.xml5.serializer_v5.read_inventory(f)
@needs_write_lock
@@ -914,7 +917,12 @@
between multiple working trees, i.e. via shared storage, then we
would probably want to lock both the local tree, and the branch.
"""
- if self._hashcache.needs_write and self.branch._lock_count==1:
+ # FIXME: We want to write out the hashcache only when the last lock on
+ # this working copy is released. Peeking at the lock count is a bit
+ # of a nasty hack; probably it's better to have a transaction object,
+ # which can do some finalization when it's either successfully or
+ # unsuccessfully completed. (Denys's original patch did that.)
+ if self._hashcache.needs_write and self.branch.control_files._lock_count==1:
self._hashcache.write()
return self.branch.unlock()
@@ -926,7 +934,7 @@
sio = StringIO()
bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
sio.seek(0)
- f = AtomicFile(self.branch.controlfilename('inventory'))
+ f = AtomicFile(self.branch.control_files.controlfilename('inventory'))
try:
pumpfile(sio, f)
f.commit()
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: Digital signature
Url : https://lists.ubuntu.com/archives/bazaar/attachments/20060112/eb55c1a1/attachment.pgp
More information about the bazaar
mailing list