D4502: util: allow lrucachedict to track cost of entries

2018-09-08 Thread lothiraldan (Boris Feld)
lothiraldan added inline comments.

INLINE COMMENTS

> indygreg wrote in util.py:1277
> Good catch! I'll send a revised patch.
> 
> FWIW, cost accounting on this data structure opens up a lot of potential 
> around caching on revlogs. I have some alpha-quality commits to replace the 
> full revision cache on the revlog with an `lrucachedict` and to add a 
> decompressed chunk cache to revlogs. Such caches can speed up certain 
> operations drastically. However, we need to be careful about adding always-on 
> caches to revlogs because they can result in memory bloat. I was thinking 
> about adding context manager methods to revlogs to temporarily activate 
> certain aggressive caches in order to facilitate certain operations. e.g. a 
> fulltext or chunk cache when applying delta groups could make it drastically 
> faster to compute deltas during bulk insertion. A chunk cache could make 
> reverse walks significantly faster. Etc. I figured you'd be interested given 
> recent work in this area :)

Having a weighted cache would combine well with our work on intermediate 
snapshots. If we can keep the right intermediate snapshots in the cache we will 
get a lot of useful cache hit.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D4502

To: indygreg, #hg-reviewers
Cc: lothiraldan, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 19 of 19] sparse-revlog: set max delta chain length to on thousand

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536333525 14400
#  Fri Sep 07 11:18:45 2018 -0400
# Node ID 113d5204173704d2ec31719eacdb40e2f88bee39
# Parent  f8b71b0b9d035171dae9162f87747ce02da4b086
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
113d52041737
sparse-revlog: set max delta chain length to on thousand

The new snapshot system used in the sparse-revlog case gave us some small size
benefit so far. However its most important property is to gracefully handle
harder limit on delta chainlength.

Long delta chain has a very detrimental impact on read (and write) performance
in revlog. Being able to shorter them provide a great boost. However, shorting
delta used to result significantly lower compression ratio. The intermediate
snapshots effectively suppress most of this effect (even all in some case).

# Effect on the test repository

The repository we use for test is not "realistic" but can still show this in
action using an unreasonably low chain limit. Limiting the chain length show a
sizeable increase but stay under control: +6% for limit=15; +15% for limit=10.
Without the snapshot system the increase is significantly bigger: +45% for
limit=15; +80% for limit=10. Even slightly larger than without delta chain
limit, the resulting size is still smaller than before we started doing
snapshots.

Here is a table for comparison. *Since the repository is not branchy, the
initial sparse-revlog version does not bring much benefit compare to the
non-sparse one):

   chain length limit | none   |   limit=15  |   limit=10  |
without sparse-revlog | 62 818 987 | 112 664 615 | 131 222 574 |
without snapshot  | 74 365 490 | 108 211 410 | 133 857 764 |
withsnapshot  | 59 230 936 |  63 002 924 |  68 415 329 |


# Effect On Real Life Repositories

The series provides significant benefits on all kind of repositories.

Using `hg debugupgraderepo -o redeltaparent --run`, we recomputed delta chain
for various repositories with different settings:
  - delta chain length: unlimited or 1000 limit
  - sparse-revlog:  enabled or disabled
  - this series:applied or not applied

We can observe multiple types of effect:

 - On very branchy repositories:
   * The delta chain limit as low impact on the repo size.
   * Intermediate snapshot greatly reduces manifest size:
 - pypy: -80%
 - netbeans: -95%
   * The delta chain limit is effective, without a size impact:
 - netbeans average:  613 -> 282
 - private #1 average:  1 068 -> 307

 - On more linear repository:
   * Intermediate snapshot limit the impact of delta chain limit:
 - mozilla:
 without the series: +360%
 with the series: +25%
   * The delta chain limit provides large improvement:
 - mozilla's average chain length:
unlimited: 15 338
limited:  469
   * Despite the chain length limit, the manifest size is reduced:
 - mercurial: -25%
 - mozilla:   -30%

It is clear that the use of chains of intermediate snapshots provide large
benefits both in storage size and delta chains quality. We should now switch our
effort toward making sure the write performance are acceptable. Then,
`sparse-revlog` will be a suitable format for all new repository.

# Raw Statistic

  * no-sparse:   general delta repository not using sparse-revlog
  * no-snapshot: sparse-revlog repository not using this series
  * snapshot:sparse-revlog repository using this series

mercurial

Manifest Size:
limit   | none|1000
|-|
no-sparse   |   8 021 373 |  8 199 366
no-snapshot |   8 103 561 |  8 259 719
snapshot|   6 137 116 |  6 126 433

Manifest Chain length data
limit   ||   none||   1000||
value   || average |   max   || average |   max   ||
||-|-||-|-||
no-sparse   || 307 |1456 || 279 |1000 ||
no-snapshot || 312 |1456 || 283 |1000 ||
snapshot|| 248 |1208 || 241 |1000 ||

Full Store Size
limit   | none|1000
|-|
no-sparse   |  51 013 198 | 51 201 574
no-snapshot |  50 930 795 | 51 141 006
snapshot|  48 072 037 | 48 093 572

pypy

Manifest Size:
limit   | none|1000
|-|
no-sparse   | 193 987 784 | 193 987 784
no-snapshot | 163 171 745 | 163 312 229
snapshot|  34 605 900 |  34 600 750

Manifest Chain length data
limit   ||   none||   1000||
value   || average |   max   || average |   max   ||

[PATCH 18 of 19] snapshot: also consider the snapshot chain of one unrelated revision

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536333525 14400
#  Fri Sep 07 11:18:45 2018 -0400
# Node ID f8b71b0b9d035171dae9162f87747ce02da4b086
# Parent  535a778ae14a756b207901db5b3c0e5a078ae6cd
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
f8b71b0b9d03
snapshot: also consider the snapshot chain of one unrelated revision

To maximize the chance of good delta chain reuse, we inject an unrelated delta
chain into our search. To do so, we search for the highest revision unrelated
to the parents of the current revision and use its snapshot chain too.

Adding this extra snapshot into the mix can have a performance impact. We'll
deal with performance impact in a later series.

diff --git a/mercurial/revlogutils/deltas.py b/mercurial/revlogutils/deltas.py
--- a/mercurial/revlogutils/deltas.py
+++ b/mercurial/revlogutils/deltas.py
@@ -719,6 +719,36 @@ def _rawgroups(revlog, p1, p2, cachedelt
 parents_snaps[idx].add(s)
 snapfloor = min(parents_snaps[0]) + 1
 _findsnapshots(revlog, snapshots, snapfloor)
+# search for the highest "unrelated" revision
+#
+# Adding snapshots used by "unrelated" revision increase the odd we
+# reuse an independant, yet better snapshot chain.
+#
+# XXX instead of building a set of revisions, we could lazily enumerate
+# over the chains. That would be more efficient, however we stick to
+# simple code for now.
+all_revs = set()
+for chain in candidate_chains:
+all_revs.update(chain)
+other = None
+for r in revlog.revs(prev, snapfloor):
+if r not in all_revs:
+other = r
+break
+if other is not None:
+# To avoid unfair competition, we won't use unrelated intermediate
+# snapshot that are deeper than the ones from the parent delta
+# chain.
+max_depth = max(parents_snaps.keys())
+chain = deltachain(other)
+for idx, s in enumerate(chain):
+if s < snapfloor:
+continue
+if max_depth < idx:
+break
+if not revlog.issnapshot(s):
+break
+parents_snaps[idx].add(s)
 # Test them as possible intermediate snapshot base
 # We test them from highest to lowest level. High level one are more
 # likely to result in small delta
@@ -756,9 +786,10 @@ def _rawgroups(revlog, p1, p2, cachedelt
 # more and more snapshot as the repository grow.
 yield tuple(snapshots[nullrev])
 
-# other approach failed try against prev to hopefully save us a
-# fulltext.
-yield (prev,)
+if not sparse:
+# other approach failed try against prev to hopefully save us a
+# fulltext.
+yield (prev,)
 
 class deltacomputer(object):
 def __init__(self, revlog):
diff --git a/tests/test-sparse-revlog.t b/tests/test-sparse-revlog.t
--- a/tests/test-sparse-revlog.t
+++ b/tests/test-sparse-revlog.t
@@ -77,7 +77,7 @@ repeatedly while some of it changes rare
   
 
   $ f -s .hg/store/data/*.d
-  .hg/store/data/_s_p_a_r_s_e-_r_e_v_l_o_g-_t_e_s_t-_f_i_l_e.d: size=59302280
+  .hg/store/data/_s_p_a_r_s_e-_r_e_v_l_o_g-_t_e_s_t-_f_i_l_e.d: size=59230936
   $ hg debugrevlog *
   format : 1
   flags  : generaldelta
@@ -89,45 +89,45 @@ repeatedly while some of it changes rare
   empty :0 ( 0.00%)
  text  :0 (100.00%)
  delta :0 (100.00%)
-  snapshot  :  168 ( 3.36%)
-lvl-0   :  4 ( 0.08%)
-lvl-1   : 18 ( 0.36%)
-lvl-2   : 39 ( 0.78%)
-lvl-3   : 54 ( 1.08%)
-lvl-4   : 53 ( 1.06%)
-  deltas: 4833 (96.64%)
-  revision size : 59302280
-  snapshot  :  5833942 ( 9.84%)
-lvl-0   : 804068 ( 1.36%)
-lvl-1   :1378470 ( 2.32%)
-lvl-2   :1608138 ( 2.71%)
-lvl-3   :1222158 ( 2.06%)
-lvl-4   : 821108 ( 1.38%)
-  deltas: 53468338 (90.16%)
+  snapshot  :  176 ( 3.52%)
+lvl-0   :  3 ( 0.06%)
+lvl-1   : 17 ( 0.34%)
+lvl-2   : 45 ( 0.90%)
+lvl-3   : 56 ( 1.12%)
+lvl-4   : 55 ( 1.10%)
+  deltas: 4825 (96.48%)
+  revision size : 59230936
+  snapshot  :  5770371 ( 9.74%)
+lvl-0   : 602962 ( 1.02%)
+lvl-1   :1534153 ( 2.59%)
+lvl-2   :1604445 ( 2.71%)
+lvl-3   :1218174 ( 2.06%)
+lvl-4   : 810637 ( 1.37%)
+  deltas: 53460565 (90.26%)
   
   chunks: 5001
   0x78 (x)  : 5001 (100.00%)
-  

[PATCH 17 of 19] snapshot: extract parent chain computation

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536333457 14400
#  Fri Sep 07 11:17:37 2018 -0400
# Node ID 535a778ae14a756b207901db5b3c0e5a078ae6cd
# Parent  2ad2e932a5e2bb5bbf2bac7b7d3870ba969c07d6
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
535a778ae14a
snapshot: extract parent chain computation

The final step of this series is to include chain related to "prev" in the
search. Before adding that code we do some simple code movement to clarify the
next diff.

diff --git a/mercurial/revlogutils/deltas.py b/mercurial/revlogutils/deltas.py
--- a/mercurial/revlogutils/deltas.py
+++ b/mercurial/revlogutils/deltas.py
@@ -711,8 +711,9 @@ def _rawgroups(revlog, p1, p2, cachedelt
 # search for snapshot in parents delta chain
 # map: snapshot-level: snapshot-rev
 parents_snaps = collections.defaultdict(set)
-for p in parents:
-for idx, s in enumerate(deltachain(p)):
+candidate_chains = [deltachain(p) for p in parents]
+for chain in candidate_chains:
+for idx, s in enumerate(chain):
 if not revlog.issnapshot(s):
 break
 parents_snaps[idx].add(s)
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 16 of 19] snapshot: refine candidate snapshot base upward

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536333456 14400
#  Fri Sep 07 11:17:36 2018 -0400
# Node ID 2ad2e932a5e2bb5bbf2bac7b7d3870ba969c07d6
# Parent  45a29cbc741ec23c1c3c98227c5ca2fcf6f0a3ff
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
2ad2e932a5e2
snapshot: refine candidate snapshot base upward

Once we found a suitable snapshot base it is useful to check if it has a
"children" snapshot that would provide a better diff. This is useful when base
not directly related to stored revision are picked. In those case, we "jumped"
to this new chain at an arbitrary point, checking if a higher point is more
appropriate will help to provide better results and increase snapshot reuse.

diff --git a/mercurial/revlogutils/deltas.py b/mercurial/revlogutils/deltas.py
--- a/mercurial/revlogutils/deltas.py
+++ b/mercurial/revlogutils/deltas.py
@@ -658,6 +658,19 @@ def _refinedgroups(revlog, p1, p2, cache
 if base == nullrev:
 break
 good = yield (base,)
+# refine snapshot up
+#
+# XXX the _findsnapshots call can be expensive and is "duplicated" with
+# the one done in `_rawgroups`. Once we start working on performance,
+# we should make the two logics share this computation.
+snapshots = collections.defaultdict(list)
+_findsnapshots(revlog, snapshots, good + 1)
+previous = None
+while good != previous:
+previous = good
+children = tuple(sorted(c for c in snapshots[good]))
+good = yield children
+
 # we have found nothing
 yield None
 
diff --git a/tests/test-sparse-revlog.t b/tests/test-sparse-revlog.t
--- a/tests/test-sparse-revlog.t
+++ b/tests/test-sparse-revlog.t
@@ -77,7 +77,7 @@ repeatedly while some of it changes rare
   
 
   $ f -s .hg/store/data/*.d
-  .hg/store/data/_s_p_a_r_s_e-_r_e_v_l_o_g-_t_e_s_t-_f_i_l_e.d: size=59303048
+  .hg/store/data/_s_p_a_r_s_e-_r_e_v_l_o_g-_t_e_s_t-_f_i_l_e.d: size=59302280
   $ hg debugrevlog *
   format : 1
   flags  : generaldelta
@@ -89,45 +89,45 @@ repeatedly while some of it changes rare
   empty :0 ( 0.00%)
  text  :0 (100.00%)
  delta :0 (100.00%)
-  snapshot  :  165 ( 3.30%)
+  snapshot  :  168 ( 3.36%)
 lvl-0   :  4 ( 0.08%)
-lvl-1   : 17 ( 0.34%)
-lvl-2   : 46 ( 0.92%)
-lvl-3   : 62 ( 1.24%)
-lvl-4   : 36 ( 0.72%)
-  deltas: 4836 (96.70%)
-  revision size : 59303048
-  snapshot  :  6105443 (10.30%)
-lvl-0   : 804187 ( 1.36%)
-lvl-1   :1476228 ( 2.49%)
-lvl-2   :1752567 ( 2.96%)
-lvl-3   :1461776 ( 2.46%)
-lvl-4   : 610685 ( 1.03%)
-  deltas: 53197605 (89.70%)
+lvl-1   : 18 ( 0.36%)
+lvl-2   : 39 ( 0.78%)
+lvl-3   : 54 ( 1.08%)
+lvl-4   : 53 ( 1.06%)
+  deltas: 4833 (96.64%)
+  revision size : 59302280
+  snapshot  :  5833942 ( 9.84%)
+lvl-0   : 804068 ( 1.36%)
+lvl-1   :1378470 ( 2.32%)
+lvl-2   :1608138 ( 2.71%)
+lvl-3   :1222158 ( 2.06%)
+lvl-4   : 821108 ( 1.38%)
+  deltas: 53468338 (90.16%)
   
   chunks: 5001
   0x78 (x)  : 5001 (100.00%)
-  chunks size   : 59303048
-  0x78 (x)  : 59303048 (100.00%)
+  chunks size   : 59302280
+  0x78 (x)  : 59302280 (100.00%)
   
   avg chain length  :   17
   max chain length  :   45
-  max chain reach   : 26194433
+  max chain reach   : 22744720
   compression ratio :   29
   
   uncompressed data size (min/max/avg) : 346468 / 346472 / 346471
-  full revision size (min/max/avg) : 200992 / 201080 / 201046
-  inter-snapshot size (min/max/avg): 11610 / 172762 / 32927
-  level-1   (min/max/avg)  : 15619 / 172762 / 86836
-  level-2   (min/max/avg)  : 13055 / 85219 / 38099
-  level-3   (min/max/avg)  : 11610 / 42645 / 23577
-  level-4   (min/max/avg)  : 12928 / 20205 / 16963
-  delta size (min/max/avg) : 10649 / 106863 / 11000
+  full revision size (min/max/avg) : 200985 / 201050 / 201017
+  inter-snapshot size (min/max/avg): 11598 / 163304 / 30669
+  level-1   (min/max/avg)  : 15616 / 163304 / 76581
+  level-2   (min/max/avg)  : 11602 / 86428 / 41234
+  level-3   (min/max/avg)  : 11598 / 42390 / 22632
+  level-4   (min/max/avg)  : 11603 / 19649 / 15492
+  delta size (min/max/avg) : 10649 / 105465 / 11063
   
-  deltas against prev  : 4162 (86.06%)
-  where prev = p1  : 4120 (98.99%)
+  deltas against prev  

[PATCH 15 of 19] snapshot: try to refine new snapshot base down the chain

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536333455 14400
#  Fri Sep 07 11:17:35 2018 -0400
# Node ID 45a29cbc741ec23c1c3c98227c5ca2fcf6f0a3ff
# Parent  cc72a5b2135b94e1176eaf7a71b5b577bb7b7a33
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
45a29cbc741e
snapshot: try to refine new snapshot base down the chain

There are cases where doing a diff against a snapshot's parent will be shorter
than against the snapshot itself. Reusing snapshot not directly related to the
revision we are trying to store increase this odd.

So once we found a possible candidate, we check the snapshots lower in the
chain.

This will involve extra processing, but this extra processing will only happen
when we are doing building a snapshot, a rare situation.

diff --git a/mercurial/revlogutils/deltas.py b/mercurial/revlogutils/deltas.py
--- a/mercurial/revlogutils/deltas.py
+++ b/mercurial/revlogutils/deltas.py
@@ -647,6 +647,17 @@ def _refinedgroups(revlog, p1, p2, cache
 good = yield candidates
 if good is not None:
 break
+
+# if we have a refinable value, try to refine it
+if good is not None and good not in (p1, p2) and revlog.issnapshot(good):
+# refine snapshot down
+previous = None
+while previous != good:
+previous = good
+base = revlog.deltaparent(good)
+if base == nullrev:
+break
+good = yield (base,)
 # we have found nothing
 yield None
 
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 14 of 19] snapshot: make sure we'll never refine delta base from a reused source

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536333454 14400
#  Fri Sep 07 11:17:34 2018 -0400
# Node ID cc72a5b2135b94e1176eaf7a71b5b577bb7b7a33
# Parent  4c849d34990166b1246426533efa517733d8a7fe
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
cc72a5b2135b
snapshot: make sure we'll never refine delta base from a reused source

The point of reusing delta from the source is to avoid doing computation when
applying a bundle. Refining such delta would go against that spirit.

We do not have refining logic in place yet. This code needed to be moved out
of the way before we could start adding such logic.

diff --git a/mercurial/revlogutils/deltas.py b/mercurial/revlogutils/deltas.py
--- a/mercurial/revlogutils/deltas.py
+++ b/mercurial/revlogutils/deltas.py
@@ -630,6 +630,19 @@ def _findsnapshots(revlog, cache, start_
 
 def _refinedgroups(revlog, p1, p2, cachedelta):
 good = None
+# First we try to reuse a the delta contained in the bundle.
+# (or from the source revlog)
+#
+# This logic only applies to general delta repositories and can be disabled
+# through configuration. Disabling reuse source delta is useful when
+# we want to make sure we recomputed "optimal" deltas.
+if cachedelta and revlog._generaldelta and revlog._lazydeltabase:
+# Assume what we received from the server is a good choice
+# build delta will reuse the cache
+good = yield (cachedelta[0],)
+if good is not None:
+yield None
+return
 for candidates in _rawgroups(revlog, p1, p2, cachedelta):
 good = yield candidates
 if good is not None:
@@ -651,17 +664,6 @@ def _rawgroups(revlog, p1, p2, cachedelt
 prev = curr - 1
 deltachain = lambda rev: revlog._deltachain(rev)[0]
 
-# First we try to reuse a the delta contained in the bundle.
-# (or from the source revlog)
-#
-# This logic only applies to general delta repositories and can be disabled
-# through configuration. Disabling reuse of source delta is useful when
-# we want to make sure we recomputed "optimal" deltas.
-if cachedelta and gdelta and revlog._lazydeltabase:
-# Assume what we received from the server is a good choice
-# build delta will reuse the cache
-yield (cachedelta[0],)
-
 if gdelta:
 # exclude already lazy tested base if any
 parents = [p for p in (p1, p2) if p != nullrev]
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 13 of 19] snapshot: turn _refinedgroups into a coroutine

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536333454 14400
#  Fri Sep 07 11:17:34 2018 -0400
# Node ID 4c849d34990166b1246426533efa517733d8a7fe
# Parent  f79dc24022976ab6ba0a8f7183651e0e4d9877d5
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
4c849d349901
snapshot: turn _refinedgroups into a coroutine

We are now almost ready to start adding refining logic.

diff --git a/mercurial/revlogutils/deltas.py b/mercurial/revlogutils/deltas.py
--- a/mercurial/revlogutils/deltas.py
+++ b/mercurial/revlogutils/deltas.py
@@ -589,7 +589,7 @@ def _candidategroups(revlog, textlen, p1
 tested = set([nullrev])
 candidates = _refinedgroups(revlog, p1, p2, cachedelta)
 while True:
-temptative = next(candidates)
+temptative = candidates.send(good)
 if temptative is None:
 break
 group = []
@@ -618,8 +618,6 @@ def _candidategroups(revlog, textlen, p1
 #  impacting performances. Some bounding or slicing mecanism
 #  would help to reduce this impact.
 good = yield tuple(group)
-if good is not None:
-break
 yield None
 
 def _findsnapshots(revlog, cache, start_rev):
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 12 of 19] snapshot: also use None as a stop value for `_refinegroup`

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536333453 14400
#  Fri Sep 07 11:17:33 2018 -0400
# Node ID f79dc24022976ab6ba0a8f7183651e0e4d9877d5
# Parent  db3775d10e1167be3487eed8b915ca2bf8c4bccb
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
f79dc2402297
snapshot: also use None as a stop value for `_refinegroup`

This is yet another small step toward turning `_refinegroups` into a co-routine.

diff --git a/mercurial/revlogutils/deltas.py b/mercurial/revlogutils/deltas.py
--- a/mercurial/revlogutils/deltas.py
+++ b/mercurial/revlogutils/deltas.py
@@ -587,7 +587,11 @@ def _candidategroups(revlog, textlen, p1
 deltas_limit = textlen * LIMIT_DELTA2TEXT
 
 tested = set([nullrev])
-for temptative in _refinedgroups(revlog, p1, p2, cachedelta):
+candidates = _refinedgroups(revlog, p1, p2, cachedelta)
+while True:
+temptative = next(candidates)
+if temptative is None:
+break
 group = []
 for rev in temptative:
 # skip over empty delta (no need to include them in a chain)
@@ -632,6 +636,8 @@ def _refinedgroups(revlog, p1, p2, cache
 good = yield candidates
 if good is not None:
 break
+# we have found nothing
+yield None
 
 def _rawgroups(revlog, p1, p2, cachedelta):
 """Provides group of revision to be tested as delta base
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 11 of 19] snapshot: add refining logic at the findeltainfo level

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536333453 14400
#  Fri Sep 07 11:17:33 2018 -0400
# Node ID db3775d10e1167be3487eed8b915ca2bf8c4bccb
# Parent  256a998ee3bf645cc6eee3a144e6825d61406a7c
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
db3775d10e11
snapshot: add refining logic at the findeltainfo level

Once we found a delta, we want to have the candidates logic challenge it,
searching for a better candidate.

The logic at the lower level is still missing. We'll introduce it later.
Adding small changes in individual commits make it simpler to explain the code
change.

This is another small step toward turning `_refinegroups` into a co-routine.

diff --git a/mercurial/revlogutils/deltas.py b/mercurial/revlogutils/deltas.py
--- a/mercurial/revlogutils/deltas.py
+++ b/mercurial/revlogutils/deltas.py
@@ -582,6 +582,7 @@ def _candidategroups(revlog, textlen, p1
 
 deltalength = revlog.length
 deltaparent = revlog.deltaparent
+good = None
 
 deltas_limit = textlen * LIMIT_DELTA2TEXT
 
@@ -612,7 +613,9 @@ def _candidategroups(revlog, textlen, p1
 # XXX: in the sparse revlog case, group can become large,
 #  impacting performances. Some bounding or slicing mecanism
 #  would help to reduce this impact.
-yield tuple(group)
+good = yield tuple(group)
+if good is not None:
+break
 yield None
 
 def _findsnapshots(revlog, cache, start_rev):
@@ -847,14 +850,20 @@ class deltacomputer(object):
 candidaterevs = next(groups)
 while candidaterevs is not None:
 nominateddeltas = []
+if deltainfo is not None:
+# if we already found a good delta,
+# challenge it against refined candidates
+nominateddeltas.append(deltainfo)
 for candidaterev in candidaterevs:
 candidatedelta = self._builddeltainfo(revinfo, candidaterev, 
fh)
 if isgooddeltainfo(self.revlog, candidatedelta, revinfo):
 nominateddeltas.append(candidatedelta)
 if nominateddeltas:
 deltainfo = min(nominateddeltas, key=lambda x: x.deltalen)
-break
-candidaterevs = next(groups)
+if deltainfo is not None:
+candidaterevs = groups.send(deltainfo.base)
+else:
+candidaterevs = next(groups)
 
 if deltainfo is None:
 deltainfo = self._fullsnapshotinfo(fh, revinfo)
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 10 of 19] snapshot: use None as a stop value when looking for a good delta

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536333452 14400
#  Fri Sep 07 11:17:32 2018 -0400
# Node ID 256a998ee3bf645cc6eee3a144e6825d61406a7c
# Parent  9ea2ef9abc22291b021b80c8f88320026ae79618
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
256a998ee3bf
snapshot: use None as a stop value when looking for a good delta

Having clear stop value should help keep clear logic around the co-routine.
The alternative of using a StopIteration exception give a messier result.

This is one small step toward turning `_refinegroups` into a co-routine.

diff --git a/mercurial/revlogutils/deltas.py b/mercurial/revlogutils/deltas.py
--- a/mercurial/revlogutils/deltas.py
+++ b/mercurial/revlogutils/deltas.py
@@ -577,6 +577,7 @@ def _candidategroups(revlog, textlen, p1
 """
 # should we try to build a delta?
 if not (len(revlog) and revlog._storedeltachains):
+yield None
 return
 
 deltalength = revlog.length
@@ -612,6 +613,7 @@ def _candidategroups(revlog, textlen, p1
 #  impacting performances. Some bounding or slicing mecanism
 #  would help to reduce this impact.
 yield tuple(group)
+yield None
 
 def _findsnapshots(revlog, cache, start_rev):
 """find snapshot from start_rev to tip"""
@@ -842,7 +844,8 @@ class deltacomputer(object):
 p1r, p2r = revlog.rev(p1), revlog.rev(p2)
 groups = _candidategroups(self.revlog, revinfo.textlen,
  p1r, p2r, cachedelta)
-for candidaterevs in groups:
+candidaterevs = next(groups)
+while candidaterevs is not None:
 nominateddeltas = []
 for candidaterev in candidaterevs:
 candidatedelta = self._builddeltainfo(revinfo, candidaterev, 
fh)
@@ -851,6 +854,7 @@ class deltacomputer(object):
 if nominateddeltas:
 deltainfo = min(nominateddeltas, key=lambda x: x.deltalen)
 break
+candidaterevs = next(groups)
 
 if deltainfo is None:
 deltainfo = self._fullsnapshotinfo(fh, revinfo)
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 09 of 19] snapshot: introduce an intermediate `_refinedgroups` generator

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536333452 14400
#  Fri Sep 07 11:17:32 2018 -0400
# Node ID 9ea2ef9abc22291b021b80c8f88320026ae79618
# Parent  979ae7a8db741f15531f76dded568e0792e57b10
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
9ea2ef9abc22
snapshot: introduce an intermediate `_refinedgroups` generator

This method will be used to improve the search for a good snapshot base. To
keep things simpler, we introduce the necessary function before doing any
delta base logic change. The next handful of commits will focus on refactoring
the code to let that new logic land as clearly as possible.

# General Idea

Right now, the search for a good delta base stop whenever we found a good one.
However, when using sparse-revlog, we should probably try a bit harder.

We do significant effort to increase delta re-use by jumping on "unrelated"
delta chains that provide better results. Moving to another chain for a better
result is good, but we have no guarantee we jump at a reasonable point in that
new chain. When we consider over the chains related to the parents, we start
from the higher-level snapshots. This is a way to consider the snapshot closer
to the current revision that has the best chance to produce a small delta. We
do benefit from this walk order when jumping to a better "unrelated" stack.

To counter-balance this, we'll introduce a way to "refine" the result. After a
good delta have been found, we'll keep searching for a better delta, using the
current best one as a starting point.

# Target Setup

The `finddeltainfo` method is responsible for the general search for a good
delta. It requests candidates base from `_candidategroups` and decides which
one are usable.

The `_candidategroups` generator act as a top-level filter, it does not care
about how we pick candidates, it just does basic filtering, excluding
revisions that have been tested already or that are an obvious misfit.

The `_rawgroups` generator is the one with the actual ancestors walking logic,
It does not care about what would do a good delta and what was already tested,
it just issues the initial candidates.

We introduce a new `_refinedgroup` function to bridge the gap between
`_candidategroups` and `_rawgroups`. It delegates the initial iteration logic
and then performing relevant refining of the valid base once found. (This
logic is yet to be added to function)

All these logics are fairly independent and easier to understand when standing
alone, not mixed with each other. It also makes it easy to test and try
different approaches for one of those four layers without affecting the other
ones.

# Technical details

To communicate `finddeltainfo` choice of "current best delta base" to the
`_refinegroup` logic, we plan to use python co-routine feature. The
`_candidategroups` and `_refinegroup` generators will become co-routine. This
will allow `_refinegroup` to detect when a good delta have been found and
triggers various refining steps.

For now, `_candidategroups` will just pass the value down the stack.

After poking at various option, the co-routine appears the best to keep each
layers focus on its duty, without the need to spread implementation details
across layers.

diff --git a/mercurial/revlogutils/deltas.py b/mercurial/revlogutils/deltas.py
--- a/mercurial/revlogutils/deltas.py
+++ b/mercurial/revlogutils/deltas.py
@@ -585,7 +585,7 @@ def _candidategroups(revlog, textlen, p1
 deltas_limit = textlen * LIMIT_DELTA2TEXT
 
 tested = set([nullrev])
-for temptative in _rawgroups(revlog, p1, p2, cachedelta):
+for temptative in _refinedgroups(revlog, p1, p2, cachedelta):
 group = []
 for rev in temptative:
 # skip over empty delta (no need to include them in a chain)
@@ -621,6 +621,13 @@ def _findsnapshots(revlog, cache, start_
 if issnapshot(rev):
 cache[deltaparent(rev)].append(rev)
 
+def _refinedgroups(revlog, p1, p2, cachedelta):
+good = None
+for candidates in _rawgroups(revlog, p1, p2, cachedelta):
+good = yield candidates
+if good is not None:
+break
+
 def _rawgroups(revlog, p1, p2, cachedelta):
 """Provides group of revision to be tested as delta base
 
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 08 of 19] snapshot: consider unrelated snapshots at a similar level first

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536333451 14400
#  Fri Sep 07 11:17:31 2018 -0400
# Node ID 979ae7a8db741f15531f76dded568e0792e57b10
# Parent  b0d159cf86730ba381b8b6a0e9031a40d52cb101
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
979ae7a8db74
snapshot: consider unrelated snapshots at a similar level first

This new step is inserted before considering using a level-N snapshot as a
base for a level-N+1 snapshot. We first check if existing level-N+1 snapshots
using the same base would be a suitable base for a level-N+2 snapshot.

This increases snapshot reuse and limits the risk of snapshot explosion in
very branchy repositories.

Using a "deeper" snapshot as the base also results in a smaller snapshot since
it builds a level-N+2 intermediate snapshot instead of an N+1 one.

This logic is similar for the one we added in a previous commit. In that
previous commit is only applied to level-0 "siblings".

We can see this effect in the test repository. Snapshots moved from lower
levels to higher levels.

diff --git a/mercurial/revlogutils/deltas.py b/mercurial/revlogutils/deltas.py
--- a/mercurial/revlogutils/deltas.py
+++ b/mercurial/revlogutils/deltas.py
@@ -616,9 +616,10 @@ def _candidategroups(revlog, textlen, p1
 def _findsnapshots(revlog, cache, start_rev):
 """find snapshot from start_rev to tip"""
 deltaparent = revlog.deltaparent
+issnapshot = revlog.issnapshot
 for rev in revlog.revs(start_rev):
-if deltaparent(rev) == nullrev:
-cache[nullrev].append(rev)
+if issnapshot(rev):
+cache[deltaparent(rev)].append(rev)
 
 def _rawgroups(revlog, p1, p2, cachedelta):
 """Provides group of revision to be tested as delta base
@@ -673,11 +674,35 @@ def _rawgroups(revlog, p1, p2, cachedelt
 if not revlog.issnapshot(s):
 break
 parents_snaps[idx].add(s)
+snapfloor = min(parents_snaps[0]) + 1
+_findsnapshots(revlog, snapshots, snapfloor)
 # Test them as possible intermediate snapshot base
 # We test them from highest to lowest level. High level one are more
 # likely to result in small delta
+floor = None
 for idx, snaps in sorted(parents_snaps.items(), reverse=True):
+siblings = set()
+for s in snaps:
+siblings.update(snapshots[s])
+# Before considering making a new intermediate snapshot, we check
+# if an existing snapshot, children of base we consider, would be
+# suitable.
+#
+# It give a change to reuse a delta chain "unrelated" to the
+# current revision instead of starting our own. Without such
+# re-use, topological branches would keep reopening new chains.
+# Creating more and more snapshot as the repository grow.
+
+if floor is not None:
+# We only do this for siblings created after the one in our
+# parent's delta chain. Those created before has less chances
+# to be valid base since our ancestors had to create a new
+# snapshot.
+siblings = [r for r in siblings if floor < r]
+yield tuple(sorted(siblings))
+# then test the base from our parent's delta chain.
 yield tuple(sorted(snaps))
+floor = min(snaps)
 # No suitable base found in the parent chain, search if any full
 # snapshots emitted since parent's base would be a suitable base for an
 # intermediate snapshot.
@@ -686,8 +711,6 @@ def _rawgroups(revlog, p1, p2, cachedelt
 # revisions instead of starting our own. Without such re-use,
 # topological branches would keep reopening new full chains. Creating
 # more and more snapshot as the repository grow.
-snapfloor = min(parents_snaps[0]) + 1
-_findsnapshots(revlog, snapshots, snapfloor)
 yield tuple(snapshots[nullrev])
 
 # other approach failed try against prev to hopefully save us a
diff --git a/tests/test-sparse-revlog.t b/tests/test-sparse-revlog.t
--- a/tests/test-sparse-revlog.t
+++ b/tests/test-sparse-revlog.t
@@ -77,7 +77,7 @@ repeatedly while some of it changes rare
   
 
   $ f -s .hg/store/data/*.d
-  .hg/store/data/_s_p_a_r_s_e-_r_e_v_l_o_g-_t_e_s_t-_f_i_l_e.d: size=63812493
+  .hg/store/data/_s_p_a_r_s_e-_r_e_v_l_o_g-_t_e_s_t-_f_i_l_e.d: size=59303048
   $ hg debugrevlog *
   format : 1
   flags  : generaldelta
@@ -89,45 +89,45 @@ repeatedly while some of it changes rare
   empty :0 ( 0.00%)
  text  :0 (100.00%)
  delta :0 (100.00%)
-  snapshot  :  179 ( 3.58%)
+  snapshot  :  165 ( 3.30%)
 lvl-0   :  4 ( 0.08%)
-lvl-1   :  

[PATCH 07 of 19] snapshot: consider all snapshots in the parents' chains

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536333450 14400
#  Fri Sep 07 11:17:30 2018 -0400
# Node ID b0d159cf86730ba381b8b6a0e9031a40d52cb101
# Parent  b7726577179f0cffc8a4753606dcc5feb858c22b
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
b0d159cf8673
snapshot: consider all snapshots in the parents' chains

There are no reasons to only consider full snapshot as a possible base for an
intermediate snapshot.  Now that the basic principles have been set, we can
start adding more levels of snapshots.

We now consider all snapshots in the parent's chains (full or intermediate).
This creates a chain of intermediate snapshots, each smaller than the previous
one.

# Effect On The Test Repository

In the test repository, we can see a decrease in the revlog size and slightly
shorter delta chain.

However, that approach creates snapshots more frequently, increasing the risk
of ending into problematic cases in very branchy repositories (not triggered
by the test repository). The next changesets will remove that risk by adding
logic that increases deltas reuse.

diff --git a/mercurial/revlogutils/deltas.py b/mercurial/revlogutils/deltas.py
--- a/mercurial/revlogutils/deltas.py
+++ b/mercurial/revlogutils/deltas.py
@@ -661,12 +661,23 @@ def _rawgroups(revlog, p1, p2, cachedelt
 yield parents
 
 if sparse and parents:
+snapshots = collections.defaultdict(list) # map: base-rev: snapshot-rev
 # See if we can use an existing snapshot in the parent chains to use as
 # a base for a new intermediate-snapshot
-bases = []
+#
+# search for snapshot in parents delta chain
+# map: snapshot-level: snapshot-rev
+parents_snaps = collections.defaultdict(set)
 for p in parents:
-bases.append(deltachain(p)[0])
-yield tuple(sorted(bases))
+for idx, s in enumerate(deltachain(p)):
+if not revlog.issnapshot(s):
+break
+parents_snaps[idx].add(s)
+# Test them as possible intermediate snapshot base
+# We test them from highest to lowest level. High level one are more
+# likely to result in small delta
+for idx, snaps in sorted(parents_snaps.items(), reverse=True):
+yield tuple(sorted(snaps))
 # No suitable base found in the parent chain, search if any full
 # snapshots emitted since parent's base would be a suitable base for an
 # intermediate snapshot.
@@ -675,8 +686,7 @@ def _rawgroups(revlog, p1, p2, cachedelt
 # revisions instead of starting our own. Without such re-use,
 # topological branches would keep reopening new full chains. Creating
 # more and more snapshot as the repository grow.
-snapfloor = min(bases) + 1
-snapshots = collections.defaultdict(list)
+snapfloor = min(parents_snaps[0]) + 1
 _findsnapshots(revlog, snapshots, snapfloor)
 yield tuple(snapshots[nullrev])
 
diff --git a/tests/test-sparse-revlog.t b/tests/test-sparse-revlog.t
--- a/tests/test-sparse-revlog.t
+++ b/tests/test-sparse-revlog.t
@@ -77,7 +77,7 @@ repeatedly while some of it changes rare
   
 
   $ f -s .hg/store/data/*.d
-  .hg/store/data/_s_p_a_r_s_e-_r_e_v_l_o_g-_t_e_s_t-_f_i_l_e.d: size=67810463
+  .hg/store/data/_s_p_a_r_s_e-_r_e_v_l_o_g-_t_e_s_t-_f_i_l_e.d: size=63812493
   $ hg debugrevlog *
   format : 1
   flags  : generaldelta
@@ -89,39 +89,45 @@ repeatedly while some of it changes rare
   empty :0 ( 0.00%)
  text  :0 (100.00%)
  delta :0 (100.00%)
-  snapshot  :  126 ( 2.52%)
+  snapshot  :  179 ( 3.58%)
 lvl-0   :  4 ( 0.08%)
-lvl-1   :120 ( 2.40%)
-lvl-2   :  2 ( 0.04%)
-  deltas: 4875 (97.48%)
-  revision size : 67810463
-  snapshot  : 14373347 (21.20%)
-lvl-0   : 804235 ( 1.19%)
-lvl-1   :   13535903 (19.96%)
-lvl-2   :  33209 ( 0.05%)
-  deltas: 53437116 (78.80%)
+lvl-1   : 49 ( 0.98%)
+lvl-2   : 56 ( 1.12%)
+lvl-3   : 63 ( 1.26%)
+lvl-4   :  7 ( 0.14%)
+  deltas: 4822 (96.42%)
+  revision size : 63812493
+  snapshot  : 10745878 (16.84%)
+lvl-0   : 804204 ( 1.26%)
+lvl-1   :4986508 ( 7.81%)
+lvl-2   :2858810 ( 4.48%)
+lvl-3   :1958906 ( 3.07%)
+lvl-4   : 137450 ( 0.22%)
+  deltas: 53066615 (83.16%)
   
   chunks: 5001
   0x78 (x)  : 5001 (100.00%)
-  chunks size   : 67810463
-  0x78 (x)  : 67810463 (100.00%)
+  chunks size   : 63812493
+  0x78 (x)  : 63812493 (100.00%)
   
-  avg chain length  :  

[PATCH 06 of 19] snapshot: search for unrelated but reusable full-snapshot

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536333450 14400
#  Fri Sep 07 11:17:30 2018 -0400
# Node ID b7726577179f0cffc8a4753606dcc5feb858c22b
# Parent  c6dd1a01b22cd2649dc7c0dfe9981305b6e762d2
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
b7726577179f
snapshot: search for unrelated but reusable full-snapshot

# New Strategy Step: Reusing Snapshot Outside Of Parents' Chain.

If no suitable bases were found in the parent's chains, see if we could reuse
a full snapshot not directly related to the current revision. Such search can
be expensive, so we only search for snapshots appended to the revlog *after*
the bases used by the parents of the current revision (the one we just
tested). We assume the parent's bases were created because the previous
snapshots were unsuitable, so there are low odds they would be useful now.

This search gives a chance to reuse a delta chain unrelated to the current
revision. Without this re-use, topological branches would keep reopening new
full chains. Creating more and more snapshots as the repository grow.

In repositories with many topological branches, the lack of delta reuse can
create too many snapshots reducing overall compression to nothing. This
results in a very large repository and other usability issues.

For now, we still focus on creating level-1 snapshots. However, this principle
will play a large part in how we avoid snapshot explosion once we have more
snapshot levels.

# Effects On The Test Repository

In the test repository we created, we can see the beneficial effect of such
reuse. We need very few level-0 snapshots and the overall revlog size has
decreased.

The `hg debugrevlog` call, show a "lvl-2" snapshot. It comes from the existing
delta logic using the `prev` revision (revlog's tip) as the base. In this
specific case, it turns out the tip was a level-1 snapshot. This is a
coincidence that can be ignored.

Finding and testing against all these unrelated snapshots can have a
performance impact at write time. We currently focus on building good deltas
chain we build. Performance concern will be dealt with later in another
series.

diff --git a/mercurial/revlogutils/deltas.py b/mercurial/revlogutils/deltas.py
--- a/mercurial/revlogutils/deltas.py
+++ b/mercurial/revlogutils/deltas.py
@@ -9,6 +9,7 @@
 
 from __future__ import absolute_import
 
+import collections
 import heapq
 import struct
 
@@ -607,8 +608,18 @@ def _candidategroups(revlog, textlen, p1
 continue
 group.append(rev)
 if group:
+# XXX: in the sparse revlog case, group can become large,
+#  impacting performances. Some bounding or slicing mecanism
+#  would help to reduce this impact.
 yield tuple(group)
 
+def _findsnapshots(revlog, cache, start_rev):
+"""find snapshot from start_rev to tip"""
+deltaparent = revlog.deltaparent
+for rev in revlog.revs(start_rev):
+if deltaparent(rev) == nullrev:
+cache[nullrev].append(rev)
+
 def _rawgroups(revlog, p1, p2, cachedelta):
 """Provides group of revision to be tested as delta base
 
@@ -656,6 +667,18 @@ def _rawgroups(revlog, p1, p2, cachedelt
 for p in parents:
 bases.append(deltachain(p)[0])
 yield tuple(sorted(bases))
+# No suitable base found in the parent chain, search if any full
+# snapshots emitted since parent's base would be a suitable base for an
+# intermediate snapshot.
+#
+# It give a chance to reuse a delta chain unrelated to the current
+# revisions instead of starting our own. Without such re-use,
+# topological branches would keep reopening new full chains. Creating
+# more and more snapshot as the repository grow.
+snapfloor = min(bases) + 1
+snapshots = collections.defaultdict(list)
+_findsnapshots(revlog, snapshots, snapfloor)
+yield tuple(snapshots[nullrev])
 
 # other approach failed try against prev to hopefully save us a
 # fulltext.
diff --git a/tests/test-sparse-revlog.t b/tests/test-sparse-revlog.t
--- a/tests/test-sparse-revlog.t
+++ b/tests/test-sparse-revlog.t
@@ -77,7 +77,7 @@ repeatedly while some of it changes rare
   
 
   $ f -s .hg/store/data/*.d
-  .hg/store/data/_s_p_a_r_s_e-_r_e_v_l_o_g-_t_e_s_t-_f_i_l_e.d: size=72315280
+  .hg/store/data/_s_p_a_r_s_e-_r_e_v_l_o_g-_t_e_s_t-_f_i_l_e.d: size=67810463
   $ hg debugrevlog *
   format : 1
   flags  : generaldelta
@@ -89,36 +89,39 @@ repeatedly while some of it changes rare
   empty :0 ( 0.00%)
  text  :0 (100.00%)
  delta :0 (100.00%)
-  snapshot  :  145 ( 2.90%)
-lvl-0   : 15 ( 0.30%)
-lvl-1   :130 ( 2.60%)
-  deltas: 4856 (97.10%)
-  revision size : 

[PATCH 05 of 19] snapshot: try intermediate snapshot against parents' base

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536333449 14400
#  Fri Sep 07 11:17:29 2018 -0400
# Node ID c6dd1a01b22cd2649dc7c0dfe9981305b6e762d2
# Parent  607e4fcb774047629cea21bc4052ce8c35eab5d5
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
c6dd1a01b22c
snapshot: try intermediate snapshot against parents' base

# Regarding The Series Started By This Changeset

This is the first changesets of a group adjusting delta chain strategy to
build a useful chain of intermediate snapshots. The series will introduce a
full strategy to produce chains of multiple snapshots on top of which a
"usual" delta chain will be built.

That strategy will have multiple steps to maximize snapshot reuse, avoiding
pathological cases and improving overall compression in very branchy
repositories. An important property of sparse-revlog using such snapshot-chain
is that they can use very short delta chain without problematic impact on the
resulting compression. Shorter delta chains are important to achieve good
performance.

To make each step clear, we'll introduce them one by one.

See the end of this series for full details.

# Regarding This Changeset

Before this change, if we cannot store the current revision as a delta against
a "simple" candidate (p1, p2, prev), we created a new level-0 snapshot (also
called full snapshot).

As the first step, we introduce a simple strategy: try an intermediate level-1
snapshot against the chain base of the "current revision" parents.
The "current revision" is the one we are currently trying to store in the
revlog, triggering this search for a good delta base.
The first item in the chain is always a level-0 snapshot.

# Effect On The Test Repository

We can already see the effect on the test-repository. Most of the snapshots
have shifted from level 0 to level 1. The overall size has slightly decreased.
(However, keep in mind that this repository only emulates real data)

# Regarding Statistic

The current series focuses on improving the chain built. Improving the
performance of this logic will be done as a second step. Sparse-revlog is
still experimental and disabled by default.

We'll provide more statistic about resulting size and delta chain at the end
of this series.

diff --git a/mercurial/revlogutils/deltas.py b/mercurial/revlogutils/deltas.py
--- a/mercurial/revlogutils/deltas.py
+++ b/mercurial/revlogutils/deltas.py
@@ -618,8 +618,10 @@ def _rawgroups(revlog, p1, p2, cachedelt
 The group order aims at providing fast or small candidates first.
 """
 gdelta = revlog._generaldelta
+sparse = revlog._sparserevlog
 curr = len(revlog)
 prev = curr - 1
+deltachain = lambda rev: revlog._deltachain(rev)[0]
 
 # First we try to reuse a the delta contained in the bundle.
 # (or from the source revlog)
@@ -647,6 +649,14 @@ def _rawgroups(revlog, p1, p2, cachedelt
 # Test all parents (1 or 2), and keep the best candidate
 yield parents
 
+if sparse and parents:
+# See if we can use an existing snapshot in the parent chains to use as
+# a base for a new intermediate-snapshot
+bases = []
+for p in parents:
+bases.append(deltachain(p)[0])
+yield tuple(sorted(bases))
+
 # other approach failed try against prev to hopefully save us a
 # fulltext.
 yield (prev,)
diff --git a/tests/test-sparse-revlog.t b/tests/test-sparse-revlog.t
--- a/tests/test-sparse-revlog.t
+++ b/tests/test-sparse-revlog.t
@@ -77,7 +77,7 @@ repeatedly while some of it changes rare
   
 
   $ f -s .hg/store/data/*.d
-  .hg/store/data/_s_p_a_r_s_e-_r_e_v_l_o_g-_t_e_s_t-_f_i_l_e.d: size=74365490
+  .hg/store/data/_s_p_a_r_s_e-_r_e_v_l_o_g-_t_e_s_t-_f_i_l_e.d: size=72315280
   $ hg debugrevlog *
   format : 1
   flags  : generaldelta
@@ -89,33 +89,36 @@ repeatedly while some of it changes rare
   empty :0 ( 0.00%)
  text  :0 (100.00%)
  delta :0 (100.00%)
-  snapshot  :  101 ( 2.02%)
-lvl-0   :101 ( 2.02%)
-  deltas: 4900 (97.98%)
-  revision size : 74365490
-  snapshot  : 20307865 (27.31%)
-lvl-0   :   20307865 (27.31%)
-  deltas: 54057625 (72.69%)
+  snapshot  :  145 ( 2.90%)
+lvl-0   : 15 ( 0.30%)
+lvl-1   :130 ( 2.60%)
+  deltas: 4856 (97.10%)
+  revision size : 72315280
+  snapshot  : 18481085 (25.56%)
+lvl-0   :3016019 ( 4.17%)
+lvl-1   :   15465066 (21.39%)
+  deltas: 53834195 (74.44%)
   
   chunks: 5001
   0x78 (x)  : 5001 (100.00%)
-  chunks size   : 74365490
-  0x78 (x)  : 74365490 (100.00%)
+  chunks size   : 72315280
+  0x78 (x)  : 72315280 (100.00%)
   
-  avg chain length  :   23
+  avg chain length  :   18

[PATCH 04 of 19] sparse-revlog: add a test checking revlog deltas for a churning file

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536333449 14400
#  Fri Sep 07 11:17:29 2018 -0400
# Node ID 607e4fcb774047629cea21bc4052ce8c35eab5d5
# Parent  a4f94a5caf6f652ff4959daedddf1d88c6059f1b
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
607e4fcb7740
sparse-revlog: add a test checking revlog deltas for a churning file

The test repository contains 5000 revisions and is therefore slow to build:
five minutes with CHG, over fifteen minutes without. It is too slow to build
during the test.  Bundling all content produce a sizeable result, 20BM, too
large to be committed. Instead, we commit a script to build the expected
bundle and the test checks if the bundle is available. Any run of the script
will produce the same repository content, using resulting in the same hashes.

Using smaller repositories was tried, however, it misses most of the cases we
are planning to improve. Having them in a 5000 repository is already nice, we
usually see these case in repositories in the order of magnitude of one
million revisions.

This test will be very useful to check various changes strategy for building
delta to store in a sparse-revlog.

In this series we will focus our attention on the following metrics:

The ones that will impact the final storage performance (size, space):
* size of the revlog data file (".hg/store/data/*.d")
* chain length info

The ones that describe the deltas patterns:
* number of snapshot revision (and their level)
* size taken by snapshot revision (and their level)

diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -19,6 +19,7 @@ syntax: glob
 *.zip
 \#*\#
 .\#*
+tests/artifacts/cache/big-file-churn.hg
 tests/.coverage*
 tests/.testtimes*
 tests/.hypothesis
diff --git a/tests/artifacts/cache/big-file-churn.hg.md5 
b/tests/artifacts/cache/big-file-churn.hg.md5
new file mode 100644
--- /dev/null
+++ b/tests/artifacts/cache/big-file-churn.hg.md5
@@ -0,0 +1,1 @@
+fe0d0bb5979de50f4fed71bb9437764d
diff --git a/tests/artifacts/scripts/generate-churning-bundle.py 
b/tests/artifacts/scripts/generate-churning-bundle.py
new file mode 100755
--- /dev/null
+++ b/tests/artifacts/scripts/generate-churning-bundle.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+#
+# generate-branchy-bundle - generate a branch for a "large" branchy repository
+#
+# Copyright 2018 Octobus, cont...@octobus.net
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+#
+# This script generates a repository suitable for testing delta computation
+# strategies.
+#
+# The repository update a single "large" file with many updates. One fixed part
+# of the files always get updated while the rest of the lines get updated over
+# time. This update happens over many topological branches, some getting merged
+# back.
+#
+# Running with `chg` in your path and `CHGHG` set is recommended for speed.
+
+from __future__ import absolute_import, print_function
+
+import hashlib
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+
+BUNDLE_NAME = 'big-file-churn.hg'
+
+# constants for generating the repository
+NB_CHANGESET = 5000
+PERIOD_MERGING = 8
+PERIOD_BRANCHING = 7
+MOVE_BACK_MIN = 3
+MOVE_BACK_RANGE = 5
+
+# constants for generating the large file we keep updating
+#
+# At each revision, the beginning on the file change,
+# and set of other lines changes too.
+FILENAME='SPARSE-REVLOG-TEST-FILE'
+NB_LINES = 10500
+ALWAYS_CHANGE_LINES = 500
+FILENAME = 'SPARSE-REVLOG-TEST-FILE'
+OTHER_CHANGES = 300
+
+def nextcontent(previous_content):
+"""utility to produce a new file content from the previous one"""
+return hashlib.md5(previous_content).hexdigest()
+
+def filecontent(iteridx, oldcontent):
+"""generate a new file content
+
+The content is generated according the iteration index and previous
+content"""
+
+# initial call
+if iteridx is None:
+current = ''
+else:
+current = str(iteridx)
+
+for idx in xrange(NB_LINES):
+do_change_line = True
+if oldcontent is not None and ALWAYS_CHANGE_LINES < idx:
+do_change_line = not ((idx - iteridx) % OTHER_CHANGES)
+
+if do_change_line:
+to_write = current + '\n'
+current = nextcontent(current)
+else:
+to_write = oldcontent[idx]
+yield to_write
+
+def updatefile(filename, idx):
+"""update  to be at appropriate content for iteration """
+existing = None
+if idx is not None:
+with open(filename, 'rb') as old:
+existing = old.readlines()
+with open(filename, 'wb') as target:
+for line in filecontent(idx, existing):
+target.write(line)
+
+def hg(command, *args):
+"""call a mercurial command with appropriate config and argument"""
+env = os.environ.copy()
+if 

[PATCH 03 of 19] tests: add a `tests/artifacts/` directory

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1534589144 -7200
#  Sat Aug 18 12:45:44 2018 +0200
# Node ID a4f94a5caf6f652ff4959daedddf1d88c6059f1b
# Parent  2d50819efa01dbb1ba0e14e30486490cb90c002a
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
a4f94a5caf6f
tests: add a `tests/artifacts/` directory

That directory is meant to cache large items used by tests that are slow to
generate. See 'PURPOSE' file for details and next changesets for a first user.

diff --git a/tests/artifacts/PURPOSE b/tests/artifacts/PURPOSE
new file mode 100644
--- /dev/null
+++ b/tests/artifacts/PURPOSE
@@ -0,0 +1,9 @@
+This directory is meant to cache artifacts useful for tests (such as bundle).
+
+Those artifacts need to be cached because they are slow to regenerate on each
+test but too large to be tracked within the repository. They are not expected
+to change between each run and can be cached.
+
+The `./scripts/` contains code to generate the artifact while the `cache`
+directory contains resulting artifact.
+
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 01 of 19] revlog: drop duplicated code

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536087921 -7200
#  Tue Sep 04 21:05:21 2018 +0200
# Node ID bdcfd96d87254a508e85a6eb502ffe4c57845bc8
# Parent  6268fed317d04dd8f0430468818ea5d0529b6503
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
bdcfd96d8725
revlog: drop duplicated code

This code probably got duplicated by a rebase/evolve conflict. We drop the
extra copy, the other copy is right below.

This had no real effects since other logic ensure that we never test the same
revision twice.

diff --git a/mercurial/revlogutils/deltas.py b/mercurial/revlogutils/deltas.py
--- a/mercurial/revlogutils/deltas.py
+++ b/mercurial/revlogutils/deltas.py
@@ -621,19 +621,6 @@ def _rawgroups(revlog, p1, p2, cachedelt
 curr = len(revlog)
 prev = curr - 1
 
-# should we try to build a delta?
-if prev != nullrev and revlog._storedeltachains:
-tested = set()
-# This condition is true most of the time when processing
-# changegroup data into a generaldelta repo. The only time it
-# isn't true is if this is the first revision in a delta chain
-# or if ``format.generaldelta=true`` disabled ``lazydeltabase``.
-if cachedelta and gdelta and revlog._lazydeltabase:
-# Assume what we received from the server is a good choice
-# build delta will reuse the cache
-yield (cachedelta[0],)
-tested.add(cachedelta[0])
-
 # This condition is true most of the time when processing
 # changegroup data into a generaldelta repo. The only time it
 # isn't true is if this is the first revision in a delta chain
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 02 of 19] revlog: clarify the comment attached to delta reuse

2018-09-08 Thread Boris Feld
# HG changeset patch
# User Boris Feld 
# Date 1536089308 -7200
#  Tue Sep 04 21:28:28 2018 +0200
# Node ID 2d50819efa01dbb1ba0e14e30486490cb90c002a
# Parent  bdcfd96d87254a508e85a6eb502ffe4c57845bc8
# EXP-Topic sparse-snapshot
# Available At https://bitbucket.org/octobus/mercurial-devel/
#  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
2d50819efa01
revlog: clarify the comment attached to delta reuse

The previous version was a bit complicated and referred to a deprecated
configuration option.

diff --git a/mercurial/revlogutils/deltas.py b/mercurial/revlogutils/deltas.py
--- a/mercurial/revlogutils/deltas.py
+++ b/mercurial/revlogutils/deltas.py
@@ -621,10 +621,12 @@ def _rawgroups(revlog, p1, p2, cachedelt
 curr = len(revlog)
 prev = curr - 1
 
-# This condition is true most of the time when processing
-# changegroup data into a generaldelta repo. The only time it
-# isn't true is if this is the first revision in a delta chain
-# or if ``format.generaldelta=true`` disabled ``lazydeltabase``.
+# First we try to reuse a the delta contained in the bundle.
+# (or from the source revlog)
+#
+# This logic only applies to general delta repositories and can be disabled
+# through configuration. Disabling reuse of source delta is useful when
+# we want to make sure we recomputed "optimal" deltas.
 if cachedelta and gdelta and revlog._lazydeltabase:
 # Assume what we received from the server is a good choice
 # build delta will reuse the cache
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D4516: contrib: use a monotonic timer in catapipe

2018-09-08 Thread lothiraldan (Boris Feld)
lothiraldan created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  As spotted by Gregory, we should use a monotonic timer to get better timings.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D4516

AFFECTED FILES
  contrib/catapipe.py

CHANGE DETAILS

diff --git a/contrib/catapipe.py b/contrib/catapipe.py
--- a/contrib/catapipe.py
+++ b/contrib/catapipe.py
@@ -29,17 +29,21 @@
 from __future__ import absolute_import, print_function
 
 import argparse
-import datetime
 import json
 import os
+import timeit
 
 _TYPEMAP = {
 'START': 'B',
 'END': 'E',
 }
 
 _threadmap = {}
 
+# Timeit already contains the whole logic about which timer to use based on
+# Python version and OS
+timer = timeit.default_timer
+
 def main():
 parser = argparse.ArgumentParser()
 parser.add_argument('pipe', type=str, nargs=1,
@@ -55,12 +59,12 @@
 try:
 with open(fn) as f, open(args.output, 'w') as out:
 out.write('[\n')
-start = datetime.datetime.now()
+start = timer()
 while True:
 ev = f.readline().strip()
 if not ev:
 continue
-now = datetime.datetime.now()
+now = timer()
 if args.debug:
 print(ev)
 verb, session, label = ev.split(' ', 2)



To: lothiraldan, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D4515: contrib: fix catapipe output argument documentation

2018-09-08 Thread lothiraldan (Boris Feld)
lothiraldan created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D4515

AFFECTED FILES
  contrib/catapipe.py

CHANGE DETAILS

diff --git a/contrib/catapipe.py b/contrib/catapipe.py
--- a/contrib/catapipe.py
+++ b/contrib/catapipe.py
@@ -45,7 +45,8 @@
 parser.add_argument('pipe', type=str, nargs=1,
 help='Path of named pipe to create and listen on.')
 parser.add_argument('output', default='trace.json', type=str, nargs='?',
-help='Path of named pipe to create and listen on.')
+help='Path of json file to create where the traces '
+ 'will be stored.')
 parser.add_argument('--debug', default=False, action='store_true',
 help='Print useful debug messages')
 args = parser.parse_args()



To: lothiraldan, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D4514: tracing: trace command function execution

2018-09-08 Thread lothiraldan (Boris Feld)
lothiraldan created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D4514

AFFECTED FILES
  mercurial/dispatch.py

CHANGE DETAILS

diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py
--- a/mercurial/dispatch.py
+++ b/mercurial/dispatch.py
@@ -992,7 +992,8 @@
 def _runcommand(ui, options, cmd, cmdfunc):
 """Run a command function, possibly with profiling enabled."""
 try:
-return cmdfunc()
+with tracing.log("Running %s command" % cmd):
+return cmdfunc()
 except error.SignatureError:
 raise error.CommandError(cmd, _('invalid arguments'))
 



To: lothiraldan, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D4513: extension: add a summary of total loading time per extension

2018-09-08 Thread lothiraldan (Boris Feld)
lothiraldan created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D4513

AFFECTED FILES
  mercurial/extensions.py
  tests/test-bad-extension.t
  tests/test-extension-timing.t

CHANGE DETAILS

diff --git a/tests/test-extension-timing.t b/tests/test-extension-timing.t
--- a/tests/test-extension-timing.t
+++ b/tests/test-extension-timing.t
@@ -68,6 +68,7 @@
   debug.extensions: > remaining aftercallbacks completed in * (glob)
   debug.extensions: - loading extension registration objects
   debug.extensions: > extension registration object loading took * (glob)
+  debug.extensions: > extension foobar take a total of * to load (glob)
   debug.extensions: extension loading complete
   debug.extensions: loading additional extensions
   debug.extensions: - processing 1 entries
diff --git a/tests/test-bad-extension.t b/tests/test-bad-extension.t
--- a/tests/test-bad-extension.t
+++ b/tests/test-bad-extension.t
@@ -122,6 +122,8 @@
   debug.extensions: > remaining aftercallbacks completed in * (glob)
   debug.extensions: - loading extension registration objects
   debug.extensions: > extension registration object loading took * (glob)
+  debug.extensions: > extension baddocext take a total of * to load (glob)
+  debug.extensions: > extension gpg take a total of * to load (glob)
   debug.extensions: extension loading complete
 #endif
 
diff --git a/mercurial/extensions.py b/mercurial/extensions.py
--- a/mercurial/extensions.py
+++ b/mercurial/extensions.py
@@ -166,7 +166,7 @@
 _rejectunicode(t, o._table)
 _validatecmdtable(ui, getattr(mod, 'cmdtable', {}))
 
-def load(ui, name, path, log=lambda *a: None):
+def load(ui, name, path, log=lambda *a: None, loadingtime=None):
 if name.startswith('hgext.') or name.startswith('hgext/'):
 shortname = name[6:]
 else:
@@ -180,6 +180,8 @@
 with util.timedcm('load extension %r', shortname) as stats:
 mod = _importext(name, path, bind(_reportimporterror, ui))
 log('  > %r extension loaded in %s\n', shortname, stats)
+if loadingtime is not None:
+loadingtime[shortname] += stats.elapsed
 
 # Before we do anything with the extension, check against minimum stated
 # compatibility. This gives extension authors a mechanism to have their
@@ -237,6 +239,7 @@
 msg % values, label='debug.extensions')
 else:
 log = lambda *a, **kw: None
+loadingtime = collections.defaultdict(int)
 result = ui.configitems("extensions")
 if whitelist is not None:
 result = [(k, v) for (k, v) in result if k in whitelist]
@@ -252,7 +255,7 @@
 _disabledextensions[name] = path[1:]
 continue
 try:
-load(ui, name, path, log)
+load(ui, name, path, log, loadingtime)
 except Exception as inst:
 msg = stringutil.forcebytestr(inst)
 if path:
@@ -292,6 +295,7 @@
 log('- the %r extension uisetup failed\n', name)
 broken.add(name)
 log('  > uisetup for %r took %s\n', name, stats)
+loadingtime[name] += stats.elapsed
 log('> all uisetup took %s\n', alluisetupstats)
 
 log('- executing extsetup hooks\n')
@@ -305,6 +309,7 @@
 log('- the %r extension extsetup failed\n', name)
 broken.add(name)
 log('  > extsetup for %r took %s\n', name, stats)
+loadingtime[name] += stats.elapsed
 log('> all extsetup took %s\n', allextetupstats)
 
 for name in broken:
@@ -360,6 +365,12 @@
 with util.timedcm('load registration objects') as stats:
 _loadextra(ui, newindex, extraloaders)
 log('> extension registration object loading took %s\n', stats)
+
+# Report per extension loading time (except reposetup)
+for name in sorted(loadingtime):
+extension_msg = '> extension %s take a total of %s to load\n'
+log(extension_msg, name, util.timecount(loadingtime[name]))
+
 log('extension loading complete\n')
 
 def _loadextra(ui, newindex, extraloaders):



To: lothiraldan, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D4512: extensions: trace the total time of running all reposetup callbacks

2018-09-08 Thread lothiraldan (Boris Feld)
lothiraldan created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D4512

AFFECTED FILES
  mercurial/hg.py
  tests/test-extension-timing.t

CHANGE DETAILS

diff --git a/tests/test-extension-timing.t b/tests/test-extension-timing.t
--- a/tests/test-extension-timing.t
+++ b/tests/test-extension-timing.t
@@ -87,6 +87,7 @@
   reposetup called for a
   ui == repo.ui
   debug.extensions:   > reposetup for 'foobar' took * (glob)
+  debug.extensions: > all reposetup took * (glob)
   Foo
 
   $ cd ..
diff --git a/mercurial/hg.py b/mercurial/hg.py
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -171,13 +171,15 @@
 for f in presetupfuncs or []:
 f(ui, obj)
 log('- executing reposetup hooks\n')
-for name, module in extensions.extensions(ui):
-log('  - running reposetup for %s\n' % (name,))
-hook = getattr(module, 'reposetup', None)
-if hook:
-with util.timedcm('reposetup %r', name) as stats:
-hook(ui, obj)
-log('  > reposetup for %r took %s\n', name, stats)
+with util.timedcm('all reposetup') as allreposetupstats:
+for name, module in extensions.extensions(ui):
+log('  - running reposetup for %s\n' % (name,))
+hook = getattr(module, 'reposetup', None)
+if hook:
+with util.timedcm('reposetup %r', name) as stats:
+hook(ui, obj)
+log('  > reposetup for %r took %s\n', name, stats)
+log('> all reposetup took %s\n', allreposetupstats)
 if not obj.local():
 for f in wirepeersetupfuncs:
 f(ui, obj)



To: lothiraldan, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D4511: extensions: trace the total time of running all extsetup callbacks

2018-09-08 Thread lothiraldan (Boris Feld)
lothiraldan created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D4511

AFFECTED FILES
  mercurial/extensions.py
  tests/test-bad-extension.t
  tests/test-extension-timing.t

CHANGE DETAILS

diff --git a/tests/test-extension-timing.t b/tests/test-extension-timing.t
--- a/tests/test-extension-timing.t
+++ b/tests/test-extension-timing.t
@@ -63,6 +63,7 @@
   debug.extensions: - executing extsetup hooks
   debug.extensions:   - running extsetup for 'foobar'
   debug.extensions:   > extsetup for 'foobar' took * (glob)
+  debug.extensions: > all extsetup took * (glob)
   debug.extensions: - executing remaining aftercallbacks
   debug.extensions: > remaining aftercallbacks completed in * (glob)
   debug.extensions: - loading extension registration objects
@@ -75,6 +76,7 @@
   debug.extensions: - executing uisetup hooks
   debug.extensions: > all uisetup took * (glob)
   debug.extensions: - executing extsetup hooks
+  debug.extensions: > all extsetup took * (glob)
   debug.extensions: - executing remaining aftercallbacks
   debug.extensions: > remaining aftercallbacks completed in * (glob)
   debug.extensions: - loading extension registration objects
diff --git a/tests/test-bad-extension.t b/tests/test-bad-extension.t
--- a/tests/test-bad-extension.t
+++ b/tests/test-bad-extension.t
@@ -117,6 +117,7 @@
   debug.extensions:   > extsetup for 'gpg' took * (glob)
   debug.extensions:   - running extsetup for 'baddocext'
   debug.extensions:   > extsetup for 'baddocext' took * (glob)
+  debug.extensions: > all extsetup took * (glob)
   debug.extensions: - executing remaining aftercallbacks
   debug.extensions: > remaining aftercallbacks completed in * (glob)
   debug.extensions: - loading extension registration objects
diff --git a/mercurial/extensions.py b/mercurial/extensions.py
--- a/mercurial/extensions.py
+++ b/mercurial/extensions.py
@@ -295,15 +295,17 @@
 log('> all uisetup took %s\n', alluisetupstats)
 
 log('- executing extsetup hooks\n')
-for name in _order[newindex:]:
-if name in broken:
-continue
-log('  - running extsetup for %r\n', name)
-with util.timedcm('extsetup %r', name) as stats:
-if not _runextsetup(name, ui):
-log('- the %r extension extsetup failed\n', name)
-broken.add(name)
-log('  > extsetup for %r took %s\n', name, stats)
+with util.timedcm('all extsetup') as allextetupstats:
+for name in _order[newindex:]:
+if name in broken:
+continue
+log('  - running extsetup for %r\n', name)
+with util.timedcm('extsetup %r', name) as stats:
+if not _runextsetup(name, ui):
+log('- the %r extension extsetup failed\n', name)
+broken.add(name)
+log('  > extsetup for %r took %s\n', name, stats)
+log('> all extsetup took %s\n', allextetupstats)
 
 for name in broken:
 log('- disabling broken %r extension\n', name)



To: lothiraldan, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D4510: extensions: trace the total time of running all uisetup callbacks

2018-09-08 Thread lothiraldan (Boris Feld)
lothiraldan created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D4510

AFFECTED FILES
  mercurial/extensions.py
  tests/test-bad-extension.t
  tests/test-extension-timing.t

CHANGE DETAILS

diff --git a/tests/test-extension-timing.t b/tests/test-extension-timing.t
--- a/tests/test-extension-timing.t
+++ b/tests/test-extension-timing.t
@@ -59,6 +59,7 @@
   uisetup called
   uisetup called [status]
   debug.extensions:   > uisetup for 'foobar' took * (glob)
+  debug.extensions: > all uisetup took * (glob)
   debug.extensions: - executing extsetup hooks
   debug.extensions:   - running extsetup for 'foobar'
   debug.extensions:   > extsetup for 'foobar' took * (glob)
@@ -72,6 +73,7 @@
   debug.extensions: > loaded 0 extensions, total time * (glob)
   debug.extensions: - loading configtable attributes
   debug.extensions: - executing uisetup hooks
+  debug.extensions: > all uisetup took * (glob)
   debug.extensions: - executing extsetup hooks
   debug.extensions: - executing remaining aftercallbacks
   debug.extensions: > remaining aftercallbacks completed in * (glob)
diff --git a/tests/test-bad-extension.t b/tests/test-bad-extension.t
--- a/tests/test-bad-extension.t
+++ b/tests/test-bad-extension.t
@@ -111,6 +111,7 @@
   debug.extensions:   > uisetup for 'gpg' took * (glob)
   debug.extensions:   - running uisetup for 'baddocext'
   debug.extensions:   > uisetup for 'baddocext' took * (glob)
+  debug.extensions: > all uisetup took * (glob)
   debug.extensions: - executing extsetup hooks
   debug.extensions:   - running extsetup for 'gpg'
   debug.extensions:   > extsetup for 'gpg' took * (glob)
diff --git a/mercurial/extensions.py b/mercurial/extensions.py
--- a/mercurial/extensions.py
+++ b/mercurial/extensions.py
@@ -284,13 +284,15 @@
 
 broken = set()
 log('- executing uisetup hooks\n')
-for name in _order[newindex:]:
-log('  - running uisetup for %r\n', name)
-with util.timedcm('uisetup %r', name) as stats:
-if not _runuisetup(name, ui):
-log('- the %r extension uisetup failed\n', name)
-broken.add(name)
-log('  > uisetup for %r took %s\n', name, stats)
+with util.timedcm('all uisetup') as alluisetupstats:
+for name in _order[newindex:]:
+log('  - running uisetup for %r\n', name)
+with util.timedcm('uisetup %r', name) as stats:
+if not _runuisetup(name, ui):
+log('- the %r extension uisetup failed\n', name)
+broken.add(name)
+log('  > uisetup for %r took %s\n', name, stats)
+log('> all uisetup took %s\n', alluisetupstats)
 
 log('- executing extsetup hooks\n')
 for name in _order[newindex:]:



To: lothiraldan, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D4509: extensions: add timing for extensions reposetup

2018-09-08 Thread lothiraldan (Boris Feld)
lothiraldan created this revision.
Herald added subscribers: mercurial-devel, mjpieters.
Herald added a reviewer: hg-reviewers.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D4509

AFFECTED FILES
  mercurial/hg.py
  tests/test-extension-timing.t
  tests/test-extension.t

CHANGE DETAILS

diff --git a/tests/test-extension.t b/tests/test-extension-timing.t
copy from tests/test-extension.t
copy to tests/test-extension-timing.t
--- a/tests/test-extension.t
+++ b/tests/test-extension-timing.t
@@ -40,1716 +40,51 @@
 
   $ echo '[extensions]' >> $HGRCPATH
   $ echo "foobar = $abspath" >> $HGRCPATH
-  $ hg foo
+
+Test extension setup timings
+
+  $ hg foo --traceback --config devel.debug.extensions=yes --debug 2>&1
+  debug.extensions: loading extensions
+  debug.extensions: - processing 1 entries
+  debug.extensions:   - loading extension: 'foobar'
+  debug.extensions:   > 'foobar' extension loaded in * (glob)
+  debug.extensions: - validating extension tables: 'foobar'
+  debug.extensions: - invoking registered callbacks: 'foobar'
+  debug.extensions: > callbacks completed in * (glob)
+  debug.extensions: > loaded 1 extensions, total time * (glob)
+  debug.extensions: - loading configtable attributes
+  debug.extensions: - executing uisetup hooks
+  debug.extensions:   - running uisetup for 'foobar'
+  uisetup called [debug]
   uisetup called
   uisetup called [status]
-  reposetup called for a
-  ui == repo.ui
-  reposetup called for a (chg !)
-  ui == repo.ui (chg !)
-  Foo
-  $ hg foo --quiet
-  uisetup called (no-chg !)
-  reposetup called for a (chg !)
-  ui == repo.ui
-  Foo
-  $ hg foo --debug
-  uisetup called [debug] (no-chg !)
-  uisetup called (no-chg !)
-  uisetup called [status] (no-chg !)
-  reposetup called for a (chg !)
-  ui == repo.ui
-  Foo
-
-  $ cd ..
-  $ hg clone a b
-  uisetup called (no-chg !)
-  uisetup called [status] (no-chg !)
-  reposetup called for a
-  ui == repo.ui
-  reposetup called for b
-  ui == repo.ui
-  updating to branch default
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-
-  $ hg bar
-  uisetup called (no-chg !)
-  uisetup called [status] (no-chg !)
-  Bar
-  $ echo 'foobar = !' >> $HGRCPATH
-
-module/__init__.py-style
-
-  $ echo "barfoo = $barfoopath" >> $HGRCPATH
-  $ cd a
-  $ hg foo
-  uisetup called
-  uisetup called [status]
+  debug.extensions:   > uisetup for 'foobar' took * (glob)
+  debug.extensions: - executing extsetup hooks
+  debug.extensions:   - running extsetup for 'foobar'
+  debug.extensions:   > extsetup for 'foobar' took * (glob)
+  debug.extensions: - executing remaining aftercallbacks
+  debug.extensions: > remaining aftercallbacks completed in * (glob)
+  debug.extensions: - loading extension registration objects
+  debug.extensions: > extension registration object loading took * (glob)
+  debug.extensions: extension loading complete
+  debug.extensions: loading additional extensions
+  debug.extensions: - processing 1 entries
+  debug.extensions: > loaded 0 extensions, total time * (glob)
+  debug.extensions: - loading configtable attributes
+  debug.extensions: - executing uisetup hooks
+  debug.extensions: - executing extsetup hooks
+  debug.extensions: - executing remaining aftercallbacks
+  debug.extensions: > remaining aftercallbacks completed in * (glob)
+  debug.extensions: - loading extension registration objects
+  debug.extensions: > extension registration object loading took * (glob)
+  debug.extensions: extension loading complete
+  debug.extensions: - executing reposetup hooks
+  debug.extensions:   - running reposetup for foobar
   reposetup called for a
   ui == repo.ui
-  reposetup called for a (chg !)
-  ui == repo.ui (chg !)
+  debug.extensions:   > reposetup for 'foobar' took * (glob)
   Foo
-  $ echo 'barfoo = !' >> $HGRCPATH
-
-Check that extensions are loaded in phases:
-
-  $ cat > foo.py < import os
-  > name = os.path.basename(__file__).rsplit('.', 1)[0]
-  > print("1) %s imported" % name)
-  > def uisetup(ui):
-  > print("2) %s uisetup" % name)
-  > def extsetup():
-  > print("3) %s extsetup" % name)
-  > def reposetup(ui, repo):
-  >print("4) %s reposetup" % name)
-  > 
-  > # custom predicate to check registration of functions at loading
-  > from mercurial import (
-  > registrar,
-  > smartset,
-  > )
-  > revsetpredicate = registrar.revsetpredicate()
-  > @revsetpredicate(name, safe=True) # safe=True for query via hgweb
-  > def custompredicate(repo, subset, x):
-  > return smartset.baseset([r for r in subset if r in {0}])
-  > EOF
-
-  $ cp foo.py bar.py
-  $ echo 'foo = foo.py' >> $HGRCPATH
-  $ echo 'bar = bar.py' >> $HGRCPATH
-
-Check normal command's load order of extensions and registration of functions
-
-  $ hg log -r "foo() and bar()" -q
-  1) foo imported
-  1) bar imported
-  2) foo uisetup
-  2) bar uisetup
-  3) foo extsetup
-  3) bar extsetup
-  4) foo reposetup
-  4) bar reposetup
-  0:c24b9ac61126
-

D4507: ancestors: add nullrev to set from the beginning

2018-09-08 Thread martinvonz (Martin von Zweigbergk)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG8eb2145ff0fb: ancestors: add nullrev to set from the 
beginning (authored by martinvonz, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D4507?vs=10840=10842

REVISION DETAIL
  https://phab.mercurial-scm.org/D4507

AFFECTED FILES
  mercurial/ancestor.py

CHANGE DETAILS

diff --git a/mercurial/ancestor.py b/mercurial/ancestor.py
--- a/mercurial/ancestor.py
+++ b/mercurial/ancestor.py
@@ -311,15 +311,14 @@
 
 If inclusive is True, the source revisions are also yielded. The
 reverse revision number order is still enforced."""
-seen = set()
+seen = {nullrev}
 revs = self._initrevs
 
 parentrevs = self._parentrevs
 stoprev = self._stoprev
 schedule = heapq.heappush
 nextitem = heapq.heappop
 see = seen.add
-see(nullrev)
 
 if self._inclusive:
 visit = [-r for r in revs]



To: martinvonz, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


Re: [PATCH] phase: report number of non-public changeset alongside the new range

2018-09-08 Thread Yuya Nishihara
On Fri, 07 Sep 2018 11:07:52 -0400, Boris Feld wrote:
> # HG changeset patch
> # User Boris Feld 
> # Date 1535586801 -7200
> #  Thu Aug 30 01:53:21 2018 +0200
> # Node ID 5f931c1d1422e9a8c08cca3a59804b25a1183449
> # Parent  ab452995eafffa69c34e863e4d8c03e163d8f3ad
> # EXP-Topic phase-report
> # Available At https://bitbucket.org/octobus/mercurial-devel/
> #  hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 
> 5f931c1d1422
> phase: report number of non-public changeset alongside the new range

Queued, thanks.

> -repo.ui.status(_('new changesets %s\n') % revrange)
> +draft = len(repo.revs('%ld and draft()', revs))
> +secret = len(repo.revs('%ld and secret()', revs))
> +if not (draft or secret):
> +msg = _('new changesets %s\n') % revrange
> +elif draft and secret:
> +msg = _('new changesets %s (%d drafts, %d secrets)\n')
> +msg %= (revrange, draft, secret)
> +elif draft:
> +msg = _('new changesets %s (%d drafts)\n')
> +msg %= (revrange, draft)
> +elif secret:
> +msg = _('new changesets %s (%d secrets)\n')
> +msg %= (revrange, secret)

Added "else: raise ProgrammingError" to make sure msg is otherwise defined.

> +repo.ui.status(msg)

Can you add tests for 'draft and secret' and 'not draft and secret' cases?
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


Re: [PATCH 1 of 2] tests: conditionalize an error message about unlinking a non empty directory

2018-09-08 Thread Yuya Nishihara
On Sat, 08 Sep 2018 00:04:41 -0400, Matt Harbison wrote:
> # HG changeset patch
> # User Matt Harbison 
> # Date 1536377989 14400
> #  Fri Sep 07 23:39:49 2018 -0400
> # Node ID cf6130dd324b0eed4c902b45df503be429fe413f
> # Parent  a60dae060bc83e773bdbafb2432bc4cf387ebce2
> tests: conditionalize an error message about unlinking a non empty directory

Queued, thanks.
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D4508: lazyancestors: reuse __iter__ implementation in __contains__

2018-09-08 Thread yuja (Yuya Nishihara)
yuja added a comment.


  > +self._containsseen = set()
  >  +self._containsiter = iter(self)
  
  Perhaps __iter__() needs to be extracted to a free function to avoid
  reference cycle.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D4508

To: martinvonz, #hg-reviewers
Cc: yuja, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


Re: D4508: lazyancestors: reuse __iter__ implementation in __contains__

2018-09-08 Thread Yuya Nishihara
> +self._containsseen = set()
> +self._containsiter = iter(self)

Perhaps __iter__() needs to be extracted to a free function to avoid
reference cycle.
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D4508: lazyancestors: reuse __iter__ implementation in __contains__

2018-09-08 Thread martinvonz (Martin von Zweigbergk)
martinvonz created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  There was a comment in the code that said "Trying to do both __iter__
  and __contains__ using the same visit heap and seen set is complex
  enough that it slows down both. Keep them separate.". However, it
  seems easy and efficient to make __contains__ keep an iterator across
  calls.
  
  I couldn't measure any slowdown from `hg bundle --all` (which seem to
  call lazyancestors.__contains__ frequently).
  
  1HG: --

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D4508

AFFECTED FILES
  mercurial/ancestor.py

CHANGE DETAILS

diff --git a/mercurial/ancestor.py b/mercurial/ancestor.py
--- a/mercurial/ancestor.py
+++ b/mercurial/ancestor.py
@@ -277,18 +277,8 @@
 self._stoprev = stoprev
 self._inclusive = inclusive
 
-# Initialize data structures for __contains__.
-# For __contains__, we use a heap rather than a deque because
-# (a) it minimizes the number of parentrevs calls made
-# (b) it makes the loop termination condition obvious
-# Python's heap is a min-heap. Multiply all values by -1 to convert it
-# into a max-heap.
-self._containsvisit = [-rev for rev in revs]
-heapq.heapify(self._containsvisit)
-if inclusive:
-self._containsseen = set(revs)
-else:
-self._containsseen = set()
+self._containsseen = set()
+self._containsiter = iter(self)
 
 def __nonzero__(self):
 """False if the set is empty, True otherwise."""
@@ -344,35 +334,29 @@
 
 def __contains__(self, target):
 """Test whether target is an ancestor of self._initrevs."""
-# Trying to do both __iter__ and __contains__ using the same visit
-# heap and seen set is complex enough that it slows down both. Keep
-# them separate.
 seen = self._containsseen
 if target in seen:
 return True
+iter = self._containsiter
+if iter is None:
+# Iterator exhausted
+return False
 # Only integer target is valid, but some callers expect 'None in self'
 # to be False. So we explicitly allow it.
 if target is None:
 return False
 
-parentrevs = self._parentrevs
-visit = self._containsvisit
-stoprev = self._stoprev
-heappop = heapq.heappop
-heappush = heapq.heappush
 see = seen.add
-
-targetseen = False
-
-while visit and -visit[0] > target and not targetseen:
-for parent in parentrevs(-heappop(visit)):
-if parent < stoprev or parent in seen:
-continue
-# We need to make sure we push all parents into the heap so
-# that we leave it in a consistent state for future calls.
-heappush(visit, -parent)
-see(parent)
-if parent == target:
-targetseen = True
-
-return targetseen
+try:
+while True:
+rev = next(iter)
+see(rev)
+if rev == target:
+return True
+if rev < target:
+return False
+except StopIteration:
+# Set to None to indicate fast-path can be used next time, and to
+# free up memory.
+self._containsiter = None
+return False



To: martinvonz, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D4507: ancestors: add nullrev to set from the beginning

2018-09-08 Thread martinvonz (Martin von Zweigbergk)
martinvonz created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D4507

AFFECTED FILES
  mercurial/ancestor.py

CHANGE DETAILS

diff --git a/mercurial/ancestor.py b/mercurial/ancestor.py
--- a/mercurial/ancestor.py
+++ b/mercurial/ancestor.py
@@ -311,15 +311,14 @@
 
 If inclusive is True, the source revisions are also yielded. The
 reverse revision number order is still enforced."""
-seen = set()
+seen = {nullrev}
 revs = self._initrevs
 
 parentrevs = self._parentrevs
 stoprev = self._stoprev
 schedule = heapq.heappush
 nextitem = heapq.heappop
 see = seen.add
-see(nullrev)
 
 if self._inclusive:
 visit = [-r for r in revs]



To: martinvonz, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


Re: [PATCH 2 of 2] ancestor: filter out initial revisions lower than stoprev

2018-09-08 Thread Martin von Zweigbergk via Mercurial-devel
On Fri, Sep 7, 2018 at 9:50 PM Yuya Nishihara  wrote:

> On Fri, 7 Sep 2018 20:49:32 -0700, Martin von Zweigbergk wrote:
> > Good catch. However, I think the docstring needs updating. I think it
> > explicitly said that inclusive mode emits the initial revs ignoring
> > stoprev.
>
> That part was for __iter__(), and was rewritten by Boris' patch.


Ah, right, I had already forgotten that. Sorry about the noise.

I'm queuing this series, thanks.
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel