Quando você usa git rebase em vez de git merge?

Quando é recomendado usar git rebase vs. git merge ?

Ainda preciso mesclar depois de um rebase bem sucedido?

Versão curta

  • A mesclagem leva todas as alterações em uma ramificação e as mescla em outra ramificação em uma confirmação.
  • Rebase diz que eu quero o ponto em que me ramifiquei para mudar para um novo ponto de partida

Então, quando você usa um deles?

Mesclar

  • Digamos que você tenha criado uma ramificação com o objective de desenvolver um único recurso. Quando você quiser trazer essas mudanças de volta ao master, você provavelmente vai querer mesclar (você não se importa em manter todos os commits provisórios).

Rebase

  • Um segundo cenário seria se você começasse a fazer algum desenvolvimento e, em seguida, outro desenvolvedor fizesse uma alteração não relacionada. Você provavelmente quer puxar e, em seguida, rebase para basear suas alterações da versão atual do repo.

É simples, com rebase você diz para usar outro ramo como a nova base para o seu trabalho.

Se você tiver, por exemplo, um master filial e criar uma ramificação para implementar um novo recurso, digamos que você nomeie cool-feature , é claro que o branch master é a base para o seu novo recurso.

Agora, em um certo ponto, você deseja adicionar o novo recurso que você implementou no ramo master . Você poderia simplesmente mudar para master e mesclar o branch de cool-feature :

 $git checkout master $git merge cool-feature 

mas desta forma um novo commit dummy é adicionado, se você quiser evitar spaghetti-history você pode rebase :

 $git checkout cool-feature $git rebase master 

e depois mesclar no master :

 $git checkout master $git merge cool-feature 

Desta vez, como o branch de tópicos tem as mesmas confirmações de master mais os commits com o novo recurso, a mesclagem será apenas um avanço rápido.

Para complementar minha própria resposta mencionada por TSamper ,

  • um rebase é muitas vezes uma boa idéia para fazer antes de uma fusão, porque a idéia é que você integre em seu ramo Y o trabalho do ramo B no qual você irá se fundir.
    Mas, novamente, antes de mesclar, você resolve qualquer conflito em sua ramificação (por exemplo: “rebase”, como em “reproduzir meu trabalho em minha ramificação a partir de um ponto recente da ramificação B )
    Se feito corretamente, a fusão subseqüente da sua filial para a filial B pode ser rápida.

  • uma mesclagem impacta diretamente a ramificação de destino B , o que significa que as mesclagens são melhor triviais, caso contrário, a ramificação B pode demorar para voltar a um estado estável (o tempo para você resolver todos os conflitos)


o ponto de fusão após um rebase?

No caso que eu descrevo, eu rebaso B no meu ramo, apenas para ter a oportunidade de repetir o meu trabalho a partir de um ponto mais recente de B , mas enquanto permanecer no meu ramo.
Nesse caso, uma mesclagem ainda é necessária para trazer meu trabalho “repetido” para B

O outro cenário ( descrito em Git Ready, por exemplo), é trazer seu trabalho diretamente em B através de um rebase (que conserva todos os seus bons commits, ou até mesmo lhe dá a oportunidade de reordená-los através de um rebase interativo).
Nesse caso (onde você rebase enquanto está no ramo B), você está certo: nenhuma outra fusão é necessária:

Uma tree git no default quando não fundimos nem criamos

rebase1

nós começamos por rebasing:

rebase3

Esse segundo cenário tem tudo a ver: como faço para que o novo recurso volte ao master.

Meu ponto, descrevendo o primeiro cenário de rebase, é lembrar a todos que um rebase também pode ser usado como um passo preliminar para isso (que é “obter novo recurso de volta ao mestre”).
Você pode usar a rebase para primeiro trazer o mestre “in” na ramificação new-feature: a rebase irá repetir commits de novos resources do HEAD master , mas ainda na ramificação new-feature, movendo efetivamente o ponto inicial de uma ramificação antiga para HEAD-master .
Isso permite que você resolva quaisquer conflitos em sua filial (ou seja, isoladamente, enquanto permite que o mestre continue a evoluir em paralelo, se o estágio de resolução de conflitos demorar muito).
Então você pode mudar para master e merge new-feature (ou rebase new-feature para master se você quiser preservar commits feitos em sua ramificação de new-feature ).

