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

Re: Filename case

From: Martin Tomes <lists_at_tomes.org.uk>
Date: 2004-06-01 17:46:21 CEST

Branko Čibej wrote:
> Martin Tomes wrote:
>>Branko Čibej wrote:
>>>Jeff Lanzarotta wrote:
>>>>Hello,
>>>>
>>>>I have the subversion server (svnserve) setup on a linux machine and
>>>>our clients run on Windows 2000.
>>>>My question is, is there a way to protect against this? In this
>>>>scenario, the files DELETE.ICO and delete.ico are actually the same
>>>>file.
>>>
>>>What exactly would you like to protect against? I see two issues here:
>>>
>>> * Someone on a case-sensitive system checked in two files that
>>> differ only in case
>>
>>Not necessarily, if two people on Windows each add a file, one called
>>delete.ico and the other DELETE.ICO and both commit then one can end
>>up in the position described.
>
> Oh bother. You're right.
>>
>>I have a hook script on our CVS server which prevents this from
>>happening, when the second person tries to commit the new file it is
>>found to already exist with a different case and the commit is
>>rejected. My next challenge is to write a pre commit hook script
>>which does the same.
>
> In this case, I think a hook script is exactly the right way to go. Of
> course the fact that it has to support Unicode case folding isn't going
> to help a bit.

I have attached a pre commit hook which looks to see if a file already exists in the repository
whose name differs only in case. This is my first stab at this, it does work, but I would
appreciate comments as to whether it goes about this in the right way. It does rely on the perl
lc() function doing the right thing with unicode. I am afraid that I am no expert in UTF-8 and am
hoping that the :utf8 stuff on the open commands will do the job. Perhaps someone with utf-8 file
paths could try this script.

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

use strict;

my $svnlook = '/usr/local/bin/svnlook';

my $repos = $ARGV[0];
my $txn = $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 . ' --transaction ' . $txn;
open(SVNLOOK, '-|:utf8', $cmd) || die($cmd);
while (<SVNLOOK>) {
  chomp;
  if (/^A\s+(.*)/) {
    push @added, $1;
  }
}
close SVNLOOK;

# 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 . ' --transaction ' . $txn;
open(SVNLOOK, '-|:utf8', $cmd) || die($cmd);
my $shortest=999999;
my $changed;
while (<SVNLOOK>) {
  chomp;
  if (length($_) < $shortest) {
    $changed = $_;
    $shortest = length($_);
  }
}
close SVNLOOK;

# Use the history of $changed path to find the revision of the previous commit.
$cmd = $svnlook . ' history ' . $repos . ' ' . $changed;
open(SVNLOOK, '-|:utf8', $cmd) || die($cmd);
my $lastrev;
while (<SVNLOOK>) {
  chomp;
  if (/(\d+)/) {
    $lastrev = $1;
    last;
  }
}

# Get the file tree at the previous revision and turn the output into
# complete paths for each file.
my @path;
$cmd = $svnlook . ' tree ' . $repos . ' --revision ' . $lastrev;
open(SVNLOOK, '-|:utf8', $cmd) || die($cmd);
while (<SVNLOOK>) {
  chomp;
  next if (/^\/$/); # Ignore the root node.
  if (/^(\s+)(.*)\/$/) { # Is a directory.
    $#path = length($1)-2; # Number of spaces at start of line is nest level.
    push @path, $2;
  } elsif (/^\s+(.*)$/) { # This is a real file name, not a directory.
    my $name = join('/', @path) . '/' . $1;
    $tree{lc($name)} = $name; # Index the hash with case folded name.
  }
}
close SVNLOOK;

my $failmsg;
foreach my $newfile (@added) {
  if (exists($tree{lc($newfile)})) {
    $failmsg .= "\n $newfile already exists as " . $tree{lc($newfile)};
  }
}
if (defined($failmsg)) {
  die "\nFile name case conflict found:\n" . $failmsg . "\n";
}
exit 0;

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Tue Jun 1 17:50:11 2004

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