Repositori Git kami dimulai sebagai bagian dari repositori monster SVN tunggal di mana masing-masing proyek masing-masing memiliki pohon sendiri seperti:
project1/branches
/tags
/trunk
project2/branches
/tags
/trunk
Jelas, cukup mudah untuk memindahkan file dari satu ke yang lain dengan svn mv
. Tetapi di Git, setiap proyek berada dalam repositori sendiri, dan hari ini saya diminta untuk memindahkan subdirektori dari project2
ke project1
. Saya melakukan sesuatu seperti ini:
$ git clone project2
$ cd project2
$ git filter-branch --subdirectory-filter deeply/buried/java/source/directory/A -- --all
$ git remote rm origin # so I don't accidentally overwrite the repo ;-)
$ mkdir -p deeply/buried/different/java/source/directory/B
$ for f in *.java; do
> git mv $f deeply/buried/different/java/source/directory/B
> done
$ git commit -m "moved files to new subdirectory"
$ cd ..
$
$ git clone project1
$ cd project1
$ git remote add p2 ../project2
$ git fetch p2
$ git branch p2 remotes/p2/master
$ git merge p2 # --allow-unrelated-histories for git 2.9+
$ git remote rm p2
$ git push
Tapi itu sepertinya berbelit-belit. Apakah ada cara yang lebih baik untuk melakukan hal semacam ini secara umum? Atau sudahkah saya mengadopsi pendekatan yang benar?
Perhatikan bahwa ini melibatkan menggabungkan sejarah ke dalam repositori yang ada, daripada hanya membuat repositori mandiri baru dari bagian yang lain ( seperti dalam pertanyaan sebelumnya ).
sumber
git fetch p2 && git merge p2
bukangit fetch p2 && git branch .. && git merge p2
? Sunting: baik-baik saja, sepertinya Anda ingin mendapatkan perubahan di cabang baru bernama p2, bukan cabang saat ini.--allow-unrelated-histories
yang terakhirgit merge
untuk membuatnya berfungsi.Jawaban:
Yap, memukul pada
--subdirectory-filter
darifilter-branch
itu kunci. Fakta bahwa Anda menggunakannya pada dasarnya membuktikan bahwa tidak ada cara yang lebih mudah - Anda tidak punya pilihan selain menulis ulang sejarah, karena Anda ingin berakhir hanya dengan subset (berganti nama) dari file, dan ini dengan definisi mengubah hash. Karena tidak ada perintah standar (misalnyapull
) penulisan ulang riwayat, tidak mungkin Anda dapat menggunakannya untuk mencapai ini.Anda bisa memperbaiki detailnya, tentu saja - beberapa kloning dan percabangan Anda tidak sepenuhnya diperlukan - tetapi pendekatan keseluruhannya bagus! Ini memalukan, rumit, tapi tentu saja, tujuan dari git bukanlah untuk membuatnya mudah untuk menulis ulang sejarah.
sumber
--index-filter
di halamanfilter-branch
manual.Jika riwayat Anda waras, Anda bisa mengambil komit sebagai tambalan dan menerapkannya di repositori baru:
Atau dalam satu baris
(Diambil dari dokumen Exherbo )
sumber
git log --pretty=email --patch-with-stat --full-index --binary --reverse -- client > patch
. Bekerja tanpa masalah AFAICT.--committer-date-is-author-date
opsi untuk mempertahankan tanggal komit asli, bukan tanggal file dipindahkan.git log
, sehingga tidak berfungsi dengan baik--follow
dan--reverse
benar). Saya menggunakan jawaban ini , dan di sini adalah naskah lengkap yang saya gunakan sekarang untuk memindahkan fileSetelah mencoba berbagai pendekatan untuk memindahkan file atau folder dari satu repositori Git ke yang lain, satu-satunya yang tampaknya berfungsi dengan baik dijelaskan di bawah ini.
Ini melibatkan kloning repositori yang ingin Anda pindahkan file atau folder, memindahkan file atau folder itu ke root, menulis ulang sejarah Git, mengkloning repositori target dan menarik file atau folder dengan sejarah langsung ke repositori target ini.
Tahap satu
Buat salinan repositori A sebagai langkah-langkah berikut membuat perubahan besar pada salinan ini yang tidak boleh Anda dorong!
masuk ke dalamnya
Hapus tautan ke repositori asli untuk menghindari perubahan jarak jauh yang tidak disengaja (mis. Dengan mendorong)
Telusuri riwayat dan file Anda, hapus semua yang tidak ada di direktori 1. Hasilnya adalah isi dari direktori 1 yang dimuntahkan ke dasar repositori A.
Hanya untuk satu file bergerak: telusuri apa yang tersisa dan hapus semuanya kecuali file yang diinginkan. (Anda mungkin perlu menghapus file yang tidak Anda inginkan dengan nama dan komit yang sama.)
Tahap Dua
Langkah pembersihan
Langkah pembersihan
Langkah pembersihan
Anda mungkin ingin mengimpor file-file ini ke repositori B dalam direktori bukan root:
Buat direktori itu
Pindahkan file ke direktori itu
Tambahkan file ke direktori itu
Komit perubahan Anda dan kami siap untuk menggabungkan file-file ini ke dalam repositori baru
Tahap Tiga
Buat salinan repositori B jika Anda belum memilikinya
(dengan asumsi FOLDER_TO_KEEP adalah nama repositori baru yang Anda salin)
masuk ke dalamnya
Buat koneksi jarak jauh ke repositori A sebagai cabang di repositori B
Tarik dari cabang ini (hanya berisi direktori yang ingin Anda pindahkan) ke dalam repositori B.
Tarikan menyalin file dan riwayat. Catatan: Anda bisa menggunakan gabungan alih-alih tarikan, tetapi tarikan bekerja lebih baik.
Terakhir, Anda mungkin ingin membersihkan sedikit dengan menghapus koneksi jarak jauh ke repositori A
Dorong dan Anda sudah siap.
sumber
Saya menemukan ini sangat berguna. Ini adalah pendekatan yang sangat sederhana di mana Anda membuat tambalan yang diterapkan pada repo baru. Lihat halaman tertaut untuk lebih jelasnya.
Itu hanya berisi tiga langkah (disalin dari blog):
Satu-satunya masalah yang saya miliki adalah bahwa saya tidak bisa menerapkan semua tambalan sekaligus menggunakan
Di Windows saya mendapat kesalahan InvalidArgument. Jadi saya harus menerapkan semua tambalan satu demi satu.
sumber
MENJAGA NAMA DIREKTORI
Subdirektori-filter (atau subtree perintah git pendek) berfungsi dengan baik tetapi tidak berfungsi untuk saya karena mereka menghapus nama direktori dari info komit. Dalam skenario saya, saya hanya ingin menggabungkan bagian dari satu repositori ke yang lain dan menyimpan sejarah DENGAN nama path lengkap.
Solusi saya adalah menggunakan tree-filter dan hanya menghapus file dan direktori yang tidak diinginkan dari klon sementara repositori sumber, kemudian tarik dari klon itu ke repositori target saya dalam 5 langkah sederhana.
sumber
Yang saya selalu gunakan ada di sini http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/ . Sederhana dan cepat.
Untuk kepatuhan dengan standar stackoverflow, berikut adalah prosedurnya:
sumber
Jawaban ini memberikan perintah menarik berdasarkan
git am
dan disajikan menggunakan contoh, langkah demi langkah.Objektif
Prosedur
git log --pretty=email -p --reverse --full-index --binary
git am
1. Ekstrak histori dalam format email
Contoh: Ekstrak riwayat
file3
,file4
danfile5
Bersihkan tujuan direktori sementara
Bersihkan sumber repo Anda
Ekstrak riwayat setiap file dalam format email
Sayangnya opsi
--follow
atau--find-copies-harder
tidak dapat digabungkan dengan--reverse
. Inilah sebabnya mengapa sejarah dipotong ketika file diganti namanya (atau ketika direktori induk diubah namanya).Setelah: Riwayat sementara dalam format email
2. Atur ulang susunan file dan perbarui perubahan nama file dalam riwayat [opsional]
Misalkan Anda ingin memindahkan ketiga file ini di repo lain ini (bisa menjadi repo yang sama).
Karena itu atur ulang file Anda:
Riwayat sementara Anda sekarang:
Ubah juga nama file dalam riwayat:
Catatan: Ini menulis ulang sejarah untuk mencerminkan perubahan path dan nama file.
(yaitu perubahan lokasi / nama baru dalam repo baru)
3. Terapkan sejarah baru
Repo Anda yang lain adalah:
Terapkan komit dari file riwayat sementara:
Repo Anda yang lain sekarang:
Gunakan
git status
untuk melihat jumlah komit yang siap didorong :-)Catatan: Karena riwayat telah ditulis ulang untuk mencerminkan jalur dan perubahan nama file:
(yaitu dibandingkan dengan lokasi / nama dalam repo sebelumnya)
git mv
mengubah lokasi / nama file.git log --follow
mengakses riwayat lengkap.Trik tambahan: Mendeteksi file yang diubah namanya / dipindahkan dalam repo Anda
Untuk membuat daftar file yang telah diubah namanya:
Kustomisasi lainnya: Anda dapat menyelesaikan perintah
git log
menggunakan opsi--find-copies-harder
atau--reverse
. Anda juga dapat menghapus dua kolom pertama menggunakancut -f3-
dan menangkap pola lengkap '{. * =>. *}'.sumber
Setelah memiliki gatal serupa dengan goresan (walaupun hanya untuk beberapa file dari repositori yang diberikan) skrip ini terbukti sangat membantu: git-import
Versi singkatnya adalah ia membuat file tambalan dari file atau direktori yang diberikan (
$object
) dari repositori yang ada:yang kemudian diterapkan ke repositori baru:
Untuk detailnya silakan lihat:
sumber
Coba ini
cd repo1
Ini akan menghapus semua direktori kecuali yang disebutkan, menjaga sejarah hanya untuk direktori ini
Sekarang Anda dapat menambahkan repo baru Anda di remote git Anda dan mendorongnya ke sana
tambahkan
-f
untuk menimpasumber
Menggunakan inspirasi dari http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/ , saya membuat fungsi Powershell ini untuk melakukan hal yang sama, yang memiliki bekerja sangat baik untuk saya sejauh ini:
Penggunaan untuk contoh ini:
Setelah melakukan ini, Anda dapat mengatur ulang file pada
migrate-from-project2
cabang sebelum menggabungkannya.sumber
Saya menginginkan sesuatu yang kuat dan dapat digunakan kembali (fungsi one-command-and-go + undo) jadi saya menulis skrip bash berikut. Bekerja untuk saya pada beberapa kesempatan, jadi saya pikir saya akan membagikannya di sini.
Ia dapat memindahkan folder arbitrer
/path/to/foo
darirepo1
ke/some/other/folder/bar
kerepo2
(path folder bisa sama atau berbeda, jarak dari folder root mungkin berbeda).Karena hanya membahas komit yang menyentuh file dalam folder input (tidak semua komit dari repo sumber), itu harus cukup cepat bahkan pada repo sumber besar, jika Anda hanya mengekstrak subfolder bersarang dalam yang tidak disentuh di setiap melakukan.
Karena yang dilakukan adalah membuat cabang yatim dengan semua sejarah repo lama dan kemudian menggabungkannya ke HEAD, bahkan akan berfungsi jika terjadi bentrokan nama file (maka Anda harus menyelesaikan penggabungan pada akhir saja) .
Jika tidak ada bentrokan nama file, Anda hanya perlu
git commit
di akhir untuk menyelesaikan penggabungan.Kelemahannya adalah bahwa ia kemungkinan tidak akan mengikuti penggantian nama file (di luar
REWRITE_FROM
folder) dalam permintaan repo - pull sumber diterima di GitHub untuk mengakomodasi hal itu.Tautan GitHub: git-move-folder-between-repos-keep-history
sumber
Dalam kasus saya, saya tidak perlu menyimpan repo tempat saya bermigrasi atau menyimpan sejarah sebelumnya. Saya memiliki patch dari cabang yang sama, dari remote yang berbeda
Dalam dua langkah itu, saya bisa membuat cabang repo lainnya muncul di repo yang sama.
Akhirnya, saya mengatur cabang ini (yang saya impor dari repo lain) untuk mengikuti garis utama repo target (sehingga saya bisa membedakannya secara akurat)
Sekarang berperilaku seolah-olah itu hanya cabang lain yang saya dorong melawan repo yang sama.
sumber
Jika jalur untuk file yang dimaksud adalah sama di kedua repo dan Anda ingin membawa hanya satu file atau satu set kecil file terkait, satu cara mudah untuk melakukan ini adalah dengan menggunakan
git cherry-pick
.Langkah pertama adalah membawa komit dari repo lain ke repo lokal Anda sendiri
git fetch <remote-url>
. Ini akan meninggalkanFETCH_HEAD
menunjuk komit kepala dari repo lain; jika Anda ingin menyimpan referensi ke komit itu setelah Anda melakukan pengambilan lain, Anda mungkin ingin memberi tag dengannyagit tag other-head FETCH_HEAD
.Anda kemudian perlu membuat komit awal untuk file itu (jika tidak ada) atau komit untuk membawa file ke keadaan yang dapat ditambal dengan komit pertama dari repo lain yang ingin Anda bawa. Anda dapat dapat melakukan ini dengan
git cherry-pick <commit-0>
jikacommit-0
memperkenalkan file yang Anda inginkan, atau Anda mungkin perlu membangun komit 'dengan tangan'. Tambahkan-n
ke opsi pick-ceri jika Anda perlu memodifikasi komit awal untuk, misalnya, drop file dari komit yang Anda tidak ingin bawaSetelah itu, Anda dapat melanjutkan ke
git cherry-pick
komitmen berikutnya, menggunakan lagi-n
jika perlu. Dalam kasus yang paling sederhana (semua komit adalah apa yang Anda inginkan dan menerapkan bersih) Anda dapat memberikan daftar lengkap komit pada baris perintah ceri-memilih:git cherry-pick <commit-1> <commit-2> <commit-3> ...
.sumber
Ini menjadi lebih sederhana dengan menggunakan git-filter-repo.
Untuk pindah
project2/sub/dir
keproject1/sub/dir
:Untuk menginstal alat ini cukup:
pip3 install git-filter-repo
( lebih detail dan opsi di README )sumber
Metode di bawah ini untuk memigrasi GIT Stash saya ke GitLab dengan mempertahankan semua cabang dan melestarikan sejarah.
Kloning repositori lama ke lokal.
Buat repositori kosong di GitLab.
Di atas saya lakukan ketika kami memigrasi kode kami dari simpanan ke GitLab dan itu bekerja dengan sangat baik.
sumber