3
# Licensed under the same terms as Subversion.
5
# A pre-commit hook to detect case-insensitive filename clashes.
7
# What this script does:
8
# - Detects new paths that 'clash' with existing, or other new, paths.
9
# - Ignores existings paths that already 'clash'
10
# - Exits with an error code, and a diagnostic on stderr, if 'clashes'
14
# - Get a list of changed paths.
15
# - From that list extract the new paths that represent adds or replaces.
16
# - For each new path:
17
# - Split the path into a directory and a name.
18
# - Get the names of all the entries in the version of the directory
20
# - Compare the canonical new name with each canonical entry name.
21
# - If the canonical names match and the pristine names do not match
22
# then we have a 'clash'.
25
# - All the paths from the Subversion filesystem bindings are encoded
26
# in UTF-8 and the separator is '/' on all OS's.
27
# - The canonical form determines what constitutes a 'clash', at present
28
# a simple 'lower case' is used. That's probably not identical to the
29
# behaviour of Windows or OSX, but it might be good enough.
30
# - Hooks get invoked with an empty environment so this script explicitly
31
# sets a locale; make sure it is a sensible value.
32
# - If used with Apache the 'clash' diagnostic must be ASCII irrespective
33
# of the locale, see the 'Force' comment near the end of the script for
34
# one way to achieve this.
38
# On a Unix system put this script in the hooks directory and add this to
39
# the pre-commit script:
41
# $REPOS/hooks/case-insensitive.py "$REPOS" "$TXN" || exit 1
43
# On a windows machine add this to pre-commit.bat:
45
# python <path-to-script>\case-insensitive.py %1 %2
46
# if errorlevel 1 goto :ERROR
49
# echo Error found in commit 1>&2
52
# Make sure the python bindings are installed and working on Windows. The
53
# zip file can be downloaded from the Subversion site. The bindings depend
54
# on dll's shipped as part of the Subversion binaries, if the script cannot
55
# load the _fs dll it is because it cannot find the other Subversion dll's.
57
# $HeadURL: http://svn.apache.org/repos/asf/subversion/branches/1.6.x/contrib/hook-scripts/case-insensitive.py $
58
# $LastChangedRevision: 867334 $
59
# $LastChangedDate: 2007-10-17 22:04:06 +0000 (Wed, 17 Oct 2007) $
60
# $LastChangedBy: philip $
63
sys.path.append('/usr/local/subversion/lib/svn-python')
64
from svn import repos, fs
65
locale.setlocale(locale.LC_ALL, 'en_GB')
67
def canonicalize(path):
68
return path.decode('utf-8').lower().encode('utf-8')
70
def get_new_paths(txn_root):
72
for path, change in fs.paths_changed(txn_root).iteritems():
73
if (change.change_kind == fs.path_change_add
74
or change.change_kind == fs.path_change_replace):
75
new_paths.append(path)
79
slash = path.rindex('/')
82
return path[:slash], path[slash+1:]
84
def join_path(dir, name):
87
return dir + '/' + name
89
def ensure_names(path, names, txn_root):
90
if (not names.has_key(path)):
92
for name, dirent in fs.dir_entries(txn_root, path).iteritems():
93
names[path].append([canonicalize(name), name])
95
names = {} # map of: key - path, value - list of two element lists of names
96
clashes = {} # map of: key - path, value - map of: key - path, value - dummy
98
native = locale.getlocale()[1]
99
if not native: native = 'ascii'
100
repos_handle = repos.open(sys.argv[1].decode(native).encode('utf-8'))
101
fs_handle = repos.fs(repos_handle)
102
txn_handle = fs.open_txn(fs_handle, sys.argv[2].decode(native).encode('utf-8'))
103
txn_root = fs.txn_root(txn_handle)
105
new_paths = get_new_paths(txn_root)
106
for path in new_paths:
107
dir, name = split_path(path)
108
canonical = canonicalize(name)
109
ensure_names(dir, names, txn_root)
110
for name_pair in names[dir]:
111
if (name_pair[0] == canonical and name_pair[1] != name):
112
canonical_path = join_path(dir, canonical)
113
if (not clashes.has_key(canonical_path)):
114
clashes[canonical_path] = {}
115
clashes[canonical_path][join_path(dir, name)] = True
116
clashes[canonical_path][join_path(dir, name_pair[1])] = True
119
# native = 'ascii' # Force ASCII output for Apache
120
for canonical_path in clashes.iterkeys():
121
sys.stderr.write(u'Clash:'.encode(native))
122
for path in clashes[canonical_path].iterkeys():
123
sys.stderr.write(u' \''.encode(native) +
124
str(path).decode('utf-8').encode(native, 'replace') +
125
u'\''.encode(native))
126
sys.stderr.write(u'\n'.encode(native))