Rev 4302: (abentley) store tree-references in a new branch format. in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Thu Apr 23 21:47:35 BST 2009


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

------------------------------------------------------------
revno: 4302
revision-id: pqm at pqm.ubuntu.com-20090423204730-kcbq9na0n3zs2y3l
parent: pqm at pqm.ubuntu.com-20090423015537-xfgqsbjj9ctpcd3o
parent: aaron at aaronbentley.com-20090423160228-gxb9fjel30ihjqm2
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Thu 2009-04-23 21:47:30 +0100
message:
  (abentley) store tree-references in a new branch format.
modified:
  bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
  bzrlib/bzrdir.py               bzrdir.py-20060131065624-156dfea39c4387cb
  bzrlib/merge.py                merge.py-20050513021216-953b65a438527106
  bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
  bzrlib/tests/test_branch.py    test_branch.py-20060116013032-97819aa07b8ab3b5
  bzrlib/tests/test_upgrade.py   test_upgrade.py-20051004040251-555fe1d2bae1bc71
    ------------------------------------------------------------
    revno: 4273.1.15
    revision-id: aaron at aaronbentley.com-20090423160228-gxb9fjel30ihjqm2
    parent: aaron at aaronbentley.com-20090422200825-nhpatpznn1rbfz39
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: branch-subtree-locations
    timestamp: Thu 2009-04-23 12:02:28 -0400
    message:
      Add reference_info caching.
    modified:
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/tests/test_branch.py    test_branch.py-20060116013032-97819aa07b8ab3b5
    ------------------------------------------------------------
    revno: 4273.1.14
    revision-id: aaron at aaronbentley.com-20090422200825-nhpatpznn1rbfz39
    parent: aaron at aaronbentley.com-20090420205851-r6s96npwkuto0n0q
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: branch-subtree-locations
    timestamp: Wed 2009-04-22 16:08:25 -0400
    message:
      Restore disabled test
    modified:
      bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
    ------------------------------------------------------------
    revno: 4273.1.13
    revision-id: aaron at aaronbentley.com-20090420205851-r6s96npwkuto0n0q
    parent: aaron at aaronbentley.com-20090414152901-r2i7jgu7gisu8hmy
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: branch-subtree-locations
    timestamp: Mon 2009-04-20 16:58:51 -0400
    message:
      Implement upgrade from branch format 7 to 8.
    modified:
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/bzrdir.py               bzrdir.py-20060131065624-156dfea39c4387cb
      bzrlib/tests/test_upgrade.py   test_upgrade.py-20051004040251-555fe1d2bae1bc71
    ------------------------------------------------------------
    revno: 4273.1.12
    revision-id: aaron at aaronbentley.com-20090414152901-r2i7jgu7gisu8hmy
    parent: aaron at aaronbentley.com-20090414152021-ggbp1i96z3w1jtpn
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: branch-subtree-locations
    timestamp: Tue 2009-04-14 11:29:01 -0400
    message:
      Don't create reference files for older formats.
    modified:
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/tests/test_branch.py    test_branch.py-20060116013032-97819aa07b8ab3b5
    ------------------------------------------------------------
    revno: 4273.1.11
    revision-id: aaron at aaronbentley.com-20090414152021-ggbp1i96z3w1jtpn
    parent: aaron at aaronbentley.com-20090414150646-442azwp8fhxi59b5
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: branch-subtree-locations
    timestamp: Tue 2009-04-14 11:20:21 -0400
    message:
      Clean up naming and docstrings
    modified:
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
    ------------------------------------------------------------
    revno: 4273.1.10
    revision-id: aaron at aaronbentley.com-20090414150646-442azwp8fhxi59b5
    parent: aaron at aaronbentley.com-20090414150246-6xfd3bwwf2m8v4px
    parent: pqm at pqm.ubuntu.com-20090414031543-gqbs23oebd68p7h7
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: branch-subtree-locations
    timestamp: Tue 2009-04-14 11:06:46 -0400
    message:
      Merge bzr.dev into branch-subtree-locations.
    added:
      bzrlib/tests/blackbox/test_dpush.py test_dpush.py-20090108125928-st1td6le59g0vyv2-1
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/_btree_serializer_c.pyx _parse_btree_c.pyx-20080703034413-3q25bklkenti3p8p-2
      bzrlib/branchbuilder.py        branchbuilder.py-20070427022007-zlxpqz2lannhk6y8-1
      bzrlib/btree_index.py          index.py-20080624222253-p0x5f92uyh5hw734-7
      bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
      bzrlib/bzrdir.py               bzrdir.py-20060131065624-156dfea39c4387cb
      bzrlib/config.py               config.py-20051011043216-070c74f4e9e338e8
      bzrlib/fetch.py                fetch.py-20050818234941-26fea6105696365d
      bzrlib/filters/__init__.py     __init__.py-20080416080515-mkxl29amuwrf6uir-2
      bzrlib/filters/eol.py          eol.py-20090327060429-todzdjmqt3bpv5r8-1
      bzrlib/foreign.py              foreign.py-20081112170002-olsxmandkk8qyfuq-1
      bzrlib/inventory.py            inventory.py-20050309040759-6648b84ca2005b37
      bzrlib/plugins/launchpad/__init__.py __init__.py-20060315182712-2d5feebd2a1032dc
      bzrlib/plugins/launchpad/account.py account.py-20071011033320-50y6vfftywf4yllw-1
      bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
      bzrlib/repofmt/groupcompress_repo.py repofmt.py-20080715094215-wp1qfvoo7093c8qr-1
      bzrlib/repository.py           rev_storage.py-20051111201905-119e9401e46257e3
      bzrlib/revision.py             revision.py-20050309040759-e77802c08f3999d5
      bzrlib/smtp_connection.py      smtp_connection.py-20070618204456-nu6wag1ste4biuk2-1
      bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
      bzrlib/tests/blackbox/__init__.py __init__.py-20051128053524-eba30d8255e08dc3
      bzrlib/tests/blackbox/test_ls.py test_ls.py-20060712232047-0jraqpecwngee12y-1
      bzrlib/tests/blackbox/test_push.py test_push.py-20060329002750-929af230d5d22663
      bzrlib/tests/blackbox/test_selftest.py test_selftest.py-20060123024542-01c5f1bbcb596d78
      bzrlib/tests/interrepository_implementations/__init__.py __init__.py-20060220054744-baf49a1f88f17b1a
      bzrlib/tests/interrepository_implementations/test_fetch.py test_fetch.py-20080425213627-j60cjh782ufm83ry-1
      bzrlib/tests/test_config.py    testconfig.py-20051011041908-742d0c15d8d8c8eb
      bzrlib/tests/test_foreign.py   test_foreign.py-20081125004048-ywb901edgp9lluxo-1
      bzrlib/tests/test_ftp_transport.py test_aftp_transport.-20060823221619-98mwjzxtwtkt527k-1
      bzrlib/tests/test_http.py      testhttp.py-20051018020158-b2eef6e867c514d9
      bzrlib/tests/test_pack_repository.py test_pack_repository-20080801043947-eaw0e6h2gu75kwmy-1
      bzrlib/tests/test_remote.py    test_remote.py-20060720103555-yeeg2x51vn0rbtdp-2
      bzrlib/tests/test_revision.py  testrevision.py-20050804210559-46f5e1eb67b01289
      bzrlib/tests/test_selftest.py  test_selftest.py-20051202044319-c110a115d8c0456a
      bzrlib/tests/test_sftp_transport.py testsftp.py-20051027032739-247570325fec7e7e
      bzrlib/tests/test_workingtree_4.py test_workingtree_4.p-20070223025758-531n3tznl3zacv2o-1
      bzrlib/tests/tree_implementations/__init__.py __init__.py-20060717075546-420s7b0bj9hzeowi-2
      bzrlib/tests/tree_implementations/test_get_symlink_target.py test_get_symlink_tar-20070225165554-ickod3w3t7u0zzqh-1
      bzrlib/tests/tree_implementations/test_inv.py test_inv.py-20070312023226-0cdvk5uwhutis9vg-1
      bzrlib/tests/tree_implementations/test_path_content_summary.py test_path_content_su-20070904100855-3vrwedz6akn34kl5-1
      bzrlib/tests/tree_implementations/test_test_trees.py test_tree_trees.py-20060720091921-3nwi5h21lf06vf5p-1
      bzrlib/tests/tree_implementations/test_walkdirs.py test_walkdirs.py-20060729160421-gmjnkotqgxdh98ce-1
      bzrlib/tests/workingtree_implementations/__init__.py __init__.py-20060203003124-b2aa5aca21a8bfad
      bzrlib/transport/ftp/__init__.py ftp.py-20051116161804-58dc9506548c2a53
      bzrlib/transport/http/_urllib2_wrappers.py _urllib2_wrappers.py-20060913231729-ha9ugi48ktx481ao-1
      bzrlib/transport/ssh.py        ssh.py-20060824042150-0s9787kng6zv1nwq-1
      bzrlib/ui/text.py              text.py-20051130153916-2e438cffc8afc478
    ------------------------------------------------------------
    revno: 4273.1.9
    revision-id: aaron at aaronbentley.com-20090414150246-6xfd3bwwf2m8v4px
    parent: aaron at aaronbentley.com-20090414145322-2z6fjycf3j17hjbm
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: branch-subtree-locations
    timestamp: Tue 2009-04-14 11:02:46 -0400
    message:
      Cleanup
    modified:
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/merge.py                merge.py-20050513021216-953b65a438527106
    ------------------------------------------------------------
    revno: 4273.1.8
    revision-id: aaron at aaronbentley.com-20090414145322-2z6fjycf3j17hjbm
    parent: aaron at aaronbentley.com-20090414135330-iowjjmz2g98nhcgb
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: branch-subtree-locations
    timestamp: Tue 2009-04-14 10:53:22 -0400
    message:
      Handle references in push, pull, merge.
    modified:
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/merge.py                merge.py-20050513021216-953b65a438527106
      bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
    ------------------------------------------------------------
    revno: 4273.1.7
    revision-id: aaron at aaronbentley.com-20090414135330-iowjjmz2g98nhcgb
    parent: aaron at aaronbentley.com-20090414132810-byuai43ht3t4htix
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: branch-subtree-locations
    timestamp: Tue 2009-04-14 09:53:30 -0400
    message:
      Make update_references do a merge.
    modified:
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
    ------------------------------------------------------------
    revno: 4273.1.6
    revision-id: aaron at aaronbentley.com-20090414132810-byuai43ht3t4htix
    parent: aaron at aaronbentley.com-20090409153149-p04wki1uuwrm2hsb
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: branch-subtree-locations
    timestamp: Tue 2009-04-14 09:28:10 -0400
    message:
      Ensure references are rebased.
    modified:
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
    ------------------------------------------------------------
    revno: 4273.1.5
    revision-id: aaron at aaronbentley.com-20090409153149-p04wki1uuwrm2hsb
    parent: aaron at aaronbentley.com-20090409143954-6beh111is9zesvqb
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: branch-subtree-locations
    timestamp: Thu 2009-04-09 11:31:49 -0400
    message:
      Ensure references are propagated by sprout/clone.
    modified:
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
    ------------------------------------------------------------
    revno: 4273.1.4
    revision-id: aaron at aaronbentley.com-20090409143954-6beh111is9zesvqb
    parent: aaron at aaronbentley.com-20090409134035-yzrbw9l6qjs7mniu
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: branch-subtree-locations
    timestamp: Thu 2009-04-09 10:39:54 -0400
    message:
      Relative reference locations are branch-relative.
    modified:
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
    ------------------------------------------------------------
    revno: 4273.1.3
    revision-id: aaron at aaronbentley.com-20090409134035-yzrbw9l6qjs7mniu
    parent: aaron at aaronbentley.com-20090408210701-l1sjtkomuy817ej3
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: branch-subtree-locations
    timestamp: Thu 2009-04-09 09:40:35 -0400
    message:
      Use branch-relative paths to references.
    modified:
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
    ------------------------------------------------------------
    revno: 4273.1.2
    revision-id: aaron at aaronbentley.com-20090408210701-l1sjtkomuy817ej3
    parent: aaron at aaronbentley.com-20090408204627-m4gbzrc255mhtjtu
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: branch-subtree-locations
    timestamp: Wed 2009-04-08 17:07:01 -0400
    message:
      Use reference_info to get reference_parent.
    modified:
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
    ------------------------------------------------------------
    revno: 4273.1.1
    revision-id: aaron at aaronbentley.com-20090408204627-m4gbzrc255mhtjtu
    parent: pqm at pqm.ubuntu.com-20090408143103-36oiu9zhl4r91hdq
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: branch-subtree-locations
    timestamp: Wed 2009-04-08 16:46:27 -0400
    message:
      Implement branch format for tree references.
    modified:
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
=== modified file 'bzrlib/branch.py'
--- a/bzrlib/branch.py	2009-04-15 07:30:34 +0000
+++ b/bzrlib/branch.py	2009-04-23 20:47:30 +0000
@@ -15,6 +15,7 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 
+from cStringIO import StringIO
 import sys
 
 from bzrlib.lazy_import import lazy_import
