Mengapa ada dua cara untuk menghapus file di Git?

1169

Terkadang git menyarankan git rm --cacheduntuk menghapus stage suatu file, terkadang git reset HEAD file. Kapan saya harus menggunakan yang mana?

EDIT:

D:\code\gt2>git init
Initialized empty Git repository in D:/code/gt2/.git/
D:\code\gt2>touch a

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       a
nothing added to commit but untracked files present (use "git add" to track)

D:\code\gt2>git add a

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   a
#
D:\code\gt2>git commit -m a
[master (root-commit) c271e05] a
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a

D:\code\gt2>touch b

D:\code\gt2>git status
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       b
nothing added to commit but untracked files present (use "git add" to track)

D:\code\gt2>git add b

D:\code\gt2>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   b
#
Senthess
sumber
20
Mengapa? Saya katakan itu karena antarmuka commandline git berevolusi secara organik dan tidak pernah mengalami restrukturisasi besar untuk membuat semuanya konsisten. (Jika Anda tidak setuju, catatan bagaimana git rmbisa kedua tahap sebuah penghapusan dan juga unstage sebuah penambahan )
Roman Starkov
3
@romkyns: Saya setuju bahwa antarmuka Git memiliki beberapa keanehan karena berevolusi secara organik, tetapi penghapusan jelas merupakan fungsi kebalikan dari penambahan, jadi bukankah logis untuk rmmembatalkan add? Menurut Anda bagaimana rmseharusnya bersikap?
Zaz
6
Satu-satunya jawaban aktual untuk pertanyaan Anda adalah bahwa tepat setelah git inittidak ada HEADuntuk diatur ulang.
Miles Rout
Dokumen terbaik untuk ini: help.github.com/articles/changing-a-remote-s-url
ScottyBlades
4
@Zaz, saya akan memberikan pendapat saya. rmmenyiratkan penghapusan dalam konteks unix. Bukan kebalikan dari menambah indeks. Fungsi untuk menghapus file tidak boleh dibebani dengan fungsi untuk mengubah status pementasan. Jika ada detail implementasi yang membuat mereka nyaman untuk digabungkan, itu hanya menunjuk pada kurangnya lapisan abstraksi di git, yang akan membuat kegunaan lebih jelas.
Joshua Goldberg

Jawaban:

1893

git rm --cached <filePath> tidak menghapus suatu file, itu sebenarnya tahapan penghapusan file dari repo (dengan asumsi itu sudah dilakukan sebelumnya) tetapi meninggalkan file di pohon kerja Anda (meninggalkan Anda dengan file yang tidak dilacak).

git reset -- <filePath>akan menghapus tahapan perubahan bertahap untuk file yang diberikan.

Yang mengatakan, jika Anda menggunakan git rm --cachedfile baru yang dipentaskan, itu pada dasarnya akan terlihat seperti Anda baru saja tidak dipentaskan karena tidak pernah dilakukan sebelumnya.

Perbarui git 2.24
Dalam versi ini lebih baru dari git Anda dapat menggunakan git restore --stagedbukan git reset. Lihat git docs .

Ryan Stewart
sumber
71
Saya akan mengatakan git rm --cachedunstages file tetapi tidak menghapusnya dari direktori kerja.
Pierre de LESPINAY
4
Untuk menghapus file yang dipentaskan untuk penambahan sehingga tidak lagi dipentaskan pasti bisa disebut "unstaging file yang dipentaskan untuk penambahan", bukan? Hasil akhirnya bukanlah penghapusan bertahap , itu sudah pasti, maka saya pikir kesalahpahaman ini benar-benar dapat dimengerti.
Roman Starkov
4
Jadi biasanya, seseorang akan menggunakan git rm --cached <filePath>untuk menghapus beberapa file dari repo setelah menyadarinya seharusnya tidak pernah dalam repo: jadi kemungkinan besar menjalankan perintah ini & kemudian menambahkan file yang relevan ke gitignore. Apakah saya benar?
Adrien Be
13
Dengan banyaknya suara pada pertanyaan dan jawaban, saya akan mengatakan bahwa tampaknya kami ingin memiliki unstageperintah git.
milosmns
4
"git status" menyarankan sekarang: gunakan "git restore --staged <file> ..." untuk unstage
yucer
334

git rm --cacheddigunakan untuk menghapus file dari indeks. Dalam kasus di mana file sudah di repo, git rm --cachedakan menghapus file dari indeks, meninggalkannya di direktori kerja dan komit sekarang akan menghapusnya dari repo juga. Pada dasarnya, setelah komit, Anda akan membatalkan versi file dan menyimpan salinan lokal.

