Förtydliga gits strategi att hantera "content" istället för "filer". Lägg in exempel med fler än en fil.

Vad är GIT?

redigera

GIT är ett distribuerat versionshanteringssystem skapat av Linus Torvalds för användning i Linux-projektet. TODO.....

Grundläggande revisionshantering

redigera

Den här guiden går igenom hur man skapar ett GIT-repository och börjar arbeta med det. För att ha något konkret att versionshantera skriver vi en simpel applikation i programmeringsspråket Python. Du behöver dock inte kunna programmera vare sig Python eller något annat språk för att följa guiden.

Installera GIT

redigera

Om du kör någon variant av GNU/Linux finns GIT med stor sannolikhet färdigpaketerat till din distribution. Om distributionen är baserad på Debian så installerar du GIT genom att skriva in följande i en terminal

sudo apt-get install git-core

Kör du Windows så finns GIT paketerat till Cygwin.

Ställ in dina användaruppgifter

redigera

Mata in följande i en terminal där du ersätter namnet och e-mailen med dina egna uppgifter.

git config --global user.name "Inge Glid"
git config --global user.email ingeglid@nosuchmail.com

Nu kommer dina historikposter visa ditt namn och din e-mail, det underlättar för andra utvecklare som kan tänkas vilja kontakta dig.

Skapa en ny tom GIT-repository

redigera

Öppna en terminal (starta Cygwin ifall du kör Window) och skriv in de kommandon som inte börjar med "#". "#"-raderna är kommentarer och finns bara till för att förklara vad varje enskilt kommando gör.

# skapa en tom mapp
mkdir hellopy
# gå till den nya mappen
cd hellopy
# initiera ett tomt GIT-repo
git init

GIT svarar

Initialized empty Git repository in .git/

Lägg till en ny fil "hello.py" och skapa en historikpost

redigera

Låt oss nu skapa ett minimalt Python-program som skriver ut texten "Hello" på skärmen. Nedan används texteditorn "GEdit" som finns installerad på många GNU/Linux-system, men kör du tex. Windows så fungerar "notepad" lika bra

# Öppna en texteditor
gedit hello.py

Skriv in följande text i editorn

#!/usr/bin/python
print "Hello"

Spara filen och stäng editorn. Återgå till terminalfönstret. Nu ska vi se vad som hänt i vår mappstruktur

# Kolla nuvarande status
git status

GIT svarar

# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#	hello.py
nothing added to commit but untracked files present (use "git add" to track)

GIT har här upptäckt en fil som inte är versionshanterad. Om vi vill att GIT ska börja versionshantera filen så får vi lägga till den med kommandot

# lägg till hello.py
git add hello.py

Nu vill vi spara det nuvarande läget för hello.py i GIT:s historik. Då gör vi en "commit" enligt

# spara läget för de tillagda filerna som en historikpost ("commit")
git commit -m "Vår första commit"

GIT svarar

Created initial commit db16eeb: Vår första commit
1 files changed, 2 insertions(+), 0 deletions(-)
create mode 100644 hello.py

Kör vi nu återigen "git status" så svarar GIT

# On branch master
nothing to commit (working directory clean)

Vilket glädjande betyder att vi inte glömt att versionhantera någon fil i trädet och att alla ändringar finns sparade i en historikpost ("commit").

I texten som följer arbetar vi av pedagogiska skäl bara med en fil "hello.py", men det går givetvis att lägga till hur många filer som helst och arbeta med dem på samma sätt.

Gör en ändring i "hello.py" och spara den som en ny historikpost

redigera

Låt oss utvidga vårt program till att fråga efter namnet på användaren på användaren och därefter hälsa på honom/henne.

# Öppna hello.py
gedit hello.py

Utvidga programmet till följande

 #!/usr/bin/python

 name = raw_input("What is your name? ")
 print "Hello " + name

Spara filen och stäng editorn. Nu ska vi se vad som hänt i repon genom att köra

# Kolla statusen
git status

GIT svarar

# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#	modified:   hello.py
#
no changes added to commit (use "git add" and/or "git commit -a")

Det visar att GIT upptäckt en ny ändring i "hello.py". Vill vi se exakt vad som ändrats i "hello.py" kan vi köra följande

# se vad som ändrats i "hello.py" sedan senaste commiten
git diff

GIT svarar

--- a/hello.py
+++ b/hello.py
@@ -1,2 +1,5 @@
 #!/usr/bin/python
-print "Hello"
+
+name = raw_input("What is your name? ")
+print "Hello " + name
+

Ändringen visas som en "diff". Dessa kan till en början vara lite krytiska att läsa men man lär sig ganska snart tolka dem. Raderna med "+" berättar vad som lagts till och rader som börjar med "-" visar vad som raderats. Här har den ursprungliga print-raden tagits bort och ersatts med fyra andra rader

Låt oss göra en "commit" enligt

# spara nuvarande läget som en historikpost
git commit -m "Nu frågar programmet efter användarens namn och skrivet ut en hälsning"

GIT svarar

# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#	modified:   hello.py
#
no changes added to commit (use "git add" and/or "git commit -a")

Hm, vi har visst glömt en sak. Vi måste lägga till hello.py innan vi kan göra en commit. Vill vi göra tilläggningen och commiten i ett steg så kan vi använda "-a" flaggan till "commit".

# lägg till alla sedan tidigare versionhanterade filer och gör en commit
git commit -a -m "Nu frågar programmet efter användarens namn och skrivet ut en hälsning"

GIT svarar

Created commit 2adc044: Nu frågar programmet efter användarens namn och skrivet ut en hälsning
1 files changed, 4 insertions(+), 1 deletions(-)

Lista historiken

redigera

Vill man se vilka commits som gjorts kan man lista historiken med följande kommando

git log

GIT svarar

commit 2adc0443929fe09111a37ab66573db20415985f0
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 15 15:59:43 2008 +0200
    Nu frågar programmet efter användarens namn och skrivet ut en hälsning
commit db16eeb9c48d0b67e496e59fbaca154d0ffc02fb
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 15 15:32:10 2008 +0200
    Vår första commit

Den långa hex-strängen efter "commit" är en kontrollsumma beräknad med den krypografiska hashtekniken SHA-1 och beräkningen sker baserat på alla tidigare commits. Många andra versionhanteringssystem nöjer sig med ett simpelt löpnummer, så varför krånglar GIT till det? SHA-1 identifieringen gör det möjligt att upptäcka ifall historiken blivit korrupt, kanske pga. en trasig hårddisk, ett trasigt RAM-minne eller att någon illvillig användare försökt förändra innehållet. Det gör GIT till ett robust och säkert sätt att versionhantera sina projekt med.

Ångra en ocommitad ändring

redigera

Låt oss göra en ändring i programmet. Öppna hello.py och ändra texten till

 #!/usr/bin/python

 name = raw_input("What is your name? ")
 print "Hello " + name

 print "It was nice to see you"

Vi sparar filen och märker sedan att vi vill ångra denna ändring. I det här trivala fallet är det trivalt att bara öppna filen och återställa innehållet (då kommer förresten "git log" rapportera att ingenting förändrats) men nu simulerar vi att vi gjort många felaktiga ändringar i en större kodbas.

Vi kör då kommandot

git reset --hard

GIT svarar

HEAD is now at 2adc044 Nu frågar programmet efter användarens namn och skrivet ut en hälsning

GIT har nu tagit bort alla ändringar vi inte commitat. Med andra ord är vi tillbaka i det läget vi hade vid den förra commiten (dock bara för de filer som vi versionhanterar).

För att kontrollera att det fungerar öppnar vi hello.py. Innehållet är som väntat

 #!/usr/bin/python

 name = raw_input("What is your name? ")
 print "Hello " + name

Ångra en commitad ändring (eller flera)

redigera

Låt oss göra en ny "felaktig" ändring som vi dessutom är dumma nog att commita

Ändra hello.py till

#!/usr/bin/python

print "Hello " + name

