web-dev-qa-db-ja.com

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

XXXという名前のフォルダーにGitリポジトリがあり、_ yyy _という2番目のGitリポジトリがあります.

XXXリポジトリをYYYにインポートしたいZZZという名前のサブディレクトリとしてリポジトリを作成し、allXXXを追加の変更履歴は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)

これはできますか、それともサブモジュールを使うことに頼らなければなりませんか。

430
Vijay Patel

おそらく最も簡単な方法は、XXXのものをYYYの中のブランチに入れてから、それをmasterにマージすることです。 :

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

私は実際にちょうど私の2、3のレポジトリでこれを試してみました、そしてそれはうまくいきます。 Jörg'sanswer とは異なり、他のリポジトリを使い続けることはできませんが、それを指定したとは思いません。

注意:これはもともと2009年に書かれたので、gitは以下の答えで言及されているサブツリーマージを追加しました。私はおそらく今日この方法を使うでしょう、もちろんこの方法はまだうまくいきますが。

387
ebneter

あなたが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はマージを実行する前にルートがどこにあるかを自分自身で判断するので、その後のマージでプレフィックスを指定する必要はありません。

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

read-treeを使用してmerge -s oursステップをスキップするもう1つの答えの方法は、事実上cpを使用してファイルをコピーして結果をコミットすることと同じです。

元のソースは githubの "Subtree Merge"ヘルプ記事 からのものです。

348
ColinM

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

リビジョン<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
68
kynan

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

  • 常にGitの一部として開発されたかのように、Gitリポジトリに表示されます。
  • すべての履歴がそのまま保持され、
  • 古いリポジトリで独立して開発することができ、変更は単純にgit pulledされます。

電子メールには再現に必要な手順が含まれていますが、気弱な人向けではありません。最初に、LinuswroteGitであるため、おそらくあなたや私よりももう少し詳しく、そして、第二に、これはほぼ5年前であり、Gitはそれ以来かなり改善されています。より簡単に。

特に、最近では、特定の場合にgitkサブモジュールを使用すると思います。

47
Jörg W Mittag

そのための簡単な方法はgit format-patchを使うことです。

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

fooに含まれるもの:

  • foo.txt
  • .git

barは以下を含みます:

  • bar.txt
  • .git

bar履歴とこれらのファイルを含むfooで終わらせたいのです。

  • foo.txt
  • .git
  • 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/*

そして、すべてのメッセージコミットをbarから書き換えたいのなら、たとえばLinuxの場合は、

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

これは各コミットメッセージの先頭に "[bar]"を追加します。

12
Damien R.

この関数は、すべてのコミットをマージした後にリモートリポジトリをローカルリポジトリに複製します。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"

注意
パスはsedを介して置き換えられるため、マージ後に必ず正しいパスに移動してください。
--allow-unrelated-historiesパラメータはgit> = 2.9以降に存在します。

6
Andrey Izman

ベース この記事の上で 、サブツリーを使用することは私のために働いたものであり、適切な履歴だけが転送されました。誰かが手順を必要とする場合に備えて、ここに投稿してください(プレースホルダをあなたに適用可能な値に置き換えてください)。

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

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

あなたの行き先のリポジトリで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

6
Alex

私はこれが少し簡単だと思うので別の答えを追加します。 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-repositorygit Push --mirror url:repo_destはあなたのローカルレポの履歴と状態をリモートにプッシュします(url:repo_dest)。しかし、それはリモートの古い歴史と状態を削除します。楽しみが続きます! :-E

3
gaoithe

私の場合は、他のリポジトリ(XXX)からいくつかのファイルだけをインポートしたいと思いました。サブツリーは私にとっては複雑すぎ、他の解決策はうまくいきませんでした。これは私がしたことです:

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

これにより、インポートしたいファイル(ZZZ)に影響するすべてのコミットのリストが、スペースで区切られた順序で逆になります(名前の変更を取り込むには、--followを追加する必要があるかもしれません)。それから私はターゲットリポジトリ(YYY)に行き、もう一方のリポジトリ(XXX)をリモートとして追加し、そこから取得して、最後に:

git cherry-pick $ALL_COMMITS

これにより、すべてのコミットがブランチに追加されます。したがって、すべてのファイルがその履歴とともに保存され、あたかもこのリポジトリにいるかのように、必要なことをすべて実行できます。

1
Sebastian Blask

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

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

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

$ git branch -d B-master

その後、プッシュが変わります。

わたしにはできる。

0
VeLKerr

私はあなたの問題に対して別の解決策( git-submodules に代わるもの)を提案することができます - gil(git links)tool

それは複雑な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 linkを以下の形式で表しています。

  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

その結果、必要なすべてのプロジェクトを複製し、それらを適切な方法で互いにリンクさせることができます。

あるリポジトリ内のすべての変更を子リンクリポジトリ内のすべての変更と一緒にコミットしたい場合は、単一のコマンドでそれを実行できます。

gil commit -a -m "Some big update"

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

gil pull
gil Push

Gil(git links)ツールは以下のコマンドをサポートします。

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
chronoxor

私は-s theirsを探している状況にありましたが、もちろん、この戦略は存在しません。私の歴史は、私がGitHubでプロジェクトをフォークしたことです。そして今、どういうわけか、このブランチにはローカルな変更を加えていませんでしたが、ローカルなmasterupstream/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と同期しています(そして同様に同期させたい他のブランチに対しても上記を繰り返すことができます)。

0
tripleee