Show More
@@ -0,0 +1,98 b'' | |||
|
1 | = Mercurial 6.3 = | |
|
2 | ||
|
3 | == New Features == | |
|
4 | ||
|
5 | * testlib: add `--raw-sha1` option to `f` | |
|
6 | * rhg: add `config.rhg` helptext | |
|
7 | * config: add alias from `hg help rhg` to `hg help rust` | |
|
8 | * rhg: add a config option to fall back immediately | |
|
9 | * bundle: introduce a --exact option | |
|
10 | * perf-bundle: add a new command to benchmark bundle creation time | |
|
11 | * perf-bundle: accept --rev arguments | |
|
12 | * perf-bundle: accept --type argument | |
|
13 | * perf-unbundle: add a perf command to time the unbundle operation | |
|
14 | * perf: introduce a benchmark for delta-find | |
|
15 | * contrib: add support for rhel9 | |
|
16 | * phase-shelve: Implement a 'shelve.store' experimental config | |
|
17 | * debug-delta-find: introduce a quiet mode | |
|
18 | * sort-revset: introduce a `random` variant | |
|
19 | * phase: introduce a dedicated requirement for the `archived` phase | |
|
20 | * rebase: add boolean config item rebase.store-source | |
|
21 | * rhg: make [rhg status -v] work when it needs no extra output | |
|
22 | * rhg: support "!" syntax for disabling extensions | |
|
23 | * rhg: add debugrhgsparse command to help figure out bugs in rhg | |
|
24 | * rhg: add sparse support | |
|
25 | * rhg-status: add support for narrow clones | |
|
26 | * templates: add filter to reverse list | |
|
27 | * contrib: add pull_logger extension | |
|
28 | * revset: handle wdir() in `roots()` | |
|
29 | * revset: handle wdir() in `sort(..., -topo)` | |
|
30 | * rhg: support tweakdefaults | |
|
31 | * rhg: parallellize computation of [unsure_is_modified] | |
|
32 | ||
|
33 | == Default Format Change == | |
|
34 | ||
|
35 | These changes affect newly created repositories (or new clones) done with | |
|
36 | Mercurial 6.3. | |
|
37 | ||
|
38 | == New Experimental Features == | |
|
39 | ||
|
40 | == Bug Fixes == | |
|
41 | ||
|
42 | * shelve: demonstrate that the state is different across platforms (issue6735) | |
|
43 | * shelve: in test for trailing whitespace, strip commit (issue6735) | |
|
44 | * shelve: remove strip and rely on prior state (issue6735) | |
|
45 | * tests: fix http-bad-server expected errors for python 3.10 (issue6643) | |
|
46 | * status: let `--no-copies` override `ui.statuscopies` | |
|
47 | * releasenotes: use re.MULTILINE mode when checking admonitions | |
|
48 | * rhg: fallback to slow path on invalid patterns in hgignore | |
|
49 | * Fix a bunch of leftover str/bytes issues from Python 3 migration | |
|
50 | * keepalive: ensure `close_all()` actually closes all cached connections | |
|
51 | * lfs: fix blob corruption when tranferring with workers on posix | |
|
52 | * lfs: avoid closing connections when the worker doesn't fork | |
|
53 | * dirstate-v2: update constant that wasn't kept in sync | |
|
54 | * dirstate-v2: fix edge case where entries aren't sorted | |
|
55 | * upgrade: no longer keep all revlogs in memory at any point | |
|
56 | * rust-status: save new dircache even if just invalidated | |
|
57 | * dirstate-v2: hash the source of the ignore patterns as well | |
|
58 | * rhg: fallback when encountering ellipsis revisions | |
|
59 | * shelve: handle empty parents and nodestoremove in shelvedstate (issue6748) | |
|
60 | * profile: prevent a crash when line number is unknown | |
|
61 | * tags-fnode-cache: do not repeatedly open the filelog in a loop | |
|
62 | * tags-fnode-cache: skip building a changectx in getfnode | |
|
63 | * rust: create wrapper struct to reduce `regex` contention issues | |
|
64 | ||
|
65 | == Backwards Compatibility Changes == | |
|
66 | ||
|
67 | * chg worker processes will now correctly load per-repository configuration | |
|
68 | when given a both a relative `--repository` path and an alternate working | |
|
69 | directory via `--cwd`. A side-effect of this change is that these workers | |
|
70 | will now return an error if hg cannot find the current working directory, | |
|
71 | even when a different directory is specified via `--cwd`. | |
|
72 | * phase: rename the requirement for internal-phase from `internal-phase` to `use-internal-phase` (see 74fb1842f8b962cf03d7cd5b841dbcf2ae065587) | |
|
73 | ||
|
74 | == Internal API Changes == | |
|
75 | ||
|
76 | == Miscellaneous == | |
|
77 | ||
|
78 | * sslutil: use proper attribute to select python 3.7+ | |
|
79 | * typing: suppress a few pyi-errors with more recent pytype | |
|
80 | * ci: bump pytype to 2022.03.29 | |
|
81 | * bundlespec: add documentation about existing option | |
|
82 | * subrepo: avoid opening console window for non-native subrepos on Windows | |
|
83 | * setup: unconditionally enable the `long-paths-support` option on Windows | |
|
84 | * setup: use the full executable manifest from `python.exe` | |
|
85 | * tests: work around libmagic bug in svn subrepo tests | |
|
86 | * packagelib: use python3 by default | |
|
87 | * Improve `hg bisect` performance | |
|
88 | * perf: properly process formatter option in perf::unbundle | |
|
89 | * compare-disco: miscellaneous display improvements | |
|
90 | * fsmonitor: better compatibility with newer Pythons | |
|
91 | * revlog: finer computation of "issnapshot" | |
|
92 | * rhg: don't fallback if `strip` or `rebase` are activated | |
|
93 | * perf: make perf::bundle compatible before 61ba04693d65 | |
|
94 | * perf: make perf::bundle compatible down to 5.2 | |
|
95 | * perf-unbundle: improve compatibility | |
|
96 | * run-tests: display the time it took to install Mercurial | |
|
97 | * mergetools: don't let meld open all changed files on startup | |
|
98 | * dirstate-v2: skip evaluation of hgignore regex on cached directories |
@@ -234,3 +234,5 b' 094a5fa3cf52f936e0de3f1e507c818bee5ece6b' | |||
|
234 | 234 | f69bffd00abe3a1b94d1032eb2c92e611d16a192 0 iQHNBAABCgA3FiEEH2b4zfZU6QXBHaBhoR4BzQ4F2VYFAmLifPsZHGFscGhhcmVAcmFwaGFlbGdvbWVzLmRldgAKCRChHgHNDgXZVukEC/oCa6AzaJlWh6G45Ap7BCWyB3EDWmcep07W8zRTfHQuuXslNFxRfj8O1DLVP05nDa1Uo2u1nkDxTH+x1fX0q4G8U/yLzCNsiBkCWSeEM8IeolarzzzvFe9Zk+UoRoRlc+vKAjxChtYTEnggQXjLdK+EdbXfEz2kJwdYlGX3lLr0Q2BKnBjSUvFe1Ma/1wxEjZIhDr6t7o8I/49QmPjK7RCYW1WBv77gnml0Oo8cxjDUR9cjqfeKtXKbMJiCsoXCS0hx3vJkBOzcs4ONEIw934is38qPNBBsaUjMrrqm0Mxs6yFricYqGVpmtNijsSRsfS7ZgNfaGaC2Bnu1E7P0A+AzPMPf/BP4uW9ixMbP1hNdr/6N41n19lkdjyQXVWGhB8RM+muf3jc6ZVvgZPMlxvFiz4/rP9nVOdrB96ssFZ9V2Ca/j2tU40AOgjI6sYsAR8pSSgmIdqe+DZQISHTT8D+4uVbtwYD49VklBcxudlbd3dAc5z9rVI3upsyByfRMROc= |
|
235 | 235 | b5c8524827d20fe2e0ca8fb1234a0fe35a1a36c7 0 iQHNBAABCgA3FiEEH2b4zfZU6QXBHaBhoR4BzQ4F2VYFAmMQxRoZHGFscGhhcmVAcmFwaGFlbGdvbWVzLmRldgAKCRChHgHNDgXZVm2gC/9HikIaOE49euIoLj6ctYsJY9PSQK4Acw7BXvdsTVMmW27o87NxH75bGBbmPQ57X1iuKLCQ1RoU3p2Eh1gPbkIsouWO3enBIfsFmkPtWQz28zpCrI9CUXg2ug4PGFPN9XyxNmhJ7vJ4Cst2tRxz9PBKUBO2EXJN1UKIdMvurIeT2sQrDQf1ePc85QkXx79231wZyF98smnV7UYU9ZPFnAzfcuRzdFn7UmH3KKxHTZQ6wAevj/fJXf5NdTlqbeNmq/t75/nGKXSFPWtRGfFs8JHGkkLgBiTJVsHYSqcnKNdVldIFUoJP4c2/SPyoBkqNvoIrr73XRo8tdDF1iY4ddmhHMSmKgSRqLnIEgew3Apa/IwPdolg+lMsOtcjgz4CB9agJ+O0+rdZd2ZUBNMN0nBSUh+lrkMjat8TJAlvut9h/6HAe4Dz8WheoWol8f8t1jLOJvbdvsMYi+Hf9CZjp7PlHT9y/TnDarcw2YIrf6Bv+Fm14ZDelu9VlF2zR1X8cofY= |
|
236 | 236 | dbdee8ac3e3fcdda1fa55b90c0a235125b7f8e6f 0 iQHNBAABCgA3FiEEH2b4zfZU6QXBHaBhoR4BzQ4F2VYFAmM77dQZHGFscGhhcmVAcmFwaGFlbGdvbWVzLmRldgAKCRChHgHNDgXZViOTC/sEPicecV3h3v47VAIUigyKNWpcJ+epbRRaH6gqHTkexvULOPL6nJrdfBHkNry1KRtOcjaxQvtWZM+TRCfqsE++Q3ZYakRpWKontb/8xQSbmENvbnElLh6k0STxN/JVc480us7viDG5pHS9DLsgbkHmdCv5KdmSE0hphRrWX+5X7RTqpAfCgdwTkacB5Geu9QfRnuYjz6lvqbs5ITKtBGUYbg3hKzw2894FHtMqV6qa5rk1ZMmVDbQfKQaMVG41UWNoN7bLESi69EmF4q5jsXdIbuBy0KtNXmB+gdAaHN03B5xtc+IsQZOTHEUNlMgov3yEVTcA6fSG9/Z+CMsdCbyQxqkwakbwWS1L2WcAsrkHyafvbNdR2FU34iYRWOck8IUg2Ffv7UFrHabJDy+nY7vcTLb0f7lV4jLXMWEt1hvXWMYek6Y4jtWahg6fjmAdD3Uf4BMfsTdnQKPvJpWXx303jnST3xvFvuqbbbDlhLfAB9M6kxVntvCVkMlMpe39+gM= |
|
237 | a3356ab610fc50000cf0ba55c424a4d96da11db7 0 iQHNBAABCgA3FiEEH2b4zfZU6QXBHaBhoR4BzQ4F2VYFAmNWr44ZHGFscGhhcmVAcmFwaGFlbGdvbWVzLmRldgAKCRChHgHNDgXZVjalC/9ddIeZ1qc3ykUZb+vKw+rZ6WS0rnDgrfFYBQFooK106lB+IC2PlghXSrY2hXn/7Dk95bK90S9AO4TFidDPiRYuBYdXR+G+CzmYFtCQzGBgGyrWgpUYsZUeA3VNqZ+Zbwn/vRNiFVNDsrFudjE6xEwaYdepmoXJsv3NdgZME7T0ZcDIujIa7ihiXvGFPVzMyF/VZg4QvdmerC4pvkeKC3KRNjhBkMQbf0GtQ4kpgMFBj5bmgXbq9rftL5yYy+rDiRQ0qzpOMHbdxvSZjPhK/do5M3rt2cjPxtF+7R3AHxQ6plOf0G89BONYebopY92OIyA3Qg9d/zIKDmibhgyxj4G9YU3+38gPEpsNeEw0fkyxhQbCY3QpNX4JGFaxq5GVCUywvVIuqoiOcQeXlTDN70zhAQHUx0rcGe1Lc6I+rT6Y2lNjJIdiCiMAWIl0D+4SVrLqdMYdSMXcBajTxOudb9KZnu03zNMXuLb8FFk1lFzkY7AcWA++d02f15P3sVZsDXE= | |
|
238 | 04f1dba53c961dfdb875c8469adc96fa999cfbed 0 iQHNBAABCgA3FiEEH2b4zfZU6QXBHaBhoR4BzQ4F2VYFAmNyC5sZHGFscGhhcmVAcmFwaGFlbGdvbWVzLmRldgAKCRChHgHNDgXZVqF+C/4uLaV/4nizZkWD3PjU1WyFYDg4bWDFOHb+PWuQ/3uoHXu1/EaYRnqmcDyOSJ99aXZBQ78rm9xhjxdmbklZ4ll1EGkqfTiYH+ld+rqE8iaqlc/DVy7pFXaenYwxletzO1OezzwF4XDLi6hcqzY9CXA3NM40vf6W4Rs5bEIi4eSbgJSNB1ll6ZzjvkU5bWTUoxSH+fxIJUuo27El2etdlKFQkS3/oTzWHejpVn6SQ1KyojTHMQBDRK4rqJBISp3gTf4TEezb0q0HTutJYDFdQNIRqx7V1Ao4Ei+YNbenJzcWJOA/2uk4V0AvZ4tnjgAzBYKwvIL1HfoQ0OmILeXjlVzV7Xu0G57lavum0sKkz/KZLKyYhKQHjYQLE7YMSM2y6/UEoFNN577vB47CHUq446PSMb8dGs2rmj66rj4iz5ml0yX+V9O2PpmIKoPAu1Y5/6zB9rCL76MRx182IW2m3rm4lsTfXPBPtea/OFt6ylxqCJRxaA0pht4FiAOvicPKXh4= |
@@ -247,3 +247,5 b' 094a5fa3cf52f936e0de3f1e507c818bee5ece6b' | |||
|
247 | 247 | f69bffd00abe3a1b94d1032eb2c92e611d16a192 6.2.1 |
|
248 | 248 | b5c8524827d20fe2e0ca8fb1234a0fe35a1a36c7 6.2.2 |
|
249 | 249 | dbdee8ac3e3fcdda1fa55b90c0a235125b7f8e6f 6.2.3 |
|
250 | a3356ab610fc50000cf0ba55c424a4d96da11db7 6.3rc0 | |
|
251 | 04f1dba53c961dfdb875c8469adc96fa999cfbed 6.3.0 |
@@ -2676,49 +2676,76 b' def perf_unbundle(ui, repo, fname, **opt' | |||
|
2676 | 2676 | """benchmark application of a bundle in a repository. |
|
2677 | 2677 | |
|
2678 | 2678 | This does not include the final transaction processing""" |
|
2679 | ||
|
2679 | 2680 | from mercurial import exchange |
|
2680 | 2681 | from mercurial import bundle2 |
|
2682 | from mercurial import transaction | |
|
2681 | 2683 | |
|
2682 | 2684 | opts = _byteskwargs(opts) |
|
2683 | 2685 | |
|
2684 | with repo.lock(): | |
|
2685 | bundle = [None, None] | |
|
2686 | orig_quiet = repo.ui.quiet | |
|
2687 | try: | |
|
2688 | repo.ui.quiet = True | |
|
2689 | with open(fname, mode="rb") as f: | |
|
2690 | ||
|
2691 | def noop_report(*args, **kwargs): | |
|
2692 | pass | |
|
2693 | ||
|
2694 | def setup(): | |
|
2695 | gen, tr = bundle | |
|
2696 | if tr is not None: | |
|
2697 | tr.abort() | |
|
2698 | bundle[:] = [None, None] | |
|
2699 | f.seek(0) | |
|
2700 | bundle[0] = exchange.readbundle(ui, f, fname) | |
|
2701 | bundle[1] = repo.transaction(b'perf::unbundle') | |
|
2702 | bundle[1]._report = noop_report # silence the transaction | |
|
2703 | ||
|
2704 | def apply(): | |
|
2705 | gen, tr = bundle | |
|
2706 | bundle2.applybundle( | |
|
2707 | repo, | |
|
2708 | gen, | |
|
2709 |
|
|
|
2710 | source=b'perf::unbundle', | |
|
2711 | url=fname, | |
|
2712 | ) | |
|
2713 | ||
|
2714 | timer, fm = gettimer(ui, opts) | |
|
2715 |
|
|
|
2716 | fm.end() | |
|
2717 | finally: | |
|
2718 | repo.ui.quiet == orig_quiet | |
|
2719 | gen, tr = bundle | |
|
2720 | if tr is not None: | |
|
2721 | tr.abort() | |
|
2686 | ### some compatibility hotfix | |
|
2687 | # | |
|
2688 | # the data attribute is dropped in 63edc384d3b7 a changeset introducing a | |
|
2689 | # critical regression that break transaction rollback for files that are | |
|
2690 | # de-inlined. | |
|
2691 | method = transaction.transaction._addentry | |
|
2692 | pre_63edc384d3b7 = "data" in getargspec(method).args | |
|
2693 | # the `detailed_exit_code` attribute is introduced in 33c0c25d0b0f | |
|
2694 | # a changeset that is a close descendant of 18415fc918a1, the changeset | |
|
2695 | # that conclude the fix run for the bug introduced in 63edc384d3b7. | |
|
2696 | args = getargspec(error.Abort.__init__).args | |
|
2697 | post_18415fc918a1 = "detailed_exit_code" in args | |
|
2698 | ||
|
2699 | old_max_inline = None | |
|
2700 | try: | |
|
2701 | if not (pre_63edc384d3b7 or post_18415fc918a1): | |
|
2702 | # disable inlining | |
|
2703 | old_max_inline = mercurial.revlog._maxinline | |
|
2704 | # large enough to never happen | |
|
2705 | mercurial.revlog._maxinline = 2 ** 50 | |
|
2706 | ||
|
2707 | with repo.lock(): | |
|
2708 | bundle = [None, None] | |
|
2709 | orig_quiet = repo.ui.quiet | |
|
2710 | try: | |
|
2711 | repo.ui.quiet = True | |
|
2712 | with open(fname, mode="rb") as f: | |
|
2713 | ||
|
2714 | def noop_report(*args, **kwargs): | |
|
2715 | pass | |
|
2716 | ||
|
2717 | def setup(): | |
|
2718 | gen, tr = bundle | |
|
2719 | if tr is not None: | |
|
2720 | tr.abort() | |
|
2721 | bundle[:] = [None, None] | |
|
2722 | f.seek(0) | |
|
2723 | bundle[0] = exchange.readbundle(ui, f, fname) | |
|
2724 | bundle[1] = repo.transaction(b'perf::unbundle') | |
|
2725 | # silence the transaction | |
|
2726 | bundle[1]._report = noop_report | |
|
2727 | ||
|
2728 | def apply(): | |
|
2729 | gen, tr = bundle | |
|
2730 | bundle2.applybundle( | |
|
2731 | repo, | |
|
2732 | gen, | |
|
2733 | tr, | |
|
2734 | source=b'perf::unbundle', | |
|
2735 | url=fname, | |
|
2736 | ) | |
|
2737 | ||
|
2738 | timer, fm = gettimer(ui, opts) | |
|
2739 | timer(apply, setup=setup) | |
|
2740 | fm.end() | |
|
2741 | finally: | |
|
2742 | repo.ui.quiet == orig_quiet | |
|
2743 | gen, tr = bundle | |
|
2744 | if tr is not None: | |
|
2745 | tr.abort() | |
|
2746 | finally: | |
|
2747 | if old_max_inline is not None: | |
|
2748 | mercurial.revlog._maxinline = old_max_inline | |
|
2722 | 2749 | |
|
2723 | 2750 | |
|
2724 | 2751 | @command( |
@@ -23,9 +23,9 b' from . import common' | |||
|
23 | 23 | # these do not work with demandimport, blacklist |
|
24 | 24 | demandimport.IGNORES.update( |
|
25 | 25 | [ |
|
26 |
|
|
|
27 |
|
|
|
28 |
|
|
|
26 | 'breezy.transactions', | |
|
27 | 'breezy.urlutils', | |
|
28 | 'ElementPath', | |
|
29 | 29 | ] |
|
30 | 30 | ) |
|
31 | 31 |
@@ -11,7 +11,7 b'' | |||
|
11 | 11 | |
|
12 | 12 | from mercurial import demandimport |
|
13 | 13 | |
|
14 |
demandimport.IGNORES.update([ |
|
|
14 | demandimport.IGNORES.update(['pkgutil', 'pkg_resources', '__main__']) | |
|
15 | 15 | |
|
16 | 16 | from mercurial import ( |
|
17 | 17 | encoding, |
@@ -601,14 +601,30 b' class _gitlfsremote:' | |||
|
601 | 601 | continue |
|
602 | 602 | raise |
|
603 | 603 | |
|
604 | # Until https multiplexing gets sorted out | |
|
604 | # Until https multiplexing gets sorted out. It's not clear if | |
|
605 | # ConnectionManager.set_ready() is externally synchronized for thread | |
|
606 | # safety with Windows workers. | |
|
605 | 607 | if self.ui.configbool(b'experimental', b'lfs.worker-enable'): |
|
608 | # The POSIX workers are forks of this process, so before spinning | |
|
609 | # them up, close all pooled connections. Otherwise, there's no way | |
|
610 | # to coordinate between them about who is using what, and the | |
|
611 | # transfers will get corrupted. | |
|
612 | # | |
|
613 | # TODO: add a function to keepalive.ConnectionManager to mark all | |
|
614 | # ready connections as in use, and roll that back after the fork? | |
|
615 | # That would allow the existing pool of connections in this process | |
|
616 | # to be preserved. | |
|
617 | def prefork(): | |
|
618 | for h in self.urlopener.handlers: | |
|
619 | getattr(h, "close_all", lambda: None)() | |
|
620 | ||
|
606 | 621 | oids = worker.worker( |
|
607 | 622 | self.ui, |
|
608 | 623 | 0.1, |
|
609 | 624 | transfer, |
|
610 | 625 | (), |
|
611 | 626 | sorted(objects, key=lambda o: o.get(b'oid')), |
|
627 | prefork=prefork, | |
|
612 | 628 | ) |
|
613 | 629 | else: |
|
614 | 630 | oids = transfer(sorted(objects, key=lambda o: o.get(b'oid'))) |
@@ -56,11 +56,11 b' assert TREE_METADATA_SIZE == TREE_METADA' | |||
|
56 | 56 | assert NODE_SIZE == NODE.size |
|
57 | 57 | |
|
58 | 58 | # match constant in mercurial/pure/parsers.py |
|
59 |
DIRSTATE_V2_DIRECTORY = 1 << |
|
|
59 | DIRSTATE_V2_DIRECTORY = 1 << 13 | |
|
60 | 60 | |
|
61 | 61 | |
|
62 | 62 | def parse_dirstate(map, copy_map, data, tree_metadata): |
|
63 |
"""parse a full v2-dirstate from a binary data into diction |
|
|
63 | """parse a full v2-dirstate from a binary data into dictionaries: | |
|
64 | 64 | |
|
65 | 65 | - map: a {path: entry} mapping that will be filled |
|
66 | 66 | - copy_map: a {path: copy-source} mapping that will be filled |
@@ -176,7 +176,7 b' class Node:' | |||
|
176 | 176 | def pack_dirstate(map, copy_map): |
|
177 | 177 | """ |
|
178 | 178 | Pack `map` and `copy_map` into the dirstate v2 binary format and return |
|
179 | the bytearray. | |
|
179 | the tuple of (data, metadata) bytearrays. | |
|
180 | 180 | |
|
181 | 181 | The on-disk format expects a tree-like structure where the leaves are |
|
182 | 182 | written first (and sorted per-directory), going up levels until the root |
@@ -191,7 +191,7 b' def pack_dirstate(map, copy_map):' | |||
|
191 | 191 | # Algorithm explanation |
|
192 | 192 | |
|
193 | 193 | This explanation does not talk about the different counters for tracked |
|
194 |
descend |
|
|
194 | descendants and storing the copies, but that work is pretty simple once this | |
|
195 | 195 | algorithm is in place. |
|
196 | 196 | |
|
197 | 197 | ## Building a subtree |
@@ -272,9 +272,9 b' def pack_dirstate(map, copy_map):' | |||
|
272 | 272 | ) |
|
273 | 273 | return data, tree_metadata |
|
274 | 274 | |
|
275 | sorted_map = sorted(map.items(), key=lambda x: x[0]) | |
|
275 | sorted_map = sorted(map.items(), key=lambda x: x[0].split(b"/")) | |
|
276 | 276 | |
|
277 |
# Use a stack to |
|
|
277 | # Use a stack to have to only remember the nodes we currently need | |
|
278 | 278 | # instead of building the entire tree in memory |
|
279 | 279 | stack = [] |
|
280 | 280 | current_node = Node(b"", None) |
@@ -486,6 +486,7 b' helptable = sorted(' | |||
|
486 | 486 | [ |
|
487 | 487 | b'rust', |
|
488 | 488 | b'rustext', |
|
489 | b'rhg', | |
|
489 | 490 | ], |
|
490 | 491 | _(b'Rust in Mercurial'), |
|
491 | 492 | loaddoc(b'rust'), |
@@ -2156,6 +2156,51 b' Alias definitions for revsets. See :hg:`' | |||
|
2156 | 2156 | Currently, only the rebase and absorb commands consider this configuration. |
|
2157 | 2157 | (EXPERIMENTAL) |
|
2158 | 2158 | |
|
2159 | ``rhg`` | |
|
2160 | ------- | |
|
2161 | ||
|
2162 | The pure Rust fast-path for Mercurial. See `rust/README.rst` in the Mercurial repository. | |
|
2163 | ||
|
2164 | ``fallback-executable`` | |
|
2165 | Path to the executable to run in a sub-process when falling back to | |
|
2166 | another implementation of Mercurial. | |
|
2167 | ||
|
2168 | ``fallback-immediately`` | |
|
2169 | Fall back to ``fallback-executable`` as soon as possible, regardless of | |
|
2170 | the `rhg.on-unsupported` configuration. Useful for debugging, for example to | |
|
2171 | bypass `rhg` if the deault `hg` points to `rhg`. | |
|
2172 | ||
|
2173 | Note that because this requires loading the configuration, it is possible | |
|
2174 | that `rhg` error out before being able to fall back. | |
|
2175 | ||
|
2176 | ``ignored-extensions`` | |
|
2177 | Controls which extensions should be ignored by `rhg`. By default, `rhg` | |
|
2178 | triggers the `rhg.on-unsupported` behavior any unsupported extensions. | |
|
2179 | Users can disable that behavior when they know that a given extension | |
|
2180 | does not need support from `rhg`. | |
|
2181 | ||
|
2182 | Expects a list of extension names, or ``*`` to ignore all extensions. | |
|
2183 | ||
|
2184 | Note: ``*:<suboption>`` is also a valid extension name for this | |
|
2185 | configuration option. | |
|
2186 | As of this writing, the only valid "global" suboption is ``required``. | |
|
2187 | ||
|
2188 | ``on-unsupported`` | |
|
2189 | Controls the behavior of `rhg` when detecting unsupported features. | |
|
2190 | ||
|
2191 | Possible values are `abort` (default), `abort-silent` and `fallback`. | |
|
2192 | ||
|
2193 | ``abort`` | |
|
2194 | Print an error message describing what feature is not supported, | |
|
2195 | and exit with code 252 | |
|
2196 | ||
|
2197 | ``abort-silent`` | |
|
2198 | Silently exit with code 252 | |
|
2199 | ||
|
2200 | ``fallback`` | |
|
2201 | Try running the fallback executable with the same parameters | |
|
2202 | (and trace the fallback reason, use `RUST_LOG=trace` to see). | |
|
2203 | ||
|
2159 | 2204 | ``share`` |
|
2160 | 2205 | --------- |
|
2161 | 2206 | |
@@ -2167,14 +2212,14 b' Alias definitions for revsets. See :hg:`' | |||
|
2167 | 2212 | `upgrade-allow`. |
|
2168 | 2213 | |
|
2169 | 2214 | ``abort`` |
|
2170 | Disallows running any command and aborts | |
|
2215 | Disallows running any command and aborts | |
|
2171 | 2216 | ``allow`` |
|
2172 | Respects the feature presence in the share source | |
|
2217 | Respects the feature presence in the share source | |
|
2173 | 2218 | ``upgrade-abort`` |
|
2174 |
|
|
|
2219 | Tries to upgrade the share to use share-safe; if it fails, aborts | |
|
2175 | 2220 | ``upgrade-allow`` |
|
2176 |
|
|
|
2177 | respecting the share source setting | |
|
2221 | Tries to upgrade the share; if it fails, continue by | |
|
2222 | respecting the share source setting | |
|
2178 | 2223 | |
|
2179 | 2224 | Check :hg:`help config.format.use-share-safe` for details about the |
|
2180 | 2225 | share-safe feature. |
@@ -2195,14 +2240,14 b' Alias definitions for revsets. See :hg:`' | |||
|
2195 | 2240 | `downgrade-allow`. |
|
2196 | 2241 | |
|
2197 | 2242 | ``abort`` |
|
2198 | Disallows running any command and aborts | |
|
2243 | Disallows running any command and aborts | |
|
2199 | 2244 | ``allow`` |
|
2200 | Respects the feature presence in the share source | |
|
2245 | Respects the feature presence in the share source | |
|
2201 | 2246 | ``downgrade-abort`` |
|
2202 |
|
|
|
2247 | Tries to downgrade the share to not use share-safe; if it fails, aborts | |
|
2203 | 2248 | ``downgrade-allow`` |
|
2204 |
|
|
|
2205 | if it fails, continue by respecting the shared source setting | |
|
2249 | Tries to downgrade the share to not use share-safe; | |
|
2250 | if it fails, continue by respecting the shared source setting | |
|
2206 | 2251 | |
|
2207 | 2252 | Check :hg:`help config.format.use-share-safe` for details about the |
|
2208 | 2253 | share-safe feature. |
@@ -283,8 +283,16 b' We define:' | |||
|
283 | 283 | in inclusion order. This definition is recursive, as included files can |
|
284 | 284 | themselves include more files. |
|
285 | 285 | |
|
286 | This hash is defined as the SHA-1 of the concatenation (in sorted | |
|
287 | order) of the "expanded contents" of each "root" ignore file. | |
|
286 | * "filepath" as the bytes of the ignore file path | |
|
287 | relative to the root of the repository if inside the repository, | |
|
288 | or the untouched path as defined in the configuration. | |
|
289 | ||
|
290 | This hash is defined as the SHA-1 of the following line format: | |
|
291 | ||
|
292 | <filepath> <sha1 of the "expanded contents">\n | |
|
293 | ||
|
294 | for each "root" ignore file. (in sorted order) | |
|
295 | ||
|
288 | 296 | (Note that computing this does not require actually concatenating |
|
289 | 297 | into a single contiguous byte sequence. |
|
290 | 298 | Instead a SHA-1 hasher object can be created |
@@ -89,6 +89,8 b' execution of certain commands while addi' | |||
|
89 | 89 | The only way of trying it out is by building it from source. Please refer to |
|
90 | 90 | `rust/README.rst` in the Mercurial repository. |
|
91 | 91 | |
|
92 | See `hg help config.rhg` for configuration options. | |
|
93 | ||
|
92 | 94 | Contributing |
|
93 | 95 | ============ |
|
94 | 96 |
@@ -166,7 +166,9 b' class ConnectionManager:' | |||
|
166 | 166 | if host: |
|
167 | 167 | return list(self._hostmap[host]) |
|
168 | 168 | else: |
|
169 |
return dict( |
|
|
169 | return dict( | |
|
170 | {h: list(conns) for (h, conns) in self._hostmap.items()} | |
|
171 | ) | |
|
170 | 172 | |
|
171 | 173 | |
|
172 | 174 | class KeepAliveHandler: |
@@ -700,6 +702,17 b' class HTTPConnection(httplib.HTTPConnect' | |||
|
700 | 702 | self.sentbytescount = 0 |
|
701 | 703 | self.receivedbytescount = 0 |
|
702 | 704 | |
|
705 | def __repr__(self): | |
|
706 | base = super(HTTPConnection, self).__repr__() | |
|
707 | local = "(unconnected)" | |
|
708 | s = self.sock | |
|
709 | if s: | |
|
710 | try: | |
|
711 | local = "%s:%d" % s.getsockname() | |
|
712 | except OSError: | |
|
713 | pass # Likely not connected | |
|
714 | return "<%s: %s <--> %s:%d>" % (base, local, self.host, self.port) | |
|
715 | ||
|
703 | 716 | |
|
704 | 717 | ######################################################################### |
|
705 | 718 | ##### TEST FUNCTIONS |
@@ -3738,8 +3738,10 b' def newreporequirements(ui, createopts):' | |||
|
3738 | 3738 | |
|
3739 | 3739 | if ui.configbool(b'format', b'use-dirstate-tracked-hint'): |
|
3740 | 3740 | version = ui.configint(b'format', b'use-dirstate-tracked-hint.version') |
|
3741 | msg = _("ignoring unknown tracked key version: %d\n") | |
|
3742 | hint = _("see `hg help config.format.use-dirstate-tracked-hint-version") | |
|
3741 | msg = _(b"ignoring unknown tracked key version: %d\n") | |
|
3742 | hint = _( | |
|
3743 | b"see `hg help config.format.use-dirstate-tracked-hint-version" | |
|
3744 | ) | |
|
3743 | 3745 | if version != 1: |
|
3744 | 3746 | ui.warn(msg % version, hint=hint) |
|
3745 | 3747 | else: |
@@ -278,9 +278,9 b' class shelvedstate:' | |||
|
278 | 278 | try: |
|
279 | 279 | d[b'originalwctx'] = bin(d[b'originalwctx']) |
|
280 | 280 | d[b'pendingctx'] = bin(d[b'pendingctx']) |
|
281 | d[b'parents'] = [bin(h) for h in d[b'parents'].split(b' ')] | |
|
281 | d[b'parents'] = [bin(h) for h in d[b'parents'].split(b' ') if h] | |
|
282 | 282 | d[b'nodestoremove'] = [ |
|
283 | bin(h) for h in d[b'nodestoremove'].split(b' ') | |
|
283 | bin(h) for h in d[b'nodestoremove'].split(b' ') if h | |
|
284 | 284 | ] |
|
285 | 285 | except (ValueError, KeyError) as err: |
|
286 | 286 | raise error.CorruptedState(stringutil.forcebytestr(err)) |
@@ -236,8 +236,8 b' class CodeSite:' | |||
|
236 | 236 | |
|
237 | 237 | def getsource(self, length): |
|
238 | 238 | if self.source is None: |
|
239 | lineno = self.lineno - 1 | |
|
240 | 239 | try: |
|
240 | lineno = self.lineno - 1 # lineno can be None | |
|
241 | 241 | with open(self.path, b'rb') as fp: |
|
242 | 242 | for i, line in enumerate(fp): |
|
243 | 243 | if i == lineno: |
@@ -773,7 +773,7 b' def display_hotpath(data, fp, limit=0.05' | |||
|
773 | 773 | codestring = codepattern % ( |
|
774 | 774 | prefix, |
|
775 | 775 | b'line'.rjust(spacing_len), |
|
776 | site.lineno, | |
|
776 | site.lineno if site.lineno is not None else -1, | |
|
777 | 777 | b''.ljust(max(0, 4 - len(str(site.lineno)))), |
|
778 | 778 | site.getsource(30), |
|
779 | 779 | ) |
@@ -491,11 +491,14 b' def _getfnodes(ui, repo, nodes):' | |||
|
491 | 491 | cachefnode = {} |
|
492 | 492 | validated_fnodes = set() |
|
493 | 493 | unknown_entries = set() |
|
494 | ||
|
495 | flog = None | |
|
494 | 496 | for node in nodes: |
|
495 | 497 | fnode = fnodescache.getfnode(node) |
|
496 | flog = repo.file(b'.hgtags') | |
|
497 | 498 | if fnode != repo.nullid: |
|
498 | 499 | if fnode not in validated_fnodes: |
|
500 | if flog is None: | |
|
501 | flog = repo.file(b'.hgtags') | |
|
499 | 502 | if flog.hasnode(fnode): |
|
500 | 503 | validated_fnodes.add(fnode) |
|
501 | 504 | else: |
@@ -758,8 +761,7 b' class hgtagsfnodescache:' | |||
|
758 | 761 | if node == self._repo.nullid: |
|
759 | 762 | return node |
|
760 | 763 | |
|
761 |
|
|
|
762 | rev = ctx.rev() | |
|
764 | rev = self._repo.changelog.rev(node) | |
|
763 | 765 | |
|
764 | 766 | self.lookupcount += 1 |
|
765 | 767 |
@@ -852,7 +852,7 b' class UpgradeOperation(BaseOperation):' | |||
|
852 | 852 | |
|
853 | 853 | return False |
|
854 | 854 | |
|
855 | def _write_labeled(self, l, label): | |
|
855 | def _write_labeled(self, l, label: bytes): | |
|
856 | 856 | """ |
|
857 | 857 | Utility function to aid writing of a list under one label |
|
858 | 858 | """ |
@@ -867,19 +867,19 b' class UpgradeOperation(BaseOperation):' | |||
|
867 | 867 | self.ui.write(_(b'requirements\n')) |
|
868 | 868 | self.ui.write(_(b' preserved: ')) |
|
869 | 869 | self._write_labeled( |
|
870 | self._preserved_requirements, "upgrade-repo.requirement.preserved" | |
|
870 | self._preserved_requirements, b"upgrade-repo.requirement.preserved" | |
|
871 | 871 | ) |
|
872 | 872 | self.ui.write((b'\n')) |
|
873 | 873 | if self._removed_requirements: |
|
874 | 874 | self.ui.write(_(b' removed: ')) |
|
875 | 875 | self._write_labeled( |
|
876 | self._removed_requirements, "upgrade-repo.requirement.removed" | |
|
876 | self._removed_requirements, b"upgrade-repo.requirement.removed" | |
|
877 | 877 | ) |
|
878 | 878 | self.ui.write((b'\n')) |
|
879 | 879 | if self._added_requirements: |
|
880 | 880 | self.ui.write(_(b' added: ')) |
|
881 | 881 | self._write_labeled( |
|
882 | self._added_requirements, "upgrade-repo.requirement.added" | |
|
882 | self._added_requirements, b"upgrade-repo.requirement.added" | |
|
883 | 883 | ) |
|
884 | 884 | self.ui.write((b'\n')) |
|
885 | 885 | self.ui.write(b'\n') |
@@ -893,7 +893,7 b' class UpgradeOperation(BaseOperation):' | |||
|
893 | 893 | self.ui.write(_(b'optimisations: ')) |
|
894 | 894 | self._write_labeled( |
|
895 | 895 | [a.name for a in optimisations], |
|
896 | "upgrade-repo.optimisation.performed", | |
|
896 | b"upgrade-repo.optimisation.performed", | |
|
897 | 897 | ) |
|
898 | 898 | self.ui.write(b'\n\n') |
|
899 | 899 |
@@ -233,18 +233,18 b' def _clonerevlogs(' | |||
|
233 | 233 | |
|
234 | 234 | # This is for the separate progress bars. |
|
235 | 235 | if rl_type & store.FILEFLAGS_CHANGELOG: |
|
236 |
changelogs[unencoded] = |
|
|
236 | changelogs[unencoded] = rl_type | |
|
237 | 237 | crevcount += len(rl) |
|
238 | 238 | csrcsize += datasize |
|
239 | 239 | crawsize += rawsize |
|
240 | 240 | elif rl_type & store.FILEFLAGS_MANIFESTLOG: |
|
241 |
manifests[unencoded] = |
|
|
241 | manifests[unencoded] = rl_type | |
|
242 | 242 | mcount += 1 |
|
243 | 243 | mrevcount += len(rl) |
|
244 | 244 | msrcsize += datasize |
|
245 | 245 | mrawsize += rawsize |
|
246 | 246 | elif rl_type & store.FILEFLAGS_FILELOG: |
|
247 |
filelogs[unencoded] = |
|
|
247 | filelogs[unencoded] = rl_type | |
|
248 | 248 | fcount += 1 |
|
249 | 249 | frevcount += len(rl) |
|
250 | 250 | fsrcsize += datasize |
@@ -289,7 +289,9 b' def _clonerevlogs(' | |||
|
289 | 289 | ) |
|
290 | 290 | ) |
|
291 | 291 | progress = srcrepo.ui.makeprogress(_(b'file revisions'), total=frevcount) |
|
292 |
for unencoded, |
|
|
292 | for unencoded, rl_type in sorted(filelogs.items()): | |
|
293 | oldrl = _revlogfrompath(srcrepo, rl_type, unencoded) | |
|
294 | ||
|
293 | 295 | newrl = _perform_clone( |
|
294 | 296 | ui, |
|
295 | 297 | dstrepo, |
@@ -329,7 +331,8 b' def _clonerevlogs(' | |||
|
329 | 331 | progress = srcrepo.ui.makeprogress( |
|
330 | 332 | _(b'manifest revisions'), total=mrevcount |
|
331 | 333 | ) |
|
332 |
for unencoded, |
|
|
334 | for unencoded, rl_type in sorted(manifests.items()): | |
|
335 | oldrl = _revlogfrompath(srcrepo, rl_type, unencoded) | |
|
333 | 336 | newrl = _perform_clone( |
|
334 | 337 | ui, |
|
335 | 338 | dstrepo, |
@@ -368,7 +371,8 b' def _clonerevlogs(' | |||
|
368 | 371 | progress = srcrepo.ui.makeprogress( |
|
369 | 372 | _(b'changelog revisions'), total=crevcount |
|
370 | 373 | ) |
|
371 |
for unencoded, |
|
|
374 | for unencoded, rl_type in sorted(changelogs.items()): | |
|
375 | oldrl = _revlogfrompath(srcrepo, rl_type, unencoded) | |
|
372 | 376 | newrl = _perform_clone( |
|
373 | 377 | ui, |
|
374 | 378 | dstrepo, |
@@ -125,7 +125,14 b' def worthwhile(ui, costperop, nops, thre' | |||
|
125 | 125 | |
|
126 | 126 | |
|
127 | 127 | def worker( |
|
128 | ui, costperarg, func, staticargs, args, hasretval=False, threadsafe=True | |
|
128 | ui, | |
|
129 | costperarg, | |
|
130 | func, | |
|
131 | staticargs, | |
|
132 | args, | |
|
133 | hasretval=False, | |
|
134 | threadsafe=True, | |
|
135 | prefork=None, | |
|
129 | 136 | ): |
|
130 | 137 | """run a function, possibly in parallel in multiple worker |
|
131 | 138 | processes. |
@@ -149,6 +156,10 b' def worker(' | |||
|
149 | 156 | threadsafe - whether work items are thread safe and can be executed using |
|
150 | 157 | a thread-based worker. Should be disabled for CPU heavy tasks that don't |
|
151 | 158 | release the GIL. |
|
159 | ||
|
160 | prefork - a parameterless Callable that is invoked prior to forking the | |
|
161 | process. fork() is only used on non-Windows platforms, but is also not | |
|
162 | called on POSIX platforms if the work amount doesn't warrant a worker. | |
|
152 | 163 | """ |
|
153 | 164 | enabled = ui.configbool(b'worker', b'enabled') |
|
154 | 165 | if enabled and _platformworker is _posixworker and not ismainthread(): |
@@ -157,11 +168,13 b' def worker(' | |||
|
157 | 168 | enabled = False |
|
158 | 169 | |
|
159 | 170 | if enabled and worthwhile(ui, costperarg, len(args), threadsafe=threadsafe): |
|
160 |
return _platformworker( |
|
|
171 | return _platformworker( | |
|
172 | ui, func, staticargs, args, hasretval, prefork=prefork | |
|
173 | ) | |
|
161 | 174 | return func(*staticargs + (args,)) |
|
162 | 175 | |
|
163 | 176 | |
|
164 | def _posixworker(ui, func, staticargs, args, hasretval): | |
|
177 | def _posixworker(ui, func, staticargs, args, hasretval, prefork=None): | |
|
165 | 178 | workers = _numworkers(ui) |
|
166 | 179 | oldhandler = signal.getsignal(signal.SIGINT) |
|
167 | 180 | signal.signal(signal.SIGINT, signal.SIG_IGN) |
@@ -207,6 +220,10 b' def _posixworker(ui, func, staticargs, a' | |||
|
207 | 220 | parentpid = os.getpid() |
|
208 | 221 | pipes = [] |
|
209 | 222 | retval = {} |
|
223 | ||
|
224 | if prefork: | |
|
225 | prefork() | |
|
226 | ||
|
210 | 227 | for pargs in partition(args, min(workers, len(args))): |
|
211 | 228 | # Every worker gets its own pipe to send results on, so we don't have to |
|
212 | 229 | # implement atomic writes larger than PIPE_BUF. Each forked process has |
@@ -316,7 +333,7 b' def _posixexitstatus(code):' | |||
|
316 | 333 | return -(os.WTERMSIG(code)) |
|
317 | 334 | |
|
318 | 335 | |
|
319 | def _windowsworker(ui, func, staticargs, args, hasretval): | |
|
336 | def _windowsworker(ui, func, staticargs, args, hasretval, prefork=None): | |
|
320 | 337 | class Worker(threading.Thread): |
|
321 | 338 | def __init__( |
|
322 | 339 | self, taskqueue, resultqueue, func, staticargs, *args, **kwargs |
@@ -13,12 +13,6 b' Mercurial XXX.' | |||
|
13 | 13 | |
|
14 | 14 | == Backwards Compatibility Changes == |
|
15 | 15 | |
|
16 | * chg worker processes will now correctly load per-repository configuration | |
|
17 | when given a both a relative `--repository` path and an alternate working | |
|
18 | directory via `--cwd`. A side-effect of this change is that these workers | |
|
19 | will now return an error if hg cannot find the current working directory, | |
|
20 | even when a different directory is specified via `--cwd`. | |
|
21 | ||
|
22 | 16 | == Internal API Changes == |
|
23 | 17 | |
|
24 | 18 | == Miscellaneous == |
@@ -479,6 +479,7 b' dependencies = [' | |||
|
479 | 479 | "same-file", |
|
480 | 480 | "sha-1 0.10.0", |
|
481 | 481 | "tempfile", |
|
482 | "thread_local", | |
|
482 | 483 | "twox-hash", |
|
483 | 484 | "zstd", |
|
484 | 485 | ] |
@@ -1120,6 +1121,15 b' dependencies = [' | |||
|
1120 | 1121 | ] |
|
1121 | 1122 | |
|
1122 | 1123 | [[package]] |
|
1124 | name = "thread_local" | |
|
1125 | version = "1.1.4" | |
|
1126 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
|
1127 | checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" | |
|
1128 | dependencies = [ | |
|
1129 | "once_cell", | |
|
1130 | ] | |
|
1131 | ||
|
1132 | [[package]] | |
|
1123 | 1133 | name = "time" |
|
1124 | 1134 | version = "0.1.44" |
|
1125 | 1135 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -29,13 +29,14 b' sha-1 = "0.10.0"' | |||
|
29 | 29 | twox-hash = "1.6.2" |
|
30 | 30 | same-file = "1.0.6" |
|
31 | 31 | tempfile = "3.1.0" |
|
32 | thread_local = "1.1.4" | |
|
32 | 33 | crossbeam-channel = "0.5.0" |
|
33 | 34 | micro-timer = "0.4.0" |
|
34 | 35 | log = "0.4.8" |
|
35 | 36 | memmap2 = { version = "0.5.3", features = ["stable_deref_trait"] } |
|
36 | 37 | zstd = "0.5.3" |
|
37 | 38 | format-bytes = "0.3.0" |
|
38 |
# once_cell 1.15 uses edition 2021, while the heptapod CI |
|
|
39 | # once_cell 1.15 uses edition 2021, while the heptapod CI | |
|
39 | 40 | # uses an old version of Cargo that doesn't support it. |
|
40 | 41 | once_cell = "=1.14.0" |
|
41 | 42 |
@@ -10,6 +10,7 b' use crate::dirstate_tree::on_disk::Dirst' | |||
|
10 | 10 | use crate::matchers::get_ignore_function; |
|
11 | 11 | use crate::matchers::Matcher; |
|
12 | 12 | use crate::utils::files::get_bytes_from_os_string; |
|
13 | use crate::utils::files::get_bytes_from_path; | |
|
13 | 14 | use crate::utils::files::get_path_from_bytes; |
|
14 | 15 | use crate::utils::hg_path::HgPath; |
|
15 | 16 | use crate::BadMatch; |
@@ -67,7 +68,7 b" pub fn status<'dirstate>(" | |||
|
67 | 68 | let (ignore_fn, warnings) = get_ignore_function( |
|
68 | 69 | ignore_files, |
|
69 | 70 | &root_dir, |
|
70 | &mut |_pattern_bytes| {}, | |
|
71 | &mut |_source, _pattern_bytes| {}, | |
|
71 | 72 | )?; |
|
72 | 73 | (ignore_fn, warnings, None) |
|
73 | 74 | } |
@@ -76,7 +77,24 b" pub fn status<'dirstate>(" | |||
|
76 | 77 | let (ignore_fn, warnings) = get_ignore_function( |
|
77 | 78 | ignore_files, |
|
78 | 79 | &root_dir, |
|
79 |
&mut |pattern_bytes| |
|
|
80 | &mut |source, pattern_bytes| { | |
|
81 | // If inside the repo, use the relative version to | |
|
82 | // make it deterministic inside tests. | |
|
83 | // The performance hit should be negligible. | |
|
84 | let source = source | |
|
85 | .strip_prefix(&root_dir) | |
|
86 | .unwrap_or(source); | |
|
87 | let source = get_bytes_from_path(source); | |
|
88 | ||
|
89 | let mut subhasher = Sha1::new(); | |
|
90 | subhasher.update(pattern_bytes); | |
|
91 | let patterns_hash = subhasher.finalize(); | |
|
92 | ||
|
93 | hasher.update(source); | |
|
94 | hasher.update(b" "); | |
|
95 | hasher.update(patterns_hash); | |
|
96 | hasher.update(b"\n"); | |
|
97 | }, | |
|
80 | 98 | )?; |
|
81 | 99 | let new_hash = *hasher.finalize().as_ref(); |
|
82 | 100 | let changed = new_hash != dmap.ignore_patterns_hash; |
@@ -122,8 +140,8 b" pub fn status<'dirstate>(" | |||
|
122 | 140 | ignore_fn, |
|
123 | 141 | outcome: Mutex::new(outcome), |
|
124 | 142 | ignore_patterns_have_changed: patterns_changed, |
|
125 | new_cachable_directories: Default::default(), | |
|
126 | outated_cached_directories: Default::default(), | |
|
143 | new_cacheable_directories: Default::default(), | |
|
144 | outdated_cached_directories: Default::default(), | |
|
127 | 145 | filesystem_time_at_status_start, |
|
128 | 146 | }; |
|
129 | 147 | let is_at_repo_root = true; |
@@ -147,12 +165,12 b" pub fn status<'dirstate>(" | |||
|
147 | 165 | is_at_repo_root, |
|
148 | 166 | )?; |
|
149 | 167 | let mut outcome = common.outcome.into_inner().unwrap(); |
|
150 | let new_cachable = common.new_cachable_directories.into_inner().unwrap(); | |
|
151 | let outdated = common.outated_cached_directories.into_inner().unwrap(); | |
|
168 | let new_cacheable = common.new_cacheable_directories.into_inner().unwrap(); | |
|
169 | let outdated = common.outdated_cached_directories.into_inner().unwrap(); | |
|
152 | 170 | |
|
153 | 171 | outcome.dirty = common.ignore_patterns_have_changed == Some(true) |
|
154 | 172 | || !outdated.is_empty() |
|
155 | || (!new_cachable.is_empty() | |
|
173 | || (!new_cacheable.is_empty() | |
|
156 | 174 | && dmap.dirstate_version == DirstateVersion::V2); |
|
157 | 175 | |
|
158 | 176 | // Remove outdated mtimes before adding new mtimes, in case a given |
@@ -160,7 +178,7 b" pub fn status<'dirstate>(" | |||
|
160 | 178 | for path in &outdated { |
|
161 | 179 | dmap.clear_cached_mtime(path)?; |
|
162 | 180 | } |
|
163 | for (path, mtime) in &new_cachable { | |
|
181 | for (path, mtime) in &new_cacheable { | |
|
164 | 182 | dmap.set_cached_mtime(path, *mtime)?; |
|
165 | 183 | } |
|
166 | 184 | |
@@ -175,9 +193,11 b" struct StatusCommon<'a, 'tree, 'on_disk:" | |||
|
175 | 193 | matcher: &'a (dyn Matcher + Sync), |
|
176 | 194 | ignore_fn: IgnoreFnType<'a>, |
|
177 | 195 | outcome: Mutex<DirstateStatus<'on_disk>>, |
|
178 | new_cachable_directories: | |
|
196 | /// New timestamps of directories to be used for caching their readdirs | |
|
197 | new_cacheable_directories: | |
|
179 | 198 | Mutex<Vec<(Cow<'on_disk, HgPath>, TruncatedTimestamp)>>, |
|
180 | outated_cached_directories: Mutex<Vec<Cow<'on_disk, HgPath>>>, | |
|
199 | /// Used to invalidate the readdir cache of directories | |
|
200 | outdated_cached_directories: Mutex<Vec<Cow<'on_disk, HgPath>>>, | |
|
181 | 201 | |
|
182 | 202 | /// Whether ignore files like `.hgignore` have changed since the previous |
|
183 | 203 | /// time a `status()` call wrote their hash to the dirstate. `None` means |
@@ -305,17 +325,18 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||
|
305 | 325 | fn check_for_outdated_directory_cache( |
|
306 | 326 | &self, |
|
307 | 327 | dirstate_node: &NodeRef<'tree, 'on_disk>, |
|
308 |
) -> Result< |
|
|
328 | ) -> Result<bool, DirstateV2ParseError> { | |
|
309 | 329 | if self.ignore_patterns_have_changed == Some(true) |
|
310 | 330 | && dirstate_node.cached_directory_mtime()?.is_some() |
|
311 | 331 | { |
|
312 | self.outated_cached_directories.lock().unwrap().push( | |
|
332 | self.outdated_cached_directories.lock().unwrap().push( | |
|
313 | 333 | dirstate_node |
|
314 | 334 | .full_path_borrowed(self.dmap.on_disk)? |
|
315 | 335 | .detach_from_tree(), |
|
316 | ) | |
|
336 | ); | |
|
337 | return Ok(true); | |
|
317 | 338 | } |
|
318 |
Ok( |
|
|
339 | Ok(false) | |
|
319 | 340 | } |
|
320 | 341 | |
|
321 | 342 | /// If this returns true, we can get accurate results by only using |
@@ -487,7 +508,8 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||
|
487 | 508 | dirstate_node: NodeRef<'tree, 'on_disk>, |
|
488 | 509 | has_ignored_ancestor: &'ancestor HasIgnoredAncestor<'ancestor>, |
|
489 | 510 | ) -> Result<(), DirstateV2ParseError> { |
|
490 | self.check_for_outdated_directory_cache(&dirstate_node)?; | |
|
511 | let outdated_dircache = | |
|
512 | self.check_for_outdated_directory_cache(&dirstate_node)?; | |
|
491 | 513 | let hg_path = &dirstate_node.full_path_borrowed(self.dmap.on_disk)?; |
|
492 | 514 | let file_or_symlink = fs_entry.is_file() || fs_entry.is_symlink(); |
|
493 | 515 | if !file_or_symlink { |
@@ -522,6 +544,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||
|
522 | 544 | children_all_have_dirstate_node_or_are_ignored, |
|
523 | 545 | fs_entry, |
|
524 | 546 | dirstate_node, |
|
547 | outdated_dircache, | |
|
525 | 548 | )? |
|
526 | 549 | } else { |
|
527 | 550 | if file_or_symlink && self.matcher.matches(&hg_path) { |
@@ -561,11 +584,17 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||
|
561 | 584 | Ok(()) |
|
562 | 585 | } |
|
563 | 586 | |
|
587 | /// Save directory mtime if applicable. | |
|
588 | /// | |
|
589 | /// `outdated_directory_cache` is `true` if we've just invalidated the | |
|
590 | /// cache for this directory in `check_for_outdated_directory_cache`, | |
|
591 | /// which forces the update. | |
|
564 | 592 | fn maybe_save_directory_mtime( |
|
565 | 593 | &self, |
|
566 | 594 | children_all_have_dirstate_node_or_are_ignored: bool, |
|
567 | 595 | directory_entry: &DirEntry, |
|
568 | 596 | dirstate_node: NodeRef<'tree, 'on_disk>, |
|
597 | outdated_directory_cache: bool, | |
|
569 | 598 | ) -> Result<(), DirstateV2ParseError> { |
|
570 | 599 | if !children_all_have_dirstate_node_or_are_ignored { |
|
571 | 600 | return Ok(()); |
@@ -635,17 +664,18 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||
|
635 | 664 | // We deem this scenario (unlike the previous one) to be |
|
636 | 665 | // unlikely enough in practice. |
|
637 | 666 | |
|
638 | let is_up_to_date = | |
|
639 |
|
|
|
640 | cached.likely_equal(directory_mtime) | |
|
641 | } else { | |
|
642 |
|
|
|
643 |
|
|
|
667 | let is_up_to_date = if let Some(cached) = | |
|
668 | dirstate_node.cached_directory_mtime()? | |
|
669 | { | |
|
670 | !outdated_directory_cache && cached.likely_equal(directory_mtime) | |
|
671 | } else { | |
|
672 | false | |
|
673 | }; | |
|
644 | 674 | if !is_up_to_date { |
|
645 | 675 | let hg_path = dirstate_node |
|
646 | 676 | .full_path_borrowed(self.dmap.on_disk)? |
|
647 | 677 | .detach_from_tree(); |
|
648 | self.new_cachable_directories | |
|
678 | self.new_cacheable_directories | |
|
649 | 679 | .lock() |
|
650 | 680 | .unwrap() |
|
651 | 681 | .push((hg_path, directory_mtime)) |
@@ -412,11 +412,11 b' pub fn parse_pattern_file_contents(' | |||
|
412 | 412 | pub fn read_pattern_file( |
|
413 | 413 | file_path: &Path, |
|
414 | 414 | warn: bool, |
|
415 | inspect_pattern_bytes: &mut impl FnMut(&[u8]), | |
|
415 | inspect_pattern_bytes: &mut impl FnMut(&Path, &[u8]), | |
|
416 | 416 | ) -> Result<(Vec<IgnorePattern>, Vec<PatternFileWarning>), PatternError> { |
|
417 | 417 | match std::fs::read(file_path) { |
|
418 | 418 | Ok(contents) => { |
|
419 | inspect_pattern_bytes(&contents); | |
|
419 | inspect_pattern_bytes(file_path, &contents); | |
|
420 | 420 | parse_pattern_file_contents(&contents, file_path, None, warn) |
|
421 | 421 | } |
|
422 | 422 | Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(( |
@@ -455,7 +455,7 b' pub type PatternResult<T> = Result<T, Pa' | |||
|
455 | 455 | pub fn get_patterns_from_file( |
|
456 | 456 | pattern_file: &Path, |
|
457 | 457 | root_dir: &Path, |
|
458 | inspect_pattern_bytes: &mut impl FnMut(&[u8]), | |
|
458 | inspect_pattern_bytes: &mut impl FnMut(&Path, &[u8]), | |
|
459 | 459 | ) -> PatternResult<(Vec<IgnorePattern>, Vec<PatternFileWarning>)> { |
|
460 | 460 | let (patterns, mut warnings) = |
|
461 | 461 | read_pattern_file(pattern_file, true, inspect_pattern_bytes)?; |
@@ -573,6 +573,39 b' impl DifferenceMatcher {' | |||
|
573 | 573 | } |
|
574 | 574 | } |
|
575 | 575 | |
|
576 | /// Wraps [`regex::bytes::Regex`] to improve performance in multithreaded | |
|
577 | /// contexts. | |
|
578 | /// | |
|
579 | /// The `status` algorithm makes heavy use of threads, and calling `is_match` | |
|
580 | /// from many threads at once is prone to contention, probably within the | |
|
581 | /// scratch space needed as the regex DFA is built lazily. | |
|
582 | /// | |
|
583 | /// We are in the process of raising the issue upstream, but for now | |
|
584 | /// the workaround used here is to store the `Regex` in a lazily populated | |
|
585 | /// thread-local variable, sharing the initial read-only compilation, but | |
|
586 | /// not the lazy dfa scratch space mentioned above. | |
|
587 | /// | |
|
588 | /// This reduces the contention observed with 16+ threads, but does not | |
|
589 | /// completely remove it. Hopefully this can be addressed upstream. | |
|
590 | struct RegexMatcher { | |
|
591 | /// Compiled at the start of the status algorithm, used as a base for | |
|
592 | /// cloning in each thread-local `self.local`, thus sharing the expensive | |
|
593 | /// first compilation. | |
|
594 | base: regex::bytes::Regex, | |
|
595 | /// Thread-local variable that holds the `Regex` that is actually queried | |
|
596 | /// from each thread. | |
|
597 | local: thread_local::ThreadLocal<regex::bytes::Regex>, | |
|
598 | } | |
|
599 | ||
|
600 | impl RegexMatcher { | |
|
601 | /// Returns whether the path matches the stored `Regex`. | |
|
602 | pub fn is_match(&self, path: &HgPath) -> bool { | |
|
603 | self.local | |
|
604 | .get_or(|| self.base.clone()) | |
|
605 | .is_match(path.as_bytes()) | |
|
606 | } | |
|
607 | } | |
|
608 | ||
|
576 | 609 | /// Returns a function that matches an `HgPath` against the given regex |
|
577 | 610 | /// pattern. |
|
578 | 611 | /// |
@@ -580,9 +613,7 b' impl DifferenceMatcher {' | |||
|
580 | 613 | /// underlying engine (the `regex` crate), for instance anything with |
|
581 | 614 | /// back-references. |
|
582 | 615 | #[timed] |
|
583 | fn re_matcher( | |
|
584 | pattern: &[u8], | |
|
585 | ) -> PatternResult<impl Fn(&HgPath) -> bool + Sync> { | |
|
616 | fn re_matcher(pattern: &[u8]) -> PatternResult<RegexMatcher> { | |
|
586 | 617 | use std::io::Write; |
|
587 | 618 | |
|
588 | 619 | // The `regex` crate adds `.*` to the start and end of expressions if there |
@@ -611,7 +642,10 b' fn re_matcher(' | |||
|
611 | 642 | .build() |
|
612 | 643 | .map_err(|e| PatternError::UnsupportedSyntax(e.to_string()))?; |
|
613 | 644 | |
|
614 | Ok(move |path: &HgPath| re.is_match(path.as_bytes())) | |
|
645 | Ok(RegexMatcher { | |
|
646 | base: re, | |
|
647 | local: Default::default(), | |
|
648 | }) | |
|
615 | 649 | } |
|
616 | 650 | |
|
617 | 651 | /// Returns the regex pattern and a function that matches an `HgPath` against |
@@ -638,7 +672,7 b" fn build_regex_match<'a, 'b>(" | |||
|
638 | 672 | let func = if !(regexps.is_empty()) { |
|
639 | 673 | let matcher = re_matcher(&full_regex)?; |
|
640 | 674 | let func = move |filename: &HgPath| { |
|
641 | exact_set.contains(filename) || matcher(filename) | |
|
675 | exact_set.contains(filename) || matcher.is_match(filename) | |
|
642 | 676 | }; |
|
643 | 677 | Box::new(func) as IgnoreFnType |
|
644 | 678 | } else { |
@@ -838,7 +872,7 b" fn build_match<'a, 'b>(" | |||
|
838 | 872 | pub fn get_ignore_matcher<'a>( |
|
839 | 873 | mut all_pattern_files: Vec<PathBuf>, |
|
840 | 874 | root_dir: &Path, |
|
841 | inspect_pattern_bytes: &mut impl FnMut(&[u8]), | |
|
875 | inspect_pattern_bytes: &mut impl FnMut(&Path, &[u8]), | |
|
842 | 876 | ) -> PatternResult<(IncludeMatcher<'a>, Vec<PatternFileWarning>)> { |
|
843 | 877 | let mut all_patterns = vec![]; |
|
844 | 878 | let mut all_warnings = vec![]; |
@@ -871,7 +905,7 b" pub fn get_ignore_matcher<'a>(" | |||
|
871 | 905 | pub fn get_ignore_function<'a>( |
|
872 | 906 | all_pattern_files: Vec<PathBuf>, |
|
873 | 907 | root_dir: &Path, |
|
874 | inspect_pattern_bytes: &mut impl FnMut(&[u8]), | |
|
908 | inspect_pattern_bytes: &mut impl FnMut(&Path, &[u8]), | |
|
875 | 909 | ) -> PatternResult<(IgnoreFnType<'a>, Vec<PatternFileWarning>)> { |
|
876 | 910 | let res = |
|
877 | 911 | get_ignore_matcher(all_pattern_files, root_dir, inspect_pattern_bytes); |
@@ -447,6 +447,11 b" impl<'a> RevlogEntry<'a> {" | |||
|
447 | 447 | ) { |
|
448 | 448 | Ok(data) |
|
449 | 449 | } else { |
|
450 | if (self.flags & REVISION_FLAG_ELLIPSIS) != 0 { | |
|
451 | return Err(HgError::unsupported( | |
|
452 | "ellipsis revisions are not supported by rhg", | |
|
453 | )); | |
|
454 | } | |
|
450 | 455 | Err(corrupted(format!( |
|
451 | 456 | "hash check failed for revision {}", |
|
452 | 457 | self.rev |
@@ -19,35 +19,9 b' The executable can then be found at `rus' | |||
|
19 | 19 | |
|
20 | 20 | `rhg` reads Mercurial configuration from the usual sources: |
|
21 | 21 | the user’s `~/.hgrc`, a repository’s `.hg/hgrc`, command line `--config`, etc. |
|
22 |
It has some specific configuration in the `[rhg]` section |
|
|
23 | ||
|
24 | * `on-unsupported` governs the behavior of `rhg` when it encounters something | |
|
25 | that it does not support but “full” `hg` possibly does. | |
|
26 | This can be in configuration, on the command line, or in a repository. | |
|
27 | ||
|
28 | - `abort`, the default value, makes `rhg` print a message to stderr | |
|
29 | to explain what is not supported, then terminate with a 252 exit code. | |
|
30 | - `abort-silent` makes it terminate with the same exit code, | |
|
31 | but without printing anything. | |
|
32 | - `fallback` makes it silently call a (presumably Python-based) `hg` | |
|
33 | subprocess with the same command-line parameters. | |
|
34 | The `rhg.fallback-executable` configuration must be set. | |
|
22 | It has some specific configuration in the `[rhg]` section. | |
|
35 | 23 | |
|
36 | * `fallback-executable`: path to the executable to run in a sub-process | |
|
37 | when falling back to a Python implementation of Mercurial. | |
|
38 | ||
|
39 | * `allowed-extensions`: a list of extension names that `rhg` can ignore. | |
|
40 | ||
|
41 | Mercurial extensions can modify the behavior of existing `hg` sub-commands, | |
|
42 | including those that `rhg` otherwise supports. | |
|
43 | Because it cannot load Python extensions, finding them | |
|
44 | enabled in configuration is considered “unsupported” (see above). | |
|
45 | A few exceptions are made for extensions that `rhg` does know about, | |
|
46 | with the Rust implementation duplicating their behavior. | |
|
47 | ||
|
48 | This configuration makes additional exceptions: `rhg` will proceed even if | |
|
49 | those extensions are enabled. | |
|
50 | ||
|
24 | See `hg help config.rhg` for details. | |
|
51 | 25 | |
|
52 | 26 | ## Installation and configuration example |
|
53 | 27 |
@@ -25,7 +25,7 b' pub fn run(invocation: &crate::CliInvoca' | |||
|
25 | 25 | let (ignore_matcher, warnings) = get_ignore_matcher( |
|
26 | 26 | vec![ignore_file], |
|
27 | 27 | &repo.working_directory_path().to_owned(), |
|
28 | &mut |_pattern_bytes| (), | |
|
28 | &mut |_source, _pattern_bytes| (), | |
|
29 | 29 | ) |
|
30 | 30 | .map_err(|e| StatusError::from(e))?; |
|
31 | 31 |
@@ -221,7 +221,12 b' impl From<(RevlogError, &str)> for Comma' | |||
|
221 | 221 | |
|
222 | 222 | impl From<StatusError> for CommandError { |
|
223 | 223 | fn from(error: StatusError) -> Self { |
|
224 | CommandError::abort(format!("{}", error)) | |
|
224 | match error { | |
|
225 | StatusError::Pattern(_) => { | |
|
226 | CommandError::unsupported(format!("{}", error)) | |
|
227 | } | |
|
228 | _ => CommandError::abort(format!("{}", error)), | |
|
229 | } | |
|
225 | 230 | } |
|
226 | 231 | } |
|
227 | 232 |
@@ -301,7 +301,7 b' fn rhg_main(argv: Vec<OsString>) -> ! {' | |||
|
301 | 301 | } |
|
302 | 302 | }; |
|
303 | 303 | |
|
304 | let exit = | |
|
304 | let simple_exit = | |
|
305 | 305 | |ui: &Ui, config: &Config, result: Result<(), CommandError>| -> ! { |
|
306 | 306 | exit( |
|
307 | 307 | &argv, |
@@ -317,7 +317,7 b' fn rhg_main(argv: Vec<OsString>) -> ! {' | |||
|
317 | 317 | ) |
|
318 | 318 | }; |
|
319 | 319 | let early_exit = |config: &Config, error: CommandError| -> ! { |
|
320 | exit(&Ui::new_infallible(config), &config, Err(error)) | |
|
320 | simple_exit(&Ui::new_infallible(config), &config, Err(error)) | |
|
321 | 321 | }; |
|
322 | 322 | let repo_result = match Repo::find(&non_repo_config, repo_path.to_owned()) |
|
323 | 323 | { |
@@ -348,6 +348,24 b' fn rhg_main(argv: Vec<OsString>) -> ! {' | |||
|
348 | 348 | let config = config_cow.as_ref(); |
|
349 | 349 | let ui = Ui::new(&config) |
|
350 | 350 | .unwrap_or_else(|error| early_exit(&config, error.into())); |
|
351 | ||
|
352 | if let Ok(true) = config.get_bool(b"rhg", b"fallback-immediately") { | |
|
353 | exit( | |
|
354 | &argv, | |
|
355 | &initial_current_dir, | |
|
356 | &ui, | |
|
357 | OnUnsupported::Fallback { | |
|
358 | executable: config | |
|
359 | .get(b"rhg", b"fallback-executable") | |
|
360 | .map(ToOwned::to_owned), | |
|
361 | }, | |
|
362 | Err(CommandError::unsupported( | |
|
363 | "`rhg.fallback-immediately is true`", | |
|
364 | )), | |
|
365 | false, | |
|
366 | ) | |
|
367 | } | |
|
368 | ||
|
351 | 369 | let result = main_with_result( |
|
352 | 370 | argv.iter().map(|s| s.to_owned()).collect(), |
|
353 | 371 | &process_start_time, |
@@ -355,7 +373,7 b' fn rhg_main(argv: Vec<OsString>) -> ! {' | |||
|
355 | 373 | repo_result.as_ref(), |
|
356 | 374 | config, |
|
357 | 375 | ); |
|
358 | exit(&ui, &config, result) | |
|
376 | simple_exit(&ui, &config, result) | |
|
359 | 377 | } |
|
360 | 378 | |
|
361 | 379 | fn main() -> ! { |
@@ -63,7 +63,16 b' def visit(opts, filenames, outfile):' | |||
|
63 | 63 | if isfile: |
|
64 | 64 | if opts.type: |
|
65 | 65 | facts.append(b'file') |
|
66 | if any((opts.hexdump, opts.dump, opts.md5, opts.sha1, opts.sha256)): | |
|
66 | needs_reading = ( | |
|
67 | opts.hexdump, | |
|
68 | opts.dump, | |
|
69 | opts.md5, | |
|
70 | opts.sha1, | |
|
71 | opts.raw_sha1, | |
|
72 | opts.sha256, | |
|
73 | ) | |
|
74 | ||
|
75 | if any(needs_reading): | |
|
67 | 76 | with open(f, 'rb') as fobj: |
|
68 | 77 | content = fobj.read() |
|
69 | 78 | elif islink: |
@@ -101,6 +110,9 b' def visit(opts, filenames, outfile):' | |||
|
101 | 110 | if opts.md5 and content is not None: |
|
102 | 111 | h = hashlib.md5(content) |
|
103 | 112 | facts.append(b'md5=%s' % binascii.hexlify(h.digest())[: opts.bytes]) |
|
113 | if opts.raw_sha1 and content is not None: | |
|
114 | h = hashlib.sha1(content) | |
|
115 | facts.append(b'raw-sha1=%s' % h.digest()[: opts.bytes]) | |
|
104 | 116 | if opts.sha1 and content is not None: |
|
105 | 117 | h = hashlib.sha1(content) |
|
106 | 118 | facts.append( |
@@ -186,6 +198,12 b' if __name__ == "__main__":' | |||
|
186 | 198 | ) |
|
187 | 199 | parser.add_option( |
|
188 | 200 | "", |
|
201 | "--raw-sha1", | |
|
202 | action="store_true", | |
|
203 | help="show raw bytes of the sha1 hash of the content", | |
|
204 | ) | |
|
205 | parser.add_option( | |
|
206 | "", | |
|
189 | 207 | "--sha256", |
|
190 | 208 | action="store_true", |
|
191 | 209 | help="show sha256 hash of the content", |
@@ -245,3 +245,17 b' We should make sure all of it (docket + ' | |||
|
245 | 245 | |
|
246 | 246 | $ hg status |
|
247 | 247 | A foo |
|
248 | $ cd .. | |
|
249 | ||
|
250 | Check dirstate ordering | |
|
251 | (e.g. `src/dirstate/` and `src/dirstate.rs` shouldn't cause issues) | |
|
252 | ||
|
253 | $ hg init repro | |
|
254 | $ cd repro | |
|
255 | $ mkdir src | |
|
256 | $ mkdir src/dirstate | |
|
257 | $ touch src/dirstate/file1 src/dirstate/file2 src/dirstate.rs | |
|
258 | $ touch file1 file2 | |
|
259 | $ hg commit -Aqm1 | |
|
260 | $ hg st | |
|
261 | $ cd .. |
@@ -59,18 +59,24 b' Should display baz only:' | |||
|
59 | 59 | ? syntax |
|
60 | 60 | |
|
61 | 61 | $ echo "*.o" > .hgignore |
|
62 | #if no-rhg | |
|
63 | 62 | $ hg status |
|
64 | 63 | abort: $TESTTMP/ignorerepo/.hgignore: invalid pattern (relre): *.o (glob) |
|
65 | 64 | [255] |
|
66 | #endif | |
|
67 | #if rhg | |
|
65 | ||
|
66 | $ echo 're:^(?!a).*\.o$' > .hgignore | |
|
68 | 67 | $ hg status |
|
69 | Unsupported syntax regex parse error: | |
|
70 | ^(?:*.o) | |
|
71 | ^ | |
|
72 | error: repetition operator missing expression | |
|
73 | [255] | |
|
68 | A dir/b.o | |
|
69 | ? .hgignore | |
|
70 | ? a.c | |
|
71 | ? a.o | |
|
72 | ? syntax | |
|
73 | #if rhg | |
|
74 | $ hg status --config rhg.on-unsupported=abort | |
|
75 | unsupported feature: Unsupported syntax regex parse error: | |
|
76 | ^(?:^(?!a).*\.o$) | |
|
77 | ^^^ | |
|
78 | error: look-around, including look-ahead and look-behind, is not supported | |
|
79 | [252] | |
|
74 | 80 | #endif |
|
75 | 81 | |
|
76 | 82 | Ensure given files are relative to cwd |
@@ -415,17 +421,51 b' Windows paths are accepted on input' | |||
|
415 | 421 | Check the hash of ignore patterns written in the dirstate |
|
416 | 422 | This is an optimization that is only relevant when using the Rust extensions |
|
417 | 423 | |
|
424 | $ cat_filename_and_hash () { | |
|
425 | > for i in "$@"; do | |
|
426 | > printf "$i " | |
|
427 | > cat "$i" | "$TESTDIR"/f --raw-sha1 | sed 's/^raw-sha1=//' | |
|
428 | > done | |
|
429 | > } | |
|
418 | 430 | $ hg status > /dev/null |
|
419 | $ cat .hg/testhgignore .hg/testhgignorerel .hgignore dir2/.hgignore dir1/.hgignore dir1/.hgignoretwo | $TESTDIR/f --sha1 | |
|
420 | sha1=6e315b60f15fb5dfa02be00f3e2c8f923051f5ff | |
|
431 | $ cat_filename_and_hash .hg/testhgignore .hg/testhgignorerel .hgignore dir2/.hgignore dir1/.hgignore dir1/.hgignoretwo | $TESTDIR/f --sha1 | |
|
432 | sha1=c0beb296395d48ced8e14f39009c4ea6e409bfe6 | |
|
421 | 433 | $ hg debugstate --docket | grep ignore |
|
422 | ignore pattern hash: 6e315b60f15fb5dfa02be00f3e2c8f923051f5ff | |
|
434 | ignore pattern hash: c0beb296395d48ced8e14f39009c4ea6e409bfe6 | |
|
423 | 435 | |
|
424 | 436 | $ echo rel > .hg/testhgignorerel |
|
425 | 437 | $ hg status > /dev/null |
|
426 | $ cat .hg/testhgignore .hg/testhgignorerel .hgignore dir2/.hgignore dir1/.hgignore dir1/.hgignoretwo | $TESTDIR/f --sha1 | |
|
427 | sha1=dea19cc7119213f24b6b582a4bae7b0cb063e34e | |
|
438 | $ cat_filename_and_hash .hg/testhgignore .hg/testhgignorerel .hgignore dir2/.hgignore dir1/.hgignore dir1/.hgignoretwo | $TESTDIR/f --sha1 | |
|
439 | sha1=b8e63d3428ec38abc68baa27631516d5ec46b7fa | |
|
428 | 440 | $ hg debugstate --docket | grep ignore |
|
429 | ignore pattern hash: dea19cc7119213f24b6b582a4bae7b0cb063e34e | |
|
441 | ignore pattern hash: b8e63d3428ec38abc68baa27631516d5ec46b7fa | |
|
442 | $ cd .. | |
|
443 | ||
|
444 | Check that the hash depends on the source of the hgignore patterns | |
|
445 | (otherwise the context is lost and things like subinclude are cached improperly) | |
|
446 | ||
|
447 | $ hg init ignore-collision | |
|
448 | $ cd ignore-collision | |
|
449 | $ echo > .hg/testhgignorerel | |
|
450 | ||
|
451 | $ mkdir dir1/ dir1/subdir | |
|
452 | $ touch dir1/subdir/f dir1/subdir/ignored1 | |
|
453 | $ echo 'ignored1' > dir1/.hgignore | |
|
454 | ||
|
455 | $ mkdir dir2 dir2/subdir | |
|
456 | $ touch dir2/subdir/f dir2/subdir/ignored2 | |
|
457 | $ echo 'ignored2' > dir2/.hgignore | |
|
458 | $ echo 'subinclude:dir2/.hgignore' >> .hgignore | |
|
459 | $ echo 'subinclude:dir1/.hgignore' >> .hgignore | |
|
460 | ||
|
461 | $ hg commit -Aqm_ | |
|
462 | ||
|
463 | $ > dir1/.hgignore | |
|
464 | $ echo 'ignored' > dir2/.hgignore | |
|
465 | $ echo 'ignored1' >> dir2/.hgignore | |
|
466 | $ hg status | |
|
467 | M dir1/.hgignore | |
|
468 | M dir2/.hgignore | |
|
469 | ? dir1/subdir/ignored1 | |
|
430 | 470 | |
|
431 | 471 | #endif |
@@ -168,6 +168,10 b' Fallback to Python' | |||
|
168 | 168 | $ rhg cat original --exclude="*.rs" |
|
169 | 169 | original content |
|
170 | 170 | |
|
171 | Check that `fallback-immediately` overrides `$NO_FALLBACK` | |
|
172 | $ $NO_FALLBACK rhg cat original --exclude="*.rs" --config rhg.fallback-immediately=1 | |
|
173 | original content | |
|
174 | ||
|
171 | 175 | $ (unset RHG_FALLBACK_EXECUTABLE; rhg cat original --exclude="*.rs") |
|
172 | 176 | abort: 'rhg.on-unsupported=fallback' without 'rhg.fallback-executable' set. |
|
173 | 177 | [255] |
@@ -944,7 +944,7 b' It is still not set when there are unkno' | |||
|
944 | 944 | $ hg debugdirstate --all --no-dates | grep '^ ' |
|
945 | 945 | 0 -1 unset subdir |
|
946 | 946 | |
|
947 | Now the directory is eligible for caching, so its mtime is save in the dirstate | |
|
947 | Now the directory is eligible for caching, so its mtime is saved in the dirstate | |
|
948 | 948 | |
|
949 | 949 | $ rm subdir/unknown |
|
950 | 950 | $ sleep 0.1 # ensure the kernel’s internal clock for mtimes has ticked |
@@ -976,4 +976,27 b' Removing a node from the dirstate resets' | |||
|
976 | 976 | $ hg status |
|
977 | 977 | ? subdir/a |
|
978 | 978 | |
|
979 | Changing the hgignore rules makes us recompute the status (and rewrite the dirstate). | |
|
980 | ||
|
981 | $ rm subdir/a | |
|
982 | $ mkdir another-subdir | |
|
983 | $ touch another-subdir/something-else | |
|
984 | ||
|
985 | $ cat > "$TESTDIR"/extra-hgignore <<EOF | |
|
986 | > something-else | |
|
987 | > EOF | |
|
988 | ||
|
989 | $ hg status --config ui.ignore.global="$TESTDIR"/extra-hgignore | |
|
990 | $ hg debugdirstate --all --no-dates | grep '^ ' | |
|
991 | 0 -1 set subdir | |
|
992 | ||
|
993 | $ hg status | |
|
994 | ? another-subdir/something-else | |
|
995 | ||
|
996 | One invocation of status is enough to populate the cache even if it's invalidated | |
|
997 | in the same run. | |
|
998 | ||
|
999 | $ hg debugdirstate --all --no-dates | grep '^ ' | |
|
1000 | 0 -1 set subdir | |
|
1001 | ||
|
979 | 1002 |
|
@@ -13,6 +13,7 b' Tests of the file helper tool' | |||
|
13 | 13 | check if file is newer (or same) |
|
14 | 14 | -r, --recurse recurse into directories |
|
15 | 15 | -S, --sha1 show sha1 hash of the content |
|
16 | --raw-sha1 show raw bytes of the sha1 hash of the content | |
|
16 | 17 | --sha256 show sha256 hash of the content |
|
17 | 18 | -M, --md5 show md5 hash of the content |
|
18 | 19 | -D, --dump dump file content |
General Comments 0
You need to be logged in to leave comments.
Login now