Mencoba memperbaiki akhir baris dengan git filter-branch, tetapi tidak berhasil

270

Saya telah digigit oleh masalah akhir baris Windows / Linux dengan git. Tampaknya, melalui GitHub, MSysGit, dan sumber-sumber lain, bahwa solusi terbaik adalah mengatur repo lokal Anda untuk menggunakan akhiran garis gaya-linux, tetapi diatur core.autocrlfke true. Sayangnya, saya tidak melakukan ini cukup awal, jadi sekarang setiap kali saya menarik perubahan, akhir baris borked.

Saya pikir saya telah menemukan jawaban di sini, tetapi saya tidak dapat membuatnya bekerja untuk saya. Pengetahuan Linux command line saya terbatas, jadi saya bahkan tidak yakin apa yang dilakukan oleh "xargs fromdos" dalam skripnya. Saya terus mendapatkan pesan tentang tidak ada file atau direktori seperti itu, dan ketika saya berhasil mengarahkannya ke direktori yang ada, ia memberi tahu saya bahwa saya tidak memiliki izin.

Saya sudah mencoba ini dengan MSysGit di Windows dan melalui terminal Mac OS X.

Brian Donahue
sumber
Saya tidak bisa membatalkan utas ini bahkan hampir cukup. +1 ++ untuk itu memberikan jawaban terbaik tentang masalah ini.
sjas
Setuju dengan Charles. Namun, dalam kasus saya (menggunakan Mac OS X 10.8)> git config core.autocrlf false bekerja, tidak> git config core.autocrlf input
user1045085

Jawaban:

187

Dokumentasi git untuk gitattributes sekarang mendokumentasikan pendekatan lain untuk "memperbaiki" atau menormalkan semua akhir baris dalam proyek Anda. Inilah intinya:

$ echo "* text=auto" >.gitattributes
$ git add --renormalize .
$ git status        # Show files that will be normalized
$ git commit -m "Introduce end-of-line normalization"

Jika ada file yang seharusnya tidak dinormalisasi muncul dalam status git, batalkan atribut teks mereka sebelum menjalankan git add -u.

manual.pdf -text

Sebaliknya, file teks yang tidak terdeteksi git dapat mengaktifkan normalisasi secara manual.

weirdchars.txt text

Ini memanfaatkan --renormalizebendera baru yang ditambahkan di git v2.16.0, dirilis Jan 2018. Untuk versi git yang lebih lama, ada beberapa langkah lagi:

$ echo "* text=auto" >>.gitattributes
$ rm .git/index     # Remove the index to force git to
$ git reset         # re-scan the working directory
$ git status        # Show files that will be normalized
$ git add -u
$ git add .gitattributes
$ git commit -m "Introduce end-of-line normalization"
Russ Egan
sumber
1
Bisakah Anda memberi tahu saya apa tujuannya git reset?
crdx
1
memaksa git untuk membangun kembali indeks, di mana ia memindai setiap file untuk membuat perkiraan tentang apakah binernya. Perusahaan menghapus indeks lama, reset membangun indeks baru.
Russ Egan
16
Terima kasih, ini berhasil untuk saya. Perintah yang berguna setelah menjalankan git statusadalah menjalankan git diff --ignore-space-at-eolhanya untuk memastikan bahwa satu-satunya perubahan yang Anda lakukan adalah akhiran baris.
zelanix
1
Catatan: Satu-satunya perbedaan "nyata" antara ini dan solusi "lama" adalah di hadapan .gitattributes (dengan konten yang sesuai). Tanpa ini, tidak git resetakan mendeteksi modifikasi, dan dengan demikian tidak berguna.
Rob
3
Petunjuk pada gitattributes halaman telah diperbarui untuk mengambil keuntungan dari --renormalizebendera ditambahkan dalam v2.16.0 git yang dirilis pada bulan Januari 2018. --renormalizeBendera mengkonsolidasikan proses akhir baris re-processing untuk setiap file dilacak ke satu perintah: git add --renormalize ..
Mike Hill
389

Cara termudah untuk memperbaikinya adalah dengan membuat satu komit yang memperbaiki semua akhir baris. Dengan asumsi bahwa Anda tidak memiliki file yang dimodifikasi, maka Anda dapat melakukan ini sebagai berikut.

# From the root of your repository remove everything from the index
git rm --cached -r .

# Change the autocrlf setting of the repository (you may want 
#  to use true on windows):
git config core.autocrlf input