@@ -30,6 +31,7 @@
         lockable_files,
         repository,
         revision as _mod_revision,
+        rio,
         symbol_versioning,
         transport,
         tsort,
@@ -500,6 +502,14 @@
         """
         raise errors.UpgradeRequired(self.base)
 
+    def set_reference_info(self, file_id, tree_path, branch_location):
+        """Set the branch location to use for a tree reference."""
+        raise errors.UnsupportedOperation(self.set_reference_info, self)
+
+    def get_reference_info(self, file_id):
+        """Get the tree_path and branch_location for a tree reference."""
+        raise errors.UnsupportedOperation(self.get_reference_info, self)
+
     @needs_write_lock
     def fetch(self, from_branch, last_revision=None, pb=None):
         """Copy revisions from from_branch into this branch.
@@ -1070,6 +1080,7 @@
         revision_id: if not None, the revision history in the new branch will
                      be truncated to end with revision_id.
         """
+        self.update_references(destination)
         self._synchronize_history(destination, revision_id)
         try:
             parent = self.get_parent()
@@ -1081,6 +1092,23 @@
         if self._push_should_merge_tags():
             self.tags.merge_to(destination.tags)
 
+    def update_references(self, target):
+        if not getattr(self._format, 'supports_reference_locations', False):
+            return
+        reference_dict = self._get_all_reference_info()
+        if len(reference_dict) == 0:
+            return
+        old_base = self.base
+        new_base = target.base
+        target_reference_dict = target._get_all_reference_info()
+        for file_id, (tree_path, branch_location) in (
+            reference_dict.items()):
+            branch_location = urlutils.rebase_url(branch_location,
+                                                  old_base, new_base)
+            target_reference_dict.setdefault(
+                file_id, (tree_path, branch_location))
+        target._set_all_reference_info(target_reference_dict)
+
     @needs_read_lock
     def check(self):
         """Check consistency of the branch.
