svn diff incluindo informações sobre anotações / culpas de quando mudanças foram feitas por quem

7

Você pode adicionar informações de anotações / culpas ao svn diff, de modo que para cada linha alterada inclua qual usuário e revisão alteraram essa linha?

Por exemplo, uma revisão de comparação de anotações 8-10 pode produzir algo como:

9    user1   - some line that user1 deleted in revision 9
10   user2   + some line that user2 added in revision 10

O contexto, as linhas em torno dele que não foram alteradas, podem ser incluídas também ou não, não importa.

Não é apenas uma questão de "rapidamente" escrever um script de shell combinando a saída de svn diff e svn annotate. anotar por exemplo nunca mostrará quem removeu uma linha. Também não é uma questão de fazer anotações em uma revisão no passado: não estamos interessados em quem originalmente adicionou a linha que foi removida (não foi essa que "causou" o diff), queremos saber quem a removeu. Eu suspeito que a única maneira de implementar algo para fazer isso é inspecionar cada commit entre as duas revisões sendo comparadas (e de alguma forma mapear todas as mudanças nos diffs separados para as linhas no diff total) ...

Existe uma ferramenta que faz algo assim?

    
por Wouter Coekaerts 10.09.2009 / 12:32

5 respostas

1

Eu não tenho certeza se entendi exatamente o que você quer, mas eu fiz isso com o TortoiseSVN:

  1. Criar culpa da revisão 1 para a revisão A - salvar como A.txt
  2. Criar culpa da revisão 1 para a revisão B - salvar como B.txt
  3. Remover a primeira coluna (linha) dos dois arquivos de texto (usei o editor Pspad, que pode excluir uma coluna de um arquivo de texto)
  4. Mesclar A.txt e B.txt (TortoiseMerge.exe / base :"a.txt "/mine:"b.txt")

Ele mostra as linhas alteradas, adicionadas e removidas entre as revisões A e B juntamente com a data, usuário e filial. Acho que é isso que você queria.

    
por 20.12.2010 / 01:20
1
O

SVN diff leva exatamente duas revisões e gera a saída na hora. Anotação SVN leva exatamente uma revisão. Sua suspeita de que o utilitário proposto precisará iterar em N revisões está correta; O SVN armazena os estados de revisão como objetos inteiros.

Você pode ter mais sorte com o git e o gateway git-svn ...

    
por 09.03.2011 / 05:15
0

Na sua configuração SVN básica baunilha não há comando que possa mesclar as saídas de culpa e diff em uma revisão particular. Se você especificou o cliente SVN em particular que você usou, eu poderia ter encontrado um plugin que faz isso, mas você não o fez, então não pude procurar por um.

A única alternativa é usar um script ou um programa pequeno para combinar as duas saídas. Isso não deve ser tão difícil se você conseguir números de linha no diff e na culpa. Mesmo que esta não seja a resposta que você esperava, é uma resposta e esta pergunta está aberta desde 2009.

    
por 11.12.2010 / 01:20
0

Isso não é exatamente o que você procurava, mas é possível ter uma visão anotada das alterações em OpenGrok .

    
por 29.12.2010 / 05:33
0

Does there exist a tool that does something like that?

Bem, acho que agora existe.

Uso; blameDiff <path> [rev1] [rev2]

função bash

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=' '

    #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'2'" \
                    --new-line-format="?%c'2'" \
                    --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='\v\v\v'

        #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
    }
}

Adicionei comentários como explicação, por isso não vou entrar aqui.
Testado para trabalhar com as saídas de diff (fedora 27), svn info (1.10.2) no meu sistema, YMMV (mas para todos os meus esforços, espero que não muito!).

Basicamente, ele reimplementa o svn diff usando apenas svn cat e o regular diff para levar em consideração os números de revisão e de linha, rastreando exatamente onde no histórico uma determinada linha foi removida. Até mesmo leva em conta se os arquivos estão em ramos diferentes e o exibe como o svn faria.

Aqui estão as capturas de tela dos dois comandos a seguir, com o código redigido por motivos de trabalho.

~/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 -

Como você pode ver, várias informações extras são fornecidas no novo formato à direita; as primeiras colunas mostram que ashley adicionou algumas linhas de volta em r6631, e deletou um monte em r6639 originalmente cometido por zes há muito tempo atrás @ r6466 & 6483.

    
por 24.10.2018 / 14:32

Tags