[PATCH] cascading lookup support in LocationConfig (bug 33430)

James Henstridge james at jamesh.id.au
Mon Sep 18 16:31:20 BST 2006


On 14/09/06, Aaron Bentley <aaron.bentley at utoronto.ca> wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> James Henstridge wrote:
> > The case I was thinking of was something like this:
> >    bzr branch http://... ~/work
> >    cd ~/work
> >    # hack on the branch
> >    bzr push sftp://...
> >    cd ~
> >    rm -rf ~/work
> >
> > Then a few days/weeks/months later:
> >    bzr branch http://... ~/work/subdir
> >    cd ~/work/subdir
> >    # hack on branch
> >    bzr push
> >
> > Here a push location was recorded for a previous unrelated branch that
> > will affect a the new branch.  So I'd expect the push location to
> > either (a) not inherit from ~/work's config or (b) use ~/work's config
> > but modify the URL based on the subdir I'm in (what you suggested).
>
> I think a) would be the best choice, and best served by using recurse=n
> in per-branch settings.  But it's really unfortunate to have the
> settings for ~/work hang around after the branch has been deleted.
> Because you can always create a new ~/work, and it will get the old
> settings.  This is one of the things that makes ./bzr/branch.conf nice.
>
> >> I think BranchConfig should set policy about whether particular values
> >> cascade.
> >
> > Then we'd need some way for other code to register what policy is
> > desired for particular keys.
>
> Let me stop here and say I don't mean "particular keys" per se.  I mean
> particular configuration values.
>
> So, for example, the policy for the user_id is set like this:
>
>     def _get_user_id(self):
>         """Return the full user id for the branch.
>
>         e.g. "John Hacker <jhacker at foo.org>"
>         This is looked up in the email controlfile for the branch.
>         """
>         try:
>             return (self.branch.control_files.get_utf8("email")
>                     .read()
>                     .decode(bzrlib.user_encoding)
>                     .rstrip("\r\n"))
>         except errors.NoSuchFile, e:
>             pass
>
>         return self._get_best_value('_get_user_id')
>
> But if you do branch_config.get_user_option('email'), it won't check the
> 'email' control file.  BranchConfig sets policy for the 'user_id' config
> value, not the 'email' key.
>
> Plugins don't need to set policy, because their config values are not
> reused by other code.  They can just choose the policy they want when
> they get/set the value.  (or, they *ought* to be able to)
>
> But if they want to set policy for their own use, they can subclass
> BranchConfig, as pqm-submit does.

As John mentioned, pqm-submit doesn't subclass BranchConfig anymore.
Furthermore, I don't think we should completely discount plugins
sharing configuration.

For example, the pqm-submit plugin has a "public_repository"
configuration key used to set the published location of a repository.
I wrote a "repo-push" plugin to push an repository plus the branches
it contains to another location, and rather than make up my own key it
made sense to reuse the pqm-submit key.

If the caller is going to be making policy decisions about how the key
is looked up (recusive vs. non-recursive), then it would be a bug for
two bits of code to use different policies.  It would be nice to catch
these sorts of problems.  Upfront registration of the policy is one
way of doing this.


> >> Can I ask what you think about the order of matching I've suggested?
> >> (i.e. exact LocationConfig, TreeConfig, recursive LocationConfig,
> >> GlobalConfig?)  I'm not really satisfied with it.  Although it seems
> >> correct, in a certain sense, it also seems confusing.
> >
> >
> > I'm not sure I can answer that.  I've never really used the
> > .bzr/branch.conf file directly at all.  Are there many options that
> > you'd want to have in both files?  Maybe this is another case where
> > the policy should be set on a key by key basis?
>
> When we discussed this in Montreal, I was pushing strongly for
> /bzr/branch, and Robert was pushing strongly for
> ~/.bazaar/locations.conf.  For example, I think 'push_location' should
> be in .bzr/branches.conf.
>
> We couldn't agree, so we decided to permit all values in all config files.
>
> There are a few values that must not appear in untrusted files, but
> aside from that, I think it still holds true.

I've attached an updated branch that exposes the choice of whether to
do a recursive lookup for the key or not in get_user_option().  I've
set the default for this argument to "True", which most closely
matches the current behaviour of using parent directory
configurations.

I haven't looked at updating any code to pick the recurse=False
policy, but Branch.get_push_location() is one candidate.


James.
-------------- next part --------------
# Bazaar revision bundle v0.8
#
# message:
#   make _get_matching_sections() return (section, extra_path) tuples, and adjust other code to match
# committer: James Henstridge <james at jamesh.id.au>
# date: Mon 2006-09-18 10:59:05.749000072 +0800