Assim:

  • “rebase versus mesclagem” pode ser visto como duas maneiras de importar um trabalho, digamos, master .
  • Mas “rebase, em seguida, mesclar” pode ser um stream de trabalho válido para resolver conflitos em isolamento, em seguida, trazer de volta o seu trabalho.

Muitas respostas aqui dizem que a fusão transforma todos os seus commits em um e, portanto, sugere usar o rebase para preservar seus commits. Isso está incorreto. E uma má ideia se você já enviou seus commits .

A mesclagem não oblitera seus commits. Mesclar preserva a história! (apenas olhe para o gitk) Rebase reescreve a história, o que é uma coisa ruim depois que você a empurrou .

Use a mesclagem – não rebase sempre que você já empurrou.

Aqui está Linus (autor do git) . É uma ótima leitura. Ou você pode ler minha própria versão da mesma ideia abaixo.

Rebasando uma ramificação no master:

  • fornece uma ideia incorreta de como os commits foram criados
  • polui mestre com um monte de commits intermediários que podem não ter sido bem testados
  • Na verdade, era possível introduzir quebras de compilation nesses commits intermediários devido a alterações que foram feitas no master entre quando o branch de tópico original foi criado e quando ele foi rebaseado.
  • faz encontrar bons lugares no mestre para check-out difícil.
  • Faz com que os registros de data e hora em confirmações não sejam alinhados com sua ordem cronológica na tree. Então você veria que commit A precede commit B no master, mas commit B é o autor primeiro. (O que?!)
  • Produz mais conflitos porque os commits individuais no branch de tópicos podem envolver conflitos de mesclagem que devem ser resolvidos individualmente (Mais informações no histórico sobre o que aconteceu em cada commit).
  • é uma reescrita da história. Se a ramificação que está sendo rebaseada foi empurrada para qualquer lugar (compartilhada com alguém que não seja você mesmo), então você estragou todo mundo que tem essa ramificação desde que você reescreveu a história.

Em contraste, mesclando um ramo de tópico em mestre:

  • preserva o histórico de onde as ramificações de tópicos foram criadas, incluindo quaisquer mesclagens do mestre para o ramo de tópico para ajudar a mantê-lo atualizado. Você realmente tem uma ideia precisa de qual código o desenvolvedor estava trabalhando quando estava criando.
  • master é uma ramificação composta principalmente de mesclagens, e cada um desses commits de mesclagem são tipicamente ‘bons pontos’ no histórico que são seguros de serem verificados porque é onde o branch tópico estava pronto para ser integrado.
  • Todos os commits individuais da ramificação de tópico são preservados, incluindo o fato de que eles estavam em uma ramificação de tópico, portanto, isolar essas alterações é natural e você pode detalhar onde necessário.
  • Os conflitos de mesclagem só precisam ser resolvidos uma vez (no ponto da mesclagem), de modo que as alterações de confirmação intermediárias feitas na ramificação do tópico não precisam ser resolvidas de forma independente.
  • pode ser feito várias vezes sem problemas. Se você integrar seu ramo de tópico ao mestre periodicamente, as pessoas podem continuar construindo no ramo de tópico e ele pode continuar sendo mesclado de forma independente.

TL; DR

Se você tiver alguma dúvida, use a mesclagem.

Resposta curta

As únicas diferenças entre um rebase e uma mesclagem são:

  • A estrutura de tree resultante do histórico (geralmente apenas perceptível ao olhar para um gráfico de confirmação) é diferente (um terá ramificações, o outro não).
  • A mesclagem geralmente cria uma confirmação extra (por exemplo, nó na tree).
  • Mesclar e rebase lidará com conflitos de maneira diferente. O Rebase apresentará conflitos, um de cada vez, onde a mesclagem os apresentará todos de uma vez.

Portanto, a resposta curta é escolher rebase ou mesclar com base no que você deseja que sua história seja semelhante .

Resposta longa

Existem alguns fatores que você deve considerar ao escolher qual operação usar.

A ramificação que você está recebendo muda de compartilhada com outros desenvolvedores externos à sua equipe (por exemplo, código aberto, público)?

Se assim for, não rebase. O Rebase destrói o branch e esses desenvolvedores terão repositorys quebrados / inconsistentes a menos que usem o git pull --rebase . Essa é uma boa maneira de incomodar outros desenvolvedores rapidamente.

Quão qualificada é sua equipe de desenvolvimento?

