Descobrindo de qual ramo veio um commit do Git

650

Existe uma maneira de descobrir de qual ramificação um commit é fornecido, devido ao seu valor de hash SHA-1 ?

Pontos de bônus se você puder me dizer como fazer isso usando Ruby Grit.

Ethan Gunderson
fonte
4
Os diferentes métodos abaixo são maneiras pragmáticas, úteis e úteis de inferir uma resposta provável , mas note que no git a pergunta em si é um mal-entendido, os commits não vêm de ramos. As ramificações vêm e vão, elas se movem, apenas confirmações representam a verdadeira história do repo. Por outro lado, essa não é uma maneira de dizer que as soluções abaixo são ruins. Apenas saiba que nenhum deles fornece uma resposta totalmente confiável, o que é impossível de obter por design no git. Ramos (Um caso simples é excluídos: I ramo, eu cometo duas vezes, I se fundem em um outro ramo, eu apagar o primeiro ramo Onde é que o commit "vir" a partir.?
RomainValeri
1
Eu tenho um caso em que eu puxo explicitamente um clone raso de profundidade 1 de uma tag. É barato, fácil e eficiente ... e até agora fiz tudo que eu queria lindamente. Agora que eu gostaria de saber em qual filial a etiqueta estava, estou de mangueira, pelo menos para esse detalhe. Você nem sempre pode ir para casa, lol
Paul Hodges

Respostas:

865

Embora Dav esteja certo de que as informações não são armazenadas diretamente, isso não significa que você nunca possa descobrir. Aqui estão algumas coisas que você pode fazer.

Localizar ramificações nas quais a confirmação está

git branch -a --contains <commit>

Isso informará todos os ramos que possuem o commit fornecido em sua história. Obviamente, isso é menos útil se o commit já tiver sido mesclado.

Pesquise os reflogs

Se você estiver trabalhando no repositório no qual a confirmação foi feita, poderá procurar nos reflogs a linha para essa confirmação. Os registros novos com mais de 90 dias são removidos pelo git-gc; portanto, se o commit for muito antigo, você não o encontrará. Dito isto, você pode fazer isso:

git reflog show --all | grep a871742

encontrar commit a871742. Note que você DEVE usar os 7 primeiros dígitos abreviados do commit. A saída deve ser algo como isto:

a871742 refs/heads/completion@{0}: commit (amend): mpc-completion: total rewrite

indicando que a confirmação foi feita na ramificação "conclusão". A saída padrão mostra hashes de confirmação abreviados, portanto, não procure o hash completo ou você não encontrará nada.

git reflog showé na verdade apenas um apelido para git log -g --abbrev-commit --pretty=oneline, então, se você quiser mexer no formato de saída para disponibilizar coisas diferentes para o grep, esse é o seu ponto de partida!

Se você não estiver trabalhando no repositório em que o commit foi feito, o melhor que você pode fazer neste caso é examinar os reflogs e descobrir quando o commit foi introduzido pela primeira vez em seu repositório; com alguma sorte, você buscou o ramo em que estava comprometido. Isso é um pouco mais complexo, porque você não pode percorrer a árvore de confirmação e os reflogs simultaneamente. Você deseja analisar a saída do reflog, examinando cada hash para ver se contém a confirmação desejada ou não.

Encontre uma confirmação de mesclagem subsequente

Isso depende do fluxo de trabalho, mas com bons fluxos de trabalho, as confirmações são feitas nas ramificações de desenvolvimento que são mescladas. Você pode fazer isso:

git log --merges <commit>..

para ver os commits de mesclagem que têm o commit fornecido como um ancestral. (Se o commit foi mesclado apenas uma vez, o primeiro deve ser a mesclagem que você procura; caso contrário, você terá que examinar alguns, suponho.) A mensagem de commit de mesclagem deve conter o nome do ramo que foi mesclado.

Se você quiser contar com isso, use a --no-ffopção git mergepara forçar a criação de consolidação de mesclagem, mesmo no caso de avanço rápido. (Porém, não fique muito ansioso. Isso pode se tornar ofuscante se for usado em excesso.) A resposta de VonC a uma pergunta relacionada é útil para elaborar esse tópico.

Cascabel
fonte
5
+1. A simples ausência de informações para esse problema específico faz você se perguntar se é realmente um problema? As ramificações podem mudar, ser renomeadas ou excluídas a qualquer momento. Pode ser o git describesuficiente, pois as tags (anotadas) podem ser vistas como mais significativas que as ramificações.
VonC
9
Eu definitivamente concordo - os galhos devem ser leves e flexíveis. Se você adotar um fluxo de trabalho no qual essas informações se --no-fftornem importantes, poderá usar a opção para garantir que haja sempre uma confirmação de mesclagem, para poder sempre rastrear o caminho de uma confirmação dada à medida que ela é mesclada com o mestre.
Cascabel
32
Para sua informação, se você deseja encontrar confirmações que existem apenas no controle remoto, adicione a -aflag ao primeiro comando, por exemplo:git branch -a --contains <commit>
Jonathan Day
8
@ JonathanDay: Não, isso encontrará confirmações em qualquer filial. Se você quiser apenas os do controle remoto, use -r.
Cascabel
7
@SimonTewsi Como eu disse, se é realmente um problema, use merge --no-ffpara registrar de forma confiável o nome da filial quando você mesclar. Caso contrário, pense nos nomes das filiais como rótulos abreviados temporários e envie as descrições como permanentes. "Que nome abreviado nos referimos a isso durante o desenvolvimento?" realmente não deve ser uma pergunta tão importante quanto "o que esse commit faz?"
Cascabel 15/05
154

Este comando simples funciona como um encanto:

git name-rev <SHA>

Por exemplo (onde test-branch é o nome do branch):

git name-rev 651ad3a
251ad3a remotes/origin/test-branch

Mesmo isso está funcionando para cenários complexos, como:

origin/branchA/
              /branchB
                      /commit<SHA1>
                                   /commit<SHA2>

Aqui git name-rev commit<SHA2>retorna branchB .

khichar.anil
fonte
3
Você salvou o meu dia. Esta é a solução perfeita.
Taco
7
E levou apenas 8 anos para esta solução perfeita. Obrigado!
S1J0
6
Eu achei git name-rev --name-only <SHA>mais útil obter apenas o nome do ramo. Minha pergunta ... Ele pode retornar mais de um ramo em qualquer circunstância?
Dean Kayton
1
Essa deve ser a melhor resposta.
JCollier 27/01
1
Obrigado @JCollier por suas amáveis ​​palavras.
khichar.anil 11/02
47

Atualização em dezembro de 2013:

sschuberth comentários

git-what-branch(Script Perl, veja abaixo) não parece mais ser mantido. git-when-mergedé uma alternativa escrita em Python que está funcionando muito bem para mim.

É baseado em " Localizar consolidação de mesclagem, que inclui uma consolidação específica ".

git when-merged [OPTIONS] COMMIT [BRANCH...]

Descubra quando uma consolidação foi mesclada em uma ou mais ramificações.
Localize a consolidação de mesclagem que trouxe COMMITpara as FILIAIS especificadas.

Especificamente, procure o commit mais antigo no histórico do primeiro pai BRANCHque contém o COMMITcomo um ancestral.


Resposta original setembro de 2010:

Sebastien Douche apenas twittou (16 minutos antes desta resposta do SO):

git-what-branch : descubra em qual ramo um commit está ou como ele chegou a um branch nomeado

Este é um script Perl de Seth Robertson que parece muito interessante:

SINOPSE

git-what-branch [--allref] [--all] [--topo-order | --date-order ]
[--quiet] [--reference-branch=branchname] [--reference=reference]
<commit-hash/tag>...

VISÃO GERAL

Diga-nos (por padrão) o caminho causal mais antigo de confirmações e mesclagens para fazer com que a confirmação solicitada chegue a um ramo nomeado. Se uma confirmação foi feita diretamente em uma ramificação nomeada, esse é obviamente o caminho mais antigo.

Por caminho causal mais antigo, queremos dizer o caminho que se fundiu em um ramo nomeado o mais cedo, pelo tempo de consolidação (a menos que --topo-orderseja especificado).

DESEMPENHO

Se muitas ramificações (por exemplo, centenas) contiverem a confirmação, o sistema poderá demorar muito tempo (para uma confirmação específica na árvore do Linux, foram necessários 8 segundos para explorar uma ramificação, mas havia mais de 200 ramificações candidatas) para rastrear o caminho para cada confirmação.
A seleção de um determinado --reference-branch --reference tagexame será centenas de vezes mais rápido (se você tiver centenas de filiais candidatas).

EXEMPLOS

 # git-what-branch --all 1f9c381fa3e0b9b9042e310c69df87eaf9b46ea4
 1f9c381fa3e0b9b9042e310c69df87eaf9b46ea4 first merged onto master using the following minimal temporal path:
   v2.6.12-rc3-450-g1f9c381 merged up at v2.6.12-rc3-590-gbfd4bda (Thu May  5 08:59:37 2005)
   v2.6.12-rc3-590-gbfd4bda merged up at v2.6.12-rc3-461-g84e48b6 (Tue May  3 18:27:24 2005)
   v2.6.12-rc3-461-g84e48b6 is on master
   v2.6.12-rc3-461-g84e48b6 is on v2.6.12-n
   [...]

Este programa não leva em consideração os efeitos de escolher a confirmação de interesse, apenas as operações de mesclagem.

VonC
fonte
4
git-what-branchnão parece mais ser mantido. O git-when-mesclado é uma alternativa escrita em Python que está funcionando muito bem para mim.
sschuberth
@sschuberth obrigado por esta atualização. Incluímos seu comentário na resposta para obter mais visibilidade.
VonC
37

Por exemplo, para descobrir que o c0118facommit veio redesign_interactions:

* ccfd449 (HEAD -> develop) Require to return undef if no digits found
*   93dd5ff Merge pull request #4 from KES777/clean_api
|\
| * 39d82d1 Fix tc0118faests for debugging debugger internals
| * ed67179 Move &push_frame out of core
| * 2fd84b5 Do not lose info about call point
| * 3ab09a2 Improve debugger output: Show info about emitted events
| *   a435005 Merge branch 'redesign_interactions' into clean_api
| |\
| | * a06cc29 Code comments
| | * d5d6266 Remove copy/paste code
| | * c0118fa Allow command to choose how continue interaction
| | * 19cb534 Emit &interact event

Você deve executar:

git log c0118fa..HEAD --ancestry-path --merges

E role para baixo para encontrar o último commit de mesclagem . Qual é:

commit a435005445a6752dfe788b8d994e155b3cd9778f
Merge: 0953cac a06cc29
Author: Eugen Konkov
Date:   Sat Oct 1 00:54:18 2016 +0300

    Merge branch 'redesign_interactions' into clean_api

Atualizar

Ou apenas um comando:

git log c0118fa..HEAD --ancestry-path --merges --oneline --color | tail -n 1
Eugen Konkov
fonte
4
Esta foi a única solução que me deu o resultado desejado. Eu tentei o resto.
Crafter
Observe que isso só funciona se os resumos de consolidação de mesclagem contiverem os nomes das ramificações usadas nas mesclagens, o que geralmente ocorre por padrão. No entanto git merge -m"Any String Here", ocultará as informações de origem e destino da ramificação.
qneill
@qneill: Não, a fusão sempre tem informações sobre filiais incorporadas: Merge: f6b70fa d58bdcb. Você pode nomear seu commit de mesclagem como você. Não terei problema #
Eugen Konkov
1
@EugenKonkov depois que as ramificações atravessarem a mesclagem, o histórico sobre o caminho no gráfico de origem é deixado no reflog, mas não no banco de dados de objetos git. Eu postei uma resposta tentando explicar o que estou dizendo
qneill
UPD Works versão para mim, comando simples que encontra o original comprometer
vpalmu
9

git branch --contains <ref>é o comando "porcelana" mais óbvio para fazer isso. Se você quiser fazer algo semelhante apenas com os comandos "encanamento":

COMMIT=$(git rev-parse <ref>) # expands hash if needed
for BRANCH in $(git for-each-ref --format "%(refname)" refs/heads); do
  if $(git rev-list $BRANCH | fgrep -q $COMMIT); then
    echo $BRANCH
  fi
done

(cruzamento desta resposta SO )

Jeff Bowman
fonte
6

khichar.anil cobriu a maior parte disso em sua resposta.

Estou apenas adicionando o sinalizador que removerá as tags da lista de nomes de revisões. Isso nos dá:

git name-rev --name-only --exclude=tags/* $SHA
phyatt
fonte
Algo parece estar faltando na segunda frase.
Peter Mortensen
Eu atualizei a descrição. Obrigado pelo feedback.
Phyatt #
4

Opção de um homem pobre é usar a ferramenta tig1 em HEAD, procure a cometer, e siga visualmente a linha do que cometem back-up até que um merge commit é visto. A mensagem de mesclagem padrão deve especificar para qual filial será mesclada onde :)

1 Tig é uma interface em modo de texto baseada em ncurses para o Git. Ele funciona principalmente como um navegador de repositório Git, mas também pode auxiliar no preparo de alterações para confirmação no nível do chunk e atuar como um pager para saída de vários comandos do Git.

user1338062
fonte
3

Como experiência, criei um gancho pós-confirmação que armazena informações sobre a ramificação com check-out atualmente nos metadados de confirmação. Também modifiquei levemente o gitk para mostrar essa informação.

Você pode conferir aqui: https://github.com/pajp/branch-info-commits

pajp
fonte
1
Mas se você deseja adicionar metadados, por que não usar notas git, em vez de mexer com os cabeçalhos de confirmações? Veja stackoverflow.com/questions/5212957/… , stackoverflow.com/questions/7298749/… ou stackoverflow.com/questions/7101158/…
VonC
Para ser sincero, eu não sabia que existiam notas git quando escrevi isso. Sim, usar notas do git para conseguir a mesma coisa é provavelmente uma idéia melhor.
Pajp
3

Eu lido com o mesmo problema ( pipeline de várias filiais da Jenkins ) - tendo apenas confirmado informações e tentando encontrar um nome de filial de onde esse commit veio originalmente. Ele deve funcionar para ramificações remotas, cópias locais não estão disponíveis.

É com isso que trabalho:

git rev-parse HEAD | xargs git name-rev

Opcionalmente, você pode retirar a saída:

git rev-parse HEAD | xargs git name-rev | cut -d' ' -f2 | sed 's/remotes\/origin\///g'
miró
fonte
0

Eu acho que alguém deve enfrentar o mesmo problema que não pode descobrir o ramo, embora ele exista realmente em um ramo.

É melhor você puxar tudo primeiro:

git pull --all

Em seguida, faça a pesquisa de ramificação:

git name-rev <SHA>

ou:

git branch --contains <SHA>
Yates Zhou
fonte
0

Se o OP estiver tentando determinar o histórico que foi percorrido por uma ramificação quando uma confirmação específica foi criada ("descubra de que ramificação uma submissão é fornecida devido ao seu valor de hash SHA-1"), sem o reflog, não haverá nenhum registros no banco de dados de objetos Git que mostra qual ramificação nomeada estava vinculada a qual histórico de confirmação.

(Postei isso como resposta em resposta a um comentário.)

Felizmente, este script ilustra meu argumento:

rm -rf /tmp/r1 /tmp/r2; mkdir /tmp/r1; cd /tmp/r1
git init; git config user.name n; git config user.email [email protected]
git commit -m"empty" --allow-empty; git branch -m b1; git branch b2
git checkout b1; touch f1; git add f1; git commit -m"Add f1"
git checkout b2; touch f2; git add f2; git commit -m"Add f2"
git merge -m"merge branches" b1; git checkout b1; git merge b2
git clone /tmp/r1 /tmp/r2; cd /tmp/r2; git fetch origin b2:b2
set -x;
cd /tmp/r1; git log --oneline --graph --decorate; git reflog b1; git reflog b2;
cd /tmp/r2; git log --oneline --graph --decorate; git reflog b1; git reflog b2;

A saída mostra a falta de alguma maneira de saber se o commit com 'Add f1' veio da ramificação b1 ou b2 do clone remoto / tmp / r2.

(Últimas linhas da saída aqui)

+ cd /tmp/r1
+ git log --oneline --graph --decorate
*   f0c707d (HEAD, b2, b1) merge branches
|\
| * 086c9ce Add f1
* | 80c10e5 Add f2
|/
* 18feb84 empty
+ git reflog b1
f0c707d b1@{0}: merge b2: Fast-forward
086c9ce b1@{1}: commit: Add f1
18feb84 b1@{2}: Branch: renamed refs/heads/master to refs/heads/b1
18feb84 b1@{3}: commit (initial): empty
+ git reflog b2
f0c707d b2@{0}: merge b1: Merge made by the 'recursive' strategy.
80c10e5 b2@{1}: commit: Add f2
18feb84 b2@{2}: branch: Created from b1
+ cd /tmp/r2
+ git log --oneline --graph --decorate
*   f0c707d (HEAD, origin/b2, origin/b1, origin/HEAD, b2, b1) merge branches
|\
| * 086c9ce Add f1
* | 80c10e5 Add f2
|/
* 18feb84 empty
+ git reflog b1
f0c707d b1@{0}: clone: from /tmp/r1
+ git reflog b2
f0c707d b2@{0}: fetch origin b2:b2: storing head
qneill
fonte
E qual é a saída git log 80c10e5..HEAD --ancestry-path --merges --oneline --color | tail -n 1e os git log 086c9ce..HEAD --ancestry-path --merges --oneline --color | tail -n 1comandos para os dois casos?
Eugen Konkov
É claro que os SHAs mudam sempre que os comandos são executados. Para respeitar seus comandos, usei HEAD ^ 1 e HEAD ^ 2 em uma nova execução. Os comandos e a saída são: o $ git log HEAD^1..HEAD --ancestry-path --merges --oneline --color | tail -n 1que produz 376142d merge branchese o $ git log HEAD^2..HEAD --ancestry-path --merges --oneline --color | tail -n 1que produz 376142d merge branches - o que mostra o resumo da consolidação de mesclagem, que (como eu estava afirmando) pode ser sobrescrito quando a mesclagem é criada, possivelmente ofuscando o histórico de ramificação da mesclagem.
21418 Qneill
0

TL; DR:

Use o abaixo se você se preocupa com os status de saída do shell:

  • branch-current - o nome do ramo atual
  • branch-names - nomes de filiais limpos (um por linha)
  • branch-name - Verifique se apenas uma ramificação é retornada de branch-names

Ambos branch-namee branch-namesaceitar um commit como argumento, e padrão para HEADse nenhum for dado.


Aliases úteis em scripts

branch-current = "symbolic-ref --short HEAD"  # https://stackoverflow.com/a/19585361/5353461
branch-names = !"[ -z \"$1\" ] && git branch-current 2>/dev/null || git branch --format='%(refname:short)' --contains \"${1:-HEAD}\" #"  # https://stackoverflow.com/a/19585361/5353461
branch-name = !"br=$(git branch-names \"$1\") && case \"$br\" in *$'\\n'*) printf \"Multiple branches:\\n%s\" \"$br\">&2; exit 1;; esac; echo \"$br\" #"

Confirmar apenas acessível a partir de apenas uma ramificação

% git branch-name eae13ea
master
% echo $?
0
  • A saída é para STDOUT
  • O valor de saída é 0.

Confirmar alcançável a partir de várias ramificações

% git branch-name 4bc6188
Multiple branches:
attempt-extract
master%
% echo $?
1
  • A saída é para STDERR
  • O valor da saída é 1.

Devido ao status de saída, eles podem ser construídos com segurança. Por exemplo, para usar o controle remoto para buscar:

remote-fetch = !"branch=$(git branch-name \"$1\") && git config branch.\"$branch\".remote || echo origin #"
Tom Hale
fonte
-1

Para encontrar a filial local:

grep -lR YOUR_COMMIT .git/refs/heads | sed 's/.git\/refs\/heads\///g'

Para encontrar a ramificação remota:

grep -lR $commit .git/refs/remotes | sed 's/.git\/refs\/remotes\///g'
Gary Lyn
fonte
1
algumas explicações de seus exemplos será grande
Eugen Konkov
-4

Além de pesquisar por toda a árvore até encontrar um hash correspondente, não.

Âmbar
fonte
7
Eu realmente não quero rebaixar isso, porque, estritamente falando, é verdade que o git não armazena permanentemente essas informações, mas é muito possível descobrir isso. Veja minha resposta.
quer