Impulso do Git rejeitado após rebase do ramo de recurso

OK, eu pensei que este fosse um cenário simples, o que eu sinto falta?

Eu tenho um branch master e um branch de feature . Eu faço algum trabalho no master , alguns no feature , e depois um pouco mais no master . Eu acabo com algo assim (a ordem lexicográfica implica a ordem dos commits):

 A--B--C------F--G (master) \ D--E (feature) 

Eu não tenho nenhum problema para git push origin master para manter o master remoto atualizado, nem com git push origin feature (quando no feature ) para manter um backup remoto para o meu trabalho de feature . Até agora, estamos bem.

Mas agora eu quero rebase feature no topo do F--G comete no mestre, então eu git checkout feature e git rebase master . Ainda bom. Agora nós temos:

 A--B--C------F--G (master) \ D'--E' (feature) 

Problema: no momento em que eu quero fazer o backup do novo feature rebased ramificado com git push origin feature , o push é rejeitado, pois a tree mudou devido ao rebasing. Isso só pode ser resolvido com o git push --force origin feature .

Eu odeio usar --force sem ter certeza de que preciso. Então, eu preciso disso? A rebasing implica necessariamente que o próximo push deve ser – --force ?

Este ramo de recurso não é compartilhado com nenhum outro devs, então eu não tenho problema de fato com o push force, não vou perder nenhum dado, a questão é mais conceitual.

O problema é que o git push assume que a ramificação remota pode ser encaminhada rapidamente para sua ramificação local, ou seja, toda a diferença entre ramificações locais e remotas está no local, tendo alguns novos commits no final, assim:

 Z--X--R < - origin/some-branch (can be fast-forwarded to Y commit) \ T--Y <- some-branch 

Quando você executa git rebase commits D e E são aplicados à nova base e novos commits são criados. Isso significa que depois de rebase você tem algo assim:

 A--B--C------F--G--D'--E' < - feature-branch \ D--E <- origin/feature-branch 

Nessa situação, a ramificação remota não pode ser redirecionada para local. Embora, teoricamente, a ramificação local possa ser mesclada em remoto (obviamente, você não precisa dela nesse caso), mas como o git push realiza apenas mesclagens de avanço rápido, isso gera um erro.

E o que a opção --force faz é simplesmente ignorar o estado do ramo remoto e configurá-lo para o commit que você está fazendo nele. Portanto, o git push --force origin feature-branch simplesmente sobrescreve origin/feature-branch com feature-branch local.

Na minha opinião, rebasing feature branches no master e force-los de volta ao repository remoto é OK, contanto que você seja o único que trabalha nesse branch.

Em vez de usar -f ou –force, os desenvolvedores devem usar

 --force-with-lease 

Por quê? Porque verifica o ramo remoto para mudanças que é absolutamente uma boa ideia. Vamos imaginar que James e Lisa estão trabalhando no mesmo ramo de resources e Lisa fez um commit. James agora rebases seu ramo local e é rejeitado ao tentar empurrar. Claro que James acha que isso é devido a rebase e usa –force e rewriteia todas as mudanças de Lisa. Se James tivesse usado –force-com-lease, ele teria recebido um aviso de que há commits feitos por outra pessoa. Não vejo por que alguém usaria –force em vez de –force-with-lease ao pressionar após um rebase.

Eu usaria em vez disso “checkout -b” e é mais fácil de entender.

 git checkout myFeature git rebase master git push origin --delete myFeature git push origin myFeature 

quando você exclui, evita empurrar uma ramificação que contenha SHA ID diferente. Estou excluindo apenas o ramo remoto neste caso.

Uma solução para isso é fazer o que o script de mesclagem de recombinação do msysGit faz – após o rebase, mesclar no antigo header de feature com -s ours . Você acaba com o gráfico de commit:

 A--B--C------F--G (master) \ \ \ D'--E' (feature) \ / \ -- \ / D--E (old-feature) 

