- fix config file name in $HOME; This didn't match the documentation. (doesn't cost us anything to check ~/.fg-submit first, and then ~/.fg-submitrc) - don't use mktemp for the backup files. Some outdated distributions (Debian) come with a version that mandates six X, which is just too ugly. Just find the first free slot with sequential number. That isn't thread safe, but mktemp isn't either, so ... (Should be using "lockfile", but its availability on CygWin is questionable. And it's not *that* important.) - some more documentation - some cleanup, too, of course
358 lines
9.7 KiB
Bash
Executable file
358 lines
9.7 KiB
Bash
Executable file
#!/bin/bash
|
|
#
|
|
# 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: fg-submit [-v] [<basename>]
|
|
#
|
|
# 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 diff, *NOT* for submission!
|
|
#
|
|
# $1 and $2 are guaranteed not to contain spaces, only $1 is guaranteed
|
|
# to actually exist. Such as 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, wput):
|
|
#
|
|
# $ 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
|
|
#
|
|
# echo "Done. URL: ftp://server.com/$1"
|
|
# else
|
|
# echo "arghh ... HELP! HELP!"
|
|
# 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 four 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
|
|
# DEFAULT ... adds default rules
|
|
#
|
|
# 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
|
|
#
|
|
# A config file that only contains the keyword DEFAULT causes the
|
|
# same behavior as no config file at all. A config file without
|
|
# DEFAULT drops the built-in default rules (with the exception of
|
|
# a few very basic ones, such as rejection of CVS files). Comments
|
|
# using the hash character '#' are allowed and ignored.
|
|
#
|
|
# The list of pattern is checked in the same in order in which it
|
|
# is built. The first match causes a file to be accepted or rejected.
|
|
# Further matches are not considered.
|
|
#
|
|
# Example:
|
|
#
|
|
# DENY test.xcf # throw out the test graphic, but ...
|
|
# ALLOW *.xcf # ... allow all other GIMP graphics (the following
|
|
# # DEFAULT keyword throws them out otherwise)
|
|
#
|
|
# DEFAULT # insert the default rules here
|
|
#
|
|
# ALLOW g.old # add this silly file :-)
|
|
# IGNORE *.old # throw out the 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
|
|
# DEFAULT
|
|
|
|
|
|
|
|
SELF=${0/#*\/}
|
|
DIR=${PWD/#*\/}
|
|
|
|
if [ "$1" == "-v" ]; then
|
|
DBG=1
|
|
shift
|
|
fi
|
|
|
|
BASE=${1:-$DIR}
|
|
BASE=${BASE// /%20}
|
|
|
|
CVS=/usr/bin/cvs # avoid colorcvs wrapper from
|
|
[ -x $CVS ] || CVS=cvs # http://www.hakubi.us/colorcvs/
|
|
UPLOAD=$(which fg-upload 2>/dev/null)
|
|
|
|
CONFIG_FILE=".fg-submit"
|
|
ARCHIVE=$BASE.tar.bz2
|
|
DIFF=$BASE.diff
|
|
CDIFF=$DIFF.bz2
|
|
|
|
# these rules are always prepended (the leading ! makes silent rejects)
|
|
PREFIX_RULES="
|
|
!$DIFF* !$CDIFF* !$ARCHIVE*
|
|
!CVS/* !*/CVS/*
|
|
"
|
|
# these rules are used when no other rules are specified,
|
|
# and wherever the DEFAULT keyword is used
|
|
DEFAULT_RULES="
|
|
+.cvsignore +*/.cvsignore
|
|
-*~ -*. -*.bak -*.orig
|
|
-*.RGB -*.RGBA -*.MDL
|
|
-*.xcf -*.XCF -*.tga -*.TGA -*.bmp -*.BMP -*.png -*.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*
|
|
"
|
|
# these rules are always appended: ignore all hidden files that
|
|
# weren't explicitly allowed so far, and accept all the rest
|
|
POSTFIX_RULES="
|
|
!.* !*/.*
|
|
+*
|
|
"
|
|
|
|
|
|
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 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. . . . binary . . . . \033[36m"file"\033[m"
|
|
} else {
|
|
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
|
|
}
|
|
/^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 {
|
|
dofile()
|
|
print "\t----------------------------------------total------------------"
|
|
line(at, rt, ct, "\033[min "n" files")
|
|
}
|
|
' <$1
|
|
}
|
|
|
|
function backup_filename {
|
|
i=1
|
|
while true; do
|
|
name=$1.$i
|
|
if ! [ -a "$name" ]; then
|
|
touch $name
|
|
echo $name
|
|
return
|
|
fi
|
|
i=$(($i + 1))
|
|
done
|
|
}
|
|
|
|
|
|
# set up accept/reject rules
|
|
function DEFAULT { RULES="$RULES $DEFAULT_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 }
|
|
|
|
RULES=
|
|
HERE=$PWD
|
|
while true; do
|
|
if [ -f $CONFIG_FILE ]; then
|
|
CONFIG="$PWD/$CONFIG_FILE"
|
|
break
|
|
fi
|
|
cd ..
|
|
[ "$PWD" == "/" ] && break
|
|
done
|
|
cd "$HERE"
|
|
|
|
if [ "$CONFIG" ]; then
|
|
DEBUG "reading config $CONFIG"
|
|
source "$CONFIG"
|
|
elif [ -f ~/$CONFIG_FILE ]; then
|
|
DEBUG "reading config ~/$CONFIG_FILE"
|
|
source ~/$CONFIG_FILE
|
|
elif [ -f ~/${CONFIG_FILE}rc ]; then
|
|
DEBUG "reading config ~/${CONFIG_FILE}rc"
|
|
source ~/$CONFIG_FILE
|
|
else
|
|
DEBUG "no config file found; using default rules"
|
|
RULES="$RULES $DEFAULT_RULES"
|
|
fi
|
|
RULES="$PREFIX_RULES $RULES $POSTFIX_RULES"
|
|
DEBUG "using these rules: $RULES"
|
|
|
|
|
|
# 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 old files out of the way giving sequential suffixes
|
|
for i in $DIFF $CDIFF $ARCHIVE; do
|
|
[ -f $i ] && mv $i $(backup_filename $i)
|
|
done
|
|
|
|
|
|
LOG "updating and checking for new files ..."
|
|
$CVS -q up -dP >$TMP/up
|
|
|
|
|
|
if grep "^C " $TMP/up &>/dev/null; then
|
|
ERROR "there are conflicts with the following files:"
|
|
grep "^C " $TMP/up
|
|
exit 1
|
|
fi
|
|
|
|
|
|
LOG "making diff ..."
|
|
if ! $CVS -q diff -up >$DIFF; then
|
|
LOG "diff statistics:"
|
|
diffstat $DIFF
|
|
echo
|
|
|
|
# add diff file itself
|
|
echo $DIFF >>$TMP/files
|
|
|
|
# add changed binary files
|
|
awk '
|
|
/^Index: / { scan = 1; file = $2; next }
|
|
/^@@/ { scan = 0; next }
|
|
/^Binary/ { if (scan) { print file } }
|
|
' <$DIFF >>$TMP/files
|
|
else
|
|
rm -f $DIFF
|
|
fi
|
|
|
|
|
|
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
|
|
find ${i#? } -type f >>$TMP/check
|
|
done
|
|
|
|
|
|
# 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"
|
|
R=${r#?}
|
|
case "!$i" in $r)
|
|
DEBUG "\t\t\t\"silently\" rejected\t\t$R"
|
|
break
|
|
;;
|
|
esac
|
|
case "-$i" in $r)
|
|
REJECT "$i\t\t$R"
|
|
break
|
|
;;
|
|
esac
|
|
case "+$i" in $r)
|
|
NEW "$i\t\t$R"
|
|
echo "$i" >>$TMP/files
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
done
|
|
fi
|
|
|
|
|
|
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 -k $DIFF
|
|
RESULT=$CDIFF
|
|
else
|
|
LOG "changed and/or new files found"
|
|
LOG "creating archive \e[1;37;40m$ARCHIVE\e[m\e[35m ..."
|
|
tar -cjf $ARCHIVE --files-from $TMP/files
|
|
RESULT=$ARCHIVE
|
|
fi
|
|
|
|
[ -x "$UPLOAD" -a -f $RESULT ] && $UPLOAD $RESULT $DIFF
|
|
|
|
exit 0
|
|
|