1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
#!/usr/bin/env zsh
# this is a git merge driver that magically fixes conflicts in the ChangeLog
# file (for simple additions at least). useful for back-porting
#
# to use, add this to your .git/config:
#
# [merge "zsh-merge-changelog"]
# name = merge the zsh changelog somewhat reasonably
# driver = Util/zsh-merge-changelog %O %A %B %L %P
#
# and add this to your .git/info/attributes:
#
# /ChangeLog merge=zsh-merge-changelog
#
# note: currently the ChangeLog is still updated even when the rest of the merge
# would be a no-op
#
# original script written by Mikachu
emulate -L zsh -o extended_glob -o warn_create_global
eputs() {
[[ $1 == (-|--) ]] && shift
print -rlu2 - "${ZSH_ARGZERO:t}: $1" "${@[2,-1]}"
}
main() {
local tmp=${TMPDIR:-/tmp}/${ZSH_ARGZERO:t}.$$.$RANDOM
local diff line ctx_after header data datb idx color
local -a out orig
diff=$( git diff --no-index -U200 -- $1 $3 )
for line in ${${(f)diff}[5,-1]}; do
case $line in
@@*) ;;
' '*)
(( $#out )) && ctx_after=1
# commit header from context (entry added under existing header)
(( ! $#out )) && [[ $line == ' '<->-<->-<->* ]] && header=$line[2,-1]
;;
+*)
(( ctx_after )) && {
eputs 'unable to handle non-consecutive addition'
return 1
}
[[ -n $header ]] && {
out+=( $header '' )
header=
}
out+=( "$line[2,-1]" )
;;
-*)
eputs 'unable to handle deletion'
return 1
;;
*)
eputs 'unexpected line in diff:' $line
return 1
;;
esac
done
(( $#out )) || {
eputs 'no changed lines found'
return 1
}
[[ $out[1] == <->-<->-<->* && $out[2] == '' ]] || {
eputs 'commit header not found'
return 1
}
trap "rm -f -- ${(q+)tmp}" EXIT HUP INT
orig=( "${(@f)"$( < $2 )"}" )
data=${(M)orig[1]##<->-<->-<->}
[[ -n $data ]] || {
eputs 'failed to get latest date in original change log'
return 1
}
datb=${(M)out[1]##<->-<->-<->}
# new entry later than last -- just prepend
if [[ $datb > $data ]]; then
print -rl - "${(@)out}" "${(@)orig}" > $tmp || return
# header already present in file -- re-use it. this always inserts above the
# existing entries, so it might not be the same order it was in originally.
# but it's fine
elif [[ -n ${(M)orig:#$out[1]} ]]; then
idx=$orig[(ie)$out[1]]
print -rl - \
"${(@)orig[1,idx]}" \
"${(@)out[2,-1]}" \
"${(@)orig[idx+2,-1]}" \
> $tmp || return
# insert in the middle somewhere
else
for (( idx = 1; idx <= $#orig; idx++ )); do
data=${(M)orig[idx]##<->-<->-<->}
[[ -n $data && $datb > $data ]] || continue
print -rl - \
"${(@)orig[1,idx-1]}" \
"${(@)out}" \
"${(@)orig[idx,-1]}" \
> $tmp || return
break
done
fi
[[ -t 2 ]] && color=--color=always
out=( "${(@f)"$( git diff --no-index $color -U4 -- $2 $tmp )"}" )
out=( "${(@)out[3,-1]}" )
out[1]=${out[1]/a\/[^[:cntrl:]]##/a/$5}
out[2]=${out[2]/b\/[^[:cntrl:]]##/b/$5}
eputs 'fixed ChangeLog entry:' "${(F)out}"
[[ -n $ZMC_FAIL ]] && return 1 # for testing
cp -- $tmp $2
}
main "$@"
|