Menulis ulang riwayat git untuk mengganti semua CRLF ke LF?

32

Saya akan mentransfer repositori Git pribadi dari kotak win32 ke Ubuntu. Walaupun saya dapat melakukan commit dos2unix terakhir, tetapi saya ingin menulis ulang seluruh sejarah, sehingga beberapa Git GUI akan menampilkan log / diff dengan benar. Misalnya, gitg akan menyisipkan baris kosong untuk setiap CR / LF.

Xiè Jìléi
sumber

Jawaban:

25

Anda dapat menggunakan git filter-branchuntuk itu, dengan --tree-filteropsi, dan menentukan --alluntuk cabang.

Berikut ini sebuah contoh (dimulai pada direktori kosong dengan file teks Unix-type:

Persiapan:

$ hexdump -C testfile 
00000000  61 0d 0a 62 0d 0a 63 0d  0a                       |a..b..c..|
00000009

$ git init
Initialized empty Git repository in /home/seigneur/tmp/a/.git/

$ git add testfile && git commit -m "dos file checked in"
[master (root-commit) df4970f] dos file checked in
 1 files changed, 3 insertions(+), 0 deletions(-)
 create mode 100644 testfile

Perintah:

$ git filter-branch --tree-filter 'git ls-files -z | xargs -0 dos2unix' -- --all

Keluaran:

Rewrite df4970f63e3196216d5986463f239e51eebb4014 (1/1)dos2unix: converting file testfile to Unix format ...

Ref 'refs/heads/master' was rewritten

$ hexdump -C testfile 
00000000  61 0a 62 0a 63 0a                                 |a.b.c.|
00000006

Saya sangat menyarankan untuk melakukan full backup terlebih dahulu . Menjalankan itu dari mesin Linux Anda (kecuali Anda memiliki set up shell yang baik di lingkungan windows Anda) mungkin lebih mudah.

Sunting: konversi telah terbalik pertama kali.

Tikar
sumber
1
Terima kasih, posting ini banyak membantu saya. Aku punya beberapa file dengan spasi di nama mereka, sedikit perubahan untuk perintah asli tetap itu: git filter-branch --tree-filter 'git ls-files -z | xargs -0 dos2unix' -- --all. Bendera -zdan -0memberitahu git ls-filesdan xargsuntuk mencetak dan menafsirkan nullsebagai akhir baris.
Ivan
Alternatif lain untuk perintah dos2unix adalah mengandalkan git itu sendiri:git filter-branch --prune-empty --tree-filter 'git add --renormalize .' -- --all
Vilmantas Baranauskas
6

Jawaban Mat telah mengatasi masalah ini tepat di kepala. Sayangnya di Ubuntu Linux, dimulai dengan versi 10.04 (Lucid Lynx), perintah dos2unix / unix2dos tidak lagi tersedia, dan telah digantikan oleh fromdos / todos. Selain itu, kedua set perintah konversi memiliki berbagai tingkat ketidaktahuan terhadap keberadaan file biner, sehingga jika repositori Anda berisi gambar, font, dll. Mereka akan rusak oleh proses ini.

Saya dapat menemukan solusi untuk masalah korupsi file biner yang menggunakan perintah 'file' Linux untuk mengidentifikasi dan memproses hanya file teks dengan benar seperti yang ditunjukkan di bawah ini. Perintah di bawah ini menggunakan opsi --tag-name-filter untuk mempertahankan tag yang ada dengan memindahkannya ke commit yang baru diubah. Juga menggunakan --force flag untuk memastikan bahwa perintah akan berfungsi jika Anda menjalankan tree-filter pada repositori Anda sebelumnya.

git filter-branch --force --tree-filter 'git ls-files | xargs file | sed -n -e "/.*: .*text.*/s/\(.*\): .*/\1/p" | xargs fromdos' --tag-name-filter cat -- --all
mgorovoy
sumber
3

Dan tanpa alat tambahan apa pun (seperti 'fromdos', 'dos2unix', dll.):

git filter-branch --force --tree-filter 'git ls-files | xargs file | sed -n -e "/.*: .*text.*/s/\(.*\): .*/\1/p" | xargs -0 sed -i"" -e "s/"$(printf "\015")"$//"' --tag-name-filter cat -- --all

Crossplatform (OS X, FreeBSD, Linux) analog yang berguna 'fromdos', 'dos2unix':

sed -i'' -e 's/'"$(printf '\015')"'$//'

Mungkin berguna 'unix2dos':

sed -i '' -e 's|$|'"`printf '\015'`"'|' file.name

Jika Anda benar-benar memastikan apa yang Anda lakukan, Anda dapat menggunakan perintah inline sederhana ini untuk menghapus "/ r" dari semua file di direktori saat ini ".":

find . -type f -exec sed -i'' -e 's/'"$(printf '\015')"'$//' {} \;
METAJIJI
sumber
1
Alih-alih, ubah \ r \ n ke \ n alih-alih menghapus hanya \ r
xdevs23
Saya pikir seddoa yang sesuai dapat diganti dengan yang lebih pendek:sed -n -e "s/\(.*\): .*text.*/\1/p"
dma_k