Bagaimana Anda menghapus revisi tertentu dalam sejarah git?

213

Misalkan riwayat git Anda terlihat seperti ini:

1 2 3 4 5

1–5 adalah revisi terpisah. Anda harus menghapus 3 sambil tetap 1, 2, 4 dan 5. Bagaimana ini bisa dilakukan?

Apakah ada metode yang efisien ketika ada ratusan revisi setelah yang akan dihapus?

INFORMASI 1800
sumber
Pertanyaan ini tidak jelas. Itu tidak secara eksplisit mengatakan bahwa penulis ingin 1-2- (3 + 4) -5 atau 1-2-4-5
RandyTek
14
8 tahun kemudian, saya tidak bisa memberi tahu Anda dengan tepat masalah apa yang saya coba selesaikan. Tetapi di git selalu ada banyak cara untuk melakukan sesuatu, dan ada banyak jawaban yang disukai banyak orang, jadi saya kira ambiguitas tidak menyebabkan terlalu banyak kesulitan bagi banyak orang
1800 INFORMASI
1
git rebase --onto 2 3 HEADcukup banyak berarti rebase ke 2, dengan komit antara 3 dan KEPALA (KEPALA adalah opsional, atau 5 dalam kasus ini)
neaumusic

Jawaban:

76

Untuk menggabungkan revisi 3 dan 4 menjadi satu revisi, Anda dapat menggunakan git rebase. Jika Anda ingin menghapus perubahan dalam revisi 3, Anda perlu menggunakan perintah edit dalam mode rebase interaktif. Jika Anda ingin menggabungkan perubahan menjadi satu revisi, gunakan squash.

Saya telah berhasil menggunakan teknik squash ini, tetapi tidak pernah perlu menghapus revisi sebelumnya. Dokumentasi git-rebase di bawah "Komitmen pemisah" semoga memberi Anda cukup ide untuk mengetahuinya. (Atau orang lain mungkin tahu).

Dari dokumentasi git :

Mulai dengan komit tertua yang ingin Anda pertahankan apa adanya:

git rebase -i <after-this-commit>

Editor akan bersemangat dengan semua komit di cabang Anda saat ini (mengabaikan komit gabungan), yang terjadi setelah komit yang diberikan. Anda dapat menyusun ulang komitmen dalam daftar ini sesuai dengan isi hati Anda, dan Anda dapat menghapusnya. Daftarnya kurang lebih seperti ini:

pick deadbee Oneline dari commit ini
pick fa1afe1 Oneline dari commit selanjutnya
...

Deskripsi oneline murni untuk kesenangan Anda; git-rebase tidak akan melihat mereka tetapi pada nama commit ("deadbee" dan "fa1afe1" dalam contoh ini), jadi jangan hapus atau sunting namanya.

Dengan mengganti perintah "pilih" dengan perintah "edit", Anda dapat memberitahu git-rebase untuk berhenti setelah menerapkan komit itu, sehingga Anda dapat mengedit file dan / atau pesan komit, mengubah komit, dan melanjutkan rebasing.

Jika Anda ingin melipat dua atau lebih komit menjadi satu, ganti perintah "pilih" dengan "squash" untuk komit kedua dan selanjutnya. Jika komit memiliki penulis yang berbeda, komit tersebut akan dikaitkan komit terjepit ke penulis komit pertama.

pakaian
sumber
42
-1 Pertanyaannya didefinisikan dengan baik tetapi jawaban ini tidak begitu jelas. Penulis tidak mengatakan apa solusi yang tepat.
Aleksandr Levchuk
1
Ini adalah petunjuk yang salah. Bagian KOMIT PENYETELAN bukan yang benar. Anda ingin membaca jauh lebih tinggi dalam manual - lihat jawaban @Rares Vernica.
Aleksandr Levchuk
1
@AleksandrLevchuk Pertanyaannya tidak terdefinisi dengan baik: tidak jelas dari cara pertanyaan tersebut dinyatakan apakah perubahan dalam 3 harus disimpan atau dibuang. Saya akan setuju bahwa jika perubahan harus dibuang, jawaban lain menawarkan pendekatan yang lebih mudah. Namun, jika perubahan harus disimpan, ini akan menjadi operasi kosmetik murni. Kedua pendekatan menulis ulang sejarah dengan cara yang berbahaya jika yang lain mungkin berdasarkan pekerjaan di atas sejarah yang cacat; jika itu yang terjadi, pembersihan kosmetik tidak boleh dilakukan, dan penghapusan perubahan akan lebih baik dilakukan melalui git revert.
Theodore Murdock
124

