Exemple #1
0
  def delete_path(self, lod, cvs_path):
    dir_path, basename = path_split(lod.get_path(cvs_path.get_cvs_path()))
    if basename == '.cvsignore':
      # When a .cvsignore file is deleted, the directory's svn:ignore
      # property needs to be deleted.
      ignore_contents = 'PROPS-END\n'
      ignore_len = len(ignore_contents)

      # write headers, then props
      self._dumpfile.write(
          'Node-path: %s\n'
          'Node-kind: dir\n'
          'Node-action: change\n'
          'Prop-content-length: %d\n'
          'Content-length: %d\n'
          '\n'
          '%s'
          % (self._utf8_path(dir_path),
             ignore_len, ignore_len, ignore_contents)
          )
      if not Ctx().keep_cvsignore:
        return

    self._dumpfile.write(
        'Node-path: %s\n'
        'Node-action: delete\n'
        '\n'
        % (self._utf8_path(lod.get_path(cvs_path.cvs_path)),)
        )
Exemple #2
0
  def _register_basic_directory(self, path, create):
    """Register the creation of PATH if it is not already there.

    Create any parent directories that do not already exist.  If
    CREATE is set, also create PATH if it doesn't already exist.  This
    method should only be used for the LOD paths and the directories
    containing them, not for directories within an LOD path."""

    if path not in self._basic_directories:
      # Make sure that the parent directory is present:
      self._register_basic_directory(path_split(path)[0], True)
      if create:
        self._make_any_dir(path)
      self._basic_directories.add(path)
Exemple #3
0
  def _add_or_change_path(self, cvs_rev, op):
    """Emit the addition or change corresponding to CVS_REV.

    OP is either the constant OP_ADD or OP_CHANGE."""

    assert op in [OP_ADD, OP_CHANGE]

    # The property handling here takes advantage of an undocumented
    # but IMHO consistent feature of the Subversion dumpfile-loading
    # code.  When a node's properties aren't mentioned (that is, the
    # "Prop-content-length:" header is absent, no properties are
    # listed at all, and there is no "PROPS-END\n" line) then no
    # change is made to the node's properties.
    #
    # This is consistent with the way dumpfiles behave w.r.t. text
    # content changes, so I'm comfortable relying on it.  If you
    # commit a change to *just* the properties of some node that
    # already has text contents from a previous revision, then in the
    # dumpfile output for the prop change, no "Text-content-length:"
    # nor "Text-content-md5:" header will be present, and the text of
    # the file will not be given.  But this does not cause the file's
    # text to be erased!  It simply remains unchanged.
    #
    # This works out great for cvs2svn, due to lucky coincidences:
    #
    # For files, we set most properties in the first revision and
    # never change them.  (The only exception is the 'cvs2svn:cvs-rev'
    # property.)  If 'cvs2svn:cvs-rev' is not being used, then there
    # is no need to remember the full set of properties on a given
    # file once we've set it.
    #
    # For directories, the only property we set is "svn:ignore", and
    # while we may change it after the first revision, we always do so
    # based on the contents of a ".cvsignore" file -- in other words,
    # CVS is doing the remembering for us, so we still don't have to
    # preserve the previous value of the property ourselves.

    # Calculate the (sorted-by-name) property string and length, if any.
    svn_props = cvs_rev.get_properties()
    if cvs_rev.properties_changed:
      prop_contents = self._string_for_props(svn_props)
      props_header = 'Prop-content-length: %d\n' % len(prop_contents)
    else:
      prop_contents = ''
      props_header = ''

    data = self._revision_reader.get_content(cvs_rev)

    # treat .cvsignore as a directory property
    dir_path, basename = path_split(cvs_rev.get_svn_path())
    if basename == '.cvsignore':
      ignore_contents = self._string_for_props({
          'svn:ignore' : ''.join((s + '\n') for s in generate_ignores(data))
          })
      ignore_len = len(ignore_contents)

      # write headers, then props
      self._dumpfile.write(
          'Node-path: %s\n'
          'Node-kind: dir\n'
          'Node-action: change\n'
          'Prop-content-length: %d\n'
          'Content-length: %d\n'
          '\n'
          '%s'
          % (self._utf8_path(dir_path),
             ignore_len, ignore_len, ignore_contents)
          )
      if not Ctx().keep_cvsignore:
        return

    checksum = md5()
    checksum.update(data)

    # The content length is the length of property data, text data,
    # and any metadata around/inside around them:
    self._dumpfile.write(
        'Node-path: %s\n'
        'Node-kind: file\n'
        'Node-action: %s\n'
        '%s'  # no property header if no props
        'Text-content-length: %d\n'
        'Text-content-md5: %s\n'
        'Content-length: %d\n'
        '\n' % (
            self._utf8_path(cvs_rev.get_svn_path()), op, props_header,
            len(data), checksum.hexdigest(), len(data) + len(prop_contents),
            )
        )

    if prop_contents:
      self._dumpfile.write(prop_contents)

    self._dumpfile.write(data)

    # This record is done (write two newlines -- one to terminate
    # contents that weren't themselves newline-termination, one to
    # provide a blank line for readability.
    self._dumpfile.write('\n\n')
  def _add_or_change_path(self, s_item, op):
    """Emit the addition or change corresponding to S_ITEM.

    OP is either the constant OP_ADD or OP_CHANGE."""

    assert op in [OP_ADD, OP_CHANGE]

    # Convenience variables
    cvs_rev = s_item.cvs_rev

    # The property handling here takes advantage of an undocumented
    # but IMHO consistent feature of the Subversion dumpfile-loading
    # code.  When a node's properties aren't mentioned (that is, the
    # "Prop-content-length:" header is absent, no properties are
    # listed at all, and there is no "PROPS-END\n" line) then no
    # change is made to the node's properties.
    #
    # This is consistent with the way dumpfiles behave w.r.t. text
    # content changes, so I'm comfortable relying on it.  If you
    # commit a change to *just* the properties of some node that
    # already has text contents from a previous revision, then in the
    # dumpfile output for the prop change, no "Text-content-length:"
    # nor "Text-content-md5:" header will be present, and the text of
    # the file will not be given.  But this does not cause the file's
    # text to be erased!  It simply remains unchanged.
    #
    # This works out great for cvs2svn, due to lucky coincidences:
    #
    # For files, the only properties we ever set are set in the first
    # revision; all other revisions (including on branches) inherit
    # from that.  After the first revision, we never change file
    # properties, therefore, there is no need to remember the full set
    # of properties on a given file once we've set it.
    #
    # For directories, the only property we set is "svn:ignore", and
    # while we may change it after the first revision, we always do so
    # based on the contents of a ".cvsignore" file -- in other words,
    # CVS is doing the remembering for us, so we still don't have to
    # preserve the previous value of the property ourselves.

    # Calculate the (sorted-by-name) property string and length, if any.
    if s_item.svn_props_changed:
      svn_props = s_item.svn_props
      prop_contents = ''
      prop_names = svn_props.keys()
      prop_names.sort()
      for pname in prop_names:
        pvalue = svn_props[pname]
        if pvalue is not None:
          prop_contents += self._string_for_prop(pname, pvalue)
      prop_contents += 'PROPS-END\n'
      props_header = 'Prop-content-length: %d\n' % len(prop_contents)
    else:
      prop_contents = ''
      props_header = ''

    # If the file has keywords, we must prevent CVS/RCS from expanding
    # the keywords because they must be unexpanded in the repository,
    # or Subversion will get confused.
    stream = self._revision_reader.get_content_stream(
        cvs_rev, suppress_keyword_substitution=s_item.has_keywords()
        )

    if Ctx().decode_apple_single:
      # Insert a filter to decode any files that are in AppleSingle
      # format:
      stream = get_maybe_apple_single_stream(stream)

    # Insert a filter to convert all EOLs to LFs if neccessary

    eol_style = s_item.svn_props.get('svn:eol-style', None)
    if eol_style:
      stream = LF_EOL_Filter(stream, eol_style)

    buf = None

    # treat .cvsignore as a directory property
    dir_path, basename = path_split(cvs_rev.get_svn_path())
    if basename == '.cvsignore':
      buf = stream.read()
      ignore_vals = generate_ignores(buf)
      ignore_contents = '\n'.join(ignore_vals)
      if ignore_contents:
        ignore_contents += '\n'
      ignore_contents = ('K 10\nsvn:ignore\nV %d\n%s\n' % \
                         (len(ignore_contents), ignore_contents))
      ignore_contents += 'PROPS-END\n'
      ignore_len = len(ignore_contents)

      # write headers, then props
      self.dumpfile.write(
          'Node-path: %s\n'
          'Node-kind: dir\n'
          'Node-action: change\n'
          'Prop-content-length: %d\n'
          'Content-length: %d\n'
          '\n'
          '%s'
          % (self._utf8_path(dir_path),
             ignore_len, ignore_len, ignore_contents)
          )
      if not Ctx().keep_cvsignore:
        stream.close()
        return

    self.dumpfile.write(
        'Node-path: %s\n'
        'Node-kind: file\n'
        'Node-action: %s\n'
        '%s'  # no property header if no props
        % (self._utf8_path(cvs_rev.get_svn_path()), op, props_header)
        )

    pos = self.dumpfile.tell()

    content_header_fmt = (
        'Text-content-length: %16d\n'
        'Text-content-md5: %32s\n'
        'Content-length: %16d\n'
        '\n'
        )

    self.dumpfile.write(content_header_fmt % (0, '', 0,))

    if prop_contents:
      self.dumpfile.write(prop_contents)

    # Insert the rev contents, calculating length and checksum as we go.
    checksum = md5()
    length = 0
    if buf is None:
      buf = stream.read(config.PIPE_READ_SIZE)
    while buf != '':
      checksum.update(buf)
      length += len(buf)
      self.dumpfile.write(buf)
      buf = stream.read(config.PIPE_READ_SIZE)

    stream.close()

    # Go back to overwrite the length and checksum headers with the
    # correct values.  The content length is the length of property
    # data, text data, and any metadata around/inside around them:
    self.dumpfile.seek(pos, 0)
    self.dumpfile.write(
        content_header_fmt
        % (length, checksum.hexdigest(), length + len(prop_contents),)
        )

    # Jump back to the end of the stream
    self.dumpfile.seek(0, 2)

    # This record is done (write two newlines -- one to terminate
    # contents that weren't themselves newline-termination, one to
    # provide a blank line for readability.
    self.dumpfile.write('\n\n')