Merge pull request #355 from markeissler/better-changelog-fixes

Fix git-changelog for compatibility for bash<4. Fixes #337, #338
This commit is contained in:
hemanth.hm 2015-04-25 20:40:19 +05:30
commit b84faa58b6

View file

@ -40,6 +40,102 @@ _error() {
echo
}
# _setValueForKeyFakeAssocArray()
# /*!
# @abstract Set value for key from a fake associative array
# @discussion
# Iterates over target_ary (an indexed array), searching for target_key, if the
# key is found its value is set to new_value otherwise the target_key and
# new_value are appended to the array.
#
# The indexed array values must conform to this format:
# "key:value"
# Where key and value are separated by a single colon character.
#
# Specify empty values as an empty, quoted string.
#
# So-called "fake" associative arrays are useful for environments where the
# installed version of bash(1) precedes 4.0.
# @param target_key Key to retrieve
# @param new_value New or updated value
# @param target_ary Indexed array to scan
# @return Returns new array with updated key (status 0) or an empty array
# (status 1) on failure.
# */
_setValueForKeyFakeAssocArray() {
# parameter list supports empty arguments!
local target_key="$1"; shift
local new_value="$1"; shift
local target_ary=()
local defaultIFS="$IFS"
local IFS="$defaultIFS"
local found=false
IFS=$' ' target_ary=( $1 ) IFS="$defaultIFS"
[[ -z "${target_key}" || "${#target_ary[@]}" -eq 0 ]] && echo "${value}" && return 1
local _target_ary_length="${#target_ary[@]}"
local i
for (( i=0; i<"${_target_ary_length}"; i++ )); do
local __val="${target_ary[$i]}"
if [[ "${__val%%:*}" == "${target_key}" ]]; then
target_ary[$i]="${__val%%:*}:${new_value}"
found=true
break
fi
unset __val
done
unset i _target_ary_length
# key not found, append
[[ "$found" == false ]] && target_ary+=( "${target_key}:${new_value}" )
printf "%s" "${target_ary[*]}"
}
# _valueForKeyFakeAssocArray()
# /*!
# @abstract Fetch value for key from a fake associative array
# @discussion
# Iterates over target_ary (an indexed array), searching for target_key, if the
# key is found its value is returned.
#
# The indexed array values must conform to this format:
# "key:value"
# Where key and value are separated by a single colon character.
#
# So-called "fake" associative arrays are useful for environments where the
# installed version of bash(1) precedes 4.0.
# @param target_key Key to retrieve
# @param target_ary Indexed array to scan
# @return Returns string containing value (status 0) or an empty string
# (status 1) on failure.
# */
_valueForKeyFakeAssocArray() {
local target_key="$1"
local target_ary=()
local defaultIFS="$IFS"
local IFS="$defaultIFS"
local value=""
IFS=$' ' target_ary=( $2 ) IFS="$defaultIFS"
[[ -z "${target_key}" || "${#target_ary[@]}" -eq 0 ]] && echo "${value}" && return 1
local t
for t in "${target_ary[@]}"; do
if [[ "${t%%:*}" == "${target_key}" ]]; then
value="${t#*:}"
break
fi
done
unset t
echo -e "${value}"; return 0
}
_fetchCommitRange() {
local list_all="${1:-false}"
@ -91,7 +187,7 @@ commitList() {
local list_style="${1:-false}" # enable/disable list format
local changelog="$FILE"
local title_date="$(date +'%Y-%m-%d')"
local -A tags_list=()
local tags_list=()
local tags_list_keys=()
local defaultIFS="$IFS"
local IFS="$defaultIFS"
@ -99,17 +195,17 @@ commitList() {
#
# Tags look like this:
#
# >git log --tags --simplify-by-decoration --date="short" --pretty="format:%h$%x09%ad$%x09%D"
# >git log --tags --simplify-by-decoration --date="short" --pretty="format:%h$%x09%ad$%x09%d"
#
# ecf1f2b$ 2015-03-15$ HEAD, tag: v1.0.1, origin/master, origin/HEAD, master, hotfix/1.0.2
# a473e9c$ 2015-03-04$ tag: v1.0.0
# f2cb562$ 2015-02-19$ tag: v0.9.2
# 6197c2b$ 2015-02-19$ tag: v0.9.1
# 1e5f5e6$ 2015-02-16$ tag: v0.9.0
# 3de8ab5$ 2015-02-11$ origin/feature/restore-auto
# a15afd1$ 2015-02-02$ origin/feature/versionable
# 38a44e0$ 2015-02-02$ origin/feature/save-auto
# 3244b80$ 2015-01-16$ origin/feature/silent-history, upstream
# ecf1f2b$ 2015-03-15$ (HEAD, tag: v1.0.1, origin/master, origin/HEAD, master, hotfix/1.0.2)
# a473e9c$ 2015-03-04$ (tag: v1.0.0)
# f2cb562$ 2015-02-19$ (tag: v0.9.2)
# 6197c2b$ 2015-02-19$ (tag: v0.9.1)
# 1e5f5e6$ 2015-02-16$ (tag: v0.9.0)
# 3de8ab5$ 2015-02-11$ (origin/feature/restore-auto)
# a15afd1$ 2015-02-02$ (origin/feature/versionable)
# 38a44e0$ 2015-02-02$ (origin/feature/save-auto)
# 3244b80$ 2015-01-16$ (origin/feature/silent-history, upstream)
# 85e45f8$ 2014-08-25$
#
# The most-recent tag will be preceded by "HEAD, " if there have been zero
@ -121,6 +217,9 @@ commitList() {
local _tag_regex='^[[:alnum:][:blank:][:punct:]]+/.*'
while IFS=$'\t' read _ref _date _tag; do
[[ -z "${_tag}" ]] && continue
# strip out tags form ()
# git v2.2.0+ supports '%D', like '%d' without the " (", ")" wrapping. One day we should use it instead.
_tag="${_tag:2:-1}"
# trap tag if it points to last commit (HEAD)
_tag="${_tag##HEAD, }"
# strip out any additional tags pointing to same commit, remove tag label
@ -128,9 +227,9 @@ commitList() {
# strip out tags that are actually feature branches (git-flow support)
[[ "${_tag}" =~ ${_tag_regex} ]] && continue
# add tag to assoc array; copy tag to tag_list_keys for ordered iteration
tags_list["${_tag}"]="${_ref}=>${_date}"
tags_list+=( "${_tag}:${_ref}=>${_date}" )
tags_list_keys+=( "${_tag}" )
done <<< "$(git log --tags --simplify-by-decoration --date="short" --pretty="format:%h${_tab}%ad${_tab}%D")"
done <<< "$(git log --tags --simplify-by-decoration --date="short" --pretty="format:%h${_tab}%ad${_tab}%d")"
IFS="$defaultIFS"
unset _tag_regex
unset _ref _date _tag _tab
@ -142,7 +241,8 @@ commitList() {
for (( i=0; i<"${_tags_list_keys_length}"; i++ )); do
local __curr_tag="${tags_list_keys[$i]}"
local __prev_tag="${tags_list_keys[$i+1]:-null}"
local __curr_date="${tags_list[${__curr_tag}]##*=>}"
local __curr_date="$(_valueForKeyFakeAssocArray "${__curr_tag}" "${tags_list[*]}")"
__curr_date="${__curr_date##*=>}"
# output latest commits, up until the most-recent tag, these are all
# new commits made since the last tagged commit.
@ -176,10 +276,14 @@ commitList() {
else
_formatCommitPretty "${__curr_tag}" "${__curr_date}" "${__prev_tag}" "${__curr_tag}"
fi
unset __curr_date
unset __prev_tag
unset __curr_tag
done
unset i
unset _start_tag_found
unset _final_tag_found
unset _tags_list_keys_length
return
}
@ -206,15 +310,16 @@ main() {
local start_tag="null" # empty string and "null" mean two different things!
local final_tag="null"
local -A option=()
option["list_all"]=false
option["list_style"]=false
option["title_tag"]="$DEF_TAG_RECENT"
option["start_tag"]=""
option["final_tag"]=""
option["output_file"]=""
option["use_stdout"]=false
option["prune_old"]=false
local option=(
"list_all:false"
"list_style:false"
"title_tag:$DEF_TAG_RECENT"
"start_tag:"
"final_tag:"
"output_file:"
"use_stdout:false"
"prune_old:false"
)
#
# We work chronologically backwards from NOW towards start_tag where NOW also
@ -229,31 +334,31 @@ main() {
while [ "$1" != "" ]; do
case $1 in
-a | --all )
option["list_all"]=true
option=( $(_setValueForKeyFakeAssocArray "list_all" true "${option[*]}") )
;;
-l | --list )
option["list_style"]=true
option=( $(_setValueForKeyFakeAssocArray "list_style" true "${option[*]}") )
;;
-t | --tag )
option["title_tag"]="$2"
option=( $(_setValueForKeyFakeAssocArray "title_tag" "$2" "${option[*]}") )
shift
;;
-f | --final-tag )
option["final_tag"]="$2"
option=( $(_setValueForKeyFakeAssocArray "final_tag" "$2" "${option[*]}") )
shift
;;
-s | --start-tag )
option["start_tag"]="$2"
option=( $(_setValueForKeyFakeAssocArray "start_tag" "$2" "${option[*]}") )
shift
;;
-n | --no-merges )
GIT_LOG_OPTS='--no-merges'
;;
-p | --prune-old )
option["prune_old"]=true
option=( $(_setValueForKeyFakeAssocArray "prune_old" true "${option[*]}") )
;;
-x | --stdout )
option["use_stdout"]=true
option=( $(_setValueForKeyFakeAssocArray "use_stdout" true "${option[*]}") )
;;
-h | ? | help | --help )
_usage
@ -261,43 +366,47 @@ main() {
;;
* )
[[ "${1:0:1}" == '-' ]] && _error "Invalid option: $1" && _usage && exit 1
option["output_file"]="$1"
option=( $(_setValueForKeyFakeAssocArray "output_file" "$1" "${option[*]}") )
;;
esac
shift
done
if [[ -n "${option["start_tag"]}" ]]; then
start_tag="$(git describe --tags --abbrev=0 "${option["start_tag"]}" 2>/dev/null)"
local _tag="$(_valueForKeyFakeAssocArray "start_tag" "${option[*]}")"
if [[ -n "${_tag}" ]]; then
start_tag="$(git describe --tags --abbrev=0 "${_tag}" 2>/dev/null)"
if [[ -z "$start_tag" ]]; then
_error "Specified start-tag does not exist!"
return 1
fi
fi
unset _tag
if [[ -n "${option["final_tag"]}" ]]; then
final_tag="$(git describe --tags --abbrev=0 "${option["final_tag"]}" 2>/dev/null)"
local _tag="$(_valueForKeyFakeAssocArray "final_tag" "${option[*]}")"
if [[ -n "${_tag}" ]]; then
final_tag="$(git describe --tags --abbrev=0 "${_tag}" 2>/dev/null)"
if [[ -z "$final_tag" ]]; then
_error "Specified final-tag does not exist!"
return 1
fi
fi
unset _tag
#
# generate changelog
#
local tmpfile="$(git_extra_mktemp)"
local changelog="${option["output_file"]}"
local title_tag="${option["title_tag"]}"
local changelog="$(_valueForKeyFakeAssocArray "output_file" "${option[*]}")"
local title_tag="$(_valueForKeyFakeAssocArray "title_tag" "${option[*]}")"
if [[ "${option["list_style"]}" == true ]]; then
if [[ "${option["list_all"]}" == true ]]; then
if [[ "$(_valueForKeyFakeAssocArray "list_style" "${option[*]}")" == true ]]; then
if [[ "$(_valueForKeyFakeAssocArray "list_all" "${option[*]}")" == true ]]; then
commitListPlain "true" >> "$tmpfile"
else
commitListPlain "false" "$start_tag" "$final_tag" >> "$tmpfile"
fi
else
if [[ "${option["list_all"]}" == true ]]; then
if [[ "$(_valueForKeyFakeAssocArray "list_all" "${option[*]}")" == true ]]; then
commitListPretty "true" "$title_tag" >> "$tmpfile"
else
commitListPretty "false" "$title_tag" "$start_tag" "$final_tag" >> "$tmpfile"
@ -312,12 +421,13 @@ main() {
fi
# append existing changelog?
if [[ -f "$changelog" && "${option["prune_old"]}" == false ]]; then
if [[ -f "$changelog" \
&& "$(_valueForKeyFakeAssocArray "prune_old" "${option[*]}")" == false ]]; then
cat "$changelog" >> "$tmpfile"
fi
# output file to stdout or move into place
if [[ "${option["use_stdout"]}" == true ]]; then
if [[ "$(_valueForKeyFakeAssocArray "use_stdout" "${option[*]}")" == true ]]; then
cat "$tmpfile"
rm -f "$tmpfile"
else