git report movei linhas

4

Eu quero acompanhar o número de adições e exclusões que excluem linhas movidas. Então, se um commit tiver 10 adições, 5 exclusões e 3 linhas movidas, então eu tenho 7 adições, 2 exclusões excluindo as linhas movidas. O 10 e o 5 são dados pelo seguinte código. Eu preciso gerar o 3 (eu só preciso de detecção de linha móvel fraca, por exemplo, qualquer linha que aparece identicamente como excluída simultaneamente de um local e adicionada a outro local dentro do mesmo commit).

Estou usando o seguinte para rastrear o número de adições e exclusões de um arquivo importante no meu repositório git.

git log --since=2014-08-01 --date=short --pretty=format:"%ad%x09" --numstat -- file.tex

Isso produz o seguinte, onde o primeiro número é adicionado e o segundo número é exclusões.

2014-08-19      
72      0       file.tex

2014-08-19      
211     290     file.tex

...

Eu quero adicionar uma terceira coluna, chame de linhas movidas. As linhas movidas por commit podem ser encontradas fazendo o seguinte em um loop para cada commit:

  1. Conjunto de alterações do Grep para linhas iniciadas por + ou -
  2. Strip levando + ou -
  3. sort
  4. uniq -d
  5. wc -l

Existe uma maneira rápida e elegante de executar esse pseudocódigo ou eu só preciso fazer um dump e analisar um monte de git diffs completos para conseguir o que eu preciso?

    
por rjturn 30.09.2014 / 21:58

1 resposta

0

Eu escrevi a seguinte implementação na linguagem TXR . No início, usei seu algoritmo para determinar a contagem movida. No entanto, notei que ele produz resultados que não são úteis: por exemplo, identificava valores positivos para "linhas movidas" em alterações que continham nada além de + linhas, simplesmente porque algumas das linhas + eram duplicadas uma da outra. . O novo algoritmo é discutido no Notes no final.

O programa completo:

#!/usr/bin/env txr
@(bind option-spec
       @(list (opt nil "since" :str
                   "Specifies the starting date (passed \
                   \ through to git); it is mandatory.")
              (opt nil "help" :bool
                   "Prints this help text")))
@(bind parsed-opts @(getopts option-spec *args*))
@(if (or [parsed-opts "help"] (not [parsed-opts "since"])))
@  (output)

usage: @{self-path} --since=<date> -- git arguments
@  (end)
@  (do (opthelp option-spec)
       (exit 0))
@(end)
@(do
   (defun histogram (strings)
     [group-reduce (hash :equal-based) identity (op succ @1) strings 0])

   (defun moved (a b)
     (let* ((hist-a (histogram a))
            (hist-b (histogram b))
            (isec [hash-isec hist-a hist-b min]))
       [reduce-left + (hash-values isec) 0])))
@(next (open-command 'git log --since=@[parsed-opts "since"] \
                     \ --date=short --pretty=format:"%H:%ad%x09" \
                     \ --numstat @{parsed-opts.out-args}'))
@(repeat)
@sha:@date@\t
@  (collect :gap 0)
@added@\t@removed@\t@rawpath
@    (next :string rawpath)
@    (cases)
@pro/{@before => @after}/@epi
@      (bind path '@pro/@after/@epi')
@    (or)
@before => @after
@      (bind path after)
@    (or)
@      (bind path rawpath)
@    (end)
@    (next (open-command 'git show -p @sha -- @path'))
@    (collect :vars ((+line nil) (-line nil)))
@      (cases)
+@{+line}
@      (or)
-@{-line}
@      (end)
@    (end)
@    (flatten -line +line)
@    (bind moved @(moved +line -line))
@  (end)
@  (output)
@date@\t
@    (repeat)
@added@\t@removed@\t@moved@\t@rawpath
@    (end)

@  (end)
@(end)

Eu tenho isso em um arquivo executável marcado chamado movedlines.txr e o uso de exemplo é:

$ ./movedlines.txr --since=2017-01-01 path/to

A opção --since é obrigatória; o path/to é um argumento opcional passado para git . Se você não especificar a opção obrigatória ou especificar --help , o programa imprimirá um resumo de ajuda e será encerrado.

Notas:

  • Alterei ligeiramente o formato de saída do seu comando git do exemplo para incluir o SHA à esquerda da data, separado por dois pontos: consulte o %H . O programa analisa isso e, em seguida, ele pode usar o SHA para fazer um git show -p em cada arquivo em cada conjunto. O SHA é omitido quando a imitação da saída é regurgitada pelo programa com a coluna movida adicional.

  • Existe uma complicação em que a saída do git mostra renomeações. A sintaxe se enquadra em três casos, que são analisados de forma clara e fácil por casos usando a construção @(cases) . Se um caminho inteiro for renomeado, a renomeação será from => to . Se apenas alguns componentes forem renomeados, será be/fore/{from => to}/after . Eu não sei se a sintaxe múltipla de chaves ocorre em um caminho; Eu não vi isso. Precisamos usar essa sintaxe e convertê-la em um caminho simples, porque o git não entende. Ou seja devemos converter be/fore/{from => to}/after em be/fore/to/after . Talvez haja uma opção git para que os caminhos sejam exibidos dessa forma sem a notação; Eu não me incomodei em procurar.

  • O script não é robusto contra espaços em nomes de arquivos devido ao uso de open-command . Para isso, precisamos de open-process , o que leva uma lista de argumentos.

  • O algoritmo calcula histogramas de frequência separados do diff - e + linhas (menos o primeiro - ou + ). Em seguida, calcula a interseção desses conjuntos, que mantém apenas as entradas do histograma que ocorrem nos dois histogramas. A função de junção para a interseção é min . Por exemplo, suponha que a linha abc tenha sido adicionada 5 vezes e removida 3 vezes. (min 3 5) é 3 e esse é o número de abc linhas movidas. Isso comuta. Se 3 ocorrências de abc forem removidas e 5 adicionadas, isso significa 3 movimentos. Isso não é de forma alguma considerado como um algoritmo perfeito para detectar movimentos. Ele tem armadilhas óbvias, como não detectar linhas que, de fato, se movem, mas também passam por uma mudança trivial de espaço em branco, como recuo.

por 12.07.2017 / 05:22

Tags