Ako već neko vrijeme koristite Git, vjerovatno ste naišli na historiju punu beskorisnih commit-ova, "WIP" poruka, brzih testova ili čak praznih commit-ova kreiranih samo da bi se pokrenuo hook ili pipeline. U tom trenutku, pogledate log i pomislite: "Niko ovo ne može razumjeti"Dobra vijest je da niste sami: to se dešava svima nama, i zato i postoji podrška. git rebase.
Zanimljivo je da, kada se pravilno koristi, git rebase vam omogućava da "očistite" historiju commit-ovaUčinite ga linearnim, bez šuma i mnogo lakšim za pregled. Možete brisati testne commitove, spajati ih nekoliko u jedan, mijenjati njihov redoslijed, ispravljati poruke commita, pa čak i uklanjati promjene sa udaljene lokacije putem prisilnog pusha. Pogledajmo, s konkretnim primjerima, kako koristiti rebase za transformaciju vaše historije iz potpunog haosa u nešto organizirano i čitljivo.
Šta je tačno Git Rebase i zašto utiče na istoriju?
Kada govorimo o preticanju, doslovno govorimo o promijeniti početnu tačku graneGit uzima commitove iz vaše grane i "reproducira" ih jedan po jedan na drugoj bazi (obično grana glavni ili ažuriranu udaljenu granu). Kao da ste se vratili u prošlost i započeli svoj rad s drugog commita, ali ste zadržali promjene.
Zamislite da vaš projekat ima ovu priču u glavnoj grani: A – B – CKreirate granu funkcionalnosti, a zatim pravite dva commita: D-EU međuvremenu, u glavnom dijelu, neko dodaje novi commit FBez ponovnog zasnivanja, vaše grane bi izgledale otprilike ovako:
A---B---C---F (main)
A---B---C---D---E (feature)
Ako uradite klasično spajanje vaše feature grane u main, ono što dobijete je dodatni merge commit, koji često generiše zamršena historija s nekoliko ukrštenih linija. S druge strane, kod rebase-a, Git uzima vaše commit-ove D i E i ponovo ih primjenjuje na F:
A---B---C---F---D'---E' (feature reescrita)
Oni D i E Ovo nisu baš isti commit-ovi kao D i E: to su "kopije" sa novim identifikatorima (hash-ovima). Zato kažemo rebase. prepisuje historijuRezultat je linearniji zapisnik, bez među-merge commit-ova koji samo dodaju šum.

Zašto biste trebali imati čistu historiju commit-ova
Organizirana evidencija nije samo pitanje estetike. Čist dnevnik znatno olakšava svakodnevni rad.Možete na prvi pogled vidjeti šta je urađeno, kada i zašto, bez potrebe da preskačete prazne commitove spajanja ili poruke o "brzim popravkama" bez konteksta.
Kada se grane spajaju stalnim spajanjem iz glavne grane, završite sa gomilom commitova poput „Spoji granu 'main' u feature-x“ koji Ne pružaju stvarne informacije o promjenama kodaOvo komplikuje zadatke kao što su:
- Pronađite commit u kojem je uvedena greška koristeći alati za poređenje datotekajer je historija puna nebitnih spajanja.
- Pregledajte zahtjeve za povlačenjemjer morate skakati između redundantnih commitova da biste vidjeli šta se zapravo promijenilo.
- Razumijevanje evolucije karakteristikeposebno ako je razvijan kroz mnogo malih testnih commitova.
Git rebase je idealan alat za "poliranje" sve te buke prije dijeljenja grane s ostatkom tima. To je kao da svoju historiju pereš u mašini za pranje veša.Važne promjene zadržavate, dobro grupirane i s jasnim porukama.
Ključne razlike između git merge-a i git rebase-a
Da biste u potpunosti razumjeli šta radite kada koristite rebase, vrijedi uporediti njegovo ponašanje sa ponašanjem git mergeOba služe za integraciju promjena, ali na različite načine i sa važnim implikacijama za zapis.
con spojitiGit kreira novi merge commit koji ima dva roditelja: vrh vaše grane i vrh grane s kojom se spajate. Rezultirajuća historija čuva tačno originalni redoslijed commitovaAli može završiti puno paralelnih grana i spajanja.
con overrunUmjesto kreiranja merge commit-a, Git uzima vaše commit-ove i primjenjuje ih jedan po jedan na novu bazu podataka. Ovo generira novi commiti s novim hashovimačak i ako su promjene koda iste.
U praksi:
- Spoji se Održava historiju tačno onako kako se dogodila, sa svim njenim unakrsnim vezama i spajanjima.
- Rebase Generira linearnu i čistu historiju, kao da se sve dogodilo u pravoj liniji.
Izbor nije "jedno je bolje od drugog", već Za koju situaciju je svaki od njih prikladniji?Za kombinovanje već dijeljenih i zatvorenih grana, spajanje je obično sigurnije. Za održavanje organizacije lokalnih radnih grana ili prije otvaranja zahtjeva za povlačenjem, rebase je odličan izbor.

Slučajevi u kojima rebase blista: ažuriranje i poliranje grana
Postoje dva scenarija u kojima većina programera svakodnevno koristi rebase: Održavajte svoju radnu granu ažurnom s glavnom y Očistite commitove prije nego što ih podijelitePogledajmo ih detaljnije.
Ažurirajte svoju granu feature s najnovijim promjenama iz glavne grane
Radite na novoj funkciji u svojoj grani, ali u međuvremenu vaše kolege i dalje prave commit-ove u glavniAko želite integrirati svoje promjene, jedna od opcija je:
git checkout tu-rama-feature
git merge main
Ovo funkcioniše, ali generiše merge commit svaki put kada sinhronizujete, što na kraju uzrokuje probleme. historija puna ponavljajućih spajanjaMeđutim, ako to uradite:
git checkout tu-rama-feature
git rebase main
Git će premjestiti vaše commitove preko posljednjeg stanja main-a, kao da ste pokrenuli granu nakon tih promjena. Dobit ćete linearna historija, bez među-merge commit-ovai pregled će biti jasniji.
Očistite svoje commit-ove prije otvaranja pull requesta
Vrlo je uobičajeno da tokom razvoja neke funkcije završite s izmjenama poput "ispravka tipografskih grešaka", "testiranje procesa", "više promjena u prijavi" itd. To je u redu dok radite, ali To nije vrsta historije koju želite prikazati. kada otvorite PR.
Interaktivno ponovno baziranje vam omogućava reorganizirajte i grupirajte te commitove u nešto logičnije. Na primjer, umjesto ovoga:
- WIP: añadir login
- Más cambios login
- Corregir tests login
- Arreglar typo variable
Možete završiti s jednim, dobro opisanim commitom:
- Añadir funcionalidad completa de login de usuario con tests
To "poliranje" historije znatno olakšava recenzentima razumijevanje. šta svaki commit doprinosi i pojednostavljuje rješavanje problema u budućnosti.
Interaktivno rebase za prepisivanje historije
Osnovna verzija rebase-a jednostavno premješta vaše commit-ove na drugu bazu. Ali dragulj u kruni je... interaktivna nadbaza, što otvara editor sa listom nedavnih commitova tako da možete odlučiti šta ćete uraditi sa svakim od njih.
Tipičan način za početak ovoga je specificiranjem koliko commitova unazad želite "dotaknuti" iz vašeg trenutnog HEAD-a. Na primjer:
git rebase -i HEAD~6
Ova naredba govori Gitu: „uzmi posljednjih 6 commit-ova iz ove grane i pripremi interaktivnu rebase za mene.“ Git će otvoriti vaš zadani editor (Vim, Nano, VS Code, itd.) sa nečim poput:
pick 7ed9c6e update version
pick ecb7ef3 empty commit 1
pick a323615 empty commit 2
pick 2c3d41d empty commit 3
pick d53c00f empty commit 4
pick 22dcc79 empty commit 5
# Rebaziraj 549dd76..22dcc79 na 549dd76 (6 naredbi)
#
# Komande:
#p, izbor = koristi commit
# r, preformulisati = Koristite commit, ali uredite poruku
# e, uredi = Koristite commit i stop za izmjenu
# s, tikva = spajanje u prethodnom commitu
# f, popravak = kao squash, ali odbacivanje poruke
# x, izvršni = izvrši shell naredbu
# b, break = zaustavljanje rebase-a ovdje
# d, ispuštanje = obriši commit
#…
Obratite pažnju na jedan važan detalj: Redoslijed u ovoj datoteci je obrnut od onoga što vidite u git loguU datoteci, prvi navedeni commit (7ed9c6e) je najstariji od šest, a posljednji (22dcc79) je najnoviji. Ovo je ključno za izbjegavanje zabune prilikom brisanja ili promjene redoslijeda commitova.
Uklonite testne ili prazne commitove iz lokalne historije
Pretpostavimo da, kako biste testirali Git hook, pravite prazne commit-ove sa opcijom –dozvoli-prazno. Na primjer:
git commit -m "commit with no changes" --allow-empty
Ova opcija vam omogućava da kreirate commit čak i kada nema promjena u datotekama, što je korisno za testiranje, ali To zatrpava historiju commitima koji nemaju stvarnu vrijednost.Zamislite da vaš dnevnik izgleda ovako:
git log --pretty=oneline --abbrev-commit
I dobijate:
22dcc79 (HEAD -> main, origin/main, origin/HEAD) empty commit 5
d53c00f empty commit 4
2c3d41d empty commit 3
a323615 empty commit 2
ecb7ef3 empty commit 1
7ed9c6e update version
U ovom scenariju, želite zadržati samo korisni commit "ažuriranje verzije" i ukloniti sve ostale. prazan commit što su bili samo testovi. Da biste to uradili, pokrećete interaktivno rebase na posljednjih 6 commit-ova:
git rebase -i HEAD~6
Editor se otvara sa 6 commitova. Vaš cilj je ukloniti redove koji odgovaraju praznim commitima. To jest, ostavljate samo nešto poput ovoga:
pick 7ed9c6e update version
# Rebaziraj 549dd76..22dcc79 na 549dd76 (6 naredbi)
# … ostali komentari …
Kao što je naznačeno u odjeljku za pomoć same datoteke, Ako izbrišete liniju, ta promjena (commit) će nestati iz historije. U novoj, prepisanoj verziji, prilikom spremanja i zatvaranja editora, Git će reproducirati samo commit "ažurirana verzija" i odbaciti ostale.
Otpremanje nove priče u Origin: korištenje prisilnog slanja
Do sada su sve promjene rebase-a napravljene u vašoj lokalnoj kopiji repozitorija. Ako želite da se to čišćenje odrazi i na udaljeni repozitorij (na primjer, u porijeklo/glavno), morat ćete prepisati udaljenu historiju novom.
Ovo se radi sa prisilni guranjeUobičajen način da se ovo označi je korištenje znaka "+" ispred imena grane prilikom slanja:
git push origin +main
Taj znak plus govori Gitu da Zanemarite odstupanje u historiji i prepišite udaljenu granu sa vašom prepisanom verzijom. Ako ste jednostavno pokušali uraditi:
git push origin main
Git bi vas upozorio da vaša lokalna historija nije direktni potomak udaljene (jer ste prepisali commitove sa rebase-om) i odbio bi push kako bi izbjegao gubitak podataka.
Važno je shvatiti da, nakon ovog prisilnog pritiska, Obrisane commit-ove više neće biti moguće pristupiti iz origin/mainMožda su još uvijek u reflogovima ili lokalnim kopijama od drugih kolega, ali iz praktičnih razloga ste izbrisali udaljenu historiju.
Ozbiljno upozorenje: prepisivanje dijeljenih grana nije igra
Prepisivanje historije zvuči odlično za organiziranje svega, ali ima ključnu posljedicu: prilikom kreiranja novih commitova s novim hešovima, Odbacujete sve koji su već zasnovali svoj rad na starim commitovima.Zato postoji ono čuveno zlatno pravilo:
Nemojte ponovo bazirati grane koje su već dijeljene i aktivno koriste drugi.
U praksi, ovo znači da je sigurno koristiti rebase na:
- Lokalne grane funkcija koje još niste objavili ili koje samo vi koristite.
- Nedavne obaveze one od kojih znate da niko drugi još ne zavisi.
I prilično je loša ideja to raditi u vezi sa:
- glavna, razvojna ili bilo koja "zvanična" grana iz repozitorija koji služi kao osnova za nekoliko ljudi.
- Zajedničke radne grane gdje postoji više od jednog programera koji pravi commit-ove.
Ako prepišete historiju dijeljene grane, a zatim izvršite prisilno slanje, drugi članovi tima će to otkriti Njegove grane pokazuju na commitove koji više ne postoje na udaljenom serveru.Morat će izvršiti naprednije operacije (kao što je ponovno baziranje na novoj historiji ili resetiranje) kako bi riješili problem.
Prisilno guranje: bolje sa sigurnosnim pojasom
U mnogim slučajevima, nakon rebase-a, morat ćete forsirati push. Klasična opcija je:
git push --force origin tu-rama
Međutim, ova varijanta prepisuje bilo šta na daljinskom upravljaču bez pitanja. Da biste izbjegli slučajno prepisivanje tuđeg rada, Git nudi mnogo bolju opciju: –prisila-uz-zakup.
Kada to uradite:
git push --force-with-lease origin tu-rama
Git prvo provjerava to Daljinski upravljač je još uvijek u stanju u kojem si mislio da jeste. Kada ste izvršili zadnji put "pull" ili "fetch". Ako se otkrije da je neko od tada poslao nove commit-ove u tu granu, push se odbija i ne forsira, čime se sprječava prepisivanje promjena drugih ljudi.
Ideja je kombinovati rebase i force push, uvijek hladne glave: samo u granama koje vi kontrolišete i korištenje, kad god je to moguće, –force-with-lease kao dodatnog sloja sigurnosti.
Šta učiniti kada rebase krene po zlu: reflog u pomoć
Svima nam se to dogodilo: pokrenete interaktivno rebaseiranje, slučajno nešto izbrišete ili promijenite, neispravno riješite konflikt i odjednom... Izgleda da si izgubio dio poslaPrije nego što paničite, sjetite se da Git ima aduta u rukavu: git reflog.
Reflog je lokalni zapis svih HEAD kretanja: novi commit-ovi, resetovanja, rebase-i, spajanja... Zahvaljujući njemu, možete locirati gdje je vaša grana bila prije nego što ste je pokvarili i vratiti se na tu tačku pomoću hard reseta.
Tipičan tok bi bio:
git reflog
Tamo ćete vidjeti listu unosa sa nečim poput:
abc1234 HEAD@{0}: rebase terminado
def5678 HEAD@{1}: checkout: moving from main to main
...
Identificirate commit na kojem ste bili prije početka rebase-a (na primjer, def5678) i vraćaš mu se sa:
git reset --hard def5678
Na ovaj način, Potpuno poništavate preklapanje i vraćate se u prethodno stanje. Također možete prekinuti tekuće ponovno baziranje (ako ste usred procesa i niste završili) jednostavno sa:
git rebase --abort
Ova naredba ostavlja vašu granu kakva je bila neposredno prije pokretanja trenutnog rebase-a, što je idealno za situacije kada se pojave konflikti koje ne želite ili kada vidite da nije dobro vrijeme za nastavak.
Git pull vs git pull –rebase: male razlike, veliki utjecaj
Još jedna stvar koja često izaziva sumnje je razlika između git pull normalno i git pull --rebaseOba ažuriraju vašu lokalnu granu sa udaljene lokacije, ali to rade na različite načine.
Kada trčite:
git pull
Git se izvršava u dva koraka: git fetch da prenese promjene sa daljinskog upravljača, a zatim git merge da ih kombinujete sa vašom trenutnom granom. Ovo može kreirati dodatni merge commit ako imate lokalne commitove iznad udaljene grane, što opet historija sa nepotrebnim spajanjima.
Međutim, ako pokrenete:
git pull --rebase
Git vrši dohvaćanje, a zatim Replicirajte svoje lokalne commit-ove preko ažurirane verzije na udaljenom serveru.Rezultat je sličan kao da ste uradili git fetch praćeno git rebase origin/maini održava čistiju, linearniju historiju.
Zato mnogi timovi konfigurišu Git da koristi rebase po defaultu prilikom povlačenja. To je jednostavan način da izbjegavanje automatskih spajanja (commit-ova) koje ne doprinose mnogo.
Brisanje datoteka ili slika iz historije: opšta ideja
Ponekad problem nije ružna potvrda, već datoteka koja nikada nije trebala stići do repozitorija: pogrešna slika, netačni podaci za prijavu, ogromne datoteke itd. Iskušenje je otići na GitHub i potražiti ikonu za smeće, ali ako se čini da je onemogućena i prikazuje poruke poput "morate biti na grani", to je zato što GitHub vam ne dozvoljava brisanje historije na taj način..
Ispravan način obično uključuje korištenje prepisivanja historije s vašeg lokalnog računara (s interaktivnim rebaseom ili alatima poput git filter-repo) a zatim izvršite prisilno slanje. U GitHub Desktopu također možete upravljati granama i commitovima, ali rad potpuno ukloni datoteku iz svih commitova Ovo podrazumijeva prepisivanje historije na sličan način kao što ovdje vidimo.
Ukratko, ako otpremite pogrešnu sliku ili osjetljivu datoteku i želite da ona "nestane" iz historije, jednostavno brisanje u posljednjem commitu nije dovoljno: Commit-ovi u kojima je to uvedeno moraju biti prepisani. a zatim prisilite guranje. To je delikatna operacija i treba je izvesti smireno i u koordinaciji sa svojim timom.
Praktičan protok za vježbanje preticanja bez oštećenja ičega
Najbolji način da postanete vješti u preticanju jeste da vježbate u kontroliranom okruženju, bez straha da ćete ometati tuđi rad. Jednostavan tok bi bio:
- Kreirajte novu granu iz glavne grane sa git checkout -b testovi-moje-grane.
- Napravite nekoliko malih promjena (to mogu biti trivijalne promjene ili testne datoteke).
- Simulirajte glavni napredak tako što ćete tamo napraviti nove commit-ove (ili zamoliti nekog drugog da to uradi).
- Vratite se na svoju testnu granu i pokrenite git rebase main da vidite kako su vaši commit-ovi repozicionirani.
- Pokušajte a git rebase -i GLAVNA_POSTAVKA~3 da kombinujete, preimenujete ili izbrišete bilo koji od najnovijih commitova.
Pomoću ove vježbe vidjet ćete kako Promijenite historiju prije i poslijeKako Git reaguje na konflikte i kako se možete vratiti na prethodna stanja koristeći reflog ako je potrebno. Što više prakse imate lokalno, to ćete biti sigurniji kada primjenjujete ove tehnike na stvarne grane projekta.
Savladavanje Git rebase-a vam omogućava da transformišete haotičnu istoriju, punu praznih commit-ova, nepotrebnih spajanja i besmislenih poruka, u jasan i čitljiv niz značajnih promjena. Korištenjem interaktivnog rebase-a, brisanjem testnih commit-ova, grupisanjem raspršenog rada, linearnim ažuriranjem grana i oslanjanjem na alate poput reflog-a i prisilnih push-ova sa `--force-with-lease`, možete održati svoj repozitorij u mnogo zdravijem i profesionalnijem stanju, sve dok se sjetite da ove tehnike primijenite samo na grane pod vašom kontrolom i obavijestite tim prije ponovnog pisanja dijeljene istorije.