Dalam kasus saya, saya memiliki my-plugin
repositori dan main-project
repositori, dan saya ingin berpura-pura yang my-plugin
selalu dikembangkan di plugins
subdirektori darimain-project
.
Pada dasarnya, saya menulis ulang sejarah my-plugin
repositori sehingga tampaknya semua pengembangan terjadi di plugins/my-plugin
subdirektori. Kemudian, saya menambahkan sejarah perkembangan my-plugin
ke dalam main-project
sejarah, dan menggabungkan kedua pohon itu bersama-sama. Karena tidak ada plugins/my-plugin
direktori yang ada dimain-project
repositori, ini adalah penggabungan tanpa konflik sepele. Repositori yang dihasilkan berisi semua sejarah dari kedua proyek asli, dan memiliki dua akar.
TL; DR
$ cp -R my-plugin my-plugin-dirty
$ cd my-plugin-dirty
$ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|plugins) plugins/my-plugin || true)'" -- --all
$ cd ../main-project
$ git checkout master
$ git remote add --fetch my-plugin ../my-plugin-dirty
$ git merge my-plugin/master --allow-unrelated-histories
$ cd ..
$ rm -rf my-plugin-dirty
Versi panjang
Pertama, buat salinan my-plugin
repositori, karena kita akan menulis ulang sejarah repositori ini.
Sekarang, navigasikan ke root my-plugin
repositori, periksa cabang utama Anda (mungkin master
), dan jalankan perintah berikut. Tentu saja, Anda harus mengganti my-plugin
dan plugins
apa pun nama Anda yang sebenarnya.
$ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|plugins) plugins/my-plugin || true)'" -- --all
Sekarang untuk penjelasan. git filter-branch --tree-filter (...) HEAD
menjalankan (...)
perintah di setiap komit yang dapat dijangkau HEAD
. Perhatikan bahwa ini beroperasi langsung pada data yang disimpan untuk setiap komit, jadi kami tidak perlu khawatir tentang gagasan "direktori kerja", "indeks", "staging", dan sebagainya.
Jika Anda menjalankan filter-branch
perintah yang gagal, itu akan meninggalkan beberapa file di .git
direktori dan pada saat Anda mencobanya filter-branch
akan mengeluh tentang ini, kecuali jika Anda memberikan -f
opsi untukfilter-branch
.
Adapun perintah yang sebenarnya, saya tidak punya banyak keberuntungan bash
untuk melakukan apa yang saya inginkan, jadi alih-alih saya gunakan zsh -c
untuk membuat zsh
menjalankan perintah. Pertama saya mengatur extended_glob
opsi, yang memungkinkan ^(...)
sintaks dalam mv
perintah, serta glob_dots
opsi, yang memungkinkan saya untuk memilih dotfile (seperti .gitignore
) dengan glob ( ^(...)
).
Berikutnya, saya menggunakan mkdir -p
perintah untuk membuat kedua plugins
dan plugins/my-plugin
pada saat yang sama.
Akhirnya, saya menggunakan fitur zsh
"gumpalan negatif" ^(.git|plugins)
untuk mencocokkan semua file di direktori root repositori kecuali untuk .git
dan my-plugin
folder yang baru dibuat . (Mengecualikan .git
mungkin tidak diperlukan di sini, tetapi mencoba memindahkan direktori ke dalam dirinya sendiri adalah kesalahan.)
Dalam repositori saya, komit awal tidak termasuk file apa pun, jadi mv
perintah mengembalikan kesalahan pada komit awal (karena tidak ada yang tersedia untuk dipindahkan). Karena itu, saya menambahkan || true
agargit filter-branch
tidak dibatalkan.
The --all
pilihan memberitahu filter-branch
untuk menulis ulang sejarah untuk semua cabang di repositori, dan ekstra --
diperlukan untuk memberitahu git
untuk menafsirkannya sebagai bagian dari daftar pilihan untuk cabang untuk menulis ulang, bukan sebagai pilihan untukfilter-branch
dirinya sendiri.
Sekarang, navigasikan ke main-project
repositori Anda dan periksa cabang apa pun yang ingin Anda gabungkan. Tambahkan salinan lokal my-plugin
repositori Anda (dengan riwayatnya diubah) sebagai remote dari main-project
dengan:
$ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORY
Anda sekarang akan memiliki dua pohon yang tidak terkait dalam riwayat komit Anda, yang dapat Anda visualisasikan dengan baik menggunakan:
$ git log --color --graph --decorate --all
Untuk menggabungkannya, gunakan:
$ git merge my-plugin/master --allow-unrelated-histories
Perhatikan bahwa pada Pra-2.9.0 Git, --allow-unrelated-histories
opsi tidak ada. Jika Anda menggunakan salah satu versi ini, cukup hapus opsi: pesan kesalahan yang --allow-unrelated-histories
mencegah juga ditambahkan di 2.9.0.
Anda seharusnya tidak memiliki konflik gabungan. Jika Anda melakukannya, itu mungkin berarti bahwa salah satu filter-branch
perintah tidak berfungsi dengan benar atau sudah ada plugins/my-plugin
direktori di main-project
.
Pastikan untuk memasukkan pesan komit penjelas untuk kontributor masa depan yang bertanya-tanya apa peretasan yang terjadi untuk membuat repositori dengan dua root.
Anda dapat memvisualisasikan grafik komit baru, yang seharusnya memiliki dua komit root, menggunakan git log
perintah di atas . Perhatikan bahwa hanya master
cabang yang akan digabung . Ini berarti bahwa jika Anda memiliki pekerjaan penting pada my-plugin
cabang lain yang ingin Anda gabungkan ke dalam main-project
pohon, Anda harus menahan diri dari menghapus my-plugin
remote sampai Anda telah melakukan penggabungan ini. Jika tidak, maka komit dari cabang-cabang itu akan tetap berada di main-project
repositori, tetapi beberapa tidak akan terjangkau dan rentan terhadap pengumpulan sampah akhirnya. (Selain itu, Anda harus merujuknya oleh SHA, karena menghapus remote menghapus cabang-cabang pelacakan jarak jauhnya.)
Secara opsional, setelah Anda menggabungkan semua yang ingin Anda simpan my-plugin
, Anda dapat menghapus my-plugin
remote menggunakan:
$ git remote remove my-plugin
Anda sekarang dapat dengan aman menghapus salinan my-plugin
repositori yang riwayatnya Anda ubah. Dalam kasus saya, saya juga menambahkan pemberitahuan penghentian ke my-plugin
repositori nyata setelah penggabungan selesai dan didorong.
Diuji pada Mac OS X El Capitan dengan git --version 2.9.0
dan zsh --version 5.2
. Jarak tempuh Anda mungkin beragam.
Referensi: