Git
Chapters ▾ 2nd Edition

3.2 Git förgreningar - Grundläggande förgrening och sammanslagning

Grundläggande förgrening och sammanslagning

Nu skall vi gå igenom ett enkelt exempel med en förgrening och sammanslagning i ett arbetsflöde som du kan stöta på i den riktiga världen. Du kommer följa dessa steg:

  1. Göra lite arbete på en hemsida.

  2. Skapa en gren för en ny berättelse du jobbar på.

  3. Göra lite arbete i den grenen.

När du håller på med detta får du ett samtal om ett annat problem är mer kritiskt och att det behövs en snabbkorrigering. Du gör följande:

  1. Byter till din produktionsgren.

  2. Skapar en gren för att lägga till korrigeringen.

  3. Efter det är testat, slår du samman ändringen och skickar till produktion.

  4. Du byter tillbaks till din berättelse och fortsätter arbeta.

Grundläggande förgrening

Låt oss anta att du jobbar på ett projekt och har ett antal versioner sparade i master-grenen.

A simple commit history.
Figur 18. En enkel versionshistorik

Du har beslutat att du skall arbeta med problem #53 i det ärendehanteringssystem som ditt företag använder. För att skapa en ny gren och byta till den samtidigt kan du köra kommandot git checkout med flaggan -b:

$ git checkout -b iss53
Switched to a new branch "iss53"

Detta är en förkortning för:

$ git branch iss53
$ git checkout iss53
Creating a new branch pointer.
Figur 19. Skapa en ny grenpekare

Du jobbar på din webbsida och sparar några versioner. När du gör så flyttas grene iss53 framåt, eftersom du har den utcheckad (det vill säga HEAD pekar på denna):

$ vim index.html
$ git commit -a -m 'added a new footer [issue 53]'
The `iss53` branch has moved forward with your work.
Figur 20. Grenen iss53 har flyttat fram i takt med ditt arbete,

Nu får du samtalet att det är ett problem med webbsidan som du måste fixa omgående. Med Git behöver du inte distribuera din fix tillsammans med ändringarna som du gjort i iss53, och du behöver inte lägga massa tid för att återställa dina ändringar innan du kan jobba på en lösning på problemet i produktion. Allt du behöver göra är att byta tillbaka till din master-gren.

Märk väl att innan du gör det bör du kontrollera om det finns osparade ändringar i din arbetskatalog eller i din prepareringsyta som påverkas av grenen du checkar ut, eftersom att Git då inte kommer att låta dig byta gren. Det är bäst att ha en ren arbetsyta när du byter gren. Det finns alltid sätt att komma runt detta (nämligen gömman och ändra senaste versionen) som vi kommer att gå igenom längre fram i Stashing and Cleaning. För nu, anta att du har sparat alla dina ändringar i en version, så att du kan byta tillbaka till din master-gren:

$ git checkout master
Switched to branch 'master'

Nu är ditt projekts arbetskatalog exakt som den var innan du började jobba på problem #53, och du kan koncetrera dig på din snabbkorrigering. Detta är viktigt att komma ihåg: när du byter gren återställer Git din arbetskatalog till att se ut så som den gjorde sist du sparade en version i den grenen. Git lägger till, tar bort, och modifierar filer automatiskt för att säkerställa att din arbetskopia är precis så som grenen såg ut när du sparade din senaste versio till den.

Nu har du en snabbkorrigering att göra. Låt oss skapa en hotfix-gren som vi jobbar på tills det är löst:

$ git checkout -b hotfix
Switched to a new branch 'hotfix'
$ vim index.html
$ git commit -a -m 'fixed the broken email address'
[hotfix 1fb7853] fixed the broken email address
 1 file changed, 2 insertions(+)
Hotfix branch based on `master`.
Figur 21. Korrigeringsgren baserad på grenen master

Du kan köra dina tester och säkerställa att din lösning fungerar som förväntat och slutligen slå samman dina ändringar till master-grenen för att skicka dem till produktion. Du gör detta med kommandot git merge:

