git push --force-with-lease vs. --force

182

Estou tentando entender a diferença entre

git push --force

e

git push --force-with-lease

Meu palpite é que o último só envia para o controle remoto se o controle remoto não tiver confirmações que a filial local não possui ?

Alexander Mills
fonte
o " ramo de rastreamento remoto local ". Basicamente, o controle remoto deve se parecer com o que seu cliente espera. git help pushtem casos de uso explicando sua finalidade (basicamente para impedir que você troque uma alteração que alguém acabou de enviar). O que não está claro para mim é como o ramo de rastreamento remoto funciona. Mas, presumivelmente, normalmente será necessário ter a aparência exata da última vez que você fez um fetchou pullsem novos commit.
Zzxyz 15/10
4
@zzxyz: a implementação real --force-with-leaseé semelhante à das instruções de comparação e troca nas CPUs modernas: quem deseja que a troca ocorra forneça o valor esperado e o novo valor. O sistema que faz a troca compara o valor esperado com o valor atual verdadeiro e faz a troca se e somente se os dois forem iguais. Com git push, o valor esperado é o que estiver no nome do rastreamento remoto, por exemplo, git push --force-with-lease origin Xenvia o seu próprio origin/Xjunto com o novo valor desejado; originO Git diz se você fez a troca ou não.
Torek # 15/18
1
Se o Git originfez a troca, está feito. Caso contrário, você pode executar git fetch originpara pegar o novo valor atual, refazer suas alterações, se necessário, e executar outra comparação e troca de força com concessão para tentar novamente.
Torek # 15/18

Respostas:

172

force substitui uma ramificação remota pela sua ramificação local.

--force-with-leaseé uma opção mais segura que não substituirá nenhum trabalho na ramificação remota se mais confirmações forem adicionadas à ramificação remota (por outro membro da equipe ou colega de trabalho ou o que você tem). Isso garante que você não substitua o trabalho de outra pessoa por força de pressão.

Eu acho que sua ideia geral em torno do comando está correta. Se a ramificação remota tiver o mesmo valor que a ramificação remota na máquina local, você substituirá a remota. Se ele não tiver o mesmo valor, indica uma alteração que alguém fez na ramificação remota enquanto você estava trabalhando no seu código e, portanto, não substituirá nenhum código. Obviamente, se houver confirmações adicionais no controle remoto, os valores não serão os mesmos.

Penso apenas --force-with-leasena opção de usar quando quero garantir que não substitua nenhum código de companheiro de equipe. Muitas equipes da minha empresa usam --force-with-leasecomo opção padrão para um fail-safe. É desnecessário na maioria das circunstâncias, mas você economizará muita dor de cabeça se você substituir algo que outra pessoa contribuiu para remotamente.

Tenho certeza que você consultou os documentos, mas pode haver mais explicações contidas aqui:

https://git-scm.com/docs/git-push

chevybow
fonte
38

À procura de uma resposta de fontes confiáveis ​​e / ou oficiais.

A "comparação e troca" mencionada por torek nos comentários e em sua outra resposta é mais ilustrada pelas fontes do próprio Git .

o último só envia para o controle remoto se o controle remoto não tiver confirmações que a filial local não possui?

Esse recurso foi introduzido neste commit (dezembro de 2013, Git v1.8.5-rc0)

--force-with-lease protegerá todas as referências remotas que serão atualizadas exigindo que seu valor atual seja o mesmo que algum padrão razoável, a menos que especificado de outra forma;

Por enquanto, "algum padrão razoável" é provisoriamente definido como " o valor da ramificação de rastreamento remoto que temos para a ref da atualização remota " e é um erro se não tivermos uma ramificação de rastreamento remoto.

Portanto, "arrendamento" significa:

" force-with-lease": Você supõe que aceitou a concessão no árbitro quando buscou decidir qual deveria ser o histórico rebased e só pode recuar se a concessão não tiver sido quebrada.

As fontes ainda mencionam "cas":

  • Esta opção foi originalmente chamada "cas " (para "comparar e trocar"), o nome de que ninguém gostava porque era muito técnico.
  • A segunda tentativa chamou de "lockref" (porque é conceitualmente como empurrar depois de bloquear), mas a palavra "lock" foi odiada porque implicava que ela pode rejeitar o push por outras pessoas, o que não é o modo como esta opção funciona.
  • Esta rodada o chama de "força com arrendamento".
    Você supõe que aceitou a concessão do árbitro quando buscou decidir qual deveria ser o histórico recuperado e só pode recuar se a concessão não tiver sido quebrada.

Então: " git push --force-with-leasevs.--force "

Como mencionei em " push --force-with-leasepor padrão ", como o Git 2.13 (Q2 2017) menciona, que a opção --force-with-leasepode ser ignorada se um processo em segundo plano (como o encontrado em um IDE com um plug-in Git) for executado git fetch origin.
Nesse caso, --forceprevalece.

VonC
fonte
29

O git push --force é destrutivo porque sobrescreve incondicionalmente o repositório remoto com o que houver localmente. empurrão do git --force é fortemente desencorajado, pois pode destruir outros commits já enviados para um repositório compartilhado. Uma das causas mais comuns de força é quando somos forçados a refazer uma ramificação.

