D10947: test: use a python script in `test-transaction-rollback-on-sigpipe.t`

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This still does not work on Windows, but at least this is a python script now.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  tests/test-transaction-rollback-on-sigpipe.t
  tests/testlib/sigpipe-remote.py

CHANGE DETAILS

diff --git a/tests/testlib/sigpipe-remote.py b/tests/testlib/sigpipe-remote.py
new file mode 100755
--- /dev/null
+++ b/tests/testlib/sigpipe-remote.py
@@ -0,0 +1,176 @@
+#!/usr/bin/env -S python3 -u
+from __future__ import print_function
+
+import os
+import subprocess
+import sys
+import threading
+import time
+
+# we cannot use mercurial.testing as long as python2 is not dropped as the 
test will only install the mercurial module for python2 in python2 run
+
+
+def _timeout_factor():
+"""return the current modification to timeout"""
+default = int(os.environ.get('HGTEST_TIMEOUT_DEFAULT', 360))
+current = int(os.environ.get('HGTEST_TIMEOUT', default))
+if current == 0:
+return 1
+return current / float(default)
+
+
+def wait_file(path, timeout=10):
+timeout *= _timeout_factor()
+start = time.time()
+while not os.path.exists(path):
+if (time.time() - start) > timeout:
+raise RuntimeError(b"timed out waiting for file: %s" % path)
+time.sleep(0.01)
+
+
+def write_file(path, content=b''):
+with open(path, 'wb') as f:
+f.write(content)
+
+
+# end of mercurial.testing content
+
+if sys.version_info[0] < 3:
+print('SIGPIPE-HELPER: script should run with Python 3', file=sys.stderr)
+sys.exit(255)
+
+
+def sysbytes(s):
+return s.encode('utf-8')
+
+
+def sysstr(s):
+return s.decode('latin-1')
+
+
+piped_stdout = os.pipe2(os.O_NONBLOCK | os.O_CLOEXEC)
+piped_stderr = os.pipe2(os.O_NONBLOCK | os.O_CLOEXEC)
+
+stdout_writer = os.fdopen(piped_stdout[1], "rb")
+stdout_reader = os.fdopen(piped_stdout[0], "rb")
+stderr_writer = os.fdopen(piped_stderr[1], "rb")
+stderr_reader = os.fdopen(piped_stderr[0], "rb")
+
+DEBUG_FILE = os.environ.get('SIGPIPE_REMOTE_DEBUG_FILE')
+if DEBUG_FILE is None:
+debug_stream = sys.stderr.buffer
+else:
+debug_stream = open(DEBUG_FILE, 'bw', buffering=0)
+
+SYNCFILE1 = os.environ.get('SYNCFILE1')
+SYNCFILE2 = os.environ.get('SYNCFILE2')
+if SYNCFILE1 is None:
+print('SIGPIPE-HELPER: missing variable $SYNCFILE1', file=sys.stderr)
+sys.exit(255)
+if SYNCFILE2 is None:
+print('SIGPIPE-HELPER: missing variable $SYNCFILE2', file=sys.stderr)
+sys.exit(255)
+
+debug_stream.write(b'SIGPIPE-HELPER: Starting\n')
+
+TESTLIB_DIR = os.path.dirname(sys.argv[0])
+WAIT_SCRIPT = os.path.join(TESTLIB_DIR, 'wait-on-file')
+
+hooks_cmd = '%s 10 %s %s'
+hooks_cmd %= (
+WAIT_SCRIPT,
+SYNCFILE2,
+SYNCFILE1,
+)
+
+cmd = ['hg']
+cmd += sys.argv[1:]
+sub = subprocess.Popen(
+cmd,
+bufsize=0,
+close_fds=True,
+stdin=sys.stdin,
+stdout=stdout_writer,
+stderr=stderr_writer,
+)
+
+debug_stream.write(b'SIGPIPE-HELPER: Mercurial started\n')
+
+
+shut_down = threading.Event()
+
+close_lock = threading.Lock()
+
+
+def _read(stream):
+try:
+return stream.read()
+except ValueError:
+# read on closed file
+return None
+
+
+def forward_stdout():
+while not shut_down.is_set():
+c = _read(stdout_reader)
+while c is not None:
+sys.stdout.buffer.write(c)
+c = _read(stdout_reader)
+time.sleep(0.001)
+with close_lock:
+if not stdout_reader.closed:
+stdout_reader.close()
+debug_stream.write(b'SIGPIPE-HELPER: stdout closed\n')
+
+
+def forward_stderr():
+while not shut_down.is_set():
+c = _read(stderr_reader)
+if c is not None:
+sys.stderr.buffer.write(c)
+c = _read(stderr_reader)
+time.sleep(0.001)
+with close_lock:
+if not stderr_reader.closed:
+stderr_reader.close()
+debug_stream.write(b'SIGPIPE-HELPER: stderr closed\n')
+
+
+stdout_thread = threading.Thread(target=forward_stdout, daemon=True)
+stderr_thread = threading.Thread(target=forward_stderr, daemon=True)
+
+try:
+stdout_thread.start()
+stderr_thread.start()
+
+debug_stream.write(b'SIGPIPE-HELPER: Redirection in place\n')
+
+try:
+wait_file(sysbytes(SYNCFILE1))
+except RuntimeError as exc:
+msg = sysbytes(str(exc))
+debug_stream.write(b'SIGPIPE-HELPER: wait failed: %s\n' % msg)
+else:
+debug_stream.write(b'SIGPIPE-HELPER: SYNCFILE1 detected\n')
+with close_lock:
+if not stdout_reader.closed:
+stdout_reader.close()
+if not stderr_reader.closed:
+stderr_reader.close()
+sys.stdin.close()
+debug_stream.write(b'SIGPIPE-HELPER: pipes closed\n')
+debug_stream.write(b'SIGPIPE-HELPER: 

D10946: test: make sure we hit the SIGPIPE in test-transaction-rollback-on-sigpipe

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  With a coming change, we might not hit the sig pipe without issue and extra 
line
  of output. We do this early to make the next change clearer.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  tests/test-transaction-rollback-on-sigpipe.t

CHANGE DETAILS

diff --git a/tests/test-transaction-rollback-on-sigpipe.t 
b/tests/test-transaction-rollback-on-sigpipe.t
--- a/tests/test-transaction-rollback-on-sigpipe.t
+++ b/tests/test-transaction-rollback-on-sigpipe.t
@@ -47,7 +47,8 @@
 
   $ cat >remote/.hg/hgrc < [hooks]
-  > pretxnchangegroup.break-things=$hook_script
+  > pretxnchangegroup.00-break-things=$hook_script
+  > pretxnchangegroup.01-output-things=echo "some remote output to be forward 
to the closed pipe"
   > EOF
 
   $ hg --cwd ./remote tip -T '{node|short}\n'



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


D10945: test: clarify some output in `test-transaction-rollback-on-sigpipe`

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  We don't want a dirty transaction to remain, but need to check the transaction
  was aborted. Otherwise it is easy to have no abandoned transaction if the
  transaction succeed.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  tests/test-transaction-rollback-on-sigpipe.t

CHANGE DETAILS

diff --git a/tests/test-transaction-rollback-on-sigpipe.t 
b/tests/test-transaction-rollback-on-sigpipe.t
--- a/tests/test-transaction-rollback-on-sigpipe.t
+++ b/tests/test-transaction-rollback-on-sigpipe.t
@@ -50,12 +50,16 @@
   > pretxnchangegroup.break-things=$hook_script
   > EOF
 
+  $ hg --cwd ./remote tip -T '{node|short}\n'
+  
   $ cd local
   $ echo foo > foo ; hg commit -qAm "commit"
   $ hg push -q -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" --remotecmd $remotecmd 
2>&1 | grep -v $killable_pipe
   abort: stream ended unexpectedly (got 0 bytes, expected 4)
 
 The remote should be left in a good state
+  $ hg --cwd ../remote tip -T '{node|short}\n'
+  
   $ hg --cwd ../remote recover
   no interrupted transaction available
   [1]



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


D10943: run-tests: stop writing a `python3` symlink pointing to python2

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  Having `python3` actually pointing to `python2` is bad. So we stop doing so.
  
  In addition we need to re-introduce a `python` executable since some of the
  script really need to be able to say "current python" in their shbang. For
  example, `hghave` is one of such script.
  
  The faulty changes where introduced by c102b704edb5 
.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  tests/dumbhttp.py
  tests/dummysmtpd.py
  tests/get-with-headers.py
  tests/hghave
  tests/run-tests.py
  tests/test-filelog.py
  tests/test-remotefilelog-datapack.py
  tests/test-remotefilelog-histpack.py
  tests/test-status-inprocess.py
  tests/test-stdio.py
  tests/tinyproxy.py

CHANGE DETAILS

diff --git a/tests/tinyproxy.py b/tests/tinyproxy.py
--- a/tests/tinyproxy.py
+++ b/tests/tinyproxy.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 
 from __future__ import absolute_import, print_function
 
diff --git a/tests/test-stdio.py b/tests/test-stdio.py
--- a/tests/test-stdio.py
+++ b/tests/test-stdio.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 """
 Tests the buffering behavior of stdio streams in `mercurial.utils.procutil`.
 """
diff --git a/tests/test-status-inprocess.py b/tests/test-status-inprocess.py
--- a/tests/test-status-inprocess.py
+++ b/tests/test-status-inprocess.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 from __future__ import absolute_import, print_function
 
 import sys
diff --git a/tests/test-remotefilelog-histpack.py 
b/tests/test-remotefilelog-histpack.py
--- a/tests/test-remotefilelog-histpack.py
+++ b/tests/test-remotefilelog-histpack.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 from __future__ import absolute_import
 
 import hashlib
diff --git a/tests/test-remotefilelog-datapack.py 
b/tests/test-remotefilelog-datapack.py
--- a/tests/test-remotefilelog-datapack.py
+++ b/tests/test-remotefilelog-datapack.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 from __future__ import absolute_import, print_function
 
 import hashlib
diff --git a/tests/test-filelog.py b/tests/test-filelog.py
--- a/tests/test-filelog.py
+++ b/tests/test-filelog.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 """
 Tests the behavior of filelog w.r.t. data starting with '\1\n'
 """
diff --git a/tests/run-tests.py b/tests/run-tests.py
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -3530,9 +3530,11 @@
 """Configure the environment to use the appropriate Python in tests."""
 # Tests must use the same interpreter as us or bad things will happen.
 if sys.platform == 'win32':
-pyexename = b'python.exe'
+pyexe_names = [b'python', b'python.exe']
+elif sys.version_info[0] < 3:
+pyexe_names = [b'python', b'python2']
 else:
-pyexename = b'python3'  # XXX this is wrong with python2...
+pyexe_names = [b'python', b'python3']
 
 # os.symlink() is a thing with py3 on Windows, but it requires
 # Administrator rights.
@@ -3540,7 +3542,7 @@
 msg = "# Making python executable in test path a symlink to '%s'"
 msg %= sysexecutable
 vlog(msg)
-for pyexename in [pyexename]:
+for pyexename in pyexe_names:
 mypython = os.path.join(self._tmpbindir, pyexename)
 try:
 if os.readlink(mypython) == sysexecutable:
@@ -3566,11 +3568,16 @@
 with open(osenvironb[b'RUNTESTDIR'] + b'/python3', 'wb') as f:
 f.write(b'#!/bin/sh\n')
 f.write(b'py -3.%d "$@"\n' % sys.version_info[1])
+if os.getenv('MSYSTEM'):
+with open(osenvironb[b'RUNTESTDIR'] + b'/python2', 'wb') as f:
+f.write(b'#!/bin/sh\n')
+f.write(b'py -2.%d "$@"\n' % sys.version_info[1])
 
 exedir, exename = os.path.split(sysexecutable)
-msg = "# Modifying search path to find %s as %s in '%s'"
-msg %= (exename, pyexename, exedir)
-vlog(msg)
+for pyexename in pyexe_names:
+msg = "# Modifying search path to find %s as %s in '%s'"
+msg %= (exename, pyexename, exedir)
+vlog(msg)
 path = os.environ['PATH'].split(os.pathsep)
 while exedir in path:
 path.remove(exedir)
@@ -3598,8 +3605,9 @@
 extra_paths.append(scripts_dir)
 
 os.environ['PATH'] = os.pathsep.join(extra_paths + path)
-if not self._findprogram(pyexename):
-print("WARNING: Cannot find %s in search path" % 

D10942: run-tests: avoid an early return

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  To fix the "python3 pointing to python2" we will also need to create a 
"python"
  pointer. So we will need to create multiple pointer. So we need to stop using
  early return.
  
  We replace the early return with a loop and a continue, since the next
  changeset will introduce that loop anyway.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  tests/run-tests.py

CHANGE DETAILS

diff --git a/tests/run-tests.py b/tests/run-tests.py
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -3540,22 +3540,23 @@
 msg = "# Making python executable in test path a symlink to '%s'"
 msg %= sysexecutable
 vlog(msg)
-mypython = os.path.join(self._tmpbindir, pyexename)
-try:
-if os.readlink(mypython) == sysexecutable:
-return
-os.unlink(mypython)
-except OSError as err:
-if err.errno != errno.ENOENT:
-raise
-if self._findprogram(pyexename) != sysexecutable:
+for pyexename in [pyexename]:
+mypython = os.path.join(self._tmpbindir, pyexename)
 try:
-os.symlink(sysexecutable, mypython)
-self._createdfiles.append(mypython)
+if os.readlink(mypython) == sysexecutable:
+continue
+os.unlink(mypython)
 except OSError as err:
-# child processes may race, which is harmless
-if err.errno != errno.EEXIST:
+if err.errno != errno.ENOENT:
 raise
+if self._findprogram(pyexename) != sysexecutable:
+try:
+os.symlink(sysexecutable, mypython)
+self._createdfiles.append(mypython)
+except OSError as err:
+# child processes may race, which is harmless
+if err.errno != errno.EEXIST:
+raise
 else:
 # Windows doesn't have `python3.exe`, and MSYS cannot understand 
the
 # reparse point with that name provided by Microsoft.  Create a



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


D10944: check-shbang: accept -S with env

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This is useful to use `python3 -u` in a coming patch.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  tests/test-check-shbang.t

CHANGE DETAILS

diff --git a/tests/test-check-shbang.t b/tests/test-check-shbang.t
--- a/tests/test-check-shbang.t
+++ b/tests/test-check-shbang.t
@@ -5,7 +5,7 @@
 
 look for python scripts that do not use /usr/bin/env
 
-  $ testrepohg files 'set:grep(r"^#!.*?python") and not 
grep(r"^#!/usr/bi{1}n/env python") - **/*.t'
+  $ testrepohg files 'set:grep(r"^#!.*?python") and not 
grep(r"^#!/usr/bi{1}n/env (-S )?python") - **/*.t'
   [1]
 
 In tests, enforce $PYTHON and *not* /usr/bin/env python or similar:



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


D10941: run-tests: clarify pyexename assignement

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  That assignement is wrong, we clarify that it is wrong before starting to fix
  it. This will make the fix clearer.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  tests/run-tests.py

CHANGE DETAILS

diff --git a/tests/run-tests.py b/tests/run-tests.py
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -3529,7 +3529,10 @@
 def _usecorrectpython(self):
 """Configure the environment to use the appropriate Python in tests."""
 # Tests must use the same interpreter as us or bad things will happen.
-pyexename = sys.platform == 'win32' and b'python.exe' or b'python3'
+if sys.platform == 'win32':
+pyexename = b'python.exe'
+else:
+pyexename = b'python3'  # XXX this is wrong with python2...
 
 # os.symlink() is a thing with py3 on Windows, but it requires
 # Administrator rights.



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


D10940: run-tests: factor appdata out

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This is a gratuitous readability change.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  tests/run-tests.py

CHANGE DETAILS

diff --git a/tests/run-tests.py b/tests/run-tests.py
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -3575,9 +3575,10 @@
 # not be in PATH by default.
 extra_paths = [exedir]
 vi = sys.version_info
-if 'APPDATA' in os.environ:
+appdata = os.environ.get('APPDATA')
+if appdata is not None:
 scripts_dir = os.path.join(
-os.environ['APPDATA'],
+appdata,
 'Python',
 'Python%d%d' % (vi[0], vi[1]),
 'Scripts',
@@ -3585,7 +3586,7 @@
 
 if vi.major == 2:
 scripts_dir = os.path.join(
-os.environ['APPDATA'],
+appdata,
 'Python',
 'Scripts',
 )



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


D10939: run-tests: unroll a non-so-one-liner

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This is shorter and clearer.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  tests/run-tests.py

CHANGE DETAILS

diff --git a/tests/run-tests.py b/tests/run-tests.py
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -3564,10 +3564,9 @@
 f.write(b'py -3.%d "$@"\n' % sys.version_info[1])
 
 exedir, exename = os.path.split(sysexecutable)
-vlog(
-"# Modifying search path to find %s as %s in '%s'"
-% (exename, pyexename, exedir)
-)
+msg = "# Modifying search path to find %s as %s in '%s'"
+msg %= (exename, pyexename, exedir)
+vlog(msg)
 path = os.environ['PATH'].split(os.pathsep)
 while exedir in path:
 path.remove(exedir)



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


D10938: run-tests: unroll a non-so-one-liner

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This is shorter and clearer.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  tests/run-tests.py

CHANGE DETAILS

diff --git a/tests/run-tests.py b/tests/run-tests.py
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -3534,10 +3534,9 @@
 # os.symlink() is a thing with py3 on Windows, but it requires
 # Administrator rights.
 if getattr(os, 'symlink', None) and os.name != 'nt':
-vlog(
-"# Making python executable in test path a symlink to '%s'"
-% sysexecutable
-)
+msg = "# Making python executable in test path a symlink to '%s'"
+msg %= sysexecutable
+vlog(msg)
 mypython = os.path.join(self._tmpbindir, pyexename)
 try:
 if os.readlink(mypython) == sysexecutable:



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


D10937: testing: fix _timeout_factor

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  With `--debug`, `run-tests.py` set the timeout to 0... that breaks the logic 
in
  `mercurial.testing`.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/testing/__init__.py

CHANGE DETAILS

diff --git a/mercurial/testing/__init__.py b/mercurial/testing/__init__.py
--- a/mercurial/testing/__init__.py
+++ b/mercurial/testing/__init__.py
@@ -16,8 +16,10 @@
 
 def _timeout_factor():
 """return the current modification to timeout"""
-default = int(environ.get('HGTEST_TIMEOUT_DEFAULT', 1))
+default = int(environ.get('HGTEST_TIMEOUT_DEFAULT', 360))
 current = int(environ.get('HGTEST_TIMEOUT', default))
+if current == 0:
+return 1
 return current / float(default)
 
 



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


D10936: cmdutil: fix newandmodified file accounting for --interactive commits

2021-07-02 Thread dploch (Daniel Ploch)
dploch created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  `originalchunks` is a misleading name, because it only contains header 
objects, which are flattened to selected hunks by the filter function. As such, 
`chunks not in originalchunks` is always True and misleading, because hunk 
objects never compare equal to header objects. This change fixes the internal 
naming and removes the useless parameter from the method.
  
  This change also fixes https://bz.mercurial-scm.org/show_bug.cgi?id=6533, by 
considering the filtered headers, rather than the hunks, when determining new 
and modified files. If a file is renamed + edited, and the edited hunks are 
deselected (but the file is not), the filtered chunks will contain a header for 
the file (because it's .special()) but but no hunks.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/cmdutil.py
  tests/test-commit-interactive.t

CHANGE DETAILS

diff --git a/tests/test-commit-interactive.t b/tests/test-commit-interactive.t
--- a/tests/test-commit-interactive.t
+++ b/tests/test-commit-interactive.t
@@ -1713,20 +1713,58 @@
   record this change to 'plain3'?
   (enter ? for help) [Ynesfdaq?] y
   
+
+Rename file but discard edits
+
+  $ echo content > new-file
+  $ hg add -q new-file
+  $ hg commit -qm 'new file'
+  $ hg mv new-file renamed-file
+  $ echo new-content >> renamed-file
+  $ hg commit -i -d '24 0' -m content-rename< y
+  > n
+  > EOF
+  diff --git a/new-file b/renamed-file
+  rename from new-file
+  rename to renamed-file
+  1 hunks, 1 lines changed
+  examine changes to 'new-file' and 'renamed-file'?
+  (enter ? for help) [Ynesfdaq?] y
+  
+  @@ -1,1 +1,2 @@
+   content
+  +new-content
+  record this change to 'renamed-file'?
+  (enter ? for help) [Ynesfdaq?] n
+  
+  $ hg status
+  M renamed-file
+  ? editedfile.orig
+  ? editedfile.rej
+  ? editor.sh
+  $ hg diff
+  diff -r a006b0d78ce9 renamed-file
+  --- a/renamed-file   Thu Jan 01 00:00:24 1970 +
+  +++ b/renamed-file   Thu Jan 01 00:00:00 1970 +
+  @@ -1,1 +1,2 @@
+   content
+  +new-content
+
 The #if execbit block above changes the hash here on some systems
   $ hg status -A plain3
   C plain3
   $ hg tip
-  changeset:   32:* (glob)
+  changeset:   34:a006b0d78ce9
   tag: tip
   user:test
-  date:Thu Jan 01 00:00:23 1970 +
-  summary: moving_files
+  date:Thu Jan 01 00:00:24 1970 +
+  summary: content-rename
   
 Editing patch of newly added file
 
   $ hg update -C .
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cat > editor.sh << '__EOF__'
   > cat "$1"  | sed "s/first/very/g"  > tt
   > mv tt  "$1"
@@ -1737,7 +1775,7 @@
   > This is the third line
   > __EOF__
   $ hg add newfile
-  $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit -i -d '23 0' 
-medit-patch-new < y
   > e
   > EOF
@@ -1770,7 +1808,7 @@
   $ cd folder
   $ echo "foo" > bar
   $ hg add bar
-  $ hg commit -i -d '23 0' -mnewfilesubdir  < y
   > y
   > EOF
@@ -1786,15 +1824,15 @@
   
 The #if execbit block above changes the hashes here on some systems
   $ hg tip -p
-  changeset:   34:* (glob)
+  changeset:   36:6d2ed8a25e2a
   tag: tip
   user:test
-  date:Thu Jan 01 00:00:23 1970 +
+  date:Thu Jan 01 00:00:26 1970 +
   summary: newfilesubdir
   
   diff -r * -r * folder/bar (glob)
   --- /dev/nullThu Jan 01 00:00:00 1970 +
-  +++ b/folder/bar Thu Jan 01 00:00:23 1970 +
+  +++ b/folder/bar Thu Jan 01 00:00:26 1970 +
   @@ -0,0 +1,1 @@
   +foo
   
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -346,18 +346,19 @@
 return isinstance(x, hunkclasses)
 
 
-def newandmodified(chunks, originalchunks):
+def isheader(x):
+headerclasses = (crecordmod.uiheader, patch.header)
+return isinstance(x, headerclasses)
+
+
+def newandmodified(chunks):
 newlyaddedandmodifiedfiles = set()
 alsorestore = set()
 for chunk in chunks:
-if (
-ishunk(chunk)
-and chunk.header.isnewfile()
-and chunk not in originalchunks
-):
-newlyaddedandmodifiedfiles.add(chunk.header.filename())
+if isheader(chunk) and chunk.isnewfile():
+newlyaddedandmodifiedfiles.add(chunk.filename())
 alsorestore.update(
-set(chunk.header.files()) - {chunk.header.filename()}
+set(chunk.files()) - {chunk.filename()}
 )
 return newlyaddedandmodifiedfiles, alsorestore
 
@@ -517,12 +518,12 @@
 diffopts.git = True
 diffopts.showfunc = True
 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
-originalchunks = 

[Bug 6533] New: hg commit --interactive doesn't handle deslecting all edits of a rename

2021-07-02 Thread mercurial-bugs
https://bz.mercurial-scm.org/show_bug.cgi?id=6533

Bug ID: 6533
   Summary: hg commit --interactive doesn't handle deslecting all
edits of a rename
   Product: Mercurial
   Version: default branch
  Hardware: All
OS: All
Status: UNCONFIRMED
  Severity: bug
  Priority: wish
 Component: Mercurial
  Assignee: bugzi...@mercurial-scm.org
  Reporter: dpl...@google.com
CC: mercurial-devel@mercurial-scm.org
Python Version: ---

Reproduction:

  > hg init repo
  > cd repo
  > echo foo > foo
  > hg commit -Am 'commit 1'
  > hg mv foo bar
  > echo bar >> bar
  > hg commit -im 'commit 2'
  # Deslect the `+bar` diff hunk, but keep the file (rename) selected

Expected state:
  > hg status
  M bar
  > hg diff
  --- a/bar
  +++ b/bar
  @@ -1,1 +1,2 @@
  foo
  +bar

Actual state:
  > hg status
  # No output
  > hg diff
  # No output
  > cat bar
  foo
  bar  # Hunk was committed despite deselection

I've identified the root cause of this error and will send a patch shortly for
it. Filing for reference.

-- 
You are receiving this mail because:
You are on the CC list for the bug.
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D10934: dirstate: split dirstatemap in its own file

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  The dirstate file is large enough and the dirstatemap is quite insulated logic
  already.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/dirstate.py
  mercurial/dirstatemap.py
  tests/fakedirstatewritetime.py

CHANGE DETAILS

diff --git a/tests/fakedirstatewritetime.py b/tests/fakedirstatewritetime.py
--- a/tests/fakedirstatewritetime.py
+++ b/tests/fakedirstatewritetime.py
@@ -10,6 +10,7 @@
 from mercurial import (
 context,
 dirstate,
+dirstatemap as dirstatemapmod,
 extensions,
 policy,
 registrar,
@@ -66,11 +67,11 @@
 if rustmod is not None:
 # The Rust implementation does not use public parse/pack dirstate
 # to prevent conversion round-trips
-orig_dirstatemap_write = dirstate.dirstatemap.write
+orig_dirstatemap_write = dirstatemapmod.dirstatemap.write
 wrapper = lambda self, st, now: orig_dirstatemap_write(
 self, st, fakenow
 )
-dirstate.dirstatemap.write = wrapper
+dirstatemapmod.dirstatemap.write = wrapper
 
 orig_dirstate_getfsnow = dirstate._getfsnow
 wrapper = lambda *args: pack_dirstate(fakenow, orig_pack_dirstate, *args)
@@ -86,7 +87,7 @@
 orig_module.pack_dirstate = orig_pack_dirstate
 dirstate._getfsnow = orig_dirstate_getfsnow
 if rustmod is not None:
-dirstate.dirstatemap.write = orig_dirstatemap_write
+dirstatemapmod.dirstatemap.write = orig_dirstatemap_write
 
 
 def _poststatusfixup(orig, workingctx, status, fixup):
diff --git a/mercurial/dirstatemap.py b/mercurial/dirstatemap.py
new file mode 100644
--- /dev/null
+++ b/mercurial/dirstatemap.py
@@ -0,0 +1,610 @@
+# dirstatemap.py
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+from __future__ import absolute_import
+
+import errno
+
+from .i18n import _
+
+from . import (
+error,
+pathutil,
+policy,
+pycompat,
+txnutil,
+util,
+)
+
+parsers = policy.importmod('parsers')
+rustmod = policy.importrust('dirstate')
+
+propertycache = util.propertycache
+
+dirstatetuple = parsers.dirstatetuple
+
+
+# a special value used internally for `size` if the file come from the other 
parent
+FROM_P2 = -2
+
+# a special value used internally for `size` if the file is 
modified/merged/added
+NONNORMAL = -1
+
+# a special value used internally for `time` if the time is ambigeous
+AMBIGUOUS_TIME = -1
+
+
+class dirstatemap(object):
+"""Map encapsulating the dirstate's contents.
+
+The dirstate contains the following state:
+
+- `identity` is the identity of the dirstate file, which can be used to
+  detect when changes have occurred to the dirstate file.
+
+- `parents` is a pair containing the parents of the working copy. The
+  parents are updated by calling `setparents`.
+
+- the state map maps filenames to tuples of (state, mode, size, mtime),
+  where state is a single character representing 'normal', 'added',
+  'removed', or 'merged'. It is read by treating the dirstate as a
+  dict.  File state is updated by calling the `addfile`, `removefile` and
+  `dropfile` methods.
+
+- `copymap` maps destination filenames to their source filename.
+
+The dirstate also provides the following views onto the state:
+
+- `nonnormalset` is a set of the filenames that have state other
+  than 'normal', or are normal but have an mtime of -1 ('normallookup').
+
+- `otherparentset` is a set of the filenames that are marked as coming
+  from the second parent when the dirstate is currently being merged.
+
+- `filefoldmap` is a dict mapping normalized filenames to the denormalized
+  form that they appear as in the dirstate.
+
+- `dirfoldmap` is a dict mapping normalized directory names to the
+  denormalized form that they appear as in the dirstate.
+"""
+
+def __init__(self, ui, opener, root, nodeconstants, use_dirstate_v2):
+self._ui = ui
+self._opener = opener
+self._root = root
+self._filename = b'dirstate'
+self._nodelen = 20
+self._nodeconstants = nodeconstants
+assert (
+not use_dirstate_v2
+), "should have detected unsupported requirement"
+
+self._parents = None
+self._dirtyparents = False
+
+# for consistent view between _pl() and _read() invocations
+self._pendingmode = None
+
+@propertycache
+def _map(self):
+self._map = {}
+self.read()
+return self._map
+
+@propertycache
+def copymap(self):
+self.copymap = {}
+self._map
+return self.copymap
+
+def directories(self):
+# Rust / dirstate-v2 only

D10933: dirstate: explicitely deal with the `added` case in `_addpath`

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This special case is now directly handled, this clarify the possible value for
  the other options.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/dirstate.py

CHANGE DETAILS

diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -464,17 +464,22 @@
 msg = _(b'file %r in dirstate clashes with %r')
 msg %= (pycompat.bytestr(d), pycompat.bytestr(f))
 raise error.Abort(msg)
-if from_p2:
+if state == b'a':
+assert not possibly_dirty
+assert not from_p2
+size = NONNORMAL
+mtime = AMBIGUOUS_TIME
+elif from_p2:
+assert not possibly_dirty
 size = FROM_P2
 mtime = AMBIGUOUS_TIME
 elif possibly_dirty:
 mtime = AMBIGUOUS_TIME
 else:
 assert size != FROM_P2
-if size != NONNORMAL:
-size = size & _rangemask
-if mtime != AMBIGUOUS_TIME:
-mtime = mtime & _rangemask
+assert size != NONNORMAL
+size = size & _rangemask
+mtime = mtime & _rangemask
 self._dirty = True
 self._updatedfiles.add(f)
 self._map.addfile(f, oldstate, state, mode, size, mtime)



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


D10932: dirstate: add an explicit `possibly_dirty` parameter to `_addpath`

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This lets says what we mean instead of using magic value. The lower level can
  then decide how to express that.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/dirstate.py

CHANGE DETAILS

diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -446,6 +446,7 @@
 size=NONNORMAL,
 mtime=AMBIGUOUS_TIME,
 from_p2=False,
+possibly_dirty=False,
 ):
 oldstate = self[f]
 if state == b'a' or oldstate == b'r':
@@ -466,6 +467,8 @@
 if from_p2:
 size = FROM_P2
 mtime = AMBIGUOUS_TIME
+elif possibly_dirty:
+mtime = AMBIGUOUS_TIME
 else:
 assert size != FROM_P2
 if size != NONNORMAL:
@@ -522,7 +525,7 @@
 return
 if entry[0] == b'm' or entry[0] == b'n' and entry[2] == 
FROM_P2:
 return
-self._addpath(f, b'n', 0)
+self._addpath(f, b'n', 0, possibly_dirty=True)
 self._map.copymap.pop(f, None)
 
 def otherparent(self, f):



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


D10935: dirstate: document the dirstatetuple content

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  We are about to modify this, so let us document the existing code.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/pure/parsers.py

CHANGE DETAILS

diff --git a/mercurial/pure/parsers.py b/mercurial/pure/parsers.py
--- a/mercurial/pure/parsers.py
+++ b/mercurial/pure/parsers.py
@@ -35,6 +35,13 @@
 # Some code below makes tuples directly because it's more convenient. However,
 # code outside this module should always use dirstatetuple.
 def dirstatetuple(*x):
+"""the four items are:
+- state (one of 'n', 'a', 'r', 'm')
+- mode,
+- size,
+- mtime,
+"""
+
 # x is a tuple
 return x
 



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


D10930: dirstate: add default value to _addpath

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  We want to remove the magic value usage from the higher level, so lets stop
  passing them explicitely when possible.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/dirstate.py

CHANGE DETAILS

diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -438,7 +438,7 @@
 def copies(self):
 return self._map.copymap
 
-def _addpath(self, f, state, mode, size, mtime):
+def _addpath(self, f, state, mode, size=NONNORMAL, mtime=AMBIGUOUS_TIME):
 oldstate = self[f]
 if state == b'a' or oldstate == b'r':
 scmutil.checkfilename(f)
@@ -509,7 +509,7 @@
 return
 if entry[0] == b'm' or entry[0] == b'n' and entry[2] == 
FROM_P2:
 return
-self._addpath(f, b'n', 0, NONNORMAL, AMBIGUOUS_TIME)
+self._addpath(f, b'n', 0)
 self._map.copymap.pop(f, None)
 
 def otherparent(self, f):
@@ -519,15 +519,15 @@
 raise error.Abort(msg)
 if f in self and self[f] == b'n':
 # merge-like
-self._addpath(f, b'm', 0, FROM_P2, AMBIGUOUS_TIME)
+self._addpath(f, b'm', 0, FROM_P2)
 else:
 # add-like
-self._addpath(f, b'n', 0, FROM_P2, AMBIGUOUS_TIME)
+self._addpath(f, b'n', 0, FROM_P2)
 self._map.copymap.pop(f, None)
 
 def add(self, f):
 '''Mark a file added.'''
-self._addpath(f, b'a', 0, NONNORMAL, AMBIGUOUS_TIME)
+self._addpath(f, b'a', 0)
 self._map.copymap.pop(f, None)
 
 def remove(self, f):



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


D10931: dirstate: add an explicit `from_p2` parameter to `_addpath`

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This lets says what we mean instead of using magic value. The lower level can
  then decide how to express that.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/dirstate.py

CHANGE DETAILS

diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -438,7 +438,15 @@
 def copies(self):
 return self._map.copymap
 
-def _addpath(self, f, state, mode, size=NONNORMAL, mtime=AMBIGUOUS_TIME):
+def _addpath(
+self,
+f,
+state,
+mode,
+size=NONNORMAL,
+mtime=AMBIGUOUS_TIME,
+from_p2=False,
+):
 oldstate = self[f]
 if state == b'a' or oldstate == b'r':
 scmutil.checkfilename(f)
@@ -455,10 +463,15 @@
 msg = _(b'file %r in dirstate clashes with %r')
 msg %= (pycompat.bytestr(d), pycompat.bytestr(f))
 raise error.Abort(msg)
-if size != NONNORMAL and size != FROM_P2:
-size = size & _rangemask
-if mtime != AMBIGUOUS_TIME:
-mtime = mtime & _rangemask
+if from_p2:
+size = FROM_P2
+mtime = AMBIGUOUS_TIME
+else:
+assert size != FROM_P2
+if size != NONNORMAL:
+size = size & _rangemask
+if mtime != AMBIGUOUS_TIME:
+mtime = mtime & _rangemask
 self._dirty = True
 self._updatedfiles.add(f)
 self._map.addfile(f, oldstate, state, mode, size, mtime)
@@ -519,10 +532,10 @@
 raise error.Abort(msg)
 if f in self and self[f] == b'n':
 # merge-like
-self._addpath(f, b'm', 0, FROM_P2)
+self._addpath(f, b'm', 0, from_p2=True)
 else:
 # add-like
-self._addpath(f, b'n', 0, FROM_P2)
+self._addpath(f, b'n', 0, from_p2=True)
 self._map.copymap.pop(f, None)
 
 def add(self, f):



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


D10929: dirstate: move the _rangemask filtering closer to its storage

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  The goal is to get ready to move these kind of processing at a lower level. 
We start with move
  move _rangemask filtering close to where it is sent to the lower level to 
make the future move trivial.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/dirstate.py

CHANGE DETAILS

diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -455,6 +455,10 @@
 msg = _(b'file %r in dirstate clashes with %r')
 msg %= (pycompat.bytestr(d), pycompat.bytestr(f))
 raise error.Abort(msg)
+if size != NONNORMAL and size != FROM_P2:
+size = size & _rangemask
+if mtime != AMBIGUOUS_TIME:
+mtime = mtime & _rangemask
 self._dirty = True
 self._updatedfiles.add(f)
 self._map.addfile(f, oldstate, state, mode, size, mtime)
@@ -476,7 +480,7 @@
 mode = s.st_mode
 size = s.st_size
 mtime = s[stat.ST_MTIME]
-self._addpath(f, b'n', mode, size & _rangemask, mtime & _rangemask)
+self._addpath(f, b'n', mode, size, mtime)
 self._map.copymap.pop(f, None)
 if f in self._map.nonnormalset:
 self._map.nonnormalset.remove(f)



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


D10928: dirstate: introduce a symbolic constant for the AMBIGUOUS_TIME marker

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This is going to be clearer and easier to track than -1. Ultimately I would
  like to get ride of this special value everywhere but in the lower level,
  however we need to clarify the API first. This changeset is part of such
  clarification.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/dirstate.py

CHANGE DETAILS

diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -54,6 +54,9 @@
 # a special value used internally for `size` if the file is 
modified/merged/added
 NONNORMAL = -1
 
+# a special value used internally for `time` if the time is ambigeous
+AMBIGUOUS_TIME = -1
+
 
 class repocache(filecache):
 """filecache for files in .hg/"""
@@ -502,7 +505,7 @@
 return
 if entry[0] == b'm' or entry[0] == b'n' and entry[2] == 
FROM_P2:
 return
-self._addpath(f, b'n', 0, NONNORMAL, -1)
+self._addpath(f, b'n', 0, NONNORMAL, AMBIGUOUS_TIME)
 self._map.copymap.pop(f, None)
 
 def otherparent(self, f):
@@ -512,15 +515,15 @@
 raise error.Abort(msg)
 if f in self and self[f] == b'n':
 # merge-like
-self._addpath(f, b'm', 0, FROM_P2, -1)
+self._addpath(f, b'm', 0, FROM_P2, AMBIGUOUS_TIME)
 else:
 # add-like
-self._addpath(f, b'n', 0, FROM_P2, -1)
+self._addpath(f, b'n', 0, FROM_P2, AMBIGUOUS_TIME)
 self._map.copymap.pop(f, None)
 
 def add(self, f):
 '''Mark a file added.'''
-self._addpath(f, b'a', 0, NONNORMAL, -1)
+self._addpath(f, b'a', 0, NONNORMAL, AMBIGUOUS_TIME)
 self._map.copymap.pop(f, None)
 
 def remove(self, f):
@@ -1537,7 +1540,7 @@
 if oldstate == b"?" and "_alldirs" in self.__dict__:
 self._alldirs.addpath(f)
 self._map[f] = dirstatetuple(state, mode, size, mtime)
-if state != b'n' or mtime == -1:
+if state != b'n' or mtime == AMBIGUOUS_TIME:
 self.nonnormalset.add(f)
 if size == FROM_P2:
 self.otherparentset.add(f)
@@ -1581,7 +1584,7 @@
 for f in files:
 e = self.get(f)
 if e is not None and e[0] == b'n' and e[3] == now:
-self._map[f] = dirstatetuple(e[0], e[1], e[2], -1)
+self._map[f] = dirstatetuple(e[0], e[1], e[2], AMBIGUOUS_TIME)
 self.nonnormalset.add(f)
 
 def nonnormalentries(self):
@@ -1592,7 +1595,7 @@
 nonnorm = set()
 otherparent = set()
 for fname, e in pycompat.iteritems(self._map):
-if e[0] != b'n' or e[3] == -1:
+if e[0] != b'n' or e[3] == AMBIGUOUS_TIME:
 nonnorm.add(fname)
 if e[0] == b'n' and e[2] == FROM_P2:
 otherparent.add(fname)



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


D10927: dirstate: introduce a symbolic constant for the NONNORMAL marker

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This is going to be clearer and easier to track than -1. Ultimately I would
  like to get ride of this special value everywhere but in the lower level,
  however we need to clarify the API first. This changeset is part of such
  clarification.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/dirstate.py

CHANGE DETAILS

diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -51,6 +51,9 @@
 # a special value used internally for `size` if the file come from the other 
parent
 FROM_P2 = -2
 
+# a special value used internally for `size` if the file is 
modified/merged/added
+NONNORMAL = -1
+
 
 class repocache(filecache):
 """filecache for files in .hg/"""
@@ -488,9 +491,9 @@
 # being removed, restore that state.
 entry = self._map.get(f)
 if entry is not None:
-if entry[0] == b'r' and entry[2] in (-1, FROM_P2):
+if entry[0] == b'r' and entry[2] in (NONNORMAL, FROM_P2):
 source = self._map.copymap.get(f)
-if entry[2] == -1:
+if entry[2] == NONNORMAL:
 self.merge(f)
 elif entry[2] == FROM_P2:
 self.otherparent(f)
@@ -499,7 +502,7 @@
 return
 if entry[0] == b'm' or entry[0] == b'n' and entry[2] == 
FROM_P2:
 return
-self._addpath(f, b'n', 0, -1, -1)
+self._addpath(f, b'n', 0, NONNORMAL, -1)
 self._map.copymap.pop(f, None)
 
 def otherparent(self, f):
@@ -517,7 +520,7 @@
 
 def add(self, f):
 '''Mark a file added.'''
-self._addpath(f, b'a', 0, -1, -1)
+self._addpath(f, b'a', 0, NONNORMAL, -1)
 self._map.copymap.pop(f, None)
 
 def remove(self, f):
@@ -530,7 +533,7 @@
 if entry is not None:
 # backup the previous state
 if entry[0] == b'm':  # merge
-size = -1
+size = NONNORMAL
 elif entry[0] == b'n' and entry[2] == FROM_P2:  # other parent
 size = FROM_P2
 self._map.otherparentset.add(f)



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


D10926: dirstate: introduce a symbolic constant for the FROM_P2 marker

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This is going to be clearer and easier to track than -2. Ultimately I would
  like to get ride of this special value everywhere but in the lower level,
  however we need to clarify the API first. This changeset is part of such
  clarification.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/dirstate.py

CHANGE DETAILS

diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -48,6 +48,10 @@
 dirstatetuple = parsers.dirstatetuple
 
 
+# a special value used internally for `size` if the file come from the other 
parent
+FROM_P2 = -2
+
+
 class repocache(filecache):
 """filecache for files in .hg/"""
 
@@ -371,7 +375,7 @@
 copies[f] = source
 self.normallookup(f)
 # Also fix up otherparent markers
-elif s[0] == b'n' and s[2] == -2:
+elif s[0] == b'n' and s[2] == FROM_P2:
 source = self._map.copymap.get(f)
 if source:
 copies[f] = source
@@ -484,16 +488,16 @@
 # being removed, restore that state.
 entry = self._map.get(f)
 if entry is not None:
-if entry[0] == b'r' and entry[2] in (-1, -2):
+if entry[0] == b'r' and entry[2] in (-1, FROM_P2):
 source = self._map.copymap.get(f)
 if entry[2] == -1:
 self.merge(f)
-elif entry[2] == -2:
+elif entry[2] == FROM_P2:
 self.otherparent(f)
 if source:
 self.copy(source, f)
 return
-if entry[0] == b'm' or entry[0] == b'n' and entry[2] == -2:
+if entry[0] == b'm' or entry[0] == b'n' and entry[2] == 
FROM_P2:
 return
 self._addpath(f, b'n', 0, -1, -1)
 self._map.copymap.pop(f, None)
@@ -505,10 +509,10 @@
 raise error.Abort(msg)
 if f in self and self[f] == b'n':
 # merge-like
-self._addpath(f, b'm', 0, -2, -1)
+self._addpath(f, b'm', 0, FROM_P2, -1)
 else:
 # add-like
-self._addpath(f, b'n', 0, -2, -1)
+self._addpath(f, b'n', 0, FROM_P2, -1)
 self._map.copymap.pop(f, None)
 
 def add(self, f):
@@ -527,8 +531,8 @@
 # backup the previous state
 if entry[0] == b'm':  # merge
 size = -1
-elif entry[0] == b'n' and entry[2] == -2:  # other parent
-size = -2
+elif entry[0] == b'n' and entry[2] == FROM_P2:  # other parent
+size = FROM_P2
 self._map.otherparentset.add(f)
 self._updatedfiles.add(f)
 self._map.removefile(f, oldstate, size)
@@ -1302,7 +1306,7 @@
 (size != st.st_size and size != st.st_size & 
_rangemask)
 or ((mode ^ st.st_mode) & 0o100 and checkexec)
 )
-or size == -2  # other parent
+or size == FROM_P2  # other parent
 or fn in copymap
 ):
 if stat.S_ISLNK(st.st_mode) and size != st.st_size:
@@ -1532,7 +1536,7 @@
 self._map[f] = dirstatetuple(state, mode, size, mtime)
 if state != b'n' or mtime == -1:
 self.nonnormalset.add(f)
-if size == -2:
+if size == FROM_P2:
 self.otherparentset.add(f)
 
 def removefile(self, f, oldstate, size):
@@ -1587,7 +1591,7 @@
 for fname, e in pycompat.iteritems(self._map):
 if e[0] != b'n' or e[3] == -1:
 nonnorm.add(fname)
-if e[0] == b'n' and e[2] == -2:
+if e[0] == b'n' and e[2] == FROM_P2:
 otherparent.add(fname)
 return nonnorm, otherparent
 



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


D10925: dirstate: split a not-so-one-liner

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This is shorter and simpler to read.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/dirstate.py

CHANGE DETAILS

diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -442,10 +442,9 @@
 break
 entry = self._map.get(d)
 if entry is not None and entry[0] != b'r':
-raise error.Abort(
-_(b'file %r in dirstate clashes with %r')
-% (pycompat.bytestr(d), pycompat.bytestr(f))
-)
+msg = _(b'file %r in dirstate clashes with %r')
+msg %= (pycompat.bytestr(d), pycompat.bytestr(f))
+raise error.Abort(msg)
 self._dirty = True
 self._updatedfiles.add(f)
 self._map.addfile(f, oldstate, state, mode, size, mtime)



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


D10924: dirstate: split a not-so-one-liner

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This is simpler to read.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/dirstate.py

CHANGE DETAILS

diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -433,9 +433,9 @@
 if state == b'a' or oldstate == b'r':
 scmutil.checkfilename(f)
 if self._map.hastrackeddir(f):
-raise error.Abort(
-_(b'directory %r already in dirstate') % 
pycompat.bytestr(f)
-)
+msg = _(b'directory %r already in dirstate')
+msg %= pycompat.bytestr(f)
+raise error.Abort(msg)
 # shadows
 for d in pathutil.finddirs(f):
 if self._map.hastrackeddir(d):



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


D10923: dirstate: split a not-so-one-liner

2021-07-02 Thread marmoute (Pierre-Yves David)
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This is shorter and simpler to read.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/dirstate.py

CHANGE DETAILS

diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -502,9 +502,8 @@
 def otherparent(self, f):
 '''Mark as coming from the other parent, always dirty.'''
 if self._pl[1] == self._nodeconstants.nullid:
-raise error.Abort(
-_(b"setting %r to other parent only allowed in merges") % f
-)
+msg = _(b"setting %r to other parent only allowed in merges") % f
+raise error.Abort(msg)
 if f in self and self[f] == b'n':
 # merge-like
 self._addpath(f, b'm', 0, -2, -1)



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


D10921: dirstate: Removed unused instances of `DirsMultiset`

2021-07-02 Thread SimonSapin
SimonSapin created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  … in Rust-backed dirstatemap.
  
  The Python class `dirstatemap` had cached properties `_dirs` and `_alldirs`
  that were not used for `hastrackeddir` and `hasdir` since they were redundant
  with corresponding fields for the Rust `DirstateMap` struct.
  
  `dirfoldmap` is modified to reuse instead the directory iterator introduced
  in 3b9914b28133c0918186b6e8b9e4f1916e21338d 
.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  contrib/perf.py
  mercurial/dirstate.py
  rust/hg-core/src/dirstate_tree/dirstate_map.rs
  rust/hg-core/src/dirstate_tree/dispatch.rs
  rust/hg-cpython/src/dirstate/dirstate_map.rs
  rust/hg-cpython/src/dirstate/dispatch.rs

CHANGE DETAILS

diff --git a/rust/hg-cpython/src/dirstate/dispatch.rs 
b/rust/hg-cpython/src/dirstate/dispatch.rs
--- a/rust/hg-cpython/src/dirstate/dispatch.rs
+++ b/rust/hg-cpython/src/dirstate/dispatch.rs
@@ -128,14 +128,6 @@
 self.get_mut().pack_v2(parents, now)
 }
 
-fn set_all_dirs( self) -> Result<(), DirstateError> {
-self.get_mut().set_all_dirs()
-}
-
-fn set_dirs( self) -> Result<(), DirstateError> {
-self.get_mut().set_dirs()
-}
-
 fn status<'a>(
 &'a mut self,
 matcher: &'a (dyn Matcher + Sync),
diff --git a/rust/hg-cpython/src/dirstate/dirstate_map.rs 
b/rust/hg-cpython/src/dirstate/dirstate_map.rs
--- a/rust/hg-cpython/src/dirstate/dirstate_map.rs
+++ b/rust/hg-cpython/src/dirstate/dirstate_map.rs
@@ -19,11 +19,11 @@
 
 use crate::{
 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
+dirstate::make_dirstate_tuple,
 dirstate::non_normal_entries::{
 NonNormalEntries, NonNormalEntriesIterator,
 },
 dirstate::owning::OwningDirstateMap,
-dirstate::{dirs_multiset::Dirs, make_dirstate_tuple},
 parsers::dirstate_parents_to_pytuple,
 };
 use hg::{
@@ -34,8 +34,8 @@
 revlog::Node,
 utils::files::normalize_case,
 utils::hg_path::{HgPath, HgPathBuf},
-DirsMultiset, DirstateEntry, DirstateError,
-DirstateMap as RustDirstateMap, DirstateParents, EntryState, StateMapIter,
+DirstateEntry, DirstateError, DirstateMap as RustDirstateMap,
+DirstateParents, EntryState, StateMapIter,
 };
 
 // TODO
@@ -391,40 +391,6 @@
 )
 }
 
-def getdirs() -> PyResult {
-// TODO don't copy, share the reference
-self.inner(py).borrow_mut().set_dirs()
-.map_err(|e| {
-PyErr::new::(py, e.to_string())
-})?;
-Dirs::from_inner(
-py,
-DirsMultiset::from_dirstate(
-self.inner(py).borrow().iter(),
-Some(EntryState::Removed),
-)
-.map_err(|e| {
-PyErr::new::(py, e.to_string())
-})?,
-)
-}
-def getalldirs() -> PyResult {
-// TODO don't copy, share the reference
-self.inner(py).borrow_mut().set_all_dirs()
-.map_err(|e| {
-PyErr::new::(py, e.to_string())
-})?;
-Dirs::from_inner(
-py,
-DirsMultiset::from_dirstate(
-self.inner(py).borrow().iter(),
-None,
-).map_err(|e| {
-PyErr::new::(py, e.to_string())
-})?,
-)
-}
-
 // TODO all copymap* methods, see docstring above
 def copymapcopy() -> PyResult {
 let dict = PyDict::new(py);
diff --git a/rust/hg-core/src/dirstate_tree/dispatch.rs 
b/rust/hg-core/src/dirstate_tree/dispatch.rs
--- a/rust/hg-core/src/dirstate_tree/dispatch.rs
+++ b/rust/hg-core/src/dirstate_tree/dispatch.rs
@@ -95,10 +95,6 @@
 now: Timestamp,
 ) -> Result, DirstateError>;
 
-fn set_all_dirs( self) -> Result<(), DirstateError>;
-
-fn set_dirs( self) -> Result<(), DirstateError>;
-
 fn status<'a>(
 &'a mut self,
 matcher: &'a (dyn Matcher + Sync),
@@ -281,14 +277,6 @@
 )
 }
 
-fn set_all_dirs( self) -> Result<(), DirstateError> {
-self.set_all_dirs()
-}
-
-fn set_dirs( self) -> Result<(), DirstateError> {
-self.set_dirs()
-}
-
 fn status<'a>(
 &'a mut self,
 matcher: &'a (dyn Matcher + Sync),
diff --git a/rust/hg-core/src/dirstate_tree/dirstate_map.rs 
b/rust/hg-core/src/dirstate_tree/dirstate_map.rs
--- a/rust/hg-core/src/dirstate_tree/dirstate_map.rs
+++ b/rust/hg-core/src/dirstate_tree/dirstate_map.rs
@@ -977,18 +977,6 @@
 on_disk::write(self, parents)
 }
 
-fn set_all_dirs( self) -> Result<(), DirstateError> {
-// Do nothing, this `DirstateMap` does not a separate `all_dirs` that
-// needs to be recomputed
-Ok(())
-}
-

D10922: dirstate-tree: Keep a counter of descendant nodes that have an entry

