Created
September 3, 2009 17:43
-
-
Save eqhmcow/180418 to your computer and use it in GitHub Desktop.
script to track multiple svn repos in one git repo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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