mercurial changegroup application
Mercurial (sometimes called “hg”, the name of its command-line tool, named after the elemental symbol for mercury) is a distributed version-control system.
When applying a “change group” (a group of changes applied as a unit, e.g. in a single hg push), Mercurial tracks the heads before and after the changeset. Previously, it stored the “oldheads” list of pre-changeset heads as a Python list, and computed the newly-created heads post-changeset with:
newheads = [h for h in repo.heads() if h not in oldheads]
Since h not in oldheads is O(n) on a list, a large repository would incur an O(N²) cost with respect to the number of heads in the repository.
What’s perhaps remarkable about this change is the size of the fix, which I think is the smallest diff in Accidentally Quadratic’s history: Wrapping a simple set(…) around the instantiation of the list.
In my judgment this fix is notable for a few reasons:
- It’s a testament to Python’s expressiveness and consistency of APIs, that swapping out a
setfor alistcan very often be a 5-character change. - It’s a vote in favor of all languages having a readily-available
settype in their standard libraries along side lists and mapping types, so that fixes of this type are easy, and also hopefully less-often necessary in the first place. - My first point notwithstanding, it’s an argument against having a polymorphic
inorcontainsmethod that silently degrades to O(n) behavior on lists or similar containers. Wherever practical, asymptotics should be clear from the call site!
