2 # Simple wrapper around svn featuring auto-ChangeLog entries and emailing.
4 # Copyright (C) 2006 Benoit Sigoure.
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
21 # Quick install: alias svn=path/to/svn-wrapper.sh -- that's all.
27 # This script is a wrapper around the svn command-line client for UNIX. It has
28 # been designed mainly to automagically generate GNU-style ChangeLog entries
29 # when committing and mail them along with a diff and an optional comment from
30 # the author to a list of persons or a mailing list. It has been made so that
31 # it's as much portable as possible and covers as many use-case as possible.
33 # HOWEVER, there will be bugs, there will be cases in which the script doesn't
34 # wrap properly the svn-cli, etc. In this case, you can try to mail me at
35 # <tsuna at lrde dot epita dot fr>. Include the revision of the svn-wrapper
36 # you're using, and full description of what's wrong etc. so I can reproduce
39 # If you feel like, you can try to fix/enhance the script yourself. It only
40 # requires some basic Shell-scripting skills. Knowing sed might help :)
46 # If you're simply looking for the usage, run svn-wrapper.sh help (or
47 # svn help if you aliased `svn' on svn-wrapper.sh) as usual.
49 # This script is (hopefully) portable, widely commented and self-contained. Do
50 # not hesitate to hack it. It might look rather long (because it does a lot of
51 # things :P) but you should be able to easily locate which part of the code
54 # The script begins by defining several functions. Then it really starts where
55 # the comment "# `main' starts here. #" is placed.
56 # Some svn commands are hooked (eg, svn st displays colors). Hooks and
57 # extra commands are defined in functions named svn_<command-name>.
63 # * Handle things such as svn ci --force foobar.
64 # * 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 # Default values (the user can export them to override them).
76 if [ x
"$SVN" = x
]; then
77 if [ -d .git
] ||
[ -d ..
/.git
]; then
95 # Override the locale.
100 # Signal number for traps (using plain signal names is not portable and breaks
101 # on recent Debians that use ash as their default shell).
104 # Pitfall: some users might be tempted to export SVN=svn-wrapper.sh for some
105 # reason. This is just *wrong*. The following is an attempt to save them from
107 if [ x
`basename "$SVN"` = 'xsvn-wrapper.sh' ]; then
111 # This code comes (mostly) from Autoconf.
112 # The user is always right.
113 if test "${PATH_SEPARATOR+set}" != set; then
114 trap "echo SIGINT; rm -f '$TMPDIR/conf$$.sh'; exit 130" $SIGINT
115 echo "#! /bin/sh" >"$TMPDIR/conf$$.sh"
116 echo "exit 0" >>"$TMPDIR/conf$$.sh"
117 chmod +x
"$TMPDIR/conf$$.sh"
118 if (PATH
="/nonexistent;."; conf$$.sh
) >/dev
/null
2>&1; then
123 rm -f "$TMPDIR/conf$$.sh"
125 trap 'echo SIGINT; exit 130' $SIGINT
129 revision
=`sed '/^# $Id[:].*$/!d;
132 s/[^0-9]*\([0-9][0-9]*\).*/\1/' "$0"`
135 # The `main' really starts after the functions definitions.
143 red
='\e[0;31m'; lred
='\e[1;31m'
144 green
='\e[0;32m'; lgreen
='\e[1;32m'
145 yellow
='\e[0;33m'; lyellow
='\e[1;33m'
146 blue
='\e[0;34m'; lblue
='\e[1;34m'
147 purple
='\e[0;35m'; lpurple
='\e[1;35m'
148 cyan
='\e[0;36m'; lcyan
='\e[1;36m'
149 grey
='\e[0;37m'; lgrey
='\e[1;37m'
150 white
='\e[0;38m'; lwhite
='\e[1;38m'
158 yellow
=''; lyellow
=''
160 purple
=''; lpurple
=''
170 echo "svn-wrapper: ${lred}abort${std}: $@" \
171 |
sed '1!s/^[ ]*/ /' >&2
178 echo "svn-wrapper: ${lred}warning${std}: $@" \
179 |
sed '1!s/^[ ]*/ /' >&2
185 echo "svn-wrapper: ${lyellow}notice${std}: $@" \
186 |
sed '1!s/^[ ]*/ /' >&2
193 read answer ||
return 1
198 return 42 # should never happen...
202 # returns true if `yes' or `proceed', false if `new'.
203 # the answer is stored in $yesnoproceed_res which is /yes|new|proceed/
206 echo -n "$@ [(y)es/(p)roceed/(N)ew] "
207 read answer ||
return 1
209 y
* | Y
*) yesnoproceed_res
=yes; return 0;;
210 p
* | P
*) yesnoproceed_res
=proceed
; return 0;;
211 *) yesnoproceed_res
=new
; return 1;;
213 return 42 # should never happen...
219 warn
"cannot find the environment variable $1
220 You might consider using \`export $1='<FIXME>'\`"
223 # get_unique_file_name file-name
224 get_unique_file_name
()
227 echo "$1" && return 0
230 while test -e "$gufn.$i"; do
236 # ensure_not_empty description value
239 ene_val
=`echo "$2" | tr -d ' \t\n'`
240 test x
"$ene_val" = x
&& abort
"$1: empty value"
243 # find_prog prog-name
244 # return true if prog-name is in the PATH
245 # echo the full path to prog-name on stdout.
246 # Based on a code from texi2dvi
253 test x
"$dir" = x
&& continue
254 # The basic test for an executable is `test -f $f && test -x $f'.
255 # (`test -x' is not enough, because it can also be true for directories.)
256 # We have to try this both for $1 and $1.exe.
258 # Note: On Cygwin and DJGPP, `test -x' also looks for .exe. On Cygwin,
259 # also `test -f' has this enhancement, bot not on DJGPP. (Both are
260 # design decisions, so there is little chance to make them consistent.)
261 # Thusly, it seems to be difficult to make use of these enhancements.
263 if test -f "$dir/$1" && test -x "$dir/$1"; then
266 elif test -f "$dir/$1.exe" && test -x "$dir/$1.exe"; then
274 # find_progs prog [progs...]
275 # Look in PATH for one of the programs given in argument.
276 # If none of the progs can be found, the string "exit 2" is "returned".
279 # This code comes mostly from Autoconf.
280 for fp_prog
in "$@"; do
281 fp_res
=`find_prog $fp_prog`
282 if [ $?
-eq 0 ]; then
291 test x
"$EDITOR" = xmissing
&& EDITOR
=`find_progs vim vi emacs nano`
292 test x
"$PAGER" = xmissing
&& PAGER
=`find_progs less more`
293 test x
"$AWK" = xmissing
&& AWK
=`find_progs gawk mawk nawk awk`
296 # return true if diffstat is in the PATH
299 if [ x
"$require_diffstat_cache" != x
]; then
300 return $require_diffstat_cache
302 if (echo | diffstat
) >/dev
/null
2>/dev
/null
; then :; else
303 warn
'diffstat is not installed on your system or not in your PATH.'
304 test -f /etc
/debian_version \
305 && notice
'you might want to `apt-get install diffstat`.'
306 require_diffstat_cache
=1
309 require_diffstat_cache
=0
314 # return 0 -> found a mailer
315 # return !0 -> no mailer found
316 # The full path to the program found is echo'ed on stdout.
320 PATH
="${PATH}${PATH_SEPARATOR}/sbin${PATH_SEPARATOR}/usr/sbin${PATH_SEPARATOR}/usr/libexec"
322 find_progs sendEmail sendmail
mail
329 # my_sendmail mail-file mail-subject mail-to [extra-headers]
330 # mail-to is a comma-separated list of email addresses.
331 # extra-headers is an optionnal argument and will be prepended at the
332 # beginning of the mail headers if the tool used to send mails supports it.
333 # The mail-file may also contain headers. They must be separated from the body
334 # of the mail by a blank line.
337 test -f "$1" || abort
"my_sendmail: Cannot find the mail file: $1"
338 test x
"$2" = x
&& warn
'my_sendmail: Empty subject.'
339 test x
"$3" = x
&& abort
'my_sendmail: No recipient specified.'
341 content_type
='Content-type: text/plain'
342 extra_headers
="X-Mailer: svn-wrapper v$version (r$revision)
344 Content-Transfer-Encoding: 7bit"
345 if test x
"$4" != x
; then
348 # Remove empty lines.
349 extra_headers
=`echo "$extra_headers" | sed '/^[ ]*$/d;s/^[ ]*//'`
352 # If we have a signature, add it.
353 if test -f ~
/.signature
; then
354 # But don't add it if it is already in the mail.
355 if grep -Fe "`cat ~/.signature`" "$1" >/dev
/null
; then :; else
356 echo '-- ' >>"$1" && cat ~
/.signature
>>"$1"
359 # VCS-compat: handle user option 'sign'.
360 if (grep '^sign: false' ~
/.vcs
) >/dev
/null
2>/dev
/null
; then :; else
361 ($GPG -h) >/dev
/null
2>/dev
/null
363 if [ -e ~
/.gnupg
] ||
[ -e ~
/.gpg
] ||
[ -e ~
/.pgp
] && [ $gpg_rv -lt 42 ]
365 if grep 'BEGIN PGP SIGNATURE' "$1" >/dev
/null
; then
366 notice
'message is already GPG-signed'
367 elif yesno
"Sign the mail using $GPG ?"; then
369 sed '1,/^$/d' "$1" >"$1.msg"
370 sed '1,/^$/!d' "$1" >"$1.hdr"
371 # Sign the message, keep only the PGP signature.
372 $GPG --clearsign <"$1.msg" >"$1.tmp" ||
{
373 rm -f "$1.msg" "$1.hdr" "$1.tmp"
374 abort
"\`$GPG' failed (r=$?)"
376 sed '/^--*BEGIN PGP SIGNATURE--*$/,/^--*END PGP SIGNATURE--*$/!d' \
379 boundary
="svn-wrapper-2-$RANDOM"
380 boundary
="$boundary$RANDOM"
381 # Prepend some stuff before the PGP signature.
384 content-type: application/pgp-signature; x-mac-type=70674453;
386 content-description: This is a digitally signed message part
387 content-disposition: inline; filename=PGP.sig
388 content-transfer-encoding: 7bit" |
cat - "$1.sig" >"$1.tmp"
389 mv -f "$1.tmp" "$1.sig"
391 # Append some stuff after the PGP signature.
393 --$boundary--" >>"$1.sig"
394 # Re-paste the headers before the signed body and prepend some stuff.
395 echo "This is an OpenPGP/MIME signed message (RFC 2440 and 3156)
397 Content-Transfer-Encoding: 8bit
398 Content-Type: text/plain; charset=iso-8859-1; format=flowed
400 |
cat "$1.hdr" - "$1.msg" "$1.sig" >"$1.tmp" \
401 && mv -f "$1.tmp" "$1"
402 content_type
="Content-Type: multipart/signed;\
403 protocol=\"application/pgp-signature\"; micalg=pgp-sha1;\
404 boundary=\"$boundary\""
406 rm -f "$1.tmp" "$1.sig" "$1.msg" "$1.hdr"
407 extra_headers
="$extra_headers
408 X-Pgp-Agent: `$GPG --version | sed q`"
412 extra_headers
="$extra_headers
415 mailer
=`require_mail`
416 if [ $?
-ne 0 ]; then
417 warn
'my_sendmail: No suitable mailer found.'
422 to
=`echo "$3" | sed 's/,//g'`
423 echo "$extra_headers" |
cat - "$1" |
$mailer $to;;
425 cat "$1" |
$mailer -s "$2" "$3";;
427 if [ x
"$SMTP" = x
]; then
428 warn
'my_sendmail: (sendEmail) please tell me the SMTP server to use'
429 echo -n 'STMP server: '
430 read SMTP || abort
'could not read the SMTP server'
431 notice
"hint: you can export SMTP=$SMTP if you don't want to be asked"
433 sendEmail
-f "$FULLNAME <$EMAIL>" -t "$3" -u "$2" \
434 -s "$SMTP" -o message-file
="$1";;
436 abort
'my_sendmail: Internal error.';;
443 my_url
='http://www.lrde.epita.fr/~sigoure/svn-wrapper'
444 # You can use https if you feel paranoiac.
446 echo ">>> Fetching svn-wrapper.sh from $my_url/svn-wrapper.sh"
448 # --------------------- #
449 # Fetch the new version #
450 # --------------------- #
452 tmp_me
=`get_unique_file_name "$TMPDIR/svn-wrapper.sh"`
453 if (wget
--help) >/dev
/null
2>/dev
/null
; then
454 wget
--no-check-certificate "$my_url/svn-wrapper.sh" -O "$tmp_me"
457 curl
--help >/dev
/null
2>/dev
/null
458 if [ $?
-gt 42 ]; then
459 abort
'Cannot find wget or curl.
460 How can I download any update without them?'
463 curl
--insecure "$my_url/svn-wrapper.sh" >"$tmp_me"
467 || abort
"Cannot find the copy of myself I downloaded in $tmp_me"
469 # ---------------------------------------- #
470 # Compare versions and update if necessary #
471 # ---------------------------------------- #
474 tmp_ver
=`sed '/^# $Id[:].*$/!d;
477 s/[^0-9]*\([0-9][0-9]*\).*/\1/' "$tmp_me"`
478 test x
"$tmp_ver" = x
&& abort
"Cannot find the version of $tmp_me"
479 if [ "$my_ver" -lt "$tmp_ver" ]; then # There IS an update...
480 echo "An update is available, r$tmp_ver (your version is r$my_ver)"
483 if yesno
"Do you want to see the ChangeLog between r$my_ver and r$tmp_ver?"
485 my_chlog
=`get_unique_file_name "$TMPDIR/ChangeLog"`
487 wget
) wget
--no-check-certificate "$my_url/ChangeLog" -O "$my_chlog"
489 curl
) curl
--insecure "$my_url/ChangeLog" >"$my_chlog"
491 *) abort
'Should never be here.'
494 sed "/^r$my_ver/q" "$my_chlog" |
$PAGER
498 # Wanna see the diff?
499 if yesno
"Do you want to see the diff between r$my_ver and r$tmp_ver?"
501 (require_diffstat
&& diff -uw "$me" "$tmp_me" | diffstat
;
503 diff -uw "$me" "$tmp_me") |
$PAGER
507 if yesno
"Overwrite $me (r$my_ver) with $tmp_me (r$tmp_ver)?"; then
509 cp -p "$me" "$me.r$my_ver"
510 mv "$tmp_me" "$me" && exit 0
514 elif [ "$my_ver" -gt "$tmp_ver" ]; then
515 echo "Wow, you're more up to date than the master copy :)"
516 echo "Your version is r$my_ver and the master copy is r$tmp_ver."
517 if yesno
'Downgrade?'; then
519 mv "$tmp_me" "$me" && exit 0
522 echo "You're already up to date [r$my_ver] :)"
527 # get_svn_diff_and_diffstat [files to diff]
528 # Helper for svn_commit
529 get_svn_diff_and_diffstat
()
532 svn_diff
=`git diff --ignore-all-space --no-color -B -C --cached`
533 svn_diff_stat
=`git diff --stat --ignore-all-space --no-color -B -C --cached`
535 # Ignore white spaces. Can't svn diff -x -w: svn 1.4 only.
536 svn_diff
=`svn_diffw "$@"`
537 test x
"$svn_diff" = x
&& svn_diff
=`$SVN diff "$@"`
538 if require_diffstat
; then
539 svn_diff_stat
=`echo "$svn_diff" | diffstat`
541 svn_diff_stat
='diffstat not available'
546 # Helper. Sets the variables repos_url, git_branch, git_head, repos_root and
547 # extra_repos_info properly.
548 git_get_repos_info_
()
550 # FIXME: 1st commit: "fatal: bad default revision 'HEAD'" on stderr
551 repos_url
=`git show | sed '/git-svn-id/{
557 test x
"$repos_url" = x
&& repos_url
='(git:unknown)'
558 git_branch
=`git branch | awk '/^\*/ { print substr($0, 3) }'`
559 if [ x
"$git_branch" = x
'(no branch)' ]; then
560 yesno
'You are on a detached HEAD, do you really want to continue?' \
563 git_head
=`git-rev-list --pretty=format:%h HEAD --max-count=1 | sed '1d;q'`
564 extra_repos_info
="Git branch: $git_branch (HEAD: $git_head)"
565 repos_root
=$repos_url
568 # ------------------------------- #
569 # Hooks for standard SVN commands #
570 # ------------------------------- #
572 # svn_commit [args...]
573 # Here is how the commit process goes:
575 # First we look in the arguments passed to commit:
576 # If there are some files or paths, the user wants to commit these only. In
577 # this case, we must search for ChangeLogs from these paths. We might find
578 # more than one ChangeLog, in this case the user will be prompted to pick up
580 # Otherwise (no path passed in the command line) the user just wants to
581 # commit the current working directory.
582 # In any case, we schedule "ChangeLog" for commit.
584 # Alright now that we know which ChangeLog to use, we look in the ChangeLog's
585 # directory if there is a ",svn-log" file which would mean that a previous
586 # commit didn't finish successfully. If there is such a file, the user is
587 # prompted to know whether they want to resume that commit or simply start a
589 # When the user wants to resume a commit, the ",svn-log" file is loaded and we
590 # retrieve the value of "$@" that was saved in the file.
591 # Otherwise we build a template ChangeLog entry.
592 # Then we open the template ChangeLog entry with $EDITOR so that the user
594 # Finally, we commit.
595 # Once the commit is sent, we ask the server to know which revision was
596 # commited and we also retrieve the diff. We then send a mail with these.
603 # Check if the user passed some paths to commit explicitly
604 # because in this case we must add the ChangeLog to the commit and search
605 # the ChangeLog from the dirname of that file.
606 i
=0; search_from
=''; add_changelog
=no
; extra_files
=''
607 while [ $i -lt $# ]; do
620 # If the argument is a valid path: add the ChangeLog in the list of
622 if test -e "$arg"; then
624 if test -d "$arg"; then
625 search_from_add
="$arg"
627 search_from_add
=`dirname "$arg"`
629 search_from
="$search_from:$search_from_add"
632 set dummy
"$@" "$arg"
636 if [ $add_changelog = no
]; then
637 # There is no path/file in the command line: the user wants to commit the
638 # current directory. Make it explicit now:
641 search_from
=`echo "$search_from" | sed 's/^://; s/^$/./'`
643 # ----------------- #
644 # Find ChangeLog(s) #
645 # ----------------- #
647 nb_chlogs
=0; change_log_dirs
=''
648 save_IFS
=$IFS; IFS
=':'
649 for dir
in $search_from; do
651 test -z "$dir" && dir
='.'
652 # First: come back to the original place
653 cd "$here" || abort
"Cannot cd to $here"
654 cd "$dir" ||
continue # Then: Enter $dir (which can be a relative path)
656 while [ $found -eq 0 ]; do
657 this_chlog_dir
=`pwd -P`
658 if [ -f .
/ChangeLog
]; then
660 nb_chlogs
=`expr $nb_chlogs + 1`
661 change_log_dirs
="$change_log_dirs:$this_chlog_dir"
665 # Stop searching when in / ... hmz :P
666 test x
"$this_chlog_dir" = x
/ && break
667 done # end while: did we find a ChangeLog
668 done # end for: find ChangeLogs in $search_from
669 if [ $nb_chlogs -gt 0 ]; then
670 change_log_dirs
=`echo "$change_log_dirs" | sed 's/^://' | tr ':' '\n' \
672 nb_chlogs
=`echo "$change_log_dirs" | wc -l`
675 # Did we find a ChangeLog? More than one?
676 if [ $nb_chlogs -eq 0 ]; then
677 if yesno
'svn-wrapper: Error: Cannot find a ChangeLog file!
678 You might want to create an empty one (eg: `touch ChangeLog` where appropriate)
679 Do you want to proceed without using a ChangeLog?'; then
686 elif [ $nb_chlogs -gt 1 ]; then
687 notice
"$nb_chlogs ChangeLogs were found, pick up one:"
690 for a_chlog_dir
in $change_log_dirs; do
692 echo "$i. $a_chlog_dir/ChangeLog"
694 echo -n "Which ChangeLog do you want to use? [1-$i] "
695 read chlog_no || abort
'Cannot read answer on stdin.'
698 *[^
0-9]*) abort
"Invalid ChangeLog number: $chlog_no"
700 test "$chlog_no" -le $i || abort
"Invalid ChangeLog number: $chlog_no
702 test "$chlog_no" -ge 1 || abort
"Invalid ChangeLog number: $chlog_no
704 change_log_dir
=`echo "$change_log_dirs" | tr ':' '\n' | sed "${chlog_no}!d"`
705 else # Only one ChangeLog found
706 change_log_dir
=$change_log_dirs
707 notice
"using $change_log_dir/ChangeLog"
710 test -f "$change_log_dir/ChangeLog" \
711 || abort
"No such file or directory: $change_log_dir/ChangeLog"
713 # Now we can safely schedule the ChangeLog for the commit.
714 extra_files
="$extra_files:$change_log_dir/ChangeLog"
715 if [ -d "$change_log_dir/.git" ] ||
$git_mode; then
720 svn_st_tmp
=`$SVN status "$change_log_dir"`
722 # Warn for files that are not added in the repos.
723 conflicts
=`echo "$svn_st_tmp" | sed '/^ *$/d;
727 if test x
"$conflicts" != x
; then
728 warn
"make sure you don't want to \`svn add'
729 any of the following files before committing:"
730 echo "$conflicts" |
sed "$sed_svn_st_color"
731 echo -n 'Type [ENTER] to continue :)' && read chiche_is_gay
734 # If there are changes in an svn:externals, advise the user to commit that
736 changed_externals
=`echo "$svn_st_tmp" | $AWK \
745 BEGIN { this_ext = ""; ext = 0; ext_modified = 0; }
746 /^Performing status on external/ {
748 sub(/.* at ./, ""); sub(/.$/, ""); this_ext = $0;
751 /^[ADMR]/ { ext_modified = ext; printext(); }
752 /^.[M]/ { ext_modified = ext; printext(); }
753 END { exit ext_modified; }'`
754 if [ $?
-ne 0 ]; then
755 warn
"the following external items have local modifications:
757 yesno
"You are advised to commit them separately first. Continue anyway?" \
761 # Detect unresolved conflicts / missing files.
762 conflicts
=`echo "$svn_st_tmp" | sed '/^[C!]/!d'`
763 test x
"$conflicts" != x
&& abort
"there are unresolved conflicts (\`C')
764 and/or missing files (\`!'):
767 svn_info_tmp
=`$SVN info "$change_log_dir"`
768 test $?
-ne 0 && abort
"Failed to get svn info on $change_log_dir"
769 repos_root
=`echo "$svn_info_tmp" | sed '/^Repository Root: /!d;s///'`
770 repos_url
=`echo "$svn_info_tmp" | sed '/^URL: /!d;s///'`
771 # It looks like svn <1.3 didn't display a "Repository Root" entry.
772 test x
"$repos_root" = x
&& repos_root
=$repos_url
781 # VCS-compat: handle user option 'new_user'
783 grep '^new_user: false' ~
/.vcs
>/dev
/null
2>/dev
/null
&& new_user
='no'
786 tmp_log
="$change_log_dir/,svn-log"
787 if [ -f "$tmp_log" ] && yesnewproceed
"It looks like the last commit did not\
788 terminate successfully.
789 Would you like to resume it or proceed immediately?"; then
790 test x
"$yesnoproceed_res" = xproceed
&& edit_changelog
=no
792 internal_tags
=`sed '/^--- Internal stuff, DO NOT change please ---$/,$!d' \
794 saved_args
=`echo "$internal_tags" | sed '/^args: */!d;s///'`
795 extra_files
=`echo "$internal_tags" | sed '/^extra_files: */!d;s///'`
796 if [ x
"$saved_args" != x
]; then
797 if [ x
"$*" != x
] && [ x
"$saved_args" != x
"$*" ]; then
798 warn
"overriding arguments:
799 you invoked $me with the following arguments: $@
800 they have been replaced by these: $saved_args"
801 set dummy
$saved_args
804 notice
"setting the following arguments: $saved_args"
805 set dummy
$saved_args
808 elif [ x
"$*" != x
]; then
809 warn
"overriding arguments:
810 you invoked $me with the following arguments: $@
811 they have been dropped"
818 if [ $git_mode ]; then
819 (cd $change_log_dir && git-add
-v -u) || abort
'`git-add -v -u` failed'
825 get_svn_diff_and_diffstat
"$@"
827 # Update the file with the new diff/diffstat in case it changed.
832 /^--This line, and those below, will be ignored--$/ {
835 /^ Your ChangeLog entry will appear here\.$/ {
836 if (tlatbwbi_seen) ycewah_seen = 1;
839 if (ycewah_seen != 2) print;
840 if (ycewah_seen == 1) ycewah_seen = 2;
841 }' "$tmp_log" >"$tmp_log.tmp"
848 $internal_tags" >>"$tmp_log.tmp"
849 mv -f "$tmp_log.tmp" "$tmp_log"
851 else # Build the template message.
853 # ------------------------------------ #
854 # Gather info for the template message #
855 # ------------------------------------ #
858 projname
=`git config svnw.project`
859 if [ x
"$projname" = x
] \
860 && [ -f "$change_log_dir/.git/svn/git-svn/unhandled.log" ]
862 projname
=`grep project "$change_log_dir/.git/svn/git-svn/unhandled.log"`
863 sed_tmp
='$!d;s/^.*+dir_prop: . project //'
864 projname
=`echo "$projname" | sed "$sed_tmp"`
866 if [ x
"$projname" = x
]; then
867 warn
'No project name set for this repository.
868 If this is a git-svn repository, do this in a SVN working copy:
869 svn propset project myproj .
870 If this is a real git repository, do this:
871 git config svnw.project myproj'
874 projname
=`$SVN propget project "$change_log_dir"`
876 # Try to be VCS-compatible and find a project name in a *.rb.
877 if [ x
"$projname" = x
] && [ -d "$change_log_dir/vcs" ]; then
878 projname
=`sed '/common_commit/!d;s/.*"\(.*\)<%= rev.*/\1/' \
879 "$change_log_dir"/vcs/*.rb`
880 test x
"$projname" != x
&& test x
$new_user = xyes \
881 && notice
"VCS-compat: found project name: $projname
882 in " "$change_log_dir"/vcs
/*.rb
884 test x
"$projname" != x
&& projname
=`echo "$projname" | sed '/[^ ]$/s/$/ /'`
887 mailto
=`git config svnw.mailto`
888 if [ x
"$mailto" = x
] \
889 && [ -f "$change_log_dir/.git/svn/git-svn/unhandled.log" ]
891 mailto
=`grep mailto "$change_log_dir/.git/svn/git-svn/unhandled.log"`
892 sed_tmp
='$!d;s/^.*+dir_prop: . mailto //'
893 mailto
=`echo "$mailto" | sed "$sed_tmp"`
895 if [ x
"$mailto" = x
]; then
896 warn
'No mailto property set for this repository.
897 If this is a git-svn repository, do this in a SVN working copy:
898 svn propset mailto maintainer1@foo.com,maint2@bar.com .
899 If this is a real git repository, do this:
900 git config svnw.mailto maintainer1@foo.com,maint2@bar.com'
903 mailto
=`$SVN propget mailto "$change_log_dir"`
906 if [ x
"$mailto" = x
]; then
907 test x
$new_user = xyes \
908 && warn
"no svn property mailto found in $change_log_dir
909 You might want to set default email adresses using:
910 svn propset mailto 'somebody@mail.com, foobar@example.com'\
912 # Try to be VCS-compatible and find a list of mails in a *.rb.
913 if [ -d "$change_log_dir/vcs" ]; then
914 mailto
=`grep '.@.*\..*' "$change_log_dir"/vcs/*.rb \
916 | sed 's/^.*[["]\([^["]*@[^]"]*\)[]"].*$/\1/' | xargs`
917 test x
"$mailto" != x
&& test x
$new_user = xyes \
918 && notice
"VCS-compat: found mailto: $mailto
919 in " "$change_log_dir"/vcs
/*.rb
921 fi # end guess mailto
923 # Ensure that emails are comma-separated.
924 mailto
=`echo "$mailto" | sed 's/[ ;]/,/g' | tr -s ',' | sed 's/,/, /g'`
925 test x
"$FULLNAME" = x
&& FULLNAME
='Type Your Name Here' \
927 test x
"$EMAIL" = x
&& EMAIL
='your.mail.here@FIXME.com' && warn_env EMAIL
930 if [ $git_commit_all = yes ]; then
931 (cd $change_log_dir && git-add
-v -u) || abort
'`git-add -v -u` failed'
933 my_git_st
=`git-diff -C --raw --cached`
934 test $?
-eq 0 || abort
'git-diff failed'
935 # Format: ":<old_mode> <new_mode> <old_sha1> <new_sha1> <status>[
936 # <similarity score>]\t<file-name>"
937 change_log_files
=`echo "$my_git_st" | sed '
940 s/^:[0-7 ]* [0-9a-f. ]* M[^ ]* \(.*\)$/ * \1: ./; t
941 s/^:[0-7 ]* [0-9a-f. ]* A[^ ]* \(.*\)$/ * \1: New./; t
942 s/^:[0-7 ]* [0-9a-f. ]* D[^ ]* \(.*\)$/ * \1: Remove./; t
943 s/^:[0-7 ]* [0-9a-f. ]* R[^ ]* \([^ ]*\) \(.*\)$/ * \1: Rename as \2./; t
944 s/^:[0-7 ]* [0-9a-f. ]* T[^ ]* \(.*\)$/ * \1: ./; t
945 s/^:[0-7 ]* [0-9a-f. ]* X[^ ]* \(.*\)$/ * \1: ???./; t
946 s/^:[0-7 ]* [0-9a-f. ]* U[^ ]* \(.*\)$/ * \1: UNMERGED./; t
949 # --ignore-externals appeared after svn 1.1.1
950 my_svn_st
=`$SVN status --ignore-externals "$@" \
951 || $SVN status "$@" | sed '/^Performing status on external/ {
955 # Files to put in the ChangeLog entry.
956 change_log_files
=`echo "$my_svn_st" | sed '
959 s/^M......\(.*\)$/ * \1: ./; t
960 s/^A......\(.*\)$/ * \1: New./; t
961 s/^D......\(.*\)$/ * \1: Remove./; t
965 if [ x
"$change_log_files" = x
]; then
966 yesno
'Nothing to commit, continue anyway?' ||
return 1
969 change_log_files
=`echo "$change_log_files" | sort -u`
971 get_svn_diff_and_diffstat
"$@"
973 # Get any older svn-log out of the way.
974 test -f "$tmp_log" && mv "$tmp_log" `get_unique_file_name "$tmp_log"`
975 # If we can't get an older svn-log out of the way, find a new name...
976 test -f "$tmp_log" && tmp_log
=`get_unique_file_name "$tmp_log"`
977 if [ x
$new_user = no
]; then
978 commit_instructions
='
980 - Fill the ChangeLog entry.
981 - If you feel like, write a comment in the "Comment:" section.
982 This comment will only appear in the email, not in the ChangeLog.
983 By default only the location of the repository is in the comment.
984 - Some tags will be replaced. Tags are of the form: <TAG>. Unknown
985 tags will be left unchanged.
986 - The tag <REV> may only be used in the Subject.
987 - Your ChangeLog entry will be used as commit message for svn.'
989 commit_instructions
=''
992 if [ x
"$mailto" != xdont_send_mails
] && [ x
"$mailto" != xnothx
]; then
997 $git_mode && r_before_rev
=
998 test x
"$extra_repos_info" = x || extra_repos_info
="
1001 --You must fill this file correctly to continue-- -*- vcs -*-
1003 Subject: ${projname}$r_before_rev<REV>: <TITLE>
1004 From: $FULLNAME <$EMAIL>$to
1007 URL: $repos_url$extra_repos_info
1011 <YYYY>-<MM>-<DD> $FULLNAME <$EMAIL>
1016 --This line, and those below, will be ignored--
1017 $commit_instructions
1018 --Preview of the message that will be sent--
1020 URL: $repos_url$extra_repos_info
1021 Your comments (if any) will appear here.
1024 $YYYY-$MM-$DD $FULLNAME <$EMAIL>
1026 Your ChangeLog entry will appear here.
1031 $svn_diff" >"$tmp_log"
1034 --- Internal stuff, DO NOT change please ---
1035 args: $@" >>"$tmp_log"
1036 echo "extra_files: $extra_files
1037 vi: ft=diff:noet:tw=76:" >>"$tmp_log"
1039 fi # end: if svn-log; then resume? else create template
1040 test x
"$edit_changelog" = xyes
&& $EDITOR "$tmp_log"
1042 # ------------------ #
1043 # Re-"parse" the log #
1044 # ------------------ #
1046 # hmz this section is a bit messy...
1047 # helper string... !@#$%* escaping \\\\\\...
1048 sed_escape
='s/\\/\\\\/g;s/@/\\@/g;s/&/\\\&/g'
1049 sed_eval_tags
="s/<MM>/$MM/g; s/<DD>/$DD/g; s/<YYYY>/$YYYY/g"
1050 full_log
=`sed '/^-*This line, and those below, will be ignored-*$/,$d;
1051 /^--You must fill this/d' "$tmp_log"`
1052 chlog_entry
=`echo "$full_log" | sed '/^ChangeLog:$/,$!d; //d'`
1053 ensure_not_empty
'ChangeLog entry' "$chlog_entry"
1054 full_log
=`echo "$full_log" | sed '/^ChangeLog:$/,$d'`
1055 mail_comment
=`echo "$full_log" | sed '/^Comment:$/,$!d; //d'`
1056 full_log
=`echo "$full_log" | sed '/^Comment:$/,$d'`
1057 # Add a period at the end of the title.
1058 sed_tmp
='/^Title: */!d;s///;/ *\.$/!s/ *$/./'
1059 mail_title
=`echo "$full_log" | sed "$sed_tmp"`
1060 ensure_not_empty
'commit title' "$mail_title"
1061 mail_title
=`echo "$mail_title" | sed "$sed_eval_tags; $sed_escape"`
1062 sed_eval_tags
="$sed_eval_tags; s@<TITLE>\\.*@$mail_title@g"
1063 mail_comment
=`echo "$mail_comment" | sed "$sed_eval_tags"`
1064 raw_chlog_entry
=$chlog_entry # ChangeLog entry without tags expanded
1065 chlog_entry
=`echo "$chlog_entry" | sed "$sed_eval_tags; 1{
1068 mail_subject
=`echo "$full_log" | sed '/^Subject: */!d;s///'`
1069 ensure_not_empty
'mail subject' "$mail_subject"
1070 mail_to
=`echo "$full_log" | sed '/^To:/!d'`
1072 if test x
"$mail_to" = x
; then
1075 mail_to
=`echo "$mail_to" | sed 's/^To: *//'`
1076 ensure_not_empty
'"To:" field of the mail' "$mail_to"
1078 mail_from
=`echo "$full_log" | sed '/^From: */!d;s///'`
1079 ensure_not_empty
'"From:" field of the mail' "$mail_from"
1081 # Sanity checks on the ChangeLog entry #
1082 # ------------------------------------ #
1084 if echo "$chlog_entry" |
grep '<REV>' >/dev
/null
; then
1085 warn
'Using the tag <REV> anywhere else than in the Subject is deprecated.'
1086 yesno
'Continue anyway?' ||
return 1
1089 if echo "$chlog_entry" |
grep ': \.$' >/dev
/null
; then
1090 warn
'It looks like you did not fill all entries in the ChangeLog:'
1091 echo "$chlog_entry" |
grep ': \.$'
1092 yesno
'Continue anyway?' ||
return 1
1095 if echo "$chlog_entry" |
grep '^--* Internal stuff' >/dev
/null
; then
1096 warn
"It looks like you messed up the delimiters and I did not properly
1097 find your ChangeLog entry. Here it is, make sure it is correct:"
1099 yesno
'Continue anyway?' ||
return 1
1102 if echo "$chlog_entry" |
grep -i 'dont[^a-z0-9]' >/dev
/null
; then
1103 warn
"Please avoid typos such as ${lred}dont$std instead of\
1104 ${lgreen}don't$std:"
1105 echo "$chlog_entry" |
grep -n -i 'dont[^a-z0-9]' \
1106 |
sed "s/[dD][oO][nN][tT]/$lred&$std/g"
1107 yesno
'Continue anyway?' ||
return 1
1110 if echo "$chlog_entry" |
grep -i 'cant[^a-z0-9]' >/dev
/null
; then
1111 warn
"Please avoid typos such as ${lred}cant$std instead of\
1112 ${lgreen}can't$std:"
1113 echo "$chlog_entry" |
grep -n -i 'cant[^a-z0-9]' \
1114 |
sed "s/[cC][aA][nN][tT]/$lred&$std/g"
1115 yesno
'Continue anyway?' ||
return 1
1118 if echo "$chlog_entry" |
grep '^.\{80,\}' >/dev
/null
; then
1119 warn
'Please avoid long lines in your ChangeLog entry (80 columns max):'
1120 echo "$chlog_entry" |
grep '^.\{80,\}'
1121 yesno
'Continue anyway?' ||
return 1
1124 if echo "$mail_title" |
grep -i '^[a-z][a-z]*ed[^a-z]' >/dev
/null
; then
1125 warn
'ChangeLog entries should be written in imperative form:'
1126 echo "$mail_title" |
grep -i '^[a-z][a-z]*ed[^a-z]' \
1127 |
sed "s/^\\([a-zA-Z][a-zA-Z]*ed\\)\\([^a-zA-Z]\\)/$lred\\1$std\\2/"
1128 yesno
'Continue anyway?' ||
return 1
1131 # Check whether the user passed -m | --message
1133 while [ $i -lt $# ]; do
1135 # This is not really a reliable way of knowing whether -m | --message was
1136 # passed but hum... Let's assume it'll do :s
1137 if [ x
"$arg" = 'x-m' ] ||
[ x
"$arg" = 'x--message' ]; then
1141 set dummy
"$@" "$arg"
1145 if [ x
"$my_message" = x
]; then
1146 # The title must not be indented in the commit message and must be
1147 # followed by a blank line. This yields much better results with most
1148 # VC-viewer (especially for Git but including for SVN, such as Trac for
1149 # instance). We assume that the title will always be on the 1st line.
1150 sed_git_title
="1s@^[ ]*<TITLE>\\.*@$mail_title\\
1152 # First, remove empty lines at the beginning, if any.
1153 # Remove also the date information (useless in commit messages)
1154 my_message
=`echo "$raw_chlog_entry" \
1156 /^'"<YYYY>-<[MD][MD]>-<[DM][DM]>"'/d
1159 | sed -e "$sed_git_title" \
1160 -e "$sed_eval_tags; 1{
1164 notice
'you are overriding the commit message.'
1167 # Show suspicious whitespace additions with Git.
1168 $git_mode && git
diff --cached --check
1170 if [ x
"$dry_run" = xyes
]; then
1171 proposal_file
=',proposal'
1172 test -f "$proposal_file" \
1173 && proposal_file
=`get_unique_file_name "$proposal_file"`
1174 sed_tmp
='s/<REV>/???/g;s/\([^.]\) *\.$/\1/'
1175 mail_subject
=`echo "$mail_subject" | sed "$sed_eval_tags; $sed_tmp"`
1177 if [ x
"$mailto" != xdont_send_mails
] && [ x
"$mailto" != xnothx
]; then
1183 Subject: $mail_subject
1193 $svn_diff" >"$proposal_file"
1194 notice
"A proposal of your commit was left in '$proposal_file'"
1199 if $git_mode && [ $git_commit_all = no
]; then
1200 notice
'You are using git, unlike SVN, do not forget to git add your
1203 test x
"$edit_changelog" = xno \
1204 || yesno
'Are you sure you want to commit?' \
1207 # Add the ChangeLog entry
1208 old_chlog
=`get_unique_file_name "$change_log_dir/ChangeLog.old"`
1209 mv "$change_log_dir/ChangeLog" "$old_chlog" || \
1210 abort
'Could not backup ChangeLog'
1211 trap "echo SIGINT; mv \"$old_chlog\" \"$change_log_dir/ChangeLog\";
1213 echo "$chlog_entry" >"$change_log_dir/ChangeLog"
1214 echo >>"$change_log_dir/ChangeLog"
1215 cat "$old_chlog" >>"$change_log_dir/ChangeLog"
1217 # Add extra files such as cwd or ChangeLog to the commit.
1218 tmp_sed
='s/ /\\ /g' # Escape spaces for the shell.
1220 # Schedule the ChangeLog for the next commit
1221 (cd "$change_log_dir" && git add ChangeLog
) \
1222 || abort
'failed to git add the ChangeLog'
1225 extra_files
=`echo "$extra_files" | sed "$tmp_sed" | tr ':' '\n'`
1228 # Update the Git index if necessary (just in case the user changed his
1229 # working copy in the mean time)
1230 if $git_mode && [ $git_commit_all = yes ]; then
1231 (cd $change_log_dir && git-add
-v -u) || abort
'`git-add -v -u` failed'
1234 # --Commit-- finally! :D
1235 $SVN commit
-m "$my_message" "$@" $extra_files ||
{
1237 mv "$old_chlog" "$change_log_dir/ChangeLog"
1238 abort
"Commit failed, $SVN returned $svn_commit_rv"
1241 echo -n 'Getting the revision number... '
1243 REV
=`git-rev-list --pretty=format:%h HEAD --max-count=1 | sed '1d;q'`
1245 svn_info_tmp
=`$SVN info "$change_log_dir/ChangeLog"`
1246 REV
=`echo "$svn_info_tmp" | sed '/^Revision: /!d;s///'`
1247 test x
"$REV" = x
&& REV
=`echo "$svn_info_tmp" \
1248 | sed '/^Last Changed Rev: /!d;s///'`
1250 test x
"$REV" = x
&& abort
'Cannot detect the current revision.'
1253 # Let's make sure we have the real diff by asking the ChangeSet we've just
1254 # committed to the server.
1256 # Backup the old stuff in case we fail to get the real diff from the server
1257 # for some reason...
1258 save_svn_diff
=$svn_diff
1259 save_svn_diff_stat
=$svn_diff_stat
1262 svn_diff
=`git diff --ignore-all-space --no-color -C 'HEAD^' HEAD`
1263 svn_diff_stat
=`git diff --stat --ignore-all-space --no-color -C 'HEAD^' HEAD`
1264 grep svn-remote
"$change_log_dir/.git/config" >/dev
/null
2>&1 && \
1265 notice
'Do not forget to use: git-svn dcommit to push your commits in SVN'
1267 # Fetch the ChangeSet and filter out the ChangeLog entry. We don't use
1268 # svn diff -c because this option is not portable to older svn versions.
1269 REV_MINUS_ONE
=`expr "$REV" - 1`
1270 svn_diff
=`svn_diffw -r"$REV_MINUS_ONE:$REV" "$repos_root" \
1271 | $AWK '/^Index: / { if (in_chlog) in_chlog = 0; }
1272 /^Index: .*ChangeLog$/ { in_chlog = 1 }
1273 { if (!in_chlog) print }'`
1274 if [ x
"$svn_diff" = x
]; then
1275 svn_diff
=$save_svn_diff
1276 svn_diff_stat
=$save_svn_diff_stat
1278 if require_diffstat
; then
1279 svn_diff_stat
=`echo "$svn_diff" | diffstat`
1281 svn_diff_stat
='diffstat not available'
1286 # Expand <REV> and remove the final period from the mail subject if there is
1288 sed_tmp
="s/<REV>/$REV/g;"'s/\([^.]\) *\.$/\1/'
1289 mail_subject
=`echo "$mail_subject" | sed "$sed_eval_tags; $sed_tmp"`
1291 mail_file
=`get_unique_file_name "$change_log_dir/+mail"`
1295 Subject: $mail_subject
1305 $svn_diff" |
sed 's/^\.$/ ./' >"$mail_file"
1306 # We change lines with only a `.' because they could mean "end-of-mail"
1309 if test x
$send_a_mail = xyes
; then
1310 trap 'echo SIGINT; exec < /dev/null' $SIGINT
1311 # FIXME: Move the mail to the +committed right now, in case the user
1312 # CTLR+C the mail-sending-thing, so that the mail will be properly saved
1314 my_sendmail
"$mail_file" "$mail_subject" "$mail_to" \
1315 "X-svn-url: $repos_root
1316 X-svn-revision: $REV"
1317 fi # end do we have to send a mail?
1320 save_mail_file
=`echo "$mail_file" | sed 's/+//'`
1321 mkdir
-p "$change_log_dir/+committed" \
1322 || warn
"Couldn't mkdir -p $change_log_dir/+committed"
1323 if [ -d "$change_log_dir/vcs" ] \
1324 ||
[ -d "$change_log_dir/+committed" ]
1326 mkdir
-p "$change_log_dir/+committed/$REV" \
1327 && mv "$mail_file" "$change_log_dir/+committed/$REV/mail"
1329 return $svn_commit_rv
1335 i
=0; src
=''; dst
=''; rev1
=''; rev2
=''; r_seen
=0
1336 while [ $i -lt $# ]; do
1338 if test -d "$arg"; then
1339 if [ x
"$src" = x
]; then
1341 elif [ x
"$dst" = x
]; then
1346 -r*:*) rev1
=`echo "$arg" | sed 's/^[^PREVHEAD0-9]*//; s/:.*//'`
1347 rev2
=`echo "$arg" | sed 's/^.*://; s/[^PREVHEAD0-9]*//'`
1353 set dummy
"$@" "$arg"
1357 if [ x
"$src" = x
]; then
1358 exec $SVN merge
"$@"
1360 test x
"$dst" = x
&& dst
='.'
1362 if yesno
"Do you want to use the automerge feature for $src ?";
1364 exec $SVN merge
"$@"
1366 # FIXME: Incomplete.
1369 # svn_automerge src-path dst-path
1372 test -f ChangeLog || abort
'You must use automerge in the folder where the
1374 amerge
=`$SVN propget automerge`
1375 if [ x
"$amerge" = x
]; then
1376 # FIXME: Use svn log --stop-on-copy to detect the original branching.
1380 svn_info_tmp
=`$SVN info`
1381 REV
=`echo "$svn_info_tmp" | sed '/^Revision: /!d;s///'`
1382 test x
"$REV" = x
&& REV
=`echo "$svn_info_tmp" \
1383 | sed '/^Last Changed Rev: /!d;s///'`
1384 test x
"$REV" = x
&& abort
'Cannot detect the current revision.'
1386 # FIXME: Incomplete.
1387 if yesno
"Are you sure you want to run:
1388 $SVN merge -r$amerge:$REV $1 $2 ?"; then
1389 $SVN merge
"-r$amerge:$REV" "$1" "$2"
1393 # svn_diffw [args...]
1396 # Ignore white spaces.
1398 git
diff --ignore-all-space -C "$@"
1400 # Can't svn diff -x -w: svn 1.4 only.
1401 $SVN diff --no-diff-deleted --diff-cmd diff -x -uw "$@"
1405 # svn_mail REV [mails...]
1408 test $# -lt 1 && abort
"Not enough arguments provided;
1409 Try 'svn help mail' for more info."
1413 REV
=`git-rev-list --pretty=format:%h 'HEAD^' --max-count=1 | sed '1d;q'`
1415 REV
=`svn_revision || abort 'Cannot get current revision number'`
1416 test x
"$REV" = x
&& abort
'Cannot get current revision number'
1417 if [ "$REV" -lt 1 ]; then
1418 abort
'No previous revision.'
1420 REV
=`expr "$REV" - 1`
1424 REV
=`svn_revision || abort 'Cannot get current revision number'`
1425 test x
"$REV" = x
&& abort
'Cannot get current revision number'
1431 found_committed
=0; found
=0
1432 while [ $found -eq 0 ]; do
1433 this_chlog_dir
=`pwd -P`
1434 if [ -d .
/+committed
]; then
1436 if [ -d .
/+committed
/$REV ]; then
1444 # Stop searching when in / ... hmz :P
1445 test x
`pwd` = x
/ && break
1447 if [ $found -eq 0 ]; then
1448 if [ $found_committed -eq 0 ]; then
1449 abort
'Could not find the +committed directory.'
1451 abort
"Could not find the revision $REV in +committed."
1453 abort
'Internal error (should never be here).'
1456 mail_file
=''; subject
=''; to
=''
1457 if [ -f .
/+committed
/$REV/mail ]; then
1458 # svn-wrapper generated file
1459 mail_file
="./+committed/$REV/mail"
1460 subject
=`sed '/^Subject: /!d;s///' $mail_file | sed '1q'`
1461 to
=`sed '/^To: /!d;s///' $mail_file | sed '1q'`
1462 elif [ -f .
/+committed
/$REV/,iform
] && [ -f .
/+committed
/$REV/,message
]
1464 # VCS-generated file
1465 subject
=`sed '/^Subject: /!d;s///;s/^"//;s/"$//' ./+committed/$REV/,iform \
1466 | sed "s/<%= *rev *%>/$REV/g"`
1467 to
=`sed '/^To:/,/^[^-]/!d' ./+committed/$REV/,iform | sed '1d;s/^- //;$d' \
1468 | xargs | sed 's/ */, /g'`
1469 mail_file
=`get_unique_file_name "$TMPDIR/mail.r$REV"`
1470 echo "From: $FULLNAME <$EMAIL>
1473 " >"$mail_file" || abort
"Cannot create $mail_file"
1474 cat .
/+committed
/$REV/,message
>>"$mail_file" \
1475 || abort
"Cannot copy ./+committed/$REV/,message in $mail_file"
1477 abort
"Couldn't find the mail to re-send in `pwd`/+committed/$REV"
1479 if [ $# -gt 0 ]; then
1480 to
=`echo "$*" | sed 's/ */, /g'`
1483 test x
"$to" = x
&& abort
'Cannot find the list of recipients.
1484 Please report this bug.'
1485 test x
"$subject" = x
&& abort
'Cannot find the subject of the mail.
1486 Please report this bug.'
1488 if yesno
"Re-sending the mail of r$REV
1491 Are you sure?"; then :; else
1498 svn_info_tmp
=`$SVN info`
1499 test $?
-ne 0 && abort
"Failed to get svn info on `pwd`"
1500 repos_root
=`echo "$svn_info_tmp" | sed '/^Repository Root: /!d;s///'`
1501 repos_url
=`echo "$svn_info_tmp" | sed '/^URL: /!d;s///'`
1502 # It looks like svn <1.3 didn't display a "Repository Root" entry.
1503 test x
"$repos_root" = x
&& repos_root
=$repos_url
1506 my_sendmail
"$mail_file" "$subject" "$to" \
1507 "X-svn-url: $repos_url
1508 X-svn-revision: $REV"
1514 echo "Using svn-wrapper v$version (C) SIGOURE Benoit [GPL]"
1515 sed '/^# $Id[:].*$/!d;s/.*$Id[:] *//;s/ *$ *//;s/ \([0-9][0-9]*\)/ (r\1)/' "$me"
1518 # has_prop prop-name [path]
1519 # return value: 0 -> path has the property prop-name set.
1520 # 1 -> path has no property prop-name.
1524 hp_plist
=`$SVN proplist "$2"`
1525 test $?
-ne 0 && return 2
1526 hp_res
=`echo "$hp_plist" | sed "/^ *$1\$/!d"`
1527 test x
"$hp_res" = x
&& return 1
1531 # svn_propadd prop-name prop-val [path]
1535 abort
'propadd is only for SVN, not for Git.'
1538 && abort
'Not enough arguments provided;
1539 try `svn help propadd` for more info'
1541 && abort
'Too many arguments provided;
1542 try `svn help propadd` for more info'
1545 test x
"$path" = x
&& path
='.' && set dummy
"$@" '.' && shift
1546 has_prop
"$1" "$3" ||
{
1547 test $?
-eq 2 && return 1 # svn error
1548 # no property found:
1549 yesno
"'$path' has no property named '$1', do you want to add it?" \
1550 && $SVN propset
"$@"
1554 current_prop_val
=`$SVN propget "$1" "$3"`
1555 test $?
-ne 0 && abort
"Failed to get the current value of property '$1'."
1557 $SVN propset
"$1" "$current_prop_val
1558 $2" "$3" >/dev
/null || abort
"Failed to add '$3' in the property '$1'."
1560 current_prop_val
=`$SVN propget "$1" "$3" || echo "$current_prop_val
1562 echo "property '$1' updated on '$path', new value:
1566 # svn_propsed prop-name sed-script [path]
1570 abort
'propsed is only for SVN, not for Git.'
1573 && abort
'Not enough arguments provided;
1574 try `svn help propsed` for more info'
1576 && abort
'Too many arguments provided;
1577 try `svn help propsed` for more info'
1580 test x
"$path" = x
&& path
='.'
1581 has_prop
"$1" "$3" ||
{
1582 test $?
-eq 2 && return 1 # svn error
1583 # no property found:
1584 abort
"'$path' has no property named '$1'."
1587 prop_val
=`$SVN propget "$1" "$3"`
1588 test $?
-ne 0 && abort
"Failed to get the current value of property '$1'."
1590 prop_val
=`echo "$prop_val" | sed "$2"`
1591 test $?
-ne 0 && abort
"Failed to run the sed script '$2'."
1593 $SVN propset
"$1" "$prop_val" "$3" >/dev
/null \
1594 || abort
"Failed to update the property '$1' with value '$prop_val'."
1596 new_prop_val
=`$SVN propget "$1" "$3" || echo "$prop_val"`
1597 echo "property '$1' updated on '$path', new value:
1601 # svn_revision [args...]
1605 short
=`git-rev-list --pretty=format:%h HEAD --max-count=1 | sed '1d;q'`
1606 long
=`git-rev-list --pretty=format:%H HEAD --max-count=1 | sed '1d;q'`
1607 echo "$short ($long)"
1609 svn_revision_info_out
=`$SVN info "$@"`
1611 echo "$svn_revision_info_out" |
sed '/^Revision: /!d;s///'
1612 return $svn_revision_rv
1616 # svn_ignore [paths]
1619 if [ $# -eq 0 ]; then # Simply display ignore-list.
1621 test -f .gitignore
&& cat .gitignore
1623 $SVN propget
'svn:ignore'
1625 elif [ $# -eq 1 ]; then
1629 echo "$b" >>"$d/.gitignore"
1630 git add
"$d/.gitignore"
1631 notice
'files ignored in this directory:'
1634 svn_propadd
'svn:ignore' "$b" "$d"
1636 else # Add arguments in svn:ignore.
1637 # This part is a bit tricky:
1638 # For each argument, we find all the other arguments with the same dirname
1639 # $dname and we svn:ignore them all in $dname.
1640 while [ $# -ne 0 ]; do
1642 dname
=`dirname "$1"`
1643 files
=`basename "$1"`
1646 while [ $j -lt $argc ] && [ $# -ne 0 ]; do
1649 this_dname
=`dirname "$this_arg"`
1650 this_file
=`basename "$this_arg"`
1651 if [ x
"$dname" = x
"$this_dname" ]; then
1655 set dummy
"$@" "$this_arg"
1661 echo "$files" >>"$dname"/.gitignore
1662 git add
"$dname"/.gitignore
1663 notice
"files ignored in $dname:"
1664 cat "$dname"/.gitignore
1666 svn_propadd
'svn:ignore' "$files" "$dname"
1675 if [ $# -eq 0 ]; then
1680 Additionnal commands provided by svn-wrapper:
1681 automerge (amerge, am)
1696 automerge | auto-merge | amerge | am
)
1697 echo 'automerge (amerge, am): FIXME
1698 usage: automerge FIXME
1703 $SVN help commit |
sed '/^Valid options:/a\
1704 \ --dry-run : do not commit, simply generate a patch with what\
1705 \ would have been comitted (svn-wrapper extension).
1710 echo 'diffstat (ds): Display the histogram from svn diff-output.'
1711 $SVN help diff |
sed '1d;
1712 s/differences*/histogram/;
1713 2,35 s/diff/diffstat/g'
1716 echo "diffw (dw): Display the differences without taking whitespaces\
1718 $SVN help diff |
sed '1d;
1719 2,35 s/diff\([^a-z]\)/diffw\1/g;
1720 /--diff-cmd/,/--no-diff-deleted/d'
1723 echo 'ignore: Add some files in the svn:ignore property.
1724 usage: 1. ignore [PATH]
1725 2. ignore FILE [FILES...] [PATH]
1727 1. Display the value of svn:ignore property on [PATH].
1728 2. Add some files in the svn:ignore property of [PATH].
1730 If you want to add directories in the ignore-list, be careful:
1731 svn ignore foo/ bar/
1732 will add "foo/" in the property svn:ignore within the directory bar!
1734 svn ignore foo/ bar/ .
1735 (It'\''s somewhat like with mv)
1741 echo 'mail: Resend the mail of a given commit.
1742 usage: mail REV [emails]
1744 REV must have an email file associated in +committed/REV.
1745 REV can also be PREV or HEAD.
1747 By default the mail is sent to same email addresses as during the original
1748 commit unless more arguments are given.'
1750 propadd | padd | pa
)
1751 echo 'propadd (padd, pa): Add something in the value of a property.
1752 usage: propadd PROPNAME PROPVAL PATH
1754 PROPVAL will be appended at the end of the property PROPNAME.
1760 echo 'proposal: Alias for: commit --dry-run.
1761 See: svn help commit.'
1764 echo 'propsed (psed): Edit a property with sed.
1765 usage: propsed PROPNAME SED-ARGS PATH
1767 eg: svn propsed svn:externals "s/http/https/" .
1773 echo 'revision (rev): Display the revision number of a local or remote item.'
1774 $SVN help info |
sed '1d;
1775 s/information/revision/g;
1776 s/revision about/the revision of/g;
1777 2,35 s/info/revision/g;
1781 echo 'touch: Touch a file and svn add it.
1782 usage: touch FILE [FILES]...
1787 selfupdate | selfup | self-update | self-up
)
1788 echo 'selfupdate (selfup): Attempt to update svn-wrapper.sh
1795 echo 'version: Display the version info of svn and svn-wrapper.
1806 # svn_status [args...]
1813 svn_status_out
=`$SVN status "$@"`
1815 test x
"$svn_status_out" = x
&& return $svn_status_rv
1816 echo "$svn_status_out" |
sed "$sed_svn_st_color"
1817 return $svn_status_rv
1820 # svn_update [args...]
1823 svn_update_out
=`$SVN update "$@"`
1825 echo "$svn_update_out" |
sed "$sed_svn_up_colors"
1826 return $svn_update_rv
1829 # ------------------- #
1830 # `main' starts here. #
1831 # ------------------- #
1833 # Define colors if stdout is a tty.
1836 else # stdout isn't a tty => don't print colors.
1840 # Consider this as a sed function :P.
1844 s@^?\\(......\\)+@+\\1+@
1845 s@^?\\(......\\)\\(.*/\\)+@+\\1\\2+@
1846 s@^?\\(......\\),@,\\1,@
1847 s@^?\\(......\\)\\(.*/\\),@,\\1\\2,@
1848 s/^\\(.\\)C/\\1${lred}C${std}/
1851 s/^?/${lred}?${std}/; t
1852 s/^M/${lgreen}M${std}/; t
1853 s/^A/${lgreen}A${std}/; t
1854 s/^X/${lblue}X${std}/; t
1855 s/^+/${lyellow}+${std}/; t
1856 s/^D/${lyellow}D${std}/; t
1857 s/^,/${lred},${std}/; t
1858 s/^C/${lred}C${std}/; t
1859 s/^I/${purple}I${std}/; t
1860 s/^R/${lblue}R${std}/; t
1861 s/^!/${lred}!${std}/; t
1862 s/^~/${lwhite}~${std}/; t"
1871 s/^\\(.\\)C/\\1${lred}C${std}/
1872 s/^\\(.\\)U/\\1${lgreen}U${std}/
1873 s/^\\(.\\)D/\\1${lred}D${std}/
1876 s/^A/${lgreen}A${std}/; t
1877 s/^U/${lgreen}U${std}/; t
1878 s/^D/${lyellow}D${std}/; t
1879 s/^G/${purple}G${std}/; t
1880 s/^C/${lred}C${std}/; t"
1883 test "x$1" = x--debug
&& shift && set -x
1885 test "x$1" = x--git
&& shift && git_mode
=: && SVN
=git
1888 # ------------------------------- #
1889 # Hooks for standard SVN commands #
1890 # ------------------------------- #
1912 # -------------------- #
1913 # Custom SVN commands #
1914 # -------------------- #
1915 automerge | auto-merge | amerge | am
)
1917 if [ $# -ne 2 ]; then
1918 abort
"automerge: not enough arguments provided;
1919 try 'svn help automerge' for more info"
1925 if [ -d .git
]; then
1928 require_diffstat
&& $SVN diff --no-diff-deleted "$@" | diffstat
1943 propadd | padd | pa
)
1949 svn_commit
--dry-run "$@"
1961 touch "$@" && $SVN add
"$@"
1963 selfupdate | selfup | self-update | self-up
)
1967 version |
-version |
--version)
1969 set dummy
'--version' "$@"