Per komentar ini (dan saya memeriksa bahwa ini benar), jawaban rado sangat dekat tetapi meninggalkan git dalam keadaan kepala yang terpisah. Alih-alih, hapus HEADdan gunakan ini untuk menghapus <commit-id>dari cabang tempat Anda berada:

git rebase --onto <commit-id>^ <commit-id>
kareem
sumber
1
Jika Anda bisa memberi tahu saya cara juga menghapus commit-id dari riwayat berdasarkan dari commit-id saja, Anda akan menjadi pahlawan saya.
kayleeFrye_onDeck
Bisakah Anda memasukkan penjelasan apa yang dilakukan perintah ajaib ke dalam jawabannya? Yaitu apa yang ditunjukkan oleh setiap parameter?
alexandroid
ini luar biasa tetapi bagaimana jika saya ingin menghapus komit pertama dalam sejarah? (itulah sebabnya saya datang ke sini: P)
Sterling Camden
2
Untuk beberapa alasan, tidak ada yang terjadi ketika saya menjalankan ini. Namun, berubah ^untuk ~1membuatnya bekerja.
Antimony
Jauh terlambat, tapi saya akan menambahkan bahwa jika Anda mengalami menggabungkan konflik, batalkan rebase dan kemudian jalankan kembali dengan --strategy-option theirsdi akhir.
LastStar007
122

Berikut adalah cara untuk menghapus sesuatu yang tidak interaktif <commit-id>, hanya mengetahui yang <commit-id>ingin Anda hapus:

git rebase --onto <commit-id>^ <commit-id> HEAD
rado
sumber
2
Bekerja untuk saya juga. Btw, apa yang dilakukan operator ^? Apakah ini berarti komit berikutnya setelah yang ditentukan?
hopia
3
@opia artinya orang tua (pertama) dari komit yang ditentukan. Lihat "revisi bantuan git"
Emil Styrke
12
Lihat rekomendasi @ kareem dalam jawabannya untuk menghilangkan HEADagar tidak ada kepala yang terpisah.
mklement0
1
Jauh lebih mudah daripada harus mencari tahu berapa banyak yang melakukan pencarian kembali.
Dana Woodman
1
ini luar biasa tetapi bagaimana jika saya ingin menghapus komit pertama dalam sejarah? (itulah sebabnya saya datang ke sini: P)
Sterling Camden
76

Seperti disebutkan sebelumnya git-rebase (1) adalah teman Anda. Dengan asumsi komit ada di mastercabang Anda, Anda akan melakukan:

git rebase --onto master~3 master~2 master

Sebelum:

1---2---3---4---5  master

Setelah:

1---2---4'---5' master

Dari git-rebase (1):

Berbagai komitmen juga dapat dihapus dengan rebase. Jika kita memiliki situasi berikut:

E---F---G---H---I---J  topicA

lalu perintahnya

git rebase --onto topicA~5 topicA~3 topicA

akan menghasilkan penghapusan komit F dan G:

E---H'---I'---J'  topicA

Ini berguna jika F dan G cacat dalam beberapa cara, atau tidak boleh menjadi bagian dari topicA. Perhatikan bahwa argumen untuk --onto dan parameter dapat berupa komit-ish yang valid.

rvernica
sumber
3
bukankah ini seharusnya --onto master~3 master~1?
Matthias
3
Jika Anda hanya ingin menghapus komit terakhir, itu --onto master ~ 1 master
MikeHoss
Saya ingin membawa sol ini. tapi saya mendapatkan MERGE CONFLICTkesalahan. Saya menggunakan skenario yang disebutkan di stackoverflow.com/questions/2938301/remove-specific-commit dan saya tidak dapat menghapus komit ke-2 dalam contoh itu.
maan81
22

Jika semua yang ingin Anda lakukan adalah menghapus perubahan yang dibuat dalam revisi 3, Anda mungkin ingin menggunakan git revert.

Git revert hanya menciptakan revisi baru dengan perubahan yang membatalkan semua perubahan dalam revisi yang Anda kembalikan.

Artinya, Anda menyimpan informasi tentang komit yang tidak diinginkan, dan komit yang menghapus perubahan tersebut.

Ini mungkin jauh lebih ramah jika mungkin seseorang telah menarik dari repositori Anda dalam waktu yang bersamaan, karena revert pada dasarnya hanyalah komit standar.

SpoonMeiser
sumber
4
Sayangnya bukan solusi yang baik bagi saya karena seseorang secara tidak sengaja melakukan 100MB omong kosong untuk repo, meledakkan ukuran dan membuat antarmuka web lamban.
Stephen Smith
18

Semua jawaban sejauh ini tidak membahas masalah trailing:

Apakah ada metode yang efisien ketika ada ratusan revisi setelah yang akan dihapus?

Langkah-langkahnya mengikuti, tetapi untuk referensi, mari kita asumsikan sejarah berikut:

[master] -> [hundreds-of-commits-including-merges] -> [C] -> [R] -> [B]

C : komit hanya mengikuti komit yang akan dihapus (bersih)

R : Komit untuk dihapus

B : komit tepat sebelum komit untuk dihapus (basis)

Karena kendala "ratusan revisi", saya mengasumsikan pra-kondisi berikut:

  1. ada beberapa komitmen memalukan yang Anda harap tidak pernah ada
  2. ada NOL komitmen selanjutnya yang benar-benar bergantung pada komitmen yang memalukan (nol konflik saat kembali)
  3. Anda tidak peduli bahwa Anda akan terdaftar sebagai 'Pengemis' dari ratusan komitmen yang ikut campur ('Pengarang' akan dipertahankan)
  4. Anda belum pernah membagikan repositori
    • atau Anda benar-benar memiliki pengaruh yang cukup besar terhadap semua orang yang pernah mengkloning sejarah dengan komitmen di dalamnya untuk meyakinkan mereka untuk menggunakan sejarah baru Anda
    • dan Anda tidak peduli tentang penulisan ulang sejarah

Ini adalah serangkaian kendala yang cukup ketat, tetapi ada jawaban menarik yang benar-benar berfungsi dalam kasus sudut ini.

Berikut langkah-langkahnya:

  1. git branch base B
  2. git branch remove-me R
  3. git branch save
  4. git rebase --preserve-merges --onto base remove-me

Jika benar-benar tidak ada konflik, maka ini harus dilanjutkan tanpa interupsi lebih lanjut. Jika ada konflik, Anda dapat menyelesaikannya dan rebase --continueatau memutuskan untuk hidup dengan rasa malu dan rebase --abort.

Sekarang Anda harusnya mastertidak lagi memiliki R di dalamnya. The savecabang poin ke tempat Anda sebelumnya, jika anda ingin mendamaikan.

Bagaimana Anda ingin mengatur transfer orang lain ke riwayat baru Anda terserah Anda. Anda perlu berkenalan dengan stash, reset --hard, dan cherry-pick. Dan Anda dapat menghapus base, remove-medan savecabang

jdsumsion
sumber
bagaimana saya bisa menyukai ini 150000 kali?
Gena Moroz
Berfungsi untuk saya menghapus 3 commit secara berurutan dari tengah sejarah cabang tempat seseorang melakukan banyak file 150MB.
Marcos
3

Saya juga mendarat di situasi yang sama. Gunakan rebase interaktif menggunakan perintah di bawah ini dan saat memilih, letakkan komit ke-3.

git rebase -i remote/branch
Sankalp
sumber
2

Jadi inilah skenario yang saya hadapi, dan bagaimana saya menyelesaikannya.

[branch-a]

[Hundreds of commits] -> [R] -> [I]

di sini Rada komit yang harus saya hapus, dan Ikomit tunggal yang muncul setelahnyaR

Saya membuat komitmen kembali dan menghancurkan mereka bersama

git revert [commit id of R]
git rebase -i HEAD~3

Selama labu rebase interaktif 2 komit terakhir.

Gautam
sumber
0

Jawaban rado dan kareem tidak melakukan apa pun untuk saya (hanya pesan "Cabang saat ini yang terbaru." Muncul). Mungkin ini terjadi karena simbol '^' tidak berfungsi di konsol Windows. Namun, menurut komentar ini , mengganti '^' dengan '~ 1' memecahkan masalah.

git rebase --onto <commit-id>^ <commit-id>
fdermishin
sumber