#!/bin/bash NEWSVNURL="file:///home/colin/svn-git/soft" NEWSVNUUID=$(svn info $NEWSVNURL | grep "^Repository UUID: " | cut -d' ' -f3) SOFTWARE=$1 if [ -z $SOFTWARE ]; then echo "Missing software to convert..." exit 1 fi BASESOFTWARE=$(basename $SOFTWARE) if [ -d $BASESOFTWARE-origin ]; then echo "It seems the svn-git repository already exists ($BASESOFTWARE-origin)" exit 1 fi if [ -d $BASESOFTWARE.git ]; then echo "It seems the converted git repository already exists ($BASESOFTWARE.git)" exit 1 fi skiprevisions= skiprevisionsx="$2" if [ -n "$skiprevisionsx" ]; then for rev in $skiprevisionsx; do rev=$(echo $rev | sed 's/r//g') rev=$(( $rev + 0 )) if [ $rev -gt 0 ]; then skiprevisions="$skiprevisions $rev" fi done fi echo "Identified software name: $SOFTWARE" echo "SVN Revisions to skip: $skiprevisions" echo git svn init $NEWSVNURL/$SOFTWARE --no-metadata --stdlayout $BASESOFTWARE-origin cd $BASESOFTWARE-origin revision=0 if [ -n "$skiprevisions" ]; then for rev in $skiprevisions; do git svn fetch -A ../authors-transform.txt -r $(( $revision + 1 )):$(( $rev - 1 )) revision=$rev done fi git svn fetch -A ../authors-transform.txt -r $(( $revision + 1 )):HEAD git reset --hard trunk echo echo "done" echo echo "Now creating bare git repository" git init --bare ../$BASESOFTWARE.git git remote rm origin git remote add origin ../$BASESOFTWARE.git #git push --set-upstream master git push origin master 'refs/remotes/*:refs/heads/*' pushd ../$BASESOFTWARE.git git branch -D trunk git branch -D origin/master 2>/dev/null # Tagging logic inspired by https://github.com/nothingmuch/git-svn-abandon/blob/master/git-svn-abandon-fix-refs # Keep a map of tags and their original sha1 for the SQL database cerated below declare -A tagmap tags=$(git for-each-ref --format='%(refname)' refs/heads/tags | cut -d / -f 4) for tag in $tags; do ref="refs/heads/tags/$tag" refsha1=$(git rev-parse "$ref") reftreesha1=$(git rev-parse "$ref":) # Find the oldest ancestor for which the tree is the same parentref="$ref" while [ "$(git rev-parse --quiet --verify "$parentref"^:)" = "$reftreesha1" ]; do parentref="$parentref"^ done parent=$(git rev-parse "$parentref") # If this ancestor is in master then we can just tag it # otherwise the tag has diverged from master and it's actually more like a # branch than a tag merge=$(git merge-base master $parent) if [ "$merge" = "$parent" ]; then targetref=$parent else targetref=$refsha1 fi tagmap[$refsha1]=$tag # create an annotated tag based on the last commit in the tag, and delete the "branchy" ref for the tag git show -s --pretty='format:%s%n%n%b' "$ref" | \ env GIT_COMMITTER_NAME="$(git show -s --pretty='format:%an' "$ref")" \ GIT_COMMITTER_EMAIL="$(git show -s --pretty='format:%ae' "$ref")" \ GIT_COMMITTER_DATE="$(git show -s --pretty='format:%ad' "$ref")" \ GIT_AUTHOR_NAME="$(git show -s --pretty='format:%an' "$ref")" \ GIT_AUTHOR_EMAIL="$(git show -s --pretty='format:%ae' "$ref")" \ GIT_AUTHOR_DATE="$(git show -s --pretty='format:%ad' "$ref")" \ git tag -a -F - "$tag" "$targetref" git update-ref -d "$ref" done # Implement a branch name policy # 1. Branches matching ([1-3]) are renamed to distro/mga\1 # 2. Branches matching (20[0-9\.]+) are renamed to distro/mdv\1 # 3. Branches matching (mes[0-9].*) are renamed to distro/\1 # 4. All other branches are renamed to topic/\1 (excl. master) branches=$(git for-each-ref --format='%(refname)' refs/heads | cut -d / -f 3-) for branch in $branches; do ref="refs/heads/$branch" refsha1=$(git rev-parse "$ref") reftreesha1=$(git rev-parse "$ref":) # Find the oldest ancestor for which the tree is the same parentref="$ref" while [ "$(git rev-parse --quiet --verify "$parentref"^:)" = "$reftreesha1" ]; do parentref="$parentref"^ done parent=$(git rev-parse "$parentref") # If this ancestor is in master then we can just squash it # otherwise the branch has diverged from master and it's actually a proper # branch merge=$(git merge-base master $parent) if [ "$merge" = "$parent" ]; then git update-ref "$ref" $parent $refsha1 fi if (echo $branch | grep -qE '^[0-3]$'); then git branch -m $branch distro/mga$branch elif (echo $branch | grep -qE '^20[0-9\.]+$'); then git branch -m $branch distro/mdv$branch elif (echo $branch | grep -qE '^mes[0-9].*$'); then git branch -m $branch distro/$branch elif [ "$branch" != "master" ]; then git branch -m $branch topic/$branch fi done git gc --aggressive popd # And finally we create some SQL for creating a nice revision map database # CREATE TABLE refs (distro char(3) NOT NULL, soft varchar(255) NOT NULL, revision int(10) unsigned NOT NULL, sha1 char(40) NOT NULL, head varchar(255) NOT NULL); # Parse a git-svn revmap file into SQL # $1 = Name of revmap file # $2 = Distro ('mdv' or 'mga') # $3 = head name i.e. branch or tag name (only used when not processing trunk/master) # $4 = Special SHA1 (last sha1 to look for if 'mdv', fake sha1 commit if 'mga') parsesvnrevmap() { if [ ! -f "$1" ]; then echo "No such file '$1'" >&2 exit 1 fi for map in $(cat "$1" | xxd -c24 -g24 | cut -b 10-57); do sha1=$(echo $map | cut -b 9-) if [ "$sha1" = "0000000000000000000000000000000000000000" ]; then continue fi if [ -n "$4" -a "$2" = "mga" -a "$sha1" = "$4" ]; then continue fi rev=$(printf "%d" 0x$(echo $map | cut -b 1-8)) if [ -n "${tagmap[$sha1]}" ]; then echo "INSERT INTO refs VALUES('$2', '$SOFTWARE', $rev, '', '${tagmap[$sha1]}');" else branch=$3 if [ -n "$branch" ]; then if (echo $branch | grep -qE '^[0-3]$'); then branch=distro/mga$branch elif (echo $branch | grep -qE '^20[0-9\.]+$'); then branch=distro/mdv$branch elif (echo $branch | grep -qE '^mes[0-9].*$'); then branch=distro/$branch elif [ "$branch" != "master" ]; then branch=topic/$branch fi fi echo "INSERT INTO refs VALUES('$2', '$SOFTWARE', $rev, '$sha1', '$branch');" fi # Exit if we've reached our reset sha1 as any future revisions are not used # by Mageia if [ -n "$4" -a "$2" = "mdv" -a "$sha1" = "$4" ]; then break fi done } echo echo -n "Creating revision -> sha1 map SQL... " sql="../$BASESOFTWARE-revmap.sql" rm -f "$sql" "$sql".xz # Mageia commits revmap=".git/svn/refs/remotes/trunk/.rev_map.$NEWSVNUUID" if [ -f "$revmap" ]; then parsesvnrevmap "$revmap" "mga" "" $fakesha1 >>"$sql" fi for revmap in $(find .git/svn/refs/remotes/tags -name .rev_map.$NEWSVNUUID 2>/dev/null); do # Note, still pass in the tag name in case the tag is really more of a branch... parsesvnrevmap "$revmap" "mga" "$(echo $revmap | cut -d'/' -f6)" >>"$sql" done for revmap in $(find .git/svn/refs/remotes -maxdepth 2 -not -iwholename ".git/svn/refs/remotes/trunk/.rev_map.*" -name .rev_map.$NEWSVNUUID 2>/dev/null); do parsesvnrevmap "$revmap" "mga" "$(echo $revmap | cut -d'/' -f5)" >>"$sql" done xz "$sql" cd .. echo "done"