2021-07-02 Thread SimonSapin
SimonSapin created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  … and change the `DirstateMap::has_dir` method to be based on this counter
  being non-zero instead of the mere presence of a node.
  
  A node with zero descendent with an entry currently should be removed from
  the tree, but soon we’ll make the dirstate track additional nodes.
  (Specifically, for non-ignored directories in order to keep track of their
  mtime and optimize status by doing fewer `read_dir` calls.)

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  rust/hg-core/src/dirstate_tree/dirstate_map.rs
  rust/hg-core/src/dirstate_tree/on_disk.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/dirstate_tree/on_disk.rs 
b/rust/hg-core/src/dirstate_tree/on_disk.rs
--- a/rust/hg-core/src/dirstate_tree/on_disk.rs
+++ b/rust/hg-core/src/dirstate_tree/on_disk.rs
@@ -76,6 +76,7 @@
 
 copy_source: OptPathSlice,
 children: ChildNodes,
+pub(super) descendants_with_entry_count: Size,
 pub(super) tracked_descendants_count: Size,
 
 /// Dependending on the value of `state`:
@@ -172,7 +173,7 @@
 /// Make sure that size-affecting changes are made knowingly
 fn _static_assert_size_of() {
 let _ = std::mem::transmute::;
-let _ = std::mem::transmute::;
+let _ = std::mem::transmute::;
 }
 
 /// Unexpected file format found in `.hg/dirstate` with the "v2" format.
