implement gitcvs.usecrlfattr

If gitcvs.usecrlfattr is set to true, git-cvsserver will consult
the "crlf" for each file to determine if it should mark the file
as binary (-kb).

Signed-off-by: Matthew Ogilvie <mmogilvi_git@miniinfo.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Matthew Ogilvie 2008-05-14 22:35:47 -06:00 committed by Junio C Hamano
parent 044182ef82
commit 8a06a63297
4 changed files with 276 additions and 22 deletions

View File

@ -660,11 +660,21 @@ gitcvs.logfile::
Path to a log file where the CVS server interface well... logs Path to a log file where the CVS server interface well... logs
various stuff. See linkgit:git-cvsserver[1]. various stuff. See linkgit:git-cvsserver[1].
gitcvs.usecrlfattr
If true, the server will look up the `crlf` attribute for
files to determine the '-k' modes to use. If `crlf` is set,
the '-k' mode will be left blank, so cvs clients will
treat it as text. If `crlf` is explicitly unset, the file
will be set with '-kb' mode, which supresses any newline munging
the client might otherwise do. If `crlf` is not specified,
then 'gitcvs.allbinary' is used. See linkgit:gitattribute[5].
gitcvs.allbinary:: gitcvs.allbinary::
If true, all files are sent to the client in mode '-kb'. This If true, all files not otherwise specified using
causes the client to treat all files as binary files which suppresses 'gitcvs.usecrlfattr' and an explicitly set or unset `crlf`
any newline munging it otherwise might do. A work-around for the attribute are sent to the client in mode '-kb'. This
fact that there is no way yet to set single files to mode '-kb'. causes the client to treat them as binary files which
suppresses any newline munging it otherwise might do.
gitcvs.dbname:: gitcvs.dbname::
Database used by git-cvsserver to cache revision information Database used by git-cvsserver to cache revision information
@ -695,8 +705,9 @@ gitcvs.dbTableNamePrefix::
linkgit:git-cvsserver[1] for details). Any non-alphabetic linkgit:git-cvsserver[1] for details). Any non-alphabetic
characters will be replaced with underscores. characters will be replaced with underscores.
All gitcvs variables except for 'gitcvs.allbinary' can also be All gitcvs variables except for 'gitcvs.usecrlfattr' and
specified as 'gitcvs.<access_method>.<varname>' (where 'access_method' 'gitcvs.allbinary' can also be specified as
'gitcvs.<access_method>.<varname>' (where 'access_method'
is one of "ext" and "pserver") to make them apply only for the given is one of "ext" and "pserver") to make them apply only for the given
access method. access method.

View File

@ -301,11 +301,27 @@ checkout, diff, status, update, log, add, remove, commit.
Legacy monitoring operations are not supported (edit, watch and related). Legacy monitoring operations are not supported (edit, watch and related).
Exports and tagging (tags and branches) are not supported at this stage. Exports and tagging (tags and branches) are not supported at this stage.
The server should set the '-k' mode to binary when relevant, however, CRLF Line Ending Conversions
this is not really implemented yet. For now, you can force the server ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
to set '-kb' for all files by setting the `gitcvs.allbinary` config
variable. In proper GIT tradition, the contents of the files are By default the server leaves the '-k' mode blank for all files,
always respected. No keyword expansion or newline munging is supported. which causes the cvs client to treat them as a text files, subject
to crlf conversion on some platforms.
You can make the server use `crlf` attributes to set the '-k' modes
for files by setting the `gitcvs.usecrlfattr` config variable.
In this case, if `crlf` is explicitly unset ('-crlf'), then the
will set '-kb' mode, for binary files. If it `crlf` is set,
then the '-k' mode will explicitly be left blank. See
also linkgit:gitattributes[5] for more information about the `crlf`
attribute.
Alternatively, if `gitcvs.usecrlfattr` config is not enabled
or if the `crlf` attribute is unspecified for a filename, then
the server uses the `gitcvs.allbinary` for the default setting.
If `gitcvs.allbinary` is set, then the files not otherwise
specified will default to '-kb' mode. Otherwise the '-k' mode
is left blank.
Dependencies Dependencies
------------ ------------

View File

@ -502,7 +502,7 @@ sub req_add
print $state->{CVSROOT} . "/$state->{module}/$filename\n"; print $state->{CVSROOT} . "/$state->{module}/$filename\n";
# this is an "entries" line # this is an "entries" line
my $kopts = kopts_from_path($filepart); my $kopts = kopts_from_path($filename);
$log->debug("/$filepart/1.$meta->{revision}//$kopts/"); $log->debug("/$filepart/1.$meta->{revision}//$kopts/");
print "/$filepart/1.$meta->{revision}//$kopts/\n"; print "/$filepart/1.$meta->{revision}//$kopts/\n";
# permissions # permissions
@ -533,9 +533,25 @@ sub req_add
print "Checked-in $dirpart\n"; print "Checked-in $dirpart\n";
print "$filename\n"; print "$filename\n";
my $kopts = kopts_from_path($filepart); my $kopts = kopts_from_path($filename);
print "/$filepart/0//$kopts/\n"; print "/$filepart/0//$kopts/\n";
my $requestedKopts = $state->{opt}{k};
if(defined($requestedKopts))
{
$requestedKopts = "-k$requestedKopts";
}
else
{
$requestedKopts = "";
}
if( $kopts ne $requestedKopts )
{
$log->warn("Ignoring requested -k='$requestedKopts'"
. " for '$filename'; detected -k='$kopts' instead");
#TODO: Also have option to send warning to user?
}
$addcount++; $addcount++;
} }
@ -615,7 +631,7 @@ sub req_remove
print "Checked-in $dirpart\n"; print "Checked-in $dirpart\n";
print "$filename\n"; print "$filename\n";
my $kopts = kopts_from_path($filepart); my $kopts = kopts_from_path($filename);
print "/$filepart/-1.$wrev//$kopts/\n"; print "/$filepart/-1.$wrev//$kopts/\n";
$rmcount++; $rmcount++;
@ -785,6 +801,7 @@ sub req_co
argsplit("co"); argsplit("co");
my $module = $state->{args}[0]; my $module = $state->{args}[0];
$state->{module} = $module;
my $checkout_path = $module; my $checkout_path = $module;
# use the user specified directory if we're given it # use the user specified directory if we're given it
@ -862,6 +879,7 @@ sub req_co
# Don't want to check out deleted files # Don't want to check out deleted files
next if ( $git->{filehash} eq "deleted" ); next if ( $git->{filehash} eq "deleted" );
my $fullName = $git->{name};
( $git->{name}, $git->{dir} ) = filenamesplit($git->{name}); ( $git->{name}, $git->{dir} ) = filenamesplit($git->{name});
if (length($git->{dir}) && $git->{dir} ne './' if (length($git->{dir}) && $git->{dir} ne './'
@ -892,7 +910,7 @@ sub req_co
print $state->{CVSROOT} . "/$module/" . ( defined ( $git->{dir} ) and $git->{dir} ne "./" ? $git->{dir} . "/" : "" ) . "$git->{name}\n"; print $state->{CVSROOT} . "/$module/" . ( defined ( $git->{dir} ) and $git->{dir} ne "./" ? $git->{dir} . "/" : "" ) . "$git->{name}\n";
# this is an "entries" line # this is an "entries" line
my $kopts = kopts_from_path($git->{name}); my $kopts = kopts_from_path($fullName);
print "/$git->{name}/1.$git->{revision}//$kopts/\n"; print "/$git->{name}/1.$git->{revision}//$kopts/\n";
# permissions # permissions
print "u=$git->{mode},g=$git->{mode},o=$git->{mode}\n"; print "u=$git->{mode},g=$git->{mode},o=$git->{mode}\n";
@ -1101,7 +1119,7 @@ sub req_update
print $state->{CVSROOT} . "/$state->{module}/$filename\n"; print $state->{CVSROOT} . "/$state->{module}/$filename\n";
# this is an "entries" line # this is an "entries" line
my $kopts = kopts_from_path($filepart); my $kopts = kopts_from_path($filename);
$log->debug("/$filepart/1.$meta->{revision}//$kopts/"); $log->debug("/$filepart/1.$meta->{revision}//$kopts/");
print "/$filepart/1.$meta->{revision}//$kopts/\n"; print "/$filepart/1.$meta->{revision}//$kopts/\n";
@ -1149,7 +1167,7 @@ sub req_update
print "Merged $dirpart\n"; print "Merged $dirpart\n";
$log->debug($state->{CVSROOT} . "/$state->{module}/$filename"); $log->debug($state->{CVSROOT} . "/$state->{module}/$filename");
print $state->{CVSROOT} . "/$state->{module}/$filename\n"; print $state->{CVSROOT} . "/$state->{module}/$filename\n";
my $kopts = kopts_from_path($filepart); my $kopts = kopts_from_path("$dirpart/$filepart");
$log->debug("/$filepart/1.$meta->{revision}//$kopts/"); $log->debug("/$filepart/1.$meta->{revision}//$kopts/");
print "/$filepart/1.$meta->{revision}//$kopts/\n"; print "/$filepart/1.$meta->{revision}//$kopts/\n";
} }
@ -1165,7 +1183,7 @@ sub req_update
{ {
print "Merged $dirpart\n"; print "Merged $dirpart\n";
print $state->{CVSROOT} . "/$state->{module}/$filename\n"; print $state->{CVSROOT} . "/$state->{module}/$filename\n";
my $kopts = kopts_from_path($filepart); my $kopts = kopts_from_path("$dirpart/$filepart");
print "/$filepart/1.$meta->{revision}/+/$kopts/\n"; print "/$filepart/1.$meta->{revision}/+/$kopts/\n";
} }
} }
@ -1416,7 +1434,7 @@ sub req_ci
} }
print "Checked-in $dirpart\n"; print "Checked-in $dirpart\n";
print "$filename\n"; print "$filename\n";
my $kopts = kopts_from_path($filepart); my $kopts = kopts_from_path($filename);
print "/$filepart/1.$meta->{revision}//$kopts/\n"; print "/$filepart/1.$meta->{revision}//$kopts/\n";
} }
} }
@ -2296,10 +2314,24 @@ sub kopts_from_path
{ {
my ($path) = @_; my ($path) = @_;
# Once it exists, the git attributes system should be used to look up if ( defined ( $cfg->{gitcvs}{usecrlfattr} ) and
# what attributes apply to this path. $cfg->{gitcvs}{usecrlfattr} =~ /\s*(1|true|yes)\s*$/i )
{
my ($val) = check_attr( "crlf", $path );
if ( $val eq "set" )
{
return "";
}
elsif ( $val eq "unset" )
{
return "-kb"
}
else
{
$log->info("Unrecognized check_attr crlf $path : $val");
}
}
# Until then, take the setting from the config file
unless ( defined ( $cfg->{gitcvs}{allbinary} ) and $cfg->{gitcvs}{allbinary} =~ /^\s*(1|true|yes)\s*$/i ) unless ( defined ( $cfg->{gitcvs}{allbinary} ) and $cfg->{gitcvs}{allbinary} =~ /^\s*(1|true|yes)\s*$/i )
{ {
# Return "" to give no special treatment to any path # Return "" to give no special treatment to any path
@ -2311,6 +2343,23 @@ sub kopts_from_path
} }
} }
sub check_attr
{
my ($attr,$path) = @_;
ensureWorkTree();
if ( open my $fh, '-|', "git", "check-attr", $attr, "--", $path )
{
my $val = <$fh>;
close $fh;
$val =~ s/.*: ([^:\r\n]*)\s*$/$1/;
return $val;
}
else
{
return undef;
}
}
# Generate a CVS author name from Git author information, by taking # Generate a CVS author name from Git author information, by taking
# the first eight characters of the user part of the email address. # the first eight characters of the user part of the email address.
sub cvs_author sub cvs_author

