def execute( self, dumpfilename, directory ): """ Executes the export. @type dumpfilename: string @param dumpfilename: Name of the svn dump file. @type directory: string @param directory: Directory to store the exported files. """ dump = SvnDumpFile() dump.open( dumpfilename ) while dump.read_next_rev(): revnr = dump.get_rev_nr() if self.__exports.has_key( revnr ): for path, filename in self.__exports[revnr].iteritems(): print "r%-6d %s" % ( revnr, path ) nodes = dump.get_nodes_by_path( path, "ACR" ) saved = False for node in nodes: if node.has_text(): outfile = open( filename, "wb" ) node.write_text_to_file( outfile ) outfile.close() saved = True print " saved as %s" % filename if not saved: if len(nodes) == 0: print " not found" else: print " has no text" dump.close() return 0
def execute(self, inputfile, outputfile): """ Fix a cvs2svn created dump file. @type inputfile: string @param inputfile: Name of the cvs2svn created dump file. @type outputfile: string @param outputfile: Name of the fixed dump file. """ indump = SvnDumpFile() indump.open(inputfile) has_rev = indump.read_next_rev() outdump = SvnDumpFile() outdump.create_like(outputfile, indump) rc = 0 self.__history = {} while has_rev and rc == 0: outdump.add_rev(indump.get_rev_props()) for node in indump.get_nodes_iter(): msglist = self.__fix_node(indump.get_rev_nr(), node) if msglist != None: rc = 1 print "Error in r%d node %s:" % \ ( indump.get_rev_nr(), node.get_path() ) for msg in msglist: print " " + msg break outdump.add_node(node) has_rev = indump.read_next_rev() indump.close() return rc
def execute( self, inputfile, outputfile ): """ Fix a cvs2svn created dump file. @type inputfile: string @param inputfile: Name of the cvs2svn created dump file. @type outputfile: string @param outputfile: Name of the fixed dump file. """ indump = SvnDumpFile() indump.open( inputfile ) has_rev = indump.read_next_rev() outdump = SvnDumpFile() outdump.create_like( outputfile, indump ) rc = 0 self.__history = {} while has_rev and rc == 0: outdump.add_rev( indump.get_rev_props() ) for node in indump.get_nodes_iter(): msglist = self.__fix_node( indump.get_rev_nr(), node ) if msglist != None: rc = 1 print "Error in r%d node %s:" % \ ( indump.get_rev_nr(), node.get_path() ) for msg in msglist: print " " + msg break outdump.add_node( node ) has_rev = indump.read_next_rev() indump.close() return rc
def copy_dump_file( srcfile, dstfile, transformer=None ): """ Copy a dump file. @type srcfile: string @param srcfile: Source filename. @type dstfile: string @param dstfile: Destination filename. @type transformer: class with method transform(dump) @param transformer: A class to perform a transformation on each revision, or None. """ # SvnDumpFile classes for reading/writing dumps srcdmp = SvnDumpFile() dstdmp = SvnDumpFile() # open source file srcdmp.open( srcfile ) hasrev = srcdmp.read_next_rev() if hasrev: # create the dump file dstdmp.create_like( dstfile, srcdmp ) # now copy all the revisions while hasrev: if transformer != None: transformer.transform( srcdmp ) dstdmp.add_rev_from_dump( srcdmp ) hasrev = srcdmp.read_next_rev() else: print "no revisions in the source dump '%s' ???" % srcfile # cleanup srcdmp.close() dstdmp.close()
def execute(self, dumpfilename, directory): """ Executes the export. @type dumpfilename: string @param dumpfilename: Name of the svn dump file. @type directory: string @param directory: Directory to store the exported files. """ dump = SvnDumpFile() dump.open(dumpfilename) while dump.read_next_rev(): revnr = dump.get_rev_nr() if self.__exports.has_key(revnr): for path, filename in self.__exports[revnr].iteritems(): print "r%-6d %s" % (revnr, path) nodes = dump.get_nodes_by_path(path, "ACR") saved = False for node in nodes: if node.has_text(): outfile = open(filename, "wb") node.write_text_to_file(outfile) outfile.close() saved = True print " saved as %s" % filename if not saved: if len(nodes) == 0: print " not found" else: print " has no text" dump.close() return 0
def copy_dump_file(srcfile, dstfile, transformer=None): """ Copy a dump file. @type srcfile: string @param srcfile: Source filename. @type dstfile: string @param dstfile: Destination filename. @type transformer: class with method transform(dump) @param transformer: A class to perform a transformation on each revision, or None. """ # SvnDumpFile classes for reading/writing dumps srcdmp = SvnDumpFile() dstdmp = SvnDumpFile() # open source file srcdmp.open(srcfile) hasrev = srcdmp.read_next_rev() if hasrev: # create the dump file dstdmp.create_like(dstfile, srcdmp) # now copy all the revisions while hasrev: if transformer != None: transformer.transform(srcdmp) dstdmp.add_rev_from_dump(srcdmp) hasrev = srcdmp.read_next_rev() else: print "no revisions in the source dump '%s' ???" % srcfile # cleanup srcdmp.close() dstdmp.close()
def copy_dump_file(srcfile, dstfile, transformer=None): """ Copy a dump file. @type srcfile: string @param srcfile: Source filename. @type dstfile: string @param dstfile: Destination filename. @type transformer: class with method transform(dump) @param transformer: A class to perform a transformation on each revision, or None. """ # SvnDumpFile classes for reading/writing dumps srcdmp = SvnDumpFile() dstdmp = SvnDumpFile() # Copy_from_rev casacading oldRevToNewRev = dict() # open source file srcdmp.open(srcfile) hasrev = srcdmp.read_next_rev() if hasrev: # create the dump file dstdmp.create_like(dstfile, srcdmp) # now copy all the revisions while hasrev: if transformer != None: transformer.transform(srcdmp) for node in srcdmp.get_nodes_iter(): if node.has_copy_from(): if oldRevToNewRev.has_key(node.get_copy_from_rev()): node.set_copy_from_rev( oldRevToNewRev[node.get_copy_from_rev()]) else: #We have a problem, the copy from revision is missing. #We look for a previous revision containing the file found = False candidate = node.get_copy_from_rev() while candidate > 0 and not found: candidate = candidate - 1 found = oldRevToNewRev.has_key(candidate) if found: oldRevToNewRev[ node.get_copy_from_rev()] = candidate node.set_copy_from_rev(candidate) dstdmp.add_rev_from_dump(srcdmp) oldRevToNewRev[srcdmp.get_rev_nr()] = dstdmp.get_rev_nr() hasrev = srcdmp.read_next_rev() else: print "no revisions in the source dump '%s' ???" % srcfile # cleanup srcdmp.close() dstdmp.close()
def copy_dump_file( srcfile, dstfile, transformer=None ): """ Copy a dump file. @type srcfile: string @param srcfile: Source filename. @type dstfile: string @param dstfile: Destination filename. @type transformer: class with method transform(dump) @param transformer: A class to perform a transformation on each revision, or None. """ # SvnDumpFile classes for reading/writing dumps srcdmp = SvnDumpFile() dstdmp = SvnDumpFile() # Copy_from_rev casacading oldRevToNewRev = dict() # open source file srcdmp.open( srcfile ) hasrev = srcdmp.read_next_rev() if hasrev: # create the dump file dstdmp.create_like( dstfile, srcdmp ) # now copy all the revisions while hasrev: if transformer != None: transformer.transform( srcdmp ) for node in srcdmp.get_nodes_iter(): if node.has_copy_from(): if oldRevToNewRev.has_key(node.get_copy_from_rev()): node.set_copy_from_rev(oldRevToNewRev[node.get_copy_from_rev()]) else: #We have a problem, the copy from revision is missing. #We look for a previous revision containing the file found = False candidate = node.get_copy_from_rev() while candidate > 0 and not found: candidate = candidate - 1 found = oldRevToNewRev.has_key(candidate) if found: oldRevToNewRev[node.get_copy_from_rev()] = candidate node.set_copy_from_rev(candidate) dstdmp.add_rev_from_dump( srcdmp ) oldRevToNewRev[srcdmp.get_rev_nr()] = dstdmp.get_rev_nr() hasrev = srcdmp.read_next_rev() else: print "no revisions in the source dump '%s' ???" % srcfile # cleanup srcdmp.close() dstdmp.close()
def execute( self, dumpfilename ): """ Print log of a dump file. @type dumpfilename: string @param dumpfilename: Name of the file to log. """ print "\n\n" + "=" * 72 line = "-" * 72 print "Dumpfile: " + dumpfilename dump = SvnDumpFile() dump.open( dumpfilename ) actions = { "add":"A", "change":"M", "delete":"D", "replace":"R" } while dump.read_next_rev(): revnr = dump.get_rev_nr() if revnr >= self.__from_rev and revnr <= self.__to_rev: author = dump.get_rev_author() date = dump.get_rev_date_str() log = dump.get_rev_log() linecnt = len( log.split( "\n" ) ) lines = "%d line" % linecnt if linecnt > 1: lines += "s" print line print "r%d | %s | %s | %s" % ( revnr, author, date, lines ) if self.__verbose: print "Changed paths:" for node in dump.get_nodes_iter(): action = actions[node.get_action()] path = node.get_path() if path == "" or path[0] != "/": path = "/" + path if node.has_copy_from(): fpath = node.get_copy_from_path() frev = node.get_copy_from_rev() if fpath == "" or fpath[0] != "/": fpath = "/" + fpath path += " (from %s:%d)" % ( fpath, frev ) print " %s %s" % ( action, path ) print "\n" + log.rstrip() + "\n" print line dump.close() return 0
def join_dumpfiles(inputlist, outfilename): """ Joins dump files. @type inputlist: list @param inputlist: A list containing the input filenames. @type outfilename: string @param outfilename: Name of the output dump file. @rtype: int @return: 0 for success. """ outdump = None noutrev = 0 lastrev = -1 for filename in inputlist: print "reading %s ..." % filename ninrev = 0 indump = SvnDumpFile() indump.open(filename) hasrev = indump.read_next_rev() if hasrev: if outdump == None: outdump = SvnDumpFile() if indump.get_rev_nr() == 0: # create new dump with revision 0 outdump.create_with_rev_0(outfilename, indump.get_uuid(), indump.get_rev_date_str()) hasrev = indump.read_next_rev() else: # create new dump starting with the # same revNr as the original dump outdump.create_with_rev_n(outfilename, indump.get_uuid(), indump.get_rev_nr()) else: # check rev number if indump.get_rev_nr() == 0: hasrev = indump.read_next_rev() if hasrev: if (lastrev + 1) != indump.get_rev_nr(): print "renumbering of revisions not supported." print "last rev was %d, next is %d." % ( lastrev, indump.get_rev_nr()) indump.close() outdump.close() return 1 while hasrev: outdump.add_rev_from_dump(indump) ninrev += 1 lastrev = indump.get_rev_nr() hasrev = indump.read_next_rev() indump.close() print " copied %d revisions." % ninrev noutrev += ninrev outdump.close() print "wrote %d revisions, last was r%d." % (noutrev, lastrev)
def join_dumpfiles( inputlist, outfilename ): """ Joins dump files. @type inputlist: list @param inputlist: A list containing the input filenames. @type outfilename: string @param outfilename: Name of the output dump file. @rtype: int @return: 0 for success. """ outdump = None noutrev = 0 lastrev = -1 for filename in inputlist: print "reading %s ..." % filename ninrev = 0 indump = SvnDumpFile() indump.open( filename ) hasrev = indump.read_next_rev() if hasrev: if outdump == None: outdump = SvnDumpFile() if indump.get_rev_nr() == 0: # create new dump with revision 0 outdump.create_with_rev_0( outfilename, indump.get_uuid(), indump.get_rev_date_str() ) hasrev = indump.read_next_rev() else: # create new dump starting with the # same revNr as the original dump outdump.create_with_rev_n( outfilename, indump.get_uuid(), indump.get_rev_nr() ) else: # check rev number if indump.get_rev_nr() == 0: hasrev = indump.read_next_rev() if hasrev: if (lastrev + 1) != indump.get_rev_nr(): print "renumbering of revisions not supported." print "last rev was %d, next is %d." % ( lastrev, indump.get_rev_nr() ) indump.close() outdump.close() return 1 while hasrev: outdump.add_rev_from_dump( indump ) ninrev += 1 lastrev = indump.get_rev_nr() hasrev = indump.read_next_rev() indump.close() print " copied %d revisions." % ninrev noutrev += ninrev outdump.close() print "wrote %d revisions, last was r%d." % ( noutrev, lastrev )
def execute(self, dumpfilename): """ Print log of a dump file. @type dumpfilename: string @param dumpfilename: Name of the file to log. """ print "\n\n" + "=" * 72 line = "-" * 72 print "Dumpfile: " + dumpfilename dump = SvnDumpFile() dump.open(dumpfilename) actions = {"add": "A", "change": "M", "delete": "D", "replace": "R"} while dump.read_next_rev(): revnr = dump.get_rev_nr() if revnr >= self.__from_rev and revnr <= self.__to_rev: author = dump.get_rev_author() date = dump.get_rev_date_str() log = dump.get_rev_log() linecnt = len(log.split("\n")) lines = "%d line" % linecnt if linecnt > 1: lines += "s" print line print "r%d | %s | %s | %s" % (revnr, author, date, lines) if self.__verbose: print "Changed paths:" for node in dump.get_nodes_iter(): action = actions[node.get_action()] path = node.get_path() if path == "" or path[0] != "/": path = "/" + path if node.has_copy_from(): fpath = node.get_copy_from_path() frev = node.get_copy_from_rev() if fpath == "" or fpath[0] != "/": fpath = "/" + fpath path += " (from %s:%d)" % (fpath, frev) print " %s %s" % (action, path) print "\n" + log.rstrip() + "\n" print line dump.close() return 0
def old_execute(self, dumpfilename): """ Print file list of a dump file. @type dumpfilename: string @param dumpfilename: Name of the file to log. """ dump = SvnDumpFile() dump.open(dumpfilename) actions = {"add": "A", "change": "M", "delete": "D", "replace": "R"} lines = "" while dump.read_next_rev(): revnr = dump.get_rev_nr() if revnr == self.revNr or self.revNr == -1: lines = "" for node in dump.get_nodes_iter(): action = actions[node.get_action()] path = node.get_path() if path == "" or path[0] != "/": path = "/" + path if node.has_copy_from(): fpath = node.get_copy_from_path() frev = node.get_copy_from_rev() if fpath == "" or fpath[0] != "/": fpath = "/" + fpath path += " (from %s:%d)" % (fpath, frev) lines += " %s %s\n" % (action, path) if revnr == self.revNr: print lines, lines = "" break if len(lines) > 0: print lines, dump.close() return 0
def old_execute( self, dumpfilename ): """ Print file list of a dump file. @type dumpfilename: string @param dumpfilename: Name of the file to log. """ dump = SvnDumpFile() dump.open( dumpfilename ) actions = { "add":"A", "change":"M", "delete":"D", "replace":"R" } lines = "" while dump.read_next_rev(): revnr = dump.get_rev_nr() if revnr == self.revNr or self.revNr == -1: lines = "" for node in dump.get_nodes_iter(): action = actions[node.get_action()] path = node.get_path() if path == "" or path[0] != "/": path = "/" + path if node.has_copy_from(): fpath = node.get_copy_from_path() frev = node.get_copy_from_rev() if fpath == "" or fpath[0] != "/": fpath = "/" + fpath path += " (from %s:%d)" % ( fpath, frev ) lines += " %s %s\n" % ( action, path ) if revnr == self.revNr: print lines, lines = "" break if len(lines) > 0: print lines, dump.close() return 0
def filter_dump_file(self, srcfile, dstfile ): # SvnDumpFile classes for reading/writing dumps srcdmp = SvnDumpFile() dstdmp = SvnDumpFile() # read a first time source file to find all referenced revisions (they should not be later removed) srcdmp.open( srcfile ) hasrev = srcdmp.read_next_rev() referenced_revs = set() while hasrev: for node in srcdmp.get_nodes_iter(): copy_from_rev = node.get_copy_from_rev() if copy_from_rev != 0: referenced_revs.add(copy_from_rev) hasrev = srcdmp.read_next_rev() srcdmp.close() # start reading again source file srcdmp.open( srcfile ) hasrev = srcdmp.read_next_rev() if hasrev: # create the dump file dstdmp.create_like( dstfile, srcdmp ) # now copy all the revisions while hasrev: if self.verbosity > 0: print "Revision-number: orig=%s, new=%s" % (srcdmp.get_rev_nr(), srcdmp.get_rev_nr()-self.skipped_revisions) if srcdmp.get_rev_log() == "This is an empty revision for padding." and srcdmp.get_rev_nr() not in referenced_revs: if self.verbosity > 0: print " * skipped padding revison (number of skipped revisions = %s)" % self.skipped_revisions self.skipped_revisions += 1 else: self.transform( srcdmp ) self.rev_map[srcdmp.get_rev_nr()] = (srcdmp.get_rev_nr() - self.skipped_revisions) dstdmp.add_rev_from_dump( srcdmp ) hasrev = srcdmp.read_next_rev() else: print "no revisions in the source dump '%s' ???" % srcfile # cleanup srcdmp.close() dstdmp.close()
class SvnDumpMerge: """ A class for merging svn dump files. """ # handle copyfrom-rev !!! def __init__(self): """ Initialize. """ # output file name self.__out_file = "" # date for revision 0 self.__out_r0_date = "9999-99-99T99:99:99.999999Z" # list additional directories self.__out_dirs = [] # log message for directory creating revision self.__out_message = "" # author for the additional revision self.__out_author = "svndumpmerge" # variables used for input dump files # file names self.__in_files = [] # path renames [ [ ( from, to ), ... ], ... ] self.__in_renames = [] # path regex substitutions [ [ ( search, replace ), ... ], ... ] self.__in_regex_subs = [] # mkdir excludes [ {}, ... ] self.__in_excludes = [] # revision number mappings [ {}, ... ] self.__in_rev_nr_maps = [] # dump files (class SvnDumpFile) self.__in_dumps = [] # revision dates of the dumps self.__in_rev_dates = [] def set_output_file(self, filename, startRev=0): """ Sets the output file name and optional start revision. @type filename: string @param filename: Name of the output dump file. @type startRev: integer @param startRev: Start revision number, default is 0. """ self.__out_file = filename self.outStartRev = startRev def add_input_file(self, filename): """ Adds an input file and returns it's index. @type filename: string @param filename: Name of a input dump file. @rtype: integer @return: Index of the input file. """ index = len(self.__in_files) self.__in_files = self.__in_files + [filename] self.__in_renames = self.__in_renames + [[]] self.__in_regex_subs = self.__in_regex_subs + [[]] self.__in_excludes = self.__in_excludes + [{}] self.__in_rev_nr_maps = self.__in_rev_nr_maps + [{}] return index def add_rename(self, index, prefixFrom, prefixTo): """ Adds a path prefix reanme. @type index: integer @param index: Index of the dump file. @type prefixFrom: string @param prefixFrom: From-path prefix (directory). @type prefixTo: string @param prefixTo: To-path prefix (directory). """ # make sure that prefixFrom starts and ends with a / if prefixFrom[0:1] == "/": prefixFrom = prefixFrom[1:] if prefixFrom[len(prefixFrom) - 1:] != "/": prefixFrom = prefixFrom + "/" # make sure that prefixTo starts and ends with a / if prefixTo[0:1] == "/": prefixTo = prefixTo[1:] if prefixTo[len(prefixTo) - 1:] != "/": prefixTo = prefixTo + "/" # add the rename self.__in_renames[index] = self.__in_renames[index] + \ [(prefixFrom, prefixTo)] def add_regex_sub(self, index, reSearch, reReplace): """ Adds a path prefix rename. @type index: integer @param index: Index of the dump file. @type reSearch: string @param reSearch: Search regular expression. @type reReplace: string @param reReplace: Replace regular expression. """ # compile regex object reSearch = re.compile(reSearch) # add the rename self.__in_regex_subs[index] = self.__in_regex_subs[index] + \ [(reSearch, reReplace)] def add_mkdir_exclude(self, index, dirName): """ Adds a mkdir exclude. @type index: integer @param index: Index of the dump file. @type dirName: string @param dirName: Name of the directory. """ # add the mkdir exclude self.__in_excludes[index][dirName] = None def add_directory(self, dirName): """ Adds an additional directory ('mkdir'). @type dirName: string @param dirName: Name of the directory. """ if dirName[0:1] == "/": dirName = dirName[1:] if dirName[-1:] == "/": dirName = dirName[:-1] self.__out_dirs = self.__out_dirs + [dirName] def set_log_message(self, msg): """ Set log message for additional dirs revision. @type msg: string @param msg: Log message. """ self.__out_message = msg def merge(self): """ Executes the merge. """ if len(self.__in_files) == 0: print("merge: no input files specified") return if len(self.__out_file) == 0: print("merge: no output file specified") return # open input dump files for inFile in self.__in_files: inDump = SvnDumpFile() inDump.open(inFile) inDump.read_next_rev() self.__in_dumps = self.__in_dumps + [inDump] if inDump.get_rev_date_str() < self.__out_r0_date: self.__out_r0_date = inDump.get_rev_date_str() # remove empty dumps dumpCount = self.__remove_empty_dumps() if dumpCount == 0: return # open output file self.outDump = SvnDumpFile() if self.outStartRev == 0: self.outDump.create_with_rev_0(self.__out_file, self.__in_dumps[0].get_uuid(), self.__out_r0_date) else: self.outDump.create_with_rev_n(self.__out_file, self.__in_dumps[0].get_uuid(), self.outStartRev) # skip revision 0 of all dumps for inDump in self.__in_dumps: if inDump.get_rev_nr() == 0: # +++ what about r0 revprops? inDump.read_next_rev() # remove empty dumps dumpCount = self.__remove_empty_dumps() if dumpCount == 0: self.outDump.close() return # get revision dates oldest = None oldestStr = "" for index in range(len(self.__in_dumps)): revDat = self.__in_dumps[index].get_rev_date() self.__in_rev_dates.append(revDat) if oldest is None or revDat < oldest: oldest = revDat oldestStr = self.__in_dumps[index].get_rev_date_str() # add additional directories if len(self.__out_dirs) > 0: self.outDump.add_rev({"svn:log": self.__out_message, "svn:author": self.__out_author, "svn:date": oldestStr}) for dirName in self.__out_dirs: node = SvnDumpNode(dirName, "add", "dir") self.outDump.add_node(node) # loop over all revisions while dumpCount > 0: # find index of the oldest revision oldestIndex = 0 for index in range(1, dumpCount): if self.__in_rev_dates[index] < self.__in_rev_dates[oldestIndex]: oldestIndex = index # copy revision self.__copy_revision(oldestIndex) print("Revision: %-8d from r%-8d %s" % (self.outDump.get_rev_nr(), self.__in_dumps[oldestIndex].get_rev_nr(), self.__in_files[oldestIndex])) # read next revision srcDump = self.__in_dumps[oldestIndex] if srcDump.read_next_rev(): self.__in_rev_dates[oldestIndex] = srcDump.get_rev_date() else: dumpCount = self.__remove_empty_dumps() # close output print("created %d revisions" % self.outDump.get_rev_nr()) self.outDump.close() def __copy_revision(self, dumpIndex): """ Copies a revision from inDump[dumpIndex] to outDump. @type dumpIndex: integer @param dumpIndex: Index of the input dump file. """ srcDump = self.__in_dumps[dumpIndex] # add revision and revprops self.outDump.add_rev(srcDump.get_rev_props()) # add nodes index = 0 nodeCount = srcDump.get_node_count() while index < nodeCount: node = srcDump.get_node(index) newNode = self.__change_node(dumpIndex, node) if newNode is not None: self.outDump.add_node(newNode) index = index + 1 # add revision info self.__in_rev_nr_maps[dumpIndex][srcDump.get_rev_nr()] = \ self.outDump.get_rev_nr() def __change_node(self, dumpIndex, node): """ Creates a new node if the path changed, else returns the old node. @type dumpIndex: integer @param dumpIndex: Index of the input dump file. @type node: SvnDumpNode @param node: A node. """ path = node.get_path() # mkdir exclude check if node.get_kind() == "dir" and node.get_action() == "add": if path in self.__in_excludes[dumpIndex]: return None fromPath = "" fromRev = 0 if node.has_copy_from(): fromPath = node.get_copy_from_path() fromRev = node.get_copy_from_rev() change = 0 newPath = self.__rename_path(path, dumpIndex) newFromPath = fromPath newFromRev = fromRev if path != newPath: change = 1 if fromRev > 0: newFromPath = self.__rename_path(fromPath, dumpIndex) if fromPath != newFromPath: change = 1 newFromRev = self.__in_rev_nr_maps[dumpIndex][fromRev] if fromRev != newFromRev: change = 1 newMergeInfo = "" if node.has_properties(): properties = node.get_properties() if properties.has_key('svn:mergeinfo'): mergeInfo = properties['svn:mergeinfo'] for line in mergeInfo.split('\n'): m = re.match('^(.*):(.*)', line) if m is not None: mergePath = m.group(1) revPart = m.group(2) newMergePath = self.__rename_path(mergePath, dumpIndex) if not newMergePath.startswith("/"): newMergePath = "/" + newMergePath if len(newMergeInfo) != 0: newMergeInfo = newMergeInfo + "\n" newMergeInfo = newMergeInfo + newMergePath + ":" revSep = "" for rm in re.finditer('(\d+)(?:-(\d+))?,?', revPart): mergeFrom = int(rm.group(1)) newMergeFrom = self.__in_rev_nr_maps[dumpIndex][mergeFrom] newMergeInfo = newMergeInfo + revSep + str(newMergeFrom) revSep = "," if rm.group(2) is not None: mergeTo = int(rm.group(2)) newMergeTo = self.__in_rev_nr_maps[dumpIndex][mergeTo] newMergeInfo = newMergeInfo + "-" + str(newMergeTo) if mergeInfo != newMergeInfo: change = 1 if not change: # no change needed return node # do the rename newNode = SvnDumpNode(newPath, node.get_action(), node.get_kind()) if node.has_copy_from(): newNode.set_copy_from(newFromPath, newFromRev) if node.has_properties(): newNode.set_properties(node.get_properties()) if len(newMergeInfo) > 0: newNode.set_property('svn:mergeinfo', newMergeInfo) if node.has_text(): newNode.set_text_node(node) return newNode def __rename_path(self, path, dumpIndex): """ Applies the renames to the path and returns the new path. @type path: string @param path: A path. @type renames: list( ( string, string ) ) @param renames: List of rename tuples. @rtype: string @return Renamed path. """ # ensure that path does not have a leading slash if len(path) > 1 and path[0:1] == "/": path = path[1:] sPath = path + "/" for sPfx, dPfx in self.__in_renames[dumpIndex]: sLen = len(sPfx) if sPfx == "/": return dPfx + path elif sPath[:sLen] == sPfx: if len(path) <= len(sPfx): # it's the full path return dPfx[0:len(dPfx) - 1] else: # there's a suffix return dPfx + path[sLen:] for reSearch, sReplace in self.__in_regex_subs[dumpIndex]: path = reSearch.sub(sReplace, path, count=1) return path def __remove_empty_dumps(self): """ Removes dump files which reached EOF and returns the count of dumps. @rtype: integer @return: Count of remaining input dump files. """ index = 0 while index < len(self.__in_dumps): inDump = self.__in_dumps[index] if inDump.has_revision(): index = index + 1 else: inDump.close() eidx = index + 1 self.__in_files[index:eidx] = [] self.__in_renames[index:eidx] = [] self.__in_regex_subs[index:eidx] = [] self.__in_excludes[index:eidx] = [] self.__in_rev_nr_maps[index:eidx] = [] self.__in_dumps[index:eidx] = [] self.__in_rev_dates[index:eidx] = [] return index
def split_dumpfiles(inputfilename, outlist): """ Splits a dump file. @type inputfilename: string @param inputfilename: Name of the input file. @type outlist: list @param outlist: List of tuples containing start revnr, end revnr and filename. @rtype: int @return: 0 for success. """ if len(outlist) == 0: return 0 outlist = outlist[:] outlist.sort() parallel = False for i in range(0, len(outlist) - 1): if outlist[i][1] > outlist[i + 1][0]: parallel = True break if not parallel: indump = SvnDumpFile() indump.open(inputfilename) index = 0 startrev = outlist[index][0] endrev = outlist[index][1] outfile = outlist[index][2] outdump = None while indump.read_next_rev(): revnr = indump.get_rev_nr() if outdump == None: if revnr >= startrev: outdump = SvnDumpFile() if revnr == 0: # create new dump with revision 0 outdump.create_with_rev_0(outfile, indump.get_uuid(), indump.get_rev_date_str()) else: # create new dump starting with the # same revNr as the original dump outdump.create_with_rev_n(outfile, indump.get_uuid(), indump.get_rev_nr()) if outdump != None: # have an output file, copy the revision if revnr > 0 if revnr > 0: outdump.add_rev_from_dump(indump) if revnr >= endrev: # end revision reached outdump.close() outdump = None index += 1 if index >= len(outlist): # done. break # next range startrev = outlist[index][0] endrev = outlist[index][1] outfile = outlist[index][2] if outdump != None: outdump.close() indump.close() else: print "overlapping revision ranges not supported (yet)." return 1 return 0
def execute(self, dumpfilename): """ Print file list of a dump file. @type dumpfilename: string @param dumpfilename: Name of the file to log. """ # pass 1: search copy-from revisions dump = SvnDumpFile() dump.open(dumpfilename) copyfromrevs = {} filedict = {} while dump.read_next_rev(): revnr = dump.get_rev_nr() if revnr > self.revNr: break for node in dump.get_nodes_iter(): #action = actions[node.get_action()] action = node.get_action() path = node.get_path() if path == "" or path[0] != "/": path = "/" + path if action == "add" and node.has_copy_from(): copyfromrevs[node.get_copy_from_rev()] = True dump.close() # pass 2: do the work dump = SvnDumpFile() dump.open(dumpfilename) filedict = {} prevrevnr = 0 while dump.read_next_rev(): revnr = dump.get_rev_nr() # loop over missing revisions prevrevnr += 1 while prevrevnr < revnr: if copyfromrevs.has_key(prevrevnr): copyfromrevs[prevrevnr] = filedict.keys()[:] prevrevnr += 1 if revnr > self.revNr: break for node in dump.get_nodes_iter(): action = node.get_action() path = node.get_path() if path == "" or path[0] != "/": path = "/" + path if action == "add": filedict[path] = path if node.has_copy_from(): frompath = node.get_copy_from_path() + "/" if frompath[0] != "/": frompath = "/" + frompath fromlen = len(frompath) topath = path + "/" for path in copyfromrevs[node.get_copy_from_rev()]: if path.startswith(frompath): newpath = topath + path[fromlen:] filedict[newpath] = newpath elif action == "delete": del filedict[path] if path[-1] != "/": path = path + "/" for subpath in filedict.keys()[:]: if subpath.startswith(path): del filedict[subpath] if copyfromrevs.has_key(revnr): copyfromrevs[revnr] = filedict.keys()[:] dump.close() filelist = [] for path in filedict: filelist.append(path) filelist.sort() for path in filelist: print path return 0
class SvnDumpMerge: """ A class for merging svn dump files. """ # handle copyfrom-rev !!! def __init__(self): """ Initialize. """ # output file name self.__out_file = "" # date for revision 0 self.__out_r0_date = "9999-99-99T99:99:99.999999Z" # list additional directories self.__out_dirs = [] # log message for directory creating revision self.__out_message = "" # author for the additional revision self.__out_author = "svndumpmerge" # variables used for input dump files # file names self.__in_files = [] # path renames [ [ ( from, to ), ... ], ... ] self.__in_renames = [] # path regex substitutions [ [ ( search, replace ), ... ], ... ] self.__in_regex_subs = [] # mkdir excludes [ {}, ... ] self.__in_excludes = [] # revision number mappings [ {}, ... ] self.__in_rev_nr_maps = [] # dump files (class SvnDumpFile) self.__in_dumps = [] # revision dates of the dumps self.__in_rev_dates = [] def set_output_file(self, filename, startRev=0): """ Sets the output file name and optional start revision. @type filename: string @param filename: Name of the output dump file. @type startRev: integer @param startRev: Start revision number, default is 0. """ self.__out_file = filename self.outStartRev = startRev def add_input_file(self, filename): """ Adds an input file and returns it's index. @type filename: string @param filename: Name of a input dump file. @rtype: integer @return: Index of the input file. """ index = len(self.__in_files) self.__in_files = self.__in_files + [filename] self.__in_renames = self.__in_renames + [[]] self.__in_regex_subs = self.__in_regex_subs + [[]] self.__in_excludes = self.__in_excludes + [{}] self.__in_rev_nr_maps = self.__in_rev_nr_maps + [{}] return index def add_rename(self, index, prefixFrom, prefixTo): """ Adds a path prefix reanme. @type index: integer @param index: Index of the dump file. @type prefixFrom: string @param prefixFrom: From-path prefix (directory). @type prefixTo: string @param prefixTo: To-path prefix (directory). """ # make sure that prefixFrom starts and ends with a / if prefixFrom[0:1] == "/": prefixFrom = prefixFrom[1:] if prefixFrom[len(prefixFrom) - 1:] != "/": prefixFrom = prefixFrom + "/" # make sure that prefixTo starts and ends with a / if prefixTo[0:1] == "/": prefixTo = prefixTo[1:] if prefixTo[len(prefixTo) - 1:] != "/": prefixTo = prefixTo + "/" # add the rename self.__in_renames[index] = self.__in_renames[index] + \ [ (prefixFrom, prefixTo ) ] def add_regex_sub(self, index, reSearch, reReplace): """ Adds a path prefix rename. @type index: integer @param index: Index of the dump file. @type reSearch: string @param reSearch: Search regular expression. @type reReplace: string @param reReplace: Replace regular expression. """ # compile regex object reSearch = re.compile(reSearch) # add the rename self.__in_regex_subs[index] = self.__in_regex_subs[index] + \ [ (reSearch, reReplace ) ] def add_mkdir_exclude(self, index, dirName): """ Adds a mkdir exclude. @type index: integer @param index: Index of the dump file. @type dirName: string @param dirName: Name of the directory. """ # add the mkdir exclude self.__in_excludes[index][dirName] = None def add_directory(self, dirName): """ Adds an additional directory ('mkdir'). @type dirName: string @param dirName: Name of the directory. """ if dirName[0:1] == "/": dirName = dirName[1:] if dirName[-1:] == "/": dirName = dirName[:-1] self.__out_dirs = self.__out_dirs + [dirName] def set_log_message(self, msg): """ Set log message for additional dirs revision. @type msg: string @param msg: Log message. """ self.__out_message = msg def merge(self): """ Executes the merge. """ if len(self.__in_files) == 0: print "merge: no input files specified" return if len(self.__out_file) == 0: print "merge: no output file specified" return # open input dump files for inFile in self.__in_files: inDump = SvnDumpFile() inDump.open(inFile) inDump.read_next_rev() self.__in_dumps = self.__in_dumps + [inDump] if inDump.get_rev_date_str() < self.__out_r0_date: self.__out_r0_date = inDump.get_rev_date_str() # remove empty dumps dumpCount = self.__remove_empty_dumps() if dumpCount == 0: return # open output file self.outDump = SvnDumpFile() if self.outStartRev == 0: self.outDump.create_with_rev_0(self.__out_file, self.__in_dumps[0].get_uuid(), self.__out_r0_date) else: self.outDump.create_with_rev_n(self.__out_file, self.__in_dumps[0].get_uuid(), self.outStartRev) # skip revision 0 of all dumps for inDump in self.__in_dumps: if inDump.get_rev_nr() == 0: # +++ what about r0 revprops? inDump.read_next_rev() # remove empty dumps dumpCount = self.__remove_empty_dumps() if dumpCount == 0: self.outDump.close() return # get revision dates oldest = None oldestStr = "" for index in range(len(self.__in_dumps)): revDat = self.__in_dumps[index].get_rev_date() self.__in_rev_dates.append(revDat) if oldest == None or revDat < oldest: oldest = revDat oldestStr = self.__in_dumps[index].get_rev_date_str() # add additional directories if len(self.__out_dirs) > 0: self.outDump.add_rev({ "svn:log": self.__out_message, "svn:author": self.__out_author, "svn:date": oldestStr }) for dirName in self.__out_dirs: node = SvnDumpNode(dirName, "add", "dir") self.outDump.add_node(node) # loop over all revisions while dumpCount > 0: # find index of the oldest revision oldestIndex = 0 for index in range(1, dumpCount): if self.__in_rev_dates[index] < self.__in_rev_dates[ oldestIndex]: oldestIndex = index # copy revision self.__copy_revision(oldestIndex) print "Revision: %-8d from r%-8d %s" % ( self.outDump.get_rev_nr(), self.__in_dumps[oldestIndex].get_rev_nr(), self.__in_files[oldestIndex]) # read next revision srcDump = self.__in_dumps[oldestIndex] if srcDump.read_next_rev(): self.__in_rev_dates[oldestIndex] = srcDump.get_rev_date() else: dumpCount = self.__remove_empty_dumps() # close output print "created %d revisions" % self.outDump.get_rev_nr() self.outDump.close() def __copy_revision(self, dumpIndex): """ Copies a revision from inDump[dumpIndex] to outDump. @type dumpIndex: integer @param dumpIndex: Index of the input dump file. """ srcDump = self.__in_dumps[dumpIndex] # add revision and revprops self.outDump.add_rev(srcDump.get_rev_props()) # add nodes index = 0 nodeCount = srcDump.get_node_count() while index < nodeCount: node = srcDump.get_node(index) newNode = self.__change_node(dumpIndex, node) if newNode != None: self.outDump.add_node(newNode) index = index + 1 # add revision info self.__in_rev_nr_maps[dumpIndex][srcDump.get_rev_nr()] = \ self.outDump.get_rev_nr() def __change_node(self, dumpIndex, node): """ Creates a new node if the path changed, else returns the old node. @type dumpIndex: integer @param dumpIndex: Index of the input dump file. @type node: SvnDumpNode @param node: A node. """ path = node.get_path() # mkdir exclude check if node.get_kind() == "dir" and node.get_action() == "add": if path in self.__in_excludes[dumpIndex]: return None fromPath = "" fromRev = 0 if node.has_copy_from(): fromPath = node.get_copy_from_path() fromRev = node.get_copy_from_rev() change = 0 newPath = self.__rename_path(path, dumpIndex) newFromPath = fromPath newFromRev = fromRev if path != newPath: change = 1 if fromRev > 0: newFromPath = self.__rename_path(fromPath, dumpIndex) if fromPath != newFromPath: change = 1 newFromRev = self.__in_rev_nr_maps[dumpIndex][fromRev] if fromRev != newFromRev: change = 1 newMergeInfo = "" if node.has_properties(): properties = node.get_properties() if properties.has_key('svn:mergeinfo'): mergeInfo = properties['svn:mergeinfo'] for line in mergeInfo.split('\n'): m = re.match('^(.*):(\d+)-(\d+)', line) if m != None: mergePath = m.group(1) mergeFrom = int(m.group(2)) mergeTo = int(m.group(3)) newMergePath = self.__rename_path(mergePath, dumpIndex) newMergeFrom = self.__in_rev_nr_maps[dumpIndex][ mergeFrom] newMergeTo = self.__in_rev_nr_maps[dumpIndex][mergeTo] if len(newMergeInfo) != 0: newMergeInfo = newMergeInfo + "\n" newMergeInfo = newMergeInfo + newMergePath + ":" + str( newMergeFrom) + "-" + str(newMergeTo) if mergeInfo != newMergeInfo: change = 1 if not change: # no change needed return node # do the rename newNode = SvnDumpNode(newPath, node.get_action(), node.get_kind()) if node.has_copy_from(): newNode.set_copy_from(newFromPath, newFromRev) if node.has_properties(): newNode.set_properties(node.get_properties()) if len(newMergeInfo) > 0: newNode.set_property('svn:mergeinfo', newMergeInfo) if node.has_text(): newNode.set_text_node(node) return newNode def __rename_path(self, path, dumpIndex): """ Applies the renames to the path and returns the new path. @type path: string @param path: A path. @type renames: list( ( string, string ) ) @param renames: List of rename tuples. @rtype: string @return Renamed path. """ # ensure that path does not have a leading slash if len(path) > 1 and path[0:1] == "/": path = path[1:] sPath = path + "/" for sPfx, dPfx in self.__in_renames[dumpIndex]: sLen = len(sPfx) if sPfx == "/": return dPfx + path elif sPath[:sLen] == sPfx: if len(path) <= len(sPfx): # it's the full path return dPfx[0:len(dPfx) - 1] else: # there's a suffix return dPfx + path[sLen:] for reSearch, sReplace in self.__in_regex_subs[dumpIndex]: path = reSearch.sub(sReplace, path, count=1) return path def __remove_empty_dumps(self): """ Removes dump files which reached EOF and returns the count of dumps. @rtype: integer @return: Count of remaining input dump files. """ index = 0 while index < len(self.__in_dumps): inDump = self.__in_dumps[index] if inDump.has_revision(): index = index + 1 else: inDump.close() eidx = index + 1 self.__in_files[index:eidx] = [] self.__in_renames[index:eidx] = [] self.__in_regex_subs[index:eidx] = [] self.__in_excludes[index:eidx] = [] self.__in_rev_nr_maps[index:eidx] = [] self.__in_dumps[index:eidx] = [] self.__in_rev_dates[index:eidx] = [] return index
def execute( self, dumpfilename ): """ Print file list of a dump file. @type dumpfilename: string @param dumpfilename: Name of the file to log. """ # pass 1: search copy-from revisions dump = SvnDumpFile() dump.open( dumpfilename ) copyfromrevs = {} filedict = {} while dump.read_next_rev(): revnr = dump.get_rev_nr() if revnr > self.revNr: break for node in dump.get_nodes_iter(): #action = actions[node.get_action()] action = node.get_action() path = node.get_path() if path == "" or path[0] != "/": path = "/" + path if action == "add" and node.has_copy_from(): copyfromrevs[node.get_copy_from_rev()] = True dump.close() # pass 2: do the work dump = SvnDumpFile() dump.open( dumpfilename ) filedict = {} prevrevnr = 0 while dump.read_next_rev(): revnr = dump.get_rev_nr() # loop over missing revisions prevrevnr += 1 while prevrevnr < revnr: if copyfromrevs.has_key(prevrevnr): copyfromrevs[prevrevnr] = filedict.keys()[:] prevrevnr += 1 if revnr > self.revNr: break for node in dump.get_nodes_iter(): action = node.get_action() path = node.get_path() if path == "" or path[0] != "/": path = "/" + path if action == "add": filedict[path] = path if node.has_copy_from(): frompath = node.get_copy_from_path() + "/" if frompath[0] != "/": frompath = "/" + frompath fromlen = len(frompath) topath = path + "/" for path in copyfromrevs[node.get_copy_from_rev()]: if path.startswith( frompath ): newpath = topath + path[fromlen:] filedict[newpath] = newpath elif action == "delete": del filedict[path] if path[-1] != "/": path = path + "/" for subpath in filedict.keys()[:]: if subpath.startswith( path ): del filedict[subpath] if copyfromrevs.has_key(revnr): copyfromrevs[revnr] = filedict.keys()[:] dump.close() filelist = [] for path in filedict: filelist.append( path ) filelist.sort() for path in filelist: print path return 0
def split_dumpfiles( inputfilename, outlist ): """ Splits a dump file. @type inputfilename: string @param inputfilename: Name of the input file. @type outlist: list @param outlist: List of tuples containing start revnr, end revnr and filename. @rtype: int @return: 0 for success. """ if len(outlist) == 0: return 0 outlist = outlist[:] outlist.sort() parallel = False for i in range( 0, len(outlist)-1 ): if outlist[i][1] > outlist[i+1][0]: parallel = True break if not parallel: indump = SvnDumpFile() indump.open( inputfilename ) index = 0 startrev = outlist[index][0] endrev = outlist[index][1] outfile = outlist[index][2] outdump = None while indump.read_next_rev(): revnr = indump.get_rev_nr() if outdump == None: if revnr >= startrev: outdump = SvnDumpFile() if revnr == 0: # create new dump with revision 0 outdump.create_with_rev_0( outfile, indump.get_uuid(), indump.get_rev_date_str() ) else: # create new dump starting with the # same revNr as the original dump outdump.create_with_rev_n( outfile, indump.get_uuid(), indump.get_rev_nr() ) if outdump != None: # have an output file, copy the revision if revnr > 0 if revnr > 0: outdump.add_rev_from_dump( indump ) if revnr >= endrev: # end revision reached outdump.close() outdump = None index += 1 if index >= len(outlist): # done. break # next range startrev = outlist[index][0] endrev = outlist[index][1] outfile = outlist[index][2] if outdump != None: outdump.close() indump.close() else: print "overlapping revision ranges not supported (yet)." return 1 return 0