Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/cache.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/cache.py?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/cache.py 
(original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/cache.py 
Sat Nov 15 01:14:46 2014
@@ -101,7 +101,7 @@ class CachedRepository(Repository):
                       """, (to_utimestamp(cset.date), cset.author,
                             cset.message, self.id, srev))
             else:
-                self._insert_changeset(db, rev, cset)
+                self._insert_changeset(db, cset.rev, cset)
         return old_cset
 
     @cached('_metadata_id')
@@ -115,62 +115,10 @@ class CachedRepository(Repository):
 
     def sync(self, feedback=None, clean=False):
         if clean:
-            self.log.info("Cleaning cache")
-            with self.env.db_transaction as db:
-                db("DELETE FROM revision WHERE repos=%s",
-                   (self.id,))
-                db("DELETE FROM node_change WHERE repos=%s",
-                   (self.id,))
-                db.executemany("DELETE FROM repository WHERE id=%s AND 
name=%s",
-                               [(self.id, k) for k in CACHE_METADATA_KEYS])
-                db.executemany("""
-                      INSERT INTO repository (id, name, value)
-                      VALUES (%s, %s, %s)
-                      """, [(self.id, k, '') for k in CACHE_METADATA_KEYS])
-                del self.metadata
+            self.remove_cache()
 
         metadata = self.metadata
-
-        with self.env.db_transaction as db:
-            invalidate = False
-
-            # -- check that we're populating the cache for the correct
-            #    repository
-            repository_dir = metadata.get(CACHE_REPOSITORY_DIR)
-            if repository_dir:
-                # directory part of the repo name can vary on case insensitive
-                # fs
-                if os.path.normcase(repository_dir) \
-                        != os.path.normcase(self.name):
-                    self.log.info("'repository_dir' has changed from %r to %r",
-                                  repository_dir, self.name)
-                    raise TracError(_("The repository directory has changed, "
-                                      "you should resynchronize the "
-                                      "repository with: trac-admin $ENV "
-                                      "repository resync '%(reponame)s'",
-                                      reponame=self.reponame or '(default)'))
-            elif repository_dir is None: #
-                self.log.info('Storing initial "repository_dir": %s',
-                              self.name)
-                db("""INSERT INTO repository (id, name, value)
-                      VALUES (%s, %s, %s)
-                      """, (self.id, CACHE_REPOSITORY_DIR, self.name))
-                invalidate = True
-            else: # 'repository_dir' cleared by a resync
-                self.log.info('Resetting "repository_dir": %s', self.name)
-                db("UPDATE repository SET value=%s WHERE id=%s AND name=%s",
-                   (self.name, self.id, CACHE_REPOSITORY_DIR))
-                invalidate = True
-
-            # -- insert a 'youngeset_rev' for the repository if necessary
-            if metadata.get(CACHE_YOUNGEST_REV) is None:
-                db("""INSERT INTO repository (id, name, value)
-                      VALUES (%s, %s, %s)
-                      """, (self.id, CACHE_YOUNGEST_REV, ''))
-                invalidate = True
-
-            if invalidate:
-                del self.metadata
+        self.save_metadata(metadata)
 
         # -- retrieve the youngest revision in the repository and the youngest
         #    revision cached so far
@@ -263,6 +211,65 @@ class CachedRepository(Repository):
                 if feedback:
                     feedback(youngest)
 
+    def remove_cache(self):
+        """Remove the repository cache."""
+        self.log.info("Cleaning cache")
+        with self.env.db_transaction as db:
+            db("DELETE FROM revision WHERE repos=%s",
+               (self.id,))
+            db("DELETE FROM node_change WHERE repos=%s",
+               (self.id,))
+            db.executemany("DELETE FROM repository WHERE id=%s AND name=%s",
+                           [(self.id, k) for k in CACHE_METADATA_KEYS])
+            db.executemany("""
+                  INSERT INTO repository (id, name, value)
+                  VALUES (%s, %s, %s)
+                  """, [(self.id, k, '') for k in CACHE_METADATA_KEYS])
+            del self.metadata
+
+    def save_metadata(self, metadata):
+        """Save the repository metadata."""
+        with self.env.db_transaction as db:
+            invalidate = False
+
+            # -- check that we're populating the cache for the correct
+            #    repository
+            repository_dir = metadata.get(CACHE_REPOSITORY_DIR)
+            if repository_dir:
+                # directory part of the repo name can vary on case insensitive
+                # fs
+                if os.path.normcase(repository_dir) \
+                        != os.path.normcase(self.name):
+                    self.log.info("'repository_dir' has changed from %r to %r",
+                                  repository_dir, self.name)
+                    raise TracError(_("The repository directory has changed, "
+                                      "you should resynchronize the "
+                                      "repository with: trac-admin $ENV "
+                                      "repository resync '%(reponame)s'",
+                                      reponame=self.reponame or '(default)'))
+            elif repository_dir is None: #
+                self.log.info('Storing initial "repository_dir": %s',
+                              self.name)
+                db("""INSERT INTO repository (id, name, value)
+                      VALUES (%s, %s, %s)
+                      """, (self.id, CACHE_REPOSITORY_DIR, self.name))
+                invalidate = True
+            else: # 'repository_dir' cleared by a resync
+                self.log.info('Resetting "repository_dir": %s', self.name)
+                db("UPDATE repository SET value=%s WHERE id=%s AND name=%s",
+                   (self.name, self.id, CACHE_REPOSITORY_DIR))
+                invalidate = True
+
+            # -- insert a 'youngeset_rev' for the repository if necessary
+            if metadata.get(CACHE_YOUNGEST_REV) is None:
+                db("""INSERT INTO repository (id, name, value)
+                      VALUES (%s, %s, %s)
+                      """, (self.id, CACHE_YOUNGEST_REV, ''))
+                invalidate = True
+
+            if invalidate:
+                del self.metadata
+
     def _insert_changeset(self, db, rev, cset):
         srev = self.db_rev(rev)
         # 1. Attempt to resync the 'revision' table.  In case of
@@ -310,9 +317,64 @@ class CachedRepository(Repository):
             return [int(rev) for rev, in db("""
                     SELECT DISTINCT rev FROM node_change
                     WHERE repos=%%s AND rev>=%%s AND rev<=%%s
-                      AND (path=%%s OR path %s)""" % db.like(),
+                      AND (path=%%s OR path %s)""" % db.prefix_match(),
                     (self.id, sfirst, slast, path,
-                     db.like_escape(path + '/') + '%'))]
+                     db.prefix_match_value(path + '/')))]
+
+    def _get_changed_revs(self, node_infos):
+        if not node_infos:
+            return {}
+
+        node_infos = [(node, self.normalize_rev(first)) for node, first
+                                                        in node_infos]
+        sfirst = self.db_rev(min(first for node, first in node_infos))
+        slast = self.db_rev(max(node.rev for node, first in node_infos))
+        path_infos = dict((node.path, (node, first)) for node, first
+                                                     in node_infos)
+        path_revs = dict((node.path, []) for node, first in node_infos)
+
+        db = self.env.get_read_db()
+        cursor = db.cursor()
+        prefix_match = db.prefix_match()
+
+        # Prevent "too many SQL variables" since max number of parameters is
+        # 999 on SQLite. No limitation on PostgreSQL and MySQL.
+        idx = 0
+        delta = (999 - 3) // 5
+        while idx < len(node_infos):
+            subset = node_infos[idx:idx + delta]
+            idx += delta
+            count = len(subset)
+
+            holders = ','.join(('%s',) * count)
+            query = """\
+                SELECT DISTINCT
+                  rev, (CASE WHEN path IN (%s) THEN path %s END) AS path
+                FROM node_change
+                WHERE repos=%%s AND rev>=%%s AND rev<=%%s AND (path IN (%s) %s)
+                """ % \
+                (holders,
+                 ' '.join(('WHEN path ' + prefix_match + ' THEN %s',) * count),
+                 holders,
+                 ' '.join(('OR path ' + prefix_match,) * count))
+            args = []
+            args.extend(node.path for node, first in subset)
+            for node, first in subset:
+                args.append(db.prefix_match_value(node.path + '/'))
+                args.append(node.path)
+            args.extend((self.id, sfirst, slast))
+            args.extend(node.path for node, first in subset)
+            args.extend(db.prefix_match_value(node.path + '/')
+                        for node, first in subset)
+            cursor.execute(query, args)
+
+            for srev, path in cursor:
+                rev = self.rev_db(srev)
+                node, first = path_infos[path]
+                if first <= rev <= node.rev:
+                    path_revs[path].append(rev)
+
+        return path_revs
 
     def has_node(self, path, rev=None):
         return self.repos.has_node(path, self.normalize_rev(rev))
