git rebase: paano ito gumagana, interactive mode, at squash ng commit
Inililipat ng git rebase ang mga commit ng kasalukuyang branch sa bagong base: pansamantalang kinukuha ng Git ang inyong mga commit, inililipat ang branch sa dulo ng tinukoy na branch, at muling ina-apply ang mga ito bilang bagong commit. Sa ganitong paraan, nagkakaroon ng linear na history nang walang merge commit; at sa git rebase -i, puwedeng pagsamahin (squash), palitan ng pangalan, o burahin ang mga commit. Isulat lang ulit ang mga commit na hindi pa nai-publish sa iba.
Ano ang ginagawa ng git rebase
git rebase ay "inililipat" ang inyong branch sa ibang punto ng history. Ipagpalagay na humiwalay kayo sa feature mula sa main noong isang linggo, at umusad na ang main mula noon. Pagkatapos ng rebase, parang nagsimula kayo ng trabaho ngayon lang, mula sa pinakasariwang commit ng main.
git switch feature
git fetch origin
git rebase origin/main
Sa ilalim, tatlong hakbang ang nangyayari. Hahanapin ng Git ang karaniwang ninuno ng dalawang branch, pansamantalang ise-save ang pagbabago ng bawat commit ninyo pagkatapos nito, ililipat ang branch sa origin/main, at muling ia-apply ang mga naka-save na pagbabago, isa-isa. Ang resulta ay mga bagong commit na may parehong nilalaman at mensahe, pero ibang hash.
Bago: A---B---C---F---G main
\
D---E feature
Pagkatapos: A---B---C---F---G main
\
D'---E' feature
Ang mga tandang D' at E' ay hindi palamuti. Ang mga ito ay ibang object na may ibang hash, at iba ang parent nila. Dito nakasalalay ang pangunahing tuntunin ng rebase, na tatalakayin sa ibaba.
Gintong tuntunin: huwag i-rebase ang nai-publish na
Isinusulat ulit ng rebase ang history. Ligtas ito habang kayo lang ang may hawak ng commit. Pero kung nakadownload na ng branch ang mga katrabaho, pagkatapos ng rebase, nasa kanila pa rin ang lumang D at E, samantalang nasa inyo na ang bagong D' at E'. Sa susunod na pull, madodoble ang history, at kailangang ayusin ulit ang mga conflict.
Praktikal na tuntunin: ang rebase ay ginagamit sa sarili ninyong lokal na branch at personal na feature branch na iisang tao lang ang gumagawa. Hindi kailanman nire-rebase ang shared na branch gaya ng main, develop, at release branch.
Paano ipadala ang branch pagkatapos ng rebase
Kung nasa server na ang branch, tatanggihan ang ordinaryong git push, dahil hindi na ipinagpapatuloy ng lokal na history ang nasa remote. Kailangan ng force push, at ang ligtas na bersyon ang gamitin:
git push --force-with-lease
Hindi tulad ng --force, tatanggi ang --force-with-lease na i-overwrite ang branch kung may commit sa server na hindi ninyo nakita. Sa ganitong paraan, hindi ninyo aksidenteng mabubura ang trabaho ng iba.
Mga conflict sa rebase
Dahil isa-isang ina-apply ang mga commit, maaaring magkaroon ng conflict sa alinman sa mga ito, at puwedeng ilang beses sunod-sunod. Hihinto ang Git at ipapaalam kung aling commit ang hindi na-apply.
$ git rebase origin/main
Auto-merging src/api.js
CONFLICT (content): Merge conflict in src/api.js
error: could not apply 4e1f9a2... Idagdag ang cache ng request
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
Ang gagawin:
- Buksan ang file na may conflict, piliin ang tamang bersyon, at burahin ang mga marker na
<<<<<<<,=======,>>>>>>>. - Markahan ang file bilang naayos na:
git add src/api.js. Hindi kailangang mag-commit. - Magpatuloy:
git rebase --continue. Ia-apply ng Git ang susunod na commit.
Iba pang command para kontrolin ang proseso:
| Command | Ano ang ginagawa |
|---|---|
git rebase --continue | Magpatuloy pagkatapos ayusin ang conflict |
git rebase --skip | Laktawan ang kasalukuyang commit nang buo (mawawala ang mga pagbabago nito) |
git rebase --abort | Kanselahin ang rebase at ibalik ang branch sa dating estado |
Pansinin ang kalituhan sa "amin" at "kanila" na panig. Habang nag-rebase, ang HEAD ay ang base na pinaglilipatan ninyo ng branch, at ang "papasok" na pagbabago ay ang sarili ninyong commit. Kaya sa rebase, ang git checkout --ours ay kumukuha ng bersyon mula sa main, at ang --theirs ay ang sarili ninyo. Sa merge, baligtad ang lahat.
Paulit-ulit na conflict: rerere
Kung regular ninyong ni-re-rebase ang mahabang branch at paulit-ulit ninyong inaayos ang iisang conflict, i-enable ang git config --global rerere.enabled true. Tatandaan ng Git kung paano ninyo inayos ang conflict, at ilalapat niya ang solusyon nang awtomatiko sa susunod.
Interactive rebase: ayusin ang history bago ilathala
Ang pinakamahalagang bahagi ng rebase ay ang interactive mode. Pinapayagan ka nitong ayusin ang sarili ninyong mga commit bago ipakita sa review: pagsamahin ang "fix", "fix typo", "isa pang fix" sa iisang makabuluhang commit, isulat ulit ang mga mensahe, baguhin ang pagkakasunod-sunod, o tanggalin ang hindi kailangan.
git rebase -i HEAD~4 # huling 4 na commit
git rebase -i origin/main # lahat ng commit ng branch na wala sa main
Magbubukas ang Git ng editor na may listahan ng mga commit, mula luma hanggang bago (kabaligtaran ng git log):
pick 1a2b3c4 Idagdag ang login form
pick 5d6e7f8 fix
pick 9a0b1c2 Validation ng e-mail
pick 3d4e5f6 typo sa validation
# Commands:
# p, pick = gamitin ang commit
# r, reword = gamitin ang commit, pero i-edit ang mensahe
# e, edit = gamitin ang commit, pero huminto para mag-amend
# s, squash = gamitin ang commit, pero pagsamahin sa nauna
# f, fixup = gaya ng "squash" pero panatilihin lang ang mensahe ng nauna
# d, drop = alisin ang commit
Palitan ang salita sa simula ng linya, i-save, at isara ang editor. Isasagawa ng Git ang plano mula itaas pababa.
| Command | Epekto |
|---|---|
pick | Panatilihin ang commit gaya ng dati |
reword | Panatilihin ang pagbabago, pero isulat ulit ang mensahe |
edit | Huminto sa commit para baguhin ang nilalaman nito (git commit --amend) o hatiin ito sa ilan |
squash | Isama sa nakaraang commit; pagsamahin at i-edit ang mga mensahe |
fixup | Isama sa nakaraang commit; itapon ang mensahe ng commit na ito |
drop | Burahin ang commit kasama ang mga pagbabago nito |
Ang pagpapalit ng pagkakasunod-sunod ng linya ay nagbabago ng ayos ng mga commit, at ang pagbura ng linya ay katumbas ng drop.
Git squash: paano pagsamahin ang mga commit sa isa
Walang hiwalay na command na git squash sa Git. May tatlong paraan para mag-"squash" ng mga commit, at ang interactive rebase ang pinakamaraming gamit.
Paraan 1. Interactive rebase
Para sa halimbawa sa itaas, gawing dalawang maayos na commit ang apat:
pick 1a2b3c4 Idagdag ang login form
fixup 5d6e7f8 fix
pick 9a0b1c2 Validation ng e-mail
fixup 3d4e5f6 typo sa validation
Dapat manatiling pick ang unang commit sa bawat grupo: isinasama ng squash at fixup ang commit sa nauna, at walang nauuna sa pinakaunang commit.
Paraan 2. Mga fixup commit at autosquash
Kung alam na ninyo kung aling commit ang aayusin, gumawa ng pagwawasto na agad na may marka:
git commit --fixup=1a2b3c4
git rebase -i --autosquash origin/main
Ilalagay ng Git ang fixup commit sa ilalim ng tamang commit at mamarkahan ito bilang fixup; kailangan na lang i-save ang plano. Para gawing default ang asal na ito, gamitin ang git config --global rebase.autoSquash true.
Paraan 3. Gawing iisang commit ang buong branch
git reset --soft $(git merge-base HEAD origin/main)
git commit -m "Login gamit ang e-mail na may validation"
Ibinabalik ng reset --soft ang pointer ng branch sa punto ng paghihiwalay, pero nananatili ang lahat ng pagbabago sa index; isang commit na lang ang kailangang gawin. Isa pang opsyon ay ang git merge --squash sa pagsasama sa pangunahing branch, o ang button na "Squash and merge" sa pull request.
Mga kapaki-pakinabang na variant ng rebase
git pull --rebase
Karaniwang nagsasama ang pull ng remote branch sa inyong branch, at gumagawa ng merge commit kapag sabay kayong nag-commit ng katrabaho. Sa halip, inilalagay ng git pull --rebase ang inyong lokal na commit sa ibabaw ng mga bagong remote. Gawin itong default: git config --global pull.rebase true. Ito ang ligtas na lugar para sa rebase: hindi pa nakikita ng iba ang inyong lokal na commit.
--onto: ilipat ang bahagi ng branch
Humiwalay kayo sa feature-b mula sa feature-a, at ngayon gusto ninyong magmula ang feature-b direkta sa main, nang walang commit ng feature-a:
git rebase --onto main feature-a feature-b
Ang kahulugan: kunin ang mga commit ng feature-b na wala sa feature-a, at ilipat ang mga ito sa main.
--exec: suriin ang bawat commit
git rebase -i --exec "npm test" origin/main
Pagkatapos ma-apply ang bawat commit, tatakbo ang test; kung pumalya ito, hihinto ang rebase sa problemadong commit. Kapaki-pakinabang kapag mahalagang mag-build ang bawat commit sa history.
--update-refs: sunud-sunod na branch
Kung may kadena kayo ng magkakadependent na branch, sa flag na --update-refs (Git 2.38+), kapag ni-rebase ang nasa itaas, ililipat din ang mga nasa gitna.
Paano i-undo ang rebase
Habang tumatakbo pa ang proseso: git rebase --abort. Kung tapos na ang rebase at hindi ninyo gusto ang resulta, nandoon pa rin ang lumang commit: naaalala ito ng reflog.
$ git reflog
e5f6a7b HEAD@{0}: rebase (finish): returning to refs/heads/feature
e5f6a7b HEAD@{1}: rebase (pick): Validation ng e-mail
...
c3d4e5f HEAD@{5}: commit: typo sa validation
$ git reset --hard HEAD@{5}
Hanapin sa git reflog ang entry bago magsimula ang rebase, at i-reset ang branch dito. Kaagad pagkatapos ng rebase, may mas maikling daan din: git reset --hard ORIG_HEAD. Lokal na nakaimbak ang reflog at karaniwang tumatagal ng humigit-kumulang 90 araw ang mga entry, kaya hindi kailangang mag-panic pagkatapos ng hindi maayos na rebase.
Karaniwang pagkakamali
- Na-rebase ang shared na branch at nag-force push: nagkagulo ang history ng mga katrabaho. Ang ayos: magkasundo, ibalik ang branch mula sa reflog ng may tamang bersyon, at ipagbawal ang force push sa mga protektadong branch sa server.
- Ginamit ang
--forcesa halip na--force-with-lease, at nabura ang commit na naipadala ng katrabaho sa parehong branch. - Squash sa unang linya ng interactive plan: magbibigay ang Git ng error na "cannot 'squash' without a previous commit".
- Nagsimula ng rebase na may maruming working directory. Tatanggi ang Git na magsimula. I-commit ang mga pagbabago, itabi sa
git stash, o gamitin anggit rebase --autostash. - Nag-commit sa halip na
git rebase --continuepagkatapos ayusin ang conflict. Lumilikha ito ng dagdag na commit; maaari itong alisin sa pamamagitan ng isa pang interactive rebase.
Buod
Inililipat ng git rebase ang inyong mga commit sa bagong base at ginagawa itong muli, kaya nagiging linear at malinis ang history. Ginagawang maayos na history ng interactive mode ang mga draft na commit: pinagsasama ng squash at fixup ang mga pagbabago, inaayos ng reword ang mga mensahe, at inaalis ng drop ang hindi kailangan. Iisa ang pangunahing tuntunin: i-rebase lang ang hindi pa naipapadala sa iba, at pagkatapos mag-rebase ng sarili ninyong remote branch, ipadala ito gamit ang --force-with-lease. Ang rebase at paglilinis ng commit history ay itinuturo nang praktikal sa kursong Git namin.
Mga madalas itanong
Binabago ba ng git rebase ang nilalaman ng code?
Kapag walang conflict, ang panghuling nilalaman ng mga file ay magiging kapareho ng sa merge, pero nire-recreate ang mga commit na may bagong hash at bagong parent. Kung sa conflict ay pumili kayo ng ibang bersyon, magkakaiba ang mga intermediate commit mula sa orihinal.
Paano i-rebase ang branch sa pinakaunang commit ng repository?
Gamitin ang git rebase -i --root: kasama sa plano ang lahat ng commit, kasama ang root. Bihira itong kailangan, halimbawa para isulat ulit ang mensahe ng pinakaunang commit.
Pinananatili ba ng rebase ang author at petsa ng commit?
Napapanatili ang author at petsa ng authorship, pero nagiging kasalukuyan ang committer at petsa ng commit. Ang flag na --committer-date-is-author-date ang nagpapanatili ng petsa ng commit na kapareho ng petsa ng authorship.
Puwede bang i-rebase ang mga merge commit?
Bilang default, pinapatag ng rebase ang history at inaalis ang mga merge commit. Para mapanatili ang istruktura ng merge, gamitin ang git rebase --rebase-merges.
Paano malalaman kung nasa gitna ng rebase ang repository?
Ipinapakita ng git status ang linyang "interactive rebase in progress" o "You are currently rebasing", at sinasabi kung anong mga command ang available. Maraming shell at IDE din ang nagpapakita nito sa prompt.
Detalyadong tinatalakay ang paksang ito, may praktis, sa kursong “Git с нуля до уверенного разработчика”.
Pumunta sa kurso