For Issue #271, implement 'purge' commands for both cvsdbadmin and

svndbadmin.  Teach the 'rebuild' commands to first purge existing data
and then crawl the repository.  Also, drop support for the 'rev'
parameter to 'svndbadmin rebuild', adding instead a '--force' option
to 'svndbadmin update'.

Suggested, and SQL commands offered, by Mark <mark@mitsein.net>.

* lib/cvsdb.py
  (CheckinDatabase.sql_delete, CheckinDatabase.PurgeRepository): New.

* bin/svndbadmin
  (handle_revision): Add 'force' parameter, used to force update of
    commits already recorded in the database.
  (main): Add 'force' parameter, passed on to handle_revision().
    Handle the new 'purge' command, and teach 'rebuild' to also purge.
  (usage): Update usage info.
  (__main__): Add support for 'update --force' and 'purge', and drop
    support for 'rebuild rev'.  Add a KeyboardInterrupt handler.

* bin/cvsdbadmin
  (usage): Add 'purge' usage info.
  (__main__): Rework command-line parameter handling.  Add support for
    'purge' command, and make 'rebuild' first do a purge.

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@1493 8cb11bc2-c004-0410-86c3-e597b4017df7
remotes/options-overhaul
cmpilato 2006-12-21 20:51:24 +00:00
parent c6db566501
commit a1c093ca2c
4 changed files with 102 additions and 39 deletions

View File

