Como faço para enviar por push corrigido ao repositório Git remoto?


662

Quando trabalhei um pouco com meu código-fonte, fiz o que de costume eu fazia e depois fui para um repositório remoto. Mas então notei que esqueci de organizar minhas importações no código fonte. Então, eu faço o comando de alteração para substituir a confirmação anterior:

> git commit --amend

Infelizmente, o commit não pode ser enviado de volta ao repositório. É rejeitado assim:

> git push origin
To //my.remote.repo.com/stuff.git/
 ! [rejected]        master -> master (non-fast forward)
error: failed to push some refs to '//my.remote.repo.com/stuff.git/'

O que devo fazer? (Eu posso acessar o repositório remoto.)


E se meu --amend fosse apenas para alterar a mensagem de confirmação? Alguma maneira de editar a última mensagem de confirmação sozinha, se já foi enviada para remoto? Fiz isso no Github e recebi a mesma mensagem sobre o avanço rápido. Então eu apliquei uma solução abaixo, mas a fusão acabou de adicionar mais cometer mensagens no topo ..

7
@faB: Eu acho que isso é um FAQ. Uma mensagem de confirmação é hash junto com o commit, portanto , controlá- lo altera o revid (hash). Se não estiver claro: não, você não pode. O IIRC pode armazenar informações fora da banda em notas (para que você possa anotar confirmações existentes sem alterá-las). Para rotular confirmações específicas, use tags
sehe

1
Em breve (git1.8.5, quarto trimestre de 2013), você poderá fazer umgit push -force procedimento com mais cuidado .
VonC 10/09/13

3
Aqui está o estilo cowboy. Não aprenda mais ou não procure maneiras de desfazer a alteração anterior do git. Basta adicionar um código de espaço reservado, quero dizer, adicionar um comentário, limpar um pouco de código ou simplesmente adicionar alguns traços traço traço .... Agora faça um commit real e envie-o para controle remoto. Feito !
Nehem

@ user58777 Se o seu -amend foi apenas para alterar a mensagem de confirmação e você não fez nenhuma confirmação local adicional desde então, é possível redefinir sua ramificação local para a confirmação remota que você enviou antes de alterar a mensagem de confirmação.
Scott Ahten 15/01/19

Respostas:


504

Eu na verdade, uma vez empurrado com --forcee .gitrepositório e tenho repreendido por Linus BIG TIME . Em geral, isso criará muitos problemas para outras pessoas. Uma resposta simples é "Não faça".

Vejo que outros deram a receita de qualquer maneira, então não os repetirei aqui. Mas aqui está uma dica para se recuperar da situação depois de enviar o commit corrigido com --force (ou + master).

  1. Use git reflogpara encontrar o antigo commit que você alterou (chame-o old, e chamaremos o novo commit que você criou alterando new).
  2. Crie uma mesclagem entre olde new, gravando a árvore de new, como git checkout new && git merge -s ours old.
  3. Mesclar isso ao seu mestre com git merge master
  4. Atualize seu mestre com o resultado com git push . HEAD:master
  5. Empurre o resultado para fora.

Então, as pessoas que estavam a infelicidade de ter baseado o seu trabalho sobre a cometer você obliterado por que altera e forçando um empurrão verá a fusão resultante vai ver que você é a favor newmais old. Suas fusões posteriores não verão os conflitos entre olde newque resultaram da sua alteração, portanto, eles não precisam sofrer.


17
Estou muito ciente do que acontece quando você força o envio de uma confirmação alterada (destruindo o histórico). Felizmente, eu era o único desenvolvedor do projeto com o repositório remoto em uma unidade de rede, então não foi um grande negócio. Eu nunca pensei em mesclar um commit de correção, por isso vou aprovar isso.
Spoike 11/01

62
Em nossa empresa, pressionamos com bastante frequência ... em ramos de recursos desenvolvidos por indivíduos.
Ondra Žižka 23/03

2
A bronca de Linus foi porque você apagou a história com a opção de força, não porque não deveria fazê-lo. A solução de GabrielleV funciona bem, porque não muda o histórico.
user411279

2
Por favor, como o autor (gitster) desta resposta parece não existir mais, alguém poderia ajudar a esclarecer o número do item 1: encontre o commit antigo. Se você não possui um backup, onde o encontrará? Alterar e empurrar a força não a destruiria? Talvez ele esteja se referindo a obtê-lo de um amigo / colaborador que ainda o tem na árvore?
Dr Beco

2
Dr Breco você pode usar git reflogpara encontrá-lo
Simon Zyx

269

Você está vendo um recurso de segurança do Git. O Git se recusa a atualizar a ramificação remota com sua ramificação, porque a confirmação principal da ramificação não é um descendente direto da confirmação principal atual da ramificação na qual você está enviando.

Se não fosse esse o caso, duas pessoas que enviariam para o mesmo repositório mais ou menos na mesma hora não saberiam que havia um novo commit entrando ao mesmo tempo e quem empurrou por último perderia o trabalho do empurrador anterior sem nenhum dos dois. eles percebendo isso.

Se você sabe que é a única pessoa que pressiona e deseja enviar por push uma confirmação ou push que confirme a ramificação, você pode 'forçar' o Git a atualizar a ramificação remota usando o -fswitch.

git push -f origin master

Mesmo isso pode não funcionar, pois o Git permite que repositórios remotos recusem push não avançados no extremo remoto usando a variável de configuração receive.denynonfastforwards. Se for esse o caso, o motivo da rejeição será semelhante a este (observe a parte 'rejeitada remotamente'):

 ! [remote rejected] master -> master (non-fast forward)

Para contornar isso, é necessário alterar a configuração do repositório remoto ou, como um hack sujo, você pode excluir e recriar a ramificação da seguinte maneira:

git push origin :master
git push origin master

Em geral, o último parâmetro a git pushusar o formato <local_ref>:<remote_ref>, onde local_refé o nome da ramificação no repositório local e remote_refé o nome da ramificação no repositório remoto. Este par de comandos usa duas atalhos. :masterpossui um local_ref nulo, o que significa enviar uma ramificação nula para o lado remoto master, ou seja, excluir a ramificação remota. Um nome de filial sem :meios significa enviar a filial local com o nome fornecido para a filial remota com o mesmo nome. masternesta situação é a abreviação de master:master.


2
isso não funcionou com o github, me deu a seguinte mensagem: mestre [remoto rejeitado] (exclusão proibida da ramificação atual)
vedang

Eu não queria forçar o push (o que eu sabia que resolveria o problema), mas agora acho que não tenho escolha.
vedang

1
Esta é a única solução que funcionou para meu repositório hospedado com o assembla.
23411 Justin Justin

1
excluir a ramificação principal remota liberará espaço no repositório remoto?
precisa saber é o seguinte

1
@Mr_and_Mrs_D: Não imediatamente, mas depois de uma git gcvez os reflogs expiram, os objetos antigos serão podados. Ninguém que clona o repositório obterá objetos que não são mais acessíveis assim que a ramificação for atualizada.
CB Bailey

211

Discurso rápido: O fato de ninguém postar a resposta simples aqui demonstra a desesperada hostilidade do usuário exibida pela CLI do Git.

De qualquer forma, a maneira "óbvia" de fazer isso, supondo que você não tenha tentado forçar o empurrão, é puxar primeiro. Isso puxa a alteração que você alterou (e não tem mais) para que você a tenha novamente.

Depois de resolver qualquer conflito, você pode enviar novamente.

Assim:

git pull

Se você receber erros no pull, talvez algo esteja errado na configuração do seu repositório local (eu tive uma referência errada na seção .git / config branch).

E depois

git push

Talvez você receba um commit extra com o assunto falando sobre uma "mesclagem trivial".


2
Sim, eu escrevi sobre isso, consulte stackoverflow.com/questions/253055/… ;)
Spoike

10
Isso realmente não funciona como eu esperava. Ele cria dois novos commits. Uma que é uma réplica da antiga, mas com as alterações alteradas. E uma consolidação de mesclagem com um diff vazio. Ainda deixando o antigo commit inalterado, revelando dados possivelmente confidenciais que eu estava tentando alterar. Acredito git push -fou git reseté o único caminho a percorrer aqui.
thnee

40
Embora tecnicamente responda ao problema, ele realmente não resolve o problema. Como você disse, ele gerará uma confirmação extra, mas a principal razão pela qual as pessoas alteram uma confirmação é evitar a criação de uma nova. Portanto, se o pôster seguisse suas instruções, ele não obteria o resultado desejado. Faria todo o sentido não alterar o commit em primeiro lugar.
Dan Jones

102

Resposta curta: não envie confirmações alteradas para um repositório público.

Resposta longa: Alguns comandos do Git, como git commit --amende git rebase, na verdade, reescrevem o gráfico do histórico. Tudo bem, desde que você não tenha publicado suas alterações, mas, uma vez publicado, você realmente não deve estar mexendo com o histórico, porque se alguém já recebeu suas alterações, então, quando elas tentarem novamente, isso poderá falhar . Em vez de alterar uma confirmação, você deve apenas fazer uma nova confirmação com as alterações.

No entanto, se você realmente deseja enviar por push uma confirmação alterada, pode fazer o seguinte:

$ git push origin +master:master

O +sinal principal forçará a ocorrência do envio, mesmo que não resulte em uma confirmação "avanço rápido". (Uma confirmação de avanço rápido ocorre quando as alterações que você está enviando são descendentes diretos das alterações já existentes no repositório público.)


5
Como isso é diferente (melhor ou pior) do que o git push -f? Obrigado!
27412

11
Bentford @: É basicamente a mesma coisa que git push -f.
Mipadi

54

Aqui está uma maneira muito simples e limpa de enviar suas alterações depois que você já fez commit --amend:

git reset --soft HEAD^
git stash
git push -f origin master
git stash pop
git commit -a
git push origin master

O que faz o seguinte:

  • Redefina o cabeçalho da ramificação para confirmação pai.
  • Esconder este último commit.
  • Força o envio para o controle remoto. O controle remoto agora não tem o último commit.
  • Pop seu esconderijo.
  • Comprometa-se de forma limpa.
  • Empurre para o controle remoto.

Lembre-se de alterar "origem" e "mestre" se aplicá-lo a uma ramificação ou controle remoto diferente.


3
2 observações: - certifique-se de alterar o nome do ramo, se você estiver trabalhando em outro - eu tive que usar git addantes do meu commit para incluir as alterações.
SylvainB

1
No Windows CMD, o primeiro comando deve ser escapado: git reset --soft "HEAD^". O resto funciona bem.
MrMister 13/09/17

2
"uma maneira muito simples e limpa .." cit. Este procedimento inclui push forçado. À luz de todas as críticas nas Respostas acima, não tenho certeza se esse procedimento é de fato um procedimento limpo.
Na13-c

24

Eu o resolvi descartando meu commit alterado local e adicionando as novas alterações na parte superior:

# Rewind to commit before conflicting
git reset --soft HEAD~1

# Pull the remote version
git pull

# Add the new commit on top
git add ...
git commit
git push

2
Esta é a versão mais simples!
Mknaf

Adicionar outra confirmação de 'alteração' é melhor do que mexer com a reescrita do histórico. Eu concordo com @mknaf
sdkks

8

Eu tive o mesmo problema.

  • Alterou acidentalmente o último commit que já foi enviado
  • Realizou muitas mudanças localmente, cometeu cinco vezes
  • Tentei enviar por push, obtive um erro, entrei em pânico, mesclou o controle remoto, recebi muitos arquivos que não eram dos meus arquivos, enviaram push, falharam etc.

Como novato no Git, pensei que fosse o FUBAR completo .

Solução: algo como o @bara sugeriu + criou uma ramificação de backup local

# Rewind to commit just before the pushed-and-amended one.
# Replace <hash> with the needed hash.
# --soft means: leave all the changes there, so nothing is lost.
git reset --soft <hash>

# Create new branch, just for a backup, still having all changes in it.
# The branch was feature/1234, new one - feature/1234-gone-bad
git checkout -b feature/1234-gone-bad

# Commit all the changes (all the mess) not to lose it & not to carry around
git commit -a -m "feature/1234 backup"

# Switch back to the original branch
git checkout feature/1234

# Pull the from remote (named 'origin'), thus 'repairing' our main problem
git pull origin/feature/1234

# Now you have a clean-and-non-diverged branch and a backup of the local changes.
# Check the needed files from the backup branch
git checkout feature/1234-gone-bad -- the/path/to/file.php

Talvez não seja uma solução rápida e limpa, e perdi minha história (1 commit em vez de 5), mas economizou um dia de trabalho.


6

Se você não enviou o código para sua ramificação remota (GitHub / Bitbucket), você pode alterar a mensagem de confirmação na linha de comando, como abaixo.

 git commit --amend -m "Your new message"

Se você estiver trabalhando em uma ramificação específica, faça o seguinte:

git commit --amend -m "BRANCH-NAME: new message"

Se você já inseriu o código com uma mensagem errada, precisa ter cuidado ao alterar a mensagem. ou seja, depois de alterar a mensagem de confirmação e tentar enviá-la novamente, você acaba tendo problemas. Para facilitar, siga os seguintes passos.

Leia a resposta completa antes de fazê-lo

git commit --amend -m "BRANCH-NAME : your new message"

git push -f origin BRANCH-NAME                # Not a best practice. Read below why?

Nota importante: Ao usar o push forçado diretamente, você pode acabar com problemas de código nos quais outros desenvolvedores estão trabalhando no mesmo ramo. Portanto, para evitar esses conflitos, você precisa extrair o código do seu ramo antes de fazer a força forçar :

 git commit --amend -m "BRANCH-NAME : your new message"
 git pull origin BRANCH-NAME
 git push -f origin BRANCH-NAME

