Početna
» Osnovno znanje
»
Kako popraviti "Git Push Rejected: Non-FastForward" bez gubitka promjena
Kako popraviti "Git Push Rejected: Non-FastForward" bez gubitka promjena
Odbijanje koje nije premotavanje unaprijed je Git koji štiti povijest, a ne briše vaš lokalni rad. Obično znači da se udaljena grana pomaknula naprijed nakon što je vaša lokalna grana zadnji put sinkronizirana, pa bi ažuriranje udaljene grane s vašom trenutnom granom odbacilo promjene koje već postoje tamo. Trenutna dokumentacija GitHuba opisuje istu situaciju: prvo dohvatite promjene uzvodno, integrirajte ih lokalno, a zatim ponovno pošaljite.
Najsigurniji opći tijek rada je: zaštititi sav nepotvrđeni rad, dohvatiti udaljenu granu bez mijenjanja trenutne grane, pregledati kako su se povijesti razišle, odabrati spajanje ili ponovno baziranje, riješiti sukobe ako je potrebno i ponovno poslati. Nemojte počinjati s git push --forceili git reset --hardsamo da biste uklonili grešku.
Pretpostavimo da vaša lokalna maingrana sadrži commit L, dok je udaljena grana origin/mainnapredovala na drugi commit R. Ako nijedan commit nije predak drugog, povijesti su se razišle. Normalni push ne može jednostavno pomaknuti pokazivač udaljene grane na Lbez da Rnestane iz vidljive povijesti te grane.
Ažuriranje ubrzanim premotavanjem je drugačije: novi savjet grane je potomak stare, pa pomicanje grane naprijed čuva sve što je već dostupno iz nje. Službeni git pushpriručnik definira ovo pravilo predaka i upozorava da ono --forceonemogućuje normalnu zaštitu i može uzrokovati gubitak udaljenih commitova.
Odbijanje koje ne omogućuje brzo premotavanje znači da Git odbija zamijeniti udaljenu povijest koju vaša lokalna grana još ne sadrži.
Prije nego što išta poduzmete: je li vaš lokalni rad sigurno zabilježen?
Trčanje:
git status
Ako je radno stablo čisto, vaše trenutne promjene su već predstavljene commitovima i možete stvoriti dodatnu sigurnosnu referencu prije integracije daljinskog upravljača:
git branch backup-before-sync
Ta grana pokazuje na trenutni commit, tako da imate jednostavan naziv za stanje prije integracije.
Ako ste modificirali ili nepraćene datoteke koje nisu potvrđene, odaberite jedan od ovih pristupa prije povlačenja ili ponovnog baziranja:
Potvrdite ih ako je rad logički spreman za potvrđivanje.
Spremite ih ako je posao nedovršen git stash push -u -m "before non-fast-forward fix":.
Opcija -uuključuje nepraćene datoteke. Službena dokumentacija za git-stash objašnjava da stash bilježi radni direktorij i stanje indeksa te vraća čisto radno stablo. Ignorirane datoteke nisu uključene od -u; -abi uključivao i ignorirane datoteke, ali to je rijetko potrebno za ovaj problem.
Važno: ako git statusprikazuje datoteke koje si ne možete priuštiti izgubiti, nemojte pokretati git reset --hard. Ta naredba može odbaciti nepotvrđene promjene radnog stabla.
Korak 1: Trebate li git pullodmah upotrijebiti?
Možete, ali je lakše razmišljati o tome da prvo dohvatite . GitHub dokumentira koji git fetchpreuzima udaljeni rad i ažurira grane za udaljeno praćenje bez spajanja tih promjena u vašu trenutnu granu. To ga čini korisnim dijagnostičkim korakom jer možete pregledati situaciju prije promjene lokalne povijesti.
Nakon dohvaćanja, grana za udaljeno praćenje origin/mainpredstavlja udaljeno stanje koje ste upravo dohvatili. Naredba log omogućuje vam da vidite commite dostupne samo s vaše lokalne strane i samo s udaljene strane.
Ako vaša lokalna grana nema jedinstvenih commitova i jednostavno kasni, često je možete premotati unaprijed:
git merge --ff-only origin/main
Tada nema ništa za usklađivanje; vaša grana jednostavno ide naprijed.
Korak 2: Spajanje ili ponovno baziranje - što odabrati?
Oba mogu sačuvati vaše promjene. Razlika je u obliku rezultirajuće povijesti.
Situacija
Obično biraju
Zašto
Zajednička grana, kao na primjer maingdje vaši lokalni commiti mogu već biti poznati drugima
Spojiti
Čuva postojeće identitete commitova i ne prepisuje vaše lokalne commite.
Vaši commiti su lokalni/privatni i želite linearnu povijest
Ponovno baziranje
Ponovno reproducira vaše lokalne commite preko ažurirane udaljene grane.
Niste sigurni i želite što manje prekrajanja povijesti
Spojiti
Lakše je objasniti i sigurnije je za zajedničku povijest.
Spoji rutu
git fetch origin
git merge origin/main
Ako nema sukoba, Git dovršava integraciju. Ako su se grane doista razišle, rezultat može uključivati commit spajanja.
Rebaziranje rute
git fetch origin
git rebase origin/main
Rebase uzima commitove koji su jedinstveni za vašu trenutnu granu i ponovno ih primjenjuje na origin/main. Gitova dokumentacija rebasea opisuje to kao presađivanje niza commitova na drugu početnu točku. Budući da ti commiti dobivaju nove ID-ove commita, rebase se najbolje koristi za lokalni rad koji još nije podijeljen kao javna povijest.
Ako preferirate prečac, git pull --rebase origin mainizvodi dohvaćanje nakon čega slijedi ponovno poravnanje, dok git pull --no-rebase origin maineksplicitno odabire ponašanje spajanja. Za situaciju oporavka, naredbe separate fetchi merge/ rebasečesto su jasnije jer prvo možete pregledati udaljeno stanje.
Nakon što se dohvate udaljene promjene, namjerno ih integrirajte - spajanjem ili rebaseiranjem - umjesto da ih prepišete.
Korak 3: Što učiniti ako Git prijavi konflikt?
Sukob ne znači da su vaše promjene nestale. To znači da Git ne može automatski odlučiti kako kombinirati promjene na istom sadržaju.
Prvo provjerite stanje:
git status
Otvorite svaku konfliktnu datoteku, odlučite kakav bi trebao biti konačni sadržaj, uklonite oznake konflikta, a zatim pripremite riješenu datoteku:
git add path/to/file
Ako ste odabrali spajanje, dovršite spajanje nakon što su svi sukobi pripremljeni:
git commit
Ako ste odabrali rebase, nastavite ponovno izvoditi svoje commite:
git rebase --continue
Ako rebase ide loše i želite vratiti granu u stanje prije rebase-a, koristite:
git rebase --abort
Trenutni Git priručnik za rebase posebno dokumentira --continuei --abortu tu svrhu. Nemojte koristiti git rebase --skipsamo za uklanjanje sukoba osim ako zapravo ne namjeravate izostaviti ponavljanje commita.
Prvo riješite sadržaj, pripremite ispravljenu datoteku, a zatim dovršite spajanje ili nastavite ponovno baziranje.
Korak 4: Kada je sigurno ponovno gurati?
Prije slanja, provjerite radno stablo i nedavnu povijest:
git status
git log --oneline --graph --decorate -n 12
Zatim pritisnite normalno:
git push origin main
Ako ste spojili udaljenu granu ili rebazirali samo commitove koji nikada prije nisu bili poslani, normalno slanje bi obično trebalo biti ispravna operacija jer će novi udaljeni savjet biti predak vašeg lokalnog savjeta.
Nakon što vaša grana sadrži i udaljeni rad i namjeravane lokalne promjene, normalno dodavanje može sigurno pomaknuti udaljeni rad naprijed.
Kada biste trebali koristiti --force-with-lease?
Koristite ga samo kada ste namjerno prepisali povijest koja već postoji na udaljenom poslužitelju - na primjer, prebazirali ste granu značajke koju ste prethodno gurnuli i sada trebate zamijeniti stari commit niz te grane.
git push --force-with-lease origin feature-branch
Službena git pushdokumentacija objašnjava zašto je ovo sigurnije od običnog --force: lease provjerava ima li udaljena referenca još uvijek vrijednost koju očekujete. Ako je neka druga osoba poslala novi rad nakon stanja na kojem ste temeljili svoje prepisivanje, prisilno ažuriranje se odbija umjesto da se slijepo prepisuju njihovi commiti.
Ta zaštita nije razlog za rutinsko korištenje prisilnog slanja. Izbjegavajte prisilno slanje dijeljenih grana, osim mainako vaš tim izričito ne dopušta prepisivanje povijesti. Zaštita grana na strani poslužitelja također može odbiti prisilno slanje bez obzira na vašu lokalnu naredbu.
Nemojte ovo zamijeniti:
git push --force origin main
Plain --forceonemogućuje normalnu sigurnosnu provjeru koja nije premotavanje unaprijed i može prebrisati udaljene commitove. Gitova vlastita dokumentacija upozorava da to može uzrokovati gubitak commitova u udaljenom repozitoriju.
Što ako ste već pokrenuli pogrešnu naredbu?
Git često još uvijek ima dovoljno lokalne povijesti za oporavak prethodne pozicije grane. Pokrenite:
git reflog
Reflogovi bilježe nedavna ažuriranja lokalnih referenci, uključujući prethodne vrijednosti HEAD. Ako pronađete commit koji je predstavljao vašu granu prije pogrešnog resetiranja ili rebase-a, stvorite rescue granu umjesto da se odmah mainponovno krećete:
git branch rescue-work <commit-id>
Sada pregledajte rescue-worki oporavite potrebne commitove. Službena dokumentacija za git-reflog opisuje reflogove kao zapise o tome gdje su prethodno ukazivali savjeti za grananje i druge reference.
Zašto se git pullponekad stvarao commit spajanja?
git pullprvo dohvaća, a zatim integrira odabranu uzvodnu granu. Ovisno o opcijama i konfiguraciji repozitorija, ta integracija može biti spajanje ili rebase. Ako želite predvidljivo ponašanje prilikom ispravljanja odbijanja koje nije premotavanje unaprijed, izričito navedite svoju namjeru:
# Preserve branch history with a merge
git pull --no-rebase origin main
# Replay private local commits on top
git pull --rebase origin main
Za najbolju vidljivost, koristite git fetch originprvo pa trčite git merge origin/mainili git rebase origin/mainodvojeno.
Što ako je udaljeni commit neželjen?
Nemojte pretpostavljati da "neželjeno" znači da je sigurno izbrisati. Prvo ga pregledajte:
Ako commit pripada drugom programeru, botu, ažuriratelju ovisnosti ili je promjena napravljena u web sučelju, integrirajte ga ili vratite kroz normalnu povijest. Ako se vaš tim namjerno složio da zamijeni udaljenu povijest, tada bi moglo biti prikladno zaštićeno prisilno slanje - ali to je odluka o povijesti repozitorija, a ne standardno rješenje za pogrešku koja ne uključuje premotavanje unaprijed.
Kontrolna lista za sigurne odluke
Nepotvrdene datoteke? Potvrdite ih ili spremite prije integracije.
Trebate sigurnosnu točku? Napravite sigurnosnu granu na trenutnom commitu.
Daljinski upravljač promijenjen? Pokrenite git fetch originprije nego što odlučite što učiniti.
Dijeljena povijest? Preferirate spajanje ako želite izbjeći prepisivanje commitova.
Privatne lokalne izmjene? Rebase može održati povijest linearnom.
Sukob? Riješite datoteku, pripremite je, a zatim dovršite spajanje ili nastavite ponovno baziranje.
Je li normalna integracija dovršena? Koristite normalnu git push.
Već objavljena povijest namjerno preoblikovana? Razmotrite --force-with-lease, a ne običan --force.
Zaključak
Poruka o nepremotavanju je sigurnosna barijera koja vam govori da udaljena grana sadrži povijest koju vaše predloženo slanje ne čuva. Rješenje nije u prevladavanju te barijere. Zaštitite svoj lokalni rad, dohvatite udaljenu granu, pregledajte divergenciju, integrirajte je sa spajanjem ili rebaseom, namjerno riješite sukobe i ponovno pošaljite.
Ako se sjećate jednog pravila, neka bude ovo: dohvati i razumi prije nego što prisiliš promjenu . To čuva i tvoje promjene i rad koji je već na daljinu - i pretvara zastrašujuće odbijanje push-a u rutinski problem sinkronizacije Gita.