File: check-case-insensitive.pl

package info (click to toggle)
subversion 1.4.2dfsg1-2
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 37,268 kB
  • ctags: 32,888
  • sloc: ansic: 406,472; python: 38,378; sh: 15,248; cpp: 9,604; ruby: 8,313; perl: 5,308; java: 4,576; lisp: 3,860; xml: 3,298; makefile: 856
file content (267 lines) | stat: -rwxr-xr-x 8,604 bytes parent folder | download | duplicates (2)
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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#!/usr/bin/perl -w
# ====================================================================
# Copyright (c) 2000-2004 CollabNet.  All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution.  The terms
# are also available at http://subversion.tigris.org/license-1.html.
# If newer versions of this license are posted there, you may use a
# newer version instead, at your option.
#
# This software consists of voluntary contributions made by many
# individuals.  For exact contribution history, see the revision
# history and logs, available at http://subversion.tigris.org/.
# ====================================================================

# $HeadURL: http://svn.collab.net/repos/svn/branches/1.4.x/contrib/hook-scripts/check-case-insensitive.pl $
# $LastChangedDate: 2006-02-04 00:36:42 +0000 (Sat, 04 Feb 2006) $
# $LastChangedBy: sunny256 $
# $LastChangedRevision: 18331 $

# This script is deprecated, please use check-case-insensitve.py instead.

use strict;
require 5.004; # This is when locale support was added.
# This 'use encoding' and setting the LANG environment variable has the
# desired effect of handling the comparison of extended characters and
# preventing a commit.  However, if any of the files in conflict have
# extended characters in them this is the error displayed by the client:
#
#   Commit failed (details follow):
#   svn: MERGE request failed on '/svn/play/martinto/trunk'
#   svn: General svn error from server
#
# It should list the file names which are in conflict.  But it does stop the
# commit. 
use encoding "utf8";
$ENV{'LANG'} = 'en_GB.UTF-8';

# Please check the path to svnlook is correct...
my $svnlook;
if ($^O eq 'MSWin32') {
  $svnlook = '"c:\Program Files\subversion\bin\svnlook.exe"';
} else {
  $svnlook = '/usr/local/bin/svnlook';
}

# This script can be called from a pre-commit hook on either Windows or a Unix
# like operating system.  It implements the checks required to ensure that the
# repository acts in a way which is compatible with a case preserving but
# case insensitive file system.
#
# When a file is added this script checks the file tree in the repository for
# files which would be the same name on a case insensitive file system and
# rejects the commit.
#
# On a Unix system put this script in the hooks directory and add this to the
# pre-commit script:
#
#  $REPOS/hooks/check-case-insensitive.pl "$REPOS" "$TXN" || exit 1
#
# On a windows machine add this to pre-commit.bat:
#
#  perl <path-to-script>\check-case-insensitive.pl %1 %2
#  if errorlevel 1 goto :ERROR
#  exit 0
#  :ERROR
#  echo Error found in commit 1>&2
#  exit 1
#
# You may need to change the setting of $svnlook to the path to the
# executable on your system.
#
# Turn on debug by adding up to three -debug options as the first options in
# the list.  The more -debug options the more output.  If you specify more
# than one the output goes into a file.
#
# If you have any problems with this script feel free to contact
# Martin Tomes <martin@tomes.org.uk>

# Bugfixes and some debug code added by Jeremy Bettis <jeremy@deadbeef.com>

my $openstr = '-|';
# Shift off any debug options.
my $debug = 0;
while (@ARGV and $ARGV[0] =~ /^-d(ebug)?$/) {
  $debug++;
  shift;
}

# If there is too much debug output to STDERR subversion doesn't like it, so,
# if a lot of output is expected send it to a file instead.
if ($debug > 0) {
  if ($^O eq 'MSWin32') {
    open(STDERR, ">c:/svnlog.txt")
      or die "$0: cannot open 'c:/svnlog.txt' for writing: $!\n";
  } else {
    open(STDERR, ">/tmp/svnlog.txt")
      or die "$0: cannot open '/tmp/svnlog.txt' for writing: $!\n";
  }
}

# Fetch the command line arguments.
unless (@ARGV > 1) {
  die "usage: $0 [-d [-d [-d]]] repos txn [--revision]\n";
}

my $repos = shift;
my $txn = shift;

# Jeremy Bettis <jeremy@deadbeef.com> wrote the $flag code and has this to
# say about it:
#
# The reason I did that was so that I could test the hook without actually
# doing a commit.  Whenever I had a commit that succeeded in making a bad file
# or directory, or when a commit took too long I just did a sequence of
# operations like this:
#
# svnlook youngest path
# (it tells me that HEAD is 987 or whatever)
# check-case-insensitive.pl -debug path 987 -r
# and then the check-case-insensitive.pl passes -r to svnlook instead of
# --transaction.
#
# Of course when it gets down to # Get the file tree at the previous revision,
# then it doesn't work, but most of my problems were found before that point.
my $flag = '--transaction';
$flag = shift if @ARGV;

