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 echo -n "$@ [(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
248 ene_val
=`echo "$2" | tr -d ' \t\n'`
249 test x
"$ene_val" = x
&& abort
"$1: empty value"
252 # find_prog prog-name
253 # return true if prog-name is in the PATH
254 # echo the full path to prog-name on stdout.
255 # Based on a code from texi2dvi
262 test x
"$dir" = x
&& continue
263 # The basic test for an executable is `test -f $f && test -x $f'.
264 # (`test -x' is not enough, because it can also be true for directories.)
265 # We have to try this both for $1 and $1.exe.
267 # Note: On Cygwin and DJGPP, `test -x' also looks for .exe. On Cygwin,
268 # also `test -f' has this enhancement, bot not on DJGPP. (Both are
269 # design decisions, so there is little chance to make them consistent.)
270 # Thusly, it seems to be difficult to make use of these enhancements.
272 if test -f "$dir/$1" && test -x "$dir/$1"; then
275 elif test -f "$dir/$1.exe" && test -x "$dir/$1.exe"; then
283 # find_progs prog [progs...]
284 # Look in PATH for one of the programs given in argument.
285 # If none of the progs can be found, the string "exit 2" is "returned".
288 # This code comes mostly from Autoconf.
289 for fp_prog
in "$@"; do
290 fp_res
=`find_prog $fp_prog`
291 if [ $?
-eq 0 ]; then
300 test x
"$EDITOR" = xmissing
&& EDITOR
=`find_progs vim vi emacs nano`
301 test x
"$PAGER" = xmissing
&& PAGER
=`find_progs less more`
302 test x
"$AWK" = xmissing
&& AWK
=`find_progs gawk mawk nawk awk`
303 test x
"$DIFF" = xmissing
&& DIFF
=`find_progs diff`
304 test x
"$COLORDIFF" = xmissing
&& COLORDIFF
=`find_progs colordiff diff`
306 # -R will tell less to interpret some terminal codes, which will turn on
309 less) PAGER
='less -R';;
313 # return true if diffstat is in the PATH
316 if [ x
"$require_diffstat_cache" != x
]; then
317 return $require_diffstat_cache
319 if (echo | diffstat
) >/dev
/null
2>/dev
/null
; then :; else
320 warn
'diffstat is not installed on your system or not in your PATH.'
321 test -f /etc
/debian_version \
322 && notice
'you might want to `apt-get install diffstat`.'
323 require_diffstat_cache
=1
326 require_diffstat_cache
=0
331 # return 0 -> found a mailer
332 # return !0 -> no mailer found
333 # The full path to the program found is echo'ed on stdout.
337 PATH
="${PATH}${PATH_SEPARATOR}/sbin${PATH_SEPARATOR}/usr/sbin${PATH_SEPARATOR}/usr/libexec"
339 find_progs sendEmail sendmail
mail
346 # my_sendmail mail-file mail-subject mail-to [extra-headers]
347 # mail-to is a comma-separated list of email addresses.
348 # extra-headers is an optionnal argument and will be prepended at the
349 # beginning of the mail headers if the tool used to send mails supports it.
350 # The mail-file may also contain headers. They must be separated from the body
351 # of the mail by a blank line.
354 test -f "$1" || abort
"my_sendmail: Cannot find the mail file: $1"
355 test x
"$2" = x
&& warn
'my_sendmail: Empty subject.'
356 test x
"$3" = x
&& abort
'my_sendmail: No recipient specified.'
358 content_type
='Content-type: text/plain'
359 extra_headers
="X-Mailer: svn-wrapper v$version (g$revision)
361 Content-Transfer-Encoding: 7bit"
362 if test x
"$4" != x
; then
365 # Remove empty lines.
366 extra_headers
=`echo "$extra_headers" | sed '/^[ ]*$/d;s/^[ ]*//'`
369 # If we have a signature, add it.
370 if test -f ~
/.signature
; then
371 # But don't add it if it is already in the mail.
372 if grep -Fe "`cat ~/.signature`" "$1" >/dev
/null
; then :; else
373 echo '-- ' >>"$1" && cat ~
/.signature
>>"$1"
376 # VCS-compat: handle user option 'sign'.
377 if (grep '^sign: false' ~
/.vcs
) >/dev
/null
2>/dev
/null
; then :; else
378 ($GPG -h) >/dev
/null
2>/dev
/null
380 if [ -e ~
/.gnupg
] ||
[ -e ~
/.gpg
] ||
[ -e ~
/.pgp
] && [ $gpg_rv -lt 42 ]
382 if grep 'BEGIN PGP SIGNATURE' "$1" >/dev
/null
; then
383 notice
'message is already GPG-signed'
384 elif yesno
"Sign the mail using $GPG ?"; then
386 sed '1,/^$/d' "$1" >"$1.msg"
387 sed '1,/^$/!d' "$1" >"$1.hdr"
388 # Sign the message, keep only the PGP signature.
389 $GPG --clearsign <"$1.msg" >"$1.tmp" ||
{
390 rm -f "$1.msg" "$1.hdr" "$1.tmp"
391 abort
"\`$GPG' failed (r=$?)"
393 sed '/^--*BEGIN PGP SIGNATURE--*$/,/^--*END PGP SIGNATURE--*$/!d' \
396 boundary
="svn-wrapper-2-$RANDOM"
397 boundary
="$boundary$RANDOM"
398 # Prepend some stuff before the PGP signature.
401 content-type: application/pgp-signature; x-mac-type=70674453;
403 content-description: This is a digitally signed message part
404 content-disposition: inline; filename=PGP.sig
405 content-transfer-encoding: 7bit" |
cat - "$1.sig" >"$1.tmp"
406 mv -f "$1.tmp" "$1.sig"
408 # Append some stuff after the PGP signature.
410 --$boundary--" >>"$1.sig"
411 # Re-paste the headers before the signed body and prepend some stuff.
412 echo "This is an OpenPGP/MIME signed message (RFC 2440 and 3156)
414 Content-Transfer-Encoding: 8bit
415 Content-Type: text/plain; charset=iso-8859-1; format=flowed
417 |
cat "$1.hdr" - "$1.msg" "$1.sig" >"$1.tmp" \
418 && mv -f "$1.tmp" "$1"
419 content_type
="Content-Type: multipart/signed;\
420 protocol=\"application/pgp-signature\"; micalg=pgp-sha1;\
421 boundary=\"$boundary\""
423 rm -f "$1.tmp" "$1.sig" "$1.msg" "$1.hdr"
424 extra_headers
="$extra_headers
425 X-Pgp-Agent: `$GPG --version | sed q`"
429 extra_headers
="$extra_headers
432 mailer
=`require_mail`
433 if [ $?
-ne 0 ]; then
434 warn
'my_sendmail: No suitable mailer found.'
439 to
=`echo "$3" | sed 's/,//g'`
440 echo "$extra_headers" |
cat - "$1" |
$mailer $to;;
442 cat "$1" |
$mailer -s "$2" "$3";;
444 if [ x
"$SMTP" = x
]; then
445 warn
'my_sendmail: (sendEmail) please tell me the SMTP server to use'
446 echo -n 'STMP server: '
447 read SMTP || abort
'could not read the SMTP server'
448 notice
"hint: you can export SMTP=$SMTP if you don't want to be asked"
450 sendEmail
-f "$FULLNAME <$EMAIL>" -t "$3" -u "$2" \
451 -s "$SMTP" -o message-file
="$1";;
453 abort
'my_sendmail: Internal error.';;
460 my_url
='http://www.lrde.epita.fr/~sigoure/svn-wrapper'
461 # You can use https if you feel paranoiac.
463 echo ">>> Fetching svn-wrapper.sh from $my_url/svn-wrapper.sh"
465 # --------------------- #
466 # Fetch the new version #
467 # --------------------- #
469 tmp_me
=`get_unique_file_name "$TMPDIR/svn-wrapper.sh"`
470 if (wget
--help) >/dev
/null
2>/dev
/null
; then
471 wget
--no-check-certificate "$my_url/svn-wrapper.sh" -O "$tmp_me"
474 curl
--help >/dev
/null
2>/dev
/null
475 if [ $?
-gt 42 ]; then
476 abort
'Cannot find wget or curl.
477 How can I download any update without them?'
480 curl
--insecure "$my_url/svn-wrapper.sh" >"$tmp_me"
484 || abort
"Cannot find the copy of myself I downloaded in $tmp_me"
486 # ---------------------------------------- #
487 # Compare versions and update if necessary #
488 # ---------------------------------------- #
491 tmp_ver
=`sed '/^# $Id[:].*$/!d;
492 s/.*$Id[:] *\([a-f0-9]\{6\}\).*/\1/' "$tmp_me"`
493 test x
"$tmp_ver" = x
&& abort
"Cannot find the revision of $tmp_me"
494 if [ x
"$my_ver" != x
"$tmp_ver" ]; then # There IS an update...
495 echo "An update is available, r$tmp_ver (your version is r$my_ver)"
497 # Wanna see the diff?
498 if yesno
'Do you want to see the diff?'
500 (require_diffstat
&& diff -uw "$me" "$tmp_me" | diffstat
;
502 $COLORDIFF -uw "$me" "$tmp_me") |
$PAGER
506 if yesno
"Overwrite $me (r$my_ver) with $tmp_me (r$tmp_ver)?"; then
508 cp -p "$me" "$me.r$my_ver"
509 mv "$tmp_me" "$me" && exit 0
514 echo "You're already up to date [r$my_ver] :)"
519 # get_svn_diff_and_diffstat [files to diff]
520 # Helper for svn_commit
521 get_svn_diff_and_diffstat
()
524 svn_diff
=`git diff --ignore-all-space --no-color -B -C --cached`
525 svn_diff_stat
=`git diff --stat --ignore-all-space --no-color -B -C --cached`
527 # Ignore white spaces. Can't svn diff -x -w: svn 1.4 only.
528 svn_diff
=`svn_diffw "$@"`
529 test x
"$svn_diff" = x
&& svn_diff
=`$SVN diff "$@"`
530 if require_diffstat
; then
531 svn_diff_stat
=`echo "$svn_diff" | diffstat`
533 svn_diff_stat
='diffstat not available'
538 # Helper. Sets the variables repos_url, git_branch, git_head, repos_root and
539 # extra_repos_info properly.
540 git_get_repos_info_
()
542 # FIXME: 1st commit: "fatal: bad default revision 'HEAD'" on stderr
543 repos_url
=`git config --get svn-remote.svn.url`
544 test x
"$repos_url" = x
&& repos_url
='(git:unknown)'
545 git_branch
=`git branch | awk '/^\*/ { print substr($0, 3) }'`
546 if [ x
"$git_branch" = x
'(no branch)' ]; then
547 yesno
'You are on a detached HEAD, do you really want to continue?' \
550 git_head
=`git rev-list --pretty=format:%h HEAD --max-count=1 | sed '1d;q'`
551 extra_repos_info
="Git branch: $git_branch (HEAD: $git_head)"
552 repos_root
=$repos_url
555 # Helper. Find the `mailto' property, be it an SVN property or a git-config
556 # option. Relies on the value of $change_log_dir and sets the values of
557 # $mailto (the value of the `mailto' property) and $to (a string to append in
558 # templates that contains the `To:' line, or an empty string if no mail must
560 get_mailto_property
()
562 test -d "$change_log_dir" || abort
'Internal error in get_mailto_property:
563 $change_log_dir not pointing to a directory'
565 mailto
=`git config svnw.mailto`
566 if [ x
"$mailto" = x
] \
567 && [ -f "$change_log_dir/.git/svn/git-svn/unhandled.log" ]
569 mailto
=`grep mailto "$change_log_dir/.git/svn/git-svn/unhandled.log"`
570 sed_tmp
='$!d;s/^.*+dir_prop: . mailto //;s/%40/@/g;s/%2C/,/g;s/%20/ /g;'
571 mailto
=`echo "$mailto" | sed "$sed_tmp"`
573 if [ x
"$mailto" = x
]; then
574 warn
'No mailto property set for this repository.
575 If this is a git-svn repository, do this in a SVN working copy:
576 svn propset mailto maintainer1@foo.com,maint2@bar.com .
577 If this is a real git repository, do this:
578 git config svnw.mailto maintainer1@foo.com,maint2@bar.com'
581 mailto
=`$SVN propget mailto "$change_log_dir"`
584 if [ x
"$mailto" = x
]; then
585 test x
$new_user = xyes \
586 && warn
"no svn property mailto found in $change_log_dir
587 You might want to set default email adresses using:
588 svn propset mailto 'somebody@mail.com, foobar@example.com'\
590 # Try to be VCS-compatible and find a list of mails in a *.rb.
591 if [ -d "$change_log_dir/vcs" ]; then
592 mailto
=`grep '.@.*\..*' "$change_log_dir"/vcs/*.rb \
594 | sed 's/^.*[["]\([^["]*@[^]"]*\)[]"].*$/\1/' | xargs`
595 test x
"$mailto" != x
&& test x
$new_user = xyes \
596 && notice
"VCS-compat: found mailto: $mailto
597 in " "$change_log_dir"/vcs
/*.rb
599 fi # end guess mailto
601 # Ensure that emails are comma-separated.
602 mailto
=`echo "$mailto" | sed 's/[ ;]/,/g' | tr -s ',' | sed 's/,/, /g'`
605 if [ x
"$mailto" != xdont_send_mails
] && [ x
"$mailto" != xnothx
]; then
613 # ------------------------------- #
614 # Hooks for standard SVN commands #
615 # ------------------------------- #
617 # svn_commit [args...]
618 # Here is how the commit process goes:
620 # First we look in the arguments passed to commit:
621 # If there are some files or paths, the user wants to commit these only. In
622 # this case, we must search for ChangeLogs from these paths. We might find
623 # more than one ChangeLog, in this case the user will be prompted to pick up
625 # Otherwise (no path passed in the command line) the user just wants to
626 # commit the current working directory.
627 # In any case, we schedule "ChangeLog" for commit.
629 # Alright now that we know which ChangeLog to use, we look in the ChangeLog's
630 # directory if there is a ",svn-log" file which would mean that a previous
631 # commit didn't finish successfully. If there is such a file, the user is
632 # prompted to know whether they want to resume that commit or simply start a
634 # When the user wants to resume a commit, the ",svn-log" file is loaded and we
635 # retrieve the value of "$@" that was saved in the file.
636 # Otherwise we build a template ChangeLog entry.
637 # Then we open the template ChangeLog entry with $EDITOR so that the user
639 # Finally, we commit.
640 # Once the commit is sent, we ask the server to know which revision was
641 # commited and we also retrieve the diff. We then send a mail with these.
647 use_log_message_from_file
=false
650 # Check if the user passed some paths to commit explicitly
651 # because in this case we must add the ChangeLog to the commit and search
652 # the ChangeLog from the dirname of that file.
653 i
=0; search_from
=; add_changelog
=false
; extra_files
=
654 while [ $i -lt $# ]; do
668 test -z "$1" && abort
"$arg needs an argument"
669 test -r "$1" || abort
"'$1' does not seem to be readable"
670 test -w "$1" || abort
"'$1' does not seem to be writable"
671 test -d "$1" && abort
"'$1' seems to be a directory"
672 use_log_message_from_file
=:
673 log_message_to_use
=$1
678 # If the argument is a valid path: add the ChangeLog in the list of
680 if test -e "$arg"; then
682 if test -d "$arg"; then
685 search_from_add
=`dirname "$arg"`
687 search_from
="$search_from:$search_from_add"
690 set dummy
"$@" "$arg"
694 if $add_changelog; then :; else
695 # There is no path/file in the command line: the user wants to commit the
696 # current directory. Make it explicit now:
699 search_from
=`echo "$search_from" | sed 's/^://; s/^$/./'`
701 # ----------------- #
702 # Find ChangeLog(s) #
703 # ----------------- #
705 nb_chlogs
=0; change_log_dirs
=
706 save_IFS
=$IFS; IFS
=':'
707 for dir
in $search_from; do
709 $use_changelog ||
break
710 test -z "$dir" && dir
='.'
711 # First: come back to the original place
712 cd "$here" || abort
"Cannot cd to $here"
713 cd "$dir" ||
continue # Then: Enter $dir (which can be a relative path)
715 while [ $found -eq 0 ]; do
716 this_chlog_dir
=`pwd -P`
717 if [ -f .
/ChangeLog
]; then
719 nb_chlogs
=$
((nb_chlogs
+ 1))
720 change_log_dirs
="$change_log_dirs:$this_chlog_dir"
724 # Stop searching when in / ... hmz :P
725 test x
"$this_chlog_dir" = x
/ && break
726 done # end while: did we find a ChangeLog
727 done # end for: find ChangeLogs in $search_from
728 if [ $nb_chlogs -gt 0 ]; then
729 change_log_dirs
=`echo "$change_log_dirs" | sed 's/^://' | tr ':' '\n' \
731 nb_chlogs
=`echo "$change_log_dirs" | wc -l`
734 # Did we find a ChangeLog? More than one?
735 if [ $nb_chlogs -eq 0 ] && $use_changelog; then
736 if yesno
'svn-wrapper: Error: Cannot find a ChangeLog file!
737 You might want to create an empty one (eg: `touch ChangeLog` where appropriate)
738 Do you want to proceed without using a ChangeLog?'; then
745 elif [ $nb_chlogs -gt 1 ]; then
746 notice
"$nb_chlogs ChangeLogs were found, pick up one:"
749 for a_chlog_dir
in $change_log_dirs; do
751 echo "$i. $a_chlog_dir/ChangeLog"
753 echo -n "Which ChangeLog do you want to use? [1-$i] "
754 read chlog_no || abort
'Cannot read answer on stdin.'
757 *[^
0-9]*) abort
"Invalid ChangeLog number: $chlog_no"
759 test "$chlog_no" -le $i || abort
"Invalid ChangeLog number: $chlog_no
761 test "$chlog_no" -ge 1 || abort
"Invalid ChangeLog number: $chlog_no
763 change_log_dir
=`echo "$change_log_dirs" | tr ':' '\n' | sed "${chlog_no}!d"`
764 else # Only one ChangeLog found
765 if $use_changelog; then
766 change_log_dir
=$change_log_dirs
767 notice
"using $change_log_dir/ChangeLog"
771 if $use_changelog; then
772 test -f "$change_log_dir/ChangeLog" \
773 || abort
"No such file or directory: $change_log_dir/ChangeLog"
774 # Now we can safely schedule the ChangeLog for the commit.
775 extra_files
="$extra_files:$change_log_dir/ChangeLog"
777 change_log_dir
='.' # Hack. FIXME: Does this work in all cases?
780 if [ -d "$change_log_dir/.git" ] ||
$git_mode; then
785 svn_st_tmp
=`$SVN status "$change_log_dir"`
787 # Warn for files that are not added in the repos.
788 conflicts
=`echo "$svn_st_tmp" | sed '/^ *$/d;
792 if test x
"$conflicts" != x
; then
793 warn
"make sure you don't want to \`svn add'
794 any of the following files before committing:"
795 echo "$conflicts" |
sed "$sed_svn_st_color"
796 echo -n 'Type [ENTER] to continue :)' && read chiche_is_gay
799 # If there are changes in an svn:externals, advise the user to commit that
801 changed_externals
=`echo "$svn_st_tmp" | $AWK \
810 BEGIN { this_ext = ""; ext = 0; ext_modified = 0; }
811 /^Performing status on external/ {
813 sub(/.* at ./, ""); sub(/.$/, ""); this_ext = $0;
816 /^[ADMR]/ { ext_modified = ext; printext(); }
817 /^.[M]/ { ext_modified = ext; printext(); }
818 END { exit ext_modified; }'`
819 if [ $?
-ne 0 ]; then
820 warn
"the following external items have local modifications:
822 yesno
"You are advised to commit them separately first. Continue anyway?" \
826 # Detect unresolved conflicts / missing files.
827 conflicts
=`echo "$svn_st_tmp" | sed '/^[C!]/!d'`
828 test x
"$conflicts" != x
&& abort
"there are unresolved conflicts (\`C')
829 and/or missing files (\`!'):
832 svn_info_tmp
=`$SVN info "$change_log_dir"`
833 test $?
-ne 0 && abort
"Failed to get svn info on $change_log_dir"
834 repos_root
=`echo "$svn_info_tmp" | sed '/^Repository Root: /!d;s///'`
835 repos_url
=`echo "$svn_info_tmp" | sed '/^URL: /!d;s///'`
836 # It looks like svn <1.3 didn't display a "Repository Root" entry.
837 test x
"$repos_root" = x
&& repos_root
=$repos_url
846 # VCS-compat: handle user option 'new_user'
848 grep '^new_user: false' ~
/.vcs
>/dev
/null
2>/dev
/null
&& new_user
='no'
851 tmp_log
="$change_log_dir/,svn-log"
852 $use_log_message_from_file \
853 && tmp_log
=$log_message_to_use \
854 && edit_changelog
=false
856 if [ -f "$tmp_log" ] \
857 && { $use_log_message_from_file \
858 || yesnewproceed
"It looks like the last commit did not\
859 terminate successfully.
860 Would you like to resume it or proceed immediately?"; }; then
861 case $yesnoproceed_res in
862 *proceed
) edit_changelog
=false
;;
864 if test x
"$yesnoproceed_res" = xupproceed
; then
865 svn_update
"$@" || abort
'update failed'
868 internal_tags
=`sed '/^--- Internal stuff, DO NOT change please ---$/,$!d' \
870 saved_args
=`echo "$internal_tags" | sed '/^args: */!d;s///'`
871 extra_files
=`echo "$internal_tags" | sed '/^extra_files: */!d;s///'`
872 if [ x
"$saved_args" != x
]; then
873 if [ x
"$*" != x
] && [ x
"$saved_args" != x
"$*" ]; then
874 warn
"overriding arguments:
875 you invoked $me with the following arguments: $@
876 they have been replaced by these: $saved_args"
877 set dummy
$saved_args
880 notice
"setting the following arguments: $saved_args"
881 set dummy
$saved_args
884 elif [ x
"$*" != x
]; then
885 warn
"overriding arguments:
886 you invoked $me with the following arguments: $@
887 they have been dropped"
894 if [ $git_mode ]; then
895 (cd $change_log_dir && git add
-v -u) || abort
'`git add -v -u` failed'
901 get_svn_diff_and_diffstat
"$@"
903 # Update the file with the new diff/diffstat in case it changed.
908 /^--This line, and those below, will be ignored--$/ {
911 /^ Your ChangeLog entry will appear here\.$/ {
912 if (tlatbwbi_seen) ycewah_seen = 1;
915 if (ycewah_seen != 2) print;
916 if (ycewah_seen == 1) ycewah_seen = 2;
919 if (tlatbwbi_seen == 0)
921 print "--This line, and those below, will be ignored--\n\n" \
922 " Your ChangeLog entry will appear here.";
924 }' "$tmp_log" >"$tmp_log.tmp"
931 $internal_tags" >>"$tmp_log.tmp"
932 mv -f "$tmp_log.tmp" "$tmp_log" || abort
"failed to write '$tmp_log'"
934 else # Build the template message.
936 # ------------------------------------ #
937 # Gather info for the template message #
938 # ------------------------------------ #
941 projname
=`git config svnw.project`
942 if [ x
"$projname" = x
] \
943 && [ -f "$change_log_dir/.git/svn/git-svn/unhandled.log" ]
945 projname
=`grep project "$change_log_dir/.git/svn/git-svn/unhandled.log"`
946 sed_tmp
='$!d;s/^.*+dir_prop: . project //'
947 projname
=`echo "$projname" | sed "$sed_tmp"`
949 if [ x
"$projname" = x
]; then
950 warn
'No project name set for this repository.
951 If this is a git-svn repository, do this in a SVN working copy:
952 svn propset project myproj .
953 If this is a real git repository, do this:
954 git config svnw.project myproj'
957 projname
=`$SVN propget project "$change_log_dir"`
959 # Try to be VCS-compatible and find a project name in a *.rb.
960 if [ x
"$projname" = x
] && [ -d "$change_log_dir/vcs" ]; then
961 projname
=`sed '/common_commit/!d;s/.*"\(.*\)<%= rev.*/\1/' \
962 "$change_log_dir"/vcs/*.rb`
963 test x
"$projname" != x
&& test x
$new_user = xyes \
964 && notice
"VCS-compat: found project name: $projname
965 in " "$change_log_dir"/vcs
/*.rb
967 test x
"$projname" != x
&& projname
=`echo "$projname" | sed '/[^ ]$/s/$/ /'`
971 test x
"$FULLNAME" = x
&& FULLNAME
='Type Your Name Here' \
973 test x
"$EMAIL" = x
&& EMAIL
='your.mail.here@FIXME.com' && warn_env EMAIL
976 if $git_commit_all; then
977 (cd $change_log_dir && git add
-v -u) || abort
'`git add -v -u` failed'
979 my_git_st
=`git diff -C --raw --cached`
980 test $?
-eq 0 || abort
'git diff failed'
981 # Format: ":<old_mode> <new_mode> <old_sha1> <new_sha1> <status>[
982 # <similarity score>]\t<file-name>"
983 change_log_files
=`echo "$my_git_st" | sed '
986 s/^:[0-7 ]* [0-9a-f. ]* M[^ ]* \(.*\)$/ * \1: ./; t
987 s/^:[0-7 ]* [0-9a-f. ]* A[^ ]* \(.*\)$/ * \1: New./; t
988 s/^:[0-7 ]* [0-9a-f. ]* D[^ ]* \(.*\)$/ * \1: Remove./; t
989 s/^:[0-7 ]* [0-9a-f. ]* R[^ ]* \([^ ]*\) \(.*\)$/ * \2: Rename from \1./;t
990 s/^:[0-7 ]* [0-9a-f. ]* C[^ ]* \([^ ]*\) \(.*\)$/ * \2: Copy from \1./;t
991 s/^:[0-7 ]* [0-9a-f. ]* T[^ ]* \(.*\)$/ * \1: ./; t
992 s/^:[0-7 ]* [0-9a-f. ]* X[^ ]* \(.*\)$/ * \1: ???./; t
993 s/^:[0-7 ]* [0-9a-f. ]* U[^ ]* \(.*\)$/ * \1: UNMERGED./; t
996 # --ignore-externals appeared after svn 1.1.1
997 my_svn_st
=`$SVN status --ignore-externals "$@" \
998 || $SVN status "$@" | sed '/^Performing status on external/ {
1002 # Files to put in the ChangeLog entry.
1003 change_log_files
=`echo "$my_svn_st" | sed '
1006 s/^M......\(.*\)$/ * \1: ./; t
1007 s/^A......\(.*\)$/ * \1: New./; t
1008 s/^D......\(.*\)$/ * \1: Remove./; t
1012 if [ x
"$change_log_files" = x
]; then
1013 yesno
'Nothing to commit, continue anyway?' ||
return 1
1016 change_log_files
=`echo "$change_log_files" | sort -u`
1018 get_svn_diff_and_diffstat
"$@"
1020 # Get any older svn-log out of the way.
1021 test -f "$tmp_log" && mv "$tmp_log" `get_unique_file_name "$tmp_log"`
1022 # If we can't get an older svn-log out of the way, find a new name...
1023 test -f "$tmp_log" && tmp_log
=`get_unique_file_name "$tmp_log"`
1024 if [ x
$new_user = no
]; then
1025 commit_instructions
='
1027 - Fill the ChangeLog entry.
1028 - If you feel like, write a comment in the "Comment:" section.
1029 This comment will only appear in the email, not in the ChangeLog.
1030 By default only the location of the repository is in the comment.
1031 - Some tags will be replaced. Tags are of the form: <TAG>. Unknown
1032 tags will be left unchanged.
1033 - The tag <REV> may only be used in the Subject.
1034 - Your ChangeLog entry will be used as commit message for svn.'
1036 commit_instructions
=
1039 $git_mode && r_before_rev
=
1040 test x
"$extra_repos_info" = x || extra_repos_info
="
1043 --You must fill this file correctly to continue-- -*- vcs -*-
1045 Subject: ${projname}$r_before_rev<REV>: <TITLE>
1046 From: $FULLNAME <$EMAIL>$to
1049 URL: $repos_url$extra_repos_info
1053 <YYYY>-<MM>-<DD> $FULLNAME <$EMAIL>
1058 --This line, and those below, will be ignored--
1059 $commit_instructions
1060 --Preview of the message that will be sent--
1062 URL: $repos_url$extra_repos_info
1063 Your comments (if any) will appear here.
1066 $YYYY-$MM-$DD $FULLNAME <$EMAIL>
1068 Your ChangeLog entry will appear here.
1073 $svn_diff" >"$tmp_log"
1076 --- Internal stuff, DO NOT change please ---
1077 args: $@" >>"$tmp_log"
1078 echo "extra_files: $extra_files
1079 vi: ft=diff:noet:tw=76:" >>"$tmp_log"
1081 fi # end: if svn-log; then resume? else create template
1082 $edit_changelog && $EDITOR "$tmp_log"
1084 # ------------------ #
1085 # Re-"parse" the log #
1086 # ------------------ #
1088 # hmz this section is a bit messy...
1089 # helper string... !@#$%* escaping \\\\\\...
1090 sed_escape
='s/\\/\\\\/g;s/@/\\@/g;s/&/\\\&/g'
1091 sed_eval_tags
="s/<MM>/$MM/g; s/<DD>/$DD/g; s/<YYYY>/$YYYY/g"
1092 full_log
=`sed '/^--*This line, and those below, will be ignored--*$/,$d;
1093 /^--You must fill this/d' "$tmp_log"`
1094 chlog_entry
=`echo "$full_log" | sed '/^ChangeLog:$/,$!d; //d'`
1095 ensure_not_empty
'ChangeLog entry' "$chlog_entry"
1096 full_log
=`echo "$full_log" | sed '/^ChangeLog:$/,$d'`
1097 mail_comment
=`echo "$full_log" | sed '/^Comment:$/,$!d; //d'`
1098 full_log
=`echo "$full_log" | sed '/^Comment:$/,$d'`
1099 # Add a period at the end of the title.
1100 sed_tmp
='/^Title: */!d;s///;/ *[.!?]$/!s/ *$/./'
1101 mail_title
=`echo "$full_log" | sed "$sed_tmp"`
1102 ensure_not_empty
'commit title' "$mail_title"
1103 mail_title
=`echo "$mail_title" | sed "$sed_eval_tags; $sed_escape"`
1104 sed_eval_tags
="$sed_eval_tags; s@<TITLE>\\.*@$mail_title@g"
1105 mail_comment
=`echo "$mail_comment" | sed "$sed_eval_tags"`
1106 raw_chlog_entry
=$chlog_entry # ChangeLog entry without tags expanded
1107 chlog_entry
=`echo "$chlog_entry" | sed "$sed_eval_tags; 1{
1110 mail_subject
=`echo "$full_log" | sed '/^Subject: */!d;s///'`
1111 ensure_not_empty
'mail subject' "$mail_subject"
1112 mail_to
=`echo "$full_log" | sed '/^To:/!d'`
1114 if test x
"$mail_to" = x
; then
1117 mail_to
=`echo "$mail_to" | sed 's/^To: *//;s///'`
1118 # If there is a <MAILTO> in the 'To:' line, we must expand it.
1122 # Are we meant to send a mail?
1124 '') # No, don't send a mail.
1128 *) # Yes, send a mail.
1129 mail_to
=`echo "$mail_to" | sed "s#<MAILTO>#$mailto#g"`
1133 $send_a_mail && ensure_not_empty
'"To:" field of the mail' "$mail_to"
1136 test x
"$FULLNAME" = x
&& warn_env FULLNAME
&& FULLNAME
=$USER
1137 test x
"$EMAIL" = x
&& warn_env EMAIL
&& EMAIL
=$USER
1138 myself
=`echo "$FULLNAME <$EMAIL>" | sed "$sed_escape"`
1139 mail_from
=`echo "$full_log" | sed "/^From: */!d;s///;s@<MYSELF>@$myself@g"`
1140 ensure_not_empty
'"From:" field of the mail' "$mail_from"
1142 # ------------------------------------ #
1143 # Sanity checks on the ChangeLog entry #
1144 # ------------------------------------ #
1146 if echo "$chlog_entry" |
grep '<REV>' >/dev
/null
; then
1147 warn
'Using the tag <REV> anywhere else than in the Subject is deprecated.'
1148 yesno
'Continue anyway?' ||
return 1
1151 if echo "$chlog_entry" |
grep ': \.$' >/dev
/null
; then
1152 warn
'It looks like you did not fill all entries in the ChangeLog:'
1153 echo "$chlog_entry" |
grep ': \.$'
1154 yesno
'Continue anyway?' ||
return 1
1157 if echo "$chlog_entry" |
grep '^--* Internal stuff' >/dev
/null
; then
1158 warn
"It looks like you messed up the delimiters and I did not properly
1159 find your ChangeLog entry. Here it is, make sure it is correct:"
1161 yesno
'Continue anyway?' ||
return 1
1164 if echo "$chlog_entry" |
grep -i 'dont[^a-z0-9]' >/dev
/null
; then
1165 warn
"Please avoid typos such as ${lred}dont$std instead of\
1166 ${lgreen}don't$std:"
1167 echo "$chlog_entry" |
grep -n -i 'dont[^a-z0-9]' \
1168 |
sed "s/[dD][oO][nN][tT]/$lred&$std/g"
1169 yesno
'Continue anyway?' ||
return 1
1172 if echo "$chlog_entry" |
grep -i 'cant[^a-z0-9]' >/dev
/null
; then
1173 warn
"Please avoid typos such as ${lred}cant$std instead of\
1174 ${lgreen}can't$std:"
1175 echo "$chlog_entry" |
grep -n -i 'cant[^a-z0-9]' \
1176 |
sed "s/[cC][aA][nN][tT]/$lred&$std/g"
1177 yesno
'Continue anyway?' ||
return 1
1180 if echo "$chlog_entry" |
grep '^.\{80,\}' >/dev
/null
; then
1181 warn
'Please avoid long lines in your ChangeLog entry (80 columns max):'
1182 echo "$chlog_entry" |
grep '^.\{80,\}'
1183 yesno
'Continue anyway?' ||
return 1
1186 if echo "$mail_title" |
grep -i '^[a-z][a-z]*ed[^a-z]' >/dev
/null
; then
1187 warn
'ChangeLog entries should be written in imperative form:'
1188 echo "$mail_title" |
grep -i '^[a-z][a-z]*ed[^a-z]' \
1189 |
sed "s/^\\([a-zA-Z][a-zA-Z]*ed\\)\\([^a-zA-Z]\\)/$lred\\1$std\\2/"
1190 yesno
'Continue anyway?' ||
return 1
1193 # Check whether the user passed -m | --message
1195 while [ $i -lt $# ]; do
1197 # This is not really a reliable way of knowing whether -m | --message was
1198 # passed but hum... Let's assume it'll do :s
1199 if [ x
"$arg" = 'x-m' ] ||
[ x
"$arg" = 'x--message' ]; then
1203 set dummy
"$@" "$arg"
1207 if [ x
"$my_message" = x
]; then
1208 # The title must not be indented in the commit message and must be
1209 # followed by a blank line. This yields much better results with most
1210 # VC-viewer (especially for Git but including for SVN, such as Trac for
1211 # instance). We assume that the title will always be on the 1st line.
1212 sed_git_title
="1s@^[ ]*<TITLE>\\.*@$mail_title\\
1214 # First, remove empty lines at the beginning, if any.
1215 # Remove also the date information (useless in commit messages)
1216 my_message
=`echo "$raw_chlog_entry" \
1218 /^<YYYY>-<[MD][MD]>-<[DM][DM]>/d
1219 /^[1-9][0-9][0-9][0-9]-[0-9][0-9]*-[0-9][0-9]*/d
1223 | sed -e "$sed_git_title" \
1224 -e "$sed_eval_tags; 1{
1228 notice
'you are overriding the commit message.'
1231 # Show suspicious whitespace additions with Git.
1232 $git_mode && git
diff --cached --check
1235 proposal_file
=',proposal'
1236 test -f "$proposal_file" \
1237 && proposal_file
=`get_unique_file_name "$proposal_file"`
1238 sed_tmp
='s/<REV>/???/g;s/\([^.]\) *\.$/\1/'
1239 mail_subject
=`echo "$mail_subject" | sed "$sed_eval_tags; $sed_tmp"`
1241 if [ x
"$mailto" != xdont_send_mails
] && [ x
"$mailto" != xnothx
]; then
1247 Subject: $mail_subject
1257 $svn_diff" >"$proposal_file"
1258 notice
"A proposal of your commit was left in '$proposal_file'"
1265 || notice
'You are using git, unlike SVN, do not forget to git add your
1269 # Change edit_changelog so that we're asking the confirmation below.
1270 $use_log_message_from_file && edit_changelog
=: \
1271 && notice
"You are about to commit the following change:
1276 yesno
'Are you sure you want to commit?' \
1280 # Add the ChangeLog entry
1281 old_chlog
=`get_unique_file_name "$change_log_dir/ChangeLog.old"`
1282 mv "$change_log_dir/ChangeLog" "$old_chlog" || \
1283 abort
'Could not backup ChangeLog'
1284 trap "echo SIGINT; mv \"$old_chlog\" \"$change_log_dir/ChangeLog\";
1286 echo "$chlog_entry" >"$change_log_dir/ChangeLog"
1287 echo >>"$change_log_dir/ChangeLog"
1288 cat "$old_chlog" >>"$change_log_dir/ChangeLog"
1290 # Add extra files such as cwd or ChangeLog to the commit.
1291 tmp_sed
='s/ /\\ /g' # Escape spaces for the shell.
1292 if $git_mode && $use_changelog; then
1293 # Schedule the ChangeLog for the next commit
1294 (cd "$change_log_dir" && git add ChangeLog
) \
1295 || abort
'failed to git add the ChangeLog'
1298 extra_files
=`echo "$extra_files" | sed "$tmp_sed" | tr ':' '\n'`
1301 # Always sign the commits with Git.
1302 $git_mode && set dummy
-s "$@" && shift
1304 # Update the Git index if necessary (just in case the user changed his
1305 # working copy in the mean time)
1306 if $git_mode && $git_commit_all; then
1307 (cd $change_log_dir && git add
-v -u) || abort
'`git add -v -u` failed'
1310 # --Commit-- finally! :D
1311 $SVN commit
-m "$my_message" "$@" $extra_files ||
{
1313 mv "$old_chlog" "$change_log_dir/ChangeLog"
1314 abort
"Commit failed, $SVN returned $svn_commit_rv"
1317 echo -n 'Getting the revision number... '
1319 REV
=`git rev-list --pretty=format:%h HEAD --max-count=1 | sed '1d;q'`
1321 svn_info_tmp
=`$SVN info "$change_log_dir/ChangeLog"`
1322 REV
=`echo "$svn_info_tmp" | sed '/^Revision: /!d;s///'`
1323 test x
"$REV" = x
&& REV
=`echo "$svn_info_tmp" \
1324 | sed '/^Last Changed Rev: /!d;s///'`
1326 test x
"$REV" = x
&& abort
'Cannot detect the current revision.'
1329 # Let's make sure we have the real diff by asking the ChangeSet we've just
1330 # committed to the server.
1332 # Backup the old stuff in case we fail to get the real diff from the server
1333 # for some reason...
1334 save_svn_diff
=$svn_diff
1335 save_svn_diff_stat
=$svn_diff_stat
1338 svn_diff
=`git diff --ignore-all-space --no-color -C 'HEAD^' HEAD`
1339 svn_diff_stat
=`git diff --stat --ignore-all-space --no-color -C 'HEAD^' HEAD`
1340 grep svn-remote
"$change_log_dir/.git/config" >/dev
/null
2>&1 && \
1341 notice
'Do not forget to use: git-svn dcommit to push your commits in SVN'
1343 # Fetch the ChangeSet and filter out the ChangeLog entry. We don't use
1344 # svn diff -c because this option is not portable to older svn versions.
1345 REV_MINUS_ONE
=$
((REV
- 1))
1346 svn_diff
=`svn_diffw -r"$REV_MINUS_ONE:$REV" "$repos_root" \
1347 | $AWK '/^Index: / { if (in_chlog) in_chlog = 0; }
1348 /^Index: .*ChangeLog$/ { in_chlog = 1 }
1349 { if (!in_chlog) print }'`
1350 if [ x
"$svn_diff" = x
]; then
1351 svn_diff
=$save_svn_diff
1352 svn_diff_stat
=$save_svn_diff_stat
1354 if require_diffstat
; then
1355 svn_diff_stat
=`echo "$svn_diff" | diffstat`
1357 svn_diff_stat
='diffstat not available'
1362 # Expand <REV> and remove the final period from the mail subject if there is
1364 sed_tmp
="s/<REV>/$REV/g;"'s/\([^.]\) *\.$/\1/'
1365 mail_subject
=`echo "$mail_subject" | sed "$sed_eval_tags; $sed_tmp"`
1367 mail_file
=`get_unique_file_name "$change_log_dir/+mail"`
1371 Subject: $mail_subject
1381 $svn_diff" |
sed 's/^\.$/ ./' >"$mail_file"
1382 # We change lines with only a `.' because they could mean "end-of-mail"
1385 if $send_a_mail; then
1386 trap 'echo SIGINT; exec < /dev/null' $SIGINT
1387 # FIXME: Move the mail to the +committed right now, in case the user
1388 # CTLR+C the mail-sending-thing, so that the mail will be properly saved
1390 my_sendmail
"$mail_file" "$mail_subject" "$mail_to" \
1391 "X-svn-url: $repos_root
1392 X-svn-revision: $REV"
1393 fi # end do we have to send a mail?
1396 save_mail_file
=`echo "$mail_file" | sed 's/+//'`
1397 mkdir
-p "$change_log_dir/+committed" \
1398 || warn
"Couldn't mkdir -p $change_log_dir/+committed"
1399 if [ -d "$change_log_dir/vcs" ] \
1400 ||
[ -d "$change_log_dir/+committed" ]
1402 mkdir
-p "$change_log_dir/+committed/$REV" \
1403 && mv "$mail_file" "$change_log_dir/+committed/$REV/mail"
1405 return $svn_commit_rv
1408 # svn_diffw [args...]
1411 # Ignore white spaces.
1416 # Can't svn diff -x -w: svn 1.4 only.
1417 # Moreover we *MUST* use -x -uw, not -x -u -w or -x -u -x -w ...
1418 # Hence the hack to stick both diff arguments together...
1420 test x
"$1" = x
'--SVNW-HACK-w' && shift && diffarg
='-uw'
1421 $SVN diff --no-diff-deleted --diff-cmd $DIFF -x $diffarg "$@"
1425 # svn_diffw [args...]
1428 # Ignore white spaces.
1430 svn_diff
--ignore-all-space "$@"
1432 svn_diff
--SVNW-HACK-w "$@"
1436 # svn_mail REV [mails...]
1439 test $# -lt 1 && abort
"Not enough arguments provided;
1440 Try 'svn help mail' for more info."
1444 REV
=`git rev-list --pretty=format:%h 'HEAD^' --max-count=1 | sed '1d;q'`
1446 REV
=`svn_revision || abort 'Cannot get current revision number'`
1447 test x
"$REV" = x
&& abort
'Cannot get current revision number'
1448 if [ "$REV" -lt 1 ]; then
1449 abort
'No previous revision.'
1455 REV
=`svn_revision || abort 'Cannot get current revision number'`
1456 test x
"$REV" = x
&& abort
'Cannot get current revision number'
1462 found_committed
=0; found
=0
1463 while [ $found -eq 0 ]; do
1464 this_chlog_dir
=`pwd -P`
1465 if [ -d .
/+committed
]; then
1467 if [ -d .
/+committed
/$REV ]; then
1475 # Stop searching when in / ... hmz :P
1476 test x
`pwd` = x
/ && break
1478 if [ $found -eq 0 ]; then
1479 if [ $found_committed -eq 0 ]; then
1480 abort
'Could not find the +committed directory.'
1482 abort
"Could not find the revision $REV in +committed."
1484 abort
'Internal error (should never be here).'
1487 mail_file
=; subject
=; to
=
1488 if [ -f .
/+committed
/$REV/mail ]; then
1489 # svn-wrapper generated file
1490 mail_file
="./+committed/$REV/mail"
1491 subject
=`sed '/^Subject: /!d;s///' $mail_file | sed '1q'`
1492 to
=`sed '/^To: /!d;s///' $mail_file | sed '1q'`
1493 elif [ -f .
/+committed
/$REV/,iform
] && [ -f .
/+committed
/$REV/,message
]
1495 # VCS-generated file
1496 subject
=`sed '/^Subject: /!d;s///;s/^"//;s/"$//' ./+committed/$REV/,iform \
1497 | sed "s/<%= *rev *%>/$REV/g"`
1498 to
=`sed '/^To:/,/^[^-]/!d' ./+committed/$REV/,iform | sed '1d;s/^- //;$d' \
1499 | xargs | sed 's/ */, /g'`
1500 mail_file
=`get_unique_file_name "$TMPDIR/mail.r$REV"`
1501 echo "From: $FULLNAME <$EMAIL>
1504 " >"$mail_file" || abort
"Cannot create $mail_file"
1505 cat .
/+committed
/$REV/,message
>>"$mail_file" \
1506 || abort
"Cannot copy ./+committed/$REV/,message in $mail_file"
1508 abort
"Couldn't find the mail to re-send in `pwd`/+committed/$REV"
1510 if [ $# -gt 0 ]; then
1511 to
=`echo "$*" | sed 's/ */, /g'`
1514 test x
"$to" = x
&& abort
'Cannot find the list of recipients.
1515 Please report this bug.'
1516 test x
"$subject" = x
&& abort
'Cannot find the subject of the mail.
1517 Please report this bug.'
1519 if yesno
"Re-sending the mail of r$REV
1522 Are you sure?"; then :; else
1529 svn_info_tmp
=`$SVN info`
1530 test $?
-ne 0 && abort
"Failed to get svn info on `pwd`"
1531 repos_root
=`echo "$svn_info_tmp" | sed '/^Repository Root: /!d;s///'`
1532 repos_url
=`echo "$svn_info_tmp" | sed '/^URL: /!d;s///'`
1533 # It looks like svn <1.3 didn't display a "Repository Root" entry.
1534 test x
"$repos_root" = x
&& repos_root
=$repos_url
1537 my_sendmail
"$mail_file" "$subject" "$to" \
1538 "X-svn-url: $repos_url
1539 X-svn-revision: $REV"
1545 echo "Using svn-wrapper v$version-g$revision (C) SIGOURE Benoit [GPL]"
1548 # has_prop prop-name [path]
1549 # return value: 0 -> path has the property prop-name set.
1550 # 1 -> path has no property prop-name.
1554 hp_plist
=`$SVN proplist "$2"`
1555 test $?
-ne 0 && return 2
1556 hp_res
=`echo "$hp_plist" | sed "/^ *$1\$/!d"`
1557 test x
"$hp_res" = x
&& return 1
1561 # svn_propadd prop-name prop-val [path]
1565 abort
'propadd is only for SVN, not for Git.'
1568 && abort
'Not enough arguments provided;
1569 try `svn help propadd` for more info'
1571 && abort
'Too many arguments provided;
1572 try `svn help propadd` for more info'
1575 test x
"$path" = x
&& path
='.' && set dummy
"$@" '.' && shift
1576 has_prop
"$1" "$3" ||
{
1577 test $?
-eq 2 && return 1 # svn error
1578 # no property found:
1579 yesno
"'$path' has no property named '$1', do you want to add it?" \
1580 && $SVN propset
"$@"
1584 current_prop_val
=`$SVN propget "$1" "$3"`
1585 test $?
-ne 0 && abort
"Failed to get the current value of property '$1'."
1587 $SVN propset
"$1" "$current_prop_val
1588 $2" "$3" >/dev
/null || abort
"Failed to add '$3' in the property '$1'."
1590 current_prop_val
=`$SVN propget "$1" "$3" || echo "$current_prop_val
1592 echo "property '$1' updated on '$path', new value:
1596 # svn_propsed prop-name sed-script [path]
1600 abort
'propsed is only for SVN, not for Git.'
1603 && abort
'Not enough arguments provided;
1604 try `svn help propsed` for more info'
1606 && abort
'Too many arguments provided;
1607 try `svn help propsed` for more info'
1610 test x
"$path" = x
&& path
='.'
1611 has_prop
"$1" "$3" ||
{
1612 test $?
-eq 2 && return 1 # svn error
1613 # no property found:
1614 abort
"'$path' has no property named '$1'."
1617 prop_val
=`$SVN propget "$1" "$3"`
1618 test $?
-ne 0 && abort
"Failed to get the current value of property '$1'."
1620 prop_val
=`echo "$prop_val" | sed "$2"`
1621 test $?
-ne 0 && abort
"Failed to run the sed script '$2'."
1623 $SVN propset
"$1" "$prop_val" "$3" >/dev
/null \
1624 || abort
"Failed to update the property '$1' with value '$prop_val'."
1626 new_prop_val
=`$SVN propget "$1" "$3" || echo "$prop_val"`
1627 echo "property '$1' updated on '$path', new value:
1631 # svn_revision [args...]
1635 short
=`git rev-list --pretty=format:%h HEAD --max-count=1 | sed '1d;q'`
1636 long
=`git rev-list --pretty=format:%H HEAD --max-count=1 | sed '1d;q'`
1637 echo "$short ($long)"
1639 svn_revision_info_out
=`$SVN info "$@"`
1641 echo "$svn_revision_info_out" |
sed '/^Revision: /!d;s///'
1642 return $svn_revision_rv
1646 # svn_ignore [paths]
1649 if [ $# -eq 0 ]; then # Simply display ignore-list.
1651 test -f .gitignore
&& cat .gitignore
1653 $SVN propget
'svn:ignore'
1655 elif [ $# -eq 1 ]; then
1659 echo "$b" >>"$d/.gitignore"
1660 git add
"$d/.gitignore"
1661 notice
'files ignored in this directory:'
1664 svn_propadd
'svn:ignore' "$b" "$d"
1666 else # Add arguments in svn:ignore.
1667 # This part is a bit tricky:
1668 # For each argument, we find all the other arguments with the same dirname
1669 # $dname and we svn:ignore them all in $dname.
1670 while [ $# -ne 0 ]; do
1672 dname
=`dirname "$1"`
1673 files
=`basename "$1"`
1676 while [ $j -lt $argc ] && [ $# -ne 0 ]; do
1679 this_dname
=`dirname "$this_arg"`
1680 this_file
=`basename "$this_arg"`
1681 if [ x
"$dname" = x
"$this_dname" ]; then
1685 set dummy
"$@" "$this_arg"
1691 echo "$files" >>"$dname"/.gitignore
1692 git add
"$dname"/.gitignore
1693 notice
"files ignored in $dname:"
1694 cat "$dname"/.gitignore
1696 svn_propadd
'svn:ignore' "$files" "$dname"
1703 # FIXME: Use here-docs.
1706 if [ $# -eq 0 ]; then
1711 Additionnal commands provided by svn-wrapper:
1716 propadd (padd, pa) -- SVN only
1718 propsed (psed) -- SVN only
1727 $SVN help commit |
sed '/^Valid options:/i\
1728 Extra options provided by svn-wrapper:\
1729 \ --dry-run : do not commit, simply generate a patch with what\
1730 \ would have been comitted.\
1731 \ --use-log-file FILE : extract the ChangeLog entry from FILE. This\
1732 \ entry must be formated in a similar fashion to\
1733 \ what svn-wrapper usually asks you to fill in.\
1734 \ The FILE needs to be writable and will be\
1735 \ removed by svn-wrapper upon success.\
1741 echo 'diffstat (ds): Display the histogram from svn diff-output.'
1742 $SVN help diff |
sed '1d;
1743 s/differences*/histogram/;
1744 2,35 s/diff/diffstat/g'
1747 echo "diffw (dw): Display the differences without taking whitespaces\
1749 $SVN help diff |
sed '1d;
1750 2,35 s/diff\([^a-z]\)/diffw\1/g;
1751 /--diff-cmd/,/--no-diff-deleted/d'
1754 what
='svn:ignore property'
1755 $git_mode && what
='.gitignore file'
1756 echo 'ignore: Add some files in the '"$what"'.
1757 usage: 1. ignore [PATH]
1758 2. ignore FILE [FILES...]
1760 1. Display the value of '"$what"' on [PATH].
1761 2. Add some files in the '"$what"' of the directory containing them.
1763 When adding ignores, each pattern is ignored in its own directory, e.g.:
1764 '"$bme"' ignore dir/file "d2/*.o"
1765 Will put `file'\'' in the '"$what"' of `dir'\'' and `*.o'\'' in the
1766 '"$what"' of `d2'\''
1772 echo 'mail: Resend the mail of a given commit.
1773 usage: mail REV [emails]
1775 REV must have an email file associated in +committed/REV.
1776 REV can also be PREV or HEAD.
1778 By default the mail is sent to same email addresses as during the original
1779 commit unless more arguments are given.'
1781 propadd | padd | pa
)
1782 echo 'propadd (padd, pa): Add something in the value of a property.
1783 usage: propadd PROPNAME PROPVAL PATH
1784 This command only works in SVN mode.
1786 PROPVAL will be appended at the end of the property PROPNAME.
1792 echo 'proposal: Alias for: commit --dry-run.
1793 See: svn help commit.'
1796 echo 'propsed (psed): Edit a property with sed.
1797 usage: propsed PROPNAME SED-ARGS PATH
1798 This command only works in SVN mode.
1800 eg: svn propsed svn:externals "s/http/https/" .
1806 echo 'revision (rev): Display the revision number of a local or remote item.'
1807 $SVN help info |
sed '1d;
1808 s/information/revision/g;
1809 s/revision about/the revision of/g;
1810 2,35 s/info/revision/g;
1814 echo 'touch: Touch a file and svn add it.
1815 usage: touch FILE [FILES]...
1820 selfupdate | selfup | self-update | self-up
)
1821 echo 'selfupdate (selfup): Attempt to update svn-wrapper.sh
1828 echo 'version: Display the version info of svn and svn-wrapper.
1839 # svn_status [args...]
1846 svn_status_out
=`$SVN status "$@"`
1848 test x
"$svn_status_out" = x
&& return $svn_status_rv
1849 echo "$svn_status_out" |
sed "$sed_svn_st_color"
1850 return $svn_status_rv
1853 # svn_update [args...]
1856 svn_update_out
=`$SVN update "$@"`
1858 echo "$svn_update_out" |
sed "$sed_svn_up_colors"
1859 return $svn_update_rv
1862 # ------------------- #
1863 # `main' starts here. #
1864 # ------------------- #
1866 # Define colors if stdout is a tty.
1869 else # stdout isn't a tty => don't print colors.
1873 # Consider this as a sed function :P.
1877 s@^?\\(......\\)+@+\\1+@
1878 s@^?\\(......\\)\\(.*/\\)+@+\\1\\2+@
1879 s@^?\\(......\\),@,\\1,@
1880 s@^?\\(......\\)\\(.*/\\),@,\\1\\2,@
1881 s/^\\(.\\)C/\\1${lred}C${std}/
1884 s/^?/${lred}?${std}/; t
1885 s/^M/${lgreen}M${std}/; t
1886 s/^A/${lgreen}A${std}/; t
1887 s/^X/${lblue}X${std}/; t
1888 s/^+/${lyellow}+${std}/; t
1889 s/^D/${lyellow}D${std}/; t
1890 s/^,/${lred},${std}/; t
1891 s/^C/${lred}C${std}/; t
1892 s/^I/${purple}I${std}/; t
1893 s/^R/${lblue}R${std}/; t
1894 s/^!/${lred}!${std}/; t
1895 s/^~/${lwhite}~${std}/; t"
1904 s/^\\(.\\)C/\\1${lred}C${std}/
1905 s/^\\(.\\)U/\\1${lgreen}U${std}/
1906 s/^\\(.\\)D/\\1${lred}D${std}/
1909 s/^A/${lgreen}A${std}/; t
1910 s/^U/${lgreen}U${std}/; t
1911 s/^D/${lyellow}D${std}/; t
1912 s/^G/${purple}G${std}/; t
1913 s/^C/${lred}C${std}/; t"
1916 test "x$1" = x--debug
&& shift && set -x
1918 test "x$1" = x--git
&& shift && git_mode
=: && SVN
=git
1920 test "x$1" = x--no-changelog
&& shift && use_changelog
=false
1923 # ------------------------------- #
1924 # Hooks for standard SVN commands #
1925 # ------------------------------- #
1942 # -------------------- #
1943 # Custom SVN commands #
1944 # -------------------- #
1947 if [ -d .git
]; then
1950 require_diffstat
&& $SVN diff --no-diff-deleted "$@" | diffstat
1971 propadd | padd | pa
)
1977 svn_commit
--dry-run "$@"
1989 touch "$@" && $SVN add
"$@"
1991 selfupdate | selfup | self-update | self-up
)
1995 version |
-version |
--version)
1997 set dummy
'--version' "$@"