You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2016/02/27 22:37:43 UTC
svn commit: r1732669 - /subversion/trunk/tools/client-side/svn-vendor.py
Author: stilor
Date: Sat Feb 27 21:37:42 2016
New Revision: 1732669
URL: http://svn.apache.org/viewvc?rev=1732669&view=rev
Log:
Minor enhancements, mostly in treating symlinks.
* svn-vendor.py
(ConfigOpt) Allow for multiline option descriptions.
(SvnVndImport) New option, 'symlink-handling' - how symlinks
are treated in imported version. Working copy is always treated
"as-is".
(SvnVndImport.get_lists) If dereferencing symlinks, verify the
symlink target is sane.
(SvnVndImport.run_svn) Add trailing empty peg revision if the
arguments contain '@'.
(SvnVndImport.do_apply) Handle symlinks.
Modified:
subversion/trunk/tools/client-side/svn-vendor.py
Modified: subversion/trunk/tools/client-side/svn-vendor.py
URL: http://svn.apache.org/viewvc/subversion/trunk/tools/client-side/svn-vendor.py?rev=1732669&r1=1732668&r2=1732669&view=diff
==============================================================================
--- subversion/trunk/tools/client-side/svn-vendor.py (original)
+++ subversion/trunk/tools/client-side/svn-vendor.py Sat Feb 27 21:37:42 2016
@@ -200,7 +200,7 @@ class FSO(object):
'''
def __init__(self):
self.wc_path = None
- self.state = [ "-", "-" ] # '-': absent, 'F': file, 'D': dir
+ self.state = [ "-", "-" ] # '-': absent, 'F': file, 'D': dir, 'L': symlink
def status(self):
return "[%s%s]" % (self.state[S_WC], self.state[S_IM])
@@ -216,8 +216,8 @@ class FSOCollection(dict):
Collection of FSOs
'''
def print(self):
- print(" / Status in working copy (-:absent, F:file, D:dir)")
- print(" |/ Status in imported sources (-:absent, F:file, D:dir)")
+ print(" / Status in working copy (-:absent, F:file, D:dir, L:link)")
+ print(" |/ Status in imported sources (-:absent, F:file, D:dir, L:link)")
for k in sorted(self.keys(), key=filename_sort_key):
e = self[k]
print("%s %s%s" % (e.status(), shlex.quote(k),
@@ -310,7 +310,8 @@ class Config(dict):
def print(self):
for k in sorted(self):
o = self[k]
- print("# %s" % o.helpmsg)
+ for s in o.helpmsg.split('\n'):
+ print("# %s" % s)
print("%-20s: %s" % (k, str(o)))
print()
@@ -330,6 +331,11 @@ class SvnVndImport(cmd.Cmd):
self.importdir = importdir
self.svninfo = svninfo
self.config = Config()
+ self.config.add_option('symlink-handling',
+ ConfigOpt("as-is", "How symbolic links are handled;\n" +
+ " 'dereference' treats as normal files/dirs (and " +
+ "ignores dangling links);\n" +
+ " 'as-is' imports as symlinks"))
self.config.add_option('save-diff-copied',
ConfigOpt(None, "Save 'svn diff' output on the " +
"moved/copied files and directories to this " +
@@ -359,12 +365,13 @@ class SvnVndImport(cmd.Cmd):
def scan(self):
self.items = FSOCollection()
self.info(1, "Scanning working copy directory...")
- self.get_lists(self.wcdir, S_WC)
+ self.get_lists(self.wcdir, S_WC, False)
self.info(1, "Scanning imported directory...")
- self.get_lists(self.importdir, S_IM)
+ self.get_lists(self.importdir, S_IM,
+ self.config.get('symlink-handling') == "dereference")
- def get_lists(self, top, where):
- for d, dn, fn in os.walk(top, followlinks=True):
+ def get_lists(self, top, where, deref):
+ for d, dn, fn in os.walk(top, followlinks=deref):
dr = os.path.relpath(d, top)
# If under .svn directory at the top (SVN 1.7+) or has .svn
# in the path (older SVN), ignore
@@ -374,9 +381,36 @@ class SvnVndImport(cmd.Cmd):
continue
if dr != '.':
self.items.add(dr, where, "D")
- for f in fn:
+ dnn = [] # List where we'll descend
+ for f in fn + dn:
fr = os.path.normpath(os.path.join(dr, f))
- self.items.add(fr, where, "F")
+ frp = os.path.join(d, f)
+ if os.path.islink(frp):
+ if deref:
+ # Dereferencing:
+ # - check for dangling/absolute/out-of-tree symlinks and abort
+ rl = os.readlink(frp)
+ if not os.path.exists(frp):
+ self.info(1, "WARN: Ignoring dangling symlink %s -> %s" % (fr, rl))
+ continue
+ if os.path.isabs(rl):
+ self.info(1, "WARN: Ignoring absolute symlink %s -> %s" % (fr, rl))
+ continue
+ tgt = os.path.normpath(os.path.join(dr, rl))
+ if tgt == ".." or tgt.startswith(".." + os.sep):
+ self.info(1, "WARN: Ignoring out-of-wc symlink %s -> %s" % (fr, rl))
+ continue
+ else:
+ # Importing symlinks as-is, no need to check.
+ self.items.add(fr, where, "L")
+ continue
+ # If we get here, treat symlinks to files as regular files, and add directories
+ # to the list of traversed subdirs
+ if os.path.isfile(frp):
+ self.items.add(fr, where, "F")
+ if os.path.isdir(frp):
+ dnn.append(f)
+ dn[:] = dnn
def onecmd(self, str):
'Override for checking number of arguments'
@@ -403,6 +437,10 @@ class SvnVndImport(cmd.Cmd):
pos = 0
atatime = 100
output = ""
+ # svn treats '@' specially (peg revision); if there's such character in a
+ # file name - append an empty peg revision
+ args_fixed = list(map(lambda x: x + "@" if x.find("@") != -1 else x, args_fixed))
+ args_split = list(map(lambda x: x + "@" if x.find("@") != -1 else x, args_split))
while pos < len(args_split) or (pos == 0 and len(args_split) == 0):
svnargs = ['svn'] + args_fixed + args_split[pos : pos + atatime]
pos += atatime
@@ -558,6 +596,10 @@ class SvnVndImport(cmd.Cmd):
if xsrc.state[S_WC] != xdst.state[S_IM]:
# Different kinds - definitely not the same object
return 0
+ if xsrc.state[S_WC] == "L" or xdst.state[S_IM] == "L":
+ # Symlinks are not considered the same object (same target in
+ # different dirs refers to different objects).
+ return 0
if xsrc.state[S_WC] == "D":
return self.similarity_dir(src, dst, threshold, lst_removal)
else:
@@ -734,8 +776,10 @@ class SvnVndImport(cmd.Cmd):
edit and load.
'''
self.parse_args(arg, 0, "detect")
+ # Configurable for file/dirs; symlinks are never similar.
self.detect({ "D": self.config.get('dir-similarity'),
- "F": self.config.get('file-similarity')})
+ "F": self.config.get('file-similarity'),
+ "L": 1001 })
def do_apply(self, arg):
'''
@@ -782,10 +826,44 @@ class SvnVndImport(cmd.Cmd):
os.path.join(self.wcdir, i))
files_added.append(i)
flg = "(added file)"
+ elif nk_im == "L":
+ tim = os.readlink(os.path.join(self.importdir, i))
+ os.symlink(tim, os.path.join(self.wcdir, i))
+ files_added.append(i)
+ flg = "(added symlink)"
else:
# Not in imported sources, not in WC (moved
# away/removed) - nothing to do
pass
+ elif nk_wc == "L":
+ # Symbolic link in a working copy
+ if nk_im == "L":
+ # Symbolic link in both. If the same target, do nothing. Otherwise,
+ # replace.
+ twc = os.readlink(os.path.join(self.wcdir, i))
+ tim = os.readlink(os.path.join(self.importdir, i))
+ if tim != twc:
+ self.run_svn(["rm", "--force", i])
+ os.symlink(tim, os.path.join(self.wcdir, i))
+ files_added.append(i)
+ flg = "(replaced symlink)"
+ elif nk_im == "D":
+ # Was a symlink, now a directory. Replace.
+ self.run_svn(["rm", "--force", i])
+ os.mkdir(os.path.join(self.wcdir, i))
+ dirs_added.append(i)
+ flg = "(replaced symlink with dir)"
+ elif nk_im == "F":
+ # Symlink replaced with file.
+ self.run_svn(["rm", "--force", i])
+ shutil.copyfile(os.path.join(self.importdir, i),
+ os.path.join(self.wcdir, i))
+ files_added.append(i)
+ flg = "(replaced symlink with file)"
+ else:
+ # Was a symlink, removed
+ files_removed.append(i)
+ flg = "(removed symlink)"
elif nk_wc == "F":
# File in a working copy
if nk_im == "D":
@@ -799,6 +877,13 @@ class SvnVndImport(cmd.Cmd):
shutil.copyfile(os.path.join(self.importdir, i),
os.path.join(self.wcdir, i))
flg = "(copied)"
+ elif nk_im == "L":
+ # Was a file, now a symlink. Replace.
+ self.run_svn(["rm", "--force", i])
+ tim = os.readlink(os.path.join(self.importdir, i))
+ os.symlink(tim, os.path.join(self.wcdir, i))
+ files_added.append(i)
+ flg = "(replaced file with symlink)"
else:
# Was a file, removed
files_removed.append(i)
@@ -817,6 +902,13 @@ class SvnVndImport(cmd.Cmd):
os.path.join(self.wcdir, i))
files_added.append(i)
flg = "(replaced dir with file)"
+ elif nk_im == "L":
+ # Was a directory, now a symlink. Replace.
+ self.run_svn(["rm", "--force", i])
+ tim = os.readlink(os.path.join(self.importdir, i))
+ os.symlink(tim, os.path.join(self.wcdir, i))
+ files_added.append(i)
+ flg = "(replaced dir with symlink)"
else:
# Directory removed
dirs_removed.append(i)