mirror of
https://github.com/vitalif/viewvc-4intranet
synced 2019-04-16 04:14:59 +03:00
Compare commits
53 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8b0bcade3b | ||
![]() |
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 | ||
![]() |
504ca48e0f | ||
![]() |
0c0ec82ca2 | ||
![]() |
58a237b14c | ||
![]() |
24d1a691dc | ||
![]() |
51011abec8 | ||
![]() |
0bbe7f5751 | ||
![]() |
1df169ab24 | ||
![]() |
98e7612420 | ||
![]() |
70b0076d88 | ||
![]() |
e23b88d389 | ||
![]() |
336ee6b95d | ||
![]() |
642130cf93 | ||
![]() |
1e3c57f2e0 | ||
![]() |
9693f2b981 | ||
![]() |
69ab22922f | ||
![]() |
d422c3abc9 | ||
![]() |
ded0015f5c | ||
![]() |
6232555f79 | ||
![]() |
81fa4ce6fd | ||
![]() |
98378e7560 | ||
![]() |
8da2897df2 | ||
![]() |
45bca25fbc | ||
![]() |
a54b3bcbbe | ||
![]() |
2612b3bd0a | ||
![]() |
588f19cb0b | ||
![]() |
6d540deba2 | ||
![]() |
9f612d3b29 |
137
CHANGES
137
CHANGES
@@ -1,53 +1,91 @@
|
||||
Version 1.0 (not yet released)
|
||||
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)
|
||||
|
||||
* fix bug in path shown for Subversion deleted-under-copy items (issue #265)
|
||||
* security fix: declare charset for views to avoid IE UTF7 XSS attack
|
||||
|
||||
Version 1.0.2 (released 29-Sep-2006)
|
||||
|
||||
* minor documentation fixes
|
||||
* fix Subversion annotate functionality on Windows (issue #18)
|
||||
* fix annotate assertions on uncanonicalized #include paths (issue #208)
|
||||
* make RSS URL method match the method used to generate it (issue #245)
|
||||
* fix Subversion annotation to run non-interactively, preventing hangs
|
||||
* fix bug in custom syntax highlighter fallback logic
|
||||
* fix bug in PHP CGI hack to avoid force-cgi-redirect errors
|
||||
|
||||
Version 1.0.1 (released 20-Jul-2006)
|
||||
|
||||
* fix exception on log page when use_pagesize is enabled
|
||||
* fix an XHTML validation bug in the footer template (issue #239)
|
||||
* fix handling of single-component CVS revision numbers (issue #237)
|
||||
* fix bug in download-as-text URL link generation (issue #241)
|
||||
* fix query.cgi bug, missing 'rss_href' template data item (issue #249)
|
||||
* no longer omit empty Subversion directories from tarballs (issue #250)
|
||||
* use actual modification time for Subversion directories in tarballs
|
||||
|
||||
Version 1.0 (released 01-May-2006)
|
||||
|
||||
* add support for viewing Subversion repositories
|
||||
* add support for running on MS Windows (2003-Feb-09)
|
||||
* generate strict XHTML output (2005-Sep-08)
|
||||
* add support for running on MS Windows
|
||||
* generate strict XHTML output
|
||||
* add support for caching by sending "Last-Modified", "Expires",
|
||||
"ETag", and "Cache-Control" headers (2004-Jun-03)
|
||||
"ETag", and "Cache-Control" headers
|
||||
* add support for Mod_Python on Apache 2.x and ASP on IIS
|
||||
* Several changes to standalone.py:
|
||||
- -h commandline option to specify hostname for non local use.
|
||||
- -r commandline option may be repeated to use more than repository
|
||||
before actually installing ViewCVS.
|
||||
- New GUI field to test paging.
|
||||
* add new, better-integrated query interface (2004-Jul-17)
|
||||
* add integrated RSS feeds (2005-Dec-22)
|
||||
* add new, better-integrated query interface
|
||||
* add integrated RSS feeds
|
||||
* add new "root_as_url_component" option to embed root names as
|
||||
path components in ViewCVS URLs for a more natural URL scheme
|
||||
in ViewCVS configurations with multiple repositories.
|
||||
(2002-Dec-12)
|
||||
* add new "use_localtime" option to display local times instead of
|
||||
UTC times (2002-May-06)
|
||||
* add new "use_localtime" option to display local times instead of UTC times
|
||||
* add new "root_parents" option to make it possible to add and
|
||||
remove repositories without modifying the ViewCVS configuration
|
||||
(2004-Jul-16)
|
||||
* add new "template_dir" option to facilitate switching between
|
||||
sets of templates (2005-Feb-08)
|
||||
* add new "template_dir" option to facilitate switching between sets of
|
||||
templates
|
||||
* add new "sort_group_dirs" option to disable grouping of
|
||||
directories in directory listings (2005-Mar-07)
|
||||
* add new "port" option to connect to a MySQL database on a nonstandard
|
||||
port (2005-Dec-22)
|
||||
directories in directory listings
|
||||
* add new "port" option to connect to a MySQL database on a nonstandard port
|
||||
* make "default_root" option optional. When no root is specified,
|
||||
show a page listing all available repositories (2005-Feb-04)
|
||||
show a page listing all available repositories
|
||||
* add "default_file_view" option to make it possible for relative
|
||||
links and image paths in checked out HTML files to work without
|
||||
the need for special /*checkout*/ prefixes in URLs. Deprecate
|
||||
"checkout_magic" option and disable by default (2006-Apr-03)
|
||||
"checkout_magic" option and disable by default
|
||||
* add "limit_changes" option to limit number of changed files shown per
|
||||
commit by default in query results and in the Subversion revision view
|
||||
* hide CVS "Attic" directories and add simple toggle for showing
|
||||
dead files in directory listings (2004-Jul-31)
|
||||
dead files in directory listings
|
||||
* show Unified, Context and Side-by-side diffs in HTML instead of
|
||||
in bare text pages (2004-Jun-22)
|
||||
in bare text pages
|
||||
* make View/Download links work the same for all file types
|
||||
(2004-Jan-21)
|
||||
* add links to tip of selected branch on log page (2005-Oct-03)
|
||||
* allow use of "Highlight" program for colorizing (2005-Dec-20)
|
||||
* add links to tip of selected branch on log page
|
||||
* allow use of "Highlight" program for colorizing
|
||||
* enable enscript colorizing for more file types
|
||||
* add sorting arrows for directory views (2004-Jul-21)
|
||||
* get rid of popup windows for checkout links (2004-Jan-21)
|
||||
* add sorting arrows for directory views
|
||||
* get rid of popup windows for checkout links
|
||||
* obfuscate email addresses in html output by encoding @ symbol
|
||||
with an HTML character reference (2004-Jul-29)
|
||||
* add paging capability (2001-Dec-31)
|
||||
with an HTML character reference
|
||||
* add paging capability
|
||||
* Improvements to templates
|
||||
- add new template authoring guide
|
||||
- increase coverage, use templates to produce HTML for diff pages,
|
||||
@@ -56,41 +94,36 @@ Version 1.0 (not yet released)
|
||||
- add new template variables providing ViewCVS URLs for more
|
||||
links between related pages and less URL generation inside
|
||||
templates
|
||||
* add new [define] EZT directive for assigning variables within
|
||||
templates (2004-Apr-21)
|
||||
* add new [define] EZT directive for assigning variables within templates
|
||||
* add command line argument parsing to install script to allow
|
||||
non-interactive installs (2005-Jan-06)
|
||||
* add stricter parameter validation to lower likelihood of CSS
|
||||
vulnerabilities (2002-May-24)
|
||||
non-interactive installs
|
||||
* add stricter parameter validation to lower likelihood of cross-site
|
||||
scripting vulnerabilities
|
||||
* add support for cvsweb's "mime_type=text/x-cvsweb-markup" URLs
|
||||
(2002-Oct-10)
|
||||
* fix incompatibility with enscript 1.6.3 (2002-Feb-05)
|
||||
* fix bug in parsing FreeBSD rlog output (2003-Jul-24)
|
||||
* fix incompatibility with enscript 1.6.3
|
||||
* fix bug in parsing FreeBSD rlog output
|
||||
* work around rlog assumption all two digit years in RCS files are
|
||||
relative to the year 1900. (2005-Sep-30)
|
||||
relative to the year 1900.
|
||||
* change loginfo-handler to cope with spaces in filenames and
|
||||
support a simpler command line invocation from CVS (2003-Feb-11)
|
||||
* show diff error when comparing two binary files (2002-Jan-23)
|
||||
* make regular expression search skip binary files (2002-Jan-17)
|
||||
support a simpler command line invocation from CVS
|
||||
* make cvsdbadmin work properly when invoked on CVS subdirectory
|
||||
paths instead of top-level CVS root paths
|
||||
* show diff error when comparing two binary files
|
||||
* make regular expression search skip binary files
|
||||
* make regular expression search skip nonversioned files in CVS
|
||||
directories instead of choking on them (2002-Sep-27)
|
||||
directories instead of choking on them
|
||||
* fix tarball generator so it doesn't include forbidden modules
|
||||
(2002-Feb-22)
|
||||
* output "404 Not Found" errors instead of "403 Forbidden" errors
|
||||
to not reveal whether forbidden paths exist (2005-May-17)
|
||||
* fix sorting bug in directory view (2002-Apr-18)
|
||||
to not reveal whether forbidden paths exist
|
||||
* fix sorting bug in directory view
|
||||
* reset log and directory page numbers when leaving those pages
|
||||
(2005-Jan-29)
|
||||
* reset sort direction in directory listing when clicking new
|
||||
columns (2004-Jul-21)
|
||||
* fix "Accept-Language" handling for Netscape 4.x browsers
|
||||
(2002-May-23)
|
||||
* fix file descriptor leak in standalone server (2004-Jul-17)
|
||||
* clean up zombie processes from running enscript (2002-Jun-15)
|
||||
* fix mysql "Too many connections" error in cvsdbadmin (2003-Jul-24)
|
||||
* get rid of mxDateTime dependency for query database (2003-Feb-09)
|
||||
* reset sort direction in directory listing when clicking new columns
|
||||
* fix "Accept-Language" handling for Netscape 4.x browsers
|
||||
* fix file descriptor leak in standalone server
|
||||
* clean up zombie processes from running enscript
|
||||
* fix mysql "Too many connections" error in cvsdbadmin
|
||||
* get rid of mxDateTime dependency for query database
|
||||
* store query database times in UTC instead of local time
|
||||
(2003-Feb-09)
|
||||
* fix daylight saving time bugs in various parts of the code
|
||||
|
||||
Version 0.9.4 (released 17-Aug-2005)
|
||||
|
460
INSTALL
460
INSTALL
@@ -1,11 +1,12 @@
|
||||
CONTENTS
|
||||
--------
|
||||
TO THE IMPATIENT
|
||||
SECURITY INFORMATION
|
||||
INSTALLING VIEWVC
|
||||
APACHE CONFIGURATION
|
||||
UPGRADING VIEWVC
|
||||
SQL CHECKIN DATABASE
|
||||
ENSCRIPT CONFIGURATION
|
||||
ENABLING SYNTAX COLORATION
|
||||
CVSGRAPH CONFIGURATION
|
||||
IF YOU HAVE PROBLEMS...
|
||||
|
||||
@@ -39,7 +40,7 @@ Congratulations on getting this far. :-)
|
||||
|
||||
* a web server capable of running CGI programs
|
||||
(for example, Apache at http://httpd.apache.org/)
|
||||
* MySQL to create and query a commit database
|
||||
* MySQL 3.22 and MySQLdb 0.9.0 or later to create a commit database
|
||||
(http://www.mysql.com/)
|
||||
(http://sourceforge.net/projects/mysql-python)
|
||||
* Enscript, code colorizer
|
||||
@@ -50,55 +51,76 @@ Congratulations on getting this far. :-)
|
||||
* CvsGraph 1.5.0 or later, graphical CVS revision tree generator
|
||||
(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
|
||||
ViewVC, type "./standalone.py -g -r /PATH/TO/CVS/ROOT". This
|
||||
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.
|
||||
If you just want to see what your repository looks like when seen
|
||||
through ViewVC, type:
|
||||
|
||||
$ 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:
|
||||
|
||||
To start installing right away (on UNIX): type "./viewvc-install"
|
||||
in the current directory and answer the prompts. When it
|
||||
finishes, edit the file viewvc.conf in the installation directory
|
||||
to tell viewvc the paths to your CVS and Subversion repositories. Next,
|
||||
configure your web server to run <INSTALL>/cgi/viewvc.cgi, as
|
||||
appropriate for your web server. The section `INSTALLING VIEWVC'
|
||||
below is still recommended reading.
|
||||
to tell ViewVC the paths to your CVS and Subversion repositories.
|
||||
Next, configure your web server (in the way appropriate to that browser)
|
||||
to run <VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/viewvc.cgi. The section
|
||||
`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
|
||||
------------------
|
||||
|
||||
1) To get viewvc.cgi to work, make sure that you have Python 1.5 or
|
||||
greater installed and a webserver which is capable of executing
|
||||
CGI scripts (either based on the .cgi extension, or by placing the
|
||||
script within a specific directory).
|
||||
NOTE: Windows users can refer to windows/README for Windows-specific
|
||||
installation instructions.
|
||||
|
||||
You need to have RCS installed. Specifically, "rlog", "rcsdiff",
|
||||
and "co". This script was tested against RedHat's rcs-5.7-10.rpm
|
||||
Someone running HP-UX reported problems, that rcs doesn'nt honour
|
||||
the setting of $PATH to find the diff utility. Please test, if
|
||||
"rcsdiff -u" works. If not you might have to recompile RCS.
|
||||
1) To get viewvc.cgi to work, make sure that you have Python installed
|
||||
and a webserver which is capable of executing CGI scripts (either
|
||||
based on the .cgi extension, or by placing the script within a specific
|
||||
directory).
|
||||
|
||||
Note, that the viewvc.cgi script needs to have READ-ONLY, physical
|
||||
access to the CVS repository (or a copy of it). Therefore, rsh/ssh or
|
||||
pserver access to the repository will not work.
|
||||
|
||||
For the more human readable diff formats you need a modern diff utility.
|
||||
If you are using Linux, this is no problem. But on commercial unices
|
||||
you might want to install GNU-diff to be able to use unified or
|
||||
side-by-side diffs.
|
||||
|
||||
If you want to use cvsgraph, you have to obtain and install this
|
||||
separately. See below. This was tested with cvsgraph-1.5.1.
|
||||
|
||||
For the checkin database to work, you will need MySQL >= 3.22,
|
||||
and the Python DBAPI 2.0 module, MySQLdb. This was tested with
|
||||
MySQLdb 0.9.1.
|
||||
Note that to browse CVS repositories, the viewvc.cgi script needs to
|
||||
have READ-ONLY, physical access to the repository (or a copy of it).
|
||||
Therefore, rsh/ssh or pserver access to the repository will not work.
|
||||
And you need to have the RCS utilities installed, specifically "rlog",
|
||||
"rcsdiff", and "co".
|
||||
|
||||
2) Installation is handled by the ./viewvc-install script. Run this
|
||||
script and you will be prompted for a installation root path.
|
||||
@@ -120,8 +142,11 @@ INSTALLING VIEWVC
|
||||
3) Edit <VIEWVC_INSTALLATION_DIRECTORY>/viewvc.conf for your specific
|
||||
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
|
||||
root_as_url_component
|
||||
rcs_path
|
||||
mime_types_file
|
||||
|
||||
@@ -131,55 +156,57 @@ INSTALLING VIEWVC
|
||||
then edit the files in <VIEWVC_INSTALLATION_DIRECTORY>/templates.
|
||||
You need knowledge about HTML to edit the templates.
|
||||
|
||||
4) The CGI programs are in <VIEWVC_INSTALLATION_DIRECTORY>/www/cgi/. You can
|
||||
4) The CGI programs are in <VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/. You can
|
||||
symlink to this directory from somewhere in your published HTTP server
|
||||
path if your webserver is configured to follow symbolic links. You can
|
||||
also copy the installed <VIEWVC_INSTALLATION_DIRECTORY>/www/cgi/*.cgi
|
||||
also copy the installed <VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/*.cgi
|
||||
scripts after the install (unlike the other files in ViewVC, the scripts
|
||||
under www/ can be moved).
|
||||
under bin/ can be moved).
|
||||
|
||||
If you are using Apache, then see below at the section
|
||||
titled APACHE CONFIGURATION.
|
||||
If you are using Apache, then see below at the section titled
|
||||
APACHE CONFIGURATION.
|
||||
|
||||
NOTE: for security reasons, it is not advisable to install ViewVC
|
||||
directly into your published HTTP directory tree (due to the MySQL
|
||||
passwords in viewvc.conf).
|
||||
|
||||
5) That's it for repository browsing. Instructions for getting the
|
||||
SQL checkin database working are below.
|
||||
That's it for repository browsing. Instructions for getting the SQL
|
||||
checkin database working are below.
|
||||
|
||||
|
||||
APACHE CONFIGURATION
|
||||
--------------------
|
||||
|
||||
1) Find out where the web server configuration file is kept. On
|
||||
SuSE Linux it is /etc/httpd/httpd.conf, On RedHat Linux 7.3 it
|
||||
is /etc/httpd/conf/httpd.conf. On other unices you may look
|
||||
at /usr/local/etc or /etc/local. Use the vendor documentation
|
||||
or the find utility, if in doubt.
|
||||
1) Find out where the web server configuration file is kept. Typical
|
||||
locations are /etc/httpd/httpd.conf, /etc/httpd/conf/httpd.conf,
|
||||
and /etc/apache/httpd.conf. Depending on how apache was installed,
|
||||
you may also look under /usr/local/etc or /etc/local. Use the vendor
|
||||
documentation or the find utility if in doubt.
|
||||
|
||||
Either METHOD A:
|
||||
2) The ScriptAlias directive is very useful for pointing
|
||||
directly to the viewvc.cgi script. Simply insert a line containing
|
||||
ScriptAlias /viewvc <VIEWVC_INSTALLATION_DIRECTORY>/www/cgi/viewvc.cgi
|
||||
|
||||
ScriptAlias /viewvc <VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/viewvc.cgi
|
||||
|
||||
into your httpd.conf file. Choose the location in httpd.conf where
|
||||
also the other ScriptAlias lines reside. Some examples:
|
||||
|
||||
ScriptAlias /viewvc /usr/local/viewvc-1.0/www/cgi/viewvc.cgi
|
||||
ScriptAlias /query /usr/local/viewvc-1.0/www/cgi/query.cgi
|
||||
ScriptAlias /viewvc /usr/local/viewvc-1.0/bin/cgi/viewvc.cgi
|
||||
ScriptAlias /query /usr/local/viewvc-1.0/bin/cgi/query.cgi
|
||||
|
||||
continue with step 3).
|
||||
|
||||
or alternatively METHOD B:
|
||||
2) Copy the CGI scripts from
|
||||
<VIEWVC_INSTALLATION_DIRECTORY>/www/cgi/*.cgi
|
||||
<VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/*.cgi
|
||||
to the /cgi-bin/ directory configured in your httpd.conf file.
|
||||
|
||||
continue with step 3).
|
||||
|
||||
and then there's METHOD C:
|
||||
2) Copy the CGI scripts from
|
||||
<VIEWVC_INSTALLATION_DIRECTORY>/www/cgi/*.cgi
|
||||
<VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/*.cgi
|
||||
to the directory of your choosing in the Document Root adding the following
|
||||
apache directives for the directory in httpd.conf or an .htaccess file:
|
||||
|
||||
@@ -194,7 +221,7 @@ and then there's METHOD C:
|
||||
|
||||
or if you've got Mod_Python installed you can use METHOD D:
|
||||
2) Copy the Python scripts and .htaccess file from
|
||||
<VIEWVC_INSTALLATION_DIRECTORY>/www/mod_python/
|
||||
<VIEWVC_INSTALLATION_DIRECTORY>/bin/mod_python/
|
||||
to a directory being served by apache.
|
||||
|
||||
In httpd.conf, make sure that "AllowOverride All" or at least
|
||||
@@ -207,39 +234,37 @@ or if you've got Mod_Python installed you can use METHOD D:
|
||||
|
||||
continue with step 3).
|
||||
|
||||
3) Restart apache. On SuSE Linux this is done using the command
|
||||
rcapache restart and on RedHat Linux this is done using the command
|
||||
/sbin/service httpd restart
|
||||
3) Restart apache. The commands to do this vary. "httpd -k restart" and
|
||||
"apache -k restart" are two common variants. On RedHat Linux it is
|
||||
done using the command "/sbin/service httpd restart" and on SuSE Linux
|
||||
it is done with "rcapache restart"
|
||||
|
||||
4) Optional: adding access control:
|
||||
4) Optional: Add access control.
|
||||
|
||||
In your httpd.conf you can control access to certain modules by adding
|
||||
directives like this:
|
||||
|
||||
<Location "<url to viewvc.cgi>/<modname_you_wish_to_access_ctl>">
|
||||
AllowOverride None
|
||||
AuthUserFile /path/to/passwd/file
|
||||
AuthName "Client Access"
|
||||
AuthType Basic
|
||||
require valid-user
|
||||
</Location>
|
||||
<Location "<url to viewvc.cgi>/<modname_you_wish_to_access_ctl>">
|
||||
AllowOverride None
|
||||
AuthUserFile /path/to/passwd/file
|
||||
AuthName "Client Access"
|
||||
AuthType Basic
|
||||
require valid-user
|
||||
</Location>
|
||||
|
||||
This idea is courtesy to Nick Bauman from http://www.cortexity.com/
|
||||
WARNING: If you enable the "checkout_magic" or "allow_tar" options, you
|
||||
will need to add additional location directives to prevent people
|
||||
from sneaking in with URLs like:
|
||||
|
||||
If you use access control, it is recommended that you disable the
|
||||
checkout_magic option so people can't "sneak in" with URLs like
|
||||
|
||||
http://<server_name>/viewvc/*checkout*/<module_name>
|
||||
http://<server_name>/viewvc/~checkout~/<module_name>
|
||||
|
||||
and get access to seeing the file contents.
|
||||
http://<server_name>/viewvc/*checkout*/<module_name>
|
||||
http://<server_name>/viewvc/~checkout~/<module_name>
|
||||
http://<server_name>/viewvc/<module_name>.tar.gz?view=tar
|
||||
|
||||
|
||||
UPGRADING VIEWVC
|
||||
-----------------
|
||||
|
||||
Please read the file upgrading.html in the website subdirectory or
|
||||
at <http://viewvc.org/upgrading.html>.
|
||||
Please read the file upgrading-howto.html in the docs/ subdirectory.
|
||||
|
||||
|
||||
SQL CHECKIN DATABASE
|
||||
@@ -252,15 +277,13 @@ database. In fact, the databases are 100% compatible.
|
||||
Various queries can be performed on the database. After installing ViewVC,
|
||||
there are some additional steps required to get the database working.
|
||||
|
||||
1) You need MySQL >= 3.22, and the Python module MySQLdb 0.9.0 installed.
|
||||
Python 1.5.2 is REQUIRED by MySQLdb, therefore to use this part of
|
||||
ViewVC you must be using Python 1.5.2.
|
||||
1) You need MySQL and MySQLdb (a Python DBAPI 2.0 module) installed.
|
||||
|
||||
2) You need to create a MySQL user who has permission to create databases.
|
||||
Optionally, you can create a second user with read-only access to the
|
||||
database.
|
||||
|
||||
3) Run the <VIEWVC_INSTALLATION_DIRECTORY>/make-database script. It will
|
||||
3) Run the <VIEWVC_INSTALLATION_DIRECTORY>/bin/make-database script. It will
|
||||
prompt you for your MySQL user, password, and the name of database you
|
||||
want to create. The database name defaults to "ViewVC". This script
|
||||
creates the database and sets up the empty tables. If you run this on a
|
||||
@@ -272,105 +295,105 @@ there are some additional steps required to get the database working.
|
||||
enabled = 1 # Whether to enable query support in viewvc.cgi
|
||||
host = # MySQL database server host
|
||||
port = # MySQL database server port (default is 3306)
|
||||
database_name = # the name of the database you created with
|
||||
# make-database
|
||||
user = # the read/write database user
|
||||
database_name = # name of database you created with make-database
|
||||
user = # read/write database user
|
||||
passwd = # password for read/write database user
|
||||
readonly_user = # the readonly database user -- it's pretty
|
||||
# safe to use the read/write user here
|
||||
readonly_passwd = # password for the readonly user
|
||||
readonly_user = # read-only database user
|
||||
readonly_passwd = # password for the read-only user
|
||||
|
||||
5) 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.
|
||||
Note that it's pretty safe in this instance for your read-only user
|
||||
and your read-write user to be the same.
|
||||
|
||||
To build a database of all the commits in the CVS repository /home/cvs,
|
||||
invoke: "./cvsdbadmin rebuild /home/cvs". If you want to update
|
||||
the checkin database, invoke: "./cvsdbadmin 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.
|
||||
5) At this point, you need to tell your version control system(s) to
|
||||
publish their commit information to the database. This is done
|
||||
using utilities that ViewVC provides.
|
||||
|
||||
To get real-time updates, you'll want to checkout the CVSROOT module
|
||||
from your CVS repository and edit CVSROOT/loginfo. Add the line:
|
||||
To publish CVS commits into the database:
|
||||
|
||||
ALL <VIEWVC_INSTALLATION_DIRECTORY>/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 make sure to change any running under the "DEFAULT" keyword to
|
||||
"ALL" like the loginfo handler, and probably carefully read the
|
||||
execution rules for CVSROOT/loginfo from the CVS manual.
|
||||
To build a database of all the commits in the CVS repository
|
||||
/home/cvs, invoke: "./cvsdbadmin rebuild /home/cvs". If you
|
||||
want to update the checkin database, invoke: "./cvsdbadmin
|
||||
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
|
||||
slightly different command line:
|
||||
To get real-time updates, you'll want to checkout the CVSROOT
|
||||
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 %p %{sVv}
|
||||
|
||||
If you are running CVS 1.11 or earlier, you'll want a slightly
|
||||
different command line in CVSROOT/loginfo:
|
||||
|
||||
ALL <VIEWVC_INSTALLATION_DIRECTORY>/bin/loginfo-handler %{sVv}
|
||||
|
||||
ALL <VIEWVC_INSTALLATION_DIRECTORY>/loginfo-handler %{sVv} cvsnt
|
||||
|
||||
The extra 'cvsnt' parameter tells the handler script to parse the
|
||||
commit information in a different way.
|
||||
If you have other scripts invoked by CVSROOT/loginfo, you will
|
||||
want to make sure to change any running under the "DEFAULT"
|
||||
keyword to "ALL" like the loginfo handler, and probably
|
||||
carefully read the execution rules for CVSROOT/loginfo from the
|
||||
CVS manual.
|
||||
|
||||
If you are running the Unix port of CVS-NT, the handler script
|
||||
need to know about it. CVS-NT delivers commit information to
|
||||
loginfo scripts differently than the way mainstream CVS does.
|
||||
Your command line should look like this:
|
||||
|
||||
ALL <VIEWVC_INSTALLATION_DIRECTORY>/bin/loginfo-handler %{sVv} cvsnt
|
||||
|
||||
For Subversion repositories, there is a single script called
|
||||
svndbadmin that performs both of the above tasks.
|
||||
To publish Subversion commits into the database:
|
||||
|
||||
To build a database of all the commits in the Subversion repository
|
||||
/home/svn, invoke: "./svndbadmin rebuild /home/svn". If you want
|
||||
to update the checkin database, invoke: "./svndbadmin update
|
||||
/home/svn".
|
||||
To build a database of all the commits in the Subversion
|
||||
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:
|
||||
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>/svndbadmin rebuild "$REPOS" "$REV"
|
||||
#!/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.
|
||||
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.
|
||||
|
||||
6) You may want to modify the HTML template file:
|
||||
|
||||
<VIEWVC_INSTALLATION_DIRECTORY>/templates/query.ezt
|
||||
|
||||
This is used by the query.cgi script to generate part of its HTML output.
|
||||
At some point the currently hardcoded table output will also vanish.
|
||||
|
||||
7) You should be ready to go. Load up the query.cgi script and give
|
||||
it a try.
|
||||
You should be ready to go. Click one of the "Query revision history"
|
||||
links in ViewVC directory listings and give it a try.
|
||||
|
||||
|
||||
ENSCRIPT CONFIGURATION
|
||||
----------------------
|
||||
ENABLING SYNTAX COLORATION
|
||||
--------------------------
|
||||
|
||||
Enscript is program that can colorize sourcecode of a lot of languages.
|
||||
Linux distributions like for example SuSE Linux from at least 7.0
|
||||
up to the recently released 7.3 already contain a precompiled and
|
||||
configured enscript 1.6.2 package.
|
||||
Enscript and Highlight are two programs that can colorize source code
|
||||
for a lot of languages. ViewVC can be configured to use either one.
|
||||
|
||||
1) Download genscript from http://www.codento.com/people/mtr/genscript/
|
||||
1) Install Enscript or Highlight using your system's package manager
|
||||
or downloading from the project home pages.
|
||||
|
||||
2) Configure and compile per instructions with enscript.
|
||||
(I 've not done this, since I'm using the precompiled package
|
||||
delivered with SuSE Linux)
|
||||
2) Set either the 'use_enscript' or 'use_highlight' options in
|
||||
viewvc.conf to 1.
|
||||
|
||||
3) Set the 'use_enscript' option in viewvc.conf to 1.
|
||||
3) You may also need to set 'enscript_path' or 'highlight_path' option
|
||||
if the executables are not located on the system PATH.
|
||||
|
||||
4) That's it!
|
||||
|
||||
5) If you want to colorize exotic languages, you might have to
|
||||
patch 'lib/viewvc.py' and add a new highlighting file to enscript.
|
||||
For example, if you are interested in a patch for Modula-2 support,
|
||||
send mail to pefu@sourceforge.net.
|
||||
That's it! Now when you view the contents of recognized filetypes in
|
||||
ViewVC, you should see colorized syntax.
|
||||
|
||||
|
||||
CVSGRAPH CONFIGURATION
|
||||
@@ -380,71 +403,69 @@ CvsGraph is a program that can display a clickable, graphical tree
|
||||
of files in a CVS repository.
|
||||
|
||||
WARNING: Under certain circumstances (many revisions of a file
|
||||
or many branches or both) cvsgraph can generate very huge images.
|
||||
or many branches or both) CvsGraph can generate very huge images.
|
||||
Especially on thin clients these images may crash the Web-Browser.
|
||||
Currently there is no known way to avoid this behavior of cvsgraph.
|
||||
Currently there is no known way to avoid this behavior of CvsGraph.
|
||||
So you have been warned!
|
||||
|
||||
Nevertheless cvsgraph can be quite helpful on repositories with
|
||||
Nevertheless, CvsGraph can be quite helpful on repositories with
|
||||
a reasonable number of revisions and branches.
|
||||
|
||||
1) Install viewvc according to instructions in 'INSTALLING
|
||||
VIEWVC' section above. The installation directory is where
|
||||
the 'viewvc-install' script copied and configured the viewvc
|
||||
programs.
|
||||
1) Install CvsGraph using your system's package manager or downloading
|
||||
from the project home page.
|
||||
|
||||
2) Download CvsGraph from http://www.akhphd.au.dk/~bertho/cvsgraph/
|
||||
2) Set the 'use_cvsgraph' options in viewvc.conf to 1.
|
||||
|
||||
3) Configure and compile per instructions with CvsGraph. I had
|
||||
problems with 'configure' finding the gd library. Had to create
|
||||
a link from libgd.so to libgd.do.4.0.0. On Solaris you might
|
||||
want to edit the link command line and add the option -R if
|
||||
you have you libraries at non-standard location.
|
||||
3) You may also need to set the 'cvsgraph_path' option if the
|
||||
CvsGraph executable is not located on the system PATH.
|
||||
|
||||
4) Place the 'cvsgraph' executable into a directory readable by the
|
||||
userid running the web server. (default is '/usr/local/bin' if
|
||||
you simply type 'make install' in the cvsgraph directory).
|
||||
|
||||
5) Check the setting of the 'cvsgraph_path' option in viewvc.conf:
|
||||
/usr/local/bin/ is most often NOT contained in $PATH of the
|
||||
webserver process (e.g. Apache), so you will have to edit this.
|
||||
Set the 'use_cvsgraph' option in viewvc.conf to 1.
|
||||
|
||||
6) That's it!
|
||||
|
||||
7) 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.
|
||||
See the cvsgraph.conf documentation. No edits are required in
|
||||
cvsgraph.conf for operation with viewvc.
|
||||
cvsgraph.conf for operation with ViewVC.
|
||||
|
||||
|
||||
SUBVERSION INTEGRATION
|
||||
----------------------
|
||||
|
||||
ViewVC now supports browsing of Subversion repositories. To use
|
||||
ViewVC with Subversion, make sure you have both Subversion itself and
|
||||
the Subversion Python bindings installed. See Subversion's
|
||||
installation notes for more details on how to build and install these
|
||||
items.
|
||||
Unlike the CVS integration, which simply wraps the RCS and CVS utility
|
||||
programs, the Subversion integration requires additional Python
|
||||
libraries. To use ViewVC with Subversion, make sure you have both
|
||||
Subversion itself and the Subversion Python bindings installed. See
|
||||
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
|
||||
bindings has been successful if you can import the 'svn.repos' module
|
||||
from within your Python interpreter:
|
||||
bindings has been successful if you can import the 'svn.core' module
|
||||
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 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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
way as with CVS repositories, only with the 'svn_roots' configuration
|
||||
variable instead of the 'cvs_roots' one.
|
||||
@@ -453,39 +474,42 @@ variable instead of the 'cvs_roots' one.
|
||||
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
|
||||
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
|
||||
* 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 (Typical
|
||||
error: you can't see any files)
|
||||
|
||||
o check whether the CGI-script has read-permissions to your
|
||||
CVS-Repository. The CGI-script often runs as the user 'nobody'
|
||||
or 'httpd' ..
|
||||
* Check whether the CGI-script has read-permissions to your
|
||||
CVS-Repository. The CGI-script generally runs as the same user
|
||||
that the web server does, often user 'nobody' or 'httpd'.
|
||||
|
||||
o does viewvc find your RCS utilities? (edit rcs_path)
|
||||
* 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:
|
||||
|
||||
o check the ViewVC home page:
|
||||
* Check the ViewVC home page:
|
||||
|
||||
http://viewvc.org/
|
||||
http://viewvc.org/
|
||||
|
||||
o review the ViewVC mailing list archive to see if somebody else had
|
||||
the same problem, and it was solved:
|
||||
* Review the ViewVC mailing list archive to see if somebody else had
|
||||
the same problem, and it was solved:
|
||||
|
||||
http://viewvc.tigris.org/servlets/SummarizeList?listName=users
|
||||
http://viewvc.tigris.org/servlets/SummarizeList?listName=users
|
||||
|
||||
o send mail to the ViewVC mailing list: users@viewvc.tigris.org
|
||||
* Check the ViewVC issue database to see if the problem you are
|
||||
seeing is the result of a known bug:
|
||||
|
||||
NOTE: make sure you provide an accurate description of the problem
|
||||
and any relevant tracebacks or error logs.
|
||||
http://viewvc.tigris.org/issues/query.cgi
|
||||
|
||||
* Send mail to the ViewVC mailing list, users@viewvc.tigris.org.
|
||||
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.
|
||||
|
@@ -32,9 +32,9 @@ import sys
|
||||
import os
|
||||
|
||||
if LIBRARY_DIR:
|
||||
sys.path.insert(0, LIBRARY_DIR)
|
||||
sys.path.insert(0, LIBRARY_DIR)
|
||||
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")))
|
||||
|
||||
#########################################################################
|
||||
|
||||
@@ -49,9 +49,13 @@ import vclib.bincvs
|
||||
DEBUG_FLAG = 0
|
||||
|
||||
## output functions
|
||||
def debug(text):
|
||||
def debug(text):
|
||||
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):
|
||||
print 'WARNING(viewvc-loginfo):', text
|
||||
@@ -61,139 +65,158 @@ def error(text):
|
||||
sys.exit(1)
|
||||
|
||||
_re_revisions = re.compile(
|
||||
r",(?P<old>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)" # comma and first revision number
|
||||
r",(?P<new>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)" # comma and second revision number
|
||||
r"(?:$| )" # space or end of string
|
||||
r",(?P<old>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)" # comma and first revision
|
||||
r",(?P<new>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)" # comma and second revision
|
||||
r"(?:$| )" # space or end of string
|
||||
)
|
||||
|
||||
def HeuristicArgParse(s, repository):
|
||||
"""Current 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"""
|
||||
|
||||
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)
|
||||
def Cvs1Dot12ArgParse(args):
|
||||
"""CVS 1.12 introduced a new loginfo format while provides the various
|
||||
pieces of interesting version information to the handler script as
|
||||
individual arguments instead of as a single string."""
|
||||
|
||||
if args[1] == '- New directory':
|
||||
return None, None
|
||||
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')
|
||||
directory = args.pop(0)
|
||||
files = []
|
||||
while len(args) >= 3:
|
||||
files.append(args[0:3])
|
||||
args = args[3:]
|
||||
return directory, files
|
||||
|
||||
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"""
|
||||
|
||||
file_data_list.append((filename, old_version, new_version))
|
||||
if (s[-16:] == ' - New directory'
|
||||
or s[:26] == ' - New directory,NONE,NONE'):
|
||||
return None, None
|
||||
|
||||
debug('File "%s", old revision %s, new revision %s'
|
||||
% (filename, old_version, new_version))
|
||||
if (s[-19:] == ' - Imported sources'
|
||||
or s[-29:] == ' - Imported sources,NONE,NONE'):
|
||||
return None, None
|
||||
|
||||
start = m.end()
|
||||
|
||||
if start == len(s): break
|
||||
file_data_list = []
|
||||
start = 0
|
||||
|
||||
return directory, file_data_list
|
||||
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)
|
||||
|
||||
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):
|
||||
"""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
|
||||
possible directory name that actually exists"""
|
||||
"""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
|
||||
possible directory name that actually exists"""
|
||||
|
||||
parts = string.split(s, " ")
|
||||
|
||||
for i in range(len(parts)-1, 0, -1):
|
||||
directory = string.join(parts[:i])
|
||||
filename = string.join(parts[i:])
|
||||
if os.path.isdir(os.path.join(repository, directory)):
|
||||
return directory, filename
|
||||
|
||||
return None, None
|
||||
parts = string.split(s, " ")
|
||||
|
||||
for i in range(len(parts)-1, 0, -1):
|
||||
directory = string.join(parts[:i])
|
||||
filename = string.join(parts[i:])
|
||||
if os.path.isdir(os.path.join(repository, directory)):
|
||||
return directory, filename
|
||||
|
||||
return None, None
|
||||
|
||||
_re_cvsnt_revisions = re.compile(
|
||||
r"(?P<filename>.*)" # comma and first revision number
|
||||
r",(?P<old>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)" # comma and first revision number
|
||||
r",(?P<new>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)" # comma and second revision number
|
||||
r"$" # end of string
|
||||
r"(?P<filename>.*)" # comma and first revision
|
||||
r",(?P<old>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)" # comma and first revision
|
||||
r",(?P<new>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)" # comma and second revision
|
||||
r"$" # end of string
|
||||
)
|
||||
|
||||
def CvsNtArgParse(s, repository):
|
||||
"""CVSNT escapes all spaces in filenames and directory names with
|
||||
backslashes"""
|
||||
"""CVSNT escapes all spaces in filenames and directory names with
|
||||
backslashes"""
|
||||
|
||||
if s[-18:] == r' -\ New\ directory':
|
||||
return None, None
|
||||
if s[-18:] == r' -\ New\ directory':
|
||||
return None, None
|
||||
|
||||
if s[-21:] == r' -\ Imported\ sources':
|
||||
return None, None
|
||||
if s[-21:] == r' -\ Imported\ sources':
|
||||
return None, None
|
||||
|
||||
file_data_list = []
|
||||
file_data_list = []
|
||||
directory, pos = NextFile(s)
|
||||
|
||||
debug('Directory name is "%s"' % directory)
|
||||
|
||||
directory, pos = NextFile(s)
|
||||
|
||||
debug('Directory name is "%s"' % directory)
|
||||
while 1:
|
||||
fileinfo, pos = NextFile(s, pos)
|
||||
if fileinfo is None:
|
||||
break
|
||||
|
||||
while 1:
|
||||
fileinfo, pos = NextFile(s, pos)
|
||||
if fileinfo is None:
|
||||
break
|
||||
m = _re_cvsnt_revisions.match(fileinfo)
|
||||
if m is None:
|
||||
warning('Can\'t parse file information in "%s"' % fileinfo)
|
||||
continue
|
||||
|
||||
m = _re_cvsnt_revisions.match(fileinfo)
|
||||
if m is None:
|
||||
warning('Can\'t parse file information in "%s"' % fileinfo)
|
||||
continue
|
||||
file_data = m.group('filename', 'old', 'new')
|
||||
file_data_list.append(file_data)
|
||||
|
||||
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)
|
||||
|
||||
file_data_list.append(file_data)
|
||||
|
||||
return directory, file_data_list
|
||||
return directory, file_data_list
|
||||
|
||||
def NextFile(s, pos = 0):
|
||||
escaped = 0
|
||||
ret = ''
|
||||
i = pos
|
||||
while i < len(s):
|
||||
c = s[i]
|
||||
if escaped:
|
||||
ret += c
|
||||
escaped = 0
|
||||
elif c == '\\':
|
||||
escaped = 1
|
||||
elif c == ' ':
|
||||
return ret, i + 1
|
||||
else:
|
||||
ret += c
|
||||
i += 1
|
||||
escaped = 0
|
||||
ret = ''
|
||||
i = pos
|
||||
while i < len(s):
|
||||
c = s[i]
|
||||
if escaped:
|
||||
ret += c
|
||||
escaped = 0
|
||||
elif c == '\\':
|
||||
escaped = 1
|
||||
elif c == ' ':
|
||||
return ret, i + 1
|
||||
else:
|
||||
ret += c
|
||||
i += 1
|
||||
|
||||
return ret or None, i
|
||||
return ret or None, i
|
||||
|
||||
def ProcessLoginfo(rootpath, directory, files):
|
||||
cfg = viewvc.load_config(CONF_PATHNAME)
|
||||
@@ -235,41 +258,58 @@ if __name__ == '__main__':
|
||||
debug('Repository name is "%s"' % repository)
|
||||
|
||||
## 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:
|
||||
# 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 neccessarily correct
|
||||
if sys.platform == "win32":
|
||||
fun = CvsNtArgParse
|
||||
else:
|
||||
fun = HeuristicArgParse
|
||||
argc = len(sys.argv)
|
||||
debug('Got %d arguments:' % (argc))
|
||||
debug(map(lambda x: ' ' + x, sys.argv))
|
||||
|
||||
if len(sys.argv) > 3:
|
||||
error('Bad arguments')
|
||||
# if we have more than 3 arguments, we are likely using the
|
||||
# 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)
|
||||
directory, files = fun(arg, repository)
|
||||
|
||||
debug('Repository: %s' % (repository))
|
||||
debug('Directory: %s' % (directory))
|
||||
debug('Files: %s' % (str(files)))
|
||||
|
||||
if files is None:
|
||||
debug('Not a checkin, nothing to do')
|
||||
debug('Not a checkin, nothing to do')
|
||||
else:
|
||||
ProcessLoginfo(repository, directory, files)
|
||||
ProcessLoginfo(repository, directory, files)
|
||||
|
||||
sys.exit(0)
|
||||
|
@@ -39,7 +39,7 @@ CREATE TABLE branches (
|
||||
branch varchar(64) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE branch (branch)
|
||||
);
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS checkins;
|
||||
CREATE TABLE checkins (
|
||||
@@ -62,7 +62,7 @@ CREATE TABLE checkins (
|
||||
KEY dirid (dirid),
|
||||
KEY fileid (fileid),
|
||||
KEY branchid (branchid)
|
||||
);
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS descs;
|
||||
CREATE TABLE descs (
|
||||
@@ -71,7 +71,7 @@ CREATE TABLE descs (
|
||||
hash bigint(20) DEFAULT '0' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY hash (hash)
|
||||
);
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS dirs;
|
||||
CREATE TABLE dirs (
|
||||
@@ -79,7 +79,7 @@ CREATE TABLE dirs (
|
||||
dir varchar(255) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE dir (dir)
|
||||
);
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS files;
|
||||
CREATE TABLE files (
|
||||
@@ -87,7 +87,7 @@ CREATE TABLE files (
|
||||
file varchar(255) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE file (file)
|
||||
);
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS people;
|
||||
CREATE TABLE people (
|
||||
@@ -95,7 +95,7 @@ CREATE TABLE people (
|
||||
who varchar(32) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE who (who)
|
||||
);
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS repositories;
|
||||
CREATE TABLE repositories (
|
||||
@@ -103,7 +103,7 @@ CREATE TABLE repositories (
|
||||
repository varchar(64) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE repository (repository)
|
||||
);
|
||||
) TYPE=MyISAM;
|
||||
|
||||
DROP TABLE IF EXISTS tags;
|
||||
CREATE TABLE tags (
|
||||
@@ -117,7 +117,7 @@ CREATE TABLE tags (
|
||||
KEY dirid (dirid),
|
||||
KEY fileid (fileid),
|
||||
KEY branchid (branchid)
|
||||
);
|
||||
) TYPE=MyISAM;
|
||||
"""
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@@ -51,16 +51,12 @@ keywordsList = ["and", "assert", "break", "class", "continue", "def",
|
||||
"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.
|
||||
commentPat = "#.*"
|
||||
|
||||
# A regexp for matching simple quoted strings.
|
||||
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!)
|
||||
pat = """
|
||||
@@ -82,7 +78,8 @@ pat = """
|
||||
qqq
|
||||
"""
|
||||
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
|
||||
# us skip the uninteresting identifier references.
|
||||
|
35
lib/blame.py
35
lib/blame.py
@@ -42,25 +42,28 @@ def link_includes(text, repos, path_parts, include_url):
|
||||
match = re_includes.match(text)
|
||||
if match:
|
||||
incfile = match.group(3)
|
||||
include_path_parts = path_parts[:-1]
|
||||
for part in filter(None, string.split(incfile, '/')):
|
||||
if part == "..":
|
||||
if not include_path_parts:
|
||||
# nothing left to pop; don't bother marking up this include.
|
||||
return text
|
||||
include_path_parts.pop()
|
||||
elif part and part != ".":
|
||||
include_path_parts.append(part)
|
||||
|
||||
# check current directory and parent directory for file
|
||||
for depth in (-1, -2):
|
||||
include_path = path_parts[:depth] + [incfile]
|
||||
try:
|
||||
# will throw if path doesn't exist
|
||||
if repos.itemtype(include_path, None) == vclib.FILE:
|
||||
break
|
||||
except vclib.ItemNotFound:
|
||||
pass
|
||||
else:
|
||||
include_path = None
|
||||
include_path = None
|
||||
try:
|
||||
if repos.itemtype(include_path_parts, None) == vclib.FILE:
|
||||
include_path = string.join(include_path_parts, '/')
|
||||
except vclib.ItemNotFound:
|
||||
pass
|
||||
|
||||
if include_path:
|
||||
url = string.replace(include_url, '/WHERE/',
|
||||
string.join(include_path, '/'))
|
||||
return '#%sinclude%s<a href="%s">"%s"</a>' % \
|
||||
(match.group(1), match.group(2), url, incfile)
|
||||
|
||||
return '#%sinclude%s<a href="%s">"%s"</a>' % \
|
||||
(match.group(1), match.group(2),
|
||||
string.replace(include_url, '/WHERE/', include_path), incfile)
|
||||
|
||||
return text
|
||||
|
||||
|
||||
|
56
lib/cvsdb.py
56
lib/cvsdb.py
@@ -23,11 +23,6 @@ import dbi
|
||||
## error
|
||||
error = "cvsdb error"
|
||||
|
||||
## cached (active) database connections
|
||||
gCheckinDatabase = None
|
||||
gCheckinDatabaseReadOnly = None
|
||||
|
||||
|
||||
## CheckinDatabase provides all interfaces needed to the SQL database
|
||||
## 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
|
||||
@@ -50,6 +45,8 @@ class CheckinDatabase:
|
||||
def Connect(self):
|
||||
self.db = dbi.connect(
|
||||
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):
|
||||
sql = "SELECT id FROM %s WHERE %s=%%s" % (table, column)
|
||||
@@ -249,8 +246,8 @@ class CheckinDatabase:
|
||||
revision = commit.GetRevision()
|
||||
sticky_tag = "NULL"
|
||||
branch_id = self.GetBranchID(commit.GetBranch())
|
||||
plus_count = commit.GetPlusCount()
|
||||
minus_count = commit.GetMinusCount()
|
||||
plus_count = commit.GetPlusCount() or '0'
|
||||
minus_count = commit.GetMinusCount() or '0'
|
||||
description_id = self.GetDescriptionID(commit.GetDescription())
|
||||
|
||||
sql = "REPLACE INTO checkins"\
|
||||
@@ -677,39 +674,20 @@ def CreateCommit():
|
||||
def CreateCheckinQuery():
|
||||
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):
|
||||
global gCheckinDatabaseReadOnly
|
||||
|
||||
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
|
||||
return ConnectDatabase(cfg, 1)
|
||||
|
||||
def GetCommitListFromRCSFile(repository, path_parts, revision=None):
|
||||
commit_list = []
|
||||
|
@@ -659,7 +659,7 @@ def _write_value(value, args, ctx):
|
||||
piece = args[idx]
|
||||
else:
|
||||
piece = '<undef>'
|
||||
printer(ctx, piece)
|
||||
printer(ctx, piece)
|
||||
|
||||
# plain old value, write to output
|
||||
else:
|
||||
|
@@ -403,6 +403,7 @@ def main(server, cfg, viewvc_link):
|
||||
'query' : query,
|
||||
'commits' : commits,
|
||||
'num_commits' : len(commits),
|
||||
'rss_href' : None,
|
||||
}
|
||||
|
||||
if form_data.hours:
|
||||
|
@@ -135,7 +135,7 @@ class CgiServer(Server):
|
||||
def addheader(self, name, value):
|
||||
self.headers.append((name, value))
|
||||
|
||||
def header(self, content_type='text/html', status=None):
|
||||
def header(self, content_type='text/html; charset=UTF-8', status=None):
|
||||
if not self.headerSent:
|
||||
self.headerSent = 1
|
||||
|
||||
@@ -209,7 +209,10 @@ class AspServer(ThreadedServer):
|
||||
if not self.headerSent:
|
||||
try:
|
||||
self.headerSent = 1
|
||||
if content_type is not None: self.response.ContentType = content_type
|
||||
if content_type is None:
|
||||
self.response.ContentType = 'text/html; charset=UTF-8'
|
||||
else:
|
||||
self.response.ContentType = content_type
|
||||
if status is not None: self.response.Status = status
|
||||
except AttributeError:
|
||||
pass
|
||||
@@ -290,7 +293,7 @@ class ModPythonServer(ThreadedServer):
|
||||
|
||||
def header(self, content_type=None, status=None):
|
||||
if content_type is None:
|
||||
self.request.content_type = 'text/html'
|
||||
self.request.content_type = 'text/html; charset=UTF-8'
|
||||
else:
|
||||
self.request.content_type = content_type
|
||||
self.headerSent = 1
|
||||
|
@@ -474,7 +474,7 @@ def _tag_tuple(revision_string):
|
||||
t = map(int, string.split(revision_string, '.'))
|
||||
l = len(t)
|
||||
if l == 1:
|
||||
raise ValueError
|
||||
return ()
|
||||
if l > 2 and t[-2] == 0 and l % 2 == 0:
|
||||
del t[-2]
|
||||
return tuple(t)
|
||||
@@ -670,7 +670,8 @@ def _parse_log_header(fp):
|
||||
_re_log_info = re.compile(r'^date:\s+([^;]+);'
|
||||
r'\s+author:\s+([^;]+);'
|
||||
r'\s+state:\s+([^;]+);'
|
||||
r'(\s+lines:\s+([0-9\s+-]+))?\n$')
|
||||
r'(\s+lines:\s+([0-9\s+-]+);?)?'
|
||||
r'(\s+commitid:\s+([a-zA-Z0-9]+))?\n$')
|
||||
### _re_rev should be updated to extract the "locked" flag
|
||||
_re_rev = re.compile(r'^revision\s+([0-9.]+).*')
|
||||
def _parse_log_entry(fp):
|
||||
|
@@ -270,62 +270,70 @@ class ChangedPath:
|
||||
self.is_copy = is_copy
|
||||
|
||||
|
||||
class ChangedPathSet:
|
||||
def __init__(self):
|
||||
self.changes = { }
|
||||
def get_revision_info(svnrepos, rev):
|
||||
fsroot = svnrepos._getroot(rev)
|
||||
|
||||
def add_change(self, change):
|
||||
# Get the changes for the revision
|
||||
editor = repos.ChangeCollector(svnrepos.fs_ptr, fsroot, svnrepos.pool)
|
||||
e_ptr, e_baton = delta.make_editor(editor, svnrepos.pool)
|
||||
repos.svn_repos_replay(fsroot, e_ptr, e_baton, svnrepos.pool)
|
||||
changes = editor.get_changes()
|
||||
changedpaths = {}
|
||||
|
||||
# Copy the Subversion changes into a new hash, converting them into
|
||||
# ChangedPath objects.
|
||||
for path in changes.keys():
|
||||
change = changes[path]
|
||||
if change.path:
|
||||
change.path = _cleanup_path(change.path)
|
||||
if change.base_path:
|
||||
change.base_path = _cleanup_path(change.base_path)
|
||||
path = change.path
|
||||
action = 'modified'
|
||||
is_copy = 0
|
||||
if not change.path:
|
||||
action = 'deleted'
|
||||
path = change.base_path
|
||||
elif change.added:
|
||||
action = 'added'
|
||||
replace_check_path = path
|
||||
if change.base_path and change.base_rev:
|
||||
is_copy = 1
|
||||
replace_check_path = change.base_path
|
||||
if self.changes.has_key(replace_check_path) \
|
||||
and self.changes[replace_check_path].action == 'deleted':
|
||||
if not hasattr(change, 'action'): # new to subversion 1.4.0
|
||||
action = 'modified'
|
||||
if not change.path:
|
||||
action = 'deleted'
|
||||
elif change.added:
|
||||
action = 'added'
|
||||
replace_check_path = path
|
||||
if change.base_path and change.base_rev:
|
||||
replace_check_path = change.base_path
|
||||
if changedpaths.has_key(replace_check_path) \
|
||||
and changedpaths[replace_check_path].action == 'deleted':
|
||||
action = 'replaced'
|
||||
else:
|
||||
if change.action == repos.CHANGE_ACTION_ADD:
|
||||
action = 'added'
|
||||
elif change.action == repos.CHANGE_ACTION_DELETE:
|
||||
action = 'deleted'
|
||||
elif change.action == repos.CHANGE_ACTION_REPLACE:
|
||||
action = 'replaced'
|
||||
else:
|
||||
action = 'modified'
|
||||
if (action == 'added' or action == 'replaced') \
|
||||
and change.base_path \
|
||||
and change.base_rev:
|
||||
is_copy = 1
|
||||
if change.item_kind == core.svn_node_dir:
|
||||
pathtype = vclib.DIR
|
||||
elif change.item_kind == core.svn_node_file:
|
||||
pathtype = vclib.FILE
|
||||
else:
|
||||
pathtype = None
|
||||
self.changes[path] = ChangedPath(path, pathtype, change.prop_changes,
|
||||
changedpaths[path] = ChangedPath(path, pathtype, change.prop_changes,
|
||||
change.text_changed, change.base_path,
|
||||
change.base_rev, action, is_copy)
|
||||
|
||||
def get_changes(self):
|
||||
changes = self.changes.values()
|
||||
changes.sort(lambda a, b: _compare_paths(a.filename, b.filename))
|
||||
return changes
|
||||
|
||||
# Actually, what we want is a sorted list of ChangedPath objects.
|
||||
change_items = changedpaths.values()
|
||||
change_items.sort(lambda a, b: _compare_paths(a.filename, b.filename))
|
||||
|
||||
def get_revision_info(svnrepos, rev):
|
||||
fsroot = svnrepos._getroot(rev)
|
||||
|
||||
# Get the changes for the revision
|
||||
cps = ChangedPathSet()
|
||||
editor = repos.ChangeCollector(svnrepos.fs_ptr, fsroot,
|
||||
svnrepos.pool, cps.add_change)
|
||||
e_ptr, e_baton = delta.make_editor(editor, svnrepos.pool)
|
||||
repos.svn_repos_replay(fsroot, e_ptr, e_baton, svnrepos.pool)
|
||||
|
||||
# Now get the revision property info. Would use
|
||||
# editor.get_root_props(), but something is broken there...
|
||||
datestr, author, msg = _fs_rev_props(svnrepos.fs_ptr, rev, svnrepos.pool)
|
||||
date = _datestr_to_date(datestr, svnrepos.pool)
|
||||
|
||||
return date, author, msg, cps.get_changes()
|
||||
return date, author, msg, change_items
|
||||
|
||||
|
||||
def _log_helper(svnrepos, rev, path, pool):
|
||||
@@ -483,10 +491,18 @@ class BlameSource:
|
||||
self.last = None
|
||||
self.first_rev = first_rev
|
||||
|
||||
# Do a little dance to get a URL that works in both Unix-y and
|
||||
# Windows worlds.
|
||||
rootpath = os.path.abspath(rootpath)
|
||||
if rootpath and rootpath[0] != '/':
|
||||
rootpath = '/' + rootpath
|
||||
if os.sep != '/':
|
||||
rootpath = string.replace(rootpath, os.sep, '/')
|
||||
|
||||
url = 'file://' + string.join([rootpath, fs_path], "/")
|
||||
fp = popen.popen(svn_client_path,
|
||||
('blame', "-r%d" % int(rev), "%s@%d" % (url, int(rev))),
|
||||
('blame', "-r%d" % int(rev), "--non-interactive",
|
||||
"%s@%d" % (url, int(rev))),
|
||||
'rb', 1)
|
||||
self.fp = fp
|
||||
|
||||
|
240
lib/viewvc.py
240
lib/viewvc.py
@@ -14,7 +14,7 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
__version__ = '1.0-dev'
|
||||
__version__ = '1.0.4'
|
||||
|
||||
# this comes from our library; measure the startup time
|
||||
import debug
|
||||
@@ -272,7 +272,9 @@ class Request:
|
||||
# If this is using an old-style 'rev' parameter, redirect to new hotness.
|
||||
# Subversion URLs will now use 'pathrev'; CVS ones use 'revision'.
|
||||
if self.repos and self.query_dict.has_key('rev'):
|
||||
if self.roottype == 'svn' and not self.query_dict.has_key('pathrev'):
|
||||
if self.roottype == 'svn' \
|
||||
and not self.query_dict.has_key('pathrev') \
|
||||
and not self.view_func == view_revision:
|
||||
self.query_dict['pathrev'] = self.query_dict['rev']
|
||||
del self.query_dict['rev']
|
||||
else: # elif not self.query_dict.has_key('revision'): ?
|
||||
@@ -746,8 +748,11 @@ def _orig_path(request, rev_param='revision', path_param=None):
|
||||
path = request.query_dict.get(path_param, request.where)
|
||||
|
||||
if rev is not None and hasattr(request.repos, '_getrev'):
|
||||
pathrev = request.repos._getrev(request.pathrev)
|
||||
rev = request.repos._getrev(rev)
|
||||
try:
|
||||
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,
|
||||
pathrev, rev)), rev
|
||||
return _path_parts(path), rev
|
||||
@@ -944,7 +949,7 @@ def get_file_view_info(request, where, rev=None, mime_type=None, pathrev=-1):
|
||||
pathtype=vclib.FILE,
|
||||
params={'content-type': 'text/plain',
|
||||
'revision': rev,
|
||||
'pathrev': rev},
|
||||
'pathrev': pathrev},
|
||||
escape=1)
|
||||
if request.cfg.options.allow_annotate:
|
||||
annotate_href = request.get_url(view_func=view_annotate,
|
||||
@@ -1311,18 +1316,15 @@ def markup_stream_php(fp, cfg):
|
||||
if not cfg.options.use_php:
|
||||
return None
|
||||
|
||||
sys.stdout.flush()
|
||||
|
||||
# clearing the following environment variables prevents a
|
||||
# "No input file specified" error from the php cgi executable
|
||||
# when ViewVC is running under a cgi environment. when the
|
||||
# php cli executable is used they can be left alone
|
||||
#
|
||||
#os.putenv("GATEWAY_INTERFACE", "")
|
||||
#os.putenv("PATH_TRANSLATED", "")
|
||||
#os.putenv("REQUEST_METHOD", "")
|
||||
#os.putenv("SERVER_NAME", "")
|
||||
#os.putenv("SERVER_SOFTWARE", "")
|
||||
# The following HACK may be be used to allow a PHP CGI executable to be
|
||||
# invoked instead of a CLI executable, on systems that do not have PHP's
|
||||
# CLI (command line interface) installed. Just uncomment the following lines:
|
||||
#os.unsetenv("SERVER_SOFTWARE")
|
||||
#os.unsetenv("SERVER_NAME")
|
||||
#os.unsetenv("GATEWAY_INTERFACE")
|
||||
#os.unsetenv("REQUEST_METHOD")
|
||||
#os.unsetenv("SCRIPT_FILENAME")
|
||||
#os.unsetenv("PATH_TRANSLATED")
|
||||
|
||||
return MarkupPHP(cfg.options.php_exe_path, fp)
|
||||
|
||||
@@ -1357,6 +1359,10 @@ def make_rss_time_string(date, cfg):
|
||||
return time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime(date)) + ' UTC'
|
||||
|
||||
def view_markup(request):
|
||||
if not request.cfg.options.allow_markup:
|
||||
raise debug.ViewVCException('Markup view is disabled',
|
||||
'403 Forbidden')
|
||||
|
||||
cfg = request.cfg
|
||||
path, rev = _orig_path(request)
|
||||
fp, revision = request.repos.openfile(path, rev)
|
||||
@@ -1423,14 +1429,17 @@ def view_markup(request):
|
||||
streamer = markup_streamers.get(ext)
|
||||
if streamer:
|
||||
markup_fp = streamer(fp, cfg)
|
||||
elif cfg.options.use_enscript:
|
||||
markup_fp = MarkupEnscript(cfg, fp, request.path_parts[-1])
|
||||
elif cfg.options.use_highlight:
|
||||
markup_fp = MarkupHighlight(cfg, fp, request.path_parts[-1])
|
||||
|
||||
# If no one has a suitable markup handler, we'll use the default.
|
||||
if not markup_fp:
|
||||
markup_fp = MarkupPipeWrapper(fp)
|
||||
# If there wasn't a custom streamer, or the streamer wasn't enabled, we'll
|
||||
# try to use one of the configured syntax highlighting programs.
|
||||
if not markup_fp:
|
||||
if cfg.options.use_enscript:
|
||||
markup_fp = MarkupEnscript(cfg, fp, request.path_parts[-1])
|
||||
elif cfg.options.use_highlight:
|
||||
markup_fp = MarkupHighlight(cfg, fp, request.path_parts[-1])
|
||||
else:
|
||||
# If no one has a suitable markup handler, we'll use the default.
|
||||
markup_fp = MarkupPipeWrapper(fp)
|
||||
|
||||
data['markup'] = markup_fp
|
||||
|
||||
@@ -1512,7 +1521,10 @@ def view_directory(request):
|
||||
# the directory listing (to take into account template changes or
|
||||
# revision property changes).
|
||||
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)
|
||||
if check_freshness(request, None, str(tree_rev), weak=1):
|
||||
return
|
||||
@@ -1823,7 +1835,7 @@ def redirect_pathrev(request):
|
||||
try:
|
||||
new_pathrev = int(new_pathrev)
|
||||
except ValueError:
|
||||
pass
|
||||
new_pathrev = youngest
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
@@ -2088,9 +2100,8 @@ def view_log(request):
|
||||
'tag_prefer_markup': prefer_markup,
|
||||
})
|
||||
else:
|
||||
if not request.pathrev:
|
||||
data['view_href'] = request.get_url(view_func=view_directory,
|
||||
params={}, escape=1)
|
||||
data['view_href'] = request.get_url(view_func=view_directory,
|
||||
params={}, escape=1)
|
||||
|
||||
taginfo = options.get('cvs_tags', {})
|
||||
tagitems = taginfo.items()
|
||||
@@ -2121,7 +2132,7 @@ def view_log(request):
|
||||
if cfg.options.use_pagesize:
|
||||
data['log_pagestart'] = int(request.query_dict.get('log_pagestart',0))
|
||||
data['entries'] = paging(data, 'entries', data['log_pagestart'],
|
||||
'revision', cfg.options.use_pagesize)
|
||||
'rev', cfg.options.use_pagesize)
|
||||
|
||||
request.server.header()
|
||||
generate_page(request, "log", data)
|
||||
@@ -2607,8 +2618,12 @@ def setup_diff(request):
|
||||
sym2 = r2[idx+1:]
|
||||
|
||||
if request.roottype == 'svn':
|
||||
rev1 = str(request.repos._getrev(rev1))
|
||||
rev2 = str(request.repos._getrev(rev2))
|
||||
try:
|
||||
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)
|
||||
p2 = _get_diff_path_parts(request, 'p2', rev2, request.pathrev)
|
||||
@@ -2781,7 +2796,7 @@ def generate_tarball_header(out, name, size=0, mode=None, mtime=0,
|
||||
uid=0, gid=0, typefrag=None, linkname='',
|
||||
uname='viewvc', gname='viewvc',
|
||||
devmajor=1, devminor=0, prefix=None,
|
||||
magic='ustar', version='', chksum=None):
|
||||
magic='ustar', version='00', chksum=None):
|
||||
if not mode:
|
||||
if name[-1:] == '/':
|
||||
mode = 0755
|
||||
@@ -2797,6 +2812,12 @@ def generate_tarball_header(out, name, size=0, mode=None, mtime=0,
|
||||
if not 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',
|
||||
name,
|
||||
'%07o' % mode,
|
||||
@@ -2828,7 +2849,7 @@ def generate_tarball_header(out, name, size=0, mode=None, mtime=0,
|
||||
|
||||
out.write(block)
|
||||
|
||||
def generate_tarball(out, request, reldir, stack):
|
||||
def generate_tarball(out, request, reldir, stack, dir_mtime=None):
|
||||
# get directory info from repository
|
||||
rep_path = request.path_parts + reldir
|
||||
entries = request.repos.listdir(rep_path, request.pathrev, {})
|
||||
@@ -2845,30 +2866,43 @@ def generate_tarball(out, request, reldir, stack):
|
||||
if reldir:
|
||||
tar_dir = tar_dir + _path_join(reldir) + '/'
|
||||
|
||||
# Subdirectory datestamps will be the youngest of the datestamps of
|
||||
# version items (files for CVS, files or dirs for Subversion) in
|
||||
# that subdirectory.
|
||||
latest_date = 0
|
||||
cvs = request.roottype == 'cvs'
|
||||
for file in entries:
|
||||
# Skip dead or busted CVS files, and CVS subdirs.
|
||||
if (cvs and (file.kind != vclib.FILE or (file.rev is None or file.dead))):
|
||||
continue
|
||||
if file.date > latest_date:
|
||||
latest_date = file.date
|
||||
|
||||
# If our caller doesn't dictate a datestamp to use for the current
|
||||
# directory, its datestamps will be the youngest of the datestamps
|
||||
# of versioned items in that subdirectory. We'll be ignoring dead
|
||||
# or busted items and, in CVS, subdirs.
|
||||
if dir_mtime is None:
|
||||
dir_mtime = 0
|
||||
for file in entries:
|
||||
if cvs and (file.kind != vclib.FILE or file.rev is None or file.dead):
|
||||
continue
|
||||
if file.date > dir_mtime:
|
||||
dir_mtime = file.date
|
||||
|
||||
# push directory onto stack. it will only be included in the tarball if
|
||||
# files are found underneath it
|
||||
# Push current directory onto the stack.
|
||||
stack.append(tar_dir)
|
||||
|
||||
# If this is Subversion, we generate a header for this directory
|
||||
# regardless of its contents. For CVS it will only get into the
|
||||
# tarball if it has files underneath it, which we determine later.
|
||||
if not cvs:
|
||||
generate_tarball_header(out, tar_dir, mtime=dir_mtime)
|
||||
|
||||
# Run through the files in this directory, skipping busted ones.
|
||||
for file in entries:
|
||||
if (file.kind != vclib.FILE or
|
||||
(cvs and (file.rev is None or file.dead))):
|
||||
if file.kind != vclib.FILE:
|
||||
continue
|
||||
if cvs and (file.rev is None or file.dead):
|
||||
continue
|
||||
|
||||
for dir in stack:
|
||||
generate_tarball_header(out, dir, mtime=latest_date)
|
||||
del stack[:]
|
||||
# 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
|
||||
# directory parents to contain it, so we flush the stack.
|
||||
if cvs:
|
||||
for dir in stack:
|
||||
generate_tarball_header(out, dir, mtime=dir_mtime)
|
||||
del stack[:]
|
||||
|
||||
if cvs:
|
||||
info = os.stat(file.path)
|
||||
@@ -2876,7 +2910,8 @@ def generate_tarball(out, request, reldir, stack):
|
||||
else:
|
||||
mode = 0644
|
||||
|
||||
### read the whole file into memory? bad... better to do 2 passes
|
||||
### FIXME: Read the whole file into memory? Bad... better to do
|
||||
### 2 passes.
|
||||
fp = request.repos.openfile(rep_path + [file.name], request.pathrev)[0]
|
||||
contents = fp.read()
|
||||
fp.close()
|
||||
@@ -2886,21 +2921,22 @@ def generate_tarball(out, request, reldir, stack):
|
||||
out.write(contents)
|
||||
out.write('\0' * (511 - ((len(contents) + 511) % 512)))
|
||||
|
||||
# recurse into subdirectories
|
||||
# Recurse into subdirectories, skipping busted ones.
|
||||
for file in entries:
|
||||
if file.errors or file.kind != vclib.DIR:
|
||||
continue
|
||||
|
||||
# skip forbidden/hidden directories (top-level only)
|
||||
# Skip forbidden/hidden directories (top-level only).
|
||||
if not rep_path:
|
||||
if (request.cfg.is_forbidden(file.name)
|
||||
or (cvs and request.cfg.options.hide_cvsroot
|
||||
and file.name == 'CVSROOT')):
|
||||
continue
|
||||
|
||||
generate_tarball(out, request, reldir + [file.name], stack)
|
||||
mtime = request.roottype == 'svn' and file.date or None
|
||||
generate_tarball(out, request, reldir + [file.name], stack, mtime)
|
||||
|
||||
# pop directory (if it's being pruned. otherwise stack is already empty)
|
||||
# Pop the current directory from the stack.
|
||||
del stack[-1:]
|
||||
|
||||
def download_tarball(request):
|
||||
@@ -2914,6 +2950,8 @@ def download_tarball(request):
|
||||
sys.stdout.flush()
|
||||
fp = popen.pipe_cmds([('gzip', '-c', '-n')])
|
||||
|
||||
### FIXME: For Subversion repositories, we can get the real mtime of the
|
||||
### top-level directory here.
|
||||
generate_tarball(fp, request, [], [])
|
||||
|
||||
fp.write('\0' * 1024)
|
||||
@@ -2926,7 +2964,11 @@ def view_revision(request):
|
||||
|
||||
data = common_template_data(request)
|
||||
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_str = make_time_string(date, request.cfg)
|
||||
|
||||
@@ -3127,22 +3169,23 @@ def english_query(request):
|
||||
ret.append('subdirectories')
|
||||
else:
|
||||
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', '')
|
||||
if file:
|
||||
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', '')
|
||||
branch = request.query_dict.get('branch', '')
|
||||
if branch:
|
||||
ret.append('on branch <em>%s</em> ' % htmlify(branch))
|
||||
ret.append('on branch <em>%s</em> ' % request.server.escape(branch))
|
||||
else:
|
||||
ret.append('on all branches ')
|
||||
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')
|
||||
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':
|
||||
ret.append('in the last day')
|
||||
elif date == 'week':
|
||||
@@ -3182,12 +3225,13 @@ def build_commit(request, files, limited_files, dir_strip):
|
||||
desc = files[0].GetDescription()
|
||||
commit.log = htmlify(desc)
|
||||
commit.short_log = format_log(desc, request.cfg)
|
||||
commit.author = htmlify(files[0].GetAuthor())
|
||||
commit.author = request.server.escape(files[0].GetAuthor())
|
||||
commit.rss_date = make_rss_time_string(files[0].GetTime(), request.cfg)
|
||||
if request.roottype == 'svn':
|
||||
commit.rev = files[0].GetRevision()
|
||||
commit.rss_url = 'http://%s%s' % \
|
||||
(request.server.getenv("HTTP_HOST"),
|
||||
commit.rss_url = '%s://%s%s' % \
|
||||
(request.server.getenv("HTTPS") == "on" and "https" or "http",
|
||||
request.server.getenv("HTTP_HOST"),
|
||||
request.get_url(view_func=view_revision,
|
||||
params={'revision': commit.rev},
|
||||
escape=1))
|
||||
@@ -3203,7 +3247,10 @@ def build_commit(request, files, limited_files, dir_strip):
|
||||
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:
|
||||
@@ -3212,30 +3259,41 @@ def build_commit(request, files, limited_files, 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()
|
||||
# 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)
|
||||
params=params, escape=1)
|
||||
log_href = request.get_url(view_func=view_log,
|
||||
where=filename, pathtype=vclib.FILE,
|
||||
params=params,
|
||||
escape=1)
|
||||
params=params, escape=1)
|
||||
diff_href = view_href = download_href = None
|
||||
view_href = request.get_url(view_func=view_markup,
|
||||
where=filename, pathtype=vclib.FILE,
|
||||
params={'revision': f.GetRevision() },
|
||||
escape=1)
|
||||
where=filename, pathtype=vclib.FILE,
|
||||
params=params, 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)
|
||||
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=filename, pathtype=vclib.FILE,
|
||||
params=diff_href_params, escape=1)
|
||||
prefer_markup = ezt.boolean(default_view(guess_mime(filename),
|
||||
request.cfg) == view_markup)
|
||||
|
||||
# skip files in forbidden or hidden modules
|
||||
dir_parts = filter(None, string.split(dirname, '/'))
|
||||
@@ -3246,21 +3304,19 @@ def build_commit(request, files, limited_files, dir_strip):
|
||||
continue
|
||||
|
||||
commit.files.append(_item(date=commit_time,
|
||||
dir=htmlify(dirname),
|
||||
file=htmlify(f.GetFile()),
|
||||
author=htmlify(f.GetAuthor()),
|
||||
rev=f.GetRevision(),
|
||||
dir=request.server.escape(dirname),
|
||||
file=request.server.escape(f.GetFile()),
|
||||
author=request.server.escape(f.GetAuthor()),
|
||||
rev=rev,
|
||||
branch=f.GetBranch(),
|
||||
plus=int(f.GetPlusCount()),
|
||||
minus=int(f.GetMinusCount()),
|
||||
type=f.GetTypeString(),
|
||||
type=change_type,
|
||||
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),
|
||||
prefer_markup=prefer_markup,
|
||||
diff_href=diff_href))
|
||||
return commit
|
||||
|
||||
@@ -3382,7 +3438,7 @@ def view_query(request):
|
||||
# run the query
|
||||
db.RunQuery(query)
|
||||
|
||||
sql = htmlify(db.CreateSQLQueryString(query))
|
||||
sql = request.server.escape(db.CreateSQLQueryString(query))
|
||||
|
||||
# gather commits
|
||||
commits = []
|
||||
|
@@ -15,8 +15,18 @@
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
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 win32event, win32file, win32api, winerror
|
||||
import win32event, win32file, winerror
|
||||
import pywintypes, msvcrt
|
||||
|
||||
# Buffer size for spooling
|
||||
|
@@ -9,7 +9,7 @@
|
||||
enter a numeric revision.
|
||||
[end]
|
||||
</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">
|
||||
<tr>
|
||||
@@ -26,7 +26,7 @@
|
||||
</select>
|
||||
<input type="text" size="12" name="tr1"
|
||||
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]
|
||||
<input type="text" size="12" name="r1"
|
||||
value="[if-any rev_selected][rev_selected][else][first_revision][end]" />
|
||||
@@ -42,7 +42,7 @@
|
||||
</select>
|
||||
<input type="text" size="12" name="tr2"
|
||||
value="[last_revision]"
|
||||
onchange="document.diff_select.r1.selectedIndex=0" />
|
||||
onchange="document.getElementById('diff_select').r2.selectedIndex=0" />
|
||||
[else]
|
||||
<input type="text" size="12" name="r2" value="[last_revision]" />
|
||||
[end]
|
||||
|
@@ -32,7 +32,7 @@
|
||||
|
||||
[# if you want to disable tarball generation remove the following: ]
|
||||
[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]
|
||||
|
||||
[include "footer.ezt"]
|
||||
|
@@ -5,11 +5,11 @@
|
||||
<table>
|
||||
<tr>
|
||||
<td><address>[cfg.general.address]</address></td>
|
||||
<td style="text-align: right;"><strong><a href="[help_href]">ViewVC Help</strong></td>
|
||||
<td style="text-align: right;"><strong><a href="[help_href]">ViewVC Help</a></strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
</table>
|
||||
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<title>[if-any rootname][[][rootname]][else]ViewVC[end] [page_title]</title>
|
||||
<meta name="generator" content="ViewVC [vsn]" />
|
||||
<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>
|
||||
<body>
|
||||
<div class="vc_navheader">
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<form method="get" action="[pathrev_action]" style="display: inline">
|
||||
<div style="display: inline">
|
||||
[pathrev_hidden_values]
|
||||
[is roottype "cvs"]
|
||||
[define pathrev_selected][pathrev][end]
|
||||
@@ -34,16 +35,19 @@
|
||||
<input type="text" name="pathrev" value="[pathrev]" size="6"/>
|
||||
[end]
|
||||
<input type="submit" value="Set" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
[if-any pathrev]
|
||||
<form method="get" action="[pathrev_clear_action]" style="display: inline">
|
||||
<div style="display: inline">
|
||||
[pathrev_clear_hidden_values]
|
||||
[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>)
|
||||
[else]
|
||||
<input type="submit" value="Clear">
|
||||
<input type="submit" value="Clear" />
|
||||
[end]
|
||||
</div>
|
||||
</form>
|
||||
[end]
|
||||
|
@@ -12,7 +12,7 @@
|
||||
<th>Jump to revision:</th>
|
||||
<td>
|
||||
[jump_rev_hidden_values]
|
||||
<input type="text" name="rev" value="[rev]" />
|
||||
<input type="text" name="revision" value="[rev]" />
|
||||
<input type="submit" value="Go" />
|
||||
[if-any prev_href]
|
||||
<a href="[prev_href]"><img src="[docroot]/images/back.png" alt="Previous" width="20" height="22" /></a>[end]
|
||||
|
645
viewvc-install
645
viewvc-install
@@ -11,10 +11,7 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# install script for viewvc -- temporary?
|
||||
#
|
||||
# ### this will eventually be replaced by autoconf plus tools. an
|
||||
# ### interactive front-end to ./configure may be provided.
|
||||
# Install script for ViewVC
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
@@ -27,7 +24,7 @@ import py_compile
|
||||
import getopt
|
||||
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'))
|
||||
|
||||
import compat
|
||||
@@ -36,324 +33,440 @@ import compat_ndiff
|
||||
version = viewvc.__version__
|
||||
|
||||
|
||||
## installer text
|
||||
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
|
||||
## Installer defaults.
|
||||
DESTDIR = None
|
||||
ROOT_DIR = None
|
||||
CLEAN_MODE = None
|
||||
|
||||
|
||||
## list of files for installation
|
||||
## tuple (source path, destination path, install mode, true/false flag for
|
||||
## search-and-replace, flag or text for prompt before replace,
|
||||
## compile_it)
|
||||
##
|
||||
|
||||
## List of files for installation.
|
||||
## tuple (source path,
|
||||
## destination path,
|
||||
## mode,
|
||||
## boolean -- search-and-replace?
|
||||
## boolean -- prompt before replacing?
|
||||
## boolean -- compile?)
|
||||
FILE_INFO_LIST = [
|
||||
("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/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/cgi/viewvc.cgi", "bin/cgi/viewvc.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/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/.htaccess", "bin/mod_python/.htaccess", 0755, 0, 0, 0),
|
||||
("bin/standalone.py", "bin/standalone.py", 0755, 1, 0, 0),
|
||||
("viewvc.conf.dist", "viewvc.conf", 0644, 0,
|
||||
"""Note: If you are upgrading from viewcvs-0.7 or earlier:
|
||||
The section [text] has been removed from viewcvs.conf. The functionality
|
||||
went into the new files in subdirectory templates.""", 0),
|
||||
("cvsgraph.conf.dist", "cvsgraph.conf", 0644, 0, 1, 0),
|
||||
|
||||
("bin/loginfo-handler", "bin/loginfo-handler", 0755, 1, 0, 0),
|
||||
("bin/cvsdbadmin", "bin/cvsdbadmin", 0755, 1, 0, 0),
|
||||
("bin/svndbadmin", "bin/svndbadmin", 0755, 1, 0, 0),
|
||||
("bin/make-database", "bin/make-database", 0755, 1, 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/loginfo-handler", "bin/loginfo-handler", 0755, 1, 0, 0),
|
||||
("bin/cvsdbadmin", "bin/cvsdbadmin", 0755, 1, 0, 0),
|
||||
("bin/svndbadmin", "bin/svndbadmin", 0755, 1, 0, 0),
|
||||
("bin/make-database", "bin/make-database", 0755, 1, 0, 0),
|
||||
("viewvc.conf.dist", "viewvc.conf.dist", 0644, 0, 0, 0),
|
||||
("viewvc.conf.dist", "viewvc.conf", 0644, 0, 1, 0),
|
||||
("cvsgraph.conf.dist", "cvsgraph.conf.dist", 0644, 0, 0, 0),
|
||||
("cvsgraph.conf.dist", "cvsgraph.conf", 0644, 0, 1, 0),
|
||||
]
|
||||
|
||||
if sys.platform == "win32":
|
||||
FILE_INFO_LIST.extend([
|
||||
("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/viewvc.asp", "bin/asp/viewvc.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 = [
|
||||
("lib", "lib", 0),
|
||||
("templates", "templates", 1),
|
||||
]
|
||||
("lib", "lib", 0),
|
||||
("templates", "templates", 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):
|
||||
print
|
||||
print "[ERROR] %s" % text
|
||||
## List of file extensions we can't show diffs for.
|
||||
BINARY_FILE_EXTS = [
|
||||
'.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:
|
||||
print '[ERROR] ',
|
||||
traceback.print_exception(etype, evalue, None, file=sys.stdout)
|
||||
traceback.print_exception(etype, evalue, None, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def MkDir(path):
|
||||
try:
|
||||
compat.makedirs(path)
|
||||
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):
|
||||
def replace_var(contents, var, value):
|
||||
"""Replace instances of the variable VAR as found in file CONTENTS
|
||||
with VALUE."""
|
||||
pattern = re.compile('^' + var + r'\s*=\s*.*$', re.MULTILINE)
|
||||
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] == '#!':
|
||||
shbang = '#!' + sys.executable
|
||||
contents = re.sub('^#![^\n]*', ReEscape(shbang), contents)
|
||||
contents = SetOnePath(contents, 'LIBRARY_DIR', 'lib')
|
||||
contents = SetOnePath(contents, 'CONF_PATHNAME', 'viewvc.conf')
|
||||
contents = re.sub('^#![^\n]*', _escape(shbang), contents)
|
||||
contents = replace_var(contents, 'LIBRARY_DIR', 'lib')
|
||||
contents = replace_var(contents, 'CONF_PATHNAME', 'viewvc.conf')
|
||||
return contents
|
||||
|
||||
|
||||
def InstallFile(src_path, dest_path, mode, set_python_paths, prompt_replace,
|
||||
compile_it):
|
||||
dest_path = os.path.join(ROOT_DIR, dest_path)
|
||||
def install_file(src_path, dst_path, mode, subst_path_vars,
|
||||
prompt_replace, compile_it):
|
||||
"""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):
|
||||
# Collect ndiff output from ndiff
|
||||
sys.stdout = StringIO.StringIO()
|
||||
compat_ndiff.main([DESTDIR + dest_path, src_path])
|
||||
ndiff_output = sys.stdout.getvalue()
|
||||
src_path = _actual_src_path(src_path)
|
||||
dst_path = os.path.join(ROOT_DIR, string.replace(dst_path, '/', os.sep))
|
||||
destdir_path = DESTDIR + dst_path
|
||||
|
||||
# Return everything to normal
|
||||
sys.stdout = sys.__stdout__
|
||||
overwrite = None
|
||||
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 ndiff output from ndiff
|
||||
sys.stdout = StringIO.StringIO()
|
||||
compat_ndiff.main([destdir_path, src_path])
|
||||
ndiff_output = sys.stdout.getvalue()
|
||||
|
||||
# Collect the '+ ' and '- ' lines
|
||||
# total collects the difference lines to be printed later
|
||||
total = ""
|
||||
# I use flag to throw out match lines.
|
||||
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
|
||||
# Return everything to normal
|
||||
sys.stdout = sys.__stdout__
|
||||
|
||||
if total == "\n":
|
||||
print " File %s exists,\n but there is no difference between target and source files.\n" % (DESTDIR + dest_path)
|
||||
return
|
||||
# Collect the '+ ' and '- ' lines.
|
||||
diff_lines = []
|
||||
looking_at_diff_lines = 0
|
||||
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] == "? ":
|
||||
diff_lines.append(line)
|
||||
looking_at_diff_lines = 1
|
||||
else:
|
||||
# Compress lines that are the same to print one blank line
|
||||
if looking_at_diff_lines:
|
||||
diff_lines.append("")
|
||||
looking_at_diff_lines = 0
|
||||
|
||||
if type(prompt_replace) == type(""):
|
||||
print prompt_replace
|
||||
while 1:
|
||||
temp = raw_input("""
|
||||
File %s exists and is different from source file.
|
||||
DO YOU WANT TO,
|
||||
overwrite [o]
|
||||
do not overwrite [d]
|
||||
view differences [v]: """ % (DESTDIR + dest_path))
|
||||
print
|
||||
|
||||
temp = string.lower(temp[0])
|
||||
|
||||
if temp == "d":
|
||||
return
|
||||
|
||||
if temp == "v":
|
||||
if string.lower(src_path[-4:]) in [ '.gif', '.png', '.jpg' ]:
|
||||
print 'Can not print differences between binary files'
|
||||
# If there are no differences, we're done here.
|
||||
if not diff_lines:
|
||||
overwrite = 1
|
||||
else:
|
||||
print total
|
||||
print """
|
||||
# If we get here, there are differences.
|
||||
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
|
||||
A leading '- ' indicates line to remove from installed file
|
||||
A leading '+ ' indicates line to add to installed file
|
||||
A leading '? ' shows intraline differences."""
|
||||
A leading '- ' indicates line to remove from installed file
|
||||
A leading '+ ' indicates line to add to installed file
|
||||
A leading '? ' shows intraline differences.
|
||||
---------------------------------------------------------------------------"""
|
||||
elif temp == "d":
|
||||
overwrite = 0
|
||||
elif temp == "o":
|
||||
overwrite = 1
|
||||
|
||||
if temp == "o":
|
||||
ReplaceFile(src_path, dest_path, mode, set_python_paths,
|
||||
prompt_replace, compile_it)
|
||||
if overwrite is not None:
|
||||
break
|
||||
|
||||
assert overwrite is not None
|
||||
if not overwrite:
|
||||
print " preserved %s" % (dst_path)
|
||||
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,
|
||||
prompt_replace, compile_it):
|
||||
try:
|
||||
contents = open(src_path, "rb").read()
|
||||
except IOError, e:
|
||||
Error(str(e))
|
||||
### If we get here, we're creating or overwriting the existing file.
|
||||
|
||||
if set_python_paths:
|
||||
contents = SetPythonPaths(contents)
|
||||
# Read the source file's contents.
|
||||
try:
|
||||
contents = open(src_path, "rb").read()
|
||||
except IOError, e:
|
||||
error(str(e))
|
||||
|
||||
## write the file to the destination location
|
||||
path, basename = os.path.split(DESTDIR + dest_path)
|
||||
MkDir(path)
|
||||
|
||||
try:
|
||||
open(DESTDIR + dest_path, "wb").write(contents)
|
||||
except IOError, e:
|
||||
if e[0] == 13:
|
||||
# EACCES: permission denied
|
||||
Error("You do not have permission to write file %s" % dest_path)
|
||||
Error("Unknown error writing file %s" % dest_path, IOError, e)
|
||||
|
||||
os.chmod(DESTDIR + dest_path, mode)
|
||||
|
||||
if compile_it:
|
||||
py_compile.compile(DESTDIR + dest_path,
|
||||
DESTDIR + dest_path + "c" , dest_path)
|
||||
|
||||
return
|
||||
# (Optionally) substitute ViewVC path variables.
|
||||
if subst_path_vars:
|
||||
contents = replace_paths(contents)
|
||||
|
||||
# Ensure the existence of the containing directories.
|
||||
dst_parent = os.path.dirname(destdir_path)
|
||||
if not os.path.exists(dst_parent):
|
||||
try:
|
||||
compat.makedirs(dst_parent)
|
||||
print " created %s%s" % (dst_parent, os.sep)
|
||||
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))
|
||||
|
||||
# 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))
|
||||
|
||||
# Set the files's mode.
|
||||
os.chmod(destdir_path, mode)
|
||||
|
||||
# (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):
|
||||
files = os.listdir(src_path)
|
||||
files.sort()
|
||||
for fname in files:
|
||||
# eliminate some items which appear in a development area
|
||||
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
|
||||
"""Install a tree 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). 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."""
|
||||
|
||||
src = os.path.join(src_path, fname)
|
||||
dst = os.path.join(dst_path, fname)
|
||||
if os.path.isdir(src):
|
||||
install_tree(src, dst, prompt_replace)
|
||||
orig_src_path = src_path
|
||||
orig_dst_path = dst_path
|
||||
src_path = _actual_src_path(src_path)
|
||||
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:
|
||||
print " ", src
|
||||
set_paths = 0
|
||||
compile_it = fname[-3:] == '.py'
|
||||
InstallFile(src, dst, 0644, set_paths, prompt_replace, compile_it)
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
# 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__":
|
||||
# option parsing
|
||||
# Option parsing.
|
||||
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:
|
||||
Error("Invalid option", getopt.GetoptError, e)
|
||||
usage_and_exit(str(e))
|
||||
for opt, arg in optlist:
|
||||
if opt == '--prefix':
|
||||
ROOT_DIR = arg
|
||||
if opt == '--destdir':
|
||||
DESTDIR = arg
|
||||
if opt == '--help' or opt == '-h' or opt == '-?':
|
||||
usage_and_exit()
|
||||
if opt == '--prefix':
|
||||
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 INFO_TEXT
|
||||
# Print the header greeting.
|
||||
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 sys.platform == "win32":
|
||||
pf = os.getenv("ProgramFiles", "C:\\Program Files")
|
||||
default = os.path.join(pf, "viewvc-" + version)
|
||||
else:
|
||||
default = "/usr/local/viewvc-" + version
|
||||
temp = string.strip(raw_input("Installation path [%s]: " % default))
|
||||
print
|
||||
if len(temp):
|
||||
ROOT_DIR = temp
|
||||
else:
|
||||
ROOT_DIR = default
|
||||
|
||||
## prompt for DESTDIR if none provided
|
||||
if sys.platform == "win32":
|
||||
pf = os.getenv("ProgramFiles", "C:\\Program Files")
|
||||
default = os.path.join(pf, "viewvc-" + version)
|
||||
else:
|
||||
default = "/usr/local/viewvc-" + version
|
||||
temp = string.strip(raw_input("Installation path [%s]: " \
|
||||
% default))
|
||||
print
|
||||
if len(temp):
|
||||
ROOT_DIR = temp
|
||||
else:
|
||||
ROOT_DIR = default
|
||||
|
||||
# Prompt for DESTDIR if none provided.
|
||||
if DESTDIR is None:
|
||||
default = ''
|
||||
temp = string.strip(raw_input(
|
||||
"DESTDIR path (generally, only package maintainers will need "
|
||||
"to change\nthis) [%s]: " % default))
|
||||
print
|
||||
if len(temp):
|
||||
DESTDIR = temp
|
||||
else:
|
||||
DESTDIR = default
|
||||
|
||||
## install the files
|
||||
print "Installing ViewVC to:", ROOT_DIR,
|
||||
if DESTDIR:
|
||||
print "(DESTDIR = %s)" % (DESTDIR)
|
||||
else:
|
||||
print
|
||||
|
||||
default = ''
|
||||
temp = string.strip(raw_input(
|
||||
"DESTDIR path (generally only used by package "
|
||||
"maintainers) [%s]: " \
|
||||
% default))
|
||||
print
|
||||
if len(temp):
|
||||
DESTDIR = temp
|
||||
else:
|
||||
DESTDIR = default
|
||||
|
||||
# Install the files.
|
||||
print "Installing ViewVC to %s%s:" \
|
||||
% (ROOT_DIR, DESTDIR and " (DESTDIR = %s)" % (DESTDIR) or "")
|
||||
for args in FILE_INFO_LIST:
|
||||
print " ", args[0]
|
||||
apply(InstallFile, args)
|
||||
|
||||
apply(install_file, args)
|
||||
for args in TREE_LIST:
|
||||
apply(install_tree, args)
|
||||
|
||||
apply(install_tree, args)
|
||||
|
||||
# Print some final thoughts.
|
||||
print """
|
||||
|
||||
ViewVC File Installation Complete
|
||||
ViewVC file installation complete.
|
||||
|
||||
Consult INSTALL for detailed information to finish the installation
|
||||
and configure ViewVC for your system.
|
||||
Consult the INSTALL document for detailed information on completing the
|
||||
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) Configure an existing web server to run (or copy to cgi-bin)
|
||||
%s.
|
||||
OR
|
||||
Run the web server that comes with ViewVC at
|
||||
%s.
|
||||
""" % (
|
||||
os.path.join(ROOT_DIR, 'viewvc.conf'),
|
||||
os.path.join(ROOT_DIR, 'bin', 'cgi', 'viewvc.cgi'),
|
||||
os.path.join(ROOT_DIR, 'standalone.py'))
|
||||
2) Either configure an existing web server to run
|
||||
%s.
|
||||
|
||||
Or, copy %s to an
|
||||
already-configured cgi-bin directory.
|
||||
|
||||
Or, use the standalone server provided by this distribution at
|
||||
%s.
|
||||
""" % (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', 'standalone.py'))
|
||||
|
@@ -359,6 +359,9 @@ hr_ignore_keyword_subst = 1
|
||||
#
|
||||
hr_intraline = 0
|
||||
|
||||
# allow on-the-fly generation of repository tarballs
|
||||
allow_tar = 0
|
||||
|
||||
# allow annotation of files.
|
||||
allow_annotate = 1
|
||||
|
||||
@@ -459,16 +462,14 @@ highlight_convert_tabs = 2
|
||||
use_php = 0
|
||||
|
||||
# path to php executable
|
||||
# (This should be set to the path of a PHP CLI executable, not the path
|
||||
# to a CGI executable. If you use a CGI executable, you may see "no input file
|
||||
# specified" or "force-cgi-redirect" errors instead of colorized source. The
|
||||
# output of "php -v" tells you whether an given executable is CLI or CGI.)
|
||||
php_exe_path = php
|
||||
# php_exe_path = /usr/local/bin/php
|
||||
# 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
|
||||
# documentation and download.
|
||||
|
@@ -64,7 +64,7 @@
|
||||
|
||||
<li>Individually configurable virtual host support.</li>
|
||||
|
||||
<li>Line-based annotation/blame display (<em>CVS only</em>).</li>
|
||||
<li>Line-based annotation/blame display.</li>
|
||||
|
||||
<li>Revision graph capabilities (via integration with <a
|
||||
href="http://www.akhphd.au.dk/~bertho/cvsgraph/">CvsGraph</a>)
|
||||
@@ -72,7 +72,8 @@
|
||||
|
||||
<li>Syntax highlighting support (via integration with <a
|
||||
href="http://www.codento.com/people/mtr/genscript/">GNU
|
||||
enscript</a>).</li>
|
||||
enscript</a> or
|
||||
<a href="http://www.andre-simon.de/">Highlight</a>).</li>
|
||||
|
||||
<li><a href="http://www.mozilla.org/projects/bonsai/">Bonsai</a>-like
|
||||
repository query facilities.</li>
|
||||
|
@@ -57,28 +57,27 @@
|
||||
|
||||
<p>This document describes some of the things that you will need to
|
||||
consider, change, or handle when upgrading an existing ViewVC
|
||||
installation to a newer version.</p>
|
||||
or ViewCVS installation to a newer version.</p>
|
||||
|
||||
<p>Upgrading from an ancient version of ViewVC to the latest version
|
||||
<p>Upgrading from an ancient version to the latest version
|
||||
isn't necessarily a multi step process. The instructions are only
|
||||
organized that way. You can certainly upgrade in a single step.</p>
|
||||
|
||||
<p>It is always recommended to install the new version in a fresh
|
||||
directory and to carefully compare the configuration files. A
|
||||
possible approach is to name the directories
|
||||
<code>/usr/local/viewcvs-0.6</code>,
|
||||
<code>/usr/local/viewcvs-0.7</code> and so on and than create a
|
||||
symbolic link <code>viewcvs</code> pointing to the production
|
||||
<code>/usr/local/viewvc-1.0</code>,
|
||||
<code>/usr/local/viewcvs-1.1</code> and so on and than create a
|
||||
symbolic link <code>viewvc</code> pointing to the production
|
||||
version. This way you can easily test several versions and switch
|
||||
back, if your users start to complain. </p>
|
||||
back if your users start to complain.</p>
|
||||
|
||||
</div>
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-from-0-9">Upgrading From 0.9</h2>
|
||||
<h2 id="sec-from-0-9">Upgrading From ViewCVS 0.9</h2>
|
||||
|
||||
<p>This section discusses how to upgrade ViewCVS 0.9 to version 1.0 or
|
||||
a later version of the software.</p>
|
||||
<p>This section discusses how to upgrade ViewCVS 0.9 to ViewVC 1.0.</p>
|
||||
|
||||
<h3>CGI Stubs</h3>
|
||||
|
||||
@@ -161,7 +160,7 @@
|
||||
<h3>Templates</h3>
|
||||
|
||||
<p>The templates have changed drastically in this version of ViewVC.
|
||||
If you are using customized templates from 0.9 or earlier, you want
|
||||
If you are using customized templates from 0.9 or earlier, you will want
|
||||
to port your old customizations to the new template files instead of
|
||||
trying to get the old template files to work with the new ViewVC.</p>
|
||||
|
||||
@@ -982,7 +981,7 @@
|
||||
</div>
|
||||
<div class="section">
|
||||
|
||||
<h2 id="sec-from-0-8">Upgrading From 0.8</h2>
|
||||
<h2 id="sec-from-0-8">Upgrading From ViewCVS 0.8</h2>
|
||||
|
||||
<p>
|
||||
This section discusses how to upgrade ViewCVS 0.8 to version
|
||||
|
@@ -14,7 +14,7 @@ ViewVC requires the Python interpreter which you can download from
|
||||
|
||||
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
|
||||
RCS tools (rlog.exe, rcsdiff.exe, and co.exe) be installed on your computer.
|
||||
@@ -74,7 +74,7 @@ See the sections below for information on setting up optional features and
|
||||
troubleshooting. From here on <PYTHON_DIR> will stand for the Python root
|
||||
directory (usually something like C:\Python22) and <VIEWVC_INSTALL_DIR> will
|
||||
represent the directory where ViewVC has been installed to (default is
|
||||
C:\Program Files\viewvc-1.0-dev).
|
||||
C:\Program Files\viewvc-VERSION).
|
||||
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
SERVER CONFIGURATION
|
||||
@@ -404,7 +404,7 @@ KNOWN ISSUES
|
||||
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
|
||||
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
|
||||
with CVSNT. This is caused by a CVSNT bug, which is described at
|
||||
|
@@ -18,6 +18,13 @@
|
||||
|
||||
<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>
|
||||
|
||||
|
Reference in New Issue
Block a user