Gibt es ein Tool, das so etwas macht?
Nun, ich denke, das tut es jetzt.
Verwendungszweck; blameDiff <path> [rev1] [rev2]
Bash-Funktion
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["${history[-1]}"]="$(\
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 "${history[@]}"
do
#read in the two line numbers from the matching change
number="$(grep "^$number " <<<"${revisions["$revision"]}")"
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[-1]}"
# buffer=()
# continue
#and add the context
origContext+=("$origLine")
newContext+=("$newLine")
if ((printing < 4))
then
if $needsContext
then
echo "@@ -${origContext[0]} +${newContext[0]} @@"
needsContext=false
fi
#print all lines in the buffer
for line in "${buffer[@]}"
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=("${origContext[@]:1}")
newContext=("${newContext[@]:1}")
buffer=("${buffer[@]:1}")
#and note that we now need to show the context because of this
needsContext=true
fi
done
}
}
Ich habe Kommentare als Erklärung hinzugefügt, damit ich hier nicht darauf eingehen werde.
Getestet, um mit den Ausgängen von diff
(fedora 27), svn info
(1.10.2) auf meinem System YMMV zu arbeiten (aber trotz all meiner Bemühungen hoffe ich nicht so viel!).
Grundsätzlich wird die svn diff
Verwendung von "Just svn cat
and Regular" neu implementiert diff
, um die Revisions- und Zeilennummern zu berücksichtigen, und es wird genau verfolgt, wo in der Historie eine bestimmte Zeile entfernt wurde.
Berücksichtigt sogar, ob sich die Dateien in verschiedenen Zweigen befinden, und zeigt sie wie svn an.
Hier sind Screenshots der beiden folgenden Befehle, wobei der Code aus Arbeitsgründen redigiert wurde.
~/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 -
Wie Sie sehen können, werden rechts im neuen Format einige zusätzliche Informationen angezeigt. Die ersten Spalten zeigen, dass Ashley in r6631 ein paar Zeilen hinzugefügt und in r6639 eine ganze Reihe von Zeilen gelöscht hat, die ursprünglich von zes vor langer Zeit @ r6466 & 6483 festgeschrieben wurden.