Encontre um branch Git contendo alterações em um determinado arquivo

88

Tenho 57 filiais locais. Eu sei que fiz uma alteração em um determinado arquivo em um deles, mas não tenho certeza de qual. Existe algum tipo de comando que posso executar para descobrir quais ramificações contêm alterações em um determinado arquivo?

Dustin
fonte

Respostas:

87

Encontre todos os ramos que contêm uma alteração para FILENAME (mesmo se antes do ponto de ramificação (não registrado))

FILENAME="<filename>"
git log --all --format=%H $FILENAME | while read f; do git branch --contains $f; done | sort -u

Inspecione manualmente:

gitk --all --date-order -- $FILENAME

Encontre todas as alterações em FILENAME não mescladas com o mestre:

git for-each-ref --format="%(refname:short)" refs/heads | grep -v master | while read br; do git cherry master $br | while read x h; do if [ "`git log -n 1 --format=%H $h -- $FILENAME`" = "$h" ]; then echo $br; fi; done; done | sort -u
Seth Robertson
fonte
Preciso substituir algo neste comando além de FILENAME? Ele retorna todos os 57 nomes de ramificação.
Dustin
@Dustin: Se todos os 57 branches contiverem aquele nome de arquivo, então todos os 57 branches contêm uma mudança que inclui aquele nome de arquivo. Ramificações iniciam a partir da revisão mais antiga alcançável nessa ramificação, mesmo se essa revisão existisse antes de você criar a ramificação (a ramificação é, em alguns sentidos, criada retroativamente). Acho que você precisa definir melhor o seu problema. Você sabe qual é a mudança? Você poderia usar git log -Schange …ou git log --grep LOGMESSAGE …(com ... representando o resto do comando que mencionei).
Seth Robertson
2
@Dustin: Outra opção é usar o gitk --all -- filenameque mostrará graficamente todas as mudanças nesse arquivo. Se você pode identificar o commit em questão, então você pode usar git branch --containspara ver para quais branches o commit foi migrado. Se você quiser ver em qual branch o commit em questão foi originalmente criado, google git-what-branch, mas esteja ciente de que as mesclagens de avanço rápido podem obscurecer essas informações.
Seth Robertson
@ Set: Não consegui lembrar a mensagem de confirmação ou a mudança exata do código. Eu só tenho uma ideia geral. O problema é que ele ainda não foi mesclado com o master, então, para usar o gitk, eu ainda teria que verificar cada branch para encontrar o que procuro. Seria bom se eu pudesse dizer, "git, mostre-me os branches que fizeram alterações em FILENAME desde seu ponto de branch"
Dustin
1
Você poderia escrever a primeira linha com mais eficiênciagit log --all --format='--contains %H' "$file" | xargs git branch
kojiro
52

Tudo o que você precisa é

git log --all -- path/to/file/filename

Se você quiser saber a filial imediatamente, também pode usar:

git log --all --format=%5 -- path/to/file/filename | xargs -I{} -n 1 echo {} found in && git branch --contains {}

Além disso, se você teve qualquer renomeação, você pode querer incluir --followpara o comando Git log.

Adam Dymitruk
fonte
15
Isso mostrará os commits, mas ele perguntou qual branch. Adicione um --sourceaí e você está dourado.
Karl Bielefeldt
Obrigado, mas não acho que mostra todos os branches para cada commit. Mas ajudaria.
Adam Dymitruk
1
É verdade, mas, neste caso específico, ele estava procurando apenas um.
Karl Bielefeldt
btw para o caminho do asp.net deve estar no formato AppName / Controllers / XController.cs
Ali Karaca
8

Parece que ainda é um problema sem uma solução adequada. Não tenho créditos suficientes para comentar, então aqui está minha pequena contribuição.

A primeira solução de Seth Robertson meio que funcionou para mim, mas só me deu branches locais, entre os quais havia muitos falsos positivos, provavelmente por causa de mesclagens do branch estável.

