Существует ли инструмент, который делает что-то подобное?
Ну, я думаю, что сейчас.
Использование; blameDiff <path> [rev1] [rev2]
функция Баш
function blameDiff() { file="$1" rev1="$2" rev2="$3" #default to HEAD if omitted if [ -n "$rev1" ] then title1="(revision $rev1)" else title1="(working copy)" rev1='HEAD' fi if [ -n "$rev2" ] then title2="(revision $rev2)" else title2="(working copy)" rev2='HEAD' fi #check that the svn urls are the same tmp1="$(svn info -r $rev1 "$file" |\ grep '^Relative URL' |\ sed 's/Relative URL: //' \ )" tmp2="$(svn info -r $rev2 "$file" |\ grep '^Relative URL' |\ sed 's/Relative URL: //' \ )" if [ "$tmp1" != "$tmp2" ] then #if not, then one of these revisions is in another branch #lets have this in the output title1="($tmp1) $title1" title2="($tmp2) $title2" fi #can just print this but you wont get deleted revision/blame # diff -u \ # <(svn blame -r "$rev1" "$file") \ # <(svn blame -r "$rev2" "$file") \ # | sed "s|^--- .*$|--- $file $title1|" \ # | sed "s|^+++ .*$|+++ $file $title2|" # return 0 #an array of commitNumber|committer pairs for the file history=() #a map between elements in `history` and a list of line numbers changed. #each item in the list is a lineNumber|newLineNumber pair declare -A revisions #the sed match and replace expressions to pull data from the #diff-line-number&cat-line-number combo and give it to the cache grabData='^ *\([0-9]\+\)\t\([0-9]\+\)$' formatData='\2 \1' #for each revision between the ones given last='' while read -r line do #read in the revision number and submitter IFS=' |' read next by tmp <<<"$line" if [ -n "$last" ] then #save them history+=("$next $by") #associate and format the list revisions["$"]="$(\ diff \ --unchanged-line-format="%dn%c'\012'" \ --new-line-format="?%c'\012'" \ --old-line-format='' \ <(svn cat -r "$last" "$file") \ <(svn cat -r "$next" "$file") \ | cat -n \ | grep -v '?$' \ | sed "s/$grabData/$formatData/" \ )" fi #remember the last revision looked at last="$next" done <<<"$( svn log -r "$rev1:$rev2" "$file" \ | grep '^r[0-9]\+ | ' \ | sed 's/^r//' \ )" #pull the full diff diff \ --new-line-format='+%L' \ --old-line-format='-%L' \ --unchanged-line-format='=%L' \ <(svn blame -r "$rev1" "$file") \ <(svn blame -r "$rev2" "$file") \ | { #header stuff echo "Index: $file" echo '===================================================================' echo "--- $file $title1" echo "+++ $file $title2" #count the line number we're up to for the original file origLine=0 #count the line number we're up to for the new file newLine=0 #keep a few of the output lines, and their line number contexts buffer=() origContext=() newContext=() #tells the script to print the buffer if <3; #the context lines around real differences printing=4 #whether or not the next print needs to show line numbers needsContext=true #the sed match and replace expressions to pull data from diff #and give it to read grabData='^\([+=-]\)\( *[0-9]\+\)\( *[^ ]\+\)\(.*\)$' formatData='\1\v\2\v\3\v\4' #for each line in the full diff while read -r data do IFS=$'\v' read flag committed who line <<<"$(\ sed $'s/\t/ /g' \ <<<"$data" \ | sed "s/$grabData/$formatData/" \ )" #the last surviving revision of the line edited="$rev2" #who killed this line by='' case "$flag" in +) #a new line was introduced ((++newLine)) printing=0 ;; -) #an old line was removed ((++origLine)) printing=0 #the line number that changes throughout history number="$origLine" #for each commit for revision in "$" do #read in the two line numbers from the matching change number="$(grep "^$number " <<<"$")" IFS=' ' read edited by <<<"$revision" #not present; this was the revision where it was destroyed if [ -z "$number" ] then break fi #pull the new line number for the next revision IFS=' ' read tmp number <<<"$number" done ;; =) #an old line continues to exist in the new file ((++newLine)) ((++origLine)) flag=' ' ((++printing)) ;; esac #format the line to print buffer+=("$(printf "%s %s:%-${#committed}s%s:%-${#who}s%s" \ "$flag" \ "$committed" \ "$edited" \ "$who" \ "$by" \ "$line" \ )") #can just end it here, but it will print the whole file/s # echo "$" # buffer=() # continue #and add the context origContext+=("$origLine") newContext+=("$newLine") if ((printing < 4)) then if $needsContext then echo "@@ -$ +$ @@" needsContext=false fi #print all lines in the buffer for line in "$" do echo "$line" done #and reset it origContext=() newContext=() buffer=() fi #if there are too many lines in the buffer if ((${#buffer[@]} > 3)) then #remove the overflow origContext=("$") newContext=("$") buffer=("$") #and note that we now need to show the context because of this needsContext=true fi done } }
Я добавил комментарии в качестве объяснения, поэтому я не буду вдаваться в подробности.
Протестировано для работы с выходами diff
(fedora 27), svn info
(1.10.2) в моей системе, YMMV (но, несмотря на все мои усилия, я надеюсь, что не так много!).
Это в основном переопределяет svn diff
использование just svn cat
и регулярно, diff
чтобы принять во внимание номер ревизии и строки, отслеживая точно, где в истории была удалена данная строка.
Даже принимает во внимание, находятся ли файлы в разных ветвях и отображает их как svn.
Вот скриншоты двух следующих команд с кодом, отредактированным по соображениям работы.
~/data/<redacted>/svn-2.4.2/$ svn diff -r 6600 services/<redacted>.w3p | gvim - ~/data/<redacted>/svn-2.4.2/$ blameDiff services/<redacted>.w3p 6600 | gvim -
Как видите, куча дополнительной информации дана в новом формате справа; первые столбцы показывают, что Эшли добавила пару строк назад в r6631 и удалила целую связку в r6639, изначально зафиксированную zes @ r6466 & 6483.