Skip to content

Instantly share code, notes, and snippets.

@eqhmcow
Created September 3, 2009 17:43
Show Gist options
  • Save eqhmcow/180418 to your computer and use it in GitHub Desktop.
Save eqhmcow/180418 to your computer and use it in GitHub Desktop.
script to track multiple svn repos in one git repo
#!/usr/bin/perl
use strict;
use warnings;
use File::Path;
# This script pulls in subversion files and history into a git repo.
# The subversion files are merged into the master branch under a specific
# subdirectory. This is useful if you have multiple svn repos that you want
# track in a single git repo, with a separate directory for each repo.
# You must have git-svn installed, of course. git 1.6 or better is highly
# recommended; this was tested on git version 1.6.3.3
# YOU MUST ALREADY HAVE A BRANCH NAMED master THAT EXISTS AND HAS HISTORY
# (at least one commit) -- running this on a brand new git repo will FAIL!
# The workaround is to commit a placeholder file in your master repo before
# running this. e.g.:
# git init
# touch .placeholder-$$
# git add .
# git commit -m 'init master repo'
# Usage:
# git-add-svn-trunk.pl <svn_repo_path> <directory>
# The given repo path must have a trunk directory. (This trunk directory does
# not have to be a top-level directory in the subversion repository.)
# Usage examples:
# simple repo with /trunk directory, merged into myproject directory
# git-add-svn-trunk.pl myproject myproject
# repo with multiple projects, each with their own trunk:
# git-add-svn-trunk.pl repo1/projects/myproject repo1/myproject
# git-add-svn-trunk.pl repo1/projects/anotherproject repo1/anotherproject
# NOTE: modify $repo_base to match your subversion repository base path
my $repo_base = 'svn+ssh://user@example.com/home/user/subversion/'; # trailing slash required
my $git_status_rc = system("git status") >> 8;
die "\nCurrent directory needs to be your git repo base directory.\nWorking copy should be on your pristine master branch" if $git_status_rc > 1;
my $repo_name = shift;
my $git_base_dir = shift;
die "\nNeed a repo name and a git base directory" unless $repo_name and $git_base_dir;
print "\nLoading svn repo $repo_name into git master subdirectory $git_base_dir\n\n";
my $repo_path = "${repo_base}${repo_name}/trunk";
$repo_name =~ s!/+!-!g;
print "Finding trunk for svn repo $repo_name ...\n";
my $trunk_revision = find_trunk_revision($repo_path);
if ($trunk_revision) {
print "$repo_path trunk found starting at revision $trunk_revision\n";
} else {
print "$repo_path trunk found\n";
}
print "\nSetting git config...\n";
git_set_svn_config($repo_name, $repo_path);
print "\nCreating temporary local git branch for svn repo...\n";
git_new_branch($repo_name);
print "\nRunning git-svn fetch...\n";
git_svn_fetch($repo_name, $trunk_revision);
print "\nRebasing temporary branch to git-svn branch...\n";
git_rebase_new_branch($repo_name);
print "\nMerging svn files into git master branch...\n";
git_merge_svn($repo_name, $git_base_dir);
print "\nRemoving temporary local git branch...\n";
`git branch -D svn-${repo_name}`;
print "\n$repo_name svn repo merged into master under $git_base_dir directory.\n\n";
print "To pull the latest subversion commits, run:\n";
print <<'EOF';
for i in `git config --get-regexp '^svn-remote.*url$'|perl -p -e 's/^svn-remote\.(.+)\.url.*$/$1/'`
do echo $i
git svn fetch -R $i
git merge -s subtree remotes/git-svn-$i
done
EOF
print "\n";
sub find_trunk_revision
{
my $repo_path = shift;
my $svn_log = `svn log -v $repo_path --stop-on-copy`;
die "svn log for $repo_path failed" unless $? >> 8 == 0;
die "Couldn't find trunk" unless $svn_log =~ s!.*\n\s+(A .*/trunk[^\n]*).*!$1!s;
# force list context for m// -- match will fail if trunk wasn't created
# via a svn cp ... in that case, we can just grab all the history with our
# fetch (below)
my ($trunk_revision) = $svn_log =~ m/^.*from.*:(\d+).*$/;
return $trunk_revision;
}
sub git_set_svn_config
{
my ($repo_name, $repo_path) = @_;
system("git config svn-remote.${repo_name}.url '${repo_path}'");
system("git config svn-remote.${repo_name}.fetch ':refs/remotes/git-svn-${repo_name}'");
}
sub git_svn_fetch
{
my ($repo_name, $revision) = @_;
if ($revision) {
system("git svn fetch -R $repo_name -r ${revision}:HEAD");
} else {
system("git svn fetch -R $repo_name");
}
}
sub git_new_branch
{
my $repo_name = shift;
# create a new branch with no history
# see http://git.or.cz/gitwiki/GitTips#Howtocreateanewbranchthathasnoancestor
`git symbolic-ref HEAD refs/heads/svn-${repo_name}`;
`rm .git/index`;
`git clean -fdx`;
# commit placeholder file
`touch .placeholder-$$`;
`git add .`;
`git commit -m 'init'`;
}
sub git_rebase_new_branch
{
my $repo_name = shift;
`git rebase remotes/git-svn-${repo_name}`;
# remove placeholder file
`git reset HEAD^`; # from the repo
`git clean -f`; # from the filesystem
}
sub git_merge_svn
{
my ($repo_name, $git_base_dir) = @_;
mkpath $git_base_dir;
`git checkout-index --prefix=${git_base_dir}/ -a`;
`git checkout master`;
`git add ${git_base_dir}`;
`git commit -m 'initial commit of $repo_name'`;
`git merge -s subtree remotes/git-svn-${repo_name}`;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment