我喜欢这个谜题,它有它的微妙之处。从这个文件中获取源代码,输入init foo.rb 1000,1005
并按照说明操作。完成后,文件@changes
将按拓扑顺序列出正确的提交列表,@blames
将显示每个提交的实际责任输出。
这比上面接受的解决方案要复杂得多。它产生的输出有时会更有用,难以复制,并且编码很有趣。
尝试在向后跟踪历史记录时自动跟踪行号范围的问题在于,如果更改块跨越了行号范围边界,您无法自动确定新范围边界在该块的哪个位置,您将不得不包含大范围的大添加,因此积累(有时是很多)不相关的更改,或者进入手动模式以确保它是正确的(当然会让你回到这里),或者在某些时候接受极端的损失。
如果您希望输出精确,请使用上面的答案和可信的正则表达式范围,例如`/^type function(/,/^}/'`,或者使用这个,它实际上并不那么糟糕,每次向后一步需要几秒钟。
虽然增加了一些复杂性,但它可以按照拓扑顺序生成命中列表,并且至少(相当成功地)尝试在每个步骤中缓解痛苦。例如,它从不运行冗余的 blame 命令,而 update-ranges 可以使调整行号更加容易。当然,还有一个可靠性,即必须逐个查看 hunks... :-P
要在完全自动模式下运行此操作,请输入 { init foo.rb /^class foo/,/^end/; auto; } 2>&-
init () {
file=$1;
range="$2"
rm -f @changes
git rev-list --topo-order HEAD -- "$file" \
| tee @checklist \
| cat -n | sort -k2 > @sequence
git blame "-ln${range:+L$range}" -- "$file" > @latest || echo >@checklist
check-cycle
cp @latest @blames
}
update-latest-checklist() {
latest=$(
sed s,^^,, @latest \
| sort -uk1,1 \
| join -1 2 -o1.1,1.2 @sequence - \
| sort -unk1,1 \
| sed 1q \
| cut -d" " -f2
)
sed -i 1,/^$latest/d @checklist
}
shhh () { shhh=1; }
check-cycle () {
update-latest-checklist
sed -n q1 @checklist || git log $latest~..$latest --format=%H\ %s | tee -a @changes
next=`sed 1q @checklist`
git cat-file -p `git rev-parse $next:"$file"` > @next
test -z "$shh$shhh$shhhh" && {
echo "A blame from the (next-)most recent alteration (id `git rev-parse --short $latest`) to '$file'"
echo is in file @latest, save its contents where you like
echo
echo you will need to look in file @next to determine the correct next range,
echo and say '`cycle its-start-line,its-end-line`' to continue
echo the "update-ranges" function starts you out with the range selected
} >&2
ncommits=`wc -l @checklist | cut -d\ -f1`
echo $ncommits commits remain in the checklist >&2
return $((ncommits==0))
}
update-ranges () {
start="${range%,*}"
end="${range#*,}"
case "$start" in
*/*) startcmd="1G$start"$'\n' ;;
*) startcmd="${start}G" ;;
esac
case "$end" in
*/*) endcmd="$end"$'\n' ;;
[0-9]*) endcmd="${end}G" ;;
+[0-9]*) endcmd="${end}j" ;;
*) endcmd="echohl Search|echo "can\'t" get to '${end}'\"|echohl None" ;;
esac
vim -c 'set buftype=nofile|let @m=":|q'$'\n"' -c "norm!${startcmd}V${endcmd}z.o" @next
}
cycle () {
sed -n q1 @checklist && { echo "No more commits to check"; return 1; }
range="${1:-$range}"
git blame "-ln${range:+L$range}" $next -- "$file" >@latest || echo >@checklist
echo >>@blames
cat @latest >>@blames
check-cycle
}
auto () {
while cycle; do true; done
}
prettyblames () {
cat >@pretty <<-\EOD
BEGIN {
RS=""
colors[0]="\033[0;30m"
colors[1]="\033[0;34m"
colors[2]="\033[0;32m"
colors[3]="\033[0;33m"
getline commits < "@changes"
split(commits,commit,/\n/)
}
NR!=1 { print "" }
{
thiscommit=gensub(/ .*/,"",1,commit[NR])
printf "%s\n","\033[0;31m"commit[NR]"\033[0m"
split($0,line,/\n/)
for ( n=1; n<=length(line); ++n ) {
color=0
split(line[n],key,/[1-9][0-9]*)/)
if ( NR!=1 && !seen[key[1]] ) color+=1
seen[key[1]]=1;
linecommit = gensub(/ .*/,"",1,line[n])
if (linecommit==thiscommit) color+=2
printf "%s%s\033[0m\n",colors[color],line[n]
}
}
EOD
awk -f @pretty @blames | less -R
}
12345
的第 15 至 20 行,那么这些行的代码在提交12345^
中可能会在第 55 至 60 行。 - asm