この質問は指定が不十分であるため、正解はありません。
Git履歴は、単に有向非巡回グラフ(DAG)であり、ノードに十分なラベルが付けられていない限り、DAG内の2つの任意のノード間の意味関係を判別することは一般に不可能です。サンプルグラフのコミットメッセージが信頼性の高いマシン解析可能なパターンに従うことを保証できない限り、コミットには十分なラベルが付けられていません。追加のコンテキストがないと、関心のあるコミットを自動的に特定することはできません(たとえば、開発者が従うことを保証します)。特定のベストプラクティス)。
これが私の言いたいことの例です。a1
コミットはに関連付けられていると言いbranch1
ますが、サンプルグラフのノードを見ただけでは、これを確実に判断することはできません。かつて、リポジトリの履歴の例が次のようになっている可能性があります。
* merge branch1 into branch2 - branch2's head
|\
_|/
/ * b1
| |
| |
_|_/
/ |
| * a1
* / m1
|/
|
* start - master's head
branch1
上のグラフにはまだ存在していないことに注意してください。上記のグラフは、次の一連のイベントから生じた可能性があります。
branch2
start
共有リポジトリに作成されます
- ユーザー#1は
a1
自分のローカルbranch2
ブランチで作成します
- その間、ユーザー#2は自分のローカルブランチを作成
m1
しますb1
branch2
- ユーザー#1は自分のローカル
branch2
ブランチを共有リポジトリにプッシュし、共有リポジトリ内のbranch2
参照がポイントするようにしますa1
- ユーザー#2は自分のローカル
branch2
ブランチを共有リポジトリにプッシュしようとしますが、これは非早送りエラーで失敗します(branch2
現在、を指しているためa1
、早送りできませんb1
)
- user#2が実行され
git pull
、にマージa1
されますb1
- user#2は
git commit --amend -m "merge branch1 into branch2"
説明のつかない理由で実行されます
- user#2がプッシュすると、共有リポジトリの履歴は上記のDAGのようになります
しばらくして、user#1はから作成branch1
してa1
を作成しa2
、user#2はに早送りでマージm1
してmaster
、次のコミット履歴を作成します。
* merge a1 into b1 - branch2's head
* |\ a2 - branch1's head
| _|/
|/ * b1
| |
| |
_|_/
/ |
| * a1
* / m1 - master's head
|/
|
* start
この一連のイベントが技術的に可能であるとすると(可能性は低いですが)、Gitはもちろん、どのコミットがどのブランチに「属している」かを人間がどのように判断できるでしょうか。
マージコミットメッセージの解析
ユーザーがマージコミットメッセージを変更しないこと(ユーザーは常にGitのデフォルトを受け入れる)を保証でき、Gitがデフォルトのマージコミットメッセージ形式を変更したことはなく、今後も変更しないことを保証できる場合は、マージコミットのコミットメッセージを手がかりとして使用できます。それはでa1
始まりましたbranch1
。コミットメッセージを解析するためのスクリプトを作成する必要があります。これを行うための単純なGitワンライナーはありません。
マージが常に意図的なものである場合
または、開発者がベストプラクティスに従っていて(各マージは意図的であり、異なる名前のブランチを取り込むことを目的としているため、によって作成された愚かなマージコミットgit pull
のないリポジトリになります)、完了した子からのコミットに関心がない場合ブランチの場合、関心のあるコミットは最初の親のパスにあります。分析しているブランチの親がどのブランチであるかがわかっている場合は、次の操作を実行できます。
git rev-list --first-parent --no-merges parent-branch-ref..branch-ref
このコマンドは、到達可能なコミットと子ブランチからマージされたコミットbranch-ref
を除外して到達可能なコミットのSHA1識別子を一覧表示します。parent-branch-ref
上記のグラフの例では、親の順序がマージコミットに入る行の順序ではなく注釈によって決定されると仮定すると、git rev-list --first-parent --no-merges master..branch1
コミットa4、a3、a2、およびa1のSHA1識別子が出力されます(この順序で、--reverse
ifを使用します)。逆の順序が必要です)、git rev-list --first-parent --no-merges master..branch2
コミットb4、b3、b2、およびb1のSHA1識別子を(ここでもこの順序で)出力します。
ブランチに明確な親子関係がある場合
開発者がベストプラクティスに従わず、ブランチにgit pull
(または同等の操作)によって作成された愚かなマージが散らばっていても、親子ブランチの関係が明確な場合は、次のアルゴリズムを実行するスクリプトを作成するとうまくいく可能性があります。
親ブランチ、親の親ブランチ、親の親ブランチなどからのすべてのコミットを除いて、対象のブランチから到達可能なすべてのコミットを検索し、結果を保存します。例えば:
git rev-list master..branch1 >commit-list
関心のあるブランチのすべての子、孫などのブランチに対して同じことを行います。たとえば、次branch2
の子と見なされると仮定しbranch1
ます。
git rev-list ^master ^branch1 branch2 >commits-to-filter-out
手順1の結果から手順2の結果を除外します。例えば:
grep -Fv -f commits-to-filter-out commit-list
このアプローチの問題は、子ブランチがその親にマージされると、子ブランチでの開発が継続されている場合でも、それらのコミットは親の一部であると見なされることです。これは意味的には理にかなっていますが、あなたが望む結果を生み出すことはありません。
いくつかのベストプラクティス
この特定の問題を将来解決しやすくするためのいくつかのベストプラクティスを次に示します。これらのすべてではないにしても、ほとんどは、共有リポジトリ内のフックを巧妙に使用することで実施できます。
- ブランチごとに1つのタスクのみ。複数のタスクは禁止されています。
- 親にマージされた後は、子ブランチでの開発の続行を許可しないでください。マージは、タスクが完了したことを意味します。ストーリーの終わりです。予想される質問への回答:
- Q:子ブランチにバグを発見した場合はどうなりますか?A:親から新しいブランチを開始します。子ブランチで開発を続行しないでください。
- Q:新機能がまだ完成していない場合はどうなりますか?A:では、なぜブランチをマージしたのですか?おそらく、完全なサブタスクをマージしました。その場合、残りのサブタスクは、親ブランチから離れた独自のブランチに移動する必要があります。子ブランチで開発を続行しないでください。
- の使用を禁止する
git pull
- すべての子ブランチがマージされていない限り、子ブランチを親にマージしてはなりません。
- ブランチに子ブランチがない場合は、とマージする前に、親ブランチにリベースすることを
--no-ff
検討してください。子ブランチがある場合でも、リベースできますが--no-ff
、子ブランチのマージを保持してください(これは本来よりも注意が必要です)。
- 親ブランチを子ブランチに頻繁にマージして、マージの競合を簡単に解決できるようにします。
- 祖父母のブランチを孫のブランチに直接マージすることは避けてください。最初に子にマージしてから、子を孫にマージします。
すべての開発者がこれらのルールに従う場合、簡単です。
git rev-list --first-parent --no-merges parent-branch..child-branch
そのブランチで行われたコミットからその子ブランチで行われたコミットを差し引いたものを確認するために必要なのはこれだけです。