Pertanyaan Bagaimana Anda menggabungkan dua repositori Git?


Pertimbangkan skenario berikut:

Saya telah mengembangkan proyek percobaan kecil A di repositori Git sendiri. Sekarang sudah matang, dan saya ingin A menjadi bagian dari proyek B yang lebih besar, yang memiliki repositori besar sendiri. Saya sekarang ingin menambahkan A sebagai subdirektori B.

Bagaimana cara menggabungkan A ke B, tanpa kehilangan sejarah di pihak mana pun?


1220
2017-09-15 08:31


asal


Jawaban:


Satu cabang dari repositori lain dapat dengan mudah ditempatkan di bawah subdirektori yang mempertahankan sejarahnya. Sebagai contoh:

git subtree add --prefix=rails git://github.com/rails/rails.git master

Ini akan muncul sebagai komit tunggal di mana semua file dari cabang master Rails ditambahkan ke dalam direktori "rails". Namun, judul komit berisi referensi ke pohon riwayat lama:

Tambahkan 'rails /' dari commit <rev>

Dimana <rev> adalah hash commit SHA-1. Anda masih bisa melihat sejarah, menyalahkan beberapa perubahan.

git log <rev>
git blame <rev> -- README.md

Perhatikan bahwa Anda tidak dapat melihat awalan direktori dari sini karena ini adalah cabang lama yang masih utuh. Anda harus memperlakukan ini seperti halnya file yang biasa dilakukan commit: Anda akan membutuhkan lompatan ekstra ketika mencapai itu.

# finishes with all files added at once commit
git log rails/README.md

# then continue from original tree
git log <rev> -- README.md

Ada solusi yang lebih kompleks seperti melakukannya 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.


326
2018-02-20 23:44



Jika Anda ingin bergabung project-a ke project-b:

cd path/to/project-b
git remote add project-a path/to/project-a
git fetch project-a
git merge --allow-unrelated-histories project-a/master # or whichever branch you want to merge
git remote remove project-a

Diambil dari: git menggabungkan repositori yang berbeda?

Metode ini bekerja cukup baik untuk saya, lebih pendek dan menurut saya jauh lebih bersih.

catatan: Itu --allow-unrelated-histories parameter hanya ada sejak git> = 2.9. Lihat Git - git menggabungkan Documentation / --allow-unrelated-histories 


1279
2018-05-11 09:37



Berikut dua solusi yang mungkin:

Submodules

Entah copy repositori A ke dalam direktori terpisah di proyek B yang lebih besar, atau (mungkin lebih baik) klon repositori A ke dalam subdirektori dalam proyek B. Kemudian gunakan git submodule untuk membuat repositori ini a submodule dari repositori B.

Ini adalah solusi yang baik untuk repositori yang digabungkan secara longgar, di mana pengembangan dalam repositori A berlanjut, dan bagian utama dari pengembangan adalah pengembangan terpisah yang berdiri sendiri di A. Lihat juga SubmoduleDukungan dan GitSubmoduleTutorial halaman di Git Wiki.

Subtree bergabung

Anda dapat menggabungkan repositori A ke dalam subdirektori dari proyek B menggunakan subtree bergabung strategi. Ini dijelaskan dalam Subtree Penggabungan dan Anda oleh Markus Prinz.

git remote add -f Bproject /path/to/B
git merge -s ours --allow-unrelated-histories --no-commit Bproject/master
git read-tree --prefix=dir-B/ -u Bproject/master
git commit -m "Merge B project as our subdirectory"
git pull -s subtree Bproject master

(pilihan --allow-unrelated-histories diperlukan untuk git> = 2.9.0)

Atau Anda bisa menggunakannya git subtree alat (repositori di github) oleh apenwarr (Avery Pennarun), diumumkan misalnya dalam posting blognya Alternatif baru untuk submodul git: git subtree.


Saya pikir dalam kasus Anda (A adalah menjadi bagian dari proyek B yang lebih besar) solusi yang tepat akan digunakan subtree bergabung


581
2017-09-15 08:38



Pendekatan submodule bagus jika Anda ingin mempertahankan proyek secara terpisah. Namun, jika Anda benar-benar ingin menggabungkan kedua proyek ke dalam repositori yang sama, maka Anda memiliki lebih banyak pekerjaan yang harus dilakukan.

Hal pertama yang akan digunakan git filter-branch untuk menulis ulang nama-nama segala sesuatu di repositori kedua untuk berada di subdirektori di mana Anda ingin mereka berakhir. Jadi, bukannya foo.c, bar.html, Anda akan memilikinya projb/foo.c dan projb/bar.html.

Maka, Anda harus dapat melakukan sesuatu seperti berikut:

git remote add projb [wherever]
git pull projb

Itu git pull akan melakukan git fetch diikuti oleh git merge. Seharusnya tidak ada konflik, jika repositori yang Anda tarik belum memiliki projb/ direktori.

Pencarian lebih lanjut menunjukkan bahwa sesuatu yang serupa dilakukan untuk digabungkan gitk ke git. Junio ​​C Hamano menulis tentang ini di sini: http://www.mail-archive.com/git@vger.kernel.org/msg03395.html


187
2018-02-01 08:10



git-subtree bagus, tapi itu mungkin bukan yang Anda inginkan.

Misalnya, jika projectA adalah direktori yang dibuat dalam B, setelah git subtree,

git log projectA

daftar hanya satu commit: penggabungan. Komit dari proyek gabungan adalah untuk jalur yang berbeda, sehingga tidak muncul.

Jawaban Greg Hewgill datang paling dekat, meskipun itu tidak benar-benar mengatakan bagaimana menulis ulang jalur.


Solusinya sangat sederhana.

(1) Dalam A,

PREFIX=projectA #adjust this

git filter-branch --index-filter '
    git ls-files -s |
    sed "s,\t,&'"$PREFIX"'/," |
    GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &&
    mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE
' HEAD

Catatan: Ini menulis ulang sejarah, jadi jika Anda berniat untuk terus menggunakan repo A ini, Anda mungkin ingin mengkloning (menyalin) salinan kosong itu terlebih dahulu.

(2) Kemudian di B, jalankan

git pull path/to/A

Voila! Anda memiliki projectA direktori di B. Jika Anda menjalankan git log projectA, Anda akan melihat semua komit dari A.


Dalam kasus saya, saya ingin dua subdirektori, projectA dan projectB. Dalam hal ini, saya melakukan langkah (1) ke B juga.


62
2018-06-11 14:31



Jika kedua repositori memiliki jenis file yang sama (seperti dua repositori Rail untuk proyek yang berbeda), Anda dapat mengambil data dari repositori sekunder ke repositori Anda saat ini:

git fetch git://repository.url/repo.git master:branch_name

lalu menggabungkannya ke repositori saat ini:

git merge --allow-unrelated-histories branch_name

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, sehingga 5 file konflik membutuhkan waktu saat membaca kode hanya beberapa menit.

Ingat untuk menyelesaikan penggabungan:

git commit

39
2018-02-27 03:09



Saya terus kehilangan sejarah saat menggunakan gabungan, jadi saya akhirnya menggunakan rebase karena dalam kasus saya kedua repositori cukup berbeda untuk tidak mengakhiri penggabungan pada setiap commit:

git clone git@gitorious/projA.git projA
git clone git@gitorious/projB.git projB

cd projB
git remote add projA ../projA/
git fetch projA 
git rebase projA/master HEAD

=> selesaikan konflik, lalu lanjutkan, sebanyak yang diperlukan ...

git rebase --continue

Melakukan hal ini mengarah pada satu proyek yang memiliki semua komitmen dari projA diikuti oleh komitmen dari projB


19
2017-08-18 21:06



Dalam kasus saya, saya punya my-plugin repositori dan a main-project repositori, dan saya ingin berpura-pura my-plugin selalu dikembangkan di plugins subdirektori dari main-project.

Pada dasarnya, saya menulis ulang sejarah my-plugin repositori sehingga tampak semua perkembangan terjadi di plugins/my-plugin subdirektori. Kemudian, saya menambahkan sejarah pengembangan my-plugin ke dalam main-project sejarah, dan menggabungkan kedua pohon itu bersama. Karena tidak ada plugins/my-plugin direktori sudah ada di main-project repositori, ini adalah gabungan tanpa konflik yang sepele. Repositori yang dihasilkan berisi semua riwayat 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|my-plugin) 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 dari my-plugin repositori, karena kita akan menulis ulang sejarah repositori ini.

Sekarang, arahkan ke root dari 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 sebenarnya Anda.

$ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|my-plugin) plugins/my-plugin || true)'" -- --all

Sekarang untuk penjelasan. git filter-branch --tree-filter (...) HEAD menjalankan (...) perintah pada setiap komit yang dapat dijangkau dari HEAD. Perhatikan bahwa ini beroperasi langsung pada data yang disimpan untuk setiap commit, jadi kita tidak perlu khawatir tentang gagasan "direktori kerja", "indeks", "pementasan", dan seterusnya.

Jika Anda menjalankan filter-branch perintah yang gagal, ia akan meninggalkan beberapa file di .git direktori dan waktu berikutnya Anda mencoba filter-branch itu akan mengeluh tentang hal ini, kecuali jika Anda memberikan -f opsi untuk filter-branch.

Untuk perintah yang sebenarnya, saya tidak beruntung bash untuk melakukan apa yang saya inginkan, jadi saya gunakan zsh -c untuk membuat zsh jalankan sebuah perintah. Pertama saya mengatur extended_glob pilihan, yang memungkinkan ^(...) sintaksis dalam mv perintah, serta glob_dots pilihan, yang memungkinkan saya untuk memilih dotfiles (seperti .gitignore) dengan gumpalan (^(...)).

Selanjutnya, saya menggunakan mkdir -p perintah untuk membuat keduanya plugins dan plugins/my-plugin pada waktu bersamaan.

Akhirnya, saya menggunakan zsh Fitur "glob negatif" ^(.git|my-plugin) untuk mencocokkan semua file di direktori root dari repositori kecuali untuk .git dan yang baru dibuat my-plugin map. (Tidak termasuk .git mungkin tidak perlu di sini, tetapi mencoba memindahkan direktori ke dalamnya sendiri merupakan kesalahan.)

Di repositori saya, commit awal tidak menyertakan file apa pun, jadi mv perintah mengembalikan kesalahan pada commit awal (karena tidak ada yang tersedia untuk dipindahkan). Oleh karena itu, saya menambahkan a || true maka git filter-branch tidak akan batalkan.

Itu --all opsi memberi tahu filter-branch untuk menulis ulang sejarah semua cabang di repositori, dan ekstra -- perlu diceritakan git untuk menafsirkannya sebagai bagian dari daftar opsi untuk cabang untuk menulis ulang, bukan sebagai opsi untuk filter-branch diri.

Sekarang, arahkan ke Anda main-project repositori dan periksa cabang apa pun yang ingin Anda gabungkan. Tambahkan salinan lokal Anda dari my-plugin repositori (dengan sejarahnya diubah) sebagai remote main-project dengan:

$ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORY

Anda sekarang akan memiliki dua pohon yang tidak terkait dalam sejarah 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 di Git pra-2.9.0, --allow-unrelated-histories opsi tidak ada. Jika Anda menggunakan salah satu versi ini, cukup abaikan opsi: pesan galat itu --allow-unrelated-histories mencegah itu juga ditambahkan dalam 2.9.0.

Anda seharusnya tidak menggabungkan konflik. Jika Anda melakukannya, itu mungkin berarti bahwa baik filter-branch perintah tidak berfungsi dengan benar atau sudah ada plugins/my-plugin direktori di main-project.

Pastikan untuk memasukkan pesan komitmen penjelas untuk kontributor masa depan yang bertanya-tanya apa yang terjadi dengan membuat repositori dengan dua akar.

Anda dapat memvisualisasikan grafik komit baru, yang seharusnya memiliki dua root commit, menggunakan yang di atas git log perintah. Perhatikan itu hanya itu master cabang akan digabungkan. Ini berarti bahwa jika Anda memiliki pekerjaan penting di bidang lain my-plugin cabang yang ingin Anda gabungkan ke dalam main-project pohon, Anda harus menahan diri dari menghapus my-plugin jarak jauh sampai Anda melakukan penggabungan ini. Jika tidak, maka komit dari cabang-cabang itu akan tetap ada di main-project repositori, tetapi beberapa akan tidak dapat dijangkau dan rentan terhadap pengumpulan sampah akhirnya. (Juga, Anda harus merujuk ke mereka oleh SHA, karena menghapus remote menghapus cabang-pelacak jarak jauhnya.)

Secara opsional, setelah Anda menggabungkan semua yang ingin Anda simpan my-plugin, Anda dapat menghapus my-plugin menggunakan remote:

$ git remote remove my-plugin

Anda sekarang dapat menghapus salinan aman dari my-plugin repositori yang riwayatnya Anda ubah. Dalam kasus saya, saya juga menambahkan pemberitahuan penghentian kepada yang asli my-plugin repositori 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 bervariasi.

Referensi:


13
2018-03-10 12:51