view tests/test-fileset.t @ 46582:b0a3ca02d17a

copies-rust: implement PartialEqual manually Now that we know that each (dest, rev) pair has at most a unique CopySource, we can simplify comparison a lot. This "simple" step buy a good share of the previous slowdown back in some case: Repo Case Source-Rev Dest-Rev # of revisions old time new time Difference Factor time per rev --------------------------------------------------------------------------------------------------------------------------------------------------------------- mozilla-try x00000_revs_x00000_added_x000_copies 9b2a99adc05e 8e29777b48e6 : 382065 revs, 43.304637 s, 34.443661 s, -8.860976 s, × 0.7954, 90 µs/rev Full benchmark: Repo Case Source-Rev Dest-Rev # of revisions old time new time Difference Factor time per rev --------------------------------------------------------------------------------------------------------------------------------------------------------------- mercurial x_revs_x_added_0_copies ad6b123de1c7 39cfcef4f463 : 1 revs, 0.000043 s, 0.000043 s, +0.000000 s, × 1.0000, 43 µs/rev mercurial x_revs_x_added_x_copies 2b1c78674230 0c1d10351869 : 6 revs, 0.000114 s, 0.000117 s, +0.000003 s, × 1.0263, 19 µs/rev mercurial x000_revs_x000_added_x_copies 81f8ff2a9bf2 dd3267698d84 : 1032 revs, 0.004937 s, 0.004892 s, -0.000045 s, × 0.9909, 4 µs/rev pypy x_revs_x_added_0_copies aed021ee8ae8 099ed31b181b : 9 revs, 0.000339 s, 0.000196 s, -0.000143 s, × 0.5782, 21 µs/rev pypy x_revs_x000_added_0_copies 4aa4e1f8e19a 359343b9ac0e : 1 revs, 0.000049 s, 0.000050 s, +0.000001 s, × 1.0204, 50 µs/rev pypy x_revs_x_added_x_copies ac52eb7bbbb0 72e022663155 : 7 revs, 0.000202 s, 0.000117 s, -0.000085 s, × 0.5792, 16 µs/rev pypy x_revs_x00_added_x_copies c3b14617fbd7 ace7255d9a26 : 1 revs, 0.000409 s, 0.6f1f4a s, -0.000087 s, × 0.7873, 322 µs/rev pypy x_revs_x000_added_x000_copies df6f7a526b60 a83dc6a2d56f : 6 revs, 0.011984 s, 0.011949 s, -0.000035 s, × 0.9971, 1991 µs/rev pypy x000_revs_xx00_added_0_copies 89a76aede314 2f22446ff07e : 4785 revs, 0.050820 s, 0.050802 s, -0.000018 s, × 0.9996, 10 µs/rev pypy x000_revs_x000_added_x_copies 8a3b5bfd266e 2c68e87c3efe : 6780 revs, 0.087953 s, 0.088090 s, +0.000137 s, × 1.0016, 12 µs/rev pypy x000_revs_x000_added_x000_copies 89a76aede314 7b3dda341c84 : 5441 revs, 0.062902 s, 0.062079 s, -0.000823 s, × 0.9869, 11 µs/rev pypy x0000_revs_x_added_0_copies d1defd0dc478 c9cb1334cc78 : 43645 revs, 0.679234 s, 0.635337 s, -0.043897 s, × 0.9354, 14 µs/rev pypy x0000_revs_xx000_added_0_copies bf2c629d0071 4ffed77c095c : 2 revs, 0.013095 s, 0.013262 s, +0.000167 s, × 1.0128, 6631 µs/rev pypy x0000_revs_xx000_added_x000_copies 08ea3258278e d9fa043f30c0 : 11316 revs, 0.120910 s, 0.120085 s, -0.000825 s, × 0.9932, 10 µs/rev netbeans x_revs_x_added_0_copies fb0955ffcbcd a01e9239f9e7 : 2 revs, 0.000087 s, 0.000085 s, -0.000002 s, × 0.9770, 42 µs/rev netbeans x_revs_x000_added_0_copies 6f360122949f 20eb231cc7d0 : 2 revs, 0.000107 s, 0.000110 s, +0.000003 s, × 1.0280, 55 µs/rev netbeans x_revs_x_added_x_copies 1ada3faf6fb6 5a39d12eecf4 : 3 revs, 0.000186 s, 0.000177 s, -0.000009 s, × 0.9516, 59 µs/rev netbeans x_revs_x00_added_x_copies 35be93ba1e2c 9eec5e90c05f : 9 revs, 0.000754 s, 0.000743 s, -0.000011 s, × 0.9854, 82 µs/rev netbeans x000_revs_xx00_added_0_copies eac3045b4fdd 51d4ae7f1290 : 1421 revs, 0.010443 s, 0.010168 s, -0.000275 s, × 0.9737, 7 µs/rev netbeans x000_revs_x000_added_x_copies e2063d266acd 6081d72689dc : 1533 revs, 0.015697 s, 0.015946 s, +0.000249 s, × 1.0159, 10 µs/rev netbeans x000_revs_x000_added_x000_copies ff453e9fee32 411350406ec2 : 5750 revs, 0.063528 s, 0.062712 s, -0.000816 s, × 0.9872, 10 µs/rev netbeans x0000_revs_xx000_added_x000_copies 588c2d1ced70 1aad62e59ddd : 66949 revs, 0.545515 s, 0.523832 s, -0.021683 s, × 0.9603, 7 µs/rev mozilla-central x_revs_x_added_0_copies 3697f962bb7b 7015fcdd43a2 : 2 revs, 0.000089 s, 0.000090 s, +0.000001 s, × 1.0112, 45 µs/rev mozilla-central x_revs_x000_added_0_copies dd390860c6c9 40d0c5bed75d : 8 revs, 0.000265 s, 0.000264 s, -0.000001 s, × 0.9962, 33 µs/rev mozilla-central x_revs_x_added_x_copies 8d198483ae3b 14207ffc2b2f : 9 revs, 0.000381 s, 0.000187 s, -0.000194 s, × 0.4908, 20 µs/rev mozilla-central x_revs_x00_added_x_copies 98cbc58cc6bc 446a150332c3 : 7 revs, 0.000672 s, 0.000665 s, -0.000007 s, × 0.9896, 95 µs/rev mozilla-central x_revs_x000_added_x000_copies 3c684b4b8f68 0a5e72d1b479 : 3 revs, 0.003497 s, 0.003556 s, +0.000059 s, × 1.0169, 1185 µs/rev mozilla-central x_revs_x0000_added_x0000_copies effb563bb7e5 c07a39dc4e80 : 6 revs, 0.073204 s, 0.071345 s, -0.001859 s, × 0.9746, 11890 µs/rev mozilla-central x000_revs_xx00_added_0_copies 6100d773079a 04a55431795e : 1593 revs, 0.006482 s, 0.006551 s, +0.000069 s, × 1.0106, 4 µs/rev mozilla-central x000_revs_x000_added_x_copies 9f17a6fc04f9 2d37b966abed : 41 revs, 0.005066 s, 0.005078 s, +0.000012 s, × 1.0024, 123 µs/rev mozilla-central x000_revs_x000_added_x000_copies 7c97034feb78 4407bd0c6330 : 7839 revs, 0.065707 s, 0.065823 s, +0.000116 s, × 1.0018, 8 µs/rev mozilla-central x0000_revs_xx000_added_0_copies 9eec5917337d 67118cc6dcad : 615 revs, 0.026800 s, 0.027050 s, +0.000250 s, × 1.0093, 43 µs/rev mozilla-central x0000_revs_xx000_added_x000_copies f78c615a656c 96a38b690156 : 30263 revs, 0.203856 s, 0.202443 s, -0.001413 s, × 0.9931, 6 µs/rev mozilla-central x00000_revs_x0000_added_x0000_copies 6832ae71433c 4c222a1d9a00 : 153721 revs, 1.293394 s, 1.261583 s, -0.031811 s, × 0.9754, 8 µs/rev mozilla-central x00000_revs_x00000_added_x000_copies 76caed42cf7c 1daa622bbe42 : 204976 revs, 1.698239 s, 1.643869 s, -0.054370 s, × 0.9680, 8 µs/rev mozilla-try x_revs_x_added_0_copies aaf6dde0deb8 9790f499805a : 2 revs, 0.000875 s, 0.000868 s, -0.000007 s, × 0.9920, 434 µs/rev mozilla-try x_revs_x000_added_0_copies d8d0222927b4 5bb8ce8c7450 : 2 revs, 0.000891 s, 0.000887 s, -0.000004 s, × 0.9955, 443 µs/rev mozilla-try x_revs_x_added_x_copies 092fcca11bdb 936255a0384a : 4 revs, 0.000292 s, 0.000168 s, -0.000124 s, × 0.5753, 42 µs/rev mozilla-try x_revs_x00_added_x_copies b53d2fadbdb5 017afae788ec : 2 revs, 0.003939 s, 0.001160 s, -0.002779 s, × 0.2945, 580 µs/rev mozilla-try x_revs_x000_added_x000_copies 20408ad61ce5 6f0ee96e21ad : 1 revs, 0.033027 s, 0.033016 s, -0.000011 s, × 0.9997, 33016 µs/rev mozilla-try x_revs_x0000_added_x0000_copies effb563bb7e5 c07a39dc4e80 : 6 revs, 0.073703 s, 0.073312 s, -0.39ae31 s, × 0.9947, 12218 µs/rev mozilla-try x000_revs_xx00_added_0_copies 6100d773079a 04a55431795e : 1593 revs, 0.006469 s, 0.006485 s, +0.000016 s, × 1.0025, 4 µs/rev mozilla-try x000_revs_x000_added_x_copies 9f17a6fc04f9 2d37b966abed : 41 revs, 0.005278 s, 0.005494 s, +0.000216 s, × 1.0409, 134 µs/rev mozilla-try x000_revs_x000_added_x000_copies 1346fd0130e4 4c65cbdabc1f : 6657 revs, 0.064995 s, 0.064879 s, -0.000116 s, × 0.9982, 9 µs/rev mozilla-try x0000_revs_x_added_0_copies 63519bfd42ee a36a2a865d92 : 40314 revs, 0.301041 s, 0.301469 s, +0.000428 s, × 1.0014, 7 µs/rev mozilla-try x0000_revs_x_added_x_copies 9fe69ff0762d bcabf2a78927 : 38690 revs, 0.285575 s, 0.297113 s, +0.011538 s, × 1.0404, 7 µs/rev mozilla-try x0000_revs_xx000_added_x_copies 156f6e2674f2 4d0f2c178e66 : 8598 revs, 0.085597 s, 0.085890 s, +0.000293 s, × 1.0034, 9 µs/rev mozilla-try x0000_revs_xx000_added_0_copies 9eec5917337d 67118cc6dcad : 615 revs, 0.027118 s, 0.027718 s, +0.000600 s, × 1.0221, 45 µs/rev mozilla-try x0000_revs_xx000_added_x000_copies 89294cd501d9 7ccb2fc7ccb5 : 97052 revs, 2.119204 s, 2.048949 s, -0.070255 s, × 0.9668, 21 µs/rev mozilla-try x0000_revs_x0000_added_x0000_copies e928c65095ed e951f4ad123a : 52031 revs, 0.701479 s, 0.685924 s, -0.015555 s, × 0.9778, 13 µs/rev mozilla-try x00000_revs_x_added_0_copies 6a320851d377 1ebb79acd503 : 363753 revs, 4.482399 s, 4.482891 s, +0.000492 s, × 1.0001, 12 µs/rev mozilla-try x00000_revs_x00000_added_0_copies dc8a3ca7010e d16fde900c9c : 34414 revs, 0.574082 s, 0.577633 s, +0.003551 s, × 1.0062, 16 µs/rev mozilla-try x00000_revs_x_added_x_copies 5173c4b6f97c 95d83ee7242d : 362229 revs, 4.480366 s, 4.397816 s, -0.082550 s, × 0.9816, 12 µs/rev mozilla-try x00000_revs_x000_added_x_copies 9126823d0e9c ca82787bb23c : 359344 revs, 4.369070 s, 4.370538 s, +0.001468 s, × 1.0003, 12 µs/rev mozilla-try x00000_revs_x0000_added_x0000_copies 8d3fafa80d4b eb884023b810 : 192665 revs, 1.592506 s, 1.570439 s, -0.022067 s, × 0.9861, 8 µs/rev mozilla-try x00000_revs_x00000_added_x0000_copies 1b661134e2ca 1ae03d022d6d : 228985 revs, 87.824489 s, 88.388512 s, +0.564023 s, × 1.0064, 386 µs/rev mozilla-try x00000_revs_x00000_added_x000_copies 9b2a99adc05e 8e29777b48e6 : 382065 revs, 43.304637 s, 34.443661 s, -8.860976 s, × 0.7954, 90 µs/rev private : 459513 revs, 33.853687 s, 27.370148 s, -6.483539 s, × 0.8085, 59 µs/rev Differential Revision: https://phab.mercurial-scm.org/D9653
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Wed, 16 Dec 2020 11:11:05 +0100
parents fc4fb2f17dd4
children 47686726545d
line wrap: on
line source

  $ fileset() {
  >   hg debugfileset --all-files "$@"
  > }

  $ hg init repo
  $ cd repo
  $ echo a > a1
  $ echo a > a2
  $ echo b > b1
  $ echo b > b2
  $ hg ci -Am addfiles
  adding a1
  adding a2
  adding b1
  adding b2