@ -6,8 +6,9 @@ Version 1.1.0 (released ??-???-????)
* add daemon mode to standalone.py
* rework and update helper application configuration options
* teach standalone.py to recognize Subversion repositories via -r option
* open relative paths in "viewvc.conf" using the location of that file,
instead of the location of the ViewVC "lib" directory, as a base
* now interpret relative paths in "viewvc.conf" as relative to that file
* add 'purge' subcommand to cvsdbadmin and svndbadmin (issue #271)
* fix orphaned data bug in cvsdbadmin/svndbadmin rebuild (issue #271)
Version 1.0.3 (released 13-Oct-2006)

View File

@ -123,15 +123,18 @@ def usage():
sys.stderr.write("""
Usage: 1. %s [[-q] -q] rebuild REPOSITORY
2. %s [[-q] -q] update REPOSITORY
3. %s [[-q] -q] purge REPOSITORY
1. Rebuild the commit database information for REPOSITORY.
2. Update the commit database information for all unrecorded commits
in REPOSITORY.
3. Purge information specific to REPOSITORY from the database.
Use the -q flag to cause this script to be less verbose; use it twice to
invoke a peaceful state of noiselessness.
""" % (cmd, cmd))
""" % (cmd, cmd, cmd))
sys.exit(1)
@ -139,7 +142,7 @@ invoke a peaceful state of noiselessness.
if __name__ == '__main__':
args = sys.argv
## check the quietness level (0 = verbose, 1 = new commits, 2 = silent)
# check the quietness level (0 = verbose, 1 = new commits, 2 = silent)
quiet_level = 0
while 1:
try:
@ -149,30 +152,30 @@ if __name__ == '__main__':
except ValueError:
break
## check that a command was given
# validate the command
if len(args) <= 2:
usage()
## set the handler function for the command
command = args[1]
if string.lower(command) == 'rebuild':
update = 0
elif string.lower(command) == 'update':
update = 1
else:
print 'ERROR: unknown command %s' % (command)
command = args[1].lower()
if command not in ('rebuild', 'update', 'purge'):
sys.stderr.write('ERROR: unknown command %s\n' % command)
usage()
# get repository path
# get repository and path, and do the work
root, path_parts = RootPath(args[2], quiet_level)
## run command
try:
## connect to the database we are updating
cfg = viewvc.load_config(CONF_PATHNAME)
db = cvsdb.ConnectDatabase(cfg)
repository = vclib.bincvs.BinCVSRepository(None, root, cfg.utilities)
RecurseUpdate(db, repository, path_parts, update, quiet_level)
if command in ('rebuild', 'purge'):
if quiet_level < 2:
print "Purging existing data for repository root `%s'" % root
db.PurgeRepository(root)
if command in ('rebuild', 'update'):
repository = vclib.bincvs.BinCVSRepository(None, root,
cfg.utilities)
RecurseUpdate(db, repository, path_parts,
command == 'update', quiet_level)
except KeyboardInterrupt:
print
print '** break **'

View File

@ -49,9 +49,9 @@ import sys
import os
if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR)
sys.path.insert(0, LIBRARY_DIR)
else:
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0], "../../lib")))
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0], "../../lib")))
#########################################################################
@ -197,7 +197,7 @@ class SvnRev:
return self.rev_roots[rev]
def handle_revision(db, command, repo, rev, verbose):
def handle_revision(db, command, repo, rev, verbose, force=0):
"""Adds a particular revision of the repository to the checkin database."""
revision = repo[rev]
committed = 0
@ -231,7 +231,7 @@ def handle_revision(db, command, repo, rev, verbose):
if command == 'update':
result = db.CheckCommit(commit)
if result:
if result and not force:
continue # already recorded
# commit to database
@ -244,38 +244,46 @@ def handle_revision(db, command, repo, rev, verbose):
else:
print "skipped (already recorded)."
def main(pool, command, repository, rev=None, verbose=0):
def main(pool, command, repository, rev=None, verbose=0, force=0):
cfg = viewvc.load_config(CONF_PATHNAME)
db = cvsdb.ConnectDatabase(cfg)
if command in ('rebuild', 'purge'):
if verbose:
print "Purging commit info for repository root `%s'" % repository
db.PurgeRepository(repository)
repo = SvnRepo(repository, pool)
if rev:
handle_revision(db, command, repo, rev, verbose)
else:
if command == 'rebuild' or (command == 'update' and not rev):
for rev in range(repo.rev_max+1):
handle_revision(db, command, repo, rev, verbose)
elif command == 'update':
handle_revision(db, command, repo, rev, verbose, force)
def usage():
cmd = os.path.basename(sys.argv[0])
sys.stderr.write("""
Usage: 1. %s [-v] rebuild REPOSITORY [REVISION]
2. %s [-v] update REPOSITORY [REVISION]
Usage: 1. %s [-v] rebuild REPOSITORY
2. %s [-v] update REPOSITORY [REVISION] [--force]
3. %s [-v] purge REPOSITORY
1. Rebuild the commit database information for REPOSITORY across all revisions
or, optionally, only for the specified REVISION.
1. Rebuild the commit database information for REPOSITORY across all
revisions.
2. Update the commit database information for REPOSITORY across all revisions
or, optionally, only for the specified REVISION. This is just like
rebuilding, except that no commit information will be stored for
commits already present in the database.
rebuilding, except that, unless --force is specified, no commit information
will be stored for commits already present in the database.
3. Purge information specific to REPOSITORY from the database.
Use the -v flag to cause this script to give progress information as it works.
""" % (cmd, cmd))
""" % (cmd, cmd, cmd))
sys.exit(1)
if __name__ == '__main__':
verbose = 0
force = 0
args = sys.argv
try:
index = args.index('-v')
@ -283,12 +291,18 @@ if __name__ == '__main__':
del args[index]
except ValueError:
pass
try:
index = args.index('--force')
force = 1
del args[index]
except ValueError:
pass
if len(args) < 3:
usage()
command = string.lower(args[1])
if command not in ('rebuild', 'update'):
if command not in ('rebuild', 'update', 'purge'):
sys.stderr.write('ERROR: unknown command %s\n' % command)
usage()
@ -298,6 +312,12 @@ if __name__ == '__main__':
usage()
if len(sys.argv) > 3:
if command == 'rebuild':
sys.stderr.write('ERROR: rebuild no longer accepts a revision '
'number argument. Usage update --force.')
usage()
elif command != 'update':
usage()
rev = sys.argv[3]
try:
rev = int(rev)
@ -307,5 +327,10 @@ if __name__ == '__main__':
else:
rev = None
repository = cvsdb.CleanRepository(os.path.abspath(repository))
svn.core.run_app(main, command, repository, rev, verbose)
try:
repository = cvsdb.CleanRepository(os.path.abspath(repository))
svn.core.run_app(main, command, repository, rev, verbose, force)
except KeyboardInterrupt:
print
print '** break **'
sys.exit(0)

View File

@ -427,6 +427,40 @@ class CheckinDatabase:
return commit
def sql_delete(self, table, key, value):
sql = "DELETE FROM %s WHERE %s=%%s" % (table, key)
sql_args = (value, )
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
def PurgeRepository(self, repository):
rep_id = self.GetRepositoryID(repository)
if not rep_id:
raise Exception, "Unknown repository '%s'" % (repository)
sql = "SELECT * FROM checkins WHERE repositoryid=%s"
sql_args = (rep_id, )
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
checkins = []
while 1:
try:
(ci_type, ci_when, who_id, repository_id,
dir_id, file_id, revision, sticky_tag, branch_id,
plus_count, minus_count, description_id) = cursor.fetchone()
except TypeError:
break
checkins.append([file_id, dir_id, branch_id, description_id])
#self.sql_delete('repositories', 'id', rep_id)
self.sql_delete('checkins', 'repositoryid', rep_id)
for checkin in checkins:
self.sql_delete('files', 'id', checkin[0])
self.sql_delete('dirs', 'id', checkin[1])
self.sql_delete('branches', 'id', checkin[2])
self.sql_delete('descs', 'id', checkin[3])
## the Commit class holds data on one commit, the representation is as
## close as possible to how it should be committed and retrieved to the
## database engine