@@ -1205,14 +1233,15 @@
         reconciler.reconcile()
         return reconciler
 
-    def reference_parent(self, file_id, path):
+    def reference_parent(self, file_id, path, possible_transports=None):
         """Return the parent branch for a tree-reference file_id
         :param file_id: The file_id of the tree reference
         :param path: The path of the file_id in the tree
         :return: A branch associated with the file_id
         """
         # FIXME should provide multiple branches, based on config
-        return Branch.open(self.bzrdir.root_transport.clone(path).base)
+        return Branch.open(self.bzrdir.root_transport.clone(path).base,
+                           possible_transports=possible_transports)
 
     def supports_tags(self):
         return self._format.supports_tags()
@@ -1716,7 +1745,45 @@
 
 
 
-class BzrBranchFormat7(BranchFormatMetadir):
+class BzrBranchFormat8(BranchFormatMetadir):
+    """Metadir format supporting storing locations of subtree branches."""
+
+    def _branch_class(self):
+        return BzrBranch8
+
+    def get_format_string(self):
+        """See BranchFormat.get_format_string()."""
+        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
+
+    def get_format_description(self):
+        """See BranchFormat.get_format_description()."""
+        return "Branch format 8"
+
+    def initialize(self, a_bzrdir):
+        """Create a branch of this format in a_bzrdir."""
+        utf8_files = [('last-revision', '0 null:\n'),
+                      ('branch.conf', ''),
+                      ('tags', ''),
+                      ('references', '')
+                      ]
+        return self._initialize_helper(a_bzrdir, utf8_files)
+
+    def __init__(self):
+        super(BzrBranchFormat8, self).__init__()
+        self._matchingbzrdir.repository_format = \
+            RepositoryFormatKnitPack5RichRoot()
+
+    def make_tags(self, branch):
+        """See bzrlib.branch.BranchFormat.make_tags()."""
+        return BasicTags(branch)
+
+    def supports_stacking(self):
+        return True
+
+    supports_reference_locations = True
+
+
+class BzrBranchFormat7(BzrBranchFormat8):
     """Branch format with last-revision, tags, and a stacked location pointer.
 
     The stacked location pointer is passed down to the repository and requires
@@ -1725,6 +1792,14 @@
     This format was introduced in bzr 1.6.
     """
 
