Add contrib/stg-cvs: helper script to manage a mixed cvs/stgit working copy
authorYann Dirson <ydirson@altern.org>
Fri, 2 Mar 2007 21:34:28 +0000 (21:34 +0000)
committerCatalin Marinas <catalin.marinas@gmail.com>
Fri, 2 Mar 2007 21:34:28 +0000 (21:34 +0000)
This is an early prototype only, use with care, and be sure to read the
LIMITATIONS section in the script comments.

Signed-off-by: Yann Dirson <ydirson@altern.org>
contrib/stg-cvs [new file with mode: 0755]

diff --git a/contrib/stg-cvs b/contrib/stg-cvs
new file mode 100755 (executable)
index 0000000..7b968d6
--- /dev/null
@@ -0,0 +1,171 @@
+#!/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
+
+# LIMITATIONS
+# - this is only a proof-of-concept prototype
+# - lacks an "init" command
+# - "commit" does not ensure the base is uptodate before trying to
+#   commit (but hey, it's CVS ;)
+# - "commit" can only commit a single patch
+# - not much robustness here
+# - still bad support for files removed in cvs (should catch "no
+#   longer in the repository" message)
+# - 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)
+# - this only deals with CVS but could surely be extended to any other
+#   VCS
+# - 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
+# - 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
+# - 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-repo-config "branch.${branch}.merge") || 
+    usage "no declared parent for '$branch' - set branch.${branch}.merge"
+
+# 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