Sort files in the ChangeLog entry.
[svn-wrapper.git] / svn-wrapper.sh
blobe1dd422c8606f95fbb2b7b28ee05926a04607d10
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 # * svn automerge + automatic fill in branches/README.branches.
65 # * Automatic proxy configuration depending on the IP in ifconfig?
66 # * Customizable behavior/colors via some ~/.<config>rc file (?)
67 # * Automatically recognize svn cp and svn mv instead of writting "New" and
68 # "Remove" in the template ChangeLog entry (hard). Pair the and new/remove
69 # to prepare a good ChangeLog entry (move to X, copy from X) [even harder].
70 # => svn info says whether an item was copied from a given *URL* or not.
74 # No args, invoke svn...
75 test $# -lt 1 && exec $SVN
77 # Default values (the user can export them to override them).
78 : ${SVN=svn}
79 : ${EDITOR=vi}
80 export EDITOR
81 : ${GPG=gpg}
82 : ${TMPDIR=/tmp}
83 export TMPDIR
84 : ${PAGER=more}
85 export PAGER
86 # Override the locale.
87 LC_ALL='C'
88 export LC_ALL
90 # Pitfall: some users might be tempted to export SVN=svn-wrapper.sh for some
91 # reason. This is just *wrong*. The following is an attempt to save them from
92 # some troubles.
93 if [ x`basename "$SVN"` = 'xsvn-wrapper.sh' ]; then
94 SVN='svn'
97 # This code comes (mostly) from Autoconf.
98 # The user is always right.
99 if test "${PATH_SEPARATOR+set}" != set; then
100 trap "echo SIGINT; rm -f conf$$.sh; exit 130" SIGINT
101 echo "#! /bin/sh" >conf$$.sh
102 echo "exit 0" >>conf$$.sh
103 chmod +x conf$$.sh
104 if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
105 PATH_SEPARATOR=';'
106 else
107 PATH_SEPARATOR=:
109 rm -f conf$$.sh
110 # "Remove" the trap
111 trap 'echo SIGINT; exit 130' SIGINT
114 version='0.3'
115 revision=`sed '/^# $Id[:].*$/!d;
116 s/.*$Id[:] *//;
117 s/ *$ *//;
118 s/[^0-9]*\([0-9][0-9]*\).*/\1/' "$0"`
119 me="$0"
121 # The `main' really starts after the functions definitions.
123 # ---------------- #
124 # Helper functions #
125 # ---------------- #
127 set_colors()
129 red='\e[0;31m'; lred='\e[1;31m'
130 green='\e[0;32m'; lgreen='\e[1;32m'
131 yellow='\e[0;33m'; lyellow='\e[1;33m'
132 blue='\e[0;34m'; lblue='\e[1;34m'
133 purple='\e[0;35m'; lpurple='\e[1;35m'
134 cyan='\e[0;36m'; lcyan='\e[1;36m'
135 grey='\e[0;37m'; lgrey='\e[1;37m'
136 white='\e[0;38m'; lwhite='\e[1;38m'
137 std='\e[m'
140 set_nocolors()
142 red=''; lred=''
143 green=''; lgreen=''
144 yellow=''; lyellow=''
145 blue=''; lblue=''
146 purple=''; lpurple=''
147 cyan=''; lcyan=''
148 grey=''; lgrey=''
149 white=''; lwhite=''
150 std=''
153 # abort err-msg
154 abort()
156 echo "svn-wrapper: ${lred}abort${std}: $@" \
157 | sed '1!s/^[ ]*/ /' >&2
158 exit 1
161 # warn msg
162 warn()
164 echo "svn-wrapper: ${lred}warning${std}: $@" \
165 | sed '1!s/^[ ]*/ /' >&2
168 # notice msg
169 notice()
171 echo "svn-wrapper: ${lyellow}notice${std}: $@" \
172 | sed '1!s/^[ ]*/ /' >&2
175 # yesno question
176 yesno()
178 echo -n "$@ [y/N] "
179 read answer || return 1
180 case "$answer" in
181 y* | Y*) return 0;;
182 *) return 1;;
183 esac
184 return 42 # should never happen...
187 # warn_env env-var
188 warn_env()
190 warn "cannot find the environment variable $1
191 You might consider using \`export $1 <FIXME>\`"
194 # get_unique_file_name file-name
195 get_unique_file_name()
197 test -e "$1" || {
198 echo "$1" && return 0
200 gufn="$1"; i=1
201 while test -e "$gufn.$i"; do
202 i=`expr $i + 1`
203 done
204 echo "$gufn.$i"
207 # ensure_not_empty description value
208 ensure_not_empty()
210 ene_val=`echo "$2" | tr -d ' \t\n'`
211 test x"$ene_val" = x && abort "$1: empty value"
214 # find_prog prog-name
215 # return true if prog-name is in the PATH
216 # echo the full path to prog-name on stdout.
217 # Based on a code from texi2dvi
218 find_prog()
220 save_IFS=$IFS
221 IFS=$PATH_SEPARATOR
222 for dir in $PATH; do
223 IFS=$save_IFS
224 test x"$dir" = x && continue
225 # The basic test for an executable is `test -f $f && test -x $f'.
226 # (`test -x' is not enough, because it can also be true for directories.)
227 # We have to try this both for $1 and $1.exe.
229 # Note: On Cygwin and DJGPP, `test -x' also looks for .exe. On Cygwin,
230 # also `test -f' has this enhancement, bot not on DJGPP. (Both are
231 # design decisions, so there is little chance to make them consistent.)
232 # Thusly, it seems to be difficult to make use of these enhancements.
234 if test -f "$dir/$1" && test -x "$dir/$1"; then
235 echo "$dir/$1"
236 return 0
237 elif test -f "$dir/$1.exe" && test -x "$dir/$1.exe"; then
238 echo "$dir/$1.exe"
239 return 0
241 done
242 return 1
245 # require_diffstat
246 # return true if diffstat is in the PATH
247 require_diffstat()
249 if [ x"$require_diffstat_cache" != x ]; then
250 return $require_diffstat_cache
252 if (echo | diffstat) >/dev/null 2>/dev/null; then :; else
253 warn 'diffstat is not installed on your system or not in your PATH.'
254 test -f /etc/debian_version \
255 && notice 'you might want to `apt-get install diffstat`.'
256 require_diffstat_cache=1
257 return 1
259 require_diffstat_cache=0
260 return 0
263 # require_mail
264 # return 0 -> found sendmail
265 # 1 -> found mail
266 # 2 -> no mailer found
267 # The full path to the program found is echo'ed on stdout.
268 require_mail()
270 if [ x"$require_mail_cache_rv" != x ]; then
271 echo "$require_mail_cache"
272 return $require_mail_cache_rv
274 save_PATH="$PATH"
275 PATH="${PATH}${PATH_SEPARATOR}/sbin${PATH_SEPARATOR}/usr/sbin${PATH_SEPARATOR}/usr/libexec"
276 export PATH
277 require_mail_cache=`find_prog sendmail`
278 if [ $? -eq 0 ] && [ x"$require_mail_cache" != x ]; then
279 echo "$require_mail_cache"
280 require_mail_cache_rv=0
281 return 0
283 PATH="$save_PATH"
284 export PATH
285 require_mail_cache=`find_prog mail`
286 if [ $? -eq 0 ] && [ x"$require_mail_cache" != x ]; then
287 echo "$require_mail_cache"
288 require_mail_cache_rv=1
289 return 1
290 else
291 warn 'mail is not installed on your system or not in your PATH.'
292 test -f /etc/debian_version \
293 && notice 'you might want to:
294 # apt-get install mailx
295 # dpkg-reconfigure exim'
296 require_mail_cache=''
297 require_mail_cache_rv=1
298 return 1
300 require_mail_cache=''
301 require_mail_cache_rv=42
302 return 42
305 # my_sendmail mail-file mail-subject mail-to [extra-headers]
306 # mail-to is a comma-separated list of email addresses.
307 # extra-headers is an optionnal argument and will be prepended at the
308 # beginning of the mail headers if the tool used to send mails supports it.
309 # The mail-file may also contain headers. They must be separated from the body
310 # of the mail by a blank line.
311 my_sendmail()
313 test -f "$1" || abort "my_sendmail: Cannot find the mail file: $1"
314 test x"$2" = x && warn 'my_sendmail: Empty subject.'
315 test x"$3" = x && abort 'my_sendmail: No recipient specified.'
317 extra_headers="Content-type: text/plain
318 X-Mailer: svn-wrapper v$version (r$revision)"
319 if test x"$4" != x; then
320 extra_headers="$4
321 $extra_headers"
322 # Remove empty lines.
323 extra_headers=`echo "$extra_headers" | sed '/^[ ]*$/d;s/^[ ]*//'`
326 test -f ~/.signature && echo '-- ' >>"$1" && \
327 cat ~/.signature >>"$1"
328 # VCS-compat: handle user option 'sign'.
329 if (grep '^sign: false' ~/.vcs) >/dev/null 2>/dev/null; then :; else
330 ($GPG -h) >/dev/null 2>/dev/null
331 gpg_rv=$?
332 if [ -e ~/.gnupg ] || [ -e ~/.gpg ] || [ -e ~/.pgp ] && [ $gpg_rv -lt 42 ]
333 then
334 if yesno "Sign the mail using $GPG ?"; then
335 # GPG sign the mail with the headers stripped.
336 cat "$1" | sed '1,/^$/d' | $GPG --clearsign >"$1.asc"
337 # Re-paste the headers before the signed body.
338 test -f "$1.asc" \
339 && cat "$1" | sed '1,/^$/!d' | cat - "$1.asc" >"$1.tmp" \
340 && mv -f "$1.tmp" "$1"
341 # Cleanup.
342 rm -f "$1.tmp" "$1.asc"
347 mailer=`require_mail`
348 r_mail_rv=$?
349 if [ $? -ge 2 ] || [ x"$mailer" = x ]; then
350 warn 'my_sendmail: No suitable mailer found.'
351 return 1
353 case $r_mail_rv in
354 0) # sendmail
355 to=`echo "$3" | sed 's/,//g'`
356 echo "$extra_headers" | cat - "$1" | $mailer $to;;
357 1) # mail
358 cat "$1" | $mailer -s "$2" "$3";;
359 *) # wtf
360 warn 'my_sendmail: Internal error.'; return 42;;
361 esac
364 # selfupdate
365 selfupdate()
367 my_url='http://www.lrde.epita.fr/~sigoure/svn-wrapper'
368 # You can use https if you feel paranoiac.
370 echo ">>> Fetching svn-wrapper.sh from $my_url/svn-wrapper.sh"
372 # --------------------- #
373 # Fetch the new version #
374 # --------------------- #
376 tmp_me=`get_unique_file_name "$TMPDIR/svn-wrapper.sh"`
377 if (wget --help) >/dev/null 2>/dev/null; then
378 wget --no-check-certificate "$my_url/svn-wrapper.sh" -O "$tmp_me"
379 my_wget='wget'
380 else
381 curl --help >/dev/null 2>/dev/null
382 if [ $? -gt 42 ]; then
383 abort 'Cannot find wget or curl.
384 How can I download any update without them?'
386 my_wget='curl'
387 curl --insecure "$my_url/svn-wrapper.sh" >"$tmp_me"
390 test -r $tmp_me \
391 || abort "Cannot find the copy of myself I downloaded in $tmp_me"
393 # ---------------------------------------- #
394 # Compare versions and update if necessary #
395 # ---------------------------------------- #
397 my_ver="$revision"
398 tmp_ver=`sed '/^# $Id[:].*$/!d;
399 s/.*$Id[:] *//;
400 s/ *$ *//;
401 s/[^0-9]*\([0-9][0-9]*\).*/\1/' "$tmp_me"`
402 test x"$tmp_ver" = x && abort "Cannot find the version of $tmp_me"
403 if [ "$my_ver" -lt "$tmp_ver" ]; then # There IS an update...
404 echo "An update is available, r$tmp_ver (your version is r$my_ver)"
406 # See the ChangeLog?
407 if yesno "Do you want to see the ChangeLog between r$my_ver and r$tmp_ver?"
408 then
409 my_chlog=`get_unique_file_name "$TMPDIR/ChangeLog"`
410 case $my_wget in
411 wget) wget --no-check-certificate "$my_url/ChangeLog" -O "$my_chlog"
413 curl) curl --insecure "$my_url/ChangeLog" >"$my_chlog"
415 *) abort 'Should never be here.'
417 esac
418 sed "/^r$my_ver/q" "$my_chlog" | $PAGER
419 rm -f "$my_chlog"
422 # Wanna see the diff?
423 if yesno "Do you want to see the diff between r$my_ver and r$tmp_ver?"
424 then
425 (require_diffstat && diff -uw "$me" "$tmp_me" | diffstat;
426 echo
427 diff -uw "$me" "$tmp_me") | $PAGER
430 # Let's go :)
431 if yesno "Overwrite $me (r$my_ver) with $tmp_me (r$tmp_ver)?"; then
432 chmod a+x "$tmp_me"
433 mv "$tmp_me" "$me" && exit 0
435 rm -f "$tmp_me"
436 return 1
437 elif [ "$my_ver" -gt "$tmp_ver" ]; then
438 echo "Wow, you're more up to date than the master copy :)"
439 echo "Your version is r$my_ver and the master copy is r$tmp_ver."
440 if yesno 'Downgrade?'; then
441 chmod a+x "$tmp_me"
442 mv "$tmp_me" "$me" && exit 0
444 else
445 echo "You're already up to date [r$my_ver] :)"
447 rm -f "$tmp_me"
450 # ------------------------------- #
451 # Hooks for standard SVN commands #
452 # ------------------------------- #
454 # svn_commit [args...]
455 # Here is how the commit process goes:
456 # First we look in the arguments passed to commit:
457 # If there are some files or paths, the user wants to commit these only. In
458 # this case, we must search for ChangeLogs from these paths. We might find
459 # more than one ChangeLog, in this case the user will be prompted to pick up
460 # one.
461 # Otherwise (no path passed in the command line) the user just wants to
462 # commit the current working directory.
463 # In any case, we schedule "ChangeLog" for commit.
464 # Alright now that we know which ChangeLog to use, we look in the ChangeLog's
465 # directory if there is a ",svn-log" file which would mean that a previous
466 # commit didn't finish successfully. If there is such a file, the user is
467 # prompted to know whether they want to resume that commit or simply start a
468 # new one.
469 # When the user wants to resume a commit, the ",svn-log" file is loaded and we
470 # retrieve the value of "$@" that was saved in the file.
471 # Otherwise we build a template ChangeLog entry.
472 # Then we open the template ChangeLog entry with $EDITOR so that the user
473 # fills it properly.
474 # Finally, we commit.
475 svn_commit()
477 here=`pwd -P`
479 # Check if the user passed some paths to commit explicitly
480 # because in this case we must add the ChangeLog to the commit and search
481 # the ChangeLog from the dirname of that file.
482 i=0; search_from=''; add_changelog=no; extra_files=''
483 while [ $i -lt $# ]; do
484 arg="$1"
485 # If the argument is a valid path: add the ChangeLog in the list of
486 # files to commit
487 if test -e "$arg"; then
488 add_changelog=yes
489 if test -d "$arg"; then
490 search_from_add="$arg"
491 else
492 search_from_add=`dirname "$arg"`
494 search_from="$search_from:$search_from_add"
496 shift
497 set dummy "$@" "$arg"
498 shift
499 i=`expr $i + 1`
500 done
501 if [ $add_changelog = no ]; then
502 # There is no path/file in the command line: the user wants to commit the
503 # current directory. Make it explicit now:
504 extra_files="$here"
506 search_from=`echo "$search_from" | sed 's/^://; s/^$/./'`
508 # ----------------- #
509 # Find ChangeLog(s) #
510 # ----------------- #
512 nb_chlogs=0; change_log_dirs=''
513 save_IFS=$IFS; IFS=':'
514 for dir in $search_from; do
515 IFS=$save_IFS
516 test -z "$dir" && dir='.'
517 # First: come back to the original place
518 cd "$here" || abort "Cannot cd to $here"
519 cd "$dir" || continue # Then: Enter $dir (which can be a relative path)
520 found=0
521 while [ $found -eq 0 ]; do
522 this_chlog_dir=`pwd -P`
523 if [ -f ./ChangeLog ]; then
524 found=1
525 nb_chlogs=`expr $nb_chlogs + 1`
526 change_log_dirs="$change_log_dirs:$this_chlog_dir"
527 else
528 cd ..
530 # Stop searching when in / ... hmz :P
531 test x"$this_chlog_dir" = x/ && break
532 done # end while: did we find a ChangeLog
533 done # end for: find ChangeLogs in $search_from
534 if [ $nb_chlogs -gt 0 ]; then
535 change_log_dirs=`echo "$change_log_dirs" | sed 's/^://' | tr ':' '\n' \
536 | sort -u`
537 nb_chlogs=`echo "$change_log_dirs" | wc -l`
540 # Did we find a ChangeLog? More than one?
541 if [ $nb_chlogs -eq 0 ]; then
542 if yesno 'svn-wrapper: Error: Cannot find a ChangeLog file!
543 You might want to create an empty one (eg: `touch ChangeLog` where appropriate)
544 Do you want to proceed without using a ChangeLog?'; then
545 cd "$here"
546 $SVN commit "$@"
547 return $?
548 else
549 return 1
551 elif [ $nb_chlogs -gt 1 ]; then
552 notice "$nb_chlogs ChangeLogs were found, pick up one:"
554 IFS=':'; i=0
555 for a_chlog_dir in $change_log_dirs; do
556 i=`expr $i + 1`
557 echo "$i. $a_chlog_dir/ChangeLog"
558 done
559 echo -n "Which ChangeLog do you want to use? [1-$i] "
560 read chlog_no || abort 'Cannot read answer on stdin.'
562 case "$chlog_no" in
563 *[^0-9]*) abort "Invalid ChangeLog number: $chlog_no"
564 esac
565 test "$chlog_no" -le $i || abort "Invalid ChangeLog number: $chlog_no
566 max value was: $i"
567 test "$chlog_no" -ge 1 || abort "Invalid ChangeLog number: $chlog_no
568 min value was: 1"
569 change_log_dir=`echo "$change_log_dirs" | tr ':' '\n' | sed "${chlog_no}!d"`
570 else # Only one ChangeLog found
571 change_log_dir=$change_log_dirs
572 notice "using $change_log_dir/ChangeLog"
575 test -f "$change_log_dir/ChangeLog" \
576 || abort "No such file or directory: $change_log_dir/ChangeLog"
578 # Now we can safely schedule the ChangeLog for the commit.
579 extra_files="$extra_files:$change_log_dir/ChangeLog"
581 svn_st_tmp=`$SVN status "$change_log_dir"`
583 # Warn for files that are not added in the repos.
584 conflicts=`echo "$svn_st_tmp" | sed '/^[^?]/d;
585 /^?.......*\/[,+]/d;
586 /^?......[,+]/d'`
587 if test x"$conflicts" != x; then
588 warn "make sure you don't want to \`svn add'
589 any of the following files before committing:"
590 echo "$conflicts" | sed "$sed_svn_st_color"
591 echo -n 'Type [ENTER] to continue :)' && read chiche_is_gay
594 # Detect unresolved conflicts / missing files.
595 conflicts=`echo "$svn_st_tmp" | sed '/^[C!]/!d'`
596 test x"$conflicts" != x && abort "there are unresolved conflicts (\`C')
597 and/or missing files (\`!'):
598 $conflicts"
600 svn_info_tmp=`$SVN info "$change_log_dir"`
601 test $? -ne 0 && abort "Failed to get svn info on $change_log_dir"
602 repos_root=`echo "$svn_info_tmp" | sed '/^Repository Root: /!d;s///'`
603 # It looks like svn <1.3 didn't display a "Repository Root" entry.
604 test x"$repos_root" = x && \
605 repos_root=`echo "$svn_info_tmp" | sed '/^URL: /!d;s///'`
606 cd "$here"
608 YYYY=`date '+%Y'`
609 MM=`date '+%m'`
610 DD=`date '+%d'`
611 echo -n 'Please wait... '
612 svn_info_tmp=`$SVN info "$repos_root"`
613 REV=`echo "$svn_info_tmp" | sed '/^Revision: /!d;s///'`
614 test x"$REV" = x && REV=`echo "$svn_info_tmp" \
615 | sed '/^Last Changed Rev: /!d;s///'`
616 test x"$REV" = x && abort 'Cannot detect the current revision.'
617 REV=`expr $REV + 1`
618 echo "next revision: $REV"
620 # VCS-compat: handle user option 'new_user'
621 new_user='yes'
622 grep '^new_user: false' ~/.vcs >/dev/null 2>/dev/null && new_user='no'
624 tmp_log="$change_log_dir/,svn-log"
625 if [ -f "$tmp_log" ] && yesno "It looks like the last commit did not\
626 terminate successfully.
627 Would you like to resume it?"; then
628 echo 'Resuming ...'
629 internal_tags=`sed '/^--- Internal stuff, DO NOT change please ---$/,$!d' \
630 "$tmp_log"`
631 saved_args=`echo "$internal_tags" | sed '/^args: */!d;s///'`
632 extra_files=`echo "$internal_tags" | sed '/^extra_files: */!d;s///'`
633 if [ x"$saved_args" != x ]; then
634 if [ x"$*" != x ] && [ x"$saved_args" != x"$*" ]; then
635 warn "overriding arguments:
636 you invoked $me with the following arguments: $@
637 they have been replaced by these: $saved_args"
638 set dummy $saved_args
639 shift
640 else
641 notice "setting the following arguments: $saved_args"
642 set dummy $saved_args
643 shift
646 # Ignore white spaces. Can't svn diff -x -w: svn 1.4 only.
647 svn_diff=`svn_diffw "$@"`
648 test x"$svn_diff" = x && svn_diff=`$SVN diff "$@"`
649 if require_diffstat; then
650 svn_diff_stat=`echo "$svn_diff" | diffstat`
651 else
652 svn_diff_stat='diffstat not available'
655 else # Build the template message.
657 # ------------------------------------ #
658 # Gather info for the template message #
659 # ------------------------------------ #
661 projname=`$SVN propget project "$change_log_dir"`
662 # Try to be VCS-compatible and find a project name in a *.rb.
663 if [ x"$projname" = x ] && [ -d "$change_log_dir/vcs" ]; then
664 projname=`sed '/common_commit/!d;s/.*"\(.*\)<%= rev.*/\1/' \
665 "$change_log_dir"/vcs/*.rb`
666 test x"$projname" != x && test x$new_user = xyes \
667 && notice "VCS-compat: found project name: $projname
668 in " "$change_log_dir"/vcs/*.rb
670 test x"$projname" != x && projname=`echo "$projname" | sed '/[^ ]$/s/$/ /'`
672 mailto=`$SVN propget mailto "$change_log_dir"`
673 if [ x"$mailto" = x ]; then
674 test x$new_user = xyes \
675 && warn "no svn property mailto found in $change_log_dir
676 You might want to set default email adresses using:
677 svn propset mailto 'somebody@mail.com, foobar@example.com'\
678 $change_log_dir" >&2
679 # Try to be VCS-compatible and find a list of mails in a *.rb.
680 if [ -d "$change_log_dir/vcs" ]; then
681 mailto=`grep '.@.*\..*' "$change_log_dir"/vcs/*.rb \
682 | tr '\n' ' ' \
683 | sed 's/^.*[["]\([^["]*@[^]"]*\)[]"].*$/\1/' | xargs`
684 test x"$mailto" != x && test x$new_user = xyes \
685 && notice "VCS-compat: found mailto: $mailto
686 in " "$change_log_dir"/vcs/*.rb
687 fi # end VCS compat
688 fi # end guess mailto
690 # Ensure that emails are comma-separated.
691 mailto=`echo "$mailto" | sed 's/[ ;]/,/g' | tr -s ',' | sed 's/,/, /g'`
692 test x"$FULLNAME" = x && FULLNAME='Type Your Name Here' \
693 && warn_env FULLNAME
694 test x"$EMAIL" = x && EMAIL='your.mail.here@FIXME.com' && warn_env EMAIL
696 # --ignore-externals appeared after svn 1.1.1
697 my_svn_st=`$SVN status --ignore-externals "$@" \
698 || $SVN status "$@" | sed '/^Performing status on external/ {
702 # Files to put in the ChangeLog entry.
703 change_log_files=`echo "$my_svn_st" | sed '
704 t dummy_sed_1
705 : dummy_sed_1
706 s/^M......\(.*\)$/ * \1: ./; t
707 s/^A......\(.*\)$/ * \1: New./; t
708 s/^D......\(.*\)$/ * \1: Remove./; t
711 if [ x"$change_log_files" = x ]; then
712 yesno 'Nothing to commit, continue anyway?' || return 1
714 change_log_files=`echo "$change_log_files" | sort`
716 svn_diff=`svn_diffw "$@"`
717 test x"$svn_diff" = x && svn_diff=`$SVN diff "$@"`
718 if require_diffstat; then
719 svn_diff_stat=`echo "$svn_diff" | diffstat`
720 else
721 svn_diff_stat='diffstat not available'
724 # Get any older svn-log out of the way.
725 test -f "$tmp_log" && mv "$tmp_log" `get_unique_file_name "$tmp_log"`
726 # If we can't get an older svn-log out of the way, find a new name...
727 test -f "$tmp_log" && tmp_log=`get_unique_file_name "$tmp_log"`
728 if [ x$new_user = no ]; then
729 commit_instructions='
730 Instructions:
731 - Fill the ChangeLog entry.
732 - If you feel like, write a comment in the "Comment:" section.
733 This comment will only appear in the email, not in the ChangeLog.
734 By default only the location of the repository is in the comment.
735 - Some tags will be replaced. Tags are of the form: <TAG>. Unknown
736 tags will be left unchanged.
737 - Your ChangeLog entry will be used as commit message for svn.'
738 else
739 commit_instructions=''
741 echo "\
742 --You must fill this file correctly to continue-- -*- vcs -*-
743 Title:
744 Subject: ${projname}r<REV>: <TITLE>
745 From: $FULLNAME <$EMAIL>
746 To: $mailto
748 Comment:
749 Repository: $repos_root
751 ChangeLog:
753 <YYYY>-<MM>-<DD> $FULLNAME <$EMAIL>
755 <TITLE>
756 $change_log_files
758 --This line, and those below, will be ignored--
759 $commit_instructions
760 --Preview of the message that will be sent--
762 Repository: $repos_root
763 Next revision: $REV
764 Your comments (if any) will appear here.
766 ChangeLog:
767 $YYYY-$MM-$DD $FULLNAME <$EMAIL>
769 Your ChangeLog entry will appear here.
771 $svn_diff_stat
773 $svn_diff" >"$tmp_log"
775 echo "
776 --- Internal stuff, DO NOT change please ---
777 args: $@" >>"$tmp_log"
778 echo "extra_files: $extra_files
779 vi: ft=diff" >>"$tmp_log"
781 fi # end: if svn-log; then resume? else create template
782 $EDITOR "$tmp_log"
784 # ------------------ #
785 # Re-"parse" the log #
786 # ------------------ #
788 # hmz this section is a bit messy...
789 sed_escape='s/@/\\@/' # helper string... !@#$%* escaping \\\\\\...
790 sed_eval_tags="s/<MM>/$MM/g; s/<DD>/$DD/g; s/<YYYY>/$YYYY/g; s/<REV>/$REV/g"
791 full_log=`sed '/^--This line, and those below, will be ignored--$/,$d;
792 /^--You must fill this/d' "$tmp_log"`
793 chlog_entry=`echo "$full_log" | sed '/^ChangeLog:$/,$!d; //d'`
794 ensure_not_empty 'ChangeLog entry' "$chlog_entry"
795 full_log=`echo "$full_log" | sed '/^ChangeLog:$/,$d'`
796 mail_comment=`echo "$full_log" | sed '/^Comment:$/,$!d; //d'`
797 full_log=`echo "$full_log" | sed '/^Comment:$/,$d'`
798 mail_title=`echo "$full_log" | sed '/^Title: */!d;s///'`
799 ensure_not_empty 'commit title' "$mail_title"
800 mail_title=`echo "$mail_title" | sed "$sed_eval_tags; $sed_escape"`
801 sed_eval_tags="$sed_eval_tags; s@<TITLE>@$mail_title@g"
802 mail_comment=`echo "$mail_comment" | sed "$sed_eval_tags"`
803 chlog_entry=`echo "$chlog_entry" | sed "$sed_eval_tags; 1{
804 /^ *$/d
806 mail_subject=`echo "$full_log" | sed '/^Subject: */!d;s///'`
807 ensure_not_empty 'mail subject' "$mail_subject"
808 mail_subject=`echo "$mail_subject" | sed "$sed_eval_tags"`
809 mail_to=`echo "$full_log" | sed '/^To:/!d'`
810 send_a_mail=yes
811 if test x"$mail_to" = x; then
812 send_a_mail=no
813 else
814 mail_to=`echo "$mail_to" | sed 's/^To: *//'`
815 ensure_not_empty '"To:" field of the mail' "$mail_to"
817 mail_from=`echo "$full_log" | sed '/^From: */!d;s///'`
818 ensure_not_empty '"From:" field of the mail' "$mail_from"
820 # Check whether the user passed -m | --message
821 i=0; has_message=0
822 while [ $i -lt $# ]; do
823 arg="$1"
824 # This is not really a reliable way of knowing whether -m | --message was
825 # passed but hum... Let's assume it'll do :s
826 test x"$arg" = 'x-m' && has_message=1
827 test x"$arg" = 'x--message' && has_message=1
828 shift
829 set dummy "$@" "$arg"
830 shift
831 i=`expr $i + 1`
832 done
833 if [ $has_message -eq 0 ]; then
834 my_message=`echo "$chlog_entry" | grep -v "^$YYYY-$MM-$DD" | sed '1,2 {
835 /^ *$/d
837 set dummy --message "$my_message" "$@"
838 shift
839 else
840 notice 'you are overriding the commit message.'
843 # Are you sure?
844 yesno 'Are you sure you want to commit?' || return 1
846 # Add the ChangeLog entry
847 old_chlog=`get_unique_file_name "$change_log_dir/ChangeLog.old"`
848 mv "$change_log_dir/ChangeLog" "$old_chlog" || \
849 abort 'Could not backup ChangeLog'
850 trap "echo SIGINT; mv \"$old_chlog\" \"$change_log_dir/ChangeLog\";
851 exit 130" SIGINT
852 echo "$chlog_entry" >"$change_log_dir/ChangeLog"
853 echo >>"$change_log_dir/ChangeLog"
854 cat "$old_chlog" >>"$change_log_dir/ChangeLog"
856 # Add extra files such as cwd or ChangeLog to the commit.
857 tmp_sed='s/ /\\ /g' # Escape spaces for the shell.
858 extra_files=`echo "$extra_files" | sed "$tmp_sed" | tr ':' '\n'`
859 set dummy "$@" $extra_files
860 shift
862 # --Commit-- finally! :D
863 $SVN commit "$@" || {
864 svn_commit_rv=$?
865 mv "$old_chlog" "$change_log_dir/ChangeLog"
866 abort "Commit failed, $SVN returned $svn_commit_rv"
868 svn_commit_rv=$?
870 mail_file=`get_unique_file_name "$change_log_dir/+mail"`
871 echo "\
872 From: $mail_from
873 To: $mail_to
874 Subject: $mail_subject
876 $mail_comment
878 ChangeLog:
879 $chlog_entry
881 $svn_diff_stat
883 $svn_diff" | sed 's/^\.$/ ./' >"$mail_file"
884 # We change lines with only a `.' because they could mean "end-of-mail"
886 # Send the mail
887 if test x$send_a_mail = xyes; then
888 trap 'echo SIGINT; exec < /dev/null' SIGINT
889 my_sendmail "$mail_file" "$mail_subject" "$mail_to" \
890 "X-svn-url: $repos_root
891 X-svn-revision: $REV"
892 fi # end do we have to send a mail?
893 rm -f "$tmp_log"
894 rm -f "$old_chlog"
895 save_mail_file=`echo "$mail_file" | sed 's/+//'`
896 mkdir -p "$change_log_dir/+committed" \
897 || warn "Couldn't mkdir -p $change_log_dir/+committed"
898 if [ -d "$change_log_dir/vcs" ] \
899 || [ -d "$change_log_dir/+committed" ]
900 then
901 mkdir -p "$change_log_dir/+committed/$REV" \
902 && mv "$mail_file" "$change_log_dir/+committed/$REV/mail"
904 return $svn_commit_rv
907 # svn_merge args...
908 svn_merge()
910 i=0; src=''; dst=''; rev1=''; rev2=''; r_seen=0
911 while [ $i -lt $# ]; do
912 arg="$1"
913 if test -d "$arg"; then
914 if [ x"$src" = x ]; then
915 src="$arg"
916 elif [ x"$dst" = x ]; then
917 dst="$arg"
919 else
920 case "$arg" in
921 -r*:*) rev1=`echo "$arg" | sed 's/^[^PREVHEAD0-9]*//; s/:.*//'`
922 rev2=`echo "$arg" | sed 's/^.*://; s/[^PREVHEAD0-9]*//'`
924 -r) r_seen=1 ;;
925 esac
927 shift
928 set dummy "$@" "$arg"
929 shift
930 i=`expr $i + 1`
931 done
932 if [ x"$src" = x ]; then
933 exec $SVN merge "$@"
935 test x"$dst" = x && dst='.'
937 if yesno "Do you want to use the automerge feature for $src ?";
938 then :; else
939 exec $SVN merge "$@"
941 # FIXME: Incomplete.
944 # svn_automerge src-path dst-path
945 svn_automerge()
947 test -f ChangeLog || abort 'You must use automerge in the folder where the
948 ChangeLog is.'
949 amerge=`$SVN propget automerge`
950 if [ x"$amerge" = x ]; then
951 # FIXME: Use svn log --stop-on-copy to detect the original branching.
955 svn_info_tmp=`$SVN info`
956 REV=`echo "$svn_info_tmp" | sed '/^Revision: /!d;s///'`
957 test x"$REV" = x && REV=`echo "$svn_info_tmp" \
958 | sed '/^Last Changed Rev: /!d;s///'`
959 test x"$REV" = x && abort 'Cannot detect the current revision.'
961 # FIXME: Incomplete.
962 if yesno "Are you sure you want to run:
963 $SVN merge -r$amerge:$REV $1 $2 ?"; then
964 $SVN merge "-r$amerge:$REV" "$1" "$2"
968 # svn_diffw [args...]
969 svn_diffw()
971 # Ignore white spaces. Can't svn diff -x -w: svn 1.4 only.
972 $SVN diff --no-diff-deleted --diff-cmd diff -x -uw "$@"
975 # svn_mail REV [mails...]
976 svn_mail()
978 test $# -lt 1 && abort "Not enough arguments provided;
979 Try 'svn help mail' for more info."
980 case "$1" in
981 PREV)
982 REV=`svn_revision || abort 'Cannot get current revision number'`
983 test x"$REV" = x && abort 'Cannot get current revision number'
984 if [ "$REV" -lt 1 ]; then
985 abort 'No previous revision.'
987 REV=`expr "$REV" - 1`
989 HEAD)
990 REV=`svn_revision || abort 'Cannot get current revision number'`
991 test x"$REV" = x && abort 'Cannot get current revision number'
993 *[^0-9]*) abort "Syntax error in revision argument '$1'";;
994 *) REV="$1";;
995 esac
996 shift
998 found_committed=0; found=0
999 while [ $found -eq 0 ]; do
1000 this_chlog_dir=`pwd -P`
1001 if [ -d ./+committed ]; then
1002 found_committed=1
1003 if [ -d ./+committed/$REV ]; then
1004 found=1
1005 else
1006 cd ..
1008 else
1009 cd ..
1011 # Stop searching when in / ... hmz :P
1012 test x`pwd` = x/ && break
1013 done
1014 if [ $found -eq 0 ]; then
1015 if [ $found_committed -eq 0 ]; then
1016 abort 'Could not find the +committed directory.'
1017 else
1018 abort "Could not find the revision $REV in +committed."
1020 abort 'Internal error (should never be here).'
1023 mail_file=''; subject=''; to=''
1024 if [ -f ./+committed/$REV/mail ]; then
1025 # svn-wrapper generated file
1026 mail_file="./+committed/$REV/mail"
1027 subject=`sed '/^Subject: /!d;s///' $mail_file | sed '1q'`
1028 to=`sed '/^To: /!d;s///' $mail_file | sed '1q'`
1029 elif [ -f ./+committed/$REV/,iform ] && [ -f ./+committed/$REV/,message ]
1030 then
1031 # VCS-generated file
1032 subject=`sed '/^Subject: /!d;s///;s/^"//;s/"$//' ./+committed/$REV/,iform \
1033 | sed "s/<%= *rev *%>/$REV/g"`
1034 to=`sed '/^To:/,/^[^-]/!d' ./+committed/$REV/,iform | sed '1d;s/^- //;$d' \
1035 | xargs | sed 's/ */, /g'`
1036 mail_file=`get_unique_file_name "$TMPDIR/mail.r$REV"`
1037 echo "From: $FULLNAME <$EMAIL>
1038 To: $to
1039 Subject: $subject
1040 " >"$mail_file" || abort "Cannot create $mail_file"
1041 cat ./+committed/$REV/,message >>"$mail_file" \
1042 || abort "Cannot copy ./+committed/$REV/,message in $mail_file"
1043 else
1044 abort "Couldn't find the mail to re-send in `pwd`/+committed/$REV"
1046 if [ $# -gt 0 ]; then
1047 to=`echo "$*" | sed 's/ */, /g'`
1050 test x"$to" = x && abort 'Cannot find the list of recipients.
1051 Please report this bug.'
1052 test x"$subject" = x && abort 'Cannot find the subject of the mail.
1053 Please report this bug.'
1055 if yesno "Re-sending the mail of r$REV
1056 Subject: $subject
1057 To: $to
1058 Are you sure?"; then :; else
1059 return 1
1062 svn_info_tmp=`$SVN info`
1063 test $? -ne 0 && abort "Failed to get svn info on `pwd`"
1064 repos_root=`echo "$svn_info_tmp" | sed '/^Repository Root: /!d;s///'`
1065 # It looks like svn <1.3 didn't display a "Repository Root" entry.
1066 test x"$repos_root" = x && \
1067 repos_root=`echo "$svn_info_tmp" | sed '/^URL: /!d;s///'`
1069 my_sendmail "$mail_file" "$subject" "$to" \
1070 "X-svn-url: $repos_root
1071 X-svn-revision: $REV"
1074 # svn_version
1075 svn_version()
1077 echo "Using svn-wrapper v$version (C) SIGOURE Benoit [GPL]"
1078 sed '/^# $Id[:].*$/!d;s/.*$Id[:] *//;s/ *$ *//;s/ \([0-9][0-9]*\)/ (r\1)/' "$me"
1081 # has_prop prop-name [path]
1082 # return value: 0 -> path has the property prop-name set.
1083 # 1 -> path has no property prop-name.
1084 # 2 -> svn error.
1085 has_prop()
1087 hp_plist=`$SVN proplist "$2"`
1088 test $? -ne 0 && return 2
1089 hp_res=`echo "$hp_plist" | sed "/^ *$1\$/!d"`
1090 test x"$hp_res" = x && return 1
1091 return 0
1094 # svn_propadd prop-name prop-val [path]
1095 svn_propadd()
1097 test $# -lt 2 \
1098 && abort 'Not enough arguments provided;
1099 try `svn help propadd` for more info'
1100 test $# -gt 3 \
1101 && abort 'Too many arguments provided;
1102 try `svn help propadd` for more info'
1104 path="$3"
1105 test x"$path" = x && path='.' && set dummy "$@" '.' && shift
1106 has_prop "$1" "$3" || {
1107 test $? -eq 2 && return 1 # svn error
1108 # no property found:
1109 yesno "'$path' has no property named '$1', do you want to add it?" \
1110 && $SVN propset "$@"
1111 return $?
1114 current_prop_val=`$SVN propget "$1" "$3"`
1115 test $? -ne 0 && abort "Failed to get the current value of property '$1'."
1117 $SVN propset "$1" "$current_prop_val
1118 $2" "$3" >/dev/null || abort "Failed to add '$3' in the property '$1'."
1120 current_prop_val=`$SVN propget "$1" "$3" || echo "$current_prop_val
1121 $2"`
1122 echo "property '$1' updated on '$path', new value:
1123 $current_prop_val"
1126 # svn_propsed prop-name sed-script [path]
1127 svn_propsed()
1129 test $# -lt 2 \
1130 && abort 'Not enough arguments provided;
1131 try `svn help propsed` for more info'
1132 test $# -gt 3 \
1133 && abort 'Too many arguments provided;
1134 try `svn help propsed` for more info'
1136 path="$3"
1137 test x"$path" = x && path='.'
1138 has_prop "$1" "$3" || {
1139 test $? -eq 2 && return 1 # svn error
1140 # no property found:
1141 abort "'$path' has no property named '$1'."
1144 prop_val=`$SVN propget "$1" "$3"`
1145 test $? -ne 0 && abort "Failed to get the current value of property '$1'."
1147 prop_val=`echo "$prop_val" | sed "$2"`
1148 test $? -ne 0 && abort "Failed to run the sed script '$2'."
1150 $SVN propset "$1" "$prop_val" "$3" >/dev/null \
1151 || abort "Failed to update the property '$1' with value '$prop_val'."
1153 new_prop_val=`$SVN propget "$1" "$3" || echo "$prop_val"`
1154 echo "property '$1' updated on '$path', new value:
1155 $new_prop_val"
1158 # svn_revision [args...]
1159 svn_revision()
1161 svn_revision_info_out=`$SVN info "$@"`
1162 svn_revision_rv=$?
1163 echo "$svn_revision_info_out" | sed '/^Revision: /!d;s///'
1164 return $svn_revision_rv
1167 # svn_ignore [paths]
1168 svn_ignore()
1170 if [ $# -eq 0 ]; then # Simply display ignore-list.
1171 $SVN propget 'svn:ignore'
1172 elif [ $# -eq 1 ]; then
1173 if [ -d "$1" ]; then # Display ignore-list.
1174 $SVN propget 'svn:ignore' "$1"
1175 else # Add in ignore-list.
1176 svn_propadd 'svn:ignore' `dirname "$1"` `basename "$1"`
1178 else # Add arguments in svn:ignore.
1179 # This part is a bit tricky:
1180 # For each argument, we find all the other arguments with the same dirname
1181 # $dname and we svn:ignore them all in $dname.
1182 while [ $# -ne 0 ]; do
1183 arg="$1"
1184 dname=`dirname "$1"`
1185 files=`basename "$1"`
1186 shift
1187 j=0; argc=$#
1188 while [ $j -lt $argc ] && [ $# -ne 0 ]; do
1189 this_arg="$1"
1190 shift
1191 this_dname=`dirname "$this_arg"`
1192 this_file=`basename "$this_arg"`
1193 if [ x"$dname" = x"$this_dname" ]; then
1194 files="$files
1195 $this_file"
1196 else
1197 set dummy "$@" "$this_arg"
1198 shift
1200 j=`expr $j + 1`
1201 done
1202 svn_propadd 'svn:ignore' "$files" "$dname"
1203 done
1207 # svn_help
1208 svn_help()
1210 if [ $# -eq 0 ]; then
1211 svn_version
1212 $SVN help
1213 rv=$?
1214 echo '
1215 Additionnal commands provided by svn-wrapper:
1216 automerge (amerge, am)
1217 diffstat (ds)
1218 diffw (dw)
1219 ignore
1220 mail
1221 propadd (padd, pa)
1222 propsed (psed)
1223 revision (rev)
1224 touch
1225 selfupdate (selfup)
1226 version'
1227 return $rv
1228 else
1229 case $1 in
1230 automerge | auto-merge | amerge | am)
1231 echo 'automerge (amerge, am): FIXME
1232 usage: automerge FIXME
1234 FIXME' # FIXME
1236 diffstat | ds)
1237 require_diffstat
1238 echo 'diffstat (ds): Display the histogram from svn diff-output.'
1239 $SVN help diff | sed '1d;
1240 s/differences*/histogram/;
1241 2,35 s/diff/diffstat/g'
1243 diffw | dw)
1244 echo "diffw (dw): Display the differences without taking whitespaces\
1245 into account."
1246 $SVN help diff | sed '1d;
1247 2,35 s/diff\([^a-z]\)/diffw\1/g;
1248 /--diff-cmd/,/--no-diff-deleted/d'
1250 ignore)
1251 echo 'ignore: Add some files in the svn:ignore property.
1252 usage: 1. ignore [PATH]
1253 2. ignore FILE [FILES...] [PATH]
1255 1. Display the value of svn:ignore property on [PATH].
1256 2. Add some files in the svn:ignore property of [PATH].
1258 If you want to add directories in the ignore-list, be careful:
1259 svn ignore foo/ bar/
1260 will add "foo/" in the property svn:ignore within the directory bar!
1261 Instead use:
1262 svn ignore foo/ bar/ .
1263 (It'\''s somewhat like with mv)
1265 Valid options:
1266 None.'
1268 mail)
1269 echo 'mail: Resend the mail of a given commit.
1270 usage: mail REV [emails]
1272 REV must have an email file associated in +committed/REV.
1273 REV can also be PREV or HEAD.
1275 By default the mail is sent to same email addresses as during the original
1276 commit unless more arguments are given.'
1278 propadd | padd | pa)
1279 echo 'propadd (padd, pa): Add something in the value of a property.
1280 usage: propadd PROPNAME PROPVAL PATH
1282 PROPVAL will be appended at the end of the property PROPNAME.
1284 Valid options:
1285 None.'
1287 propsed | psed)
1288 echo 'propsed (psed): Edit a property with sed.
1289 usage: propsed PROPNAME SED-ARGS PATH
1291 eg: svn propsed svn:externals "s/http/https/" .
1293 Valid options:
1294 None.'
1296 revision | rev)
1297 echo 'revision (rev): Display the revision number of a local or remote item.'
1298 $SVN help info | sed '1d;
1299 s/information/revision/g;
1300 s/revision about/the revision of/g;
1301 2,35 s/info/revision/g;
1302 /-xml/d'
1304 touch)
1305 echo 'touch: Touch a file and svn add it.
1306 usage: touch FILE [FILES]...
1308 Valid options:
1309 None.'
1311 selfupdate | selfup | self-update | self-up)
1312 echo 'selfupdate (selfup): Attempt to update svn-wrapper.sh
1313 usage: selfupdate
1315 Valid options:
1316 None.'
1318 version)
1319 echo 'version: Display the version info of svn and svn-wrapper.
1320 usage: version
1322 Valid options:
1323 None.'
1325 *) $SVN help "$@";;
1326 esac
1330 # svn_status [args...]
1331 svn_status()
1333 svn_status_out=`$SVN status "$@"`
1334 svn_status_rv=$?
1335 test x"$svn_status_out" = x && return $svn_status_rv
1336 echo "$svn_status_out" | sed "$sed_svn_st_color"
1337 return $svn_status_rv
1340 # svn_update [args...]
1341 svn_update()
1343 svn_update_out=`$SVN update "$@"`
1344 svn_update_rv=$?
1345 echo "$svn_update_out" | sed "$sed_svn_up_colors"
1346 return $svn_update_rv
1349 # ------------------- #
1350 # `main' starts here. #
1351 # ------------------- #
1353 # Define colors if stdout is a tty.
1354 if test -t 1; then
1355 set_colors
1356 else # stdout isn't a tty => don't print colors.
1357 set_nocolors
1360 # Consider this as a sed function :P.
1361 sed_svn_st_color="
1362 t dummy_sed_1
1363 : dummy_sed_1
1364 s@^?\\(......\\)+@+\\1+@
1365 s@^?\\(......\\)\\(.*/\\)+@+\\1\\2+@
1366 s@^?\\(......\\),@,\\1,@
1367 s@^?\\(......\\)\\(.*/\\),@,\\1\\2,@
1368 s/^\\(.\\)C/\\1${lred}C${std}/
1369 t dummy_sed_2
1370 : dummy_sed_2
1371 s/^?/${lred}?${std}/; t
1372 s/^M/${lgreen}M${std}/; t
1373 s/^A/${lgreen}A${std}/; t
1374 s/^X/${lblue}X${std}/; t
1375 s/^+/${lyellow}+${std}/; t
1376 s/^D/${lyellow}D${std}/; t
1377 s/^,/${lred},${std}/; t
1378 s/^C/${lred}C${std}/; t
1379 s/^I/${purple}I${std}/; t
1380 s/^R/${lblue}R${std}/; t
1381 s/^!/${lred}!${std}/; t
1382 s/^~/${lwhite}~${std}/; t"
1384 sed_svn_up_colors="
1385 t dummy_sed_1
1386 : dummy_sed_1
1388 /^Updated/ t
1389 /^Fetching/ t
1390 /^External/ t
1391 s/^\\(.\\)C/\\1${lred}C${std}/
1392 s/^\\(.\\)U/\\1${lgreen}U${std}/
1393 s/^\\(.\\)D/\\1${lred}D${std}/
1394 t dummy_sed_2
1395 : dummy_sed_2
1396 s/^A/${lgreen}A${std}/; t
1397 s/^U/${lgreen}U${std}/; t
1398 s/^D/${lyellow}D${std}/; t
1399 s/^G/${purple}G${std}/; t
1400 s/^C/${lred}C${std}/; t"
1402 # For dev's:
1403 test "x$1" = x--debug && shift && set -x
1405 case "$1" in
1406 # ------------------------------- #
1407 # Hooks for standard SVN commands #
1408 # ------------------------------- #
1409 commit | ci)
1410 shift
1411 svn_commit "$@"
1413 help | \? | h)
1414 shift
1415 svn_help "$@"
1417 # FIXME: Incomplete
1418 # merge)
1419 # shift
1420 # svn_merge "$@"
1421 # ;;
1422 status | stat | st)
1423 shift
1424 svn_status "$@"
1426 update | up)
1427 shift
1428 svn_update "$@"
1430 # -------------------- #
1431 # Custom SVN commands #
1432 # -------------------- #
1433 automerge | auto-merge | amerge | am)
1434 shift
1435 if [ $# -ne 2 ]; then
1436 abort "automerge: not enough arguments provided;
1437 try 'svn help automerge' for more info"
1439 svn_automerge "$@"
1441 diffstat | ds)
1442 shift
1443 require_diffstat && $SVN diff "$@" | diffstat
1445 diffw | dw)
1446 shift
1447 svn_diffw "$@"
1449 ignore)
1450 shift
1451 svn_ignore "$@"
1453 mail)
1454 shift
1455 svn_mail "$@"
1457 propadd | padd | pa)
1458 shift
1459 svn_propadd "$@"
1461 propsed | psed)
1462 shift
1463 svn_propsed "$@"
1465 revision | rev)
1466 shift
1467 svn_revision "$@"
1469 touch)
1470 shift
1471 touch "$@" && svn add "$@"
1473 selfupdate | selfup | self-update | self-up)
1474 shift
1475 selfupdate "$@"
1477 version | -version | --version)
1478 shift
1479 set dummy '--version' "$@"
1480 shift
1481 svn_version
1482 exec $SVN "$@"
1484 *) exec $SVN "$@"
1486 esac