Spara och commita enligt

git commit -a -m "Dum dum, tog bort inmatningsraden"

En logvisning enligt

git log

ger

commit 5c93f3b3b63eb50bf94dc90bcd9b4cec1e1831d2
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Mon Jun 16 00:46:45 2008 +0200

    Dum dum, tog bort inmatningsraden

commit 2adc0443929fe09111a37ab66573db20415985f0
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 15 15:59:43 2008 +0200

    Nu frågar programmet efter användarens namn och skrivet ut en hälsning

commit db16eeb9c48d0b67e496e59fbaca154d0ffc02fb
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 15 15:32:10 2008 +0200

    Vår första commit

Nu vill vi gå tillbaka till läget vid den föregående commiten, då kan vi köra

git revert --no-edit -r HEAD~0

Siffran (i det här fallet "0") anger vilken commit relativt den senaste som vi vill upphäva effekten av. Ett högre tal hade backat längre bak i historiken. I just det här fallet är "-r HEAD~0" överflödigt eftersom "0" är förvalt men jag visar hur man gjort i det generella fallet. Om revisionen ligger långt bak och det är besvärligt att ta reda på hur många steg vi vill upphäva så kan man ange (minst) de fyra första siffrorna i SHA-1-summan istället. I det här fallet hade med andra ord

git revert --no-edit

eller

git revert --no-edit -r 5c93

fungerat lika bra

Som svar på något av kommandoalternativen ovan ger GIT

Finished one revert.
Created commit feed231: Revert "Dum dum, tog bort inmatningsraden"
 1 files changed, 1 insertions(+), 0 deletions(-)

Visar vi historiken ger GIT

Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Mon Jun 16 00:55:14 2008 +0200

    Revert "Dum dum, tog bort inmatningsraden"

    This reverts commit 5c93f3b3b63eb50bf94dc90bcd9b4cec1e1831d2.

commit 5c93f3b3b63eb50bf94dc90bcd9b4cec1e1831d2
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Mon Jun 16 00:46:45 2008 +0200

    Dum dum, tog bort inmatningsraden

commit 2adc0443929fe09111a37ab66573db20415985f0
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 15 15:59:43 2008 +0200

    Nu frågar programmet efter användarens namn och skrivet ut en hälsning

commit db16eeb9c48d0b67e496e59fbaca154d0ffc02fb
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 15 15:32:10 2008 +0200

    Vår första commit

Loggen är tydlig men kanske inte så snygg. Vi vill kanske inte visa vårt misstag utan helt enkelt ta bort historiken efter revision "2adc". Då kan vi återigen bruka "git reset --hard" enligt

git reset --hard 2adc

GIT ger

HEAD is now at 2adc044 Nu frågar programmet efter användarens namn och skrivet ut en hälsning

Var dock riktigt försiktig med detta kommando! Det raderar all historik efter den angivna revisionen och det som tas bort går INTE att återställa.

Historiken visar nu

commit 2adc0443929fe09111a37ab66573db20415985f0
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 15 15:59:43 2008 +0200

    Nu frågar programmet efter användarens namn och skrivet ut en hälsning

commit db16eeb9c48d0b67e496e59fbaca154d0ffc02fb
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 15 15:32:10 2008 +0200

    Vår första commit

Jämför ändringar gjorda mellan commits

redigera

Om vi bara kör

git diff

visas vilka ändringar som ännu inte commitats

Vill vi se skillnaden mellan den nuvarande och en föregående revision kör vi tex.

git diff -r db16

eller enklast

git diff -r HEAD~1

vilket ger

diff --git a/hello.py b/hello.py
index ba529fa..d7632c8 100644
--- a/hello.py
+++ b/hello.py
@@ -1,2 +1,5 @@
 #!/usr/bin/python
-print "Hello"
+
+name = raw_input("What is your name? ")
+print "Hello " + name
+

I det mer generella fallet vill vi ange de två revisioner som vi vill jämföra. I det här fallet har vi bara två stycken i loggen så

git diff -r 2adc -r db16

