Recently I had to commit a bunch of changes via SVN. Cause it's
really recommended to review all changes made in the working
directory before actually committing the data, I issued a
svn status | grep ^M to see all files that have been
modified since the last commit. The result was a fairly long list
of files and I wanted to check which changes where actually made to
each individual file. Of course, every SVN user knows about svn
diff or even better svn diff |
less, which gives a complete diff of all modified
files.
However, I don't really like this output...it just glues diff after
diff together an if you scroll too fast, you will miss one or more
small but important changes. That's why i wanted to have a
mechanism, that shows one diffed file at a time until I explicitly
proceed to the next file. My first approach was a simple
one-liner:
svn status | grep ^M | awk '{print $2}' | while read l; do echo "****** $l ******"; svn diff "$l" ; read tmp; doneAs you will notice, this doesn't really work - the two read commands take turns in reading the output of svn status. One elegant solution for this includes the use of the shell builtin exec:
#!/bin/bash exec 3<&0 svn status | grep ^M | awk '{print $2}' | while read l; do echo "****** $l ******"; svn diff "$l" | less ; read tmp <&3 ;doneThe line following the shebang creates a copy of the current stdin (filehandle 0) and assigns it to a new filehandle 3 i.e. 0 and 3 both will read commands from the keyboard being the default in a newly created shell. In the next line filehandle 0 is redirected several times (remember: a | b redirects the stdout of a into the stdin of b), so the first read reads its lines from the awk command. The second read, however, reads its input from filehandle 3 which still has the value that filehandle 0 had in the beginning of the script, i.e. it reads the keyboard input (I also piped svn diff through less, but that's just a small enhancement which is unrelated to the main problem).