DAT055 Objektorienterade applikationer Övning GIT Andreas Wieden andreas.wieden@chalmers.se Introduktion Denna övningen är avsedd att ge en grunläggande inblick i versionshanteringssystemet Git. Vi kommer att gå igenom hur man arbetar i Git med hjälp av kommandon i en terminalmiljö. Detta har fördelen att man får en bättre överblick och kontroll. Samtidigt underlättar det också förståelsen för vad man egentligen gör om man senare väljer att arbeta med grafiska användarmiljöer vid versionshantering. GitBash och terminalmiljön Då denna övning är baserad i en simulerad linuxmiljö (genom applikationen Git Bash), innefattar detta avsnitt en snabb genomgång av de vanligaste kommandon som används i Linux. Git Bash startas enklast på skolans datorer genom att trycka på Windows knappen i nedre vänstra hörnet och skriva gitbash följt av att man trycker på Enter. Ett kommando skrivs in och exekveras genom att trycka på Enter. Vissa kommandon har dessutom argument vilka är separerade med ett mellanslag. För att få information om vilka argument som kan användas till ett visst kommando används help <kommando>. Om man som ett exempel vill veta mer om hur kommandot cd fungerar så skriver man help cd och trycker på retur. Nedan följer en lista över de vanligaste kommandon som finns i linux och som vi kommer använda under övning. pwd Skriver ut sökvägen till den befintliga arbetskatalogen(mappen). ls Listar alla kataloger/mappar och filer i arbetskatalogen. Om man lägger till flaggan -l formateras utskriften lite snyggare. mkdir touch rm mv Skapar en ny katalog/mapp i det nuvarande arbetskatalogen. Skapar en tom fil. Man kan skapa flera filer genom att namnge dem i följd efter att man skrivit kommandot touch. Detta är i denna övning praktiskt då vi vill kunna skapa filer samtidigt som deras innehåll inte har någon betydelse. Tar bort en eller flera filer från filsystemet. För att ta bort en katalog/mapp måste man använda alternativet -r. Flyttar en mapp eller fil till en annan plats i filsystemet. sid 1
Konfigurera GIT För att kunna koppla en användares bidrag till ett projektarbete med git så använder man sig av användarnamn och email-address. Det rekommenderas att när ni ställer in ert användarnamn och email att ni då använder riktiga namn och uppgifter. Detta förenklar att veta vem som är ägare till olika versioner i versionshistoriken. För att ta reda på befintlig inställning om användaruppgifter skriv: git config -global user.name git config -global user.email För att ställa in eller ändra dessa uppgifter gör man följande git config -global user.name Ditt för och efternamn Tänk på att få med citatteknen när du skriver för och efternamn. git config -global user.email Din emailadress Samma sak här dvs glöm inte citattecken. Övningar Nedan följer de övningar som skall utföras. Tillämpning av några enkla kommandon Innan ni börjar övningen så skapa en map i er användararea som ni kan använda för att arbeta med övningen. Döp mappen till GITovn och placera den gärna nära roten på er användararea. Detta kan exempelvis göras på följande sätt. 1. Starta Git Bash genom att trycka på windows knappen och skriv gitbash. När ni ser symbolen för gitbash så välj det med pekaren och tryck retur. 2. Git bash startas och ni kan skriva in kommandon och köra dessa genom att trycka retur. Skriv ned följande kommandon. wian42@cse-272984 MINGW64 ~ $ cd z: wian42@cse-272984 MINGW64 /z $ mkdir GITovn wian42@cse-272984 MINGW64 /z $ cd GITovn/ Ni har nu skapat en katalog som heter GITovn(med kommandot mkdir GITovn) som ligger under roten på er användararea. Samtidigt så har ni genom kommandot cd GITovn förlyttat er in i denna katalog. Starta gärna en filhanterare så att ni kan se detta och få det bekräftat genom att navigera till roten på er användararea. Testa även komandot pwd i Git Bash som skriver ut den befintliga sökvägen. Skapa nu en ny katalog inuti GITovn som ni kallar för Projektrepo. Se nedan. sid 2
wian42@cse-272984 MINGW64 /z/gitovn $ mkdir Projektrepo wian42@cse-272984 MINGW64 /z/gitovn $ ls Projektrepo/ Komandot mkdir skapar precis som tidigare en ny katalog. Bekräfta detta både genom att se med filhanterare men även genom att skriva komandot ls. Förflytta er sedan in i katalogen Projektrepo med kommandot cd Projektrepo precis som i tidigare exempel. Initsiering Försäkra dig att du befinner dig i katalogen Projektrepo genom att skriva komandot pwd. Om du inte är det så använd cd kommandot precis som visats i tidigare exempel. Innan vi skapar ett Git arkiv skall vi först testa kommandot git status. Skriv git status i Git Bash och undersök det felmeddelande som kommer. Vilket är felmedelandet? Vad tror ni det betyder? Nu skall vi skapa ett Gitarkiv(arkiv kallas även för repository förkortas repo) genom att skriva kommandot git init. Gör detta i Git Bash och följ sedan upp med att skriva git status. Se nedan. wian42@cse-272984 MINGW64 /z/gitovn/projektrepo $ git init Initialized empty Git repository in Z:/GITovn/Projektrepo/.git/ $ git status On branch master Initial commit nothing to commit (create/copy files and use "git add" to track) Vi ser nu att git status ger oss information om att ett Gitrepo finns. Några vanliga Git kommandon Vi skall nu fortsätta med att först skapa några filler för att sedan lägga till dem i indexet(staging area) så att Git systemet känner till att vi vill hantera dessa. Vi kommer sedan att göra en commit så att dessa filer kommer med i versionshistoriken. sid 3
Skapa nu en textfil som heter hello.txt. Detta kan du göra antingen genom att starta en valfri texteditor(exempelvis notepad++) och spara filen i Projektrepo mapen med namnet hello. Ett snabbare sätt att skapa en fil är att använda touch kommandot enligt nedan. wian42@cse-272984 MINGW64 /z/gitovn/projektrepo $ touch hello.txt Försäkra dig om att filen skapats med både filhanteraren samt att skriva komantod ls i Git Bash. Skapa även filerna wrongname.txt och trash.txt i samma katalog. Använd återigen ls och pwd kommandot för att se att filerna verkligen skapats och att de finns på rätt plats. Använd kommandot git status för att få infomration om tillståndet för Gitrepot. Se nedan. $ git status On branch master Initial commit Untracked files: (use "git add <file>..." to include in what will be committed) hello.txt trash.txt wrongname.txt nothing added to commit but untracked files present (use "git add" to track) Vi ser nu att filerna som skapats indikeras av git status genom att färga dem röda. Detta betyder att git ser att det finns filer i katalogen men att dessa inte är inkluderade i indexet(staging area) så att dessa kan spåras eller läggas till i en ny commit. Vi skall nu lägga till dessa filer i indexet enligt följande. $ git add hello.txt $ git add trash.txt $ git add wrongname.txt Undersök återigen Projektrepots tillstånd med kommandot git status. Se nedan. $ git status On branch master Initial commit Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: new file: new file: hello.txt trash.txt wrongname.txt Vi ser nu att filerna som vi tidigare skapat är färgade med grön färg vilket markerar att dessa är med i git indexet över filer som skall läggas till i nästa commit. Vi fortsätter med detta. sid 4
Skriv kommandot git commit -m följt av ett kort beskrivande meddelande. Observera att det är viktigt att få med citattecken. Det är vanligt att man skriver meddelandet initial commit just vid första gången då man har skapat ett Gitrepo. Se nedan. $ git commit -m "initial commit" [master (root-commit) 493602a] initial commit 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 hello.txt create mode 100644 trash.txt create mode 100644 wrongname.txt Använd status kommandot igen för att se vilken information som ges. Vi ser nu att de grönt markerade filerna inte längre är med vilket innebär att de har lagts till i versionshistoriken. Skriv nu kommandot git log enligt nedan. $ git log commit 493602aa26863035f7a467b690b4de1c85dbd2eb (HEAD -> master) Author: Andreas Wieden <andreas.wieden@chalmers.se> Date: Fri Sep 14 09:00:41 2018 +0200 initial commit Vi ser nu information om vår första kommit i versionshistoriken. Den första raden ger ett unikt id nummer som går att använda för att referera till. Detta är praktiskt om man råkar ut för ett problem och vill ställa tillbaka till en tidigare version som man vet fungerar. Då används detta nummer för att tala om för git vilken av versionerna i versionshistoriken som man vill återställa till. De andra raderna ger information om vem som skapat commiten samt datum. Längst ned ser vi kommentaren vi skapade i samband med commiten. Prova nu på att öppna filen hello.txt och skriv in lite text. Spara sedan och utför ovan procedur för att indexera och commita förändringarna. Glöm inte att skriva en kortfattad och beskrivande kommentar när du kör kommandot commit. Vi vill nu få bort filen trash.txt. Gör först en git status och försäkra dig om att gitrepot är rent. För att få bort filen trash.txt så skall vi använda kommandot git rm. Gör nu detta och gör sedan återigen en git status. Se nedan. $ git rm trash.txt rm 'trash.txt' $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) deleted: trash.txt Vi ser att filen trash.txt är markerad grön och att det inikeras att den är borttagen. Filen finns altså med i indexet men är inte ännu med som en färändring i versionshistoriken. För att göra detta så använd kommandot git commit -m deleted trash.txt. Efter att versionshistoriken uppdaterats så försäkra dig om att filen verkligen är borttagen antingen med filhanteraren eller med kommandot ls. sid 5
Utför också kommandot git log för att se att den nya versionen finns med i versionshistoriken. Vi vill nu döpa om filen wrongname.txt till rightname.txt. Gör detta med kommandot git mv enligt nedan. $ git mv wrongname.txt rightname.txt Använd git status kommandot för att bekräfta att git har indexerat förändringen du vill få med i versionshistoriken. Commita sedan detta och lägg till ett lämpligt komentarmeddelande. Bekräfta slutligen att filen bytt namn på det sätt som vi gjort tidigare i denna övning(ls eller filhanterare). Skriv git log kommandot och se vilka commit meddelanden som finns med. Skriv sedan gitk & (glöm inte & tecknet )i Git Bash. Detta startar upp ett program som visualiserar och visar versionhistoriken som ett träd. Notera att de meddelanden som du såg med git log kommandot också visas i gitk applikationen. Versionshistorik Vi skall nu testa hur man kan undersöka tidigare versioner. Innan vi börjar med detta kör ls kommandot så att ni vet vilka filer som finns och vad de heter i den version som ni befinner er i just nu. Utför en git log och notera ID numret som den allra första commiten har. Skriv sedan git checkout <ID-nummer på första commiten>. Undersök därefter vilka filer som finns och vad de heter. Ser ni några förändringar? Utför gärna också en git status, git log och gitk & för att undersöka hur versionshistoriken ser ut nu. Se figuren nedan som stöd för vad ni skall göra. sid 6
$ ls hello.txt rightname.txt $ git log commit 9611a941ee9045680b6b906de7eb0c82d1e06bcb (HEAD -> master) Author: Andreas Wieden <andreas.wieden@chalmers.se> Date: Fri Sep 14 10:03:49 2018 +0200 changed name wrongname -> rightname commit 79378b6a5db12dd9f9030b3c4dfabed0a4ccddc6 Author: Andreas Wieden <andreas.wieden@chalmers.se> Date: Fri Sep 14 10:00:06 2018 +0200 deleted trash.txt commit 493602aa26863035f7a467b690b4de1c85dbd2eb Author: Andreas Wieden <andreas.wieden@chalmers.se> Date: Fri Sep 14 09:00:41 2018 +0200 initial commit $ ls hello.txt rightname.txt $ git checkout 493602aa2 Note: checking out '493602aa2'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. Example: git checkout -b <new-branch-name> HEAD is now at 493602a... initial commit wian42@cse-272984 MINGW64 /z/gitovn/projektrepo ((493602a...)) $ ls hello.txt trash.txt wrongname.txt wian42@cse-272984 MINGW64 /z/gitovn/projektrepo ((493602a...)) $ git log commit 493602aa26863035f7a467b690b4de1c85dbd2eb (HEAD) Author: Andreas Wieden <andreas.wieden@chalmers.se> Date: Fri Sep 14 09:00:41 2018 +0200 initial commit wian42@cse-272984 MINGW64 /z/gitovn/projektrepo ((493602a...)) $ git status HEAD detached at 493602a nothing to commit, working tree clean Ni har nu hoppat tillbaka i versionshistoriken till första commiten. Nu går det att undersöka denna version om man vill. För att återgå till den nyaste versionen använder ni nu kommandot git checkout master. Undersök återigen både vilka filer som finns, vad de heter, status på index samt versionshistoriken enligt tidigare. sid 7
Om man vill återgå till en tidigare version och samtidigt ta bort allt efterföljande kan man använda sig av git reset -hard <ID-nummer på commiten>. Detta skall däremot användas försiktigt då de efterföljande commitsen tas bort permanent från versionshistoriken. Ett bättre sätt att återställa till en tidigare version är att kopiera den tidigare versionens tillstånd och sedan placera den längst fram i versionshistoriken. Den stora skillnaden är att om man mot förmodan ångrar att man återställt till en tidigare version, då fortfarande kan komma tillbaka till det ursprungliga läget. Så hur gör man detta? Jo man använder återigen kommandot checkout men med lite andra parametrar. Vi skall nu testa att återställa innehållet i textfilen hello.txt till den allra första versionen. Undersök först vad textfilen hello.txt innehåller(gör detta med tex en texteditor förslagsvis notepad++). Kör sedan kommandot git log och notera versions idnummret på första commiten. Sedan kör du kommandot git checkout <versions idnummer på första committen> -- hello.txt Missa inte de två minusstrecken i ovan visade kommando. Undersök nu igen innehållet i filen hello.txt på samma sätt som innan. Kör även git status(filen hello.txt är nu markerad grön vilket innebär att förändringarna är indexerade och redo att commita). Commita sedan förändringen på samma sätt som du tidigare har gjort och bekräfta att du fått in en ny version med kommandot git log. Gitk och Git-gui Denna del är tänkt att ge en insikt mellan hur git kommandon samt användande av grafisk applikation relaterar. Vi kommer nu skapa en ny fil och använda oss utav gitk och git-gui för att överblicka samt lägga till förändringen som en ny commit i versionshistoriken. Starta gitk genom att sriva gitk & ( glöm ej & tecknet). Starta git-gui genom att gå igenom File menyn och välja start git gui. Ni ser nu fyra stycken fällt som alla är tomma. Fältet som heter Unstaged Changes kommer att visa filer som är nya och ej indexerade. Fältet som heter Staged Changes visar indexerade filer. Till höger om detta fält finns också några knappar som vi strax kommer att använda oss av för att lägga till fil till vår nya commit. Vi börjar först med att skriva ls kommandot i Git Bash för att se vilka filer som vi har i vårat repo. Skapa nu en textfil som ni kallar för textgui.txt. Använd även git status kommandot för att se att git är medvetet om att en ny fil skapats. Lägg märke till att filen textgui.txt är markerad röd. Gå nu över till git-gui fönstret och gör den aktiv(klicka någonstans på den). Tryck sedan på F5(Skannar om det lagts till några nya filer) och se om något uppdateras i fälten. Markera den fil ni vill indexera(textgui.txt) genom att klicka på den. Tryck sedan på knappen Stage Changes. Notera vad som händer i de olika fälten. sid 8
Gå nu tillbaka till Git Bash och skriv git status. Notera att våran fil är markerad grön. Vår fil är nu indexerad att komma med vid nästa commit. Gå tillbaka till git-gui och skriv ett lämpligt commit meddelande i textfältet som finns i nedre högra hörnet. Tryck sedan på Commit knappen. Lägg märke till hur textfälten uppdaterats. Gå tillbaka till Git Bash och kör git status kommandot. Kör även en git log och undersök om det meddelande ni skrivit när ni kommitade med hjälp av git-gui kommit med. Undersök nu om commit meddelandet även kommit med i gitk. Om ni inte ser det så gör gitk aktivt genom att klicka någonstans på applikationen. Tryck sedan på F5 och notera om ert commit meddelande finns med. Om ni hittar det så har ni lyckats göra en kommit och bekräfta att den blivit utförd med hjälp av gitgui och gitk. Även om ni enbart använder er av git kommandon samt terminalfönster för att arbeta med git så kan gitk vara trevligt att använda ibland. Detta därför att man får en visualisering av versionshistoriken. Detta är speciellt trevligt om man arbetar med brancher och vill kunna få en visuell bild över hur versionshistoriken ser ut. Fjärrarkiv Att arbeta med ett lokalt arkiv som vi gort innan är ett bra hjälpmedel när man arbetar enskilt. För att samarbeta med andra och därmed öka nyttan av versionshangering så behöver se till att man kan dela på ett gemensamt arkiv. Det är då man använder sig av Fjärarkiv(även kallat fjärrepo). I övningarna direkt under så kommer ni att arbeta med ett lokalt fjärarkiv. Detta för att även kunna göra det lättare att undersöka några problem som kan uppstå när man arbetar med andra och samarbetar på ett fjärarkiv. Däremot så är tillämpningen i stort sett lika när man arbetar med fjärarkiv lokalt som när man använder ett fjärarkiv ute på nätet, exempelvis GitHub. Enda skillnaden när man arbetar mot ett fjärarkiv som ligger i ett lokalt system eller ute på internet är sökvägen man anger när man tex clonar, pullar och pushar. För övrigt så är det lika. Efter att ni är klara med övningarna i detta stycke så kan ni därför med fördel upprepa de kommandon ni lärt er och istället använda sökvägen mot ett fjärarkiv ute på nätet. Skapa ett Lokalt fjärrarkiv Tag först reda på sökvägen till Projektrepo katalogen med hjälp av kommandot pwd. Gå nu ut ur mappen Projektrepo i Git Bash. När man vill hoppa tillbaka ett steg ifrån en katalog som man redan befinner sig i så gör man det med kommandot cd.. (glöm inte mellanslag och två punkter). Använd kommandot pwd för att försäkra dig att du befinner dig rätt i katalogstrukturen(z/gitovn/). Skapa nu en katalog som heter Remoterepo(mkdir kommandot kan vara lämpligt). Gå sedan in i denna katalog med kommandot cd Remoterepo. För att tala om att denna katalog skall fungera som ett fjärrepo skriver man följande komandon. wian42@cse-272984 MINGW64 /z/gitovn/remoterepo $ git init --bare Initialized empty Git repository in Z:/GITovn/Remoterepo/ wian42@cse-272984 MINGW64 /z/gitovn/remoterepo (BARE:master) $ git update-server-info sid 9
Denna katalog kan nu användas som ett lokalt fjärrepo. Det har samma funktion som ett repo som ligger tex på github. Det som är kvar att göra är att pusha upp de filer man vill att alla skall ha tillgång till. Detta skall vi göra nu. Gå nu tillbaka till katalogen Projektrepo och skriv följande komandon. $ git remote add origin /z/gitovn/remoterepo $ git push -u origin master Första raden kopplar det befintliga arkivet med fjärrepot som ligger i katalogen. Den andra raden skickar upp allt i det befintliga arkivet till fjärrepot som ligger i Remoterepo katalogen. Nu kan alla andra som har tillgång till detta fjärarkiv att möjlighet att klona det. Att arbeta med ett Lokalt fjärrarkiv Eftersom ni är 2 i gruppen som arbetar mot samma dator så är tanken att ni skall dela upp er i två delar. En skall tillhöra undergrupp A och den andre undergrupp B. Nedan så får den som tillhör undergrupp A utföra övning som markerade PrjA och som beskrivs lite längre ner i texten. Motsvarande gäller då även för undergrupp B. Gå ut ur mappen Projektrepo i Git Bash. När man vill hoppa tillbaka ett steg ifrån en katalog som man redan befinner sig i så gör man det med kommandot cd.. (glöm inte mellanslag och två punkter). Använd kommandot pwd för att försäkra dig att du befinner dig rätt i katalogstrukturen(z/gitovn/). Skapa två nya kataloger och namnje dem PrjA och PrjB. Som ni kommer ihåg så skapar man kataloger med kommandot mkdir <namn på katalog>. PrjA: Gå in i katalogen PrjA med hjälp av kommandot cd. Clona nu Remoterepo som ni arbetade med innan så att dess innehåll hamnar i katalogen PrjA. Detta görs med kommandot git clone <sökväg till Remoterepo>. PrjA: Om du gör ls kommandot så ser du att det finns en katalog som heter Remoterepo. Det är denna katalog som du just har klonat. Gå in i denna katalog och skapa en textfil som du kallar för texta.txt och se till att indexera med kommandot add och att sedan commita. Undersök sedan att detta har hänt med de kommandon som vi använt tidigare i denna övning. Pusha sedan upp innehållet Remoterepo med kommandot git push origin master. PrjB: Gå in i katalogen PrjB och clona Remotrepot på samma sätt som PrjA nyss gjort. Gå in i det klonade repot och undersök vilka filer som finns och vilken status som detta repo har. Skapa också en textfil som heter textb.txt och indexera samt commita. Pusha sedan upp det till Remoterepo. Samtliga i gruppen: Både PrjA och PrjB skall nu testa att pulla från Remoterepo istallet för att clona som ni gjorde vid första tillfället. PrjA: Gå in i katalogen PrjA/Remoterepo och kör kommandot git pull origin master. Undersök att PrjBs förändring följde med. Lägg sedan till lite text i texta.txt och efter att du sparat indexera och commita. Pusha upp ändringarna precis som vid föregående tillfälle. sid 10
PrjB: Utför samma procedur som PrjA utförde nyss dvs pulla från Remoterepo och undersök att du fått med dig förändringarna. Konflikter Ibland så kan det hända att någon annan har hunnit att skicka upp en nyare version projektgruppens fjärarkiv. Om det inte är samma fil som man arbetar med själv så brukar det gå smidigt att låta git slå samman förändringarna. Däremot så kan det ibland bli så att någon är inne på precis samma fil som man själv arbetar med och gör förändringar vilket kan skapa konflikter som behöver lösas manuellt. Detta skall vi se på nu. PrjB: se till att pulla så att du har den senaste versionen av alla filer. Öppna PrjAs fil texta.txt och sabotera den lite genom att lägga in lite skräptext. Spara sedan, indexera, commita och pusha precis som vi har tjatat om innan PrjA: Se till så att du INTE gör någon pull. Åtminståne inte ännu. Öppna din fil texta.txt och skriv några korta rader. Spara nu, indexera, commita och pusha. Nu får du ett konfliktmeddelande eftersom det redan finns en förändrad version av din fil(prjb var ju inne i den och förändrade den). När du försöker pulla så blir du informerad om att det redan finns en uppdaterad version på fjärarkivet. Du måste nu först pulla och hämta ner det som redan finns i fjärarkivet. När du gör detta så undersök det meddelande som git ger efter att du kört pull kommandot. Här får du reda på vilken fil som konflikten finns i. Öppna denna textfil och justera texten så som du tycker är bra. Ta sedan och indexera, commita och pusha precis som vanligt. Nu är konflikten löst. Skapa ett Fjärarkiv på nätet För att kunna arbeta mot ett fjärrarkiv behöver vi först skapa ett konto på någon tjänst som erbjuder detta. I den följande framställningen kommer Github att användas, men det går lika bra med någon annan motsvarande tjänst. Under denna och andra kurser där ett fjärrarkiv används är det viktigt att det är privat så att andra studenter inte kommer åt er kod. På Github är det förenat med en kostnad, men de erbjuder ett studentabonnemang med kostnadsfri tillgång till privata arkiv. Gå till https://education.github.com/pack och registrera ett konto eller lägg till dig som student på ditt befintliga Github-konto. Skapa fjärrarkiv Det har nu blivit dags att skapa ett fjärrarkiv. På förstasidan (github.com) finns en knapp där det står New repository. Fyll i ett namn på arkivet och eventuellt en beskrivning. Klicka i Private och sedan Create repository. Undersök nu den sida som kommer upp. Det skall finnas en URL address som ni behöver om ni vill clona, pusha eller pulla till ert precis skapade fjärarkiv. Kopiera denna adress och försök upprepa samma övningar som ni gjort innan på det lokala fjärarkivet. sid 11
Interaktiv träning och annan information Om ni vill få lite interaktiv träning och visualisering av att arbeta med git så har github en resursida som kan vara trevlig att undersöka. https://try.github.io/ Git Referenser och dokumentation finns på: http://git-scm.com/documentation och olika tips och snabb genomgång finns att hitta på: http://www-cs-students.stanford.edu/~blynn/gitmagic/ sid 12