@@ -324,10 +386,16 @@ class CachedRepository(Repository):
         return self.rev_db(self.metadata.get(CACHE_YOUNGEST_REV))
 
     def previous_rev(self, rev, path=''):
-        if self.has_linear_changesets:
-            return self._next_prev_rev('<', rev, path)
-        else:
-            return self.repos.previous_rev(self.normalize_rev(rev), path)
+        # Hitting the repository directly is faster than searching the
+        # database.  When there is a long stretch of inactivity on a file (in
+        # particular, when a file is added late in the history) the database
+        # query can take a very long time to determine that there is no
+        # previous revision in the node_changes table.  However, the repository
+        # will have a datastructure that will allow it to find the previous
+        # version of a node fairly directly.
+        #if self.has_linear_changesets:
+        #    return self._next_prev_rev('<', rev, path)
+        return self.repos.previous_rev(self.normalize_rev(rev), path)
 
     def next_rev(self, rev, path=''):
         if self.has_linear_changesets:
@@ -346,8 +414,8 @@ class CachedRepository(Repository):
             if path:
                 path = path.lstrip('/')
                 # changes on path itself or its children
-                sql += " AND (path=%s OR path " + db.like()
-                args.extend((path, db.like_escape(path + '/') + '%'))
+                sql += " AND (path=%s OR path " + db.prefix_match()
+                args.extend((path, db.prefix_match_value(path + '/')))
                 # deletion of path ancestors
                 components = path.lstrip('/').split('/')
                 parents = ','.join(('%s',) * len(components))
@@ -361,6 +429,12 @@ class CachedRepository(Repository):
             for rev, in db(sql, args):
                 return int(rev)
 
+    def parent_revs(self, rev):
+        if self.has_linear_changesets:
+            return Repository.parent_revs(self, rev)
+        else:
+            return self.repos.parent_revs(rev)
+
     def rev_older_than(self, rev1, rev2):
         return self.repos.rev_older_than(self.normalize_rev(rev1),
                                          self.normalize_rev(rev2))

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/admin_repositories.html
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/admin_repositories.html?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/admin_repositories.html
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/admin_repositories.html
 Sat Nov 15 01:14:46 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2009-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
@@ -11,7 +21,7 @@
   </head>
 
   <body py:with="sorted_repos = sorted(repositories.iteritems(), key=lambda 
item: item[0].lower())">
-    <h2>Manage Repositories</h2>
+    <h2>Manage Repositories <span py:if="view == 'list'" 
class="trac-count">(${len(repositories)})</span></h2>
 
     <py:def function="type_field(editable, multiline=False, selected=None)">
       <div class="field">
@@ -39,7 +49,8 @@
 
     <py:choose test="view">
       <form py:when="'detail'" py:with="info = repositories[reponame]" 
class="mod" id="trac-modrepos" method="post" action="">
-        <fieldset py:choose="" py:with="readonly = not info.editable or None">
+        <fieldset py:choose="" py:with="disabled = 'disabled' if not 
info.editable else None;
+                                        readonly = 'readonly' if not 
info.editable else None">
           <legend py:when="info.editable">Modify Repository:</legend>
           <legend py:otherwise="">View Repository:</legend>
           <p py:if="not info.editable" class="hint" 
i18n:msg=""><strong>Note:</strong>
@@ -47,7 +58,8 @@
             and cannot be edited on this page.
           </p>
           <div class="field">
-            <label>Name:<br/><input type="text" name="name" value="$info.name" 
readonly="$readonly"/></label>
+            <label>Name:<br/><input type="text" name="name" 
class="trac-autofocus"
+                                    value="$info.name" readonly="$readonly" 
/></label>
           </div>
           <py:choose>
             <py:when test="'alias' in info">
@@ -64,25 +76,26 @@
             </py:otherwise>
           </py:choose>
           <div class="field">
-            <label><input type="checkbox" name="hidden" value="1" 
checked="${info.hidden or None}" disabled="$readonly"/>
+            <label><input type="checkbox" name="hidden" value="1" 
checked="${info.hidden or None}"
+                          disabled="${not info.editable or None}"/>
               Hide from repository index
             </label>
           </div>
           <div class="field">
             <fieldset>
               <label for="description" i18n:msg="">
-                Description (you may use <a tabindex="42" 
href="${href.wiki('WikiFormatting')}">WikiFormatting</a> here):
+                Description: (you may use <a tabindex="42" 
href="${href.wiki('WikiFormatting')}">WikiFormatting</a> here)
               </label>
               <p>
-                <textarea id="description" name="description" class="wikitext 
trac-resizable"
+                <textarea id="description" name="description" class="wikitext 
trac-fullwidth trac-resizable"
                           rows="6" cols="60" readonly="$readonly">
 $info.description</textarea>
               </p>
             </fieldset>
           </div>
           <div class="buttons">
+            <input py:if="info.editable" type="submit" name="save" 
class="trac-disable-on-submit" value="${_('Save')}"/>
             <input type="submit" name="cancel" value="${_('Cancel')}"/>
-            <input py:if="info.editable" type="submit" name="save" 
value="${_('Save')}"/>
           </div>
         </fieldset>
       </form>
@@ -99,7 +112,7 @@ $info.description</textarea>
               <label>Directory: <input type="text" name="dir"/></label>
             </div>
             <div class="buttons">
-              <input type="submit" name="add_repos" value="${_('Add')}"/>
+              <input type="submit" name="add_repos" 
class="trac-disable-on-submit" value="${_('Add')}"/>
             </div>
           </fieldset>
         </form>
@@ -113,12 +126,12 @@ $info.description</textarea>
             </div>
             ${alias_field(True)}
             <div class="buttons">
-              <input type="submit" name="add_alias" value="${_('Add')}"/>
+              <input type="submit" name="add_alias" 
class="trac-disable-on-submit" value="${_('Add')}"/>
             </div>
           </fieldset>
         </form>
 
-        <form id="trac-repository_table" method="post" action="">
+        <form py:if="sorted_repos" id="trac-repository_table" method="post" 
action="">
           <table class="listing" id="trac-reposlist">
             <thead>
               <tr><th class="sel">&nbsp;</th>
@@ -142,7 +155,7 @@ $info.description</textarea>
           </table>
           <div class="buttons">
             <input type="submit" name="refresh" value="${_('Refresh')}"/>
-            <input type="submit" name="remove" value="${_('Remove selected 
items')}"/>
+            <input type="submit" name="remove" class="trac-disable-on-submit" 
value="${_('Remove selected items')}"/>
           </div>
         </form>
       </py:otherwise>

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/browser.html
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/browser.html?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/browser.html
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/browser.html
 Sat Nov 15 01:14:46 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2006-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
@@ -96,7 +106,7 @@
           </form>
         </div>
 
-        <div class="trac-tags" py:with="changeset = 
repos.get_changeset(repos.normalize_rev(stickyrev))">
+        <div class="trac-tags" py:if="changeset">
           <span py:for="branch, head in changeset.get_branches()" 
py:if="branch not in ('default', 'master')"
                 class="branch${' head' if head else ''}"
                 title="${_('Branch head') if head else 
_('Branch')}">${branch}</span>

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/changeset.html
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/changeset.html?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/changeset.html
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/changeset.html
 Sat Nov 15 01:14:46 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2006-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/diff_form.html
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/diff_form.html?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/diff_form.html
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/diff_form.html
 Sat Nov 15 01:14:46 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2006-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/dir_entries.html
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/dir_entries.html?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/dir_entries.html
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/dir_entries.html
 Sat Nov 15 01:14:46 2014
@@ -1,4 +1,15 @@
-<!--! Template for generating rows corresponding to directory entries -->
+<!--!  Copyright (C) 2007-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+
+Template for generating rows corresponding to directory entries
+-->
 <html xmlns="http://www.w3.org/1999/xhtml";
       xmlns:py="http://genshi.edgewall.org/";
       xmlns:xi="http://www.w3.org/2001/XInclude"; py:strip="">

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/dirlist_thead.html
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/dirlist_thead.html?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/dirlist_thead.html
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/dirlist_thead.html
 Sat Nov 15 01:14:46 2014
