既存のGitリポジトリを別のGitリポジトリにインポートする方法は?


476

XXXというフォルダーにGitリポジトリーがあり、YYYという2番目のGitリポジトリーがあります。

私は、インポートしたいXXXのにリポジトリをYYYの名前のサブディレクトリとしてリポジトリZZZと、すべての追加XXXへの変更履歴をYYY

以前のフォルダ構造:

├── XXX
│   ├── .git
│   └── (project files)
└── YYY
    ├── .git
    └── (project files)

後のフォルダー構造:

YYY
├── .git  <-- This now contains the change history from XXX
├──  ZZZ  <-- This was originally XXX
│    └── (project files)
└──  (project files)

これは可能ですか、それともサブモジュールを使用する必要がありますか?


2
Githubでは、新しいリポジトリを作成するときにWebインターフェイスからこれを実行できるようになりました
bgcode

回答:


430

おそらく最も簡単な方法は、XXXのものをYYYのブランチにプルしてから、それをマスターにマージすることです。

ではYYY

git remote add other /path/to/XXX
git fetch other
git checkout -b ZZZ other/master
mkdir ZZZ
git mv stuff ZZZ/stuff                      # repeat as necessary for each file/dir
git commit -m "Moved stuff to ZZZ"
git checkout master                
git merge ZZZ --allow-unrelated-histories   # should add ZZZ/ to master
git commit
git remote rm other
git branch -d ZZZ                           # to get rid of the extra branch before pushing
git push                                    # if you have a remote, that is

私は実際にいくつかのリポジトリでこれを試したところ、うまくいきました。Jörgの回答とは異なり、他のリポジトリを引き続き使用することはできませんが、とにかくそれを指定したとは思いません。

注:これはもともと2009年に作成されたため、gitは以下の回答で言及されているサブツリーのマージを追加しています。今日はおそらくこの方法を使用しますが、もちろんこの方法でも機能します。


1
ありがとう。私はあなたのテクニックを少し変更したバージョンを使用しました。私はXXXに「ステージング」ブランチを作成し、ZZZフォルダーを作成し、そこに「もの」を移動しました。次に、XXXをYYYにマージしました。
Vijay Patel、

1
これは私にとってはうまくいきました。私が行った唯一の変更は次のとおりです。1)この一時的なブランチをぶら下げたくなかったので、プッシュする前の「git branch -d ZZZ」。2) "git push"でエラーが発生しました: "共通の参照がなく、何も指定されていません。何もしていません。おそらく 'master'などのブランチを指定する必要があります。" (私がプッシュしていた起源は空のベアリポジトリでした。)しかし、 "git push --all"は大胆に動作しました。
CrazyPyro 2011

1
ZYZフォルダーとYYYリポジトリの履歴だけで終了したいと思いました。元のXXXリポジトリとYYYリポジトリのZZZブランチを削除したいと思いました。@CrazyPyroが履歴を削除することを提案したため、ZZZブランチを削除することがわかりました—削除する前に、ZZZブランチをマスターにマージしました。
Oli Studholme、2012年

4
@SebastianBlask私はこれを私のリポジトリの2つといじりました、そして何年もこれに賛成を得ていたにもかかわらず、誰もこれまで気づいていないように見えないステップがあることに気づきました。:-)私それをマスターにマージすることについて述べましたが、実際には表示しませんでした。今すぐ編集しています...
ebneter 2013

2
ファイルをサブフォルダーに移動するときに、次のようなものを追加できます。 git mv $(ls|grep -v <your foldername>) <your foldername>/ これにより、すべてのファイルとフォルダーが新しいフォルダーにコピーされます
serup

367

2番目のリポジトリの正確なコミット履歴を保持したいので、将来のアップストリームの変更を簡単にマージする機能も保持したい場合は、次の方法が必要です。その結果、サブツリーの変更されていない履歴がリポジトリにインポートされ、マージされたリポジトリをサブディレクトリに移動するための1つのマージコミットが発生します。

