Por que as confirmações do git não contêm o nome da ramificação em que foram criadas?

20

Ao trabalhar com o git em uma equipe usando ramificações de recursos, muitas vezes acho difícil entender a estrutura da ramificação no histórico.

Exemplo:

Digamos que havia um recurso de ramificação de recurso / café , e a correção de erros continuou no mestre em paralelo ao ramo de recurso.

A história pode ficar assim:

*     merge feature/make-coffee
|\
| *   small bugfix
| |
* |    fix bug #1234
| |
| *    add milk and sugar
| |
* |    improve comments
| |
* |    fix bug #9434
| |
| *    make coffe (without milk or sugar)
| |
* |    improve comments
|/
*

Problema

À primeira vista, acho difícil dizer de que lado é o ramo do recurso. Normalmente, preciso procurar vários comentários de ambos os lados para ter uma idéia de qual é qual. Isso fica mais complicado se houver várias ramificações de recurso em paralelo (principalmente se forem para recursos intimamente relacionados) ou se houver uma fusão nas duas direções entre a ramificação de característica e o mestre.

Por outro lado, no Subversion, isso é significativamente mais fácil, porque o nome do ramo faz parte da história - para que eu possa dizer imediatamente que um commit foi originalmente feito em "feature / make-coffee".

O Git pode facilitar isso ao incluir o nome da ramificação atual nos metadados de confirmação ao criar uma confirmação (junto com autor, data etc.). No entanto, o git não faz isso.

Existe alguma razão fundamental para que isso não seja feito? Ou será que ninguém queria o recurso? Se for o último, existem outras maneiras de entender o propósito dos ramos históricos sem ver o nome?

sleske
fonte
1
Pergunta relacionada: Localizando de qual ramificação um commit veio . É sobre como encontrar essas informações, apesar do git não registrá-las explicitamente.
sleske
1
Note to self: Curiosamente, em Mercurial a cometer não conter o nome da ramificação. Parece que os desenvolvedores do Mercurial tomaram uma decisão de design diferente do que os desenvolvedores do git. Veja, por exemplo, felipec.wordpress.com/2012/05/26/… para obter detalhes.
sleske

Respostas:

14

Provavelmente porque os nomes das ramificações são significativos apenas em um repositório. Se eu estiver na make-coffeeequipe e enviar todas as minhas alterações por meio de um gatekeeper, meu masterramo poderá ser puxado para o make-coffee-guiramo do gatekeeper , que ele mesclará com o make-coffee-backendramo, antes de mudar para um make-coffeeramo que será mesclado no masterramo central .

Mesmo em um repositório, os nomes das filiais podem mudar e mudar à medida que seu fluxo de trabalho evolui. masterpode mudar mais tarde para ser chamado development, por exemplo.

Como o CodeGnome aludiu, o git tem uma forte filosofia de design de não inserir algo nos dados, se não for necessário. Espera-se que os usuários usem opções git loge git diffproduzam os dados ao seu gosto após o fato. Eu recomendo experimentar alguns até encontrar um formato que funcione melhor para você. git log --first-parent, por exemplo, não mostrará as confirmações de uma filial que está sendo mesclada.

Karl Bielefeldt
fonte
2
Note-se, porém, que o primeiro pai geralmente é o errado. Porque, geralmente, alguém apenas coloca o mestre no que quer que esteja trabalhando e faz com que o trabalho seja o primeiro pai e o segundo.
Jan Hudec
1
@JanHudec: Na verdade, isso depende :-). Se a pessoa que faz o trabalho o mescla, então sim. Se a mesclagem ocorrer mais tarde, talvez após uma revisão, é mais provável que o revisor tenha feito o check-out principal e mesclado o ramo do recurso no master, testado e enviado por push.
sleske
5

Acompanhe o histórico de filiais com --no-ff

No Git, um commit tem ancestrais, mas um "branch" é realmente apenas o chefe atual de alguma linha de desenvolvimento. Em outras palavras, uma confirmação é uma captura instantânea da árvore de trabalho em algum momento e pode pertencer a qualquer número de ramificações de uma só vez. Isso faz parte do que torna a ramificação do Git tão leve quando comparada a outros DVCSes.

As confirmações do Git não carregam informações de ramificação porque não são necessárias para o histórico do Git. No entanto, mesclagens que não são de avanço rápido certamente podem levar informações adicionais sobre qual ramificação foi mesclada. Você pode garantir que seu histórico contenha essas informações sempre passando o --no-ffsinalizador para suas mesclagens. O git-merge (1) diz:

   --no-ff
       Create a merge commit even when the merge resolves as a
       fast-forward. This is the default behaviour when merging an
       annotated (and possibly signed) tag.

Isso geralmente criará uma mensagem de mesclagem semelhante a Merge branch 'foo', para que você possa normalmente encontrar informações sobre "ramificações ancestrais" com uma linha semelhante à seguinte:

