Git
Chapters ▾ 2nd Edition

5.2 Distribuerade Git - Medverka i ett projekt

Medverka i ett projekt

Det svåra med att beskriva hur du gör för att bidra till ett projekt är att medverkan kan ske på mängder av olika sätt. Tack vare Gits flexibelt skiljer sig användningen åt, vilket gör det näst intill omöjligt att säga hur du borde göra — varje projekt arbetar på sitt sätt. Faktorer som påverkar hur ett projekt är organiserat är antalet aktivt involverade, överenskomna arbetsprocesser och fördelningen av behörigheter för att checka in kod Det finns dock några generella riktlinjer som kan hjälpa dig att komma igång.

För det första — hur många involverade bidrar aktivt med källkod och hur ofta? I mindre pojekt är det vanligt att ett par, tre utvecklare gör några få incheckningar om dagen, kanske ännu färre om projektet är vilande. I större projekt, eller företag för den delen, kan hundratals utvecklare arbeta aktivt med kodbasen och bidra med tusentals incheckningar varje dag.

Det är viktigt att känna till ungefär hur många som arbetar aktivt i kodbasen. Ju fler utvecklare, ju mer problem kan du stöta på för att få din kod att läggas till och integreras sömlöst. Under tiden du arbetar med dina ändringar, eller väntar på att få dem godkända, kan ny kod göra dem ofunktionella — eller rentav inaktuella. Hur kan du då göra för att din egen kodbas ska hållas uppdaterad och dina incheckningar aktuella?

För det andra, hur ser projektets beslutade arbetsprocess ut? Är det en centraliserad process, så att varje utvecklare har samma skrivrättigheter till koden på huvudgrenen? Har projektet en förvaltare eller integrationsansvarig som granskar alla patcher? Ska samtliga patcher granskas och godkännas av en annan utvecklare? Kommer du att vara involverad i den processen? Eller finns det ett kvalitetsledningssystem med testare på plats som du behöver skicka dina ändringar till först?

Till sist spelar behörigheter för incheckningar in. Din medverkan kommer att skilja sig mycket åt beroende på om du har skrivrättigheter till kodbasen eller inte. Om du inte har det, hur ser processen ut för att granska och godkänna bidrag? Finns det ens en sådan process? Hur många ändringar ska skickas med i taget? Hur ofta?

Alla dessa frågor påverkar hur du bäst bidrar till ett projekt, liksom vilka arbetssätt du själv föredrar eller har tillgång till. Vi kommer att gå igenom tillvägagångssätten ur olika aspekter i en serie användarfall, från enkla till mer komplexa. Du borde känna igen det specifika arbetssätt du förväntas använda i dessa exempel.

Riktlinjer för incheckningar

Innan vi går in på användarfallen kommer en kort kommentar om incheckningsmeddelanden. Att ha bra riktlinjer för incheckningar, och att hålla sig till dem, gör det betydligt enklare att använda Git tillsammans med andra. I Git-projektet finns ett dokument med flera bra tips på saker att tänka på för att göra incheckningar till en patch. Du hittar det i filen Documentation/SubmittingPatches i Gits källkod.

Först av allt, så bör dina bidrag inte ha några felaktiga mellanslag. Git har ett lätt sätt att kontrollera det — innan du gör en incheckning, kör kommandot git diff --check. Det ger dig en lista över möjliga mellanslag som kan vara felaktiga.

Output av `git diff --check`.
Figur 57. Output of git diff --check.

Kör kommandot innan en incheckning för att snabbt kontrollera om du är på väg att checka in mellanslag som kan irritera andra utvecklare.

För det andra, försök att göra varje incheckning till logiskt separat enhet. Om du kan, försök att hålla dina ändringar lättsmälta - koda inte en hel helg på fem olika uppgifter, för att sen skicka dem som en enda gigantisk incheckning på måndan. Även om du inte har checkat in på en hel helg, använd köområdet för att dela upp ditt arbete i minst fem incheckningar på måndagen, med ett tydligt meddelande per incheckning. Om några av ändringarna är i samma fil, försök att använda git add --patch för att delvis köa filer (läs mer i Interactive Staging). Projektets ögonblicksbild längst ut på grenen kommer att se likadan ut oavsett om du gör en incheckning eller fem, så länge som alla ändringar läggs till förr eller senare. Försök därför att göra det så enkelt som möjligt för dina kollegor när de ska granska dina ändringar.

Med det tillvägagångssättet blir det också enklare att dra ut eller återställa någon av ändringarna i efterhand, om det skulle behövas. I avsnittet Rewriting History finns en mängd användbara tips för att skriva om Git-historiken och interaktivt köa filer — använd dessa verktyg för att få en logisk och förståelig historik innan du skickar arbetet vidare till någon annan.

Slutligen behövs en struktur för incheckningsmeddelandet. Med vanan att alltid skriva bra meddelanden blir användningen av - och samarbetet i - Git betydligt enklare. Tumregeln är att dina meddelanden ska börja med en mening på max 50 tecken som sammanfattar ändringen, följt av en blank rad och en mer detaljerad beskrivning. Git-projektet gör gällande att beskrivningen bör inkludera anledningen till ändringen och en jämförelse med tidigare beteende — det är en bra riktlinje att följa. Det är också bra att skriva i imperativ form. Med andra ord, ge order. Istället för “Jag lade till test för” eller “Lägger till test för,” skriv “Lägg till test för” Här är en mall ursprungligen skriven av Tim Pope:

Kort (50 tecken max), befallande sammanfattning

Mer detaljerad text, om nödvändigt. Håll den till cirka 72 tecken eller
så. Första raden kan ofta jämföras med ämnet på ett mejl och resten av
innehållet med brödtexten. Den tomma raden mellan titel och brödtext
är absolut nödvändig (om du inte utelämnar innehållet helt); verktyg som
ombasera kan bli förvirrade om de skrivs ihop.

Ytterligare paragrafer skrivs efter en tom rad.

  - Punktlistor är också ok

  - Efter en tom rad och ett inledande mellanslag används ofta
    bindestreck eller asterisk som punkt, men det finns olika
    konventioner.

Om alla dina incheckningsmeddelande följer den här mallen kommer det att bli lättare, både för dig och dem du samarbetar med. Git-projektet har välformatterade incheckningsmeddelanden — kör git log --no-merges där för att få inspiration till hur en väl formatterad incheckningshistorik kan se ut.

Notera
Gör som vi säger och inte som vi gör

För att vara helt ärliga, många av exemplen i den här boken har inte så värst välformatterade incheckningsmeddelanden; vi använder ofta -m efter git commit, helt enkelt.

Som sagt, gör som vi säger, inte som vi gör.

Privat, litet team

Det enklaste arbetssättet du sannolikt kommer stöta på är ett privat projekt med en eller två involverade utvecklare. I den här kontexten betyder “privat” sluten källkod — den är inte tillgänglig för någon utomstående. Du och de andra utvecklarna har skrivbehörigheter till arkivet.

I den här uppsättningen liknar arbetssättet det som du kanske stöter på när du använder Subversion eller något annat centraliserat versionshanteringssystem. Du behåller fördelarna med saker som att kunna checka in offline, en betydligt enklare förgrening och sammanslagning, men arbetsprocesserna är mycket lika; den största skillnaden är att sammanslagningar sker i klienten istället för från servern vid incheckning. Låt oss ta ett exempel på hur det kan gå till när två utvecklare börjar samarbeta i ett gemensamt arkiv. Den första utvecklaren, John, klonar arkivet och gör en ändring som checkas in lokalt. (Informationsmeddelandena har ersatts med ... för att korta ner exemplen.)

# Johns dator
$ git clone john@githost:simplegit.git
Klonar till 'simplegit'...
...
$ cd simplegit/
$ vim lib/simplegit.rb
$ git commit -am 'remove invalid default value'
[master 738ee87] remove invalid default value
 1 fil ändrad, 1 tillagd(+), 1 borttagen(-)

Jessika, den andra utvecklaren, gör samma sak — klonar arkivet och checkar in en ändring:

# Jessikas dator
$ git clone jessika@githost:simplegit.git
Klonar till 'simplegit'...
...
$ cd simplegit/
$ vim TODO
$ git commit -am 'add reset task'
[master fbff5bc] add reset task
 1 fil ändrad, 1 tillagd(+), 0 borttagen(-)

Sen skickar Jessika sina ändringar till servern, vilket funkar bra:

# Jessikas dator
$ git push origin master
...
Till jessika@githost:simplegit.git
   1edee6b..fbff5bc  master -> master

Den sista raden i outputen är ett användbart meddelande från sändningen. Formateringen är <gammalref>..<nyref> frånref -> tillref, där gammalref betyder tidigare referens, nyref är den nya referensen, frånref är namnet på den lokala referens som ändringen kommer från och tillref är fjärreferensen som har blivit uppdaterad. Du kommer att se liknande output i exemplen nedanför, en grundförståelse kommer göra det lättare att förstå innebörden av arkivens varierade stadier. Mer detaljer finns i git-push dokumentation.

För att fortsätta med det här exemeplet — kort efteråt gör John några ändringar, checkar in dem lokalt och försöker skicka dem till samma server som Jessika:

# Johns dator
$ git push origin master
Till john@githost:simplegit.git
 ! [refuserad]        master -> master (ej snabbspolad)
error: misslyckades sända vissa referenser till 'john@githost:simplegit.git'

I det här fallet kan John inte skicka sina incheckningar på grund av Jessikas tidigare incheckning av sina ändringar. Det här är särskilt viktigt att förstå om du tidigare har använt Subversion, för du kommer att märka att de två utvecklarna inte ändrade i samma fil. Subversion gör en sammanslagning automatiskt på servern om olika filer har ändrats, men med Git behöver du själv först sammanfoga incheckningarna lokalt. Med andra ord, John måste först hämta Jessikas ändringar i fjärrarkivet och sammanfoga dem i sitt lokala arkiv innan han kommer att få tillåtelse att skicka ändringarna.

Som ett första steg hämtar John Jessikas ändringar(Jessikas ändringar hämtas bara, de slås inte ihop med hans filer):

$ git fetch origin
...
Från john@githost:simplegit
 + 049d078...fbff5bc master     -> origin/master

Johns arkiv ser nu ut ungefär så här:

Johns divergerande historik.
Figur 58. Johns divergerande historik.

Nu kan John slå ihop de ändringar han hämtade av Jessika med filerna på sin dator:

$ git merge origin/master
Sammanslagning gjord med metoden 'recursive'.
 TODO |    1 +
 1 fil ändrad, 1 tillägg(+), 0 borttagna(-)

Om den lokala sammanslagningen går smidigt kommer Johns uppdaterade historik se ut ungefär så här:

Johns arkiv efter sammanslagning av ändringar från `origin/master`.
Figur 59. Johns arkiv efter sammanslagning av ändringar från origin/master.

Nu kanske John vill testa den nya koden för att vara helt säker på att ingen av Jessikas ändringar påverkat hans, och om allt går bra kan han slutligen skicka sina sammanslagna ändringar till servern:

$ git push origin master
...
Till john@githost:simplegit.git
   fbff5bc..72bbc59  master -> master

Till sist ser Johns incheckningshistorik ut så här:

Johns historik efter att ha skickat sina ändringar till servern `origin`.
Figur 60. Johns historik eftervatt ha skickat sina ändringar till servern origin.

Under tiden har Jessika skapat en ny gren med namnet issue54, och gjort tre incheckningar till den nya grenen. Hon har inte hämtat Johns ändringar än, så hennes incheckningshistorik ser ut så här:

Jessikas gren.
Figur 61. Jessikas gren.

Plötsligt får Jessika veta att John har skickat nya ändringar till servern och vill kika på dem, så hon hämtar alla ändringar från servern med kommandot:

# Jessikas dator
$ git fetch origin
...
Från jessika@githost:simplegit
   fbff5bc..72bbc59  master     -> origin/master

Nu hämtas Johns incheckade ändringar ner. Jessikas historik kommer att se ut så här:

Jessikas historik efter att ha hämtat Johns ändringar.
Figur 62. Jessikas historik efter att ha hämtat Johns ändringar.

Jessika tycker att ändringarna på hennes nya gren är klara, men hon vill veta vilka delar av Johns arbete som hon måste slå ihop med sitt arbete så att hon kan skicka dem. Hon kör git log för att få reda på det:

$ git log --no-merges issue54..origin/master
commit 738ee872852dfaa9d6634e0dea7a324040193016
Författare: John Smith <jsmith@example.com>
Datum:   Fri May 29 16:01:27 2009 -0700

   ta bort ogiltigt standardvärde

Syntaxen issue54..origin/master används för att filtrera loggar. Git ombeds att bara visa de incheckningar i den senare referensen (origin/master i det här fallet) som inte finns på den första referensen (här issue54). Vi kommer att gå igenom syntaxen i detalj i avsnitt Commit Ranges.

Från ovanstående output kan vi utläsa att det bara är en enda av Johns incheckningar som Jessika inte har slagit ihop med sitt arbete lokalt. Om hon slår ihop sina ändringar med ändringarna på origin/master är det bara den incheckningen som kommer att påverka hennes lokala arkiv.

Nu kan Jessika slå ihop sin lokala funktionsgren med sin huvudgren, slå samman Johns ändringar (origin/master) med sin master gren och sen skicka sitt arbete till servern igen. .

När alla ändringar på grenen issue54 har checkats in, börjar Jessika med att byta tillbaka till sin huvudgren:

$ git checkout master
Byter till gren 'master'
Din gren ligger efter 'origin/master' med 2 incheckningar, kan snabbspolas.

Jessika kan slå ihop antingen origin/master eller issue54 först — de är båda uppströms, så ordningen spelar ingen roll. Den slutliga ögonblicksbilden blir identisk oavsett vilken ordning hon väljer, det är bara historiken som kommer att skilja sig åt. Hon bestämmer sig för att slå ihop grenen issue54 först:

$ git merge issue54
Uppdaterar fbff5bc..4af4298
Snabbspolar
 README           |    1 +
 lib/simplegit.rb |    6 +++++-
 2 filer ändrade, 6 tillägg(+), 1 borttagen(-)

Inget oväntat händer, som du kan se var det en sammanslagning som kunde snabbspolas framåt. Jessika kan nu avsluta sammanslagningarna lokalt genom att slå ihop de ändringar hon hämtade tidigare från John och som är väntar kvar på origin/master:

$ git merge origin/master
Slår ihop lib/simplegit.rb automatiskt
Sammanslagning gjorde med metoden 'recursive'.
 lib/simplegit.rb |    2 +-
 1 fil ändrad, 1 tillägg(+), 1 borttagning(-)

Allt slogs ihop smidigt, Jessikas historik ser nu ut så här:

Jessikas historik efter att ha slagit ihop Johns ändringar infogats.
Figur 63. Jessikas historik efter att ha slagit ihop Johns ändringar.

Fjärreferensen origin/master kan nås från Jessikas huvudgren, så hon borde kunna skicka sina ändringar utan problem (förutsatt att John inte har skickat fler ändringar under tiden):

$ git push origin master
...
Till jessika@githost:simplegit.git
   72bbc59..8059c15  master -> master

Båda utvecklarna har nu checkat in sina versioner av filerna några gånger och fått in varandras ändringar i sina lokala kodbaser.

Jessikas historik efter att ha skickat tillbaka ändringarna till servern.
Figur 64. Jessikas historik efter att ha skickat tillbaka ändringarna till servern.

Det här är en av de enklaste arbetsprocesserna.

Du arbetar ett tag (vanligtvis i en funktionsgren), och slår ihop arbetet i huvudgrenen när det är klart. När du vill dela ditt arbete, hämtar du och slår ihop din huvudgren med origin/master och skickar tillbaka din huvudgren till servern. Den generella arbetsprocessen ser ut ungefär så här:

Generell arbetsprocess med Git för flera utvecklare.
Figur 65. Generell arbetsprocess med Git för flera utvecklare.

Privat större team

Nästa exempel är medverkan i ett större, privat team. Här tittar vi närmre på hur arbetsprocessen kan se ut när mindre team samarbetar på features, som därefter slås ihop av andra team.

Säg att John och Jessika arbetar tillsammans på en funktion (vi kallar den “featureA”). Samtidigt samarbetar Jessika och en tredje utecklare, Josie, på en annan, (“featureB”). I det här fallet använder sig företaget av en slags integrationsstyrt arbetsprocess, där arbetet av ett enskilt team slås samman med huvudgrenen av specifika ingengörer. Arkivets huvudgren kan endast uppdateras av dessa. Allt arbete sker på förgreningar som sedan slås ihop av andra i ett senare skede.

Vi följer med Jessika medan hon arbetar på sina features parallellt, med två olika utvecklare, i en sådan här miljö. Vi utgår ifrån att hon redan har klonat arkivet, när hon bestämmer sig för att börja med featureA. Hon skapar en ny gren för funktionen och jobbar lite på den:

# Jessikas dator
$ git checkout -b featureA
Bytte till en ny gren 'featureA'
$ vim lib/simplegit.rb
$ git commit -am 'add limit to log function'
[featureA 3300904] add limit to log function
 1 fil ändrades, 1 tillägg(+), 1 borttagning(-)

I det här läget behöver hon dela sitt jobb med John, så hon skickar sin featureA-gren med incheckningarna till servern. Jessika har inte behörighet att slå ihop sina ändringar med huvuudgrenen, så hon behöver skicka dem till en annan gren för att kunna samarbeta med John:

$ git push -u origin featureA
...
Till jessica@githost:simplegit.git
 * [new branch]      featureA -> featureA

Jessika mejlar John för att berätta att hon har skickat ändringar till en gren med namnet featureA och att han kan kolla på dem nu. Medan hon väntar på feedback från John bestämmer hon sig för att börja jobba på featureB tillsammans med Josie. Hon börjar med att skapa en ny gren från serverns huvudgren:

# Jessikas dator
$ git fetch origin
$ git checkout -b featureB origin/master
Bytte till en ny gren 'featureB'

Nu gör Jessika ett par inckeckningar på `featureB`grenen:

