7

次の動作がgitでどのように良いかを理解するのに苦労しています。問題を説明するためにまとめた例については、以下を参照してください。多くの場合、私のチームと私は、私たちがそこに行きたくないブランチに変更/コミットを行っています。

> git init sandbox && cd sandbox
> echo "data a" > a.txt && echo "data b" > b.txt
> git add -A && git commit -a -m "initial population"
[master (root-commit) d7eb6af] initial population
 2 files changed, 2 insertions(+)
 create mode 100644 a.txt
 create mode 100644 b.txt
> git branch branch1
> echo "more data a" >> a.txt && git commit -a -m "changed a.txt on master"
[master 11eb82a] changed a.txt on master
 1 file changed, 1 insertion(+)
> git branch branch2 && git checkout branch2
Switched to branch 'branch2'
> echo "more data b" >> b.txt && git commit -a -m "changed b.txt on branch2"
[branch2 25b38db] changed b.txt on branch2
 1 file changed, 1 insertion(+)
> git checkout branch1
Switched to branch 'branch1'
> git merge branch2
Updating d7eb6af..25b38db
Fast-forward
 a.txt | 1 +
 b.txt | 1 +
 2 files changed, 2 insertions(+)

上記の点に注意してください。a.txtは、branch2で変更/変更されていなくても、マージで更新されます。上記のシナリオでは、gitはbranch2でa.txtが変更されていないことを認識し、したがって、branch1に更新を適用するときに、それらの変更を行わないことをインテリジェントに期待します。

私が間違っていることはありますか?はい、私はチェリーピックをすることができました。それは私が何を変更したかを知っているこの単純な例ではそうですが、変更がはるかに大きく、何が影響を受けたのかわからない実際の状況では現実的ではありません。

明確にするために、私はgitからこの動作を望んでいません。

4

3 に答える 3

3

上記のコマンドが完全かつ正しく入力されている場合、gitは正しいです。これがあなたがしたことです:

  1. リポジトリを作成しました(デフォルトでは「マスター」をブランチします)
  2. 「マスター」に1つのチェンジセット(2つの新しいファイル)を追加しました
  3. ブランチ( "branch1")を作成しましたが、変更しませんでした
  4. 「マスター」に1つのチェンジセットを追加(a.txtを変更)
  5. ブランチ( "branch2")を作成し、それに変更しました(このブランチには変更されたa.txtが含まれています)
  6. 「branch2」に1つのchageset(b.txtを変更)を追加しました
  7. 「branch1」に切り替えました(2つの元の変更されていないファイルが含まれています)
  8. 「branch2」とマージ(早送り)(両方の変更を適用:a.txtとb.txt)

これはまさにあなたが説明することであり、正確に何が起こるべきかです。おそらく間違っていたのは、「branch2」を作成する前に「master」で実際に変更したときに「branch1」のa.txtを変更していると考えていたため、マージ時に「master」から「branch1」に魔法のように変化するような錯覚を与えました。 「branch2」を使用しますが、実際には「branch2」から変更が加えられました。

テストを繰り返しますが、ステップ3で、a.txtへの変更を「master」にコミットする代わりに「branch1」git checkout -b branch1)に切り替えると、期待どおりのマージが得られると思います。

于 2013-01-22T04:06:54.703 に答える
3

「branch1」と「branch2」はコミットポインタに他なりません。これらは、特定の時点でのコミット履歴の状態です。そのため、「branch2」を「branch1」にマージする場合、gitは共通の祖先を確立し、両方のツリーからの変更を一緒に適用しようとするだけです。

簡単な図を見てください:

 branch1       E <- branch2
    |         /
    v        /
A - B - C - D <- master

上記の例では、「branch1」はコミットBを指し、「branch2」はコミットを指しEます。これは、多かれ少なかれ、上記で入力した操作の順序を表しています。'branch2'を'branch1'にマージすると、gitは共通の祖先を見つけて、 'branch1'の間にB存在するすべての履歴を適用します。具体的には、、、をコミットします。BECDE

ただし、必要なのはE。すでに特定したように、1つの(悪い)解決策はチェリーピッキングです。はるかに優れた解決策は、「branch2」を「branch1」にリベースし、それによって「branch2」の履歴を書き換えて、「branch1」を過ぎたコミットのみを含めることEです。

git rebase --onto branch1 master branch2

その結果、まさにあなたが求めているものが得られ、「元々マスターに基づいていたbranch2をbranch1にリベースする」と読み取られます。簡単にするために、この図から「branch1」ポインタを省略し、コミットハッシュが変更されたためにEなりました(これらの図の一般的な規則のように)。E'

       E' <- branch2
      /
     /
A - B - C - D <- master

で同様の効果を得ることができますgit checkout branch2 && git rebase -i B。その後、コミットCを削除Dし、インタラクティブなリベースセッションから削除します。

私の最後の仕事では、孤立した機能ブランチでこの問題に日常的に直面していました。同じ本番ブランチからさまざまな時点でカットされ、リベースせずにマージされた場合、不要な変更が反映されます。統合マネージャーとして、私は定期的に彼らの履歴を過去(最後の製品リリース)の共通点に書き直し、それによって完全にクリーンなマージを可能にしました。これは、考えられる多くのワークフローの1つです。最良の答えは、チームがコードをどのように移動するかに大きく依存します。たとえば、CI環境では、説明したようなマージと一緒にプルすることはそれほど重要ではない場合がありCますD

最後に、またはのEコードに依存している場合、このソリューションは、「branch1」(現在は変更セットを含む)を「master」にマージするときに履歴に大混乱をもたらすことに注意してください。ワークフローがインクリメンタルであり、「branch1」と「branch2」が同様の関数やファイルに干渉している場合、当然のことながらマージの競合が発生します。その場合、チームのワークフローを詳しく調べる必要があります。CDE'

于 2013-01-22T04:37:51.280 に答える
2

a.txtは、branch2で変更/変更されていなくても、マージで更新されます。

しかし、それはサーでした。すべてのコマンドを実行しますがgit merge branch2、その後

$ cat a.txt
data a

$ git checkout branch2
Switched to branch 'branch2'

$ cat a.txt
data a
more data a

そのようにbranch2にあることは理解していますが、分岐した後は変更/タッチしませんでした。

あなたはにコミットしmore data aましたmasterbranch2次に、から作成しましたmaster。したがって、同様branch2に含まmore data aれます。

于 2013-01-22T04:06:33.160 に答える