Hvordan (og hvorfor!) Holde Git-forpliktelseshistorikken ren

Forpliktelser er en av nøkkeldelene i et Git-depot, og mer så er kommittemeldingen en livslogg for depotet. Etter hvert som prosjektet / depotet utvikler seg over tid (nye funksjoner blir lagt til, feil blir løst, arkitektur blir omformet), er meldingsplasser stedet hvor man kan se hva som ble endret og hvordan. Så det er viktig at disse meldingene gjenspeiler den underliggende endringen på en kort, presis måte.

Hvorfor er en meningsfylt Git-forpliktelseshistorie viktig

Hva gjør Git forplikter? er fingeravtrykkene du legger igjen på koden du berører. En hvilken som helst kode du begår i dag, et år fra nå når du ser på den samme endringen; du vil være takknemlig for en klar, meningsfull forpliktelsesmelding som du skrev, og den vil også gjøre livet til medutviklerne dine lettere. Når forpliktelser isoleres basert på kontekst, blir en feil som ble introdusert av en viss forpliktelse raskere å finne, og jo lettere er det å tilbakeføre forpliktelsen som forårsaket feilen i utgangspunktet. / p>

Mens vi arbeider med et stort prosjekt, har vi ofte å gjøre med mange bevegelige deler som oppdateres, legges til eller fjernes. Å sikre at forpliktende meldinger opprettholdes i slike tilfeller kan være vanskelig, spesielt når utviklingen spenner over dager, uker, eller til og med måneder. Så for å forenkle innsatsen for å opprettholde konsis e commit-historie, denne artikkelen vil bruke noen av de vanlige situasjonene som en utvikler kan møte når han jobber med et Git-arkiv.

  • Situasjon 1: Jeg må endre den siste forpliktelsen
  • Situasjon 2: Jeg trenger å endre en bestemt forpliktelse
  • Situasjon 3: Jeg trenger å legge til, fjerne eller kombinere forpliktelser
  • Situasjon 4: Forpliktelseshistorikken min gjør ikke fornuftig, jeg trenger en ny start!

Men før vi dykker inn, la oss raskt gå gjennom hvordan en typisk utviklingsarbeidsflyt ser ut i en vår hypotetiske Ruby-applikasjon.

Merk: Denne artikkelen forutsetter at du er klar over det grunnleggende om Git, hvordan grener fungerer, hvordan du legger til uforpliktede endringer av en gren til scenen og hvordan du kan forandre endringene. Hvis du er usikker på disse strømningene, er dokumentasjonen vår et flott utgangspunkt.

En dag i livet

Her jobber vi med et lite Ruby on Rails-prosjekt hvor vi trenger å legge til en navigasjonsvisning på hjemmesiden og det innebærer oppdatering og å legge til flere filer. Følgende er en trinnvis oversikt over hele flyten:

  • Du begynner å jobbe med en funksjon med oppdatering av en fil ; la oss kalle det application_controller.rb
  • Denne funksjonen krever at du også oppdaterer en visning: index.html.haml
  • Du la til en del som brukes på indeksiden: _navigation.html.haml
  • Stilene for siden må også oppdateres for å gjenspeile den delen vi la til: styles.css.scss
  • Funksjonen er nå klar med de ønskede endringene, tid til å også oppdatere tester; filene som skal oppdateres er som følger:
    • application_controller_spec.rb
    • navigation_spec.rb
  • Testene oppdateres og går som forventet, nå er det tid for å utføre endringene!

Siden alle filene tilhører forskjellige territorier i arkitekturen, forplikter vi oss endringene isolert fra hverandre for å sikre at hver forpliktelse representerer en bestemt kontekst og gjøres i en bestemt rekkefølge. Jeg foretrekker vanligvis backend – > frontend-rekkefølge der mest backend-sentriske endring begås først, etterfulgt av mellomlaget og deretter av frontend-sentriske endringer i Git-listen forplikter.

