@@ -3,22 +3,26 @@ set -euo pipefail
33
44usage () {
55 cat << EOF
6- Usage: $0 [-h] <file> [file ...]
6+ Usage: $0 [-h] <file>[@<lines>] [file ...]
77
88List commits that changed the given file(s) in chronological order,
99annotated with the release in which each commit landed on master.
1010
1111A commit's release is the oldest chart/X.Y.0 tag on master whose
1212tagged commit is not older than the commit itself.
1313
14+ Line ranges limit output to commits that changed those lines.
15+ Syntax: file.hs@1,15-18 (single lines and ranges, comma-separated)
16+ If no line ranges are given, all commits for the file are shown.
17+
1418Options:
1519 -h Show this help
1620EOF
1721 exit 0
1822}
1923
2024if [ $# -eq 0 ]; then
21- echo " Usage: $0 [-h] <file> [file ...]" >&2
25+ echo " Usage: $0 [-h] <file>[@<lines>] [file ...]" >&2
2226 exit 1
2327fi
2428
@@ -32,32 +36,63 @@ trap 'rm -f "$releases_tmp"' EXIT
3236while read -r tag; do
3337 commit=$( git rev-list -1 " $tag " )
3438 ts=$( git log -1 --format=" %ct" " $commit " )
35- echo " ${ts} ${tag} "
39+ date=$( git log -1 --format=" %ai" " $commit " )
40+ echo " ${ts} ${tag} ${date} "
3641done < <( git tag --merged master ' chart/*.0' | sort -V) > " $releases_tmp "
3742
38- for file in " $@ " ; do
39- if [ ! -f " $file " ]; then
40- echo " Error: file not found: $file " >&2
41- exit 1
42- fi
43-
44- echo " === $file ==="
45-
46- git log --follow --format=" %H %ct %ai %s" -- " $file " | while read -r commit commit_ts rest; do
43+ annotate () {
44+ while read -r commit commit_ts rest; do
4745 release=" "
48- while read -r ts tag; do
46+ release_date=" "
47+ while read -r ts tag rdate; do
4948 if [ " $ts " -ge " $commit_ts " ]; then
5049 release=" $tag "
50+ release_date=" $rdate "
5151 break
5252 fi
5353 done < <( sort -n " $releases_tmp " )
54-
5554 if [ -n " $release " ]; then
56- echo " $commit $rest [$release ]"
55+ echo " $commit $rest [$release $release_date ]"
5756 else
5857 echo " $commit $rest [unreleased]"
5958 fi
6059 done
60+ }
61+
62+ for arg in " $@ " ; do
63+ file=" ${arg%@* } "
64+ lines=" ${arg##*@ } "
65+ if [ " $file " = " $arg " ]; then
66+ lines=" "
67+ fi
68+
69+ if [ ! -f " $file " ]; then
70+ echo " Error: file not found: $file " >&2
71+ exit 1
72+ fi
73+
74+ echo " === $arg ==="
75+
76+ if [ -z " $lines " ]; then
77+ git log --follow --format=" %H %ct %ai %s" -- " $file " | annotate
78+ else
79+ l_args=()
80+ IFS=' ,' read -ra ranges <<< " $lines"
81+ for r in " ${ranges[@]} " ; do
82+ if [[ " $r " == * " -" * ]]; then
83+ start=" ${r% -* } "
84+ end=" ${r#* -} "
85+ else
86+ start=" $r "
87+ end=" $r "
88+ fi
89+ l_args+=(" -L" " ${start} ,${end} :${file} " )
90+ done
91+ git log " ${l_args[@]} " --format=" %H %ct %ai %s" 2> /dev/null \
92+ | grep -E ' ^[0-9a-f]{40} [0-9]{10}' \
93+ | awk ' !seen[$1]++' \
94+ | annotate
95+ fi
6196
6297 echo
6398done
0 commit comments