Versionshantering

Versionshantering är ett viktigt verktyg att kunna använda sig av som utvecklare, speciellt i projekt där man utvecklar flera personer tillsammans. Versionshanteringssystemets främsta uppgift är att se till så att olika utvecklare inte förstör varandras ändringar. Det finns flera olika system för versionshantering och det är givetvis en fördel att känna till många av dem. Exempel på andra system är: Git, Subversion, Perfoce, Team Foundation Server/source safe och CVS. Ibland länkas versionshanterinssystemet ihop med med system för projektdokumentation/hantering, t.ex. en wiki, issue-hantering, planeringsverktyg etc.

I kapitel 25 i kurslitteraturen hittar du vissa beröringspunkter med nedstående.

Introduktion till att arbeta med versionshantering

Git är ett versionshanteringssytem, som gör det möjligt att hantera ändringar av filer. Git hjälper till med att avgöra vad som ändrats, vem som ändrade och varför. Git är inte det enda versionshanteringssystemet, men ett av de absolut mest populära.

Grundläggande Git-kommandon

En förutsättning för att arbeta effektivt med versionshantering är att ha kännedom om de mest grundläggande Git-kommandona och flöden de ger upphov till.

Den lokala datorn har tre olika areor, working directory, staging area och local repository.

I working directory gör du alla ändringar, som att lägga till nya filer (märks som untracked), ändra i filer (märks som modified) och ta bort filer (märks som deleted). Genom att använda git status kan du få aktuell status för ditt working directory, och se om det finns ändringar som behöver läggas till staging area.

För att ändring som görs i working directory ska kunna skickas till local repository måste de först placeras i staging area, vilket du gör med git add ., som förberedelse inför en commit.

Local repository innehåller samtliga commits (och branches).

På en server återfinns det som kallas remote repository.

Simple Git Flow

git clone <repository>

Laddar ner ett repositorium till din dator. Som standard skapas en katalog, "working directory", med repositoriets namn, innehållande samtliga filer. Argumentet <repository> är en Git URL till ett "remote repository" och kallas "remote origin", platsen varifrån filerna ursprungligen hämtades.

git add

Efter att ändringar gjorts i "working directory" markerar detta kommando ändringar som gjorts som staged, innebärande att de är redo för en "commit". Med kommandot git status kan du kontrollera om det finns ändringar som är "staged" eller inte.

git commit -m '<message>'

Spara ändringar som finns i "staging area" lokalt i "local repository". Använder du växeln -a behöver du inte använda git add ., då ändringar då först hamnar i "staging area" för att sedan sparas lokalt.

OBS ! Använder du git commit -a -m '<message>' kommer inga nya filer att läggas i "staging area". För att nya filer ska placeras i "staging area" måste du använda git add.

git push

Laddar upp innehåll, "commits", i "local repository" till "remote repository".

git fetch

Laddar ner det senaste informationen från "remote repository" (som exempelvis alla "branches", till exempel "main branch"). Inga lokala filer i "working directory" ändras, enbart informationen i "local repository" uppdateras.

git merge <other-branch>

Tar samtliga "commits" från angiven "branch" och lägger till dem till aktuell "branch". "Working directory" uppdateras som ett resultat av kommandot. Det kan uppstå konflikter då Git inte kan avgöra vilka förändringar som ska väljas, varför du kan behöva hjälpa till med att välja vilken förändring som ska gälla.

git pull

Slår samman förändringar från "remote repository" med aktuell "branch". (Kommandot git pull är samma sak som git fetch följt av git merge FETCH_HEAD.)

Arbeta med Git

Distribuerade versionshanteringssystem som Git gör det möjligt för dig att dela och hantera kod (och andra nödvändiga resurser). För att arbetet med den hanterade koden ska fungera på ett bra sätt är det viktigt att det finns en arbetsgång att följa. Genom att följa en arbetsgång kan mindre tid ägnas åt versionshantering och mer tid åt att skriva kod.

Git erbjuder mer än hantering av förändringar av innehållet i ett "working directory". Möjlighet att, genom "branches", bedriva utveckling av olika delar av den hanterade koden samtidigt är central. Detta är en förutsättning främst då flera utvecklare arbetar parallellt med olika delar av den hanterade koden, men "branches" är även av intresse då du är en ensam utvecklare.

Det finns flera förslag till arbetsgångar, eller arbetsflöden, att följa, exempelvis Git Flow, GitHub Flow, GitLab Flow och Release Flow. De har sina egenheter, för- och nackdelar, och du kan bilda din egen uppfattning genom att studera och jämföra de olika arbetsflödena.

Exemplen ovan på arbetsflöden kan säkert i vissa avseenden upplevas som komplexa och svårarbetade. De är främst framtagna för att fungera väl då flera utvecklare samarbetar och bidrar med olika delar till den hanterade koden.

Det finns flera fördelar att arbeta utefter ett arbetsflöde även för den ensamma utvecklaren. Även om behovet att samarbeta med andra utvecklare inte finns så finns situationer då arbetet har förutsättningar att bli enklare. En ensam utvecklare kan till exempel behöva arbeta med flera olika delar av den hanterade koden parallellt, vilket kan bli svårt utan att ha ett bra arbetsflöde på plats.

Arbetsflöden bygger på "branches"

Oavsett typ av arbetsflöde så används "branches", varför denna typ av arbetsflöde ofta benämns "branch strategies".

Många projekt har en "backlog" med funktionalitet att implementera och buggar att åtgärda. Ett sätt att arbeta med kod rörande funktionalitet och åtgärd av buggar är att göra "commits" direkt till din "main branch". En kedja med "commits" skapas och nya "commits" läggs till efter varandra.

Main branch

Genom att, utöver "main branch", använda en "branch" för en funktionalitet, en "branch" för åtgärd av en bugg kan en ensam utvecklare arbeta parallellt med olika delar av den hanterade koden.

Multiple branches

Cirklarna representerar gjorda "commits". De olika färgerna indikerar olika "branches": "main", "bugFix" och "feature". Asterisken som avslutar "bugFix" i figuren indikerar att det är just denna "branch" som är aktiv. En "commit" som görs kommer att läggas till i slutet av den "branch"_ som är aktiv.

När arbetet i en "branch" är klart återstår det att slå samman koden med "main branch". Efter att en "branch" slagits samman med "main branch" kan den tas bort. Tas en "branch" bort, som slagits samman med en annan "branch", är det bara namnet som tas bort och inga av de "commits" som gjorts.

Merged branches

När en "branch" slås samman med en annan, skapas en "merge commit" (C11/C12) representerande själva sammanslagningen.

Git-kommandon för "branches"

Kommandot git branch listar existerande "branches" i "local repository". Asterisken visar vilken "branch" som är aktiv.

Kommandot git branch <new-branch-name> skapat en ny "branch" baserad på aktiv "branch". Aktiv "branch" ändras inte.

Med kommandot git checkout <branch-name> byter du aktiv "branch". Innehållet i "working directory" uppdateras för att överensstämma med innehållet i aktiverad "branch".

Kommandot git checkout -b <new-branch-name> skapar och byter aktiv "branch" till nyligen skapad "branch".

För att på den lokala datorn slå samman aktiv "branch" med en annan "branch" använder du kommandot git merge <other-branch>.

Vill du inte behålla en "branch" efter en sammanslagning kan du ta bort den med git branch -d <branch-to-delete>.

För att du ska kunna ta bort en "branch" måste den vara fullständigt sammanslagen med en annan "branch". Är en "branch" inte fullständigt sammanslagen och du vill ta bort den trots det kan du lägga till -f, kort för --force, för att tvinga Git att ta bort den.

Hantering av "commits" i samband med "merge"

När du slår samman en "branch" med en annan kan du välja att behålla alla individuella "commits", eller att slå samman alla "commits" till en enda "merge commit".

Lokalt på din dator kan du med kommandot git merge <other-branch> göra en fullständig sammanslagning och alla individuella "commits" behålls.

Merged branches

Vill du trycka ihop samtliga "commits" i en "branch" till en enda "commit", en "merge commit", kan du göra det med kommandot git merge --squash feature.

Merged branches

OBS! När du använder --squash uppdateras ditt "working directory" och "staging area" men någon "commit" skapas inte automatiskt, utan det får du göra själv.

OBS! När du använder --squash så räknas det inte som en fullständig sammanslagning varför du måste använda -f om du vill ta bort den "branch" som slagits samman.

Vilka effekter ger det att använda --squash vid git merge? Det kommer att endast finnas en "commit" i slutändan för att representera alla individuella "commits" i en "branch". Din "main branch" blir då renare utan alla "commits" som gjorts då den endast kommer att innehålla "merge commits" med sammanfattande "commit"-meddelanden. Att konsekvent behålla samtliga "commits" i samband med "merge" ger många förgreningar vilket kan ge en "main branch" som med tiden blir svår att överblicka. Detta i synnerhet om flera parallella "branches" används. En anna sak att ta i beaktande är att du kanske inte behöver lägga så mycket möda på att författa perfekta "commit"-meddelanden om du vet att de "commits" du gör i slutändan åndå kommer att ersättas med ett enda sammanfattande "commit"-meddelande.

OBS! Du behöver inte (ska inte?) använda "git merge" lokalt! Du kan istället använda GitLabs "merge reguest" och uppnå exakt samma sak. I den avslutande förslaget till arbetsflöde används just GitLab för att göra en "merge", fast då i "remote repository" istället för i "local repository".

Ett enkelt arbetsflöde

Bestäm dig för och håll dig till ett arbetsflöde, "branch strategy", som är enkelt att följa. Som utgångspunkt kan du ha följande punkter:

  • Se till att din "main branch" innehåller stabil kod av hög kvalitet.
  • Utgå alltid från "main branch" när en ny "branch" skapas.
    • Använda gärna prefix vid namngivning av olika "branches" för att enkelt kunna gruppera dem. En "branch" som lägger till ny funktionalitet för att logga in kan exempelvis ges namnet "feature/login". En "branch" som åtgärdar en bugg kan ges namnet "bugFix/login_error_status_code". (Det kan också vara en bra idé att använda eventuella "issue IDs" vid namngivningen.)
  • Ha som ambition att aldrig gör en "push" av en eller flera "commits" till din "main branch". Se istället till att använda "merge requests" för att slå samman kod från olika "branches" med din "main branch".

Under rubriken "Adding a new feature (or bug fix, or hot fix, or whatever)" hittar du ett förslag till ett arbetsflöde som bygger på ovanstående.


Adding a new feature (or bug fix, or hot fix, or whatever)

  1. Verify that the current branch is the main branch by showing all the branches, and switch to the main branch if necessary.
    • git branch
      If the main branch in the local branches' list is not starred, you need to switch to it.
    • git checkout main
  2. Create a new branch (feature/login, bugfix/login_error_status_code).
    • git branch feature/login
      This step and the next one could be combined into a single step
      git checkout -b feature/login
  3. Change to the new branch.
    • git checkout feature/login
  4. Repeat…
    1. Make changes in the working directory.
    2. Add the changes to the staging area.
      • git add .
    3. Commit the changes to the local repository.
      • git commit -m '<message>'
    4. (On occasion, maybe push the commits in the branch to the remote repository.)
      • git push --set-upstream origin feature/login
  5. …until the feature is implemented.
  6. Push the final commits in the branch to the remote repository.
    • git push
  7. At the remote repository (GitLab)
    1. Create and submit a merge request.
      • Delete the source branch when the merge request is accepted?
      • Squash commits when the merge request is accepted?
        • Add a new commit message for the squashed commits.
    2. Accept the merge request.
      • The last chance to decide whether to delete the branch.
      • The last chance to decide whether to squash the commits.
  8. Go back to the local repository.
  9. Change to the main branch.
    • git checkout main
  10. Ge the changes from the remote repository.
    • git pull
  11. Update the remotes?
    • git remote update origin
  12. Remove local branches?
    • git branch -d feature/login
      or
      git branch -d -f feature/login
      or the short version
      git branch -D feature/login