Git: Cara menghapus file dari indeks tanpa menghapus file dari repositori apa pun

163

Saat Anda menggunakan

git rm --cached myfile

itu tidak menghapus dari sistem file lokal, yang merupakan tujuannya. Tetapi jika Anda sudah membuat versi dan mengkomit file, mendorongnya ke repositori pusat, dan menariknya ke repositori lain sebelum menggunakan perintah, itu akan menghapus file dari sistem itu.

Apakah ada cara untuk menghapus file dari versi tanpa menghapusnya dari sistem file apa pun?

Sunting: Klarifikasi, saya harap.

Fletcher Moore
sumber
Mungkin Anda bisa memperluas use case Anda. Indeks adalah area pementasan untuk komit berikutnya jadi mengapa Anda ingin menghapus file dari indeks jika Anda tidak ingin menghapusnya dari cabang saat ini?
CB Bailey
1
Maafkan saya. Maksud saya mengatakan file saya untuk beberapa alasan telah dilakukan dan didistribusikan sejak lama. Sekarang tujuannya adalah untuk menghapusnya dari versi tanpa menghapusnya dari sistem orang. Alasan ini muncul adalah karena saya tidak sengaja memversi file konfigurasi. File konfigurasi diperlukan, jadi saya tidak ingin menghapusnya dari sistem asing.
Fletcher Moore
Saya sudah mengedit pertanyaan agar lebih jelas.
Fletcher Moore
3
git rm --cached tidak akan menghapus file dari direktori kerja lainnya. File hanya akan dihapus jika seseorang yang bekerja di direktori itu melakukan tarikan. File dapat dengan mudah dipulihkan dengan "git checkout HEAD @ {1} foo" (jika dieksekusi segera setelah tarikan.)
William Pursell

Jawaban:

119

Saya tidak berpikir komit Git dapat merekam niat seperti "berhenti melacak file ini, tetapi jangan menghapusnya".

Mengaktifkan niat seperti itu akan memerlukan intervensi di luar Git dalam setiap repositori yang menggabungkan (atau rebase) komit yang menghapus file.


Simpan Salinan, Terapkan Penghapusan, Pulihkan

Mungkin hal termudah untuk dilakukan adalah memberi tahu pengguna hilir Anda untuk menyimpan salinan file, tarik penghapusan Anda, lalu pulihkan file tersebut. Jika mereka menarik melalui rebase dan 'membawa' modifikasi ke file, mereka akan mendapatkan konflik. Untuk menyelesaikan konflik seperti itu, gunakan git rm foo.conf && git rebase --continue(jika komit yang bertikai memiliki perubahan selain yang ke file yang dihapus) atau git rebase --skip(jika komit yang bertikai hanya berubah menjadi file yang dihapus).

Pulihkan File sebagai Tidak Terlacak Setelah Menarik Komit yang Menghapusnya

Jika mereka sudah menarik komit penghapusan Anda, mereka masih dapat memulihkan versi file sebelumnya dengan git show :

git show @{1}:foo.conf >foo.conf

Atau dengan git checkout (per komentar oleh William Pursell; tetapi jangan lupa untuk menghapusnya kembali dari indeks!):

git checkout @{1} -- foo.conf && git rm --cached foo.conf

Jika mereka telah mengambil tindakan lain sejak menarik penghapusan Anda (atau mereka menarik dengan rebase ke KEPALA terpisah), mereka mungkin membutuhkan sesuatu selain @{1}. Mereka dapat digunakan git log -guntuk menemukan komit tepat sebelum mereka menarik penghapusan Anda.


Dalam komentar, Anda menyebutkan bahwa file yang ingin Anda “lacak, tetapi simpan” adalah beberapa jenis file konfigurasi yang diperlukan untuk menjalankan perangkat lunak (langsung dari repositori).

Simpan File sebagai 'Default' dan Aktifkan Secara Manual / Otomatis

Jika tidak sepenuhnya tidak dapat diterima untuk terus mempertahankan konten file konfigurasi dalam repositori, Anda mungkin dapat mengubah nama file yang dilacak dari (misalnya) foo.confke foo.conf.defaultdan kemudian menginstruksikan pengguna Anda untuk cp foo.conf.default foo.confsetelah menerapkan komit nama ganti. Atau, jika pengguna sudah menggunakan bagian repositori yang ada (mis. Skrip atau program lain yang dikonfigurasi oleh konten dalam repositori (mis. MakefileAtau sejenisnya)) untuk meluncurkan / menggunakan perangkat lunak Anda, Anda dapat memasukkan mekanisme default ke dalam peluncuran / proses penyebaran:

