Esempio n. 1
0
    def __replace_file(self, node, replacement_path):
        """
        Replace the contents of a node.

        @type node: SvnDumpNode
        @param node: The node to replace the contents of.
        @type replacement_path: string
        @param replacement_path: The path to the replacement file.
        @rtype: SvnDumpNode
        @return: The converted node.
        """

        if node.get_text_length() == -1:
            # no text
            self.__print(1, "    no content to replace.")
            return node

        self.__print(1, "    replace content with '%s'" % replacement_path)
        if self.dry_run:
            # do not replace with --dry-run
            return node

        # do the replacement
        newnode = SvnDumpNode(node.get_path(), node.get_action(),
                              node.get_kind())
        if node.has_copy_from():
            newnode.set_copy_from(node.get_copy_from_path(),
                                  node.get_copy_from_rev())
        if node.has_properties():
            newnode.set_properties(node.get_properties())

        newnode.set_text_file(replacement_path)

        return newnode
Esempio n. 2
0
    def __replace_file(self, node, replacement_path):
        """
        Replace the contents of a node.

        @type node: SvnDumpNode
        @param node: The node to replace the contents of.
        @type replacement_path: string
        @param replacement_path: The path to the replacement file.
        @rtype: SvnDumpNode
        @return: The converted node.
        """

        if node.get_text_length() == -1:
            # no text
            self.__print(1, "    no content to replace.")
            return node

        self.__print(1, "    replace content with '%s'" % replacement_path)
        if self.dry_run:
            # do not replace with --dry-run
            return node

        # do the replacement
        newnode = SvnDumpNode(node.get_path(), node.get_action(),
                              node.get_kind())
        if node.has_copy_from():
            newnode.set_copy_from(node.get_copy_from_path(),
                                  node.get_copy_from_rev())
        if node.has_properties():
            newnode.set_properties(node.get_properties())

        newnode.set_text_file(replacement_path)

        return newnode
Esempio n. 3
0
    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

        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 node.has_text():
            newNode.set_text_node(node)
        return newNode
Esempio n. 4
0
def copy_adding_git_ignore(srcfile, dstfile):
    """
    Copy a dump file adding .gitignore files in all directories that have
    an svn:ignore property.

    @type srcfile: string
    @param srcfile: Source filename.
    @type dstfile: string
    @param dstfile: Destination filename.
    """

    # SvnDumpFile classes for reading/writing dumps
    srcdmp = SvnDumpFile()
    dstdmp = SvnDumpFile()

    # open source file
    srcdmp.open(srcfile)
    gitignores = {}
    # used to ensure that copyfrom-revs are correct after the copy.  If
    # there are any empty revision in the source dump file, the copyfrom-revs
    # could be affected.
    revmap = {}

    hasrev = srcdmp.read_next_rev()
    if hasrev:
        # create the dump file
        dstdmp.create_like(dstfile, srcdmp)
        # now copy all the revisions
        while hasrev:
            dstdmp.add_rev(srcdmp.get_rev_props())
            for node in srcdmp.get_nodes_iter():
                if node.has_copy_from():
                    node.set_copy_from(node.get_copy_from_path(),
                                       revmap[node.get_copy_from_rev()])
                # add the original node
                dstdmp.add_node(node)
                if node.get_property("svn:ignore") is not None:
                    # find out what the change is and act on it appropriately
                    path = node.get_path() + "/.gitignore"
                    action = node.get_action()
                    if action == "change" or action == "add":
                        if path in gitignores:
                            # already saw this one - it is a change to the .gitignore file
                            newnode = SvnDumpNode(path, "change", "file")
                        else:
                            # haven't seen this one yet
                            newnode = SvnDumpNode(path, "add", "file")
                            gitignores[path] = True
                        f = open("gitignore", "wb")
                        f.write(node.get_property("svn:ignore"))
                        f.close()
                        newnode.set_text_file("gitignore")
                        dstdmp.add_node(newnode)
                        os.remove("gitignore")
                    elif action == "delete":
                        newnode = SvnDumpNode(path, "delete", "file")
                        dstdmp.add_node(newnode)
                        del gitignores[path]
                    else:
                        print("Unhandled action: '%s'" % action)
            revmap[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()
Esempio n. 5
0
    def read_next_rev( self ):
        """
        Read the next revision.

        @rtype: bool
        @return: False if EOF occured.
        """

        # check state
        if self.__state != self.ST_READ:
            raise SvnDumpException, "invalid state %d (should be %d)" % \
                        ( self.__state, self.ST_READ )

        # check for end of file
        if self.__file_eof:
            self.__state = self.ST_EOF
            return False

        # go to start of revision
        if self.__rev_start_offset != self.__file.tell():
            self.__file.seek( self.__rev_start_offset )

        # get rev tags
        tags = self.__get_tag_list()
        self.__rev_nr = int( tags["Revision-number:"] )

        # read revision properties
        self.__rev_props = self.__get_prop_list()
        self.__skip_empty_line()
        if not self.__rev_props.has_key("svn:log"):
            self.__rev_props["svn:log"] = ""
        if not self.__rev_props.has_key("svn:author"):
            self.__rev_props["svn:author"] = ""
        if self.__rev_props.has_key("svn:date"):
            self.set_rev_date(self.__rev_props["svn:date"] )
        else:
            self.set_rev_date( "" )

        # read nodes (files, dirs)
        self.__nodes.clear()
        #self.nodeList = []
        tags = self.__get_tag_list()
        while len(tags) != 0:
            # check that it's not the next revision
            if tags.has_key( "Revision-number:" ):
                # go back to start of tag list
                self.__file.seek( self.__tag_start_offset )
                self.__line_nr = self.__tag_start_line_nr
                break
            # get node properties
            if tags.has_key( "Prop-content-length:" ):
                properties = self.__get_prop_list()
            else:
                properties = None
            # skip node data
            if tags.has_key( "Text-content-length:" ):
                tags["Text-content-length:"] = int( tags["Text-content-length:"] )
                offset = self.__file.tell()
                self.__skip_bin( tags["Text-content-length:"] )
                self.__skip_empty_line()
            else:
                offset = 0
            # add node
            path = tags["Node-path:"].lstrip('/')
            action = tags["Node-action:"]
            kind = ""
            if tags.has_key( "Node-kind:" ):
                kind = tags["Node-kind:"]
            node = SvnDumpNode( path, action, kind )
            if properties != None:
                node.set_properties( properties )
            if tags.has_key( "Node-copyfrom-path:" ):
                node.set_copy_from( tags["Node-copyfrom-path:"].lstrip('/'),
                                    int(tags["Node-copyfrom-rev:"]) )
            if tags.has_key( "Text-content-md5:" ):
              md5 = tags["Text-content-md5:"]
            else:
              md5 = ""
            if tags.has_key( "Text-content-length:" ):
                node.set_text_fileobj( self.__file, offset,
                                       int(tags["Text-content-length:"]),
                                       md5 )
            upath = ( action[0].upper(), path )
            self.__nodes[upath] = node
            # next one...
            tags = self.__get_tag_list()

        self.__rev_start_offset = self.__file.tell()
        return True
Esempio n. 6
0
    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
Esempio n. 7
0
    def read_next_rev(self):
        """
        Read the next revision.

        @rtype: bool
        @return: False if EOF occured.
        """

        # check state
        if self.__state != self.ST_READ:
            raise SvnDumpException("invalid state %d (should be %d)" % \
                                   (self.__state, self.ST_READ))

        # check for end of file
        if self.__file_eof:
            self.__state = self.ST_EOF
            return False

        # go to start of revision
        if self.__rev_start_offset != self.__file.tell():
            self.__file.seek(self.__rev_start_offset)

        # get rev tags
        tags = self.__get_tag_list()
        self.__rev_nr = int(tags["Revision-number:"])

        # read revision properties
        self.__rev_props = self.__get_prop_list()
        self.__skip_empty_line()
        if not self.__rev_props.has_key("svn:log"):
            self.__rev_props["svn:log"] = ""
        if not self.__rev_props.has_key("svn:author"):
            self.__rev_props["svn:author"] = ""
        if self.__rev_props.has_key("svn:date"):
            self.set_rev_date(self.__rev_props["svn:date"])
        else:
            self.set_rev_date("")

        # read nodes (files, dirs)
        self.__nodes.clear()
        # self.nodeList = []
        tags = self.__get_tag_list()
        while len(tags) != 0:
            # check that it's not the next revision
            if tags.has_key("Revision-number:"):
                # go back to start of tag list
                self.__file.seek(self.__tag_start_offset)
                self.__line_nr = self.__tag_start_line_nr
                break
            # get node properties
            if tags.has_key("Prop-content-length:"):
                properties = self.__get_prop_list()
            else:
                properties = None
            # skip node data
            if tags.has_key("Text-content-length:"):
                tags["Text-content-length:"] = int(tags["Text-content-length:"])
                offset = self.__file.tell()
                self.__skip_bin(tags["Text-content-length:"])
                self.__skip_empty_line()
            else:
                offset = 0
            # add node
            path = tags["Node-path:"].lstrip('/')
            action = tags["Node-action:"]
            kind = ""
            if tags.has_key("Node-kind:"):
                kind = tags["Node-kind:"]
            node = SvnDumpNode(path, action, kind)
            if properties is not None:
                node.set_properties(properties)
            if tags.has_key("Node-copyfrom-path:"):
                node.set_copy_from(tags["Node-copyfrom-path:"].lstrip('/'),
                                   int(tags["Node-copyfrom-rev:"]))
            if tags.has_key("Text-content-md5:"):
                md5 = tags["Text-content-md5:"]
            else:
                md5 = ""
            if tags.has_key("Text-content-sha1:"):
                sha1 = tags["Text-content-sha1:"]
            else:
                sha1 = ""
            if tags.has_key("Text-content-length:"):
                node.set_text_fileobj(self.__file, offset,
                                      int(tags["Text-content-length:"]),
                                      md5,
                                      sha1)
            upath = (action[0].upper(), path)
            self.__nodes[upath] = node
            # next one...
            tags = self.__get_tag_list()

        self.__rev_start_offset = self.__file.tell()
        return True
Esempio n. 8
0
def copy_adding_git_ignore(srcfile, dstfile):
    """
    Copy a dump file adding .gitignore files in all directories that have
    an svn:ignore property.

    @type srcfile: string
    @param srcfile: Source filename.
    @type dstfile: string
    @param dstfile: Destination filename.
    """

    # SvnDumpFile classes for reading/writing dumps
    srcdmp = SvnDumpFile()
    dstdmp = SvnDumpFile()

    # open source file
    srcdmp.open(srcfile)
    gitignores = {}
    # used to ensure that copyfrom-revs are correct after the copy.  If
    # there are any empty revision in the source dump file, the copyfrom-revs
    # could be affected.
    revmap = {}

    hasrev = srcdmp.read_next_rev()
    if hasrev:
        # create the dump file
        dstdmp.create_like(dstfile, srcdmp)
        # now copy all the revisions
        while hasrev:
            dstdmp.add_rev(srcdmp.get_rev_props())
            for node in srcdmp.get_nodes_iter():
                if node.has_copy_from():
                    node.set_copy_from(node.get_copy_from_path(), revmap[node.get_copy_from_rev()])
                # add the original node
                dstdmp.add_node(node)
                if node.get_property("svn:ignore") is not None:
                    # find out what the change is and act on it appropriately
                    path = node.get_path() + "/.gitignore"
                    action = node.get_action()
                    if action == "change" or action == "add":
                        if path in gitignores:
                            # already saw this one - it is a change to the .gitignore file
                            newnode = SvnDumpNode(path, "change", "file")
                        else:
                            # haven't seen this one yet
                            newnode = SvnDumpNode(path, "add", "file")
                            gitignores[path] = True
                        f = open("gitignore", "wb")
                        f.write(node.get_property("svn:ignore"))
                        f.close()
                        newnode.set_text_file("gitignore")
                        dstdmp.add_node(newnode)
                        os.remove("gitignore")
                    elif action == "delete":
                        newnode = SvnDumpNode(path, "delete", "file")
                        dstdmp.add_node(newnode)
                        del gitignores[path]
                    else:
                        print("Unhandled action: '%s'" % action)
            revmap[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()
Esempio n. 9
0
    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
Esempio n. 10
0
    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()
Esempio n. 11
0
    def __convert_eol(self, node, revnr):
        """
        Convert EOL of a node.

        @type node: SvnDumpNode
        @param node: The node to convert.
        @type revnr: integer
        @param revnr: The current revision number.
        @rtype: SvnDumpNode
        @return: The converted node.
        """

        if not node.has_text():
            # no text
            print("    selected file, no text changes")
            return node

        # search for CR
        need_conv = False
        handle = node.text_open()
        data = node.text_read(handle)
        while len(data) > 0:
            if data.find("\r") != -1:
                # found one, need to convert the file
                need_conv = True
                break
            data = node.text_read(handle)
        fix = self.__fix
        if need_conv:
            # special fix option for rev/file ?
            key = (revnr, node.get_path())
            if self.__fix_rev_path.has_key(key):
                fix = self.__fix_rev_path[key]
            print("    selected file, convert (fix option %d)" % fix)
            if self.__dry_run or fix == 0:
                # do not convert with --dry-run or when there's nothing to fix
                need_conv = False
        else:
            print("    selected file, no conversion required")
        if need_conv:
            # do the conversion
            node.text_reopen(handle)
            outfilename = self.__temp_file_name()
            outfile = open(outfilename, "wb")
            outlen = 0
            md = sdt_md5()
            data = node.text_read(handle)
            carry = ""
            warning_printed = False
            while len(data) > 0:
                if len(carry) != 0:
                    data = carry + data
                n = len(data) - 1
                carry = data[n]
                if carry == "\r":
                    data = data[:n]
                else:
                    carry = ""
                if fix & 1 != 0:
                    data = data.replace("\r\n", "\n")
                if fix & 2 != 0:
                    data = data.replace("\r", "\n")
                if fix & 4 != 0:
                    data = data.replace("\r", "")
                if not warning_printed and data.find("\r") >= 0:
                    warning_printed = True
                    print("    WARNING: file still contains CR")
                    print("      file: '%s'" % node.get_path())
                    if self.__warning_file is not None:
                        self.__warning_file.write(
                            "# WARNING: file still contains CR\n")
                        file = node.get_path()
                        while file[0] == "/":
                            file = file[1:]
                        tmpfile = file.replace("/", "__")
                        cmd = '$SVN cat -r %d "$REPOS/%s" > "%s"\n' % \
                              (revnr, node.get_path(), tmpfile)
                        self.__warning_file.write(cmd)
                        self.__warning_count += 1
                md.update(data)
                outfile.write(data)
                outlen = outlen + len(data)
                data = node.text_read(handle)
            if len(carry) != 0:
                if fix & 2 != 0:
                    carry = "\n"
                elif fix & 4 != 0:
                    carry = ""
                outfile.write(carry)
                md.update(carry)
                outlen += len(carry)
            outfile.close()
            newnode = SvnDumpNode(node.get_path(), node.get_action(),
                                  node.get_kind())
            if node.has_copy_from():
                newnode.set_copy_from(node.get_copy_from_path(),
                                      node.get_copy_from_rev())
            if node.has_properties():
                newnode.set_properties(node.get_properties())
            newnode.set_text_file(outfilename, outlen, md.hexdigest())
        else:
            newnode = node

        node.text_close(handle)
        return newnode