Como os enxertos e substitutos do git diferem? (Os enxertos agora estão obsoletos?)

Há muito poucos Q & A em grafts git versus replace . A pesquisa [git] + enxertos + replace encontrou apenas dois que pareciam relevantes do 5. what-are-git-info-enxertos-para e git-o-que-é-um-enxerto-com-um-enxerto-id . Há também uma nota em git.wiki.kernel.org:GraftPoint

Os enxertos agora estão completamente ultrapassados pela replace e filter-branch , ou ainda são necessários para alguns casos especiais de canto (e compatibilidade com versões anteriores)?

Em geral, como eles diferem (por exemplo, quais são transportados entre repos) e como eles são genericamente os mesmos? Eu vi que Linus não parece se importar com os enxertos atualmente na discussão sobre números de geração de commit (dos pais máximos de volta para qualquer variedade de raiz) “Enxertos já não são confiáveis.”

EDIT: mais informações encontradas.
Uma pesquisa em www.kernel.org/pub/software/scm/git/docs para graft encontrou apenas 3 resultados:

  1. git-filter-branch (1),
  2. v1.5.4.7 / git-filter-branch (1),
  3. v1.5.0.7 / git-svn (1).

Uma pesquisa um pouco mais abrangente encontrou o RelNotes / 1.6.5.txt, que contém:

  • refs / replace / hierarchy foi projetado para ser usado como uma substituição do mecanismo de “enxertos”, com a vantagem adicional de poder ser transferido entre repositorys.

Infelizmente, o gitrepository-layout (5) ainda não está atualizado com as informações de layout do refs / replace / repository (e notas), nem com qualquer nota de depreciação de info / enxts.

Isso se aproxima de apoiar o que eu estava pensando, mas eu gostaria de receber qualquer confirmação ou esclarecimento.

Na mesma discussão sobre Commit Generation Number que você mencionou, Jakub Narębski confirma que os enxertos são mais um problema do que uma solução:

enxertos são tão horríveis que eu não seria contra desligar números de geração se eles fossem usados.
No caso de replace objects, você precisa dos números de geração do DAG não substituídos e substituídos.
[…] Enxertos não são transferíveis, e se você usá-los para selecionar em vez de adicionar histórico, eles não são seguros contra a garbage collection … eu acho.

(a publicação sempre foi cuidada com o git filter-branch , conforme ilustrado por este tópico de 2008 sobre o stream de trabalho de enxertos ).

A diferença entre os enxertos e o git replace é melhor ilustrada por esta pergunta SO “Configurando o ponteiro pai do git para um pai diferente” , e os comentários da resposta (do Jakub novamente) .

Inclui a referência ao Git1.6.5

Pelo que entendi (do GraftPoints ), o git replace substituiu os git grafts (supondo que você tenha o git 1.6.5 ou mais recente)

(Jakub 🙂

  • Se você quiser rewrite o histórico , grafts + git-filter-branch (ou rebase interativo, ou fast-export + eg reposurgeon) são a maneira de fazê-lo.
  • Se você quer / precisa preservar o histórico , então o git-replace é muito superior ao enxerto

Se você precisar rewrite um commit pai usando o git replace , isto é como fazê-lo.

Como Philip Oakley mencionou, git replace simplesmente substitui um commit por outro. Para enxertar um pai em um commit existente, primeiro você precisa criar um commit falso com o pai correto.

Digamos que você tenha dois branchs git que você quer enxertar:

 (a)-(b)-(c) (d)-(e)-(f) 

Agora queremos que (d) seja o pai de (c). Então criamos um substituto para (c) com o pai correto (vamos chamar isso de c1), então git replace (c) com (c1). Nestas etapas, cada uma das letras refere-se ao hash SHA1 que representa essa confirmação.

Para criar o novo commit:

 git checkout d git rm -rf * # remove all files from working direcotry git checkout c -- . # commit everything from c over top of it GIT_AUTHOR_DATE="..." GIT_COMMITTER_DATE="..." git commit -m "..." # create replacement commit with date author 

Agora você tem commit (c1) que tem o pai correto (d). Então, tudo o que precisamos fazer é replace o existente (c) por (c1):

 git replace c c1 

Agora seu histórico é assim:

 (a)-(b)-(c1)-(d)-(e)-(f) 

Bingo!

EDIT: git replace --graft […​] faz a mesma coisa que enxertos e pode adicionar ou remover pais. A documentação diz:

Crie uma confirmação de enxerto. Um novo commit é criado com o mesmo conteúdo de , exceto que seus pais serão […] ao invés dos pais de . Uma ref de substituição é criada para replace a confirmação recém-criada.

(Estou deixando a velha resposta abaixo como referência.)


AFAIK, há um caso de uso que os grafts podem manipular, mas replace não pode: adicionar ou remover pais. É uma ferramenta poderosa para refatorar histórias.

Por exemplo, se você estiver importando o histórico de um repository SVN antigo para o Git, não haverá informações de mesclagem. O que você pode fazer (e eu fiz isso várias vezes) é ler as mensagens de confirmação para descobrir onde uma “mesclagem” do SVN foi feita e, em seguida, usar os enxertos Git para adicionar um pai à consolidação de mesclagem.

IIRC, eu também tive alguns casos em que eu removi o pai de um commit, a fim de torná-lo o primeiro commit na história. Criar um histórico limpo com base em vários repositorys legados caóticos às vezes requer medidas drásticas (há algumas experiências de migration de projetos para o Git no meu blog).

Então, depois de limpar todo o histórico, você faria um git filter-branch antes de publicar o novo repository Git.