Test operators and basic patterns

  $ fileset -v a1
  (symbol 'a1')
  * matcher:
  <patternmatcher patterns='a1$'>
  a1
  $ fileset -v 'a*'
  (symbol 'a*')
  * matcher:
  <patternmatcher patterns='a[^/]*$'>
  a1
  a2
  $ fileset -v '"re:a\d"'
  (string 're:a\\d')
  * matcher:
  <patternmatcher patterns='a\\d'>
  a1
  a2
  $ fileset -v '!re:"a\d"'
  (not
    (kindpat
      (symbol 're')
      (string 'a\\d')))
  * matcher:
  <predicatenmatcher
    pred=<not
      <patternmatcher patterns='a\\d'>>>
  b1
  b2
  $ fileset -v 'path:a1 or glob:b?'
  (or
    (kindpat
      (symbol 'path')
      (symbol 'a1'))
    (kindpat
      (symbol 'glob')
      (symbol 'b?')))
  * matcher:
  <patternmatcher patterns='a1(?:/|$)|b.$'>
  a1
  b1
  b2
  $ fileset -v --no-show-matcher 'a1 or a2'
  (or
    (symbol 'a1')
    (symbol 'a2'))
  a1
  a2
  $ fileset 'a1 | a2'
  a1
  a2
  $ fileset 'a* and "*1"'
  a1
  $ fileset 'a* & "*1"'
  a1
  $ fileset 'not (r"a*")'
  b1
  b2
  $ fileset '! ("a*")'
  b1
  b2
  $ fileset 'a* - a1'
  a2
  $ fileset 'a_b'
  $ fileset '"\xy"'
  hg: parse error: invalid \x escape* (glob)
  [10]

Test invalid syntax

  $ fileset -v '"added"()'
  (func
    (string 'added')
    None)
  hg: parse error: not a symbol
  [10]
  $ fileset -v '()()'
  (func
    (group
      None)
    None)
  hg: parse error: not a symbol
  [10]
  $ fileset -v -- '-x'
  (negate
    (symbol 'x'))
  hg: parse error: can't use negate operator in this context
  [10]
  $ fileset -v -- '-()'
  (negate
    (group
      None))
  hg: parse error: can't use negate operator in this context
  [10]
  $ fileset -p parsed 'a, b, c'
  * parsed:
  (list
    (symbol 'a')
    (symbol 'b')
    (symbol 'c'))
  hg: parse error: can't use a list in this context
  (see 'hg help "filesets.x or y"')
  [10]

  $ fileset '"path":.'
  hg: parse error: not a symbol
  [10]
  $ fileset 'path:foo bar'
  hg: parse error at 9: invalid token
  [10]
  $ fileset 'foo:bar:baz'
  hg: parse error: not a symbol
  [10]
  $ fileset 'foo:bar()'
  hg: parse error: pattern must be a string
  [10]
  $ fileset 'foo:bar'
  hg: parse error: invalid pattern kind: foo
  [10]

Show parsed tree at stages:

  $ fileset -p unknown a
  abort: invalid stage name: unknown
  [255]

  $ fileset -p parsed 'path:a1 or glob:b?'
  * parsed:
  (or
    (kindpat
      (symbol 'path')
      (symbol 'a1'))
    (kindpat
      (symbol 'glob')
      (symbol 'b?')))
  a1
  b1
  b2

  $ fileset -p all -s 'a1 or a2 or (grep("b") & clean())'
  * parsed:
  (or
    (symbol 'a1')
    (symbol 'a2')
    (group
      (and
        (func
          (symbol 'grep')
          (string 'b'))
        (func
          (symbol 'clean')
          None))))
  * analyzed:
  (or
    (symbol 'a1')
    (symbol 'a2')
    (and
      (func
        (symbol 'grep')
        (string 'b'))
      (withstatus
        (func
          (symbol 'clean')
          None)
        (string 'clean'))))
  * optimized:
  (or
    (patterns
      (symbol 'a1')
      (symbol 'a2'))
    (and
      (withstatus
        (func
          (symbol 'clean')
          None)
        (string 'clean'))
      (func
        (symbol 'grep')
        (string 'b'))))
  * matcher:
  <unionmatcher matchers=[
    <patternmatcher patterns='a1$|a2$'>,
    <intersectionmatcher
      m1=<predicatenmatcher pred=clean>,
      m2=<predicatenmatcher pred=grep('b')>>]>
  a1
  a2
  b1
  b2

