Log Git para obter confirmações apenas para uma ramificação específica


214

Quero listar todos os commits que são apenas parte de um ramo específico.

Com o seguinte, ele lista todos os commits da ramificação, mas também do pai (mestre)

git log mybranch

A outra opção que encontrei foi excluir os commits acessíveis pelo mestre e me fornecer o que quero, mas gostaria de evitar a necessidade de conhecer os nomes dos outros ramos.

git log mybranch --not master

Eu estava tentando usar git for-each-ref, mas ele também está listando mybranch e, na verdade, está excluindo tudo:

git log mybranch --not $(git for-each-ref --format '^%(refname:short)' refs/heads/)

Atualizar:

Estou testando uma nova opção que encontrei há algum tempo e, até agora, parece que isso poderia ser o que eu estava procurando:

git log --walk-reflogs mybranch

Atualização (2013-02-13T15: 08):

A opção --walk-reflogs é boa, mas verifiquei se há uma expiração para os reflogs (padrão: 90 dias, gc.reflogExpire ).

Acho que encontrei a resposta que estava procurando:

git log mybranch --not $(git for-each-ref --format='%(refname)' refs/heads/ | grep -v "refs/heads/mybranch")

Estou apenas removendo a ramificação atual da lista de ramificações disponíveis e usando essa lista para ser excluída do log. Dessa forma, só recebo os commits que são alcançados apenas pelo mybranch .



Já vi essa pergunta também, mas não o mesmo
dimirc

2
Tem certeza de que é isso que você quer? Acho melhor ter um objetivo em mente: "o que está no meu ramo que não está no upstream" ou "o que está no meu ramo que não está no mestre". Além disso, enquanto o git é rápido na poda, isso fica mais caro à medida que você tem mais filiais. Eu tenho um script que uso que chamo de "git missing" após o comando "missing" do bzr. Você pode encontrá-lo aqui: github.com/jszakmeister/etc/blob/master/git-addons/git-missing .
John Szakmeister 13/02/2013

1
Eu realmente preciso disto para um gancho post-receive, então "mestre" não vai ser sempre o ramo de excluir
dimirc

Sim, o duplicado mencionado por StarPinkER funcionou bem para mim: git log $ (git merge-base da cabeça de ramo) .. ramo
charo

Respostas:


165

Pelo que parece, você deve estar usando cherry:

git cherry -v develop mybranch

Isso mostraria todos os commits que estão contidos no mybranch , mas NÃO em desenvolvimento . Se você deixar de fora a última opção ( mybranch ), ela comparará a ramificação atual.

Como VonC apontou, você SEMPRE está comparando sua ramificação com outra ramificação, portanto, conheça suas ramificações e escolha qual delas comparar.


4
Era exatamente isso que eu precisava, mas parece que deveria haver um comando mais intuitivo (sim, mesmo no mundo do Git) para isso.
Seth

12
Você também pode git cherry -v mastercomparar sua ramificação atual com a ramificação mestre.
Pithikos

2
O perigo de usar git cherry é que as confirmações são correspondidas apenas se as diferenças de arquivos forem idênticas entre as ramificações. Se algum tipo de mesclagem foi feito, o que tornaria as diferenças diferentes entre um ramo e o outro, então o git cherry as vê como confirmações diferentes.
Ben

Isso só me dá fatal: Unknown commit mybranch.
Matt Arnold

1
@MattArnold você tem que mudar o texto "desenvolver" e "mybranch" para ramos que existem no seu repo
Smilie

40

MAS eu gostaria de evitar a necessidade de conhecer os nomes dos outros ramos.

Eu não acho que isso seja possível: uma ramificação no Git é sempre baseada em outra ou pelo menos em outra confirmação, conforme explicado em " git diff não mostra o suficiente ":

insira a descrição da imagem aqui

Você precisa de um ponto de referência para o seu log mostrar as confirmações corretas.

Conforme mencionado em " GIT - De onde eu ramifiquei? ":

ramificações são simplesmente indicadores de certas confirmações em um DAG

Assim, mesmo se git log master..mybranché uma resposta, seria ainda mostrar muitos commits, se mybranchbaseia-se myotherbranch, ela própria baseada em master.

Para encontrar essa referência (a origem do seu ramo), você só pode analisar confirmações e ver em qual filial elas estão, conforme visto em:


26

Finalmente encontrei o caminho para fazer o que o OP queria. É tão simples quanto:

git log --graph [branchname]

O comando exibirá todas as confirmações alcançáveis ​​a partir da ramificação fornecida no formato de gráfico. Porém, você pode filtrar facilmente todas as confirmações nesse ramo, observando o gráfico de confirmações cujo *é o primeiro caractere na linha de confirmação.

Por exemplo, vejamos o trecho do git log --graph masterrepositório cakephp do GitHub abaixo:

D:\Web Folder\cakephp>git log --graph master
*   commit 8314c2ff833280bbc7102cb6d4fcf62240cd3ac4
|\  Merge: c3f45e8 0459a35
| | Author: José Lorenzo Rodríguez <lorenzo@users.noreply.github.com>
| | Date:   Tue Aug 30 08:01:59 2016 +0200
| |
| |     Merge pull request #9367 from cakephp/fewer-allocations
| |
| |     Do fewer allocations for simple default values.
| |
| * commit 0459a35689fec80bd8dca41e31d244a126d9e15e
| | Author: Mark Story <mark@mark-story.com>
| | Date:   Mon Aug 29 22:21:16 2016 -0400
| |
| |     The action should only be defaulted when there are no patterns
| |
| |     Only default the action name when there is no default & no pattern
| |     defined.
| |
| * commit 80c123b9dbd1c1b3301ec1270adc6c07824aeb5c
| | Author: Mark Story <mark@mark-story.com>
| | Date:   Sun Aug 28 22:35:20 2016 -0400
| |
| |     Do fewer allocations for simple default values.
| |
| |     Don't allocate arrays when we are only assigning a single array key
| |     value.
| |
* |   commit c3f45e811e4b49fe27624b57c3eb8f4721a4323b
|\ \  Merge: 10e5734 43178fd
| |/  Author: Mark Story <mark@mark-story.com>
|/|   Date:   Mon Aug 29 22:15:30 2016 -0400
| |
| |       Merge pull request #9322 from cakephp/add-email-assertions
| |
| |       Add email assertions trait
| |
| * commit 43178fd55d7ef9a42706279fa275bb783063cf34
| | Author: Jad Bitar <jadbitar@mac.com>
| | Date:   Mon Aug 29 17:43:29 2016 -0400
| |
| |     Fix `@since` in new files docblocks
| |

Como você pode ver, apenas confirma 8314c2ff833280bbc7102cb6d4fcf62240cd3ac4e c3f45e811e4b49fe27624b57c3eb8f4721a4323btem *o primeiro caractere nas linhas de confirmação. Esses commits são da ramificação principal, enquanto os outros quatro são de outras ramificações.


11

O seguinte comando shell deve fazer o que você deseja:

git log --all --not $(git rev-list --no-walk --exclude=refs/heads/mybranch --all)

Ressalvas

Se você mybranchfez o check-out, o comando acima não funcionará. Isso porque os commits on mybranchtambém são acessíveis HEAD, então o Git não considera os commits exclusivos mybranch. Para que ele funcione quando o mybranchcheck-out é feito, você também deve adicionar uma exclusão para HEAD:

git log --all --not $(git rev-list --no-walk \
    --exclude=refs/heads/mybranch \
    --exclude=HEAD \
    --all)

No entanto, você não deve excluir a HEADmenos que o mybranchcheck-out esteja marcado, caso contrário corre o risco de mostrar confirmações que não são exclusivas mybranch.

Da mesma forma, se você tiver uma ramificação remota chamada origin/mybranchque corresponda à mybranchramificação local , será necessário excluí-la:

git log --all --not $(git rev-list --no-walk \
    --exclude=refs/heads/mybranch \
    --exclude=refs/remotes/origin/mybranch \
    --all)

E se a ramificação remota for a ramificação padrão para o repositório remoto (geralmente apenas verdadeiro origin/master), você também terá que excluir origin/HEAD:

git log --all --not $(git rev-list --no-walk \
    --exclude=refs/heads/mybranch \
    --exclude=refs/remotes/origin/mybranch \
    --exclude=refs/remotes/origin/HEAD \
    --all)

Se você tiver feito o check-out da ramificação, e houver uma ramificação remota, e a ramificação remota for o padrão para o repositório remoto, você acaba excluindo muito:

git log --all --not $(git rev-list --no-walk \
    --exclude=refs/heads/mybranch \
    --exclude=HEAD
    --exclude=refs/remotes/origin/mybranch \
    --exclude=refs/remotes/origin/HEAD \
    --all)

Explicação

O git rev-listcomando é um comando de nível inferior (encanamento) que percorre as revisões fornecidas e despeja os identificadores SHA1 encontrados. Pense nisso como equivalente a, git logexceto que mostra apenas o SHA1 - nenhuma mensagem de log, nenhum nome de autor, nenhum registro de data e hora, nada disso "sofisticado".

A --no-walkopção, como o nome indica, impede git rev-listde percorrer a cadeia ancestral. Portanto, se você digitar git rev-list --no-walk mybranch, imprimirá apenas um identificador SHA1: o identificador da confirmação de ponta da mybranchramificação.

Os --exclude=refs/heads/mybranch --allargumentos dizem git rev-listpara começar a partir de cada referência, exceto refs/heads/mybranch.

