天天看点

svn问题处理

1.问题:svn客户端更新代码报错“svn: This client is too old to work with……”

原因:svn之间的版本差异造成。当我们已经用1.4版本的svn checkout一份work copy后,但某天我们尝试用高版本1.5svn更新,等再切换成1.4svn更新代码时就会出现此错误。具体请见”http://subversion.apache.org/faq.html#working-copy-format-change”

解决方法:除了客户端升级之外,我们还可以通过对work copy进行降级处理。如下:

[[email protected] ~]# svn up /test/web
svn: This client is too old to work with working copy ‘/test/web’. You need
to get a newer Subversion client, or to downgrade this working copy.
See http://subversion.tigris.org/faq.html#working-copy-format-change
for details.
           

(1)下载脚本change-svn-wc-format.py

[root@test ~]# wget http://svn.apache.org/repos/asf/subversion/trunk/tools/client-side/change-svn-wc-format.py
[root@test ~]# python change-svn-wc-format.py --help
usage: change-svn-wc-format.py WC_PATH SVN_VERSION [--verbose] [--force] [--skip-unknown-format]
       change-svn-wc-format.py --help

Change the format of a Subversion working copy to that of SVN_VERSION.

  --skip-unknown-format    : skip directories with unknown working copy
                             format and continue the update
           

其中:

WC_PATH 表示你的work copy

SVN_VERSION 表示你当前使用的svn客户端版本号,如版本为1.4.2,就写成1.4

(2)执行命令

[root@test ~]# python change-svn-wc-format.py /web/app.cityrebank.com  --skip-unknown-format
Converted WC at '/test/web' into format  for Subversion 
           

(3)svn up 再次更新,成功

脚本代码如下:

[root@test ~]# vim change-svn-wc-format.py
#!/usr/bin/env python
#
# change-svn-wc-format.py: Change the format of a Subversion working copy.
#
# ====================================================================
#    Licensed to the Apache Software Foundation (ASF) under one
#    or more contributor license agreements.  See the NOTICE file
#    distributed with this work for additional information
#    regarding copyright ownership.  The ASF licenses this file
#    to you under the Apache License, Version 2.0 (the
#    "License"); you may not use this file except in compliance
#    with the License.  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing,
#    software distributed under the License is distributed on an
#    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
#    KIND, either express or implied.  See the License for the
#    specific language governing permissions and limitations
#    under the License.
# ====================================================================

import sys
import os
import getopt
try:
  my_getopt = getopt.gnu_getopt
except AttributeError:
  my_getopt = getopt.getopt

### The entries file parser in subversion/tests/cmdline/svntest/entry.py
### handles the XML-based WC entries file format used by Subversion
### 1.3 and lower.  It could be rolled into this script.

LATEST_FORMATS = { "1.4" : ,
                   "1.5" : ,
                   "1.6" : ,
                   # Do NOT add format 11 here.  See comment in must_retain_fields
                   # for why.
                 }

def usage_and_exit(error_msg=None):
  """Write usage information and exit.  If ERROR_MSG is provide, that
  error message is printed first (to stderr), the usage info goes to
  stderr, and the script exits with a non-zero status.  Otherwise,
  usage info goes to stdout and the script exits with a zero status."""
  progname = os.path.basename(sys.argv[])

  stream = error_msg and sys.stderr or sys.stdout
  if error_msg:
    stream.write("ERROR: %s\n\n" % error_msg)
  stream.write("""\
usage: %s WC_PATH SVN_VERSION [--verbose] [--force] [--skip-unknown-format]
       %s --help

Change the format of a Subversion working copy to that of SVN_VERSION.

  --skip-unknown-format    : skip directories with unknown working copy
                             format and continue the update

""" % (progname, progname))
  stream.flush()
  sys.exit(error_msg and  or )

def get_adm_dir():
  """Return the name of Subversion's administrative directory,
  adjusted for the SVN_ASP_DOT_NET_HACK environment variable.  See
  <http://svn.apache.org/repos/asf/subversion/trunk/notes/asp-dot-net-hack.txt>
  for details."""
  return "SVN_ASP_DOT_NET_HACK" in os.environ and "_svn" or ".svn"

