mirror of
https://github.com/vitalif/viewvc-4intranet
synced 2019-04-16 04:14:59 +03:00
Compare commits
90 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
64f27831ba | ||
![]() |
9d097d15a0 | ||
![]() |
e55a53826a | ||
![]() |
2e42e8210a | ||
![]() |
07019eaf2b | ||
![]() |
ee17367b8b | ||
![]() |
63e89c6517 | ||
![]() |
89c5af7d9d | ||
![]() |
743fcdd6ae | ||
![]() |
9451f373e8 | ||
![]() |
c0ce6b9dbe | ||
![]() |
e498d9fdbe | ||
![]() |
1bb0b7ee38 | ||
![]() |
7bafa7b77e | ||
![]() |
1bc2914f4b | ||
![]() |
06f6ba27d0 | ||
![]() |
6cf12dd9b3 | ||
![]() |
a234ea1105 | ||
![]() |
fd3db29ade | ||
![]() |
2e684b109f | ||
![]() |
39c10d903b | ||
![]() |
25a30fb45f | ||
![]() |
2f1008737a | ||
![]() |
e800659377 | ||
![]() |
f75096b6c6 | ||
![]() |
8f28ea0798 | ||
![]() |
2a6c68b575 | ||
![]() |
b1becd4c33 | ||
![]() |
e3514e6a3d | ||
![]() |
2337c9be23 | ||
![]() |
d803878191 | ||
![]() |
269f2087b4 | ||
![]() |
220af43401 | ||
![]() |
de7782a53a | ||
![]() |
d177c057b0 | ||
![]() |
65a68936e7 | ||
![]() |
1c613a947f | ||
![]() |
da5b54e95d | ||
![]() |
4e27b4f06b | ||
![]() |
88c95433f2 | ||
![]() |
4db1ef6d19 | ||
![]() |
2af50a5f27 | ||
![]() |
f4f3459c8e | ||
![]() |
ec081f5d8e | ||
![]() |
e0eb1f6331 | ||
![]() |
94ba398034 | ||
![]() |
7e250b8f24 | ||
![]() |
64eb245377 | ||
![]() |
d3092409b2 | ||
![]() |
652dc5b571 | ||
![]() |
d04cdeca8c | ||
![]() |
480eb1a1f7 | ||
![]() |
e477c03157 | ||
![]() |
b4d5c1d9d6 | ||
![]() |
8e1eb9be9a | ||
![]() |
cb1bf2a45b | ||
![]() |
60ae172120 | ||
![]() |
f029071eae | ||
![]() |
fa95ce8b08 | ||
![]() |
3a415a308e | ||
![]() |
627e506a76 | ||
![]() |
267ea727a0 | ||
![]() |
965e1cfaff | ||
![]() |
3ffe822b0c | ||
![]() |
e6de1376bc | ||
![]() |
3f09d2e039 | ||
![]() |
ef501bd217 | ||
![]() |
e0342ff46e | ||
![]() |
d3210a1fec | ||
![]() |
2ba682c141 | ||
![]() |
6f67d8874a | ||
![]() |
88ef87c6af | ||
![]() |
dae7542569 | ||
![]() |
ea28160e45 | ||
![]() |
384d0dc1a6 | ||
![]() |
95927627b2 | ||
![]() |
d95ceab8a3 | ||
![]() |
1aff68d647 | ||
![]() |
4a3e55dac7 | ||
![]() |
171fadee27 | ||
![]() |
bef839ab4e | ||
![]() |
6647de80c3 | ||
![]() |
99e1a51e20 | ||
![]() |
c25dd8c213 | ||
![]() |
6cc6bde99f | ||
![]() |
d4c351e01f | ||
![]() |
7a5a7cfb69 | ||
![]() |
38b429220e | ||
![]() |
f42e157e88 | ||
![]() |
4312ecd3fa |
53
CHANGES
53
CHANGES
@@ -1,3 +1,56 @@
|
|||||||
|
Version 1.0.8 (released 05-May-2009)
|
||||||
|
|
||||||
|
* fix directory view sorting UI
|
||||||
|
* tolerate malformed Accept-Language headers (issue #396)
|
||||||
|
* fix directory log views in revision-less Subversion repositories
|
||||||
|
* fix exception in rev-sorted remote Subversion directory views (issue #409)
|
||||||
|
|
||||||
|
Version 1.0.7 (released 14-Oct-2008)
|
||||||
|
|
||||||
|
* fix regression in the 'as text' download view (issue #373)
|
||||||
|
|
||||||
|
Version 1.0.6 (released 16-Sep-2008)
|
||||||
|
|
||||||
|
* security fix: ignore arbitrary user-provided MIME types (issue #354)
|
||||||
|
* fix bug in regexp search filter when used with sticky tag (issue #346)
|
||||||
|
* fix bug in handling of certain 'co' output (issue #348)
|
||||||
|
* fix regexp search filter template bug
|
||||||
|
* fix annotate code syntax error
|
||||||
|
* fix mod_python import cycle (issue #369)
|
||||||
|
|
||||||
|
Version 1.0.5 (released 28-Feb-2008)
|
||||||
|
|
||||||
|
* security fix: omit commits of all-forbidden files from query results
|
||||||
|
* security fix: disallow direct URL navigation to hidden CVSROOT folder
|
||||||
|
* security fix: strip forbidden paths from revision view
|
||||||
|
* security fix: don't traverse log history thru forbidden locations
|
||||||
|
* security fix: honor forbiddenness via diff view path parameters
|
||||||
|
* new 'forbiddenre' regexp-based path authorization feature
|
||||||
|
* fix root name conflict resolution inconsistencies (issue #287)
|
||||||
|
* fix an oversight in the CVS 1.12.9 loginfo-handler support
|
||||||
|
* fix RSS feed content type to be more specific (issue #306)
|
||||||
|
* fix entity escaping problems in RSS feed data (issue #238)
|
||||||
|
* fix bug in tarball generation for remote Subversion repositories
|
||||||
|
* fix query interface file-count-limiting logic
|
||||||
|
* fix query results plus/minus count to ignore forbidden files
|
||||||
|
* fix blame error caused by 'svn' unable to create runtime config dir
|
||||||
|
|
||||||
|
Version 1.0.4 (released 10-Apr-2007)
|
||||||
|
|
||||||
|
* fix some markup bugs in query views (issue #266)
|
||||||
|
* fix loginfo-handler's support for CVS 1.12.9 (issues #151, #257)
|
||||||
|
* make viewvc-install able to run from an arbitrary location
|
||||||
|
* update viewvc-install's output for readability
|
||||||
|
* fix bug writing commits to non-MyISAM databases (issue #262)
|
||||||
|
* allow long paths in generated tarballs (issue #12)
|
||||||
|
* fix bug interpreting EZT substitute patterns
|
||||||
|
* fix broken markup view disablement
|
||||||
|
* fix broken directory view link generation in directory log view
|
||||||
|
* fix Windows-specific viewvc-install bugs
|
||||||
|
* fix broke query result links for Subversion deleted items (issue #296)
|
||||||
|
* fix some output XHTML validation buglets
|
||||||
|
* fix database query cache staleness problems (issue #180)
|
||||||
|
|
||||||
Version 1.0.3 (released 13-Oct-2006)
|
Version 1.0.3 (released 13-Oct-2006)
|
||||||
|
|
||||||
* fix bug in path shown for Subversion deleted-under-copy items (issue #265)
|
* fix bug in path shown for Subversion deleted-under-copy items (issue #265)
|
||||||
|
314
INSTALL
314
INSTALL
@@ -1,11 +1,12 @@
|
|||||||
CONTENTS
|
CONTENTS
|
||||||
--------
|
--------
|
||||||
TO THE IMPATIENT
|
TO THE IMPATIENT
|
||||||
|
SECURITY INFORMATION
|
||||||
INSTALLING VIEWVC
|
INSTALLING VIEWVC
|
||||||
APACHE CONFIGURATION
|
APACHE CONFIGURATION
|
||||||
UPGRADING VIEWVC
|
UPGRADING VIEWVC
|
||||||
SQL CHECKIN DATABASE
|
SQL CHECKIN DATABASE
|
||||||
ENSCRIPT AND HIGHLIGHT CONFIGURATION
|
ENABLING SYNTAX COLORATION
|
||||||
CVSGRAPH CONFIGURATION
|
CVSGRAPH CONFIGURATION
|
||||||
IF YOU HAVE PROBLEMS...
|
IF YOU HAVE PROBLEMS...
|
||||||
|
|
||||||
@@ -50,26 +51,60 @@ Congratulations on getting this far. :-)
|
|||||||
* CvsGraph 1.5.0 or later, graphical CVS revision tree generator
|
* CvsGraph 1.5.0 or later, graphical CVS revision tree generator
|
||||||
(http://www.akhphd.au.dk/~bertho/cvsgraph/)
|
(http://www.akhphd.au.dk/~bertho/cvsgraph/)
|
||||||
|
|
||||||
GUI Operation:
|
Quick sanity check:
|
||||||
|
|
||||||
If you just want to see what your CVS repository looks like with
|
If you just want to see what your repository looks like when seen
|
||||||
ViewVC, type "bin/standalone.py -g -r /PATH/TO/CVS/ROOT". This
|
through ViewVC, type:
|
||||||
will start a tiny webserver serving at http://localhost:7467/.
|
|
||||||
PLEASE NOTE: This requires Python with thread support enabled and
|
|
||||||
the Tkinter GUI. If you don't have one of these, omit the '-g' option.
|
|
||||||
|
|
||||||
|
$ bin/standalone.py -r /PATH/TO/REPOSITORY
|
||||||
|
|
||||||
|
This will start a tiny ViewVC server at http://localhost:7467/viewvc/,
|
||||||
|
to which you can connect with your browser.
|
||||||
|
|
||||||
Standard operation:
|
Standard operation:
|
||||||
|
|
||||||
To start installing right away (on UNIX): type "./viewvc-install"
|
To start installing right away (on UNIX): type "./viewvc-install"
|
||||||
in the current directory and answer the prompts. When it
|
in the current directory and answer the prompts. When it
|
||||||
finishes, edit the file viewvc.conf in the installation directory
|
finishes, edit the file viewvc.conf in the installation directory
|
||||||
to tell viewvc the paths to your CVS and Subversion repositories.
|
to tell ViewVC the paths to your CVS and Subversion repositories.
|
||||||
Next, configure your web server (in the way appropriate to that browser)
|
Next, configure your web server (in the way appropriate to that browser)
|
||||||
to run <VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/viewvc.cgi. The section
|
to run <VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/viewvc.cgi. The section
|
||||||
`INSTALLING VIEWVC' below is still recommended reading.
|
`INSTALLING VIEWVC' below is still recommended reading.
|
||||||
|
|
||||||
|
|
||||||
|
SECURITY INFORMATION
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
ViewVC provides a feature which allows version controlled content to
|
||||||
|
be served to web browsers just like static web server content. So, if
|
||||||
|
you have a directory full of interrelated HTML files that is housed in
|
||||||
|
your version control repository, ViewVC can serve those files as HTML.
|
||||||
|
You'll see in your web browser what you'd see if the files were part
|
||||||
|
of your website, with working references to stylesheets and images and
|
||||||
|
links to other pages.
|
||||||
|
|
||||||
|
It is important to realize, however, that as useful as that feature
|
||||||
|
is, there is some risk security-wise in its use. Essentially, anyone
|
||||||
|
with commit access to the CVS or Subversion repositories served by
|
||||||
|
ViewVC has the ability to affect site content. If a discontented or
|
||||||
|
ignorant user commits malicious HTML to a version controlled file
|
||||||
|
(perhaps just by way of documenting examples of such), that malicious
|
||||||
|
HTML is effectively published and live on your ViewVC instance.
|
||||||
|
Visitors viewing those versioned controlled documents get the
|
||||||
|
malicious code, too, which might not be what the original author
|
||||||
|
intended.
|
||||||
|
|
||||||
|
If you wish to disable ViewVC's "checkout" view which implements this
|
||||||
|
feature, you can do so by editing lib/viewvc.py, and modifying the
|
||||||
|
function view_checkout() like so, adding the lines indicated:
|
||||||
|
|
||||||
|
def view_checkout(request):
|
||||||
|
>> raise debug.ViewVCException('Checkout view is disabled',
|
||||||
|
>> '403 Forbidden')
|
||||||
|
path, rev = _orig_path(request)
|
||||||
|
fp, revision = request.repos.openfile(path, rev)
|
||||||
|
|
||||||
|
|
||||||
INSTALLING VIEWVC
|
INSTALLING VIEWVC
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
@@ -107,8 +142,11 @@ installation instructions.
|
|||||||
3) Edit <VIEWVC_INSTALLATION_DIRECTORY>/viewvc.conf for your specific
|
3) Edit <VIEWVC_INSTALLATION_DIRECTORY>/viewvc.conf for your specific
|
||||||
configuration. In particular, examine the following configuration options:
|
configuration. In particular, examine the following configuration options:
|
||||||
|
|
||||||
cvs_roots
|
cvs_roots (for CVS)
|
||||||
|
svn_roots (for Subversion)
|
||||||
|
root_parents (for CVS or Subversion)
|
||||||
default_root
|
default_root
|
||||||
|
root_as_url_component
|
||||||
rcs_path
|
rcs_path
|
||||||
mime_types_file
|
mime_types_file
|
||||||
|
|
||||||
@@ -125,15 +163,15 @@ installation instructions.
|
|||||||
scripts after the install (unlike the other files in ViewVC, the scripts
|
scripts after the install (unlike the other files in ViewVC, the scripts
|
||||||
under bin/ can be moved).
|
under bin/ can be moved).
|
||||||
|
|
||||||
If you are using Apache, then see below at the section
|
If you are using Apache, then see below at the section titled
|
||||||
titled APACHE CONFIGURATION.
|
APACHE CONFIGURATION.
|
||||||
|
|
||||||
NOTE: for security reasons, it is not advisable to install ViewVC
|
NOTE: for security reasons, it is not advisable to install ViewVC
|
||||||
directly into your published HTTP directory tree (due to the MySQL
|
directly into your published HTTP directory tree (due to the MySQL
|
||||||
passwords in viewvc.conf).
|
passwords in viewvc.conf).
|
||||||
|
|
||||||
5) That's it for repository browsing. Instructions for getting the
|
That's it for repository browsing. Instructions for getting the SQL
|
||||||
SQL checkin database working are below.
|
checkin database working are below.
|
||||||
|
|
||||||
|
|
||||||
APACHE CONFIGURATION
|
APACHE CONFIGURATION
|
||||||
@@ -206,28 +244,37 @@ or if you've got Mod_Python installed you can use METHOD D:
|
|||||||
In your httpd.conf you can control access to certain modules by adding
|
In your httpd.conf you can control access to certain modules by adding
|
||||||
directives like this:
|
directives like this:
|
||||||
|
|
||||||
<Location "<url to viewvc.cgi>/<modname_you_wish_to_access_ctl>">
|
<Location "<url to viewvc.cgi>/<modname_you_wish_to_access_ctl>">
|
||||||
AllowOverride None
|
AllowOverride None
|
||||||
AuthUserFile /path/to/passwd/file
|
AuthUserFile /path/to/passwd/file
|
||||||
AuthName "Client Access"
|
AuthName "Client Access"
|
||||||
AuthType Basic
|
AuthType Basic
|
||||||
require valid-user
|
require valid-user
|
||||||
</Location>
|
</Location>
|
||||||
|
|
||||||
WARNING: If you enable the "checkout_magic" or "allow_tar" options, you
|
WARNING: If you enable the "checkout_magic" or "allow_tar" options, you
|
||||||
will need to add additional location directives to prevent people
|
will need to add additional location directives to prevent people
|
||||||
from sneaking in with URLs like:
|
from sneaking in with URLs like:
|
||||||
|
|
||||||
http://<server_name>/viewvc/*checkout*/<module_name>
|
http://<server_name>/viewvc/*checkout*/<module_name>
|
||||||
http://<server_name>/viewvc/~checkout~/<module_name>
|
http://<server_name>/viewvc/~checkout~/<module_name>
|
||||||
http://<server_name>/viewvc/<module_name>.tar.gz?view=tar
|
http://<server_name>/viewvc/<module_name>.tar.gz?view=tar
|
||||||
|
|
||||||
|
5) Optional: Protect your ViewVC instance from server-whacking webcrawlers.
|
||||||
|
|
||||||
|
As ViewVC is a web-based application which each page containing various
|
||||||
|
links to other pages and views, you can expect your server's performance
|
||||||
|
to suffer if a webcrawler finds your ViewVC instance and begins
|
||||||
|
traversing those links. We highly recommend that you add your ViewVC
|
||||||
|
location to a site-wide robots.txt file. Visit the Wikipedia page
|
||||||
|
for Robots.txt (http://en.wikipedia.org/wiki/Robots.txt) for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
|
||||||
UPGRADING VIEWVC
|
UPGRADING VIEWVC
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
Please read the file upgrading.html in the viewvc.org/ subdirectory or
|
Please read the file upgrading-howto.html in the docs/ subdirectory.
|
||||||
at <http://viewvc.org/upgrading.html>.
|
|
||||||
|
|
||||||
|
|
||||||
SQL CHECKIN DATABASE
|
SQL CHECKIN DATABASE
|
||||||
@@ -258,91 +305,105 @@ there are some additional steps required to get the database working.
|
|||||||
enabled = 1 # Whether to enable query support in viewvc.cgi
|
enabled = 1 # Whether to enable query support in viewvc.cgi
|
||||||
host = # MySQL database server host
|
host = # MySQL database server host
|
||||||
port = # MySQL database server port (default is 3306)
|
port = # MySQL database server port (default is 3306)
|
||||||
database_name = # the name of the database you created with
|
database_name = # name of database you created with make-database
|
||||||
# make-database
|
user = # read/write database user
|
||||||
user = # the read/write database user
|
|
||||||
passwd = # password for read/write database user
|
passwd = # password for read/write database user
|
||||||
readonly_user = # the readonly database user -- it's pretty
|
readonly_user = # read-only database user
|
||||||
# safe to use the read/write user here
|
readonly_passwd = # password for the read-only user
|
||||||
readonly_passwd = # password for the readonly user
|
|
||||||
|
|
||||||
5) Two programs are provided for updating the checkin database from a
|
Note that it's pretty safe in this instance for your read-only user
|
||||||
CVS repository, cvsdbadmin and loginfo-handler. They serve two
|
and your read-write user to be the same.
|
||||||
different purposes. The cvsdbadmin program walks through your CVS
|
|
||||||
repository and adds every commit in every file. This is commonly
|
|
||||||
used for initializing the database from a repository which has been
|
|
||||||
in use. The loginfo-handler script is executed by the CVS server's
|
|
||||||
CVSROOT/loginfo system upon each commit. It makes real-time
|
|
||||||
updates to the checkin database as commits are made to the
|
|
||||||
repository.
|
|
||||||
|
|
||||||
To build a database of all the commits in the CVS repository /home/cvs,
|
5) At this point, you need to tell your version control system(s) to
|
||||||
invoke: "./cvsdbadmin rebuild /home/cvs". If you want to update
|
publish their commit information to the database. This is done
|
||||||
the checkin database, invoke: "./cvsdbadmin update /home/cvs". The
|
using utilities that ViewVC provides.
|
||||||
update mode checks to see if a commit is already in the database,
|
|
||||||
and only adds it if it is absent.
|
|
||||||
|
|
||||||
To get real-time updates, you'll want to checkout the CVSROOT module
|
To publish CVS commits into the database:
|
||||||
from your CVS repository and edit CVSROOT/loginfo. Add the line:
|
|
||||||
|
|
||||||
ALL <VIEWVC_INSTALLATION_DIRECTORY>/bin/loginfo-handler %{sVv}
|
Two programs are provided for updating the checkin database from
|
||||||
|
a CVS repository, cvsdbadmin and loginfo-handler. They serve
|
||||||
|
two different purposes. The cvsdbadmin program walks through
|
||||||
|
your CVS repository and adds every commit in every file. This
|
||||||
|
is commonly used for initializing the database from a repository
|
||||||
|
which has been in use. The loginfo-handler script is executed
|
||||||
|
by the CVS server's CVSROOT/loginfo system upon each commit. It
|
||||||
|
makes real-time updates to the checkin database as commits are
|
||||||
|
made to the repository.
|
||||||
|
|
||||||
If you have other scripts invoked by CVSROOT/loginfo, you will want
|
To build a database of all the commits in the CVS repository
|
||||||
to make sure to change any running under the "DEFAULT" keyword to
|
/home/cvs, invoke: "./cvsdbadmin rebuild /home/cvs". If you
|
||||||
"ALL" like the loginfo handler, and probably carefully read the
|
want to update the checkin database, invoke: "./cvsdbadmin
|
||||||
execution rules for CVSROOT/loginfo from the CVS manual.
|
update /home/cvs". The update mode checks to see if a commit is
|
||||||
|
already in the database, and only adds it if it is absent.
|
||||||
|
|
||||||
If you are running the Unix port of CVS-NT, you'll need to use a
|
To get real-time updates, you'll want to checkout the CVSROOT
|
||||||
slightly different command line:
|
module from your CVS repository and edit CVSROOT/loginfo. For
|
||||||
|
folks running CVS 1.12 or better, add this line:
|
||||||
|
|
||||||
ALL <VIEWVC_INSTALLATION_DIRECTORY>/bin/loginfo-handler %{sVv} cvsnt
|
ALL <VIEWVC_INSTALLATION_DIRECTORY>/bin/loginfo-handler %p %{sVv}
|
||||||
|
|
||||||
The extra 'cvsnt' parameter tells the handler script to parse the
|
If you are running CVS 1.11 or earlier, you'll want a slightly
|
||||||
commit information in a different way.
|
different command line in CVSROOT/loginfo:
|
||||||
|
|
||||||
For Subversion repositories, there is a single script called
|
ALL <VIEWVC_INSTALLATION_DIRECTORY>/bin/loginfo-handler %{sVv}
|
||||||
svndbadmin that performs both of the above tasks.
|
|
||||||
|
|
||||||
To build a database of all the commits in the Subversion repository
|
If you have other scripts invoked by CVSROOT/loginfo, you will
|
||||||
/home/svn, invoke: "./svndbadmin rebuild /home/svn". If you want
|
want to make sure to change any running under the "DEFAULT"
|
||||||
to update the checkin database, invoke: "./svndbadmin update
|
keyword to "ALL" like the loginfo handler, and probably
|
||||||
/home/svn".
|
carefully read the execution rules for CVSROOT/loginfo from the
|
||||||
|
CVS manual.
|
||||||
|
|
||||||
To get real time updates, you will need to add a post-commit hook
|
If you are running the Unix port of CVS-NT, the handler script
|
||||||
(for the repository example above, the script should go in
|
need to know about it. CVS-NT delivers commit information to
|
||||||
/home/svn/hooks/post-commit). The script should look something
|
loginfo scripts differently than the way mainstream CVS does.
|
||||||
like this:
|
Your command line should look like this:
|
||||||
|
|
||||||
#!/bin/sh
|
ALL <VIEWVC_INSTALLATION_DIRECTORY>/bin/loginfo-handler %{sVv} cvsnt
|
||||||
REPOS="$1"
|
|
||||||
REV="$2"
|
|
||||||
<VIEWVC_INSTALLATION_DIRECTORY>/bin/svndbadmin rebuild "$REPOS" "$REV"
|
|
||||||
|
|
||||||
If you allow revision property changes in your repository, create a
|
To publish Subversion commits into the database:
|
||||||
post-revprop-change hook script containing the same commands as the
|
|
||||||
post-commit one. This will make sure that the checkin database
|
|
||||||
stays consistent when you change the svn:log, svn:author or
|
|
||||||
svn:date revision properties.
|
|
||||||
|
|
||||||
6) You should be ready to go. Click one of the "Query revision history"
|
To build a database of all the commits in the Subversion
|
||||||
links in ViewVC directory listings and give it a try.
|
repository /home/svn, invoke: "./svndbadmin rebuild /home/svn".
|
||||||
|
If you want to update the checkin database, invoke:
|
||||||
|
"./svndbadmin update /home/svn".
|
||||||
|
|
||||||
|
To get real time updates, you will need to add a post-commit
|
||||||
|
hook (for the repository example above, the script should go in
|
||||||
|
/home/svn/hooks/post-commit). The script should look something
|
||||||
|
like this:
|
||||||
|
|
||||||
|
#!/bin/sh
|
||||||
|
REPOS="$1"
|
||||||
|
REV="$2"
|
||||||
|
<VIEWVC_INSTALLATION_DIRECTORY>/bin/svndbadmin rebuild "$REPOS" "$REV"
|
||||||
|
|
||||||
|
If you allow revision property changes in your repository,
|
||||||
|
create a post-revprop-change hook script containing the same
|
||||||
|
commands as the post-commit one. This will make sure that the
|
||||||
|
checkin database stays consistent when you change the svn:log,
|
||||||
|
svn:author or svn:date revision properties.
|
||||||
|
|
||||||
|
You should be ready to go. Click one of the "Query revision history"
|
||||||
|
links in ViewVC directory listings and give it a try.
|
||||||
|
|
||||||
|
|
||||||
ENSCRIPT AND HIGHLIGHT CONFIGURATION
|
ENABLING SYNTAX COLORATION
|
||||||
------------------------------------
|
--------------------------
|
||||||
|
|
||||||
Enscript and Highlight are programs that can colorize source code for
|
Enscript and Highlight are two programs that can colorize source code
|
||||||
a lot of languages. ViewVC can be configured to use either one.
|
for a lot of languages. ViewVC can be configured to use either one.
|
||||||
|
|
||||||
1) Install Enscript or Highlight using your system's package manager
|
1) Install Enscript or Highlight using your system's package manager
|
||||||
or downloading from the project home pages.
|
or downloading from the project home pages.
|
||||||
|
|
||||||
2) Set the 'use_enscript' or 'use_highlight' options in viewvc.conf to 1.
|
2) Set either the 'use_enscript' or 'use_highlight' options in
|
||||||
|
viewvc.conf to 1.
|
||||||
|
|
||||||
3) You may also need to set 'enscript_path' and 'highlight_path' options
|
3) You may also need to set 'enscript_path' or 'highlight_path' option
|
||||||
if the executables are not located on the system PATH.
|
if the executables are not located on the system PATH.
|
||||||
|
|
||||||
4) That's it!
|
That's it! Now when you view the contents of recognized filetypes in
|
||||||
|
ViewVC, you should see colorized syntax.
|
||||||
|
|
||||||
|
|
||||||
CVSGRAPH CONFIGURATION
|
CVSGRAPH CONFIGURATION
|
||||||
@@ -371,36 +432,50 @@ a reasonable number of revisions and branches.
|
|||||||
4) There is a file <VIEWVC_INSTALLATION_DIRECTORY>/cvsgraph.conf that
|
4) There is a file <VIEWVC_INSTALLATION_DIRECTORY>/cvsgraph.conf that
|
||||||
you may want to edit if desired to set color and font characteristics.
|
you may want to edit if desired to set color and font characteristics.
|
||||||
See the cvsgraph.conf documentation. No edits are required in
|
See the cvsgraph.conf documentation. No edits are required in
|
||||||
cvsgraph.conf for operation with viewvc.
|
cvsgraph.conf for operation with ViewVC.
|
||||||
|
|
||||||
|
|
||||||
SUBVERSION INTEGRATION
|
SUBVERSION INTEGRATION
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
ViewVC supports browsing of Subversion repositories. To use ViewVC
|
Unlike the CVS integration, which simply wraps the RCS and CVS utility
|
||||||
with Subversion, make sure you have both Subversion itself and
|
programs, the Subversion integration requires additional Python
|
||||||
the Subversion Python bindings installed. See Subversion's
|
libraries. To use ViewVC with Subversion, make sure you have both
|
||||||
installation notes for more details on how to build and install these
|
Subversion itself and the Subversion Python bindings installed. See
|
||||||
items.
|
Subversion's installation notes for more details on how to build and
|
||||||
|
install these items.
|
||||||
|
|
||||||
Generally speaking, you'll know when your installation of Subversion's
|
Generally speaking, you'll know when your installation of Subversion's
|
||||||
bindings has been successful if you can import the 'svn.repos' module
|
bindings has been successful if you can import the 'svn.core' module
|
||||||
from within your Python interpreter:
|
from within your Python interpreter. Here's an example of doing so
|
||||||
|
which doubles as a quick way to check what version of the Subversion
|
||||||
|
Python binding you have:
|
||||||
|
|
||||||
% python
|
% python
|
||||||
Python 2.2.2 (#1, Oct 29 2002, 02:47:30)
|
Python 2.2.2 (#1, Oct 29 2002, 02:47:30)
|
||||||
[GCC 2.96 20000731 (Red Hat Linux 7.2 2.96-108.7.2)] on linux2
|
[GCC 2.96 20000731 (Red Hat Linux 7.2 2.96-108.7.2)] on linux2
|
||||||
Type "help", "copyright", "credits" or "license" for more information.
|
Type "help", "copyright", "credits" or "license" for more information.
|
||||||
>>> import svn.repos
|
>>> from svn.core import *
|
||||||
|
>>> "%s.%s.%s" % (SVN_VER_MAJOR, SVN_VER_MINOR, SVN_VER_PATCH)
|
||||||
|
'1.2.0'
|
||||||
>>>
|
>>>
|
||||||
|
|
||||||
Note that by default, Subversion installs its bindings in a location
|
Note that by default, Subversion installs its bindings in a location
|
||||||
that is not in Python's default module search path (for example, on
|
that is not in Python's default module search path (for example, on
|
||||||
Linux systems the default is usually /usr/local/lib/svn-python). You
|
Linux systems the default is usually /usr/local/lib/svn-python). You
|
||||||
need to remedy this, either by adding this path to Python's module
|
need to remedy this, either by adding this path to Python's module
|
||||||
search path, or by relocation the bindings to some place in that
|
search path, or by relocating the bindings to some place in that
|
||||||
search path.
|
search path.
|
||||||
|
|
||||||
|
For example, you might want to create .pth file in your Python
|
||||||
|
installation directory's site-packages area which tells Python where
|
||||||
|
to find additional modules (in this case, you Subversion Python
|
||||||
|
bindings). You would do this as follows (and as root):
|
||||||
|
|
||||||
|
$ echo "/path/to/svn/bindings" > /path/to/python/site-packages/svn.pth
|
||||||
|
|
||||||
|
(Though, obviously, with the correct paths specified.)
|
||||||
|
|
||||||
Configuration of the Subversion repositories happens in much the same
|
Configuration of the Subversion repositories happens in much the same
|
||||||
way as with CVS repositories, only with the 'svn_roots' configuration
|
way as with CVS repositories, only with the 'svn_roots' configuration
|
||||||
variable instead of the 'cvs_roots' one.
|
variable instead of the 'cvs_roots' one.
|
||||||
@@ -409,39 +484,42 @@ variable instead of the 'cvs_roots' one.
|
|||||||
IF YOU HAVE PROBLEMS ...
|
IF YOU HAVE PROBLEMS ...
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
If you've trouble to make viewvc.cgi work:
|
If nothing seems to work:
|
||||||
|
|
||||||
=== If nothing seems to work:
|
* Check if you can execute CGI-scripts (Apache needs to have an
|
||||||
|
ScriptAlias /cgi-bin or cgi-script Handler defined). Try to
|
||||||
|
execute a simple CGI-script that often comes with the distribution
|
||||||
|
of the webserver; locate the logfiles and try to find hints which
|
||||||
|
explain the malfunction
|
||||||
|
|
||||||
o check if you can execute CGI-scripts (Apache needs to have an
|
* View the entries in the webserver's error.log
|
||||||
ScriptAlias /cgi-bin or cgi-script Handler defined). Try to
|
|
||||||
execute a simple CGI-script that often comes with the distribution
|
|
||||||
of the webserver; locate the logfiles and try to find hints
|
|
||||||
which explain the malfunction
|
|
||||||
|
|
||||||
o view the entries in the webserver's error.log
|
If ViewVC seems to work but doesn't show the expected result (Typical
|
||||||
|
error: you can't see any files)
|
||||||
|
|
||||||
=== If viewvc seems to work but doesn't show the expected result
|
* Check whether the CGI-script has read-permissions to your
|
||||||
(Typical error: you can't see any files)
|
CVS-Repository. The CGI-script generally runs as the same user
|
||||||
|
that the web server does, often user 'nobody' or 'httpd'.
|
||||||
|
|
||||||
o check whether the CGI-script has read-permissions to your
|
* Does ViewVC find your RCS utilities? (edit rcs_path)
|
||||||
CVS-Repository. The CGI-script often runs as the user 'nobody'
|
|
||||||
or 'httpd' ..
|
|
||||||
|
|
||||||
o does viewvc find your RCS utilities? (edit rcs_path)
|
If something else happens or you can't get it to work:
|
||||||
|
|
||||||
=== If something else happens or you can't get it to work:
|
* Check the ViewVC home page:
|
||||||
|
|
||||||
o check the ViewVC home page:
|
http://viewvc.org/
|
||||||
|
|
||||||
http://viewvc.org/
|
* Review the ViewVC mailing list archive to see if somebody else had
|
||||||
|
the same problem, and it was solved:
|
||||||
|
|
||||||
o review the ViewVC mailing list archive to see if somebody else had
|
http://viewvc.tigris.org/servlets/SummarizeList?listName=users
|
||||||
the same problem, and it was solved:
|
|
||||||
|
|
||||||
http://viewvc.tigris.org/servlets/SummarizeList?listName=users
|
* Check the ViewVC issue database to see if the problem you are
|
||||||
|
seeing is the result of a known bug:
|
||||||
|
|
||||||
o send mail to the ViewVC mailing list: users@viewvc.tigris.org
|
http://viewvc.tigris.org/issues/query.cgi
|
||||||
|
|
||||||
NOTE: make sure you provide an accurate description of the problem
|
* Send mail to the ViewVC mailing list, users@viewvc.tigris.org.
|
||||||
and any relevant tracebacks or error logs.
|
NOTE: make sure you provide an accurate description of the problem
|
||||||
|
-- including the version of ViewVC you are using -- and any
|
||||||
|
relevant tracebacks or error logs.
|
||||||
|
@@ -1,63 +1,21 @@
|
|||||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>ViewVC: License v1</title>
|
<title>ViewVC: License v1</title>
|
||||||
<link rel="stylesheet" type="text/css" href="./styles.css"/>
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div id="title">
|
|
||||||
<a href="http://www.viewvc.org/"><img
|
|
||||||
src="./images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="menu">
|
|
||||||
<p><a href="./index.html">Home</a> |
|
|
||||||
<a href="http://viewvc.tigris.org/">Project Page</a> |
|
|
||||||
<a href="./download.html">Download</a> |
|
|
||||||
<a href="./upgrading.html">Upgrading</a> |
|
|
||||||
<a href="./contributing.html">Contributing</a> |
|
|
||||||
<a href="./license-1.html">License</a> |
|
|
||||||
<a href="./contact.html">Contact</a> |
|
|
||||||
<a href="./who.html">About</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table id="pagetable">
|
|
||||||
<tr>
|
|
||||||
<td id="pagecolumn1">
|
|
||||||
|
|
||||||
<h4>On this page:</h4>
|
|
||||||
|
|
||||||
<ul id="bookmarks">
|
|
||||||
<li><a href="#sec-license">License</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<hr/>
|
|
||||||
|
|
||||||
<address><a href="mailto:users@viewvc.tigris.org">ViewVC Users Group</a></address>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td id="pagecolumn2">
|
|
||||||
|
|
||||||
<div class="section">
|
|
||||||
|
|
||||||
<h2 id="sec-license">License</h2>
|
|
||||||
|
|
||||||
<p>The following text constitutes the license agreement for the <a
|
<p>The following text constitutes the license agreement for the <a
|
||||||
href="./index.html">ViewVC</a> software (formerly known as
|
href="http://www.viewvc.org/">ViewVC</a> software (formerly known
|
||||||
ViewCVS). It is an agreement between <a
|
as ViewCVS). It is an agreement between <a
|
||||||
href="./who.html#sec-viewcvs-group">The ViewCVS Group</a> and the
|
href="http://www.viewvc.org/who.html#sec-viewcvs-group">The ViewCVS
|
||||||
users of ViewVC.</p>
|
Group</a> and the users of ViewVC.</p>
|
||||||
|
|
||||||
<p style="font-size: 80%;"><em>Note: the copyright years were updated
|
<blockquote>
|
||||||
on May 12, 2001 and September 5, 2002. No other changes were made
|
|
||||||
to the license.</em></p>
|
|
||||||
|
|
||||||
<hr/>
|
<p><strong>Copyright © 1999-2008 The ViewCVS Group. All rights
|
||||||
|
|
||||||
<p><strong>Copyright © 1999-2002 The ViewCVS Group. All rights
|
|
||||||
reserved.</strong></p>
|
reserved.</strong></p>
|
||||||
|
|
||||||
<p>By using ViewVC, you agree to the terms and conditions set forth
|
<p>By using ViewVC, you agree to the terms and conditions set forth
|
||||||
@@ -90,10 +48,18 @@
|
|||||||
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
SUCH DAMAGE.</p>
|
SUCH DAMAGE.</p>
|
||||||
|
|
||||||
</div>
|
</blockquote>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<p>The following changes have occured to this license over time:</p>
|
||||||
|
<ul>
|
||||||
|
<li>May 12, 2001 — copyright years updated</li>
|
||||||
|
<li>September 5, 2002 — copyright years updated</li>
|
||||||
|
<li>March 17, 2006 — software renamed from "ViewCVS"</li>
|
||||||
|
<li>April 10, 2007 — copyright years updated</li>
|
||||||
|
<li>February 22, 2008 — copyright years updated</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -32,9 +32,9 @@ import sys
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
if LIBRARY_DIR:
|
if LIBRARY_DIR:
|
||||||
sys.path.insert(0, LIBRARY_DIR)
|
sys.path.insert(0, LIBRARY_DIR)
|
||||||
else:
|
else:
|
||||||
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0], "../../lib")))
|
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0], "../../lib")))
|
||||||
|
|
||||||
#########################################################################
|
#########################################################################
|
||||||
|
|
||||||
@@ -51,7 +51,11 @@ DEBUG_FLAG = 0
|
|||||||
## output functions
|
## output functions
|
||||||
def debug(text):
|
def debug(text):
|
||||||
if DEBUG_FLAG:
|
if DEBUG_FLAG:
|
||||||
print 'DEBUG(viewvc-loginfo):', text
|
if type(text) != (type([])):
|
||||||
|
text = [text]
|
||||||
|
for line in text:
|
||||||
|
line = line.rstrip('\n\r')
|
||||||
|
print 'DEBUG(viewvc-loginfo):', line
|
||||||
|
|
||||||
def warning(text):
|
def warning(text):
|
||||||
print 'WARNING(viewvc-loginfo):', text
|
print 'WARNING(viewvc-loginfo):', text
|
||||||
@@ -61,139 +65,160 @@ def error(text):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
_re_revisions = re.compile(
|
_re_revisions = re.compile(
|
||||||
r",(?P<old>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)" # comma and first revision number
|
r",(?P<old>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)" # comma and first revision
|
||||||
r",(?P<new>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)" # comma and second revision number
|
r",(?P<new>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)" # comma and second revision
|
||||||
r"(?:$| )" # space or end of string
|
r"(?:$| )" # space or end of string
|
||||||
)
|
)
|
||||||
|
|
||||||
def HeuristicArgParse(s, repository):
|
def Cvs1Dot12ArgParse(args):
|
||||||
"""Current versions of CVS (except for CVSNT) do not escape spaces in file
|
"""CVS 1.12 introduced a new loginfo format while provides the various
|
||||||
and directory names that are passed to the loginfo handler. Since the input
|
pieces of interesting version information to the handler script as
|
||||||
to loginfo is a space separated string, this can lead to ambiguities. This
|
individual arguments instead of as a single string."""
|
||||||
function attempts to guess intelligently which spaces are separators and
|
|
||||||
which are part of file or directory names. It disambiguates spaces in
|
|
||||||
filenames from the separator spaces between files by assuming that every
|
|
||||||
space which is preceded by two well-formed revision numbers is in fact a
|
|
||||||
separator. It disambiguates the first separator space from spaces in the
|
|
||||||
directory name by choosing the longest possible directory name that actually
|
|
||||||
exists in the repository"""
|
|
||||||
|
|
||||||
if s[-16:] == ' - New directory':
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
if s[-19:] == r' - Imported sources':
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
file_data_list = []
|
|
||||||
start = 0
|
|
||||||
|
|
||||||
while 1:
|
|
||||||
m = _re_revisions.search(s, start)
|
|
||||||
|
|
||||||
if start == 0:
|
|
||||||
if m is None:
|
|
||||||
error('Argument "%s" does not contain any revision numbers' % s)
|
|
||||||
|
|
||||||
directory, filename = FindLongestDirectory(s[:m.start()], repository)
|
|
||||||
if directory is None:
|
|
||||||
error('Argument "%s" does not start with a valid directory' % s)
|
|
||||||
|
|
||||||
debug('Directory name is "%s"' % directory)
|
|
||||||
|
|
||||||
|
if args[1] == '- New directory':
|
||||||
|
return None, None
|
||||||
|
elif args[1] == '- Imported sources':
|
||||||
|
return None, None
|
||||||
else:
|
else:
|
||||||
if m is None:
|
directory = args.pop(0)
|
||||||
warning('Failed to interpret past position %i in the loginfo argument, '
|
files = []
|
||||||
'leftover string is "%s"' % start, pos[start:])
|
while len(args) >= 3:
|
||||||
|
files.append(args[0:3])
|
||||||
|
args = args[3:]
|
||||||
|
return directory, files
|
||||||
|
|
||||||
filename = s[start:m.start()]
|
def HeuristicArgParse(s, repository):
|
||||||
|
"""Older versions of CVS (except for CVSNT) do not escape spaces in file
|
||||||
|
and directory names that are passed to the loginfo handler. Since the input
|
||||||
|
to loginfo is a space separated string, this can lead to ambiguities. This
|
||||||
|
function attempts to guess intelligently which spaces are separators and
|
||||||
|
which are part of file or directory names. It disambiguates spaces in
|
||||||
|
filenames from the separator spaces between files by assuming that every
|
||||||
|
space which is preceded by two well-formed revision numbers is in fact a
|
||||||
|
separator. It disambiguates the first separator space from spaces in the
|
||||||
|
directory name by choosing the longest possible directory name that
|
||||||
|
actually exists in the repository"""
|
||||||
|
|
||||||
old_version, new_version = m.group('old', 'new')
|
if (s[-16:] == ' - New directory'
|
||||||
|
or s[:26] == ' - New directory,NONE,NONE'):
|
||||||
|
return None, None
|
||||||
|
|
||||||
file_data_list.append((filename, old_version, new_version))
|
if (s[-19:] == ' - Imported sources'
|
||||||
|
or s[-29:] == ' - Imported sources,NONE,NONE'):
|
||||||
|
return None, None
|
||||||
|
|
||||||
debug('File "%s", old revision %s, new revision %s'
|
file_data_list = []
|
||||||
% (filename, old_version, new_version))
|
start = 0
|
||||||
|
|
||||||
start = m.end()
|
while 1:
|
||||||
|
m = _re_revisions.search(s, start)
|
||||||
|
|
||||||
if start == len(s): break
|
if start == 0:
|
||||||
|
if m is None:
|
||||||
|
error('Argument "%s" does not contain any revision numbers' \
|
||||||
|
% s)
|
||||||
|
|
||||||
return directory, file_data_list
|
directory, filename = FindLongestDirectory(s[:m.start()],
|
||||||
|
repository)
|
||||||
|
if directory is None:
|
||||||
|
error('Argument "%s" does not start with a valid directory' \
|
||||||
|
% s)
|
||||||
|
|
||||||
|
debug('Directory name is "%s"' % directory)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if m is None:
|
||||||
|
warning('Failed to interpret past position %i in the loginfo '
|
||||||
|
'argument, leftover string is "%s"' \
|
||||||
|
% start, pos[start:])
|
||||||
|
|
||||||
|
filename = s[start:m.start()]
|
||||||
|
|
||||||
|
old_version, new_version = m.group('old', 'new')
|
||||||
|
|
||||||
|
file_data_list.append((filename, old_version, new_version))
|
||||||
|
|
||||||
|
debug('File "%s", old revision %s, new revision %s'
|
||||||
|
% (filename, old_version, new_version))
|
||||||
|
|
||||||
|
start = m.end()
|
||||||
|
|
||||||
|
if start == len(s): break
|
||||||
|
|
||||||
|
return directory, file_data_list
|
||||||
|
|
||||||
def FindLongestDirectory(s, repository):
|
def FindLongestDirectory(s, repository):
|
||||||
"""Splits the first part of the argument string into a directory name
|
"""Splits the first part of the argument string into a directory name
|
||||||
and a file name, either of which may contain spaces. Returns the longest
|
and a file name, either of which may contain spaces. Returns the longest
|
||||||
possible directory name that actually exists"""
|
possible directory name that actually exists"""
|
||||||
|
|
||||||
parts = string.split(s, " ")
|
parts = string.split(s, " ")
|
||||||
|
|
||||||
for i in range(len(parts)-1, 0, -1):
|
for i in range(len(parts)-1, 0, -1):
|
||||||
directory = string.join(parts[:i])
|
directory = string.join(parts[:i])
|
||||||
filename = string.join(parts[i:])
|
filename = string.join(parts[i:])
|
||||||
if os.path.isdir(os.path.join(repository, directory)):
|
if os.path.isdir(os.path.join(repository, directory)):
|
||||||
return directory, filename
|
return directory, filename
|
||||||
|
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
_re_cvsnt_revisions = re.compile(
|
_re_cvsnt_revisions = re.compile(
|
||||||
r"(?P<filename>.*)" # comma and first revision number
|
r"(?P<filename>.*)" # comma and first revision
|
||||||
r",(?P<old>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)" # comma and first revision number
|
r",(?P<old>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)" # comma and first revision
|
||||||
r",(?P<new>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)" # comma and second revision number
|
r",(?P<new>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)" # comma and second revision
|
||||||
r"$" # end of string
|
r"$" # end of string
|
||||||
)
|
)
|
||||||
|
|
||||||
def CvsNtArgParse(s, repository):
|
def CvsNtArgParse(s, repository):
|
||||||
"""CVSNT escapes all spaces in filenames and directory names with
|
"""CVSNT escapes all spaces in filenames and directory names with
|
||||||
backslashes"""
|
backslashes"""
|
||||||
|
|
||||||
if s[-18:] == r' -\ New\ directory':
|
if s[-18:] == r' -\ New\ directory':
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
if s[-21:] == r' -\ Imported\ sources':
|
if s[-21:] == r' -\ Imported\ sources':
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
file_data_list = []
|
file_data_list = []
|
||||||
|
directory, pos = NextFile(s)
|
||||||
|
|
||||||
directory, pos = NextFile(s)
|
debug('Directory name is "%s"' % directory)
|
||||||
|
|
||||||
debug('Directory name is "%s"' % directory)
|
while 1:
|
||||||
|
fileinfo, pos = NextFile(s, pos)
|
||||||
|
if fileinfo is None:
|
||||||
|
break
|
||||||
|
|
||||||
while 1:
|
m = _re_cvsnt_revisions.match(fileinfo)
|
||||||
fileinfo, pos = NextFile(s, pos)
|
if m is None:
|
||||||
if fileinfo is None:
|
warning('Can\'t parse file information in "%s"' % fileinfo)
|
||||||
break
|
continue
|
||||||
|
|
||||||
m = _re_cvsnt_revisions.match(fileinfo)
|
file_data = m.group('filename', 'old', 'new')
|
||||||
if m is None:
|
file_data_list.append(file_data)
|
||||||
warning('Can\'t parse file information in "%s"' % fileinfo)
|
|
||||||
continue
|
|
||||||
|
|
||||||
file_data = m.group('filename', 'old', 'new')
|
debug('File "%s", old revision %s, new revision %s' % file_data)
|
||||||
|
|
||||||
debug('File "%s", old revision %s, new revision %s' % file_data)
|
return directory, file_data_list
|
||||||
|
|
||||||
file_data_list.append(file_data)
|
|
||||||
|
|
||||||
return directory, file_data_list
|
|
||||||
|
|
||||||
def NextFile(s, pos = 0):
|
def NextFile(s, pos = 0):
|
||||||
escaped = 0
|
escaped = 0
|
||||||
ret = ''
|
ret = ''
|
||||||
i = pos
|
i = pos
|
||||||
while i < len(s):
|
while i < len(s):
|
||||||
c = s[i]
|
c = s[i]
|
||||||
if escaped:
|
if escaped:
|
||||||
ret += c
|
ret += c
|
||||||
escaped = 0
|
escaped = 0
|
||||||
elif c == '\\':
|
elif c == '\\':
|
||||||
escaped = 1
|
escaped = 1
|
||||||
elif c == ' ':
|
elif c == ' ':
|
||||||
return ret, i + 1
|
return ret, i + 1
|
||||||
else:
|
else:
|
||||||
ret += c
|
ret += c
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
return ret or None, i
|
return ret or None, i
|
||||||
|
|
||||||
def ProcessLoginfo(rootpath, directory, files):
|
def ProcessLoginfo(rootpath, directory, files):
|
||||||
cfg = viewvc.load_config(CONF_PATHNAME)
|
cfg = viewvc.load_config(CONF_PATHNAME)
|
||||||
@@ -235,41 +260,58 @@ if __name__ == '__main__':
|
|||||||
debug('Repository name is "%s"' % repository)
|
debug('Repository name is "%s"' % repository)
|
||||||
|
|
||||||
## parse arguments
|
## parse arguments
|
||||||
if len(sys.argv) > 1:
|
|
||||||
# the first argument should contain file version information
|
|
||||||
arg = sys.argv[1]
|
|
||||||
else:
|
|
||||||
# if there are no arguments, read version information from first line
|
|
||||||
# of input like old versions of viewcvs
|
|
||||||
arg = string.rstrip(sys.stdin.readline())
|
|
||||||
|
|
||||||
if len(sys.argv) > 2:
|
argc = len(sys.argv)
|
||||||
# if there is a second argument it indicates which parser should be
|
debug('Got %d arguments:' % (argc))
|
||||||
# used to interpret the version information
|
debug(map(lambda x: ' ' + x, sys.argv))
|
||||||
if sys.argv[2] == 'cvs':
|
|
||||||
fun = HeuristicArgParse
|
|
||||||
elif sys.argv[2] == 'cvsnt':
|
|
||||||
fun = CvsNtArgParse
|
|
||||||
else:
|
|
||||||
error('Bad arguments')
|
|
||||||
else:
|
|
||||||
# if there is no second argument, guess which parser to use based
|
|
||||||
# on the operating system. Since CVSNT now runs on Windows and
|
|
||||||
# Linux, the guess isn't neccessarily correct
|
|
||||||
if sys.platform == "win32":
|
|
||||||
fun = CvsNtArgParse
|
|
||||||
else:
|
|
||||||
fun = HeuristicArgParse
|
|
||||||
|
|
||||||
if len(sys.argv) > 3:
|
# if we have more than 3 arguments, we are likely using the
|
||||||
error('Bad arguments')
|
# newer loginfo format introduced in CVS 1.12:
|
||||||
|
#
|
||||||
|
# ALL <path>/bin/loginfo-handler %p %{sVv}
|
||||||
|
if argc > 3:
|
||||||
|
directory, files = Cvs1Dot12ArgParse(sys.argv[1:])
|
||||||
|
else:
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
# the first argument should contain file version information
|
||||||
|
arg = sys.argv[1]
|
||||||
|
else:
|
||||||
|
# if there are no arguments, read version information from
|
||||||
|
# first line of input like old versions of ViewCVS did
|
||||||
|
arg = string.rstrip(sys.stdin.readline())
|
||||||
|
|
||||||
|
if len(sys.argv) > 2:
|
||||||
|
# if there is a second argument it indicates which parser
|
||||||
|
# should be used to interpret the version information
|
||||||
|
if sys.argv[2] == 'cvs':
|
||||||
|
fun = HeuristicArgParse
|
||||||
|
elif sys.argv[2] == 'cvsnt':
|
||||||
|
fun = CvsNtArgParse
|
||||||
|
else:
|
||||||
|
error('Bad arguments')
|
||||||
|
else:
|
||||||
|
# if there is no second argument, guess which parser to use based
|
||||||
|
# on the operating system. Since CVSNT now runs on Windows and
|
||||||
|
# Linux, the guess isn't necessarily correct
|
||||||
|
if sys.platform == "win32":
|
||||||
|
fun = CvsNtArgParse
|
||||||
|
else:
|
||||||
|
fun = HeuristicArgParse
|
||||||
|
|
||||||
|
directory, files = fun(arg, repository)
|
||||||
|
|
||||||
|
debug('Discarded from stdin:')
|
||||||
|
debug(map(lambda x: ' ' + x, sys.stdin.readlines())) # consume stdin
|
||||||
|
|
||||||
repository = cvsdb.CleanRepository(repository)
|
repository = cvsdb.CleanRepository(repository)
|
||||||
directory, files = fun(arg, repository)
|
|
||||||
|
debug('Repository: %s' % (repository))
|
||||||
|
debug('Directory: %s' % (directory))
|
||||||
|
debug('Files: %s' % (str(files)))
|
||||||
|
|
||||||
if files is None:
|
if files is None:
|
||||||
debug('Not a checkin, nothing to do')
|
debug('Not a checkin, nothing to do')
|
||||||
else:
|
else:
|
||||||
ProcessLoginfo(repository, directory, files)
|
ProcessLoginfo(repository, directory, files)
|
||||||
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -39,7 +39,7 @@ CREATE TABLE branches (
|
|||||||
branch varchar(64) binary DEFAULT '' NOT NULL,
|
branch varchar(64) binary DEFAULT '' NOT NULL,
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
UNIQUE branch (branch)
|
UNIQUE branch (branch)
|
||||||
);
|
) TYPE=MyISAM;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS checkins;
|
DROP TABLE IF EXISTS checkins;
|
||||||
CREATE TABLE checkins (
|
CREATE TABLE checkins (
|
||||||
@@ -62,7 +62,7 @@ CREATE TABLE checkins (
|
|||||||
KEY dirid (dirid),
|
KEY dirid (dirid),
|
||||||
KEY fileid (fileid),
|
KEY fileid (fileid),
|
||||||
KEY branchid (branchid)
|
KEY branchid (branchid)
|
||||||
);
|
) TYPE=MyISAM;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS descs;
|
DROP TABLE IF EXISTS descs;
|
||||||
CREATE TABLE descs (
|
CREATE TABLE descs (
|
||||||
@@ -71,7 +71,7 @@ CREATE TABLE descs (
|
|||||||
hash bigint(20) DEFAULT '0' NOT NULL,
|
hash bigint(20) DEFAULT '0' NOT NULL,
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
KEY hash (hash)
|
KEY hash (hash)
|
||||||
);
|
) TYPE=MyISAM;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS dirs;
|
DROP TABLE IF EXISTS dirs;
|
||||||
CREATE TABLE dirs (
|
CREATE TABLE dirs (
|
||||||
@@ -79,7 +79,7 @@ CREATE TABLE dirs (
|
|||||||
dir varchar(255) binary DEFAULT '' NOT NULL,
|
dir varchar(255) binary DEFAULT '' NOT NULL,
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
UNIQUE dir (dir)
|
UNIQUE dir (dir)
|
||||||
);
|
) TYPE=MyISAM;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS files;
|
DROP TABLE IF EXISTS files;
|
||||||
CREATE TABLE files (
|
CREATE TABLE files (
|
||||||
@@ -87,7 +87,7 @@ CREATE TABLE files (
|
|||||||
file varchar(255) binary DEFAULT '' NOT NULL,
|
file varchar(255) binary DEFAULT '' NOT NULL,
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
UNIQUE file (file)
|
UNIQUE file (file)
|
||||||
);
|
) TYPE=MyISAM;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS people;
|
DROP TABLE IF EXISTS people;
|
||||||
CREATE TABLE people (
|
CREATE TABLE people (
|
||||||
@@ -95,7 +95,7 @@ CREATE TABLE people (
|
|||||||
who varchar(32) binary DEFAULT '' NOT NULL,
|
who varchar(32) binary DEFAULT '' NOT NULL,
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
UNIQUE who (who)
|
UNIQUE who (who)
|
||||||
);
|
) TYPE=MyISAM;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS repositories;
|
DROP TABLE IF EXISTS repositories;
|
||||||
CREATE TABLE repositories (
|
CREATE TABLE repositories (
|
||||||
@@ -103,7 +103,7 @@ CREATE TABLE repositories (
|
|||||||
repository varchar(64) binary DEFAULT '' NOT NULL,
|
repository varchar(64) binary DEFAULT '' NOT NULL,
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
UNIQUE repository (repository)
|
UNIQUE repository (repository)
|
||||||
);
|
) TYPE=MyISAM;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS tags;
|
DROP TABLE IF EXISTS tags;
|
||||||
CREATE TABLE tags (
|
CREATE TABLE tags (
|
||||||
@@ -117,7 +117,7 @@ CREATE TABLE tags (
|
|||||||
KEY dirid (dirid),
|
KEY dirid (dirid),
|
||||||
KEY fileid (fileid),
|
KEY fileid (fileid),
|
||||||
KEY branchid (branchid)
|
KEY branchid (branchid)
|
||||||
);
|
) TYPE=MyISAM;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -42,9 +42,23 @@ if LIBRARY_DIR:
|
|||||||
sys.path.insert(0, LIBRARY_DIR)
|
sys.path.insert(0, LIBRARY_DIR)
|
||||||
|
|
||||||
import sapi
|
import sapi
|
||||||
import viewvc
|
import imp
|
||||||
import query
|
|
||||||
reload(query) # need reload because initial import loads this stub file
|
# Import real ViewVC module
|
||||||
|
fp, pathname, description = imp.find_module('viewvc', [LIBRARY_DIR])
|
||||||
|
try:
|
||||||
|
viewvc = imp.load_module('viewvc', fp, pathname, description)
|
||||||
|
finally:
|
||||||
|
if fp:
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
# Import real ViewVC Query modules
|
||||||
|
fp, pathname, description = imp.find_module('query', [LIBRARY_DIR])
|
||||||
|
try:
|
||||||
|
query = imp.load_module('query', fp, pathname, description)
|
||||||
|
finally:
|
||||||
|
if fp:
|
||||||
|
fp.close()
|
||||||
|
|
||||||
cfg = viewvc.load_config(CONF_PATHNAME)
|
cfg = viewvc.load_config(CONF_PATHNAME)
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -42,9 +42,15 @@ if LIBRARY_DIR:
|
|||||||
sys.path.insert(0, LIBRARY_DIR)
|
sys.path.insert(0, LIBRARY_DIR)
|
||||||
|
|
||||||
import sapi
|
import sapi
|
||||||
import viewvc
|
import imp
|
||||||
reload(viewvc) # need reload because initial import loads this stub file
|
|
||||||
|
|
||||||
|
# Import real ViewVC module
|
||||||
|
fp, pathname, description = imp.find_module('viewvc', [LIBRARY_DIR])
|
||||||
|
try:
|
||||||
|
viewvc = imp.load_module('viewvc', fp, pathname, description)
|
||||||
|
finally:
|
||||||
|
if fp:
|
||||||
|
fp.close()
|
||||||
|
|
||||||
def index(req):
|
def index(req):
|
||||||
server = sapi.ModPythonServer(req)
|
server = sapi.ModPythonServer(req)
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
|
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
||||||
# Copyright (C) 2004 James Henstridge
|
# Copyright (C) 2004 James Henstridge
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
|
@@ -1586,10 +1586,20 @@ td {
|
|||||||
<td>Boolean</td>
|
<td>Boolean</td>
|
||||||
<td>True if files list was cut short due to <tt>limit_changes</tt>.</td>
|
<td>True if files list was cut short due to <tt>limit_changes</tt>.</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr class="varlevel2">
|
||||||
|
<td class="varname">commits.minus</td>
|
||||||
|
<td>String</td>
|
||||||
|
<td>Total number of lines removed from files in this commit.</td>
|
||||||
|
</tr>
|
||||||
<tr class="varlevel2">
|
<tr class="varlevel2">
|
||||||
<td class="varname">commits.num_files</td>
|
<td class="varname">commits.num_files</td>
|
||||||
<td>String</td>
|
<td>String</td>
|
||||||
<td>Number of files in the <var>commits.files</var> list.</td>
|
<td>Total number of files in the <var>commits.files</var> list.</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="varlevel2">
|
||||||
|
<td class="varname">commits.plus</td>
|
||||||
|
<td>String</td>
|
||||||
|
<td>Number of lines added to files in this commit.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="varlevel2">
|
<tr class="varlevel2">
|
||||||
<td class="varname">commits.rev</td>
|
<td class="varname">commits.rev</td>
|
||||||
@@ -1630,12 +1640,14 @@ td {
|
|||||||
<tr class="varlevel1">
|
<tr class="varlevel1">
|
||||||
<td class="varname">minus_count</td>
|
<td class="varname">minus_count</td>
|
||||||
<td>String</td>
|
<td>String</td>
|
||||||
<td>Total number of lines removed in the commit (over all files).</td>
|
<td>Total number of lines removed from all files across all returned
|
||||||
|
commits.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="varlevel1">
|
<tr class="varlevel1">
|
||||||
<td class="varname">plus_count</td>
|
<td class="varname">plus_count</td>
|
||||||
<td>String</td>
|
<td>String</td>
|
||||||
<td>Total number of lines added in the commit (over all files).</td>
|
<td>Total number of lines added to all files across all returned
|
||||||
|
commits.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="varlevel1">
|
<tr class="varlevel1">
|
||||||
<td class="varname">queryform_href</td>
|
<td class="varname">queryform_href</td>
|
@@ -89,7 +89,7 @@ th.caption {
|
|||||||
<li><a href="#compat-tarball">'<code>tarball=1</code>' Parameter ⇒ '<code>view=tar</code>'</a></li>
|
<li><a href="#compat-tarball">'<code>tarball=1</code>' Parameter ⇒ '<code>view=tar</code>'</a></li>
|
||||||
<li><a href="#compat-graph">'<code>graph=1</code>' Parameter ⇒ '<code>view=graph</code>'</a></li>
|
<li><a href="#compat-graph">'<code>graph=1</code>' Parameter ⇒ '<code>view=graph</code>'</a></li>
|
||||||
<li><a href="#compat-makeimage">'<code>graph=1&makeimage=1</code>' Parameters ⇒ '<code>view=graphimg</code>'</a></li>
|
<li><a href="#compat-makeimage">'<code>graph=1&makeimage=1</code>' Parameters ⇒ '<code>view=graphimg</code>'</a></li>
|
||||||
<li><a href="#compat-content_type">'<code>content_type=text/vnd.viewcvs-markup</code>' and '<code>content_type=text/x-cvsweb-markup</code>' Parameters⇒ '<code>view=markup</code>'
|
<li><a href="#compat-content_type">'<code>content-type=text/vnd.viewcvs-markup</code>' and '<code>content-type=text/x-cvsweb-markup</code>' Parameters⇒ '<code>view=markup</code>'
|
||||||
<li><a href="#compat-attic">'<code>Attic/FILE</code>' Paths ⇒ '<code>FILE</code>'</a></li>
|
<li><a href="#compat-attic">'<code>Attic/FILE</code>' Paths ⇒ '<code>FILE</code>'</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -284,11 +284,6 @@ th.caption {
|
|||||||
<td>depends</td>
|
<td>depends</td>
|
||||||
<td><a href="#view-param"><code>view</code> parameter</a>, not needed if the <code>default_file_view</code> configuration variable is set to <code>co</code>, since that makes the checkout view the default view for file paths. Also not needed if the <code>/*checkout*</code> magic prefix or the <code>revision</code> parameter is present.
|
<td><a href="#view-param"><code>view</code> parameter</a>, not needed if the <code>default_file_view</code> configuration variable is set to <code>co</code>, since that makes the checkout view the default view for file paths. Also not needed if the <code>/*checkout*</code> magic prefix or the <code>revision</code> parameter is present.
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td><code>content-type=<var>TYPE</var></code></td>
|
|
||||||
<td>optional</td>
|
|
||||||
<td>MIME type to send with checked out file, default is a guess based on file extension</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>revision=<var>REVISION</var></code></td>
|
<td><code>revision=<var>REVISION</var></code></td>
|
||||||
<td>optional</td>
|
<td>optional</td>
|
||||||
@@ -1233,8 +1228,8 @@ th.caption {
|
|||||||
<h3 id="compat-makeimage">'<code>graph=1&makeimage=1</code>' Parameters ⇒ '<code>view=graphimg</code>'</h3>
|
<h3 id="compat-makeimage">'<code>graph=1&makeimage=1</code>' Parameters ⇒ '<code>view=graphimg</code>'</h3>
|
||||||
<p>A <code>graph=1&makeimage=1</code> parameter is treated like a <code>view=graph</code> parameter. There is currently no redirect when it is encountered, but there could be one in the future.</p>
|
<p>A <code>graph=1&makeimage=1</code> parameter is treated like a <code>view=graph</code> parameter. There is currently no redirect when it is encountered, but there could be one in the future.</p>
|
||||||
|
|
||||||
<h3 id="compat-content_type">'<code>content_type=text/vnd.viewcvs-markup</code>' and '<code>content_type=text/x-cvsweb-markup</code>' Parameters⇒ '<code>view=markup</code>'</h3>
|
<h3 id="compat-content_type">'<code>content-type=text/vnd.viewcvs-markup</code>' and '<code>content-type=text/x-cvsweb-markup</code>' Parameters⇒ '<code>view=markup</code>'; other values ignored</h3>
|
||||||
<p><code>content_type=text/vnd.viewcvs-markup</code> and <code>content_type=text/x-cvsweb-markup</code> parameters are treated like a <code>view=markup</code> parameter. There is currently no redirect when it is encountered, but there could be one in the future.</p>
|
<p><code>content-type=text/vnd.viewcvs-markup</code> and <code>content-type=text/x-cvsweb-markup</code> parameters are treated like a <code>view=markup</code> parameter. There is currently no redirect when it is encountered, but there could be one in the future. Other values of the <code>content-type</code> parameter, which were used to dictate the MIME type of files displayed in the checkout/download view prior to ViewVC 1.0.6, are ignored.</p>
|
||||||
|
|
||||||
<h3 id="compat-attic">'<code>Attic/FILE</code>' Paths ⇒ '<code>FILE</code>'</h3>
|
<h3 id="compat-attic">'<code>Attic/FILE</code>' Paths ⇒ '<code>FILE</code>'</h3>
|
||||||
<p>When ViewVC encounters an invalid repository path whose last or second-to-last component is named <code>Attic</code>, and stripping the component yields a valid path, it will redirect to a URL with that path.</p>
|
<p>When ViewVC encounters an invalid repository path whose last or second-to-last component is named <code>Attic</code>, and stripping the component yields a valid path, it will redirect to a URL with that path.</p>
|
@@ -51,16 +51,12 @@ keywordsList = ["and", "assert", "break", "class", "continue", "def",
|
|||||||
"return", "try", "while",
|
"return", "try", "while",
|
||||||
]
|
]
|
||||||
|
|
||||||
# First a little helper, since I don't like to repeat things. (Tismer speaking)
|
|
||||||
def replace(where, what, with):
|
|
||||||
return string.join(string.split(where, what), with)
|
|
||||||
|
|
||||||
# A regexp for matching Python comments.
|
# A regexp for matching Python comments.
|
||||||
commentPat = "#.*"
|
commentPat = "#.*"
|
||||||
|
|
||||||
# A regexp for matching simple quoted strings.
|
# A regexp for matching simple quoted strings.
|
||||||
pat = "q[^q\\n]*(\\[\000-\377][^q\\n]*)*q"
|
pat = "q[^q\\n]*(\\[\000-\377][^q\\n]*)*q"
|
||||||
quotePat = replace(pat, "q", "'") + "|" + replace(pat, 'q', '"')
|
quotePat = string.replace(pat, "q", "'") + "|" + string.replace(pat, 'q', '"')
|
||||||
|
|
||||||
# A regexp for matching multi-line tripled-quoted strings. (Way to go, Tim!)
|
# A regexp for matching multi-line tripled-quoted strings. (Way to go, Tim!)
|
||||||
pat = """
|
pat = """
|
||||||
@@ -82,7 +78,8 @@ pat = """
|
|||||||
qqq
|
qqq
|
||||||
"""
|
"""
|
||||||
pat = string.join(string.split(pat), '') # get rid of whitespace
|
pat = string.join(string.split(pat), '') # get rid of whitespace
|
||||||
tripleQuotePat = replace(pat, "q", "'") + "|" + replace(pat, 'q', '"')
|
tripleQuotePat = string.replace(pat, "q", "'") + "|" \
|
||||||
|
+ string.replace(pat, 'q', '"')
|
||||||
|
|
||||||
# A regexp which matches all and only Python keywords. This will let
|
# A regexp which matches all and only Python keywords. This will let
|
||||||
# us skip the uninteresting identifier references.
|
# us skip the uninteresting identifier references.
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -39,7 +39,7 @@ def _parse(hdr, result):
|
|||||||
while pos < len(hdr):
|
while pos < len(hdr):
|
||||||
name = _re_token.match(hdr, pos)
|
name = _re_token.match(hdr, pos)
|
||||||
if not name:
|
if not name:
|
||||||
raise AcceptParseError()
|
raise AcceptLanguageParseError()
|
||||||
a = result.item_class(string.lower(name.group(1)))
|
a = result.item_class(string.lower(name.group(1)))
|
||||||
pos = name.end()
|
pos = name.end()
|
||||||
while 1:
|
while 1:
|
||||||
@@ -210,7 +210,7 @@ class _LanguageSelector:
|
|||||||
def append(self, item):
|
def append(self, item):
|
||||||
self.requested.append(item)
|
self.requested.append(item)
|
||||||
|
|
||||||
class AcceptParseError(Exception):
|
class AcceptLanguageParseError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _test():
|
def _test():
|
||||||
|
10
lib/blame.py
10
lib/blame.py
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||||
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
|
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
@@ -33,7 +33,6 @@ import time
|
|||||||
import math
|
import math
|
||||||
import cgi
|
import cgi
|
||||||
import vclib
|
import vclib
|
||||||
import vclib.ccvs.blame
|
|
||||||
|
|
||||||
|
|
||||||
re_includes = re.compile('\\#(\\s*)include(\\s*)"(.*?)"')
|
re_includes = re.compile('\\#(\\s*)include(\\s*)"(.*?)"')
|
||||||
@@ -100,6 +99,7 @@ class _item:
|
|||||||
|
|
||||||
|
|
||||||
def make_html(root, rcs_path):
|
def make_html(root, rcs_path):
|
||||||
|
import vclib.ccvs.blame
|
||||||
bs = vclib.ccvs.blame.BlameSource(os.path.join(root, rcs_path))
|
bs = vclib.ccvs.blame.BlameSource(os.path.join(root, rcs_path))
|
||||||
|
|
||||||
count = bs.num_lines
|
count = bs.num_lines
|
||||||
@@ -161,9 +161,9 @@ def make_html(root, rcs_path):
|
|||||||
|
|
||||||
# Close the highlighted section
|
# Close the highlighted section
|
||||||
#if (defined $mark_cmd and mark_cmd != 'begin'):
|
#if (defined $mark_cmd and mark_cmd != 'begin'):
|
||||||
# chop($output)
|
# chop($output)
|
||||||
# output = output + endOfRow + (startOfRow % row_color)
|
# output = output + endOfRow + (startOfRow % row_color)
|
||||||
# inMark = 0
|
# inMark = 0
|
||||||
|
|
||||||
print output
|
print output
|
||||||
print endOfRow + '</table>'
|
print endOfRow + '</table>'
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -19,6 +19,8 @@ import os
|
|||||||
import string
|
import string
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
import fnmatch
|
import fnmatch
|
||||||
|
import re
|
||||||
|
import vclib
|
||||||
|
|
||||||
|
|
||||||
#########################################################################
|
#########################################################################
|
||||||
@@ -39,7 +41,7 @@ import fnmatch
|
|||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
_sections = ('general', 'options', 'cvsdb', 'templates')
|
_sections = ('general', 'options', 'cvsdb', 'templates')
|
||||||
_force_multi_value = ('cvs_roots', 'forbidden',
|
_force_multi_value = ('cvs_roots', 'forbidden', 'forbiddenre',
|
||||||
'svn_roots', 'languages', 'kv_files',
|
'svn_roots', 'languages', 'kv_files',
|
||||||
'root_parents')
|
'root_parents')
|
||||||
|
|
||||||
@@ -151,8 +153,9 @@ class Config:
|
|||||||
self.general.svn_path = ''
|
self.general.svn_path = ''
|
||||||
self.general.mime_types_file = ''
|
self.general.mime_types_file = ''
|
||||||
self.general.address = '<a href="mailto:user@insert.your.domain.here">No admin address has been configured</a>'
|
self.general.address = '<a href="mailto:user@insert.your.domain.here">No admin address has been configured</a>'
|
||||||
self.general.forbidden = ()
|
self.general.forbidden = []
|
||||||
self.general.kv_files = [ ]
|
self.general.forbiddenre = []
|
||||||
|
self.general.kv_files = []
|
||||||
self.general.languages = ['en-us']
|
self.general.languages = ['en-us']
|
||||||
|
|
||||||
self.templates.directory = None
|
self.templates.directory = None
|
||||||
@@ -222,19 +225,65 @@ class Config:
|
|||||||
self.options.http_expiration_time = 600
|
self.options.http_expiration_time = 600
|
||||||
self.options.generate_etags = 1
|
self.options.generate_etags = 1
|
||||||
|
|
||||||
def is_forbidden(self, module):
|
def is_forbidden(self, root, path_parts, pathtype):
|
||||||
if not module:
|
# If we don't have a root and path to check, get outta here.
|
||||||
|
if not (root and path_parts):
|
||||||
return 0
|
return 0
|
||||||
default = 0
|
|
||||||
for pat in self.general.forbidden:
|
|
||||||
if pat[0] == '!':
|
|
||||||
default = 1
|
|
||||||
if fnmatch.fnmatchcase(module, pat[1:]):
|
|
||||||
return 0
|
|
||||||
elif fnmatch.fnmatchcase(module, pat):
|
|
||||||
return 1
|
|
||||||
return default
|
|
||||||
|
|
||||||
|
# Give precedence to the new 'forbiddenre' stuff first.
|
||||||
|
if self.general.forbiddenre:
|
||||||
|
|
||||||
|
# Join the root and path-parts together into one path-like thing.
|
||||||
|
root_and_path = string.join([root] + path_parts, "/")
|
||||||
|
if pathtype == vclib.DIR:
|
||||||
|
root_and_path = root_and_path + '/'
|
||||||
|
|
||||||
|
# If we still have a list of strings, replace those suckers with
|
||||||
|
# lists of (compiled_regex, negation_flag)
|
||||||
|
if type(self.general.forbiddenre[0]) == type(""):
|
||||||
|
for i in range(len(self.general.forbiddenre)):
|
||||||
|
pat = self.general.forbiddenre[i]
|
||||||
|
if pat[0] == '!':
|
||||||
|
self.general.forbiddenre[i] = (re.compile(pat[1:]), 1)
|
||||||
|
else:
|
||||||
|
self.general.forbiddenre[i] = (re.compile(pat), 0)
|
||||||
|
|
||||||
|
# Do the forbiddenness test.
|
||||||
|
default = 0
|
||||||
|
for (pat, negated) in self.general.forbiddenre:
|
||||||
|
match = pat.search(root_and_path)
|
||||||
|
if negated:
|
||||||
|
default = 1
|
||||||
|
if match:
|
||||||
|
return 0
|
||||||
|
elif match:
|
||||||
|
return 1
|
||||||
|
return default
|
||||||
|
|
||||||
|
# If no 'forbiddenre' is in use, we check 'forbidden', which only
|
||||||
|
# looks at the top-most directory.
|
||||||
|
elif self.general.forbidden:
|
||||||
|
|
||||||
|
# A root and a single non-directory path component? That's not
|
||||||
|
# a module.
|
||||||
|
if len(path_parts) == 1 and pathtype != vclib.DIR:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Do the forbiddenness test.
|
||||||
|
module = path_parts[0]
|
||||||
|
default = 0
|
||||||
|
for pat in self.general.forbidden:
|
||||||
|
if pat[0] == '!':
|
||||||
|
default = 1
|
||||||
|
if fnmatch.fnmatchcase(module, pat[1:]):
|
||||||
|
return 0
|
||||||
|
elif fnmatch.fnmatchcase(module, pat):
|
||||||
|
return 1
|
||||||
|
return default
|
||||||
|
|
||||||
|
# No forbiddenness configuration? Just allow it.
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
def _parse_roots(config_name, config_value):
|
def _parse_roots(config_name, config_value):
|
||||||
roots = { }
|
roots = { }
|
||||||
|
58
lib/cvsdb.py
58
lib/cvsdb.py
@@ -1,6 +1,6 @@
|
|||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -23,11 +23,6 @@ import dbi
|
|||||||
## error
|
## error
|
||||||
error = "cvsdb error"
|
error = "cvsdb error"
|
||||||
|
|
||||||
## cached (active) database connections
|
|
||||||
gCheckinDatabase = None
|
|
||||||
gCheckinDatabaseReadOnly = None
|
|
||||||
|
|
||||||
|
|
||||||
## CheckinDatabase provides all interfaces needed to the SQL database
|
## CheckinDatabase provides all interfaces needed to the SQL database
|
||||||
## back-end; it needs to be subclassed, and have its "Connect" method
|
## back-end; it needs to be subclassed, and have its "Connect" method
|
||||||
## defined to actually be complete; it should run well off of any DBI 2.0
|
## defined to actually be complete; it should run well off of any DBI 2.0
|
||||||
@@ -50,6 +45,8 @@ class CheckinDatabase:
|
|||||||
def Connect(self):
|
def Connect(self):
|
||||||
self.db = dbi.connect(
|
self.db = dbi.connect(
|
||||||
self._host, self._port, self._user, self._passwd, self._database)
|
self._host, self._port, self._user, self._passwd, self._database)
|
||||||
|
cursor = self.db.cursor()
|
||||||
|
cursor.execute("SET AUTOCOMMIT=1")
|
||||||
|
|
||||||
def sql_get_id(self, table, column, value, auto_set):
|
def sql_get_id(self, table, column, value, auto_set):
|
||||||
sql = "SELECT id FROM %s WHERE %s=%%s" % (table, column)
|
sql = "SELECT id FROM %s WHERE %s=%%s" % (table, column)
|
||||||
@@ -249,8 +246,8 @@ class CheckinDatabase:
|
|||||||
revision = commit.GetRevision()
|
revision = commit.GetRevision()
|
||||||
sticky_tag = "NULL"
|
sticky_tag = "NULL"
|
||||||
branch_id = self.GetBranchID(commit.GetBranch())
|
branch_id = self.GetBranchID(commit.GetBranch())
|
||||||
plus_count = commit.GetPlusCount()
|
plus_count = commit.GetPlusCount() or '0'
|
||||||
minus_count = commit.GetMinusCount()
|
minus_count = commit.GetMinusCount() or '0'
|
||||||
description_id = self.GetDescriptionID(commit.GetDescription())
|
description_id = self.GetDescriptionID(commit.GetDescription())
|
||||||
|
|
||||||
sql = "REPLACE INTO checkins"\
|
sql = "REPLACE INTO checkins"\
|
||||||
@@ -677,39 +674,20 @@ def CreateCommit():
|
|||||||
def CreateCheckinQuery():
|
def CreateCheckinQuery():
|
||||||
return CheckinDatabaseQuery()
|
return CheckinDatabaseQuery()
|
||||||
|
|
||||||
|
def ConnectDatabase(cfg, readonly=0):
|
||||||
|
if readonly:
|
||||||
|
user = cfg.cvsdb.readonly_user
|
||||||
|
passwd = cfg.cvsdb.readonly_passwd
|
||||||
|
else:
|
||||||
|
user = cfg.cvsdb.user
|
||||||
|
passwd = cfg.cvsdb.passwd
|
||||||
|
db = CheckinDatabase(cfg.cvsdb.host, cfg.cvsdb.port, user, passwd,
|
||||||
|
cfg.cvsdb.database_name, cfg.cvsdb.row_limit)
|
||||||
|
db.Connect()
|
||||||
|
return db
|
||||||
|
|
||||||
def ConnectDatabaseReadOnly(cfg):
|
def ConnectDatabaseReadOnly(cfg):
|
||||||
global gCheckinDatabaseReadOnly
|
return ConnectDatabase(cfg, 1)
|
||||||
|
|
||||||
if gCheckinDatabaseReadOnly:
|
|
||||||
return gCheckinDatabaseReadOnly
|
|
||||||
|
|
||||||
gCheckinDatabaseReadOnly = CheckinDatabase(
|
|
||||||
cfg.cvsdb.host,
|
|
||||||
cfg.cvsdb.port,
|
|
||||||
cfg.cvsdb.readonly_user,
|
|
||||||
cfg.cvsdb.readonly_passwd,
|
|
||||||
cfg.cvsdb.database_name,
|
|
||||||
cfg.cvsdb.row_limit)
|
|
||||||
|
|
||||||
gCheckinDatabaseReadOnly.Connect()
|
|
||||||
return gCheckinDatabaseReadOnly
|
|
||||||
|
|
||||||
def ConnectDatabase(cfg):
|
|
||||||
global gCheckinDatabase
|
|
||||||
|
|
||||||
if gCheckinDatabase:
|
|
||||||
return gCheckinDatabase
|
|
||||||
|
|
||||||
gCheckinDatabase = CheckinDatabase(
|
|
||||||
cfg.cvsdb.host,
|
|
||||||
cfg.cvsdb.port,
|
|
||||||
cfg.cvsdb.user,
|
|
||||||
cfg.cvsdb.passwd,
|
|
||||||
cfg.cvsdb.database_name,
|
|
||||||
cfg.cvsdb.row_limit)
|
|
||||||
|
|
||||||
gCheckinDatabase.Connect()
|
|
||||||
return gCheckinDatabase
|
|
||||||
|
|
||||||
def GetCommitListFromRCSFile(repository, path_parts, revision=None):
|
def GetCommitListFromRCSFile(repository, path_parts, revision=None):
|
||||||
commit_list = []
|
commit_list = []
|
||||||
|
14
lib/debug.py
14
lib/debug.py
@@ -1,6 +1,6 @@
|
|||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -38,13 +38,17 @@ if SHOW_TIMES:
|
|||||||
else:
|
else:
|
||||||
_times[which] = t
|
_times[which] = t
|
||||||
|
|
||||||
def dump():
|
def t_dump(out):
|
||||||
for name, value in _times.items():
|
out.write('<div>')
|
||||||
print '%s: %.6f<br />' % (name, value)
|
names = _times.keys()
|
||||||
|
names.sort()
|
||||||
|
for name in names:
|
||||||
|
out.write('%s: %.6fs<br/>\n' % (name, _times[name]))
|
||||||
|
out.write('</div>')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
t_start = t_end = dump = lambda *args: None
|
t_start = t_end = t_dump = lambda *args: None
|
||||||
|
|
||||||
|
|
||||||
class ViewVCException:
|
class ViewVCException:
|
||||||
|
@@ -200,7 +200,7 @@ Directives
|
|||||||
equivalent to "[CALLBACK QUAL_NAME]"
|
equivalent to "[CALLBACK QUAL_NAME]"
|
||||||
"""
|
"""
|
||||||
#
|
#
|
||||||
# Copyright (C) 2001-2005 Greg Stein. All Rights Reserved.
|
# Copyright (C) 2001-2007 Greg Stein. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Redistribution and use in source and binary forms, with or without
|
||||||
# modification, are permitted provided that the following conditions are
|
# modification, are permitted provided that the following conditions are
|
||||||
@@ -659,7 +659,7 @@ def _write_value(value, args, ctx):
|
|||||||
piece = args[idx]
|
piece = args[idx]
|
||||||
else:
|
else:
|
||||||
piece = '<undef>'
|
piece = '<undef>'
|
||||||
printer(ctx, piece)
|
printer(ctx, piece)
|
||||||
|
|
||||||
# plain old value, write to output
|
# plain old value, write to output
|
||||||
else:
|
else:
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -364,11 +364,11 @@ class _pipe:
|
|||||||
else:
|
else:
|
||||||
if self.thread:
|
if self.thread:
|
||||||
self.thread.join()
|
self.thread.join()
|
||||||
if type(self.child_pid) == type([]):
|
if type(self.child_pid) == type([]):
|
||||||
for pid in self.child_pid:
|
for pid in self.child_pid:
|
||||||
exit = os.waitpid(pid, 0)[1]
|
exit = os.waitpid(pid, 0)[1]
|
||||||
return exit
|
return exit
|
||||||
else:
|
else:
|
||||||
return os.waitpid(self.child_pid, 0)[1]
|
return os.waitpid(self.child_pid, 0)[1]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
23
lib/query.py
23
lib/query.py
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -25,6 +25,7 @@ import time
|
|||||||
|
|
||||||
import cvsdb
|
import cvsdb
|
||||||
import viewvc
|
import viewvc
|
||||||
|
import vclib
|
||||||
import ezt
|
import ezt
|
||||||
import debug
|
import debug
|
||||||
import urllib
|
import urllib
|
||||||
@@ -282,10 +283,8 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
|
|||||||
ob.log = ' '
|
ob.log = ' '
|
||||||
|
|
||||||
for commit in files:
|
for commit in files:
|
||||||
dir_parts = filter(None, string.split(commit.GetDirectory(), '/'))
|
parts = filter(None, string.split(commit.GetDirectory(), '/'))
|
||||||
if dir_parts \
|
if parts and cfg.options.hide_cvsroot and parts[0] == 'CVSROOT':
|
||||||
and ((dir_parts[0] == 'CVSROOT' and cfg.options.hide_cvsroot) \
|
|
||||||
or cfg.is_forbidden(dir_parts[0])):
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
ctime = commit.GetTime()
|
ctime = commit.GetTime()
|
||||||
@@ -304,6 +303,11 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
|
|||||||
file = (directory and directory + "/") + commit.GetFile()
|
file = (directory and directory + "/") + commit.GetFile()
|
||||||
cvsroot_name = cvsroots.get(repository)
|
cvsroot_name = cvsroots.get(repository)
|
||||||
|
|
||||||
|
## skip forbidden files
|
||||||
|
if cfg.is_forbidden(cvsroot_name,
|
||||||
|
filter(None, string.split(file, "/")), vclib.FILE):
|
||||||
|
continue
|
||||||
|
|
||||||
## if we couldn't find the cvsroot path configured in the
|
## if we couldn't find the cvsroot path configured in the
|
||||||
## viewvc.conf file, then don't make the link
|
## viewvc.conf file, then don't make the link
|
||||||
if cvsroot_name:
|
if cvsroot_name:
|
||||||
@@ -345,7 +349,7 @@ def run_query(server, cfg, form_data, viewvc_link):
|
|||||||
files = [ ]
|
files = [ ]
|
||||||
|
|
||||||
cvsroots = {}
|
cvsroots = {}
|
||||||
rootitems = cfg.general.cvs_roots.items() + cfg.general.svn_roots.items()
|
rootitems = cfg.general.svn_roots.items() + cfg.general.cvs_roots.items()
|
||||||
for key, value in rootitems:
|
for key, value in rootitems:
|
||||||
cvsroots[cvsdb.CleanRepository(value)] = key
|
cvsroots[cvsdb.CleanRepository(value)] = key
|
||||||
|
|
||||||
@@ -366,6 +370,13 @@ def run_query(server, cfg, form_data, viewvc_link):
|
|||||||
commits.append(build_commit(server, cfg, current_desc, files,
|
commits.append(build_commit(server, cfg, current_desc, files,
|
||||||
cvsroots, viewvc_link))
|
cvsroots, viewvc_link))
|
||||||
|
|
||||||
|
# Strip out commits that don't have any files attached to them. The
|
||||||
|
# files probably aren't present because they've been blocked via
|
||||||
|
# forbiddenness.
|
||||||
|
def _only_with_files(commit):
|
||||||
|
return len(commit.files) > 0
|
||||||
|
commits = filter(_only_with_files, commits)
|
||||||
|
|
||||||
return commits
|
return commits
|
||||||
|
|
||||||
def main(server, cfg, viewvc_link):
|
def main(server, cfg, viewvc_link):
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -534,31 +534,25 @@ def _parse_co_header(fp):
|
|||||||
raise COMalformedOutput, "Unable to find filename in co output stream"
|
raise COMalformedOutput, "Unable to find filename in co output stream"
|
||||||
filename = match.group(1)
|
filename = match.group(1)
|
||||||
|
|
||||||
# look for a revision in the second line.
|
# look through subsequent lines for a revision. we might encounter
|
||||||
line = fp.readline()
|
# some ignorable or problematic lines along the way.
|
||||||
if not line:
|
while 1:
|
||||||
raise COMalformedOutput, "Missing second line from co output stream"
|
line = fp.readline()
|
||||||
match = _re_co_revision.match(line)
|
if not line:
|
||||||
if match:
|
break
|
||||||
return filename, match.group(1)
|
# look for a revision.
|
||||||
elif _re_co_missing_rev.match(line) or _re_co_side_branches.match(line):
|
match = _re_co_revision.match(line)
|
||||||
raise COMissingRevision, "Got missing revision error from co output stream"
|
if match:
|
||||||
elif _re_co_warning.match(line):
|
return filename, match.group(1)
|
||||||
pass
|
elif _re_co_missing_rev.match(line) or _re_co_side_branches.match(line):
|
||||||
else:
|
raise COMissingRevision, "Got missing revision error from co output stream"
|
||||||
raise COMalformedOutput, "Unable to find revision in co output stream"
|
elif _re_co_warning.match(line):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
# if we get here, the second line wasn't a revision, but it was a
|
|
||||||
# warning we can ignore. look for a revision in the third line.
|
|
||||||
line = fp.readline()
|
|
||||||
if not line:
|
|
||||||
raise COMalformedOutput, "Missing third line from co output stream"
|
|
||||||
match = _re_co_revision.match(line)
|
|
||||||
if match:
|
|
||||||
return filename, match.group(1)
|
|
||||||
raise COMalformedOutput, "Unable to find revision in co output stream"
|
raise COMalformedOutput, "Unable to find revision in co output stream"
|
||||||
|
|
||||||
|
|
||||||
# if your rlog doesn't use 77 '=' characters, then this must change
|
# if your rlog doesn't use 77 '=' characters, then this must change
|
||||||
LOG_END_MARKER = '=' * 77 + '\n'
|
LOG_END_MARKER = '=' * 77 + '\n'
|
||||||
ENTRY_END_MARKER = '-' * 28 + '\n'
|
ENTRY_END_MARKER = '-' * 28 + '\n'
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||||
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
|
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
@@ -346,7 +346,7 @@ class CVSParser(rcsparse.Sink):
|
|||||||
is_trunk_revision = self.trunk_rev.match(revision) is not None
|
is_trunk_revision = self.trunk_rev.match(revision) is not None
|
||||||
|
|
||||||
if is_trunk_revision:
|
if is_trunk_revision:
|
||||||
diffs = self.deltatext_split(last_revision)
|
diffs = self.deltatext_split(last_revision)
|
||||||
|
|
||||||
# Revisions on the trunk specify deltas that transform a
|
# Revisions on the trunk specify deltas that transform a
|
||||||
# revision into an earlier revision, so invert the translation
|
# revision into an earlier revision, so invert the translation
|
||||||
@@ -379,7 +379,7 @@ class CVSParser(rcsparse.Sink):
|
|||||||
# the trunk. They specify deltas that transform a revision
|
# the trunk. They specify deltas that transform a revision
|
||||||
# into a later revision.
|
# into a later revision.
|
||||||
adjust = 0
|
adjust = 0
|
||||||
diffs = self.deltatext_split(revision)
|
diffs = self.deltatext_split(revision)
|
||||||
for command in diffs:
|
for command in diffs:
|
||||||
if skip > 0:
|
if skip > 0:
|
||||||
skip = skip - 1
|
skip = skip - 1
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -78,7 +78,7 @@ class RCSStopParser(Exception):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class _Parser:
|
class _Parser:
|
||||||
stream_class = None # subclasses need to define this
|
stream_class = None # subclasses need to define this
|
||||||
|
|
||||||
def parse_rcs_admin(self):
|
def parse_rcs_admin(self):
|
||||||
while 1:
|
while 1:
|
||||||
@@ -196,7 +196,7 @@ class _Parser:
|
|||||||
if token == ';':
|
if token == ';':
|
||||||
break
|
break
|
||||||
author = author + token + ' '
|
author = author + token + ' '
|
||||||
author = author[:-1] # toss the trailing space
|
author = author[:-1] # toss the trailing space
|
||||||
|
|
||||||
# Parse state
|
# Parse state
|
||||||
self.ts.match('state')
|
self.ts.match('state')
|
||||||
@@ -206,7 +206,7 @@ class _Parser:
|
|||||||
if token == ';':
|
if token == ';':
|
||||||
break
|
break
|
||||||
state = state + token + ' '
|
state = state + token + ' '
|
||||||
state = state[:-1] # toss the trailing space
|
state = state[:-1] # toss the trailing space
|
||||||
|
|
||||||
# Parse branches
|
# Parse branches
|
||||||
self.ts.match('branches')
|
self.ts.match('branches')
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -26,7 +26,7 @@ class _TokenStream:
|
|||||||
# note: we use a multiple of a standard block size
|
# note: we use a multiple of a standard block size
|
||||||
CHUNK_SIZE = 192 * 512 # about 100k
|
CHUNK_SIZE = 192 * 512 # about 100k
|
||||||
|
|
||||||
# CHUNK_SIZE = 5 # for debugging, make the function grind...
|
# CHUNK_SIZE = 5 # for debugging, make the function grind...
|
||||||
|
|
||||||
def __init__(self, file):
|
def __init__(self, file):
|
||||||
self.rcsfile = file
|
self.rcsfile = file
|
||||||
@@ -51,7 +51,7 @@ class _TokenStream:
|
|||||||
buf = self.rcsfile.read(self.CHUNK_SIZE)
|
buf = self.rcsfile.read(self.CHUNK_SIZE)
|
||||||
if buf == '':
|
if buf == '':
|
||||||
# signal EOF by returning None as the token
|
# signal EOF by returning None as the token
|
||||||
del self.buf # so we fail if get() is called again
|
del self.buf # so we fail if get() is called again
|
||||||
return None
|
return None
|
||||||
idx = 0
|
idx = 0
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -25,7 +25,7 @@ _tt = TextTools
|
|||||||
_idchar_list = map(chr, range(33, 127)) + map(chr, range(160, 256))
|
_idchar_list = map(chr, range(33, 127)) + map(chr, range(160, 256))
|
||||||
_idchar_list.remove('$')
|
_idchar_list.remove('$')
|
||||||
_idchar_list.remove(',')
|
_idchar_list.remove(',')
|
||||||
#_idchar_list.remove('.') leave as part of 'num' symbol
|
#_idchar_list.remove('.') # leave as part of 'num' symbol
|
||||||
_idchar_list.remove(':')
|
_idchar_list.remove(':')
|
||||||
_idchar_list.remove(';')
|
_idchar_list.remove(';')
|
||||||
_idchar_list.remove('@')
|
_idchar_list.remove('@')
|
||||||
@@ -41,10 +41,10 @@ _T_STRING_START = 40
|
|||||||
_T_STRING_SPAN = 60
|
_T_STRING_SPAN = 60
|
||||||
_T_STRING_END = 70
|
_T_STRING_END = 70
|
||||||
|
|
||||||
_E_COMPLETE = 100 # ended on a complete token
|
_E_COMPLETE = 100 # ended on a complete token
|
||||||
_E_TOKEN = 110 # ended mid-token
|
_E_TOKEN = 110 # ended mid-token
|
||||||
_E_STRING_SPAN = 130 # ended within a string
|
_E_STRING_SPAN = 130 # ended within a string
|
||||||
_E_STRING_END = 140 # ended with string-end ('@') (could be mid-@@)
|
_E_STRING_END = 140 # ended with string-end ('@') (could be mid-@@)
|
||||||
|
|
||||||
_SUCCESS = +100
|
_SUCCESS = +100
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ class _mxTokenStream:
|
|||||||
# note: we use a multiple of a standard block size
|
# note: we use a multiple of a standard block size
|
||||||
CHUNK_SIZE = 192 * 512 # about 100k
|
CHUNK_SIZE = 192 * 512 # about 100k
|
||||||
|
|
||||||
# CHUNK_SIZE = 5 # for debugging, make the function grind...
|
# CHUNK_SIZE = 5 # for debugging, make the function grind...
|
||||||
|
|
||||||
def __init__(self, file):
|
def __init__(self, file):
|
||||||
self.rcsfile = file
|
self.rcsfile = file
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -15,9 +15,11 @@
|
|||||||
import vclib
|
import vclib
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
|
import stat
|
||||||
import string
|
import string
|
||||||
import cStringIO
|
import cStringIO
|
||||||
import signal
|
import signal
|
||||||
|
import shutil
|
||||||
import time
|
import time
|
||||||
import tempfile
|
import tempfile
|
||||||
import popen
|
import popen
|
||||||
@@ -237,6 +239,9 @@ class NodeHistory:
|
|||||||
|
|
||||||
|
|
||||||
def _get_history(svnrepos, full_name, rev, options={}):
|
def _get_history(svnrepos, full_name, rev, options={}):
|
||||||
|
if svnrepos.youngest == 0:
|
||||||
|
return {}
|
||||||
|
|
||||||
fsroot = svnrepos._getroot(rev)
|
fsroot = svnrepos._getroot(rev)
|
||||||
show_all_logs = options.get('svn_show_all_dir_logs', 0)
|
show_all_logs = options.get('svn_show_all_dir_logs', 0)
|
||||||
if not show_all_logs:
|
if not show_all_logs:
|
||||||
@@ -499,13 +504,36 @@ class BlameSource:
|
|||||||
if os.sep != '/':
|
if os.sep != '/':
|
||||||
rootpath = string.replace(rootpath, os.sep, '/')
|
rootpath = string.replace(rootpath, os.sep, '/')
|
||||||
|
|
||||||
|
# Make a read-only temporary directory for Subversion to use as
|
||||||
|
# its runtime config dir. (Read-only because that will prevent
|
||||||
|
# Subversion from fleshing out all the default runtime config
|
||||||
|
# contents.)
|
||||||
|
self.config_dir = self._mkdtemp()
|
||||||
|
os.chmod(self.config_dir, stat.S_IRUSR | stat.S_IXUSR)
|
||||||
|
|
||||||
url = 'file://' + string.join([rootpath, fs_path], "/")
|
url = 'file://' + string.join([rootpath, fs_path], "/")
|
||||||
fp = popen.popen(svn_client_path,
|
fp = popen.popen(svn_client_path,
|
||||||
('blame', "-r%d" % int(rev), "--non-interactive",
|
("blame",
|
||||||
|
"-r%d" % int(rev),
|
||||||
|
"--non-interactive",
|
||||||
|
"--config-dir", self.config_dir,
|
||||||
"%s@%d" % (url, int(rev))),
|
"%s@%d" % (url, int(rev))),
|
||||||
'rb', 1)
|
'rb', 1)
|
||||||
self.fp = fp
|
self.fp = fp
|
||||||
|
|
||||||
|
def _mkdtemp(self):
|
||||||
|
### FIXME: When we require Python 2.3, this can go away.
|
||||||
|
for i in range(10):
|
||||||
|
dir = tempfile.mktemp()
|
||||||
|
try:
|
||||||
|
os.mkdir(dir, 0700)
|
||||||
|
return dir
|
||||||
|
except OSError, e:
|
||||||
|
if e.errno == errno.EEXIST:
|
||||||
|
continue # try again
|
||||||
|
raise
|
||||||
|
raise IOError, (errno.EEXIST, "No usable temporary directory name found")
|
||||||
|
|
||||||
def __getitem__(self, idx):
|
def __getitem__(self, idx):
|
||||||
if idx == self.idx:
|
if idx == self.idx:
|
||||||
return self.last
|
return self.last
|
||||||
@@ -530,6 +558,13 @@ class BlameSource:
|
|||||||
self.idx = idx
|
self.idx = idx
|
||||||
return item
|
return item
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
try:
|
||||||
|
if self.config_dir:
|
||||||
|
shutil.rmtree(self.config_dir)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BlameSequencingError(Exception):
|
class BlameSequencingError(Exception):
|
||||||
pass
|
pass
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -100,8 +100,12 @@ def created_rev(svnrepos, full_name, rev):
|
|||||||
kind = ra.svn_ra_check_path(svnrepos.ra_session, full_name, rev,
|
kind = ra.svn_ra_check_path(svnrepos.ra_session, full_name, rev,
|
||||||
svnrepos.pool)
|
svnrepos.pool)
|
||||||
if kind == core.svn_node_dir:
|
if kind == core.svn_node_dir:
|
||||||
props = ra.svn_ra_get_dir(svnrepos.ra_session, full_name,
|
retval = ra.svn_ra_get_dir(svnrepos.ra_session, full_name,
|
||||||
rev, svnrepos.pool)
|
rev, svnrepos.pool)
|
||||||
|
if type(retval) == type([]) and len(retval) == 3:
|
||||||
|
props = retval[2]
|
||||||
|
else: # compat with older (broken) bindings
|
||||||
|
props = retval
|
||||||
return int(props[core.SVN_PROP_ENTRY_COMMITTED_REV])
|
return int(props[core.SVN_PROP_ENTRY_COMMITTED_REV])
|
||||||
return core.SVN_INVALID_REVNUM
|
return core.SVN_INVALID_REVNUM
|
||||||
|
|
||||||
@@ -176,12 +180,14 @@ class LogCollector:
|
|||||||
# Changed paths have leading slashes
|
# Changed paths have leading slashes
|
||||||
changed_paths = paths.keys()
|
changed_paths = paths.keys()
|
||||||
changed_paths.sort(lambda a, b: _compare_paths(a, b))
|
changed_paths.sort(lambda a, b: _compare_paths(a, b))
|
||||||
this_path = None
|
copyfrom_path = copyfrom_rev = this_path = None
|
||||||
if self.path in changed_paths:
|
if self.path in changed_paths:
|
||||||
this_path = self.path
|
this_path = self.path
|
||||||
change = paths[self.path]
|
change = paths[self.path]
|
||||||
if change.copyfrom_path:
|
if change.copyfrom_path:
|
||||||
this_path = change.copyfrom_path
|
this_path = change.copyfrom_path
|
||||||
|
copyfrom_path = change.copyfrom_path[1:]
|
||||||
|
copyfrom_rev = change.copyfrom_rev
|
||||||
for changed_path in changed_paths:
|
for changed_path in changed_paths:
|
||||||
if changed_path != self.path:
|
if changed_path != self.path:
|
||||||
# If a parent of our path was copied, our "next previous"
|
# If a parent of our path was copied, our "next previous"
|
||||||
@@ -194,7 +200,7 @@ class LogCollector:
|
|||||||
if self.show_all_logs or this_path:
|
if self.show_all_logs or this_path:
|
||||||
date = _datestr_to_date(date, pool)
|
date = _datestr_to_date(date, pool)
|
||||||
entry = Revision(revision, date, author, message, None,
|
entry = Revision(revision, date, author, message, None,
|
||||||
self.path[1:], None, None)
|
self.path[1:], copyfrom_path, copyfrom_rev)
|
||||||
self.logs.append(entry)
|
self.logs.append(entry)
|
||||||
if this_path:
|
if this_path:
|
||||||
self.path = this_path
|
self.path = this_path
|
||||||
@@ -214,7 +220,7 @@ def get_logs(svnrepos, full_name, rev, files):
|
|||||||
rev, author, date, log, changes = \
|
rev, author, date, log, changes = \
|
||||||
_get_rev_details(svnrepos, entry.created_rev, subpool)
|
_get_rev_details(svnrepos, entry.created_rev, subpool)
|
||||||
rev_info_cache[entry.created_rev] = rev, author, date, log
|
rev_info_cache[entry.created_rev] = rev, author, date, log
|
||||||
file.rev = rev
|
file.rev = str(rev)
|
||||||
file.author = author
|
file.author = author
|
||||||
file.date = _datestr_to_date(date, subpool)
|
file.date = _datestr_to_date(date, subpool)
|
||||||
file.log = log
|
file.log = log
|
||||||
@@ -240,7 +246,7 @@ class SelfCleanFP:
|
|||||||
self._path = path
|
self._path = path
|
||||||
self._eof = 0
|
self._eof = 0
|
||||||
|
|
||||||
def read(self, len):
|
def read(self, len=None):
|
||||||
if len:
|
if len:
|
||||||
chunk = self._fp.read(len)
|
chunk = self._fp.read(len)
|
||||||
else:
|
else:
|
||||||
|
600
lib/viewvc.py
600
lib/viewvc.py
@@ -1,6 +1,6 @@
|
|||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
#
|
#
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
|
|
||||||
__version__ = '1.0.3'
|
__version__ = '1.0.8'
|
||||||
|
|
||||||
# this comes from our library; measure the startup time
|
# this comes from our library; measure the startup time
|
||||||
import debug
|
import debug
|
||||||
@@ -112,7 +112,10 @@ class Request:
|
|||||||
|
|
||||||
# process the Accept-Language: header
|
# process the Accept-Language: header
|
||||||
hal = server.getenv('HTTP_ACCEPT_LANGUAGE','')
|
hal = server.getenv('HTTP_ACCEPT_LANGUAGE','')
|
||||||
self.lang_selector = accept.language(hal)
|
try:
|
||||||
|
self.lang_selector = accept.language(hal)
|
||||||
|
except accept.AcceptLanguageParseError:
|
||||||
|
self.lang_selector = accept.language('en')
|
||||||
self.language = self.lang_selector.select_from(cfg.general.languages)
|
self.language = self.lang_selector.select_from(cfg.general.languages)
|
||||||
|
|
||||||
# load the key/value files, given the selected language
|
# load the key/value files, given the selected language
|
||||||
@@ -160,6 +163,15 @@ class Request:
|
|||||||
# validate the parameter
|
# validate the parameter
|
||||||
_validate_param(name, values[0])
|
_validate_param(name, values[0])
|
||||||
|
|
||||||
|
# Only allow the magic ViewVC MIME types (the ones used for
|
||||||
|
# requesting the markup as as-text views) to be declared via CGI
|
||||||
|
# params. Ignore disallowed values.
|
||||||
|
if (name == 'content-type') and \
|
||||||
|
(not values[0] in (viewcvs_mime_type,
|
||||||
|
alt_mime_type,
|
||||||
|
'text/plain')):
|
||||||
|
continue
|
||||||
|
|
||||||
# if we're here, then the parameter is okay
|
# if we're here, then the parameter is okay
|
||||||
self.query_dict[name] = values[0]
|
self.query_dict[name] = values[0]
|
||||||
|
|
||||||
@@ -191,7 +203,7 @@ class Request:
|
|||||||
# handle tarball magic suffixes
|
# handle tarball magic suffixes
|
||||||
if self.view_func is download_tarball:
|
if self.view_func is download_tarball:
|
||||||
if (self.query_dict.get('parent')):
|
if (self.query_dict.get('parent')):
|
||||||
del path_parts[-1]
|
del path_parts[-1]
|
||||||
elif path_parts[-1][-7:] == ".tar.gz":
|
elif path_parts[-1][-7:] == ".tar.gz":
|
||||||
path_parts[-1] = path_parts[-1][:-7]
|
path_parts[-1] = path_parts[-1][:-7]
|
||||||
|
|
||||||
@@ -283,13 +295,19 @@ class Request:
|
|||||||
needs_redirect = 1
|
needs_redirect = 1
|
||||||
|
|
||||||
if self.repos and self.view_func is not redirect_pathrev:
|
if self.repos and self.view_func is not redirect_pathrev:
|
||||||
|
# If this is an intended-to-be-hidden CVSROOT path, complain.
|
||||||
|
if cfg.options.hide_cvsroot \
|
||||||
|
and is_cvsroot_path(self.roottype, path_parts):
|
||||||
|
raise debug.ViewVCException('%s: unknown location'
|
||||||
|
% self.where, '404 Not Found')
|
||||||
|
|
||||||
# Make sure path exists
|
# Make sure path exists
|
||||||
self.pathrev = pathrev = self.query_dict.get('pathrev')
|
self.pathrev = pathrev = self.query_dict.get('pathrev')
|
||||||
self.pathtype = _repos_pathtype(self.repos, path_parts, pathrev)
|
self.pathtype = _repos_pathtype(self.repos, path_parts, pathrev)
|
||||||
|
|
||||||
if self.pathtype is None:
|
if self.pathtype is None:
|
||||||
# path doesn't exist, see if it could be an old-style ViewVC URL
|
# Path doesn't exist, see if it could be an old-style ViewVC URL
|
||||||
# with a fake suffix
|
# with a fake suffix.
|
||||||
result = _strip_suffix('.diff', path_parts, pathrev, vclib.FILE, \
|
result = _strip_suffix('.diff', path_parts, pathrev, vclib.FILE, \
|
||||||
self.repos, view_diff) or \
|
self.repos, view_diff) or \
|
||||||
_strip_suffix('.tar.gz', path_parts, pathrev, vclib.DIR, \
|
_strip_suffix('.tar.gz', path_parts, pathrev, vclib.DIR, \
|
||||||
@@ -308,7 +326,7 @@ class Request:
|
|||||||
if result:
|
if result:
|
||||||
self.path_parts, self.pathtype, self.view_func = result
|
self.path_parts, self.pathtype, self.view_func = result
|
||||||
self.where = _path_join(self.path_parts)
|
self.where = _path_join(self.path_parts)
|
||||||
needs_redirect = 1
|
needs_redirect = 1
|
||||||
else:
|
else:
|
||||||
raise debug.ViewVCException('%s: unknown location'
|
raise debug.ViewVCException('%s: unknown location'
|
||||||
% self.where, '404 Not Found')
|
% self.where, '404 Not Found')
|
||||||
@@ -327,11 +345,11 @@ class Request:
|
|||||||
self.where = _path_join(attic_parts)
|
self.where = _path_join(attic_parts)
|
||||||
needs_redirect = 1
|
needs_redirect = 1
|
||||||
|
|
||||||
# If this is a forbidden directory, stop now
|
# If this is a forbidden location, stop now
|
||||||
if self.path_parts and self.pathtype == vclib.DIR \
|
if cfg.is_forbidden(self.rootname, self.path_parts, self.pathtype):
|
||||||
and cfg.is_forbidden(self.path_parts[0]):
|
raise debug.ViewVCException('%s: unknown location' \
|
||||||
raise debug.ViewVCException('%s: unknown location' % path_parts[0],
|
% _path_join(self.path_parts),
|
||||||
'404 Not Found')
|
'404 Not Found')
|
||||||
|
|
||||||
if self.view_func is None:
|
if self.view_func is None:
|
||||||
# view parameter is not set, try looking at pathtype and the
|
# view parameter is not set, try looking at pathtype and the
|
||||||
@@ -340,7 +358,7 @@ class Request:
|
|||||||
self.view_func = view_roots
|
self.view_func = view_roots
|
||||||
elif self.pathtype == vclib.DIR:
|
elif self.pathtype == vclib.DIR:
|
||||||
# ViewCVS 0.9.2 used to put ?tarball=1 at the end of tarball urls
|
# ViewCVS 0.9.2 used to put ?tarball=1 at the end of tarball urls
|
||||||
if self.query_dict.has_key('tarball'):
|
if self.query_dict.has_key('tarball'):
|
||||||
self.view_func = download_tarball
|
self.view_func = download_tarball
|
||||||
else:
|
else:
|
||||||
self.view_func = view_directory
|
self.view_func = view_directory
|
||||||
@@ -499,7 +517,7 @@ class Request:
|
|||||||
if view_func is download_tarball:
|
if view_func is download_tarball:
|
||||||
if not where and not cfg.options.root_as_url_component:
|
if not where and not cfg.options.root_as_url_component:
|
||||||
url = url + '/' + rootname + '-root'
|
url = url + '/' + rootname + '-root'
|
||||||
params['parent'] = '1'
|
params['parent'] = '1'
|
||||||
url = url + '.tar.gz'
|
url = url + '.tar.gz'
|
||||||
|
|
||||||
# add trailing slash for a directory
|
# add trailing slash for a directory
|
||||||
@@ -748,8 +766,11 @@ def _orig_path(request, rev_param='revision', path_param=None):
|
|||||||
path = request.query_dict.get(path_param, request.where)
|
path = request.query_dict.get(path_param, request.where)
|
||||||
|
|
||||||
if rev is not None and hasattr(request.repos, '_getrev'):
|
if rev is not None and hasattr(request.repos, '_getrev'):
|
||||||
pathrev = request.repos._getrev(request.pathrev)
|
try:
|
||||||
rev = request.repos._getrev(rev)
|
pathrev = request.repos._getrev(request.pathrev)
|
||||||
|
rev = request.repos._getrev(rev)
|
||||||
|
except vclib.InvalidRevision:
|
||||||
|
raise debug.ViewVCException('Invalid revision', '404 Not Found')
|
||||||
return _path_parts(vclib.svn.get_location(request.repos, path,
|
return _path_parts(vclib.svn.get_location(request.repos, path,
|
||||||
pathrev, rev)), rev
|
pathrev, rev)), rev
|
||||||
return _path_parts(path), rev
|
return _path_parts(path), rev
|
||||||
@@ -903,6 +924,9 @@ def is_viewable_image(mime_type):
|
|||||||
def is_text(mime_type):
|
def is_text(mime_type):
|
||||||
return not mime_type or mime_type[:5] == 'text/'
|
return not mime_type or mime_type[:5] == 'text/'
|
||||||
|
|
||||||
|
def is_cvsroot_path(roottype, path_parts):
|
||||||
|
return roottype == 'cvs' and path_parts and path_parts[0] == 'CVSROOT'
|
||||||
|
|
||||||
def is_plain_text(mime_type):
|
def is_plain_text(mime_type):
|
||||||
return not mime_type or mime_type == 'text/plain'
|
return not mime_type or mime_type == 'text/plain'
|
||||||
|
|
||||||
@@ -976,8 +1000,13 @@ def htmlify(html):
|
|||||||
html = re.sub(_re_rewrite_email, r'<a href="mailto:\1@\2">\1@\2</a>', html)
|
html = re.sub(_re_rewrite_email, r'<a href="mailto:\1@\2">\1@\2</a>', html)
|
||||||
return html
|
return html
|
||||||
|
|
||||||
def format_log(log, cfg):
|
def format_log(log, cfg, htmlize=1):
|
||||||
s = htmlify(log[:cfg.options.short_log_len])
|
if not log:
|
||||||
|
return log
|
||||||
|
if htmlize:
|
||||||
|
s = htmlify(log[:cfg.options.short_log_len])
|
||||||
|
else:
|
||||||
|
s = cgi.escape(log[:cfg.options.short_log_len])
|
||||||
if len(log) > cfg.options.short_log_len:
|
if len(log) > cfg.options.short_log_len:
|
||||||
s = s + '...'
|
s = s + '...'
|
||||||
return s
|
return s
|
||||||
@@ -1356,6 +1385,10 @@ def make_rss_time_string(date, cfg):
|
|||||||
return time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime(date)) + ' UTC'
|
return time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime(date)) + ' UTC'
|
||||||
|
|
||||||
def view_markup(request):
|
def view_markup(request):
|
||||||
|
if not request.cfg.options.allow_markup:
|
||||||
|
raise debug.ViewVCException('Markup view is disabled',
|
||||||
|
'403 Forbidden')
|
||||||
|
|
||||||
cfg = request.cfg
|
cfg = request.cfg
|
||||||
path, rev = _orig_path(request)
|
path, rev = _orig_path(request)
|
||||||
fp, revision = request.repos.openfile(path, rev)
|
fp, revision = request.repos.openfile(path, rev)
|
||||||
@@ -1514,7 +1547,10 @@ def view_directory(request):
|
|||||||
# the directory listing (to take into account template changes or
|
# the directory listing (to take into account template changes or
|
||||||
# revision property changes).
|
# revision property changes).
|
||||||
if request.roottype == 'svn':
|
if request.roottype == 'svn':
|
||||||
rev = request.repos._getrev(request.pathrev)
|
try:
|
||||||
|
rev = request.repos._getrev(request.pathrev)
|
||||||
|
except vclib.InvalidRevision:
|
||||||
|
raise debug.ViewVCException('Invalid revision', '404 Not Found')
|
||||||
tree_rev = vclib.svn.created_rev(request.repos, request.where, rev)
|
tree_rev = vclib.svn.created_rev(request.repos, request.where, rev)
|
||||||
if check_freshness(request, None, str(tree_rev), weak=1):
|
if check_freshness(request, None, str(tree_rev), weak=1):
|
||||||
return
|
return
|
||||||
@@ -1527,16 +1563,9 @@ def view_directory(request):
|
|||||||
cfg.options.hide_attic))
|
cfg.options.hide_attic))
|
||||||
options["cvs_subdirs"] = (cfg.options.show_subdir_lastmod and
|
options["cvs_subdirs"] = (cfg.options.show_subdir_lastmod and
|
||||||
cfg.options.show_logs)
|
cfg.options.show_logs)
|
||||||
|
|
||||||
file_data = request.repos.listdir(request.path_parts, request.pathrev,
|
file_data = request.repos.listdir(request.path_parts, request.pathrev,
|
||||||
options)
|
options)
|
||||||
|
|
||||||
# Filter file list if a regex is specified
|
|
||||||
search_re = request.query_dict.get('search', '')
|
|
||||||
if cfg.options.use_re_search and search_re:
|
|
||||||
file_data = search_files(request.repos, request.path_parts, request.pathrev,
|
|
||||||
file_data, search_re)
|
|
||||||
|
|
||||||
# Retrieve log messages, authors, revision numbers, timestamps
|
# Retrieve log messages, authors, revision numbers, timestamps
|
||||||
request.repos.dirlogs(request.path_parts, request.pathrev, file_data, options)
|
request.repos.dirlogs(request.path_parts, request.pathrev, file_data, options)
|
||||||
|
|
||||||
@@ -1546,6 +1575,12 @@ def view_directory(request):
|
|||||||
sort_file_data(file_data, request.roottype, sortdir, sortby,
|
sort_file_data(file_data, request.roottype, sortdir, sortby,
|
||||||
cfg.options.sort_group_dirs)
|
cfg.options.sort_group_dirs)
|
||||||
|
|
||||||
|
# If a regex is specified, build a compiled form thereof for filtering
|
||||||
|
searchstr = None
|
||||||
|
search_re = request.query_dict.get('search', '')
|
||||||
|
if cfg.options.use_re_search and search_re:
|
||||||
|
searchstr = re.compile(search_re)
|
||||||
|
|
||||||
# loop through entries creating rows and changing these values
|
# loop through entries creating rows and changing these values
|
||||||
rows = [ ]
|
rows = [ ]
|
||||||
num_displayed = 0
|
num_displayed = 0
|
||||||
@@ -1577,13 +1612,14 @@ def view_directory(request):
|
|||||||
(file.kind == vclib.DIR and 'dir')
|
(file.kind == vclib.DIR and 'dir')
|
||||||
row.errors = file.errors
|
row.errors = file.errors
|
||||||
|
|
||||||
|
if cfg.is_forbidden(request.rootname, request.path_parts + [file.name],
|
||||||
|
file.kind):
|
||||||
|
continue
|
||||||
|
|
||||||
if file.kind == vclib.DIR:
|
if file.kind == vclib.DIR:
|
||||||
|
if cfg.options.hide_cvsroot \
|
||||||
if (where == '') and (cfg.is_forbidden(file.name)):
|
and is_cvsroot_path(request.roottype,
|
||||||
continue
|
request.path_parts + [file.name]):
|
||||||
|
|
||||||
if (request.roottype == 'cvs' and cfg.options.hide_cvsroot
|
|
||||||
and where == '' and file.name == 'CVSROOT'):
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
row.view_href = request.get_url(view_func=view_directory,
|
row.view_href = request.get_url(view_func=view_directory,
|
||||||
@@ -1611,10 +1647,17 @@ def view_directory(request):
|
|||||||
escape=1)
|
escape=1)
|
||||||
|
|
||||||
elif file.kind == vclib.FILE:
|
elif file.kind == vclib.FILE:
|
||||||
|
if searchstr is not None:
|
||||||
|
if request.roottype == 'cvs' and (file.errors or file.dead):
|
||||||
|
continue
|
||||||
|
if not search_file(request.repos, request.path_parts + [file.name],
|
||||||
|
request.pathrev, searchstr):
|
||||||
|
continue
|
||||||
if request.roottype == 'cvs' and file.dead:
|
if request.roottype == 'cvs' and file.dead:
|
||||||
num_dead = num_dead + 1
|
num_dead = num_dead + 1
|
||||||
if hideattic:
|
if hideattic:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
num_displayed = num_displayed + 1
|
num_displayed = num_displayed + 1
|
||||||
|
|
||||||
file_where = where_prefix + file.name
|
file_where = where_prefix + file.name
|
||||||
@@ -1687,6 +1730,9 @@ def view_directory(request):
|
|||||||
data['sortby_%s_href' % sortby] = request.get_url(params={'sortdir':
|
data['sortby_%s_href' % sortby] = request.get_url(params={'sortdir':
|
||||||
revsortdir},
|
revsortdir},
|
||||||
escape=1)
|
escape=1)
|
||||||
|
# CVS doesn't support sorting by rev
|
||||||
|
if request.roottype == "cvs":
|
||||||
|
data['sortby_rev_href'] = None
|
||||||
|
|
||||||
# set cvs-specific fields
|
# set cvs-specific fields
|
||||||
if request.roottype == 'cvs':
|
if request.roottype == 'cvs':
|
||||||
@@ -1825,7 +1871,7 @@ def redirect_pathrev(request):
|
|||||||
try:
|
try:
|
||||||
new_pathrev = int(new_pathrev)
|
new_pathrev = int(new_pathrev)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
new_pathrev = youngest
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
@@ -1881,10 +1927,12 @@ def view_log(request):
|
|||||||
# selected revision
|
# selected revision
|
||||||
selected_rev = request.query_dict.get('r1')
|
selected_rev = request.query_dict.get('r1')
|
||||||
|
|
||||||
|
paths_forbidden = {}
|
||||||
entries = [ ]
|
entries = [ ]
|
||||||
name_printed = { }
|
name_printed = { }
|
||||||
cvs = request.roottype == 'cvs'
|
cvs = request.roottype == 'cvs'
|
||||||
for rev in show_revs:
|
for rev in show_revs:
|
||||||
|
last_one = 0
|
||||||
entry = _item()
|
entry = _item()
|
||||||
entry.rev = rev.string
|
entry.rev = rev.string
|
||||||
entry.state = (cvs and rev.dead and 'dead')
|
entry.state = (cvs and rev.dead and 'dead')
|
||||||
@@ -1949,8 +1997,27 @@ def view_log(request):
|
|||||||
entry.vendor_branch = None
|
entry.vendor_branch = None
|
||||||
if rev.filename != request.where:
|
if rev.filename != request.where:
|
||||||
entry.orig_path = rev.filename
|
entry.orig_path = rev.filename
|
||||||
entry.copy_path = rev.copy_path
|
|
||||||
entry.copy_rev = rev.copy_rev
|
# If this path has been copied, check the copy source for
|
||||||
|
# forbiddenness. If it's forbidden, we'll a) pretend this is a
|
||||||
|
# regular add (instead of a copy), and b) stop traversing history.
|
||||||
|
if rev.copy_path:
|
||||||
|
if not paths_forbidden.has_key(rev.copy_path):
|
||||||
|
paths_forbidden[rev.copy_path] = \
|
||||||
|
cfg.is_forbidden(request.rootname,
|
||||||
|
_path_parts(rev.copy_path), pathtype)
|
||||||
|
|
||||||
|
if paths_forbidden[rev.copy_path]:
|
||||||
|
entry.prev = None
|
||||||
|
last_one = 1
|
||||||
|
else:
|
||||||
|
entry.copy_path = rev.copy_path
|
||||||
|
entry.copy_rev = rev.copy_rev
|
||||||
|
entry.copy_href = request.get_url(view_func=view_log,
|
||||||
|
where=rev.copy_path,
|
||||||
|
pathtype=vclib.FILE,
|
||||||
|
params={'pathrev': rev.copy_rev},
|
||||||
|
escape=1)
|
||||||
|
|
||||||
if entry.orig_path:
|
if entry.orig_path:
|
||||||
entry.orig_href = request.get_url(view_func=view_log,
|
entry.orig_href = request.get_url(view_func=view_log,
|
||||||
@@ -1959,14 +2026,6 @@ def view_log(request):
|
|||||||
params={'pathrev': rev.string},
|
params={'pathrev': rev.string},
|
||||||
escape=1)
|
escape=1)
|
||||||
|
|
||||||
if rev.copy_path:
|
|
||||||
entry.copy_href = request.get_url(view_func=view_log,
|
|
||||||
where=rev.copy_path,
|
|
||||||
pathtype=vclib.FILE,
|
|
||||||
params={'pathrev': rev.copy_rev},
|
|
||||||
escape=1)
|
|
||||||
|
|
||||||
|
|
||||||
# view/download links
|
# view/download links
|
||||||
if pathtype is vclib.FILE:
|
if pathtype is vclib.FILE:
|
||||||
entry.view_href, entry.download_href, entry.download_text_href, \
|
entry.view_href, entry.download_href, entry.download_text_href, \
|
||||||
@@ -2027,6 +2086,8 @@ def view_log(request):
|
|||||||
if entry.copy_path:
|
if entry.copy_path:
|
||||||
entry.copy_path = request.server.escape(entry.copy_path)
|
entry.copy_path = request.server.escape(entry.copy_path)
|
||||||
entries.append(entry)
|
entries.append(entry)
|
||||||
|
if last_one:
|
||||||
|
break
|
||||||
|
|
||||||
data = common_template_data(request)
|
data = common_template_data(request)
|
||||||
data.update({
|
data.update({
|
||||||
@@ -2090,9 +2151,8 @@ def view_log(request):
|
|||||||
'tag_prefer_markup': prefer_markup,
|
'tag_prefer_markup': prefer_markup,
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
if not request.pathrev:
|
data['view_href'] = request.get_url(view_func=view_directory,
|
||||||
data['view_href'] = request.get_url(view_func=view_directory,
|
params={}, escape=1)
|
||||||
params={}, escape=1)
|
|
||||||
|
|
||||||
taginfo = options.get('cvs_tags', {})
|
taginfo = options.get('cvs_tags', {})
|
||||||
tagitems = taginfo.items()
|
tagitems = taginfo.items()
|
||||||
@@ -2151,7 +2211,7 @@ def view_annotate(request):
|
|||||||
|
|
||||||
diff_url = request.get_url(view_func=view_diff,
|
diff_url = request.get_url(view_func=view_diff,
|
||||||
params={'r1': None, 'r2': None},
|
params={'r1': None, 'r2': None},
|
||||||
escape=1, partial=1)
|
escape=1, partial=1)
|
||||||
|
|
||||||
include_url = request.get_url(view_func=view_log, where='/WHERE/',
|
include_url = request.get_url(view_func=view_log, where='/WHERE/',
|
||||||
pathtype=vclib.FILE, params={}, escape=1)
|
pathtype=vclib.FILE, params={}, escape=1)
|
||||||
@@ -2233,58 +2293,23 @@ def view_cvsgraph(request):
|
|||||||
request.server.header()
|
request.server.header()
|
||||||
generate_page(request, "graph", data)
|
generate_page(request, "graph", data)
|
||||||
|
|
||||||
def search_files(repos, path_parts, rev, files, search_re):
|
def search_file(repos, path_parts, rev, search_re):
|
||||||
""" Search files in a directory for a regular expression.
|
"""Return 1 iff the contents of the file at PATH_PARTS in REPOS as
|
||||||
|
of revision REV matches regular expression SEARCH_RE."""
|
||||||
Does a check-out of each file in the directory. Only checks for
|
|
||||||
the first match.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Pass in search regular expression. We check out
|
|
||||||
# each file and look for the regular expression. We then return the data
|
|
||||||
# for all files that match the regex.
|
|
||||||
|
|
||||||
# Compile to make sure we do this as fast as possible.
|
|
||||||
searchstr = re.compile(search_re)
|
|
||||||
|
|
||||||
# Will become list of files that have at least one match.
|
|
||||||
# new_file_list also includes directories.
|
|
||||||
new_file_list = [ ]
|
|
||||||
|
|
||||||
# Loop on every file (and directory)
|
|
||||||
for file in files:
|
|
||||||
# Is this a directory? If so, append name to new_file_list
|
|
||||||
# and move to next file.
|
|
||||||
if file.kind != vclib.FILE:
|
|
||||||
new_file_list.append(file)
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Only files at this point
|
|
||||||
|
|
||||||
# Shouldn't search binary files, or should we?
|
|
||||||
# Should allow all text mime types to pass.
|
|
||||||
if not is_text(guess_mime(file.name)):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Only text files at this point
|
|
||||||
|
|
||||||
# Assign contents of checked out file to fp.
|
|
||||||
fp = repos.openfile(path_parts + [file.name], rev)[0]
|
|
||||||
|
|
||||||
# Read in each line, use re.search to search line.
|
|
||||||
# If successful, add file to new_file_list and break.
|
|
||||||
while 1:
|
|
||||||
line = fp.readline()
|
|
||||||
if not line:
|
|
||||||
break
|
|
||||||
if searchstr.search(line):
|
|
||||||
new_file_list.append(file)
|
|
||||||
# close down the pipe (and wait for the child to terminate)
|
|
||||||
fp.close()
|
|
||||||
break
|
|
||||||
|
|
||||||
return new_file_list
|
|
||||||
|
|
||||||
|
# Read in each line of a checked-out file, and then use re.search to
|
||||||
|
# search line.
|
||||||
|
fp = repos.openfile(path_parts, rev)[0]
|
||||||
|
matches = 0
|
||||||
|
while 1:
|
||||||
|
line = fp.readline()
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
if search_re.search(line):
|
||||||
|
matches = 1
|
||||||
|
fp.close()
|
||||||
|
break
|
||||||
|
return matches
|
||||||
|
|
||||||
def view_doc(request):
|
def view_doc(request):
|
||||||
"""Serve ViewVC static content locally.
|
"""Serve ViewVC static content locally.
|
||||||
@@ -2553,21 +2578,29 @@ def diff_parse_headers(fp, diff_type, rev1, rev2, sym1=None, sym2=None):
|
|||||||
return date1, date2, flag, string.join(header_lines, '')
|
return date1, date2, flag, string.join(header_lines, '')
|
||||||
|
|
||||||
|
|
||||||
|
def _get_svn_location(request, base_rev, rev):
|
||||||
|
repos = request.repos
|
||||||
|
try:
|
||||||
|
parts = _path_parts(vclib.svn.get_location(repos, request.where,
|
||||||
|
repos._getrev(base_rev),
|
||||||
|
repos._getrev(rev)))
|
||||||
|
except vclib.InvalidRevision:
|
||||||
|
raise debug.ViewVCException('Invalid path(s) or revision(s) passed '
|
||||||
|
'to diff', '400 Bad Request')
|
||||||
|
except vclib.ItemNotFound:
|
||||||
|
raise debug.ViewVCException('Invalid path(s) or revision(s) passed '
|
||||||
|
'to diff', '400 Bad Request')
|
||||||
|
if request.cfg.is_forbidden(request.rootname, parts, vclib.FILE):
|
||||||
|
raise debug.ViewVCException('Invalid path(s) or revision(s) passed '
|
||||||
|
'to diff', '400 Bad Request')
|
||||||
|
return parts
|
||||||
|
|
||||||
|
|
||||||
def _get_diff_path_parts(request, query_key, rev, base_rev):
|
def _get_diff_path_parts(request, query_key, rev, base_rev):
|
||||||
if request.query_dict.has_key(query_key):
|
if request.query_dict.has_key(query_key):
|
||||||
parts = _path_parts(request.query_dict[query_key])
|
parts = _path_parts(request.query_dict[query_key])
|
||||||
elif request.roottype == 'svn':
|
elif request.roottype == 'svn':
|
||||||
try:
|
parts = _get_svn_location(request, base_rev, rev)
|
||||||
repos = request.repos
|
|
||||||
parts = _path_parts(vclib.svn.get_location(repos, request.where,
|
|
||||||
repos._getrev(base_rev),
|
|
||||||
repos._getrev(rev)))
|
|
||||||
except vclib.InvalidRevision:
|
|
||||||
raise debug.ViewVCException('Invalid path(s) or revision(s) passed '
|
|
||||||
'to diff', '400 Bad Request')
|
|
||||||
except vclib.ItemNotFound:
|
|
||||||
raise debug.ViewVCException('Invalid path(s) or revision(s) passed '
|
|
||||||
'to diff', '400 Bad Request')
|
|
||||||
else:
|
else:
|
||||||
parts = request.path_parts
|
parts = request.path_parts
|
||||||
return parts
|
return parts
|
||||||
@@ -2609,8 +2642,12 @@ def setup_diff(request):
|
|||||||
sym2 = r2[idx+1:]
|
sym2 = r2[idx+1:]
|
||||||
|
|
||||||
if request.roottype == 'svn':
|
if request.roottype == 'svn':
|
||||||
rev1 = str(request.repos._getrev(rev1))
|
try:
|
||||||
rev2 = str(request.repos._getrev(rev2))
|
rev1 = str(request.repos._getrev(rev1))
|
||||||
|
rev2 = str(request.repos._getrev(rev2))
|
||||||
|
except vclib.InvalidRevision:
|
||||||
|
raise debug.ViewVCException('Invalid revision(s) passed to diff',
|
||||||
|
'400 Bad Request')
|
||||||
|
|
||||||
p1 = _get_diff_path_parts(request, 'p1', rev1, request.pathrev)
|
p1 = _get_diff_path_parts(request, 'p1', rev1, request.pathrev)
|
||||||
p2 = _get_diff_path_parts(request, 'p2', rev2, request.pathrev)
|
p2 = _get_diff_path_parts(request, 'p2', rev2, request.pathrev)
|
||||||
@@ -2783,7 +2820,7 @@ def generate_tarball_header(out, name, size=0, mode=None, mtime=0,
|
|||||||
uid=0, gid=0, typefrag=None, linkname='',
|
uid=0, gid=0, typefrag=None, linkname='',
|
||||||
uname='viewvc', gname='viewvc',
|
uname='viewvc', gname='viewvc',
|
||||||
devmajor=1, devminor=0, prefix=None,
|
devmajor=1, devminor=0, prefix=None,
|
||||||
magic='ustar', version='', chksum=None):
|
magic='ustar', version='00', chksum=None):
|
||||||
if not mode:
|
if not mode:
|
||||||
if name[-1:] == '/':
|
if name[-1:] == '/':
|
||||||
mode = 0755
|
mode = 0755
|
||||||
@@ -2799,6 +2836,12 @@ def generate_tarball_header(out, name, size=0, mode=None, mtime=0,
|
|||||||
if not prefix:
|
if not prefix:
|
||||||
prefix = ''
|
prefix = ''
|
||||||
|
|
||||||
|
# generate a GNU tar extension header for long names.
|
||||||
|
if len(name) >= 100:
|
||||||
|
generate_tarball_header(out, '././@LongLink', len(name), 0644, 0, 0, 0, 'L')
|
||||||
|
out.write(name)
|
||||||
|
out.write('\0' * (511 - ((len(name) + 511) % 512)))
|
||||||
|
|
||||||
block1 = struct.pack('100s 8s 8s 8s 12s 12s',
|
block1 = struct.pack('100s 8s 8s 8s 12s 12s',
|
||||||
name,
|
name,
|
||||||
'%07o' % mode,
|
'%07o' % mode,
|
||||||
@@ -2877,6 +2920,11 @@ def generate_tarball(out, request, reldir, stack, dir_mtime=None):
|
|||||||
if cvs and (file.rev is None or file.dead):
|
if cvs and (file.rev is None or file.dead):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Skip forbidden files.
|
||||||
|
if request.cfg.is_forbidden(request.rootname, rep_path + [file.name],
|
||||||
|
file.kind):
|
||||||
|
continue
|
||||||
|
|
||||||
# If we get here, we've seen at least one valid file in the
|
# If we get here, we've seen at least one valid file in the
|
||||||
# current directory. For CVS, we need to make sure there are
|
# current directory. For CVS, we need to make sure there are
|
||||||
# directory parents to contain it, so we flush the stack.
|
# directory parents to contain it, so we flush the stack.
|
||||||
@@ -2907,12 +2955,15 @@ def generate_tarball(out, request, reldir, stack, dir_mtime=None):
|
|||||||
if file.errors or file.kind != vclib.DIR:
|
if file.errors or file.kind != vclib.DIR:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Skip forbidden/hidden directories (top-level only).
|
# Skip hidden directories (top-level only).
|
||||||
if not rep_path:
|
if request.cfg.options.hide_cvsroot \
|
||||||
if (request.cfg.is_forbidden(file.name)
|
and is_cvsroot_path(request.roottype, rep_path + [file.name]):
|
||||||
or (cvs and request.cfg.options.hide_cvsroot
|
continue
|
||||||
and file.name == 'CVSROOT')):
|
|
||||||
continue
|
# Skip forbidden subdirs.
|
||||||
|
if request.cfg.is_forbidden(request.rootname, rep_path + [file.name],
|
||||||
|
file.kind):
|
||||||
|
continue
|
||||||
|
|
||||||
mtime = request.roottype == 'svn' and file.date or None
|
mtime = request.roottype == 'svn' and file.date or None
|
||||||
generate_tarball(out, request, reldir + [file.name], stack, mtime)
|
generate_tarball(out, request, reldir + [file.name], stack, mtime)
|
||||||
@@ -2940,12 +2991,17 @@ def download_tarball(request):
|
|||||||
|
|
||||||
def view_revision(request):
|
def view_revision(request):
|
||||||
if request.roottype == "cvs":
|
if request.roottype == "cvs":
|
||||||
raise ViewVCException("Revision view not supported for CVS repositories "
|
raise debug.ViewVCException("Revision view not supported for CVS "
|
||||||
"at this time.", "400 Bad Request")
|
"repositories at this time.",
|
||||||
|
"400 Bad Request")
|
||||||
|
|
||||||
data = common_template_data(request)
|
data = common_template_data(request)
|
||||||
query_dict = request.query_dict
|
query_dict = request.query_dict
|
||||||
rev = request.repos._getrev(query_dict.get('revision'))
|
try:
|
||||||
|
rev = request.repos._getrev(query_dict.get('revision'))
|
||||||
|
except vclib.InvalidRevision:
|
||||||
|
raise debug.ViewVCException('Invalid revision', '404 Not Found')
|
||||||
|
|
||||||
date, author, msg, changes = vclib.svn.get_revision_info(request.repos, rev)
|
date, author, msg, changes = vclib.svn.get_revision_info(request.repos, rev)
|
||||||
date_str = make_time_string(date, request.cfg)
|
date_str = make_time_string(date, request.cfg)
|
||||||
|
|
||||||
@@ -2953,6 +3009,14 @@ def view_revision(request):
|
|||||||
if check_freshness(request, None, str(rev), weak=1):
|
if check_freshness(request, None, str(rev), weak=1):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Strip forbidden changed paths (we allow forbidden copyfrom-paths
|
||||||
|
# to leak through, though).
|
||||||
|
def _only_allowed(change):
|
||||||
|
return not request.cfg.is_forbidden(request.rootname,
|
||||||
|
_path_parts(change.filename),
|
||||||
|
change.pathtype)
|
||||||
|
changes = filter(_only_allowed, changes)
|
||||||
|
|
||||||
# Handle limit_changes parameter
|
# Handle limit_changes parameter
|
||||||
cfg_limit_changes = request.cfg.options.limit_changes
|
cfg_limit_changes = request.cfg.options.limit_changes
|
||||||
limit_changes = int(query_dict.get('limit_changes', cfg_limit_changes))
|
limit_changes = int(query_dict.get('limit_changes', cfg_limit_changes))
|
||||||
@@ -2978,10 +3042,24 @@ def view_revision(request):
|
|||||||
pathtype = (change.pathtype == vclib.FILE and 'file') \
|
pathtype = (change.pathtype == vclib.FILE and 'file') \
|
||||||
or (change.pathtype == vclib.DIR and 'dir') \
|
or (change.pathtype == vclib.DIR and 'dir') \
|
||||||
or None
|
or None
|
||||||
if (change.action == 'added' or change.action == 'replaced') \
|
|
||||||
and not change.is_copy:
|
# If this is an add or a replacement, we'll verify that copyfrom
|
||||||
change.text_mods = 0
|
# paths are readable (if this is a copy), and if not claim this
|
||||||
change.prop_mods = 0
|
# isn't a copy after all. And if it ain't a copy (now or "after
|
||||||
|
# all"), we'll clear the text_mods and prop_mods flags.
|
||||||
|
if (change.action == 'added' or change.action == 'replaced'):
|
||||||
|
if change.is_copy \
|
||||||
|
and request.cfg.is_forbidden(request.rootname,
|
||||||
|
_path_parts(change.base_path),
|
||||||
|
change.pathtype):
|
||||||
|
change.is_copy = 0
|
||||||
|
if change.action == 'added':
|
||||||
|
change.base_path = None
|
||||||
|
change.base_rev = None
|
||||||
|
|
||||||
|
if not change.is_copy:
|
||||||
|
change.text_mods = 0
|
||||||
|
change.prop_mods = 0
|
||||||
|
|
||||||
view_func = None
|
view_func = None
|
||||||
if change.pathtype is vclib.FILE:
|
if change.pathtype is vclib.FILE:
|
||||||
@@ -3146,22 +3224,23 @@ def english_query(request):
|
|||||||
ret.append('subdirectories')
|
ret.append('subdirectories')
|
||||||
else:
|
else:
|
||||||
ret.append('subdirectory')
|
ret.append('subdirectory')
|
||||||
ret.append(' <em>%s</em> ' % htmlify(dir))
|
ret.append(' <em>%s</em> ' % request.server.escape(dir))
|
||||||
file = request.query_dict.get('file', '')
|
file = request.query_dict.get('file', '')
|
||||||
if file:
|
if file:
|
||||||
if len(ret) != 1: ret.append('and ')
|
if len(ret) != 1: ret.append('and ')
|
||||||
ret.append('to file <em>%s</em> ' % htmlify(file))
|
ret.append('to file <em>%s</em> ' % request.server.escape(file))
|
||||||
who = request.query_dict.get('who', '')
|
who = request.query_dict.get('who', '')
|
||||||
branch = request.query_dict.get('branch', '')
|
branch = request.query_dict.get('branch', '')
|
||||||
if branch:
|
if branch:
|
||||||
ret.append('on branch <em>%s</em> ' % htmlify(branch))
|
ret.append('on branch <em>%s</em> ' % request.server.escape(branch))
|
||||||
else:
|
else:
|
||||||
ret.append('on all branches ')
|
ret.append('on all branches ')
|
||||||
if who:
|
if who:
|
||||||
ret.append('by <em>%s</em> ' % htmlify(who))
|
ret.append('by <em>%s</em> ' % request.server.escape(who))
|
||||||
date = request.query_dict.get('date', 'hours')
|
date = request.query_dict.get('date', 'hours')
|
||||||
if date == 'hours':
|
if date == 'hours':
|
||||||
ret.append('in the last %s hours' % htmlify(request.query_dict.get('hours', '2')))
|
ret.append('in the last %s hours' \
|
||||||
|
% request.server.escape(request.query_dict.get('hours', '2')))
|
||||||
elif date == 'day':
|
elif date == 'day':
|
||||||
ret.append('in the last day')
|
ret.append('in the last day')
|
||||||
elif date == 'week':
|
elif date == 'week':
|
||||||
@@ -3195,16 +3274,126 @@ def prev_rev(rev):
|
|||||||
r = r[:-2]
|
r = r[:-2]
|
||||||
return string.join(r, '.')
|
return string.join(r, '.')
|
||||||
|
|
||||||
def build_commit(request, files, limited_files, dir_strip):
|
def build_commit(request, files, max_files, dir_strip, format):
|
||||||
commit = _item(num_files=len(files), files=[])
|
"""Return a commit object build from the information in FILES, or
|
||||||
commit.limited_files = ezt.boolean(limited_files)
|
None if no allowed files are present in the set. DIR_STRIP is the
|
||||||
|
path prefix to remove from the commit object's set of files. If
|
||||||
|
MAX_FILES is non-zero, it is used to limit the number of files
|
||||||
|
returned in the commit object. FORMAT is the requested output
|
||||||
|
format of the query request."""
|
||||||
|
|
||||||
|
author = files[0].GetAuthor()
|
||||||
|
date = files[0].GetTime()
|
||||||
desc = files[0].GetDescription()
|
desc = files[0].GetDescription()
|
||||||
|
commit_rev = files[0].GetRevision()
|
||||||
|
len_strip = len(dir_strip)
|
||||||
|
commit_files = []
|
||||||
|
num_allowed = 0
|
||||||
|
plus_count = 0
|
||||||
|
minus_count = 0
|
||||||
|
|
||||||
|
for f in files:
|
||||||
|
commit_time = f.GetTime()
|
||||||
|
if commit_time:
|
||||||
|
commit_time = make_time_string(commit_time, request.cfg)
|
||||||
|
else:
|
||||||
|
commit_time = ' '
|
||||||
|
change_type = f.GetTypeString()
|
||||||
|
rev = f.GetRevision()
|
||||||
|
rev_prev = prev_rev(rev)
|
||||||
|
|
||||||
|
dirname = f.GetDirectory()
|
||||||
|
filename = f.GetFile()
|
||||||
|
if dir_strip:
|
||||||
|
assert dirname[:len_strip] == dir_strip
|
||||||
|
assert len(dirname) == len_strip or dirname[len(dir_strip)] == '/'
|
||||||
|
dirname = dirname[len_strip+1:]
|
||||||
|
where = dirname and ("%s/%s" % (dirname, filename)) or filename
|
||||||
|
|
||||||
|
# skip files in forbidden or hidden modules
|
||||||
|
path_parts = _path_parts(where)
|
||||||
|
if request.cfg.is_forbidden(request.rootname, path_parts, vclib.FILE):
|
||||||
|
continue
|
||||||
|
if request.cfg.options.hide_cvsroot \
|
||||||
|
and is_cvsroot_path(request.roottype, path_parts):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# In CVS, we can actually look at deleted revisions; in Subversion
|
||||||
|
# we can't -- we'll look at the previous revision instead.
|
||||||
|
if request.roottype == 'svn':
|
||||||
|
if change_type == 'Remove':
|
||||||
|
params = { 'pathrev': rev_prev }
|
||||||
|
else:
|
||||||
|
params = { 'pathrev': rev }
|
||||||
|
else:
|
||||||
|
params = { 'revision': rev, 'pathrev': f.GetBranch() or None }
|
||||||
|
|
||||||
|
dir_href = request.get_url(view_func=view_directory,
|
||||||
|
where=dirname, pathtype=vclib.DIR,
|
||||||
|
params=params, escape=1)
|
||||||
|
log_href = request.get_url(view_func=view_log,
|
||||||
|
where=where, pathtype=vclib.FILE,
|
||||||
|
params=params, escape=1)
|
||||||
|
diff_href = view_href = download_href = None
|
||||||
|
view_href = request.get_url(view_func=view_markup,
|
||||||
|
where=where, pathtype=vclib.FILE,
|
||||||
|
params=params, escape=1)
|
||||||
|
download_href = request.get_url(view_func=view_checkout,
|
||||||
|
where=where, pathtype=vclib.FILE,
|
||||||
|
params=params, escape=1)
|
||||||
|
if change_type == 'Change':
|
||||||
|
diff_href_params = params.copy()
|
||||||
|
diff_href_params.update({
|
||||||
|
'r1': rev_prev,
|
||||||
|
'r2': rev,
|
||||||
|
'diff_format': None
|
||||||
|
})
|
||||||
|
diff_href = request.get_url(view_func=view_diff,
|
||||||
|
where=where, pathtype=vclib.FILE,
|
||||||
|
params=diff_href_params, escape=1)
|
||||||
|
prefer_markup = ezt.boolean(default_view(guess_mime(filename),
|
||||||
|
request.cfg) == view_markup)
|
||||||
|
|
||||||
|
# Update plus/minus line change count.
|
||||||
|
plus = int(f.GetPlusCount())
|
||||||
|
minus = int(f.GetMinusCount())
|
||||||
|
plus_count = plus_count + plus
|
||||||
|
minus_count = minus_count + minus
|
||||||
|
|
||||||
|
num_allowed = num_allowed + 1
|
||||||
|
if max_files and num_allowed > max_files:
|
||||||
|
continue
|
||||||
|
|
||||||
|
commit_files.append(_item(date=commit_time,
|
||||||
|
dir=request.server.escape(dirname),
|
||||||
|
file=request.server.escape(filename),
|
||||||
|
author=request.server.escape(f.GetAuthor()),
|
||||||
|
rev=rev,
|
||||||
|
branch=f.GetBranch(),
|
||||||
|
plus=plus,
|
||||||
|
minus=minus,
|
||||||
|
type=change_type,
|
||||||
|
dir_href=dir_href,
|
||||||
|
log_href=log_href,
|
||||||
|
view_href=view_href,
|
||||||
|
download_href=download_href,
|
||||||
|
prefer_markup=prefer_markup,
|
||||||
|
diff_href=diff_href))
|
||||||
|
|
||||||
|
# No files survived forbiddenness checks? Let's just pretend this
|
||||||
|
# little commit didn't happen, shall we?
|
||||||
|
if not len(commit_files):
|
||||||
|
return None
|
||||||
|
|
||||||
|
commit = _item(num_files=len(commit_files), files=commit_files,
|
||||||
|
plus=plus_count, minus=minus_count)
|
||||||
|
commit.limited_files = ezt.boolean(num_allowed > len(commit_files))
|
||||||
commit.log = htmlify(desc)
|
commit.log = htmlify(desc)
|
||||||
commit.short_log = format_log(desc, request.cfg)
|
commit.short_log = format_log(desc, request.cfg, format != 'rss')
|
||||||
commit.author = htmlify(files[0].GetAuthor())
|
commit.author = request.server.escape(author)
|
||||||
commit.rss_date = make_rss_time_string(files[0].GetTime(), request.cfg)
|
commit.rss_date = make_rss_time_string(date, request.cfg)
|
||||||
if request.roottype == 'svn':
|
if request.roottype == 'svn':
|
||||||
commit.rev = files[0].GetRevision()
|
commit.rev = commit_rev
|
||||||
commit.rss_url = '%s://%s%s' % \
|
commit.rss_url = '%s://%s%s' % \
|
||||||
(request.server.getenv("HTTPS") == "on" and "https" or "http",
|
(request.server.getenv("HTTPS") == "on" and "https" or "http",
|
||||||
request.server.getenv("HTTP_HOST"),
|
request.server.getenv("HTTP_HOST"),
|
||||||
@@ -3214,74 +3403,6 @@ def build_commit(request, files, limited_files, dir_strip):
|
|||||||
else:
|
else:
|
||||||
commit.rev = None
|
commit.rev = None
|
||||||
commit.rss_url = None
|
commit.rss_url = None
|
||||||
|
|
||||||
len_strip = len(dir_strip)
|
|
||||||
|
|
||||||
for f in files:
|
|
||||||
commit_time = f.GetTime()
|
|
||||||
if commit_time:
|
|
||||||
commit_time = make_time_string(commit_time, request.cfg)
|
|
||||||
else:
|
|
||||||
commit_time = ' '
|
|
||||||
|
|
||||||
dirname = f.GetDirectory()
|
|
||||||
filename = f.GetFile()
|
|
||||||
if dir_strip:
|
|
||||||
assert dirname[:len_strip] == dir_strip
|
|
||||||
assert len(dirname) == len_strip or dirname[len(dir_strip)] == '/'
|
|
||||||
dirname = dirname[len_strip+1:]
|
|
||||||
filename = dirname and ("%s/%s" % (dirname, filename)) or filename
|
|
||||||
|
|
||||||
params = { 'revision': f.GetRevision() }
|
|
||||||
if f.GetBranch(): params['pathrev'] = f.GetBranch()
|
|
||||||
dir_href = request.get_url(view_func=view_directory,
|
|
||||||
where=dirname, pathtype=vclib.DIR,
|
|
||||||
params=params,
|
|
||||||
escape=1)
|
|
||||||
log_href = request.get_url(view_func=view_log,
|
|
||||||
where=filename, pathtype=vclib.FILE,
|
|
||||||
params=params,
|
|
||||||
escape=1)
|
|
||||||
view_href = request.get_url(view_func=view_markup,
|
|
||||||
where=filename, pathtype=vclib.FILE,
|
|
||||||
params={'revision': f.GetRevision() },
|
|
||||||
escape=1)
|
|
||||||
download_href = request.get_url(view_func=view_checkout,
|
|
||||||
where=filename, pathtype=vclib.FILE,
|
|
||||||
params={'revision': f.GetRevision() },
|
|
||||||
escape=1)
|
|
||||||
diff_href = request.get_url(view_func=view_diff,
|
|
||||||
where=filename, pathtype=vclib.FILE,
|
|
||||||
params={'r1': prev_rev(f.GetRevision()),
|
|
||||||
'r2': f.GetRevision(),
|
|
||||||
'diff_format': None},
|
|
||||||
escape=1)
|
|
||||||
|
|
||||||
# skip files in forbidden or hidden modules
|
|
||||||
dir_parts = filter(None, string.split(dirname, '/'))
|
|
||||||
if dir_parts \
|
|
||||||
and ((dir_parts[0] == 'CVSROOT'
|
|
||||||
and request.cfg.options.hide_cvsroot) \
|
|
||||||
or request.cfg.is_forbidden(dir_parts[0])):
|
|
||||||
continue
|
|
||||||
|
|
||||||
commit.files.append(_item(date=commit_time,
|
|
||||||
dir=htmlify(dirname),
|
|
||||||
file=htmlify(f.GetFile()),
|
|
||||||
author=htmlify(f.GetAuthor()),
|
|
||||||
rev=f.GetRevision(),
|
|
||||||
branch=f.GetBranch(),
|
|
||||||
plus=int(f.GetPlusCount()),
|
|
||||||
minus=int(f.GetMinusCount()),
|
|
||||||
type=f.GetTypeString(),
|
|
||||||
dir_href=dir_href,
|
|
||||||
log_href=log_href,
|
|
||||||
view_href=view_href,
|
|
||||||
download_href=download_href,
|
|
||||||
prefer_markup=ezt.boolean
|
|
||||||
(default_view(guess_mime(filename), request.cfg)
|
|
||||||
== view_markup),
|
|
||||||
diff_href=diff_href))
|
|
||||||
return commit
|
return commit
|
||||||
|
|
||||||
def query_backout(request, commits):
|
def query_backout(request, commits):
|
||||||
@@ -3329,7 +3450,7 @@ def view_query(request):
|
|||||||
format = request.query_dict.get('format')
|
format = request.query_dict.get('format')
|
||||||
limit = int(request.query_dict.get('limit', 0))
|
limit = int(request.query_dict.get('limit', 0))
|
||||||
limit_changes = int(request.query_dict.get('limit_changes',
|
limit_changes = int(request.query_dict.get('limit_changes',
|
||||||
request.cfg.options.limit_changes))
|
request.cfg.options.limit_changes))
|
||||||
|
|
||||||
match_types = { 'exact':1, 'like':1, 'glob':1, 'regex':1, 'notregex':1 }
|
match_types = { 'exact':1, 'like':1, 'glob':1, 'regex':1, 'notregex':1 }
|
||||||
sort_types = { 'date':1, 'author':1, 'file':1 }
|
sort_types = { 'date':1, 'author':1, 'file':1 }
|
||||||
@@ -3402,7 +3523,7 @@ def view_query(request):
|
|||||||
# run the query
|
# run the query
|
||||||
db.RunQuery(query)
|
db.RunQuery(query)
|
||||||
|
|
||||||
sql = htmlify(db.CreateSQLQueryString(query))
|
sql = request.server.escape(db.CreateSQLQueryString(query))
|
||||||
|
|
||||||
# gather commits
|
# gather commits
|
||||||
commits = []
|
commits = []
|
||||||
@@ -3415,45 +3536,48 @@ def view_query(request):
|
|||||||
current_desc = query.commit_list[0].GetDescriptionID()
|
current_desc = query.commit_list[0].GetDescriptionID()
|
||||||
current_rev = query.commit_list[0].GetRevision()
|
current_rev = query.commit_list[0].GetRevision()
|
||||||
dir_strip = _path_join(repos_dir)
|
dir_strip = _path_join(repos_dir)
|
||||||
|
|
||||||
for commit in query.commit_list:
|
for commit in query.commit_list:
|
||||||
# base modification time on the newest commit ...
|
commit_desc = commit.GetDescriptionID()
|
||||||
if commit.GetTime() > mod_time: mod_time = commit.GetTime()
|
commit_rev = commit.GetRevision()
|
||||||
# form plus/minus totals
|
|
||||||
plus_count = plus_count + int(commit.GetPlusCount())
|
# base modification time on the newest commit
|
||||||
minus_count = minus_count + int(commit.GetMinusCount())
|
if commit.GetTime() > mod_time:
|
||||||
# group commits with the same commit message ...
|
mod_time = commit.GetTime()
|
||||||
desc = commit.GetDescriptionID()
|
|
||||||
# For CVS, group commits with the same commit message.
|
# For CVS, group commits with the same commit message.
|
||||||
# For Subversion, group them only if they have the same revision number
|
# For Subversion, group them only if they have the same revision number
|
||||||
if request.roottype == 'cvs':
|
if request.roottype == 'cvs':
|
||||||
if current_desc == desc:
|
if current_desc == commit_desc:
|
||||||
if not limit_changes or len(files) < limit_changes:
|
files.append(commit)
|
||||||
files.append(commit)
|
|
||||||
else:
|
|
||||||
limited_files = 1
|
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
if current_rev == commit.GetRevision():
|
if current_rev == commit_rev:
|
||||||
if not limit_changes or len(files) < limit_changes:
|
files.append(commit)
|
||||||
files.append(commit)
|
|
||||||
else:
|
|
||||||
limited_files = 1
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# if our current group has any allowed files, append a commit
|
# append this grouping
|
||||||
# with those files.
|
commit_item = build_commit(request, files, limit_changes,
|
||||||
if len(files):
|
dir_strip, format)
|
||||||
commits.append(build_commit(request, files, limited_files, dir_strip))
|
if commit_item:
|
||||||
|
# update running plus/minus totals
|
||||||
|
plus_count = plus_count + commit_item.plus
|
||||||
|
minus_count = minus_count + commit_item.minus
|
||||||
|
commits.append(commit_item)
|
||||||
|
|
||||||
files = [ commit ]
|
files = [ commit ]
|
||||||
limited_files = 0
|
limited_files = 0
|
||||||
current_desc = desc
|
current_desc = commit_desc
|
||||||
current_rev = commit.GetRevision()
|
current_rev = commit_rev
|
||||||
|
|
||||||
# we need to tack on our last commit grouping, but, again, only if
|
# we need to tack on our last commit grouping, if any
|
||||||
# it has allowed files.
|
commit_item = build_commit(request, files, limit_changes,
|
||||||
if len(files):
|
dir_strip, format)
|
||||||
commits.append(build_commit(request, files, limited_files, dir_strip))
|
if commit_item:
|
||||||
|
# update running plus/minus totals
|
||||||
|
plus_count = plus_count + commit_item.plus
|
||||||
|
minus_count = minus_count + commit_item.minus
|
||||||
|
commits.append(commit_item)
|
||||||
|
|
||||||
# only show the branch column if we are querying all branches
|
# only show the branch column if we are querying all branches
|
||||||
# or doing a non-exact branch match on a CVS repository.
|
# or doing a non-exact branch match on a CVS repository.
|
||||||
@@ -3498,7 +3622,7 @@ def view_query(request):
|
|||||||
})
|
})
|
||||||
|
|
||||||
if format == 'rss':
|
if format == 'rss':
|
||||||
request.server.header("text/xml")
|
request.server.header("application/rss+xml")
|
||||||
generate_page(request, "rss", data)
|
generate_page(request, "rss", data)
|
||||||
else:
|
else:
|
||||||
request.server.header()
|
request.server.header()
|
||||||
@@ -3528,10 +3652,10 @@ for code, view in _views.items():
|
|||||||
|
|
||||||
def list_roots(cfg):
|
def list_roots(cfg):
|
||||||
allroots = { }
|
allroots = { }
|
||||||
for root in cfg.general.cvs_roots.keys():
|
|
||||||
allroots[root] = [cfg.general.cvs_roots[root], 'cvs']
|
|
||||||
for root in cfg.general.svn_roots.keys():
|
for root in cfg.general.svn_roots.keys():
|
||||||
allroots[root] = [cfg.general.svn_roots[root], 'svn']
|
allroots[root] = [cfg.general.svn_roots[root], 'svn']
|
||||||
|
for root in cfg.general.cvs_roots.keys():
|
||||||
|
allroots[root] = [cfg.general.cvs_roots[root], 'cvs']
|
||||||
return allroots
|
return allroots
|
||||||
|
|
||||||
def load_config(pathname=None, server=None):
|
def load_config(pathname=None, server=None):
|
||||||
@@ -3634,7 +3758,7 @@ def main(server, cfg):
|
|||||||
|
|
||||||
finally:
|
finally:
|
||||||
debug.t_end('main')
|
debug.t_end('main')
|
||||||
debug.dump()
|
debug.t_dump(server.file())
|
||||||
debug.DumpChildren(server)
|
debug.DumpChildren(server)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# -*-python-*-
|
# -*-python-*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -15,8 +15,18 @@
|
|||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
|
|
||||||
import os, sys, traceback, string, thread
|
import os, sys, traceback, string, thread
|
||||||
|
try:
|
||||||
|
import win32api
|
||||||
|
except ImportError, e:
|
||||||
|
raise ImportError, str(e) + """
|
||||||
|
|
||||||
|
Did you install the Python for Windows Extensions?
|
||||||
|
|
||||||
|
http://sourceforge.net/projects/pywin32/
|
||||||
|
"""
|
||||||
|
|
||||||
import win32process, win32pipe, win32con
|
import win32process, win32pipe, win32con
|
||||||
import win32event, win32file, win32api, winerror
|
import win32event, win32file, winerror
|
||||||
import pywintypes, msvcrt
|
import pywintypes, msvcrt
|
||||||
|
|
||||||
# Buffer size for spooling
|
# Buffer size for spooling
|
||||||
|
53
notes/TODO
Normal file
53
notes/TODO
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
PREFACE
|
||||||
|
-------
|
||||||
|
This file will go away soon after release 0.8. Please use the SourceForge
|
||||||
|
tracker to resubmit any of the items listed below, if you think, it is
|
||||||
|
still an issue:
|
||||||
|
http://sourceforge.net/tracker/?group_id=18760
|
||||||
|
Before reporting please check, whether someone else has already done this.
|
||||||
|
Working patches increase the chance to be included into the next release.
|
||||||
|
-- PeFu / October 2001
|
||||||
|
|
||||||
|
TODO ITEMS
|
||||||
|
----------
|
||||||
|
*) add Tamminen Eero's comments on how to make Linux directly execute
|
||||||
|
the Python script. From email on Feb 19.
|
||||||
|
[ add other examples, such as my /bin/sh hack or the teeny CGI stub
|
||||||
|
importing the bulk hack ]
|
||||||
|
|
||||||
|
*) insert rcs_path into PATH before calling "rcsdiff". rcsdiff might
|
||||||
|
use "co" and needs to find it on the path.
|
||||||
|
|
||||||
|
*) show the "locked" flag (attach it to the LogEntry objects).
|
||||||
|
Idea from Russell Gordon <russell@hoopscotch.dhs.org>
|
||||||
|
|
||||||
|
*) committing with a specific revision number:
|
||||||
|
http://mailman.lyra.org/pipermail/viewcvs/2000q1/000008.html
|
||||||
|
|
||||||
|
*) add capability similar to cvs2cl.pl:
|
||||||
|
http://mailman.lyra.org/pipermail/viewcvs/2000q2/000050.html
|
||||||
|
suggestion from Chris Meyer <cmeyer@gatan.com>.
|
||||||
|
|
||||||
|
*) add a tree view of the directory structure (and files?)
|
||||||
|
|
||||||
|
*) include a ConfigParser.py to help older Python installations
|
||||||
|
|
||||||
|
*) add a check for the rcs programs/paths to viewvc-install. clarify the
|
||||||
|
dependency on RCS in the docs.
|
||||||
|
|
||||||
|
*) have a "check" mode that verifies binaries are available on rcs_path
|
||||||
|
|
||||||
|
-> alternately (probably?): use rcsparse rather than external tools
|
||||||
|
|
||||||
|
KNOWN BUGS
|
||||||
|
----------
|
||||||
|
*) time.timezone seems to not be available on some 1.5.2 installs.
|
||||||
|
I was unable to verify this. On RedHat and SuSE Linux this bug
|
||||||
|
is non existant.
|
||||||
|
|
||||||
|
*) With old repositories containing many branches, tags or thousands
|
||||||
|
or revisions, the cvsgraph feature becomes unusable (see INSTALL).
|
||||||
|
ViewVC can't do much about this, but it might be possible to
|
||||||
|
investigate the number of branches, tags and revision in advance
|
||||||
|
and disable the cvsgraph links, if the numbers exceed a certain
|
||||||
|
treshold.
|
85
notes/releases.txt
Normal file
85
notes/releases.txt
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
RELEASE MANAGEMENT
|
||||||
|
|
||||||
|
ViewVC rolls releases from release branches associate with each minor
|
||||||
|
version of the software. For example, the 1.1.0 is rolled from the
|
||||||
|
1.1.x branch. The same is true for the 1.1.1, 1.1.2, ... releases.
|
||||||
|
|
||||||
|
There is a script, `tools/make-release', which creates a release
|
||||||
|
directory and the various archive files that we distribute. All other
|
||||||
|
steps required to get a ViewVC release out of the door require manual
|
||||||
|
execution (currently by C. Michael Pilato). Those steps are as
|
||||||
|
follows:
|
||||||
|
|
||||||
|
Checkout a working copy of the release branch for the release you
|
||||||
|
intend to roll, and in that working copy, perform the following steps
|
||||||
|
(X, Y, and Z below represent integral major, minor, and patch version
|
||||||
|
numbers, and not literal):
|
||||||
|
|
||||||
|
1. Review any open bug reports:
|
||||||
|
|
||||||
|
http://viewvc.tigris.org/servlets/ProjectIssues
|
||||||
|
|
||||||
|
2. Add a new subsection to the file 'docs/upgrading.html' describing
|
||||||
|
all user visible changes for users of previous releases of ViewVC.
|
||||||
|
Commit any modifications. NOTE: This step should not be necessary
|
||||||
|
for patch releases.
|
||||||
|
|
||||||
|
3. Verify that copyright years are correct in both the LICENSE.html
|
||||||
|
file and the source code.
|
||||||
|
|
||||||
|
4. Update and commit the 'CHANGES' file.
|
||||||
|
|
||||||
|
5. Test, test, test! There is no automatic testsuite available. So
|
||||||
|
just run with permuting different `viewvc.conf' settings... and
|
||||||
|
pray. Fix what needs fixin', keeping the CHANGES file in sync
|
||||||
|
with the branch.
|
||||||
|
|
||||||
|
6. At this point, the source code committed to the release branch
|
||||||
|
should exactly reflect what you wish to distribute and dub "the
|
||||||
|
release".
|
||||||
|
|
||||||
|
7. Edit the file 'lib/viewvc.py' and remove the "-dev" suffix from
|
||||||
|
__version__. The remainder should be of the form "X.Y.Z", where X,
|
||||||
|
Y, and Z are positive integers. Do NOT commit this change.
|
||||||
|
|
||||||
|
8. Update your working copy to HEAD, and tag the release:
|
||||||
|
|
||||||
|
svn update
|
||||||
|
svn cp -m "Tag the X.Y.Z final release." . \
|
||||||
|
http://viewvc.tigris.org/svn/viewvc/tags/X.Y.Z
|
||||||
|
|
||||||
|
9. Go into an empty directory and run the 'make-release' script:
|
||||||
|
|
||||||
|
tools/make-release viewvc-X.Y.Z tags/X.Y.Z
|
||||||
|
|
||||||
|
10. Verify the archive files:
|
||||||
|
|
||||||
|
- do they have a LICENSE.html file?
|
||||||
|
- do they have necessary include documentation?
|
||||||
|
- do they *not* have unnecessary stuff?
|
||||||
|
- do they install and work correctly?
|
||||||
|
|
||||||
|
11. Upload the created archive files (tar.gz and zip) into the Files
|
||||||
|
and Documents section of the Tigris.org project, and modify the
|
||||||
|
CHECKSUMS document there accordingly. Also, drop a copy of the
|
||||||
|
archive files into the root directory of the viewvc.org website
|
||||||
|
(unversioned).
|
||||||
|
|
||||||
|
12. Update the websites (both the viewvc.org/ and www/ ones) to refer
|
||||||
|
to the new release files.
|
||||||
|
|
||||||
|
13. Edit the file 'lib/viewvc.py' again, re-adding the "-dev" suffix
|
||||||
|
and incrementing the patch number assigned to the __version__
|
||||||
|
variable, and commit:
|
||||||
|
|
||||||
|
svn ci -m "Begin a new release cycle."
|
||||||
|
|
||||||
|
14. Edit the Issue Tracker configuration options, adding a new Version
|
||||||
|
for the just-released one, and a new Milestone for the next patch
|
||||||
|
(and possibly, minor or major) release. (For the Milestone sort
|
||||||
|
key, use a packed integer XXYYZZ: 1.0.3 == 10003, 2.11.4 == 21104.)
|
||||||
|
|
||||||
|
15. Write an announcement explaining all the cool new features and
|
||||||
|
post it to the announce@ list, to the project's News area, and to
|
||||||
|
other places interested in this sort of stuff, such as Freshmeat
|
||||||
|
(http://www.freshmeat.net).
|
@@ -4,22 +4,30 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="vc_header[is sortby "file"]_sort[end]" colspan="2">
|
<th class="vc_header[is sortby "file"]_sort[end]" colspan="2">
|
||||||
<a href="[sortby_file_href]#dirlist">File
|
[if-any sortby_file_href]<a href="[sortby_file_href]#dirlist">File</a>[else]File[end]
|
||||||
[is sortby "file"]
|
[is sortby "file"]
|
||||||
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
|
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
|
||||||
width="13" height="13"
|
width="13" height="13"
|
||||||
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
|
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
|
||||||
[end]
|
[end]
|
||||||
</a>
|
|
||||||
</th>
|
</th>
|
||||||
|
[if-any sortby_rev_href]
|
||||||
<th class="vc_header[is sortby "rev"]_sort[end]">
|
<th class="vc_header[is sortby "rev"]_sort[end]">
|
||||||
<a href="[sortby_rev_href]#dirlist">Last Change
|
<a href="[sortby_rev_href]#dirlist">Last Change</a>
|
||||||
[is sortby "rev"]
|
[is sortby "rev"]
|
||||||
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
|
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
|
||||||
width="13" height="13"
|
width="13" height="13"
|
||||||
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
|
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
|
||||||
[end]
|
[end]
|
||||||
</a>
|
[else]
|
||||||
|
<th class="vc_header[is sortby "date"]_sort[end]">
|
||||||
|
[if-any sortby_date_href]<a href="[sortby_date_href]#dirlist">Last Change</a>[else]Last Change[end]
|
||||||
|
[is sortby "date"]
|
||||||
|
<img class="vc_sortarrow" alt="[is sortdir "down"](date)[end]"
|
||||||
|
width="13" height="13"
|
||||||
|
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
|
||||||
|
[end]
|
||||||
|
[end]
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@@ -4,50 +4,45 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="vc_header[is sortby "file"]_sort[end]" colspan="2">
|
<th class="vc_header[is sortby "file"]_sort[end]" colspan="2">
|
||||||
<a href="[sortby_file_href]#dirlist">File
|
[if-any sortby_file_href]<a href="[sortby_file_href]#dirlist">File</a>[else]File[end]
|
||||||
[is sortby "file"]
|
[is sortby "file"]
|
||||||
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
|
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
|
||||||
width="13" height="13"
|
width="13" height="13"
|
||||||
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
|
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
|
||||||
[end]
|
[end]
|
||||||
</a>
|
|
||||||
</th>
|
</th>
|
||||||
<th class="vc_header[is sortby "rev"]_sort[end]">
|
<th class="vc_header[is sortby "rev"]_sort[end]">
|
||||||
<a href="[sortby_rev_href]#dirlist">Rev.
|
[if-any sortby_rev_href]<a href="[sortby_rev_href]#dirlist">Rev.</a>[else]Rev.[end]
|
||||||
[is sortby "rev"]
|
[is sortby "rev"]
|
||||||
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
|
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
|
||||||
width="13" height="13"
|
width="13" height="13"
|
||||||
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
|
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
|
||||||
[end]
|
[end]
|
||||||
</a>
|
|
||||||
</th>
|
</th>
|
||||||
<th class="vc_header[is sortby "date"]_sort[end]">
|
<th class="vc_header[is sortby "date"]_sort[end]">
|
||||||
<a href="[sortby_date_href]#dirlist">Age
|
[if-any sortby_date_href]<a href="[sortby_date_href]#dirlist">Age</a>[else]Age[end]
|
||||||
[is sortby "date"]
|
[is sortby "date"]
|
||||||
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
|
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
|
||||||
width="13" height="13"
|
width="13" height="13"
|
||||||
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
|
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
|
||||||
[end]
|
[end]
|
||||||
</a>
|
|
||||||
</th>
|
</th>
|
||||||
<th class="vc_header[is sortby "author"]_sort[end]">
|
<th class="vc_header[is sortby "author"]_sort[end]">
|
||||||
<a href="[sortby_author_href]#dirlist">Author
|
[if-any sortby_author_href]<a href="[sortby_author_href]#dirlist">Author</a>[else]Author[end]
|
||||||
[is sortby "author"]
|
[is sortby "author"]
|
||||||
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
|
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
|
||||||
width="13" height="13"
|
width="13" height="13"
|
||||||
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
|
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
|
||||||
[end]
|
[end]
|
||||||
</a>
|
|
||||||
</th>
|
</th>
|
||||||
[is cfg.options.show_logs "1"]
|
[is cfg.options.show_logs "1"]
|
||||||
<th class="vc_header[is sortby "log"]_sort[end]">
|
<th class="vc_header[is sortby "log"]_sort[end]">
|
||||||
<a href="[sortby_log_href]#dirlist">Last log entry
|
[if-any sortby_log_href]<a href="[sortby_log_href]#dirlist">Last log entry</a>[else]Last log entry[end]
|
||||||
[is sortby "log"]
|
[is sortby "log"]
|
||||||
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
|
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
|
||||||
width="13" height="13"
|
width="13" height="13"
|
||||||
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
|
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
|
||||||
[end]
|
[end]
|
||||||
</a>
|
|
||||||
</th>
|
</th>
|
||||||
[end]
|
[end]
|
||||||
</tr>
|
</tr>
|
||||||
|
@@ -9,16 +9,43 @@
|
|||||||
<h3>An Exception Has Occurred</h3>
|
<h3>An Exception Has Occurred</h3>
|
||||||
|
|
||||||
[if-any msg]
|
[if-any msg]
|
||||||
<p><pre>[msg]</pre></p>
|
<p>[msg]</p>
|
||||||
[end]
|
[end]
|
||||||
|
|
||||||
[if-any status]
|
[if-any status]
|
||||||
<h4>HTTP Response Status</h4>
|
<h4>HTTP Response Status</h4>
|
||||||
<p><pre>[status]</pre></p>
|
<p><pre>[status]</pre></p>
|
||||||
<hr />
|
<hr />
|
||||||
[end]
|
[end]
|
||||||
|
|
||||||
|
[if-any msg][else]
|
||||||
<h4>Python Traceback</h4>
|
<h4>Python Traceback</h4>
|
||||||
<p><pre>
|
<p><pre>
|
||||||
[stacktrace]
|
[stacktrace]
|
||||||
</pre></p>
|
</pre></p>
|
||||||
|
[end]
|
||||||
|
|
||||||
|
[# Here follows a bunch of space characters, present to ensure that
|
||||||
|
our error message is larger than 512 bytes so that IE's "Friendly
|
||||||
|
Error Message" won't show. For more information, see
|
||||||
|
http://oreillynet.com/onjava/blog/2002/09/internet_explorer_subverts_err.html]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
enter a numeric revision.
|
enter a numeric revision.
|
||||||
[end]
|
[end]
|
||||||
</p>
|
</p>
|
||||||
<form method="get" action="[diff_select_action]" name="diff_select">
|
<form method="get" action="[diff_select_action]" id="diff_select">
|
||||||
|
|
||||||
<table cellpadding="2" cellspacing="0" class="auto">
|
<table cellpadding="2" cellspacing="0" class="auto">
|
||||||
<tr>
|
<tr>
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
</select>
|
</select>
|
||||||
<input type="text" size="12" name="tr1"
|
<input type="text" size="12" name="tr1"
|
||||||
value="[if-any rev_selected][rev_selected][else][first_revision][end]"
|
value="[if-any rev_selected][rev_selected][else][first_revision][end]"
|
||||||
onchange="document.diff_select.r1.selectedIndex=0" />
|
onchange="document.getElementById('diff_select').r1.selectedIndex=0" />
|
||||||
[else]
|
[else]
|
||||||
<input type="text" size="12" name="r1"
|
<input type="text" size="12" name="r1"
|
||||||
value="[if-any rev_selected][rev_selected][else][first_revision][end]" />
|
value="[if-any rev_selected][rev_selected][else][first_revision][end]" />
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
</select>
|
</select>
|
||||||
<input type="text" size="12" name="tr2"
|
<input type="text" size="12" name="tr2"
|
||||||
value="[last_revision]"
|
value="[last_revision]"
|
||||||
onchange="document.diff_select.r1.selectedIndex=0" />
|
onchange="document.getElementById('diff_select').r2.selectedIndex=0" />
|
||||||
[else]
|
[else]
|
||||||
<input type="text" size="12" name="r2" value="[last_revision]" />
|
<input type="text" size="12" name="r2" value="[last_revision]" />
|
||||||
[end]
|
[end]
|
||||||
|
@@ -1,38 +1,28 @@
|
|||||||
[if-any search_re_form]
|
[if-any search_re_form]
|
||||||
<hr />
|
<hr />
|
||||||
[# this table holds the selectors on the left, and reset on the right ]
|
<div>
|
||||||
<table class="auto">
|
Show files containing the regular expression:
|
||||||
<tr>
|
<form method="get" action="[search_re_action]" style="display: inline;">
|
||||||
<td>Show files containing the regular expression:</td>
|
<div style="display: inline;">
|
||||||
<td>
|
[search_re_hidden_values]
|
||||||
<form method="get" action="[search_re_action]">
|
<input type="text" name="search" value="[search_re]" />
|
||||||
<div>
|
<input type="submit" value="Show" />
|
||||||
[search_re_hidden_values]
|
</div>
|
||||||
<input type="text" name="search" value="[search_re]" />
|
</form>
|
||||||
<input type="submit" value="Show" />
|
[if-any search_re]
|
||||||
</div>
|
<form method="get" action="[search_re_action]" style="display: inline;">
|
||||||
</form>
|
<div style="display: inline;">
|
||||||
</td>
|
[search_re_hidden_values]
|
||||||
</tr>
|
<input type="submit" value="Show all files" />
|
||||||
[if-any search_re]
|
</div>
|
||||||
<tr>
|
</form>
|
||||||
<td> </td>
|
|
||||||
<td>
|
|
||||||
<form method="get" action="[search_tag_action]">
|
|
||||||
<div>
|
|
||||||
[search_tag_hidden_values]
|
|
||||||
<input type="submit" value="Show all files" />
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
[end]
|
|
||||||
</table>
|
|
||||||
[end]
|
[end]
|
||||||
|
[end]
|
||||||
|
</div>
|
||||||
|
|
||||||
[# if you want to disable tarball generation remove the following: ]
|
[# if you want to disable tarball generation remove the following: ]
|
||||||
[if-any tarball_href]
|
[if-any tarball_href]
|
||||||
<p style="margin:0;"><a href="[tarball_href]">Download tarball</a></p>
|
<p style="margin:0;"><a href="[tarball_href]">Download GNU tarball</a></p>
|
||||||
[end]
|
[end]
|
||||||
|
|
||||||
[include "footer.ezt"]
|
[include "footer.ezt"]
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Powered by <a href="http://viewvc.tigris.org/">ViewVC [vsn]</a></td>
|
<td>Powered by <a href="http://viewvc.tigris.org/">ViewVC [vsn]</a></td>
|
||||||
<td style="text-align: right;">[if-any rss_href]<a href="[rss_href]" title="RSS 2.0 feed"><img src="[docroot]/images/feed-icon-16x16.jpg"/>[else] [end]</td>
|
<td style="text-align: right;">[if-any rss_href]<a href="[rss_href]" title="RSS 2.0 feed"><img src="[docroot]/images/feed-icon-16x16.jpg" alt="RSS 2.0 feed"/></a>[else] [end]</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
<title>[if-any rootname][[][rootname]][else]ViewVC[end] [page_title]</title>
|
<title>[if-any rootname][[][rootname]][else]ViewVC[end] [page_title]</title>
|
||||||
<meta name="generator" content="ViewVC [vsn]" />
|
<meta name="generator" content="ViewVC [vsn]" />
|
||||||
<link rel="stylesheet" href="[docroot]/styles.css" type="text/css" />
|
<link rel="stylesheet" href="[docroot]/styles.css" type="text/css" />
|
||||||
[if-any rss_href]<link rel="alternate" type="application/rss+xml" title="RSS [[][rootname]][where]" HREF="[rss_href]">[end]
|
[if-any rss_href]<link rel="alternate" type="application/rss+xml" title="RSS [[][rootname]][where]" href="[rss_href]" />[end]
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="vc_navheader">
|
<div class="vc_navheader">
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
<form method="get" action="[pathrev_action]" style="display: inline">
|
<form method="get" action="[pathrev_action]" style="display: inline">
|
||||||
|
<div style="display: inline">
|
||||||
[pathrev_hidden_values]
|
[pathrev_hidden_values]
|
||||||
[is roottype "cvs"]
|
[is roottype "cvs"]
|
||||||
[define pathrev_selected][pathrev][end]
|
[define pathrev_selected][pathrev][end]
|
||||||
@@ -34,16 +35,19 @@
|
|||||||
<input type="text" name="pathrev" value="[pathrev]" size="6"/>
|
<input type="text" name="pathrev" value="[pathrev]" size="6"/>
|
||||||
[end]
|
[end]
|
||||||
<input type="submit" value="Set" />
|
<input type="submit" value="Set" />
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
[if-any pathrev]
|
[if-any pathrev]
|
||||||
<form method="get" action="[pathrev_clear_action]" style="display: inline">
|
<form method="get" action="[pathrev_clear_action]" style="display: inline">
|
||||||
|
<div style="display: inline">
|
||||||
[pathrev_clear_hidden_values]
|
[pathrev_clear_hidden_values]
|
||||||
[if-any lastrev]
|
[if-any lastrev]
|
||||||
[is pathrev lastrev][else]<input type="submit" value="Set to [lastrev]">[end]
|
[is pathrev lastrev][else]<input type="submit" value="Set to [lastrev]" />[end]
|
||||||
(<i>Current path doesn't exist after revision <strong>[lastrev]</strong></i>)
|
(<i>Current path doesn't exist after revision <strong>[lastrev]</strong></i>)
|
||||||
[else]
|
[else]
|
||||||
<input type="submit" value="Clear">
|
<input type="submit" value="Clear" />
|
||||||
[end]
|
[end]
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
[end]
|
[end]
|
||||||
|
@@ -9,8 +9,8 @@
|
|||||||
<title>[if-any commits.rev][commits.rev]: [end][[commits.author]] [commits.short_log]</title>
|
<title>[if-any commits.rev][commits.rev]: [end][[commits.author]] [commits.short_log]</title>
|
||||||
[if-any commits.rss_url]<link>[commits.rss_url]</link>[end]
|
[if-any commits.rss_url]<link>[commits.rss_url]</link>[end]
|
||||||
<author>[commits.author]</author>
|
<author>[commits.author]</author>
|
||||||
<pubDate>[commits.rss_date]</pubDate>
|
<pubDate>[if-any commits.rss_date][commits.rss_date][else](unknown date)[end]</pubDate>
|
||||||
<description>[commits.log]</description>
|
<description><pre>[format "xml"][commits.log][end]</pre></description>
|
||||||
</item>[end]
|
</item>[end]
|
||||||
</channel>
|
</channel>
|
||||||
</rss>
|
</rss>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -17,17 +17,18 @@
|
|||||||
|
|
||||||
### Validate input
|
### Validate input
|
||||||
if test $# != 2 && test $# != 1; then
|
if test $# != 2 && test $# != 1; then
|
||||||
echo "Usage: $0 TARGET-DIRECTORY [TAGNAME]"
|
echo "Usage: $0 TARGET-DIRECTORY [BRANCH]"
|
||||||
echo ""
|
echo ""
|
||||||
echo "If TAGNAME is not provided, the release will be rolled from trunk."
|
echo "If BRANCH (i.e. \"tags/1.1.0\" or \"branches/1.0.x\") is not provided,"
|
||||||
|
echo "the release will be rolled from trunk."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
TARGET=${1}
|
TARGET=${1}
|
||||||
if test $# == 1; then
|
if test $# = 1; then
|
||||||
ROOT=trunk
|
ROOT=trunk
|
||||||
else
|
else
|
||||||
ROOT=tags/${2}
|
ROOT=${2}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test -e ${TARGET}; then
|
if test -e ${TARGET}; then
|
||||||
@@ -36,7 +37,8 @@ if test -e ${TARGET}; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
### Grab an export from the Subversion repository.
|
### Grab an export from the Subversion repository.
|
||||||
echo "Exporting into:" ${TARGET}
|
EXPORT_URL="http://viewvc.tigris.org/svn/viewvc/${ROOT}"
|
||||||
|
echo "Exporting '${EXPORT_URL}' into '${TARGET}'"
|
||||||
|
|
||||||
for PLATFORM in unix windows; do
|
for PLATFORM in unix windows; do
|
||||||
if test ${PLATFORM} = windows; then
|
if test ${PLATFORM} = windows; then
|
||||||
@@ -45,30 +47,44 @@ for PLATFORM in unix windows; do
|
|||||||
EOL="--native-eol LF"
|
EOL="--native-eol LF"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
svn export ${EOL} http://viewvc.tigris.org/svn/viewvc/${ROOT} ${TARGET}
|
echo "Beginning build for ${PLATFORM}:"
|
||||||
|
|
||||||
|
echo " Exporting source code..."
|
||||||
|
svn export --quiet ${EOL} ${EXPORT_URL} ${TARGET}
|
||||||
|
|
||||||
### Various shifting, cleanup.
|
### Various shifting, cleanup.
|
||||||
|
|
||||||
# Documentation is now also distributed together with the release, but
|
|
||||||
# we still copy the license file to its traditional place (it is small
|
|
||||||
# and many files still contain comments refering to this location):
|
|
||||||
|
|
||||||
# Remove some not useful directories
|
# Remove some not useful directories
|
||||||
rm -r ${TARGET}/{elemx,tests,tools,tparse,viewcvs.sourceforge.net,www}
|
for JUNK in elemx \
|
||||||
|
notes \
|
||||||
|
tests \
|
||||||
|
tools \
|
||||||
|
tparse \
|
||||||
|
viewcvs.sourceforge.net \
|
||||||
|
viewvc.org \
|
||||||
|
www; do
|
||||||
|
if [ -d ${TARGET}/${JUNK} ]; then
|
||||||
|
echo " Removing ${TARGET}/${JUNK}..."
|
||||||
|
rm -r ${TARGET}/${JUNK}
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
# Make sure permissions are reasonable:
|
# Make sure permissions are reasonable:
|
||||||
|
echo " Normalizing permissions..."
|
||||||
find ${TARGET} -print | xargs chmod uoa+r
|
find ${TARGET} -print | xargs chmod uoa+r
|
||||||
find ${TARGET} -type d -print | xargs chmod uoa+x
|
find ${TARGET} -type d -print | xargs chmod uoa+x
|
||||||
|
|
||||||
if test ${PLATFORM} = windows; then
|
if test ${PLATFORM} = windows; then
|
||||||
# Create also a ZIP file for those poor souls :-) still using Windows:
|
# Create also a ZIP file for those poor souls :-) still using Windows:
|
||||||
|
echo " Creating ZIP archive..."
|
||||||
zip -qor9 ${TARGET}.zip ${TARGET}
|
zip -qor9 ${TARGET}.zip ${TARGET}
|
||||||
else
|
else
|
||||||
# Cut the tarball:
|
# Cut the tarball:
|
||||||
|
echo " Creating tarball archive..."
|
||||||
tar cf - ${TARGET} | gzip -9 > ${TARGET}.tar.gz
|
tar cf - ${TARGET} | gzip -9 > ${TARGET}.tar.gz
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# remove target directory
|
# remove target directory
|
||||||
rm -r ${TARGET}
|
rm -r ${TARGET}
|
||||||
done
|
done
|
||||||
echo 'Done.'
|
echo "Done."
|
||||||
|
@@ -1,2 +0,0 @@
|
|||||||
*.tar.gz
|
|
||||||
*.zip
|
|
@@ -1 +0,0 @@
|
|||||||
RedirectMatch ^(.*)$ http://www.viewvc.org/$1
|
|
628
viewvc-install
628
viewvc-install
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- Mode: python -*-
|
# -*- Mode: python -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# By using this file, you agree to the terms and conditions set forth in
|
# By using this file, you agree to the terms and conditions set forth in
|
||||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||||
@@ -11,10 +11,7 @@
|
|||||||
#
|
#
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# install script for viewvc -- temporary?
|
# Install script for ViewVC
|
||||||
#
|
|
||||||
# ### this will eventually be replaced by autoconf plus tools. an
|
|
||||||
# ### interactive front-end to ./configure may be provided.
|
|
||||||
#
|
#
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -27,7 +24,7 @@ import py_compile
|
|||||||
import getopt
|
import getopt
|
||||||
import StringIO
|
import StringIO
|
||||||
|
|
||||||
# get access to our library modules
|
# Get access to our library modules.
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), 'lib'))
|
sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), 'lib'))
|
||||||
|
|
||||||
import compat
|
import compat
|
||||||
@@ -36,324 +33,441 @@ import compat_ndiff
|
|||||||
version = viewvc.__version__
|
version = viewvc.__version__
|
||||||
|
|
||||||
|
|
||||||
## installer text
|
## Installer defaults.
|
||||||
INFO_TEXT = """
|
|
||||||
|
|
||||||
This is the ViewVC %s installer.
|
|
||||||
|
|
||||||
It will allow you to choose the install path for ViewVC. You will
|
|
||||||
now be asked some installation questions.
|
|
||||||
|
|
||||||
Defaults are given in square brackets. Just hit [Enter] if a default
|
|
||||||
is okay.
|
|
||||||
""" % version
|
|
||||||
|
|
||||||
## installer defaults
|
|
||||||
DESTDIR = None
|
DESTDIR = None
|
||||||
ROOT_DIR = None
|
ROOT_DIR = None
|
||||||
|
CLEAN_MODE = None
|
||||||
|
|
||||||
|
|
||||||
## list of files for installation
|
## List of files for installation.
|
||||||
## tuple (source path, destination path, install mode, true/false flag for
|
## tuple (source path,
|
||||||
## search-and-replace, flag or text for prompt before replace,
|
## destination path,
|
||||||
## compile_it)
|
## mode,
|
||||||
##
|
## boolean -- search-and-replace?
|
||||||
|
## boolean -- prompt before replacing?
|
||||||
|
## boolean -- compile?)
|
||||||
FILE_INFO_LIST = [
|
FILE_INFO_LIST = [
|
||||||
("bin/cgi/viewvc.cgi", "bin/cgi/viewvc.cgi", 0755, 1, 0, 0),
|
("bin/cgi/viewvc.cgi", "bin/cgi/viewvc.cgi", 0755, 1, 0, 0),
|
||||||
("bin/cgi/query.cgi", "bin/cgi/query.cgi", 0755, 1, 0, 0),
|
("bin/cgi/query.cgi", "bin/cgi/query.cgi", 0755, 1, 0, 0),
|
||||||
("bin/mod_python/viewvc.py", "bin/mod_python/viewvc.py", 0755, 1, 0, 0),
|
("bin/mod_python/viewvc.py", "bin/mod_python/viewvc.py", 0755, 1, 0, 0),
|
||||||
("bin/mod_python/query.py", "bin/mod_python/query.py", 0755, 1, 0, 0),
|
("bin/mod_python/query.py", "bin/mod_python/query.py", 0755, 1, 0, 0),
|
||||||
("bin/mod_python/handler.py", "bin/mod_python/handler.py", 0755, 1, 0, 0),
|
("bin/mod_python/handler.py", "bin/mod_python/handler.py", 0755, 1, 0, 0),
|
||||||
("bin/mod_python/.htaccess", "bin/mod_python/.htaccess", 0755, 0, 0, 0),
|
("bin/mod_python/.htaccess", "bin/mod_python/.htaccess", 0755, 0, 0, 0),
|
||||||
("bin/standalone.py", "bin/standalone.py", 0755, 1, 0, 0),
|
("bin/standalone.py", "bin/standalone.py", 0755, 1, 0, 0),
|
||||||
("viewvc.conf.dist", "viewvc.conf", 0644, 0,
|
("bin/loginfo-handler", "bin/loginfo-handler", 0755, 1, 0, 0),
|
||||||
"""Note: If you are upgrading from viewcvs-0.7 or earlier:
|
("bin/cvsdbadmin", "bin/cvsdbadmin", 0755, 1, 0, 0),
|
||||||
The section [text] has been removed from viewcvs.conf. The functionality
|
("bin/svndbadmin", "bin/svndbadmin", 0755, 1, 0, 0),
|
||||||
went into the new files in subdirectory templates.""", 0),
|
("bin/make-database", "bin/make-database", 0755, 1, 0, 0),
|
||||||
("cvsgraph.conf.dist", "cvsgraph.conf", 0644, 0, 1, 0),
|
("viewvc.conf.dist", "viewvc.conf.dist", 0644, 0, 0, 0),
|
||||||
|
("viewvc.conf.dist", "viewvc.conf", 0644, 0, 1, 0),
|
||||||
("bin/loginfo-handler", "bin/loginfo-handler", 0755, 1, 0, 0),
|
("cvsgraph.conf.dist", "cvsgraph.conf.dist", 0644, 0, 0, 0),
|
||||||
("bin/cvsdbadmin", "bin/cvsdbadmin", 0755, 1, 0, 0),
|
("cvsgraph.conf.dist", "cvsgraph.conf", 0644, 0, 1, 0),
|
||||||
("bin/svndbadmin", "bin/svndbadmin", 0755, 1, 0, 0),
|
|
||||||
("bin/make-database", "bin/make-database", 0755, 1, 0, 0),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
FILE_INFO_LIST.extend([
|
FILE_INFO_LIST.extend([
|
||||||
("bin/asp/viewvc.asp", "bin/asp/viewvc.asp", 0755, 1, 0, 0),
|
("bin/asp/viewvc.asp", "bin/asp/viewvc.asp", 0755, 1, 0, 0),
|
||||||
("bin/asp/query.asp", "bin/asp/query.asp", 0755, 1, 0, 0),
|
("bin/asp/query.asp", "bin/asp/query.asp", 0755, 1, 0, 0),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
## List of directories for installation.
|
||||||
|
## type (source path,
|
||||||
|
## destination path,
|
||||||
|
## boolean -- prompt before replacing?)
|
||||||
TREE_LIST = [
|
TREE_LIST = [
|
||||||
("lib", "lib", 0),
|
("lib", "lib", 0),
|
||||||
("templates", "templates", 1),
|
("templates", "templates", 1),
|
||||||
]
|
("templates-contrib", "templates-contrib", 1),
|
||||||
|
]
|
||||||
|
|
||||||
# used to escape substitution strings passed to re.sub(). re.escape() is no
|
|
||||||
# good because it blindly puts backslashes in front of anything that is not
|
|
||||||
# a number or letter regardless of whether the resulting sequence will be
|
|
||||||
# interpreted.
|
|
||||||
def ReEscape(str):
|
|
||||||
return string.replace(str, "\\", "\\\\")
|
|
||||||
|
|
||||||
def Error(text, etype=None, evalue=None):
|
## List of file extensions we can't show diffs for.
|
||||||
print
|
BINARY_FILE_EXTS = [
|
||||||
print "[ERROR] %s" % text
|
'.png',
|
||||||
|
'.gif',
|
||||||
|
'.jpg',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _escape(str):
|
||||||
|
"""Callback function for re.sub().
|
||||||
|
|
||||||
|
re.escape() is no good because it blindly puts backslashes in
|
||||||
|
front of anything that is not a number or letter regardless of
|
||||||
|
whether the resulting sequence will be interpreted."""
|
||||||
|
return string.replace(str, "\\", "\\\\")
|
||||||
|
|
||||||
|
|
||||||
|
def _actual_src_path(path):
|
||||||
|
"""Return the real on-disk location of PATH, which is relative to
|
||||||
|
the ViewVC source directory."""
|
||||||
|
return os.path.join(os.path.dirname(sys.argv[0]),
|
||||||
|
string.replace(path, '/', os.sep))
|
||||||
|
|
||||||
|
|
||||||
|
def error(text, etype=None, evalue=None):
|
||||||
|
"""Print error TEXT to stderr, pretty printing the optional
|
||||||
|
exception type and value (ETYPE and EVALUE, respective), and then
|
||||||
|
exit the program with an errorful code."""
|
||||||
|
sys.stderr.write("\n[ERROR] %s\n" % (text))
|
||||||
if etype:
|
if etype:
|
||||||
print '[ERROR] ',
|
traceback.print_exception(etype, evalue, None, file=sys.stderr)
|
||||||
traceback.print_exception(etype, evalue, None, file=sys.stdout)
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def MkDir(path):
|
def replace_var(contents, var, value):
|
||||||
try:
|
"""Replace instances of the variable VAR as found in file CONTENTS
|
||||||
compat.makedirs(path)
|
with VALUE."""
|
||||||
except os.error, e:
|
|
||||||
if e[0] == 17:
|
|
||||||
# EEXIST: file exists
|
|
||||||
return
|
|
||||||
if e[0] == 13:
|
|
||||||
# EACCES: permission denied
|
|
||||||
Error("You do not have permission to create directory %s" % path)
|
|
||||||
Error("Unknown error creating directory %s" % path, OSError, e)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def SetOnePath(contents, var, value):
|
|
||||||
pattern = re.compile('^' + var + r'\s*=\s*.*$', re.MULTILINE)
|
pattern = re.compile('^' + var + r'\s*=\s*.*$', re.MULTILINE)
|
||||||
repl = '%s = r"%s"' % (var, os.path.join(ROOT_DIR, value))
|
repl = '%s = r"%s"' % (var, os.path.join(ROOT_DIR, value))
|
||||||
return re.sub(pattern, ReEscape(repl), contents)
|
return re.sub(pattern, _escape(repl), contents)
|
||||||
|
|
||||||
|
|
||||||
def SetPythonPaths(contents):
|
def replace_paths(contents):
|
||||||
|
"""Replace all ViewVC path placeholders found in file CONTENTS."""
|
||||||
if contents[:2] == '#!':
|
if contents[:2] == '#!':
|
||||||
shbang = '#!' + sys.executable
|
shbang = '#!' + sys.executable
|
||||||
contents = re.sub('^#![^\n]*', ReEscape(shbang), contents)
|
contents = re.sub('^#![^\n]*', _escape(shbang), contents)
|
||||||
contents = SetOnePath(contents, 'LIBRARY_DIR', 'lib')
|
contents = replace_var(contents, 'LIBRARY_DIR', 'lib')
|
||||||
contents = SetOnePath(contents, 'CONF_PATHNAME', 'viewvc.conf')
|
contents = replace_var(contents, 'CONF_PATHNAME', 'viewvc.conf')
|
||||||
return contents
|
return contents
|
||||||
|
|
||||||
|
|
||||||
def InstallFile(src_path, dest_path, mode, set_python_paths, prompt_replace,
|
def install_file(src_path, dst_path, mode, subst_path_vars,
|
||||||
compile_it):
|
prompt_replace, compile_it):
|
||||||
dest_path = os.path.join(ROOT_DIR, dest_path)
|
"""Install a single file whose source is at SRC_PATH (which is
|
||||||
|
relative to the ViewVC source directory) into the location
|
||||||
|
DST_PATH (which is relative both to the global ROOT_DIR and
|
||||||
|
DESTDIR settings), and set the file's MODE. If SUBST_PATH_VARS is
|
||||||
|
set, substitute path variables in the file's contents. If
|
||||||
|
PROMPT_REPLACE is set (and is not overridden by global setting
|
||||||
|
CLEAN_MODE), prompt the user for how to deal with already existing
|
||||||
|
files that differ from the to-be-installed version. If COMPILE_IT
|
||||||
|
is set, compile the file as a Python module."""
|
||||||
|
|
||||||
if prompt_replace and os.path.exists(DESTDIR + dest_path):
|
src_path = _actual_src_path(src_path)
|
||||||
# Collect ndiff output from ndiff
|
dst_path = os.path.join(ROOT_DIR, string.replace(dst_path, '/', os.sep))
|
||||||
sys.stdout = StringIO.StringIO()
|
destdir_path = DESTDIR + dst_path
|
||||||
compat_ndiff.main([DESTDIR + dest_path, src_path])
|
|
||||||
ndiff_output = sys.stdout.getvalue()
|
|
||||||
|
|
||||||
# Return everything to normal
|
overwrite = None
|
||||||
sys.stdout = sys.__stdout__
|
if not (prompt_replace and os.path.exists(destdir_path)):
|
||||||
|
# If the file doesn't already exist, or we've been instructed to
|
||||||
|
# replace it without prompting, then drop in the new file and get
|
||||||
|
# outta here.
|
||||||
|
overwrite = 1
|
||||||
|
else:
|
||||||
|
# If we're here, then the file already exists, and we've possibly
|
||||||
|
# got to prompt the user for what to do about that.
|
||||||
|
|
||||||
# Collect the '+ ' and '- ' lines
|
# Collect ndiff output from ndiff
|
||||||
# total collects the difference lines to be printed later
|
sys.stdout = StringIO.StringIO()
|
||||||
total = ""
|
compat_ndiff.main([destdir_path, src_path])
|
||||||
# I use flag to throw out match lines.
|
ndiff_output = sys.stdout.getvalue()
|
||||||
flag = 1
|
|
||||||
for line in string.split(ndiff_output,'\n'):
|
|
||||||
# Print line if it is a difference line
|
|
||||||
if line[:2] == "+ " or line[:2] == "- " or line[:2] == "? ":
|
|
||||||
total = total + line + "\n"
|
|
||||||
flag = 1
|
|
||||||
else:
|
|
||||||
# Compress lines that are the same to print one blank line
|
|
||||||
if flag:
|
|
||||||
total = total + "\n"
|
|
||||||
flag = 0
|
|
||||||
|
|
||||||
if total == "\n":
|
# Return everything to normal
|
||||||
print " File %s exists,\n but there is no difference between target and source files.\n" % (DESTDIR + dest_path)
|
sys.stdout = sys.__stdout__
|
||||||
return
|
|
||||||
|
|
||||||
if type(prompt_replace) == type(""):
|
# Collect the '+ ' and '- ' lines.
|
||||||
print prompt_replace
|
diff_lines = []
|
||||||
while 1:
|
looking_at_diff_lines = 0
|
||||||
temp = raw_input("""
|
for line in string.split(ndiff_output, '\n'):
|
||||||
File %s exists and is different from source file.
|
# Print line if it is a difference line
|
||||||
DO YOU WANT TO,
|
if line[:2] == "+ " or line[:2] == "- " or line[:2] == "? ":
|
||||||
overwrite [o]
|
diff_lines.append(line)
|
||||||
do not overwrite [d]
|
looking_at_diff_lines = 1
|
||||||
view differences [v]: """ % (DESTDIR + dest_path))
|
else:
|
||||||
print
|
# Compress lines that are the same to print one blank line
|
||||||
|
if looking_at_diff_lines:
|
||||||
|
diff_lines.append("")
|
||||||
|
looking_at_diff_lines = 0
|
||||||
|
|
||||||
temp = string.lower(temp[0])
|
# If there are no differences, we're done here.
|
||||||
|
if not diff_lines:
|
||||||
if temp == "d":
|
overwrite = 1
|
||||||
return
|
|
||||||
|
|
||||||
if temp == "v":
|
|
||||||
if string.lower(src_path[-4:]) in [ '.gif', '.png', '.jpg' ]:
|
|
||||||
print 'Can not print differences between binary files'
|
|
||||||
else:
|
else:
|
||||||
print total
|
# If we get here, there are differences.
|
||||||
print """
|
if CLEAN_MODE == 'true':
|
||||||
|
overwrite = 1
|
||||||
|
elif CLEAN_MODE == 'false':
|
||||||
|
overwrite = 0
|
||||||
|
else:
|
||||||
|
print "File %s exists and is different from source file." \
|
||||||
|
% (destdir_path)
|
||||||
|
while 1:
|
||||||
|
name, ext = os.path.splitext(src_path)
|
||||||
|
if ext in BINARY_FILE_EXTS:
|
||||||
|
temp = raw_input("Do you want to [O]verwrite or "
|
||||||
|
"[D]o not overwrite? ")
|
||||||
|
else:
|
||||||
|
temp = raw_input("Do you want to [O]verwrite, [D]o "
|
||||||
|
"not overwrite, or [V]iew "
|
||||||
|
"differences? ")
|
||||||
|
temp = string.lower(temp[0])
|
||||||
|
if temp == "v" and ext not in BINARY_FILE_EXTS:
|
||||||
|
print """
|
||||||
|
---------------------------------------------------------------------------"""
|
||||||
|
print string.join(diff_lines, '\n') + '\n'
|
||||||
|
print """
|
||||||
LEGEND
|
LEGEND
|
||||||
A leading '- ' indicates line to remove from installed file
|
A leading '- ' indicates line to remove from installed file
|
||||||
A leading '+ ' indicates line to add to installed file
|
A leading '+ ' indicates line to add to installed file
|
||||||
A leading '? ' shows intraline differences."""
|
A leading '? ' shows intraline differences.
|
||||||
|
---------------------------------------------------------------------------"""
|
||||||
|
elif temp == "d":
|
||||||
|
overwrite = 0
|
||||||
|
elif temp == "o":
|
||||||
|
overwrite = 1
|
||||||
|
|
||||||
if temp == "o":
|
if overwrite is not None:
|
||||||
ReplaceFile(src_path, dest_path, mode, set_python_paths,
|
break
|
||||||
prompt_replace, compile_it)
|
|
||||||
|
assert overwrite is not None
|
||||||
|
if not overwrite:
|
||||||
|
print " preserved %s" % (dst_path)
|
||||||
return
|
return
|
||||||
else:
|
|
||||||
ReplaceFile(src_path, dest_path, mode, set_python_paths,
|
|
||||||
prompt_replace, compile_it)
|
|
||||||
return
|
|
||||||
|
|
||||||
def ReplaceFile(src_path, dest_path, mode, set_python_paths,
|
### If we get here, we're creating or overwriting the existing file.
|
||||||
prompt_replace, compile_it):
|
|
||||||
try:
|
|
||||||
contents = open(src_path, "rb").read()
|
|
||||||
except IOError, e:
|
|
||||||
Error(str(e))
|
|
||||||
|
|
||||||
if set_python_paths:
|
# Read the source file's contents.
|
||||||
contents = SetPythonPaths(contents)
|
try:
|
||||||
|
contents = open(src_path, "rb").read()
|
||||||
|
except IOError, e:
|
||||||
|
error(str(e))
|
||||||
|
|
||||||
## write the file to the destination location
|
# (Optionally) substitute ViewVC path variables.
|
||||||
path, basename = os.path.split(DESTDIR + dest_path)
|
if subst_path_vars:
|
||||||
MkDir(path)
|
contents = replace_paths(contents)
|
||||||
|
|
||||||
try:
|
# Ensure the existence of the containing directories.
|
||||||
open(DESTDIR + dest_path, "wb").write(contents)
|
dst_parent = os.path.dirname(destdir_path)
|
||||||
except IOError, e:
|
if not os.path.exists(dst_parent):
|
||||||
if e[0] == 13:
|
try:
|
||||||
# EACCES: permission denied
|
compat.makedirs(dst_parent)
|
||||||
Error("You do not have permission to write file %s" % dest_path)
|
print " created %s%s" % (dst_parent, os.sep)
|
||||||
Error("Unknown error writing file %s" % dest_path, IOError, e)
|
except os.error, e:
|
||||||
|
if e.errno == 17: # EEXIST: file exists
|
||||||
|
return
|
||||||
|
if e.errno == 13: # EACCES: permission denied
|
||||||
|
error("You do not have permission to create directory %s" \
|
||||||
|
% (dst_parent))
|
||||||
|
error("Unknown error creating directory %s" \
|
||||||
|
% (dst_parent, OSError, e))
|
||||||
|
|
||||||
os.chmod(DESTDIR + dest_path, mode)
|
# Now, write the file contents to their destination.
|
||||||
|
try:
|
||||||
|
exists = os.path.exists(destdir_path)
|
||||||
|
open(destdir_path, "wb").write(contents)
|
||||||
|
print " %s %s" \
|
||||||
|
% (exists and 'replaced ' or 'installed', dst_path)
|
||||||
|
except IOError, e:
|
||||||
|
if e.errno == 13:
|
||||||
|
# EACCES: permission denied
|
||||||
|
error("You do not have permission to write file %s" % (dst_path))
|
||||||
|
error("Unknown error writing file %s" % (dst_path, IOError, e))
|
||||||
|
|
||||||
if compile_it:
|
# Set the files's mode.
|
||||||
py_compile.compile(DESTDIR + dest_path,
|
os.chmod(destdir_path, mode)
|
||||||
DESTDIR + dest_path + "c" , dest_path)
|
|
||||||
|
|
||||||
return
|
# (Optionally) compile the file.
|
||||||
|
if compile_it:
|
||||||
|
py_compile.compile(destdir_path, destdir_path + "c" , dst_path)
|
||||||
|
|
||||||
|
|
||||||
def install_tree(src_path, dst_path, prompt_replace):
|
def install_tree(src_path, dst_path, prompt_replace):
|
||||||
files = os.listdir(src_path)
|
"""Install a tree whose source is at SRC_PATH (which is relative
|
||||||
files.sort()
|
to the ViewVC source directory) into the location DST_PATH (which
|
||||||
for fname in files:
|
is relative both to the global ROOT_DIR and DESTDIR settings). If
|
||||||
# eliminate some items which appear in a development area
|
PROMPT_REPLACE is set (and is not overridden by global setting
|
||||||
if fname == 'CVS' or fname == '.svn' or fname == '_svn' \
|
CLEAN_MODE), prompt the user for how to deal with already existing
|
||||||
or fname[-4:] == '.pyc' or fname[-5:] == '.orig' \
|
files that differ from the to-be-installed version."""
|
||||||
or fname[-4:] == '.rej' or fname[0] == '.' \
|
|
||||||
or fname[-1] == '~':
|
|
||||||
continue
|
|
||||||
|
|
||||||
src = os.path.join(src_path, fname)
|
orig_src_path = src_path
|
||||||
dst = os.path.join(dst_path, fname)
|
orig_dst_path = dst_path
|
||||||
if os.path.isdir(src):
|
src_path = _actual_src_path(src_path)
|
||||||
install_tree(src, dst, prompt_replace)
|
dst_path = os.path.join(ROOT_DIR, string.replace(dst_path, '/', os.sep))
|
||||||
|
destdir_path = os.path.join(DESTDIR + dst_path)
|
||||||
|
|
||||||
|
# Get a list of items in the directory.
|
||||||
|
files = os.listdir(src_path)
|
||||||
|
files.sort()
|
||||||
|
for fname in files:
|
||||||
|
# Ignore some stuff found in development directories, but not
|
||||||
|
# intended for installation.
|
||||||
|
if fname == 'CVS' or fname == '.svn' or fname == '_svn' \
|
||||||
|
or fname[-4:] == '.pyc' or fname[-5:] == '.orig' \
|
||||||
|
or fname[-4:] == '.rej' or fname[0] == '.' \
|
||||||
|
or fname[-1] == '~':
|
||||||
|
continue
|
||||||
|
|
||||||
|
orig_src_child = orig_src_path + '/' + fname
|
||||||
|
orig_dst_child = orig_dst_path + '/' + fname
|
||||||
|
|
||||||
|
# If the item is a subdirectory, recurse. Otherwise, install the file.
|
||||||
|
if os.path.isdir(os.path.join(src_path, fname)):
|
||||||
|
install_tree(orig_src_child, orig_dst_child, prompt_replace)
|
||||||
|
else:
|
||||||
|
set_paths = 0
|
||||||
|
compile_it = fname[-3:] == '.py'
|
||||||
|
install_file(orig_src_child, orig_dst_child, 0644,
|
||||||
|
set_paths, prompt_replace, compile_it)
|
||||||
|
|
||||||
|
# Check for .py and .pyc files that don't belong in installation.
|
||||||
|
for fname in os.listdir(destdir_path):
|
||||||
|
if not os.path.isfile(os.path.join(destdir_path, fname)) or \
|
||||||
|
not ((fname[-3:] == '.py' and fname not in files) or
|
||||||
|
(fname[-4:] == '.pyc' and fname[:-1] not in files)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# If we get here, there's cruft.
|
||||||
|
delete = None
|
||||||
|
if CLEAN_MODE == 'true':
|
||||||
|
delete = 1
|
||||||
|
elif CLEAN_MODE == 'false':
|
||||||
|
delete = 0
|
||||||
|
else:
|
||||||
|
print "File %s does not belong in ViewVC %s." \
|
||||||
|
% (dst_path, version)
|
||||||
|
while 1:
|
||||||
|
temp = raw_input("Do you want to [D]elete it, or [L]eave "
|
||||||
|
"it as is? ")
|
||||||
|
temp = string.lower(temp[0])
|
||||||
|
if temp == "l":
|
||||||
|
delete = 0
|
||||||
|
elif temp == "d":
|
||||||
|
delete = 1
|
||||||
|
|
||||||
|
if delete is not None:
|
||||||
|
break
|
||||||
|
|
||||||
|
assert delete is not None
|
||||||
|
if delete:
|
||||||
|
print " deleted %s" % (os.path.join(dst_path, fname))
|
||||||
|
os.unlink(os.path.join(destdir_path, fname))
|
||||||
|
else:
|
||||||
|
print " preserved %s" % (os.path.join(dst_path, fname))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def usage_and_exit(errstr=None):
|
||||||
|
stream = errstr and sys.stderr or sys.stdout
|
||||||
|
stream.write("""Usage: %s [OPTIONS]
|
||||||
|
|
||||||
|
Installs the ViewVC web-based version control repository browser.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
--help, -h, -? Show this usage message and exit.
|
||||||
|
|
||||||
|
--prefix=DIR Install ViewVC into the directory DIR. If not provided,
|
||||||
|
the script will prompt for this information.
|
||||||
|
|
||||||
|
--destdir=DIR Use DIR as the DESTDIR. This is generally only used
|
||||||
|
by package maintainers. If not provided, the script will
|
||||||
|
prompt for this information.
|
||||||
|
|
||||||
|
--clean-mode= If 'true', overwrite existing ViewVC configuration files
|
||||||
|
found in the target directory, and purge Python modules
|
||||||
|
from the target directory that aren't part of the ViewVC
|
||||||
|
distribution. If 'false', do not overwrite configuration
|
||||||
|
files, and do not purge any files from the target
|
||||||
|
directory. If not specified, the script will prompt
|
||||||
|
for the appropriate action on a per-file basis.
|
||||||
|
|
||||||
|
""" % (os.path.basename(sys.argv[0])))
|
||||||
|
if errstr:
|
||||||
|
stream.write("ERROR: %s\n\n" % (errstr))
|
||||||
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
print " ", src
|
sys.exit(0)
|
||||||
set_paths = 0
|
|
||||||
compile_it = fname[-3:] == '.py'
|
|
||||||
InstallFile(src, dst, 0644, set_paths, prompt_replace, compile_it)
|
|
||||||
|
|
||||||
# prompt to delete all .py and .pyc files that don't belong in installation
|
|
||||||
full_dst_path = os.path.join(DESTDIR + ROOT_DIR, dst_path)
|
|
||||||
for fname in os.listdir(full_dst_path):
|
|
||||||
if not os.path.isfile(os.path.join(full_dst_path, fname)) or \
|
|
||||||
not ((fname[-3:] == '.py' and fname not in files) or
|
|
||||||
(fname[-4:] == '.pyc' and fname[:-1] not in files)):
|
|
||||||
continue
|
|
||||||
|
|
||||||
while 1:
|
|
||||||
temp = raw_input("""
|
|
||||||
File %s does not belong in ViewVC %s.
|
|
||||||
DO YOU WANT TO,
|
|
||||||
delete [d]
|
|
||||||
leave as is [l]: """ % (os.path.join(dst_path, fname), version))
|
|
||||||
print
|
|
||||||
|
|
||||||
temp = string.lower(temp[0])
|
|
||||||
|
|
||||||
if temp == "l":
|
|
||||||
break
|
|
||||||
|
|
||||||
if temp == "d":
|
|
||||||
os.unlink(os.path.join(full_dst_path, fname))
|
|
||||||
break
|
|
||||||
|
|
||||||
## MAIN
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# option parsing
|
# Option parsing.
|
||||||
try:
|
try:
|
||||||
optlist, args = getopt.getopt(sys.argv[1:], "", ['prefix=', 'destdir='])
|
optlist, args = getopt.getopt(sys.argv[1:], "h?",
|
||||||
|
['prefix=',
|
||||||
|
'destdir=',
|
||||||
|
'clean-mode=',
|
||||||
|
'help'])
|
||||||
except getopt.GetoptError, e:
|
except getopt.GetoptError, e:
|
||||||
Error("Invalid option", getopt.GetoptError, e)
|
usage_and_exit(str(e))
|
||||||
for opt, arg in optlist:
|
for opt, arg in optlist:
|
||||||
if opt == '--prefix':
|
if opt == '--help' or opt == '-h' or opt == '-?':
|
||||||
ROOT_DIR = arg
|
usage_and_exit()
|
||||||
if opt == '--destdir':
|
if opt == '--prefix':
|
||||||
DESTDIR = arg
|
ROOT_DIR = arg
|
||||||
|
if opt == '--destdir':
|
||||||
|
DESTDIR = arg
|
||||||
|
if opt == '--clean-mode':
|
||||||
|
arg = arg.lower()
|
||||||
|
if arg not in ('true', 'false'):
|
||||||
|
usage_and_exit("Invalid value for --overwrite parameter.")
|
||||||
|
CLEAN_MODE = arg
|
||||||
|
|
||||||
## print greeting
|
# Print the header greeting.
|
||||||
print INFO_TEXT
|
print """This is the ViewVC %s installer.
|
||||||
|
|
||||||
## prompt for ROOT_DIR if none provided
|
It will allow you to choose the install path for ViewVC. You will now
|
||||||
|
be asked some installation questions. Defaults are given in square brackets.
|
||||||
|
Just hit [Enter] if a default is okay.
|
||||||
|
""" % version
|
||||||
|
|
||||||
|
# Prompt for ROOT_DIR if none provided.
|
||||||
if ROOT_DIR is None:
|
if ROOT_DIR is None:
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
pf = os.getenv("ProgramFiles", "C:\\Program Files")
|
pf = os.getenv("ProgramFiles", "C:\\Program Files")
|
||||||
default = os.path.join(pf, "viewvc-" + version)
|
default = os.path.join(pf, "viewvc-" + version)
|
||||||
else:
|
else:
|
||||||
default = "/usr/local/viewvc-" + version
|
default = "/usr/local/viewvc-" + version
|
||||||
temp = string.strip(raw_input("Installation path [%s]: " % default))
|
temp = string.strip(raw_input("Installation path [%s]: " \
|
||||||
print
|
% default))
|
||||||
if len(temp):
|
print
|
||||||
ROOT_DIR = temp
|
if len(temp):
|
||||||
else:
|
ROOT_DIR = temp
|
||||||
ROOT_DIR = default
|
else:
|
||||||
|
ROOT_DIR = default
|
||||||
|
|
||||||
## prompt for DESTDIR if none provided
|
# Prompt for DESTDIR if none provided.
|
||||||
if DESTDIR is None:
|
if DESTDIR is None:
|
||||||
default = ''
|
default = ''
|
||||||
temp = string.strip(raw_input(
|
temp = string.strip(raw_input(
|
||||||
"DESTDIR path (generally, only package maintainers will need "
|
"DESTDIR path (generally only used by package "
|
||||||
"to change\nthis) [%s]: " % default))
|
"maintainers) [%s]: " \
|
||||||
print
|
% default))
|
||||||
if len(temp):
|
print
|
||||||
DESTDIR = temp
|
if len(temp):
|
||||||
else:
|
DESTDIR = temp
|
||||||
DESTDIR = default
|
else:
|
||||||
|
DESTDIR = default
|
||||||
## install the files
|
|
||||||
print "Installing ViewVC to:", ROOT_DIR,
|
|
||||||
if DESTDIR:
|
|
||||||
print "(DESTDIR = %s)" % (DESTDIR)
|
|
||||||
else:
|
|
||||||
print
|
|
||||||
|
|
||||||
|
# Install the files.
|
||||||
|
print "Installing ViewVC to %s%s:" \
|
||||||
|
% (ROOT_DIR, DESTDIR and " (DESTDIR = %s)" % (DESTDIR) or "")
|
||||||
for args in FILE_INFO_LIST:
|
for args in FILE_INFO_LIST:
|
||||||
print " ", args[0]
|
apply(install_file, args)
|
||||||
apply(InstallFile, args)
|
|
||||||
|
|
||||||
for args in TREE_LIST:
|
for args in TREE_LIST:
|
||||||
apply(install_tree, args)
|
apply(install_tree, args)
|
||||||
|
|
||||||
|
# Print some final thoughts.
|
||||||
print """
|
print """
|
||||||
|
|
||||||
ViewVC File Installation Complete
|
ViewVC file installation complete.
|
||||||
|
|
||||||
Consult INSTALL for detailed information to finish the installation
|
Consult the INSTALL document for detailed information on completing the
|
||||||
and configure ViewVC for your system.
|
installation and configuration of ViewVC on your system. Here's a brief
|
||||||
|
overview of the remaining steps:
|
||||||
|
|
||||||
Overview of remaining steps:
|
1) Edit the %s file.
|
||||||
|
|
||||||
1) Edit the %s file.
|
2) Either configure an existing web server to run
|
||||||
|
%s.
|
||||||
|
|
||||||
2) Configure an existing web server to run (or copy to cgi-bin)
|
Or, copy %s to an
|
||||||
%s.
|
already-configured cgi-bin directory.
|
||||||
OR
|
|
||||||
Run the web server that comes with ViewVC at
|
Or, use the standalone server provided by this distribution at
|
||||||
%s.
|
%s.
|
||||||
""" % (
|
""" % (os.path.join(ROOT_DIR, 'viewvc.conf'),
|
||||||
os.path.join(ROOT_DIR, 'viewvc.conf'),
|
os.path.join(ROOT_DIR, 'bin', 'cgi', 'viewvc.cgi'),
|
||||||
os.path.join(ROOT_DIR, 'bin', 'cgi', 'viewvc.cgi'),
|
os.path.join(ROOT_DIR, 'bin', 'cgi', 'viewvc.cgi'),
|
||||||
os.path.join(ROOT_DIR, 'standalone.py'))
|
os.path.join(ROOT_DIR, 'bin', 'standalone.py'))
|
||||||
|
@@ -142,8 +142,9 @@ use_rcsparse = 0
|
|||||||
address = <a href="mailto:cvs-admin@insert.your.domain.here">No admin address has been configured</a>
|
address = <a href="mailto:cvs-admin@insert.your.domain.here">No admin address has been configured</a>
|
||||||
|
|
||||||
#
|
#
|
||||||
# This should contain a list of modules in the repository that should not be
|
# This should contain a list of modules (that is, top-level directories within
|
||||||
# displayed (by default or by explicit path specification).
|
# repositories) that should not be displayed (by default or by explicit path
|
||||||
|
# specification).
|
||||||
#
|
#
|
||||||
# This configuration can be a simple list of modules, or it can get quite
|
# This configuration can be a simple list of modules, or it can get quite
|
||||||
# complex:
|
# complex:
|
||||||
@@ -162,8 +163,9 @@ address = <a href="mailto:cvs-admin@insert.your.domain.here">No admin address ha
|
|||||||
#
|
#
|
||||||
# Tests are case-sensitive.
|
# Tests are case-sensitive.
|
||||||
#
|
#
|
||||||
forbidden =
|
# NOTE: This is for the hiding of modules within repositories, *not*
|
||||||
|
# for the hiding of repositories (roots) themselves.
|
||||||
|
#
|
||||||
# Some examples:
|
# Some examples:
|
||||||
#
|
#
|
||||||
# Disallow "example" but allow all others:
|
# Disallow "example" but allow all others:
|
||||||
@@ -184,6 +186,40 @@ forbidden =
|
|||||||
# Allow "xml", forbid other modules starting with "x", and allow the rest:
|
# Allow "xml", forbid other modules starting with "x", and allow the rest:
|
||||||
# forbidden = !xml, x*, !*
|
# forbidden = !xml, x*, !*
|
||||||
#
|
#
|
||||||
|
forbidden =
|
||||||
|
|
||||||
|
#
|
||||||
|
# This is similar to 'forbidden', but differs in some key ways:
|
||||||
|
#
|
||||||
|
# *) Rather than shell-style "glob" expressions, the values in this
|
||||||
|
# list are regular expressions. You can still prepend a ! character
|
||||||
|
# to each regular expression to invert its meaning, though.
|
||||||
|
#
|
||||||
|
# *) It compares not against modules only, but against paths consisting
|
||||||
|
# of the repository (or root) name plus the path of the versioned file
|
||||||
|
# or directory to be tested. For example, to see if the user is
|
||||||
|
# authorized to see the path "/trunk/www/index.html" in the repository
|
||||||
|
# whose root name is "svnrepos", this authorizer will test the path
|
||||||
|
# "svnrepos/trunk/www/index.html" against the list of forbidden regular
|
||||||
|
# expressions. Directory paths will be terminated by a forward slash.
|
||||||
|
#
|
||||||
|
# NOTE: Use of this configuration option will *disable* any configuration of
|
||||||
|
# the 'forbidden' option -- they cannot be used simultaneously.
|
||||||
|
#
|
||||||
|
# Some examples:
|
||||||
|
#
|
||||||
|
# Disallow files named "PRIVATE", but allow all others:
|
||||||
|
# forbiddenre = /PRIVATE$
|
||||||
|
#
|
||||||
|
# Allow only the "example1" and "example2" roots and the paths inside them,
|
||||||
|
# disallowing all others (which can be done in multiple ways):
|
||||||
|
# forbiddenre = !^example1(/|$), !^example2(/|$)/
|
||||||
|
# forbiddenre = !^example[12](/|$)
|
||||||
|
#
|
||||||
|
# Only allow visibility of HTML files and the directories that hold them:
|
||||||
|
# forbiddenre = !^.*(/|\.html)$
|
||||||
|
#
|
||||||
|
forbiddenre =
|
||||||
|
|
||||||
#
|
#
|
||||||
# This option provides a mechanism for custom key/value pairs to be
|
# This option provides a mechanism for custom key/value pairs to be
|
||||||
@@ -359,6 +395,9 @@ hr_ignore_keyword_subst = 1
|
|||||||
#
|
#
|
||||||
hr_intraline = 0
|
hr_intraline = 0
|
||||||
|
|
||||||
|
# allow on-the-fly generation of repository tarballs
|
||||||
|
allow_tar = 0
|
||||||
|
|
||||||
# allow annotation of files.
|
# allow annotation of files.
|
||||||
allow_annotate = 1
|
allow_annotate = 1
|
||||||
|
|
||||||
@@ -467,12 +506,6 @@ php_exe_path = php
|
|||||||
# php_exe_path = /usr/local/bin/php
|
# php_exe_path = /usr/local/bin/php
|
||||||
# php_exe_path = C:\Program Files\php\cli\php.exe
|
# php_exe_path = C:\Program Files\php\cli\php.exe
|
||||||
|
|
||||||
#
|
|
||||||
# ViewVC can generate tarball from a repository on the fly.
|
|
||||||
#
|
|
||||||
allow_tar = 0
|
|
||||||
# allow_tar = 1
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Use CvsGraph. See http://www.akhphd.au.dk/~bertho/cvsgraph/ for
|
# Use CvsGraph. See http://www.akhphd.au.dk/~bertho/cvsgraph/ for
|
||||||
# documentation and download.
|
# documentation and download.
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
<a href="./download.html">Download</a> |
|
<a href="./download.html">Download</a> |
|
||||||
<a href="./upgrading.html">Upgrading</a> |
|
<a href="./upgrading.html">Upgrading</a> |
|
||||||
<a href="./contributing.html">Contributing</a> |
|
<a href="./contributing.html">Contributing</a> |
|
||||||
<a href="./license-1.html">License</a> |
|
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
|
||||||
<a href="./contact.html">Contact</a> |
|
<a href="./contact.html">Contact</a> |
|
||||||
<a href="./who.html">About</a>
|
<a href="./who.html">About</a>
|
||||||
</p>
|
</p>
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
<a href="./download.html">Download</a> |
|
<a href="./download.html">Download</a> |
|
||||||
<a href="./upgrading.html">Upgrading</a> |
|
<a href="./upgrading.html">Upgrading</a> |
|
||||||
<a href="./contributing.html">Contributing</a> |
|
<a href="./contributing.html">Contributing</a> |
|
||||||
<a href="./license-1.html">License</a> |
|
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
|
||||||
<a href="./contact.html">Contact</a> |
|
<a href="./contact.html">Contact</a> |
|
||||||
<a href="./who.html">About</a>
|
<a href="./who.html">About</a>
|
||||||
</p>
|
</p>
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
<a href="./download.html">Download</a> |
|
<a href="./download.html">Download</a> |
|
||||||
<a href="./upgrading.html">Upgrading</a> |
|
<a href="./upgrading.html">Upgrading</a> |
|
||||||
<a href="./contributing.html">Contributing</a> |
|
<a href="./contributing.html">Contributing</a> |
|
||||||
<a href="./license-1.html">License</a> |
|
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
|
||||||
<a href="./contact.html">Contact</a> |
|
<a href="./contact.html">Contact</a> |
|
||||||
<a href="./who.html">About</a>
|
<a href="./who.html">About</a>
|
||||||
</p>
|
</p>
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
<a href="./download.html">Download</a> |
|
<a href="./download.html">Download</a> |
|
||||||
<a href="./upgrading.html">Upgrading</a> |
|
<a href="./upgrading.html">Upgrading</a> |
|
||||||
<a href="./contributing.html">Contributing</a> |
|
<a href="./contributing.html">Contributing</a> |
|
||||||
<a href="./license-1.html">License</a> |
|
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
|
||||||
<a href="./contact.html">Contact</a> |
|
<a href="./contact.html">Contact</a> |
|
||||||
<a href="./who.html">About</a>
|
<a href="./who.html">About</a>
|
||||||
</p>
|
</p>
|
||||||
|
95
viewvc.org/nightly/build-viewvc-snapshot
Executable file
95
viewvc.org/nightly/build-viewvc-snapshot
Executable file
@@ -0,0 +1,95 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
DATE=`date +"%Y%m%d"`
|
||||||
|
EXPORTDIR="viewvc-${DATE}"
|
||||||
|
PUBLISH_DIR=/www/viewvc/nightly
|
||||||
|
|
||||||
|
# export HEAD of trunk
|
||||||
|
svn export --quiet http://viewvc.tigris.org/svn/viewvc/trunk ${EXPORTDIR} --username guest --password ""
|
||||||
|
|
||||||
|
# use Python to determine the version number, reading it from
|
||||||
|
# viewvc.__version__
|
||||||
|
cd $EXPORTDIR/lib
|
||||||
|
VERSION=`python -c "import viewvc; print viewvc.__version__"`
|
||||||
|
TARGET=viewvc-${VERSION}-${DATE}
|
||||||
|
|
||||||
|
# make a release
|
||||||
|
cd ../tools
|
||||||
|
./make-release ${TARGET}
|
||||||
|
|
||||||
|
# remove results of last build
|
||||||
|
rm -rf ${PUBLISH_DIR}/viewvc*.tar.gz ${PUBLISH_DIR}/viewvc*.zip ${PUBLISH_DIR}/index.html
|
||||||
|
|
||||||
|
# publish new build
|
||||||
|
mv ${TARGET}.* ${PUBLISH_DIR}
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
cat > ${PUBLISH_DIR}/index.html <<EOF
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>ViewVC: Nightly Snapshots</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="../styles.css"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="title">
|
||||||
|
<a href="http://www.viewvc.org/"><img
|
||||||
|
src="../images/title.jpg" alt="ViewVC: Repository Browsing"/></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="menu">
|
||||||
|
<p><a href="../index.html">Home</a> |
|
||||||
|
<a href="http://viewvc.tigris.org/">Project Page</a> |
|
||||||
|
<a href="../download.html">Download</a> |
|
||||||
|
<a href="../contributing.html">Contributing</a> |
|
||||||
|
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
|
||||||
|
<a href="../contact.html">Contact</a> |
|
||||||
|
<a href="../who.html">About</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table id="pagetable">
|
||||||
|
<tr>
|
||||||
|
<td id="pagecolumn1">
|
||||||
|
|
||||||
|
<h4>On this page:</h4>
|
||||||
|
|
||||||
|
<ul id="bookmarks">
|
||||||
|
<li><a href="#sec-snapshots">Snapshots</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p><a href="http://validator.w3.org/check?uri=referer"><img
|
||||||
|
src="http://www.w3.org/Icons/valid-xhtml10"
|
||||||
|
alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td id="pagecolumn2">
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
|
||||||
|
<h2 id="sec-snapshots">Snapshots</h2>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "<li><a href=\"${TARGET}.tar.gz\">${TARGET}.tar.gz</a></li>" >> ${PUBLISH_DIR}/index.html
|
||||||
|
echo "<li><a href=\"${TARGET}.zip\">${TARGET}.zip</a></li>" >> ${PUBLISH_DIR}/index.html
|
||||||
|
|
||||||
|
cat >> ${PUBLISH_DIR}/index.html <<EOF
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# more cleanup
|
||||||
|
rm -rf ${EXPORTDIR}
|
@@ -18,7 +18,7 @@
|
|||||||
<a href="./download.html">Download</a> |
|
<a href="./download.html">Download</a> |
|
||||||
<a href="./upgrading.html">Upgrading</a> |
|
<a href="./upgrading.html">Upgrading</a> |
|
||||||
<a href="./contributing.html">Contributing</a> |
|
<a href="./contributing.html">Contributing</a> |
|
||||||
<a href="./license-1.html">License</a> |
|
<a href="http://viewvc.tigris.org/nonav/source/browse/*checkout*/viewvc/trunk/LICENSE.html">License</a> |
|
||||||
<a href="./contact.html">Contact</a> |
|
<a href="./contact.html">Contact</a> |
|
||||||
<a href="./who.html">About</a>
|
<a href="./who.html">About</a>
|
||||||
</p>
|
</p>
|
||||||
|
@@ -14,7 +14,7 @@ ViewVC requires the Python interpreter which you can download from
|
|||||||
|
|
||||||
and the Python for Windows Extensions which are at
|
and the Python for Windows Extensions which are at
|
||||||
|
|
||||||
http://starship.python.net/crew/mhammond/win32/
|
http://sourceforge.net/projects/pywin32/
|
||||||
|
|
||||||
For CVS support, ViewVC also requires that the CVSNT client (cvs.exe) OR the
|
For CVS support, ViewVC also requires that the CVSNT client (cvs.exe) OR the
|
||||||
RCS tools (rlog.exe, rcsdiff.exe, and co.exe) be installed on your computer.
|
RCS tools (rlog.exe, rcsdiff.exe, and co.exe) be installed on your computer.
|
||||||
@@ -404,7 +404,7 @@ KNOWN ISSUES
|
|||||||
page would always return nothing (leaving the screen blank). There were a
|
page would always return nothing (leaving the screen blank). There were a
|
||||||
number of workarounds for this problem, but the fix is to download and
|
number of workarounds for this problem, but the fix is to download and
|
||||||
install the latest python win32 extensions from
|
install the latest python win32 extensions from
|
||||||
http://starship.python.net/crew/mhammond/win32/Downloads.html
|
http://sourceforge.net/projects/pywin32/
|
||||||
|
|
||||||
- ViewVC can't convert timestamps on diff pages to local time when it is used
|
- ViewVC can't convert timestamps on diff pages to local time when it is used
|
||||||
with CVSNT. This is caused by a CVSNT bug, which is described at
|
with CVSNT. This is caused by a CVSNT bug, which is described at
|
||||||
|
@@ -1,70 +0,0 @@
|
|||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>ViewVC - Version Control Repository Browser</title>
|
|
||||||
<!-- Custom stylations to hide the obnoxious project info -->
|
|
||||||
<style type="text/css">
|
|
||||||
#projecthome .axial { display: none; }
|
|
||||||
#apphead h1 { display: none; }
|
|
||||||
#longdescription { border: none; }
|
|
||||||
#longdescription h2 { display: none; }
|
|
||||||
#customcontent h2 { display: block; }
|
|
||||||
</style>
|
|
||||||
<!-- End custom stylations -->
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="app" id="customcontent">
|
|
||||||
|
|
||||||
<h1>ViewVC — Web-based Version Control Repository Browsing</h1>
|
|
||||||
|
|
||||||
<div class="h2">
|
|
||||||
<h2>Latest Release</h2>
|
|
||||||
|
|
||||||
<p>The most recent release of ViewVC is: <strong>1.0.0-rc1</strong></p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="h2">
|
|
||||||
<h2>What Is ViewVC?</h2>
|
|
||||||
|
|
||||||
<p>ViewVC is a browser interface for CVS and Subversion version
|
|
||||||
control repositories. It generates templatized HTML to present
|
|
||||||
navigable directory, revision, and change log listings. It can
|
|
||||||
display specific versions of files as well as diffs between those
|
|
||||||
versions. Basically, ViewVC provides the bulk of the report-like
|
|
||||||
functionality you expect out of your version control tool, but much
|
|
||||||
more prettily than the average textual command-line program
|
|
||||||
output.</p>
|
|
||||||
|
|
||||||
<p>Here are some of the additional features of ViewVC:</p>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>Support for filesystem-accessible CVS and Subversion repositories</li>
|
|
||||||
<li>Individually configurable virtual host support</li>
|
|
||||||
<li>Line-based annotation/blame display</li>
|
|
||||||
<li>Revision graph capabilities (<em>CVS only</em>)</li>
|
|
||||||
<li>Syntax highlighting support</li>
|
|
||||||
<li>Commit metadata query facilities</li>
|
|
||||||
<li>Template-driven output generation</li>
|
|
||||||
<li>Colorized, side-by-side differences</li>
|
|
||||||
<li>Tarball generation (by tag/branch for CVS, by revision for
|
|
||||||
Subversion)</li>
|
|
||||||
<li>Localization support based on the Accept-Language request header</li>
|
|
||||||
<li>Ability to run either as CGI script or as a standalone
|
|
||||||
server</li>
|
|
||||||
<li>Regexp-based file searching</li>
|
|
||||||
<li>INI-like configuration file (as opposed to requiring actual code
|
|
||||||
tweaks)</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p>For a complete list of changes present in each release, see
|
|
||||||
ViewVC's <a
|
|
||||||
href="http://viewvc.tigris.org/source/browse/viewvc/trunk/CHANGES?rev=HEAD"
|
|
||||||
>CHANGES</a> file.</p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@@ -1,30 +0,0 @@
|
|||||||
<!-- Overrides the left-nav tool bar on viewvc.tigris.org, a feature
|
|
||||||
specific to tigris.org's CEE branding. See www/overrides/ in the
|
|
||||||
look.tigris.org project for details. -->
|
|
||||||
|
|
||||||
<!-- dd --><ul>
|
|
||||||
|
|
||||||
<li><a href="http://www.viewvc.org/"
|
|
||||||
>Project website</a></li>
|
|
||||||
|
|
||||||
<!-- #################################################### --></ul></dd><dd><ul>
|
|
||||||
|
|
||||||
<li><a href="http://viewvc.tigris.org/servlets/ProjectMemberList"
|
|
||||||
>Membership</a></li>
|
|
||||||
|
|
||||||
<!-- #################################################### --></ul></dd><dd><ul>
|
|
||||||
|
|
||||||
<li><a href="http://viewvc.tigris.org/servlets/ProjectNewsList"
|
|
||||||
>Announcements</a></li>
|
|
||||||
<!-- li><a href="http://viewvc.tigris.org/servlets/ProjectForumView"
|
|
||||||
>Discussion forums</a></li -->
|
|
||||||
<li><a href="http://viewvc.tigris.org/servlets/ProjectMailingListList"
|
|
||||||
>Mailing lists</a></li>
|
|
||||||
<li><a href="http://viewvc.tigris.org/servlets/ProjectDocumentList"
|
|
||||||
>Documents & files</a></li>
|
|
||||||
<li><a href="http://viewvc.tigris.org/source/browse/viewvc/"
|
|
||||||
>Subversion</a></li>
|
|
||||||
<li><a href="http://viewvc.tigris.org/issues/buglist.cgi?component=viewvc&issue_status=UNCONFIRMED&issue_status=NEW&issue_status=STARTED&issue_status=REOPENED"
|
|
||||||
>Issue tracker</a></li>
|
|
||||||
|
|
||||||
</ul><!-- /dd -->
|
|
Reference in New Issue
Block a user