Issue a "Type [ENTER] to continue" when some files are not under version
[svn-wrapper.git] / svn-wrapper.sh
blob5e48622fc11d728094df300f40f221ce30a05ead
1 #! /bin/sh
2 # Simple wrapper around svn featuring auto-ChangeLog entries and emailing.
3 # $Id$
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,
19 # USA.
21 # Quick install: alias svn=path/to/svn-wrapper.sh -- that's all.
23 # ------ #
24 # README #
25 # ------ #
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
37 # your problem.
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 :)
42 # ------------- #
43 # DOCUMENTATION #
44 # ------------- #
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
52 # you're looking for.
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>.
59 # ---- #
60 # TODO #
61 # ---- #
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).
71 : ${SVN=svn}
72 : ${EDITOR=vi}
73 export EDITOR
74 : ${GPG=gpg}
75 : ${TMPDIR=/tmp}
76 export TMPDIR
77 : ${PAGER=more}
78 export PAGER
79 # Override the locale.
80 LC_ALL='C'
81 export LC_ALL
83 # No args, invoke svn...
84 test $# -lt 1 && exec $SVN
86 version='0.2'
88 # The `main' really starts after the functions definitions.
90 # ---------------- #
91 # Helper functions #
92 # ---------------- #
94 set_colors()
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'
104 std='\e[m'
107 set_nocolors()
109 red=''; lred=''
110 green=''; lgreen=''
111 yellow=''; lyellow=''
112 blue=''; lblue=''
113 purple=''; lpurple=''
114 cyan=''; lcyan=''
115 grey=''; lgrey=''
116 white=''; lwhite=''
117 std=''
120 # abort err-msg
121 abort()
123 echo "svn-wrapper: ${lred}abort${std}: $@" | sed '1!s/^/ /' >&2
124 exit 1
127 # warn msg
128 warn()
130 echo "svn-wrapper: ${lred}warning${std}: $@" | sed '1!s/^/ /' >&2
133 # notice msg
134 notice()
136 echo "svn-wrapper: ${lyellow}notice${std}: $@" | sed '1!s/^/ /' >&2
139 # yesno question
140 yesno()
142 echo -n "$@ [y/N] "
143 read answer || return 1
144 case "$answer" in
145 y* | Y*) return 0;;
146 *) return 1;;
147 esac
148 return 42 # should never happen...
151 # warn_env env-var
152 warn_env()
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()
161 test -f "$1" || {
162 echo "$1" && return 0
164 gufn="$1"; i=1
165 while test -f "$gufn.$i"; do
166 i=`expr $i + 1`
167 done
168 echo "$gufn.$i"
171 # ensure_not_empty description value
172 ensure_not_empty()
174 ene_val=`echo "$2" | tr -d ' \t\n'`
175 test x"$ene_val" = x && abort "$1: empty value"
178 # selfupdate
179 selfupdate()
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"
193 my_wget='wget'
194 else
195 curl --help >/dev/null 2>/dev/null
196 my_wget='curl'
197 if [ $? -gt 42 ]; then
198 abort 'Cannot find wget or curl.
199 How can I download any update without them?'
201 curl --insecure "$my_url/svn-wrapper.sh" >"$tmp_me"
204 test -r $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;
212 s/.*$Id[:] *//;
213 s/ *$ *//;
214 s/[^0-9]*\([0-9][0-9]*\).*/\1/' "$tmp_me"`
215 my_ver=`sed '/^# $Id[:].*$/!d;
216 s/.*$Id[:] *//;
217 s/ *$ *//;
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)"
222 # See the ChangeLog?
223 if yesno "Do you want to see the ChangeLog between r$my_ver and r$tmp_ver?"
224 then
225 my_chlog=`get_unique_file_name "$TMPDIR/ChangeLog"`
226 case $my_wget in
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.'
233 esac
234 sed "/^r$my_ver/q" "$my_chlog" | $PAGER
235 rm -f "$my_chlog"
238 # Wanna see the diff?
239 if yesno "Do you want to see the diff between r$my_ver and r$tmp_ver?"
240 then
241 (diff -uw "$0" "$tmp_me" | diffstat;
242 echo
243 diff -uw "$0" "$tmp_me") | $PAGER
246 # Let's go :)
247 if yesno "Overwrite $0 (r$my_ver) with $tmp_me (r$tmp_ver)?"; then
248 chmod a+x "$tmp_me"
249 mv "$tmp_me" "$0" && exit 0
251 rm -f "$tmp_me"
252 return 1
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."
256 else
257 echo "You're already up to date [r$my_ver] :)"
259 rm -f "$tmp_me"
262 # ------------------------------- #
263 # Hooks for standard SVN commands #
264 # ------------------------------- #
266 # svn_commit [args...]
267 svn_commit()
269 # -------------- #
270 # Find ChangeLog #
271 # -------------- #
273 here=`pwd`; found=0; change_log_files=''
274 while [ $found -eq 0 ]; do
275 if [ -f ./ChangeLog ]; then
276 found=1
277 else
278 cd ..
280 change_log_dir=`pwd -P`
281 # Stop searching when in / ... hmz :P
282 test x"$change_log_dir" = x/ && {
283 if yesno 'svn-wrapper: Error: Cannot find a ChangeLog file!
284 You might want to create an empty one (eg: `touch ChangeLog` where appropriate)
285 Do you want to proceed without using a ChangeLog?'; then
286 cd "$here"
287 $SVN commit "$@" && $SVN update
288 return $?
289 else
290 return 1
293 done
294 notice "using $change_log_dir/ChangeLog"
296 # Warn for files that are not added in the repos.
297 conflicts=`$SVN status | sed '/^\?/!d'`
298 if test x"$conflicts" != x; then
299 warn "make sure you don't want to \`svn add'
300 any of the following files before committing:"
301 echo "$conflicts" | sed "$sed_svn_st_color"
302 echo -n "Type [ENTER] to continue :)" && read chiche_is_gay
305 # Detect unresolved conflicts / missing files.
306 conflicts=`$SVN status | sed '/^[C!]/!d'`
307 test x"$conflicts" != x && abort "there are unresolved conflicts (\`C')
308 and/or missing files (\`!'):
309 $conflicts"
311 $SVN update || abort 'svn update failed.'
313 YYYY=`date '+%Y'`
314 MM=`date '+%m'`
315 DD=`date '+%d'`
316 REV=`$SVN info | sed '/^Revision: /!d;s///'`
317 test x"$REV" = x && REV=`$SVN info | sed '/^Last Changed Rev: /!d;s///'`
318 test x"$REV" = x && warn 'Cannot detect the current revision.'
319 REV=`expr $REV + 1`
321 tmp_log="$change_log_dir/,svn-log"
322 if [ -f "$tmp_log" ] && yesno "It looks like the last commit did not\
323 terminate successfully.
324 Would you like to resume it?"; then
325 echo 'Resuming ...'
326 internal_tags=`sed '/^--- Internal stuff, DO NOT change please ---$/,$!d' \
327 "$tmp_log"`
328 saved_args=`echo "$internal_tags" | sed '/^args: */!d;s///'`
329 if [ x"$saved_args" != x ]; then
330 if [ x"$@" != x ] && [ x"$saved_args" != x"$@" ]; then
331 warn "overriding arguments:
332 you invoked $0 with the following arguments: $@
333 they have been replaced by these: $saved_args"
334 set dummy "$saved_args"
335 shift
336 else
337 notice "setting the following arguments: $saved_args"
338 set dummy "$saved_args"
339 shift
342 # Ignore white spaces. Can't svn diff -x -w: svn 1.4 only.
343 svn_diff=`$SVN diff --diff-cmd diff -x -uw "$@"`
344 test x"$svn_diff" = x && svn_diff=`$SVN diff "$@"`
345 svn_diff_stat=`echo "$svn_diff" | diffstat`
347 else # Build the template message.
349 # ------------------------------------ #
350 # Gather info for the template message #
351 # ------------------------------------ #
353 repos_root=`$SVN info | sed '/^Repository Root/!d;s/^Repository Root: //'`
354 # It looks like svn <1.3 didn't display a "Repository Root" entry.
355 test x"$repos_root" = x && \
356 repos_root=`$SVN info | sed '/^URL/!d;s/^URL: //'`
357 cd "$here"
359 projname=`$SVN propget project "$change_log_dir"`
360 # Try to be VCS-compatible and find a project name in a *.rb.
361 if [ x"$projname" = x ] && [ -d "$change_log_dir/vcs" ]; then
362 projname=`sed '/common_commit/!d;s/.*"\(.*\)<%= rev.*/\1/' \
363 "$change_log_dir"/vcs/*.rb`
364 test x"$projname" != x \
365 && notice "VCS-compat: found project name: $projname
366 in " "$change_log_dir"/vcs/*.rb
368 test x"$projname" != x && projname=`echo "$projname" | sed '/[^ ]$/s/$/ /'`
370 mailto=`$SVN propget mailto "$change_log_dir"`
371 if [ x"$mailto" = x ]; then
372 warn "no svn property mailto found in $change_log_dir
373 You might want to set default email adresses using:
374 svn propset mailto 'somebody@mail.com, foobar@example.com'\
375 $change_log_dir" >&2
376 # Try to be VCS-compatible and find a list of mails in a *.rb.
377 if [ -d "$change_log_dir/vcs" ]; then
378 mailto=`grep '.@.*\..*' "$change_log_dir"/vcs/*.rb \
379 | tr '\n' ' ' \
380 | sed 's/^.*[["]\([^["]*@[^]"]*\)[]"].*$/\1/' | xargs`
381 test x"$mailto" != x && notice "VCS-compat: found mailto: $mailto
382 in " "$change_log_dir"/vcs/*.rb
383 fi # end VCS compat
384 fi # end guess mailto
386 # Ensure that emails are comma-separated.
387 mailto=`echo "$mailto" | sed 's/[ ;]/,/g' | tr -s ',' | sed 's/,/, /g'`
388 test x"$FULLNAME" = x && FULLNAME='Type Your Name Here' \
389 && warn_env FULLNAME
390 test x"$EMAIL" = x && EMAIL='your.mail.here@FIXME.com' && warn_env EMAIL
392 # --ignore-externals appeared after svn 1.1.1
393 my_svn_st=`$SVN status --ignore-externals "$@" \
394 || $SVN status "$@" | sed '/^Performing status on external/ {
399 # Files to put in the ChangeLog entry.
400 change_log_files=`echo "$my_svn_st" | sed '
401 s/^M......\(.*\)$/ * \1: ./; t
402 s/^A......\(.*\)$/ * \1: New./; t
403 s/^D......\(.*\)$/ * \1: Remove./; t
406 if [ x"$change_log_files" = x ]; then
407 yesno 'Nothing to commit, continue anyway?' || return 1
410 svn_diff=`$SVN diff --diff-cmd diff -x -uw "$@"`
411 svn_diff_stat=`echo "$svn_diff" | diffstat`
413 # Get any older svn-log out of the way.
414 test -f "$tmp_log" && mv "$tmp_log" `get_unique_file_name "$tmp_log"`
415 # If we can't get an older svn-log out of the way, find a new name...
416 test -f "$tmp_log" && tmp_log=`get_unique_file_name "$tmp_log"`
417 commit_instructions='
418 Instructions:
419 - Fill the ChangeLog entry.
420 - If you feel like, write a comment in the "Comment:" section.
421 This comment will only appear in the email, not in the ChangeLog.
422 By default only the location of the repository is in the comment.
423 - Some tags will be replaced. Tags are of the form: <TAG>. Unknown
424 tags will be left unchanged.
425 - Your ChangeLog entry will be used as commit message for svn.'
426 # VCS-compat: handle user option 'new_user'
427 grep '^new_user: false' ~/.vcs >/dev/null 2>/dev/null \
428 && commit_instructions=''
429 echo "\
430 --You must fill this file correctly to continue-- -*- vcs -*-
431 Title:
432 Subject: ${projname}r<REV>: <TITLE>
433 From: $FULLNAME <$EMAIL>
434 To: $mailto
436 Comment:
437 Repository: $repos_root
439 ChangeLog:
441 <YYYY>-<MM>-<DD> $FULLNAME <$EMAIL>
443 <TITLE>
445 $change_log_files
447 --This line, and those below, will be ignored--
448 $commit_instructions
450 --Preview of the message that will be sent--
452 Repository: $repos_root
453 Your comments (if any) will appear here.
455 ChangeLog:
456 $YYYY-$MM-$DD $FULLNAME <$EMAIL>
458 Your ChangeLog entry will appear here.
460 $svn_diff_stat
462 $svn_diff" >"$tmp_log"
464 echo "
465 --- Internal stuff, DO NOT change please ---
466 args: $@" >>"$tmp_log"
468 fi # end: if svn-log; then resume? else create template
469 $EDITOR "$tmp_log"
471 # ------------------ #
472 # Re-"parse" the log #
473 # ------------------ #
475 # hmz this section is a bit messy...
476 sed_escape='s/@/\\@/' # helper string... !@#$%* escaping \\\\\\...
477 sed_eval_tags="s/<MM>/$MM/g; s/<DD>/$DD/g; s/<YYYY>/$YYYY/g; s/<REV>/$REV/g"
478 full_log=`sed '/^--This line, and those below, will be ignored--$/,$d;
479 /^--You must fill this/d' "$tmp_log"`
480 chlog_entry=`echo "$full_log" | sed '/^ChangeLog:$/,$!d; //d'`
481 ensure_not_empty 'ChangeLog entry' "$chlog_entry"
482 full_log=`echo "$full_log" | sed '/^ChangeLog:$/,$d'`
483 mail_comment=`echo "$full_log" | sed '/^Comment:$/,$!d; //d'`
484 full_log=`echo "$full_log" | sed '/^Comment:$/,$d'`
485 mail_title=`echo "$full_log" | sed '/^Title: */!d;s///'`
486 ensure_not_empty 'commit title' "$mail_title"
487 mail_title=`echo "$mail_title" | sed "$sed_eval_tags; $sed_escape"`
488 sed_eval_tags="$sed_eval_tags; s@<TITLE>@$mail_title@g"
489 mail_comment=`echo "$mail_comment" | sed "$sed_eval_tags"`
490 chlog_entry=`echo "$chlog_entry" | sed "$sed_eval_tags; 1{
491 /^ *$/d
493 mail_subject=`echo "$full_log" | sed '/^Subject: */!d;s///'`
494 ensure_not_empty 'mail subject' "$mail_subject"
495 mail_subject=`echo "$mail_subject" | sed "$sed_eval_tags"`
496 mail_to=`echo "$full_log" | sed '/^To:/!d'`
497 send_a_mail=yes
498 if test x"$mail_to" = x; then
499 send_a_mail=no
500 else
501 mail_to=`echo "$mail_to" | sed 's/^To: *//'`
502 ensure_not_empty '"To:" field of the mail' "$mail_to"
504 mail_from=`echo "$full_log" | sed '/^From: */!d;s///'`
505 ensure_not_empty '"From:" field of the mail' "$mail_from"
507 # Check whether the user passed -m | --message
508 i=0; has_message=0
509 while [ $i -lt $# ]; do
510 arg="$1"
511 # This is not really a reliable way of knowing whether -m | --message was
512 # passed but hum... Let's assume it'll do :s
513 test x"$arg" = 'x-m' && has_message=1
514 test x"$arg" = 'x--message' && has_message=1
515 shift
516 set dummy "$@" "$arg"
517 shift
518 i=`expr $i + 1`
519 done
520 if [ $has_message -eq 0 ]; then
521 my_message=`echo "$chlog_entry" | grep -v "^$YYYY-$MM-$DD" | sed '1,2 {
522 /^ *$/d
524 set dummy --message "$my_message" "$@"
525 shift
526 else
527 notice 'you are overriding the commit message.'
530 # Are you sure?
531 yesno 'Are you sure you want to commit?' || return 1
533 # Add the ChangeLog entry
534 old_chlog=`get_unique_file_name "$change_log_dir/ChangeLog.old"`
535 mv "$change_log_dir/ChangeLog" "$old_chlog" || \
536 abort 'Could not backup ChangeLog'
537 trap "echo SIGINT; mv \"$old_chlog\" \"$change_log_dir/ChangeLog\"" SIGINT
538 echo "$chlog_entry" >"$change_log_dir/ChangeLog"
539 echo >>"$change_log_dir/ChangeLog"
540 cat "$old_chlog" >>"$change_log_dir/ChangeLog"
542 # --Commit-- finally! :D
543 $SVN commit "$@" || {
544 mv "$old_chlog" "$change_log_dir/ChangeLog"
545 abort "Commit failed, $SVN returned $?"
548 # In the end, perform an svn up to update externals
549 $SVN update "$change_log_dir"
550 svn_commit_rv=$?
552 # Send the mail
553 if test x$send_a_mail = xyes; then
554 mail_file=`get_unique_file_name "$change_log_dir/+mail"`
555 echo "\
556 From: $mail_from
557 To: $mail_to
558 Subject: $mail_subject
560 $mail_comment
562 ChangeLog:
563 $chlog_entry
565 $svn_diff_stat
567 $svn_diff" | sed 's/^\.$/ ./' >"$mail_file"
568 # We change lines with only a `.' because they could mean "end-of-mail"
570 trap 'echo SIGINT; exec < /dev/null' SIGINT
571 test -f ~/.signature && echo '-- ' >>"$mail_file" && \
572 cat ~/.signature >>"$mail_file"
573 # VCS-compat: handle user option 'sign'.
574 if (grep '^sign: false' ~/.vcs) >/dev/null 2>/dev/null; then :; else
575 ($GPG -h) >/dev/null 2>/dev/null
576 gpg_rv=$?
577 if [ -f ~/.gnupg ] || [ -f ~/.gpg ] || [ -f ~/.pgp ] && [ $gpg_rv -gt 42 ]
578 then
579 yesno "Sign the mail using $GPG ?" && \
580 $GPG --clearsign "$mail_file"
581 test -f "$mail_file.asc" && mv "$mail_file.asc" "$mail_file"
584 # FIXME: This looks a bit fragile. What if $mail_to = -s foo ? Will it
585 # override the previous -s option?
586 cat "$mail_file" | mail -s "$mail_subject" "$mail_to"
587 fi # end do we have to send a mail?
588 rm -f "$tmp_log"
589 rm -f "$old_chlog"
590 save_mail_file=`echo "$mail_file" | sed 's/+//'`
591 if [ -d "$change_log_dir/vcs" ] \
592 || [ -d "$change_log_dir/+committed" ]
593 then
594 mkdir -p "$change_log_dir/+committed/$REV" \
595 && mv "$mail_file" "$change_log_dir/+committed/$REV/mail"
597 return $svn_commit_rv
600 # svn_version
601 svn_version()
603 echo "Using svn-wrapper v$version (C) SIGOURE Benoit [GPL]"
604 sed '/^# $Id[:].*$/!d;s/.*$Id[:] *//;s/ *$ *//;s/ \([0-9][0-9]*\)/ (r\1)/' "$0"
607 # has_prop prop-name [path]
608 # return value: 0 -> path has the property prop-name set.
609 # 1 -> path has no property prop-name.
610 # 2 -> svn error.
611 has_prop()
613 hp_plist=`$SVN proplist "$2"`
614 test $? -ne 0 && return 2
615 hp_res=`echo "$hp_plist" | sed "/^ *$1\$/!d"`
616 test x"$hp_res" = x && return 1
617 return 0
620 # svn_propadd prop-name prop-val [path]
621 svn_propadd()
623 test $# -lt 2 \
624 && abort 'Not enough arguments provided; try `svn help propadd` for more info'
625 test $# -gt 3 \
626 && abort 'Too many arguments provided; try `svn help propadd` for more info'
628 path="$3"
629 test x"$path" = x && path='.' && set dummy "$@" '.' && shift
630 has_prop "$1" "$3" || {
631 test $? -eq 2 && return 1 # svn error
632 # no property found:
633 yesno "'$path' has no property named '$1', do you want to add it?" \
634 && $SVN propset "$@"
635 return $?
638 current_prop_val=`$SVN propget "$1" "$3"`
639 test $? -ne 0 && abort "Failed to get the current value of property '$1'."
641 $SVN propset "$1" "$current_prop_val
642 $2" "$3" >/dev/null || abort "Failed to add '$3' in the property '$1'."
644 current_prop_val=`$SVN propget "$1" "$3" || echo "$current_prop_val
645 $2"`
646 echo "property '$1' updated on '$path', new value:
647 $current_prop_val"
650 # svn_propsed prop-name sed-script [path]
651 svn_propsed()
653 test $# -lt 2 \
654 && abort 'Not enough arguments provided; try `svn help propsed` for more info'
655 test $# -gt 3 \
656 && abort 'Too many arguments provided; try `svn help propsed` for more info'
658 path="$3"
659 test x"$path" = x && path='.'
660 has_prop "$1" "$3" || {
661 test $? -eq 2 && return 1 # svn error
662 # no property found:
663 abort "'$path' has no property named '$1'."
666 prop_val=`$SVN propget "$1" "$3"`
667 test $? -ne 0 && abort "Failed to get the current value of property '$1'."
669 prop_val=`echo "$prop_val" | sed "$2"`
670 test $? -ne 0 && abort "Failed to run the sed script '$2'."
672 $SVN propset "$1" "$prop_val" "$3" >/dev/null \
673 || abort "Failed to update the property '$1' with value '$prop_val'."
675 new_prop_val=`$SVN propget "$1" "$3" || echo "$prop_val"`
676 echo "property '$1' updated on '$path', new value:
677 $new_prop_val"
680 # svn_ignore [paths]
681 svn_ignore()
683 if [ $# -eq 0 ]; then # Simply display ignore-list.
684 $SVN propget 'svn:ignore'
685 elif [ $# -eq 1 ]; then
686 if [ -d "$1" ]; then # Display ignore-list for $1
687 $SVN propget 'svn:ignore' "$1"
688 elif [ x`dirname "$1"` != x. ]; then
689 # Add `basename $1` in the ignore-list of `dirname $1`
690 i_basename=`basename "$1"`
691 i_dirname=`dirname "$1"`
692 svn_propadd 'svn:ignore' "$i_basename" "$i_dirname"
693 else # Add $1 in ignore-list of `.'.
694 svn_propadd 'svn:ignore' "$1"
696 else # Add arguments in svn:ignore.
697 i=0; last_arg=''
698 while [ $i -lt $# ]; do
699 arg="$1"
700 shift
701 if [ $i -eq $# ]; then
702 last_arg="$arg"
703 else
704 set dummy "$@" "$arg"
705 shift
707 i=`expr $i + 1`
708 done
709 i_files=`echo "$*" | tr -s ' ' '\n'`
710 if [ -d "$last_arg" ]; then # Add in ignore-list of $last_arg
711 svn_propadd 'svn:ignore' "$i_files" "$last_arg"
712 else # Add in ignore-list of `.'
713 svn_propadd 'svn:ignore' "$i_files
714 $last_arg"
719 # svn_help
720 svn_help()
722 if [ $# -eq 0 ]; then
723 svn_version
724 $SVN help
725 rv=$?
726 echo '
727 Additionnal commands provided by svn-wrapper:
728 diffstat (ds)
729 ignore
730 propadd (padd, pa)
731 propsed (psed)
732 revision (rev)
733 touch
734 selfupdate (selfup)
735 version'
736 return $rv
737 else
738 case $1 in
739 diffstat | ds)
740 echo 'diffstat (ds): Display the histogram from svn diff-output.'
741 $SVN help diff | sed '1d;
742 s/differences*/histogram/;
743 2,35 s/diff/diffstat/g'
745 ignore)
746 echo 'ignore: Add some files in the svn:ignore property.
747 usage: 1. ignore [PATH]
748 2. ignore FILE [FILES...] [PATH]
750 1. Display the value of svn:ignore property on [PATH].
751 2. Add some files in the svn:ignore property of [PATH].
753 If you want to add directories in the ignore-list, be careful:
754 svn ignore foo/ bar/
755 will add "foo/" in the property svn:ignore within the directory bar!
756 Instead use:
757 svn ignore foo/ bar/ .
758 (It'\''s somewhat like with mv)
760 Valid options:
761 None.'
763 propadd | padd | pa)
764 echo 'propadd (padd, pa): Add something in the value of a property.
765 usage: propadd PROPNAME PROPVAL PATH
767 PROPVAL will be appended at the end of the property PROPNAME.
769 Valid options:
770 None.'
772 propsed | psed)
773 echo 'propsed (psed): Edit a property with sed.
774 usage: propsed PROPNAME SED-ARGS PATH
776 eg: svn propsed svn:externals "s/http/https/" .
778 Valid options:
779 None.'
781 revision | rev)
782 echo 'revision (rev): Display the revision number of a local or remote item.'
783 $SVN help info | sed '1d;
784 s/information/revision/g;
785 s/revision about/the revision of/g;
786 2,35 s/info/revision/g;
787 /-xml/d'
789 touch)
790 echo 'touch: Touch a file and svn add it.
791 usage: touch FILE [FILES]...
793 Valid options:
794 None.'
796 selfupdate | selfup | self-update | self-up)
797 echo 'selfupdate (selfup): Attempt to update svn-wrapper.sh
798 usage: selfupdate
800 Valid options:
801 None.'
803 version)
804 echo 'version: Display the version info of svn and svn-wrapper.
805 usage: version
807 Valid options:
808 None.'
810 *) $SVN help "$@";;
811 esac
815 # svn_status [args...]
816 svn_status()
818 $SVN status "$@" | sed "$sed_svn_st_color"
819 return $?
822 # ------------------- #
823 # `main' starts here. #
824 # ------------------- #
826 # Sanity checks.
827 if (echo | diffstat) >/dev/null 2>/dev/null; then :; else
828 warn 'diffstat is not installed on your system or not in your PATH.'
829 test -f /etc/debian_version \
830 && notice 'you might want to `apt-get install diffstat`.'
832 if (echo | mail) >/dev/null 2>/dev/null; then :; else
833 if [ $? -gt 100 ]; then
834 warn 'mail is not installed on your system or not in your PATH.'
835 test -f /etc/debian_version \
836 && notice 'you might want to:
837 # apt-get install mailx
838 # dpkg-reconfigure exim'
842 # Define colors if stdout is a tty.
843 if test -t 1; then
844 set_colors
845 else # stdout isn't a tty => don't print colors.
846 set_nocolors
849 # Considere this as a sed function :P.
850 sed_svn_st_color="
851 t dummy_sed_1
852 : dummy_sed_1
853 s/^? +/+ +/
854 s/^? ,/, ,/
855 s/^\\(.\\)C/\\1${lred}C${std}/
856 t dummy_sed_2
857 : dummy_sed_2
858 s/^\\?/${lred}?${std}/; t
859 s/^M/${lgreen}M${std}/; t
860 s/^A/${lgreen}A${std}/; t
861 s/^X/${lblue}X${std}/; t
862 s/^+/${lyellow}+${std}/; t
863 s/^D/${lyellow}D${std}/; t
864 s/^,/${lred},${std}/; t
865 s/^C/${lred}C${std}/; t
866 s/^I/${purple}I${std}/; t
867 s/^R/${lblue}R${std}/; t
868 s/^!/${lred}!${std}/; t
869 s/^~/${lwhite}~${std}/; t"
871 # For dev's:
872 test "x$1" = x--debug && shift && set -x
874 case "$1" in
875 # ------------------------------- #
876 # Hooks for standard SVN commands #
877 # ------------------------------- #
878 commit | ci)
879 shift
880 svn_commit "$@"
882 help | \? | h)
883 shift
884 svn_help "$@"
886 status | stat | st)
887 shift
888 svn_status "$@"
890 # -------------------- #
891 # Custom SVN commands #
892 # -------------------- #
893 diffstat | ds)
894 shift
895 $SVN diff "$@" | diffstat
897 ignore)
898 shift
899 svn_ignore "$@"
901 propadd | padd | pa)
902 shift
903 svn_propadd "$@"
905 propsed | psed)
906 shift
907 svn_propsed "$@"
909 revision | rev)
910 shift
911 $SVN info "$@" | sed '/^Revision/!d;s/^Revision: //'
913 touch)
914 shift
915 touch "$@" && svn add "$@"
917 selfupdate | selfup | self-update | self-up)
918 shift
919 selfupdate "$@"
921 version | -version | --version)
922 shift
923 set dummy '--version' "$@"
924 shift
925 svn_version
926 exec $SVN "$@"
928 *) exec $SVN "$@"
930 esac