我喜歡這個謎題,它有它的微妙之處。源文件,說init foo.rb 1000,1005
並按照說明。完成之後,文件@changes
將按照拓撲順序具有正確的提交列表,並且@blames
將具有來自每個提交的實際責任輸出。
這比the accepted solution above複雜得多。它產生的輸出有時更有用,難以重現,而且編碼很有趣。
嘗試跟蹤歷史行數時自動跟蹤行號範圍的問題是,如果更改大塊穿過行號範圍的邊界,則無法自動確定新範圍邊界應位於該塊的位置,而您我們必須包括一個大範圍的大型增加項,並且積累(有時候很多)不相關的變化,或者進入手動模式以確保它是正確的(這當然會讓你回到這裏),或者有時會接受極度的損失。
如果你想要你的輸出是準確的,使用上面的答案與可信賴的正則表達式範圍,如`/^type function(/,/ ^} /',或者使用它,這實際上並不壞,一對夫婦每秒退後一步
爲了換取額外的複雜性,它在拓撲序列中產生了hitlist,它至少(相當成功地)試圖改善每一步的痛苦。 ,例如,更新範圍使得調整行號更容易,當然還有可能性不得不單獨對眼睛的視線進行調整:-P
要在全自動狀態下運行此功能,請說{ init foo.rb /^class foo/,/^end/; auto; } 2>&-
### functions here create random @-prefix files in the current directory ###
#
# git blame history for a range, finding every change to that range
# throughout the available history. It's somewhat, ahh, "intended for
# customization", is that enough of a warning? It works as advertised
# but drops @-prefix temporary files in your current directory and
# defines new commands
#
# Source this file in a subshell, it defines functions for your use.
# If you have @-prefix files you care about, change all @ in this file
# to something you don't have and source it again.
#
# init path/to/file [<start>,<end>] # range optional
# update-ranges # check range boundaries for the next step
# cycle [<start>,<end>] # range unchanged if not supplied
# prettyblame # pretty colors,
# blue="child commit doesn't have this line"
# green="parent commit doesn't have this line"
# brown=both
# shhh # silence the pre-cycle blurb
#
# For regex ranges, you can _usually_ source this file and say `init
# path/to/file /startpattern/,/endpattern/` and then cycle until it says 0
# commits remain in the checklist
#
# for line-number ranges, or regex ranges you think might be unworthy, you
# need to check and possibly update the range before each cycle. File
# @next is the next blame start-point revision text; and command
# update-ranges will bring up vim with the current range V-selected. If
# that looks good, `@M` is set up to quit even while selecting, so `@M` and
# cycle. If it doesn't look good, 'o' and the arrow keys will make getting
# good line numbers easy, or you can find better regex's. Either way, `@M`
# out and say `cycle <start>,<end>` to update the ranges.
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() {
# update $latest with the latest sha that actually touched our range,
# and delete that and everything later than that from the 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
}
您確定這是您想要的嗎?使用行號識別更改僅適用於文件的給定狀態。如果你需要第15-20行用於提交'12345',那麼這些行上的代碼可能在第55-60行提交'12345 ^'。 – asm
很確定。這就是爲什麼我需要編寫一個腳本來識別它。爲了簡單起見,我們仍然假定定義從來沒有在文件中從最初的提交回滾中移動。 –
[檢索文件中特定行的提交日誌?](http://stackoverflow.com/questions/8435343/retrieve-the-commit-log-for-a-specific-line-in-a -file) –