Rebase é uma operação destrutiva. Isso significa que, se você não aplicar corretamente, poderá perder o trabalho comprometido e / ou quebrar a consistência dos repositorys de outros desenvolvedores.

Eu trabalhei em equipes onde todos os desenvolvedores vieram de uma época em que as empresas podiam pagar pessoal dedicado para lidar com ramificação e fusão. Esses desenvolvedores não sabem muito sobre o Git e não querem saber muito. Nessas equipes, não correria o risco de recomendar rebasing por qualquer motivo.

A filial em si representa informação útil?

Algumas equipes usam o modelo branch-per-feature, em que cada ramo representa um recurso (ou correção de erros, sub-recurso etc.). Nesse modelo, a ramificação ajuda a identificar conjuntos de confirmações relacionadas. Por exemplo, pode-se reverter rapidamente um recurso revertendo a mesclagem desse ramo (para ser justo, esta é uma operação rara). Ou difira um recurso comparando dois ramos (mais comuns). Rebase iria destruir o ramo e isso não seria simples.

Eu também trabalhei em equipes que usaram o modelo branch-per-developer (todos nós já estivemos lá). Neste caso, o ramo em si não transmite qualquer informação adicional (o commit já tem o autor). Não haveria nenhum dano em rebasing.

Você deseja reverter a mesclagem por algum motivo?

Reverter (como em desfazer) um rebase é consideravelmente difícil e / ou impossível (se o rebase tiver conflitos) comparado a reverter uma mesclagem. Se você acha que há uma chance de reverter, use a mesclagem.

Você trabalha em equipe? Em caso afirmativo, você está disposto a adotar uma abordagem de tudo ou nada neste ramo?

As operações de Rebase precisam ser puxadas com uma base de referência correspondente – git pull --rebase . Se você está trabalhando sozinho, você pode se lembrar do que deve usar no momento apropriado. Se você estiver trabalhando em uma equipe, isso será muito difícil de coordenar. É por isso que a maioria dos streams de trabalho de recompilation recomendam o uso de rebase para todas as mesclagens (e git pull --rebase para todos os pulls).

Mitos Comuns

Mesclar destrói histórico (squashes confirma)

Supondo que você tenha a seguinte fusão:

  B -- C / \ A--------D 

Algumas pessoas irão afirmar que a mesclagem “destrói” o histórico de commits porque se você olhasse para o log de apenas o branch master (A – D), você perderia as importantes mensagens de commit contidas em B e C.

Se isso fosse verdade, não teríamos perguntas como essa . Basicamente, você verá B e C, a menos que você peça explicitamente para não vê-los (usando – first-parent). Isso é muito fácil de tentar por si mesmo.

Rebase permite fusões mais seguras / simples

As duas abordagens se mesclam de maneira diferente, mas não está claro se uma é sempre melhor que a outra e pode depender do stream de trabalho do desenvolvedor. Por exemplo, se um desenvolvedor tende a se comprometer regularmente (por exemplo, talvez eles cometem duas vezes por dia à medida que fazem a transição de trabalho para casa), pode haver muitos commits para uma determinada ramificação. Muitos desses commits podem não se parecer em nada com o produto final (tenho a tendência de refatorar minha abordagem uma ou duas vezes por recurso). Se alguém estava trabalhando em uma área de código relacionada e tentou rebase minhas alterações, poderia ser uma operação bastante tediosa.

Rebase é mais legal / mais sexy / mais profissional

Se você gosta de alias rm para rm -rf para “economizar tempo” então talvez rebase é para você.

Meus dois centavos

Eu sempre penso que um dia vou me deparar com um cenário onde o git rebase é a ferramenta incrível que resolve o problema. Assim como eu acho que vou me deparar com um cenário onde o git reflog é uma ferramenta incrível que resolve o meu problema. Eu trabalhei com o git por mais de cinco anos agora. Isso não aconteceu.

Histórias confusas nunca foram realmente um problema para mim. Eu não leio apenas meu histórico de commits como um romance excitante. Na maioria das vezes eu preciso de uma história, eu vou usar git blame ou git bisect de qualquer maneira. Nesse caso, ter o commit de mesclagem é realmente útil para mim, porque se a mesclagem introduziu o problema que é uma informação significativa para mim.

Atualização (4/2017)

Sinto-me obrigado a mencionar que me amolei pessoalmente ao usar rebase, embora meu conselho geral ainda esteja de pé. Recentemente, tenho interagido muito com o projeto Angular 2 Material . Eles usaram rebase para manter um histórico de commit muito limpo. Isso me permitiu ver facilmente qual commit consertava um determinado defeito e se esse commit estava ou não incluído em um release. Ele serve como um ótimo exemplo do uso de rebase corretamente.

Mesclar significa: Criar um novo commit único que mescla minhas alterações no destino.

Rebase significa: Criar uma nova série de commits, usando meu conjunto atual de commits como dicas. Em outras palavras, calcule quais seriam as minhas alterações se eu tivesse começado a fazê-las a partir do ponto em que estou voltando. Depois do rebase, portanto, talvez seja necessário testar novamente suas alterações e, durante o rebase, você possivelmente teria alguns conflitos.

Dado isto, porque você rebase? Apenas para manter o histórico de desenvolvimento claro. Digamos que você esteja trabalhando no recurso X e, quando terminar, mescle suas alterações. O destino agora terá um único commit que diria algo como “Adicionado recurso X”. Agora, em vez de mesclar, se você tiver sido reimplantado e depois mesclado, o histórico de desenvolvimento de destino conterá todas as confirmações individuais em uma única progressão lógica. Isso torna a revisão das alterações mais tarde muito mais fácil. Imagine como você acharia difícil revisar o histórico de desenvolvimento se 50 desenvolvedores estivessem mesclando vários resources o tempo todo.

Dito isso, se você já empurrou o branch no qual está trabalhando, você não deve fazer o rebase, mas sim mesclar. Para as ramificações que não foram enviadas pelo desenvolvedor, rebase, teste e mescle.

Outra vez você pode querer rebase é quando você quer se livrar de commits do seu branch antes de empurrar upstream. Por exemplo: Confirmações que introduzem um código de debugging no início e outras confirmam que o código é limpo. A única maneira de fazer isso é realizando um rebase interativo: git rebase -i

ATUALIZAÇÃO: Você também quer usar rebase quando estiver usando o Git para fazer interface com um sistema de version control que não suporta histórico não linear (subversão, por exemplo). Ao usar a ponte git-svn, é muito importante que as mudanças que você mescla na subversão sejam uma lista sequencial de mudanças no topo das mudanças mais recentes no tronco. Existem apenas duas maneiras de fazer isso: (1) Recrie manualmente as alterações e (2) Usando o comando rebase, que é muito mais rápido.

UPDATE2: Uma maneira adicional de pensar em um rebase é que ele permite um tipo de mapeamento do seu estilo de desenvolvimento para o estilo aceito no repository no qual você está se comprometendo. Digamos que você gosta de se comprometer em pequenos e pequenos pedaços. Você tem um commit para corrigir um erro de digitação, um commit para se livrar do código não utilizado e assim por diante. Quando você terminar o que precisa fazer, terá uma longa série de commits. Agora, digamos que o repository que você está comprometendo a encorajar grandes commits, então, para o trabalho que você está fazendo, seria de se esperar um ou dois commits. Como você pega sua cadeia de commits e comprime-os para o que é esperado? Você usaria um rebase interativo e esmagaria seus pequenos commits em menos partes maiores. O mesmo acontece se o reverso for necessário – se o seu estilo tiver alguns commits grandes, mas o repository exigiu longas sequências de pequenos commits. Você usaria um rebase para fazer isso também. Se você tivesse mesclado, agora você já enxertou seu estilo de commit no repository principal. Se houver muitos desenvolvedores, você pode imaginar como seria difícil seguir um histórico com diversos estilos de commit após algum tempo.

UPDATE3: Does one still need to merge after a successful rebase? Sim, você faz. A razão é que um rebase envolve essencialmente um “deslocamento” de commits. Como eu disse acima, estes commits são calculados, mas se você tivesse 14 commits do ponto de ramificação, então assumindo que nada dá errado com o seu rebase, você terá 14 commits à frente (do ponto em que você está rebaixando) depois o rebase é feito. Você tinha um ramo antes de um rebase. Você terá um ramo do mesmo comprimento depois. Você ainda precisa mesclar antes de publicar suas alterações. Em outras palavras, rebase quantas vezes quiser (novamente, apenas se você não tiver empurrado suas alterações para o upstream). Mesclar somente depois que você rebase.

antes de mesclar / rebase:

 A < - B <- C [master] ^ \ D <- E [branch] 

após git merge master :

 A < - B <- C ^ ^ \ \ D <- E <- F 

