1627

私はGitを、2つの並行する(ただし現在は実験的な)開発ブランチを持つ新しいプロジェクトで使用しています。

  • master:既存のコードベースのインポートに加えて、私が一般的に確信しているいくつかの変更
  • exp1:実験ブランチ#1
  • exp2:実験ブランチ#2

exp1そして、exp22つの非常に異なるアーキテクチャアプローチを表しています。さらに進むまで、どちらが機能するか(どちらかがあれば)を知る方法はありません。一方のブランチで進歩を遂げていると、もう一方のブランチで役立つ編集があり、それらだけをマージしたい場合があります。

他のすべてを残したまま、ある開発ブランチから別の開発ブランチへの選択的な変更をマージするための最良の方法は何ですか?

私が検討したアプローチ:

  1. git merge --no-commit続いて、ブランチ間で共通にしたくない多数の編集を手動でアンステージします。

  2. 共通ファイルを一時ディレクトリにgit checkout手動でコピーした後、他のブランチに移動し、さらに手動で一時ディレクトリから作業ツリーにコピーします。

  3. 上記のバリエーション。今のところブランチを放棄し、exp実験のために2つの追加のローカルリポジトリを使用します。これにより、ファイルの手動コピーがはるかに簡単になります。

これらの3つのアプローチはすべて、面倒でエラーが発生しやすいようです。より良いアプローチがあることを願っています。git-mergeより選択的になるフィルターパスパラメーターに似たもの。

4

28 に答える 28

1077

上記とまったく同じ問題がありました。しかし、答えを説明することで、これがより明確になりました。

概要:

  • マージしたいブランチからのパスをチェックアウトし、

     $ git checkout source_branch -- <paths>...
    
    Hint: It also works without `--` like seen in the linked post.
    
  • またはハンクを選択的にマージする

     $ git checkout -p source_branch -- <paths>...
    

または、reset を使用してから、オプション-pで追加します。

    $ git reset <paths>...
    $ git add -p <paths>...
  • 最後にコミット

     $ git commit -m "'Merge' these changes"
    
于 2009-08-31T05:53:03.230 に答える
532

1 つのブランチから個々のコミットを取得するには、 cherry-pickコマンドを使用します。

必要な変更が個々のコミットに含まれていない場合は、ここに示す方法を使用して、コミットを個々のコミットに分割します。大まかに言えば、git rebase -i元のコミットを編集するために使用し、git reset HEAD^変更を選択的に元に戻し、git commitそのビットを履歴の新しいコミットとしてコミットします。

ここRed Hat Magazine には、個別のファイルにさまざまな変更を分割したい場合に、ハンクの一部だけを追加する方法を使用するgit add --patchか、可能であれば別の優れた方法があります (そのページで「分割」を検索してください)。git add --interactive

変更を分割したら、必要なものだけをチェリー ピックできます。

于 2009-01-16T06:01:26.250 に答える
405

あるブランチから別のブランチにファイルを選択的にマージするには、次のコマンドを実行します。

git merge --no-ff --no-commit branchX

branchX現在のブランチにマージするブランチはどこにありますか。

この--no-commitオプションは、Gitによってマージされたファイルを、実際にコミットせずにステージングします。これにより、マージされたファイルを好きなように変更して、自分でコミットすることができます。

ファイルをマージする方法に応じて、次の4つのケースがあります。

1)真のマージが必要です。

この場合、Gitが自動的にマージした方法でマージされたファイルを受け入れてから、コミットします。

2)マージしたくないファイルがいくつかあります。

たとえば、現在のブランチのバージョンを保持し、マージ元のブランチのバージョンを無視したいとします。

現在のブランチのバージョンを選択するには、次のコマンドを実行します。

git checkout HEAD file1

file1これにより、現在のブランチののバージョンが取得さfile1れ、Gitによって自動マージされたものが上書きされます。

3)branchXのバージョンが必要な場合(真のマージではない場合)。

走る:

git checkout branchX file1

file1これにより、 inのバージョンが取得され、Gitによって自動マージされbranchXて上書きされます。file1

4)最後のケースは、で特定のマージのみを選択する場合ですfile1

この場合、変更file1を直接編集し、バージョンを変更したいものに更新してからfile1、コミットすることができます。

Gitがファイルを自動的にマージできない場合、ファイルは「マージされていない」と報告され、競合を手動で解決する必要があるコピーが生成されます。



例を使ってさらに説明するためbranchXに、現在のブランチにマージするとします。

git merge --no-ff --no-commit branchX

次に、git statusコマンドを実行して、変更されたファイルのステータスを表示します。

例えば:

git status

# On branch master
# Changes to be committed:
#
#       modified:   file1
#       modified:   file2
#       modified:   file3
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      file4
#

ここfile1で、、、file2およびfile3はgitが正常に自動マージしたファイルです。

