Menangani nama file di git

440

Saya pernah membaca bahwa ketika mengganti nama file di git , Anda harus melakukan perubahan apa pun, melakukan penggantian nama, lalu memformat file yang diubah namanya. Git akan mengenali file dari konten, daripada melihatnya sebagai file baru yang tidak terlacak, dan menyimpan sejarah perubahan.

Namun, melakukan hal ini malam ini akhirnya saya kembalikan git mv.

> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   index.html
#

Ganti nama stylesheet saya di Finder dari iphone.cssmenjadimobile.css

> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   index.html
#
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   deleted:    css/iphone.css
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   css/mobile.css

Jadi git sekarang berpikir saya sudah menghapus satu file CSS, dan menambahkan yang baru. Bukan yang saya inginkan, mari batalkan ganti nama dan biarkan git melakukan pekerjaan.

> $ git reset HEAD .
Unstaged changes after reset:
M   css/iphone.css
M   index.html

Kembali ke tempat saya mulai.

> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   index.html
#

Mari kita gunakan git mvsaja.

> $ git mv css/iphone.css css/mobile.css
> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   renamed:    css/iphone.css -> css/mobile.css
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   index.html
#

Sepertinya kita baik-baik saja. Jadi mengapa git tidak mengenali nama baru pertama kali ketika saya menggunakan Finder?

Greg K.
sumber
29
Git melacak konten, bukan file, jadi tidak masalah bagaimana Anda memasukkan indeks ke kondisi yang tepat - add+rmatau mv- menghasilkan hasil yang sama. Git kemudian menggunakan rename / copy detection untuk memberi tahu Anda bahwa itu adalah rename. Sumber yang Anda kutip juga tidak akurat. Tidak masalah apakah Anda memodifikasi + mengganti nama di komit yang sama atau tidak. Ketika Anda melakukan diff di kedua modifikasi dan ganti nama, deteksi ganti nama akan melihatnya sebagai perubahan nama + modifikasi, atau jika modifikasi adalah penulisan ulang total, itu akan ditampilkan sebagai ditambahkan dan dihapus - masih tidak peduli bagaimana Anda melakukan Itu.
Cascabel
6
Jika ini benar, mengapa tidak dideteksi dengan nama saya menggunakan Finder?
Greg K
26
git mv old newsecara otomatis memperbarui indeks. Ketika Anda mengganti nama di luar Git, Anda harus melakukan git add newdan git rm oldmelakukan perubahan pada indeks. Setelah selesai, ini git statusakan berfungsi seperti yang Anda harapkan.
Chris Johnsen
4
Saya baru saja memindahkan banyak file ke public_htmldir, yang dilacak di git. Setelah dilakukan git add .dan git commit, masih menunjukkan banyak file 'dihapus' di git status. Saya melakukan git commit -adan penghapusan dilakukan tetapi sekarang saya tidak memiliki riwayat pada file yang hidup public_htmlsekarang. Alur kerja ini tidak semulus yang saya inginkan.
Greg K

Jawaban:

352

Untuk git mvitu halaman buku panduan mengatakan

Indeks diperbarui setelah berhasil diselesaikan, [...]

Jadi, pada awalnya, Anda harus memperbarui indeks sendiri (dengan menggunakan git add mobile.css). Namun
git status akan tetap menampilkan dua file berbeda

$ git status
# On branch master
warning: LF will be replaced by CRLF in index.html
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   index.html
#       new file:   mobile.css
#
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       deleted:    iphone.css
#

Anda bisa mendapatkan output berbeda dengan menjalankan git commit --dry-run -a, yang menghasilkan apa yang Anda harapkan:

Tanascius@H181 /d/temp/blo (master)
$ git commit --dry-run -a
# On branch master
warning: LF will be replaced by CRLF in index.html
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   index.html
#       renamed:    iphone.css -> mobile.css
#

Saya tidak bisa memberi tahu Anda dengan tepat mengapa kita melihat perbedaan antara git statusdan
git commit --dry-run -a, tapi ini adalah petunjuk dari Linus :

git benar-benar bahkan tidak peduli dengan keseluruhan "rename detection" secara internal, dan komitmen apa pun yang telah Anda lakukan dengan penggantian nama benar-benar independen dari heuristik yang kemudian kami gunakan untuk menunjukkan penggantian nama tersebut.

A dry-runmenggunakan mekanisme penggantian nama yang sebenarnya, sementara yang git statusmungkin tidak.

