Pertimbangkan skenario berikut:
Saya telah mengembangkan proyek eksperimental kecil A di repo Git sendiri. Sekarang telah matang, dan saya ingin A menjadi bagian dari proyek B yang lebih besar, yang memiliki repositori besar sendiri. Sekarang saya ingin menambahkan A sebagai subdirektori dari B.
Bagaimana cara menggabungkan A menjadi B, tanpa kehilangan riwayat di pihak mana pun?
git
merge
repository
git-subtree
static_rtti
sumber
sumber
Jawaban:
Cabang tunggal dari repositori lain dapat dengan mudah ditempatkan di bawah subdirektori yang menyimpan sejarahnya. Sebagai contoh:
Ini akan muncul sebagai komit tunggal di mana semua file cabang master Rails ditambahkan ke direktori "rails". Namun judul komit berisi referensi ke pohon sejarah lama:
Di mana
<rev>
hash komit SHA-1. Anda masih bisa melihat sejarah, menyalahkan beberapa perubahan.Perhatikan bahwa Anda tidak dapat melihat awalan direktori dari sini karena ini adalah cabang lama aktual yang tersisa. Anda harus memperlakukan ini seperti komit pemindahan file: Anda perlu lompatan ekstra saat mencapainya.
Ada solusi yang lebih kompleks seperti melakukan ini secara manual atau menulis ulang sejarah seperti yang dijelaskan dalam jawaban lain.
Perintah git-subtree adalah bagian dari git-contrib resmi, beberapa manajer paket menginstalnya secara default (OS X Homebrew). Tetapi Anda mungkin harus menginstalnya sendiri selain git.
sumber
git co v1.7.11.3
dengan... v1.8.3
).git log rails/somefile
tidak akan menampilkan file yang melakukan riwayat kecuali komit gabungan. Seperti yang disarankan @artfulrobot, periksa jawaban Greg Hewgill . Dan Anda mungkin perlu menggunakangit filter-branch
repo yang ingin Anda sertakan.git subtree
mungkin tidak melakukan apa yang Anda pikirkan! Lihat di sini untuk solusi yang lebih lengkap.Jika Anda ingin bergabung
project-a
keproject-b
:Diambil dari: git menggabungkan repositori yang berbeda?
Metode ini bekerja cukup baik untuk saya, lebih pendek dan menurut saya lebih bersih.
Jika Anda ingin dimasukkan
project-a
ke dalam subdirektori, Anda dapat menggunakangit-filter-repo
(filter-branch
tidak disarankan ). Jalankan perintah berikut sebelum perintah di atas:Contoh menggabungkan 2 repositori besar, menempatkan salah satunya ke dalam subdirektori: https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731
Catatan: The
--allow-unrelated-histories
parameter hanya ada sejak git> = 2,9. Lihat Git - git merge Documentation / --allow-unrelated-historiesPembaruan : Ditambahkan
--tags
seperti yang disarankan oleh @jstadler untuk menjaga tag.sumber
git mv source-dir/ dest/new-source-dir
git merge
Langkah gagal di sini denganfatal: refusing to merge unrelated histories
;--allow-unrelated-histories
perbaikan itu seperti yang dijelaskan dalam dokumen .--allow-unrelated-histories
diperkenalkan di git 2.9 . Dalam versi sebelumnya itu adalah perilaku default.git fetch /path/to/project-a master; git merge --allow-unrelated-histories FETCH_HEAD
.Berikut adalah dua solusi yang mungkin:
Submodules
Salin repositori A ke direktori terpisah di proyek B yang lebih besar, atau (mungkin lebih baik) klon repositori A ke subdirektori dalam proyek B. Lalu gunakan git submodule untuk menjadikan repositori ini sebagai submodule dari repositori B.
Ini adalah solusi yang bagus untuk repositori yang digabungkan secara longgar, di mana pengembangan dalam repositori A berlanjut, dan bagian utama dari pengembangan adalah pengembangan terpisah yang terpisah di A. Lihat juga SubmoduleSupport dan GitSubmoduleTutorial page di Git Wiki.
Subtree menggabungkan
Anda bisa menggabungkan repositori A ke subdirektori proyek B menggunakan strategi gabungan subtree . Ini dijelaskan dalam Penggabungan Subtree dan Anda oleh Markus Prinz.
(Opsi
--allow-unrelated-histories
diperlukan untuk Git> = 2.9.0.)Atau Anda dapat menggunakan alat git subtree ( repositori di GitHub ) oleh apenwarr (Avery Pennarun), diumumkan misalnya dalam posting blognya. Sebuah alternatif baru untuk submitran Git: git subtree .
Saya pikir dalam kasus Anda (A adalah menjadi bagian dari proyek yang lebih besar B) solusi yang tepat adalah dengan menggunakan penggabungan subtree .
sumber
git log dir-B/somefile
tidak akan menampilkan apa pun kecuali yang digabung. Lihat jawaban Greg Hewgill merujuk masalah penting ini.Pendekatan submodule baik jika Anda ingin mempertahankan proyek secara terpisah. Namun, jika Anda benar-benar ingin menggabungkan kedua proyek ke dalam repositori yang sama, maka Anda memiliki sedikit pekerjaan yang harus dilakukan.
Hal pertama yang akan digunakan
git filter-branch
untuk menulis ulang nama-nama segala sesuatu di repositori kedua menjadi di subdirektori di mana Anda ingin mereka berakhir. Jadi, bukannyafoo.c
,bar.html
, Anda akan memilikiprojb/foo.c
danprojb/bar.html
.Maka, Anda harus dapat melakukan sesuatu seperti berikut:
The
git pull
akan melakukangit fetch
diikuti olehgit merge
. Seharusnya tidak ada konflik, jika repositori yang Anda tarik belum memilikiprojb/
direktori.Pencarian lebih lanjut menunjukkan bahwa sesuatu yang mirip dilakukan untuk penggabungan
gitk
ke dalamgit
. Junio C Hamano menulisnya di sini: http://www.mail-archive.com/[email protected]/msg03395.htmlsumber
git filter-branch
untuk mencapai ini. Di halaman manual tertulis tentang sebaliknya: menjadikan subdir / menjadi root, tetapi bukan sebaliknya.git-subtree
itu bagus, tetapi mungkin itu bukan yang Anda inginkan.Misalnya, jika
projectA
direktori dibuat dalam B, setelahgit subtree
,daftar hanya satu komit: gabungan. Komit dari proyek gabungan adalah untuk jalur yang berbeda, sehingga tidak muncul.
Jawaban Greg Hewgill datang paling dekat, meskipun tidak benar-benar mengatakan bagaimana menulis ulang jalan.
Solusinya sangat sederhana.
(1) Dalam A,
Catatan: Riwayat penulisan ulang ini, jadi jika Anda ingin terus menggunakan repo A ini, Anda mungkin ingin mengkloning (menyalin) salinan yang dibuang terlebih dahulu.
Catatan Bene: Anda harus memodifikasi skrip pengganti di dalam perintah sed jika Anda menggunakan karakter non-ascii (atau karakter putih) dalam nama file atau path. Dalam hal ini lokasi file di dalam catatan yang diproduksi oleh "ls-file -s" dimulai dengan tanda kutip.
(2) Lalu di B, jalankan
Voila! Anda memiliki
projectA
direktori di B. Jika dijalankangit log projectA
, Anda akan melihat semua komit dari A.Dalam kasus saya, saya ingin dua subdirektori,
projectA
danprojectB
. Dalam hal itu, saya melakukan langkah (1) ke B juga.sumber
"$GIT_INDEX_FILE"
harus dikutip (dua kali), jika tidak, metode Anda akan gagal jika jalur berisi spasi.Ctrl-V <tab>
Jika kedua repositori memiliki jenis file yang sama (seperti dua repositori Rails untuk proyek yang berbeda), Anda dapat mengambil data dari repositori sekunder ke repositori Anda saat ini:
dan kemudian menggabungkannya ke repositori saat ini:
Jika versi Git Anda lebih kecil dari 2,9, hapus
--allow-unrelated-histories
.Setelah ini, konflik dapat terjadi. Anda dapat menyelesaikannya misalnya dengan
git mergetool
.kdiff3
dapat digunakan hanya dengan keyboard, jadi 5 file konflik diperlukan saat membaca kode hanya beberapa menit.Ingatlah untuk menyelesaikan penggabungan:
sumber
Saya terus kehilangan sejarah ketika menggunakan penggabungan, jadi saya akhirnya menggunakan rebase karena dalam kasus saya kedua repositori cukup berbeda untuk tidak berakhir pada penggabungan di setiap komit:
=> menyelesaikan konflik, lalu melanjutkan, sebanyak yang diperlukan ...
Melakukan ini mengarah ke satu proyek yang memiliki semua komitmen dari projA diikuti oleh komitmen dari projB
sumber
Dalam kasus saya, saya memiliki
my-plugin
repositori danmain-project
repositori, dan saya ingin berpura-pura yangmy-plugin
selalu dikembangkan diplugins
subdirektori darimain-project
.Pada dasarnya, saya menulis ulang sejarah
my-plugin
repositori sehingga tampaknya semua pengembangan terjadi diplugins/my-plugin
subdirektori. Kemudian, saya menambahkan sejarah perkembanganmy-plugin
ke dalammain-project
sejarah, dan menggabungkan kedua pohon itu bersama-sama. Karena tidak adaplugins/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
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 (mungkinmaster
), dan jalankan perintah berikut. Tentu saja, Anda harus menggantimy-plugin
danplugins
apa pun nama Anda yang sebenarnya.Sekarang untuk penjelasan.
git filter-branch --tree-filter (...) HEAD
menjalankan(...)
perintah di setiap komit yang dapat dijangkauHEAD
. 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 mencobanyafilter-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 gunakanzsh -c
untuk membuatzsh
menjalankan perintah. Pertama saya mengaturextended_glob
opsi, yang memungkinkan^(...)
sintaks dalammv
perintah, sertaglob_dots
opsi, yang memungkinkan saya untuk memilih dotfile (seperti.gitignore
) dengan glob (^(...)
).Berikutnya, saya menggunakan
mkdir -p
perintah untuk membuat keduaplugins
danplugins/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
danmy-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 memberitahufilter-branch
untuk menulis ulang sejarah untuk semua cabang di repositori, dan ekstra--
diperlukan untuk memberitahugit
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 lokalmy-plugin
repositori Anda (dengan riwayatnya diubah) sebagai remote darimain-project
dengan:Anda sekarang akan memiliki dua pohon yang tidak terkait dalam riwayat komit Anda, yang dapat Anda visualisasikan dengan baik menggunakan:
Untuk menggabungkannya, gunakan:
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 adaplugins/my-plugin
direktori dimain-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 hanyamaster
cabang yang akan digabung . Ini berarti bahwa jika Anda memiliki pekerjaan penting padamy-plugin
cabang lain yang ingin Anda gabungkan ke dalammain-project
pohon, Anda harus menahan diri dari menghapusmy-plugin
remote sampai Anda telah melakukan penggabungan ini. Jika tidak, maka komit dari cabang-cabang itu akan tetap berada dimain-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 menghapusmy-plugin
remote menggunakan:Anda sekarang dapat dengan aman menghapus salinan
my-plugin
repositori yang riwayatnya Anda ubah. Dalam kasus saya, saya juga menambahkan pemberitahuan penghentian kemy-plugin
repositori nyata setelah penggabungan selesai dan didorong.Diuji pada Mac OS X El Capitan dengan
git --version 2.9.0
danzsh --version 5.2
. Jarak tempuh Anda mungkin beragam.Referensi:
sumber
--allow-unrelated-histories
datangnya?man git-merge
. Secara default, perintah git merge menolak untuk menggabungkan sejarah yang tidak memiliki nenek moyang yang sama. Opsi ini dapat digunakan untuk mengesampingkan keamanan ini saat menggabungkan sejarah dua proyek yang memulai kehidupan mereka secara mandiri. Karena ini adalah kejadian yang sangat jarang, tidak ada variabel konfigurasi untuk mengaktifkannya secara default dan tidak akan ditambahkan.git version 2.7.2.windows.1
?Saya sudah mencoba melakukan hal yang sama selama berhari-hari, saya menggunakan git 2.7.2. Subtree tidak melestarikan sejarah.
Anda dapat menggunakan metode ini jika Anda tidak akan menggunakan proyek yang lama lagi.
Saya menyarankan agar Anda cabang B dulu dan bekerja di cabang.
Berikut langkah-langkahnya tanpa bercabang:
Jika sekarang Anda mencatat salah satu file di subdir A Anda akan mendapatkan riwayat lengkap
Ini adalah pos yang membantu saya melakukan ini:
http://saintgimp.org/2013/01/22/merging-two-git-repository-into-one-repository-without-losing-file-history/
sumber
Jika Anda ingin meletakkan file dari cabang di repo B di subtree dari repo A dan juga melestarikan sejarah, teruslah membaca. (Dalam contoh di bawah ini, saya berasumsi bahwa kita ingin cabang master repo B bergabung menjadi cabang master repo A.)
Di repo A, pertama-tama lakukan hal berikut untuk membuat repo B tersedia:
Sekarang kami membuat cabang baru (dengan hanya satu komit) di repo A yang kami sebut
new_b_root
. Komit yang dihasilkan akan memiliki file yang dikomit di komit pertama dari cabang master repo B tetapi dimasukkan ke dalam subdirektori bernamapath/to/b-files/
.Penjelasan:
--orphan
Opsi untuk perintah checkout memeriksa file dari cabang master A tetapi tidak membuat komit. Kami dapat memilih komit karena selanjutnya kami akan menghapus semua file. Kemudian, tanpa melakukan (-n
), kami memilih-ceri komit pertama dari cabang utama B. (Pilihan ceri mempertahankan pesan komit asli yang tampaknya tidak dilakukan checkout langsung.) Kemudian kita membuat subtree tempat kita ingin meletakkan semua file dari repo B. Kita kemudian harus memindahkan semua file yang diperkenalkan di cherry-pick ke subtree. Pada contoh di atas, hanya ada satuREADME
file untuk dipindahkan. Kemudian kami melakukan komit B-repo kami, dan, pada saat yang sama, kami juga menjaga stempel waktu dari komit asli.Sekarang, kita akan membuat
B/master
cabang baru di atas yang baru dibuatnew_b_root
. Kami memanggil cabang barub
:Sekarang, kami menggabungkan
b
cabang kami menjadiA/master
:Akhirnya, Anda dapat menghapus cabang
B
jarak jauh dan sementara:Grafik akhir akan memiliki struktur seperti ini:
sumber
Saya telah mengumpulkan banyak informasi di sini di Stack OverFlow, dll, dan telah berhasil menyatukan skrip yang memecahkan masalah bagi saya.
Peringatannya adalah ia hanya memperhitungkan cabang 'berkembang' dari masing-masing repositori dan menggabungkannya ke direktori terpisah dalam repositori yang sama sekali baru.
Tag dan cabang lainnya diabaikan - ini mungkin bukan yang Anda inginkan.
Script bahkan menangani cabang fitur dan tag - menamai mereka dalam proyek baru sehingga Anda tahu dari mana mereka berasal.
Anda juga bisa mendapatkannya dari http://paste.ubuntu.com/11732805
Pertama buat file dengan URL ke setiap repositori, misalnya:
Kemudian panggil skrip yang memberi nama proyek dan jalur ke skrip:
Script itu sendiri memiliki banyak komentar yang harus menjelaskan apa fungsinya.
sumber
Saya tahu itu sudah lama setelah kenyataan, tetapi saya tidak senang dengan jawaban lain yang saya temukan di sini, jadi saya menulis ini:
sumber
if [[ $dirname =~ ^.*\.git$ ]]; then
Jika Anda mencoba merekatkan dua repositori bersama-sama, submodul dan penggabungan subtree adalah alat yang salah untuk digunakan karena mereka tidak mempertahankan semua riwayat file (seperti yang telah dicatat orang pada jawaban lain). Lihat jawaban ini di sini untuk cara sederhana dan benar untuk melakukan ini.
sumber
Saya memiliki tantangan yang sama, tetapi dalam kasus saya, kami telah mengembangkan satu versi basis kode di repo A, lalu mengkloningnya menjadi repo baru, repo B, untuk versi baru produk. Setelah memperbaiki beberapa bug di repo A, kami perlu melakukan FI perubahan menjadi repo B. Akhirnya melakukan hal berikut:
Berhasil memperlakukan :)
sumber
Mirip dengan @Smar tetapi menggunakan jalur sistem file, ditetapkan dalam PRIMARY dan SECONDARY:
Kemudian Anda secara manual bergabung.
(diadaptasi dari pos oleh Anar Manafov )
sumber
Menggabungkan 2 repo
sumber
Saat Anda ingin menggabungkan tiga proyek atau lebih dalam satu komit, lakukan langkah-langkah seperti yang dijelaskan dalam jawaban lain (
remote add -f
,merge
). Kemudian, (lunak) setel ulang indeks ke head lama (di mana tidak ada penggabungan yang terjadi). Tambahkan semua file (git add -A
) dan komit (pesan "Menggabungkan proyek A, B, C, dan D menjadi satu proyek). Sekarang ini adalah kom-id master.Sekarang, buat
.git/info/grafts
dengan konten berikut:Lari
git filter-branch -- head^..head head^2..head head^3..head
. Jika Anda memiliki lebih dari tiga cabang, tambahkan saja sebanyak yanghead^n..head
Anda miliki cabang. Untuk memperbarui tag, tambahkan--tag-name-filter cat
. Jangan selalu menambahkan itu, karena ini dapat menyebabkan penulisan ulang beberapa commit. Untuk detailnya lihat halaman manual cabang-filter , cari "cangkokan".Sekarang, komit terakhir Anda memiliki orang tua yang tepat terkait.
sumber
Untuk menggabungkan A dalam B:
1) Dalam proyek A
2) Dalam proyek B
Di cabang ini lakukan semua operasi yang perlu Anda lakukan dan komit.
C) Kemudian kembali ke master dan penggabungan klasik antara dua cabang:
sumber
Fungsi ini akan mengkloning repo jarak jauh ke dir repo lokal, setelah menggabungkan semua komit akan disimpan,
git log
akan ditampilkan komit asli dan jalur yang benar:Cara Penggunaan:
Jika membuat sedikit perubahan, Anda bahkan dapat memindahkan file / dir repo yang digabungkan ke jalur yang berbeda, misalnya:
Jalur Pemberitahuan menggantikan via
sed
, jadi pastikan jalur tersebut dipindahkan di jalur yang benar setelah penggabungan.The
--allow-unrelated-histories
parameter hanya ada sejak git> = 2,9.sumber
Perintah yang diberikan adalah solusi terbaik yang saya sarankan.
sumber
Saya menggabungkan proyek sedikit secara manual, yang memungkinkan saya untuk menghindari perlunya menangani konflik penggabungan.
pertama, salin file-file dari proyek lain sesuai keinginan Anda.
tarikan berikutnya dalam sejarah
suruh git untuk bergabung dalam sejarah benda yang terakhir diambil
sekarang komit namun Anda biasanya komit
sumber
Saya ingin memindahkan proyek kecil ke subdirektori yang lebih besar. Karena proyek kecil saya tidak memiliki banyak komitmen, saya menggunakan
git format-patch --output-directory /path/to/patch-dir
. Kemudian pada proyek yang lebih besar, saya menggunakangit am --directory=dir/in/project /path/to/patch-dir/*
.Ini terasa jauh lebih menakutkan dan jauh lebih bersih daripada cabang-filter. Memang, itu mungkin tidak berlaku untuk semua kasus.
sumber