Hjem
» Basis viden
»
How to Fix “Git Push Rejected: Non-Fast-Forward” Without Losing Changes
How to Fix “Git Push Rejected: Non-Fast-Forward” Without Losing Changes
A non-fast-forward rejection is Git protecting history, not deleting your local work. It usually means the remote branch moved forward after your local branch last synchronized, so updating the remote with your current branch would discard commits that already exist there. GitHub’s current documentation describes the same situation: retrieve the upstream changes first, integrate them locally, then push again.
The safest general workflow is: protect any uncommitted work, fetch the remote branch without modifying your current branch, inspect how the histories diverged, choose either merge or rebase, resolve conflicts if necessary, and push again. Do not start with git push --force or git reset --hard just to make the error disappear.
Suppose your local main branch contains commit L, while the remote origin/main has advanced to a different commit R. If neither commit is an ancestor of the other, the histories have diverged. A normal push cannot simply move the remote branch pointer to L without making R disappear from that branch’s visible history.
A fast-forward update is different: the new branch tip is a descendant of the old one, so moving the branch forward preserves everything already reachable from it. The official git push manual defines this ancestry rule and warns that --force disables the normal protection and can cause remote commits to be lost.
A non-fast-forward rejection means Git is refusing to replace remote history that your local branch does not yet contain.
Before doing anything: is your local work safely recorded?
Run:
git status
If the working tree is clean, your current changes are already represented by commits and you can create an extra safety reference before integrating the remote:
git branch backup-before-sync
That branch points to the current commit, so you have an easy name for the pre-integration state.
If you have modified or untracked files that are not committed, choose one of these approaches before pulling or rebasing:
Commit them if the work is logically ready for a commit.
Stash them if the work is incomplete: git stash push -u -m "before non-fast-forward fix".
The -u option includes untracked files. The official git-stash documentation explains that a stash records the working directory and index state and restores a clean working tree. Ignored files are not included by -u; -a would include ignored files too, but that is rarely necessary for this problem.
Important: if git status shows files you cannot afford to lose, do not run git reset --hard. That command can discard uncommitted working-tree changes.
Step 1: Should you use git pull immediately?
You can, but fetch first is easier to reason about. GitHub documents that git fetch downloads remote work and updates remote-tracking branches without merging those changes into your current branch. That makes it a useful diagnostic step because you can inspect the situation before altering local history.
After the fetch, the remote-tracking branch origin/main represents the remote state you just retrieved. The log command lets you see commits reachable only from your local side and only from the remote side.
If your local branch has no unique commits and is simply behind, you can often fast-forward it:
git merge --ff-only origin/main
Then there is nothing to reconcile; your branch simply moves forward.
Step 2: Merge or rebase—which should you choose?
Both can preserve your changes. The difference is the shape of the resulting history.
Situation
Usually choose
Why
Shared branch such as main where your local commits may already be known to others
Merge
Preserves existing commit identities and does not rewrite your local commits.
Your commits are local/private and you want a linear history
Rebase
Replays your local commits on top of the updated remote branch.
You are unsure and want the least history rewriting
Merge
It is easier to explain and safer for shared history.
Merge route
git fetch origin
git merge origin/main
If there are no conflicts, Git completes the integration. If the branches truly diverged, the result may include a merge commit.
Rebase route
git fetch origin
git rebase origin/main
Rebase takes the commits that are unique to your current branch and reapplies them on top of origin/main. Git’s rebase documentation describes it as transplanting a series of commits onto a different starting point. Because those commits receive new commit IDs, rebase is best used for local work that has not already been shared as public history.
If you prefer a shortcut, git pull --rebase origin main performs a fetch followed by a rebase, while git pull --no-rebase origin main explicitly chooses merge behavior. For a recovery situation, separate fetch and merge/rebase commands are often clearer because you can inspect the remote state first.
After the remote changes are retrieved, integrate them deliberately—by merge or rebase—rather than overwriting them.
Step 3: What should you do if Git reports a conflict?
A conflict does not mean your changes are gone. It means Git cannot automatically decide how to combine changes to the same content.
Først skal du undersøge staten:
git status
Åbn hver konfliktfyldt fil, bestem, hvad det endelige indhold skal være, fjern konfliktmarkørerne, og opsæt derefter den løste fil:
git add path/to/file
Hvis du vælger fletning, skal du afslutte fletningen, når alle konflikter er opdelt:
git commit
Hvis du valgte rebase, fortsæt med at afspille dine commits:
git rebase --continue
Hvis rebasen går dårligt, og du vil returnere branchen til dens tilstand før rebasen, skal du bruge:
git rebase --abort
Den nuværende Git rebase-manual dokumenterer specifikt --continueog --aborttil dette formål. Brug ikke git rebase --skipblot til at få en konflikt til at forsvinde, medmindre du rent faktisk har til hensigt at udelade afspilningen af commit'en.
Løs først indholdet, opret den rettede fil i stages, og afslut derefter fletningen eller fortsæt rebasen.
Trin 4: Hvornår er det sikkert at presse igen?
Før du pusher, skal du kontrollere arbejdstræet og den seneste historik:
git status
git log --oneline --graph --decorate -n 12
Tryk derefter normalt:
git push origin main
Hvis du fusionerede den eksterne branch, eller kun rebaserede commits, der aldrig var blevet pushet før, burde et normalt push typisk være den rigtige handling, fordi det nye eksterne tip vil være en forfader til dit lokale tip.
Når din gren indeholder både fjernarbejdet og dine tilsigtede lokale ændringer, kan et normalt tryk føre fjernarbejdet sikkert frem.
Hvornår skal du bruge --force-with-lease?
Brug den kun, når du bevidst omskriver en historik, der allerede findes på fjernbetjeningen — for eksempel hvis du har rebaseret en funktionsgren, som du tidligere har pushet, og nu skal erstatte den gren's gamle commit-sekvens.
git push --force-with-lease origin feature-branch
Den officielle git pushdokumentation forklarer, hvorfor dette er sikrere end almindeligt --force: leasen kontrollerer, at den eksterne reference stadig har den værdi, du forventer. Hvis en anden person pushede nyt arbejde efter den tilstand, du baserede din omskrivning på, afvises den tvungne opdatering i stedet for blindt at overskrive deres commits.
Den beskyttelse er ikke en grund til rutinemæssigt at bruge force pushing. Undgå force pushing af delte branches, medmindre maindit team eksplicit tillader omskrevet historik. Server-side branch Protection kan også afvise force pushes uanset din lokale kommando.
Erstat ikke dette:
git push --force origin main
Plain --forcedeaktiverer den normale sikkerhedskontrol uden spolning fremad og kan overskrive eksterne commits. Gits egen dokumentation advarer om, at det kan forårsage, at det eksterne arkiv mister commits.
Hvad hvis du allerede har kørt den forkerte kommando?
Git har ofte stadig nok lokal historik til at gendanne en tidligere branch-position. Kør:
git reflog
Reblogs registrerer de seneste opdateringer til lokale referencer, inklusive tidligere værdier af HEAD. Hvis du finder den commit, der repræsenterede din branch før den fejlagtige nulstilling eller rebase, skal du oprette en rescue branch i stedet for at flytte den mainigen med det samme:
git branch rescue-work <commit-id>
Undersøg rescue-workog gendan nu de commits, du har brug for. Den officielle git-reflog-dokumentation beskriver reflogs som optegnelser over, hvor branch-tips og andre referencer tidligere pegede hen.
Hvorfor git pulloprettes der nogle gange en merge-commit?
git pullhenter først og integrerer derefter den valgte upstream-gren. Afhængigt af indstillingerne og repository-konfigurationen kan denne integration være en merge eller en rebase. Hvis du ønsker forudsigelig adfærd, mens du retter en ikke-spolende afvisning, skal du eksplicit angive din hensigt:
# Preserve branch history with a merge
git pull --no-rebase origin main
# Replay private local commits on top
git pull --rebase origin main
For mest synlighed, brug git fetch originførst og løb git merge origin/maineller git rebase origin/mainseparat.
Hvad hvis den eksterne commit er uønsket?
Gå ikke ud fra, at "uønsket" betyder, at det er sikkert at slette. Undersøg det først:
Hvis commit'en tilhører en anden udvikler, en bot, en afhængighedsopdaterer eller en ændring foretaget i webgrænsefladen, skal du integrere eller tilbageføre den via normal historik. Hvis dit team bevidst har accepteret at erstatte den eksterne historik, kan en bevogtet kraftpush være passende – men det er en beslutning om repository-historik, ikke standardløsningen til en ikke-spolingsfejl.
En tjekliste til sikker beslutning
Ikke-committede filer? Committ eller gem dem før integration.
Brug for et sikkerhedspunkt? Opret en backup-branch ved den nuværende commit.
Fjernbetjening ændret? Kør git fetch originfør du beslutter dig for, hvad du skal gøre.
Delt historik? Foretrækker du merge, hvis du vil undgå at omskrive commits.
Private lokale commits? Rebase kan holde historikken lineær.
Konflikt? Løs filen, opret en midlertidig løsning, og afslut derefter fletningen eller fortsæt rebasen.
Normal integration fuldført? Brug en normal git push.
Allerede udgivet historie, der bevidst er rebaseret? Overvej det --force-with-lease, ikke almindelig --force.
Konklusion
Beskeden om ikke at spole frem er en sikkerhedsbarriere, der fortæller dig, at den eksterne gren indeholder historik, som dit foreslåede push ikke bevarer. Løsningen er ikke at overvinde denne barriere. Beskyt dit lokale arbejde, hent den eksterne gren, undersøg divergensen, integrer den med merge eller rebase, løs konflikter bevidst, og push igen.
Hvis du husker én regel, så gør den til denne: hent og forstå, før du gennemtvinger . Det bevarer både dine ændringer og det arbejde, der allerede er på fjernbetjeningen – og forvandler en skræmmende push-afvisning til et rutinemæssigt Git-synkroniseringsproblem.