tanascius
sumber
1
Anda gagal menyebutkan langkah di mana Anda melakukannya git add mobile.css. Tanpanya git status -ahanya akan 'melihat' penghapusan iphone.cssfile yang sebelumnya dilacak tetapi tidak akan menyentuh file baru yang tidak terlacak mobile.css. Juga, git status -atidak valid dengan Git 1.7.0 dan yang lebih baru. "" Git status "bukan" git commit --dry-run "lagi." di kernel.org/pub/software/scm/git/docs/RelNotes-1.7.0.txt . Gunakan git commit --dry-run -ajika Anda ingin fungsi ini. Seperti yang orang lain katakan, perbarui saja indeks dan git statushanya akan berfungsi seperti yang diharapkan OP.
Chris Johnsen
3
jika Anda melakukan git commithal yang normal itu tidak akan melakukan file yang diubah namanya dan pohon yang bekerja masih sama. git commit -amengalahkan hampir setiap aspek dari alur kerja / model berpikir git — setiap perubahan dilakukan. bagaimana jika Anda hanya ingin mengubah nama file, tetapi komit perubahan index.htmldi komit lain?
Knittl
@ Chris: ya, tentu saja saya menambahkan mobile.cssyang seharusnya saya sebutkan. Tapi itulah inti dari jawaban saya: halaman manual mengatakan itu the index is updatedketika Anda menggunakan git-mv. Terima kasih atas status -aklarifikasi, saya menggunakan git 1.6.4
tanascius
4
Jawaban bagus! Saya membenturkan kepala ke dinding mencoba mencari tahu mengapa git statustidak mendeteksi penggantian nama. Berjalan git commit -a --dry-runsetelah menambahkan file "baru" saya menunjukkan nama dan akhirnya memberi saya kepercayaan untuk melakukan!
stephen.hanson
1
Di git 1.9.1 git statussekarang berperilaku seperti git commit.
Jacques René Mesrine
77

Anda harus menambahkan dua file yang dimodifikasi ke indeks sebelum git akan mengenalinya sebagai langkah.

Satu-satunya perbedaan antara mv old newdan git mv old newadalah bahwa git mv juga menambahkan file ke indeks.

mv old newmaka git add -Aakan bekerja juga.

Perhatikan bahwa Anda tidak bisa hanya menggunakan git add .karena itu tidak menambah kepindahan ke indeks.

Lihat Perbedaan antara "git add -A" dan "git add."

tidak berbentuk persegi panjang
sumber
3
Terima kasih atas git add -Atautannya, sangat berguna, karena saya mencari jalan pintas seperti itu!
PhiLho
5
Perhatikan bahwa dengan git 2, git add . tidak menambah kepindahan ke indeks.
Nick McCurdy
19

Hal terbaik adalah mencobanya sendiri.

mkdir test
cd test
git init
touch aaa.txt
git add .
git commit -a -m "New file"
mv aaa.txt bbb.txt
git add .
git status
git commit --dry-run -a

Sekarang status git dan komit git --dry-run -a menunjukkan dua hasil yang berbeda di mana status git menunjukkan bbb.txt sebagai file baru / aaa.txt dihapus, dan perintah --dry-run perintah menunjukkan nama sebenarnya.

~/test$ git status

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   bbb.txt
#
# 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)
#
#   deleted:    aaa.txt
#


/test$ git commit --dry-run -a

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   renamed:    aaa.txt -> bbb.txt
#

Sekarang lanjutkan dan lakukan check-in.

git commit -a -m "Rename"

Sekarang Anda dapat melihat bahwa file tersebut sebenarnya diganti namanya, dan apa yang ditampilkan dalam status git salah.

Moral dari cerita ini: Jika Anda tidak yakin apakah file Anda diganti namanya, berikan "git commit --dry-run -a". Jika itu menunjukkan bahwa file diubah namanya, Anda baik untuk pergi.