git remote add XXX_remote <path-or-url-to-XXX-repo>
git fetch XXX_remote
git merge -s ours --no-commit --allow-unrelated-histories XXX_remote/master
git read-tree --prefix=ZZZ/ -u XXX_remote/master
git commit -m "Imported XXX as a subtree."

次のように上流の変更を追跡できます。

git pull -s subtree XXX_remote master

Gitはマージを実行する前にルートがどこにあるかを独自に判断するため、後続のマージでプレフィックスを指定する必要はありません。

欠点は、マージされた歴史の中のファイルは(いないサブディレクトリに)接頭辞であるということです。その結果git log ZZZ/a、マージされた履歴の変更を除くすべての変更(ある場合)が表示されます。できるよ:

git log --follow -- a

ただし、マージされた履歴以外の変更は表示されません。

つまり、ZZZリポジトリののファイルを変更しない場合は、接頭辞のないパスXXXを指定する必要があります--follow。両方のリポジトリでそれらを変更した場合、2つのコマンドがあり、どのコマンドでもすべての変更が表示されません。

2.9より前のGitバージョン--allow-unrelated-historiesオプションをに渡す必要はありませんgit merge

ステップを使用read-treeしてスキップする他の回答の方法は、merge -s ourscpを使用してファイルをコピーし、結果をコミットすることと実質的に同じです。

元のソースはgithubの「Subtree Merge」ヘルプ記事からのものです。そして、もう一つの便利なリンク


9
これには履歴が保存されていないようです... git logプルしたファイルのいずれかを実行すると、単一のマージコミットが表示され、他のリポジトリの以前のライフから何も表示されません。Git 1.8.0
Anentropic

8
ああ!インポートされたファイルの古いパスを使用する場合、つまり、インポート先のサブディレクトリを省略すると、git logにコミット履歴が表示されます。たとえばgit log -- myfilegit log -- rack/myfile
Anentropic

2
@FrancescoFrassinelli、それは望ましいことではありませんか?この方法の特徴は、履歴を取り込むことです。
patrickvacek 2013

4
@FrancescoFrassinelli、あなたが歴史を望まないなら、なぜ通常のコピーをしないのですか?私は歴史のためにこの方法にあなたを引き付けるものを理解しようとしています-それが私がこの方法を使用した唯一の理由です!
patrickvacek 2013

7
Git 2.9以降で--allow-unrelated-historiesは、マージを行うときにオプションが必要です。
stuXnet

112

git-subtreeは、履歴を保持しながら複数のリポジトリを1つにマージする(および/またはサブツリーの履歴を分割する、この質問には無関係のようですが)まさにこのユースケース用に設計されたスクリプトです。リリース1.7.11以降、gitツリーの一部として配布されます。

<repo>リビジョンのリポジトリを<rev>サブディレクトリとしてマージする<prefix>にはgit subtree add、次のように使用します。

git subtree add -P <prefix> <repo> <rev>

git-subtree は、より使いやすい方法でサブツリーマージ戦略を実装します。

あなたの場合、リポジトリYYY内で、次を実行します。

git subtree add -P ZZZ /path/to/XXX.git master

欠点は、マージされた歴史の中のファイルは(いないサブディレクトリに)接頭辞であるということです。その結果git log ZZZ/a、マージされた履歴の変更を除くすべての変更(ある場合)が表示されます。できるよ:

git log --follow -- a

ただし、マージされた履歴以外の変更は表示されません。

つまり、ZZZリポジトリののファイルを変更しない場合は、接頭辞のないパスXXXを指定する必要があります--follow。両方のリポジトリでそれらを変更した場合、2つのコマンドがあり、どのコマンドでもすべての変更が表示されません。

詳細はこちら


4
ベアリポジトリまたはリモートの代わりにマージするディレクトリがある場合git subtree add -P name-of-desired-prefix ~/location/of/git/repo-without-.git branch-name
Tatsh