ger samma utdata som ovan. Vill man kan man vända på argumenten och få ett inverterat resultat enligt

diff --git a/hello.py b/hello.py
index d7632c8..ba529fa 100644
--- a/hello.py
+++ b/hello.py
@@ -1,5 +1,2 @@
 #!/usr/bin/python
-
-name = raw_input("What is your name? ")
-print "Hello " + name
-
+print "Hello"

Arbeta med branches

redigera

GIT gör det väldigt enkelt att skapa och hantera olika branches. En branch är ett parallellt utvecklingsspår och varje branch har sin egen revisionshistoria. Det finns många sätt att använda branches men det vanligaste är att man skapar en branch när man vill utveckla någon experimentell funktion eller göra en ändring som gör projektet "instabilt" under en viss tid. Då vill man fortfarande att andra utvecklare ska kunna hämta ens stabila version och då är det lämpligt att man lägger de två spåren i olika brancher

När man kör "git init" skapar GIT ett repository med en branch "master". Det är den branchen som är tänkt att vara det stabila och publika spåret, så helst ska man inte utveckla på "master" utan jobba på en annan branch och efter hand som funktioner mognar "merga" (smälta samman) de delarna in i "master". merging och branching kan göras mellan vilka brancher man vill, hur ofta eller hur sällan man så vill.

GIT kan hålla flera brancher i samman träd, inte bara i olika mappar såsom andra versionshanteringssystem. Detta spar både uttrymme (eftersom omodifierande delar i olika brancher med samma ursprung delar data) och gör merging och branching väldigt effektivt.

Skapa en ny experimentell branch

redigera

Anta att vi vill utvidga programmet till att säga hej till ett flertal personer i följd. Då kan vi börja skriva ändringen enligt

#!/usr/bin/python

name = "start"
while name!="":
  name = raw_input("What is your name? ")
  print "Hello " + name
git status 

ger

# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#	modified:   hello.py
#

och visar att hello.py modifierats

Vi vill inte lägga in ändringen i vår stabila branch "master" utan vi skapar en ny branch "expmultiname" baserat på den nuvarande enligt

git branch expmultiname

Vi byter till den nya branchen med

git checkout expmultiname 

GIT svarar

M	hello.py
Switched to branch "expmultiname"

Båda operationerna göras med snabbkommandot

git checkout -b expmultiname

Den första raden "M hello.py" berättar att hello.py är "dirty" (smutsig), dvs den har ändringar som ännu inte commitats. Observera något väldigt viktigt - ocommitade ändringar reser mellan brancher när man byter mellan dem. I det här fallet är det inga problem då vi påbörjade ändringen innan vi bytte. Vi ville att det vi inte commitat skulle appliceras på den nya branchen, för det är just det som händer. Vill man inte att det ska inträffa kan det lösas genom att man commitar innan man byter branch eller så kan man använda "git stash" som vi återkommer till.

För att lista vilka brancher som finns kör vi

git branch

GIT svarar

* expmultiname
  master

Låt oss commita ändringen enligt

git commit -a -m "Hälsar och frågar nu i en loop"

GIT svarar

Created commit f6c4c95: Hälsar och frågar nu i en loop
 1 files changed, 4 insertions(+), 2 deletions(-)
 mode change 100644 => 100755 hello.py

Kör vi nu

git status

vilket visar

# On branch expmultiname
nothing to commit (working directory clean)

ser vi att vi trädet är "rent", dvs inget ocommitat finns i trädet

Kör vi programmet märker vi att det fungerar fram till att vi ger en tomrad som input. Då avslutar programmet (vilket vi vill), men det lämnar en utskrift "Hello " på en ny rad och det vill vi undvika. Vi fixar buggen genom att skriva om programmet enligt

#!/usr/bin/python

while 1:
  name = raw_input("What is your name? ")
  if name == "": break
  print "Hello " + name

Vi kan köra "git status" och/eller "git diff" för att se vad som hänt. Det är en god vana att alltid göra det innan man gör en commit, annars kan man råka commita saker man inte riktigt räknat med. Nåväl, i det här fallet vet vi vad vi gjort så vi kör en commit

