Domů
» Základní znalosti
»
Jak opravit chybu „Git Push Rejected: Non-FastForward“ bez ztráty změn
Jak opravit chybu „Git Push Rejected: Non-FastForward“ bez ztráty změn
Odmítnutí bez možnosti rychlého přehrávání je způsobeno ochranou historie ze strany Gitu, nikoli smazáním vaší lokální práce. Obvykle to znamená, že vzdálená větev se po poslední synchronizaci vaší lokální větve přesunula vpřed, takže aktualizace vzdálené větve s vaší aktuální větví by zahodila commity, které tam již existují. Aktuální dokumentace GitHubu popisuje stejnou situaci: nejprve načtěte změny z upstreamu, integrujte je lokálně a poté je znovu odešlete.
Nejbezpečnější obecný pracovní postup je: chránit veškerou nepotvrzenou práci, načíst vzdálenou větev bez úpravy aktuální větve, zkontrolovat, jak se historie odchýlila, zvolit buď sloučení, nebo rebase, v případě potřeby vyřešit konflikty a znovu provést odeslání. Nezačínejte s nebo git push --forcejen git reset --hardproto, abyste chybu odstranili.
Předpokládejme, že vaše lokální mainvětev obsahuje commit L, zatímco vzdálená větev origin/mainpostoupila na jiný commit R. Pokud ani jeden commit není předkem druhého, historie se rozdělily. Normální příkaz push nemůže jednoduše přesunout ukazatel vzdálené větve na , Laniž by Rzmizel z viditelné historie dané větve.
Aktualizace fast forward je jiná: nová větev je potomkem té staré, takže posunutí větve vpřed zachovává vše, co je z ní již dosažitelné. Oficiální git pushmanuál definuje toto pravidlo předků a varuje, že --forcedeaktivuje normální ochranu a může způsobit ztrátu vzdálených commitů.
Odmítnutí bez možnosti rychlého přetáčení znamená, že Git odmítá nahradit vzdálenou historii, kterou vaše lokální větev ještě neobsahuje.
Než cokoli podniknete: je vaše lokální práce bezpečně zaznamenána?
Běh:
git status
Pokud je pracovní strom čistý, vaše aktuální změny jsou již reprezentovány commity a před integrací vzdáleného serveru můžete vytvořit další bezpečnostní referenci:
git branch backup-before-sync
Tato větev odkazuje na aktuální commit, takže máte jednoduchý název pro stav před integrací.
Pokud jste upravili nebo nesledovali soubory, které nebyly potvrzeny, zvolte před stažením nebo změnou báze jeden z těchto přístupů:
Pokud je práce logicky připravena k potvrzení, potvrďte je .
Schovejte je, pokud je práce nedokončená git stash push -u -m "before non-fast-forward fix":.
Tato -umožnost zahrnuje i nesledované soubory. Oficiální dokumentace git-stash vysvětluje, že stash zaznamenává stav pracovního adresáře a indexu a obnovuje čistý pracovní strom. Ignorované soubory nejsou zahrnuty pomocí -u; -aby zahrnovalo i ignorované soubory, ale to je pro tento problém zřídka nutné.
Důležité: Pokud git statuszobrazuje soubory, o které si nemůžete dovolit přijít, nespouštějte git reset --hard. Tento příkaz může zahodit nepotvrzené změny pracovního stromu.
Krok 1: Měli byste použít git pullokamžitě?
Můžete, ale načítání dat je snazší . Dokumenty GitHubu git fetchstahují vzdálenou práci a aktualizují větve pro vzdálené sledování, aniž by tyto změny slučovaly do vaší aktuální větve. Díky tomu je to užitečný diagnostický krok, protože můžete situaci prozkoumat před změnou lokální historie.
Po načtení origin/mainpředstavuje větev remote-tracking právě načtený vzdálený stav. Příkaz log umožňuje zobrazit commity dostupné pouze z vaší lokální a pouze ze vzdálené strany.
Pokud vaše lokální pobočka nemá žádné unikátní commity a jednoduše je pozadu, můžete ji často přetočit dopředu:
git merge --ff-only origin/main
Pak není co slaďovat; vaše větev se jednoduše posouvá kupředu.
Krok 2: Sloučení nebo rebase – co si vybrat?
Oba mohou zachovat vaše změny. Rozdíl je ve tvaru výsledné historie.
Situace
Obvykle si vybírají
Proč
Sdílená větev, například maintam, kde vaše lokální commity mohou být již známy ostatním
Spojit
Zachovává existující identity commitů a nepřepisuje vaše lokální commity.
Vaše commity jsou lokální/soukromé a chcete lineární historii.
Znovu vypočítat
Znovu přehraje vaše lokální commity přes aktualizovanou vzdálenou větev.
Nejste si jisti a chcete co nejméně přepisovat historii
Spojit
Je to snadněji vysvětlitelné a bezpečnější pro sdílenou historii.
Sloučit trasu
git fetch origin
git merge origin/main
Pokud nedojde k žádným konfliktům, Git dokončí integraci. Pokud se větve skutečně rozdělily, může výsledkem být commit sloučení.
Znovu vypočítat trasu
git fetch origin
git rebase origin/main
Rebase vezme commity, které jsou jedinečné pro vaši aktuální větev, a znovu je aplikuje na origin/main. Dokumentace k rebase v Gitu to popisuje jako přenesení série commits na jiný výchozí bod. Protože tyto commity obdrží nová ID commitu, je rebase nejlépe použitelný pro lokální práci, která ještě nebyla sdílena jako veřejná historie.
Pokud dáváte přednost zkratce, git pull --rebase origin mainprovede načtení následované rebase, zatímco git pull --no-rebase origin mainexplicitně zvolí chování sloučení. Pro situaci obnovy jsou příkazy separate fetcha merge/ rebasečasto přehlednější, protože si můžete nejprve prohlédnout vzdálený stav.
Po načtení vzdálených změn je záměrně integrujte – sloučením nebo rebase – namísto jejich přepsání.
Krok 3: Co dělat, když Git nahlásí konflikt?
Konflikt neznamená, že vaše změny jsou pryč. Znamená to, že Git nemůže automaticky rozhodnout, jak kombinovat změny stejného obsahu.
Nejprve zkontrolujte stav:
git status
Otevřete každý konfliktní soubor, rozhodněte se, jaký by měl být konečný obsah, odstraňte značky konfliktu a poté připravte vyřešený soubor:
git add path/to/file
Pokud zvolíte sloučení, dokončete sloučení po vyřešení všech konfliktů:
git commit
Pokud jste zvolili rebase, pokračujte v přehrávání commitů:
git rebase --continue
Pokud rebase probíhá špatně a chcete vrátit větev do stavu před rebase, použijte:
git rebase --abort
Aktuální manuál k rebaseování Gitu dokumentuje konkrétně ` --continuea` --abortpro tento účel. Nepoužívejte git rebase --skip`pouhé odstranění konfliktu`, pokud skutečně nemáte v úmyslu vynechat přehrávaný commit.
Nejprve vyřešte obsah, připravte opravený soubor a poté dokončete sloučení nebo pokračujte v rebase.
Krok 4: Kdy je bezpečné znovu tlačit?
Před odesláním ověřte pracovní strom a nedávnou historii:
git status
git log --oneline --graph --decorate -n 12
Pak normálně stiskněte:
git push origin main
Pokud jste sloučili vzdálenou větev nebo přebazovali pouze commity, které nikdy předtím nebyly odeslány, normální odeslání by obvykle mělo být správnou operací, protože nový vzdálený tip bude předkem vašeho lokálního tipu.
Jakmile vaše větev obsahuje jak vzdálenou práci, tak i zamýšlené lokální změny, může normální odeslání (push) bezpečně přesunout vzdálenou práci.
Kdy byste měli použít --force-with-lease?
Použijte ho pouze tehdy, když jste úmyslně přepsali historii, která již na vzdáleném serveru existuje – například jste přepracovali větev funkcí, kterou jste dříve odeslali, a nyní potřebujete nahradit starou sekvenci commitů této větve.
git push --force-with-lease origin feature-branch
Oficiální git pushdokumentace vysvětluje, proč je to bezpečnější než prostý způsob --force: pronájem kontroluje, zda má vzdálený odkaz stále očekávanou hodnotu. Pokud jiná osoba odeslala novou práci po stavu, na kterém jste založili svůj přepis, vynucená aktualizace je odmítnuta, místo aby se její commity slepě přepsaly.
Tato ochrana není důvodem k rutinnímu používání vynuceného odesílání. Vyhněte se vynucenému odesílání sdílených větví, pokud mainváš tým explicitně nepovolí přepisování historie. Ochrana větví na straně serveru může také odmítnout vynucené odesílání bez ohledu na váš lokální příkaz.
Toto nenahrazujte:
git push --force origin main
Plain --forcezakáže normální bezpečnostní kontrolu bez přetáčení vpřed a může přepsat vzdálené commity. Dokumentace Gitu varuje, že to může způsobit ztrátu commitů ve vzdáleném repozitáři.
Co když jste již spustili špatný příkaz?
Git má často stále dostatek lokální historie k obnovení předchozí pozice větve. Spusťte:
git reflog
Reflogy zaznamenávají nedávné aktualizace lokálních referencí, včetně předchozích hodnot HEAD. Pokud najdete commit, který reprezentoval vaši větev před chybným resetem nebo rebase, vytvořte záchrannou větev namísto okamžitého mainopětovného přesunu:
git branch rescue-work <commit-id>
Nyní prohlédněte rescue-worka obnovte potřebné commity. Oficiální dokumentace git-reflog popisuje reflogy jako záznamy o tom, kam dříve odkazovaly tipy na větve a další odkazy.
Proč se git pullněkdy vytvářel commit sloučení?
git pullnejprve načte a poté integruje vybranou větev z upstreamu. V závislosti na možnostech a konfiguraci repozitáře může být tato integrace sloučením nebo rebase. Pokud chcete předvídatelné chování při opravě odmítnutí, které neumožňuje rychlé přetočení, explicitně uveďte svůj záměr:
# Preserve branch history with a merge
git pull --no-rebase origin main
# Replay private local commits on top
git pull --rebase origin main
Pro co nejlepší viditelnost použijte git fetch originnejprve a poté spusťte git merge origin/main, nebo je použijte git rebase origin/mainsamostatně.
Co když je vzdálený commit nežádoucí?
Nepředpokládejte, že „nežádoucí“ znamená, že je bezpečné jej smazat. Nejprve jej zkontrolujte:
Pokud commit patří jinému vývojáři, botovi, aktualizátoru závislostí nebo se jedná o změnu provedenou ve webovém rozhraní, integrujte jej nebo jej vraťte zpět pomocí normální historie. Pokud se váš tým záměrně dohodl na nahrazení vzdálené historie, pak může být vhodné chráněné vynucené nahrání – ale to je rozhodnutí týkající se historie repozitáře, nikoli standardní oprava chyby, která neumožňuje rychlé přetočení.
Kontrolní seznam pro bezpečné rozhodování
Nepřijaté soubory? Před integrací je přijatelné nebo uložené.
Potřebujete bezpečnostní bod? Vytvořte záložní větev v aktuálním commitu.
Změnil se dálkový ovladač? Spusťte to git fetch origin, než se rozhodnete, co dělat.
Sdílená historie? Pokud se chcete vyhnout přepisování commitů, preferujete sloučení.
Soukromé lokální commity? Rebase může udržet historii lineární.
Konflikt? Vyřešte soubor, připravte ho a poté dokončete sloučení nebo pokračujte v rebase.
Již publikovaná historie záměrně přepracovaná? Zvažte --force-with-lease, ne prostý --force.
Sečteno a podtrženo
Zpráva o nepřetáčení vpřed je bezpečnostní bariéra, která vám říká, že vzdálená větev obsahuje historii, kterou vámi navrhovaný push neuchovává. Oprava nespočívá v překonání této bariéry. Chraňte svou lokální práci, načtěte vzdálenou větev, prozkoumejte divergenci, integrujte ji pomocí sloučení nebo rebase, záměrně vyřešte konflikty a push znovu.
Pokud si pamatujete jedno pravidlo, zkuste ho: nejprve načtěte a pochopte změny, než je vynutíte . Tím se zachovají jak vaše změny, tak i práce, která již byla na vzdáleném serveru – a děsivé odmítnutí odeslaných změn se změní v rutinní problém se synchronizací Gitu.