blob: c451062846abb8437e73e5d4797d7cfade21d882 [file] [log] [blame]
#!/bin/bash
#-------------------------------------------------------------------------
# Author: Ross Mark (rossm@controllingedge.com.au)
# Date: Tue Mar 11 10:02:57 EST 2003
#
# Copyright (C) 2003-2004 Ross Mark
#
#-------------------------------------------------------------------------
#
# Description:
# Archive SVN (asvn) will allow the recording of file types not
# normally handled by svn. Currently this includes devices,
# symlinks and file ownership/permissions.
#
# Every file and directory has a 'file:permissions' property set and
# every directory has a 'dir:devices' and 'dir:symlinks' for
# recording the extra information.
#
# Run this script instead of svn with the normal svn arguments.
#
#
# Licensing:
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
#
#-------------------------------------------------------------------------
# $HeadURL$
# $LastChangedDate$
# $LastChangedBy$
# $LastChangedRevision$
SVN=/usr/bin/svn
ACTION=""
DEV_PROP="dir:devices"
SYM_PROP="dir:symlinks"
FILE_PROP="file:permissions"
TMPFILE=/tmp/asvn.tmp.$$
TMPFILE1=/tmp/asvn.tmp1.$$
TMPFILE2=/tmp/asvn.tmp2.$$
PCWD=`/bin/pwd`
SKIPSVN='\( -name .svn -prune -false \)'
PRINTDETAILS="-printf \"file='%p' mode=%m user=%u(%U) group=%g(%G)\n\""
trap cleanup 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
function cleanup()
{
rm -f $TMPFILE $TMPFILE1 $TMPFILE2
}
function basedirname()
{
refname="$1"
dir="`dirname \"$2\"`"
ref="`expr \"$dir\" : \"$refname/\(.*\)\"`"
if [ -z "$ref" ]
then
echo .
else
echo $ref
fi
}
#
# Modifies TMPFILE2
#
function addignorefile()
{
file="`basename \"$1\"`"
dir="`dirname \"$1\"`"
efile="`echo $file |sed -e 's!\([\[\(\$]\)!\\\\\1!g'`"
gefile="`echo $efile |sed -e 's!\(\\\\\)!\\\\\\\\\1!g'`"
if ! ($SVN propget svn:ignore "$dir" | grep -q "^$gefile\$")
then
$SVN propget svn:ignore "$dir" |sed -e '/^$/d' >$TMPFILE2
echo "$efile" >>$TMPFILE2
$SVN propset svn:ignore -F $TMPFILE2 "$dir"
echo setting ignore
#cat $TMPFILE2 >&2
fi
}
function deleteignorefile()
{
file="`basename \"$1\"`"
dir="`dirname \"$1\"`"
efile="`echo $file |sed -e 's!\([\[\(\$]\)!\\\\\1!g'`"
gefile="`echo $efile |sed -e 's!\(\\\\\)!\\\\\\\\\1!g'`"
echo "deleting ignore setting for '$file'"
if ($SVN propget svn:ignore "$dir" | grep -q "^$gefile\$")
then
$SVN propget svn:ignore "$dir" |sed -e '/^$/d' |grep -v "^$gefile\$" >$TMPFILE2
$SVN propset svn:ignore -F $TMPFILE2 "$dir"
#cat $TMPFILE2 >&2
fi
}
function recorddirinfo
{
eval "find $PCWD $SKIPSVN -o \( -type d ! -name .svn -print \)" |while read dirlist
do
updatedirsymlinks $1 "$dirlist"
updatedirdevices $1 "$dirlist"
done
}
function updatedirdevices()
{
CHECKIN=false
if [ "$1" = "-ci" ]
then
CHECKIN=true
shift
fi
dir="$1"
echo checking $dir for devices
#
# Obtain the list of devices in this directory
#
find "$dir" \( \( -type b -o -type c -o -type p \) -print \) -o -type d ! -name "`basename \"$dir\"`" -prune | while read file
do
echo -n `find "$file" -printf "file='%f' mode=%m user=%u(%U) group=%g(%G)"`
[ -b "$file" ] && echo -n ' type=b'
[ -c "$file" ] && echo -n ' type=c'
[ -p "$file" ] && echo ' type=p'
if [ -b "$file" -o -c "$file" ]
then
ls -l "$file" |
sed -e 's/^[-lcpbrdwxXstugoTS]* *[0-9] [^ ]* *[^ ]* *\([0-9]*\), *\([0-9]*\) .*/ major=\1 minor=\2/'
fi
# In this case file is the full path.
addignorefile "$file"
done | sort >$TMPFILE
#
# Obtain the currently defined devices
#
$SVN propget $DEV_PROP "$dir" >$TMPFILE1
#
# If the two list are the same then there is nothing to do.
#
if /usr/bin/cmp $TMPFILE1 $TMPFILE >/dev/null
then
return
fi
if [ -s $TMPFILE ]
then
# There are devices in this directory
if [ "$CHECKIN" = "true" ]
then
# Add the current devices to the property
$SVN propset $DEV_PROP "$dir" -F $TMPFILE
else
# Delete all the unwanted devices ie not in TMPFILE1
cat $TMPFILE |while read line
do
file="`expr \"$line\" : \"file='\(.*\)' mode\"`"
if ! grep -q "file='$file'" $TMPFILE1
then
rm "$file"
deleteignorefile "$file"
fi
done
fi
else
# There are no devices in this directory
if [ "$CHECKIN" = "true" ]
then
$SVN propdel $DEV_PROP "$dir"
fi
fi
#
# If we are not a checkin then make sure all the devices are defined
#
if [ "$CHECKIN" != "true" ]
then
cat $TMPFILE1 |while read info
do
#echo info = $info
[ -z "$info" ] && continue
grep -q "$info" $TMPFILE && continue # This line still matches
file="`expr \"$info\" : \"file='\(.*\)' \"`"
mode=`expr "$info" : ".*' mode=\([0-9]*\) "`
user=`expr "$info" : ".* user=\([^(]*\)("`
uid=`expr "$info" : ".* user=[^(]*(\([0-9]*\) "`
group=`expr "$info" : ".* group=\([^(]*\)("`
gid=`expr "$info" : ".* group=[^(]*(\([0-9]*\) "`
type=`expr "$info" : ".* type=\(.\)"`
major=`expr "$info" : ".* major=\([0-9]*\)"`
minor=`expr "$info" : ".* minor=\([0-9]*\)"`
#
# This file is either missing or wrong
# Delete the old and create it anew.
#
rm -f "$dir/$file"
mknod --mode=$mode "$dir/$file" $type $major $minor
chown $user:$group "$dir/$file"
addignorefile "$dir/$file"
done
fi
}
function updatedirsymlinks()
{
CHECKIN=false
if [ "$1" = "-ci" ]
then
CHECKIN=true
shift
fi
dir="$1"
echo checking $dir for symlinks
cp /dev/null $TMPFILE
#
# Obtain the list of symlinks in this directory
#
find "$dir" \( -type l -printf "file='%f' dest='%l'\n" \) -o -type d ! -name "`basename \"$dir\"`" -prune |
sort >$TMPFILE
#
# Make sure all the symlinks are being ignored.
#
cat $TMPFILE |while read line
do
file="`expr \"$line\" : \"file='\(.*\)' dest\"`"
addignorefile "$dir/$file"
done
#
# Obtain the currently defined symlinks
#
$SVN propget $SYM_PROP "$dir" >$TMPFILE1
#
# If the two list are the same then there is nothing to do.
#
if cmp $TMPFILE1 $TMPFILE >/dev/null
then
return
fi
if [ -s $TMPFILE ]
then
# There are symlinks in this directory
if [ "$CHECKIN" = "true" ]
then
# Add the current symlinks to the property
$SVN propset $SYM_PROP "$dir" -F $TMPFILE
else
# Delete all the unwanted symlinks
cat $TMPFILE |while read line
do
file="`expr \"$line\" : \"file='\(.*\)' dest\"`"
efile="`echo \"$file\" |sed -e 's!\([\[\(\$]\)!\\\\\1!g'`"
if ! grep -q "file='$efile'" $TMPFILE1
then
rm "$dir/$file"
deleteignorefile "$dir/$file"
fi
done
fi
else
# There are no symlinks in this directory
if [ "$CHECKIN" = "true" ]
then
$SVN propdel $SYM_PROP "$dir"
fi
fi
#
# If we are not a checkin then make sure all the symlinks are defined
#
if [ "$CHECKIN" != "true" ]
then
cat $TMPFILE1 |while read info
do
[ -z "$info" ] && continue
file="`expr \"$info\" : \"file='\(.*\)' dest\"`"
dest="`expr \"$info\" : \".*' dest='\(.*\)'$\"`"
if [ -L "$dir/$file" ]
then
[ "`find \"$dir/$file\" -printf '%l'`" = "$dest" ] && continue
fi
rm -f "$dir/$file"
ln -s "$dest" "$dir/$file"
done
fi
}
function recordpermissions()
{
CHECKIN=false
if [ "$1" = "-ci" ]
then
CHECKIN=true
shift
fi
# Find all the directories and files
cp /dev/null $TMPFILE
eval "find $PCWD $SKIPSVN -o \( \( -type d ! -name .svn \) -o -type f \) $PRINTDETAILS" | while read info
do
device=`expr "$info" : "file='\(.*\)' mode"`
info=`expr "$info" : "file='.*' \(mode.*\)"`
if [ "$PCWD" = "$device" ]
then
dir="."
file=""
else
dir="`basedirname \"$PCWD\" \"$device\"`"
file="`basename \"$device\"`"
fi
# see if the properties have changed.
if [ "`$SVN propget $FILE_PROP \"$dir/$file\"`" != "$info" ]
then
if [ "$CHECKIN" = "true" ]
then
$SVN propset $FILE_PROP "$info" "$dir/$file"
else
info=`$SVN propget $FILE_PROP "$dir/$file"`
mode=`expr "$info" : "mode=\([0-9]*\) "`
user=`expr "$info" : ".* user=\([^(]*\)("`
uid=`expr "$info" : ".* user=[^(]*(\([0-9]*\) "`
group=`expr "$info" : ".* group=\([^(]*\)("`
gid=`expr "$info" : ".* group=[^(]*(\([0-9]*\) "`
if [ "$user" = "" -o "$group" = "" -o "$mode" = "" ]
then
echo "property $FILE_PROP not set for $dir/$file"
else
chown $user:$group "$dir/$file"
chmod $mode "$dir/$file"
fi
fi
fi
done
}
function pre_checkin()
{
echo this is the pre checkin process
recorddirinfo -ci
recordpermissions -ci
}
function post_checkout()
{
echo this is the post checkout process
if [ "$CHDIR" = "true" ]
then
shift $(($# -1))
cd "`basename \"$1\"`"
PCWD="$PCWD/`basename \"$1\"`"
fi
recorddirinfo
recordpermissions
}
CHDIR=false
case "$1" in
checkout|co) CHDIR=true; ACTION="post";;
commit|ci) ACTION="pre";;
switch|sw) ACTION="post";;
update|up) ACTION="post";;
*);;
esac
[ "$ACTION" = "pre" ] && pre_checkin "$@"
$SVN "$@"
[ $? = 0 -a "$ACTION" = "post" ] && post_checkout "$@"
cleanup
#
# vim: set ai ts=8 sw=4
#