Portanto, quando você executa git rev-list --no-walk --exclude=refs/heads/mybranch --all, o Git imprime o identificador SHA1 da confirmação da dica de cada referência, exceto refs/heads/mybranch. Esses commits e seus ancestrais são os commits nos quais você não está interessado - esses são os commits que você não deseja ver.

Os outros commits são os que você deseja ver, então coletamos a saída git rev-list --no-walk --exclude=refs/heads/mybranch --alle pedimos ao Git para mostrar tudo, exceto esses commits e seus ancestrais.

O --no-walkargumento é necessário para repositórios grandes (e é uma otimização para repositórios pequenos): sem ele, o Git precisaria imprimir e o shell precisaria coletar (e armazenar na memória) muito mais identificadores de confirmação do que o necessário. Com um repositório grande, o número de confirmações coletadas pode facilmente exceder o limite de argumentos da linha de comando do shell.

Bug do Git?

Eu esperava que o seguinte funcionasse:

git log --all --not --exclude=refs/heads/mybranch --all

mas isso não acontece. Acho que isso é um bug no Git, mas talvez seja intencional.


Boa explicação. +1
VonC

5
Eu vim aqui querendo saber como fazer Mercurial's hg log -b <branch>. Não entendo por que as pessoas dizem que o git é hostil. / s
weberc2

Não sei por git log --all --not --exclude=refs/heads/mybranch --allque não funciona, mas git log refs/heads/mybranch --not --exclude=refs/heads/mybranch --allfunciona, com as mesmas ressalvas em excluir HEAD e origem.
Ben C

8

Resposta rápida:

git log $(git merge-base master b2)..HEAD

Digamos:

  1. Que você tem um ramo mestre

  2. Faça algumas confirmações

  3. Você criou um ramo chamado b2

  4. Faça git log -n1; o ID de confirmação é a base de mesclagem entre b2 e master

  5. Faça alguns commits em b2

  6. git log mostrará seu histórico de log de b2 e master

  7. Use o intervalo de confirmação, se você não estiver familiarizado com o conceito, convido você a pesquisar no Google ou empilhar o estouro,

    Para o seu contexto real, você pode fazer, por exemplo

    git log commitID_FOO..comitID_BAR
    

    O ".." é o operador de intervalo para o comando log.

    Isso significa que, de uma forma simples, me dê todos os logs mais recentes que o commitID_FOO ...

  8. Veja o ponto 4, a base de mesclagem

    Então: git log COMMITID_mergeBASE..HEADmostrará a diferença

  9. O Git pode recuperar a base de mesclagem para você assim

    git merge-base b2 master
    
  10. Finalmente você pode fazer:

    git log $(git merge-base master b2)..HEAD
    

4

Você pode tentar algo como isto:

#!/bin/bash

all_but()
{
    target="$(git rev-parse $1)"
    echo "$target --not"
    git for-each-ref --shell --format="ref=%(refname)" refs/heads | \
    while read entry
    do
        eval "$entry"

        test "$ref" != "$target" && echo "$ref"
    done
}

git log $(all_but $1)

Ou, tomando emprestado da receita no Manual do Usuário do Git :

#!/bin/bash
git log $1 --not $( git show-ref --heads | cut -d' ' -f2 | grep -v "^$1" )

+1. Eu estava escrevendo na minha resposta que você precisa analisar os commits para encontrar a origem do seu ramo, e você parece ter feito exatamente isso.
VonC

Você já usou log --walk-reflogs? Estou lendo sobre esta opção e é está me dando os resultados que eu preciso até agora, (ainda testando)
dimirc

Os reflogs do @dimirc são todos diferentes. Ele registra pontos de ramificação ao longo do tempo, geralmente para fins de recuperação. Não sei ao certo o que você está fazendo no gancho pós-recebimento, mas talvez, se o explicasse, as pessoas pudessem fornecer uma resposta mais sensata ao seu problema.
John Szakmeister 13/02/2013

Eu só preciso analisar / verificar todas as mensagens de confirmação de uma só vez. O caso que eu quero abordar é se alguém enviar várias confirmações no mestre e criar uma nova ramificação com várias confirmações também. O gancho pós-recebimento precisa verificar as alterações feitas para refs / branches, por exemplo, master e newbranch, preciso conhecer os limites do novo branch para evitar a análise das mensagens de confirmação duas vezes, pois elas podem ser confirmadas pelo master.
dimirc

1
OK. Então acho que a abordagem acima que descrevi realmente é o que você deseja. Com o reflog, as entradas caem após um certo período de tempo, e acho que isso causará algumas dores de cabeça. Você pode querer usar git show-ref --tagstambém.
John Szakmeister

4
git rev-list --exclude=master --branches --no-walk

listará as dicas de cada ramo que não é master.

git rev-list master --not $(git rev-list --exclude=master --branches --no-walk)

listará todos os commit no masterhistórico que não estão no histórico de nenhum outro ramo.

O seqüenciamento é importante para as opções que configuram o pipeline de filtro para a seleção de confirmação, portanto --branches, deve seguir todos os padrões de exclusão que ele deve aplicar e --no-walkseguir os filtros que fornecem a lista de revisão de commits que não devem andar.


4

Isso produzirá as confirmações na ramificação atual. Se qualquer argumento for passado, ele apenas gera os hashes.

git_show_all_commits_only_on_this_branch

#!/bin/bash
function show_help()
{
  ME=$(basename $0)
  IT=$(cat <<EOF
  
  usage: $ME {NEWER_BRANCH} {OLDER_BRANCH} {VERBOSE}
  
  Compares 2 different branches, and lists the commits found only 
  in the first branch (newest branch). 

  e.g. 
  
  $ME         -> default. compares current branch to master
  $ME B1      -> compares branch B1 to master
  $ME B1 B2   -> compares branch B1 to B2
  $ME B1 B2 V -> compares branch B1 to B2, and displays commit messages
  
  )
  echo "$IT"
  exit
}

if [ "$1" == "help" ]
then
  show_help
fi

# Show commit msgs if any arg passed for arg 3
if [ "$3" ]
then
  OPT="-v"
fi

# get branch names
OLDER_BRANCH=${2:-"master"}
if [ -z "$1" ]
then
  NEWER_BRANCH=$(git rev-parse --abbrev-ref HEAD)
else
  NEWER_BRANCH=$1
fi

if [ "$NEWER_BRANCH" == "$OLDER_BRANCH" ]
then
  echo "  Please supply 2 different branches to compare!"
  show_help
fi

OUT=$(\git cherry $OPT $OLDER_BRANCH $NEWER_BRANCH)

if [ -z "$OUT" ]
then
  echo "No differences found. The branches $NEWER_BRANCH and $OLDER_BRANCH are in sync."
  exit;
fi

if [ "$OPT" == "-v" ]
then
  echo "$OUT"
else
  echo "$OUT" | awk '{print $2}'
fi

1
Isso produziu cerca de 100 mensagens de log irrelevantes para mim.
Arlie Stephens

Hey @ArlieStephens - Eu tentei e ainda parecia funcionar para mim? Acabei de adicionar ajuda e mais mensagens de erro ao acima. Deixe-me saber como vai!
Brad Parks

Interessante. Olhando para o código como você o possui agora, acho que pode ser tão simples quanto o original, assumindo que o ramo com o qual me importo saiu do master. Com o código atual, eu poderia ter especificado o ramo pai explicitamente. (IIRC, meu ramo dev veio de um ramo release, não senhor.)
Arlie Stephens

@ArlieStephens - sim, acho que não mudei nenhuma funcionalidade .... Atualizei-a mais uma vez e tornei os documentos um pouco mais precisos após alguns testes locais ... e adicionei uma opção "detalhada" que estava lá antes, mas eu não doc-lo
Brad Parks

3

Estou usando os seguintes comandos:

git shortlog --no-merges --graph --abbrev-commit master..<mybranch>

ou

git log --no-merges --graph --oneline --decorate master..<mybranch>

1

Achei essa abordagem relativamente fácil.

Saída para a agência e depois

  1. Corre

    git rev-list --simplify-by-decoration -2 HEAD
    

Isso fornecerá apenas dois SHAs:

1) último commit do ramo [C1]

2) e confirmar o pai no primeiro commit do ramo [C2]

  1. Agora corra

    git log --decorate --pretty=oneline --reverse --name-status <C2>..<C1>
    

Aqui C1 e C2 são duas strings que você obterá quando executar o primeiro comando. Coloque esses valores sem <> no segundo comando.

Isso fornecerá uma lista do histórico de alterações de arquivos dentro da ramificação.


Se você ler as três primeiras linhas da questão, você verá que as listas de autor este comando exato e explica por que não é o que eles estão procurando
black_fm

Ah @black_fm eu fiz algumas mudanças na resposta. Você pode votar se achar útil agora. :)
Mustkeem K

0

Na minha situação, estamos usando o Git Flow e o GitHub. Tudo o que você precisa fazer é: Compare sua ramificação de recursos com a ramificação de desenvolvimento no GitHub.

Ele mostrará as confirmações feitas apenas no ramo de recursos.

Por exemplo:

https://github.com/your_repo/compare/develop...feature_branch_name


Concordo que, quando você entra na situação "Quero ver como será uma solicitação pull", a melhor solução é ignorar gite usar o GitHub para fazer uma solicitação pull ou comparar as ramificações.
Pkamb # 12/18
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.