Entendo o cenário apresentado no Pro Git sobre The Perils of Rebasing . O autor basicamente explica como evitar confirmações duplicadas:
Não rebase as confirmações enviadas por push para um repositório público.
Vou lhe contar minha situação específica, porque acho que ela não se encaixa exatamente no cenário do Pro Git e ainda termino com confirmações duplicadas.
Digamos que eu tenha duas ramificações remotas com suas contrapartes locais:
origin/master origin/dev
| |
master dev
Todos os quatro ramos contêm os mesmos commits e vou iniciar o desenvolvimento em dev
:
origin/master : C1 C2 C3 C4
master : C1 C2 C3 C4
origin/dev : C1 C2 C3 C4
dev : C1 C2 C3 C4
Após algumas confirmações, envio as alterações para origin/dev
:
origin/master : C1 C2 C3 C4
master : C1 C2 C3 C4
origin/dev : C1 C2 C3 C4 C5 C6 # (2) git push
dev : C1 C2 C3 C4 C5 C6 # (1) git checkout dev, git commit
Eu tenho que voltar master
para fazer uma solução rápida:
origin/master : C1 C2 C3 C4 C7 # (2) git push
master : C1 C2 C3 C4 C7 # (1) git checkout master, git commit
origin/dev : C1 C2 C3 C4 C5 C6
dev : C1 C2 C3 C4 C5 C6
E voltando a dev
refazer as alterações para incluir a correção rápida no meu desenvolvimento atual:
origin/master : C1 C2 C3 C4 C7
master : C1 C2 C3 C4 C7
origin/dev : C1 C2 C3 C4 C5 C6
dev : C1 C2 C3 C4 C7 C5' C6' # git checkout dev, git rebase master
Se eu exibir o histórico de confirmações com o GitX / gitk, notarei que origin/dev
agora contém duas confirmações idênticas C5'
e C6'
diferentes do Git. Agora, se eu empurrar as alterações para origin/dev
este é o resultado:
origin/master : C1 C2 C3 C4 C7
master : C1 C2 C3 C4 C7
origin/dev : C1 C2 C3 C4 C5 C6 C7 C5' C6' # git push
dev : C1 C2 C3 C4 C7 C5' C6'
Talvez eu não entenda completamente a explicação no Pro Git, então gostaria de saber duas coisas:
- Por que o Git duplica esses commits durante o rebasing? Existe uma razão específica para fazer isso, em vez de apenas aplicar
C5
eC6
depoisC7
? - Como posso evitar isso? Seria sensato fazê-lo?
origin/dev
. Quandodev
é rebased, seu histórico é modificado (C5 / C6 removido temporariamente e reaplicado após C7). O histórico de modificações de repositórios enviados é geralmente uma Really Bad Idea ™, a menos que você saiba o que está fazendo. Nesse caso simples, o problema poderia ser resolvido pressionando a força de umdev
para o outroorigin/dev
após a reformulação e notificando qualquer outra pessoa que trabalhasse comorigin/dev
isso, provavelmente eles estão prestes a ter um dia ruim. A melhor resposta, novamente, é "não faça isso ... use a mesclagem"Resposta curta
Você omitiu o fato de executar
git push
, obteve o seguinte erro e prosseguiu com a execuçãogit pull
:Apesar de o Git tentar ser útil, seu conselho sobre 'git pull' provavelmente não é o que você deseja fazer .
Se você é:
git push --force
para atualizar o controle remoto com seus commits pós-REBASE ( como por resposta das user4405677 ).git rebase
em primeiro lugar. Para atualizardev
com as alterações demaster
, você deve, em vez de executargit rebase master dev
, executargit merge master
enquanto estiverdev
( conforme a resposta de Justin ).Uma explicação um pouco mais longa
Cada hash de confirmação no Git é baseado em vários fatores, um dos quais é o hash da confirmação que vem antes dele.
Se você reordenar as confirmações, alterará os hashes de confirmação; rebasing (quando faz alguma coisa) mudará os hashes de confirmação. Com isso, o resultado da execução
git rebase master dev
, ondedev
está fora de sincroniamaster
, criará novas confirmações (e, portanto, hashes) com o mesmo conteúdo que as ativadas,dev
mas com as confirmaçõesmaster
inseridas antes deles.Você pode acabar em uma situação como essa de várias maneiras. Duas maneiras em que posso pensar:
master
quais deseja basear seudev
trabalhodev
que já foram enviadas para um controle remoto, e depois proceder à alteração (reformular as mensagens de confirmação, reordenar confirmações, squash confirma, etc.)Vamos entender melhor o que aconteceu - aqui está um exemplo:
Você tem um repositório:
Em seguida, prossiga para alterar confirmações.
(É aqui que você terá que aceitar minha palavra: existem várias maneiras de alterar confirmações no Git. Neste exemplo, mudei o horário de
C3
, mas você está inserindo novas confirmações, alterando mensagens de confirmação, reordenando confirmações, squashing comete em conjunto, etc.)É aqui que é importante notar que os hashes de confirmação são diferentes. Esse é um comportamento esperado, pois você alterou algo (qualquer coisa) sobre eles. Está tudo bem, MAS:
Tentar empurrar mostrará um erro (e sugerirá que você deve executar
git pull
).Se rodarmos
git pull
, vemos este log:Ou, mostrado de outra maneira:
E agora temos confirmações duplicadas localmente. Se rodássemos,
git push
nós os enviaríamos para o servidor.Para evitar chegar a esse estágio, poderíamos ter corrido
git push --force
(onde corremosgit pull
). Isso teria enviado nossos commits com os novos hashes para o servidor sem problemas. Para corrigir o problema neste estágio, podemos redefinir para antes de executargit pull
:Olhe para o reflog (
git reflog
) para ver o que o commit de hash foi antes de nós corremosgit pull
.Acima, vemos que
ba7688a
era o commit em que estávamos antes de executargit pull
. Com esse hash de commit em mãos, podemos redefinir para that (git reset --hard ba7688a
) e depois executargit push --force
.E nós terminamos.
Mas espere, continuei baseando o trabalho nos commits duplicados
Se você não percebeu que as confirmações foram duplicadas e continuou a trabalhar em cima das confirmações duplicadas, você realmente fez uma bagunça. O tamanho da bagunça é proporcional ao número de confirmações que você tem sobre as duplicatas.
Como é isso:
Ou, mostrado de outra maneira:
Nesse cenário, queremos remover as confirmações duplicadas, mas manter as confirmações que baseamos nelas - queremos manter C6 a C10. Como na maioria das coisas, existem várias maneiras de fazer isso:
Ou:
cherry-pick
cada confirmação (inclusive C6 a C10) nessa nova ramificação e trate essa nova ramificação como canônica.git rebase --interactive $commit
, onde$commit
está a confirmação antes das confirmações duplicadas 2 . Aqui, podemos excluir completamente as linhas das duplicatas.1 Não importa qual dos dois você escolher,
ba7688a
ou2a2e220
funciona bem.2 No exemplo seria
85f59ab
.TL; DR
Defina
advice.pushNonFastForward
comofalse
:fonte
git push
é--force-with-lease
hoje em dia, como é um padrão melhorAcho que você pulou um detalhe importante ao descrever seus passos. Mais especificamente, sua última etapa,
git push
no desenvolvimento , na verdade causaria um erro, pois normalmente você não pode enviar alterações não rápidas.Como você fez
git pull
antes do último envio, que resultou em uma consolidação de mesclagem com C6 e C6 'como pais, e é por isso que ambos permanecerão listados no log. Um formato de log mais bonito pode ter tornado mais óbvio que são ramificações mescladas de confirmações duplicadas.Ou você fez um
git pull --rebase
(ou sem explícito,--rebase
se isso estiver implícito na sua configuração), que retirou os C5 e C6 originais de volta no seu desenvolvedor local (e re-redimensionou os seguintes para novos hashes, C7 'C5' 'C6' ').Uma maneira de sair disso poderia ter sido
git push -f
forçar o empurrão quando deu o erro e limpar C5 C6 da origem, mas se alguém mais os tiver puxado antes de você os limpar, você terá muito mais problemas. basicamente todo mundo que possui C5 C6 precisaria executar etapas especiais para se livrar deles. É exatamente por isso que eles dizem que você nunca deve refazer nada que já tenha sido publicado. Ainda é possível se a publicação for dentro de uma equipe pequena.fonte
git pull
é crucial. Sua recomendaçãogit push -f
, embora perigosa, é provavelmente o que os leitores estão procurando.git push --force
, apenas para ver o que o Git faria. Eu aprendi muito sobre o Git desde então e hoje em diarebase
faz parte do meu fluxo de trabalho normal. No entanto,git push --force-with-lease
evito substituir o trabalho de outra pessoa.--force-with-lease
é um bom padrão, eu vou deixar um comentário sob a minha resposta bemEu descobri que, no meu caso, esse problema é consequência de um problema de configuração do Git. (Envolver e puxar)
Descrição do problema:
Sintomas: confirma duplicado na ramificação filha após a rebase, implicando várias mesclagens durante e após a rebase.
Fluxo de trabalho: Aqui estão as etapas do fluxo de trabalho que eu estava executando:
Como conseqüências desse fluxo de trabalho, a duplicação de todos os commits de "Feature-branch" desde a rebase anterior ... :-(
O problema ocorreu devido a mudanças nas ramificações filhas antes da rebase. A configuração padrão do Git é "mesclar". Isso está alterando os índices de confirmações executadas na ramificação filha.
A solução: no arquivo de configuração do Git, configure o pull para funcionar no modo rebase:
Espero que possa ajudar JN Grx
fonte
Você pode ter extraído de um ramo remoto diferente do seu atual. Por exemplo, você pode ter saído do Master quando sua filial está desenvolvendo o rastreamento. O Git aplicará obedientemente cópias confirmadas duplicadas se extraídas de um ramo não rastreado.
Se isso acontecer, você pode fazer o seguinte:
Onde
n == <number of duplicate commits that shouldn't be there.>
Em seguida, verifique se você está puxando da ramificação correta e execute:
Usar essa opção
--rebase
garantirá que você não esteja adicionando confirmações estranhas, o que pode atrapalhar o histórico de confirmação.Aqui está um pouco de mão para git rebase.
fonte