Odrzucono Git non-fast-forward

88

Wydaje mi się, że to pytanie było zadawane wiele razy, ale zazwyczaj jest to rozwiązanie: „Usunąłem katalog i ponownie wykonałem pracę z nowym wyewidencjonowaniem”. Wykonałem zatwierdzenie i naciśnięcie, ale zdałem sobie sprawę, że odniosłem się do niewłaściwego numeru biletu w komunikacie dotyczącym zatwierdzenia. Poszukałem więc szybkiego rozwiązania w SO i skończyło się na wpisaniu w terminalu:

$ git reset --soft HEAD^
$ git commit -m "... correct message ..."

Jedynym problemem jest to, że otrzymuję następujący komunikat o błędzie:

To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes before pushing again.  See the 'Note about
fast-forwards' section of 'git push --help' for details.

Używam modelu git-flow i pracuję nad gałęzią deweloperską. Jak mogę ponownie scalić rzeczy, aby ponownie uszczęśliwić gita?

rynmrtn
źródło

Odpowiedzi:

52

Siła git push:

git push origin +develop
Alan Haggai Alavi
źródło
24
To rozwiązanie, ale przeczytaj komentarz Briana Campbella, aby zrozumieć, co robisz, zanim tego użyjesz.
thelem
5
git push origin + master
Aniket Thakur
Zobacz także notatkę o receive.denyNonFastForwardsw odpowiedzi Brian Campbell : +albo --forcemoże nie być wystarczająco energiczne, w zależności od sposobu ich kimkolwiek oni są, zostały skonfigurowane swoje repozytorium git.
torek
174

Jeśli naciśniesz commit do serwera, a następnie przepisać że popełniania lokalnie (z git reset, git rebase, git filter-branchlub jakiejkolwiek innej manipulacji historii), a następnie popchnął że przepisany popełnić powrotem do serwera, można zepsuć nikogo, kto pociągnął. Oto przykład; powiedz, że popełniłeś A i wypchnąłeś go na serwer.

- * - * - A <- master

- * - * - A <- początek / wzorzec

Teraz decydujesz się przepisać A w sposób, o którym wspomniałeś, resetując i ponownie zatwierdzając. Zauważ, że pozostawia to wiszące zatwierdzenie, A, które ostatecznie zostanie zebrane jako śmieci, ponieważ nie jest osiągalne.

-*-*-ZA
    \
     A '<- mistrz

- * - * - A <- początek / wzorzec

Jeśli ktoś inny, powiedzmy Fred, ściągnie masterserwer z serwera, kiedy to robisz, będzie miał odniesienie do A, od którego może zacząć pracę:

- * - * - A '<- master

- * - * - A <- początek / wzorzec

- * - * - AB <- fred / master

Teraz, gdybyś był w stanie popchnąć swoje A 'do początku / końca, co spowodowałoby szybkie przewijanie do przodu, nie miałoby ono A w swojej historii. Więc jeśli Fred spróbuje ponownie pociągnąć, musiałby nagle scalić i ponownie wprowadzić zatwierdzenie A:

- * - * - A '<- master

- * - * - A <- początek / wzorzec

- * - * - AB- \ 
    \ * <- fred / master
     ZA'--/

Jeśli Fred zdarzy się, że to zauważy, może wykonać rebase, co uniemożliwi ponowne pojawienie się zatwierdzenia A. Ale musiałby to zauważyć i pamiętać, żeby to zrobić; a jeśli masz więcej niż jedną osobę, która ściągnęła A, wszyscy musieliby zmienić bazę, aby uniknąć dodatkowego zatwierdzenia A w drzewie.

Więc generalnie nie jest dobrym pomysłem zmienianie historii w repozytorium, z którego korzystają inni ludzie. Jeśli jednak wiesz, że nikt inny nie wyciąga z tego repozytorium (na przykład jest to twoje własne prywatne repozytorium lub masz tylko jednego programistę pracującego nad projektem, z którym możesz łatwo koordynować), możesz wymusić aktualizacja poprzez uruchomienie:

git push -f

lub

git push origin +master

Obaj zignorują sprawdzanie, czy nie ma szybkiego przewijania do przodu, i zaktualizują zawartość serwera do nowej wersji A ', porzucając wersję A, aby ostatecznie została usunięta.

Możliwe, że wymuszenie wypychania jest całkowicie wyłączone za pomocą receive.denyNonFastForwardsopcji konfiguracji. Ta opcja jest domyślnie włączona w repozytoriach współdzielonych. W takim przypadku, jeśli naprawdę chcesz wymusić wypychanie, najlepszą opcją jest usunięcie gałęzi i ponowne utworzenie jej za pomocą git push origin :master; git push origin master:master. Jednak denyNonFastForwardsopcja jest włączona nie bez powodu, który opisano powyżej; na współdzielonym repozytorium oznacza to, że teraz każdy, kto go używa, musi upewnić się, że bazuje na nowej historii.

W repozytorium współużytkowanym generalnie lepiej jest po prostu wypchnąć nowe zatwierdzenia, które naprawią każdy problem; możesz użyć git revertdo wygenerowania zatwierdzeń, które cofną zmiany poprzednich zatwierdzeń.

Brian Campbell
źródło
świetna edukacja, a komendę dostałeś w końcu, ale moja gałąź nazywała się develop (na podstawie git-flow), a drugi facet podał +developkomendę - więc czek trafia do niego. I tak masz astronomiczną liczbę punktów: P
rynmrtn 14.04.11
7
Lub użyj mniej tajemniczegogit push --force
Bennett McElwee
3
@Panique Próbujesz pozwolić kilku osobom pracować na dużym, złożonym kodzie w tym samym czasie, bez blokowania się nawzajem (pozwalając tylko jednej osobie na pracę w tym samym czasie) i bez nadpisywania zmian. Każda osoba musi mieć możliwość niezależnego wprowadzania zmian i scalania tych zmian. Scalanie (ręczne lub automatyczne) może spowodować nieoczekiwane problemy; więc chcesz zachować jak najwięcej informacji, aby móc dowiedzieć się, co się stało, gdyby coś poszło nie tak. Jest to z natury złożone; to nie jest brudne, tylko trudny problem.
Brian Campbell,
Wypróbowałem opcję -f i +, aby przepisać historię zdalnego repozytorium. W obu opcjach napotkałem problem braku przewijania do przodu. [17:05] $ git push -f origin local_A: remote_A Zliczanie obiektów: 35, gotowe. Kompresja delta z wykorzystaniem do 2 wątków. Kompresja obiektów: 100% (18/18), gotowe. Pisanie obiektów: 100% (21/21), 7,41 KiB, gotowe. Łącznie 21 (delta 9), ponownie użyte 0 (delta 0) zdalne: aby zapobiec utracie historii, odrzucono aktualizacje bez szybkiego przewijania do przodu. Scal zdalne zmiany (np. „Git pull”) przed ponownym naciśnięciem. Zobacz sekcję „Note about fast-forward” w „git push --help” po szczegóły.
Srikanth
4
@Srikanth Można całkowicie wyłączyć wymuszone wypychanie za pomocą receive.denyNonFastForwardsopcji config. Ta opcja jest domyślnie włączona w repozytoriach współdzielonych. W takim przypadku, jeśli naprawdę chcesz wymusić wypychanie, najlepszą opcją jest usunięcie gałęzi i ponowne utworzenie jej za pomocą git push origin :remote_A; git push origin local_A:remote_A. Ale przeczytaj to, co napisałem powyżej, o tym, dlaczego wykonywanie tego rodzaju przepływu pracy we współdzielonym repozytorium jest złym pomysłem. Powinieneś spróbować to zrobić tylko wtedy, gdy masz coś, co powoduje poważne problemy w zatwierdzeniu, którego próbujesz się pozbyć lub przepisać.
Brian Campbell
14

Być może będziesz musiał zrobić git pull, co MOŻE automatycznie scalić elementy za Ciebie. Następnie możesz ponownie zatwierdzić. Jeśli masz konflikty, wyświetli się monit o ich rozwiązanie.

Pamiętaj, że musisz określić, z której gałęzi chcesz pobierać, jeśli nie zaktualizowałeś gitconfig, aby określić ...

Na przykład:

git pull origin develop:develop
Tony
źródło
Wciąż szalony o nie przewijaniu do przodu. Jakieś przemyślenia, jak zmusić to do połączenia? ! [rejected] develop -> develop (non-fast-forward)
rynmrtn
Myślę, że przełącznik to -f, ale mogę się mylić. kernel.org/pub/software/scm/git/docs/git-pull.html
Tony
To częściowo działa. Nie widzę jednak aktualizacji na githubie (pokazuje poprzednie zatwierdzenie jako najnowsze nawet z a git push origin develop)
rynmrtn 14.04.11
7

Używałem EGit i napotkałem również ten problem. Właśnie spróbowałem do rebasebieżącej gałęzi i zadziałało.

Nguyen Minh Binh
źródło