the working copy are ignored; useful on broken filesystems like FAT.
See gitlink:git-update-index[1]. True by default.
+core.autocrlf::
+ If true, makes git convert `CRLF` at the end of lines in text files to
+ `LF` when reading from the filesystem, and convert in reverse when
+ writing to the filesystem. The variable can be set to
+ 'input', in which case the conversion happens only while
+ reading from the filesystem but files are written out with
+ `LF` at the end of lines. Currently, which paths to consider
+ "text" (i.e. be subjected to the autocrlf mechanism) is
+ decided purely based on the contents.
+
core.symlinks::
If false, symbolic links are checked out as small plain files that
contain the link text. gitlink:git-update-index[1] and
The default is 15 days. See gitlink:git-rerere[1].
gitcvs.enabled::
- Whether the cvs pserver interface is enabled for this repository.
+ Whether the cvs server interface is enabled for this repository.
See gitlink:git-cvsserver[1].
gitcvs.logfile::
- Path to a log file where the cvs pserver interface well... logs
+ Path to a log file where the cvs server interface well... logs
various stuff. See gitlink:git-cvsserver[1].
+gitcvs.allbinary::
+ If true, all files are sent to the client in mode '-kb'. This
+ causes the client to treat all files as binary files which suppresses
+ any newline munging it otherwise might do. A work-around for the
+ fact that there is no way yet to set single files to mode '-kb'.
++
+ gitcvs.dbname::
+ Database used by git-cvsserver to cache revision information
+ derived from the git repository. The exact meaning depends on the
+ used database driver, for SQLite (which is the default driver) this
+ is a filename. Supports variable substitution (see
+ gitlink:git-cvsserver[1] for details). May not contain semicolons (`;`).
+ Default: '%Ggitcvs.%m.sqlite'
+
+ gitcvs.dbdriver::
+ Used Perl DBI driver. You can specify any available driver
+ for this here, but it might not work. git-cvsserver is tested
+ with 'DBD::SQLite', reported to work with 'DBD::Pg', and
+ reported *not* to work with 'DBD::mysql'. Experimental feature.
+ May not contain double colons (`:`). Default: 'SQLite'.
See gitlink:git-cvsserver[1].
+ gitcvs.dbuser, gitcvs.dbpass::
+ Database user and password. Only useful if setting 'gitcvs.dbdriver',
+ since SQLite has no concept of database users and/or passwords.
+ 'gitcvs.dbuser' supports variable substitution (see
+ gitlink:git-cvsserver[1] for details).
+
+ All gitcvs variables except for 'gitcvs.allbinary' can also specifed
+ as 'gitcvs.<access_method>.<varname>' (where 'access_method' is one
+ of "ext" and "pserver") to make them apply only for the given access
+ method.
+
http.sslVerify::
Whether to verify the SSL certificate when fetching or pushing
over HTTPS. Can be overridden by the 'GIT_SSL_NO_VERIFY' environment
http.noEPSV::
A boolean which disables using of EPSV ftp command by curl.
- This can helpful with some "poor" ftp servers which doesn't
+ This can helpful with some "poor" ftp servers which don't
support EPSV mode. Can be overridden by the 'GIT_CURL_FTP_NO_EPSV'
environment variable. Default is false (curl will use EPSV).
CVS clients cannot tag, branch or perform GIT merges.
+ git-cvsserver maps GIT branches to CVS modules. This is very different
+ from what most CVS users would expect since in CVS modules usually represent
+ one or more directories.
+
INSTALLATION
------------
------
Note: you need to ensure each user that is going to invoke git-cvsserver has
- write access to the log file and to the git repository. When offering anon
- access via pserver, this means that the nobody user should have write access
- to at least the sqlite database at the root of the repository.
+ write access to the log file and to the database (see
+ <<dbbackend,Database Backend>>. If you want to offer write access over
+ SSH, the users of course also need write access to the git repository itself.
+
+ [[configaccessmethod]]
+ All configuration variables can also be overriden for a specific method of
+ access. Valid method names are "ext" (for SSH access) and "pserver". The
+ following example configuration would disable pserver access while still
+ allowing access over SSH.
+ ------
+ [gitcvs]
+ enabled=0
+
+ [gitcvs "ext"]
+ enabled=1
+ ------
--
3. On the client machine you need to set the following variables.
CVSROOT should be set as per normal, but the directory should point at the
cvs co -d project-master master
------
+ [[dbbackend]]
+ Database Backend
+ ----------------
+
+ git-cvsserver uses one database per git head (i.e. CVS module) to
+ store information about the repository for faster access. The
+ database doesn't contain any persitent data and can be completly
+ regenerated from the git repository at any time. The database
+ needs to be updated (i.e. written to) after every commit.
+
+ If the commit is done directly by using git (as opposed to
+ using git-cvsserver) the update will need to happen on the
+ next repository access by git-cvsserver, independent of
+ access method and requested operation.
+
+ That means that even if you offer only read access (e.g. by using
+ the pserver method), git-cvsserver should have write access to
+ the database to work reliably (otherwise you need to make sure
+ that the database if up-to-date all the time git-cvsserver is run).
+
+ By default it uses SQLite databases in the git directory, named
+ `gitcvs.<module_name>.sqlite`. Note that the SQLite backend creates
+ temporary files in the same directory as the database file on
+ write so it might not be enough to grant the users using
+ git-cvsserver write access to the database file without granting
+ them write access to the directory, too.
+
+ You can configure the database backend with the following
+ configuration variables:
+
+ Configuring database backend
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ git-cvsserver uses the Perl DBI module. Please also read
+ its documentation if changing these variables, especially
+ about `DBI->connect()`.
+
+ gitcvs.dbname::
+ Database name. The exact meaning depends on the
+ used database driver, for SQLite this is a filename.
+ Supports variable substitution (see below). May
+ not contain semicolons (`;`).
+ Default: '%Ggitcvs.%m.sqlite'
+
+ gitcvs.dbdriver::
+ Used DBI driver. You can specify any available driver
+ for this here, but it might not work. cvsserver is tested
+ with 'DBD::SQLite', reported to work with
+ 'DBD::Pg', and reported *not* to work with 'DBD::mysql'.
+ Please regard this as an experimental feature. May not
+ contain double colons (`:`).
+ Default: 'SQLite'
+
+ gitcvs.dbuser::
+ Database user. Only useful if setting `dbdriver`, since
+ SQLite has no concept of database users. Supports variable
+ substitution (see below).
+
+ gitcvs.dbpass::
+ Database password. Only useful if setting `dbdriver`, since
+ SQLite has no concept of database passwords.
+
+ All variables can also be set per access method, see <<configaccessmethod,above>>.
+
+ Variable substitution
+ ^^^^^^^^^^^^^^^^^^^^^
+ In `dbdriver` and `dbuser` you can use the following variables:
+
+ %G::
+ git directory name
+ %g::
+ git directory name, where all characters except for
+ alpha-numeric ones, `.`, and `-` are replaced with
+ `_` (this should make it easier to use the directory
+ name in a filename if wanted)
+ %m::
+ CVS module/git head name
+ %a::
+ access method (one of "ext" or "pserver")
+ %u::
+ Name of the user running git-cvsserver.
+ If no name can be determined, the
+ numeric uid is used.
+
Eclipse CVS Client Notes
------------------------
Protocol notes: If you are using anonymous access via pserver, just select that.
Those using SSH access should choose the 'ext' protocol, and configure 'ext'
access on the Preferences->Team->CVS->ExtConnection pane. Set CVS_SERVER to
-'git-cvsserver'. Not that password support is not good when using 'ext',
+'git-cvsserver'. Note that password support is not good when using 'ext',
you will definitely want to have SSH keys setup.
Alternatively, you can just use the non-standard extssh protocol that Eclipse
offer. In that case CVS_SERVER is ignored, and you will have to replace
-the cvs utility on the server with git-cvsserver or manipulate your .bashrc
+the cvs utility on the server with git-cvsserver or manipulate your `.bashrc`
so that calling 'cvs' effectively calls git-cvsserver.
Clients known to work
Legacy monitoring operations are not supported (edit, watch and related).
Exports and tagging (tags and branches) are not supported at this stage.
-The server will set the -k mode to binary when relevant. In proper GIT
-tradition, the contents of the files are always respected.
-No keyword expansion or newline munging is supported.
+The server should set the '-k' mode to binary when relevant, however,
+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
+always respected. No keyword expansion or newline munging is supported.
Dependencies
------------
# if we are called with a pserver argument,
# deal with the authentication cat before entering the
# main loop
+ $state->{method} = 'ext';
if (@ARGV && $ARGV[0] eq 'pserver') {
+ $state->{method} = 'pserver';
my $line = <STDIN>; chomp $line;
unless( $line eq 'BEGIN AUTH REQUEST') {
die "E Do not understand $line - expecting BEGIN AUTH REQUEST\n";
}
foreach my $line ( @gitvars )
{
- next unless ( $line =~ /^(.*?)\.(.*?)=(.*)$/ );
- $cfg->{$1}{$2} = $3;
+ next unless ( $line =~ /^(.*?)\.(.*?)(?:\.(.*?))?=(.*)$/ );
+ unless ($3) {
+ $cfg->{$1}{$2} = $4;
+ } else {
+ $cfg->{$1}{$2}{$3} = $4;
+ }
}
- unless ( defined ( $cfg->{gitcvs}{enabled} ) and $cfg->{gitcvs}{enabled} =~ /^\s*(1|true|yes)\s*$/i )
+ unless ( ($cfg->{gitcvs}{$state->{method}}{enabled}
+ and $cfg->{gitcvs}{$state->{method}}{enabled} =~ /^\s*(1|true|yes)\s*$/i)
+ or ($cfg->{gitcvs}{enabled}
+ and $cfg->{gitcvs}{enabled} =~ /^\s*(1|true|yes)\s*$/i) )
{
print "E GITCVS emulation needs to be enabled on this repo\n";
print "E the repo config file needs a [gitcvs] section added, and the parameter 'enabled' set to 1\n";
return 0;
}
- if ( defined ( $cfg->{gitcvs}{logfile} ) )
+ my $logfile = $cfg->{gitcvs}{$state->{method}}{logfile} || $cfg->{gitcvs}{logfile};
+ if ( $logfile )
{
- $log->setfile($cfg->{gitcvs}{logfile});
+ $log->setfile($logfile);
} else {
$log->nofile();
}
argsplit("add");
+ my $updater = GITCVS::updater->new($state->{CVSROOT}, $state->{module}, $log);
+ $updater->update();
+
+ argsfromdir($updater);
+
my $addcount = 0;
foreach my $filename ( @{$state->{args}} )
{
$filename = filecleanup($filename);
+ my $meta = $updater->getmeta($filename);
+ my $wrev = revparse($filename);
+
+ if ($wrev && $meta && ($wrev < 0))
+ {
+ # previously removed file, add back
+ $log->info("added file $filename was previously removed, send 1.$meta->{revision}");
+
+ print "MT +updated\n";
+ print "MT text U \n";
+ print "MT fname $filename\n";
+ print "MT newline\n";
+ print "MT -updated\n";
+
+ unless ( $state->{globaloptions}{-n} )
+ {
+ my ( $filepart, $dirpart ) = filenamesplit($filename,1);
+
+ print "Created $dirpart\n";
+ print $state->{CVSROOT} . "/$state->{module}/$filename\n";
+
+ # this is an "entries" line
+ my $kopts = kopts_from_path($filepart);
+ $log->debug("/$filepart/1.$meta->{revision}//$kopts/");
+ print "/$filepart/1.$meta->{revision}//$kopts/\n";
+ # permissions
+ $log->debug("SEND : u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}");
+ print "u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}\n";
+ # transmit file
+ transmitfile($meta->{filehash});
+ }
+
+ next;
+ }
+
unless ( defined ( $state->{entries}{$filename}{modified_filename} ) )
{
print "E cvs add: nothing known about `$filename'\n";
if ( defined ( $wrev )
and defined($meta->{revision})
and $wrev == $meta->{revision}
+ and defined($state->{entries}{$filename}{modified_hash})
and not exists ( $state->{opt}{C} ) )
{
$log->info("Tell the client the file is modified");
$log->info("req_ci : " . ( defined($data) ? $data : "[NULL]" ));
- if ( @ARGV && $ARGV[0] eq 'pserver')
+ if ( $state->{method} eq 'pserver')
{
print "error 1 pserver access cannot commit\n";
exit;
bless $self, $class;
- $self->{dbdir} = $config . "/";
- die "Database dir '$self->{dbdir}' isn't a directory" unless ( defined($self->{dbdir}) and -d $self->{dbdir} );
-
$self->{module} = $module;
- $self->{file} = $self->{dbdir} . "/gitcvs.$module.sqlite";
-
$self->{git_path} = $config . "/";
$self->{log} = $log;
die "Git repo '$self->{git_path}' doesn't exist" unless ( -d $self->{git_path} );
- $self->{dbh} = DBI->connect("dbi:SQLite:dbname=" . $self->{file},"","");
+ $self->{dbdriver} = $cfg->{gitcvs}{$state->{method}}{dbdriver} ||
+ $cfg->{gitcvs}{dbdriver} || "SQLite";
+ $self->{dbname} = $cfg->{gitcvs}{$state->{method}}{dbname} ||
+ $cfg->{gitcvs}{dbname} || "%Ggitcvs.%m.sqlite";
+ $self->{dbuser} = $cfg->{gitcvs}{$state->{method}}{dbuser} ||
+ $cfg->{gitcvs}{dbuser} || "";
+ $self->{dbpass} = $cfg->{gitcvs}{$state->{method}}{dbpass} ||
+ $cfg->{gitcvs}{dbpass} || "";
+ my %mapping = ( m => $module,
+ a => $state->{method},
+ u => getlogin || getpwuid($<) || $<,
+ G => $self->{git_path},
+ g => mangle_dirname($self->{git_path}),
+ );
+ $self->{dbname} =~ s/%([mauGg])/$mapping{$1}/eg;
+ $self->{dbuser} =~ s/%([mauGg])/$mapping{$1}/eg;
+
+ die "Invalid char ':' in dbdriver" if $self->{dbdriver} =~ /:/;
+ die "Invalid char ';' in dbname" if $self->{dbname} =~ /;/;
+ $self->{dbh} = DBI->connect("dbi:$self->{dbdriver}:dbname=$self->{dbname}",
+ $self->{dbuser},
+ $self->{dbpass});
+ die "Error connecting to database\n" unless defined $self->{dbh};
$self->{tables} = {};
- foreach my $table ( $self->{dbh}->tables )
+ foreach my $table ( keys %{$self->{dbh}->table_info(undef,undef,undef,'TABLE')->fetchall_hashref('TABLE_NAME')} )
{
- $table =~ s/^"//;
- $table =~ s/"$//;
$self->{tables}{$table} = 1;
}
return wantarray ? @output : join('',@output);
}
+ =head2 mangle_dirname
+
+ create a string from a directory name that is suitable to use as
+ part of a filename, mainly by converting all chars except \w.- to _
+
+ =cut
+ sub mangle_dirname {
+ my $dirname = shift;
+ return unless defined $dirname;
+
+ $dirname =~ s/[^\w.-]/_/g;
+
+ return $dirname;
+ }
1;