Qual é a diferença entre `git merge` e` git merge --no-ff`?

Respostas:

1080

O --no-ffsinalizador impede a git mergeexecução de um "avanço rápido" se detectar que o seu atual HEADé um ancestral do commit que você está tentando mesclar. Um avanço rápido é quando, em vez de construir um commit de mesclagem, o git apenas move o ponteiro do branch para apontar para o commit de entrada. Isso geralmente ocorre ao fazer um git pullsem alterações locais.

No entanto, ocasionalmente, você deseja impedir que esse comportamento aconteça, normalmente porque você deseja manter uma topologia de ramificação específica (por exemplo, você está mesclando em uma ramificação de tópico e deseja garantir a aparência dessa maneira ao ler o histórico). A fim de fazer isso, você pode passar a --no-ffbandeira e git mergevai sempre construir uma mesclagem em vez de fast-encaminhamento.

Da mesma forma, se você deseja executar um git pullou usar git mergepara avançar explicitamente, e deseja resgatar se ele não puder avançar rapidamente, use o --ff-onlysinalizador. Dessa forma, você pode git pull --ff-onlyexecutar regularmente algo como sem pensar e, se ocorrer um erro, poderá voltar e decidir se deseja mesclar ou refazer o processo.

Lily Ballard
fonte
87
Para responder mais diretamente à pergunta do OP: elas nem sempre são diferentes, mas, se são, é claro gitkou git log --graphque a mesclagem de avanço rápido não criou uma consolidação de mesclagem, enquanto a não-avanço rápido fez.
Cascabel
11
Seria bom expandir o motivo para evitar ff: o autor mencionado "topologia de ramificação específica" significa que, no caso --no-ff, uma confirmação de mesclagem extra serve como marcador da mesclagem. Os profissionais são marcadores de mesclagem explícitos com nomes do autor e da fusão. Os contras são uma história não linear que parece um conjunto de trilhos de trem convergentes. Uma possível psicológica efeito colateral do que mesclagem é contribuidores perder o interesse devido a um processo de revisão mais: blog.spreedly.com/2014/06/24/...
Vlad
6
Seria justo dizer que --no-ffde um recurso para desenvolver ou desenvolver para dominar é semelhante à mesclagem de uma solicitação pull?
Merlinpatt
9
@merlinpatt Claro. Se você mesclar uma solicitação pull no GitHub, ela é equivalente a --no-ff.
Lily Ballard
1
Esta é uma boa explicação. Homem, não é apenas o suficiente explicações boa git lá fora, para as pessoas a entender por que a história git limpa é muito, muito importante (eu incluído!)
dudewad
1037

Resposta gráfica a esta pergunta

Aqui está um site com uma explicação clara e uma ilustração gráfica do uso git merge --no-ff:

diferença entre git merge --no-ff e git merge

Até ver isso, eu estava completamente perdido com o git. O uso --no-ffpermite que alguém que revise o histórico veja claramente a ramificação na qual você fez check-out para trabalhar. (esse link aponta para a ferramenta de visualização "rede" do github) E aqui está outra ótima referência com ilustrações. Essa referência complementa bem a primeira, com mais foco naqueles menos familiarizados com o git.


Informações básicas para newbs como eu

Se você é como eu, e não um git-guru, minha resposta aqui descreve como lidar com a exclusão de arquivos do rastreamento do git sem excluí-los do sistema de arquivos local, que parece mal documentado, mas geralmente ocorre. Outra situação nova é obter o código atual , que ainda consegue me iludir.


Exemplo de fluxo de trabalho

Atualizei um pacote para o meu site e tive que voltar às minhas anotações para ver meu fluxo de trabalho; Achei útil adicionar um exemplo a esta resposta.

Meu fluxo de trabalho de comandos git:

git checkout -b contact-form
(do your work on "contact-form")
git status
git commit -am  "updated form in contact module"
git checkout master
git merge --no-ff contact-form
git branch -d contact-form
git push origin master

Abaixo: uso real, incluindo explicações.
Nota: a saída abaixo é cortada; git é bastante detalhado.

$ git status
# On branch master
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   ecc/Desktop.php
#       modified:   ecc/Mobile.php
#       deleted:    ecc/ecc-config.php
#       modified:   ecc/readme.txt
#       modified:   ecc/test.php
#       deleted:    passthru-adapter.igs
#       deleted:    shop/mickey/index.php
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       ecc/upgrade.php
#       ecc/webgility-config.php
#       ecc/webgility-config.php.bak
#       ecc/webgility-magento.php

Observe três coisas acima:
1) Na saída, você pode ver as alterações da atualização do pacote ECC, incluindo a adição de novos arquivos.
2) Observe também que existem dois arquivos (que não estão na /eccpasta) que apaguei independentemente dessa alteração. Em vez de confundir essas exclusões de arquivos ecc, criarei uma cleanupramificação diferente posteriormente para refletir a exclusão desses arquivos.
3) Não segui meu fluxo de trabalho! Eu esqueci o git enquanto tentava fazer o ecc funcionar novamente.

Abaixo: em vez de incluir tudo incluído git commit -am "updated ecc package", normalmente, eu só queria adicionar os arquivos na /eccpasta. Esses arquivos excluídos não faziam parte especificamente dos meus git add, mas como eles já foram rastreados no git, preciso removê-los do commit deste ramo:

$ git checkout -b ecc
$ git add ecc/*
$ git reset HEAD passthru-adapter.igs
$ git reset HEAD shop/mickey/index.php
Unstaged changes after reset:
M       passthru-adapter.igs
M       shop/mickey/index.php

$ git commit -m "Webgility ecc desktop connector files; integrates with Quickbooks"

$ git checkout master
D       passthru-adapter.igs
D       shop/mickey/index.php
Switched to branch 'master'
$ git merge --no-ff ecc
$ git branch -d ecc
Deleted branch ecc (was 98269a2).
$ git push origin master
Counting objects: 22, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (14/14), 59.00 KiB, done.
Total 14 (delta 10), reused 0 (delta 0)
To [email protected]:me/mywebsite.git
   8a0d9ec..333eff5  master -> master



Script para automatizar o acima

Tendo usado esse processo mais de 10 vezes em um dia, comecei a escrever scripts em lote para executar os comandos, então criei um git_update.sh <branch> <"commit message">script quase adequado para executar as etapas acima. Aqui está a fonte Gist para esse script.

Em vez de git commit -amselecionar arquivos da lista "modificada" produzida por git statuse colá-los no script. Isso aconteceu porque eu fiz dezenas de edições, mas queria nomes de filial variados para ajudar a agrupar as alterações.

Chris K
fonte
11
Você ainda pode excluir com segurança uma ramificação depois de mesclar com a --no-ffopção?
Andy Fleming
17
@DesignerGuy sim, você pode excluir com segurança o ramo antigo. Pense nas ramificações es como apenas ponteiros para um commit específico.
Zyphrax
2
Achei este texto da página vinculada útil: sem --no-ff "é impossível ver no histórico do Git quais objetos de consolidação juntos implementaram um recurso - você precisaria ler manualmente todas as mensagens de log".
Lorne Laliberte
uma imagem sempre melhor que mil palavras!
Rupps
1
O gráfico não mostra o ramo de recursos na segunda imagem, por que não? Ainda existe, não é?
precisa saber é o seguinte
269

Estratégias de mesclagem

Mesclagem explícita : cria uma nova confirmação de mesclagem. (Isso é o que você obterá se usou --no-ff.)

insira a descrição da imagem aqui

Mesclagem de avanço rápido : encaminhe rapidamente, sem criar uma nova confirmação:

insira a descrição da imagem aqui

Rebase : estabeleça um novo nível básico:

insira a descrição da imagem aqui

Squash: Esmague ou aperte (alguma coisa) com força para que fique plana:

insira a descrição da imagem aqui

Premraj
fonte
11
Gráfico interessante, mas realmente não mostra o caso --no-ff e, portanto, não chega a responder à pergunta aqui
Vib
22
O primeiro, a "mesclagem explícita", intitulada aqui como "mesclagem", é uma mesclagem não-ff.
bkribbs
3
que os gráficos realmente me ajudou muito
exexzian
2
não responde à pergunta por dizer, mas este gráfico é incrível, UPVOTE!
SovietFrontier
3
Então, qual é a diferença entre Rebase e mesclagem de avanço rápido?
ThanosFisherman
217

A --no-ffopção garante que uma mesclagem de avanço rápido não aconteça e que um novo objeto de confirmação seja sempre criado . Isso pode ser desejável se você quiser que o git mantenha um histórico de ramificações de recursos.             git merge - não-ff vs git merge Na imagem acima, o lado esquerdo é um exemplo do histórico do git após o uso git merge --no-ffe o lado direito é um exemplo de uso git mergeonde uma fusão ff era possível.

EDIT : Uma versão anterior desta imagem indicava apenas um pai único para a confirmação de mesclagem. As confirmações de mesclagem têm várias confirmações pai que o git usa para manter um histórico do "ramo de recurso" e do ramo original. Os vários links pai estão destacados em verde.

Daniel Smith
fonte
3
O que a seta verde indica contra a seta preta?
Rtconner
11
a seta verde indica um link entre uma confirmação e um pai em que a confirmação tem mais de um pai. As confirmações normais (preto) têm apenas um pai.
Daniel Smith
9
@DanielSmith Como você desenha esse gráfico elegante?
usar o seguinte código
4
@chancyWu com Adobe Illustrator
Daniel Smith
Parece que na minha empresa todas as solicitações pull são mescladas com a opção --no-ff. Sob essas circunstâncias, ainda faz sentido fazer uma nova recuperação antes de criar uma solicitação de recebimento?
Nickpick
37

Essa é uma pergunta antiga e mencionada de maneira sutil nas outras postagens, mas a explicação que me deu esse clique é que mesclagens sem avanço rápido exigirão uma confirmação separada .

Parris Varney
fonte
você poderia revisar o fluxo de trabalho na minha resposta acima: isso significa que preciso de confirmações adicionais além do que tenho agora? Obrigado.
Chris K
3
No @ChrisK, git merge --no-ff eccvocê simplesmente terá uma confirmação de mesclagem adicional no git logmestre de ramificação for. Tecnicamente, isso não é necessário, caso o mestre esteja apontando para um ancestral direto do commit ecc, mas especificando a opção --no-ff, você está forçando a criação desse commit de mesclagem. Ele terá o título:Merge branch 'ecc'
Ankur Agarwal
Ótimo resumo! :)
Eduard
4

O sinalizador --no-ff faz com que a mesclagem sempre crie um novo objeto de confirmação, mesmo que a mesclagem possa ser executada com um avanço rápido. Isso evita a perda de informações sobre a existência histórica de uma ramificação de recurso e agrupa todas as confirmações que adicionaram o recurso

jsina
fonte