これが意味するのは、これら3つのファイルすべての変更が、master競合branchXすることなく結合されているということです。

git diff --cached;を実行すると、マージがどのように行われたかを調べることができます。

git diff --cached file1
git diff --cached file2
git diff --cached file3

いくつかのマージが望ましくないことがわかった場合は、次のことができます

  1. ファイルを直接編集する
  2. 保存する
  3. git commit

マージせずfile1、現在のブランチのバージョンを保持したい場合

走る

git checkout HEAD file1

マージせずfile2、バージョンのみが必要な場合branchX

走る

git checkout branchX file2

自動的にマージする場合file3は、何もしないでください。

Gitはこの時点ですでにそれをマージしています。


file4上記はGitによるマージの失敗です。これは、同じ行で発生する両方のブランチに変更があることを意味します。ここで、競合を手動で解決する必要があります。ファイルを直接編集するか、目的のブランチのバージョンのcheckoutコマンドを実行することで、マージされたものを破棄できますfile4


最後に、忘れないでくださいgit commit

于 2011-09-03T08:48:40.487 に答える
114

私は上記のアプローチが好きではありません。チェリーピックを使用すると、単一の変更を選択するのに最適ですが、いくつかの悪い変更を除いてすべての変更を取り込みたい場合は面倒です。これが私のアプローチです。

--interactivegit merge に渡すことができる引数はありません。

代替案は次のとおりです。

ブランチの 'feature' にいくつかの変更があり、それらのすべてではなく一部をずさんな方法で 'master' に持ち込みたい (つまり、チェリーピックしてそれぞれをコミットしたくない)

git checkout feature
git checkout -b temp
git rebase -i master

# Above will drop you in an editor and pick the changes you want ala:
pick 7266df7 First change
pick 1b3f7df Another change
pick 5bbf56f Last change

# Rebase b44c147..5bbf56f onto b44c147
#
# Commands:
# pick = use commit
# edit = use commit, but stop for amending
# squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

git checkout master
git pull . temp
git branch -d temp

これをシェル スクリプトにラップし、master を $to に、feature を $from に変更すれば、準備完了です。

#!/bin/bash
# git-interactive-merge
from=$1
to=$2
git checkout $from
git checkout -b ${from}_tmp
git rebase -i $to
# Above will drop you in an editor and pick the changes you want
git checkout $to
git pull . ${from}_tmp
git branch -d ${from}_tmp
于 2009-05-21T03:00:21.950 に答える
106

別の方法があります。

git checkout -p

それは と の間の混合でgit checkoutありgit add -p、あなたが探しているものとまったく同じかもしれません:

   -p, --patch
       Interactively select hunks in the difference between the <tree-ish>
       (or the index, if unspecified) and the working tree. The chosen
       hunks are then applied in reverse to the working tree (and if a
       <tree-ish> was specified, the index).

       This means that you can use git checkout -p to selectively discard
       edits from your current working tree. See the “Interactive Mode”
       section of git-add(1) to learn how to operate the --patch mode.
于 2012-08-28T18:47:17.393 に答える
65

これらの答えのいくつかはかなり良いものですが、OPの元の制約である特定のブランチから特定のファイルを選択することに実際に答えたものはないように感じます。このソリューションはそれを行いますが、ファイルが多い場合は面倒な場合があります。

、、、およびブランチがあるmasterとします。各実験ブランチからの1つのファイルをマスターにマージします。私はこのようなことをします:exp1exp2

git checkout master
git checkout exp1 path/to/file_a
git checkout exp2 path/to/file_b

# Save these files as a stash
git stash

# Merge stash with master
git merge stash

これにより、必要なファイルごとにファイル内の差分が得られます。これ以上何もない。それ以下ではありません。バージョン間でファイルの変更が根本的に異なると便利です。私の場合、アプリケーションをRuby onRails2からRubyonRails3に変更します。

これはファイルをマージしますが、スマートマージを行います。このメソッドを使用してファイル内の差分情報を取得する方法を理解できませんでした(おそらく、極端な違いが発生する可能性があります。-s recursive -X ignore-all-spaceオプションを使用しない限り、空白などの厄介な小さなものがマージされます)

于 2011-08-25T01:31:22.263 に答える
54

1800INFORMATIONの答えは完全に正しいです。ただし、Git を初めて使用する人として、「git cherry-pick を使用する」だけでは、インターネットをもう少し掘り下げないと理解できませんでした。似たような船で。

私のユースケースは、他の誰かの GitHub ブランチから自分のブランチに変更を選択的に取り込みたいというものでした。変更を含むローカル ブランチが既にある場合は、手順 2 と 5 ~ 7 のみを実行する必要があります。

  1. 取り込みたい変更を含むローカル ブランチを作成します (作成されていない場合)。

    $ git branch mybranch <base branch>

  2. それに切り替えます。

    $ git checkout mybranch

  3. 他の人のアカウントから必要な変更をプルダウンします。まだ追加していない場合は、それらをリモートとして追加することをお勧めします。

    $ git remote add repos-w-changes <git url>

  4. ブランチからすべてをプルダウンします。

    $ git pull repos-w-changes branch-i-want

  5. コミット ログを表示して、必要な変更を確認します。

    $ git log

  6. 変更をプルしたいブランチに戻ります。

    $ git checkout originalbranch

  7. ハッシュを使用して、コミットを 1 つずつチェリー ピックします。

    $ git cherry-pick -x hash-of-commit

于 2009-05-07T11:38:51.973 に答える
44

Myclass.javaブランチ内のファイルをmasterブランチMyclass.java内に置き換える方法は次のとおりですfeature1Myclass.javaに存在しなくても機能しmasterます。

git checkout master
git checkout feature1 Myclass.java

これはマージではなく上書きされ、マスターブランチのローカルの変更は無視されることに注意してください。

于 2013-07-29T08:37:12.800 に答える
31

特定のファイルを別のブランチのファイルに置き換えるだけでなく、2 つのブランチの特定のファイルを実際にマージする簡単な方法。

ステップ 1: ブランチを比較する

git diff branch_b > my_patch_file.patch

現在のブランチと branch_b の違いのパッチ ファイルを作成します。

ステップ 2: パターンに一致するファイルにパッチを適用する

git apply -p1 --include=pattern/matching/the/path/to/file/or/folder my_patch_file.patch

オプションに関する注意事項

*インクルード パターンでワイルドカードとして使用できます。

スラッシュはエスケープする必要はありません。

また、代わりに --exclude を使用して、パターンに一致するファイルを除くすべてに適用するか、-R でパッチを元に戻すことができます

-p1 オプションは、*Unix パッチ コマンドからの名残りであり、パッチ ファイルの内容が各ファイル名の先頭にa/またはb/(パッチ ファイルの生成方法によってはそれ以上) を追加するという事実から、これらを取り除く必要があるため、理解できるようになります。パッチを適用する必要があるファイルへのパスに実際のファイルを出力します。

その他のオプションについては、git-apply の man ページを参照してください。

ステップ 3: ステップ 3 はありません

変更をコミットしたいのは明らかですが、コミットを行う前に、他の関連する調整を行う必要がないと誰が言いますか?

于 2012-02-27T22:42:08.593 に答える
25

より「単純な」マージによって不要な変更がさらに多く発生した場合でも、履歴を取得して、別のブランチからの2、3のファイルを最小限の手間で追跡する方法を次に示します。

まず、Gitが作業ディレクトリ内のファイルに対して何も行わずに、コミットしようとしているのはマージであることを事前に宣言するという珍しい手順を実行します。

git merge --no-ff --no-commit -s ours branchname1

...ここで、「ブランチ名」は、マージ元であると主張するものです。すぐにコミットした場合、変更はありませんが、他のブランチからの祖先が表示されます。必要に応じて、コマンドラインにブランチやタグなどを追加することもできます。ただし、この時点ではコミットする変更はないので、次に他のリビジョンからファイルを取得します。

git checkout branchname1 -- file1 file2 etc.

他の複数のブランチからマージする場合は、必要に応じて繰り返します。

git checkout branchname2 -- file3 file4 etc.

これで、他のブランチのファイルがインデックスに登録され、コミットする準備が整いました。

git commit

そして、あなたはそのコミットメッセージでやるべきことをたくさん説明するでしょう。

ただし、明確でない場合は、これはめちゃくちゃなことであることに注意してください。それは「ブランチ」の目的の精神ではありません。チェリーピックは、ここで行うことを行うためのより正直な方法です。前回持ち込まなかった同じブランチ上の他のファイルに対して別の「マージ」を実行したい場合は、「すでに最新です」というメッセージが表示されて停止します。これは、「from」ブランチが複数の異なるブランチである必要があるという点で、必要なときに分岐しないという症状です。

于 2012-09-27T02:55:45.240 に答える
18

これは、選択的なファイルをマージするための私のワークフローです。

# Make a new branch (this will be temporary)
git checkout -b newbranch

# Grab the changes
git merge --no-commit  featurebranch

# Unstage those changes
git reset HEAD
(You can now see the files from the merge are unstaged)

# Now you can chose which files are to be merged.
git add -p

# Remember to "git add" any new files you wish to keep
git commit
于 2010-02-18T04:28:19.650 に答える
17

最も簡単な方法は、リポジトリをマージしたいブランチに設定してから実行することです

git checkout [branch with file] [path to file you would like to merge]

あなたが実行する場合

git status

ファイルがすでにステージングされていることがわかります...

次に実行します

