mirror of
https://github.com/tj/git-extras.git
synced 2026-09-10 07:26:17 -04:00
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:
commit
b84faa58b6
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue