#!/bin/sh # # $Id: svn_cvsinject 452 2004-05-25 07:47:58Z sam $ # svn_cvsinject: reinject SVN commits to a CVS repository # see http://sam.zoy.org/writings/programming/svn2cvs.html for more details # # (c) 2004 Sam Hocevar # # This program is free software; you can redistribute it and/or # modify it under the terms of the Do What The Fuck You Want To # Public License as published by Banlu Kemiyatorn. See # http://sam.zoy.org/projects/COPYING.WTFPL for more details. # # # usage: # svn_cvsinject [options] # # or in your hooks/post-commit: # svn_cvsinject -r "$2" "$1" "/var/lib/cvs/videolan/vlc" # CVSREPO="" SVNREPO="" SVNREV="" ALIASES="" DEBUG="no" while test $# -gt 0; do opt="$1" shift case "$opt" in -h|--help) echo "usage: $0 [options] " echo "options are:" echo " -r reinject SVN revision (default: latest)" echo " -a / alias SVN branch to CVS branch " echo " -d, --debug debug mode" echo " -h, --help display help message and quit" exit 0 ;; -d|--debug) DEBUG="yes" ;; -r) SVNREV="$1" shift ;; -a) SUBST="`echo "$1" | sed -ne 's,^\([^/]*\)/\([^/]*\)$,\\\\<\1\\\\>/\2,p'`" if test -z "$SUBST"; then echo "$0 error: bad substitution ($1)" exit 1 fi ALIASES="${ALIASES} ; s/$SUBST/g" shift ;; *) if test -z "$SVNREPO"; then SVNREPO="$opt" elif test -z "$CVSREPO"; then CVSREPO="$opt" else echo "$0 error: too many arguments" echo "Try \`$0 --help' for more information." exit 1 fi ;; esac done # If no repositories were specified, abort if test -z "$CVSREPO"; then echo "$0 error: too few arguments" echo "Try \`$0 --help' for more information." exit 1 fi # If no revision was specified, use the latest commit LASTSVNREV="`svn ls -v "file://$SVNREPO" 2>/dev/null | awk '{ print $1 }' | sort -n | tail -n 1`" if test -z "$LASTSVNREV"; then echo "$0 error: repository file://$SVNREPO not found" fi if test -z "$SVNREV"; then SVNREV="$LASTSVNREV" fi if test "$DEBUG" = "yes"; then set -x fi TMPDIR="/tmp/svn_cvsinject-`date +%Y%m%d-%H%M%S`-$$" COMMITLOG="${TMPDIR}/commit.log" FILELOG="${TMPDIR}/file.log" SVNDIFF="${TMPDIR}/patch.diff" CVSROOT="`echo "${CVSREPO}" | sed 's,/[^/]*$,,'`" CVSMODULE="`echo "${CVSREPO}" | sed 's,.*/,,'`" # # Create our working directory # mkdir -p "${TMPDIR}" cd "${TMPDIR}" # Get a commit log and extract the filenames # FIXME: using --xml would make this less error-prone svn log -v "file://${SVNREPO}" -r "${SVNREV}" | sed -e '/^--*$/d' > "${COMMITLOG}" sed -ne '1,/^Changed/d' -e '/^$/q' -e 's/ (from.*)$//' -e 'p' < "${COMMITLOG}" > "${FILELOG}" # CVS uses $LOGNAME LOGNAME="`awk '{ print $3 }' "${COMMITLOG}" | sed -ne 1p`" for BRANCH in `sed -ne 's,[^/]*/branches/\([^/]*\).*,\1,p' "${FILELOG}" | sort | uniq ; grep -q '[^/]*/trunk\>' "${FILELOG}" && echo trunk`; do # Convert the SVN branch name to the CVS branch name case "${BRANCH}" in trunk) CVSBRANCH=HEAD; SVNBRANCH=trunk ;; *) CVSBRANCH="`echo "${BRANCH}" | sed "$ALIASES"`" SVNBRANCH="branches/${BRANCH}" ;; esac # Get the SVN diff, skip this version if failed svn diff "file://${SVNREPO}/${SVNBRANCH}" -r "$((${SVNREV} - 1)):${SVNREV}" > "${SVNDIFF}-${CVSBRANCH}" || continue # Get the subset of modified files from the CVS repository sed -ne 's,^--- \(.*\) *(revision.*,\1,p' "${SVNDIFF}-${CVSBRANCH}" | while read entry; do cvs -d "${CVSROOT}" checkout -l `test "${CVSBRANCH}" = "HEAD" || echo -r "${CVSBRANCH}"` "${CVSMODULE}/$entry" || true done # Also get the removed stuff sed -ne 's,^ D /'"${SVNBRANCH}"'/,,p' "${FILELOG}" | while read entry; do cvs -d "${CVSROOT}" checkout -l `test "${CVSBRANCH}" = "HEAD" || echo -r "${CVSBRANCH}"` "${CVSMODULE}/$entry" || true if test -d "${CVSMODULE}/$entry"; then (cd "${CVSMODULE}/$entry" && cvs update -d) fi done # If no directory was created (which may happen if only new directories and # files were checked in), we just retrieve the parent directory if ! test -d "${CVSMODULE}"; then cvs -d "${CVSROOT}" checkout -l `test "${CVSBRANCH}" = "HEAD" || echo -r "${CVSBRANCH}"` "${CVSMODULE}" fi # Patch the repository (if there is anything to patch) cd "${CVSMODULE}" if grep -q '^+++' "${SVNDIFF}-${CVSBRANCH}"; then patch -p0 < "${SVNDIFF}-${CVSBRANCH}" fi # Add all new directories and files in branch sed -ne 's,^ A /'"${SVNBRANCH}"'/,,p' "${FILELOG}" | while read entry; do if test -d "$entry"; then # if $entry is a directory copied from somewhere else, the SVN # changeset may be incomplete, we need to add all subdirectories # and files manually. find "$entry" -type d -a '!' -name CVS -exec cvs add '{}' ';' sed -ne 's,^+++ '"$entry/"'\(.*\) *(revision.*,\1,p' "${SVNDIFF}-${CVSBRANCH}" | while read file; do cvs add "$entry/$file" done else # if $entry is a file, just add it. cvs add "$entry" fi done # Get rid of removed files in branch sed -ne 's,^ D /'"${SVNBRANCH}"'/,,p' "${FILELOG}" | while read entry; do if test -d "$entry"; then # if $entry is a directory, the SVN changeset will be incomplete, # we need to remove all subfiles manually. find "$entry" -type d -a '!' -name CVS | while read dir; do find "$dir" -type f -maxdepth 1 | while read file; do rm -f "$file" cvs delete "$file" done done else # if $entry is a file, just remove it. Also, it might be a previously # removed directory, hence the || true. rm -f "$entry" || true cvs delete "$entry" || true fi done # Commit our changes using the same commit message cvs commit -m "`cat "${COMMITLOG}"`" # Clean up our mess cd .. rm -Rf "${CVSMODULE}" done # Clean up the remaining mess cd /tmp rm -Rf "${TMPDIR}"