Meny for Per-Arne's Git Good!
Fag og prosjekt Per-Arne's Git Good!

Interaktiv rebase: rydd før du deler

Modul 4: Angre og skrive om · 13 min

Den interaktive leksjonen trenger JavaScript. Teksten under er hele leksjonen.

Etter leksjonen kan du

  • lese planen fra git rebase -i og forklare hvorfor den eldste commiten står øverst forstå
  • bruke reword, squash, fixup, drop og ny rekkefølge til å rydde en privat branch, og kontrollere resultatet med git diff ORIG_HEAD HEAD anvende
  • bruke git commit --fixup og git rebase -i --autosquash til å smelte en senere rettelse inn i riktig commit anvende
  • avgjøre om commits kan ryddes med interaktiv rebase eller må angres med revert fordi de er delt vurdere

Forstå: Historien du forteller

Før du deler en branch, bør du lese historien den forteller. Her står det: legg til søkefelt, rett skrivefeil, legg til debug-utskrift, og en commit som bare heter results. Det viser hvordan du jobbet, ikke hva som endret seg. En leser vil heller se to ryddige commits.

git rebase -i main åpner en plan i editoren, med én linje per commit på branchen. Hver linje starter med pick: spill av denne commiten som den er. Hvilken commit står øverst?

Du endrer ordet først på linjen. Fixup smelter commiten inn i den over og kaster sin egen melding. Squash gjør det samme, men lar deg slå sammen meldingene.

Drop fjerner commiten og endringen dens, her debug-utskriften. Reword beholder endringen, men lar deg skrive en bedre melding, her for results.

Git spiller av planen ovenfra. Rettelsen er smeltet inn i søkefeltet, debug-commiten er borte, og den siste commiten har en tydelig melding. Som ved en vanlig rebase er dette nye commits. De gamle er stiplet: ingen branch peker på dem.

Ofte oppdager du en feil senere, når andre commits allerede ligger oppå. Da lager git commit --fixup en commit med en melding som starter med fixup og navnet på målet. Det er en lapp til Git: dette hører hjemme i den commiten.

git rebase -i --autosquash main leser disse lappene. Den flytter hver fixup-linje rett under målet sitt og merker den fixup, så planen er ferdig. Du trenger bare å lagre.

Rydd bare commits som fortsatt er private, før du pusher eller lager en pull request. Og sjekk resultatet. git diff ORIG_HEAD HEAD viser hvordan innholdet endret seg. Har du bare skrevet om meldinger, slått sammen eller byttet rekkefølge, skal den ikke vise noe.

Se demo: Rydd en søke-branch

Vi står på den private branchen search. Loggen viser fire commits oppå snapshotet, med den nyeste øverst.

$ git log --oneline

Planen åpnes med git rebase -i main. Vi setter fixup på skrivefeilen, drop på debug-utskriften og reword på results. Etterpå ber Git om den nye meldingen.

$ git rebase -i main

To ryddige commits, begge med nye ID-er. De gamle fire ligger stiplet i grafen.

$ git log --oneline

Nå sjekker vi hva som endret seg i innholdet. Bare debug.txt er borte. Fixup og reword rørte ikke koden. Hadde vi droppet feil commit, ville git reset --hard ORIG_HEAD tatt oss tilbake.

$ git diff --stat ORIG_HEAD HEAD

Senere forbedrer vi søkefeltet. Endringen hører hjemme i den første commiten, ikke i en ny commit på toppen.

$ echo 'Search box with hint text' > search.txt && git add search.txt

git commit --fixup tar imot commiten som skal rettes. Her er det HEAD~1. Se på meldingen Git skriver.

$ git commit --fixup HEAD~1

Nå kjører vi git rebase -i --autosquash main. Planen er allerede ferdig: fixup-linjen står rett under målet sitt. Vi lagrer den bare.

$ git rebase -i --autosquash main

To commits igjen. Forbedringen bor nå inne i Add search box, og ingen kan se at den kom senere.

$ git log --oneline

Oppgaver

  1. Les historien branchen forteller, med én linje per commit.
  2. Rydd branchen: smelt skrivefeilrettingen inn i søkefelt-commiten, fjern debug-commiten og gi results meldingen Add search results.
  3. Sammenlign innholdet før og etter ryddingen.
  4. Lagre den stagede endringen som en fixup av commiten Add search box.
  5. Smelt fixup-commiten inn i målet sitt.

Øv: Overfør: en rapport i fem commits

Du står på report, en privat branch med fem commits. Ingen demo denne gangen.

Oppgaver

  1. Slå sammen Add chart og Add chart caption til én commit med meldingen Add chart with caption.
  2. Flytt Write method foran Write results, slik at rapporten kommer i naturlig rekkefølge.
  3. intro.txt inneholder skrivefeilen Introductoin. Rett den, og smelt rettelsen inn i commiten Write intro, slik at det ikke blir igjen noen ekstra commit.

Sjekk deg selv: Hent det fram igjen

Du har slått sammen to commits med squash og gitt en tredje ny melding med reword. Hva viser git diff ORIG_HEAD HEAD?

  • Ingenting, innholdet er det samme som før
  • Endringene fra de to commitene du slo sammen
  • Den gamle og den nye meldingen

Du vil smelte en commit inn i den over, og du vil ikke beholde meldingen dens. Hvilket ord bruker du?

  • squash
  • fixup
  • drop

Du har tre commits og bruker reword på den midterste. Hvilke commits får nye ID-er?

  • Bare den midterste, for det var bare den som endret seg
  • Den midterste og den etter, mens den første beholder ID-en sin
  • Ingen, for innholdet i filene er det samme