178
t/t9401-git-cvsserver-crlf.sh Executable file
View File

@ -0,0 +1,178 @@
#!/bin/sh
#
# Copyright (c) 2008 Matthew Ogilvie
# Parts adapted from other tests.
#
test_description='git-cvsserver -kb modes
tests -kb mode for binary files when accessing a git
repository using cvs CLI client via git-cvsserver server'
. ./test-lib.sh
q_to_nul () {
perl -pe 'y/Q/\000/'
}
q_to_cr () {
tr Q '\015'
}
marked_as () {
foundEntry="$(grep "^/$2/" "$1/CVS/Entries")"
if [ x"$foundEntry" = x"" ] ; then
echo "NOT FOUND: $1 $2 1 $3" >> "${WORKDIR}/marked.log"
return 1
fi
test x"$(grep "^/$2/" "$1/CVS/Entries" | cut -d/ -f5)" = x"$3"
stat=$?
echo "$1 $2 $stat '$3'" >> "${WORKDIR}/marked.log"
return $stat
}
not_present() {
foundEntry="$(grep "^/$2/" "$1/CVS/Entries")"
if [ -r "$1/$2" ] ; then
echo "Error: File still exists: $1 $2" >> "${WORKDIR}/marked.log"
return 1;
fi
if [ x"$foundEntry" != x"" ] ; then
echo "Error: should not have found: $1 $2" >> "${WORKDIR}/marked.log"
return 1;
else
echo "Correctly not found: $1 $2" >> "${WORKDIR}/marked.log"
return 0;
fi
}
cvs >/dev/null 2>&1
if test $? -ne 1
then
test_expect_success 'skipping git-cvsserver tests, cvs not found' :
test_done
exit
fi
perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
test_expect_success 'skipping git-cvsserver tests, Perl SQLite interface unavailable' :
test_done
exit
}
unset GIT_DIR GIT_CONFIG
WORKDIR=$(pwd)
SERVERDIR=$(pwd)/gitcvs.git
git_config="$SERVERDIR/config"
CVSROOT=":fork:$SERVERDIR"
CVSWORK="$(pwd)/cvswork"
CVS_SERVER=git-cvsserver
export CVSROOT CVS_SERVER
rm -rf "$CVSWORK" "$SERVERDIR"
test_expect_success 'setup' '
echo "Simple text file" >textfile.c &&
echo "File with embedded NUL: Q <- there" | q_to_nul > binfile.bin &&
mkdir subdir &&
echo "Another text file" > subdir/file.h &&
echo "Another binary: Q (this time CR)" | q_to_cr > subdir/withCr.bin &&
echo "Mixed up NUL, but marked text: Q <- there" | q_to_nul > mixedUp.c
echo "Unspecified" > subdir/unspecified.other &&
echo "/*.bin -crlf" > .gitattributes &&
echo "/*.c crlf" >> .gitattributes &&
echo "subdir/*.bin -crlf" >> .gitattributes &&
echo "subdir/*.c crlf" >> .gitattributes &&
echo "subdir/file.h crlf" >> .gitattributes &&
git add .gitattributes textfile.c binfile.bin mixedUp.c subdir/* &&
git commit -q -m "First Commit" &&
git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log"
'
test_expect_success 'cvs co (default crlf)' '
GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
test x"$(grep '/-k' cvswork/CVS/Entries cvswork/subdir/CVS/Entries)" = x""
'
rm -rf cvswork
test_expect_success 'cvs co (allbinary)' '
GIT_DIR="$SERVERDIR" git config --bool gitcvs.allbinary true &&
GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
marked_as cvswork textfile.c -kb &&
marked_as cvswork binfile.bin -kb &&
marked_as cvswork .gitattributes -kb &&
marked_as cvswork mixedUp.c -kb &&
marked_as cvswork/subdir withCr.bin -kb &&
marked_as cvswork/subdir file.h -kb &&
marked_as cvswork/subdir unspecified.other -kb
'
rm -rf cvswork cvs.log
test_expect_success 'cvs co (use attributes/allbinary)' '
GIT_DIR="$SERVERDIR" git config --bool gitcvs.usecrlfattr true &&
GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
marked_as cvswork textfile.c "" &&
marked_as cvswork binfile.bin -kb &&
marked_as cvswork .gitattributes -kb &&
marked_as cvswork mixedUp.c "" &&
marked_as cvswork/subdir withCr.bin -kb &&
marked_as cvswork/subdir file.h "" &&
marked_as cvswork/subdir unspecified.other -kb
'
rm -rf cvswork
test_expect_success 'cvs co (use attributes)' '
GIT_DIR="$SERVERDIR" git config --bool gitcvs.allbinary false &&
GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
marked_as cvswork textfile.c "" &&
marked_as cvswork binfile.bin -kb &&
marked_as cvswork .gitattributes "" &&
marked_as cvswork mixedUp.c "" &&
marked_as cvswork/subdir withCr.bin -kb &&
marked_as cvswork/subdir file.h "" &&
marked_as cvswork/subdir unspecified.other ""
'
test_expect_success 'adding files' '
cd cvswork/subdir &&
echo "more text" > src.c &&
GIT_CONFIG="$git_config" cvs -Q add src.c >cvs.log 2>&1 &&
marked_as . src.c "" &&
echo "psuedo-binary" > temp.bin &&
cd .. &&
GIT_CONFIG="$git_config" cvs -Q add subdir/temp.bin >cvs.log 2>&1 &&
marked_as subdir temp.bin "-kb" &&
cd subdir &&
GIT_CONFIG="$git_config" cvs -Q ci -m "adding files" >cvs.log 2>&1 &&
marked_as . temp.bin "-kb" &&
marked_as . src.c ""
'
cd "$WORKDIR"
test_expect_success 'updating' '
git pull gitcvs.git &&
echo 'hi' > subdir/newfile.bin &&
echo 'junk' > subdir/file.h &&
echo 'hi' > subdir/newfile.c &&
echo 'hello' >> binfile.bin &&
git add subdir/newfile.bin subdir/file.h subdir/newfile.c binfile.bin &&
git commit -q -m "Add and change some files" &&
git push gitcvs.git >/dev/null &&
cd cvswork &&
GIT_CONFIG="$git_config" cvs -Q update &&
cd .. &&
marked_as cvswork textfile.c "" &&
marked_as cvswork binfile.bin -kb &&
marked_as cvswork .gitattributes "" &&
marked_as cvswork mixedUp.c "" &&
marked_as cvswork/subdir withCr.bin -kb &&
marked_as cvswork/subdir file.h "" &&
marked_as cvswork/subdir unspecified.other "" &&
marked_as cvswork/subdir newfile.bin -kb &&
marked_as cvswork/subdir newfile.c "" &&
echo "File with embedded NUL: Q <- there" | q_to_nul > tmpExpect1 &&
echo "hello" >> tmpExpect1 &&
cmp cvswork/binfile.bin tmpExpect1
'
test_done