Por exemplo. Temos um projeto com um ramo de recursos em que Alice e Bob vão trabalhar. Ambos clonam este repositório e começam o trabalho. Alice inicialmente completa sua parte do recurso e envia isso para o repositório principal. Está tudo bem e bom. Bob também termina seu trabalho, mas antes de empurrá-lo, percebe que algumas mudanças foram mescladas ao mestre. Querendo manter uma árvore limpa, ele realiza uma nova recuperação contra o ramo mestre. Obviamente, quando ele for empurrar esse ramo rebaseado, ele será rejeitado. No entanto, não percebendo que Alice já empurrou seu trabalho, ele executa uma força de empurrão. Infelizmente, isso apagará todos os registros das alterações de Alice no repositório central.

O que --force-with-lease faz é recusar-se a atualizar uma filial, a menos que seja o estado que esperamos; ou seja, ninguém atualizou o ramo a montante. Na prática, isso funciona verificando se o ref upstream é o que esperamos, porque refs são hashes e codificam implicitamente a cadeia de pais em seu valor.

Aqui está um bom post sobre git push --force e git push --force-with-lease.

Shakil
fonte
2
o que eu não entendo - se você se refaz com o mestre, deve conseguir empurrar sem --force-with-lease ? Por que é --force-with-leasenecessário após rebasear com o mestre?
Alexander Mills
3
@AlexanderMills, pode haver alguma possibilidade que cause problemas. Se você é a última pessoa que pressiona isso para dominar, então não há problema, mas há outra pessoa que trabalha com outra tarefa, isso pode causar problemas sérios. Digamos que A - B - C tenha sido o mestre, a princípio Alice faça A - B - C - D - E e ao mesmo tempo Bob faça A - B - C - F - G. Se Alice é a última pessoa que pressiona no mestre após rebasear A - B - C - D - E - F - G não causará nenhum problema, mas outra pessoa Tom tenta pressionar A - B - C - D - E - R ao mesmo tempo em um desastre mestre! !
Shakil
3
--forcenão perde commits, apenas os desanexa. Eles podem ser ressuscitados por seus hashes, como em git checkout <SHA>ou git reset --hard <SHA>, supondo que o mantenedor do repositório remoto não execute nenhuma operação de GC ou de remoção.
Keith Russell
1
"o push do git --force é fortemente desencorajado, pois pode destruir outros commits já enviados para um repositório compartilhado". Esta afirmação é fortemente baseada em opiniões. Fazer um push forçado faz parte de um fluxo de trabalho de revisão de código normal do git rebase. Também como mencionado, você pode recuperar os commits mesmo depois de fazer isso.
Étienne
9

Supondo que qualquer gancho de pré-recebimento no servidor aceite o envio, isso sempre será bem-sucedido:

git push --force

Considerando que isso executa uma verificação específica do lado do cliente antes de continuar:

git push --force-with-lease

Você pode executar a verificação específica manualmente. Aqui está o algoritmo "verificação de concessão":

  1. Descobrir sua filial atual.

  2. Corra git for-each-ref refs/remotes. Anote o ID de confirmação que seu cliente git acha que corresponde ao estado upstream da sua filial atual.

Por exemplo, se você estiver no ramo "foo", observe o ID do commit associado a "refs / remotes / origin / foo".

  1. Determine o ID de confirmação real da filial remota no servidor git upstream no momento.

  2. Apenas deixe o "git push" prosseguir se os IDs de confirmação que você extraiu das etapas 2 e 3 concordarem. Em outras palavras, prossiga apenas se a noção de upstream do seu clone git local concordar com a atual upstream.

Há uma implicação triste aqui: como git fetchatualiza todos os refs em "refs / remotes / origin / *" para suas versões mais recentes, essa combinação de comandos é essencialmente idêntica a git push --force:

git fetch

# The command below behaves identically to "git push --force"
# if a "git fetch" just happened!

git push --force-with-lease

Para contornar essa fraqueza inerente, git push --force-with-leasetento nunca correr git fetch. Em vez disso, eu sempre corro git pull --rebasesempre que preciso sincronizar com o upstream, já que git pullapenas atualiza uma única ref em refs / remotes, mantendo o "contrato" --force-with-leaseútil.

G. Sylvie Davies
fonte
1
se a verificação for do lado do cliente, há potencial para uma condição de corrida? ou seja, alguém envia alterações após a verificação, mas antes do empurrão forçado?
craq 23/12/19
2

Forçar com locação não é necessariamente seguro. Funciona como Sylvie disse. Uma observação: no git, um branch é apenas um ponteiro em um commit. E confirmações apontam para zero ou mais confirmações pai. Mesmo que você tenha mudado completamente o ramo com uma redefinição rígida do git e um push forçado ou um push com - - force-with-lease sem querer, isso não é necessariamente um grande problema. Você pode usar o reflog git local para ver como sua dica local nos galhos (Onde estava o HEAD naquele momento?) Mudou e redefiniu e empurre o galho novamente. Então você perde apenas novas confirmações na ramificação remota, mas mesmo elas podem ser restauradas pelos membros da equipe.

Peixe
fonte