Merge con trunk master
authordanigm <dani@danigm.net>
Tue, 12 May 2009 13:05:44 +0000 (15:05 +0200)
committerdanigm <dani@danigm.net>
Tue, 12 May 2009 13:05:44 +0000 (15:05 +0200)
12 files changed:
1  2 
loggerhead/apps/branch.py
loggerhead/controllers/__init__.py
loggerhead/controllers/directory_ui.py
loggerhead/controllers/download_ui.py
loggerhead/history.py
loggerhead/templatefunctions.py
loggerhead/templates/changelog.pt
loggerhead/templates/inventory.pt
loggerhead/templates/macros.pt
loggerhead/templates/macros2.pt
loggerhead/templates/revision.pt
serve-branches

@@@ -11,15 -11,16 +11,17 @@@ from paste import reques
  from paste import httpexceptions
  
  from loggerhead.apps import static_app
- from loggerhead.controllers.changelog_ui import ChangeLogUI
- from loggerhead.controllers.inventory_ui import InventoryUI
 +from loggerhead.controllers.download_ui import TGZ
  from loggerhead.controllers.annotate_ui import AnnotateUI
- from loggerhead.controllers.revision_ui import RevisionUI
  from loggerhead.controllers.atom_ui import AtomUI
+ from loggerhead.controllers.changelog_ui import ChangeLogUI
+ from loggerhead.controllers.diff_ui import DiffUI
  from loggerhead.controllers.download_ui import DownloadUI
+ from loggerhead.controllers.filediff_ui import FileDiffUI
+ from loggerhead.controllers.inventory_ui import InventoryUI
+ from loggerhead.controllers.revision_ui import RevisionUI
+ from loggerhead.controllers.revlog_ui import RevLogUI
  from loggerhead.controllers.search_ui import SearchUI
- from loggerhead.controllers.diff_ui import DiffUI
  from loggerhead.history import History
  from loggerhead import util
  
@@@ -75,16 -84,24 +85,25 @@@ class BranchWSGIApp(object)
      def static_url(self, path):
          return self._static_url_base + path
  
+     def yui_url(self, path):
+         if self.use_cdn:
+             base = 'http://yui.yahooapis.com/3.0.0pr2/build/'
+         else:
+             base = self.static_url('/static/javascript/yui/build/')
+         return base + path
      controllers_dict = {
+         '+filediff': FileDiffUI,
+         '+revlog': RevLogUI,
          'annotate': AnnotateUI,
+         'atom': AtomUI,
          'changes': ChangeLogUI,
+         'diff': DiffUI,
++        'tgz': TGZ,
+         'download': DownloadUI,
          'files': InventoryUI,
          'revision': RevisionUI,
-         'download': DownloadUI,
-         'atom': AtomUI,
          'search': SearchUI,
-         'diff': DiffUI,
-         'tgz': TGZ,
          }
  
      def last_updated(self):
          if self._static_url_base is None:
              self._static_url_base = self._url_base
          self._environ = environ
 -                self.served_url = self.url([])
