/usr/lib/python2.7/dist-packages/xxdiff/scm/subversion.py is in xxdiff-scripts 1:4.0.1+dfsg-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | # This file is part of the xxdiff package. See xxdiff for license and details.
"""
Functions for Subversion.
Note: we do not use the official Python bindings because we want to evolve
independently and not break when Subversion changes. This only makes sense
inasmuch as we do not do much with Subversion. Should we get do more involved
stuff with it, we should switch this code to use the Subversion Python bindings.
"""
__author__ = 'Martin Blais <blais@furius.ca>'
# stdlib imports.
import os, tempfile, re
from subprocess import Popen, call, PIPE
from os.path import abspath, isabs
# xxdiff imports.
from xxdiff.scripts import script_name, tmpprefix
def is_checked_out(filename):
"""
There is no real global mutual editing policy in Subversion. The restricted
locking mechanism that is available is not meant to be automated (on the
contrary).
"""
return True
def checkout(filename):
"""
Checkout the given filename from Subversion.
"""
return
def commit(filenames, comments=None):
"""
Commit the given filename into CVS.
"""
if comments is None:
comments = 'Committed by %s' % script_name
if isinstance(filenames, (str, unicode)):
filenames = [filenames]
else:
assert isinstance(filenames, (tuple, list))
# Note: if the comments are ever so huge, we consider using a file with the
# -f option to 'svn commit'. Wondering if there may be any important issues
# with passing large objects through the environment. This is certainly not
# the usual thing to do.
if len(comments) < 512:
call(['svn', 'commit', '--message', comments] + filenames)
else:
tmpf = tempfile.NamedTemporaryFile('w', prefix=tmpprefix)
tmpf.write(comments)
tmpf.flush()
call(['svn', 'commit', '--file', tmpf.name] + filenames)
def resolve(filename):
"""
Commit the given filename into CVS.
"""
p = Popen(['svn', 'resolved', filename], stdout=PIPE, stderr=PIPE)
stdout, stderr = p.communicate()
if p.returncode != 0:
raise SystemExit("Error: Resolving file '%s':\n%s\n" %
(filename, stderr))
class SvnStatus(object):
"""
Status result for a file in a subversion repository.
"""
def status(rootdirs):
"""
Obtains the status from all the given root directories.
A list of status objects is returned.
"""
if isinstance(rootdirs, (str, unicode)):
rootdirs = [rootdirs]
else:
assert isinstance(rootdirs, (list, tuple))
if len(rootdirs) == 0:
rootdirs.append(os.getcwd())
# Note: since we specify abspaths we should get abspaths on the output
p = Popen(['svn', 'status'] + map(abspath, rootdirs), stdout=PIPE)
stdout, stderr = p.communicate()
statii = []
for line in stdout.splitlines():
if not line or re.match('^Performing', line):
continue
status = SvnStatus()
status.parsed_line = line
# The first six columns in the output are each one character wide:
lc = iter(line)
# First column: Says if item was added, deleted, or otherwise
# changed
# ' ' no modifications
# 'A' Added
# 'C' Conflicted
# 'D' Deleted
# 'G' Merged
# 'I' Ignored
# 'M' Modified
# 'R' Replaced
# 'X' item is unversioned, but is used by an externals
# definition
# '?' item is not under version control
# '!' item is missing (removed by non-svn command) or incomplete
# '~' versioned item obstructed by some item of a different kind
status.status = lc.next()
# Second column: Modifications of a file's or directory's
# properties
# ' ' no modifications
# 'C' Conflicted
# 'M' Modified
status.modprops = lc.next()
# Third column: Whether the working copy directory is locked
# ' ' not locked
# 'L' locked
status.dirlocked = lc.next()
# Fourth column: Scheduled commit will contain
# addition-with-history
# ' ' no history scheduled with commit
# '+' history scheduled with commit
status.withhist = lc.next()
# Fifth column: Whether the item is switched relative to its
# parent
# ' ' normal
# 'S' switched
status.switched = lc.next()
# Sixth column: Repository lock token
# (without -u)
# ' ' no lock token
# 'K' lock token present
# (with -u)
# ' ' not locked in repository, no lock token
# 'K' locked in repository, lock toKen present
# 'O' locked in repository, lock token in some Other working
# copy
# 'T' locked in repository, lock token present but sTolen
# 'B' not locked in repository, lock token present but Broken
status.locktoken = lc.next()
# Seventh column: Repository lock token
# 'C' (conflict on merged files)
# '>' (conflict comments)
status.mconflict = lc.next()
if status.mconflict == '>':
continue
# There is a space and the rest is a filename:
status.filename = line[7:].strip()
assert isabs(status.filename)
statii.append(status)
# Note: we could parse this information, but this requires server access:
# -----------------------------------------------------------------------
# The out-of-date information appears in the eighth column (with -u):
# '*' a newer revision exists on the server
# ' ' the working copy is up to date
#
# Remaining fields are variable width and delimited by spaces:
# The working revision (with -u or -v)
# The last committed revision and last committed author (with -v)
# The working copy path is always the final field, so it can
# include spaces.
return statii
def getinfo(filename):
"""
Return the fields of 'svn info', in a dictionary.
"""
p = Popen(['svn', 'info', filename], stdout=PIPE, stderr=PIPE)
stdout, stderr = p.communicate()
if p.returncode != 0:
raise SystemExit("Error: Cannot get history for: %s" % stderr)
colon_split_re = re.compile(': ')
d = dict([colon_split_re.split(x, 1) for x in stdout.strip().splitlines()])
return d
def cat_revision_temp(filename, revision):
"""
Fetches a specific revision of a file and place it in a temporary file,
which is returned.
"""
# Fetch the local ancestor to a temporary file.
tmpf = tempfile.NamedTemporaryFile('w', prefix=tmpprefix)
p = Popen(['svn', 'cat', '--revision', revision, filename],
stdout=tmpf)
stdout, stderr = p.communicate()
if p.returncode != 0:
raise SystemExit("Error: Cannot fetch BASE revision: %s" % stderr)
return tmpf
def test():
"""
Simple interactive test.
"""
import optparse
parser = optparse.OptionParser(__doc__.strip())
opts, args = parser.parse_args()
for s in status(args[0]):
print s.filename, ':', s.status
if __name__ == '__main__':
test()
|