$ git log --regexp-ignore-case --grep 'merge branch'
CodeGnome
fonte
Obrigado, mas isso (principalmente) não responde à minha pergunta. Não estou perguntando de que outras maneiras existem para encontrar a ramificação original, mas por que as informações não são incluídas como metadados regulares - é mais uma questão de design.
sleske
1
@sleske Acho que minha resposta foi responsiva: o Git não rastreia porque o histórico do Git não precisa; Não acho que seja mais fundamental que isso. Você precisaria procurar na fonte detalhes específicos, mas o histórico é efetivamente calculado ao contrário a partir da ponta da ramificação atual. Você sempre pode comparar isso com outros DVCS que tratam ramificações como objetos de primeira classe e ver se você gosta mais dessa filosofia de design. YMMV.
precisa saber é o seguinte
1
Esse último deveria ser git log --merges.
Dan
3

A resposta mais simples é que os nomes dos ramos são efêmeros. E se você fosse, digamos, renomear um ramo ( git branch -m <oldname> <newname>)? O que aconteceria com todos os commits contra esse ramo? Ou, se duas pessoas tiverem ramificações com o mesmo nome em repositórios locais diferentes?

A única coisa que tem significado no git é a soma de verificação do próprio commit. Esta é a unidade básica de rastreamento.

A informação está lá, você simplesmente não está pedindo, e não está exatamente lá.

O Git armazena a soma de verificação da cabeça de cada ramificação em .git/refs/heads. Por exemplo:

... /. git / refs / cabeças teste $ ls 
-rw-rw-r-- 1 michealt michealt 41 set 30 11:50 teste
... /. git / refs / heads $ cat teste 
87111111111111111111111111111111111111d4

(sim, estou usando minhas somas de verificação git, não é especial, mas são repositórios particulares que estou vendo no momento em que não precisam ter nada acidentalmente vazado).

Observando isso e rastreando todos os commit que têm isso como pai, ou os pais pai ou pai pais ... você pode descobrir em quais ramos um determinado commit está. É apenas um grande gráfico para renderizar.

Armazenar onde especificamente o nome de uma ramificação (que pode ser alterada conforme mencionado acima) uma confirmação está no próprio envio é uma sobrecarga extra na confirmação que não ajuda. Armazene o (s) pai (s) da confirmação e seu bem.

Você pode ver as ramificações executando o comando git log com o sinalizador apropriado.

git log --branches --source --pretty = oneline --graph
git log --all --source --pretty = oneline --graph

Existem muitas maneiras diferentes de escolher o que você deseja para isso - veja a documentação do log do git - a -allseção e as poucas opções a seguir.

* 871111111111111111111111111111111111d4 test Mesclar ramificação 'test1' no teste
| \  
| * 42111111111111111111111111111111111111e8 atualização de teste1: adicionar itens
* dd111111111111111111111111111111111159 test Mesclar ramificação 'test3' no teste
| \ \  

Esse 'teste', 'teste1' e 'teste2' são os ramos com os quais eles foram comprometidos.

Observe que a documentação do git log é enorme e é possível obter quase tudo o que você deseja.


fonte
3

Por que as confirmações do git não contêm o nome da ramificação em que foram criadas?

Apenas uma decisão de design. Se você precisar dessas informações, adicione um gancho prepare-commit-msg ou commit-msg para aplicá-lo em um único repositório.

Após o desastre do BitKeeper, Linus Torvalds desenvolveu o git para corresponder ao processo de desenvolvimento do kernel do Linux. Lá, até que um patch seja aceito, ele é revisado, editado, polido várias vezes (por todo o Mail), desconectado (= editando um commit), escolhido pela cereja, vagando pelos ramos do tenente, talvez muitas vezes reformulado, mais edições, cereja -picaretas e assim por diante até que finalmente sejam fundidas a montante por Linus.

Nesse fluxo de trabalho, pode não haver origem específica de uma confirmação, e geralmente uma confirmação é criada em alguma ramificação de depuração temporária em algum repositório aleatório privado sem importância com nomes de ramificações engraçados, à medida que o git é distribuído.

Talvez essa decisão de design tenha acontecido por coincidência, mas corresponde exatamente ao processo de desenvolvimento do kernel Linux.

villekulla
fonte
2

Como no Git, os commit como "fazer café" são considerados parte de ambos os ramos quando o ramo do recurso é mesclado. Existe até um comando para verificar isso:

% git branch --contains @
  feature/make-coffee
  master

Além disso, os ramos são baratos, locais e etéreos; eles podem ser facilmente adicionados, removidos, renomeados, enviados por push e excluídos do servidor.

O Git segue o princípio de que tudo pode ser feito, motivo pelo qual você pode remover facilmente uma ramificação de um servidor remoto e reescrever o histórico com facilidade.

Digamos que eu estava no ramo 'mestre' e fiz dois commits: 'faça café' e 'adicione leite e açúcar'; então, decido usar um novo ramo para isso, então redefini 'mestre' de volta para 'origem /mestre'. Não é um problema, toda a história ainda está limpa: 'fazer café' e 'adicionar leite e açúcar' não fazem parte do mestre, apenas recurso / fazer café.

Mercurial é o oposto; não quer mudar as coisas. As ramificações são permanentes, o histórico de reescritas é desaprovado, você não pode simplesmente excluir uma ramificação do servidor.

Resumindo: o Git permite que você faça tudo, o Mercurial quer facilitar as coisas (e torna realmente difícil deixá-lo fazer o que deseja).

FelipeC
fonte