這是一個非常舊的但沒有答案的問題。我決定調查,發現我可以證明Git的行爲與問題所說的不同。 一種解釋是,Git的改進算法的對開,或提問做出標記提交一個錯誤。
我想了解更多和git bisect,但我有這個歷史的麻煩。我知道107ca95
是好的,3830e61
是壞的。當我運行git bisect時,提交107ca95..3e667f8
被忽略。我碰巧知道43a07b1
是犯介紹了一個迴歸,但它永遠不會評估。
我寫了一些代碼來檢查其是否評估或沒有。我的測試顯示它已被評估。運行下面的代碼並驗證提交消息Add menu styles.
出現。
進一步意見:
- 「承諾
107ca95..3e667f8
被忽略」:請注意,提交你標記爲「好」將不被評估,因爲混帳已經知道它是好的。
- 請參閱this article by 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'))
過去我google搜索了類似的問題
,發現標有所有「合併」分支的東西好,所以只留下合併提交自己的腳本。 –
@BalogPal - 我看到了類似的建議,但似乎這樣會將分支中的所有內容都標記爲好,但實際上它包含錯誤的提交。對我來說奇怪的是,我甚至無法解決合併提交問題。奇怪的是,它解決了一個甚至不在提交範圍內的提交。 – tollmanz
沒關係,如果第一個識別出合併,那麼你做一個2-pass對分,然後你將它與它提交的提交合並。但是,如果你有更好的主意,只需將它應用於技術,關鍵是你可以使用腳本預先標記某些提交 –