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>
Matthew Ogilvie committed
May 14, 2008 at 22:35 UTC
8a06a632976ead891115ad9ac9dea7b99c52158e
4 files changed
+276
-22
Documentation/config.txt
+17
-6
@@ -660,11 +660,21 @@ gitcvs.logfile::
660
Path to a log file where the CVS server interface well... logs
661
various stuff. See linkgit:git-cvsserver[1].
662
663
+gitcvs.usecrlfattr
664
+ If true, the server will look up the `crlf` attribute for
665
+ files to determine the '-k' modes to use. If `crlf` is set,
666
+ the '-k' mode will be left blank, so cvs clients will
667
+ treat it as text. If `crlf` is explicitly unset, the file
668
+ will be set with '-kb' mode, which supresses any newline munging
669
+ the client might otherwise do. If `crlf` is not specified,
670
+ then 'gitcvs.allbinary' is used. See linkgit:gitattribute[5].
671
+
672
gitcvs.allbinary::
664
- If true, all files are sent to the client in mode '-kb'. This
665
- causes the client to treat all files as binary files which suppresses
666
- any newline munging it otherwise might do. A work-around for the
667
- fact that there is no way yet to set single files to mode '-kb'.
673
+ If true, all files not otherwise specified using
674
+ 'gitcvs.usecrlfattr' and an explicitly set or unset `crlf`
675
+ attribute are sent to the client in mode '-kb'. This
676
+ causes the client to treat them as binary files which
677
+ suppresses any newline munging it otherwise might do.
678
679
gitcvs.dbname::
680
Database used by git-cvsserver to cache revision information
@@ -695,8 +705,9 @@ gitcvs.dbTableNamePrefix::
705
linkgit:git-cvsserver[1] for details). Any non-alphabetic
706
characters will be replaced with underscores.
707
698
-All gitcvs variables except for 'gitcvs.allbinary' can also be
699
-specified as 'gitcvs.<access_method>.<varname>' (where 'access_method'
708
+All gitcvs variables except for 'gitcvs.usecrlfattr' and
709
+'gitcvs.allbinary' can also be specified as
710
+'gitcvs.<access_method>.<varname>' (where 'access_method'
711
is one of "ext" and "pserver") to make them apply only for the given
712
access method.
713
Documentation/git-cvsserver.txt
+21
-5
@@ -301,11 +301,27 @@ checkout, diff, status, update, log, add, remove, commit.
301
Legacy monitoring operations are not supported (edit, watch and related).
302
Exports and tagging (tags and branches) are not supported at this stage.
303
304
-The server should set the '-k' mode to binary when relevant, however,
305
-this is not really implemented yet. For now, you can force the server
306
-to set '-kb' for all files by setting the `gitcvs.allbinary` config
307
-variable. In proper GIT tradition, the contents of the files are
308
-always respected. No keyword expansion or newline munging is supported.
304
+CRLF Line Ending Conversions
305
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
306
+
307
+By default the server leaves the '-k' mode blank for all files,
308
+which causes the cvs client to treat them as a text files, subject
309
+to crlf conversion on some platforms.
310
+
311
+You can make the server use `crlf` attributes to set the '-k' modes
312
+for files by setting the `gitcvs.usecrlfattr` config variable.
313
+In this case, if `crlf` is explicitly unset ('-crlf'), then the
314
+will set '-kb' mode, for binary files. If it `crlf` is set,
315
+then the '-k' mode will explicitly be left blank. See
316
+also linkgit:gitattributes[5] for more information about the `crlf`
317
+attribute.
318
+
319
+Alternatively, if `gitcvs.usecrlfattr` config is not enabled
320
+or if the `crlf` attribute is unspecified for a filename, then
321
+the server uses the `gitcvs.allbinary` for the default setting.
322
+If `gitcvs.allbinary` is set, then the files not otherwise
323
+specified will default to '-kb' mode. Otherwise the '-k' mode
324
+is left blank.
325
326
Dependencies
327
------------
git-cvsserver.perl
+60
-11
@@ -502,7 +502,7 @@ sub req_add
502
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
503
504
# this is an "entries" line
505
- my $kopts = kopts_from_path($filepart);
505
+ my $kopts = kopts_from_path($filename);
506
$log->debug("/$filepart/1.$meta->{revision}//$kopts/");
507
print "/$filepart/1.$meta->{revision}//$kopts/\n";
508
# permissions
@@ -533,9 +533,25 @@ sub req_add
533
534
print "Checked-in $dirpart\n";
535
print "$filename\n";
536
- my $kopts = kopts_from_path($filepart);
536
+ my $kopts = kopts_from_path($filename);
537
print "/$filepart/0//$kopts/\n";
538
539
+ my $requestedKopts = $state->{opt}{k};
540
+ if(defined($requestedKopts))
541
+ {
542
+ $requestedKopts = "-k$requestedKopts";
543
+ }
544
+ else
545
+ {
546
+ $requestedKopts = "";
547
+ }
548
+ if( $kopts ne $requestedKopts )
549
+ {
550
+ $log->warn("Ignoring requested -k='$requestedKopts'"
551
+ . " for '$filename'; detected -k='$kopts' instead");
552
+ #TODO: Also have option to send warning to user?
553
+ }
554
+
555
$addcount++;
556
}
557
@@ -615,7 +631,7 @@ sub req_remove
631
632
print "Checked-in $dirpart\n";
633
print "$filename\n";
618
- my $kopts = kopts_from_path($filepart);
634
+ my $kopts = kopts_from_path($filename);
635
print "/$filepart/-1.$wrev//$kopts/\n";
636
637
$rmcount++;
@@ -785,6 +801,7 @@ sub req_co
801
argsplit("co");
802
803
my $module = $state->{args}[0];
804
+ $state->{module} = $module;
805
my $checkout_path = $module;
806
807
# use the user specified directory if we're given it
@@ -862,6 +879,7 @@ sub req_co
879
# Don't want to check out deleted files
880
next if ( $git->{filehash} eq "deleted" );
881
882
+ my $fullName = $git->{name};
883
( $git->{name}, $git->{dir} ) = filenamesplit($git->{name});
884
885
if (length($git->{dir}) && $git->{dir} ne './'
@@ -892,7 +910,7 @@ sub req_co
910
print $state->{CVSROOT} . "/$module/" . ( defined ( $git->{dir} ) and $git->{dir} ne "./" ? $git->{dir} . "/" : "" ) . "$git->{name}\n";
911
912
# this is an "entries" line
895
- my $kopts = kopts_from_path($git->{name});
913
+ my $kopts = kopts_from_path($fullName);
914
print "/$git->{name}/1.$git->{revision}//$kopts/\n";
915
# permissions
916
print "u=$git->{mode},g=$git->{mode},o=$git->{mode}\n";
@@ -1101,7 +1119,7 @@ sub req_update
1119
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
1120
1121
# this is an "entries" line
1104
- my $kopts = kopts_from_path($filepart);
1122
+ my $kopts = kopts_from_path($filename);
1123
$log->debug("/$filepart/1.$meta->{revision}//$kopts/");
1124
print "/$filepart/1.$meta->{revision}//$kopts/\n";
1125
@@ -1149,7 +1167,7 @@ sub req_update
1167
print "Merged $dirpart\n";
1168
$log->debug($state->{CVSROOT} . "/$state->{module}/$filename");
1169
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
1152
- my $kopts = kopts_from_path($filepart);
1170
+ my $kopts = kopts_from_path("$dirpart/$filepart");
1171
$log->debug("/$filepart/1.$meta->{revision}//$kopts/");
1172
print "/$filepart/1.$meta->{revision}//$kopts/\n";
1173
}
@@ -1165,7 +1183,7 @@ sub req_update
1183
{
1184
print "Merged $dirpart\n";
1185
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
1168
- my $kopts = kopts_from_path($filepart);
1186
+ my $kopts = kopts_from_path("$dirpart/$filepart");
1187
print "/$filepart/1.$meta->{revision}/+/$kopts/\n";
1188
}
1189
}
@@ -1416,7 +1434,7 @@ sub req_ci
1434
}
1435
print "Checked-in $dirpart\n";
1436
print "$filename\n";
1419
- my $kopts = kopts_from_path($filepart);
1437
+ my $kopts = kopts_from_path($filename);
1438
print "/$filepart/1.$meta->{revision}//$kopts/\n";
1439
}
1440
}
@@ -2296,10 +2314,24 @@ sub kopts_from_path
2314
{
2315
my ($path) = @_;
2316
2299
- # Once it exists, the git attributes system should be used to look up
2300
- # what attributes apply to this path.
2317
+ if ( defined ( $cfg->{gitcvs}{usecrlfattr} ) and
2318
+ $cfg->{gitcvs}{usecrlfattr} =~ /\s*(1|true|yes)\s*$/i )
2319
+ {
2320
+ my ($val) = check_attr( "crlf", $path );
2321
+ if ( $val eq "set" )
2322
+ {
2323
+ return "";
2324
+ }
2325
+ elsif ( $val eq "unset" )
2326
+ {
2327
+ return "-kb"
2328
+ }
2329
+ else
2330
+ {
2331
+ $log->info("Unrecognized check_attr crlf $path : $val");
2332
+ }
2333
+ }
2334
2302
- # Until then, take the setting from the config file
2335
unless ( defined ( $cfg->{gitcvs}{allbinary} ) and $cfg->{gitcvs}{allbinary} =~ /^\s*(1|true|yes)\s*$/i )
2336
{
2337
# Return "" to give no special treatment to any path
@@ -2311,6 +2343,23 @@ sub kopts_from_path
2343
}
2344
}
2345
2346
+sub check_attr
2347
+{
2348
+ my ($attr,$path) = @_;
2349
+ ensureWorkTree();
2350
+ if ( open my $fh, '-|', "git", "check-attr", $attr, "--", $path )
2351
+ {
2352
+ my $val = <$fh>;
2353
+ close $fh;
2354
+ $val =~ s/.*: ([^:\r\n]*)\s*$/$1/;
2355
+ return $val;
2356
+ }
2357
+ else
2358
+ {
2359
+ return undef;
2360
+ }
2361
+}
2362
+
2363
# Generate a CVS author name from Git author information, by taking
2364
# the first eight characters of the user part of the email address.
2365
sub cvs_author
t/t9401-git-cvsserver-crlf.sh
new
+178
@@ -0,0 +1,178 @@
1
+#!/bin/sh
2
+#
3
+# Copyright (c) 2008 Matthew Ogilvie
4
+# Parts adapted from other tests.
5
+#
6
+
7
+test_description='git-cvsserver -kb modes
8
+
9
+tests -kb mode for binary files when accessing a git
10
+repository using cvs CLI client via git-cvsserver server'
11
+
12
+. ./test-lib.sh
13
+
14
+q_to_nul () {
15
+ perl -pe 'y/Q/\000/'
16
+}
17
+
18
+q_to_cr () {
19
+ tr Q '\015'
20
+}
21
+
22
+marked_as () {
23
+ foundEntry="$(grep "^/$2/" "$1/CVS/Entries")"
24
+ if [ x"$foundEntry" = x"" ] ; then
25
+ echo "NOT FOUND: $1 $2 1 $3" >> "${WORKDIR}/marked.log"
26
+ return 1
27
+ fi
28
+ test x"$(grep "^/$2/" "$1/CVS/Entries" | cut -d/ -f5)" = x"$3"
29
+ stat=$?
30
+ echo "$1 $2 $stat '$3'" >> "${WORKDIR}/marked.log"
31
+ return $stat
32
+}
33
+
34
+not_present() {
35
+ foundEntry="$(grep "^/$2/" "$1/CVS/Entries")"
36
+ if [ -r "$1/$2" ] ; then
37
+ echo "Error: File still exists: $1 $2" >> "${WORKDIR}/marked.log"
38
+ return 1;
39
+ fi
40
+ if [ x"$foundEntry" != x"" ] ; then
41
+ echo "Error: should not have found: $1 $2" >> "${WORKDIR}/marked.log"
42
+ return 1;
43
+ else
44
+ echo "Correctly not found: $1 $2" >> "${WORKDIR}/marked.log"
45
+ return 0;
46
+ fi
47
+}
48
+
49
+cvs >/dev/null 2>&1
50
+if test $? -ne 1
51
+then
52
+ test_expect_success 'skipping git-cvsserver tests, cvs not found' :
53
+ test_done
54
+ exit
55
+fi
56
+perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
57
+ test_expect_success 'skipping git-cvsserver tests, Perl SQLite interface unavailable' :
58
+ test_done
59
+ exit
60
+}
61
+
62
+unset GIT_DIR GIT_CONFIG
63
+WORKDIR=$(pwd)
64
+SERVERDIR=$(pwd)/gitcvs.git
65
+git_config="$SERVERDIR/config"
66
+CVSROOT=":fork:$SERVERDIR"
67
+CVSWORK="$(pwd)/cvswork"
68
+CVS_SERVER=git-cvsserver
69
+export CVSROOT CVS_SERVER
70
+
71
+rm -rf "$CVSWORK" "$SERVERDIR"
72
+test_expect_success 'setup' '
73
+ echo "Simple text file" >textfile.c &&
74
+ echo "File with embedded NUL: Q <- there" | q_to_nul > binfile.bin &&
75
+ mkdir subdir &&
76
+ echo "Another text file" > subdir/file.h &&
77
+ echo "Another binary: Q (this time CR)" | q_to_cr > subdir/withCr.bin &&
78
+ echo "Mixed up NUL, but marked text: Q <- there" | q_to_nul > mixedUp.c
79
+ echo "Unspecified" > subdir/unspecified.other &&
80
+ echo "/*.bin -crlf" > .gitattributes &&
81
+ echo "/*.c crlf" >> .gitattributes &&
82
+ echo "subdir/*.bin -crlf" >> .gitattributes &&
83
+ echo "subdir/*.c crlf" >> .gitattributes &&
84
+ echo "subdir/file.h crlf" >> .gitattributes &&
85
+ git add .gitattributes textfile.c binfile.bin mixedUp.c subdir/* &&
86
+ git commit -q -m "First Commit" &&
87
+ git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
88
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
89
+ GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log"
90
+'
91
+
92
+test_expect_success 'cvs co (default crlf)' '
93
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
94
+ test x"$(grep '/-k' cvswork/CVS/Entries cvswork/subdir/CVS/Entries)" = x""
95
+'
96
+
97
+rm -rf cvswork
98
+test_expect_success 'cvs co (allbinary)' '
99
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.allbinary true &&
100
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
101
+ marked_as cvswork textfile.c -kb &&
102
+ marked_as cvswork binfile.bin -kb &&
103
+ marked_as cvswork .gitattributes -kb &&
104
+ marked_as cvswork mixedUp.c -kb &&
105
+ marked_as cvswork/subdir withCr.bin -kb &&
106
+ marked_as cvswork/subdir file.h -kb &&
107
+ marked_as cvswork/subdir unspecified.other -kb
108
+'
109
+
110
+rm -rf cvswork cvs.log
111
+test_expect_success 'cvs co (use attributes/allbinary)' '
112
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.usecrlfattr true &&
113
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
114
+ marked_as cvswork textfile.c "" &&
115
+ marked_as cvswork binfile.bin -kb &&
116
+ marked_as cvswork .gitattributes -kb &&
117
+ marked_as cvswork mixedUp.c "" &&
118
+ marked_as cvswork/subdir withCr.bin -kb &&
119
+ marked_as cvswork/subdir file.h "" &&
120
+ marked_as cvswork/subdir unspecified.other -kb
121
+'
122
+
123
+rm -rf cvswork
124
+test_expect_success 'cvs co (use attributes)' '
125
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.allbinary false &&
126
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
127
+ marked_as cvswork textfile.c "" &&
128
+ marked_as cvswork binfile.bin -kb &&
129
+ marked_as cvswork .gitattributes "" &&
130
+ marked_as cvswork mixedUp.c "" &&
131
+ marked_as cvswork/subdir withCr.bin -kb &&
132
+ marked_as cvswork/subdir file.h "" &&
133
+ marked_as cvswork/subdir unspecified.other ""
134
+'
135
+
136
+test_expect_success 'adding files' '
137
+ cd cvswork/subdir &&
138
+ echo "more text" > src.c &&
139
+ GIT_CONFIG="$git_config" cvs -Q add src.c >cvs.log 2>&1 &&
140
+ marked_as . src.c "" &&
141
+ echo "psuedo-binary" > temp.bin &&
142
+ cd .. &&
143
+ GIT_CONFIG="$git_config" cvs -Q add subdir/temp.bin >cvs.log 2>&1 &&
144
+ marked_as subdir temp.bin "-kb" &&
145
+ cd subdir &&
146
+ GIT_CONFIG="$git_config" cvs -Q ci -m "adding files" >cvs.log 2>&1 &&
147
+ marked_as . temp.bin "-kb" &&
148
+ marked_as . src.c ""
149
+'
150
+
151
+cd "$WORKDIR"
152
+test_expect_success 'updating' '
153
+ git pull gitcvs.git &&
154
+ echo 'hi' > subdir/newfile.bin &&
155
+ echo 'junk' > subdir/file.h &&
156
+ echo 'hi' > subdir/newfile.c &&
157
+ echo 'hello' >> binfile.bin &&
158
+ git add subdir/newfile.bin subdir/file.h subdir/newfile.c binfile.bin &&
159
+ git commit -q -m "Add and change some files" &&
160
+ git push gitcvs.git >/dev/null &&
161
+ cd cvswork &&
162
+ GIT_CONFIG="$git_config" cvs -Q update &&
163
+ cd .. &&
164
+ marked_as cvswork textfile.c "" &&
165
+ marked_as cvswork binfile.bin -kb &&
166
+ marked_as cvswork .gitattributes "" &&
167
+ marked_as cvswork mixedUp.c "" &&
168
+ marked_as cvswork/subdir withCr.bin -kb &&
169
+ marked_as cvswork/subdir file.h "" &&
170
+ marked_as cvswork/subdir unspecified.other "" &&
171
+ marked_as cvswork/subdir newfile.bin -kb &&
172
+ marked_as cvswork/subdir newfile.c "" &&
173
+ echo "File with embedded NUL: Q <- there" | q_to_nul > tmpExpect1 &&
174
+ echo "hello" >> tmpExpect1 &&
175
+ cmp cvswork/binfile.bin tmpExpect1
176
+'
177
+
178
+test_done