Bagaimana menghapus entri log komit terpilih dari repositori Git sambil menyimpan perubahannya?

241

Saya ingin menghapus entri log komit terpilih dari pohon komit linier, sehingga entri tidak ditampilkan di log komit.

Pohon komit saya terlihat seperti:

R--A--B--C--D--E--HEAD

Saya ingin menghapus entri B dan C agar tidak ditampilkan di log komit, tetapi perubahan dari A ke D harus dipertahankan. Mungkin dengan memperkenalkan komit tunggal, sehingga B dan C menjadi BC dan pohon itu terlihat seperti.

R--A--BC--D--E--HEAD

Atau, idealnya, setelah A datang D langsung. D 'mewakili perubahan dari A ke B, B ke C dan C ke D.

R--A--D'--E--HEAD

Apakah ini mungkin? jika ya, bagaimana?

Ini adalah proyek yang cukup baru sehingga tidak memiliki cabang seperti sekarang, karenanya tidak ada penggabungan juga.

xk0der
sumber
@ xk0der: "commit" adalah istilah yang tepat di sini. rebasedapat menghapus yang lama / membuat komitmen baru. Saya tidak tahu apa artinya "komit entri log".
jfs
@ JFSebastian Saya tidak melihat masalah dengan "commit log" - Log dari semua commit. Dan saya ingin menghapus beberapa entri dari log - sambil menjaga perubahan yang sebenarnya (komit).
xk0der
@ xk0der: komit git dapat dialamatkan dengan konten , yaitu, jika Anda mengubah sesuatu dalam komit misalnya, pesan lognya; Anda membuat komit baru. Anda bisa membaca komit git tanpa git dan melihatnya sendiri .
jfs
@ JSFSebastian - Terima kasih atas tautannya - Saya tahu itu - Tapi apakah teknis itu benar-benar mengubah masalah yang saya hadapi dan bagaimana saya mengemukakannya? Saya rasa tidak. Pada akhirnya: Saya ingin menghapus "pesan log komit" - tanpa menghapus "komit perubahan" - Harap baca kembali pertanyaan saya - khususnya paragraf kedua. Untuk menambahkan lebih banyak tampilkangit log "komit log" git-scm.com/docs/git-log . Dan saya ingin menyingkirkan dua entri dari log itu - bukan perubahannya.
xk0der

Jawaban:

273

git-rebase (1) melakukan hal itu.

$ git rebase -i HEAD~5

git awsome-ness [git rebase --interactive] berisi contoh.

  1. Jangan gunakan git-rebasepada komitmen publik (jarak jauh).
  2. Pastikan direktori kerja Anda bersih ( commitatau stashperubahan Anda saat ini).
  3. Jalankan perintah di atas. Ini meluncurkan $EDITOR.
  4. Ganti picksebelum Cdan Doleh squash. Ini akan menggabungkan C dan D menjadi B. Jika Anda ingin menghapus komit maka cukup hapus barisnya.

Jika Anda tersesat, ketikkan:

$ git rebase --abort  
jfs
sumber
Terima kasih atas balasan cepatnya. Jadi, apakah saya checkout A dan melakukan rebase, seperti git rebase -i D [A]?
xk0der
3
Bagaimana kita bisa melakukannya di repo jarak jauh?
Eray
6
@Eray: hanya push -fperubahan Anda. Jangan lakukan itu jika Anda tidak bekerja sendirian.
jfs
2
@ ripper234: Saya telah memperbaiki tautan ke git-rebasemesin manual dan wayback untuk posting blog.
jfs
75
# detach head and move to D commit
git checkout <SHA1-for-D>

# move HEAD to A, but leave the index and working tree as for D
git reset --soft <SHA1-for-A>

# Redo the D commit re-using the commit message, but now on top of A
git commit -C <SHA1-for-D>

# Re-apply everything from the old D onwards onto this new place 
git rebase --onto HEAD <SHA1-for-D> master
CB Bailey
sumber
Ini juga berfungsi dan membantu saya memahami apa itu soft reset. Memang, jawaban "teratas" juga benar dan lebih pendek, tetapi terima kasih atas jawaban ini juga.
cgp
41

Berikut adalah cara untuk menghapus id komit tertentu dengan hanya mengetahui id komit yang ingin Anda hapus.

git rebase --onto commit-id^ commit-id

Perhatikan bahwa ini sebenarnya menghapus perubahan yang diperkenalkan oleh komit.

rado
sumber
7
KEPALA ekstra dalam perintah ini akan menyebabkan rebase selesai dengan 'KEPALA terlepas' yang tidak diinginkan. Itu harus dihentikan.
Frosty
3
Ini mengembalikan perubahan yang diperkenalkan pada commit-id saya, OP ingin mempertahankan perubahan, cukup tekan komit.
CB Bailey
1
-1 karena tidak melakukan apa yang diminta OP (melainkan menghancurkan sesuatu yang secara eksplisit ingin dipertahankan).
Emil Styrke
1
Meskipun tidak melakukan apa yang diminta OP, itu persis apa yang saya butuhkan, jadi +1 untuk jawaban yang bermanfaat.
Edvins
20

Untuk memperluas jawaban JF Sebastian:

Anda dapat menggunakan git-rebase untuk dengan mudah membuat semua jenis perubahan pada histori komit Anda.

