Rozumiem scenariusz przedstawiony w Pro Git o zagrożeniach związanych z ponownym użyciem . Autor w zasadzie mówi ci, jak uniknąć powielonych zatwierdzeń:
Nie zmieniaj bazy zatwierdzeń, które przesłałeś do publicznego repozytorium.
Opowiem wam o mojej szczególnej sytuacji, ponieważ uważam, że nie pasuje do scenariusza Pro Git i nadal otrzymuję zduplikowane zatwierdzenia.
Powiedzmy, że mam dwa zdalne oddziały z ich lokalnymi odpowiednikami:
origin/master origin/dev
| |
master dev
Wszystkie cztery gałęzie zawierają te same commity, a programowanie zacznę w dev
:
origin/master : C1 C2 C3 C4
master : C1 C2 C3 C4
origin/dev : C1 C2 C3 C4
dev : C1 C2 C3 C4
Po kilku zatwierdzeniach wprowadzam zmiany do origin/dev
:
origin/master : C1 C2 C3 C4
master : C1 C2 C3 C4
origin/dev : C1 C2 C3 C4 C5 C6 # (2) git push
dev : C1 C2 C3 C4 C5 C6 # (1) git checkout dev, git commit
Muszę wrócić master
do, aby szybko naprawić:
origin/master : C1 C2 C3 C4 C7 # (2) git push
master : C1 C2 C3 C4 C7 # (1) git checkout master, git commit
origin/dev : C1 C2 C3 C4 C5 C6
dev : C1 C2 C3 C4 C5 C6
Wracając do dev
zmiany podstaw zmian, aby uwzględnić szybką poprawkę w moim rzeczywistym rozwoju:
origin/master : C1 C2 C3 C4 C7
master : C1 C2 C3 C4 C7
origin/dev : C1 C2 C3 C4 C5 C6
dev : C1 C2 C3 C4 C7 C5' C6' # git checkout dev, git rebase master
Gdybym wyświetlić historię zatwierdzeń z GitX / gitk Zauważyłem, że origin/dev
teraz zawiera dwa identyczne zobowiązuje C5'
i C6'
które różnią się Git. Jeśli teraz wprowadzę zmiany do origin/dev
tego, otrzymam wynik:
origin/master : C1 C2 C3 C4 C7
master : C1 C2 C3 C4 C7
origin/dev : C1 C2 C3 C4 C5 C6 C7 C5' C6' # git push
dev : C1 C2 C3 C4 C7 C5' C6'
Może nie do końca rozumiem wyjaśnienie w Pro Git, więc chciałbym wiedzieć dwie rzeczy:
- Dlaczego Git powiela te zatwierdzenia podczas zmiany bazy? Czy jest jakiś konkretny powód, aby to zrobić zamiast po prostu złożyć wniosek
C5
iC6
późniejC7
? - Jak mogę tego uniknąć? Czy mądrze byłoby to zrobić?
origin/dev
. Podev
zmianie bazy, jego historia jest modyfikowana (C5 / C6 tymczasowo usuwane i ponownie stosowane po C7). Modyfikowanie historii wypychanych repozytoriów jest generalnie naprawdę złym pomysłem ™, chyba że wiesz, co robisz. W tym prostym przypadku problem można rozwiązać, wykonując wymuszony ruch od końcadev
doorigin/dev
po zakończeniu rebase i powiadamiając każdego, kto pracujeorigin/dev
, że prawdopodobnie czeka go zły dzień. Lepszą odpowiedzią jest znowu „nie rób tego ... zamiast tego użyj scalania”Krótka odpowiedź
Pominąłeś fakt, że uruchomiłeś
git push
, pojawił się następujący błąd, a następnie przystąpiłeś do uruchamianiagit pull
:Mimo że Git stara się być pomocny, jego rada „git pull” najprawdopodobniej nie jest tym, co chcesz zrobić .
Jeśli jesteś:
git push --force
aktualizację pilota za pomocą zatwierdzeń po rebase ( zgodnie z odpowiedzią użytkownika4405677 ).git rebase
w pierwszej kolejności. Aby zaktualizowaćdev
ze zmianami zmaster
, powinieneś zamiast biegaćgit rebase master dev
,git merge master
gdy jest włączonydev
( zgodnie z odpowiedzią Justina ).Nieco dłuższe wyjaśnienie
Każdy skrót zatwierdzenia w Git jest oparty na wielu czynnikach, z których jednym jest skrót zatwierdzenia, który występuje przed nim.
Jeśli zmienisz kolejność zatwierdzeń, zmienisz skróty zatwierdzeń; rebasing (kiedy coś robi) zmieni skróty zatwierdzeń. Dzięki temu wynik uruchomienia
git rebase master dev
, gdzie niedev
jest zsynchronizowany zmaster
, utworzy nowe zatwierdzenia (a tym samym skróty) z taką samą zawartością jak te włączone,dev
ale z zatwierdzeniamimaster
wstawionymi przed nimi.W takiej sytuacji możesz znaleźć się na wiele sposobów. Dwa sposoby, które przychodzą mi do głowy:
master
których chcesz oprzeć swojądev
pracędev
które zostały już przesłane do pilota, a następnie przystąpisz do ich zmiany (przeredaguj komunikaty o zatwierdzeniach, zmień kolejność zatwierdzeń, zatwierdzeń squash itp.)Lepiej zrozummy, co się stało - oto przykład:
Masz repozytorium:
Następnie przystępujesz do zmiany zatwierdzeń.
(Tutaj musisz uwierzyć na moje słowo: istnieje wiele sposobów zmiany zatwierdzeń w Git. W tym przykładzie zmieniłem czas
C3
, ale ty wstawiasz nowe zatwierdzenia, zmieniasz komunikaty zmian, zmieniam kolejność zatwierdzeń, zgniatanie zatwierdzeń, itp.)W tym miejscu ważne jest, aby zauważyć, że skróty zatwierdzania są różne. Jest to oczekiwane zachowanie, ponieważ zmieniłeś coś (cokolwiek) w nich. To jest w porządku, ALE:
Próba pchnięcia spowoduje wyświetlenie błędu (i podpowiedź, że powinieneś uruchomić
git pull
).Jeśli biegniemy
git pull
, widzimy ten dziennik:Lub pokazane w inny sposób:
A teraz mamy lokalnie zduplikowane zatwierdzenia. Gdybyśmy mieli uruchomić
git push
, wysłalibyśmy je na serwer.Aby uniknąć dotarcia do tego etapu, mogliśmy biec
git push --force
(gdzie zamiast tego biegliśmygit pull
). To bez problemu wysłałoby nasze zatwierdzenia z nowymi hashami na serwer. Aby rozwiązać problem na tym etapie, możemy cofnąć się do stanu sprzed uruchomieniagit pull
:Spójrz na reflog (
git reflog
), aby zobaczyć, jaki był skrót zatwierdzenia przed uruchomieniemgit pull
.Powyżej widzimy, że
ba7688a
było to zobowiązanie, na którym byliśmy przed uruchomieniemgit pull
. Z tym hashem commita w ręku możemy zresetować z powrotem do tego (git reset --hard ba7688a
), a następnie uruchomićgit push --force
.Gotowe.
Ale czekaj, kontynuowałem pracę na podstawie zduplikowanych zatwierdzeń
Jeśli w jakiś sposób nie zauważyłeś, że zatwierdzenia zostały zduplikowane i kontynuowałeś pracę nad zduplikowanymi zatwierdzeniami, naprawdę narobiłeś sobie bałaganu. Rozmiar bałaganu jest proporcjonalny do liczby zatwierdzeń, które masz na szczycie duplikatów.
Jak to wygląda:
Lub pokazane w inny sposób:
W tym scenariuszu chcemy usunąć zduplikowane zatwierdzenia, ale zachować zmiany, które oparliśmy na nich - chcemy zachować C6 do C10. Podobnie jak w przypadku większości rzeczy, można to zrobić na kilka sposobów:
Zarówno:
cherry-pick
każdym zatwierdzeniu (włącznie z C6 do C10) na tę nową gałąź i traktuj tę nową gałąź jako kanoniczną.git rebase --interactive $commit
, gdzie$commit
jest zatwierdzeniem przed obydwoma zduplikowanymi zatwierdzeniami 2 . Tutaj możemy wprost usunąć linie dla duplikatów.1 To nie ma znaczenia, który z nich wybrać, albo
ba7688a
czy2a2e220
działają poprawnie.2 W tym przykładzie byłoby to
85f59ab
.TL; DR
Ustaw
advice.pushNonFastForward
nafalse
:źródło
git push
ich w--force-with-lease
dzisiejszych czasach, ponieważ jest to lepsze ustawienie domyślneMyślę, że opisując swoje kroki, pominąłeś ważny szczegół. Mówiąc dokładniej, twój ostatni krok,
git push
na dev, faktycznie spowodowałby błąd, ponieważ normalnie nie możesz przesuwać zmian, które nie są szybkie do przodu.Zrobiłeś to
git pull
przed ostatnim wypchnięciem, co spowodowało zatwierdzenie scalenia z C6 i C6 jako rodzicami, dlatego oba pozostaną wymienione w dzienniku. Ładniejszy format dziennika mógł uczynić bardziej oczywistym, że są to połączone gałęzie zduplikowanych zatwierdzeń.Albo zamiast tego utworzyłeś
git pull --rebase
(lub bez jawnego,--rebase
jeśli jest to sugerowane przez twoją konfigurację), co ściągnęło oryginalne C5 i C6 z powrotem do twojego lokalnego dewelopera (i ponownie przestawiłeś następujące na nowe skróty, C7 'C5' 'C6' ').Jednym ze sposobów wyjścia z tego mogło być
git push -f
wymuszenie pchnięcia, gdy wystąpił błąd i wyczyszczenie C5 C6 z początku, ale jeśli ktoś inny również je wyciągnął, zanim je wyczyściłeś, będziesz miał o wiele więcej kłopotów. w zasadzie każdy, kto ma C5 C6, musiałby wykonać specjalne kroki, aby się ich pozbyć. Właśnie dlatego mówią, że nigdy nie należy zmieniać bazy niczego, co zostało już opublikowane. Nadal jest to wykonalne, jeśli powiedzenie „publikowanie” odbywa się w małym zespole.źródło
git pull
jest kluczowe. Twoja rekomendacjagit push -f
, choć niebezpieczna, jest prawdopodobnie tym, czego szukają czytelnicy.git push --force
, tylko po to, aby zobaczyć, co zrobi Git. Od tamtej pory wiele się nauczyłem o Git i obecnierebase
jest to część mojego normalnego przepływu pracy. Jednak staramgit push --force-with-lease
się unikać nadpisywania cudzej pracy.--force-with-lease
jest dobrym ustawieniem domyślnym, zostawię komentarz również pod moją odpowiedziąDowiedziałem się, że w moim przypadku ten problem jest konsekwencją problemu z konfiguracją Gita. (Obejmuje ciągnięcie i scalanie)
Opis problemu:
Objawy: Zatwierdzenia zduplikowane w gałęzi podrzędnej po rebase, co oznacza liczne scalenia w trakcie i po ponownym bazowaniu.
Przepływ pracy: Oto kroki przepływu pracy, które wykonywałem:
Konsekwencją tego przepływu pracy jest powielenie wszystkich zatwierdzeń „Feature-branch” od czasu poprzedniej rebase ... :-(
Problem wynikał z konieczności wprowadzenia zmian w gałęzi potomnej przed rebase. Domyślna konfiguracja ściągania Git to „scalanie”. To zmienia indeksy zatwierdzeń wykonywanych na gałęzi potomnej.
Rozwiązanie: w pliku konfiguracyjnym Git skonfiguruj pull do pracy w trybie rebase:
Mam nadzieję, że to pomoże JN Grx
źródło
Być może ściągnąłeś ze zdalnej gałęzi innej niż Twoja obecna. Na przykład mogłeś wyciągnąć z Mastera, gdy twoja gałąź rozwija śledzenie rozwoju. Git sumiennie pobierze zduplikowane zatwierdzenia, jeśli zostanie pobrany z nieśledzonej gałęzi.
Jeśli tak się stanie, możesz wykonać następujące czynności:
gdzie
n == <number of duplicate commits that shouldn't be there.>
Następnie upewnij się, że wyciągasz z właściwej gałęzi, a następnie uruchom:
Ciągnięcie za pomocą
--rebase
zapewni, że nie dodasz zbędnych zatwierdzeń, które mogłyby zagmatwać historię zmian.Oto trochę trzymania ręki dla rebase git.
źródło