após git rebase master :

 A < - B <- C <- D' <- E' 

(A, B, C, D, E e F são commits)

Este exemplo e muito mais bem ilustrado informações sobre o git podem ser encontradas aqui: http://excess.org/article/2008/07/ogre-git-tutorial/

Esta frase entende:

Em geral, a maneira de obter o melhor dos dois mundos é reformular as mudanças locais que você fez, mas ainda não compartilhou, antes de empurrá-las para limpar sua história, mas nunca rebase qualquer coisa que você tenha feito em algum lugar.

Fonte: http://www.git-scm.com/book/en/v2/Git-Branching-Rebasing#Rebase-vs.-Merge

Embora a fusão seja definitivamente a maneira mais fácil e mais comum de integrar as mudanças, ela não é a única: o Rebase é um meio alternativo de integração.

Compreensão Mesclar um pouco melhor

Quando o Git realiza uma mesclagem, ele procura por três commits:

  • (1) Cometer ancestral comum Se você segue a história de dois ramos em um projeto, eles sempre têm pelo menos um commit em comum: neste momento, ambos os branches tinham o mesmo conteúdo e então evoluíram de forma diferente.
  • (2) + (3) Endpoints de cada ramificação O objective de uma integração é combinar os estados atuais de duas ramificações. Portanto, suas respectivas revisões mais recentes são de especial interesse. Combinar esses três commits resultará na integração que pretendemos.

Fast-Forward ou Merge Commit

Em casos muito simples, um dos dois ramos não tem novos commits desde que a ramificação aconteceu – seu último commit ainda é o ancestral comum.

insira a descrição da imagem aqui

Neste caso, executar a integração é muito simples: o Git pode apenas adicionar todos os commits da outra ramificação no topo da submissão ancestral comum. No Git, essa forma mais simples de integração é chamada de mesclagem “fast-forward”. Ambas as filiais compartilham exatamente a mesma história.

insira a descrição da imagem aqui

Em muitos casos, no entanto, ambos os ramos avançaram individualmente. insira a descrição da imagem aqui

Para fazer uma integração, o Git terá que criar um novo commit que contenha as diferenças entre eles – o commit da merge.

insira a descrição da imagem aqui

Compromissos Humanos e Commits de Mesclagem

Normalmente, um commit é cuidadosamente criado por um ser humano. É uma unidade significativa que envolve apenas alterações relacionadas e as anota com um comentário.

Uma consolidação de mesclagem é um pouco diferente: em vez de ser criada por um desenvolvedor, ela é criada automaticamente pelo Git. E em vez de envolver um conjunto de alterações relacionadas, seu objective é conectar dois ramos, como um nó. Se você quiser entender uma operação de mesclagem mais tarde, você precisa dar uma olhada no histórico de ambas as ramificações e no gráfico de confirmação correspondente.

Integrando com Rebase

Algumas pessoas preferem ficar sem esses commits automáticos de mesclagem. Em vez disso, eles querem que a história do projeto pareça ter evoluído em uma única linha reta. Não há indicação de que ele tenha sido dividido em várias ramificações em algum momento.

insira a descrição da imagem aqui

Vamos percorrer uma operação de rebase passo a passo. O cenário é o mesmo dos exemplos anteriores: queremos integrar as mudanças da ramificação B para a ramificação A, mas agora usando rebase.

insira a descrição da imagem aqui

Nós vamos fazer isso em três etapas

  1. git rebase branch-A // syncs the history with branch-A
  2. git checkout branch-A // change the current branch to branch-A
  3. git merge branch-B // merge/take the changes from branch-B to branch-A

Primeiro, o Git irá “desfazer” todos os commits no branch-A que aconteceram depois que as linhas começaram a se ramificar (depois do commit ancestral comum). No entanto, é claro, não irá descartá-los: em vez disso, você pode pensar que os commits são “salvos temporariamente”.

insira a descrição da imagem aqui

Em seguida, aplica os commits da branch-B que queremos integrar. Nesse ponto, os dois ramos parecem exatamente iguais.

insira a descrição da imagem aqui

Na etapa final, os novos commits na ramificação-A são reaplicados – mas em uma nova posição, além dos commits integrados da branch-B (eles são re-baseados). O resultado parece que o desenvolvimento aconteceu em linha reta. Em vez de uma consolidação de mesclagem que contém todas as alterações combinadas, a estrutura de confirmação original foi preservada.

