git reset at git revert: paano i-undo ang commit nang hindi nasisira ang history
Ginagalaw ng git reset ang pointer ng branch pabalik at isinusulat muli ang history, kaya para lang ito sa lokal at hindi pa naipapadalang commit. Gumagawa naman ang git revert ng bagong commit na nagbabaligtad ng pagbabago, nang hindi ginagalaw ang history, kaya ito ang ligtas na paraan para i-undo ang commit na nasa server na.
Sa madaling sabi: iniuurong ng git reset ang branch at kumikilos na parang walang sobrang commit. Iniiwan ng git revert ang history gaya ng dati at nagdadagdag ng bagong commit na nagbabaligtad sa mga pagbabago ng luma. Ang una ay para sa lokal na trabaho; ang ikalawa ay para sa lahat ng nakarating na sa shared repository. Kung ito lang ang tandaan ninyo, maiiwasan na ang karamihan ng aksidente sa Git.
Sa ibaba, tatalakayin kung paano gumagana ang dalawang command, ano ang pagkakaiba ng mga mode ng reset, paano i-undo ang commit pagkatapos ng push, paano ibalik ang "nawalang" commit, at ano ang kinalaman ng git clean.
Tatlong antas na nagbabago sa mga command ng pag-undo
Para maintindihan ang pagkakaiba, alalahanin ang tatlong bahagi ng Git: working directory (mga file sa disk), index (staging area, ang papasok sa susunod na commit), at history (mga commit na tinuturo ng branch at HEAD). May sariling hanay ng mga bahaging ito ang bawat command ng pag-undo.
| Command | History | Index | Working directory | Ligtas para sa shared branch |
|---|---|---|---|---|
git restore file | hindi | hindi | oo | oo |
git restore --staged file | hindi | oo | hindi | oo |
git reset --soft | oo | hindi | hindi | hindi |
git reset --mixed (default) | oo | oo | hindi | hindi |
git reset --hard | oo | oo | oo | hindi |
git revert | nagdaragdag ng commit | — | — | oo |
git clean | hindi | hindi | binubura ang mga hindi tinatrack na file | oo, pero hindi na maibabalik |
Paano gumagana ang git reset
Inililipat ng git reset <commit> ang pointer ng kasalukuyang branch (at kasama nito ang HEAD) papunta sa tinukoy na commit. Hindi pisikal na nabubura ang mga commit na nasa "likod" nito, pero hindi na sila nakikita sa branch. Tinutukoy ng mode kung ano ang mangyayari sa index at mga file.
git reset --soft: i-undo ang commit, pero nasa index pa rin ang mga pagbabago
Nailipat ang branch, pero nananatili ang index at mga file gaya ng dati. Lahat ng pagbabago mula sa mga inatras na commit ay nakahanda na para sa commit. Ito ang perpektong paraan para palitan ang mensahe o pagsamahin ang ilang huling commit sa isa.
git reset --soft HEAD~3
git commit -m "feat: form ng registration"
Naging isang commit ang huling tatlong commit, at hindi nagbago ang nilalaman ng mga file.
git reset --mixed: i-undo ang commit at ang index
Ito ang default na mode: ang git reset HEAD~1 ay katumbas ng git reset --mixed HEAD~1. Nailipat ang branch, na-reset ang index, at nananatili ang mga pagbabago sa working directory bilang hindi pa nahahanda. Kapaki-pakinabang kapag may nakommit kayong hindi dapat, at gusto ninyong pag-isipang muli kung ano at sa anong bahagi idadagdag.
git reset HEAD~1
git status
# Changes not staged for commit:
# modified: app.py
# modified: config.yml
git reset --hard: ganap na rollback
Ang branch, index, at working directory ay ibabalik sa estado ng tinukoy na commit. Lahat ng hindi pa naka-commit na pagbabago sa mga tinatrack na file ay mawawala nang walang babala. Ito lang ang mode ng reset na talagang makakasira ng trabaho ninyo.
git reset --hard HEAD~1 # itapon ang huling commit at lahat ng edit
git reset --hard origin/main # ibalik ang branch sa estado ng server
Bago ang --hard, makabubuting patakbuhin ang git status at git stash kung may mahalaga. Maibabalik ang naka-commit (tingnan sa ibaba), pero ang hindi pa naka-commit ay hindi na.
git reset para sa isang file
Kapag may path, hindi ginagalaw ng reset ang branch, binabago lang nito ang index: ang git reset app.py ay inaalis ang file sa mga nahanda na. Ang modernong katumbas nito ay git restore --staged app.py, mas malinaw ito. Hindi gumagana ang --soft at --hard kapag may path.
Paano gumagana ang git revert
Kinukuwenta ng git revert <commit> ang pagbabago ng tinukoy na commit, ina-apply ito sa kabaligtaran na direksyon, at gumagawa ng bagong commit. Binubura ang mga naidagdag na linya, at ibinabalik ang mga nabura. Lumalago ang history pasulong, walang nire-rewrite, kaya walang kailangang ayusin sa panig ng katrabaho.
git log --oneline
# a3f9c21 (HEAD -> main) docs: README
# 7be41d0 feat: cache ng sagot
# 1c02e8f fix: timeout ng request
git revert 7be41d0
# [main 5d8e2aa] Revert "feat: cache ng sagot"
# 2 files changed, 3 insertions(+), 41 deletions(-)
Magbubukas ang Git ng editor na may mensaheng gaya ng Revert "..." at linyang This reverts commit 7be41d0.... Mainam na dagdagan ito ng paliwanag kung bakit inatras ang pagbabago.
Mga kapaki-pakinabang na flag ng revert
--no-edit: tanggapin ang default na mensahe nang walang editor.-n(--no-commit): ilagay ang kabaligtarang pagbabago sa index nang hindi gumagawa ng commit. Sa ganitong paraan, puwedeng i-undo ang ilang commit sa iisang commit.git revert A..B: i-undo ang saklaw ng mga commit (bawat isa ay may hiwalay na revert commit, mula bago hanggang luma).-m 1: kailangan para sa merge commit; tinutukoy nito kung aling parent ang ituturing na pangunahing linya.
git revert --no-commit HEAD~3..HEAD
git commit -m "Revert: pag-undo sa tatlong commit ng eksperimento"
Pag-revert ng merge commit
May dalawang parent ang merge, at hindi malalaman ng Git kung alin ang paghahambingan para sa "kabaligtarang" pagbabago, kaya kung walang -m, magbibigay ito ng error na is a merge but no -m option was given. Karaniwang ang pangunahing branch ang unang parent:
git revert -m 1 9f1e2ab
Bitag: kung kalaunan ay gusto ninyong isama ulit ang branch na iyon, ituturing ng Git na nasama na ang mga commit nito, at hindi nito dadalhin ang mga pagbabago. Ang solusyon ay i-revert muna ang revert commit, at pagkatapos ay idagdag ang mga bagong pagbabago.
git reset vs git revert: alin ang piliin
| Pamantayan | git reset | git revert |
|---|---|---|
| Ginagawa sa history | isinusulat muli (nawawala sa branch ang mga commit) | nagdaragdag ng bagong commit |
| Na-push na ang commit | kailangan ang push --force, nasisira ang gawa ng katrabaho | karaniwang git push |
| Pag-undo ng commit sa gitna ng history | kasama lahat ng sumunod na commit | puwedeng tiyak lang |
| Bakas sa history | walang natitira | nananatili, kita kung sino at bakit nag-rollback |
| Karaniwang sitwasyon | linisin ang lokal na commit bago mag-push | i-undo ang commit na sumira sa production sa main |
Simpleng tuntunin: kung nakita na ng iba ang commit, revert; kung hindi, reset. Ang eksepsiyon ay ang personal ninyong feature branch na walang ibang kumuha: doon, puwedeng isulat muli ang history at mag-push gamit ang git push --force-with-lease.
Bakit masamang ideya ang reset --hard sa shared branch
Isipin: nagpatakbo kayo ng git reset --hard HEAD~2 sa main, tapos git push --force. Nasa mga katrabaho pa rin ang dalawang commit. Sa susunod na git pull, susubukan ng Git na pagsamahin ang kanilang branch sa inyo, at babalik ang "nabura" na commit, kasama ang magulong merge commit. Mas malala pa kung may nag-push na ng kanilang commit sa ibabaw: mabubura lang ng force push ang mga iyon.
Kaya sa mga protektadong branch (main, release), karaniwang ipinagbabawal ng settings ng hosting ang force push. Kung hindi maiiwasan ang pagsulat muli, gamitin ang git push --force-with-lease: tatanggi ito kung may nakita kayong commit sa server na hindi ninyo alam.
Karaniwang sitwasyon ng pag-undo
Paano i-undo ang huling commit na hindi pa na-push
git reset --soft HEAD~1 # nasa index ang mga pagbabago, puwedeng mag-commit ulit
git reset HEAD~1 # nasa mga file ang pagbabago, walang laman ang index
git reset --hard HEAD~1 # itinapon ang mga pagbabago
Kung ang kailangan lang ay ayusin ang mensahe o idagdag ang nakalimutang file, hindi kailangan ng reset: git add nakalimutang.txt at git commit --amend.
Paano i-undo ang commit pagkatapos ng push
git revert <hash>
git push
Ito ang sagot sa madalas itanong na "git reverse commit": walang command na git reverse sa Git. Ang "kabaligtarang" commit ay nililikha ng git revert.
Paano ibalik ang branch sa estado ng server
git fetch origin
git reset --hard origin/main
Kapaki-pakinabang kapag magulo ang lokal na branch pagkatapos ng hindi maayos na rebase o eksperimento. Mawawala sa branch ang lahat ng lokal na commit na wala sa server.
Paano ibalik ang mga commit pagkatapos ng maling reset
Itinatala ng Git ang bawat galaw ng HEAD sa reflog. Nananatili sa object database ang mga commit pagkatapos ng reset nang hindi bababa sa ilang linggo (ayon sa default, 30 araw para sa mga hindi maabot na entry ng reflog).
git reflog
# 1c02e8f HEAD@{0}: reset: moving to HEAD~2
# a3f9c21 HEAD@{1}: commit: docs: README
# 7be41d0 HEAD@{2}: commit: feat: cache ng sagot
git reset --hard a3f9c21 # o HEAD@{1}
Lokal lang ang reflog: nasa clone ninyo lang ito at hindi ipinapadala sa server.
git clean: pagbura ng mga hindi tinatrack na file
Hindi ginagalaw ng git reset --hard ang mga file na hindi tinatrack ng Git: bagong file na hindi pa naidadagdag sa index, mga build artifact, mga log. Para sa mga ito, mayroong git clean. Hindi na maibabalik ang command: ang mga file na nabura nito ay hindi napupunta sa reflog o sa recycle bin.
git clean -n # dry run: ipakita kung ano ang bubura
git clean -f # burahin ang mga hindi tinatrack na file
git clean -fd # kasama rin ang mga hindi tinatrack na folder
git clean -fdx # kasama rin ang mga naka-ignore na file (.gitignore): node_modules, dist
git clean -i # interactive na pagpili
Kung wala ang -f, tatanggi ang Git na gumana bilang default (setting na clean.requireForce). Laging magsimula sa -n, lalo na sa -x: buburahin nito pati ang lokal na .env kung nasa ignore list ito.
Pagkombinahin para sa buong reset sa huling commit: git reset --hard && git clean -fd.
Karaniwang pagkakamali
- Ginamit ang reset --hard sa halip na --soft. Nasasagip ng reflog ang naka-commit, pero nawawala nang tuluyan ang hindi pa naka-commit. Ugaliing mag-
git stashbago ang eksperimento. - Revert nang walang pag-unawa sa mga dependency. Kung may mas bagong commit na binago ang parehong linya, magkakaroon ng conflict. Ayusin ito tulad ng dati: i-edit ang file,
git add,git revert --continue; kung nagbago ang isip,git revert --abort. - Pagkalito sa restore at reset. Para i-rollback ang mga edit sa isang file, hindi kailangan ng reset ng buong branch:
git restore fileang sapat. - Force push sa main. Mas mainam ang revert at karaniwang push, kahit medyo humaba ang history.
Buod
Ang git reset ay kasangkapan para sa sariling history na hindi pa nailalathala: --soft ang nagpapanatili ng mga pagbabago sa index, --mixed sa mga file, at --hard ang nagtatapon ng lahat. Ang git revert ay ligtas na pag-undo sa shared branch, at nag-iiwan ng tapat na bakas sa history. Ang git clean ay naglilinis ng mga hindi tinatrack na file, pero walang puwang sa pagkakamali. Ang mga sitwasyon ng pag-undo gamit ang restore, reset, at revert ay itinuturo nang praktikal sa kursong Git namin.
Mga madalas itanong
Binubura ba ng git reset ang mga commit nang tuluyan?
Hindi, nananatili ang mga commit sa object database at makikita sa git reflog hanggang sa burahin ng garbage collector, na karaniwang nangyayari pagkalipas ng 30 araw para sa mga hindi maabot na entry.
Puwede bang i-undo ang git revert?
Oo. Ang revert commit ay ordinaryong commit, kaya maaari itong i-undo sa pamamagitan ng isa pang git revert, o kung hindi pa naipapadala, tanggalin gamit ang git reset.
Ano ang pagkakaiba ng git reset at git checkout?
Inililipat ng git reset ang pointer ng kasalukuyang branch, habang ang git checkout (o git switch) ay inililipat ang HEAD sa ibang branch o commit nang hindi binabago ang branch mismo.
Ano ang ibig sabihin ng HEAD~1 at HEAD^?
Parehong tumutukoy sa parent ng kasalukuyang commit; ang HEAD~3 ay ang ikatlong ninuno sa pamamagitan ng unang parent, at ang HEAD^2 ay ang pangalawang parent ng merge commit.
Paano i-undo ang git clean?
Gamit ang Git, hindi ito posible: hindi naka-imbak sa repository ang mga hindi tinatrack na file na nabura. Kaya palaging patakbuhin muna ang git clean -n.
Detalyadong tinatalakay ang paksang ito, may praktis, sa kursong “Git с нуля до уверенного разработчика”.
Pumunta sa kurso