1 #! /bin/bash 2 3 SVN_URL="$1" 4 FOSSIL_REPO="$2" 5 WORKDIR="${TMPDIR:-/var/tmp}/svn-to-fossil.workdir-$$${RANDOM}${RANDOM}${RANDOM}" 6 7 if [ -z "${SVN_URL}" -o -z "${FOSSIL_REPO}" ]; then 8 echo 'Usage: svn-to-fossil <svn-url> <fossil-repo>' >&2 9 10 exit 1 11 fi 12 13 # Create working directory 14 mkdir -p "${WORKDIR}/svn" || exit 1 15 mkdir -p "${WORKDIR}/fossil" || exit 1 16 chmod 700 "${WORKDIR}" || exit 1 17 18 # Create a list of revisions 19 SVN_REVS="$( 20 ( 21 svn log -g "${SVN_URL}" 2>/dev/null || \ 22 svn log "${SVN_URL}" 2>/dev/null 23 ) | grep '^r[0-9][0-9]* | .*' | awk '{ print $1 }' | sed 's@^r@@' | tac 24 )" 25 26 # Determine root of repository and directory within repository for renames 27 SVN_ROOT="$(svn info "${SVN_URL}" | grep '^Repository Root:' | sed 's@^Repository Root: @@')" 28 SVN_PROJ="$(echo "${SVN_URL}" | sed "s@^${SVN_ROOT}@@")" 29 30 # Create the fossil repository 31 touch "${FOSSIL_REPO}" 32 FOSSIL_REPO="$(readlink -f "${FOSSIL_REPO}")" 33 rm -f "${FOSSIL_REPO}" 34 35 # For each revision, sync with fossil 36 for rev in ${SVN_REVS}; do 37 ( 38 echo "Processing r${rev}..." 39 svnrevlog="$(svn log -r"${rev}" "${SVN_URL}")" 40 41 42 commituser="$(echo "${svnrevlog}" | head -n 2 | tail -n 1 | cut -f 2 -d '|' | sed 's@ @@g')" 43 committime="$(echo "${svnrevlog}" | head -n 2 | tail -n 1 | cut -f 3 -d '|' | sed 's@^ *@@;s@(.*$@@;s@ *$@@')" 44 committime_sec="$(date -d "${committime}" +%s)" 45 committime_ps="$(echo "puts [clock format \"[expr {$committime_sec - 1}]\" -format {%Y-%m-%d %H:%M:%S}]" | tclsh)" 46 committime="$(echo "puts [clock format \"$committime_sec\" -format {%Y-%m-%d %H:%M:%S}]" | tclsh)" 47 48 # Create the fossil repo if needed 49 if [ ! -f "${FOSSIL_REPO}" ]; then 50 fossil new "${FOSSIL_REPO}" -date-override "$committime_ps" 51 52 ( 53 cd "${WORKDIR}/svn" || exit 1 54 fossil open "${FOSSIL_REPO}" 55 ) 56 fi 57 58 if [ -d "${WORKDIR}/svn" ]; then 59 svnrevlog_v="$(svn log -v -r"${rev}" "${SVN_URL}")" 60 61 # Attempt to perform renames 62 echo "${svnrevlog_v}" | grep '^ A .* (from ' | sed 's@^ A @@;s@ (from @|@;s@:[0-9]*)$@@' | while read pathnames; do 63 src_pathname_raw="$(echo "${pathnames}" | cut -f 2 -d '|')" 64 src_pathname="$(echo "${pathnames}" | cut -f 2 -d '|' | sed "s@^${SVN_PROJ}/@@")" 65 dst_pathname="$(echo "${pathnames}" | cut -f 1 -d '|' | sed "s@^${SVN_PROJ}/@@")" 66 67 if echo "${src_pathname}" | grep '^/' >/dev/null; then 68 src_pathname="" 69 fi 70 71 if echo "${dst_pathname}" | grep '^/' >/dev/null; then 72 dst_pathname="" 73 fi 74 75 if [ -n "${src_pathname}" -a -n "${dst_pathname}" ]; then 76 cd "${WORKDIR}/svn" || exit 1 77 78 if echo "${svnrevlog_v}" | grep "^ D ${src_pathname_raw} *$" >/dev/null 2>/dev/null; then 79 # Copy + Delete = Rename 80 echo "Handling move \"${src_pathname}\" -> \"${dst_pathname}\"" 81 82 fossil mv "${src_pathname}" "${dst_pathname}" 83 else 84 # Simple copy... 85 echo "Handling copy \"${src_pathname}\" -> \"${dst_pathname}\"" 86 87 cp "${src_pathname}" "${dst_pathname}" 88 89 sleep 2 90 fossil add "${dst_pathname}" 91 fossil ci -date-override "${committime_ps}" -m "Subversion to Fossil Copy Commit. Please Ignore. Recording copying ${src_pathname} to ${dst_pathname}." "${dst_pathname}" 92 fi 93 fi 94 done 95 fi 96 97 # Preserve fossil repo information 98 mv "${WORKDIR}/svn"/_FOSSIL_ "${WORKDIR}/fossil/" 99 100 ## These files are apparently optional now... 101 for save_file in "${WORKDIR}/svn"/manifest "${WORKDIR}/svn"/manifest.uuid; do 102 if [ -f "${save_file}" ]; then 103 mv "${save_file}" "${WORKDIR}/fossil/" 104 fi 105 done 106 107 # Cleanup working directory 108 rm -rf "${WORKDIR}/svn" 109 mkdir -p "${WORKDIR}/svn" 110 cd "${WORKDIR}/svn" || exit 1 111 112 # Restore fossil repo information 113 mv "${WORKDIR}/fossil"/* "${WORKDIR}/svn/" 114 115 # Check out the appropriate version 116 ## We can't use an update, since it will break on renames 117 svn checkout --ignore-externals -r"${rev}" "${SVN_URL}" . >/dev/null 2>/dev/null || exit 1 118 119 # Remove things that should not go in the repository 120 find . -type d -name .svn | xargs rm -rf 121 122 # Add new files to fossil 123 fossil extras --dotfiles | while read filename; do 124 fossil add "${filename}" 125 done 126 127 # Delete files from fossil 128 fossil status | grep '^MISSING' | sed 's@^MISSING@@' | while read filename; do 129 fossil rm "${filename}" 130 done 131 132 # Find commit message 133 commit_msg="$(echo "${svnrevlog}" | tail -n +4 | head -n -1)" 134 if [ -z "${commit_msg}" ]; then 135 commit_msg="(no message specified)" 136 fi 137 138 # Commit 139 fossil commit -date-override "${committime}" -user-override "${commituser}" -m "${commit_msg}" || ( 140 if fossil commit -date-override "${committime}" -user-override "${commituser}"-m "${commit_msg}" 2>&1 | grep 'nothing has changed'; then 141 exit 0 142 fi 143 144 exit 1 145 ) || exit 1 146 ) || exit 1 147 done 148 149 # Find ignore glob (end result is the only one that matters since fossil doesn't revision control the settings) 150 ( 151 cd "${WORKDIR}/svn" || exit 1 152 ignores="$(find . -type d | grep -v '/\.svn/' | while read dirname; do 153 if svn proplist "${dirname}" 2>/dev/null | grep 'svn:ignore' >/dev/null; then 154 svn propget svn:ignore "${dirname}" 2>/dev/null | grep -v '^ *$' | sed "s@^@${dirname}/@;"'s@^\./@@' 155 fi 156 done | sed 'p;s@$@/*@' | sort -u | tr "\n" ',' | sed 's@,$@@')" 157 fossil settings ignore-glob "${ignores}" 158 ) 159 160 # Cleanup 161 ( 162 cd "${WORKDIR}/svn" || exit 1 163 164 fossil close 165 ) 166 rm -rf "${WORKDIR}" 167 168 exit 0 svn-to-fossil.sh is a script to convert a (portion of) a Subversion repository to a Fossil repository. |