Nå som vi har gjort endringene våre, oppretter vi en fusjonsforespørsel med filialen. Når du har fusjonsforespørselen åpen, blir den vanligvis vurdert av jevnaldrende før endringene blir slått sammen til repo «s master gren. La oss nå lære hvilke forskjellige situasjoner vi kan ende opp med under kodegjennomgang.

Situasjon 1: Jeg må endre den siste forpliktelsen

Tenk deg et tilfelle der korrekturleseren så på styles.css.scss og foreslo en endring. I et slikt tilfelle er det veldig enkelt å gjøre endringen da stilarkendringene er en del av siste forpliktelse på grenen din. Her kan vi håndtere dette.

  • Du gjør de nødvendige endringene direkte til styles.css.scss i din filial.
  • En gang du er ferdig med endringene, legg til disse endringene på scenen; kjør git add styles.css.scss.
  • Når endringene er iscenesatt, må vi legge til disse endringene i vår siste forpliktelse; kjør git commit --amend.
    • Kommandosammenbrudd: Her ber vi kommandoen git commit om å endre de endringene som er tilstede i trinn til den siste forpliktelsen.
  • Dette åpner den siste forpliktelsen i den Git-definerte teksteditoren din som har forpliktelsesmeldingen Legg til stiler for navigering.
  • Siden vi bare oppdaterte CSS-erklæringen, trenger vi ikke endre forpliktelsesmeldingen.På dette tidspunktet kan du bare lagre og avslutte teksteditoren som Git åpnet for deg, og endringene dine vil gjenspeiles i forpliktelsen.

Siden du endret en eksisterende forpliktelse, er disse endringene påkrevd å bli tvunget til din eksterne repo ved hjelp av git push --force-with-lease <remote_name> <branch_name>. Denne kommandoen overstyrer forpliktelsen Add styles for navigation på ekstern repo med oppdatert forpliktelse som vi nettopp gjorde i vårt lokale repo.

En ting å huske på mens du presser på grener er at hvis du jobber med den samme grenen med flere personer, kan pressing av tvang føre til problemer for andre brukere når de prøver å normalt skyve endringene sine på en ekstern gren som har ny presset kraft. Bruk derfor denne funksjonen klokt. Du kan lære mer om Git force push-alternativer her.

Situasjon 2: Jeg trenger å endre en spesifikk forpliktelse

I den forrige situasjonen var løsningen ganske enkel da vi måtte endre bare vår siste Git-forpliktelse, men tenk deg om anmelderen foreslo å endre noe i _navigation.html.haml. I dette tilfellet er det andre forpliktelse fra toppen, så det å endre det vil ikke være like direkte som det var i den første situasjonen. La oss se hvordan vi kan håndtere dette:

Når en forpliktelse er laget i en gren, er det identifisert av en unik SHA1 hash-streng. Tenk på det som en unik ID som skiller en forpliktelse fra en annen. Du kan se alle forpliktelsene, sammen med SHA1-hashene i en gren ved å kjøre git log. Med dette vil du se en utgang som ser noe ut som følger, der de siste forpliktelsene er øverst;

Det er her git rebase kommandoen spiller inn. Når vi ønsker å redigere en spesifikk forpliktelse med git rebase, må vi først basere grenen vår på nytt ved å flytte HEAD tilbake til punktet rett før forpliktelsen vi ønsker å redigere. I vårt tilfelle må vi endre forpliktelsen som leser Page Navigation View.

Legg merke til hash av forpliktelse som er rett før forpliktelsen vi vil endre; kopier hashen og utfør følgende trinn:

  • Sett grenen på nytt for å flytte til å begå før målet forplikter oss; kjør git rebase -i 8d74af102941aa0b51e1a35b8ad731284e4b5a20
    • Kommandooppdeling: Her kjører vi Git «s rebase kommando med interaktiv modus med gitt SHA1-hash som forplikte seg til å rebase til.
  • Dette kjører rebase-kommandoen for Git i interaktiv modus og åpner teksteditoren din som viser alle dine forpliktelser som kom etter forpliktelsen du rebased til . Det vil se litt slik ut:

Legg merke til hvordan hver kommisjon har et ord pick foran den, og i innholdet nedenfor, er det alle mulige nøkkelord vi kan bruke. Siden vi vil redigere en forpliktelse, må vi endre pick 4155df1cdc7 Page Navigation View til edit 4155df1cdc7 Page Navigation View. Lagre endringene og avslutte redigeringsprogrammet.

Nå er grenen din rebasert til tidspunktet rett før forpliktelsen du gjorde som inkluderte _navigation.html.haml. Åpne filen og utfør ønskede endringer i henhold til tilbakemeldingen. Når du er ferdig med endringene, kan du iscenesette dem ved å kjøre git add _navigation.html.haml.

Siden vi har iscenesatt endringene, er det på tide å flytte grenen HEAD tilbake til forplikte vi opprinnelig hadde (mens vi også inkluderer de nye endringene vi la til), kjør git rebase --continue, dette åpner standardeditoren din i terminalen og viser deg kommittemeldingen som vi redigerte under rebase; Page Navigation View. Du kan endre denne meldingen hvis du vil, men vi vil la den være som den er for nå, så lagre og gå ut av redaktøren. På dette tidspunktet vil Git spille av alle forpliktelsene som fulgte etter forpliktelsen du nettopp redigerte og nå er grenen HEAD tilbake til den øverste forpliktelsen vi opprinnelig hadde, og den inkluderer også de nye endringene du gjorde i en av forpliktelsene.

Siden vi igjen modifiserte en forpliktelse som allerede er til stede i ekstern repo, trenger vi å skyve denne grenen igjen ved å bruke git push --force-with-lease <remote_name> <branch_name>.

Situasjon 3 : Jeg må legge til, fjerne eller kombinere forpliktelser

En vanlig situasjon er når du har gjort flere forpliktelser bare for å fikse noe som er tidligere begått. La oss nå redusere dem så mye vi kan, og kombinere dem med de opprinnelige forpliktelsene.

Alt du trenger å gjøre er å starte den interaktive rebasen som du ville gjort i de andre scenariene.

Tenk deg at du vil kombinere alle disse løsningene i c22a3fa0c5c Render navigation partial. Du trenger bare å:

  1. Flytt opprettelsene slik at de er rett under forpliktelsen du vil beholde til slutt.
  2. Endre pick til squash eller fixup for hver av rettelsene.

Merk: squash beholder kommisjonsmeldingene i beskrivelsen. fixup vil glemme kommisjonsmeldingene til rettelsene og beholde originalen.

Du vil ende opp med noe sånt som dette:

Lagre endringene, gå ut av redigeringsprogrammet , og du er ferdig! Dette er den resulterende historikken:

Som før er alt du trenger å gjøre nå git push --force-with-lease <remote_name> <branch_name> og endringene er oppe.

Hvis du vil fjerne forpliktelse fra grenen helt, i stedet for squash eller fixup, bare skriv drop eller bare slett den linjen.

Unngå konflikter

For å unngå konflikter, sørg for at forpliktelsene er for deg «vi beveger oss oppover tidslinjen ikke berører de samme filene som berøres av forpliktelsene som er igjen etter dem.

Pro-tip : Quick fixups

Hvis du vet nøyaktig hvilket forpliktelse du vil fikse, trenger du ikke å kaste bort hjernesykluser når du forplikter deg til å tenke på gode midlertidige navn for «Fix 1», «Fix 2», …, «Løs 42».

Trinn 1: Møt --fixup

Etter at du har iscenesatt endringene som løser hva det er som må fikses, bare Git begår alle endringene slik:

(Vær oppmerksom på at dette er hash for forpliktelsen c22a3fa0c5c Render navigation partial)