insira a descrição da imagem aqui

Finalmente, você obtém um ramo branch-A limpo, sem commits automáticos e indesejados.

Nota: Extraído do post incrível de git-tower . As desvantagens do rebase também são uma boa leitura no mesmo post.

O livro pro git como uma boa explicação na página de rebasing .

Basicamente, uma mesclagem vai levar 2 commits e combiná-los.

A rebase will go to the common ancestor on the 2 and incrementally apply the changes on top of each other. This makes for a ‘cleaner’ and more linear history.

But when you rebase you abandon previous commits and create new ones. So you should never rebase a repo that is public. The other people working on the repo will hate you.

For that reason alone I almost exclusively merge. 99% of the time my branches don’t differ that much, so if there are conflicts it’s only in one or two places.

This answer is widely oriented around Git Flow . The tables have been generated with the nice ASCII Table Generator , and the history trees with this wonderful command ( aliased as git lg ):

 git log --graph --abbrev-commit --decorate --date=format:'%Y-%m-%d %H:%M:%S' --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%ad%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)' 

Tables are in reverse chronological order to be more consistent with the history trees. See also the difference between git merge and git merge --no-ff first (you usually want to use git merge --no-ff as it makes your history look closer to the reality):

git merge

Commands:

 Time Branch "develop" Branch "features/foo" ------- ------------------------------ ------------------------------- 15:04 git merge features/foo 15:03 git commit -m "Third commit" 15:02 git commit -m "Second commit" 15:01 git checkout -b features/foo 15:00 git commit -m "First commit" 

Resultado:

 * 142a74a - YYYY-MM-DD 15:03:00 (XX minutes ago) (HEAD -> develop, features/foo) | Third commit - Christophe * 00d848c - YYYY-MM-DD 15:02:00 (XX minutes ago) | Second commit - Christophe * 298e9c5 - YYYY-MM-DD 15:00:00 (XX minutes ago) First commit - Christophe 

git merge --no-ff

Commands:

 Time Branch "develop" Branch "features/foo" ------- -------------------------------- ------------------------------- 15:04 git merge --no-ff features/foo 15:03 git commit -m "Third commit" 15:02 git commit -m "Second commit" 15:01 git checkout -b features/foo 15:00 git commit -m "First commit" 

Resultado:

 * 1140d8c - YYYY-MM-DD 15:04:00 (XX minutes ago) (HEAD -> develop) |\ Merge branch 'features/foo' - Christophe | * 69f4a7a - YYYY-MM-DD 15:03:00 (XX minutes ago) (features/foo) | | Third commit - Christophe | * 2973183 - YYYY-MM-DD 15:02:00 (XX minutes ago) |/ Second commit - Christophe * c173472 - YYYY-MM-DD 15:00:00 (XX minutes ago) First commit - Christophe 

git merge vs git rebase

First point: always merge features into develop, never rebase develop from features . This is a consequence of the Golden Rule of Rebasing :

The golden rule of git rebase is to never use it on public branches.

In other words :

Never rebase anything you’ve pushed somewhere.

I would personally add: unless it’s a feature branch AND you and your team are aware of the consequences .

So the question of git merge vs git rebase applies almost only to the feature branches (in the following examples, --no-ff has always been used when merging). Note that since I’m not sure there’s one better solution ( a debate exists ), I’ll only provide how both commands behave. In my case, I prefer using git rebase as it produces a nicer history tree 🙂

Between feature branches

git merge

Commands:

 Time Branch "develop" Branch "features/foo" Branch "features/bar" ------- -------------------------------- ------------------------------- -------------------------------- 15:10 git merge --no-ff features/bar 15:09 git merge --no-ff features/foo 15:08 git commit -m "Sixth commit" 15:07 git merge --no-ff features/foo 15:06 git commit -m "Fifth commit" 15:05 git commit -m "Fourth commit" 15:04 git commit -m "Third commit" 15:03 git commit -m "Second commit" 15:02 git checkout -b features/bar 15:01 git checkout -b features/foo 15:00 git commit -m "First commit" 