Union of basic patterns:

  $ fileset -p optimized -s -r. 'a1 or a2 or path:b1'
  * optimized:
  (patterns
    (symbol 'a1')
    (symbol 'a2')
    (kindpat
      (symbol 'path')
      (symbol 'b1')))
  * matcher:
  <patternmatcher patterns='a1$|a2$|b1(?:/|$)'>
  a1
  a2
  b1

OR expression should be reordered by weight:

  $ fileset -p optimized -s -r. 'grep("a") or a1 or grep("b") or b2'
  * optimized:
  (or
    (patterns
      (symbol 'a1')
      (symbol 'b2'))
    (func
      (symbol 'grep')
      (string 'a'))
    (func
      (symbol 'grep')
      (string 'b')))
  * matcher:
  <unionmatcher matchers=[
    <patternmatcher patterns='a1$|b2$'>,
    <predicatenmatcher pred=grep('a')>,
    <predicatenmatcher pred=grep('b')>]>
  a1
  a2
  b1
  b2

Use differencematcher for 'x and not y':

  $ fileset -p optimized -s 'a* and not a1'
  * optimized:
  (minus
    (symbol 'a*')
    (symbol 'a1'))
  * matcher:
  <differencematcher
    m1=<patternmatcher patterns='a[^/]*$'>,
    m2=<patternmatcher patterns='a1$'>>
  a2

  $ fileset -p optimized -s '!binary() and a*'
  * optimized:
  (minus
    (symbol 'a*')
    (func
      (symbol 'binary')
      None))
  * matcher:
  <differencematcher
    m1=<patternmatcher patterns='a[^/]*$'>,
    m2=<predicatenmatcher pred=binary>>
  a1
  a2

'x - y' is rewritten to 'x and not y' first so the operands can be reordered:

  $ fileset -p analyzed -p optimized -s 'a* - a1'
  * analyzed:
  (and
    (symbol 'a*')
    (not
      (symbol 'a1')))
  * optimized:
  (minus
    (symbol 'a*')
    (symbol 'a1'))
  * matcher:
  <differencematcher
    m1=<patternmatcher patterns='a[^/]*$'>,
    m2=<patternmatcher patterns='a1$'>>
  a2

  $ fileset -p analyzed -p optimized -s 'binary() - a*'
  * analyzed:
  (and
    (func
      (symbol 'binary')
      None)
    (not
      (symbol 'a*')))
  * optimized:
  (and
    (not
      (symbol 'a*'))
    (func
      (symbol 'binary')
      None))
  * matcher:
  <intersectionmatcher
    m1=<predicatenmatcher
      pred=<not
        <patternmatcher patterns='a[^/]*$'>>>,
    m2=<predicatenmatcher pred=binary>>

Test files status

  $ rm a1
  $ hg rm a2
  $ echo b >> b2
  $ hg cp b1 c1
  $ echo c > c2
  $ echo c > c3
  $ cat > .hgignore <<EOF
  > \.hgignore
  > 2$
  > EOF
  $ fileset 'modified()'
  b2
  $ fileset 'added()'
  c1
  $ fileset 'removed()'
  a2
  $ fileset 'deleted()'
  a1
  $ fileset 'missing()'
  a1
  $ fileset 'unknown()'
  c3
  $ fileset 'ignored()'
  .hgignore
  c2
  $ fileset 'hgignore()'
  .hgignore
  a2
  b2
  c2
  $ fileset 'clean()'
  b1
  $ fileset 'copied()'
  c1

Test files status in different revisions

  $ hg status -m
  M b2
  $ fileset -r0 'revs("wdir()", modified())' --traceback
  b2
  $ hg status -a
  A c1
  $ fileset -r0 'revs("wdir()", added())'
  c1
  $ hg status --change 0 -a
  A a1
  A a2
  A b1
  A b2
  $ hg status -mru
  M b2
  R a2
  ? c3
  $ fileset -r0 'added() and revs("wdir()", modified() or removed() or unknown())'
  a2
  b2
  $ fileset -r0 'added() or revs("wdir()", added())'
  a1
  a2
  b1
  b2
  c1

Test insertion of status hints

  $ fileset -p optimized 'added()'
  * optimized:
  (withstatus
    (func
      (symbol 'added')
      None)
    (string 'added'))
  c1

  $ fileset -p optimized 'a* & removed()'
  * optimized:
  (and
    (symbol 'a*')
    (withstatus
      (func
        (symbol 'removed')
        None)
      (string 'removed')))
  a2

  $ fileset -p optimized 'a* - removed()'
  * optimized:
  (minus
    (symbol 'a*')
    (withstatus
      (func
        (symbol 'removed')
        None)
      (string 'removed')))
  a1

  $ fileset -p analyzed -p optimized '(added() + removed()) - a*'
  * analyzed:
  (and
    (withstatus
      (or
        (func
          (symbol 'added')
          None)
        (func
          (symbol 'removed')
          None))
      (string 'added removed'))
    (not
      (symbol 'a*')))
  * optimized:
  (and
    (not
      (symbol 'a*'))
    (withstatus
      (or
        (func
          (symbol 'added')
          None)
        (func
          (symbol 'removed')
          None))
      (string 'added removed')))
  c1

  $ fileset -p optimized 'a* + b* + added() + unknown()'
  * optimized:
  (withstatus
    (or
      (patterns
        (symbol 'a*')
        (symbol 'b*'))
      (func
        (symbol 'added')
        None)
      (func
        (symbol 'unknown')
        None))
    (string 'added unknown'))
  a1
  a2
  b1
  b2
  c1
  c3

  $ fileset -p analyzed -p optimized 'removed() & missing() & a*'
  * analyzed:
  (and
    (withstatus
      (and
        (func
          (symbol 'removed')
          None)
        (func
          (symbol 'missing')
          None))
      (string 'removed missing'))
    (symbol 'a*'))
  * optimized:
  (and
    (symbol 'a*')
    (withstatus
      (and
        (func
          (symbol 'removed')
          None)
        (func
          (symbol 'missing')
          None))
      (string 'removed missing')))

  $ fileset -p optimized 'clean() & revs(0, added())'
  * optimized:
  (and
    (withstatus
      (func
        (symbol 'clean')
        None)
      (string 'clean'))
    (func
      (symbol 'revs')
      (list
        (symbol '0')
        (withstatus
          (func
            (symbol 'added')
            None)
          (string 'added')))))
  b1

  $ fileset -p optimized 'clean() & status(null, 0, b* & added())'
  * optimized:
  (and
    (withstatus
      (func
        (symbol 'clean')
        None)
      (string 'clean'))
    (func
      (symbol 'status')
      (list
        (symbol 'null')
        (symbol '0')
        (and
          (symbol 'b*')
          (withstatus
            (func
              (symbol 'added')
              None)
            (string 'added'))))))
  b1

Test files properties

  >>> open('bin', 'wb').write(b'\0a') and None
  $ fileset 'binary()'
  bin
  $ fileset 'binary() and unknown()'
  bin
  $ echo '^bin$' >> .hgignore
  $ fileset 'binary() and ignored()'
  bin
  $ hg add bin
  $ fileset 'binary()'
  bin

  $ fileset -p optimized -s 'binary() and b*'
  * optimized:
  (and
    (symbol 'b*')
    (func
      (symbol 'binary')
      None))
  * matcher:
  <intersectionmatcher
    m1=<patternmatcher patterns='b[^/]*$'>,
    m2=<predicatenmatcher pred=binary>>
  bin

  $ fileset 'grep("b{1}")'
  .hgignore
  b1
  b2
  c1
  $ fileset 'grep("missingparens(")'
  hg: parse error: invalid match pattern: (unbalanced parenthesis|missing \)).* (re)
  [10]

#if execbit
  $ chmod +x b2
  $ fileset 'exec()'
  b2
#endif

#if symlink
  $ ln -s b2 b2link
  $ fileset 'symlink() and unknown()'
  b2link
  $ hg add b2link
#endif

#if no-windows
  $ echo foo > con.xml
  $ fileset 'not portable()'
  con.xml
  $ hg --config ui.portablefilenames=ignore add con.xml
#endif

  >>> open('1k', 'wb').write(b' '*1024) and None
  >>> open('2k', 'wb').write(b' '*2048) and None
  $ hg add 1k 2k
  $ fileset 'size("bar")'
  hg: parse error: couldn't parse size: bar
  [10]
  $ fileset '(1k, 2k)'
  hg: parse error: can't use a list in this context
  (see 'hg help "filesets.x or y"')
  [10]
  $ fileset 'size(1k)'
  1k
  $ fileset '(1k or 2k) and size("< 2k")'
  1k
  $ fileset '(1k or 2k) and size("<=2k")'
  1k
  2k
  $ fileset '(1k or 2k) and size("> 1k")'
  2k
  $ fileset '(1k or 2k) and size(">=1K")'
  1k
  2k
  $ fileset '(1k or 2k) and size(".5KB - 1.5kB")'
  1k
  $ fileset 'size("1M")'
  $ fileset 'size("1 GB")'

Test merge states

  $ hg ci -m manychanges
  $ hg file -r . 'set:copied() & modified()'
  [1]
  $ hg up -C 0
  * files updated, 0 files merged, * files removed, 0 files unresolved (glob)
  $ echo c >> b2
  $ hg ci -m diverging b2
  created new head
  $ fileset 'resolved()'
  $ fileset 'unresolved()'
  $ hg merge
  merging b2
  warning: conflicts while merging b2! (edit, then use 'hg resolve --mark')
  * files updated, 0 files merged, 1 files removed, 1 files unresolved (glob)
  use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
  [1]
  $ fileset 'resolved()'
  $ fileset 'unresolved()'
  b2
  $ echo e > b2
  $ hg resolve -m b2
  (no more unresolved files)
  $ fileset 'resolved()'
  b2
  $ fileset 'unresolved()'
  $ hg ci -m merge

Test subrepo predicate

  $ hg init sub
  $ echo a > sub/suba
  $ hg -R sub add sub/suba
  $ hg -R sub ci -m sub
  $ echo 'sub = sub' > .hgsub
  $ hg init sub2
  $ echo b > sub2/b
  $ hg -R sub2 ci -Am sub2
  adding b
  $ echo 'sub2 = sub2' >> .hgsub
  $ fileset 'subrepo()'
  $ hg add .hgsub
  $ fileset 'subrepo()'
  sub
  sub2
  $ fileset 'subrepo("sub")'
  sub
  $ fileset 'subrepo("glob:*")'
  sub
  sub2
  $ hg ci -m subrepo

Test that .hgsubstate is updated as appropriate during a conversion.  The
saverev property is enough to alter the hashes of the subrepo.

  $ hg init ../converted
  $ hg --config extensions.convert= convert --config convert.hg.saverev=True  \
  >      sub ../converted/sub
  initializing destination ../converted/sub repository
  scanning source...
  sorting...
  converting...
  0 sub
  $ hg clone -U sub2 ../converted/sub2
  $ hg --config extensions.convert= convert --config convert.hg.saverev=True  \
  >      . ../converted
  scanning source...
  sorting...
  converting...
  4 addfiles
  3 manychanges
  2 diverging
  1 merge
  0 subrepo
  no ".hgsubstate" updates will be made for "sub2"
  $ hg up -q -R ../converted -r tip
  $ hg --cwd ../converted cat sub/suba sub2/b -r tip
  a
  b
  $ oldnode=`hg log -r tip -T "{node}\n"`
  $ newnode=`hg log -R ../converted -r tip -T "{node}\n"`
  $ [ "$oldnode" != "$newnode" ] || echo "nothing changed"

Test with a revision

  $ hg log -G --template '{rev} {desc}\n'
  @  4 subrepo
  |
  o    3 merge
  |\
  | o  2 diverging
  | |
  o |  1 manychanges
  |/
  o  0 addfiles
  
  $ echo unknown > unknown
  $ fileset -r1 'modified()'
  b2
  $ fileset -r1 'added() and c1'
  c1
  $ fileset -r1 'removed()'
  a2
  $ fileset -r1 'deleted()'
  $ fileset -r1 'unknown()'
  $ fileset -r1 'ignored()'
  $ fileset -r1 'hgignore()'
  .hgignore
  a2
  b2
  bin
  c2
  sub2
  $ fileset -r1 'binary()'
  bin
  $ fileset -r1 'size(1k)'
  1k
  $ fileset -r3 'resolved()'
  $ fileset -r3 'unresolved()'

#if execbit
  $ fileset -r1 'exec()'
  b2
#endif

#if symlink
  $ fileset -r1 'symlink()'
  b2link
#endif

#if no-windows
  $ fileset -r1 'not portable()'
  con.xml
  $ hg forget 'con.xml'
#endif

  $ fileset -r4 'subrepo("re:su.*")'
  sub
  sub2
  $ fileset -r4 'subrepo(re:su.*)'
  sub
  sub2
  $ fileset -r4 'subrepo("sub")'
  sub
  $ fileset -r4 'b2 or c1'
  b2
  c1

  >>> open('dos', 'wb').write(b"dos\r\n") and None
  >>> open('mixed', 'wb').write(b"dos\r\nunix\n") and None
  >>> open('mac', 'wb').write(b"mac\r") and None
  $ hg add dos mixed mac

(remove a1, to examine safety of 'eol' on removed files)
  $ rm a1

  $ fileset 'eol(dos)'
  dos
  mixed
  $ fileset 'eol(unix)'
  .hgignore
  .hgsub
  .hgsubstate
  b1
  b2
  b2.orig
  c1
  c2
  c3
  con.xml (no-windows !)
  mixed
  unknown
  $ fileset 'eol(mac)'
  mac

Test safety of 'encoding' on removed files

  $ fileset 'encoding("ascii")'
  .hgignore
  .hgsub
  .hgsubstate
  1k
  2k
  b1
  b2
  b2.orig
  b2link (symlink !)
  bin
  c1
  c2
  c3
  con.xml (no-windows !)
  dos
  mac
  mixed
  unknown

Test 'revs(...)'
================

small reminder of the repository state

  $ hg log -G
  @  changeset:   4:* (glob)
  |  tag:         tip
  |  user:        test
  |  date:        Thu Jan 01 00:00:00 1970 +0000
  |  summary:     subrepo
  |
  o    changeset:   3:* (glob)
  |\   parent:      2:55b05bdebf36
  | |  parent:      1:* (glob)
  | |  user:        test
  | |  date:        Thu Jan 01 00:00:00 1970 +0000
  | |  summary:     merge
  | |
  | o  changeset:   2:55b05bdebf36
  | |  parent:      0:8a9576c51c1f
  | |  user:        test
  | |  date:        Thu Jan 01 00:00:00 1970 +0000
  | |  summary:     diverging
  | |
  o |  changeset:   1:* (glob)
  |/   user:        test
  |    date:        Thu Jan 01 00:00:00 1970 +0000
  |    summary:     manychanges
  |
  o  changeset:   0:8a9576c51c1f
     user:        test
     date:        Thu Jan 01 00:00:00 1970 +0000
     summary:     addfiles
  
  $ hg status --change 0
  A a1
  A a2
  A b1
  A b2
  $ hg status --change 1
  M b2
  A 1k
  A 2k
  A b2link (symlink !)
  A bin
  A c1
  A con.xml (no-windows !)
  R a2
  $ hg status --change 2
  M b2
  $ hg status --change 3
  M b2
  A 1k
  A 2k
  A b2link (symlink !)
  A bin
  A c1
  A con.xml (no-windows !)
  R a2
  $ hg status --change 4
  A .hgsub
  A .hgsubstate
  $ hg status
  A dos
  A mac
  A mixed
  R con.xml (no-windows !)
  ! a1
  ? b2.orig
  ? c3
  ? unknown

Test files at -r0 should be filtered by files at wdir
-----------------------------------------------------

  $ fileset -r0 'tracked() and revs("wdir()", tracked())'
  a1
  b1
  b2

Test that "revs()" work at all
------------------------------

  $ fileset "revs('2', modified())"
  b2

Test that "revs()" work for file missing in the working copy/current context
----------------------------------------------------------------------------

(a2 not in working copy)

  $ fileset "revs('0', added())"
  a1
  a2
  b1
  b2

(none of the file exist in "0")

  $ fileset -r 0 "revs('4', added())"
  .hgsub
  .hgsubstate

Call with empty revset
--------------------------

  $ fileset "revs('2-2', modified())"

Call with revset matching multiple revs
---------------------------------------

  $ fileset "revs('0+4', added())"
  .hgsub
  .hgsubstate
  a1
  a2
  b1
  b2

overlapping set

  $ fileset "revs('1+2', modified())"
  b2

test 'status(...)'
=================

Simple case
-----------

  $ fileset "status(3, 4, added())"
  .hgsub
  .hgsubstate

use rev to restrict matched file
-----------------------------------------

  $ hg status --removed --rev 0 --rev 1
  R a2
  $ fileset "status(0, 1, removed())"
  a2
  $ fileset "tracked() and status(0, 1, removed())"
  $ fileset -r 4 "status(0, 1, removed())"
  a2
  $ fileset -r 4 "tracked() and status(0, 1, removed())"
  $ fileset "revs('4', tracked() and status(0, 1, removed()))"
  $ fileset "revs('0', tracked() and status(0, 1, removed()))"
  a2

check wdir()
------------

  $ hg status --removed  --rev 4
  R con.xml (no-windows !)
  $ fileset "status(4, 'wdir()', removed())"
  con.xml (no-windows !)

  $ hg status --removed --rev 2
  R a2
  $ fileset "status('2', 'wdir()', removed())"
  a2

test backward status
--------------------

  $ hg status --removed --rev 0 --rev 4
  R a2
  $ hg status --added --rev 4 --rev 0
  A a2
  $ fileset "status(4, 0, added())"
  a2

test cross branch status
------------------------

  $ hg status --added --rev 1 --rev 2
  A a2
  $ fileset "status(1, 2, added())"
  a2

test with multi revs revset
---------------------------
  $ hg status --added --rev 0:1 --rev 3:4
  A .hgsub
  A .hgsubstate
  A 1k
  A 2k
  A b2link (symlink !)
  A bin
  A c1
  A con.xml (no-windows !)
  $ fileset "status('0:1', '3:4', added())"
  .hgsub
  .hgsubstate
  1k
  2k
  b2link (symlink !)
  bin
  c1
  con.xml (no-windows !)

tests with empty value
----------------------

Fully empty revset

  $ fileset "status('', '4', added())"
  hg: parse error: first argument to status must be a revision
  [10]
  $ fileset "status('2', '', added())"
  hg: parse error: second argument to status must be a revision
  [10]

Empty revset will error at the revset layer

  $ fileset "status(' ', '4', added())"
  hg: parse error at 1: not a prefix: end
  ( 
    ^ here)
  [10]
  $ fileset "status('2', ' ', added())"
  hg: parse error at 1: not a prefix: end
  ( 
    ^ here)
  [10]