合并提交的 Git 平分

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17267816/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 08:40:24  来源:igfitidea点击:

Git bisect with merged commits

gitgit-bisect

提问by tollmanz

I have a history that looks like this:

我有一个看起来像这样的历史:

* 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.

I am trying to learn more and git bisect, but am having trouble with this history. I know that 107ca95is good and 3830e61is bad. When I run a git bisect, commits 107ca95..3e667f8are ignored. I happen to know that 43a07b1is the commit that introduced a regression, but it is never evaluated.

我正在尝试了解更多信息git bisect,但是我对这段历史有困难。我知道这107ca95是好事3830e61也是坏事。当我运行 a 时git bisect,提交107ca95..3e667f8被忽略。我碰巧知道这43a07b1是引入回归的提交,但它从未被评估过。

Here is roughly what I've done:

这大概是我所做的:

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

No matter what I do, 107ca95..3e667f8are never checked out to be tested.

无论我做什么,107ca95..3e667f8都永远不会被检查出来进行测试。

Is there any way that I can essentially "flatten" the history during the bisect to test those commits? I know I can use an interactive rebaseto flatten the history, but I do not want to have to do that.

有什么方法可以在二等分期间从本质上“展平”历史以测试这些提交?我知道我可以使用交互式rebase来扁平化历史,但我不想这样做。

回答by Unapiedra

This is a very old but unanswered question. I decided to investigate, and found that I could show that the behavior of Git is different to what the question says it is. One explanation is that Git improved the algorithm for bisect, or that the questioner made a mistake in marking commits.

这是一个非常古老但没有答案的问题。我决定进行调查,发现我可以证明 Git 的行为与问题所说的不同。一种解释是 Git 改进了 bisect 的算法,或者提问者在标记提交时犯了错误。

I am trying to learn more and git bisect, but am having trouble with this history. I know that 107ca95is good and 3830e61is bad. When I run a git bisect, commits 107ca95..3e667f8are ignored. I happen to know that 43a07b1is the commit that introduced a regression, but it is never evaluated.

我正在尝试了解更多信息和 git bisect,但我对这段历史有困难。我知道这107ca95是好事3830e61也是坏事。当我运行 git bisect 时,提交107ca95..3e667f8被忽略。我碰巧知道这43a07b1是引入回归的提交,但它从未被评估过

I wrote some code to check whether it is evaluated or not. My test shows that it is evaluated. Run the code below and verify that a commit with message Add menu styles.appears.

我写了一些代码来检查它是否被评估。我的测试表明它被评估了。运行下面的代码并验证是否Add menu styles.出现带有消息的提交。

Further comments:

进一步评论:

  • "commits 107ca95..3e667f8are ignored": Please note, that the commit you marked as "good" will not be evaluated because git already knows it to be good.
  • Please read the section "Bisection Algorithm" in this article by Christian Couder. Also the section "Checking merge bases" might be relevant.
  • As mentioned above, the question was certainly using a different version then the one I used (Question is from 2013, Git 2.11 is from 2016).
  • “提交107ca95..3e667f8被忽略”:请注意,您标记为“好”的提交将不会被评估,因为 git 已经知道它是好的。
  • 请阅读Christian Couder 这篇文章中的“二分算法”部分。“检查合并基础”部分也可能相关。
  • 如上所述,问题肯定使用了与我使用的版本不同的版本(问题来自 2013 年,Git 2.11 来自 2016 年)。

Bisect run output

平分运行输出

  • Notice that first 'Add Admin notice' is checked (line 4) because that provides the most information. (Read "Checking merge bases" from article mentioned above.)
  • From then on, it bisects the linear history as would be expected.
  • 请注意,首先选中了“添加管理员通知”(第 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)

Code

代码

Run in Python 3, with Git 2.11.0. Command to run: python3 script.py

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'))

回答by Basilevs

This is already answered

已经回答了

Basic idea - to find which commit from feature-branch breaks your master, you will have to reapply it on top of ea3d736 - relevant master HEAD.

基本思想 - 要查找来自功能分支的哪个提交破坏了您的主控,您必须在 ea3d736 - 相关主控 HEAD 之上重新应用它。

Following is an example (from git doc)of test script which does that for you:

以下是为您执行此操作的测试脚本示例(来自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

Run it:

运行:

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

回答by VonC

Warning: the git bisectsection regarding "Automatically bisect with temporary modifications" has been updated with Git 2.25 (Q1 2020).

警告:git bisect有关“使用临时修改自动二等分”的部分已使用 Git 2.25(2020 年第一季度)更新。

It involves the step where you reapply the commit you are testing on top of your relevant mastercommit (which was ea3d736in the OP's case)

它涉及在相关master提交之上重新应用正在测试的提交的步骤(ea3d736在 OP 的情况下)

The "git merge --no-commit" needs "--no-ff" if you do not want to move HEAD, which has been corrected in the manual page for "git bisect".

在“ git merge --no-commit”需要--no-ff“”如果你不想动HEAD,这在手册页被修正为“ git bisect”。

See commit 8dd327b(28 Oct 2019) by Mihail Atanassov (matana).
(Merged by Junio C Hamano -- gitster--in commit fac9ab1, 01 Dec 2019)

请参阅Mihail Atanassov ( ) 的commit 8dd327b(2019 年 10 月 28 日(由Junio C Hamano合并-- --提交 fac9ab1,2019 年 12 月 1 日)matana
gitster

Documentation/git-bisect.txt: add --no-ff to merge command

Signed-off-by: Mihail Atanassov
Reviewed-by: Jonathan Nieder

The hotfixapplication example uses git merge --no-committo apply temporary changes to the working tree during a bisect operation.

In some situations this can be a fast-forward and mergewill apply the hotfix branch's commits regardless of --no-commit(as documented in the git mergemanual).

In the pathological case this will make a [git bisect](https://git-scm.com/docs/git-bisect) runinvocation loop indefinitely between the first bisect step and the fast-forwarded post-merge HEAD.

Add --no-ffto the merge command to avoid this issue.

Documentation/git-bisect.txt: 添加 --no-ff 以合并命令

签字人:Mihail Atanassov
评论人:Jonathan Nieder

hotfix应用程序示例用于git merge --no-commit在二等分操作期间对工作树应用临时更改。

在某些情况下,这可能是一个快进,并且merge将应用修补程序分支的提交而不管--no-commit(如git merge手册中所述)。

在病理情况下,这将在第一个 bisect 步骤和快进 post-merge 之间无限期地进行[git bisect](https://git-scm.com/docs/git-bisect) run调用循环HEAD

添加--no-ff到合并命令以避免此问题。

git mergementions indeed:

git merge确实提到:

Note that fast-forward updates do not create a merge commit and therefore there is no way to stop those merges with --no-commit.

Thus, if you want to ensure your branch is not changed or updated by the mergecommand, use --no-ffwith --no-commit.

请注意,快进更新不会创建合并提交,因此无法通过--no-commit.

因此,如果要确保merge命令不会更改或更新您的分支,请使用--no-ffwith --no-commit

回答by Daniel F. Boyd

You can select the range of commits with the "git start" command. The synopsis of the command is:

您可以使用“git start”命令选择提交范围。命令的概要是:

git bisect start <bad> <good>

In your specific case I think the right command would be:

在您的具体情况下,我认为正确的命令是:

git bisect start 3830e61 107ca95