]> git.mxchange.org Git - flightgear.git/blobdiff - scripts/tools/fg-submit
replay/FDM shell subsystem refactoring
[flightgear.git] / scripts / tools / fg-submit
index f471a8830532d76fc067b367aa86fb0667549012..9ca695ff803d6abd9dbbb68d6b5ae8c549c94cd2 100755 (executable)
 #!/bin/bash
 #
-# This script called in a CVS directory generates an archive in that
-# same directory, which contains all locally added new files (except
-# those rejected by the script) and a diff with all local changes.
-# This archive can then be offered to one of the CVS maintainers for
-# committing.
+# This script called in a CVS directory compares local files with
+# the repository, and prepares an update package containing all
+# changes and new files for submission to a CVS maintainer. If there
+# are only changes in text files, then a compressed unified diff is
+# made (foo.diff.bz2). If there are also changed binary or new files,
+# then an archive is made instead (foo.tar.bz2). The base name ("foo")
+# can be given as command line argument. Otherwise the directory name
+# is used. The script also leaves a diff in uncompressed/unpackaged
+# form. This is only for developer convenience -- for a quick check
+# of the diff correctness. It is not to be submitted. The script will
+# not overwrite any file, but rather rename conflicting files.
 #
-# Usage:
-#         $ cd $FG_ROOT/Aircraft/foo
-#         $ fg-submit      # generates foo.tar.bz2 and foo.diff
+# Usage:  fg-submit [-v] [<basename>]
 #
-# The archive contains a copy of the diff, which is only left for
-# convenience and doesn't have to be submitted.
+# Options:
+#         -v ... verbose output
+#
+# Example:
+#     $ cd $FG_ROOT/Aircraft/bo105
+#     $ fg-submit                # -> bo105.diff.bz2 or bo105.tar.bz2
+#
+#     $ fg-submit update         # -> update.diff.bz2 or update.tar.bz2
+#
+#
+# Spaces in the basename are replaced with "%20". People who prefer
+# to have the date in the archive name can conveniently achieve this
+# by defining a shell alias in ~/.bashrc:
+#
+#     alias submit='fg-submit "${PWD##*/}-$(date +%Y-%m-%d)"'
+#
+#
+#
+# If the script finds an application named "fg-upload", then it calls
+# this at the end with two arguments:
+#
+#     $1 ... archive or compressed diff for submission
+#     $2 ... accessory uncompressed diff, *NOT* for submission!
+#
+# $1 and $2 are guaranteed not to contain spaces, only $1 is guaranteed
+# to actually exist. Such a script can be used to upload the file to an
+# ftp-/webserver, and/or to remove one or both files. Example using
+# KDE's kfmclient for upload (alternatives: ncftpput, gnomevfs-copy):
+#
+#     $ cat ~/bin/fg-upload
+#     #!/bin/bash
+#     echo "uploading $1"
+#     if kfmclient copy $1 ftp://user:password@server.com; then
+#             echo "deleting $1 $2"
+#             rm -f $1 $2
+#
+#             URL=ftp://server.com/$1
+#
+#             # copy URL to KDE's clipboard, so that MMB-clicking pastes it
+#             dcop klipper klipper setClipboardContents $URL
+#
+#             echo "Done.  -->  $URL"
+#     else
+#             echo "$0: uploading failed!"
+#     fi
+#
+#
+#
+# Whether a file should be included in the archive or not, is decided
+# by pattern rules. There is a set of reasonable default rules predefined,
+# but alternative settings can be defined in a hidden configuration file
+# named ".fg-submit". Such a file is searched in the current directory,
+# in its parent directory, in its grand-parent directory and so on,
+# and finally in the $HOME directory. The first found file is taken.
+#
+# A file can use a list of three keywords with arguments, each on a
+# separate line:
+#
+#     ALLOW  <pattern-list>     ... accept & report matching file
+#     DENY   <pattern-list>     ... reject & report matching file
+#     IGNORE <pattern-list>     ... silently reject matching file
+#
+# A <pattern-list> is a space-separated list of shell pattern.
+# It may also be empty, in which case it has no effect. Examples:
+#
+#     DENY   test.blend
+#     ALLOW  *.xcf *.blend
+#
+# The list of pattern is checked in the same order in which it was
+# built. The first match causes a file to be accepted or rejected.
+# Further matches are not considered. Comments using the hash
+# character '#' are allowed and ignored.
+#
+# Some default rules are always added at the end. If you want to
+# bypass them, then finish your configuration with an "ALLOW *"
+# or "DENY *", and no file will ever reach the default rules.
+#
+#
+# Example:
+#
+#     DENY  test.xcf # throw out the test image, but ...
+#     ALLOW *.xcf    # ... allow all other GIMP images (the default
+#                    # rules would otherwise throw them out)
+#
+#     ALLOW  not.old # add this file, but ...
+#     IGNORE *.old   # throw out all other "old" files (and don't
+#                    # report that to the terminal)
+#
+#
+# .fg-submit configuration files are "sourced" bash scripts, the
+# keywords are simple shell functions. That means that you can
+# also use other bash commands in that file, such as "echo", or
+# write several commands on one line, separated with semicolon.
+# You can even put all special rules in your ~/.fg-submit file,
+# with rules depending on the working directory:
+#
+#     case "$PWD" in
+#     */bo105*)  DENY *.osg; ALLOW livery.xcf ;;
+#     */ufo*)    DENY *.tiff ;;
+#     esac
+
+
+
+SELF=${0##*/}
+DIR=${PWD##*/}
+
+if [ "$1" == "-v" ]; then
+       DBG=1
+       shift
+fi
+
+BASE=${1:-$DIR}
+BASE=${BASE// /%20}
 
-SELF=$(basename $0)
-AIRCRAFT=$(basename $PWD)
+CVS=/usr/bin/cvs                 # avoid colorcvs wrapper from
+[ -x $CVS ] || CVS=cvs           # http://www.hakubi.us/colorcvs/
+UPLOAD=$(which fg-upload 2>/dev/null)
 
-CVS=/usr/bin/cvs
-ARCHIVE=$AIRCRAFT.tar.bz2
-DIFF=$AIRCRAFT.diff
+CONFIG_FILE=".fg-submit"
+ARCHIVE=$BASE.tar.bz2
+DIFF=$BASE.diff
+CDIFF=$DIFF.bz2
 
+# these rules are always prepended; the first letter decides if the
+# rule accepts (+), rejects (-), or ignores (!) a matching file.
+PREFIX_RULES="
+       !$DIFF* !$CDIFF* !$ARCHIVE*
+       !CVS/* !*/CVS/*
+"
+# these rules are always appended
+DEFAULT_RULES="
+       +.cvsignore +*/.cvsignore
+       -*~ -*. -*.bak -*.orig
+       -*.RGB -*.RGBA -*.MDL
+       -*.XCF -*.tga -*.TGA -*.bmp -*.BMP -*.PNG
+       -*.blend -*.blend[0-9] -*blend[0-9][0-9] -*.blend[0-9][0-9][0-9]
+       -*.gz -*.tgz -*.bz2 -*.zip -*.tar.gz* -*.tar.bz2*
+"
+POSTFIX_RULES="
+       !.* !*/.*
+       +*
+"
 
-function ERROR  { echo -e "\e[31;1m$*\e[m";   }
-function LOG    { echo -e "\e[35m$*\e[m";     }
-function ADD    { echo -e "\e[32m\t+ $*\e[m"; }
-function REJECT { echo -e "\e[31m\t- $*\e[m"; }
+
+
+function ERROR   { echo -e "\e[31;1m$*\e[m";   }
+function LOG     { echo -e "\e[35m$*\e[m";     }
+function NEW     { echo -e "\e[32m\t+ $*\e[m"; }
+function CHANGED { echo -e "\e[36m\t+ $*\e[m"; }
+function REJECT  { echo -e "\e[31m\t- $*\e[m"; }
+function DEBUG   { [ $DBG ] && echo -e "$*";   }
 
 function diffstat {
        # output diff statistics, similar to the "diffstat" utility
        awk '
-               function out() {
+               function line(a, r, c, f) {
+                       print "\t\033[32m"a"\033[m\t\033[31m"r"\033[m\t\033[34m"c"\033[m\t"f
+               }
+               function dofile() {
+                       if (!file) {
+                               return
+                       }
                        if (bin) {
-                               print "\t\tbinary\t\t"file;
+                               print "\t. . . . binary  . . . . \033[36m"file"\033[m"
                        } else {
-                               print "\t+"a"\t-"r"\t!"c"\t"file
+                               line(a, r, c, file)
+                               at += a; rt += r; ct += c
                        }
+                       a = r = c = 0
                }
                BEGIN      {
-                       print "\tadded___removed_changed___________________________________";
-                       a = r = c = at = rt = ct = n = bin = 0;
+                       print "\tadded---removed-changed----------------------------------------"
+                       a = r = c = at = rt = ct = n = bin = 0
                }
-               /^Index: / { if (file) { out() } a = r = c = scan = bin = 0; file = $2; n += 1 }
-               /^@@/      { scan = 1 }
-               /^Binary/  { if (!scan) { bin = 1 } }
-               /^+/       { if (scan) { a += 1; at += 1 } }
-               /^-/       { if (scan) { r += 1; rt += 1 } }
-               /^!/       { if (scan) { c += 1; ct += 1 } }
+               /^Index: / { dofile(); scan = bin = 0; file = $2; n++; next }
+               /^@@/      { scan = 1; next }
+               /^Binary/  { if (!scan) bin = 1; next }
+               /^\+/      { if (scan) a++; next }
+               /^-/       { if (scan) r++; next }
+               /^!/       { if (scan) c++; next }
                END        {
-                       if (file) { out() }
-                       print "\t-----------------------------------total------------------";
-                       print "\t+"at"\t-"rt"\t!"ct"\tin "n" files"
+                       dofile()
+                       print "\t----------------------------------------total------------------"
+                       line(at, rt, ct, "\033[min "n" files")
                }
-       ' < $1
+       ' <$1
+}
+
+function backup_filename {
+       for ((i = 1; 1; i = i + 1)); do
+               name=$1.$i
+               if ! [ -a "$name" ]; then
+                       touch $name
+                       echo $name
+                       return
+               fi
+       done
+}
+
+
+
+# set up accept/reject rules
+function ALLOW   { for i in $*; do RULES="$RULES +$i"; done }
+function DENY    { for i in $*; do RULES="$RULES -$i"; done }
+function IGNORE  { for i in $*; do RULES="$RULES !$i"; done }
+
+
+function search_config {
+       file="$1/$CONFIG_FILE"
+       DEBUG "checking for config file $file"
+       if [ -f "$file" ]; then
+               CONFIG="$file"
+               return 0
+       elif [ "$1" ]; then
+               search_config ${1%/${1##*/}} # parent dir
+               return
+       fi
+       return 1
 }
 
 
-# create temporary dir that's automatcally removed on exit
-TMP=$(mktemp -d /tmp/$SELF.$AIRCRAFT.XXXXXX) || (echo "$0: can't create temporary dir"; exit 1)
+set -f
+RULES=
+if search_config "$PWD"; then
+       LOG "loading config file $CONFIG"
+       source "$CONFIG"
+elif [ -f ~/$CONFIG_FILE ]; then
+       DEBUG "loading config file ~/$CONFIG_FILE"
+       source ~/$CONFIG_FILE
+elif [ -f ~/${CONFIG_FILE}rc ]; then
+       DEBUG "loading config file ~/${CONFIG}rc"
+       source ~/${CONFIG_FILE}rc
+fi
+RULES="$PREFIX_RULES $RULES $DEFAULT_RULES $POSTFIX_RULES"
+set +f
+
+
+if [ $DBG ]; then
+       DEBUG "using these rules: "
+       for i in $RULES; do echo -n "$i "; done
+       echo
+fi
+
+
+
+# create temporary dir that's automatically removed on exit
+TMP=$(mktemp -d /tmp/$SELF.$BASE.XXXXXX) || (echo "$0: can't create temporary dir"; exit 1)
 trap "rm -rf $TMP" 0 1 2 3 13 15
 
 
-# move older archive or diff file out of the way
-[ -f $DIFF ] && mv -i $DIFF $DIFF~
-[ -f $ARCHIVE ] && mv -i $ARCHIVE $ARCHIVE~
+
+# move old files out of the way adding sequential suffixes
+for i in $DIFF $CDIFF $ARCHIVE; do
+       [ -f $i ] && mv $i $(backup_filename $i)
+done
+
 
 
-LOG "updating and checking for changed and new files ..."
-$CVS -q up -dP >$TMP/up
+LOG "updating and checking for new files ..."
+$CVS -q up -dP >$TMP/up || exit 1
 
 
 if grep "^C " $TMP/up &>/dev/null; then
@@ -76,73 +283,84 @@ if grep "^C " $TMP/up &>/dev/null; then
 fi
 
 
+
 LOG "making diff ..."
 if ! $CVS -q diff -up >$DIFF; then
        LOG "diff statistics:"
        diffstat $DIFF
+       echo
 
        # add diff file itself
-       echo $DIFF >> $TMP/include
+       echo $DIFF >>$TMP/files
 
        # add changed binary files
        awk '
-               /^Index: / { scan = 1; file = $2 }
-               /^@@/      { scan = 0 }
+               /^Index: / { scan = 1; file = $2; next }
+               /^@@/      { scan = 0; next }
                /^Binary/  { if (scan) { print file } }
-       ' < $DIFF >>$TMP/include
+       ' <$DIFF >>$TMP/files
 else
        rm -f $DIFF
 fi
 
 
-# write list of all files to add
-LOG "adding to archive ..."
-if [ -f $TMP/include ]; then
-       cat $TMP/include|while read i; do
-               ADD "$i"
-               echo $i >> $TMP/files
+
+LOG "checking for files to submit ..."
+if [ -f $TMP/files ]; then
+       cat $TMP/files|while read i; do
+               CHANGED "$i"
        done
 fi
+
 grep "^? " $TMP/up|while read i; do
-       FILE=${i#? }
-       find $FILE -type f >> $TMP/files
+       find ${i#? } -type f >>$TMP/check
 done
 
 
-# classify and filter files
-if [ -f $TMP/files ]; then
-       for FILE in $(cat $TMP/files); do
-               case "$FILE" in
-               $ARCHIVE*|$DIFF*) # don't add files generated by the script
-                       ;;
-               */.*|.*)          # silently drop hidden files
-                       ;;
-               *~|*.bak|*.orig|*..)
-                       REJECT "$FILE\t\t(backup file)"
-                       ;;
-               *CVS/*)
-                       REJECT "$FILE\t\t(CVS file)"
-                       ;;
-               *.blend|*.blend[0-9]|*.blend[0-9][0-9])
-                       REJECT "$FILE\t\t(blender file)"
-                       ;;
-               *.xcf|*.tga|*.bmp|*.BMP|*.png)
-                       REJECT "$FILE\t\t(graphics file)"
-                       ;;
-               *)
-                       ADD "$FILE"
-                       echo "$FILE" >> $TMP/include
-                       ;;
-               esac
+
+# filter files according to the pattern rules
+if [ -f $TMP/check ]; then
+       for i in $(cat $TMP/check); do
+               DEBUG "checking whether file '$i' matches"
+               for r in $RULES; do
+                       DEBUG "\t\trule $r"
+                       class=${r:0:1}
+                       rule=${r:1}
+                       case "$i" in $rule)
+                               case $class in
+                               !) DEBUG "$i\t\t\"silently\" rejected\t\t$rule" ;;
+                               -) REJECT "$i\t\t$rule" ;;
+                               +) NEW "$i\t\t$rule" && echo "$i" >>$TMP/files ;;
+                               esac
+                               break
+                               ;;
+                       esac
+               done
        done
 fi
 
 
-if [ -f $TMP/include ]; then
-       LOG "creating archive $ARCHIVE"
-       tar -cjf $ARCHIVE --files-from $TMP/include
+
+if ! [ -f $TMP/files ]; then
+       LOG "no changed or new files found"
+       exit 0
+fi
+
+echo
+numfiles=$(awk '//{n++}END{print n}' <$TMP/files)
+if [ -f $DIFF -a $numfiles == 1 ]; then
+       LOG "only changed non-binary files found"
+       LOG "creating compressed diff \e[1;37;40m$CDIFF\e[m\e[35m ..."
+       bzip2 --keep $DIFF
+       RESULT=$CDIFF
 else
-       LOG "no changes or new files detected"
+       LOG "changed and/or new files found"
+       LOG "creating archive \e[1;37;40m$ARCHIVE\e[m\e[35m ..."
+       tar --create --bzip2 --file=$ARCHIVE --files-from $TMP/files
+       RESULT=$ARCHIVE
 fi
-exit 0
+
+
+[ -x "$UPLOAD" -a -f $RESULT ] && $UPLOAD $RESULT $DIFF
+