18

マージされていないブランチからコードレビューを作成する必要があります。

解決策を見つける際には、ローカルブランチコンテキストの問題に進まないでください。これはサーバー上で実行されるためです。オリジンリモートだけがあり、他のコマンドの前に常にgit fetch originコマンドを実行します。ブランチについて話すときは、origin/branch-nameを参照します。

セットアップが単純で、マスターから発生した各ブランチが独自の方法で続行された場合、次のコマンドを実行できます。

git rev-list origin/branch-name --not origin/master --no-merges

マージされていないブランチごとに、結果のコミットをブランチごとの各レビューに追加します。

この問題は、2〜3のブランチ間でマージが行われ、それらの一部で作業が続行される場合に発生します。私が言ったように、ブランチごとにプログラムでコードレビューを作成したいのですが、複数のレビューにコミットを含めたくありません。

主に、コミットごとに元のブランチを見つける際の問題が軽減されます。または、簡単に言うと...おそらく作成され
たブランチによってグループ化されたすべてのマージされていないコミットを検索します。

簡単な例に焦点を当てましょう:

      *    b4 - branch2's head
   *  |    a4 - branch1's head
   |  *    b3
   *  |    merge branch2 into branch1
*  |\ |    m3 - master's head
|  * \|    a3
|  |  |
|  |  *    b2
|  *  |    merge master into branch1
* /|  |    m2
|/ |  *    merge branch1 into branch2
|  * /|    a2
|  |/ |
|  |  *    b1
|  | /
|  |/
| /|
|/ |
|  *       a1
* /        m1
|/
|
*          start

そして私が取得したいのは:

  • branch1:a1、a2、a3、a4
  • branch2:b1、b2、b3、b4

私がこれまでに見つけた最善の解決策は、実行することです。

git show-branch --topo-order --topics origin/master origin/branch1 origin/branch2

結果を解析します。

* [master] m3
 ! [branch1] a4
  ! [branch2] b4
---
  + [branch2] b4
  + [branch2^] b3
 +  [branch1] a4
 ++ [branch2~2] b2
 -- [branch2~3] Merge branch 'branch1' into branch2
 ++ [branch2~4] b1
 +  [branch1~2] a3
 +  [branch1~4] a2
 ++ [branch1~5] a1
*++ [branch2~5] m1

出力の解釈は次のようになります。

  1. 最初のn行は、分析されたn個のブランチです。
  2. ----の1行
  3. コミットがn番目のブランチにある場合は、コミットごとに1行で、n番目のインデント文字にプラス(マージコミットの場合はマイナス)が付きます。
  4. 最後の行は、分析されたすべてのブランチのマージベースです

ポイント3の場合、コミット名の解決はブランチ名で始まります。私が見る限り、このブランチは、おそらく最初の親によるパス到達を促進することによって、コミットが作成されたブランチに対応します。

マージコミットには興味がないので、無視します。

次に、各branch-path-commitを解析して、rev-parseでハッシュを取得します。

この状況にどのように対処できますか?

4

4 に答える 4

18

リポジトリのクローン--mirrorを作成して、元のリポジトリのミラーとして使用できるベアリポジトリを作成し、更新してgit remote update --pruneから、この機能のすべてのタグを削除することができます。

私はそれをこのように実装します:
1。マスターにマージされていないブランチのリストを取得します

git branch --no-merged master

2.ブランチごとに、マスターブランチではなく、そのブランチのリビジョンのリストを取得します

git rev-list branch1 --not master --no-merges

リストが空の場合は、ブランチのリストからブランチを削除します。3
.リビジョンごとに、元のブランチを次のように決定します。

git name-rev --name-only revisionHash1

の正規表現を一致させ^([^\~\^]*)([\~\^].*)?$ます。最初のパターンはブランチ名で、2番目のパターンはブランチへの相対パスです。
見つかったブランチ名が最初のブランチと等しくない場合は、リストからリビジョンを削除します。

最後に、ブランチのリストを取得し、それぞれについてコミットのリストを取得しました。


さらにいくつかのbash調査を行った後、次の方法ですべてを1行で実行できます。

git rev-list --all --not master --no-merges | xargs -L1 git name-rev | grep -oE '[0-9a-f]{40}\s[^\~\^]*'

結果は次の形式で出力されます

hash branch

読み取り、解析、順序付け、グループ化などが可能です。

于 2013-04-02T15:36:02.963 に答える
4

問題のあるスペースを把握している場合は、-sha1-nameを使用できると思います

git show-branch --topo-order --topics --sha1-name origin / master origin / branch1 origin / branch2

興味のあるものをリストするには、 git-what-branchを介してコミットを実行します

git-what-branch:コミットがどのブランチにあるか、またはコミットが名前付きブランチにどのように到達したかを検出します。これはSethRobertsonのPerlスクリプトです

ニーズに合わせてレポートをフォーマットしますか?

于 2013-03-28T15:26:20.210 に答える
4

この質問は指定が不十分であるため、正解はありません。

Git履歴は、単に有向非巡回グラフ(DAG)であり、ノードに十分なラベルが付けられていない限り、DAG内の2つの任意のノード間の意味関係を判別することは一般に不可能です。サンプルグラフのコミットメッセージが信頼性の高いマシン解析可能なパターンに従うことを保証できない限り、コミットには十分なラベルが付けられていません。追加のコンテキストがないと、関心のあるコミットを自動的に特定することはできません(たとえば、開発者が従うことを保証します)。特定のベストプラクティス)。

これが私の言いたいことの例です。a1コミットはに関連付けられていると言いbranch1ますが、サンプルグラフのノードを見ただけでは、これを確実に判断することはできません。かつて、リポジトリの履歴の例が次のようになっている可能性があります。

      *    merge branch1 into branch2 - branch2's head
      |\
     _|/
    / *    b1
   |  |
   |  |
  _|_/
 / |
|  *       a1
* /        m1
|/
|
*          start - master's head

branch1上のグラフにはまだ存在していないことに注意してください。上記のグラフは、次の一連のイベントから生じた可能性があります。

  1. branch2start共有リポジトリに作成されます
  2. ユーザー#1はa1自分のローカルbranch2ブランチで作成します
  3. その間、ユーザー#2は自分のローカルブランチを作成m1しますb1branch2
  4. ユーザー#1は自分のローカルbranch2ブランチを共有リポジトリにプッシュし、共有リポジトリ内のbranch2参照がポイントするようにしますa1
  5. ユーザー#2は自分のローカルbranch2ブランチを共有リポジトリにプッシュしようとしますが、これは非早送りエラーで失敗します(branch2現在、を指しているためa1、早送りできませんb1
  6. user#2が実行されgit pull、にマージa1されますb1
  7. user#2はgit commit --amend -m "merge branch1 into branch2"説明のつかない理由で実行されます
  8. 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識別子が出力されます(この順序で、--reverseifを使用します)。逆の順序が必要です)、git rev-list --first-parent --no-merges master..branch2コミットb4、b3、b2、およびb1のSHA1識別子を(ここでもこの順序で)出力します。

ブランチに明確な親子関係がある場合

開発者がベストプラクティスに従わず、ブランチにgit pull(または同等の操作)によって作成された愚かなマージが散らばっていても、親子ブランチの関係が明確な場合は、次のアルゴリズムを実行するスクリプトを作成するとうまくいく可能性があります。

  1. 親ブランチ、親の親ブランチ、親の親ブランチなどからのすべてのコミットを除いて、対象のブランチから到達可能なすべてのコミットを検索し、結果を保存します。例えば:

    git rev-list master..branch1 >commit-list
    
  2. 関心のあるブランチのすべての子、孫などのブランチに対して同じことを行います。たとえば、次branch2の子と見なされると仮定しbranch1ます。

    git rev-list ^master ^branch1 branch2 >commits-to-filter-out
    
  3. 手順1の結果から手順2の結果を除外します。例えば:

    grep -Fv -f commits-to-filter-out commit-list
    

このアプローチの問題は、子ブランチがその親にマージされると、子ブランチでの開発が継続されている場合でも、それらのコミットは親の一部であると見なされることです。これは意味的には理にかなっていますが、あなたが望む結果を生み出すことはありません。

いくつかのベストプラクティス

この特定の問題を将来解決しやすくするためのいくつかのベストプラクティスを次に示します。これらのすべてではないにしても、ほとんどは、共有リポジトリ内のフックを巧妙に使用することで実施できます。

  1. ブランチごとに1つのタスクのみ。複数のタスクは禁止されています。
  2. にマージされた後は、子ブランチでの開発の続行を許可しないでください。マージは、タスクが完了したことを意味します。ストーリーの終わりです。予想される質問への回答:
    • Q:子ブランチにバグを発見した場合はどうなりますか?A:親から新しいブランチを開始します。子ブランチで開発を続行しないでください。
    • Q:新機能がまだ完成していない場合はどうなりますか?A:では、なぜブランチをマージしたのですか?おそらく、完全なサブタスクをマージしました。その場合、残りのサブタスクは、親ブランチから離れた独自のブランチに移動する必要があります。子ブランチで開発を続行しないでください。
  3. の使用を禁止するgit pull
  4. すべての子ブランチがマージされていない限り、子ブランチを親にマージしてはなりません。
  5. ブランチに子ブランチがない場合は、とマージする前に、親ブランチにリベースすることを--no-ff検討してください。子ブランチがある場合でも、リベースできますが--no-ff、子ブランチのマージを保持してください(これは本来よりも注意が必要です)。
  6. 親ブランチを子ブランチに頻繁にマージして、マージの競合を簡単に解決できるようにします。
  7. 祖父母のブランチを孫のブランチに直接マージすることは避けてください。最初に子にマージしてから、子を孫にマージします。

すべての開発者がこれらのルールに従う場合、簡単です。

git rev-list --first-parent --no-merges parent-branch..child-branch

そのブランチで行われたコミットからその子ブランチで行われたコミットを差し引いたものを確認するために必要なのはこれだけです。

于 2013-03-30T14:49:47.870 に答える
3

私はあなたがそれを説明したような方法でそれをすることを提案します。しかし、私はの出力に取り組むgit log --format="%H:%P:%s" ^origin/master origin/branch1 origin/branch2ので、あなたはより良いツリーウォーキングを行うことができます。

  1. 出力から適切なツリー構造を構築し、親と子をマークします。
  2. 頭から歩き始めます(からSHAを取得しますgit rev-parse)。あなたが来た頭の名前とその距離ですべてのコミットをマークします。
    • 最初の親ではないステップ(マージの他の部分)の場合、距離に100を追加します。
    • マージコミットに遭遇した場合は、どのブランチがどのブランチにマージされたかについての説明を確認してください。2つの親リンクをたどるときに、この情報を使用します。解析するブランチの名前が現在のHEADと一致しない場合は、距離に10000を追加します。
    • 両親の両方のために:あなたは今彼らの名前を知っています。最初の親であるすべての子をdictに追加しますcommit -> known-name
  3. 既知の名前のコミットの口述を取り、ツリーを上って歩き始めます(親ではなく子供に向かって)。マージされたブランチからの距離から10000を減算します。このウォークを実行している間、最初の親ではないコミットに移動せず、分岐点(2つの子を持つコミット)に到達するとすぐに停止します。また、ブランチヘッドの1つに当たった場合も停止します。

これで、コミットごとに、ブランチヘッドまでの距離値(負の値になる可能性があります)のリストが表示されます。コミットごとに、距離が最小のブランチが、コミットが作成された可能性が最も高いブランチです。

時間がある場合は、履歴全体を調べてから、マスターの履歴を差し引くことをお勧めします。これにより、ブランチが以前にマスターにマージされている場合は、わずかに良い結果が得られる可能性があります。


抵抗できませんでした:私が説明したことを実行するPythonスクリプトを作成しました。しかし、1つの変更で、通常のステップごとに、距離は増加しませんが、減少します。これには、マージポイントの後で長生きしたブランチが優先されるという効果があります。これは個人的にはもっと好きです。ここにあります:https ://gist.github.com/Chronial/5275577

使用法:git-annotate-log.py ^origin/master origin/branch1 origin/branch2結果の品質をチェックするだけです(注釈付きのgitログツリーが出力されます)。

于 2013-03-30T03:47:44.863 に答える