Nybörjarguide för interaktiv omformning
@StrideStride
Informationsteknik och tjänster
Förrförra året gjorde jag för första gången en interaktiv rebase och jag blev imponerad av vad man kan göra med den. Jag tyckte också att det var lite komplicerat till en början. Förhoppningsvis kommer den här guiden att bidra till att undanröja en viss osäkerhet kring det.
Och eftersom det är så kraftfullt och man i princip kan skriva om historien, en liten varning innan vi börjar: Det finns många åsikter om Git och om huruvida rebasing är en bra idé eller inte. Det här inlägget kommer inte att dyka ner i dessa diskussioner och är enbart tänkt att gå igenom grunderna för att använda interaktiv rebasing.
TL;DR
- Interaktiv rebasing kan användas för att ändra commits på många olika sätt, till exempel redigering, radering och squashing.
- För att tala om för Git var den interaktiva ombaseringen ska börja använder du SHA-1 eller indexet för den commit som omedelbart föregår den commit du vill ändra.
- Under en interaktiv ombasering, när Git gör en paus vid en commit som du har markerat för att redigera, skiljer sig arbetsflödet inte från en normal commit-process – du iscensätter filer och sedan beger du dem. Den enda skillnaden är att du använder kommandot
git commit --amend
istället förgit commit
. - Interaktiv rebasing kommer att skapa nya SHA-1:s därför är det bäst att använda interaktiv rebasing på commits som du inte har pushat till en fjärrgren.
Problemet
I det här exemplet kommer vi att arbeta oss igenom en situation där vi har jobbat i en funktionsgren och vi har ett par commits som vi vill ändra eller ta bort. Så här ser vår git-logg ut:
Från git-loggen ovan är det här de två commits vi vill ändra: 4a4d705
– I denna commit har vi av misstag begått en fusionskonflikt6c01350
– I denna commit har vi tagit bort fusionskonflikten
Vad vi vill göra är att gå tillbaka i tiden till 4a4d705
, ta bort fusionskonflikten i commiten och sedan radera 6c01350
eftersom fusionskonflikten är löst och vi inte längre behöver denna commit. Detta kommer att vara en förbättring för vår commit-historik av två skäl:
- Vi kommer inte att ha trasiga commits (sammanfogningskonflikter)
Vi kommer bara att ha meningsfulla commits, inga commits som enbart är relaterade till att fixa en missad sammanfogningskonflikt
Lösningen
Denna situation är en bra kandidat för interaktiv rebasing. Scott Chacon beskriver i sin bok Pro Git interaktiv rebasing på följande sätt: ”Ibland kan den sak som korrigeras … inte ändras till det inte helt perfekta commit som det korrigerar, eftersom det commitet är djupt begravt i en patchserie. Det är precis vad interaktiv rebase är till för: använd den efter massor av , genom att arrangera om och redigera commits och trycka ihop flera commits till en.”
Vilka commits vill vi ändra?
För att starta en interaktiv rebase måste vi tala om för Git vilka commits vi vill ändra. Detta gör vi genom att referera till den commit som ligger omedelbart före den tidigaste commit vi vill ändra. Eller, enkelt uttryckt, vi refererar till ”det sista commit som vi vill behålla som det är”, enligt Scott Chacon.
Låt oss titta på vårt exempel för att få en bättre förståelse. Det finns två sätt att referera till denna commit:
Med SHA-1 – Den sista commit vi vill behålla som den är har en SHA-1 på 528f82e
så vi kan skicka detta till vårt interaktiva rebase-kommando.
Med index – Det sista commit vi vill behålla som det är har en indexposition på 3 (Git använder nollbaserad indexering) så vi kan skicka in HEAD~3
till vårt interaktiva rebase-kommando.
Anmärkning – Om du bara har ett fåtal commits som du ska interaktivt rebasera, är det förmodligen enklare för de flesta att använda indexet. Men om du har många commits är det förmodligen enklare att använda SHA-1 så att du inte behöver räkna hela vägen ner i git-loggen.
Start interactive rebasing
Baserat på vårt exempel kommer vi att köra antingen:
$ git rebase -i 528f82e
Och
$ git rebase -i HEAD~3
vilket öppnar det här fönstret i Vim:
Märk att ändringarna är i motsatt ordning jämfört med git-loggen. I git log är det senaste commitet överst. I den här vyn är det senaste commit längst ner. Lägg också märke till att kommentarerna nedan ger en användbar lista över de giltiga kommandon vi kan använda på varje commit.
Om du inte kan Vim klickar du bara på varje ord pick
som du vill redigera och trycker sedan på <i>
-tangenten (för insert-läge). När du har skrivit färdigt trycker du på <esc>
för att avsluta insättningsläget.
I vårt exempel har vi ändrat kommandot till edit
för den commit vi vill ändra och vi har ändrat kommandot till drop
för den commit vi vill ta bort. Sedan kör vi :wq
för att spara och avsluta Vim-fönstret.
Ändra commit
Tillbaka i terminalen ser vi det här meddelandet:
Det här är logiskt att vi stoppas vid 4a4d705
. Det här är den äldsta commiten i serien av commits som vi vill ändra. Vi börjar med denna commit och arbetar oss igenom varje commit fram till den senaste.
Som en påminnelse var 4a4d705
den commit med sammanslagningskonflikten som vi av misstag begick. När vi öppnar vår editor ser vi fusionskonflikten där:
Så vi fixar fusionskonflikten i filen men vad gör vi nu? När du är osäker, git status
:
Cool! Det här är faktiskt till stor hjälp. Vi ser att vi för närvarande redigerar 4a4d705
, och vi ser de två nästa commits som ska åtgärdas efter detta.
Resten av meddelandet förklarar ett välbekant arbetsflöde för oss. Git talar om för oss att om vi vill ändra i commiten kör vi git commit --amend
. Detta kommer i huvudsak att fungera som vårt typiska git commit
som vi använder i ett normalt arbetsflöde. Längst ner i meddelandet ser vi att vår fil har ändrats och återspeglar de ändringar vi just gjort för att ta bort fusionskonflikten. Vi måste nu sätta filen i scenarion innan vi lägger in den. Detta skiljer sig inte från ett normalt arbetsflöde.
Allt vi gör är git add tempChanger.js
för att iscensätta den redigerade filen och sedan git commit --amend
för att bekräfta den iscensatta filen! Detta kommer nu att öppna ett Vim-fönster igen med commit-meddelandet:
Vi kan antingen redigera commit-meddelandet eller lämna det som det är. Vi väljer att behålla meddelandet oförändrat och skriver :wq
för att spara och avsluta fönstret.
Vi har nu redigerat vårt gamla meddelande. Så vad händer nu? Kör git status
:
Fortsätt interaktiv återställning
Vi har inget annat att ändra i commit så låt oss fortsätta!
Vi kör git rebase --continue
och ser följande meddelande:
Woah, är vi klara? Men hur är det med de andra två commits? Tja, nästa commit att agera på var 6c01350
. Denna commit markerade vi för att ta bort (drop
) när vi startade den interaktiva ombaseringen. Git raderade det automatiskt och gick vidare till nästa commit, 41aa9d2
. Denna ändrades aldrig i den första interaktiva ombasen. Dess standardkommando var pick
vilket innebär att commit kommer att användas. Git tillämpade den commiten och eftersom det var den sista commiten avslutades den interaktiva återställningen.
Notera att om vi hade fler commits att redigera skulle vi helt enkelt ha gått vidare till nästa commit och påbörjat processen att ändra den precis som vi gjorde ovan. Cykeln fortsätter tills det inte finns några commits kvar.
Utgångsknappen
Det är värt att notera att om vi vid något tillfälle i vår interaktiva rebase gör bort oss och inte vet hur vi ska åtgärda det, kan vi alltid avbryta. När som helst kan vi köra git rebase --abort
i terminalen och den interaktiva ombasen kommer att avbrytas utan att några ändringar sparas. Vi skulle då behöva starta den interaktiva rebasen på nytt.
Efter den interaktiva rebasen
Vår git-logg ser nu ut så här:
Du kommer att märka att några saker har ändrats jämfört med innan vi startade den interaktiva rebasen:
- Vi har inte längre commit
6c01350
med commitmeddelandet ”Remove merge conflict”. Detta är den commit som vi tog bort i vår interaktiva återställning. - Vår commit som vi redigerade,
4a4d705
, har en annan SHA-1,2b7443d
. - Vår senaste commit från vår ursprungliga git-logg,
41aa9d2
, har också en ny SHA-1,2b95e92
. Denna commit ändrades inte utan tillämpades helt enkelt på commiten före den2b7443d
.
Biverkningar av interaktiv ombasering
För de två senaste commitsna i vår git-logg, eftersom de har nya SHA-1:or, ser Git dem som helt nya commits. Detta gäller även för vår senaste commit, 2b95e92
, där varken commitmeddelandet eller filerna ändrades alls. Detta tar upp en viktig punkt med interaktiv rebasing: Om du ändrar en commit kommer den commiten och alla efterföljande commits att ha nya SHA-1:s.
Detta kommer inte att påverka någonting om de commits som du har ändrat inte har pushats till en fjärrgren. Men om du faktiskt genomförde en interaktiv återställning på commits som redan hade lagts ut till en fjärrgren och sedan lade ut din gren igen skulle du se:
Tekniskt sett skulle du kunna kringgå det här genom att använda git push --force
, men det är mycket farligt. Om fjärrgrenen har commits från andra personer men din lokala gren inte har dessa commits ännu, kommer du effektivt att radera deras commits.
En annan lösning är att använda git push --force-with-lease
som bara kommer att ändra dina commits men inte commits som tillhör andra, men detta kan också vara problematiskt. Om en annan utvecklare till exempel redan har de commits som fått nya SHA-1 i sin lokala gren, kommer de att få sammanslagningskonflikter med var och en av dessa commits när de drar den fjärrstyrda grenen.
När man ska använda --force-with-lease
ligger utanför det här blogginläggets räckvidd, men det är bäst att rådgöra med andra medlemmar i teamet innan du gör det. Du kan läsa mer om git push --force-with-lease
här.
Den viktigaste behållningen från det här avsnittet är att det är mycket enklare och säkrare att använda interaktiv rebasing på commits som ännu inte har pushats till en fjärrgren.
Skrivet av Blake DeBoer. Ursprungligen publicerad på Dev.to
Senior, Lead, or Principal developer in NYC? Stride anställer! Vill du höja nivån på ditt teknikteam? Se hur vi gör det! www.stridenyc.com
Taggar
Skapa ett kostnadsfritt konto för att låsa upp din egen läsupplevelse.
Leave a Reply