Skip to content

Kõik WordPressist, veebiarendusest — ja mitte ainult

🐙 Git-viga fatal refusing to merge unrelated histories: põhjused ja lahendus

🐙 Git-viga fatal refusing to merge unrelated histories: põhjused ja lahendus

Sa tegid git pull ja oodatud muudatuste asemel tervitas terminal sind punase reaga: fatal: refusing to merge unrelated histories. Projekt ei uuenenud, kommitid ei tõmmanud ja sa istud ning mõtled, kas lõhkusid repositooriumi.

Ei, sa ei lõhkunud seda. Git lihtsalt keeldub kahte sõltumatut ajalugu segamast ja see on tahtlik käitumine, mitte viga. Viie minuti pärast sa mitte ainult ei mõista, miks see viga tekib, vaid õpid ka seda igas olukorras parandama: esimesel tõukamisel, pärast .git kaotamist ja erinevate projektide ühendamisel.

💡 Kiire ülevaade:

  • Millal Git keeldub mitteseotud harusid ühendamast ja miks see on õige
  • --allow-unrelated-histories lipp, universaalne lahendus pull, merge ja esimese push'i jaoks
  • Samm-sammult stsenaariumid: kloonimine ilma ajaloota, uus repositoorium, rebase ja force-push
  • Mida teha, kui lipp ei aita, ja kuidas seda viga tulevikus vältida

Mida viga tähendab ja miks Git selle viskab

Git jälgib ajalugu läbi kommitide ahela. Iga kommit viitab oma eellasele, ehitades graafi, mida Git kasutab mõistmaks, mis kust tuli. Kui teed git merge, otsib Git kahe haru ühist eellast ja arvutab sellest lähtuvalt erinevuse.

Kuid mõnikord pole ühist eellast lihtsalt olemas. Kaks kommitigraafi ei lõiku, nagu kaks eraldi projekti, mis teineteisest kunagi ei teadnud. Sellises olukorras keeldub Git ajalugusid pimesi ühendamast ja viskab:

1fatal: refusing to merge unrelated histories

See pole viga tavapärases mõttes. See on kaitse: Git ütleb sulle: „Ma ei mõista, kuidas need kaks ajalugu on seotud, seega ma ei hakka oletama." Lahendus on olemas ja see on Giti sisse ehitatud alates versioonist 2.9.0 (välja antud juunis 2016).

Kaks tüüpilist stsenaariumi, mis veani viivad

Esimene stsenaarium, .git kataloogi riknemine või kustutamine. Sa kloonisid projekti, töötasid koodiga, kuid .git kaust sai kustutatud (kogemata, viirusetõrje poolt või kopeerimisel ilma peidetud failideta). Git kaotab kogu kohaliku ajaloo ja git push või git pull katsel käsitleb sinu töökataloogi täiesti uue projektina, mis pole kaugrepositooriumiga seotud.

Teine stsenaarium, uus repositoorium kohtub olemasolevaga. Sa tegid git init, lisasid mitu kommiti kohapeal, seejärel proovisid ühendada kaugrepositooriumi, millel on juba oma ajalugu. Git näeb kahte sõltumatut kommitigraafi ja keeldub neid segamast. See juhtub sageli, kui alustad projekti nullist, seejärel otsustad selle GitHubi üles laadida olemasoleva repositooriumi peale või koodi ühest projektist teise üle kandes.

Mõlemad stsenaariumid lahendatakse sama mehhanismiga, kuid enne selle rakendamist peaksid mõistma, mida täpselt saavutada soovid: kahe ajaloo ühendamist üheks või ühe ajaloo täielikku asendamist teisega.

Lahendus: --allow-unrelated-histories lipp

Selle parandamise võti on --allow-unrelated-histories lipp. See ütleb Gitile selgesõnaliselt: „Ma tean, et neil harudel pole ühist eellast ja ma tahan neid teadlikult ühendada." Lipp töötab mõlema peamise käsuga, git pull ja git merge.

git pull jaoks (kõige tavalisem juhtum):

1git pull origin main --allow-unrelated-histories

Asenda main oma haru nimega, kui see erineb (master, develop jne). Git loob ühendamiskommiti, mis seob kaks sõltumatut ajalugu. Tõenäoliselt avaneb redaktor kommiti sõnumi jaoks, kirjelda, miks sa ajalugusid ühendad, salvesta ja sulge redaktor.

git merge jaoks (kui harud on kohalikud):

1git merge feature-branch --allow-unrelated-histories

Pärast edukat ühendamist palub Git sul tulemuse üles lükata. Ära unusta seda teha:

1git push origin main

Oluline nüanss: --allow-unrelated-histories ei kõrvalda ühendamiskonflikte. Kui mõlemal harul on samanimelised failid, palub Git sul konfliktid ikkagi käsitsi lahendada, lipp tegeleb ainult ajalugude ühendamisega, mitte failide sisuga.

Samm-sammult stsenaariumid erinevateks olukordadeks

Olukord 1: esimene push mittetühja kaugrepositooriumisse

Sa lõid projekti kohapeal (git init → kommitid) ja GitHubis on juba repositoorium koos README.md ja .gitignore failidega. Otsene git push ei tööta, sest kaug-haru sisaldab kommitte, mida sul pole.

Õige järjekord:

Esmalt tõmba kauge ajalugu ja ühenda see oma kohalikuga:

1git pull origin main --allow-unrelated-histories

Lahenda konfliktid, kui neid on (tavaliselt README.md konfliktid), tee ühendamiskommit, seejärel:

1git push origin main

Olukord 2: taastamine pärast.git kaotamist

.git kaust on kustutatud, kuid töökataloog on puutumata. Saad taastada ühenduse kaugrepositooriumiga, kaotamata salvestamata muudatusi:

1git init
2git remote add origin <repository-url>
3git fetch origin
4git reset --mixed origin/main

Käsk git reset --mixed sünkroniseerib Giti indeksi kaug-haruga, kuid jätab kõik sinu tööfailid puutumata. Pärast seda lisa muudatused ja tee uus kommit:

1git add .
2git commit -m "Recovery after losing .git"
3git push origin main

See lähenemine on eelistatavam kui --allow-unrelated-histories, sest see ei loo kunstlikku ühendamiskommiti ja hoiab ajaloo puhtana.

Olukord 3: rebase mitteseotud ajalugudega

Ka git rebase käsk võib selle vea visata, eriti kui kasutad --preserve-merges lippu (nüüd asendatud --rebase-merges lipuga). Lahendus, lisa --allow-unrelated-histories:

1git rebase --rebase-merges --allow-unrelated-histories main

Kuid ole ettevaatlik: rebase kirjutab ajaloo ümber ja kui keegi teine selle haruga töötab, tekitate talle probleeme. Jagatud harude puhul eelista alati merge käsku.

Mida teha, kui lipp ei aita

Mõnikord töötab --allow-unrelated-histories vigadeta, kuid tulemus pole see, mida soovisid.

Probleem: ühendamiskommit risustab ajalugu. Kui ühendasid kaks suurt projekti, muutub kommitigraaf raskesti loetavaks. Sel juhul kaalu alternatiivi, failide ülekandmist ajaloo säilitamisega git format-patch ja git am abil:

1git format-patch --root -o patches/ HEAD
2git am patches/*.patch

Probleem: pärast ühendamist projekt ei kompileeru. Mitteseotud ajalugude ühendamine võib viia duplikaat konfiguratsioonifailide, sõltuvuste konfliktide või mitteühilduvate paketiversioonideni. Pärast --allow-unrelated-histories kontrolli alati: sõltuvused (npm install / composer install), konfiguratsioonifailid (.env, config/), teed ja impordid koodis. Parem kulutada viis minutit kontrollimisele nüüd kui tegeleda ebaõnnestunud ehitustega CI-s hiljem.

Probleem: sa muutsid meelt. Saad mitteseotud ajalugude ühendamise tagasi võtta standardsel viisil, git reset --hard HEAD~1 (kui sa pole tulemust veel üles lükanud) või git revert -m 1 HEAD (kui sa juba lükkasid üles).

Kuidas viga tulevikus vältida

Kolm lihtsat reeglit, mis säästavad sind sellest veast igapäevatöös.

Ära kustuta .git ilma absoluutse vajaduseta. Kui pead kopeerima koodi ilma ajaloota, kasuta git archive või kopeeri failid, jättes peidetud .git kausta teadlikult, mitte kogemata välja.

Ära loo uut repositooriumi olemasoleva sisse. Kui pead osa koodibaasist eraldi projekti eraldama, kasuta git subtree split või git filter-branch (nüüd soovitatakse git filter-repo). Need tööriistad säilitavad vajalike failide ajaloo ja Git teab, kust need tulid.

Enne git init kasutamist koodiga kaustas kontrolli alati, kas seal on juba repositoorium: git status. Kui Git vastab fatal: not a git repository, võid initsialiseerida. Kui see näitab staatust, oled juba olemasolevas repositooriumis ja git init pole siin vajalik.

Neile, kes alles alustavad Gitiga töötamist, soovitame meie juhendit "Git'i juhend algajatele", see käsitleb SSH võtmeid, repositooriumi loomist ja kogu GitHubi töövoogu samm-sammult. Ja kui Git pole veel installitud, alusta juhendist "Kuidas installida Git Windowsile".

⁉️🤔 Korduma kippuvad küsimused

Millised Giti versioonid toetavad --allow-unrelated-histories lippu?

Lipp ilmus Git 2.9.0-s (juuni 2016) ja on olemas kõigis järgnevates versioonides. Kui sinu Giti versioon on vanem, uuenda seda: käsk git --version näitab praegust versiooni ja git update-git-for-windows (Windowsis) või sinu süsteemi paketihaldur uuendab praeguse väljalaskeni. Lihtsaim viis Giti versiooni kontrollimiseks on git --version käsk terminalis. 2026. aasta keskpaiga seisuga on praegune haru 2.48+. Kui oled Windowsis ja Git paigaldati ammu, lae värske paigaldaja alla saidilt git-scm.com, automaatne uuendamine vanades versioonides töötas ebastabiilselt.

Kas ma saan lippu kasutada otse git push käsuga?

Ei, git push ei aktsepteeri --allow-unrelated-histories lippu. Push ei loo ühendamist, see saadab ainult olemasolevaid kommitte. Viga „unrelated histories" push'i puhul tähendab, et sinu kohalik ja kauge haru on ajaloo tasemel lahknenud. Lahendus: esmalt git pull --allow-unrelated-histories, lahenda konfliktid ja alles seejärel git push. Formaalselt töötab --allow-unrelated-histories käskudega git fetch + git merge ja git pull (mis sisemiselt teeb fetch + merge). Push jääb eraldi operatsiooniks, mille sooritad pärast edukat ühendamist. Ära ürita sellest mööda hiilida --force lipuga, sa kaotad teiste inimeste kommitid kaugrepositooriumis.

Mis on puhta ajaloo jaoks parem: merge või rebase?

Mitteseotud ajalugude ühendamiseks kindlasti merge. Rebase selles kontekstis tekitab rohkem probleeme kui lahendab: see üritab ühe haru kommitte teise peale uuesti esitada, kuid ilma ühise eellaseta viib see konfliktideni iga kommiti juures. Merge koos --allow-unrelated-histories lipuga teeb täpselt seda, mida vaja, loob ühe ühenduspunkti kahe graafi vahel, mille järel on ajalugu ühtne. Erand: kui soovid tahtlikult ajalugu ümber kirjutada ja tead täpselt, mida teed. Näiteks koodi ühest repositooriumist teise ülekandmisel koos vanadest kommititest puhastamisega. Sel juhul võib git rebase --allow-unrelated-histories olla mõttekas, kuid igapäevatööks vali merge.

Kaotasin .git kausta, kuid mul on salvestamata muudatusi. Kas ma kaotan need?

Ei, sa ei kaota neid. .git kaust ise sisaldab ainult ajalugu ja Giti metaandmeid, kuid mitte sinu tööfaile. Kõik muudetud, uued ja isegi salvestamata failid jäävad töökataloogi puutumata. Taastamise protseduur on kirjeldatud ülalpool „Olukord 2" all, git initgit remote addgit fetchgit reset --mixed. Võtmepunkt: kasuta täpselt --mixed, mitte --hard. --mixed lipp lähtestab indeksi, kuid säilitab kõik muudatused failides. Kui pole kindel, tee enne taastamist kogu projektikaustast varukoopia, see võtab kümme sekundit ja välistab täielikult andmekao riski igas mittestandardses olukorras.

Viga ilmneb läbi IDE kloonimisel. Kas see on sama probleem?

Jah, sama. Mõned IDE-d (näiteks PHPStorm, Visual Studio, IntelliJ vanemad versioonid) initsialiseerivad projektist mallist loomisel uue Giti repositooriumi ja proovivad seejärel kaugrepositooriumiga ühenduda. See on täpselt teine stsenaarium artikli algusest. Lahendus on sama: ava terminal projektikaustas ja käivita git pull origin main --allow-unrelated-histories. Pärast käsitsi ühendamist tuvastab IDE uue oleku automaatselt, lihtsalt värskenda projekti akent või klõpsa Git paneelil Refresh.

Kas peaksid kartma viga „unrelated histories"?

Ei. See on üks ohutumaid Giti vigu, see ei riku andmeid, ei kustuta faile ega takista töö jätkamist. --allow-unrelated-histories lipp pole kark ega möödahiilimine, vaid dokumenteeritud võimalus, mille Giti arendajad spetsiaalselt lisasid juhtudeks, kui soovid teadlikult ühendada kaks sõltumatut ajalugu.

Olles selle käsu selgeks õppinud, saad võimsa tööriista: nüüd saad ühendada mis tahes isolatsiooniastmega projekte, kanda koodi repositooriumide vahel ja taastada töö pärast .git kaotamist, ja seda kõike ilma paanikata ja repositooriumi nullist uuesti loomata. Kui Git sulle täna midagi õpetas, siis seda, et „fatal" tema sõnumites ei tähenda „projektile saatuslik", vaid „ma ei hakka oletama, ütle mulle selgesõnaliselt, mida teha."