複数のGitリポジトリを組み合わせる


207

たとえば、次のような設定があるとします。

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

歴史的な理由により、これらにはすべて独自のgitリポジトリがあります。ただし、少し単純にするために、それらを1つにまとめたいと思います。たとえば、現在、2つのセットの変更を行い、次のようなことを行う必要があるとします。

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

実行するだけで(今)いいです

cd phd
git commit

サブモジュールを使用したり、サブリポジトリから取得したりする方法はいくつかあるようですが、それは私が探しているよりも少し複雑です。少なくとも、私は満足しています

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

しかし、それはワンライナーのようには見えません。git私を助けることができる何かがありますか?


:また、この偉大なアプローチを検討stackoverflow.com/questions/1425892/...
ヨハン・シェーベルイ


join-git-repos.pyあなたが別のリポジトリ、あなたが結合したいマスター支店を持つ各を持っている場合、スクリプトは素晴らしい仕事をしていません。
マーク

回答:


149

ここに私がここに与えた解決策があります

  1. 最初にphdディレクトリの完全なバックアップを作成します。長年のハードワークの損失に対して責任を負うことはしたくないです!;-)

    $ cp -r phd phd-backup
    
  2. のコンテンツをphd/codeに移動しphd/code/code、履歴が常にそこにあるように修正します(これはgitのfilter-branchコマンドを使用します):

    $ 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. 内容を同じphd/figuresphd/thesis(単に置き換えるcodefiguresしてthesis)。

    これで、ディレクトリ構造は次のようになります。

    phd
      |_code
      |    |_.git
      |    |_code
      |         |_(your code...)
      |_figures
      |    |_.git
      |    |_figures
      |         |_(your figures...)
      |_thesis
           |_.git
           |_thesis
                |_(your thesis...)
    
  4. 次に、ルートディレクトリにgitリポジトリを作成し、そこにすべてをプルして、古いリポジトリを削除します。

    $ 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
    

    最後に、あなたは今あなたが望むものを持っているはずです:

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

この手順の良い面の1つは、バージョン管理されていないファイルとディレクトリがそのまま残ることです。

お役に立てれば。


警告の1ワードだけしかし:あなたの場合codeディレクトリがすでに持っているcodeサブディレクトリやファイルを、物事は(ため同じ非常にうまくいかないかもしれないfiguresし、thesisもちろん)。その場合は、この手順全体を実行する前に、そのディレクトリまたはファイルの名前を変更してください。

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

そして、手順が完了したら、この最後のステップを追加します。

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

もちろん、codeサブディレクトリまたはファイルがバージョン管理されていない場合は、のmv代わりに使用しgit mvて、git commits を忘れてください。


13
このスニペットをありがとう-それは私が必要とするものを正確に実行しました(Mac OS Xを使用すると "\ t"が処理されなかった(代わりに^ V ^ Iを使用する必要があった)
Craig Trader

6
最初はこれを機能させることができず、最終的には別の古いメッセージボードで問題の解決策を見つけました。最後の行では、ファイル名を次のように引用符で囲むmv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD必要がありました。
ジョリン

3
ファンキーなフィルターブランチコマンドは、gitのフィルターブランチのmanページからのものです。次のように言う必要があります:a)正しく属性付けする必要がありますb)誰かが、たとえ高い評価を得ていても、StackOverflowに投稿したからといって、そのようなコマンドを実行しません。それが私がするマニュアルページからのものであることを知っています。
tymtam

5
気を付けて!MacOS XはsedのGNU拡張機能を使用しないため、シーケンス\ tがわかりません。結果はめちゃくちゃな歴史です!私の解決策は、スクリプトファイルにコードを貼り付け、その中に実際の<TAB>文字を書き込むことでした。ターミナルから、ctrl + vを押してタブを入力し、<TAB>を書き込むことができます。私はCraigのソリューションを試していません
Gil Vegliach '19年

5
ウォッチアウト(2)!また、一部のファイルまたはディレクトリにハイフン( '-')が含まれている場合、sedコマンドが失敗することにも注意してください。その場合、 's〜\ t〜&& code /〜'のように置き換えることができます。ここで、同じロジックを適用して、名前の「〜」に注意してください
Gil Vegliach

75

git-stitch-repogit-fast-export --all --date-orderコマンドラインで指定されたgitリポジトリでの出力を処理し、git-fast-importそれに適したストリームを作成して、すべてのソースリポジトリの履歴を尊重する新しいコミットツリーにすべてのコミットを含む新しいリポジトリを作成します。


