Eu quero saber um algoritmo exato (ou perto disso) por trás do 'git merge'. As respostas, pelo menos a essas subquestões, serão úteis:
- Como o git detecta o contexto de uma mudança não conflitante em particular?
- Como o git descobre que há um conflito nessas linhas exatas?
- Quais coisas o git mescla automaticamente?
- Como o git funciona quando não há uma base comum para mesclar branches?
- Como o git funciona quando há várias bases comuns para mesclar branches?
- O que acontece quando mesclo várias ramificações de uma vez?
- Qual é a diferença entre estratégias de mesclagem?
Mas a descrição de todo um algoritmo será muito melhor.
Respostas:
Talvez seja melhor você procurar uma descrição de um algoritmo de mesclagem de 3 vias. Uma descrição de alto nível seria mais ou menos assim:
B
- uma versão do arquivo que é ancestral de ambas as novas versões (X
eY
), e geralmente a mais recente dessa base (embora haja casos em que terá que voltar mais longe, que é um dos os recursos de mesclagemgit
padrãorecursive
)X
comB
eY
comB
.O algoritmo completo lida com isso com muito mais detalhes e ainda tem alguma documentação ( https://github.com/git/git/blob/master/Documentation/technical/trivial-merge.txt para um, junto com as
git help XXX
páginas , onde XXX é um dosmerge-base
,merge-file
,merge
,merge-one-file
e possivelmente alguns outros). Se isso não for profundo o suficiente, sempre há código-fonte ...fonte
Como o git funciona quando há várias bases comuns para mesclar branches?
Este artigo foi muito útil: http://codicesoftware.blogspot.com/2011/09/merge-recursive-strategy.html (aqui está a parte 2 ).
Recursivo usa diff3 recursivamente para gerar uma ramificação virtual que será usada como ancestral.
Por exemplo:
Então:
Existem 2 melhores ancestrais comuns (ancestrais comuns que não são ancestrais de nenhum outro)
C
eD
. O Git os mescla em um novo branch virtualV
e os usaV
como base.Suponho que o Git apenas continuaria com o se houvesse mais ancestrais comuns melhores, se fundindo
V
com o próximo.O artigo diz que se houver um conflito de mesclagem durante a geração do branch virtual, o Git apenas deixa os marcadores de conflito onde estão e continua.
O que acontece quando mesclo várias ramificações de uma vez?
Como @Nevik Rehnel explicou, depende da estratégia, está bem explicado na
man git-merge
MERGE STRATEGIES
seção.Apenas
octopus
eours
/theirs
suporta mesclar vários ramos de uma vez,recursive
por exemplo, não.octopus
recusa-se a mesclar se houver conflitos, eours
é uma mesclagem trivial, portanto não pode haver conflitos.Esses comandos geram um novo commit que terá mais de 2 pais.
Eu fiz um
merge -X octopus
no Git 1.8.5 sem conflitos para ver como funciona.Estado inicial:
Açao:
Novo estado:
Como esperado,
E
tem 3 pais.TODO: exatamente como o polvo opera em modificações de um único arquivo. Mesclagens de três vias recursivas dois por dois?
Como o git funciona quando não há uma base comum para mesclar branches?
@Torek menciona que desde o 2.9, a mesclagem falha sem
--allow-unrelated-histories
.Eu tentei empiricamente no Git 1.8.5:
a
contém:Então:
a
contém:Interpretação:
a\nc\n
como uma adição de linha únicafonte
e379fdf34fee96cd205be83ff4e71699bdc32b18
), o Git agora se recusa a mesclar se não houver uma base de mesclagem a menos que você adicione--allow-unrelated-histories
.--allow-unrelated-histories
pode ser omitido se não houver caminhos de arquivo comuns entre as ramificações que você está mesclando.ours
estratégia de fusão, mas nenhumatheirs
estratégia de fusão.recursive
+theirs
estratégia só pode resolver dois ramos. git-scm.com/docs/git-merge#_merge_strategiesTambém estou interessado. Não sei a resposta, mas ...
Acho que a fusão do git é altamente sofisticada e será muito difícil de entender - mas uma maneira de abordar isso é partindo de seus precursores e focar no centro de sua preocupação. Ou seja, dados dois arquivos que não têm um ancestral comum, como git merge funciona como mesclá-los e onde estão os conflitos?
Vamos tentar encontrar alguns precursores. De
git help merge-file
:Da wikipedia: http://en.wikipedia.org/wiki/Git_%28software%29 -> http://en.wikipedia.org/wiki/Three-way_merge#Three-way_merge -> http: //en.wikipedia .org / wiki / Diff3 -> http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf
O último link é um pdf de um artigo que descreve o
diff3
algoritmo em detalhes. Aqui está uma versão do visualizador de pdf do google . Tem apenas 12 páginas, e o algoritmo tem apenas algumas páginas - mas um tratamento matemático completo. Isso pode parecer um pouco formal, mas se você quiser entender o merge do git, você precisará entender a versão mais simples primeiro. Eu não verifiquei ainda, mas com um nome comodiff3
, você provavelmente também precisará entender diff (que usa um algoritmo de subsequência comum mais longo ). No entanto, pode haver uma explicação mais intuitiva do que estádiff3
por aí, se você tiver um google ...Agora, acabei de fazer uma experiência comparando
diff3
egit merge-file
. Eles tomam os mesmos três arquivos de entrada version1 OldVersion version2 e conflitos marcam o caminho mesma, com<<<<<<< version1
,=======
,>>>>>>> version2
(diff3
também tem||||||| oldversion
), mostrando o seu património comum.Eu usei um arquivo vazio para OldVersion e arquivos quase idênticas para version1 e version2 com apenas uma linha extra adicionado ao version2 .
Resultado:
git merge-file
identificou a única linha alterada como o conflito; masdiff3
tratou os dois arquivos inteiros como um conflito. Portanto, por mais sofisticado que o diff3 seja, o merge do git é ainda mais sofisticado, mesmo para os casos mais simples.Aqui estão os resultados reais (usei a resposta de @twalberg para o texto). Observe as opções necessárias (consulte as respectivas páginas de manual).
$ git merge-file -p fun1.txt fun0.txt fun2.txt
$ diff3 -m fun1.txt fun0.txt fun2.txt
Se você estiver realmente interessado nisso, é uma espécie de toca de coelho. Para mim, parece tão profundo quanto expressões regulares, o algoritmo de subsequência comum mais longo de diff, gramáticas livres de contexto ou álgebra relacional. Se você quiser chegar ao fundo disso, acho que pode, mas vai exigir um estudo determinado.
fonte
Aqui está a implementação original
http://git.kaarsemaker.net/git/blob/857f26d2f41e16170e48076758d974820af685ff/git-merge-recursive.py
Basicamente, você cria uma lista de ancestrais comuns para dois commits e então os mescla recursivamente, seja avançando rapidamente ou criando commits virtuais que são usados como base para uma mesclagem de três vias nos arquivos.
fonte
Se a mesma linha mudou em ambos os lados da fusão, é um conflito; se não o fizeram, a mudança de um lado (se existente) é aceita.
Mudanças que não entram em conflito (veja acima)
Pela definição de uma base de mesclagem Git , existe apenas um (o último ancestral comum).
Isso depende da estratégia de fusão (apenas as estratégias
octopus
eours
/theirs
suportam a fusão de mais de dois ramos).Isso é explicado na página de
git merge
manual .fonte
git-merge-recursive
existe?git-merge-recursive
deveria ser (não existe uma página de manual e o google não produz nada). Mais informações sobre isso podem ser encontradas nas páginas de manualgit merge
egit merge-base
.git-merge
página dogit-merge-base
manual e as páginas do manual que você aponta discutem vários ancestrais comuns e mesclagem recursiva. Sinto que sua resposta está incompleta sem uma discussão sobre isso.