Bagaimana cara menerapkan patch Git ke file dengan nama dan jalur berbeda?

101

Saya memiliki dua repositori. Dalam satu, saya membuat perubahan ke file ./hello.test. Saya melakukan perubahan dan membuat tambalan dari komit itu dengan git format-patch -1 HEAD. Sekarang, saya memiliki repositori kedua yang berisi file yang memiliki isi yang sama seperti hello.test tapi ditempatkan dalam direktori yang berbeda dengan nama yang berbeda: ./blue/red/hi.test. Bagaimana cara saya menerapkan tambalan yang disebutkan di atas ke hi.testfile? Saya mencoba git am --directory='blue/red' < patch_filetetapi tentu saja mengeluh bahwa file tersebut tidak bernama sama (yang menurut saya Git tidak peduli?). Saya tahu saya mungkin bisa mengedit diff untuk diterapkan ke file tertentu itu tetapi saya sedang mencari solusi perintah.

mart1n
sumber
Terkait dengan: stackoverflow.com/q/3367254/1959808
Ioannis Filippidis

Jawaban:

97

Anda dapat membuat tambalan menggunakan git diffdan kemudian menerapkannya menggunakan patchutilitas, yang memungkinkan Anda menentukan file yang ingin Anda terapkan diff.

Sebagai contoh:

cd first-repo
git diff HEAD^ -- hello.test > ~/patch_file

cd ../second-repo
patch -p1 blue/red/hi.test ~/patch_file
georgebrock.dll
sumber
7
Ah, bagus, tidak memikirkan itu. Namun, adakah cara untuk melakukan ini dengan perintah Git sehingga data komit (tanggal & waktu, pembuat komit, pesan komit) tetap sama?
mart1n
2
Mungkin ada sesuatu yang dapat Anda lakukan dengan amatau apply, tetapi saya tidak dapat menemukannya. Jika Anda sering menduplikasi perubahan, mungkin ada solusi yang lebih baik menggunakan submodul, atau apa pun bahasa pilihan Anda yang disediakan untuk berbagi kode (misalnya di Ruby Anda dapat mengekstrak kode duplikat sebagai permata).
georgebrock
1
Ini sebenarnya terkait dengan dokumentasi (file sumber adalah XML). Submodul sebenarnya bukan pilihan karena saya harus membuat alasan yang kuat untuk mereka di infrastruktur kami yang ada.
mart1n
52

Ada solusi sederhana yang tidak melibatkan pengeditan tambalan manual atau skrip eksternal.

Di repositori pertama (ini juga dapat mengekspor berbagai komit, gunakan -1jika Anda ingin memilih hanya satu komit):

git format-patch --relative <committish> --stdout > ~/patch

Di repositori kedua:

git am --directory blue/red/ ~/patch

Alih-alih menggunakan --relativein git format-patch, solusi lain adalah menggunakan -p<n>opsi in git amuntuk menghapus ndirektori dari jalur tambalan, seperti yang disebutkan dalam jawaban untuk pertanyaan serupa .

Dimungkinkan juga untuk dijalankan git format-patch --relative <committish>tanpa --stdout, dan itu akan menghasilkan sekumpulan .patchfile. File-file ini kemudian dapat diumpankan langsung ke git amwith git am --directory blue/red/ path/to/*.patch.

magiraud
sumber
9
Ini masih bergantung pada fakta bahwa nama filenya sama, bukan?
mart1n
3
Perlu dicatat bahwa --directoryopsi tersebut tampaknya meminta Anda untuk menentukan path lengkap dari direktori relatif terhadap root repo; sesuatu seperti --directory=./sementara chdir ke subdirektori di repo tidak akan berfungsi.
Reid
1
Menggunakan --3waybantuan dengan does not exist in index:git am --3way --directory (relative-path) (patch)
Brent Bradburn
Gunakan -kkunci di kedua perintah untuk tidak menghapus baris pertama dari pesan komit.
ruvim
Menggunakan --3waytidak hanya membantu dengan kesalahan "tidak ada dalam indeks" (seperti yang ditunjukkan oleh @nobar), tetapi juga memungkinkan Anda menangani konflik penggabungan dengan rapi. Alih-alih membiarkan file konflik tidak tersentuh, blok konflik ditambahkan yang kemudian dapat diselesaikan.
Daniel Wolf
11

Menjawab pertanyaan saya sendiri dengan skrip yang hanya melakukan ini: https://github.com/mprpic/apply-patch-to-file

Daripada memodifikasi file patch secara manual, ia meminta pengguna untuk file target, memodifikasi patch, dan menerapkannya ke repo tempat Anda saat ini berada.

mart1n
sumber
5

Dibangun berdasarkan jawaban oleh @georgebrock, inilah solusi yang saya gunakan:

Pertama, buat file patch seperti biasa (mis. git format-patch commitA..commitB).

Kemudian pastikan bahwa repositori target Anda bersih (seharusnya tidak ada file yang diubah atau tidak terlacak) dan terapkan patch seperti ini:

cd second-repo
git am ~/00*.patch

Untuk setiap file patch Anda akan mendapatkan error seperti "error: XYZ tidak ada di indeks". Anda sekarang dapat menerapkan file tambalan ini secara manual:

patch --directory blue/red < ~/0001-*.patch
git add -a
git am --continue

Anda harus melakukan tiga langkah ini untuk setiap file patch.

Ini akan mempertahankan pesan komit asli dll. Tanpa memerlukan git format-patchperintah khusus atau mengedit file tambalan.

Oliver
sumber
1
Jawaban yang bagus, saya pikir ini adalah fondasi terbaik untuk segala jenis manipulasi tambalan "non-standar". Saya melakukannya dalam 3 langkah. (1) Berkomitmen pada teks - git format-patch -1 commitA --stdout > thing.diff; (2) Edit file patch hingga melakukan apa yang saya butuhkan; (3) Teks untuk komit git am --3way thing.diff yang memiliki keuntungan bahwa Anda dapat menerima bagian dari tambalan yang diterapkan dengan bersih, dan menggunakan gitproses resolusi konflik standar untuk bagian yang tidak.
We Are All Monica
2

Saya memahami bahwa kedua file tersebut persis sama dalam situasi Anda, sehingga patch kemungkinan besar akan berhasil.

Namun, jika Anda ingin menerapkan tambalan ke file yang serupa, tetapi tidak persis sama, atau Anda ingin melakukan tambalan interaktif, Anda akan menggunakan penggabungan tiga cara.

Katakanlah Anda memodifikasi File A, mari tunjukkan A~1sebagai versi sebelumnya, dan Anda ingin menerapkan perbedaan antara A~1ke Ake File B.

Buka alat penggabung tiga arah, misalnya Beyond Compare, jalur panel kiri adalah A, panel tengah adalah leluhur yang sama sehingga jalurnya adalah A~1, jalur panel kanan adalah B. Kemudian, lebih rendah menunjukkan panel hasil dari penerapan diff antara A~1untuk Ake file B.

Gambar berikut mengilustrasikan ide tersebut.

masukkan deskripsi gambar di sini

Gqqnbig.dll
sumber
0

FYI: Baru-baru ini saya mengalami masalah saat mencoba mendownload patch dari Github dan menerapkannya ke file lokal (yang merupakan "override" di lokasi baru).

git amtidak akan menerapkan tambalan karena file itu "tidak ada dalam indeks" atau "kotor". Tapi, saya menemukan bahwa patchperintah sederhana dapat menerapkan tambalan. Itu meminta saya untuk nama file yang akan ditambal.

Tetap menyelesaikan pekerjaan ...

Mike Robinson
sumber