2
Noobの経験:git(バージョン2.9.0.windows.1)が「致命的:あいまいな引数 'HEAD':不明なリビジョンまたはパスが作業ツリーにありません」と、新しく初期化されたローカルのベアリポジトリでこれを試したところ、しかし、私が実際に新しいリポジトリを作成した後、つまり、プレーンファイルを追加して通常の方法でコミットした後は、問題なく動作しました。
Stein

私のシナリオでは美しく機能しました。
ジョニーUtahh

ああ、これは素晴らしいです。
dwjohnston

私は@Tatshの提案を使用し、それは私にとっては
うまくいっ

49

Gitリポジトリ自体には、このよく知られているインスタンスがあります。これは、Gitコミュニティで「これまでで最もクールなマージ」と総称されています(これについて説明するGitメーリングリストへの電子メールで使用されている件名のLinus Torvaldsの後)マージ)。この場合、gitk現在Git固有の一部であるGit GUIは、実際には別のプロジェクトでした。Linusは、そのリポジトリをGitリポジトリに次のようにマージすることに成功しました

  • Gitリポジトリでは、常にGitの一部として開発されたかのように見えます。
  • すべての歴史はそのまま維持され、
  • それでも、古いリポジトリで独立して開発でき、変更は単純にgit pull編集されます。

電子メールには再現に必要な手順が含まれていますが、心の弱い人には向いていません。最初に、Linus Gitを書いたので、おそらくあなたや私よりも少し詳しく知っています。次に、これはほぼ5年前です。それ以来、Gitは大幅に改善されたので、おそらくはるかに簡単になりました。

特に、今日では、特定のケースでgitkサブモジュールを使用すると思います。


3
ところで。後続のマージ(存在する場合)に使用される戦略はサブツリーマージと呼ばれ、git-subtreeこれを支援するサードパーティツールがあります。github.com
JakubNarębski09年

ありがとう、それを忘れてしまいました。subtree特にと一緒にマージ戦略、git-subtreeツールが素敵な、サブモジュールに多分優れた代替手段です。
イェルクWミッターク

12

これを行う簡単な方法は、git format-patchを使用することです。

2つのgitリポジトリfoobarがあると仮定します

fooに含まれるもの:

  • foo.txt
  • 。ギット

バーに含まれるもの:

  • bar.txt
  • 。ギット

そして、バーの履歴とこれらのファイルを含むfooで終了する必要があります。

  • foo.txt
  • 。ギット
  • foob​​ar / bar.txt