dimuthu
sumber
3
Untuk apa yang penting bagi Git, keduanya benar . Yang terakhir lebih dekat dengan bagaimana Anda, seperti yang mungkin dilihat oleh commiter. Perbedaan nyata antara rename dan delete + create hanya pada level OS / sistem file (mis. Inode yang sama # vs inode baru #), yang mana Git tidak terlalu peduli.
Alois Mahdal
17

Untuk git 1.7.x perintah berikut ini berfungsi untuk saya:

git mv css/iphone.css css/mobile.css
git commit -m 'Rename folder.' 

Tidak perlu untuk git add, karena file asli (yaitu css / mobile.css) sudah ada di file yang dikomit sebelumnya.

GrigorisG
sumber
6
Ini. Semua jawaban lain sangat rumit dan tidak perlu. Ini mempertahankan riwayat file antara komit sehingga penggabungan sebelum / sesudah nama file tidak rusak.
Phlucious
10

Anda harus ke git add css/mobile.cssfile baru dan git rm css/iphone.css, jadi git tahu tentang itu. maka akan menampilkan output yang sama digit status

Anda dapat melihatnya dengan jelas di keluaran status (nama baru file):

# Untracked files:
#   (use "git add <file>..." to include in what will be committed)

dan (nama lama):

# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)

saya pikir di balik layar git mvtidak lebih dari skrip pembungkus yang melakukan hal itu: hapus file dari indeks dan tambahkan dengan nama yang berbeda

rajutan
sumber
Saya tidak berpikir saya harus melakukannya git rm css/iphone.csskarena saya pikir ini akan menghapus sejarah yang ada. Mungkin saya salah paham alur kerja di git.
Greg K
4
@Greg K: git rmtidak akan menghapus riwayat. Itu hanya menghapus entri dari indeks sehingga komit berikutnya tidak akan memiliki entri. Namun, itu masih akan ada dalam komitmen leluhur. Yang mungkin Anda bingung adalah bahwa (misalnya) git log -- newakan berhenti pada titik di mana Anda berkomitmen git mv old new. Jika Anda ingin mengikuti penggantian nama, gunakan git log --follow -- new.
Chris Johnsen
9

Mari kita pikirkan file Anda dari perspektif git.

Ingat, git tidak melacak metadata apa pun tentang file Anda

Repositori Anda memiliki (antara lain)

$ cd repo
$ ls
...
iphone.css
...

dan itu di bawah kontrol git:

$ git ls-files --error-unmatch iphone.css &>/dev/null && echo file is tracked
file is tracked

Uji ini dengan:

$ touch newfile
$ git ls-files --error-unmatch newfile &>/dev/null && echo file is tracked
(no output, it is not tracked)
$ rm newfile

Saat kamu melakukan

$ mv iphone.css mobile.css

Dari perspektif git,

  • tidak ada iphone.css (ini dihapus -git memperingatkan tentang itu-).
  • ada file baru mobile.css .
  • File-file itu sama sekali tidak terkait.

Jadi, git menyarankan tentang file-file yang sudah dikenalnya ( iphone.css ) dan file-file baru yang dideteksinya ( mobile.css ) tetapi hanya ketika file-file tersebut berada dalam indeks atau HEAD git mulai memeriksa isinya.

Saat ini, penghapusan "iphone.css" maupun mobile.css tidak ada dalam indeks.

Tambahkan penghapusan iphone.css ke indeks

$ git rm iphone.css

git memberi tahu Anda apa yang telah terjadi: ( iphone.css dihapus. Tidak ada lagi yang terjadi)

lalu tambahkan file baru mobile.css

$ git add mobile.css

Kali ini penghapusan dan file baru sedang dalam indeks. Sekarang git mendeteksi konteksnya sama dan mengeksposnya sebagai ganti nama. Bahkan jika file 50% mirip itu akan mendeteksi itu sebagai nama, yang memungkinkan Anda mengubah mobile.css sedikit sambil menjaga operasi sebagai nama.

Lihat ini dapat direproduksi pada git diff. Sekarang file Anda berada di indeks yang harus Anda gunakan --cached. Edit sedikit mobile.css , tambahkan itu ke indeks dan lihat perbedaan antara:

$ git diff --cached 

dan

$ git diff --cached -M

-Madalah opsi "detect renames" untuk git diff. -Mkependekan -M50%(50% atau lebih kesamaan akan membuat git mengekspresikannya sebagai ganti nama) tetapi Anda dapat mengurangi ini menjadi -M20%(20%) jika Anda banyak mengedit mobile.css.

Albfan
sumber
8

Langkah 1: ganti nama file dari file lama ke file baru

git mv #oldfile #newfile

Langkah2: git melakukan dan menambahkan komentar

git commit -m "rename oldfile to newfile"

Langkah 3: dorong perubahan ini ke jarak jauh

git push origin #localbranch:#remotebranch
Haimei
sumber
1
Silakan tambahkan beberapa komentar sehingga sangat membantu OP
Devrath
Langkah 2 tidak perlu. Setelah itu git mv, file baru sudah ada dalam indeks.
Alois Mahdal
7

Git akan mengenali file dari konten, daripada melihatnya sebagai file baru yang tidak terlacak

Di situlah Anda salah.

Hanya setelah Anda menambahkan file, git akan mengenalinya dari konten.

Hasen
sumber
Persis. Saat dipentaskan git akan menampilkan ganti nama dengan benar.
Max MacLeod
3

Anda tidak menampilkan hasil gerakan pencari Anda. Saya percaya jika Anda melakukan pemindahan melalui Finder dan kemudian melakukannya git add css/mobile.css ; git rm css/iphone.css, git akan menghitung hash dari file baru dan baru kemudian menyadari bahwa hash dari file-file tersebut cocok (dan dengan demikian itu adalah nama baru).

Mike Seplowitz
sumber
2

Dalam kasus di mana Anda benar-benar harus mengganti nama file secara manual, misalnya. menggunakan skrip untuk batch mengubah nama banyak file, kemudian menggunakan git add -A .bekerja untuk saya.

pckben
sumber
2

Untuk pengguna Xcode: Jika Anda mengganti nama file Anda di Xcode, Anda melihat ikon lencana berubah untuk ditambahkan. Jika Anda melakukan komit menggunakan XCode, Anda sebenarnya akan membuat file baru dan kehilangan histori.

Penanganannya mudah tetapi Anda harus melakukannya sebelum berkomitmen menggunakan Xcode:

  1. Lakukan Status git pada folder Anda. Anda harus melihat bahwa perubahan yang dilakukan sudah benar:

diganti namanya: Project / OldName.h -> Project / NewName.h diganti nama: Project / OldName.m -> Project / NewName.m

  1. lakukan commit -m 'perubahan nama'

Kemudian kembali ke XCode dan Anda akan melihat lencana berubah dari A ke M dan itu disimpan untuk melakukan perubahan furtur dalam menggunakan xcode sekarang.

doozMen
sumber