+    def initialize(self, a_bzrdir):
+        """Create a branch of this format in a_bzrdir."""
+        utf8_files = [('last-revision', '0 null:\n'),
+                      ('branch.conf', ''),
+                      ('tags', ''),
+                      ]
+        return self._initialize_helper(a_bzrdir, utf8_files)
+
     def _branch_class(self):
         return BzrBranch7
 
@@ -1736,25 +1811,7 @@
         """See BranchFormat.get_format_description()."""
         return "Branch format 7"
 
-    def initialize(self, a_bzrdir):
-        """Create a branch of this format in a_bzrdir."""
-        utf8_files = [('last-revision', '0 null:\n'),
-                      ('branch.conf', ''),
-                      ('tags', ''),
-                      ]
-        return self._initialize_helper(a_bzrdir, utf8_files)
-
-    def __init__(self):
-        super(BzrBranchFormat7, self).__init__()
-        self._matchingbzrdir.repository_format = \
-            RepositoryFormatKnitPack5RichRoot()
-
-    def make_tags(self, branch):
-        """See bzrlib.branch.BranchFormat.make_tags()."""
-        return BasicTags(branch)
-
-    def supports_stacking(self):
-        return True
+    supports_reference_locations = False
 
 
 class BranchReferenceFormat(BranchFormat):
@@ -1867,10 +1924,12 @@
 __format5 = BzrBranchFormat5()
 __format6 = BzrBranchFormat6()
 __format7 = BzrBranchFormat7()
+__format8 = BzrBranchFormat8()
 BranchFormat.register_format(__format5)
 BranchFormat.register_format(BranchReferenceFormat())
 BranchFormat.register_format(__format6)
 BranchFormat.register_format(__format7)
+BranchFormat.register_format(__format8)
 BranchFormat.set_default_format(__format6)
 _legacy_formats = [BzrBranchFormat4(),
     ]
@@ -2119,6 +2178,7 @@
         try:
             # We assume that during 'pull' the local repository is closer than
             # the remote one.
