start work on HACKING updates
[m4/ericb.git] / commit
blobad2a3967aa6d116e0a3da2ab18a4fb51f87fa8cd
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, 2007 Free Software Foundation, Inc.
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
22 # Usage: $progname [-v] [-h] [-f] [-l] [-n] [-q] [-z N] [-C ChangeLog_file]
23 # [-m msg|-F msg_file|-1] [-s addr [--from addr]] [--] [file|dir ...]
25 # -C file --changelog=file extract commit message from specified ChangeLog
26 # -zN --compress=N set compression level (0-9, 0=none, 9=max)
27 # -n --dry-run don't commit anything
28 # --fast same as --force --first
29 # -F file --file=file read commit message from file
30 # -1 --first extract first entry from ChangeLog, no cvs diff
31 # -f --force don't check (unless *followed* by -n), and just
32 # display commit message instead of running $PAGER
33 # --from=addr override default from address in commit email
34 # -l --local don't descend into subdirectories
35 # -m msg --message=msg set commit message
36 # --msg=msg same as -m
37 # -q --quiet run cvs in quiet mode
38 # -s addr --sendmail=addr send a commit email of the differences to ADDR
39 # --signature[=file] add FILE to the end of the email (~/.signature)
40 # -v --version print version information
41 # -h,-? --help print short or long help message
43 # This script eases checking in changes to CVS-maintained projects
44 # with ChangeLog files. It will check that there have been no
45 # conflicting commits in the CVS repository and print which files it
46 # is going to commit to stderr. A list of files to compare and to
47 # check in can be given in the command line. If it is not given, all
48 # files in the current directory (and below, unless `-l' is given) are
49 # considered for check in.
51 # The commit message will be extracted from the differences between a
52 # file named ChangeLog* in the commit list, or named after -C, and the
53 # one in the repository (unless a message was specified with `-m' or
54 # `-F'). An empty message is not accepted (but a blank line is). If
55 # the message is acceptable, it will be presented for verification
56 # (and possible edition) using the $PAGER environment variable (or
57 # `more', if it is not set, or `cat', if the `-f' switch is given).
58 # If $PAGER exits successfully, the modified files (at that moment)
59 # are checked in, unless `-n' was specified, in which case nothing is
60 # checked in.
62 # Report bugs to <gary@gnu.org>
64 : ${CVS="cvs"}
65 : ${MAILNOTIFY="./ltdl/config/mailnotify"}
66 : ${MKSTAMP="./ltdl/config/mkstamp"}
67 : ${MV="mv -f"}
68 : ${RM="rm -f"}
69 : ${SED="sed"}
71 SHELL="/bin/sh"
72 dirname="s,/[^/]*$,,"
73 basename="s,^.*/,,g"
75 # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
76 # is ksh but when the shell is invoked as "sh" and the current value of
77 # the _XPG environment variable is not equal to 1 (one), the special
78 # positional parameter $0, within a function call, is the name of the
79 # function.
80 progpath="$0"
82 # The name of this program:
83 progname=`echo "$progpath" | $SED "$basename"`
84 PROGRAM=clcommit
86 # Global variables:
87 EXIT_SUCCESS=0
88 EXIT_FAILURE=1
90 cvs_flags=
91 update_flags=
92 commit_flags=
93 opt_commit=:
94 opt_update=:
95 opt_first=false
97 sendmail_to=
98 sendmail_from=
99 exit_cmd=:
101 # Locations for important files
102 signature_file=
103 log_file="${TMPDIR-/tmp}/commitlog.$$"
105 $RM "${log_file}*"
106 trap '$RM "${log_file}*"; exit $EXIT_FAILURE' 1 2 15
108 set -e
111 # func_echo arg...
112 # Echo program name prefixed message.
113 func_echo ()
115 echo $progname: ${1+"$@"}
118 # func_error arg...
119 # Echo program name prefixed message to standard error.
120 func_error ()
122 echo $progname: ${1+"$@"} 1>&2
125 # func_fatal_error arg...
126 # Echo program name prefixed message to standard error, and exit.
127 func_fatal_error ()
129 func_error ${1+"$@"}
130 exit $EXIT_FAILURE
133 # func_verbose arg...
134 # Echo program name prefixed message in verbose mode only.
135 func_verbose ()
137 $opt_verbose && func_error ${1+"$@"}
140 # func_fatal_help arg...
141 # Echo program name prefixed message to standard error, followed by
142 # a help hint, and exit.
143 func_fatal_help ()
145 func_error ${1+"$@"}
146 func_fatal_error "Try \`$progname --help' for more information."
149 # func_missing_arg argname
150 # Echo program name prefixed message to standard error and set global
151 # exit_cmd.
152 func_missing_arg ()
154 func_error "missing argument for $1"
155 exit_cmd=exit
158 # func_usage
159 # Echo short help message to standard output and exit.
160 func_usage ()
162 $SED '/^# Usage:/,/# -h/ {
163 s/^# //; s/^# *$//;
164 s/\$progname/'$progname'/;
166 }; d' < "$progpath"
167 echo
168 echo "run \`$progname --help | more' for full usage"
169 exit $EXIT_SUCCESS
172 # func_help
173 # Echo long help message to standard output and exit.
174 func_help ()
176 $SED '/^# Usage:/,/# Report bugs to/ {
177 s/^# //; s/^# *$//;
178 s/\$progname/'$progname'/;
180 }; d' < "$progpath"
181 exit $EXIT_SUCCESS
184 # func_version
185 # Echo version message to standard output and exit.
186 func_version ()
188 $SED '/^# '$PROGRAM' (GNU /,/# warranty; / {
189 s/^# //; s/^# *$//;
190 s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/;
192 }; d' < "$progpath"
193 exit $EXIT_SUCCESS
196 # Parse options once, thoroughly. This comes as soon as possible in
197 # the script to make things like `commit --version' happen quickly.
199 # sed scripts:
200 my_sed_single_opt='1s/^\(..\).*$/\1/;q'
201 my_sed_single_rest='1s/^..\(.*\)$/\1/;q'
202 my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q'
203 my_sed_long_arg='1s/^--[^=]*=//'
205 # this just eases exit handling
206 while test $# -gt 0; do
207 opt="$1"
208 shift
209 case $opt in
211 --fast) set -- --force --first ${1+"$@"} ;;
213 -f|--force) opt_update=false; PAGER=cat ;;
215 --from) test $# = 0 && func_missing_arg $opt && break
216 sendmail_from="$1"
217 shift
220 -l|--local) update_flags="$update_flags -l"
221 commit_flags="$commit_flags -l"
224 -m|--message|--msg)
225 test $# = 0 && func_missing_arg $opt && break
226 if $opt_first || test -f "$log_file"; then
227 func_error "you can have at most one of -m, -F and -1"
228 break
230 echo "$1" > "$log_file"
231 shift
234 -F|--file) test $# = 0 && func_missing_arg $opt && break
235 if $opt_first || test -f "$log_file"; then
236 func_error "you can have at most one of -m, -F and -1"
237 break
239 if cat < "$1" > "$log_file"; then :; else
240 break
242 shift
245 -1|--first) if test -f "$log_File"; then
246 func_error "you can have at most one of -m, -F and -1"
247 break
249 opt_first=:
252 -C|--[cC]hange[lL]og)
253 test $# = 0 && func_missing_arg $opt && break
254 if test -f "$1"; then :; else
255 func_error "ChangeLog file \`$1' does not exist"
256 break
258 ChangeLog="$1"
259 shift
262 -n|--dry-run) opt_commit=false; opt_update=: ;;
264 -q|--quiet) cvs_flags="$cvs_flags -q" ;;
266 -s|--sendmail) test $# = 0 && func_missing_arg $opt && break
267 sendmail_to="$1"
268 shift
271 --signature) test $# = 0 && func_missing_arg $opt && break
272 signature_file="$HOME/.signature"
273 case $1 in
274 -*) ;;
275 *) signature_file="$1"; shift ;;
276 esac
277 if test -f "$signature_file"; then :; else
278 func_error "\`$signature_file': file not found"
279 break
283 -z|--compress)
284 test $# = 0 && func_missing_arg $opt && break
285 case "$1" in
286 [0-9]) :;;
287 *) func_error "invalid argument for $opt"
288 break
290 esac
291 cvs_flags="$cvs_flags -z$1"
292 shift
295 # Separate optargs to long options:
296 --message=*|--msg=*|--from=*|--file=*|--[Cc]hange[Ll]og=*|--compress=*|--sendmail=*|--signature=*)
297 arg=`echo "$opt" | $SED "$my_sed_long_arg"`
298 opt=`echo "$opt" | $SED "$my_sed_long_opt"`
299 set -- "$opt" "$arg" ${1+"$@"}
302 # Separate optargs to short options:
303 -m*|-F*|-C*|-s*|-z*)
304 arg=`echo "$opt" |$SED "$my_sed_single_rest"`
305 opt=`echo "$opt" |$SED "$my_sed_single_opt"`
306 set -- "$opt" "$arg" ${1+"$@"}
309 # Separate non-argument short options:
310 -f*|-1*|-n*|-q*)
311 rest=`echo "$opt" |$SED "$my_sed_single_rest"`
312 opt=`echo "$opt" |$SED "$my_sed_single_opt"`
313 set -- "$opt" "-$rest" ${1+"$@"}
316 -\?|-h) func_usage ;;
317 --help) func_help ;;
318 --version) func_version ;;
319 --) break ;;
320 -*) func_fatal_help "unrecognized option \`$opt'" ;;
321 *) set -- "$opt" ${1+"$@"}; break ;;
322 esac
323 done
325 if test -z "$sendmail_to"; then
327 # can't have a from address without a destination address
328 test -n "$sendmail_from" &&
329 func_error "can't use --from without --sendmail." && exit_cmd=exit
331 # can't use a signature file without a destination address
332 test -n "$signature_file" &&
333 func_error "can't use --signature without --sendmail." && exit_cmd=exit
336 # Bail if the options were screwed
337 $exit_cmd $EXIT_FAILURE
341 $opt_update && {
342 func_error "$progname: checking for conflicts..."
343 if ( $CVS $cvs_flags -q -n update $update_flags ${1+"$@"} |
344 while read line; do
345 echo "$line"
346 echo "$line" >&3
347 done | grep '^C'
348 ) 3>&1 >/dev/null; then
349 func_fatal_error "some conflicts were found, aborting..."
353 if test -f "$log_file"; then :; else
354 if test -z "$ChangeLog"; then
355 for f in ${1+"$@"}; do
356 case "$f" in
357 ChangeLog* | */ChangeLog*)
358 if test -z "$ChangeLog"; then
359 ChangeLog="$f"
360 else
361 func_fatal_error "multiple ChangeLog files: $ChangeLog and $f"
364 esac
365 done
368 func_error "$progname: checking commit message..."
369 if $opt_first; then
370 skipping=:
371 sed 's,^,+,' < ${ChangeLog-ChangeLog} |
372 while read line; do
373 case "$line" in
374 "+") if $skipping; then skipping=false; else break; fi;;
375 "+ "*)
376 func_error "*** Warning: lines should start with tabs, not spaces; ignoring line:"
377 echo "$line" | sed 's/^.//' >&2;;
378 "+ "*)
379 $skipping || echo "$line" ;;
380 esac
381 done |
382 sed 's,^\+ ,,' > "$log_file" || exit $EXIT_FAILURE
383 else
384 $CVS $cvs_flags diff -u ${ChangeLog-ChangeLog} |
385 while read line; do
386 case $line in
387 "--- "*) :;;
388 "-"*)
389 func_error "*** Warning: the following line in ChangeLog diff is suspicious:"
390 echo "$line" | sed 's/^.//' >&2;;
391 "+ "*)
392 func_error "*** Warning: lines should start with tabs, not spaces; ignoring line:"
393 echo "$line" | sed 's/^.//' >&2;;
394 "+") echo ;;
395 "+ "*) echo "$line";;
396 esac
397 done |
398 sed -e 's,\+ ,,' -e '/./p' -e '/./d' -e '1d' -e '$d' > "$log_file" \
399 || exit $EXIT_FAILURE
401 # The sed script above removes "+TAB" from the beginning of a line, then
402 # deletes the first and/or the last line, when they happen to be empty
405 grep '[^ ]' < "$log_file" > /dev/null ||
406 func_fatal_error "empty commit message, aborting"
408 if grep '^$' < "$log_file" > /dev/null; then
409 func_error "*** Warning: blank lines should not appear within commit messages."
410 func_error "*** They should be used to separate distinct commits."
413 ${PAGER-more} "$log_file" || exit $EXIT_FAILURE
415 sleep 1 # give the user some time for a ^C
417 filelist=`cvs -nq up 2>/dev/null | grep '^[MAD] ' | sed 's/^. //'`
419 # Do not check for empty $log_file again, even though the user might have
420 # zeroed it out. If s/he did, it was probably intentional.
422 if $opt_commit; then
423 $CVS $cvs_flags commit $commit_flags -F $log_file ${1+"$@"} || exit $EXIT_FAILURE
426 # Send a copy of the log_file if sendmail_to was set:
427 if test -n "$sendmail_to"; then
428 notify_file="${log_file}.2"
429 func_error "Mailing commit notification to $sendmail_to"
430 test $# -gt 0 && filelist="$@"
433 test -f CVS/Root &&
434 echo "CVSROOT: `sed -e 's,.*:,,g' CVS/Root`"
435 test -f $MKSTAMP &&
436 echo "TIMESTAMP: `$SHELL $MKSTAMP < ./ChangeLog`"
437 test -f CVS/Repository &&
438 echo "Module name: `cat CVS/Repository`"
439 test -f CVS/Tag &&
440 echo "Branch: `sed -e 's,^T,,;1q' CVS/Tag`"
441 test -f CVS/Root &&
442 echo "Changes by: `sed -e 's,:.*$,,g;s,^.*:,,' CVS/Root`"
443 echo ""
444 echo "Log Message:"
445 sed -e 's,^, ,' "$log_file"
446 test -f "$signature_file" && cat "$signature_file"
447 } > "$notify_file"
449 if test -n "$sendmail_from"; then
450 $SHELL $MAILNOTIFY -F "$sendmail_from" -s "`echo $filelist`" -f "$notify_file" -m "text/plain" "$sendmail_to"
451 else
452 $SHELL $MAILNOTIFY -s "`echo $filelist`" -f "$notify_file" -m "text/plain" "$sendmail_to"
456 $RM "${log_file}*"
458 exit $EXIT_SUCCESS
460 # Local Variables:
461 # mode:shell-script
462 # sh-indentation:2
463 # End: