Como "culpo" uma linha excluída?

506

git blameé ótimo para linhas modificadas e adicionadas, mas como posso descobrir quando uma linha que existia em uma confirmação anterior específica foi excluída? Estou pensando bisect, mas esperava algo mais prático.

(Antes que você pergunte: neste caso, eu apenas fiz uma git log -pe procurou através da linha de código e (a) algum idiota tinha apenas excluída a linha vital na submissão anterior e (b) eu era aquele idiota.)

Malvolio
fonte
4
Há um acompanhamento com uma resposta esclarecendo que git log -S<string> /path/to/filedeseja -cou -ccque também mostre remoções durante a mesclagem (conflitos) #
687
3
Deveria ser -ce --cc. @Steen: Correto, obrigado por apontar! Supervisão estúpida. Gostaria de poder editar o comentário. Adicionar um novo, depois excluir o meu e excluir o seu é muito complicado, eu acho :) #
5813
4
Eu gostaria git blameque tivesse uma opção para mostrar as linhas excluídas (talvez com tachado ou texto em vermelho) com a revisão na qual elas foram excluídas.
Craig McQueen
Seria difícil escrever? Eu não sei muito sobre o Git internals.
Malvolio

Respostas:

636

Se você conhece o conteúdo da linha, este é um caso de uso ideal para:

git log -S <string> path/to/file

que mostra as confirmações que introduzem ou removem uma instância dessa sequência. Há também o -G<regex>que faz a mesma coisa com expressões regulares! Consulte man git-loge pesquise as opções -Ge -S, ou a picareta (o nome amigável para esses recursos) para obter mais informações.

A -Sopção também é realmente mencionada no cabeçalho da página de git-blamemanual, na seção de descrição, onde fornece um exemplo usando git log -S....

Cascabel
fonte
Brilhante ... exatamente o que eu precisava neste trabalho de portabilidade em que estou trabalhando +1
jkp
37
Depois de usar o Git por mais de um ano, ainda me surpreende ver que o Git sempre tem um comando / opção em algum lugar para abordar quase qualquer cenário de uso que eu tenho. Obrigado por compartilhar este, é exatamente o que eu preciso agora!
Pascal Bourque
22
Esse método já funcionou para mim antes, mas agora vi um caso em que não encontrei o commit em que a linha foi excluída. Acontece que a linha em questão foi excluída em um commit de mesclagem - isso explicaria a falha? (O git blame --reversemétodo encontrou-lo embora.)
antinome
9
@antinome Para mostrar as confirmações da mesclagem, use a -copção adicionalmente.
yunzen
2
Eu fiz um ctrl + f em "-s" na página de manual e não encontrei nada. Onde você vê a página ?? Estou usando o git 1.8.5.2
temporary_user_name
135

Eu acho que o que você realmente quer é

git blame --reverse START..END filename

Na página de manual :

Caminhe para a frente em vez de para trás. Em vez de mostrar a revisão em que uma linha apareceu, isso mostra a última revisão em que uma linha existe. Isso requer várias revisões, como START..END, onde o caminho para culpar existe em START.

Com git blame reverse, você pode encontrar a última confirmação na qual a linha apareceu. Você ainda precisa obter a confirmação que vem depois.

Você pode usar o seguinte comando para mostrar um log git invertido. A primeira confirmação mostrada será a última vez que a linha aparecer e a próxima confirmação será quando ela for alterada ou removida.

git log --reverse --ancestry-path COMMIT^..master
Chronial
fonte
14
Se houver várias mesclagens do ramo em que a linha foi adicionada ao ramo em que a linha está ausente (ou qualquer outro caso em que haja vários caminhos na linhagem de START a END), git blame --reversea revisão será mostrada antes da mesclagem cronologicamente por último, não a revisão antes da mesclagem inicial em que a decisão foi tomada para não seguir a linha. Existe alguma maneira de encontrar a revisão mais antiga em que a linha parou de existir e não a mais recente?
rakslice
2
@rakslice, por isso você pode usar a culpa - reversa - first-parent, é um pouco melhor.
max630
17

Apenas para completar a resposta de Cascabel :

git log --full-history -S <string> path/to/file

Eu tive o mesmo problema mencionado aqui, mas a linha estava faltando, porque um commit de mesclagem de um ramo foi revertido e depois mesclado novamente, removendo efetivamente a linha em questão. A --full-historysinalização evita ignorar essas confirmações.

estani
fonte
9

git blame --reverse pode chegar perto de onde a linha é excluída. Mas, na verdade , não aponta para a revisão em que a linha é excluída. Aponta para a última revisão em que a linha estava presente . Então, se a revisão a seguir é uma confirmação simples, você tem sorte e obteve a revisão de exclusão. OTOH, se a revisão a seguir for um commit de mesclagem , as coisas podem ficar um pouco selvagens.

Como parte do esforço para criar o difflame , lidei com esse mesmo problema. Se você já possui o Python instalado na sua caixa e deseja experimentá-lo, não espere mais e informe-me como ele está.

https://github.com/eantoranz/difflame

eftshift0
fonte
0

Para alterações ocultas em confirmações de mesclagem

As confirmações de mesclagem automaticamente ocultam suas alterações na saída do log do Git. Ambos picareta e reverse-culpa não encontrou a mudança. Então a linha que eu queria tinha sido adicionada e removida mais tarde e eu queria encontrar a mesclagem que a removeu. O git log -p -- path/filehistórico do arquivo apenas mostrou que ele foi adicionado. Aqui está a melhor maneira que encontrei para encontrá-lo:

git log -p -U9999 -- path/file

Pesquise a alteração e, em seguida, pesquise "^ commit" - o primeiro "^ commit" é o commit em que o arquivo tinha essa linha pela última vez. O segundo "^ commit" é depois que desapareceu. O segundo commit pode ser o que o removeu. O -U9999objetivo é mostrar o conteúdo inteiro do arquivo (após cada vez que o arquivo foi alterado), assumindo que seus arquivos tenham no máximo 9999 linhas.

Localiza todas as mesclagens relacionadas via força bruta (difira cada confirmação de mesclagem possível com seu primeiro pai, execute várias toneladas de confirmações)

git log --merges --pretty=format:"git diff %h^...%h | grep target_text" HEAD ^$(git merge-base A B) | sh -v 2>&1 | less

(Tentei restringir mais o filtro de revisão, mas encontrei problemas e não o recomendo. As alterações de adição / remoção que eu procurava estavam em ramos diferentes que foram mesclados em momentos diferentes e A ... B não incluiu quando as alterações foram mescladas na linha principal.)

Mostre uma árvore Git com esses dois commits (e muito do histórico complexo do Git foi removido):

git log --graph --oneline A B ^$(git merge-base A B) (A é o primeiro commit acima, B é o segundo commit acima)

Mostrar histórico de A e histórico de B menos histórico de A e B.

Versão alternativa (parece mostrar o caminho mais linearmente do que a árvore de histórico regular do Git - no entanto, eu prefiro a árvore de histórico regular do Git):

git log --graph --oneline A...B

Três, não dois pontos - três pontos significa "r1 r2 - e não $ (git merge-base - todos os r1 r2). É o conjunto de confirmações que é acessível a partir de r1 (lado esquerdo) ou r2 (direita lado), mas não de ambos. " - fonte: "man gitrevisions"

Curtis Yallop
fonte