Esta não é exatamente uma resposta real, mas preciso acessar a formatação e muito espaço. Tentarei descrever a teoria por trás do que considero as duas melhores respostas: a aceita e a (pelo menos atualmente) melhor colocada . Mas, na verdade, eles respondem a perguntas diferentes .
Commits no Git estão frequentemente "em" mais de um branch por vez. Na verdade, é disso que se trata. Dado:
...--F--G--H <-- master
\
I--J <-- develop
onde as letras maiúsculas representam os hash IDs reais do Git, geralmente procuramos apenas commitH ou apenas commitsI-J em nossa git logsaída. Os commits até Gestão em ambas as ramificações, portanto, gostaríamos de excluí-los.
(Observe que em gráficos desenhados assim, os commits mais recentes estão à direita. Os nomes selecionam o único commit mais à direita nessa linha. Cada um desses commits tem um commit pai, que é o commit à sua esquerda: o pai de Hé G, e o pai de Jé I. O pai de Ié Gnovamente. O pai de Gé Fe Ftem um pai que simplesmente não é mostrado aqui: faz parte da ...seção.)
Para este caso particularmente simples, podemos usar:
git log master..develop # note: two dots
para ver I-J, ou:
git log develop..master # note: two dots
para ver Hapenas. O nome do lado direito, após os dois pontos, diz ao Git: sim, esses commits . O nome do lado esquerdo, antes dos dois pontos, diz ao Git: não, não esses commits . Git começa no final - em commit Hou commit J- e funciona de trás para frente . Para (muito) mais sobre isso, consulte Think Like (a) Git .
Da forma como a pergunta original é formulada, o desejo é encontrar commits que sejam acessíveis a partir de um nome em particular, mas não de qualquer outro nome na mesma categoria geral. Ou seja, se tivermos um gráfico mais complexo:
O--P <-- name5
/
N <-- name4
/
...--F--G--H--I---M <-- name1
\ /
J-----K <-- name2
\
L <-- name3
poderíamos escolher um desses nomes, como name4ou name3, e perguntar: quais commits podem ser encontrados por esse nome, mas não por qualquer um dos outros nomes? Se escolhermos, name3a resposta é cometer L. Se escolhermos name4, a resposta é nenhum commit: o commit que name4names é commit Nmas commit Npode ser encontrado começando em name5e trabalhando para trás.
A resposta aceita funciona com nomes de rastreamento remoto, em vez de nomes de ramificações, e permite que você designe um - aquele escrito origin/merge-only- como o nome selecionado e observe todos os outros nomes naquele namespace. Também evita a exibição de mesclagens: se escolhermos name1como o "nome interessante" e dissermos mostre os commits que podem ser acessados, name1mas não com qualquer outro nome , veremos o commit de mesclagem Massim como o commit regular I.
A resposta mais popular é bem diferente. É tudo sobre percorrer o gráfico de commit sem seguir as duas pernas de uma mesclagem e sem mostrar nenhum dos commits que são mesclagens. Se começarmos com name1, por exemplo, não mostraremos M(é um merge), mas assumindo que o primeiro pai de merge Mé commit I, nem mesmo olharemos para commits Je K. Vamos acabar mostrando cometer I, e também se compromete H, G, F, e assim por diante-nenhuma delas é commits mesclagem e todos são acessíveis pela partir de Me trabalhando para trás, visitando apenas o primeiro pai de cada merge cometer.
A resposta mais popular é bastante adequada, por exemplo, para ver masterquando masterse destina a ser um branch somente de mesclagem. Se todo o "trabalho real" foi feito nas ramificações laterais que foram posteriormente mescladas master, teremos um padrão como este:
I---------M---------N <-- master
\ / \ /
o--o--o o--o--o
onde todos os ocommits un-letter-named são commits comuns (não-merge) e Me Nsão commits de mesclagem. Commit Ié o commit inicial: o primeiro commit já feito, e o único que deveria estar no master que não é um merge commit. Se o git log --first-parent --no-merges mastermostrar qualquer commit diferente de I , temos uma situação como esta:
I---------M----*----N <-- master
\ / \ /
o--o--o o--o--o
onde queremos ver o commit *feito diretamente master, não por meio da fusão de algum branch de recurso.
Resumindo, a resposta popular é ótima para saber masterquando masterdeve ser somente mesclagem, mas não é tão boa para outras situações. A resposta aceita funciona para essas outras situações.
Os nomes de rastreamento remoto são como nomes de origin/master agências ?
Algumas partes do Git dizem que não são:
git checkout master
...
git status
diz on branch master, mas:
git checkout origin/master
...
git status
diz HEAD detached at origin/master. Prefiro concordar com git checkout/ git switch: origin/masternão é um nome de branch porque você não pode "ativá-lo".
A resposta aceita usa nomes de rastreamento remoto origin/*como "nomes de agências":
git log --no-merges origin/merge-only \
--not $(git for-each-ref --format="%(refname)" refs/remotes/origin |
grep -Fv refs/remotes/origin/merge-only)
A linha do meio, que invoca git for-each-ref, itera sobre os nomes de rastreamento remoto para o remoto nomeado origin.
O motivo pelo qual esta é uma boa solução para o problema original é que estamos interessados aqui nos nomes de branch de outra pessoa , e não nos nomes de nosso branch. Mas isso significa que definimos branch como algo diferente de nossos nomes de branch . Tudo bem: apenas esteja ciente de que você está fazendo isso, quando fizer isso.
git log atravessa alguma (s) parte (s) do gráfico de confirmação
O que estamos realmente procurando aqui são séries do que chamei de daglets: veja o que exatamente queremos dizer com "galho"? Ou seja, estamos procurando fragmentos em algum subconjunto do gráfico de confirmação geral .
Sempre que fazemos o Git olhar para um nome de branch como master, um nome de tag como v2.1, ou um nome de rastreamento remoto como origin/master, tendemos a querer que o Git nos diga sobre esse commit e cada commit que podemos obter a partir desse commit: começando aí e trabalhando para trás.
Em matemática, isso é conhecido como gráfico de caminhada . O gráfico de commit do Git é um Directed Acyclic Graph ou DAG , e esse tipo de gráfico é particularmente adequado para caminhadas. Ao percorrer esse gráfico, visitaremos cada vértice do gráfico que pode ser alcançado através do caminho que está sendo usado. Os vértices no gráfico Git são os commits, com as arestas sendo arcos - links unilaterais - indo de cada filho para cada pai. (É aqui que entra o Think Like (a) Git . A natureza unilateral dos arcos significa que o Git deve trabalhar de trás para frente, do filho para o pai.)
Os dois comandos principais do Git para caminhar no gráfico são git loge git rev-list. Esses comandos são extremamente semelhantes - na verdade, eles são construídos principalmente a partir dos mesmos arquivos de origem - mas sua saída é diferente: git logproduz saída para humanos lerem, enquanto git rev-listproduz saída para outros programas Git lerem. 1 Ambos os comandos fazem esse tipo de caminhada no gráfico.
A caminhada no gráfico que eles fazem é especificamente: dado algum conjunto de commits de ponto de partida (talvez apenas um commit, talvez um monte de IDs de hash, talvez um monte de nomes que resolvem em IDs de hash), ande no gráfico, visitando commits . Diretivas específicas, como --notou um prefixo ^, ou --ancestry-path, ou --first-parent, modificam a caminhada do gráfico de alguma forma.
Enquanto fazem a caminhada do gráfico, eles visitam cada commit. Mas eles imprimem apenas alguns subconjuntos selecionados dos commits percorridos. Diretivas como --no-mergesou --before <date>informam o código do gráfico que se compromete a imprimir .
Para fazer esta visita, um commit de cada vez, esses dois comandos usam uma fila prioritária . Você executa git logou git rev-liste dá a ele alguns commits de ponto de partida. Eles colocam esses commits na fila de prioridade. Por exemplo, um simples:
git log master
transforma o nome masterem um ID de hash bruto e coloca esse ID de hash na fila. Ou:
git log master develop
transforma ambos os nomes em IDs de hash e - supondo que sejam dois IDs de hash diferentes - coloca ambos na fila.
A prioridade dos commits nesta fila é determinada por ainda mais argumentos. Por exemplo, o argumento --author-date-orderinforma git logou git rev-listpara usar o carimbo de data / hora do autor , em vez do carimbo de data / hora do committer. O padrão é usar o carimbo de data / hora do committer e escolher o commit mais recente por data: aquele com a data numérica mais alta. Assim master develop, assumindo que estes resolvam dois commits diferentes, o Git mostrará o que vier depois primeiro, porque estará na frente da fila.
Em qualquer caso, o código de passeio de revisão agora executa em um loop:
- Enquanto houver commits na fila:
- Remova a primeira entrada da fila.
- Decida se deseja imprimir este commit. Por exemplo
--no-merges: print nothing se for um commit de mesclagem; --before: não imprime nada se sua data não for anterior à hora designada. Se a impressão não for suprimida, imprima o commit: for git log, mostre seu log; para git rev-listimprimir seu ID de hash.
- Coloque alguns ou todos os commits pais deste commit na fila (contanto que não esteja lá agora, e ainda não tenha sido visitado 2 ). O padrão normal é incluir todos os pais. Usar
--first-parentsuprime tudo, exceto o primeiro pai de cada mesclagem.
(Ambos git loge git rev-listpodem fazer a simplificação do histórico com ou sem a reescrita dos pais neste ponto também, mas vamos pular isso aqui).
Para uma cadeia simples, como começar em HEADe trabalhar para trás quando não há commits de mesclagem, a fila sempre tem um commit no topo do loop. Há um commit, então nós o retiramos e imprimimos e colocamos seu (único) pai na fila e contornamos novamente, e seguimos a cadeia de trás para frente até chegarmos ao primeiro commit, ou o usuário se cansa da git logsaída e sai o programa. Nesse caso, nenhuma das opções de pedido importa: há apenas um commit para mostrar.
Quando há fusões e nós seguimos ambos os pais - ambas as "pernas" da fusão - ou quando você dá git logou git rev-listmais de um commit inicial, as opções de classificação são importantes.
Por último, considere o efeito de --notou ^na frente de um especificador de confirmação. Eles têm várias maneiras de escrevê-los:
git log master --not develop
ou:
git log ^develop master
ou:
git log develop..master
todos significam a mesma coisa. O --noté como o prefixo, ^exceto que se aplica a mais de um nome:
git log ^branch1 ^branch2 branch3
significa não branch1, não branch2, sim branch3; mas:
git log --not branch1 branch2 branch3
significa não branch1, não branch2, não branch3, e você tem que usar um segundo --notpara desligá-lo:
git log --not branch1 branch2 --not branch3
o que é um pouco estranho. As duas diretivas "não" são combinadas via XOR, então, se você realmente quiser, pode escrever:
git log --not branch1 branch2 ^branch3
para significar não branch1, não branch2, sim branch3 , se você quiser ofuscar .
Todos eles funcionam afetando a caminhada do gráfico. Conforme git logou git rev-listpercorre o gráfico, ele se certifica de não colocar na fila de prioridade qualquer confirmação que seja alcançável de qualquer uma das referências negadas . (Na verdade, eles afetam a configuração inicial também: commits negados não podem ir para a fila de prioridade diretamente da linha de comando, então git log master ^masternão mostra nada, por exemplo.)
Toda a sintaxe extravagante descrita na documentação do gitrevisions faz uso disso, e você pode expor isso com uma simples chamada para git rev-parse. Por exemplo:
$ git rev-parse origin/pu...origin/master # note: three dots
b34789c0b0d3b137f0bb516b417bd8d75e0cb306
fc307aa3771ece59e174157510c6db6f0d4b40ec
^b34789c0b0d3b137f0bb516b417bd8d75e0cb306
A sintaxe de três pontos significa commits acessíveis do lado esquerdo ou direito, mas excluindo commits alcançáveis de ambos . Neste caso, o próprio origin/mastercommit,, b34789c0bpode ser alcançado a partir de origin/pu( fc307aa37...), então o origin/masterhash aparece duas vezes, uma vez com uma negação, mas na verdade o Git atinge a sintaxe de três pontos colocando duas referências positivas - os dois IDs de hash não negados - e um negativo, representado pelo ^prefixo.
Simiarly:
$ git rev-parse master^^@
2c42fb76531f4565b5434e46102e6d85a0861738
2f0a093dd640e0dad0b261dae2427f2541b5426c
A ^@sintaxe significa todos os pais do commit fornecido , e master^ela mesma - o primeiro pai do commit selecionado por branch-name master- é um commit de mesclagem, então tem dois pais. Estes são os dois pais. E:
$ git rev-parse master^^!
0b07eecf6ed9334f09d6624732a4af2da03e38eb
^2c42fb76531f4565b5434e46102e6d85a0861738
^2f0a093dd640e0dad0b261dae2427f2541b5426c
O ^!sufixo significa o próprio compromisso, mas nenhum de seus pais . Nesse caso, master^é 0b07eecf6.... Já vimos ambos os pais com o ^@sufixo; aqui estão eles de novo, mas desta vez, negados.
1 Muitos programas Git literalmente rodam git rev-listcom várias opções, e lêem sua saída, para saber quais commits e / ou outros objetos Git usar.
2 Como o gráfico é acíclico , é possível garantir que nenhum já foi visitado, se adicionarmos a restrição nunca mostrar um pai antes de mostrar todos os seus filhos à prioridade. --date-order, --author-date-ordere --topo-orderadicione esta restrição. A ordem de classificação padrão - que não tem nome - não tem. Se os carimbos de data / hora de commits são complicados - se, por exemplo, alguns commits foram feitos "no futuro" por um computador cujo relógio estava desligado - isso poderia em alguns casos levar a uma saída de aparência estranha.
Se você chegou até aqui, agora sabe muito sobre git log
Resumo:
git log é sobre mostrar alguns commits selecionados enquanto percorre parte ou toda parte do gráfico.
- O
--no-mergesargumento, encontrado em ambas as respostas aceitas e atualmente no topo da classificação, suprime a exibição de alguns commits que são percorridos.
- O
--first-parentargumento, da resposta atualmente no topo da classificação, suprime a caminhada de algumas partes do gráfico, durante a própria caminhada do gráfico.
- O
--notprefixo para os argumentos da linha de comando, conforme usado na resposta aceita, elimina a visita a algumas partes do gráfico, desde o início.
Obtemos as respostas que gostamos, para duas perguntas diferentes, usando esses recursos.
git log ^branch1 ^branch2 merge-only-branchsintaxe?