[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: RFC: a list of all known properties

From: Ross Mark <rossm_at_controllingedge.com.au>
Date: 2004-03-18 04:25:49 CET

Jani Averbach wrote:

>Would you like to post it again as an attachment? That would be
>marvelous.
>
Done. The only difference between this version and the original is that
it doesn't generate an error message if a file didn't have a
file:permissions property (the actual error was from the chmod and chown
calls).

>
>Moreover, I am interested in hearing what kind of name you would
>choose for properties today if your hands were not bound?
>
If I was rewriting the script now I would put the properties under the
"asvn" name space, so it was obvious that were all associated with the
script. However if there our thoughts of using the same properties in
other tools then possibly a different name space may be more
appropriate, but then again I like the term Archive SVN so maybe asvn
isn't so bad :->

>
>Secondly, in the original thread, Sander was willing to put your
>script in /contrib, so this time that would even happen, if there aren't
>any concerns among other developers. =)
>
Since posting the script I've had a few inquiries from people wanting to
use it so I think there is some demand for something like this.

>
>BR, Jani
>
>
>
Ross

#!/bin/bash
#-------------------------------------------------------------------------
# Author: Ross Mark (rossm@controllingedge.com.au)
# Date: Tue Mar 11 10:02:57 EST 2003
#
# Document Id: 104.733.737
#
# Copyright (C) 2003 Controlling Edge Pty Ltd
#
#-------------------------------------------------------------------------
#
# 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.
#
# This script worked for me yesterday on my machine and I hope it will
# continue to work tomorrow. If you run this program then
# risk and blame are yours.
#
#-------------------------------------------------------------------------
SVN=/usr/local/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 $1
    PCWD="$PCWD/$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
#

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@subversion.tigris.org
For additional commands, e-mail: users-help@subversion.tigris.org
Received on Thu Mar 18 04:26:16 2004

This is an archived mail posted to the Subversion Users mailing list.