Resultado:

 * c0a3b89 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop) |\ Merge branch 'features/bar' - Christophe | * 37e933e - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar) | | Sixth commit - Christophe | * eb5e657 - YYYY-MM-DD 15:07:00 (XX minutes ago) | |\ Merge branch 'features/foo' into features/bar - Christophe | * | 2e4086f - YYYY-MM-DD 15:06:00 (XX minutes ago) | | | Fifth commit - Christophe | * | 31e3a60 - YYYY-MM-DD 15:05:00 (XX minutes ago) | | | Fourth commit - Christophe * | | 98b439f - YYYY-MM-DD 15:09:00 (XX minutes ago) |\ \ \ Merge branch 'features/foo' - Christophe | |/ / |/| / | |/ | * 6579c9c - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo) | | Third commit - Christophe | * 3f41d96 - YYYY-MM-DD 15:03:00 (XX minutes ago) |/ Second commit - Christophe * 14edc68 - YYYY-MM-DD 15:00:00 (XX minutes ago) First commit - Christophe 

git rebase

Commands:

 Time Branch "develop" Branch "features/foo" Branch "features/bar" ------- -------------------------------- ------------------------------- ------------------------------- 15:10 git merge --no-ff features/bar 15:09 git merge --no-ff features/foo 15:08 git commit -m "Sixth commit" 15:07 git rebase features/foo 15:06 git commit -m "Fifth commit" 15:05 git commit -m "Fourth commit" 15:04 git commit -m "Third commit" 15:03 git commit -m "Second commit" 15:02 git checkout -b features/bar 15:01 git checkout -b features/foo 15:00 git commit -m "First commit" 

Resultado:

 * 7a99663 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop) |\ Merge branch 'features/bar' - Christophe | * 708347a - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar) | | Sixth commit - Christophe | * 949ae73 - YYYY-MM-DD 15:06:00 (XX minutes ago) | | Fifth commit - Christophe | * 108b4c7 - YYYY-MM-DD 15:05:00 (XX minutes ago) | | Fourth commit - Christophe * | 189de99 - YYYY-MM-DD 15:09:00 (XX minutes ago) |\ \ Merge branch 'features/foo' - Christophe | |/ | * 26835a0 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo) | | Third commit - Christophe | * a61dd08 - YYYY-MM-DD 15:03:00 (XX minutes ago) |/ Second commit - Christophe * ae6f5fc - YYYY-MM-DD 15:00:00 (XX minutes ago) First commit - Christophe 

From develop to a feature branch

git merge

Commands:

 Time Branch “develop" Branch "features/foo" Branch "features/bar" ------- -------------------------------- ------------------------------- ------------------------------- 15:10 git merge --no-ff features/bar 15:09 git commit -m “Sixth commit" 15:08 git merge --no-ff development 15:07 git merge --no-ff features/foo 15:06 git commit -m “Fifth commit" 15:05 git commit -m “Fourth commit" 15:04 git commit -m “Third commit" 15:03 git commit -m “Second commit" 15:02 git checkout -b features/bar 15:01 git checkout -b features/foo 15:00 git commit -m “First commit" 

Resultado:

 * 9e6311a - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop) |\ Merge branch 'features/bar' - Christophe | * 3ce9128 - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar) | | Sixth commit - Christophe | * d0cd244 - YYYY-MM-DD 15:08:00 (XX minutes ago) | |\ Merge branch 'develop' into features/bar - Christophe | |/ |/| * | 5bd5f70 - YYYY-MM-DD 15:07:00 (XX minutes ago) |\ \ Merge branch 'features/foo' - Christophe | * | 4ef3853 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo) | | | Third commit - Christophe | * | 3227253 - YYYY-MM-DD 15:03:00 (XX minutes ago) |/ / Second commit - Christophe | * b5543a2 - YYYY-MM-DD 15:06:00 (XX minutes ago) | | Fifth commit - Christophe | * 5e84b79 - YYYY-MM-DD 15:05:00 (XX minutes ago) |/ Fourth commit - Christophe * 2da6d8d - YYYY-MM-DD 15:00:00 (XX minutes ago) First commit - Christophe 

git rebase

Commands:

 Time Branch “develop" Branch "features/foo" Branch "features/bar" ------- -------------------------------- ------------------------------- ------------------------------- 15:10 git merge --no-ff features/bar 15:09 git commit -m “Sixth commit" 15:08 git rebase development 15:07 git merge --no-ff features/foo 15:06 git commit -m “Fifth commit" 15:05 git commit -m “Fourth commit" 15:04 git commit -m “Third commit" 15:03 git commit -m “Second commit" 15:02 git checkout -b features/bar 15:01 git checkout -b features/foo 15:00 git commit -m “First commit" 

