Friday, September 27, 2013

Mysteriet Git delvis avmystifisert

Språket her blir en salig røre av norsk, engelsk og techlees. Kanskje gir ikke det jeg skriver så veldig mye mening for deg uansett. Men noen kan ha nytte av dette en gang i fremtiden. Om ikke annet kan den noen være meg. Eller deg.

Jeg har en feature-branch spunnet ut fra en master-branch. Git altså. Det underlige og veldig funksjonelle dyret. I min gren har jeg gjort en hel masse greier. Alt for mye. Ikke har jeg vært noe flink med commitene heller (skal vi kalle dem bidrag? Eller har vi noe annet godt norsk ord?). Bidragene inneholder som regel endringer i forskjellige filer på forskjellige steder i fil-treet. Og det er blitt for mye, så å dytte det hele tilbake til hovedgrenen, gjennom en passende Pull Request - Review prosess, det er ikke særlig lett håndterlig. Men Git er jo bare en rekke oppdateringer, som man kan plukke fra hverandre som man vil. Og jeg vil forsøke å trekke ut alle endringene i en del av treet, og blåse i andre endringer - de kan jeg spare til senere. Så hva gjør jeg da. For å ikke rote alt til lager jeg en ny gren spunnet ut fra min feature. La oss kalle den A i kveld:
git co -b A
Og så får jeg finne ut hvor problemet starter. En grei måte er å kikke på loggen:
git log 
og finne frem til det bidraget der det hele gikk feil. Denne har et passende <sha> navn (den lange rare rekken av bokstaver og tall, også kalt heksadesimal). La oss flytte grenen tilbake dit.
git reset --hard <sha>
Nå trenger jeg å finne alle bidragene som mangler. Det skulle vel bli omtrent
git log featureB..HEAD
Nå kan jeg ta hver enkelt bidrag, starter med den eldste, og se på innholdet med for eksempel
git show <sha> | grep '^diff' 
Da ser jeg filer som er endret. Er det bare sånne jeg vil ha med kan jeg hente bidraget direkte inn med
git cherry-pick <sha>
Men hva om den inneholder også endringer i filer som ikke skal med. Da må vi gå mer detaljert til verks med
git cherry-pick --no-commit <sha>; git reset
Dette henter ut endringene, men vi resetter før vi commiter. Så kan vi plukke på hunk-nivå (eller endrings-nivå i bidraget) med
git add -p
Du svarer 'y' på de du vil ha med, 'n' på de andre, og avslutter med
git commit -m "Passende melding"
Så går vi videre til neste bidrag. Helt til vi er fremme.

Går dette alltid bra? Sikkert ikke. Det dukker nok opp konflikter underveis. Og kanskje har du 'merge' som ikke kan hentes inn med cherry-pick sånn utenvidere (her må jeg lære mer). Et stykke på vei kan det i hvertfall fungere.

Det store og viktige spørsmålet er selvsagt: Kan det automatiseres? Som en start har jeg denne fine kommandoen:
for c in $(git log A..B | grep '^commit' | awk '{print $2}'); do echo "commit: $c"; git show $c | grep '^diff' | awk '{print $3}' | sed 's#a/##'; done
Den vil hente ut alle bidrag, og så finne alle filer som har endringer i hvert enkelt bidrag. Spørsmålet er om jeg utifra dette kan kjøre git add <file-bidrag> og commit. Det må jeg finne ut av en annen dag.

Jeg håper dette ga deg noen ideer om du kom fordi du så etter hjelp med Git. Eller noe rart å lese om du ikke har fnugg av peiling på Git. Men hver trygg, jeg skal finne ut av dette - og da skal det komme en fin beskrivelse og script som gjør det jeg trenger. Stay tuned!