# Each added path put here.
my @added;

# The file tree as a hash, index lower cased name, value actual name.
my %tree;

# Command being executed.
my $cmd;

print STDERR "LANG=", $ENV{'LANG'}, "\n" if ($debug and defined($ENV{'LANG'}));
# Get a list of added files.
local *SVNLOOK;
$cmd = "$svnlook changed \"$repos\" $flag $txn";
print STDERR "$cmd\n" if ($debug);
open(SVNLOOK, $openstr, $cmd)
  or die("$0: cannot open '$cmd' pipe for reading: $!\n");
while (<SVNLOOK>) {
  chomp;
  if (/^A\s+(\S.*)/) {
    push @added, $1;
  }
}
close SVNLOOK;

if ($debug) {
  print STDERR "Added " . ($#added + 1) . " items:\n";
  foreach my $itm (@added) {
    print STDERR " $itm\n";
  }
}

unless (@added) {
  print STDERR "No files added\n" if ($debug);
  # No added files so no problem.
  exit(0);
}

# Get the shortest directory name which has changed, this will be the path
# into the repository to use to get the history.
$cmd = "$svnlook dirs-changed \"$repos\" $flag $txn";
print STDERR "$cmd\n" if ($debug);
open(SVNLOOK, $openstr, $cmd)
  or die("$0: cannot open '$cmd' pipe for reading: $!\n");
my $shortest=999999;
my $changed;
while (<SVNLOOK>) {
  chomp;
  print STDERR "  ", $_, "\n" if ($debug > 2);
  if (length($_) < $shortest) {
    $changed = $_;
    $shortest = length($_);
  }
}
close SVNLOOK;
# There isn't a leading slash on $changed but there is a trailing one.  When
# it is the root of the repository the / is a pain, so always remove the
# trailing slash and put it back in where needed.
$changed =~ s/\/$//;

# Use the history of $changed path to find the revision of the previous commit.
$cmd = "$svnlook history \"$repos\" \"$changed/\"";
print STDERR "$cmd\n" if ($debug);
open(SVNLOOK, $openstr, $cmd)
  or die("$0: cannot open '$cmd' pipe for reading: $!\n");
my $lastrev;
while (<SVNLOOK>) {
  chomp;
  if (/(\d+)/) {
    $lastrev = $1;
    last;
  }
}
close SVNLOOK;

# Get the file tree at the previous revision and turn the output into
# complete paths for each file.
my @path;
$cmd = "$svnlook tree \"$repos\" \"$changed/\" --revision $lastrev";
print STDERR "$cmd\n" if ($debug);
open(SVNLOOK, $openstr, $cmd)
  or die("$0: cannot open '$cmd' pipe for reading: $!\n");
while (<SVNLOOK>) {
  chomp;
  print STDERR "tree: '", $_, "'\n" if ($debug > 2);
  next if (/^\/{1,2}$/); # Ignore the root node.  Two /'s at root of the repos.
  if (/^(\s+)(.*)\/$/) { # Is a directory.
    $#path = length($1)-2; # Number of spaces at start of line is nest level.
    push @path, $2;
    my $name = join('/', @path) . '/';
    my $index;
    if ($changed eq '') {
      $index = $name;
    } else {
      $index = $changed . '/' . $name;
    }
    $tree{lc($index)} = $name; # Index the hash with case folded name.
    print STDERR "\$tree{lc($index)}=$name (dir)\n" if ($debug > 1);
  } elsif (/^(\s+)(.*)$/) {  # This is a real file name, not a directory.
    $#path = length($1)-2; # Number of spaces at start of line is nest level.
    my $name;
    if ($#path eq -1) {
      $name = $2;
    } else {
      $name = join('/', @path) . '/' . $2;
    }
    my $index;
    if ($changed eq '') {
      $index = $name;
    } else {
      $index = $changed . '/' . $name;
    }
    $tree{lc($index)} = $name; # Index the hash with case folded name.
    print STDERR "\$tree{lc($index)}=$name\n" if ($debug > 1);
  }
}
close SVNLOOK;

my $failmsg;

my %newtree;
foreach my $newfile (@added) {
  print STDERR "Checking \$tree{lc($newfile)}\n" if ($debug > 1);
  # Without the following line it gets the lc() wrong.
  my $junk = "x$newfile";
  my $lcnewfile = lc($newfile);
  if (exists($tree{$lcnewfile})) {
    $failmsg .= "\n  $newfile already exists as " . $tree{lc($newfile)};
  }
  elsif (exists($newtree{$lcnewfile})) {
    $failmsg .= "\n  $newfile also added as " . $newtree{lc($newfile)};
  }
  $newtree{$lcnewfile} = $newfile;
}
if (defined($failmsg)) {
  print STDERR "\nFile name case conflict found:\n" . $failmsg . "\n";
  exit 1;
}

exit 0;