git commit -a -m "Fixade bug som skrev ut Hello när programmet avslutades"

En logvisning ger som förväntat

commit 043e861919d386499a7c0df8a847352f8c8b36b5
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Mon Jun 16 03:19:25 2008 +0200

    Fixade bug som skrev ut Hello när programmet avslutades

commit f6c4c95a4a66c600cfdea15c0696199a9482d99e
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Mon Jun 16 03:06:05 2008 +0200

    Hälsar och frågar nu i en loop

commit 2adc0443929fe09111a37ab66573db20415985f0
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 15 15:59:43 2008 +0200

    Nu frågar programmet efter användarens namn och skrivet ut en hälsning

commit db16eeb9c48d0b67e496e59fbaca154d0ffc02fb
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 15 15:32:10 2008 +0200

    Vår första commit

Applicera ändringar i den experimentella branchen på den ursprungliga branchen

redigera

Låt oss byta tillbaka till vår "master"-branch. Innan vi byter kör vi

git status

vilket ger

# On branch expmultiname
nothing to commit (working directory clean)

dvs vi kan byta branch utan att riskera att någon ocommitad ändring "liftar med" i branchbytet.

Bytet görs med

git checkout master

Tittar vi på loggen

commit 2adc0443929fe09111a37ab66573db20415985f0
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 15 15:59:43 2008 +0200
    Nu frågar programmet efter användarens namn och skrivet ut en hälsning
commit db16eeb9c48d0b67e496e59fbaca154d0ffc02fb
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 15 15:32:10 2008 +0200
    Vår första commit

ser vi att vi ännu inte påverkat master-branchen med vår loop-konstruktion.

Nu ska vi göra någonting kul, merga in ändringarna ifrån vår experimentella branch till master-branchen. Vi kör

git pull . expmultiname

Punkten talar om att vi ska merga med en branch ifrån samma filträd

GIT svarar

Updating 2adc044..043e861
Fast forward
 hello.py |    6 ++++--
 1 files changed, 4 insertions(+), 2 deletions(-)
 mode change 100644 => 100755 hello.py

Merging verkar ha gått bra, så låt oss kika på loggen

commit 043e861919d386499a7c0df8a847352f8c8b36b5
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Mon Jun 16 03:19:25 2008 +0200

    Fixade bug som skrev ut Hello när programmet avslutades

commit f6c4c95a4a66c600cfdea15c0696199a9482d99e
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Mon Jun 16 03:06:05 2008 +0200

    Hälsar och frågar nu i en loop

commit 2adc0443929fe09111a37ab66573db20415985f0
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 15 15:59:43 2008 +0200

    Nu frågar programmet efter användarens namn och skrivet ut en hälsning

commit db16eeb9c48d0b67e496e59fbaca154d0ffc02fb
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 15 15:32:10 2008 +0200

    Vår första commit

och därefter på koden

cat hello.py

vilket skriver ut hello.py på skärmen

#!/usr/bin/python

while 1:
  name = raw_input("What is your name? ")
  if name == "": break
  print "Hello " + name

Perfekt! Låt oss nu bara köra en statuskontroll för att se att allt är okej

 
git status

vi får

# On branch master
nothing to commit (working directory clean)

och ser att vi har ett rent träd

Ta bort den experimentella branchen

redigera

När ändringarna är mergade in i master finns det ingen direkt anledning att behålla branchen expmultiname. Vi raderar den enkelt genom

git branch -D expmultiname

GIT svarar

Deleted branch expmultiname.

Mer avancerat arbete med branches

redigera

Göm undan ocommitade ändringar (stashing)

redigera

Säg att du skrivit lite kod som du ännu inte commitat. Du känner dock att du skulle vilja göra en liten ändring/bugfix på ett annat ställe i koden och commita den ändringen innan din stora ändring. Är det i olika filer så kan du enkelt commita olika filer i olika omgångar, men är det samma fil den lilla och stora ändringen görs i så fungerar inte den metoden. När du kodat en bit på den stora ändringen och känner för att göra den lilla så kör

