Skriva historia med Git Rebase

I det grundläggande Git-arbetsflödet utvecklar du en ny funktion i en dedikerad ämnesgren och slår sedan tillbaka den till en produktionsgren när den är klar. Detta gör git-sammanslagning ett integrerat verktyg för att kombinera grenar. Det är dock inte den enda Git erbjuder.

Kombinerar grenar genom att slå samman dem

Som ett alternativ till ovanstående scenario kan du kombinera grenarna med git rebase kommando. Istället för att binda grenarna tillsammans med ett sammanslagningsfördrag flyttar återhämtning hela funktionsgrenen till toppen av bemästra enligt nedanstående.

Kombinerar grenar med git rebase

Detta tjänar samma syfte som git-sammanslagning, integrering åtar sig från olika grenar. Men det finns två anledningar till att vi kanske vill välja en rabatt över en sammanslagning:

  • Det resulterar i en linjär projekthistoria.
  • Det ger oss möjlighet att städa upp lokala förpliktelser.

I denna handledning undersöker vi dessa två vanliga fall av git rebase. Tyvärr fördelarna med git rebase komma på en trade-off. När den används felaktigt kan den vara en av de farligaste operationerna du kan utföra i ett Git-arkiv. Så vi ska också noggrant titta på farorna med att återuppta.

förutsättningar

Denna handledning förutsätter att du är bekant med de grundläggande Git-kommandona och samarbetsflödena. Du bör vara bekväm att placera och begå ögonblicksbilder, utveckla funktioner i isolerade grenar, sammanfoga grenar tillsammans och trycka / dra grenar till / från avlägsna repositorier.

1. Återställande för en linjär historia

Det första användningsfallet vi ska utforska innebär en divergerande projekthistorik. Tänk på ett förråd där din produktionsgren har gått framåt medan du utvecklade en funktion:

Att rebase funktion gren på bemästra gren, du skulle köra följande kommandon:

git checkout funktion git rebase master

Detta transplanterar funktion gren från sin nuvarande plats till toppen av bemästra gren:

Det finns två scenarier där du vill göra det här. För det första om funktionen åberopade den nya förpliktelsen i bemästra, det skulle nu ha tillgång till dem. För det andra, om funktionen var klar skulle den nu vara inställd för en snabb framåtfogning bemästra. I båda fallen ger återhämtning resultat i en linjär historia, medan git-sammanslagning skulle leda till onödiga sammanslagningar.

Tänk på vad som skulle hända om du integrerar uppströmsförbindelserna med en sammanslagning istället för en rebase:

git checkout funktion git merge master 

Detta skulle ha gett oss ett extra sammanslagningsfördrag i funktion gren. Dessutom skulle detta hända varje gång du ville integrera uppströms förbindelser i din funktion. Så småningom kommer din projekthistoria att fyllas med meningslösa sammanslagningar.

Integrering uppströms förbinder sig med en sammanslagning

Samma fördel kan ses vid sammanslagning i andra riktningen. Utan en rebase, integrera den färdiga funktion gren in i bemästra kräver sammanslagning. Även om detta faktiskt är en meningsfull sammanslagning (i den meningen att den representerar en färdig funktion) är den resulterande historien full av gafflar:

Integrerar en färdig funktion med en sammanslagning

När du reser innan du går ihop kan Git spola framåt bemästra till toppen av funktion. Du hittar en linjär historia om hur ditt projekt har utvecklats i git logg output-förbinder sig i funktion är snyggt grupperade på toppen av förbindelserna bemästra. Detta är inte nödvändigtvis fallet när filialer är bundna ihop med ett sammanslagningsfördrag.

Återupptagning före sammanslagning

Lösa konflikter

När du kör git rebase, Git tar varje förpliktelse i grenen och flyttar dem, en för en, till den nya basen. Om någon av dessa förbinder ändrar samma linje (er) av kod som uppströmsförbindelsen, leder det till konflikt.

De git-sammanslagning kommandot låter dig lösa alla grenens konflikter i slutet av sammanslagningen, vilket är ett av de främsta syftet med ett sammanslagningsfördrag. Det fungerar dock lite annorlunda när du återkommer. Konflikter löses på grundval av befogenheter. Så om git rebase finner en konflikt, kommer det att stoppa återbetalningsförfarandet och visa ett varningsmeddelande:

Automatisk sammansmältning readme.txt CONFLICT (innehåll): Sammanfoga konflikt i readme.txt Misslyckades att slå samman i ändringarna ... När du har löst det här problemet kör du "git rebase - continuinue". Om du föredrar att hoppa över denna patch, kör "git rebase - skip" istället. För att kolla in den ursprungliga filialen och sluta återställa, kör "git rebase --abort". 

Visuellt är det här vad din projekthistorik ser ut när git rebase möter en konflikt:

Konflikten kan inspekteras genom att springa git status. Produktionen ser mycket ut som en sammanslagningskonflikt:

Unmerged paths: (använd "git reset HEAD ... "till unstage) (använd" git add ... "för att markera upplösning) båda modifierade: readme.txt inga ändringar läggas till för att begå (använd" git add "och / eller" git commit -a ") 

För att lösa konflikten öppnar du den konflikta filen (readme.txt i ovanstående exempel), hitta de drabbade raderna och manuellt redigera dem till önskat resultat. Därefter berätta för Git att konflikten är löst genom att ställa in filen:

git lägg till readme.txt 