git reset HEAD file(yang secara default menggunakan --mixedflag) berbeda dalam hal di mana file sudah dalam repo, ia mengganti versi indeks file dengan yang dari repo (KEPALA), efektif membatalkan modifikasi untuk itu.

Dalam hal file tidak berversi, itu akan menghapus stage seluruh file karena file itu tidak ada di HEAD. Dalam aspek ini git reset HEAD filedan git rm --cachedsama, tetapi mereka tidak sama (seperti yang dijelaskan dalam kasus file yang sudah di repo)

Untuk pertanyaan Why are there 2 ways to unstage a file in git?- tidak pernah ada satu cara untuk melakukan apapun di git. itulah keindahannya :)

manojlds
sumber
7
Baik jawaban yang diterima dan yang ini bagus, dan jelaskan mengapa Anda akan menggunakan yang satu vs yang lain. Tetapi mereka tidak langsung menjawab pertanyaan implisit mengapa git menyarankan dua metode berbeda. Dalam kasus pertama dalam contoh OP, git init baru saja dilakukan. Dalam kasus itu, git menyarankan "git rm --cached" karena pada saat itu tidak ada komit dalam repositori sehingga HEAD tidak valid. "git reset HEAD - a" menghasilkan: "fatal: Gagal menyelesaikan 'HEAD' sebagai referensi yang valid."
sootsnoot
5
dengan 'git checkout', tidakkah Anda akan kehilangan semua perubahan yang Anda buat pada file itu? Itu tidak sama dengan menghapus file, kecuali saya salah paham.
John Deighan
there is never really only one way to do anything in git. that is the beauty of it- Hmm ... kenapa? itu selalu hebat, ketika hanya ada satu cara yang jelas. ini menghemat banyak waktu dan ingatan kita di otak))
Oto Shavadze
128

Cukup sederhana:

  • git rm --cached <file> membuat git berhenti melacak file sepenuhnya (meninggalkannya di sistem file, tidak seperti biasa git rm*)
  • git reset HEAD <file> hapus tahapan modifikasi apa pun yang dilakukan pada file sejak komit terakhir (tetapi tidak mengembalikannya dalam sistem file, bertentangan dengan apa yang mungkin disarankan nama perintah **). File tetap di bawah kendali revisi.

Jika file tidak dalam kontrol revisi sebelumnya (yaitu Anda menghapus stage file yang baru saja Anda git addedit untuk pertama kali), maka kedua perintah memiliki efek yang sama, maka penampilan ini menjadi "dua cara melakukan sesuatu ".

* Ingatlah peringatan @DrewT menyebutkan dalam jawabannya, tentang git rm --cachedfile yang sebelumnya dikomit ke repositori. Dalam konteks pertanyaan ini, dari file yang baru saja ditambahkan dan belum dilakukan, tidak ada yang perlu dikhawatirkan.

** Saya takut untuk waktu yang lama dan memalukan untuk menggunakan perintah git reset karena namanya - dan masih hari ini saya sering mencari sintaks untuk memastikan saya tidak mengacau. ( pembaruan : Saya akhirnya meluangkan waktu untuk meringkas penggunaan git resetdalam halaman tldr , jadi sekarang saya memiliki model mental yang lebih baik tentang cara kerjanya, dan referensi cepat ketika saya lupa beberapa detail.)

waldyrious
sumber
Inigit rm <file> --cached
neonmate
8
Saya benar-benar tidak berpikir hasil edit pada 4 Agustus 2015 untuk jawaban ini merupakan peningkatan keseluruhan. Mungkin memperbaiki kebenaran teknis (saya tidak merasa memenuhi syarat untuk mengevaluasi itu), tapi saya khawatir itu membuat nada jawaban menjadi jauh lebih mudah diakses, dengan memperkenalkan bahasa seperti "unsets imperative untuk mulai melacak file yang saat ini tidak terlacak ", dan menggunakan jargon seperti" indeks "dan" HEAD ", tepatnya jenis barang yang menakuti para pemula. Jika ada yang bisa, silakan edit untuk mengembalikan bahasa yang lebih ramah pengguna baru.
waldyrious
5
Setuju dengan @waldyrious. Jawaban aslinya mungkin tidak langsung dari buku teks git, tetapi menjawab pertanyaan pada tingkat teknis yang memadai. Rincian teknis seharusnya diklarifikasi dalam komentar, bukan sebagai suntingan yang mengaburkan maksud aslinya.
Simon Robb
Saya telah mengembalikan hasil edit. Saya percaya sudah ada validasi yang cukup oleh komunitas (dalam komentar sebelumnya dan suara mereka) bahwa suntingan itu merusak kejelasan jawabannya.
waldyrious
Catatan @DrewT memperingatkan bahwa jika menggunakan rm --cacheddan mendorong, siapa pun yang menarik cabang yang sama akan benar-benar menghapus file dari pohon kerjanya.
Tom Hale
53

