Index: svn-restore-dumps.py =================================================================== --- svn-restore-dumps.py (revision 2) +++ svn-restore-dumps.py (working copy) @@ -33,7 +33,7 @@ # -__version = "0.2" +__version = "0.3" import sys import os @@ -151,6 +151,7 @@ self.__dumpdir = args[2] # set options self.__create = options.create + self.__verbose = options.verbose # check repospath rpathparts = os.path.split(self.__repospath) if len(rpathparts[1]) == 0: @@ -307,73 +308,99 @@ print(r[2]) return rc - def get_dump_for_rev(self, respos_rev): - search_rev = respos_rev - if search_rev != 0: - search_rev += 1 # we're looking for the next rev following + class DumpInfo: + def __init__(self, filename, rev_end, file_extension): + self.filename = filename + self.rev_end = rev_end + self.file_extension = file_extension - highest_match_rev = search_rev - 1 # looking for somthing greater than search_rev-1 - highest_non_match_rev = -1 # looking for anything at all - dump_name = None - dump_ext = None - + def scan_all_dump_files(self): prog = re.compile("(.+)\.(\d+)-(\d+)\.svndmp(.*)") + highest_dump_rev = -1 + dump_info_table = dict() # keyed on the rev_start + for filename in os.listdir(self.__dumpdir): m = prog.match( filename ) if not m: + # a non-matching filename continue + if m.group(1) != self.__reposname: - print "Ignoring dump file '%s' - this is for another repos" % filename - else: - rev_start = int(m.group(2)) - rev_end = int(m.group(3)) + if self.__verbose: + print("Ignoring dump file '%s' - this is for another repos" % filename) + continue - if (rev_start == search_rev) and (rev_end > highest_match_rev): - # the revision we want is in this range - highest_match_rev = rev_end - dump_name = filename # keep track of the best so far - dump_ext = None - if m.group(4): - if m.group(4)[0] == ".": - dump_ext = m.group(4)[1:] # grab the filename extension - elif (rev_end > highest_non_match_rev): - highest_non_match_rev = rev_end + rev_start = int(m.group(2)) + rev_end = int(m.group(3)) - if not dump_name: - # no dump found, probably just got to the end, but check for some inconsistencies - if highest_non_match_rev > (search_rev - 1): - raise SvnRestoreException("Cannot find dump file for revision %s yet later dumps exist" % search_rev) - elif highest_non_match_rev == -1: - raise SvnRestoreException("There are no matching dump files in this directory") - elif highest_non_match_rev < respos_rev: - raise SvnRestoreException("The repository revision (%s) is later than the highest dump (%s)" % (respos_rev, highest_non_match_rev)) + dump_ext = None + if m.group(4): + if m.group(4)[0] == ".": + dump_ext = m.group(4)[1:] # grab the filename extension - return dump_name, dump_ext + dump_info = SvnRestore.DumpInfo( filename, rev_end, dump_ext ) + if rev_start in dump_info_table: + # only overwrite if a larger range of revisions is covered + if rev_end <= dump_info_table[rev_start].rev_end: + if (rev_end == dump_info_table[rev_start].rev_end) and self.__verbose: + # this would indicate multiple 'matching' files - each with a unique extension + print("Ignoring dump file '%s' for duplicate revision range %s-%s" % (filename, rev_start, rev_end)) + continue + + # add or overwrite the existing entry + dump_info_table[rev_start] = dump_info + + if rev_end > highest_dump_rev: + highest_dump_rev = rev_end + + if highest_dump_rev == -1: + raise SvnRestoreException("There are no matching dump files in this directory") + + return dump_info_table, highest_dump_rev + def load_dumps(self): - while True: - headrev = self.get_head_rev() - if headrev == -1: - return False + dump_info_table, highest_dump_rev = self.scan_all_dump_files() - dump_file, dump_ext = self.get_dump_for_rev(headrev) - if not dump_file: - return True + headrev = self.get_head_rev() + if headrev == -1: + return False + if self.__verbose: + print("Repository revision is %s" % headrev) + if headrev > highest_dump_rev: + raise SvnRestoreException("The repository revision (%s) is later than the highest dump (%s)" % (headrev, highest_dump_rev)) + + if headrev == 0: + # special case to ensure revision 0 is loaded against the repository + revision_required = 0 + else: + revision_required = headrev + 1 + + while revision_required <= highest_dump_rev: + try: + dump_info = dump_info_table[revision_required] + except KeyError: + # must be a gap in the dumps - we should be able to go up to highest_dump_rev + raise SvnRestoreException("Cannot find dump file for revision %s yet later dumps exist" % revision_required) + + if self.__verbose: + print("Processing dump file %s, for rev %s" % (dump_info.filename, revision_required)) + input = None - if dump_ext: - if dump_ext == "gz": - input = SvnRestoreInputGzip(self.__dumpdir, dump_file) - elif dump_ext == "bz2": + if dump_info.file_extension: + if dump_info.file_extension == "gz": + input = SvnRestoreInputGzip(self.__dumpdir, dump_info.filename) + elif dump_info.file_extension == "bz2": if not have_bz2: raise SvnRestoreException("bz2 decompression unavailable on this system") - input = SvnRestoreInputBzip2(self.__dumpdir, dump_file) + input = SvnRestoreInputBzip2(self.__dumpdir, dump_info.filename) else: - raise SvnRestoreException("unknown extension for dump_file '%s'." % dump_file) + raise SvnRestoreException("unsupported extension for dump_file '%s'." % dump_info.filename) else: - input = SvnRestoreInputPlain(self.__dumpdir, dump_file) - + input = SvnRestoreInputPlain(self.__dumpdir, dump_info.filename) + cmd = [ "svnadmin", "load", self.__repospath ] input.open() rc = self.exec_input_cmd(cmd, input) @@ -382,7 +409,11 @@ if rc: return False + revision_required = dump_info.rev_end + 1 + return True + + if __name__ == "__main__": usage = "usage: svn-restore-dumps.py [options] repospath dumpdir" parser = OptionParser(usage=usage, version="%prog "+__version) @@ -390,6 +421,10 @@ action="store_true", dest="create", default=False, help="create repository if it doesn't exist.") + parser.add_option("-v", + action="store_true", + dest="verbose", default=False, + help="verbose logging.") (options, args) = parser.parse_args(sys.argv) rc = False try: