Domov
» Základné znalosti
»
Ako opraviť chybu „Git Push Rejected: Non-FastForward“ bez straty zmien
Ako opraviť chybu „Git Push Rejected: Non-FastForward“ bez straty zmien
Odmietnutie bez pretáčania dopredu je ochrana histórie zo strany Gitu, nie vymazanie vašej lokálnej práce. Zvyčajne to znamená, že vzdialená vetva sa posunula dopredu po poslednej synchronizácii vašej lokálnej vetvy, takže aktualizácia vzdialenej vetvy s vašou aktuálnou vetvou by zahodila commity, ktoré tam už existujú. Aktuálna dokumentácia GitHubu popisuje rovnakú situáciu: najprv načítajte zmeny z upstreamu, integrujte ich lokálne a potom ich znova odošlite.
Najbezpečnejší všeobecný pracovný postup je: chrániť akúkoľvek nepotvrdenú prácu, načítať vzdialenú vetvu bez úpravy aktuálnej vetvy, skontrolovať, ako sa história rozchádzala, vybrať buď zlúčenie, alebo rebase, v prípade potreby vyriešiť konflikty a znova odoslať. Nezačínajte s alebo git push --forcelen git reset --hardpreto, aby chyba zmizla.
Predpokladajme, že vaša lokálna mainvetva obsahuje commit L, zatiaľ čo vzdialená vetva origin/mainpostúpila na iný commit R. Ak ani jeden commit nie je predkom druhého, histórie sa rozišli. Normálny push nemôže jednoducho presunúť ukazovateľ vzdialenej vetvy na , Laby Rnezmizol z viditeľnej histórie tejto vetvy.
Aktualizácia s rýchlym posunom vpred je iná: nový tip vetvy je potomkom starého, takže posunutie vetvy vpred zachová všetko, čo je z nej už dostupné. Oficiálna git pushpríručka definuje toto pravidlo pôvodu a varuje, že --forcedeaktivuje bežnú ochranu a môže spôsobiť stratu vzdialených commitov.
Odmietnutie, ktoré neumožňuje rýchle pretáčanie, znamená, že Git odmieta nahradiť vzdialenú históriu, ktorú vaša lokálna vetva ešte neobsahuje.
Predtým, ako čokoľvek urobíte: je vaša lokálna práca bezpečne zaznamenaná?
Spustiť:
git status
Ak je pracovný strom čistý, vaše aktuálne zmeny sú už reprezentované commitmi a pred integráciou vzdialeného servera môžete vytvoriť dodatočnú bezpečnostnú referenciu:
git branch backup-before-sync
Táto vetva odkazuje na aktuálny commit, takže máte jednoduchý názov pre stav pred integráciou.
Ak ste upravili alebo nesledovali súbory, ktoré nie sú potvrdené, pred stiahnutím alebo opätovným založením zvoľte jeden z týchto prístupov:
Ak je práca logicky pripravená na potvrdenie, potvrďte ich .
Ak je práca nedokončená, odložte ichgit stash push -u -m "before non-fast-forward fix" :.
Táto -umožnosť zahŕňa aj nesledované súbory. Oficiálna dokumentácia git-stash vysvetľuje, že stash zaznamenáva stav pracovného adresára a indexu a obnovuje čistý pracovný strom. Ignorované súbory nie sú zahrnuté v -u; -aby zahŕňalo aj ignorované súbory, ale to je pre tento problém zriedka potrebné.
Dôležité: ak git statuszobrazuje súbory, ktoré si nemôžete dovoliť stratiť, nespúšťajte ho git reset --hard. Tento príkaz môže zahodiť nepotvrdené zmeny v pracovnom strome.
Krok 1: Mali by ste použiť git pullokamžite?
Môžete, ale metóda „najprv načítať“ je jednoduchšia na uvažovanie . Dokumenty GitHubu git fetchsťahujú vzdialenú prácu a aktualizujú vetvy so vzdialeným sledovaním bez zlúčenia týchto zmien do vašej aktuálnej vetvy. Vďaka tomu je to užitočný diagnostický krok, pretože môžete skontrolovať situáciu pred zmenou lokálnej histórie.
Po načítaní vetva remote-tracking origin/mainpredstavuje práve načítaný vzdialený stav. Príkaz log vám umožňuje zobraziť commity dostupné iba z vašej lokálnej a iba zo vzdialenej strany.
Ak vaša lokálna pobočka nemá žiadne unikátne commity a jednoducho mešká, často ju môžete pretáčať dopredu:
git merge --ff-only origin/main
Potom nie je čo zosúlaďovať; vaša vetva sa jednoducho pohne vpred.
Krok 2: Zlúčenie alebo zmena základne – čo si vybrať?
Obidva môžu zachovať vaše zmeny. Rozdiel je v tvare výslednej histórie.
Situácia
Zvyčajne si vyberajú
Prečo
Zdieľaná vetva, napríklad maintam, kde vaše lokálne commity už môžu byť známe ostatným
Zlúčiť
Zachováva existujúce identity commitov a neprepisuje vaše lokálne commity.
Vaše commity sú lokálne/súkromné a chcete lineárnu históriu
Prebazovať
Prehrá vaše lokálne commity cez aktualizovanú vzdialenú vetvu.
Nie ste si istí a chcete čo najmenej prepisovať históriu
Zlúčiť
Je to jednoduchšie vysvetliť a bezpečnejšie pre spoločnú históriu.
Zlúčiť trasu
git fetch origin
git merge origin/main
Ak neexistujú žiadne konflikty, Git dokončí integráciu. Ak sa vetvy skutočne rozišli, výsledkom môže byť zlúčenie (merge commit).
Prebazovať trasu
git fetch origin
git rebase origin/main
Rebase vezme commity, ktoré sú jedinečné pre vašu aktuálnu vetvu, a znova ich aplikuje na origin/main. Dokumentácia rebase v Gite to opisuje ako presun série commitov na iný východiskový bod. Keďže tieto commity dostanú nové ID commitov, rebase sa najlepšie používa pre lokálnu prácu, ktorá ešte nebola zdieľaná ako verejná história.
Ak uprednostňujete skratku, git pull --rebase origin mainvykoná načítanie a následné rebase, pričom git pull --no-rebase origin mainexplicitne vyberie správanie zlúčenia. V prípade obnovy sú príkazy separate fetcha merge/ rebasečasto prehľadnejšie, pretože najprv môžete skontrolovať vzdialený stav.
Po načítaní vzdialených zmien ich zámerne integrujte – zlúčením alebo rebase – namiesto ich prepísania.
Krok 3: Čo by ste mali urobiť, ak Git nahlási konflikt?
Konflikt neznamená, že vaše zmeny sú preč. Znamená to, že Git sa nemôže automaticky rozhodnúť, ako skombinovať zmeny v tom istom obsahu.
Najprv skontrolujte stav:
git status
Otvorte každý konfliktný súbor, rozhodnite sa, aký by mal byť konečný obsah, odstráňte značky konfliktu a potom pripravte vyriešený súbor:
git add path/to/file
Ak ste zvolili zlúčenie, dokončite zlúčenie po vyriešení všetkých konfliktov:
git commit
Ak ste zvolili rebase, pokračujte v prehrávaní svojich commitov:
git rebase --continue
Ak rebase ide zle a chcete vrátiť vetvu do stavu pred rebase, použite:
git rebase --abort
Aktuálna príručka k rebase systému Git dokumentuje --continuea --abortna tento účel. Nepoužívajte git rebase --skiplen na odstránenie konfliktu, pokiaľ skutočne nemáte v úmysle vynechať prehrávanie commitu.
Najprv vyriešte obsah, pripravte opravený súbor a potom dokončite zlúčenie alebo pokračujte v obnovovaní základne.
Krok 4: Kedy je bezpečné znova tlačiť?
Pred odoslaním overte pracovný strom a nedávnu históriu:
git status
git log --oneline --graph --decorate -n 12
Potom stlačte normálne:
git push origin main
Ak ste zlúčili vzdialenú vetvu alebo prebaseovali iba commity, ktoré nikdy predtým neboli odoslané, normálne odoslanie by malo byť zvyčajne správnou operáciou, pretože nový vzdialený tip bude predkom vášho lokálneho tipu.
Keď vaša vetva obsahuje vzdialenú prácu aj zamýšľané lokálne zmeny, bežným odoslaním môžete bezpečne posunúť vzdialenú prácu ďalej.
Kedy by ste mali použiť --force-with-lease?
Použite ho iba vtedy, keď ste zámerne prepísali históriu, ktorá už na vzdialenom serveri existuje – napríklad ste prebaseovali vetvu funkcií, ktorú ste predtým odoslali a teraz potrebujete nahradiť starú sekvenciu commitov tejto vetvy.
git push --force-with-lease origin feature-branch
Oficiálna git pushdokumentácia vysvetľuje, prečo je to bezpečnejšie ako obyčajné --force: prenájom overuje, či má vzdialená referencia stále očakávanú hodnotu. Ak iná osoba odoslala novú prácu po stave, na ktorom ste založili svoje prepísanie, vynútená aktualizácia sa odmietne namiesto slepého prepísania jej commitov.
Táto ochrana nie je dôvodom na bežné používanie vynúteného odosielania. Vyhnite sa vynútenému odosielaniu zdieľaných vetiev, napríklad mainpokiaľ váš tím výslovne nepovoľuje prepisovanie histórie. Ochrana vetiev na strane servera môže tiež odmietnuť vynútené odosielanie bez ohľadu na váš lokálny príkaz.
Toto nenahrádzajte:
git push --force origin main
Plain --forcezakáže bežnú bezpečnostnú kontrolu bez pretáčania dopredu a môže prepísať vzdialené commity. Vlastná dokumentácia Gitu varuje, že to môže spôsobiť stratu commitov zo vzdialeného repozitára.
Čo ak ste už spustili nesprávny príkaz?
Git má často stále dostatok lokálnej histórie na obnovenie predchádzajúcej pozície vetvenia. Spustite:
git reflog
Reflogy zaznamenávajú nedávne aktualizácie lokálnych referencií vrátane predchádzajúcich hodnôt HEAD. Ak nájdete commit, ktorý reprezentoval vašu vetvu pred chybným resetom alebo rebázovaním, vytvorte záchrannú vetvu namiesto okamžitého opätovného presunu main:
git branch rescue-work <commit-id>
Teraz skontrolujte rescue-worka obnovte potrebné commity. Oficiálna dokumentácia git-reflog opisuje reflogy ako záznamy o tom, kam predtým ukazovali tipy na vetvenie a iné referencie.
Prečo sa git pullniekedy vytvoril zlúčený commit?
git pullnajprv načíta a potom integruje vybranú vetvu z hlavného prúdu. V závislosti od možností a konfigurácie repozitára môže byť táto integrácia zlúčením alebo rebase. Ak chcete predvídateľné správanie pri oprave odmietnutia, ktoré nepretáča dopredu, explicitne uveďte svoj zámer:
# Preserve branch history with a merge
git pull --no-rebase origin main
# Replay private local commits on top
git pull --rebase origin main
Pre čo najlepšiu viditeľnosť použite git fetch originako prvé a potom spustite git merge origin/mainalebo git rebase origin/mainsamostatne.
Čo ak je vzdialený commit nechcený?
Nepredpokladajte, že „nežiaduce“ znamená, že je bezpečné ho vymazať. Najprv ho skontrolujte:
Ak commit patrí inému vývojárovi, botovi, aktualizátorovi závislostí alebo ide o zmenu vykonanú vo webovom rozhraní, integrujte ho alebo ho vráťte cez bežnú históriu. Ak sa váš tím zámerne dohodol na nahradení vzdialenej histórie, potom môže byť vhodné chránené vynútené odoslanie – ale to je rozhodnutie o histórii repozitára, nie štandardná oprava chyby, ktorá neumožňuje rýchle pretáčanie.
Kontrolný zoznam pre bezpečné rozhodovanie
Nezaznamenané súbory? Pred integráciou ich zaznamenajte alebo uložte.
Potrebujete bezpečnostný bod? Vytvorte záložnú vetvu v aktuálnom commite.
Zmenili ste diaľkové ovládanie? Spustite ho git fetch originpredtým, ako sa rozhodnete, čo robiť.
Zdieľaná história? Uprednostňujete zlúčenie, ak sa chcete vyhnúť prepisovaniu commitov.
Súkromné lokálne commity? Rebase dokáže udržať históriu lineárnu.
Konflikt? Vyriešte súbor, pripravte ho a potom dokončite zlúčenie alebo pokračujte v obnovovaní databázy.
Už publikovaná história bola zámerne prepracovaná? Zvážte --force-with-lease, nie obyčajné --force.
Zrátané a podčiarknuté
Správa o nepretáčaní dopredu je bezpečnostná bariéra, ktorá vám hovorí, že vzdialená vetva obsahuje históriu, ktorú ste zamýšľali preniesť do vetvy. Riešením nie je prekonať túto bariéru. Ochráňte svoju lokálnu prácu, načítajte vzdialenú vetvu, skontrolujte divergenciu, integrujte ju pomocou zlúčenia alebo rebase, zámerne vyriešte konflikty a znova preneste do vetvy.
Ak si pamätáte jedno pravidlo, urobte ho takto: najprv načítajte a pochopte zmeny predtým, ako ich vynútite . To zachová vaše zmeny aj prácu, ktorá už bola na vzdialenom serveri – a desivé odmietnutie push zmien sa zmení na rutinný problém so synchronizáciou Gitu.