@@ -360,6 +361,9 @@
 ),
 copy_source: self.copy_source(on_disk)?.map(Cow::Borrowed),
 data: self.node_data()?,
+descendants_with_entry_count: self
+.descendants_with_entry_count
+.get(),
 tracked_descendants_count: self.tracked_descendants_count.get(),
 })
 }
@@ -565,6 +569,9 @@
 // Could only panic for paths over 4 GiB
 .expect("dirstate-v2 offset overflow")
 .into(),
+descendants_with_entry_count: node
+.descendants_with_entry_count
+.into(),
 tracked_descendants_count: node
 .tracked_descendants_count
 .into(),
diff --git a/rust/hg-core/src/dirstate_tree/dirstate_map.rs 
b/rust/hg-core/src/dirstate_tree/dirstate_map.rs
--- a/rust/hg-core/src/dirstate_tree/dirstate_map.rs
+++ b/rust/hg-core/src/dirstate_tree/dirstate_map.rs
@@ -332,6 +332,15 @@
 }
 }
 
+pub(super) fn descendants_with_entry_count() -> u32 {
+match self {
+NodeRef::InMemory(_path, node) => {
+node.descendants_with_entry_count
+}
+NodeRef::OnDisk(node) => node.descendants_with_entry_count.get(),
+}
+}
+
 pub(super) fn tracked_descendants_count() -> u32 {
 match self {
 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
@@ -349,7 +358,11 @@
 
 pub(super) children: ChildNodes<'on_disk>,
 
-/// How many (non-inclusive) descendants of this node are tracked files
+/// How many (non-inclusive) descendants of this node have an entry.
+pub(super) descendants_with_entry_count: u32,
+
+/// How many (non-inclusive) descendants of this node have an entry whose
+/// state is "tracked".
 pub(super) tracked_descendants_count: u32,
 }
 
@@ -421,6 +434,7 @@
 if tracked {
 ancestor.tracked_descendants_count += 1
 }
+ancestor.descendants_with_entry_count += 1
 },
 )?;
 assert!(
@@ -547,6 +561,7 @@
 old_state: EntryState,
 new_entry: DirstateEntry,
 ) -> Result<(), DirstateV2ParseError> {
+let had_entry = old_state != EntryState::Unknown;
 let tracked_count_increment =
 match (old_state.is_tracked(), new_entry.state.is_tracked()) {
 (false, true) => 1,
@@ -560,6 +575,10 @@
 path,
 WithBasename::to_cow_owned,
 |ancestor| {
+if !had_entry {
+ancestor.descendants_with_entry_count += 1;
+}
+
 // We can’t use `+= increment` because the counter is unsigned,
 // and we want debug builds to detect accidental underflow
 // through zero
@@ -570,7 +589,7 @@
 }
 },
 )?;
