2 # Simple wrapper around svn featuring auto-ChangeLog entries and emailing.
4 # Copyright (C) 2006 Benoit Sigoure.
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
21 # Quick install: alias svn=path/to/svn-wrapper.sh -- that's all.
27 # This script is a wrapper around the svn command-line client for UNIX. It has
28 # been designed mainly to automagically generate GNU-style ChangeLog entries
29 # when committing and mail them along with a diff and an optional comment from
30 # the author to a list of persons or a mailing list. It has been made so that
31 # it's as much portable as possible and covers as many use-case as possible.
33 # HOWEVER, there will be bugs, there will be cases in which the script doesn't
34 # wrap properly the svn-cli, etc. In this case, you can try to mail me at
35 # <tsuna at lrde dot epita dot fr>. Include the revision of the svn-wrapper
36 # you're using, and full description of what's wrong etc. so I can reproduce
39 # If you feel like, you can try to fix/enhance the script yourself. It only
40 # requires some basic Shell-scripting skills. Knowing sed might help :)
46 # If you're simply looking for the usage, run svn-wrapper.sh help (or
47 # svn help if you aliased `svn' on svn-wrapper.sh) as usual.
49 # This script is (hopefully) portable, widely commented and self-contained. Do
50 # not hesitate to hack it. It might look rather long (because it does a lot of
51 # things :P) but you should be able to easily locate which part of the code
54 # The script begins by defining several functions. Then it really starts where
55 # the comment "# `main' starts here. #" is placed.
56 # Some svn commands are hooked (eg, svn st displays colors). Hooks and
57 # extra commands are defined in functions named svn_<command-name>.
63 # * Handle things such as svn ci --force foobar.
64 # * Automatic proxy configuration depending on the IP in ifconfig?
65 # * Customizable behavior/colors via some ~/.<config>rc file (?)
66 # * Improve Auto-Emailing (if possible in sh :|)
70 # Default values (the user can export them to override them).
79 # Override the locale.
83 # No args, invoke svn...
84 test $# -lt 1 && exec $SVN
88 # The `main' really starts after the functions definitions.
96 red
='\e[0;31m'; lred
='\e[1;31m'
97 green
='\e[0;32m'; lgreen
='\e[1;32m'
98 yellow
='\e[0;33m'; lyellow
='\e[1;33m'
99 blue
='\e[0;34m'; lblue
='\e[1;34m'
100 purple
='\e[0;35m'; lpurple
='\e[1;35m'
101 cyan
='\e[0;36m'; lcyan
='\e[1;36m'
102 grey
='\e[0;37m'; lgrey
='\e[1;37m'
103 white
='\e[0;38m'; lwhite
='\e[1;38m'
111 yellow
=''; lyellow
=''
113 purple
=''; lpurple
=''
123 echo "svn-wrapper: ${lred}abort${std}: $@" |
sed '1!s/^/ /' >&2
130 echo "svn-wrapper: ${lred}warning${std}: $@" |
sed '1!s/^/ /' >&2
136 echo "svn-wrapper: ${lyellow}notice${std}: $@" |
sed '1!s/^/ /' >&2
143 read answer ||
return 1
148 return 42 # should never happen...
154 warn
"cannot find the environment variable $1
155 You might consider using \`export $1 <FIXME>\`"
158 # get_unique_file_name file-name
159 get_unique_file_name
()
162 echo "$1" && return 0
165 while test -e "$gufn.$i"; do
171 # ensure_not_empty description value
174 ene_val
=`echo "$2" | tr -d ' \t\n'`
175 test x
"$ene_val" = x
&& abort
"$1: empty value"
181 my_url
='http://www.lrde.epita.fr/~sigoure/svn-wrapper'
182 # You can use https if you feel paranoiac.
184 echo ">>> Fetching svn-wrapper.sh from $my_url/svn-wrapper.sh"
186 # --------------------- #
187 # Fetch the new version #
188 # --------------------- #
190 tmp_me
=`get_unique_file_name "$TMPDIR/svn-wrapper.sh"`
191 if (wget
--help) >/dev
/null
2>/dev
/null
; then
192 wget
--no-check-certificate "$my_url/svn-wrapper.sh" -O "$tmp_me"
195 curl
--help >/dev
/null
2>/dev
/null
196 if [ $?
-gt 42 ]; then
197 abort
'Cannot find wget or curl.
198 How can I download any update without them?'
201 curl
--insecure "$my_url/svn-wrapper.sh" >"$tmp_me"
205 || abort
"Can't find the copy of myself I downloaded in $tmp_me"
207 # ---------------------------------------- #
208 # Compare versions and update if necessary #
209 # ---------------------------------------- #
211 tmp_ver
=`sed '/^# $Id[:].*$/!d;
214 s/[^0-9]*\([0-9][0-9]*\).*/\1/' "$tmp_me"`
215 my_ver
=`sed '/^# $Id[:].*$/!d;
218 s/[^0-9]*\([0-9][0-9]*\).*/\1/' "$0"`
219 if [ $my_ver -lt $tmp_ver ]; then # There IS an update...
220 echo "An update is available, r$tmp_ver (your version is r$my_ver)"
223 if yesno
"Do you want to see the ChangeLog between r$my_ver and r$tmp_ver?"
225 my_chlog
=`get_unique_file_name "$TMPDIR/ChangeLog"`
227 wget
) wget
--no-check-certificate "$my_url/ChangeLog" -O "$my_chlog"
229 curl
) curl
--insecure "$my_url/ChangeLog" >"$my_chlog"
231 *) abort
'Should never be here.'
234 sed "/^r$my_ver/q" "$my_chlog" |
$PAGER
238 # Wanna see the diff?
239 if yesno
"Do you want to see the diff between r$my_ver and r$tmp_ver?"
241 (diff -uw "$0" "$tmp_me" | diffstat
;
243 diff -uw "$0" "$tmp_me") |
$PAGER
247 if yesno
"Overwrite $0 (r$my_ver) with $tmp_me (r$tmp_ver)?"; then
249 mv "$tmp_me" "$0" && exit 0
253 elif [ $my_ver -gt $tmp_ver ]; then
254 echo "Wow, you're more up to date than the master copy :)"
255 echo "Your version is r$my_ver and the master copy is r$tmp_ver."
257 echo "You're already up to date [r$my_ver] :)"
262 # ------------------------------- #
263 # Hooks for standard SVN commands #
264 # ------------------------------- #
266 # svn_commit [args...]
267 # Here is how the commit process goes:
268 # First we look in the arguments passed to commit:
269 # If there are some files or paths, the user wants to commit these only. In
270 # this case, we must search for ChangeLogs from these paths. We might find
271 # more than one ChangeLog, in this case the user will be prompted to pick up
273 # Otherwise (no path passed in the command line) the user just wants to
274 # commit the current working directory.
275 # In any case, we schedule "ChangeLog" for commit.
276 # Alright now that we know which ChangeLog to use, we look in the ChangeLog's
277 # directory if there is a ",svn-log" file which would mean that a previous
278 # commit didn't finish successfully. If there is such a file, the user is
279 # prompted to know whether they want to resume that commit or simply start a
281 # When the user wants to resume a commit, the ",svn-log" file is loaded and we
282 # retrieve the value of "$@" that was saved in the file.
283 # Otherwise we build a template ChangeLog entry.
284 # Then we open the template ChangeLog entry with $EDITOR so that the user
286 # Finally, we commit.
291 # Check if the user passed some paths to commit explicitly
292 # because in this case we must add the ChangeLog to the commit and search
293 # the ChangeLog from the dirname of that file.
294 i
=0; search_from
=''; add_changelog
=no
295 while [ $i -lt $# ]; do
297 # If the argument is a valid path: add the ChangeLog in the list of
299 if test -e "$arg"; then
301 if test -d "$arg"; then
302 search_from_add
="$arg"
304 search_from_add
=`dirname "$arg"`
306 search_from
="$search_from:$search_from_add"
309 set dummy
"$@" "$arg"
313 if [ $add_changelog = no
]; then
314 # There is no path/file in the command line: the user wants to commit the
315 # current directory. Make it explicit now:
319 search_from
=`echo "$search_from" | sed 's/^://; s/^$/./'`
321 # ----------------- #
322 # Find ChangeLog(s) #
323 # ----------------- #
325 nb_chlogs
=0; change_log_dirs
=''
326 save_IFS
=$IFS; IFS
=':'
327 for dir
in $search_from; do
329 test -z "$dir" && dir
='.'
330 # First: come back to the original place
331 cd "$here" || abort
"Cannot cd to $here"
332 cd "$dir" ||
continue # Then: Enter $dir (which can be a relative path)
334 while [ $found -eq 0 ]; do
335 this_chlog_dir
=`pwd -P`
336 if [ -f .
/ChangeLog
]; then
338 nb_chlogs
=`expr $nb_chlogs + 1`
339 change_log_dirs
="$change_log_dirs:$this_chlog_dir"
343 # Stop searching when in / ... hmz :P
344 test x
"$this_chlog_dir" = x
/ && break
345 done # end while: did we find a ChangeLog
346 done # end for: find ChangeLogs in $search_from
347 change_log_dirs
=`echo "$change_log_dirs" | sed 's/^://'`
349 # Did we find a ChangeLog? More than one?
350 if [ $nb_chlogs -eq 0 ]; then
351 if yesno
'svn-wrapper: Error: Cannot find a ChangeLog file!
352 You might want to create an empty one (eg: `touch ChangeLog` where appropriate)
353 Do you want to proceed without using a ChangeLog?'; then
355 $SVN commit
"$@" && $SVN update
360 elif [ $nb_chlogs -gt 1 ]; then
361 notice
"$nb_chlogs ChangeLogs were found, pick up one:"
364 for a_chlog_dir
in $change_log_dirs; do
366 echo "$i. $a_chlog_dir/ChangeLog"
368 echo -n "Which ChangeLog do you want to use? [1-$i] "
369 read chlog_no || abort
"Cannot read answer on stdin."
372 *[^
0-9]*) abort
"Invalid ChangeLog number: $chlog_no"
374 test "$chlog_no" -le $i || abort
"Invalid ChangeLog number: $chlog_no
376 test "$chlog_no" -ge 1 || abort
"Invalid ChangeLog number: $chlog_no
378 change_log_dir
=`echo "$change_log_dirs" | tr ':' '\n' | sed "${chlog_no}!d"`
379 else # Only one ChangeLog found
380 change_log_dir
=$change_log_dirs
381 notice
"using $change_log_dir/ChangeLog"
384 test -f "$change_log_dir/ChangeLog" \
385 || abort
"No such file or directory: $change_log_dir/ChangeLog"
387 # Now we can safely schedule the ChangeLog for the commit.
388 set dummy
"$@" "$change_log_dir/ChangeLog"
391 # Warn for files that are not added in the repos.
392 conflicts
=`$SVN status | sed '/^\?......[^,+]/!d'`
393 if test x
"$conflicts" != x
; then
394 warn
"make sure you don't want to \`svn add'
395 any of the following files before committing:"
396 echo "$conflicts" |
sed "$sed_svn_st_color"
397 echo -n "Type [ENTER] to continue :)" && read chiche_is_gay
400 # Detect unresolved conflicts / missing files.
401 conflicts
=`$SVN status | sed '/^[C!]/!d'`
402 test x
"$conflicts" != x
&& abort
"there are unresolved conflicts (\`C')
403 and/or missing files (\`!'):
406 repos_root
=`$SVN info | sed '/^Repository Root/!d;s/^Repository Root: //'`
407 # It looks like svn <1.3 didn't display a "Repository Root" entry.
408 test x
"$repos_root" = x
&& \
409 repos_root
=`$SVN info | sed '/^URL/!d;s/^URL: //'`
415 REV
=`$SVN info "$repos_root" | sed '/^Revision: /!d;s///'`
416 test x
"$REV" = x
&& REV
=`$SVN info | sed '/^Last Changed Rev: /!d;s///'`
417 test x
"$REV" = x
&& warn
'Cannot detect the current revision.'
420 # VCS-compat: handle user option 'new_user'
422 grep '^new_user: false' ~
/.vcs
>/dev
/null
2>/dev
/null
&& new_user
='no'
424 tmp_log
="$change_log_dir/,svn-log"
425 if [ -f "$tmp_log" ] && yesno
"It looks like the last commit did not\
426 terminate successfully.
427 Would you like to resume it?"; then
429 internal_tags
=`sed '/^--- Internal stuff, DO NOT change please ---$/,$!d' \
431 saved_args
=`echo "$internal_tags" | sed '/^args: */!d;s///'`
432 if [ x
"$saved_args" != x
]; then
433 if [ x
"$@" != x
] && [ x
"$saved_args" != x
"$@" ]; then
434 warn
"overriding arguments:
435 you invoked $0 with the following arguments: $@
436 they have been replaced by these: $saved_args"
437 set dummy
"$saved_args"
440 notice
"setting the following arguments: $saved_args"
441 set dummy
"$saved_args"
445 # Ignore white spaces. Can't svn diff -x -w: svn 1.4 only.
446 svn_diff
=`$SVN diff --diff-cmd diff -x -uw "$@"`
447 test x
"$svn_diff" = x
&& svn_diff
=`$SVN diff "$@"`
448 svn_diff_stat
=`echo "$svn_diff" | diffstat`
450 else # Build the template message.
452 # ------------------------------------ #
453 # Gather info for the template message #
454 # ------------------------------------ #
456 projname
=`$SVN propget project "$change_log_dir"`
457 # Try to be VCS-compatible and find a project name in a *.rb.
458 if [ x
"$projname" = x
] && [ -d "$change_log_dir/vcs" ]; then
459 projname
=`sed '/common_commit/!d;s/.*"\(.*\)<%= rev.*/\1/' \
460 "$change_log_dir"/vcs/*.rb`
461 test x
"$projname" != x
&& test x
$new_user = xyes \
462 && notice
"VCS-compat: found project name: $projname
463 in " "$change_log_dir"/vcs
/*.rb
465 test x
"$projname" != x
&& projname
=`echo "$projname" | sed '/[^ ]$/s/$/ /'`
467 mailto
=`$SVN propget mailto "$change_log_dir"`
468 if [ x
"$mailto" = x
]; then
469 test x
$new_user = xyes \
470 && warn
"no svn property mailto found in $change_log_dir
471 You might want to set default email adresses using:
472 svn propset mailto 'somebody@mail.com, foobar@example.com'\
474 # Try to be VCS-compatible and find a list of mails in a *.rb.
475 if [ -d "$change_log_dir/vcs" ]; then
476 mailto
=`grep '.@.*\..*' "$change_log_dir"/vcs/*.rb \
478 | sed 's/^.*[["]\([^["]*@[^]"]*\)[]"].*$/\1/' | xargs`
479 test x
"$mailto" != x
&& test x
$new_user = xyes \
480 && notice
"VCS-compat: found mailto: $mailto
481 in " "$change_log_dir"/vcs
/*.rb
483 fi # end guess mailto
485 # Ensure that emails are comma-separated.
486 mailto
=`echo "$mailto" | sed 's/[ ;]/,/g' | tr -s ',' | sed 's/,/, /g'`
487 test x
"$FULLNAME" = x
&& FULLNAME
='Type Your Name Here' \
489 test x
"$EMAIL" = x
&& EMAIL
='your.mail.here@FIXME.com' && warn_env EMAIL
491 # --ignore-externals appeared after svn 1.1.1
492 my_svn_st
=`$SVN status --ignore-externals "$@" \
493 || $SVN status "$@" | sed '/^Performing status on external/ {
497 # Files to put in the ChangeLog entry.
498 change_log_files
=`echo "$my_svn_st" | sed '
501 s/^M......\(.*\)$/ * \1: ./; t
502 s/^A......\(.*\)$/ * \1: New./; t
503 s/^D......\(.*\)$/ * \1: Remove./; t
506 if [ x
"$change_log_files" = x
]; then
507 yesno
'Nothing to commit, continue anyway?' ||
return 1
510 svn_diff
=`$SVN diff --diff-cmd diff -x -uw "$@"`
511 svn_diff_stat
=`echo "$svn_diff" | diffstat`
513 # Get any older svn-log out of the way.
514 test -f "$tmp_log" && mv "$tmp_log" `get_unique_file_name "$tmp_log"`
515 # If we can't get an older svn-log out of the way, find a new name...
516 test -f "$tmp_log" && tmp_log
=`get_unique_file_name "$tmp_log"`
517 if [ x
$new_user = no
]; then
518 commit_instructions
='
520 - Fill the ChangeLog entry.
521 - If you feel like, write a comment in the "Comment:" section.
522 This comment will only appear in the email, not in the ChangeLog.
523 By default only the location of the repository is in the comment.
524 - Some tags will be replaced. Tags are of the form: <TAG>. Unknown
525 tags will be left unchanged.
526 - Your ChangeLog entry will be used as commit message for svn.'
528 commit_instructions
=''
531 --You must fill this file correctly to continue-- -*- vcs -*-
533 Subject: ${projname}r<REV>: <TITLE>
534 From: $FULLNAME <$EMAIL>
538 Repository: $repos_root
542 <YYYY>-<MM>-<DD> $FULLNAME <$EMAIL>
547 --This line, and those below, will be ignored--
549 --Preview of the message that will be sent--
551 Repository: $repos_root
553 Your comments (if any) will appear here.
556 $YYYY-$MM-$DD $FULLNAME <$EMAIL>
558 Your ChangeLog entry will appear here.
562 $svn_diff" >"$tmp_log"
565 --- Internal stuff, DO NOT change please ---
566 args: $@" >>"$tmp_log"
568 fi # end: if svn-log; then resume? else create template
571 # ------------------ #
572 # Re-"parse" the log #
573 # ------------------ #
575 # hmz this section is a bit messy...
576 sed_escape
='s/@/\\@/' # helper string... !@#$%* escaping \\\\\\...
577 sed_eval_tags
="s/<MM>/$MM/g; s/<DD>/$DD/g; s/<YYYY>/$YYYY/g; s/<REV>/$REV/g"
578 full_log
=`sed '/^--This line, and those below, will be ignored--$/,$d;
579 /^--You must fill this/d' "$tmp_log"`
580 chlog_entry
=`echo "$full_log" | sed '/^ChangeLog:$/,$!d; //d'`
581 ensure_not_empty
'ChangeLog entry' "$chlog_entry"
582 full_log
=`echo "$full_log" | sed '/^ChangeLog:$/,$d'`
583 mail_comment
=`echo "$full_log" | sed '/^Comment:$/,$!d; //d'`
584 full_log
=`echo "$full_log" | sed '/^Comment:$/,$d'`
585 mail_title
=`echo "$full_log" | sed '/^Title: */!d;s///'`
586 ensure_not_empty
'commit title' "$mail_title"
587 mail_title
=`echo "$mail_title" | sed "$sed_eval_tags; $sed_escape"`
588 sed_eval_tags
="$sed_eval_tags; s@<TITLE>@$mail_title@g"
589 mail_comment
=`echo "$mail_comment" | sed "$sed_eval_tags"`
590 chlog_entry
=`echo "$chlog_entry" | sed "$sed_eval_tags; 1{
593 mail_subject
=`echo "$full_log" | sed '/^Subject: */!d;s///'`
594 ensure_not_empty
'mail subject' "$mail_subject"
595 mail_subject
=`echo "$mail_subject" | sed "$sed_eval_tags"`
596 mail_to
=`echo "$full_log" | sed '/^To:/!d'`
598 if test x
"$mail_to" = x
; then
601 mail_to
=`echo "$mail_to" | sed 's/^To: *//'`
602 ensure_not_empty
'"To:" field of the mail' "$mail_to"
604 mail_from
=`echo "$full_log" | sed '/^From: */!d;s///'`
605 ensure_not_empty
'"From:" field of the mail' "$mail_from"
607 # Check whether the user passed -m | --message
609 while [ $i -lt $# ]; do
611 # This is not really a reliable way of knowing whether -m | --message was
612 # passed but hum... Let's assume it'll do :s
613 test x
"$arg" = 'x-m' && has_message
=1
614 test x
"$arg" = 'x--message' && has_message
=1
616 set dummy
"$@" "$arg"
620 if [ $has_message -eq 0 ]; then
621 my_message
=`echo "$chlog_entry" | grep -v "^$YYYY-$MM-$DD" | sed '1,2 {
624 set dummy
--message "$my_message" "$@"
627 notice
'you are overriding the commit message.'
631 yesno
'Are you sure you want to commit?' ||
return 1
633 # Add the ChangeLog entry
634 old_chlog
=`get_unique_file_name "$change_log_dir/ChangeLog.old"`
635 mv "$change_log_dir/ChangeLog" "$old_chlog" || \
636 abort
'Could not backup ChangeLog'
637 trap "echo SIGINT; mv \"$old_chlog\" \"$change_log_dir/ChangeLog\"" SIGINT
638 echo "$chlog_entry" >"$change_log_dir/ChangeLog"
639 echo >>"$change_log_dir/ChangeLog"
640 cat "$old_chlog" >>"$change_log_dir/ChangeLog"
642 # --Commit-- finally! :D
643 $SVN commit
"$@" ||
{
645 mv "$old_chlog" "$change_log_dir/ChangeLog"
646 abort
"Commit failed, $SVN returned $svn_commit_rv"
649 # In the end, perform an svn up to update externals
650 $SVN update
"$change_log_dir"
654 if test x
$send_a_mail = xyes
; then
655 mail_file
=`get_unique_file_name "$change_log_dir/+mail"`
659 Subject: $mail_subject
668 $svn_diff" |
sed 's/^\.$/ ./' >"$mail_file"
669 # We change lines with only a `.' because they could mean "end-of-mail"
671 trap 'echo SIGINT; exec < /dev/null' SIGINT
672 test -f ~
/.signature
&& echo '-- ' >>"$mail_file" && \
673 cat ~
/.signature
>>"$mail_file"
674 # VCS-compat: handle user option 'sign'.
675 if (grep '^sign: false' ~
/.vcs
) >/dev
/null
2>/dev
/null
; then :; else
676 ($GPG -h) >/dev
/null
2>/dev
/null
678 if [ -e ~
/.gnupg
] ||
[ -e ~
/.gpg
] ||
[ -e ~
/.pgp
] && [ $gpg_rv -lt 42 ]
680 yesno
"Sign the mail using $GPG ?" && \
681 $GPG --clearsign "$mail_file"
682 test -f "$mail_file.asc" && mv "$mail_file.asc" "$mail_file"
685 # FIXME: This looks a bit fragile. What if $mail_to = -s foo ? Will it
686 # override the previous -s option?
687 cat "$mail_file" |
mail -s "$mail_subject" "$mail_to"
688 fi # end do we have to send a mail?
691 save_mail_file
=`echo "$mail_file" | sed 's/+//'`
692 if [ -d "$change_log_dir/vcs" ] \
693 ||
[ -d "$change_log_dir/+committed" ]
695 mkdir
-p "$change_log_dir/+committed/$REV" \
696 && mv "$mail_file" "$change_log_dir/+committed/$REV/mail"
698 return $svn_commit_rv
704 echo "Using svn-wrapper v$version (C) SIGOURE Benoit [GPL]"
705 sed '/^# $Id[:].*$/!d;s/.*$Id[:] *//;s/ *$ *//;s/ \([0-9][0-9]*\)/ (r\1)/' "$0"
708 # has_prop prop-name [path]
709 # return value: 0 -> path has the property prop-name set.
710 # 1 -> path has no property prop-name.
714 hp_plist
=`$SVN proplist "$2"`
715 test $?
-ne 0 && return 2
716 hp_res
=`echo "$hp_plist" | sed "/^ *$1\$/!d"`
717 test x
"$hp_res" = x
&& return 1
721 # svn_propadd prop-name prop-val [path]
725 && abort
'Not enough arguments provided; try `svn help propadd` for more info'
727 && abort
'Too many arguments provided; try `svn help propadd` for more info'
730 test x
"$path" = x
&& path
='.' && set dummy
"$@" '.' && shift
731 has_prop
"$1" "$3" ||
{
732 test $?
-eq 2 && return 1 # svn error
734 yesno
"'$path' has no property named '$1', do you want to add it?" \
739 current_prop_val
=`$SVN propget "$1" "$3"`
740 test $?
-ne 0 && abort
"Failed to get the current value of property '$1'."
742 $SVN propset
"$1" "$current_prop_val
743 $2" "$3" >/dev
/null || abort
"Failed to add '$3' in the property '$1'."
745 current_prop_val
=`$SVN propget "$1" "$3" || echo "$current_prop_val
747 echo "property '$1' updated on '$path', new value:
751 # svn_propsed prop-name sed-script [path]
755 && abort
'Not enough arguments provided; try `svn help propsed` for more info'
757 && abort
'Too many arguments provided; try `svn help propsed` for more info'
760 test x
"$path" = x
&& path
='.'
761 has_prop
"$1" "$3" ||
{
762 test $?
-eq 2 && return 1 # svn error
764 abort
"'$path' has no property named '$1'."
767 prop_val
=`$SVN propget "$1" "$3"`
768 test $?
-ne 0 && abort
"Failed to get the current value of property '$1'."
770 prop_val
=`echo "$prop_val" | sed "$2"`
771 test $?
-ne 0 && abort
"Failed to run the sed script '$2'."
773 $SVN propset
"$1" "$prop_val" "$3" >/dev
/null \
774 || abort
"Failed to update the property '$1' with value '$prop_val'."
776 new_prop_val
=`$SVN propget "$1" "$3" || echo "$prop_val"`
777 echo "property '$1' updated on '$path', new value:
784 if [ $# -eq 0 ]; then # Simply display ignore-list.
785 $SVN propget
'svn:ignore'
786 elif [ $# -eq 1 ]; then
787 if [ -d "$1" ]; then # Display ignore-list for $1
788 $SVN propget
'svn:ignore' "$1"
789 elif [ x
`dirname "$1"` != x.
]; then
790 # Add `basename $1` in the ignore-list of `dirname $1`
791 i_basename
=`basename "$1"`
792 i_dirname
=`dirname "$1"`
793 svn_propadd
'svn:ignore' "$i_basename" "$i_dirname"
794 else # Add $1 in ignore-list of `.'.
795 svn_propadd
'svn:ignore' "$1"
797 else # Add arguments in svn:ignore.
799 while [ $i -lt $# ]; do
802 if [ $i -eq $# ]; then
805 set dummy
"$@" "$arg"
810 i_files
=`echo "$*" | tr -s ' ' '\n'`
811 if [ -d "$last_arg" ]; then # Add in ignore-list of $last_arg
812 svn_propadd
'svn:ignore' "$i_files" "$last_arg"
813 else # Add in ignore-list of `.'
814 svn_propadd
'svn:ignore' "$i_files
823 if [ $# -eq 0 ]; then
828 Additionnal commands provided by svn-wrapper:
841 echo 'diffstat (ds): Display the histogram from svn diff-output.'
842 $SVN help diff |
sed '1d;
843 s/differences*/histogram/;
844 2,35 s/diff/diffstat/g'
847 echo 'ignore: Add some files in the svn:ignore property.
848 usage: 1. ignore [PATH]
849 2. ignore FILE [FILES...] [PATH]
851 1. Display the value of svn:ignore property on [PATH].
852 2. Add some files in the svn:ignore property of [PATH].
854 If you want to add directories in the ignore-list, be careful:
856 will add "foo/" in the property svn:ignore within the directory bar!
858 svn ignore foo/ bar/ .
859 (It'\''s somewhat like with mv)
865 echo 'propadd (padd, pa): Add something in the value of a property.
866 usage: propadd PROPNAME PROPVAL PATH
868 PROPVAL will be appended at the end of the property PROPNAME.
874 echo 'propsed (psed): Edit a property with sed.
875 usage: propsed PROPNAME SED-ARGS PATH
877 eg: svn propsed svn:externals "s/http/https/" .
883 echo 'revision (rev): Display the revision number of a local or remote item.'
884 $SVN help info |
sed '1d;
885 s/information/revision/g;
886 s/revision about/the revision of/g;
887 2,35 s/info/revision/g;
891 echo 'touch: Touch a file and svn add it.
892 usage: touch FILE [FILES]...
897 selfupdate | selfup | self-update | self-up
)
898 echo 'selfupdate (selfup): Attempt to update svn-wrapper.sh
905 echo 'version: Display the version info of svn and svn-wrapper.
916 # svn_status [args...]
919 svn_status_out
=`$SVN status "$@"`
921 echo "$svn_status_out" |
sed "$sed_svn_st_color"
922 return $svn_status_rv
925 # svn_update [args...]
928 svn_update_out
=`$SVN update "$@"`
930 echo "$svn_update_out" |
sed "$sed_svn_up_colors"
931 return $svn_update_rv
934 # ------------------- #
935 # `main' starts here. #
936 # ------------------- #
939 if (echo | diffstat
) >/dev
/null
2>/dev
/null
; then :; else
940 warn
'diffstat is not installed on your system or not in your PATH.'
941 test -f /etc
/debian_version \
942 && notice
'you might want to `apt-get install diffstat`.'
944 if (echo |
mail) >/dev
/null
2>/dev
/null
; then :; else
945 if [ $?
-gt 100 ]; then
946 warn
'mail is not installed on your system or not in your PATH.'
947 test -f /etc
/debian_version \
948 && notice
'you might want to:
949 # apt-get install mailx
950 # dpkg-reconfigure exim'
954 # Define colors if stdout is a tty.
957 else # stdout isn't a tty => don't print colors.
961 # Considere this as a sed function :P.
965 s@^?\\(......\\)\\([^+]*/\\)*+@+\\1\\2+@
966 s@^?\\(......\\)\\([^,]*/\\)*,@,\\1\\2,@
967 s/^\\(.\\)C/\\1${lred}C${std}/
970 s/^?/${lred}?${std}/; t
971 s/^M/${lgreen}M${std}/; t
972 s/^A/${lgreen}A${std}/; t
973 s/^X/${lblue}X${std}/; t
974 s/^+/${lyellow}+${std}/; t
975 s/^D/${lyellow}D${std}/; t
976 s/^,/${lred},${std}/; t
977 s/^C/${lred}C${std}/; t
978 s/^I/${purple}I${std}/; t
979 s/^R/${lblue}R${std}/; t
980 s/^!/${lred}!${std}/; t
981 s/^~/${lwhite}~${std}/; t"
990 s/^\\(.\\)C/\\1${lred}C${std}/
991 s/^\\(.\\)U/\\1${lgreen}U${std}/
992 s/^\\(.\\)D/\\1${lred}D${std}/
995 s/^A/${lgreen}A${std}/; t
996 s/^U/${lgreen}U${std}/; t
997 s/^D/${lyellow}D${std}/; t
998 s/^G/${purple}G${std}/; t
999 s/^C/${lred}C${std}/; t"
1002 test "x$1" = x--debug
&& shift && set -x
1005 # ------------------------------- #
1006 # Hooks for standard SVN commands #
1007 # ------------------------------- #
1024 # -------------------- #
1025 # Custom SVN commands #
1026 # -------------------- #
1029 $SVN diff "$@" | diffstat
1035 propadd | padd | pa
)
1045 $SVN info
"$@" |
sed '/^Revision/!d;s/^Revision: //'
1049 touch "$@" && svn add
"$@"
1051 selfupdate | selfup | self-update | self-up
)
1055 version |
-version |
--version)
1057 set dummy
'--version' "$@"