$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
 index.html | 2 ++
 1 file changed, 2 insertions(+)

Du kommer notera frasen “fast-forward” (sv. “snabbspolning”) i den sammanslagninen. Detta eftersom versionen C4 som grenen hotfix pekar på var direkt före versionen `C2 som du är på, kommer Git helt enkelt flytta fram pekaren. Detta är som att säga att när du försöker slå samman en version med en version som kan nås av den första versionens historik, gör Git saken enkel för sig genom att bara flytta pekaren framåt eftersom det inte finns något arbete som divergerat som behöver slås ihop — detta kallas “snabbspolning”.

Din ändring är nu i ögonblicksbilden av versionen som grenen master pekar på och du kan frisläppa korrigeringn.

`master` is fast-forwarded to `hotfix`.
Figur 22. master snabbspolas till hotfix

Efter att din oerhört viktiga korrigering är frisläppt är du redo att byta tillbaks till det du höll på med innan du blev avbruten. Först tar du bort hotfix-grenen, eftersom du inte längre behöver den. — grenen master pekar på samma ställe. Du kan ta bort den med flaggan -d till git branch:

$ git branch -d hotfix
Deleted branch hotfix (3a0874c).

Nu kan du byta tillbaks till din gren med pågående arbete på problem #53 och fortsätta arbeta med det.

$ git checkout iss53
Switched to branch "iss53"
$ vim index.html
$ git commit -a -m 'finished the new footer [issue 53]'
[iss53 ad82d7a] finished the new footer [issue 53]
1 file changed, 1 insertion(+)
Work continues on `iss53`.
Figur 23. Arbete fortsätter på iss53

Det är värt att notera att arbetet du gjorde i din hotfix-gren inte finns i motsvarande filer i iss53-grenen. Om du behöver dra in dessa ändringar, kan du slå samman din master-gren in till din iss53-gren genom att köra git merge master, eller så kan du vänta med att integrera ändringarna tills du beslutar att slå samman ändringarna från iss53 tillbaks i master senare.

Grundläggande sammanslagning

Anta att du beslutat att ditt jobb på problem #53 är färdigt och redo att slås samman med din master-gren. För att göra det behöver du slå ihop din iss53 gren in i master, precis som du gjorde med din hotfix-gren tidigare. Allt du behöver göra är att checka ut den gren du vill hämta ändringarna till och sedan köra kommandot git merge:

$ git checkout master
Switched to branch 'master'
$ git merge iss53
Merge made by the 'recursive' strategy.
index.html |    1 +
1 file changed, 1 insertion(+)

Detta ser lite annorlunda ut än hotfix-sammanslagningen du gjorde tidigare. I detta fallet har din utvecklingshistorik divergerat från en tidigare punkt. Eftersom versionen i grenen du är på inte är en direkt ättling av den gren du hämtar in, måste Git göra lite jobb. I detta fallet gör git en enkel trevägs-sammanslagning genom att använda de två ögonblicksbilderna som framgår av bägge grenpekarna samt den första gemensamma versionen av de två.

This looks a bit different than the hotfix merge you did earlier. In this case, your development history has diverged from some older point. Because the commit on the branch you’re on isn’t a direct ancestor of the branch you’re merging in, Git has to do some work. In this case, Git does a simple three-way merge, using the two snapshots pointed to by the branch tips and the common ancestor of the two.

Three snapshots used in a typical merge.
Figur 24. Tre ögonblicksbilder som används i en typisk sammanslagning

Istället för att bara flytta grenpekaren framåt, skapar Git en ny ögonblicksbild som är resultatet av denna trevägssammanslagning och skapar automatiskt en ny version och pekar på denna. Detta kallas för en sammanslagningsversion och är speciell så tillvida att den har fler än en förälder.

A merge commit.
Figur 25. En sammanslagningsversion.

Nu när ditt arbete är sammanslaget har du inte längre nytta av grenen iss53. Du kan stänga ärendet i ditt ärendehanteringssystem och ta bort grenen:

$ git branch -d iss53

Grundläggande sammanslagninskonflikter

Emellanåt går inte denna processen smärtfritt. Om du har ändrat samma del av samma fil olika i de två grenarna du slår ihop kommer inte Git att kunna slå samman ändringarna på ett korrekt sätt. Om din lösning för problem #53 modifierade samma del av filen som din hotfix-gren, kommer du få en sammanslagninskonflikt som ser ut något i stil med detta:

$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

Git har inte automatiskt skapat den nya sammanslagningsversionen. Den har pausat processen medan du löser upp konflikten. Om du vill se vilka filer som inte är sammanslagna någon gång efter en konflikt kan du köra git status:

$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add <file>..." to mark resolution)

    both modified:      index.html

no changes added to commit (use "git add" and/or "git commit -a")

Allt som har sammanslagninskonflikter och inte har lösts listas som icke-sammanslaget. Git lägger till standardmarkörer i filerna som har konflikter så att du kan öppna dem manuellt och lösa konflikterna. Din fil inehåller ett avsnitt som ser ut ungefär såhär:

<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
 please contact us at support@github.com
</div>
>>>>>>> iss53:index.html

Detta betyder att versionen i HEAD (din master-gren, fftersom det är den du hade utcheckad när du körde ditt sammanslagningskommando) är övre delen av blocket (allt över =======), medan versionen i grenen iss53 ser ut som i den nedre delen. För att lösa konflikten måste du antingen välja en sida eller slå samman innehållet själv. Du kan till exempel lösa konflikten genom att ersätta hela blocket med detta:

<div id="footer">
please contact us at email.support@github.com
</div>

Denna lösningen har lite av båda delar, och raderna med <<<<<<<, =======, och >>>>>>> har tagits bort helt. Efter att du löst varenda av dessa delar i en fil med konflikter så kör du git add på varje fil för att markera den som löst. Att preparera filen markerar den som löst i Git.

Om du vill använda ett grafiskt verktyg för att lösa dessa problem kan du köra git mergetool som plockar upp ett lämpligt visuellt sammanslagningsverktyg och hjälper dig igenom konflikterna:

$ git mergetool

This message is displayed because 'merge.tool' is not configured.
See 'git mergetool --tool-help' or 'git help config' for more details.
'git mergetool' will now attempt to use one of the following tools:
opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge p4merge araxis bc3 codecompare vimdiff emerge
Merging:
index.html

Normal merge conflict for 'index.html':
  {local}: modified file
  {remote}: modified file
Hit return to start merge resolution tool (opendiff):

Om du vill använda ett annat verktyg än vad som är standard (Git valde opendiff i detta fallet eftersom kommandot kördes på en Mac), så kan du se alla stödda verktyg längst up efter “one of the following tools.” Ange bara namnet av verktyget du hellre använder.

Notera

Om du behöver mer avancerade verktyg för att lösa upp kluriga sammanslagninskonflikter, går vi igenom mer sammanslagning i Advanced Merging.

Efter att du avslutat verktyget frågar Git om sammanslagninen var lyckad. Om du säger att den var det, prepareras filen för att markera den som löst. Du kan köra git status igen för att verifiera att alla konflikter har blivit lösta:

$ git status
On branch master
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:

    modified:   index.html

Om du är nöjd med det och har verifierat att alla konflikter har preparerats kan du skriva git commit för att slutföra din sammanslagningsversion. Standardmeddelandet för en sammanslagninsversion ser ut något likt detta:

Merge branch 'iss53'

Conflicts:
    index.html
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
#	.git/MERGE_HEAD
# and try again.


# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# All conflicts fixed but you are still merging.
#
# Changes to be committed:
#	modified:   index.html
#

Om du tycker att det vore hjälpsamt för andra att kolla på denna sammanslagningen i framtiden kan du modifiera meddelandet med detaljer kring hur du löste sammanslagninskonflikterna och förklara varför du gjorde ändringar om dessa inte är uppenbara.

scroll-to-top