Menggabungkan beberapa repositori git

207

Katakanlah saya punya setup yang terlihat seperti

phd/code/
phd/figures/
phd/thesis/

Untuk alasan historis, ini semua memiliki repositori git mereka sendiri. Tapi saya ingin menggabungkan mereka menjadi satu untuk menyederhanakan banyak hal. Misalnya, saat ini saya mungkin membuat dua set perubahan dan harus melakukan sesuatu seperti

cd phd/code
git commit 
cd ../figures
git commit

Akan (sekarang) menyenangkan hanya untuk tampil

cd phd
git commit

Tampaknya ada beberapa cara untuk melakukan ini menggunakan submodula atau menarik dari sub-repositori saya, tapi itu sedikit lebih kompleks daripada yang saya cari. Paling tidak, aku akan senang

cd phd
git init
git add [[everything that's already in my other repositories]]

tapi itu tidak tampak seperti satu kalimat. Apakah ada sesuatu gityang bisa membantu saya keluar?

Will Robertson
sumber
Pertimbangkan juga pendekatan hebat ini: stackoverflow.com/questions/1425892/…
Johan Sjöberg
Juga pertimbangkan: saintgimp.org/2013/01/22/…
ptim
The join-git-repos.py Script melakukan pekerjaan yang bagus jika Anda memiliki repositori terpisah, masing-masing dengan cabang master yang Anda ingin menggabungkan.
Tandai

Jawaban:

149

Inilah solusi yang saya berikan di sini :

  1. Pertama-tama lakukan pencadangan lengkap terhadap direktori phd Anda: Saya tidak ingin dianggap bertanggung jawab atas kehilangan tahun kerja keras Anda! ;-)

    $ cp -r phd phd-backup
    
  2. Pindahkan konten phd/codeke phd/code/code, dan perbaiki histori sehingga sepertinya selalu ada (ini menggunakan perintah cabang-filter git ):

    $ cd phd/code
    $ git filter-branch --index-filter \
        'git ls-files -s | sed "s#\t#&code/#" |
         GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
         git update-index --index-info &&
         mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' HEAD
    
  3. Sama untuk konten phd/figuresdan phd/thesis(hanya ganti codedengan figuresdan thesis).

    Sekarang struktur direktori Anda akan terlihat seperti ini:

    phd
      |_code
      |    |_.git
      |    |_code
      |         |_(your code...)
      |_figures
      |    |_.git
      |    |_figures
      |         |_(your figures...)
      |_thesis
           |_.git
           |_thesis
                |_(your thesis...)
    
  4. Kemudian buat repositori git di direktori root, tarik semuanya ke dalamnya dan hapus repositori lama:

    $ cd phd
    $ git init
    
    $ git pull code
    $ rm -rf code/code
    $ rm -rf code/.git
    
    $ git pull figures --allow-unrelated-histories
    $ rm -rf figures/figures
    $ rm -rf figures/.git
    
    $ git pull thesis --allow-unrelated-histories
    $ rm -rf thesis/thesis
    $ rm -rf thesis/.git
    

    Akhirnya, Anda sekarang harus memiliki apa yang Anda inginkan:

    phd
      |_.git
      |_code
      |    |_(your code...)
      |_figures
      |    |_(your figures...)
      |_thesis
           |_(your thesis...)
    

Satu sisi bagus dari prosedur ini adalah ia akan meninggalkan file dan direktori yang tidak berversi di tempatnya.

Semoga ini membantu.


Hanya satu kata peringatan: jika codedirektori Anda sudah memiliki codesubdirektori atau file, ada yang salah (sama untuk figuresdan thesistentu saja). Jika itu masalahnya, cukup ganti nama direktori atau file tersebut sebelum menjalani seluruh prosedur ini:

$ cd phd/code
$ git mv code code-repository-migration
$ git commit -m "preparing the code directory for migration"

Dan ketika prosedur selesai, tambahkan langkah terakhir ini:

$ cd phd
$ git mv code/code-repository-migration code/code
$ git commit -m "final step for code directory migration"

Tentu saja, jika codesubdirektori atau file tidak diversi, gunakan saja mvalih-alih git mv, dan lupakan git commits.

MiniQuark
sumber
13
Terima kasih atas cuplikan ini - ini tepat seperti yang saya butuhkan (begitu saya menghitung untuk Mac OS X dan tidak memproses "\ t" (saya harus menggunakan ^ V ^ saya sebagai gantinya).
Craig Trader
6
Saya tidak bisa mendapatkan ini bekerja pada awalnya dan akhirnya menemukan solusi untuk masalah di papan pesan lama lainnya. Pada baris terakhir, saya harus memberi tanda kutip di sekitar nama file seperti ini: mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEADdan kemudian bekerja dengan baik!
Jorin
3
Perintah branch-filter funky berasal dari halaman manual branch-filter git. Anda harus mengatakan bahwa sebagai: a) itu harus dikaitkan dengan benar b) Saya tidak akan menjalankan perintah seperti itu hanya karena seseorang, bahkan dengan reputasi tinggi, mempostingnya di StackOverflow. Mengetahui itu dari halaman manual saya akan.
tymtam
5
AWAS! MacOS X tidak menggunakan ekstensi sed GNU, jadi ia tidak tahu urutan \ t. Hasilnya adalah sejarah yang kacau! Solusi saya adalah menempelkan kode dalam file skrip dan menulis karakter <TAB> yang sebenarnya di dalamnya. Dari Terminal, sebuah tab dapat dimasukkan dengan menekan ctrl + v dan kemudian menulis a <TAB>. Saya belum mencoba solusi Craig
Gil Vegliach
5
HATI-HATI (2)! Juga perhatikan bahwa jika beberapa file atau direktori mengandung tanda hubung ('-') perintah sed akan gagal. Dalam hal ini, Anda dapat menggantinya dengan sesuatu seperti '~ ~ t ~ & kode / ~'. Di sini, menerapkan logika yang sama, waspadai '~' dalam nama
Gil Vegliach
75

git-stitch-repoakan memproses output dari git-fast-export --all --date-orderrepositori git yang diberikan pada command-line, dan membuat aliran yang sesuai untuk git-fast-importitu akan membuat repositori baru yang berisi semua komit di pohon komit baru yang menghormati sejarah semua repositori sumber.

Aristoteles Pagaltzis
sumber
33
Eh, ini alat pihak ketiga, bukan bagian dari git… :-)
Aristotle Pagaltzis
1
Memang, sekarang Anda memberi tahu saya :) Oh well, saya kira saya harus belajar cara menginstal paket CPAN suatu hari ...
Will Robertson
1
Terima kasih telah menunjukkan perintah itu. Baru saja menggunakannya untuk membantu memindahkan beberapa repo dari SVN ke Git.
masuk
1
PERINGATAN mungkin tidak berfungsi jika Anda memiliki cabang / gabungan! Dari halaman git-stich-repo : "git-stich-repo bekerja dengan baik dengan repositori yang memiliki sejarah linier (tanpa penggabungan) ... Peningkatan pada algoritma penjahitan yang ditambahkan dalam versi 0.06 harus membuatnya cocok untuk bekerja dengan repositori yang memiliki cabang dan gabungan. "
Bryan P
6
Ini adalah skrip eksternal, jawabannya terlalu pendek dan tidak terlalu membantu, skrip ini memiliki masalah dengan gabungan komit, tidak banyak orang akan menangani Perl atau CPAN dan ini tidak dijelaskan dengan baik dalam jawabannya. Jadi ... -1, maaf.
Haralan Dobrev
20

Mungkin, secara sederhana (mirip dengan jawaban sebelumnya, tetapi menggunakan perintah yang lebih sederhana) membuat di masing-masing repositori lama yang terpisah komit yang memindahkan konten ke subdirektori yang sesuai, misalnya:

$ cd phd/code
$ mkdir code
# This won't work literally, because * would also match the new code/ subdir, but you understand what I mean:
$ git mv * code/
$ git commit -m "preparing the code directory for migration"

dan kemudian menggabungkan ketiga repo yang terpisah menjadi satu yang baru, dengan melakukan sesuatu seperti:

$ cd ../..
$ mkdir phd.all
$ cd phd.all
$ git init
$ git pull ../phd/code
...

Maka Anda akan menyimpan riwayat Anda, tetapi akan melanjutkan dengan satu repo.

imz - Ivan Zakharyaschev
sumber
Ini boleh saja, tetapi jika Anda menggabungkan satu repo ke repo lain (yaitu, phd bukan repo yang sudah ada) maka jika phd memiliki folder dengan nama yang sama dengan subfolder di direktori kode Anda akan mengalami masalah seperti 'git pull .. / phd / code 'menarik semua commit dengan jalur orignal dan hanya pada akhirnya berlaku komit mv.
tymtam
1
@Tymek: tetapi ini masih akan berfungsi dalam situasi itu, tanpa masalah. Hal yang tidak akan menyenangkan adalah bahwa jalur dalam sejarah tidak akan "benar" (sesuai dengan jalur baru).
imz - Ivan Zakharyaschev
19

Anda bisa mencoba strategi menggabungkan subtree . Ini akan memungkinkan Anda menggabungkan repo B menjadi repo A. Keuntungan lebih dari git-filter-branchitu adalah tidak mengharuskan Anda untuk menulis ulang riwayat repo A Anda (melanggar jumlah SHA1).

Leif Gruenwoldt
sumber
Tautan tidak berfungsi dan ini tidak akan melindungi sejarah, bukan?
tymtam
3
@Tymek (Maaf bagian dari kernel.org masih turun setelah pelanggaran keamanan). Ini melanggar SHA1 tentang repo yang masuk B. Tapi A tetap utuh.
Leif Gruenwoldt
2
Inilah cermin dari dokumen itu untuk saat ini ftp.sunet.se/pub/Linux/kernel.org/software/scm/git/docs/howto/…
Leif Gruenwoldt
1
@LeifGruenwoldt Tautan 1 berfungsi sekarang. Dan tautan cerminnya hilang, Anda harus menghapusnya saya kira.
Vadim Kotov
9

Solusi git-filter-branch berfungsi dengan baik, tetapi perhatikan bahwa jika repo git Anda berasal dari impor SVN mungkin gagal dengan pesan seperti:

Rewrite 422a38a0e9d2c61098b98e6c56213ac83b7bacc2 (1/42)mv: cannot stat `/home/.../wikis/nodows/.git-rewrite/t/../index.new': No such file or directory

Dalam hal ini Anda perlu mengecualikan revisi awal dari cabang-filter - yaitu mengubah HEADdi akhir menjadi [SHA of 2nd revision]..HEAD- lihat:

http://www.git.code-experiments.com/blog/2010/03/merging-git-repositories.html

Gareth
sumber
2
Terima kasih! Saya telah menggaruk-garuk kepala saya mengapa ini tidak berhasil! Repo itu memang berasal dari SVN.
Arthur Maltson
1
Kesalahan yang sama ketika saya melakukan itu. Punya harapan saya. Juga, tautannya sekarang rusak.
Ryan
Bisakah Anda menguraikan apa yang Anda maksud dengan "mengubah kepala di ke ...", repo saya berasal dari impor SVN dan saya menghadapi masalah ini, akan sangat membantu!
5

Solusi @MiniQuark banyak membantu saya, tapi sayangnya itu tidak memperhitungkan tag akun yang ada di repositori sumber (Setidaknya dalam kasus saya). Di bawah ini adalah peningkatan saya untuk jawaban @MiniQuark.

  1. Pertama buat direktori yang akan berisi repo yang dibuat dan repo yang digabungkan, buat direktori untuk masing-masing satu yang digabungkan.

    $ mkdir new_phd
    $ mkdir new_phd / kode
    $ mkdir new_phd / angka
    $ mkdir new_phd / tesis

  2. Lakukan penarikan setiap repositori dan ambil semua tag. (Menyajikan instruksi hanya untuk codesub-direktori)

    $ cd new_phd / kode
    $ git init
    $ git pull ../../original_phd/code master
    $ git fetch ../../original_phd/code refs / tags / *: refs / tag / *

  3. (Ini adalah peningkatan ke poin 2 dalam jawaban MiniQuark) Pindahkan konten new_phd/codeke new_phd/code/codedan tambahkan code_prefeix sebelum setiap tag

    $ git filter-branch --index-filter 'git ls-files -s | sed "s- \ t \" * - & kode / - "| GIT_INDEX_FILE = $ GIT_INDEX_FILE.new git update-index --index-info && mv $ GIT_INDEX_FILE.new $ GIT_INDEX_FILE '--tag-name-filter' sed" s "s" -. * - kode _ & - "'KEPALA

  4. Setelah melakukannya, akan ada tag dua kali lebih banyak daripada sebelum melakukan cabang-filter. Tag lama tetap dalam repo dan tag baru dengan code_awalan ditambahkan.

    $ git tag
    mytag1
    code_mytag1

    Hapus tag lama secara manual:

    $ ls .git / refs / tag / * | grep -v "/ code_" | xargs rm

    Ulangi poin 2,3,4 untuk subdirektori lainnya

  5. Sekarang kita memiliki struktur direktori seperti di @MiniQuark anwser point 3.

  6. Lakukan seperti pada poin 4 dari MiniQuark anwser, tetapi setelah melakukan tarikan dan sebelum menghapus .gitdir, ambil tag:

    $ git ambil katalog ref / tag / *: ref / tag / *

    Terus..

Ini hanyalah solusi lain. Semoga ini bisa membantu seseorang, itu membantu saya :)

Sial
sumber
5

git-stitch-repo dari jawaban Aristoteles Pagaltzis hanya berfungsi untuk repositori dengan sejarah linier yang sederhana.

Jawaban MiniQuark bekerja untuk semua repositori, tetapi tidak menangani tag dan cabang.

Saya membuat sebuah program yang bekerja dengan cara yang sama seperti yang dijelaskan oleh MiniQuark, tetapi ia menggunakan satu komit gabungan (dengan N orang tua) dan juga membuat ulang semua tag dan cabang untuk menunjukkan komit gabungan tersebut.

Lihat repositori git-merge-repos untuk contoh bagaimana menggunakannya.

robinst
sumber
3

Saya telah membuat alat yang membuat tugas ini. Metode yang digunakan mirip (secara internal membuat beberapa hal seperti --filter-branch) tetapi lebih ramah. Apakah GPL 2.0

http://github.com/geppo12/GitCombineRepo

Giuseppe Monteleone
sumber
3

Sebenarnya, git-stitch-repo sekarang mendukung cabang dan tag, termasuk tag beranotasi (saya menemukan ada bug yang saya laporkan, dan diperbaiki). Apa yang saya temukan berguna adalah dengan tag. Karena tag dilampirkan pada komit, dan beberapa solusi (seperti pendekatan Eric Lee) gagal menangani tag. Anda mencoba untuk membuat cabang dari tag yang diimpor, dan itu akan membatalkan git gabungan / bergerak dan mengirim Anda kembali seperti repositori terkonsolidasi yang hampir identik dengan repositori yang berasal dari tag. Juga, ada masalah jika Anda menggunakan tag yang sama di beberapa repositori yang Anda 'gabungkan / konsolidasi'. Misalnya, jika Anda memiliki repo A ad B, keduanya memiliki tag rel_1.0. Anda menggabungkan repo A dan repo B ke repo AB. Karena tag rel_1.0 ada di dua komit yang berbeda (satu untuk A dan satu untuk B), tag mana yang akan terlihat di AB? Entah tag dari repo A yang diimpor atau dari repo B yang diimpor, tetapi tidak keduanya.

git-stitch-repo membantu mengatasi masalah itu dengan membuat tag rel_1.0-A dan rel_1.0-B. Anda mungkin tidak dapat checkout tag rel_1.0 dan mengharapkan keduanya, tetapi setidaknya Anda dapat melihat keduanya, dan secara teoritis, Anda dapat menggabungkan mereka ke cabang lokal umum kemudian membuat tag rel_1.0 pada cabang gabungan (dengan asumsi Anda baru saja bergabung dan tidak mengubah kode sumber). Lebih baik bekerja dengan cabang, karena Anda dapat menggabungkan seperti cabang dari setiap repo ke cabang lokal. (dev-a dan dev-b dapat digabung menjadi cabang dev lokal yang kemudian dapat didorong ke asalnya).

pengguna3622356
sumber
2

Urutan yang Anda sarankan

git init
git add *
git commit -a -m "import everything"

akan bekerja, tetapi Anda akan kehilangan riwayat komit Anda.

Patrick_O
sumber
Kehilangan sejarah tidak terlalu buruk, tetapi karena repositori adalah untuk pekerjaan saya sendiri (yaitu, itu pribadi) ada banyak hal di sana yang saya tidak ingin versi atau yang belum diversi.
Will Robertson
1

Untuk menggabungkan Proyek kedua dalam Proyek utama:

A) Dalam Proyek kedua

git fast-export --all --date-order > /tmp/secondProjectExport

B) Dalam Proyek utama:

git checkout -b secondProject
git fast-import --force < /tmp/secondProjectExport

Di cabang ini lakukan semua transformasi besar yang perlu Anda lakukan dan lakukan mereka.

C) Kemudian kembali ke master dan penggabungan klasik antara dua cabang:

git checkout master
git merge secondProject
pengguna123568943685
sumber
Ini akan menggabungkan semua file dan folder di root dari kedua proyek git menjadi satu proyek. Saya ragu _anyone_would ingin ini terjadi.
Clintm
0

Saya akan melemparkan solusi saya di sini juga. Ini pada dasarnya pembungkus skrip bash yang cukup sederhana git filter-branch. Seperti solusi lain, hanya memigrasikan cabang utama dan tidak memigrasikan tag. Tetapi histori master penuh komit dimigrasikan dan itu adalah skrip bash pendek sehingga seharusnya relatif mudah bagi pengguna untuk meninjau atau mengubah.

https://github.com/Oakleon/git-join-repos

chrishiestand
sumber
0

Skrip bash ini berfungsi di sekitar masalah karakter tab sed (di MacOS misalnya) dan masalah file yang hilang.

export SUBREPO="subrepo"; # <= your subrepository name here
export TABULATOR=`printf '\t'`;
FILTER='git ls-files -s | sed "s#${TABULATOR}#&${SUBREPO}/#" |
  GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
  git update-index --index-info &&
  if [ -f "$GIT_INDEX_FILE.new" ]; then mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE; else echo "git filter skipped missing file: $GIT_INXEX_FILE.new"; fi'

git filter-branch --index-filter "$FILTER" HEAD

Ini adalah kombinasi dari posting miniquark , marius-butuc dan ryan . Cheers untuk mereka!

bue
sumber