Utas ini agak lama, tetapi saya masih ingin menambahkan sedikit demonstrasi karena masih bukan masalah intuitif:

me$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   to-be-added
#   modified:   to-be-modified
#   deleted:    to-be-removed
#

me$ git reset -q HEAD to-be-added

    # ok

me$ git reset -q HEAD to-be-modified

    # ok

me$ git reset -q HEAD to-be-removed

    # ok

# or alternatively:

me$ git reset -q HEAD to-be-added to-be-removed to-be-modified

    # ok

me$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   to-be-modified
#   deleted:    to-be-removed
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   to-be-added
no changes added to commit (use "git add" and/or "git commit -a")

git reset HEAD(tanpa -q) memberikan peringatan tentang file yang dimodifikasi dan kode keluarnya adalah 1 yang akan dianggap sebagai kesalahan dalam skrip.

Sunting: git checkout HEAD to-be-modified to-be-removedjuga berfungsi untuk menghapus stage, tetapi menghapus perubahan sepenuhnya dari ruang kerja

Perbarui git 2.23.0: Dari waktu ke waktu, perintah berubah. Sekarang, git statuskatakan:

  (use "git restore --staged <file>..." to unstage)

... yang berfungsi untuk ketiga jenis perubahan

Daniel Alder
sumber
Terima kasih, tidak sepenuhnya jelas dari dua jawaban pertama (mungkin hanya ketidaktahuan saya tentang terminologi) bahwa git reset meninggalkan modifikasi dalam file secara lokal (sebagai lawan dari git checkout yang akan mengembalikannya).
soupdog
Anda harus memberi peringatan di awal tentang versi, karena versi lama menghapus file dalam versi baru
Luis Mauricio
@LuisMauricio perintah mana yang menghapus file?
Daniel Alder
@DanielAlder sry, saya hanya mencoba ulang, itu tidak menghapus, kesalahan saya.
Luis Mauricio
36

jika Anda secara tidak sengaja melakukan pementasan file yang tidak ingin Anda komit, dan ingin memastikan Anda menyimpan perubahannya, Anda juga dapat menggunakan:

git stash
git stash pop

ini melakukan reset ke HEAD dan menerapkan kembali perubahan Anda, memungkinkan Anda untuk mem-stage ulang file individu untuk komit. ini juga membantu jika Anda lupa membuat cabang fitur untuk permintaan tarik ( git stash ; git checkout -b <feature> ; git stash pop).

ives
sumber
3
Ini adalah solusi bersih dan jauh lebih mengkhawatirkan daripada mengetik "git rm"
Subimage
1
git stashmemiliki manfaat terkait lainnya, karena membuat entri di reflog yang kemudian tersedia di masa depan. ketika ragu, silakan dan lakukan git stash(misalnya git stash save -u "WIP notes to self"('-u' adalah untuk memasukkan file baru / tidak terlacak dalam komit simpanan) ... kemudian cobalah git reflog show stashuntuk melihat daftar komit simpanan dan sha mereka. Saya sarankan shell alias sukaalias grs="git reflog show stash"
minggu
15

2 perintah ini memiliki beberapa perbedaan halus jika file tersebut sudah dalam repo dan di bawah kontrol versi (sebelumnya dilakukan dll):

  • git reset HEAD <file> hapus stage file di komit saat ini.
  • git rm --cached <file>akan membatalkan pentahapan file untuk komitmen selanjutnya. Ini tidak dipentaskan hingga ditambahkan lagi dengan git add <file>.

Dan ada satu lagi perbedaan penting:

  • Setelah berjalan git rm --cached <file>dan mendorong cabang Anda ke jarak jauh, siapa pun yang menarik cabang Anda dari jarak jauh akan mendapatkan file SEBENARNYA dihapus dari folder mereka, meskipun di set kerja lokal Anda file hanya menjadi tidak terlacak (yaitu secara fisik tidak dihapus dari folder).

Perbedaan terakhir ini penting untuk proyek-proyek yang menyertakan file konfigurasi di mana setiap pengembang di tim memiliki konfigurasi yang berbeda (yaitu url basis, pengaturan ip atau port yang berbeda) sehingga jika Anda menggunakan git rm --cached <file>siapa pun yang menarik cabang Anda harus secara manual mengulangi buat config, atau Anda dapat mengirimkan milik Anda kepada mereka dan mereka dapat mengeditnya kembali ke pengaturan ip mereka (dll.), karena hanya menghapus efek orang yang menarik cabang Anda dari jarak jauh.

DrewT
sumber
10

Katakanlah Anda stageseluruh direktori melalui git add <folder>, tetapi Anda ingin mengecualikan file dari daftar bertahap (yaitu daftar yang menghasilkan saat menjalankan git status) dan menyimpan modifikasi dalam file yang dikecualikan (Anda sedang mengerjakan sesuatu dan itu tidak siap untuk komit, tetapi Anda tidak ingin kehilangan pekerjaan Anda ...). Anda cukup menggunakan:

git reset <file>

Ketika Anda menjalankan git status, Anda akan melihat bahwa file apa pun yang Anda resetmiliki unstageddan sisa file addedyang masih ada dalam stageddaftar.

Jiminikiz
sumber
10

1.

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   a

(gunakan "git rm --cached ..." untuk unstage)

  • git adalah sistem pointer

  • Anda belum memiliki komit untuk mengubah pointer Anda

  • satu-satunya cara untuk 'mengeluarkan file dari bucket yang sedang diarahkan' adalah dengan menghapus file yang Anda beri tahu git untuk melihat perubahan

2.

D:\code\gt2>git commit -m a
[master (root-commit) c271e05] a
0 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 a

git melakukan -ma

  • Anda berkomitmen, ' disimpan '

3.

D:\code\gt2>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   b
#

(gunakan "git reset HEAD ..." untuk unstage)

  • Anda membuat komit dalam kode Anda saat ini
  • sekarang Anda dapat mengatur ulang kursor ke komit ' kembalikan ke penyimpanan sebelumnya '
Timothy LJ Stewart
sumber
1
Ini sebenarnya satu-satunya jawaban yang menjawab pertanyaan dengan benar, IMO. Ini sebenarnya menjawab pertanyaan, yang bukan 'apa perbedaan antara' git rm --cached 'dan' git reset HEAD 'tetapi' mengapa git tidak konsisten memberikan keduanya sebagai opsi? ', Jawabannya adalah bahwa tidak ada HEAD untuk diatur ulang ketika Anda git inituntuk pertama kalinya.
Miles Rout
5

Saya terkejut tidak ada yang menyebutkan reflit git ( http://git-scm.com/docs/git-reflog ):

# git reflog
<find the place before your staged anything>
# git reset HEAD@{1}

Reflog adalah riwayat git yang tidak hanya melacak perubahan pada repo, tetapi juga melacak tindakan pengguna (Misalnya tarikan, checkout ke cabang yang berbeda, dll) dan memungkinkan untuk membatalkan tindakan tersebut. Jadi, alih-alih menghapus file yang dipentaskan secara salah, di mana Anda dapat kembali ke titik di mana Anda tidak menampilkan file.

Ini mirip dengan git reset HEAD <file>tetapi dalam kasus-kasus tertentu mungkin lebih rinci.

Maaf - tidak benar-benar menjawab pertanyaan Anda, tetapi hanya menunjuk cara lain untuk meng-unstage file yang saya gunakan cukup sering (I untuk satu seperti jawaban oleh Ryan Stewart dan sangat banyak.);) Saya harap ini membantu.

Alex
sumber
5

Cukup gunakan:

git reset HEAD <filename>

Ini menghapus panggung file dan membuat perubahan yang Anda lakukan padanya, sehingga Anda dapat, pada gilirannya, mengubah cabang jika Anda ingin dan git addfile-file itu ke cabang lain sebagai gantinya. Semua perubahan disimpan.

Edgar Quintero
sumber
3

Sepertinya saya yang git rm --cached <file>menghapus file dari indeks tanpa menghapusnya dari direktori di mana sebuah plain git rm <file>akan melakukan keduanya, seperti halnya OS rm <file>akan menghapus file dari direktori tanpa menghapus versinya.

ernie.cordell
sumber
1

Hanya untuk versi 2.23 dan lebih baru,

Alih-alih saran ini, Anda dapat menggunakan git restore --staged <file>untuk unstagefile.

Kaan Taha Köken
sumber
Ini berfungsi baik dengan opsi --stagemaupun --staged.
dhana1310