@@ -1,4 +1,15 @@
-<!--! Template snippet for a standard table header for a dirlist -->
+<!--!  Copyright (C) 2008-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+
+Template snippet for a standard table header for a dirlist
+-->
 <html xmlns="http://www.w3.org/1999/xhtml";
     xmlns:py="http://genshi.edgewall.org/";
     xmlns:xi="http://www.w3.org/2001/XInclude"; py:strip="">

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/path_links.html
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/path_links.html?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/path_links.html
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/path_links.html
 Sat Nov 15 01:14:46 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2008-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <div xmlns="http://www.w3.org/1999/xhtml";
      xmlns:py="http://genshi.edgewall.org/"; py:strip="">
   <!--!  Display a sequence of path components.

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/repository_index.html
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/repository_index.html?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/repository_index.html
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/repository_index.html
 Sat Nov 15 01:14:46 2014
@@ -1,4 +1,15 @@
-<!--! Template snippet for a table of repositories -->
+<!--!  Copyright (C) 2008-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+
+Template snippet for a table of repositories
+-->
 <html xmlns="http://www.w3.org/1999/xhtml";
     xmlns:py="http://genshi.edgewall.org/";
     xmlns:xi="http://www.w3.org/2001/XInclude"; py:strip="">
@@ -11,11 +22,11 @@
         <tr class="${'odd' if idx % 2 else 'even'}">
           <td class="name">
             <em py:strip="not err">
-              <b py:strip="repoinfo.alias != ''">
+              <strong py:strip="repoinfo.alias != ''">
                 <a class="dir" title="View Root Directory"
                    href="${href.browser(repos.reponame if repos else reponame,
                                         order=order if order != 'name' else 
None, desc=desc)}">$reponame</a>
-              </b>
+              </strong>
             </em>
           </td>
           <td class="size">

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/revisionlog.html
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/revisionlog.html?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/revisionlog.html
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/revisionlog.html
 Sat Nov 15 01:14:46 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2006-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/sortable_th.html
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/sortable_th.html?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/sortable_th.html
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/templates/sortable_th.html
 Sat Nov 15 01:14:46 2014
@@ -1,4 +1,14 @@
-<!--! Snippet for a <th> corresponding to a sortable column.
+<!--!  Copyright (C) 2008-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+
+Snippet for a <th> corresponding to a sortable column.
 
     Expects the following variables to be set specifically:
 

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/__init__.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/__init__.py?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/__init__.py
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/__init__.py
 Sat Nov 15 01:14:46 2014
@@ -1,3 +1,16 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2005-2013 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.org/wiki/TracLicense.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://trac.edgewall.org/log/.
+
 import unittest
 
 from trac.versioncontrol.tests import cache, diff, svn_authz, api

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/api.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/api.py?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/api.py
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/api.py
 Sat Nov 15 01:14:46 2014
@@ -1,5 +1,6 @@
 # -*- coding: utf-8 -*-
 #
+# Copyright (C) 2007-2013 Edgewall Software
 # Copyright (C) 2007 CommProve, Inc. <eli.car...@commprove.com>
 # All rights reserved.
 #
@@ -27,40 +28,40 @@ class ApiTestCase(unittest.TestCase):
                                     None)
 
     def test_raise_NotImplementedError_close(self):
-        self.failUnlessRaises(NotImplementedError, self.repo_base.close)
+        self.assertRaises(NotImplementedError, self.repo_base.close)
 
     def test_raise_NotImplementedError_get_changeset(self):
-        self.failUnlessRaises(NotImplementedError, 
self.repo_base.get_changeset, 1)
+        self.assertRaises(NotImplementedError, self.repo_base.get_changeset, 1)
 
     def test_raise_NotImplementedError_get_node(self):
-        self.failUnlessRaises(NotImplementedError, self.repo_base.get_node, 
'path')
+        self.assertRaises(NotImplementedError, self.repo_base.get_node, 'path')
 
     def test_raise_NotImplementedError_get_oldest_rev(self):
-        self.failUnlessRaises(NotImplementedError, 
self.repo_base.get_oldest_rev)
+        self.assertRaises(NotImplementedError, self.repo_base.get_oldest_rev)
 
     def test_raise_NotImplementedError_get_youngest_rev(self):
-        self.failUnlessRaises(NotImplementedError, 
self.repo_base.get_youngest_rev)
+        self.assertRaises(NotImplementedError, self.repo_base.get_youngest_rev)
 
     def test_raise_NotImplementedError_previous_rev(self):
-        self.failUnlessRaises(NotImplementedError, 
self.repo_base.previous_rev, 1)
+        self.assertRaises(NotImplementedError, self.repo_base.previous_rev, 1)
 
     def test_raise_NotImplementedError_next_rev(self):
-        self.failUnlessRaises(NotImplementedError, self.repo_base.next_rev, 1)
+        self.assertRaises(NotImplementedError, self.repo_base.next_rev, 1)
 
     def test_raise_NotImplementedError_rev_older_than(self):
-        self.failUnlessRaises(NotImplementedError, 
self.repo_base.rev_older_than, 1, 2)
+        self.assertRaises(NotImplementedError, self.repo_base.rev_older_than, 
1, 2)
 
     def test_raise_NotImplementedError_get_path_history(self):
-        self.failUnlessRaises(NotImplementedError, 
self.repo_base.get_path_history, 'path')
+        self.assertRaises(NotImplementedError, 
self.repo_base.get_path_history, 'path')
 
     def test_raise_NotImplementedError_normalize_path(self):
-        self.failUnlessRaises(NotImplementedError, 
self.repo_base.normalize_path, 'path')
+        self.assertRaises(NotImplementedError, self.repo_base.normalize_path, 
'path')
 
     def test_raise_NotImplementedError_normalize_rev(self):
-        self.failUnlessRaises(NotImplementedError, 
self.repo_base.normalize_rev, 1)
+        self.assertRaises(NotImplementedError, self.repo_base.normalize_rev, 1)
 
     def test_raise_NotImplementedError_get_changes(self):
-        self.failUnlessRaises(NotImplementedError, self.repo_base.get_changes, 
'path', 1, 'path', 2)
+        self.assertRaises(NotImplementedError, self.repo_base.get_changes, 
'path', 1, 'path', 2)
 
 
 class ResourceManagerTestCase(unittest.TestCase):
@@ -110,13 +111,19 @@ class ResourceManagerTestCase(unittest.T
         self.assertEqual('/trac.cgi/browser/testrepo',
                          get_resource_url(self.env, res, self.env.href))
 
+        res = Resource('repository', '')  # default repository
+        self.assertEqual('Default repository',
+                         get_resource_description(self.env, res))
+        self.assertEqual('/trac.cgi/browser',
+                         get_resource_url(self.env, res, self.env.href))
+
 
 def suite():
     suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(ApiTestCase, 'test'))
-    suite.addTest(unittest.makeSuite(ResourceManagerTestCase, 'test'))
+    suite.addTest(unittest.makeSuite(ApiTestCase))
+    suite.addTest(unittest.makeSuite(ResourceManagerTestCase))
     return suite
 
 
 if __name__ == '__main__':
-    unittest.main()
+    unittest.main(defaultTest='suite')

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/cache.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/cache.py?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/cache.py
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/cache.py
 Sat Nov 15 01:14:46 2014
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C)2005-2009 Edgewall Software
+# Copyright (C) 2005-2013 Edgewall Software
 # Copyright (C) 2005 Christopher Lenz <cml...@gmx.de>
 # All rights reserved.
 #
@@ -18,6 +18,7 @@ from __future__ import with_statement
 
 from datetime import datetime
 
+import trac.tests.compat
 from trac.test import EnvironmentStub, Mock
 from trac.util.datefmt import to_utimestamp, utc
 from trac.versioncontrol import Repository, Changeset, Node, NoSuchChangeset
@@ -81,9 +82,9 @@ class CacheTestCase(unittest.TestCase):
         cache.sync()
 
         with self.env.db_query as db:
-            self.assertEquals([], db(
+            self.assertEqual([], db(
                 "SELECT rev, time, author, message FROM revision"))
-            self.assertEquals(0, db("SELECT COUNT(*) FROM node_change")[0][0])
+            self.assertEqual(0, db("SELECT COUNT(*) FROM node_change")[0][0])
 
     def test_initial_sync(self):
         t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc)
@@ -101,17 +102,17 @@ class CacheTestCase(unittest.TestCase):
 
         with self.env.db_query as db:
             rows = db("SELECT rev, time, author, message FROM revision")
-            self.assertEquals(len(rows), 2)
-            self.assertEquals(('0', to_utimestamp(t1), '', ''), rows[0])
-            self.assertEquals(('1', to_utimestamp(t2), 'joe', 'Import'),
-                              rows[1])
+            self.assertEqual(len(rows), 2)
+            self.assertEqual(('0', to_utimestamp(t1), '', ''), rows[0])
+            self.assertEqual(('1', to_utimestamp(t2), 'joe', 'Import'),
+                             rows[1])
             rows = db("""
                 SELECT rev, path, node_type, change_type, base_path, base_rev
                 FROM node_change""")
-            self.assertEquals(len(rows), 2)
-            self.assertEquals(('1', 'trunk', 'D', 'A', None, None), rows[0])
-            self.assertEquals(('1', 'trunk/README', 'F', 'A', None, None),
-                              rows[1])
+            self.assertEqual(len(rows), 2)
+            self.assertEqual(('1', 'trunk', 'D', 'A', None, None), rows[0])
+            self.assertEqual(('1', 'trunk/README', 'F', 'A', None, None),
+                             rows[1])
 
     def test_update_sync(self):
         t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc)
@@ -137,10 +138,10 @@ class CacheTestCase(unittest.TestCase):
         cache.sync()
 
         with self.env.db_query as db:
-            self.assertEquals([(to_utimestamp(t3), 'joe', 'Update')],
+            self.assertEqual([(to_utimestamp(t3), 'joe', 'Update')],
                 db("SELECT time, author, message FROM revision WHERE rev='2'"))
-            self.assertEquals([('trunk/README', 'F', 'E', 'trunk/README',
-                                '1')],
+            self.assertEqual([('trunk/README', 'F', 'E', 'trunk/README',
+                               '1')],
                     db("""SELECT path, node_type, change_type, base_path,
                                  base_rev
                           FROM node_change WHERE rev='2'"""))
@@ -175,20 +176,20 @@ class CacheTestCase(unittest.TestCase):
         rows = self.env.db_query("""
             SELECT time, author, message FROM revision ORDER BY rev
             """)
-        self.assertEquals(3, len(rows))
-        self.assertEquals((to_utimestamp(t1), 'joe', '**empty**'), rows[0])
-        self.assertEquals((to_utimestamp(t2), 'joe', 'Initial Import'),
-                          rows[1])
-        self.assertEquals((to_utimestamp(t3), 'joe', 'Update'), rows[2])
+        self.assertEqual(3, len(rows))
+        self.assertEqual((to_utimestamp(t1), 'joe', '**empty**'), rows[0])
+        self.assertEqual((to_utimestamp(t2), 'joe', 'Initial Import'),
+                         rows[1])
+        self.assertEqual((to_utimestamp(t3), 'joe', 'Update'), rows[2])
 
         rows = self.env.db_query("""
             SELECT rev, path, node_type, change_type, base_path, base_rev
             FROM node_change ORDER BY rev, path""")
-        self.assertEquals(3, len(rows))
-        self.assertEquals(('1', 'trunk', 'D', 'A', None, None), rows[0])
-        self.assertEquals(('1', 'trunk/README', 'F', 'A', None, None), rows[1])
-        self.assertEquals(('2', 'trunk/README', 'F', 'E', 'trunk/README', '1'),
-                          rows[2])
+        self.assertEqual(3, len(rows))
+        self.assertEqual(('1', 'trunk', 'D', 'A', None, None), rows[0])
+        self.assertEqual(('1', 'trunk/README', 'F', 'A', None, None), rows[1])
+        self.assertEqual(('2', 'trunk/README', 'F', 'E', 'trunk/README', '1'),
+                         rows[2])
 
     def test_sync_changeset(self):
         t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc)
@@ -215,9 +216,9 @@ class CacheTestCase(unittest.TestCase):
 
         rows = self.env.db_query(
                 "SELECT time, author, message FROM revision ORDER BY rev")
-        self.assertEquals(2, len(rows))
-        self.assertEquals((to_utimestamp(t1), 'joe', '**empty**'), rows[0])
-        self.assertEquals((to_utimestamp(t2), 'joe', 'Import'), rows[1])
+        self.assertEqual(2, len(rows))
+        self.assertEqual((to_utimestamp(t1), 'joe', '**empty**'), rows[0])
+        self.assertEqual((to_utimestamp(t2), 'joe', 'Import'), rows[1])
 
     def test_sync_changeset_if_not_exists(self):
         t = [
@@ -260,7 +261,7 @@ class CacheTestCase(unittest.TestCase):
         cache.sync()
         self.assertRaises(NoSuchChangeset, cache.get_changeset, 2)
 
-        self.assertEqual(None, cache.sync_changeset(2))
+        self.assertIsNone(cache.sync_changeset(2))
         cset = cache.get_changeset(2)
         self.assertEqual('john', cset.author)
         self.assertEqual('Created directories', cset.message)
@@ -275,12 +276,46 @@ class CacheTestCase(unittest.TestCase):
 
         rows = self.env.db_query(
                 "SELECT time,author,message FROM revision ORDER BY rev")
-        self.assertEquals(4, len(rows))
-        self.assertEquals((to_utimestamp(t[0]), 'joe', '**empty**'), rows[0])
-        self.assertEquals((to_utimestamp(t[1]), 'joe', 'Import'), rows[1])
-        self.assertEquals((to_utimestamp(t[2]), 'john', 'Created directories'),
-                          rows[2])
-        self.assertEquals((to_utimestamp(t[3]), 'joe', 'Add COPYING'), rows[3])
+        self.assertEqual(4, len(rows))
+        self.assertEqual((to_utimestamp(t[0]), 'joe', '**empty**'), rows[0])
+        self.assertEqual((to_utimestamp(t[1]), 'joe', 'Import'), rows[1])
+        self.assertEqual((to_utimestamp(t[2]), 'john', 'Created directories'),
+                         rows[2])
+        self.assertEqual((to_utimestamp(t[3]), 'joe', 'Add COPYING'), rows[3])
+
+    def test_sync_changeset_with_string_rev(self):  # ticket:11660
+
+        class MockCachedRepository(CachedRepository):
+            def db_rev(self, rev):
+                return '%010d' % rev
+
+        t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc)
+        t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc)
+        repos = self.get_repos(get_changeset=lambda x: changesets[int(x)],
+                               youngest_rev=1)
+        changesets = [
+            Mock(Changeset, repos, 0, 'empty', 'joe', t1,
+                 get_changes=lambda: []),
+            Mock(Changeset, repos, 1, 'first', 'joe', t2,
+                 get_changes=lambda: []),
+            ]
+        cache = MockCachedRepository(self.env, repos, self.log)
+
+        cache.sync_changeset('0')   # not cached yet
+        cache.sync_changeset(u'1')  # not cached yet
+        rows = self.env.db_query(
+            "SELECT rev,author FROM revision ORDER BY rev")
+        self.assertEqual(2, len(rows))
+        self.assertEquals(('0000000000', 'joe'), rows[0])
+        self.assertEquals(('0000000001', 'joe'), rows[1])
+
+        cache.sync_changeset(u'0')  # cached
+        cache.sync_changeset('1')   # cached
+        rows = self.env.db_query(
+            "SELECT rev,author FROM revision ORDER BY rev")
+        self.assertEqual(2, len(rows))
+        self.assertEquals(('0000000000', 'joe'), rows[0])
+        self.assertEquals(('0000000001', 'joe'), rows[1])
 
     def test_get_changes(self):
         t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc)
@@ -307,7 +342,8 @@ class CacheTestCase(unittest.TestCase):
 
 
 def suite():
-    return unittest.makeSuite(CacheTestCase, 'test')
+    return unittest.makeSuite(CacheTestCase)
+
 
 if __name__ == '__main__':
-    unittest.main()
+    unittest.main(defaultTest='suite')

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/diff.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/diff.py?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/diff.py
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/diff.py
 Sat Nov 15 01:14:46 2014
@@ -1,3 +1,16 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2004-2013 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.org/wiki/TracLicense.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://trac.edgewall.org/log/.
+
 from trac.versioncontrol import diff
 
 import unittest
@@ -159,55 +172,55 @@ class DiffTestCase(unittest.TestCase):
         """Make sure that the escape calls leave quotes along, we don't need
         to escape them."""
         changes = diff.diff_blocks(['ab'], ['a"b'])