class WCFormatConverter:
  "Performs WC format conversions."
  root_path = None
  error_on_unrecognized = True
  force = False
  verbosity = 

  def write_dir_format(self, format_nbr, dirname, paths):
    """Attempt to write the WC format FORMAT_NBR to the entries file
    for DIRNAME.  Throws LossyConversionException when not in --force
    mode, and unconvertable WC data is encountered."""

    # Avoid iterating in unversioned directories.
    if not (get_adm_dir() in paths):
      del paths[:]
      return

    # Process the entries file for this versioned directory.
    if self.verbosity:
      print("Processing directory '%s'" % dirname)
    entries = Entries(os.path.join(dirname, get_adm_dir(), "entries"))
    entries_parsed = True
    if self.verbosity:
      print("Parsing file '%s'" % entries.path)
    try:
      entries.parse(self.verbosity)
    except UnrecognizedWCFormatException, e:
      if self.error_on_unrecognized:
        raise
      sys.stderr.write("%s, skipping\n" % e)
      sys.stderr.flush()
      entries_parsed = False

    if entries_parsed:
      format = Format(os.path.join(dirname, get_adm_dir(), "format"))
      if self.verbosity:
        print("Updating file '%s'" % format.path)
      format.write_format(format_nbr, self.verbosity)
    else:
      if self.verbosity:
        print("Skipping file '%s'" % format.path)

    if self.verbosity:
      print("Checking whether WC format can be converted")
    try:
      entries.assert_valid_format(format_nbr, self.verbosity)
    except LossyConversionException, e:
      # In --force mode, ignore complaints about lossy conversion.
      if self.force:
        print("WARNING: WC format conversion will be lossy. Dropping "\
              "field(s) %s " % ", ".join(e.lossy_fields))
      else:
        raise

    if self.verbosity:
      print("Writing WC format")
    entries.write_format(format_nbr)

  def change_wc_format(self, format_nbr):
    """Walk all paths in a WC tree, and change their format to
    FORMAT_NBR.  Throw LossyConversionException or NotImplementedError
    if the WC format should not be converted, or is unrecognized."""
    for dirpath, dirs, files in os.walk(self.root_path):
      self.write_dir_format(format_nbr, dirpath, dirs + files)

class Entries:
  """Represents a .svn/entries file.

  'The entries file' section in subversion/libsvn_wc/README is a
  useful reference."""

  # The name and index of each field composing an entry's record.
  entry_fields = (
    "name",
    "kind",
    "revision",
    "url",
    "repos",
    "schedule",
    "text-time",
    "checksum",
    "committed-date",
    "committed-rev",
    "last-author",
    "has-props",
    "has-prop-mods",
    "cachable-props",
    "present-props",
    "conflict-old",
    "conflict-new",
    "conflict-wrk",
    "prop-reject-file",
    "copied",
    "copyfrom-url",
    "copyfrom-rev",
    "deleted",
    "absent",
    "incomplete",
    "uuid",
    "lock-token",
    "lock-owner",
    "lock-comment",
    "lock-creation-date",
    "changelist",
    "keep-local",
    "working-size",
    "depth",
    "tree-conflicts",
    "file-external",
  )

  # The format number.
  format_nbr = -

  # How many bytes the format number takes in the file.  (The format number
  # may have leading zeroes after using this script to convert format 10 to
  # format 9 -- which would write the format number as '09'.)
  format_nbr_bytes = -

  def __init__(self, path):
    self.path = path
    self.entries = []

  def parse(self, verbosity=):
    """Parse the entries file.  Throw NotImplementedError if the WC
    format is unrecognized."""

    input = open(self.path, "r")

    # Read WC format number from INPUT.  Validate that it
    # is a supported format for conversion.
    format_line = input.readline()
    try:
      self.format_nbr = int(format_line)
      self.format_nbr_bytes = len(format_line.rstrip()) # remove '\n'
    except ValueError:
      self.format_nbr = -
      self.format_nbr_bytes = -
    if not self.format_nbr in LATEST_FORMATS.values():
      raise UnrecognizedWCFormatException(self.format_nbr, self.path)

    # Parse file into individual entries, to later inspect for
    # non-convertable data.
    entry = None
    while True:
      entry = self.parse_entry(input, verbosity)
      if entry is None:
        break
      self.entries.append(entry)

    input.close()

  def assert_valid_format(self, format_nbr, verbosity=):
    if verbosity >= :
      print("Validating format for entries file '%s'" % self.path)
    for entry in self.entries:
      if verbosity >= :
        print("Validating format for entry '%s'" % entry.get_name())
      try:
        entry.assert_valid_format(format_nbr)
      except LossyConversionException:
        if verbosity >= :
          sys.stderr.write("Offending entry:\n%s\n" % entry)
          sys.stderr.flush()
        raise

  def parse_entry(self, input, verbosity=):
    "Read an individual entry from INPUT stream."
    entry = None

    while True:
      line = input.readline()
      if line in ("", "\x0c\n"):
        # EOF or end of entry terminator encountered.
        break

      if entry is None:
        entry = Entry()

      # Retain the field value, ditching its field terminator ("\x0a").
      entry.fields.append(line[:-])

    if entry is not None and verbosity >= :
      sys.stdout.write(str(entry))
      print("-" * )
    return entry

  def write_format(self, format_nbr):
    # Overwrite all bytes of the format number (which are the first bytes in
    # the file).  Overwrite format '10' by format '09', which will be converted
    # to '9' by Subversion when it rewrites the file.  (Subversion 1.4 and later
    # ignore leading zeroes in the format number.)
    assert len(str(format_nbr)) <= self.format_nbr_bytes
    format_string = '%0' + str(self.format_nbr_bytes) + 'd'

    os.chmod(self.path, )
    output = open(self.path, "r+", )
    output.write(format_string % format_nbr)
    output.close()
    os.chmod(self.path, )

