3 # hot-backup.py: perform a "hot" backup of a Berkeley DB repository.
4 # (and clean old logfiles after backup completes.)
6 # Subversion is a tool for revision control.
7 # See http://subversion.tigris.org for more information.
9 # ====================================================================
10 # Copyright (c) 2000-2004 CollabNet. All rights reserved.
12 # This software is licensed as described in the file COPYING, which
13 # you should have received as part of this distribution. The terms
14 # are also available at http://subversion.tigris.org/license-1.html.
15 # If newer versions of this license are posted there, you may use a
16 # newer version instead, at your option.
18 # This software consists of voluntary contributions made by many
19 # individuals. For exact contribution history, see the revision
20 # history and logs, available at http://subversion.tigris.org/.
21 # ====================================================================
23 ######################################################################
25 import sys, os, shutil, string, re, subprocess
27 ######################################################################
30 svnpath = os.environ["SVN_PATH"]
32 # Path to svnlook utility
33 svnlook = svnpath + "/svnlook"
35 # Path to svnadmin utility
36 svnadmin = svnpath + "/svnadmin"
38 # Number of backups to keep around (0 for "keep them all")
41 ######################################################################
42 # Command line arguments
45 if len(sys.argv) != 3:
46 print "Usage: ", os.path.basename(sys.argv[0]), " <repos_path> <backup_path>"
50 repo_dir = sys.argv[1]
51 repo = os.path.basename(os.path.abspath(repo_dir))
53 # Where to store the repository backup. The backup will be placed in
54 # a *subdirectory* of this location, named after the youngest
56 backup_dir = sys.argv[2]
58 ######################################################################
62 # We pass in filenames so there is never a case where they are equal.
63 regexp = re.compile("-(?P<revision>[0-9]+)(-(?P<increment>[0-9]+))?$")
64 matcha = regexp.search(a)
65 matchb = regexp.search(b)
66 reva = int(matcha.groupdict()['revision'])
67 revb = int(matchb.groupdict()['revision'])
73 inca = matcha.groupdict()['increment']
74 incb = matchb.groupdict()['increment']
79 elif (int(inca) < int(incb)):
84 ######################################################################
87 print "Beginning hot backup of '"+ repo_dir + "'."
90 ### Step 1: get the youngest revision.
92 p = subprocess.Popen([svnlook, 'youngest', repo_dir],
93 stdin=subprocess.PIPE,
94 stdout=subprocess.PIPE,
95 stderr=subprocess.PIPE)
96 infile, outfile, errfile = p.stdin, p.stdout, p.stderr
97 stdout_lines = outfile.readlines()
98 stderr_lines = errfile.readlines()
104 youngest = string.strip(stdout_lines[0])
105 print "Youngest revision is", youngest
107 print "Error executing svnlook, maybe wrong SVN_PATH?"
111 ### Step 2: Find next available backup path
113 backup_subdir = os.path.join(backup_dir, repo + "-" + youngest)
115 # If there is already a backup of this revision, then append the
116 # next highest increment to the path. We still need to do a backup
117 # because the repository might have changed despite no new revision
118 # having been created. We find the highest increment and add one
119 # rather than start from 1 and increment because the starting
120 # increments may have already been removed due to num_backups.
122 regexp = re.compile("^" + repo + "-" + youngest + "(-(?P<increment>[0-9]+))?$")
123 directory_list = os.listdir(backup_dir)
124 young_list = filter(lambda x: regexp.search(x), directory_list)
126 young_list.sort(comparator)
127 increment = regexp.search(young_list.pop()).groupdict()['increment']
129 backup_subdir = os.path.join(backup_dir, repo + "-" + youngest + "-"
130 + str(int(increment) + 1))
132 backup_subdir = os.path.join(backup_dir, repo + "-" + youngest + "-1")
134 ### Step 3: Ask subversion to make a hot copy of a repository.
137 print "Backing up repository to '" + backup_subdir + "'..."
138 err_code = os.spawnl(os.P_WAIT, svnadmin, "svnadmin", "hotcopy", repo_dir,
139 backup_subdir, "--clean-logs")
141 print "Unable to backup the repository."
147 ### Step 4: finally, remove all repository backups other than the last
151 regexp = re.compile("^" + repo + "-[0-9]+(-[0-9]+)?$")
152 directory_list = os.listdir(backup_dir)
153 old_list = filter(lambda x: regexp.search(x), directory_list)
154 old_list.sort(comparator)
155 del old_list[max(0,len(old_list)-num_backups):]
156 for item in old_list:
157 old_backup_subdir = os.path.join(backup_dir, item)
158 print "Removing old backup: " + old_backup_subdir
159 shutil.rmtree(old_backup_subdir)