-        self.assertEquals(len(changes), 1)
+        self.assertEqual(len(changes), 1)
         blocks = changes[0]
-        self.assertEquals(len(blocks), 1)
+        self.assertEqual(len(blocks), 1)
         block = blocks[0]
-        self.assertEquals(block['type'], 'mod')
-        self.assertEquals(str(block['base']['lines'][0]), 'a<del></del>b')
-        self.assertEquals(str(block['changed']['lines'][0]), 'a<ins>"</ins>b')
+        self.assertEqual(block['type'], 'mod')
+        self.assertEqual(str(block['base']['lines'][0]), 'a<del></del>b')
+        self.assertEqual(str(block['changed']['lines'][0]), 'a<ins>"</ins>b')
 
     def test_whitespace_marked_up1(self):
         """Regression test for #5795"""
         changes = diff.diff_blocks(['*a'], [' *a'])
         block = changes[0][0]
-        self.assertEquals(block['type'], 'mod')
-        self.assertEquals(str(block['base']['lines'][0]), '<del></del>*a')
-        self.assertEquals(str(block['changed']['lines'][0]),
-                          '<ins>&nbsp;</ins>*a')
+        self.assertEqual(block['type'], 'mod')
+        self.assertEqual(str(block['base']['lines'][0]), '<del></del>*a')
+        self.assertEqual(str(block['changed']['lines'][0]),
+                         '<ins>&nbsp;</ins>*a')
 
     def test_whitespace_marked_up2(self):
         """Related to #5795"""
         changes = diff.diff_blocks(['   a'], ['   b'])
         block = changes[0][0]
-        self.assertEquals(block['type'], 'mod')
-        self.assertEquals(str(block['base']['lines'][0]),
-                          '&nbsp; &nbsp;<del>a</del>')
-        self.assertEquals(str(block['changed']['lines'][0]),
-                          '&nbsp; &nbsp;<ins>b</ins>')
+        self.assertEqual(block['type'], 'mod')
+        self.assertEqual(str(block['base']['lines'][0]),
+                         '&nbsp; &nbsp;<del>a</del>')
+        self.assertEqual(str(block['changed']['lines'][0]),
+                         '&nbsp; &nbsp;<ins>b</ins>')
 
     def test_whitespace_marked_up3(self):
         """Related to #5795"""
         changes = diff.diff_blocks(['a   '], ['b   '])
         block = changes[0][0]
-        self.assertEquals(block['type'], 'mod')
-        self.assertEquals(str(block['base']['lines'][0]),
-                          '<del>a</del>&nbsp; &nbsp;')
-        self.assertEquals(str(block['changed']['lines'][0]),
-                          '<ins>b</ins>&nbsp; &nbsp;')
+        self.assertEqual(block['type'], 'mod')
+        self.assertEqual(str(block['base']['lines'][0]),
+                         '<del>a</del>&nbsp; &nbsp;')
+        self.assertEqual(str(block['changed']['lines'][0]),
+                         '<ins>b</ins>&nbsp; &nbsp;')
 
     def test_expandtabs_works_right(self):
         """Regression test for #4557"""
         changes = diff.diff_blocks(['aa\tb'], ['aaxb'])
         block = changes[0][0]
-        self.assertEquals(block['type'], 'mod')
-        self.assertEquals(str(block['base']['lines'][0]),
-                          'aa<del>&nbsp; &nbsp; &nbsp; </del>b')
-        self.assertEquals(str(block['changed']['lines'][0]),
-                          'aa<ins>x</ins>b')
+        self.assertEqual(block['type'], 'mod')
+        self.assertEqual(str(block['base']['lines'][0]),
+                         'aa<del>&nbsp; &nbsp; &nbsp; </del>b')
+        self.assertEqual(str(block['changed']['lines'][0]),
+                         'aa<ins>x</ins>b')
 
 def suite():
-    return unittest.makeSuite(DiffTestCase, 'test')
+    return unittest.makeSuite(DiffTestCase)
 
 if __name__ == '__main__':
     unittest.main()

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/functional.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/functional.py?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/functional.py
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/functional.py
 Sat Nov 15 01:14:46 2014
@@ -1,7 +1,31 @@
-#!/usr/bin/python
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2008-2013 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.org/wiki/TracLicense.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://trac.edgewall.org/log/.
+
+import tempfile
+
+from trac.admin.tests.functional import AuthorizationTestCaseSetup
 from trac.tests.functional import *
 
 
+class TestAdminRepositoryAuthorization(AuthorizationTestCaseSetup):
+    def runTest(self):
+        """Check permissions required to access the Version Control
+        Repositories panel."""
+        self.test_authorization('/admin/versioncontrol/repository',
+                                'VERSIONCONTROL_ADMIN', "Manage Repositories")
+
+
 class TestEmptySvnRepo(FunctionalTwillTestCaseSetup):
     def runTest(self):
         """Check empty repository"""
