Rev 3014: (robertc) Various lock and write-group correctness issues which show in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Thu Nov 22 21:20:40 GMT 2007


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 3014
revision-id:pqm at pqm.ubuntu.com-20071122212030-ehfexlt3qzptnsdv
parent: pqm at pqm.ubuntu.com-20071122201722-4l5bgljqn7qdux7f
parent: robertc at robertcollins.net-20071122023439-1stck34grarbn91o
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Thu 2007-11-22 21:20:30 +0000
message:
  (robertc) Various lock and write-group correctness issues which show
  	up when packs are the default format. (Robert Collins, #154204).
modified:
  bzrlib/annotate.py             annotate.py-20050922133147-7c60541d2614f022
  bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
  bzrlib/repofmt/pack_repo.py    pack_repo.py-20070813041115-gjv5ma7ktfqwsjgn-1
  bzrlib/repository.py           rev_storage.py-20051111201905-119e9401e46257e3
  bzrlib/revisionspec.py         revisionspec.py-20050907152633-17567659fd5c0ddb
  bzrlib/sign_my_commits.py      sign_my_commits.py-20060215152201-5a6363365180e671
  bzrlib/tests/blackbox/test_info.py test_info.py-20060215045507-bbdd2d34efab9e0a
  bzrlib/tests/blackbox/test_reconcile.py test_fix.py-20060223013051-9a188e15a5ee9451
  bzrlib/tests/blackbox/test_versioning.py versioning.py-20050622071821-3ddf5e2e5e93c602
  bzrlib/tests/interrepository_implementations/test_interrepository.py test_interrepository.py-20060220061411-1ec13fa99e5e3eee
  bzrlib/tests/test_annotate.py  test_annotate.py-20061213215015-sttc9agsxomls7q0-1
  bzrlib/tests/test_commit.py    test_commit.py-20050914060732-279f057f8c295434
  bzrlib/tests/test_dirstate.py  test_dirstate.py-20060728012006-d6mvoihjb3je9peu-2
  bzrlib/tests/test_fetch.py     testfetch.py-20050825090644-f73e07e7dfb1765a
  bzrlib/tests/test_graph.py     test_graph_walker.py-20070525030405-enq4r60hhi9xrujc-1
  bzrlib/tests/test_memorytree.py test_memorytree.py-20060906023413-4wlkalbdpsxi2r4y-3
  bzrlib/tests/test_merge_core.py test_merge_core.py-20050824132511-eb99b23a0eec641b
  bzrlib/tests/test_merge_directive.py test_merge_directive-20070228184838-ja62280spt1g7f4x-2
  bzrlib/transport/local.py      local_transport.py-20050711165921-9b1f142bfe480c24
    ------------------------------------------------------------
    revno: 3010.1.17
    revision-id:robertc at robertcollins.net-20071122023439-1stck34grarbn91o
    parent: robertc at robertcollins.net-20071122023349-i0nc5or8vpvdu84q
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: pack.read-locks
    timestamp: Thu 2007-11-22 13:34:39 +1100
    message:
      Lock correctness and commit_group management for re-sign, in builtins.
    modified:
      bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
    ------------------------------------------------------------
    revno: 3010.1.16
    revision-id:robertc at robertcollins.net-20071122023349-i0nc5or8vpvdu84q
    parent: robertc at robertcollins.net-20071122022357-17rpgugyooepb30y
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: pack.read-locks
    timestamp: Thu 2007-11-22 13:33:49 +1100
    message:
      Lock correctness in test_versioning
    modified:
      bzrlib/tests/blackbox/test_versioning.py versioning.py-20050622071821-3ddf5e2e5e93c602
    ------------------------------------------------------------
    revno: 3010.1.15
    revision-id:robertc at robertcollins.net-20071122022357-17rpgugyooepb30y
    parent: robertc at robertcollins.net-20071122020949-kpvph7srqw3h60p1
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: pack.read-locks
    timestamp: Thu 2007-11-22 13:23:57 +1100
    message:
      Manage write groups in sign_my_commits, for efficiency.
    modified:
      bzrlib/sign_my_commits.py      sign_my_commits.py-20060215152201-5a6363365180e671
    ------------------------------------------------------------
    revno: 3010.1.14
    revision-id:robertc at robertcollins.net-20071122020949-kpvph7srqw3h60p1
    parent: robertc at robertcollins.net-20071122020018-a30xrdfzxfwi8g2h
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: pack.read-locks
    timestamp: Thu 2007-11-22 13:09:49 +1100
    message:
      Handle packs in blackbox.test_reconcile.
    modified:
      bzrlib/tests/blackbox/test_reconcile.py test_fix.py-20060223013051-9a188e15a5ee9451
    ------------------------------------------------------------
    revno: 3010.1.13
    revision-id:robertc at robertcollins.net-20071122020018-a30xrdfzxfwi8g2h
    parent: robertc at robertcollins.net-20071122003315-ldoyi2mi3nma5qnf
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: pack.read-locks
    timestamp: Thu 2007-11-22 13:00:18 +1100
    message:
      Use the info code functions to determine format strings in the blackbox tests, and handle repositories that do not lock like packs.
    modified:
      bzrlib/tests/blackbox/test_info.py test_info.py-20060215045507-bbdd2d34efab9e0a
    ------------------------------------------------------------
    revno: 3010.1.12
    revision-id:robertc at robertcollins.net-20071122003315-ldoyi2mi3nma5qnf
    parent: robertc at robertcollins.net-20071122003029-puc7qosvic1beace
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: pack.read-locks
    timestamp: Thu 2007-11-22 11:33:15 +1100
    message:
      Lock branches while doing revision specification lookups.
    modified:
      bzrlib/revisionspec.py         revisionspec.py-20050907152633-17567659fd5c0ddb
    ------------------------------------------------------------
    revno: 3010.1.11
    revision-id:robertc at robertcollins.net-20071122003029-puc7qosvic1beace
    parent: robertc at robertcollins.net-20071122002958-szgbifl258kcstzp
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: pack.read-locks
    timestamp: Thu 2007-11-22 11:30:29 +1100
    message:
      Provide file modes to files created by pack repositories
    modified:
      bzrlib/repofmt/pack_repo.py    pack_repo.py-20070813041115-gjv5ma7ktfqwsjgn-1
    ------------------------------------------------------------
    revno: 3010.1.10
    revision-id:robertc at robertcollins.net-20071122002958-szgbifl258kcstzp
    parent: robertc at robertcollins.net-20071122000512-27cvsv9vs6k0s4hn
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: pack.read-locks
    timestamp: Thu 2007-11-22 11:29:58 +1100
    message:
      Honour file modes for write streams.
    modified:
      bzrlib/transport/local.py      local_transport.py-20050711165921-9b1f142bfe480c24
    ------------------------------------------------------------
    revno: 3010.1.9
    revision-id:robertc at robertcollins.net-20071122000512-27cvsv9vs6k0s4hn
    parent: robertc at robertcollins.net-20071121235702-y3gnrt33q6ng81mz
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: pack.read-locks
    timestamp: Thu 2007-11-22 11:05:12 +1100
    message:
      test_merge_directive locking correctness.
    modified:
      bzrlib/tests/test_merge_directive.py test_merge_directive-20070228184838-ja62280spt1g7f4x-2
    ------------------------------------------------------------
    revno: 3010.1.8
    revision-id:robertc at robertcollins.net-20071121235702-y3gnrt33q6ng81mz
    parent: robertc at robertcollins.net-20071121234250-hnltm60sryng2w55
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: pack.read-locks
    timestamp: Thu 2007-11-22 10:57:02 +1100
    message:
      test_merge_core locking correctness.
    modified:
      bzrlib/tests/test_merge_core.py test_merge_core.py-20050824132511-eb99b23a0eec641b
    ------------------------------------------------------------
    revno: 3010.1.7
    revision-id:robertc at robertcollins.net-20071121234250-hnltm60sryng2w55
    parent: robertc at robertcollins.net-20071121233940-3l1xpdnxqdneogqe
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: pack.read-locks
    timestamp: Thu 2007-11-22 10:42:50 +1100
    message:
      Locking in test_memorytree.
    modified:
      bzrlib/tests/test_memorytree.py test_memorytree.py-20060906023413-4wlkalbdpsxi2r4y-3
    ------------------------------------------------------------
    revno: 3010.1.6
    revision-id:robertc at robertcollins.net-20071121233940-3l1xpdnxqdneogqe
    parent: robertc at robertcollins.net-20071121233632-63hxiuhd5kcweg21
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: pack.read-locks
    timestamp: Thu 2007-11-22 10:39:40 +1100
    message:
      Locking in test_graph.
    modified:
      bzrlib/tests/test_graph.py     test_graph_walker.py-20070525030405-enq4r60hhi9xrujc-1
    ------------------------------------------------------------
    revno: 3010.1.5
    revision-id:robertc at robertcollins.net-20071121233632-63hxiuhd5kcweg21
    parent: robertc at robertcollins.net-20071121233532-6pr4b7vvq3s01b7m
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: pack.read-locks
    timestamp: Thu 2007-11-22 10:36:32 +1100
    message:
      Test that missing_revision_ids handles the case of the source not having the requested revision correctly with and without find_ghosts.
    modified:
      bzrlib/repository.py           rev_storage.py-20051111201905-119e9401e46257e3
      bzrlib/tests/interrepository_implementations/test_interrepository.py test_interrepository.py-20060220061411-1ec13fa99e5e3eee
    ------------------------------------------------------------
    revno: 3010.1.4
    revision-id:robertc at robertcollins.net-20071121233532-6pr4b7vvq3s01b7m
    parent: robertc at robertcollins.net-20071121221702-winh9l8xzqt177px
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: pack.read-locks
    timestamp: Thu 2007-11-22 10:35:32 +1100
    message:
      Make the knit specific fetch tests knit specific, and lock the branch when looking at historical texts in test_fetch.
    modified:
      bzrlib/tests/test_fetch.py     testfetch.py-20050825090644-f73e07e7dfb1765a
    ------------------------------------------------------------
    revno: 3010.1.3
    revision-id:robertc at robertcollins.net-20071121221702-winh9l8xzqt177px
    parent: robertc at robertcollins.net-20071121221620-lnx3jph93gfq2t03
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: pack.read-locks
    timestamp: Thu 2007-11-22 09:17:02 +1100
    message:
      Lock RevisionTrees correctly in commit tests.
    modified:
      bzrlib/tests/test_commit.py    test_commit.py-20050914060732-279f057f8c295434
    ------------------------------------------------------------
    revno: 3010.1.2
    revision-id:robertc at robertcollins.net-20071121221620-lnx3jph93gfq2t03
    parent: robertc at robertcollins.net-20071121215711-meep8d5qazsufllc
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: pack.read-locks
    timestamp: Thu 2007-11-22 09:16:20 +1100
    message:
      Use valid file-ids for dirstate tests.
    modified:
      bzrlib/tests/test_dirstate.py  test_dirstate.py-20060728012006-d6mvoihjb3je9peu-2
    ------------------------------------------------------------
    revno: 3010.1.1
    revision-id:robertc at robertcollins.net-20071121215711-meep8d5qazsufllc
    parent: pqm at pqm.ubuntu.com-20071121045727-gqycrul94d0ssir1
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: pack.read-locks
    timestamp: Thu 2007-11-22 08:57:11 +1100
    message:
      Lock the tree's used to test annotate_file, and add a docstring for annotate_file explaining its needs.
    modified:
      bzrlib/annotate.py             annotate.py-20050922133147-7c60541d2614f022
      bzrlib/tests/test_annotate.py  test_annotate.py-20061213215015-sttc9agsxomls7q0-1
=== modified file 'bzrlib/annotate.py'
--- a/bzrlib/annotate.py	2007-10-16 16:02:01 +0000
+++ b/bzrlib/annotate.py	2007-11-21 21:57:11 +0000
@@ -39,6 +39,20 @@
 
 def annotate_file(branch, rev_id, file_id, verbose=False, full=False,
                   to_file=None, show_ids=False):
+    """Annotate file_id at revision rev_id in branch.
+
+    The branch should already be read_locked() when annotate_file is called.
+
+    :param branch: The branch to look for revision numbers and history from.
+    :param rev_id: The revision id to annotate.
+    :param file_id: The file_id to annotate.
+    :param verbose: Show all details rather than truncating to ensure
+        reasonable text width.
+    :param full: XXXX Not sure what this does.
+    :param to_file: The file to output the annotation to; if None stdout is
+        used.
+    :param show_ids: Show revision ids in the annotation output.
+    """
     if to_file is None:
         to_file = sys.stdout
 

=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py	2007-11-20 14:43:47 +0000
+++ b/bzrlib/builtins.py	2007-11-22 02:34:39 +0000
@@ -2115,7 +2115,6 @@
         if revision is not None and len(revision) != 1:
             raise errors.BzrCommandError("bzr cat --revision takes exactly"
                                         " one number")
-
         tree = None
         try:
             tree, b, relpath = \
@@ -2125,6 +2124,14 @@
 
         if revision is not None and revision[0].get_branch() is not None:
             b = Branch.open(revision[0].get_branch())
+        b.lock_read()
+        try:
+            return self._run(tree, b, relpath, filename, revision,
+                name_from_revision)
+        finally:
+            b.unlock()
+
+    def _run(self, tree, b, relpath, filename, revision, name_from_revision):
         if tree is None:
             tree = b.basis_tree()
         if revision is None:
@@ -2686,14 +2693,21 @@
         
         branch1 = Branch.open_containing(branch)[0]
         branch2 = Branch.open_containing(other)[0]
-
-        last1 = ensure_null(branch1.last_revision())
-        last2 = ensure_null(branch2.last_revision())
-
-        graph = branch1.repository.get_graph(branch2.repository)
-        base_rev_id = graph.find_unique_lca(last1, last2)
-
-        print 'merge base is revision %s' % base_rev_id
+        branch1.lock_read()
+        try:
+            branch2.lock_read()
+            try:
+                last1 = ensure_null(branch1.last_revision())
+                last2 = ensure_null(branch2.last_revision())
+
+                graph = branch1.repository.get_graph(branch2.repository)
+                base_rev_id = graph.find_unique_lca(last1, last2)
+
+                print 'merge base is revision %s' % base_rev_id
+            finally:
+                branch2.unlock()
+        finally:
+            branch1.unlock()
 
 
 class cmd_merge(Command):
@@ -3422,20 +3436,41 @@
     takes_options = ['revision']
     
     def run(self, revision_id_list=None, revision=None):
-        import bzrlib.gpg as gpg
         if revision_id_list is not None and revision is not None:
             raise errors.BzrCommandError('You can only supply one of revision_id or --revision')
         if revision_id_list is None and revision is None:
             raise errors.BzrCommandError('You must supply either --revision or a revision_id')
         b = WorkingTree.open_containing(u'.')[0].branch
+        b.lock_write()
+        try:
+            return self._run(b, revision_id_list, revision)
+        finally:
+            b.unlock()
+
+    def _run(self, b, revision_id_list, revision):
+        import bzrlib.gpg as gpg
         gpg_strategy = gpg.GPGStrategy(b.get_config())
         if revision_id_list is not None:
-            for revision_id in revision_id_list:
-                b.repository.sign_revision(revision_id, gpg_strategy)
+            b.repository.start_write_group()
+            try:
+                for revision_id in revision_id_list:
+                    b.repository.sign_revision(revision_id, gpg_strategy)
+            except:
+                b.repository.abort_write_group()
+                raise
+            else:
+                b.repository.commit_write_group()
         elif revision is not None:
             if len(revision) == 1:
                 revno, rev_id = revision[0].in_history(b)
-                b.repository.sign_revision(rev_id, gpg_strategy)
+                b.repository.start_write_group()
+                try:
+                    b.repository.sign_revision(rev_id, gpg_strategy)
+                except:
+                    b.repository.abort_write_group()
+                    raise
+                else:
+                    b.repository.commit_write_group()
             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
@@ -3446,9 +3481,16 @@
                     to_revno = b.revno()
                 if from_revno is None or to_revno is None:
                     raise errors.BzrCommandError('Cannot sign a range of non-revision-history revisions')
-                for revno in range(from_revno, to_revno + 1):
-                    b.repository.sign_revision(b.get_rev_id(revno), 
-                                               gpg_strategy)
+                b.repository.start_write_group()
+                try:
+                    for revno in range(from_revno, to_revno + 1):
+                        b.repository.sign_revision(b.get_rev_id(revno),
+                                                   gpg_strategy)
+                except:
+                    b.repository.abort_write_group()
+                    raise
+                else:
+                    b.repository.commit_write_group()
             else:
                 raise errors.BzrCommandError('Please supply either one revision, or a range.')
 
@@ -3965,6 +4007,9 @@
             outfile = open(output, 'wb')
         try:
             branch = Branch.open_containing(from_)[0]
+            # we may need to write data into branch's repository to calculate
+            # the data to send.
+            branch.lock_write()
             if output is None:
                 config = branch.get_config()
                 if mail_to is None:
@@ -4052,6 +4097,7 @@
         finally:
             if output != '-':
                 outfile.close()
+            branch.unlock()
 
 
 class cmd_bundle_revisions(cmd_send):

=== modified file 'bzrlib/repofmt/pack_repo.py'
--- a/bzrlib/repofmt/pack_repo.py	2007-11-13 21:39:37 +0000
+++ b/bzrlib/repofmt/pack_repo.py	2007-11-22 00:30:29 +0000
@@ -187,7 +187,7 @@
         }
 
     def __init__(self, upload_transport, index_transport, pack_transport,
-        upload_suffix=''):
+        upload_suffix='', file_mode=None):
         """Create a NewPack instance.
 
         :param upload_transport: A writable transport for the pack to be
@@ -199,6 +199,7 @@
             upload_transport.clone('../packs').
         :param upload_suffix: An optional suffix to be given to any temporary
             files created during the pack creation. e.g '.autopack'
+        :param file_mode: An optional file mode to create the new files with.
         """
         # The relative locations of the packs are constrained, but all are
         # passed in because the caller has them, so as to avoid object churn.