`[source,console]

$ vim lib/simplegit.rb
$ git commit -am 'made the ls-tree function recursive'
[featureB e5b0fdc] made the ls-tree function recursive
 1 fil ändrad, 1 tillägg(+), 1 borttagen(-)
$ vim lib/simplegit.rb
$ git commit -am 'add ls-files'
[featureB 8512791] add ls-files
 1 fil ändrad, 5 tillägg(+), 0 borttagna(-)

Jessikas arkiv ser nu ut så här:

Jessikas initiala versionshistorik.
Figur 66. Jessikas initiala versionshistorik.

Hon är redo att skicka sina ändringar när hon får ett mejl från Josie som skriver att en gren hon har börjat på för featureB redan har skickats till servern som grenen featureBee. Jessika behöver slå ihop de ändringarna med sina innan hon kan skicka sitt arbete till den gren Josie skickat till servern. Jessika hämtar först Josies ändringar med git fetch:

$ git fetch origin
...
Från jessika@githost:simplegit
 * [new branch]      featureBee -> origin/featureBee

Om vi antar att Jessika fortfarande är utcheckad på grenen featureB, så kan hon nu slå Josies arbete med den grenen med kommandot git merge:

$ git merge origin/featureBee
Slår ihop lib/simplegit.rb automatiskt.
Sammanslagning gjord med metoden 'recursive'.
 lib/simplegit.rb |    6 ++++++
 1 fil ändrad, 6 tillägg(+), 0 borttagna(-)
Sammanslagning gjord med metoden 'recursive'.
 lib/simplegit.rb |    4 ++++
 1 fil ändrad, 4 tillägg(+), 0 borttagna(-)

I det här läget vill Jessika skicka allt som finns på featureB tillbaka till servern, men utan att skicka upp sin egna gren. Eftersom Josie redan har påbörjat en uppströms featureBee-gren så vill Jessika skicka till den grenen. Det gör hon med:

$ git push -u origin featureB:featureBee
...
Till jessick@githost:simplegit.git
   fba9af8..cd685d1  featureB -> featureBee

Detta kallas för en referensspecifikation. Se The Refspec för mer information om Gits referensspecifikationer och hur du kan använda dem. Lägg också märke till -u-flaggan; det är en kortversion av kommandot --set-upstream, som sparar en referens till den fjärrgren som din lokala gren spårar.

Plötsligt får Jessika ett mail från John, som berättar att han har skickat några ändringar till featureA-grenen och ber henne kolla på dem. Återigen kör Jessika ett enkelt git fetch för att hämta allt nytt innehåll från servern, inklusive Johns senaste arbete:

$ git fetch origin
...
Från jessika@githost:simplegit
   3300904..aad881d  featureA   -> origin/featureA

Jessika kan nu läsa loggarna med Johns senaste ändringar genom att jämföra innehållet som hämtades för featureA med den lokala kopian av samma gren:

$ git log featureA..origin/featureA
commit aad881d154acdaeb2b6b18ea0e827ed8a6d671e6
Författare: John Smith <jsmith@example.com>
Datum:   Fri May 29 19:57:33 2009 -0700

    changed log output to 30 from 25

Om Jessika gillar det hon ser kan hon fusionera Johns nya arbete till sin lokala featureA gren med:

$ git checkout featureA
Bytte till gren 'featureA'
$ git merge origin/featureA
Uppdaterar 3300904..aad881d
Snabbspolar
 lib/simplegit.rb |   10 +++++++++-
1 fil ändrad, 9 tillägg(+), 1 borttagen(-)

Till sist kanske Jessika vill göra några mindre ändringar i det sammanslagna innehållet. Hon har full frihet att göra sina ändringar, checka in dem till sin lokala gren featureA och skicka tillbaka slutresultatet till servern.

$ git commit -am 'small tweak'
[featureA 774b3ed] small tweak
 1 fil ändrad, 1 tillägg(+), 1 borttagen(-)
$ git push
...
Till jessika@githost:simplegit.git
   3300904..774b3ed  featureA -> featureA

Jessikas incheckningshistorik kommer nu att se ut ungefär så här:

Jessikas historik efter incheckningar på funktionsgrenen.
Figur 67. Jessikas historik efter incheckningar till funktionsgrenen.

Vid någon tidpunkt kommer Jessika, John och Josie att behöva informera dem som ansvarar för huvudgrenen att featureA och featureB är redo att slås ihop med den. När sammanslagningen med huvudgrenen då är klar, kommer en hämtning av den att få med den nya versionsincheckningen. Historiken kommer då att se ut så här:

Jessikas historik efter sammanslagning av ämnesgrenar med huvudgren.
Figur 68. Jessikas historik efter sammanslagning av ämnesgrenar med huvudgren.

Många byter till Git på grund av möjligheten för flera team att arbeta parallellt och kombinera sina förgreningar sent i processen. Möjligheten att mindre grupperingar inom ett team kan samarbeta via fjärrgrenar utan att nödvändigtvis behöva involvera hela teamet är utan tvekan en av Gits stora fördelar. Arbetsprocessen som precis har beskrivts ser ut ungefär såhär:

Grundläggande arbetsflöde för flera team.
Figur 69. Grundläggande arbetsprocess för flera team.

Öppet, litet projekt

Att bidra till öppna projekt är ofta lite annorlunda än privata. Eftersom du vanligtvis inte har behörighet att göra ändringar direkt i projektet behöver förvaltarna få ditt arbete på något annat sätt. I det första exemplet ska vi titta på hur man förgrenar ett arkiv på de hostingsajter för Git som tillåter det. Många hostingsajter har stöd för förgreningar, (såsom GitHub, BitBucket, repo.or.cz med flera) och många projektansvariga förväntar sig att andra medverkar på det sättet. Det andra exemplet beskriver arbetsprocessen i de projekt som istället föredrar att acceptera patchade bidrag via mejl.

Det första du behöver göra är troligen att klona grundarkivet och skapa en funktionsgren för patcherna du planerar att bidra med Flödet ser helt enkelt ut så här:

$ git clone <url>
$ cd project
$ git checkout -b featureA
  ... arbeta ...
$ git commit
  ... arbeta ...
$ git commit
Notera

Du kanske kommer att vilja använda rebase -i för att komprimera ditt arbete till en enda incheckning, eller arrangera om incheckningnarna så att de blir enklare för de ansvariga att granska — se Rewriting History för mer information om att ombasera grenar interaktivt.

När arbetet är klart och du är redo att dela det med förvaltarnas, gå till ursprungsarkivets projektsida och klicka på knappen ``Fork`` för att skapa en kopia av projektet som du har full behörighet till. Därefter lägger du till det nya repots URL som ett nytt fjärrarkiv för ditt lokala arkiv; i det här exemplet kan vi kalla det för mingaffel:

$ git remote add mingaffel <url>

Sedan behöver du skicka ditt arbete till det här arkivet. Det är enklare att skicka den finessgren du arbetar på till ditt kopierade arkiv än att fusionera det arbetet i din huvudgren och fusionera den. Om ditt arbete inte bli godkänt, eller om din incheckning inte blir cherry-pickad, så slipper du spola tillbaka din huvudgren (läs mer om Gits cherry-pick i Arbetsflöden med ombasering och plocka russin ur kakan). Om de ansvariga å andra sidan slår ihop, ombaserar eller cherry-pickar din funktion, så kommer du att få tillbaka den genom att dra ner ändringar från deras repo ändå. Hur du än gör kan du skicka ditt arbete med:

$ git push -u mingaffel featureA

När ditt arbete har skickats till din förgrening av arkivet behöver du meddela förvaltarna av originalprojektet att du har arbete som du skulle vilja att de slår samman. Detta kallas ofta för en pull request och du behöver vanligtvis göra en sådan begäran antingen via websida — GitHub har sin egen ``Pull Request``-mekanism som vi kommer att gå igenom i GitHub — eller så kan du köra kommandot git request-pull och mejla den efterföljande utdatan till de ansvariga.

Kommandot git request-pull tar basgrenenen, det vill säga den gren du vill att din funktionsgren dras in i, och Git-arkivets URL som du vill att de accepterar kod ifrån, och sammanfattar alla ändringar du begär ska tas in. Om Jessika till exempel vill skicka en pull request till John, och hon har gjort två incheckningar på funktionsgrenen som hon just har skickat, så kan hon använda det så här:

$ git request-pull origin/master mingaffel
Följande ändringar sen incheckning 1edee6b1d61823a2de3b09c160d7080b8d1b3a40:
Jessika Smith (1):
        added a new function

är tillgängliga i git-arkivet på:

  git://githost/simplegit.git featureA

Jessika Smith (2):
      add limit to log function
      change log output to 30 from 25

 lib/simplegit.rb |   10 +++++++++-
 1 fil ändrad, 9 tillägg(+), 1 borttagning(-)

Den här utdatan kan skickas till de ansvariga — den beskriver från vilken gren arbetet förgrenades, ger en sammanfattning av incheckningarna och varifrån den nya funktionen kan hämtas.

I ett projekt där du inte är ansvarig är det oftast enklast att ha en huvudgren, t.ex master, som alltid följer origin/master och sen arbeta i funktionsgrenar som du enkelt kan radera om de inte godkänns. Att isolera funktioner tll funktionsgrenar gör det också lättare för dig att flytta ditt arbete om änden på huvudarkivet har rört sig under tiden du arbetat så att din version inte längre kan sammanfogas på ett smidigt sätt. Om du till exempel vill skicka in en andra funktionsgren till projektet, fortsätt inte att arbeta på den funktionsgren du nyss skickade upp — börja istället om från arkivets huvudgren:

$ git checkout -b featureB origin/master
  ... arbeta ...
$ git commit
$ git push mingaffel featureB
$ git request-pull origin/master myfork
  ... e-postbrevet genererar förfrågan att hämta till ansvarig ...
$ git fetch origin

Now, each of your topics is contained within a silo — similar to a patch queue — that you can rewrite, rebase, and modify without the topics interfering or interdepending on each other, like so: Nu är båda dina funktioner i varsitt silo — som en kö för patchar — som du kan skriva om, ombasera och ändra utan att funktionerna påverkar eller blir beroende av varandra:

Initial versionshistorik för `featureB`.
Figur 70. Initial versionshistorik för featureB.

Om vi säger att projektets ansvariga har dragit in ett gäng andra patches innan de testar din första gren, så kan den inte sammanfogas automatiskt. I det här fallet kan du försöka att ombasera den grenen så att den hamnar längst ut på toppen av origin/master, lösa eventuella konflikter och skicka in din version igen:

$ git checkout featureA
$ git rebase origin/master
$ git push -f mingaffel featureA

Det här skriver om historiken enligt bilden nedan Versionshistorik efter avslutat arbete med featureA..

Versionshistorik efter avslutat arbete med `featureA`.
Figur 71. Versionshistorik efter avslutat arbete med featureA.

Eftersom du flyttade grenen behöver du ange -f till ditt kommando för att kunna ersätta serverns featureA-gren med en annan incheckning än den ursprungliga. Ett alternativ vore att skicka det här nya arbetet till en annnan gren på servern (som kanske kan heta featureAv2),

Vi tittar närmre på ytterligare ett alternativ: ansvariga har tittat ändringarna i din andra gren och gillar dem mestadels, men de skulle vilja att du gjorde en justering. Du använder möjligheten att flytta förgreningen från featureA till huvudgrenen.

Du kan göra det här genom att skapa en ny gren från origin/master, sammanfoga featureB där, lösa eventuella konflikter, göra ändringen och skicka det som en ny gren:

$ git checkout -b featureBv2 origin/master
$ git merge --squash featureB
  ... justera ...
$ git commit
$ git push mingaffel featureBv2

Flaggan --squash komprimerar alla incheckningar på grenen som ska slås ihop till en enda versionsändring, vilket ger samma status i arkivet som vid en sammanslagning. Det innnebär att dina framtida incheckningar bara kommer att ha en förälder och ger dig möjlighet att dra in alla ändringar från en annan gren och göra fler ändringar innan den nya incheckningen kommer på pränt. Ibland kan det vara användarbart att använda flaggan --no-commit för att fördröja en ny incheckning vid en sammanslagning istället för som i den ordinarie sammanslagningsprocessen. I det här läget kan du meddela ansvarig att du har gjort de begärda ändringarna och att de kan hitta dessa i din featureBv2.

Versionshistorik efter `featureBv2`.
Figur 72. Versionshistorik efter featureBv2.

Öppet, större projekt via mejl

Många projekt har sina rutiner för att acceptera patchar — du behöver kolla upp de specifika reglerna för varje projekt, eftersom de kommer att skilja sig åt. Eftersom det finns flera äldre, större projekt som endast accepterar patchar via en mejllista för utvecklare, så kommer vi att gå igenom ett exempel på hur det går till.

Arbetsprocessen liknar den i föregående exemplet — du skapar finessgrenar som du arbetar på. Den stora skillnaden är att du inte kan skicka dina inceckningar till fjärrarkivet. Istället behöver du mejla varje commit till en mejllista för utvecklare.

$ git checkout -b topicA
  ... arbeta ...
$ git commit
  ... arbeta ...
$ git commit

Nu har du två incheckningar som du vill skicka till mejllistan. För att generera mbox-formatterade filer som du kan mejla till listan, använder du git format-patch — det gör om varje incheckning till ett mejl med den första raden i incheckningsmeddelandet som ämne och resten av meddelandet plus patchen som innehåll. Det fina med det här är att när en patch från ett mejl genererat med format-patch appliceras, så bevaras incheckningsinformationen korrekt.

$ git format-patch -M origin/master
0001-add-limit-to-log-function.patch
0002-changed-log-output-to-30-from-25.patch

format-patch-kommandot skriver ut namnen på de patchfiler som skapas. Flaggan -M talar om för Git att leta efter omdöpta filer. Filerna kommer att se ut så här:

$ cat 0001-add-limit-to-log-function.patch
Av 330090432754092d704da8e76ca5c05c198e71a8 Mån Sep 17 00:00:00 2001
Från: Jessica Smith <jessica@example.com>
Datum: Sön, 6 apr 2008 10:17:23 -0700
Ämne: [PATCH 1/2] add limit to log function

Limit log functionality to the first 20

---
 lib/simplegit.rb |    2 +-
 1 fil ändrad, 1 tillagd(+), 1 borttagen(-)

diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index 76f47bc..f9815f1 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -14,7 +14,7 @@ class SimpleGit
   end

   def log(treeish = 'master')
-    command("git log #{treeish}")
+    command("git log -n 20 #{treeish}")
   end

   def ls_tree(treeish = 'master')
--
2.1.0

Du kan också redigera patchfilerna för att lägga till mer information till mejllistan som du inte vill ska visas i incheckningsmeddelandet. Om du lägger till text mellan raden --- och början av patchen (inleds med raden diff --git), så kan utvecklarna läsa meddelandet, men det ignoreras i patchningsprocessen. Det är en bra plats att lägga till information om varför du gjorde ändringarna, eller om du har några speciella instruktioner för att testa dem.

För att skicka patchfilen till mejllistan kan du antingen klistra in innehållet i ditt mejlprogram eller skicka det via kommandoraden. Att klistra in texten orsakar ofta formatteringsproblem, speciellt med “smartare” klienter som inte bevarar radbrytningar och mellanslag korrekt. Som tur är har Git ett verktyg som hjälper dig att skicka korrekt formatterade patchar via IMAP, vilket gör det enklare. Vi kommer att visa hur du skickar en patch via Gmail, som råkar vara den e-postklient vi känner till bäst. Du kan läsa detaljerade instruktioner för en mängd e-postklienter i slutet av filen Documentation/SubmittingPatches i Gits källkod.

Först behöver du sätta upp IMAP-sektionen i din ~/.gitconfig-fil. Du kan sätta varje värde separat med en serie git config-kommandon, eller lägga till dem manuellt. I slutänden ska din konfigurationsfil se ut något så här:

[imap]
  folder = "[Gmail]/Drafts"
  host = imaps://imap.gmail.com
  user = user@gmail.com
  pass = YX]8g76G_2^sFbd
  port = 993
  sslverify = false

Om din IMAP-server inte använder SSL så är de två sista raderna nog inte nödvändiga, och värdet för host kommer då att vara imap:// istället för imaps://. När det är inställt kan du använda git imap-send för att placera patchserien i mappen Drafts på den angivna IMAP-servern:

$ cat *.patch |git imap-send
Analyserar imap.gmail.com... ok
Ansluter till [74.125.142.109]:993... ok
Loggar in...
skickar 2 meddelanden
100% (2/2) klart

Nu borde du kunna gå till din Drafts-mapp, ändra To-fältet till mejllistan du ska skicka patchen till, eventuellt lägga till cc:a till den person som är ansvarig, och skicka iväg det.

Du kan också skicka patcharna via en SMTP-server. Precis som tidigare kan du sätta varje värde separat med en serie git config-kommandon, eller lägga till dem manuellt i ~/.gitconfig-filen:

[sendemail]
  smtpencryption = tls
  smtpserver = smtp.gmail.com
  smtpuser = user@gmail.com
  smtpserverport = 587

När det är inställt kan du använda git send-email för att skicka patcharna:

$ git send-email *.patch
0001-added-limit-to-log-function.patch
0002-changed-log-output-to-30-from-25.patch
Från vem ska breven skickas? [Jessica Smith <jessica@example.com>]
E-postbreven kommer att skickas från: Jessica Smith <jessica@example.com>
Till vem ska breven sändas (om någon)? jessica@example.com
Message-ID att använda som In-Reply-To för det första brevet? y

Git kommer att ställa en rad frågor om hur du vill att e-posten ska se ut för varje skickad patch. De kommer att se ut ungefär så här:

(mbox) Lägger till cc: Jessica Smith <jessica@example.com> från
  \line 'From: Jessica Smith <jessica@example.com>'
OK. Loggen säger:
Sendmail: /usr/sbin/sendmail -i jessica@example.com
Från: Jessica Smith <jessica@example.com>
Till: jessica@example.com
Ämne: [PATCH 1/2] added limit to log function
Datum: Sat, 30 May 2009 13:29:15 -0700
Message-Id: <1243715356-61726-1-git-send-email-jessica@example.com>
X-Mailer: git-send-email 1.6.2.rc1.20.g8c5b.dirty
In-Reply-To: <y>
References: <y>

Result: OK

Sammanfattning

I den här delen har vi gått igenom en mängd olika arbetsflöden för olika slags Git-projekt som du troligen kommer att stöta på, samt introducerat ett par nya verktyg för att hjälpa dig att hantera processerna. I nästa del kommer du att få lära dig hur du hanterar den andra sidan av myntet: att underhålla ett Git-projekt. Du får lära dig att vara en välvillig diktator eller integrationsansvarig.

scroll-to-top