# Re-add all the deleted files to the index
# (You should get lots of messages like:
#   warning: CRLF will be replaced by LF in <file>.)
git diff --cached --name-only -z | xargs -0 git add

# Commit
git commit -m "Fixed crlf issue"

# If you're doing this on a Unix/Mac OSX clone then optionally remove
# the working tree and re-check everything out with the correct line endings.
git ls-files -z | xargs -0 rm
git checkout .
CB Bailey
sumber
7
PS Saya merekomendasikan perbaikan Anda kepada orang-orang di github.com dan mereka memperbarui panduan bantuan mereka untuk menggunakan solusi Anda (sebelumnya itu hanya merekomendasikan klon baru dan hard reset, yang tampaknya tidak mendapatkan semua file.) Help.github. com / dealing-with-lineendings
Brian Donahue
31
Terima kasih ... ini perbaikan yang bagus. Ditemukan di GitHub.
PHLAK
4
Anda mungkin juga ingin memeriksa config.safecrlf untuk memastikan bahwa Anda tidak mengubah crlf di file non-teks (seperti biner). Lihat di docs kernel.org/pub/software/scm/git/docs/git-config.html .
vrish88
4
@ vrish88: Namun, jika Anda berada dalam situasi ini, kemungkinan besar Anda menderita ujung campuran berjajar dan core.safecrlf sebenarnya dapat mencegah Anda melakukan apa yang perlu Anda lakukan. Mungkin lebih mudah untuk tidak menggunakan safecrlf. git tidak sering mendapatkan deteksi file biner yang salah dan jika itu Anda dapat secara manual menandainya sebagai biner dengan .gitattribute dan memulihkan versi yang benar dari komit sebelumnya.
CB Bailey
26
Solusi yang lebih baru yang direkomendasikan dalam jawaban Russ Egan di bawah ini lebih sederhana dan tidak melibatkan hal-hal yang menakutkan seperti menghapus semua kode sumber Anda , jadi saya benar-benar akan merekomendasikan orang menggunakannya, meskipun solusi lama ini memiliki 10 kali lebih banyak suara!
Porculus
11

Prosedur saya untuk berurusan dengan akhir baris adalah sebagai berikut (pertempuran diuji pada banyak repo):

Saat membuat repo baru:

  • masukkan .gitattributeskomit pertama bersama dengan file khas lainnya seperti .gitignoredanREADME.md

Saat berurusan dengan repo yang ada:

  • Buat / modifikasi .gitattributessesuai
  • git commit -a -m "Modified gitattributes"
  • git rm --cached -r . && git reset --hard && git commit -a -m 'Normalize CRLF' -n"
    • -n( --no-verifyadalah untuk melewatkan kait pra-komit)
    • Saya harus cukup sering melakukannya sehingga saya mendefinisikannya sebagai alias alias fixCRLF="..."
  • ulangi perintah sebelumnya
    • ya, itu voodoo, tetapi umumnya saya harus menjalankan perintah dua kali, pertama kali itu menormalkan beberapa file, kedua kali bahkan lebih banyak file. Secara umum mungkin yang terbaik adalah mengulangi sampai tidak ada komit baru dibuat :)
  • bolak-balik antara yang lama (sesaat sebelum normalisasi) dan cabang baru beberapa kali. Setelah berpindah cabang, terkadang git akan menemukan lebih banyak file yang perlu dinormalisasi ulang!

Dalam .gitattributesI menyatakan semua file teks secara eksplisit memiliki LF EOL karena umumnya Windows tooling kompatibel dengan LF sementara non-Windows tooling tidak kompatibel dengan CRLF (bahkan banyak alat baris perintah nodejs menganggap LF dan karenanya dapat mengubah EOL dalam file Anda).

Isi dari .gitattributes

.gitattributesBiasanya saya terlihat seperti:

*.html eol=lf
*.js   eol=lf
*.json eol=lf
*.less eol=lf
*.md   eol=lf
*.svg  eol=lf
*.xml  eol=lf

Untuk mengetahui ekstensi berbeda apa yang dilacak oleh git di repo saat ini, lihat di sini

Masalah setelah normalisasi

Setelah ini selesai, ada satu peringatan lagi yang umum.

Katakanlah Anda mastersudah mutakhir dan dinormalisasi, lalu Anda checkout outdated-branch. Cukup sering setelah memeriksa cabang itu, git menandai banyak file yang dimodifikasi.

Solusinya adalah dengan melakukan commit palsu ( git add -A . && git commit -m 'fake commit') lalu git rebase master. Setelah rebase, komit palsu harus pergi.

jakub.g
sumber
1
Saya pikir saya akan menjadi gila, sampai saya membaca posting Anda, karena saya harus menjalankan urutan perintah yang ditentukan beberapa kali juga. Voodoo! ;)
Sean Fausett
Dengan versi git 2.7.0.windows.1, saya menggunakan yang berikut: git rm --cached -r . && git reset --hard && git add . && git commit -m "Normalize EOL" -n
Sean Fausett
4
git status --short|grep "^ *M"|awk '{print $2}'|xargs fromdos

Penjelasan:

  • git status --short

    Ini menampilkan setiap baris yang git sedang dan tidak sadari. File yang tidak di bawah kontrol git ditandai di awal baris dengan '?'. File yang dimodifikasi ditandai dengan M.

  • grep "^ *M"

    Ini menyaring hanya file-file yang telah dimodifikasi.

  • awk '{print $2}'

    Ini hanya menampilkan nama file tanpa spidol.

  • xargs fromdos

    Ini mengambil nama file dari perintah sebelumnya dan menjalankannya melalui utilitas 'fromdos' untuk mengkonversi akhir baris.

Lloyd Moore
sumber
Ini luar biasa. Terima kasih. Bagi siapa pun yang mencari solusi menggunakan Homebrew, dos2unixalih-alih fromdos.
Almir Sarajčić
4

Inilah cara saya memperbaiki semua akhir baris di seluruh riwayat menggunakan git filter-branch. The ^Mkarakter harus dimasukkan dengan menggunakan CTRL-V+ CTRL-M. Saya biasa dos2unixmengonversi file karena ini secara otomatis melompati file biner.

$ git filter-branch --tree-filter 'grep -IUrl "^M" | xargs -I {} dos2unix "{}"'
pfrenssen
sumber
3

"| Xargs fromdos" membaca dari input standar (file findditemukan) dan menggunakannya sebagai argumen untuk perintah fromdos, yang mengubah akhir baris. (Apakah fromdos standar dalam lingkungan itu? Saya sudah terbiasa dengan dos2unix). Perhatikan bahwa Anda dapat menghindari penggunaan xargs (terutama berguna jika Anda memiliki cukup file sehingga daftar argumen terlalu panjang untuk xargs):

find <path, tests...> -exec fromdos '{}' \;

atau

find <path, tests...> | while read file; do fromdos $file; done

Saya tidak sepenuhnya yakin tentang pesan kesalahan Anda. Saya berhasil menguji metode ini. Program apa yang menghasilkan masing-masing? Untuk file / direktori apa Anda tidak memiliki izin? Namun, inilah saran untuk menebak apa yang Anda miliki:

Salah satu cara mudah untuk mendapatkan kesalahan 'file tidak ditemukan' untuk skrip adalah dengan menggunakan jalur relatif - gunakan yang absolut. Anda juga bisa mendapatkan kesalahan izin jika skrip Anda belum dapat dieksekusi (chmod + x).

Tambahkan komentar dan saya akan mencoba dan membantu Anda menyelesaikannya!

Cascabel
sumber
Saya melihat contoh lain dengan dos2unix dan saya pikir ini entah bagaimana menyalin file ke folder bernama itu, tetapi sekarang saya mendapatkannya. Wow, sepertinya sudah jelas sekarang. Terima kasih atas bantuan Anda!
Brian Donahue
1

oke ... di bawah cygwin kita tidak memiliki fromdos yang mudah tersedia, dan subtitle awk itu meledak di wajah Anda jika Anda memiliki ruang di jalur untuk file yang dimodifikasi (yang kami punya), jadi saya harus melakukan itu agak berbeda:

git status --short | grep "^ *M" | sed 's/^ *M//' | xargs -n 1 dos2unix

kudos to @lloyd untuk sebagian besar solusi ini

Anton K
sumber
-2

Ikuti langkah-langkah ini jika tidak ada jawaban lain yang cocok untuk Anda:

  1. Jika Anda menggunakan Windows, lakukan git config --global core.autocrlf true; jika Anda menggunakan Unix, lakukangit config core.autocrlf input
  2. Lari git rm --cached -r .
  3. Hapus file .gitattributes
  4. Lari git add -A
  5. Lari git reset --hard

Maka lokal Anda harus bersih sekarang.

zs2020
sumber
4
Betulkah? Menghapus .gitattributesfile adalah solusi untuk masalah akhir baris?
Aleksandr M
Ya, harap jawab komentar oleh @AleksandrM
Mr_and_Mrs_D