それを行うには:

 1. create a temporary directory eg PATH_YOU_WANT/patch-bar
 2. go in bar directory
 3. git format-patch --root HEAD --no-stat -o PATH_YOU_WANT/patch-bar --src-prefix=a/foobar/ --dst-prefix=b/foobar/
 4. go in foo directory
 5. git am PATH_YOU_WANT/patch-bar/*

そして、私たちができるバーからのすべてのメッセージコミットを書き直したいなら、例えばLinuxで:

git filter-branch --msg-filter 'sed "1s/^/\[bar\] /"' COMMIT_SHA1_OF_THE_PARENT_OF_THE_FIRST_BAR_COMMIT..HEAD

これにより、各コミットメッセージの先頭に「[bar]」が追加されます。


元のリポジトリにブランチとマージが含まれていた場合、git amおそらく失敗します。
Adam Monsen 2012

1
マイナーな落とし穴:git am [ ]はコミットメッセージから何かを取り除きます。したがって、別のマーカーを使用する必要があります[bar]
HRJ

うまくいきませんでした。「エラー:foobar / mySubDir / test_host1:はインデックスに存在しません。失敗したパッチのコピーは/home/myuser/src/proj/.git/rebase-apply/patchにあります。この問題を解決した場合。、実行"gitの午前--continue"これは(60のうちの)11個のパッチを適用した後だった。
oligofren

1
このブログには、多少異なる質問(選択したファイルのみを移動する)に対する同様の回答があります。
Jesse Glick 2014

欠点が1つあります。すべてのコミットがターゲットリポジトリのHEADに追加されます。
CSchulz 2014

8

この関数は、リモートリポジトリをローカルリポジトリディレクトリに複製します。すべてのコミットをマージすると、保存され、git log元のコミットと適切なパスが表示されます。

function git-add-repo
{
    repo="$1"
    dir="$(echo "$2" | sed 's/\/$//')"
    path="$(pwd)"

    tmp="$(mktemp -d)"
    remote="$(echo "$tmp" | sed 's/\///g'| sed 's/\./_/g')"

    git clone "$repo" "$tmp"
    cd "$tmp"

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

    cd "$path"
    git remote add -f "$remote" "file://$tmp/.git"
    git pull "$remote/master"
    git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
    git remote remove "$remote"
    rm -rf "$tmp"
}

使い方:

cd current/package
git-add-repo https://github.com/example/example dir/to/save

少し変更すれば、マージされたリポジトリのファイル/ディレクトリを別のパスに移動することもできます。たとえば、次のようにします。

repo="https://github.com/example/example"
path="$(pwd)"

tmp="$(mktemp -d)"
remote="$(echo "$tmp" | sed 's/\///g' | sed 's/\./_/g')"

git clone "$repo" "$tmp"
cd "$tmp"

GIT_ADD_STORED=""

function git-mv-store
{
    from="$(echo "$1" | sed 's/\./\\./')"
    to="$(echo "$2" | sed 's/\./\\./')"

    GIT_ADD_STORED+='s,\t'"$from"',\t'"$to"',;'
}

# NOTICE! This paths used for example! Use yours instead!
git-mv-store 'public/index.php' 'public/admin.php'
git-mv-store 'public/data' 'public/x/_data'
git-mv-store 'public/.htaccess' '.htaccess'
git-mv-store 'core/config' 'config/config'
git-mv-store 'core/defines.php' 'defines/defines.php'
git-mv-store 'README.md' 'doc/README.md'
git-mv-store '.gitignore' 'unneeded/.gitignore'

git filter-branch --index-filter '
    git ls-files -s |
    sed "'"$GIT_ADD_STORED"'" |
    GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
    mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
' HEAD

GIT_ADD_STORED=""

cd "$path"
git remote add -f "$remote" "file://$tmp/.git"
git pull "$remote/master"
git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
git remote remove "$remote"
rm -rf "$tmp"

注意
パスはvia sedに置き換わるので、マージ後に適切なパスに移動するようにしてください。パラメータは、唯一のgit> = 2.9以降が存在します。
--allow-unrelated-histories


2
OS X gnu-sedをご利用の場合は、インストールしてgit-add-repo関数を機能させてください。アンドレイ、ありがとう!
ptaylor 2017

7

この記事に基づくに、私にとってサブツリーを使用することがうまくいき、該当する履歴のみが転送されました。誰かが手順を必要とする場合に備えて、ここに投稿します(プレースホルダーを適切な値に置き換えてください):

ソースリポジトリのサブフォルダーを新しいブランチに分割

git subtree split --prefix=<source-path-to-merge> -b subtree-split-result

宛先リポジトリのスプリット結果ブランチのマージ

git remote add merge-source-repo <path-to-your-source-repository>
git fetch merge-source-repo
git merge -s ours --no-commit merge-source-repo/subtree-split-result
git read-tree --prefix=<destination-path-to-merge-into> -u merge-source-repo/subtree-split-result

変更を確認してコミット

git status
git commit

することを忘れないでください

subtree-split-resultブランチを削除してクリーンアップ

git branch -D subtree-split-result

ソースリポジトリからデータを取得するために追加したリモートを削除します

git remote rm merge-source-repo


3

これは少し簡単だと思うので、別の答えを追加します。repo_destのプルがrepo_to_importに行われ、次にpush --set-upstream url:repo_destマスターが行われます。

この方法は、いくつかの小さなリポジトリを大きなリポジトリにインポートするのに役立ちました。

インポートする方法:repo1_to_importからrepo_destへ

# checkout your repo1_to_import if you don't have it already 
git clone url:repo1_to_import repo1_to_import
cd repo1_to_import

# now. pull all of repo_dest
git pull url:repo_dest
ls 
git status # shows Your branch is ahead of 'origin/master' by xx commits.
# now push to repo_dest
git push --set-upstream url:repo_dest master

# repeat for other repositories you want to import

インポートを行う前に、ファイルとディレクトリを名前変更または移動して、元のリポジトリの目的の位置に移動します。例えば

cd repo1_to_import
mkdir topDir
git add topDir
git mv this that and the other topDir/
git commit -m"move things into topDir in preparation for exporting into new repo"
# now do the pull and push to import

次のリンクで説明されている方法は、この答えに影響を与えました。シンプルに思えたので気に入った。しかし注意してください!ドラゴンがいる!https://help.github.com/articles/importing-an-external-git-repositoryは git push --mirror url:repo_dest、ローカルリポジトリの履歴と状態をリモートにプッシュします(url:repo_dest)。しかし、リモートの古い履歴と状態を削除します。楽しい結果が続きます!:-E


1

私の場合、他のリポジトリ(XXX)から一部のファイルのみをインポートしたいと思いました。サブツリーは私には複雑すぎるため、他のソリューションは機能しませんでした。これは私がやったことです:

ALL_COMMITS=$(git log --reverse --pretty=format:%H -- ZZZ | tr '\n' ' ')

これにより、インポートしたいファイル(ZZZ)に影響するすべてのコミットのスペース区切りリストが逆の順序で表示されます(名前変更をキャプチャするために--followを追加する必要がある場合もあります)。次に、ターゲットリポジトリ(YYY)に移動し、他のリポジトリ(XXX)をリモートとして追加し、そこからフェッチし、最後に次のようにしました。

git cherry-pick $ALL_COMMITS

これにより、すべてのコミットがブランチに追加されます。これにより、すべてのファイルに履歴が付けられ、常にこのリポジトリにあるかのように、ファイルを使用して好きなことができます。


1

この記事の基本的な例を参照して、リポジトリーでのそのようなマッピングを検討してください

  • A<-> YYY
  • B <-> XXX

この章で説明されているすべてのアクティビティの後(マージ後)、ブランチを削除しB-masterます。

$ git branch -d B-master

次に、変更をプッシュします。

わたしにはできる。


0

探して-s theirsいた状況でしたが、もちろんこの戦略は存在しません。私の歴史は、GitHubでプロジェクトをforkしていたmasterことでしたが、なんらかの理由で、私のローカルをupstream/masterこのブランチにローカルの変更を加えていなかったにもかかわらず。(そこで何が起こったのか本当にわかりません-おそらく、上流は裏でいくつかの汚いプッシュをしたのでしょうね?)

私がやったことは

# as per https://help.github.com/articles/syncing-a-fork/
git fetch upstream
git checkout master
git merge upstream/master
....
# Lots of conflicts, ended up just abandonging this approach
git reset --hard   # Ditch failed merge
git checkout upstream/master
# Now in detached state
git branch -d master # !
git checkout -b master   # create new master from upstream/master

これで、私masterは再び同期しupstream/masterています(同様に同期したい他のブランチについても上記を繰り返すことができます)。


1
git reset --hard upstream/masterあなたの地元のmaster支店のA は仕事をします。これにより、デフォルトのアップストリームなど、ローカルのブランチ構成を失うことがなくなります。
tomekwi 2017年

0

私はあなたの問題に別の解決策(git-submodulesの代わり)を提案できます-gil(gitリンク)ツール

複雑なgitリポジトリの依存関係を記述および管理できます。

また、git再帰サブモジュールの依存関係の問題に対する解決策も提供します。

次のプロジェクト依存関係があると考えてください: サンプルgitリポジトリ依存関係グラフ

次に.gitlinks、リポジトリ関係の説明を含むファイルを定義できます。

# Projects
CppBenchmark CppBenchmark https://github.com/chronoxor/CppBenchmark.git master
CppCommon CppCommon https://github.com/chronoxor/CppCommon.git master
CppLogging CppLogging https://github.com/chronoxor/CppLogging.git master

# Modules
Catch2 modules/Catch2 https://github.com/catchorg/Catch2.git master
cpp-optparse modules/cpp-optparse https://github.com/weisslj/cpp-optparse.git master
fmt modules/fmt https://github.com/fmtlib/fmt.git master
HdrHistogram modules/HdrHistogram https://github.com/HdrHistogram/HdrHistogram_c.git master
zlib modules/zlib https://github.com/madler/zlib.git master

# Scripts
build scripts/build https://github.com/chronoxor/CppBuildScripts.git master
cmake scripts/cmake https://github.com/chronoxor/CppCMakeScripts.git master

各行は、次の形式でgitリンクを示しています。

  1. リポジトリの一意の名前
  2. リポジトリの相対パス(.gitlinksファイルのパスから開始)
  3. チェックアウトするgit cloneコマンドリポジトリブランチで使用されるGitリポジトリ
  4. 空行または#で始まる行は解析されません(コメントとして扱われます)。

最後に、ルートサンプルリポジトリを更新する必要があります。

# Clone and link all git links dependencies from .gitlinks file
gil clone
gil link

# The same result with a single command
gil update

その結果、必要なすべてのプロジェクトを複製し、適切な方法で相互にリンクします。

いくつかのリポジトリのすべての変更を子リンクされたリポジトリのすべての変更とともにコミットする場合は、1つのコマンドで実行できます。

gil commit -a -m "Some big update"

プル、プッシュコマンドは同様に機能します。

gil pull
gil push

Gil(gitリンク)ツールは、次のコマンドをサポートしています。

usage: gil command arguments
Supported commands:
    help - show this help
    context - command will show the current git link context of the current directory
    clone - clone all repositories that are missed in the current context
    link - link all repositories that are missed in the current context
    update - clone and link in a single operation
    pull - pull all repositories in the current directory
    push - push all repositories in the current directory
    commit - commit all repositories in the current directory

詳細をgitの再帰的なサブモジュールの依存関係の問題


0

私は名前を使用してみましょうa(の代わりにXXXZZZ)とbの代わりに(YYY)します。これにより、説明が少し読みやすくなります。

リポジトリをマージしたいと言うab(私は彼らが互いに並んで位置していると仮定しています):

cd a
git filter-repo --to-subdirectory-filter a
cd ..
cd b
git remote add a ../a
git fetch a
git merge --allow-unrelated-histories a/master
git remote remove a

このためには必要git-filter-repo(インストールfilter-branchされてがっかり)。

2つの大きなリポジトリをマージし、そのうちの1つをサブディレクトリに配置する例:https : //gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731

詳細はこちら


-1

簡単な方法は知りません。あなたはこれを行うことができます:

  1. git filter-branchを使用して、XXXリポジトリにZZZスーパーディレクトリを追加します
  2. 新しいブランチをYYYリポジトリにプッシュする
  3. プッシュされたブランチをYYYのトランクにマージします。

魅力的に聞こえる場合は、詳細を編集できます。


-2

これは「git mv」と「git pull」を使用して実行できると思います。

私は公正なgit noobです。メインリポジトリに注意してください。ただし、これを一時ディレクトリで試したところ、うまくいくようです。

最初に、XXXの構造の名前を変更して、YYY内にある場合の外観を一致させます。

cd XXX
mkdir tmp
git mv ZZZ tmp/ZZZ
git mv tmp ZZZ

XXXは次のようになります。

XXX
 |- ZZZ
     |- ZZZ

ここで 'git pull'を使用して、変更をフェッチします。

cd ../YYY
git pull ../XXX

YYYは次のようになります。

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