Tambahkan hanya perubahan non-spasi putih

343

Saya memiliki editor teks saya untuk secara otomatis memangkas trailing whitespace saat menyimpan file, dan saya berkontribusi pada proyek open source yang memiliki masalah parah dengan trailing whitespace.

Setiap kali saya mencoba mengirimkan tambalan, saya harus terlebih dahulu mengabaikan semua perubahan hanya spasi putih, untuk memilih hanya informasi yang relevan. Bukan hanya itu, tetapi ketika saya menjalankan git rebasesaya biasanya mengalami beberapa masalah karena mereka.

Karena itu saya ingin dapat menambahkan hanya indeks perubahan non-spasi putih, dengan cara yang mirip git add -p, tetapi tanpa harus mengambil semua perubahan sendiri.

Adakah yang tahu bagaimana melakukan ini?

EDIT: Saya tidak bisa mengubah cara kerja proyek, dan mereka memutuskan, setelah mendiskusikannya di milis, untuk mengabaikan ini.

Edu Felipe
sumber

Jawaban:

395

@Frew solusi tidak cukup apa yang saya butuhkan, jadi ini adalah alias yang saya buat untuk masalah yang sama persis:

alias.addnw=!sh -c 'git diff -U0 -w --no-color "$@" | git apply --cached --ignore-whitespace --unidiff-zero -'

Atau Anda cukup menjalankan:

git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero -

Memperbarui

Menambahkan opsi -U0, dan --unidiff-zeromasing - masing untuk memecahkan masalah pencocokan konteks, menurut komentar ini .

Pada dasarnya ini menerapkan tambalan yang akan diterapkan addtanpa perubahan spasi. Anda akan melihat bahwa setelah git addnw your/filemasih ada perubahan yang tidak dipentaskan, itu adalah ruang putih yang tersisa.

--No-color tidak diperlukan tetapi karena saya selalu mengatur warna, saya harus menggunakannya. Lagi pula, lebih baik aman daripada menyesal.

Colin Hebert
sumber
7
Ini bekerja dengan baik untuk saya, tetapi saya harus menggunakan git apply --ignore-whitespacejika tambalan tidak akan berlaku untuk alasan yang jelas.
jupp0r
106
Seharusnya ada opsi untuk git add, seperti git add -wyang dilakukan ini.
Jarl
7
Ini memberiku masalah patch does not applydan error while searching for... Ada ide?
DTI-Matt
18
tidak bekerja untuk saya. Ada patch does not applykesalahan.
Jerry Saravia
13
Jika Anda mendapatkan 'patch yang gagal' karena spasi dalam konteks sebagai @bronson poin, perintah revisi ini karya (Ini menghasilkan patch tanpa konteks): git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero. Ini tidak berisiko karena indeksnya sudah mutakhir, jadi ini adalah basis yang andal untuk tambalan.
void.pointer
36

Ini bekerja untuk saya:

Jika Anda ingin menyimpannya, ini berfungsi

git stash && git stash apply && git diff -w > foo.patch && git checkout . && git apply foo.patch && rm foo.patch

Saya tidak suka simpanan, tetapi saya telah menemukan bug di git + cygwin di mana saya kehilangan perubahan, jadi untuk memastikan bahwa barang-barang pergi ke reflog setidaknya saya mengatur yang berikut:

git add . && git commit -am 'tmp' && git reset HEAD^ && git diff -w > foo.patch && git checkout . && git apply foo.patch && rm foo.patch

Pada dasarnya kami membuat diff yang tidak termasuk perubahan ruang, mengembalikan semua perubahan kami, dan kemudian menerapkan diff.

Frew Schmidt
sumber
1
+1. Anda mungkin ingin melakukan git stashalih - alih checkout, untuk membuat cadangan perubahan Anda, setidaknya sampai diuji.
Paŭlo Ebermann
1
Anda akan berakhir dengan banyak simpanan dan pada dasarnya Anda tidak benar-benar perlu melakukan semua ini. Ini bekerja tetapi saya pikir ini agak berantakan
Colin Hebert
3
Saya setuju dengan Colin. Jika skrip berfungsi, maka seharusnya tidak perlu membuat simpanan. Apa yang mungkin baik untuk dipertimbangkan adalah menjalankan simpanan, lalu simpanan pop. Stash muncul dapat dipulihkan jika perlu, tetapi Anda tidak akan berakhir dengan banyak simpanan sebaliknya. Ini juga meninggalkan file tambahan di sekitar
Casebash
Bagaimana dengan melewatkan file biner? Saat mencoba menerapkan cuplikan di atas, saya mendapatkan kesalahan bahwa tambalan tidak dapat diterapkan tanpa garis indeks lengkap! Apa yang mengalahkan saya adalah bahwa saya bahkan tidak menyentuh file / binari ini di tempat pertama!
tver3305
1
Saya pikir pada akhir perintah pertama "git rm foo.patch" seharusnya hanya "rm foo.patch". Kalau tidak, terima kasih.
Jack Casey
33

Buat file tambalan yang hanya berisi perubahan nyata (tidak termasuk baris dengan hanya perubahan spasi putih), lalu bersihkan ruang kerja Anda dan terapkan file tambalan itu:

git diff> backup
git diff -w> perubahan
git reset -
patch keras <perubahan

Tinjau perbedaan yang tersisa, lalu adddancommit seperti biasa.

Setara dengan Mercurial adalah dengan melakukan ini:

hg diff> backup
hg diff -w> perubahan
hg kembalikan --semua
hg impor --tidak ada perubahan komitmen

Steve Pitchers
sumber
Apa itu pertanyaan "terlindungi"? dan Aku juga menjawab. Saya tidak berpikir ini bahkan memenuhi syarat sebagai jawaban saya juga karena tampaknya pertanyaan itu ditarik keluar dari udara tipis ...
jww
4
@jww Inti dari pertanyaan pengirim asli adalah "bagaimana cara menghindari melakukan perubahan pada ruang putih hanya untuk kontrol sumber". OP kebetulan menggunakan Git, tetapi ini juga berlaku untuk setiap sistem kontrol sumber yang pernah saya gunakan. Jawaban ini menunjukkan prosedur yang benar jika seseorang menggunakan Mercurial. Saya bisa membayangkan orang lain mungkin juga berkontribusi solusi untuk orang yang menggunakan Sublesion, dll.
Steve Pitchers
1
@jww dan @ pagid: Saya mengedit jawaban saya untuk secara khusus mengatasi Git, menggunakan pendekatan yang sama dengan solusi saya untuk Mercurial. Dalam pandangan saya, StackOverflow lebih dari sekadar forum Q + A lainnya - ia juga memiliki peran sebagai gudang pengetahuan. Orang selain poster asli mungkin mendapat manfaat dari jawaban yang diberikan, dan keadaan mereka bervariasi. Itu sebabnya saya percaya jawaban yang menyampaikan prinsip umum itu valid, daripada hanya menargetkan satu situasi tertentu.
Steve Pitchers
@Steve - "Saya mengedit jawaban saya untuk secara khusus mengatasi Git ..." - mengapa Anda tidak mengajukan pertanyaan baru dalam konteks lincah, dan kemudian menambahkan jawaban Anda sendiri ke pertanyaan baru ???
jww
8
Ini sebenarnya pendekatan yang paling bersih, paling mudah dipahami, dan paling tidak bisa dipecahkan yang pernah saya lihat.
Kzqai
12

Jawaban terpilih tidak berfungsi dalam semua kasus, karena spasi putih dalam konteks tambalan menurut pengguna di komentar.

Saya merevisi perintah sebagai berikut:

$ git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero

Ini menghasilkan tambalan tanpa konteks. Seharusnya tidak menjadi masalah karena patch itu berumur pendek.

Alias ​​yang sesuai, lagi revisi apa yang sudah disediakan oleh pengguna lain:

addw = !sh -c 'git diff -U0 -w --no-color "$@" | git apply --cached --ignore-whitespace --unidiff-zero' -
void.pointer
sumber
Untuk hanya mengabaikan perubahan indentasi yang harus saya gunakan --ignore-space-changealih-alih -w. git diff -U0 --ignore-space-change --no-color | git apply --cached --unidiff-zero
Andy
Sebuah kata peringatan untuk tidak menggunakan trik konteks yang bagus ini, --ignore-blank-linesAnda akan menemukan potongan yang berbeda ditambal pada offset yang salah jika beberapa 'ruang putih' berubah yang Anda ingin abaikan adalah pemindahan / penambahan baris kosong.
elbeardmorez
12

Tambahkan yang berikut ke Anda .gitconfig:

anw = !git diff -U0 -w --no-color -- \"$@\" | git apply --cached --ignore-whitespace --unidiff-zero "#"

Terima kasih atas jawaban @Colin Herbert untuk inspirasi.

Penjelasan Sintaks

Akhir # harus dikutip sehingga tidak diperlakukan sebagai komentar di dalam .gitconfig, tetapi diteruskan dan diperlakukan sebagai komentar di dalam shell - itu dimasukkan di antara akhir git applydan argumen yang disediakan pengguna yang gitsecara otomatis ditempatkan di akhir dari baris perintah. Argumen ini tidak diinginkan di sini - kami tidak ingin git applymengkonsumsinya, karena itu karakter komentar sebelumnya. Anda mungkin ingin menjalankan perintah ini GIT_TRACE=1 git anwuntuk melihat ini dalam tindakan.

The --sinyal mengakhiri argumen dan memungkinkan untuk kasus bahwa Anda memiliki file bernama -watau sesuatu yang akan terlihat seperti sebuah saklar untuk git diff.

Kutipan ganda melarikan diri $@diperlukan untuk mempertahankan argumen kutipan yang disediakan pengguna. Jika "karakter tidak lolos, itu akan dikonsumsi oleh .gitconfigpengurai dan tidak mencapai shell.

Catatan: .gitconfigalias parsing tidak mengakui tanda kutip tunggal sebagai sesuatu yang istimewa - yang hanya karakter khusus ", \,\n , dan ;(di luar dari "tali -quoted). Inilah sebabnya mengapa "harus selalu diloloskan, bahkan jika itu tampak di dalam string yang dikutip tunggal (yang git benar-benar agnostik tentang).

Ini penting, misalnya. jika Anda memiliki alias yang berguna untuk menjalankanbash perintah di root pohon kerja. Formulasi yang salah adalah:

sh = !bash -c '"$@"' -

Sedangkan yang benar adalah:

sh = !bash -c '\"$@\"' -
Tom Hale
sumber
Luar biasa. Ini memungkinkan saya untuk menambahkan satu file sekaligus. Selain menambahkan direktori root untuk argumen, apakah ada cara untuk membuat ini berfungsi seperti 'git add -A'?
Chucky
7

Bagaimana dengan yang berikut:

git add `git diff -w --ignore-submodules |grep "^[+][+][+]" |cut -c7-`

Perintah di dalam backquotes mendapatkan nama-nama file yang memiliki perubahan non-spasi putih.

karmakaze
sumber
2
atau hanya git add `git diff -w |grep '^+++' |cut -c7-`jika submodula tidak digunakan
karmakaze
-1

Anda harus terlebih dahulu mempertimbangkan apakah spasi trailing disengaja. Banyak proyek, termasuk kernel Linux, Mozilla, Drupal, dan Kerberos (untuk beberapa nama dari halaman Wikipedia tentang gaya) melarang spasi spasi. Dari dokumentasi kernel Linux:

Dapatkan editor yang baik dan jangan tinggalkan spasi di akhir baris.

Dalam kasus Anda, masalahnya adalah sebaliknya: komit sebelumnya (dan mungkin yang sekarang) tidak mengikuti pedoman ini.

Saya berani bertaruh bahwa tidak ada yang benar-benar menginginkan spasi putih, dan memperbaiki masalah mungkin merupakan perubahan yang disambut baik. Pengguna lain mungkin juga mengalami masalah yang sama dengan Anda. Kemungkinan juga kontributor yang menambahkan spasi spasi tidak menyadari bahwa mereka melakukannya.

Daripada mencoba mengkonfigurasi ulang git untuk mengabaikan masalah, atau menonaktifkan fungsionalitas yang diinginkan di editor Anda, saya akan memulai dengan posting ke milis proyek menjelaskan masalah. Banyak editor (dan git itu sendiri) dapat dikonfigurasi untuk menangani spasi spasi tambahan.

Kevin Vermeer
sumber
16
Itu tidak disengaja, tapi saya tidak bisa mengubah cara 100+ orang yang berkontribusi dalam proyek berpikir. Mereka tidak keberatan, dan tidak akan menerima tambalan dengan 1000+ perubahan yang hanya berurusan dengan spasi kosong. Mereka tahu tentang masalahnya dan memutuskan untuk mengabaikannya. Diskusi ini sudah terjadi dalam daftar dan ditutup. Dalam hal ini, akulah yang perlu beradaptasi dengan mereka.
Edu Felipe
19
Kemudian konfigurasikan editor Anda sedemikian rupa sehingga tidak memotong spasi spasi saat mengerjakan kode proyek ini.
jamessan
-2

Saya menemukan hook pre-commit git yang menghilangkan spasi spasi . Namun, jika Anda tidak bisa membuat orang lain menggunakan ini, maka itu mungkin bukan solusi yang valid.

  #!/bin/sh

  if git-rev-parse --verify HEAD >/dev/null 2>&1 ; then
     against=HEAD
  else
     # Initial commit: diff against an empty tree object
     against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
  fi
  # Find files with trailing whitespace
  for FILE in `exec git diff-index --check --cached $against -- | sed '/^[+-]/d' | sed -r 's/:[0-9]+:.*//' | uniq` ; do
     # Fix them!
     sed -i 's/[[:space:]]*$//' "$FILE"
  done
  exit
cmcginty
sumber
4
Pertanyaan ini menanyakan bagaimana cara melestarikan spasi putih.
Douglas
@Douglas: orang mungkin bisa menggunakan jawaban ini untuk membuat komit pada cabang sementara, komit tambalan nyata di sana dan ceri-pilih diff hanya ke dalam cabang kerja entah bagaimana ...
Tobias Kienzler