test -f foo.conf || test -f foo.conf.default &&
    cp foo.conf.default foo.conf

Dengan seperti mekanisme default di tempat, pengguna harus dapat menarik komit yang mengganti nama foo.confuntuk foo.conf.defaulttanpa harus melakukan kerja ekstra. Selain itu, Anda menghindari menyalin file konfigurasi secara manual jika Anda membuat instalasi / repositori tambahan di masa mendatang.

Menulis Ulang Sejarah Membutuhkan Intervensi Manual ...

Jika tidak bisa mempertahankan konten dalam repositori maka Anda mungkin ingin menghapusnya sepenuhnya dari sejarah dengan sesuatu seperti git filter-branch --index-filter …. Ini berjumlah menulis ulang sejarah, yang akan membutuhkan intervensi manual untuk setiap cabang / repositori (lihat “Memulihkan Dari Hulu Rebase” di bagian git rebase manualnya ). Perlakuan khusus yang diperlukan untuk file konfigurasi Anda hanyalah langkah lain yang harus dilakukan ketika memulihkan dari penulisan ulang:

  1. Simpan salinan file konfigurasi.
  2. Pulihkan dari penulisan ulang.
  3. Pulihkan file konfigurasi.

Abaikan Itu untuk Mencegah Pengulangan

Apapun metode yang Anda gunakan, Anda mungkin ingin memasukkan nama file konfigurasi dalam .gitignorefile di repositori sehingga tidak ada yang dapat secara tidak sengaja git add foo.conflagi (mungkin, tetapi membutuhkan -f/ --force). Jika Anda memiliki lebih dari satu file konfigurasi, Anda dapat mempertimbangkan 'memindahkan' semuanya ke dalam satu direktori dan mengabaikan semuanya (dengan 'memindahkan' maksud saya mengubah di mana program mengharapkan untuk menemukan file konfigurasinya, dan mendapatkan pengguna (atau peluncuran / mekanisme menyebarkan) untuk menyalin / memindahkan file ke lokasi baru mereka, Anda jelas tidak ingin git mv file ke dalam direktori yang Anda akan mengabaikan).

Chris Johnsen
sumber
5
Respon luar biasa teliti. Terima kasih!
Fletcher Moore
Jawaban Tom Power, di bawah, tampaknya bertentangan dengan kalimat pertama Anda.
Mike S
1
@MikeS: Efeknya --{,no-}assume-unchangedmurni lokal: negaranya tidak langsung dicatat dalam commit. Ini dapat membantu mencegah repositori melakukan perubahan baru pada file, tetapi itu tidak menghapusnya dari kontrol versi. Jika Anda dapat mengaturnya untuk semua yang relevan, terkait, repositori non-telanjang, maka dapat membantu situasi Anda, tapi itu bukan sesuatu yang Anda bisa langsung mendorong + tarik / rebase ke terkait, repositori non-telanjang lainnya (terutama yang Anda tidak mengontrol, seperti komentar klarifikasi penanya asli pada pertanyaan: lihat "sistem orang" / "sistem asing").
Chris Johnsen
1
I do not think a Git commit can record an intention like “stop tracking this file, but do not delete it”.- sekarang bisa, dengangit rm --cached foo.conf
Nick Volynkin
1
@NickVolynkin: Bukankah pertanyaannya sudah menunjukkan bahwa ini tidak memadai untuk tujuan si penanya: (secara otomatis) menyimpan file di sekitar ketika komit yang dihasilkan ditarik ke repositori lain?
Chris Johnsen
86

Memiliki masalah yang sama minggu ini ketika saya secara tidak sengaja melakukan, kemudian mencoba untuk menghapus file build dari repositori bersama, dan ini:

http://gitready.com/intermediate/2009/02/18/temporently-ignoring-files.html

telah bekerja dengan baik untuk saya dan tidak disebutkan sejauh ini.

git update-index --assume-unchanged <file>

Untuk menghapus file yang Anda minati dari kontrol versi, maka gunakan semua perintah Anda seperti biasa.

git update-index --no-assume-unchanged <file>

Jika Anda ingin memasukkannya kembali.

