Intro: Anda Memiliki 5 Solusi
Poster asli menyatakan:
Saya tidak sengaja melakukan file yang tidak diinginkan ... ke repositori saya beberapa komit yang lalu ... Saya ingin sepenuhnya menghapus file dari riwayat repositori.
Apakah mungkin untuk menulis ulang sejarah perubahan sedemikian rupa sehingga filename.orig
tidak pernah ditambahkan ke repositori?
Ada banyak cara berbeda untuk menghapus riwayat file sepenuhnya dari git:
- Mengubah komitmen.
- Reset keras (mungkin ditambah rebase).
- Rebase non-interaktif.
- Rebases interaktif.
- Memfilter cabang.
Dalam hal poster asli, mengubah komit sebenarnya bukan pilihan dengan sendirinya, karena dia membuat beberapa komitmen tambahan setelahnya, tetapi demi kelengkapan, saya juga akan menjelaskan bagaimana melakukannya, untuk siapa pun yang hanya ingin untuk mengubah komit mereka sebelumnya.
Perhatikan bahwa semua solusi ini melibatkan mengubah / menulis ulang riwayat / komit dengan satu cara lain, sehingga siapa pun yang memiliki salinan komit lama harus melakukan pekerjaan ekstra untuk menyinkronkan kembali sejarah mereka dengan riwayat baru.
Solusi 1: Mengubah Komitmen
Jika Anda secara tidak sengaja membuat perubahan (seperti menambahkan file) di komit sebelumnya, dan Anda tidak ingin riwayat perubahan itu ada lagi, maka Anda dapat mengubah komit sebelumnya untuk menghapus file dari itu:
git rm <file>
git commit --amend --no-edit
Solusi 2: Hard Reset (Kemungkinan Plus Rebase)
Seperti solusi # 1, jika Anda hanya ingin menghilangkan komit Anda sebelumnya, maka Anda juga memiliki opsi untuk melakukan hard reset ke induknya:
git reset --hard HEAD^
Perintah tersebut akan sulit-ulang cabang Anda dengan sebelumnya 1 st orangtua komit.
Namun , jika, seperti poster asli, Anda telah membuat beberapa komit setelah komit yang ingin Anda batalkan perubahannya, Anda masih dapat menggunakan pengaturan ulang yang keras untuk memodifikasinya, tetapi melakukannya juga melibatkan penggunaan rebase. Berikut adalah langkah-langkah yang dapat Anda gunakan untuk mengubah komitmen lebih lanjut dalam sejarah:
# Create a new branch at the commit you want to amend
git checkout -b temp <commit>
# Amend the commit
git rm <file>
git commit --amend --no-edit
# Rebase your previous branch onto this new commit, starting from the old-commit
git rebase --preserve-merges --onto temp <old-commit> master
# Verify your changes
git diff master@{1}
Solusi 3: Rebase Non-interaktif
Ini akan berfungsi jika Anda hanya ingin menghapus seluruh komit dari riwayat:
# Create a new branch at the parent-commit of the commit that you want to remove
git branch temp <parent-commit>
# Rebase onto the parent-commit, starting from the commit-to-remove
git rebase --preserve-merges --onto temp <commit-to-remove> master
# Or use `-p` insteda of the longer `--preserve-merges`
git rebase -p --onto temp <commit-to-remove> master
# Verify your changes
git diff master@{1}
Solusi 4: Rebases Interaktif
Solusi ini akan memungkinkan Anda untuk mencapai hal-hal yang sama seperti solusi # 2 dan # 3, yaitu memodifikasi atau menghapus commit lebih lanjut dalam sejarah daripada komit Anda sebelumnya, sehingga solusi yang Anda pilih untuk digunakan adalah terserah Anda. Rebases interaktif tidak cocok untuk rebending ratusan komit, karena alasan kinerja, jadi saya akan menggunakan rebases non-interaktif atau solusi cabang filter (lihat di bawah) dalam situasi semacam itu.
Untuk memulai rebase interaktif, gunakan yang berikut:
git rebase --interactive <commit-to-amend-or-remove>~
# Or `-i` instead of the longer `--interactive`
git rebase -i <commit-to-amend-or-remove>~
Ini akan menyebabkan git untuk memundurkan kembali komit ke induk dari komit yang ingin Anda modifikasi atau hapus. Ini kemudian akan memberi Anda daftar komitmen rewound dalam urutan terbalik dalam editor git apa pun yang ditetapkan untuk digunakan (ini adalah Vim secara default):
pick 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
pick 7668f34 Modify Bash config to use Homebrew recommended PATH
pick 475593a Add global .gitignore file for OS X
pick 1b7f496 Add alias for Dr Java to Bash config (OS X)
Komit yang ingin Anda modifikasi atau hapus akan berada di bagian atas daftar ini. Untuk menghapusnya, cukup hapus barisnya dalam daftar. Jika tidak, ganti "memilih" dengan "edit" pada 1 st line, seperti begitu:
edit 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
Selanjutnya, masuk git rebase --continue
. Jika Anda memilih untuk menghapus komit sepenuhnya, maka hanya itu yang perlu Anda lakukan (selain verifikasi, lihat langkah terakhir untuk solusi ini). Jika, di sisi lain, Anda ingin memodifikasi komit, maka git akan menerapkan kembali komit dan kemudian menjeda rebase.
Stopped at 00ddaacab0a85d9989217dd9fe9e1b317ed069ac... Add symlinks
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
Pada titik ini, Anda dapat menghapus file dan mengubah komit, kemudian melanjutkan rebase:
git rm <file>
git commit --amend --no-edit
git rebase --continue
Itu dia. Sebagai langkah terakhir, apakah Anda memodifikasi komit atau menghapusnya sepenuhnya, itu ide yang baik untuk memverifikasi bahwa tidak ada perubahan tak terduga lainnya yang dilakukan pada cabang Anda dengan membedakannya dengan statusnya sebelum rebase:
git diff master@{1}
Solusi 5: Memfilter Cabang
Akhirnya, solusi ini yang terbaik jika Anda ingin menghapus semua jejak keberadaan file dari sejarah, dan tidak ada solusi lain yang cukup untuk tugas itu.
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>'
Itu akan menghapus <file>
dari semua komit, mulai dari komit root. Jika sebaliknya Anda hanya ingin menulis ulang rentang komit HEAD~5..HEAD
, maka Anda bisa meneruskannya sebagai argumen tambahan filter-branch
, seperti yang ditunjukkan dalam
jawaban ini :
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>' HEAD~5..HEAD
Sekali lagi, setelah filter-branch
selesai, biasanya ide yang baik untuk memverifikasi bahwa tidak ada perubahan tak terduga lainnya dengan membedakan cabang Anda dengan keadaan sebelumnya sebelum operasi penyaringan:
git diff master@{1}
Alternatif Filter-Cabang: BFG Repo Cleaner
Saya pernah mendengar bahwa alat Repo Cleaner BFG berjalan lebih cepat daripada git filter-branch
, jadi Anda mungkin ingin memeriksanya sebagai opsi juga. Bahkan disebutkan secara resmi dalam dokumentasi cabang-filter sebagai alternatif yang layak:
git-filter-branch memungkinkan Anda membuat penulisan ulang shell-script yang kompleks dari sejarah Git Anda, tetapi Anda mungkin tidak membutuhkan fleksibilitas ini jika Anda hanya menghapus data yang tidak diinginkan seperti file besar atau kata sandi. Untuk operasi-operasi tersebut, Anda mungkin ingin mempertimbangkan The BFG Repo-Cleaner , sebuah alternatif berbasis JVM untuk git-filter-branch, biasanya setidaknya 10-50x lebih cepat untuk case-use tersebut, dan dengan karakteristik yang sangat berbeda:
Versi file tertentu dibersihkan tepat sekali . BFG, tidak seperti git-filter-branch, tidak memberi Anda kesempatan untuk menangani file secara berbeda berdasarkan di mana atau kapan itu dilakukan dalam sejarah Anda. Batasan ini memberikan manfaat kinerja inti dari The BFG, dan sangat cocok untuk tugas membersihkan data buruk - Anda tidak peduli di mana data buruk itu, Anda hanya ingin itu hilang .
Secara default BFG mengambil keuntungan penuh dari mesin multi-core, membersihkan pohon file komit secara paralel. membersihkan git-filter-cabang komit berurutan (yaitu dengan cara tunggal berulir), meskipun adalah
mungkin untuk menulis filter yang mencakup parallellism mereka sendiri, di skrip dijalankan terhadap setiap komit.
The opsi perintah jauh lebih ketat daripada cabang git-filter, dan didedikasikan hanya untuk tugas-tugas menghapus yang tidak diinginkan data- misalnya: --strip-blobs-bigger-than 1M
.
Sumber daya tambahan
- Pro Git § 6.4 Alat Git - Sejarah Penulisan Ulang .
- git-filter-branch (1) Halaman Manual .
- git-commit (1) Halaman Manual .
- git-reset (1) Halaman Manual .
- git-rebase (1) Halaman Manual .
- Pembersih Repo BFG (lihat juga jawaban dari penciptanya sendiri ).