Release 1.4.7:
[m4.git] / commit
blobb4cda25699e95599b36452de9b1df08142d3a247
1 #! /bin/sh
3 # clcommit (GNU cvs-utils) version 0.11
4 # Written by Gary V. Vaughan <gary@gnu.org>
5 # and Alexandre Oliva <aoliva@redhat.com>
7 # Copyright (C) 1999, 2000, 2004, 2006 Free Software Foundation, Inc.
8 # This is free software; see the source for copying conditions. There is NO
9 # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful, but
17 # WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 # General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program; if not, a copy can be downloaded from
23 # http://www.gnu.org/copyleft/gpl.html, or by writing to the Free
24 # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # MA 02110-1301, USA.
27 # Usage: $progname [-v] [-h] [-f] [-l] [-n] [-q] [-z N] [-C ChangeLog_file]
28 # [-m msg|-F msg_file|-1] [-s addr [--from addr]] [--] [file|dir ...]
30 # -C file --changelog=file extract commit message from specified ChangeLog
31 # -zN --compress=N set compression level (0-9, 0=none, 9=max)
32 # -n --dry-run don't commit anything
33 # --fast same as --force --first
34 # -F file --file=file read commit message from file
35 # -1 --first extract first entry from ChangeLog, no cvs diff
36 # -f --force don't check (unless *followed* by -n), and just
37 # display commit message instead of running $PAGER
38 # --from=addr override default from address in commit email
39 # -l --local don't descend into subdirectories
40 # -m msg --message=msg set commit message
41 # --msg=msg same as -m
42 # -q --quiet run cvs in quiet mode
43 # -s addr --sendmail=addr send a commit email of the differences to ADDR
44 # --signature[=file] add FILE to the end of the email (~/.signature)
45 # -v --version print version information
46 # -h,-? --help print short or long help message
48 # This script eases checking in changes to CVS-maintained projects
49 # with ChangeLog files. It will check that there have been no
50 # conflicting commits in the CVS repository and print which files it
51 # is going to commit to stderr. A list of files to compare and to
52 # check in can be given in the command line. If it is not given, all
53 # files in the current directory (and below, unless `-l' is given) are
54 # considered for check in.
56 # The commit message will be extracted from the differences between a
57 # file named ChangeLog* in the commit list, or named after -C, and the
58 # one in the repository (unless a message was specified with `-m' or
59 # `-F'). An empty message is not accepted (but a blank line is). If
60 # the message is acceptable, it will be presented for verification
61 # (and possible edition) using the $PAGER environment variable (or
62 # `more', if it is not set, or `cat', if the `-f' switch is given).
63 # If $PAGER exits successfully, the modified files (at that moment)
64 # are checked in, unless `-n' was specified, in which case nothing is
65 # checked in.
67 # Report bugs to <gary@gnu.org>
69 : ${CVS="cvs"}
70 : ${MAILNOTIFY="./ltdl/config/mailnotify"}
71 : ${MKSTAMP="./ltdl/config/mkstamp"}
72 : ${MV="mv -f"}
73 : ${RM="rm -f"}
74 : ${SED="sed"}
76 SHELL="/bin/sh"
77 dirname="s,/[^/]*$,,"
78 basename="s,^.*/,,g"
80 # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
81 # is ksh but when the shell is invoked as "sh" and the current value of
82 # the _XPG environment variable is not equal to 1 (one), the special
83 # positional parameter $0, within a function call, is the name of the
84 # function.
85 progpath="$0"
87 # The name of this program:
88 progname=`echo "$progpath" | $SED "$basename"`
89 PROGRAM=clcommit
91 # Global variables:
92 EXIT_SUCCESS=0
93 EXIT_FAILURE=1
95 cvs_flags=
96 update_flags=
97 commit_flags=
98 opt_commit=:
99 opt_update=:
100 opt_first=false
102 sendmail_to=
103 sendmail_from=
104 exit_cmd=:
106 # Locations for important files
107 signature_file=
108 log_file="${TMPDIR-/tmp}/commitlog.$$"
110 $RM "${log_file}*"
111 trap '$RM "${log_file}*"; exit $EXIT_FAILURE' 1 2 15
113 set -e
116 # func_echo arg...
117 # Echo program name prefixed message.
118 func_echo ()
120 echo $progname: ${1+"$@"}
123 # func_error arg...
124 # Echo program name prefixed message to standard error.
125 func_error ()
127 echo $progname: ${1+"$@"} 1>&2
130 # func_fatal_error arg...
131 # Echo program name prefixed message to standard error, and exit.
132 func_fatal_error ()
134 func_error ${1+"$@"}
135 exit $EXIT_FAILURE
138 # func_verbose arg...
139 # Echo program name prefixed message in verbose mode only.
140 func_verbose ()
142 $opt_verbose && func_error ${1+"$@"}
145 # func_fatal_help arg...
146 # Echo program name prefixed message to standard error, followed by
147 # a help hint, and exit.
148 func_fatal_help ()
150 func_error ${1+"$@"}
151 func_fatal_error "Try \`$progname --help' for more information."
154 # func_missing_arg argname
155 # Echo program name prefixed message to standard error and set global
156 # exit_cmd.
157 func_missing_arg ()
159 func_error "missing argument for $1"
160 exit_cmd=exit
163 # func_usage
164 # Echo short help message to standard output and exit.
165 func_usage ()
167 $SED '/^# Usage:/,/# -h/ {
168 s/^# //; s/^# *$//;
169 s/\$progname/'$progname'/;
171 }; d' < "$progpath"
172 echo
173 echo "run \`$progname --help | more' for full usage"
174 exit $EXIT_SUCCESS
177 # func_help
178 # Echo long help message to standard output and exit.
179 func_help ()
181 $SED '/^# Usage:/,/# Report bugs to/ {
182 s/^# //; s/^# *$//;
183 s/\$progname/'$progname'/;
185 }; d' < "$progpath"
186 exit $EXIT_SUCCESS
189 # func_version
190 # Echo version message to standard output and exit.
191 func_version ()
193 $SED '/^# '$PROGRAM' (GNU /,/# warranty; / {
194 s/^# //; s/^# *$//;
195 s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/;
197 }; d' < "$progpath"
198 exit $EXIT_SUCCESS
201 # Parse options once, thoroughly. This comes as soon as possible in
202 # the script to make things like `commit --version' happen quickly.
204 # sed scripts:
205 my_sed_single_opt='1s/^\(..\).*$/\1/;q'
206 my_sed_single_rest='1s/^..\(.*\)$/\1/;q'
207 my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q'
208 my_sed_long_arg='1s/^--[^=]*=//'
210 # this just eases exit handling
211 while test $# -gt 0; do
212 opt="$1"
213 shift
214 case $opt in
216 --fast) set -- --force --first ${1+"$@"} ;;
218 -f|--force) opt_update=false; PAGER=cat ;;
220 --from) test $# = 0 && func_missing_arg $opt && break
221 sendmail_from="$1"
222 shift
225 -l|--local) update_flags="$update_flags -l"
226 commit_flags="$commit_flags -l"
229 -m|--message|--msg)
230 test $# = 0 && func_missing_arg $opt && break
231 if $opt_first || test -f "$log_file"; then
232 func_error "you can have at most one of -m, -F and -1"
233 break
235 echo "$1" > "$log_file"
236 shift
239 -F|--file) test $# = 0 && func_missing_arg $opt && break
240 if $opt_first || test -f "$log_file"; then
241 func_error "you can have at most one of -m, -F and -1"
242 break
244 if cat < "$1" > "$log_file"; then :; else
245 break
247 shift
250 -1|--first) if test -f "$log_File"; then
251 func_error "you can have at most one of -m, -F and -1"
252 break
254 opt_first=:
257 -C|--[cC]hange[lL]og)
258 test $# = 0 && func_missing_arg $opt && break
259 if test -f "$1"; then :; else
260 func_error "ChangeLog file \`$1' does not exist"
261 break
263 ChangeLog="$1"
264 shift
267 -n|--dry-run) opt_commit=false; opt_update=: ;;
269 -q|--quiet) cvs_flags="$cvs_flags -q" ;;
271 -s|--sendmail) test $# = 0 && func_missing_arg $opt && break
272 sendmail_to="$1"
273 shift
276 --signature) test $# = 0 && func_missing_arg $opt && break
277 signature_file="$HOME/.signature"
278 case $1 in
279 -*) ;;
280 *) signature_file="$1"; shift ;;
281 esac
282 if test -f "$signature_file"; then :; else
283 func_error "\`$signature_file': file not found"
284 break
288 -z|--compress)
289 test $# = 0 && func_missing_arg $opt && break
290 case "$1" in
291 [0-9]) :;;
292 *) func_error "invalid argument for $opt"
293 break
295 esac
296 cvs_flags="$cvs_flags -z$1"
297 shift
300 # Separate optargs to long options:
301 --message=*|--msg=*|--from=*|--file=*|--[Cc]hange[Ll]og=*|--compress=*|--sendmail=*|--signature=*)
302 arg=`echo "$opt" | $SED "$my_sed_long_arg"`
303 opt=`echo "$opt" | $SED "$my_sed_long_opt"`
304 set -- "$opt" "$arg" ${1+"$@"}
307 # Separate optargs to short options:
308 -m*|-F*|-C*|-s*|-z*)
309 arg=`echo "$opt" |$SED "$my_sed_single_rest"`
310 opt=`echo "$opt" |$SED "$my_sed_single_opt"`
311 set -- "$opt" "$arg" ${1+"$@"}
314 # Separate non-argument short options:
315 -f*|-1*|-n*|-q*)
316 rest=`echo "$opt" |$SED "$my_sed_single_rest"`
317 opt=`echo "$opt" |$SED "$my_sed_single_opt"`
318 set -- "$opt" "-$rest" ${1+"$@"}
321 -\?|-h) func_usage ;;
322 --help) func_help ;;
323 --version) func_version ;;
324 --) break ;;
325 -*) func_fatal_help "unrecognized option \`$opt'" ;;
326 *) set -- "$opt" ${1+"$@"}; break ;;
327 esac
328 done
330 if test -z "$sendmail_to"; then
332 # can't have a from address without a destination address
333 test -n "$sendmail_from" &&
334 func_error "can't use --from without --sendmail." && exit_cmd=exit
336 # can't use a signature file without a destination address
337 test -n "$signature_file" &&
338 func_error "can't use --signature without --sendmail." && exit_cmd=exit
341 # Bail if the options were screwed
342 $exit_cmd $EXIT_FAILURE
346 $opt_update && {
347 func_error "$progname: checking for conflicts..."
348 if ( $CVS $cvs_flags -q -n update $update_flags ${1+"$@"} |
349 while read line; do
350 echo "$line"
351 echo "$line" >&3
352 done | grep '^C'
353 ) 3>&1 >/dev/null; then
354 func_fatal_error "some conflicts were found, aborting..."
358 if test -f "$log_file"; then :; else
359 if test -z "$ChangeLog"; then
360 for f in ${1+"$@"}; do
361 case "$f" in
362 ChangeLog* | */ChangeLog*)
363 if test -z "$ChangeLog"; then
364 ChangeLog="$f"
365 else
366 func_fatal_error "multiple ChangeLog files: $ChangeLog and $f"
369 esac
370 done
373 func_error "$progname: checking commit message..."
374 if $opt_first; then
375 skipping=:
376 sed 's,^,+,' < ${ChangeLog-ChangeLog} |
377 while read line; do
378 case "$line" in
379 "+") if $skipping; then skipping=false; else break; fi;;
380 "+ "*)
381 func_error "*** Warning: lines should start with tabs, not spaces; ignoring line:"
382 echo "$line" | sed 's/^.//' >&2;;
383 "+ "*)
384 $skipping || echo "$line" ;;
385 esac
386 done |
387 sed 's,^\+ ,,' > "$log_file" || exit $EXIT_FAILURE
388 else
389 $CVS $cvs_flags diff -u ${ChangeLog-ChangeLog} |
390 while read line; do
391 case $line in
392 "--- "*) :;;
393 "-"*)
394 func_error "*** Warning: the following line in ChangeLog diff is suspicious:"
395 echo "$line" | sed 's/^.//' >&2;;
396 "+ "*)
397 func_error "*** Warning: lines should start with tabs, not spaces; ignoring line:"
398 echo "$line" | sed 's/^.//' >&2;;
399 "+") echo ;;
400 "+ "*) echo "$line";;
401 esac
402 done |
403 sed -e 's,\+ ,,' -e '/./p' -e '/./d' -e '1d' -e '$d' > "$log_file" \
404 || exit $EXIT_FAILURE
406 # The sed script above removes "+TAB" from the beginning of a line, then
407 # deletes the first and/or the last line, when they happen to be empty
410 grep '[^ ]' < "$log_file" > /dev/null ||
411 func_fatal_error "empty commit message, aborting"
413 if grep '^$' < "$log_file" > /dev/null; then
414 func_error "*** Warning: blank lines should not appear within commit messages."
415 func_error "*** They should be used to separate distinct commits."
418 ${PAGER-more} "$log_file" || exit $EXIT_FAILURE
420 sleep 1 # give the user some time for a ^C
422 filelist=`cvs -nq up 2>/dev/null | grep '^[MAD] ' | sed 's/^. //'`
424 # Do not check for empty $log_file again, even though the user might have
425 # zeroed it out. If s/he did, it was probably intentional.
427 if $opt_commit; then
428 $CVS $cvs_flags commit $commit_flags -F $log_file ${1+"$@"} || exit $EXIT_FAILURE
431 # Send a copy of the log_file if sendmail_to was set:
432 if test -n "$sendmail_to"; then
433 notify_file="${log_file}.2"
434 func_error "Mailing commit notification to $sendmail_to"
435 test $# -gt 0 && filelist="$@"
438 test -f CVS/Root &&
439 echo "CVSROOT: `sed -e 's,.*:,,g' CVS/Root`"
440 test -f $MKSTAMP &&
441 echo "TIMESTAMP: `$SHELL $MKSTAMP < ./ChangeLog`"
442 test -f CVS/Repository &&
443 echo "Module name: `cat CVS/Repository`"
444 test -f CVS/Tag &&
445 echo "Branch: `sed -e 's,^T,,;1q' CVS/Tag`"
446 test -f CVS/Root &&
447 echo "Changes by: `sed -e 's,:.*$,,g;s,^.*:,,' CVS/Root`"
448 echo ""
449 echo "Log Message:"
450 sed -e 's,^, ,' "$log_file"
451 test -f "$signature_file" && cat "$signature_file"
452 } > "$notify_file"
454 if test -n "$sendmail_from"; then
455 $SHELL $MAILNOTIFY -F "$sendmail_from" -s "`echo $filelist`" -f "$notify_file" -m "text/plain" "$sendmail_to"
456 else
457 $SHELL $MAILNOTIFY -s "`echo $filelist`" -f "$notify_file" -m "text/plain" "$sendmail_to"
461 $RM "${log_file}*"
463 exit $EXIT_SUCCESS
465 # Local Variables:
466 # mode:shell-script
467 # sh-indentation:2
468 # End: