svn_commit: Regenerate the diff after the commit. This way we are sure the
[svn-wrapper.git] / svn-wrapper.sh
blobc423c062d864cf46d2f182b37abb5f43cafa1996
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=missing}
80 export EDITOR
81 : ${GPG=gpg}
82 : ${TMPDIR=/tmp}
83 export TMPDIR
84 : ${PAGER=missing}
85 export PAGER
86 # Override the locale.
87 LC_ALL='C'
88 export LC_ALL
89 : ${AWK=missing}
91 # Pitfall: some users might be tempted to export SVN=svn-wrapper.sh for some
92 # reason. This is just *wrong*. The following is an attempt to save them from
93 # some troubles.
94 if [ x`basename "$SVN"` = 'xsvn-wrapper.sh' ]; then
95 SVN='svn'
98 # This code comes (mostly) from Autoconf.
99 # The user is always right.
100 if test "${PATH_SEPARATOR+set}" != set; then
101 trap "echo SIGINT; rm -f '$TMPDIR/conf$$.sh'; exit 130" SIGINT
102 echo "#! /bin/sh" >"$TMPDIR/conf$$.sh"
103 echo "exit 0" >>"$TMPDIR/conf$$.sh"
104 chmod +x "$TMPDIR/conf$$.sh"
105 if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
106 PATH_SEPARATOR=';'
107 else
108 PATH_SEPARATOR=:
110 rm -f "$TMPDIR/conf$$.sh"
111 # "Remove" the trap
112 trap 'echo SIGINT; exit 130' SIGINT
115 version='0.3'
116 revision=`sed '/^# $Id[:].*$/!d;
117 s/.*$Id[:] *//;
118 s/ *$ *//;
119 s/[^0-9]*\([0-9][0-9]*\).*/\1/' "$0"`
120 me="$0"
122 # The `main' really starts after the functions definitions.
124 # ---------------- #
125 # Helper functions #
126 # ---------------- #
128 set_colors()
130 red='\e[0;31m'; lred='\e[1;31m'
131 green='\e[0;32m'; lgreen='\e[1;32m'
132 yellow='\e[0;33m'; lyellow='\e[1;33m'
133 blue='\e[0;34m'; lblue='\e[1;34m'
134 purple='\e[0;35m'; lpurple='\e[1;35m'
135 cyan='\e[0;36m'; lcyan='\e[1;36m'
136 grey='\e[0;37m'; lgrey='\e[1;37m'
137 white='\e[0;38m'; lwhite='\e[1;38m'
138 std='\e[m'
141 set_nocolors()
143 red=''; lred=''
144 green=''; lgreen=''
145 yellow=''; lyellow=''
146 blue=''; lblue=''
147 purple=''; lpurple=''
148 cyan=''; lcyan=''
149 grey=''; lgrey=''
150 white=''; lwhite=''
151 std=''
154 # abort err-msg
155 abort()
157 echo "svn-wrapper: ${lred}abort${std}: $@" \
158 | sed '1!s/^[ ]*/ /' >&2
159 exit 1
162 # warn msg
163 warn()
165 echo "svn-wrapper: ${lred}warning${std}: $@" \
166 | sed '1!s/^[ ]*/ /' >&2
169 # notice msg
170 notice()
172 echo "svn-wrapper: ${lyellow}notice${std}: $@" \
173 | sed '1!s/^[ ]*/ /' >&2
176 # yesno question
177 yesno()
179 echo -n "$@ [y/N] "
180 read answer || return 1
181 case "$answer" in
182 y* | Y*) return 0;;
183 *) return 1;;
184 esac
185 return 42 # should never happen...
188 # warn_env env-var
189 warn_env()
191 warn "cannot find the environment variable $1
192 You might consider using \`export $1 <FIXME>\`"
195 # get_unique_file_name file-name
196 get_unique_file_name()
198 test -e "$1" || {
199 echo "$1" && return 0
201 gufn="$1"; i=1
202 while test -e "$gufn.$i"; do
203 i=`expr $i + 1`
204 done
205 echo "$gufn.$i"
208 # ensure_not_empty description value
209 ensure_not_empty()
211 ene_val=`echo "$2" | tr -d ' \t\n'`
212 test x"$ene_val" = x && abort "$1: empty value"
215 # find_prog prog-name
216 # return true if prog-name is in the PATH
217 # echo the full path to prog-name on stdout.
218 # Based on a code from texi2dvi
219 find_prog()
221 save_IFS=$IFS
222 IFS=$PATH_SEPARATOR
223 for dir in $PATH; do
224 IFS=$save_IFS
225 test x"$dir" = x && continue
226 # The basic test for an executable is `test -f $f && test -x $f'.
227 # (`test -x' is not enough, because it can also be true for directories.)
228 # We have to try this both for $1 and $1.exe.
230 # Note: On Cygwin and DJGPP, `test -x' also looks for .exe. On Cygwin,
231 # also `test -f' has this enhancement, bot not on DJGPP. (Both are
232 # design decisions, so there is little chance to make them consistent.)
233 # Thusly, it seems to be difficult to make use of these enhancements.
235 if test -f "$dir/$1" && test -x "$dir/$1"; then
236 echo "$dir/$1"
237 return 0
238 elif test -f "$dir/$1.exe" && test -x "$dir/$1.exe"; then
239 echo "$dir/$1.exe"
240 return 0
242 done
243 return 1
246 # find_progs prog [progs...]
247 # Look in PATH for one of the programs given in argument.
248 # If none of the progs can be found, the string "exit 2" is "returned".
249 find_progs()
251 # This code comes mostly from Autoconf.
252 for fp_prog in "$@"; do
253 fp_res=`find_prog $fp_prog`
254 if [ $? -eq 0 ]; then
255 echo "$fp_res"
256 return 0
258 done
259 echo "exit 2"
260 return 1
263 test x"$EDITOR" = xmissing && EDITOR=`find_progs vim vi emacs nano`
264 test x"$PAGER" = xmissing && PAGER=`find_progs less more`
265 test x"$AWK" = xmissing && AWK=`find_progs gawk mawk nawk awk`
267 # require_diffstat
268 # return true if diffstat is in the PATH
269 require_diffstat()
271 if [ x"$require_diffstat_cache" != x ]; then
272 return $require_diffstat_cache
274 if (echo | diffstat) >/dev/null 2>/dev/null; then :; else
275 warn 'diffstat is not installed on your system or not in your PATH.'
276 test -f /etc/debian_version \
277 && notice 'you might want to `apt-get install diffstat`.'
278 require_diffstat_cache=1
279 return 1
281 require_diffstat_cache=0
282 return 0
285 # require_mail
286 # return 0 -> found sendmail
287 # 1 -> found mail
288 # 2 -> no mailer found
289 # The full path to the program found is echo'ed on stdout.
290 require_mail()
292 if [ x"$require_mail_cache_rv" != x ]; then
293 echo "$require_mail_cache"
294 return $require_mail_cache_rv
296 save_PATH="$PATH"
297 PATH="${PATH}${PATH_SEPARATOR}/sbin${PATH_SEPARATOR}/usr/sbin${PATH_SEPARATOR}/usr/libexec"
298 export PATH
299 require_mail_cache=`find_prog sendmail`
300 if [ $? -eq 0 ] && [ x"$require_mail_cache" != x ]; then
301 echo "$require_mail_cache"
302 require_mail_cache_rv=0
303 return 0
305 PATH="$save_PATH"
306 export PATH
307 require_mail_cache=`find_prog mail`
308 if [ $? -eq 0 ] && [ x"$require_mail_cache" != x ]; then
309 echo "$require_mail_cache"
310 require_mail_cache_rv=1
311 return 1
312 else
313 warn 'mail is not installed on your system or not in your PATH.'
314 test -f /etc/debian_version \
315 && notice 'you might want to:
316 # apt-get install mailx
317 # dpkg-reconfigure exim'
318 require_mail_cache=''
319 require_mail_cache_rv=1
320 return 1
322 require_mail_cache=''
323 require_mail_cache_rv=42
324 return 42
327 # my_sendmail mail-file mail-subject mail-to [extra-headers]
328 # mail-to is a comma-separated list of email addresses.
329 # extra-headers is an optionnal argument and will be prepended at the
330 # beginning of the mail headers if the tool used to send mails supports it.
331 # The mail-file may also contain headers. They must be separated from the body
332 # of the mail by a blank line.
333 my_sendmail()
335 test -f "$1" || abort "my_sendmail: Cannot find the mail file: $1"
336 test x"$2" = x && warn 'my_sendmail: Empty subject.'
337 test x"$3" = x && abort 'my_sendmail: No recipient specified.'
339 extra_headers="Content-type: text/plain
340 X-Mailer: svn-wrapper v$version (r$revision)"
341 if test x"$4" != x; then
342 extra_headers="$4
343 $extra_headers"
344 # Remove empty lines.
345 extra_headers=`echo "$extra_headers" | sed '/^[ ]*$/d;s/^[ ]*//'`
348 test -f ~/.signature && echo '-- ' >>"$1" && \
349 cat ~/.signature >>"$1"
350 # VCS-compat: handle user option 'sign'.
351 if (grep '^sign: false' ~/.vcs) >/dev/null 2>/dev/null; then :; else
352 ($GPG -h) >/dev/null 2>/dev/null
353 gpg_rv=$?
354 if [ -e ~/.gnupg ] || [ -e ~/.gpg ] || [ -e ~/.pgp ] && [ $gpg_rv -lt 42 ]
355 then
356 if yesno "Sign the mail using $GPG ?"; then
357 # GPG sign the mail with the headers stripped.
358 cat "$1" | sed '1,/^$/d' | $GPG --clearsign >"$1.asc"
359 # Re-paste the headers before the signed body.
360 test -f "$1.asc" \
361 && cat "$1" | sed '1,/^$/!d' | cat - "$1.asc" >"$1.tmp" \
362 && mv -f "$1.tmp" "$1"
363 # Cleanup.
364 rm -f "$1.tmp" "$1.asc"
369 mailer=`require_mail`
370 r_mail_rv=$?
371 if [ $? -ge 2 ] || [ x"$mailer" = x ]; then
372 warn 'my_sendmail: No suitable mailer found.'
373 return 1
375 case $r_mail_rv in
376 0) # sendmail
377 to=`echo "$3" | sed 's/,//g'`
378 echo "$extra_headers" | cat - "$1" | $mailer $to;;
379 1) # mail
380 cat "$1" | $mailer -s "$2" "$3";;
381 *) # wtf
382 warn 'my_sendmail: Internal error.'; return 42;;
383 esac
386 # selfupdate
387 selfupdate()
389 my_url='http://www.lrde.epita.fr/~sigoure/svn-wrapper'
390 # You can use https if you feel paranoiac.
392 echo ">>> Fetching svn-wrapper.sh from $my_url/svn-wrapper.sh"
394 # --------------------- #
395 # Fetch the new version #
396 # --------------------- #
398 tmp_me=`get_unique_file_name "$TMPDIR/svn-wrapper.sh"`
399 if (wget --help) >/dev/null 2>/dev/null; then
400 wget --no-check-certificate "$my_url/svn-wrapper.sh" -O "$tmp_me"
401 my_wget='wget'
402 else
403 curl --help >/dev/null 2>/dev/null
404 if [ $? -gt 42 ]; then
405 abort 'Cannot find wget or curl.
406 How can I download any update without them?'
408 my_wget='curl'
409 curl --insecure "$my_url/svn-wrapper.sh" >"$tmp_me"
412 test -r $tmp_me \
413 || abort "Cannot find the copy of myself I downloaded in $tmp_me"
415 # ---------------------------------------- #
416 # Compare versions and update if necessary #
417 # ---------------------------------------- #
419 my_ver="$revision"
420 tmp_ver=`sed '/^# $Id[:].*$/!d;
421 s/.*$Id[:] *//;
422 s/ *$ *//;
423 s/[^0-9]*\([0-9][0-9]*\).*/\1/' "$tmp_me"`
424 test x"$tmp_ver" = x && abort "Cannot find the version of $tmp_me"
425 if [ "$my_ver" -lt "$tmp_ver" ]; then # There IS an update...
426 echo "An update is available, r$tmp_ver (your version is r$my_ver)"
428 # See the ChangeLog?
429 if yesno "Do you want to see the ChangeLog between r$my_ver and r$tmp_ver?"
430 then
431 my_chlog=`get_unique_file_name "$TMPDIR/ChangeLog"`
432 case $my_wget in
433 wget) wget --no-check-certificate "$my_url/ChangeLog" -O "$my_chlog"
435 curl) curl --insecure "$my_url/ChangeLog" >"$my_chlog"
437 *) abort 'Should never be here.'
439 esac
440 sed "/^r$my_ver/q" "$my_chlog" | $PAGER
441 rm -f "$my_chlog"
444 # Wanna see the diff?
445 if yesno "Do you want to see the diff between r$my_ver and r$tmp_ver?"
446 then
447 (require_diffstat && diff -uw "$me" "$tmp_me" | diffstat;
448 echo
449 diff -uw "$me" "$tmp_me") | $PAGER
452 # Let's go :)
453 if yesno "Overwrite $me (r$my_ver) with $tmp_me (r$tmp_ver)?"; then
454 chmod a+x "$tmp_me"
455 cp -p "$me" "$me.r$my_ver"
456 mv "$tmp_me" "$me" && exit 0
458 rm -f "$tmp_me"
459 return 1
460 elif [ "$my_ver" -gt "$tmp_ver" ]; then
461 echo "Wow, you're more up to date than the master copy :)"
462 echo "Your version is r$my_ver and the master copy is r$tmp_ver."
463 if yesno 'Downgrade?'; then
464 chmod a+x "$tmp_me"
465 mv "$tmp_me" "$me" && exit 0
467 else
468 echo "You're already up to date [r$my_ver] :)"
470 rm -f "$tmp_me"
473 # ------------------------------- #
474 # Hooks for standard SVN commands #
475 # ------------------------------- #
477 # svn_commit [args...]
478 # Here is how the commit process goes:
480 # First we look in the arguments passed to commit:
481 # If there are some files or paths, the user wants to commit these only. In
482 # this case, we must search for ChangeLogs from these paths. We might find
483 # more than one ChangeLog, in this case the user will be prompted to pick up
484 # one.
485 # Otherwise (no path passed in the command line) the user just wants to
486 # commit the current working directory.
487 # In any case, we schedule "ChangeLog" for commit.
489 # Alright now that we know which ChangeLog to use, we look in the ChangeLog's
490 # directory if there is a ",svn-log" file which would mean that a previous
491 # commit didn't finish successfully. If there is such a file, the user is
492 # prompted to know whether they want to resume that commit or simply start a
493 # new one.
494 # When the user wants to resume a commit, the ",svn-log" file is loaded and we
495 # retrieve the value of "$@" that was saved in the file.
496 # Otherwise we build a template ChangeLog entry.
497 # Then we open the template ChangeLog entry with $EDITOR so that the user
498 # fills it properly.
499 # Finally, we commit.
500 # Once the commit is sent, we ask the server to know which revision was
501 # commited and we also retrieve the diff. We then send a mail with these.
502 svn_commit()
504 here=`pwd -P`
506 # Check if the user passed some paths to commit explicitly
507 # because in this case we must add the ChangeLog to the commit and search
508 # the ChangeLog from the dirname of that file.
509 i=0; search_from=''; add_changelog=no; extra_files=''
510 while [ $i -lt $# ]; do
511 arg="$1"
512 # If the argument is a valid path: add the ChangeLog in the list of
513 # files to commit
514 if test -e "$arg"; then
515 add_changelog=yes
516 if test -d "$arg"; then
517 search_from_add="$arg"
518 else
519 search_from_add=`dirname "$arg"`
521 search_from="$search_from:$search_from_add"
523 shift
524 set dummy "$@" "$arg"
525 shift
526 i=`expr $i + 1`
527 done
528 if [ $add_changelog = no ]; then
529 # There is no path/file in the command line: the user wants to commit the
530 # current directory. Make it explicit now:
531 extra_files="$here"
533 search_from=`echo "$search_from" | sed 's/^://; s/^$/./'`
535 # ----------------- #
536 # Find ChangeLog(s) #
537 # ----------------- #
539 nb_chlogs=0; change_log_dirs=''
540 save_IFS=$IFS; IFS=':'
541 for dir in $search_from; do
542 IFS=$save_IFS
543 test -z "$dir" && dir='.'
544 # First: come back to the original place
545 cd "$here" || abort "Cannot cd to $here"
546 cd "$dir" || continue # Then: Enter $dir (which can be a relative path)
547 found=0
548 while [ $found -eq 0 ]; do
549 this_chlog_dir=`pwd -P`
550 if [ -f ./ChangeLog ]; then
551 found=1
552 nb_chlogs=`expr $nb_chlogs + 1`
553 change_log_dirs="$change_log_dirs:$this_chlog_dir"
554 else
555 cd ..
557 # Stop searching when in / ... hmz :P
558 test x"$this_chlog_dir" = x/ && break
559 done # end while: did we find a ChangeLog
560 done # end for: find ChangeLogs in $search_from
561 if [ $nb_chlogs -gt 0 ]; then
562 change_log_dirs=`echo "$change_log_dirs" | sed 's/^://' | tr ':' '\n' \
563 | sort -u`
564 nb_chlogs=`echo "$change_log_dirs" | wc -l`
567 # Did we find a ChangeLog? More than one?
568 if [ $nb_chlogs -eq 0 ]; then
569 if yesno 'svn-wrapper: Error: Cannot find a ChangeLog file!
570 You might want to create an empty one (eg: `touch ChangeLog` where appropriate)
571 Do you want to proceed without using a ChangeLog?'; then
572 cd "$here"
573 $SVN commit "$@"
574 return $?
575 else
576 return 1
578 elif [ $nb_chlogs -gt 1 ]; then
579 notice "$nb_chlogs ChangeLogs were found, pick up one:"
581 IFS=':'; i=0
582 for a_chlog_dir in $change_log_dirs; do
583 i=`expr $i + 1`
584 echo "$i. $a_chlog_dir/ChangeLog"
585 done
586 echo -n "Which ChangeLog do you want to use? [1-$i] "
587 read chlog_no || abort 'Cannot read answer on stdin.'
589 case "$chlog_no" in
590 *[^0-9]*) abort "Invalid ChangeLog number: $chlog_no"
591 esac
592 test "$chlog_no" -le $i || abort "Invalid ChangeLog number: $chlog_no
593 max value was: $i"
594 test "$chlog_no" -ge 1 || abort "Invalid ChangeLog number: $chlog_no
595 min value was: 1"
596 change_log_dir=`echo "$change_log_dirs" | tr ':' '\n' | sed "${chlog_no}!d"`
597 else # Only one ChangeLog found
598 change_log_dir=$change_log_dirs
599 notice "using $change_log_dir/ChangeLog"
602 test -f "$change_log_dir/ChangeLog" \
603 || abort "No such file or directory: $change_log_dir/ChangeLog"
605 # Now we can safely schedule the ChangeLog for the commit.
606 extra_files="$extra_files:$change_log_dir/ChangeLog"
608 svn_st_tmp=`$SVN status "$change_log_dir"`
610 # Warn for files that are not added in the repos.
611 conflicts=`echo "$svn_st_tmp" | sed '/^ *$/d;
612 /^[^?]/d;
613 /^?.......*\/[,+]/d;
614 /^?......[,+]/d'`
615 if test x"$conflicts" != x; then
616 warn "make sure you don't want to \`svn add'
617 any of the following files before committing:"
618 echo "$conflicts" | sed "$sed_svn_st_color"
619 echo -n 'Type [ENTER] to continue :)' && read chiche_is_gay
622 # If there are changes in an svn:externals, advise the user to commit that
623 # first.
624 changed_externals=`echo "$svn_st_tmp" | $AWK \
625 'function printext()
627 if (ext && !printed)
629 print this_ext "\n";
630 printed = 1;
633 BEGIN { this_ext = ""; ext = 0; ext_modified = 0; }
634 /^Performing status on external/ {
635 ext = 1;
636 sub(/.* at ./, ""); sub(/.$/, ""); this_ext = $0;
637 printed = 0;
639 /^[ADMR]/ { ext_modified = ext; printext(); }
640 /^.[M]/ { ext_modified = ext; printext(); }
641 END { exit ext_modified; }'`
642 if [ $? -ne 0 ]; then
643 warn "the following external items have local modifications:
644 $changed_externals"
645 yesno "You are advised to commit them separately first. Continue anyway?" \
646 || return 1
649 # Detect unresolved conflicts / missing files.
650 conflicts=`echo "$svn_st_tmp" | sed '/^[C!]/!d'`
651 test x"$conflicts" != x && abort "there are unresolved conflicts (\`C')
652 and/or missing files (\`!'):
653 $conflicts"
655 svn_info_tmp=`$SVN info "$change_log_dir"`
656 test $? -ne 0 && abort "Failed to get svn info on $change_log_dir"
657 repos_root=`echo "$svn_info_tmp" | sed '/^Repository Root: /!d;s///'`
658 repos_url=`echo "$svn_info_tmp" | sed '/^URL: /!d;s///'`
659 # It looks like svn <1.3 didn't display a "Repository Root" entry.
660 test x"$repos_root" = x && repos_root=$repos_url
661 cd "$here"
663 YYYY=`date '+%Y'`
664 MM=`date '+%m'`
665 DD=`date '+%d'`
667 # VCS-compat: handle user option 'new_user'
668 new_user='yes'
669 grep '^new_user: false' ~/.vcs >/dev/null 2>/dev/null && new_user='no'
671 tmp_log="$change_log_dir/,svn-log"
672 if [ -f "$tmp_log" ] && yesno "It looks like the last commit did not\
673 terminate successfully.
674 Would you like to resume it?"; then
675 echo 'Resuming ...'
676 internal_tags=`sed '/^--- Internal stuff, DO NOT change please ---$/,$!d' \
677 "$tmp_log"`
678 saved_args=`echo "$internal_tags" | sed '/^args: */!d;s///'`
679 extra_files=`echo "$internal_tags" | sed '/^extra_files: */!d;s///'`
680 if [ x"$saved_args" != x ]; then
681 if [ x"$*" != x ] && [ x"$saved_args" != x"$*" ]; then
682 warn "overriding arguments:
683 you invoked $me with the following arguments: $@
684 they have been replaced by these: $saved_args"
685 set dummy $saved_args
686 shift
687 else
688 notice "setting the following arguments: $saved_args"
689 set dummy $saved_args
690 shift
693 # Ignore white spaces. Can't svn diff -x -w: svn 1.4 only.
694 svn_diff=`svn_diffw "$@"`
695 test x"$svn_diff" = x && svn_diff=`$SVN diff "$@"`
696 if require_diffstat; then
697 svn_diff_stat=`echo "$svn_diff" | diffstat`
698 else
699 svn_diff_stat='diffstat not available'
702 # Update the file with the new diff/diffstat in case it changed.
703 $AWK 'BEGIN {
704 tlatbwbi_seen = 0;
705 ycewah_seen = 0;
707 /^--This line, and those below, will be ignored--$/ {
708 tlatbwbi_seen = 1;
710 /^ Your ChangeLog entry will appear here\.$/ {
711 if (tlatbwbi_seen) ycewah_seen = 1;
714 if (ycewah_seen != 2) print;
715 if (ycewah_seen == 1) ycewah_seen = 2;
716 }' "$tmp_log" >"$tmp_log.tmp"
717 echo "
718 $svn_diff_stat
720 $svn_diff
722 $internal_tags" >>"$tmp_log.tmp"
723 mv -f "$tmp_log.tmp" "$tmp_log"
725 else # Build the template message.
727 # ------------------------------------ #
728 # Gather info for the template message #
729 # ------------------------------------ #
731 projname=`$SVN propget project "$change_log_dir"`
732 # Try to be VCS-compatible and find a project name in a *.rb.
733 if [ x"$projname" = x ] && [ -d "$change_log_dir/vcs" ]; then
734 projname=`sed '/common_commit/!d;s/.*"\(.*\)<%= rev.*/\1/' \
735 "$change_log_dir"/vcs/*.rb`
736 test x"$projname" != x && test x$new_user = xyes \
737 && notice "VCS-compat: found project name: $projname
738 in " "$change_log_dir"/vcs/*.rb
740 test x"$projname" != x && projname=`echo "$projname" | sed '/[^ ]$/s/$/ /'`
742 mailto=`$SVN propget mailto "$change_log_dir"`
743 if [ x"$mailto" = x ]; then
744 test x$new_user = xyes \
745 && warn "no svn property mailto found in $change_log_dir
746 You might want to set default email adresses using:
747 svn propset mailto 'somebody@mail.com, foobar@example.com'\
748 $change_log_dir" >&2
749 # Try to be VCS-compatible and find a list of mails in a *.rb.
750 if [ -d "$change_log_dir/vcs" ]; then
751 mailto=`grep '.@.*\..*' "$change_log_dir"/vcs/*.rb \
752 | tr '\n' ' ' \
753 | sed 's/^.*[["]\([^["]*@[^]"]*\)[]"].*$/\1/' | xargs`
754 test x"$mailto" != x && test x$new_user = xyes \
755 && notice "VCS-compat: found mailto: $mailto
756 in " "$change_log_dir"/vcs/*.rb
757 fi # end VCS compat
758 fi # end guess mailto
760 # Ensure that emails are comma-separated.
761 mailto=`echo "$mailto" | sed 's/[ ;]/,/g' | tr -s ',' | sed 's/,/, /g'`
762 test x"$FULLNAME" = x && FULLNAME='Type Your Name Here' \
763 && warn_env FULLNAME
764 test x"$EMAIL" = x && EMAIL='your.mail.here@FIXME.com' && warn_env EMAIL
766 # --ignore-externals appeared after svn 1.1.1
767 my_svn_st=`$SVN status --ignore-externals "$@" \
768 || $SVN status "$@" | sed '/^Performing status on external/ {
772 # Files to put in the ChangeLog entry.
773 change_log_files=`echo "$my_svn_st" | sed '
774 t dummy_sed_1
775 : dummy_sed_1
776 s/^M......\(.*\)$/ * \1: ./; t
777 s/^A......\(.*\)$/ * \1: New./; t
778 s/^D......\(.*\)$/ * \1: Remove./; t
781 if [ x"$change_log_files" = x ]; then
782 yesno 'Nothing to commit, continue anyway?' || return 1
784 change_log_files=`echo "$change_log_files" | sort -u`
786 svn_diff=`svn_diffw "$@"`
787 test x"$svn_diff" = x && svn_diff=`$SVN diff "$@"`
788 if require_diffstat; then
789 svn_diff_stat=`echo "$svn_diff" | diffstat`
790 else
791 svn_diff_stat='diffstat not available'
794 # Get any older svn-log out of the way.
795 test -f "$tmp_log" && mv "$tmp_log" `get_unique_file_name "$tmp_log"`
796 # If we can't get an older svn-log out of the way, find a new name...
797 test -f "$tmp_log" && tmp_log=`get_unique_file_name "$tmp_log"`
798 if [ x$new_user = no ]; then
799 commit_instructions='
800 Instructions:
801 - Fill the ChangeLog entry.
802 - If you feel like, write a comment in the "Comment:" section.
803 This comment will only appear in the email, not in the ChangeLog.
804 By default only the location of the repository is in the comment.
805 - Some tags will be replaced. Tags are of the form: <TAG>. Unknown
806 tags will be left unchanged.
807 - The tag <REV> may only be used in the Subject.
808 - Your ChangeLog entry will be used as commit message for svn.'
809 else
810 commit_instructions=''
812 echo "\
813 --You must fill this file correctly to continue-- -*- vcs -*-
814 Title:
815 Subject: ${projname}r<REV>: <TITLE>
816 From: $FULLNAME <$EMAIL>
817 To: $mailto
819 Comment:
820 URL: $repos_url
822 ChangeLog:
824 <YYYY>-<MM>-<DD> $FULLNAME <$EMAIL>
826 <TITLE>
827 $change_log_files
829 --This line, and those below, will be ignored--
830 $commit_instructions
831 --Preview of the message that will be sent--
833 URL: $repos_url
834 Your comments (if any) will appear here.
836 ChangeLog:
837 $YYYY-$MM-$DD $FULLNAME <$EMAIL>
839 Your ChangeLog entry will appear here.
841 $svn_diff_stat
843 $svn_diff" >"$tmp_log"
845 echo "
846 --- Internal stuff, DO NOT change please ---
847 args: $@" >>"$tmp_log"
848 echo "extra_files: $extra_files
849 vi: ft=diff:noet:" >>"$tmp_log"
851 fi # end: if svn-log; then resume? else create template
852 $EDITOR "$tmp_log"
854 # ------------------ #
855 # Re-"parse" the log #
856 # ------------------ #
858 # hmz this section is a bit messy...
859 sed_escape='s/@/\\@/g;s/&/\\\&/g' # helper string... !@#$%* escaping \\\\\\...
860 sed_eval_tags="s/<MM>/$MM/g; s/<DD>/$DD/g; s/<YYYY>/$YYYY/g"
861 full_log=`sed '/^--This line, and those below, will be ignored--$/,$d;
862 /^--You must fill this/d' "$tmp_log"`
863 chlog_entry=`echo "$full_log" | sed '/^ChangeLog:$/,$!d; //d'`
864 ensure_not_empty 'ChangeLog entry' "$chlog_entry"
865 full_log=`echo "$full_log" | sed '/^ChangeLog:$/,$d'`
866 mail_comment=`echo "$full_log" | sed '/^Comment:$/,$!d; //d'`
867 full_log=`echo "$full_log" | sed '/^Comment:$/,$d'`
868 mail_title=`echo "$full_log" | sed '/^Title: */!d;s///'`
869 ensure_not_empty 'commit title' "$mail_title"
870 mail_title=`echo "$mail_title" | sed "$sed_eval_tags; $sed_escape"`
871 sed_eval_tags="$sed_eval_tags; s@<TITLE>@$mail_title@g"
872 mail_comment=`echo "$mail_comment" | sed "$sed_eval_tags"`
873 chlog_entry=`echo "$chlog_entry" | sed "$sed_eval_tags; 1{
874 /^ *$/d
876 mail_subject=`echo "$full_log" | sed '/^Subject: */!d;s///'`
877 ensure_not_empty 'mail subject' "$mail_subject"
878 mail_to=`echo "$full_log" | sed '/^To:/!d'`
879 send_a_mail=yes
880 if test x"$mail_to" = x; then
881 send_a_mail=no
882 else
883 mail_to=`echo "$mail_to" | sed 's/^To: *//'`
884 ensure_not_empty '"To:" field of the mail' "$mail_to"
886 mail_from=`echo "$full_log" | sed '/^From: */!d;s///'`
887 ensure_not_empty '"From:" field of the mail' "$mail_from"
889 if echo "$chlog_entry" | grep -q '<REV>'; then
890 warn 'Using the tag <REV> anywhere else than in the Subject is deprecated.'
891 yesno 'Continue anyway?' || return 1
894 # Check whether the user passed -m | --message
895 i=0; has_message=0
896 while [ $i -lt $# ]; do
897 arg="$1"
898 # This is not really a reliable way of knowing whether -m | --message was
899 # passed but hum... Let's assume it'll do :s
900 test x"$arg" = 'x-m' && has_message=1
901 test x"$arg" = 'x--message' && has_message=1
902 shift
903 set dummy "$@" "$arg"
904 shift
905 i=`expr $i + 1`
906 done
907 if [ $has_message -eq 0 ]; then
908 my_message=`echo "$chlog_entry" | grep -v "^$YYYY-$MM-$DD" | sed '1,2 {
909 /^ *$/d
911 set dummy --message "$my_message" "$@"
912 shift
913 else
914 notice 'you are overriding the commit message.'
917 # Are you sure?
918 yesno 'Are you sure you want to commit?' || return 1
920 # Add the ChangeLog entry
921 old_chlog=`get_unique_file_name "$change_log_dir/ChangeLog.old"`
922 mv "$change_log_dir/ChangeLog" "$old_chlog" || \
923 abort 'Could not backup ChangeLog'
924 trap "echo SIGINT; mv \"$old_chlog\" \"$change_log_dir/ChangeLog\";
925 exit 130" SIGINT
926 echo "$chlog_entry" >"$change_log_dir/ChangeLog"
927 echo >>"$change_log_dir/ChangeLog"
928 cat "$old_chlog" >>"$change_log_dir/ChangeLog"
930 # Add extra files such as cwd or ChangeLog to the commit.
931 tmp_sed='s/ /\\ /g' # Escape spaces for the shell.
932 extra_files=`echo "$extra_files" | sed "$tmp_sed" | tr ':' '\n'`
933 set dummy "$@" $extra_files
934 shift
936 # --Commit-- finally! :D
937 $SVN commit "$@" || {
938 svn_commit_rv=$?
939 mv "$old_chlog" "$change_log_dir/ChangeLog"
940 abort "Commit failed, $SVN returned $svn_commit_rv"
943 echo -n 'Getting the revision number... '
944 svn_info_tmp=`$SVN info "$change_log_dir/ChangeLog"`
945 REV=`echo "$svn_info_tmp" | sed '/^Revision: /!d;s///'`
946 test x"$REV" = x && REV=`echo "$svn_info_tmp" \
947 | sed '/^Last Changed Rev: /!d;s///'`
948 test x"$REV" = x && abort 'Cannot detect the current revision.'
949 echo "$REV"
951 # Let's make sure we have the real diff by asking the ChangeSet we've just
952 # committed to the server.
954 # Backup the old stuff in case we fail to get the real diff from the server
955 # for some reason...
956 save_svn_diff=$svn_diff
957 save_svn_diff_stat=$svn_diff_stat
959 # Fetch the ChangeSet and filter out the ChangeLog entry. We don't use
960 # svn diff -c because this option is not portable to older svn versions.
961 REV_MINUS_ONE=`expr "$REV" - 1`
962 svn_diff=`svn_diffw -r"$REV_MINUS_ONE:$REV" \
963 | awk '/^Index: / { if (in_chlog) in_chlog = 0; }
964 /^Index: .*ChangeLog$/ { in_chlog = 1 }
965 { if (!in_chlog) print }'`
966 if [ x"$svn_diff" = x ]; then
967 svn_diff=$save_svn_diff
968 svn_diff_stat=$save_svn_diff_stat
969 else
970 if require_diffstat; then
971 svn_diff_stat=`echo "$svn_diff" | diffstat`
972 else
973 svn_diff_stat='diffstat not available'
977 mail_subject=`echo "$mail_subject" | sed "$sed_eval_tags; s/<REV>/$REV/g"`
979 mail_file=`get_unique_file_name "$change_log_dir/+mail"`
980 echo "\
981 From: $mail_from
982 To: $mail_to
983 Subject: $mail_subject
985 $mail_comment
987 ChangeLog:
988 $chlog_entry
990 $svn_diff_stat
992 $svn_diff" | sed 's/^\.$/ ./' >"$mail_file"
993 # We change lines with only a `.' because they could mean "end-of-mail"
995 # Send the mail
996 if test x$send_a_mail = xyes; then
997 trap 'echo SIGINT; exec < /dev/null' SIGINT
998 my_sendmail "$mail_file" "$mail_subject" "$mail_to" \
999 "X-svn-url: $repos_root
1000 X-svn-revision: $REV"
1001 fi # end do we have to send a mail?
1002 rm -f "$tmp_log"
1003 rm -f "$old_chlog"
1004 save_mail_file=`echo "$mail_file" | sed 's/+//'`
1005 mkdir -p "$change_log_dir/+committed" \
1006 || warn "Couldn't mkdir -p $change_log_dir/+committed"
1007 if [ -d "$change_log_dir/vcs" ] \
1008 || [ -d "$change_log_dir/+committed" ]
1009 then
1010 mkdir -p "$change_log_dir/+committed/$REV" \
1011 && mv "$mail_file" "$change_log_dir/+committed/$REV/mail"
1013 return $svn_commit_rv
1016 # svn_merge args...
1017 svn_merge()
1019 i=0; src=''; dst=''; rev1=''; rev2=''; r_seen=0
1020 while [ $i -lt $# ]; do
1021 arg="$1"
1022 if test -d "$arg"; then
1023 if [ x"$src" = x ]; then
1024 src="$arg"
1025 elif [ x"$dst" = x ]; then
1026 dst="$arg"
1028 else
1029 case "$arg" in
1030 -r*:*) rev1=`echo "$arg" | sed 's/^[^PREVHEAD0-9]*//; s/:.*//'`
1031 rev2=`echo "$arg" | sed 's/^.*://; s/[^PREVHEAD0-9]*//'`
1033 -r) r_seen=1 ;;
1034 esac
1036 shift
1037 set dummy "$@" "$arg"
1038 shift
1039 i=`expr $i + 1`
1040 done
1041 if [ x"$src" = x ]; then
1042 exec $SVN merge "$@"
1044 test x"$dst" = x && dst='.'
1046 if yesno "Do you want to use the automerge feature for $src ?";
1047 then :; else
1048 exec $SVN merge "$@"
1050 # FIXME: Incomplete.
1053 # svn_automerge src-path dst-path
1054 svn_automerge()
1056 test -f ChangeLog || abort 'You must use automerge in the folder where the
1057 ChangeLog is.'
1058 amerge=`$SVN propget automerge`
1059 if [ x"$amerge" = x ]; then
1060 # FIXME: Use svn log --stop-on-copy to detect the original branching.
1064 svn_info_tmp=`$SVN info`
1065 REV=`echo "$svn_info_tmp" | sed '/^Revision: /!d;s///'`
1066 test x"$REV" = x && REV=`echo "$svn_info_tmp" \
1067 | sed '/^Last Changed Rev: /!d;s///'`
1068 test x"$REV" = x && abort 'Cannot detect the current revision.'
1070 # FIXME: Incomplete.
1071 if yesno "Are you sure you want to run:
1072 $SVN merge -r$amerge:$REV $1 $2 ?"; then
1073 $SVN merge "-r$amerge:$REV" "$1" "$2"
1077 # svn_diffw [args...]
1078 svn_diffw()
1080 # Ignore white spaces. Can't svn diff -x -w: svn 1.4 only.
1081 $SVN diff --no-diff-deleted --diff-cmd diff -x -uw "$@"
1084 # svn_mail REV [mails...]
1085 svn_mail()
1087 test $# -lt 1 && abort "Not enough arguments provided;
1088 Try 'svn help mail' for more info."
1089 case "$1" in
1090 PREV)
1091 REV=`svn_revision || abort 'Cannot get current revision number'`
1092 test x"$REV" = x && abort 'Cannot get current revision number'
1093 if [ "$REV" -lt 1 ]; then
1094 abort 'No previous revision.'
1096 REV=`expr "$REV" - 1`
1098 HEAD)
1099 REV=`svn_revision || abort 'Cannot get current revision number'`
1100 test x"$REV" = x && abort 'Cannot get current revision number'
1102 *[^0-9]*) abort "Syntax error in revision argument '$1'";;
1103 *) REV="$1";;
1104 esac
1105 shift
1107 found_committed=0; found=0
1108 while [ $found -eq 0 ]; do
1109 this_chlog_dir=`pwd -P`
1110 if [ -d ./+committed ]; then
1111 found_committed=1
1112 if [ -d ./+committed/$REV ]; then
1113 found=1
1114 else
1115 cd ..
1117 else
1118 cd ..
1120 # Stop searching when in / ... hmz :P
1121 test x`pwd` = x/ && break
1122 done
1123 if [ $found -eq 0 ]; then
1124 if [ $found_committed -eq 0 ]; then
1125 abort 'Could not find the +committed directory.'
1126 else
1127 abort "Could not find the revision $REV in +committed."
1129 abort 'Internal error (should never be here).'
1132 mail_file=''; subject=''; to=''
1133 if [ -f ./+committed/$REV/mail ]; then
1134 # svn-wrapper generated file
1135 mail_file="./+committed/$REV/mail"
1136 subject=`sed '/^Subject: /!d;s///' $mail_file | sed '1q'`
1137 to=`sed '/^To: /!d;s///' $mail_file | sed '1q'`
1138 elif [ -f ./+committed/$REV/,iform ] && [ -f ./+committed/$REV/,message ]
1139 then
1140 # VCS-generated file
1141 subject=`sed '/^Subject: /!d;s///;s/^"//;s/"$//' ./+committed/$REV/,iform \
1142 | sed "s/<%= *rev *%>/$REV/g"`
1143 to=`sed '/^To:/,/^[^-]/!d' ./+committed/$REV/,iform | sed '1d;s/^- //;$d' \
1144 | xargs | sed 's/ */, /g'`
1145 mail_file=`get_unique_file_name "$TMPDIR/mail.r$REV"`
1146 echo "From: $FULLNAME <$EMAIL>
1147 To: $to
1148 Subject: $subject
1149 " >"$mail_file" || abort "Cannot create $mail_file"
1150 cat ./+committed/$REV/,message >>"$mail_file" \
1151 || abort "Cannot copy ./+committed/$REV/,message in $mail_file"
1152 else
1153 abort "Couldn't find the mail to re-send in `pwd`/+committed/$REV"
1155 if [ $# -gt 0 ]; then
1156 to=`echo "$*" | sed 's/ */, /g'`
1159 test x"$to" = x && abort 'Cannot find the list of recipients.
1160 Please report this bug.'
1161 test x"$subject" = x && abort 'Cannot find the subject of the mail.
1162 Please report this bug.'
1164 if yesno "Re-sending the mail of r$REV
1165 Subject: $subject
1166 To: $to
1167 Are you sure?"; then :; else
1168 return 1
1171 svn_info_tmp=`$SVN info`
1172 test $? -ne 0 && abort "Failed to get svn info on `pwd`"
1173 repos_root=`echo "$svn_info_tmp" | sed '/^Repository Root: /!d;s///'`
1174 repos_url=`echo "$svn_info_tmp" | sed '/^URL: /!d;s///'`
1175 # It looks like svn <1.3 didn't display a "Repository Root" entry.
1176 test x"$repos_root" = x && repos_root=$repos_url
1178 my_sendmail "$mail_file" "$subject" "$to" \
1179 "X-svn-url: $repos_url
1180 X-svn-revision: $REV"
1183 # svn_version
1184 svn_version()
1186 echo "Using svn-wrapper v$version (C) SIGOURE Benoit [GPL]"
1187 sed '/^# $Id[:].*$/!d;s/.*$Id[:] *//;s/ *$ *//;s/ \([0-9][0-9]*\)/ (r\1)/' "$me"
1190 # has_prop prop-name [path]
1191 # return value: 0 -> path has the property prop-name set.
1192 # 1 -> path has no property prop-name.
1193 # 2 -> svn error.
1194 has_prop()
1196 hp_plist=`$SVN proplist "$2"`
1197 test $? -ne 0 && return 2
1198 hp_res=`echo "$hp_plist" | sed "/^ *$1\$/!d"`
1199 test x"$hp_res" = x && return 1
1200 return 0
1203 # svn_propadd prop-name prop-val [path]
1204 svn_propadd()
1206 test $# -lt 2 \
1207 && abort 'Not enough arguments provided;
1208 try `svn help propadd` for more info'
1209 test $# -gt 3 \
1210 && abort 'Too many arguments provided;
1211 try `svn help propadd` for more info'
1213 path="$3"
1214 test x"$path" = x && path='.' && set dummy "$@" '.' && shift
1215 has_prop "$1" "$3" || {
1216 test $? -eq 2 && return 1 # svn error
1217 # no property found:
1218 yesno "'$path' has no property named '$1', do you want to add it?" \
1219 && $SVN propset "$@"
1220 return $?
1223 current_prop_val=`$SVN propget "$1" "$3"`
1224 test $? -ne 0 && abort "Failed to get the current value of property '$1'."
1226 $SVN propset "$1" "$current_prop_val
1227 $2" "$3" >/dev/null || abort "Failed to add '$3' in the property '$1'."
1229 current_prop_val=`$SVN propget "$1" "$3" || echo "$current_prop_val
1230 $2"`
1231 echo "property '$1' updated on '$path', new value:
1232 $current_prop_val"
1235 # svn_propsed prop-name sed-script [path]
1236 svn_propsed()
1238 test $# -lt 2 \
1239 && abort 'Not enough arguments provided;
1240 try `svn help propsed` for more info'
1241 test $# -gt 3 \
1242 && abort 'Too many arguments provided;
1243 try `svn help propsed` for more info'
1245 path="$3"
1246 test x"$path" = x && path='.'
1247 has_prop "$1" "$3" || {
1248 test $? -eq 2 && return 1 # svn error
1249 # no property found:
1250 abort "'$path' has no property named '$1'."
1253 prop_val=`$SVN propget "$1" "$3"`
1254 test $? -ne 0 && abort "Failed to get the current value of property '$1'."
1256 prop_val=`echo "$prop_val" | sed "$2"`
1257 test $? -ne 0 && abort "Failed to run the sed script '$2'."
1259 $SVN propset "$1" "$prop_val" "$3" >/dev/null \
1260 || abort "Failed to update the property '$1' with value '$prop_val'."
1262 new_prop_val=`$SVN propget "$1" "$3" || echo "$prop_val"`
1263 echo "property '$1' updated on '$path', new value:
1264 $new_prop_val"
1267 # svn_revision [args...]
1268 svn_revision()
1270 svn_revision_info_out=`$SVN info "$@"`
1271 svn_revision_rv=$?
1272 echo "$svn_revision_info_out" | sed '/^Revision: /!d;s///'
1273 return $svn_revision_rv
1276 # svn_ignore [paths]
1277 svn_ignore()
1279 if [ $# -eq 0 ]; then # Simply display ignore-list.
1280 $SVN propget 'svn:ignore'
1281 elif [ $# -eq 1 ]; then
1282 svn_propadd 'svn:ignore' `basename "$1"` `dirname "$1"`
1283 else # Add arguments in svn:ignore.
1284 # This part is a bit tricky:
1285 # For each argument, we find all the other arguments with the same dirname
1286 # $dname and we svn:ignore them all in $dname.
1287 while [ $# -ne 0 ]; do
1288 arg="$1"
1289 dname=`dirname "$1"`
1290 files=`basename "$1"`
1291 shift
1292 j=0; argc=$#
1293 while [ $j -lt $argc ] && [ $# -ne 0 ]; do
1294 this_arg="$1"
1295 shift
1296 this_dname=`dirname "$this_arg"`
1297 this_file=`basename "$this_arg"`
1298 if [ x"$dname" = x"$this_dname" ]; then
1299 files="$files
1300 $this_file"
1301 else
1302 set dummy "$@" "$this_arg"
1303 shift
1305 j=`expr $j + 1`
1306 done
1307 svn_propadd 'svn:ignore' "$files" "$dname"
1308 done
1312 # svn_help
1313 svn_help()
1315 if [ $# -eq 0 ]; then
1316 svn_version
1317 $SVN help
1318 rv=$?
1319 echo '
1320 Additionnal commands provided by svn-wrapper:
1321 automerge (amerge, am)
1322 diffstat (ds)
1323 diffw (dw)
1324 ignore
1325 mail
1326 propadd (padd, pa)
1327 propsed (psed)
1328 revision (rev)
1329 touch
1330 selfupdate (selfup)
1331 version'
1332 return $rv
1333 else
1334 case $1 in
1335 automerge | auto-merge | amerge | am)
1336 echo 'automerge (amerge, am): FIXME
1337 usage: automerge FIXME
1339 FIXME' # FIXME
1341 diffstat | ds)
1342 require_diffstat
1343 echo 'diffstat (ds): Display the histogram from svn diff-output.'
1344 $SVN help diff | sed '1d;
1345 s/differences*/histogram/;
1346 2,35 s/diff/diffstat/g'
1348 diffw | dw)
1349 echo "diffw (dw): Display the differences without taking whitespaces\
1350 into account."
1351 $SVN help diff | sed '1d;
1352 2,35 s/diff\([^a-z]\)/diffw\1/g;
1353 /--diff-cmd/,/--no-diff-deleted/d'
1355 ignore)
1356 echo 'ignore: Add some files in the svn:ignore property.
1357 usage: 1. ignore [PATH]
1358 2. ignore FILE [FILES...] [PATH]
1360 1. Display the value of svn:ignore property on [PATH].
1361 2. Add some files in the svn:ignore property of [PATH].
1363 If you want to add directories in the ignore-list, be careful:
1364 svn ignore foo/ bar/
1365 will add "foo/" in the property svn:ignore within the directory bar!
1366 Instead use:
1367 svn ignore foo/ bar/ .
1368 (It'\''s somewhat like with mv)
1370 Valid options:
1371 None.'
1373 mail)
1374 echo 'mail: Resend the mail of a given commit.
1375 usage: mail REV [emails]
1377 REV must have an email file associated in +committed/REV.
1378 REV can also be PREV or HEAD.
1380 By default the mail is sent to same email addresses as during the original
1381 commit unless more arguments are given.'
1383 propadd | padd | pa)
1384 echo 'propadd (padd, pa): Add something in the value of a property.
1385 usage: propadd PROPNAME PROPVAL PATH
1387 PROPVAL will be appended at the end of the property PROPNAME.
1389 Valid options:
1390 None.'
1392 propsed | psed)
1393 echo 'propsed (psed): Edit a property with sed.
1394 usage: propsed PROPNAME SED-ARGS PATH
1396 eg: svn propsed svn:externals "s/http/https/" .
1398 Valid options:
1399 None.'
1401 revision | rev)
1402 echo 'revision (rev): Display the revision number of a local or remote item.'
1403 $SVN help info | sed '1d;
1404 s/information/revision/g;
1405 s/revision about/the revision of/g;
1406 2,35 s/info/revision/g;
1407 /-xml/d'
1409 touch)
1410 echo 'touch: Touch a file and svn add it.
1411 usage: touch FILE [FILES]...
1413 Valid options:
1414 None.'
1416 selfupdate | selfup | self-update | self-up)
1417 echo 'selfupdate (selfup): Attempt to update svn-wrapper.sh
1418 usage: selfupdate
1420 Valid options:
1421 None.'
1423 version)
1424 echo 'version: Display the version info of svn and svn-wrapper.
1425 usage: version
1427 Valid options:
1428 None.'
1430 *) $SVN help "$@";;
1431 esac
1435 # svn_status [args...]
1436 svn_status()
1438 svn_status_out=`$SVN status "$@"`
1439 svn_status_rv=$?
1440 test x"$svn_status_out" = x && return $svn_status_rv
1441 echo "$svn_status_out" | sed "$sed_svn_st_color"
1442 return $svn_status_rv
1445 # svn_update [args...]
1446 svn_update()
1448 svn_update_out=`$SVN update "$@"`
1449 svn_update_rv=$?
1450 echo "$svn_update_out" | sed "$sed_svn_up_colors"
1451 return $svn_update_rv
1454 # ------------------- #
1455 # `main' starts here. #
1456 # ------------------- #
1458 # Define colors if stdout is a tty.
1459 if test -t 1; then
1460 set_colors
1461 else # stdout isn't a tty => don't print colors.
1462 set_nocolors
1465 # Consider this as a sed function :P.
1466 sed_svn_st_color="
1467 t dummy_sed_1
1468 : dummy_sed_1
1469 s@^?\\(......\\)+@+\\1+@
1470 s@^?\\(......\\)\\(.*/\\)+@+\\1\\2+@
1471 s@^?\\(......\\),@,\\1,@
1472 s@^?\\(......\\)\\(.*/\\),@,\\1\\2,@
1473 s/^\\(.\\)C/\\1${lred}C${std}/
1474 t dummy_sed_2
1475 : dummy_sed_2
1476 s/^?/${lred}?${std}/; t
1477 s/^M/${lgreen}M${std}/; t
1478 s/^A/${lgreen}A${std}/; t
1479 s/^X/${lblue}X${std}/; t
1480 s/^+/${lyellow}+${std}/; t
1481 s/^D/${lyellow}D${std}/; t
1482 s/^,/${lred},${std}/; t
1483 s/^C/${lred}C${std}/; t
1484 s/^I/${purple}I${std}/; t
1485 s/^R/${lblue}R${std}/; t
1486 s/^!/${lred}!${std}/; t
1487 s/^~/${lwhite}~${std}/; t"
1489 sed_svn_up_colors="
1490 t dummy_sed_1
1491 : dummy_sed_1
1493 /^Updated/ t
1494 /^Fetching/ t
1495 /^External/ t
1496 s/^\\(.\\)C/\\1${lred}C${std}/
1497 s/^\\(.\\)U/\\1${lgreen}U${std}/
1498 s/^\\(.\\)D/\\1${lred}D${std}/
1499 t dummy_sed_2
1500 : dummy_sed_2
1501 s/^A/${lgreen}A${std}/; t
1502 s/^U/${lgreen}U${std}/; t
1503 s/^D/${lyellow}D${std}/; t
1504 s/^G/${purple}G${std}/; t
1505 s/^C/${lred}C${std}/; t"
1507 # For dev's:
1508 test "x$1" = x--debug && shift && set -x
1510 case "$1" in
1511 # ------------------------------- #
1512 # Hooks for standard SVN commands #
1513 # ------------------------------- #
1514 commit | ci)
1515 shift
1516 svn_commit "$@"
1518 help | \? | h)
1519 shift
1520 svn_help "$@"
1522 # FIXME: Incomplete
1523 # merge)
1524 # shift
1525 # svn_merge "$@"
1526 # ;;
1527 status | stat | st)
1528 shift
1529 svn_status "$@"
1531 update | up)
1532 shift
1533 svn_update "$@"
1535 # -------------------- #
1536 # Custom SVN commands #
1537 # -------------------- #
1538 automerge | auto-merge | amerge | am)
1539 shift
1540 if [ $# -ne 2 ]; then
1541 abort "automerge: not enough arguments provided;
1542 try 'svn help automerge' for more info"
1544 svn_automerge "$@"
1546 diffstat | ds)
1547 shift
1548 require_diffstat && $SVN diff "$@" | diffstat
1550 diffw | dw)
1551 shift
1552 svn_diffw "$@"
1554 ignore)
1555 shift
1556 svn_ignore "$@"
1558 mail)
1559 shift
1560 svn_mail "$@"
1562 propadd | padd | pa)
1563 shift
1564 svn_propadd "$@"
1566 propsed | psed)
1567 shift
1568 svn_propsed "$@"
1570 revision | rev)
1571 shift
1572 svn_revision "$@"
1574 touch)
1575 shift
1576 touch "$@" && svn add "$@"
1578 selfupdate | selfup | self-update | self-up)
1579 shift
1580 selfupdate "$@"
1582 version | -version | --version)
1583 shift
1584 set dummy '--version' "$@"
1585 shift
1586 svn_version
1587 exec $SVN "$@"
1589 *) exec $SVN "$@"
1591 esac