Perguntas sobre fluxo de trabalho e rebase vs mesclagem do Git

971

Estou usando o Git agora há alguns meses em um projeto com outro desenvolvedor. Eu tenho vários anos de experiência com SVN , então acho que trago muita bagagem para o relacionamento.

Ouvi dizer que o Git é excelente para ramificação e fusão e, até agora, simplesmente não o vejo. Claro, a ramificação é simples, mas quando tento me fundir, tudo vai para o inferno. Agora, estou acostumado com isso no SVN, mas parece-me que eu apenas troquei um sistema de versões subpars por outro.

Meu parceiro me diz que meus problemas decorrem do meu desejo de mesclar-se à vontade e que eu deveria estar usando rebase em vez de mesclar em muitas situações. Por exemplo, aqui está o fluxo de trabalho que ele estabeleceu:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature
git checkout master
git merge my_new_feature

Essencialmente, crie uma ramificação de recurso, SEMPRE rebase do mestre para o ramo e mescle do ramo de volta para o mestre. Importante notar é que a filial sempre permanece local.

Aqui está o fluxo de trabalho com o qual comecei

clone remote repository
create my_new_feature branch on remote repository
git checkout -b --track my_new_feature origin/my_new_feature
..work, commit, push to origin/my_new_feature
git merge master (to get some changes that my partner added)
..work, commit, push to origin/my_new_feature
git merge master
..finish my_new_feature, push to origin/my_new_feature
git checkout master
git merge my_new_feature
delete remote branch
delete local branch

Existem duas diferenças essenciais (eu acho): eu uso a mesclagem sempre em vez de rebasear e empurro minha ramificação de recursos (e minha ramificação de recursos se confirma) para o repositório remoto.

Meu raciocínio para a ramificação remota é que quero fazer backup do meu trabalho enquanto estou trabalhando. Nosso backup é feito automaticamente e pode ser restaurado se algo der errado. Meu laptop não é, ou não tão completamente. Portanto, eu odeio ter código no meu laptop que não seja espelhado em outro lugar.

Meu raciocínio para a mesclagem em vez de rebase é que a mesclagem parece ser padrão e a rebase parece ser um recurso avançado. Meu pressentimento é que o que estou tentando fazer não é uma configuração avançada, portanto a recuperação deve ser desnecessária. Até examinei o novo livro de programação pragmática sobre o Git, e eles cobrem extensivamente a mesclagem e mal mencionam rebase.

De qualquer forma, eu estava acompanhando meu fluxo de trabalho em uma ramificação recente e, quando tentei mesclá-lo de volta ao master, tudo foi para o inferno. Havia toneladas de conflitos com coisas que não deveriam ter importância. Os conflitos simplesmente não faziam sentido para mim. Levei um dia para resolver tudo e, eventualmente, culminou em um empurrão forçado para o mestre remoto, já que meu mestre local todos os conflitos foram resolvidos, mas o remoto ainda não estava feliz.

Qual é o fluxo de trabalho "correto" para algo assim? O Git deve tornar a ramificação e a fusão super fáceis, e eu simplesmente não estou vendo.

Atualização 2011-04-15

Essa parece ser uma pergunta muito popular, então pensei em atualizar com meus dois anos de experiência desde a primeira vez que perguntei.

Acontece que o fluxo de trabalho original está correto, pelo menos no nosso caso. Em outras palavras, é isso que fazemos e funciona:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge my_new_feature

De fato, nosso fluxo de trabalho é um pouco diferente, pois tendemos a fazer mesclagens de squash em vez de mesclas brutas. ( Nota: isso é controverso, veja abaixo. ) Isso nos permite transformar todo o ramo de recursos em um único commit no master. Em seguida, excluímos nosso ramo de recursos. Isso nos permite estruturar logicamente nossos commits no master, mesmo que eles estejam um pouco confusos em nossos branches. Então, é isso que fazemos:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge --squash my_new_feature
git commit -m "added my_new_feature"
git branch -D my_new_feature

Controvérsia de mesclagem de squash - Como vários comentadores apontaram, a mesclagem de squash jogará fora todo o histórico em sua ramificação de recursos. Como o nome indica, esmaga todos os commits em um único. Para pequenos recursos, isso faz sentido, pois o condensa em um único pacote. Para recursos maiores, provavelmente não é uma boa ideia, especialmente se o seu commit individual já é atômico. Realmente se resume à preferência pessoal.

Solicitações Pull do Github e Bitbucket (outras?) - Caso você esteja se perguntando como a mesclagem / rebase se relaciona às Solicitações Pull, eu recomendo seguir todas as etapas acima até estar pronto para voltar ao master. Em vez de mesclar manualmente com o git, você apenas aceita o PR. Observe que isso não fará uma mescla de squash (pelo menos não por padrão), mas não-squash e avanço rápido é a convenção de mesclagem aceita na comunidade Pull Request (tanto quanto eu sei). Especificamente, funciona assim:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git push # May need to force push
...submit PR, wait for a review, make any changes requested for the PR
git rebase master
git push # Will probably need to force push (-f), due to previous rebases from master
...accept the PR, most likely also deleting the feature branch in the process
git checkout master
git branch -d my_new_feature
git remote prune origin

Eu vim a amar o Git e nunca mais quero voltar ao SVN. Se estiver com dificuldades, fique com ela e, eventualmente, você verá a luz no fim do túnel.

Micah
fonte
31
Infelizmente, o novo livro de programação Pragmstic foi escrito principalmente sobre o uso do Git enquanto você ainda pensa no SVN e, nesse caso, enganou você. No Git, o rebase mantém as coisas simples quando podem ser. Sua experiência pode dizer que seu fluxo de trabalho não funciona no Git, não que o Git não funcione.
Paul
18
Eu não recomendaria a mesclagem de squash nesse caso, pois não salva informações sobre o que é mesclado (como svn, mas não mergeinfo aqui).
Marius K
7
Adoro a nota na parte inferior, tive uma experiência semelhante de luta com o Git, mas agora luto para imaginar não usá-lo. Obrigado pela explicação final também, ajudou muito com rebasea compreensão
Jon Phenow
6
Depois de concluir o recurso, você não deve refazer a última atualização antes de mesclar new_feature para dominar?
softarn
17
Seu fluxo de trabalho perde todo o histórico de consolidação da ramificação excluída :(
Max Nanasy

Respostas:

372

"Conflitos" significam "evoluções paralelas de um mesmo conteúdo". Portanto, se for "tudo para o inferno" durante uma mesclagem, significa que você tem evoluções maciças no mesmo conjunto de arquivos.

A razão pela qual uma rebase é melhor do que uma mesclagem é que:

  • você reescreve seu histórico de consolidação local com o mestre (e reaplica seu trabalho, resolvendo qualquer conflito)
  • a mesclagem final certamente será de "avanço rápido", porque terá todo o histórico de consolidação do mestre, além de apenas suas alterações para se aplicar novamente.

Confirmo que o fluxo de trabalho correto nesse caso (evoluções no conjunto comum de arquivos) é primeiro rebase e depois mesclado .

No entanto, isso significa que, se você enviar por push sua ramificação local (por motivo de backup), essa ramificação não deverá ser puxada (ou pelo menos usada) por mais ninguém (já que o histórico de confirmação será reescrito pelo rebase sucessivo).


Nesse tópico (rebase e depois mesclar o fluxo de trabalho), barraponto menciona nos comentários duas postagens interessantes, ambas de randyfay.com :

Usando essa técnica, seu trabalho sempre fica no topo do ramo público como um patch atualizado com o atual HEAD.

( existe uma técnica semelhante para o bazar )

VonC
fonte
27
Para uma técnica que permite rebasing e partilha, consulte softwareswirl.blogspot.com/2009/04/...
mhagger
2
randyfay.com/node/91 e randyfay.com/node/89 são leituras maravilhosas. esses artigos me fizeram entender o que estava desgastado no meu fluxo de trabalho e qual seria o fluxo de trabalho ideal.
Capi Etheriel
só para esclarecer, refazer a transição da ramificação principal para o local é basicamente atualizar qualquer histórico que o local tenha perdido e que o mestre tenha conhecimento após qualquer tipo de mesclagem?
hellatan
@ Dtan o que eu descrevo aqui é rebasing local em cima do mestre. Você não está atualizando exatamente o histórico local, mas reaplicando o histórico local sobre o mestre para resolver qualquer conflito dentro da ramificação local.
VonC
386

TL; DR

Um fluxo de trabalho git rebase não o protege de pessoas que são ruins na resolução de conflitos ou que estão acostumadas a um fluxo de trabalho SVN, como sugerido em Evitando Desastres Git: Uma História Gory . Isso apenas torna a resolução de conflitos mais tediosa para eles e dificulta a recuperação de uma resolução ruim de conflitos. Em vez disso, use diff3 para que não seja tão difícil em primeiro lugar.


O rebase do fluxo de trabalho não é melhor para a resolução de conflitos!

Eu sou muito pró-rebase para limpar a história. No entanto, se algum dia eu entrar em conflito, abortarei imediatamente a rebase e, em vez disso, faço uma mesclagem! Realmente me parece que as pessoas estão recomendando um fluxo de trabalho de rebase como uma alternativa melhor a um fluxo de trabalho de mesclagem para resolução de conflitos (que é exatamente o que essa pergunta tratava).

Se for "tudo para o inferno" durante uma mesclagem, será "tudo para o inferno" durante uma rebase, e potencialmente muito mais inferno também! Aqui está o porquê:

Razão # 1: resolver conflitos uma vez, em vez de uma vez para cada confirmação

Quando você refaz o processo em vez de mesclar, terá que executar a resolução de conflitos tantas vezes quantas as que se compromete a refazer o mesmo conflito!

Cenário real

Eu ramifico do mestre para refatorar um método complicado em um galho. Meu trabalho de refatoração é composto por 15 confirmações totais, enquanto trabalho para refatorá-lo e obter revisões de código. Parte da minha refatoração envolve a fixação de guias e espaços mistos que estavam presentes no mestre antes. Isso é necessário, mas infelizmente entrará em conflito com qualquer alteração feita posteriormente neste método no master. Com certeza, enquanto estou trabalhando nesse método, alguém faz uma alteração simples e legítima no mesmo método no ramo mestre que deve ser mesclado às minhas alterações.

Quando é hora de mesclar minha ramificação novamente com o mestre, tenho duas opções:

git merge: Eu tenho um conflito. Eu vejo a mudança que eles fizeram para dominar e fundi-la com (o produto final) da minha filial. Feito.

git rebase: eu tenho um conflito com meu primeiro commit. Eu resolvo o conflito e continuo a rebase. Eu tenho um conflito com meu segundo commit. Eu resolvo o conflito e continuo a rebase. Eu tenho um conflito com meu terceiro commit. Eu resolvo o conflito e continuo a rebase. Eu tenho um conflito com meu quarto commit. Eu resolvo o conflito e continuo a rebase. Eu tenho um conflito com o meu quinto commit. Eu resolvo o conflito e continuo a rebase. Eu tenho um conflito com meu sexto commit. Eu resolvo o conflito e continuo a rebase. Eu tenho um conflito com o meu sétimoconfirmar. Eu resolvo o conflito e continuo a rebase. Eu tenho um conflito com o meu oitavo commit. Eu resolvo o conflito e continuo a rebase. Eu tenho um conflito com o meu nono commit. Eu resolvo o conflito e continuo a rebase. Eu tenho um conflito com meu décimo commit. Eu resolvo o conflito e continuo a rebase. Eu tenho um conflito com o meu décimo primeiro compromisso. Eu resolvo o conflito e continuo a rebase. Eu tenho um conflito com meu décimo segundo compromisso. Eu resolvo o conflito e continuo a rebase. Eu tenho um conflito com meu décimo terceiro compromisso. Eu resolvo o conflito e continuo a rebase. Eu tenho um conflito com o meu décimo quartoconfirmar. Eu resolvo o conflito e continuo a rebase. Eu tenho um conflito com meu décimo quinto commit. Eu resolvo o conflito e continuo a rebase.

Você só pode estar brincando se esse for o seu fluxo de trabalho preferido. Basta uma correção de espaço em branco que entre em conflito com uma alteração feita no mestre e todas as confirmações entrarão em conflito e deverão ser resolvidas. E este é um cenário simples , com apenas um conflito de espaço em branco. O céu proíbe que você tenha um conflito real que envolva grandes alterações de código nos arquivos e precise resolvê- lo várias vezes.

Com toda a resolução extra de conflito que você precisa, apenas aumenta a possibilidade de cometer um erro . Mas erros são bons no git desde que você pode desfazer, certo? Exceto, é claro ...

Razão # 2: com rebase, não há como desfazer!

Penso que todos podemos concordar que a resolução de conflitos pode ser difícil e também que algumas pessoas são muito más. Pode ser muito propenso a erros, e por isso é tão bom que o git facilita a desfazer!

Quando você mescla uma ramificação, o git cria uma consolidação de mesclagem que pode ser descartada ou alterada se a resolução do conflito for ruim. Mesmo se você já enviou a confirmação de mesclagem incorreta para o git revertrepositório público / autorizado, você pode usar para desfazer as alterações introduzidas pela mesclagem e refazer a mesclagem corretamente em uma nova consolidação de mesclagem.

Quando você refaz uma ramificação, no caso provável de a resolução de conflitos ser incorreta, você está ferrado. Agora, todo commit contém a mesclagem incorreta e você não pode simplesmente refazer a rebase *. Na melhor das hipóteses, você deve voltar e alterar cada um dos commit afetados. Não tem graça.

Após uma reformulação, é impossível determinar o que originalmente fazia parte dos commits e o que foi introduzido como resultado da má resolução de conflitos.

* Pode ser possível desfazer uma nova recuperação se você conseguir extrair os antigos refs dos logs internos do git ou criar uma terceira ramificação que aponte para o último commit antes de refazer a recuperação.

Tire o inferno da resolução de conflitos: use diff3

Tome este conflito, por exemplo:

<<<<<<< HEAD
TextMessage.send(:include_timestamp => true)
=======
EmailMessage.send(:include_timestamp => false)
>>>>>>> feature-branch

Olhando para o conflito, é impossível dizer o que cada ramo mudou ou qual era sua intenção. Esta é a maior razão na minha opinião, por que a resolução de conflitos é confusa e difícil.

diff3 para o resgate!

git config --global merge.conflictstyle diff3

Quando você usa o diff3, cada novo conflito terá uma terceira seção, o ancestral comum mesclado.

<<<<<<< HEAD
TextMessage.send(:include_timestamp => true)
||||||| merged common ancestor
EmailMessage.send(:include_timestamp => true)
=======
EmailMessage.send(:include_timestamp => false)
>>>>>>> feature-branch

Primeiro examine o ancestral comum mesclado. Em seguida, compare cada lado para determinar a intenção de cada ramo. Você pode ver que HEAD mudou o EmailMessage para TextMessage. Sua intenção é alterar a classe usada para TextMessage, passando os mesmos parâmetros. Você também pode ver que a intenção do ramo de recurso é passar false em vez de true para a opção: include_timestamp. Para mesclar essas alterações, combine a intenção de ambos:

TextMessage.send(:include_timestamp => false)

Em geral:

  1. Compare o ancestral comum com cada ramo e determine qual ramo tem a alteração mais simples
  2. Aplique essa alteração simples à versão do código da outra ramificação, para que ela contenha as alterações mais simples e mais complexas
  3. Remova todas as seções do código de conflito que não sejam as que você acabou de mesclar nas alterações

Alternativo: resolva aplicando manualmente as alterações da ramificação

Finalmente, alguns conflitos são terríveis de entender, mesmo com diff3. Isso acontece especialmente quando o diff encontra linhas em comum que não são semanticamente comuns (por exemplo, os dois ramos tinham uma linha em branco no mesmo lugar!). Por exemplo, um ramo altera a indentação do corpo de uma classe ou reordena métodos semelhantes. Nesses casos, uma melhor estratégia de resolução pode ser examinar a alteração de ambos os lados da mesclagem e aplicar manualmente o diff ao outro arquivo.

Vejamos como podemos resolver um conflito em um cenário em que a fusão é em origin/feature1que lib/message.rbconflito.

  1. Decida se nossa filial atualmente com check-out ( HEAD, ou --ours) ou a filial que estamos mesclando ( origin/feature1, ou --theirs) é uma alteração mais simples de aplicar. O uso de diff com triple dot ( git diff a...b) mostra as alterações ocorridas bdesde a última divergência de a, ou em outras palavras, comparar o ancestral comum de aeb com b.

    git diff HEAD...origin/feature1 -- lib/message.rb # show the change in feature1
    git diff origin/feature1...HEAD -- lib/message.rb # show the change in our branch
    
  2. Confira a versão mais complicada do arquivo. Isso removerá todos os marcadores de conflito e usará o lado que você escolher.

    git checkout --ours -- lib/message.rb   # if our branch's change is more complicated
    git checkout --theirs -- lib/message.rb # if origin/feature1's change is more complicated
    
  3. Com a alteração complicada marcada, puxe o diff da alteração mais simples (consulte a etapa 1). Aplique cada alteração desse diff ao arquivo conflitante.

Edward Anderson
fonte
4
Como a fusão de todos os conflitos de uma só vez funcionaria melhor do que as confirmações individuais? Eu já tenho problemas ao mesclar confirmações únicas (especialmente de pessoas que não dividem confirmações em partes lógicas E fornecem testes suficientes para verificação). Além disso, rebase não é pior do que mesclar quando se trata de opções de backup, o uso inteligente de rebase interativo e ferramentas como tortoisegit (que permite a seleção de quais confirma incluir) ajudará muito.
prusswan
8
Sinto como se tivesse abordado o motivo no # 1. Se as confirmações individuais não forem logicamente consistentes, mais um motivo para mesclar a ramificação logicamente consistente, para que você possa realmente entender o conflito. Se o commit 1 for com erros e o commit 2 o corrigir, a mesclagem do commit 1 será confusa. Há razões legítimas para você ter 15 conflitos seguidos, como o que descrevi acima. Além disso, seu argumento para rebase não ser pior é um tanto infundado. Rebase mistura mesclagens ruins com os commits originais bons e não deixa os commits por aí para permitir que você tente novamente. Mesclar faz.
Edward Anderson
6
Eu concordo plenamente com você, nilbus. Ótimo post; isso esclarece algumas coisas. Gostaria de saber se rerere seria alguma ajuda aqui embora. Além disso, obrigado pela sugestão de usar o diff3, eu definitivamente vou mudar esse agora.
Der
45
+1 por me contar apenas sobre o diff3 - quantas vezes estava olhando para um conflito incompreensível xingando quem é responsável por não me dizer o que o ancestral comum tinha a dizer. Muito obrigado.
John
4
Essa deveria ter sido a resposta aceita. O fluxo de trabalho de rebase também é horrível porque oculta o fato de que houve uma grande divergência na base de código em algum momento, o que pode ser útil para saber se você deseja entender como o código que você está visualizando foi escrito. Somente pequenos ramos que não conflitem devem ser rebatizados para o mestre.
Robert Rüger
32

No meu fluxo de trabalho, refiz o máximo possível (e tento fazê-lo com frequência. Não deixar que as discrepâncias se acumulem reduz drasticamente a quantidade e a gravidade das colisões entre ramificações).

No entanto, mesmo em um fluxo de trabalho principalmente baseado em rebase, há um lugar para mesclagens.

Lembre-se de que a mesclagem realmente cria um nó que tem dois pais. Agora considere a seguinte situação: Eu tenho duas ramificações de recurso independentes A e B e agora quero desenvolver coisas no ramo de recurso C, que depende de A e B, enquanto A e B estão sendo revisados.

O que faço então é o seguinte:

  1. Crie (e faça o checkout) o ramo C sobre A.
  2. Mesclar com B

Agora, o ramo C inclui alterações de A e B, e posso continuar desenvolvendo. Se eu fizer alguma alteração em A, reconstruo o gráfico de ramificações da seguinte maneira:

  1. crie o ramo T no novo topo de A
  2. mesclar T com B
  3. rebase C em T
  4. excluir ramo T

Dessa forma, eu posso realmente manter gráficos arbitrários de ramificações, mas fazer algo mais complexo do que a situação descrita acima já é muito complexo, uma vez que não há ferramenta automática para fazer o rebase quando o pai muda.

Alex Gontmakher
fonte
1
Você poderia conseguir o mesmo com apenas rebotes. A mesclagem não é realmente necessária aqui (exceto se você não deseja duplicar as confirmações - mas dificilmente vejo isso como argumento).
Odwl 25/05/09
1
Na verdade, não quero duplicar os commits. Gostaria de manter a estrutura de bordo do meu trabalho o mais limpa possível. Mas isso é uma questão de gosto pessoal e não é necessariamente adequado para todos.
Alex Gontmakher
Concordo 100% com o primeiro parágrafo. (A resposta de @ Edward funciona onde não é esse o caso, mas eu prefiro que todos os projetos do mundo funcionem como você sugere). O restante da resposta parece um tanto absurdo no sentido de que trabalhar em C enquanto A e B estão em andamento já é meio arriscado (pelo menos na medida em que realmente depende de A e B), e mesmo no final você provavelmente não manteria as fusões (C seria rebased no topo dos mais recentes e melhores).
Alois Mahdal
23

NÃO use a origem git push --mirror SOB QUASE CIRCUNSTÂNCIA.

Ele não pergunta se você tem certeza de que deseja fazer isso e é melhor ter certeza, porque isso apagará todas as suas ramificações remotas que não estão na sua caixa local.

http://twitter.com/dysinger/status/1273652486

Scott Brown
fonte
6
Ou não faça coisas que não tenha certeza de qual será o resultado? Uma máquina que eu costumava administrar tinha Instructions to this machine may lead to unintended consequences, loss of work/data, or even death (at the hands of the sysad). Remember that you are solely responsible for the consequences of your actions no MOTD.
richo 9/09/11
usá-lo se você tem um repo espelhado (embora no meu caso agora é executado por um usuário especial no repo de origem no pós-receber gancho)
prusswan
14

Depois de ler sua explicação, tenho uma pergunta: será que você nunca fez uma

git checkout master
git pull origin
git checkout my_new_feature

antes de fazer o 'git rebase / merge master' no seu ramo de recursos?

Porque o seu ramo principal não será atualizado automaticamente a partir do repositório do seu amigo. Você tem que fazer isso com o git pull origin. Ou seja, talvez você sempre se recupere de um ramo mestre local que nunca muda? E então, chegou a hora do envio, você está enviando um repositório com confirmações (locais) que você nunca viu e, portanto, o envio falha.

Knweiss
fonte
13

Na sua situação, acho que seu parceiro está correto. O que é bom em rebasear é que, para quem está de fora, suas alterações parecem que tudo aconteceu em uma sequência limpa por si mesmas. Isso significa

  • suas alterações são muito fáceis de revisar
  • você pode continuar fazendo confirmações pequenas e agradáveis ​​e, no entanto, pode tornar públicos os conjuntos dessas confirmações (mesclando-os no mestre) de uma só vez
  • quando você olha para o ramo principal público, verá diferentes séries de confirmações para diferentes recursos de diferentes desenvolvedores, mas elas não serão todas misturadas

Você ainda pode continuar enviando sua ramificação de desenvolvimento privada para o repositório remoto por razões de backup, mas outras pessoas não devem tratá-la como uma ramificação "pública", pois você estará reestruturando. BTW, um comando fácil para fazer isso é git push --mirror origin.

O artigo Empacotando software usando o Git faz um bom trabalho explicando as vantagens em mesclar versus rebasear. É um contexto um pouco diferente, mas os princípios são os mesmos - basicamente se resume a se suas filiais são públicas ou privadas e como você planeja integrá-las à linha principal.

Pat Notz
fonte
1
O link para o software de empacotamento usando o git não funciona mais. Não foi possível encontrar um bom link para editar a resposta original.
Chetan 31/03
Você não deve espelhar origin, deve espelhar para um terceiro repositório de backup dedicado.
Miral
12

De qualquer forma, eu estava acompanhando meu fluxo de trabalho em uma ramificação recente e, quando tentei mesclá-lo de volta ao master, tudo foi para o inferno. Havia toneladas de conflitos com coisas que não deveriam ter importância. Os conflitos simplesmente não faziam sentido para mim. Levei um dia para resolver tudo e, eventualmente, culminou em um empurrão forçado para o mestre remoto, já que meu mestre local todos os conflitos foram resolvidos, mas o remoto ainda não estava feliz.

Nos fluxos de trabalho sugeridos pelo seu parceiro nem pelos seus, você deve encontrar conflitos que não faziam sentido. Mesmo se você tiver, se estiver seguindo os fluxos de trabalho sugeridos, após a resolução, não será necessário um envio "forçado". Isso sugere que você realmente não mesclou a ramificação para a qual estava empurrando, mas teve que empurrar uma ramificação que não era descendente da ponta remota.

Eu acho que você precisa olhar atentamente para o que aconteceu. Alguém poderia (deliberadamente ou não) rebobinar a ramificação principal remota entre a criação da ramificação local e o ponto em que você tentou mesclá-la novamente à ramificação local?

Comparado a muitos outros sistemas de controle de versão, descobri que o uso do Git envolve menos luta com a ferramenta e permite que você trabalhe nos problemas que são fundamentais para os fluxos de origem. O Git não executa mágica; portanto, mudanças conflitantes causam conflitos, mas deve facilitar a gravação, acompanhando a parentalidade de confirmação.

CB Bailey
fonte
Você está sugerindo que o OP teve alguma recuperação ou erro não descoberto em seu processo, certo?
27510 krosenvold
8

"Mesmo que você seja um desenvolvedor único com apenas algumas ramificações, vale a pena adquirir o hábito de usar rebase e mesclar adequadamente. O padrão básico de trabalho será semelhante a:

  • Crie uma nova ramificação B a partir da ramificação existente A

  • Adicionar / confirmar alterações na ramificação B

  • Rebase atualizações da ramificação A

  • Mesclar alterações da ramificação B na ramificação A "

https://www.atlassian.com/git/tutorials/merging-vs-rebasing/

Rakka Rage
fonte
7

Pelo que observei, o git merge tende a manter os ramos separados mesmo após a mesclagem, enquanto o rebase mesclado o combina em um único ramo. O último sai muito mais limpo, enquanto no primeiro seria mais fácil descobrir quais confirmações pertencem a qual filial, mesmo após a fusão.

Pepe
fonte
4

Com o Git, não há fluxo de trabalho "correto". Use o que flutua no seu barco. No entanto, se você constantemente entra em conflito ao mesclar filiais, talvez você deva coordenar melhor seus esforços com seus colegas desenvolvedores. Parece que vocês dois continuam editando os mesmos arquivos. Além disso, observe as palavras-chave em branco e subversão (ou seja, "$ Id $" e outras).

Bombe
fonte
0

Eu só uso o rebase do fluxo de trabalho, porque é visualmente mais claro (não apenas no GitKraken, mas também no Intellij e no gitk, mas eu recomendo o primeiro): você tem um ramo, ele se origina do mestre e volta ao mestre . Quando o diagrama estiver limpo e bonito, você saberá que nada vai para o inferno, nunca .

insira a descrição da imagem aqui

Meu fluxo de trabalho é quase o mesmo do seu, mas com apenas uma pequena diferença: eu me squashcomprometo em um no meu ramo local antes do rebasemeu ramo para as alterações mais recentes master, porque:

rebasetrabalha com base em cada confirmação

o que significa que, se você tiver 15 confirmações alterando a mesma linha master, precisará verificar 15 vezes se não fizer squash, mas o que importa é o resultado final, certo?

Portanto, todo o fluxo de trabalho é:

  1. Faça check-out mastere puxe para garantir que você tenha a versão mais recente

  2. A partir daí, crie uma nova ramificação

  3. Faça o seu trabalho lá, você pode se comprometer livremente várias vezes e enviar para o controle remoto, sem preocupações, porque é o seu ramo.

  4. Se alguém lhe disser "ei, meu PR / MR foi aprovado, agora está mesclado para dominar", você pode buscá-los / puxá-los. Você pode fazê-lo a qualquer momento ou na etapa 6.

  5. Depois de fazer todo o seu trabalho, confirme-os e, se você tiver vários commits, esmague-os (eles são todos o seu trabalho e quantas vezes você altera uma linha de código não importa; a única coisa importante é a versão final). Empurre ou não, isso não importa.

  6. Faça o checkout para master, pullnovamente , para garantir que você tenha as últimas novidades masterno local. Seu diagrama deve ser semelhante a este:

insira a descrição da imagem aqui

Como você pode ver, você está em sua filial local, que se origina de um status desatualizado master, enquanto master(local e remoto) avançou com as alterações do seu colega.

  1. Efetue o check-out de volta ao seu ramo e refaz o master. Agora você terá apenas um commit, para resolver os conflitos apenas uma vez . (E no GitKraken, você só precisa arrastar seu branch para mastere escolher "Rebase"; outra razão pela qual eu gosto.) Depois disso, você será gostar:

insira a descrição da imagem aqui

  1. Portanto, agora você tem todas as alterações mais recentes master, combinadas com as alterações em sua filial. Agora você pode empurrar para o controle remoto e, se você já empurrou antes, precisará forçar o empurrão; O Git lhe dirá que você não pode simplesmente avançar rapidamente. Isso é normal, por causa do rebase, você alterou o ponto de início do seu ramo. Mas você não deve ter medo: use a força com sabedoria . No final, o controle remoto também é sua ramificação, para que você não afete, mastermesmo que faça algo errado.

  2. Crie PR / MR e aguarde até que seja aprovado, assim masterterá sua contribuição. Parabéns! Agora você pode fazer check-out master, fazer suas alterações e excluir sua filial local para limpar o diagrama. A ramificação remota também deve ser excluída, se isso não for feito quando você a mesclar no mestre.

O diagrama final está limpo e claro novamente:

insira a descrição da imagem aqui

WesternGun
fonte