Dette vil generere denne forpliktelsesmeldingen: fixup! Render navigation partial.

Trinn 2: Og sidemannen --autosquash

Enkel interaktiv rebase. Du kan ha git plassere fixup s automatisk på rett sted.

git rebase -i 4155df1cdc7 --autosquash

Historikken vises slik:

Klar for deg å bare gjennomgå og fortsett.

Hvis du føler deg eventyrlysten, kan du gjøre en ikke-interaktiv rebase git rebase --autosquash, men bare hvis du liker å leve farlig, slik du vil har ingen mulighet til å gjennomgå squashene som blir laget før de blir brukt.

Situasjon 4: My Git commit-historien gir ikke mening, jeg trenger en ny start!

Hvis vi «arbeider med en stor funksjon, er det vanlig å ha flere endringer i reparasjon og tilbakemelding som blir begått ofte. I stedet for å kontinuerlig gjøre om grenen, kan vi la opprydding av forpliktelser være til slutten av utviklingen.

Det er her det er veldig nyttig å lage oppdateringsfiler. Oppdateringsfiler var faktisk den viktigste måten å dele kode via e-post mens du samarbeidet på store open source-prosjekter før Git-baserte tjenester som GitLab var tilgjengelige for utviklere. Tenk deg at du har en slik gren (f.eks. add-page-navigation) der det er mange forpliktelser som ikke overfører de underliggende endringene tydelig. Slik kan du lage en oppdateringsfil for alle endringene du gjorde i denne grenen:

  • Det første trinnet for å lage oppdateringsfilen er å sørge for at grenen din har alle endringene fra master filial og har ingen konflikter med det samme.
  • Du kan kjøre git rebase master eller git merge master mens du «sjekket ute i add-page-navigation gren for å få alle endringene fra master til din gren.
  • Opprett nå oppdateringsfilen ; kjør git diff master add-page-navigation > ~/add_page_navigation.patch.
    • Kommandooversikt: Her bruker vi Gits diff-funksjon, og ber om en forskjell mellom master gren og add-page-navigation gren, og omdirigerer utgangen (via > symbol) til en fil med navnet add_page_navigation.patch i vår oss er hjemmekatalog (vanligvis ~/ i * nix-operativsystemer).
  • Du kan angi hvilken vei du vil beholde denne filen. inn og filnavnet og filtypen kan være hva du vil.
  • Når kommandoen er kjørt og du ikke ser noen feil, blir oppdateringsfilen generert.
  • Nå til kassen master gren; kjør git checkout master.
  • Slett grenen add-page-navigation fra lokal repo; kjør git branch -D add-page-navigation. Husk at vi allerede har endringer i denne grenen i en opprettet oppdateringsfil.
  • Opprett nå en ny gren med samme navn (mens master er sjekket ut); kjør git checkout -b add-page-navigation.
  • På dette tidspunktet er dette en frisk gren og har ingen av endringene dine.
  • Til slutt, bruk endringene fra oppdateringsfilen; git apply ~/add_page_navigation.patch.
  • Her blir alle endringene dine brukt i en gren, og de vil fremstå som uforpliktende, som om alle endringene dine hvor det er gjort, men ingen av endringene ble faktisk begått i grenen.
  • Nå kan du fortsette og begå individuelle filer eller filer gruppert etter påvirkningsområde i den rekkefølgen du vil ha med kortfattede kommisjonsmeldinger.

Som med tidligere situasjoner, modifiserte vi i utgangspunktet hele grenen, så det er på tide å tvinge push!

Konklusjon

Selv om vi har dekket de vanligste og grunnleggende situasjonene som oppstår i en daglig arbeidsflyt med Git, er omskriving av Git-historien et enormt tema og som du blir kjent med ovenfor tips, kan du lære mer avanserte konsepter rundt emnet i Git Official Documentation. Happy git «ing!

Foto av pan xiaozhen på Unsplash

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *