Like to be free? ... then: Fight for Freedom
asvn - svn including file permissions and ownership
If you want to control somethink like /etc with svn you definitely
need svn to take care of file permissions. I found this asvn
script which works good after I spend some time to sort out all
this patch-chaos. I found nowhere an up-to-date version. So I
decided to put it up here.
This script was written by Ross Mark (rossm at
controllingedge.com.au), I downloaded it from http://svn.collab.net/repos/svn/trunk/contrib/client-side/asvn. That
I patched it with http://markmail.org/message/rqitgigeh2ep5dsw
and made it working. So I hope its going to help you.
download
download
#!/bin/bash
#-------------------------------------------------------------------------
# Author: Ross Mark (rossm@controllingedge.com.au)
# Date: Tue Mar 11 10:02:57 EST 2003
#
# Copyright (C) 2003-2004 Ross Mark
#
#-------------------------------------------------------------------------
# This script was written by Ross Mark (rossm at
# controllingedge.com.au), I (Andreas Hofmeier, http://www.abmh.de/)
# downloaded it from
# http://svn.collab.net/repos/svn/trunk/contrib/client-side/asvn. Than
# I patched it with http://markmail.org/message/rqitgigeh2ep5dsw and
# made it working. So I hope its going to help you.
# 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
#
# http://svn.collab.net/repos/svn/trunk/contrib/client-side/asvn
#-------------------------------------------------------------------------
# $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
}
# Escapes all "special" filename characters. SED magic was modified from
# the original found at the website:
# http://elonen.iki.fi/code/misc-notes/remove-duplicate-files/index.html
#
function escapename()
{
echo "`echo $1 | sed -r 's/([^a-zA-Z0-9./_-])/\\\\\1/g'`"
}
#
# 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'`"
efile="`escapename \"$file\"`"
gefile="`echo $efile |sed -e 's!\([\\]\)!\1\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" > /dev/null 2>&1
# $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'`"
file=`basename "$1"`
dir=`dirname "$1"`
efile="`escapename \"$file\"`"
gefile="`echo $efile |sed -e 's!\([\\]\)!\1\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"
$SVN propset svn:ignore -F $TMPFILE2 "$dir" > /dev/null 2>&1
#cat $TMPFILE2 >&2
fi
}
function recorddirinfo
{
# eval "find $PCWD $SKIPSVN -o \( -type d ! -name .svn -print \)" |while read dirlist
find "$PCWD" \( -type d -name .svn -print \) | sed -e 's/\/\.svn//' | while read dirlist
do
# updatedirsymlinks $1 "$dirlist"
# updatedirdevices $1 "$dirlist"
recordpermissions $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
find "$dir" -maxdepth 1 \( -type b -o -type c -o -type p \) -printf "file='%p' mode=%m user=%u(%U) group=%g(%G)\n" |
sed -r 's#\\#\\\\#g' | sort | while read devinfo
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" ]
file=`expr "$devinfo" : "file='\(.*\)' mode"`
echo -n "$devinfo"
[ -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
}
# No longer needed.
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
dir="$1"
echo checking $dir for permissions
cp /dev/null $TMPFILE
# eval "find $PCWD $SKIPSVN -o \( \( -type d ! -name .svn \) -o -type f \) $PRINTDETAILS" | while read info
#
# Obtain the list of files and subdirectories in this directory
# We also need to fix the backslash characters before the 'while read' or
# they will get interepreted within the read.
#
find "$dir" -maxdepth 1 \( -type f -o \( -type d ! -name \.svn \) \) -printf "file='%p' mode=%m user=%u(%U) group=%g(%G)\n" |
sed -r 's#\\#\\\\#g' | sort | while read info
do
device=`expr "$info" : "file='\(.*\)' mode"`
file=`expr "$info" : "file='\(.*\)' mode"`
info=`expr "$info" : "file='.*' \(mode.*\)"`
# if [ "$PCWD" = "$device" ]
if [ "$PCWD" = "$file" ]
then
dir="."
file=""
else
# dir="`basedirname \"$PCWD\" \"$device\"`"
# file="`basename \"$device\"`"
dir="`basedirname \"$PCWD\" \"$file\"`"
file="`basename \"$file\"`"
# All chars except spaces and tabs seem to be handled correctly
# within the double quotes, so we just fix the spaces and tabs
# this leads to an error: sed: -e expression #1, char 26: invalid reference \1 on `s' command's RHS
# file="`echo \"$file\" | sed -r 's#\([\ \ ]\)#\\\\\1#g'`"
fi
props="`$SVN propget $FILE_PROP \"$dir/$file\"`"
if [ $? -eq 0 ]
then
#file is under svn control
# see if the properties have changed.
# if [ "`$SVN propget $FILE_PROP \"$dir/$file\"`" != "$info" ]
if [ "$props" != "$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"
echo "property $FILE_PROP not set for \"$dir/$file\""
else
# chown $user:$group "$dir/$file"
# chmod $mode "$dir/$file"
echo "changing owner/group mode on \"$dir/$file\""
chown $user:$group "$dir/$file"
chmod $mode "$dir/$file"
fi
fi
fi
else
#file is not under svn control
# echo -n "" is just a no-op allowing this else block to exist
echo -n ""
fi
done
}
function pre_checkin()
{
echo this is the pre checkin process
recorddirinfo -ci
# recordpermissions is called from recorddirinfo now
# 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
# recordpermissions is called from recorddirinfo now
}
CHDIR=false
DOSVN=true
case "$1" in
checkout|co) CHDIR=true; ACTION="post";;
commit|ci) ACTION="pre";;
import) ACTION="pre";;
prework|pre) DOSVN=false; ACTION="pre";;
switch|sw) ACTION="post";;
update|up) ACTION="post";;
*);;
esac
[ "$ACTION" = "pre" ] && pre_checkin $@
#$SVN $@
if [ "$DOSVN" = "true" ]
then
echo $SVN "$@"
$SVN "$@"
fi
[ $? = 0 -a "$ACTION" = "post" ] && post_checkout $@
cleanup
#
# vim: set ai ts=8 sw=4
#
Last modified: Sun Jan 25 14:36:54 CET 2009