@@ -223,6 +224,8 @@
         self.index_transport = index_transport
         # where is the pack renamed to when it is finished?
         self.pack_transport = pack_transport
+        # What file mode to upload the pack and indices with.
+        self._file_mode = file_mode
         # tracks the content written to the .pack file.
         self._hash = md5.new()
         # a four-tuple with the length in bytes of the indices, once the pack
@@ -239,7 +242,7 @@
         self.start_time = time.time()
         # open an output stream for the data added to the pack.
         self.write_stream = self.upload_transport.open_write_stream(
-            self.random_name)
+            self.random_name, mode=self._file_mode)
         if 'pack' in debug.debug_flags:
             mutter('%s: create_pack: pack stream open: %s%s t+%6.3fs',
                 time.ctime(), self.upload_transport.base, self.random_name,
@@ -368,7 +371,8 @@
         """
         index_name = self.index_name(index_type, self.name)
         self.index_sizes[self.index_offset(index_type)] = \
-            self.index_transport.put_file(index_name, index.finish())
+            self.index_transport.put_file(index_name, index.finish(),
+            mode=self._file_mode)
         if 'pack' in debug.debug_flags:
             # XXX: size might be interesting?
             mutter('%s: create_pack: wrote %s index: %s%s t+%6.3fs',
@@ -533,7 +537,8 @@
         """Open a pack for the pack we are creating."""
         return NewPack(self._pack_collection._upload_transport,
             self._pack_collection._index_transport,
-            self._pack_collection._pack_transport, upload_suffix=self.suffix)
+            self._pack_collection._pack_transport, upload_suffix=self.suffix,
+            file_mode=self._pack_collection.repo.control_files._file_mode)
 
     def _create_pack_from_packs(self):
         self.pb.update("Opening pack", 0, 5)
@@ -1245,7 +1250,8 @@
             # changing it.
             for key, value in disk_nodes:
                 builder.add_node(key, value)
-            self.transport.put_file('pack-names', builder.finish())
+            self.transport.put_file('pack-names', builder.finish(),
+                mode=self.repo.control_files._file_mode)
             # move the baseline forward
             self._packs_at_load = disk_nodes
             # now clear out the obsolete packs directory
@@ -1288,7 +1294,8 @@
         if not self.repo.is_write_locked():
             raise errors.NotWriteLocked(self)
         self._new_pack = NewPack(self._upload_transport, self._index_transport,
-            self._pack_transport, upload_suffix='.pack')
+            self._pack_transport, upload_suffix='.pack',
+            file_mode=self.repo.control_files._file_mode)
         # allow writing: queue writes to a new index
         self.revision_index.add_writable_index(self._new_pack.revision_index,
             self._new_pack)

=== modified file 'bzrlib/repository.py'
--- a/bzrlib/repository.py	2007-11-18 19:25:42 +0000
+++ b/bzrlib/repository.py	2007-11-22 21:20:30 +0000
@@ -792,14 +792,15 @@
                 (format, data_list, StringIO(knit_bytes).read))
 
     @needs_read_lock
-    def missing_revision_ids(self, other, revision_id=None):
+    def missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
         """Return the revision ids that other has that this does not.
         
         These are returned in topological order.
 
         revision_id: only return revision ids included by revision_id.
         """
-        return InterRepository.get(other, self).missing_revision_ids(revision_id)
+        return InterRepository.get(other, self).missing_revision_ids(
+            revision_id, find_ghosts)
 
     @staticmethod
     def open(base):
@@ -2054,7 +2055,7 @@
         raise NotImplementedError(self.fetch)
    
     @needs_read_lock
-    def missing_revision_ids(self, revision_id=None):
+    def missing_revision_ids(self, revision_id=None, find_ghosts=True):
         """Return the revision ids that source has that target does not.
         
         These are returned in topological order.
@@ -2221,7 +2222,7 @@
         return f.count_copied, f.failed_revisions
 
     @needs_read_lock
-    def missing_revision_ids(self, revision_id=None):
+    def missing_revision_ids(self, revision_id=None, find_ghosts=True):
         """See InterRepository.missing_revision_ids()."""
         # we want all revisions to satisfy revision_id in source.
         # but we don't want to stat every file here and there.
@@ -2299,7 +2300,7 @@
         return f.count_copied, f.failed_revisions
 
     @needs_read_lock
-    def missing_revision_ids(self, revision_id=None):
+    def missing_revision_ids(self, revision_id=None, find_ghosts=True):
         """See InterRepository.missing_revision_ids()."""
         if revision_id is not None:
             source_ids = self.source.get_ancestry(revision_id)
@@ -2376,7 +2377,7 @@
             # sensibly detect 'new revisions' without doing a full index scan.
         elif _mod_revision.is_null(revision_id):
             # nothing to do:
-            return
+            return (0, [])
         else:
             try:
                 revision_ids = self.missing_revision_ids(revision_id,
@@ -2392,9 +2393,9 @@
             # a pack creation, but for now it is simpler to think about as
             # 'upload data, then repack if needed'.
             self.target._pack_collection.autopack()
-            return pack.get_revision_count()
+            return (pack.get_revision_count(), [])
         else:
-            return 0
+            return (0, [])
 
     @needs_read_lock
     def missing_revision_ids(self, revision_id=None, find_ghosts=True):
@@ -2421,6 +2422,12 @@
                     target_index.iter_entries(target_keys))
                 missing_revs.update(next_revs - have_revs)
                 searcher.stop_searching_any(have_revs)
+            if next_revs - have_revs == set([revision_id]):
+                # we saw the start rev itself, but no parents from it (or
+                # next_revs would have been updated to e.g. set(). We remove
+                # have_revs because if we found revision_id locally we
+                # stop_searching at the first time around.
+                raise errors.NoSuchRevision(self.source, revision_id)
             return missing_revs
         elif revision_id is not None:
             source_ids = self.source.get_ancestry(revision_id)

=== modified file 'bzrlib/revisionspec.py'
--- a/bzrlib/revisionspec.py	2007-08-20 13:03:46 +0000
+++ b/bzrlib/revisionspec.py	2007-11-22 00:33:15 +0000
@@ -639,22 +639,28 @@
         for r, b in ((revision_a, branch), (revision_b, other_branch)):
             if r in (None, revision.NULL_REVISION):
                 raise errors.NoCommits(b)
-        revision_source = revision.MultipleRevisionSources(
-                branch.repository, other_branch.repository)
-        graph = branch.repository.get_graph(other_branch.repository)
-        revision_a = revision.ensure_null(revision_a)
-        revision_b = revision.ensure_null(revision_b)
-        if revision.NULL_REVISION in (revision_a, revision_b):
-            rev_id = revision.NULL_REVISION
-        else:
-            rev_id = graph.find_unique_lca(revision_a, revision_b)
-            if rev_id == revision.NULL_REVISION:
-                raise errors.NoCommonAncestor(revision_a, revision_b)
+        branch.lock_read()
+        other_branch.lock_read()
         try:
-            revno = branch.revision_id_to_revno(rev_id)
-        except errors.NoSuchRevision:
-            revno = None
-        return RevisionInfo(branch, revno, rev_id)
+            revision_source = revision.MultipleRevisionSources(
+                    branch.repository, other_branch.repository)
+            graph = branch.repository.get_graph(other_branch.repository)
+            revision_a = revision.ensure_null(revision_a)
+            revision_b = revision.ensure_null(revision_b)
+            if revision.NULL_REVISION in (revision_a, revision_b):
+                rev_id = revision.NULL_REVISION
+            else:
+                rev_id = graph.find_unique_lca(revision_a, revision_b)
+                if rev_id == revision.NULL_REVISION:
+                    raise errors.NoCommonAncestor(revision_a, revision_b)
+            try:
+                revno = branch.revision_id_to_revno(rev_id)
+            except errors.NoSuchRevision:
+                revno = None
+            return RevisionInfo(branch, revno, rev_id)
+        finally:
+            branch.unlock()
+            other_branch.unlock()
 
 
 SPEC_TYPES.append(RevisionSpec_ancestor)

=== modified file 'bzrlib/sign_my_commits.py'
--- a/bzrlib/sign_my_commits.py	2007-07-10 10:33:45 +0000
+++ b/bzrlib/sign_my_commits.py	2007-11-22 02:23:57 +0000
@@ -65,18 +65,25 @@
         count = 0
         repo.lock_write()
         try:
-            for rev_id in repo.get_ancestry(branch.last_revision())[1:]:
-                if repo.has_signature_for_revision_id(rev_id):
-                    continue
-                rev = repo.get_revision(rev_id)
-                if rev.committer != committer:
-                    continue
-                # We have a revision without a signature who has a 
-                # matching committer, start signing
-                print rev_id
-                count += 1
-                if not dry_run:
-                    repo.sign_revision(rev_id, gpg_strategy)
+            repo.start_write_group()
+            try:
+                for rev_id in repo.get_ancestry(branch.last_revision())[1:]:
+                    if repo.has_signature_for_revision_id(rev_id):
+                        continue
+                    rev = repo.get_revision(rev_id)
+                    if rev.committer != committer:
+                        continue
+                    # We have a revision without a signature who has a 
+                    # matching committer, start signing
+                    print rev_id
+                    count += 1
+                    if not dry_run:
+                        repo.sign_revision(rev_id, gpg_strategy)
+            except:
+                repo.abort_write_group()
+                raise
+            else:
+                repo.commit_write_group()
         finally:
             repo.unlock()
         print 'Signed %d revisions' % (count,)

=== modified file 'bzrlib/tests/blackbox/test_info.py'
--- a/bzrlib/tests/blackbox/test_info.py	2007-11-15 04:08:32 +0000
+++ b/bzrlib/tests/blackbox/test_info.py	2007-11-22 21:20:30 +0000
@@ -24,6 +24,7 @@
 from bzrlib import (
     bzrdir,
     errors,
+    info,
     osutils,
     repository,
     urlutils,
@@ -486,7 +487,7 @@
         repo = branch.repository
         out, err = self.run_bzr('info branch -v')
         self.assertEqualDiff(
-"""Standalone branch (format: dirstate-tags)
+"""Standalone branch (format: %s)
 Location:
   branch root: branch
 
@@ -502,7 +503,8 @@
 Repository:
          0 revisions
          0 KiB
-""" % (format.get_branch_format().get_format_description(),
+""" % (info.describe_format(repo.bzrdir, repo, branch, None),
+       format.get_branch_format().get_format_description(),
        format.repository_format.get_format_description(),
        ), out)
         self.assertEqual('', err)
@@ -1106,7 +1108,7 @@
        ), out)
         self.assertEqual('', err)
 
-    def assertCheckoutStatusOutput(self, 
+    def assertCheckoutStatusOutput(self,
         command_string, lco_tree, shared_repo=None,
         repo_branch=None,
         tree_locked=False,
@@ -1131,6 +1133,10 @@
         :param tree_locked: If true, expect the tree to be locked.
         :param branch_locked: If true, expect the branch to be locked.
         :param repo_locked: If true, expect the repository to be locked.
+            Note that the lco_tree.branch.repository is inspected, and if is not
+            actually locked then this parameter is overridden. This is because
+            pack repositories do not have any public API for obtaining an
+            exclusive repository wide lock.
         :param verbose: If true, expect verbose output
         """
         def friendly_location(url):
@@ -1156,6 +1162,8 @@
         format = {True: 'dirstate or dirstate-tags or knitpack-experimental'
                         ' or rich-root',
                   False: 'dirstate'}[light_checkout]
+        if repo_locked:
+            repo_locked = lco_tree.branch.repository.get_physical_lock_status()
         if repo_locked or branch_locked or tree_locked:
             def locked_message(a_bool):
                 if a_bool:

=== modified file 'bzrlib/tests/blackbox/test_reconcile.py'
--- a/bzrlib/tests/blackbox/test_reconcile.py	2007-10-03 06:37:01 +0000
+++ b/bzrlib/tests/blackbox/test_reconcile.py	2007-11-22 02:09:49 +0000
@@ -42,10 +42,15 @@
     def test_trivial_reconcile(self):
         t = bzrdir.BzrDir.create_standalone_workingtree('.')
         (out, err) = self.run_bzr('reconcile')
+        if t.branch.repository._reconcile_backsup_inventory:
+            does_backup_text = "Inventory ok.\n"
+        else:
+            does_backup_text = ""
         self.assertEqualDiff(out, "Reconciling repository %s\n"
-                                  "Inventory ok.\n"
+                                  "%s"
                                   "Reconciliation complete.\n" %
-                                  t.bzrdir.root_transport.base)
+                                  (t.bzrdir.root_transport.base,
+                                   does_backup_text))
         self.assertEqualDiff(err, "")
 
     def test_does_something_reconcile(self):
@@ -60,9 +65,15 @@
         repo.commit_write_group()
         repo.unlock()
         (out, err) = self.run_bzr('reconcile')
-        self.assertEqualDiff(out, "Reconciling repository %s\n"
-                                  "Backup Inventory created.\n"
-                                  "Inventory regenerated.\n"
-                                  "Reconciliation complete.\n" %
-                                  t.bzrdir.root_transport.base)
+        if repo._reconcile_backsup_inventory:
+            does_backup_text = (
+                "Backup Inventory created.\n"
+                "Inventory regenerated.\n")
+        else:
+            does_backup_text = ""
+        expected = ("Reconciling repository %s\n"
+                    "%s"
+                    "Reconciliation complete.\n" %
+                    (t.bzrdir.root_transport.base, does_backup_text))
+        self.assertEqualDiff(expected, out)
         self.assertEqualDiff(err, "")

=== modified file 'bzrlib/tests/blackbox/test_versioning.py'
--- a/bzrlib/tests/blackbox/test_versioning.py	2007-06-28 12:26:11 +0000
+++ b/bzrlib/tests/blackbox/test_versioning.py	2007-11-22 02:33:49 +0000
@@ -148,24 +148,30 @@
 
         old = b.repository.revision_tree(b.get_rev_id(1))
         new = b.repository.revision_tree(b.get_rev_id(2))
+        new.lock_read()
 
         eq(new.get_file_by_path('b/two').read(), 'old contents')
         eq(new.get_file_by_path('top').read(), 'old contents')
         eq(new.get_file_by_path('a/one').read(), 'new contents')
+        new.unlock()
 
         os.chdir('a')
         # commit from here should do nothing
         run_bzr(['commit', '.', '-m', 'commit subdir only', '--unchanged'])
         v3 = b.repository.revision_tree(b.get_rev_id(3))
+        v3.lock_read()
         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')
+        v3.unlock()
 
         # commit in subdirectory commits whole tree
         run_bzr(['commit', '-m', 'commit whole tree from subdir'])
         v4 = b.repository.revision_tree(b.get_rev_id(4))
+        v4.lock_read()
         eq(v4.get_file_by_path('b/two').read(), 'new contents')
         eq(v4.get_file_by_path('top').read(), 'new contents')
+        v4.unlock()
 
         # TODO: factor out some kind of assert_tree_state() method
 

=== modified file 'bzrlib/tests/interrepository_implementations/test_interrepository.py'
--- a/bzrlib/tests/interrepository_implementations/test_interrepository.py	2007-10-30 17:39:11 +0000
+++ b/bzrlib/tests/interrepository_implementations/test_interrepository.py	2007-11-21 23:36:32 +0000
@@ -275,6 +275,20 @@
         self.assertEqual(['rev2'],
                          repo_b.missing_revision_ids(repo_a))
 
+    def test_missing_revision_ids_absent_requested_raises(self):
+        # Asking for missing revisions with a tip that is itself absent in the
+        # source raises NoSuchRevision.
+        repo_b = self.make_to_repository('target')
+        repo_a = self.bzrdir.open_repository()
+        # No pizza revisions anywhere
+        self.assertFalse(repo_a.has_revision('pizza'))
+        self.assertFalse(repo_b.has_revision('pizza'))
+        # Asking specifically for an absent revision errors.
+        self.assertRaises(NoSuchRevision, repo_b.missing_revision_ids, repo_a,
+            revision_id='pizza', find_ghosts=True)
+        self.assertRaises(NoSuchRevision, repo_b.missing_revision_ids, repo_a,
+            revision_id='pizza', find_ghosts=False)
+
     def test_missing_revision_ids_revision_limited(self):
         # revision ids in repository A that are not referenced by the
         # requested revision are not returned.

=== modified file 'bzrlib/tests/test_annotate.py'
--- a/bzrlib/tests/test_annotate.py	2007-08-31 19:38:52 +0000
+++ b/bzrlib/tests/test_annotate.py	2007-11-21 21:57:11 +0000
@@ -149,6 +149,8 @@
         tree1.commit('merge 2', rev_id='rev-3',
                      committer='sal at foo.com',
                      timestamp=1166046003.00, timezone=0)
+        tree1.lock_read()
+        self.addCleanup(tree1.unlock)
         return tree1, tree2
 
     def create_deeply_merged_trees(self):
@@ -175,6 +177,7 @@
         rev-6
         """
         tree1, tree2 = self.create_merged_trees()
+        tree1.unlock()
 
         tree3 = tree2.bzrdir.clone('tree3').open_workingtree()
 
@@ -202,6 +205,7 @@
                      timestamp=1166046005.00, timezone=0)
         self.assertEqual(0, tree1.merge_from_branch(tree4.branch))
         tree1.commit('merge five and six', rev_id='rev-6')
+        tree1.lock_read()
         return tree1
 
     def test_annotate_shows_dotted_revnos(self):
@@ -325,6 +329,8 @@
                      committer=u'p\xe9rez',
                      timestamp=1166046000.00, timezone=0)
 