+            source.update_references(self)
             graph = self.repository.get_graph(source.repository)
             result.old_revno, result.old_revid = self.last_revision_info()
             self.update_revisions(source, stop_revision, overwrite=overwrite,
@@ -2221,6 +2281,7 @@
         result.source_branch = self
         result.target_branch = target
         result.old_revno, result.old_revid = target.last_revision_info()
+        self.update_references(target)
         if result.old_revid != self.last_revision():
             # We assume that during 'push' this repository is closer than
             # the target.
@@ -2376,8 +2437,8 @@
         return None
 
 
-class BzrBranch7(BzrBranch5):
-    """A branch with support for a fallback repository."""
+class BzrBranch8(BzrBranch5):
+    """A branch that stores tree-reference locations."""
 
     def _open_hook(self):
         if self._ignore_fallbacks:
@@ -2399,14 +2460,16 @@
 
     def __init__(self, *args, **kwargs):
         self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
-        super(BzrBranch7, self).__init__(*args, **kwargs)
+        super(BzrBranch8, self).__init__(*args, **kwargs)
         self._last_revision_info_cache = None
         self._partial_revision_history_cache = []
+        self._reference_info = None
 
     def _clear_cached_state(self):
-        super(BzrBranch7, self)._clear_cached_state()
+        super(BzrBranch8, self)._clear_cached_state()
         self._last_revision_info_cache = None
         self._partial_revision_history_cache = []
+        self._reference_info = None
 
     def _last_revision_info(self):
         revision_string = self._transport.get_bytes('last-revision')
@@ -2522,6 +2585,81 @@
         """Set the parent branch"""
         return self._get_config_location('parent_location')
 
+    @needs_write_lock
+    def _set_all_reference_info(self, info_dict):
+        """Replace all reference info stored in a branch.
+
+        :param info_dict: A dict of {file_id: (tree_path, branch_location)}
+        """
+        s = StringIO()
+        writer = rio.RioWriter(s)
+        for key, (tree_path, branch_location) in info_dict.iteritems():
+            stanza = rio.Stanza(file_id=key, tree_path=tree_path,
+                                branch_location=branch_location)
+            writer.write_stanza(stanza)
+        self._transport.put_bytes('references', s.getvalue())
+        self._reference_info = info_dict
+
+    @needs_read_lock
+    def _get_all_reference_info(self):
+        """Return all the reference info stored in a branch.
+
+        :return: A dict of {file_id: (tree_path, branch_location)}
+        """
+        if self._reference_info is not None:
+            return self._reference_info
+        rio_file = self._transport.get('references')
+        try:
+            stanzas = rio.read_stanzas(rio_file)
+            info_dict = dict((s['file_id'], (s['tree_path'],
+                             s['branch_location'])) for s in stanzas)
+        finally:
+            rio_file.close()
+        self._reference_info = info_dict
+        return info_dict
+
+    def set_reference_info(self, file_id, tree_path, branch_location):
+        """Set the branch location to use for a tree reference.
+
+        :param file_id: The file-id of the tree reference.
+        :param tree_path: The path of the tree reference in the tree.
+        :param branch_location: The location of the branch to retrieve tree
+            references from.
+        """
+        info_dict = self._get_all_reference_info()
+        info_dict[file_id] = (tree_path, branch_location)
+        if None in (tree_path, branch_location):
+            if tree_path is not None:
+                raise ValueError('tree_path must be None when branch_location'
+                                 ' is None.')
+            if branch_location is not None:
+                raise ValueError('branch_location must be None when tree_path'
+                                 ' is None.')
+            del info_dict[file_id]
+        self._set_all_reference_info(info_dict)
+
+    def get_reference_info(self, file_id):
+        """Get the tree_path and branch_location for a tree reference.
+
+        :return: a tuple of (tree_path, branch_location)
+        """
+        return self._get_all_reference_info().get(file_id, (None, None))
+
+    def reference_parent(self, file_id, path, possible_transports=None):
+        """Return the parent branch for a tree-reference file_id.
+
+        :param file_id: The file_id of the tree reference
+        :param path: The path of the file_id in the tree
+        :return: A branch associated with the file_id
+        """
+        branch_location = self.get_reference_info(file_id)[1]
+        if branch_location is None:
+            return Branch.reference_parent(self, file_id, path,
+                                           possible_transports)
+        branch_location = urlutils.join(self.base, branch_location)
+        return Branch.open(branch_location,
+                           possible_transports=possible_transports)
+
     def set_push_location(self, location):
         """See Branch.set_push_location."""
         self._set_config_location('push_location', location)
@@ -2625,6 +2763,20 @@
         return self.revno() - index
 
 
+class BzrBranch7(BzrBranch8):
+    """A branch with support for a fallback repository."""
+
+    def set_reference_info(self, file_id, tree_path, branch_location):
+        Branch.set_reference_info(self, file_id, tree_path, branch_location)
+
+    def get_reference_info(self, file_id):
+        Branch.get_reference_info(self, file_id)
+
+    def reference_parent(self, file_id, path, possible_transports=None):
+        return Branch.reference_parent(self, file_id, path,
+                                       possible_transports)
+
+
 class BzrBranch6(BzrBranch7):
     """See BzrBranchFormat6 for the capabilities of this branch.
 
@@ -2771,6 +2923,15 @@
         branch._transport.put_bytes('format', format.get_format_string())
 
 
+class Converter7to8(object):
+    """Perform an in-place upgrade of format 6 to format 7"""
+
+    def convert(self, branch):
+        format = BzrBranchFormat8()
+        branch._transport.put_bytes('references', '')
+        # update target format
+        branch._transport.put_bytes('format', format.get_format_string())
+
 
 def _run_with_write_locked_target(target, callable, *args, **kwargs):
     """Run ``callable(*args, **kwargs)``, write-locking target for the

=== modified file 'bzrlib/bzrdir.py'
--- a/bzrlib/bzrdir.py	2009-04-14 02:35:07 +0000
+++ b/bzrlib/bzrdir.py	2009-04-23 20:47:30 +0000
@@ -2788,11 +2788,16 @@
             while old != new:
                 if (old == _mod_branch.BzrBranchFormat5 and
                     new in (_mod_branch.BzrBranchFormat6,
-                        _mod_branch.BzrBranchFormat7)):
+                        _mod_branch.BzrBranchFormat7,
+                        _mod_branch.BzrBranchFormat8)):
                     branch_converter = _mod_branch.Converter5to6()
                 elif (old == _mod_branch.BzrBranchFormat6 and
-                    new == _mod_branch.BzrBranchFormat7):
+                    new in (_mod_branch.BzrBranchFormat7,
+                            _mod_branch.BzrBranchFormat8)):
                     branch_converter = _mod_branch.Converter6to7()
