Mercurial > public > mercurial-scm > hg
comparison mercurial/dispatch.py @ 23871:b2d8f3685b06
dispatch: only check compatibility against major and minor versions (BC)
Extensions can declare compatibility with Mercurial versions. If an
error occurs, Mercurial will attempt to pin blame on an extension that
isn't marked as compatible.
While all bets are off when it comes to the internal API, my experience
has shown that a monthly/patch release of Mercurial has never broken any
of the extensions I've written. I think that expecting extensions to
declare compatibility with every patch release of Mercurial is asking a
bit much and adds little to no value.
This patch changes the blame logic from exact version matching to only
match on the major and minor Mercurial versions. This means that
extensions only need to mark themselves as compatible with the major,
quarterly releases and not the monthly ones in order to stay current and
avoid what is almost certainly unfair blame. This will mean less work
for extension authors and almost certainly fewer false positives in the
blame attribution.
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Thu, 15 Jan 2015 20:36:03 -0800 |
parents | 41c03b7592ed |
children | 2ee35b6ee4fb 97a548aeb749 |
comparison
equal
deleted
inserted
replaced
23870:9070e20057ae | 23871:b2d8f3685b06 |
---|---|
281 report = getattr(mod, 'buglink', _('the extension author.')) | 281 report = getattr(mod, 'buglink', _('the extension author.')) |
282 if not testedwith.strip(): | 282 if not testedwith.strip(): |
283 # We found an untested extension. It's likely the culprit. | 283 # We found an untested extension. It's likely the culprit. |
284 worst = name, 'unknown', report | 284 worst = name, 'unknown', report |
285 break | 285 break |
286 if compare not in testedwith.split() and testedwith != 'internal': | 286 |
287 tested = [tuplever(v) for v in testedwith.split()] | 287 # Never blame on extensions bundled with Mercurial. |
288 lower = [t for t in tested if t < ct] | 288 if testedwith == 'internal': |
289 nearest = max(lower or tested) | 289 continue |
290 if worst[0] is None or nearest < worst[1]: | 290 |
291 worst = name, nearest, report | 291 tested = [tuplever(t) for t in testedwith.split()] |
292 if ct in tested: | |
293 continue | |
294 | |
295 lower = [t for t in tested if t < ct] | |
296 nearest = max(lower or tested) | |
297 if worst[0] is None or nearest < worst[1]: | |
298 worst = name, nearest, report | |
292 if worst[0] is not None: | 299 if worst[0] is not None: |
293 name, testedwith, report = worst | 300 name, testedwith, report = worst |
294 if not isinstance(testedwith, str): | 301 if not isinstance(testedwith, str): |
295 testedwith = '.'.join([str(c) for c in testedwith]) | 302 testedwith = '.'.join([str(c) for c in testedwith]) |
296 warning = (_('** Unknown exception encountered with ' | 303 warning = (_('** Unknown exception encountered with ' |
313 | 320 |
314 return -1 | 321 return -1 |
315 | 322 |
316 def tuplever(v): | 323 def tuplever(v): |
317 try: | 324 try: |
318 return tuple([int(i) for i in v.split('.')]) | 325 # Assertion: tuplever is only used for extension compatibility |
326 # checking. Otherwise, the discarding of extra version fields is | |
327 # incorrect. | |
328 return tuple([int(i) for i in v.split('.')[0:2]]) | |
319 except ValueError: | 329 except ValueError: |
320 return tuple() | 330 return tuple() |
321 | 331 |
322 def aliasargs(fn, givenargs): | 332 def aliasargs(fn, givenargs): |
323 args = getattr(fn, 'args', []) | 333 args = getattr(fn, 'args', []) |