Angre delt historikk: git revert
Den interaktive leksjonen trenger JavaScript. Teksten under er hele leksjonen.
Etter leksjonen kan du
- angre en pushet commit med
git revertog dele rettelsen uten force-push anvende - reverte en bestemt eldre commit og beholde commitene som kom etter anvende
- forklare hvorfor reset skaper problemer i delt historikk, og hva revert ikke skjuler forstå
- velge mellom restore, reset og revert ut fra hvor endringen ligger vurdere
Forstå: Angre ved å legge til
Commit B doblet alle prisene, og det var en feil. Både B og C er pushet, og Ada har hentet dem. Hvordan angrer du B når andre allerede har den?
Reset virker fristende. Men da flytter du bare din egen main tilbake til A. Serveren og Ada har fortsatt B og C. Git avviser neste push, og tvinger du den gjennom, forsvinner også den gode commiten C fra serveren.
Revert gjør det motsatte av å fjerne. Den legger til en ny commit, R, som angrer endringen i B. Ingenting forsvinner. Historikken blir bare lengre.
Hva ligger i R? Akkurat den motsatte endringen av B. Prisen går fra 60 tilbake til 30. Åpningstidene fra C blir stående, fordi R bare angrer B.
Fordi R bygger videre på C, pusher du den helt vanlig. Ada får R med en vanlig pull, uten konflikt og uten force.
Du kan også reverte en eldre commit, ikke bare den nyeste. Har senere commits endret de samme linjene, kan du få en konflikt. Den løser du som i en merge, og så avslutter du med git revert --continue.
Her er beslutningsregelen for all angring. Er endringen ikke committet, bruker du restore. Er den committet, men ikke delt, kan du bruke reset. Er den delt, bruker du revert. Og husk: revert retter innholdet, men skjuler ikke den gamle commiten.
Se demo: Angre en pushet prisendring
Først finner vi den skyldige commiten. Se at origin/main står på samme commit som main. Alt er pushet, så reset er uaktuelt.
$ git log --oneline --decorate
Den dårlige commiten er nest nyeste, altså én før HEAD. Vi reverterer akkurat den. --no-edit beholder meldingen Git foreslår, så ingen editor åpnes.
$ git revert --no-edit HEAD~1
La oss se hva den nye commiten inneholder. Linjen med 60 fjernes, og linjen med 30 kommer tilbake. Det er B snudd på hodet.
$ git show HEAD
Nå deler vi rettelsen. Pushen går rett gjennom, fordi vi bare har lagt til en commit oppå det serveren allerede har.
$ git push
Til slutt kjører Ada pull. Det blir en enkel fast-forward. Ingen konflikt, og ingen historikk hun allerede har, forsvinner.
$ git pull --no-rebase
Oppgaver
- Finn commiten som doblet prisene, og sjekk om den er pushet.
- Angre prisendringen med en ny commit, og behold åpningstidene.
- Del rettelsen med teamet.
Øv: Overfør: tre problemer, tre verktøy
Før hver oppgave: spør deg selv om endringen bare ligger i filene, i en lokal commit eller i en delt commit. Bruk git status og git log --oneline for å finne svaret.
Oppgaver
- menu.txt har noe rot som aldri ble staget. Kast det.
- Den nyeste commiten, Quick test, var en feil. Fjern den fra historikken til main, og fjern også endringen fra filene.
- Commiten Remove tea var en feil. Få te tilbake på menyen, og del rettelsen med teamet.
Sjekk deg selv: Hent det fram igjen
En commit du pushet i går, har ødelagt bygget. Teamet har allerede hentet den. Hva gjør du?
git reset --hard HEAD~1og så force-pushgit revertpå commiten, og sågit pushgit restorepå filene, uten å committe
Du reverterer en commit tre steg tilbake, og Git stopper med CONFLICT i prices.txt. Hva gjør du?
- Kjør
git revert --continuemed en gang - Rett opp prices.txt, kjør
git add prices.txtog sågit revert --continue git reset --hard HEAD~3, så slipper du konflikten
Du pushet ved et uhell en commit med et passord. Gjør git revert passordet trygt igjen?
- Ja, revert fjerner passordet fra historikken
- Nei, passordet ligger fortsatt i den gamle commiten, så du må bytte det
- Ja, så lenge ingen har kjørt pull ennå
Repetisjon fra «Fetch, pull og push»: git push blir avvist fordi Ada har pushet først. Hva er riktig neste steg?
git push --force, så kommer din versjon opp- Hent med
git fetch, integrer med merge eller rebase, og push på nytt git reset --hard origin/main, og push igjen
Vanlige misforståelser
- «Revert sletter commiten.» Revert legger til en ny commit med motsatt endring. Den gamle commiten blir værende i historikken.
- «Etter en revert er et lekket passord borte.» Passordet ligger fortsatt i den gamle commiten. Bytt passordet.
- «Reset og force-push er den vanlige måten å angre pushet arbeid på.» Det skriver om historikk som andre allerede har, og lager trøbbel for hele teamet.
Les mer
Hva revert gjør, helt presist
git revert <commit> finner ut hvilken endring commiten gjorde sammenlignet med forelderen sin, legger den motsatte endringen oppå HEAD, og committer resultatet. Den gamle commiten blir liggende der den er. Den nye commiten får en melding som navngir den:
$ git revert --no-edit HEAD~1
[main 890a669] Revert "Double all prices"
Date: Tue Oct 13 09:14:35 2026 +0200
1 file changed, 1 insertion(+), 1 deletion(-)
$ git show -s --format=%B HEAD
Revert "Double all prices"
This reverts commit 54235c6e205c2e2cfa55a5517d06f4c6d1c03532.
Revert flytter branchen din fram med én ny commit, akkurat som git commit. Den fjerner aldri noe, så resultatet kan pushes helt vanlig og hentes av alle uten force. Staging-området og arbeidsmappen bør være rene før du starter, fordi Git trenger dem for å bygge den nye commiten.
Nyttige valg:
--no-editbeholder den foreslåtte meldingen i stedet for å åpne en editor.-n(--no-commit) legger den motsatte endringen i staging-området og arbeidsmappen, men committer ikke. Da kan du samle flere reverts, eller justere resultatet, i én egen commit.- Git reverterer flere commits eller et område én om gangen, nyeste først.
git revert --no-edit HEAD~2..HEADlager én revert-commit for hver av de to nyeste commitene.
Når revert møter en konflikt
Har senere commits endret de samme linjene, kan ikke Git legge på den motsatte endringen automatisk, og stopper midt i, akkurat som en merge:
$ git revert --no-edit HEAD~1
Auto-merging p.txt
CONFLICT (content): Merge conflict in p.txt
error: could not revert c228a05... Double prices
Åpne filen, bestem hva innholdet skal være, og kjør så git add p.txt og git revert --continue. git revert --abort tar deg tilbake til tilstanden før du startet, og git revert --skip hopper over commiten når du reverterer flere.
Revert av en merge-commit: -m 1
En merge-commit har to foreldre, så «endringen commiten gjorde» avhenger av hvilken forelder du sammenligner med. Git nekter å gjette:
$ git revert HEAD
error: commit 9b1a80256199feb0cf5f16d62319782869b67874 is a merge but no -m option was given.
fatal: revert failed
-m 1 sier at forelder nummer 1, branchen du sto på da du merget (vanligvis main), er hovedlinjen som skal beholdes. Da fjerner revert alt den mergede branchen tok med seg:
$ git revert --no-edit -m 1 HEAD
[main 14a1912] Revert "Merge branch 'feature'"
Her er det en felle. Innholdet er borte, men historikken sier fortsatt at branchen er merget. Retter du branchen senere og merger den på nytt, tar Git bare med de nye commitene på branchen, ikke de du reverterte. Løsningen er å reverte reverten først, slik at de opprinnelige endringene kommer tilbake, og deretter merge det nye arbeidet.
Revert av en revert
En revert-commit er en helt vanlig commit, så du kan også reverte den. Det er den ryddige måten å angre en angring på, for eksempel når den «ødelagte» commiten likevel viste seg å være riktig. Nyere Git-versjoner kaller denne commiten Reapply; eldre skriver Revert "Revert ...". Historikken forteller nå hele historien: endringen, angringen og tilbakekomsten.
Hva revert ikke gjør
Revert lager en ny commit som opphever innholdet i endringen. Den skjuler ikke den gamle commiten. Hvem som helst kan fortsatt kjøre git show på den, og hver klone og fork inneholder den fortsatt. Har du committet et passord eller en API-nøkkel, er ikke revert nok: regn hemmeligheten som lekket og bytt den med en gang. Å fjerne den fra hele historikken krever at alle commits siden skrives om, en tvungen push og at hver kollega kloner på nytt. Det er en større operasjon enn denne leksjonen dekker.
Restore, reset eller revert?
Spør først om endringen er committet. Er den ikke det, bruker du restore. Er den det, spør om den er pushet eller delt. Hvis ikke, er reset lov. Hvis den er det, bruker du revert.
| Du vil angre … | Hvor den ligger | Verktøy | Flytter en branch? | Kan arbeid gå tapt? |
|---|---|---|---|---|
| en endring du ikke har staget | arbeidsmappen | git restore app.txt |
nei | ja, den ustagede endringen |
| en fil du staget for tidlig | staging-området | git restore --staged app.txt |
nei | nei |
| innholdet i en fil, tilbake til en eldre versjon | en commit | git restore --source=HEAD~1 app.txt |
nei | ja, nåværende versjon i arbeidsfilen |
| siste commit, og beholde endringen staget | upushet commit | git reset --soft HEAD~1 |
ja | nei |
| siste commit, og beholde endringen i filene | upushet commit | git reset HEAD~1 |
ja | nei |
| siste commit og endringen dens | upushet commit | git reset --hard HEAD~1 |
ja | ja, ucommittede endringer |
| en commit andre allerede har | pushet commit | git revert <commit> |
ja, fram med én ny commit | nei |
Offisiell dokumentasjon
- git-revert, se
-mog--no-commit - How to revert a faulty merge
- Pro Git: Undoing Merges
Per-Arne's Git Good!