33
ええと、それはgitの一部ではなく、サードパーティのツールです…:-)
Aristotle Pagaltzis 2008年

1
確かに、あなたは私に教えてくれます:)まあ、私はいつかCPANパッケージをインストールする方法を学ばなければならなかったと思います…
Will Robertson

1
そのコマンドを指摘してくれてありがとう。ちょうどいくつかのリポジトリをSVNからGitに移動するのに役立つようにそれを使用しています。
10

1
ブランチ/マージがある場合、警告が機能しない可能性があります!以下からのgit-シュティッヒ・レポ。ページ:「gitの-シュティッヒ-レポは線形の歴史(なしマージを)持っているリポジトリで完璧に動作します。..作るべきバージョン0.06で追加されスティッチングアルゴリズムの改良は、リポジトリが持つ仕事に適していますブランチとマージ。」
Bryan P

6
これは外部スクリプトです。答えは短すぎてあまり役に立ちません。このスクリプトにはマージコミットに関する問題があります。PerlやCPANを扱う人は多くなく、これは答えで十分に説明されていません。だから...- 1、ごめんなさい。
Haralan Dobrev 2014

20

おそらく、単純に(前の回答と同様ですが、より単純なコマンドを使用して)、別々の古いリポジトリのそれぞれで、コンテンツを適切な名前のサブディレクトリに移動するコミットを作成します。例:

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

次に、次のようなsmthを実行して、3つの別々のリポジトリを1つの新しいリポジトリにマージします。

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

その後、履歴を保存しますが、1つのリポジトリで続行されます。


これは問題ありませんが、1つのリポジトリを別のリポジトリにマージする場合(つまり、phdが既存の空のリポジトリではなかった場合)、phdにコードディレクトリ内のサブフォルダと同じ名前のフォルダがある場合、 'git pull .. / phd / code 'は、元のパスを持つすべてのコミットをプルし、最後にのみmvコミットを適用します。
チムタム

1
@Tymek:しかし、この状況でも問題なく機能します。良くないことは、履歴のパスが「正しくない」ことです(新しいパスに対応)。
imz-Ivan Zakharyaschev

19

サブツリーのマージ戦略を試すことができます。これにより、レポBをレポAにマージできます。利点git-filter-branchは、レポAの履歴を書き直す必要がないことです(SHA1の合計を壊す)。


リンクは機能せず、これは履歴を保持しませんか?
tymtam

3
@Tymek(kernel.orgの申し訳ない部分は、セキュリティ侵害後もまだダウンしています)。着信レポBのSHA1を壊します。しかし、Aはそのままです。
Leif Gruenwoldt、2011

2
ここでは今のところそのドキュメントの鏡だftp.sunet.se/pub/Linux/kernel.org/software/scm/git/docs/howto/...は
レイフ・Gruenwoldt

1
@LeifGruenwoldt最初のリンクは現在機能しています。ミラーリンクがなくなったので、削除する必要があります。
Vadim Kotov

9

git-filter-branchソリューションはうまく機能しますが、gitリポジトリがSVNインポートからのものである場合、次のようなメッセージで失敗する可能性があることに注意してください。

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

この場合、フィルターブランチから最初のリビジョンを除外する必要があります。つまりHEAD、末尾のを次のように変更します[SHA of 2nd revision]..HEAD-参照:

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


2
ありがとうございました!なぜこれがうまくいかないのか頭を悩ませてきました!リポジトリは確かにSVNからのものでした。
Arthur Maltson 2013年

1
私がそれをするときと同じエラー。私の希望は上がった。また、リンクが壊れています。
ライアン14

「頭をtoに変更する」とはどういう意味ですか?私のリポジトリはSVNインポートからのものであり、私はまさにこの問題に直面しています。多くの助けに感謝します!

5

@MiniQuarkソリューションは非常に役立ちましたが、残念ながらソースリポジトリにあるタグは考慮されません(少なくとも私の場合)。以下は、@ MiniQuarkの回答に対する私の改善点です。

  1. 最初に、構成されたリポジトリとマージされたリポジトリを含むディレクトリを作成し、マージされたリポジトリごとにディレクトリを作成します。

    $ mkdir new_phd
    $ mkdir new_phd / code
    $ mkdir new_phd / figures
    $ mkdir new_phd / thesis

  2. 各リポジトリをプルして、すべてのタグを取得します。(codeサブディレクトリのみの表示手順)

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

  3. (これはMiniQuark回答のポイント2への改善です)のコンテンツを移動します new_phd/codeに各タグのnew_phd/code/codecode_prefeix を追加します

    $ 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 '--tag-name-filter' sed" s -。*-code _&-"'HEAD

  4. これを実行すると、フィルター分岐を実行する前のタグの2倍のタグが作成されます。古いタグはリポジトリに残り、code_プレフィックス付きの新しいタグが追加されます。

    $ gitタグ
    mytag1
    code_mytag1

    古いタグを手動で削除します。

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

    他のサブディレクトリに対してポイント2、3、4を繰り返す

  5. これで、@ MiniQuark anwser point 3のようなディレクトリ構造ができました。

  6. MiniQuark anwserのポイント4と同じようにしますが、プルを実行した後、.gitdirを削除する前に、タグをフェッチします。

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

    継続する..

これは単なる別のソリューションです。それが誰かを助けることを願って、それが私を助けてくれた:)


5

Aristotle Pagaltzisの git-stitch-repo の回答は、単純で線形の履歴を持つリポジトリでのみ機能します。

MiniQuarkの回答はすべてのリポジトリで機能しますが、タグとブランチは処理しません。

MiniQuarkと同じように機能するプログラムを作成しましたが、1つのマージコミット(親はN)を使用し、すべてのタグとブランチを再作成して、これらのマージコミットをポイントします。

使用例については、git-merge-reposリポジトリを参照してください。



3

実際、git-stitch-repoは注釈付きタグを含むブランチとタグをサポートするようになりました(私が報告したバグが見つかり、修正されました)。私が便利だと思ったのはタグです。タグはコミットに添付されているため、一部のソリューション(Eric Leeのアプローチなど)はタグを処理できません。インポートしたタグからブランチを作成しようとすると、すべてのgitマージ/移動が取り消され、統合リポジトリがタグの元のリポジトリとほぼ同じであるかのように返送されます。また、「マージ/統合」した複数のリポジトリで同じタグを使用すると問題が発生します。たとえば、リポジトリAと広告Bがあり、どちらにもタグrel_1.0があるとします。レポAとレポBをレポABにマージします。rel_1.0タグは2つの異なるコミット(A用とB用)にあるため、ABではどのタグが表示されますか?インポートされたリポジトリAまたはインポートされたリポジトリBのどちらか一方のタグ。両方は含まれません。

git-stitch-repoはrel_1.0-Aおよびrel_1.0-Bタグを作成することでその問題に対処するのに役立ちます。rel_1.0タグをチェックアウトして両方を期待することはできないかもしれませんが、少なくとも両方を確認できます。理論的には、それらを共通のローカルブランチにマージしてから、そのマージされたブランチにrel_1.0タグを作成できます(ただし、ソースコードをマージして変更しないでください)。各リポジトリのブランチのようにローカルブランチにマージできるため、ブランチを使用する方が適切です。(dev-aとdev-bは、ローカルのdevブランチにマージでき、その後、オリジンにプッシュできます)。


2

あなたが提案したシーケンス

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

動作しますが、コミット履歴は失われます。


履歴を失うことはそれほど悪いことではありませんが、リポジトリは私自身の作業(つまり、プライベート)のためのものなので、バージョン管理をしたくない、またはまだバージョン管理されていないものがたくさんあります。
ウィルロバートソン、

1

mainProject内でsecondProjectをマージするには:

A)secondProject

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

B)mainProjectで:

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

このブランチでは、実行する必要があるすべての重い変換を実行してコミットします。

C)次にマスターに戻り、2つのブランチ間の従来のマージ:

git checkout master
git merge secondProject

これにより、両方のgitプロジェクトのルートにあるすべてのファイルとフォルダーが1つのプロジェクトにマージされます。誰もがこれを実現したいとは思いません。
Clintm 2015

0

私もここに解決策を投げ入れます。基本的には、かなり単純なbashスクリプトラッパーです。git filter-branchです。他のソリューションと同様に、マスターブランチのみを移行し、タグは移行しません。ただし、完全なマスターコミット履歴は移行され、短いbashスクリプトであるため、ユーザーが比較的簡単に確認または調整できます。

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


0

このbashスクリプトは、sedタブ文字の問題(MacOSなど)とファイルの欠落の問題を回避します。

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

これは、の組み合わせですminiquarkマリウス・butucライアンさんの記事。彼らに乾杯!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.