
🐙 Błąd Git «fatal: refusing to merge unrelated histories»: przyczyny i rozwiązania
Wykonali Państwo git pull i zamiast oczekiwanych zmian terminal przywitał Państwa czerwonym komunikatem: fatal: refusing to merge unrelated histories. Projekt się nie zaktualizował, commity nie zostały pobrane, a Państwo zastanawiają się, czy nie uszkodzili Państwo repozytorium.
Nie, nie uszkodzili Państwo. Git po prostu odmawia zmieszania dwóch niezależnych historii i jest to przemyślane zachowanie, a nie błąd. W ciągu pięciu minut nie tylko zrozumieją Państwo, dlaczego ten błąd występuje, ale także nauczą się go naprawiać w każdej sytuacji: podczas pierwszego pusha, po utracie .git oraz przy łączeniu różnych projektów.
💡 Szybki przegląd:
- Kiedy Git odmawia scalenia niepowiązanych gałęzi i dlaczego jest to słuszne
- Flaga
--allow-unrelated-histories, uniwersalne rozwiązanie dlapull,mergei pierwszego pusha - Scenariusze krok po kroku: klonowanie bez historii, nowe repozytorium, rebase i force-push
- Co zrobić, jeśli flaga nie pomogła, i jak nie doprowadzać do tego błędu w przyszłości
Co oznacza błąd i dlaczego Git go zgłasza
Git śledzi historię poprzez łańcuch commitów. Każdy commit odwołuje się do rodzica, w ten sposób budowany jest graf, na podstawie którego Git rozumie, co skąd pochodzi. Kiedy wykonują Państwo git merge, Git szuka wspólnego przodka dwóch gałęzi i na jego podstawie oblicza różnicę.
Ale czasami wspólnego przodka po prostu nie ma. Dwa grafy commitów nie przecinają się, jak dwa osobne projekty, które nigdy o sobie nie wiedziały. W takiej sytuacji Git odmawia ślepego scalenia historii i wyrzuca:
1 fatal: refusing to merge unrelated histories
To nie jest błąd w zwykłym sensie. To zabezpieczenie: Git mówi Państwu „nie rozumiem, jak te dwie historie są powiązane, więc nie będę zgadywać". Rozwiązanie istnieje i jest wbudowane w Gita, począwszy od wersji 2.9.0 (wydanej w czerwcu 2016 roku).
Dwa typowe scenariusze prowadzące do błędu
Pierwszy scenariusz, **uszkodzenie lub usunięcie katalogu **.git. Sklonowali Państwo projekt, pracowali z kodem, ale folder .git został usunięty (przypadkowo, przez antywirusa lub podczas kopiowania bez ukrytych plików). Git traci całą lokalną historię i podczas próby git push lub git pull postrzega Państwa katalog roboczy jako zupełnie nowy projekt, w żaden sposób niepowiązany ze zdalnym repozytorium.
Drugi scenariusz, nowe repozytorium spotyka się z istniejącym. Wykonali Państwo git init, dodali kilka commitów lokalnie, a następnie spróbowali podłączyć zdalne repozytorium, które ma już własną historię. Git widzi dwa niezależne grafy commitów i odmawia ich zmieszania. Często zdarza się to, gdy zaczynają Państwo projekt od zera, a potem decydują się załadować go na GitHub na istniejące repozytorium, lub gdy przenoszą kod z jednego projektu do drugiego.
Oba scenariusze rozwiązuje się tym samym mechanizmem, ale przed jego zastosowaniem warto zrozumieć, co dokładnie chcą Państwo uzyskać w rezultacie: scalenie dwóch historii w jedną czy całkowite zastąpienie jednej historii drugą.
Rozwiązanie: flaga --allow-unrelated-histories
Kluczem do naprawy jest flaga --allow-unrelated-histories. Mówi ona Gitowi wprost: „Wiem, że te gałęzie nie mają wspólnego przodka i świadomie chcę je scalić". Flaga działa z obiema głównymi komendami, git pull i git merge.
**Dla **git pull (najczęstszy przypadek):
1 git pull origin main --allow-unrelated-histories
Proszę zastąpić main nazwą Państwa gałęzi, jeśli jest inna (master, develop itp.). Git utworzy merge-commit, który połączy dwie niezależne historie. Najprawdopodobniej otworzy się edytor komunikatu commita, proszę opisać, dlaczego łączą Państwo historie, zapisać i zamknąć edytor.
**Dla **git merge (gdy gałęzie są lokalne):
1 git merge feature-branch --allow-unrelated-histories
Po udanym scaleniu Git zaproponuje wypchnięcie wyniku. Proszę nie zapomnieć tego zrobić:
1 git push origin main
Ważny niuans: --allow-unrelated-histories nie eliminuje konfliktów scalania. Jeśli w obu gałęziach są pliki o takich samych nazwach, Git i tak poprosi Państwa o ręczne rozwiązanie konfliktów, flaga odpowiada tylko za połączenie historii, a nie za zawartość plików.
Scenariusze krok po kroku dla różnych sytuacji
Sytuacja 1: pierwszy push do niepustego zdalnego repozytorium
Utworzyli Państwo projekt lokalnie (git init → commity), a na GitHubie jest już repozytorium z README.md i .gitignore. Bezpośredni git push nie przejdzie, ponieważ zdalna gałąź zawiera commity, których Państwo nie mają.
Prawidłowa kolejność:
Najpierw proszę pobrać zdalną historię i scalić ją z Państwa lokalną:
1 git pull origin main --allow-unrelated-histories
Proszę rozwiązać konflikty, jeśli występują (zazwyczaj konfliktuje README.md), wykonać commit scalenia, następnie:
1 git push origin main
Sytuacja 2: odzyskiwanie po utracie.git
Folder .git został usunięty, ale katalog roboczy jest nienaruszony. Można przywrócić połączenie ze zdalnym repozytorium bez utraty niezacommitowanych zmian:
1 git init 2 git remote add origin <url-репозитория> 3 git fetch origin 4 git reset --mixed origin/main
Komenda git reset --mixed synchronizuje indeks Gita ze zdalną gałęzią, ale zachowuje wszystkie Państwa pliki robocze nietknięte. Następnie proszę dodać zmiany i wykonać nowy commit:
1 git add . 2 git commit -m "Восстановление после потери .git" 3 git push origin main
To podejście jest lepsze niż --allow-unrelated-histories, ponieważ nie tworzy sztucznego merge-commita i zachowuje historię czystą.
Sytuacja 3: rebase z niepowiązanymi historiami
Komenda git rebase również może zgłosić ten błąd, szczególnie często podczas używania flagi --preserve-merges (obecnie zastąpionej przez --rebase-merges). Rozwiązaniem jest dodanie --allow-unrelated-histories:
1 git rebase --rebase-merges --allow-unrelated-histories main
Proszę jednak być ostrożnym: rebase przepisuje historię i jeśli ktoś jeszcze pracuje z tą gałęzią, stworzą mu Państwo problemy. Dla współdzielonych gałęzi zawsze proszę preferować merge.
Co zrobić, jeśli flaga nie pomogła
Czasami --allow-unrelated-histories wykonuje się bez błędów, ale rezultat nie jest tym, czego Państwo chcieli.
Problem: merge-commit zaśmieca historię. Jeśli połączyli Państwo dwa duże projekty, graf commitów staje się nieczytelny. W takim przypadku proszę rozważyć alternatywę, przeniesienie plików z zachowaniem historii poprzez git format-patch i git am:
1 git format-patch --root -o patches/ HEAD 2 git am patches/*.patch
Problem: po scaleniu projekt się nie kompiluje. Scalanie niepowiązanych historii może prowadzić do duplikacji plików konfiguracyjnych, konfliktów zależności lub niekompatybilnych wersji pakietów. Po --allow-unrelated-histories zawsze proszę sprawdzać: zależności (npm install / composer install), pliki konfiguracyjne (.env, config/), ścieżki i importy w kodzie. Lepiej poświęcić pięć minut na sprawdzenie teraz, niż rozwiązywać problem z padającym buildem w CI później.
Problem: rozmyślili się Państwo. Wycofanie scalenia niepowiązanych historii można wykonać standardowo, git reset --hard HEAD~1 (jeśli jeszcze nie wypchnęli Państwo wyniku) lub git revert -m 1 HEAD (jeśli już wypchnęli).
Jak uniknąć błędu w przyszłości
Trzy proste zasady, które uchronią Państwa przed tym błędem w codziennej pracy.
Nie usuwać .git bez skrajnej potrzeby. Jeśli trzeba skopiować kod bez historii, proszę używać git archive lub kopiować pliki, świadomie wykluczając ukryty folder .git, a nie przypadkowo.
Nie tworzyć nowego repozytorium wewnątrz istniejącego. Jeśli trzeba wydzielić część bazy kodu do osobnego projektu, proszę używać git subtree split lub git filter-branch (obecnie zalecane jest git filter-repo). Te narzędzia zachowają historię potrzebnych plików i Git będzie wiedział, skąd pochodzą.
Przed git init w folderze z kodem zawsze proszę sprawdzać, czy nie ma tam już repozytorium: git status. Jeśli Git odpowiada fatal: not a git repository, można inicjalizować. Jeśli pokazuje status, są już Państwo wewnątrz istniejącego repozytorium i git init nie jest tu potrzebne.
Tym, którzy dopiero zaczynają pracę z Gitem, polecamy nasz poradnik „Git instrukcja dla początkujących", gdzie krok po kroku omówiono klucze SSH, tworzenie repozytorium i pełny cykl pracy z GitHubem. A jeśli Git nie jest jeszcze zainstalowany, proszę zacząć od instrukcji „Jak zainstalować Git w Windows".
⁉️🤔 Częste pytania
W jakich wersjach Gita działa --allow-unrelated-histories?
Flaga pojawiła się w Git 2.9.0 (czerwiec 2016) i jest obecna we wszystkich kolejnych wersjach. Jeśli Państwa wersja Gita jest starsza, proszę ją zaktualizować: komenda
git --versionpokaże bieżącą, agit update-git-for-windows(na Windows) lub menedżer pakietów Państwa systemu zaktualizuje do aktualnej. Najprościej sprawdzić wersję Gita komendągit --versionw terminalu. Na połowę 2026 roku aktualna jest gałąź 2.48+. Jeśli są Państwo na Windows i Git był instalowany bardzo dawno, proszę pobrać świeży instalator z git-scm.com, autoaktualizacja w starych wersjach działała niestabilnie.
Czy można używać flagi bezpośrednio z git push?
Nie,
git pushnie przyjmuje--allow-unrelated-histories. Push nie tworzy scalenia, jedynie wysyła istniejące commity. Błąd „unrelated histories" podczas push oznacza, że Państwa lokalna gałąź i zdalna rozeszły się na poziomie historii. Rozwiązanie: najpierwgit pull --allow-unrelated-histories, proszę rozwiązać konflikty i dopiero potemgit push. Formalnie--allow-unrelated-historiesdziała zgit fetch+git mergeoraz zgit pull(który wewnętrznie wykonuje fetch + merge). Push pozostaje osobną operacją, którą wykonują Państwo po udanym scaleniu. Proszę nie próbować obchodzić tego przez--force, stracą Państwo cudze commity na zdalnym repozytorium.
Co jest lepsze dla czystości historii: merge czy rebase?
Dla łączenia niepowiązanych historii zdecydowanie
merge. Rebase w tym kontekście stwarza więcej problemów, niż rozwiązuje: próbuje odtworzyć commity jednej gałęzi na drugiej, ale bez wspólnego przodka prowadzi to do konfliktów przy każdym commicie. Merge z--allow-unrelated-historiesrobi dokładnie to, co potrzebne, tworzy jeden punkt połączenia dwóch grafów, po którym historia jest jednolita. Wyjątek stanowi sytuacja, gdy celowo chcą Państwo przepisać historię i dokładnie wiedzą, co robią. Na przykład podczas przenoszenia kodu z jednego repozytorium do drugiego z oczyszczeniem ze starych commitów. W tym przypadkugit rebase --allow-unrelated-historiesmoże mieć sens, ale do codziennej pracy proszę wybierać merge.
Utraciłem folder .git, ale mam niezacommitowane zmiany. Czy je stracę?
Nie, nie stracą ich Państwo. Sam folder
.gitzawiera tylko historię i metadane Gita, ale nie Państwa pliki robocze. Wszystkie zmienione, nowe, a nawet niezacommitowane pliki pozostaną w katalogu roboczym nietknięte. Procedura odzyskiwania została opisana w sekcji „Sytuacja 2" powyżej,git init→git remote add→git fetch→git reset --mixed. Kluczowy moment: proszę użyć właśnie--mixed, a nie--hard. Flaga--mixedresetuje indeks, ale zachowuje wszystkie zmiany w plikach. Jeśli nie są Państwo pewni, proszę zrobić kopię zapasową całego folderu projektu przed odzyskiwaniem, zajmie to dziesięć sekund i całkowicie wyeliminuje ryzyko utraty danych w każdej niestandardowej sytuacji.
Błąd występuje podczas klonowania przez IDE. Czy to ten sam problem?
Tak, ten sam. Niektóre IDE (na przykład PHPStorm, Visual Studio, stare wersje IntelliJ) podczas tworzenia projektu z szablonu inicjalizują nowe repozytorium Git, a następnie próbują podłączyć zdalne. Powstaje dokładnie drugi scenariusz z początku artykułu. Rozwiązanie jest takie samo: proszę otworzyć terminal w folderze projektu i wykonać
git pull origin main --allow-unrelated-histories. Po ręcznym scaleniu IDE automatycznie wykryje nowy stan, wystarczy odświeżyć okno projektu lub kliknąć Refresh na panelu Git.
Czy należy bać się błędu „unrelated histories"?
Nie. To jeden z najbezpieczniejszych błędów Gita, nie uszkadza danych, nie usuwa plików i nie przeszkadza w kontynuowaniu pracy. Flaga --allow-unrelated-histories nie jest protezą ani obejściem, lecz udokumentowaną możliwością, specjalnie dodaną przez twórców Gita na wypadek, gdy świadomie chcą Państwo połączyć dwie niezależne historie.
Po opanowaniu tej komendy zyskują Państwo do dyspozycji potężne narzędzie: teraz mogą Państwo łączyć projekty o dowolnym stopniu izolacji, przenosić kod między repozytoriami i odzyskiwać pracę po utracie .git, a wszystko to bez paniki i tworzenia repozytorium od nowa. Jeśli Git czegoś dziś Państwa nauczył, to tego, że „fatal" w jego komunikatach nie oznacza „fatalny dla projektu", oznacza „nie będę zgadywać, powiedz mi wprost, co robić".