-if !node.data.has_entry() {
+if !had_entry {
 self.nodes_with_entry_count += 1
 }
 node.data = NodeData::Entry(new_entry);
@@ -755,6 +774,9 @@
 recur(on_disk,  node.children, rest)?
 {
 dropped = d;
+if dropped.had_entry {
+

D10920: dirstate-v2: Use 32-bit integers instead of 64-bit for offsets

2021-07-02 Thread SimonSapin
SimonSapin created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This saves 12 bytes per node. (Nodes representing files or directories.)
  
  These are offsets to other parts of the file. This would only be a limitation
  for a `.hg/dirstate` file larger than 4 GiB, which would only happen for a
  repository with dozens of millions of files and directories.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  rust/hg-core/src/dirstate_tree/on_disk.rs
  tests/test-hgignore.t

CHANGE DETAILS

diff --git a/tests/test-hgignore.t b/tests/test-hgignore.t
--- a/tests/test-hgignore.t
+++ b/tests/test-hgignore.t
@@ -406,19 +406,19 @@
 #if dirstate-v2
 
 Check the hash of ignore patterns written in the dirstate at offset
-12 + 20 + 20 + 8 + 4 + 4 + 4 = 72
+12 + 20 + 20 + 4 + 4 + 4 + 4 = 68
 
   $ hg status > /dev/null
   $ cat .hg/testhgignore .hg/testhgignorerel .hgignore dir2/.hgignore 
dir1/.hgignore dir1/.hgignoretwo | $TESTDIR/f --sha1
   sha1=6e315b60f15fb5dfa02be00f3e2c8f923051f5ff
-  >>> import binascii; print(binascii.hexlify(open(".hg/dirstate", 
"rb").read()[72:][:20]).decode())
+  >>> import binascii; print(binascii.hexlify(open(".hg/dirstate", 
"rb").read()[68:][:20]).decode())
   6e315b60f15fb5dfa02be00f3e2c8f923051f5ff
 
   $ echo rel > .hg/testhgignorerel
   $ hg status > /dev/null
   $ cat .hg/testhgignore .hg/testhgignorerel .hgignore dir2/.hgignore 
dir1/.hgignore dir1/.hgignoretwo | $TESTDIR/f --sha1
   sha1=dea19cc7119213f24b6b582a4bae7b0cb063e34e
-  >>> import binascii; print(binascii.hexlify(open(".hg/dirstate", 
"rb").read()[72:][:20]).decode())
+  >>> import binascii; print(binascii.hexlify(open(".hg/dirstate", 
"rb").read()[68:][:20]).decode())
   dea19cc7119213f24b6b582a4bae7b0cb063e34e
 
 #endif
diff --git a/rust/hg-core/src/dirstate_tree/on_disk.rs 
b/rust/hg-core/src/dirstate_tree/on_disk.rs
--- a/rust/hg-core/src/dirstate_tree/on_disk.rs
+++ b/rust/hg-core/src/dirstate_tree/on_disk.rs
@@ -17,7 +17,7 @@
 use crate::DirstateError;
 use crate::DirstateParents;
 use crate::EntryState;
-use bytes_cast::unaligned::{I32Be, I64Be, U32Be, U64Be};
+use bytes_cast::unaligned::{I32Be, I64Be, U32Be};
 use bytes_cast::BytesCast;
 use std::borrow::Cow;
 use std::convert::TryFrom;
@@ -135,9 +135,8 @@
 
 /// Counted in bytes from the start of the file
 ///
-/// NOTE: If we decide to never support `.hg/dirstate` files larger than 4 GiB
-/// we could save space by using `U32Be` instead.
-type Offset = U64Be;
+/// NOTE: not supporting `.hg/dirstate` files larger than 4 GiB.
+type Offset = U32Be;
 
 /// Counted in number of items
 ///
@@ -172,8 +171,8 @@
 
 /// Make sure that size-affecting changes are made knowingly
 fn _static_assert_size_of() {
-let _ = std::mem::transmute::;
-let _ = std::mem::transmute::;
+let _ = std::mem::transmute::;
+let _ = std::mem::transmute::;
 }
 
 /// Unexpected file format found in `.hg/dirstate` with the "v2" format.
@@ -589,8 +588,8 @@
 where
 T: BytesCast,
 {
-let start = u64::try_from(out.len())
-// Could only panic on a 128-bit CPU with a dirstate over 16 EiB
+let start = u32::try_from(out.len())
+// Could only panic for a dirstate file larger than 4 GiB
 .expect("dirstate-v2 offset overflow")
 .into();
 let len = u32::try_from(slice.len())



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