class Entry:
  "Describes an entry in a WC."

  # Maps format numbers to indices of fields within an entry's record that must
  # be retained when downgrading to that format.
  must_retain_fields = {
      # Not in 1.4: changelist, keep-local, depth, tree-conflicts, file-externals
        : (, , , , ),
      # Not in 1.5: tree-conflicts, file-externals
        : (, ),
       : (),
      # Downgrading from format 11 (1.7-dev) to format 10 is not possible,
      # because 11 does not use has-props and cachable-props (but 10 does).
      # Naively downgrading in that situation causes properties to disappear
      # from the wc.
      #
      # Downgrading from the 1.7 SQLite-based format to format 10 is not
      # implemented.
      }

  def __init__(self):
    self.fields = []

  def assert_valid_format(self, format_nbr):
    "Assure that conversion will be non-lossy by examining fields."

    # Check whether lossy conversion is being attempted.
    lossy_fields = []
    for field_index in self.must_retain_fields[format_nbr]:
      if len(self.fields) -  >= field_index and self.fields[field_index]:
        lossy_fields.append(Entries.entry_fields[field_index])
    if lossy_fields:
      raise LossyConversionException(lossy_fields,
        "Lossy WC format conversion requested for entry '%s'\n"
        "Data for the following field(s) is unsupported by older versions "
        "of\nSubversion, and is likely to be subsequently discarded, and/or "
        "have\nunexpected side-effects: %s\n\n"
        "WC format conversion was cancelled, use the --force option to "
        "override\nthe default behavior."
        % (self.get_name(), ", ".join(lossy_fields)))

  def get_name(self):
    "Return the name of this entry."
    return len(self.fields) >  and self.fields[] or ""

  def __str__(self):
    "Return all fields from this entry as a multi-line string."
    rep = ""
    for i in range(, len(self.fields)):
      rep += "[%s] %s\n" % (Entries.entry_fields[i], self.fields[i])
    return rep

class Format:
  """Represents a .svn/format file."""

  def __init__(self, path):
    self.path = path

  def write_format(self, format_nbr, verbosity=):
    format_string = '%d\n'
    if os.path.exists(self.path):
      if verbosity >= :
        print("%s will be updated." % self.path)
      os.chmod(self.path,)
    else:
      if verbosity >= :
        print("%s does not exist, creating it." % self.path)
    format = open(self.path, "w")
    format.write(format_string % format_nbr)
    format.close()
    os.chmod(self.path, )

class LocalException(Exception):
  """Root of local exception class hierarchy."""
  pass

class LossyConversionException(LocalException):
  "Exception thrown when a lossy WC format conversion is requested."
  def __init__(self, lossy_fields, str):
    self.lossy_fields = lossy_fields
    self.str = str
  def __str__(self):
    return self.str

class UnrecognizedWCFormatException(LocalException):
  def __init__(self, format, path):
    self.format = format
    self.path = path
  def __str__(self):
    return ("Unrecognized WC format %d in '%s'; "
            "only formats 8, 9, and 10 can be supported") % (self.format, self.path)


def main():
  try:
    opts, args = my_getopt(sys.argv[:], "vh?",
                           ["debug", "force", "skip-unknown-format",
                            "verbose", "help"])
  except:
    usage_and_exit("Unable to process arguments/options")

  converter = WCFormatConverter()

  # Process arguments.
  if len(args) == :
    converter.root_path = args[]
    svn_version = args[]
  else:
    usage_and_exit()

  # Process options.
  debug = False
  for opt, value in opts:
    if opt in ("--help", "-h", "-?"):
      usage_and_exit()
    elif opt == "--force":
      converter.force = True
    elif opt == "--skip-unknown-format":
      converter.error_on_unrecognized = False
    elif opt in ("--verbose", "-v"):
      converter.verbosity += 
    elif opt == "--debug":
      debug = True
    else:
      usage_and_exit("Unknown option '%s'" % opt)

  try:
    new_format_nbr = LATEST_FORMATS[svn_version]
  except KeyError:
    usage_and_exit("Unsupported version number '%s'; "
                   "only 1.4, 1.5, and 1.6 can be supported" % svn_version)

  try:
    converter.change_wc_format(new_format_nbr)
  except LocalException, e:
    if debug:
      raise
    sys.stderr.write("%s\n" % e)
    sys.stderr.flush()
    sys.exit()

  print("Converted WC at '%s' into format %d for Subversion %s" % \
        (converter.root_path, new_format_nbr, svn_version))

if __name__ == "__main__":
  main()
           

继续阅读