+        tree1.lock_read()
+        self.addCleanup(tree1.unlock)
         # this passes if no exception is raised
         to_file = StringIO()
         annotate.annotate_file(tree1.branch, 'rev-1', 'a-id', to_file=to_file)
@@ -362,6 +368,8 @@
                      author='Author <author at example.com>',
                      timestamp=1166046000.00, timezone=0)
 
+        tree1.lock_read()
+        self.addCleanup(tree1.unlock)
         to_file = StringIO()
         annotate.annotate_file(tree1.branch, 'rev-1', 'a-id', to_file=to_file)
         self.assertEqual('1   committ | hello\n', to_file.getvalue())

=== modified file 'bzrlib/tests/test_commit.py'
--- a/bzrlib/tests/test_commit.py	2007-11-01 09:52:45 +0000
+++ b/bzrlib/tests/test_commit.py	2007-11-21 22:17:02 +0000
@@ -95,11 +95,16 @@
         eq(rev.message, 'add hello')
 
         tree1 = b.repository.revision_tree(rh[0])
+        tree1.lock_read()
         text = tree1.get_file_text(file_id)
-        eq(text, 'hello world')
+        tree1.unlock()
+        self.assertEqual('hello world', text)
 
         tree2 = b.repository.revision_tree(rh[1])