+                elif (old == _mod_branch.BzrBranchFormat7 and
+                      new is _mod_branch.BzrBranchFormat8):
+                    branch_converter = _mod_branch.Converter7to8()
                 else:
                     raise errors.BadConversionTarget("No converter", new)
                 branch_converter.convert(branch)

=== modified file 'bzrlib/merge.py'
--- a/bzrlib/merge.py	2009-04-04 02:50:01 +0000
+++ b/bzrlib/merge.py	2009-04-14 15:02:46 +0000
@@ -462,6 +462,8 @@
                                **kwargs)
 
     def _do_merge_to(self, merge):
+        if self.other_branch is not None:
+            self.other_branch.update_references(self.this_branch)
         merge.do_merge()
         if self.recurse == 'down':
             for relpath, file_id in self.this_tree.iter_references():

=== modified file 'bzrlib/tests/branch_implementations/test_branch.py'
--- a/bzrlib/tests/branch_implementations/test_branch.py	2009-03-24 06:40:26 +0000
+++ b/bzrlib/tests/branch_implementations/test_branch.py	2009-04-22 20:08:25 +0000
@@ -24,6 +24,7 @@
     bzrdir,
     errors,
     gpg,
+    merge,
     urlutils,
     transactions,
     remote,
@@ -769,8 +770,202 @@
         self.get_transport('').rename('fallback', 'moved')
         reopened = stacked.bzrdir.open_branch(ignore_fallbacks=True)
         self.assertEqual([], reopened.repository._fallback_repositories)
-        
+
     def test_fallbacks_are_opened(self):
         stacked = self.make_branch_with_fallback()
         reopened = stacked.bzrdir.open_branch(ignore_fallbacks=False)
         self.assertLength(1, reopened.repository._fallback_repositories)
+
+
+class TestReferenceLocation(TestCaseWithBranch):
+
+    def test_reference_parent(self):
+        tree = self.make_branch_and_tree('tree')
+        subtree = self.make_branch_and_tree('tree/subtree')
+        subtree.set_root_id('subtree-id')
+        try:
+            tree.add_reference(subtree)
+        except bzrlib.errors.UnsupportedOperation:
+            raise tests.TestNotApplicable('Tree cannot hold references.')
+        reference_parent = tree.branch.reference_parent('subtree-id',
+                                                        'subtree')
+        self.assertEqual(subtree.branch.base, reference_parent.base)
+
+    def test_reference_parent_accepts_possible_transports(self):
+        tree = self.make_branch_and_tree('tree')
+        subtree = self.make_branch_and_tree('tree/subtree')
+        subtree.set_root_id('subtree-id')
+        try:
+            tree.add_reference(subtree)
+        except bzrlib.errors.UnsupportedOperation:
+            raise tests.TestNotApplicable('Tree cannot hold references.')
+        reference_parent = tree.branch.reference_parent('subtree-id',
+            'subtree', possible_transports=[subtree.bzrdir.root_transport])
+
+    def test_get_reference_info(self):
+        branch = self.make_branch('branch')
+        try:
+            path, loc = branch.get_reference_info('file-id')
+        except bzrlib.errors.UnsupportedOperation:
+            raise tests.TestNotApplicable('Branch cannot hold references.')
+        self.assertIs(None, path)
+        self.assertIs(None, loc)
+
+    def test_set_reference_info(self):
+        branch = self.make_branch('branch')
+        try:
+            branch.set_reference_info('file-id', 'path/to/location',
+                                      'path/to/file')
+        except bzrlib.errors.UnsupportedOperation:
+            raise tests.TestNotApplicable('Branch cannot hold references.')
+
+    def test_set_get_reference_info(self):
+        branch = self.make_branch('branch')
+        try:
+            branch.set_reference_info('file-id', 'path/to/file',
+                                      'path/to/location')
+        except bzrlib.errors.UnsupportedOperation:
+            raise tests.TestNotApplicable('Branch cannot hold references.')
+        # Create a new instance to ensure storage is permanent
+        branch = Branch.open('branch')
+        tree_path, branch_location = branch.get_reference_info('file-id')
+        self.assertEqual('path/to/location', branch_location)
+
+    def test_set_null_reference_info(self):
+        branch = self.make_branch('branch')
+        try:
+            branch.set_reference_info('file-id', 'path/to/file',
+                                      'path/to/location')
+        except bzrlib.errors.UnsupportedOperation:
+            raise tests.TestNotApplicable('Branch cannot hold references.')
+        branch.set_reference_info('file-id', None, None)
+        tree_path, branch_location = branch.get_reference_info('file-id')
+        self.assertIs(None, tree_path)
+        self.assertIs(None, branch_location)
+
+    def test_set_null_reference_info_when_null(self):
+        branch = self.make_branch('branch')
+        try:
+            tree_path, branch_location = branch.get_reference_info('file-id')
+        except bzrlib.errors.UnsupportedOperation:
+            raise tests.TestNotApplicable('Branch cannot hold references.')
+        self.assertIs(None, tree_path)
+        self.assertIs(None, branch_location)
+        branch.set_reference_info('file-id', None, None)
+
+    def test_set_null_requires_two_nones(self):
+        branch = self.make_branch('branch')
+        try:
+            e = self.assertRaises(ValueError, branch.set_reference_info,
+                                  'file-id', 'path', None)
+        except bzrlib.errors.UnsupportedOperation:
+            raise tests.TestNotApplicable('Branch cannot hold references.')
+        self.assertEqual('tree_path must be None when branch_location is'
+                         ' None.', str(e))
+        e = self.assertRaises(ValueError, branch.set_reference_info,
+                              'file-id', None, 'location')
+        self.assertEqual('branch_location must be None when tree_path is'
+                         ' None.', str(e))
+
+    def make_branch_with_reference(self, location, reference_location,
+                                   file_id='file-id'):
+        branch = self.make_branch(location)
+        try:
+            branch.set_reference_info(file_id, 'path/to/file',
+                                      reference_location)
+        except bzrlib.errors.UnsupportedOperation:
+            raise tests.TestNotApplicable('Branch cannot hold references.')
+        return branch
+
+    def test_reference_parent_from_reference_info_(self):
+        referenced_branch = self.make_branch('reference_branch')
+        branch = self.make_branch_with_reference('branch',
+                                                 referenced_branch.base)
+        parent = branch.reference_parent('file-id', 'path/to/file')
+        self.assertEqual(parent.base, referenced_branch.base)
+
+    def test_branch_relative_reference_location(self):
+        branch = self.make_branch('branch')
+        try:
+            branch.set_reference_info('file-id', 'path/to/file',
+            '../reference_branch')
+        except bzrlib.errors.UnsupportedOperation:
+            raise tests.TestNotApplicable('Branch cannot hold references.')
+        referenced_branch = self.make_branch('reference_branch')
+        parent = branch.reference_parent('file-id', 'path/to/file')
+        self.assertEqual(parent.base, referenced_branch.base)
+
+    def test_sprout_copies_reference_location(self):
+        branch = self.make_branch_with_reference('branch', '../reference')
+        new_branch = branch.bzrdir.sprout('new-branch').open_branch()
+        self.assertEqual('../reference',
+                         new_branch.get_reference_info('file-id')[1])
+
+    def test_clone_copies_reference_location(self):
+        branch = self.make_branch_with_reference('branch', '../reference')
+        new_branch = branch.bzrdir.clone('new-branch').open_branch()
+        self.assertEqual('../reference',
+                         new_branch.get_reference_info('file-id')[1])
+
+    def test_copied_locations_are_rebased(self):
+        branch = self.make_branch_with_reference('branch', 'reference')
+        new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
+        self.assertEqual('../reference',
+                         new_branch.get_reference_info('file-id')[1])
+
+    def test_update_references_retains_old_references(self):
+        branch = self.make_branch_with_reference('branch', 'reference')
+        new_branch = self.make_branch_with_reference(
+            'new_branch', 'reference', 'file-id2')
+        new_branch.update_references(branch)
+        self.assertEqual('reference',
+                         branch.get_reference_info('file-id')[1])
+
+    def test_update_references_retains_known_references(self):
+        branch = self.make_branch_with_reference('branch', 'reference')
+        new_branch = self.make_branch_with_reference(
+            'new_branch', 'reference2')
+        new_branch.update_references(branch)
+        self.assertEqual('reference',
+                         branch.get_reference_info('file-id')[1])
+
+    def test_update_references_skips_known_references(self):
+        branch = self.make_branch_with_reference('branch', 'reference')
+        new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
+        new_branch.set_reference_info('file-id', '../foo', '../foo')
+        new_branch.update_references(branch)
+        self.assertEqual('reference',
+                         branch.get_reference_info('file-id')[1])
+
+    def test_pull_updates_references(self):
+        branch = self.make_branch_with_reference('branch', 'reference')
+        new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
+        new_branch.set_reference_info('file-id2', '../foo', '../foo')
+        branch.pull(new_branch)
+        self.assertEqual('foo',
+                         branch.get_reference_info('file-id2')[1])
+
+    def test_push_updates_references(self):
+        branch = self.make_branch_with_reference('branch', 'reference')
+        new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
+        new_branch.set_reference_info('file-id2', '../foo', '../foo')
+        new_branch.push(branch)
+        self.assertEqual('foo',
+                         branch.get_reference_info('file-id2')[1])
+
+    def test_merge_updates_references(self):
+        branch = self.make_branch_with_reference('branch', 'reference')
+        tree = self.make_branch_and_tree('tree')
+        tree.commit('foo')
+        branch.pull(tree.branch)
+        checkout = branch.create_checkout('checkout', lightweight=True)
+        checkout.commit('bar')
+        tree.lock_write()
+        self.addCleanup(tree.unlock)
+        merger = merge.Merger.from_revision_ids(None, tree,
+                                                branch.last_revision(),
+                                                other_branch=branch)
+        merger.merge_type = merge.Merge3Merger
+        merger.do_merge()
+        self.assertEqual('../branch/reference',
+                         tree.branch.get_reference_info('file-id')[1])

