Reset Git - twardy i wypchnij do zdalnego repozytorium

199

Miałem repozytorium z kilkoma złymi zatwierdzeniami (D, E i F w tym przykładzie).

ABCDEF master i origin / master

Zmodyfikowałem lokalne repozytorium specjalnie za pomocą git reset --hard. Przed resetem wziąłem gałąź, więc teraz mam repozytorium, które wygląda następująco:

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

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

Teraz potrzebowałem części tych złych zatwierdzeń, więc wybrałem potrzebne bity i zrobiłem kilka nowych zatwierdzeń, więc teraz mam następujące lokalnie:

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

Teraz chcę przekazać ten stan rzeczy do zdalnego repozytorium. Jednak, gdy próbuję zrobić git pushGit, uprzejmie daj mi odpocząć:

$ 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'  

Jak sprawić, by zdalne repozytorium przyjęło bieżący stan repozytorium lokalnego?

robertpostill
źródło
2
Jest to „prawie” duplikat kilku „jak wcisnąć poprawione pytania historyczne”, np. Zobacz odpowiedź tutaj stackoverflow.com/questions/253055/…
CB Bailey,
2
To prawda i przed wysłaniem szukałem odpowiedzi na StackOverflow. Jednak moje wyszukiwanie przyniosło tylko odpowiedzi, w których polecenie git push - force rozwiązało problem. Dziękujemy za link do twojego postu :)
robertpostill
2
Wkrótce (git1.8.5, IV kwartał 2013 r.) Będziesz mógł robić to git push -forceostrożniej .
VonC

Odpowiedzi:

286

Jeśli wymuszenie wypychania nie pomaga ( powinno wystarczyć „ git push --force origin” lub „ git push --force origin master”), może to oznaczać, że serwer zdalny odmawia wypychania niezwiązanego z szybkim przewijaniem albo przez zmienną konfiguracyjną receive.denyNonFastForwards (zobacz opis instrukcji git config ) lub za pośrednictwem haka aktualizacji / odbioru wstępnego.

W starszej wersji Git możesz obejść to ograniczenie, usuwając „ git push origin :master” (patrz „:” przed nazwą oddziału), a następnie ponownie tworząc „ git push origin master” dany oddział.

Jeśli nie możesz tego zmienić, jedynym rozwiązaniem byłoby zamiast przepisywania historii, aby utworzyć zmiany cofające zmiany w DEF :

ABCDEF - [(DEF) ^ - 1] master

ABCDEF origin / master
Jakub Narębski
źródło
2
@ JakubNarębski, dzięki. get revert HEAD~Npomógł. Nto liczba zatwierdzeń. Np. Jeśli potrzebuję poprzedniego zatwierdzenia, użyjęgit revert HEAD~1
Maksim Dmitriev
1
I pamiętaj, że w ten sposób złamiesz wszystkich lokalnych mistrzów.
Tom Brito
24

Aby uzupełnić odpowiedź Jakuba, jeśli masz dostęp do zdalnego serwera git w ssh, możesz przejść do zdalnego katalogu git i ustawić:

user@remote$ git config receive.denyNonFastforwards false

Następnie wróć do lokalnego repozytorium, spróbuj ponownie wykonać zatwierdzenie za pomocą --force:

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

I w końcu przywróć ustawienia serwera w pierwotnym stanie chronionym:

user@remote$ git config receive.denyNonFastforwards true
Zazdrość
źródło
Zobacz także pete.akeo.ie/2011/02/denying-non-fast-forward-and.html, aby uzyskać dostosowane informacje o sourceforge.
hlovdal
Szczegółowe instrukcje dotyczące wyłączania używania funkcji denyNonFastForwards viznajdują się w tym poście SO: stackoverflow.com/a/43721579/2073804
ron190
2

Zamiast naprawiać gałąź „master”, o wiele łatwiej jest zamienić ją na „pożądany master”, zmieniając nazwy gałęzi. Zobacz https://stackoverflow.com/a/2862606/2321594 . W ten sposób nie zostawisz nawet śladu wielu dzienników przywracania.

Aidin
źródło
1

Cały proces resetowania gitów wydawał mi się skomplikowany.

Zrobiłem więc coś, co sprawiło, że mój folder src znalazł się w stanie, który kilka razy temu zatwierdziłem

# 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

W ten sposób stan rzeczy w src jest przechowywany w pliku tar, a git jest zmuszony zaakceptować ten stan bez zbytniej manipulacji, w zasadzie katalog src zostaje zastąpiony stanem, który miał kilka zatwierdzeń wcześniej.

Wolfgang Fahl
źródło
0

Dla użytkowników GitHub działało to dla mnie:

  1. We wszystkich regułach ochrony gałęzi, w których chcesz dokonać zmiany, upewnij się, że opcja Zezwalaj na wymuszanie wypychania jest włączona
  2. git reset --hard <full_hash_of_commit_to_reset_to>
  3. git push --force

Spowoduje to „skorygowanie” historii gałęzi na twoim komputerze lokalnym i serwerze GitHub, ale każdy, kto zsynchronizował tę gałąź z serwerem od czasu złego zatwierdzenia, będzie miał historię na swoim komputerze lokalnym. Jeśli mają uprawnienia do bezpośredniego przesyłania do oddziału, wówczas te zatwierdzenia będą wyświetlane od razu po zsynchronizowaniu.

Wszystko, co wszyscy muszą zrobić, to git reset polecenie z góry, aby „poprawić” gałąź na lokalnym komputerze. Oczywiście musieliby uważać na wszelkie lokalne zobowiązania dokonane w tym oddziale po haszu docelowym. Cherry pick / backup i zastosuj je ponownie, jeśli to konieczne, ale jeśli jesteś w chronionym oddziale, liczba osób, które mogą bezpośrednio do niego zaangażować, jest prawdopodobnie ograniczona.

Jason Faulkner
źródło