web-dev-qa-db-ja.com

コミットがマージされたGit bisect

私はこのような歴史を持っています:

* 3830e61 Add data escaping.              (Bad)
* 0f5e148 Improve function for getting page template.
*   aaf8dc5 Merge branch 'navigation'
|\
| * 3e667f8 Add icons.
| * 43a07b1 Add menu styles.              (Breaks)
| * 107ca95 Add Responsive Nav.           (Good)
* | ea3d736 Add ‘Admin’ notice.
* | 17ca0bb Update placeholder text.
|/
* f52cc34 Add featured image.
* 2abd954 Style placeholders.

詳細とgit bisectを学習しようとしていますが、この履歴に問題があります。 107ca95は良いことであり、3830e61は悪いことです。 git bisectを実行すると、コミット107ca95..3e667f8が無視されます。 43a07b1は回帰を導入したコミットであることを偶然知っていますが、評価されません。

ここに私が大体行ったことがあります:

git checkout master
git bisect start
git bisect bad
git bisect good 107ca95
git bisect bad (multiple times)

私が何をしても、107ca95..3e667f8はテストのためにチェックアウトされることはありません。

これらのコミットをテストするためにバイセクト中に履歴を本質的に「フラット化」できる方法はありますか?インタラクティブなrebaseを使用して履歴を平坦化できることはわかっていますが、その必要はありません。

41
tollmanz

これは非常に古い質問ですが、未回答の質問です。調査することにしましたが、Gitの動作が質問の内容とは異なることを示すことができることがわかりました。 1つの説明は、Gitがbisectのアルゴリズムを改善したこと、または質問者がコミットのマーク付けを間違えたことです。

私はgit bisectについてもっと学びたいと思っていますが、この歴史に問題があります。そんなこと知ってる 107ca95は良好で、3830e61 悪い。 git bisectを実行すると、107ca95..3e667f8は無視されます。たまたま43a07b1は回帰を導入したコミットですが、評価されることはありません

評価されたかどうかを確認するコードを書きました。私のテストは、それが評価されていることを示しています。以下のコードを実行して、メッセージAdd menu styles.が表示されます。

その他のコメント:

  • 「コミット107ca95..3e667f8は無視されます ":gitがすでにそれが適切であることを認識しているため、" good "としてマークしたコミットは評価されないことに注意してください。
  • Christian Couderによるこの記事 の「二分割アルゴリズム」のセクションを読んでください。また、「マージベースの確認」のセクションが関連する場合があります。
  • 上記のように、質問は確かに私が使用したものとは異なるバージョンを使用していました(質問は2013年から、Git 2.11は2016年からです)。

バイセクト実行出力

  • 最初に「管理者通知を追加」がチェックされていることに注意してください(4行目)。 (上記の記事から「マージベースの確認」を読んでください。)
  • それ以降、予想どおり線形履歴を二等分します。
# bad: [d7761d6f146eaca1d886f793ced4315539326866] Add data escaping. (Bad)
# good: [f555d9063a25a20a6ec7c3b0c0504ffe0a997e98] Add Responsive Nav. (Good)
git bisect start 'd7761d6f146eaca1d886f793ced4315539326866' 'f555d9063a25a20a6ec7c3b0c0504ffe0a997e98'
# good: [1b3b7f4952732fec0c68a37d5f313d6f4219e4ae] Add ‘Admin’ notice. (Good)
git bisect good 1b3b7f4952732fec0c68a37d5f313d6f4219e4ae
# bad: [f9a65fe9e6cde4358e5b8ef7569332abfb07675e] Add icons. (Bad)
git bisect bad f9a65fe9e6cde4358e5b8ef7569332abfb07675e
# bad: [165b8a6e5137c40ce8b90911e59d7ec8eec30f46] Add menu styles. (Bad)
git bisect bad 165b8a6e5137c40ce8b90911e59d7ec8eec30f46
# first bad commit: [165b8a6e5137c40ce8b90911e59d7ec8eec30f46] Add menu styles. (Bad)

コード

Python 3、Git 2.11.0で実行します。実行するコマンド:python3 script.py

""" The following code creates a git repository in '/tmp/git-repo' and populates
it with the following commit graph. Each commit has a test.sh which can be used
as input to a git-bisect-run.

The code then tries to find the breaking change automatically.
And prints out the git bisect log.

Written in response to http://stackoverflow.com/questions/17267816/git-bisect-with-merged-commits
to test the claim that '107ca95..3e667f8 are never checked out'.

Needs Python 3!
"""


from itertools import chain
import os.path
import os
import sh

repo = {
0x3830e61:  {'message': "Add data escaping.", 'parents': [    0x0f5e148    ], 'test': False} , # Last:    (Bad)
0x0f5e148: {'message': "Improve function for getting page template.", 'parents': [ 0xaaf8dc5], 'test': False},
0xaaf8dc5: {'message': "Merge branch 'navigation'", 'parents': [ 0x3e667f8, 0xea3d736], 'test': False},
    0x3e667f8: {'message': "Add icons.", 'parents': [  0x43a07b1], 'test': False},
    0x43a07b1: {'message': "Add menu styles.", 'parents': [    0x107ca95], 'test': False}  , # First:       (Breaks)
    0x107ca95: {'message': "Add Responsive Nav.", 'parents': [   0xf52cc34], 'test': True}, # First:        (Good)
  0xea3d736: {'message': "Add ‘Admin’ notice.", 'parents': [ 0x17ca0bb], 'test': True},
  0x17ca0bb: {'message': "Update placeholder text.", 'parents': [  0xf52cc34], 'test': True},
0xf52cc34: {'message': "Add featured image.", 'parents': [  0x2abd954], 'test': True},
0x2abd954: {'message': "Style placeholders.", 'parents': [], 'test': True},
}

