Culpa do Git - confirmações anteriores?

391

É possível ver quem editou uma linha específica antes da confirmação relatada por git blame, como um histórico de confirmações para uma determinada linha?

Por exemplo, eu executo o seguinte (no excelente uncrustifyprojeto):

$ git blame -L10,+1 src/options.cpp
^fe25b6d (Ben Gardner 2009-10-17 13:13:55 -0500 10) #include "prototypes.h"

Como posso descobrir quem editou essa linha antes de confirmar fe25b6d? E quem editou antes desse commit?

Sedate Alien
fonte
7
se o motivo pelo qual você está procurando confirmações anteriores for alterações de espaço em branco, use a -wopção Há também -Mpara se mudou código / copiado
brita_
A olhar para todos os commits que envolvem uma determinada palavra, ver o meu script abaixo
VonC
Aqui está um script útil para adicionar essa funcionalidade no github greasyfork.org/en/scripts/…
Aaron Hoffman
3
Não tenho certeza de como era o github quando o @AaronHoffman postou, mas é muito fácil culpar - e obter culpas pelas versões anteriores - no github agora .
ruffin 12/09

Respostas:

389
git blame -L 10,+1 fe25b6d^ -- src/options.cpp

Você pode especificar uma revisão para a culpa do git olhar para trás a partir de (em vez do padrão de HEAD); fe25b6d^é o pai de fe25b6d.

Âmbar
fonte
107
Você pode obter um histórico completo, sem precisar redigitar o comando várias vezes com hashes diferentes?
Anders Zommarin
13
Não acredito que o Git tenha uma maneira integrada de obter toda a culpa que tocou em um número de linha (o que meio que faz sentido, pois uma determinada linha pode não ter um número de linha consistente ao longo do histórico de um arquivo devido a inserções e exclusões de linhas).
Amber
17
@ Amber: Tenho certeza de que você está certo de que o recurso não existe, mas parece que poderia ser implementado de forma ingênua, simplesmente fazendo o que um ser humano faria: culpe uma vez, pegue as informações relatadas, culpe , e assim por diante.
Cascabel
15
O git gui facilita bastante verificar o histórico de uma linha, pois as versões são clicáveis.
Zitrax
5
O @shadyabhi --é comumente usado como um separador nos argumentos da linha de comando - no caso do Git, geralmente é usado para separar coisas como commit hashes de uma lista de nomes de arquivos.
Âmbar
192

Você pode usar git log -L para visualizar a evolução de um intervalo de linhas.

Por exemplo :

git log -L 15,23:filename.txt

significa "rastrear a evolução das linhas 15 a 23 no arquivo nomeado filename.txt".

Navneet
fonte
12
Esta é uma resposta sólida e aborda a pergunta de Anders Zommarin acima sobre como ver as alterações em linhas específicas ao longo do tempo.
bigtex777
4
FYI: git log -L <start>, <end>: <file> requer o Git 1.8.4+, consulte: git-scm.com/docs/git-log#git-log--Lltstartgtltendgtltfilegt para opções de sintaxe
Neon
30

A resposta de Amber está correta, mas achei incerto; A sintaxe é:

git blame {commit_id} -- {path/to/file}

Nota: --é usado para separar o tree-ish sha1 dos caminhos de arquivo relativos. 1 1

Por exemplo:

git blame master -- index.html

Crédito total para Amber por saber todas as coisas! :)

ThorSummoner
fonte
11
Eu concordo com o seu sentimento. O sistema de comentários é, no entanto, muito restrito para apresentar todas as informações claramente. Eu adicionei o conteúdo desta resposta em um comentário; no entanto, insisto em deixar que esta resposta seja um local de fácil acesso.
ThorSummoner
11
Deve ser uma postagem separada ou uma edição. Eu gosto disso como uma resposta separada.
Flimm
27

Você pode querer conferir:

git gui blame <filename>

Oferece uma boa exibição gráfica de alterações como "culpa do git", mas com links clicáveis ​​por linha, para passar para confirmações anteriores. Passe o mouse sobre os links para obter um pop-up com detalhes de confirmação. Nem meus créditos ... encontrei aqui:

http://zsoltfabok.com/blog/2012/02/git-blame-line-history/

git guié uma interface gráfica Tcl / Tc para git. Sem nenhum outro parâmetro, ele inicia um aplicativo gráfico bastante simples, mas útil, para confirmar arquivos, blocos ou até linhas simples e outros comandos semelhantes, como alterar, reverter, empurrar ... Faz parte do conjunto de ações git. No Windows, está incluído no instalador. No debian - eu não conheço outros sistemas * nix - ele deve ser instalado separadamente:

apt-get install git-gui

Dos documentos:

https://git-scm.com/docs/git-gui

DESCRIÇÃO

Uma interface gráfica de usuário baseada em Tcl / Tk para o Git. O git gui se concentra em permitir que os usuários façam alterações em seu repositório fazendo novos commits, alterando os existentes, criando ramificações, realizando mesclagens locais e buscando / enviando para repositórios remotos.

Diferentemente do gitk, o git gui se concentra na geração de confirmação e na anotação de arquivo único e não mostra o histórico do projeto. No entanto, ele fornece ações de menu para iniciar uma sessão do gitk a partir do git gui.

Sabe-se que o git gui funciona em todos os sistemas UNIX populares, Mac OS X e Windows (tanto no Cygwin quanto no MSYS). Na medida do possível, são seguidas as diretrizes específicas da interface do usuário do SO, tornando o git gui uma interface bastante nativa para os usuários.

COMANDOS

culpa

Inicie um visualizador de culpa no arquivo especificado na versão fornecida (ou no diretório de trabalho, se não especificado).

navegador

Inicie um navegador em árvore mostrando todos os arquivos na confirmação especificada. Os arquivos selecionados pelo navegador são abertos no visualizador de culpa.

citool

Inicie o git gui e organize para fazer exatamente um commit antes de sair e retornar ao shell. A interface é limitada apenas a ações de confirmação, reduzindo um pouco o tempo de inicialização do aplicativo e simplificando a barra de menus.

versão

Exiba a versão atualmente em execução do git gui.

Holger Böhnke
fonte
Isso não funciona para mim. Posso clicar na alteração na linha especificada, mas isso muda apenas a exibição para esse commit, e a linha atual agora é exibida como this: mas como vejo a versão anterior da linha e quando ela foi adicionada?
BeeOnRope
Este é o caso de uso Eu sei de onde gui git é a melhor solução
cambunctious
17

Com base na resposta anterior, essa linha única do bash deve fornecer o que você está procurando. Ele exibe o histórico de culpa do git para uma linha específica de um arquivo específico, através das últimas 5 revisões:

LINE=10 FILE=src/options.cpp REVS=5; for commit in $(git rev-list -n $REVS HEAD $FILE); do git blame -n -L$LINE,+1 $commit -- $FILE; done

Na saída deste comando, você pode ver o conteúdo da linha mudar, ou o número da linha exibido pode até mudar, para uma confirmação específica.

Isso geralmente indica que a linha foi adicionada pela primeira vez, após esse commit específico. Também pode indicar que a linha foi movida de outra parte do arquivo.

Will Sheppard
fonte
5
Observe que estas são as últimas revisões do $ REVS nas quais $ FILE foi alterado, em vez das últimas revisões do $ REVS nas quais $ LINE foi alterado.
Max Nanasy
A que resposta você está se referindo?
Flimm
Não me lembro mais. Possivelmente eu poderia ter melhorado minha resposta no futuro.
Will Sheppard
12

Há também recursive-blame. Pode ser instalado com

npm install -g recursive-blame
Thomas W
fonte
11

Uma solução muito exclusiva para esse problema está usando o git log:

git log -p -M --follow --stat - caminho / para / seu / arquivo

Como explicado por Andre aqui

Mannu
fonte
11
Eu criei um alias para usar esta: git config --global alias.changes 'log -p -M --follow --stat --'e então eu posso simplesmente digitargit changes path/to/your/file
Tizio Fittizio
Esta é de longe a melhor resposta e exatamente o que eu estava procurando. Simples e elegante.
maesk
10

Se você estiver usando o JetBrains Idea IDE (e derivados), poderá selecionar várias linhas, clique com o botão direito do mouse no menu de contexto e, em seguida, Git -> Mostrar histórico para seleção. Você verá a lista de confirmações que estavam afetando as linhas selecionadas:

insira a descrição da imagem aqui

warvariuc
fonte
Isso funcionou melhor do que as outras respostas para mim (usando o IntelliJ). Demorou um pouco para carregar todas as revisões, mas valeu a pena esperar.
Steve Chambers
2

A partir do Git 2.23, você pode usar a culpa do git --ignore-rev

Para o exemplo dado na pergunta, seria:

git blame -L10,+1 src/options.cpp --ignore-rev fe25b6d

(no entanto, é uma pergunta complicada porque fe25b6d é a primeira revisão do arquivo!)

Michael Platings
fonte
1

Com base na resposta de Will Shepard, sua saída incluirá linhas duplicadas para confirmações onde não houve alterações, para que você possa filtrá-las da seguinte maneira (usando esta resposta )

LINE=1 FILE=a; for commit in $(git rev-list HEAD $FILE); do git blame -n -L$LINE,+1 $commit -- $FILE; done | sed '$!N; /^\(.*\)\n\1$/!P; D'

Observe que eu removi o argumento REVS e isso remonta ao commit raiz. Isso se deve à observação de Max Nanasy acima.

DavidN
fonte
1

Com base na resposta de DavidN e desejo seguir o arquivo renomeado:

LINE=8 FILE=Info.plist; for commit in $(git log --format='%h%%' --name-only --follow -- $FILE | xargs echo | perl -pe 's/\%\s/,/g'); do hash=$(echo $commit | cut -f1 -d ','); fileMayRenamed=$(echo $commit | cut -f2 -d ','); git blame -n -L$LINE,+1 $hash -- $fileMayRenamed; done | sed '$!N; /^\(.*\)\n\1$/!P; D'

ref: exibe bem o histórico de renomeação de arquivos no log do git

Bill Chan
fonte
0

Eu uso esse pequeno script bash para examinar um histórico de culpa.

Primeiro parâmetro: arquivo para olhar

Parâmetros subsequentes: passados ​​para a culpa

#!/bin/bash
f=$1
shift
{ git log --pretty=format:%H -- "$f"; echo; } | {
  while read hash; do
    echo "--- $hash"
    git blame $@ $hash -- "$f" | sed 's/^/  /'
  done
}

Você pode fornecer parâmetros de culpa como -L 70, + 10, mas é melhor usar a pesquisa regex da culpa git porque os números de linha geralmente "mudam" ao longo do tempo.

stangls
fonte
0

Desenvolver a stangls 's resposta , eu coloquei esse script no meu PATH (mesmo no Windows) como git-bh:

Isso me permite procurar todos os commits em que uma palavra estava envolvida:

git bh path/to/myfile myWord

Roteiro:

#!/bin/bash
f=$1
shift
csha=""
{ git log --pretty=format:%H -- "$f"; echo; } | {
  while read hash; do
    res=$(git blame -L"/$1/",+1 $hash -- "$f" 2>/dev/null | sed 's/^/  /')
    sha=${res%% (*}
    if [[ "${res}" != "" && "${csha}" != "${sha}" ]]; then
      echo "--- ${hash}"
      echo "${res}"
      csha="${sha}"
    fi
  done
}
VonC
fonte