Resultado:

 * b0f6752 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop) |\ Merge branch 'features/bar' - Christophe | * 621ad5b - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar) | | Sixth commit - Christophe | * 9cb1a16 - YYYY-MM-DD 15:06:00 (XX minutes ago) | | Fifth commit - Christophe | * b8ddd19 - YYYY-MM-DD 15:05:00 (XX minutes ago) |/ Fourth commit - Christophe * 856433e - YYYY-MM-DD 15:07:00 (XX minutes ago) |\ Merge branch 'features/foo' - Christophe | * 694ac81 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo) | | Third commit - Christophe | * 5fd94d3 - YYYY-MM-DD 15:03:00 (XX minutes ago) |/ Second commit - Christophe * d01d589 - YYYY-MM-DD 15:00:00 (XX minutes ago) First commit - Christophe 

Side notes

git cherry-pick

When you just need one specific commit, git cherry-pick is a nice solution (the -x option appends a line that says ” (cherry picked from commit…) ” to the original commit message body, so it’s usually a good idea to use it – git log to see it):

Commands:

 Time Branch “develop" Branch "features/foo" Branch "features/bar" ------- -------------------------------- ------------------------------- ----------------------------------------- 15:10 git merge --no-ff features/bar 15:09 git merge --no-ff features/foo 15:08 git commit -m “Sixth commit" 15:07 git cherry-pick -x  15:06 git commit -m “Fifth commit" 15:05 git commit -m “Fourth commit" 15:04 git commit -m “Third commit" 15:03 git commit -m “Second commit" 15:02 git checkout -b features/bar 15:01 git checkout -b features/foo 15:00 git commit -m “First commit" 

Resultado:

 * 50839cd - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop) |\ Merge branch 'features/bar' - Christophe | * 0cda99f - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar) | | Sixth commit - Christophe | * f7d6c47 - YYYY-MM-DD 15:03:00 (XX minutes ago) | | Second commit - Christophe | * dd7d05a - YYYY-MM-DD 15:06:00 (XX minutes ago) | | Fifth commit - Christophe | * d0d759b - YYYY-MM-DD 15:05:00 (XX minutes ago) | | Fourth commit - Christophe * | 1a397c5 - YYYY-MM-DD 15:09:00 (XX minutes ago) |\ \ Merge branch 'features/foo' - Christophe | |/ |/| | * 0600a72 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo) | | Third commit - Christophe | * f4c127a - YYYY-MM-DD 15:03:00 (XX minutes ago) |/ Second commit - Christophe * 0cf894c - YYYY-MM-DD 15:00:00 (XX minutes ago) First commit - Christophe 

git pull --rebase

Not sure I can explain it better than Derek Gourlay … Basically, use git pull --rebase instead of git pull 🙂 What’s missing in the article though, is that you can enable it by default :

 git config --global pull.rebase true 

git rerere

Again, nicely explained here . But put simple, if you enable it, you won’t have to resolve the same conflict multiple times anymore.

Git rebase is used to make the branching paths in history cleaner and repository structure linear.

It is also used to keep the branches created by you private, as after rebasing and pushing the changes to server, if you delete your branch, there will be no evidence of branch you have worked upon. So your branch is now your local concern.

After doing rebase we also get rid of an extra commit which we used to see if we do normal merge.

And yes one still needs to do merge after a successful rebase as rebase command just puts your work on top of the branch you mentioned during rebase say master and makes the first commit of your branch as a direct descendant of the master branch. This means we can now do a fast forward merge to bring changes from this branch to master branch.

Some practical examples, somewhat connected to large scale development where gerrit is used for review and delivery integration.

I merge when i uplift my feature branch to a fresh remote master. This gives minimal uplift work and it’s easy to follow the history of the feature development in for example gitk.

 git fetch git checkout origin/my_feature git merge origin/master git commit git push origin HEAD:refs/for/my_feature 

I merge when I prepare a delivery commit.

 git fetch git checkout origin/master git merge --squash origin/my_feature git commit git push origin HEAD:refs/for/master 

I rebase when my delivery commit fails integration for whatever reason and I need to update it towards a fresh remote master.

 git fetch git fetch  git checkout FETCH_HEAD git rebase origin/master git push origin HEAD:refs/for/master 

When do I use git rebase ? Almost never, because it rewrites history. git merge is almost always the preferable choice, because it respects what actually happened in your project.