blob: 393c982994e1bf9942895befa77188b226d1e123 [file] [log] [blame]
#!/bin/bash
set -e
# stg-cvs - helper script to manage a mixed cvs/stgit working copy.
# Allows quick synchronization of a cvs mirror branch (does not try to
# reconstruct patchsets, creates "jumbo" commits), and commits stgit
# patches to CVS.
# Copyright (c) 2007 Yann Dirson <ydirson@altern.org>
# Subject to the GNU GPL, version 2.
# NOTES
# - you want to add a "CVS" line to .git/info/exclude
# - you may want to add a ".git" line to the top .cvsignore
# BRANCH INIT
# - ensure the cvs wc is clean (eg. with "cvsco")
# $ git init
# $ echo CVS >> .git/info/exclude
# $ git add .
# $ git commit -m "Initial import."
# $ git branch -m master cvs
# $ stg branch -c master cvs
# $ git config branch.master.stgit.parentbranch cvs (0.12.1 and earlier only)
# $ git config branch.cvs.description "CVS $(cat CVS/Root) $(cat CVS/Repository) $(cat CVS/Tag 2>/dev/null | echo HEAD)"
# $ git config branch.master.description "Changes for $(cat CVS/Repository) $(cat CVS/Tag 2>/dev/null | echo HEAD)"
# LIMITATIONS
# - this is only a proof-of-concept prototype
# - lacks an "init" command (see above)
# - "commit" does not ensure the base is uptodate before trying to
# commit (but hey, it's CVS ;): better "stg-cvs pull" first
# - "commit" can only commit a single patch
# - not much robustness here
# - still no support for files removed in cvs (should catch "no
# longer in the repository" message)
# - this only deals with CVS but could surely be extended to any other
# VCS
# - lacks synchronisation of .cvsignore <-> .gitignore
# - no support for filenames with spaces (stg lacks --zero output format)
# - git commit is too chatty when it finds nothing to commit
# - lacks a "quick cvs commit" feature
# DESIGN FLAWS
# - while fetching, if a file change was not git update-index'd when
# cvs-update'd (eg. because of a stg-cvs bug), it is not seen on further
# fetches until it changes again, since we scan "cvs update" output.
# This yields possible inconsistencies with CVS.
# - similarly, any conflict while cvs-updating (whether due to illegal
# changes to the cvs-mirror-branch, or due to files added to cvs but
# already-existing in working copy, or to directory moves inside the
# cvs repository, or <fill here>) has to be dealt with by hand (although
# the situation is better here: cvs sees the conflict on subsequent tries)
# - bad/no support for cvsutils:
# - stg push/pop operations confuse cvsu because of timestamp changes
# - cvspurge/cvsco would nuke .git => does not make it easy to ensure
# synchronisation
# - should use a separate workspace for cvs branch like tailor does
# - confused by cvs keyword substitution
usage()
{
[ "$#" = 0 ] || echo "ERROR: $*"
echo "Usage: $(basename $0) <command>"
echo " commands: $(do_commands)"
exit 1
}
do_commands()
{
echo $(grep '^[a-z-]*)' $0 | cut -d')' -f1)
}
do_fetch()
{
local return=0
local path
local parent="$1"
local branch="$2"
# record changes from cvs into index
stg branch "$parent" || exit $?
cvs -fq update -dP | grep -v '^\? ' | tee /dev/tty | while read status path; do
if [ -e "$path" ]; then
git update-index --add "$path" || exit $?
else
git update-index --remove "$path" || exit $?
fi
# cvs update: `FELIN1_PEP/src/java/com/sagem/felin/ui/widget/PEPRifStateIcon.java' is no longer in the repository
done
# create commit
if git commit -m "stg-cvs sync"; then
:
else
return=$?
fi
# back to branch
stg branch "$branch" || exit $?
return $return
}
cvs_add_dir()
{
local parent=$(dirname "$1")
if [ ! -e "$parent/CVS" ]; then
cvs_add_dir "$parent"
fi
cvs add "$1"
}
# get context
branch=$(stg branch)
parent=$(git config "branch.${branch}.stgit.parentbranch") ||
usage "no declared parent for '$branch' - set branch.${branch}.stgit.parentbranch"
# extract command
[ "$#" -ge 1 ] || usage
command="$1"
shift
case "$command" in
fetch)
do_fetch "$parent" "$branch"
;;
pull)
if do_fetch "$parent" "$branch"; then
# update
# --merged
stg rebase "$parent"
stg clean --applied
fi
;;
commit)
# sanity asserts
[ $(stg applied | wc -l) = 1 ] ||
usage "you don't have exactly one patch applied"
# context
patch=$(stg top)
# adds
stg files | grep ^A | cut -c3- | while read file; do
parent=$(dirname "$file")
if [ ! -e "$parent/CVS" ]; then
cvs_add_dir "$parent"
fi
cvs -f add "$file"
done
# removes
stg files | grep ^D | cut -c3- | xargs -r cvs -f remove
# commit
stg files --bare | xargs -r cvs -fq commit \
-F ".git/patches/$branch/patches/$patch/description"
# sync the parent branch
stg branch "$parent"
git cherry-pick "patches/${branch}/${patch}"
stg branch "${branch}"
# update
# --merged
stg rebase "$parent"
stg clean --applied
;;
_commands)
# hint for bash-completion people :)
do_commands
;;
*)
usage "unknown command '$command'"
;;
esac