=== modified file 'bzrlib/tests/test_branch.py'
--- a/bzrlib/tests/test_branch.py	2009-04-08 03:34:31 +0000
+++ b/bzrlib/tests/test_branch.py	2009-04-23 16:02:28 +0000
@@ -228,6 +228,7 @@
         branch = self.make_branch('a', format=self.get_format_name())
         self.failUnlessExists('a/.bzr/branch/last-revision')
         self.failIfExists('a/.bzr/branch/revision-history')
+        self.failIfExists('a/.bzr/branch/references')
 
     def test_config(self):
         """Ensure that all configuration data is stored in the branch"""
@@ -378,6 +379,67 @@
         self.assertTrue(branch.repository.has_revision(revid))
 
 
+class BzrBranch8(TestCaseWithTransport):
+
+    def make_branch(self, location, format=None):
+        if format is None:
+            format = bzrdir.format_registry.make_bzrdir('1.9')
+            format.set_branch_format(_mod_branch.BzrBranchFormat8())
+        return TestCaseWithTransport.make_branch(self, location, format=format)
+
+    def create_branch_with_reference(self):
+        branch = self.make_branch('branch')
+        branch._set_all_reference_info({'file-id': ('path', 'location')})
+        return branch
+
+    @staticmethod
+    def instrument_branch(branch, gets):
+        old_get = branch._transport.get
+        def get(*args, **kwargs):
+            gets.append((args, kwargs))
+            return old_get(*args, **kwargs)
+        branch._transport.get = get
+
+    def test_reference_info_caching_read_locked(self):
+        gets = []
+        branch = self.create_branch_with_reference()
+        branch.lock_read()
+        self.addCleanup(branch.unlock)
+        self.instrument_branch(branch, gets)
+        branch.get_reference_info('file-id')
+        branch.get_reference_info('file-id')
+        self.assertEqual(1, len(gets))
+
+    def test_reference_info_caching_read_unlocked(self):
+        gets = []
+        branch = self.create_branch_with_reference()
+        self.instrument_branch(branch, gets)
+        branch.get_reference_info('file-id')
+        branch.get_reference_info('file-id')
+        self.assertEqual(2, len(gets))
+
+    def test_reference_info_caching_write_locked(self):
+        gets = []
+        branch = self.make_branch('branch')
+        branch.lock_write()
+        self.instrument_branch(branch, gets)
+        self.addCleanup(branch.unlock)
+        branch._set_all_reference_info({'file-id': ('path2', 'location2')})
+        path, location = branch.get_reference_info('file-id')
+        self.assertEqual(0, len(gets))
+        self.assertEqual('path2', path)
+        self.assertEqual('location2', location)
+
+    def test_reference_info_caches_cleared(self):
+        branch = self.make_branch('branch')
+        branch.lock_write()
+        branch.set_reference_info('file-id', 'path2', 'location2')
+        branch.unlock()
+        doppelganger = Branch.open('branch')
+        doppelganger.set_reference_info('file-id', 'path3', 'location3')
+        self.assertEqual(('path3', 'location3'),
+                         branch.get_reference_info('file-id'))
+
 class TestBranchReference(TestCaseWithTransport):
     """Tests for the branch reference facility."""
 

=== modified file 'bzrlib/tests/test_upgrade.py'
--- a/bzrlib/tests/test_upgrade.py	2009-03-28 14:24:46 +0000
+++ b/bzrlib/tests/test_upgrade.py	2009-04-20 20:58:51 +0000
@@ -201,6 +201,16 @@
         branch2 = _mod_branch.Branch.open(self.get_url('branch'))
         self.assertIs(branch2.__class__, _mod_branch.BzrBranch6)
 
+    def test_convert_branch7_branch8(self):
+        branch = self.make_branch('branch', format='1.9')
+        target = bzrdir.format_registry.make_bzrdir('1.9')
+        target.set_branch_format(_mod_branch.BzrBranchFormat8())
+        converter = branch.bzrdir._format.get_converter(target)
+        converter.convert(branch.bzrdir, progress.DummyProgress())
+        branch = _mod_branch.Branch.open(self.get_url('branch'))
+        self.assertIs(branch.__class__, _mod_branch.BzrBranch8)
+        self.assertEqual({}, branch._get_all_reference_info())
+
     def test_convert_knit_dirstate_empty(self):
         # test that asking for an upgrade from knit to dirstate works.
         tree = self.make_branch_and_tree('tree', format='knit')




More information about the bazaar-commits mailing list