@@ -114,6 +138,53 @@ class RegressionTestTicket5819(Functiona
         tc.find(components, 's')
 
 
+class RegressionTestTicket11186(FunctionalTwillTestCaseSetup):
+    def runTest(self):
+        """Test for regression of http://trac.edgewall.org/ticket/11186
+        TracError should be raised when repository with name already exists
+        """
+        self._tester.go_to_admin()
+        tc.follow("\\bRepositories\\b")
+        tc.url(self._tester.url + '/admin/versioncontrol/repository')
+        name = random_word()
+        tc.formvalue('trac-addrepos', 'name', name)
+        tc.formvalue('trac-addrepos', 'dir', '/var/svn/%s' % name)
+        tc.submit()
+        tc.find('The repository "%s" has been added.' % name)
+        tc.formvalue('trac-addrepos', 'name', name)
+        tc.formvalue('trac-addrepos', 'dir', '/var/svn/%s' % name)
+        tc.submit()
+        tc.find('The repository "%s" already exists.' % name)
+        tc.notfind(internal_error)
+
+
+class RegressionTestTicket11186Alias(FunctionalTwillTestCaseSetup):
+    def runTest(self):
+        """Test for regression of http://trac.edgewall.org/ticket/11186 alias
+        TracError should be raised when repository alias with name already
+        exists
+        """
+        self._tester.go_to_admin()
+        tc.follow("\\bRepositories\\b")
+        tc.url(self._tester.url + '/admin/versioncontrol/repository')
+        word = random_word()
+        target = '%s_repos' % word
+        name = '%s_alias' % word
+        tc.formvalue('trac-addrepos', 'name', target)
+        tc.formvalue('trac-addrepos', 'dir', '/var/svn/%s' % target)
+        tc.submit()
+        tc.find('The repository "%s" has been added.' % target)
+        tc.formvalue('trac-addalias', 'name', name)
+        tc.formvalue('trac-addalias', 'alias', target)
+        tc.submit()
+        tc.find('The alias "%s" has been added.' % name)
+        tc.formvalue('trac-addalias', 'name', name)
+        tc.formvalue('trac-addalias', 'alias', target)
+        tc.submit()
+        tc.find('The alias "%s" already exists.' % name)
+        tc.notfind(internal_error)
+
+
 class RegressionTestRev5877(FunctionalTwillTestCaseSetup):
     def runTest(self):
         """Test for regression of the source browser fix in r5877"""
@@ -121,16 +192,169 @@ class RegressionTestRev5877(FunctionalTw
         tc.notfind(internal_error)
 
 
+class RegressionTestTicket11194(FunctionalTwillTestCaseSetup):
+    def runTest(self):
+        """Test for regression of http://trac.edgewall.org/ticket/11194
+        TracError should be raised when repository with name already exists
+        """
+        self._tester.go_to_admin()
+        tc.follow("\\bRepositories\\b")
+        tc.url(self._tester.url + '/admin/versioncontrol/repository')
+
+        word = random_word()
+        names = ['%s_%d' % (word, n) for n in xrange(3)]
+        tc.formvalue('trac-addrepos', 'name', names[0])
+        tc.formvalue('trac-addrepos', 'dir', '/var/svn/%s' % names[0])
+        tc.submit()
+        tc.notfind(internal_error)
+
+        tc.formvalue('trac-addrepos', 'name', names[1])
+        tc.formvalue('trac-addrepos', 'dir', '/var/svn/%s' % names[1])
+        tc.submit()
+        tc.notfind(internal_error)
+
+        tc.follow('\\b' + names[1] + '\\b')
+        tc.url(self._tester.url + '/admin/versioncontrol/repository/' + 
names[1])
+        tc.formvalue('trac-modrepos', 'name', names[2])
+        tc.submit('save')
+        tc.notfind(internal_error)
+        tc.url(self._tester.url + '/admin/versioncontrol/repository')
+
+        tc.follow('\\b' + names[2] + '\\b')
+        tc.url(self._tester.url + '/admin/versioncontrol/repository/' + 
names[2])
+        tc.formvalue('trac-modrepos', 'name', names[0])
+        tc.submit('save')
+        tc.find('The repository "%s" already exists.' % names[0])
+        tc.notfind(internal_error)
+
+
+class RegressionTestTicket11346(FunctionalTwillTestCaseSetup):
+    def runTest(self):
+        """Test for regression of http://trac.edgewall.org/ticket/11346
+        fix for log: link with revision ranges included oldest wrongly
+        showing HEAD revision
+        """
+        # create new 3 revisions
+        self._testenv.svn_mkdir(['ticket11346'], '')
+        for i in (1, 2):
+            rev = self._testenv.svn_add('ticket11346/file%d.txt' % i, '')
+        tc.go(self._tester.url + '/log?revs=1-2')
+        tc.find('@1')
+        tc.find('@2')
+        tc.notfind('@3')
+        tc.notfind('@%d' % rev)
+
+
+class RegressionTestTicket11355(FunctionalTwillTestCaseSetup):
+    def runTest(self):
+        """Test for regression of http://trac.edgewall.org/ticket/11355
+        Save with no changes should redirect back to the repository listing.
+        """
+        # Add a repository
+        self._tester.go_to_admin("Repositories")
+        name = random_unique_camel()
+        dir = os.path.join(tempfile.gettempdir(), name.lower())
+        tc.formvalue('trac-addrepos', 'name', name)
+        tc.formvalue('trac-addrepos', 'dir', dir)
+        tc.submit('add_repos')
+        tc.find('The repository "%s" has been added.' % name)
+
+        # Save unmodified form and redirect back to listing page
+        tc.follow(r"\b%s\b" % name)
+        tc.url(self._tester.url + '/admin/versioncontrol/repository/' + name)
+        tc.submit('save', formname='trac-modrepos')
+        tc.url(self._tester.url + '/admin/versioncontrol/repository')
+        tc.find("Your changes have been saved.")
+
+        # Warning is added when repository dir is not an absolute path
+        tc.follow(r"\b%s\b" % name)
+        tc.url(self._tester.url + '/admin/versioncontrol/repository/' + name)
+        tc.formvalue('trac-modrepos', 'dir', os.path.basename(dir))
+        tc.submit('save')
+        tc.url(self._tester.url + '/admin/versioncontrol/repository/' + name)
+        tc.find('The repository directory must be an absolute path.')
+
+
+class RegressionTestTicket11438(FunctionalTwillTestCaseSetup):
+    def runTest(self):
+        """Test for regression of http://trac.edgewall.org/ticket/11438
+        fix for log: link with revision ranges included "head" keyword
+        """
+        rev = self._testenv.svn_mkdir(['ticket11438'], '')
+        rev = self._testenv.svn_add('ticket11438/file1.txt', '')
+        rev = self._testenv.svn_add('ticket11438/file2.txt', '')
+        tc.go(self._tester.url + '/intertrac/log:@%d:head' % (rev - 1))
+        tc.url(self._tester.url + r'/log/\?revs=' + str(rev - 1) + '%3Ahead')
+        tc.notfind('@%d' % (rev + 1))
+        tc.find('@%d' % rev)
+        tc.find('@%d' % (rev - 1))
+        tc.notfind('@%d' % (rev - 2))
+
+
+class RegressionTestTicket11584(FunctionalTwillTestCaseSetup):
+    def runTest(self):
+        """Test for regression of http://trac.edgewall.org/ticket/11584
+        don't raise NoSuchChangeset for empty repository if no "rev" parameter
+        """
+        repo_path = self._testenv.svnadmin_create('repo-t11584')
+
+        self._tester.go_to_admin()
+        tc.follow("\\bRepositories\\b")
+        tc.url(self._tester.url + '/admin/versioncontrol/repository')
+
+        tc.formvalue('trac-addrepos', 'name', 't11584')
+        tc.formvalue('trac-addrepos', 'dir', repo_path)
+        tc.submit()
+        tc.notfind(internal_error)
+        self._testenv._tracadmin('repository', 'sync', 't11584')
+
+        browser_url = self._tester.url + '/browser/t11584'
+        tc.go(browser_url)
+        tc.url(browser_url)
+        tc.notfind('Error: No such changeset')
+
+
+class RegressionTestTicket11618(FunctionalTwillTestCaseSetup):
+    def runTest(self):
+        """Test for regression of http://trac.edgewall.org/ticket/11618
+        fix for malformed `readonly="True"` attribute in repository admin.
+        """
+        env = self._testenv.get_trac_environment()
+        env.config.set('repositories', 't11618.dir',
+                       self._testenv.repo_path_for_initenv())
+        env.config.save()
+        try:
+            self._tester.go_to_admin()
+            tc.follow(r'\bRepositories\b')
+            tc.url(self._tester.url + '/admin/versioncontrol/repository')
+            tc.follow(r'\bt11618\b')
+            tc.url(self._tester.url + 
'/admin/versioncontrol/repository/t11618')
+            tc.notfind(' readonly="True"')
+            tc.find(' readonly="readonly"')
+        finally:
+            env.config.remove('repositories', 't11618.dir')
+            env.config.save()
+
+
 def functionalSuite(suite=None):
     if not suite:
-        import trac.tests.functional.testcases
-        suite = trac.tests.functional.testcases.functionalSuite()
+        import trac.tests.functional
+        suite = trac.tests.functional.functionalSuite()
+    suite.addTest(TestAdminRepositoryAuthorization())
+    suite.addTest(RegressionTestTicket11355())
     if has_svn:
         suite.addTest(TestEmptySvnRepo())
         suite.addTest(TestRepoCreation())
         suite.addTest(TestRepoBrowse())
         suite.addTest(TestNewFileLog())
         suite.addTest(RegressionTestTicket5819())
+        suite.addTest(RegressionTestTicket11186())
+        suite.addTest(RegressionTestTicket11186Alias())
+        suite.addTest(RegressionTestTicket11194())
+        suite.addTest(RegressionTestTicket11346())
+        suite.addTest(RegressionTestTicket11438())
+        suite.addTest(RegressionTestTicket11584())
+        suite.addTest(RegressionTestTicket11618())
         suite.addTest(RegressionTestRev5877())
     else:
         print "SKIP: versioncontrol/tests/functional.py (no svn bindings)"

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/svn_authz.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/svn_authz.py?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/svn_authz.py
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/tests/svn_authz.py
 Sat Nov 15 01:14:46 2014
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2010 Edgewall Software
+# Copyright (C) 2005-2013 Edgewall Software
 # All rights reserved.
 #
 # This software is licensed as described in the file COPYING, which
@@ -390,12 +390,10 @@ unknown = r
 
 def suite():
     suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(AuthzParserTestCase, 'test'))
-    suite.addTest(unittest.makeSuite(AuthzSourcePolicyTestCase, 'test'))
+    suite.addTest(unittest.makeSuite(AuthzParserTestCase))
+    suite.addTest(unittest.makeSuite(AuthzSourcePolicyTestCase))
     return suite
 
 
 if __name__ == '__main__':
-    runner = unittest.TextTestRunner()
-    runner.run(suite())
-
+    unittest.main(defaultTest='suite')

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/web_ui/__init__.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/web_ui/__init__.py?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/web_ui/__init__.py
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/web_ui/__init__.py
 Sat Nov 15 01:14:46 2014
@@ -1,3 +1,16 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2005-2013 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.com/license.html.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://trac.edgewall.org/.
+
 from trac.versioncontrol.web_ui.browser import *
 from trac.versioncontrol.web_ui.changeset import *
 from trac.versioncontrol.web_ui.log import *

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/web_ui/browser.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/web_ui/browser.py?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/web_ui/browser.py
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/web_ui/browser.py
 Sat Nov 15 01:14:46 2014
@@ -24,15 +24,14 @@ from genshi.builder import tag
 from trac.config import ListOption, BoolOption, Option
 from trac.core import *
 from trac.mimeview.api import IHTMLPreviewAnnotator, Mimeview, is_binary
-from trac.perm import IPermissionRequestor
+from trac.perm import IPermissionRequestor, PermissionError
 from trac.resource import Resource, ResourceNotFound
 from trac.util import as_bool, embedded_numbers
-from trac.util.compat import cleandoc
 from trac.util.datefmt import http_date, to_datetime, utc
 from trac.util.html import escape, Markup
 from trac.util.text import exception_to_unicode, shorten_line
 from trac.util.translation import _, cleandoc_
-from trac.web import IRequestHandler, RequestDone
+from trac.web.api import IRequestHandler, RequestDone
 from trac.web.chrome import (INavigationContributor, add_ctxtnav, add_link,
                              add_script, add_stylesheet, prevnext_nav,
                              web_context)
@@ -296,7 +295,8 @@ class BrowserModule(Component):
 
     def get_navigation_items(self, req):
         rm = RepositoryManager(self.env)
-        if 'BROWSER_VIEW' in req.perm and rm.get_real_repositories():
+        if any(repos.is_viewable(req.perm) for repos
+                                           in rm.get_real_repositories()):
             yield ('mainnav', 'browser',
                    tag.a(_('Browse Source'), href=req.href.browser()))
 
@@ -327,8 +327,6 @@ class BrowserModule(Component):
             return True
 
     def process_request(self, req):
-        req.perm.require('BROWSER_VIEW')
-
         presel = req.args.get('preselected')
         if presel and (presel + '/').startswith(req.href.browser() + '/'):
             req.redirect(presel)
@@ -337,8 +335,9 @@ class BrowserModule(Component):
         rev = req.args.get('rev', '')
         if rev.lower() in ('', 'head'):
             rev = None
+        format = req.args.get('format')
         order = req.args.get('order', 'name').lower()
-        desc = req.args.has_key('desc')
+        desc = 'desc' in req.args
         xhr = req.get_header('X-Requested-With') == 'XMLHttpRequest'
 
         rm = RepositoryManager(self.env)
@@ -365,6 +364,7 @@ class BrowserModule(Component):
         # Find node for the requested path/rev
         context = web_context(req)
         node = None
+        changeset = None
         display_rev = lambda rev: rev
         if repos:
             try:
@@ -377,6 +377,12 @@ class BrowserModule(Component):
             except NoSuchChangeset, e:
                 raise ResourceNotFound(e.message,
                                        _('Invalid changeset number'))
+            if node:
+                try:
+                    # use changeset instance to retrieve branches and tags
+                    changeset = repos.get_changeset(node.rev)
+                except NoSuchChangeset:
+                    pass
 
             context = context.child(repos.resource.child('source', path,
                                                    version=rev_or_latest))
@@ -391,13 +397,25 @@ class BrowserModule(Component):
             repo_data = self._render_repository_index(
                                         context, all_repositories, order, desc)
         if node:
+            if not node.is_viewable(req.perm):
+                raise PermissionError('BROWSER_VIEW' if node.isdir else
+                                      'FILE_VIEW', node.resource, self.env)
             if node.isdir:
+                if format in ('zip',): # extension point here...
+                    self._render_zip(req, context, repos, node, rev)
+                    # not reached
                 dir_data = self._render_dir(req, repos, node, rev, order, desc)
             elif node.isfile:
                 file_data = self._render_file(req, context, repos, node, rev)
 
         if not repos and not (repo_data and repo_data['repositories']):
-            raise ResourceNotFound(_("No node %(path)s", path=path))
+            # If no viewable repositories, check permission instead of
+            # repos.is_viewable()
+            req.perm.require('BROWSER_VIEW')
+            if show_index:
+                raise ResourceNotFound(_("No viewable repositories"))
+            else:
+                raise ResourceNotFound(_("No node %(path)s", path=path))
 
         quickjump_data = properties_data = None
         if node and not xhr:
@@ -409,7 +427,7 @@ class BrowserModule(Component):
             'context': context, 'reponame': reponame, 'repos': repos,
             'repoinfo': all_repositories.get(reponame or ''),
             'path': path, 'rev': node and node.rev, 'stickyrev': rev,
-            'display_rev': display_rev,
+            'display_rev': display_rev, 'changeset': changeset,
             'created_path': node and node.created_path,
             'created_rev': node and node.created_rev,
             'properties': properties_data,
@@ -500,6 +518,10 @@ class BrowserModule(Component):
                 continue
             try:
                 repos = rm.get_repository(reponame)
+            except TracError, err:
+                entry = (reponame, repoinfo, None, None,
+                         exception_to_unicode(err), None)
+            else:
                 if repos:
                     if not repos.is_viewable(context.perm):
                         continue
@@ -518,10 +540,7 @@ class BrowserModule(Component):
                              raw_href)
                 else:
                     entry = (reponame, repoinfo, None, None, u"\u2013", None)
