Question

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
Was it helpful?

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.

OTHER TIPS

$SVNLOOK changed -t "$TXN" "$REPOS" prints all the paths that were changed. See here for more info on the parameters: http://svnbook.red-bean.com/en/1.7/svn.ref.svnlook.c.changed.html

Next, the list of changed paths are being piped into grep which is checking if anything has changed that has "/tags/" in the path, | grep "^U\W.*\/tags\/".

The && is an AND operator in the shell. If the grep command finds a match, it returns status code 0 which in this case is equivalent to "TRUE" and the command after the && will execute, otherwise it will not.

The part after the &&, /bin/echo "Cannot commit to tags!" 1>&2 runs the echo command and writes the message from 1 (standard out) to &2 which is the standard error. If this succeeds, the command exits with a status code 1 exit 1. This will cause your hook script to return with a non-zero status code and your commit will fail returning the "Cannot commit to tags!" message to the user.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top