version/1.3
[gitchangelog.sh.git] / gitchangelog.sh
blob0db3359adf3f0791d0831a3333b0447d4941817a
1 #!/bin/sh
2 # Copyright © 2013,2014 Géraud Meyer <graud@gmx.com>
4 # This program is free software; you can redistribute it and/or modify it under
5 # the terms of the GNU General Public License version 2 as published by the
6 # Free Software Foundation.
8 # This program is distributed in the hope that it will be useful, but WITHOUT
9 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11 # details.
13 # You should have received a copy of the GNU General Public License along with
14 # this program. If not, see <http://www.gnu.org/licenses/>.
16 PROGRAM_NAME="gitchangelog.sh"
17 PROGRAM_VERSION="1.3"
18 help_usage () {
19 cat <<"HelpMessage"
20 Name
21 gitchangelog.sh - portable script generating a GNU-like changelog from a Git log
22 Usage
23 gitchangelog.sh [<options>] [--] [ {-|<outfile>} [<git_log_args>] ]
24 gitchangelog.sh [<options>] [--] {-|<outfile>} -
25 Options
26 --tags
27 Prepend changelog entries by tag names in brackets.
28 --tag-pattern <sed_BRE>
29 Tags to consider for marking entries. The pattern should not match
30 across spaces; the default is "[^ ]\{1,\}".
31 --merge
32 Prepend changelog entries by merge marks.
33 --title-only
34 Only keep the title of a commit. This implies --title-star, that can
35 be disabled by a supplemental --git-body.
36 --title-star
37 Prepend the title by a star as is done with the other parapgraphs.
38 --git-body
39 Do not reformat the commit message, only reindent it.
40 --no-blankline
41 Do not separate the title from the rest by a blank line.
42 --version
43 --help, --helpm
44 HelpMessage
46 help_message () {
47 help_usage
48 cat <<"HelpMessage"
49 Description
50 Each commit message is made into a changelog entry. Each paragraph of a
51 Git commit message body is made into a star item of the changelog. The
52 message title is kept on a separate line. Text lines are never broken nor
53 joined.
55 By default git is run to get the log, with the required options. If a dash
56 is given in place of additional <git_log_args>, then the log is read from
57 the standard input; in that case pass the option --date=short and possibly
58 also --decorate to git to get the appropriate log format.
59 Portability
60 The script is portable to POSIX sh and to other less comformant shells such
61 as pdksh.
63 The sed code is portable to POSIX sed but requires only Simple Regular
64 Expressions (SRE) instead of Basic Regular Expressions (BRE) which makes it
65 prone to work under Solaris and old BSDs. git-log is required if the log
66 is not read from stdin.
67 Examples
68 $ ./gitchangelog.sh --title-star --no-blankline GNUChangeLog
69 $ ./gitchangelog.sh --tags --tag-pattern 'release\/[^ ]*' -- - --date-order |
70 sed 's/^\[release/^L\[release/' > ChangeLog
71 $ git log --date=short --decorate --first-parent |
72 ./gitchangelog.sh --merge ChangeLog -
73 Author
74 gitchangelog.sh was written by G.raud Meyer.
75 See also
76 git-log(1)
77 HelpMessage
80 # parameters
81 GITBODY= NO_GITBODY=yes
82 BLANKLINE=yes
83 TAGS=
84 TAG_PATTERN='[^ ]\+'
85 MERGE=
86 TITLE= NO_TITLE=yes
87 TITLESTAR=
88 while [ $# -gt 0 ]
90 case $1 in
91 --tags)
92 TAGS=yes ;;
93 --tag-pattern)
94 shift; TAG_PATTERN=${1-} ;;
95 --merge)
96 MERGE=yes ;;
97 --title-only)
98 TITLE=yes NO_TITLE= TITLESTAR=yes ;;
99 --title-star)
100 TITLESTAR=yes ;;
101 --git-body)
102 GITBODY=yes NO_GITBODY= ;;
103 --no-blankline)
104 BLANKLINE= ;;
105 --version)
106 echo "$PROGRAM_NAME version $PROGRAM_VERSION"
107 exit ;;
108 --help)
109 help_usage; exit ;;
110 --helpm)
111 help_message; exit ;;
112 --?*)
113 echo "$0: unknown option $1" >&2
114 exit 255 ;;
116 shift; break ;;
118 break ;;
119 esac
120 shift
121 done
122 ANNOTATE=
123 [ -n "$TAGS" ] || [ -n "$MERGE" ] && ANNOTATE=yes
124 # git repository check
125 test x"${2-}" != x"-" &&
126 if test -d .git || git rev-parse --git-dir >/dev/null
127 then :
128 else
129 echo "$0: error not in a git repository" >&2
130 exit 255
132 # output file
133 if test $# -gt 0
134 then
135 test x"${1-}" != x"-" &&
136 exec >"$1"
137 shift
140 # input source
141 B='{' b='}'
142 s='\'
143 # Some shells, like the family of pdksh, behave differently regarding
144 # the backslash in variable substitutions inside a here document.
145 NL='
147 # pdksh and posh do not keep a newline preceded by a backslash inside a
148 # single quoted string inside two levels of command substitution (when the
149 # second level is double quoted).
150 exec 4>&1 # to pass stdout to the following process substitution
151 eval "$( exec 3>&1 1>&4 4>&- # eval will set $gitrc and $rc
152 if test x"${1-}" = x"-"
153 then cat; echo >&3 gitrc=$?
154 else git log --date=short ${ANNOTATE:+--decorate} ${1+"$@"}; echo >&3 gitrc=$?
155 fi |
156 # processing
157 LC_ALL=C sed -e "$(# Transform the GNU sed program into a more portable one
158 LC_ALL=C sed -e 's/\\s/[ \\t]/g
159 s/\\+/\\{1,\\}/g
160 s/; /\'"$NL"'/g
161 s/@n/\\\'"$NL"'/g
162 s/\\t/ /g
163 s/^ *#/#/' <<EOF
164 # Put the tags in the hold space
165 /^commit / {
166 # parse the refs
167 s/^commit [a-zA-Z0-9]\+//; s/^ \+(\(.*\))\$/, \1/
168 s/^/@n/; s/, /@n /g
169 # conditionnal branch to reset
170 ${TAGS:+t a;} s/.*//; t e
171 # extract the desired tags
172 :a; s/\n\$//; t e
173 s/\(.*\)\n tag: \($TAG_PATTERN\)\$/[\2]@n\1/; t a
174 s/\(.*\)\n [^ ]*\$/\1/; t a
175 s/\(.*\)\n [a-zA-Z]\+: *[^ ]*\$/\1/; t a
176 :e; s/\n/ /g; h; d
178 # Add the merge marker to the hold space
179 /^Merge: / { ${MERGE:+x; s/\$/${B}M$b /; x;} d; }
180 /^Author:/ {
181 # Process author, date, tag
182 ${ANNOTATE:+x; s/${s}s*\$/@n/; x; b s;} x; s/.*/@n/; x${ANNOTATE:+; :s}
183 N; G; s/^Author:\s*\(.*\) \s*\(<[^<>]*>\)\s*\nDate:\s*\(.*\)\n\(.*\n\)\$/\4\3 \1 \2@n/
184 s/^\n//
185 # Process title
186 n; N; s/^.*\n /\t${TITLESTAR:+${NO_GITBODY:+* }}/
187 t t; :t; n; s/^ \(.\+\)\$/\t${TITLESTAR:+${NO_GITBODY:+ }}\1/; t t
188 # If non empty body, print an extra line
189 ${NO_TITLE:+${NO_GITBODY:+${BLANKLINE:+/^\$/ !$B s/^ /${s}t/p; s/^${s}t/ /; $b}}}
191 ${TITLE:+/^ / d}
192 # First line of the paragraph
193 :b; /^ \$/ { N; s/^ \n//; s/^ \(.\)/${GITBODY:+${s}t@n}\t${NO_GITBODY:+* }\1/; b b; }
194 # Rest of the paragraph
195 s/^ /\t${NO_GITBODY:+ }/
196 # Reset the hold space for the next entry
197 /^\$/ h
199 )" # sed
200 echo >&3 rc=$?
201 )" # eval
202 exec 4>&-
204 # error check
205 test $gitrc -eq 0 ||
206 echo >&2 "$0: ERROR git or cat failed with #$gitrc"
207 test $rc -eq 0 ||
208 { echo >&2 "$0: ERROR sed failed with #$rc"; exit $rc; }
209 exit $gitrc