web-dev-qa-db-ja.com

チームでgitの競合を回避するにはどうすればよいですか?

私たちは同じプロジェクトに取り組む開発者であり、プロジェクトにはgitを使用しています。 2人以上が同じファイルで作業している場合、対処が難しいgitの競合が発生します。これらの競合が発生すると、1人の開発者が行った変更が失われることがあります。

チームでgitをどのように使用しますか? gitの競合を回避するための適切なフローは何ですか?

前もって感謝します。

42
Dev01

ワークフローを深く掘り下げる前に、コミュニケーションプロセスの再構築に少しの時間やお金を費やす前に、チームに3つの質問をしてください。

  1. 空白の規則を適用しますか?
  2. IDEを使用している場合、それらはすべて同じフォーマットとキーワード設定を使用していますか?たとえば、Eclipseは自動的に パラメータを確定 できます。
  3. テキストのビルドアーティファクトを生成しますか?たとえば、jsを縮小したり、.sassまたは.scssファイルからcssルールを生成したり、その場でxml構成を構築したりしますか?チェックインしますか?

何年にもわたって競合が発生し、他者が一元化されたワークフローでそれらを解決する手助けをした後、私はこれらの3つを提出して、私たちの集団的な紛争の痛みの大部分を引き起こします。

The causes of merge conflicts

上の画像では、「!」スライスは正当なマージの競合を表します。 vastひどいマージの大部分は、怠惰な空白の慣習や過度に積極的なIDE(または、場合によっては、過度にリファクタリングな開発者)によるものです。他に何かをする前に、IDE設定と空白の規則を標準化します。次に、ローカルリポジトリでこのコマンドを発行するように全員に指示します。

# Enable the repository's stock pre-commit hook
mv .git/hooks/pre-commit.sample .git/hooks/pre-commit

そのpre-commitフックは、git commitのバージョンを含むgit diff --checkを発行するたびに一連のチェックを実行します。これは、コミットされた変更によって空白エラーが発生するかどうかをチェックするコマンドです。コミットすると拒否されます。本当にそれを回避する必要がある場合は、git commit --no-verifyを発行できますが、できません。空白エラーは、バージョン管理の不発条例のようなものです。それらは、発生するのを待っているマージ競合です。

空白をクリーンアップする場合、インデントを改善するためにファイルをリファクタリングする場合、または純粋にフォーマットを変更する一連の大規模な変更を行う場合は、個別のコミットで行い、進行中の競合する作業を最初にチェックするようにチームに警告します。

コードレビューを実施する場合、すべてのレビュアーが最初に尋ねる質問を次のようにします。「この変更セットは、想定されていなかったものに触れましたか?書式設定をクリーンアップしましたか?メソッドを不必要にリファクタリングしますか?」失敗した場合、レビューは失敗します。または、できない場合は、それらの変更が論理的な変更(つまり、異なるコミット)から十分に分離されていることを確認して、履歴を有効にしてください。

スタイルシート言語、RequireJS、およびjs minifiersも、生成されたターゲットがチェックインされている場合に競合を引き起こします。これらのテクノロジーによって作成されたファイルはビルドアーティファクトです。 WARアーカイブはチェックインしません。 SASSでコンパイルされたCSSファイルをチェックインしないでください。または、必要に応じて、 .gitattributes file を使用してgitにバイナリとして処理させます。

これらすべてを行った後でもマージの競合が発生する場合は、ワークフローを改善してください。 Gary Fixlerの答えは完全に正しいです。合法的なマージの競合は、チームがプロジェクトの範囲についてうまくコミュニケーションできないか、うまくコミュニケーションできないために発生します。最初に、不十分に適用されているフォーマットルールを確認してください。

52
Christopher

正直なところ、適切なワークフローには適切なコミュニケーションと管理が必要です。チームメンバーは多くの場合が同じことに取り組んでいて、競合を引き起こすべきではありません。毎日のスタンドアップのようなことや、チームの各メンバーが何をしているのかを知っている気配りのあるマネージャーは、少なくとも一般的には、これを制限するために長い道のりを行くでしょう。

もちろん、それは製品の正確な性質に依存しますが、これはgitの問題よりも組織の問題です。実際、紛争は「良い」ものと見なされることがよくあります。なぜなら、彼らは人々を机から立ち上がらせたり、対立を起こした理由や今後の対応について話し合ったりするためです。これは、1人の人が1つの領域または一連のファイルを所有する必要があること、または少なくともそのセクションの変更について話し合うための連絡窓口であることを理解するチャンスです。

事前の計画を立てる以外に、gitの競合を回避する方法はありませんが、これはどのバージョン管理システムでも同じ問題になります。 2人が同じコードセクションを変更するときはいつでも、誰が勝つかを理解する必要があります。これが、ソリューションのバージョン管理者に目を向けるのではなく、チームの方法と実践に目を向けることが適切でない理由です。 2台の車が同時に交差点を通過することはできませんが、相互に通過できる車を発明することは非常に難しいため、一時停止標識や信号機の制御システムを発明しました。これは同様の問題です。競合しない同じものに2つの変更を実際に加えることはできないため、ファイルの操作方法を制御する必要があります。

あなたcould gitでのロックを許可するフロントエンドの1つを検討しますが、マージ不可能なファイルタイプ以外の概念には本当に同意しません。私は、より良いチームワークフローを理解する方が良いと思います。その間、これをファイルのマージで本当に上手になる機会として使用してください。私はそれをしました、そして今やそれを何ヶ月もした後、紛争は私にとって動揺することではありません。

14
Gary Fixler

競合を回避する方法は1つだけです。別の人が同じファイルを同時に編集しないようにしてください。基本的に、各ファイルには、すべての編集を担当し、所有権を別のファイルに渡すことができる所有者がいます。ファイルの所有権は、所有権が明確である限り、特定の機能/ブランチまたは日常に基づいて渡す​​ことができます。

各ファイルに1人の所有者を与えることができない場合は、次のようにします。

  • ファイルを1人の所有者に割り当てることができるより小さなファイルに分割する必要がある
  • gITの競合を解決する必要があります(すべての編集者が一緒に座って、個々の競合を解決します)。
  • 優れたマルチマージツールを使用して、競合を視覚化して解決します。
10
GoZoner

注文。

他にもあなたができるかもしれないいくつかのことが他にもあります。別々に投稿すればもっとわかりやすくなります。

新しいものを挿入する場所は、競合を作成するかどうかを判断するのに役立ちます。

従業員の名前のリストを想像してください

Andy, 
Oliver, 
Ivan,

次に、ブラッドとパトリックが参加し、それらの名前がリストに追加されます。あなたはブラッドを追加し、私はパトリックを追加します。名前をリストの一番下に追加し、次にgitを使用してリストをマージします。結果はgitユーザーにはおなじみでしょう:-

Merge branch 'Patrick' into Brad

Conflicts:
    names.txt

@@@ -1,4 -1,4 +1,8 @@@
  Andy, 
  Oliver, 
  Ivan,
  <<<<<<< HEAD
 +Brad,
  =======
+ Patrick,
  >>>>>>> Patrick

次に、同じことを行ったが、単純なアルファベット順の規則をリストに課したとします。さて、2つのブランチをマージするようになると、結果はもう少し楽しいものになります:-

Andy,
Ivan,
Oliver,

1つの名前を自分で追加し、他の人の変更をgitとマージして、他の名前を追加します。

Auto-merging names.txt
Merge made by the 'recursive' strategy.
 names.txt | 1 +
 1 file changed, 1 insertion(+)

そして私たちは

Andy,
Brad,
Ivan,
Oliver,
Patrick,

次に誰が会社に参加するかわからないため、ランダムにリストに効果的に追加しています。ランダムな場所に挿入することにより、ファイル内の場所の競合が発生する可能性が低くなります。

4
Ivan

追加されたブロック開始を使用

このようなソフトウェアでは...

function chess() 
{
    while (!end_of_game)
    {
        make_move()

左中かっこで始まるブロックの行は、単一の左中かっこで構成されるソフトウェアの他の行と簡単に混同されます。同じソフトウェアがこのように書かれている場合、ブロックを追加すると前の行に始まります...

function chess() {
    while (!end_of_game) {
        make_move()

私は個人的には好きではありませんが、Gitはそうです Gitに似ている行が少なく、相互に間違えられます。つまり、Gitは私たちが行うのと同じ方法で編集を認識しやすくなります、競合をより簡単に解決できます。

そして、ブロックエンディングについてのコメント

コメントを使用して、似たような線を区別できるようにします。

たくさんのJavaScriptとJSONを書くと、このような行がたくさんあるかもしれません。

            }
        }
    }
)

コメントを付けると、見分けやすくなります。

            }
        }
    }
) // end of weekdays()

そして

            }
        }
    }
) // end of weekend()

もはやgitと同じに見えません。これはgitが変更をよりよく理解するのに役立ちます。あなたが何かを追加した場合、

function a()
{
    ...
} // end of a()

gitは変更の単位としてそれを見る可能性が高く、次のようなものを追加したとは思わない

}

function a()
{
    ...

他の関数が終了する直前。これによって競合が防止されない場合でも、gitが変更を適切に確認して提示する場合(つまり、変更を精神的に表示する方法)、競合をより簡単に解決できます。関数が何をするか、それらが取るパラメーターなどをコメントする説明的なヘッダーは、gitが隣接する関数の内容を混同しないようにするのにさらに役立ちます。

3
Ivan

誰が何を編集しているかを気にせずにバージョン管理の競合の数を減らすには、小さな変更を加えて段階的にコミット/プッシュするだけです。問題を小さくすばやく変更できるようにファイルを細かく分割し、それらをトランクにプッシュします。ブランチの寿命が短いほど、マージの競合が発生する可能性が低くなります。

それでも、ある時点で、2人が自分の責任で同じファイルを同時に編集します。それが起こるときの明白な質問は次のとおりです:

"gitの競合を解決するためにどのようにして痛みを取り除くことができますか?"

答えは、トランクをプルしてブランチを頻繁にリベースする必要があることです。競合はまだ小さいのに、できるだけ早く競合に気づくでしょう。この時点で、両方の開発者は一緒に座って、変更が頭の中で新鮮な間、続行する最良の方法を落ち着いて話し合う必要があります。

このアプローチを、開発者が以前に行ったすべての変更の背後にある考え方を思い出すのに苦労している、リリース期限の直前のパニックで長命の巨大ブランチの競合を解決しようとするのと対照的です。

1
Will Sheppard

テキストファイルにはUTF-16ではなくUTF-8を使用します。

多くのバージョンのGit(つまり、私が知る限り、使用したすべてのバージョン)では、UTF-16ファイルをバイナリファイルとして扱うことができます。これは、一般的なUTF-16テキストファイルにゼロバイトが多数存在するためです。

Gitがファイルをバイナリファイルと見なすと、ファイルが変更された場合でも、ファイルをバイナリファイルとして扱い続ける可能性があります。これは、バージョン管理がファイルの完全なバージョンを保存することで行われることを意味し、バージョン管理システムのいくつかの利点が失われます。 (編集:いいえ。最近、UTF-16ファイルをUTF-8に変更してコミットし、編集して再度コミットすることでこれをテストしました。元のファイルと編集したファイルの両方がUTF-8になると、変更をテキスト変更として扱い始めました。 )

最近のほとんどのエディターは、ファイルの文字エンコードと行末スタイルを認識し、同じ形式でファイルを保存します。一部のエディター(Babelpadなど)では、ファイルをUTF-8で保存するか、UTF-16で保存するか、バイトオーダーマークを付けるか付けないかなどを選択できます。

バージョン管理するファイルが(i)UTF-16形式であり、(ii)UTF-8でも同じように機能する場合(たとえば、まともな最新のコンパイラのソースプログラムなど)、次のように変換することを検討する価値があります。 UTF-8。

最初のコミットの前にソーステキストがバイナリファイルであるとGitが判断した場合は、それを見て、エディターにロードして、Gitがテキストとして認識する別の形式で保存する価値があるかどうかを確認します。

(注:LinuxファイルはデフォルトでUTF-8になる傾向があります。一部のWindowsプログラムはUTF-16を作成する習慣がありました。そのため、問題が発生する可能性が最も高いのはWindowsユーザーです。また、非常に前にこれを修正する必要があることに注意してください。 Gitがバイナリファイルを持っていると信じる前に、ファイルの最初のコミット!)

enter image description here

Git rerereを使用する

記録された解像度を再利用してください!

https://git-scm.com/book/en/v2/Git-Tools-Rerere

1
Ivan

変更をプッシュしたときに同僚に通知する

競合の痛みを軽減する別の方法は、変更をプッシュしたときに同僚に通知することです。それは彼らがあなたの変更を引っ張って、そしてそこでいくつかの衝突を解決することを可能にします。どんな矛盾も、あなたの最近の変化と、あなたの心の中で新鮮であり、彼らが何に取り組んでいるのか、彼らの心の中で新鮮であるかのようです。

大規模な開発が終了するまでメインブランチから変更をプルせず、同じ領域にいる多数の人々による変更と競合する場合、解決は難しくなります。

Mergetoolを使用する

enter image description here

Gitの目的の1つはバージョン管理です。他のプログラムは、ファイルのマージと競合の解決を専門としています。 gitで使用するようにmergetoolを構成する場合、gitが競合と見なす多くの問題を自動的に解決するか、少なくとも調査して非常に良い推測を行い、問題がないように見えるか、ツールが信頼できる場合はそのままにしておくことができます。 。

これにより、解決のためにインテリジェントな決定を必要とする真の衝突が少なくなります。

小さいファイルを使用する

mycontrollers.jsの下部に新しいコントローラーを追加し、yourcontrollersの下部に新しいコントローラーを追加します.js:問題ありません。

allcontrollers.jsの下部に新しいコントローラーを追加します:競合。

(ただし、アルファベット順に並べることに関するアドバイスも覚えておいてください。Mで始まるmyNewController()はファイルの中央に移動し、Yで始まるyourNewController()は同じファイルの最後に移動する場合がありますが、やはり競合はありません。)

1
Ivan