git stash

Då "pushas din ändring", indexet ger skenet av att ha rensats och du är tillbaka vid din senaste commit. Nu kan du göra din lilla ändring, spara ändringen och göra en commit

git commit -a -m "did small change"

För att "poppa fram" din stora ändring och applicera den på din lilla ändring kör

git stash apply

"stash"-operationer går att stacka (så du kan trixa ännu mer om du vill) så för att undvika överraskningar i framtiden rensar vi stacken enligt

git stash clear

Dessa båda kommandon kan utföra samtidigt genom kommandot

git stash pop

"stash" är också användbart när man vill byta mellan brancher i samma träd utan att commita först (det smutsiga indexet följer annars med när man gör checkout).

Körsbärsplockning

redigera

Säg att du skapat en ny branch och gjort en serie ändringar. I vårt fall har vi branchen "exp_more_questions" där vi gjort två commits, en ändring som frågar efter personens ålder och en som frågar efter var personen bor. Det resulterande programmet ser ut enligt

#!/usr/bin/python

while 1:
  name = raw_input("What is your name? ")
  if name == "": break
  print "Hello " + name
  age = raw_input("What is your age? ")
  print age + " ... hey, that's not very old"
  city = raw_input("Where do you live? ")
  print city + " is really a nice place"

Loggen ser ut enligt

commit f374134012fe631863ef582b7b6ff7d57491224e
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 29 17:49:13 2008 +0200

    added city question

commit d26e573707da7d39528bd5b1ec59d114420327c3
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 29 17:47:05 2008 +0200

    added age question

commit 043e861919d386499a7c0df8a847352f8c8b36b5
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Mon Jun 16 03:19:25 2008 +0200

    Fixade bug som skrev ut Hello när programmet avslutades

commit f6c4c95a4a66c600cfdea15c0696199a9482d99e
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Mon Jun 16 03:06:05 2008 +0200

    Hälsar och frågar nu i en loop

commit 2adc0443929fe09111a37ab66573db20415985f0
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 15 15:59:43 2008 +0200

    Nu frågar programmet efter användarens namn och skrivet ut en hälsning

commit db16eeb9c48d0b67e496e59fbaca154d0ffc02fb
Author: Inge Glid <ingeglid@nosuchmail.com>
Date:   Sun Jun 15 15:32:10 2008 +0200

    Vår första commit

Nu vill vi återföra modifikationerna till huvudbranchen, dock vill vi inte ha med ändringen som ger en fråga om åldern (det anses ofta vara en oartig fråga) och bara behålla stadsfrågan. I ett sådant litet program skulle vi mycket väl kunna göra pull på hela programmet och sedan manuellt radera åldersfrågan men GIT tillhandahåller en kraftfullare lösning: "cherry-picking". Det låter användaren plocka ändringar ifrån en viss commit och applicera dem på den aktuella branchen. För att applicera commit f374 på master kör vi

git checkout master
git cherry-pick f374

Nu svarar GIT

Auto-merged hello.py
CONFLICT (content): Merge conflict in hello.py
Automatic cherry-pick failed.  After resolving the conflicts,
mark the corrected paths with 'git add <paths>' or 'git rm <paths>' and commit the result.
When commiting, use the option '-c f374134' to retain authorship and message.

vi öppnar hello.py och ser vad konflikten gäller. hello.py ser ut enligt

#!/usr/bin/python

while 1:
  name = raw_input("What is your name? ")
  if name == "": break
  print "Hello " + name
<<<<<<< HEAD:hello.py
=======
  age = raw_input("What is your age? ")
  print age + " ... hey, that's not very old"
  city = raw_input("Where do you live? ")
  print city + " is really a nice place"
>>>>>>> f374134... added city question:hello.py

Anledningen till att GIT protesterar är att GIT tror att åldersfrågan är en nödvändig förändring till stadsfrågan, då den direkt ansluter till kodstycket. För att lösa konflikten tar vi manuellt bort den kod inom konfliktblocket vi inte vill ha (inklusive själva blockmarkeringarna). Resultatet ser ut enligt

#!/usr/bin/python

while 1:
  name = raw_input("What is your name? ")
  if name == "": break
  print "Hello " + name
  city = raw_input("Where do you live? ")
  print city + " is really a nice place"

Nu gör vi

git add hello.py
git commit --no-edit -c f374134

för att slutföra konfliktlösningen och commita resultatet.

Om vi istället velat behålla åldersfrågan så hade vi inte fått någon konflikt eftersom ingen annan ändring gjorts i anslutning till det aktuella kodstycket. Vi kan ändå prova hur det hade blivit genom att ta bort den senaste commiten med

git reset --hard 043e
git cherry-pick d26e

GIT svarar glatt

Finished one cherry-pick.
Created commit 8330687: added age question
 1 files changed, 2 insertions(+), 0 deletions(-)

Tittar vi på hello.py ges precis som väntat

#!/usr/bin/python

while 1:
  name = raw_input("What is your name? ")
  if name == "": break
  print "Hello " + name
  age = raw_input("What is your age? ")
  print age + " ... hey, that's not very old"

Vi raderar den tillfälliga branchen med

git branch -D exp_more_questions

Rebasing (historikomskrivning)

redigera

Rebase är ett mycket kraftfullt verktyg som kan användas på ändringar som BARA finns lokalt. Att det inte lämpar sig för publika ändringar beror på att rebase skriver om historiken och ändrar därigenom SHA-1:s för trädet. Huvudregeln är därför att bara rebasa inom ett intervall av commits som man ännu inte delgivit någon annan.

Interaktiv rebase

redigera

För att göra en interaktiv rebase anger vi ett SHA-1 som från den nuvarande commiten definierar det intervall vi vill "rebasa" inom.

git rebase -i db16eeb9c48d0b67

öppar upp en texteditor enligt

 pick f6c4c95 Hälsar och frågar nu i en loop
 pick 043e861 Fixade bug som skrev ut Hello när programmet avslutades
 pick 8330687 added age question
 pick d607386 added title

 # Rebase db16eeb..d607386 onto f6c4c95
 #
 # Commands:
 #  p, pick = use commit
 #  e, edit = use commit, but stop for amending
 #  s, squash = use commit, but meld into previous commit
 #
 # If you remove a line here THAT COMMIT WILL BE LOST.
 # However, if you remove everything, the rebase will be aborted.
 #

Här kan vi ändra ordningen på commits genom att flytta runt rader. Vi kan ta bort rader för att radera commits. Vi kan också ändra "pick" till "edit" för att röra oss tillbaka i historiken och ändra i någon commit, eller infoga nya commits mellan två existerande commits. Ändrar man "pick" till "squash" slår man ihop flera commits till en commit.

Man bör vara försiktig när man gör rebase. Gör man fel kan man förlora eller förstöra delar av historiken. Innan man gör en rabase är det därför lämpligt att skapa en ny branch som man säkert kan "leka i". Anta att vi jobbar i master

git checkout -b ny_branch_att_rebasa_i

Därefter gör man sin rebase. Går något galet så tar man bort resultatet och går tillbaka till master enligt

git checkout master
git branch -D ny_branch_att_rebasa_i

Om resultatet däremot blir bra kan man istället ta bort den gamla branchen och byta namn på sin nya branch

git branch -D master
git branch -m master

Pull rebase

redigera

Gör man "pull" från en server och man samtidigt har egna ändringar skapas en "merge" i historiken. Kör man ofta "pull" blir det väldigt många bågar och grenar i historiken vilket kan ses som rörigt och fult.

git pull --rebase

Löser problemet genom att först "lyfta av" dina egna ändringar från historiken, därefter fylla på från källan därifrån man hämtar ändringar och slutligen åter lägga på dina egna ändringar. Resultatet är en linjär historik. Kommandot kan köras hur många gånger som helst, men fungerar bara ifall dina ändringar ännu inte publicerats.