-            except TracError, err:
-                entry = (reponame, repoinfo, None, None,
-                         exception_to_unicode(err), None)
-            if entry[-1] is not None:   # Check permission in case of error
+            if entry[4] is not None:  # Check permission in case of error
                 root = Resource('repository', reponame).child('source', '/')
                 if 'BROWSER_VIEW' not in context.perm(root):
                     continue
@@ -620,13 +639,35 @@ class BrowserModule(Component):
                                    timerange.to_seconds(timerange.oldest)),
                 }
 
+    def _iter_nodes(self, node):
+        stack = [node]
+        while stack:
+            node = stack.pop()
+            yield node
+            if node.isdir:
+                stack.extend(sorted(node.get_entries(),
+                                    key=lambda x: x.name,
+                                    reverse=True))
+
+    def _render_zip(self, req, context, repos, root_node, rev=None):
+        if not self.is_path_downloadable(repos, root_node.path):
+            raise TracError(_("Path not available for download"))
+        req.perm(context.resource).require('FILE_VIEW')
+        root_path = root_node.path.rstrip('/')
+        if root_path:
+            archive_name = root_node.name
+        else:
+            archive_name = repos.reponame or 'repository'
+        filename = '%s-%s.zip' % (archive_name, root_node.rev)
+        render_zip(req, filename, repos, root_node, self._iter_nodes)
+
     def _render_file(self, req, context, repos, node, rev=None):
         req.perm(node.resource).require('FILE_VIEW')
 
         mimeview = Mimeview(self.env)
 
         # MIME type detection
-        content = node.get_content()
+        content = node.get_processed_content()
         chunk = content.read(CHUNK_SIZE)
         mime_type = node.content_type
         if not mime_type or mime_type == 'application/octet-stream':
@@ -639,7 +680,6 @@ class BrowserModule(Component):
             req.send_response(200)
             req.send_header('Content-Type',
                             'text/plain' if format == 'txt' else mime_type)
-            req.send_header('Content-Length', node.content_length)
             req.send_header('Last-Modified', http_date(node.last_modified))
             if rev is None:
                 req.send_header('Pragma', 'no-cache')
@@ -652,11 +692,12 @@ class BrowserModule(Component):
                 req.send_header('Content-Disposition', 'attachment')
             req.end_headers()
 
-            while 1:
-                if not chunk:
-                    raise RequestDone
-                req.write(chunk)
-                chunk = content.read(CHUNK_SIZE)
+            def chunks():
+                c = chunk
+                while c:
+                    yield c
+                    c = content.read(CHUNK_SIZE)
+            raise RequestDone(chunks())
         else:
             # The changeset corresponding to the last change on `node`
             # is more interesting than the `rev` changeset.
@@ -678,7 +719,7 @@ class BrowserModule(Component):
             self.log.debug("Rendering preview of node %s@%s with mime-type %s"
                            % (node.name, str(rev), mime_type))
 
-            del content # the remainder of that content is not needed
+            content = None # the remainder of that content is not needed
 
             add_stylesheet(req, 'common/css/code.css')
 
@@ -686,7 +727,8 @@ class BrowserModule(Component):
             annotate = req.args.get('annotate')
             if annotate:
                 annotations.insert(0, annotate)