A segunda solução de Adam Dymitruk não funcionou para mim. Para começar, o que é --format =% 5? Não é reconhecido pelo git, não consegui encontrar nada sobre ele e não consegui fazê-lo funcionar com outras opções de formato.

Mas sua primeira solução combinada com a opção --source e com um grep simples provou ser útil:

git log --all --source -- <filename> | grep -o "refs/.*" | sort -u

Isso me dá uma série de tags e branches remotos e um branch local, onde fiz as alterações mais recentes no arquivo. Não tenho certeza de como isso é completo.

ATUALIZE conforme a solicitação de @nealmcb, classificando os ramos pela mudança mais recente:

Primeiro, você pode alterar o grep para "refs / heads /.*", que fornecerá apenas os branches locais. Se houver apenas alguns branches, você pode examinar o commit mais recente de cada um assim:

git log -1 <branch> -- <filename>

Se houver mais branches e você realmente quiser automatizar isso, você pode combinar os dois comandos usando xargs, git log formatting e outra classificação neste one-liner:

git log --all --source -- <filename> | grep -o "refs/heads/.*" | sort -u | xargs -I '{}' git log -1 --format=%aI%x20%S '{}' -- <filename> | sort -r

Isso resultará em uma saída como esta:

2020-05-07T15:10:59+02:00 refs/heads/branch1
2020-05-05T16:11:52+02:00 refs/heads/branch3
2020-03-27T11:45:48+00:00 refs/heads/branch2
Simplório
fonte
Ótimo - obrigado. Agora, que tal classificar pela mudança mais recente?
nealmcb
1

Sei que essa pergunta é antiga, mas continuei voltando a ela antes de desenvolver minha própria solução. Eu sinto que é mais elegante e graças ao uso de filtros de base de mesclagem de ramos indesejados.

#!/bin/bash

file=$1
base=${2:-master}

b=$(tput bold) # Pretty print
n=$(tput sgr0)

echo "Searching for branches with changes to $file related to the $base branch"

# We look through all the local branches using plumbing
for branch in $(git for-each-ref --format='%(refname:short)' refs/heads/); do
  # We're establishing a shared ancestor between base and branch, to only find forward changes.  
  merge_base=$(git merge-base $base $branch)
  # Check if there are any changes in a given path.
  changes=$(git diff $merge_base..$branch --stat -- $file)

  if [[ ! -z $changes ]]; then
    echo "Branch: ${b}$branch${n} | Merge Base: $merge_base"
    # Show change statistics pretty formatted
    git diff $merge_base..$branch --stat -- $file
  fi
done

Se você colocá-lo em PATH como git-find-changes (com permissões de executável) - você pode chamá-lo comgit find-changes /path

Saída de exemplo para git find-changes app/models/

Branch: update_callbacks | Merge base: db07d23b5d9600d88ba0864aca8fe79aad14e55b
 app/models/api/callback_config.rb | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
Branch: repackaging | Merge base: 76578b9b7ee373fbe541a9e39cf93cf5ff150c73
 app/models/order.rb                 | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)
Marcin Raczkowski
fonte
Roteiro muito interessante. Votado. É mostrar mudanças ou encontrar mudanças?
VonC
Obrigado @VonC, uso corrigido, deve ser encontrado em todos os lugares
Marcin Raczkowski
0

O que segue é um método de força bruta deselegante, mas espero que funcione. Certifique-se de que você armazenou todas as alterações não confirmadas primeiro, pois isso mudará o branch em que você está.

for branch in $(git for-each-ref --format="%(refname:short)" refs/heads); do
    git checkout $branch && git grep SOMETHING
done
whiteinge
fonte
1
Porque se você sabe a mudança, você pode usargit log -S
Seth Robertson
Que shell você está assumindo? Bash?
Peter Mortensen
Sem bashismos, isso deve funcionar no POSIX. Não tenho certeza se for-each-refainda respeita essa shortbandeira. Dito isso, ignore minha resposta; as outras respostas são melhores.
whiteinge