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.
rebase
a compreensãoRespostas:
"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:
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 :
( existe uma técnica semelhante para o bazar )
git push --force
(em vez de umgit pull --rebase
por exemplo)fonte
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 revert
repositó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:
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!
Quando você usa o diff3, cada novo conflito terá uma terceira seção, o ancestral comum mesclado.
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:
Em geral:
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/feature1
quelib/message.rb
conflito.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 ocorridasb
desde a última divergência dea
, ou em outras palavras, comparar o ancestral comum de aeb com b.Confira a versão mais complicada do arquivo. Isso removerá todos os marcadores de conflito e usará o lado que você escolher.
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.
fonte
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:
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:
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.
fonte
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
fonte
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.Depois de ler sua explicação, tenho uma pergunta: será que você nunca fez uma
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.fonte
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
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.
fonte
origin
, deve espelhar para um terceiro repositório de backup dedicado.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.
fonte
"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/
fonte
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.
fonte
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).
fonte
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 .Meu fluxo de trabalho é quase o mesmo do seu, mas com apenas uma pequena diferença: eu me
squash
comprometo em um no meu ramo local antes dorebase
meu ramo para as alterações mais recentesmaster
, porque: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 é:
Faça check-out
master
e puxe para garantir que você tenha a versão mais recenteA partir daí, crie uma nova ramificação
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.
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.
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.
Faça o checkout para
master
,pull
novamente , para garantir que você tenha as últimas novidadesmaster
no local. Seu diagrama deve ser semelhante a este:Como você pode ver, você está em sua filial local, que se origina de um status desatualizado
master
, enquantomaster
(local e remoto) avançou com as alterações do seu colega.master
e escolher "Rebase"; outra razão pela qual eu gosto.) Depois disso, você será gostar: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,master
mesmo que faça algo errado.Crie PR / MR e aguarde até que seja aprovado, assim
master
terá sua contribuição. Parabéns! Agora você pode fazer check-outmaster
, 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:
fonte