=== modified file bzrlib/config.py
--- bzrlib/config.py
+++ bzrlib/config.py
@@ -114,13 +114,13 @@
     def _get_signing_policy(self):
         """Template method to override signature creation policy."""
 
-    def _get_user_option(self, option_name):
+    def _get_user_option(self, option_name, recurse=True):
         """Template method to provide a user option."""
         return None
 
-    def get_user_option(self, option_name):
+    def get_user_option(self, option_name, recurse=True):
         """Get a generic option - no special process, no default."""
-        return self._get_user_option(option_name)
+        return self._get_user_option(option_name, recurse=recurse)
 
     def gpg_signing_command(self):
         """What program should be used to sign signatures?"""
@@ -255,6 +255,18 @@
             raise errors.ParseConfigError(e.errors, e.config.filename)
         return self._parser
 
+    def _get_matching_sections(self):
+        """Return an ordered list of (section_name, extra_path) pairs.
+
+        If the section contains inherited configuration, extra_path is
+        a string containing the additional path components.
+        """
+        section = self._get_section()
+        if section is not None:
+            return [(section, '')]
+        else:
+            return []
+
     def _get_section(self):
         """Override this to define the section used by the config."""
         return "DEFAULT"
@@ -275,13 +287,17 @@
         """Get the user id from the 'email' key in the current section."""
         return self._get_user_option('email')
 
-    def _get_user_option(self, option_name):
+    def _get_user_option(self, option_name, recurse=True):
         """See Config._get_user_option."""
-        try:
-            return self._get_parser().get_value(self._get_section(),
-                                                option_name)
-        except KeyError:
-            pass
+        for (section, extra_path) in self._get_matching_sections():
+            if not recurse and extra_path != '':
+                continue
+            try:
+                return self._get_parser().get_value(section, option_name)
+            except KeyError:
+                pass
+        else:
+            return None
 
     def _gpg_signing_command(self):
         """See Config.gpg_signing_command."""
@@ -379,13 +395,8 @@
             location = urlutils.local_path_from_url(location)
         self.location = location
 
-    def _get_section(self):
-        """Get the section we should look in for config items.
-
-        Returns None if none exists. 
-        TODO: perhaps return a NullSection that thunks through to the 
-              global config.
-        """
+    def _get_matching_sections(self):
+        """Return an ordered list of section names matching this location."""
         sections = self._get_parser()
         location_names = self.location.split('/')
         if self.location.endswith('/'):
@@ -415,18 +426,19 @@
             # if section is longer, no match.
             if len(section_names) > len(location_names):
                 continue
-            # if path is longer, and recurse is not true, no match
-            if len(section_names) < len(location_names):
-                try:
-                    if not self._get_parser()[section].as_bool('recurse'):
-                        continue
-                except KeyError:
-                    pass
-            matches.append((len(section_names), section))
-        if not len(matches):
-            return None
+            matches.append((len(section_names), section,
+                            '/'.join(location_names[len(section_names):])))
         matches.sort(reverse=True)
-        return matches[0][1]
+        sections = []
+        for (length, section, extra_path) in matches:
+            sections.append((section, extra_path))
+            # should we stop looking for parent configs here?
+            try:
+                if self._get_parser()[section].as_bool('ignore_parents'):
+                    break
+            except KeyError:
+                pass
+        return sections
 
     def set_user_option(self, option, value):
         """Save option and its value in the configuration."""
@@ -515,10 +527,10 @@
         """See Config._get_signing_policy."""
         return self._get_best_value('_get_signing_policy')
 
-    def _get_user_option(self, option_name):
+    def _get_user_option(self, option_name, recurse=True):
         """See Config._get_user_option."""
         for source in self.option_sources:
-            value = source()._get_user_option(option_name)
+            value = source()._get_user_option(option_name, recurse=recurse)
             if value is not None:
                 return value
         return None

=== modified file bzrlib/tests/test_config.py
--- bzrlib/tests/test_config.py
+++ bzrlib/tests/test_config.py
@@ -64,9 +64,9 @@
 sample_branches_text = ("[http://www.example.com]\n"
                         "# Top level policy\n"
                         "email=Robert Collins <robertc at example.org>\n"
-                        "[http://www.example.com/useglobal]\n"
-                        "# different project, forces global lookup\n"
-                        "recurse=false\n"
+                        "[http://www.example.com/ignoreparent]\n"
+                        "# different project: ignore parent dir config\n"
+                        "ignore_parents=true\n"
                         "[/b/]\n"
                         "check_signatures=require\n"
                         "# test trailing / matching with no children\n"
@@ -77,7 +77,6 @@
                         "# test trailing / matching\n"
                         "[/a/*]\n"
                         "#subdirs will match but not the parent\n"
-                        "recurse=False\n"
                         "[/a/c]\n"
                         "check_signatures=ignore\n"
                         "post_commit=bzrlib.tests.test_config.post_commit\n"
@@ -535,58 +534,66 @@
         self.failUnless(isinstance(global_config, config.GlobalConfig))
         self.failUnless(global_config is my_config._get_global_config())
 
-    def test__get_section_no_match(self):
+    def test__get_matching_sections_no_match(self):
         self.get_branch_config('/')
-        self.assertEqual(None, self.my_location_config._get_section())
+        self.assertEqual([], self.my_location_config._get_matching_sections())
         
-    def test__get_section_exact(self):
+    def test__get_matching_sections_exact(self):
         self.get_branch_config('http://www.example.com')
-        self.assertEqual('http://www.example.com',
-                         self.my_location_config._get_section())
+        self.assertEqual([('http://www.example.com', '')],
+                         self.my_location_config._get_matching_sections())
    
-    def test__get_section_suffix_does_not(self):
+    def test__get_matching_sections_suffix_does_not(self):
         self.get_branch_config('http://www.example.com-com')
-        self.assertEqual(None, self.my_location_config._get_section())
+        self.assertEqual([], self.my_location_config._get_matching_sections())
 
-    def test__get_section_subdir_recursive(self):
+    def test__get_matching_sections_subdir_recursive(self):
         self.get_branch_config('http://www.example.com/com')
-        self.assertEqual('http://www.example.com',
-                         self.my_location_config._get_section())
-
-    def test__get_section_subdir_matches(self):
-        self.get_branch_config('http://www.example.com/useglobal')
-        self.assertEqual('http://www.example.com/useglobal',
-                         self.my_location_config._get_section())
-
-    def test__get_section_subdir_nonrecursive(self):
+        self.assertEqual([('http://www.example.com', 'com')],
+                         self.my_location_config._get_matching_sections())
+
+    def test__get_matching_sections_subdir_matches(self):
+        self.get_branch_config('http://www.example.com/ignoreparent')
+        self.assertEqual([('http://www.example.com/ignoreparent', '')],
+                         self.my_location_config._get_matching_sections())
+
+    def test__get_matching_sections_subdir_nonrecursive(self):
         self.get_branch_config(
-            'http://www.example.com/useglobal/childbranch')
-        self.assertEqual('http://www.example.com',
-                         self.my_location_config._get_section())
+            'http://www.example.com/ignoreparent/childbranch')
+        self.assertEqual([('http://www.example.com/ignoreparent', 'childbranch')],
+                         self.my_location_config._get_matching_sections())
 
-    def test__get_section_subdir_trailing_slash(self):
+    def test__get_matching_sections_subdir_trailing_slash(self):
         self.get_branch_config('/b')
-        self.assertEqual('/b/', self.my_location_config._get_section())
+        self.assertEqual([('/b/', '')],
+                         self.my_location_config._get_matching_sections())
 
-    def test__get_section_subdir_child(self):
+    def test__get_matching_sections_subdir_child(self):
         self.get_branch_config('/a/foo')
-        self.assertEqual('/a/*', self.my_location_config._get_section())
+        self.assertEqual([('/a/*', ''), ('/a/', 'foo')],
+                         self.my_location_config._get_matching_sections())
 
-    def test__get_section_subdir_child_child(self):
+    def test__get_matching_sections_subdir_child_child(self):
         self.get_branch_config('/a/foo/bar')
-        self.assertEqual('/a/', self.my_location_config._get_section())
+        self.assertEqual([('/a/*', 'bar'), ('/a/', 'foo/bar')],
+                         self.my_location_config._get_matching_sections())
 
-    def test__get_section_trailing_slash_with_children(self):
+    def test__get_matching_sections_trailing_slash_with_children(self):
         self.get_branch_config('/a/')
-        self.assertEqual('/a/', self.my_location_config._get_section())
+        self.assertEqual([('/a/', '')],
+                         self.my_location_config._get_matching_sections())
 
-    def test__get_section_explicit_over_glob(self):
+    def test__get_matching_sections_explicit_over_glob(self):
+        # XXX: 2006-09-08 jamesh
+        # This test only passes because ord('c') > ord('*').  If there
+        # was a config section for '/a/?', it would get precedence
+        # over '/a/c'.
         self.get_branch_config('/a/c')