Observera att detta är exakt samma sätt som du markerar en git-sammanslagning konflikt som löst. Men kom ihåg att du är mitt i en rebase-du vill inte glömma resten av de förpliktelser som behöver flyttas. Det sista steget är att berätta för Git att avsluta återhämtning med --Fortsätta alternativ:

git rebase - fortsätt 

Detta kommer att flytta resten av förbindelserna, en för en, och om några andra konflikter uppstår måste du upprepa processen helt och hållet igen.

Om du inte vill lösa konflikten kan du välja antingen --hoppa eller --avbryta flaggor. Den senare är särskilt användbar om du inte har någon aning om vad som händer och bara vill komma tillbaka till säkerheten.

# Ignorera det åtagande som orsakade konflikten git rebase - skip # Avbryt hela rebasen och gå tillbaka till ritbordet git rebase --abort 

2. Återställa för att rensa upp lokala förpliktelser

Hittills har vi bara använt git rebase att flytta grenar, men det är mycket kraftfullare än det. Genom att passera -jag flagga, kan du börja en interaktiv återkommande session. Interaktiv återställning kan du definiera exakt hur varje commit kommer att flyttas till den nya basen. Detta ger dig möjlighet att städa upp en funktions historia innan du delar den med andra utvecklare.

Låt oss säga att du slutat arbeta med din funktion gren och du är redo att integrera den i bemästra. För att starta en interaktiv återkommande session, kör följande kommando:

git checkout funktion git rebase -i master 

Detta öppnar en redaktör som innehåller alla förpliktelser i funktion som håller på att flyttas:

välj 5c43c2b [Beskrivning för äldsta commit] välj b8f3240 [Beskrivning för 2: e äldsta commit] välj c069f4a [Beskrivning för senaste commit] 

Denna lista definierar vad funktion gren kommer att se ut efter rebasen. Varje rad representerar en commit och plocka kommando före varje commit hash definierar vad som kommer att hända under repasen. Observera att åtagandena är listade från äldsta till senaste. Genom att ändra den här listan får du fullständig kontroll över din projekthistorik.

Om du vill ändra ordningen för förpliktelserna, ordnar du bara raderna. Om du vill ändra ett buds meddelande, använd omformulera kommando. Om du vill kombinera två förbindelser, ändra plocka kommandot till squash. Detta kommer att rulla alla ändringar i det åtagandet till det ovanstående. Till exempel, om du squashed det andra engagemanget i ovanstående notering, den funktion gren skulle se ut som följer efter att ha sparat och stängt redigeraren:

Squashing 2: a commit med en interaktiv rebase

De redigera kommandot är särskilt kraftfullt. När det når det angivna åtagandet, kommer Git pausa återbetalningsförfarandet, ungefär som när det möter en konflikt. Detta ger dig möjlighet att ändra innehållet i förbindelsen med git commit --amend eller till och med lägga till mer förpliktelser med standarden git lägg till/git commit kommandon. Någon ny förbinder dig att lägga till kommer att vara en del av den nya filialen.

Interaktiv återhämtning kan få en djupgående inverkan på ditt utvecklingsarbete. I stället för att oroa dig för att bryta upp dina ändringar i inkapslade förpliktelser kan du fokusera på att skriva din kod. Om du slutade begå vad som skulle vara en enda ändring i fyra separata ögonblicksbilder, så är det inte en problemskrivningshistoria med git rebase -i och squash dem alla till ett meningsfullt engagemang.

3. Faror vid återupptagning

Nu när du förstår git rebase, vi kan prata om när vi inte ska använda den. Internt, återföring flyttar faktiskt inte till en ny filial. I stället skapar det helt nya begåvningar som innehåller önskade ändringar. Med det här är sinnet, är återhämtning bättre visualiserad som följande:

Efter rebasen begås i funktion kommer att ha olika fördragshastigheter. Det innebär att vi inte bara bytte ut en filial - vi har bokstavligen omskrivit vår projekthistoria. Detta är en mycket viktig bieffekt av git rebase.

När du arbetar ensam på ett projekt är omskrivning av historia inte en stor sak. Men så fort du börjar arbeta i en samarbetsmiljö kan det bli mycket farligt. Om du skriver om, förbinder du att andra utvecklare använder (t.ex. förbinder sig på bemästra gren), det kommer att se ut som om de begås försvinner nästa gång de försöker dra in ditt arbete. Detta resulterar i ett förvirrande scenario som är svårt att återhämta sig från.

Med det här är det, bör du aldrig begå förpliktelser som har drivits till ett offentligt förråd, såvida du inte är positiv att ingen har byggt sitt arbete av dem.

Slutsats

Denna handledning introducerade de två vanligaste användningsfallen av git rebase. Vi pratade mycket om att flytta grenar runt, men kom ihåg att återuppbyggnad handlar om att styra din projekthistoria. Förmågan att skriva om på nytt förbjuder dig att fokusera på dina utvecklingsuppgifter istället för att bryta ner ditt arbete i isolerade ögonblicksbilder.

Observera att återhämtning är ett helt valfritt tillägg till din Git-verktygslåda. Du kan fortfarande göra allt du behöver med vanlig gammal git-sammanslagning kommandon. Det är faktiskt säkrare eftersom det undviker möjligheten att skriva om den offentliga historien. Men om du förstår riskerna, git rebase kan vara ett mycket renare sätt att integrera filialer jämfört med sammanslagningar.