Herschrijven van geschiedenis met Git Rebase

In de fundamentele Git-workflow ontwikkel je een nieuwe functie in een speciale topictak en voeg je hem vervolgens weer in een productietak zodra deze is voltooid. Dit maakt git fuseren een integraal hulpmiddel voor het combineren van branches. Het is echter niet de enige die Git aanbiedt.

Combinatie van takken door ze samen te voegen

Als alternatief voor het bovenstaande scenario zou je de takken kunnen combineren met de git rebase commando. In plaats van de takken samen te binden met een samenvoegverbintenis, verplaatst rebasen de volledige kenmerktak naar de top van meester zoals hieronder getoond.

Combinatie van takken met git rebase

Dit dient hetzelfde doel als git fuseren, integratie van commits uit verschillende branches. Maar er zijn twee redenen waarom we zouden kunnen kiezen voor een rebase via een samenvoeging:

  • Het resulteert in een lineaire projectgeschiedenis.
  • Het geeft ons de mogelijkheid om lokale commits op te ruimen.

In deze zelfstudie onderzoeken we deze twee veelvoorkomende gebruiksmogelijkheden van git rebase. Helaas zijn de voordelen van git rebase kom op tegen een compromis. Als het verkeerd wordt gebruikt, kan dit een van de gevaarlijkste bewerkingen zijn die u in een Git-repository kunt uitvoeren. We zullen dus ook goed kijken naar de gevaren van rebasen.

voorwaarden

Deze tutorial gaat ervan uit dat je bekend bent met de standaard Git-commando's en samenwerkingsworkflows. Je zou het op een comfortabele manier moeten opnemen en vastleggen van snapshots, functies ontwikkelen in geïsoleerde takken, takken samenvoegen en takken duwen / trekken naar / van externe opslagplaatsen.

1. Rebasing voor een lineaire geschiedenis

De eerste use-case die we zullen verkennen, betreft een uiteenlopende projectgeschiedenis. Overweeg een opslagplaats waar uw productietak is vooruitgegaan terwijl u een functie aan het ontwikkelen was:

Om de voorzien zijn van vertakken naar de meester branch, zou je de volgende commando's uitvoeren:

git checkout functie git rebase master

Dit transplanteert het voorzien zijn van aftakking van de huidige locatie naar de top van de meester tak:

Er zijn twee scenario's waar u dit zou willen doen. Ten eerste, als de functie is gebaseerd op de nieuwe commits in meester, het zou nu toegang hebben tot hen. Ten tweede, als de functie was voltooid, zou deze nu worden ingesteld voor een snel doorsturen naar meester. In beide gevallen resulteert rebasen in een lineaire geschiedenis, terwijl git fuseren zou resulteren in onnodige merge commits.

Overweeg bijvoorbeeld wat er zou gebeuren als u de upstream commits zou integreren met een fusie in plaats van een rebase:

git checkout feature git merge master 

Dit zou ons een extra merge commit hebben gegeven in de voorzien zijn van tak. Wat meer is, dit zou elke keer gebeuren dat je upstream commits in je feature wilde opnemen. Uiteindelijk zou je projectgeschiedenis bezaaid zijn met zinloze samenvoegingsbevoegdheden.

Stroomopwaartse commits integreren met een fusie

Ditzelfde voordeel kan worden gezien bij het samenvoegen in de andere richting. Zonder een rebase, het voltooide integreren voorzien zijn van vertakken naar meester vereist een merge commit. Hoewel dit eigenlijk een zinvolle merge commit is (in de zin dat het een voltooide functie vertegenwoordigt), is de resulterende geschiedenis vol met vorken:

Een voltooide functie integreren met een samenvoeging

Wanneer je rebase voor het samenvoegen, kan Git snel vooruitspoelen meester naar het puntje van voorzien zijn van. U zult een lineair verhaal vinden over hoe uw project is geëvolueerd in de git log output - de commits in voorzien zijn van zijn netjes gegroepeerd bovenop de commits in meester. Dit is niet noodzakelijk het geval wanneer takken samen worden gebonden met een samenvoegverbintenis.

