"Fossies" - the Fresh Open Source Software Archive

Member "viewvc-1.2.1/bin/svndbadmin" (26 Mar 2020, 12816 Bytes) of package /linux/misc/viewvc-1.2.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "svndbadmin": 1.1.28_vs_1.2.1.

    1 #!/usr/bin/env python
    2 # -*-python-*-
    3 #
    4 # Copyright (C) 2004-2020 The ViewCVS Group. All Rights Reserved.
    5 # Copyright (C) 2004-2007 James Henstridge
    6 #
    7 # By using this file, you agree to the terms and conditions set forth in
    8 # the LICENSE.html file which can be found at the top level of the ViewVC
    9 # distribution or at http://viewvc.org/license-1.html.
   10 #
   11 # For more information, visit http://viewvc.org/
   12 #
   13 # -----------------------------------------------------------------------
   14 #
   15 # administrative program for loading Subversion revision information
   16 # into the checkin database.  It can be used to add a single revision
   17 # to the database, or rebuild/update all revisions.
   18 #
   19 # To add all the checkins from a Subversion repository to the checkin
   20 # database, run the following:
   21 #    /path/to/svndbadmin rebuild /path/to/repo
   22 #
   23 # This script can also be called from the Subversion post-commit hook,
   24 # something like this:
   25 #    REPOS="$1"
   26 #    REV="$2"
   27 #    /path/to/svndbadmin update "$REPOS" "$REV"
   28 #
   29 # If you allow changes to revision properties in your repository, you
   30 # might also want to set up something similar in the
   31 # post-revprop-change hook using "update" with the --force option to
   32 # keep the checkin database consistent with the repository.
   33 #
   34 # -----------------------------------------------------------------------
   35 #
   36 
   37 #########################################################################
   38 #
   39 # INSTALL-TIME CONFIGURATION
   40 #
   41 # These values will be set during the installation process. During
   42 # development, they will remain None.
   43 #
   44 
   45 LIBRARY_DIR = None
   46 CONF_PATHNAME = None
   47 
   48 # Adjust sys.path to include our library directory
   49 import sys
   50 import os
   51 
   52 if LIBRARY_DIR:
   53     sys.path.insert(0, LIBRARY_DIR)
   54 else:
   55     sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0], "../../lib")))
   56 
   57 #########################################################################
   58 
   59 import os
   60 import re
   61 
   62 import svn.core
   63 import svn.repos
   64 import svn.fs
   65 import svn.delta
   66 
   67 import cvsdb
   68 import viewvc
   69 import vclib
   70 
   71 class SvnRepo:
   72     """Class used to manage a connection to a SVN repository."""
   73     def __init__(self, path):
   74         self.path = path
   75         self.repo = svn.repos.svn_repos_open(path)
   76         self.fs = svn.repos.svn_repos_fs(self.repo)
   77         self.rev_max = svn.fs.youngest_rev(self.fs)
   78     def __getitem__(self, rev):
   79         if rev is None:
   80             rev = self.rev_max
   81         elif rev < 0:
   82             rev = rev + self.rev_max + 1
   83         assert 0 <= rev <= self.rev_max
   84         rev = SvnRev(self, rev)
   85         return rev
   86 
   87 _re_diff_change_command = re.compile('(\d+)(?:,(\d+))?([acd])(\d+)(?:,(\d+))?')
   88 
   89 def _get_diff_counts(diff_fp):
   90     """Calculate the plus/minus counts by parsing the output of a
   91     normal diff.  The reasons for choosing Normal diff format are:
   92       - the output is short, so should be quicker to parse.
   93       - only the change commands need be parsed to calculate the counts.
   94       - All file data is prefixed, so won't be mistaken for a change
   95         command.
   96     This code is based on the description of the format found in the
   97     GNU diff manual."""
   98 
   99     plus, minus = 0, 0
  100     line = diff_fp.readline()
  101     while line:
  102         match = re.match(_re_diff_change_command, line)
  103         if match:
  104             # size of first range
  105             if match.group(2):
  106                 count1 = int(match.group(2)) - int(match.group(1)) + 1
  107             else:
  108                 count1 = 1
  109             cmd = match.group(3)
  110             # size of second range
  111             if match.group(5):
  112                 count2 = int(match.group(5)) - int(match.group(4)) + 1
  113             else:
  114                 count2 = 1
  115 
  116             if cmd == 'a':
  117                 # LaR - insert after line L of file1 range R of file2
  118                 plus = plus + count2
  119             elif cmd == 'c':
  120                 # FcT - replace range F of file1 with range T of file2
  121                 minus = minus + count1
  122                 plus = plus + count2
  123             elif cmd == 'd':
  124                 # RdL - remove range R of file1, which would have been
  125                 #       at line L of file2
  126                 minus = minus + count1
  127         line = diff_fp.readline()
  128     return plus, minus
  129 
  130 
  131 class SvnRev:
  132     """Class used to hold information about a particular revision of
  133     the repository."""
  134     def __init__(self, repo, rev):
  135         self.repo = repo
  136         self.rev = rev
  137         self.rev_roots = {} # cache of revision roots
  138 
  139         # revision properties ...
  140         revprops = svn.fs.revision_proplist(repo.fs, rev)
  141         self.author = str(revprops.get(svn.core.SVN_PROP_REVISION_AUTHOR,''))
  142         self.date = str(revprops.get(svn.core.SVN_PROP_REVISION_DATE, ''))
  143         self.log = str(revprops.get(svn.core.SVN_PROP_REVISION_LOG, ''))
  144 
  145         # convert the date string to seconds since epoch ...
  146         try:
  147             self.date = svn.core.svn_time_from_cstring(self.date) / 1000000
  148         except:
  149             self.date = None
  150 
  151         # get a root for the current revisions
  152         fsroot = self._get_root_for_rev(rev)
  153         
  154         # find changes in the revision
  155         editor = svn.repos.ChangeCollector(repo.fs, fsroot)
  156         e_ptr, e_baton = svn.delta.make_editor(editor)
  157         svn.repos.svn_repos_replay(fsroot, e_ptr, e_baton)
  158 
  159         self.changes = []
  160         for path, change in editor.changes.items():
  161             # skip non-file changes
  162             if change.item_kind != svn.core.svn_node_file:
  163                 continue
  164 
  165             # deal with the change types we handle
  166             action = None
  167             base_root = None
  168             base_path = change.base_path
  169             if change.base_path:
  170                 base_root = self._get_root_for_rev(change.base_rev)
  171 
  172             # figure out what kind of change this is, and get a diff
  173             # object for it.  note that prior to 1.4 Subversion's
  174             # bindings didn't give us change.action, but that's okay
  175             # because back then deleted paths always had a change.path
  176             # of None.
  177             if hasattr(change, 'action') \
  178                and change.action == svn.repos.CHANGE_ACTION_DELETE:
  179                 action = 'remove'
  180             elif not change.path:
  181                 action = 'remove'
  182             elif change.added:
  183                 action = 'add'
  184             else:
  185                 action = 'change'
  186 
  187             if action == 'remove':
  188                 diffobj = svn.fs.FileDiff(base_root, base_path, None, None)
  189             else:
  190                 diffobj = svn.fs.FileDiff(base_root, base_path,
  191                                           fsroot, change.path)
  192 
  193             diff_fp = diffobj.get_pipe()
  194             plus, minus = _get_diff_counts(diff_fp)
  195             self.changes.append((path, action, plus, minus))
  196 
  197     def _get_root_for_rev(self, rev):
  198         """Fetch a revision root from a cache of such, or a fresh root
  199         (which is then cached for later use."""
  200         if not self.rev_roots.has_key(rev):
  201             self.rev_roots[rev] = svn.fs.revision_root(self.repo.fs, rev)
  202         return self.rev_roots[rev]
  203 
  204 
  205 def handle_revision(db, command, repo, rev, verbose, force=0):
  206     """Adds a particular revision of the repository to the checkin database."""
  207     revision = repo[rev]
  208     committed = 0
  209 
  210     if verbose: print "Building commit info for revision %d..." % (rev),
  211 
  212     if not revision.changes:
  213         if verbose: print "skipped (no changes)."
  214         return
  215 
  216     for (path, action, plus, minus) in revision.changes:
  217         directory, file = os.path.split(path)
  218         commit = cvsdb.CreateCommit()
  219         commit.SetRepository(repo.path)
  220         commit.SetDirectory(directory)
  221         commit.SetFile(file)
  222         commit.SetRevision(str(rev))
  223         commit.SetAuthor(revision.author)
  224         commit.SetDescription(revision.log)
  225         commit.SetTime(revision.date)
  226         commit.SetPlusCount(plus)
  227         commit.SetMinusCount(minus)
  228         commit.SetBranch(None)
  229 
  230         if action == 'add':
  231             commit.SetTypeAdd()
  232         elif action == 'remove':
  233             commit.SetTypeRemove()
  234         elif action == 'change':
  235             commit.SetTypeChange()
  236 
  237         if command == 'update':
  238             result = db.CheckCommit(commit)
  239             if result and not force:
  240                 continue # already recorded
  241 
  242         # commit to database
  243         db.AddCommit(commit)
  244         committed = 1
  245 
  246     if verbose:
  247         if committed:
  248             print "done."
  249         else:
  250             print "skipped (already recorded)."
  251 
  252 def main(command, repository, revs=[], verbose=0, force=0):
  253     cfg = viewvc.load_config(CONF_PATHNAME)
  254     db = cvsdb.ConnectDatabase(cfg)
  255 
  256     # Purge what must be purged.
  257     if command in ('rebuild', 'purge'):
  258         if verbose:
  259             print "Purging commit info for repository root `%s'" % repository
  260         try:
  261             db.PurgeRepository(repository)
  262         except cvsdb.UnknownRepositoryError, e:
  263             if command == 'purge':
  264                 sys.stderr.write("ERROR: " + str(e) + "\n")
  265                 sys.exit(1)
  266 
  267     # Record what must be recorded.
  268     if command in ('rebuild', 'update'):
  269         if not os.path.exists(repository):
  270             sys.stderr.write('ERROR: could not find repository %s\n'
  271                              % (repository))
  272             sys.exit(1)
  273         repo = SvnRepo(repository)
  274         if command == 'rebuild' or (command == 'update' and not revs):
  275             for rev in range(repo.rev_max+1):
  276                 handle_revision(db, command, repo, rev, verbose)
  277         elif command == 'update':
  278             if revs[0] is None:
  279                 revs[0] = repo.rev_max
  280             if revs[1] is None:
  281                 revs[1] = repo.rev_max
  282             revs.sort()
  283             for rev in range(revs[0], revs[1]+1):
  284                 handle_revision(db, command, repo, rev, verbose, force)
  285 
  286 def _rev2int(r):
  287     if r == 'HEAD':
  288         r = None
  289     else:
  290         r = int(r)
  291         if r < 0:
  292             raise ValueError, "invalid revision '%d'" % (r)
  293     return r
  294 
  295 def usage():
  296     cmd = os.path.basename(sys.argv[0])
  297     sys.stderr.write(
  298 """Administer the ViewVC checkins database data for the Subversion repository
  299 located at REPOS-PATH.
  300 
  301 Usage: 1. %s [-v] rebuild REPOS-PATH
  302        2. %s [-v] update REPOS-PATH [REV[:REV2]] [--force]
  303        3. %s [-v] purge REPOS-PATH
  304 
  305 1.  Rebuild the commit database information for the repository located
  306     at REPOS-PATH across all revisions, after first purging
  307     information specific to that repository (if any).
  308 
  309 2.  Update the commit database information for the repository located
  310     at REPOS-PATH across all revisions or, optionally, only for the
  311     specified revision REV (or revision range REV:REV2).  This is just
  312     like rebuilding, except that, unless --force is specified, no
  313     commit information will be stored for commits already present in
  314     the database.  If a range is specified, the revisions will be
  315     processed in ascending order, and you may specify "HEAD" to
  316     indicate "the youngest revision currently in the repository".
  317     
  318 3.  Purge information specific to the repository located at REPOS-PATH
  319     from the database.
  320 
  321 Use the -v flag to cause this script to give progress information as it works.
  322 
  323 """ % (cmd, cmd, cmd))
  324     sys.exit(1)
  325 
  326 if __name__ == '__main__':
  327     verbose = 0
  328     force = 0
  329     args = sys.argv
  330     try:
  331         index = args.index('-v')
  332         verbose = 1
  333         del args[index]
  334     except ValueError:
  335         pass
  336     try:
  337         index = args.index('--force')
  338         force = 1
  339         del args[index]
  340     except ValueError:
  341         pass
  342         
  343     if len(args) < 3:
  344         usage()
  345 
  346     command = args[1].lower()
  347     if command not in ('rebuild', 'update', 'purge'):
  348         sys.stderr.write('ERROR: unknown command %s\n' % command)
  349         usage()
  350 
  351     revs = []
  352     if len(sys.argv) > 3:
  353         if command == 'rebuild':
  354             sys.stderr.write('ERROR: rebuild no longer accepts a revision '
  355                              'number argument.  Usage update --force.')
  356             usage()
  357         elif command != 'update':
  358             usage()
  359         try:
  360             revs = map(lambda x: _rev2int(x), sys.argv[3].split(':'))
  361             if len(revs) > 2:
  362                 raise ValueError, "too many revisions in range"
  363             if len(revs) == 1:
  364                 revs.append(revs[0])
  365         except ValueError:
  366             sys.stderr.write('ERROR: invalid revision specification "%s"\n' \
  367                              % sys.argv[3])
  368             usage()
  369     else:
  370         rev = None
  371 
  372     try:
  373         repository = vclib.svn.canonicalize_rootpath(args[2])
  374         repository = cvsdb.CleanRepository(os.path.abspath(repository))
  375         main(command, repository, revs, verbose, force)
  376     except KeyboardInterrupt:
  377         print
  378         print '** break **'
  379     sys.exit(0)