Göm meny
Gäller för: VT24

Versionshantering med Git

Materialet nedan är under arbete och att betrakta som preliminärt.

Versionshanteringssystem

Ett versionshanteringssystem (VCS, Version Control System) är vad det låter som; ett system för att hantera olika versioner av filer. Syftet är dels att göra det lätt att prova lösningar, ångra sig, backa tillbaka till tidigare versioner, etc.

Versionshantering används också för att underlätta utvecklingsprocessen i projekt där flera (ibland hundratals) programmerare arbetar med samma kodbas. Man vill se till att utvecklare kan arbeta parallellt samtidigt som deras ändringar inte påverkar varandra samt att på ett smidigt sätt kunna slå ihop ändringarna när de är klara till ett fungerande program.

Tidigare versionshanteringssystem som t.ex. CVS eller SVN (Subversion) byggde på att det fanns ett centralt repository från vilket användare hämtade (check out) filer som de skulle jobba med och sedan skickade in (commit) när ändringen var genomförd.

Dagens versionshanteringssystem, som Git och Mercurial, är distribuerade, d.v.s. att det inte behöver finns en central nod som alla hämtar och lämnar filer till. Med det sagt så används oftast någon form av central nod trots det. Idag använder nästan 94 % av enskilda programmerare Git (enligt en undersökning på StackOverflow) men i industrin förekommer andra alternativ.

En nackdel med git är att binärfiler som bilder, video, musik, 3D-modeller, etc. inte kan versionshanteras på ett effektivt sätt. Det har inneburit att t.ex. spelindustrin valt att använda andra system som exempelvis Perforce istället.

Centrala begrepp i Git

  • git add: Alla filer i vår working directory behöver inte versionshanteras. För att en fil ska versionshanteras behöver vi berätta det för git. Detta gör vi genom att använda kommandot git add <fil/er>.
  • branch: en utvecklingsgren. I git kan vi snabbt växla mellan olika branches i vår arbetskatalog. Branches används t.ex. för att skilja på vad som är en släppt version av koden och vad som är under utveckling. Typisk används också branches av utvecklare för att jobba på en förbättring av koden lokalt. När man tror att man är klar så pushar man upp sin branch och gör en merge-request, dvs säger “nu är jag klar med min utveckling, här är filerna, kan du kolla igenom och godkänna ihopslagning med master”.
  • checkout: Vi använder git checkout för att hämta ut eller byta till en specifik branch eller commit från repot.
  • clone: Vi använder git kommandot git clone för att
  • commit: commit används både som verb “vi gör en commit” och som substantiv för att referera till en specifik commit i repot. I Git och många andra DVCS:er hanteras versioner inte på fil-nivå, utan på repo-nivå. Vi checkar ut en specifik commit och vi får ut alla filer. När vi commit:ar något väljer vi en eller fler filer som ska ingå i den commit:en. När vi gör en commit säger vi alltså endast vilka filer som ändrats, men när vi checkar ut en specifik commit får vi alla filer som de var just då, inte bara de som ingick i själva commit:en.
  • GitHub, GitLab: Både GitHub är webbaserad tjänst som låter dig lägga upp dina repon på webben. Integrerat finns bl.a. ärendehantering (issue-tracking), wiki för dokumentation m.m. GitLab är ett exempel på en liknande webbaserad tjänst. GitLab är open source och LiU har en egen GitLab-server som ni kan använda: https://gitlab.liu.se
  • merge: Vi kan slå ihop t.ex. olika commits (som t.ex. gjorts av olika användare på olika ställen) eller olika branches (t.ex. vår utvecklingsbranch, som ofta döps till dev med master)
  • remote/remote repository: Hänvisar till ett repo som inte är lokalt. Vi kan lägga till flera remotes till lokalt git-repo, men många gånger har man bara ett.
  • repository: Många säger “repo” på både svenska och engelska. Ett repo är en katalog med filer och eventuellt underkataloger som utgör vår kodbas kombinerat med information om vilka branches och versioner som finns och deras historik.
  • working directory: Utcheckad katalog som innehåller filer för en specifik version av repot.

Flöde i git

När en utvecklare ska implementera en ny funktion börjar hen med att hämta den senaste versionen av projektet från något centralt repository på t.ex. LiU’s GitLab. Utvecklaren gör sina ändringar och när hen är klar skapar hen en commit som innehåller alla ändringar. Den nya commiten slås ihop med det som tidigare finns uppladdat på GitLab som i sin tur får den nya funktionaliteten. Varje utvecklare har en egen kopia av hela projektet lokalt på sin dator som man manuellt uppdaterar under processen.

Man vill synkronisera ändringar mellan flera utvecklare och datorer och då brukar en onlinetjänst användas som en central server. För att kommunicera med servern “pushas” och “pullas” kod till och från servern.

Nedan följer ett exempel på hur flödet kan se ut:

Exempel

Exempel på typisk användning av Git med en onlinetjänst

Nedanstående är ett exempel. Git är väldigt flexibelt och det går att göra saker på väldigt många olika sätt. För att göra texten mer lättläst låtsas vi att detta är det “enda” sättet, men det är det absolut inte.

Man kan också jobba helt lokalt, dvs utan ett remote repo. Vi kan då jobba med versioner på vår lokala dator, skapa branches etc utan att ha något i “molnet”. Läs exemplet som en översiktlig och orienterande beskrivning av ett arbetsflöde. Mer information om kommandon etc. får du läsa i andra resurser.

Vi skapar ett repo på GitLab och klonar sedan det till vår egna dator med git clone så att vi har en lokal kopia av repositoryt. När vi klonar repot checkar vi även automatiskt ut branchen master. Den klonade katalogen är egentligen vår arbetskatalog (working directory) och själva repot finns i mappen .git som återfinnes i arbetskatalogen.

Vi redigerar vår kod i filer som läggs i arbetskatalogen. När vi vill spara en version av systemet berättar vi för git vilka filer som ska läggas till vår commit med git add och sedan gör vi en commit med git commit.

För att vår kollega ska kunna ta del av den version jag precis committat, gör vi en git push till repot på GitLab, vårt remote repository. Vår kollega kör sedan en git pull hos sig för att hämta våra ändringar. Git klarar av att slå ihop ändringar som gjorts remote och lokalt, men ibland går det inte och då får man en konflikt som man behöver lösa genom att säga exakt hur filen ska se ut. För att undvika detta är det bäst att kommunicera med varandra och säga vilka filer man jobbar i.

Kom igång med Git och SSH

Setup

Ni loggar in på GitLab med era liu-konton.

Först behöver ni skapa ett eget projekt. Följ gärna manualen:projekt i GitLab. Lägg även till labbassistenter och er labbpartner som members.

När ni har skapat projektet och lagt till teammedlemmar är det dags att hämta projektet lokalt. Detta görs med kommandot:

git clone <url>

Det finns två olika sätt att kommunicera med GitLab-servern, HTTPS och SSH. För HTTPS används inloggningsuppgifter varje gång man ska autentisera sig med servern medan SSH använder en SSH-nyckel som gör det automatiskt. SSH är ett smidigare alternativ men kan ta lite längre tid i början när man ska skapa en ny SSH-nyckel.

SSH

För att autentisera sig via SSH behöver man skapa en SSH-nyckel och dela den med GitLab-servern så att den är associerad med ditt konto.

Mer info om hur man skapar SSH-nycklar finner ni på: SSH.

Välj sedan alternativet SSH när du hämtar URLen som ska användas till git clone.

Om git säger “Agent admitted failure to sign using the key” när man klonar med SSH måste man följa instruktionerna här.

HTTPS

Du behöver inte göra någon setup för HTTPS utan kan istället använda HTTPS-urlen med git clone.

Användbara kommandon

git clone

Används främst i början när man skapar ett projekt på GitLab och vill hämta en kopia lokalt. Url:en är hämtad från GitLab och kan antingen vara SSH eller HTTPS.

git clone <url>

git pull

Används för att hämta de senaste ändringarna från ett git repository på någon annan plats. I vårt fall är det nästan alltid GitLab-servern. Främst för att undvika att man arbetar med en äldre version av koden.

git pull

git status

Används för att visa en lista av alla filer som ändrats/ej ändrats lokalt. Kommandot git status håller koll på vilka filer som ändrats, vilka ändringar som lagts till med git add och därför kommer sparas vid git commit.

git status

git add

Används för att lägga till (eng. stage) filer med ändringar till en kommande commit. Det fungerar även att lägga till en katalog eller sökväg som argument.

git add <filename>

Om man vill lägga till alla filer och underkataloger med ändringar i en katalog används kommandot:

git add *

git commit -m

Används för att spara de ändringar man lagt till med git add. Flaggan -m indikerar att man vill spara ett meddelande tillsammans med ändringarna, normalt sett en kortfattad sammanfattning över vilka ändringar som gjorts.

git commit -m "Skriv meddelande här" 

git push

Skickar alla commits man gjort lokalt till ett git repository på någon annan plats, dvs remote. I vårt fall är det i princip alltid GitLab-servern. Används efter git commit.

git push

Vanliga uppgifter som utförs med git

Återställa nuvarande version till en tidigare commit.

För att se ens commit-historik används kommandot git log --oneline. När kommandot utförs radas alla tidigare commits upp och där kan man ta ett beslut om vilken specifik commit man vill återställa nuvarande version till. Därefter används kommandot git reset som kan användas på bland annat dessa två sätt:

git reset current~2 

Använder ett relativt värde innan nuvarande tagg (i git –onelog listan), i detta fall -2.

git reset 9ef3453

Gå till en specifik commit oberoende av hur många versioner bakåt den ligger.

Om vi sedan använder kommandot git log --oneline, kommer commiten vi ändrade till att vara nuvarande.

Återställa en ändrad men ej commitad fil till den senaste commitade versionen.

För att återställa en specifik fil till hur den såg ut i senaste commiten, dvs ta bort alla ändringar vi gjort sedan senaste commit, används:

git checkout <sökväg till fil>

Merge

En merge-konflikt uppstår när ändringar gjorts från olika enheter och git kan ej avgöra vilken version som är aktuell. Om två commits från olika enheter försöker ändra samma kodsnutt sker en merge-konflikt och du behöver manuellt titta på filerna och bestämma vilken version som ska behållas. För att förebygga konflikter kan man alltid börja med att ta ner den senaste versionen som finns på git med hjälp av git pull.

Mer information om git finner ni på: git dokumentation.


Sidansvarig: Johan Falkenjack
Senast uppdaterad: 2023-12-04