The Git daemon supports a feature called Interpolated Path. For example, my Git daemon is called like this:
git-daemon --user=git --group=git \
--listen=0.0.0.0 --reuseaddr \
--interpolated-path=/var/git/repositories/%IP%D \
/var/git/repositories
What it does is it translates the request for a git repository before
actually searching for it. When I do a git clone
git://git.plenz.com/configs.git
what actually happens is that the Git
daemon will deliver the repository at
/var/git/repository/176.9.247.89/configs.git
. That is especially
nice in environments where you share one git daemon for several
people/projects and have sufficient IP addresses.
However, Gitolite doesn't seem to have a config option for this. Now what I did was patch Gitolite so that it'll prepend the repository name with the IP of the network interface the call came through.
This information is available from the SSH_CONNECTION
environment variable.
(The patch to gl-compile-conf
is needed so that Gitolite will create a
projects.list
per virtual host. That way, you can use different
configuration files for each CGit instance, according to your VHost.)
diff --git a/src/gl-auth-command b/src/gl-auth-command
index 61b2f5a..a8d1976 100755
--- a/src/gl-auth-command
+++ b/src/gl-auth-command
@@ -124,6 +124,12 @@ unless ( $verb and ( $verb eq 'git-init' or $verb =~ $R_COMMANDS or $verb =~ $W_
exit 0;
}
+# prepend host's ip address
+# SSH_CONNECTION looks like this: 92.225.139.246 41714 176.9.34.52 22
+# from-ip port to-ip port
+my $connected_via = (split " " => $ENV{"SSH_CONNECTION"})[2] // "";
+$repo = $connected_via . "/" . $repo;
+
# some final sanity checks
die "$repo ends with a slash; I don't like that\n" if $repo =~ /\/$/;
die "$repo has two consecutive periods; I don't like that\n" if $repo =~ /\.\./;
diff --git a/src/gl-compile-conf b/src/gl-compile-conf
index 2d4110f..6f8f0d7 100755
--- a/src/gl-compile-conf
+++ b/src/gl-compile-conf
@@ -577,6 +577,22 @@ unless ($GL_NO_DAEMON_NO_GITWEB) {
}
close $projlist_fh;
rename "$PROJECTS_LIST.$$", $PROJECTS_LIST;
+
+ # vhost stuff
+ my %vhost = ();
+ for my $proj (sort keys %projlist) {
+ my ($ip, $repo) = split '/' => $proj => 2;
+ $vhost{$ip} //= [];
+ push @{$vhost{$ip}} => $repo;
+ }
+ for my $v (keys %vhost) {
+ my $v_file = "$REPO_BASE/$v/projects.list";
+ my $v_fh = wrap_open( ">", $v_file . ".$$");
+ print $v_fh $_ . "\n" for @{$vhost{$v}};
+ close $v_fh;
+ rename $v_file . ".$$" => $v_file;
+ }
}
# ----------------------------------------------------------------------------
With this patch applied, I can git push git.plenz.com:config.git
and it will end up pushing to 176.9.247.89/configs.git
, fully transparent
to the user.
N.B.: This strictly is a "works for me" solution. It's not very clean – but I don't plan on properly implementing a config setting. As I said, it works for me. ;-)
Update 2014-09-10: I upgraded to Gitolite v3.6, and it’s different there.
There is a functionality called “triggers” now, which you can activate in the
.gitolite.rc
like such: Uncomment the key LOCAL_CODE => "$ENV{HOME}/local",
and insert these two trigger actions:
INPUT => [ 'VHost::input', ],
POST_COMPILE => [ 'VHost::post_compile', ],
Then, place this code under ~/local/lib/Gitolite/Triggers/VHost.pm
(you’ll
need to create that directory first):
package Gitolite::Triggers::VHost;
use strict;
use warnings;
use File::Slurp qw(read_file write_file);
use Gitolite::Rc;
sub input {
return unless $ENV{SSH_ORIGINAL_COMMAND};
return unless $ENV{SSH_CONNECTION};
my $dstip = (split " " => $ENV{"SSH_CONNECTION"})[2] // "";
return unless $dstip;
return if $dstip eq "127.0.0.1";
my $git_commands = "git-upload-pack|git-receive-pack|git-upload-archive";
if($ENV{SSH_ORIGINAL_COMMAND} =~ m/(?<cmd>$git_commands) '\/?(?<repo>\S+)'$/) {
$ENV{SSH_ORIGINAL_COMMAND} = "$+{cmd} '$dstip/$+{repo}'";
}
}
sub post_compile {
my %vhost = ();
my @projlist = read_file("$ENV{HOME}/projects.list");
for my $proj (sort @projlist) {
my ($ip, $repo) = split '/' => $proj => 2;
$vhost{$ip} //= [];
push @{$vhost{$ip}} => $repo;
}
for my $v (keys %vhost) {
write_file("$rc{GL_REPO_BASE}/$v/projects.list",
{ atomic => 1 }, @{$vhost{$v}});
}
}
1;