Sunting: silakan lihat komentar dari Chris Johnsen dan KPM, ini hanya berfungsi secara lokal dan file tetap di bawah kontrol versi untuk pengguna lain jika mereka juga tidak melakukannya. Jawaban yang diterima memberikan metode yang lebih lengkap / benar untuk menangani ini. Juga beberapa catatan dari tautan jika menggunakan metode ini:

Jelas ada beberapa peringatan yang ikut bermain dengan ini. Jika Anda langsung menambahkan file, itu akan ditambahkan ke indeks. Menggabungkan komit dengan flag ini aktif akan menyebabkan penggabungan gagal dengan anggun sehingga Anda dapat menanganinya secara manual.

Tom Power
sumber
7
Ini bukan jawaban untuk masalah khusus yang disebutkan. Ini hanya akan berfungsi untuk repositori lokal Anda sendiri, sehingga setiap pengguna harus melakukan ini sendiri. Ini menyakitkan.
KPM
28

Untuk menghapus file dari indeks, gunakan:

git reset myfile

Ini seharusnya tidak mempengaruhi salinan lokal Anda atau milik orang lain.

Armand
sumber
1
resethanya menghapus file dari indeks jika file tersebut tidak ada dalam komit KEPALA saat ini, jika tidak hanya mengembalikan versi indeks ke versi HEAD saat ini.
CB Bailey
1
Mungkin saya salah paham pertanyaannya, tetapi sepertinya berhubungan dengan menghapus file dari indeks tanpa mempengaruhi komitmen.
Armand
Seperti kata Charles, reset tidak "menghapus file". Ketikkan git help reset untuk info lebih lanjut.
Simon B.
4
Ini menjawab pertanyaan yang berjudul "Bagaimana menghapus file dari indeks tanpa menghapus file dari repositori apa pun". Meskipun OP benar-benar bertanya "Bagaimana cara membuka file tanpa menghapus salinan lokal."
hewsonism
@hewsonism OP memang mengatakan "sistem file apa pun" (bahkan sebelum diedit).
jbobbins
19
  1. git rm --cached remove_file
  2. tambahkan file ke gitignore
  3. git add .gitignore
  4. git commit -m "Excluding"
  5. Selamat bersenang-senang ;)
Анастасия Ермолик
sumber
1
Ini yang paling mudah.
Julien
15

Setelah melakukan git rm --cachedperintah, coba tambahkan myfileke .gitignorefile (buat satu jika tidak ada). Ini seharusnya memberitahu git untuk mengabaikannya myfile.

The .gitignorefile berversi, sehingga Anda akan perlu untuk melakukan itu dan mendorongnya ke repositori jauh.

awgy
sumber
1

Solusi saya adalah menarik copy pekerjaan lain dan kemudian lakukan:

git log --pretty="format:" --name-only -n1 | xargs git checkout HEAD^1

yang mengatakan dapatkan semua jalur file di komentar terbaru, dan periksa dari induk HEAD. Pekerjaan selesai.

Tom Viner
sumber
0

Solusi di atas berfungsi dengan baik untuk sebagian besar kasus. Namun, jika Anda juga perlu menghapus semua jejak file itu (yaitu data sensitif seperti kata sandi), Anda juga ingin menghapusnya dari seluruh riwayat komit Anda, karena file tersebut masih dapat diambil dari sana.

Berikut adalah solusi yang menghapus semua jejak file dari seluruh riwayat komit Anda, seolah-olah tidak pernah ada, namun tetap menyimpan file di sistem Anda.

https://help.github.com/articles/remove-sensitive-data/

Anda sebenarnya dapat melompat ke langkah 3 jika Anda berada di repositori git lokal Anda, dan tidak perlu melakukan dry run. Dalam kasus saya, saya hanya perlu langkah 3 dan 6, karena saya sudah membuat file .gitignore saya, dan berada di repositori yang ingin saya kerjakan.

Untuk melihat perubahan Anda, Anda mungkin perlu pergi ke root GitHub dari repositori Anda dan menyegarkan halaman. Kemudian navigasikan melalui tautan untuk mendapatkan komit lama yang pernah memiliki file, untuk melihat bahwa itu sekarang telah dihapus. Bagi saya, hanya menyegarkan halaman komit lama tidak menunjukkan perubahan.

Itu tampak menakutkan pada awalnya, tapi sungguh, itu mudah dan bekerja seperti pesona! :-)

SherylHohman
sumber