Git сбросить --hard и отправить в удаленный репозиторий

200

У меня был репозиторий с плохими коммитами (D, E и F для этого примера).

ABCDEF мастер и происхождение / мастер

Я изменил местный репозиторий специально с git reset --hard. Я взял ветку до сброса, так что теперь у меня есть репозиторий, который выглядит так:

A-B-C master  
     \ D-E-F old_master

A-B-C-D-E-F origin/master

Теперь мне нужны были некоторые части этих плохих коммитов, поэтому я выбрал нужные биты и сделал несколько новых коммитов, так что теперь у меня есть следующее:

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

Теперь я хочу перенести это положение на удаленное репо. Тем не менее, когда я пытаюсь сделать git pushGit вежливо, я отмахиваюсь:

$ git push origin +master:master --force  
Total 0 (delta 0), reused 0 (delta 0)  
error: denying non-fast forward refs/heads/master (you should pull first)  
To git@git.example.com:myrepo.git  
! [remote rejected] master -> master (non-fast forward)  
error: failed to push some refs to 'git@git.example.com:myrepo.git'  

Как заставить удаленное репо принять текущее состояние локального репо?

robertpostill
источник
2
Это «почти» дубликат нескольких «как мне задавать исправленные вопросы истории», например, см. Ответ здесь stackoverflow.com/questions/253055/…
CB Bailey
2
Это правда, и я искал StackOverflow ответ перед публикацией. Однако мой поиск нашел только ответы, в которых git push --force устранил проблему. Спасибо за
ссылку
2
Вы скоро (git1.8.5, Q4 2013) сможете сделать git push -forceболее осторожно .
VonC

Ответы:

287

Если принудительное нажатие не помогает (" git push --force origin" или " git push --force origin master" должно быть достаточно), это может означать, что удаленный сервер отказывается от push-уведомлений без ускоренной пересылки либо через переменную конфигурации receive.denyNonFastForwards (см. Описание на странице конфигурации git config ), либо через хук обновления / предварительного получения.

В более старом Git вы можете обойти это ограничение, удалив « git push origin :master» (см. «:» Перед именем ветви), а затем заново создав « git push origin master» данную ветку.

Если вы не можете изменить это, то единственным решением было бы вместо переписывания истории создать коммит, возвращающий изменения в DEF :

ABCDEF - [(DEF) ^ - 1] мастер

ABCDEF происхождение / мастер
Якуб Наребски
источник
2
@ JakubNarębski, спасибо. get revert HEAD~Nпомог. Nэто количество коммитов. Например, если мне понадобится предыдущий коммит, я буду использоватьgit revert HEAD~1
Максим Дмитриев
1
И имейте в виду, что этим вы сломаете всех остальных местных мастеров.
Том Брито
24

В дополнение к ответу Якуба, если у вас есть доступ к удаленному git-серверу в ssh, вы можете зайти в удаленный каталог git и установить:

user@remote$ git config receive.denyNonFastforwards false

Затем вернитесь в локальный репозиторий и попробуйте снова выполнить коммит --force:

user@local$ git push origin +master:master --force

И, наконец, вернуть настройки сервера в исходное защищенное состояние:

user@remote$ git config receive.denyNonFastforwards true
Jealie
источник
См. Также pete.akeo.ie/2011/02/denying-non-fast-forward-and.html для получения подробной информации об этом в sourceforge.
Хловдал
Подробные инструкции о том, как отключить использование denyNonFastForwards, viприведены в этом сообщении SO: stackoverflow.com/a/43721579/2073804
ron190
2

Вместо того, чтобы исправлять вашу «главную» ветку, проще заменить ее на «желаемый мастер», переименовав ветви. См. Https://stackoverflow.com/a/2862606/2321594 . Таким образом, вы даже не оставите никаких следов нескольких обращенных журналов.

Помощь в
источник
1

Весь бизнес, связанный с перезагрузкой, выглядел далеко не таким сложным для меня.

Так что я сделал что-то вроде того, чтобы моя папка src была в том состоянии, в котором я был несколько коммитов назад.

# reset the local state
git reset <somecommit> --hard 
# copy the relevant part e.g. src (exclude is only needed if you specify .)
tar cvfz /tmp/current.tgz --exclude .git  src
# get the current state of git
git pull
# remove what you don't like anymore
rm -rf src
# restore from the tar file
tar xvfz /tmp/current.tgz
# commit everything back to git
git commit -a
# now you can properly push
git push

Таким образом, состояние дел в src сохраняется в файле tar, и git вынужден принимать это состояние без особых усилий, в основном каталог src заменяется состоянием, которое было несколько коммитов назад.

Вольфганг Фаль
источник
0

Для пользователей GitHub это работает для меня:

  1. В любых правилах защиты филиалов, в которых вы хотите внести изменения, убедитесь, что Разрешить принудительное нажатие включено
  2. git reset --hard <full_hash_of_commit_to_reset_to>
  3. git push --force

Это «исправит» историю веток на вашем локальном компьютере и на сервере GitHub, но любой, кто синхронизировал эту ветку с сервером после плохой фиксации, будет иметь историю на своей локальной машине. Если у них есть разрешение на прямую передачу в ветку, тогда эти коммиты будут отображаться обратно при синхронизации.

Все остальное, что нужно сделать, - это git resetкоманда сверху, чтобы «исправить» ветку на их локальной машине. Конечно, они должны быть осторожны с любыми локальными коммитами, сделанными в этой ветке после целевого хэша. Вишня выбирает / резервирует и повторно применяет их по мере необходимости, но если вы находитесь в защищенной ветке, то число людей, которые могут принять непосредственное участие в ней, вероятно, ограничено

Джейсон Фолкнер
источник