Index: bitten/admin.py
===================================================================
--- bitten/admin.py	(revision 816)
+++ bitten/admin.py	(working copy)
@@ -175,7 +175,7 @@
                 # Prepare template variables
                 data['config'] = {
                     'name': config.name, 'label': config.label or config.name,
-                    'active': config.active, 'path': config.path,
+                    'active': config.active, 'paths': config.paths,
                     'min_rev': config.min_rev, 'max_rev': config.max_rev,
                     'description': config.description,
                     'recipe': config.recipe,
@@ -207,7 +207,7 @@
             for config in BuildConfig.select(self.env, include_inactive=True):
                 configs.append({
                     'name': config.name, 'label': config.label or config.name,
-                    'active': config.active, 'path': config.path,
+                    'active': config.active, 'paths': config.paths,
                     'min_rev': config.min_rev, 'max_rev': config.max_rev,
                     'href': req.href.admin('bitten', 'configs', config.name),
                     'recipe': config.recipe and True or False
@@ -273,17 +273,19 @@
             warnings.append('The field "name" may only contain letters, '
                             'digits, periods, or dashes.')
 
-        path = req.args.get('path', '')
+        paths = [p.strip() for p in req.args.get('paths', '').split('\n')]
         repos = self.env.get_repository(req.authname)
         max_rev = req.args.get('max_rev') or None
         try:
-            node = repos.get_node(path, max_rev)
-            assert node.isdir, '%s is not a directory' % node.path
+            for path in paths:
+                node = repos.get_node(path, max_rev)
+                assert node.isdir, '%s is not a directory' % node.path
         except (AssertionError, TracError), e:
             warnings.append('Invalid Repository Path "%s".' % path)
         if req.args.get('min_rev'):
             try:
-                repos.get_node(path, req.args.get('min_rev'))
+                for path in paths:
+                    repos.get_node(path, req.args.get('min_rev'))
             except TracError, e:
                 warnings.append('Invalid Oldest Revision: %s.' % unicode(e))
 
@@ -297,7 +299,7 @@
                 warnings.append('Invalid Recipe: %s.' % unicode(e))
 
         config.name = name
-        config.path = repos.normalize_path(path)
+        config.paths = paths
         config.recipe = recipe_xml
         config.min_rev = req.args.get('min_rev')
         config.max_rev = req.args.get('max_rev')
Index: bitten/master.py
===================================================================
--- bitten/master.py	(revision 816)
+++ bitten/master.py	(working copy)
@@ -227,7 +227,7 @@
             listener.build_started(build)
 
         xml = xmlio.parse(config.recipe)
-        xml.attr['path'] = config.path
+        xml.attr['path'] = config.paths[0]
         xml.attr['revision'] = build.rev
         xml.attr['config'] = config.name
         xml.attr['build'] = str(build.id)
Index: bitten/model.py
===================================================================
--- bitten/model.py	(revision 816)
+++ bitten/model.py	(working copy)
@@ -31,7 +31,7 @@
         ]
     ]
 
-    def __init__(self, env, name=None, path=None, active=False, recipe=None,
+    def __init__(self, env, name=None, paths=[], active=False, recipe=None,
                  min_rev=None, max_rev=None, label=None, description=None):
         """Initialize a new build configuration with the specified attributes.
 
@@ -41,7 +41,7 @@
         self.env = env
         self._old_name = None
         self.name = name
-        self.path = path or ''
+        self.paths = paths
         self.active = bool(active)
         self.recipe = recipe or ''
         self.min_rev = min_rev or None
@@ -97,7 +97,7 @@
         cursor.execute("INSERT INTO bitten_config (name,path,active,"
                        "recipe,min_rev,max_rev,label,description) "
                        "VALUES (%s,%s,%s,%s,%s,%s,%s,%s)",
-                       (self.name, self.path, int(self.active or 0),
+                       (self.name, '\n'.join(self.paths), int(self.active or 0),
                         self.recipe or '', self.min_rev, self.max_rev,
                         self.label or '', self.description or ''))
 
@@ -119,7 +119,7 @@
         cursor.execute("UPDATE bitten_config SET name=%s,path=%s,active=%s,"
                        "recipe=%s,min_rev=%s,max_rev=%s,label=%s,"
                        "description=%s WHERE name=%s",
-                       (self.name, self.path, int(self.active or 0),
+                       (self.name, '\n'.join(self.paths), int(self.active or 0),
                         self.recipe, self.min_rev, self.max_rev,
                         self.label, self.description, self._old_name))
         if self.name != self._old_name:
@@ -148,7 +148,7 @@
 
         config = BuildConfig(env)
         config.name = config._old_name = name
-        config.path = row[0] or ''
+        config.paths = (row[0] or '').split('\n')
         config.active = bool(row[1])
         config.recipe = row[2] or ''
         config.min_rev = row[3] or None
@@ -176,7 +176,7 @@
                            "WHERE active=1 ORDER BY name")
         for name, path, active, recipe, min_rev, max_rev, label, description \
                 in cursor:
-            config = BuildConfig(env, name=name, path=path or '',
+            config = BuildConfig(env, name=name, paths=(path or '').split('\n'),
                                  active=bool(active), recipe=recipe or '',
                                  min_rev=min_rev or None,
                                  max_rev=max_rev or None, label=label or '',
Index: bitten/templates/bitten_admin_configs.html
===================================================================
--- bitten/templates/bitten_admin_configs.html	(revision 816)
+++ bitten/templates/bitten_admin_configs.html	(working copy)
@@ -51,9 +51,13 @@
       <fieldset id="repos">
         <legend>Repository Mapping</legend>
         <table class="form" summary=""><tr>
-          <th><label for="path">Path:</label></th>
-          <td colspan="3"><input id="path" type="text" name="path"
-              size="48" value="$config.path" /></td>
+          <th><label for="path">Paths:</label></th>
+          <td colspan="4"><fieldset class="iefix">
+          <label for="paths">Paths (all entries trigger a build, the first entry is used for the $${path} property):</label>
+          <p>
+              <textarea id="paths" type="text" name="paths" rows="8" cols="48">${'\n'.join(config.paths)}</textarea>
+          </p>
+        </fieldset></td>
         </tr><tr>
           <th><label for="min_rev">
             First revision:
@@ -205,8 +209,8 @@
         </label></div></td>
       </tr><tr>
         <td class="path" colspan="2"><div class="field">
-          <label>Path:<br />
-            <input type="text" name="path" size="32" />
+          <label>Paths (all entries trigger a build, the first entry is used for the $${path} property):<br />
+            <textarea type="text" name="paths" rows="8" cols="32" />
           </label>
         </div></td>
       </tr></table>
@@ -219,7 +223,7 @@
       <table class="listing" id="configlist">
         <thead>
           <tr><th class="sel">&nbsp;</th><th>Name</th>
-          <th>Path</th><th>Active</th></tr>
+          <th>Paths</th><th>Active</th></tr>
         </thead><tbody>
         <tr py:if="not configs">
           <td colspan="4"><em>(No Build Configurations)</em></td>
@@ -232,7 +236,9 @@
           <td class="name">
             <a href="$config.href">$config.label</a>
           </td>
-          <td class="path"><code>$config.path</code></td>
+          <td class="path"><code>
+              <py:for each="path in config.paths">${path}<br /></py:for>
+          </code></td>
           <td class="active">
             <input py:if="config.recipe"
                    type="checkbox" name="active"
Index: bitten/templates/bitten_config.html
===================================================================
--- bitten/templates/bitten_config.html	(revision 816)
+++ bitten/templates/bitten_config.html	(working copy)
@@ -113,9 +113,11 @@
         </div>
       </form>
       <p class="path">
-        Repository path: 
-        <a py:if="config.path" href="$config.browser_href">$config.path</a>
-        ${not config.path and '&mdash;' or ''}
+        Repository path(s): 
+        <py:for each="config_path in config.paths">
+        <a href="$config.browser_href_prefix$config_path">$config_path</a>
+        </py:for>
+        &mdash;
         <py:if test="config.min_rev or config.max_rev">
         (<py:if test="config.min_rev">starting at 
          <a href="$config.min_rev_href">[$config.min_rev]</a></py:if>
Index: bitten/web_ui.py
===================================================================
--- bitten/web_ui.py	(revision 816)
+++ bitten/web_ui.py	(working copy)
@@ -15,6 +15,8 @@
 from StringIO import StringIO
 
 import pkg_resources
+from array import array
+from datetime import datetime
 from genshi.builder import tag
 from trac.attachment import AttachmentModule
 from trac.core import *
@@ -186,8 +188,9 @@
 
         configs = []
         for config in BuildConfig.select(self.env, include_inactive=show_all):
-            if not repos.authz.has_permission(config.path):
-                continue
+            for config_path in config.paths:
+                if not repos.authz.has_permission(config_path):
+                    continue
 
             description = config.description
             if description:
@@ -208,7 +211,7 @@
 
             config_data = {
                 'name': config.name, 'label': config.label or config.name,
-                'active': config.active, 'path': config.path,
+                'active': config.active, 'paths': config.paths,
                 'description': description,
                 'builds_pending' : len(list(Build.select(self.env,
                                                 config=config.name,
@@ -273,8 +276,9 @@
 
         configs = []
         for config in BuildConfig.select(self.env, include_inactive=False):
-            if not repos.authz.has_permission(config.path):
-                continue
+            for config_path in config.paths:
+                if not repos.authz.has_permission(config_path):
+                    continue
 
             self.log.debug(config.name)
             if not config.active:
@@ -318,7 +322,7 @@
                 description = wiki_to_html(description, self.env, req)
             configs.append({
                 'name': config.name, 'label': config.label or config.name,
-                'active': config.active, 'path': config.path,
+                'active': config.active, 'paths': config.paths,
                 'description': description,
                 'href': req.href.build(config.name),
                 'builds': builds
@@ -336,7 +340,8 @@
                                 % config_name)
 
         repos = self.env.get_repository(req.authname)
-        repos.authz.assert_permission(config.path)
+        for config_path in config.paths:
+            repos.authz.assert_permission(config_path)
 
         data = {'title': 'Build Configuration "%s"' \
                           % config.label or config.name,
@@ -352,13 +357,13 @@
                                 config=config.name, status=Build.IN_PROGRESS))
 
         data['config'] = {
-            'name': config.name, 'label': config.label, 'path': config.path,
+            'name': config.name, 'label': config.label, 'paths': config.paths,
             'min_rev': config.min_rev,
             'min_rev_href': req.href.changeset(config.min_rev),
             'max_rev': config.max_rev,
             'max_rev_href': req.href.changeset(config.max_rev),
             'active': config.active, 'description': description,
-            'browser_href': req.href.browser(config.path),
+            'browser_href_prefix': req.href.browser('/'),
             'builds_pending' : len(pending_builds),
             'builds_inprogress' : len(inprogress_builds)
         }
@@ -534,7 +539,8 @@
                                    and build.status != build.PENDING)
 
         repos = self.env.get_repository(req.authname)
-        repos.authz.assert_permission(config.path)
+        for config_path in config.paths:
+            repos.authz.assert_permission(config_path)
         chgset = repos.get_changeset(build.rev)
         data['build']['chgset_author'] = chgset.author
 
@@ -579,8 +585,9 @@
                        Build.FAILURE: 'failedbuild'}
 
         for id_, config, label, path, rev, platform, stopped, status in cursor:
-            if not repos.authz.has_permission(path):
-                continue
+            for cursor_path in (path or '').split('\n'):
+                if not repos.authz.has_permission(cursor_path):
+                    continue;
             errors = []
             if status == Build.FAILURE:
                 for step in BuildStep.select(self.env, build=id_,
@@ -713,7 +720,8 @@
 
     implements(ILogFormatter)
 
-    _fileref_re = re.compile(r'(?P<prefix>-[A-Za-z])?(?P<path>[\w.-]+(?:[\\/][\w.-]+)+)(?P<line>:\d+)?')
+    _fileref_re = re.compile(r'(?P<prefix>-[A-Za-z])?(?P<path>([A-Za-z]+\:)?[\\/\w.-]+)((:|, line )(?P<line>\d+))?((\#)(?P<revision>\d+))?')
+    _url_re = re.compile(r'(?P<scheme>[A-Za-z]+\:.*)')
 
     def get_formatter(self, req, build):
         """Return the log message formatter function."""
@@ -724,31 +732,52 @@
 
         def _replace(m):
             filepath = posixpath.normpath(m.group('path').replace('\\', '/'))
-            if not cache.get(filepath) is True:
-                parts = filepath.split('/')
-                path = ''
-                for part in parts:
-                    path = posixpath.join(path, part)
-                    if path not in cache:
+            if filepath not in cache:
+                # Check the suffixes of the path in order to avoid local prefixes
+                # e.g., where the local path is workspace/plugins/folder1/folder2/file
+                #       and the repository path is config_path/folder1/folder2/file
+                pathIndexes = [match.start() for match in re.finditer('/', filepath)];
+                if 0 not in pathIndexes:
+                    pathIndexes = [0] + pathIndexes
+                for pathIndex in reversed(pathIndexes):
+                    path = posixpath.normpath(filepath[pathIndex:])
+                    if path and path[0] == '/':
+                        path = path[1:]
+                    for config_path in config.paths:
                         try:
-                            full_path = posixpath.join(config.path, path)
-                            full_path = posixpath.normpath(full_path)
-                            if full_path.startswith(config.path + "/") \
-                                        or full_path == config.path:
-                                repos.get_node(full_path,
-                                               build.rev)
-                                cache[path] = True
+                            if path.startswith(config_path + "/") \
+                                or path == config_path:
+                            full_path = path
                             else:
-                                cache[path] = False
+                                full_path = posixpath.join(config_path, path)
+                                full_path = posixpath.normpath(full_path)
+                            if full_path.startswith(config_path + "/") \
+                                    or full_path == config_path:
+                                repos.get_node(full_path, build.rev)
+                path = full_path[len(config_path):]
+                                cache[filepath] = (config_path, path)
+                                break # but continiue searching for longer paths
                         except TracError:
-                            cache[path] = False
-                    if cache[path] is False:
-                        return m.group(0)
-            link = href(config.path, filepath)
-            if m.group('line'):
-                link += '#L' + m.group('line')[1:]
-            return Markup(tag.a(m.group(0), href=link))
+                            pass
+                if filepath not in cache:
+                    cache[filepath] = False
 
+            if filepath in cache and cache[filepath] is not False:
+                config_path, path = cache[filepath]
+                link = href(config_path, path)
+                if m.group('line'):
+                    link += '#L' + m.group('line')
+                if m.group('revision'):
+                    link += '?rev=' + m.group('revision')
+                    return Markup(tag.a(m.group(0), href=link))
+                return Markup(tag.a(m.group(0), href=link)) +tag.i(Markup("@") + Markup(tag.a(build.rev, href=link+'?rev='+build.rev)))
+
+            if self._url_re.match(m.group(0)):
+                if not m.group(0).startswith("file:"):
+                    return Markup(tag.a(m.group(0), href=m.group(0)))
+
+            return m.group(0)
+
         def _formatter(step, type, level, message):
             buf = []
             offset = 0
Index: bitten/tests/admin.py
===================================================================
--- bitten/tests/admin.py	(revision 816)
+++ bitten/tests/admin.py	(working copy)
@@ -177,9 +177,9 @@
         self.assertEqual([], data['configs'])
 
     def test_process_view_configs(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
-        BuildConfig(self.env, name='bar', label='Bar', path='branches/bar',
+        BuildConfig(self.env, name='bar', label='Bar', paths=['branches/bar'],
                     min_rev='123', max_rev='456').insert()
 
         req = Mock(method='GET', chrome={}, href=Href('/'),
@@ -197,16 +197,16 @@
         self.assertEqual({
             'name': 'bar', 'href': '/admin/bitten/configs/bar',
             'label': 'Bar', 'min_rev': '123', 'max_rev': '456',
-            'path': 'branches/bar', 'active': False, 'recipe': False
+            'paths': ['branches/bar'], 'active': False, 'recipe': False
         }, configs[0])
         self.assertEqual({
             'name': 'foo', 'href': '/admin/bitten/configs/foo',
             'label': 'Foo', 'min_rev': None, 'max_rev': None,
-            'path': 'branches/foo', 'active': True, 'recipe': False
+            'paths': ['branches/foo'], 'active': True, 'recipe': False
         }, configs[1])
 
     def test_process_view_config(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
         TargetPlatform(self.env, config='foo', name='any').insert()
 
@@ -223,7 +223,7 @@
         config = data['config']
         self.assertEqual({
             'name': 'foo', 'label': 'Foo', 'description': '', 'recipe': '',
-            'path': 'branches/foo', 'min_rev': None, 'max_rev': None,
+            'paths': ['branches/foo'], 'min_rev': None, 'max_rev': None,
             'active': True, 'platforms': [{
                 'href': '/admin/bitten/configs/foo/1',
                 'name': 'any', 'id': 1, 'rules': []
@@ -231,8 +231,8 @@
         }, config)
 
     def test_process_activate_config(self):
-        BuildConfig(self.env, name='foo', path='branches/foo').insert()
-        BuildConfig(self.env, name='bar', path='branches/bar').insert()
+        BuildConfig(self.env, name='foo', paths=['branches/foo']).insert()
+        BuildConfig(self.env, name='bar', paths=['branches/bar']).insert()
 
         redirected_to = []
         def redirect(url):
@@ -255,9 +255,9 @@
             self.assertEqual(True, config.active)
 
     def test_process_deactivate_config(self):
-        BuildConfig(self.env, name='foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', paths=['branches/foo'],
                     active=True).insert()
-        BuildConfig(self.env, name='bar', path='branches/bar',
+        BuildConfig(self.env, name='bar', paths=['branches/bar'],
                     active=True).insert()
 
         redirected_to = []
@@ -283,7 +283,7 @@
             self.assertEqual(False, config.active)
 
     def test_process_add_config(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
 
         redirected_to = []
@@ -358,7 +358,7 @@
     def test_new_config_submit_with_invalid_path(self):
         req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
                    authname='joe',
-                   args={'add': '', 'name': 'foo', 'path': 'invalid/path'})
+                   args={'add': '', 'name': 'foo', 'paths': ['invalid/path']})
 
         def get_node(path, rev=None):
             raise TracError('No such node')
@@ -373,7 +373,7 @@
             self.failUnless('Invalid Repository Path' in e.message)
 
     def test_process_add_config_no_perms(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
         PermissionSystem(self.env).revoke_permission('joe', 'BUILD_CREATE')
 
@@ -386,9 +386,9 @@
                           'bitten', 'configs', '')
 
     def test_process_remove_config(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
-        BuildConfig(self.env, name='bar', label='Bar', path='branches/bar',
+        BuildConfig(self.env, name='bar', label='Bar', paths=['branches/bar'],
                     min_rev='123', max_rev='456').insert()
 
         redirected_to = []
@@ -410,9 +410,9 @@
             assert not BuildConfig.fetch(self.env, name='bar')
 
     def test_process_remove_config_cancel(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
-        BuildConfig(self.env, name='bar', label='Bar', path='branches/bar',
+        BuildConfig(self.env, name='bar', label='Bar', paths=['branches/bar'],
                     min_rev='123', max_rev='456').insert()
 
         redirected_to = []
@@ -435,7 +435,7 @@
             self.assertEqual(2, len(configs))
 
     def test_process_remove_config_no_selection(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
 
         req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
@@ -450,7 +450,7 @@
             self.assertEqual('No configuration selected', e.message)
 
     def test_process_remove_config_bad_selection(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
 
         req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
@@ -465,7 +465,7 @@
             self.assertEqual("Configuration 'baz' not found", e.message)
 
     def test_process_remove_config_no_perms(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
         PermissionSystem(self.env).revoke_permission('joe', 'BUILD_DELETE')
 
@@ -478,7 +478,7 @@
                           'bitten', 'configs', '')
 
     def test_process_update_config(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
 
         redirected_to = []
@@ -506,7 +506,7 @@
             self.assertEqual('Thanks for all the fish!', config.description)
 
     def test_process_update_config_no_name(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
 
         req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
@@ -521,7 +521,7 @@
                             req.chrome['warnings'])
 
     def test_process_update_config_invalid_name(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
 
         req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
@@ -537,12 +537,12 @@
                         'only contain letters, digits, periods, or dashes.'])
 
     def test_process_update_config_invalid_path(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
 
         req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
                    authname='joe', chrome={'warnings': []}, href=Href('/'),
-                   args={'save': '', 'name': 'foo', 'path': 'invalid/path'})
+                   args={'save': '', 'name': 'foo', 'paths': ['invalid/path']})
 
         def get_node(path, rev=None):
             raise TracError('No such node')
@@ -556,7 +556,7 @@
                             ['Invalid Repository Path "invalid/path".'])
 
     def test_process_update_config_non_wellformed_recipe(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
 
         req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
@@ -571,7 +571,7 @@
                         'column 0.'], req.chrome['warnings'])
 
     def test_process_update_config_invalid_recipe(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
 
         req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
@@ -587,7 +587,7 @@
                     ['Invalid Recipe: Steps must have an "id" attribute.'])
 
     def test_process_new_platform_no_name(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
 
         data = {}
@@ -604,7 +604,7 @@
             self.assertEquals(e.title, 'Missing field')
 
     def test_process_new_platform(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
 
         redirected_to = []
@@ -630,7 +630,7 @@
             self.assertEqual([], platforms[0].rules)
 
     def test_process_remove_platforms(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
         platform = TargetPlatform(self.env, config='foo', name='any')
         platform.insert()
@@ -659,7 +659,7 @@
             self.assertEqual(0, len(platforms))
 
     def test_process_remove_platforms_no_selection(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
         platform = TargetPlatform(self.env, config='foo', name='any')
         platform.insert()
@@ -682,7 +682,7 @@
             self.assertEqual('No platform selected', e.message)
 
     def test_process_edit_platform(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
         platform = TargetPlatform(self.env, config='foo', name='any')
         platform.insert()
@@ -703,7 +703,7 @@
         }, platform)
 
     def test_process_update_platform(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
         platform = TargetPlatform(self.env, config='foo', name='any')
         platform.insert()
@@ -733,7 +733,7 @@
             self.assertEqual([('family', 'posix')], platforms[0].rules)
 
     def test_process_update_platform_cancel(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
+        BuildConfig(self.env, name='foo', label='Foo', paths=['branches/foo'],
                     active=True).insert()
         platform = TargetPlatform(self.env, config='foo', name='any')
         platform.insert()
Index: bitten/tests/web_ui.py
===================================================================
--- bitten/tests/web_ui.py	(revision 816)
+++ bitten/tests/web_ui.py	(working copy)
@@ -105,7 +105,7 @@
                                 data['config']['attachments']['attach_href'])
 
     def test_view_config_paging(self):
-        config = BuildConfig(self.env, name='test', path='trunk')
+        config = BuildConfig(self.env, name='test', paths=['trunk'])
         config.insert()
         platform = TargetPlatform(self.env, config='test', name='any')
         platform.insert()
@@ -152,7 +152,7 @@
 class BuildControllerTestCase(AbstractWebUITestCase):
 
     def test_view_build(self):
-        config = BuildConfig(self.env, name='test', path='trunk')
+        config = BuildConfig(self.env, name='test', paths=['trunk'])
         config.insert()
         platform = TargetPlatform(self.env, config='test', name='any')
         platform.insert()
@@ -191,7 +191,7 @@
     def test_raise_404(self):
         PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW')
         module = BuildController(self.env)
-        config = BuildConfig(self.env, name='existing', path='trunk')
+        config = BuildConfig(self.env, name='existing', paths=['trunk'])
         config.insert()
         req = Mock(method='GET', base_path='', cgi_location='',
                    path_info='/build/existing/42', href=Href('/trac'), args={},
@@ -211,7 +211,7 @@
 class SourceFileLinkFormatterTestCase(AbstractWebUITestCase):
 
     def test_format_simple_link_in_repos(self):
-        BuildConfig(self.env, name='test', path='trunk').insert()
+        BuildConfig(self.env, name='test', paths=['trunk']).insert()
         build = Build(self.env, config='test', platform=1, rev=123, rev_time=42,
                       status=Build.SUCCESS, slave='hal')
         build.insert()
@@ -237,7 +237,7 @@
                          'foo\win.c</a>: bad', output)
 
     def test_format_bad_links(self):
-        BuildConfig(self.env, name='test', path='trunk').insert()
+        BuildConfig(self.env, name='test', paths=['trunk']).insert()
         build = Build(self.env, config='test', platform=1, rev=123, rev_time=42,
                       status=Build.SUCCESS, slave='hal')
         build.insert()
@@ -256,7 +256,7 @@
         self.assertEqual('Linking -I../.. with ../libtool', output)
 
     def test_format_simple_link_not_in_repos(self):
-        BuildConfig(self.env, name='test', path='trunk').insert()
+        BuildConfig(self.env, name='test', paths=['trunk']).insert()
         build = Build(self.env, config='test', platform=1, rev=123, rev_time=42,
                       status=Build.SUCCESS, slave='hal')
         build.insert()
@@ -277,7 +277,7 @@
         self.assertEqual('error in foo/bar.c: bad', output)
 
     def test_format_link_in_repos_with_line(self):
-        BuildConfig(self.env, name='test', path='trunk').insert()
+        BuildConfig(self.env, name='test', paths=['trunk']).insert()
         build = Build(self.env, config='test', platform=1, rev=123, rev_time=42,
                       status=Build.SUCCESS, slave='hal')
         build.insert()
@@ -303,7 +303,7 @@
                          'foo\win.c:123</a>: bad', output)
 
     def test_format_link_not_in_repos_with_line(self):
-        BuildConfig(self.env, name='test', path='trunk').insert()
+        BuildConfig(self.env, name='test', paths=['trunk']).insert()
         build = Build(self.env, config='test', platform=1, rev=123, rev_time=42,
                       status=Build.SUCCESS, slave='hal')
         build.insert()
Index: bitten/tests/master.py
===================================================================
--- bitten/tests/master.py	(revision 816)
+++ bitten/tests/master.py	(working copy)
@@ -56,7 +56,7 @@
         shutil.rmtree(self.env.path)
 
     def test_create_build(self):
-        BuildConfig(self.env, 'test', path='somepath', active=True).insert()
+        BuildConfig(self.env, 'test', paths=['somepath'], active=True).insert()
         platform = TargetPlatform(self.env, config='test', name="Unix")
         platform.rules.append(('family', 'posix'))
         platform.insert()
@@ -228,7 +228,7 @@
                             outbody.getvalue())
 
     def test_cancel_build(self):
-        config = BuildConfig(self.env, 'test', path='somepath', active=True,
+        config = BuildConfig(self.env, 'test', paths=['somepath'], active=True,
                              recipe='<build></build>')
         config.insert()
         build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
@@ -260,7 +260,7 @@
         assert not build.started
 
     def test_initiate_build(self):
-        config = BuildConfig(self.env, 'test', path='somepath', active=True,
+        config = BuildConfig(self.env, 'test', paths=['somepath'], active=True,
                              recipe='<build></build>')
         config.insert()
         platform = TargetPlatform(self.env, config='test', name="Unix")
@@ -323,7 +323,7 @@
         self.assertEquals('No such build (123)', outbody.getvalue())
 
     def test_process_unknown_collection(self):
-        BuildConfig(self.env, 'test', path='somepath', active=True,
+        BuildConfig(self.env, 'test', paths=['somepath'], active=True,
                     recipe='<build></build>').insert()
         build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42)
         build.insert()
@@ -352,7 +352,7 @@
   <step id="foo">
   </step>
 </build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
+        BuildConfig(self.env, 'test', paths=['somepath'], active=True,
                     recipe=recipe).insert()
         build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
                       started=42, status=Build.IN_PROGRESS)
@@ -400,7 +400,7 @@
   <step id="foo">
   </step>
 </build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
+        BuildConfig(self.env, 'test', paths=['somepath'], active=True,
                     recipe=recipe).insert()
         build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
                       started=42, status=Build.IN_PROGRESS)
@@ -460,7 +460,7 @@
   <step id="foo">
   </step>
 </build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
+        BuildConfig(self.env, 'test', paths=['somepath'], active=True,
                     recipe=recipe).insert()
         build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
                       started=42, status=Build.IN_PROGRESS)
@@ -530,7 +530,7 @@
   <attach file="baz.txt" description="baz baz" resource="config"/>
   </step>
 </build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
+        BuildConfig(self.env, 'test', paths=['somepath'], active=True,
                     recipe=recipe).insert()
         build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
                       started=42, status=Build.IN_PROGRESS)
@@ -605,7 +605,7 @@
   <step id="foo">
   </step>
 </build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
+        BuildConfig(self.env, 'test', paths=['somepath'], active=True,
                     recipe=recipe).insert()
         build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
                       started=42, status=Build.IN_PROGRESS)
@@ -655,7 +655,7 @@
   <step id="foo2">
   </step>
 </build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
+        BuildConfig(self.env, 'test', paths=['somepath'], active=True,
                     recipe=recipe).insert()
         build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
                       started=42, status=Build.IN_PROGRESS)
@@ -738,7 +738,7 @@
   <step id="foo">
   </step>
 </build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
+        BuildConfig(self.env, 'test', paths=['somepath'], active=True,
                     recipe=recipe).insert()
         build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
                       started=42, status=Build.IN_PROGRESS)
@@ -786,7 +786,7 @@
   <step id="foo" onerror="ignore">
   </step>
 </build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
+        BuildConfig(self.env, 'test', paths=['somepath'], active=True,
                     recipe=recipe).insert()
         build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
                       started=42, status=Build.IN_PROGRESS)
@@ -835,7 +835,7 @@
   <step id="foo">
   </step>
 </build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
+        BuildConfig(self.env, 'test', paths=['somepath'], active=True,
                     recipe=recipe).insert()
         build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
                       started=42)
@@ -867,7 +867,7 @@
   <step id="foo">
   </step>
 </build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
+        BuildConfig(self.env, 'test', paths=['somepath'], active=True,
                     recipe=recipe).insert()
         build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
                       started=42, status=Build.IN_PROGRESS)
@@ -900,7 +900,7 @@
                              outbody.getvalue())
 
     def test_process_build_step_no_post(self):
-        BuildConfig(self.env, 'test', path='somepath', active=True,
+        BuildConfig(self.env, 'test', paths=['somepath'], active=True,
                     recipe='<build></build>').insert()
         build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
                       started=42)
Index: bitten/tests/model.py
===================================================================
--- bitten/tests/model.py	(revision 816)
+++ bitten/tests/model.py	(working copy)
@@ -62,7 +62,7 @@
         config = BuildConfig.fetch(self.env, name='test')
         assert config.exists
         self.assertEqual('test', config.name)
-        self.assertEqual('trunk', config.path)
+        self.assertEqual(['trunk'], [config.paths])
         self.assertEqual('Test', config.label)
         self.assertEqual(False, config.active)
 
@@ -75,7 +75,7 @@
         self.assertRaises(StopIteration, configs.next)
 
     def test_insert(self):
-        config = BuildConfig(self.env, name='test', path='trunk', label='Test')
+        config = BuildConfig(self.env, name='test', paths=['trunk'], label='Test')
         config.insert()
 
         db = self.env.get_db_cnx()
@@ -95,7 +95,7 @@
                        "VALUES (%s,%s,%s,%s)", ('test', 'trunk', 'Test', 0))
 
         config = BuildConfig.fetch(self.env, 'test')
-        config.path = 'some_branch'
+        config.paths = ['some_branch']
         config.label = 'Updated'
         config.active = True
         config.description = 'Bla bla bla'
Index: bitten/tests/queue.py
===================================================================
--- bitten/tests/queue.py	(revision 816)
+++ bitten/tests/queue.py	(working copy)
@@ -38,7 +38,7 @@
             for stmt in connector.to_sql(table):
                 cursor.execute(stmt)
 
-        self.config = BuildConfig(self.env, name='test', path='somepath')
+        self.config = BuildConfig(self.env, name='test', paths=['somepath'])
         self.config.insert(db=db)
         self.platform = TargetPlatform(self.env, config='test', name='Foo')
         self.platform.insert(db=db)
@@ -204,7 +204,7 @@
             normalize_path=lambda path: path,
             rev_older_than=lambda rev1, rev2: rev1 < rev2
         )
-        BuildConfig(self.env, 'test', path='somepath', active=True).insert()
+        BuildConfig(self.env, 'test', paths=['somepath'], active=True).insert()
         platform1 = TargetPlatform(self.env, config='test', name='P1')
         platform1.insert()
         platform2 = TargetPlatform(self.env, config='test', name='P2')
@@ -235,7 +235,7 @@
             normalize_path=lambda path: path,
             rev_older_than=lambda rev1, rev2: rev1 < rev2
         )
-        BuildConfig(self.env, 'test', path='somepath', active=True).insert()
+        BuildConfig(self.env, 'test', paths=['somepath'], active=True).insert()
         platform1 = TargetPlatform(self.env, config='test', name='P1')
         platform1.insert()
         platform2 = TargetPlatform(self.env, config='test', name='P2')
@@ -279,7 +279,7 @@
             normalize_path=lambda path: path,
             rev_older_than=lambda rev1, rev2: rev1 < rev2
         )
-        BuildConfig(self.env, 'test', path='somepath', active=True).insert()
+        BuildConfig(self.env, 'test', paths=['somepath'], active=True).insert()
         platform1 = TargetPlatform(self.env, config='test', name='P1')
         platform1.insert()
         platform2 = TargetPlatform(self.env, config='test', name='P2')
Index: bitten/report/coverage.py
===================================================================
--- bitten/report/coverage.py	(revision 816)
+++ bitten/report/coverage.py	(working copy)
@@ -112,7 +112,7 @@
             if loc:
                 d = {'name': unit, 'loc': loc, 'cov': int(cov)}
                 if file:
-                    d['href'] = req.href.browser(config.path, file, rev=build.rev, annotate='coverage')
+                    d['href'] = req.href.browser(config.paths[0], file, rev=build.rev, annotate='coverage')
                 units.append(d)
                 total_loc += loc
                 total_cov += loc * cov
@@ -137,7 +137,7 @@
     >>> from bitten.report.tests.coverage import env_stub_with_tables
     >>> env = env_stub_with_tables()
 
-    >>> BuildConfig(env, name='trunk', path='trunk').insert()
+    >>> BuildConfig(env, name='trunk', paths=['trunk']).insert()
     >>> Build(env, rev=123, config='trunk', rev_time=12345, platform=1).insert()
     >>> rpt = Report(env, build=1, step='test', category='coverage')
     >>> rpt.items.append({'file': 'foo.py', 'line_hits': '5 - 0'})
@@ -205,11 +205,11 @@
         reports = []
         for build in builds:
             config = BuildConfig.fetch(self.env, build.config)
-            if not resource.id.startswith('/' + config.path.lstrip('/')):
+            if not resource.id.startswith('/' + config.paths[0].lstrip('/')):
                 continue
             reports = Report.select(self.env, build=build.id,
                                     category='coverage')
-            path_in_config = resource.id[len(config.path)+1:].lstrip('/')
+            path_in_config = resource.id[len(config.paths[0])+1:].lstrip('/')
             for report in reports:
                 for item in report.items:
                     if item.get('file') == path_in_config:
Index: bitten/report/testing.py
===================================================================
--- bitten/report/testing.py	(revision 816)
+++ bitten/report/testing.py	(working copy)
@@ -124,7 +124,7 @@
             total_failure += num_failure
             total_error += num_error
             if file:
-                fixtures[-1]['href'] = req.href.browser(config.path, file)
+                fixtures[-1]['href'] = req.href.browser(config.paths[0], file)
 
         # For each fixture, get a list of tests that don't succeed
         for fixture in fixtures:
Index: bitten/report/lint.py
===================================================================
--- bitten/report/lint.py	(revision 816)
+++ bitten/report/lint.py	(working copy)
@@ -141,7 +141,7 @@
             d['category'][category] += 1
 
             if file:
-                d['href'] = req.href.browser(config.path, file)
+                d['href'] = req.href.browser(config.paths[0], file)
 
             if not type_total.has_key(type):
                 type_total[type] = 0
Index: bitten/queue.py
===================================================================
--- bitten/queue.py	(revision 816)
+++ bitten/queue.py	(working copy)
@@ -45,48 +45,59 @@
     env = config.env
     if not db:
         db = env.get_db_cnx()
-    try:
-        node = repos.get_node(config.path)
-    except Exception, e:
-        env.log.warn('Error accessing path %r for configuration %r',
-                    config.path, config.name, exc_info=True)
-        return
 
-    for path, rev, chg in node.get_history():
+    # Find all build revisions for paths
+	    revs = set([]) 
+    for config_path in config.paths:
+	        try:
+            node = repos.get_node(config_path)
+        except Exception, e: 
+            env.log.warn('Error accessing path %r for configuration %r', 
+                        config_path, config.name, exc_info=True)
+            continue
+        for path, rev, chg in node.get_history():
+            # Stay within the limits of the build config
+            if config.min_rev and repos.rev_older_than(rev, config.min_rev):
+                break
+            if config.max_rev and repos.rev_older_than(config.max_rev, rev):
+                continue
+            # Don't reprocess revisions already found
+	            if rev in revs:
+                continue;
 
-        # Don't follow moves/copies
-        if path != repos.normalize_path(config.path):
-            break
+            # Don't follow moves/copies
+            if path != repos.normalize_path(path):
+                break
 
-        # Stay within the limits of the build config
-        if config.min_rev and repos.rev_older_than(rev, config.min_rev):
-            break
-        if config.max_rev and repos.rev_older_than(config.max_rev, rev):
-            continue
+            # Make sure the repository directory isn't empty at this
+            # revision
+            old_node = repos.get_node(config_path, rev)
+            is_empty = True
+            for entry in old_node.get_entries():
+                is_empty = False
+                break
+            if is_empty:
+                continue
 
-        # Make sure the repository directory isn't empty at this
-        # revision
-        old_node = repos.get_node(path, rev)
-        is_empty = True
-        for entry in old_node.get_entries():
-            is_empty = False
-            break
-        if is_empty:
-            continue
+            # By this point, we have a build revision
+            revs.add(rev)
 
-        # For every target platform, check whether there's a build
-        # of this revision
-        for platform in TargetPlatform.select(env, config.name, db=db):
-            builds = list(Build.select(env, config.name, rev, platform.id,
-                                       db=db))
-            if builds:
-                build = builds[0]
-            else:
-                build = None
+    # Create builds for each platform
+	    for rev in sorted(revs, reverse=True):
+            # For every target platform, check whether there's a build
+            # of this revision
+            for platform in TargetPlatform.select(env, config.name, db=db):
+                builds = list(Build.select(env, config.name, rev, platform.id,
+	                                           db=db))
+                if builds:
+                    build = builds[0]
+	                else:
+                    build = None
 
+	                yield platform, rev, build
+
             yield platform, rev, build
 
-
 class BuildQueue(object):
     """Enapsulates the build queue of an environment.
     