git commit -m "Merge changes on '[branch]' to [file]"

単純。

于 2013-10-29T20:54:31.263 に答える
16

git が「すぐに使える」便利なツールをまだ持っていないのは奇妙です。現在のバージョンのブランチからのバグ修正だけで、古いバージョンのブランチ (まだ多くのソフトウェア ユーザーがいる) を更新するときに、これを頻繁に使用します。この場合、多くの場合、トランク内のファイルから数行のコードのみをすばやく取得する必要があり、他の多くの変更 (古いバージョンには適用されないはずです) を無視します...そしてもちろん、インタラクティブな 3 者間マージこの場合は必要ですが、git checkout --patch <branch> <file path>この選択的マージの目的には使用できません。

簡単にできます:

グローバル ファイルまたはローカルファイル[alias]のセクションに次の行を追加するだけです。.gitconfig.git/config

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; /C/BCompare3/BCompare.exe $2.theirs $2 $2.base $2; rm -f $2.theirs; rm -f $2.base;' -"

Beyond Compare を使用することを意味します。必要に応じて、お好みのソフトウェアに変更してください。または、インタラクティブな選択的マージが必要ない場合は、3 方向の自動マージに変更できます。

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; git merge-file $2 $2.base $2.theirs; rm -f $2.theirs; rm -f $2.base;' -"

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

git mergetool-file <source branch> <file path>

これにより、他のブランチ内の任意のファイルだけを真に選択的にツリーウェイマージする機会が得られます。

于 2014-07-03T04:29:55.433 に答える
14

この投稿には最も簡単な答えが含まれていることがわかりました。単に行う:

git checkout <branch from which you want files> <file paths>

.gitignore ファイルを branchB から現在のブランチにプル:

git checkout branchB .gitignore

詳細については、投稿を参照してください。

于 2013-11-04T10:51:34.490 に答える
9

上記とまったく同じ問題がありました。しかし、この Git ブログでは、答えをより明確に説明していることがわかりました。

上記のリンクからのコマンド:

# You are in the branch you want to merge to
git checkout <branch_you_want_to_merge_from> <file_paths...>
于 2012-06-19T10:15:42.790 に答える
8

私はするだろう

git diff commit1..commit2 ファイルパターン | git-apply --index && git commit

このようにして、ブランチからのファイルパターンのコミットの範囲を制限できます。

Re:あるブランチから別のブランチに少数のファイルのみをプルする方法は?

于 2011-01-26T10:50:17.233 に答える
7

以前の「git-interactive-merge」の回答が気に入っていますが、もっと簡単な回答があります。Interactive と on のリベースの組み合わせを使用して、Git にこれを行わせます。

      A---C1---o---C2---o---o feature
     /
----o---o---o---o master

したがって、'feature' ブランチ (ブランチ ポイント 'A') から C1 と C2 が必要な場合がありますが、残りは今のところ必要ありません。

# git branch temp feature
# git checkout master
# git rebase -i --onto HEAD A temp

前の回答と同様に、対話型エディターに移動し、C1 と C2 の「選択」行を選択します (上記のように)。保存して終了すると、リベースが続行され、ブランチ「temp」と master + C1 + C2 の HEAD が表示されます。

      A---C1---o---C2---o---o feature
     /
----o---o---o---o-master--C1---C2 [HEAD, temp]

次に、master を HEAD に更新し、temp ブランチを削除するだけで準備完了です。

# git branch -f master HEAD
# git branch -d temp
于 2011-12-09T19:46:52.703 に答える
6

ディレクトリを部分的にマージするために、「pmerge」という独自のスクリプトを作成しました。これは進行中の作業であり、GitとBashの両方のスクリプトをまだ学習しています。

このコマンドはgit merge --no-commit、指定されたパスと一致しない変更を使用してから適用を解除します。

使用法:git pmerge branch path
例:git merge develop src/

私はそれを広範囲にテストしていません。作業ディレクトリには、コミットされていない変更や追跡されていないファイルがないようにする必要があります。

#!/bin/bash

E_BADARGS=65

if [ $# -ne 2 ]
then
    echo "Usage: `basename $0` branch path"
    exit $E_BADARGS
fi

git merge $1 --no-commit
IFS=$'\n'

# List of changes due to merge | replace nulls with newlines | strip lines to just filenames | ensure lines are unique
for f in $(git status --porcelain -z -uno | tr '\000' '\n' | sed -e 's/^[[:graph:]][[:space:]]\{1,\}//' | uniq); do
    [[ $f == $2* ]] && continue
    if git reset $f >/dev/null 2>&1; then
        # Reset failed... file was previously unversioned
        echo Deleting $f
        rm $f
    else
        echo Reverting $f
        git checkout -- $f >/dev/null 2>&1
    fi
done
unset IFS
于 2011-05-27T01:33:15.090 に答える