-        eq(tree2.get_file_text(file_id), 'version 2')
+        tree2.lock_read()
+        text = tree2.get_file_text(file_id)
+        tree2.unlock()
+        self.assertEqual('version 2', text)
 
     def test_delete_commit(self):
         """Test a commit with a deleted file"""
@@ -168,11 +173,15 @@
         eq(b.revno(), 3)
 
         tree2 = b.repository.revision_tree('test at rev-2')
+        tree2.lock_read()
+        self.addCleanup(tree2.unlock)
         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.repository.revision_tree('test at rev-3')
+        tree3.lock_read()
+        self.addCleanup(tree3.unlock)
         self.assertFalse(tree3.has_filename('hello'))
         self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
 
@@ -189,6 +198,8 @@
 
         eq = self.assertEquals
         tree1 = b.repository.revision_tree('test at rev-1')
+        tree1.lock_read()
+        self.addCleanup(tree1.unlock)
         eq(tree1.id2path('hello-id'), 'hello')
         eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
         self.assertFalse(tree1.has_filename('fruity'))
@@ -197,6 +208,8 @@
         eq(ie.revision, 'test at rev-1')
 
         tree2 = b.repository.revision_tree('test at rev-2')
+        tree2.lock_read()
+        self.addCleanup(tree2.unlock)
         eq(tree2.id2path('hello-id'), 'fruity')
         eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
         self.check_inventory_shape(tree2.inventory, ['fruity'])