git log --oneline --decorate på main viser (HEAD -> main, origin/main) på den nyeste commiten, og en debug-commit to linjer lenger ned. Hvordan blir du kvitt debug-commiten?

  • Interaktiv rebase med drop, og så git push --force-with-lease
  • git revert på debug-commiten, og så en vanlig git push
  • git reset --hard til commiten før den, og så force-push

Repetisjon fra «Gode commits»: du gir commiten stuff, som legger til en pristabell, ny melding med reword. Hvilken emnelinje er best?

  • Added price table
  • Add price table
  • Update stuff

Vanlige misforståelser

  • «Planen viser den nyeste commiten øverst, som git log.» Den eldste står øverst, fordi Git spiller av planen ovenfra og ned.
  • «Interaktiv rebase redigerer commitene der de står.» Fra første endrede linje og nedover blir hver commit en ny commit med ny ID.
  • «Squash og fixup er det samme.» Begge smelter en commit inn i den over. Squash lar deg slå sammen meldingene, fixup beholder bare meldingen til målet.

Les mer

Planen er et manus

git rebase -i <base> skriver en huskeliste for commitene i base..HEAD, med den eldste først, og åpner den i editoren. Når du lagrer og lukker, kjører Git den ovenfra og ned. En ekte plan for en branch med tre commits:

pick c6766f7 # Add search box
pick 00d10e3 # Rename button
pick 1218aec # Add search results
# Rebase a5ea1be..1218aec onto a5ea1be (3 commands)

Det er ID-en som teller. Emnelinjen etter den står der bare for å hjelpe deg. Eldre Git-versjoner viser den uten #. Basen kan være en branch (git rebase -i main) eller et antall (git rebase -i HEAD~3 tar de tre siste commitene). git rebase -i --root tar med den aller første commiten også.

Ord Kort Hva det gjør
pick p Bruk commiten som den er
reword r Bruk endringen, stopp for å skrive ny melding
edit e Bruk commiten, og stopp så du kan endre eller dele den
squash s Smelt inn i commiten over, slå sammen begge meldingene
fixup f Smelt inn i commiten over, behold bare meldingen over (fixup -C beholder denne commitens melding i stedet)
exec x Kjør en skallkommando. Rebasen stopper hvis den feiler
break b Stopp her. git rebase --continue går videre
drop d Utelat commiten

Å slette en linje har samme virkning som drop, og Git advarer deg ikke. Sett git config rebase.missingCommitsCheck error hvis du vil at Git skal nekte en plan der en linje har forsvunnet. Mens en rebase står stille, kan du endre resten av planen med git rebase --edit-todo.

Stopp og endre en commit: edit

edit legger på commiten og stopper, med den commiten som HEAD. Endre filer, kjør git commit --amend, og avslutt med git rebase --continue.

Du kan også dele en commit. Angre commiten, men behold endringene, med en mixed reset, og commit så delene hver for seg:

$ git rebase -i main            # mark "Add a and b" with edit
Stopped at fce6fb0...  # Add a and b
You can amend the commit now, with
  git commit --amend
Once you are satisfied with your changes, run
  git rebase --continue
$ git reset HEAD^
$ git add a.txt && git commit -m "Add a"
$ git add b.txt && git commit -m "Add b"
$ git rebase --continue
Successfully rebased and updated refs/heads/big.
$ git log --oneline
016f3d8 Add b
7170811 Add a
a5ea1be Initial snapshot

git reset HEAD^ uten --hard flytter bare HEAD, som står løsrevet under rebasen, og staging-området. Filene dine blir som de er.

Forbered rettelser mens du jobber

git commit kan skrive planlinjene for deg. Commit-meldingen forteller --autosquash hvor commiten hører hjemme:

Kommando Melding Senere i planen
git commit --fixup=<commit> fixup! <subject> fixup: behold målets melding
git commit --squash=<commit> squash! <subject> squash: slå sammen meldingene
git commit --fixup=amend:<commit> amend! <subject> fixup -C: endring og ny melding
git commit --fixup=reword:<commit> amend! <subject> bare ny melding, ingen endring

Deretter flytter git rebase -i --autosquash main hver linje inn under målet sitt. git config rebase.autoSquash true gjør --autosquash til standard for interaktive rebaser.

Sjekk at du bare endret det du mente

git diff ORIG_HEAD HEAD sammenligner innholdet før og etter. Den skal være tom når du bare har skrevet om meldinger, byttet rekkefølge eller slått sammen commits. Viser den noe du ikke mente, må du se nærmere etter. For å sammenligne commit for commit bruker du git range-diff. Her ble Rename button smeltet inn i Add search box:

$ git range-diff main ORIG_HEAD HEAD
1:  c6766f7 ! 1:  c463304 Add search box
    @@ search.html (new)
     +<form>
     +  <label>Search</label>
     +  <input name=q>
    -+  <button>Go</button>
    ++  <button>Search</button>
     +</form>
2:  00d10e3 < -:  ------- Rename button
3:  1218aec = 2:  1bb4102 Add search results

! merker en commit der endringen er forskjellig, med en diff av de to diffene under. < betyr at den gamle commiten ikke lenger har noen motpart, og = betyr at endringen er identisk.

Angre en opprydding

Mens rebasen pågår, setter git rebase --abort alt tilbake. Når den er ferdig, ligger branchens gamle tupp i branchens reflog og som regel i ORIG_HEAD, så git reset --hard ORIG_HEAD tar deg tilbake, så lenge du ikke har arbeid som ikke er committet. En reset under rebasen, som i delingseksempelet over, flytter ORIG_HEAD. Da finner du den gamle tuppen i branchens reflog, som leksjonen «Reflog» viser.

Tommelfingerregel

Rydd før du deler. Hvordan du pusher en omskrevet branch som bare er din, viser leksjonen «Rebase». Rydd aldri main eller andre brancher som andre bruker.

Offisiell dokumentasjon