svnsvn-hookssvnlook

How to use svnlook in svn to search changed items


Could you please guide me on how svnlook is working below. I want to include this in a hook to prevent commits in a tag under /tags:

$SVNLOOK changed -t "$TXN" "$REPOS" | grep "^U\W.*\/tags\/" && /bin/echo "Cannot commit to tags!" 1>&2 && exit 1

Solution

  • A Subversion pre-commit hook will allow a commit to go through if it returns an exit code of 0. Otherwise, the pre-commit hook will fail and the commit will not be processed.

    The $SVNLOOK changed -t "$TXN" "$REPOS" will show the changes in $REPOS that took place in $TXN. The shell variables must be set by you. If you look at the pre-commit script that comes with Subversion, you'll see:

    $TXN=$1
    $REPO=$2
    

    The output of the svnlook changed command looks like this:

    $ $SVNLOOK changed -t $TXN $REPOS
    A   trunk/vendors/deli/
    A   trunk/vendors/deli/chips.txt
    A   trunk/vendors/deli/sandwich.txt
    A   trunk/vendors/deli/pickle.txt
    U   trunk/vendors/baker/bagel.txt
    _U  trunk/vendors/baker/croissant.txt
    UU  trunk/vendors/baker/pretzel.txt
    D   trunk/vendors/baker/baguette.txt
    

    The first column is whether something was Uprated, Added, or Deleted. The second column refers to attributes.

    The rest is the name of the file that was acted upon. You can see that baguette.txt was deleted, and that a property on croissant.txt was changed, but the file itself wasn't updated.

    Let's say someone tried to change a tag. The output of svnlook changed will look like this:

    $SVNLOOK changed -t $TXN $REPOS
    U   tags/4.2.1/vendors/baker/bagel.txt
    

    The grep command is this:

    grep "^U\W.*\/tags\/" 
    

    This is looking for a line that starts with ^U meaning it was an update. Then, it looks for a string that begins with /tags. Hmmm... that might be an issue. It doesn't match the output of the svnlook changed command.

    Maybe it should be:

    grep -q "^U.[[\s+tags/"
    

    This will match any string that starts with U, possibly followed by another character, followed by whitespace, and then immediately the word tags/.

    You might want to verify that expression.

    The && is a list operator. If the expression on the left side of && executes successfully (i.e. it returns a zero exit code), the statement on the right side will be executed. Otherwise, the statement on the right won't be executed.

    Thus, if your grep matches a pattern that looks like someone updated a tag, it will be true. The statement on the right side of the && will be executed.

    Thus,

    /bin/echo "Cannot commit to tags!" 1>&2
    

    will be executed. This is sent to STDERR which will be sent to the Subversion client, but only if the exit code of the pre-commit hook is zero.

    Thus the next list operator command exit 1 will execute if the /bin/echo is successful. (It might not be, but usually will be). With that, the pre-commit hook exits with a non-zero exit code, the hook fails, and the Cannot commit to tags! will be sent to the SVN client for the user to see.

    There is absolutely no reason in the world for this statement to look like this. This is almost equivalent, and is easier to understand:

    if $SVNLOOK changed -t $TXN $REPOS | grep -q "^U.[[\s+tags/"
    then
        /bin/echo "Cannot commit to tags!" 1>&2"
        exit 1
    fi
    exit 0
    

    After all, you need to put this in a shell script called pre-commit anyway and have the shell variables $SVNLOOK, $REPOS, and $TXN set anyway.

    The reason this isn't quite equivalent is that this will fail the commit even if the /bin/echo fails.


    If you are looking for a pre-commit hook to control tags, you should take a look at mine. This has been tested on hundreds of site, and will give you a lot more control over your repository and does better error checking.

    This hook uses a control file to control access to the repository. For example, you might want to be able to let yourself change tags if necessary.

    [file You are allowed to create a new tag, but you may not make any changes to it]
    file = /tags/**
    access = read-only
    users = @ALL
    
    [file You are allowed to create a new tag, but you may not make any changes to it]
    file = /tags/
    access = add-only
    users = @ALL
    
    [file I can modify and delete tags]
    file = /tags/**
    access = read-write
    users = jazzr
    

    Take a look at the hook. It works with the standard Perl 5.8.8 installation and up. It requires no other modules. However, if you use LDAP or Active Directory for Subversion access control, you can install the Net::LDAP Perl module and use LDAP or Active Directory groups in your pre-commit hook for access control.