Quando é recomendado usar o Git rebase vs. Git merge?
Ainda preciso mesclar após uma nova recuperação bem-sucedida?
Quando é recomendado usar o Git rebase vs. Git merge?
Ainda preciso mesclar após uma nova recuperação bem-sucedida?
Respostas:
Então, quando você usa um dos dois?
init
um novo repositório, add
o arquivo e commit
. Fazer check-out de uma nova ramificação de recurso ( checkout -b feature
.) Altere o arquivo de texto, confirme e repita para que haja duas novas confirmações na ramificação de recurso. Então checkout master
e merge feature
. Em log
, vejo meu commit inicial no master, seguido pelos dois que foram mesclados do recurso. Se você merge --squash feature
, o recurso for mesclado no mestre, mas não confirmado, o único novo commit no mestre será aquele que você criar.
É simples. Com o rebase, você diz usar outro ramo como a nova base para o seu trabalho.
Se você tem, por exemplo, uma ramificação master
, cria uma ramificação para implementar um novo recurso e diz o nome cool-feature
, é claro que o ramo mestre é a base para o seu novo recurso.
Agora, em um determinado momento, você deseja adicionar o novo recurso implementado na master
ramificação. Você pode simplesmente mudar master
e mesclar o cool-feature
ramo:
$ git checkout master
$ git merge cool-feature
Mas dessa forma, uma nova confirmação fictícia é adicionada. Se você quiser evitar o histórico de espaguete, pode fazer uma nova reformulação :
$ git checkout cool-feature
$ git rebase master
E depois mesclá-lo master
:
$ git checkout master
$ git merge cool-feature
Desta vez, como o ramo de tópicos tem as mesmas confirmações do mestre e confirmadas com o novo recurso, a mesclagem será apenas um avanço rápido.
but this way a new dummy commit is added, if you want to avoid spaghetti-history
- como está ruim?
Sean Schofield
coloca em um comentário: "Rebase também é bom, porque uma vez que você eventualmente mescla suas coisas de volta ao master (o que é trivial como já descrito), você as coloca no" topo "do histórico de confirmação. projetos em que os recursos podem ser escritos, mas mesclados várias semanas depois, você não deseja mesclá-los ao mestre porque eles são "recheados" no caminho mestre de volta à história.Pessoalmente, eu gosto de poder fazer o git log e ver esse recurso recente logo no "topo". Observe que as datas de confirmação são preservadas - a rebase não altera essas informações. "
merge
, rebase
, fast-forward
, etc.) são referentes às manipulações específicas de um gráfico acíclico dirigido. Eles se tornam mais fáceis de raciocinar com esse modelo mental em mente.
Para complementar minha própria resposta mencionada por TSamper ,
uma rebase geralmente é uma boa idéia para ser feita antes da mesclagem, porque a idéia é que você integre ao seu ramo Y
o trabalho do ramo B
no qual irá mesclar.
Porém, novamente, antes da mesclagem, você resolve qualquer conflito em sua ramificação (ou seja: "rebase", como em "repetir meu trabalho em minha ramificação a partir de um ponto recente da ramificação B
).
Se feito corretamente, a mesclagem subsequente de sua ramificação para ramo B
pode ser rápido.
uma mesclagem afeta diretamente a ramificação de destino B
, o que significa que as mesclas devem ser triviais; caso contrário, essa ramificação B
pode demorar muito para voltar a um estado estável (hora de resolver todos os conflitos)
o ponto de mesclar após uma rebase?
No caso que eu descrevo, eu me refiro B
ao meu ramo, apenas para ter a oportunidade de reproduzir meu trabalho de um ponto mais recente B
, mas permanecendo no meu ramo.
Nesse caso, ainda é necessária uma mesclagem para trazer meu trabalho "reproduzido" B
.
O outro cenário ( descrito no Git Ready, por exemplo) é trazer o seu trabalho diretamente B
através de uma nova base (que conserva todos os seus commits legais ou até a oportunidade de reordená-los através de uma nova base interativa).
Nesse caso (em que você rebase enquanto está na ramificação B), você está certo: não é necessária mais nenhuma mesclagem:
Uma árvore Git no padrão quando não mesclamos nem refizemos
obtemos rebasing:
O segundo cenário é o seguinte: como faço para recuperar o novo recurso no master.
O que quero dizer, ao descrever o primeiro cenário de rebase, é lembrar a todos que uma rebase também pode ser usada como uma etapa preliminar para isso (sendo "colocar o novo recurso de volta no master").
Você pode usar o rebase para primeiro trazer o mestre "para dentro" da ramificação de novos recursos: a rebase repetirá as confirmações de novos recursos do HEAD master
, mas ainda no ramo de novos recursos, movendo efetivamente o ponto inicial da ramificação de um antigo mestre confirmado HEAD-master
.
Isso permite que você resolva quaisquer conflitos em sua ramificação (ou seja, isoladamente, enquanto permite que o mestre continue evoluindo em paralelo se o estágio de resolução de conflitos demorar muito).
Em seguida, você pode alternar para mestre e merge new-feature
(ou rebase new-feature
para master
se você quiser preservar alterações feitas em suanew-feature
ramo).
Assim:
master
,.Se você tiver alguma dúvida, use mesclagem.
As únicas diferenças entre uma rebase e uma mesclagem são:
Portanto, a resposta mais curta é escolher reorganizar ou mesclar com base no que você deseja que seu histórico seja .
Existem alguns fatores que você deve considerar ao escolher qual operação usar.
Nesse caso, não faça uma nova recuperação. Rebase destrói a ramificação e esses desenvolvedores terão repositórios quebrados / inconsistentes, a menos que eles usem git pull --rebase
. Essa é uma boa maneira de perturbar outros desenvolvedores rapidamente.
Rebase é uma operação destrutiva. Isso significa que, se você não o aplicar corretamente, poderá perder o trabalho comprometido e / ou interromper a consistência dos repositórios de outros desenvolvedores.
Trabalhei em equipes nas quais todos os desenvolvedores vieram de uma época em que as empresas podiam pagar uma equipe dedicada para lidar com ramificações e fusões. 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.
Algumas equipes usam o modelo de ramificação por recurso, em que cada ramificação representa um recurso (ou correção de bug, 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, essa é uma operação rara). Ou diferencie um recurso comparando duas ramificações (mais comuns). Rebase destruiria o ramo e isso não seria simples.
Também trabalhei em equipes que usavam o modelo de ramificação por desenvolvedor (todos nós já estivemos lá). Nesse caso, o próprio ramo não transmite nenhuma informação adicional (o commit já tem o autor). Não haveria mal em rebater.
A reversão (como em desfazer) de uma rebase é consideravelmente difícil e / ou impossível (se a rebase tiver conflitos) em comparação com a reversão de uma mesclagem. Se você acha que há uma chance de querer reverter, use a mesclagem.
As operações de rebase precisam ser puxadas com um correspondente git pull --rebase
. Se você estiver trabalhando sozinho, poderá se lembrar de qual deve usar no momento apropriado. Se você estiver trabalhando em uma equipe, será muito difícil coordenar. É por isso que a maioria dos fluxos de trabalho de rebase recomenda o uso de rebase para todas as mesclagens (e git pull --rebase
para todos os pulls).
Supondo que você tenha a seguinte mesclagem:
B -- C
/ \
A--------D
Algumas pessoas afirmam que a mesclagem "destrói" o histórico de consolidação, porque se você examinar o log apenas da ramificação principal (A - D), perderá as importantes mensagens de consolidação contidas em B e C.
Se isso fosse verdade, não teríamos perguntas como esta . Basicamente, você verá B e C, a menos que solicite explicitamente para não vê-los (usando --first-parent). É muito fácil tentar por si mesmo.
As duas abordagens se mesclam de maneira diferente, mas não está claro se uma é sempre melhor que a outra e pode depender do fluxo de trabalho do desenvolvedor. Por exemplo, se um desenvolvedor tende a se comprometer regularmente (por exemplo, talvez ele se comprometa duas vezes por dia durante a transição do trabalho para casa), pode haver muitos comprometimentos para um determinado ramo. Muitos desses commits podem não se parecer com o produto final (eu tendem a refatorar minha abordagem uma ou duas vezes por recurso). Se alguém estivesse trabalhando em uma área relacionada do código e tentasse refazer minhas alterações, poderia ser uma operação bastante tediosa.
Se você gosta de usar o alias rm
para rm -rf
"economizar tempo", talvez a redefinição seja a melhor para você.
Eu sempre acho que um dia me depararei com um cenário em que o Git rebase é a ferramenta incrível que resolve o problema. Bem como eu acho que vou encontrar um cenário em que o reflit Git é uma ferramenta incrível que resolve meu problema. Eu trabalho com o Git há mais de cinco anos. Isso não aconteceu.
Histórias confusas nunca foram realmente um problema para mim. Eu nunca apenas leio minha história de commit como um romance emocionante. Na maior parte do tempo, preciso de um histórico. Vou usar a culpa do Git ou o bit do Git de qualquer maneira. Nesse caso, ter a confirmação de mesclagem é realmente útil para mim, porque se a mesclagem introduziu o problema, isso é uma informação significativa para mim.
Sinto-me obrigado a mencionar que pessoalmente amoleci o uso do rebase, embora meu conselho geral ainda permaneça. Recentemente, tenho interagido bastante com o projeto Angular 2 Material . Eles usaram o rebase para manter um histórico de consolidação muito limpo. Isso me permitiu ver com muita facilidade qual commit corrigiu um determinado defeito e se esse commit foi incluído ou não em uma liberação. Serve como um ótimo exemplo de uso do rebase corretamente.
Muitas respostas aqui dizem que a mesclagem transforma todos os seus commits em um e, portanto, sugerimos o uso de rebase para preservar seus commits. Isto está incorreto. E uma má idéia se você já enviou seus commits .
A mesclagem não anula seus commits. Mesclar preserva a história! (basta olhar para o gitk) Rebase reescreve a história, que é uma coisa ruim depois que você a pressionou .
Use mesclagem - não rebase sempre que você já tiver pressionado.
Aqui está a abordagem de Linus (autor do Git) (agora hospedada em meu próprio blog, recuperada pela Wayback Machine ). É uma leitura muito boa.
Ou você pode ler minha própria versão da mesma idéia abaixo.
Rebaseando uma ramificação no mestre:
Por outro lado, mesclando uma ramificação de tópico no mestre:
TLDR: Depende do que é mais importante - uma história organizada ou uma representação verdadeira da sequência de desenvolvimento
Se um histórico organizado é o mais importante, primeiro você deve fazer uma nova redefinição e depois mesclar suas alterações, para que fique claro exatamente qual é o novo código. Se você já empurrou sua ramificação, não faça uma nova recuperação, a menos que possa lidar com as consequências.
Se a representação verdadeira da sequência for a mais importante, você mesclaria sem rebasear.
Mesclar significa: Criar uma única nova confirmação que mescla minhas alterações no destino. Nota: Esse novo commit terá dois pais - o último commit da sua sequência de commit e o último commit do outro ramo que você está mesclando.
Rebase significa: Crie uma nova série de confirmações, usando meu conjunto atual de confirmações como dicas. Em outras palavras, calcule como seriam as minhas alterações se eu tivesse começado a fazê-las a partir do ponto em que estou refazendo. Após o rebase, portanto, talvez seja necessário testar novamente suas alterações e, durante o rebase, você poderá ter alguns conflitos.
Diante disso, por que você faria uma nova recuperação? Apenas para manter o histórico de desenvolvimento claro. Digamos que você esteja trabalhando no recurso X e, quando terminar, integre suas alterações. O destino agora terá um único commit que diria algo como "Adicionado o recurso X". Agora, em vez de mesclar, se você reformulou e mesclou, o histórico de desenvolvimento de destino conteria todas as confirmações individuais em uma única progressão lógica. Isso facilita a revisão das alterações mais tarde. Imagine como seria difícil revisar o histórico de desenvolvimento se 50 desenvolvedores estivessem mesclando vários recursos o tempo todo.
Dito isto, se você já empurrou a ramificação na qual está trabalhando no upstream, não deve se refazer, mas sim mesclar. Para ramificações que não foram enviadas a montante, faça uma nova análise, teste e mesclagem.
Outro momento em que você pode querer refazer a recuperação é quando deseja se livrar das confirmações de sua ramificação antes de avançar a montante. Por exemplo: Confirmações que introduzem algum código de depuração desde o início e outras confirmações adicionais que limpam esse código. A única maneira de fazer isso é realizando um rebase interativo:git rebase -i <branch/commit/tag>
ATUALIZAÇÃO: Você também deseja usar o rebase quando estiver usando o Git para fazer interface com um sistema de controle de versão que não suporta histórico não linear ( por exemplo, o Subversion ). Ao usar a ponte git-svn, é muito importante que as alterações que você mesclar novamente no Subversion sejam uma lista seqüencial de alterações, além das alterações mais recentes no tronco. Existem apenas duas maneiras de fazer isso: (1) recrie manualmente as alterações e (2) use o comando rebase, que é muito mais rápido.
ATUALIZAÇÃO 2: Uma maneira adicional de pensar em uma rebase é que ela permite um tipo de mapeamento do estilo de desenvolvimento para o estilo aceito no repositório em que você está se comprometendo. Digamos que você gosta de se comprometer em 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ê termina o que precisa fazer, você tem uma longa série de confirmações. Agora, digamos que o repositório que você está comprometendo incentive grandes confirmações, portanto, para o trabalho que você está fazendo, seria de esperar uma ou talvez duas confirmações. Como você pega sua sequência de confirmações e as compacta com o que é esperado? Você usaria um rebase interativo e esmagaria seus commits minúsculos em menos pedaços maiores. O mesmo se aplica se o inverso for necessário - se o seu estilo tiver alguns commits grandes, mas o repositório exigia longas sequências de pequenas confirmações. Você usaria uma rebase para fazer isso também. Se você fez a mesclagem, agora enxertou seu estilo de confirmação no repositório principal. Se houver muitos desenvolvedores, você pode imaginar o quão difícil seria seguir um histórico com vários estilos de confirmação diferentes após algum tempo.
ATUALIZAÇÃO 3: Does one still need to merge after a successful rebase?
Sim, você faz. A razão é que uma rebase envolve essencialmente uma "mudança" de confirmações. Como eu disse acima, esses commits são calculados, mas se você tiver 14 commits a partir do ponto de ramificação, assumindo que nada dê errado com sua rebase, você terá 14 commits à frente (do ponto em que está refazendo) depois a rebase está feita. Você tinha uma ramificação antes de uma rebase. Você terá um ramo do mesmo comprimento depois. Você ainda precisa mesclar antes de publicar suas alterações. Em outras palavras, refaça o remanejamento quantas vezes quiser (novamente, apenas se você não tiver promovido suas alterações a montante). Mesclar somente depois que você rebase.
git merge
suporta a --no-ff
opção que o força a fazer uma consolidação de mesclagem.
Embora a fusão seja definitivamente a maneira mais fácil e comum de integrar mudanças, não é a única: Rebase é um meio alternativo de integração.
Compreendendo a mesclagem um pouco melhor
Quando o Git executa uma mesclagem, ele procura três confirmações:
Confirmação de avanço rápido ou mesclagem
Em casos muito simples, um dos dois ramos não tem nenhum novo commit desde que a ramificação aconteceu - seu último commit ainda é o ancestral comum.
Nesse caso, executar a integração é simples: o Git pode simplesmente adicionar todos os commits do outro ramo no topo do commit ancestral comum. No Git, essa forma mais simples de integração é chamada de mesclagem "avanço rápido". Ambos os ramos compartilham exatamente a mesma história.
Em muitos casos, no entanto, os dois ramos avançaram individualmente.
Para fazer uma integração, o Git precisará criar um novo commit que contenha as diferenças entre eles - o commit de mesclagem.
Confirmações humanas e confirmações de mesclagem
Normalmente, um commit é cuidadosamente criado por um ser humano. É uma unidade significativa que envolve apenas as alterações relacionadas e as anota com um comentário.
Um commit de mesclagem é um pouco diferente: em vez de ser criado por um desenvolvedor, ele é criado automaticamente pelo Git. E, em vez de incluir um conjunto de alterações relacionadas, seu objetivo é conectar dois ramos, como um nó. Se você deseja entender uma operação de mesclagem posteriormente, verifique o histórico de ambas as ramificações e o gráfico de consolidação correspondente.
Integrando com Rebase
Algumas pessoas preferem ficar sem esses commits de mesclagem automática. Em vez disso, eles querem que a história do projeto pareça ter evoluído em uma única linha reta. Não resta indicação de que ele foi dividido em várias ramificações em algum momento.
Vamos percorrer uma operação de rebase passo a passo. O cenário é o mesmo dos exemplos anteriores: queremos integrar as alterações do ramo B no ramo A, mas agora usando rebase.
Faremos isso em três etapas
git rebase branch-A // Synchronises the history with branch-A
git checkout branch-A // Change the current branch to branch-A
git merge branch-B // Merge/take the changes from branch-B to branch-A
Primeiro, o Git "desfaz" todas as confirmações no ramo A que ocorreram depois que as linhas começaram a se ramificar (após o comprometimento do ancestral comum). No entanto, é claro, isso não será descartado: em vez disso, você pode pensar nesses commits como "salvos temporariamente".
Em seguida, aplica os commits do ramo B que queremos integrar. Nesse ponto, os dois ramos parecem exatamente iguais.
Na etapa final, as novas confirmações na ramificação A agora são reaplicadas - mas em uma nova posição, em cima das confirmações integradas da ramificação-B (elas são baseadas novamente).
O resultado parece que o desenvolvimento aconteceu em uma linha reta. Em vez de uma consolidação de mesclagem que contém todas as alterações combinadas, a estrutura de consolidação original foi preservada.
Por fim, você obtém uma ramificação limpa da ramificação A, sem confirmações indesejadas e geradas automaticamente.
Nota: Retirado da postagem incrível por git-tower
. As desvantagens de rebase
também é uma boa leitura no mesmo post.
Antes da mesclagem / rebase:
A <- B <- C [master]
^
\
D <- E [branch]
Depois git merge master
:
A <- B <- C
^ ^
\ \
D <- E <- F
Depois git rebase master
:
A <- B <- C <- D' <- E'
(A, B, C, D, E e F são confirmados)
Este exemplo e informações muito mais bem ilustradas sobre o Git podem ser encontradas no Tutorial Básico do Git .
Esta frase entende:
Em geral, a maneira de obter o melhor dos dois mundos é refazer as alterações locais que você fez, mas ainda não as compartilhou, antes de empurrá-las para limpar sua história, mas nunca refazer tudo o que empurrou para algum lugar .
Esta resposta é amplamente orientada em torno do Git Flow . As tabelas foram gerados com o bom ASCII Generator Tabela , e as árvores de história com este comando maravilhoso ( alias como 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)'
As tabelas estão em ordem cronológica inversa para serem mais consistentes com as árvores do histórico. Veja também a diferença entre git merge
e git merge --no-ff
primeiro (você geralmente deseja usar git merge --no-ff
, pois faz com que seu histórico pareça mais próximo da realidade):
git merge
Comandos:
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
Comandos:
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
Primeiro ponto: sempre mescle os recursos no desenvolvimento, nunca os refaça . Isso é uma consequência da regra de ouro do rebaseamento :
A regra de ouro
git rebase
é nunca usá-lo em agências públicas .
Nunca rebase qualquer coisa que você empurrou para algum lugar.
Eu acrescentaria pessoalmente: a menos que seja um ramo de recursos E você e sua equipe estejam cientes das consequências .
Portanto, a questão git merge
vs git rebase
se aplica quase apenas aos ramos do recurso (nos exemplos a seguir, --no-ff
sempre foi usado durante a mesclagem). Observe que, como não tenho certeza de que há uma solução melhor ( existe um debate ), fornecerei apenas como os dois comandos se comportam. No meu caso, prefiro usar git rebase
, pois produz uma árvore de histórico mais agradável :)
git merge
Comandos:
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
Comandos:
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
develop
para uma ramificação de recursogit merge
Comandos:
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 develop
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
Comandos:
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 develop
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
git cherry-pick
Quando você só precisa de uma confirmação específica, git cherry-pick
é uma boa solução (a -x
opção anexa uma linha que diz " (cereja escolhida na confirmação ...) " ao corpo da mensagem de confirmação original, portanto, geralmente é uma boa ideia usá-la - git log <commit_sha1>
para ver isto):
Comandos:
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 <second_commit_sha1>
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
Não sei se posso explicar melhor do que Derek Gourlay ... Basicamente, use em git pull --rebase
vez de git pull
:) O que está faltando no artigo, porém, é que você pode ativá-lo por padrão :
git config --global pull.rebase true
git rerere
Mais uma vez, bem explicado aqui . Simplificando, se você ativá-lo, não precisará mais resolver o mesmo conflito várias vezes.
O livro Pro Git tem uma explicação realmente boa na página de rebasing .
Basicamente, uma mesclagem leva dois commits e os combina.
Uma rebase irá para o ancestral comum nos dois e aplicará as alterações gradualmente umas sobre as outras. Isso cria uma história mais "limpa" e mais linear.
Mas quando você se refaz, você abandona as confirmações anteriores e cria novas. Portanto, você nunca deve refazer um repositório que é público. As outras pessoas que trabalham no repositório o odiarão.
Por essa razão, eu quase me uno exclusivamente. Em 99% das vezes, meus ramos não diferem tanto; portanto, se houver conflitos, é apenas em um ou dois lugares.
O Git rebase é usado para tornar os caminhos de ramificação no histórico mais limpos e a estrutura do repositório linear.
Ele também é usado para manter as ramificações criadas por você em particular, pois, depois de refazer e empurrar as alterações para o servidor, se você excluir sua ramificação, não haverá evidências de ramificações nas quais você trabalhou. Portanto, sua filial agora é sua preocupação local.
Depois de fazer o rebase, também nos livramos de um commit extra que costumávamos ver se fazemos uma mesclagem normal.
E sim, ainda é necessário fazer a mesclagem após um rebase bem-sucedido, pois o comando rebase apenas coloca seu trabalho no ramo que você mencionou durante o rebase, digamos mestre, e faz o primeiro commit do seu ramo como descendente direto do ramo mestre . Isso significa que agora podemos fazer uma junção de avanço rápido para trazer alterações dessa ramificação para a ramificação mestre.
Alguns exemplos práticos, um pouco conectados ao desenvolvimento em larga escala, onde o Gerrit é usado para integração de revisão e entrega:
Eu mesclo quando elevo minha ramificação de recursos para um novo mestre remoto. Isso proporciona um trabalho mínimo de melhoria e é fácil acompanhar o histórico do desenvolvimento de recursos, por exemplo, no gitk .
git fetch
git checkout origin/my_feature
git merge origin/master
git commit
git push origin HEAD:refs/for/my_feature
Mesclar quando preparo uma confirmação de entrega.
git fetch
git checkout origin/master
git merge --squash origin/my_feature
git commit
git push origin HEAD:refs/for/master
Eu me refiz quando minha confirmação de entrega falha na integração por qualquer motivo e preciso atualizá-la para um novo mestre remoto.
git fetch
git fetch <gerrit link>
git checkout FETCH_HEAD
git rebase origin/master
git push origin HEAD:refs/for/master
Foi explicado muitas vezes o que é rebase e o que é mesclagem, mas quando você deve usar o quê?
Quando você deve usar o rebase?
À medida que o Git rebase muda o histórico. Portanto, você não deve usá-lo quando alguém estiver trabalhando no mesmo ramo / se você o tiver pressionado. Mas se você tiver uma filial local, poderá fazer uma mesclagem do mestre de rebase antes de mesclar sua ramificação novamente no mestre para manter um histórico mais limpo. Fazendo isso, depois de mesclar na ramificação principal, não será visível que você tenha usado uma ramificação na ramificação principal - o histórico é "mais limpo", pois você não possui "mesclado .." gerado automaticamente, mas ainda tem o histórico completo em sua ramificação principal sem ter confirmado automaticamente "mesclado ..".
Certifique-se, porém, git merge feature-branch --ff-only
de garantir que não haja conflitos ao criar um único commit ao mesclar seu recurso de volta ao main. Isso é interessante se você estiver usando ramificações de recursos para todas as tarefas em que trabalha, à medida que obtém o histórico da ramificação de recursos, mas não um commit "mesclado .."
Um segundo cenário seria, se você ramificasse de uma ramificação e desejasse saber o que mudou na ramificação principal. Rebase fornece as informações, pois inclui todas as confirmações.
Quando você deve usar a mesclagem?
Quando você não precisa ou deseja ter todo o histórico de uma ramificação de recurso em sua ramificação principal ou se outras pessoas estão trabalhando na mesma ramificação / você o pressionou. Se você ainda deseja ter o histórico, basta mesclar o mestre na ramificação do recurso antes de mesclar a ramificação do recurso no mestre. Isso resultará em uma mesclagem de avanço rápido, na qual você tem o histórico da ramificação de recursos em seu mestre (incluindo a consolidação de mesclagem que estava em sua ramificação de recursos porque você mesclou o mestre a ela).
Quando uso git rebase
? Quase nunca, porque reescreve a história. git merge
é quase sempre a escolha preferível, porque respeita o que realmente aconteceu no seu projeto.