bad = 0x3830e61
good = 0x107ca95


def generate_queue(_dag, parents):
    for prev in parents:
        yield prev
        yield from generate_queue(_dag, _dag[prev]['parents'])

def make_queue(_dag, inits):
    """ Converts repo (a DAG) into a queue """
    q = list(generate_queue(_dag, inits))
    q.reverse()
    seen = set()
    r = [x for x in q if not (x in seen or seen.add(x))]
    return r

if __name__ == '__main__':
    pwd = '/tmp/git-repo'
    sh.rm('-r', pwd)
    sh.mkdir('-p', pwd)
    g = sh.git.bake(_cwd=pwd)
    g.init()

    parents = set(chain.from_iterable((repo[c]['parents'] for c in repo)))

    commits = set(repo)
    inits = list(commits - parents)
    queue = make_queue(repo, inits)

    assert len(queue) == len(repo), "queue {} vs repo {}".format(len(queue), len(repo))

    commit_ids = {}
    # Create commits
    for c in queue:
        # Set up repo
        parents = repo[c]['parents']
        if len(parents) > 0:
            g.checkout(commit_ids[parents[0]])
        if len(parents) > 1:
            if len(parents) > 2: raise NotImplementedError('Octopus merges not support yet.')
            g.merge('--no-commit', '-s', 'ours', commit_ids[parents[1]])  # just force to use 'ours' strategy.

        # Make changes
        with open(os.path.join(pwd, 'test.sh'), 'w') as f:
            f.write('exit {:d}\n'.format(0 if repo[c]['test'] else 1))
        os.chmod(os.path.join(pwd, 'test.sh'), 0o0755)
        with open(os.path.join(pwd, 'message'), 'w') as f:
            f.write(repo[c]['message'])
        g.add('test.sh', 'message')
        g.commit('-m', '{msg} ({test})'.format(msg=repo[c]['message'], test='Good' if repo[c]['test'] else 'Bad'))
        commit_ids[c] = g('rev-parse', 'HEAD').strip()

    # Run git-bisect
    g.bisect('start', commit_ids[bad], commit_ids[good])
    g.bisect('run', './test.sh')
    print(g.bisect('log'))
13
Unapiedra

これは 回答済み です。

基本的なアイデア-機能ブランチからどのコミットがマスターを壊すかを見つけるには、ea3d736の上に再適用する必要があります-関連するマスターHEAD。

以下はあなたのためにそれを行うテストスクリプトの例です(- git doc から):

$ cat ~/test.sh
#!/bin/sh

# Tweak the working tree by merging the hot-fix branch
# and then attempt a build
if  git merge --no-commit ea3d736 &&
    make
then
    # run project specific test and report its status
    ~/check_test_case.sh
    status=$?
else
    # tell the caller this is untestable
    status=125
fi

# undo the Tweak to allow clean flipping to the next commit
git reset --hard

# return control
exit $status

それを実行します:

git bisect start 3830e61 f52cc34 
git bisect good ea3d736 17ca0bb #If you want to test feature branch only
git bisect run ~/test.sh
10
Basilevs

警告: _git bisect_一時的な変更で自動的に二分する 」に関するセクションは、Git 2.25(2020年第1四半期)で更新されました。

これには、関連するmasterコミット(OPの場合は_ea3d736_でした)の上に、テストしているコミットを再適用するステップが含まれます

「_git merge --no-commit_」のマニュアルページで修正されているHEADを移動しない場合は、「_--no-ff_」に「_git bisect_」が必要です。

Mihail Atanassov(matana による commit 8dd327b (2019年10月28日)を参照してください。
Junio C Hamano-gitster- によってマージ commit fac9ab1 、2019年12月1日)

_Documentation/git-bisect.txt_ :コマンドをマージするために--no-ffを追加します

署名者:ミハイルアタナソフ
レビュー担当者:Jonathan Nieder

hotfixアプリケーションの例では、_git merge --no-commit_を使用して、バイセクト操作中に作業ツリーに一時的な変更を適用します。

状況によってはこれが早送りになる可能性があり、mergeは_--no-commit_に関係なくホットフィックスブランチのコミットを適用します( _git merge_マニュアル に記載されています)。

病理学的なケースでは、これは_[_ git bisect](https://git-scm.com/docs/git-bisect) run呼び出しループを最初のbisectステップと高速転送されたマージ後のHEADの間で無期限にループします。

この問題を回避するには、マージコマンドに_--no-ff_を追加します。

_git merge_ 確かに言及:

早送り更新はマージコミットを作成しないため、_--no-commit_を使用してこれらのマージを停止する方法はありません。

したがって、mergeコマンドによってブランチが変更または更新されないようにする場合は、_--no-ff_を_--no-commit_とともに使用します。

0
VonC