Flytt branchen tilbake: git reset
Den interaktive leksjonen trenger JavaScript. Teksten under er hele leksjonen.
Etter leksjonen kan du
- angre lokale commits ved å flytte branchen med
git reset, og velge modus (--soft,--mixed,--hard) ut fra hva som skal skje med staging og arbeidsfilene anvende - sette en backup-branch før en risikabel reset, og resette tilbake til den etterpå anvende
- avgjøre når reset er trygt, og når restore eller revert passer bedre vurdere
Forstå: Én peker, tre rekkevidder
Du committet B litt for tidlig. Ingen andre har sett den. Akkurat nå er versjonen med Draft på alle tre stedene: i arbeidsmappen, i staging og i siste commit.
git reset --soft HEAD~1 flytter bare branchen. main hopper tilbake til A. Staging og arbeidsfil er urørt, så endringen ser nå ut som staget og klar til en ny commit.
Tilbake på B: git reset HEAD~1 uten modus betyr --mixed. Den flytter branchen og kopierer i tillegg A inn i staging. Arbeidsfilen beholder Draft, nå som en ustaget endring.
Igjen fra B: git reset --hard HEAD~1 når helt til arbeidsmappen. Alle tre stedene får versjonen fra A. Dette er den farlige modusen: ucommittede endringer i sporede filer blir overskrevet uten spørsmål.
Se på B. Den er tegnet stiplet, fordi ingen branch peker på den lenger. Men den er ikke slettet. Kjenner du IDen, flytter git reset --hard main tilbake dit. Leksjonen «Reflog» viser hvordan du finner IDen.
Her er regelen. Reset flytter branchen din, og modusen bestemmer hvor langt mot venstre den når. Bruk reset bare på commits ingen andre har. Gjelder det bare filer eller staging, holder restore. Er commiten delt, bruker du revert.
Se demo: Tre moduser på samme commit
Vi har to commits, og den siste vil vi gjøre om. Vi starter med den mildeste modusen.
$ git reset --soft HEAD~1
Vi committer igjen for å prøve neste modus.
$ git commit -m "Add draft"
Nå uten modus, altså --mixed. Se staging-kortet: det går tilbake til Welcome, mens arbeidsfilen fortsatt har Draft.
$ git reset HEAD~1
Vi stager og committer en siste gang.
$ git commit -am "Add draft"
Nå den harde. Se alle tre kortene gå tilbake til Welcome. Draft-linjen er borte fra filen, men commiten finnes fortsatt.
$ git reset --hard HEAD~1
Oppgaver
- Angre siste commit, men behold endringen staget.
- Angre siste commit og ta endringen ut av staging, men behold den i filen.
- Fjern siste commit helt fra branch, staging og arbeidsfil.
Øv: Overfør: rydd i en innloggingsside
Ingen av commitene er pushet. Se på git log --oneline før du starter.
Oppgaver
- Add login form og Add login button er egentlig én endring. Slå dem sammen til én commit med meldingen Add login page.
- Du vil eksperimentere med en hard reset. Sett først et sikkerhetsbokmerke: en branch
backuppå commiten du står på, uten å bytte til den. Fjern så Add login page helt fra main, staging og filene. - Eksperimentet var en feil. Få main tilbake til bokmerket ditt, uten å lete i reflogen.
Sjekk deg selv: Hent det fram igjen
De to siste lokale commitene dine er egentlig én endring. Du vil gjøre dem om til én commit og beholde alle endringene staget. Hvilken kommando starter jobben?
git reset --soft HEAD~2git reset --hard HEAD~2git restore --staged .
Hva kan git reset --hard ødelegge for godt?
- Commits du har laget
- Ucommittede endringer i sporede filer
- Alle usporede filer i mappen
For en time siden pushet du feature, bare som backup. Ingen andre bruker branchen. Nå vil du slå sammen de to siste commitene på den til én. Hva gjør du?
git reset --soft HEAD~2,git commit, og sågit pushgit reset --soft HEAD~2,git commit, og sågit push --force-with-leasegit revertpå de to commitene, og sågit push
Repetisjon fra «Les historikken»: hva peker HEAD~2 på i git reset --hard HEAD~2?
- Commiten to steg bakover langs første forelder, altså besteforelderen til HEAD
- Den andre forelderen til HEAD
- Der HEAD var for to kommandoer siden
Vanlige misforståelser
- «Reset sletter commits.» Reset flytter en branch. Commits som blir liggende igjen, finnes fortsatt en god stund.
- «Alle reset-moduser endrer filene mine.» Av
--soft,--mixedog--harder det bare--hardsom rører arbeidsfilene.--softog--mixedlar dem være i fred. - «Reset er greit også for commits jeg har pushet.» Teamet har fortsatt de gamle commitene. Pushet historikk angrer du med revert.
Les mer
Hva reset gjør, steg for steg
git reset <commit> jobber i opptil tre steg, og modusen bestemmer hvor den stopper:
- Flytt branchen
HEADpeker på, for eksempelmain, til<commit>.HEADfølger med, fordi den peker på branchen. - Gjør staging-området, også kalt indeksen, lik
<commit>. Hoppes over med--soft. - Gjør de sporede filene i arbeidsmappen like
<commit>. Bare med--hard.
| Modus | Branch og HEAD |
Staging-området | Arbeidsmappen |
|---|---|---|---|
git reset --soft HEAD~1 |
flyttet | uendret | uendret |
git reset HEAD~1 (samme som --mixed) |
flyttet | = HEAD~1 |
uendret |
git reset --hard HEAD~1 |
flyttet | = HEAD~1 |
= HEAD~1 |
Git forteller hva som skjedde. Etter en mixed reset lister den filene som nå har ustagede endringer; etter en hard reset sier den hvilken commit du havnet på:
$ git reset HEAD~1
Unstaged changes after reset:
M README.md
$ git reset --hard HEAD~1
HEAD is now at 831f7ba Initial snapshot
git reset uten commit betyr git reset HEAD: branchen blir stående, og staging-området settes tilbake til siste commit. I praksis tar det alt ut av staging og beholder alle endringer i filene.
Reset med filstier er en annen kommando
git reset -- README.md eller git reset HEAD~1 -- README.md flytter aldri en branch. Den kopierer filen fra commiten (standard HEAD) bare inn i staging-området, akkurat som git restore --staged. Arbeidsfilen blir ikke rørt:
$ git reset -q HEAD~1 -- README.md
$ git status --short
MM README.md
MM betyr at staging-området nå har den eldre versjonen (en staget endring sammenlignet med HEAD), mens arbeidsfilen fortsatt har den nyeste. Siden branchen ikke flytter seg, gir modusene ingen mening med filstier:
$ git reset --soft HEAD~1 -- README.md
fatal: Cannot do soft reset with paths.
git reset -p lar deg velge biter som skal ut av staging, som git restore -p --staged.
Nøyaktig hva --hard ødelegger
--hard overskriver ucommittede endringer i sporede filer, både stagede og ustagede. Usporede filer overlever, med mindre de står i veien for en sporet fil i mål-commiten: en usporet fil med samme navn, eller en usporet mappe der commiten har en fil, blir slettet uten advarsel. Committet arbeid blir ikke ødelagt, for den gamle commiten finnes fortsatt, og reflogen husker den. Før et eksperiment hjelper to billige sikkerhetsnett:
git stash -ulegger ucommittede endringer, også usporede filer, til side, så en hard reset ikke kan røre dem.git branch backupsetter et bokmerke på commiten du står på. Etterpå targit reset --hard backupdeg rett tilbake, uten leting i reflogen.
Mildere alternativer: --keep og --merge
git reset --keep HEAD~1 flytter branchen som --hard, men beholder de lokale endringene dine, og nekter hvis en endret fil måtte blitt overskrevet:
$ git reset --keep HEAD~1
error: Entry 'notes.txt' not uptodate. Cannot merge.
fatal: Could not reset index file to revision 'HEAD~1'.
Ingenting ble endret, så du kan committe eller stashe først. Det gjør --keep til en tryggere vane enn --hard når du bare vil fjerne commits. --merge er laget for å komme ut av en merge med konflikter; i dag gjør git merge --abort den jobben og er lettere å huske.
Tilbake etter en reset
Reset lagrer forrige posisjon i ORIG_HEAD, så rett etter en feil reset tar git reset --hard ORIG_HEAD deg tilbake. Har du ucommittet arbeid du vil beholde, bruker du --keep i stedet for --hard. Eldre posisjoner finner du i reflogen, som leksjonen «Reflog» viser.
Aldri reset det andre har
Reset skriver om branchen din. Er commitene du fjerner allerede pushet, blir git push avvist, fordi branchen din ikke lenger inneholder serverens commits. Tvinger du pushen gjennom, slettes de commitene fra serveren, også arbeid kolleger har pushet oppå. For delt historikk bruker du git revert i stedet. Tabellen i leksjonen «Revert» viser hvilket verktøy som passer når.
Offisiell dokumentasjon
- git-reset, med tabellen over modusene under «DISCUSSION»
- git-reflog
- Pro Git: Reset Demystified
Per-Arne's Git Good!