Birden çok git deposunu birleştirme


207

Diyelim ki benzeyen bir kurulum var

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

Tarihsel nedenlerle, bunların hepsinin kendi git depoları vardır. Ama işleri basitleştirmek için onları tek bir tanede birleştirmek istiyorum. Örneğin, şu anda iki takım değişiklik yapabilirim ve şöyle bir şey yapmalıyım

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

Sadece icra etmek güzel (şimdi) olurdu

cd phd
git commit

Bunu alt modülleri kullanarak veya alt depolarımdan çekmenin birkaç yolu var gibi görünüyor, ancak bu aradığımdan biraz daha karmaşık. En azından mutlu olurum

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

ama bu tek astar gibi görünmüyor. İçinde gitbana yardımcı olabilecek bir şey var mı ?


Ayrıca bu harika yaklaşımı da göz önünde bulundurun: stackoverflow.com/questions/1425892/…
Johan Sjöberg

Ayrıca göz önünde bulundurun: saintgimp.org/2013/01/22/…
ptim

Join-git-repos.py ayrı depoları varsa komut Birleştirmek istediğiniz ana dalları her biri güzel bir iş yapar.
Mark

Yanıtlar:


149

İşte burada verdiğim bir çözüm :

  1. Öncelikle doktora dizininizin tam bir yedeğini alın: Sıkı çalışma yıllarını kaybetmekten sorumlu tutulmak istemiyorum! ;-)

    $ cp -r phd phd-backup
    
  2. İçeriğini taşıma phd/codeiçin phd/code/codeve her zaman (bu kullanımları seyahatseverlerin Git orada olmuştur gibi görünecek biçimde tarihini düzeltmek filtre şube komutu):

    $ 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. İçeriğinden aynı phd/figuresve phd/thesis(sadece yerini codeile figuresve thesis).

    Şimdi dizin yapınız şöyle görünmelidir:

    phd
      |_code
      |    |_.git
      |    |_code
      |         |_(your code...)
      |_figures
      |    |_.git
      |    |_figures
      |         |_(your figures...)
      |_thesis
           |_.git
           |_thesis
                |_(your thesis...)
    
  4. Ardından kök dizinde bir git deposu oluşturun, her şeyi içine çekin ve eski depoları kaldırın:

    $ 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
    

    Son olarak, şimdi istediğiniz şeye sahip olmalısınız:

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

Bu prosedürün güzel bir yanı, sürümlendirilmemiş dosyaları ve dizinleri yerinde bırakmasıdır .

Bu yardımcı olur umarım.


Ancak bir uyarı kelimesi var: codeDizininiz zaten bir codealt dizine veya dosyaya sahipse , işler çok yanlış gidebilir ( figuresve thesiselbette aynıdır ). Bu durumda, tüm prosedürü gerçekleştirmeden önce bu dizini veya dosyayı yeniden adlandırın:

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

Ve prosedür tamamlandığında, bu son adımı ekleyin:

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

Tabii ki, codealt dizin veya dosya sürümlendirilmemişse, s mvyerine kullanın git mvve unutun git commit.


13
Bu snippet için teşekkürler - tam olarak ihtiyacım olanı yaptı (bir kez Mac OS X sed "\ t" işleme değil hesapladı (yerine ^ V ^ I kullanmak zorunda kaldım)
Craig Trader

6
İlk başta bunu çalıştıramadım ve sonuçta başka bir eski mesaj panosunda soruna çözüm buldum. Son satırda, dosya adlarının etrafına tırnak işaretleri koymak zorunda kaldım: mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEADve sonra harika çalıştı!
Jorin

3
Funky filtre dalı komutu git'in filtre dalı kılavuz sayfalarından gelir. Şöyle söylemelisiniz: a) doğru bir şekilde atfedilmelidir b) Böyle bir komutu çalıştırmayacağım, çünkü yüksek şöhretli biri bile StackOverflow'da yayınladı. Bunu adam sayfalarından bilerek yapacağım.
timtam

5
DİKKAT ET! MacOS X, sed'in GNU uzantısını kullanmaz, bu nedenle \ t dizisini bilmez. Sonuç berbat bir tarih! Benim çözüm kodu bir komut dosyasına gerçek bir <TAB> karakteri yazmak için yapıştırmak oldu. Terminal'den, ctrl + v tuşlarına basıp bir <TAB> yazarak bir sekme girilebilir. Craig'in çözümünü denemedim
Gil Vegliach

4
DİKKAT (2)! Ayrıca bazı dosya veya dizinlerde tire ('-') varsa sed komutunun başarısız olacağını unutmayın. Bu durumda 's ~ \ t ~ & code / ~' gibi bir şeyle değiştirebilirsiniz. Burada, aynı mantığı uygulayarak, isimlerde '~' dikkat edin
Gil Vegliach

75

git-stitch-repogit-fast-export --all --date-orderkomut satırında verilen git depolarının çıktısını işleyecek ve git-fast-importtüm kaynak depolarının geçmişine saygı duyan yeni bir taahhüt ağacında tüm taahhütleri içeren yeni bir havuz oluşturacak uygun bir akış oluşturacaktır.


33
Uh, bu üçüncü taraf bir araç,
git'in bir

1
Gerçekten, şimdi bana söylüyorsun :) Ah, sanırım bir gün CPAN paketlerini nasıl kuracağımı öğrenmek zorunda kaldım…
Robertson

1
Bu komutu gösterdiğin için teşekkürler. Birkaç depoyu SVN'den Git'e taşımak için kullanıyordum.
signine

1
Dallarınız / birleştirmeleriniz varsa UYARI çalışmayabilir! Gönderen git-stich-Repo . Sayfa: "git-stich-Repo doğrusal bir geçmişi (hayır birleştirmeleri) sahip depoları ile mükemmel çalışıyor .. yapmalıdır sürümü 0,06 eklenen dikiş algoritmasına iyileştirmeler depoları sahip çalışmalarına uygundur dalları ve birleşmeleri. "
Bryan P

6
Bu harici bir komut dosyasıdır, cevap çok kısa ve gerçekten yararlı değildir, bu komut dosyasının birleştirme taahhütleriyle ilgili sorunları vardır, pek çok kişi Perl veya CPAN'ı işlemez ve bu cevapta iyi açıklanmamıştır. Yani ... -1, üzgünüm.
Haralan Dobrev

20

Belki de, basitçe (önceki cevaba benzer şekilde, ancak daha basit komutları kullanarak), ayrı eski depoların her birinde, içeriği uygun şekilde adlandırılmış bir alt dizine taşıyan bir taahhütte bulunur, örneğin:

$ 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"

ve sonra üç ayrı depoyu yenisini birleştirmek

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

Sonra geçmişinizi kaydedeceksiniz, ancak tek bir repo ile devam edecek.


Bu tamam, ama bir başka bir repo birleştiriyorsa (yani, phd zaten mevcut bir repo boş değildi) o zaman phd kod dizinindeki alt klasörlerle aynı isimleri olan klasörler varsa 'git pull .. / phd / code 'tüm taahhütleri orignal yollarla çeker ve yalnızca sonunda mv komutunu uygular.
timtam

1
@Tymek: ama bu yine de bu durumda sorunsuz çalışıyor. Hoş olmayacak şey, tarihin yollarının "doğru" olmayacağıdır (yeni yollara karşılık gelir).
imz - Ivan Zakharyaschev

19

Alt ağaç birleştirme stratejisini deneyebilirsiniz . Repo B'yi repo A'ya birleştirmenize izin verir. Avantajı git-filter-branch, repo A geçmişinizi (SHA1 toplamlarını kırmak) yeniden yazmanızı gerektirmez.


Bağlantı çalışmıyor ve bu tarihi korumaz, değil mi?
Timtam

3
@Tymek (kernel.org'un özür dilerim, güvenlik ihlalinden sonra hala çalışmıyor). Gelen repo B'nin SHA1'lerini kırar. Ancak A sağlam kalır.
Leif Gruenwoldt


1
@LeifGruenwoldt 1. bağlantı şu anda çalışıyor. Ve ayna bağlantısı gitti, sanırım onu ​​kaldırmalısın.
Vadim Kotov

9

Git-filter-branch çözümü iyi çalışıyor, ancak git repo'nuz bir SVN içe aktarmasından geliyorsa aşağıdaki gibi bir mesajla başarısız olabileceğini unutmayın:

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

Bu durumda, ilk revizyonu filtre dalından hariç tutmanız gerekir - yani HEADsondaki değerini değiştirin [SHA of 2nd revision]..HEAD- bkz:

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


2
Teşekkür ederim! Neden işe yaramıyor diye başımı kaşıyorum! Repo gerçekten SVN'den geldi.
Arthur Maltson

1
Bunu yaptığımda da aynı hata. Umutlarımı uyandırdı. Ayrıca, bağlantı artık bozuk.
Ryan

Ne demek istediğini açıklayabilir misin "başını değiştirmek ...", benim repo bir SVN ithalat geliyor ve ben tam olarak bu sorunla karşı karşıya, çok yardım takdir!

5

@MiniQuark çözümü bana çok yardımcı oldu, ancak maalesef kaynak depolarındaki etiketleri dikkate almıyor (en azından benim durumumda). @MiniQuark cevabı için geliştirmem aşağıda.

  1. Önce birleştirilmiş repo ve birleştirilmiş depolar içeren bir dizin oluşturun, birleştirilmiş her bir dizin için bir dizin oluşturun.

    $ mkdir new_phd
    $ mkdir new_phd / kod
    $ mkdir new_phd / rakamlar
    $ mkdir new_phd / thesis

  2. Her depodan bir çekiş yapın ve tüm etiketleri getirin. (Yalnızca codealt dizin için talimatlar sunulması )

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

  3. İçeriğini taşıma (Bu MiniQuark yanıtında noktasına 2'ye iyileştirilmesi) new_phd/codeiçin new_phd/code/codeve eklemek code_her öncesi prefeix etiketi

    $ git filter-branch --index-filter 'git ls-dosya -s | sed "s- \ t \" * - & kod / - "| 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 -. * - kod _ & - "'KAFA

  4. Bunu yaptıktan sonra filtre dalı yapmadan iki kat daha fazla etiket olacaktır. Eski etiketler depoda kalır ve code_ön ekli yeni etiketler eklenir.

    $ git tag
    mytag1
    code_mytag1

    Eski etiketleri manuel olarak kaldırın:

    $ ls .git / refs / tags / * | grep -v "/ kod_" | xargs rm

    Diğer alt dizinler için 2,3,4 noktasını tekrarlayın

  5. Şimdi @MiniQuark anwser point 3'teki gibi dizin yapısına sahibiz.

  6. MiniQuark anwser'ın 4. noktasında olduğu gibi yapın, ancak bir çekme işlemi gerçekleştirdikten sonra ve .gitdizini kaldırmadan önce , etiketleri getirin:

    $ git fetch catalog refs / tags / *: refs / tags / *

    Devam et..

Bu sadece başka bir çözüm. Umarım birine yardımcı olur, bana yardımcı olur :)


5

Aristoteles Pagaltzis'in cevabından git-stitch-repo sadece basit, doğrusal geçmişe sahip depolar için çalışır.

MiniQuark'ın yanıtı tüm depolar için geçerlidir, ancak etiketleri ve dalları işlemez.

MiniQuark'ın açıkladığı gibi çalışan bir program oluşturdum, ancak bir birleştirme taahhüdü (N ebeveynleri ile birlikte) kullanıyor ve bu birleştirme taahhütlerine işaret etmek için tüm etiketleri ve dalları yeniden oluşturuyor.

Nasıl kullanılacağına ilişkin örnekler için git-merge-repos deposuna bakın.



3

Aslında git-stitch-repo, ek açıklama eklenmiş etiketler de dahil olmak üzere şubeleri ve etiketleri destekliyor (rapor ettiğim bir hata olduğunu gördüm ve düzeltildi). Ne yararlı buldum etiketleri. Etiketler taahhütlere eklendiğinden ve bazı çözümler (Eric Lee'nin yaklaşımı gibi) etiketlerle başa çıkmada başarısız olur. İçe aktarılan bir etiketin dışında bir şube oluşturmaya çalışırsınız ve git git birleştirme / taşıma işlemlerini geri alır ve konsolun deposının etiketin geldiği depoya yakın olduğu gibi geri gönderir. Ayrıca, 'birleştirdiğiniz / birleştirdiğiniz' birden çok depoda aynı etiketi kullanırsanız sorunlar vardır. Örneğin, repo'nuzun A reklamı B'niz varsa, her ikisinde de rel_1.0 etiketi bulunur. Repo A ve repo B'yi repo AB'ye birleştiriyorsunuz. Rel_1.0 etiketleri iki farklı taahhütte bulunduğundan (biri A için ve diğeri B için), AB'de hangi etiket görünecek? İçe aktarılan repo A'dan veya içe aktarılan repo B'den gelen etiket, ancak her ikisi birden değil.

git-stitch-repo rel_1.0-A ve rel_1.0-B etiketleri oluşturarak bu sorunu çözmeye yardımcı olur. Rel_1.0 etiketini kontrol edemeyebilir ve her ikisini de bekleyemeyebilirsiniz, ancak en azından her ikisini de görebilirsiniz ve teorik olarak, bunları ortak bir yerel dalda birleştirebilir ve birleştirilmiş dalda bir rel_1.0 etiketi oluşturabilirsiniz (yalnızca kaynak kodunu değiştirmeyin ve değiştirmeyin). Her repodaki şubeler gibi şubeleri yerel şubelere birleştirebileceğiniz için şubelerle çalışmak daha iyidir. (dev-a ve dev-b, daha sonra başlangıç ​​noktasına itilebilen bir yerel geliştirici dalına birleştirilebilir).


2

Önerdiğiniz sıra

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

çalışır, ancak taahhüt geçmişinizi kaybedersiniz.


Tarihi kaybetmek o kadar da kötü değil, ancak depo kendi işim için olduğu için (yani, özel) orada sürümlendirilmesini istemediğim veya henüz sürümlendirilmemiş bir çok şey var.
Will Robertson

1

Bir mainProject içindeki secondProject öğesini birleştirmek için:

A) İkinciProjede

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

B) Ana Projede:

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

Bu dalda yapmanız ve yapmanız gereken tüm ağır dönüşümü yapın.

C) Sonra ustaya geri dönün ve iki dal arasında klasik bir birleşme:

git checkout master
git merge secondProject

Bu, git projelerinin kökündeki tüm dosya ve klasörleri tek bir projede birleştirir. Bunun olmasını isteyeceğimden şüpheliyim.
Clintm

0

Çözümümü de buraya atacağım. Temelde oldukça basit bir bash script sarıcı git filter-branch. Diğer çözümler gibi, yalnızca ana dalları taşır ve etiketleri taşımaz. Ancak tam ana taahhüt geçmişleri taşınır ve kısa bir bash betiğidir, bu nedenle kullanıcıların incelemesi veya ayarlaması nispeten kolay olmalıdır.

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


0

Bu bash betiği, sed sekme karakteri sorunu (örneğin MacOS'ta) ve eksik dosyalar sorunu etrafında çalışır .

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

Bu bir kombinasyonudur miniquark , Marius-Butuc ve Ryan adlı kullanıcının yayınlarına. Onlara şerefe!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.