-            preview_data = mimeview.preview_data(context, node.get_content(),
+            preview_data = mimeview.preview_data(context,
+                                                 node.get_processed_content(),
                                                  node.get_content_length(),
                                                  mime_type, node.created_path,
                                                  raw_href,
@@ -704,18 +746,19 @@ class BrowserModule(Component):
         if node is not None and node.isfile:
             return href.export(rev or 'HEAD', repos.reponame or None,
                                node.path)
-        path = npath = '' if node is None else node.path.strip('/')
-        if repos.reponame:
-            path = (repos.reponame + '/' + npath).rstrip('/')
-        if any(fnmatchcase(path, p.strip('/'))
-               for p in self.downloadable_paths):
-            return href.changeset(rev or repos.youngest_rev,
-                                  repos.reponame or None, npath,
-                                  old=rev, old_path=repos.reponame or '/',
-                                  format='zip')
+        path = '' if node is None else node.path.strip('/')
+        if self.is_path_downloadable(repos, path):
+            return href.browser(repos.reponame or None, path,
+                                rev=rev or repos.youngest_rev, format='zip')
 
     # public methods
 
+    def is_path_downloadable(self, repos, path):
+        if repos.reponame:
+            path = repos.reponame + '/' + path
+        return any(fnmatchcase(path, dp.strip('/'))
+                   for dp in self.downloadable_paths)
+
     def render_properties(self, mode, context, props):
         """Prepare rendering of a collection of properties."""
         return filter(None, [self.render_property(name, mode, context, props)

Modified: 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/web_ui/changeset.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/web_ui/changeset.py?rev=1639823&r1=1639822&r2=1639823&view=diff
==============================================================================
--- 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/web_ui/changeset.py
 (original)
+++ 
bloodhound/branches/trac-1.0.2-integration/trac/trac/versioncontrol/web_ui/changeset.py
 Sat Nov 15 01:14:46 2014
@@ -20,6 +20,7 @@
 
 from __future__ import with_statement
 
+from functools import partial
 from itertools import groupby
 import os
 import posixpath
@@ -39,12 +40,13 @@ from trac.util import as_bool, content_d
 from trac.util.datefmt import from_utimestamp, pretty_timedelta
 from trac.util.text import exception_to_unicode, to_unicode, \
                            unicode_urlencode, shorten_line, CRLF
-from trac.util.translation import _, ngettext
+from trac.util.translation import _, ngettext, tag_
 from trac.versioncontrol.api import RepositoryManager, Changeset, Node, \
                                     NoSuchChangeset
 from trac.versioncontrol.diff import get_diff_options, diff_blocks, \
                                      unified_diff
 from trac.versioncontrol.web_ui.browser import BrowserModule
+from trac.versioncontrol.web_ui.util import render_zip
 from trac.web import IRequestHandler, RequestDone
 from trac.web.chrome import (Chrome, INavigationContributor, add_ctxtnav,
                              add_link, add_script, add_stylesheet,
@@ -100,7 +102,7 @@ class DefaultPropertyDiffRenderer(Compon
         unidiff = '--- \n+++ \n' + \
                   '\n'.join(unified_diff(old.splitlines(), new.splitlines(),
                                          options.get('contextlines', 3)))
-        return tag.li('Property ', tag.strong(name),
+        return tag.li(tag_("Property %(name)s", name=tag.strong(name)),
                       Mimeview(self.env).render(old_context, 'text/x-diff',
                                                 unidiff))
 
@@ -213,9 +215,9 @@ class ChangesetModule(Component):
         req.perm.require('CHANGESET_VIEW')
 
         # -- retrieve arguments
-        full_new_path = new_path = req.args.get('new_path')
+        new_path = req.args.get('new_path')
         new = req.args.get('new')
-        full_old_path = old_path = req.args.get('old_path')
+        old_path = req.args.get('old_path')
         old = req.args.get('old')
         reponame = req.args.get('reponame')
 
@@ -252,14 +254,14 @@ class ChangesetModule(Component):
 
         # -- normalize and check for special case
         try:
-            new_path = repos.normalize_path(new_path)
             new = repos.normalize_rev(new)
-            full_new_path = '/' + pathjoin(repos.reponame, new_path)
-            old_path = repos.normalize_path(old_path or new_path)
             old = repos.normalize_rev(old or new)
-            full_old_path = '/' + pathjoin(repos.reponame, old_path)
         except NoSuchChangeset, e:
-            raise ResourceNotFound(e.message, _('Invalid Changeset Number'))
+            raise ResourceNotFound(e.message, _("Invalid Changeset Number"))
+        new_path = repos.normalize_path(new_path)
+        old_path = repos.normalize_path(old_path or new_path)
+        full_new_path = '/' + pathjoin(repos.reponame, new_path)
+        full_old_path = '/' + pathjoin(repos.reponame, old_path)
 
         if old_path == new_path and old == new: # revert to Changeset
             old_path = old = None
@@ -268,7 +270,7 @@ class ChangesetModule(Component):
         diff_opts = diff_data['options']
 
         # -- setup the `chgset` and `restricted` flags, see docstring above.
-        chgset = not old and not old_path
+        chgset = not old and old_path is None
         if chgset:
             restricted = new_path not in ('', '/') # (subset or not)
         else:
@@ -305,7 +307,7 @@ class ChangesetModule(Component):
                 new = repos.youngest_rev
             elif not old:
                 old = repos.youngest_rev
-            if not old_path:
+            if old_path is None:
                 old_path = new_path
             data = {'old_path': old_path, 'old_rev': old,
                     'new_path': new_path, 'new_rev': new}
@@ -339,15 +341,14 @@ class ChangesetModule(Component):
                 if restricted:
                     filename = 'diff-%s-from-%s-to-%s' \
                                   % (rpath, old, new)
-                elif old_path == '/': # special case for download (#238)
-                    filename = '%s-%s' % (rpath, old)
                 else:
                     filename = 'diff-from-%s-%s-to-%s-%s' \
                                % (old_path.replace('/','_'), old, rpath, new)
             if format == 'diff':
                 self._render_diff(req, filename, repos, data)
             elif format == 'zip':
-                self._render_zip(req, filename, repos, data)
+                render_zip(req, filename + '.zip', repos, None,
+                           partial(self._zip_iter_nodes, req, repos, data))
 
         # -- HTML format
         self._render_html(req, repos, chgset, restricted, xhr, data)
@@ -753,47 +754,16 @@ class ChangesetModule(Component):
         req.write(diff_str)
         raise RequestDone
 
-    def _render_zip(self, req, filename, repos, data):
-        """ZIP archive containing all the added and/or modified files."""
-        req.send_response(200)
-        req.send_header('Content-Type', 'application/zip')
-        req.send_header('Content-Disposition',
-                        content_disposition('attachment', filename + '.zip'))
-
-        from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED as compression
-
-        buf = StringIO()
-        zipfile = ZipFile(buf, 'w', compression)
+    def _zip_iter_nodes(self, req, repos, data, root_node):
+        """Node iterator yielding all the added and/or modified files."""
         for old_node, new_node, kind, change in repos.get_changes(
             new_path=data['new_path'], new_rev=data['new_rev'],
             old_path=data['old_path'], old_rev=data['old_rev']):
             if (kind == Node.FILE or kind == Node.DIRECTORY) and \
                     change != Changeset.DELETE \
                     and new_node.is_viewable(req.perm):
-                zipinfo = ZipInfo()
-                # Note: unicode filenames are not supported by zipfile.
-                # UTF-8 is not supported by all Zip tools either,
-                # but as some do, UTF-8 is the best option here.
-                zipinfo.filename = new_node.path.strip('/').encode('utf-8')
-                zipinfo.flag_bits |= 0x800 # filename is encoded with utf-8
-                zipinfo.date_time = new_node.last_modified.utctimetuple()[:6]
-                zipinfo.compress_type = compression
-                # setting zipinfo.external_attr is needed since Python 2.5
-                if new_node.isfile:
-                    zipinfo.external_attr = 0644 << 16L
-                    content = new_node.get_content().read()
-                elif new_node.isdir:
-                    zipinfo.filename += '/'
-                    zipinfo.external_attr = 040755 << 16L
-                    content = ''
-                zipfile.writestr(zipinfo, content)
-        zipfile.close()
+                yield new_node
 
-        zip_str = buf.getvalue()
-        req.send_header("Content-Length", len(zip_str))
-        req.end_headers()
-        req.write(zip_str)
-        raise RequestDone
 
     def title_for_diff(self, data):
         # TRANSLATOR: 'latest' (revision)
@@ -1205,7 +1175,7 @@ class AnyDiffModule(Component):
                                if repos.is_viewable(req.perm))
 
             elem = tag.ul(
-                [tag.li(tag.b(path) if isdir else path)
+                [tag.li(tag.strong(path) if isdir else path)
                  for (isdir, name, path) in sorted(entries, key=kind_order)
                  if name.lower().startswith(prefix)])
 


Reply via email to