-        self.assertEqual('/a/c', self.my_location_config._get_section())
-
+        self.assertEqual([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')],
+                         self.my_location_config._get_matching_sections())
 
     def test_location_without_username(self):
-        self.get_branch_config('http://www.example.com/useglobal')
+        self.get_branch_config('http://www.example.com/ignoreparent')
         self.assertEqual(u'Erik B\u00e5gfors <erik at bagfors.nu>',
                          self.my_config.username())
 
@@ -641,7 +648,18 @@
         self.get_branch_config('/a')
         self.assertEqual('local',
                          self.my_config.get_user_option('user_local_option'))
-        
+
+    def test_get_user_option_non_recursive(self):
+        self.get_branch_config('/a/c')
+        # Doesn't see values from "/a/" config:
+        self.assertEqual(None,
+                         self.my_config.get_user_option('user_local_option',
+                                                        recurse=False))
+        # Sees values from global config:
+        self.assertEqual('vim',
+                         self.my_config.get_user_option('editor',
+                                                        recurse=False))
+
     def test_post_commit_default(self):
         self.get_branch_config('/a/c')
         self.assertEqual('bzrlib.tests.test_config.post_commit',

# revision id: james at jamesh.id.au-20060918025905-aa20656f1b0b34a0
# sha1: bb5e826b1cfaf27cb78e87a4658d7bc60d457703
# inventory sha1: c56709f16364ad6577324bef4ef43885c12d4d66
# parent ids:
#   james at jamesh.id.au-20060908043002-c7f13407e7d4109c
# base id: pqm at pqm.ubuntu.com-20060908003811-74eab872c372a895
# properties:
#   branch-nick: bzr.locationconfig

# message:
#   make test break again
# committer: James Henstridge <james at jamesh.id.au>
# date: Fri 2006-09-08 12:30:02.898999929 +0800

=== modified file bzrlib/tests/test_config.py // encoding:base64
LS0tIGJ6cmxpYi90ZXN0cy90ZXN0X2NvbmZpZy5weQorKysgYnpybGliL3Rlc3RzL3Rlc3RfY29u
ZmlnLnB5CkBAIC01OTYsNyArNTk2LDcgQEAKIAogCiAgICAgZGVmIHRlc3RfbG9jYXRpb25fd2l0
aG91dF91c2VybmFtZShzZWxmKToKLSAgICAgICAgc2VsZi5nZXRfYnJhbmNoX2NvbmZpZygnL2Iv
JykKKyAgICAgICAgc2VsZi5nZXRfYnJhbmNoX2NvbmZpZygnaHR0cDovL3d3dy5leGFtcGxlLmNv
bS91c2VnbG9iYWwnKQogICAgICAgICBzZWxmLmFzc2VydEVxdWFsKHUnRXJpayBCXHUwMGU1Z2Zv
cnMgPGVyaWtAYmFnZm9ycy5udT4nLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYubXlf
Y29uZmlnLnVzZXJuYW1lKCkpCiAKCg==

# revision id: james at jamesh.id.au-20060908043002-c7f13407e7d4109c
# sha1: 280af3599bf2ffeeae26a825da65c057faf1f400
# inventory sha1: fa00b1c04d5b16d16ec4662cc825f891452a1c45
# parent ids:
#   james at jamesh.id.au-20060908031106-1b304673906dcf8f
# properties:
#   branch-nick: bzr.locationconfig

# message:
#   first go at making location config lookup recursive
# committer: James Henstridge <james at jamesh.id.au>
# date: Fri 2006-09-08 11:11:06.072000027 +0800

=== modified file bzrlib/config.py // encoding:base64
LS0tIGJ6cmxpYi9jb25maWcucHkKKysrIGJ6cmxpYi9jb25maWcucHkKQEAgLTI4MSw3ICsyODEs
NyBAQAogICAgICAgICAgICAgcmV0dXJuIHNlbGYuX2dldF9wYXJzZXIoKS5nZXRfdmFsdWUoc2Vs
Zi5fZ2V0X3NlY3Rpb24oKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgIG9wdGlvbl9uYW1lKQogICAgICAgICBleGNlcHQgS2V5RXJyb3I6Ci0gICAgICAg
ICAgICBwYXNzCisgICAgICAgICAgICByZXR1cm4gTm9uZQogCiAgICAgZGVmIF9ncGdfc2lnbmlu
Z19jb21tYW5kKHNlbGYpOgogICAgICAgICAiIiJTZWUgQ29uZmlnLmdwZ19zaWduaW5nX2NvbW1h
bmQuIiIiCkBAIC0zNzksMTMgKzM3OSw4IEBACiAgICAgICAgICAgICBsb2NhdGlvbiA9IHVybHV0
aWxzLmxvY2FsX3BhdGhfZnJvbV91cmwobG9jYXRpb24pCiAgICAgICAgIHNlbGYubG9jYXRpb24g
PSBsb2NhdGlvbgogCi0gICAgZGVmIF9nZXRfc2VjdGlvbihzZWxmKToKLSAgICAgICAgIiIiR2V0
IHRoZSBzZWN0aW9uIHdlIHNob3VsZCBsb29rIGluIGZvciBjb25maWcgaXRlbXMuCi0KLSAgICAg
ICAgUmV0dXJucyBOb25lIGlmIG5vbmUgZXhpc3RzLiAKLSAgICAgICAgVE9ETzogcGVyaGFwcyBy
ZXR1cm4gYSBOdWxsU2VjdGlvbiB0aGF0IHRodW5rcyB0aHJvdWdoIHRvIHRoZSAKLSAgICAgICAg
ICAgICAgZ2xvYmFsIGNvbmZpZy4KLSAgICAgICAgIiIiCisgICAgZGVmIF9nZXRfbWF0Y2hpbmdf
c2VjdGlvbnMoc2VsZik6CisgICAgICAgICIiIlJldHVybiBhbiBvcmRlcmVkIGxpc3Qgb2Ygc2Vj
dGlvbiBuYW1lcyBtYXRjaGluZyB0aGlzIGxvY2F0aW9uLiIiIgogICAgICAgICBzZWN0aW9ucyA9
IHNlbGYuX2dldF9wYXJzZXIoKQogICAgICAgICBsb2NhdGlvbl9uYW1lcyA9IHNlbGYubG9jYXRp
b24uc3BsaXQoJy8nKQogICAgICAgICBpZiBzZWxmLmxvY2F0aW9uLmVuZHN3aXRoKCcvJyk6CkBA
IC00MjMsMTAgKzQxOCwxOCBAQAogICAgICAgICAgICAgICAgIGV4Y2VwdCBLZXlFcnJvcjoKICAg
ICAgICAgICAgICAgICAgICAgcGFzcwogICAgICAgICAgICAgbWF0Y2hlcy5hcHBlbmQoKGxlbihz
ZWN0aW9uX25hbWVzKSwgc2VjdGlvbikpCi0gICAgICAgIGlmIG5vdCBsZW4obWF0Y2hlcyk6Ci0g
ICAgICAgICAgICByZXR1cm4gTm9uZQogICAgICAgICBtYXRjaGVzLnNvcnQocmV2ZXJzZT1UcnVl
KQotICAgICAgICByZXR1cm4gbWF0Y2hlc1swXVsxXQorICAgICAgICByZXR1cm4gW3NlY3Rpb24g
Zm9yIChsZW5ndGgsIHNlY3Rpb24pIGluIG1hdGNoZXNdCisKKyAgICBkZWYgX2dldF91c2VyX29w
dGlvbihzZWxmLCBvcHRpb25fbmFtZSk6CisgICAgICAgICIiIlNlZSBDb25maWcuX2dldF91c2Vy
X29wdGlvbi4iIiIKKyAgICAgICAgZm9yIHNlY3Rpb24gaW4gc2VsZi5fZ2V0X21hdGNoaW5nX3Nl
Y3Rpb25zKCk6CisgICAgICAgICAgICB0cnk6CisgICAgICAgICAgICAgICAgcmV0dXJuIHNlbGYu
X2dldF9wYXJzZXIoKS5nZXRfdmFsdWUoc2VjdGlvbiwgb3B0aW9uX25hbWUpCisgICAgICAgICAg
ICBleGNlcHQgS2V5RXJyb3I6CisgICAgICAgICAgICAgICAgcGFzcworICAgICAgICBlbHNlOgor
ICAgICAgICAgICAgcmV0dXJuIE5vbmUKIAogICAgIGRlZiBzZXRfdXNlcl9vcHRpb24oc2VsZiwg
b3B0aW9uLCB2YWx1ZSk6CiAgICAgICAgICIiIlNhdmUgb3B0aW9uIGFuZCBpdHMgdmFsdWUgaW4g
dGhlIGNvbmZpZ3VyYXRpb24uIiIiCgo=

=== modified file bzrlib/tests/test_config.py // encoding:base64
LS0tIGJ6cmxpYi90ZXN0cy90ZXN0X2NvbmZpZy5weQorKysgYnpybGliL3Rlc3RzL3Rlc3RfY29u
ZmlnLnB5CkBAIC01MzUsNTggKzUzNSw2OCBAQAogICAgICAgICBzZWxmLmZhaWxVbmxlc3MoaXNp
bnN0YW5jZShnbG9iYWxfY29uZmlnLCBjb25maWcuR2xvYmFsQ29uZmlnKSkKICAgICAgICAgc2Vs
Zi5mYWlsVW5sZXNzKGdsb2JhbF9jb25maWcgaXMgbXlfY29uZmlnLl9nZXRfZ2xvYmFsX2NvbmZp
ZygpKQogCi0gICAgZGVmIHRlc3RfX2dldF9zZWN0aW9uX25vX21hdGNoKHNlbGYpOgorICAgIGRl
ZiB0ZXN0X19nZXRfbWF0Y2hpbmdfc2VjdGlvbnNfbm9fbWF0Y2goc2VsZik6CiAgICAgICAgIHNl
bGYuZ2V0X2JyYW5jaF9jb25maWcoJy8nKQotICAgICAgICBzZWxmLmFzc2VydEVxdWFsKE5vbmUs
IHNlbGYubXlfbG9jYXRpb25fY29uZmlnLl9nZXRfc2VjdGlvbigpKQorICAgICAgICBzZWxmLmFz
c2VydEVxdWFsKFtdLCBzZWxmLm15X2xvY2F0aW9uX2NvbmZpZy5fZ2V0X21hdGNoaW5nX3NlY3Rp
b25zKCkpCiAgICAgICAgIAotICAgIGRlZiB0ZXN0X19nZXRfc2VjdGlvbl9leGFjdChzZWxmKToK
KyAgICBkZWYgdGVzdF9fZ2V0X21hdGNoaW5nX3NlY3Rpb25zX2V4YWN0KHNlbGYpOgogICAgICAg
ICBzZWxmLmdldF9icmFuY2hfY29uZmlnKCdodHRwOi8vd3d3LmV4YW1wbGUuY29tJykKLSAgICAg
ICAgc2VsZi5hc3NlcnRFcXVhbCgnaHR0cDovL3d3dy5leGFtcGxlLmNvbScsCi0gICAgICAgICAg
ICAgICAgICAgICAgICAgc2VsZi5teV9sb2NhdGlvbl9jb25maWcuX2dldF9zZWN0aW9uKCkpCisg
ICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoWydodHRwOi8vd3d3LmV4YW1wbGUuY29tJ10sCisgICAg
ICAgICAgICAgICAgICAgICAgICAgc2VsZi5teV9sb2NhdGlvbl9jb25maWcuX2dldF9tYXRjaGlu
Z19zZWN0aW9ucygpKQogICAgCi0gICAgZGVmIHRlc3RfX2dldF9zZWN0aW9uX3N1ZmZpeF9kb2Vz
X25vdChzZWxmKToKKyAgICBkZWYgdGVzdF9fZ2V0X21hdGNoaW5nX3NlY3Rpb25zX3N1ZmZpeF9k
b2VzX25vdChzZWxmKToKICAgICAgICAgc2VsZi5nZXRfYnJhbmNoX2NvbmZpZygnaHR0cDovL3d3
dy5leGFtcGxlLmNvbS1jb20nKQotICAgICAgICBzZWxmLmFzc2VydEVxdWFsKE5vbmUsIHNlbGYu
bXlfbG9jYXRpb25fY29uZmlnLl9nZXRfc2VjdGlvbigpKQorICAgICAgICBzZWxmLmFzc2VydEVx
dWFsKFtdLCBzZWxmLm15X2xvY2F0aW9uX2NvbmZpZy5fZ2V0X21hdGNoaW5nX3NlY3Rpb25zKCkp
CiAKLSAgICBkZWYgdGVzdF9fZ2V0X3NlY3Rpb25fc3ViZGlyX3JlY3Vyc2l2ZShzZWxmKToKKyAg
ICBkZWYgdGVzdF9fZ2V0X21hdGNoaW5nX3NlY3Rpb25zX3N1YmRpcl9yZWN1cnNpdmUoc2VsZik6
CiAgICAgICAgIHNlbGYuZ2V0X2JyYW5jaF9jb25maWcoJ2h0dHA6Ly93d3cuZXhhbXBsZS5jb20v
Y29tJykKLSAgICAgICAgc2VsZi5hc3NlcnRFcXVhbCgnaHR0cDovL3d3dy5leGFtcGxlLmNvbScs
Ci0gICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5teV9sb2NhdGlvbl9jb25maWcuX2dldF9z
ZWN0aW9uKCkpCisgICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoWydodHRwOi8vd3d3LmV4YW1wbGUu
Y29tJ10sCisgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5teV9sb2NhdGlvbl9jb25maWcu
X2dldF9tYXRjaGluZ19zZWN0aW9ucygpKQogCi0gICAgZGVmIHRlc3RfX2dldF9zZWN0aW9uX3N1
YmRpcl9tYXRjaGVzKHNlbGYpOgorICAgIGRlZiB0ZXN0X19nZXRfbWF0Y2hpbmdfc2VjdGlvbnNf
c3ViZGlyX21hdGNoZXMoc2VsZik6CiAgICAgICAgIHNlbGYuZ2V0X2JyYW5jaF9jb25maWcoJ2h0
dHA6Ly93d3cuZXhhbXBsZS5jb20vdXNlZ2xvYmFsJykKLSAgICAgICAgc2VsZi5hc3NlcnRFcXVh
bCgnaHR0cDovL3d3dy5leGFtcGxlLmNvbS91c2VnbG9iYWwnLAotICAgICAgICAgICAgICAgICAg
ICAgICAgIHNlbGYubXlfbG9jYXRpb25fY29uZmlnLl9nZXRfc2VjdGlvbigpKQorICAgICAgICBz
ZWxmLmFzc2VydEVxdWFsKFsnaHR0cDovL3d3dy5leGFtcGxlLmNvbS91c2VnbG9iYWwnLAorICAg
ICAgICAgICAgICAgICAgICAgICAgICAnaHR0cDovL3d3dy5leGFtcGxlLmNvbSddLAorICAgICAg
ICAgICAgICAgICAgICAgICAgIHNlbGYubXlfbG9jYXRpb25fY29uZmlnLl9nZXRfbWF0Y2hpbmdf
c2VjdGlvbnMoKSkKIAotICAgIGRlZiB0ZXN0X19nZXRfc2VjdGlvbl9zdWJkaXJfbm9ucmVjdXJz
aXZlKHNlbGYpOgorICAgIGRlZiB0ZXN0X19nZXRfbWF0Y2hpbmdfc2VjdGlvbnNfc3ViZGlyX25v
bnJlY3Vyc2l2ZShzZWxmKToKICAgICAgICAgc2VsZi5nZXRfYnJhbmNoX2NvbmZpZygKICAgICAg
ICAgICAgICdodHRwOi8vd3d3LmV4YW1wbGUuY29tL3VzZWdsb2JhbC9jaGlsZGJyYW5jaCcpCi0g
ICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoJ2h0dHA6Ly93d3cuZXhhbXBsZS5jb20nLAotICAgICAg
ICAgICAgICAgICAgICAgICAgIHNlbGYubXlfbG9jYXRpb25fY29uZmlnLl9nZXRfc2VjdGlvbigp
KQorICAgICAgICBzZWxmLmFzc2VydEVxdWFsKFsnaHR0cDovL3d3dy5leGFtcGxlLmNvbSddLAor
ICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYubXlfbG9jYXRpb25fY29uZmlnLl9nZXRfbWF0
Y2hpbmdfc2VjdGlvbnMoKSkKIAotICAgIGRlZiB0ZXN0X19nZXRfc2VjdGlvbl9zdWJkaXJfdHJh
aWxpbmdfc2xhc2goc2VsZik6CisgICAgZGVmIHRlc3RfX2dldF9tYXRjaGluZ19zZWN0aW9uc19z
dWJkaXJfdHJhaWxpbmdfc2xhc2goc2VsZik6CiAgICAgICAgIHNlbGYuZ2V0X2JyYW5jaF9jb25m
aWcoJy9iJykKLSAgICAgICAgc2VsZi5hc3NlcnRFcXVhbCgnL2IvJywgc2VsZi5teV9sb2NhdGlv
bl9jb25maWcuX2dldF9zZWN0aW9uKCkpCisgICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoWycvYi8n
XSwKKyAgICAgICAgICAgICAgICAgICAgICAgICBzZWxmLm15X2xvY2F0aW9uX2NvbmZpZy5fZ2V0
X21hdGNoaW5nX3NlY3Rpb25zKCkpCiAKLSAgICBkZWYgdGVzdF9fZ2V0X3NlY3Rpb25fc3ViZGly
X2NoaWxkKHNlbGYpOgorICAgIGRlZiB0ZXN0X19nZXRfbWF0Y2hpbmdfc2VjdGlvbnNfc3ViZGly
X2NoaWxkKHNlbGYpOgogICAgICAgICBzZWxmLmdldF9icmFuY2hfY29uZmlnKCcvYS9mb28nKQot
ICAgICAgICBzZWxmLmFzc2VydEVxdWFsKCcvYS8qJywgc2VsZi5teV9sb2NhdGlvbl9jb25maWcu
X2dldF9zZWN0aW9uKCkpCisgICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoWycvYS8qJywgJy9hLydd
LAorICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYubXlfbG9jYXRpb25fY29uZmlnLl9nZXRf
bWF0Y2hpbmdfc2VjdGlvbnMoKSkKIAotICAgIGRlZiB0ZXN0X19nZXRfc2VjdGlvbl9zdWJkaXJf
Y2hpbGRfY2hpbGQoc2VsZik6CisgICAgZGVmIHRlc3RfX2dldF9tYXRjaGluZ19zZWN0aW9uc19z
dWJkaXJfY2hpbGRfY2hpbGQoc2VsZik6CiAgICAgICAgIHNlbGYuZ2V0X2JyYW5jaF9jb25maWco
Jy9hL2Zvby9iYXInKQotICAgICAgICBzZWxmLmFzc2VydEVxdWFsKCcvYS8nLCBzZWxmLm15X2xv
Y2F0aW9uX2NvbmZpZy5fZ2V0X3NlY3Rpb24oKSkKKyAgICAgICAgc2VsZi5hc3NlcnRFcXVhbChb
Jy9hLyddLAorICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYubXlfbG9jYXRpb25fY29uZmln
Ll9nZXRfbWF0Y2hpbmdfc2VjdGlvbnMoKSkKIAotICAgIGRlZiB0ZXN0X19nZXRfc2VjdGlvbl90
cmFpbGluZ19zbGFzaF93aXRoX2NoaWxkcmVuKHNlbGYpOgorICAgIGRlZiB0ZXN0X19nZXRfbWF0
Y2hpbmdfc2VjdGlvbnNfdHJhaWxpbmdfc2xhc2hfd2l0aF9jaGlsZHJlbihzZWxmKToKICAgICAg
ICAgc2VsZi5nZXRfYnJhbmNoX2NvbmZpZygnL2EvJykKLSAgICAgICAgc2VsZi5hc3NlcnRFcXVh
bCgnL2EvJywgc2VsZi5teV9sb2NhdGlvbl9jb25maWcuX2dldF9zZWN0aW9uKCkpCisgICAgICAg
IHNlbGYuYXNzZXJ0RXF1YWwoWycvYS8nXSwKKyAgICAgICAgICAgICAgICAgICAgICAgICBzZWxm
Lm15X2xvY2F0aW9uX2NvbmZpZy5fZ2V0X21hdGNoaW5nX3NlY3Rpb25zKCkpCiAKLSAgICBkZWYg
dGVzdF9fZ2V0X3NlY3Rpb25fZXhwbGljaXRfb3Zlcl9nbG9iKHNlbGYpOgorICAgIGRlZiB0ZXN0
X19nZXRfbWF0Y2hpbmdfc2VjdGlvbnNfZXhwbGljaXRfb3Zlcl9nbG9iKHNlbGYpOgorICAgICAg
ICAjIFhYWDogMjAwNi0wOS0wOCBqYW1lc2gKKyAgICAgICAgIyBUaGlzIHRlc3Qgb25seSBwYXNz
ZXMgYmVjYXVzZSBvcmQoJ2MnKSA+IG9yZCgnKicpLiAgSWYgdGhlcmUKKyAgICAgICAgIyB3YXMg
YSBjb25maWcgc2VjdGlvbiBmb3IgJy9hLz8nLCBpdCB3b3VsZCBnZXQgcHJlY2VkZW5jZQorICAg
ICAgICAjIG92ZXIgJy9hL2MnLgogICAgICAgICBzZWxmLmdldF9icmFuY2hfY29uZmlnKCcvYS9j
JykKLSAgICAgICAgc2VsZi5hc3NlcnRFcXVhbCgnL2EvYycsIHNlbGYubXlfbG9jYXRpb25fY29u
ZmlnLl9nZXRfc2VjdGlvbigpKQorICAgICAgICBzZWxmLmFzc2VydEVxdWFsKFsnL2EvYycsICcv
YS8qJywgJy9hLyddLAorICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYubXlfbG9jYXRpb25f
Y29uZmlnLl9nZXRfbWF0Y2hpbmdfc2VjdGlvbnMoKSkKIAogCiAgICAgZGVmIHRlc3RfbG9jYXRp
b25fd2l0aG91dF91c2VybmFtZShzZWxmKToKLSAgICAgICAgc2VsZi5nZXRfYnJhbmNoX2NvbmZp
ZygnaHR0cDovL3d3dy5leGFtcGxlLmNvbS91c2VnbG9iYWwnKQorICAgICAgICBzZWxmLmdldF9i
cmFuY2hfY29uZmlnKCcvYi8nKQogICAgICAgICBzZWxmLmFzc2VydEVxdWFsKHUnRXJpayBCXHUw
MGU1Z2ZvcnMgPGVyaWtAYmFnZm9ycy5udT4nLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNl
bGYubXlfY29uZmlnLnVzZXJuYW1lKCkpCiAKCg==

# revision id: james at jamesh.id.au-20060908031106-1b304673906dcf8f
# sha1: 8c8c290f4c1bae0d712d1718e7b5c09426f56e04
# inventory sha1: ac8be32a9018b3706d1a84cd327403aea9104d2d
# parent ids:
#   pqm at pqm.ubuntu.com-20060908003811-74eab872c372a895
# properties:
#   branch-nick: bzr.locationconfig


More information about the bazaar mailing list