Git-subtreeを使用して、リモートリポジトリのサブディレクトリを私のリポジトリのサブディレクトリに追加する方法はありますか?
私がこれを持っているとしましょうmainリポジトリ:
/
dir1
dir2
そしてこれライブラリリポジトリ:
/
libdir
some-file
some-file-to-be-ignored
library/libdirをmain/dir1にインポートして、次のようにします。
/
dir1
some-file
dir2
Git-subtreeを使用すると、--prefix
引数を使用してdir1にインポートするように指定できますが、内容のみを取得するように指定することもできますサブツリーの特定のディレクトリ?
Git-subtreeを使用する理由は、後で2つのリポジトリを同期できるためです。
私はこれを実験して、いくつかの部分的な解決策を見つけましたが、完全ではありません。
これらの例では、- https://github.com/git/git.git のcontrib/completion/
から4つのファイルをローカルリポジトリのthird_party/git_completion/
にマージすることを検討します。
これはおそらく私が見つけた最良の方法です。私は一方向のマージのみをテストしました。変更を上流のリポジトリに送り返そうとはしていません。
# Do this the first time:
$ git remote add -f -t master --no-tags gitgit https://github.com/git/git.git
# The next line is optional. Without it, the upstream commits get
# squashed; with it they will be included in your local history.
$ git merge -s ours --no-commit gitgit/master
# The trailing slash is important here!
$ git read-tree --prefix=third_party/git-completion/ -u gitgit/master:contrib/completion
$ git commit
# In future, you can merge in additional changes as follows:
# The next line is optional. Without it, the upstream commits get
# squashed; with it they will be included in your local history.
$ git merge -s ours --no-commit gitgit/master
# Replace the SHA1 below with the commit hash that you most recently
# merged in using this technique (i.e. the most recent commit on
# gitgit/master at the time).
$ git diff --color=never 53e53c7c81ce2c7c4cd45f95bc095b274cb28b76:contrib/completion gitgit/master:contrib/completion | git apply -3 --directory=third_party/git-completion
# Now fix any conflicts if you'd modified third_party/git-completion.
$ git commit
アップストリームリポジトリからマージした最新のコミットSHA1を覚えておくのは面倒なので、このBash関数を作成して、すべてのハードワークを実行します(gitログから取得)。
git-merge-subpath() {
local SQUASH
if [[ $1 == "--squash" ]]; then
SQUASH=1
shift
fi
if (( $# != 3 )); then
local PARAMS="[--squash] SOURCE_COMMIT SOURCE_PREFIX DEST_PREFIX"
echo "USAGE: ${FUNCNAME[0]} $PARAMS"
return 1
fi
# Friendly parameter names; strip any trailing slashes from prefixes.
local SOURCE_COMMIT="$1" SOURCE_PREFIX="${2%/}" DEST_PREFIX="${3%/}"
local SOURCE_SHA1
SOURCE_SHA1=$(git rev-parse --verify "$SOURCE_COMMIT^{commit}") || return 1
local OLD_SHA1
local GIT_ROOT=$(git rev-parse --show-toplevel)
if [[ -n "$(ls -A "$GIT_ROOT/$DEST_PREFIX" 2> /dev/null)" ]]; then
# OLD_SHA1 will remain empty if there is no match.
local RE="^${FUNCNAME[0]}: [0-9a-f]{40} $SOURCE_PREFIX $DEST_PREFIX\$"
OLD_SHA1=$(git log -1 --format=%b -E --grep="$RE" \
| grep --color=never -E "$RE" | tail -1 | awk '{print $2}')
fi
local OLD_TREEISH
if [[ -n $OLD_SHA1 ]]; then
OLD_TREEISH="$OLD_SHA1:$SOURCE_PREFIX"
else
# This is the first time git-merge-subpath is run, so diff against the
# empty commit instead of the last commit created by git-merge-subpath.
OLD_TREEISH=$(git hash-object -t tree /dev/null)
fi &&
if [[ -z $SQUASH ]]; then
git merge -s ours --no-commit "$SOURCE_COMMIT"
fi &&
git diff --color=never "$OLD_TREEISH" "$SOURCE_COMMIT:$SOURCE_PREFIX" \
| git apply -3 --directory="$DEST_PREFIX" || git mergetool
if (( $? == 1 )); then
echo "Uh-oh! Try cleaning up with |git reset --merge|."
else
git commit -em "Merge $SOURCE_COMMIT:$SOURCE_PREFIX/ to $DEST_PREFIX/
# Feel free to edit the title and body above, but make sure to keep the
# ${FUNCNAME[0]}: line below intact, so ${FUNCNAME[0]} can find it
# again when grepping git log.
${FUNCNAME[0]}: $SOURCE_SHA1 $SOURCE_PREFIX $DEST_PREFIX"
fi
}
次のように使用します。
# Do this the first time:
$ git remote add -f -t master --no-tags gitgit https://github.com/git/git.git
$ git-merge-subpath gitgit/master contrib/completion third_party/git-completion
# In future, you can merge in additional changes as follows:
$ git fetch gitgit
$ git-merge-subpath gitgit/master contrib/completion third_party/git-completion
# Now fix any conflicts if you'd modified third_party/git-completion.
マージされたファイルをローカルで変更するつもりがない場合、つまり、アップストリームからの最新バージョンでローカルサブディレクトリを常に上書きしたい場合は、git read-tree
を使用するのが同様ですがより簡単な方法です。
# Do this the first time:
$ git remote add -f -t master --no-tags gitgit https://github.com/git/git.git
# The next line is optional. Without it, the upstream commits get
# squashed; with it they will be included in your local history.
$ git merge -s ours --no-commit gitgit/master
$ git read-tree --prefix=third_party/git-completion/ -u gitgit/master:contrib/completion
$ git commit
# In future, you can *overwrite* with the latest changes as follows:
# As above, the next line is optional (affects squashing).
$ git merge -s ours --no-commit gitgit/master
$ git rm -rf third_party/git-completion
$ git read-tree --prefix=third_party/git-completion/ -u gitgit/master:contrib/completion
$ git commit
同様の手法を使用して(上書きせずに)マージできると主張している ブログ投稿 を見つけましたが、試してもうまくいきませんでした。
http://jrsmith3.github.io/merging-a-subdirectory-from-another-repo-via-git-subtree.html のおかげで、git subtree
を使用するソリューションを実際に見つけましたが、それは信じられないほど遅いです(以下の各git subtree split
コマンドは、デュアルXeon X5675での39000コミットで28 MBのリポジトリに対して9分かかりますが、他の解決策は1秒未満です)。
あなたが遅さで生きることができるなら、それは実行可能であるべきです:
# Do this the first time:
$ git remote add -f -t master --no-tags gitgit https://github.com/git/git.git
$ git checkout gitgit/master
$ git subtree split -P contrib/completion -b temporary-split-branch
$ git checkout master
$ git subtree add --squash -P third_party/git-completion temporary-split-branch
$ git branch -D temporary-split-branch
# In future, you can merge in additional changes as follows:
$ git checkout gitgit/master
$ git subtree split -P contrib/completion -b temporary-split-branch
$ git checkout master
$ git subtree merge --squash -P third_party/git-completion temporary-split-branch
# Now fix any conflicts if you'd modified third_party/git-completion.
$ git branch -D temporary-split-branch
たくさんのコミットでローカルリポジトリを汚染しないように--squash
を渡していますが、コミット履歴を保持したい場合は--squash
を削除できます。
--rejoin
を使用すると、後続の分割をより高速にできる可能性があります( https://stackoverflow.com/a/16139361/691281 を参照)-私はテストしていません。
OPは、アップストリームリポジトリのサブディレクトリをローカルリポジトリのサブディレクトリにマージすることを明確に述べています。ただし、代わりにアップストリームリポジトリ全体をローカルリポジトリのサブディレクトリにマージする場合は、よりシンプルで、クリーンで、より適切にサポートされている方法があります。
# Do this the first time:
$ git subtree add --squash --prefix=third_party/git https://github.com/git/git.git master
# In future, you can merge in additional changes as follows:
$ git subtree pull --squash --prefix=third_party/git https://github.com/git/git.git master
または、リポジトリURLの繰り返しを避けたい場合は、リモートとして追加できます。
# Do this the first time:
$ git remote add -f -t master --no-tags gitgit https://github.com/git/git.git
$ git subtree add --squash --prefix=third_party/git gitgit/master
# In future, you can merge in additional changes as follows:
$ git subtree pull --squash --prefix=third_party/git gitgit/master
# And you can Push changes back upstream as follows:
$ git subtree Push --prefix=third_party/git gitgit/master
# Or possibly (not sure what the difference is):
$ git subtree Push --squash --prefix=third_party/git gitgit/master
以下も参照してください。
関連する手法は git submodules ですが、それらには厄介な警告があります(たとえば、リポジトリを複製する人は、git clone --recursive
を呼び出さない限りサブモジュールを複製しません)。したがって、それらがサポートできるかどうかは調査しませんでしたサブパス。
編集: git-subtrac (以前のgit-subtreeの作者による)は、gitサブモジュールの問題のいくつかを解決するようです。したがって、これはアップストリームリポジトリ全体をサブディレクトリにマージするのに適したオプションですが、それでも、アップストリームリポジトリのサブディレクトリのみを含めることはサポートされていないようです。
Read-treeコマンドに:dirname
を追加することで、このようなことを行うことができました。 (私は実際には今週自分でgitとgit-subtreesを習得しようとしているだけで、svn:externalsを使用してSubversionでプロジェクトを作成したのと同じような環境をセットアップしようとしていることに注意してください-または私がここに示しているコマンドよりも簡単な方法...)
たとえば、上記の例の構造を使用して:
git remote add library_remote _URL_TO_LIBRARY_REPO_
git fetch library_remote
git checkout -b library_branch library_remote/master
git checkout master
git read-tree --prefix=dir1 -u library_branch:libdir