##// END OF EJS Templates
merge with stable
Martin von Zweigbergk -
r43741:93f74a7d merge default
parent child Browse files
Show More
@@ -0,0 +1,15 b''
1 FROM centos:centos8
2
3 RUN groupadd -g %GID% build && \
4 useradd -u %UID% -g %GID% -s /bin/bash -d /build -m build
5
6 RUN yum install -y \
7 gcc \
8 gettext \
9 make \
10 python3-devel \
11 python3-docutils \
12 rpm-build
13
14 # For creating repo meta data
15 RUN yum install -y createrepo
@@ -0,0 +1,6 b''
1 from __future__ import absolute_import, print_function
2
3 import re
4 import sys
5
6 print(re.sub(r"(?<=Message-Id:) \n ", " ", sys.stdin.read()), end="")
@@ -185,3 +185,5 b' e386b5f4f8360dbb43a576dd9b1368e386fefa5b'
185 185 e91930d712e8507d1bc1b2dffd96c83edc4cbed3 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl1DD/sQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91bvmD/4/QDZZGVe+WiMUxbT+grfFjwjX4nkg7Vt+6vQbjN68NC5XpSiCzW8uu0LRemX0KJKoOfQxqHk3YKkZZHIk10Fe6RSLWt8dqlfa2J9B2U8DwMEBykCOuxcLlDe7DGaaMXlXXRhNXebRheNPLeNe+r7beMAAjwchTIIJD5xcFnPRFR0nN7Vj7eRUdWIQ9H/s7TolPz1Mf7IWqapLjPtofiwSgtRoXfIAkuuabnE4eMVJ8rsLwcuMhxWP2zjEfEg68YkiGBAFmlnRk+3lJpiB9kVapB3cWcsWv2OBhz0D3NgGp82eWkjJCZZhZ+zHHrQ6L9zbiArzW9NVvPEAKLbl3XUhFUzFTUD+S38wsYLYL5RkzhlCI2/K1LJLOtj7r0Seen0v8X842p0cXmxTg/o1Vg3JOm04l9AwzCsnqwIqV7Ru//KPqH91MFFH6T6tbfjtLHRmjxRjMZmVt7ZQjS84opVCZwgUTZZJB2kd1goROjdowQVK6qsEonlzGjWb9zc3el5L9uzDeim3e5t2GNRVt8veQaLc+U2hHWniVsDJMvqp2Hr9IWUKp+bu/35B1nElvooS40gj2WhkfkCbbXSg9qnVLwGxxcGdF28Z0nhQcfKiJAc+8l9l19GNhdKxOi4zUXlp90opPWfT7wGQmysvTjQeFL2zX9ziuHUZZwlW1YbeMQ==
186 186 a4e32fd539ab41489a51b2aa88bda9a73b839562 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl1xTxUQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91ZQgD/96mViQ6fEh84l4XyAlY6Dq3SgMqEXttsUpk/GPoW4ykDFKN6VoiOaPoyNODO/46V3yeAjYjy3vX7Ua4/MY1NlnNoliQcTYtRV3SlDdoueTPOLfO6YSV27LG+dX/HYvPc/htCVmIVItU1JL+KEpXnv+bT50Bk+m6OgzfJMDzdHQ5ICImT8gW7UXlH/mlNtWMOrJDk3cArGhGs/pTFVrfgRTfDfDGSA9xW0/QvsNI5iwZHgMYaqoPFDnw6d/NXWRlk77KNiXkBEOKHf6UEWecMKmiSCm8RePSiX9ezqdcBAHygOg4KUeiR2kPNl4QJtskyG4CwWxlmGlfgKx07s7rGafE+DWLEYC9Wa8qK6/LPiowm17m/UlAYxdFXaBCiN0wgEw7oNmjcx/791ez+CL1+h6pd0+iSVI4bO9/YZ8LPROYef18MFm+IFIDIOgZU4eUbpBrzBb3IM1a519xgnmWXAjtRtGWEZMuHaSoLJf2pDXvaUPX6YpJeqCBFO3q/swbiJsQsy6xRW0Dwtn7umU1PGdmMoTnskTRKy9Kgzv7lf/nsUuRbzzM4ut9m1TOo27AulObMrmQB4YvLi/LEnYaRNx18yaqOceMxb/mS0tHLgcZToy9rTV+vtC21vgwfzGia2neLLe50tnIsBPP/AdTOw9ZDMRfXMCajWM22hPxvnGcw==
187 187 181e52f2b62f4768aa0d988936c929dc7c4a41a0 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl2UzlMQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91SDzD/0YZqtN+LK5AusJjWaTa61DRIPhJQoZD+HKg4kAzjL8zw8SxBGLxMZkGmve9QFMNzqIr5kkPk6yEKrEWYqyPtpwrv5Xh5D4d8AKfphdzwSr+BvMk4fBEvwnBhrUJtKDEiuYQdbh4+OQfQs1c3xhtinjXn30160uzFvLQY6/h4hxai2XWj4trgoNXqPHDHlQKc6kRfPpmNO2UZhG+2Xfsava2JpcP4xA2R0XkI10be5MDoGU4AFCMUcXZzIto0DYT+HOezowoNpdC1EWVHfa+bdrlzHHO7WPaTLzEPy44/IhXmNhbwFKOk5RZ/qBADQvs9BDfmIDczOoZKTC5+ESZM0PR2np5t7+JFMUeeRcINqBdSc4Aszw3iHjgNbJJ3viU72JZvGGGd9MglP590tA0proVGxQgvXDq3mtq3Se5yOLAjmRnktW5Tnt8/Z3ycuZz+QsTEMXR5uIZvgz63ibfsCGTXFYUz9h7McGgmhfKWvQw9+MH6kRbE9U8qaUumgf4zi4HNzmf8AyaMJo07DIMwWVgjlVUdWUlN/Eg61fU3wC79mV8mLVsi5/TZ986obz4csoYSYXyyez5ScRji+znSw8vUx0YhoiOQbDms/y2QZR/toyon554tHkDZsya2lhpwXs8T0IFZhERXsmz/XmT3fWnhSzyrUe6VjBMep1zn6lvQ==
188 59338f9561099de77c684c00f76507f11e46ebe8 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl2ty1MQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91XBUD/wJqwW0cuMCUvuUODLIfWa7ZxNl1mV9eW3tFQEuLGry97s12KDwBe0Erdjj7DASl4/6Xpc4PYxelZwSw4xT1UQg7wd/C3daCq/cDXrAkl7ZNTAHu6iAnHh25mOpIBfhMbh4j3YD0A2OoI17QGScU6S7Uv0Gz1CY20lJmEqsMzuuDPm2zrdPnTWffRUuPgskAg3czaw45Na7nUBeaxN1On0O5WqMYZsCGyi14g5S0Z0LHMKRJzc/s48JUTDjTbbzJ6HBxrxWTW2v8gN2J6QDYykcLBB9kV6laal9jhWs9n/w0yWwHfBfJ+E4EiMXeRdZgGA55OCOuDxnmmONs1/Z0WwPo+vQlowEnjDMT0jPrPePZ5P4BDXZD3tGsmdXDHM7j+VfDyPh1FBFpcaej44t84X1OWtAnLZ3VMPLwobz9MOzz4wr9UuHq23hus0Fen+FJYOAlTx9qPAqBrCTpGl+h1DMKD62D7lF8Z1CxTlqg9PPBB7IZNCXoN7FZ4Wfhv1AarMVNNUgBx6m0r6OScCXrluuFklYDSIZrfgiwosXxsHW27RjxktrV4O+J1GT/chLBJFViTZg/gX/9UC3eLkzp1t6gC6T9SQ+lq0/I+1/rHQkxNaywLycBPOG1yb/59mibEwB9+Mu9anRYKFNHEktNoEmyw5G9UoZhD+1tHt4tkJCwA==
189 ca3dca416f8d5863ca6f5a4a6a6bb835dcd5feeb 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl3BrQ4QHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91ZXjEACfBdZczf0a4bmeaaxRwxXAniSS4rVkF790g22fsvSZFvQEpmwqNtsvbTt3N1V2QSDSZyhBa+/qfpuZ689VXMlR3rcJOVjo/7193QLXHOPfRn7sDeeCxjsbtXXLbLa8UT56gtT5gUa4i0LC2kHBEi+UhV9EGgSaDTBxWUFJ9RY2sosy1XFiOUlkUoHUbqUF28J3/CxEXzULWkqTOPwh94JYsgXSSS69WNZEfsuEBSPCzn8Gd7z7lWudZ/VTZBTpTji7HQxpFtSZxNzpwmcmVOH9HlEKoA1K4JoR+1TMHqSytQXlz3FMF6c6Z1G+OPpwTGCjGTkB9ZAusP3gU8KIZTTEXthiEluRtnRq1yu4K2LTyY172JPJvANAWpVEvBvn4k5c9tDOEt9RCAPqCrgNGzDTrw02+gZyyNkjcS6hPn+cDJ6OQ1j2eCQtHlqfHLSc7FsRjUSTiKSEUTdWvHbNfOYe6Yth/tnQ7TnpnS9S0eiugFzZs2f8P85Gfa3uTFQIDm67Ud+8Yu1uOxa6bhECLaXEACnLofzz8sioLsJMiOoG2HmwhyPyfZUHXlb2zdsSP3LC+gKN39VvzSxhhjrIUJoM4ulP0GP1/lkMVzOady66iLaEwDvEn4FLmu395SubHwbre1Jx83hiCQpZfPkI0PhKnh4yVm+BRGUpX97rMTGjzw==
@@ -198,3 +198,5 b' e386b5f4f8360dbb43a576dd9b1368e386fefa5b'
198 198 e91930d712e8507d1bc1b2dffd96c83edc4cbed3 5.1
199 199 a4e32fd539ab41489a51b2aa88bda9a73b839562 5.1.1
200 200 181e52f2b62f4768aa0d988936c929dc7c4a41a0 5.1.2
201 59338f9561099de77c684c00f76507f11e46ebe8 5.2rc0
202 ca3dca416f8d5863ca6f5a4a6a6bb835dcd5feeb 5.2
@@ -183,16 +183,15 b' packaging_targets := \\'
183 183 centos5 \
184 184 centos6 \
185 185 centos7 \
186 centos8 \
186 187 deb \
187 188 docker-centos5 \
188 189 docker-centos6 \
189 190 docker-centos7 \
191 docker-centos8 \
190 192 docker-debian-jessie \
191 193 docker-debian-stretch \
192 docker-fedora20 \
193 docker-fedora21 \
194 docker-fedora28 \
195 docker-fedora29 \
194 docker-fedora \
196 195 docker-ubuntu-trusty \
197 196 docker-ubuntu-trusty-ppa \
198 197 docker-ubuntu-xenial \
@@ -201,10 +200,7 b' packaging_targets := \\'
201 200 docker-ubuntu-artful-ppa \
202 201 docker-ubuntu-bionic \
203 202 docker-ubuntu-bionic-ppa \
204 fedora20 \
205 fedora21 \
206 fedora28 \
207 fedora29 \
203 fedora \
208 204 linux-wheels \
209 205 linux-wheels-x86_64 \
210 206 linux-wheels-i686 \
@@ -9,9 +9,7 b' build/'
9 9 | \.mypy_cache/
10 10 | \.venv/
11 11 | mercurial/thirdparty/
12 | hgext/fsmonitor/pywatchman/
13 12 | contrib/python-zstandard/
14 | contrib/grey.py
15 13 '''
16 14 skip-string-normalization = true
17 15 quiet = true
@@ -191,6 +191,10 b" winrm set winrm/config/client/auth '@{Ba"
191 191 $Setting = 'LocalAccountTokenFilterPolicy'
192 192 Set-ItemProperty -Path $Key -Name $Setting -Value 1 -Force
193 193
194 # Avoid long usernames in the temp directory path because the '~' causes extra quoting in ssh output
195 [System.Environment]::SetEnvironmentVariable('TMP', 'C:\Temp', [System.EnvironmentVariableTarget]::User)
196 [System.Environment]::SetEnvironmentVariable('TEMP', 'C:\Temp', [System.EnvironmentVariableTarget]::User)
197
194 198 # Configure and restart the WinRM Service; Enable the required firewall exception
195 199 Stop-Service -Name WinRM
196 200 Set-Service -Name WinRM -StartupType Automatic
@@ -25,12 +25,12 b' DISTROS = {'
25 25 }
26 26
27 27 INSTALL_PYTHONS = r'''
28 PYENV2_VERSIONS="2.7.16 pypy2.7-7.1.1"
29 PYENV3_VERSIONS="3.5.7 3.6.9 3.7.4 3.8.0 pypy3.5-7.0.0 pypy3.6-7.1.1"
28 PYENV2_VERSIONS="2.7.17 pypy2.7-7.2.0"
29 PYENV3_VERSIONS="3.5.7 3.6.9 3.7.5 3.8.0 pypy3.5-7.0.0 pypy3.6-7.2.0"
30 30
31 31 git clone https://github.com/pyenv/pyenv.git /hgdev/pyenv
32 32 pushd /hgdev/pyenv
33 git checkout d6d6bc8bb08bcdcbf4eb79509aa7061011ade1c4
33 git checkout 0e7cfc3b3d4eca46ad83d632e1505f5932cd179b
34 34 popd
35 35
36 36 export PYENV_ROOT="/hgdev/pyenv"
@@ -266,6 +266,7 b' PACKAGES="\\'
266 266 python3-fuzzywuzzy \
267 267 python3-pygments \
268 268 python3-vcr \
269 python3-venv \
269 270 rsync \
270 271 sqlite3 \
271 272 subversion \
@@ -4,10 +4,25 b''
4 4 #
5 5 # pip-compile --generate-hashes --output-file=contrib/automation/linux-requirements-py3.txt contrib/automation/linux-requirements.txt.in
6 6 #
7 appdirs==1.4.3 \
8 --hash=sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92 \
9 --hash=sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e \
10 # via black
7 11 astroid==2.2.5 \
8 12 --hash=sha256:6560e1e1749f68c64a4b5dee4e091fce798d2f0d84ebe638cf0e0585a343acf4 \
9 13 --hash=sha256:b65db1bbaac9f9f4d190199bb8680af6f6f84fd3769a5ea883df8a91fe68b4c4 \
10 14 # via pylint
15 attrs==19.3.0 \
16 --hash=sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c \
17 --hash=sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72 \
18 # via black
19 black==19.10b0 ; python_version >= "3.6" and platform_python_implementation != "PyPy" \
20 --hash=sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b \
21 --hash=sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539
22 click==7.0 \
23 --hash=sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13 \
24 --hash=sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7 \
25 # via black
11 26 docutils==0.15.2 \
12 27 --hash=sha256:6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0 \
13 28 --hash=sha256:9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827 \
@@ -78,6 +93,9 b' multidict==4.5.2 \\'
78 93 --hash=sha256:d3be11ac43ab1a3e979dac80843b42226d5d3cccd3986f2e03152720a4297cd7 \
79 94 --hash=sha256:db603a1c235d110c860d5f39988ebc8218ee028f07a7cbc056ba6424372ca31b \
80 95 # via yarl
96 pathspec==0.6.0 \
97 --hash=sha256:e285ccc8b0785beadd4c18e5708b12bb8fcf529a1e61215b3feff1d1e559ea5c \
98 # via black
81 99 pyflakes==2.1.1 \
82 100 --hash=sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0 \
83 101 --hash=sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2
@@ -104,10 +122,27 b' pyyaml==5.1.2 \\'
104 122 --hash=sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41 \
105 123 --hash=sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8 \
106 124 # via vcrpy
125 regex==2019.11.1 \
126 --hash=sha256:15454b37c5a278f46f7aa2d9339bda450c300617ca2fca6558d05d870245edc7 \
127 --hash=sha256:1ad40708c255943a227e778b022c6497c129ad614bb7a2a2f916e12e8a359ee7 \
128 --hash=sha256:5e00f65cc507d13ab4dfa92c1232d004fa202c1d43a32a13940ab8a5afe2fb96 \
129 --hash=sha256:604dc563a02a74d70ae1f55208ddc9bfb6d9f470f6d1a5054c4bd5ae58744ab1 \
130 --hash=sha256:720e34a539a76a1fedcebe4397290604cc2bdf6f81eca44adb9fb2ea071c0c69 \
131 --hash=sha256:7caf47e4a9ac6ef08cabd3442cc4ca3386db141fb3c8b2a7e202d0470028e910 \
132 --hash=sha256:c31eaf28c6fe75ea329add0022efeed249e37861c19681960f99bbc7db981fb2 \
133 --hash=sha256:c7393597191fc2043c744db021643549061e12abe0b3ff5c429d806de7b93b66 \
134 --hash=sha256:d2b302f8cdd82c8f48e9de749d1d17f85ce9a0f082880b9a4859f66b07037dc6 \
135 --hash=sha256:e3d8dd0ec0ea280cf89026b0898971f5750a7bd92cb62c51af5a52abd020054a \
136 --hash=sha256:ec032cbfed59bd5a4b8eab943c310acfaaa81394e14f44454ad5c9eba4f24a74 \
137 # via black
107 138 six==1.12.0 \
108 139 --hash=sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c \
109 140 --hash=sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73 \
110 141 # via astroid, vcrpy
142 toml==0.10.0 \
143 --hash=sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c \
144 --hash=sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e \
145 # via black
111 146 typed-ast==1.4.0 ; python_version >= "3.0" and platform_python_implementation != "PyPy" \
112 147 --hash=sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e \
113 148 --hash=sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e \
@@ -146,4 +181,4 b' yarl==1.3.0 \\'
146 181
147 182 # WARNING: The following packages were not pinned, but pip requires them to be
148 183 # pinned when the requirements file includes hashes. Consider using the --allow-unsafe flag.
149 # setuptools==41.0.1 # via python-levenshtein
184 # setuptools==41.6.0 # via python-levenshtein
@@ -1,3 +1,5 b''
1 # black pulls in typed-ast, which doesn't install on PyPy.
2 black ; python_version >= '3.6' and platform_python_implementation != 'PyPy'
1 3 # Bazaar doesn't work with Python 3 nor PyPy.
2 4 bzr ; python_version <= '2.7' and platform_python_implementation == 'CPython'
3 5 docutils
@@ -339,7 +339,7 b' def main():'
339 339
340 340
341 341 if __name__ == '__main__':
342 if sys.version_info.major < 3:
343 print('This script must be run under Python 3.')
342 if sys.version_info[0:2] < (3, 7):
343 print('This script must be run under Python 3.7+')
344 344 sys.exit(3)
345 345 main()
@@ -5,11 +5,5 b' clang-format:pattern = (**.c or **.cc or'
5 5 rustfmt:command = rustfmt {rootpath}
6 6 rustfmt:pattern = set:**.rs
7 7
8 # We use black, but currently with
9 # https://github.com/psf/black/pull/826 applied. For now
10 # contrib/grey.py is our fork of black. You need to pip install
11 # git+https://github.com/python/black/@d9e71a75ccfefa3d9156a64c03313a0d4ad981e5
12 # to have the dependencies for grey.
13 #
14 # black:command = python3.7 contrib/grey.py --config=black.toml -
15 # black:pattern = set:**.py - hgext/fsmonitor/pywatchman/** - mercurial/thirdparty/** - "contrib/python-zstandard/** - contrib/grey.py"
8 black:command = black --config=black.toml -
9 black:pattern = set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"
@@ -4,6 +4,7 b' from __future__ import absolute_import, '
4 4
5 5 import ast
6 6 import collections
7 import io
7 8 import os
8 9 import sys
9 10
@@ -754,7 +755,11 b' def sources(f, modname):'
754 755 yield src.read(), modname, f, 0
755 756 py = True
756 757 if py or f.endswith('.t'):
757 with open(f, 'r') as src:
758 # Strictly speaking we should sniff for the magic header that denotes
759 # Python source file encoding. But in reality we don't use anything
760 # other than ASCII (mainly) and UTF-8 (in a few exceptions), so
761 # simplicity is fine.
762 with io.open(f, 'r', encoding='utf-8') as src:
758 763 for script, modname, t, line in embedded(f, modname, src):
759 764 yield script, modname.encode('utf8'), t, line
760 765
@@ -22,10 +22,10 b''
22 22 $VC9_PYTHON_URL = "https://download.microsoft.com/download/7/9/6/796EF2E4-801B-4FC4-AB28-B59FBF6D907B/VCForPython27.msi"
23 23 $VC9_PYTHON_SHA256 = "070474db76a2e625513a5835df4595df9324d820f9cc97eab2a596dcbc2f5cbf"
24 24
25 $PYTHON27_x64_URL = "https://www.python.org/ftp/python/2.7.16/python-2.7.16.amd64.msi"
26 $PYTHON27_x64_SHA256 = "7c0f45993019152d46041a7db4b947b919558fdb7a8f67bcd0535bc98d42b603"
27 $PYTHON27_X86_URL = "https://www.python.org/ftp/python/2.7.16/python-2.7.16.msi"
28 $PYTHON27_X86_SHA256 = "d57dc3e1ba490aee856c28b4915d09e3f49442461e46e481bc6b2d18207831d7"
25 $PYTHON27_x64_URL = "https://www.python.org/ftp/python/2.7.17/python-2.7.17.amd64.msi"
26 $PYTHON27_x64_SHA256 = "3b934447e3620e51d2daf5b2f258c9b617bcc686ca2f777a49aa3b47893abf1b"
27 $PYTHON27_X86_URL = "https://www.python.org/ftp/python/2.7.17/python-2.7.17.msi"
28 $PYTHON27_X86_SHA256 = "a4e3a321517c6b0c2693d6f712a0d18c82600b3d0c759c299b3d14384a17f863"
29 29
30 30 $PYTHON35_x86_URL = "https://www.python.org/ftp/python/3.5.4/python-3.5.4.exe"
31 31 $PYTHON35_x86_SHA256 = "F27C2D67FD9688E4970F3BFF799BB9D722A0D6C2C13B04848E1F7D620B524B0E"
@@ -37,10 +37,10 b''
37 37 $PYTHON36_x64_URL = "https://www.python.org/ftp/python/3.6.8/python-3.6.8-amd64.exe"
38 38 $PYTHON36_x64_SHA256 = "96088A58B7C43BC83B84E6B67F15E8706C614023DD64F9A5A14E81FF824ADADC"
39 39
40 $PYTHON37_x86_URL = "https://www.python.org/ftp/python/3.7.4/python-3.7.4.exe"
41 $PYTHON37_x86_SHA256 = "9a30ab5568ba37bfbcae5cdee19e9dc30765c42cf066f605221563ff8b20ee34"
42 $PYTHON37_X64_URL = "https://www.python.org/ftp/python/3.7.4/python-3.7.4-amd64.exe"
43 $PYTHON37_x64_SHA256 = "bab92f987320975c7826171a072bfd64f8f0941aaf2cdeba6924b7025c9968a3"
40 $PYTHON37_x86_URL = "https://www.python.org/ftp/python/3.7.5/python-3.7.5.exe"
41 $PYTHON37_x86_SHA256 = "3c2ae8f72b48e6e0c2b482206e322bf5d0344ff91abc3b3c200cec9e275c7168"
42 $PYTHON37_X64_URL = "https://www.python.org/ftp/python/3.7.5/python-3.7.5-amd64.exe"
43 $PYTHON37_x64_SHA256 = "f3d60c127e7a92ed547efa3321bf70cd96b75c53bf4b903147015257c1314981"
44 44
45 45 $PYTHON38_x86_URL = "https://www.python.org/ftp/python/3.8.0/python-3.8.0.exe"
46 46 $PYTHON38_x86_SHA256 = "b471908de5e10d8fb5c3351a5affb1172da7790c533e0c9ffbaeec9c11611b15"
@@ -11,19 +11,17 b' UBUNTU_CODENAMES := \\'
11 11 cosmic \
12 12 disco
13 13
14 FEDORA_RELEASES := \
15 20 \
16 21 \
17 28 \
18 29
14 FEDORA_RELEASE := 31
19 15
20 16 CENTOS_RELEASES := \
21 17 5 \
22 18 6 \
23 7
19 7 \
20 8
24 21
25 22 # Build a Python for these CentOS releases.
26 23 CENTOS_WITH_PYTHON_RELEASES := 5 6
24 CENTOS_WITH_NONVERSIONED_PYTHON := 5 6 7
27 25
28 26 help:
29 27 @echo 'Packaging Make Targets'
@@ -34,8 +32,8 b' help:'
34 32 @echo 'docker-debian-{$(strip $(DEBIAN_CODENAMES))}'
35 33 @echo ' Build Debian packages specific to a Debian distro using Docker.'
36 34 @echo ''
37 @echo 'docker-fedora{$(strip $(FEDORA_RELEASES))}'
38 @echo ' Build an RPM for a specific Fedora version using Docker.'
35 @echo 'docker-fedora'
36 @echo ' Build an RPM for a Fedora $(FEDORA_RELEASE) using Docker.'
39 37 @echo ''
40 38 @echo 'docker-ubuntu-{$(strip $(UBUNTU_CODENAMES))}'
41 39 @echo ' Build Debian package specific to an Ubuntu distro using Docker.'
@@ -59,8 +57,8 b' help:'
59 57 @echo 'centos{$(strip $(CENTOS_RELEASES))}'
60 58 @echo ' Build an RPM for a specific CentOS version locally'
61 59 @echo ''
62 @echo 'fedora{$(strip $(FEDORA_RELEASES))}'
63 @echo ' Build an RPM for a specific Fedora version locally'
60 @echo 'fedora'
61 @echo ' Build an RPM for Fedora $(FEDORA_RELEASE) locally'
64 62
65 63 .PHONY: help
66 64
@@ -97,37 +95,30 b' endef'
97 95 $(foreach codename,$(UBUNTU_CODENAMES),$(eval $(call ubuntu_targets,$(codename))))
98 96
99 97 # Fedora targets.
100 define fedora_targets
101 .PHONY: fedora$(1)
102 fedora$(1):
103 mkdir -p $$(HGROOT)/packages/fedora$(1)
98 .PHONY: fedora
99 fedora:
100 mkdir -p $(HGROOT)/packages/fedora$(FEDORA_RELEASE)
104 101 ./buildrpm
105 cp $$(HGROOT)/contrib/packaging/rpmbuild/RPMS/*/* $$(HGROOT)/packages/fedora$(1)
106 cp $$(HGROOT)/contrib/packaging/rpmbuild/SRPMS/* $$(HGROOT)/packages/fedora$(1)
102 cp $(HGROOT)/contrib/packaging/rpmbuild/RPMS/*/* $(HGROOT)/packages/fedora$(FEDORA_RELEASE)
103 cp $(HGROOT)/contrib/packaging/rpmbuild/SRPMS/* $(HGROOT)/packages/fedora$(FEDORA_RELEASE)
107 104 rm -rf $(HGROOT)/rpmbuild
108 105
109 .PHONY: docker-fedora$(1)
110 docker-fedora$(1):
111 mkdir -p $$(HGROOT)/packages/fedora$(1)
112 ./dockerrpm fedora$(1)
113
114 endef
115
116 $(foreach release,$(FEDORA_RELEASES),$(eval $(call fedora_targets,$(release))))
106 .PHONY: docker-fedora
107 docker-fedora:
108 ./dockerrpm fedora$(FEDORA_RELEASE)
117 109
118 110 # CentOS targets.
119 111 define centos_targets
120 112 .PHONY: centos$(1)
121 113 centos$(1):
122 114 mkdir -p $$(HGROOT)/packages/centos$(1)
123 ./buildrpm $$(if $$(filter $(1),$$(CENTOS_WITH_PYTHON_RELEASES)),--withpython)
115 ./buildrpm $$(if $$(filter $(1),$$(CENTOS_WITH_PYTHON_RELEASES)),--withpython,$$(if $$(filter $(1),$$(CENTOS_WITH_NONVERSIONED_PYTHON)),--python python,))
124 116 cp $$(HGROOT)/contrib/packaging/rpmbuild/RPMS/*/* $$(HGROOT)/packages/centos$(1)
125 117 cp $$(HGROOT)/contrib/packaging/rpmbuild/SRPMS/* $$(HGROOT)/packages/centos$(1)
126 118
127 119 .PHONY: docker-centos$(1)
128 120 docker-centos$(1):
129 mkdir -p $$(HGROOT)/packages/centos$(1)
130 ./dockerrpm centos$(1) $$(if $$(filter $(1),$$(CENTOS_WITH_PYTHON_RELEASES)),--withpython)
121 ./dockerrpm centos$(1) $$(if $$(filter $(1),$$(CENTOS_WITH_PYTHON_RELEASES)),--withpython,$$(if $$(filter $(1),$$(CENTOS_WITH_NONVERSIONED_PYTHON)),--python python,))
131 122
132 123 endef
133 124
@@ -106,10 +106,10 b' if [ "$CLEANUP" ] ; then'
106 106 echo
107 107 OUTPUTDIR=${OUTPUTDIR:=packages/$DISTID-$CODENAME}
108 108 mkdir -p "$OUTPUTDIR"
109 find ../mercurial*.deb ../mercurial_*.build ../mercurial_*.changes \
109 find ../mercurial*.deb ../mercurial_*.build* ../mercurial_*.changes \
110 110 ../mercurial*.dsc ../mercurial*.gz \
111 111 -type f -newer $control -print0 2>/dev/null | \
112 112 xargs -Inarf -0 mv narf "$OUTPUTDIR"
113 113 echo "Built packages for $debver:"
114 find "$PWD"/"$OUTPUTDIR" -type f -newer $control -name '*.deb'
114 find "$OUTPUTDIR" -type f -newer $control -name '*.deb'
115 115 fi
@@ -1,16 +1,12 b''
1 1 #!/bin/bash -e
2 2 #
3 # Build a Mercurial RPM from the current repo
4 #
5 # Tested on
6 # - Fedora 20
7 # - CentOS 5
8 # - centOS 6
3 # Build a Mercurial RPM from the current repo, mainly for Fedora/CentOS/RHEL
9 4
10 5 . $(dirname $0)/packagelib.sh
11 6
12 7 BUILD=1
13 8 RPMBUILDDIR="$PWD/rpmbuild"
9 PYTHONEXE=python3
14 10
15 11 while [ "$1" ]; do
16 12 case "$1" in
@@ -18,10 +14,16 b' while [ "$1" ]; do'
18 14 shift
19 15 BUILD=
20 16 ;;
17 --python)
18 shift
19 PYTHONEXE=$1
20 shift
21 ;;
21 22 --withpython | --with-python)
22 23 shift
23 24 PYTHONVER=2.7.16
24 25 PYTHONMD5=f1a2ace631068444831d01485466ece0
26 PYTHONEXE=python
25 27 ;;
26 28 --rpmbuilddir )
27 29 shift
@@ -51,7 +53,7 b' fi'
51 53 gethgversion
52 54
53 55 if [ -z "$type" ] ; then
54 release=1
56 release=1
55 57 else
56 58 release=0.9_$type
57 59 fi
@@ -96,6 +98,7 b' rpmspec=$RPMBUILDDIR/SPECS/mercurial.spe'
96 98
97 99 sed -e "s,^Version:.*,Version: $version," \
98 100 -e "s,^Release:.*,Release: $release," \
101 -e "s/^%global pythonexe .*/%global pythonexe $PYTHONEXE/" \
99 102 $specfile > $rpmspec
100 103
101 104 echo >> $rpmspec
@@ -121,8 +124,8 b' for l in sorted(changelog, reverse=True)'
121 124 if prevtitle != title:
122 125 prevtitle = title
123 126 print
124 print title
125 print "- %s" % l[3].strip()
127 print(title)
128 print("- %s" % l[3].strip())
126 129 ' >> $rpmspec
127 130
128 131 else
@@ -138,7 +141,7 b' def datestr(date, format):'
138 141 for l in sys.stdin.readlines():
139 142 tok = l.split("\t")
140 143 hgdate = tuple(int(v) for v in tok[0].split())
141 print "* %s %s\n- %s" % (datestr(hgdate, "%a %b %d %Y"), tok[1], tok[2])
144 print("* %s %s\n- %s" % (datestr(hgdate, "%a %b %d %Y"), tok[1], tok[2]))
142 145 ' >> $rpmspec
143 146
144 147 fi
@@ -7,21 +7,24 b' Build-Depends:'
7 7 dh-python,
8 8 less,
9 9 netbase,
10 python-all,
11 python-all-dev,
12 python-docutils,
10 python3-all,
11 python3-all-dev,
12 python3-docutils,
13 13 unzip,
14 14 zip
15 15 Standards-Version: 3.9.4
16 X-Python-Version: >= 2.7
16 X-Python3-Version: >= 3.5
17 17
18 18 Package: mercurial
19 19 Depends:
20 python,
20 sensible-utils,
21 21 ${shlibs:Depends},
22 22 ${misc:Depends},
23 ${python:Depends},
24 mercurial-common (= ${source:Version})
23 ${python3:Depends},
24 Recommends: ca-certificates
25 Suggests: wish
26 Replaces: mercurial-common
27 Breaks: mercurial-common
25 28 Architecture: any
26 29 Description: fast, easy to use, distributed revision control tool.
27 30 Mercurial is a fast, lightweight Source Control Management system designed
@@ -36,19 +39,3 b' Description: fast, easy to use, distribu'
36 39 * Easy-to-use command-line interface
37 40 * Integrated stand-alone web interface
38 41 * Small Python codebase
39
40 Package: mercurial-common
41 Architecture: all
42 Depends:
43 ${misc:Depends},
44 ${python:Depends},
45 Recommends: mercurial (= ${source:Version}), ca-certificates
46 Suggests: wish
47 Breaks: mercurial (<< ${source:Version})
48 Replaces: mercurial (<< 2.6.3)
49 Description: easy-to-use, scalable distributed version control system (common files)
50 Mercurial is a fast, lightweight Source Control Management system designed
51 for efficient handling of very large distributed projects.
52 .
53 This package contains the architecture independent components of Mercurial,
54 and is generally useless without the mercurial package.
@@ -4,41 +4,41 b''
4 4
5 5 CPUS=$(shell cat /proc/cpuinfo | grep -E ^processor | wc -l)
6 6
7 export HGPYTHON3=1
8 export PYTHON=python3
9
7 10 %:
8 dh $@ --with python2
11 dh $@ --with python3
9 12
10 13 override_dh_auto_test:
11 14 http_proxy='' dh_auto_test -- TESTFLAGS="-j$(CPUS)"
12 15
13 override_dh_python2:
14 dh_python2
15 find debian/mercurial/usr/share -type d -empty -delete
16 override_dh_python3:
17 dh_python3 --shebang=/usr/bin/python3
18
19 override_dh_auto_clean:
20 $(MAKE) cleanbutpackages
21 $(MAKE) -C contrib/chg clean
16 22
17 override_dh_install:
18 python$(PYVERS) setup.py install --root "$(CURDIR)"/debian/mercurial --install-layout=deb
23 override_dh_auto_build:
24 $(MAKE) all
25 $(MAKE) -C contrib/chg all
26
27 override_dh_auto_install:
28 python3 setup.py install --root "$(CURDIR)"/debian/mercurial --install-layout=deb
19 29 # chg
20 30 make -C contrib/chg \
21 31 DESTDIR="$(CURDIR)"/debian/mercurial \
22 32 PREFIX=/usr \
23 clean install
24 # remove arch-independent python stuff
25 find "$(CURDIR)"/debian/mercurial/usr/lib \
26 ! -name '*.so' ! -type d -delete , \
27 -type d -empty -delete
28 python$(PYVERS) setup.py install --root "$(CURDIR)/debian/mercurial-common" --install-layout=deb
29 make install-doc PREFIX="$(CURDIR)"/debian/mercurial-common/usr
30 # remove arch-dependent python stuff
31 find "$(CURDIR)"/debian/mercurial-common/usr/lib \
32 -name '*.so' ! -type d -delete , \
33 -type d -empty -delete
34 cp contrib/hg-ssh "$(CURDIR)"/debian/mercurial-common/usr/bin
35 mkdir -p "$(CURDIR)"/debian/mercurial-common/usr/share/mercurial
36 cp contrib/hgk "$(CURDIR)"/debian/mercurial-common/usr/share/mercurial
37 mkdir -p "$(CURDIR)"/debian/mercurial-common/etc/mercurial/hgrc.d/
38 cp contrib/packaging/debian/*.rc "$(CURDIR)"/debian/mercurial-common/etc/mercurial/hgrc.d/
33 install
34 make install-doc PREFIX="$(CURDIR)"/debian/mercurial/usr
35 cp contrib/hg-ssh "$(CURDIR)"/debian/mercurial/usr/bin
36 mkdir -p "$(CURDIR)"/debian/mercurial/usr/share/mercurial
37 cp contrib/hgk "$(CURDIR)"/debian/mercurial/usr/share/mercurial
38 mkdir -p "$(CURDIR)"/debian/mercurial/etc/mercurial/hgrc.d/
39 cp contrib/packaging/debian/*.rc "$(CURDIR)"/debian/mercurial/etc/mercurial/hgrc.d/
39 40 # completions
40 mkdir -p "$(CURDIR)"/debian/mercurial-common/usr/share/bash-completion/completions
41 cp contrib/bash_completion "$(CURDIR)"/debian/mercurial-common/usr/share/bash-completion/completions/hg
42 mkdir -p "$(CURDIR)"/debian/mercurial-common/usr/share/zsh/vendor-completions
43 cp contrib/zsh_completion "$(CURDIR)"/debian/mercurial-common/usr/share/zsh/vendor-completions/_hg
44 rm "$(CURDIR)"/debian/mercurial-common/usr/bin/hg
41 mkdir -p "$(CURDIR)"/debian/mercurial/usr/share/bash-completion/completions
42 cp contrib/bash_completion "$(CURDIR)"/debian/mercurial/usr/share/bash-completion/completions/hg
43 mkdir -p "$(CURDIR)"/debian/mercurial/usr/share/zsh/vendor-completions
44 cp contrib/zsh_completion "$(CURDIR)"/debian/mercurial/usr/share/zsh/vendor-completions/_hg
@@ -1,4 +1,4 b''
1 FROM fedora:29
1 FROM fedora:%OS_RELEASE%
2 2
3 3 RUN groupadd -g 1000 build && \
4 4 useradd -u 1000 -g 1000 -s /bin/bash -d /build -m build
@@ -7,8 +7,8 b' RUN dnf install -y \\'
7 7 gcc \
8 8 gettext \
9 9 make \
10 python-devel \
11 python-docutils \
10 python3-devel \
11 python3-docutils \
12 12 rpm-build
13 13
14 14 # For creating repo meta data
@@ -6,6 +6,14 b' export ROOTDIR=$(cd $BUILDDIR/../..; pwd'
6 6 PLATFORM="$1"
7 7 shift # extra params are passed to buildrpm
8 8
9 DOCKERFILE="$PLATFORM"
10 OS_RELEASE="${PLATFORM//[a-z]/}"
11 case "$PLATFORM" in
12 fedora*)
13 DOCKERFILE="${PLATFORM//[0-9]/}.template"
14 ;;
15 esac
16
9 17 DOCKER=$($BUILDDIR/hg-docker docker-path)
10 18
11 19 CONTAINER=hg-docker-$PLATFORM
@@ -18,9 +26,14 b' else'
18 26 DOCKERGID=$(id -g)
19 27 fi
20 28
21 $BUILDDIR/hg-docker build --build-arg UID=$DOCKERUID --build-arg GID=$DOCKERGID $BUILDDIR/docker/$PLATFORM $CONTAINER
29 $BUILDDIR/hg-docker build \
30 --build-arg UID=$DOCKERUID \
31 --build-arg GID=$DOCKERGID \
32 --build-arg OS_RELEASE=${OS_RELEASE:-latest} \
33 $BUILDDIR/docker/$DOCKERFILE $CONTAINER
22 34
23 35 RPMBUILDDIR=$ROOTDIR/packages/$PLATFORM
36 mkdir -p $RPMBUILDDIR
24 37 $ROOTDIR/contrib/packaging/buildrpm --rpmbuilddir $RPMBUILDDIR --prepare $*
25 38
26 39 DSHARED=/mnt/shared
@@ -2,37 +2,119 b''
2 2 # This file is autogenerated by pip-compile
3 3 # To update, run:
4 4 #
5 # pip-compile --generate-hashes contrib/packaging/inno/requirements.txt.in -o contrib/packaging/inno/requirements.txt
5 # pip-compile --generate-hashes --output-file=contrib/packaging/inno/requirements.txt contrib/packaging/inno/requirements.txt.in
6 6 #
7 certifi==2018.11.29 \
8 --hash=sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7 \
9 --hash=sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033 \
7 certifi==2019.9.11 \
8 --hash=sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50 \
9 --hash=sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef \
10 10 # via dulwich
11 configparser==3.7.3 \
12 --hash=sha256:27594cf4fc279f321974061ac69164aaebd2749af962ac8686b20503ac0bcf2d \
13 --hash=sha256:9d51fe0a382f05b6b117c5e601fc219fede4a8c71703324af3f7d883aef476a3 \
11 cffi==1.13.1 \
12 --hash=sha256:00d890313797d9fe4420506613384b43099ad7d2b905c0752dbcc3a6f14d80fa \
13 --hash=sha256:0cf9e550ac6c5e57b713437e2f4ac2d7fd0cd10336525a27224f5fc1ec2ee59a \
14 --hash=sha256:0ea23c9c0cdd6778146a50d867d6405693ac3b80a68829966c98dd5e1bbae400 \
15 --hash=sha256:193697c2918ecdb3865acf6557cddf5076bb39f1f654975e087b67efdff83365 \
16 --hash=sha256:1ae14b542bf3b35e5229439c35653d2ef7d8316c1fffb980f9b7647e544baa98 \
17 --hash=sha256:1e389e069450609c6ffa37f21f40cce36f9be7643bbe5051ab1de99d5a779526 \
18 --hash=sha256:263242b6ace7f9cd4ea401428d2d45066b49a700852334fd55311bde36dcda14 \
19 --hash=sha256:33142ae9807665fa6511cfa9857132b2c3ee6ddffb012b3f0933fc11e1e830d5 \
20 --hash=sha256:364f8404034ae1b232335d8c7f7b57deac566f148f7222cef78cf8ae28ef764e \
21 --hash=sha256:47368f69fe6529f8f49a5d146ddee713fc9057e31d61e8b6dc86a6a5e38cecc1 \
22 --hash=sha256:4895640844f17bec32943995dc8c96989226974dfeb9dd121cc45d36e0d0c434 \
23 --hash=sha256:558b3afef987cf4b17abd849e7bedf64ee12b28175d564d05b628a0f9355599b \
24 --hash=sha256:5ba86e1d80d458b338bda676fd9f9d68cb4e7a03819632969cf6d46b01a26730 \
25 --hash=sha256:63424daa6955e6b4c70dc2755897f5be1d719eabe71b2625948b222775ed5c43 \
26 --hash=sha256:6381a7d8b1ebd0bc27c3bc85bc1bfadbb6e6f756b4d4db0aa1425c3719ba26b4 \
27 --hash=sha256:6381ab708158c4e1639da1f2a7679a9bbe3e5a776fc6d1fd808076f0e3145331 \
28 --hash=sha256:6fd58366747debfa5e6163ada468a90788411f10c92597d3b0a912d07e580c36 \
29 --hash=sha256:728ec653964655d65408949b07f9b2219df78badd601d6c49e28d604efe40599 \
30 --hash=sha256:7cfcfda59ef1f95b9f729c56fe8a4041899f96b72685d36ef16a3440a0f85da8 \
31 --hash=sha256:819f8d5197c2684524637f940445c06e003c4a541f9983fd30d6deaa2a5487d8 \
32 --hash=sha256:825ecffd9574557590e3225560a8a9d751f6ffe4a49e3c40918c9969b93395fa \
33 --hash=sha256:9009e917d8f5ef780c2626e29b6bc126f4cb2a4d43ca67aa2b40f2a5d6385e78 \
34 --hash=sha256:9c77564a51d4d914ed5af096cd9843d90c45b784b511723bd46a8a9d09cf16fc \
35 --hash=sha256:a19089fa74ed19c4fe96502a291cfdb89223a9705b1d73b3005df4256976142e \
36 --hash=sha256:a40ed527bffa2b7ebe07acc5a3f782da072e262ca994b4f2085100b5a444bbb2 \
37 --hash=sha256:bb75ba21d5716abc41af16eac1145ab2e471deedde1f22c6f99bd9f995504df0 \
38 --hash=sha256:e22a00c0c81ffcecaf07c2bfb3672fa372c50e2bd1024ffee0da191c1b27fc71 \
39 --hash=sha256:e55b5a746fb77f10c83e8af081979351722f6ea48facea79d470b3731c7b2891 \
40 --hash=sha256:ec2fa3ee81707a5232bf2dfbd6623fdb278e070d596effc7e2d788f2ada71a05 \
41 --hash=sha256:fd82eb4694be712fcae03c717ca2e0fc720657ac226b80bbb597e971fc6928c2 \
42 # via cryptography
43 configparser==4.0.2 \
44 --hash=sha256:254c1d9c79f60c45dfde850850883d5aaa7f19a23f13561243a050d5a7c3fe4c \
45 --hash=sha256:c7d282687a5308319bf3d2e7706e575c635b0a470342641c93bea0ea3b5331df \
14 46 # via entrypoints
15 docutils==0.14 \
16 --hash=sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6 \
17 --hash=sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274 \
18 --hash=sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6
19 dulwich==0.19.11 \
20 --hash=sha256:afbe070f6899357e33f63f3f3696e601731fef66c64a489dea1bc9f539f4a725
47 cryptography==2.8 \
48 --hash=sha256:02079a6addc7b5140ba0825f542c0869ff4df9a69c360e339ecead5baefa843c \
49 --hash=sha256:1df22371fbf2004c6f64e927668734070a8953362cd8370ddd336774d6743595 \
50 --hash=sha256:369d2346db5934345787451504853ad9d342d7f721ae82d098083e1f49a582ad \
51 --hash=sha256:3cda1f0ed8747339bbdf71b9f38ca74c7b592f24f65cdb3ab3765e4b02871651 \
52 --hash=sha256:44ff04138935882fef7c686878e1c8fd80a723161ad6a98da31e14b7553170c2 \
53 --hash=sha256:4b1030728872c59687badcca1e225a9103440e467c17d6d1730ab3d2d64bfeff \
54 --hash=sha256:58363dbd966afb4f89b3b11dfb8ff200058fbc3b947507675c19ceb46104b48d \
55 --hash=sha256:6ec280fb24d27e3d97aa731e16207d58bd8ae94ef6eab97249a2afe4ba643d42 \
56 --hash=sha256:7270a6c29199adc1297776937a05b59720e8a782531f1f122f2eb8467f9aab4d \
57 --hash=sha256:73fd30c57fa2d0a1d7a49c561c40c2f79c7d6c374cc7750e9ac7c99176f6428e \
58 --hash=sha256:7f09806ed4fbea8f51585231ba742b58cbcfbfe823ea197d8c89a5e433c7e912 \
59 --hash=sha256:90df0cc93e1f8d2fba8365fb59a858f51a11a394d64dbf3ef844f783844cc793 \
60 --hash=sha256:971221ed40f058f5662a604bd1ae6e4521d84e6cad0b7b170564cc34169c8f13 \
61 --hash=sha256:a518c153a2b5ed6b8cc03f7ae79d5ffad7315ad4569b2d5333a13c38d64bd8d7 \
62 --hash=sha256:b0de590a8b0979649ebeef8bb9f54394d3a41f66c5584fff4220901739b6b2f0 \
63 --hash=sha256:b43f53f29816ba1db8525f006fa6f49292e9b029554b3eb56a189a70f2a40879 \
64 --hash=sha256:d31402aad60ed889c7e57934a03477b572a03af7794fa8fb1780f21ea8f6551f \
65 --hash=sha256:de96157ec73458a7f14e3d26f17f8128c959084931e8997b9e655a39c8fde9f9 \
66 --hash=sha256:df6b4dca2e11865e6cfbfb708e800efb18370f5a46fd601d3755bc7f85b3a8a2 \
67 --hash=sha256:ecadccc7ba52193963c0475ac9f6fa28ac01e01349a2ca48509667ef41ffd2cf \
68 --hash=sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8 \
69 # via secretstorage
70 docutils==0.15.2 \
71 --hash=sha256:6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0 \
72 --hash=sha256:9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827 \
73 --hash=sha256:a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99
74 dulwich==0.19.13 \
75 --hash=sha256:0e442f6f96e6d97270a7cca4e75306b6b0228627bdf57dde3759e0e345a6b523 \
76 --hash=sha256:667f49536ccba09d3b90bac80d44048e45566f84b98a5e139cc8c70757a6ae60 \
77 --hash=sha256:82792a9d49b112fa2151fa0fb29b01667855a843ff99325b1c1578a4aec11b57 \
78 --hash=sha256:aa628449c5f594a9a282f4d9e5993fef65481ef5e3b9b6c52ff31200f8f5dc95 \
79 --hash=sha256:ab4668bc4e1996d12eb1910e123a09edcff8e166e7ec46db5aafb5c7e250b99f \
80 --hash=sha256:c35ed2cd5b263ce0d67758ffba590c0466ff13b048457ff060b7d2e6cb55a40e \
81 --hash=sha256:c8b48079a14850cbeb788b38e1061ae6db75061431c1c0f91382460be4c84bbe \
82 --hash=sha256:dfcd9943c69f963dd61a027f480d16f548ea5905c2485be8f4b8f130df2c32de \
83 --hash=sha256:e3693c3238c1a5fc1e4427281c4455d78549f4797f2a7107a5f4443b21efafb4
21 84 entrypoints==0.3 \
22 85 --hash=sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19 \
23 86 --hash=sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451 \
24 87 # via keyring
88 enum34==1.1.6 \
89 --hash=sha256:2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850 \
90 --hash=sha256:644837f692e5f550741432dd3f223bbb9852018674981b1664e5dc339387588a \
91 --hash=sha256:6bd0f6ad48ec2aa117d3d141940d484deccda84d4fcd884f5c3d93c23ecd8c79 \
92 --hash=sha256:8ad8c4783bf61ded74527bffb48ed9b54166685e4230386a9ed9b1279e2df5b1 \
93 # via cryptography
94 ipaddress==1.0.23 \
95 --hash=sha256:6e0f4a39e66cb5bb9a137b00276a2eff74f93b71dcbdad6f10ff7df9d3557fcc \
96 --hash=sha256:b7f8e0369580bb4a24d5ba1d7cc29660a4a6987763faf1d8a8046830e020e7e2 \
97 # via cryptography
25 98 keyring==18.0.1 \
26 99 --hash=sha256:67d6cc0132bd77922725fae9f18366bb314fd8f95ff4d323a4df41890a96a838 \
27 100 --hash=sha256:7b29ebfcf8678c4da531b2478a912eea01e80007e5ddca9ee0c7038cb3489ec6
28 pygments==2.3.1 \
29 --hash=sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a \
30 --hash=sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d
101 pycparser==2.19 \
102 --hash=sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3 \
103 # via cffi
104 pygments==2.4.2 \
105 --hash=sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127 \
106 --hash=sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297
31 107 pywin32-ctypes==0.2.0 \
32 108 --hash=sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942 \
33 --hash=sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98 \
109 --hash=sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98
110 secretstorage==2.3.1 \
111 --hash=sha256:3af65c87765323e6f64c83575b05393f9e003431959c9395d1791d51497f29b6 \
34 112 # via keyring
35 urllib3==1.24.1 \
36 --hash=sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39 \
37 --hash=sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22 \
113 six==1.12.0 \
114 --hash=sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c \
115 --hash=sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73 \
116 # via cryptography
117 urllib3==1.25.6 \
118 --hash=sha256:3de946ffbed6e6746608990594d08faac602528ac7015ac28d33cee6a45b7398 \
119 --hash=sha256:9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86 \
38 120 # via dulwich
@@ -2,3 +2,6 b' docutils'
2 2 dulwich
3 3 keyring
4 4 pygments
5 # Need to list explicitly so dependency gets pulled in when
6 # not running on Windows.
7 pywin32-ctypes
@@ -2,6 +2,8 b''
2 2
3 3 %define withpython %{nil}
4 4
5 %global pythonexe python3
6
5 7 %if "%{?withpython}"
6 8
7 9 %global pythonver %{withpython}
@@ -15,7 +17,7 b''
15 17
16 18 %else
17 19
18 %global pythonver %(python -c 'import sys;print ".".join(map(str, sys.version_info[:2]))')
20 %global pythonver %(%{pythonexe} -c 'import sys;print(".".join(map(str, sys.version_info[:2])))')
19 21
20 22 %endif
21 23
@@ -37,8 +39,8 b' BuildRequires: make, gcc, gettext'
37 39 %if "%{?withpython}"
38 40 BuildRequires: readline-devel, openssl-devel, ncurses-devel, zlib-devel, bzip2-devel
39 41 %else
40 BuildRequires: python >= 2.7, python-devel, python-docutils >= 0.5
41 Requires: python >= 2.7
42 BuildRequires: %{pythonexe} >= %{pythonver}, %{pythonexe}-devel, %{pythonexe}-docutils >= 0.5
43 Requires: %{pythonexe} >= %{pythonver}
42 44 %endif
43 45 # The hgk extension uses the wish tcl interpreter, but we don't enforce it
44 46 #Requires: tk
@@ -52,13 +54,15 b' for efficient handling of very large dis'
52 54 %if "%{?withpython}"
53 55 %setup -q -n mercurial-%{version}-%{release} -a1 -a2
54 56 # despite the comments in cgi.py, we do this to prevent rpmdeps from picking /usr/local/bin/python up
55 sed -i '1c#! /usr/bin/env python' %{pythonname}/Lib/cgi.py
57 sed -i '1c#! /usr/bin/env %{pythonexe}' %{pythonname}/Lib/cgi.py
56 58 %else
57 59 %setup -q -n mercurial-%{version}-%{release}
58 60 %endif
59 61
60 62 %build
61 63
64 export HGPYTHON3=1
65
62 66 %if "%{?withpython}"
63 67
64 68 PYPATH=$PWD/%{pythonname}
@@ -82,12 +86,16 b' export PYTHONPATH=$PWD/%{docutilsname}'
82 86
83 87 %endif
84 88
85 make all
89 make all PYTHON=%{pythonexe}
86 90 make -C contrib/chg
87 91
92 sed -i -e '1s|#!/usr/bin/env python$|#!/usr/bin/env %{pythonexe}|' contrib/hg-ssh
93
88 94 %install
89 95 rm -rf $RPM_BUILD_ROOT
90 96
97 export HGPYTHON3=1
98
91 99 %if "%{?withpython}"
92 100
93 101 PYPATH=$PWD/%{pythonname}
@@ -101,14 +109,14 b' cd %{docutilsname}'
101 109 LD_LIBRARY_PATH=$PYPATH $PYPATH/python setup.py install --root="$RPM_BUILD_ROOT"
102 110 cd -
103 111
104 PATH=$PYPATH:$PATH LD_LIBRARY_PATH=$PYPATH make install DESTDIR=$RPM_BUILD_ROOT PREFIX=%{hgpyprefix} MANDIR=%{_mandir}
112 PATH=$PYPATH:$PATH LD_LIBRARY_PATH=$PYPATH make install PYTHON=%{pythonexe} DESTDIR=$RPM_BUILD_ROOT PREFIX=%{hgpyprefix} MANDIR=%{_mandir}
105 113 mkdir -p $RPM_BUILD_ROOT%{_bindir}
106 114 ( cd $RPM_BUILD_ROOT%{_bindir}/ && ln -s ../..%{hgpyprefix}/bin/hg . )
107 115 ( cd $RPM_BUILD_ROOT%{_bindir}/ && ln -s ../..%{hgpyprefix}/bin/python2.? %{pythonhg} )
108 116
109 117 %else
110 118
111 make install DESTDIR=$RPM_BUILD_ROOT PREFIX=%{_prefix} MANDIR=%{_mandir}
119 make install PYTHON=%{pythonexe} DESTDIR=$RPM_BUILD_ROOT PREFIX=%{_prefix} MANDIR=%{_mandir}
112 120
113 121 %endif
114 122
@@ -135,7 +143,7 b' rm -rf $RPM_BUILD_ROOT'
135 143
136 144 %files
137 145 %defattr(-,root,root,-)
138 %doc CONTRIBUTORS COPYING doc/README doc/hg*.txt doc/hg*.html *.cgi contrib/*.fcgi
146 %doc CONTRIBUTORS COPYING doc/README doc/hg*.txt doc/hg*.html *.cgi contrib/*.fcgi contrib/*.wsgi
139 147 %doc %attr(644,root,root) %{_mandir}/man?/hg*
140 148 %doc %attr(644,root,root) contrib/*.svg
141 149 %dir %{_datadir}/zsh/
@@ -2,12 +2,12 b''
2 2 # This file is autogenerated by pip-compile
3 3 # To update, run:
4 4 #
5 # pip-compile --generate-hashes contrib/packaging/wix/requirements.txt.in -o contrib/packaging/wix/requirements.txt
5 # pip-compile --generate-hashes --output-file=contrib/packaging/wix/requirements.txt contrib/packaging/wix/requirements.txt.in
6 6 #
7 docutils==0.14 \
8 --hash=sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6 \
9 --hash=sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274 \
10 --hash=sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6
11 pygments==2.3.1 \
12 --hash=sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a \
13 --hash=sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d
7 docutils==0.15.2 \
8 --hash=sha256:6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0 \
9 --hash=sha256:9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827 \
10 --hash=sha256:a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99
11 pygments==2.4.2 \
12 --hash=sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127 \
13 --hash=sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297
@@ -18,6 +18,7 b' from mercurial import ('
18 18 )
19 19 from mercurial.utils import (
20 20 procutil,
21 stringutil
21 22 )
22 23
23 24 options = [(b'L', b'label', [], _(b'labels to use on conflict markers')),
@@ -75,8 +76,7 b' try:'
75 76 context.arbitraryfilectx(other),
76 77 **pycompat.strkwargs(opts)))
77 78 except ParseError as e:
78 if pycompat.ispy3:
79 e = str(e).encode('utf8')
79 e = stringutil.forcebytestr(e)
80 80 pycompat.stdout.write(b"%s: %s\n" % (sys.argv[0].encode('utf8'), e))
81 81 showhelp()
82 82 sys.exit(1)
@@ -53,9 +53,13 b' class _lazyloaderex(importlib.util.LazyL'
53 53
54 54 # This is 3.6+ because with Python 3.5 it isn't possible to lazily load
55 55 # extensions. See the discussion in https://bugs.python.org/issue26186 for more.
56 _extensions_loader = _lazyloaderex.factory(
57 importlib.machinery.ExtensionFileLoader
58 )
56 if sys.version_info[0:2] >= (3, 6):
57 _extensions_loader = _lazyloaderex.factory(
58 importlib.machinery.ExtensionFileLoader
59 )
60 else:
61 _extensions_loader = importlib.machinery.ExtensionFileLoader
62
59 63 _bytecode_loader = _lazyloaderex.factory(
60 64 importlib.machinery.SourcelessFileLoader
61 65 )
@@ -955,7 +955,7 b' class bzrestapi(bzaccess):'
955 955 def _fetch(self, burl):
956 956 try:
957 957 resp = url.open(self.ui, burl)
958 return json.loads(resp.read())
958 return pycompat.json_loads(resp.read())
959 959 except util.urlerr.httperror as inst:
960 960 if inst.code == 401:
961 961 raise error.Abort(_(b'authorization failed'))
@@ -978,7 +978,7 b' class bzrestapi(bzaccess):'
978 978 req = request_type(burl, data, {b'Content-Type': b'application/json'})
979 979 try:
980 980 resp = url.opener(self.ui).open(req)
981 return json.loads(resp.read())
981 return pycompat.json_loads(resp.read())
982 982 except util.urlerr.httperror as inst:
983 983 if inst.code == 401:
984 984 raise error.Abort(_(b'authorization failed'))
@@ -7,7 +7,6 b''
7 7 # GNU General Public License version 2 or any later version.
8 8 from __future__ import absolute_import
9 9
10 import email.parser as emailparser
11 10 import os
12 11 import shutil
13 12 import stat
@@ -17,6 +16,7 b' from mercurial.i18n import _'
17 16 from mercurial import (
18 17 encoding,
19 18 error,
19 mail,
20 20 pycompat,
21 21 util,
22 22 )
@@ -69,7 +69,6 b' class gnuarch_source(common.converter_so'
69 69 self.changes = {}
70 70 self.parents = {}
71 71 self.tags = {}
72 self.catlogparser = emailparser.Parser()
73 72 self.encoding = encoding.encoding
74 73 self.archives = []
75 74
@@ -299,26 +298,29 b' class gnuarch_source(common.converter_so'
299 298
300 299 def _parsecatlog(self, data, rev):
301 300 try:
302 catlog = self.catlogparser.parsestr(data)
301 catlog = mail.parsebytes(data)
303 302
304 303 # Commit date
305 304 self.changes[rev].date = dateutil.datestr(
306 dateutil.strdate(catlog[b'Standard-date'], b'%Y-%m-%d %H:%M:%S')
305 dateutil.strdate(catlog[r'Standard-date'], b'%Y-%m-%d %H:%M:%S')
307 306 )
308 307
309 308 # Commit author
310 self.changes[rev].author = self.recode(catlog[b'Creator'])
309 self.changes[rev].author = self.recode(catlog[r'Creator'])
311 310
312 311 # Commit description
313 312 self.changes[rev].summary = b'\n\n'.join(
314 (catlog[b'Summary'], catlog.get_payload())
313 (
314 self.recode(catlog[r'Summary']),
315 self.recode(catlog.get_payload()),
316 )
315 317 )
316 318 self.changes[rev].summary = self.recode(self.changes[rev].summary)
317 319
318 320 # Commit revision origin when dealing with a branch or tag
319 if b'Continuation-of' in catlog:
321 if r'Continuation-of' in catlog:
320 322 self.changes[rev].continuationof = self.recode(
321 catlog[b'Continuation-of']
323 catlog[r'Continuation-of']
322 324 )
323 325 except Exception:
324 326 raise error.Abort(_(b'could not parse cat-log of %s') % rev)
@@ -126,7 +126,6 b' from __future__ import absolute_import'
126 126
127 127 import collections
128 128 import itertools
129 import json
130 129 import os
131 130 import re
132 131 import subprocess
@@ -642,7 +641,7 b' def fixfile(ui, repo, opts, fixers, fixc'
642 641 if fixer.shouldoutputmetadata():
643 642 try:
644 643 metadatajson, newerdata = stdout.split(b'\0', 1)
645 metadata[fixername] = json.loads(metadatajson)
644 metadata[fixername] = pycompat.json_loads(metadatajson)
646 645 except ValueError:
647 646 ui.warn(
648 647 _(b'ignored invalid output from fixer tool: %s\n')
@@ -132,6 +132,7 b' from mercurial import ('
132 132 util,
133 133 )
134 134 from mercurial import match as matchmod
135 from mercurial.utils import stringutil
135 136
136 137 from . import (
137 138 pywatchman,
@@ -189,10 +190,10 b' def debuginstall(ui, fm):'
189 190 fm.write(
190 191 b"fsmonitor-watchman-version",
191 192 _(b" watchman binary version %s\n"),
192 v[b"version"],
193 pycompat.bytestr(v["version"]),
193 194 )
194 195 except watchmanclient.Unavailable as e:
195 err = str(e)
196 err = stringutil.forcebytestr(e)
196 197 fm.condwrite(
197 198 err,
198 199 b"fsmonitor-watchman-error",
@@ -207,15 +208,23 b' def _handleunavailable(ui, state, ex):'
207 208 if isinstance(ex, watchmanclient.Unavailable):
208 209 # experimental config: fsmonitor.verbose
209 210 if ex.warn and ui.configbool(b'fsmonitor', b'verbose'):
210 if b'illegal_fstypes' not in str(ex):
211 ui.warn(str(ex) + b'\n')
211 if b'illegal_fstypes' not in stringutil.forcebytestr(ex):
212 ui.warn(stringutil.forcebytestr(ex) + b'\n')
212 213 if ex.invalidate:
213 214 state.invalidate()
214 215 # experimental config: fsmonitor.verbose
215 216 if ui.configbool(b'fsmonitor', b'verbose'):
216 ui.log(b'fsmonitor', b'Watchman unavailable: %s\n', ex.msg)
217 ui.log(
218 b'fsmonitor',
219 b'Watchman unavailable: %s\n',
220 stringutil.forcebytestr(ex.msg),
221 )
217 222 else:
218 ui.log(b'fsmonitor', b'Watchman exception: %s\n', ex)
223 ui.log(
224 b'fsmonitor',
225 b'Watchman exception: %s\n',
226 stringutil.forcebytestr(ex),
227 )
219 228
220 229
221 230 def _hashignore(ignore):
@@ -227,8 +236,8 b' def _hashignore(ignore):'
227 236
228 237 """
229 238 sha1 = hashlib.sha1()
230 sha1.update(repr(ignore))
231 return sha1.hexdigest()
239 sha1.update(pycompat.byterepr(ignore))
240 return pycompat.sysbytes(sha1.hexdigest())
232 241
233 242
234 243 _watchmanencoding = pywatchman.encoding.get_local_encoding()
@@ -245,12 +254,14 b' def _watchmantofsencoding(path):'
245 254 try:
246 255 decoded = path.decode(_watchmanencoding)
247 256 except UnicodeDecodeError as e:
248 raise error.Abort(str(e), hint=b'watchman encoding error')
257 raise error.Abort(
258 stringutil.forcebytestr(e), hint=b'watchman encoding error'
259 )
249 260
250 261 try:
251 262 encoded = decoded.encode(_fsencoding, 'strict')
252 263 except UnicodeEncodeError as e:
253 raise error.Abort(str(e))
264 raise error.Abort(stringutil.forcebytestr(e))
254 265
255 266 return encoded
256 267
@@ -372,7 +383,7 b' def overridewalk(orig, self, match, subr'
372 383 else:
373 384 # We need to propagate the last observed clock up so that we
374 385 # can use it for our next query
375 state.setlastclock(result[b'clock'])
386 state.setlastclock(pycompat.sysbytes(result[b'clock']))
376 387 if result[b'is_fresh_instance']:
377 388 if state.walk_on_invalidate:
378 389 state.invalidate()
@@ -396,8 +407,15 b' def overridewalk(orig, self, match, subr'
396 407 # for name case changes.
397 408 for entry in result[b'files']:
398 409 fname = entry[b'name']
410
411 # Watchman always give us a str. Normalize to bytes on Python 3
412 # using Watchman's encoding, if needed.
413 if not isinstance(fname, bytes):
414 fname = fname.encode(_watchmanencoding)
415
399 416 if _fixencoding:
400 417 fname = _watchmantofsencoding(fname)
418
401 419 if switch_slashes:
402 420 fname = fname.replace(b'\\', b'/')
403 421 if normalize:
@@ -486,9 +504,9 b' def overridewalk(orig, self, match, subr'
486 504 for f in auditfail:
487 505 results[f] = None
488 506
489 nf = iter(auditpass).next
507 nf = iter(auditpass)
490 508 for st in util.statfiles([join(f) for f in auditpass]):
491 f = nf()
509 f = next(nf)
492 510 if st or f in dmap:
493 511 results[f] = st
494 512
@@ -916,7 +934,7 b' def reposetup(ui, repo):'
916 934 return
917 935
918 936 try:
919 client = watchmanclient.client(repo.ui, repo._root)
937 client = watchmanclient.client(repo.ui, repo.root)
920 938 except Exception as ex:
921 939 _handleunavailable(ui, fsmonitorstate, ex)
922 940 return
This diff has been collapsed as it changes many lines, (604 lines changed) Show them Hide them
@@ -26,10 +26,8 b''
26 26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 28
29 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 29 # no unicode literals
30 from __future__ import absolute_import, division, print_function
33 31
34 32 import inspect
35 33 import math
@@ -38,33 +36,22 b' import socket'
38 36 import subprocess
39 37 import time
40 38
39 from . import capabilities, compat, encoding, load
40
41
41 42 # Sometimes it's really hard to get Python extensions to compile,
42 43 # so fall back to a pure Python implementation.
43 44 try:
44 45 from . import bser
46
45 47 # Demandimport causes modules to be loaded lazily. Force the load now
46 48 # so that we can fall back on pybser if bser doesn't exist
47 49 bser.pdu_info
48 50 except ImportError:
49 51 from . import pybser as bser
50 52
51 from mercurial.utils import (
52 procutil,
53 )
54 53
55 from mercurial import (
56 pycompat,
57 )
58
59 from . import (
60 capabilities,
61 compat,
62 encoding,
63 load,
64 )
65
66
67 if os.name == 'nt':
54 if os.name == "nt":
68 55 import ctypes
69 56 import ctypes.wintypes
70 57
@@ -73,7 +60,7 b" if os.name == 'nt':"
73 60 GENERIC_WRITE = 0x40000000
74 61 FILE_FLAG_OVERLAPPED = 0x40000000
75 62 OPEN_EXISTING = 3
76 INVALID_HANDLE_VALUE = -1
63 INVALID_HANDLE_VALUE = ctypes.c_void_p(-1).value
77 64 FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
78 65 FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100
79 66 FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
@@ -92,9 +79,11 b" if os.name == 'nt':"
92 79
93 80 class OVERLAPPED(ctypes.Structure):
94 81 _fields_ = [
95 ("Internal", ULONG_PTR), ("InternalHigh", ULONG_PTR),
96 ("Offset", wintypes.DWORD), ("OffsetHigh", wintypes.DWORD),
97 ("hEvent", wintypes.HANDLE)
82 ("Internal", ULONG_PTR),
83 ("InternalHigh", ULONG_PTR),
84 ("Offset", wintypes.DWORD),
85 ("OffsetHigh", wintypes.DWORD),
86 ("hEvent", wintypes.HANDLE),
98 87 ]
99 88
100 89 def __init__(self):
@@ -107,9 +96,15 b" if os.name == 'nt':"
107 96 LPDWORD = ctypes.POINTER(wintypes.DWORD)
108 97
109 98 CreateFile = ctypes.windll.kernel32.CreateFileA
110 CreateFile.argtypes = [wintypes.LPSTR, wintypes.DWORD, wintypes.DWORD,
111 wintypes.LPVOID, wintypes.DWORD, wintypes.DWORD,
112 wintypes.HANDLE]
99 CreateFile.argtypes = [
100 wintypes.LPSTR,
101 wintypes.DWORD,
102 wintypes.DWORD,
103 wintypes.LPVOID,
104 wintypes.DWORD,
105 wintypes.DWORD,
106 wintypes.HANDLE,
107 ]
113 108 CreateFile.restype = wintypes.HANDLE
114 109
115 110 CloseHandle = ctypes.windll.kernel32.CloseHandle
@@ -117,13 +112,23 b" if os.name == 'nt':"
117 112 CloseHandle.restype = wintypes.BOOL
118 113
119 114 ReadFile = ctypes.windll.kernel32.ReadFile
120 ReadFile.argtypes = [wintypes.HANDLE, wintypes.LPVOID, wintypes.DWORD,
121 LPDWORD, ctypes.POINTER(OVERLAPPED)]
115 ReadFile.argtypes = [
116 wintypes.HANDLE,
117 wintypes.LPVOID,
118 wintypes.DWORD,
119 LPDWORD,
120 ctypes.POINTER(OVERLAPPED),
121 ]
122 122 ReadFile.restype = wintypes.BOOL
123 123
124 124 WriteFile = ctypes.windll.kernel32.WriteFile
125 WriteFile.argtypes = [wintypes.HANDLE, wintypes.LPVOID, wintypes.DWORD,
126 LPDWORD, ctypes.POINTER(OVERLAPPED)]
125 WriteFile.argtypes = [
126 wintypes.HANDLE,
127 wintypes.LPVOID,
128 wintypes.DWORD,
129 LPDWORD,
130 ctypes.POINTER(OVERLAPPED),
131 ]
127 132 WriteFile.restype = wintypes.BOOL
128 133
129 134 GetLastError = ctypes.windll.kernel32.GetLastError
@@ -135,34 +140,56 b" if os.name == 'nt':"
135 140 SetLastError.restype = None
136 141
137 142 FormatMessage = ctypes.windll.kernel32.FormatMessageA
138 FormatMessage.argtypes = [wintypes.DWORD, wintypes.LPVOID, wintypes.DWORD,
139 wintypes.DWORD, ctypes.POINTER(wintypes.LPSTR),
140 wintypes.DWORD, wintypes.LPVOID]
143 FormatMessage.argtypes = [
144 wintypes.DWORD,
145 wintypes.LPVOID,
146 wintypes.DWORD,
147 wintypes.DWORD,
148 ctypes.POINTER(wintypes.LPSTR),
149 wintypes.DWORD,
150 wintypes.LPVOID,
151 ]
141 152 FormatMessage.restype = wintypes.DWORD
142 153
143 154 LocalFree = ctypes.windll.kernel32.LocalFree
144 155
145 156 GetOverlappedResult = ctypes.windll.kernel32.GetOverlappedResult
146 GetOverlappedResult.argtypes = [wintypes.HANDLE,
147 ctypes.POINTER(OVERLAPPED), LPDWORD,
148 wintypes.BOOL]
157 GetOverlappedResult.argtypes = [
158 wintypes.HANDLE,
159 ctypes.POINTER(OVERLAPPED),
160 LPDWORD,
161 wintypes.BOOL,
162 ]
149 163 GetOverlappedResult.restype = wintypes.BOOL
150 164
151 GetOverlappedResultEx = getattr(ctypes.windll.kernel32,
152 'GetOverlappedResultEx', None)
165 GetOverlappedResultEx = getattr(
166 ctypes.windll.kernel32, "GetOverlappedResultEx", None
167 )
153 168 if GetOverlappedResultEx is not None:
154 GetOverlappedResultEx.argtypes = [wintypes.HANDLE,
155 ctypes.POINTER(OVERLAPPED), LPDWORD,
156 wintypes.DWORD, wintypes.BOOL]
169 GetOverlappedResultEx.argtypes = [
170 wintypes.HANDLE,
171 ctypes.POINTER(OVERLAPPED),
172 LPDWORD,
173 wintypes.DWORD,
174 wintypes.BOOL,
175 ]
157 176 GetOverlappedResultEx.restype = wintypes.BOOL
158 177
159 178 WaitForSingleObjectEx = ctypes.windll.kernel32.WaitForSingleObjectEx
160 WaitForSingleObjectEx.argtypes = [wintypes.HANDLE, wintypes.DWORD, wintypes.BOOL]
179 WaitForSingleObjectEx.argtypes = [
180 wintypes.HANDLE,
181 wintypes.DWORD,
182 wintypes.BOOL,
183 ]
161 184 WaitForSingleObjectEx.restype = wintypes.DWORD
162 185
163 186 CreateEvent = ctypes.windll.kernel32.CreateEventA
164 CreateEvent.argtypes = [LPDWORD, wintypes.BOOL, wintypes.BOOL,
165 wintypes.LPSTR]
187 CreateEvent.argtypes = [
188 LPDWORD,
189 wintypes.BOOL,
190 wintypes.BOOL,
191 wintypes.LPSTR,
192 ]
166 193 CreateEvent.restype = wintypes.HANDLE
167 194
168 195 # Windows Vista is the minimum supported client for CancelIoEx.
@@ -178,9 +205,15 b' sniff_len = 13'
178 205 if _debugging:
179 206
180 207 def log(fmt, *args):
181 print('[%s] %s' %
182 (time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime()),
183 fmt % args[:]))
208 print(
209 "[%s] %s"
210 % (
211 time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime()),
212 fmt % args[:],
213 )
214 )
215
216
184 217 else:
185 218
186 219 def log(fmt, *args):
@@ -193,8 +226,16 b' def _win32_strerror(err):'
193 226 # FormatMessage will allocate memory and assign it here
194 227 buf = ctypes.c_char_p()
195 228 FormatMessage(
196 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER
197 | FORMAT_MESSAGE_IGNORE_INSERTS, None, err, 0, buf, 0, None)
229 FORMAT_MESSAGE_FROM_SYSTEM
230 | FORMAT_MESSAGE_ALLOCATE_BUFFER
231 | FORMAT_MESSAGE_IGNORE_INSERTS,
232 None,
233 err,
234 0,
235 buf,
236 0,
237 None,
238 )
198 239 try:
199 240 return buf.value
200 241 finally:
@@ -211,21 +252,30 b' class WatchmanError(Exception):'
211 252
212 253 def __str__(self):
213 254 if self.cmd:
214 return '%s, while executing %s' % (self.msg, self.cmd)
255 return "%s, while executing %s" % (self.msg, self.cmd)
215 256 return self.msg
216 257
217 258
259 class BSERv1Unsupported(WatchmanError):
260 pass
261
262
263 class UseAfterFork(WatchmanError):
264 pass
265
266
218 267 class WatchmanEnvironmentError(WatchmanError):
219 268 def __init__(self, msg, errno, errmsg, cmd=None):
220 269 super(WatchmanEnvironmentError, self).__init__(
221 '{0}: errno={1} errmsg={2}'.format(msg, errno, errmsg),
222 cmd)
270 "{0}: errno={1} errmsg={2}".format(msg, errno, errmsg), cmd
271 )
223 272
224 273
225 274 class SocketConnectError(WatchmanError):
226 275 def __init__(self, sockpath, exc):
227 276 super(SocketConnectError, self).__init__(
228 'unable to connect to %s: %s' % (sockpath, exc))
277 "unable to connect to %s: %s" % (sockpath, exc)
278 )
229 279 self.sockpath = sockpath
230 280 self.exc = exc
231 281
@@ -245,15 +295,16 b' class CommandError(WatchmanError):'
245 295
246 296 self.msg is the message returned by watchman.
247 297 """
298
248 299 def __init__(self, msg, cmd=None):
249 300 super(CommandError, self).__init__(
250 'watchman command error: %s' % (msg, ),
251 cmd,
301 "watchman command error: %s" % (msg,), cmd
252 302 )
253 303
254 304
255 305 class Transport(object):
256 306 """ communication transport to the watchman server """
307
257 308 buf = None
258 309
259 310 def close(self):
@@ -289,7 +340,7 b' class Transport(object):'
289 340 while True:
290 341 b = self.readBytes(4096)
291 342 if b"\n" in b:
292 result = b''.join(self.buf)
343 result = b"".join(self.buf)
293 344 (line, b) = b.split(b"\n", 1)
294 345 self.buf = [b]
295 346 return result + line
@@ -298,6 +349,7 b' class Transport(object):'
298 349
299 350 class Codec(object):
300 351 """ communication encoding for the watchman server """
352
301 353 transport = None
302 354
303 355 def __init__(self, transport):
@@ -315,9 +367,10 b' class Codec(object):'
315 367
316 368 class UnixSocketTransport(Transport):
317 369 """ local unix domain socket transport """
370
318 371 sock = None
319 372
320 def __init__(self, sockpath, timeout, watchman_exe):
373 def __init__(self, sockpath, timeout):
321 374 self.sockpath = sockpath
322 375 self.timeout = timeout
323 376
@@ -331,8 +384,9 b' class UnixSocketTransport(Transport):'
331 384 raise SocketConnectError(self.sockpath, e)
332 385
333 386 def close(self):
334 self.sock.close()
335 self.sock = None
387 if self.sock:
388 self.sock.close()
389 self.sock = None
336 390
337 391 def setTimeout(self, value):
338 392 self.timeout = value
@@ -342,16 +396,16 b' class UnixSocketTransport(Transport):'
342 396 try:
343 397 buf = [self.sock.recv(size)]
344 398 if not buf[0]:
345 raise WatchmanError('empty watchman response')
399 raise WatchmanError("empty watchman response")
346 400 return buf[0]
347 401 except socket.timeout:
348 raise SocketTimeout('timed out waiting for response')
402 raise SocketTimeout("timed out waiting for response")
349 403
350 404 def write(self, data):
351 405 try:
352 406 self.sock.sendall(data)
353 407 except socket.timeout:
354 raise SocketTimeout('timed out sending query command')
408 raise SocketTimeout("timed out sending query command")
355 409
356 410
357 411 def _get_overlapped_result_ex_impl(pipe, olap, nbytes, millis, alertable):
@@ -364,7 +418,7 b' def _get_overlapped_result_ex_impl(pipe,'
364 418 source code (see get_overlapped_result_ex_impl in stream_win.c). This
365 419 way, maintenance should be simplified.
366 420 """
367 log('Preparing to wait for maximum %dms', millis )
421 log("Preparing to wait for maximum %dms", millis)
368 422 if millis != 0:
369 423 waitReturnCode = WaitForSingleObjectEx(olap.hEvent, millis, alertable)
370 424 if waitReturnCode == WAIT_OBJECT_0:
@@ -383,12 +437,12 b' def _get_overlapped_result_ex_impl(pipe,'
383 437 elif waitReturnCode == WAIT_FAILED:
384 438 # something went wrong calling WaitForSingleObjectEx
385 439 err = GetLastError()
386 log('WaitForSingleObjectEx failed: %s', _win32_strerror(err))
440 log("WaitForSingleObjectEx failed: %s", _win32_strerror(err))
387 441 return False
388 442 else:
389 443 # unexpected situation deserving investigation.
390 444 err = GetLastError()
391 log('Unexpected error: %s', _win32_strerror(err))
445 log("Unexpected error: %s", _win32_strerror(err))
392 446 return False
393 447
394 448 return GetOverlappedResult(pipe, olap, nbytes, False)
@@ -397,36 +451,52 b' def _get_overlapped_result_ex_impl(pipe,'
397 451 class WindowsNamedPipeTransport(Transport):
398 452 """ connect to a named pipe """
399 453
400 def __init__(self, sockpath, timeout, watchman_exe):
454 def __init__(self, sockpath, timeout):
401 455 self.sockpath = sockpath
402 456 self.timeout = int(math.ceil(timeout * 1000))
403 457 self._iobuf = None
404 458
405 self.pipe = CreateFile(sockpath, GENERIC_READ | GENERIC_WRITE, 0, None,
406 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, None)
459 if compat.PYTHON3:
460 sockpath = os.fsencode(sockpath)
461 self.pipe = CreateFile(
462 sockpath,
463 GENERIC_READ | GENERIC_WRITE,
464 0,
465 None,
466 OPEN_EXISTING,
467 FILE_FLAG_OVERLAPPED,
468 None,
469 )
407 470
408 if self.pipe == INVALID_HANDLE_VALUE:
471 err = GetLastError()
472 if self.pipe == INVALID_HANDLE_VALUE or self.pipe == 0:
409 473 self.pipe = None
410 self._raise_win_err('failed to open pipe %s' % sockpath,
411 GetLastError())
474 raise SocketConnectError(self.sockpath, self._make_win_err("", err))
412 475
413 476 # event for the overlapped I/O operations
414 477 self._waitable = CreateEvent(None, True, False, None)
478 err = GetLastError()
415 479 if self._waitable is None:
416 self._raise_win_err('CreateEvent failed', GetLastError())
480 self._raise_win_err("CreateEvent failed", err)
417 481
418 482 self._get_overlapped_result_ex = GetOverlappedResultEx
419 if (os.getenv('WATCHMAN_WIN7_COMPAT') == '1' or
420 self._get_overlapped_result_ex is None):
483 if (
484 os.getenv("WATCHMAN_WIN7_COMPAT") == "1"
485 or self._get_overlapped_result_ex is None
486 ):
421 487 self._get_overlapped_result_ex = _get_overlapped_result_ex_impl
422 488
423 489 def _raise_win_err(self, msg, err):
424 raise IOError('%s win32 error code: %d %s' %
425 (msg, err, _win32_strerror(err)))
490 raise self._make_win_err(msg, err)
491
492 def _make_win_err(self, msg, err):
493 return IOError(
494 "%s win32 error code: %d %s" % (msg, err, _win32_strerror(err))
495 )
426 496
427 497 def close(self):
428 498 if self.pipe:
429 log('Closing pipe')
499 log("Closing pipe")
430 500 CloseHandle(self.pipe)
431 501 self.pipe = None
432 502
@@ -460,7 +530,7 b' class WindowsNamedPipeTransport(Transpor'
460 530 olap = OVERLAPPED()
461 531 olap.hEvent = self._waitable
462 532
463 log('made read buff of size %d', size)
533 log("made read buff of size %d", size)
464 534
465 535 # ReadFile docs warn against sending in the nread parameter for async
466 536 # operations, so we always collect it via GetOverlappedResultEx
@@ -469,23 +539,23 b' class WindowsNamedPipeTransport(Transpor'
469 539 if not immediate:
470 540 err = GetLastError()
471 541 if err != ERROR_IO_PENDING:
472 self._raise_win_err('failed to read %d bytes' % size,
473 GetLastError())
542 self._raise_win_err("failed to read %d bytes" % size, err)
474 543
475 544 nread = wintypes.DWORD()
476 if not self._get_overlapped_result_ex(self.pipe, olap, nread,
477 0 if immediate else self.timeout,
478 True):
545 if not self._get_overlapped_result_ex(
546 self.pipe, olap, nread, 0 if immediate else self.timeout, True
547 ):
479 548 err = GetLastError()
480 549 CancelIoEx(self.pipe, olap)
481 550
482 551 if err == WAIT_TIMEOUT:
483 log('GetOverlappedResultEx timedout')
484 raise SocketTimeout('timed out after waiting %dms for read' %
485 self.timeout)
552 log("GetOverlappedResultEx timedout")
553 raise SocketTimeout(
554 "timed out after waiting %dms for read" % self.timeout
555 )
486 556
487 log('GetOverlappedResultEx reports error %d', err)
488 self._raise_win_err('error while waiting for read', err)
557 log("GetOverlappedResultEx reports error %d", err)
558 self._raise_win_err("error while waiting for read", err)
489 559
490 560 nread = nread.value
491 561 if nread == 0:
@@ -494,7 +564,7 b' class WindowsNamedPipeTransport(Transpor'
494 564 # other way this shows up is if the client has gotten in a weird
495 565 # state, so let's bail out
496 566 CancelIoEx(self.pipe, olap)
497 raise IOError('Async read yielded 0 bytes; unpossible!')
567 raise IOError("Async read yielded 0 bytes; unpossible!")
498 568
499 569 # Holds precisely the bytes that we read from the prior request
500 570 buf = buf[:nread]
@@ -511,21 +581,25 b' class WindowsNamedPipeTransport(Transpor'
511 581 olap = OVERLAPPED()
512 582 olap.hEvent = self._waitable
513 583
514 immediate = WriteFile(self.pipe, ctypes.c_char_p(data), len(data),
515 None, olap)
584 immediate = WriteFile(
585 self.pipe, ctypes.c_char_p(data), len(data), None, olap
586 )
516 587
517 588 if not immediate:
518 589 err = GetLastError()
519 590 if err != ERROR_IO_PENDING:
520 self._raise_win_err('failed to write %d bytes' % len(data),
521 GetLastError())
591 self._raise_win_err(
592 "failed to write %d bytes to handle %r"
593 % (len(data), self.pipe),
594 err,
595 )
522 596
523 597 # Obtain results, waiting if needed
524 598 nwrote = wintypes.DWORD()
525 if self._get_overlapped_result_ex(self.pipe, olap, nwrote,
526 0 if immediate else self.timeout,
527 True):
528 log('made write of %d bytes', nwrote.value)
599 if self._get_overlapped_result_ex(
600 self.pipe, olap, nwrote, 0 if immediate else self.timeout, True
601 ):
602 log("made write of %d bytes", nwrote.value)
529 603 return nwrote.value
530 604
531 605 err = GetLastError()
@@ -535,10 +609,21 b' class WindowsNamedPipeTransport(Transpor'
535 609 CancelIoEx(self.pipe, olap)
536 610
537 611 if err == WAIT_TIMEOUT:
538 raise SocketTimeout('timed out after waiting %dms for write' %
539 self.timeout)
540 self._raise_win_err('error while waiting for write of %d bytes' %
541 len(data), err)
612 raise SocketTimeout(
613 "timed out after waiting %dms for write" % self.timeout
614 )
615 self._raise_win_err(
616 "error while waiting for write of %d bytes" % len(data), err
617 )
618
619
620 def _default_binpath(binpath=None):
621 if binpath:
622 return binpath
623 # The test harness sets WATCHMAN_BINARY to the binary under test,
624 # so we use that by default, otherwise, allow resolving watchman
625 # from the users PATH.
626 return os.environ.get("WATCHMAN_BINARY", "watchman")
542 627
543 628
544 629 class CLIProcessTransport(Transport):
@@ -560,13 +645,14 b' class CLIProcessTransport(Transport):'
560 645 It is the responsibility of the caller to set the send and
561 646 receive codecs appropriately.
562 647 """
648
563 649 proc = None
564 650 closed = True
565 651
566 def __init__(self, sockpath, timeout, watchman_exe):
652 def __init__(self, sockpath, timeout, binpath=None):
567 653 self.sockpath = sockpath
568 654 self.timeout = timeout
569 self.watchman_exe = watchman_exe
655 self.binpath = _default_binpath(binpath)
570 656
571 657 def close(self):
572 658 if self.proc:
@@ -574,32 +660,32 b' class CLIProcessTransport(Transport):'
574 660 self.proc.kill()
575 661 self.proc.stdin.close()
576 662 self.proc.stdout.close()
663 self.proc.wait()
577 664 self.proc = None
578 665
579 666 def _connect(self):
580 667 if self.proc:
581 668 return self.proc
582 669 args = [
583 self.watchman_exe,
584 '--sockname={0}'.format(self.sockpath),
585 '--logfile=/BOGUS',
586 '--statefile=/BOGUS',
587 '--no-spawn',
588 '--no-local',
589 '--no-pretty',
590 '-j',
670 self.binpath,
671 "--sockname={0}".format(self.sockpath),
672 "--logfile=/BOGUS",
673 "--statefile=/BOGUS",
674 "--no-spawn",
675 "--no-local",
676 "--no-pretty",
677 "-j",
591 678 ]
592 self.proc = subprocess.Popen(pycompat.rapply(procutil.tonativestr,
593 args),
594 stdin=subprocess.PIPE,
595 stdout=subprocess.PIPE)
679 self.proc = subprocess.Popen(
680 args, stdin=subprocess.PIPE, stdout=subprocess.PIPE
681 )
596 682 return self.proc
597 683
598 684 def readBytes(self, size):
599 685 self._connect()
600 686 res = self.proc.stdout.read(size)
601 if res == '':
602 raise WatchmanError('EOF on CLI process transport')
687 if not res:
688 raise WatchmanError("EOF on CLI process transport")
603 689 return res
604 690
605 691 def write(self, data):
@@ -616,13 +702,22 b' class CLIProcessTransport(Transport):'
616 702 class BserCodec(Codec):
617 703 """ use the BSER encoding. This is the default, preferred codec """
618 704
705 def __init__(self, transport, value_encoding, value_errors):
706 super(BserCodec, self).__init__(transport)
707 self._value_encoding = value_encoding
708 self._value_errors = value_errors
709
619 710 def _loads(self, response):
620 return bser.loads(response) # Defaults to BSER v1
711 return bser.loads(
712 response,
713 value_encoding=self._value_encoding,
714 value_errors=self._value_errors,
715 )
621 716
622 717 def receive(self):
623 718 buf = [self.transport.readBytes(sniff_len)]
624 719 if not buf[0]:
625 raise WatchmanError('empty watchman response')
720 raise WatchmanError("empty watchman response")
626 721
627 722 _1, _2, elen = bser.pdu_info(buf[0])
628 723
@@ -631,15 +726,15 b' class BserCodec(Codec):'
631 726 buf.append(self.transport.readBytes(elen - rlen))
632 727 rlen += len(buf[-1])
633 728
634 response = b''.join(buf)
729 response = b"".join(buf)
635 730 try:
636 731 res = self._loads(response)
637 732 return res
638 733 except ValueError as e:
639 raise WatchmanError('watchman response decode error: %s' % e)
734 raise WatchmanError("watchman response decode error: %s" % e)
640 735
641 736 def send(self, *args):
642 cmd = bser.dumps(*args) # Defaults to BSER v1
737 cmd = bser.dumps(*args) # Defaults to BSER v1
643 738 self.transport.write(cmd)
644 739
645 740
@@ -648,74 +743,96 b' class ImmutableBserCodec(BserCodec):'
648 743 immutable object support """
649 744
650 745 def _loads(self, response):
651 return bser.loads(response, False) # Defaults to BSER v1
746 return bser.loads(
747 response,
748 False,
749 value_encoding=self._value_encoding,
750 value_errors=self._value_errors,
751 )
652 752
653 753
654 754 class Bser2WithFallbackCodec(BserCodec):
655 755 """ use BSER v2 encoding """
656 756
657 def __init__(self, transport):
658 super(Bser2WithFallbackCodec, self).__init__(transport)
659 # Once the server advertises support for bser-v2 we should switch this
660 # to 'required' on Python 3.
661 self.send(["version", {"optional": ["bser-v2"]}])
757 def __init__(self, transport, value_encoding, value_errors):
758 super(Bser2WithFallbackCodec, self).__init__(
759 transport, value_encoding, value_errors
760 )
761 if compat.PYTHON3:
762 bserv2_key = "required"
763 else:
764 bserv2_key = "optional"
765
766 self.send(["version", {bserv2_key: ["bser-v2"]}])
662 767
663 768 capabilities = self.receive()
664 769
665 if 'error' in capabilities:
666 raise Exception('Unsupported BSER version')
770 if "error" in capabilities:
771 raise BSERv1Unsupported(
772 "The watchman server version does not support Python 3. Please "
773 "upgrade your watchman server."
774 )
667 775
668 if capabilities['capabilities']['bser-v2']:
776 if capabilities["capabilities"]["bser-v2"]:
669 777 self.bser_version = 2
670 778 self.bser_capabilities = 0
671 779 else:
672 780 self.bser_version = 1
673 781 self.bser_capabilities = 0
674 782
675 def _loads(self, response):
676 return bser.loads(response)
677
678 783 def receive(self):
679 784 buf = [self.transport.readBytes(sniff_len)]
680 785 if not buf[0]:
681 raise WatchmanError('empty watchman response')
786 raise WatchmanError("empty watchman response")
682 787
683 788 recv_bser_version, recv_bser_capabilities, elen = bser.pdu_info(buf[0])
684 789
685 if hasattr(self, 'bser_version'):
686 # Readjust BSER version and capabilities if necessary
687 self.bser_version = max(self.bser_version, recv_bser_version)
688 self.capabilities = self.bser_capabilities & recv_bser_capabilities
790 if hasattr(self, "bser_version"):
791 # Readjust BSER version and capabilities if necessary
792 self.bser_version = max(self.bser_version, recv_bser_version)
793 self.capabilities = self.bser_capabilities & recv_bser_capabilities
689 794
690 795 rlen = len(buf[0])
691 796 while elen > rlen:
692 797 buf.append(self.transport.readBytes(elen - rlen))
693 798 rlen += len(buf[-1])
694 799
695 response = b''.join(buf)
800 response = b"".join(buf)
696 801 try:
697 802 res = self._loads(response)
698 803 return res
699 804 except ValueError as e:
700 raise WatchmanError('watchman response decode error: %s' % e)
805 raise WatchmanError("watchman response decode error: %s" % e)
701 806
702 807 def send(self, *args):
703 if hasattr(self, 'bser_version'):
704 cmd = bser.dumps(*args, version=self.bser_version,
705 capabilities=self.bser_capabilities)
808 if hasattr(self, "bser_version"):
809 cmd = bser.dumps(
810 *args,
811 version=self.bser_version,
812 capabilities=self.bser_capabilities
813 )
706 814 else:
707 815 cmd = bser.dumps(*args)
708 816 self.transport.write(cmd)
709 817
710 818
819 class ImmutableBser2Codec(Bser2WithFallbackCodec, ImmutableBserCodec):
820 """ use the BSER encoding, decoding values using the newer
821 immutable object support """
822
823 pass
824
825
711 826 class JsonCodec(Codec):
712 827 """ Use json codec. This is here primarily for testing purposes """
828
713 829 json = None
714 830
715 831 def __init__(self, transport):
716 832 super(JsonCodec, self).__init__(transport)
717 833 # optional dep on json, only if JsonCodec is used
718 834 import json
835
719 836 self.json = json
720 837
721 838 def receive(self):
@@ -727,7 +844,7 b' class JsonCodec(Codec):'
727 844 # but it's possible we might get non-ASCII bytes that are valid
728 845 # UTF-8.
729 846 if compat.PYTHON3:
730 line = line.decode('utf-8')
847 line = line.decode("utf-8")
731 848 return self.json.loads(line)
732 849 except Exception as e:
733 850 print(e, line)
@@ -739,12 +856,13 b' class JsonCodec(Codec):'
739 856 # containing Unicode strings to Unicode string. Even with (the default)
740 857 # ensure_ascii=True, dumps returns a Unicode string.
741 858 if compat.PYTHON3:
742 cmd = cmd.encode('ascii')
859 cmd = cmd.encode("ascii")
743 860 self.transport.write(cmd + b"\n")
744 861
745 862
746 863 class client(object):
747 864 """ Handles the communication with the watchman service """
865
748 866 sockpath = None
749 867 transport = None
750 868 sendCodec = None
@@ -754,60 +872,100 b' class client(object):'
754 872 subs = {} # Keyed by subscription name
755 873 sub_by_root = {} # Keyed by root, then by subscription name
756 874 logs = [] # When log level is raised
757 unilateral = ['log', 'subscription']
875 unilateral = ["log", "subscription"]
758 876 tport = None
759 877 useImmutableBser = None
760 watchman_exe = None
878 pid = None
761 879
762 def __init__(self,
763 sockpath=None,
764 timeout=1.0,
765 transport=None,
766 sendEncoding=None,
767 recvEncoding=None,
768 useImmutableBser=False,
769 watchman_exe=None):
880 def __init__(
881 self,
882 sockpath=None,
883 timeout=1.0,
884 transport=None,
885 sendEncoding=None,
886 recvEncoding=None,
887 useImmutableBser=False,
888 # use False for these two because None has a special
889 # meaning
890 valueEncoding=False,
891 valueErrors=False,
892 binpath=None,
893 ):
770 894 self.sockpath = sockpath
771 895 self.timeout = timeout
772 896 self.useImmutableBser = useImmutableBser
773 self.watchman_exe = watchman_exe
897 self.binpath = _default_binpath(binpath)
774 898
775 899 if inspect.isclass(transport) and issubclass(transport, Transport):
776 900 self.transport = transport
777 901 else:
778 transport = transport or os.getenv('WATCHMAN_TRANSPORT') or 'local'
779 if transport == 'local' and os.name == 'nt':
902 transport = transport or os.getenv("WATCHMAN_TRANSPORT") or "local"
903 if transport == "local" and os.name == "nt":
780 904 self.transport = WindowsNamedPipeTransport
781 elif transport == 'local':
905 elif transport == "local":
782 906 self.transport = UnixSocketTransport
783 elif transport == 'cli':
907 elif transport == "cli":
784 908 self.transport = CLIProcessTransport
785 909 if sendEncoding is None:
786 sendEncoding = 'json'
910 sendEncoding = "json"
787 911 if recvEncoding is None:
788 912 recvEncoding = sendEncoding
789 913 else:
790 raise WatchmanError('invalid transport %s' % transport)
914 raise WatchmanError("invalid transport %s" % transport)
791 915
792 sendEncoding = str(sendEncoding or os.getenv('WATCHMAN_ENCODING') or
793 'bser')
794 recvEncoding = str(recvEncoding or os.getenv('WATCHMAN_ENCODING') or
795 'bser')
916 sendEncoding = str(
917 sendEncoding or os.getenv("WATCHMAN_ENCODING") or "bser"
918 )
919 recvEncoding = str(
920 recvEncoding or os.getenv("WATCHMAN_ENCODING") or "bser"
921 )
796 922
797 923 self.recvCodec = self._parseEncoding(recvEncoding)
798 924 self.sendCodec = self._parseEncoding(sendEncoding)
799 925
926 # We want to act like the native OS methods as much as possible. This
927 # means returning bytestrings on Python 2 by default and Unicode
928 # strings on Python 3. However we take an optional argument that lets
929 # users override this.
930 if valueEncoding is False:
931 if compat.PYTHON3:
932 self.valueEncoding = encoding.get_local_encoding()
933 self.valueErrors = encoding.default_local_errors
934 else:
935 self.valueEncoding = None
936 self.valueErrors = None
937 else:
938 self.valueEncoding = valueEncoding
939 if valueErrors is False:
940 self.valueErrors = encoding.default_local_errors
941 else:
942 self.valueErrors = valueErrors
943
944 def _makeBSERCodec(self, codec):
945 def make_codec(transport):
946 return codec(transport, self.valueEncoding, self.valueErrors)
947
948 return make_codec
949
800 950 def _parseEncoding(self, enc):
801 if enc == 'bser':
951 if enc == "bser":
802 952 if self.useImmutableBser:
803 return ImmutableBserCodec
804 return BserCodec
805 elif enc == 'experimental-bser-v2':
806 return Bser2WithFallbackCodec
807 elif enc == 'json':
953 return self._makeBSERCodec(ImmutableBser2Codec)
954 return self._makeBSERCodec(Bser2WithFallbackCodec)
955 elif enc == "bser-v1":
956 if compat.PYTHON3:
957 raise BSERv1Unsupported(
958 "Python 3 does not support the BSER v1 encoding: specify "
959 '"bser" or omit the sendEncoding and recvEncoding '
960 "arguments"
961 )
962 if self.useImmutableBser:
963 return self._makeBSERCodec(ImmutableBserCodec)
964 return self._makeBSERCodec(BserCodec)
965 elif enc == "json":
808 966 return JsonCodec
809 967 else:
810 raise WatchmanError('invalid encoding %s' % enc)
968 raise WatchmanError("invalid encoding %s" % enc)
811 969
812 970 def _hasprop(self, result, name):
813 971 if self.useImmutableBser:
@@ -817,26 +975,25 b' class client(object):'
817 975 def _resolvesockname(self):
818 976 # if invoked via a trigger, watchman will set this env var; we
819 977 # should use it unless explicitly set otherwise
820 path = os.getenv('WATCHMAN_SOCK')
978 path = os.getenv("WATCHMAN_SOCK")
821 979 if path:
822 980 return path
823 981
824 cmd = [self.watchman_exe, '--output-encoding=bser', 'get-sockname']
982 cmd = [self.binpath, "--output-encoding=bser", "get-sockname"]
825 983 try:
826 args = dict(stdout=subprocess.PIPE,
827 stderr=subprocess.PIPE,
828 close_fds=os.name != 'nt')
984 args = dict(
985 stdout=subprocess.PIPE, stderr=subprocess.PIPE
986 ) # noqa: C408
829 987
830 if os.name == 'nt':
988 if os.name == "nt":
831 989 # if invoked via an application with graphical user interface,
832 990 # this call will cause a brief command window pop-up.
833 991 # Using the flag STARTF_USESHOWWINDOW to avoid this behavior.
834 992 startupinfo = subprocess.STARTUPINFO()
835 993 startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
836 args['startupinfo'] = startupinfo
994 args["startupinfo"] = startupinfo
837 995
838 p = subprocess.Popen(pycompat.rapply(procutil.tonativestr, cmd),
839 **args)
996 p = subprocess.Popen(cmd, **args)
840 997
841 998 except OSError as e:
842 999 raise WatchmanError('"watchman" executable not in PATH (%s)' % e)
@@ -848,27 +1005,43 b' class client(object):'
848 1005 raise WatchmanError("watchman exited with code %d" % exitcode)
849 1006
850 1007 result = bser.loads(stdout)
851 if b'error' in result:
852 raise WatchmanError('get-sockname error: %s' % result['error'])
1008 if "error" in result:
1009 raise WatchmanError("get-sockname error: %s" % result["error"])
853 1010
854 return result[b'sockname']
1011 return result["sockname"]
855 1012
856 1013 def _connect(self):
857 1014 """ establish transport connection """
858 1015
859 1016 if self.recvConn:
1017 if self.pid != os.getpid():
1018 raise UseAfterFork(
1019 "do not re-use a connection after fork; open a new client instead"
1020 )
860 1021 return
861 1022
862 1023 if self.sockpath is None:
863 1024 self.sockpath = self._resolvesockname()
864 1025
865 self.tport = self.transport(self.sockpath, self.timeout, self.watchman_exe)
1026 kwargs = {}
1027 if self.transport == CLIProcessTransport:
1028 kwargs["binpath"] = self.binpath
1029
1030 self.tport = self.transport(self.sockpath, self.timeout, **kwargs)
866 1031 self.sendConn = self.sendCodec(self.tport)
867 1032 self.recvConn = self.recvCodec(self.tport)
1033 self.pid = os.getpid()
868 1034
869 1035 def __del__(self):
870 1036 self.close()
871 1037
1038 def __enter__(self):
1039 self._connect()
1040 return self
1041
1042 def __exit__(self, exc_type, exc_value, exc_traceback):
1043 self.close()
1044
872 1045 def close(self):
873 1046 if self.tport:
874 1047 self.tport.close()
@@ -893,26 +1066,20 b' class client(object):'
893 1066
894 1067 self._connect()
895 1068 result = self.recvConn.receive()
896 if self._hasprop(result, 'error'):
897 error = result['error']
898 if compat.PYTHON3 and isinstance(self.recvConn, BserCodec):
899 error = result['error'].decode('utf-8', 'surrogateescape')
900 raise CommandError(error)
1069 if self._hasprop(result, "error"):
1070 raise CommandError(result["error"])
901 1071
902 if self._hasprop(result, 'log'):
903 log = result['log']
904 if compat.PYTHON3 and isinstance(self.recvConn, BserCodec):
905 log = log.decode('utf-8', 'surrogateescape')
906 self.logs.append(log)
1072 if self._hasprop(result, "log"):
1073 self.logs.append(result["log"])
907 1074
908 if self._hasprop(result, 'subscription'):
909 sub = result['subscription']
1075 if self._hasprop(result, "subscription"):
1076 sub = result["subscription"]
910 1077 if not (sub in self.subs):
911 1078 self.subs[sub] = []
912 1079 self.subs[sub].append(result)
913 1080
914 1081 # also accumulate in {root,sub} keyed store
915 root = os.path.normcase(result['root'])
1082 root = os.path.normpath(os.path.normcase(result["root"]))
916 1083 if not root in self.sub_by_root:
917 1084 self.sub_by_root[root] = {}
918 1085 if not sub in self.sub_by_root[root]:
@@ -922,7 +1089,7 b' class client(object):'
922 1089 return result
923 1090
924 1091 def isUnilateralResponse(self, res):
925 if 'unilateral' in res and res['unilateral']:
1092 if "unilateral" in res and res["unilateral"]:
926 1093 return True
927 1094 # Fall back to checking for known unilateral responses
928 1095 for k in self.unilateral:
@@ -955,18 +1122,11 b' class client(object):'
955 1122 remove processing impacts both the unscoped and scoped stores
956 1123 for the subscription data.
957 1124 """
958 if compat.PYTHON3 and issubclass(self.recvCodec, BserCodec):
959 # People may pass in Unicode strings here -- but currently BSER only
960 # returns bytestrings. Deal with that.
961 if isinstance(root, str):
962 root = encoding.encode_local(root)
963 if isinstance(name, str):
964 name = name.encode('utf-8')
965
966 1125 if root is not None:
967 if not root in self.sub_by_root:
1126 root = os.path.normpath(os.path.normcase(root))
1127 if root not in self.sub_by_root:
968 1128 return None
969 if not name in self.sub_by_root[root]:
1129 if name not in self.sub_by_root[root]:
970 1130 return None
971 1131 sub = self.sub_by_root[root][name]
972 1132 if remove:
@@ -976,7 +1136,7 b' class client(object):'
976 1136 del self.subs[name]
977 1137 return sub
978 1138
979 if not (name in self.subs):
1139 if name not in self.subs:
980 1140 return None
981 1141 sub = self.subs[name]
982 1142 if remove:
@@ -992,7 +1152,7 b' class client(object):'
992 1152 and NOT returned via this method.
993 1153 """
994 1154
995 log('calling client.query')
1155 log("calling client.query")
996 1156 self._connect()
997 1157 try:
998 1158 self.sendConn.send(args)
@@ -1006,27 +1166,27 b' class client(object):'
1006 1166 # When we can depend on Python 3, we can use PEP 3134
1007 1167 # exception chaining here.
1008 1168 raise WatchmanEnvironmentError(
1009 'I/O error communicating with watchman daemon',
1169 "I/O error communicating with watchman daemon",
1010 1170 ee.errno,
1011 1171 ee.strerror,
1012 args)
1172 args,
1173 )
1013 1174 except WatchmanError as ex:
1014 1175 ex.setCommand(args)
1015 1176 raise
1016 1177
1017 1178 def capabilityCheck(self, optional=None, required=None):
1018 1179 """ Perform a server capability check """
1019 res = self.query('version', {
1020 'optional': optional or [],
1021 'required': required or []
1022 })
1180 res = self.query(
1181 "version", {"optional": optional or [], "required": required or []}
1182 )
1023 1183
1024 if not self._hasprop(res, 'capabilities'):
1184 if not self._hasprop(res, "capabilities"):
1025 1185 # Server doesn't support capabilities, so we need to
1026 1186 # synthesize the results based on the version
1027 1187 capabilities.synthesize(res, optional)
1028 if 'error' in res:
1029 raise CommandError(res['error'])
1188 if "error" in res:
1189 raise CommandError(res["error"])
1030 1190
1031 1191 return res
1032 1192
@@ -175,7 +175,22 b' static PyObject* bserobj_getattrro(PyObj'
175 175 const char* item_name = NULL;
176 176 PyObject* key = PyTuple_GET_ITEM(obj->keys, i);
177 177
178 item_name = PyBytes_AsString(key);
178 if (PyUnicode_Check(key)) {
179 #if PY_MAJOR_VERSION >= 3
180 item_name = PyUnicode_AsUTF8(key);
181 #else
182 PyObject* utf = PyUnicode_AsEncodedString(key, "utf-8", "ignore");
183 if (utf == NULL) {
184 goto bail;
185 }
186 item_name = PyBytes_AsString(utf);
187 #endif
188 } else {
189 item_name = PyBytes_AsString(key);
190 }
191 if (item_name == NULL) {
192 goto bail;
193 }
179 194 if (!strcmp(item_name, namestr)) {
180 195 ret = PySequence_GetItem(obj->values, i);
181 196 goto bail;
@@ -1147,11 +1162,15 b' static PyObject* bser_loads(PyObject* se'
1147 1162 }
1148 1163
1149 1164 static PyObject* bser_load(PyObject* self, PyObject* args, PyObject* kw) {
1150 PyObject *load, *string;
1165 PyObject* load;
1166 PyObject* load_method;
1167 PyObject* string;
1168 PyObject* load_method_args;
1169 PyObject* load_method_kwargs;
1151 1170 PyObject* fp = NULL;
1152 1171 PyObject* mutable_obj = NULL;
1153 const char* value_encoding = NULL;
1154 const char* value_errors = NULL;
1172 PyObject* value_encoding = NULL;
1173 PyObject* value_errors = NULL;
1155 1174
1156 1175 static char* kw_list[] = {
1157 1176 "fp", "mutable", "value_encoding", "value_errors", NULL};
@@ -1159,7 +1178,7 b' static PyObject* bser_load(PyObject* sel'
1159 1178 if (!PyArg_ParseTupleAndKeywords(
1160 1179 args,
1161 1180 kw,
1162 "OOzz:load",
1181 "O|OOO:load",
1163 1182 kw_list,
1164 1183 &fp,
1165 1184 &mutable_obj,
@@ -1172,8 +1191,33 b' static PyObject* bser_load(PyObject* sel'
1172 1191 if (load == NULL) {
1173 1192 return NULL;
1174 1193 }
1175 string = PyObject_CallMethod(
1176 load, "load", "OOzz", fp, mutable_obj, value_encoding, value_errors);
1194 load_method = PyObject_GetAttrString(load, "load");
1195 if (load_method == NULL) {
1196 return NULL;
1197 }
1198 // Mandatory method arguments
1199 load_method_args = Py_BuildValue("(O)", fp);
1200 if (load_method_args == NULL) {
1201 return NULL;
1202 }
1203 // Optional method arguments
1204 load_method_kwargs = PyDict_New();
1205 if (load_method_kwargs == NULL) {
1206 return NULL;
1207 }
1208 if (mutable_obj) {
1209 PyDict_SetItemString(load_method_kwargs, "mutable", mutable_obj);
1210 }
1211 if (value_encoding) {
1212 PyDict_SetItemString(load_method_kwargs, "value_encoding", value_encoding);
1213 }
1214 if (value_errors) {
1215 PyDict_SetItemString(load_method_kwargs, "value_errors", value_errors);
1216 }
1217 string = PyObject_Call(load_method, load_method_args, load_method_kwargs);
1218 Py_DECREF(load_method_kwargs);
1219 Py_DECREF(load_method_args);
1220 Py_DECREF(load_method);
1177 1221 Py_DECREF(load);
1178 1222 return string;
1179 1223 }
@@ -26,20 +26,20 b''
26 26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 28
29 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 29 # no unicode literals
30 from __future__ import absolute_import, division, print_function
33 31
34 32 import re
35 33
34
36 35 def parse_version(vstr):
37 36 res = 0
38 for n in vstr.split('.'):
37 for n in vstr.split("."):
39 38 res = res * 1000
40 39 res = res + int(n)
41 40 return res
42 41
42
43 43 cap_versions = {
44 44 "cmd-watch-del-all": "3.1.1",
45 45 "cmd-watch-project": "3.1",
@@ -49,23 +49,29 b' cap_versions = {'
49 49 "wildmatch": "3.7",
50 50 }
51 51
52
52 53 def check(version, name):
53 54 if name in cap_versions:
54 55 return version >= parse_version(cap_versions[name])
55 56 return False
56 57
58
57 59 def synthesize(vers, opts):
58 60 """ Synthesize a capability enabled version response
59 61 This is a very limited emulation for relatively recent feature sets
60 62 """
61 parsed_version = parse_version(vers['version'])
62 vers['capabilities'] = {}
63 for name in opts['optional']:
64 vers['capabilities'][name] = check(parsed_version, name)
65 for name in opts['required']:
63 parsed_version = parse_version(vers["version"])
64 vers["capabilities"] = {}
65 for name in opts["optional"]:
66 vers["capabilities"][name] = check(parsed_version, name)
67 failed = False # noqa: F841 T25377293 Grandfathered in
68 for name in opts["required"]:
66 69 have = check(parsed_version, name)
67 vers['capabilities'][name] = have
70 vers["capabilities"][name] = have
68 71 if not have:
69 vers['error'] = 'client required capability `' + name + \
70 '` is not supported by this server'
72 vers["error"] = (
73 "client required capability `"
74 + name
75 + "` is not supported by this server"
76 )
71 77 return vers
@@ -26,20 +26,22 b''
26 26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 28
29 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 29 # no unicode literals
33
34 '''Compatibility module across Python 2 and 3.'''
30 from __future__ import absolute_import, division, print_function
35 31
36 32 import sys
37 33
34
35 """Compatibility module across Python 2 and 3."""
36
37
38 PYTHON2 = sys.version_info < (3, 0)
38 39 PYTHON3 = sys.version_info >= (3, 0)
39 40
40 41 # This is adapted from https://bitbucket.org/gutworth/six, and used under the
41 42 # MIT license. See LICENSE for a full copyright notice.
42 43 if PYTHON3:
44
43 45 def reraise(tp, value, tb=None):
44 46 try:
45 47 if value is None:
@@ -50,16 +52,20 b' if PYTHON3:'
50 52 finally:
51 53 value = None
52 54 tb = None
55
56
53 57 else:
54 exec('''
58 exec(
59 """
55 60 def reraise(tp, value, tb=None):
56 61 try:
57 62 raise tp, value, tb
58 63 finally:
59 64 tb = None
60 '''.strip())
65 """.strip()
66 )
61 67
62 68 if PYTHON3:
63 69 UNICODE = str
64 70 else:
65 UNICODE = unicode
71 UNICODE = unicode # noqa: F821 We handled versioning above
@@ -26,48 +26,50 b''
26 26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 28
29 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 29 # no unicode literals
33
34 '''Module to deal with filename encoding on the local system, as returned by
35 Watchman.'''
30 from __future__ import absolute_import, division, print_function
36 31
37 32 import sys
38 33
39 from . import (
40 compat,
41 )
34 from . import compat
35
36
37 """Module to deal with filename encoding on the local system, as returned by
38 Watchman."""
39
42 40
43 41 if compat.PYTHON3:
44 default_local_errors = 'surrogateescape'
42 default_local_errors = "surrogateescape"
45 43
46 44 def get_local_encoding():
47 if sys.platform == 'win32':
45 if sys.platform == "win32":
48 46 # Watchman always returns UTF-8 encoded strings on Windows.
49 return 'utf-8'
47 return "utf-8"
50 48 # On the Python 3 versions we support, sys.getfilesystemencoding never
51 49 # returns None.
52 50 return sys.getfilesystemencoding()
51
52
53 53 else:
54 54 # Python 2 doesn't support surrogateescape, so use 'strict' by
55 55 # default. Users can register a custom surrogateescape error handler and use
56 56 # that if they so desire.
57 default_local_errors = 'strict'
57 default_local_errors = "strict"
58 58
59 59 def get_local_encoding():
60 if sys.platform == 'win32':
60 if sys.platform == "win32":
61 61 # Watchman always returns UTF-8 encoded strings on Windows.
62 return 'utf-8'
62 return "utf-8"
63 63 fsencoding = sys.getfilesystemencoding()
64 64 if fsencoding is None:
65 65 # This is very unlikely to happen, but if it does, just use UTF-8
66 fsencoding = 'utf-8'
66 fsencoding = "utf-8"
67 67 return fsencoding
68 68
69
69 70 def encode_local(s):
70 71 return s.encode(get_local_encoding(), default_local_errors)
71 72
73
72 74 def decode_local(bs):
73 75 return bs.decode(get_local_encoding(), default_local_errors)
@@ -26,17 +26,17 b''
26 26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 28
29 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 29 # no unicode literals
30 from __future__ import absolute_import, division, print_function
31
32 import ctypes
33
33 34
34 35 try:
35 36 from . import bser
36 37 except ImportError:
37 38 from . import pybser as bser
38 39
39 import ctypes
40 40
41 41 EMPTY_HEADER = b"\x00\x01\x05\x00\x00\x00\x00"
42 42
@@ -95,13 +95,15 b' def load(fp, mutable=True, value_encodin'
95 95 ctypes.resize(buf, total_len)
96 96
97 97 body = (ctypes.c_char * (total_len - len(header))).from_buffer(
98 buf, len(header))
98 buf, len(header)
99 )
99 100 read_len = _read_bytes(fp, body)
100 101 if read_len < len(body):
101 raise RuntimeError('bser data ended early')
102 raise RuntimeError("bser data ended early")
102 103
103 104 return bser.loads(
104 105 (ctypes.c_char * total_len).from_buffer(buf, 0),
105 106 mutable,
106 107 value_encoding,
107 value_errors)
108 value_errors,
109 )
@@ -26,10 +26,8 b''
26 26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 28
29 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 29 # no unicode literals
30 from __future__ import absolute_import, division, print_function
33 31
34 32 import binascii
35 33 import collections
@@ -37,30 +35,31 b' import ctypes'
37 35 import struct
38 36 import sys
39 37
40 from . import (
41 compat,
42 )
38 from . import compat
39
43 40
44 BSER_ARRAY = b'\x00'
45 BSER_OBJECT = b'\x01'
46 BSER_BYTESTRING = b'\x02'
47 BSER_INT8 = b'\x03'
48 BSER_INT16 = b'\x04'
49 BSER_INT32 = b'\x05'
50 BSER_INT64 = b'\x06'
51 BSER_REAL = b'\x07'
52 BSER_TRUE = b'\x08'
53 BSER_FALSE = b'\x09'
54 BSER_NULL = b'\x0a'
55 BSER_TEMPLATE = b'\x0b'
56 BSER_SKIP = b'\x0c'
57 BSER_UTF8STRING = b'\x0d'
41 BSER_ARRAY = b"\x00"
42 BSER_OBJECT = b"\x01"
43 BSER_BYTESTRING = b"\x02"
44 BSER_INT8 = b"\x03"
45 BSER_INT16 = b"\x04"
46 BSER_INT32 = b"\x05"
47 BSER_INT64 = b"\x06"
48 BSER_REAL = b"\x07"
49 BSER_TRUE = b"\x08"
50 BSER_FALSE = b"\x09"
51 BSER_NULL = b"\x0a"
52 BSER_TEMPLATE = b"\x0b"
53 BSER_SKIP = b"\x0c"
54 BSER_UTF8STRING = b"\x0d"
58 55
59 56 if compat.PYTHON3:
60 57 STRING_TYPES = (str, bytes)
61 58 unicode = str
59
62 60 def tobytes(i):
63 return str(i).encode('ascii')
61 return str(i).encode("ascii")
62
64 63 long = int
65 64 else:
66 65 STRING_TYPES = (unicode, str)
@@ -72,6 +71,7 b' else:'
72 71 EMPTY_HEADER = b"\x00\x01\x05\x00\x00\x00\x00"
73 72 EMPTY_HEADER_V2 = b"\x00\x02\x00\x00\x00\x00\x05\x00\x00\x00\x00"
74 73
74
75 75 def _int_size(x):
76 76 """Return the smallest size int that can store the value"""
77 77 if -0x80 <= x <= 0x7F:
@@ -83,29 +83,34 b' def _int_size(x):'
83 83 elif long(-0x8000000000000000) <= x <= long(0x7FFFFFFFFFFFFFFF):
84 84 return 8
85 85 else:
86 raise RuntimeError('Cannot represent value: ' + str(x))
86 raise RuntimeError("Cannot represent value: " + str(x))
87
87 88
88 89 def _buf_pos(buf, pos):
89 90 ret = buf[pos]
90 # In Python 2, buf is a str array so buf[pos] is a string. In Python 3, buf
91 # is a bytes array and buf[pos] is an integer.
92 if compat.PYTHON3:
91 # Normalize the return type to bytes
92 if compat.PYTHON3 and not isinstance(ret, bytes):
93 93 ret = bytes((ret,))
94 94 return ret
95 95
96
96 97 class _bser_buffer(object):
97
98 98 def __init__(self, version):
99 99 self.bser_version = version
100 100 self.buf = ctypes.create_string_buffer(8192)
101 101 if self.bser_version == 1:
102 struct.pack_into(tobytes(len(EMPTY_HEADER)) + b's', self.buf, 0,
103 EMPTY_HEADER)
102 struct.pack_into(
103 tobytes(len(EMPTY_HEADER)) + b"s", self.buf, 0, EMPTY_HEADER
104 )
104 105 self.wpos = len(EMPTY_HEADER)
105 106 else:
106 107 assert self.bser_version == 2
107 struct.pack_into(tobytes(len(EMPTY_HEADER_V2)) + b's', self.buf, 0,
108 EMPTY_HEADER_V2)
108 struct.pack_into(
109 tobytes(len(EMPTY_HEADER_V2)) + b"s",
110 self.buf,
111 0,
112 EMPTY_HEADER_V2,
113 )
109 114 self.wpos = len(EMPTY_HEADER_V2)
110 115
111 116 def ensure_size(self, size):
@@ -117,42 +122,68 b' class _bser_buffer(object):'
117 122 to_write = size + 1
118 123 self.ensure_size(to_write)
119 124 if size == 1:
120 struct.pack_into(b'=cb', self.buf, self.wpos, BSER_INT8, val)
125 struct.pack_into(b"=cb", self.buf, self.wpos, BSER_INT8, val)
121 126 elif size == 2:
122 struct.pack_into(b'=ch', self.buf, self.wpos, BSER_INT16, val)
127 struct.pack_into(b"=ch", self.buf, self.wpos, BSER_INT16, val)
123 128 elif size == 4:
124 struct.pack_into(b'=ci', self.buf, self.wpos, BSER_INT32, val)
129 struct.pack_into(b"=ci", self.buf, self.wpos, BSER_INT32, val)
125 130 elif size == 8:
126 struct.pack_into(b'=cq', self.buf, self.wpos, BSER_INT64, val)
131 struct.pack_into(b"=cq", self.buf, self.wpos, BSER_INT64, val)
127 132 else:
128 raise RuntimeError('Cannot represent this long value')
133 raise RuntimeError("Cannot represent this long value")
129 134 self.wpos += to_write
130 135
131
132 136 def append_string(self, s):
133 137 if isinstance(s, unicode):
134 s = s.encode('utf-8')
138 s = s.encode("utf-8")
135 139 s_len = len(s)
136 140 size = _int_size(s_len)
137 141 to_write = 2 + size + s_len
138 142 self.ensure_size(to_write)
139 143 if size == 1:
140 struct.pack_into(b'=ccb' + tobytes(s_len) + b's', self.buf,
141 self.wpos, BSER_BYTESTRING, BSER_INT8, s_len, s)
144 struct.pack_into(
145 b"=ccb" + tobytes(s_len) + b"s",
146 self.buf,
147 self.wpos,
148 BSER_BYTESTRING,
149 BSER_INT8,
150 s_len,
151 s,
152 )
142 153 elif size == 2:
143 struct.pack_into(b'=cch' + tobytes(s_len) + b's', self.buf,
144 self.wpos, BSER_BYTESTRING, BSER_INT16, s_len, s)
154 struct.pack_into(
155 b"=cch" + tobytes(s_len) + b"s",
156 self.buf,
157 self.wpos,
158 BSER_BYTESTRING,
159 BSER_INT16,
160 s_len,
161 s,
162 )
145 163 elif size == 4:
146 struct.pack_into(b'=cci' + tobytes(s_len) + b's', self.buf,
147 self.wpos, BSER_BYTESTRING, BSER_INT32, s_len, s)
164 struct.pack_into(
165 b"=cci" + tobytes(s_len) + b"s",
166 self.buf,
167 self.wpos,
168 BSER_BYTESTRING,
169 BSER_INT32,
170 s_len,
171 s,
172 )
148 173 elif size == 8:
149 struct.pack_into(b'=ccq' + tobytes(s_len) + b's', self.buf,
150 self.wpos, BSER_BYTESTRING, BSER_INT64, s_len, s)
174 struct.pack_into(
175 b"=ccq" + tobytes(s_len) + b"s",
176 self.buf,
177 self.wpos,
178 BSER_BYTESTRING,
179 BSER_INT64,
180 s_len,
181 s,
182 )
151 183 else:
152 raise RuntimeError('Cannot represent this string value')
184 raise RuntimeError("Cannot represent this string value")
153 185 self.wpos += to_write
154 186
155
156 187 def append_recursive(self, val):
157 188 if isinstance(val, bool):
158 189 needed = 1
@@ -161,12 +192,12 b' class _bser_buffer(object):'
161 192 to_encode = BSER_TRUE
162 193 else:
163 194 to_encode = BSER_FALSE
164 struct.pack_into(b'=c', self.buf, self.wpos, to_encode)
195 struct.pack_into(b"=c", self.buf, self.wpos, to_encode)
165 196 self.wpos += needed
166 197 elif val is None:
167 198 needed = 1
168 199 self.ensure_size(needed)
169 struct.pack_into(b'=c', self.buf, self.wpos, BSER_NULL)
200 struct.pack_into(b"=c", self.buf, self.wpos, BSER_NULL)
170 201 self.wpos += needed
171 202 elif isinstance(val, (int, long)):
172 203 self.append_long(val)
@@ -175,61 +206,106 b' class _bser_buffer(object):'
175 206 elif isinstance(val, float):
176 207 needed = 9
177 208 self.ensure_size(needed)
178 struct.pack_into(b'=cd', self.buf, self.wpos, BSER_REAL, val)
209 struct.pack_into(b"=cd", self.buf, self.wpos, BSER_REAL, val)
179 210 self.wpos += needed
180 elif isinstance(val, collections.Mapping) and \
181 isinstance(val, collections.Sized):
211 elif isinstance(val, collections.Mapping) and isinstance(
212 val, collections.Sized
213 ):
182 214 val_len = len(val)
183 215 size = _int_size(val_len)
184 216 needed = 2 + size
185 217 self.ensure_size(needed)
186 218 if size == 1:
187 struct.pack_into(b'=ccb', self.buf, self.wpos, BSER_OBJECT,
188 BSER_INT8, val_len)
219 struct.pack_into(
220 b"=ccb",
221 self.buf,
222 self.wpos,
223 BSER_OBJECT,
224 BSER_INT8,
225 val_len,
226 )
189 227 elif size == 2:
190 struct.pack_into(b'=cch', self.buf, self.wpos, BSER_OBJECT,
191 BSER_INT16, val_len)
228 struct.pack_into(
229 b"=cch",
230 self.buf,
231 self.wpos,
232 BSER_OBJECT,
233 BSER_INT16,
234 val_len,
235 )
192 236 elif size == 4:
193 struct.pack_into(b'=cci', self.buf, self.wpos, BSER_OBJECT,
194 BSER_INT32, val_len)
237 struct.pack_into(
238 b"=cci",
239 self.buf,
240 self.wpos,
241 BSER_OBJECT,
242 BSER_INT32,
243 val_len,
244 )
195 245 elif size == 8:
196 struct.pack_into(b'=ccq', self.buf, self.wpos, BSER_OBJECT,
197 BSER_INT64, val_len)
246 struct.pack_into(
247 b"=ccq",
248 self.buf,
249 self.wpos,
250 BSER_OBJECT,
251 BSER_INT64,
252 val_len,
253 )
198 254 else:
199 raise RuntimeError('Cannot represent this mapping value')
255 raise RuntimeError("Cannot represent this mapping value")
200 256 self.wpos += needed
201 257 if compat.PYTHON3:
202 258 iteritems = val.items()
203 259 else:
204 iteritems = val.iteritems()
260 iteritems = val.iteritems() # noqa: B301 Checked version above
205 261 for k, v in iteritems:
206 262 self.append_string(k)
207 263 self.append_recursive(v)
208 elif isinstance(val, collections.Iterable) and \
209 isinstance(val, collections.Sized):
264 elif isinstance(val, collections.Iterable) and isinstance(
265 val, collections.Sized
266 ):
210 267 val_len = len(val)
211 268 size = _int_size(val_len)
212 269 needed = 2 + size
213 270 self.ensure_size(needed)
214 271 if size == 1:
215 struct.pack_into(b'=ccb', self.buf, self.wpos, BSER_ARRAY,
216 BSER_INT8, val_len)
272 struct.pack_into(
273 b"=ccb", self.buf, self.wpos, BSER_ARRAY, BSER_INT8, val_len
274 )
217 275 elif size == 2:
218 struct.pack_into(b'=cch', self.buf, self.wpos, BSER_ARRAY,
219 BSER_INT16, val_len)
276 struct.pack_into(
277 b"=cch",
278 self.buf,
279 self.wpos,
280 BSER_ARRAY,
281 BSER_INT16,
282 val_len,
283 )
220 284 elif size == 4:
221 struct.pack_into(b'=cci', self.buf, self.wpos, BSER_ARRAY,
222 BSER_INT32, val_len)
285 struct.pack_into(
286 b"=cci",
287 self.buf,
288 self.wpos,
289 BSER_ARRAY,
290 BSER_INT32,
291 val_len,
292 )
223 293 elif size == 8:
224 struct.pack_into(b'=ccq', self.buf, self.wpos, BSER_ARRAY,
225 BSER_INT64, val_len)
294 struct.pack_into(
295 b"=ccq",
296 self.buf,
297 self.wpos,
298 BSER_ARRAY,
299 BSER_INT64,
300 val_len,
301 )
226 302 else:
227 raise RuntimeError('Cannot represent this sequence value')
303 raise RuntimeError("Cannot represent this sequence value")
228 304 self.wpos += needed
229 305 for v in val:
230 306 self.append_recursive(v)
231 307 else:
232 raise RuntimeError('Cannot represent unknown value type')
308 raise RuntimeError("Cannot represent unknown value type")
233 309
234 310
235 311 def dumps(obj, version=1, capabilities=0):
@@ -238,18 +314,19 b' def dumps(obj, version=1, capabilities=0'
238 314 # Now fill in the overall length
239 315 if version == 1:
240 316 obj_len = bser_buf.wpos - len(EMPTY_HEADER)
241 struct.pack_into(b'=i', bser_buf.buf, 3, obj_len)
317 struct.pack_into(b"=i", bser_buf.buf, 3, obj_len)
242 318 else:
243 319 obj_len = bser_buf.wpos - len(EMPTY_HEADER_V2)
244 struct.pack_into(b'=i', bser_buf.buf, 2, capabilities)
245 struct.pack_into(b'=i', bser_buf.buf, 7, obj_len)
246 return bser_buf.buf.raw[:bser_buf.wpos]
320 struct.pack_into(b"=i", bser_buf.buf, 2, capabilities)
321 struct.pack_into(b"=i", bser_buf.buf, 7, obj_len)
322 return bser_buf.buf.raw[: bser_buf.wpos]
323
247 324
248 325 # This is a quack-alike with the bserObjectType in bser.c
249 326 # It provides by getattr accessors and getitem for both index
250 327 # and name.
251 328 class _BunserDict(object):
252 __slots__ = ('_keys', '_values')
329 __slots__ = ("_keys", "_values")
253 330
254 331 def __init__(self, keys, values):
255 332 self._keys = keys
@@ -261,18 +338,19 b' class _BunserDict(object):'
261 338 def __getitem__(self, key):
262 339 if isinstance(key, (int, long)):
263 340 return self._values[key]
264 elif key.startswith('st_'):
341 elif key.startswith("st_"):
265 342 # hack^Wfeature to allow mercurial to use "st_size" to
266 343 # reference "size"
267 344 key = key[3:]
268 345 try:
269 346 return self._values[self._keys.index(key)]
270 347 except ValueError:
271 raise KeyError('_BunserDict has no key %s' % key)
348 raise KeyError("_BunserDict has no key %s" % key)
272 349
273 350 def __len__(self):
274 351 return len(self._keys)
275 352
353
276 354 class Bunser(object):
277 355 def __init__(self, mutable=True, value_encoding=None, value_errors=None):
278 356 self.mutable = mutable
@@ -281,7 +359,7 b' class Bunser(object):'
281 359 if value_encoding is None:
282 360 self.value_errors = None
283 361 elif value_errors is None:
284 self.value_errors = 'strict'
362 self.value_errors = "strict"
285 363 else:
286 364 self.value_errors = value_errors
287 365
@@ -290,33 +368,35 b' class Bunser(object):'
290 368 try:
291 369 int_type = _buf_pos(buf, pos)
292 370 except IndexError:
293 raise ValueError('Invalid bser int encoding, pos out of range')
371 raise ValueError("Invalid bser int encoding, pos out of range")
294 372 if int_type == BSER_INT8:
295 373 needed = 2
296 fmt = b'=b'
374 fmt = b"=b"
297 375 elif int_type == BSER_INT16:
298 376 needed = 3
299 fmt = b'=h'
377 fmt = b"=h"
300 378 elif int_type == BSER_INT32:
301 379 needed = 5
302 fmt = b'=i'
380 fmt = b"=i"
303 381 elif int_type == BSER_INT64:
304 382 needed = 9
305 fmt = b'=q'
383 fmt = b"=q"
306 384 else:
307 raise ValueError('Invalid bser int encoding 0x%s' %
308 binascii.hexlify(int_type).decode('ascii'))
385 raise ValueError(
386 "Invalid bser int encoding 0x%s at position %s"
387 % (binascii.hexlify(int_type).decode("ascii"), pos)
388 )
309 389 int_val = struct.unpack_from(fmt, buf, pos + 1)[0]
310 390 return (int_val, pos + needed)
311 391
312 392 def unser_utf8_string(self, buf, pos):
313 393 str_len, pos = self.unser_int(buf, pos + 1)
314 str_val = struct.unpack_from(tobytes(str_len) + b's', buf, pos)[0]
315 return (str_val.decode('utf-8'), pos + str_len)
394 str_val = struct.unpack_from(tobytes(str_len) + b"s", buf, pos)[0]
395 return (str_val.decode("utf-8"), pos + str_len)
316 396
317 397 def unser_bytestring(self, buf, pos):
318 398 str_len, pos = self.unser_int(buf, pos + 1)
319 str_val = struct.unpack_from(tobytes(str_len) + b's', buf, pos)[0]
399 str_val = struct.unpack_from(tobytes(str_len) + b"s", buf, pos)[0]
320 400 if self.value_encoding is not None:
321 401 str_val = str_val.decode(self.value_encoding, self.value_errors)
322 402 # str_len stays the same because that's the length in bytes
@@ -325,12 +405,12 b' class Bunser(object):'
325 405 def unser_array(self, buf, pos):
326 406 arr_len, pos = self.unser_int(buf, pos + 1)
327 407 arr = []
328 for i in range(arr_len):
408 for _ in range(arr_len):
329 409 arr_item, pos = self.loads_recursive(buf, pos)
330 410 arr.append(arr_item)
331 411
332 412 if not self.mutable:
333 arr = tuple(arr)
413 arr = tuple(arr)
334 414
335 415 return arr, pos
336 416
@@ -342,7 +422,7 b' class Bunser(object):'
342 422 keys = []
343 423 vals = []
344 424
345 for i in range(obj_len):
425 for _ in range(obj_len):
346 426 key, pos = self.unser_utf8_string(buf, pos)
347 427 val, pos = self.loads_recursive(buf, pos)
348 428 if self.mutable:
@@ -359,13 +439,13 b' class Bunser(object):'
359 439 def unser_template(self, buf, pos):
360 440 val_type = _buf_pos(buf, pos + 1)
361 441 if val_type != BSER_ARRAY:
362 raise RuntimeError('Expect ARRAY to follow TEMPLATE')
442 raise RuntimeError("Expect ARRAY to follow TEMPLATE")
363 443 # force UTF-8 on keys
364 keys_bunser = Bunser(mutable=self.mutable, value_encoding='utf-8')
444 keys_bunser = Bunser(mutable=self.mutable, value_encoding="utf-8")
365 445 keys, pos = keys_bunser.unser_array(buf, pos + 1)
366 446 nitems, pos = self.unser_int(buf, pos)
367 447 arr = []
368 for i in range(nitems):
448 for _ in range(nitems):
369 449 if self.mutable:
370 450 obj = {}
371 451 else:
@@ -392,11 +472,15 b' class Bunser(object):'
392 472
393 473 def loads_recursive(self, buf, pos):
394 474 val_type = _buf_pos(buf, pos)
395 if (val_type == BSER_INT8 or val_type == BSER_INT16 or
396 val_type == BSER_INT32 or val_type == BSER_INT64):
475 if (
476 val_type == BSER_INT8
477 or val_type == BSER_INT16
478 or val_type == BSER_INT32
479 or val_type == BSER_INT64
480 ):
397 481 return self.unser_int(buf, pos)
398 482 elif val_type == BSER_REAL:
399 val = struct.unpack_from(b'=d', buf, pos + 1)[0]
483 val = struct.unpack_from(b"=d", buf, pos + 1)[0]
400 484 return (val, pos + 9)
401 485 elif val_type == BSER_TRUE:
402 486 return (True, pos + 1)
@@ -415,23 +499,26 b' class Bunser(object):'
415 499 elif val_type == BSER_TEMPLATE:
416 500 return self.unser_template(buf, pos)
417 501 else:
418 raise ValueError('unhandled bser opcode 0x%s' %
419 binascii.hexlify(val_type).decode('ascii'))
502 raise ValueError(
503 "unhandled bser opcode 0x%s"
504 % binascii.hexlify(val_type).decode("ascii")
505 )
420 506
421 507
422 508 def _pdu_info_helper(buf):
509 bser_version = -1
423 510 if buf[0:2] == EMPTY_HEADER[0:2]:
424 511 bser_version = 1
425 512 bser_capabilities = 0
426 513 expected_len, pos2 = Bunser.unser_int(buf, 2)
427 514 elif buf[0:2] == EMPTY_HEADER_V2[0:2]:
428 515 if len(buf) < 8:
429 raise ValueError('Invalid BSER header')
516 raise ValueError("Invalid BSER header")
430 517 bser_version = 2
431 518 bser_capabilities = struct.unpack_from("I", buf, 2)[0]
432 519 expected_len, pos2 = Bunser.unser_int(buf, 6)
433 520 else:
434 raise ValueError('Invalid BSER header')
521 raise ValueError("Invalid BSER header")
435 522
436 523 return bser_version, bser_capabilities, expected_len, pos2
437 524
@@ -470,14 +557,20 b' def loads(buf, mutable=True, value_encod'
470 557 pos = info[3]
471 558
472 559 if len(buf) != expected_len + pos:
473 raise ValueError('bser data len != header len')
560 raise ValueError(
561 "bser data len %d != header len %d" % (expected_len + pos, len(buf))
562 )
474 563
475 bunser = Bunser(mutable=mutable, value_encoding=value_encoding,
476 value_errors=value_errors)
564 bunser = Bunser(
565 mutable=mutable,
566 value_encoding=value_encoding,
567 value_errors=value_errors,
568 )
477 569
478 570 return bunser.loads_recursive(buf, pos)[0]
479 571
480 572
481 573 def load(fp, mutable=True, value_encoding=None, value_errors=None):
482 574 from . import load
575
483 576 return load.load(fp, mutable, value_encoding, value_errors)
@@ -14,6 +14,7 b' import struct'
14 14
15 15 from mercurial.i18n import _
16 16 from mercurial import (
17 encoding,
17 18 pathutil,
18 19 util,
19 20 )
@@ -81,7 +82,7 b' class state(object):'
81 82 self.invalidate()
82 83 return None, None, None
83 84 diskhostname = state[0]
84 hostname = socket.gethostname()
85 hostname = encoding.strtolocal(socket.gethostname())
85 86 if diskhostname != hostname:
86 87 # file got moved to a different host
87 88 self._ui.log(
@@ -127,7 +128,7 b' class state(object):'
127 128
128 129 with file:
129 130 file.write(struct.pack(_versionformat, _version))
130 file.write(socket.gethostname() + b'\0')
131 file.write(encoding.strtolocal(socket.gethostname()) + b'\0')
131 132 file.write(clock + b'\0')
132 133 file.write(ignorehash + b'\0')
133 134 if notefiles:
@@ -9,7 +9,14 b' from __future__ import absolute_import'
9 9
10 10 import getpass
11 11
12 from mercurial import util
12 from mercurial import (
13 encoding,
14 util,
15 )
16 from mercurial.utils import (
17 procutil,
18 stringutil,
19 )
13 20
14 21 from . import pywatchman
15 22
@@ -22,12 +29,14 b' class Unavailable(Exception):'
22 29 self.warn = False
23 30 self.invalidate = invalidate
24 31
25 def __str__(self):
32 def __bytes__(self):
26 33 if self.warn:
27 34 return b'warning: Watchman unavailable: %s' % self.msg
28 35 else:
29 36 return b'Watchman unavailable: %s' % self.msg
30 37
38 __str__ = encoding.strmethod(__bytes__)
39
31 40
32 41 class WatchmanNoRoot(Unavailable):
33 42 def __init__(self, root, msg):
@@ -92,15 +101,17 b' class client(object):'
92 101 self._watchmanclient = pywatchman.client(
93 102 timeout=self._timeout,
94 103 useImmutableBser=True,
95 watchman_exe=watchman_exe,
104 binpath=procutil.tonativestr(watchman_exe),
96 105 )
97 106 return self._watchmanclient.query(*watchmanargs)
98 107 except pywatchman.CommandError as ex:
99 108 if b'unable to resolve root' in ex.msg:
100 raise WatchmanNoRoot(self._root, ex.msg)
109 raise WatchmanNoRoot(
110 self._root, stringutil.forcebytestr(ex.msg)
111 )
101 112 raise Unavailable(ex.msg)
102 113 except pywatchman.WatchmanError as ex:
103 raise Unavailable(str(ex))
114 raise Unavailable(stringutil.forcebytestr(ex))
104 115
105 116 def command(self, *args):
106 117 try:
@@ -76,10 +76,9 b' class gpg(object):'
76 76 fp = os.fdopen(fd, r'wb')
77 77 fp.write(data)
78 78 fp.close()
79 gpgcmd = b"%s --logger-fd 1 --status-fd 1 --verify \"%s\" \"%s\"" % (
80 self.path,
81 sigfile,
82 datafile,
79 gpgcmd = (
80 b"%s --logger-fd 1 --status-fd 1 --verify \"%s\" \"%s\""
81 % (self.path, sigfile, datafile,)
83 82 )
84 83 ret = procutil.filter(b"", gpgcmd)
85 84 finally:
@@ -217,6 +217,7 b' from mercurial import ('
217 217 copies,
218 218 destutil,
219 219 discovery,
220 encoding,
220 221 error,
221 222 exchange,
222 223 extensions,
@@ -1117,7 +1118,7 b' class histeditrule(object):'
1117 1118 self.pos = pos
1118 1119 self.conflicts = []
1119 1120
1120 def __str__(self):
1121 def __bytes__(self):
1121 1122 # Some actions ('fold' and 'roll') combine a patch with a previous one.
1122 1123 # Add a marker showing which patch they apply to, and also omit the
1123 1124 # description for 'roll' (since it will get discarded). Example display:
@@ -1135,10 +1136,16 b' class histeditrule(object):'
1135 1136 desc = self.ctx.description().splitlines()[0].strip()
1136 1137 if self.action == b'roll':
1137 1138 desc = b''
1138 return b"#{0:<2} {1:<6} {2}:{3} {4}".format(
1139 self.origpos, action, r, h, desc
1139 return b"#%s %s %d:%s %s" % (
1140 (b'%d' % self.origpos).ljust(2),
1141 action.ljust(6),
1142 r,
1143 h,
1144 desc,
1140 1145 )
1141 1146
1147 __str__ = encoding.strmethod(__bytes__)
1148
1142 1149 def checkconflicts(self, other):
1143 1150 if other.pos > self.pos and other.origpos <= self.origpos:
1144 1151 if set(other.ctx.files()) & set(self.ctx.files()) != set():
@@ -1315,7 +1322,7 b' def makecommands(rules):'
1315 1322 our list of rules"""
1316 1323 commands = []
1317 1324 for rules in rules:
1318 commands.append(b"{0} {1}\n".format(rules.action, rules.ctx))
1325 commands.append(b'%s %s\n' % (rules.action, rules.ctx))
1319 1326 return commands
1320 1327
1321 1328
@@ -1324,7 +1331,7 b' def addln(win, y, x, line, color=None):'
1324 1331 whitespace characters, so that the color appears on the whole line"""
1325 1332 maxy, maxx = win.getmaxyx()
1326 1333 length = maxx - 1 - x
1327 line = (b"{0:<%d}" % length).format(str(line).strip())[:length]
1334 line = bytes(line).ljust(length)[:length]
1328 1335 if y < 0:
1329 1336 y = maxy + y
1330 1337 if x < 0:
@@ -1395,17 +1402,17 b' def _chisteditmain(repo, rules, stdscr):'
1395 1402 maxy, maxx = win.getmaxyx()
1396 1403 length = maxx - 3
1397 1404
1398 line = b"changeset: {0}:{1:<12}".format(ctx.rev(), ctx)
1405 line = b"changeset: %d:%s" % (ctx.rev(), ctx.hex()[:12])
1399 1406 win.addstr(1, 1, line[:length])
1400 1407
1401 line = b"user: {0}".format(ctx.user())
1408 line = b"user: %s" % ctx.user()
1402 1409 win.addstr(2, 1, line[:length])
1403 1410
1404 1411 bms = repo.nodebookmarks(ctx.node())
1405 line = b"bookmark: {0}".format(b' '.join(bms))
1412 line = b"bookmark: %s" % b' '.join(bms)
1406 1413 win.addstr(3, 1, line[:length])
1407 1414
1408 line = b"summary: {0}".format(ctx.description().splitlines()[0])
1415 line = b"summary: %s" % (ctx.description().splitlines()[0])
1409 1416 win.addstr(4, 1, line[:length])
1410 1417
1411 1418 line = b"files: "
@@ -1425,8 +1432,8 b' def _chisteditmain(repo, rules, stdscr):'
1425 1432
1426 1433 conflicts = rule.conflicts
1427 1434 if len(conflicts) > 0:
1428 conflictstr = b','.join(map(lambda r: str(r.ctx), conflicts))
1429 conflictstr = b"changed files overlap with {0}".format(conflictstr)
1435 conflictstr = b','.join(map(lambda r: r.ctx.hex()[:12], conflicts))
1436 conflictstr = b"changed files overlap with %s" % conflictstr
1430 1437 else:
1431 1438 conflictstr = b'no overlap'
1432 1439
@@ -1464,7 +1471,9 b' pgup/K: move patch up, pgdn/J: move patc'
1464 1471
1465 1472 conflicts = [r.ctx for r in rules if r.conflicts]
1466 1473 if len(conflicts) > 0:
1467 line = b"potential conflict in %s" % b','.join(map(str, conflicts))
1474 line = b"potential conflict in %s" % b','.join(
1475 map(pycompat.bytestr, conflicts)
1476 )
1468 1477 addln(rulesscr, -1, 0, line, curses.color_pair(COLOR_WARN))
1469 1478
1470 1479 for y, rule in enumerate(rules[start:]):
@@ -1601,7 +1610,7 b' pgup/K: move patch up, pgdn/J: move patc'
1601 1610 renderhelp(helpwin, state)
1602 1611 curses.doupdate()
1603 1612 # done rendering
1604 ch = stdscr.getkey()
1613 ch = encoding.strtolocal(stdscr.getkey())
1605 1614 except curses.error:
1606 1615 pass
1607 1616
@@ -1675,11 +1684,10 b' def _chistedit(ui, repo, *freeargs, **op'
1675 1684 if type(rc) is list:
1676 1685 ui.status(_(b"performing changes\n"))
1677 1686 rules = makecommands(rc)
1678 filename = repo.vfs.join(b'chistedit')
1679 with open(filename, b'w+') as fp:
1687 with repo.vfs(b'chistedit', b'w+') as fp:
1680 1688 for r in rules:
1681 1689 fp.write(r)
1682 opts[b'commands'] = filename
1690 opts['commands'] = fp.name
1683 1691 return _texthistedit(ui, repo, *freeargs, **opts)
1684 1692 except KeyboardInterrupt:
1685 1693 pass
@@ -959,7 +959,7 b' def logservicecall(logger, service, **kw'
959 959 service,
960 960 eventtype=b'failure',
961 961 elapsedms=(time.time() - start) * 1000,
962 errormsg=str(e),
962 errormsg=stringutil.forcebytestr(e),
963 963 **kwargs
964 964 )
965 965 raise
@@ -1223,7 +1223,7 b' def storebundle(op, params, bundlefile):'
1223 1223 scratchbranchparttype,
1224 1224 eventtype=b'failure',
1225 1225 elapsedms=(time.time() - parthandlerstart) * 1000,
1226 errormsg=str(e),
1226 errormsg=stringutil.forcebytestr(e),
1227 1227 )
1228 1228 raise
1229 1229 finally:
@@ -363,7 +363,7 b' class _gitlfsremote(object):'
363 363 _(b'LFS error: %s') % _urlerrorreason(ex), hint=hint
364 364 )
365 365 try:
366 response = json.loads(rawjson)
366 response = pycompat.json_loads(rawjson)
367 367 except ValueError:
368 368 raise LfsRemoteError(
369 369 _(b'LFS server returns invalid JSON: %s')
@@ -133,7 +133,7 b' def _processbatchrequest(repo, req, res)'
133 133 return True
134 134
135 135 # XXX: specify an encoding?
136 lfsreq = json.loads(req.bodyfh.read())
136 lfsreq = pycompat.json_loads(req.bodyfh.read())
137 137
138 138 # If no transfer handlers are explicitly requested, 'basic' is assumed.
139 139 if r'basic' not in lfsreq.get(r'transfers', [r'basic']):
@@ -148,7 +148,7 b' web.baseurl'
148 148 from __future__ import absolute_import
149 149
150 150 import email.errors as emailerrors
151 import email.parser as emailparser
151 import email.utils as emailutils
152 152 import fnmatch
153 153 import hashlib
154 154 import socket
@@ -382,9 +382,8 b' class notifier(object):'
382 382 )
383 383 return
384 384
385 p = emailparser.Parser()
386 385 try:
387 msg = p.parsestr(encoding.strfromlocal(data))
386 msg = mail.parsebytes(data)
388 387 except emailerrors.MessageParseError as inst:
389 388 raise error.Abort(inst)
390 389
@@ -392,16 +391,16 b' class notifier(object):'
392 391 sender = msg[r'From']
393 392 subject = msg[r'Subject']
394 393 if sender is not None:
395 sender = encoding.strtolocal(sender)
394 sender = mail.headdecode(sender)
396 395 if subject is not None:
397 subject = encoding.strtolocal(subject)
396 subject = mail.headdecode(subject)
398 397 del msg[r'From'], msg[r'Subject']
399 398
400 399 if not msg.is_multipart():
401 400 # create fresh mime message from scratch
402 401 # (multipart templates must take care of this themselves)
403 402 headers = msg.items()
404 payload = msg.get_payload()
403 payload = msg.get_payload(decode=pycompat.ispy3)
405 404 # for notification prefer readability over data precision
406 405 msg = mail.mimeencode(self.ui, payload, self.charsets, self.test)
407 406 # reinstate custom headers
@@ -440,7 +439,7 b' class notifier(object):'
440 439 msg[r'Message-Id'] = messageid(ctx, self.domain, self.messageidseed)
441 440 msg[r'To'] = encoding.strfromlocal(b', '.join(sorted(subs)))
442 441
443 msgtext = encoding.strtolocal(msg.as_string())
442 msgtext = msg.as_bytes() if pycompat.ispy3 else msg.as_string()
444 443 if self.test:
445 444 self.ui.write(msgtext)
446 445 if not msgtext.endswith(b'\n'):
@@ -452,7 +451,7 b' class notifier(object):'
452 451 )
453 452 mail.sendmail(
454 453 self.ui,
455 stringutil.email(msg[r'From']),
454 emailutils.parseaddr(msg[r'From'])[1],
456 455 subs,
457 456 msgtext,
458 457 mbox=self.mbox,
@@ -960,7 +960,10 b' def email(ui, repo, *revs, **opts):'
960 960 hdr = pycompat.strurl(hdr)
961 961 change = True
962 962 if isinstance(val, bytes):
963 val = pycompat.strurl(val)
963 # header value should be ASCII since it's encoded by
964 # mail.headencode(), but -n/--test disables it and raw
965 # value of platform encoding is stored.
966 val = encoding.strfromlocal(val)
964 967 if not change:
965 968 # prevent duplicate headers
966 969 del m[hdr]
@@ -152,8 +152,8 b' def vcrcommand(name, flags, spec, helpca'
152 152 value = r1params[key][0]
153 153 # we want to compare json payloads without worrying about ordering
154 154 if value.startswith(b'{') and value.endswith(b'}'):
155 r1json = json.loads(value)
156 r2json = json.loads(r2params[key][0])
155 r1json = pycompat.json_loads(value)
156 r2json = pycompat.json_loads(r2params[key][0])
157 157 if r1json != r2json:
158 158 return False
159 159 elif r2params[key][0] != value:
@@ -307,7 +307,7 b' def callconduit(ui, name, params):'
307 307 if isinstance(x, pycompat.unicode)
308 308 else x,
309 309 # json.loads only accepts bytes from py3.6+
310 json.loads(encoding.unifromlocal(body)),
310 pycompat.json_loads(encoding.unifromlocal(body)),
311 311 )
312 312 if parsed.get(b'error_code'):
313 313 msg = _(b'Conduit Error (%s): %s') % (
@@ -332,7 +332,7 b' def debugcallconduit(ui, repo, name):'
332 332 lambda x: encoding.unitolocal(x)
333 333 if isinstance(x, pycompat.unicode)
334 334 else x,
335 json.loads(rawparams),
335 pycompat.json_loads(rawparams),
336 336 )
337 337 # json.dumps only accepts unicode strings
338 338 result = pycompat.rapply(
@@ -441,7 +441,7 b' def getoldnodedrevmap(repo, nodelist):'
441 441 )
442 442 unfi.ui.warn(
443 443 _(
444 b'D%s: local tag removed - does not match '
444 b'D%d: local tag removed - does not match '
445 445 b'Differential history\n'
446 446 )
447 447 % drev
@@ -1168,7 +1168,7 b' def phabsend(ui, repo, *revs, **opts):'
1168 1168 writediffproperties(unfi[newnode], diffmap[old.node()])
1169 1169 except util.urlerr.urlerror:
1170 1170 ui.warnnoi18n(
1171 b'Failed to update metadata for D%s\n' % drevid
1171 b'Failed to update metadata for D%d\n' % drevid
1172 1172 )
1173 1173 # Remove local tags since it's no longer necessary
1174 1174 tagname = b'D%d' % drevid
@@ -1208,7 +1208,7 b' def _confirmbeforesend(repo, revs, oldma'
1208 1208 desc = ctx.description().splitlines()[0]
1209 1209 oldnode, olddiff, drevid = oldmap.get(ctx.node(), (None, None, None))
1210 1210 if drevid:
1211 drevdesc = ui.label(b'D%s' % drevid, b'phabricator.drev')
1211 drevdesc = ui.label(b'D%d' % drevid, b'phabricator.drev')
1212 1212 else:
1213 1213 drevdesc = ui.label(_(b'NEW'), b'phabricator.drev')
1214 1214
@@ -1613,7 +1613,7 b' def phabupdate(ui, repo, spec, **opts):'
1613 1613
1614 1614 actions = []
1615 1615 for f in flags:
1616 actions.append({b'type': f, b'value': b'true'})
1616 actions.append({b'type': f, b'value': True})
1617 1617
1618 1618 drevs = querydrev(repo, spec)
1619 1619 for i, drev in enumerate(drevs):
@@ -2267,7 +2267,13 b' def config(ui, repo, *values, **opts):'
2267 2267 fm.write(b'value', b'%s\n', value)
2268 2268 else:
2269 2269 fm.write(b'name value', b'%s=%s\n', entryname, value)
2270 fm.data(defaultvalue=defaultvalue)
2270 if formatter.isprintable(defaultvalue):
2271 fm.data(defaultvalue=defaultvalue)
2272 elif isinstance(defaultvalue, list) and all(
2273 formatter.isprintable(e) for e in defaultvalue
2274 ):
2275 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2276 # TODO: no idea how to process unsupported defaultvalue types
2271 2277 matched = True
2272 2278 fm.end()
2273 2279 if matched:
@@ -212,11 +212,9 b' class config(object):'
212 212 def read(self, path, fp=None, sections=None, remap=None):
213 213 if not fp:
214 214 fp = util.posixfile(path, b'rb')
215 assert (
216 getattr(fp, 'mode', r'rb') == r'rb'
217 ), b'config files must be opened in binary mode, got fp=%r mode=%r' % (
218 fp,
219 fp.mode,
215 assert getattr(fp, 'mode', r'rb') == r'rb', (
216 b'config files must be opened in binary mode, got fp=%r mode=%r'
217 % (fp, fp.mode,)
220 218 )
221 219 self.parse(
222 220 path, fp.read(), sections=sections, remap=remap, include=self.read
@@ -1187,10 +1187,9 b' class filectx(basefilectx):'
1187 1187
1188 1188 assert (
1189 1189 changeid is not None or fileid is not None or changectx is not None
1190 ), b"bad args: changeid=%r, fileid=%r, changectx=%r" % (
1191 changeid,
1192 fileid,
1193 changectx,
1190 ), (
1191 b"bad args: changeid=%r, fileid=%r, changectx=%r"
1192 % (changeid, fileid, changectx,)
1194 1193 )
1195 1194
1196 1195 if filelog is not None:
@@ -1816,7 +1816,7 b' are you sure you want to review/edit and'
1816 1816 try:
1817 1817 patch = self.ui.edit(patch.getvalue(), b"", action=b"diff")
1818 1818 except error.Abort as exc:
1819 self.errorstr = str(exc)
1819 self.errorstr = stringutil.forcebytestr(exc)
1820 1820 return None
1821 1821 finally:
1822 1822 self.stdscr.clear()
@@ -111,6 +111,14 b' class Abort(Hint, Exception):'
111 111
112 112 __bytes__ = _tobytes
113 113
114 if pycompat.ispy3:
115
116 def __str__(self):
117 # the output would be unreadable if the message was translated,
118 # but do not replace it with encoding.strfromlocal(), which
119 # may raise another exception.
120 return pycompat.sysstr(self.__bytes__())
121
114 122
115 123 class HookLoadError(Abort):
116 124 """raised when loading a hook fails, aborting an operation
@@ -136,6 +136,16 b' from .utils import ('
136 136 pickle = util.pickle
137 137
138 138
139 def isprintable(obj):
140 """Check if the given object can be directly passed in to formatter's
141 write() and data() functions
142
143 Returns False if the object is unsupported or must be pre-processed by
144 formatdate(), formatdict(), or formatlist().
145 """
146 return isinstance(obj, (type(None), bool, int, pycompat.long, float, bytes))
147
148
139 149 class _nullconverter(object):
140 150 '''convert non-primitive data types to be processed by formatter'''
141 151
@@ -505,6 +515,10 b' class templateformatter(baseformatter):'
505 515 if part not in self._parts:
506 516 return
507 517 ref = self._parts[part]
518 # None can't be put in the mapping dict since it means <unset>
519 for k, v in item.items():
520 if v is None:
521 item[k] = templateutil.wrappedvalue(v)
508 522 self._out.write(self._t.render(ref, item))
509 523
510 524 @util.propertycache
@@ -1417,7 +1417,7 b' class localrepository(object):'
1417 1417
1418 1418 def _refreshchangelog(self):
1419 1419 """make sure the in memory changelog match the on-disk one"""
1420 if b'changelog' in vars(self) and self.currenttransaction() is None:
1420 if 'changelog' in vars(self) and self.currenttransaction() is None:
1421 1421 del self.changelog
1422 1422
1423 1423 @property
@@ -342,6 +342,7 b' def _encode(ui, s, charsets):'
342 342 s.decode('ascii')
343 343 except UnicodeDecodeError:
344 344 for ics in (encoding.encoding, encoding.fallbackencoding):
345 ics = pycompat.sysstr(ics)
345 346 try:
346 347 u = s.decode(ics)
347 348 except UnicodeDecodeError:
@@ -362,13 +363,13 b' def headencode(ui, s, charsets=None, dis'
362 363 if not display:
363 364 # split into words?
364 365 s, cs = _encode(ui, s, charsets)
365 return str(email.header.Header(s, cs))
366 return encoding.strtolocal(email.header.Header(s, cs).encode())
366 367 return s
367 368
368 369
369 370 def _addressencode(ui, name, addr, charsets=None):
370 371 assert isinstance(addr, bytes)
371 name = headencode(ui, name, charsets)
372 name = encoding.strfromlocal(headencode(ui, name, charsets))
372 373 try:
373 374 acc, dom = addr.split(b'@')
374 375 acc.decode('ascii')
@@ -440,6 +441,10 b' if pycompat.ispy3:'
440 441 finally:
441 442 fp.detach()
442 443
444 def parsebytes(data):
445 ep = email.parser.BytesParser()
446 return ep.parsebytes(data)
447
443 448
444 449 else:
445 450
@@ -449,6 +454,10 b' else:'
449 454 ep = email.parser.Parser()
450 455 return ep.parse(fp)
451 456
457 def parsebytes(data):
458 ep = email.parser.Parser()
459 return ep.parsestr(data)
460
452 461
453 462 def headdecode(s):
454 463 '''Decodes RFC-2047 header'''
@@ -458,7 +467,7 b' def headdecode(s):'
458 467 try:
459 468 uparts.append(part.decode(charset))
460 469 continue
461 except UnicodeDecodeError:
470 except (UnicodeDecodeError, LookupError):
462 471 pass
463 472 # On Python 3, decode_header() may return either bytes or unicode
464 473 # depending on whether the header has =?<charset>? or not
@@ -867,12 +867,15 b' class treemanifest(object):'
867 867 return not self._dirs or all(m._isempty() for m in self._dirs.values())
868 868
869 869 def __repr__(self):
870 return b'<treemanifest dir=%s, node=%s, loaded=%s, dirty=%s at 0x%x>' % (
871 self._dir,
872 hex(self._node),
873 bool(self._loadfunc is _noop),
874 self._dirty,
875 id(self),
870 return (
871 b'<treemanifest dir=%s, node=%s, loaded=%s, dirty=%s at 0x%x>'
872 % (
873 self._dir,
874 hex(self._node),
875 bool(self._loadfunc is _noop),
876 self._dirty,
877 id(self),
878 )
876 879 )
877 880
878 881 def dir(self):
@@ -12,6 +12,7 b' from __future__ import absolute_import'
12 12
13 13 import getopt
14 14 import inspect
15 import json
15 16 import os
16 17 import shlex
17 18 import sys
@@ -88,6 +89,7 b' def rapply(f, xs):'
88 89
89 90 if ispy3:
90 91 import builtins
92 import codecs
91 93 import functools
92 94 import io
93 95 import struct
@@ -347,6 +349,48 b' if ispy3:'
347 349 iteritems = lambda x: x.items()
348 350 itervalues = lambda x: x.values()
349 351
352 # Python 3.5's json.load and json.loads require str. We polyfill its
353 # code for detecting encoding from bytes.
354 if sys.version_info[0:2] < (3, 6):
355
356 def _detect_encoding(b):
357 bstartswith = b.startswith
358 if bstartswith((codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE)):
359 return 'utf-32'
360 if bstartswith((codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE)):
361 return 'utf-16'
362 if bstartswith(codecs.BOM_UTF8):
363 return 'utf-8-sig'
364
365 if len(b) >= 4:
366 if not b[0]:
367 # 00 00 -- -- - utf-32-be
368 # 00 XX -- -- - utf-16-be
369 return 'utf-16-be' if b[1] else 'utf-32-be'
370 if not b[1]:
371 # XX 00 00 00 - utf-32-le
372 # XX 00 00 XX - utf-16-le
373 # XX 00 XX -- - utf-16-le
374 return 'utf-16-le' if b[2] or b[3] else 'utf-32-le'
375 elif len(b) == 2:
376 if not b[0]:
377 # 00 XX - utf-16-be
378 return 'utf-16-be'
379 if not b[1]:
380 # XX 00 - utf-16-le
381 return 'utf-16-le'
382 # default
383 return 'utf-8'
384
385 def json_loads(s, *args, **kwargs):
386 if isinstance(s, (bytes, bytearray)):
387 s = s.decode(_detect_encoding(s), 'surrogatepass')
388
389 return json.loads(s, *args, **kwargs)
390
391 else:
392 json_loads = json.loads
393
350 394 else:
351 395 import cStringIO
352 396
@@ -424,6 +468,7 b' else:'
424 468 getargspec = inspect.getargspec
425 469 iteritems = lambda x: x.iteritems()
426 470 itervalues = lambda x: x.itervalues()
471 json_loads = json.loads
427 472
428 473 isjython = sysplatform.startswith(b'java')
429 474
@@ -1854,7 +1854,7 b' class simplekeyvaluefile(object):'
1854 1854 raise error.CorruptedState(e % self.firstlinekey)
1855 1855 d.update(updatedict)
1856 1856 except ValueError as e:
1857 raise error.CorruptedState(str(e))
1857 raise error.CorruptedState(stringutil.forcebytestr(e))
1858 1858 return d
1859 1859
1860 1860 def write(self, data, firstline=None):
@@ -31,9 +31,6 b' from .utils import ('
31 31 urlerr = util.urlerr
32 32 urlreq = util.urlreq
33 33
34 if pycompat.ispy3:
35 long = int
36
37 34 # filters are callables like:
38 35 # fn(obj)
39 36 # with:
@@ -329,7 +326,7 b' def json(obj, paranoid=True):'
329 326 return b'false'
330 327 elif obj is True:
331 328 return b'true'
332 elif isinstance(obj, (int, long, float)):
329 elif isinstance(obj, (int, pycompat.long, float)):
333 330 return pycompat.bytestr(obj)
334 331 elif isinstance(obj, bytes):
335 332 return b'"%s"' % encoding.jsonescape(obj, paranoid=paranoid)
@@ -1564,6 +1564,7 b' class ui(object):'
1564 1564 # - http://bugs.python.org/issue12833
1565 1565 with self.timeblockedsection(b'stdio'):
1566 1566 if usereadline:
1567 self.flush()
1567 1568 prompt = encoding.strfromlocal(prompt)
1568 1569 line = encoding.strtolocal(pycompat.rawinput(prompt))
1569 1570 # When stdin is in binary mode on Windows, it can cause
@@ -6,32 +6,27 b''
6 6
7 7 import os
8 8
9 supportedpy = '~= 2.7'
10 if os.environ.get('HGALLOWPYTHON3', ''):
11 # Mercurial will never work on Python 3 before 3.5 due to a lack
12 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
13 # due to a bug in % formatting in bytestrings.
14 # We cannot support Python 3.5.0, 3.5.1, 3.5.2 because of bug in
15 # codecs.escape_encode() where it raises SystemError on empty bytestring
16 # bug link: https://bugs.python.org/issue25270
17 #
18 # TODO: when we actually work on Python 3, use this string as the
19 # actual supportedpy string.
20 supportedpy = ','.join(
21 [
22 '>=2.7',
23 '!=3.0.*',
24 '!=3.1.*',
25 '!=3.2.*',
26 '!=3.3.*',
27 '!=3.4.*',
28 '!=3.5.0',
29 '!=3.5.1',
30 '!=3.5.2',
31 '!=3.6.0',
32 '!=3.6.1',
33 ]
34 )
9 # Mercurial will never work on Python 3 before 3.5 due to a lack
10 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
11 # due to a bug in % formatting in bytestrings.
12 # We cannot support Python 3.5.0, 3.5.1, 3.5.2 because of bug in
13 # codecs.escape_encode() where it raises SystemError on empty bytestring
14 # bug link: https://bugs.python.org/issue25270
15 supportedpy = ','.join(
16 [
17 '>=2.7',
18 '!=3.0.*',
19 '!=3.1.*',
20 '!=3.2.*',
21 '!=3.3.*',
22 '!=3.4.*',
23 '!=3.5.0',
24 '!=3.5.1',
25 '!=3.5.2',
26 '!=3.6.0',
27 '!=3.6.1',
28 ]
29 )
35 30
36 31 import sys, platform
37 32 import sysconfig
@@ -89,39 +84,6 b' Python {py} detected.'
89 84 printf(error, file=sys.stderr)
90 85 sys.exit(1)
91 86
92 # We don't yet officially support Python 3. But we want to allow developers to
93 # hack on. Detect and disallow running on Python 3 by default. But provide a
94 # backdoor to enable working on Python 3.
95 if sys.version_info[0] != 2:
96 badpython = True
97
98 # Allow Python 3 from source checkouts.
99 if os.path.isdir('.hg') or 'HGPYTHON3' in os.environ:
100 badpython = False
101
102 if badpython:
103 error = """
104 Python {py} detected.
105
106 Mercurial currently has beta support for Python 3 and use of Python 2.7 is
107 recommended for the best experience.
108
109 Please re-run with Python 2.7 for a faster, less buggy experience.
110
111 If you would like to beta test Mercurial with Python 3, this error can
112 be suppressed by defining the HGPYTHON3 environment variable when invoking
113 this command. No special environment variables or configuration changes are
114 necessary to run `hg` with Python 3.
115
116 See https://www.mercurial-scm.org/wiki/Python3 for more on Mercurial's
117 Python 3 support.
118 """.format(
119 py='.'.join('%d' % x for x in sys.version_info[0:2])
120 )
121
122 printf(error, file=sys.stderr)
123 sys.exit(1)
124
125 87 if sys.version_info[0] >= 3:
126 88 DYLIB_SUFFIX = sysconfig.get_config_vars()['EXT_SUFFIX']
127 89 else:
@@ -98,7 +98,7 b' def request(host, path, show):'
98 98 if formatjson:
99 99 # json.dumps() will print trailing newlines. Eliminate them
100 100 # to make tests easier to write.
101 data = json.loads(data)
101 data = pycompat.json_loads(data)
102 102 lines = json.dumps(data, sort_keys=True, indent=2).splitlines()
103 103 for line in lines:
104 104 bodyfh.write(pycompat.sysbytes(line.rstrip()))
@@ -1,5 +1,6 b''
1 1 from __future__ import absolute_import, print_function
2 2
3 import distutils.version
3 4 import os
4 5 import re
5 6 import socket
@@ -828,6 +829,17 b' def has_dev_full():'
828 829 return os.path.exists('/dev/full')
829 830
830 831
832 @check("ensurepip", "ensurepip module")
833 def has_ensurepip():
834 try:
835 import ensurepip
836
837 ensurepip.bootstrap
838 return True
839 except ImportError:
840 return False
841
842
831 843 @check("virtualenv", "Python virtualenv support")
832 844 def has_virtualenv():
833 845 try:
@@ -980,12 +992,10 b' def has_emacs():'
980 992 return matchoutput('emacs --version', b'GNU Emacs 2(4.4|4.5|5|6|7|8|9)')
981 993
982 994
983 # @check('black', 'the black formatter for python')
984 @check('grey', 'grey, the fork of the black formatter for python')
995 @check('black', 'the black formatter for python')
985 996 def has_black():
986 # use that to actual black as soon as possible
987 # blackcmd = 'black --version'
988 blackcmd = 'python3 $RUNTESTDIR/../contrib/grey.py --version'
989 # version_regex = b'black, version \d'
990 version_regex = b'grey.py, version \d'
991 return matchoutput(blackcmd, version_regex)
997 blackcmd = 'black --version'
998 version_regex = b'black, version ([0-9a-b.]+)'
999 version = matchoutput(blackcmd, version_regex)
1000 sv = distutils.version.StrictVersion
1001 return version and sv(_strpath(version.group(1))) >= sv('19.10b0')
@@ -1504,7 +1504,7 b' class PythonTest(Test):'
1504 1504 py3switch = self._py3warnings and b' -3' or b''
1505 1505 # Quote the python(3) executable for Windows
1506 1506 cmd = b'"%s"%s "%s"' % (PYTHON, py3switch, self.path)
1507 vlog("# Running", cmd)
1507 vlog("# Running", cmd.decode("utf-8"))
1508 1508 normalizenewlines = os.name == 'nt'
1509 1509 result = self._runcommand(cmd, env, normalizenewlines=normalizenewlines)
1510 1510 if self._aborted:
@@ -1589,7 +1589,7 b' class TTest(Test):'
1589 1589 f.write(l)
1590 1590
1591 1591 cmd = b'%s "%s"' % (self._shell, fname)
1592 vlog("# Running", cmd)
1592 vlog("# Running", cmd.decode("utf-8"))
1593 1593
1594 1594 exitcode, output = self._runcommand(cmd, env)
1595 1595
@@ -1770,7 +1770,9 b' class TTest(Test):'
1770 1770 if l.startswith(b'#require'):
1771 1771 lsplit = l.split()
1772 1772 if len(lsplit) < 2 or lsplit[0] != b'#require':
1773 after.setdefault(pos, []).append(' !!! invalid #require\n')
1773 after.setdefault(pos, []).append(
1774 b' !!! invalid #require\n'
1775 )
1774 1776 if not skipping:
1775 1777 haveresult, message = self._hghave(lsplit[1:])
1776 1778 if not haveresult:
@@ -1780,19 +1782,19 b' class TTest(Test):'
1780 1782 elif l.startswith(b'#if'):
1781 1783 lsplit = l.split()
1782 1784 if len(lsplit) < 2 or lsplit[0] != b'#if':
1783 after.setdefault(pos, []).append(' !!! invalid #if\n')
1785 after.setdefault(pos, []).append(b' !!! invalid #if\n')
1784 1786 if skipping is not None:
1785 after.setdefault(pos, []).append(' !!! nested #if\n')
1787 after.setdefault(pos, []).append(b' !!! nested #if\n')
1786 1788 skipping = not self._iftest(lsplit[1:])
1787 1789 after.setdefault(pos, []).append(l)
1788 1790 elif l.startswith(b'#else'):
1789 1791 if skipping is None:
1790 after.setdefault(pos, []).append(' !!! missing #if\n')
1792 after.setdefault(pos, []).append(b' !!! missing #if\n')
1791 1793 skipping = not skipping
1792 1794 after.setdefault(pos, []).append(l)
1793 1795 elif l.startswith(b'#endif'):
1794 1796 if skipping is None:
1795 after.setdefault(pos, []).append(' !!! missing #if\n')
1797 after.setdefault(pos, []).append(b' !!! missing #if\n')
1796 1798 skipping = None
1797 1799 after.setdefault(pos, []).append(l)
1798 1800 elif skipping:
@@ -1841,7 +1843,7 b' class TTest(Test):'
1841 1843 if inpython:
1842 1844 script.append(b'EOF\n')
1843 1845 if skipping is not None:
1844 after.setdefault(pos, []).append(' !!! missing #endif\n')
1846 after.setdefault(pos, []).append(b' !!! missing #endif\n')
1845 1847 addsalt(n + 1, False)
1846 1848 # Need to end any current per-command trace
1847 1849 if activetrace:
@@ -3111,12 +3113,14 b' class TestRunner(object):'
3111 3113 'extensions.logexceptions=%s' % logexceptions.decode('utf-8')
3112 3114 )
3113 3115
3114 vlog("# Using TESTDIR", self._testdir)
3115 vlog("# Using RUNTESTDIR", osenvironb[b'RUNTESTDIR'])
3116 vlog("# Using HGTMP", self._hgtmp)
3116 vlog("# Using TESTDIR", _strpath(self._testdir))
3117 vlog("# Using RUNTESTDIR", _strpath(osenvironb[b'RUNTESTDIR']))
3118 vlog("# Using HGTMP", _strpath(self._hgtmp))
3117 3119 vlog("# Using PATH", os.environ["PATH"])
3118 vlog("# Using", IMPL_PATH, osenvironb[IMPL_PATH])
3119 vlog("# Writing to directory", self._outputdir)
3120 vlog(
3121 "# Using", _strpath(IMPL_PATH), _strpath(osenvironb[IMPL_PATH]),
3122 )
3123 vlog("# Writing to directory", _strpath(self._outputdir))
3120 3124
3121 3125 try:
3122 3126 return self._runtests(testdescs) or 0
@@ -3357,7 +3361,7 b' class TestRunner(object):'
3357 3361 if self.options.keep_tmpdir:
3358 3362 return
3359 3363
3360 vlog("# Cleaning up HGTMP", self._hgtmp)
3364 vlog("# Cleaning up HGTMP", _strpath(self._hgtmp))
3361 3365 shutil.rmtree(self._hgtmp, True)
3362 3366 for f in self._createdfiles:
3363 3367 try:
@@ -3468,7 +3472,7 b' class TestRunner(object):'
3468 3472 makedirs(self._pythondir)
3469 3473 makedirs(self._bindir)
3470 3474
3471 vlog("# Running", cmd)
3475 vlog("# Running", cmd.decode("utf-8"))
3472 3476 if subprocess.call(_strpath(cmd), shell=True) == 0:
3473 3477 if not self.options.verbose:
3474 3478 try:
@@ -3643,13 +3647,11 b' class TestRunner(object):'
3643 3647 if os.name == 'nt' and not p.endswith(b'.exe'):
3644 3648 p += b'.exe'
3645 3649 found = self._findprogram(p)
3650 p = p.decode("utf-8")
3646 3651 if found:
3647 vlog("# Found prerequisite", p, "at", found)
3652 vlog("# Found prerequisite", p, "at", _strpath(found))
3648 3653 else:
3649 print(
3650 "WARNING: Did not find prerequisite tool: %s "
3651 % p.decode("utf-8")
3652 )
3654 print("WARNING: Did not find prerequisite tool: %s " % p)
3653 3655
3654 3656
3655 3657 def aggregateexceptions(path):
@@ -1,4 +1,4 b''
1 #require py3
1 #require py37
2 2
3 3 $ byteify_strings () {
4 4 > $PYTHON "$TESTDIR/../contrib/byteify-strings.py" "$@"
@@ -21,7 +21,6 b' New errors are not allowed. Warnings are'
21 21 Skipping contrib/automation/hgautomation/try_server.py it has no-che?k-code (glob)
22 22 Skipping contrib/automation/hgautomation/windows.py it has no-che?k-code (glob)
23 23 Skipping contrib/automation/hgautomation/winrm.py it has no-che?k-code (glob)
24 Skipping contrib/grey.py it has no-che?k-code (glob)
25 24 Skipping contrib/packaging/hgpackaging/downloads.py it has no-che?k-code (glob)
26 25 Skipping contrib/packaging/hgpackaging/inno.py it has no-che?k-code (glob)
27 26 Skipping contrib/packaging/hgpackaging/py2exe.py it has no-che?k-code (glob)
@@ -1,7 +1,5 b''
1 #require grey
2
3 (this should use the actual black as soon as possible)
1 #require black
4 2
5 3 $ cd $RUNTESTDIR/..
6 $ python3 contrib/grey.py --config=black.toml --check --diff `hg files 'set:**.py - hgext/fsmonitor/pywatchman/** - mercurial/thirdparty/** - "contrib/python-zstandard/**" - contrib/grey.py'`
4 $ black --config=black.toml --check --diff `hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"'`
7 5
@@ -20,7 +20,6 b' outputs, which should be fixed later.'
20 20 > -X setup.py \
21 21 > -X contrib/automation/ \
22 22 > -X contrib/debugshell.py \
23 > -X contrib/grey.py \
24 23 > -X contrib/hgweb.fcgi \
25 24 > -X contrib/packaging/hg-docker \
26 25 > -X contrib/packaging/hgpackaging/ \
@@ -6,7 +6,6 b''
6 6 #if no-py3
7 7 $ testrepohg files 'set:(**.py)' \
8 8 > -X contrib/automation/ \
9 > -X contrib/grey.py \
10 9 > -X contrib/packaging/hgpackaging/ \
11 10 > -X contrib/packaging/inno/ \
12 11 > -X contrib/packaging/wix/ \
@@ -87,6 +87,172 b' Test case sensitive configuration'
87 87 }
88 88 ]
89 89
90 Test config default of various types:
91
92 {"defaultvalue": ""} for -T'json(defaultvalue)' looks weird, but that's
93 how the templater works. Unknown keywords are evaluated to "".
94
95 dynamicdefault
96
97 $ hg config --config alias.foo= alias -Tjson
98 [
99 {
100 "name": "alias.foo",
101 "source": "--config",
102 "value": ""
103 }
104 ]
105 $ hg config --config alias.foo= alias -T'json(defaultvalue)'
106 [
107 {"defaultvalue": ""}
108 ]
109 $ hg config --config alias.foo= alias -T'{defaultvalue}\n'
110
111
112 null
113
114 $ hg config --config auth.cookiefile= auth -Tjson
115 [
116 {
117 "defaultvalue": null,
118 "name": "auth.cookiefile",
119 "source": "--config",
120 "value": ""
121 }
122 ]
123 $ hg config --config auth.cookiefile= auth -T'json(defaultvalue)'
124 [
125 {"defaultvalue": null}
126 ]
127 $ hg config --config auth.cookiefile= auth -T'{defaultvalue}\n'
128
129
130 false
131
132 $ hg config --config commands.commit.post-status= commands -Tjson
133 [
134 {
135 "defaultvalue": false,
136 "name": "commands.commit.post-status",
137 "source": "--config",
138 "value": ""
139 }
140 ]
141 $ hg config --config commands.commit.post-status= commands -T'json(defaultvalue)'
142 [
143 {"defaultvalue": false}
144 ]
145 $ hg config --config commands.commit.post-status= commands -T'{defaultvalue}\n'
146 False
147
148 true
149
150 $ hg config --config format.dotencode= format -Tjson
151 [
152 {
153 "defaultvalue": true,
154 "name": "format.dotencode",
155 "source": "--config",
156 "value": ""
157 }
158 ]
159 $ hg config --config format.dotencode= format -T'json(defaultvalue)'
160 [
161 {"defaultvalue": true}
162 ]
163 $ hg config --config format.dotencode= format -T'{defaultvalue}\n'
164 True
165
166 bytes
167
168 $ hg config --config commands.resolve.mark-check= commands -Tjson
169 [
170 {
171 "defaultvalue": "none",
172 "name": "commands.resolve.mark-check",
173 "source": "--config",
174 "value": ""
175 }
176 ]
177 $ hg config --config commands.resolve.mark-check= commands -T'json(defaultvalue)'
178 [
179 {"defaultvalue": "none"}
180 ]
181 $ hg config --config commands.resolve.mark-check= commands -T'{defaultvalue}\n'
182 none
183
184 empty list
185
186 $ hg config --config commands.show.aliasprefix= commands -Tjson
187 [
188 {
189 "defaultvalue": [],
190 "name": "commands.show.aliasprefix",
191 "source": "--config",
192 "value": ""
193 }
194 ]
195 $ hg config --config commands.show.aliasprefix= commands -T'json(defaultvalue)'
196 [
197 {"defaultvalue": []}
198 ]
199 $ hg config --config commands.show.aliasprefix= commands -T'{defaultvalue}\n'
200
201
202 nonempty list
203
204 $ hg config --config progress.format= progress -Tjson
205 [
206 {
207 "defaultvalue": ["topic", "bar", "number", "estimate"],
208 "name": "progress.format",
209 "source": "--config",
210 "value": ""
211 }
212 ]
213 $ hg config --config progress.format= progress -T'json(defaultvalue)'
214 [
215 {"defaultvalue": ["topic", "bar", "number", "estimate"]}
216 ]
217 $ hg config --config progress.format= progress -T'{defaultvalue}\n'
218 topic bar number estimate
219
220 int
221
222 $ hg config --config profiling.freq= profiling -Tjson
223 [
224 {
225 "defaultvalue": 1000,
226 "name": "profiling.freq",
227 "source": "--config",
228 "value": ""
229 }
230 ]
231 $ hg config --config profiling.freq= profiling -T'json(defaultvalue)'
232 [
233 {"defaultvalue": 1000}
234 ]
235 $ hg config --config profiling.freq= profiling -T'{defaultvalue}\n'
236 1000
237
238 float
239
240 $ hg config --config profiling.showmax= profiling -Tjson
241 [
242 {
243 "defaultvalue": 0.999,
244 "name": "profiling.showmax",
245 "source": "--config",
246 "value": ""
247 }
248 ]
249 $ hg config --config profiling.showmax= profiling -T'json(defaultvalue)'
250 [
251 {"defaultvalue": 0.999}
252 ]
253 $ hg config --config profiling.showmax= profiling -T'{defaultvalue}\n'
254 0.999
255
90 256 Test empty config source:
91 257
92 258 $ cat <<EOF > emptysource.py
@@ -13,18 +13,16 b" Ensure debuild doesn't run the testsuite"
13 13 $ make deb > $OUTPUTDIR/build.log 2>&1
14 14 $ cd $OUTPUTDIR
15 15 $ ls *.deb | grep -v 'dbg'
16 mercurial-common_*.deb (glob)
17 16 mercurial_*.deb (glob)
18 main deb should have .so but no .py
17 should have .so and .py
19 18 $ dpkg --contents mercurial_*.deb | egrep '(localrepo|parsers)'
20 * ./usr/lib/python2.7/dist-packages/mercurial/cext/parsers*.so (glob)
21 mercurial-common should have py but no .so or pyc
22 $ dpkg --contents mercurial-common_*.deb | egrep '(localrepo|parsers.*so)'
23 * ./usr/lib/python2.7/dist-packages/mercurial/localrepo.py (glob)
24 zsh completions should be in the common package
25 $ dpkg --contents mercurial-common_*.deb | egrep 'zsh.*[^/]$'
19 * ./usr/lib/python3/dist-packages/mercurial/cext/parsers*.so (glob)
20 * ./usr/lib/python3/dist-packages/mercurial/localrepo.py (glob)
21 * ./usr/lib/python3/dist-packages/mercurial/pure/parsers.py (glob)
22 should have zsh completions
23 $ dpkg --contents mercurial_*.deb | egrep 'zsh.*[^/]$'
26 24 * ./usr/share/zsh/vendor-completions/_hg (glob)
27 chg should be installed alongside hg, in the 'mercurial' package
25 should have chg
28 26 $ dpkg --contents mercurial_*.deb | egrep 'chg$'
29 27 * ./usr/bin/chg (glob)
30 28 chg should come with a man page
@@ -209,12 +209,12 b' Ensure the data got to the server OK'
209 209 insertflagprocessor(flag, processor, flagprocessors)
210 210 File "*/mercurial/revlogutils/flagutil.py", line *, in insertflagprocessor (glob)
211 211 raise error.Abort(msg)
212 mercurial.error.Abort: b"cannot register multiple processors on flag '0x8'." (py3 !)
212 mercurial.error.Abort: cannot register multiple processors on flag '0x8'. (py3 !)
213 213 Abort: cannot register multiple processors on flag '0x8'. (no-py3 !)
214 214 *** failed to set up extension duplicate: cannot register multiple processors on flag '0x8'.
215 215 $ hg st 2>&1 | egrep 'cannot register multiple processors|flagprocessorext'
216 216 File "*/tests/flagprocessorext.py", line *, in extsetup (glob)
217 mercurial.error.Abort: b"cannot register multiple processors on flag '0x8'." (py3 !)
217 mercurial.error.Abort: cannot register multiple processors on flag '0x8'. (py3 !)
218 218 Abort: cannot register multiple processors on flag '0x8'. (no-py3 !)
219 219 *** failed to set up extension duplicate: cannot register multiple processors on flag '0x8'.
220 220 File "*/tests/flagprocessorext.py", line *, in b64decode (glob)
@@ -966,7 +966,9 b' errors encountered'
966 966 $ cd ..
967 967 $ hg init eucjp
968 968 $ cd eucjp
969 $ "$PYTHON" -c 'print("\265\376")' >> eucjp.txt # Japanese kanji "Kyo"
969 >>> with open('eucjp.txt', 'wb') as fh:
970 ... # Japanese kanji "Kyo"
971 ... fh.write(u'\265\376'.encode('utf-8')) and None
970 972 $ hg ci -Ama
971 973 adding eucjp.txt
972 974 $ hgserveget () {
@@ -988,7 +988,7 b' test python hooks'
988 988 ModuleNotFoundError: No module named 'hgext_syntaxerror' (py36 !)
989 989 Traceback (most recent call last): (py3 !)
990 990 HookLoadError: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed (no-py3 !)
991 mercurial.error.HookLoadError: b'preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed' (py3 !)
991 mercurial.error.HookLoadError: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed (py3 !)
992 992 abort: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
993 993
994 994 $ echo '[hooks]' > ../a/.hg/hgrc
@@ -1161,7 +1161,7 b' make sure --traceback works on hook impo'
1161 1161 ModuleNotFoundError: No module named 'hgext_importfail' (py36 !)
1162 1162 Traceback (most recent call last):
1163 1163 HookLoadError: precommit.importfail hook is invalid: import of "importfail" failed (no-py3 !)
1164 mercurial.error.HookLoadError: b'precommit.importfail hook is invalid: import of "importfail" failed' (py3 !)
1164 mercurial.error.HookLoadError: precommit.importfail hook is invalid: import of "importfail" failed (py3 !)
1165 1165 abort: precommit.importfail hook is invalid: import of "importfail" failed
1166 1166
1167 1167 Issue1827: Hooks Update & Commit not completely post operation
@@ -236,25 +236,56 b' Verify the json works too:'
236 236
237 237 #endif
238 238
239 #if py3
240 $ HGALLOWPYTHON3=1
241 $ export HGALLOWPYTHON3
242 #endif
243
244 #if virtualenv
245
246 239 Verify that Mercurial is installable with pip. Note that this MUST be
247 240 the last test in this file, because we do some nasty things to the
248 241 shell environment in order to make the virtualenv work reliably.
249 242
243 On Python 3, we use the venv module, which is part of the standard library.
244 But some Linux distros strip out this module's functionality involving pip,
245 so we have to look for the ensurepip module, which these distros strip out
246 completely.
247 On Python 2, we use the 3rd party virtualenv module, if available.
248
250 249 $ cd $TESTTMP
250 $ unset PYTHONPATH
251
252 #if py3 ensurepip
253 $ "$PYTHON" -m venv installenv >> pip.log
254
255 Note: we use this weird path to run pip and hg to avoid platform differences,
256 since it's bin on most platforms but Scripts on Windows.
257 $ ./installenv/*/pip install --no-index $TESTDIR/.. >> pip.log
258 $ ./installenv/*/hg debuginstall || cat pip.log
259 checking encoding (ascii)...
260 checking Python executable (*) (glob)
261 checking Python version (3.*) (glob)
262 checking Python lib (*)... (glob)
263 checking Python security support (*) (glob)
264 checking Mercurial version (*) (glob)
265 checking Mercurial custom build (*) (glob)
266 checking module policy (*) (glob)
267 checking installed modules (*/mercurial)... (glob)
268 checking registered compression engines (*) (glob)
269 checking available compression engines (*) (glob)
270 checking available compression engines for wire protocol (*) (glob)
271 checking "re2" regexp engine \((available|missing)\) (re)
272 checking templates ($TESTTMP/installenv/*/site-packages/mercurial/templates)... (glob)
273 checking default template ($TESTTMP/installenv/*/site-packages/mercurial/templates/map-cmdline.default) (glob)
274 checking commit editor... (*) (glob)
275 checking username (test)
276 no problems detected
277 #endif
278
279 #if no-py3 virtualenv
280
251 281 Note: --no-site-packages is deprecated, but some places have an
252 282 ancient virtualenv from their linux distro or similar and it's not yet
253 283 the default for them.
254 $ unset PYTHONPATH
284
255 285 $ "$PYTHON" -m virtualenv --no-site-packages --never-download installenv >> pip.log
256 286 DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7. (?)
257 287 DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support (?)
288
258 289 Note: we use this weird path to run pip and hg to avoid platform differences,
259 290 since it's bin on most platforms but Scripts on Windows.
260 291 $ ./installenv/*/pip install --no-index $TESTDIR/.. >> pip.log
@@ -263,8 +294,7 b" since it's bin on most platforms but Scr"
263 294 $ ./installenv/*/hg debuginstall || cat pip.log
264 295 checking encoding (ascii)...
265 296 checking Python executable (*) (glob)
266 checking Python version (2.*) (glob) (no-py3 !)
267 checking Python version (3.*) (glob) (py3 !)
297 checking Python version (2.*) (glob)
268 298 checking Python lib (*)... (glob)
269 299 checking Python security support (*) (glob)
270 300 TLS 1.2 not supported by Python install; network connections lack modern security (?)
@@ -256,7 +256,8 b' ie. if patch.diff wrapper acts as it sho'
256 256
257 257 Pull from bundle and trigger notify
258 258
259 $ hg pull -u ../kw.hg
259 $ hg pull -u ../kw.hg | \
260 > "$PYTHON" $TESTDIR/unwrap-message-id.py
260 261 pulling from ../kw.hg
261 262 requesting all changes
262 263 adding changesets
@@ -354,7 +354,8 b' Test a checksum failure during the proce'
354 354 $LOCALIP - - [$ERRDATE$] HG error: localstore.download(oid, req.bodyfh) (glob)
355 355 $LOCALIP - - [$ERRDATE$] HG error: super(badstore, self).download(oid, src) (glob)
356 356 $LOCALIP - - [$ERRDATE$] HG error: _(b'corrupt remote lfs object: %s') % oid (glob)
357 $LOCALIP - - [$ERRDATE$] HG error: LfsCorruptionError: corrupt remote lfs object: b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c (glob)
357 $LOCALIP - - [$ERRDATE$] HG error: LfsCorruptionError: corrupt remote lfs object: b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c (no-py3 !)
358 $LOCALIP - - [$ERRDATE$] HG error: hgext.lfs.blobstore.LfsCorruptionError: corrupt remote lfs object: b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c (py3 !)
358 359 $LOCALIP - - [$ERRDATE$] HG error: (glob)
359 360 $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d': (glob)
360 361 Traceback (most recent call last):
@@ -376,7 +377,8 b' Test a checksum failure during the proce'
376 377 $LOCALIP - - [$ERRDATE$] HG error: blob = self._read(self.vfs, oid, verify) (glob)
377 378 $LOCALIP - - [$ERRDATE$] HG error: blobstore._verify(oid, b'dummy content') (glob)
378 379 $LOCALIP - - [$ERRDATE$] HG error: hint=_(b'run hg verify'), (glob)
379 $LOCALIP - - [$ERRDATE$] HG error: LfsCorruptionError: detected corrupt lfs object: 276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d (glob)
380 $LOCALIP - - [$ERRDATE$] HG error: LfsCorruptionError: detected corrupt lfs object: 276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d (no-py3 !)
381 $LOCALIP - - [$ERRDATE$] HG error: hgext.lfs.blobstore.LfsCorruptionError: detected corrupt lfs object: 276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d (py3 !)
380 382 $LOCALIP - - [$ERRDATE$] HG error: (glob)
381 383
382 384 Basic Authorization headers are returned by the Batch API, and sent back with
@@ -2,6 +2,7 b' from __future__ import absolute_import'
2 2
3 3 import unittest
4 4 from mercurial import error, mdiff
5 from mercurial.utils import stringutil
5 6
6 7 # for readability, line numbers are 0-origin
7 8 text1 = b'''
@@ -228,7 +229,10 b' class blocksinrangetests(unittest.TestCa'
228 229 try:
229 230 mdiff.blocksinrange(self.blocks, linerange2)
230 231 except exctype as exc:
231 self.assertTrue('line range exceeds file size' in str(exc))
232 self.assertTrue(
233 b'line range exceeds file size'
234 in stringutil.forcebytestr(exc)
235 )
232 236 else:
233 237 self.fail('%s not raised' % exctype.__name__)
234 238
@@ -316,7 +316,7 b' class DifferenceMatcherTests(unittest.Te'
316 316
317 317 # We're using includematcher instead of patterns because it behaves slightly
318 318 # better (giving narrower results) than patternmatcher.
319 def testVisitdirIncludeIncludfe(self):
319 def testVisitdirIncludeInclude(self):
320 320 m1 = matchmod.match(b'', b'', include=[b'path:dir/subdir'])
321 321 m2 = matchmod.match(b'', b'', include=[b'rootfilesin:dir'])
322 322 dm = matchmod.differencematcher(m1, m2)
@@ -430,7 +430,7 b' class IntersectionMatcherTests(unittest.'
430 430
431 431 # We're using includematcher instead of patterns because it behaves slightly
432 432 # better (giving narrower results) than patternmatcher.
433 def testVisitdirIncludeIncludfe(self):
433 def testVisitdirIncludeInclude(self):
434 434 m1 = matchmod.match(b'', b'', include=[b'path:dir/subdir'])
435 435 m2 = matchmod.match(b'', b'', include=[b'rootfilesin:dir'])
436 436 im = matchmod.intersectmatchers(m1, m2)
@@ -644,7 +644,7 b' class UnionMatcherTests(unittest.TestCas'
644 644
645 645 # We're using includematcher instead of patterns because it behaves slightly
646 646 # better (giving narrower results) than patternmatcher.
647 def testVisitdirIncludeIncludfe(self):
647 def testVisitdirIncludeInclude(self):
648 648 m1 = matchmod.match(b'', b'', include=[b'path:dir/subdir'])
649 649 m2 = matchmod.match(b'', b'', include=[b'rootfilesin:dir'])
650 650 um = matchmod.unionmatcher([m1, m2])
@@ -39,6 +39,7 b' commit'
39 39 push
40 40
41 41 $ hg --traceback --cwd b push ../a 2>&1 |
42 > "$PYTHON" $TESTDIR/unwrap-message-id.py | \
42 43 > "$PYTHON" -c 'from __future__ import print_function ; import sys,re; print(re.sub("\n\t", " ", sys.stdin.read()), end="")'
43 44 pushing to ../a
44 45 searching for changes
@@ -93,6 +94,7 b' unbundle with unrelated source'
93 94 unbundle with correct source
94 95
95 96 $ hg --config notify.sources=unbundle --cwd a unbundle ../test.hg 2>&1 |
97 > "$PYTHON" $TESTDIR/unwrap-message-id.py | \
96 98 > "$PYTHON" -c 'from __future__ import print_function ; import sys,re; print(re.sub("\n\t", " ", sys.stdin.read()), end="")'
97 99 adding changesets
98 100 adding manifests
@@ -169,6 +171,7 b' merge as a different user'
169 171 push
170 172
171 173 $ hg --traceback --cwd b --config notify.fromauthor=True push ../a 2>&1 |
174 > "$PYTHON" $TESTDIR/unwrap-message-id.py | \
172 175 > "$PYTHON" -c 'from __future__ import print_function ; import sys,re; print(re.sub("\n\t", " ", sys.stdin.read()), end="")'
173 176 pushing to ../a
174 177 searching for changes
@@ -196,7 +196,9 b' the python call below wraps continuation'
196 196 of the very long subject line
197 197 pull (minimal config)
198 198
199 $ hg --traceback --cwd b --config notify.domain=example.com --config notify.messageidseed=example pull ../a | "$PYTHON" $TESTTMP/filter.py
199 $ hg --traceback --cwd b --config notify.domain=example.com --config notify.messageidseed=example pull ../a | \
200 > "$PYTHON" $TESTDIR/unwrap-message-id.py | \
201 > "$PYTHON" $TESTTMP/filter.py
200 202 pulling from ../a
201 203 searching for changes
202 204 adding changesets
@@ -255,7 +257,9 b' pull'
255 257
256 258 $ hg --cwd b rollback
257 259 repository tip rolled back to revision 0 (undo pull)
258 $ hg --traceback --cwd b pull ../a | "$PYTHON" $TESTTMP/filter.py
260 $ hg --traceback --cwd b pull ../a | \
261 > "$PYTHON" $TESTDIR/unwrap-message-id.py | \
262 > "$PYTHON" $TESTTMP/filter.py
259 263 pulling from ../a
260 264 searching for changes
261 265 adding changesets
@@ -303,7 +307,9 b' pull'
303 307
304 308 $ hg --cwd b rollback
305 309 repository tip rolled back to revision 0 (undo pull)
306 $ hg --traceback --config notify.maxdiffstat=1 --cwd b pull ../a | "$PYTHON" $TESTTMP/filter.py
310 $ hg --traceback --config notify.maxdiffstat=1 --cwd b pull ../a | \
311 > "$PYTHON" $TESTDIR/unwrap-message-id.py | \
312 > "$PYTHON" $TESTTMP/filter.py
307 313 pulling from ../a
308 314 searching for changes
309 315 adding changesets
@@ -354,7 +360,9 b' test merge'
354 360 (branch merge, don't forget to commit)
355 361 $ hg ci -m merge -d '3 0'
356 362 $ cd ..
357 $ hg --traceback --cwd b pull ../a | "$PYTHON" $TESTTMP/filter.py
363 $ hg --traceback --cwd b pull ../a | \
364 > "$PYTHON" $TESTDIR/unwrap-message-id.py | \
365 > "$PYTHON" $TESTTMP/filter.py
358 366 pulling from ../a
359 367 searching for changes
360 368 adding changesets
@@ -418,8 +426,9 b' non-ascii content and truncation of mult'
418 426 > EOF
419 427 $ echo a >> a/a
420 428 $ hg --cwd a --encoding utf-8 commit -A -d '0 0' \
421 > -m `"$PYTHON" -c 'print("\xc3\xa0\xc3\xa1\xc3\xa2\xc3\xa3\xc3\xa4")'`
429 > -m `"$PYTHON" -c 'import sys; getattr(sys.stdout, "buffer", sys.stdout).write(b"\xc3\xa0\xc3\xa1\xc3\xa2\xc3\xa3\xc3\xa4")'`
422 430 $ hg --traceback --cwd b --encoding utf-8 pull ../a | \
431 > "$PYTHON" $TESTDIR/unwrap-message-id.py | \
423 432 > "$PYTHON" $TESTTMP/filter.py
424 433 pulling from ../a
425 434 searching for changes
@@ -433,7 +442,8 b' non-ascii content and truncation of mult'
433 442 Content-Transfer-Encoding: 8bit
434 443 X-Test: foo
435 444 Date: * (glob)
436 Subject: \xc3\xa0... (esc)
445 Subject: \xc3\xa0... (esc) (no-py3 !)
446 Subject: =?utf-8?b?w6AuLi4=?= (py3 !)
437 447 From: test@test.com
438 448 X-Hg-Notification: changeset 0f25f9c22b4c
439 449 Message-Id: <*> (glob)
@@ -473,7 +483,7 b' long lines'
473 483 new changesets a846b5f6ebb7
474 484 notify: sending 2 subscribers 1 changes
475 485 (run 'hg update' to get a working copy)
476 $ "$PYTHON" $TESTTMP/filter.py < b/mbox
486 $ cat b/mbox | "$PYTHON" $TESTDIR/unwrap-message-id.py | "$PYTHON" $TESTTMP/filter.py
477 487 From test@test.com ... ... .. ..:..:.. .... (re)
478 488 MIME-Version: 1.0
479 489 Content-Type: text/plain; charset="*" (glob)
@@ -533,7 +543,9 b' long lines'
533 543 (branches are permanent and global, did you want a bookmark?)
534 544 $ echo a >> a/a
535 545 $ hg --cwd a ci -m test -d '1 0'
536 $ hg --traceback --cwd b pull ../a | "$PYTHON" $TESTTMP/filter.py
546 $ hg --traceback --cwd b pull ../a | \
547 > "$PYTHON" $TESTDIR/unwrap-message-id.py | \
548 > "$PYTHON" $TESTTMP/filter.py
537 549 pulling from ../a
538 550 searching for changes
539 551 adding changesets
@@ -563,7 +575,9 b' from different branch'
563 575 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
564 576 $ echo a >> a/a
565 577 $ hg --cwd a ci -m test -d '1 0'
566 $ hg --traceback --cwd b pull ../a | "$PYTHON" $TESTTMP/filter.py
578 $ hg --traceback --cwd b pull ../a | \
579 > "$PYTHON" $TESTDIR/unwrap-message-id.py | \
580 > "$PYTHON" $TESTTMP/filter.py
567 581 pulling from ../a
568 582 searching for changes
569 583 adding changesets
@@ -592,7 +606,9 b' default template:'
592 606 $ mv "$HGRCPATH.new" $HGRCPATH
593 607 $ echo a >> a/a
594 608 $ hg --cwd a commit -m 'default template'
595 $ hg --cwd b pull ../a -q | "$PYTHON" $TESTTMP/filter.py
609 $ hg --cwd b pull ../a -q | \
610 > "$PYTHON" $TESTDIR/unwrap-message-id.py | \
611 > "$PYTHON" $TESTTMP/filter.py
596 612 MIME-Version: 1.0
597 613 Content-Type: text/plain; charset="us-ascii"
598 614 Content-Transfer-Encoding: 7bit
@@ -621,7 +637,9 b' with style:'
621 637 > EOF
622 638 $ echo a >> a/a
623 639 $ hg --cwd a commit -m 'with style'
624 $ hg --cwd b pull ../a -q | "$PYTHON" $TESTTMP/filter.py
640 $ hg --cwd b pull ../a -q | \
641 > "$PYTHON" $TESTDIR/unwrap-message-id.py | \
642 > "$PYTHON" $TESTTMP/filter.py
625 643 MIME-Version: 1.0
626 644 Content-Type: text/plain; charset="us-ascii"
627 645 Content-Transfer-Encoding: 7bit
@@ -644,7 +662,9 b' with template (overrides style):'
644 662 > EOF
645 663 $ echo a >> a/a
646 664 $ hg --cwd a commit -m 'with template'
647 $ hg --cwd b pull ../a -q | "$PYTHON" $TESTTMP/filter.py
665 $ hg --cwd b pull ../a -q | \
666 > "$PYTHON" $TESTDIR/unwrap-message-id.py | \
667 > "$PYTHON" $TESTTMP/filter.py
648 668 MIME-Version: 1.0
649 669 Content-Type: text/plain; charset="us-ascii"
650 670 Content-Transfer-Encoding: 7bit
@@ -675,7 +695,8 b' showfunc diff'
675 695 > EOF
676 696 $ hg commit -Am addfunction
677 697 adding f1
678 $ hg --cwd ../b pull ../a
698 $ hg --cwd ../b pull ../a | \
699 > "$PYTHON" $TESTDIR/unwrap-message-id.py
679 700 pulling from ../a
680 701 searching for changes
681 702 adding changesets
@@ -718,7 +739,8 b' showfunc diff'
718 739 > }
719 740 > EOF
720 741 $ hg commit -m changefunction
721 $ hg --cwd ../b --config notify.showfunc=True pull ../a
742 $ hg --cwd ../b --config notify.showfunc=True pull ../a | \
743 > "$PYTHON" $TESTDIR/unwrap-message-id.py
722 744 pulling from ../a
723 745 searching for changes
724 746 adding changesets
@@ -445,7 +445,9 b' with a specific bundle type'
445 445
446 446 utf-8 patch:
447 447 $ "$PYTHON" -c 'fp = open("utf", "wb"); fp.write(b"h\xC3\xB6mma!\n"); fp.close();'
448 $ hg commit -A -d '4 0' -m 'utf-8 content'
448 $ hg commit -A -d '4 0' \
449 > --encoding "utf-8" \
450 > -m `"$PYTHON" -c 'import sys; getattr(sys.stdout, "buffer", sys.stdout).write(b"\xc3\xa7a")'`
449 451 adding description
450 452 adding utf
451 453
@@ -454,16 +456,16 b' no mime encoding for email --test:'
454 456 this patch series consists of 1 patches.
455 457
456 458
457 displaying [PATCH] utf-8 content ...
459 displaying [PATCH] ?a ...
458 460 MIME-Version: 1.0
459 461 Content-Type: text/plain; charset="iso-8859-1"
460 462 Content-Transfer-Encoding: quoted-printable
461 Subject: [PATCH] utf-8 content
462 X-Mercurial-Node: 909a00e13e9d78b575aeee23dddbada46d5a143f
463 Subject: [PATCH] ?a
464 X-Mercurial-Node: f81ef97829467e868fc405fccbcfa66217e4d3e6
463 465 X-Mercurial-Series-Index: 1
464 466 X-Mercurial-Series-Total: 1
465 Message-Id: <909a00e13e9d78b575ae.240@test-hostname>
466 X-Mercurial-Series-Id: <909a00e13e9d78b575ae.240@test-hostname>
467 Message-Id: <f81ef97829467e868fc4.240@test-hostname>
468 X-Mercurial-Series-Id: <f81ef97829467e868fc4.240@test-hostname>
467 469 User-Agent: Mercurial-patchbomb/* (glob)
468 470 Date: Thu, 01 Jan 1970 00:04:00 +0000
469 471 From: quux
@@ -474,18 +476,18 b' no mime encoding for email --test:'
474 476 # User test
475 477 # Date 4 0
476 478 # Thu Jan 01 00:00:04 1970 +0000
477 # Node ID 909a00e13e9d78b575aeee23dddbada46d5a143f
479 # Node ID f81ef97829467e868fc405fccbcfa66217e4d3e6
478 480 # Parent ff2c9fa2018b15fa74b33363bda9527323e2a99f
479 utf-8 content
480
481 diff -r ff2c9fa2018b -r 909a00e13e9d description
481 ?a
482
483 diff -r ff2c9fa2018b -r f81ef9782946 description
482 484 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
483 485 +++ b/description Thu Jan 01 00:00:04 1970 +0000
484 486 @@ -0,0 +1,3 @@
485 487 +a multiline
486 488 +
487 489 +description
488 diff -r ff2c9fa2018b -r 909a00e13e9d utf
490 diff -r ff2c9fa2018b -r f81ef9782946 utf
489 491 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
490 492 +++ b/utf Thu Jan 01 00:00:04 1970 +0000
491 493 @@ -0,0 +1,1 @@
@@ -497,35 +499,36 b' mime encoded mbox (base64):'
497 499 this patch series consists of 1 patches.
498 500
499 501
500 sending [PATCH] utf-8 content ...
502 sending [PATCH] ?a ...
501 503
502 504 $ cat mbox
503 505 From quux ... ... .. ..:..:.. .... (re)
504 506 MIME-Version: 1.0
505 507 Content-Type: text/plain; charset="utf-8"
506 508 Content-Transfer-Encoding: base64
507 Subject: [PATCH] utf-8 content
508 X-Mercurial-Node: 909a00e13e9d78b575aeee23dddbada46d5a143f
509 Subject: [PATCH] ?a
510 X-Mercurial-Node: f81ef97829467e868fc405fccbcfa66217e4d3e6
509 511 X-Mercurial-Series-Index: 1
510 512 X-Mercurial-Series-Total: 1
511 Message-Id: <909a00e13e9d78b575ae.240@test-hostname>
512 X-Mercurial-Series-Id: <909a00e13e9d78b575ae.240@test-hostname>
513 Message-Id: <f81ef97829467e868fc4.240@test-hostname>
514 X-Mercurial-Series-Id: <f81ef97829467e868fc4.240@test-hostname>
513 515 User-Agent: Mercurial-patchbomb/* (glob)
514 516 Date: Thu, 01 Jan 1970 00:04:00 +0000
515 From: Q <quux>
517 From: Q <quux> (no-py3 !)
518 From: =?iso-8859-1?q?Q?= <quux> (py3 !)
516 519 To: foo
517 520 Cc: bar
518 521
519 522 IyBIRyBjaGFuZ2VzZXQgcGF0Y2gKIyBVc2VyIHRlc3QKIyBEYXRlIDQgMAojICAgICAgVGh1IEph
520 biAwMSAwMDowMDowNCAxOTcwICswMDAwCiMgTm9kZSBJRCA5MDlhMDBlMTNlOWQ3OGI1NzVhZWVl
521 MjNkZGRiYWRhNDZkNWExNDNmCiMgUGFyZW50ICBmZjJjOWZhMjAxOGIxNWZhNzRiMzMzNjNiZGE5
522 NTI3MzIzZTJhOTlmCnV0Zi04IGNvbnRlbnQKCmRpZmYgLXIgZmYyYzlmYTIwMThiIC1yIDkwOWEw
523 MGUxM2U5ZCBkZXNjcmlwdGlvbgotLS0gL2Rldi9udWxsCVRodSBKYW4gMDEgMDA6MDA6MDAgMTk3
524 MCArMDAwMAorKysgYi9kZXNjcmlwdGlvbglUaHUgSmFuIDAxIDAwOjAwOjA0IDE5NzAgKzAwMDAK
525 QEAgLTAsMCArMSwzIEBACithIG11bHRpbGluZQorCitkZXNjcmlwdGlvbgpkaWZmIC1yIGZmMmM5
526 ZmEyMDE4YiAtciA5MDlhMDBlMTNlOWQgdXRmCi0tLSAvZGV2L251bGwJVGh1IEphbiAwMSAwMDow
527 MDowMCAxOTcwICswMDAwCisrKyBiL3V0ZglUaHUgSmFuIDAxIDAwOjAwOjA0IDE5NzAgKzAwMDAK
528 QEAgLTAsMCArMSwxIEBACitow7ZtbWEhCg==
523 biAwMSAwMDowMDowNCAxOTcwICswMDAwCiMgTm9kZSBJRCBmODFlZjk3ODI5NDY3ZTg2OGZjNDA1
524 ZmNjYmNmYTY2MjE3ZTRkM2U2CiMgUGFyZW50ICBmZjJjOWZhMjAxOGIxNWZhNzRiMzMzNjNiZGE5
525 NTI3MzIzZTJhOTlmCj9hCgpkaWZmIC1yIGZmMmM5ZmEyMDE4YiAtciBmODFlZjk3ODI5NDYgZGVz
526 Y3JpcHRpb24KLS0tIC9kZXYvbnVsbAlUaHUgSmFuIDAxIDAwOjAwOjAwIDE5NzAgKzAwMDAKKysr
527 IGIvZGVzY3JpcHRpb24JVGh1IEphbiAwMSAwMDowMDowNCAxOTcwICswMDAwCkBAIC0wLDAgKzEs
528 MyBAQAorYSBtdWx0aWxpbmUKKworZGVzY3JpcHRpb24KZGlmZiAtciBmZjJjOWZhMjAxOGIgLXIg
529 ZjgxZWY5NzgyOTQ2IHV0ZgotLS0gL2Rldi9udWxsCVRodSBKYW4gMDEgMDA6MDA6MDAgMTk3MCAr
530 MDAwMAorKysgYi91dGYJVGh1IEphbiAwMSAwMDowMDowNCAxOTcwICswMDAwCkBAIC0wLDAgKzEs
531 MSBAQAoraMO2bW1hIQo=
529 532
530 533
531 534 >>> import base64
@@ -540,18 +543,18 b' mime encoded mbox (base64):'
540 543 # User test
541 544 # Date 4 0
542 545 # Thu Jan 01 00:00:04 1970 +0000
543 # Node ID 909a00e13e9d78b575aeee23dddbada46d5a143f
546 # Node ID f81ef97829467e868fc405fccbcfa66217e4d3e6
544 547 # Parent ff2c9fa2018b15fa74b33363bda9527323e2a99f
545 utf-8 content
546
547 diff -r ff2c9fa2018b -r 909a00e13e9d description
548 ?a
549
550 diff -r ff2c9fa2018b -r f81ef9782946 description
548 551 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
549 552 +++ b/description Thu Jan 01 00:00:04 1970 +0000
550 553 @@ -0,0 +1,3 @@
551 554 +a multiline
552 555 +
553 556 +description
554 diff -r ff2c9fa2018b -r 909a00e13e9d utf
557 diff -r ff2c9fa2018b -r f81ef9782946 utf
555 558 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
556 559 +++ b/utf Thu Jan 01 00:00:04 1970 +0000
557 560 @@ -0,0 +1,1 @@
@@ -574,11 +577,11 b' no mime encoding for email --test:'
574 577 Content-Type: text/plain; charset="us-ascii"
575 578 Content-Transfer-Encoding: quoted-printable
576 579 Subject: [PATCH] long line
577 X-Mercurial-Node: a2ea8fc83dd8b93cfd86ac97b28287204ab806e1
580 X-Mercurial-Node: 0c7b871cb86b61a1c07e244393603c361e4a178d
578 581 X-Mercurial-Series-Index: 1
579 582 X-Mercurial-Series-Total: 1
580 Message-Id: <a2ea8fc83dd8b93cfd86.240@test-hostname>
581 X-Mercurial-Series-Id: <a2ea8fc83dd8b93cfd86.240@test-hostname>
583 Message-Id: <0c7b871cb86b61a1c07e.240@test-hostname>
584 X-Mercurial-Series-Id: <0c7b871cb86b61a1c07e.240@test-hostname>
582 585 User-Agent: Mercurial-patchbomb/* (glob)
583 586 Date: Thu, 01 Jan 1970 00:04:00 +0000
584 587 From: quux
@@ -589,11 +592,11 b' no mime encoding for email --test:'
589 592 # User test
590 593 # Date 4 0
591 594 # Thu Jan 01 00:00:04 1970 +0000
592 # Node ID a2ea8fc83dd8b93cfd86ac97b28287204ab806e1
593 # Parent 909a00e13e9d78b575aeee23dddbada46d5a143f
595 # Node ID 0c7b871cb86b61a1c07e244393603c361e4a178d
596 # Parent f81ef97829467e868fc405fccbcfa66217e4d3e6
594 597 long line
595 598
596 diff -r 909a00e13e9d -r a2ea8fc83dd8 long
599 diff -r f81ef9782946 -r 0c7b871cb86b long
597 600 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
598 601 +++ b/long Thu Jan 01 00:00:04 1970 +0000
599 602 @@ -0,0 +1,4 @@
@@ -628,11 +631,11 b' mime encoded mbox (quoted-printable):'
628 631 Content-Type: text/plain; charset="us-ascii"
629 632 Content-Transfer-Encoding: quoted-printable
630 633 Subject: [PATCH] long line
631 X-Mercurial-Node: a2ea8fc83dd8b93cfd86ac97b28287204ab806e1
634 X-Mercurial-Node: 0c7b871cb86b61a1c07e244393603c361e4a178d
632 635 X-Mercurial-Series-Index: 1
633 636 X-Mercurial-Series-Total: 1
634 Message-Id: <a2ea8fc83dd8b93cfd86.240@test-hostname>
635 X-Mercurial-Series-Id: <a2ea8fc83dd8b93cfd86.240@test-hostname>
637 Message-Id: <0c7b871cb86b61a1c07e.240@test-hostname>
638 X-Mercurial-Series-Id: <0c7b871cb86b61a1c07e.240@test-hostname>
636 639 User-Agent: Mercurial-patchbomb/* (glob)
637 640 Date: Thu, 01 Jan 1970 00:04:00 +0000
638 641 From: quux
@@ -643,11 +646,11 b' mime encoded mbox (quoted-printable):'
643 646 # User test
644 647 # Date 4 0
645 648 # Thu Jan 01 00:00:04 1970 +0000
646 # Node ID a2ea8fc83dd8b93cfd86ac97b28287204ab806e1
647 # Parent 909a00e13e9d78b575aeee23dddbada46d5a143f
649 # Node ID 0c7b871cb86b61a1c07e244393603c361e4a178d
650 # Parent f81ef97829467e868fc405fccbcfa66217e4d3e6
648 651 long line
649 652
650 diff -r 909a00e13e9d -r a2ea8fc83dd8 long
653 diff -r f81ef9782946 -r 0c7b871cb86b long
651 654 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
652 655 +++ b/long Thu Jan 01 00:00:04 1970 +0000
653 656 @@ -0,0 +1,4 @@
@@ -690,11 +693,11 b' iso-8859-1 mbox:'
690 693 Content-Type: text/plain; charset="iso-8859-1"
691 694 Content-Transfer-Encoding: quoted-printable
692 695 Subject: [PATCH] isolatin 8-bit encoding
693 X-Mercurial-Node: 240fb913fc1b7ff15ddb9f33e73d82bf5277c720
696 X-Mercurial-Node: 4d6f44f466c96d89f2e7e865a70ff41d8b6eee37
694 697 X-Mercurial-Series-Index: 1
695 698 X-Mercurial-Series-Total: 1
696 Message-Id: <240fb913fc1b7ff15ddb.300@test-hostname>
697 X-Mercurial-Series-Id: <240fb913fc1b7ff15ddb.300@test-hostname>
699 Message-Id: <4d6f44f466c96d89f2e7.300@test-hostname>
700 X-Mercurial-Series-Id: <4d6f44f466c96d89f2e7.300@test-hostname>
698 701 User-Agent: Mercurial-patchbomb/* (glob)
699 702 Date: Thu, 01 Jan 1970 00:05:00 +0000
700 703 From: quux
@@ -705,11 +708,11 b' iso-8859-1 mbox:'
705 708 # User test
706 709 # Date 5 0
707 710 # Thu Jan 01 00:00:05 1970 +0000
708 # Node ID 240fb913fc1b7ff15ddb9f33e73d82bf5277c720
709 # Parent a2ea8fc83dd8b93cfd86ac97b28287204ab806e1
711 # Node ID 4d6f44f466c96d89f2e7e865a70ff41d8b6eee37
712 # Parent 0c7b871cb86b61a1c07e244393603c361e4a178d
710 713 isolatin 8-bit encoding
711 714
712 diff -r a2ea8fc83dd8 -r 240fb913fc1b isolatin
715 diff -r 0c7b871cb86b -r 4d6f44f466c9 isolatin
713 716 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
714 717 +++ b/isolatin Thu Jan 01 00:00:05 1970 +0000
715 718 @@ -0,0 +1,1 @@
@@ -937,11 +940,11 b' test inline for single patch (quoted-pri'
937 940 Content-Type: multipart/mixed; boundary="===*==" (glob)
938 941 MIME-Version: 1.0
939 942 Subject: [PATCH] test
940 X-Mercurial-Node: a2ea8fc83dd8b93cfd86ac97b28287204ab806e1
943 X-Mercurial-Node: 0c7b871cb86b61a1c07e244393603c361e4a178d
941 944 X-Mercurial-Series-Index: 1
942 945 X-Mercurial-Series-Total: 1
943 Message-Id: <a2ea8fc83dd8b93cfd86.60@test-hostname>
944 X-Mercurial-Series-Id: <a2ea8fc83dd8b93cfd86.60@test-hostname>
946 Message-Id: <0c7b871cb86b61a1c07e.60@test-hostname>
947 X-Mercurial-Series-Id: <0c7b871cb86b61a1c07e.60@test-hostname>
945 948 User-Agent: Mercurial-patchbomb/* (glob)
946 949 Date: Thu, 01 Jan 1970 00:01:00 +0000
947 950 From: quux
@@ -958,11 +961,11 b' test inline for single patch (quoted-pri'
958 961 # User test
959 962 # Date 4 0
960 963 # Thu Jan 01 00:00:04 1970 +0000
961 # Node ID a2ea8fc83dd8b93cfd86ac97b28287204ab806e1
962 # Parent 909a00e13e9d78b575aeee23dddbada46d5a143f
964 # Node ID 0c7b871cb86b61a1c07e244393603c361e4a178d
965 # Parent f81ef97829467e868fc405fccbcfa66217e4d3e6
963 966 long line
964 967
965 diff -r 909a00e13e9d -r a2ea8fc83dd8 long
968 diff -r f81ef9782946 -r 0c7b871cb86b long
966 969 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
967 970 +++ b/long Thu Jan 01 00:00:04 1970 +0000
968 971 @@ -0,0 +1,4 @@
@@ -1088,10 +1091,10 b' test inline for multiple patches:'
1088 1091 Content-Type: multipart/mixed; boundary="===*==" (glob)
1089 1092 MIME-Version: 1.0
1090 1093 Subject: [PATCH 3 of 3] long line
1091 X-Mercurial-Node: a2ea8fc83dd8b93cfd86ac97b28287204ab806e1
1094 X-Mercurial-Node: 0c7b871cb86b61a1c07e244393603c361e4a178d
1092 1095 X-Mercurial-Series-Index: 3
1093 1096 X-Mercurial-Series-Total: 3
1094 Message-Id: <a2ea8fc83dd8b93cfd86.63@test-hostname>
1097 Message-Id: <0c7b871cb86b61a1c07e.63@test-hostname>
1095 1098 X-Mercurial-Series-Id: <8580ff50825a50c8f716.61@test-hostname>
1096 1099 In-Reply-To: <patchbomb.60@test-hostname>
1097 1100 References: <patchbomb.60@test-hostname>
@@ -1111,11 +1114,11 b' test inline for multiple patches:'
1111 1114 # User test
1112 1115 # Date 4 0
1113 1116 # Thu Jan 01 00:00:04 1970 +0000
1114 # Node ID a2ea8fc83dd8b93cfd86ac97b28287204ab806e1
1115 # Parent 909a00e13e9d78b575aeee23dddbada46d5a143f
1117 # Node ID 0c7b871cb86b61a1c07e244393603c361e4a178d
1118 # Parent f81ef97829467e868fc405fccbcfa66217e4d3e6
1116 1119 long line
1117 1120
1118 diff -r 909a00e13e9d -r a2ea8fc83dd8 long
1121 diff -r f81ef9782946 -r 0c7b871cb86b long
1119 1122 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1120 1123 +++ b/long Thu Jan 01 00:00:04 1970 +0000
1121 1124 @@ -0,0 +1,4 @@
@@ -1199,11 +1202,11 b' test attach for single patch (quoted-pri'
1199 1202 Content-Type: multipart/mixed; boundary="===*==" (glob)
1200 1203 MIME-Version: 1.0
1201 1204 Subject: [PATCH] test
1202 X-Mercurial-Node: a2ea8fc83dd8b93cfd86ac97b28287204ab806e1
1205 X-Mercurial-Node: 0c7b871cb86b61a1c07e244393603c361e4a178d
1203 1206 X-Mercurial-Series-Index: 1
1204 1207 X-Mercurial-Series-Total: 1
1205 Message-Id: <a2ea8fc83dd8b93cfd86.60@test-hostname>
1206 X-Mercurial-Series-Id: <a2ea8fc83dd8b93cfd86.60@test-hostname>
1208 Message-Id: <0c7b871cb86b61a1c07e.60@test-hostname>
1209 X-Mercurial-Series-Id: <0c7b871cb86b61a1c07e.60@test-hostname>
1207 1210 User-Agent: Mercurial-patchbomb/* (glob)
1208 1211 Date: Thu, 01 Jan 1970 00:01:00 +0000
1209 1212 From: quux
@@ -1229,11 +1232,11 b' test attach for single patch (quoted-pri'
1229 1232 # User test
1230 1233 # Date 4 0
1231 1234 # Thu Jan 01 00:00:04 1970 +0000
1232 # Node ID a2ea8fc83dd8b93cfd86ac97b28287204ab806e1
1233 # Parent 909a00e13e9d78b575aeee23dddbada46d5a143f
1235 # Node ID 0c7b871cb86b61a1c07e244393603c361e4a178d
1236 # Parent f81ef97829467e868fc405fccbcfa66217e4d3e6
1234 1237 long line
1235 1238
1236 diff -r 909a00e13e9d -r a2ea8fc83dd8 long
1239 diff -r f81ef9782946 -r 0c7b871cb86b long
1237 1240 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1238 1241 +++ b/long Thu Jan 01 00:00:04 1970 +0000
1239 1242 @@ -0,0 +1,4 @@
@@ -1438,10 +1441,10 b' test attach for multiple patches:'
1438 1441 Content-Type: multipart/mixed; boundary="===*==" (glob)
1439 1442 MIME-Version: 1.0
1440 1443 Subject: [PATCH 3 of 3] long line
1441 X-Mercurial-Node: a2ea8fc83dd8b93cfd86ac97b28287204ab806e1
1444 X-Mercurial-Node: 0c7b871cb86b61a1c07e244393603c361e4a178d
1442 1445 X-Mercurial-Series-Index: 3
1443 1446 X-Mercurial-Series-Total: 3
1444 Message-Id: <a2ea8fc83dd8b93cfd86.63@test-hostname>
1447 Message-Id: <0c7b871cb86b61a1c07e.63@test-hostname>
1445 1448 X-Mercurial-Series-Id: <8580ff50825a50c8f716.61@test-hostname>
1446 1449 In-Reply-To: <patchbomb.60@test-hostname>
1447 1450 References: <patchbomb.60@test-hostname>
@@ -1470,11 +1473,11 b' test attach for multiple patches:'
1470 1473 # User test
1471 1474 # Date 4 0
1472 1475 # Thu Jan 01 00:00:04 1970 +0000
1473 # Node ID a2ea8fc83dd8b93cfd86ac97b28287204ab806e1
1474 # Parent 909a00e13e9d78b575aeee23dddbada46d5a143f
1476 # Node ID 0c7b871cb86b61a1c07e244393603c361e4a178d
1477 # Parent f81ef97829467e868fc405fccbcfa66217e4d3e6
1475 1478 long line
1476 1479
1477 diff -r 909a00e13e9d -r a2ea8fc83dd8 long
1480 diff -r f81ef9782946 -r 0c7b871cb86b long
1478 1481 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1479 1482 +++ b/long Thu Jan 01 00:00:04 1970 +0000
1480 1483 @@ -0,0 +1,4 @@
@@ -1929,11 +1932,11 b' test inreplyto:'
1929 1932 Content-Type: text/plain; charset="us-ascii"
1930 1933 Content-Transfer-Encoding: 7bit
1931 1934 Subject: [PATCH] Added tag two, two.diff for changeset ff2c9fa2018b
1932 X-Mercurial-Node: 7aead2484924c445ad8ce2613df91f52f9e502ed
1935 X-Mercurial-Node: 9cea7492c36bdda2c72e7dd5f35f7fc367adeb2c
1933 1936 X-Mercurial-Series-Index: 1
1934 1937 X-Mercurial-Series-Total: 1
1935 Message-Id: <7aead2484924c445ad8c.60@test-hostname>
1936 X-Mercurial-Series-Id: <7aead2484924c445ad8c.60@test-hostname>
1938 Message-Id: <9cea7492c36bdda2c72e.60@test-hostname>
1939 X-Mercurial-Series-Id: <9cea7492c36bdda2c72e.60@test-hostname>
1937 1940 In-Reply-To: <baz>
1938 1941 References: <baz>
1939 1942 User-Agent: Mercurial-patchbomb/* (glob)
@@ -1946,11 +1949,11 b' test inreplyto:'
1946 1949 # User test
1947 1950 # Date 0 0
1948 1951 # Thu Jan 01 00:00:00 1970 +0000
1949 # Node ID 7aead2484924c445ad8ce2613df91f52f9e502ed
1950 # Parent 045ca29b1ea20e4940411e695e20e521f2f0f98e
1952 # Node ID 9cea7492c36bdda2c72e7dd5f35f7fc367adeb2c
1953 # Parent 3b775b32716d9b54291ccddf0a36ceea45449bfb
1951 1954 Added tag two, two.diff for changeset ff2c9fa2018b
1952 1955
1953 diff -r 045ca29b1ea2 -r 7aead2484924 .hgtags
1956 diff -r 3b775b32716d -r 9cea7492c36b .hgtags
1954 1957 --- a/.hgtags Thu Jan 01 00:00:00 1970 +0000
1955 1958 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
1956 1959 @@ -2,3 +2,5 @@
@@ -2397,9 +2400,12 b' test multi-address parsing:'
2397 2400 User-Agent: Mercurial-patchbomb/* (glob)
2398 2401 Date: Tue, 01 Jan 1980 00:01:00 +0000
2399 2402 From: quux
2400 To: spam <spam>, eggs, toast
2401 Cc: foo, bar@example.com, "A, B <>" <a@example.com>
2402 Bcc: "Quux, A." <quux>
2403 To: spam <spam>, eggs, toast (no-py3 !)
2404 Cc: foo, bar@example.com, "A, B <>" <a@example.com> (no-py3 !)
2405 Bcc: "Quux, A." <quux> (no-py3 !)
2406 To: =?iso-8859-1?q?spam?= <spam>, eggs, toast (py3 !)
2407 Cc: foo, bar@example.com, =?iso-8859-1?q?A=2C_B_=3C=3E?= <a@example.com> (py3 !)
2408 Bcc: =?iso-8859-1?q?Quux=2C_A=2E?= <quux> (py3 !)
2403 2409
2404 2410 # HG changeset patch
2405 2411 # User test
@@ -2601,17 +2607,17 b' test outgoing:'
2601 2607 |
2602 2608 o 9:2f9fa9b998c5 d
2603 2609 |
2604 | o 8:7aead2484924 Added tag two, two.diff for changeset ff2c9fa2018b
2610 | o 8:9cea7492c36b Added tag two, two.diff for changeset ff2c9fa2018b
2605 2611 | |
2606 | o 7:045ca29b1ea2 Added tag one, one.patch for changeset 97d72e5f12c7
2612 | o 7:3b775b32716d Added tag one, one.patch for changeset 97d72e5f12c7
2607 2613 | |
2608 | o 6:5d5ef15dfe5e Added tag zero, zero.foo for changeset 8580ff50825a
2614 | o 6:c41d7353114c Added tag zero, zero.foo for changeset 8580ff50825a
2609 2615 | |
2610 | o 5:240fb913fc1b isolatin 8-bit encoding
2616 | o 5:4d6f44f466c9 isolatin 8-bit encoding
2611 2617 | |
2612 | o 4:a2ea8fc83dd8 long line
2618 | o 4:0c7b871cb86b long line
2613 2619 | |
2614 | o 3:909a00e13e9d utf-8 content
2620 | o 3:f81ef9782946 \xe7a (esc)
2615 2621 | |
2616 2622 | o 2:ff2c9fa2018b c
2617 2623 |/
@@ -2673,15 +2679,16 b' test outgoing:'
2673 2679 @@ -0,0 +1,1 @@
2674 2680 +c
2675 2681
2676 displaying [PATCH 2 of 6] utf-8 content ...
2682 displaying [PATCH 2 of 6] \xe7a ... (esc)
2677 2683 MIME-Version: 1.0
2678 2684 Content-Type: text/plain; charset="iso-8859-1"
2679 2685 Content-Transfer-Encoding: quoted-printable
2680 Subject: [PATCH 2 of 6] utf-8 content
2681 X-Mercurial-Node: 909a00e13e9d78b575aeee23dddbada46d5a143f
2686 Subject: [PATCH 2 of 6] \xe7a (esc) (no-py3 !)
2687 Subject: =?utf-8?b?W1BBVENIIDIgb2YgNl0gw6dh?= (py3 !)
2688 X-Mercurial-Node: f81ef97829467e868fc405fccbcfa66217e4d3e6
2682 2689 X-Mercurial-Series-Index: 2
2683 2690 X-Mercurial-Series-Total: 6
2684 Message-Id: <909a00e13e9d78b575ae.315532862@test-hostname>
2691 Message-Id: <f81ef97829467e868fc4.315532862@test-hostname>
2685 2692 X-Mercurial-Series-Id: <ff2c9fa2018b15fa74b3.315532861@test-hostname>
2686 2693 In-Reply-To: <patchbomb.315532860@test-hostname>
2687 2694 References: <patchbomb.315532860@test-hostname>
@@ -2694,18 +2701,18 b' test outgoing:'
2694 2701 # User test
2695 2702 # Date 4 0
2696 2703 # Thu Jan 01 00:00:04 1970 +0000
2697 # Node ID 909a00e13e9d78b575aeee23dddbada46d5a143f
2704 # Node ID f81ef97829467e868fc405fccbcfa66217e4d3e6
2698 2705 # Parent ff2c9fa2018b15fa74b33363bda9527323e2a99f
2699 utf-8 content
2700
2701 diff -r ff2c9fa2018b -r 909a00e13e9d description
2706 =E7a
2707
2708 diff -r ff2c9fa2018b -r f81ef9782946 description
2702 2709 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2703 2710 +++ b/description Thu Jan 01 00:00:04 1970 +0000
2704 2711 @@ -0,0 +1,3 @@
2705 2712 +a multiline
2706 2713 +
2707 2714 +description
2708 diff -r ff2c9fa2018b -r 909a00e13e9d utf
2715 diff -r ff2c9fa2018b -r f81ef9782946 utf
2709 2716 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2710 2717 +++ b/utf Thu Jan 01 00:00:04 1970 +0000
2711 2718 @@ -0,0 +1,1 @@
@@ -2716,10 +2723,10 b' test outgoing:'
2716 2723 Content-Type: text/plain; charset="us-ascii"
2717 2724 Content-Transfer-Encoding: quoted-printable
2718 2725 Subject: [PATCH 3 of 6] long line
2719 X-Mercurial-Node: a2ea8fc83dd8b93cfd86ac97b28287204ab806e1
2726 X-Mercurial-Node: 0c7b871cb86b61a1c07e244393603c361e4a178d
2720 2727 X-Mercurial-Series-Index: 3
2721 2728 X-Mercurial-Series-Total: 6
2722 Message-Id: <a2ea8fc83dd8b93cfd86.315532863@test-hostname>
2729 Message-Id: <0c7b871cb86b61a1c07e.315532863@test-hostname>
2723 2730 X-Mercurial-Series-Id: <ff2c9fa2018b15fa74b3.315532861@test-hostname>
2724 2731 In-Reply-To: <patchbomb.315532860@test-hostname>
2725 2732 References: <patchbomb.315532860@test-hostname>
@@ -2732,11 +2739,11 b' test outgoing:'
2732 2739 # User test
2733 2740 # Date 4 0
2734 2741 # Thu Jan 01 00:00:04 1970 +0000
2735 # Node ID a2ea8fc83dd8b93cfd86ac97b28287204ab806e1
2736 # Parent 909a00e13e9d78b575aeee23dddbada46d5a143f
2742 # Node ID 0c7b871cb86b61a1c07e244393603c361e4a178d
2743 # Parent f81ef97829467e868fc405fccbcfa66217e4d3e6
2737 2744 long line
2738 2745
2739 diff -r 909a00e13e9d -r a2ea8fc83dd8 long
2746 diff -r f81ef9782946 -r 0c7b871cb86b long
2740 2747 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2741 2748 +++ b/long Thu Jan 01 00:00:04 1970 +0000
2742 2749 @@ -0,0 +1,4 @@
@@ -2763,10 +2770,10 b' test outgoing:'
2763 2770 Content-Type: text/plain; charset="iso-8859-1"
2764 2771 Content-Transfer-Encoding: quoted-printable
2765 2772 Subject: [PATCH 4 of 6] isolatin 8-bit encoding
2766 X-Mercurial-Node: 240fb913fc1b7ff15ddb9f33e73d82bf5277c720
2773 X-Mercurial-Node: 4d6f44f466c96d89f2e7e865a70ff41d8b6eee37
2767 2774 X-Mercurial-Series-Index: 4
2768 2775 X-Mercurial-Series-Total: 6
2769 Message-Id: <240fb913fc1b7ff15ddb.315532864@test-hostname>
2776 Message-Id: <4d6f44f466c96d89f2e7.315532864@test-hostname>
2770 2777 X-Mercurial-Series-Id: <ff2c9fa2018b15fa74b3.315532861@test-hostname>
2771 2778 In-Reply-To: <patchbomb.315532860@test-hostname>
2772 2779 References: <patchbomb.315532860@test-hostname>
@@ -2779,11 +2786,11 b' test outgoing:'
2779 2786 # User test
2780 2787 # Date 5 0
2781 2788 # Thu Jan 01 00:00:05 1970 +0000
2782 # Node ID 240fb913fc1b7ff15ddb9f33e73d82bf5277c720
2783 # Parent a2ea8fc83dd8b93cfd86ac97b28287204ab806e1
2789 # Node ID 4d6f44f466c96d89f2e7e865a70ff41d8b6eee37
2790 # Parent 0c7b871cb86b61a1c07e244393603c361e4a178d
2784 2791 isolatin 8-bit encoding
2785 2792
2786 diff -r a2ea8fc83dd8 -r 240fb913fc1b isolatin
2793 diff -r 0c7b871cb86b -r 4d6f44f466c9 isolatin
2787 2794 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2788 2795 +++ b/isolatin Thu Jan 01 00:00:05 1970 +0000
2789 2796 @@ -0,0 +1,1 @@
@@ -2794,10 +2801,10 b' test outgoing:'
2794 2801 Content-Type: text/plain; charset="us-ascii"
2795 2802 Content-Transfer-Encoding: 7bit
2796 2803 Subject: [PATCH 5 of 6] Added tag zero, zero.foo for changeset 8580ff50825a
2797 X-Mercurial-Node: 5d5ef15dfe5e7bd3a4ee154b5fff76c7945ec433
2804 X-Mercurial-Node: c41d7353114ccb07a50a822ad5ddf47051c88ec2
2798 2805 X-Mercurial-Series-Index: 5
2799 2806 X-Mercurial-Series-Total: 6
2800 Message-Id: <5d5ef15dfe5e7bd3a4ee.315532865@test-hostname>
2807 Message-Id: <c41d7353114ccb07a50a.315532865@test-hostname>
2801 2808 X-Mercurial-Series-Id: <ff2c9fa2018b15fa74b3.315532861@test-hostname>
2802 2809 In-Reply-To: <patchbomb.315532860@test-hostname>
2803 2810 References: <patchbomb.315532860@test-hostname>
@@ -2810,11 +2817,11 b' test outgoing:'
2810 2817 # User test
2811 2818 # Date 0 0
2812 2819 # Thu Jan 01 00:00:00 1970 +0000
2813 # Node ID 5d5ef15dfe5e7bd3a4ee154b5fff76c7945ec433
2814 # Parent 240fb913fc1b7ff15ddb9f33e73d82bf5277c720
2820 # Node ID c41d7353114ccb07a50a822ad5ddf47051c88ec2
2821 # Parent 4d6f44f466c96d89f2e7e865a70ff41d8b6eee37
2815 2822 Added tag zero, zero.foo for changeset 8580ff50825a
2816 2823
2817 diff -r 240fb913fc1b -r 5d5ef15dfe5e .hgtags
2824 diff -r 4d6f44f466c9 -r c41d7353114c .hgtags
2818 2825 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2819 2826 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2820 2827 @@ -0,0 +1,2 @@
@@ -85,7 +85,7 b' Known exception should be caught, but pr'
85 85 $ hg --config "extensions.t=$abspath" --config 'worker.numcpus=8' \
86 86 > test 100000.0 abort --traceback 2>&1 | egrep '(SystemExit|Abort)'
87 87 raise error.Abort(b'known exception')
88 mercurial.error.Abort: b'known exception' (py3 !)
88 mercurial.error.Abort: known exception (py3 !)
89 89 Abort: known exception (no-py3 !)
90 90 SystemExit: 255
91 91
1 NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (4094 lines changed) Show them Hide them
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now