mirror of
https://github.com/vitalif/viewvc-4intranet
synced 2019-04-16 04:14:59 +03:00
Compare commits
54 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
030d381b88 | ||
![]() |
22d1e72c66 | ||
![]() |
0e7d4061ed | ||
![]() |
56dd2dcf28 | ||
![]() |
5932f24a68 | ||
![]() |
161421a20f | ||
![]() |
2a297c5361 | ||
![]() |
f43117b10f | ||
![]() |
a025237f7e | ||
![]() |
228db2fadb | ||
![]() |
0cd26cc79f | ||
![]() |
b808d5d1e8 | ||
![]() |
f843c054b7 | ||
![]() |
cf06a971e8 | ||
![]() |
bc8f3bdd4f | ||
![]() |
a6fcab67b0 | ||
![]() |
4069208316 | ||
![]() |
cfaa30b40f | ||
![]() |
97c5a82b7b | ||
![]() |
21bd391d62 | ||
![]() |
9d1476ef1d | ||
![]() |
151fcd05e6 | ||
![]() |
22400ddcfb | ||
![]() |
ae55530edc | ||
![]() |
112700a12c | ||
![]() |
cecffecf39 | ||
![]() |
bdac8697fe | ||
![]() |
3d9404b67d | ||
![]() |
7c50f55153 | ||
![]() |
368e4dc360 | ||
![]() |
964d8bb5f9 | ||
![]() |
846e8e46c5 | ||
![]() |
75c719bcde | ||
![]() |
fc8793bf15 | ||
![]() |
65935c40fd | ||
![]() |
a30d0b44cc | ||
![]() |
fded8462d2 | ||
![]() |
b1095ac763 | ||
![]() |
e54399a169 | ||
![]() |
4cc0db75be | ||
![]() |
74a9cbb2a0 | ||
![]() |
390e337a8e | ||
![]() |
ecdac77d5f | ||
![]() |
79158c2ee7 | ||
![]() |
96fdfbdbcf | ||
![]() |
361538da21 | ||
![]() |
6250d4134b | ||
![]() |
8589949521 | ||
![]() |
f12e262fa5 | ||
![]() |
f379070697 | ||
![]() |
d47fc0ff3b | ||
![]() |
f2b6f0ba86 | ||
![]() |
ddbe150be1 | ||
![]() |
6b5ed7c857 |
29
CHANGES
29
CHANGES
@@ -1,3 +1,32 @@
|
||||
Version 1.1.13 (released 23-Jan-2012)
|
||||
|
||||
* fix svndbadmin failure on deleted paths under Subversion 1.7 (issue #499)
|
||||
* fix annotation of files in svn roots with non-URI-safe paths
|
||||
* fix stray annotation warning in markup display of images
|
||||
* more gracefully handle attempts to display binary content (issue #501)
|
||||
|
||||
Version 1.1.12 (released 03-Nov-2011)
|
||||
|
||||
* fix path display in patch and certain diff views (issue #485)
|
||||
* fix broken cvsdb glob searching (issue 486)
|
||||
* allow svn revision specifiers to have leading r's (issue #441, #448)
|
||||
* allow environmental override of configuration location (issue #494)
|
||||
* fix exception HTML-escaping non-string data under WSGI (issue #454)
|
||||
* add links to root logs from roots view (issue #470)
|
||||
* use Pygments lexer-guessing functionality (issue #495)
|
||||
|
||||
Version 1.1.11 (released 17-May-2011)
|
||||
|
||||
* security fix: remove user-reachable override of cvsdb row limit
|
||||
* fix broken standalone.py -c and -d options handling
|
||||
* add --help option to standalone.py
|
||||
* fix stack trace when asked to checkout a directory (issue #478)
|
||||
* improve memory usage and speed of revision log markup (issue #477)
|
||||
* fix broken annotation view in CVS keyword-bearing files (issue #479)
|
||||
* warn users when query results are incomplete (issue #433)
|
||||
* avoid parsing errors on RCS newphrases in the admin section (issue #483)
|
||||
* make rlog parsing code more robust in certain error cases (issue #444)
|
||||
|
||||
Version 1.1.10 (released 15-Mar-2011)
|
||||
|
||||
* fix stack trace in Subversion revision info logic (issue #475, issue #476)
|
||||
|
55
INSTALL
55
INSTALL
@@ -34,7 +34,7 @@ Congratulations on getting this far. :-)
|
||||
(http://www.python.org/)
|
||||
* Subversion, Version Control System, 1.3.1 or later
|
||||
(binary installation and Python bindings)
|
||||
(http://subversion.tigris.org/)
|
||||
(http://subversion.apache.org/)
|
||||
|
||||
Optional:
|
||||
|
||||
@@ -176,7 +176,24 @@ APACHE CONFIGURATION
|
||||
or /etc/local. Use the vendor documentation or the find utility if
|
||||
in doubt.
|
||||
|
||||
2) Configure Apache to expose ViewVC to users at the URL of your choice.
|
||||
2) Depending on how your Apache configuration is setup by default, you
|
||||
might need to explicitly allow high-level access to the ViewVC
|
||||
install location.
|
||||
|
||||
<Directory <VIEWVC_INSTALLATION_DIRECTORY>>
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
For example, if ViewVC is installed in /usr/local/viewvc-1.0 on
|
||||
your system:
|
||||
|
||||
<Directory /usr/local/viewvc-1.0>
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
3) Configure Apache to expose ViewVC to users at the URL of your choice.
|
||||
|
||||
ViewVC provides several different ways to do this. Choose one of
|
||||
the following methods:
|
||||
@@ -187,13 +204,13 @@ APACHE CONFIGURATION
|
||||
The ScriptAlias directive is very useful for pointing
|
||||
directly to the viewvc.cgi script. Simply insert a line containing
|
||||
|
||||
ScriptAlias /viewvc <VIEWVC_INSTALLATION_DIRECTORY>/bin/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/bin/cgi/viewvc.cgi
|
||||
ScriptAlias /query /usr/local/viewvc-1.0/bin/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
|
||||
|
||||
----------------------------------------
|
||||
METHOD B: CGI mode in cgi-bin directory
|
||||
@@ -201,6 +218,10 @@ APACHE CONFIGURATION
|
||||
Copy the CGI scripts from
|
||||
<VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/*.cgi
|
||||
to the /cgi-bin/ directory configured in your httpd.conf file.
|
||||
|
||||
You can override configuration file location using:
|
||||
|
||||
SetEnv VIEWVC_CONF_PATHNAME /etc/viewvc.conf
|
||||
|
||||
------------------------------------------
|
||||
METHOD C: CGI mode in ExecCGI'd directory
|
||||
@@ -210,8 +231,8 @@ APACHE CONFIGURATION
|
||||
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:
|
||||
|
||||
Options +ExecCGI
|
||||
AddHandler cgi-script .cgi
|
||||
Options +ExecCGI
|
||||
AddHandler cgi-script .cgi
|
||||
|
||||
Note: For this to work mod_cgi has to be loaded. And for the .htaccess file
|
||||
to be effective, "AllowOverride All" or "AllowOverride Options FileInfo"
|
||||
@@ -227,6 +248,10 @@ APACHE CONFIGURATION
|
||||
In httpd.conf, make sure that "AllowOverride All" or at least
|
||||
"AllowOverride FileInfo Options" are enabled for the directory
|
||||
you copied the files to.
|
||||
|
||||
You can override configuration file location using:
|
||||
|
||||
SetEnv VIEWVC_CONF_PATHNAME /etc/viewvc.conf
|
||||
|
||||
Note: If you are using Mod_Python under Apache 1.3 the tarball generation
|
||||
feature may not work because it uses multithreading. This works fine
|
||||
@@ -278,13 +303,6 @@ APACHE CONFIGURATION
|
||||
ScriptAlias /viewvc /usr/local/viewvc/bin/wsgi/viewvc.fcgi
|
||||
ScriptAlias /query /usr/local/viewvc/bin/wsgi/query.fcgi
|
||||
|
||||
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". Other systems use "apachectl restart".
|
||||
|
||||
4) [Optional] Add access control.
|
||||
|
||||
In your httpd.conf you can control access to certain modules by
|
||||
@@ -306,7 +324,14 @@ APACHE CONFIGURATION
|
||||
http://<server_name>/viewvc/~checkout~/<module_name>
|
||||
http://<server_name>/viewvc/<module_name>.tar.gz?view=tar
|
||||
|
||||
5) Optional: Protect your ViewVC instance from server-whacking webcrawlers.
|
||||
5) 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". Other systems use "apachectl restart".
|
||||
|
||||
6) Optional: Protect your ViewVC instance from server-whacking webcrawlers.
|
||||
|
||||
As ViewVC is a web-based application which each page containing various
|
||||
links to other pages and views, you can expect your server's performance
|
||||
|
@@ -15,7 +15,7 @@
|
||||
|
||||
<blockquote>
|
||||
|
||||
<p><strong>Copyright © 1999-2011 The ViewCVS Group. All rights
|
||||
<p><strong>Copyright © 1999-2012 The ViewCVS Group. All rights
|
||||
reserved.</strong></p>
|
||||
|
||||
<p>By using ViewVC, you agree to the terms and conditions set forth
|
||||
@@ -62,6 +62,7 @@
|
||||
<li>March 18, 2009 — copyright years updated</li>
|
||||
<li>March 29, 2010 — copyright years updated</li>
|
||||
<li>February 18, 2011 — copyright years updated</li>
|
||||
<li>January 23, 2012 — copyright years updated</li>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -76,8 +76,6 @@ except ImportError:
|
||||
|
||||
class Options:
|
||||
port = 49152 # default TCP/IP port used for the server
|
||||
start_gui = 0 # No GUI unless requested.
|
||||
daemon = 0 # stay in the foreground by default
|
||||
repositories = {} # use default repositories specified in config
|
||||
host = sys.platform == 'mac' and '127.0.0.1' or 'localhost'
|
||||
script_alias = 'viewvc'
|
||||
@@ -707,13 +705,66 @@ def gui(host, port):
|
||||
|
||||
# --- command-line interface: ----------------------------------------------
|
||||
|
||||
def cli(argv):
|
||||
|
||||
def usage():
|
||||
clean_options = Options()
|
||||
cmd = os.path.basename(sys.argv[0])
|
||||
port = clean_options.port
|
||||
host = clean_options.host
|
||||
script_alias = clean_options.script_alias
|
||||
sys.stderr.write("""Usage: %(cmd)s [OPTIONS]
|
||||
|
||||
Run a simple, standalone HTTP server configured to serve up ViewVC requests.
|
||||
|
||||
Options:
|
||||
|
||||
--config-file=FILE (-c) Read configuration options from FILE. If not
|
||||
specified, ViewVC will look for a configuration
|
||||
file in its installation tree, falling back to
|
||||
built-in default values. (Not valid in GUI mode.)
|
||||
|
||||
--daemon (-d) Background the server process.
|
||||
|
||||
--gui (-g) Pop up a graphical configuration interface.
|
||||
Requires a valid X11 display connection.
|
||||
|
||||
--help Show this usage message and exit.
|
||||
|
||||
--host=HOSTNAME (-h) Listen on HOSTNAME. Required for access from a
|
||||
remote machine. [default: %(host)s]
|
||||
|
||||
--htpasswd-file=FILE Authenticate incoming requests, validating against
|
||||
against FILE, which is an Apache HTTP Server
|
||||
htpasswd file. (CRYPT only; no DIGEST support.)
|
||||
|
||||
--port=PORT (-p) Listen on PORT. [default: %(port)d]
|
||||
|
||||
--repository=PATH (-r) Serve the Subversion or CVS repository located
|
||||
at PATH. This option may be used more than once.
|
||||
|
||||
--script-alias=PATH (-s) Use PATH as the virtual script location (similar
|
||||
to Apache HTTP Server's ScriptAlias directive).
|
||||
For example, "--script-alias=repo/view" will serve
|
||||
ViewVC at "http://HOSTNAME:PORT/repo/view".
|
||||
[default: %(script_alias)s]
|
||||
""" % locals())
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def badusage(errstr):
|
||||
cmd = os.path.basename(sys.argv[0])
|
||||
sys.stderr.write("ERROR: %s\n\n"
|
||||
"Try '%s --help' for detailed usage information.\n"
|
||||
% (errstr, cmd))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(argv):
|
||||
"""Command-line interface (looks at argv to decide what to do)."""
|
||||
import getopt
|
||||
class BadUsage(Exception): pass
|
||||
|
||||
short_opts = string.join(['c',
|
||||
'd:',
|
||||
short_opts = string.join(['c:',
|
||||
'd',
|
||||
'g',
|
||||
'h:',
|
||||
'p:',
|
||||
@@ -723,6 +774,7 @@ def cli(argv):
|
||||
long_opts = ['daemon',
|
||||
'config-file=',
|
||||
'gui',
|
||||
'help',
|
||||
'host=',
|
||||
'htpasswd-file=',
|
||||
'port=',
|
||||
@@ -730,113 +782,98 @@ def cli(argv):
|
||||
'script-alias=',
|
||||
]
|
||||
|
||||
try:
|
||||
opt_daemon = 0
|
||||
opt_gui = 0
|
||||
opt_host = None
|
||||
opt_port = None
|
||||
opt_htpasswd_file = None
|
||||
opt_config_file = None
|
||||
opt_script_alias = None
|
||||
opt_repositories = []
|
||||
|
||||
# Parse command-line options.
|
||||
try:
|
||||
opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
|
||||
for opt, val in opts:
|
||||
if opt in ('-g', '--gui'):
|
||||
options.start_gui = 1
|
||||
elif opt in ('-r', '--repository'):
|
||||
if options.repositories: # option may be used more than once:
|
||||
num = len(options.repositories.keys())+1
|
||||
symbolic_name = "Repository"+str(num)
|
||||
options.repositories[symbolic_name] = val
|
||||
else:
|
||||
options.repositories["Development"] = val
|
||||
elif opt in ('-d', '--daemon'):
|
||||
options.daemon = 1
|
||||
elif opt in ('-p', '--port'):
|
||||
try:
|
||||
options.port = int(val)
|
||||
except ValueError:
|
||||
raise BadUsage, "Port '%s' is not a valid port number" % (val)
|
||||
elif opt in ('-h', '--host'):
|
||||
options.host = val
|
||||
elif opt in ('-s', '--script-alias'):
|
||||
options.script_alias = \
|
||||
string.join(filter(None, string.split(val, '/')), '/')
|
||||
elif opt in ('-c', '--config-file'):
|
||||
if not os.path.isfile(val):
|
||||
raise BadUsage, "'%s' does not appear to be a valid " \
|
||||
"configuration file." % (val)
|
||||
options.config_file = val
|
||||
elif opt in ('-c', '--htpasswd-file'):
|
||||
if not os.path.isfile(val):
|
||||
raise BadUsage, "'%s' does not appear to be a valid " \
|
||||
"htpasswd file." % (val)
|
||||
if not has_crypt:
|
||||
raise BadUsage, "Unable to locate suitable `crypt' module for use " \
|
||||
"with --htpasswd-file option. If your Python " \
|
||||
"distribution does not include this module (as is " \
|
||||
"the case on many non-Unix platforms), consider " \
|
||||
"installing the `fcrypt' module instead (see " \
|
||||
"http://carey.geek.nz/code/python-fcrypt/)."
|
||||
options.htpasswd_file = val
|
||||
if options.start_gui and options.config_file:
|
||||
raise BadUsage, "--config-file option is not valid in GUI mode."
|
||||
if not options.start_gui and not options.port:
|
||||
raise BadUsage, "You must supply a valid port, or run in GUI mode."
|
||||
if options.daemon:
|
||||
pid = os.fork()
|
||||
if pid != 0:
|
||||
sys.exit()
|
||||
if options.start_gui:
|
||||
gui(options.host, options.port)
|
||||
return
|
||||
elif options.port:
|
||||
def ready(server):
|
||||
print 'server ready at %s%s' % (server.url, options.script_alias)
|
||||
serve(options.host, options.port, ready)
|
||||
return
|
||||
except (getopt.error, BadUsage), err:
|
||||
cmd = os.path.basename(sys.argv[0])
|
||||
port = options.port
|
||||
host = options.host
|
||||
script_alias = options.script_alias
|
||||
if str(err):
|
||||
sys.stderr.write("ERROR: %s\n\n" % (str(err)))
|
||||
sys.stderr.write("""Usage: %(cmd)s [OPTIONS]
|
||||
if opt in ['--help']:
|
||||
usage()
|
||||
elif opt in ['-g', '--gui']:
|
||||
opt_gui = 1
|
||||
elif opt in ['-r', '--repository']: # may be used more than once
|
||||
opt_repositories.append(val)
|
||||
elif opt in ['-d', '--daemon']:
|
||||
opt_daemon = 1
|
||||
elif opt in ['-p', '--port']:
|
||||
opt_port = val
|
||||
elif opt in ['-h', '--host']:
|
||||
opt_host = val
|
||||
elif opt in ['-s', '--script-alias']:
|
||||
opt_script_alias = val
|
||||
elif opt in ['-c', '--config-file']:
|
||||
opt_config_file = val
|
||||
elif opt in ['--htpasswd-file']:
|
||||
opt_htpasswd_file = val
|
||||
except getopt.error, err:
|
||||
badusage(str(err))
|
||||
|
||||
Run a simple, standalone HTTP server configured to serve up ViewVC
|
||||
requests.
|
||||
# Validate options that need validating.
|
||||
class BadUsage(Exception): pass
|
||||
try:
|
||||
if opt_port is not None:
|
||||
try:
|
||||
options.port = int(opt_port)
|
||||
except ValueError:
|
||||
raise BadUsage("Port '%s' is not a valid port number" % (opt_port))
|
||||
if not options.port:
|
||||
raise BadUsage("You must supply a valid port.")
|
||||
if opt_htpasswd_file is not None:
|
||||
if not os.path.isfile(opt_htpasswd_file):
|
||||
raise BadUsage("'%s' does not appear to be a valid htpasswd file."
|
||||
% (opt_htpasswd_file))
|
||||
if not has_crypt:
|
||||
raise BadUsage("Unable to locate suitable `crypt' module for use "
|
||||
"with --htpasswd-file option. If your Python "
|
||||
"distribution does not include this module (as is "
|
||||
"the case on many non-Unix platforms), consider "
|
||||
"installing the `fcrypt' module instead (see "
|
||||
"http://carey.geek.nz/code/python-fcrypt/).")
|
||||
options.htpasswd_file = opt_htpasswd_file
|
||||
if opt_config_file is not None:
|
||||
if not os.path.isfile(opt_config_file):
|
||||
raise BadUsage("'%s' does not appear to be a valid configuration file."
|
||||
% (opt_config_file))
|
||||
options.config_file = opt_config_file
|
||||
if opt_host is not None:
|
||||
options.host = opt_host
|
||||
if opt_script_alias is not None:
|
||||
options.script_alias = string.join(filter(None,
|
||||
string.split(opt_script_alias,
|
||||
'/')),
|
||||
'/')
|
||||
for repository in opt_repositories:
|
||||
if not options.repositories.has_key('Development'):
|
||||
rootname = 'Development'
|
||||
else:
|
||||
rootname = 'Repository%d' % (len(options.repositories.keys()) + 1)
|
||||
options.repositories[rootname] = repository
|
||||
except BadUsage, err:
|
||||
badusage(str(err))
|
||||
|
||||
# Fork if we're in daemon mode.
|
||||
if opt_daemon:
|
||||
pid = os.fork()
|
||||
if pid != 0:
|
||||
sys.exit()
|
||||
|
||||
Options:
|
||||
# Finaly, start the server.
|
||||
if opt_gui:
|
||||
gui(options.host, options.port)
|
||||
else:
|
||||
def ready(server):
|
||||
print 'server ready at %s%s' % (server.url, options.script_alias)
|
||||
serve(options.host, options.port, ready)
|
||||
|
||||
--config-file=PATH (-c) Use the file at PATH as the ViewVC configuration
|
||||
file. If not specified, ViewVC will try to use
|
||||
the configuration file in its installation tree;
|
||||
otherwise, built-in default values are used.
|
||||
(Not valid in GUI mode.)
|
||||
|
||||
--daemon (-d) Background the server process.
|
||||
|
||||
--host=HOST (-h) Start the server listening on HOST. You need
|
||||
to provide the hostname if you want to
|
||||
access the standalone server from a remote
|
||||
machine. [default: %(host)s]
|
||||
|
||||
--port=PORT (-p) Start the server on the given PORT.
|
||||
[default: %(port)d]
|
||||
|
||||
--repository=PATH (-r) Serve up the Subversion or CVS repository located
|
||||
at PATH. This option may be used more than once.
|
||||
|
||||
--script-alias=PATH (-s) Specify the ScriptAlias, the artificial path
|
||||
location that at which ViewVC appears to be
|
||||
located. For example, if your ScriptAlias is
|
||||
"cgi-bin/viewvc", then ViewVC will be accessible
|
||||
at "http://%(host)s:%(port)s/cgi-bin/viewvc".
|
||||
[default: %(script_alias)s]
|
||||
|
||||
--htpasswd-file=FILE Demand authentication from clients, validating
|
||||
authentication credentials against FILE, which is
|
||||
an Apache htpasswd file that employs CRYPT
|
||||
encryption. (Sorry, no DIGEST support yet.)
|
||||
|
||||
--gui (-g) Pop up a graphical interface for serving and
|
||||
testing ViewVC. NOTE: this requires a valid
|
||||
X11 display connection.
|
||||
""" % locals())
|
||||
|
||||
if __name__ == '__main__':
|
||||
options = Options()
|
||||
cli(sys.argv)
|
||||
main(sys.argv)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2004-2008 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2004-2012 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2004-2007 James Henstridge
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
@@ -153,7 +153,7 @@ class SvnRev:
|
||||
fsroot = self._get_root_for_rev(rev)
|
||||
|
||||
# find changes in the revision
|
||||
editor = svn.repos.RevisionChangeCollector(repo.fs, rev)
|
||||
editor = svn.repos.ChangeCollector(repo.fs, fsroot)
|
||||
e_ptr, e_baton = svn.delta.make_editor(editor)
|
||||
svn.repos.svn_repos_replay(fsroot, e_ptr, e_baton)
|
||||
|
||||
@@ -164,21 +164,33 @@ class SvnRev:
|
||||
continue
|
||||
|
||||
# deal with the change types we handle
|
||||
action = None
|
||||
base_root = None
|
||||
base_path = change.base_path
|
||||
if change.base_path:
|
||||
base_root = self._get_root_for_rev(change.base_rev)
|
||||
|
||||
if not change.path:
|
||||
|
||||
# figure out what kind of change this is, and get a diff
|
||||
# object for it. note that prior to 1.4 Subversion's
|
||||
# bindings didn't give us change.action, but that's okay
|
||||
# because back then deleted paths always had a change.path
|
||||
# of None.
|
||||
if hasattr(change, 'action') \
|
||||
and change.action == svn.repos.CHANGE_ACTION_DELETE:
|
||||
action = 'remove'
|
||||
elif not change.path:
|
||||
action = 'remove'
|
||||
elif change.added:
|
||||
action = 'add'
|
||||
else:
|
||||
action = 'change'
|
||||
|
||||
diffobj = svn.fs.FileDiff(base_root and base_root or None,
|
||||
base_root and change.base_path or None,
|
||||
change.path and fsroot or None,
|
||||
change.path and change.path or None)
|
||||
if action == 'remove':
|
||||
diffobj = svn.fs.FileDiff(base_root, base_path, None, None)
|
||||
else:
|
||||
diffobj = svn.fs.FileDiff(base_root, base_path,
|
||||
fsroot, change.path)
|
||||
|
||||
diff_fp = diffobj.get_pipe()
|
||||
plus, minus = _get_diff_counts(diff_fp)
|
||||
self.changes.append((path, action, plus, minus))
|
||||
@@ -288,7 +300,7 @@ def usage():
|
||||
located at REPOS-PATH.
|
||||
|
||||
Usage: 1. %s [-v] rebuild REPOS-PATH
|
||||
2. %s [-v] update REPOS-PATH [REV:[REV2]] [--force]
|
||||
2. %s [-v] update REPOS-PATH [REV[:REV2]] [--force]
|
||||
3. %s [-v] purge REPOS-PATH
|
||||
|
||||
1. Rebuild the commit database information for the repository located
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -86,8 +86,8 @@
|
||||
|
||||
## cvs_roots: Specifies each of the CVS roots on your system and
|
||||
## assigns names to them. Each root should be given by a "name: path"
|
||||
## value. Multiple roots should be separated by commas and can be
|
||||
## placed on separate lines.
|
||||
## value (where the path is an absolute filesystem path). Multiple roots
|
||||
## should be separated by commas and can be placed on separate lines.
|
||||
##
|
||||
## Example:
|
||||
## cvs_roots = cvsroot: /opt/cvs/repos1,
|
||||
@@ -97,8 +97,13 @@
|
||||
|
||||
## svn_roots: Specifies each of the Subversion roots (repositories) on
|
||||
## your system and assigns names to them. Each root should be given by
|
||||
## a "name: path" value. Multiple roots should be separated by commas
|
||||
## and can be placed on separate lines.
|
||||
## a "name: path" value (where the path is an absolute filesystem path).
|
||||
## Multiple roots should be separated by commas and can be placed on
|
||||
## separate lines.
|
||||
##
|
||||
## NOTE: ViewVC offers *experimental* support for displaying remote
|
||||
## Subversion repositories. Simply use the repository's URL instead
|
||||
## of a local filesystem path when defining the root.
|
||||
##
|
||||
## Example:
|
||||
## svn_roots = svnrepos: /opt/svn/,
|
||||
@@ -109,7 +114,8 @@
|
||||
## root_parents: Specifies a list of directories under which any
|
||||
## number of repositories may reside. You can specify multiple root
|
||||
## parents separated by commas or new lines, each of which is of the
|
||||
## form "path: type" (where type is either "cvs" or "svn").
|
||||
## form "path: type" (where the type is either "cvs" or "svn", and
|
||||
## the path is an absolute filesystem path).
|
||||
##
|
||||
## Rather than force you to add a new entry to 'cvs_roots' or
|
||||
## 'svn_roots' each time you create a new repository, ViewVC rewards
|
||||
@@ -345,7 +351,43 @@
|
||||
## allowed_views: List the ViewVC views which are enabled. Views not
|
||||
## in this comma-delited list will not be served (or, will return an
|
||||
## error on attempted access).
|
||||
## Possible values: "annotate", "co", "diff", "markup", "roots", "tar"
|
||||
##
|
||||
## Valid items for this list include: "annotate", "co", "diff", "markup",
|
||||
## "roots", "tar".
|
||||
##
|
||||
## ----------+---------------------------------------------------------
|
||||
## VIEW | DESCRIPTION
|
||||
## ----------+---------------------------------------------------------
|
||||
## annotate | The 'annotate' view shows the contents of a single
|
||||
## | revision of a versioned file in exactly the same way as
|
||||
## | the markup view, but with additional line-by-line
|
||||
## | change attribution (the revision number, author, etc.
|
||||
## | the most recent edit to that line of text as of the
|
||||
## | displayed version).
|
||||
## ----------+---------------------------------------------------------
|
||||
## co | The 'co' (aka "checkout" or "download") view isn't
|
||||
## | really a branded view at all, but allows for direct
|
||||
## | downloading of the contents of a single revision of a
|
||||
## | versioned file.
|
||||
## ----------+---------------------------------------------------------
|
||||
## diff | The 'diff' view displays line-based differences between
|
||||
## | two revisions of a versioned file in a variety of
|
||||
## | different user-selectable formats.
|
||||
## ----------+---------------------------------------------------------
|
||||
## markup | The 'markup' view shows the contents of a single
|
||||
## | revision of a versioned file, with syntax highlighting
|
||||
## | where possible and enabled. It can also optionally
|
||||
## | show change log information for that revision of the
|
||||
## | file.
|
||||
## ----------+---------------------------------------------------------
|
||||
## roots | The 'roots' view is a simple listing of the various
|
||||
## | repositories which ViewVC has been configured to serve
|
||||
## | to users.
|
||||
## ----------+---------------------------------------------------------
|
||||
## tar | The 'tar' view isn't a branded view, but generates
|
||||
## | a GNU Tar archive file containing a single versioned
|
||||
## | directory and its contents (recursively).
|
||||
## ----------+---------------------------------------------------------
|
||||
##
|
||||
#allowed_views = annotate, diff, markup, roots
|
||||
|
||||
@@ -603,7 +645,13 @@
|
||||
#short_log_len = 80
|
||||
|
||||
## enable_syntax_coloration: Should we colorize known file content
|
||||
## syntaxes? [Requires Pygments Python module]
|
||||
## syntaxes?
|
||||
##
|
||||
## NOTE: This feature requires the Pygments Python module
|
||||
## (http://pygments.org) and works only when ViewVC can determine the
|
||||
## MIME content type of the file whose contents it wishes to colorize.
|
||||
## Use the 'mime_types_files' configuration option to specify MIME
|
||||
## type mapping files useful for making that determination.
|
||||
##
|
||||
#enable_syntax_coloration = 1
|
||||
|
||||
@@ -797,6 +845,14 @@
|
||||
## row_limit: Maximum number of rows returned by a given normal query
|
||||
## to the database.
|
||||
##
|
||||
## NOTE: This limits the amount of data provided to ViewVC by the
|
||||
## database. It is from this already-reduced data set that ViewVC
|
||||
## builds the query response it presents to the user, which may or may
|
||||
## not include still more limiting via the query form's 'limit'
|
||||
## parameter. In other words, there is no value which the user can use
|
||||
## in the query form's 'limit' parameter which will cause more data to
|
||||
## be returned by the database for ViewVC to process.
|
||||
##
|
||||
#row_limit = 1000
|
||||
|
||||
## rss_row_limit: Maximum number of rows returned by a given query to
|
||||
@@ -804,6 +860,9 @@
|
||||
## that RSS readers tend to poll regularly for new data, you might want
|
||||
## to keep this set to a conservative number.)
|
||||
##
|
||||
## See also the `NOTE' for the 'row_limit' option, which applies here
|
||||
## as well.
|
||||
##
|
||||
#rss_row_limit = 100
|
||||
|
||||
## check_database_for_root: Check if the repository is found in the
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>ViewVC 1.0 Template Authoring Guide</title>
|
||||
<title>ViewVC 1.1 Template Authoring Guide</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: rgb(180,193,205);
|
||||
@@ -38,13 +38,13 @@ td {
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>ViewVC 1.0 Template Authoring Guide</h1>
|
||||
<h1>ViewVC 1.1 Template Authoring Guide</h1>
|
||||
|
||||
<div class="h2">
|
||||
<h2 id="introduction">Introduction</h2>
|
||||
|
||||
<p>This document represents an (unfinished) attempt at providing
|
||||
documentation for how to customize ViewVC 1.0-dev's HTML output via
|
||||
instructions for how to customize ViewVC's HTML output via
|
||||
modification of its templates.</p>
|
||||
|
||||
</div>
|
||||
@@ -802,6 +802,11 @@ td {
|
||||
<td>String</td>
|
||||
<td>This is a URL for the markup view of the left file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">patch_href</td>
|
||||
<td>String</td>
|
||||
<td>URL of the patch view for the file.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">raw_diff</td>
|
||||
<td>String</td>
|
||||
@@ -1821,6 +1826,14 @@ td {
|
||||
<td>Indicates how query results are being sorted. Possible values:
|
||||
<tt>date</tt>, <tt>author</tt>, and <tt>file</tt>.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">row_limit_reached</td>
|
||||
<td>Boolean</td>
|
||||
<td>Indicates whether the internal database row limit threshold (set
|
||||
via the <code>cvsdb.row_limit</code>
|
||||
and <code>cvsdb.rss_row_limit</code> configuration options) was
|
||||
reached by the query.</td>
|
||||
</tr>
|
||||
<tr class="varlevel1">
|
||||
<td class="varname">show_branch</td>
|
||||
<td>Boolean</td>
|
||||
@@ -2170,6 +2183,12 @@ td {
|
||||
<td>String</td>
|
||||
<td>Log message of last modification to the root.</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">roots.log_href</td>
|
||||
<td>String</td>
|
||||
<td>URL of log revision view for the top-most (root) directory of
|
||||
the root (repository).</td>
|
||||
</tr>
|
||||
<tr class="varlevel2">
|
||||
<td class="varname">roots.name</td>
|
||||
<td>String</td>
|
||||
|
@@ -1192,13 +1192,6 @@ th.caption {
|
||||
commands to back out changes instead showing a normal query result
|
||||
page</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>limit=<var>LIMIT</var></code></td>
|
||||
<td>optional</td>
|
||||
<td>maximum number of file-revisions to process during a
|
||||
query. Default is value of <code>row_limit</code> configuration
|
||||
option</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>limit_changes=<var>LIMIT_CHANGES</var></code></td>
|
||||
<td>optional</td>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
@@ -74,7 +74,8 @@ class HTMLBlameSource:
|
||||
self.path_parts = path_parts
|
||||
self.diff_url = diff_url
|
||||
self.include_url = include_url
|
||||
self.annotation, self.revision = self.repos.annotate(path_parts, opt_rev)
|
||||
self.annotation, self.revision = self.repos.annotate(path_parts, opt_rev,
|
||||
True)
|
||||
|
||||
def __getitem__(self, idx):
|
||||
item = self.annotation.__getitem__(idx)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
79
lib/cvsdb.py
79
lib/cvsdb.py
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -38,13 +38,12 @@ error = "cvsdb error"
|
||||
## complient database interface
|
||||
|
||||
class CheckinDatabase:
|
||||
def __init__(self, host, port, user, passwd, database, row_limit):
|
||||
def __init__(self, host, port, user, passwd, database):
|
||||
self._host = host
|
||||
self._port = port
|
||||
self._user = user
|
||||
self._passwd = passwd
|
||||
self._database = database
|
||||
self._row_limit = row_limit
|
||||
self._version = None
|
||||
|
||||
## database lookup caches
|
||||
@@ -169,6 +168,9 @@ class CheckinDatabase:
|
||||
|
||||
return list
|
||||
|
||||
def GetCommitsTable(self):
|
||||
return self._version >= 1 and 'commits' or 'checkins'
|
||||
|
||||
def GetTableList(self):
|
||||
sql = "SHOW TABLES"
|
||||
cursor = self.db.cursor()
|
||||
@@ -309,8 +311,7 @@ class CheckinDatabase:
|
||||
minus_count = commit.GetMinusCount() or '0'
|
||||
description_id = self.GetDescriptionID(commit.GetDescription())
|
||||
|
||||
commits_table = self._version >= 1 and 'commits' or 'checkins'
|
||||
sql = "REPLACE INTO %s" % (commits_table)
|
||||
sql = "REPLACE INTO %s" % (self.GetCommitsTable())
|
||||
sql = sql + \
|
||||
" (type,ci_when,whoid,repositoryid,dirid,fileid,revision,"\
|
||||
" stickytag,branchid,addedlines,removedlines,descid)"\
|
||||
@@ -351,9 +352,17 @@ class CheckinDatabase:
|
||||
match = " LIKE "
|
||||
elif query_entry.match == "glob":
|
||||
match = " REGEXP "
|
||||
# use fnmatch to translate the glob into a regexp
|
||||
# Use fnmatch to translate the glob into a regular
|
||||
# expression. Sadly, we have to account for the fact
|
||||
# that in Python 2.6, fnmatch.translate() started
|
||||
# sticking '\Z(?ms)' at the end of the regular
|
||||
# expression instead of just '$', and doesn't prepend
|
||||
# the '^'.
|
||||
data = fnmatch.translate(data)
|
||||
if data[0] != '^': data = '^' + data
|
||||
if data[0] != '^':
|
||||
data = '^' + data
|
||||
if data[-7:] == '\Z(?ms)':
|
||||
data = data[:-7] + '$'
|
||||
elif query_entry.match == "regex":
|
||||
match = " REGEXP "
|
||||
elif query_entry.match == "notregex":
|
||||
@@ -363,8 +372,8 @@ class CheckinDatabase:
|
||||
|
||||
return "(%s)" % (string.join(sqlList, " OR "))
|
||||
|
||||
def CreateSQLQueryString(self, query):
|
||||
commits_table = self._version >= 1 and 'commits' or 'checkins'
|
||||
def CreateSQLQueryString(self, query, detect_leftover=0):
|
||||
commits_table = self.GetCommitsTable()
|
||||
tableList = [(commits_table, None)]
|
||||
condList = []
|
||||
|
||||
@@ -444,13 +453,14 @@ class CheckinDatabase:
|
||||
conditions = string.join(joinConds + condList, " AND ")
|
||||
conditions = conditions and "WHERE %s" % conditions
|
||||
|
||||
## limit the number of rows requested or we could really slam
|
||||
## a server with a large database
|
||||
## apply the query's row limit, if any (so we avoid really
|
||||
## slamming a server with a large database)
|
||||
limit = ""
|
||||
if query.limit:
|
||||
limit = "LIMIT %s" % (str(query.limit))
|
||||
elif self._row_limit:
|
||||
limit = "LIMIT %s" % (str(self._row_limit))
|
||||
if detect_leftover:
|
||||
limit = "LIMIT %s" % (str(query.limit + 1))
|
||||
else:
|
||||
limit = "LIMIT %s" % (str(query.limit))
|
||||
|
||||
sql = "SELECT %s.* FROM %s %s %s %s" \
|
||||
% (commits_table, tables, conditions, order_by, limit)
|
||||
@@ -458,14 +468,20 @@ class CheckinDatabase:
|
||||
return sql
|
||||
|
||||
def RunQuery(self, query):
|
||||
sql = self.CreateSQLQueryString(query)
|
||||
sql = self.CreateSQLQueryString(query, 1)
|
||||
cursor = self.db.cursor()
|
||||
cursor.execute(sql)
|
||||
query.SetExecuted()
|
||||
row_count = 0
|
||||
|
||||
while 1:
|
||||
row = cursor.fetchone()
|
||||
if not row:
|
||||
break
|
||||
row_count = row_count + 1
|
||||
if query.limit and (row_count > query.limit):
|
||||
query.SetLimitReached()
|
||||
break
|
||||
|
||||
(dbType, dbCI_When, dbAuthorID, dbRepositoryID, dbDirID,
|
||||
dbFileID, dbRevision, dbStickyTag, dbBranchID, dbAddedLines,
|
||||
@@ -504,13 +520,12 @@ class CheckinDatabase:
|
||||
if file_id == None:
|
||||
return None
|
||||
|
||||
commits_table = self._version >= 1 and 'commits' or 'checkins'
|
||||
sql = "SELECT * FROM %s WHERE "\
|
||||
" repositoryid=%%s "\
|
||||
" AND dirid=%%s"\
|
||||
" AND fileid=%%s"\
|
||||
" AND revision=%%s"\
|
||||
% (commits_table)
|
||||
% (self.GetCommitsTable())
|
||||
sql_args = (repository_id, dir_id, file_id, commit.GetRevision())
|
||||
|
||||
cursor = self.db.cursor()
|
||||
@@ -527,10 +542,9 @@ class CheckinDatabase:
|
||||
def sql_delete(self, table, key, value, keep_fkey = None):
|
||||
sql = "DELETE FROM %s WHERE %s=%%s" % (table, key)
|
||||
sql_args = (value, )
|
||||
commits_table = self._version >= 1 and 'commits' or 'checkins'
|
||||
if keep_fkey:
|
||||
sql += " AND %s NOT IN (SELECT %s FROM %s WHERE %s = %%s)" \
|
||||
% (key, keep_fkey, commits_table, keep_fkey)
|
||||
% (key, keep_fkey, self.GetCommitsTable(), keep_fkey)
|
||||
sql_args = (value, value)
|
||||
cursor = self.db.cursor()
|
||||
cursor.execute(sql, sql_args)
|
||||
@@ -769,8 +783,9 @@ class QueryEntry:
|
||||
self.data = data
|
||||
self.match = match
|
||||
|
||||
## CheckinDatabaseQueryData is a object which contains the search parameters
|
||||
## for a query to the CheckinDatabase
|
||||
## CheckinDatabaseQuery is an object which contains the search
|
||||
## parameters for a query to the Checkin Database and -- after the
|
||||
## query is executed -- the data returned by the query.
|
||||
class CheckinDatabaseQuery:
|
||||
def __init__(self):
|
||||
## sorting
|
||||
@@ -790,7 +805,8 @@ class CheckinDatabaseQuery:
|
||||
|
||||
## limit on number of rows to return
|
||||
self.limit = None
|
||||
|
||||
self.limit_reached = 0
|
||||
|
||||
## list of commits -- filled in by CVS query
|
||||
self.commit_list = []
|
||||
|
||||
@@ -798,6 +814,9 @@ class CheckinDatabaseQuery:
|
||||
## are added
|
||||
self.commit_cb = None
|
||||
|
||||
## has this query been run?
|
||||
self.executed = 0
|
||||
|
||||
def SetRepository(self, repository, match = "exact"):
|
||||
self.repository_list.append(QueryEntry(repository, match))
|
||||
|
||||
@@ -843,6 +862,20 @@ class CheckinDatabaseQuery:
|
||||
def AddCommit(self, commit):
|
||||
self.commit_list.append(commit)
|
||||
|
||||
def SetExecuted(self):
|
||||
self.executed = 1
|
||||
|
||||
def SetLimitReached(self):
|
||||
self.limit_reached = 1
|
||||
|
||||
def GetLimitReached(self):
|
||||
assert self.executed
|
||||
return self.limit_reached
|
||||
|
||||
def GetCommitList(self):
|
||||
assert self.executed
|
||||
return self.commit_list
|
||||
|
||||
|
||||
##
|
||||
## entrypoints
|
||||
@@ -861,7 +894,7 @@ def ConnectDatabase(cfg, readonly=0):
|
||||
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)
|
||||
cfg.cvsdb.database_name)
|
||||
db.Connect()
|
||||
return db
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
25
lib/query.py
25
lib/query.py
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -217,8 +217,9 @@ def decode_command(cmd):
|
||||
else:
|
||||
return "exact"
|
||||
|
||||
def form_to_cvsdb_query(form_data):
|
||||
def form_to_cvsdb_query(cfg, form_data):
|
||||
query = cvsdb.CreateCheckinQuery()
|
||||
query.SetLimit(cfg.cvsdb.row_limit)
|
||||
|
||||
if form_data.repository:
|
||||
for cmd, str in listparse_string(form_data.repository):
|
||||
@@ -377,12 +378,15 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
|
||||
return ob
|
||||
|
||||
def run_query(server, cfg, form_data, viewvc_link):
|
||||
query = form_to_cvsdb_query(form_data)
|
||||
query = form_to_cvsdb_query(cfg, form_data)
|
||||
db = cvsdb.ConnectDatabaseReadOnly(cfg)
|
||||
db.RunQuery(query)
|
||||
|
||||
if not query.commit_list:
|
||||
return [ ]
|
||||
commit_list = query.GetCommitList()
|
||||
if not commit_list:
|
||||
return [ ], 0
|
||||
|
||||
row_limit_reached = query.GetLimitReached()
|
||||
|
||||
commits = [ ]
|
||||
files = [ ]
|
||||
@@ -393,8 +397,8 @@ def run_query(server, cfg, form_data, viewvc_link):
|
||||
for key, value in rootitems:
|
||||
cvsroots[cvsdb.CleanRepository(value)] = key
|
||||
|
||||
current_desc = query.commit_list[0].GetDescription()
|
||||
for commit in query.commit_list:
|
||||
current_desc = commit_list[0].GetDescription()
|
||||
for commit in commit_list:
|
||||
desc = commit.GetDescription()
|
||||
if current_desc == desc:
|
||||
files.append(commit)
|
||||
@@ -417,7 +421,7 @@ def run_query(server, cfg, form_data, viewvc_link):
|
||||
return len(commit.files) > 0
|
||||
commits = filter(_only_with_files, commits)
|
||||
|
||||
return commits
|
||||
return commits, row_limit_reached
|
||||
|
||||
def main(server, cfg, viewvc_link):
|
||||
try:
|
||||
@@ -426,10 +430,12 @@ def main(server, cfg, viewvc_link):
|
||||
form_data = FormData(form)
|
||||
|
||||
if form_data.valid:
|
||||
commits = run_query(server, cfg, form_data, viewvc_link)
|
||||
commits, row_limit_reached = run_query(server, cfg,
|
||||
form_data, viewvc_link)
|
||||
query = None
|
||||
else:
|
||||
commits = [ ]
|
||||
row_limit_reached = 0
|
||||
query = 'skipped'
|
||||
|
||||
docroot = cfg.options.docroot
|
||||
@@ -449,6 +455,7 @@ def main(server, cfg, viewvc_link):
|
||||
'sortby' : form_data.sortby,
|
||||
'date' : form_data.date,
|
||||
'query' : query,
|
||||
'row_limit_reached' : ezt.boolean(row_limit_reached),
|
||||
'commits' : commits,
|
||||
'num_commits' : len(commits),
|
||||
'rss_href' : None,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -233,9 +233,6 @@ class WsgiServer(Server):
|
||||
self.header(status='301 Moved')
|
||||
self._wsgi_write('This document is located <a href="%s">here</a>.' % url)
|
||||
|
||||
def escape(self, s, quote = None):
|
||||
return cgi.escape(s, quote)
|
||||
|
||||
def getenv(self, name, value=None):
|
||||
return self._environ.get(name, value)
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2006-2008 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2006-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2006-2008 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2006-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2008 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2008-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2006-2011 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2006-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -76,7 +76,7 @@ class Repository:
|
||||
"""
|
||||
pass
|
||||
|
||||
def openfile(self, path_parts, rev):
|
||||
def openfile(self, path_parts, rev, options):
|
||||
"""Open a file object to read file contents at a given path and revision.
|
||||
|
||||
The return value is a 2-tuple of containg the file object and revision
|
||||
@@ -86,6 +86,8 @@ class Repository:
|
||||
of the repository. e.g. ["subdir1", "subdir2", "filename"]
|
||||
|
||||
rev is the revision of the file to check out
|
||||
|
||||
options is a dictionary of implementation specific options
|
||||
"""
|
||||
|
||||
def listdir(self, path_parts, rev, options):
|
||||
@@ -168,12 +170,19 @@ class Repository:
|
||||
Return value is a python file object
|
||||
"""
|
||||
|
||||
def annotate(self, path_parts, rev):
|
||||
"""Return a list of annotate file content lines and a revision.
|
||||
def annotate(self, path_parts, rev, include_text=False):
|
||||
"""Return a list of Annotation object, sorted by their
|
||||
"line_number" components, which describe the lines of given
|
||||
version of a file.
|
||||
|
||||
The result is a list of Annotation objects, sorted by their
|
||||
line_number components.
|
||||
"""
|
||||
The file path is specified as a list of components, relative to
|
||||
the root of the repository. e.g. ["subdir1", "subdir2", "filename"]
|
||||
|
||||
rev is the revision of the item to return information about.
|
||||
|
||||
If include_text is true, populate the Annotation objects' "text"
|
||||
members with the corresponding line of file content; otherwise,
|
||||
leave that member set to None."""
|
||||
|
||||
def revinfo(self, rev):
|
||||
"""Return information about a global revision
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -14,6 +14,7 @@ import os.path
|
||||
|
||||
|
||||
def canonicalize_rootpath(rootpath):
|
||||
assert os.path.isabs(rootpath)
|
||||
return os.path.normpath(rootpath)
|
||||
|
||||
|
||||
@@ -22,6 +23,7 @@ def expand_root_parent(parent_path):
|
||||
# "CVSROOT/config" is added the set of returned roots. Or, if the
|
||||
# PARENT_PATH itself contains a child "CVSROOT/config", then all its
|
||||
# subdirectories are returned as roots.
|
||||
assert os.path.isabs(parent_path)
|
||||
roots = {}
|
||||
subpaths = os.listdir(parent_path)
|
||||
cvsroot = os.path.exists(os.path.join(parent_path, "CVSROOT", "config"))
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -167,7 +167,14 @@ class BinCVSRepository(BaseCVSRepository):
|
||||
return revs[-1]
|
||||
return None
|
||||
|
||||
def openfile(self, path_parts, rev):
|
||||
def openfile(self, path_parts, rev, options):
|
||||
"""see vclib.Repository.openfile docstring
|
||||
|
||||
Option values recognized by this implementation:
|
||||
|
||||
cvs_oldkeywords
|
||||
boolean. true to use the original keyword substitution values.
|
||||
"""
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file."
|
||||
% (string.join(path_parts, "/")))
|
||||
@@ -175,12 +182,14 @@ class BinCVSRepository(BaseCVSRepository):
|
||||
rev_flag = '-p'
|
||||
else:
|
||||
rev_flag = '-p' + rev
|
||||
if options.get('cvs_oldkeywords', 0):
|
||||
kv_flag = '-ko'
|
||||
else:
|
||||
kv_flag = '-kkv'
|
||||
full_name = self.rcsfile(path_parts, root=1, v=0)
|
||||
|
||||
used_rlog = 0
|
||||
tip_rev = None # used only if we have to fallback to using rlog
|
||||
|
||||
fp = self.rcs_popen('co', (rev_flag, full_name), 'rb')
|
||||
fp = self.rcs_popen('co', (kv_flag, rev_flag, full_name), 'rb')
|
||||
try:
|
||||
filename, revision = _parse_co_header(fp)
|
||||
except COMissingRevision:
|
||||
@@ -323,13 +332,13 @@ class BinCVSRepository(BaseCVSRepository):
|
||||
args = rcs_args
|
||||
return popen.popen(cmd, args, mode, capture_err)
|
||||
|
||||
def annotate(self, path_parts, rev=None):
|
||||
def annotate(self, path_parts, rev=None, include_text=False):
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file."
|
||||
% (string.join(path_parts, "/")))
|
||||
|
||||
from vclib.ccvs import blame
|
||||
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev)
|
||||
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev, include_text)
|
||||
return source, source.revision
|
||||
|
||||
def revinfo(self, rev):
|
||||
@@ -1027,16 +1036,16 @@ def _get_logs(repos, dir_path_parts, entries, view_tag, get_dirs):
|
||||
file.errors.append("rlog error: %s" % msg)
|
||||
continue
|
||||
|
||||
tag = None
|
||||
if view_tag == 'MAIN' or view_tag == 'HEAD':
|
||||
tag = Tag(None, default_branch)
|
||||
elif taginfo.has_key(view_tag):
|
||||
tag = Tag(None, taginfo[view_tag])
|
||||
elif view_tag:
|
||||
# the tag wasn't found, so skip this file
|
||||
elif view_tag and (eof != _EOF_FILE):
|
||||
# the tag wasn't found, so skip this file (unless we already
|
||||
# know there's nothing left of it to read)
|
||||
_skip_file(rlog)
|
||||
eof = 1
|
||||
else:
|
||||
tag = None
|
||||
eof = _EOF_FILE
|
||||
|
||||
# we don't care about the specific values -- just the keys and whether
|
||||
# the values point to branches or revisions. this the fastest way to
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
@@ -414,7 +414,7 @@ class CVSParser(rcsparse.Sink):
|
||||
|
||||
|
||||
class BlameSource:
|
||||
def __init__(self, rcs_file, opt_rev=None):
|
||||
def __init__(self, rcs_file, opt_rev=None, include_text=False):
|
||||
# Parse the CVS file
|
||||
parser = CVSParser()
|
||||
revision = parser.parse_cvs_file(rcs_file, opt_rev)
|
||||
@@ -428,6 +428,7 @@ class BlameSource:
|
||||
self.lines = lines
|
||||
self.num_lines = count
|
||||
self.parser = parser
|
||||
self.include_text = include_text
|
||||
|
||||
# keep track of where we are during an iteration
|
||||
self.idx = -1
|
||||
@@ -447,6 +448,8 @@ class BlameSource:
|
||||
line_number = idx + 1
|
||||
author = self.parser.revision_author[rev]
|
||||
thisline = self.lines[idx]
|
||||
if not self.include_text:
|
||||
thisline = None
|
||||
### TODO: Put a real date in here.
|
||||
item = vclib.Annotation(thisline, line_number, rev, prev_rev, author, None)
|
||||
self.last = item
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -127,9 +127,9 @@ class CCVSRepository(BaseCVSRepository):
|
||||
% (string.join(path_parts2, "/")))
|
||||
|
||||
temp1 = tempfile.mktemp()
|
||||
open(temp1, 'wb').write(self.openfile(path_parts1, rev1)[0].getvalue())
|
||||
open(temp1, 'wb').write(self.openfile(path_parts1, rev1, {})[0].getvalue())
|
||||
temp2 = tempfile.mktemp()
|
||||
open(temp2, 'wb').write(self.openfile(path_parts2, rev2)[0].getvalue())
|
||||
open(temp2, 'wb').write(self.openfile(path_parts2, rev2, {})[0].getvalue())
|
||||
|
||||
r1 = self.itemlog(path_parts1, rev1, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
|
||||
r2 = self.itemlog(path_parts2, rev2, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
|
||||
@@ -142,17 +142,17 @@ class CCVSRepository(BaseCVSRepository):
|
||||
return vclib._diff_fp(temp1, temp2, info1, info2,
|
||||
self.utilities.diff or 'diff', diff_args)
|
||||
|
||||
def annotate(self, path_parts, rev=None):
|
||||
def annotate(self, path_parts, rev=None, include_text=False):
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file."
|
||||
% (string.join(path_parts, "/")))
|
||||
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev)
|
||||
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev, include_text)
|
||||
return source, source.revision
|
||||
|
||||
def revinfo(self, rev):
|
||||
raise vclib.UnsupportedFeature
|
||||
|
||||
def openfile(self, path_parts, rev=None):
|
||||
def openfile(self, path_parts, rev, options):
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file."
|
||||
% (string.join(path_parts, "/")))
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -194,7 +194,8 @@ class _Parser:
|
||||
else:
|
||||
# Chew up "newphrase"
|
||||
# warn("Unexpected RCS token: $token\n")
|
||||
pass
|
||||
while self.ts.get() != ';':
|
||||
pass
|
||||
else:
|
||||
if f is None:
|
||||
self.ts.unget(token)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -38,6 +38,7 @@ def canonicalize_rootpath(rootpath):
|
||||
return os.path.normpath(urllib.unquote(rootpath[7:]))
|
||||
if re.search(_re_url, rootpath):
|
||||
return rootpath.rstrip('/')
|
||||
assert os.path.isabs(rootpath)
|
||||
return os.path.normpath(rootpath)
|
||||
|
||||
|
||||
@@ -48,6 +49,7 @@ def expand_root_parent(parent_path):
|
||||
else:
|
||||
# Any subdirectories of PARENT_PATH which themselves have a child
|
||||
# "format" are returned as roots.
|
||||
assert os.path.isabs(parent_path)
|
||||
subpaths = os.listdir(parent_path)
|
||||
for rootname in subpaths:
|
||||
rootpath = os.path.join(parent_path, rootname)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -241,7 +241,7 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
raise vclib.ItemNotFound(path_parts)
|
||||
return pathtype
|
||||
|
||||
def openfile(self, path_parts, rev):
|
||||
def openfile(self, path_parts, rev, options):
|
||||
path = self._getpath(path_parts)
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % path)
|
||||
@@ -338,7 +338,7 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
_rev2optrev(rev), 0, self.ctx)
|
||||
return pairs and pairs[0][1] or {}
|
||||
|
||||
def annotate(self, path_parts, rev):
|
||||
def annotate(self, path_parts, rev, include_text=False):
|
||||
path = self._getpath(path_parts)
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % path)
|
||||
@@ -352,12 +352,13 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
prev_rev = None
|
||||
if revision > 1:
|
||||
prev_rev = revision - 1
|
||||
blame_data.append(vclib.Annotation(line, line_no+1, revision, prev_rev,
|
||||
if not include_text:
|
||||
line = None
|
||||
blame_data.append(vclib.Annotation(line, line_no + 1, revision, prev_rev,
|
||||
author, None))
|
||||
|
||||
client.svn_client_blame(url, _rev2optrev(1), _rev2optrev(rev),
|
||||
_blame_cb, self.ctx)
|
||||
|
||||
return blame_data, rev
|
||||
|
||||
def revinfo(self, rev):
|
||||
@@ -407,8 +408,11 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
if rev is None or rev == 'HEAD':
|
||||
return self.youngest
|
||||
try:
|
||||
if type(rev) == type(''):
|
||||
while rev[0] == 'r':
|
||||
rev = rev[1:]
|
||||
rev = int(rev)
|
||||
except ValueError:
|
||||
except:
|
||||
raise vclib.InvalidRevision(rev)
|
||||
if (rev < 0) or (rev > self.youngest):
|
||||
raise vclib.InvalidRevision(rev)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -22,6 +22,7 @@ import time
|
||||
import tempfile
|
||||
import popen
|
||||
import re
|
||||
import urllib
|
||||
from svn import fs, repos, core, client, delta
|
||||
|
||||
|
||||
@@ -108,11 +109,15 @@ def _rev2optrev(rev):
|
||||
|
||||
def _rootpath2url(rootpath, path):
|
||||
rootpath = os.path.abspath(rootpath)
|
||||
if rootpath and rootpath[0] != '/':
|
||||
rootpath = '/' + rootpath
|
||||
drive, rootpath = os.path.splitdrive(rootpath)
|
||||
if os.sep != '/':
|
||||
rootpath = string.replace(rootpath, os.sep, '/')
|
||||
return 'file://' + string.join([rootpath, path], "/")
|
||||
rootpath = urllib.quote(rootpath)
|
||||
path = urllib.quote(path)
|
||||
if drive:
|
||||
return 'file:///' + drive + rootpath + '/' + path
|
||||
else:
|
||||
return 'file://' + rootpath + '/' + path
|
||||
|
||||
|
||||
# Given a dictionary REVPROPS of revision properties, pull special
|
||||
@@ -282,10 +287,11 @@ class FileContentsPipe:
|
||||
|
||||
|
||||
class BlameSource:
|
||||
def __init__(self, local_url, rev, first_rev, config_dir):
|
||||
def __init__(self, local_url, rev, first_rev, include_text, config_dir):
|
||||
self.idx = -1
|
||||
self.first_rev = first_rev
|
||||
self.blame_data = []
|
||||
self.include_text = include_text
|
||||
|
||||
ctx = client.svn_client_create_context()
|
||||
core.svn_config_ensure(config_dir)
|
||||
@@ -306,6 +312,8 @@ class BlameSource:
|
||||
prev_rev = None
|
||||
if rev > self.first_rev:
|
||||
prev_rev = rev - 1
|
||||
if not self.include_text:
|
||||
text = None
|
||||
self.blame_data.append(vclib.Annotation(text, line_no + 1, rev,
|
||||
prev_rev, author, None))
|
||||
|
||||
@@ -400,7 +408,7 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
raise vclib.ItemNotFound(path_parts)
|
||||
return pathtype
|
||||
|
||||
def openfile(self, path_parts, rev):
|
||||
def openfile(self, path_parts, rev, options):
|
||||
path = self._getpath(path_parts)
|
||||
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
|
||||
raise vclib.Error("Path '%s' is not a file." % path)
|
||||
@@ -521,7 +529,7 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
fsroot = self._getroot(rev)
|
||||
return fs.node_proplist(fsroot, path)
|
||||
|
||||
def annotate(self, path_parts, rev):
|
||||
def annotate(self, path_parts, rev, include_text=False):
|
||||
path = self._getpath(path_parts)
|
||||
path_type = self.itemtype(path_parts, rev) # does auth-check
|
||||
if path_type != vclib.FILE:
|
||||
@@ -532,8 +540,8 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
{'svn_cross_copies': 1})
|
||||
youngest_rev, youngest_path = history[0]
|
||||
oldest_rev, oldest_path = history[-1]
|
||||
source = BlameSource(_rootpath2url(self.rootpath, path),
|
||||
youngest_rev, oldest_rev, self.config_dir)
|
||||
source = BlameSource(_rootpath2url(self.rootpath, path), youngest_rev,
|
||||
oldest_rev, include_text, self.config_dir)
|
||||
return source, youngest_rev
|
||||
|
||||
def revinfo(self, rev):
|
||||
@@ -809,8 +817,11 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
if rev is None or rev == 'HEAD':
|
||||
return self.youngest
|
||||
try:
|
||||
if type(rev) == type(''):
|
||||
while rev[0] == 'r':
|
||||
rev = rev[1:]
|
||||
rev = int(rev)
|
||||
except ValueError:
|
||||
except:
|
||||
raise vclib.InvalidRevision(rev)
|
||||
if (rev < 0) or (rev > self.youngest):
|
||||
raise vclib.InvalidRevision(rev)
|
||||
|
389
lib/viewvc.py
389
lib/viewvc.py
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -14,7 +14,7 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
__version__ = '1.1.10-dev'
|
||||
__version__ = '1.1.13'
|
||||
|
||||
# this comes from our library; measure the startup time
|
||||
import debug
|
||||
@@ -703,7 +703,6 @@ _legal_params = {
|
||||
'mindate' : _re_validate_datetime,
|
||||
'maxdate' : _re_validate_datetime,
|
||||
'format' : _re_validate_alpha,
|
||||
'limit' : _re_validate_number,
|
||||
|
||||
# for redirect_pathrev
|
||||
'orig_path' : None,
|
||||
@@ -1131,7 +1130,7 @@ class ViewVCHtmlFormatter:
|
||||
linkified email address, with no more than MAXLEN characters
|
||||
in the non-HTML-tag bits. If MAXLEN is 0, there is no maximum.
|
||||
- the number of non-HTML-tag characters returned.
|
||||
"""
|
||||
"""
|
||||
s = mobj.group(0)
|
||||
trunc_s = maxlen and s[:maxlen] or s
|
||||
return '<a href="mailto:%s">%s</a>' % (urllib.quote(s),
|
||||
@@ -1232,45 +1231,51 @@ class ViewVCHtmlFormatter:
|
||||
|
||||
def _tokenize_text(self, s):
|
||||
tokens = []
|
||||
while s:
|
||||
best_match = best_conv = best_userdata = None
|
||||
for test in self._formatters:
|
||||
match = test[0].search(s)
|
||||
# If we find and match and (a) its our first one, or (b) it
|
||||
# matches text earlier than our previous best match, or (c) it
|
||||
# matches text at the same location as our previous best match
|
||||
# but extends to cover more text than that match, then this is
|
||||
# our new best match.
|
||||
#
|
||||
# Implied here is that when multiple formatters match exactly
|
||||
# the same text, the first formatter in the registration list wins.
|
||||
if match \
|
||||
and ((best_match is None) \
|
||||
or (match.start() < best_match.start())
|
||||
or ((match.start() == best_match.start()) \
|
||||
and (match.end() > best_match.end()))):
|
||||
best_match = match
|
||||
best_conv = test[1]
|
||||
best_userdata = test[2]
|
||||
# If we found a match...
|
||||
if best_match:
|
||||
# ... add any non-matching stuff first, then the matching bit.
|
||||
start = best_match.start()
|
||||
end = best_match.end()
|
||||
if start > 0:
|
||||
tokens.append(_item(match=s[:start],
|
||||
# We could just have a "while s:" here instead of "for line: while
|
||||
# line:", but for really large log messages with heavy
|
||||
# tokenization, the cost in both performance and memory
|
||||
# consumption of the approach taken was atrocious.
|
||||
for line in string.split(string.replace(s, '\r\n', '\n'), '\n'):
|
||||
line = line + '\n'
|
||||
while line:
|
||||
best_match = best_conv = best_userdata = None
|
||||
for test in self._formatters:
|
||||
match = test[0].search(line)
|
||||
# If we find and match and (a) its our first one, or (b) it
|
||||
# matches text earlier than our previous best match, or (c) it
|
||||
# matches text at the same location as our previous best match
|
||||
# but extends to cover more text than that match, then this is
|
||||
# our new best match.
|
||||
#
|
||||
# Implied here is that when multiple formatters match exactly
|
||||
# the same text, the first formatter in the registration list wins.
|
||||
if match \
|
||||
and ((best_match is None) \
|
||||
or (match.start() < best_match.start())
|
||||
or ((match.start() == best_match.start()) \
|
||||
and (match.end() > best_match.end()))):
|
||||
best_match = match
|
||||
best_conv = test[1]
|
||||
best_userdata = test[2]
|
||||
# If we found a match...
|
||||
if best_match:
|
||||
# ... add any non-matching stuff first, then the matching bit.
|
||||
start = best_match.start()
|
||||
end = best_match.end()
|
||||
if start > 0:
|
||||
tokens.append(_item(match=line[:start],
|
||||
converter=self.format_text,
|
||||
userdata=None))
|
||||
tokens.append(_item(match=best_match,
|
||||
converter=best_conv,
|
||||
userdata=best_userdata))
|
||||
line = line[end:]
|
||||
else:
|
||||
# Otherwise, just add the rest of the string.
|
||||
tokens.append(_item(match=line,
|
||||
converter=self.format_text,
|
||||
userdata=None))
|
||||
tokens.append(_item(match=best_match,
|
||||
converter=best_conv,
|
||||
userdata=best_userdata))
|
||||
s = s[end:]
|
||||
else:
|
||||
# Otherwise, just add the rest of the string.
|
||||
tokens.append(_item(match=s,
|
||||
converter=self.format_text,
|
||||
userdata=None))
|
||||
s = ''
|
||||
line = ''
|
||||
return tokens
|
||||
|
||||
|
||||
@@ -1542,33 +1547,36 @@ def markup_escaped_urls(s):
|
||||
return "<a href=\"%s\">%s</a>" % (unescaped_url, url)
|
||||
return re.sub(_re_rewrite_escaped_url, _url_repl, s)
|
||||
|
||||
def markup_stream_pygments(request, cfg, blame_data, fp, filename,
|
||||
mime_type, encoding):
|
||||
# Determine if we should use Pygments to highlight our output.
|
||||
# Reasons not to include a) being told not to by the configuration,
|
||||
# b) not being able to import the Pygments modules, and c) Pygments
|
||||
# not having a lexer for our file's format.
|
||||
blame_source = []
|
||||
if blame_data:
|
||||
for i in blame_data:
|
||||
i.text = sapi.escape(i.text)
|
||||
i.diff_href = None
|
||||
if i.prev_rev:
|
||||
i.diff_href = request.get_url(view_func=view_diff,
|
||||
params={'r1': i.prev_rev,
|
||||
'r2': i.rev},
|
||||
escape=1, partial=1)
|
||||
blame_source.append(i)
|
||||
blame_data = blame_source
|
||||
lexer = None
|
||||
use_pygments = cfg.options.enable_syntax_coloration
|
||||
try:
|
||||
|
||||
def markup_stream(request, cfg, blame_data, file_lines, filename,
|
||||
mime_type, encoding, colorize):
|
||||
"""Return the contents of a versioned file as a list of
|
||||
vclib.Annotation objects, each representing one line of the file's
|
||||
contents. Use BLAME_DATA as the annotation information for the file
|
||||
if provided. Use FILE_LINES as the lines of file content text
|
||||
themselves. MIME_TYPE is the MIME content type of the file;
|
||||
ENCODING is its character encoding. If COLORIZE is true, attempt to
|
||||
apply syntax coloration to the file contents, and use the
|
||||
HTML-marked-up results as the text in the return vclib.Annotation
|
||||
objects."""
|
||||
|
||||
# Nothing to mark up? So be it.
|
||||
if not file_lines:
|
||||
return []
|
||||
|
||||
# Determine if we should (and can) use Pygments to highlight our
|
||||
# output. Reasons not to include a) being told not to by the
|
||||
# configuration, b) not being able to import the Pygments modules,
|
||||
# and c) Pygments not having a lexer for our file's format.
|
||||
pygments_lexer = None
|
||||
if colorize:
|
||||
from pygments import highlight
|
||||
from pygments.formatters import HtmlFormatter
|
||||
from pygments.lexers import ClassNotFound, \
|
||||
get_lexer_by_name, \
|
||||
get_lexer_for_mimetype, \
|
||||
get_lexer_for_filename
|
||||
get_lexer_for_filename, \
|
||||
guess_lexer
|
||||
if not encoding:
|
||||
encoding = 'guess'
|
||||
if cfg.options.detect_encoding:
|
||||
@@ -1577,50 +1585,50 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
|
||||
encoding = 'chardet'
|
||||
except (SyntaxError, ImportError):
|
||||
pass
|
||||
try:
|
||||
lexer = get_lexer_for_mimetype(mime_type,
|
||||
encoding=encoding,
|
||||
tabsize=cfg.options.tabsize,
|
||||
stripnl=False)
|
||||
except ClassNotFound:
|
||||
try:
|
||||
lexer = get_lexer_for_filename(filename,
|
||||
encoding=encoding,
|
||||
tabsize=cfg.options.tabsize,
|
||||
stripnl=False)
|
||||
except ClassNotFound:
|
||||
use_pygments = 0
|
||||
except ImportError:
|
||||
use_pygments = 0
|
||||
|
||||
# If we aren't going to be highlighting anything, just return the
|
||||
# BLAME_SOURCE. If there's no blame_source, we'll generate a fake
|
||||
# one from the file contents we fetch with PATH and REV.
|
||||
if not use_pygments:
|
||||
if blame_source:
|
||||
class BlameSourceTabsizeWrapper:
|
||||
def __init__(self, blame_source, tabsize):
|
||||
self.blame_source = blame_source
|
||||
self.tabsize = cfg.options.tabsize
|
||||
def __getitem__(self, idx):
|
||||
item = self.blame_source.__getitem__(idx)
|
||||
item.text = string.expandtabs(item.text, self.tabsize)
|
||||
item.text = markup_escaped_urls(item.text)
|
||||
return item
|
||||
return BlameSourceTabsizeWrapper(blame_source, cfg.options.tabsize)
|
||||
else:
|
||||
lines = []
|
||||
line_no = 0
|
||||
while 1:
|
||||
line = fp.readline()
|
||||
if not line:
|
||||
break
|
||||
line_no = line_no + 1
|
||||
line = sapi.escape(string.expandtabs(line, cfg.options.tabsize))
|
||||
line = markup_escaped_urls(line)
|
||||
item = vclib.Annotation(line, line_no, None, None, None, None)
|
||||
item.diff_href = None
|
||||
lines.append(item)
|
||||
# First, see if there's a Pygments lexer associated with MIME_TYPE.
|
||||
if mime_type:
|
||||
try:
|
||||
pygments_lexer = get_lexer_for_mimetype(mime_type,
|
||||
encoding=encoding,
|
||||
tabsize=cfg.options.tabsize,
|
||||
stripnl=False)
|
||||
except ClassNotFound:
|
||||
pygments_lexer = None
|
||||
|
||||
# If we've no lexer thus far, try to find one based on the FILENAME.
|
||||
if not pygments_lexer:
|
||||
try:
|
||||
pygments_lexer = get_lexer_for_filename(filename,
|
||||
encoding=encoding,
|
||||
tabsize=cfg.options.tabsize,
|
||||
stripnl=False)
|
||||
except ClassNotFound:
|
||||
pygments_lexer = None
|
||||
|
||||
# Still no lexer? If we've reason to believe this is a text
|
||||
# file, try to guess the lexer based on the file's content.
|
||||
if not pygments_lexer and is_text(mime_type) and file_lines:
|
||||
try:
|
||||
pygments_lexer = guess_lexer(file_lines[0])
|
||||
except ClassNotFound:
|
||||
pygments_lexer = None
|
||||
|
||||
# If we aren't highlighting, just return an amalgamation of the
|
||||
# BLAME_DATA (if any) and the FILE_LINES.
|
||||
if not pygments_lexer:
|
||||
lines = []
|
||||
for i in range(len(file_lines)):
|
||||
line = file_lines[i]
|
||||
line = sapi.escape(string.expandtabs(line, cfg.options.tabsize))
|
||||
line = markup_escaped_urls(line)
|
||||
if blame_data:
|
||||
blame_item = blame_data[i]
|
||||
blame_item.text = line
|
||||
else:
|
||||
blame_item = vclib.Annotation(line, i + 1, None, None, None, None)
|
||||
blame_item.diff_href = None
|
||||
lines.append(blame_item)
|
||||
return lines
|
||||
|
||||
# If we get here, we're highlighting something.
|
||||
@@ -1644,8 +1652,9 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
|
||||
item.diff_href = None
|
||||
self.blame_data.append(item)
|
||||
self.line_no = self.line_no + 1
|
||||
ps = PygmentsSink(blame_source)
|
||||
highlight(fp.read(), lexer,
|
||||
|
||||
ps = PygmentsSink(blame_data)
|
||||
highlight(string.join(file_lines, ''), pygments_lexer,
|
||||
HtmlFormatter(nowrap=True,
|
||||
classprefix="pygments-",
|
||||
encoding='utf-8'), ps)
|
||||
@@ -1739,38 +1748,75 @@ def markup_or_annotate(request, is_annotate):
|
||||
# Is this a viewable image type?
|
||||
if is_viewable_image(mime_type) \
|
||||
and 'co' in cfg.options.allowed_views:
|
||||
fp, revision = request.repos.openfile(path, rev)
|
||||
fp, revision = request.repos.openfile(path, rev, {})
|
||||
fp.close()
|
||||
if check_freshness(request, None, revision, weak=1):
|
||||
return
|
||||
annotation = 'binary'
|
||||
if is_annotate:
|
||||
annotation = 'binary'
|
||||
image_src_href = request.get_url(view_func=view_checkout,
|
||||
params={'revision': rev}, escape=1)
|
||||
|
||||
# Not a viewable image.
|
||||
else:
|
||||
blame_source = None
|
||||
blame_data = None
|
||||
|
||||
# If this was an annotation request, try to annotate this file.
|
||||
# If something goes wrong, that's okay -- we'll gracefully revert
|
||||
# to a plain markup display.
|
||||
if is_annotate:
|
||||
# Try to annotate this file, but don't croak if we fail.
|
||||
try:
|
||||
blame_source, revision = request.repos.annotate(path, rev)
|
||||
annotation = 'annotated'
|
||||
blame_source, revision = request.repos.annotate(path, rev, False)
|
||||
if check_freshness(request, None, revision, weak=1):
|
||||
return
|
||||
# Create BLAME_DATA list from BLAME_SOURCE, adding diff_href
|
||||
# items to each relevant "line".
|
||||
blame_data = []
|
||||
for item in blame_source:
|
||||
item.diff_href = None
|
||||
if item.prev_rev:
|
||||
item.diff_href = request.get_url(view_func=view_diff,
|
||||
params={'r1': item.prev_rev,
|
||||
'r2': item.rev},
|
||||
escape=1, partial=1)
|
||||
blame_data.append(item)
|
||||
annotation = 'annotated'
|
||||
except vclib.NonTextualFileContents:
|
||||
annotation = 'binary'
|
||||
except:
|
||||
annotation = 'error'
|
||||
|
||||
fp, revision = request.repos.openfile(path, rev)
|
||||
# Grab the file contents.
|
||||
fp, revision = request.repos.openfile(path, rev, {'cvs_oldkeywords' : 1})
|
||||
if check_freshness(request, None, revision, weak=1):
|
||||
fp.close()
|
||||
return
|
||||
lines = markup_stream_pygments(request, cfg, blame_source, fp,
|
||||
path[-1], mime_type, encoding)
|
||||
file_lines = fp.readlines()
|
||||
fp.close()
|
||||
|
||||
data = common_template_data(request, revision)
|
||||
# Do we have a differing number of file content lines and
|
||||
# annotation items? That's no good. Call it an error and don't
|
||||
# bother attempting the annotation display.
|
||||
if blame_data and (len(file_lines) != len(blame_data)):
|
||||
annotation = 'error'
|
||||
blame_data = None
|
||||
|
||||
# Try to markup the file contents/annotation. If we get an error
|
||||
# and we were colorizing the stream, try once more without the
|
||||
# colorization enabled.
|
||||
colorize = cfg.options.enable_syntax_coloration
|
||||
try:
|
||||
lines = markup_stream(request, cfg, blame_data, file_lines,
|
||||
path[-1], mime_type, encoding, colorize)
|
||||
except:
|
||||
if colorize:
|
||||
lines = markup_stream(request, cfg, blame_data, file_lines,
|
||||
path[-1], mime_type, encoding, False)
|
||||
else:
|
||||
raise debug.ViewVCException('Error displaying file contents',
|
||||
'500 Internal Server Error')
|
||||
|
||||
data = common_template_data(request, revision, mime_type)
|
||||
data.merge(ezt.TemplateData({
|
||||
'mime_type' : mime_type,
|
||||
'log' : None,
|
||||
@@ -1917,20 +1963,27 @@ def view_roots(request):
|
||||
rootnames = allroots.keys()
|
||||
rootnames.sort(icmp)
|
||||
for rootname in rootnames:
|
||||
root_path, root_type, lastmod = allroots[rootname]
|
||||
href = request.get_url(view_func=view_directory,
|
||||
where='', pathtype=vclib.DIR,
|
||||
params={'root': rootname}, escape=1)
|
||||
lastmod = allroots[rootname][2]
|
||||
if root_type == vclib.SVN:
|
||||
log_href = request.get_url(view_func=view_log,
|
||||
where='', pathtype=vclib.DIR,
|
||||
params={'root': rootname}, escape=1)
|
||||
else:
|
||||
log_href = None
|
||||
roots.append(_item(name=request.server.escape(rootname),
|
||||
type=allroots[rootname][1],
|
||||
path=allroots[rootname][0],
|
||||
type=root_type,
|
||||
path=root_path,
|
||||
author=lastmod and lastmod.author or None,
|
||||
ago=lastmod and lastmod.ago or None,
|
||||
date=lastmod and lastmod.date or None,
|
||||
log=lastmod and lastmod.log or None,
|
||||
short_log=lastmod and lastmod.short_log or None,
|
||||
rev=lastmod and lastmod.rev or None,
|
||||
href=href))
|
||||
href=href,
|
||||
log_href=log_href))
|
||||
|
||||
data = common_template_data(request)
|
||||
data.merge(ezt.TemplateData({
|
||||
@@ -2662,9 +2715,12 @@ def view_checkout(request):
|
||||
if 'co' not in cfg.options.allowed_views:
|
||||
raise debug.ViewVCException('Checkout view is disabled',
|
||||
'403 Forbidden')
|
||||
|
||||
if request.pathtype != vclib.FILE:
|
||||
raise debug.ViewVCException('Unsupported feature: checkout view on '
|
||||
'directory', '400 Bad Request')
|
||||
|
||||
path, rev = _orig_path(request)
|
||||
fp, revision = request.repos.openfile(path, rev)
|
||||
fp, revision = request.repos.openfile(path, rev, {})
|
||||
|
||||
# The revision number acts as a strong validator.
|
||||
if not check_freshness(request, None, revision):
|
||||
@@ -2750,7 +2806,7 @@ def search_file(repos, path_parts, rev, search_re):
|
||||
|
||||
# Read in each line of a checked-out file, and then use re.search to
|
||||
# search line.
|
||||
fp = repos.openfile(path_parts, rev)[0]
|
||||
fp = repos.openfile(path_parts, rev, {})[0]
|
||||
matches = 0
|
||||
while 1:
|
||||
line = fp.readline()
|
||||
@@ -2978,7 +3034,8 @@ class DiffSource:
|
||||
class DiffSequencingError(Exception):
|
||||
pass
|
||||
|
||||
def diff_parse_headers(fp, diff_type, rev1, rev2, sym1=None, sym2=None):
|
||||
def diff_parse_headers(fp, diff_type, path1, path2, rev1, rev2,
|
||||
sym1=None, sym2=None):
|
||||
date1 = date2 = log_rev1 = log_rev2 = flag = None
|
||||
header_lines = []
|
||||
|
||||
@@ -3007,15 +3064,15 @@ def diff_parse_headers(fp, diff_type, rev1, rev2, sym1=None, sym2=None):
|
||||
if match:
|
||||
date1 = match.group(1)
|
||||
log_rev1 = match.group(2)
|
||||
if sym1:
|
||||
line = line[:-1] + ' %s\n' % sym1
|
||||
line = '%s%s\t%s\t%s%s\n' % (f1, path1, date1, log_rev1,
|
||||
sym1 and ' ' + sym1 or '')
|
||||
elif line[:len(f2)] == f2:
|
||||
match = _re_extract_rev.match(line)
|
||||
if match:
|
||||
date2 = match.group(1)
|
||||
log_rev2 = match.group(2)
|
||||
if sym2:
|
||||
line = line[:-1] + ' %s\n' % sym2
|
||||
line = '%s%s\t%s\t%s%s\n' % (f2, path2, date2, log_rev2,
|
||||
sym2 and ' ' + sym2 or '')
|
||||
parsing = 0
|
||||
elif line[:3] == 'Bin':
|
||||
flag = _RCSDIFF_IS_BINARY
|
||||
@@ -3143,8 +3200,11 @@ def view_patch(request):
|
||||
raise debug.ViewVCException('Invalid path(s) or revision(s) passed '
|
||||
'to diff', '400 Bad Request')
|
||||
|
||||
date1, date2, flag, headers = diff_parse_headers(fp, diff_type, rev1, rev2,
|
||||
sym1, sym2)
|
||||
path_left = _path_join(p1)
|
||||
path_right = _path_join(p2)
|
||||
date1, date2, flag, headers = diff_parse_headers(fp, diff_type,
|
||||
path_left, path_right,
|
||||
rev1, rev2, sym1, sym2)
|
||||
|
||||
server_fp = get_writeready_server_file(request, 'text/plain')
|
||||
server_fp.write(headers)
|
||||
@@ -3210,13 +3270,13 @@ def view_diff(request):
|
||||
if (cfg.options.hr_intraline and idiff
|
||||
and ((human_readable and idiff.sidebyside)
|
||||
or (not human_readable and diff_type == vclib.UNIFIED))):
|
||||
f1 = request.repos.openfile(p1, rev1)[0]
|
||||
f1 = request.repos.openfile(p1, rev1, {})[0]
|
||||
try:
|
||||
lines_left = f1.readlines()
|
||||
finally:
|
||||
f1.close()
|
||||
|
||||
f2 = request.repos.openfile(p2, rev2)[0]
|
||||
f2 = request.repos.openfile(p2, rev2, {})[0]
|
||||
try:
|
||||
lines_right = f2.readlines()
|
||||
finally:
|
||||
@@ -3240,6 +3300,7 @@ def view_diff(request):
|
||||
changes = []
|
||||
if fp:
|
||||
date1, date2, flag, headers = diff_parse_headers(fp, diff_type,
|
||||
path_left, path_right,
|
||||
rev1, rev2, sym1, sym2)
|
||||
if human_readable:
|
||||
if flag is not None:
|
||||
@@ -3429,7 +3490,7 @@ def generate_tarball(out, request, reldir, stack, dir_mtime=None):
|
||||
|
||||
### FIXME: Read the whole file into memory? Bad... better to do
|
||||
### 2 passes.
|
||||
fp = request.repos.openfile(rep_path + [file.name], request.pathrev)[0]
|
||||
fp = request.repos.openfile(rep_path + [file.name], request.pathrev, {})[0]
|
||||
contents = fp.read()
|
||||
fp.close()
|
||||
|
||||
@@ -4078,7 +4139,6 @@ def view_query(request):
|
||||
mindate = request.query_dict.get('mindate', '')
|
||||
maxdate = request.query_dict.get('maxdate', '')
|
||||
format = request.query_dict.get('format')
|
||||
limit = int(request.query_dict.get('limit', 0))
|
||||
limit_changes = int(request.query_dict.get('limit_changes',
|
||||
cfg.options.limit_changes))
|
||||
|
||||
@@ -4148,29 +4208,32 @@ def view_query(request):
|
||||
query.SetFromDateObject(mindate)
|
||||
if maxdate is not None:
|
||||
query.SetToDateObject(maxdate)
|
||||
if limit:
|
||||
query.SetLimit(limit)
|
||||
elif format == 'rss':
|
||||
|
||||
# Set the admin-defined (via configuration) row limits. This is to avoid
|
||||
# slamming the database server with a monster query.
|
||||
if format == 'rss':
|
||||
query.SetLimit(cfg.cvsdb.rss_row_limit)
|
||||
else:
|
||||
query.SetLimit(cfg.cvsdb.row_limit)
|
||||
|
||||
# run the query
|
||||
db.RunQuery(query)
|
||||
|
||||
sql = request.server.escape(db.CreateSQLQueryString(query))
|
||||
|
||||
commit_list = query.GetCommitList()
|
||||
row_limit_reached = query.GetLimitReached()
|
||||
|
||||
# gather commits
|
||||
commits = []
|
||||
plus_count = 0
|
||||
minus_count = 0
|
||||
mod_time = -1
|
||||
if query.commit_list:
|
||||
if commit_list:
|
||||
files = []
|
||||
limited_files = 0
|
||||
current_desc = query.commit_list[0].GetDescriptionID()
|
||||
current_rev = query.commit_list[0].GetRevision()
|
||||
current_desc = commit_list[0].GetDescriptionID()
|
||||
current_rev = commit_list[0].GetRevision()
|
||||
dir_strip = _path_join(repos_dir)
|
||||
|
||||
for commit in query.commit_list:
|
||||
for commit in commit_list:
|
||||
commit_desc = commit.GetDescriptionID()
|
||||
commit_rev = commit.GetRevision()
|
||||
|
||||
@@ -4239,7 +4302,7 @@ def view_query(request):
|
||||
|
||||
data = common_template_data(request)
|
||||
data.merge(ezt.TemplateData({
|
||||
'sql': sql,
|
||||
'sql': request.server.escape(db.CreateSQLQueryString(query)),
|
||||
'english_query': english_query(request),
|
||||
'queryform_href': request.get_url(view_func=view_queryform, escape=1),
|
||||
'backout_href': backout_href,
|
||||
@@ -4248,6 +4311,7 @@ def view_query(request):
|
||||
'show_branch': show_branch,
|
||||
'querysort': querysort,
|
||||
'commits': commits,
|
||||
'row_limit_reached' : ezt.boolean(row_limit_reached),
|
||||
'limit_changes': limit_changes,
|
||||
'limit_changes_href': limit_changes_href,
|
||||
'rss_link_href': request.get_url(view_func=view_query,
|
||||
@@ -4394,14 +4458,35 @@ def locate_root(cfg, rootname):
|
||||
return None, None
|
||||
|
||||
def load_config(pathname=None, server=None):
|
||||
"""Load the ViewVC configuration file. SERVER is the server object
|
||||
that will be using this configuration. Consult the environment for
|
||||
the variable VIEWVC_CONF_PATHNAME and VIEWCVS_CONF_PATHNAME (its
|
||||
legacy name) and, if set, use its value as the path of the
|
||||
configuration file; otherwise, use PATHNAME (if provided). Failing
|
||||
all else, use a hardcoded default configuration path."""
|
||||
|
||||
debug.t_start('load-config')
|
||||
|
||||
if pathname is None:
|
||||
pathname = (os.environ.get("VIEWVC_CONF_PATHNAME")
|
||||
or os.environ.get("VIEWCVS_CONF_PATHNAME")
|
||||
or os.path.join(os.path.dirname(os.path.dirname(__file__)),
|
||||
"viewvc.conf"))
|
||||
# See if the environment contains overrides to the configuration
|
||||
# path. If we have a SERVER object, consult its environment; use
|
||||
# the OS environment otherwise.
|
||||
env_pathname = None
|
||||
if server is not None:
|
||||
env_pathname = (server.getenv("VIEWVC_CONF_PATHNAME")
|
||||
or server.getenv("VIEWCVS_CONF_PATHNAME"))
|
||||
else:
|
||||
env_pathname = (os.environ.get("VIEWVC_CONF_PATHNAME")
|
||||
or os.environ.get("VIEWCVS_CONF_PATHNAME"))
|
||||
|
||||
# Try to find the configuration pathname by searching these ordered
|
||||
# locations: the environment, the passed-in PATHNAME, the hard-coded
|
||||
# default.
|
||||
pathname = (env_pathname
|
||||
or pathname
|
||||
or os.path.join(os.path.dirname(os.path.dirname(__file__)),
|
||||
"viewvc.conf"))
|
||||
|
||||
# Load the configuration!
|
||||
cfg = config.Config()
|
||||
cfg.set_defaults()
|
||||
cfg.load_config(pathname, server and server.getenv("HTTP_HOST"))
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -113,3 +113,6 @@ numbers, and not literal):
|
||||
18. Send to the announce@ list a message explaining all the cool new
|
||||
features, and post similar announcements to other places interested
|
||||
in this sort of stuff, such as Freshmeat (http://www.freshmeat.net).
|
||||
|
||||
19. Merge CHANGES for this release into the CHANGES file for newer
|
||||
release lines.
|
||||
|
@@ -269,3 +269,14 @@ table.vc_idiff tbody th {
|
||||
.vc_query_form {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
|
||||
/*** Warning! ***/
|
||||
.vc_warning {
|
||||
border-width: 1px 2px 2px 2px;
|
||||
border-color: black;
|
||||
border-style: solid;
|
||||
background-color: red;
|
||||
color: white;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
@@ -158,7 +158,15 @@
|
||||
[is query "skipped"]
|
||||
[else]
|
||||
<p><strong>[num_commits]</strong> matches found.</p>
|
||||
|
||||
[if-any row_limit_reached]
|
||||
<p class="vc_warning">WARNING: These query results have been
|
||||
artificially limited by an administrative threshold value and do
|
||||
<em>not</em> represent the entirety of the data set which matches
|
||||
the query. Consider modifying your query to be more specific</a>,
|
||||
using your version control tool's query capabilities, or asking
|
||||
your administrator to raise the database response size
|
||||
threshold.</p>
|
||||
[end]
|
||||
[if-any commits]
|
||||
<table cellspacing="0" cellpadding="2">
|
||||
<thead>
|
||||
|
@@ -7,6 +7,15 @@
|
||||
|
||||
<p><strong>[english_query]</strong></p>
|
||||
[# <!-- {sql} --> ]
|
||||
[if-any row_limit_reached]
|
||||
<p class="vc_warning">WARNING: These query results have been
|
||||
artificially limited by an administrative threshold value and do
|
||||
<em>not</em> represent the entirety of the data set which matches
|
||||
the query. Consider <a href="[queryform_href]">modifying your
|
||||
query to be more specific</a>, using your version control tool's
|
||||
query capabilities, or asking your administrator to raise the
|
||||
database response size threshold.</p>
|
||||
[end]
|
||||
<p><a href="[queryform_href]">Modify query</a></p>
|
||||
<p><a href="[backout_href]">Show commands which could be used to back out these changes</a></p>
|
||||
|
||||
|
@@ -27,7 +27,7 @@
|
||||
[roots.name]</a>
|
||||
</td>
|
||||
[is cfg.options.show_roots_lastmod "1"]
|
||||
<td style="width:20"> [roots.rev]</td>
|
||||
<td style="width:20"> [if-any roots.log_href]<a href="[roots.log_href]">[roots.rev]</a>[else][roots.rev][end]</td>
|
||||
<td style="width:20"> [roots.ago]</td>
|
||||
<td style="width:20"> [roots.author]</td>
|
||||
<td style="width:20"> [roots.short_log]</td>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewVC
|
||||
@@ -209,6 +209,8 @@ def install_file(src_path, dst_path, mode, subst_path_vars,
|
||||
temp = raw_input("Do you want to [O]verwrite, [D]o "
|
||||
"not overwrite, or [V]iew "
|
||||
"differences? ")
|
||||
if len(temp) == 0:
|
||||
continue
|
||||
temp = string.lower(temp[0])
|
||||
if temp == "v" and ext not in BINARY_FILE_EXTS:
|
||||
print """
|
||||
|
Reference in New Issue
Block a user