Git
Chapters ▾ 2nd Edition

3.1 Git förgreningar - Grenar i ett nötskal

Nästan alla versionshanteringssystem har någon form av stöd för förgreningar. En förgrening innbär att du avviker från utvecklingens huvudspår och fortsätter att arbeta utan att påverka huvudspåret. I många versionshanteringsverktyg är detta är ganska dyr process som ofta innebär att skapa en ny kopia av källkodsmappen och tar lång tid för stora projekt.

En del refererar till Gits förgreningsmodell som dess “mördarfunktion,” och den gör verkligen att Git sticker ut i versionshanteringsvärlden. Vad är så speciellt? Det sätt som Git gör grenar på är fantastiskt lättviktigt och att skapa nya grenar görs näst intill ögonblickligen, likaså att byta mellan olika grenar. Olikt många andra versionshanteringssystem uppmuntrar Git till ett arbetsflöde där man ofta förgrenar och slår ihop dem ofta, även flera gånger dagligen. Att förstå och behärska denna teknik ger dig ett kraftfullt och unikt verktyg och kan helt påvekar hur du väljer att utveckla.

Grenar i ett nötskal

För att verligen förstå hur Git använder grenar måste vi ta ett steg tillbaka och undersöka hur Git sparar data.

Som du kanske kommer ihåg från Kom igång så sparar Git inte data som en serie ändringar eller skillnader, utan istället som en serie ögonblicksbilder.

När du skapar en version, skapar Git ett versionsobjekt som innehåller en pekare till ögonblicksbilden av innehållet du förberett för ändringen. Detta objektet innehåller författarens namn och e-postadress, meddelandet som angavs, och pekare till den version eller de versioner som var direkt före denna version (dess förälder eller föräldrar): Ina föräldrar för den första versionen, en förälder för en normal version och flera föräldrar för en version som är resultatet efter en sammanslagning mellan två eller flera grenar.

För att visualisera detta, anta att du har en mapp som innehåller tre filer. Du preparerar alla och därefter skapar din version. När du preparerar filerna beräknas en checksumma för varje fil (SHA-1 kontrollsumman som vi nämnt tidigare i Kom igång), lagrar versionen av filen i Git-förvaret (Git refererar till dem som blobbar), och lägger till kontrollsumman till prepareringsytan:

$ git add README test.rb LICENSE
$ git commit -m 'The initial commit of my project'

När du skapar en version genom att köra git commit, beräknar Git en kontrollsumma för varje underkatalog (i detta fall enbart projektets rotkatalog) och sparar dessa tre objekt i Git-förvar. Git skapar sedan ett versionsobjekt som har metadata och en pekare till rotkatalogens projektträd så att den kan återskapa den ögonblicksbilden när så behövs.

Ditt Git-förvar innehåller nu fem objekt: tre blobbar (varje representerar innehållet i en av de tre filerna), ett träd som listar innehållet i en katalog och specificerar vilka filnamn som finns lagrade som vilka blobbar, och en version med pekaren till det rotträdet och versionens metadata.

A commit and its tree.
Figur 9. En version och dess träd.

Om du gör några ändringar och skapar en ny version, kommer nästa version spara en pekare till den version som var omedelbart före denna.

Commits and their parents.
Figur 10. Versioner och deras föräldrar

En gren i Git är bara en enkel flyttbar pekare till någon av dessa versioner. Standardgrenen i Git är master. Om du börjar skapa vereionser, så får du en master-gren som pekar på din senast sparade version. Varje gång du gör en ny version, så förflyttas master-pekaren med framåt automatiskt.

Notera

Grenen “master” i Git är inte speciell. Den är precis som vilken annan gren som helst. Enda anledningen att nästan alla förvar har en, är för att kommandot git init skapar den som standard och de flesta bryr sig inte om att ändra det.

A branch and its commit history.
Figur 11. En gren och dess versionshistorik.

Skapa en ny gren

Vad händer när du skapar en ny gren? Att göra det skapar en ny pekare som du kan flyutta runt. Anta att du vill skapa en gren som heter testing. Du kan göra detta med kommandot git branch:

$ git branch testing

Detta skapar en ny pekare till samma version som du just nu står på.

Two branches pointing into the same series of commits.
Figur 12. Två grenar pekar på samma serie av versioner

Hur vet Git vilken gren du just nu står på? Den har en speciell pekare som kallas HEAD. Notera att detta är mycket annorlunda än konceptet HEAD i andra versionshanteringssystem som du kanske använt, som t.ex. Subversion eller CVS. I Git är detta en pekare till den lokala gren du just nu står på. I detta fallet är du fortfarande på master. Kommandot git branch skapade bara en ny gren — den bytte inte till den grenen.

HEAD pointing to a branch.
Figur 13. HEAD pekar på en gren

Du kan enkelt se detta genom att köra kommandot git log som visar va grenpekarna pekar på. Denna flagga kallas --decorate.

$ git log --oneline --decorate
f30ab (HEAD -> master, testing) add feature #32 - ability to add new formats to the central interface
34ac2 Fixed bug #1328 - stack overflow under certain conditions
98ca9 The initial commit of my project

Du kan se grenarna “master” och “testing” som är precis vid versionen f30ab.

Byta grenar

Att byta till en existerande gren görs med kommandot git checkout. Låt oss byta till den nya grenen testing:

$ git checkout testing

Detta flyttar HEAD till att peka på grenen testing.

HEAD points to the current branch.
Figur 14. HEAD pekar på aktuell gren

Vad är vitsen med det? Låt oss illustrera genom att skapa en ny version:

$ vim test.rb
$ git commit -a -m 'made a change'
The HEAD branch moves forward when a commit is made.
Figur 15. Grenen som HEAD pekar på flyttar fram när en version sparas

Det intressanta är att din gren testing har flyttat fram, men din master-gren pekar fortfarande på versionen du var på innan du körde git checkout för att byta gren. Låt oss gå tillbaks till master-grenen:

$ git checkout master
HEAD moves when you checkout.
Figur 16. HEAD flyttar när du gör checkout

Kommandot gjorde två saker. Den flyttade din HEAD pekare tillaka till grenen master och den återställde filerna i din arbetskopia till ögonblicksbilden som master pekar på. Detta betyder att alla ändringar från och med nu kommer divergera från en tidigare version av projektet. I praktiken gör den det jobb du gjort i din testing-gren ogjort så att du kan gå i en annan riktning.

Notera
Byta grenar ändrar filer i din arbetskopia

Det är viktigt att veta att när du byter gren i Git så ändras filer i din arbetskopia. Om du byter till en äldre gren kommer din arbetskopia återställas till att se ut som den gjorde senast du gjorde en version på den grenen. Om Git inte kan göra detta utan att kasta icke-versionshanterat arbete, kommer du inte tillåtas byta alls.

Låt oss göra några ändringar och spara en till version:

$ vim test.rb
$ git commit -a -m 'made other changes'

Nu har din projekthistorik divergerat (se Divergent historik). Du skapade och bytte till en gren, gjorde lite arbete på den, och sedan bytte du tillbaks till ditt huvudspår och gjorde lite annat arbete. Båda ändringarna är isolerade i olika grenar: du kan byta fram och tillbaka mellan grenarna och slå ihop dem när du är redo. Allt detta gjorde du med några enkla kommandon; branch, checkout och commit.

Divergent history.
Figur 17. Divergent historik

Du kan också se detta enkelt med kommandot git log. Om du kör git log --oneline --decorate --graph --all så skrivs hela din versionshistorik ut, samt var dina grenar pekar och visar hur din historik har divergerat.

$ git log --oneline --decorate --graph --all
* c2b9e (HEAD, master) made other changes
| * 87ab2 (testing) made a change
|/
* f30ab add feature #32 - ability to add new formats to the
* 34ac2 fixed bug #1328 - stack overflow under certain conditions
* 98ca9 initial commit of my project

Eftersom en gren i Git egentligen är en fil som innehåller den 40 tecken långa SHA-1 kontrollsumman av versionen den pekar på, är grenar enkla att skapa och ta bort. Att skapa en ny gren är lika enkelt som att skriva 41 byte till en fil (40 tecken och ett nyradstecken).

Detta är i skarp kontrast till hur de flesta äldre versionshanteringssystem fungerar, då förgreningar innefattar att kopiera hela projektets filer till en ny katalog. Detta kan ta flera sekunder eller till och med minuter, beroende på storleken på projektet, men i Git är processen alltid momentan. Eftersom vi lagrar informationen om föräldrarna till en version blir det lätt att automatiskt hitta en bra bas för sammanslagning och det är ganska lätt att genomföra. Detta uppmuntrar utvecklare att skapa och använda grenar ofta.

Låt oss undersöka varför vi skall göra på detta viset.

Notera
Skapa en ny gren och byta till den samtidigt

Det händer ofta att du vill skapa en ny gren och byta till den omedelbart. — Detta kan göras med en operation genom kommandot git checkout -b <nyttgrennamn>.

scroll-to-top