2 # Simple wrapper around SVN and Git featuring auto-ChangeLog entries and
5 # Copyright (C) 2006, 2007 Benoit Sigoure.
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 # Quick install: alias svn=path/to/svn-wrapper.sh -- that's all.
26 # This script is a wrapper around the SVN and Git command-line clients for
27 # UNIX. It has been designed mainly to automagically generate GNU-style
28 # ChangeLog entries when committing and mail them along with a diff and an
29 # optional comment from the author to a list of persons or a mailing list.
30 # Although this script was originally written for SVN (hence the name), it
31 # also works with Git and tries to detect whether the current repository is a
32 # Git or SVN repository.
33 # This script has been written to be as much portable as possible and cover as
34 # many use-case as reasonably possible. It won't work with Solaris'
35 # brain-damaged stock /bin/sh.
37 # HOWEVER, there will be bugs, there will be cases in which the script doesn't
38 # wrap properly the svn-cli, etc. In this case, you can try to mail me at
39 # <tsuna at lrde dot epita dot fr>. Include the revision of the svn-wrapper
40 # you're using, and full description of what's wrong etc. so I can reproduce
43 # If you feel like, you can try to fix/enhance the script yourself. It only
44 # requires some basic Shell-scripting skills. Knowing sed will prove useful :)
50 # If you're simply looking for the usage, run `svn-wrapper.sh help' (or
51 # `svn help' if you aliased `svn' on svn-wrapper.sh) as usual.
53 # This script is (hopefully) portable, widely commented and self-contained. Do
54 # not hesitate to hack it. It might look rather long (because it does a lot of
55 # things :P) but you should be able to easily locate which part of the code
58 # The script begins by defining several functions. Then it really starts where
59 # the comment "# `main' starts here. #" is placed.
60 # Some svn commands are hooked (eg, `svn st' displays colors). Hooks and
61 # extra commands are defined in functions named `svn_<command-name>'.
67 # * Write a real testsuite. :/
68 # * Automatic proxy configuration depending on the IP in ifconfig?
69 # * Customizable behavior/colors via some ~/.<config>rc file (?)
70 # * Handle things such as svn ci --force foobar.
72 # * svn automerge + automatic fill in branches/README.branches.
73 # => won't do (SVN is too borken WRT merges, I use Git now so I don't feel
74 # like dealing with this sort of SVN issue)
75 # * Automatically recognize svn cp and svn mv instead of writing "New" and
76 # "Remove" in the template ChangeLog entry (hard). Pair the and new/remove
77 # to prepare a good ChangeLog entry (move to X, copy from X) [even harder].
78 # => won't do (Git does this fine, can't be bothered to deal with this for
83 # Default values (the user can export them to override them).
89 if [ -d .git
] ||
[ -d ..
/.git
]; then
105 # Override the locale.
110 : ${COLORDIFF=missing}
112 # Signal number for traps (using plain signal names is not portable and breaks
113 # on recent Debians that use ash as their default shell).
119 # Pitfall: some users might be tempted to export SVN=svn-wrapper.sh for some
120 # reason. This is just *wrong*. The following is an attempt to save them from
122 if [ x
`basename "$SVN"` = x
"$bme" ]; then
123 echo "warning: setting SVN to $bme is wrong"
127 # This code comes (mostly) from Autoconf.
128 # The user is always right.
129 if test "${PATH_SEPARATOR+set}" != set; then
131 (PATH
='/bin;/bin'; FPATH
=$PATH; sh
-c :) >/dev
/null
2>&1 && {
132 (PATH
='/bin:/bin'; FPATH
=$PATH; sh
-c :) >/dev
/null
2>&1 ||
139 # Get the first 6 digits (without forking)
140 revision
=${full_rev#'$'Id': '}
141 revision
=${revision%??????????????????????????????????' $'}
143 # The `main' really starts after the functions definitions.
151 red
='\e[0;31m'; lred
='\e[1;31m'
152 green
='\e[0;32m'; lgreen
='\e[1;32m'
153 yellow
='\e[0;33m'; lyellow
='\e[1;33m'
154 blue
='\e[0;34m'; lblue
='\e[1;34m'
155 purple
='\e[0;35m'; lpurple
='\e[1;35m'
156 cyan
='\e[0;36m'; lcyan
='\e[1;36m'
157 grey
='\e[0;37m'; lgrey
='\e[1;37m'
158 white
='\e[0;38m'; lwhite
='\e[1;38m'
178 echo "svn-wrapper: ${lred}abort${std}: $@" \
179 |
sed '1!s/^[ ]*/ /' >&2
186 echo "svn-wrapper: ${lred}warning${std}: $@" \
187 |
sed '1!s/^[ ]*/ /' >&2
193 echo "svn-wrapper: ${lyellow}notice${std}: $@" \
194 |
sed '1!s/^[ ]*/ /' >&2
201 read answer ||
return 1
206 return 42 # should never happen...
210 # returns true if `yes' or `proceed', false if `new'.
211 # the answer is stored in $yesnoproceed_res which is /yes|new|proceed/
214 printf "$@ [(y)es/(p)roceed/(u)p+proceed/(N)ew] "
215 read answer ||
return 1
217 y
* | Y
*) yesnoproceed_res
=yes; return 0;;
218 p
* | P
*) yesnoproceed_res
=proceed
; return 0;;
219 u
* | U
*) yesnoproceed_res
=upproceed
; return 0;;
220 *) yesnoproceed_res
=new
; return 1;;
222 return 42 # should never happen...
228 warn
"cannot find the environment variable $1
229 You might consider using \`export $1='<FIXME>'\`"
232 # get_unique_file_name file-name
233 get_unique_file_name
()
236 echo "$1" && return 0
239 while test -e "$gufn.$i"; do
245 # ensure_not_empty description value
249 # `value' has at least one non-space character.
250 *[-a-zA-Z0-9_!@
*{}|
,.
/:]*)
253 ene_val
=`echo "$2" | tr -d ' \t\n'`
254 test -z "$ene_val" && abort
"$1: empty value"
257 # find_prog prog-name
258 # return true if prog-name is in the PATH
259 # echo the full path to prog-name on stdout.
260 # Based on a code from texi2dvi
267 test -z "$dir" && continue
268 # The basic test for an executable is `test -f $f && test -x $f'.
269 # (`test -x' is not enough, because it can also be true for directories.)
270 # We have to try this both for $1 and $1.exe.
272 # Note: On Cygwin and DJGPP, `test -x' also looks for .exe. On Cygwin,
273 # also `test -f' has this enhancement, bot not on DJGPP. (Both are
274 # design decisions, so there is little chance to make them consistent.)
275 # Thusly, it seems to be difficult to make use of these enhancements.
277 if test -f "$dir/$1" && test -x "$dir/$1"; then
280 elif test -f "$dir/$1.exe" && test -x "$dir/$1.exe"; then
288 # find_progs prog [progs...]
289 # Look in PATH for one of the programs given in argument.
290 # If none of the progs can be found, the string "exit 2" is "returned".
293 # This code comes mostly from Autoconf.
294 for fp_prog
in "$@"; do
295 fp_res
=`find_prog $fp_prog`
296 if [ $?
-eq 0 ]; then
305 test x
"$EDITOR" = xmissing
&& EDITOR
=`find_progs vim vi emacs nano`
306 test x
"$PAGER" = xmissing
&& PAGER
=`find_progs less more`
307 test x
"$AWK" = xmissing
&& AWK
=`find_progs gawk mawk nawk awk`
308 test x
"$DIFF" = xmissing
&& DIFF
=`find_progs diff`
309 if test x
"$COLORDIFF" = xmissing
; then
311 COLORDIFF
=`find_progs colordiff diff`
317 # -R will tell less to interpret some terminal codes, which will turn on
320 less) PAGER
='less -R';;
324 # return true if diffstat is in the PATH
327 if [ x
"$require_diffstat_cache" != x
]; then
328 return $require_diffstat_cache
330 if (echo | diffstat
) >/dev
/null
2>/dev
/null
; then :; else
331 warn
'diffstat is not installed on your system or not in your PATH.'
332 test -f /etc
/debian_version \
333 && notice
'you might want to `apt-get install diffstat`.'
334 require_diffstat_cache
=1
337 require_diffstat_cache
=0
342 # return 0 -> found a mailer
343 # return !0 -> no mailer found
344 # The full path to the program found is echo'ed on stdout.
348 PATH
="${PATH}${PATH_SEPARATOR}/sbin${PATH_SEPARATOR}/usr/sbin${PATH_SEPARATOR}/usr/libexec"
350 find_progs sendEmail sendmail
mail
357 # my_sendmail mail-file mail-subject mail-to [extra-headers]
358 # mail-to is a comma-separated list of email addresses.
359 # extra-headers is an optionnal argument and will be prepended at the
360 # beginning of the mail headers if the tool used to send mails supports it.
361 # The mail-file may also contain headers. They must be separated from the body
362 # of the mail by a blank line.
365 test -f "$1" || abort
"my_sendmail: Cannot find the mail file: $1"
366 test -z "$2" && warn
'my_sendmail: Empty subject.'
367 test -z "$3" && abort
'my_sendmail: No recipient specified.'
369 content_type
='Content-type: text/plain'
370 extra_headers
="X-Mailer: svn-wrapper v$version (g$revision)
372 Content-Transfer-Encoding: 7bit"
373 if test x
"$4" != x
; then
376 # Remove empty lines.
377 extra_headers
=`echo "$extra_headers" | sed '/^[ ]*$/d;s/^[ ]*//'`
380 # If we have a signature, add it.
381 if test -f ~
/.signature
; then
382 # But don't add it if it is already in the mail.
383 if grep -Fe "`cat ~/.signature`" "$1" >/dev
/null
; then :; else
384 echo '-- ' >>"$1" && cat ~
/.signature
>>"$1"
387 # VCS-compat: handle user option 'sign'.
388 if (grep '^sign: false' ~
/.vcs
) >/dev
/null
2>/dev
/null
; then :; else
389 ($GPG -h) >/dev
/null
2>/dev
/null
391 if [ -e ~
/.gnupg
] ||
[ -e ~
/.gpg
] ||
[ -e ~
/.pgp
] && [ $gpg_rv -lt 42 ]
393 if grep 'BEGIN PGP SIGNATURE' "$1" >/dev
/null
; then
394 notice
'message is already GPG-signed'
395 elif yesno
"Sign the mail using $GPG ?"; then
397 sed '1,/^$/d' "$1" >"$1.msg"
398 sed '1,/^$/!d' "$1" >"$1.hdr"
399 # Sign the message, keep only the PGP signature.
400 $GPG --clearsign <"$1.msg" >"$1.tmp" ||
{
401 rm -f "$1.msg" "$1.hdr" "$1.tmp"
402 abort
"\`$GPG' failed (r=$?)"
404 sed '/^--*BEGIN PGP SIGNATURE--*$/,/^--*END PGP SIGNATURE--*$/!d' \
407 boundary
="svn-wrapper-2-$RANDOM"
408 boundary
="$boundary$RANDOM"
409 # Prepend some stuff before the PGP signature.
412 content-type: application/pgp-signature; x-mac-type=70674453;
414 content-description: This is a digitally signed message part
415 content-disposition: inline; filename=PGP.sig
416 content-transfer-encoding: 7bit" |
cat - "$1.sig" >"$1.tmp"
417 mv -f "$1.tmp" "$1.sig"
419 # Append some stuff after the PGP signature.
421 --$boundary--" >>"$1.sig"
422 # Re-paste the headers before the signed body and prepend some stuff.
423 echo "This is an OpenPGP/MIME signed message (RFC 2440 and 3156)
425 Content-Transfer-Encoding: 8bit
426 Content-Type: text/plain; charset=iso-8859-1; format=flowed
428 |
cat "$1.hdr" - "$1.msg" "$1.sig" >"$1.tmp" \
429 && mv -f "$1.tmp" "$1"
430 content_type
="Content-Type: multipart/signed;\
431 protocol=\"application/pgp-signature\"; micalg=pgp-sha1;\
432 boundary=\"$boundary\""
434 rm -f "$1.tmp" "$1.sig" "$1.msg" "$1.hdr"
435 extra_headers
="$extra_headers
436 X-Pgp-Agent: `$GPG --version | sed q`"
440 extra_headers
="$extra_headers
443 mailer
=`require_mail`
444 if [ $?
-ne 0 ]; then
445 warn
'my_sendmail: No suitable mailer found.'
450 to
=`echo "$3" | sed 's/,//g'`
451 echo "$extra_headers" |
cat - "$1" |
$mailer $to;;
453 cat "$1" |
$mailer -s "$2" "$3";;
455 if [ x
"$SMTP" = x
]; then
456 warn
'my_sendmail: (sendEmail) please tell me the SMTP server to use'
457 printf 'STMP server: '
458 read SMTP || abort
'could not read the SMTP server'
459 notice
"hint: you can export SMTP=$SMTP if you don't want to be asked"
461 sendEmail
-f "$FULLNAME <$EMAIL>" -t "$3" -u "$2" \
462 -s "$SMTP" -o message-file
="$1";;
464 abort
'my_sendmail: Internal error.';;
471 my_url
='http://www.lrde.epita.fr/~sigoure/svn-wrapper'
472 # You can use https if you feel paranoiac.
474 echo ">>> Fetching svn-wrapper.sh from $my_url/svn-wrapper.sh"
476 # --------------------- #
477 # Fetch the new version #
478 # --------------------- #
480 tmp_me
=`get_unique_file_name "$TMPDIR/svn-wrapper.sh"`
481 if (wget
--help) >/dev
/null
2>/dev
/null
; then
482 wget
--no-check-certificate "$my_url/svn-wrapper.sh" -O "$tmp_me"
485 curl
--help >/dev
/null
2>/dev
/null
486 if [ $?
-gt 42 ]; then
487 abort
'Cannot find wget or curl.
488 How can I download any update without them?'
491 curl
--insecure "$my_url/svn-wrapper.sh" >"$tmp_me"
495 || abort
"Cannot find the copy of myself I downloaded in $tmp_me"
497 # ---------------------------------------- #
498 # Compare versions and update if necessary #
499 # ---------------------------------------- #
502 tmp_ver
=`sed '/^# $Id[:].*$/!d;
503 s/.*$Id[:] *\([a-f0-9]\{6\}\).*/\1/' "$tmp_me"`
504 test -z "$tmp_ver" && abort
"Cannot find the revision of $tmp_me"
505 if [ x
"$my_ver" != x
"$tmp_ver" ]; then # There IS an update...
506 echo "An update is available, r$tmp_ver (your version is r$my_ver)"
508 # Wanna see the diff?
509 if yesno
'Do you want to see the diff?'
511 (require_diffstat
&& diff -uw "$me" "$tmp_me" | diffstat
;
513 $COLORDIFF -uw "$me" "$tmp_me") |
$PAGER
517 if yesno
"Overwrite $me (r$my_ver) with $tmp_me (r$tmp_ver)?"; then
519 cp -p "$me" "$me.r$my_ver"
520 mv "$tmp_me" "$me" && exit 0
525 echo "You're already up to date [r$my_ver] :)"
530 # get_svn_diff_and_diffstat [files to diff]
531 # Helper for svn_commit
532 get_svn_diff_and_diffstat
()
535 svn_diff
=`git diff --ignore-all-space --no-color -B -C --cached`
536 svn_diff_stat
=`git diff --stat --ignore-all-space --no-color -B -C --cached`
538 # Ignore white spaces. Can't svn diff -x -w: svn 1.4 only.
539 svn_diff
=`svn_diffw "$@"`
540 test -z "$svn_diff" && svn_diff
=`$SVN diff "$@"`
541 if require_diffstat
; then
542 svn_diff_stat
=`echo "$svn_diff" | diffstat`
544 svn_diff_stat
='diffstat not available'
549 # Helper. Sets the variables repos_url, git_branch, git_head, repos_root,
550 # extra_repos_info and using_git_svn properly.
551 git_get_repos_info_
()
553 # FIXME: 1st commit: "fatal: bad default revision 'HEAD'" on stderr
554 git_config_list
=`git config -l`
556 case $git_config_list in #(
558 repos_url
=`echo "$git_config_list" \
559 | sed '/^svn-remote.svn.url=\(.*\)$/!d;s//\1/'`
562 *remote.origin.url
=*)
563 repos_url
=`echo "$git_config_list" \
564 | sed '/^remote.origin.url=\(.*\)$/!d;s//\1/'`
567 test -z "$repos_url" && repos_url
='(git:unknown)'
568 git_branch
=`git branch | awk '/^\*/ { print substr($0, 3) }'`
569 if [ x
"$git_branch" = x
'(no branch)' ]; then
570 yesno
'You are on a detached HEAD, do you really want to continue?' \
573 git_head
=`git rev-list --pretty=format:%h HEAD --max-count=1 | sed '1d;q'`
574 extra_repos_info
="Git branch: $git_branch (HEAD: $git_head)"
575 repos_root
=$repos_url
578 # git_warn_missing_prop(PROP-NAME, SAMPLE-VALUE)
579 git_warn_missing_prop
()
581 if $using_git_svn; then
582 warn
"No $1 property set for this repository.
583 This is a git-svn repository, so you can set it in an SVN working copy:
585 Otherwise you can just set the property in your git-svn repository:
586 git config svnw.$1 $2"
588 warn
"No $1 property set for this repository.
589 You can set it like this:
590 git config svnw.$1 $2"
594 # Helper. Find the `mailto' property, be it an SVN property or a git-config
595 # option. Relies on the value of $change_log_dir and sets the values of
596 # $mailto (the value of the `mailto' property) and $to (a string to append in
597 # templates that contains the `To:' line, or an empty string if no mail must
599 get_mailto_property
()
601 test -d "$change_log_dir" || abort
'Internal error in get_mailto_property:
602 $change_log_dir not pointing to a directory'
604 mailto
=`git config svnw.mailto`
605 if [ x
"$mailto" = x
] \
606 && [ -f "$change_log_dir/.git/svn/git-svn/unhandled.log" ]
608 mailto
=`grep mailto "$change_log_dir/.git/svn/git-svn/unhandled.log"`
609 sed_tmp
='$!d;s/^.*+dir_prop: . mailto //;s/%40/@/g;s/%2C/,/g;s/%20/ /g;'
610 mailto
=`echo "$mailto" | sed "$sed_tmp"`
612 if [ x
"$mailto" = x
]; then
613 git_warn_missing_prop
'mailto' 'maintainer1@foo.com,maint2@bar.com'
616 mailto
=`$SVN propget mailto "$change_log_dir"`
619 if [ x
"$mailto" = x
]; then
620 test x
$new_user = xyes \
621 && warn
"no svn property mailto found in $change_log_dir
622 You might want to set default email adresses using:
623 svn propset mailto 'somebody@mail.com, foobar@example.com'\
625 # Try to be VCS-compatible and find a list of mails in a *.rb.
626 if [ -d "$change_log_dir/vcs" ]; then
627 mailto
=`grep '.@.*\..*' "$change_log_dir"/vcs/*.rb \
629 | sed 's/^.*[["]\([^["]*@[^]"]*\)[]"].*$/\1/' | xargs`
630 test x
"$mailto" != x
&& test x
$new_user = xyes \
631 && notice
"VCS-compat: found mailto: $mailto
632 in " "$change_log_dir"/vcs
/*.rb
634 fi # end guess mailto
636 # Ensure that emails are comma-separated.
637 mailto
=`echo "$mailto" | sed 's/[ ;]/,/g' | tr -s ',' | sed 's/,/, /g'`
640 if [ x
"$mailto" != xdont_send_mails
] && [ x
"$mailto" != xnothx
]; then
648 # ------------------------------- #
649 # Hooks for standard SVN commands #
650 # ------------------------------- #
652 # svn_commit [args...]
653 # Here is how the commit process goes:
655 # First we look in the arguments passed to commit:
656 # If there are some files or paths, the user wants to commit these only. In
657 # this case, we must search for ChangeLogs from these paths. We might find
658 # more than one ChangeLog, in this case the user will be prompted to pick up
660 # Otherwise (no path passed in the command line) the user just wants to
661 # commit the current working directory.
662 # In any case, we schedule "ChangeLog" for commit.
664 # Alright now that we know which ChangeLog to use, we look in the ChangeLog's
665 # directory if there is a ",svn-log" file which would mean that a previous
666 # commit didn't finish successfully. If there is such a file, the user is
667 # prompted to know whether they want to resume that commit or simply start a
669 # When the user wants to resume a commit, the ",svn-log" file is loaded and we
670 # retrieve the value of "$@" that was saved in the file.
671 # Otherwise we build a template ChangeLog entry.
672 # Then we open the template ChangeLog entry with $EDITOR so that the user
674 # Finally, we commit.
675 # Once the commit is sent, we ask the server to know which revision was
676 # commited and we also retrieve the diff. We then send a mail with these.
682 use_log_message_from_file
=false
685 # Check if the user passed some paths to commit explicitly
686 # because in this case we must add the ChangeLog to the commit and search
687 # the ChangeLog from the dirname of that file.
688 i
=0; search_from
=; add_changelog
=false
; extra_files
=
689 while [ $i -lt $# ]; do
703 test -z "$1" && abort
"$arg needs an argument"
704 test -r "$1" || abort
"'$1' does not seem to be readable"
705 test -w "$1" || abort
"'$1' does not seem to be writable"
706 test -d "$1" && abort
"'$1' seems to be a directory"
707 use_log_message_from_file
=:
708 log_message_to_use
=$1
713 # If the argument is a valid path: add the ChangeLog in the list of
715 if test -e "$arg"; then
717 if test -d "$arg"; then
720 search_from_add
=`dirname "$arg"`
722 search_from
="$search_from:$search_from_add"
725 set dummy
"$@" "$arg"
729 if $add_changelog; then :; else
730 # There is no path/file in the command line: the user wants to commit the
731 # current directory. Make it explicit now:
734 search_from
=`echo "$search_from" | sed 's/^://; s/^$/./'`
736 # ----------------- #
737 # Find ChangeLog(s) #
738 # ----------------- #
740 nb_chlogs
=0; change_log_dirs
=
741 save_IFS
=$IFS; IFS
=':'
742 for dir
in $search_from; do
744 $use_changelog ||
break
745 test -z "$dir" && dir
='.'
746 # First: come back to the original place
747 cd "$here" || abort
"Cannot cd to $here"
748 cd "$dir" ||
continue # Then: Enter $dir (which can be a relative path)
750 while [ $found -eq 0 ]; do
751 this_chlog_dir
=`pwd -P`
752 if [ -f .
/ChangeLog
]; then
754 nb_chlogs
=$
((nb_chlogs
+ 1))
755 change_log_dirs
="$change_log_dirs:$this_chlog_dir"
759 # Stop searching when in / ... hmz :P
760 test x
"$this_chlog_dir" = x
/ && break
761 done # end while: did we find a ChangeLog
762 done # end for: find ChangeLogs in $search_from
763 if [ $nb_chlogs -gt 0 ]; then
764 change_log_dirs
=`echo "$change_log_dirs" | sed 's/^://' | tr ':' '\n' \
766 nb_chlogs
=`echo "$change_log_dirs" | wc -l`
769 # Did we find a ChangeLog? More than one?
770 if [ $nb_chlogs -eq 0 ] && $use_changelog; then
771 if yesno
'svn-wrapper: Error: Cannot find a ChangeLog file!
772 You might want to create an empty one (eg: `touch ChangeLog` where appropriate)
773 Do you want to proceed without using a ChangeLog?'; then
780 elif [ $nb_chlogs -gt 1 ]; then
781 notice
"$nb_chlogs ChangeLogs were found, pick up one:"
784 for a_chlog_dir
in $change_log_dirs; do
786 echo "$i. $a_chlog_dir/ChangeLog"
788 printf "Which ChangeLog do you want to use? [1-$i] "
789 read chlog_no || abort
'Cannot read answer on stdin.'
792 *[^
0-9]*) abort
"Invalid ChangeLog number: $chlog_no"
794 test "$chlog_no" -le $i || abort
"Invalid ChangeLog number: $chlog_no
796 test "$chlog_no" -ge 1 || abort
"Invalid ChangeLog number: $chlog_no
798 change_log_dir
=`echo "$change_log_dirs" | tr ':' '\n' | sed "${chlog_no}!d"`
799 else # Only one ChangeLog found
800 if $use_changelog; then
801 change_log_dir
=$change_log_dirs
802 notice
"using $change_log_dir/ChangeLog"
806 if $use_changelog; then
807 test -f "$change_log_dir/ChangeLog" \
808 || abort
"No such file or directory: $change_log_dir/ChangeLog"
809 # Now we can safely schedule the ChangeLog for the commit.
810 extra_files
="$extra_files:$change_log_dir/ChangeLog"
812 change_log_dir
='.' # Hack. FIXME: Does this work in all cases?
815 if [ -d "$change_log_dir/.git" ] ||
$git_mode; then
820 svn_st_tmp
=`$SVN status "$change_log_dir"`
822 # Warn for files that are not added in the repos.
823 conflicts
=`echo "$svn_st_tmp" | sed '/^ *$/d;
827 if test x
"$conflicts" != x
; then
828 warn
"make sure you don't want to \`svn add'
829 any of the following files before committing:"
830 echo "$conflicts" |
sed "$sed_svn_st_color"
831 printf 'Type [ENTER] to continue :)' && read chiche_is_gay
834 # If there are changes in an svn:externals, advise the user to commit that
836 changed_externals
=`echo "$svn_st_tmp" | $AWK \
845 BEGIN { this_ext = ""; ext = 0; ext_modified = 0; }
846 /^Performing status on external/ {
848 sub(/.* at ./, ""); sub(/.$/, ""); this_ext = $0;
851 /^[ADMR]/ { ext_modified = ext; printext(); }
852 /^.[M]/ { ext_modified = ext; printext(); }
853 END { exit ext_modified; }'`
854 if [ $?
-ne 0 ]; then
855 warn
"the following external items have local modifications:
857 yesno
"You are advised to commit them separately first. Continue anyway?" \
861 # Detect unresolved conflicts / missing files.
862 conflicts
=`echo "$svn_st_tmp" | sed '/^[C!]/!d'`
863 test x
"$conflicts" != x
&& abort
"there are unresolved conflicts (\`C')
864 and/or missing files (\`!'):
867 svn_info_tmp
=`$SVN info "$change_log_dir"`
868 test $?
-ne 0 && abort
"Failed to get svn info on $change_log_dir"
869 repos_root
=`echo "$svn_info_tmp" | sed '/^Repository Root: /!d;s///'`
870 repos_url
=`echo "$svn_info_tmp" | sed '/^URL: /!d;s///'`
871 # It looks like svn <1.3 didn't display a "Repository Root" entry.
872 test -z "$repos_root" && repos_root
=$repos_url
881 # VCS-compat: handle user option 'new_user'
883 grep '^new_user: false' ~
/.vcs
>/dev
/null
2>/dev
/null
&& new_user
='no'
886 tmp_log
="$change_log_dir/,svn-log"
887 $use_log_message_from_file \
888 && tmp_log
=$log_message_to_use \
889 && edit_changelog
=false
891 if [ -f "$tmp_log" ] \
892 && { $use_log_message_from_file \
893 || yesnewproceed
"It looks like the last commit did not\
894 terminate successfully.
895 Would you like to resume it or proceed immediately?"; }; then
896 case $yesnoproceed_res in
897 *proceed
) edit_changelog
=false
;;
899 if test x
"$yesnoproceed_res" = xupproceed
; then
900 svn_update
"$@" || abort
'update failed'
903 internal_tags
=`sed '/^--- Internal stuff, DO NOT change please ---$/,$!d' \
905 saved_args
=`echo "$internal_tags" | sed '/^args: */!d;s///'`
906 extra_files
=`echo "$internal_tags" | sed '/^extra_files: */!d;s///'`
907 if [ x
"$saved_args" != x
]; then
908 if [ x
"$*" != x
] && [ x
"$saved_args" != x
"$*" ]; then
909 warn
"overriding arguments:
910 you invoked $me with the following arguments: $@
911 they have been replaced by these: $saved_args"
912 set dummy
$saved_args
915 notice
"setting the following arguments: $saved_args"
916 set dummy
$saved_args
919 elif [ x
"$*" != x
]; then
920 warn
"overriding arguments:
921 you invoked $me with the following arguments: $@
922 they have been dropped"
929 if [ $git_mode ]; then
930 (cd $change_log_dir && git add
-v -u) || abort
'`git add -v -u` failed'
936 get_svn_diff_and_diffstat
"$@"
938 # Update the file with the new diff/diffstat in case it changed.
943 /^--This line, and those below, will be ignored--$/ {
946 /^ Your ChangeLog entry will appear here\.$/ {
947 if (tlatbwbi_seen) ycewah_seen = 1;
950 if (ycewah_seen != 2) print;
951 if (ycewah_seen == 1) ycewah_seen = 2;
954 if (tlatbwbi_seen == 0)
956 print "--This line, and those below, will be ignored--\n\n" \
957 " Your ChangeLog entry will appear here.";
959 }' "$tmp_log" >"$tmp_log.tmp"
966 $internal_tags" >>"$tmp_log.tmp"
967 mv -f "$tmp_log.tmp" "$tmp_log" || abort
"failed to write '$tmp_log'"
969 else # Build the template message.
971 # ------------------------------------ #
972 # Gather info for the template message #
973 # ------------------------------------ #
976 projname
=`git config svnw.project`
977 if [ x
"$projname" = x
] \
978 && [ -f "$change_log_dir/.git/svn/git-svn/unhandled.log" ]
980 projname
=`grep project "$change_log_dir/.git/svn/git-svn/unhandled.log"`
981 sed_tmp
='$!d;s/^.*+dir_prop: . project //'
982 projname
=`echo "$projname" | sed "$sed_tmp"`
984 if [ x
"$projname" = x
]; then
985 git_warn_missing_prop
'project' 'myproj'
988 projname
=`$SVN propget project "$change_log_dir"`
990 # Try to be VCS-compatible and find a project name in a *.rb.
991 if [ x
"$projname" = x
] && [ -d "$change_log_dir/vcs" ]; then
992 projname
=`sed '/common_commit/!d;s/.*"\(.*\)<%= rev.*/\1/' \
993 "$change_log_dir"/vcs/*.rb`
994 test x
"$projname" != x
&& test x
$new_user = xyes \
995 && notice
"VCS-compat: found project name: $projname
996 in " "$change_log_dir"/vcs
/*.rb
998 test x
"$projname" != x
&& projname
=`echo "$projname" | sed '/[^ ]$/s/$/ /'`
1002 test -z "$FULLNAME" && FULLNAME
='Type Your Name Here' \
1003 && warn_env FULLNAME
1004 test -z "$EMAIL" && EMAIL
='your.mail.here@FIXME.com' && warn_env EMAIL
1007 if $git_commit_all; then
1008 (cd $change_log_dir && git add
-v -u) || abort
'`git add -v -u` failed'
1010 my_git_st
=`git diff -C --raw --cached`
1011 test $?
-eq 0 || abort
'git diff failed'
1012 # Format: ":<old_mode> <new_mode> <old_sha1> <new_sha1> <status>[
1013 # <similarity score>]\t<file-name>"
1014 change_log_files
=`echo "$my_git_st" | sed '
1017 s/^:[0-7 ]* [0-9a-f. ]* M[^ ]* \(.*\)$/ * \1: ./; t
1018 s/^:[0-7 ]* [0-9a-f. ]* A[^ ]* \(.*\)$/ * \1: New./; t
1019 s/^:[0-7 ]* [0-9a-f. ]* D[^ ]* \(.*\)$/ * \1: Remove./; t
1020 s/^:[0-7 ]* [0-9a-f. ]* R[^ ]* \([^ ]*\) \(.*\)$/ * \2: Rename from \1./;t
1021 s/^:[0-7 ]* [0-9a-f. ]* C[^ ]* \([^ ]*\) \(.*\)$/ * \2: Copy from \1./;t
1022 s/^:[0-7 ]* [0-9a-f. ]* T[^ ]* \(.*\)$/ * \1: ./; t
1023 s/^:[0-7 ]* [0-9a-f. ]* X[^ ]* \(.*\)$/ * \1: ???./; t
1024 s/^:[0-7 ]* [0-9a-f. ]* U[^ ]* \(.*\)$/ * \1: UNMERGED./; t
1027 # --ignore-externals appeared after svn 1.1.1
1028 my_svn_st
=`$SVN status --ignore-externals "$@" \
1029 || $SVN status "$@" | sed '/^Performing status on external/ {
1033 # Files to put in the ChangeLog entry.
1034 change_log_files
=`echo "$my_svn_st" | sed '
1037 s/^M......\(.*\)$/ * \1: ./; t
1038 s/^A......\(.*\)$/ * \1: New./; t
1039 s/^D......\(.*\)$/ * \1: Remove./; t
1043 if [ x
"$change_log_files" = x
]; then
1044 yesno
'Nothing to commit, continue anyway?' ||
return 1
1047 change_log_files
=`echo "$change_log_files" | sort -u`
1049 get_svn_diff_and_diffstat
"$@"
1051 # Get any older svn-log out of the way.
1052 test -f "$tmp_log" && mv "$tmp_log" `get_unique_file_name "$tmp_log"`
1053 # If we can't get an older svn-log out of the way, find a new name...
1054 test -f "$tmp_log" && tmp_log
=`get_unique_file_name "$tmp_log"`
1055 if [ x
$new_user = no
]; then
1056 commit_instructions
='
1058 - Fill the ChangeLog entry.
1059 - If you feel like, write a comment in the "Comment:" section.
1060 This comment will only appear in the email, not in the ChangeLog.
1061 By default only the location of the repository is in the comment.
1062 - Some tags will be replaced. Tags are of the form: <TAG>. Unknown
1063 tags will be left unchanged.
1064 - The tag <REV> may only be used in the Subject.
1065 - Your ChangeLog entry will be used as commit message for svn.'
1067 commit_instructions
=
1070 $git_mode && r_before_rev
=
1071 test -z "$extra_repos_info" || extra_repos_info
="
1074 --You must fill this file correctly to continue-- -*- vcs -*-
1076 Subject: ${projname}$r_before_rev<REV>: <TITLE>
1077 From: $FULLNAME <$EMAIL>$to
1080 URL: $repos_url$extra_repos_info
1084 <YYYY>-<MM>-<DD> $FULLNAME <$EMAIL>
1089 --This line, and those below, will be ignored--
1090 $commit_instructions
1091 --Preview of the message that will be sent--
1093 URL: $repos_url$extra_repos_info
1094 Your comments (if any) will appear here.
1097 $YYYY-$MM-$DD $FULLNAME <$EMAIL>
1099 Your ChangeLog entry will appear here.
1104 $svn_diff" >"$tmp_log"
1107 --- Internal stuff, DO NOT change please ---
1108 args: $@" >>"$tmp_log"
1109 echo "extra_files: $extra_files
1110 vi: ft=diff:noet:tw=76:" >>"$tmp_log"
1112 fi # end: if svn-log; then resume? else create template
1113 $edit_changelog && $EDITOR "$tmp_log"
1115 # ------------------ #
1116 # Re-"parse" the log #
1117 # ------------------ #
1119 # hmz this section is a bit messy...
1120 # helper string... !@#$%* escaping \\\\\\...
1121 sed_escape
='s/\\/\\\\/g;s/@/\\@/g;s/&/\\\&/g'
1122 sed_eval_tags
="s/<MM>/$MM/g; s/<DD>/$DD/g; s/<YYYY>/$YYYY/g"
1123 full_log
=`sed '/^--*This line, and those below, will be ignored--*$/,$d;
1124 /^--You must fill this/d' "$tmp_log"`
1125 chlog_entry
=`echo "$full_log" | sed '/^ChangeLog:$/,$!d; //d'`
1126 ensure_not_empty
'ChangeLog entry' "$chlog_entry"
1127 full_log
=`echo "$full_log" | sed '/^ChangeLog:$/,$d'`
1128 mail_comment
=`echo "$full_log" | sed '/^Comment:$/,$!d; //d'`
1129 full_log
=`echo "$full_log" | sed '/^Comment:$/,$d'`
1130 mail_title
=`echo "$full_log" | sed '/^Title: */!d;s///;'`
1131 ensure_not_empty
'commit title' "$mail_title"
1132 # Add a period at the end of the title.
1133 mail_title
=`echo "$mail_title" | sed -e '/ *[.!?]$/!s/ *$/./' \
1134 -e "$sed_eval_tags; $sed_escape"`
1135 sed_eval_tags
="$sed_eval_tags; s@<TITLE>\\.*@$mail_title@g"
1136 mail_comment
=`echo "$mail_comment" | sed "$sed_eval_tags"`
1137 raw_chlog_entry
=$chlog_entry # ChangeLog entry without tags expanded
1138 chlog_entry
=`echo "$chlog_entry" | sed "$sed_eval_tags; 1{
1141 mail_subject
=`echo "$full_log" | sed '/^Subject: */!d;s///'`
1142 ensure_not_empty
'mail subject' "$mail_subject"
1143 mail_to
=`echo "$full_log" | sed '/^To:/!d'`
1145 if test x
"$mail_to" = x
; then
1148 mail_to
=`echo "$mail_to" | sed 's/^To: *//;s///'`
1149 # If there is a <MAILTO> in the 'To:' line, we must expand it.
1153 # Are we meant to send a mail?
1155 '') # No, don't send a mail.
1159 *) # Yes, send a mail.
1160 mail_to
=`echo "$mail_to" | sed "s#<MAILTO>#$mailto#g"`
1164 $send_a_mail && ensure_not_empty
'"To:" field of the mail' "$mail_to"
1167 test -z "$FULLNAME" && warn_env FULLNAME
&& FULLNAME
=$USER
1168 test -z "$EMAIL" && warn_env EMAIL
&& EMAIL
=$USER
1169 myself
=`echo "$FULLNAME <$EMAIL>" | sed "$sed_escape"`
1170 mail_from
=`echo "$full_log" | sed "/^From: */!d;s///;s@<MYSELF>@$myself@g"`
1171 ensure_not_empty
'"From:" field of the mail' "$mail_from"
1173 # ------------------------------------ #
1174 # Sanity checks on the ChangeLog entry #
1175 # ------------------------------------ #
1177 if echo "$chlog_entry" |
grep '<REV>' >/dev
/null
; then
1178 warn
'Using the tag <REV> anywhere else than in the Subject is deprecated.'
1179 yesno
'Continue anyway?' ||
return 1
1182 if echo "$chlog_entry" |
grep ': \.$' >/dev
/null
; then
1183 warn
'It looks like you did not fill all entries in the ChangeLog:'
1184 echo "$chlog_entry" |
grep ': \.$'
1185 yesno
'Continue anyway?' ||
return 1
1188 if echo "$chlog_entry" |
grep '^--* Internal stuff' >/dev
/null
; then
1189 warn
"It looks like you messed up the delimiters and I did not properly
1190 find your ChangeLog entry. Here it is, make sure it is correct:"
1192 yesno
'Continue anyway?' ||
return 1
1195 if echo "$chlog_entry" |
grep -i 'dont[^a-z0-9]' >/dev
/null
; then
1196 warn
"Please avoid typos such as ${lred}dont$std instead of\
1197 ${lgreen}don't$std:"
1198 echo "$chlog_entry" |
grep -n -i 'dont[^a-z0-9]' \
1199 |
sed "s/[dD][oO][nN][tT]/$lred&$std/g"
1200 yesno
'Continue anyway?' ||
return 1
1203 if echo "$chlog_entry" |
grep -i 'cant[^a-z0-9]' >/dev
/null
; then
1204 warn
"Please avoid typos such as ${lred}cant$std instead of\
1205 ${lgreen}can't$std:"
1206 echo "$chlog_entry" |
grep -n -i 'cant[^a-z0-9]' \
1207 |
sed "s/[cC][aA][nN][tT]/$lred&$std/g"
1208 yesno
'Continue anyway?' ||
return 1
1211 if echo "$chlog_entry" |
grep '^.\{80,\}' >/dev
/null
; then
1212 warn
'Please avoid long lines in your ChangeLog entry (80 columns max):'
1213 echo "$chlog_entry" |
grep '^.\{80,\}'
1214 yesno
'Continue anyway?' ||
return 1
1217 if echo "$mail_title" |
grep -i '^[a-z][a-z]*ed[^a-z]' >/dev
/null
; then
1218 warn
'ChangeLog entries should be written in imperative form:'
1219 echo "$mail_title" |
grep -i '^[a-z][a-z]*ed[^a-z]' \
1220 |
sed "s/^\\([a-zA-Z][a-zA-Z]*ed\\)\\([^a-zA-Z]\\)/$lred\\1$std\\2/"
1221 yesno
'Continue anyway?' ||
return 1
1224 # Check whether the user passed -m | --message
1226 while [ $i -lt $# ]; do
1228 # This is not really a reliable way of knowing whether -m | --message was
1229 # passed but hum... Let's assume it'll do :s
1230 if [ x
"$arg" = 'x-m' ] ||
[ x
"$arg" = 'x--message' ]; then
1234 set dummy
"$@" "$arg"
1238 if [ x
"$my_message" = x
]; then
1239 # The title must not be indented in the commit message and must be
1240 # followed by a blank line. This yields much better results with most
1241 # VC-viewer (especially for Git but including for SVN, such as Trac for
1242 # instance). We assume that the title will always be on the 1st line.
1243 sed_git_title
="1s@^[ ]*<TITLE>\\.*@$mail_title\\
1245 # First, remove empty lines at the beginning, if any.
1246 # Remove also the date information (useless in commit messages)
1247 my_message
=`echo "$raw_chlog_entry" \
1249 /^<YYYY>-<[MD][MD]>-<[DM][DM]>/d
1250 /^[1-9][0-9][0-9][0-9]-[0-9][0-9]*-[0-9][0-9]*/d
1254 | sed -e "$sed_git_title" \
1255 -e "$sed_eval_tags; 1{
1259 notice
'you are overriding the commit message.'
1262 # Show suspicious whitespace additions with Git.
1263 $git_mode && git
diff --cached --check
1266 proposal_file
=',proposal'
1267 test -f "$proposal_file" \
1268 && proposal_file
=`get_unique_file_name "$proposal_file"`
1269 sed_tmp
='s/<REV>/???/g;s/\([^.]\) *\.$/\1/'
1270 mail_subject
=`echo "$mail_subject" | sed "$sed_eval_tags; $sed_tmp"`
1272 if [ x
"$mailto" != xdont_send_mails
] && [ x
"$mailto" != xnothx
]; then
1278 Subject: $mail_subject
1288 $svn_diff" >"$proposal_file"
1289 notice
"A proposal of your commit was left in '$proposal_file'"
1296 || notice
'You are using git, unlike SVN, do not forget to git add your
1300 # Change edit_changelog so that we're asking the confirmation below.
1301 $use_log_message_from_file && edit_changelog
=: \
1302 && notice
"You are about to commit the following change:
1307 yesno
'Are you sure you want to commit?' \
1311 # Add the ChangeLog entry
1312 old_chlog
=`get_unique_file_name "$change_log_dir/ChangeLog.old"`
1313 mv "$change_log_dir/ChangeLog" "$old_chlog" || \
1314 abort
'Could not backup ChangeLog'
1315 trap "echo SIGINT; mv \"$old_chlog\" \"$change_log_dir/ChangeLog\";
1317 echo "$chlog_entry" >"$change_log_dir/ChangeLog"
1318 echo >>"$change_log_dir/ChangeLog"
1319 cat "$old_chlog" >>"$change_log_dir/ChangeLog"
1321 # Add extra files such as cwd or ChangeLog to the commit.
1322 tmp_sed
='s/ /\\ /g' # Escape spaces for the shell.
1323 if $git_mode && $use_changelog; then
1324 # Schedule the ChangeLog for the next commit
1325 (cd "$change_log_dir" && git add ChangeLog
) \
1326 || abort
'failed to git add the ChangeLog'
1329 extra_files
=`echo "$extra_files" | sed "$tmp_sed" | tr ':' '\n'`
1332 # Always sign the commits with Git (but not with git-svn).
1333 $using_git_svn ||
$git_mode && set dummy
--signoff "$@" && shift
1335 # Update the Git index if necessary (just in case the user changed his
1336 # working copy in the mean time)
1337 if $git_mode && $git_commit_all; then
1338 (cd $change_log_dir && git add
-v -u) || abort
'`git add -v -u` failed'
1341 # --Commit-- finally! :D
1342 $SVN commit
-m "$my_message" "$@" $extra_files ||
{
1344 mv "$old_chlog" "$change_log_dir/ChangeLog"
1345 abort
"Commit failed, $SVN returned $svn_commit_rv"
1348 printf 'Getting the revision number... '
1350 REV
=`git rev-list --pretty=format:%h HEAD --max-count=1 | sed '1d;q'`
1352 svn_info_tmp
=`$SVN info "$change_log_dir/ChangeLog"`
1353 REV
=`echo "$svn_info_tmp" | sed '/^Revision: /!d;s///'`
1354 test -z "$REV" && REV
=`echo "$svn_info_tmp" \
1355 | sed '/^Last Changed Rev: /!d;s///'`
1357 test -z "$REV" && abort
'Cannot detect the current revision.'
1360 # Let's make sure we have the real diff by asking the ChangeSet we've just
1361 # committed to the server.
1363 # Backup the old stuff in case we fail to get the real diff from the server
1364 # for some reason...
1365 save_svn_diff
=$svn_diff
1366 save_svn_diff_stat
=$svn_diff_stat
1369 svn_diff
=`git diff --ignore-all-space --no-color -C 'HEAD^' HEAD`
1370 svn_diff_stat
=`git diff --stat --ignore-all-space --no-color -C 'HEAD^' HEAD`
1372 notice
'Do not forget to use `git-svn dcommit` to push your commits in SVN'
1374 # Fetch the ChangeSet and filter out the ChangeLog entry. We don't use
1375 # svn diff -c because this option is not portable to older svn versions.
1376 REV_MINUS_ONE
=$
((REV
- 1))
1377 svn_diff
=`svn_diffw -r"$REV_MINUS_ONE:$REV" "$repos_root" \
1378 | $AWK '/^Index: / { if (in_chlog) in_chlog = 0; }
1379 /^Index: .*ChangeLog$/ { in_chlog = 1 }
1380 { if (!in_chlog) print }'`
1381 if [ x
"$svn_diff" = x
]; then
1382 svn_diff
=$save_svn_diff
1383 svn_diff_stat
=$save_svn_diff_stat
1385 if require_diffstat
; then
1386 svn_diff_stat
=`echo "$svn_diff" | diffstat`
1388 svn_diff_stat
='diffstat not available'
1393 # Expand <REV> and remove the final period from the mail subject if there is
1395 sed_tmp
="s/<REV>/$REV/g;"'s/\([^.]\) *\.$/\1/'
1396 mail_subject
=`echo "$mail_subject" | sed "$sed_eval_tags; $sed_tmp"`
1398 mail_file
=`get_unique_file_name "$change_log_dir/+mail"`
1402 Subject: $mail_subject
1412 $svn_diff" |
sed 's/^\.$/ ./' >"$mail_file"
1413 # We change lines with only a `.' because they could mean "end-of-mail"
1416 if $send_a_mail; then
1417 trap 'echo SIGINT; exec < /dev/null' $SIGINT
1418 # FIXME: Move the mail to the +committed right now, in case the user
1419 # CTLR+C the mail-sending-thing, so that the mail will be properly saved
1421 my_sendmail
"$mail_file" "$mail_subject" "$mail_to" \
1422 "X-svn-url: $repos_root
1423 X-svn-revision: $REV"
1424 fi # end do we have to send a mail?
1427 save_mail_file
=`echo "$mail_file" | sed 's/+//'`
1428 mkdir
-p "$change_log_dir/+committed" \
1429 || warn
"Couldn't mkdir -p $change_log_dir/+committed"
1430 if [ -d "$change_log_dir/vcs" ] \
1431 ||
[ -d "$change_log_dir/+committed" ]
1433 mkdir
-p "$change_log_dir/+committed/$REV" \
1434 && mv "$mail_file" "$change_log_dir/+committed/$REV/mail"
1436 return $svn_commit_rv
1439 # svn_diffw [args...]
1442 # Ignore white spaces.
1447 # Can't svn diff -x -w: svn 1.4 only.
1448 # Moreover we *MUST* use -x -uw, not -x -u -w or -x -u -x -w ...
1449 # Hence the hack to stick both diff arguments together...
1451 test x
"$1" = x
'--SVNW-HACK-w' && shift && diffarg
='-uw'
1452 $SVN diff --no-diff-deleted --diff-cmd $DIFF -x $diffarg "$@"
1456 # svn_diffw [args...]
1459 # Ignore white spaces.
1461 svn_diff
--ignore-all-space "$@"
1463 svn_diff
--SVNW-HACK-w "$@"
1467 # svn_mail REV [mails...]
1470 test $# -lt 1 && abort
"Not enough arguments provided;
1471 Try 'svn help mail' for more info."
1475 REV
=`git rev-list --pretty=format:%h 'HEAD^' --max-count=1 | sed '1d;q'`
1477 REV
=`svn_revision || abort 'Cannot get current revision number'`
1478 test -z "$REV" && abort
'Cannot get current revision number'
1479 if [ "$REV" -lt 1 ]; then
1480 abort
'No previous revision.'
1486 REV
=`svn_revision || abort 'Cannot get current revision number'`
1487 test -z "$REV" && abort
'Cannot get current revision number'
1493 found_committed
=0; found
=0
1494 while [ $found -eq 0 ]; do
1495 this_chlog_dir
=`pwd -P`
1496 if [ -d .
/+committed
]; then
1498 if [ -d .
/+committed
/$REV ]; then
1506 # Stop searching when in / ... hmz :P
1507 test x
`pwd` = x
/ && break
1509 if [ $found -eq 0 ]; then
1510 if [ $found_committed -eq 0 ]; then
1511 abort
'Could not find the +committed directory.'
1513 abort
"Could not find the revision $REV in +committed."
1515 abort
'Internal error (should never be here).'
1518 mail_file
=; subject
=; to
=
1519 if [ -f .
/+committed
/$REV/mail ]; then
1520 # svn-wrapper generated file
1521 mail_file
="./+committed/$REV/mail"
1522 subject
=`sed '/^Subject: /!d;s///' $mail_file | sed '1q'`
1523 to
=`sed '/^To: /!d;s///' $mail_file | sed '1q'`
1524 elif [ -f .
/+committed
/$REV/,iform
] && [ -f .
/+committed
/$REV/,message
]
1526 # VCS-generated file
1527 subject
=`sed '/^Subject: /!d;s///;s/^"//;s/"$//' ./+committed/$REV/,iform \
1528 | sed "s/<%= *rev *%>/$REV/g"`
1529 to
=`sed '/^To:/,/^[^-]/!d' ./+committed/$REV/,iform | sed '1d;s/^- //;$d' \
1530 | xargs | sed 's/ */, /g'`
1531 mail_file
=`get_unique_file_name "$TMPDIR/mail.r$REV"`
1532 echo "From: $FULLNAME <$EMAIL>
1535 " >"$mail_file" || abort
"Cannot create $mail_file"
1536 cat .
/+committed
/$REV/,message
>>"$mail_file" \
1537 || abort
"Cannot copy ./+committed/$REV/,message in $mail_file"
1539 abort
"Couldn't find the mail to re-send in `pwd`/+committed/$REV"
1541 if [ $# -gt 0 ]; then
1542 to
=`echo "$*" | sed 's/ */, /g'`
1545 test -z "$to" && abort
'Cannot find the list of recipients.
1546 Please report this bug.'
1547 test -z "$subject" && abort
'Cannot find the subject of the mail.
1548 Please report this bug.'
1550 if yesno
"Re-sending the mail of r$REV
1553 Are you sure?"; then :; else
1560 svn_info_tmp
=`$SVN info`
1561 test $?
-ne 0 && abort
"Failed to get svn info on `pwd`"
1562 repos_root
=`echo "$svn_info_tmp" | sed '/^Repository Root: /!d;s///'`
1563 repos_url
=`echo "$svn_info_tmp" | sed '/^URL: /!d;s///'`
1564 # It looks like svn <1.3 didn't display a "Repository Root" entry.
1565 test -z "$repos_root" && repos_root
=$repos_url
1568 my_sendmail
"$mail_file" "$subject" "$to" \
1569 "X-svn-url: $repos_url
1570 X-svn-revision: $REV"
1576 echo "Using svn-wrapper v$version-g$revision (C) SIGOURE Benoit [GPL]"
1579 # has_prop prop-name [path]
1580 # return value: 0 -> path has the property prop-name set.
1581 # 1 -> path has no property prop-name.
1585 hp_plist
=`$SVN proplist "$2"`
1586 test $?
-ne 0 && return 2
1587 hp_res
=`echo "$hp_plist" | sed "/^ *$1\$/!d"`
1588 test -z "$hp_res" && return 1
1592 # svn_propadd prop-name prop-val [path]
1596 abort
'propadd is only for SVN, not for Git.'
1599 && abort
'Not enough arguments provided;
1600 try `svn help propadd` for more info'
1602 && abort
'Too many arguments provided;
1603 try `svn help propadd` for more info'
1606 test -z "$path" && path
='.' && set dummy
"$@" '.' && shift
1607 has_prop
"$1" "$3" ||
{
1608 test $?
-eq 2 && return 1 # svn error
1609 # no property found:
1610 yesno
"'$path' has no property named '$1', do you want to add it?" \
1611 && $SVN propset
"$@"
1615 current_prop_val
=`$SVN propget "$1" "$3"`
1616 test $?
-ne 0 && abort
"Failed to get the current value of property '$1'."
1618 $SVN propset
"$1" "$current_prop_val
1619 $2" "$3" >/dev
/null || abort
"Failed to add '$3' in the property '$1'."
1621 current_prop_val
=`$SVN propget "$1" "$3" || echo "$current_prop_val
1623 echo "property '$1' updated on '$path', new value:
1627 # svn_propsed prop-name sed-script [path]
1631 abort
'propsed is only for SVN, not for Git.'
1634 && abort
'Not enough arguments provided;
1635 try `svn help propsed` for more info'
1637 && abort
'Too many arguments provided;
1638 try `svn help propsed` for more info'
1641 test -z "$path" && path
='.'
1642 has_prop
"$1" "$3" ||
{
1643 test $?
-eq 2 && return 1 # svn error
1644 # no property found:
1645 abort
"'$path' has no property named '$1'."
1648 prop_val
=`$SVN propget "$1" "$3"`
1649 test $?
-ne 0 && abort
"Failed to get the current value of property '$1'."
1651 prop_val
=`echo "$prop_val" | sed "$2"`
1652 test $?
-ne 0 && abort
"Failed to run the sed script '$2'."
1654 $SVN propset
"$1" "$prop_val" "$3" >/dev
/null \
1655 || abort
"Failed to update the property '$1' with value '$prop_val'."
1657 new_prop_val
=`$SVN propget "$1" "$3" || echo "$prop_val"`
1658 echo "property '$1' updated on '$path', new value:
1662 # svn_revision [args...]
1666 short
=`git rev-list --pretty=format:%h HEAD --max-count=1 | sed '1d;q'`
1667 long
=`git rev-list --pretty=format:%H HEAD --max-count=1 | sed '1d;q'`
1668 echo "$short ($long)"
1670 svn_revision_info_out
=`$SVN info "$@"`
1672 echo "$svn_revision_info_out" |
sed '/^Revision: /!d;s///'
1673 return $svn_revision_rv
1677 # svn_ignore [paths]
1680 if [ $# -eq 0 ]; then # Simply display ignore-list.
1682 test -f .gitignore
&& cat .gitignore
1684 $SVN propget
'svn:ignore'
1686 elif [ $# -eq 1 ]; then
1690 echo "$b" >>"$d/.gitignore"
1691 git add
"$d/.gitignore"
1692 notice
'files ignored in this directory:'
1695 svn_propadd
'svn:ignore' "$b" "$d"
1697 else # Add arguments in svn:ignore.
1698 # This part is a bit tricky:
1699 # For each argument, we find all the other arguments with the same dirname
1700 # $dname and we svn:ignore them all in $dname.
1701 while [ $# -ne 0 ]; do
1703 dname
=`dirname "$1"`
1704 files
=`basename "$1"`
1707 while [ $j -lt $argc ] && [ $# -ne 0 ]; do
1710 this_dname
=`dirname "$this_arg"`
1711 this_file
=`basename "$this_arg"`
1712 if [ x
"$dname" = x
"$this_dname" ]; then
1716 set dummy
"$@" "$this_arg"
1722 echo "$files" >>"$dname"/.gitignore
1723 git add
"$dname"/.gitignore
1724 notice
"files ignored in $dname:"
1725 cat "$dname"/.gitignore
1727 svn_propadd
'svn:ignore' "$files" "$dname"
1736 if [ $# -eq 0 ]; then
1741 Additionnal commands provided by svn-wrapper:
1746 propadd (padd, pa) -- SVN only
1748 propsed (psed) -- SVN only
1757 $SVN help commit |
sed '/^Valid options:/i\
1758 Extra options provided by svn-wrapper:\
1759 \ --dry-run : do not commit, simply generate a patch with what\
1760 \ would have been comitted.\
1761 \ --use-log-file FILE : extract the ChangeLog entry from FILE. This\
1762 \ entry must be formated in a similar fashion to\
1763 \ what svn-wrapper usually asks you to fill in.\
1764 \ The FILE needs to be writable and will be\
1765 \ removed by svn-wrapper upon success.\
1771 echo 'diffstat (ds): Display the histogram from svn diff-output.'
1772 $SVN help diff |
sed '1d;
1773 s/differences*/histogram/;
1774 2,35 s/diff/diffstat/g'
1777 echo "diffw (dw): Display the differences without taking whitespaces\
1779 $SVN help diff |
sed '1d;
1780 2,35 s/diff\([^a-z]\)/diffw\1/g;
1781 /--diff-cmd/,/--no-diff-deleted/d'
1784 what
='svn:ignore property'
1785 $git_mode && what
='.gitignore file'
1787 ignore: Add some files in the $what.
1788 usage: 1. ignore [PATH]
1789 2. ignore FILE [FILES...]
1791 1. Display the value of $what on [PATH].
1792 2. Add some files in the $what of the directory containing them.
1794 When adding ignores, each pattern is ignored in its own directory, e.g.:
1795 $bme ignore dir/file "d2/*.o"
1796 Will put 'file' in the $what of 'dir' and '*.o' in the
1804 echo 'mail: Resend the mail of a given commit.
1805 usage: mail REV [emails]
1807 REV must have an email file associated in +committed/REV.
1808 REV can also be PREV or HEAD.
1810 By default the mail is sent to same email addresses as during the original
1811 commit unless more arguments are given.'
1813 propadd | padd | pa
)
1814 echo 'propadd (padd, pa): Add something in the value of a property.
1815 usage: propadd PROPNAME PROPVAL PATH
1816 This command only works in SVN mode.
1818 PROPVAL will be appended at the end of the property PROPNAME.
1824 echo 'proposal: Alias for: commit --dry-run.
1825 See: svn help commit.'
1828 echo 'propsed (psed): Edit a property with sed.
1829 usage: propsed PROPNAME SED-ARGS PATH
1830 This command only works in SVN mode.
1832 eg: svn propsed svn:externals "s/http/https/" .
1838 echo 'revision (rev): Display the revision number of a local or remote item.'
1839 $SVN help info |
sed '1d;
1840 s/information/revision/g;
1841 s/revision about/the revision of/g;
1842 2,35 s/info/revision/g;
1846 echo 'touch: Touch a file and svn add it.
1847 usage: touch FILE [FILES]...
1852 selfupdate | selfup | self-update | self-up
)
1853 echo 'selfupdate (selfup): Attempt to update svn-wrapper.sh
1860 echo 'version: Display the version info of svn and svn-wrapper.
1871 # svn_status [args...]
1878 svn_status_out
=`$SVN status "$@"`
1880 test -z "$svn_status_out" && return $svn_status_rv
1881 echo "$svn_status_out" |
sed "$sed_svn_st_color"
1882 return $svn_status_rv
1885 # svn_update [args...]
1888 svn_update_out
=`$SVN update "$@"`
1890 echo "$svn_update_out" |
sed "$sed_svn_up_colors"
1891 return $svn_update_rv
1894 # ------------------- #
1895 # `main' starts here. #
1896 # ------------------- #
1898 # Define colors if stdout is a tty.
1901 else # stdout isn't a tty => don't print colors.
1905 # Consider this as a sed function :P.
1909 s@^?\\(......\\)+@+\\1+@
1910 s@^?\\(......\\)\\(.*/\\)+@+\\1\\2+@
1911 s@^?\\(......\\),@,\\1,@
1912 s@^?\\(......\\)\\(.*/\\),@,\\1\\2,@
1913 s/^\\(.\\)C/\\1${lred}C${std}/
1916 s/^?/${lred}?${std}/; t
1917 s/^M/${lgreen}M${std}/; t
1918 s/^A/${lgreen}A${std}/; t
1919 s/^X/${lblue}X${std}/; t
1920 s/^+/${lyellow}+${std}/; t
1921 s/^D/${lyellow}D${std}/; t
1922 s/^,/${lred},${std}/; t
1923 s/^C/${lred}C${std}/; t
1924 s/^I/${purple}I${std}/; t
1925 s/^R/${lblue}R${std}/; t
1926 s/^!/${lred}!${std}/; t
1927 s/^~/${lwhite}~${std}/; t"
1936 s/^\\(.\\)C/\\1${lred}C${std}/
1937 s/^\\(.\\)U/\\1${lgreen}U${std}/
1938 s/^\\(.\\)D/\\1${lred}D${std}/
1941 s/^A/${lgreen}A${std}/; t
1942 s/^U/${lgreen}U${std}/; t
1943 s/^D/${lyellow}D${std}/; t
1944 s/^G/${purple}G${std}/; t
1945 s/^C/${lred}C${std}/; t"
1948 test "x$1" = x--debug
&& shift && set -x
1950 test "x$1" = x--git
&& shift && git_mode
=: && SVN
=git
1952 test "x$1" = x--no-changelog
&& shift && use_changelog
=false
1955 # ------------------------------- #
1956 # Hooks for standard SVN commands #
1957 # ------------------------------- #
1974 # -------------------- #
1975 # Custom SVN commands #
1976 # -------------------- #
1979 if [ -d .git
]; then
1982 require_diffstat
&& $SVN diff --no-diff-deleted "$@" | diffstat
2000 if $git_mode; then # let Git handle log
2002 else # pipe svn log through PAGER by default
2003 exec $SVN "$@" |
$PAGER
2010 propadd | padd | pa
)
2016 svn_commit
--dry-run "$@"
2028 touch "$@" && $SVN add
"$@"
2030 selfupdate | selfup | self-update | self-up
)
2034 version |
-version |
--version)
2036 set dummy
'--version' "$@"