[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: pre-commit hook that stops CaSe-InSenSiTive collisions

From: Martin Tomes <lists_at_tomes.org>
Date: 2004-06-08 18:28:36 CEST

kfogel@collab.net wrote:
> Time for version control, sounds like.
>
> When you guys get the "latest canonical version" of this script sorted
> out, can you post it here & clearly label it as such?

This is the version of this script which needs to be put in contrib. The confusion is mainly my
fault as I upgraded my Linux box and set up sendmail to masquerade domains too enthusiastically and
all my posts were not coming from the address I subscribed to the list with so they all ended up in
the moderators queue.

Pre-commit hook that stops case insensitive collisions.

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 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 if there is a match.

    * tools/hook-scripts/check-case-insensitive.pl

-- 
Martin Tomes
echo 'Martin x Tomes at controls x eurotherm x co x uk'\
  | sed -e 's/ x /\./g' -e 's/ at /@/'

#!/usr/bin/perl

# ====================================================================
# Copyright (c) 2000-2004 Martin Tomes. 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/.
# ====================================================================

# 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 compatable 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>

use strict;

my $opSys = $^O;

my $svnlook;
my $openstr;

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

# Shift off any debug options.
my $debug = 0;
while ($ARGV[0] eq '-debug') {
  $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 > 1) {
  if ($opSys eq 'MSWin32') {
    open(STDERR, ">c:/svnlog.txt");
  } else {
    open(STDERR, ">/tmp/svnlog.txt");
  }
}

# Fetch the command line arguments.
my $repos = $ARGV[0];
my $txn = $ARGV[1];
# 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 succeded 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-insensitiv.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 = $ARGV[2] if ($#ARGV > 1);

my @added; # Each added path put here.
my %tree; # The file tree as a hash, index lower cased name, value actual name.
my $cmd; # Command being executed.

# Get a list of added files.
local *SVNLOOK;
$cmd = $svnlook . ' changed "' . $repos . '" ' . $flag . ' ' . $txn;
print STDERR "$cmd\n" if ($debug);
open(SVNLOOK, $openstr, $cmd) || die($cmd);
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";
  }
}

if ($#added < 0) {
  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) || die($cmd);
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) || die($cmd);
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) || die($cmd);
while (<SVNLOOK>) {
  chomp;
  print STDERR "tree: '", $_, "'\n" if ($debug > 2);
  next if (/^\/{1,2}$/); # Ignore the root node. Two /'s at root of the repo.
  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;
foreach my $newfile (@added) {
  print STDERR "Checking \$tree{lc($newfile)}\n" if ($debug > 1);
  if (exists($tree{lc($newfile)})) {
    $failmsg .= "\n $newfile already exists as " . $tree{lc($newfile)};
  }
}
if (defined($failmsg)) {
  print STDERR "\nFile name case conflict found:\n" . $failmsg . "\n";
  exit 1;
}
exit 0;

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@subversion.tigris.org
For additional commands, e-mail: users-help@subversion.tigris.org
Received on Tue Jun 8 18:30:26 2004

This is an archived mail posted to the Subversion Users mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.