Essa é a melhor prática ao alterar a mensagem de confirmação, se já tiver sido enviada.


1
Se você retirou com êxito o commit no seu último exemplo, por que precisa forçar o push? Um push padrão não seria suficiente? Obrigado
Thomas

A pergunta feita por Thomas é de fato muito válida. Eu mesmo não precisava forçar o empurrão após o puxão.
Na13-c

Por favor, não chame isso de "a melhor prática", porque existe uma maneira de contornar --force, veja a resposta aceita
Farid

5

Se você sabe que ninguém obteve seu commit não alterado, use a --force-with-leaseopção degit push .

No TortoiseGit, você pode fazer o mesmo nas opções "Empurrar ...", "Forçar: Pode descartar" e marcar "alterações conhecidas".

Forçar (pode descartar alterações conhecidas) permite que o repositório remoto aceite um envio mais rápido sem avanço rápido. Isso pode fazer com que o repositório remoto perca confirmações; use-o com cuidado. Isso pode impedir a perda de alterações desconhecidas de outras pessoas no controle remoto. Ele verifica se a ramificação do servidor aponta para a mesma confirmação que a ramificação de rastreamento remoto (alterações conhecidas). Se sim, um empurrão forçado será executado. Caso contrário, será rejeitado. Como o git não possui tags de rastreamento remoto, as tags não podem ser substituídas usando esta opção.


4

Você está recebendo este erro porque o remoto Git já possui esses arquivos de confirmação. Você precisa forçar o push do branch para que isso funcione:

git push -f origin branch_name

Além disso, certifique-se de extrair o código do controle remoto, pois outra pessoa da sua equipe pode ter enviado para o mesmo ramo.

git pull origin branch_name

Este é um dos casos em que precisamos forçar o envio remoto para commit.


Por que essa resposta não explica os principais comentários levantados nas respostas anteriores?
Na13-c

2

Aqui está uma maneira muito simples e limpa de enviar suas alterações depois de você já ter feito um git add "your files"e git commit --amend:

git push origin master -f

ou:

git push origin master --force

Ouvi dizer que isso é ruim e tenho certeza que é. Há uma (boa) razão para o git falhar por padrão (e exigir --force), tenho certeza.
Rolf

1

Eu tive que corrigir esse problema retirando do repositório remoto e lidar com os conflitos de mesclagem que surgiram, confirmar e depois enviar. Mas sinto que há uma maneira melhor.


Na verdade não. O problema pode ser que você não atualizou sua cópia local a partir do repositório remoto. O Git não vai empurrá-lo porque você pode ter que lidar com mesclagens manualmente. Na minha outra resposta, tenho um comando (e explicação) que forçará um empurrão - mas cuidado, isso pode excluir as alterações no controle remoto.
Mipadi 31/10/08

1

Eu apenas continuei fazendo o que Git me disse para fazer. Assim:

  • Não é possível enviar por causa da confirmação alterada.
  • Eu faço um puxão, como sugerido.
  • Falha na mesclagem. então eu corrijo isso manualmente.
  • Crie um novo commit (rotulado "mesclar") e pressione-o.
  • Parece funcionar!

Nota: O commit alterado foi o mais recente.


1
Eu teria voto negativo, se tivesse mais pontos de reputação, então vou perguntar aqui educadamente, qual foi você dos que sofrem? Aquele que alterou? O que puxou e trabalhou em um ramo com commit alterado? Antes da emenda ou depois dela? Eu só limpou a cada modificação da minha, porque eu não entendi você ... Felizmente não havia muito ...
Bartis Áron

1

O seguinte funcionou para mim ao alterar o Autor e o Committer de um commit.

git push -f origin master

O Git foi inteligente o suficiente para descobrir que esses eram commits de deltas idênticos, que apenas diferiam na seção de meta informações.

Tanto os chefes locais quanto os remotos apontaram para os commits em questão.



0

Aqui, como eu corrigi uma edição em um commit anterior:

  1. Salve seu trabalho até agora.
  2. Esconda suas alterações por enquanto, se feitas: git stash agora sua cópia de trabalho está limpa no estado do seu último commit.
  3. Faça as edições e correções.
  4. Confirme as alterações no modo "alterar" :git commit --all --amend
  5. Seu editor aparecerá solicitando uma mensagem de log (por padrão, a antiga mensagem de log). Salve e saia do editor quando estiver satisfeito com ele.

    As novas alterações são adicionadas ao commit antigo. Veja você mesmo com git logegit diff HEAD^

  6. Reaplique as alterações ocultas, se feitas: git stash apply

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.