Rebasen voor samenvoegen

Conflicten oplossen

Wanneer je rent git rebase, Git neemt elke commit in de branch en verplaatst ze één voor één naar de nieuwe basis. Als een van deze commits dezelfde regel (s) code wijzigt als de stroomopwaartse commits, resulteert dit in een conflict.

De git fuseren Met de opdracht kunt u alle conflicten van de branch oplossen aan het einde van de samenvoeging, wat een van de primaire doelen van een samenvoeg-commit is. Het werkt echter een beetje anders wanneer je rebaseert. Conflicten worden per commit opgelost. Dus indien git rebase vindt een conflict, het stopt de rebase-procedure en geeft een waarschuwing weer:

Readme.txt automatisch samenvoegen CONFLICT (inhoud): conflict samenvoegen in readme.txt kon de wijzigingen niet samenvoegen ... Als u dit probleem hebt opgelost, voert u "git rebase --continue" uit. Als u liever deze patch overslaat, voert u in plaats daarvan "git rebase --skip" uit. Om de originele branch te bekijken en te stoppen met rebasen, voer je "git rebase --abort" uit. 

Visueel is dit hoe uw projectgeschiedenis eruitziet wanneer git rebase ontmoet een conflict:

De conflicten kunnen worden geïnspecteerd door te hardlopen git status. De uitvoer lijkt erg op een samenvoegconflict:

Onverharde paden: (gebruik "git reset HEAD ... "unstage) (gebruik" git add " ... "om de resolutie te markeren) beide aangepast: readme.txt geen wijzigingen toegevoegd aan commit (gebruik" git add "en / of" git commit -a ") 

Als u het conflict wilt oplossen, opent u het conflicterende bestand (readme.txt in het bovenstaande voorbeeld), zoekt u de betreffende regels en bewerkt u deze handmatig naar het gewenste resultaat. Vertel Git vervolgens dat het conflict is opgelost door het bestand te organiseren:

git voeg readme.txt toe 

Let op: dit is exact dezelfde manier waarop u een a markeert git fuseren conflict als opgelost. Maar onthoud dat je midden in een rebase zit - je wilt de rest van de commits die verplaatst moeten worden niet vergeten. De laatste stap is om Git te vertellen dat je moet rebasen met de --doorgaan met keuze:

git rebase - vervolg 

Hierdoor worden de rest van de commits een voor een verplaatst en als er andere conflicten optreden, moet je dit proces helemaal opnieuw herhalen.

Als u het conflict niet wilt oplossen, kunt u kiezen voor de --overspringen of --afbreken vlaggen. Dit laatste is vooral handig als je geen idee hebt wat er aan de hand is en gewoon weer veilig wilt zijn.

# Negeer de commit die het conflict veroorzaakte git rebase --skip # Annuleer de volledige rebase en ga terug naar de tekentafel git rebase --abort 

2. Rebasen om lokale commits op te ruimen

Tot nu toe hebben we alleen gebruikt git rebase om takken te verplaatsen, maar het is veel krachtiger dan dat. Door het -ik vlag, kunt u een interactieve rebasing-sessie starten. Met interactieve rebasing kun je precies definiëren hoe elke commit naar het nieuwe basisstation wordt verplaatst. Dit geeft u de mogelijkheid om de geschiedenis van een functie op te schonen voordat u deze met andere ontwikkelaars deelt.

Stel bijvoorbeeld dat u klaar bent met uw werk voorzien zijn van branch en je bent klaar om het te integreren meester. Om een ​​interactieve rebasesessie te starten, voert u de volgende opdracht uit:

git uitcheck functie git rebase -i master 

Dit opent een editor met daarin alle commits voorzien zijn van die op het punt staan ​​verplaatst te worden:

kies 5c43c2b [Beschrijving voor oudste commit] pick b8f3240 [Beschrijving voor 2e oudste commit] pick c069f4a [Beschrijving voor meest recente commit] 

Deze lijst definieert wat de voorzien zijn van tak zal er na de rebase uitzien. Elke regel vertegenwoordigt een commit en de plukken commando voordat elke commit hash definieert wat er met de rebase zal gebeuren. Merk op dat de commits worden opgesomd van oudste naar meest recente. Door deze aanbieding te wijzigen, verkrijgt u volledige controle over uw projectgeschiedenis.

Als u de volgorde van de commits wilt wijzigen, kunt u de regels gewoon opnieuw ordenen. Als u het bericht van een commit wilt veranderen, gebruik dan de met andere woorden uitdrukken commando. Als u twee commits wilt combineren, wijzigt u de plukken commando om squash. Dit zal alle veranderingen in die commit naar die erboven gooien. Als u bijvoorbeeld de tweede commit in de bovenstaande lijst hebt geplet, is de voorzien zijn van branch ziet er als volgt uit na het opslaan en sluiten van de editor:

Het pletten van de 2e commit met een interactieve rebase

De Bewerk commando is bijzonder krachtig. Wanneer het de opgegeven commit bereikt, zal Git de rebase-procedure pauzeren, net zoals wanneer het een conflict tegenkomt. Dit geeft je de mogelijkheid om de inhoud van de commit te wijzigen git commit --amend of voeg zelfs meer commits toe met de standaard git add/Git commit commando's. Elke nieuwe commit die je toevoegt, zal deel uitmaken van de nieuwe branch.

Interactief rebasen kan een grote impact hebben op uw ontwikkelworkflow. In plaats van je zorgen te maken over het verbreken van je wijzigingen in ingekapselde commits, kun je je richten op het schrijven van je code. Als je uiteindelijk hebt vastgelegd wat een enkele wijziging in vier afzonderlijke snapshots zou moeten zijn, dan is dat geen probleem - herschrijf de geschiedenis met git rebase -i en plet ze allemaal in een zinvolle commit.

3. Gevaren van rebasen

Nu dat je het begrijpt git rebase, we kunnen praten over wanneer we het niet moeten gebruiken. Intern leidt het rebasen eigenlijk niet tot commits naar een nieuwe vestiging. In plaats daarvan worden er nieuwe commits gemaakt die de gewenste wijzigingen bevatten. Met dit is geest, rebasen wordt beter gevisualiseerd als het volgende:

Na de rebase, de commits in voorzien zijn van zal verschillende commit hashes hebben. Dit betekent dat we niet alleen een filiaal hebben gerepositioneerd, we hebben onze projectgeschiedenis letterlijk herschreven. Dit is een zeer belangrijk neveneffect van git rebase.

Wanneer je alleen aan een project werkt, is het herschrijven van de geschiedenis geen probleem. Zodra u in een samenwerkingsomgeving begint te werken, kan het echter zeer gevaarlijk worden. Als je commits herschrijft die andere ontwikkelaars gebruiken (bijv. Commits op de meester tak), het zal eruit zien alsof die commits verdwenen zijn de volgende keer dat ze proberen je werk op te halen. Dit resulteert in een verwarrend scenario dat moeilijk te herstellen is.

Met dit is geest, zou je nooit commits moeten rebasen die naar een openbare repository zijn geduwd, tenzij je zeker weet dat niemand hun werk op hen heeft gebaseerd.

Conclusie

Deze tutorial introduceerde de twee meest voorkomende gebruiksmogelijkheden van git rebase. We hebben veel gesproken over het verplaatsen van branches, maar onthoud dat rebasen eigenlijk gaat over het beheersen van je projectgeschiedenis. De kracht om commits opnieuw te schrijven maakt je vrij om je te concentreren op je ontwikkeltaken in plaats van je werk op te delen in geïsoleerde snapshots.

Merk op dat rebasen een volledig optionele toevoeging is aan je Git-toolbox. Je kunt nog steeds alles doen wat je nodig hebt met gewoon oud git fuseren commando's. Inderdaad, dit is veiliger omdat het de mogelijkheid van het herschrijven van de openbare geschiedenis vermijdt. Echter, als u de risico's begrijpt, git rebase kan een veel schonere manier zijn om branches te integreren in vergelijking met het samenvoegen van commits.