Git reset --hard и удаленный репозиторий

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

A-B-C-D-E-F мастер и источник/мастер

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

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 push Git вежливо, я отбрасываю кисть:

$ 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 [email protected]:myrepo.git  
! [remote rejected] master -> master (non-fast forward)  
error: failed to push some refs to '[email protected]:myrepo.git'  

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

Ответ 1

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

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

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

A-B-C-D-E-F-[(D-E-F)^-1]   master

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

Ответ 2

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

[email protected]$ git config receive.denyNonFastforwards false

Затем вернитесь к местному репо, повторите попытку с помощью --force:

[email protected]$ git push origin +master:master --force

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

[email protected]$ git config receive.denyNonFastforwards true

Ответ 3

Вместо того, чтобы фиксировать свою "ведущую" ветвь, проще ее заменить своим "желаемым мастером", переименовав ветки. См. fooobar.com/questions/1278/.... Таким образом, вы даже не оставите никаких следов нескольких журналов возврата.

Ответ 4

Весь бизнес сброса git выглядел намного усложняющим для меня.

Итак, я сделал что-то в этом роде, чтобы получить мою папку 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 заменяется состоянием, которое у него было несколько месяцев назад.