bisect: support lazy evaluation for 'skip' option

Summary:
Currently, the 'skip' option is too slow when users want to use it to bisect on
a file or folder like below:

    sl bisect --skip "!( file('path:foo') & file('path:bar') )"

Since it tries to eval the skip revset upfront, which takes a long time for
current `file` implementation. This diff make it lazy evaluated.

Reviewed By: muirdm

Differential Revision: D46643574

fbshipit-source-id: c063fbea2bc0703772e07d5167adc8fbf989a68f
This commit is contained in:
Zhaolong Zhu 2023-06-14 11:07:02 -07:00 committed by Facebook GitHub Bot
parent 38c190a35a
commit a7e82b1613
4 changed files with 47 additions and 24 deletions

View File

@ -1174,16 +1174,34 @@ the sparse profile from the known %s changeset %s\n"
def _update_state(repo, state, rev, good, bad, skip):
if rev:
nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
else:
nodes = [repo.lookup(".")]
def get_nodes(rev):
nodes = (
[repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
if rev
else [repo.lookup(".")]
)
return nodes
def get_revs(rev):
"""lazy evaluate `rev` revset expression if it is large.
This is only used for 'skip' status now.
"""
revs = scmutil.revrange(repo, [rev or "."])
fastlen = revs.fastlen()
# revset check is slower than nodes check, so avoid revset expr for small revsets.
# for example, user might just want to skip a single commit.
if fastlen is not None and fastlen < 10:
return list(repo.changelog.tonodes(revs))
else:
return [f"revset:{rev}"]
if good:
state["good"] += nodes
state["good"] += get_nodes(rev)
elif bad:
state["bad"] += nodes
state["bad"] += get_nodes(rev)
elif skip:
state["skip"] += nodes
state["skip"] += get_revs(rev)
@command(

View File

@ -37,7 +37,7 @@ def bisect(repo, state):
changelog = repo.changelog
clparents = changelog.parentrevs
skip = set([changelog.rev(n) for n in state["skip"]])
skip = _state_to_revs(repo, state, "skip")
def buildancestors(bad, good):
badrev = min([changelog.rev(n) for n in bad])
@ -219,6 +219,22 @@ def checkstate(state) -> bool:
raise error.Abort(_("cannot bisect (no known bad revisions)"))
def _state_to_revs(repo, state, kind):
items = state[kind]
nodes = []
revset_exprs = []
for item in items:
if isinstance(item, bytes):
nodes.append(item)
elif item.startswith("revset:"):
revset_exprs.append(item[7:])
else:
raise error.Abort(_("invalid node: %s, kind: %s") % (item, kind))
return repo.revs("%ln or %lr", nodes, revset_exprs)
def get(repo, status):
"""
Return a list of revision(s) that match the given status:
@ -233,7 +249,7 @@ def get(repo, status):
"""
state = load_state(repo)
if status in ("good", "bad", "skip", "current"):
return list(map(repo.changelog.rev, state[status]))
return list(_state_to_revs(repo, state, status))
else:
# In the following sets, we do *not* call 'bisect()' with more
# than one level of recursion, because that can be very, very

View File

@ -18,18 +18,7 @@
$ hg bisect -s "! (file('path:E') or file('path:M'))"
$ cat .hg/bisect.state
skip 426bada5c67598ca65036d57d9e4b64b0c1ce7a0
skip 112478962961147124edd43549aedd1a335e44bf
skip 26805aba1e600a82e93661149f2313866a221a7b
skip f585351a92f85104bff7c284233c338b10eb1df7
skip a194cadd16930608adaa649035ad4c16930cbd0f
skip 43195508e3bb704c08d24c40375bdd826789dd72
skip a31451c3c1debad52cf22ef2aebfc88c75dc899a
skip 47eb959e86339c47666b6d1e12c7a9ea534aea1c
skip 08eb06eada62c63c386dde447d379684d2a0156d
skip fb35f87c67da3431b7514753fc516ec66a60be78
skip 652ea04869f63d6b6ce47da40f4fb76e01516a98
skip 71d6430ccd823b5187025bfdedc0ac6d8c0f7e34
skip revset:! (file('path:E') or file('path:M'))
$ hg bisect -g $A
$ hg bisect -b $N

View File

@ -246,10 +246,10 @@
good b5bd63375ab9a290419f2024b7f4ee9ea7ce90a8
good ed2d2f24b11c368fa8aa0da9f4e1db580abade59
good 58c80a7c8a4025a94cedaf7b4a4e3124e8909a96
skip 9d7d07bc967ca98ad0600c24953fd289ad5fa991
skip ce8f0998e922c179e80819d5066fbe46e2998784
skip e7fa0811edb063f6319531f0d0a865882138e180
skip a2e6ea4973e9196ddd3386493b0c214b41fd97d3
skip e7fa0811edb063f6319531f0d0a865882138e180
skip ce8f0998e922c179e80819d5066fbe46e2998784
skip 9d7d07bc967ca98ad0600c24953fd289ad5fa991
# bisect reverse test