+         if self.served_url is _DEFAULT:
+             public_branch = self.branch_url()
+             if public_branch is not None:
+                 self.served_url = public_branch
+             else:
++                #self.served_url = self.url([])
++                self.served_url = 'http://repo.danigm.net/%s' % self.branch.nick
          path = request.path_info_pop(environ)
          if not path:
              raise httpexceptions.HTTPMovedPermanently(
          self.branch.lock_read()
          try:
              try:
-                 c = cls(self, self.get_history, path=self.path)
+                 c = cls(self, self.get_history)
 -                return c(environ, start_response)
 +                to_ret = c(environ, start_response)
              except:
                  environ['exc_info'] = sys.exc_info()
                  environ['branch'] = self
Simple merge
@@@ -18,7 -18,7 +18,8 @@@
  
  import datetime
  import logging
+ import stat
 +import os
  
  from bzrlib import branch
  
@@@ -30,18 -30,10 +31,18 @@@ class DirEntry(object)
  
      def __init__(self, dirname, parity, branch):
          self.dirname = dirname
 +        self.dirdown = '(bzr branch http://repo.danigm.net/' + self.dirname + ')'
          self.parity = parity
          self.branch = branch
-         v = self.branch.base
-         f = '/' + os.path.join(*v.split('/')[3:] + ['ABOUT'])
-         if os.path.exists(f):
-             self.desc = open(f).read().strip()
-         else:
-             self.desc = ''
++        self.desc = ''
 +
          if branch is not None:
              # If a branch is empty, bzr raises an exception when trying this
++            v = self.branch.base
++            f = '/' + os.path.join(*v.split('/')[3:] + ['ABOUT'])
++            if os.path.exists(f):
++                self.desc = open(f).read().strip()
++
              try:
                  self.last_change = datetime.datetime.fromtimestamp(
                      branch.repository.get_revision(
Simple merge
@@@ -789,139 -733,24 +734,29 @@@ iso style "yyyy-mm-dd"
              modified: list(
                  filename: str,
                  file_id: str,
-             )
-         """
-         added = []
-         modified = []
-         renamed = []
-         removed = []
-         for path, fid, kind in delta.added:
-             added.append((rich_filename(path, kind), fid))
-         for path, fid, kind, text_modified, meta_modified in delta.modified:
-             modified.append(util.Container(filename=rich_filename(path, kind),
-                                            file_id=fid))
-         for old_path, new_path, fid, kind, text_modified, meta_modified in \
- delta.renamed:
-             renamed.append((rich_filename(old_path, kind),
-                             rich_filename(new_path, kind), fid))
-             if meta_modified or text_modified:
-                 modified.append(util.Container(
-                     filename=rich_filename(new_path, kind), file_id=fid))
-         for path, fid, kind in delta.removed:
-             removed.append((rich_filename(path, kind), fid))
-         return util.Container(added=added, renamed=renamed,
-                               removed=removed, modified=modified)
-     @staticmethod
-     def add_side_by_side(changes):
-         # FIXME: this is a rotten API.
-         for change in changes:
-             for m in change.changes.modified:
-                 m.sbs_chunks = _make_side_by_side(m.chunks)
-     def get_filelist(self, inv, file_id, sort_type=None):
-         """
-         return the list of all files (and their attributes) within a given
-         path subtree.
+             ),
+             text_changes: list((filename, file_id)),
          """
-         dir_ie = inv[file_id]
-         path = inv.id2path(file_id)
-         file_list = []
-         revid_set = set()
-         for filename, entry in dir_ie.children.iteritems():
-             revid_set.add(entry.revision)
-         change_dict = {}
-         for change in self.get_changes(list(revid_set)):
-             change_dict[change.revid] = change
-         for filename, entry in dir_ie.children.iteritems():
-             pathname = filename
-             if entry.kind == 'directory':
-                 pathname += '/'
-             if path == '':
-                 absolutepath = pathname
-             else:
-                 absolutepath = urllib.quote(path + '/' + pathname)
-             revid = entry.revision
-             file = util.Container(
-                 filename=filename, executable=entry.executable,
-                 kind=entry.kind, pathname=pathname, absolutepath=absolutepath,
-                 file_id=entry.file_id, size=entry.text_size, revid=revid,
-                 change=change_dict[revid])
-             file_list.append(file)
-         if sort_type == 'filename' or sort_type is None:
-             file_list.sort(key=lambda x: x.filename.lower()) # case-insensitive
-         elif sort_type == 'size':
-             file_list.sort(key=lambda x: x.size)
-         elif sort_type == 'date':
-             file_list.sort(key=lambda x: x.change.date)
-         # Always sort by kind to get directories first
-         file_list.sort(key=lambda x: x.kind != 'directory')
-         parity = 0
-         for file in file_list:
-             file.parity = parity
-             parity ^= 1
-         return file_list
-     def annotate_file(self, file_id, revid):
-         z = time.time()
-         lineno = 1
-         parity = 0
-         file_revid = self.get_inventory(revid)[file_id].revision
-         oldvalues = None
-         tree = self._branch.repository.revision_tree(file_revid)
-         revid_set = set()
-         try:
-             bzrlib.textfile.check_text_lines(tree.get_file_lines(file_id))
-         except bzrlib.errors.BinaryFile:
-                 # bail out; this isn't displayable text
-                 yield util.Container(parity=0, lineno=1, status='same',
-                                      text='(This is a binary file.)',
-                                      change=util.Container())
+         repo = self._branch.repository
+         if bzrlib.revision.is_null(old_revid) or \
+                bzrlib.revision.is_null(new_revid):
+             old_tree, new_tree = map(
+                 repo.revision_tree, [old_revid, new_revid])
          else:
-             for line_revid, text in tree.annotate_iter(file_id):
-                 revid_set.add(line_revid)
+             old_tree, new_tree = repo.revision_trees([old_revid, new_revid])
  
-             change_cache = dict([(c.revid, c) \
-                     for c in self.get_changes(list(revid_set))])
+         reporter = FileChangeReporter(old_tree.inventory, new_tree.inventory)
  
-             last_line_revid = None
-             for line_revid, text in tree.annotate_iter(file_id):
-                 if line_revid == last_line_revid:
-                     # remember which lines have a new revno and which don't
-                     status = 'same'
-                 else:
-                     status = 'changed'
-                     parity ^= 1
-                     last_line_revid = line_revid
-                     change = change_cache[line_revid]
-                     trunc_revno = change.revno
-                     if len(trunc_revno) > 10:
-                         trunc_revno = trunc_revno[:9] + '...'
-                 yield util.Container(parity=parity, lineno=lineno, status=status,
-                                      change=change, text=util.fixed_width(text))
-                 lineno += 1
-         self.log.debug('annotate: %r secs' % (time.time() - z))
+         bzrlib.delta.report_changes(new_tree.iter_changes(old_tree), reporter)
+         return util.Container(
+             added=sorted(reporter.added, key=lambda x:x.filename),
+             renamed=sorted(reporter.renamed, key=lambda x:x.new_filename),
+             removed=sorted(reporter.removed, key=lambda x:x.filename),
+             modified=sorted(reporter.modified, key=lambda x:x.filename),
+             text_changes=sorted(reporter.text_changes, key=lambda x:x.filename))
 +
 +    def export(self, revid, dest):
 +        rev_tree = self._branch.repository.revision_tree(revid)
 +        bzrlib.export.export(rev_tree, dest)
 +        return dest
Simple merge
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <tal:block define="onload string:sortCollapseElements()">
-   <html xmlns="http://www.w3.org/1999/xhtml" metal:use-macro="macros/main">
-     <head>
-       <title metal:fill-slot="title"
-              tal:content="string:${branch/friendly_name} : changes"></title>
-       <metal:block fill-slot="header_extras">
-         <link rel="alternate" type="application/atom+xml"
-               tal:attributes="href python:url(['/atom']);
-                               title string:RSS feed for ${branch/friendly_name}" />
-       </metal:block>
-     </head>
+ <html xmlns="http://www.w3.org/1999/xhtml" metal:use-macro="macros/main">
+   <head>
+     <title metal:fill-slot="title"
+            tal:content="string:${branch/friendly_name} : changes"></title>
+     <metal:block fill-slot="header_extras">
+       <link rel="alternate" type="application/atom+xml"
+             tal:attributes="href python:url(['/atom']);
+                             title string:RSS feed for ${branch/friendly_name}" />
+     <script type="text/javascript">
+       var revids = <tal:block content="data" />;
+     </script>
+       <script type="text/javascript"
+               tal:attributes="src python:branch.static_url('/static/javascript/changelog.js')"></script>
+     </metal:block>
+   </head>
  
-     <body>
-         <tal:block metal:fill-slot="heading">
-          <h1>
-            <tal:has-link condition="branch/branch_link">
-              <a tal:attributes="href branch/branch_link"
-                 tal:content="branch/friendly_name">
-                nice/branch/name
-              </a>
-            </tal:has-link>
-            <tal:no-link condition="not: branch/branch_link">
-              <span metal:use-macro="breadcrumbs/directory"></span>
-            </tal:no-link>
-            <tal:block condition="changes">
-              : changes
-              <tal:block condition="filter_file_id">to <span tal:content="python:history.get_path(revid, filter_file_id)" /></tal:block>
-              <tal:block condition="start_revid">from revision
-                <span tal:content="python:history.get_revno(start_revid)"/>
-              </tal:block>
-              <tal:block condition="query">matching <tal:block content="query" /></tal:block>
-            </tal:block>
-          </h1>
-          <h3>bzr branch http://repo.danigm.net<span tal:content="string:${branch/friendly_name}"/></h3>
+   <body>
+     <tal:block metal:fill-slot="heading">
+       <h1>
+         <tal:has-link condition="branch/branch_link">
+           <a tal:attributes="href branch/branch_link"
+              tal:content="branch/friendly_name">
+             nice/branch/name
+           </a>
+         </tal:has-link>
+         <tal:no-link condition="not: branch/branch_link">
+           <span metal:use-macro="breadcrumbs/directory"></span>
+         </tal:no-link>
+         <tal:block condition="changes">
+           : changes
+           <tal:block condition="filter_file_id">to <span tal:content="python:history.get_path(revid, filter_file_id)" /></tal:block>
+           <tal:block condition="start_revid">from revision
+             <span tal:content="python:history.get_revno(start_revid)"/>
+           </tal:block>
+           <tal:block condition="query">matching <tal:block content="query" /></tal:block>
          </tal:block>
+       </h1>
++      <h3>bzr branch http://repo.danigm.net<span tal:content="string:${branch/friendly_name}"/></h3>
+     </tal:block>
+     <div metal:fill-slot="content">
+       <tal:branch-info replace="structure python:branchinfo(branch)" />
  
-        <div metal:fill-slot="content">
-            <!-- Something nicer-looking should be done with search -->
-         <p tal:condition="search_failed">
-           Sorry, no results found for your search.
-         </p>
+       <!-- Something nicer-looking should be done with search -->
+       <p tal:condition="search_failed">
+         Sorry, no results found for your search.
+       </p>
  
-         <p tal:condition="not:changes">
-           No revisions!
-         </p>
+       <p tal:condition="not:changes">
+         No revisions!
+       </p>
  
-         <p class="fr revnolink">From Revision <a tal:attributes="href #
-                                                             title python:'Show revision '+history.get_revno(revid)" 
-                                 tal:content="python:history.get_revno(revid)"></a>
-           <tal:block tal:condition="python:navigation.last_in_page_revid is not None"> to
-           <a tal:attributes="href # 
+       <p class="fr revnolink">From Revision <a tal:attributes="href #
+                                                                title python:'Show revision '+history.get_revno(revid)"
+                                                tal:content="python:history.get_revno(revid)"></a>
+         <tal:block tal:condition="python:navigation.last_in_page_revid is not None"> to
+           <a tal:attributes="href #
                               title 'Show revision '+history.get_revno(navigation.last_in_page_revid)"
               tal:content="python:history.get_revno(navigation.last_in_page_revid)"></a>
-          </tal:block></p>
-         <p class="expand" id="expand_all"><a tal:attributes="href string:javascript:toggle_expand_all('open')">
-             <img tal:attributes="src python:branch.static_url('/static/images/treeCollapsed.png')"
-                   alt="expand all" /> expand all</a>
-         </p>
-         <p class="expand" id="collapse_all" style="display:none;"><a tal:attributes="href string:javascript:toggle_expand_all('close')">
-             <img tal:attributes="src python:branch.static_url('/static/images/treeExpanded.png')"
-                   alt="collapse all" /> collapse all</a>
-         </p>
-         <!-- Table -->        
-         <table id="logentries">
-             <tr class="logheader">
-                 <td class="revisionnumber">Rev</td>
-                 <td class="expandcell">&nbsp;</td>
-                 <td class="summarycell">Summary</td>
-                 <td class="authorcell">Author</td>
-                 <td class="datecell">Date</td>
-                 <td class="diffcell">Diff</td>
-                 <td class="downloadcell">Files</td>
-             </tr>
-             <tal:block tal:repeat="entry changes">
-             <a tal:attributes="name string:entry-${entry/revno}"/>
-                 <tr tal:attributes="class string:blueRow${entry/parity} revision_log">
-                 <td class="revnro revnolink"><a tal:attributes="title python:'Show revision '+entry.revno;
-                                                                 href  python:url(['/revision', entry.revno], clear=1)"
-                                                 tal:content="python:util.trunc(entry.revno)"></a>
-                 </td>            
-                 <td class="expcell">
-                     <div class="expand_revisioninfo">
-                         <!-- So, this is interesting. I'm using "alt" to have the correct URL for the image of the expanded icon
-                              and "title" tag for the contracted URL of the image. This is a bit of a hack, but it's better than
-                              other approaches I tried  :)  -->
-                         <img tal:attributes="src python:branch.static_url('/static/images/treeCollapsed.png');
-                                              alt python:branch.static_url('/static/images/treeExpanded.png');
-                                              title python:branch.static_url('/static/images/treeCollapsed.png')" 
-                              class="expand_icon" />
-                     </div>
-                 </td>
-                 <td class="summcell"><div tal:attributes="class string:short_description">
-                                         <a tal:attributes="title python:'Show revision '+entry.revno;
-                                                            href  python:url(['/revision', entry.revno], clear=1);
-                                                            class string:link"
-                                            tal:content="entry/short_comment"></a>
-                                      </div>
-                                      <div tal:attributes="class string:long_description;
-                                                           style string:display:none">
-                                         <a tal:attributes="title python:'Show revision '+entry.revno;
-                                                            href  python:url(['/revision', entry.revno], clear=1);
-                                                            class string:link"
-                                            tal:content="entry/comment"></a>
-                                      </div>
-                                 <div class="revisioninfo" style="display:none;">
-                                     <ul class="expandrev">
-                                               <li class="mfrom" tal:repeat="parent python:entry.parents[1:]">
-                                             <span class="revnolink">
-                                                 <a tal:attributes="href python:url(['/changes', parent.revno])"
-                                                    tal:content="parent/revno"></a>
-                                             </span>
-                                             <a tal:condition="parent.branch_nick"
-                                                tal:attributes="href python:url(['/changes'], start_revid=parent.revno)"
-                                                tal:content="python:'(' + parent.branch_nick + ')'"
-                                                 title="Show history" class="link"></a>
-                                         </li>
-                                               <li class="mto" tal:repeat="merge_point entry/merge_points">
-                                             <a tal:attributes="href python:url(['/changes'], start_revid=merge_point.revno)"
-                                                tal:content="python:revno_with_nick(merge_point)"
-                                                title="Show history" class="link"></a>
-                                         </li>
-                                         <li class="committerli" tal:content="python:util.hide_email(entry.author)"></li>
-                                         <tal:block content="structure python:file_change_summary(url, entry, modified_file_link_log)" />
-                                     </ul>
-                                 </div>
-                 </td>
-                 <td tal:content="python:util.trunc(util.hide_email(entry.author), 20)"
-                     class="autcell"></td>
-                 <td class="date">
-                     <span tal:attributes="title python:util.date_time(entry.date)"
-                           tal:content="python:util._approximatedate(entry.date)"></span>
-                 </td>
-                 <td class="diffr"><a tal:attributes="title python:'Show diff at revision '+entry.revno;
-                                                      href python:url(['/revision', entry.revno], clear=1)">
-                                   <img tal:attributes="src python:branch.static_url('/static/images/ico_diff.gif')" alt="Diff" /></a></td>
-                 <td class="downr"><a tal:attributes="href python:branch.url(['/files', entry.revno]);
-                                                      title string:Files at revision ${entry/revno}">
-                                    <img tal:attributes="src python:branch.static_url('/static/images/ico_file.gif')" alt="Files" /></a>
-                 </td>
-             </tr>
-           </tal:block>
-         </table>
+       </tal:block></p>
+       <p class="expand show_if_js" id="expand_all"><a href="#">
+           <img tal:attributes="src python:branch.static_url('/static/images/treeCollapsed.png')"
+                alt="expand all" /> expand all</a>
+       </p>
+       <p class="expand" id="collapse_all" style="display:none;"><a href="#">
+           <img tal:attributes="src python:branch.static_url('/static/images/treeExpanded.png')"
+                alt="collapse all" /> collapse all</a>
+       </p>
+       <!-- Table -->
+       <table id="logentries">
+         <tr class="logheader">
+           <td class="revisionnumber">Rev</td>
+           <td class="expandcell show_if_js">&nbsp;</td>
+           <td class="summarycell">Summary</td>
+           <td class="authorcell">Authors</td>
+           <td class="datecell">Date</td>
+           <td class="diffcell">Diff</td>
+           <td class="downloadcell">Files</td>
+         </tr>
+         <tal:block tal:repeat="entry changes">
+           <a tal:attributes="name string:entry-${entry/revno}"/>
+           <tr tal:attributes="class string:blueRow${entry/parity} revision_log; id string:log-${entry/index}">
+             <td class="revnro revnolink"><a tal:attributes="title python:'Show revision '+entry.revno;
+                                                             href  python:url(['/revision', entry.revno], clear=1)"
+                                             tal:content="python:util.trunc(entry.revno)"></a>
+             </td>
+             <td class="expcell show_if_js">
+               <div class="expand_revisioninfo">
+                 <a href="#">
+                   <img tal:attributes="src python:branch.static_url('/static/images/treeCollapsed.png')"
+                        class="expand_icon" />
+                 </a>
+               </div>
+             </td>
+             <td class="summcell">
+               <div class="short_description">
+                 <a tal:attributes="title python:'Show revision '+entry.revno;
+                                    href  python:url(['/revision', entry.revno], clear=1);
+                                    class string:link"
+                    tal:content="entry/short_comment"></a>
+               </div>
+               <div class="long_description" style="display: none">
+                 <a tal:attributes="title python:'Show revision '+entry.revno;
+                                    href  python:url(['/revision', entry.revno], clear=1);
+                                    class string:link"
+                    tal:content="structure python:util.fixed_width(entry.comment)"></a>
+                 <div class="loading">
+                   <img tal:attributes="src python:branch.static_url('/static/images/spinner.gif')" />
+                 </div>
+               </div>
+             </td>
+             <td tal:content="python:util.trunc(util.hide_email(entry.authors[0]), 20)"
+                 class="autcell"></td>
+             <td class="date">
+               <span tal:attributes="title python:util.date_time(entry.date)"
+                     tal:content="python:util._approximatedate(entry.date)"></span>
+             </td>
+             <td class="diffr"><a tal:attributes="title python:'Show diff at revision '+entry.revno;
+                                                  href python:url(['/revision', entry.revno], clear=1)">
+                 <img tal:attributes="src python:branch.static_url('/static/images/ico_diff.gif')" alt="Diff" /></a></td>
+             <td class="downr"><a tal:attributes="href python:branch.url(['/files', entry.revno]);
+                                                  title string:Files at revision ${entry/revno}">
+                 <img tal:attributes="src python:branch.static_url('/static/images/ico_file.gif')" alt="Files" /></a>
+             </td>
+           </tr>
+         </tal:block>
+       </table>
  
-         <ul tal:condition="python:navigation.prev_page_revid or navigation.next_page_revid"
-             id="pages">
-             <li tal:condition="navigation/prev_page_revid" 
-                 class="previous">
-                 <a tal:attributes="href navigation/prev_page_url">&laquo; Previous</a>
-             </li>
-             <!-- FIXME: Leaving this to eventually show page numbers. Can't show all of them,
-                         so some magic has to be done to just show the previous and next N page numbers
+       <ul tal:condition="python:navigation.prev_page_revid or navigation.next_page_revid"
+           id="pages">
+         <li tal:condition="navigation/prev_page_revid"
+             class="previous">
+           <a tal:attributes="href navigation/prev_page_url">&laquo; Newer</a>
+         </li>
+         <!-- FIXME: Leaving this to eventually show page numbers. Can't show all of them,
+              so some magic has to be done to just show the previous and next N page numbers
  
-             <li class="active">1</li>
-             <tal:block tal:repeat="page_number python:range(navigation.page_count)">
-             <li><a href="#"
-                    tal:content="page_number"></a></li>
-                </tal:block>-->
-             <li tal:condition="navigation/next_page_revid"
-                 class="next">
-                 <a tal:attributes="href navigation/next_page_url">Next &raquo;</a>
-             </li>
-         </ul>
+              <li class="active">1</li>
+              <tal:block tal:repeat="page_number python:range(navigation.page_count)">
+                <li><a href="#"
+                       tal:content="page_number"></a></li>
+              </tal:block>-->
+         <li tal:condition="navigation/next_page_revid"
+             class="next">
+           <a tal:attributes="href navigation/next_page_url">Older &raquo;</a>
+         </li>
+       </ul>
  
-       </div>
-     </body>
-   </html>
- </tal:block>
+     </div>
+   </body>
+ </html>
          for revision <span tal:content="change/revno"/>
        </tal:block>
      </h1>
 +    <h3>bzr branch http://repo.danigm.net<span tal:content="string:${branch/friendly_name}"/></h3>
 +    </span>
  
      <div metal:fill-slot="content">
+         <tal:branch-info replace="structure python:branchinfo(branch)" />
          <p tal:condition="python:not change">
            No revisions!
          </p>
@@@ -6,19 -6,36 +6,36 @@@
  
      <title metal:define-slot="title"></title>
      <link rel="stylesheet"
 -          tal:attributes="href python:branch.static_url('/static/css/global.css')" />
 +          tal:attributes="href python:branch.static_url('/static/css/global2.css')" />
+     <tal:comment condition="nothing">
+       <script type="text/javascript"
+               src="http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js"></script>
+     </tal:comment>
+     <script type="text/javascript">
+       var global_path = <tal:block content="python:'\''+branch.url('/')+'\''" />;
+       var collapsed_icon_path = <tal:block content="python:'\''+branch.static_url('/static/images/treeCollapsed.png')+'\''" />;
+       var expanded_icon_path = <tal:block content="python:'\''+branch.static_url('/static/images/treeExpanded.png')+'\''" />;
+     </script>
+     <script type="text/javascript"
+             tal:attributes="src python:branch.yui_url('yui/yui-min.js')"></script>
+     <script type="text/javascript"
+             tal:attributes="src python:branch.yui_url('oop/oop-min.js')"></script>
+     <script type="text/javascript"
+             tal:attributes="src python:branch.yui_url('event/event-min.js')"></script>
+     <script type="text/javascript"
+             tal:attributes="src python:branch.yui_url('attribute/attribute-min.js')"></script>
+     <script type="text/javascript"
+             tal:attributes="src python:branch.yui_url('base/base-min.js')"></script>
      <script type="text/javascript"
-             tal:attributes="src python:branch.static_url('/static/javascript/mootools-1.2-core.js')"></script>
+             tal:attributes="src python:branch.yui_url('dom/dom-min.js')"></script>
      <script type="text/javascript"
-             tal:attributes="src python:branch.static_url('/static/javascript/mootools-1.2-more.js')"></script>
+             tal:attributes="src python:branch.yui_url('node/node-min.js')"></script>
+     <script type="text/javascript"
+             tal:attributes="src python:branch.yui_url('anim/anim-min.js')"></script>
+     <script type="text/javascript"
+             tal:attributes="src python:branch.yui_url('io/io-base-min.js')"></script>
      <script type="text/javascript"
              tal:attributes="src python:branch.static_url('/static/javascript/custom.js')"></script>
-       <script type="text/javascript">
-           var global_path = <tal:block content="python:'\''+branch.url('/')+'\''" />;
-       </script>
-       <script type="text/javascript"
-               tal:attributes="src
-               python:branch.static_url('/static/javascript/yui/build/yui/yui-min.js')"></script>
      <metal:block metal:define-slot="header_extras" />
    </head>
  
  
          <div id="search_terms"></div>
  
 -        <h1 metal:define-slot="heading"></h1>
 +        <div id="header">
 +            <h1 metal:define-slot="heading"></h1>
 +        </div>
          <div metal:define-slot="content"></div>
 -        <p class="fl">Loggerhead is a web-based interface for <a href="http://bazaar-vcs.org/">Bazaar</a> branches</p>
 +        <p class="fl">Loggerhead is a web-based interface for <a
 +        href="http://bazaar-vcs.org/">Bazaar</a> branches.<br/>
 +        Modified by danigm for <a href="http://danigm.net">danigm.net</a>
 +        </p>
        </div>
    </body>
  </html>
index 38e514e,0000000..7653f70
mode 100644,000000..100644
--- /dev/null
@@@ -1,48 -1,0 +1,70 @@@
-             tal:attributes="src python:branch.static_url('/static/javascript/mootools-1.2-core.js')"></script>
 +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 +          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 +<html xmlns="http://www.w3.org/1999/xhtml" metal:define-macro="main">
 +  <head>
 +    <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
 +
 +    <title metal:define-slot="title"></title>
 +    <link rel="stylesheet"
 +          tal:attributes="href python:branch.static_url('/static/css/global.css')" />
++    <tal:comment condition="nothing">
++      <script type="text/javascript"
++              src="http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js"></script>
++    </tal:comment>
++    <script type="text/javascript">
++      var global_path = <tal:block content="python:'\''+branch.url('/')+'\''" />;
++      var collapsed_icon_path = <tal:block content="python:'\''+branch.static_url('/static/images/treeCollapsed.png')+'\''" />;
++      var expanded_icon_path = <tal:block content="python:'\''+branch.static_url('/static/images/treeExpanded.png')+'\''" />;
++    </script>
++    <script type="text/javascript"
++            tal:attributes="src python:branch.yui_url('yui/yui-min.js')"></script>
++    <script type="text/javascript"
++            tal:attributes="src python:branch.yui_url('oop/oop-min.js')"></script>
++    <script type="text/javascript"
++            tal:attributes="src python:branch.yui_url('event/event-min.js')"></script>
++    <script type="text/javascript"
++            tal:attributes="src python:branch.yui_url('attribute/attribute-min.js')"></script>
 +    <script type="text/javascript"
-             tal:attributes="src python:branch.static_url('/static/javascript/mootools-1.2-more.js')"></script>
++            tal:attributes="src python:branch.yui_url('base/base-min.js')"></script>
 +    <script type="text/javascript"
-       <script type="text/javascript">
-           var global_path = <tal:block content="python:'\''+branch.url('/')+'\''" />;
-       </script>
-       <script type="text/javascript"
-               tal:attributes="src
-               python:branch.static_url('/static/javascript/yui/build/yui/yui-min.js')"></script>
++            tal:attributes="src python:branch.yui_url('dom/dom-min.js')"></script>
++    <script type="text/javascript"
++            tal:attributes="src python:branch.yui_url('node/node-min.js')"></script>
++    <script type="text/javascript"
++            tal:attributes="src python:branch.yui_url('anim/anim-min.js')"></script>
++    <script type="text/javascript"
++            tal:attributes="src python:branch.yui_url('io/io-base-min.js')"></script>
 +    <script type="text/javascript"
 +            tal:attributes="src python:branch.static_url('/static/javascript/custom.js')"></script>
-         <h1 metal:define-slot="heading"></h1>
 +    <metal:block metal:define-slot="header_extras" />
 +  </head>
 +
 +<body>
 +    <!-- Loggerhead Content Area -->
 +      <div id="finderBox">
 +         <tal:search-box define="navigation navigation"
 +                        replace="structure python:search_box(branch,
 +                                 navigation)" />
 +        <div>
 +          <tal:feed-link replace="structure python:feed_link(branch, url)" />
 +        </div>
 +      </div>
 +
 +      <tal:menu define="fileview_active fileview_active"
 +                replace="structure python:menu(branch, url, fileview_active)" />
 +      <div id="loggerheadCont">
 +
 +        <div id="search_terms"></div>
 +
-         <p class="fl">Loggerhead is a web-based interface for <a href="http://bazaar-vcs.org/">Bazaar</a> branches</p>
++        <div id="header">
++            <h1 metal:define-slot="heading"></h1>
++        </div>
 +        <div metal:define-slot="content"></div>
 +
++        <p class="fl">Loggerhead is a web-based interface for <a
++        href="http://bazaar-vcs.org/">Bazaar</a> branches.<br/>
++        Modified by danigm for <a href="http://danigm.net">danigm.net</a>
++        </p>
 +      </div>
 +  </body>
 +</html>
            <tal:no-link condition="not: branch/branch_link">
              <span metal:use-macro="breadcrumbs/directory"></span>
            </tal:no-link>
-           <span>: <tal:revno content="change/revno"></tal:revno>
-         <tal:compare-to condition="python:compare_revid is not None">
-           (compared to revision <tal:block content="python:history.get_revno(compare_revid)" />)
-           </tal:compare-to>
-         </span></h1>
+           <span class="breadcrumb">:
+             <tal:revno condition="not:specific_path" content="change/revno"></tal:revno>
+             <a tal:condition="specific_path" tal:content="change/revno"
+                tal:attributes="href python:url(['/revision', change.revno])"
+                title="View changes to all files"></a>
+                       <tal:compare-to condition="python:compare_revid is not None">
+               (compared to revision <tal:block content="python:history.get_revno(compare_revid)" />)
+             </tal:compare-to>
+           </span>
+           <span class="breadcrumb" tal:condition="specific_path">
+             : <tal:annotate content="structure python:annotate_link(url, change.revno, specific_path)" />
+           </span>
+         </h1>
+         <tal:branch-info replace="structure python:branchinfo(branch)" />
+         <p tal:condition="not:specific_path">
+           Viewing all changes in revision <tal:revno content="change/revno" />.
+         </p>
+         <p tal:condition="specific_path">
+           <a tal:attributes="href python:url(['/revision', change.revno])">
+             &#xAB; back to all changes in this revision
+           </a>
+         </p>
+         <p tal:condition="specific_path">
+           Viewing changes to <tal:annotate content="structure python:annotate_link(url, change.revno, specific_path)" />
+         </p>
          <ul id="submenuTabs">
-             <li id="first"><a tal:attributes="href python:url(['/files', change.revno]); 
-                                                title string:browse files at revision ${change/revno}"
-                                tal:content="string:browse files at revision ${change/revno}"></a></li>
-               <li><a tal:attributes="href python:url(['/tgz', change.revno])">download revision tgz</a></li>
-             <li tal:condition="python:remember != change.revno">
-                 <a tal:attributes="href python:url(['/revision', change.revno], remember=change.revno, compare_revid=None); 
-                                    title string:compare with another revision"
-                    tal:content="string:Compare with another revision"></a></li>       
-             <li tal:condition="python:(remember is not None) and (compare_revid is None) and (change.revno != remember)" >
-                 <a tal:attributes="href python:url(['/revision', change.revno], compare_revid=history.get_revno(remember))">
-                 compare with revision <tal:b content="python:history.get_revno(remember)" />
-                 </a>
-             </li>
-             <li>
+           <li id="first"><a tal:attributes="href python:url(['/files', change.revno]);
+                                             title string:browse files at revision ${change/revno}"
+                             tal:content="string:browse files at revision ${change/revno}"></a></li>
++
++          <li><a tal:attributes="href python:url(['/tgz', change.revno])">download revision tgz</a></li>
+           <li tal:condition="python:remember != change.revno">
+             <a tal:attributes="href python:url(['/revision', change.revno], remember=change.revno, compare_revid=None);
+                                title string:compare with another revision"
+                tal:content="string:Compare with another revision"></a></li>
+           <li tal:condition="python:(remember is not None) and (compare_revid is None) and (change.revno != remember)" >
+             <a tal:attributes="href python:url(['/revision', change.revno], compare_revid=history.get_revno(remember))">
+               compare with revision <tal:b content="python:history.get_revno(remember)" />
+             </a>
+           </li>
+           <li>
              <a tal:condition="python:len(change.parents) > 0 and compare_revid is None"
-                    tal:attributes="href python:url(['/diff', change.revno], clear=1)">download diff</a>
+                tal:attributes="href python:url(['/diff', change.revno], clear=1)">download diff</a>
              <a tal:condition="python:compare_revid is not None"
-                    tal:attributes="href python:url(['/diff', change.revno, history.get_revno(compare_revid)], clear=1)">download diff</a>
-             </li>
-             <li id="last"><a tal:attributes="href python:url(['/changes', change.revno]); 
-                                   title string:view history from revision ${change/revno}"
-                   tal:content="string:view history from revision ${change/revno}"></a></li>
+                tal:attributes="href python:url(['/diff', change.revno, history.get_revno(compare_revid)], clear=1)">download diff</a>
+           </li>
+           <li id="last"><a tal:attributes="href python:url(['/changes', change.revno]);
+                                            title string:view history from revision ${change/revno}"
+                            tal:content="string:view history from revision ${change/revno}"></a></li>
          </ul>
  
          <tal:we-are-comparing condition="python:compare_revid is not None">
diff --cc serve-branches
@@@ -135,17 -122,21 +122,20 @@@ def main(args)
      else:
          app = PrefixMiddleware(app, prefix=prefix)
  
-     if not options.user_port:
+     app = HTTPExceptionHandler(app)
+     app = ErrorHandlerApp(app)
+     if not config.get_option('user_port'):
          port = '8080'
      else:
-         port = options.user_port
+         port = config.get_option('user_port')
  
-     if not options.user_host:
+     if not config.get_option('user_host'):
          host = '0.0.0.0'
      else:
-         host = options.user_host
+         host = config.get_option('user_host')
  
 -    httpserver.serve(app, host=host, port=port)
 -
 +    httpserver.serve(app, host=host, port=port, use_threadpool=False)
  
  if __name__ == "__main__":
      main(sys.argv)