=== modified file 'bzrlib/tests/test_dirstate.py'
--- a/bzrlib/tests/test_dirstate.py	2007-11-15 01:07:51 +0000
+++ b/bzrlib/tests/test_dirstate.py	2007-11-21 22:16:20 +0000
@@ -420,7 +420,7 @@
     def get_tree_with_a_file(self):
         tree = self.make_branch_and_tree('tree')
         self.build_tree(['tree/a file'])
-        tree.add('a file', 'a file id')
+        tree.add('a file', 'a-file-id')
         return tree
 
     def test_non_empty_no_parents_to_dirstate(self):
@@ -431,7 +431,7 @@
             (('', '', tree.get_root_id()), # common details
              [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
              ]),
-            (('', 'a file', 'a file id'), # common
+            (('', 'a file', 'a-file-id'), # common
              [('f', '', 0, False, dirstate.DirState.NULLSTAT), # current
              ]),
             ])
@@ -450,7 +450,7 @@
              [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
               ('d', '', 0, False, rev_id), # first parent details
              ]),
-            (('', 'a file', 'a file id'), # common
+            (('', 'a file', 'a-file-id'), # common
              [('f', '', 0, False, dirstate.DirState.NULLSTAT), # current
               ('f', 'c3ed76e4bfd45ff1763ca206055bca8e9fc28aa8', 24, False,
                rev_id), # first parent
@@ -478,7 +478,7 @@
               ('d', '', 0, False, rev_id), # first parent details
               ('d', '', 0, False, rev_id2), # second parent details
              ]),
-            (('', 'a file', 'a file id'), # common
+            (('', 'a file', 'a-file-id'), # common
              [('f', '', 0, False, dirstate.DirState.NULLSTAT), # current
               ('f', 'c3ed76e4bfd45ff1763ca206055bca8e9fc28aa8', 24, False,
                rev_id), # first parent
@@ -1008,12 +1008,12 @@
             (('', '', 'TREE_ROOT'), [
              ('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
              ]),
-            (('', 'a file', 'a file id'), [
+            (('', 'a file', 'a-file-id'), [
              ('f', '1'*20, 19, False, dirstate.pack_stat(stat)), # current tree
              ]),
             ]
         try:
-            state.add('a file', 'a file id', 'file', stat, '1'*20)
+            state.add('a file', 'a-file-id', 'file', stat, '1'*20)
             # having added it, it should be in the output of iter_entries.
             self.assertEqual(expected_entries, list(state._iter_entries()))
             # saving and reloading should not affect this.
@@ -1038,7 +1038,7 @@
         state = dirstate.DirState.initialize('dirstate')
         try:
             self.assertRaises(errors.NotVersionedError, state.add,
-                'unversioned/a file', 'a file id', 'file', None, None)
+                'unversioned/a file', 'a-file-id', 'file', None, None)
         finally:
             state.unlock()
 
@@ -1115,7 +1115,7 @@
             (('', 'a dir', 'a dir id'), [
              ('d', '', 0, False, dirstate.pack_stat(dirstat)), # current tree
              ]),
-            (('a dir', 'a file', 'a file id'), [
+            (('a dir', 'a file', 'a-file-id'), [
              ('f', '1'*20, 25, False,
               dirstate.pack_stat(filestat)), # current tree details
              ]),
@@ -1123,7 +1123,7 @@
         state = dirstate.DirState.initialize('dirstate')
         try:
             state.add('a dir', 'a dir id', 'directory', dirstat, None)
-            state.add('a dir/a file', 'a file id', 'file', filestat, '1'*20)
+            state.add('a dir/a file', 'a-file-id', 'file', filestat, '1'*20)
             # added it, it should be in the output of iter_entries.
             self.assertEqual(expected_entries, list(state._iter_entries()))
             # saving and reloading should not affect this.

=== modified file 'bzrlib/tests/test_fetch.py'
--- a/bzrlib/tests/test_fetch.py	2007-10-16 16:02:01 +0000
+++ b/bzrlib/tests/test_fetch.py	2007-11-21 23:35:32 +0000
@@ -231,6 +231,8 @@
         br2 = Branch.open('br2')
         br1 = Branch.open('br1')
         wt2 = WorkingTree.open('br2').merge_from_branch(br1)
+        br2.lock_read()
+        self.addCleanup(br2.unlock)
         for rev_id, text in [('1-2', 'original from 1\n'),
                              ('1-3', 'agreement\n'),
                              ('2-1', 'contents in 2\n'),
@@ -264,7 +266,8 @@
 
     def test_weaves_are_retrieved_once(self):
         self.build_tree(("source/", "source/file", "target/"))
-        wt = self.make_branch_and_tree('source')
+        # This test depends on knit dasta storage.
+        wt = self.make_branch_and_tree('source', format='dirstate-tags')
         branch = wt.branch
         wt.add(["file"], ["id"])
         wt.commit("added file")

=== modified file 'bzrlib/tests/test_graph.py'
--- a/bzrlib/tests/test_graph.py	2007-10-22 05:44:49 +0000
+++ b/bzrlib/tests/test_graph.py	2007-11-21 23:39:40 +0000
@@ -162,7 +162,7 @@
     def make_graph(self, ancestors):
         tree = self.prepare_memory_tree('.')
         self.build_ancestry(tree, ancestors)
-        tree.unlock()
+        self.addCleanup(tree.unlock)
         return tree.branch.repository.get_graph()
 
     def prepare_memory_tree(self, location):
@@ -271,13 +271,14 @@
         """Ensure we do unique_lca using data from two repos"""
         mainline_tree = self.prepare_memory_tree('mainline')
         self.build_ancestry(mainline_tree, mainline)
-        mainline_tree.unlock()
+        self.addCleanup(mainline_tree.unlock)
 
         # This is cheating, because the revisions in the graph are actually
         # different revisions, despite having the same revision-id.
         feature_tree = self.prepare_memory_tree('feature')
         self.build_ancestry(feature_tree, feature_branch)
-        feature_tree.unlock()
+        self.addCleanup(feature_tree.unlock)
+
         graph = mainline_tree.branch.repository.get_graph(
             feature_tree.branch.repository)
         self.assertEqual('rev2b', graph.find_unique_lca('rev2a', 'rev3b'))

=== modified file 'bzrlib/tests/test_memorytree.py'
--- a/bzrlib/tests/test_memorytree.py	2007-11-13 16:42:56 +0000
+++ b/bzrlib/tests/test_memorytree.py	2007-11-21 23:42:50 +0000
@@ -143,6 +143,8 @@
         tree.unlock()
         # and we should have a revision that is accessible outside the tree lock
         revtree = tree.branch.repository.revision_tree(revision_id)
+        revtree.lock_read()
+        self.addCleanup(revtree.unlock)
         self.assertEqual('barshoom', revtree.get_file('foo-id').read())
 
     def test_unversion(self):

=== modified file 'bzrlib/tests/test_merge_core.py'
--- a/bzrlib/tests/test_merge_core.py	2007-08-29 16:09:51 +0000
+++ b/bzrlib/tests/test_merge_core.py	2007-11-21 23:57:02 +0000
@@ -725,6 +725,8 @@
         this, other = self.set_up_trees()
         self.assertRaises(errors.RevisionNotPresent, Merger.from_revision_ids,
                           progress.DummyProgress(), this, 'rev2b')
+        this.lock_write()
+        self.addCleanup(this.unlock)
         merger = Merger.from_revision_ids(progress.DummyProgress(), this,
             'rev2b', other_branch=other.branch)
         self.assertEqual('rev2b', merger.other_rev_id)
@@ -743,8 +745,12 @@
     def test_from_mergeable(self):
         this, other = self.set_up_trees()
         other.commit('rev3', rev_id='rev3')
+        this.lock_write()
+        self.addCleanup(this.unlock)
         md = merge_directive.MergeDirective2.from_objects(
             other.branch.repository, 'rev3', 0, 0, 'this')
+        other.lock_read()
+        self.addCleanup(other.unlock)
         merger, verified = Merger.from_mergeable(this, md,
             progress.DummyProgress())
         md.patch = None

=== modified file 'bzrlib/tests/test_merge_directive.py'
--- a/bzrlib/tests/test_merge_directive.py	2007-08-15 20:49:22 +0000
+++ b/bzrlib/tests/test_merge_directive.py	2007-11-22 00:05:12 +0000
@@ -565,9 +565,13 @@
     def from_objects(self, repository, revision_id, time, timezone,
         target_branch, patch_type='bundle', local_target_branch=None,
         public_branch=None, message=None):
-        return merge_directive.MergeDirective.from_objects(
-            repository, revision_id, time, timezone, target_branch,
-            patch_type, local_target_branch, public_branch, message)
+        repository.lock_write()
+        try:
+            return merge_directive.MergeDirective.from_objects( repository,
+                revision_id, time, timezone, target_branch, patch_type,
+                local_target_branch, public_branch, message)
+        finally:
+            repository.unlock()
 
     def make_merge_directive(self, revision_id, testament_sha1, time, timezone,
                  target_branch, patch=None, patch_type=None,

=== modified file 'bzrlib/transport/local.py'
--- a/bzrlib/transport/local.py	2007-10-04 22:00:07 +0000
+++ b/bzrlib/transport/local.py	2007-11-22 00:29:58 +0000
@@ -307,7 +307,10 @@
         """See Transport.open_write_stream."""
         # initialise the file
         self.put_bytes_non_atomic(relpath, "", mode=mode)
-        handle = open(self._abspath(relpath), 'wb')
+        abspath = self._abspath(relpath)
+        handle = open(abspath, 'wb')
+        if mode is not None:
+            self._check_mode_and_size(abspath, handle.fileno(), mode)
         transport._file_streams[self.abspath(relpath)] = handle
         return transport.FileFileStream(self, relpath, handle)
 




More information about the bazaar-commits mailing list