解决svn客户端版本冲突的python代码

本文提供了一段Python代码,该代码旨在帮助解决Subversion(SVN)客户端遇到的版本冲突问题。通过运行此脚本,用户可以在Linux环境中更有效地管理SVN版本冲突。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以下代码存到文件里,文件另存为xxxx.py

#!/usr/bin/env python 
# 
#windows的svn版本和虚拟机的svn版本不同,且用其中的高版本进行提交后,造成低版本无法执行svn命令  
# change-svn-wc-format.py: Change the format of a Subversion working copy. 
# 
#命令:    ./change-svn-format.py ./   "1.4" --verbose       1.4是版本号 
# ==================================================================== 
#    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" : 8, 
                   "1.5" : 9, 
                   "1.6" : 10, 
                   # 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[0]) 
  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 1 or 0) 
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 = 0
  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 = -1
  # 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 = -1
  def __init__(self, path): 
    self.path = path 
    self.entries = [] 
  def parse(self, verbosity=0): 
    """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 = -1
      self.format_nbr_bytes = -1
    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=0): 
    if verbosity >= 2: 
      print("Validating format for entries file '%s'" % self.path) 
    for entry in self.entries: 
      if verbosity >= 3: 
        print("Validating format for entry '%s'" % entry.get_name()) 
      try: 
        entry.assert_valid_format(format_nbr) 
      except LossyConversionException: 
        if verbosity >= 3: 
          sys.stderr.write("Offending entry:\n%s\n" % entry) 
          sys.stderr.flush() 
        raise
  def parse_entry(self, input, verbosity=0): 
    "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[:-1]) 
    if entry is not None and verbosity >= 3: 
      sys.stdout.write(str(entry)) 
      print("-" * 76) 
    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, 0600) 
    output = open(self.path, "r+", 0) 
    output.write(format_string % format_nbr) 
    output.close() 
    os.chmod(self.path, 0400) 
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 
      8  : (30, 31, 33, 34, 35), 
      # Not in 1.5: tree-conflicts, file-externals 
      9  : (34, 35), 
      10 : (), 
      # 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) - 1 >= 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) > 0 and self.fields[0] or "" 
  def __str__(self): 
    "Return all fields from this entry as a multi-line string."
    rep = "" 
    for i in range(0, 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=0): 
    format_string = '%d\n'
    if os.path.exists(self.path): 
      if verbosity >= 1: 
        print("%s will be updated." % self.path) 
      os.chmod(self.path,0600) 
    else: 
      if verbosity >= 1: 
        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, 0400) 
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[1:], "vh?", 
                           ["debug", "force", "skip-unknown-format", 
                            "verbose", "help"]) 
  except: 
    usage_and_exit("Unable to process arguments/options") 
  converter = WCFormatConverter() 
  # Process arguments. 
  if len(args) == 2: 
    converter.root_path = args[0] 
    svn_version = args[1] 
  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 += 1
    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(1) 
  print("Converted WC at '%s' into format %d for Subversion %s" % \ 
        (converter.root_path, new_format_nbr, svn_version)) 
if __name__ == "__main__": 
  main()


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值