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)
これはできますか、それともサブモジュールを使うことに頼らなければなりませんか。
おそらく最も簡単な方法は、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は以下の答えで言及されているサブツリーマージを追加しました。私はおそらく今日この方法を使うでしょう、もちろんこの方法はまだうまくいきますが。
あなたが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"ヘルプ記事 からのものです。
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
これのよく知られたインスタンスはGitリポジトリ自体にあり、Gitコミュニティでは「 最もクールなマージ 」(電子メールで使用される件名Linus Torvaldsの後)このマージについて説明しているGitメーリングリストへ)。この場合、gitk
Git GUIは現在Gitの一部であり、実際には別のプロジェクトとして使用されていました。 Linusは、そのリポジトリをGitリポジトリにマージすることができました。
git pull
edされます。電子メールには再現に必要な手順が含まれていますが、気弱な人向けではありません。最初に、LinuswroteGitであるため、おそらくあなたや私よりももう少し詳しく、そして、第二に、これはほぼ5年前であり、Gitはそれ以来かなり改善されています。より簡単に。
特に、最近では、特定の場合にgitkサブモジュールを使用すると思います。
そのための簡単な方法はgit format-patchを使うことです。
2つのgitリポジトリfooとbarがあるとします。
fooに含まれるもの:
barは以下を含みます:
bar履歴とこれらのファイルを含むfooで終わらせたいのです。
そうするために:
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]"を追加します。
この関数は、すべてのコミットをマージした後にリモートリポジトリをローカルリポジトリに複製します。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以降に存在します。
ベース この記事の上で 、サブツリーを使用することは私のために働いたものであり、適切な履歴だけが転送されました。誰かが手順を必要とする場合に備えて、ここに投稿してください(プレースホルダをあなたに適用可能な値に置き換えてください)。
ソースリポジトリでサブフォルダを新しいブランチに分割する
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
私はこれが少し簡単だと思うので別の答えを追加します。 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
私の場合は、他のリポジトリ(XXX)からいくつかのファイルだけをインポートしたいと思いました。サブツリーは私にとっては複雑すぎ、他の解決策はうまくいきませんでした。これは私がしたことです:
ALL_COMMITS=$(git log --reverse --pretty=format:%H -- ZZZ | tr '\n' ' ')
これにより、インポートしたいファイル(ZZZ)に影響するすべてのコミットのリストが、スペースで区切られた順序で逆になります(名前の変更を取り込むには、--followを追加する必要があるかもしれません)。それから私はターゲットリポジトリ(YYY)に行き、もう一方のリポジトリ(XXX)をリモートとして追加し、そこから取得して、最後に:
git cherry-pick $ALL_COMMITS
これにより、すべてのコミットがブランチに追加されます。したがって、すべてのファイルがその履歴とともに保存され、あたかもこのリポジトリにいるかのように、必要なことをすべて実行できます。
この記事 の基本的な例を参照し、そのようなリポジトリへのマッピングを検討してください。
A
<-> YYY
、B
<-> XXX
この章で説明されているすべての作業の後(マージ後)、ブランチB-master
を削除します。
$ git branch -d B-master
その後、プッシュが変わります。
わたしにはできる。
私はあなたの問題に対して別の解決策( 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を以下の形式で表しています。
最後に、ルートサンプルリポジトリを更新する必要があります。
# 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再帰的サブモジュール依存関係の問題 についてもっと詳しく.
私は-s theirs
を探している状況にありましたが、もちろん、この戦略は存在しません。私の歴史は、私がGitHubでプロジェクトをフォークしたことです。そして今、どういうわけか、このブランチにはローカルな変更を加えていませんでしたが、ローカルな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
と同期しています(そして同様に同期させたい他のブランチに対しても上記を繰り返すことができます)。