Setelah menjalankan git rebase --interactive Anda mendapatkan yang berikut di $ EDITOR Anda:

pick 366eca1 This has a huge file
pick d975b30 delete foo
pick 121802a delete bar
# Rebase 57d0b28..121802a onto 57d0b28
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit

Anda dapat memindahkan garis untuk mengubah urutan komit dan menghapus garis untuk menghapus komit itu. Atau Anda dapat menambahkan perintah untuk menggabungkan (squash) dua komit menjadi komit tunggal (komit sebelumnya adalah komit di atas), edit komit (apa yang diubah), atau reword komit pesan.

Saya pikir memilih berarti Anda ingin membiarkan komit itu sendirian.

(Contoh dari sini )

idbrii
sumber
14

Anda dapat secara non-interaktif menghapus B dan C dalam contoh Anda dengan:

git rebase --onto HEAD~5 HEAD~3 HEAD

atau secara simbolis,

git rebase --onto A C HEAD

Perhatikan bahwa perubahan dalam B dan C tidak akan menjadi dalam D; mereka akan pergi .

Kepala
sumber
Lihat di sini untuk info lebih lanjut: sethrobertson.github.io/GitFixUm/fixup.html#remove_deep
Maks.
3

Satu lagi cara,

git rebase -i ad0389efc1a79b1f9c4dd6061dca6edc1d5bb78a (C's hash)
and
git push origin master  -f

pilih hash yang ingin Anda gunakan sebagai basis, dan perintah di atas harus membuatnya interaktif sehingga Anda dapat menekan semua pesan teratas (Anda harus meninggalkan yang tertua)

hasilnya
sumber
2

Saya menemukan proses ini jauh lebih aman dan lebih mudah untuk dipahami dengan membuat cabang lain dari SHA1 dari A dan memetik perubahan yang diinginkan sehingga saya bisa memastikan saya puas dengan penampilan cabang baru ini. Setelah itu, mudah untuk menghapus cabang lama dan mengganti nama yang baru.

git checkout <SHA1 of A>
git log #verify looks good
git checkout -b rework
git cherry-pick <SHA1 of D>
....
git log #verify looks good
git branch -D <oldbranch>
git branch -m rework <oldbranch>
Eric Woodruff
sumber
jika Anda melakukan ini, Anda juga akan kehilangan komit E, bukan? Seperti yang saya mengerti, Anda menghapus master dan mengganti nama pengerjaan ulang sebagai master (mengingat aliran ABCDE adalah cabang master).
Renan Bandeira
1

Baru saja mengumpulkan jawaban semua orang: (m baru to git plz gunakan hanya untuk referensi)

git rebase untuk menghapus semua commit

log git

-first check from which commit you want to rebase

git rebase -i KEPALA ~ 1

-Here i want to rebase on the second last commit- commit count starts from '1')
-this will open the command line editor (called vim editor i guess)

Maka layar akan terlihat seperti ini:

pick 0c2236d Ditambahkan baris baru.

Rebase 2a1cd65..0c2236d ke 2a1cd65 (1 perintah)

#

Perintah:

p, pilih = gunakan komit

r, reword = gunakan komit, tetapi edit pesan komit

e, edit = gunakan komit, tetapi berhenti untuk mengubah

s, squash = gunakan komit, tetapi berbaur menjadi komit sebelumnya

f, fixup = seperti "squash", tetapi buang pesan log komit ini

x, exec = jalankan perintah (sisa baris) menggunakan shell

d, drop = hapus komit

#

Garis-garis ini dapat dipesan ulang; mereka dieksekusi dari atas ke bawah.

#

Jika Anda menghapus garis di sini, KOMIT YANG AKAN HILANG.

#

Namun, jika Anda menghapus semuanya, rebase akan dibatalkan.

#

Perhatikan bahwa komit kosong dikomentari ~ ~

~
~
~
~
~
~
~
~
~

Di sini, ubah baris pertama sesuai kebutuhan Anda (menggunakan perintah yang tercantum di atas yaitu 'jatuhkan' untuk menghapus komit dll.) Setelah selesai mengedit tekan ': x' untuk menyimpan dan keluar dari editor (ini hanya untuk editor vim)

Lalu

git push

Jika ini menunjukkan masalah maka Anda perlu mendorong perubahan ke paksa (ITS SANGAT KRITIS: jangan paksa mendorong jika Anda bekerja dalam tim)

git push -f asal

Shrinath Kopare
sumber
-1

Anda dapat menggunakan git cherry-pick untuk ini. 'cherry-pick' akan menerapkan komit ke cabang Anda sekarang.

lalu lakukan

git rebase --hard <SHA1 of A>

kemudian terapkan komit D dan E.

git cherry-pick <SHA1 of D>
git cherry-pick <SHA1 of E>

Ini akan mengabaikan komit B dan C. Setelah mengatakan bahwa mungkin tidak mungkin untuk menerapkan komit D ke cabang tanpa B, begitu YMMV.

Rory
sumber
2
OP ingin menggabungkan B, C, D yang berkomitmen, bukan untuk menghapus perubahannya.
jfs
3
Saya pikir Anda maksud reset --hard, bukan rebase --hard(yang tidak ada)
Mauricio Scheffer