Gitでサブディレクトリをマージするにはどうすればよいですか?


84

サブディレクトリの変更のみをローカルGitブランチからリモートGitブランチにマージすることは可能ですか、それとも「オールオアナッシング」ですか?

たとえば、私は持っています:

branch-a
 - content-1
 - dir-1
   - content-2

そして

branch-b
 - content-1
 - dir-1
   - `content-2

branch-adir-1の内容をbranch-bdir-1の内容とマージしたいだけです。


1
:私は、これはの重複だと思う stackoverflow.com/questions/449541/...
カールVoigtland

回答:


81

SOの質問「選択したファイルをgit-mergeでどのようにマージしますか?」の代わりに、git read-treeに基づいてサブディレクトリ全体をマージするのにより適したこのGitHubスレッドを見つけました:

  • 私のリポジトリ=>cookbooks
    私のリポジトリのターゲットディレクトリ=>cookbooks/cassandra
  • リモートリポジトリ=>infochimps
    マージしたいリモートリポジトリソースcookbooks/cassandra=>infochimps/cookbooks/cassandra

これらをマージするために使用したコマンドは次のとおりです

  • リポジトリを追加してフェッチします
git remote add -f infochimps git://github.com/infochimps/cluster_chef.git
  • マージを実行します
git merge --allow-unrelated-histories -s ours --no-commit infochimps / master

(これは-s ours、ソースブランチからの変更を破棄する「ours」戦略()を使用してマージを実行します。これinfochimps/masterは、ターゲットブランチ内のファイルを実際に変更せずに、マージされたファクトを記録します)

  • マージのみinfochimps/cookbooks/cassandracassandra
git read-tree --prefix = cassandra / -u infochimps / master:cookbooks / cassandra

これにより、必要なソースサブディレクトリ、つまりソースリポジトリのcookbooks/cassandraアップストリームブランチのみのツリーが読み取られます。

ターゲットサブディレクトリ名もcookbooks/cassandra、である必要があることに注意してください。そうしないと、次のように表示されます。

fatal: Not a valid object name
  • 変更をコミットする
 git commit -m'infochimpscassandraにマージ '

補遺

それは奇妙です、[私を編集してください] —しかし、read-treeステップはおそらく次のように失敗する可能性があります:

error: Entry 'infochimps/cookbooks/cassandra/README' overlaps with 'cookbooks/cassandra/README'. Cannot bind.

...両方のファイルが同一の場合でも。これは役立つかもしれません:

git rm -r cassandra
git read-tree --prefix=cassandra/ -u infochimps/master:cookbooks/cassandra

しかしもちろん、これがあなたが望むことをすることを手動で確認してください。


3
@Martin git-scm.com/docs/git-rev-parse#_specifying_revisionsについて見て<rev>:<path>、例えばHEAD:README:READMEmaster:./README
VonC

6
git read-treeステップは私のために失敗しますerror: Entry 'foo/bar/baz.php' overlaps with 'bar/baz.php'. Cannot bind.
ウェストンRuter

1
@VonCですが、いいえ、履歴が必要です。その答えは、ツリー内のファイルにローカルな変更がある場合を考慮していません。したがって、マージする必要があります。
Weston Ruter 2013

私の場合、同じファイルでoverlaps with上記のエラーも表示されます。それはどれほど奇妙ですか?
ulidtko 2016年

1
@ChrisHalcrowマージされたファクトを記録しinfochimps/masterますが、ターゲットブランチのファイルを実際に変更することはありません。次のステップでgit read-tree --prefix=cassandra変更を行うためです。最後のコミットでは、実際の「マージ」コンテンツが記録されます。
vonC

29

私の例では、ブランチ 'source'とブランチ 'destination'があり、どちらも自身のアップストリームバージョンを反映し(ローカルのみの場合はそうではない)、最新のコードにプルされていると仮定します。'source'ブランチにのみ存在するnewFeatureというリポジトリのサブディレクトリが必要だとしましょう。

git checkout destination
git checkout source newFeature/
git commit -am "Merged the new feature from source to destination branch."
git pull --rebase
git push

それは私が見た他のすべてのものよりもかなり複雑ではなく、これは私にとって完璧に機能しました、ここにあります

これは「実際のマージ」ではないため、宛先ブランチにnewFeatureに関するコミット情報はなく、そのサブディレクトリ内のファイルへの変更のみが含まれることに注意してください。しかし、おそらくブランチ全体を後でマージするか、破棄するので、それは問題ではないかもしれません。


2
それは歴史を保存しますか?
Bibrak 2017年

2
@Bibrakこのアプローチは履歴を保存しません。少なくとも現在のコマンドではありません。
Ravi Gidwani 2018年

6

これはEclipseのフォーラムスレッドから入手しましたが、魅力のように機能しました。

git checkout source-branch
git checkout target-branch <directories-or-files-you-do-**NOT**-want> 
git commit
git checkout target-branch
git merge source-branch

1
これを試してみたところ、ターゲットブランチのソースブランチから不要なディレクトリが作成されました。一連のdirで指定したにもかかわらず、その2番目のコマンドでは必要ありませんでした。
マラソン

2
これはマージされず、フォルダをソースブランチのフォルダに置き換えます。
Gp2mv3

@ Gp2mv3しっかりしているように見えます。checkoutフォルダを同じにします=>違いはcheckoutフォルダを削除するだけです=>merge違いです。それは有効な戦略です。
穴居人

5

彼らは二つの枝を持っていますが、唯一の歴史をマージしたいOPのシナリオを考えるとDIR-1をから分岐への分岐B

# Make sure you are in the branch with the changes you want
git checkout branch-a

# Split the desired folder into its own temporary branch
# This replays all commits, so it could take a while
git subtree split -P dir-1 -b temp-branch

# Enter the branch where you want to merge the desired changes into
git checkout branch-b

# Merge the changes from the temporary branch
git subtree merge -P dir-1 temp-branch

# Handle any conflicts
git mergetool

# Commit
git commit -am "Merged dir-1 changes from branch-a"

# Delete temp-branch
git branch -d temp-branch

0

ブランチaとブランチbの両方を含むGitリポジトリを作成します。

git checkout branch-a
git diff branch-b dir-1 > a.diff
patch -R -p1 < a.diff

14
この回答にはさらに情報が必要です。これは実際のコードとコメントの違いは何ですか?
qodeninja 2013年

2
リクエスターはマージしたいと考えています。パッチを使用して変更を引き継ぐと、すべてのコミットが1つのパッチに自動的に押しつぶされ、履歴が失われます。
エリック

0

git cherry-pick必要なコミットを選択し、これらのコミットのみをマージするために使用します。ここでの重要な秘訣は、これらのコミットを簡単な方法で取得することです(Gitログを手動でチェックして手動で入力することでコミットを把握する必要がないようにするため)。方法は次のとおりです。次のようgit logに、コミットのSHA-1IDを出力するために使用します。

git log ^<commit-a> <commit-b> --pretty=format:"%h" --reverse -- <subdir>

'commit-a'は、マージするブランチの開始点の直前のコミットであり、 'commit-b'は、マージするブランチの最後のコミットです。'--reverse'は、後でチェリーピッキングを行うために、これらのコミットを逆の順序で出力します。

次に、次のようにします。

git cherry-pick $(git log ^<commit-a> <commit-b> --pretty=format:"%h" --reverse -- <subdir>)

シンプルで安定した2つのステップです!


別のブランチからの単一のディレクトリをマージして履歴を保持するための優れた唯一の方法。しかし...... 1つのコミットだけが必要な場合は、最初のコマンドのすべてのコミットからブランチを作成し、その新しいブランチを1つのスカッシュマージでマージします....
tom

1
これは、見つかったコミットが問題のディレクトリにのみ影響することを前提としています。コミットがマージしたいディレクトリとマージしたくないディレクトリの両方に影響を与える場合、これはあまりにも多くの選択を行います。
スコット

Two steps, simple and stable!まったく単純ではありません:)
Rafa19年

それで、あなたはそれが簡単だと思いますか?@Rafa
ロバート

@Robert私はあなたの答えが単純ではないことを意味するつもりはありませんでした。欠点はすべてオンgitであり、それはひどいインターフェースと謎とランダムな名前と意味でいっぱいのひどいメンタルモデルを持っています。
ラファ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.