Git ile Değişiklikleri Geri Almak 2: git revert
Paylaşılmış geçmişte güvenli geri alma: git revert'ün ters commit'i, reset ile revert arasındaki seçim, bir merge commit'i -m 1 ile revert etmek, branch'i yeniden merge ederken revert'ü geri almak, --no-commit ile birden çok commit ve revert sırasında conflict.
Sürüm notu: Bu yazıdaki komutlar Git 2.54 ile denenmiştir.
Önce Tahmin Et
Ada'nın dün push ettiği indirim commit'inde bir hata var: indirim iki kez düşülüyor. Commit artık origin/main'de ve üzerine bir commit daha atılmış. Ada hatalı commit'i geri almak için git revert kullanıyor:
$ git log --oneline
39db42c Add Ulysses
da98f2c Add discount to cart total
7f7eeec Add cart total
61419c1 Add book list
5102cb8 Add README and .gitignore
$ git revert --no-edit HEAD~1
Bu komuttan sonra git log --oneline kaç satır gösterir? da98f2c listeden kaybolur mu?
[main 94e1693] Revert "Add discount to cart total"
Date: Wed Nov 11 10:04:00 2026 +0300
1 file changed, 2 insertions(+), 3 deletions(-)
$ git log --oneline
94e1693 Revert "Add discount to cart total"
39db42c Add Ulysses
da98f2c Add discount to cart total
7f7eeec Add cart total
61419c1 Add book list
5102cb8 Add README and .gitignore
Altı satır. Hiçbir commit kaybolmadı, en üste bir tane eklendi. Önceki yazıdaki reset geçmişi geri sarıyordu; revert ise geçmişe dokunmadan, hatalı commit'in tersini yapan yeni bir commit yazıyor. Muhasebedeki ters kayıt gibi: yanlış girilen fişi defterden yırtmazsın, onu sıfırlayan yeni bir fiş kesersin.
Revert Ne Yapar?
Revert, hedef commit'in diff'ini alır, tersini working tree'ye ve index'e uygular ve sonucu commit'ler. Mesajı da kendisi yazar:
$ git log -1 --format=%B
Revert "Add discount to cart total"
This reverts commit da98f2c3aef8bedd08bfa250631455852238d5a1.
$ git show --stat --format=%s HEAD
Revert "Add discount to cart total"
src/cart.js | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
$ git push
To ../bookstore.git
39db42c..94e1693 main -> main
Mesajın gövdesi geri alınan commit'in tam hash'ini taşıyor; aylar sonra git log'a bakan biri neyin neden geri alındığını buradan izleyebilir. İyi bir alışkanlık, bu satırın altına nedeni de yazmak. --no-edit vermeseydin Git mesajı editörde açacaktı; bunu nedeni eklemek için kullanabilirsin. Push ise sıradan bir push oldu: geçmişin sonuna yeni bir commit eklendiği için fast-forward, ne force gerekti ne de kimsenin geçmişi bozuldu.
Revert değişikliği yeniden hesaplar, commit'i silmez. Hatalı commit'ten sonra gelen 39db42c başka bir dosyaya dokunduğu için hiç etkilenmedi. Geri alınan commit'in değiştirdiği satırlar sonradan başka commit'lerde de değiştiyse, revert bir conflict ile durabilir; aşağıda ona da değineceğim.
Reset mi, Revert mi?
Aynı hatayı reset ile geri almaya çalışsaydık ne olurdu? Revert'ten önceki hâli tutan başka bir klonda denedim:
$ git reset --hard HEAD~1
HEAD is now at 39db42c Add Ulysses
$ git push
To ../bookstore.git
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to '../bookstore.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Git, remote'taki geçmişi geri saramayacağını söylüyor. Bunu geçmenin tek yolu force push, ve o da yerelinde eski commit'leri olan herkesin geçmişiyle çatışır. Seçim aslında basit, iki durum ve kısa tradeoff. Commit henüz yalnızca senin makinendeyse reset kullan: geçmiş temiz kalır, kimse etkilenmez. Commit paylaşıldıysa, yani push edildiyse ve özellikle başkaları üzerine iş yaptıysa, revert kullan: geçmişe bir commit daha eklenir ama kimsenin repo'su bozulmaz. GitHub'daki korumalı bir main'de zaten force push yapamazsın; orada geri almanın tek yolu revert ve GitHub bunun için merge edilmiş PR'ın sayfasında bir Revert butonu da sunar; buton main'e doğrudan dokunmaz, revert'ü içeren yeni bir PR açar.
Bir Merge Commit'i Revert Etmek
Bu kez bir branch'in tamamı geri alınacak. feature/search iki commit'le main'e merge edildi, sonra aramanın canlıda sorun çıkardığı anlaşıldı:
$ git switch -q main && git merge --no-ff --no-edit feature/search
Merge made by the 'ort' strategy.
README.md | 2 ++
src/search.js | 5 +++++
2 files changed, 7 insertions(+)
create mode 100644 src/search.js
$ git log --oneline --graph -4
* 9782f41 Merge branch 'feature/search'
|\
| * 77fe4b0 Document book search
| * e3b6858 Add book search
|/
* 94e1693 Revert "Add discount to cart total"
$ git revert HEAD
error: commit 9782f41a85249b203613489cf36556707676e383 is a merge but no -m option was given.
fatal: revert failed
$ git revert -m 1 --no-edit HEAD
[main 7672c03] Revert "Merge branch 'feature/search'"
Date: Wed Nov 11 10:17:00 2026 +0300
2 files changed, 7 deletions(-)
delete mode 100644 src/search.js
Bir merge commit'in iki parent'ı var ve Git hangisine göre ters diff alacağını bilemez. -m 1 "birinci parent'a göre" demek; birinci parent merge'ü yaptığın branch'tir, burada main'in merge'den önceki ucu 94e1693. Yani "bu merge'ün main'e getirdiği her şeyi geri al". Neredeyse her zaman istediğin değer 1'dir. Grafikte birinci parent, merge satırından aşağı düz inen çizgidir.
Revert'ü Geri Almak
Bu yöntemin bir tuzağı var ve neredeyse herkes bir kez düşer. Arama düzeltildi, branch'i tekrar merge etmek istiyorsun:
$ git status -s && ls src
books.js
cart.js
$ git merge feature/search
Already up to date.
Git'e göre feature/search'ün commit'leri zaten main'in geçmişinde, çünkü merge commit hâlâ orada. Revert onların değişikliğini geri aldı ama commit'leri geçmişten çıkarmadı. Branch'e yeni bir düzeltme commit'i atıp merge etsen bile yalnızca o yeni commit gelir, ilk iki commit'in değişikliği gelmez. Çıkış yolu, revert'ün kendisini revert etmek:
$ git revert --no-edit HEAD
[main a6939ee] Reapply "Merge branch 'feature/search'"
Date: Wed Nov 11 10:20:00 2026 +0300
2 files changed, 7 insertions(+)
create mode 100644 src/search.js
$ ls src
books.js
cart.js
search.js
$ git log --oneline -4
a6939ee Reapply "Merge branch 'feature/search'"
7672c03 Revert "Merge branch 'feature/search'"
9782f41 Merge branch 'feature/search'
77fe4b0 Document book search
Git, bir revert'ün revert'üne "Reapply" adını veriyor. Arama geri geldi; şimdi branch'teki düzeltme commit'lerini normal şekilde merge edebilirsin. Sıra önemli: önce revert'ü geri al, sonra yeni commit'leri merge et.
--no-commit ile Birden Çok Commit
Son iki commit fiyatları artırdı ve ikisi de geri alınacak. Her biri için ayrı bir revert commit'i istemiyorsan --no-commit ile değişiklikleri biriktirip tek commit'te toplarsın:
$ git log --oneline -3
95595a3 Raise Emma price
c4d1075 Raise Dune price
a6939ee Reapply "Merge branch 'feature/search'"
$ git revert --no-commit HEAD~2..HEAD
$ git status -s
M src/books.js
$ git commit -m "Revert price increases"
[main 5de9101] Revert price increases
1 file changed, 2 insertions(+), 2 deletions(-)
$ git log --oneline -4
5de9101 Revert price increases
95595a3 Raise Emma price
c4d1075 Raise Dune price
a6939ee Reapply "Merge branch 'feature/search'"
HEAD~2..HEAD aralığı HEAD~2'yi içermez; yani son iki commit. Git onları en yeniden eskiye doğru geri alır, böylece aynı satırlara dokunan commit'ler de birbirine takılmaz. Değişiklikler index'te birikti ve tek bir commit'le kaydedildi. Mesajı bu kez kendin yazdığın için, hangi commit'lerin geri alındığını gövdeye eklemek iyi olur.
Revert Sırasında Conflict
Geri aldığın commit'in değiştirdiği satırlar sonradan başka bir commit'te de değiştiyse, Git tersini temiz uygulayamaz ve merge'deki gibi conflict marker'larıyla durur. Akış onuncu yazıdaki conflict çözümüyle aynı: dosyaları düzelt, git add ile işaretle, sonra git revert --continue ile devam et. Vazgeçersen git revert --abort her şeyi revert'ten önceki hâline döndürür. Bir aralığı geri alırken yalnızca o anki commit'i atlamak istersen git revert --skip var.
Sık Düşülen Tuzaklar
İlk tuzak revert'ü "silme" sanmak. Revert edilen commit geçmişte durmaya devam eder. Gizli bir bilgiyi, örneğin bir token'ı commit'lediysen revert onu kurtarmaz; token'ı hemen iptal et.
İkincisi merge commit'i revert edip branch'i sonra yeniden merge etmeye çalışmak. Yukarıda gördüğün gibi Git "Already up to date" der ya da yalnızca yeni commit'leri getirir. Önce revert'ü revert et.
Üçüncüsü yanlış -m değeri. -m 2, merge'ü feature branch'in gözünden geri alır: branch ayrıldıktan sonra main'e giren değişiklikleri geri alır, feature'ı yerinde bırakır. Emin değilsen git show HEAD ile merge commit'in parent sırasına bak.
Dördüncüsü paylaşılmış bir commit için önce reset deneyip sonra force push'la "düzeltmek". Önceki yazıda ve bu yazıda gördüğün gibi, paylaşılmış geçmişte geri almanın yolu revert.
Küçük Bir Deneme
★☆☆ Üç commit at ve ortadakini git revert ile geri al. git log -p -1 çıktısını ortadaki commit'in git show çıktısıyla karşılaştır: diff'ler birbirinin tersi mi?
★★☆ Bir satırı değiştiren bir commit at, sonra aynı satırı yeniden değiştiren ikinci bir commit at. İlk commit'i revert etmeyi dene, conflict'i çöz ve git revert --continue ile bitir.
★★★ Bir branch'i --no-ff ile merge et, merge'ü -m 1 ile revert et, branch'e bir düzeltme commit'i atıp tekrar merge et. Dosyaların son hâline bak: ilk commit'lerin değişikliği orada mı? Sonra aynı senaryoyu revert'ü revert ederek baştan dene.
Revert ile paylaşılmış geçmişi bozmadan geri almayı gördük. Bir sonraki yazıda yarım kalmış işi kenara koymayı öğreniyoruz: git stash ile değişiklikleri saklamak, apply ile pop farkı, untracked dosyalar, yalnızca stage'i saklamak ve bir stash'ten branch açmak.