… e seu empurrão de feature será um avanço rápido.

Em outras palavras, você pode fazer:

 git checkout feature git branch old-feature git rebase master git merge -s ours old-feature git push origin feature 

(Não testado, mas acho que está certo …)

Pode ou não ser o caso de haver apenas um desenvolvedor neste ramo, que é agora (após o rebase) não alinhado com a origem / recurso.

Como tal, sugiro usar a seguinte sequência:

 git rebase master git checkout -b feature_branch_2 git push origin feature_branch_2 

Sim, novo branch, isso deve resolver isso sem um –force, que eu acho que geralmente é um grande problema git.

Outros responderam sua pergunta. Se você rebase uma ramificação, precisará forçar o envio dessa ramificação.

Rebase e um repository compartilhado geralmente não se dão bem. Isso é rewrite a história. Se outras pessoas estiverem usando esse ramo ou se ramificarem desse ramo, o rebase será bastante desagradável.

Em geral, o rebase funciona bem para o gerenciamento de filial local. O gerenciamento de ramificação remota funciona melhor com mesclagens explícitas (–no-ff).

Também evitamos mesclar o mestre em um ramo de resources. Em vez disso, rebase para masterizar, mas com um novo nome de ramificação (por exemplo, adicionando um sufixo de versão). Isso evita o problema de rebasing no repository compartilhado.

O que há de errado com um git merge master no ramo de feature ? Isso preservará o trabalho que você teve, mantendo-o separado do ramo principal.

 A--B--C------F--G \ \ D--E------H 

Edit: Ah desculpe não leu o seu problema. Você precisará de força ao realizar um rebase . Todos os comandos que modificam o histórico precisarão do argumento --force . Isso é uma falha de segurança para evitar que você perca o trabalho (os antigos D e E seriam perdidos).

Então você realizou um git rebase que fez a tree parecer (embora parcialmente escondida como D e E não estão mais em um branch nomeado):

 A--B--C------F--G \ \ D--E D'--E' 

Então, ao tentar empurrar seu novo branch de feature (com D' e E' nele), você perderia D e E

Minha maneira de evitar o esforço forçado é criar uma nova ramificação e continuar o trabalho nessa nova ramificação e, após alguma estabilidade, remover a ramificação antiga que foi rebaseada:

  • Rebasing the checked out branch localmente
  • Ramificação do ramo rebased para um novo ramo
  • Empurrando esse ramo como um novo ramo para remoto. e excluindo o ramo antigo no remoto

O seguinte funciona para mim:

git push -f origin branch_name

e não remove nenhum dos meus códigos.

Mas, se você quiser evitar isso, então você pode fazer o seguinte:

 git checkout master git pull --rebase git checkout -b new_branch_name 

então você pode escolher todos os seus commits para o novo branch. git cherry-pick COMMIT ID e, em seguida, empurre sua nova ramificação.

Como o OP entende o problema, apenas procura por uma solução melhor …

Como sobre isso como uma prática?

  • Tenha no ramo de desenvolvimento de resources real (onde você nunca rebase e force-push, então seus desenvolvedores de resources não o odeiam). Aqui, pegue regularmente essas alterações do main com uma mesclagem. História mais confusa , sim, mas a vida é fácil e ninguém é interrompido em seu trabalho.

  • Ter uma segunda ramificação de desenvolvimento de resources, onde um membro da equipe de resources regulador envia todos os commits de resources para, na verdade, rebased, de fato forçados. Então, quase com base em um commit master bem recente. Após a conclusão do recurso, empurre esse ramo em cima do mestre.

Pode haver um nome de padrão para este método já.

Para mim, seguindo passos simples, funciona:

 1. git checkout myFeature 2. git rebase master 3. git push --force-with-lease 4. git branch -f master HEAD 5. git checkout master 6. git pull 

Depois de fazer tudo acima, podemos excluir o ramo myFeature, seguindo o comando:

 git push origin --delete myFeature