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 ch01-getting-started.asc 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 ch01-getting-started.asc), 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.
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.
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.
Note
|
Grenen |
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å.
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.
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
.
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
.
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'
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
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.
Note
|
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
.
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.
Note
|
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 |