##// END OF EJS Templates
branching: merge stable into default
Raphaël Gomès -
r52401:0239ebdd merge default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,259 +1,260 b''
1 1 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0 iD8DBQBEYmO2ywK+sNU5EO8RAnaYAKCO7x15xUn5mnhqWNXqk/ehlhRt2QCfRDfY0LrUq2q4oK/KypuJYPHgq1A=
2 2 2be3001847cb18a23c403439d9e7d0ace30804e9 0 iD8DBQBExUbjywK+sNU5EO8RAhzxAKCtyHAQUzcTSZTqlfJ0by6vhREwWQCghaQFHfkfN0l9/40EowNhuMOKnJk=
3 3 36a957364b1b89c150f2d0e60a99befe0ee08bd3 0 iD8DBQBFfL2QywK+sNU5EO8RAjYFAKCoGlaWRTeMsjdmxAjUYx6diZxOBwCfY6IpBYsKvPTwB3oktnPt5Rmrlys=
4 4 27230c29bfec36d5540fbe1c976810aefecfd1d2 0 iD8DBQBFheweywK+sNU5EO8RAt7VAKCrqJQWT2/uo2RWf0ZI4bLp6v82jACgjrMdsaTbxRsypcmEsdPhlG6/8F4=
5 5 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0 iD8DBQBGgHicywK+sNU5EO8RAgNxAJ0VG8ixAaeudx4sZbhngI1syu49HQCeNUJQfWBgA8bkJ2pvsFpNxwYaX3I=
6 6 23889160905a1b09fffe1c07378e9fc1827606eb 0 iD8DBQBHGTzoywK+sNU5EO8RAr/UAJ0Y8s4jQtzgS+G9vM8z6CWBThZ8fwCcCT5XDj2XwxKkz/0s6UELwjsO3LU=
7 7 bae2e9c838e90a393bae3973a7850280413e091a 0 iD8DBQBH6DO5ywK+sNU5EO8RAsfrAJ0e4r9c9GF/MJsM7Xjd3NesLRC3+ACffj6+6HXdZf8cswAoFPO+DY00oD0=
8 8 d5cbbe2c49cee22a9fbeb9ea41daa0ac4e26b846 0 iD8DBQBINdwsywK+sNU5EO8RAjIUAKCPmlFJSpsPAAUKF+iNHAwVnwmzeQCdEXrL27CWclXuUKdbQC8De7LICtE=
9 9 d2375bbee6d47e62ba8e415c86e83a465dc4dce9 0 iD8DBQBIo1wpywK+sNU5EO8RAmRNAJ94x3OFt6blbqu/yBoypm/AJ44fuACfUaldXcV5z9tht97hSp22DVTEPGc=
10 10 2a67430f92f15ea5159c26b09ec4839a0c549a26 0 iEYEABECAAYFAkk1hykACgkQywK+sNU5EO85QACeNJNUanjc2tl4wUoPHNuv+lSj0ZMAoIm93wSTc/feyYnO2YCaQ1iyd9Nu
11 11 3773e510d433969e277b1863c317b674cbee2065 0 iEYEABECAAYFAklNbbAACgkQywK+sNU5EO8o+gCfeb2/lfIJZMvyDA1m+G1CsBAxfFsAoIa6iAMG8SBY7hW1Q85Yf/LXEvaE
12 12 11a4eb81fb4f4742451591489e2797dc47903277 0 iEYEABECAAYFAklcAnsACgkQywK+sNU5EO+uXwCbBVHNNsLy1g7BlAyQJwadYVyHOXoAoKvtAVO71+bv7EbVoukwTzT+P4Sx
13 13 11efa41037e280d08cfb07c09ad485df30fb0ea8 0 iEYEABECAAYFAkmvJRQACgkQywK+sNU5EO9XZwCeLMgDgPSMWMm6vgjL4lDs2pEc5+0AnRxfiFbpbBfuEFTqKz9nbzeyoBlx
14 14 02981000012e3adf40c4849bd7b3d5618f9ce82d 0 iEYEABECAAYFAknEH3wACgkQywK+sNU5EO+uXwCeI+LbLMmhjU1lKSfU3UWJHjjUC7oAoIZLvYDGOL/tNZFUuatc3RnZ2eje
15 15 196d40e7c885fa6e95f89134809b3ec7bdbca34b 0 iEYEABECAAYFAkpL2X4ACgkQywK+sNU5EO9FOwCfXJycjyKJXsvQqKkHrglwOQhEKS4An36GfKzptfN8b1qNc3+ya/5c2WOM
16 16 3ef6c14a1e8e83a31226f5881b7fe6095bbfa6f6 0 iEYEABECAAYFAkpopLIACgkQywK+sNU5EO8QSgCfZ0ztsd071rOa2lhmp9Fyue/WoI0AoLTei80/xrhRlB8L/rZEf2KBl8dA
17 17 31ec469f9b556f11819937cf68ee53f2be927ebf 0 iEYEABECAAYFAksBuxAACgkQywK+sNU5EO+mBwCfagB+A0txzWZ6dRpug3LEoK7Z1QsAoKpbk8vsLjv6/oRDicSk/qBu33+m
18 18 439d7ea6fe3aa4ab9ec274a68846779153789de9 0 iEYEABECAAYFAksVw0kACgkQywK+sNU5EO/oZwCfdfBEkgp38xq6wN2F4nj+SzofrJIAnjmxt04vaJSeOOeHylHvk6lzuQsw
19 19 296a0b14a68621f6990c54fdba0083f6f20935bf 0 iEYEABECAAYFAks+jCoACgkQywK+sNU5EO9J8wCeMUGF9E/gS2UBsqIz56WS4HMPRPUAoI5J95mwEIK8Clrl7qFRidNI6APq
20 20 4aa619c4c2c09907034d9824ebb1dd0e878206eb 0 iEYEABECAAYFAktm9IsACgkQywK+sNU5EO9XGgCgk4HclRQhexEtooPE5GcUCdB6M8EAn2ptOhMVbIoO+JncA+tNACPFXh0O
21 21 ff2704a8ded37fbebd8b6eb5ec733731d725da8a 0 iEYEABECAAYFAkuRoSQACgkQywK+sNU5EO//3QCeJDc5r2uFyFCtAlpSA27DEE5rrxAAn2FSwTy9fhrB3QAdDQlwkEZcQzDh
22 22 2b01dab594167bc0dd33331dbaa6dca3dca1b3aa 0 iEYEABECAAYFAku1IwIACgkQywK+sNU5EO9MjgCdHLVwkTZlNHxhcznZKBL1rjN+J7cAoLLWi9LTL6f/TgBaPSKOy1ublbaW
23 23 39f725929f0c48c5fb3b90c071fc3066012456ca 0 iEYEABECAAYFAkvclvsACgkQywK+sNU5EO9FSwCeL9i5x8ALW/LE5+lCX6MFEAe4MhwAn1ev5o6SX6GrNdDfKweiemfO2VBk
24 24 fdcf80f26604f233dc4d8f0a5ef9d7470e317e8a 0 iEYEABECAAYFAkvsKTkACgkQywK+sNU5EO9qEACgiSiRGvTG2vXGJ65tUSOIYihTuFAAnRzRIqEVSw8M8/RGeUXRps0IzaCO
25 25 24fe2629c6fd0c74c90bd066e77387c2b02e8437 0 iEYEABECAAYFAkwFLRsACgkQywK+sNU5EO+pJACgp13tPI+pbwKZV+LeMjcQ4H6tCZYAoJebzhd6a8yYx6qiwpJxA9BXZNXy
26 26 f786fc4b8764cd2a5526d259cf2f94d8a66924d9 0 iEYEABECAAYFAkwsyxcACgkQywK+sNU5EO+crACfUpNAF57PmClkSri9nJcBjb2goN4AniPCNaKvnki7TnUsi1u2oxltpKKL
27 27 bf1774d95bde614af3956d92b20e2a0c68c5fec7 0 iEYEABECAAYFAkxVwccACgkQywK+sNU5EO+oFQCeJzwZ+we1fIIyBGCddHceOUAN++cAnjvT6A8ZWW0zV21NXIFF1qQmjxJd
28 28 c00f03a4982e467fb6b6bd45908767db6df4771d 0 iEYEABECAAYFAkxXDqsACgkQywK+sNU5EO/GJACfT9Rz4hZOxPQEs91JwtmfjevO84gAmwSmtfo5mmWSm8gtTUebCcdTv0Kf
29 29 ff5cec76b1c5b6be9c3bb923aae8c3c6d079d6b9 0 iD8DBQBMdo+qywK+sNU5EO8RAqQpAJ975BL2CCAiWMz9SXthNQ9xG181IwCgp4O+KViHPkufZVFn2aTKMNvcr1A=
30 30 93d8bff78c96fe7e33237b257558ee97290048a4 0 iD8DBQBMpfvdywK+sNU5EO8RAsxVAJ0UaL1XB51C76JUBhafc9GBefuMxwCdEWkTOzwvE0SarJBe9i008jhbqW4=
31 31 333421b9e0f96c7bc788e5667c146a58a9440a55 0 iD8DBQBMz0HOywK+sNU5EO8RAlsEAJ0USh6yOG7OrWkADGunVt9QimBQnwCbBqeMnKgSbwEw8jZwE3Iz1mdrYlo=
32 32 4438875ec01bd0fc32be92b0872eb6daeed4d44f 0 iD8DBQBM4WYUywK+sNU5EO8RAhCVAJ0dJswachwFAHALmk1x0RJehxzqPQCbBNskP9n/X689jB+btNTZTyKU/fw=
33 33 6aff4f144ad356311318b0011df0bb21f2c97429 0 iD8DBQBM9uxXywK+sNU5EO8RAv+4AKCDj4qKP16GdPaq1tP6BUwpM/M1OACfRyzLPp/qiiN8xJTWoWYSe/XjJug=
34 34 e3bf16703e2601de99e563cdb3a5d50b64e6d320 0 iD8DBQBNH8WqywK+sNU5EO8RAiQTAJ9sBO+TeiGro4si77VVaQaA6jcRUgCfSA28dBbjj0oFoQwvPoZjANiZBH8=
35 35 a6c855c32ea081da3c3b8ff628f1847ff271482f 0 iD8DBQBNSJJ+ywK+sNU5EO8RAoJaAKCweDEF70fu+r1Zn7pYDXdlk5RuSgCeO9gK/eit8Lin/1n3pO7aYguFLok=
36 36 2b2155623ee2559caf288fd333f30475966c4525 0 iD8DBQBNSJeBywK+sNU5EO8RAm1KAJ4hW9Cm9nHaaGJguchBaPLlAr+O3wCgqgmMok8bdAS06N6PL60PSTM//Gg=
37 37 2616325766e3504c8ae7c84bd15ee610901fe91d 0 iD8DBQBNbWy9ywK+sNU5EO8RAlWCAJ4mW8HbzjJj9GpK98muX7k+7EvEHwCfaTLbC/DH3QEsZBhEP+M8tzL6RU4=
38 38 aa1f3be38ab127280761889d2dca906ca465b5f4 0 iD8DBQBNeQq7ywK+sNU5EO8RAlEOAJ4tlEDdetE9lKfjGgjbkcR8PrC3egCfXCfF3qNVvU/2YYjpgvRwevjvDy0=
39 39 b032bec2c0a651ca0ddecb65714bfe6770f67d70 0 iD8DBQBNlg5kywK+sNU5EO8RAnGEAJ9gmEx6MfaR4XcG2m/93vwtfyzs3gCgltzx8/YdHPwqDwRX/WbpYgi33is=
40 40 3cb1e95676ad089596bd81d0937cad37d6e3b7fb 0 iD8DBQBNvTy4ywK+sNU5EO8RAmp8AJ9QnxK4jTJ7G722MyeBxf0UXEdGwACgtlM7BKtNQfbEH/fOW5y+45W88VI=
41 41 733af5d9f6b22387913e1d11350fb8cb7c1487dd 0 iD8DBQBN5q/8ywK+sNU5EO8RArRGAKCNGT94GKIYtSuwZ57z1sQbcw6uLACfffpbMV4NAPMl8womAwg+7ZPKnIU=
42 42 de9eb6b1da4fc522b1cab16d86ca166204c24f25 0 iD8DBQBODhfhywK+sNU5EO8RAr2+AJ4ugbAj8ae8/K0bYZzx3sascIAg1QCeK3b+zbbVVqd3b7CDpwFnaX8kTd4=
43 43 4a43e23b8c55b4566b8200bf69fe2158485a2634 0 iD8DBQBONzIMywK+sNU5EO8RAj5SAJ0aPS3+JHnyI6bHB2Fl0LImbDmagwCdGbDLp1S7TFobxXudOH49bX45Iik=
44 44 d629f1e89021103f1753addcef6b310e4435b184 0 iD8DBQBOWAsBywK+sNU5EO8RAht4AJwJl9oNFopuGkj5m8aKuf7bqPkoAQCeNrEm7UhFsZKYT5iUOjnMV7s2LaM=
45 45 351a9292e430e35766c552066ed3e87c557b803b 0 iD8DBQBOh3zUywK+sNU5EO8RApFMAKCD3Y/u3avDFndznwqfG5UeTHMlvACfUivPIVQZyDZnhZMq0UhC6zhCEQg=
46 46 384082750f2c51dc917d85a7145748330fa6ef4d 0 iD8DBQBOmd+OywK+sNU5EO8RAgDgAJ9V/X+G7VLwhTpHrZNiOHabzSyzYQCdE2kKfIevJUYB9QLAWCWP6DPwrwI=
47 47 41453d55b481ddfcc1dacb445179649e24ca861d 0 iD8DBQBOsFhpywK+sNU5EO8RAqM6AKCyfxUae3/zLuiLdQz+JR78690eMACfQ6JTBQib4AbE+rUDdkeFYg9K/+4=
48 48 195dbd1cef0c2f9f8bcf4ea303238105f716bda3 0 iD8DBQBO1/fWywK+sNU5EO8RAmoPAKCR5lpv1D6JLURHD8KVLSV4GRVEBgCgnd0Sy78ligNfqAMafmACRDvj7vo=
49 49 6344043924497cd06d781d9014c66802285072e4 0 iD8DBQBPALgmywK+sNU5EO8RAlfhAJ9nYOdWnhfVDHYtDTJAyJtXBAQS9wCgnefoSQt7QABkbGxM+Q85UYEBuD0=
50 50 db33555eafeaf9df1e18950e29439eaa706d399b 0 iD8DBQBPGdzxywK+sNU5EO8RAppkAJ9jOXhUVE/97CPgiMA0pMGiIYnesQCfengAszcBiSiKGugiI8Okc9ghU+Y=
51 51 2aa5b51f310fb3befd26bed99c02267f5c12c734 0 iD8DBQBPKZ9bywK+sNU5EO8RAt1TAJ45r1eJ0YqSkInzrrayg4TVCh0SnQCgm0GA/Ua74jnnDwVQ60lAwROuz1Q=
52 52 53e2cd303ecf8ca7c7eeebd785c34e5ed6b0f4a4 0 iD8DBQBPT/fvywK+sNU5EO8RAnfYAKCn7d0vwqIb100YfWm1F7nFD5B+FACeM02YHpQLSNsztrBCObtqcnfod7Q=
53 53 b9bd95e61b49c221c4cca24e6da7c946fc02f992 0 iD8DBQBPeLsIywK+sNU5EO8RAvpNAKCtKe2gitz8dYn52IRF0hFOPCR7AQCfRJL/RWCFweu2T1vH/mUOCf8SXXc=
54 54 d9e2f09d5488c395ae9ddbb320ceacd24757e055 0 iD8DBQBPju/dywK+sNU5EO8RArBYAJ9xtifdbk+hCOJO8OZa4JfHX8OYZQCeKPMBaBWiT8N/WHoOm1XU0q+iono=
55 55 00182b3d087909e3c3ae44761efecdde8f319ef3 0 iD8DBQBPoFhIywK+sNU5EO8RAhzhAKCBj1n2jxPTkZNJJ5pSp3soa+XHIgCgsZZpAQxOpXwCp0eCdNGe0+pmxmg=
56 56 5983de86462c5a9f42a3ad0f5e90ce5b1d221d25 0 iD8DBQBPovNWywK+sNU5EO8RAhgiAJ980T91FdPTRMmVONDhpkMsZwVIMACgg3bKvoWSeuCW28llUhAJtUjrMv0=
57 57 85a358df5bbbe404ca25730c9c459b34263441dc 0 iD8DBQBPyZsWywK+sNU5EO8RAnpLAJ48qrGDJRT+pteS0mSQ11haqHstPwCdG4ccGbk+0JHb7aNy8/NRGAOqn9w=
58 58 b013baa3898e117959984fc64c29d8c784d2f28b 0 iD8DBQBP8QOPywK+sNU5EO8RAqimAKCFRSx0lvG6y8vne2IhNG062Hn0dACeMLI5/zhpWpHBIVeAAquYfx2XFeA=
59 59 7f5094bb3f423fc799e471aac2aee81a7ce57a0b 0 iD8DBQBQGiL8ywK+sNU5EO8RAq5oAJ4rMMCPx6O+OuzNXVOexogedWz/QgCeIiIxLd76I4pXO48tdXhr0hQcBuM=
60 60 072209ae4ddb654eb2d5fd35bff358c738414432 0 iD8DBQBQQkq0ywK+sNU5EO8RArDTAJ9nk5CySnNAjAXYvqvx4uWCw9ThZwCgqmFRehH/l+oTwj3f8nw8u8qTCdc=
61 61 b3f0f9a39c4e1d0250048cd803ab03542d6f140a 0 iD8DBQBQamltywK+sNU5EO8RAlsqAJ4qF/m6aFu4mJCOKTiAP5RvZFK02ACfawYShUZO6OXEFfveU0aAxDR0M1k=
62 62 d118a4f4fd16d9b558ec3f3e87bfee772861d2b7 0 iD8DBQBQgPV5ywK+sNU5EO8RArylAJ0abcx5NlDjyv3ZDWpAfRIHyRsJtQCgn4TMuEayqgxzrvadQZHdTEU2g38=
63 63 195ad823b5d58c68903a6153a25e3fb4ed25239d 0 iD8DBQBQkuT9ywK+sNU5EO8RAhB4AKCeerItoK2Jipm2cVf4euGofAa/WACeJj3TVd4pFILpb+ogj7ebweFLJi0=
64 64 0c10cf8191469e7c3c8844922e17e71a176cb7cb 0 iD8DBQBQvQWoywK+sNU5EO8RAnq3AJoCn98u4geFx5YaQaeh99gFhCd7bQCgjoBwBSUyOvGd0yBy60E3Vv3VZhM=
65 65 a4765077b65e6ae29ba42bab7834717b5072d5ba 0 iD8DBQBQ486sywK+sNU5EO8RAhmJAJ90aLfLKZhmcZN7kqphigQJxiFOQACeJ5IUZxjGKH4xzi3MrgIcx9n+dB0=
66 66 f5fbe15ca7449f2c9a3cf817c86d0ae68b307214 0 iD8DBQBQ+yuYywK+sNU5EO8RAm9JAJoD/UciWvpGeKBcpGtZJBFJVcL/HACghDXSgQ+xQDjB+6uGrdgAQsRR1Lg=
67 67 a6088c05e43a8aee0472ca3a4f6f8d7dd914ebbf 0 iD8DBQBRDDROywK+sNU5EO8RAh75AJ9uJCGoCWnP0Lv/+XuYs4hvUl+sAgCcD36QgAnuw8IQXrvv684BAXAnHcA=
68 68 7511d4df752e61fe7ae4f3682e0a0008573b0402 0 iD8DBQBRFYaoywK+sNU5EO8RAuErAJoDyhXn+lptU3+AevVdwAIeNFyR2gCdHzPHyWd+JDeWCUR+pSOBi8O2ppM=
69 69 5b7175377babacce80a6c1e12366d8032a6d4340 0 iD8DBQBRMCYgywK+sNU5EO8RAq1/AKCWKlt9ysibyQgYwoxxIOZv5J8rpwCcDSHQaaf1fFZUTnQsOePwcM2Y/Sg=
70 70 50c922c1b5145dab8baefefb0437d363b6a6c21c 0 iD8DBQBRWnUnywK+sNU5EO8RAuQRAJwM42cJqJPeqJ0jVNdMqKMDqr4dSACeP0cRVGz1gitMuV0x8f3mrZrqc7I=
71 71 8a7bd2dccd44ed571afe7424cd7f95594f27c092 0 iD8DBQBRXfBvywK+sNU5EO8RAn+LAKCsMmflbuXjYRxlzFwId5ptm8TZcwCdGkyLbZcASBOkzQUm/WW1qfknJHU=
72 72 292cd385856d98bacb2c3086f8897bc660c2beea 0 iD8DBQBRcM0BywK+sNU5EO8RAjp4AKCJBykQbvXhKuvLSMxKx3a2TBiXcACfbr/kLg5GlZTF/XDPmY+PyHgI/GM=
73 73 23f785b38af38d2fca6b8f3db56b8007a84cd73a 0 iD8DBQBRgZwNywK+sNU5EO8RAmO4AJ4u2ILGuimRP6MJgE2t65LZ5dAdkACgiENEstIdrlFC80p+sWKD81kKIYI=
74 74 ddc7a6be20212d18f3e27d9d7e6f079a66d96f21 0 iD8DBQBRkswvywK+sNU5EO8RAiYYAJsHTHyHbJeAgmGvBTmDrfcKu4doUgCeLm7eGBjx7yAPUvEtxef8rAkQmXI=
75 75 cceaf7af4c9e9e6fa2dbfdcfe9856c5da69c4ffd 0 iD8DBQBRqnFLywK+sNU5EO8RAsWNAJ9RR6t+y1DLFc2HeH0eN9VfZAKF9gCeJ8ezvhtKq/LMs0/nvcgKQc/d5jk=
76 76 009794acc6e37a650f0fae37872e733382ac1c0c 0 iD8DBQBR0guxywK+sNU5EO8RArNkAKCq9pMihVzP8Os5kCmgbWpe5C37wgCgqzuPZTHvAsXF5wTyaSTMVa9Ccq4=
77 77 f0d7721d7322dcfb5af33599c2543f27335334bb 0 iD8DBQBR8taaywK+sNU5EO8RAqeEAJ4idDhhDuEsgsUjeQgWNj498matHACfT67gSF5w0ylsrBx1Hb52HkGXDm0=
78 78 f37b5a17e6a0ee17afde2cdde5393dd74715fb58 0 iD8DBQBR+ymFywK+sNU5EO8RAuSdAJkBMcd9DAZ3rWE9WGKPm2YZ8LBoXACfXn/wbEsVy7ZgJoUwiWmHSnQaWCI=
79 79 335a558f81dc73afeab4d7be63617392b130117f 0 iQIVAwUAUiZrIyBXgaxoKi1yAQK2iw//cquNqqSkc8Re5/TZT9I6NH+lh6DbOKjJP0Xl1Wqq0K+KSIUgZG4G32ovaEb2l5X0uY+3unRPiZ0ebl0YSw4Fb2ZiPIADXLBTOYRrY2Wwd3tpJeGI6wEgZt3SfcITV/g7NJrCjT3FlYoSOIayrExM80InSdcEM0Q3Rx6HKzY2acyxzgZeAtAW5ohFvHilSvY6p5Gcm4+QptMxvw45GPdreUmjeXZxNXNXZ8P+MjMz/QJbai/N7PjmK8lqnhkBsT48Ng/KhhmOkGntNJ2/ImBWLFGcWngSvJ7sfWwnyhndvGhe0Hq1NcCf7I8TjNDxU5TR+m+uW7xjXdLoDbUjBdX4sKXnh8ZjbYiODKBOrrDq25cf8nA/tnpKyE/qsVy60kOk6loY4XKiYmn1V49Ta0emmDx0hqo3HgxHHsHX0NDnGdWGol7cPRET0RzVobKq1A0jnrhPooWidvLh9bPzLonrWDo+ib+DuySoRkuYUK4pgZJ2mbg6daFOBEZygkSyRB8bo1UQUP7EgQDrWe4khb/5GHEfDkrQz3qu/sXvc0Ir1mOUWBFPHC2DjjCn/oMJuUkG1SwM8l2Bfv7h67ssES6YQ2+RjOix4yid7EXS/Ogl45PzCIPSI5+BbNs10JhE0w5uErBHlF53EDTe/TSLc+GU6DB6PP6dH912Njdr3jpNSUQ=
80 80 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
@@ -1,275 +1,276 b''
1 1 d40cc5aacc31ed673d9b5b24f98bee78c283062c 0.4f
2 2 1c590d34bf61e2ea12c71738e5a746cd74586157 0.4e
3 3 7eca4cfa8aad5fce9a04f7d8acadcd0452e2f34e 0.4d
4 4 b4d0c3786ad3e47beacf8412157326a32b6d25a4 0.4c
5 5 f40273b0ad7b3a6d3012fd37736d0611f41ecf54 0.5
6 6 0a28dfe59f8fab54a5118c5be4f40da34a53cdb7 0.5b
7 7 12e0fdbc57a0be78f0e817fd1d170a3615cd35da 0.6
8 8 4ccf3de52989b14c3d84e1097f59e39a992e00bd 0.6b
9 9 eac9c8efcd9bd8244e72fb6821f769f450457a32 0.6c
10 10 979c049974485125e1f9357f6bbe9c1b548a64c3 0.7
11 11 3a56574f329a368d645853e0f9e09472aee62349 0.8
12 12 6a03cff2b0f5d30281e6addefe96b993582f2eac 0.8.1
13 13 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0.9
14 14 2be3001847cb18a23c403439d9e7d0ace30804e9 0.9.1
15 15 36a957364b1b89c150f2d0e60a99befe0ee08bd3 0.9.2
16 16 27230c29bfec36d5540fbe1c976810aefecfd1d2 0.9.3
17 17 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0.9.4
18 18 23889160905a1b09fffe1c07378e9fc1827606eb 0.9.5
19 19 bae2e9c838e90a393bae3973a7850280413e091a 1.0
20 20 d5cbbe2c49cee22a9fbeb9ea41daa0ac4e26b846 1.0.1
21 21 d2375bbee6d47e62ba8e415c86e83a465dc4dce9 1.0.2
22 22 2a67430f92f15ea5159c26b09ec4839a0c549a26 1.1
23 23 3773e510d433969e277b1863c317b674cbee2065 1.1.1
24 24 11a4eb81fb4f4742451591489e2797dc47903277 1.1.2
25 25 11efa41037e280d08cfb07c09ad485df30fb0ea8 1.2
26 26 02981000012e3adf40c4849bd7b3d5618f9ce82d 1.2.1
27 27 196d40e7c885fa6e95f89134809b3ec7bdbca34b 1.3
28 28 3ef6c14a1e8e83a31226f5881b7fe6095bbfa6f6 1.3.1
29 29 31ec469f9b556f11819937cf68ee53f2be927ebf 1.4
30 30 439d7ea6fe3aa4ab9ec274a68846779153789de9 1.4.1
31 31 296a0b14a68621f6990c54fdba0083f6f20935bf 1.4.2
32 32 4aa619c4c2c09907034d9824ebb1dd0e878206eb 1.4.3
33 33 ff2704a8ded37fbebd8b6eb5ec733731d725da8a 1.5
34 34 2b01dab594167bc0dd33331dbaa6dca3dca1b3aa 1.5.1
35 35 39f725929f0c48c5fb3b90c071fc3066012456ca 1.5.2
36 36 fdcf80f26604f233dc4d8f0a5ef9d7470e317e8a 1.5.3
37 37 24fe2629c6fd0c74c90bd066e77387c2b02e8437 1.5.4
38 38 f786fc4b8764cd2a5526d259cf2f94d8a66924d9 1.6
39 39 bf1774d95bde614af3956d92b20e2a0c68c5fec7 1.6.1
40 40 c00f03a4982e467fb6b6bd45908767db6df4771d 1.6.2
41 41 ff5cec76b1c5b6be9c3bb923aae8c3c6d079d6b9 1.6.3
42 42 93d8bff78c96fe7e33237b257558ee97290048a4 1.6.4
43 43 333421b9e0f96c7bc788e5667c146a58a9440a55 1.7
44 44 4438875ec01bd0fc32be92b0872eb6daeed4d44f 1.7.1
45 45 6aff4f144ad356311318b0011df0bb21f2c97429 1.7.2
46 46 e3bf16703e2601de99e563cdb3a5d50b64e6d320 1.7.3
47 47 a6c855c32ea081da3c3b8ff628f1847ff271482f 1.7.4
48 48 2b2155623ee2559caf288fd333f30475966c4525 1.7.5
49 49 2616325766e3504c8ae7c84bd15ee610901fe91d 1.8
50 50 aa1f3be38ab127280761889d2dca906ca465b5f4 1.8.1
51 51 b032bec2c0a651ca0ddecb65714bfe6770f67d70 1.8.2
52 52 3cb1e95676ad089596bd81d0937cad37d6e3b7fb 1.8.3
53 53 733af5d9f6b22387913e1d11350fb8cb7c1487dd 1.8.4
54 54 de9eb6b1da4fc522b1cab16d86ca166204c24f25 1.9
55 55 4a43e23b8c55b4566b8200bf69fe2158485a2634 1.9.1
56 56 d629f1e89021103f1753addcef6b310e4435b184 1.9.2
57 57 351a9292e430e35766c552066ed3e87c557b803b 1.9.3
58 58 384082750f2c51dc917d85a7145748330fa6ef4d 2.0-rc
59 59 41453d55b481ddfcc1dacb445179649e24ca861d 2.0
60 60 195dbd1cef0c2f9f8bcf4ea303238105f716bda3 2.0.1
61 61 6344043924497cd06d781d9014c66802285072e4 2.0.2
62 62 db33555eafeaf9df1e18950e29439eaa706d399b 2.1-rc
63 63 2aa5b51f310fb3befd26bed99c02267f5c12c734 2.1
64 64 53e2cd303ecf8ca7c7eeebd785c34e5ed6b0f4a4 2.1.1
65 65 b9bd95e61b49c221c4cca24e6da7c946fc02f992 2.1.2
66 66 d9e2f09d5488c395ae9ddbb320ceacd24757e055 2.2-rc
67 67 00182b3d087909e3c3ae44761efecdde8f319ef3 2.2
68 68 5983de86462c5a9f42a3ad0f5e90ce5b1d221d25 2.2.1
69 69 85a358df5bbbe404ca25730c9c459b34263441dc 2.2.2
70 70 b013baa3898e117959984fc64c29d8c784d2f28b 2.2.3
71 71 a06e2681dd1786e2354d84a5fa9c1c88dd4fa3e0 2.3-rc
72 72 7f5094bb3f423fc799e471aac2aee81a7ce57a0b 2.3
73 73 072209ae4ddb654eb2d5fd35bff358c738414432 2.3.1
74 74 b3f0f9a39c4e1d0250048cd803ab03542d6f140a 2.3.2
75 75 d118a4f4fd16d9b558ec3f3e87bfee772861d2b7 2.4-rc
76 76 195ad823b5d58c68903a6153a25e3fb4ed25239d 2.4
77 77 0c10cf8191469e7c3c8844922e17e71a176cb7cb 2.4.1
78 78 a4765077b65e6ae29ba42bab7834717b5072d5ba 2.4.2
79 79 f5fbe15ca7449f2c9a3cf817c86d0ae68b307214 2.5-rc
80 80 a6088c05e43a8aee0472ca3a4f6f8d7dd914ebbf 2.5
81 81 7511d4df752e61fe7ae4f3682e0a0008573b0402 2.5.1
82 82 5b7175377babacce80a6c1e12366d8032a6d4340 2.5.2
83 83 50c922c1b5145dab8baefefb0437d363b6a6c21c 2.5.3
84 84 8a7bd2dccd44ed571afe7424cd7f95594f27c092 2.5.4
85 85 292cd385856d98bacb2c3086f8897bc660c2beea 2.6-rc
86 86 23f785b38af38d2fca6b8f3db56b8007a84cd73a 2.6
87 87 ddc7a6be20212d18f3e27d9d7e6f079a66d96f21 2.6.1
88 88 cceaf7af4c9e9e6fa2dbfdcfe9856c5da69c4ffd 2.6.2
89 89 009794acc6e37a650f0fae37872e733382ac1c0c 2.6.3
90 90 f0d7721d7322dcfb5af33599c2543f27335334bb 2.7-rc
91 91 f37b5a17e6a0ee17afde2cdde5393dd74715fb58 2.7
92 92 335a558f81dc73afeab4d7be63617392b130117f 2.7.1
93 93 e7fa36d2ad3a7944a52dca126458d6f482db3524 2.7.2
94 94 1596f2d8f2421314b1ddead8f7d0c91009358994 2.8-rc
95 95 d825e4025e39d1c39db943cdc89818abd0a87c27 2.8
96 96 209e04a06467e2969c0cc6501335be0406d46ef0 2.8.1
97 97 ca387377df7a3a67dbb90b6336b781cdadc3ef41 2.8.2
98 98 8862469e16f9236208581b20de5f96bd13cc039d 2.9-rc
99 99 3cec5134e9c4bceab6a00c60f52a4f80677a78f2 2.9
100 100 b96cb15ec9e04d8ac5ee08b34fcbbe4200588965 2.9.1
101 101 3f83fc5cfe715d292069ee8417c83804f6c6c1e4 2.9.2
102 102 564f55b251224f16508dd1311452db7780dafe2b 3.0-rc
103 103 2195ac506c6ababe86985b932f4948837c0891b5 3.0
104 104 269c80ee5b3cb3684fa8edc61501b3506d02eb10 3.0.1
105 105 2d8cd3d0e83c7336c0cb45a9f88638363f993848 3.0.2
106 106 6c36dc6cd61a0e1b563f1d51e55bdf4dacf12162 3.1-rc
107 107 3178e49892020336491cdc6945885c4de26ffa8b 3.1
108 108 5dc91146f35369949ea56b40172308158b59063a 3.1.1
109 109 f768c888aaa68d12dd7f509dcc7f01c9584357d0 3.1.2
110 110 7f8d16af8cae246fa5a48e723d48d58b015aed94 3.2-rc
111 111 ced632394371a36953ce4d394f86278ae51a2aae 3.2
112 112 643c58303fb0ec020907af28b9e486be299ba043 3.2.1
113 113 902554884335e5ca3661d63be9978eb4aec3f68a 3.2.2
114 114 6dad422ecc5adb63d9fa649eeb8e05a5f9bc4900 3.2.3
115 115 1265a3a71d75396f5d4cf6935ae7d9ba5407a547 3.2.4
116 116 db8e3f7948b1fdeb9ad12d448fc3525759908b9f 3.3-rc
117 117 fbdd5195528fae4f41feebc1838215c110b25d6a 3.3
118 118 5b4ed033390bf6e2879c8f5c28c84e1ee3b87231 3.3.1
119 119 07a92bbd02e5e3a625e0820389b47786b02b2cea 3.3.2
120 120 2e2e9a0750f91a6fe0ad88e4de34f8efefdcab08 3.3.3
121 121 e89f909edffad558b56f4affa8239e4832f88de0 3.4-rc
122 122 8cc6036bca532e06681c5a8fa37efaa812de67b5 3.4
123 123 ed18f4acf435a2824c6f49fba40f42b9df5da7ad 3.4.1
124 124 540cd0ddac49c1125b2e013aa2ff18ecbd4dd954 3.4.2
125 125 96a38d44ba093bd1d1ecfd34119e94056030278b 3.5-rc
126 126 21aa1c313b05b1a85f8ffa1120d51579ddf6bf24 3.5
127 127 1a45e49a6bed023deb229102a8903234d18054d3 3.5.1
128 128 9a466b9f9792e3ad7ae3fc6c43c3ff2e136b718d 3.5.2
129 129 b66e3ca0b90c3095ea28dfd39aa24247bebf5c20 3.6-rc
130 130 47dd34f2e7272be9e3b2a5a83cd0d20be44293f4 3.6
131 131 1aa5083cbebbe7575c88f3402ab377539b484897 3.6.1
132 132 2d437a0f3355834a9485bbbeb30a52a052c98f19 3.6.2
133 133 ea389970c08449440587712117f178d33bab3f1e 3.6.3
134 134 158bdc8965720ca4061f8f8d806563cfc7cdb62e 3.7-rc
135 135 2408645de650d8a29a6ce9e7dce601d8dd0d1474 3.7
136 136 b698abf971e7377d9b7ec7fc8c52df45255b0329 3.7.1
137 137 d493d64757eb45ada99fcb3693e479a51b7782da 3.7.2
138 138 ae279d4a19e9683214cbd1fe8298cf0b50571432 3.7.3
139 139 740156eedf2c450aee58b1a90b0e826f47c5da64 3.8-rc
140 140 f85de28eae32e7d3064b1a1321309071bbaaa069 3.8
141 141 a56296f55a5e1038ea5016dace2076b693c28a56 3.8.1
142 142 aaabed77791a75968a12b8c43ad263631a23ee81 3.8.2
143 143 a9764ab80e11bcf6a37255db7dd079011f767c6c 3.8.3
144 144 26a5d605b8683a292bb89aea11f37a81b06ac016 3.8.4
145 145 519bb4f9d3a47a6e83c2b414d58811ed38f503c2 3.9-rc
146 146 299546f84e68dbb9bd026f0f3a974ce4bdb93686 3.9
147 147 ccd436f7db6d5d7b9af89715179b911d031d44f1 3.9.1
148 148 149433e68974eb5c63ccb03f794d8b57339a80c4 3.9.2
149 149 438173c415874f6ac653efc1099dec9c9150e90f 4.0-rc
150 150 eab27446995210c334c3d06f1a659e3b9b5da769 4.0
151 151 b3b1ae98f6a0e14c1e1ba806a6c18e193b6dae5c 4.0.1
152 152 e69874dc1f4e142746ff3df91e678a09c6fc208c 4.0.2
153 153 a1dd2c0c479e0550040542e392e87bc91262517e 4.1-rc
154 154 e1526da1e6d84e03146151c9b6e6950fe9a83d7d 4.1
155 155 25703b624d27e3917d978af56d6ad59331e0464a 4.1.1
156 156 ed5b25874d998ababb181a939dd37a16ea644435 4.1.2
157 157 77eaf9539499a1b8be259ffe7ada787d07857f80 4.1.3
158 158 616e788321cc4ae9975b7f0c54c849f36d82182b 4.2-rc
159 159 bb96d4a497432722623ae60d9bc734a1e360179e 4.2
160 160 c850f0ed54c1d42f9aa079ad528f8127e5775217 4.2.1
161 161 26c49ed51a698ec016d2b4c6b44ca3c3f73cc788 4.2.2
162 162 857876ebaed4e315f63157bd157d6ce553c7ab73 4.3-rc
163 163 5544af8622863796a0027566f6b646e10d522c4c 4.3
164 164 943c91326b23954e6e1c6960d0239511f9530258 4.2.3
165 165 3fee7f7d2da04226914c2258cc2884dc27384fd7 4.3.1
166 166 920977f72c7b70acfdaf56ab35360584d7845827 4.3.2
167 167 2f427b57bf9019c6dc3750baa539dc22c1be50f6 4.3.3
168 168 1e2454b60e5936f5e77498cab2648db469504487 4.4-rc
169 169 0ccb43d4cf01d013ae05917ec4f305509f851b2d 4.4
170 170 cabc840ffdee8a72f3689fb77dd74d04fdc2bc04 4.4.1
171 171 a92b9f8e11ba330614cdfd6af0e03b15c1ff3797 4.4.2
172 172 27b6df1b5adbdf647cf5c6675b40575e1b197c60 4.5-rc
173 173 d334afc585e29577f271c5eda03378736a16ca6b 4.5
174 174 369aadf7a3264b03c8b09efce715bc41e6ab4a9b 4.5.1
175 175 8bba684efde7f45add05f737952093bb2aa07155 4.5.2
176 176 7de7bd407251af2bc98e5b809c8598ee95830daf 4.5.3
177 177 ed5448edcbfa747b9154099e18630e49024fd47b 4.6rc0
178 178 1ec874717d8a93b19e0d50628443e0ee5efab3a9 4.6rc1
179 179 6614cac550aea66d19c601e45efd1b7bd08d7c40 4.6
180 180 9c5ced5276d6e7d54f7c3dadf5247b7ee98ec79c 4.6.1
181 181 0b63a6743010dfdbf8a8154186e119949bdaa1cc 4.6.2
182 182 e90130af47ce8dd53a3109aed9d15876b3e7dee8 4.7rc0
183 183 33ac6a72308a215e6086fbced347ec10aa963b0a 4.7
184 184 ede3bf31fe63677fdf5bd8db687977d4e3d792ed 4.7.1
185 185 5405cb1a79010ac50c58cd84e6f50c4556bf2a4c 4.7.2
186 186 956ec6f1320df26f3133ec40f3de866ea0695fd7 4.8rc0
187 187 a91a2837150bdcb27ae76b3646e6c93cd6a15904 4.8
188 188 1c8c54cf97256f4468da2eb4dbee24f7f3888e71 4.8.1
189 189 197f092b2cd9691e2a55d198f717b231af9be6f9 4.8.2
190 190 593718ff5844cad7a27ee3eb5adad89ac8550949 4.9rc0
191 191 83377b4b4ae0e9a6b8e579f7b0a693b8cf5c3b10 4.9
192 192 4ea21df312ec7159c5b3633096b6ecf68750b0dd 4.9.1
193 193 4a8d9ed864754837a185a642170cde24392f9abf 5.0rc0
194 194 07e479ef7c9639be0029f00e6a722b96dcc05fee 5.0
195 195 c3484ddbdb9621256d597ed86b90d229c59c2af9 5.0.1
196 196 97ada9b8d51bef24c5cb4cdca4243f0db694ab6e 5.0.2
197 197 e386b5f4f8360dbb43a576dd9b1368e386fefa5b 5.1rc0
198 198 e91930d712e8507d1bc1b2dffd96c83edc4cbed3 5.1
199 199 a4e32fd539ab41489a51b2aa88bda9a73b839562 5.1.1
200 200 181e52f2b62f4768aa0d988936c929dc7c4a41a0 5.1.2
201 201 59338f9561099de77c684c00f76507f11e46ebe8 5.2rc0
202 202 ca3dca416f8d5863ca6f5a4a6a6bb835dcd5feeb 5.2
203 203 a50fecefa691c9b72a99e49aa6fe9dd13943c2bf 5.2.1
204 204 b4c82b70418022e67cc0e69b1aa3c3aa43aa1d29 5.2.2
205 205 84a0102c05c7852c8215ef6cf21d809927586b69 5.3rc0
206 206 e4344e463c0c888a2f437b78b5982ecdf3f6650a 5.3rc1
207 207 7f5410dfc8a64bb587d19637deb95d378fd1eb5c 5.3
208 208 6d121acbb82e65fe4dd3c2318a1b61981b958492 5.3.1
209 209 8fca7e8449a847e3cf1054f2c07b51237699fad3 5.3.2
210 210 26ce8e7515036d3431a03aaeb7bc72dd96cb1112 5.4rc0
211 211 cf3e07d7648a4371ce584d15dd692e7a6845792f 5.4
212 212 065704cbdbdbb05dcd6bb814eb9bbdd982211b28 5.4.1
213 213 0ea9c86fac8974cd74dc12ea681c8986eb6da6c4 5.4.2
214 214 28163c5de797e5416f9b588940f4608269b4d50a 5.5rc0
215 215 7fc3c5fbc65f6fe85d70ea63923b8767dda4f2e0 5.5
216 216 f62bb5d07848ca598aa860a517394130b61bf2ee 5.5.1
217 217 07731064ac41dacdf0ec869ebd05c2e848c14fbf 5.5.2
218 218 0e06a7ab9e0d5c65af4e511aee1e0342998799df 5.6rc0
219 219 18c17d63fdabd009e70bf994e5efb7db422f4f7f 5.6
220 220 1d5189a57405ceca5aa244052c9f948977f4699b 5.6.1
221 221 9da65e3cf3706ff41e08b311381c588440c27baf 5.7rc0
222 222 0e2e7300f4302b02412b0b734717697049494c4c 5.7
223 223 d5d9177c0045d206db575bae6daa98e2cb2fe5bc 5.7.1
224 224 f67b8946bb1b6cfa8328dbf8d6a9128b69ccdcb4 5.8rc0
225 225 8d2b62d716b095507effaa8d56f87cd27ba659ab 5.8rc1
226 226 067f2c53fb24506c9e9fb4639871b13b19a85f8a 5.8
227 227 411dc27fd9fd076d6a031a08fcaace659afe2fe3 5.8.1
228 228 d7515d29761d5ada7d9c765f517db67db75dea9a 5.9rc0
229 229 2813d406b03607cdb8c06cb04c44efcc9a79d9a2 5.9rc1
230 230 53221078e0de65d1a821ce5311dec45a7a978301 5.9
231 231 86a60679cf619e14cee9442f865fcf31b142cb9f 5.9.1
232 232 750920b18aaaddd654756be40dec59d90f2643be 5.9.2
233 233 6ee0244fc1cf889ae543d2ce0ec45201ae0be6e1 5.9.3
234 234 a44bb185f6bdbecc754996d8386722e2f0123b0a 6.0rc0
235 235 5d08b289e2e526259d7d5ea32b70fe76d5b327d7 6.0
236 236 799fdf4cca80cb9ae40537a90995e6bd163ebc0b 6.0.1
237 237 75676122c2bf7594ac732b7388db4c74c648b365 6.0.2
238 238 dcec16e799ddb6d33fcd11b04af530250a417a58 6.0.3
239 239 c00d3ce4e94bb0ee8d809e25e1dcb2a5fab84e2c 6.1rc0
240 240 d4486810a1795fba9521449b8885ced034f3a6dd 6.1
241 241 5bd6bcd31dd1ebb63b8914b00064f96297267af7 6.1.1
242 242 0ddd5e1f5f67438af85d12e4ce6c39021dde9916 6.1.2
243 243 6b10151b962108f65bfa12b3918b1021ca334f73 6.1.3
244 244 0cc5f74ff7f0f4ac2427096bddbe102dbc2453ae 6.1.4
245 245 288de6f5d724bba7bf1669e2838f196962bb7528 6.2rc0
246 246 094a5fa3cf52f936e0de3f1e507c818bee5ece6b 6.2
247 247 f69bffd00abe3a1b94d1032eb2c92e611d16a192 6.2.1
248 248 b5c8524827d20fe2e0ca8fb1234a0fe35a1a36c7 6.2.2
249 249 dbdee8ac3e3fcdda1fa55b90c0a235125b7f8e6f 6.2.3
250 250 a3356ab610fc50000cf0ba55c424a4d96da11db7 6.3rc0
251 251 04f1dba53c961dfdb875c8469adc96fa999cfbed 6.3.0
252 252 04f1dba53c961dfdb875c8469adc96fa999cfbed 6.3
253 253 04f1dba53c961dfdb875c8469adc96fa999cfbed 6.3.0
254 254 0000000000000000000000000000000000000000 6.3.0
255 255 c890d8b8bc59b18e5febf60caada629df5356ee2 6.3.1
256 256 59466b13a3ae0e29a5d4f485393e516cfbb057d0 6.3.2
257 257 8830004967ad865ead89c28a410405a6e71e0796 6.3.3
258 258 05de4896508e8ec387b33eb30d8aab78d1c8e9e4 6.4rc0
259 259 f14864fffdcab725d9eac6d4f4c07be05a35f59a 6.4
260 260 83ea6ce48b4fd09fb79c4e34cc5750c805699a53 6.4.1
261 261 f952be90b0514a576dcc8bbe758ce3847faba9bb 6.4.2
262 262 fc445f8abcf90b33db7c463816a1b3560681767f 6.4.3
263 263 da372c745e0f053bb7a64e74cccd15810d96341d 6.4.4
264 264 271a4ab29605ffa0bae5d3208eaa21a95427ff92 6.4.5
265 265 bb42988c7e156931b0ff1e93732b98173ebbcb7f 6.5rc0
266 266 3ffc7209bbae5804a53084c9dc2d41139e88c867 6.5
267 267 787af4e0e8b787e1b77a8059926b123730a4cd01 6.5.1
268 268 5a8b5420103937fca97c584c5162178eed828ada 6.5.2
269 269 c083d9776cb2fb6056715b2988d1ea48055f3162 6.5.3
270 270 27055614b68538576fb0439007009acf93fe0a49 6.6rc0
271 271 26c57e7a0890b96e2c473b394de380d6753c9230 6.6
272 272 71bd09bebbe36a09569cbfb388f371433360056b 6.6.1
273 273 136902b3a95db38854ebaf5198a627641065c2ea 6.6.2
274 274 3fd1efb3ad124e6686c0fb66e6943cd8aeea5681 6.6.3
275 275 d1d48d18db37106b801ef6cb90955536458e7ffc 6.7rc0
276 c9ceb4f6025690167bdb245e530de6bac8baae95 6.7
@@ -1,231 +1,226 b''
1 1 # chainsaw.py
2 2 #
3 3 # Copyright 2022 Georges Racinet <georges.racinet@octobus.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7 """chainsaw is a collection of single-minded and dangerous tools. (EXPERIMENTAL)
8 8
9 9 "Don't use a chainsaw to cut your food!"
10 10
11 The chainsaw extension provides commands that are so much geared towards a
11 The chainsaw is a collection of commands that are so much geared towards a
12 12 specific use case in a specific context or environment that they are totally
13 13 inappropriate and **really dangerous** in other contexts.
14 14
15 15 The help text of each command explicitly summarizes its context of application
16 16 and the wanted end result.
17 17
18 18 It is recommended to run these commands with the ``HGPLAIN`` environment
19 19 variable (see :hg:`help scripting`).
20 20 """
21 21
22 22 import shutil
23 23
24 from mercurial.i18n import _
25 from mercurial import (
24 from ..i18n import _
25 from .. import (
26 26 cmdutil,
27 27 commands,
28 28 error,
29 29 localrepo,
30 30 registrar,
31 31 )
32 from mercurial.utils import (
32 from ..utils import (
33 33 urlutil,
34 34 )
35 35
36 36 cmdtable = {}
37 37 command = registrar.command(cmdtable)
38 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
39 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
40 # be specifying the version(s) of Mercurial they are tested with, or
41 # leave the attribute unspecified.
42 testedwith = b'ships-with-hg-core'
43 38
44 39
45 40 @command(
46 41 b'admin::chainsaw-update',
47 42 [
48 43 (
49 44 b'',
50 45 b'purge-unknown',
51 46 True,
52 47 _(
53 48 b'Remove unversioned files before update. Disabling this can '
54 49 b'in some cases interfere with the update.'
55 50 b'See also :hg:`purge`.'
56 51 ),
57 52 ),
58 53 (
59 54 b'',
60 55 b'purge-ignored',
61 56 True,
62 57 _(
63 58 b'Remove ignored files before update. Disable this for '
64 59 b'instance to reuse previous compiler object files. '
65 60 b'See also :hg:`purge`.'
66 61 ),
67 62 ),
68 63 (
69 64 b'',
70 65 b'rev',
71 66 b'',
72 67 _(b'revision to update to'),
73 68 ),
74 69 (
75 70 b'',
76 71 b'source',
77 72 b'',
78 73 _(b'repository to clone from'),
79 74 ),
80 75 (
81 76 b'',
82 77 b'dest',
83 78 b'',
84 79 _(b'repository to update to REV (possibly cloning)'),
85 80 ),
86 81 (
87 82 b'',
88 83 b'initial-clone-minimal',
89 84 False,
90 85 _(
91 86 b'Pull only the prescribed revision upon initial cloning. '
92 87 b'This has the side effect of ignoring clone-bundles, '
93 88 b'which if often slower on the client side and stressful '
94 89 b'to the server than applying available clone bundles.'
95 90 ),
96 91 ),
97 92 ],
98 93 _(
99 94 b'hg admin::chainsaw-update [OPTION] --rev REV --source SOURCE --dest DEST'
100 95 ),
101 96 helpbasic=True,
102 97 norepo=True,
103 98 )
104 99 def update(ui, **opts):
105 100 """pull and update to a given revision, no matter what, (EXPERIMENTAL)
106 101
107 102 Context of application: *some* Continuous Integration (CI) systems,
108 103 packaging or deployment tools.
109 104
110 105 Wanted end result: local repository at the given REPO_PATH, having the
111 106 latest changes to the given revision and with a clean working directory
112 107 updated at the given revision.
113 108
114 109 chainsaw-update pulls from one source, then updates the working directory
115 110 to the given revision, overcoming anything that would stand in the way.
116 111
117 112 By default, it will:
118 113
119 114 - clone if the local repo does not exist yet, **removing any directory
120 115 at the given path** that would not be a Mercurial repository.
121 116 The initial clone is full by default, so that clonebundles can be
122 117 applied. Use the --initial-clone-minimal flag to avoid this.
123 118 - break locks if needed, leading to possible corruption if there
124 119 is a concurrent write access.
125 120 - perform recovery actions if needed
126 121 - revert any local modification.
127 122 - purge unknown and ignored files.
128 123 - go as far as to reclone if everything else failed (not implemented yet).
129 124
130 125 DO NOT use it for anything else than performing a series
131 126 of unattended updates, with full exclusive repository access each time
132 127 and without any other local work than running build scripts.
133 128 In case the local repository is a share (see :hg:`help share`), exclusive
134 129 write access to the share source is also mandatory.
135 130
136 131 It is recommended to run these commands with the ``HGPLAIN`` environment
137 132 variable (see :hg:`scripting`).
138 133
139 134 Motivation: in Continuous Integration and Delivery systems (CI/CD), the
140 135 occasional remnant or bogus lock are common sources of waste of time (both
141 136 working time and calendar time). CI/CD scripts tend to grow with counter-
142 137 measures, often done in urgency. Also, whilst it is neat to keep
143 138 repositories from one job to the next (especially with large
144 139 repositories), an exceptional recloning is better than missing a release
145 140 deadline.
146 141 """
147 142 rev = opts['rev']
148 143 source = opts['source']
149 144 repo_path = opts['dest']
150 145 if not rev:
151 146 raise error.InputError(_(b'specify a target revision with --rev'))
152 147 if not source:
153 148 raise error.InputError(_(b'specify a pull path with --source'))
154 149 if not repo_path:
155 150 raise error.InputError(_(b'specify a repo path with --dest'))
156 151 repo_path = urlutil.urllocalpath(repo_path)
157 152
158 153 try:
159 154 repo = localrepo.instance(ui, repo_path, create=False)
160 155 repo_created = False
161 156 ui.status(_(b'loaded repository at "%s"\n' % repo_path))
162 157 except error.RepoError:
163 158 try:
164 159 shutil.rmtree(repo_path)
165 160 except FileNotFoundError:
166 161 ui.status(_(b'no such directory: "%s"\n' % repo_path))
167 162 else:
168 163 ui.status(
169 164 _(
170 165 b'removed non-repository file or directory '
171 166 b'at "%s"' % repo_path
172 167 )
173 168 )
174 169
175 170 ui.status(_(b'creating repository at "%s"\n' % repo_path))
176 171 repo = localrepo.instance(ui, repo_path, create=True)
177 172 repo_created = True
178 173
179 174 if repo.svfs.tryunlink(b'lock'):
180 175 ui.status(_(b'had to break store lock\n'))
181 176 if repo.vfs.tryunlink(b'wlock'):
182 177 ui.status(_(b'had to break working copy lock\n'))
183 178 # If another process relock after the breacking above, the next locking
184 179 # will have to wait.
185 180 with repo.wlock(), repo.lock():
186 181 ui.status(_(b'recovering after interrupted transaction, if any\n'))
187 182 repo.recover()
188 183
189 184 ui.status(_(b'pulling from %s\n') % source)
190 185 if repo_created and not opts.get('initial_clone_minimal'):
191 186 pull_revs = []
192 187 else:
193 188 pull_revs = [rev]
194 189 overrides = {(b'ui', b'quiet'): True}
195 190 with repo.ui.configoverride(overrides, b'chainsaw-update'):
196 191 pull = cmdutil.findcmd(b'pull', commands.table)[1][0]
197 192 ret = pull(
198 193 repo.ui,
199 194 repo,
200 195 source,
201 196 rev=pull_revs,
202 197 remote_hidden=False,
203 198 )
204 199 if ret:
205 200 return ret
206 201
207 202 purge = cmdutil.findcmd(b'purge', commands.table)[1][0]
208 203 ret = purge(
209 204 ui,
210 205 repo,
211 206 dirs=True,
212 207 all=opts.get('purge_ignored'),
213 208 files=opts.get('purge_unknown'),
214 209 confirm=False,
215 210 )
216 211 if ret:
217 212 return ret
218 213
219 214 ui.status(_(b'updating to revision \'%s\'\n') % rev)
220 215 update = cmdutil.findcmd(b'update', commands.table)[1][0]
221 216 ret = update(ui, repo, rev=rev, clean=True)
222 217 if ret:
223 218 return ret
224 219
225 220 ui.status(
226 221 _(
227 222 b'chainsaw-update to revision \'%s\' '
228 223 b'for repository at \'%s\' done\n'
229 224 )
230 225 % (rev, repo.root)
231 226 )
@@ -1,49 +1,50 b''
1 1 # admin_commands.py - command processing for admin* commands
2 2 #
3 3 # Copyright 2022 Mercurial Developers
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from .i18n import _
9 from .admin import verify
9 from .admin import chainsaw, verify
10 10 from . import error, registrar, transaction
11 11
12 12
13 13 table = {}
14 table.update(chainsaw.command._table)
14 15 command = registrar.command(table)
15 16
16 17
17 18 @command(
18 19 b'admin::verify',
19 20 [
20 21 (b'c', b'check', [], _(b'add a check'), _(b'CHECK')),
21 22 (b'o', b'option', [], _(b'pass an option to a check'), _(b'OPTION')),
22 23 ],
23 24 helpcategory=command.CATEGORY_MAINTENANCE,
24 25 )
25 26 def admin_verify(ui, repo, **opts):
26 27 """verify the integrity of the repository
27 28
28 29 Alternative UI to `hg verify` with a lot more control over the
29 30 verification process and better error reporting.
30 31 """
31 32
32 33 if not repo.url().startswith(b'file:'):
33 34 raise error.Abort(_(b"cannot verify bundle or remote repos"))
34 35
35 36 if transaction.has_abandoned_transaction(repo):
36 37 ui.warn(_(b"abandoned transaction found - run hg recover\n"))
37 38
38 39 checks = opts.get("check", [])
39 40 options = opts.get("option", [])
40 41
41 42 funcs = verify.get_checks(repo, ui, names=checks, options=options)
42 43
43 44 ui.status(_(b"running %d checks\n") % len(funcs))
44 45 # Done in two times so the execution is separated from the resolving step
45 46 for name, func in sorted(funcs.items(), key=lambda x: x[0]):
46 47 ui.status(_(b"running %s\n") % name)
47 48 errors = func()
48 49 if errors:
49 50 ui.warn(_(b"found %d errors\n") % len(errors))
@@ -1,1079 +1,1079 b''
1 1 # branchmap.py - logic to computes, maintain and stores branchmap for local repo
2 2 #
3 3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8
9 9 import struct
10 10
11 11 from .node import (
12 12 bin,
13 13 hex,
14 14 nullrev,
15 15 )
16 16
17 17 from typing import (
18 18 Any,
19 19 Callable,
20 20 Dict,
21 21 Iterable,
22 22 List,
23 23 Optional,
24 24 Set,
25 25 TYPE_CHECKING,
26 26 Tuple,
27 27 Union,
28 28 )
29 29
30 30 from . import (
31 31 encoding,
32 32 error,
33 33 obsolete,
34 34 scmutil,
35 35 util,
36 36 )
37 37
38 38 from .utils import (
39 39 repoviewutil,
40 40 stringutil,
41 41 )
42 42
43 43 if TYPE_CHECKING:
44 44 from . import localrepo
45 45
46 46 assert [localrepo]
47 47
48 48 subsettable = repoviewutil.subsettable
49 49
50 50 calcsize = struct.calcsize
51 51 pack_into = struct.pack_into
52 52 unpack_from = struct.unpack_from
53 53
54 54
55 55 class BranchMapCache:
56 56 """mapping of filtered views of repo with their branchcache"""
57 57
58 58 def __init__(self):
59 59 self._per_filter = {}
60 60
61 61 def __getitem__(self, repo):
62 62 self.updatecache(repo)
63 63 bcache = self._per_filter[repo.filtername]
64 64 assert bcache._filtername == repo.filtername, (
65 65 bcache._filtername,
66 66 repo.filtername,
67 67 )
68 68 return bcache
69 69
70 70 def update_disk(self, repo):
71 71 """ensure and up-to-date cache is (or will be) written on disk
72 72
73 73 The cache for this repository view is updated if needed and written on
74 74 disk.
75 75
76 76 If a transaction is in progress, the writing is schedule to transaction
77 77 close. See the `BranchMapCache.write_dirty` method.
78 78
79 79 This method exist independently of __getitem__ as it is sometime useful
80 80 to signal that we have no intend to use the data in memory yet.
81 81 """
82 82 self.updatecache(repo)
83 83 bcache = self._per_filter[repo.filtername]
84 84 assert bcache._filtername == repo.filtername, (
85 85 bcache._filtername,
86 86 repo.filtername,
87 87 )
88 88 tr = repo.currenttransaction()
89 89 if getattr(tr, 'finalized', True):
90 90 bcache.sync_disk(repo)
91 91
92 92 def updatecache(self, repo):
93 93 """Update the cache for the given filtered view on a repository"""
94 94 # This can trigger updates for the caches for subsets of the filtered
95 95 # view, e.g. when there is no cache for this filtered view or the cache
96 96 # is stale.
97 97
98 98 cl = repo.changelog
99 99 filtername = repo.filtername
100 100 bcache = self._per_filter.get(filtername)
101 101 if bcache is None or not bcache.validfor(repo):
102 102 # cache object missing or cache object stale? Read from disk
103 103 bcache = branchcache.fromfile(repo)
104 104
105 105 revs = []
106 106 if bcache is None:
107 107 # no (fresh) cache available anymore, perhaps we can re-use
108 108 # the cache for a subset, then extend that to add info on missing
109 109 # revisions.
110 110 subsetname = subsettable.get(filtername)
111 111 if subsetname is not None:
112 112 subset = repo.filtered(subsetname)
113 113 self.updatecache(subset)
114 114 bcache = self._per_filter[subset.filtername].inherit_for(repo)
115 115 extrarevs = subset.changelog.filteredrevs - cl.filteredrevs
116 116 revs.extend(r for r in extrarevs if r <= bcache.tiprev)
117 117 else:
118 118 # nothing to fall back on, start empty.
119 119 bcache = branchcache(repo)
120 120
121 121 revs.extend(cl.revs(start=bcache.tiprev + 1))
122 122 if revs:
123 123 bcache.update(repo, revs)
124 124
125 125 assert bcache.validfor(repo), filtername
126 126 self._per_filter[repo.filtername] = bcache
127 127
128 128 def replace(self, repo, remotebranchmap):
129 129 """Replace the branchmap cache for a repo with a branch mapping.
130 130
131 131 This is likely only called during clone with a branch map from a
132 132 remote.
133 133
134 134 """
135 135 cl = repo.changelog
136 136 clrev = cl.rev
137 137 clbranchinfo = cl.branchinfo
138 138 rbheads = []
139 139 closed = set()
140 140 for bheads in remotebranchmap.values():
141 141 rbheads += bheads
142 142 for h in bheads:
143 143 r = clrev(h)
144 144 b, c = clbranchinfo(r)
145 145 if c:
146 146 closed.add(h)
147 147
148 148 if rbheads:
149 149 rtiprev = max((int(clrev(node)) for node in rbheads))
150 150 cache = branchcache(
151 151 repo,
152 152 remotebranchmap,
153 153 repo[rtiprev].node(),
154 154 rtiprev,
155 155 closednodes=closed,
156 156 )
157 157
158 158 # Try to stick it as low as possible
159 159 # filter above served are unlikely to be fetch from a clone
160 160 for candidate in (b'base', b'immutable', b'served'):
161 161 rview = repo.filtered(candidate)
162 162 if cache.validfor(rview):
163 163 cache._filtername = candidate
164 164 self._per_filter[candidate] = cache
165 165 cache._state = STATE_DIRTY
166 166 cache.write(rview)
167 167 return
168 168
169 169 def clear(self):
170 170 self._per_filter.clear()
171 171
172 172 def write_dirty(self, repo):
173 173 unfi = repo.unfiltered()
174 174 for filtername in repoviewutil.get_ordered_subset():
175 175 cache = self._per_filter.get(filtername)
176 176 if cache is None:
177 177 continue
178 178 if filtername is None:
179 179 repo = unfi
180 180 else:
181 181 repo = unfi.filtered(filtername)
182 182 cache.sync_disk(repo)
183 183
184 184
185 185 def _unknownnode(node):
186 186 """raises ValueError when branchcache found a node which does not exists"""
187 187 raise ValueError('node %s does not exist' % node.hex())
188 188
189 189
190 190 def _branchcachedesc(repo):
191 191 if repo.filtername is not None:
192 192 return b'branch cache (%s)' % repo.filtername
193 193 else:
194 194 return b'branch cache'
195 195
196 196
197 197 class _BaseBranchCache:
198 198 """A dict like object that hold branches heads cache.
199 199
200 200 This cache is used to avoid costly computations to determine all the
201 201 branch heads of a repo.
202 202
203 203 The cache is serialized on disk in the following format:
204 204
205 205 <tip hex node> <tip rev number> [optional filtered repo hex hash]
206 206 <branch head hex node> <open/closed state> <branch name>
207 207 <branch head hex node> <open/closed state> <branch name>
208 208 ...
209 209
210 210 The first line is used to check if the cache is still valid. If the
211 211 branch cache is for a filtered repo view, an optional third hash is
212 212 included that hashes the hashes of all filtered and obsolete revisions.
213 213
214 214 The open/closed state is represented by a single letter 'o' or 'c'.
215 215 This field can be used to avoid changelog reads when determining if a
216 216 branch head closes a branch or not.
217 217 """
218 218
219 219 def __init__(
220 220 self,
221 221 repo: "localrepo.localrepository",
222 222 entries: Union[
223 223 Dict[bytes, List[bytes]], Iterable[Tuple[bytes, List[bytes]]]
224 224 ] = (),
225 225 closed_nodes: Optional[Set[bytes]] = None,
226 226 ) -> None:
227 227 """hasnode is a function which can be used to verify whether changelog
228 228 has a given node or not. If it's not provided, we assume that every node
229 229 we have exists in changelog"""
230 230 # closednodes is a set of nodes that close their branch. If the branch
231 231 # cache has been updated, it may contain nodes that are no longer
232 232 # heads.
233 233 if closed_nodes is None:
234 234 closed_nodes = set()
235 235 self._closednodes = set(closed_nodes)
236 236 self._entries = dict(entries)
237 237
238 238 def __iter__(self):
239 239 return iter(self._entries)
240 240
241 241 def __setitem__(self, key, value):
242 242 self._entries[key] = value
243 243
244 244 def __getitem__(self, key):
245 245 return self._entries[key]
246 246
247 247 def __contains__(self, key):
248 248 return key in self._entries
249 249
250 250 def iteritems(self):
251 251 return self._entries.items()
252 252
253 253 items = iteritems
254 254
255 255 def hasbranch(self, label):
256 256 """checks whether a branch of this name exists or not"""
257 257 return label in self._entries
258 258
259 259 def _branchtip(self, heads):
260 260 """Return tuple with last open head in heads and false,
261 261 otherwise return last closed head and true."""
262 262 tip = heads[-1]
263 263 closed = True
264 264 for h in reversed(heads):
265 265 if h not in self._closednodes:
266 266 tip = h
267 267 closed = False
268 268 break
269 269 return tip, closed
270 270
271 271 def branchtip(self, branch):
272 272 """Return the tipmost open head on branch head, otherwise return the
273 273 tipmost closed head on branch.
274 274 Raise KeyError for unknown branch."""
275 275 return self._branchtip(self[branch])[0]
276 276
277 277 def iteropen(self, nodes):
278 278 return (n for n in nodes if n not in self._closednodes)
279 279
280 280 def branchheads(self, branch, closed=False):
281 281 heads = self._entries[branch]
282 282 if not closed:
283 283 heads = list(self.iteropen(heads))
284 284 return heads
285 285
286 286 def iterbranches(self):
287 287 for bn, heads in self.items():
288 288 yield (bn, heads) + self._branchtip(heads)
289 289
290 290 def iterheads(self):
291 291 """returns all the heads"""
292 292 return self._entries.values()
293 293
294 294 def update(self, repo, revgen):
295 295 """Given a branchhead cache, self, that may have extra nodes or be
296 296 missing heads, and a generator of nodes that are strictly a superset of
297 297 heads missing, this function updates self to be correct.
298 298 """
299 299 starttime = util.timer()
300 300 cl = repo.changelog
301 301 # collect new branch entries
302 302 newbranches = {}
303 303 getbranchinfo = repo.revbranchcache().branchinfo
304 304 max_rev = -1
305 305 for r in revgen:
306 306 branch, closesbranch = getbranchinfo(r)
307 307 newbranches.setdefault(branch, []).append(r)
308 308 if closesbranch:
309 309 self._closednodes.add(cl.node(r))
310 310 max_rev = max(max_rev, r)
311 311 if max_rev < 0:
312 312 msg = "running branchcache.update without revision to update"
313 313 raise error.ProgrammingError(msg)
314 314
315 315 # Delay fetching the topological heads until they are needed.
316 316 # A repository without non-continous branches can skip this part.
317 317 topoheads = None
318 318
319 319 # If a changeset is visible, its parents must be visible too, so
320 320 # use the faster unfiltered parent accessor.
321 321 parentrevs = repo.unfiltered().changelog.parentrevs
322 322
323 323 # Faster than using ctx.obsolete()
324 324 obsrevs = obsolete.getrevs(repo, b'obsolete')
325 325
326 326 for branch, newheadrevs in newbranches.items():
327 327 # For every branch, compute the new branchheads.
328 328 # A branchhead is a revision such that no descendant is on
329 329 # the same branch.
330 330 #
331 331 # The branchheads are computed iteratively in revision order.
332 332 # This ensures topological order, i.e. parents are processed
333 333 # before their children. Ancestors are inclusive here, i.e.
334 334 # any revision is an ancestor of itself.
335 335 #
336 336 # Core observations:
337 337 # - The current revision is always a branchhead for the
338 338 # repository up to that point.
339 339 # - It is the first revision of the branch if and only if
340 340 # there was no branchhead before. In that case, it is the
341 341 # only branchhead as there are no possible ancestors on
342 342 # the same branch.
343 343 # - If a parent is on the same branch, a branchhead can
344 344 # only be an ancestor of that parent, if it is parent
345 345 # itself. Otherwise it would have been removed as ancestor
346 346 # of that parent before.
347 347 # - Therefore, if all parents are on the same branch, they
348 348 # can just be removed from the branchhead set.
349 349 # - If one parent is on the same branch and the other is not
350 350 # and there was exactly one branchhead known, the existing
351 351 # branchhead can only be an ancestor if it is the parent.
352 352 # Otherwise it would have been removed as ancestor of
353 353 # the parent before. The other parent therefore can't have
354 354 # a branchhead as ancestor.
355 355 # - In all other cases, the parents on different branches
356 356 # could have a branchhead as ancestor. Those parents are
357 357 # kept in the "uncertain" set. If all branchheads are also
358 358 # topological heads, they can't have descendants and further
359 359 # checks can be skipped. Otherwise, the ancestors of the
360 360 # "uncertain" set are removed from branchheads.
361 361 # This computation is heavy and avoided if at all possible.
362 362 bheads = self._entries.get(branch, [])
363 363 bheadset = {cl.rev(node) for node in bheads}
364 364 uncertain = set()
365 365 for newrev in sorted(newheadrevs):
366 366 if newrev in obsrevs:
367 367 # We ignore obsolete changesets as they shouldn't be
368 368 # considered heads.
369 369 continue
370 370
371 371 if not bheadset:
372 372 bheadset.add(newrev)
373 373 continue
374 374
375 375 parents = [p for p in parentrevs(newrev) if p != nullrev]
376 376 samebranch = set()
377 377 otherbranch = set()
378 378 obsparents = set()
379 379 for p in parents:
380 380 if p in obsrevs:
381 381 # We ignored this obsolete changeset earlier, but now
382 382 # that it has non-ignored children, we need to make
383 383 # sure their ancestors are not considered heads. To
384 384 # achieve that, we will simply treat this obsolete
385 385 # changeset as a parent from other branch.
386 386 obsparents.add(p)
387 387 elif p in bheadset or getbranchinfo(p)[0] == branch:
388 388 samebranch.add(p)
389 389 else:
390 390 otherbranch.add(p)
391 391 if not (len(bheadset) == len(samebranch) == 1):
392 392 uncertain.update(otherbranch)
393 393 uncertain.update(obsparents)
394 394 bheadset.difference_update(samebranch)
395 395 bheadset.add(newrev)
396 396
397 397 if uncertain:
398 398 if topoheads is None:
399 399 topoheads = set(cl.headrevs())
400 400 if bheadset - topoheads:
401 401 floorrev = min(bheadset)
402 402 if floorrev <= max(uncertain):
403 403 ancestors = set(cl.ancestors(uncertain, floorrev))
404 404 bheadset -= ancestors
405 405 if bheadset:
406 406 self[branch] = [cl.node(rev) for rev in sorted(bheadset)]
407 407
408 408 duration = util.timer() - starttime
409 409 repo.ui.log(
410 410 b'branchcache',
411 411 b'updated %s in %.4f seconds\n',
412 412 _branchcachedesc(repo),
413 413 duration,
414 414 )
415 415 return max_rev
416 416
417 417
418 418 STATE_CLEAN = 1
419 419 STATE_INHERITED = 2
420 420 STATE_DIRTY = 3
421 421
422 422
423 423 class branchcache(_BaseBranchCache):
424 424 """Branchmap info for a local repo or repoview"""
425 425
426 426 _base_filename = b"branch2"
427 427
428 428 def __init__(
429 429 self,
430 430 repo: "localrepo.localrepository",
431 431 entries: Union[
432 432 Dict[bytes, List[bytes]], Iterable[Tuple[bytes, List[bytes]]]
433 433 ] = (),
434 434 tipnode: Optional[bytes] = None,
435 435 tiprev: Optional[int] = nullrev,
436 436 filteredhash: Optional[bytes] = None,
437 437 closednodes: Optional[Set[bytes]] = None,
438 438 hasnode: Optional[Callable[[bytes], bool]] = None,
439 439 verify_node: bool = False,
440 440 inherited: bool = False,
441 441 ) -> None:
442 442 """hasnode is a function which can be used to verify whether changelog
443 443 has a given node or not. If it's not provided, we assume that every node
444 444 we have exists in changelog"""
445 445 self._filtername = repo.filtername
446 446 if tipnode is None:
447 447 self.tipnode = repo.nullid
448 448 else:
449 449 self.tipnode = tipnode
450 450 self.tiprev = tiprev
451 451 self.filteredhash = filteredhash
452 452 self._state = STATE_CLEAN
453 453 if inherited:
454 454 self._state = STATE_INHERITED
455 455
456 456 super().__init__(repo=repo, entries=entries, closed_nodes=closednodes)
457 457 # closednodes is a set of nodes that close their branch. If the branch
458 458 # cache has been updated, it may contain nodes that are no longer
459 459 # heads.
460 460
461 461 # Do we need to verify branch at all ?
462 462 self._verify_node = verify_node
463 463 # branches for which nodes are verified
464 464 self._verifiedbranches = set()
465 465 self._hasnode = None
466 466 if self._verify_node:
467 467 self._hasnode = repo.changelog.hasnode
468 468
469 469 def validfor(self, repo):
470 470 """check that cache contents are valid for (a subset of) this repo
471 471
472 472 - False when the order of changesets changed or if we detect a strip.
473 473 - True when cache is up-to-date for the current repo or its subset."""
474 474 try:
475 475 node = repo.changelog.node(self.tiprev)
476 476 except IndexError:
477 477 # changesets were stripped and now we don't even have enough to
478 478 # find tiprev
479 479 return False
480 480 if self.tipnode != node:
481 481 # tiprev doesn't correspond to tipnode: repo was stripped, or this
482 482 # repo has a different order of changesets
483 483 return False
484 484 tiphash = scmutil.filteredhash(repo, self.tiprev, needobsolete=True)
485 485 # hashes don't match if this repo view has a different set of filtered
486 486 # revisions (e.g. due to phase changes) or obsolete revisions (e.g.
487 487 # history was rewritten)
488 488 return self.filteredhash == tiphash
489 489
490 490 @classmethod
491 491 def fromfile(cls, repo):
492 492 f = None
493 493 try:
494 494 f = repo.cachevfs(cls._filename(repo))
495 495 lineiter = iter(f)
496 496 init_kwargs = cls._load_header(repo, lineiter)
497 497 bcache = cls(
498 498 repo,
499 499 verify_node=True,
500 500 **init_kwargs,
501 501 )
502 502 if not bcache.validfor(repo):
503 503 # invalidate the cache
504 504 raise ValueError('tip differs')
505 505 bcache._load_heads(repo, lineiter)
506 506 except (IOError, OSError):
507 507 return None
508 508
509 509 except Exception as inst:
510 510 if repo.ui.debugflag:
511 511 msg = b'invalid %s: %s\n'
512 512 msg %= (
513 513 _branchcachedesc(repo),
514 514 stringutil.forcebytestr(inst),
515 515 )
516 516 repo.ui.debug(msg)
517 517 bcache = None
518 518
519 519 finally:
520 520 if f:
521 521 f.close()
522 522
523 523 return bcache
524 524
525 525 @classmethod
526 526 def _load_header(cls, repo, lineiter) -> "dict[str, Any]":
527 527 """parse the head of a branchmap file
528 528
529 529 return parameters to pass to a newly created class instance.
530 530 """
531 531 cachekey = next(lineiter).rstrip(b'\n').split(b" ", 2)
532 532 last, lrev = cachekey[:2]
533 533 last, lrev = bin(last), int(lrev)
534 534 filteredhash = None
535 535 if len(cachekey) > 2:
536 536 filteredhash = bin(cachekey[2])
537 537 return {
538 538 "tipnode": last,
539 539 "tiprev": lrev,
540 540 "filteredhash": filteredhash,
541 541 }
542 542
543 543 def _load_heads(self, repo, lineiter):
544 544 """fully loads the branchcache by reading from the file using the line
545 545 iterator passed"""
546 546 for line in lineiter:
547 547 line = line.rstrip(b'\n')
548 548 if not line:
549 549 continue
550 550 node, state, label = line.split(b" ", 2)
551 551 if state not in b'oc':
552 552 raise ValueError('invalid branch state')
553 553 label = encoding.tolocal(label.strip())
554 554 node = bin(node)
555 555 self._entries.setdefault(label, []).append(node)
556 556 if state == b'c':
557 557 self._closednodes.add(node)
558 558
559 559 @classmethod
560 560 def _filename(cls, repo):
561 561 """name of a branchcache file for a given repo or repoview"""
562 562 filename = cls._base_filename
563 563 if repo.filtername:
564 564 filename = b'%s-%s' % (filename, repo.filtername)
565 565 return filename
566 566
567 567 def inherit_for(self, repo):
568 568 """return a deep copy of the branchcache object"""
569 569 assert repo.filtername != self._filtername
570 570 other = type(self)(
571 571 repo=repo,
572 572 # we always do a shally copy of self._entries, and the values is
573 573 # always replaced, so no need to deepcopy until the above remains
574 574 # true.
575 575 entries=self._entries,
576 576 tipnode=self.tipnode,
577 577 tiprev=self.tiprev,
578 578 filteredhash=self.filteredhash,
579 579 closednodes=set(self._closednodes),
580 580 verify_node=self._verify_node,
581 581 inherited=True,
582 582 )
583 583 # also copy information about the current verification state
584 584 other._verifiedbranches = set(self._verifiedbranches)
585 585 return other
586 586
587 587 def sync_disk(self, repo):
588 588 """synchronise the on disk file with the cache state
589 589
590 590 If new value specific to this filter level need to be written, the file
591 591 will be updated, if the state of the branchcache is inherited from a
592 592 subset, any stalled on disk file will be deleted.
593 593
594 594 That method does nothing if there is nothing to do.
595 595 """
596 596 if self._state == STATE_DIRTY:
597 597 self.write(repo)
598 598 elif self._state == STATE_INHERITED:
599 599 filename = self._filename(repo)
600 600 repo.cachevfs.tryunlink(filename)
601 601
602 602 def write(self, repo):
603 603 assert self._filtername == repo.filtername, (
604 604 self._filtername,
605 605 repo.filtername,
606 606 )
607 607 assert self._state == STATE_DIRTY, self._state
608 608 # This method should not be called during an open transaction
609 609 tr = repo.currenttransaction()
610 610 if not getattr(tr, 'finalized', True):
611 611 msg = "writing branchcache in the middle of a transaction"
612 612 raise error.ProgrammingError(msg)
613 613 try:
614 614 filename = self._filename(repo)
615 615 with repo.cachevfs(filename, b"w", atomictemp=True) as f:
616 616 self._write_header(f)
617 617 nodecount = self._write_heads(f)
618 618 repo.ui.log(
619 619 b'branchcache',
620 620 b'wrote %s with %d labels and %d nodes\n',
621 621 _branchcachedesc(repo),
622 622 len(self._entries),
623 623 nodecount,
624 624 )
625 625 self._state = STATE_CLEAN
626 626 except (IOError, OSError, error.Abort) as inst:
627 627 # Abort may be raised by read only opener, so log and continue
628 628 repo.ui.debug(
629 629 b"couldn't write branch cache: %s\n"
630 630 % stringutil.forcebytestr(inst)
631 631 )
632 632
633 633 def _write_header(self, fp) -> None:
634 634 """write the branch cache header to a file"""
635 635 cachekey = [hex(self.tipnode), b'%d' % self.tiprev]
636 636 if self.filteredhash is not None:
637 637 cachekey.append(hex(self.filteredhash))
638 638 fp.write(b" ".join(cachekey) + b'\n')
639 639
640 640 def _write_heads(self, fp) -> int:
641 641 """write list of heads to a file
642 642
643 643 Return the number of heads written."""
644 644 nodecount = 0
645 645 for label, nodes in sorted(self._entries.items()):
646 646 label = encoding.fromlocal(label)
647 647 for node in nodes:
648 648 nodecount += 1
649 649 if node in self._closednodes:
650 650 state = b'c'
651 651 else:
652 652 state = b'o'
653 653 fp.write(b"%s %s %s\n" % (hex(node), state, label))
654 654 return nodecount
655 655
656 656 def _verifybranch(self, branch):
657 657 """verify head nodes for the given branch."""
658 658 if not self._verify_node:
659 659 return
660 660 if branch not in self._entries or branch in self._verifiedbranches:
661 661 return
662 662 assert self._hasnode is not None
663 663 for n in self._entries[branch]:
664 664 if not self._hasnode(n):
665 665 _unknownnode(n)
666 666
667 667 self._verifiedbranches.add(branch)
668 668
669 669 def _verifyall(self):
670 670 """verifies nodes of all the branches"""
671 671 for b in self._entries.keys():
672 672 if b not in self._verifiedbranches:
673 673 self._verifybranch(b)
674 674
675 675 def __getitem__(self, key):
676 676 self._verifybranch(key)
677 677 return super().__getitem__(key)
678 678
679 679 def __contains__(self, key):
680 680 self._verifybranch(key)
681 681 return super().__contains__(key)
682 682
683 683 def iteritems(self):
684 684 self._verifyall()
685 685 return super().iteritems()
686 686
687 687 items = iteritems
688 688
689 689 def iterheads(self):
690 690 """returns all the heads"""
691 691 self._verifyall()
692 692 return super().iterheads()
693 693
694 694 def hasbranch(self, label):
695 695 """checks whether a branch of this name exists or not"""
696 696 self._verifybranch(label)
697 697 return super().hasbranch(label)
698 698
699 699 def branchheads(self, branch, closed=False):
700 700 self._verifybranch(branch)
701 701 return super().branchheads(branch, closed=closed)
702 702
703 703 def update(self, repo, revgen):
704 704 assert self._filtername == repo.filtername, (
705 705 self._filtername,
706 706 repo.filtername,
707 707 )
708 708 cl = repo.changelog
709 709 max_rev = super().update(repo, revgen)
710 710 # new tip revision which we found after iterating items from new
711 711 # branches
712 712 if max_rev is not None and max_rev > self.tiprev:
713 713 self.tiprev = max_rev
714 714 self.tipnode = cl.node(max_rev)
715 715
716 716 if not self.validfor(repo):
717 717 # old cache key is now invalid for the repo, but we've just updated
718 718 # the cache and we assume it's valid, so let's make the cache key
719 719 # valid as well by recomputing it from the cached data
720 720 self.tipnode = repo.nullid
721 721 self.tiprev = nullrev
722 722 for heads in self.iterheads():
723 723 if not heads:
724 724 # all revisions on a branch are obsolete
725 725 continue
726 726 # note: tiprev is not necessarily the tip revision of repo,
727 727 # because the tip could be obsolete (i.e. not a head)
728 728 tiprev = max(cl.rev(node) for node in heads)
729 729 if tiprev > self.tiprev:
730 730 self.tipnode = cl.node(tiprev)
731 731 self.tiprev = tiprev
732 732 self.filteredhash = scmutil.filteredhash(
733 733 repo, self.tiprev, needobsolete=True
734 734 )
735 735 self._state = STATE_DIRTY
736 736 tr = repo.currenttransaction()
737 737 if getattr(tr, 'finalized', True):
738 738 # Avoid premature writing.
739 739 #
740 740 # (The cache warming setup by localrepo will update the file later.)
741 741 self.write(repo)
742 742
743 743
744 744 class remotebranchcache(_BaseBranchCache):
745 745 """Branchmap info for a remote connection, should not write locally"""
746 746
747 747 def __init__(
748 748 self,
749 749 repo: "localrepo.localrepository",
750 750 entries: Union[
751 751 Dict[bytes, List[bytes]], Iterable[Tuple[bytes, List[bytes]]]
752 752 ] = (),
753 753 closednodes: Optional[Set[bytes]] = None,
754 754 ) -> None:
755 755 super().__init__(repo=repo, entries=entries, closed_nodes=closednodes)
756 756
757 757
758 758 # Revision branch info cache
759 759
760 760 _rbcversion = b'-v1'
761 761 _rbcnames = b'rbc-names' + _rbcversion
762 762 _rbcrevs = b'rbc-revs' + _rbcversion
763 763 # [4 byte hash prefix][4 byte branch name number with sign bit indicating open]
764 764 _rbcrecfmt = b'>4sI'
765 765 _rbcrecsize = calcsize(_rbcrecfmt)
766 766 _rbcmininc = 64 * _rbcrecsize
767 767 _rbcnodelen = 4
768 768 _rbcbranchidxmask = 0x7FFFFFFF
769 769 _rbccloseflag = 0x80000000
770 770
771 771
772 772 class rbcrevs:
773 773 """a byte string consisting of an immutable prefix followed by a mutable suffix"""
774 774
775 775 def __init__(self, revs):
776 776 self._prefix = revs
777 777 self._rest = bytearray()
778 778
779 779 def __len__(self):
780 780 return len(self._prefix) + len(self._rest)
781 781
782 782 def unpack_record(self, rbcrevidx):
783 783 if rbcrevidx < len(self._prefix):
784 784 return unpack_from(_rbcrecfmt, util.buffer(self._prefix), rbcrevidx)
785 785 else:
786 786 return unpack_from(
787 787 _rbcrecfmt,
788 788 util.buffer(self._rest),
789 789 rbcrevidx - len(self._prefix),
790 790 )
791 791
792 792 def make_mutable(self):
793 793 if len(self._prefix) > 0:
794 794 entirety = bytearray()
795 795 entirety[:] = self._prefix
796 796 entirety.extend(self._rest)
797 797 self._rest = entirety
798 798 self._prefix = bytearray()
799 799
800 800 def truncate(self, pos):
801 801 self.make_mutable()
802 802 del self._rest[pos:]
803 803
804 804 def pack_into(self, rbcrevidx, node, branchidx):
805 805 if rbcrevidx < len(self._prefix):
806 806 self.make_mutable()
807 807 buf = self._rest
808 808 start_offset = rbcrevidx - len(self._prefix)
809 809 end_offset = start_offset + _rbcrecsize
810 810
811 811 if len(self._rest) < end_offset:
812 812 # bytearray doesn't allocate extra space at least in Python 3.7.
813 813 # When multiple changesets are added in a row, precise resize would
814 814 # result in quadratic complexity. Overallocate to compensate by
815 815 # using the classic doubling technique for dynamic arrays instead.
816 816 # If there was a gap in the map before, less space will be reserved.
817 817 self._rest.extend(b'\0' * end_offset)
818 818 return pack_into(
819 819 _rbcrecfmt,
820 820 buf,
821 821 start_offset,
822 822 node,
823 823 branchidx,
824 824 )
825 825
826 826 def extend(self, extension):
827 827 return self._rest.extend(extension)
828 828
829 829 def slice(self, begin, end):
830 830 if begin < len(self._prefix):
831 831 acc = bytearray()
832 832 acc[:] = self._prefix[begin:end]
833 833 acc.extend(
834 834 self._rest[begin - len(self._prefix) : end - len(self._prefix)]
835 835 )
836 836 return acc
837 837 return self._rest[begin - len(self._prefix) : end - len(self._prefix)]
838 838
839 839
840 840 class revbranchcache:
841 841 """Persistent cache, mapping from revision number to branch name and close.
842 842 This is a low level cache, independent of filtering.
843 843
844 844 Branch names are stored in rbc-names in internal encoding separated by 0.
845 845 rbc-names is append-only, and each branch name is only stored once and will
846 846 thus have a unique index.
847 847
848 848 The branch info for each revision is stored in rbc-revs as constant size
849 849 records. The whole file is read into memory, but it is only 'parsed' on
850 850 demand. The file is usually append-only but will be truncated if repo
851 851 modification is detected.
852 852 The record for each revision contains the first 4 bytes of the
853 853 corresponding node hash, and the record is only used if it still matches.
854 854 Even a completely trashed rbc-revs fill thus still give the right result
855 855 while converging towards full recovery ... assuming no incorrectly matching
856 856 node hashes.
857 857 The record also contains 4 bytes where 31 bits contains the index of the
858 858 branch and the last bit indicate that it is a branch close commit.
859 859 The usage pattern for rbc-revs is thus somewhat similar to 00changelog.i
860 860 and will grow with it but be 1/8th of its size.
861 861 """
862 862
863 863 def __init__(self, repo, readonly=True):
864 864 assert repo.filtername is None
865 865 self._repo = repo
866 866 self._names = [] # branch names in local encoding with static index
867 867 self._rbcrevs = rbcrevs(bytearray())
868 868 self._rbcsnameslen = 0 # length of names read at _rbcsnameslen
869 869 try:
870 870 bndata = repo.cachevfs.read(_rbcnames)
871 871 self._rbcsnameslen = len(bndata) # for verification before writing
872 872 if bndata:
873 873 self._names = [
874 874 encoding.tolocal(bn) for bn in bndata.split(b'\0')
875 875 ]
876 876 except (IOError, OSError):
877 877 if readonly:
878 878 # don't try to use cache - fall back to the slow path
879 879 self.branchinfo = self._branchinfo
880 880
881 881 if self._names:
882 882 try:
883 if repo.ui.configbool(b'format', b'mmap-revbranchcache'):
883 if repo.ui.configbool(b'storage', b'revbranchcache.mmap'):
884 884 with repo.cachevfs(_rbcrevs) as fp:
885 885 data = util.buffer(util.mmapread(fp))
886 886 else:
887 887 data = repo.cachevfs.read(_rbcrevs)
888 888 self._rbcrevs = rbcrevs(data)
889 889 except (IOError, OSError) as inst:
890 890 repo.ui.debug(
891 891 b"couldn't read revision branch cache: %s\n"
892 892 % stringutil.forcebytestr(inst)
893 893 )
894 894 # remember number of good records on disk
895 895 self._rbcrevslen = min(
896 896 len(self._rbcrevs) // _rbcrecsize, len(repo.changelog)
897 897 )
898 898 if self._rbcrevslen == 0:
899 899 self._names = []
900 900 self._rbcnamescount = len(self._names) # number of names read at
901 901 # _rbcsnameslen
902 902
903 903 def _clear(self):
904 904 self._rbcsnameslen = 0
905 905 del self._names[:]
906 906 self._rbcnamescount = 0
907 907 self._rbcrevslen = len(self._repo.changelog)
908 908 self._rbcrevs = rbcrevs(bytearray(self._rbcrevslen * _rbcrecsize))
909 909 util.clearcachedproperty(self, b'_namesreverse')
910 910
911 911 @util.propertycache
912 912 def _namesreverse(self):
913 913 return {b: r for r, b in enumerate(self._names)}
914 914
915 915 def branchinfo(self, rev):
916 916 """Return branch name and close flag for rev, using and updating
917 917 persistent cache."""
918 918 changelog = self._repo.changelog
919 919 rbcrevidx = rev * _rbcrecsize
920 920
921 921 # avoid negative index, changelog.read(nullrev) is fast without cache
922 922 if rev == nullrev:
923 923 return changelog.branchinfo(rev)
924 924
925 925 # if requested rev isn't allocated, grow and cache the rev info
926 926 if len(self._rbcrevs) < rbcrevidx + _rbcrecsize:
927 927 return self._branchinfo(rev)
928 928
929 929 # fast path: extract data from cache, use it if node is matching
930 930 reponode = changelog.node(rev)[:_rbcnodelen]
931 931 cachenode, branchidx = self._rbcrevs.unpack_record(rbcrevidx)
932 932 close = bool(branchidx & _rbccloseflag)
933 933 if close:
934 934 branchidx &= _rbcbranchidxmask
935 935 if cachenode == b'\0\0\0\0':
936 936 pass
937 937 elif cachenode == reponode:
938 938 try:
939 939 return self._names[branchidx], close
940 940 except IndexError:
941 941 # recover from invalid reference to unknown branch
942 942 self._repo.ui.debug(
943 943 b"referenced branch names not found"
944 944 b" - rebuilding revision branch cache from scratch\n"
945 945 )
946 946 self._clear()
947 947 else:
948 948 # rev/node map has changed, invalidate the cache from here up
949 949 self._repo.ui.debug(
950 950 b"history modification detected - truncating "
951 951 b"revision branch cache to revision %d\n" % rev
952 952 )
953 953 truncate = rbcrevidx + _rbcrecsize
954 954 self._rbcrevs.truncate(truncate)
955 955 self._rbcrevslen = min(self._rbcrevslen, truncate)
956 956
957 957 # fall back to slow path and make sure it will be written to disk
958 958 return self._branchinfo(rev)
959 959
960 960 def _branchinfo(self, rev):
961 961 """Retrieve branch info from changelog and update _rbcrevs"""
962 962 changelog = self._repo.changelog
963 963 b, close = changelog.branchinfo(rev)
964 964 if b in self._namesreverse:
965 965 branchidx = self._namesreverse[b]
966 966 else:
967 967 branchidx = len(self._names)
968 968 self._names.append(b)
969 969 self._namesreverse[b] = branchidx
970 970 reponode = changelog.node(rev)
971 971 if close:
972 972 branchidx |= _rbccloseflag
973 973 self._setcachedata(rev, reponode, branchidx)
974 974 return b, close
975 975
976 976 def setdata(self, rev, changelogrevision):
977 977 """add new data information to the cache"""
978 978 branch, close = changelogrevision.branchinfo
979 979
980 980 if branch in self._namesreverse:
981 981 branchidx = self._namesreverse[branch]
982 982 else:
983 983 branchidx = len(self._names)
984 984 self._names.append(branch)
985 985 self._namesreverse[branch] = branchidx
986 986 if close:
987 987 branchidx |= _rbccloseflag
988 988 self._setcachedata(rev, self._repo.changelog.node(rev), branchidx)
989 989 # If no cache data were readable (non exists, bad permission, etc)
990 990 # the cache was bypassing itself by setting:
991 991 #
992 992 # self.branchinfo = self._branchinfo
993 993 #
994 994 # Since we now have data in the cache, we need to drop this bypassing.
995 995 if 'branchinfo' in vars(self):
996 996 del self.branchinfo
997 997
998 998 def _setcachedata(self, rev, node, branchidx):
999 999 """Writes the node's branch data to the in-memory cache data."""
1000 1000 if rev == nullrev:
1001 1001 return
1002 1002 rbcrevidx = rev * _rbcrecsize
1003 1003 self._rbcrevs.pack_into(rbcrevidx, node, branchidx)
1004 1004 self._rbcrevslen = min(self._rbcrevslen, rev)
1005 1005
1006 1006 tr = self._repo.currenttransaction()
1007 1007 if tr:
1008 1008 tr.addfinalize(b'write-revbranchcache', self.write)
1009 1009
1010 1010 def write(self, tr=None):
1011 1011 """Save branch cache if it is dirty."""
1012 1012 repo = self._repo
1013 1013 wlock = None
1014 1014 step = b''
1015 1015 try:
1016 1016 # write the new names
1017 1017 if self._rbcnamescount < len(self._names):
1018 1018 wlock = repo.wlock(wait=False)
1019 1019 step = b' names'
1020 1020 self._writenames(repo)
1021 1021
1022 1022 # write the new revs
1023 1023 start = self._rbcrevslen * _rbcrecsize
1024 1024 if start != len(self._rbcrevs):
1025 1025 step = b''
1026 1026 if wlock is None:
1027 1027 wlock = repo.wlock(wait=False)
1028 1028 self._writerevs(repo, start)
1029 1029
1030 1030 except (IOError, OSError, error.Abort, error.LockError) as inst:
1031 1031 repo.ui.debug(
1032 1032 b"couldn't write revision branch cache%s: %s\n"
1033 1033 % (step, stringutil.forcebytestr(inst))
1034 1034 )
1035 1035 finally:
1036 1036 if wlock is not None:
1037 1037 wlock.release()
1038 1038
1039 1039 def _writenames(self, repo):
1040 1040 """write the new branch names to revbranchcache"""
1041 1041 if self._rbcnamescount != 0:
1042 1042 f = repo.cachevfs.open(_rbcnames, b'ab')
1043 1043 if f.tell() == self._rbcsnameslen:
1044 1044 f.write(b'\0')
1045 1045 else:
1046 1046 f.close()
1047 1047 repo.ui.debug(b"%s changed - rewriting it\n" % _rbcnames)
1048 1048 self._rbcnamescount = 0
1049 1049 self._rbcrevslen = 0
1050 1050 if self._rbcnamescount == 0:
1051 1051 # before rewriting names, make sure references are removed
1052 1052 repo.cachevfs.unlinkpath(_rbcrevs, ignoremissing=True)
1053 1053 f = repo.cachevfs.open(_rbcnames, b'wb')
1054 1054 f.write(
1055 1055 b'\0'.join(
1056 1056 encoding.fromlocal(b)
1057 1057 for b in self._names[self._rbcnamescount :]
1058 1058 )
1059 1059 )
1060 1060 self._rbcsnameslen = f.tell()
1061 1061 f.close()
1062 1062 self._rbcnamescount = len(self._names)
1063 1063
1064 1064 def _writerevs(self, repo, start):
1065 1065 """write the new revs to revbranchcache"""
1066 1066 revs = min(len(repo.changelog), len(self._rbcrevs) // _rbcrecsize)
1067 1067 with repo.cachevfs.open(_rbcrevs, b'ab') as f:
1068 1068 if f.tell() != start:
1069 1069 repo.ui.debug(
1070 1070 b"truncating cache/%s to %d\n" % (_rbcrevs, start)
1071 1071 )
1072 1072 f.seek(start)
1073 1073 if f.tell() != start:
1074 1074 start = 0
1075 1075 f.seek(start)
1076 1076 f.truncate()
1077 1077 end = revs * _rbcrecsize
1078 1078 f.write(self._rbcrevs.slice(start, end))
1079 1079 self._rbcrevslen = revs
@@ -1,2920 +1,2947 b''
1 1 # configitems.toml - centralized declaration of configuration options
2 2 #
3 3 # This file contains declarations of the core Mercurial configuration options.
4 4 #
5 5 # # Structure
6 6 #
7 7 # items: array of config items
8 8 # templates: mapping of template name to template declaration
9 9 # template-applications: array of template applications
10 10 #
11 11 # # Elements
12 12 #
13 13 # ## Item
14 14 #
15 15 # Declares a core Mercurial option.
16 16 #
17 17 # - section: string (required)
18 18 # - name: string (required)
19 19 # - default-type: boolean, changes how `default` is read
20 20 # - default: any
21 21 # - generic: boolean
22 22 # - priority: integer, only if `generic` is true
23 23 # - alias: list of 2-tuples of strings
24 24 # - experimental: boolean
25 25 # - documentation: string
26 26 # - in_core_extension: string
27 27 #
28 28 # ## Template
29 29 #
30 30 # Declares a group of options to be re-used for multiple sections.
31 31 #
32 32 # - all the same fields as `Item`, except `section` and `name`
33 33 # - `suffix` (string, required)
34 34 #
35 35 # ## Template applications
36 36 #
37 37 # Uses a `Template` to instanciate its options in a given section.
38 38 #
39 39 # - template: string (required, must match a `Template` name)
40 40 # - section: string (required)
41 41
42 42 [[items]]
43 43 section = "alias"
44 44 name = ".*"
45 45 default-type = "dynamic"
46 46 generic = true
47 47
48 48 [[items]]
49 49 section = "auth"
50 50 name = "cookiefile"
51 51
52 52 # bookmarks.pushing: internal hack for discovery
53 53 [[items]]
54 54 section = "bookmarks"
55 55 name = "pushing"
56 56 default-type = "list_type"
57 57
58 58 # bundle.mainreporoot: internal hack for bundlerepo
59 59 [[items]]
60 60 section = "bundle"
61 61 name = "mainreporoot"
62 62 default = ""
63 63
64 64 [[items]]
65 65 section = "censor"
66 66 name = "policy"
67 67 default = "abort"
68 68 experimental = true
69 69
70 70 [[items]]
71 71 section = "chgserver"
72 72 name = "idletimeout"
73 73 default = 3600
74 74
75 75 [[items]]
76 76 section = "chgserver"
77 77 name = "skiphash"
78 78 default = false
79 79
80 80 [[items]]
81 81 section = "cmdserver"
82 82 name = "log"
83 83
84 84 [[items]]
85 85 section = "cmdserver"
86 86 name = "max-log-files"
87 87 default = 7
88 88
89 89 [[items]]
90 90 section = "cmdserver"
91 91 name = "max-log-size"
92 92 default = "1 MB"
93 93
94 94 [[items]]
95 95 section = "cmdserver"
96 96 name = "max-repo-cache"
97 97 default = 0
98 98 experimental = true
99 99
100 100 [[items]]
101 101 section = "cmdserver"
102 102 name = "message-encodings"
103 103 default-type = "list_type"
104 104
105 105 [[items]]
106 106 section = "cmdserver"
107 107 name = "shutdown-on-interrupt"
108 108 default = true
109 109
110 110 [[items]]
111 111 section = "cmdserver"
112 112 name = "track-log"
113 113 default-type = "lambda"
114 114 default = [ "chgserver", "cmdserver", "repocache",]
115 115
116 116 [[items]]
117 117 section = "color"
118 118 name = ".*"
119 119 generic = true
120 120
121 121 [[items]]
122 122 section = "color"
123 123 name = "mode"
124 124 default = "auto"
125 125
126 126 [[items]]
127 127 section = "color"
128 128 name = "pagermode"
129 129 default-type = "dynamic"
130 130
131 131 [[items]]
132 132 section = "command-templates"
133 133 name = "graphnode"
134 134 alias = [["ui", "graphnodetemplate"]]
135 135
136 136 [[items]]
137 137 section = "command-templates"
138 138 name = "log"
139 139 alias = [["ui", "logtemplate"]]
140 140
141 141 [[items]]
142 142 section = "command-templates"
143 143 name = "mergemarker"
144 144 default = '{node|short} {ifeq(tags, "tip", "", ifeq(tags, "", "", "{tags} "))}{if(bookmarks, "{bookmarks} ")}{ifeq(branch, "default", "", "{branch} ")}- {author|user}: {desc|firstline}'
145 145 alias = [["ui", "mergemarkertemplate"]]
146 146
147 147 [[items]]
148 148 section = "command-templates"
149 149 name = "oneline-summary"
150 150
151 151 [[items]]
152 152 section = "command-templates"
153 153 name = "oneline-summary.*"
154 154 default-type = "dynamic"
155 155 generic = true
156 156
157 157 [[items]]
158 158 section = "command-templates"
159 159 name = "pre-merge-tool-output"
160 160 alias = [["ui", "pre-merge-tool-output-template"]]
161 161
162 162 [[items]]
163 163 section = "commands"
164 164 name = "commit.post-status"
165 165 default = false
166 166
167 167 [[items]]
168 168 section = "commands"
169 169 name = "grep.all-files"
170 170 default = false
171 171 experimental = true
172 172
173 173 [[items]]
174 174 section = "commands"
175 175 name = "merge.require-rev"
176 176 default = false
177 177
178 178 [[items]]
179 179 section = "commands"
180 180 name = "push.require-revs"
181 181 default = false
182 182
183 183 # Rebase related configuration moved to core because other extension are doing
184 184 # strange things. For example, shelve import the extensions to reuse some bit
185 185 # without formally loading it.
186 186 [[items]]
187 187 section = "commands"
188 188 name = "rebase.requiredest"
189 189 default = false
190 190
191 191 [[items]]
192 192 section = "commands"
193 193 name = "resolve.confirm"
194 194 default = false
195 195
196 196 [[items]]
197 197 section = "commands"
198 198 name = "resolve.explicit-re-merge"
199 199 default = false
200 200
201 201 [[items]]
202 202 section = "commands"
203 203 name = "resolve.mark-check"
204 204 default = "none"
205 205
206 206 [[items]]
207 207 section = "commands"
208 208 name = "show.aliasprefix"
209 209 default-type = "list_type"
210 210
211 211 [[items]]
212 212 section = "commands"
213 213 name = "status.relative"
214 214 default = false
215 215
216 216 [[items]]
217 217 section = "commands"
218 218 name = "status.skipstates"
219 219 default = []
220 220 experimental = true
221 221
222 222 [[items]]
223 223 section = "commands"
224 224 name = "status.terse"
225 225 default = ""
226 226
227 227 [[items]]
228 228 section = "commands"
229 229 name = "status.verbose"
230 230 default = false
231 231
232 232 [[items]]
233 233 section = "commands"
234 234 name = "update.check"
235 235
236 236 [[items]]
237 237 section = "commands"
238 238 name = "update.requiredest"
239 239 default = false
240 240
241 241 [[items]]
242 242 section = "committemplate"
243 243 name = ".*"
244 244 generic = true
245 245
246 246 [[items]]
247 247 section = "convert"
248 248 name = "bzr.saverev"
249 249 default = true
250 250
251 251 [[items]]
252 252 section = "convert"
253 253 name = "cvsps.cache"
254 254 default = true
255 255
256 256 [[items]]
257 257 section = "convert"
258 258 name = "cvsps.fuzz"
259 259 default = 60
260 260
261 261 [[items]]
262 262 section = "convert"
263 263 name = "cvsps.logencoding"
264 264
265 265 [[items]]
266 266 section = "convert"
267 267 name = "cvsps.mergefrom"
268 268
269 269 [[items]]
270 270 section = "convert"
271 271 name = "cvsps.mergeto"
272 272
273 273 [[items]]
274 274 section = "convert"
275 275 name = "git.committeractions"
276 276 default-type = "lambda"
277 277 default = [ "messagedifferent",]
278 278
279 279 [[items]]
280 280 section = "convert"
281 281 name = "git.extrakeys"
282 282 default-type = "list_type"
283 283
284 284 [[items]]
285 285 section = "convert"
286 286 name = "git.findcopiesharder"
287 287 default = false
288 288
289 289 [[items]]
290 290 section = "convert"
291 291 name = "git.remoteprefix"
292 292 default = "remote"
293 293
294 294 [[items]]
295 295 section = "convert"
296 296 name = "git.renamelimit"
297 297 default = 400
298 298
299 299 [[items]]
300 300 section = "convert"
301 301 name = "git.saverev"
302 302 default = true
303 303
304 304 [[items]]
305 305 section = "convert"
306 306 name = "git.similarity"
307 307 default = 50
308 308
309 309 [[items]]
310 310 section = "convert"
311 311 name = "git.skipsubmodules"
312 312 default = false
313 313
314 314 [[items]]
315 315 section = "convert"
316 316 name = "hg.clonebranches"
317 317 default = false
318 318
319 319 [[items]]
320 320 section = "convert"
321 321 name = "hg.ignoreerrors"
322 322 default = false
323 323
324 324 [[items]]
325 325 section = "convert"
326 326 name = "hg.preserve-hash"
327 327 default = false
328 328
329 329 [[items]]
330 330 section = "convert"
331 331 name = "hg.revs"
332 332
333 333 [[items]]
334 334 section = "convert"
335 335 name = "hg.saverev"
336 336 default = false
337 337
338 338 [[items]]
339 339 section = "convert"
340 340 name = "hg.sourcename"
341 341
342 342 [[items]]
343 343 section = "convert"
344 344 name = "hg.startrev"
345 345
346 346 [[items]]
347 347 section = "convert"
348 348 name = "hg.tagsbranch"
349 349 default = "default"
350 350
351 351 [[items]]
352 352 section = "convert"
353 353 name = "hg.usebranchnames"
354 354 default = true
355 355
356 356 [[items]]
357 357 section = "convert"
358 358 name = "ignoreancestorcheck"
359 359 default = false
360 360 experimental = true
361 361
362 362 [[items]]
363 363 section = "convert"
364 364 name = "localtimezone"
365 365 default = false
366 366
367 367 [[items]]
368 368 section = "convert"
369 369 name = "p4.encoding"
370 370 default-type = "dynamic"
371 371
372 372 [[items]]
373 373 section = "convert"
374 374 name = "p4.startrev"
375 375 default = 0
376 376
377 377 [[items]]
378 378 section = "convert"
379 379 name = "skiptags"
380 380 default = false
381 381
382 382 [[items]]
383 383 section = "convert"
384 384 name = "svn.branches"
385 385
386 386 [[items]]
387 387 section = "convert"
388 388 name = "svn.dangerous-set-commit-dates"
389 389 default = false
390 390
391 391 [[items]]
392 392 section = "convert"
393 393 name = "svn.debugsvnlog"
394 394 default = true
395 395
396 396 [[items]]
397 397 section = "convert"
398 398 name = "svn.startrev"
399 399 default = 0
400 400
401 401 [[items]]
402 402 section = "convert"
403 403 name = "svn.tags"
404 404
405 405 [[items]]
406 406 section = "convert"
407 407 name = "svn.trunk"
408 408
409 409 [[items]]
410 410 section = "debug"
411 411 name = "bundling-stats"
412 412 default = false
413 413 documentation = "Display extra information about the bundling process."
414 414
415 415 [[items]]
416 416 section = "debug"
417 417 name = "dirstate.delaywrite"
418 418 default = 0
419 419
420 420 [[items]]
421 421 section = "debug"
422 422 name = "revlog.debug-delta"
423 423 default = false
424 424
425 425 [[items]]
426 426 section = "debug"
427 427 name = "revlog.verifyposition.changelog"
428 428 default = ""
429 429
430 430 [[items]]
431 431 section = "debug"
432 432 name = "unbundling-stats"
433 433 default = false
434 434 documentation = "Display extra information about the unbundling process."
435 435
436 436 [[items]]
437 437 section = "defaults"
438 438 name = ".*"
439 439 generic = true
440 440
441 441 [[items]]
442 442 section = "devel"
443 443 name = "all-warnings"
444 444 default = false
445 445
446 446 [[items]]
447 447 section = "devel"
448 448 name = "bundle.delta"
449 449 default = ""
450 450
451 451 [[items]]
452 452 section = "devel"
453 453 name = "bundle2.debug"
454 454 default = false
455 455
456 456 [[items]]
457 457 section = "devel"
458 458 name = "cache-vfs"
459 459
460 460 [[items]]
461 461 section = "devel"
462 462 name = "check-locks"
463 463 default = false
464 464
465 465 [[items]]
466 466 section = "devel"
467 467 name = "check-relroot"
468 468 default = false
469 469
470 470 [[items]]
471 471 section = "devel"
472 472 name = "copy-tracing.multi-thread"
473 473 default = true
474 474
475 475 # Track copy information for all files, not just "added" ones (very slow)
476 476 [[items]]
477 477 section = "devel"
478 478 name = "copy-tracing.trace-all-files"
479 479 default = false
480 480
481 481 [[items]]
482 482 section = "devel"
483 483 name = "debug.abort-update"
484 484 default = false
485 485 documentation = """If true, then any merge with the working copy, \
486 486 e.g. [hg update], will be aborted after figuring out what needs to be done, \
487 487 but before spawning the parallel worker."""
488 488
489 489 [[items]]
490 490 section = "devel"
491 491 name = "debug.copies"
492 492 default = false
493 493
494 494 [[items]]
495 495 section = "devel"
496 496 name = "debug.extensions"
497 497 default = false
498 498
499 499 [[items]]
500 500 section = "devel"
501 501 name = "debug.peer-request"
502 502 default = false
503 503
504 504 [[items]]
505 505 section = "devel"
506 506 name = "debug.repo-filters"
507 507 default = false
508 508
509 509 [[items]]
510 510 section = "devel"
511 511 name = "default-date"
512 512
513 513 [[items]]
514 514 section = "devel"
515 515 name = "deprec-warn"
516 516 default = false
517 517
518 518 # possible values:
519 519 # - auto (the default)
520 520 # - force-append
521 521 # - force-new
522 522 [[items]]
523 523 section = "devel"
524 524 name = "dirstate.v2.data_update_mode"
525 525 default = "auto"
526 526
527 527 [[items]]
528 528 section = "devel"
529 529 name = "disableloaddefaultcerts"
530 530 default = false
531 531
532 532 [[items]]
533 533 section = "devel"
534 534 name = "discovery.exchange-heads"
535 535 default = true
536 536 documentation = """If false, the discovery will not start with remote \
537 537 head fetching and local head querying."""
538 538
539 539 [[items]]
540 540 section = "devel"
541 541 name = "discovery.grow-sample"
542 542 default = true
543 543 documentation = """If false, the sample size used in set discovery \
544 544 will not be increased through the process."""
545 545
546 546 [[items]]
547 547 section = "devel"
548 548 name = "discovery.grow-sample.dynamic"
549 549 default = true
550 550 documentation = """If true, the default, the sample size is adapted to the shape \
551 551 of the undecided set. It is set to the max of:
552 552 `<target-size>, len(roots(undecided)), len(heads(undecided))`"""
553 553
554 554 [[items]]
555 555 section = "devel"
556 556 name = "discovery.grow-sample.rate"
557 557 default = 1.05
558 558 documentation = "Controls the rate at which the sample grows."
559 559
560 560 [[items]]
561 561 section = "devel"
562 562 name = "discovery.randomize"
563 563 default = true
564 564 documentation = """If false, random samplings during discovery are deterministic. \
565 565 It is meant for integration tests."""
566 566
567 567 [[items]]
568 568 section = "devel"
569 569 name = "discovery.sample-size"
570 570 default = 200
571 571 documentation = "Controls the initial size of the discovery sample."
572 572
573 573 [[items]]
574 574 section = "devel"
575 575 name = "discovery.sample-size.initial"
576 576 default = 100
577 577 documentation = "Controls the initial size of the discovery for initial change."
578 578
579 579 [[items]]
580 580 section = "devel"
581 581 name = "legacy.exchange"
582 582 default-type = "list_type"
583 583
584 584 [[items]]
585 585 section = "devel"
586 name = "lock-wait-sync-file"
587 default = ""
588
589 [[items]]
590 section = "devel"
586 591 name = "persistent-nodemap"
587 592 default = false
588 593 documentation = """When true, revlogs use a special reference version of the \
589 594 nodemap, that is not performant but is "known" to behave properly."""
590 595
591 596 [[items]]
592 597 section = "devel"
593 598 name = "server-insecure-exact-protocol"
594 599 default = ""
595 600
596 601 [[items]]
597 602 section = "devel"
598 603 name = "servercafile"
599 604 default = ""
600 605
601 606 [[items]]
602 607 section = "devel"
603 608 name = "serverexactprotocol"
604 609 default = ""
605 610
606 611 [[items]]
607 612 section = "devel"
608 613 name = "serverrequirecert"
609 614 default = false
610 615
611 616 [[items]]
612 617 section = "devel"
613 618 name = "strip-obsmarkers"
614 619 default = true
615 620
616 621 [[items]]
617 622 section = 'devel'
618 623 name = 'sync.status.pre-dirstate-write-file'
619 624 documentation = """
620 625 Makes the status algorithm wait for the existence of this file \
621 626 (or until a timeout of `devel.sync.status.pre-dirstate-write-file-timeout` \
622 627 seconds) before taking the lock and writing the dirstate. \
623 628 Status signals that it's ready to wait by creating a file \
624 629 with the same name + `.waiting`. \
625 630 Useful when testing race conditions."""
626 631
627 632 [[items]]
628 633 section = 'devel'
629 634 name = 'sync.status.pre-dirstate-write-file-timeout'
630 635 default=2
631 636
632 637 [[items]]
633 638 section = 'devel'
634 639 name = 'sync.dirstate.post-docket-read-file'
635 640
636 641 [[items]]
637 642 section = 'devel'
638 643 name = 'sync.dirstate.post-docket-read-file-timeout'
639 644 default=2
640 645
641 646 [[items]]
642 647 section = 'devel'
643 648 name = 'sync.dirstate.pre-read-file'
644 649
645 650 [[items]]
646 651 section = 'devel'
647 652 name = 'sync.dirstate.pre-read-file-timeout'
648 653 default=2
649 654
650 655 [[items]]
651 656 section = "devel"
652 657 name = "user.obsmarker"
653 658
654 659 [[items]]
655 660 section = "devel"
656 661 name = "warn-config"
657 662
658 663 [[items]]
659 664 section = "devel"
660 665 name = "warn-config-default"
661 666
662 667 [[items]]
663 668 section = "devel"
664 669 name = "warn-config-unknown"
665 670
666 671 [[items]]
667 672 section = "devel"
668 673 name = "warn-empty-changegroup"
669 674 default = false
670 675
671 676 [[items]]
672 677 section = "diff"
673 678 name = "merge"
674 679 default = false
675 680 experimental = true
676 681
677 682 [[items]]
678 683 section = "email"
679 684 name = "bcc"
680 685
681 686 [[items]]
682 687 section = "email"
683 688 name = "cc"
684 689
685 690 [[items]]
686 691 section = "email"
687 692 name = "charsets"
688 693 default-type = "list_type"
689 694
690 695 [[items]]
691 696 section = "email"
692 697 name = "from"
693 698
694 699 [[items]]
695 700 section = "email"
696 701 name = "method"
697 702 default = "smtp"
698 703
699 704 [[items]]
700 705 section = "email"
701 706 name = "reply-to"
702 707
703 708 [[items]]
704 709 section = "email"
705 710 name = "to"
706 711
707 712 [[items]]
708 713 section = "experimental"
709 714 name = "archivemetatemplate"
710 715 default-type = "dynamic"
711 716
712 717 [[items]]
713 718 section = "experimental"
714 719 name = "auto-publish"
715 720 default = "publish"
716 721
717 722 [[items]]
718 723 section = "experimental"
719 724 name = "bundle-phases"
720 725 default = false
721 726
722 727 [[items]]
723 728 section = "experimental"
724 729 name = "bundle2-advertise"
725 730 default = true
726 731
727 732 [[items]]
728 733 section = "experimental"
729 734 name = "bundle2-output-capture"
730 735 default = false
731 736
732 737 [[items]]
733 738 section = "experimental"
734 739 name = "bundle2.pushback"
735 740 default = false
736 741
737 742 [[items]]
738 743 section = "experimental"
739 744 name = "bundle2lazylocking"
740 745 default = false
741 746
742 747 [[items]]
743 748 section = "experimental"
744 749 name = "bundlecomplevel"
745 750
746 751 [[items]]
747 752 section = "experimental"
748 753 name = "bundlecomplevel.bzip2"
749 754
750 755 [[items]]
751 756 section = "experimental"
752 757 name = "bundlecomplevel.gzip"
753 758
754 759 [[items]]
755 760 section = "experimental"
756 761 name = "bundlecomplevel.none"
757 762
758 763 [[items]]
759 764 section = "experimental"
760 765 name = "bundlecomplevel.zstd"
761 766
762 767 [[items]]
763 768 section = "experimental"
764 769 name = "bundlecompthreads"
765 770
766 771 [[items]]
767 772 section = "experimental"
768 773 name = "bundlecompthreads.bzip2"
769 774
770 775 [[items]]
771 776 section = "experimental"
772 777 name = "bundlecompthreads.gzip"
773 778
774 779 [[items]]
775 780 section = "experimental"
776 781 name = "bundlecompthreads.none"
777 782
778 783 [[items]]
779 784 section = "experimental"
780 785 name = "bundlecompthreads.zstd"
781 786
782 787 [[items]]
783 788 section = "experimental"
784 789 name = "changegroup3"
785 790 default = true
786 791
787 792 [[items]]
788 793 section = "experimental"
789 794 name = "changegroup4"
790 795 default = false
791 796
792 797 # might remove rank configuration once the computation has no impact
793 798 [[items]]
794 799 section = "experimental"
795 800 name = "changelog-v2.compute-rank"
796 801 default = true
797 802
798 803 [[items]]
799 804 section = "experimental"
800 805 name = "cleanup-as-archived"
801 806 default = false
802 807
803 808 [[items]]
804 809 section = "experimental"
805 810 name = "clientcompressionengines"
806 811 default-type = "list_type"
807 812
808 813 [[items]]
809 814 section = "experimental"
810 815 name = "copies.read-from"
811 816 default = "filelog-only"
812 817
813 818 [[items]]
814 819 section = "experimental"
815 820 name = "copies.write-to"
816 821 default = "filelog-only"
817 822
818 823 [[items]]
819 824 section = "experimental"
820 825 name = "copytrace"
821 826 default = "on"
822 827
823 828 [[items]]
824 829 section = "experimental"
825 830 name = "copytrace.movecandidateslimit"
826 831 default = 100
827 832
828 833 [[items]]
829 834 section = "experimental"
830 835 name = "copytrace.sourcecommitlimit"
831 836 default = 100
832 837
833 838 [[items]]
834 839 section = "experimental"
835 840 name = "crecordtest"
836 841
837 842 [[items]]
838 843 section = "experimental"
839 844 name = "directaccess"
840 845 default = false
841 846
842 847 [[items]]
843 848 section = "experimental"
844 849 name = "directaccess.revnums"
845 850 default = false
846 851
847 852 [[items]]
848 853 section = "experimental"
849 854 name = "editortmpinhg"
850 855 default = false
851 856
852 857 [[items]]
853 858 section = "experimental"
854 859 name = "evolution"
855 860 default-type = "list_type"
856 861
857 862 [[items]]
858 863 section = "experimental"
859 864 name = "evolution.allowdivergence"
860 865 default = false
861 866 alias = [["experimental", "allowdivergence"]]
862 867
863 868 [[items]]
864 869 section = "experimental"
865 870 name = "evolution.allowunstable"
866 871
867 872 [[items]]
868 873 section = "experimental"
869 874 name = "evolution.bundle-obsmarker"
870 875 default = false
871 876
872 877 [[items]]
873 878 section = "experimental"
874 879 name = "evolution.bundle-obsmarker:mandatory"
875 880 default = true
876 881
877 882 [[items]]
878 883 section = "experimental"
879 884 name = "evolution.createmarkers"
880 885
881 886 [[items]]
882 887 section = "experimental"
883 888 name = "evolution.effect-flags"
884 889 default = true
885 890 alias = [["experimental", "effect-flags"]]
886 891
887 892 [[items]]
888 893 section = "experimental"
889 894 name = "evolution.exchange"
890 895
891 896 [[items]]
892 897 section = "experimental"
893 898 name = "evolution.report-instabilities"
894 899 default = true
895 900
896 901 [[items]]
897 902 section = "experimental"
898 903 name = "evolution.track-operation"
899 904 default = true
900 905
901 906 [[items]]
902 907 section = "experimental"
903 908 name = "exportableenviron"
904 909 default-type = "list_type"
905 910
906 911 [[items]]
907 912 section = "experimental"
908 913 name = "extendedheader.index"
909 914
910 915 [[items]]
911 916 section = "experimental"
912 917 name = "extendedheader.similarity"
913 918 default = false
914 919
915 920 [[items]]
916 921 section = "experimental"
917 922 name = "extra-filter-revs"
918 923 documentation = """Repo-level config to prevent a revset from being visible.
919 924 The target use case is to use `share` to expose different subsets of the same \
920 925 repository, especially server side. See also `server.view`."""
921 926
922 927 [[items]]
923 928 section = "experimental"
924 929 name = "graphshorten"
925 930 default = false
926 931
927 932 [[items]]
928 933 section = "experimental"
929 934 name = "graphstyle.grandparent"
930 935 default-type = "dynamic"
931 936
932 937 [[items]]
933 938 section = "experimental"
934 939 name = "graphstyle.missing"
935 940 default-type = "dynamic"
936 941
937 942 [[items]]
938 943 section = "experimental"
939 944 name = "graphstyle.parent"
940 945 default-type = "dynamic"
941 946
942 947 [[items]]
943 948 section = "experimental"
944 949 name = "hook-track-tags"
945 950 default = false
946 951
947 952 [[items]]
948 953 section = "experimental"
949 954 name = "httppostargs"
950 955 default = false
951 956
952 957 [[items]]
953 958 section = "experimental"
954 959 name = "log.topo"
955 960 default = false
956 961
957 962 [[items]]
958 963 section = "experimental"
959 964 name = "maxdeltachainspan"
960 965 default = -1
961 966
962 967 [[items]]
963 968 section = "experimental"
964 969 name = "merge-track-salvaged"
965 970 default = false
966 971 documentation = """Tracks files which were undeleted (merge might delete them \
967 972 but we explicitly kept/undeleted them) and creates new filenodes for them."""
968 973
969 974 [[items]]
970 975 section = "experimental"
971 976 name = "merge.checkpathconflicts"
972 977 default = false
973 978
974 979 [[items]]
975 980 section = "experimental"
976 981 name = "mmapindexthreshold"
977 982
978 983 [[items]]
979 984 section = "experimental"
980 985 name = "narrow"
981 986 default = false
982 987
983 988 [[items]]
984 989 section = "experimental"
985 990 name = "nointerrupt"
986 991 default = false
987 992
988 993 [[items]]
989 994 section = "experimental"
990 995 name = "nointerrupt-interactiveonly"
991 996 default = true
992 997
993 998 [[items]]
994 999 section = "experimental"
995 1000 name = "nonnormalparanoidcheck"
996 1001 default = false
997 1002
998 1003 [[items]]
999 1004 section = "experimental"
1000 1005 name = "obsmarkers-exchange-debug"
1001 1006 default = false
1002 1007
1003 1008 [[items]]
1004 1009 section = "experimental"
1005 1010 name = "rebaseskipobsolete"
1006 1011 default = true
1007 1012
1008 1013 [[items]]
1009 1014 section = "experimental"
1010 1015 name = "remotenames"
1011 1016 default = false
1012 1017
1013 1018 [[items]]
1014 1019 section = "experimental"
1015 1020 name = "removeemptydirs"
1016 1021 default = true
1017 1022
1018 1023 [[items]]
1019 1024 section = "experimental"
1020 1025 name = "revert.interactive.select-to-keep"
1021 1026 default = false
1022 1027
1023 1028 [[items]]
1024 1029 section = "experimental"
1025 1030 name = "revisions.disambiguatewithin"
1026 1031
1027 1032 [[items]]
1028 1033 section = "experimental"
1029 1034 name = "revisions.prefixhexnode"
1030 1035 default = false
1031 1036
1032 1037 # "out of experimental" todo list.
1033 1038 #
1034 1039 # * include management of a persistent nodemap in the main docket
1035 1040 # * enforce a "no-truncate" policy for mmap safety
1036 1041 # - for censoring operation
1037 1042 # - for stripping operation
1038 1043 # - for rollback operation
1039 1044 # * proper streaming (race free) of the docket file
1040 1045 # * track garbage data to evemtually allow rewriting -existing- sidedata.
1041 1046 # * Exchange-wise, we will also need to do something more efficient than
1042 1047 # keeping references to the affected revlogs, especially memory-wise when
1043 1048 # rewriting sidedata.
1044 1049 # * introduce a proper solution to reduce the number of filelog related files.
1045 1050 # * use caching for reading sidedata (similar to what we do for data).
1046 1051 # * no longer set offset=0 if sidedata_size=0 (simplify cutoff computation).
1047 1052 # * Improvement to consider
1048 1053 # - avoid compression header in chunk using the default compression?
1049 1054 # - forbid "inline" compression mode entirely?
1050 1055 # - split the data offset and flag field (the 2 bytes save are mostly trouble)
1051 1056 # - keep track of uncompressed -chunk- size (to preallocate memory better)
1052 1057 # - keep track of chain base or size (probably not that useful anymore)
1053 1058 [[items]]
1054 1059 section = "experimental"
1055 1060 name = "revlogv2"
1056 1061
1057 1062 [[items]]
1058 1063 section = "experimental"
1059 1064 name = "rust.index"
1060 1065 default = false
1061 1066
1062 1067 [[items]]
1063 1068 section = "experimental"
1064 1069 name = "server.allow-hidden-access"
1065 1070 default-type = "list_type"
1066 1071
1067 1072 [[items]]
1068 1073 section = "experimental"
1069 1074 name = "server.filesdata.recommended-batch-size"
1070 1075 default = 50000
1071 1076
1072 1077 [[items]]
1073 1078 section = "experimental"
1074 1079 name = "server.manifestdata.recommended-batch-size"
1075 1080 default = 100000
1076 1081
1077 1082 [[items]]
1078 1083 section = "experimental"
1079 1084 name = "server.stream-narrow-clones"
1080 1085 default = false
1081 1086
1082 1087 [[items]]
1083 1088 section = "experimental"
1084 1089 name = "single-head-per-branch"
1085 1090 default = false
1086 1091
1087 1092 [[items]]
1088 1093 section = "experimental"
1089 1094 name = "single-head-per-branch:account-closed-heads"
1090 1095 default = false
1091 1096
1092 1097 [[items]]
1093 1098 section = "experimental"
1094 1099 name = "single-head-per-branch:public-changes-only"
1095 1100 default = false
1096 1101
1097 1102 [[items]]
1098 1103 section = "experimental"
1099 1104 name = "sparse-read"
1100 1105 default = false
1101 1106
1102 1107 [[items]]
1103 1108 section = "experimental"
1104 1109 name = "sparse-read.density-threshold"
1105 1110 default = 0.5
1106 1111
1107 1112 [[items]]
1108 1113 section = "experimental"
1109 1114 name = "sparse-read.min-gap-size"
1110 1115 default = "65K"
1111 1116
1112 1117 [[items]]
1113 1118 section = "experimental"
1114 1119 name = "stream-v3"
1115 1120 default = false
1116 1121
1117 1122 [[items]]
1118 1123 section = "experimental"
1119 1124 name = "treemanifest"
1120 1125 default = false
1121 1126
1122 1127 [[items]]
1123 1128 section = "experimental"
1124 1129 name = "update.atomic-file"
1125 1130 default = false
1126 1131
1127 1132 [[items]]
1128 1133 section = "experimental"
1129 1134 name = "web.full-garbage-collection-rate"
1130 1135 default = 1 # still forcing a full collection on each request
1131 1136
1132 1137 [[items]]
1133 1138 section = "experimental"
1134 1139 name = "worker.repository-upgrade"
1135 1140 default = false
1136 1141
1137 1142 [[items]]
1138 1143 section = "experimental"
1139 1144 name = "worker.wdir-get-thread-safe"
1140 1145 default = false
1141 1146
1142 1147 [[items]]
1143 1148 section = "experimental"
1144 1149 name = "xdiff"
1145 1150 default = false
1146 1151
1147 1152 [[items]]
1148 1153 section = "extdata"
1149 1154 name = ".*"
1150 1155 generic = true
1151 1156
1152 1157 [[items]]
1153 1158 section = "extensions"
1154 1159 name = "[^:]*"
1155 1160 generic = true
1156 1161
1157 1162 [[items]]
1158 1163 section = "extensions"
1159 1164 name = "[^:]*:required"
1160 1165 default = false
1161 1166 generic = true
1162 1167
1168
1169 # The format section is dedicated to control of the repository on disk format
1170 # and constraints.
1171 #
1172 # A format change affects which data is expected to be stored in the repository
1173 # and how. It impacts other client whichever their version are, format change
1174 # often comes with an associated entry in the requirements.
1175 #
1176 # The option are usually in the form `use-xxx-yyy` (with xxx-yy the feature name).
1177 #
1178 # To configure details of how the repository is accessed, without affect the
1179 # repository formats, see the `storage section`.
1180
1163 1181 [[items]]
1164 1182 section = "format"
1165 1183 name = "bookmarks-in-store"
1166 1184 default = false
1167 1185
1168 1186 [[items]]
1169 1187 section = "format"
1170 1188 name = "chunkcachesize"
1171 1189 experimental = true
1172 1190
1173 1191 [[items]]
1174 1192 section = "format"
1175 1193 name = "dotencode"
1176 1194 default = true
1177 1195
1178 1196 # The interaction between the archived phase and obsolescence markers needs to
1179 1197 # be sorted out before wider usage of this are to be considered.
1180 1198 #
1181 1199 # At the time this message is written, behavior when archiving obsolete
1182 1200 # changeset differ significantly from stripping. As part of stripping, we also
1183 1201 # remove the obsolescence marker associated to the stripped changesets,
1184 1202 # revealing the precedecessors changesets when applicable. When archiving, we
1185 1203 # don't touch the obsolescence markers, keeping everything hidden. This can
1186 1204 # result in quite confusing situation for people combining exchanging draft
1187 1205 # with the archived phases. As some markers needed by others may be skipped
1188 1206 # during exchange.
1189 1207 [[items]]
1190 1208 section = "format"
1191 1209 name = "exp-archived-phase"
1192 1210 default = false
1193 1211 experimental = true
1194 1212
1195 1213 # Experimental TODOs:
1196 1214 #
1197 1215 # * Same as for revlogv2 (but for the reduction of the number of files)
1198 1216 # * Actually computing the rank of changesets
1199 1217 # * Improvement to investigate
1200 1218 # - storing .hgtags fnode
1201 1219 # - storing branch related identifier
1202 1220 [[items]]
1203 1221 section = "format"
1204 1222 name = "exp-use-changelog-v2"
1205 1223 experimental = true
1206 1224
1207 1225 [[items]]
1208 1226 section = "format"
1209 1227 name = "exp-use-copies-side-data-changeset"
1210 1228 default = false
1211 1229 experimental = true
1212 1230
1213 1231 [[items]]
1214 1232 section = "format"
1215 1233 name = "generaldelta"
1216 1234 default = false
1217 1235 experimental = true
1218 1236
1219 1237 [[items]]
1220 1238 section = "format"
1221 1239 name = "manifestcachesize"
1222 1240 experimental = true
1223 1241
1224 1242 [[items]]
1225 1243 section = "format"
1226 1244 name = "maxchainlen"
1227 1245 default-type = "dynamic"
1228 1246 experimental = true
1229 1247
1230 1248 [[items]]
1231 1249 section = "format"
1232 1250 name = "obsstore-version"
1233 1251
1234 1252 [[items]]
1235 1253 section = "format"
1236 1254 name = "revlog-compression"
1237 1255 default-type = "lambda"
1238 1256 alias = [["experimental", "format.compression"]]
1239 1257 default = [ "zstd", "zlib",]
1240 1258
1241 1259 [[items]]
1242 1260 section = "format"
1243 1261 name = "sparse-revlog"
1244 1262 default = true
1245 1263
1246 1264 [[items]]
1247 1265 section = "format"
1248 1266 name = "use-dirstate-tracked-hint"
1249 1267 default = false
1250 1268 experimental = true
1251 1269
1252 1270 [[items]]
1253 1271 section = "format"
1254 1272 name = "use-dirstate-tracked-hint.automatic-upgrade-of-mismatching-repositories"
1255 1273 default = false
1256 1274 experimental = true
1257 1275
1258 1276 [[items]]
1259 1277 section = "format"
1260 1278 name = "use-dirstate-tracked-hint.automatic-upgrade-of-mismatching-repositories:quiet"
1261 1279 default = false
1262 1280 experimental = true
1263 1281
1264 1282 [[items]]
1265 1283 section = "format"
1266 1284 name = "use-dirstate-tracked-hint.version"
1267 1285 default = 1
1268 1286 experimental = true
1269 1287
1270 1288 [[items]]
1271 1289 section = "format"
1272 1290 name = "use-dirstate-v2"
1273 1291 default = false
1274 1292 alias = [["format", "exp-rc-dirstate-v2"]]
1275 1293 experimental = true
1276 1294 documentation = """Enables dirstate-v2 format *when creating a new repository*.
1277 1295 Which format to use for existing repos is controlled by `.hg/requires`."""
1278 1296
1279 1297 [[items]]
1280 1298 section = "format"
1281 1299 name = "use-dirstate-v2.automatic-upgrade-of-mismatching-repositories"
1282 1300 default = false
1283 1301 experimental = true
1284 1302
1285 1303 [[items]]
1286 1304 section = "format"
1287 1305 name = "use-dirstate-v2.automatic-upgrade-of-mismatching-repositories:quiet"
1288 1306 default = false
1289 1307 experimental = true
1290 1308
1291 1309 # Having this on by default means we are confident about the scaling of phases.
1292 1310 # This is not garanteed to be the case at the time this message is written.
1293 1311 [[items]]
1294 1312 section = "format"
1295 1313 name = "use-internal-phase"
1296 1314 default = false
1297 1315 experimental = true
1298 1316
1299 1317 [[items]]
1300 1318 section = "format"
1301 1319 name = "use-persistent-nodemap"
1302 1320 default-type = "dynamic"
1303 1321
1304 1322 [[items]]
1305 1323 section = "format"
1306 1324 name = "use-share-safe"
1307 1325 default = true
1308 1326
1309 1327 [[items]]
1310 1328 section = "format"
1311 1329 name = "use-share-safe.automatic-upgrade-of-mismatching-repositories"
1312 1330 default = false
1313 1331 experimental = true
1314 1332
1315 1333 [[items]]
1316 1334 section = "format"
1317 1335 name = "use-share-safe.automatic-upgrade-of-mismatching-repositories:quiet"
1318 1336 default = false
1319 1337 experimental = true
1320 1338
1321 1339 [[items]]
1322 1340 section = "format"
1323 1341 name = "usefncache"
1324 1342 default = true
1325 1343
1326 1344 [[items]]
1327 1345 section = "format"
1328 1346 name = "usegeneraldelta"
1329 1347 default = true
1330 1348
1331 1349 [[items]]
1332 1350 section = "format"
1333 1351 name = "usestore"
1334 1352 default = true
1335 1353
1336 1354 [[items]]
1337 1355 section = "fsmonitor"
1338 1356 name = "warn_update_file_count"
1339 1357 default = 50000
1340 1358
1341 1359 [[items]]
1342 1360 section = "fsmonitor"
1343 1361 name = "warn_update_file_count_rust"
1344 1362 default = 400000
1345 1363
1346 1364 [[items]]
1347 1365 section = "fsmonitor"
1348 1366 name = "warn_when_unused"
1349 1367 default = true
1350 1368
1351 1369 [[items]]
1352 1370 section = "help"
1353 1371 name = 'hidden-command\..*'
1354 1372 default = false
1355 1373 generic = true
1356 1374
1357 1375 [[items]]
1358 1376 section = "help"
1359 1377 name = 'hidden-topic\..*'
1360 1378 default = false
1361 1379 generic = true
1362 1380
1363 1381 [[items]]
1364 1382 section = "hgweb-paths"
1365 1383 name = ".*"
1366 1384 default-type = "list_type"
1367 1385 generic = true
1368 1386
1369 1387 [[items]]
1370 1388 section = "hooks"
1371 1389 name = ".*:run-with-plain"
1372 1390 default = true
1373 1391 generic = true
1374 1392
1375 1393 [[items]]
1376 1394 section = "hooks"
1377 1395 name = "[^:]*"
1378 1396 default-type = "dynamic"
1379 1397 generic = true
1380 1398
1381 1399 [[items]]
1382 1400 section = "hostfingerprints"
1383 1401 name = ".*"
1384 1402 default-type = "list_type"
1385 1403 generic = true
1386 1404
1387 1405 [[items]]
1388 1406 section = "hostsecurity"
1389 1407 name = ".*:ciphers$"
1390 1408 default-type = "dynamic"
1391 1409 generic = true
1392 1410
1393 1411 [[items]]
1394 1412 section = "hostsecurity"
1395 1413 name = ".*:fingerprints$"
1396 1414 default-type = "list_type"
1397 1415 generic = true
1398 1416
1399 1417 [[items]]
1400 1418 section = "hostsecurity"
1401 1419 name = ".*:minimumprotocol$"
1402 1420 default-type = "dynamic"
1403 1421 generic = true
1404 1422
1405 1423 [[items]]
1406 1424 section = "hostsecurity"
1407 1425 name = ".*:verifycertsfile$"
1408 1426 generic = true
1409 1427
1410 1428 [[items]]
1411 1429 section = "hostsecurity"
1412 1430 name = "ciphers"
1413 1431
1414 1432 [[items]]
1415 1433 section = "hostsecurity"
1416 1434 name = "minimumprotocol"
1417 1435 default-type = "dynamic"
1418 1436
1419 1437 [[items]]
1420 1438 section = "http"
1421 1439 name = "timeout"
1422 1440
1423 1441 [[items]]
1424 1442 section = "http_proxy"
1425 1443 name = "always"
1426 1444 default = false
1427 1445
1428 1446 [[items]]
1429 1447 section = "http_proxy"
1430 1448 name = "host"
1431 1449
1432 1450 [[items]]
1433 1451 section = "http_proxy"
1434 1452 name = "no"
1435 1453 default-type = "list_type"
1436 1454
1437 1455 [[items]]
1438 1456 section = "http_proxy"
1439 1457 name = "passwd"
1440 1458
1441 1459 [[items]]
1442 1460 section = "http_proxy"
1443 1461 name = "user"
1444 1462
1445 1463 [[items]]
1446 1464 section = "logtoprocess"
1447 1465 name = "command"
1448 1466
1449 1467 [[items]]
1450 1468 section = "logtoprocess"
1451 1469 name = "commandexception"
1452 1470
1453 1471 [[items]]
1454 1472 section = "logtoprocess"
1455 1473 name = "commandfinish"
1456 1474
1457 1475 [[items]]
1458 1476 section = "logtoprocess"
1459 1477 name = "develwarn"
1460 1478
1461 1479 [[items]]
1462 1480 section = "logtoprocess"
1463 1481 name = "uiblocked"
1464 1482
1465 1483 [[items]]
1466 1484 section = "merge"
1467 1485 name = "checkignored"
1468 1486 default = "abort"
1469 1487
1470 1488 [[items]]
1471 1489 section = "merge"
1472 1490 name = "checkunknown"
1473 1491 default = "abort"
1474 1492
1475 1493 [[items]]
1476 1494 section = "merge"
1477 1495 name = "disable-partial-tools"
1478 1496 default = false
1479 1497 experimental = true
1480 1498
1481 1499 [[items]]
1482 1500 section = "merge"
1483 1501 name = "followcopies"
1484 1502 default = true
1485 1503
1486 1504 [[items]]
1487 1505 section = "merge"
1488 1506 name = "on-failure"
1489 1507 default = "continue"
1490 1508
1491 1509 [[items]]
1492 1510 section = "merge"
1493 1511 name = "preferancestor"
1494 1512 default-type = "lambda"
1495 1513 default = ["*"]
1496 1514 experimental = true
1497 1515
1498 1516 [[items]]
1499 1517 section = "merge"
1500 1518 name = "strict-capability-check"
1501 1519 default = false
1502 1520
1503 1521 [[items]]
1504 1522 section = "merge-tools"
1505 1523 name = ".*"
1506 1524 generic = true
1507 1525
1508 1526 [[items]]
1509 1527 section = "merge-tools"
1510 1528 name = '.*\.args$'
1511 1529 default = "$local $base $other"
1512 1530 generic = true
1513 1531 priority = -1
1514 1532
1515 1533 [[items]]
1516 1534 section = "merge-tools"
1517 1535 name = '.*\.binary$'
1518 1536 default = false
1519 1537 generic = true
1520 1538 priority = -1
1521 1539
1522 1540 [[items]]
1523 1541 section = "merge-tools"
1524 1542 name = '.*\.check$'
1525 1543 default-type = "list_type"
1526 1544 generic = true
1527 1545 priority = -1
1528 1546
1529 1547 [[items]]
1530 1548 section = "merge-tools"
1531 1549 name = '.*\.checkchanged$'
1532 1550 default = false
1533 1551 generic = true
1534 1552 priority = -1
1535 1553
1536 1554 [[items]]
1537 1555 section = "merge-tools"
1538 1556 name = '.*\.executable$'
1539 1557 default-type = "dynamic"
1540 1558 generic = true
1541 1559 priority = -1
1542 1560
1543 1561 [[items]]
1544 1562 section = "merge-tools"
1545 1563 name = '.*\.fixeol$'
1546 1564 default = false
1547 1565 generic = true
1548 1566 priority = -1
1549 1567
1550 1568 [[items]]
1551 1569 section = "merge-tools"
1552 1570 name = '.*\.gui$'
1553 1571 default = false
1554 1572 generic = true
1555 1573 priority = -1
1556 1574
1557 1575 [[items]]
1558 1576 section = "merge-tools"
1559 1577 name = '.*\.mergemarkers$'
1560 1578 default = "basic"
1561 1579 generic = true
1562 1580 priority = -1
1563 1581
1564 1582 [[items]]
1565 1583 section = "merge-tools"
1566 1584 name = '.*\.mergemarkertemplate$' # take from command-templates.mergemarker
1567 1585 default-type = "dynamic"
1568 1586 generic = true
1569 1587 priority = -1
1570 1588
1571 1589 [[items]]
1572 1590 section = "merge-tools"
1573 1591 name = '.*\.premerge$'
1574 1592 default-type = "dynamic"
1575 1593 generic = true
1576 1594 priority = -1
1577 1595
1578 1596 [[items]]
1579 1597 section = "merge-tools"
1580 1598 name = '.*\.priority$'
1581 1599 default = 0
1582 1600 generic = true
1583 1601 priority = -1
1584 1602
1585 1603 [[items]]
1586 1604 section = "merge-tools"
1587 1605 name = '.*\.regappend$'
1588 1606 default = ""
1589 1607 generic = true
1590 1608 priority = -1
1591 1609
1592 1610 [[items]]
1593 1611 section = "merge-tools"
1594 1612 name = '.*\.symlink$'
1595 1613 default = false
1596 1614 generic = true
1597 1615 priority = -1
1598 1616
1599 1617 [[items]]
1600 1618 section = "pager"
1601 1619 name = "attend-.*"
1602 1620 default-type = "dynamic"
1603 1621 generic = true
1604 1622
1605 1623 [[items]]
1606 1624 section = "pager"
1607 1625 name = "ignore"
1608 1626 default-type = "list_type"
1609 1627
1610 1628 [[items]]
1611 1629 section = "pager"
1612 1630 name = "pager"
1613 1631 default-type = "dynamic"
1614 1632
1615 1633 [[items]]
1616 1634 section = "partial-merge-tools"
1617 1635 name = ".*"
1618 1636 generic = true
1619 1637 experimental = true
1620 1638
1621 1639 [[items]]
1622 1640 section = "partial-merge-tools"
1623 1641 name = '.*\.args'
1624 1642 default = "$local $base $other"
1625 1643 generic = true
1626 1644 priority = -1
1627 1645 experimental = true
1628 1646
1629 1647 [[items]]
1630 1648 section = "partial-merge-tools"
1631 1649 name = '.*\.disable'
1632 1650 default = false
1633 1651 generic = true
1634 1652 priority = -1
1635 1653 experimental = true
1636 1654
1637 1655 [[items]]
1638 1656 section = "partial-merge-tools"
1639 1657 name = '.*\.executable$'
1640 1658 default-type = "dynamic"
1641 1659 generic = true
1642 1660 priority = -1
1643 1661 experimental = true
1644 1662
1645 1663 [[items]]
1646 1664 section = "partial-merge-tools"
1647 1665 name = '.*\.order'
1648 1666 default = 0
1649 1667 generic = true
1650 1668 priority = -1
1651 1669 experimental = true
1652 1670
1653 1671 [[items]]
1654 1672 section = "partial-merge-tools"
1655 1673 name = '.*\.patterns'
1656 1674 default-type = "dynamic"
1657 1675 generic = true
1658 1676 priority = -1
1659 1677 experimental = true
1660 1678
1661 1679 [[items]]
1662 1680 section = "patch"
1663 1681 name = "eol"
1664 1682 default = "strict"
1665 1683
1666 1684 [[items]]
1667 1685 section = "patch"
1668 1686 name = "fuzz"
1669 1687 default = 2
1670 1688
1671 1689 [[items]]
1672 1690 section = "paths"
1673 1691 name = "[^:]*"
1674 1692 generic = true
1675 1693
1676 1694 [[items]]
1677 1695 section = "paths"
1678 1696 name = ".*:bookmarks.mode"
1679 1697 default = "default"
1680 1698 generic = true
1681 1699
1682 1700 [[items]]
1683 1701 section = "paths"
1684 1702 name = ".*:multi-urls"
1685 1703 default = false
1686 1704 generic = true
1687 1705
1688 1706 [[items]]
1689 1707 section = "paths"
1690 1708 name = ".*:pulled-delta-reuse-policy"
1691 1709 generic = true
1692 1710
1693 1711 [[items]]
1694 1712 section = "paths"
1695 1713 name = ".*:pushrev"
1696 1714 generic = true
1697 1715
1698 1716 [[items]]
1699 1717 section = "paths"
1700 1718 name = ".*:pushurl"
1701 1719 generic = true
1702 1720
1703 1721 [[items]]
1704 1722 section = "paths"
1705 1723 name = "default"
1706 1724
1707 1725 [[items]]
1708 1726 section = "paths"
1709 1727 name = "default-push"
1710 1728
1711 1729 [[items]]
1712 1730 section = "phases"
1713 1731 name = "checksubrepos"
1714 1732 default = "follow"
1715 1733
1716 1734 [[items]]
1717 1735 section = "phases"
1718 1736 name = "new-commit"
1719 1737 default = "draft"
1720 1738
1721 1739 [[items]]
1722 1740 section = "phases"
1723 1741 name = "publish"
1724 1742 default = true
1725 1743
1726 1744 [[items]]
1727 1745 section = "profiling"
1728 1746 name = "enabled"
1729 1747 default = false
1730 1748
1731 1749 [[items]]
1732 1750 section = "profiling"
1733 1751 name = "format"
1734 1752 default = "text"
1735 1753
1736 1754 [[items]]
1737 1755 section = "profiling"
1738 1756 name = "freq"
1739 1757 default = 1000
1740 1758
1741 1759 [[items]]
1742 1760 section = "profiling"
1743 1761 name = "limit"
1744 1762 default = 30
1745 1763
1746 1764 [[items]]
1747 1765 section = "profiling"
1748 1766 name = "nested"
1749 1767 default = 0
1750 1768
1751 1769 [[items]]
1752 1770 section = "profiling"
1753 1771 name = "output"
1754 1772
1755 1773 [[items]]
1756 1774 section = "profiling"
1757 1775 name = "showmax"
1758 1776 default = 0.999
1759 1777
1760 1778 [[items]]
1761 1779 section = "profiling"
1762 1780 name = "showmin"
1763 1781 default-type = "dynamic"
1764 1782
1765 1783 [[items]]
1766 1784 section = "profiling"
1767 1785 name = "showtime"
1768 1786 default = true
1769 1787
1770 1788 [[items]]
1771 1789 section = "profiling"
1772 1790 name = "sort"
1773 1791 default = "inlinetime"
1774 1792
1775 1793 [[items]]
1776 1794 section = "profiling"
1777 1795 name = "statformat"
1778 1796 default = "hotpath"
1779 1797
1780 1798 [[items]]
1781 1799 section = "profiling"
1782 1800 name = "time-track"
1783 1801 default-type = "dynamic"
1784 1802
1785 1803 [[items]]
1786 1804 section = "profiling"
1787 1805 name = "type"
1788 1806 default = "stat"
1789 1807
1790 1808 [[items]]
1791 1809 section = "progress"
1792 1810 name = "assume-tty"
1793 1811 default = false
1794 1812
1795 1813 [[items]]
1796 1814 section = "progress"
1797 1815 name = "changedelay"
1798 1816 default = 1
1799 1817
1800 1818 [[items]]
1801 1819 section = "progress"
1802 1820 name = "clear-complete"
1803 1821 default = true
1804 1822
1805 1823 [[items]]
1806 1824 section = "progress"
1807 1825 name = "debug"
1808 1826 default = false
1809 1827
1810 1828 [[items]]
1811 1829 section = "progress"
1812 1830 name = "delay"
1813 1831 default = 3
1814 1832
1815 1833 [[items]]
1816 1834 section = "progress"
1817 1835 name = "disable"
1818 1836 default = false
1819 1837
1820 1838 [[items]]
1821 1839 section = "progress"
1822 1840 name = "estimateinterval"
1823 1841 default = 60.0
1824 1842
1825 1843 [[items]]
1826 1844 section = "progress"
1827 1845 name = "format"
1828 1846 default-type = "lambda"
1829 1847 default = [ "topic", "bar", "number", "estimate",]
1830 1848
1831 1849 [[items]]
1832 1850 section = "progress"
1833 1851 name = "refresh"
1834 1852 default = 0.1
1835 1853
1836 1854 [[items]]
1837 1855 section = "progress"
1838 1856 name = "width"
1839 1857 default-type = "dynamic"
1840 1858
1841 1859 [[items]]
1842 1860 section = "pull"
1843 1861 name = "confirm"
1844 1862 default = false
1845 1863
1846 1864 [[items]]
1847 1865 section = "push"
1848 1866 name = "pushvars.server"
1849 1867 default = false
1850 1868
1851 1869 [[items]]
1852 1870 section = "rebase"
1853 1871 name = "experimental.inmemory"
1854 1872 default = false
1855 1873
1856 1874 [[items]]
1857 1875 section = "rebase"
1858 1876 name = "singletransaction"
1859 1877 default = false
1860 1878
1861 1879 [[items]]
1862 1880 section = "rebase"
1863 1881 name = "store-source"
1864 1882 default = true
1865 1883 experimental = true
1866 1884 documentation = """Controls creation of a `rebase_source` extra field during rebase.
1867 1885 When false, no such field is created. This is useful e.g. for incrementally \
1868 1886 converting changesets and then rebasing them onto an existing repo.
1869 1887 WARNING: this is an advanced setting reserved for people who know \
1870 1888 exactly what they are doing. Misuse of this setting can easily \
1871 1889 result in obsmarker cycles and a vivid headache."""
1872 1890
1873 1891 [[items]]
1874 1892 section = "rewrite"
1875 1893 name = "backup-bundle"
1876 1894 default = true
1877 1895 alias = [["ui", "history-editing-backup"]]
1878 1896
1879 1897 [[items]]
1880 1898 section = "rewrite"
1881 1899 name = "empty-successor"
1882 1900 default = "skip"
1883 1901 experimental = true
1884 1902
1885 1903 [[items]]
1886 1904 section = "rewrite"
1887 1905 name = "update-timestamp"
1888 1906 default = false
1889 1907
1890 1908 [[items]]
1891 1909 section = "rhg"
1892 1910 name = "cat"
1893 1911 default = true
1894 1912 experimental = true
1895 1913 documentation = """rhg cat has some quirks that need to be ironed out. \
1896 1914 In particular, the `-r` argument accepts a partial hash, but does not \
1897 1915 correctly resolve `abcdef` as a potential bookmark, tag or branch name."""
1898 1916
1899 1917 [[items]]
1900 1918 section = "rhg"
1901 1919 name = "fallback-exectutable"
1902 1920 experimental = true
1903 1921
1904 1922 [[items]]
1905 1923 section = "rhg"
1906 1924 name = "fallback-immediately"
1907 1925 default = false
1908 1926 experimental = true
1909 1927
1910 1928 [[items]]
1911 1929 section = "rhg"
1912 1930 name = "ignored-extensions"
1913 1931 default-type = "list_type"
1914 1932 experimental = true
1915 1933
1916 1934 [[items]]
1917 1935 section = "rhg"
1918 1936 name = "on-unsupported"
1919 1937 default = "abort"
1920 1938 experimental = true
1921 1939
1922 1940 [[items]]
1923 1941 section = "server"
1924 1942 name = "bookmarks-pushkey-compat"
1925 1943 default = true
1926 1944
1927 1945 [[items]]
1928 1946 section = "server"
1929 1947 name = "bundle1"
1930 1948 default = true
1931 1949
1932 1950 [[items]]
1933 1951 section = "server"
1934 1952 name = "bundle1.pull"
1935 1953
1936 1954 [[items]]
1937 1955 section = "server"
1938 1956 name = "bundle1.push"
1939 1957
1940 1958 [[items]]
1941 1959 section = "server"
1942 1960 name = "bundle1gd"
1943 1961
1944 1962 [[items]]
1945 1963 section = "server"
1946 1964 name = "bundle1gd.pull"
1947 1965
1948 1966 [[items]]
1949 1967 section = "server"
1950 1968 name = "bundle1gd.push"
1951 1969
1952 1970 [[items]]
1953 1971 section = "server"
1954 1972 name = "bundle2.stream"
1955 1973 default = true
1956 1974 alias = [["experimental", "bundle2.stream"]]
1957 1975
1958 1976 [[items]]
1959 1977 section = "server"
1960 1978 name = "compressionengines"
1961 1979 default-type = "list_type"
1962 1980
1963 1981 [[items]]
1964 1982 section = "server"
1965 1983 name = "concurrent-push-mode"
1966 1984 default = "check-related"
1967 1985
1968 1986 [[items]]
1969 1987 section = "server"
1970 1988 name = "disablefullbundle"
1971 1989 default = false
1972 1990
1973 1991 [[items]]
1974 1992 section = "server"
1975 1993 name = "maxhttpheaderlen"
1976 1994 default = 1024
1977 1995
1978 1996 [[items]]
1979 1997 section = "server"
1980 1998 name = "preferuncompressed"
1981 1999 default = false
1982 2000
1983 2001 [[items]]
1984 2002 section = "server"
1985 2003 name = "pullbundle"
1986 2004 default = true
1987 2005
1988 2006 [[items]]
1989 2007 section = "server"
1990 2008 name = "streamunbundle"
1991 2009 default = false
1992 2010
1993 2011 [[items]]
1994 2012 section = "server"
1995 2013 name = "uncompressed"
1996 2014 default = true
1997 2015
1998 2016 [[items]]
1999 2017 section = "server"
2000 2018 name = "uncompressedallowsecret"
2001 2019 default = false
2002 2020
2003 2021 [[items]]
2004 2022 section = "server"
2005 2023 name = "validate"
2006 2024 default = false
2007 2025
2008 2026 [[items]]
2009 2027 section = "server"
2010 2028 name = "view"
2011 2029 default = "served"
2012 2030
2013 2031 [[items]]
2014 2032 section = "server"
2015 2033 name = "zliblevel"
2016 2034 default = -1
2017 2035
2018 2036 [[items]]
2019 2037 section = "server"
2020 2038 name = "zstdlevel"
2021 2039 default = 3
2022 2040
2023 2041 [[items]]
2024 2042 section = "share"
2025 2043 name = "pool"
2026 2044
2027 2045 [[items]]
2028 2046 section = "share"
2029 2047 name = "poolnaming"
2030 2048 default = "identity"
2031 2049
2032 2050 [[items]]
2033 2051 section = "share"
2034 2052 name = "safe-mismatch.source-not-safe"
2035 2053 default = "abort"
2036 2054
2037 2055 [[items]]
2038 2056 section = "share"
2039 2057 name = "safe-mismatch.source-not-safe.warn"
2040 2058 default = true
2041 2059
2042 2060 [[items]]
2043 2061 section = "share"
2044 2062 name = "safe-mismatch.source-not-safe:verbose-upgrade"
2045 2063 default = true
2046 2064
2047 2065 [[items]]
2048 2066 section = "share"
2049 2067 name = "safe-mismatch.source-safe"
2050 2068 default = "abort"
2051 2069
2052 2070 [[items]]
2053 2071 section = "share"
2054 2072 name = "safe-mismatch.source-safe.warn"
2055 2073 default = true
2056 2074
2057 2075 [[items]]
2058 2076 section = "share"
2059 2077 name = "safe-mismatch.source-safe:verbose-upgrade"
2060 2078 default = true
2061 2079
2062 2080 [[items]]
2063 2081 section = "shelve"
2064 2082 name = "maxbackups"
2065 2083 default = 10
2066 2084
2067 2085 [[items]]
2068 2086 section = "shelve"
2069 2087 name = "store"
2070 2088 default = "internal"
2071 2089 experimental = true
2072 2090
2073 2091 [[items]]
2074 2092 section = "smtp"
2075 2093 name = "host"
2076 2094
2077 2095 [[items]]
2078 2096 section = "smtp"
2079 2097 name = "local_hostname"
2080 2098
2081 2099 [[items]]
2082 2100 section = "smtp"
2083 2101 name = "password"
2084 2102
2085 2103 [[items]]
2086 2104 section = "smtp"
2087 2105 name = "port"
2088 2106 default-type = "dynamic"
2089 2107
2090 2108 [[items]]
2091 2109 section = "smtp"
2092 2110 name = "tls"
2093 2111 default = "none"
2094 2112
2095 2113 [[items]]
2096 2114 section = "smtp"
2097 2115 name = "username"
2098 2116
2099 2117 [[items]]
2100 2118 section = "sparse"
2101 2119 name = "missingwarning"
2102 2120 default = true
2103 2121 experimental = true
2104 2122
2123
2124 # The "storage" section house config options that change how the repository
2125 # data are accessed by the current process but does not affects the on disk
2126 # format. They can also adjust how the storage is computed, but without affect
2127 # compatibility wither other clients.
2128 #
2129 # For deeper format change, see the `format` section.
2130
2131
2105 2132 [[items]]
2106 2133 section = "storage"
2107 2134 name = "dirstate-v2.slow-path"
2108 2135 default = "abort"
2109 2136 experimental = true # experimental as long as format.use-dirstate-v2 is.
2110 2137
2111 2138 [[items]]
2112 2139 section = "storage"
2140 name = "revbranchcache.mmap"
2141 default = true
2142
2143 [[items]]
2144 section = "storage"
2113 2145 name = "new-repo-backend"
2114 2146 default = "revlogv1"
2115 2147 experimental = true
2116 2148
2117 2149 [[items]]
2118 2150 section = "storage"
2119 2151 name = "revlog.delta-parent-search.candidate-group-chunk-size"
2120 2152 default = 20
2121 2153
2122 2154 [[items]]
2123 2155 section = "storage"
2124 2156 name = "revlog.issue6528.fix-incoming"
2125 2157 default = true
2126 2158
2127 2159 [[items]]
2128 2160 section = "storage"
2129 2161 name = "revlog.optimize-delta-parent-choice"
2130 2162 default = true
2131 2163 alias = [["format", "aggressivemergedeltas"]]
2132 2164
2133 2165 [[items]]
2134 2166 section = "storage"
2135 2167 name = "revlog.persistent-nodemap.mmap"
2136 2168 default = true
2137 2169
2138 2170 [[items]]
2139 2171 section = "storage"
2140 2172 name = "revlog.persistent-nodemap.slow-path"
2141 2173 default = "abort"
2142 2174
2143 2175 [[items]]
2144 2176 section = "storage"
2145 2177 name = "revlog.reuse-external-delta"
2146 2178 default = true
2147 2179
2148 2180 [[items]]
2149 2181 section = "storage"
2150 2182 name = "revlog.reuse-external-delta-parent"
2151 2183 documentation = """This option is true unless `format.generaldelta` is set."""
2152 2184
2153 2185 [[items]]
2154 2186 section = "storage"
2155 2187 name = "revlog.zlib.level"
2156 2188
2157 2189 [[items]]
2158 2190 section = "storage"
2159 2191 name = "revlog.zstd.level"
2160 2192
2161 2193 [[items]]
2162 2194 section = "subrepos"
2163 2195 name = "allowed"
2164 2196 default-type = "dynamic" # to make backporting simpler
2165 2197
2166 2198 [[items]]
2167 2199 section = "subrepos"
2168 2200 name = "git:allowed"
2169 2201 default-type = "dynamic"
2170 2202
2171 2203 [[items]]
2172 2204 section = "subrepos"
2173 2205 name = "hg:allowed"
2174 2206 default-type = "dynamic"
2175 2207
2176 2208 [[items]]
2177 2209 section = "subrepos"
2178 2210 name = "svn:allowed"
2179 2211 default-type = "dynamic"
2180 2212
2181 2213 [[items]]
2182 2214 section = "templateconfig"
2183 2215 name = ".*"
2184 2216 default-type = "dynamic"
2185 2217 generic = true
2186 2218
2187 2219 [[items]]
2188 2220 section = "templates"
2189 2221 name = ".*"
2190 2222 generic = true
2191 2223
2192 2224 [[items]]
2193 2225 section = "trusted"
2194 2226 name = "groups"
2195 2227 default-type = "list_type"
2196 2228
2197 2229 [[items]]
2198 2230 section = "trusted"
2199 2231 name = "users"
2200 2232 default-type = "list_type"
2201 2233
2202 2234 [[items]]
2203 2235 section = "ui"
2204 2236 name = "_usedassubrepo"
2205 2237 default = false
2206 2238
2207 2239 [[items]]
2208 2240 section = "ui"
2209 2241 name = "allowemptycommit"
2210 2242 default = false
2211 2243
2212 2244 [[items]]
2213 2245 section = "ui"
2214 2246 name = "archivemeta"
2215 2247 default = true
2216 2248
2217 2249 [[items]]
2218 2250 section = "ui"
2219 2251 name = "askusername"
2220 2252 default = false
2221 2253
2222 2254 [[items]]
2223 2255 section = "ui"
2224 2256 name = "available-memory"
2225 2257
2226 2258 [[items]]
2227 2259 section = "ui"
2228 2260 name = "clonebundlefallback"
2229 2261 default = false
2230 2262
2231 2263 [[items]]
2232 2264 section = "ui"
2233 2265 name = "clonebundleprefers"
2234 2266 default-type = "list_type"
2235 2267
2236 2268 [[items]]
2237 2269 section = "ui"
2238 2270 name = "clonebundles"
2239 2271 default = true
2240 2272
2241 2273 [[items]]
2242 2274 section = "ui"
2243 2275 name = "color"
2244 2276 default = "auto"
2245 2277
2246 2278 [[items]]
2247 2279 section = "ui"
2248 2280 name = "commitsubrepos"
2249 2281 default = false
2250 2282
2251 2283 [[items]]
2252 2284 section = "ui"
2253 2285 name = "debug"
2254 2286 default = false
2255 2287
2256 2288 [[items]]
2257 2289 section = "ui"
2258 2290 name = "debugger"
2259 2291
2260 2292 [[items]]
2261 2293 section = "ui"
2262 2294 name = "detailed-exit-code"
2263 2295 default = false
2264 2296 experimental = true
2265 2297
2266 2298 [[items]]
2267 2299 section = "ui"
2268 2300 name = "editor"
2269 2301 default-type = "dynamic"
2270 2302
2271 2303 [[items]]
2272 2304 section = "ui"
2273 2305 name = "fallbackencoding"
2274 2306
2275 2307 [[items]]
2276 2308 section = "ui"
2277 2309 name = "forcecwd"
2278 2310
2279 2311 [[items]]
2280 2312 section = "ui"
2281 2313 name = "forcemerge"
2282 2314
2283 2315 [[items]]
2284 2316 section = "ui"
2285 2317 name = "formatdebug"
2286 2318 default = false
2287 2319
2288 2320 [[items]]
2289 2321 section = "ui"
2290 2322 name = "formatjson"
2291 2323 default = false
2292 2324
2293 2325 [[items]]
2294 2326 section = "ui"
2295 2327 name = "formatted"
2296 2328
2297 2329 [[items]]
2298 2330 section = "ui"
2299 2331 name = "interactive"
2300 2332
2301 2333 [[items]]
2302 2334 section = "ui"
2303 2335 name = "interface"
2304 2336
2305 2337 [[items]]
2306 2338 section = "ui"
2307 2339 name = "interface.chunkselector"
2308 2340
2309 2341 [[items]]
2310 2342 section = "ui"
2311 2343 name = "large-file-limit"
2312 2344 default = 10485760
2313 2345
2314 2346 [[items]]
2315 2347 section = "ui"
2316 2348 name = "logblockedtimes"
2317 2349 default = false
2318 2350
2319 2351 [[items]]
2320 2352 section = "ui"
2321 2353 name = "merge"
2322 2354
2323 2355 [[items]]
2324 2356 section = "ui"
2325 2357 name = "mergemarkers"
2326 2358 default = "basic"
2327 2359
2328 2360 [[items]]
2329 2361 section = "ui"
2330 2362 name = "message-output"
2331 2363 default = "stdio"
2332 2364
2333 2365 [[items]]
2334 2366 section = "ui"
2335 2367 name = "nontty"
2336 2368 default = false
2337 2369
2338 2370 [[items]]
2339 2371 section = "ui"
2340 2372 name = "origbackuppath"
2341 2373
2342 2374 [[items]]
2343 2375 section = "ui"
2344 2376 name = "paginate"
2345 2377 default = true
2346 2378
2347 2379 [[items]]
2348 2380 section = "ui"
2349 2381 name = "patch"
2350 2382
2351 2383 [[items]]
2352 2384 section = "ui"
2353 2385 name = "portablefilenames"
2354 2386 default = "warn"
2355 2387
2356 2388 [[items]]
2357 2389 section = "ui"
2358 2390 name = "promptecho"
2359 2391 default = false
2360 2392
2361 2393 [[items]]
2362 2394 section = "ui"
2363 2395 name = "quiet"
2364 2396 default = false
2365 2397
2366 2398 [[items]]
2367 2399 section = "ui"
2368 2400 name = "quietbookmarkmove"
2369 2401 default = false
2370 2402
2371 2403 [[items]]
2372 2404 section = "ui"
2373 2405 name = "relative-paths"
2374 2406 default = "legacy"
2375 2407
2376 2408 [[items]]
2377 2409 section = "ui"
2378 2410 name = "remotecmd"
2379 2411 default = "hg"
2380 2412
2381 2413 [[items]]
2382 2414 section = "ui"
2383 2415 name = "report_untrusted"
2384 2416 default = true
2385 2417
2386 2418 [[items]]
2387 2419 section = "ui"
2388 2420 name = "rollback"
2389 2421 default = true
2390 2422
2391 2423 [[items]]
2392 2424 section = "ui"
2393 2425 name = "signal-safe-lock"
2394 2426 default = true
2395 2427
2396 2428 [[items]]
2397 2429 section = "ui"
2398 2430 name = "slash"
2399 2431 default = false
2400 2432
2401 2433 [[items]]
2402 2434 section = "ui"
2403 2435 name = "ssh"
2404 2436 default = "ssh"
2405 2437
2406 2438 [[items]]
2407 2439 section = "ui"
2408 2440 name = "ssherrorhint"
2409 2441
2410 2442 [[items]]
2411 2443 section = "ui"
2412 2444 name = "statuscopies"
2413 2445 default = false
2414 2446
2415 2447 [[items]]
2416 2448 section = "ui"
2417 2449 name = "strict"
2418 2450 default = false
2419 2451
2420 2452 [[items]]
2421 2453 section = "ui"
2422 2454 name = "style"
2423 2455 default = ""
2424 2456
2425 2457 [[items]]
2426 2458 section = "ui"
2427 2459 name = "supportcontact"
2428 2460
2429 2461 [[items]]
2430 2462 section = "ui"
2431 2463 name = "textwidth"
2432 2464 default = 78
2433 2465
2434 2466 [[items]]
2435 2467 section = "ui"
2436 2468 name = "timeout"
2437 2469 default = "600"
2438 2470
2439 2471 [[items]]
2440 2472 section = "ui"
2441 2473 name = "timeout.warn"
2442 2474 default = 0
2443 2475
2444 2476 [[items]]
2445 2477 section = "ui"
2446 2478 name = "timestamp-output"
2447 2479 default = false
2448 2480
2449 2481 [[items]]
2450 2482 section = "ui"
2451 2483 name = "traceback"
2452 2484 default = false
2453 2485
2454 2486 [[items]]
2455 2487 section = "ui"
2456 2488 name = "tweakdefaults"
2457 2489 default = false
2458 2490
2459 2491 [[items]]
2460 2492 section = "ui"
2461 2493 name = "username"
2462 2494 alias = [["ui", "user"]]
2463 2495
2464 2496 [[items]]
2465 2497 section = "ui"
2466 2498 name = "verbose"
2467 2499 default = false
2468 2500
2469 2501 [[items]]
2470 2502 section = "usage"
2471 2503 name = "repository-role"
2472 2504 default = "default"
2473 2505 documentation = """What this repository is used for.
2474 2506
2475 2507 This is used to adjust behavior and performance to best fit the repository purpose.
2476 2508
2477 2509 Currently recognised values are:
2478 2510 - default: an all purpose repository
2479 2511 """
2480 2512
2481 2513 [[items]]
2482 2514 section = "usage"
2483 2515 name = "resources"
2484 2516 default = "default"
2485 2517 documentation = """How aggressive Mercurial can be with resource usage:
2486 2518
2487 2519 Currently recognised values are:
2488 2520 - default: the default value currently is equivalent to medium,
2489 2521 - high: allows for higher cpu, memory and disk-space usage to improve the performance of some operations.
2490 2522 - medium: aims at a moderate resource usage,
2491 2523 - low: reduces resources usage when possible, decreasing overall performance.
2492 2524
2493 2525 For finer configuration, see also `usage.resources.cpu`,
2494 2526 `usage.resources.disk` and `usage.resources.memory`.
2495 2527 """
2496 2528
2497 2529 [[items]]
2498 2530 section = "usage"
2499 2531 name = "resources.cpu"
2500 2532 default = "default"
2501 2533 documentation = """How aggressive Mercurial can be in terms of cpu usage:
2502 2534
2503 2535 Currently recognised values are:
2504 2536 - default: the default value, inherits the value from `usage.resources`,
2505 2537 - high: allows for more aggressive cpu usage, improving storage quality and
2506 2538 the performance of some operations at the expense of machine load
2507 2539 - medium: aims at a moderate cpu usage,
2508 2540 - low: reduces cpu usage when possible, potentially at the expense of
2509 2541 slower operations, increased storage and exchange payload.
2510 2542
2511 2543 """
2512 2544
2513 2545 [[items]]
2514 2546 section = "usage"
2515 2547 name = "resources.disk"
2516 2548 default = "default"
2517 2549 documentation = """How aggressive Mercurial can be in terms of disk usage:
2518 2550
2519 2551 Currently recognised values are:
2520 2552 - default: the default value, inherits the value from `usage.resources`,
2521 2553 - high: allows for more disk space usage where it can improve the performance,
2522 2554 - medium: aims at a moderate disk usage,
2523 2555 - low: reduces disk usage when possible, decreasing performance in some occasion.
2524 2556 """
2525 2557
2526 2558 [[items]]
2527 2559 section = "usage"
2528 2560 name = "resources.memory"
2529 2561 default = "default"
2530 2562 documentation = """How aggressive Mercurial can be in terms of memory usage:
2531 2563
2532 2564 Currently recognised values are:
2533 2565 - default: the default value, inherits the value from `usage.resources`,
2534 2566 - high: allows for more aggressive memory usage to improve overall performance,
2535 2567 - medium: aims at a moderate memory usage,
2536 2568 - low: reduces memory usage when possible at the cost of overall performance.
2537 2569 """
2538 2570
2539 2571 [[items]]
2540 2572 section = "verify"
2541 2573 name = "skipflags"
2542 2574 default = 0
2543 2575
2544 2576 [[items]]
2545 2577 section = "web"
2546 2578 name = "accesslog"
2547 2579 default = "-"
2548 2580
2549 2581 [[items]]
2550 2582 section = "web"
2551 2583 name = "address"
2552 2584 default = ""
2553 2585
2554 2586 [[items]]
2555 2587 section = "web"
2556 2588 name = "allow-archive"
2557 2589 default-type = "list_type"
2558 2590 alias = [["web", "allow_archive"]]
2559 2591
2560 2592 [[items]]
2561 2593 section = "web"
2562 2594 name = "allow-pull"
2563 2595 default = true
2564 2596 alias = [["web", "allowpull"]]
2565 2597
2566 2598 [[items]]
2567 2599 section = "web"
2568 2600 name = "allow-push"
2569 2601 default-type = "list_type"
2570 2602 alias = [["web", "allow_push"]]
2571 2603
2572 2604 [[items]]
2573 2605 section = "web"
2574 2606 name = "allow_read"
2575 2607 default-type = "list_type"
2576 2608
2577 2609 [[items]]
2578 2610 section = "web"
2579 2611 name = "allowbz2"
2580 2612 default = false
2581 2613
2582 2614 [[items]]
2583 2615 section = "web"
2584 2616 name = "allowgz"
2585 2617 default = false
2586 2618
2587 2619 [[items]]
2588 2620 section = "web"
2589 2621 name = "allowzip"
2590 2622 default = false
2591 2623
2592 2624 [[items]]
2593 2625 section = "web"
2594 2626 name = "archivesubrepos"
2595 2627 default = false
2596 2628
2597 2629 [[items]]
2598 2630 section = "web"
2599 2631 name = "baseurl"
2600 2632
2601 2633 [[items]]
2602 2634 section = "web"
2603 2635 name = "cacerts"
2604 2636
2605 2637 [[items]]
2606 2638 section = "web"
2607 2639 name = "cache"
2608 2640 default = true
2609 2641
2610 2642 [[items]]
2611 2643 section = "web"
2612 2644 name = "certificate"
2613 2645
2614 2646 [[items]]
2615 2647 section = "web"
2616 2648 name = "collapse"
2617 2649 default = false
2618 2650
2619 2651 [[items]]
2620 2652 section = "web"
2621 2653 name = "comparisoncontext"
2622 2654 default = 5
2623 2655
2624 2656 [[items]]
2625 2657 section = "web"
2626 2658 name = "contact"
2627 2659
2628 2660 [[items]]
2629 2661 section = "web"
2630 2662 name = "csp"
2631 2663
2632 2664 [[items]]
2633 2665 section = "web"
2634 2666 name = "deny_push"
2635 2667 default-type = "list_type"
2636 2668
2637 2669 [[items]]
2638 2670 section = "web"
2639 2671 name = "deny_read"
2640 2672 default-type = "list_type"
2641 2673
2642 2674 [[items]]
2643 2675 section = "web"
2644 2676 name = "descend"
2645 2677 default = true
2646 2678
2647 2679 [[items]]
2648 2680 section = "web"
2649 2681 name = "description"
2650 2682 default = ""
2651 2683
2652 2684 [[items]]
2653 2685 section = "web"
2654 2686 name = "encoding"
2655 2687 default-type = "lazy_module"
2656 2688 default = "encoding.encoding"
2657 2689
2658 2690 [[items]]
2659 2691 section = "web"
2660 2692 name = "errorlog"
2661 2693 default = "-"
2662 2694
2663 2695 [[items]]
2664 2696 section = "web"
2665 2697 name = "guessmime"
2666 2698 default = false
2667 2699
2668 2700 [[items]]
2669 2701 section = "web"
2670 2702 name = "hidden"
2671 2703 default = false
2672 2704
2673 2705 [[items]]
2674 2706 section = "web"
2675 2707 name = "ipv6"
2676 2708 default = false
2677 2709
2678 2710 [[items]]
2679 2711 section = "web"
2680 2712 name = "labels"
2681 2713 default-type = "list_type"
2682 2714
2683 2715 [[items]]
2684 2716 section = "web"
2685 2717 name = "logoimg"
2686 2718 default = "hglogo.png"
2687 2719
2688 2720 [[items]]
2689 2721 section = "web"
2690 2722 name = "logourl"
2691 2723 default = "https://mercurial-scm.org/"
2692 2724
2693 2725 [[items]]
2694 2726 section = "web"
2695 2727 name = "maxchanges"
2696 2728 default = 10
2697 2729
2698 2730 [[items]]
2699 2731 section = "web"
2700 2732 name = "maxfiles"
2701 2733 default = 10
2702 2734
2703 2735 [[items]]
2704 2736 section = "web"
2705 2737 name = "maxshortchanges"
2706 2738 default = 60
2707 2739
2708 2740 [[items]]
2709 2741 section = "web"
2710 2742 name = "motd"
2711 2743 default = ""
2712 2744
2713 2745 [[items]]
2714 2746 section = "web"
2715 2747 name = "name"
2716 2748 default-type = "dynamic"
2717 2749
2718 2750 [[items]]
2719 2751 section = "web"
2720 2752 name = "port"
2721 2753 default = 8000
2722 2754
2723 2755 [[items]]
2724 2756 section = "web"
2725 2757 name = "prefix"
2726 2758 default = ""
2727 2759
2728 2760 [[items]]
2729 2761 section = "web"
2730 2762 name = "push_ssl"
2731 2763 default = true
2732 2764
2733 2765 [[items]]
2734 2766 section = "web"
2735 2767 name = "refreshinterval"
2736 2768 default = 20
2737 2769
2738 2770 [[items]]
2739 2771 section = "web"
2740 2772 name = "server-header"
2741 2773
2742 2774 [[items]]
2743 2775 section = "web"
2744 2776 name = "static"
2745 2777
2746 2778 [[items]]
2747 2779 section = "web"
2748 2780 name = "staticurl"
2749 2781
2750 2782 [[items]]
2751 2783 section = "web"
2752 2784 name = "stripes"
2753 2785 default = 1
2754 2786
2755 2787 [[items]]
2756 2788 section = "web"
2757 2789 name = "style"
2758 2790 default = "paper"
2759 2791
2760 2792 [[items]]
2761 2793 section = "web"
2762 2794 name = "templates"
2763 2795
2764 2796 [[items]]
2765 2797 section = "web"
2766 2798 name = "view"
2767 2799 default = "served"
2768 2800 experimental = true
2769 2801
2770 2802 [[items]]
2771 2803 section = "worker"
2772 2804 name = "backgroundclose"
2773 2805 default-type = "dynamic"
2774 2806
2775 2807 [[items]]
2776 2808 section = "worker"
2777 2809 name = "backgroundclosemaxqueue"
2778 2810 # Windows defaults to a limit of 512 open files. A buffer of 128
2779 2811 # should give us enough headway.
2780 2812 default = 384
2781 2813
2782 2814 [[items]]
2783 2815 section = "worker"
2784 2816 name = "backgroundcloseminfilecount"
2785 2817 default = 2048
2786 2818
2787 2819 [[items]]
2788 2820 section = "worker"
2789 2821 name = "backgroundclosethreadcount"
2790 2822 default = 4
2791 2823
2792 2824 [[items]]
2793 2825 section = "worker"
2794 2826 name = "enabled"
2795 2827 default = true
2796 2828
2797 2829 [[items]]
2798 2830 section = "worker"
2799 2831 name = "numcpus"
2800 2832
2801 2833 # Templates and template applications
2802 2834
2803 2835 [[template-applications]]
2804 2836 template = "diff-options"
2805 2837 section = "annotate"
2806 2838
2807 2839 [[template-applications]]
2808 2840 template = "diff-options"
2809 2841 section = "commands"
2810 2842 prefix = "commit.interactive"
2811 2843
2812 2844 [[template-applications]]
2813 2845 template = "diff-options"
2814 2846 section = "commands"
2815 2847 prefix = "revert.interactive"
2816 2848
2817 2849 [[template-applications]]
2818 2850 template = "diff-options"
2819 2851 section = "diff"
2820 2852
2821 2853 [templates]
2822 2854 [[templates.diff-options]]
2823 2855 suffix = "nodates"
2824 2856 default = false
2825 2857
2826 2858 [[templates.diff-options]]
2827 2859 suffix = "showfunc"
2828 2860 default = false
2829 2861
2830 2862 [[templates.diff-options]]
2831 2863 suffix = "unified"
2832 2864
2833 2865 [[templates.diff-options]]
2834 2866 suffix = "git"
2835 2867 default = false
2836 2868
2837 2869 [[templates.diff-options]]
2838 2870 suffix = "ignorews"
2839 2871 default = false
2840 2872
2841 2873 [[templates.diff-options]]
2842 2874 suffix = "ignorewsamount"
2843 2875 default = false
2844 2876
2845 2877 [[templates.diff-options]]
2846 2878 suffix = "ignoreblanklines"
2847 2879 default = false
2848 2880
2849 2881 [[templates.diff-options]]
2850 2882 suffix = "ignorewseol"
2851 2883 default = false
2852 2884
2853 2885 [[templates.diff-options]]
2854 2886 suffix = "nobinary"
2855 2887 default = false
2856 2888
2857 2889 [[templates.diff-options]]
2858 2890 suffix = "noprefix"
2859 2891 default = false
2860 2892
2861 2893 [[templates.diff-options]]
2862 2894 suffix = "word-diff"
2863 2895 default = false
2864 2896
2865 2897 # In-core extensions
2866 2898
2867 2899 [[items]]
2868 2900 section = "blackbox"
2869 2901 name = "debug.to-stderr"
2870 2902 default = false
2871 2903 in_core_extension = "blackbox"
2872 2904
2873 2905 [[items]]
2874 2906 section = "blackbox"
2875 2907 name = "dirty"
2876 2908 default = false
2877 2909 in_core_extension = "blackbox"
2878 2910
2879 2911 [[items]]
2880 2912 section = "blackbox"
2881 2913 name = "maxsize"
2882 2914 default = "1 MB"
2883 2915 in_core_extension = "blackbox"
2884 2916
2885 2917 [[items]]
2886 2918 section = "blackbox"
2887 2919 name = "logsource"
2888 2920 default = false
2889 2921 in_core_extension = "blackbox"
2890 2922
2891 2923 [[items]]
2892 2924 section = "blackbox"
2893 2925 name = "maxfiles"
2894 2926 default = 7
2895 2927 in_core_extension = "blackbox"
2896 2928
2897 2929 [[items]]
2898 2930 section = "blackbox"
2899 2931 name = "track"
2900 2932 default-type = "lambda"
2901 2933 default = ["*"]
2902 2934 in_core_extension = "blackbox"
2903 2935
2904 2936 [[items]]
2905 2937 section = "blackbox"
2906 2938 name = "ignore"
2907 2939 default-type = "lambda"
2908 2940 default = ["chgserver", "cmdserver", "extension"]
2909 2941 in_core_extension = "blackbox"
2910 2942
2911 2943 [[items]]
2912 2944 section = "blackbox"
2913 2945 name = "date-format"
2914 2946 default = ""
2915 2947 in_core_extension = "blackbox"
2916
2917 [[items]]
2918 section = "format"
2919 name = "mmap-revbranchcache"
2920 default = false
@@ -1,2133 +1,2131 b''
1 1 # stuff related specifically to patch manipulation / parsing
2 2 #
3 3 # Copyright 2008 Mark Edgington <edgimar@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7 #
8 8 # This code is based on the Mark Edgington's crecord extension.
9 9 # (Itself based on Bryan O'Sullivan's record extension.)
10 10
11 11
12 12 import os
13 13 import re
14 14 import signal
15 15
16 16 from .i18n import _
17 17 from .pycompat import (
18 18 open,
19 19 )
20 20 from . import (
21 21 diffhelper,
22 22 encoding,
23 23 error,
24 24 patch as patchmod,
25 25 pycompat,
26 26 scmutil,
27 27 util,
28 28 )
29 29 from .utils import stringutil
30 30
31 31 stringio = util.stringio
32 32
33 33 # patch comments based on the git one
34 34 diffhelptext = _(
35 35 b"""# To remove '-' lines, make them ' ' lines (context).
36 36 # To remove '+' lines, delete them.
37 37 # Lines starting with # will be removed from the patch.
38 38 """
39 39 )
40 40
41 41 hunkhelptext = _(
42 42 b"""#
43 43 # If the patch applies cleanly, the edited hunk will immediately be
44 44 # added to the record list. If it does not apply cleanly, a rejects file
45 45 # will be generated. You can use that when you try again. If all lines
46 46 # of the hunk are removed, then the edit is aborted and the hunk is left
47 47 # unchanged.
48 48 """
49 49 )
50 50
51 51 patchhelptext = _(
52 52 b"""#
53 53 # If the patch applies cleanly, the edited patch will immediately
54 54 # be finalised. If it does not apply cleanly, rejects files will be
55 55 # generated. You can use those when you try again.
56 56 """
57 57 )
58 58
59 59 try:
60 60 import curses
61 61 import curses.ascii
62 62
63 63 curses.error
64 64 except (ImportError, AttributeError):
65 65 curses = None
66 66
67 67
68 68 class fallbackerror(error.Abort):
69 69 """Error that indicates the client should try to fallback to text mode."""
70 70
71 71 # Inherits from error.Abort so that existing behavior is preserved if the
72 72 # calling code does not know how to fallback.
73 73
74 74
75 75 def checkcurses(ui):
76 76 """Return True if the user wants to use curses
77 77
78 78 This method returns True if curses is found (and that python is built with
79 79 it) and that the user has the correct flag for the ui.
80 80 """
81 81 return curses and ui.interface(b"chunkselector") == b"curses"
82 82
83 83
84 84 class patchnode:
85 85 """abstract class for patch graph nodes
86 86 (i.e. patchroot, header, hunk, hunkline)
87 87 """
88 88
89 89 @property
90 90 def content(self):
91 91 return b''
92 92
93 93 def firstchild(self):
94 94 raise NotImplementedError(b"method must be implemented by subclass")
95 95
96 96 def lastchild(self):
97 97 raise NotImplementedError(b"method must be implemented by subclass")
98 98
99 99 def allchildren(self):
100 100 """Return a list of all of the direct children of this node"""
101 101 raise NotImplementedError(b"method must be implemented by subclass")
102 102
103 103 def nextsibling(self):
104 104 """
105 105 Return the closest next item of the same type where there are no items
106 106 of different types between the current item and this closest item.
107 107 If no such item exists, return None.
108 108 """
109 109 raise NotImplementedError(b"method must be implemented by subclass")
110 110
111 111 def prevsibling(self):
112 112 """
113 113 Return the closest previous item of the same type where there are no
114 114 items of different types between the current item and this closest item.
115 115 If no such item exists, return None.
116 116 """
117 117 raise NotImplementedError(b"method must be implemented by subclass")
118 118
119 119 def parentitem(self):
120 120 raise NotImplementedError(b"method must be implemented by subclass")
121 121
122 122 def nextitem(self, skipfolded=True):
123 123 """
124 124 Try to return the next item closest to this item, regardless of item's
125 125 type (header, hunk, or hunkline).
126 126
127 127 If skipfolded == True, and the current item is folded, then the child
128 128 items that are hidden due to folding will be skipped when determining
129 129 the next item.
130 130
131 131 If it is not possible to get the next item, return None.
132 132 """
133 133 try:
134 134 itemfolded = self.folded
135 135 except AttributeError:
136 136 itemfolded = False
137 137 if skipfolded and itemfolded:
138 138 nextitem = self.nextsibling()
139 139 if nextitem is None:
140 140 try:
141 141 nextitem = self.parentitem().nextsibling()
142 142 except AttributeError:
143 143 nextitem = None
144 144 return nextitem
145 145 else:
146 146 # try child
147 147 item = self.firstchild()
148 148 if item is not None:
149 149 return item
150 150
151 151 # else try next sibling
152 152 item = self.nextsibling()
153 153 if item is not None:
154 154 return item
155 155
156 156 try:
157 157 # else try parent's next sibling
158 158 item = self.parentitem().nextsibling()
159 159 if item is not None:
160 160 return item
161 161
162 162 # else return grandparent's next sibling (or None)
163 163 return self.parentitem().parentitem().nextsibling()
164 164
165 165 except AttributeError: # parent and/or grandparent was None
166 166 return None
167 167
168 168 def previtem(self, skipfolded=None):
169 169 """
170 170 Try to return the previous item closest to this item, regardless of
171 171 item's type (header, hunk, or hunkline).
172 172
173 173 If it is not possible to get the previous item, return None.
174 174 """
175 175 # try previous sibling's last child's last child,
176 176 # else try previous sibling's last child, else try previous sibling
177 177 prevsibling = self.prevsibling()
178 178 if prevsibling is not None:
179 179 prevsiblinglastchild = prevsibling.lastchild()
180 180 if (prevsiblinglastchild is not None) and not prevsibling.folded:
181 181 prevsiblinglclc = prevsiblinglastchild.lastchild()
182 182 if (
183 183 prevsiblinglclc is not None
184 184 ) and not prevsiblinglastchild.folded:
185 185 return prevsiblinglclc
186 186 else:
187 187 return prevsiblinglastchild
188 188 else:
189 189 return prevsibling
190 190
191 191 # try parent (or None)
192 192 return self.parentitem()
193 193
194 194
195 195 class patch(patchnode, list): # todo: rename patchroot
196 196 """
197 197 list of header objects representing the patch.
198 198 """
199 199
200 200 def __init__(self, headerlist):
201 201 self.extend(headerlist)
202 202 # add parent patch object reference to each header
203 203 for header in self:
204 204 header.patch = self
205 205
206 206
207 207 class uiheader(patchnode):
208 208 """
209 209 patchnode class wrapping a patch.header
210 210 """
211 211
212 212 def __init__(self, header):
213 213 self.nonuiheader = header
214 214 # flag to indicate whether to apply this chunk
215 215 self.applied = True
216 216 # flag which only affects the status display indicating if a node's
217 217 # children are partially applied (i.e. some applied, some not).
218 218 self.partial = False
219 219
220 220 # flag to indicate whether to display as folded/unfolded to user
221 221 self.folded = True
222 222
223 223 # list of all headers in patch
224 224 self.patch = None
225 225
226 226 # flag is False if this header was ever unfolded from initial state
227 227 self.neverunfolded = True
228 228 self.hunks = [uihunk(h, self) for h in self.hunks]
229 229
230 230 @property
231 231 def content(self):
232 232 return self.filename()
233 233
234 234 def prettystr(self):
235 235 x = stringio()
236 236 self.pretty(x)
237 237 return x.getvalue()
238 238
239 239 def nextsibling(self):
240 240 numheadersinpatch = len(self.patch)
241 241 indexofthisheader = self.patch.index(self)
242 242
243 243 if indexofthisheader < numheadersinpatch - 1:
244 244 nextheader = self.patch[indexofthisheader + 1]
245 245 return nextheader
246 246 else:
247 247 return None
248 248
249 249 def prevsibling(self):
250 250 indexofthisheader = self.patch.index(self)
251 251 if indexofthisheader > 0:
252 252 previousheader = self.patch[indexofthisheader - 1]
253 253 return previousheader
254 254 else:
255 255 return None
256 256
257 257 def parentitem(self):
258 258 """
259 259 there is no 'real' parent item of a header that can be selected,
260 260 so return None.
261 261 """
262 262 return None
263 263
264 264 def firstchild(self):
265 265 """return the first child of this item, if one exists. otherwise
266 266 None."""
267 267 if len(self.hunks) > 0:
268 268 return self.hunks[0]
269 269 else:
270 270 return None
271 271
272 272 def lastchild(self):
273 273 """return the last child of this item, if one exists. otherwise
274 274 None."""
275 275 if len(self.hunks) > 0:
276 276 return self.hunks[-1]
277 277 else:
278 278 return None
279 279
280 280 def allchildren(self):
281 281 """return a list of all of the direct children of this node"""
282 282 return self.hunks
283 283
284 284 def __getattr__(self, name):
285 285 return getattr(self.nonuiheader, name)
286 286
287 287
288 288 class uihunkline(patchnode):
289 289 """represents a changed line in a hunk"""
290 290
291 291 def __init__(self, linetext, hunk):
292 292 self.linetext = linetext
293 293 self.applied = True
294 294 # the parent hunk to which this line belongs
295 295 self.hunk = hunk
296 296 # folding lines currently is not used/needed, but this flag is needed
297 297 # in the previtem method.
298 298 self.folded = False
299 299
300 300 @property
301 301 def content(self):
302 302 return self.linetext
303 303
304 304 def prettystr(self):
305 305 return self.linetext
306 306
307 307 def nextsibling(self):
308 308 numlinesinhunk = len(self.hunk.changedlines)
309 309 indexofthisline = self.hunk.changedlines.index(self)
310 310
311 311 if indexofthisline < numlinesinhunk - 1:
312 312 nextline = self.hunk.changedlines[indexofthisline + 1]
313 313 return nextline
314 314 else:
315 315 return None
316 316
317 317 def prevsibling(self):
318 318 indexofthisline = self.hunk.changedlines.index(self)
319 319 if indexofthisline > 0:
320 320 previousline = self.hunk.changedlines[indexofthisline - 1]
321 321 return previousline
322 322 else:
323 323 return None
324 324
325 325 def parentitem(self):
326 326 """return the parent to the current item"""
327 327 return self.hunk
328 328
329 329 def firstchild(self):
330 330 """return the first child of this item, if one exists. otherwise
331 331 None."""
332 332 # hunk-lines don't have children
333 333 return None
334 334
335 335 def lastchild(self):
336 336 """return the last child of this item, if one exists. otherwise
337 337 None."""
338 338 # hunk-lines don't have children
339 339 return None
340 340
341 341
342 342 class uihunk(patchnode):
343 343 """ui patch hunk, wraps a hunk and keep track of ui behavior"""
344 344
345 345 maxcontext = 3
346 346
347 347 def __init__(self, hunk, header):
348 348 self._hunk = hunk
349 349 self.changedlines = [uihunkline(line, self) for line in hunk.hunk]
350 350 self.header = header
351 351 # used at end for detecting how many removed lines were un-applied
352 352 self.originalremoved = self.removed
353 353
354 354 # flag to indicate whether to display as folded/unfolded to user
355 355 self.folded = True
356 356 # flag to indicate whether to apply this chunk
357 357 self.applied = True
358 358 # flag which only affects the status display indicating if a node's
359 359 # children are partially applied (i.e. some applied, some not).
360 360 self.partial = False
361 361
362 362 @property
363 363 def content(self):
364 364 return self.proc if self.proc else b''
365 365
366 366 def nextsibling(self):
367 367 numhunksinheader = len(self.header.hunks)
368 368 indexofthishunk = self.header.hunks.index(self)
369 369
370 370 if indexofthishunk < numhunksinheader - 1:
371 371 nexthunk = self.header.hunks[indexofthishunk + 1]
372 372 return nexthunk
373 373 else:
374 374 return None
375 375
376 376 def prevsibling(self):
377 377 indexofthishunk = self.header.hunks.index(self)
378 378 if indexofthishunk > 0:
379 379 previoushunk = self.header.hunks[indexofthishunk - 1]
380 380 return previoushunk
381 381 else:
382 382 return None
383 383
384 384 def parentitem(self):
385 385 """return the parent to the current item"""
386 386 return self.header
387 387
388 388 def firstchild(self):
389 389 """return the first child of this item, if one exists. otherwise
390 390 None."""
391 391 if len(self.changedlines) > 0:
392 392 return self.changedlines[0]
393 393 else:
394 394 return None
395 395
396 396 def lastchild(self):
397 397 """return the last child of this item, if one exists. otherwise
398 398 None."""
399 399 if len(self.changedlines) > 0:
400 400 return self.changedlines[-1]
401 401 else:
402 402 return None
403 403
404 404 def allchildren(self):
405 405 """return a list of all of the direct children of this node"""
406 406 return self.changedlines
407 407
408 408 def countchanges(self):
409 409 """changedlines -> (n+,n-)"""
410 410 add = len(
411 411 [
412 412 l
413 413 for l in self.changedlines
414 414 if l.applied and l.prettystr().startswith(b'+')
415 415 ]
416 416 )
417 417 rem = len(
418 418 [
419 419 l
420 420 for l in self.changedlines
421 421 if l.applied and l.prettystr().startswith(b'-')
422 422 ]
423 423 )
424 424 return add, rem
425 425
426 426 def getfromtoline(self):
427 427 # calculate the number of removed lines converted to context lines
428 428 removedconvertedtocontext = self.originalremoved - self.removed
429 429
430 430 contextlen = (
431 431 len(self.before) + len(self.after) + removedconvertedtocontext
432 432 )
433 433 if self.after and self.after[-1] == diffhelper.MISSING_NEWLINE_MARKER:
434 434 contextlen -= 1
435 435 fromlen = contextlen + self.removed
436 436 tolen = contextlen + self.added
437 437
438 438 # diffutils manual, section "2.2.2.2 detailed description of unified
439 439 # format": "an empty hunk is considered to end at the line that
440 440 # precedes the hunk."
441 441 #
442 442 # so, if either of hunks is empty, decrease its line start. --immerrr
443 443 # but only do this if fromline > 0, to avoid having, e.g fromline=-1.
444 444 fromline, toline = self.fromline, self.toline
445 445 if fromline != 0:
446 446 if fromlen == 0:
447 447 fromline -= 1
448 448 if tolen == 0 and toline > 0:
449 449 toline -= 1
450 450
451 451 fromtoline = b'@@ -%d,%d +%d,%d @@%s\n' % (
452 452 fromline,
453 453 fromlen,
454 454 toline,
455 455 tolen,
456 456 self.proc and (b' ' + self.proc),
457 457 )
458 458 return fromtoline
459 459
460 460 def write(self, fp):
461 461 # updated self.added/removed, which are used by getfromtoline()
462 462 self.added, self.removed = self.countchanges()
463 463 fp.write(self.getfromtoline())
464 464
465 465 hunklinelist = []
466 466 # add the following to the list: (1) all applied lines, and
467 467 # (2) all unapplied removal lines (convert these to context lines)
468 468 for changedline in self.changedlines:
469 469 changedlinestr = changedline.prettystr()
470 470 if changedline.applied:
471 471 hunklinelist.append(changedlinestr)
472 472 elif changedlinestr.startswith(b"-"):
473 473 hunklinelist.append(b" " + changedlinestr[1:])
474 474
475 475 fp.write(b''.join(self.before + hunklinelist + self.after))
476 476
477 477 pretty = write
478 478
479 479 def prettystr(self):
480 480 x = stringio()
481 481 self.pretty(x)
482 482 return x.getvalue()
483 483
484 484 def reversehunk(self):
485 485 """return a recordhunk which is the reverse of the hunk
486 486
487 487 Assuming the displayed patch is diff(A, B) result. The returned hunk is
488 488 intended to be applied to B, instead of A.
489 489
490 490 For example, when A is "0\n1\n2\n6\n" and B is "0\n3\n4\n5\n6\n", and
491 491 the user made the following selection:
492 492
493 493 0
494 494 [x] -1 [x]: selected
495 495 [ ] -2 [ ]: not selected
496 496 [x] +3
497 497 [ ] +4
498 498 [x] +5
499 499 6
500 500
501 501 This function returns a hunk like:
502 502
503 503 0
504 504 -3
505 505 -4
506 506 -5
507 507 +1
508 508 +4
509 509 6
510 510
511 511 Note "4" was first deleted then added. That's because "4" exists in B
512 512 side and "-4" must exist between "-3" and "-5" to make the patch
513 513 applicable to B.
514 514 """
515 515 dels = []
516 516 adds = []
517 517 noeol = False
518 518 for line in self.changedlines:
519 519 text = line.linetext
520 520 if line.linetext == diffhelper.MISSING_NEWLINE_MARKER:
521 521 noeol = True
522 522 continue
523 523 if line.applied:
524 524 if text.startswith(b'+'):
525 525 dels.append(text[1:])
526 526 elif text.startswith(b'-'):
527 527 adds.append(text[1:])
528 528 elif text.startswith(b'+'):
529 529 dels.append(text[1:])
530 530 adds.append(text[1:])
531 531 hunk = [b'-%s' % l for l in dels] + [b'+%s' % l for l in adds]
532 532 if noeol and hunk:
533 533 # Remove the newline from the end of the hunk.
534 534 hunk[-1] = hunk[-1][:-1]
535 535 h = self._hunk
536 536 return patchmod.recordhunk(
537 537 h.header, h.toline, h.fromline, h.proc, h.before, hunk, h.after
538 538 )
539 539
540 540 def __getattr__(self, name):
541 541 return getattr(self._hunk, name)
542 542
543 543 def __repr__(self):
544 544 return '<hunk %r@%d>' % (self.filename(), self.fromline)
545 545
546 546
547 547 def filterpatch(ui, chunks, chunkselector, operation=None):
548 548 """interactively filter patch chunks into applied-only chunks"""
549 549 chunks = list(chunks)
550 550 # convert chunks list into structure suitable for displaying/modifying
551 551 # with curses. create a list of headers only.
552 552 headers = [c for c in chunks if isinstance(c, patchmod.header)]
553 553
554 554 # if there are no changed files
555 555 if len(headers) == 0:
556 556 return [], {}
557 557 uiheaders = [uiheader(h) for h in headers]
558 558 # let user choose headers/hunks/lines, and mark their applied flags
559 559 # accordingly
560 560 ret = chunkselector(ui, uiheaders, operation=operation)
561 561 appliedhunklist = []
562 562 for hdr in uiheaders:
563 563 if hdr.applied and (
564 564 hdr.special() or len([h for h in hdr.hunks if h.applied]) > 0
565 565 ):
566 566 appliedhunklist.append(hdr)
567 567 fixoffset = 0
568 568 for hnk in hdr.hunks:
569 569 if hnk.applied:
570 570 appliedhunklist.append(hnk)
571 571 # adjust the 'to'-line offset of the hunk to be correct
572 572 # after de-activating some of the other hunks for this file
573 573 if fixoffset:
574 574 # hnk = copy.copy(hnk) # necessary??
575 575 hnk.toline += fixoffset
576 576 else:
577 577 fixoffset += hnk.removed - hnk.added
578 578
579 579 return (appliedhunklist, ret)
580 580
581 581
582 582 def chunkselector(ui, headerlist, operation=None):
583 583 """
584 584 curses interface to get selection of chunks, and mark the applied flags
585 585 of the chosen chunks.
586 586 """
587 587 ui.write(_(b'starting interactive selection\n'))
588 588 chunkselector = curseschunkselector(headerlist, ui, operation)
589 589 origsigtstp = sentinel = object()
590 590 if hasattr(signal, 'SIGTSTP'):
591 591 origsigtstp = signal.getsignal(signal.SIGTSTP)
592 592 try:
593 593 with util.with_lc_ctype():
594 594 curses.wrapper(chunkselector.main)
595 595 if chunkselector.initexc is not None:
596 596 raise chunkselector.initexc
597 597 # ncurses does not restore signal handler for SIGTSTP
598 598 finally:
599 599 if origsigtstp is not sentinel:
600 600 signal.signal(signal.SIGTSTP, origsigtstp)
601 601 return chunkselector.opts
602 602
603 603
604 604 def testdecorator(testfn, f):
605 605 def u(*args, **kwargs):
606 606 return f(testfn, *args, **kwargs)
607 607
608 608 return u
609 609
610 610
611 611 def testchunkselector(testfn, ui, headerlist, operation=None):
612 612 """
613 613 test interface to get selection of chunks, and mark the applied flags
614 614 of the chosen chunks.
615 615 """
616 616 chunkselector = curseschunkselector(headerlist, ui, operation)
617 617
618 618 class dummystdscr:
619 619 def clear(self):
620 620 pass
621 621
622 622 def refresh(self):
623 623 pass
624 624
625 625 chunkselector.stdscr = dummystdscr()
626 626 if testfn and os.path.exists(testfn):
627 627 testf = open(testfn, b'r')
628 628 # TODO: open in binary mode?
629 629 testcommands = [x.rstrip('\n') for x in testf.readlines()]
630 630 testf.close()
631 631 while True:
632 632 if chunkselector.handlekeypressed(testcommands.pop(0), test=True):
633 633 break
634 634 return chunkselector.opts
635 635
636 636
637 637 _headermessages = { # {operation: text}
638 638 b'apply': _(b'Select hunks to apply'),
639 639 b'discard': _(b'Select hunks to discard'),
640 640 b'keep': _(b'Select hunks to keep'),
641 641 None: _(b'Select hunks to record'),
642 642 }
643 643
644 644
645 645 class curseschunkselector:
646 646 def __init__(self, headerlist, ui, operation=None):
647 647 # put the headers into a patch object
648 648 self.headerlist = patch(headerlist)
649 649
650 650 self.ui = ui
651 651 self.opts = {}
652 652
653 653 self.errorstr = None
654 654 # list of all chunks
655 655 self.chunklist = []
656 656 for h in headerlist:
657 657 self.chunklist.append(h)
658 658 self.chunklist.extend(h.hunks)
659 659
660 660 # dictionary mapping (fgcolor, bgcolor) pairs to the
661 661 # corresponding curses color-pair value.
662 662 self.colorpairs = {}
663 663 # maps custom nicknames of color-pairs to curses color-pair values
664 664 self.colorpairnames = {}
665 665
666 666 # Honor color setting of ui section. Keep colored setup as
667 667 # long as not explicitly set to a falsy value - especially,
668 668 # when not set at all. This is to stay most compatible with
669 669 # previous (color only) behaviour.
670 670 uicolor = stringutil.parsebool(self.ui.config(b'ui', b'color'))
671 671 self.usecolor = uicolor is not False
672 672
673 673 # the currently selected header, hunk, or hunk-line
674 674 self.currentselecteditem = self.headerlist[0]
675 675 self.lastapplieditem = None
676 676
677 677 # updated when printing out patch-display -- the 'lines' here are the
678 678 # line positions *in the pad*, not on the screen.
679 679 self.selecteditemstartline = 0
680 680 self.selecteditemendline = None
681 681
682 682 # define indentation levels
683 683 self.headerindentnumchars = 0
684 684 self.hunkindentnumchars = 3
685 685 self.hunklineindentnumchars = 6
686 686
687 687 # the first line of the pad to print to the screen
688 688 self.firstlineofpadtoprint = 0
689 689
690 690 # keeps track of the number of lines in the pad
691 691 self.numpadlines = None
692 692
693 693 self.numstatuslines = 1
694 694
695 695 # keep a running count of the number of lines printed to the pad
696 696 # (used for determining when the selected item begins/ends)
697 697 self.linesprintedtopadsofar = 0
698 698
699 699 # stores optional text for a commit comment provided by the user
700 700 self.commenttext = b""
701 701
702 702 # if the last 'toggle all' command caused all changes to be applied
703 703 self.waslasttoggleallapplied = True
704 704
705 705 # affects some ui text
706 706 if operation not in _headermessages:
707 707 raise error.ProgrammingError(
708 708 b'unexpected operation: %s' % operation
709 709 )
710 710 self.operation = operation
711 711
712 712 self.regex = None
713 713
714 714 def uparrowevent(self):
715 715 """
716 716 try to select the previous item to the current item that has the
717 717 most-indented level. for example, if a hunk is selected, try to select
718 718 the last hunkline of the hunk prior to the selected hunk. or, if
719 719 the first hunkline of a hunk is currently selected, then select the
720 720 hunk itself.
721 721 """
722 722 currentitem = self.currentselecteditem
723 723
724 724 nextitem = currentitem.previtem()
725 725
726 726 if nextitem is None:
727 727 # if no parent item (i.e. currentitem is the first header), then
728 728 # no change...
729 729 nextitem = currentitem
730 730
731 731 self.currentselecteditem = nextitem
732 732
733 733 def uparrowshiftevent(self):
734 734 """
735 735 select (if possible) the previous item on the same level as the
736 736 currently selected item. otherwise, select (if possible) the
737 737 parent-item of the currently selected item.
738 738 """
739 739 currentitem = self.currentselecteditem
740 740 nextitem = currentitem.prevsibling()
741 741 # if there's no previous sibling, try choosing the parent
742 742 if nextitem is None:
743 743 nextitem = currentitem.parentitem()
744 744 if nextitem is None:
745 745 # if no parent item (i.e. currentitem is the first header), then
746 746 # no change...
747 747 nextitem = currentitem
748 748
749 749 self.currentselecteditem = nextitem
750 750 self.recenterdisplayedarea()
751 751
752 752 def downarrowevent(self):
753 753 """
754 754 try to select the next item to the current item that has the
755 755 most-indented level. for example, if a hunk is selected, select
756 756 the first hunkline of the selected hunk. or, if the last hunkline of
757 757 a hunk is currently selected, then select the next hunk, if one exists,
758 758 or if not, the next header if one exists.
759 759 """
760 760 # self.startprintline += 1 #debug
761 761 currentitem = self.currentselecteditem
762 762
763 763 nextitem = currentitem.nextitem()
764 764 # if there's no next item, keep the selection as-is
765 765 if nextitem is None:
766 766 nextitem = currentitem
767 767
768 768 self.currentselecteditem = nextitem
769 769
770 770 def downarrowshiftevent(self):
771 771 """
772 772 select (if possible) the next item on the same level as the currently
773 773 selected item. otherwise, select (if possible) the next item on the
774 774 same level as the parent item of the currently selected item.
775 775 """
776 776 currentitem = self.currentselecteditem
777 777 nextitem = currentitem.nextsibling()
778 778 # if there's no next sibling, try choosing the parent's nextsibling
779 779 if nextitem is None:
780 780 try:
781 781 nextitem = currentitem.parentitem().nextsibling()
782 782 except AttributeError:
783 783 # parentitem returned None, so nextsibling() can't be called
784 784 nextitem = None
785 785 if nextitem is None:
786 786 # if parent has no next sibling, then no change...
787 787 nextitem = currentitem
788 788
789 789 self.currentselecteditem = nextitem
790 790 self.recenterdisplayedarea()
791 791
792 792 def nextsametype(self, test=False):
793 793 currentitem = self.currentselecteditem
794 794 sametype = lambda item: isinstance(item, type(currentitem))
795 795 nextitem = currentitem.nextitem()
796 796
797 797 while nextitem is not None and not sametype(nextitem):
798 798 nextitem = nextitem.nextitem()
799 799
800 800 if nextitem is None:
801 801 nextitem = currentitem
802 802 else:
803 803 parent = nextitem.parentitem()
804 804 if parent is not None and parent.folded:
805 805 self.togglefolded(parent)
806 806
807 807 self.currentselecteditem = nextitem
808 808 if not test:
809 809 self.recenterdisplayedarea()
810 810
811 811 def rightarrowevent(self):
812 812 """
813 813 select (if possible) the first of this item's child-items.
814 814 """
815 815 currentitem = self.currentselecteditem
816 816 nextitem = currentitem.firstchild()
817 817
818 818 # turn off folding if we want to show a child-item
819 819 if currentitem.folded:
820 820 self.togglefolded(currentitem)
821 821
822 822 if nextitem is None:
823 823 # if no next item on parent-level, then no change...
824 824 nextitem = currentitem
825 825
826 826 self.currentselecteditem = nextitem
827 827
828 828 def leftarrowevent(self):
829 829 """
830 830 if the current item can be folded (i.e. it is an unfolded header or
831 831 hunk), then fold it. otherwise try select (if possible) the parent
832 832 of this item.
833 833 """
834 834 currentitem = self.currentselecteditem
835 835
836 836 # try to fold the item
837 837 if not isinstance(currentitem, uihunkline):
838 838 if not currentitem.folded:
839 839 self.togglefolded(item=currentitem)
840 840 return
841 841
842 842 # if it can't be folded, try to select the parent item
843 843 nextitem = currentitem.parentitem()
844 844
845 845 if nextitem is None:
846 846 # if no item on parent-level, then no change...
847 847 nextitem = currentitem
848 848 if not nextitem.folded:
849 849 self.togglefolded(item=nextitem)
850 850
851 851 self.currentselecteditem = nextitem
852 852
853 853 def leftarrowshiftevent(self):
854 854 """
855 855 select the header of the current item (or fold current item if the
856 856 current item is already a header).
857 857 """
858 858 currentitem = self.currentselecteditem
859 859
860 860 if isinstance(currentitem, uiheader):
861 861 if not currentitem.folded:
862 862 self.togglefolded(item=currentitem)
863 863 return
864 864
865 865 # select the parent item recursively until we're at a header
866 866 while True:
867 867 nextitem = currentitem.parentitem()
868 868 if nextitem is None:
869 869 break
870 870 else:
871 871 currentitem = nextitem
872 872
873 873 self.currentselecteditem = currentitem
874 874
875 875 def updatescroll(self):
876 876 """scroll the screen to fully show the currently-selected"""
877 877 selstart = self.selecteditemstartline
878 878 selend = self.selecteditemendline
879 879
880 880 padstart = self.firstlineofpadtoprint
881 881 padend = padstart + self.yscreensize - self.numstatuslines - 1
882 882 # 'buffered' pad start/end values which scroll with a certain
883 883 # top/bottom context margin
884 884 padstartbuffered = padstart + 3
885 885 padendbuffered = padend - 3
886 886
887 887 if selend > padendbuffered:
888 888 self.scrolllines(selend - padendbuffered)
889 889 elif selstart < padstartbuffered:
890 890 # negative values scroll in pgup direction
891 891 self.scrolllines(selstart - padstartbuffered)
892 892
893 893 def scrolllines(self, numlines):
894 894 """scroll the screen up (down) by numlines when numlines >0 (<0)."""
895 895 self.firstlineofpadtoprint += numlines
896 896 if self.firstlineofpadtoprint < 0:
897 897 self.firstlineofpadtoprint = 0
898 898 if self.firstlineofpadtoprint > self.numpadlines - 1:
899 899 self.firstlineofpadtoprint = self.numpadlines - 1
900 900
901 901 def toggleapply(self, item=None):
902 902 """
903 903 toggle the applied flag of the specified item. if no item is specified,
904 904 toggle the flag of the currently selected item.
905 905 """
906 906 if item is None:
907 907 item = self.currentselecteditem
908 908 # Only set this when NOT using 'toggleall'
909 909 self.lastapplieditem = item
910 910
911 911 item.applied = not item.applied
912 912
913 913 if isinstance(item, uiheader):
914 914 item.partial = False
915 915 if item.applied:
916 916 # apply all its hunks
917 917 for hnk in item.hunks:
918 918 hnk.applied = True
919 919 # apply all their hunklines
920 920 for hunkline in hnk.changedlines:
921 921 hunkline.applied = True
922 922 else:
923 923 # un-apply all its hunks
924 924 for hnk in item.hunks:
925 925 hnk.applied = False
926 926 hnk.partial = False
927 927 # un-apply all their hunklines
928 928 for hunkline in hnk.changedlines:
929 929 hunkline.applied = False
930 930 elif isinstance(item, uihunk):
931 931 item.partial = False
932 932 # apply all it's hunklines
933 933 for hunkline in item.changedlines:
934 934 hunkline.applied = item.applied
935 935
936 936 siblingappliedstatus = [hnk.applied for hnk in item.header.hunks]
937 937 allsiblingsapplied = not (False in siblingappliedstatus)
938 938 nosiblingsapplied = not (True in siblingappliedstatus)
939 939
940 940 siblingspartialstatus = [hnk.partial for hnk in item.header.hunks]
941 941 somesiblingspartial = True in siblingspartialstatus
942 942
943 943 # cases where applied or partial should be removed from header
944 944
945 945 # if no 'sibling' hunks are applied (including this hunk)
946 946 if nosiblingsapplied:
947 947 if not item.header.special():
948 948 item.header.applied = False
949 949 item.header.partial = False
950 950 else: # some/all parent siblings are applied
951 951 item.header.applied = True
952 952 item.header.partial = (
953 953 somesiblingspartial or not allsiblingsapplied
954 954 )
955 955
956 956 elif isinstance(item, uihunkline):
957 957 siblingappliedstatus = [ln.applied for ln in item.hunk.changedlines]
958 958 allsiblingsapplied = not (False in siblingappliedstatus)
959 959 nosiblingsapplied = not (True in siblingappliedstatus)
960 960
961 961 # if no 'sibling' lines are applied
962 962 if nosiblingsapplied:
963 963 item.hunk.applied = False
964 964 item.hunk.partial = False
965 965 elif allsiblingsapplied:
966 966 item.hunk.applied = True
967 967 item.hunk.partial = False
968 968 else: # some siblings applied
969 969 item.hunk.applied = True
970 970 item.hunk.partial = True
971 971
972 972 parentsiblingsapplied = [
973 973 hnk.applied for hnk in item.hunk.header.hunks
974 974 ]
975 975 noparentsiblingsapplied = not (True in parentsiblingsapplied)
976 976 allparentsiblingsapplied = not (False in parentsiblingsapplied)
977 977
978 978 parentsiblingspartial = [
979 979 hnk.partial for hnk in item.hunk.header.hunks
980 980 ]
981 981 someparentsiblingspartial = True in parentsiblingspartial
982 982
983 983 # if all parent hunks are not applied, un-apply header
984 984 if noparentsiblingsapplied:
985 985 if not item.hunk.header.special():
986 986 item.hunk.header.applied = False
987 987 item.hunk.header.partial = False
988 988 # set the applied and partial status of the header if needed
989 989 else: # some/all parent siblings are applied
990 990 item.hunk.header.applied = True
991 991 item.hunk.header.partial = (
992 992 someparentsiblingspartial or not allparentsiblingsapplied
993 993 )
994 994
995 995 def toggleall(self):
996 996 """toggle the applied flag of all items."""
997 997 if self.waslasttoggleallapplied: # then unapply them this time
998 998 for item in self.headerlist:
999 999 if item.applied:
1000 1000 self.toggleapply(item)
1001 1001 else:
1002 1002 for item in self.headerlist:
1003 1003 if not item.applied:
1004 1004 self.toggleapply(item)
1005 1005 self.waslasttoggleallapplied = not self.waslasttoggleallapplied
1006 1006
1007 1007 def flipselections(self):
1008 1008 """
1009 1009 Flip all selections. Every selected line is unselected and vice
1010 1010 versa.
1011 1011 """
1012 1012 for header in self.headerlist:
1013 1013 for hunk in header.allchildren():
1014 1014 for line in hunk.allchildren():
1015 1015 self.toggleapply(line)
1016 1016
1017 1017 def toggleallbetween(self):
1018 1018 """toggle applied on or off for all items in range [lastapplied,
1019 1019 current]."""
1020 1020 if (
1021 1021 not self.lastapplieditem
1022 1022 or self.currentselecteditem == self.lastapplieditem
1023 1023 ):
1024 1024 # Treat this like a normal 'x'/' '
1025 1025 self.toggleapply()
1026 1026 return
1027 1027
1028 1028 startitem = self.lastapplieditem
1029 1029 enditem = self.currentselecteditem
1030 1030 # Verify that enditem is "after" startitem, otherwise swap them.
1031 1031 for direction in [b'forward', b'reverse']:
1032 1032 nextitem = startitem.nextitem()
1033 1033 while nextitem and nextitem != enditem:
1034 1034 nextitem = nextitem.nextitem()
1035 1035 if nextitem:
1036 1036 break
1037 1037 # Looks like we went the wrong direction :)
1038 1038 startitem, enditem = enditem, startitem
1039 1039
1040 1040 if not nextitem:
1041 1041 # We didn't find a path going either forward or backward? Don't know
1042 1042 # how this can happen, let's not crash though.
1043 1043 return
1044 1044
1045 1045 nextitem = startitem
1046 1046 # Switch all items to be the opposite state of the currently selected
1047 1047 # item. Specifically:
1048 1048 # [ ] startitem
1049 1049 # [x] middleitem
1050 1050 # [ ] enditem <-- currently selected
1051 1051 # This will turn all three on, since the currently selected item is off.
1052 1052 # This does *not* invert each item (i.e. middleitem stays marked/on)
1053 1053 desiredstate = not self.currentselecteditem.applied
1054 1054 while nextitem != enditem.nextitem():
1055 1055 if nextitem.applied != desiredstate:
1056 1056 self.toggleapply(item=nextitem)
1057 1057 nextitem = nextitem.nextitem()
1058 1058
1059 1059 def togglefolded(self, item=None, foldparent=False):
1060 1060 """toggle folded flag of specified item (defaults to currently
1061 1061 selected)"""
1062 1062 if item is None:
1063 1063 item = self.currentselecteditem
1064 1064 if foldparent or (isinstance(item, uiheader) and item.neverunfolded):
1065 1065 if not isinstance(item, uiheader):
1066 1066 # we need to select the parent item in this case
1067 1067 self.currentselecteditem = item = item.parentitem()
1068 1068 elif item.neverunfolded:
1069 1069 item.neverunfolded = False
1070 1070
1071 1071 # also fold any foldable children of the parent/current item
1072 1072 if isinstance(item, uiheader): # the original or 'new' item
1073 1073 for child in item.allchildren():
1074 1074 child.folded = not item.folded
1075 1075
1076 1076 if isinstance(item, (uiheader, uihunk)):
1077 1077 item.folded = not item.folded
1078 1078
1079 1079 def alignstring(self, instr, window):
1080 1080 """
1081 1081 add whitespace to the end of a string in order to make it fill
1082 1082 the screen in the x direction. the current cursor position is
1083 1083 taken into account when making this calculation. the string can span
1084 1084 multiple lines.
1085 1085 """
1086 1086 y, xstart = window.getyx()
1087 1087 width = self.xscreensize
1088 1088 # turn tabs into spaces
1089 1089 instr = instr.expandtabs(4)
1090 1090 strwidth = encoding.colwidth(instr)
1091 1091 numspaces = width - ((strwidth + xstart) % width)
1092 1092 return instr + b" " * numspaces
1093 1093
1094 1094 def printstring(
1095 1095 self,
1096 1096 window,
1097 1097 text,
1098 1098 fgcolor=None,
1099 1099 bgcolor=None,
1100 1100 pair=None,
1101 1101 pairname=None,
1102 1102 attrlist=None,
1103 1103 towin=True,
1104 1104 align=True,
1105 1105 showwhtspc=False,
1106 1106 ):
1107 1107 """
1108 1108 print the string, text, with the specified colors and attributes, to
1109 1109 the specified curses window object.
1110 1110
1111 1111 the foreground and background colors are of the form
1112 1112 curses.color_xxxx, where xxxx is one of: [black, blue, cyan, green,
1113 1113 magenta, red, white, yellow]. if pairname is provided, a color
1114 1114 pair will be looked up in the self.colorpairnames dictionary.
1115 1115
1116 1116 attrlist is a list containing text attributes in the form of
1117 1117 curses.a_xxxx, where xxxx can be: [bold, dim, normal, standout,
1118 1118 underline].
1119 1119
1120 1120 if align == True, whitespace is added to the printed string such that
1121 1121 the string stretches to the right border of the window.
1122 1122
1123 1123 if showwhtspc == True, trailing whitespace of a string is highlighted.
1124 1124 """
1125 1125 # preprocess the text, converting tabs to spaces
1126 1126 text = text.expandtabs(4)
1127 1127 # strip \n, and convert control characters to ^[char] representation
1128 1128 text = re.sub(
1129 1129 br'[\x00-\x08\x0a-\x1f]',
1130 1130 lambda m: b'^' + pycompat.sysbytes(chr(ord(m.group()) + 64)),
1131 1131 text.strip(b'\n'),
1132 1132 )
1133 1133
1134 1134 if pair is not None:
1135 1135 colorpair = pair
1136 1136 elif pairname is not None:
1137 1137 colorpair = self.colorpairnames[pairname]
1138 1138 else:
1139 1139 if fgcolor is None:
1140 1140 fgcolor = -1
1141 1141 if bgcolor is None:
1142 1142 bgcolor = -1
1143 1143 if (fgcolor, bgcolor) in self.colorpairs:
1144 1144 colorpair = self.colorpairs[(fgcolor, bgcolor)]
1145 1145 else:
1146 1146 colorpair = self.getcolorpair(fgcolor, bgcolor)
1147 1147 # add attributes if possible
1148 1148 if attrlist is None:
1149 1149 attrlist = []
1150 1150 if colorpair < 256:
1151 1151 # then it is safe to apply all attributes
1152 1152 for textattr in attrlist:
1153 1153 colorpair |= textattr
1154 1154 else:
1155 1155 # just apply a select few (safe?) attributes
1156 1156 for textattr in (curses.A_UNDERLINE, curses.A_BOLD):
1157 1157 if textattr in attrlist:
1158 1158 colorpair |= textattr
1159 1159
1160 1160 y, xstart = self.chunkpad.getyx()
1161 1161 t = b"" # variable for counting lines printed
1162 1162 # if requested, show trailing whitespace
1163 1163 if showwhtspc:
1164 1164 origlen = len(text)
1165 1165 text = text.rstrip(b' \n') # tabs have already been expanded
1166 1166 strippedlen = len(text)
1167 1167 numtrailingspaces = origlen - strippedlen
1168 1168
1169 1169 if towin:
1170 1170 window.addstr(encoding.strfromlocal(text), colorpair)
1171 1171 t += text
1172 1172
1173 1173 if showwhtspc:
1174 1174 wscolorpair = colorpair | curses.A_REVERSE
1175 1175 if towin:
1176 1176 for i in range(numtrailingspaces):
1177 1177 window.addch(curses.ACS_CKBOARD, wscolorpair)
1178 1178 t += b" " * numtrailingspaces
1179 1179
1180 1180 if align:
1181 1181 if towin:
1182 1182 extrawhitespace = self.alignstring(b"", window)
1183 1183 window.addstr(extrawhitespace, colorpair)
1184 1184 else:
1185 1185 # need to use t, since the x position hasn't incremented
1186 1186 extrawhitespace = self.alignstring(t, window)
1187 1187 t += extrawhitespace
1188 1188
1189 1189 # is reset to 0 at the beginning of printitem()
1190 1190
1191 1191 linesprinted = (xstart + len(t)) // self.xscreensize
1192 1192 self.linesprintedtopadsofar += linesprinted
1193 1193 return t
1194 1194
1195 1195 def _getstatuslinesegments(self):
1196 1196 """-> [str]. return segments"""
1197 1197 selected = self.currentselecteditem.applied
1198 1198 spaceselect = _(b'space/enter: select')
1199 1199 spacedeselect = _(b'space/enter: deselect')
1200 1200 # Format the selected label into a place as long as the longer of the
1201 1201 # two possible labels. This may vary by language.
1202 1202 spacelen = max(len(spaceselect), len(spacedeselect))
1203 1203 selectedlabel = b'%-*s' % (
1204 1204 spacelen,
1205 1205 spacedeselect if selected else spaceselect,
1206 1206 )
1207 1207 segments = [
1208 1208 _headermessages[self.operation],
1209 1209 b'-',
1210 1210 _(b'[x]=selected **=collapsed'),
1211 1211 _(b'c: confirm'),
1212 1212 _(b'q: abort'),
1213 1213 _(b'arrow keys: move/expand/collapse'),
1214 1214 selectedlabel,
1215 1215 _(b'?: help'),
1216 1216 ]
1217 1217 return segments
1218 1218
1219 1219 def _getstatuslines(self):
1220 1220 """() -> [str]. return short help used in the top status window"""
1221 1221 if self.errorstr is not None:
1222 1222 lines = [self.errorstr, _(b'Press any key to continue')]
1223 1223 else:
1224 1224 # wrap segments to lines
1225 1225 segments = self._getstatuslinesegments()
1226 1226 width = self.xscreensize
1227 1227 lines = []
1228 1228 lastwidth = width
1229 1229 for s in segments:
1230 1230 w = encoding.colwidth(s)
1231 1231 sep = b' ' * (1 + (s and s[0] not in b'-['))
1232 1232 if lastwidth + w + len(sep) >= width:
1233 1233 lines.append(s)
1234 1234 lastwidth = w
1235 1235 else:
1236 1236 lines[-1] += sep + s
1237 1237 lastwidth += w + len(sep)
1238 1238 if len(lines) != self.numstatuslines:
1239 1239 self.numstatuslines = len(lines)
1240 1240 self.statuswin.resize(self.numstatuslines, self.xscreensize)
1241 1241 return [stringutil.ellipsis(l, self.xscreensize - 1) for l in lines]
1242 1242
1243 1243 def updatescreen(self):
1244 1244 self.statuswin.erase()
1245 1245 self.chunkpad.erase()
1246 1246
1247 1247 printstring = self.printstring
1248 1248
1249 1249 # print out the status lines at the top
1250 1250 try:
1251 1251 for line in self._getstatuslines():
1252 1252 printstring(self.statuswin, line, pairname=b"legend")
1253 1253 self.statuswin.refresh()
1254 1254 except curses.error:
1255 1255 pass
1256 1256 if self.errorstr is not None:
1257 1257 return
1258 1258
1259 1259 # print out the patch in the remaining part of the window
1260 1260 try:
1261 1261 self.printitem()
1262 1262 self.updatescroll()
1263 1263 self.chunkpad.refresh(
1264 1264 self.firstlineofpadtoprint,
1265 1265 0,
1266 1266 self.numstatuslines,
1267 1267 0,
1268 1268 self.yscreensize - self.numstatuslines,
1269 1269 self.xscreensize - 1,
1270 1270 )
1271 1271 except curses.error:
1272 1272 pass
1273 1273
1274 1274 def getstatusprefixstring(self, item):
1275 1275 """
1276 1276 create a string to prefix a line with which indicates whether 'item'
1277 1277 is applied and/or folded.
1278 1278 """
1279 1279
1280 1280 # create checkbox string
1281 1281 if item.applied:
1282 1282 if not isinstance(item, uihunkline) and item.partial:
1283 1283 checkbox = b"[~]"
1284 1284 else:
1285 1285 checkbox = b"[x]"
1286 1286 else:
1287 1287 checkbox = b"[ ]"
1288 1288
1289 1289 try:
1290 1290 if item.folded:
1291 1291 checkbox += b"**"
1292 1292 if isinstance(item, uiheader):
1293 1293 # one of "m", "a", or "d" (modified, added, deleted)
1294 1294 filestatus = item.changetype
1295 1295
1296 1296 checkbox += filestatus + b" "
1297 1297 else:
1298 1298 checkbox += b" "
1299 1299 if isinstance(item, uiheader):
1300 1300 # add two more spaces for headers
1301 1301 checkbox += b" "
1302 1302 except AttributeError: # not foldable
1303 1303 checkbox += b" "
1304 1304
1305 1305 return checkbox
1306 1306
1307 1307 def printheader(
1308 1308 self, header, selected=False, towin=True, ignorefolding=False
1309 1309 ):
1310 1310 """
1311 1311 print the header to the pad. if countlines is True, don't print
1312 1312 anything, but just count the number of lines which would be printed.
1313 1313 """
1314 1314
1315 1315 outstr = b""
1316 1316 text = header.prettystr()
1317 1317 chunkindex = self.chunklist.index(header)
1318 1318
1319 1319 if chunkindex != 0 and not header.folded:
1320 1320 # add separating line before headers
1321 1321 outstr += self.printstring(
1322 1322 self.chunkpad, b'_' * self.xscreensize, towin=towin, align=False
1323 1323 )
1324 1324 # select color-pair based on if the header is selected
1325 1325 colorpair = self.getcolorpair(
1326 1326 name=selected and b"selected" or b"normal", attrlist=[curses.A_BOLD]
1327 1327 )
1328 1328
1329 1329 # print out each line of the chunk, expanding it to screen width
1330 1330
1331 1331 # number of characters to indent lines on this level by
1332 1332 indentnumchars = 0
1333 1333 checkbox = self.getstatusprefixstring(header)
1334 1334 if not header.folded or ignorefolding:
1335 1335 textlist = text.split(b"\n")
1336 1336 linestr = checkbox + textlist[0]
1337 1337 else:
1338 1338 linestr = checkbox + header.filename()
1339 1339 outstr += self.printstring(
1340 1340 self.chunkpad, linestr, pair=colorpair, towin=towin
1341 1341 )
1342 1342 if not header.folded or ignorefolding:
1343 1343 if len(textlist) > 1:
1344 1344 for line in textlist[1:]:
1345 1345 linestr = b" " * (indentnumchars + len(checkbox)) + line
1346 1346 outstr += self.printstring(
1347 1347 self.chunkpad, linestr, pair=colorpair, towin=towin
1348 1348 )
1349 1349
1350 1350 return outstr
1351 1351
1352 1352 def printhunklinesbefore(
1353 1353 self, hunk, selected=False, towin=True, ignorefolding=False
1354 1354 ):
1355 1355 """includes start/end line indicator"""
1356 1356 outstr = b""
1357 1357 # where hunk is in list of siblings
1358 1358 hunkindex = hunk.header.hunks.index(hunk)
1359 1359
1360 1360 if hunkindex != 0:
1361 1361 # add separating line before headers
1362 1362 outstr += self.printstring(
1363 1363 self.chunkpad, b' ' * self.xscreensize, towin=towin, align=False
1364 1364 )
1365 1365
1366 1366 colorpair = self.getcolorpair(
1367 1367 name=selected and b"selected" or b"normal", attrlist=[curses.A_BOLD]
1368 1368 )
1369 1369
1370 1370 # print out from-to line with checkbox
1371 1371 checkbox = self.getstatusprefixstring(hunk)
1372 1372
1373 1373 lineprefix = b" " * self.hunkindentnumchars + checkbox
1374 1374 frtoline = b" " + hunk.getfromtoline().strip(b"\n")
1375 1375
1376 1376 outstr += self.printstring(
1377 1377 self.chunkpad, lineprefix, towin=towin, align=False
1378 1378 ) # add uncolored checkbox/indent
1379 1379 outstr += self.printstring(
1380 1380 self.chunkpad, frtoline, pair=colorpair, towin=towin
1381 1381 )
1382 1382
1383 1383 if hunk.folded and not ignorefolding:
1384 1384 # skip remainder of output
1385 1385 return outstr
1386 1386
1387 1387 # print out lines of the chunk preceeding changed-lines
1388 1388 for line in hunk.before:
1389 1389 linestr = (
1390 1390 b" " * (self.hunklineindentnumchars + len(checkbox)) + line
1391 1391 )
1392 1392 outstr += self.printstring(self.chunkpad, linestr, towin=towin)
1393 1393
1394 1394 return outstr
1395 1395
1396 1396 def printhunklinesafter(self, hunk, towin=True, ignorefolding=False):
1397 1397 outstr = b""
1398 1398 if hunk.folded and not ignorefolding:
1399 1399 return outstr
1400 1400
1401 1401 # a bit superfluous, but to avoid hard-coding indent amount
1402 1402 checkbox = self.getstatusprefixstring(hunk)
1403 1403 for line in hunk.after:
1404 1404 linestr = (
1405 1405 b" " * (self.hunklineindentnumchars + len(checkbox)) + line
1406 1406 )
1407 1407 outstr += self.printstring(self.chunkpad, linestr, towin=towin)
1408 1408
1409 1409 return outstr
1410 1410
1411 1411 def printhunkchangedline(self, hunkline, selected=False, towin=True):
1412 1412 outstr = b""
1413 1413 checkbox = self.getstatusprefixstring(hunkline)
1414 1414
1415 1415 linestr = hunkline.prettystr().strip(b"\n")
1416 1416
1417 1417 # select color-pair based on whether line is an addition/removal
1418 1418 if selected:
1419 1419 colorpair = self.getcolorpair(name=b"selected")
1420 1420 elif linestr.startswith(b"+"):
1421 1421 colorpair = self.getcolorpair(name=b"addition")
1422 1422 elif linestr.startswith(b"-"):
1423 1423 colorpair = self.getcolorpair(name=b"deletion")
1424 1424 elif linestr.startswith(b"\\"):
1425 1425 colorpair = self.getcolorpair(name=b"normal")
1426 1426
1427 1427 lineprefix = b" " * self.hunklineindentnumchars + checkbox
1428 1428 outstr += self.printstring(
1429 1429 self.chunkpad, lineprefix, towin=towin, align=False
1430 1430 ) # add uncolored checkbox/indent
1431 1431 outstr += self.printstring(
1432 1432 self.chunkpad, linestr, pair=colorpair, towin=towin, showwhtspc=True
1433 1433 )
1434 1434 return outstr
1435 1435
1436 1436 def printitem(
1437 1437 self, item=None, ignorefolding=False, recursechildren=True, towin=True
1438 1438 ):
1439 1439 """
1440 1440 use __printitem() to print the the specified item.applied.
1441 1441 if item is not specified, then print the entire patch.
1442 1442 (hiding folded elements, etc. -- see __printitem() docstring)
1443 1443 """
1444 1444
1445 1445 if item is None:
1446 1446 item = self.headerlist
1447 1447 if recursechildren:
1448 1448 self.linesprintedtopadsofar = 0
1449 1449
1450 1450 outstr = []
1451 1451 self.__printitem(
1452 1452 item, ignorefolding, recursechildren, outstr, towin=towin
1453 1453 )
1454 1454 return b''.join(outstr)
1455 1455
1456 1456 def outofdisplayedarea(self):
1457 1457 y, _ = self.chunkpad.getyx() # cursor location
1458 1458 # * 2 here works but an optimization would be the max number of
1459 1459 # consecutive non selectable lines
1460 1460 # i.e the max number of context line for any hunk in the patch
1461 1461 miny = min(0, self.firstlineofpadtoprint - self.yscreensize)
1462 1462 maxy = self.firstlineofpadtoprint + self.yscreensize * 2
1463 1463 return y < miny or y > maxy
1464 1464
1465 1465 def handleselection(self, item, recursechildren):
1466 1466 selected = item is self.currentselecteditem
1467 1467 if selected and recursechildren:
1468 1468 # assumes line numbering starting from line 0
1469 1469 self.selecteditemstartline = self.linesprintedtopadsofar
1470 1470 selecteditemlines = self.getnumlinesdisplayed(
1471 1471 item, recursechildren=False
1472 1472 )
1473 1473 self.selecteditemendline = (
1474 1474 self.selecteditemstartline + selecteditemlines - 1
1475 1475 )
1476 1476 return selected
1477 1477
1478 1478 def __printitem(
1479 1479 self, item, ignorefolding, recursechildren, outstr, towin=True
1480 1480 ):
1481 1481 """
1482 1482 recursive method for printing out patch/header/hunk/hunk-line data to
1483 1483 screen. also returns a string with all of the content of the displayed
1484 1484 patch (not including coloring, etc.).
1485 1485
1486 1486 if ignorefolding is True, then folded items are printed out.
1487 1487
1488 1488 if recursechildren is False, then only print the item without its
1489 1489 child items.
1490 1490 """
1491 1491
1492 1492 if towin and self.outofdisplayedarea():
1493 1493 return
1494 1494
1495 1495 selected = self.handleselection(item, recursechildren)
1496 1496
1497 1497 # patch object is a list of headers
1498 1498 if isinstance(item, patch):
1499 1499 if recursechildren:
1500 1500 for hdr in item:
1501 1501 self.__printitem(
1502 1502 hdr, ignorefolding, recursechildren, outstr, towin
1503 1503 )
1504 1504 # todo: eliminate all isinstance() calls
1505 1505 if isinstance(item, uiheader):
1506 1506 outstr.append(
1507 1507 self.printheader(
1508 1508 item, selected, towin=towin, ignorefolding=ignorefolding
1509 1509 )
1510 1510 )
1511 1511 if recursechildren:
1512 1512 for hnk in item.hunks:
1513 1513 self.__printitem(
1514 1514 hnk, ignorefolding, recursechildren, outstr, towin
1515 1515 )
1516 1516 elif isinstance(item, uihunk) and (
1517 1517 (not item.header.folded) or ignorefolding
1518 1518 ):
1519 1519 # print the hunk data which comes before the changed-lines
1520 1520 outstr.append(
1521 1521 self.printhunklinesbefore(
1522 1522 item, selected, towin=towin, ignorefolding=ignorefolding
1523 1523 )
1524 1524 )
1525 1525 if recursechildren:
1526 1526 for l in item.changedlines:
1527 1527 self.__printitem(
1528 1528 l, ignorefolding, recursechildren, outstr, towin
1529 1529 )
1530 1530 outstr.append(
1531 1531 self.printhunklinesafter(
1532 1532 item, towin=towin, ignorefolding=ignorefolding
1533 1533 )
1534 1534 )
1535 1535 elif isinstance(item, uihunkline) and (
1536 1536 (not item.hunk.folded) or ignorefolding
1537 1537 ):
1538 1538 outstr.append(
1539 1539 self.printhunkchangedline(item, selected, towin=towin)
1540 1540 )
1541 1541
1542 1542 return outstr
1543 1543
1544 1544 def getnumlinesdisplayed(
1545 1545 self, item=None, ignorefolding=False, recursechildren=True
1546 1546 ):
1547 1547 """
1548 1548 return the number of lines which would be displayed if the item were
1549 1549 to be printed to the display. the item will not be printed to the
1550 1550 display (pad).
1551 1551 if no item is given, assume the entire patch.
1552 1552 if ignorefolding is True, folded items will be unfolded when counting
1553 1553 the number of lines.
1554 1554 """
1555 1555
1556 1556 # temporarily disable printing to windows by printstring
1557 1557 patchdisplaystring = self.printitem(
1558 1558 item, ignorefolding, recursechildren, towin=False
1559 1559 )
1560 1560 numlines = len(patchdisplaystring) // self.xscreensize
1561 1561 return numlines
1562 1562
1563 1563 def sigwinchhandler(self, n, frame):
1564 1564 """handle window resizing"""
1565 1565 try:
1566 curses.endwin()
1567 1566 self.xscreensize, self.yscreensize = scmutil.termsize(self.ui)
1568 1567 self.statuswin.resize(self.numstatuslines, self.xscreensize)
1569 1568 self.numpadlines = self.getnumlinesdisplayed(ignorefolding=True) + 1
1570 1569 self.chunkpad = curses.newpad(self.numpadlines, self.xscreensize)
1571 1570 except curses.error:
1572 1571 pass
1573 1572
1574 1573 def getcolorpair(
1575 1574 self, fgcolor=None, bgcolor=None, name=None, attrlist=None
1576 1575 ):
1577 1576 """
1578 1577 get a curses color pair, adding it to self.colorpairs if it is not
1579 1578 already defined. an optional string, name, can be passed as a shortcut
1580 1579 for referring to the color-pair. by default, if no arguments are
1581 1580 specified, the white foreground / black background color-pair is
1582 1581 returned.
1583 1582
1584 1583 it is expected that this function will be used exclusively for
1585 1584 initializing color pairs, and not curses.init_pair().
1586 1585
1587 1586 attrlist is used to 'flavor' the returned color-pair. this information
1588 1587 is not stored in self.colorpairs. it contains attribute values like
1589 1588 curses.A_BOLD.
1590 1589 """
1591 1590
1592 1591 if (name is not None) and name in self.colorpairnames:
1593 1592 # then get the associated color pair and return it
1594 1593 colorpair = self.colorpairnames[name]
1595 1594 else:
1596 1595 if fgcolor is None:
1597 1596 fgcolor = -1
1598 1597 if bgcolor is None:
1599 1598 bgcolor = -1
1600 1599 if (fgcolor, bgcolor) in self.colorpairs:
1601 1600 colorpair = self.colorpairs[(fgcolor, bgcolor)]
1602 1601 else:
1603 1602 pairindex = len(self.colorpairs) + 1
1604 1603 if self.usecolor:
1605 1604 curses.init_pair(pairindex, fgcolor, bgcolor)
1606 1605 colorpair = self.colorpairs[
1607 1606 (fgcolor, bgcolor)
1608 1607 ] = curses.color_pair(pairindex)
1609 1608 if name is not None:
1610 1609 self.colorpairnames[name] = curses.color_pair(pairindex)
1611 1610 else:
1612 1611 cval = 0
1613 1612 if name is not None:
1614 1613 if name == b'selected':
1615 1614 cval = curses.A_REVERSE
1616 1615 self.colorpairnames[name] = cval
1617 1616 colorpair = self.colorpairs[(fgcolor, bgcolor)] = cval
1618 1617
1619 1618 # add attributes if possible
1620 1619 if attrlist is None:
1621 1620 attrlist = []
1622 1621 if colorpair < 256:
1623 1622 # then it is safe to apply all attributes
1624 1623 for textattr in attrlist:
1625 1624 colorpair |= textattr
1626 1625 else:
1627 1626 # just apply a select few (safe?) attributes
1628 1627 for textattrib in (curses.A_UNDERLINE, curses.A_BOLD):
1629 1628 if textattrib in attrlist:
1630 1629 colorpair |= textattrib
1631 1630 return colorpair
1632 1631
1633 1632 def initcolorpair(self, *args, **kwargs):
1634 1633 """same as getcolorpair."""
1635 1634 self.getcolorpair(*args, **kwargs)
1636 1635
1637 1636 def helpwindow(self):
1638 1637 """print a help window to the screen. exit after any keypress."""
1639 1638 helptext = _(
1640 1639 b""" [press any key to return to the patch-display]
1641 1640
1642 1641 The curses hunk selector allows you to interactively choose among the
1643 1642 changes you have made, and confirm only those changes you select for
1644 1643 further processing by the command you are running (such as commit,
1645 1644 shelve, or revert). After confirming the selected changes, the
1646 1645 unselected changes are still present in your working copy, so you can
1647 1646 use the hunk selector multiple times to split large changes into
1648 1647 smaller changesets. the following are valid keystrokes:
1649 1648
1650 1649 x [space] : (un-)select item ([~]/[x] = partly/fully applied)
1651 1650 [enter] : (un-)select item and go to next item of same type
1652 1651 A : (un-)select all items
1653 1652 X : (un-)select all items between current and most-recent
1654 1653 up/down-arrow [k/j] : go to previous/next unfolded item
1655 1654 pgup/pgdn [K/J] : go to previous/next item of same type
1656 1655 right/left-arrow [l/h] : go to child item / parent item
1657 1656 shift-left-arrow [H] : go to parent header / fold selected header
1658 1657 g : go to the top
1659 1658 G : go to the bottom
1660 1659 f : fold / unfold item, hiding/revealing its children
1661 1660 F : fold / unfold parent item and all of its ancestors
1662 1661 ctrl-l : scroll the selected line to the top of the screen
1663 1662 m : edit / resume editing the commit message
1664 1663 e : edit the currently selected hunk
1665 1664 a : toggle all selections
1666 1665 c : confirm selected changes
1667 1666 r : review/edit and confirm selected changes
1668 1667 / : regex search for code or filename
1669 1668 n : next search result for code or filename
1670 1669 N : previous search result for code or filename
1671 1670 q : quit without confirming (no changes will be made)
1672 1671 ? : help (what you're currently reading)"""
1673 1672 )
1674 1673
1675 1674 helpwin = curses.newwin(self.yscreensize, 0, 0, 0)
1676 1675 helplines = helptext.split(b"\n")
1677 1676 helplines = helplines + [b" "] * (
1678 1677 self.yscreensize - self.numstatuslines - len(helplines) - 1
1679 1678 )
1680 1679 try:
1681 1680 for line in helplines:
1682 1681 self.printstring(helpwin, line, pairname=b"legend")
1683 1682 except curses.error:
1684 1683 pass
1685 1684 helpwin.refresh()
1686 1685 try:
1687 1686 with self.ui.timeblockedsection(b'crecord'):
1688 1687 helpwin.getkey()
1689 1688 except curses.error:
1690 1689 pass
1691 1690
1692 1691 def commitMessageWindow(self):
1693 1692 """Create a temporary commit message editing window on the screen."""
1694 1693
1695 1694 curses.raw()
1696 1695 curses.def_prog_mode()
1697 curses.endwin()
1698 1696 self.commenttext = self.ui.edit(self.commenttext, self.ui.username())
1699 1697 curses.cbreak()
1700 1698 self.stdscr.refresh()
1701 1699 self.stdscr.keypad(1) # allow arrow-keys to continue to function
1702 1700
1703 1701 def handlefirstlineevent(self):
1704 1702 """
1705 1703 Handle 'g' to navigate to the top most file in the ncurses window.
1706 1704 """
1707 1705 self.currentselecteditem = self.headerlist[0]
1708 1706 currentitem = self.currentselecteditem
1709 1707 # select the parent item recursively until we're at a header
1710 1708 while True:
1711 1709 nextitem = currentitem.parentitem()
1712 1710 if nextitem is None:
1713 1711 break
1714 1712 else:
1715 1713 currentitem = nextitem
1716 1714
1717 1715 self.currentselecteditem = currentitem
1718 1716
1719 1717 def handlelastlineevent(self):
1720 1718 """
1721 1719 Handle 'G' to navigate to the bottom most file/hunk/line depending
1722 1720 on the whether the fold is active or not.
1723 1721
1724 1722 If the bottom most file is folded, it navigates to that file and
1725 1723 stops there. If the bottom most file is unfolded, it navigates to
1726 1724 the bottom most hunk in that file and stops there. If the bottom most
1727 1725 hunk is unfolded, it navigates to the bottom most line in that hunk.
1728 1726 """
1729 1727 currentitem = self.currentselecteditem
1730 1728 nextitem = currentitem.nextitem()
1731 1729 # select the child item recursively until we're at a footer
1732 1730 while nextitem is not None:
1733 1731 nextitem = currentitem.nextitem()
1734 1732 if nextitem is None:
1735 1733 break
1736 1734 else:
1737 1735 currentitem = nextitem
1738 1736
1739 1737 self.currentselecteditem = currentitem
1740 1738 self.recenterdisplayedarea()
1741 1739
1742 1740 def confirmationwindow(self, windowtext):
1743 1741 """display an informational window, then wait for and return a
1744 1742 keypress."""
1745 1743
1746 1744 confirmwin = curses.newwin(self.yscreensize, 0, 0, 0)
1747 1745 try:
1748 1746 lines = windowtext.split(b"\n")
1749 1747 for line in lines:
1750 1748 self.printstring(confirmwin, line, pairname=b"selected")
1751 1749 except curses.error:
1752 1750 pass
1753 1751 self.stdscr.refresh()
1754 1752 confirmwin.refresh()
1755 1753 try:
1756 1754 with self.ui.timeblockedsection(b'crecord'):
1757 1755 response = chr(self.stdscr.getch())
1758 1756 except ValueError:
1759 1757 response = None
1760 1758
1761 1759 return response
1762 1760
1763 1761 def reviewcommit(self):
1764 1762 """ask for 'y' to be pressed to confirm selected. return True if
1765 1763 confirmed."""
1766 1764 confirmtext = _(
1767 1765 b"""If you answer yes to the following, your currently chosen patch chunks
1768 1766 will be loaded into an editor. To modify the patch, make the changes in your
1769 1767 editor and save. To accept the current patch as-is, close the editor without
1770 1768 saving.
1771 1769
1772 1770 note: don't add/remove lines unless you also modify the range information.
1773 1771 failing to follow this rule will result in the commit aborting.
1774 1772
1775 1773 are you sure you want to review/edit and confirm the selected changes [yn]?
1776 1774 """
1777 1775 )
1778 1776 with self.ui.timeblockedsection(b'crecord'):
1779 1777 response = self.confirmationwindow(confirmtext)
1780 1778 if response is None:
1781 1779 response = "n"
1782 1780 if response.lower().startswith("y"):
1783 1781 return True
1784 1782 else:
1785 1783 return False
1786 1784
1787 1785 def recenterdisplayedarea(self):
1788 1786 """
1789 1787 once we scrolled with pg up pg down we can be pointing outside of the
1790 1788 display zone. we print the patch with towin=False to compute the
1791 1789 location of the selected item even though it is outside of the displayed
1792 1790 zone and then update the scroll.
1793 1791 """
1794 1792 self.printitem(towin=False)
1795 1793 self.updatescroll()
1796 1794
1797 1795 def toggleedit(self, item=None, test=False):
1798 1796 """
1799 1797 edit the currently selected chunk
1800 1798 """
1801 1799
1802 1800 def updateui(self):
1803 1801 self.numpadlines = self.getnumlinesdisplayed(ignorefolding=True) + 1
1804 1802 self.chunkpad = curses.newpad(self.numpadlines, self.xscreensize)
1805 1803 self.updatescroll()
1806 1804 self.stdscr.refresh()
1807 1805 self.statuswin.refresh()
1808 1806 self.stdscr.keypad(1)
1809 1807
1810 1808 def editpatchwitheditor(self, chunk):
1811 1809 if chunk is None:
1812 1810 self.ui.write(_(b'cannot edit patch for whole file'))
1813 1811 self.ui.write(b"\n")
1814 1812 return None
1815 1813 if chunk.header.binary():
1816 1814 self.ui.write(_(b'cannot edit patch for binary file'))
1817 1815 self.ui.write(b"\n")
1818 1816 return None
1819 1817
1820 1818 # write the initial patch
1821 1819 patch = stringio()
1822 1820 patch.write(diffhelptext + hunkhelptext)
1823 1821 chunk.header.write(patch)
1824 1822 chunk.write(patch)
1825 1823
1826 1824 # start the editor and wait for it to complete
1827 1825 try:
1828 1826 patch = self.ui.edit(patch.getvalue(), b"", action=b"diff")
1829 1827 except error.Abort as exc:
1830 1828 self.errorstr = exc.message
1831 1829 return None
1832 1830 finally:
1833 1831 self.stdscr.clear()
1834 1832 self.stdscr.refresh()
1835 1833
1836 1834 # remove comment lines
1837 1835 patch = [
1838 1836 line + b'\n'
1839 1837 for line in patch.splitlines()
1840 1838 if not line.startswith(b'#')
1841 1839 ]
1842 1840 return patchmod.parsepatch(patch)
1843 1841
1844 1842 if item is None:
1845 1843 item = self.currentselecteditem
1846 1844 if isinstance(item, uiheader):
1847 1845 return
1848 1846 if isinstance(item, uihunkline):
1849 1847 item = item.parentitem()
1850 1848 if not isinstance(item, uihunk):
1851 1849 return
1852 1850
1853 1851 # To go back to that hunk or its replacement at the end of the edit
1854 1852 itemindex = item.parentitem().hunks.index(item)
1855 1853
1856 1854 beforeadded, beforeremoved = item.added, item.removed
1857 1855 newpatches = editpatchwitheditor(self, item)
1858 1856 if newpatches is None:
1859 1857 if not test:
1860 1858 updateui(self)
1861 1859 return
1862 1860 header = item.header
1863 1861 editedhunkindex = header.hunks.index(item)
1864 1862 hunksbefore = header.hunks[:editedhunkindex]
1865 1863 hunksafter = header.hunks[editedhunkindex + 1 :]
1866 1864 newpatchheader = newpatches[0]
1867 1865 newhunks = [uihunk(h, header) for h in newpatchheader.hunks]
1868 1866 newadded = sum([h.added for h in newhunks])
1869 1867 newremoved = sum([h.removed for h in newhunks])
1870 1868 offset = (newadded - beforeadded) - (newremoved - beforeremoved)
1871 1869
1872 1870 for h in hunksafter:
1873 1871 h.toline += offset
1874 1872 for h in newhunks:
1875 1873 h.folded = False
1876 1874 header.hunks = hunksbefore + newhunks + hunksafter
1877 1875 if self.emptypatch():
1878 1876 header.hunks = hunksbefore + [item] + hunksafter
1879 1877 self.currentselecteditem = header
1880 1878 if len(header.hunks) > itemindex:
1881 1879 self.currentselecteditem = header.hunks[itemindex]
1882 1880
1883 1881 if not test:
1884 1882 updateui(self)
1885 1883
1886 1884 def emptypatch(self):
1887 1885 item = self.headerlist
1888 1886 if not item:
1889 1887 return True
1890 1888 for header in item:
1891 1889 if header.hunks:
1892 1890 return False
1893 1891 return True
1894 1892
1895 1893 def handlesearch(self):
1896 1894 win = curses.newwin(1, self.xscreensize, self.yscreensize - 1, 0)
1897 1895 win.echochar("/")
1898 1896
1899 1897 curses.echo()
1900 1898 curses.curs_set(1)
1901 1899 self.regex = win.getstr() or None
1902 1900 curses.noecho()
1903 1901 curses.curs_set(0)
1904 1902
1905 1903 if not self.showsearch(self.regex):
1906 1904 self.printstring(
1907 1905 win,
1908 1906 _(b"Pattern not found (press ENTER)"),
1909 1907 pairname=b"legend",
1910 1908 align=False,
1911 1909 )
1912 1910 while win.getkey() not in ["\n", "KEY_ENTER"]:
1913 1911 pass
1914 1912 del win
1915 1913
1916 1914 self.stdscr.clear()
1917 1915 self.stdscr.refresh()
1918 1916
1919 1917 def showsearch(self, regex, forward=True):
1920 1918 if not regex:
1921 1919 return
1922 1920
1923 1921 moveattr = 'nextitem' if forward else 'previtem'
1924 1922 currentitem = getattr(self.currentselecteditem, moveattr)(
1925 1923 skipfolded=False
1926 1924 )
1927 1925
1928 1926 matches = None
1929 1927 regex = re.compile(regex)
1930 1928 while currentitem:
1931 1929 matches = regex.search(currentitem.content)
1932 1930 if matches:
1933 1931 self.currentselecteditem = currentitem
1934 1932 break
1935 1933 currentitem = getattr(currentitem, moveattr)(skipfolded=False)
1936 1934
1937 1935 # Whatever is selected should now be visible
1938 1936 unfolded = self.currentselecteditem
1939 1937 while matches and unfolded:
1940 1938 unfolded.folded = False
1941 1939 unfolded = unfolded.parentitem()
1942 1940
1943 1941 return matches
1944 1942
1945 1943 def searchdirection(self, failuremsg, forward=True):
1946 1944 if not self.regex:
1947 1945 return
1948 1946
1949 1947 if not self.showsearch(self.regex, forward=forward):
1950 1948 win = curses.newwin(1, self.xscreensize, self.yscreensize - 1, 0)
1951 1949 self.printstring(win, failuremsg, pairname=b"legend", align=False)
1952 1950 while win.getkey() not in ["\n", "KEY_ENTER"]:
1953 1951 pass
1954 1952 del win
1955 1953
1956 1954 self.stdscr.clear()
1957 1955 self.stdscr.refresh()
1958 1956
1959 1957 def handlenextsearch(self):
1960 1958 self.searchdirection(
1961 1959 _(b"Next pattern not found (press ENTER)"), forward=True
1962 1960 )
1963 1961
1964 1962 def handleprevsearch(self):
1965 1963 self.searchdirection(
1966 1964 _(b"Previous pattern not found (press ENTER)"), forward=False
1967 1965 )
1968 1966
1969 1967 def handlekeypressed(self, keypressed, test=False):
1970 1968 """
1971 1969 Perform actions based on pressed keys.
1972 1970
1973 1971 Return true to exit the main loop.
1974 1972 """
1975 1973 if keypressed in ["k", "KEY_UP"]:
1976 1974 self.uparrowevent()
1977 1975 elif keypressed in ["K", "KEY_PPAGE"]:
1978 1976 self.uparrowshiftevent()
1979 1977 elif keypressed in ["j", "KEY_DOWN"]:
1980 1978 self.downarrowevent()
1981 1979 elif keypressed in ["J", "KEY_NPAGE"]:
1982 1980 self.downarrowshiftevent()
1983 1981 elif keypressed in ["l", "KEY_RIGHT"]:
1984 1982 self.rightarrowevent()
1985 1983 elif keypressed in ["h", "KEY_LEFT"]:
1986 1984 self.leftarrowevent()
1987 1985 elif keypressed in ["H", "KEY_SLEFT"]:
1988 1986 self.leftarrowshiftevent()
1989 1987 elif keypressed in ["q"]:
1990 1988 raise error.CanceledError(_(b'user quit'))
1991 1989 elif keypressed in ['a']:
1992 1990 self.flipselections()
1993 1991 elif keypressed in ["c"]:
1994 1992 return True
1995 1993 elif keypressed in ["r"]:
1996 1994 if self.reviewcommit():
1997 1995 self.opts[b'review'] = True
1998 1996 return True
1999 1997 elif test and keypressed in ["R"]:
2000 1998 self.opts[b'review'] = True
2001 1999 return True
2002 2000 elif keypressed in [" ", "x"]:
2003 2001 self.toggleapply()
2004 2002 elif keypressed in ["\n", "KEY_ENTER"]:
2005 2003 self.toggleapply()
2006 2004 self.nextsametype(test=test)
2007 2005 elif keypressed in ["X"]:
2008 2006 self.toggleallbetween()
2009 2007 elif keypressed in ["A"]:
2010 2008 self.toggleall()
2011 2009 elif keypressed in ["e"]:
2012 2010 self.toggleedit(test=test)
2013 2011 elif keypressed in ["f"]:
2014 2012 self.togglefolded()
2015 2013 elif keypressed in ["F"]:
2016 2014 self.togglefolded(foldparent=True)
2017 2015 elif keypressed in ["m"]:
2018 2016 self.commitMessageWindow()
2019 2017 elif keypressed in ["/"]:
2020 2018 self.handlesearch()
2021 2019 elif keypressed in ["n"]:
2022 2020 self.handlenextsearch()
2023 2021 elif keypressed in ["N"]:
2024 2022 self.handleprevsearch()
2025 2023 elif keypressed in ["g", "KEY_HOME"]:
2026 2024 self.handlefirstlineevent()
2027 2025 elif keypressed in ["G", "KEY_END"]:
2028 2026 self.handlelastlineevent()
2029 2027 elif keypressed in ["?"]:
2030 2028 self.helpwindow()
2031 2029 self.stdscr.clear()
2032 2030 self.stdscr.refresh()
2033 2031 elif keypressed in [curses.ascii.ctrl("L")]:
2034 2032 # scroll the current line to the top of the screen, and redraw
2035 2033 # everything
2036 2034 self.scrolllines(self.selecteditemstartline)
2037 2035 self.stdscr.clear()
2038 2036 self.stdscr.refresh()
2039 2037
2040 2038 def main(self, stdscr):
2041 2039 """
2042 2040 method to be wrapped by curses.wrapper() for selecting chunks.
2043 2041 """
2044 2042
2045 2043 origsigwinch = sentinel = object()
2046 2044 if hasattr(signal, 'SIGWINCH'):
2047 2045 origsigwinch = signal.signal(signal.SIGWINCH, self.sigwinchhandler)
2048 2046 try:
2049 2047 return self._main(stdscr)
2050 2048 finally:
2051 2049 if origsigwinch is not sentinel:
2052 2050 signal.signal(signal.SIGWINCH, origsigwinch)
2053 2051
2054 2052 def _main(self, stdscr):
2055 2053 self.stdscr = stdscr
2056 2054 # error during initialization, cannot be printed in the curses
2057 2055 # interface, it should be printed by the calling code
2058 2056 self.initexc = None
2059 2057 self.yscreensize, self.xscreensize = self.stdscr.getmaxyx()
2060 2058
2061 2059 curses.start_color()
2062 2060 try:
2063 2061 curses.use_default_colors()
2064 2062 except curses.error:
2065 2063 self.usecolor = False
2066 2064
2067 2065 # In some situations we may have some cruft left on the "alternate
2068 2066 # screen" from another program (or previous iterations of ourself), and
2069 2067 # we won't clear it if the scroll region is small enough to comfortably
2070 2068 # fit on the terminal.
2071 2069 self.stdscr.clear()
2072 2070
2073 2071 # don't display the cursor
2074 2072 try:
2075 2073 curses.curs_set(0)
2076 2074 except curses.error:
2077 2075 pass
2078 2076
2079 2077 # available colors: black, blue, cyan, green, magenta, white, yellow
2080 2078 # init_pair(color_id, foreground_color, background_color)
2081 2079 self.initcolorpair(None, None, name=b"normal")
2082 2080 self.initcolorpair(
2083 2081 curses.COLOR_WHITE, curses.COLOR_MAGENTA, name=b"selected"
2084 2082 )
2085 2083 self.initcolorpair(curses.COLOR_RED, None, name=b"deletion")
2086 2084 self.initcolorpair(curses.COLOR_GREEN, None, name=b"addition")
2087 2085 self.initcolorpair(
2088 2086 curses.COLOR_WHITE, curses.COLOR_BLUE, name=b"legend"
2089 2087 )
2090 2088 # newwin([height, width,] begin_y, begin_x)
2091 2089 self.statuswin = curses.newwin(self.numstatuslines, 0, 0, 0)
2092 2090 self.statuswin.keypad(True) # interpret arrow-key, etc. esc sequences
2093 2091
2094 2092 # figure out how much space to allocate for the chunk-pad which is
2095 2093 # used for displaying the patch
2096 2094
2097 2095 # stupid hack to prevent getnumlinesdisplayed from failing
2098 2096 self.chunkpad = curses.newpad(1, self.xscreensize)
2099 2097
2100 2098 # add 1 so to account for last line text reaching end of line
2101 2099 self.numpadlines = self.getnumlinesdisplayed(ignorefolding=True) + 1
2102 2100
2103 2101 try:
2104 2102 self.chunkpad = curses.newpad(self.numpadlines, self.xscreensize)
2105 2103 except curses.error:
2106 2104 self.initexc = fallbackerror(
2107 2105 _(b'this diff is too large to be displayed')
2108 2106 )
2109 2107 return
2110 2108 # initialize selecteditemendline (initial start-line is 0)
2111 2109 self.selecteditemendline = self.getnumlinesdisplayed(
2112 2110 self.currentselecteditem, recursechildren=False
2113 2111 )
2114 2112
2115 2113 while True:
2116 2114 self.updatescreen()
2117 2115 try:
2118 2116 with self.ui.timeblockedsection(b'crecord'):
2119 2117 keypressed = self.statuswin.getkey()
2120 2118 if self.errorstr is not None:
2121 2119 self.errorstr = None
2122 2120 continue
2123 2121 except curses.error:
2124 2122 keypressed = b"foobar"
2125 2123 if self.handlekeypressed(keypressed):
2126 2124 break
2127 2125
2128 2126 if self.commenttext != b"":
2129 2127 whitespaceremoved = re.sub(
2130 2128 br"(?m)^\s.*(\n|$)", b"", self.commenttext
2131 2129 )
2132 2130 if whitespaceremoved != b"":
2133 2131 self.opts[b'message'] = self.commenttext
@@ -1,4034 +1,4038 b''
1 1 # localrepo.py - read/write repository class for mercurial
2 2 # coding: utf-8
3 3 #
4 4 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9
10 10 import functools
11 11 import os
12 12 import random
13 13 import re
14 14 import sys
15 15 import time
16 16 import weakref
17 17
18 18 from concurrent import futures
19 19 from typing import (
20 20 Optional,
21 21 )
22 22
23 23 from .i18n import _
24 24 from .node import (
25 25 bin,
26 26 hex,
27 27 nullrev,
28 28 sha1nodeconstants,
29 29 short,
30 30 )
31 31 from . import (
32 32 bookmarks,
33 33 branchmap,
34 34 bundle2,
35 35 bundlecaches,
36 36 changegroup,
37 37 color,
38 38 commit,
39 39 context,
40 40 dirstate,
41 41 discovery,
42 42 encoding,
43 43 error,
44 44 exchange,
45 45 extensions,
46 46 filelog,
47 47 hook,
48 48 lock as lockmod,
49 49 match as matchmod,
50 50 mergestate as mergestatemod,
51 51 mergeutil,
52 52 namespaces,
53 53 narrowspec,
54 54 obsolete,
55 55 pathutil,
56 56 phases,
57 57 policy,
58 58 pushkey,
59 59 pycompat,
60 60 rcutil,
61 61 repoview,
62 62 requirements as requirementsmod,
63 63 revlog,
64 64 revset,
65 65 revsetlang,
66 66 scmutil,
67 67 sparse,
68 68 store as storemod,
69 69 subrepoutil,
70 70 tags as tagsmod,
71 71 transaction,
72 72 txnutil,
73 73 util,
74 74 vfs as vfsmod,
75 75 wireprototypes,
76 76 )
77 77
78 78 from .interfaces import (
79 79 repository,
80 80 util as interfaceutil,
81 81 )
82 82
83 83 from .utils import (
84 84 hashutil,
85 85 procutil,
86 86 stringutil,
87 87 urlutil,
88 88 )
89 89
90 90 from .revlogutils import (
91 91 concurrency_checker as revlogchecker,
92 92 constants as revlogconst,
93 93 sidedata as sidedatamod,
94 94 )
95 95
96 96 release = lockmod.release
97 97 urlerr = util.urlerr
98 98 urlreq = util.urlreq
99 99
100 100 RE_SKIP_DIRSTATE_ROLLBACK = re.compile(
101 101 b"^((dirstate|narrowspec.dirstate).*|branch$)"
102 102 )
103 103
104 104 # set of (path, vfs-location) tuples. vfs-location is:
105 105 # - 'plain for vfs relative paths
106 106 # - '' for svfs relative paths
107 107 _cachedfiles = set()
108 108
109 109
110 110 class _basefilecache(scmutil.filecache):
111 111 """All filecache usage on repo are done for logic that should be unfiltered"""
112 112
113 113 def __get__(self, repo, type=None):
114 114 if repo is None:
115 115 return self
116 116 # proxy to unfiltered __dict__ since filtered repo has no entry
117 117 unfi = repo.unfiltered()
118 118 try:
119 119 return unfi.__dict__[self.sname]
120 120 except KeyError:
121 121 pass
122 122 return super(_basefilecache, self).__get__(unfi, type)
123 123
124 124 def set(self, repo, value):
125 125 return super(_basefilecache, self).set(repo.unfiltered(), value)
126 126
127 127
128 128 class repofilecache(_basefilecache):
129 129 """filecache for files in .hg but outside of .hg/store"""
130 130
131 131 def __init__(self, *paths):
132 132 super(repofilecache, self).__init__(*paths)
133 133 for path in paths:
134 134 _cachedfiles.add((path, b'plain'))
135 135
136 136 def join(self, obj, fname):
137 137 return obj.vfs.join(fname)
138 138
139 139
140 140 class storecache(_basefilecache):
141 141 """filecache for files in the store"""
142 142
143 143 def __init__(self, *paths):
144 144 super(storecache, self).__init__(*paths)
145 145 for path in paths:
146 146 _cachedfiles.add((path, b''))
147 147
148 148 def join(self, obj, fname):
149 149 return obj.sjoin(fname)
150 150
151 151
152 152 class changelogcache(storecache):
153 153 """filecache for the changelog"""
154 154
155 155 def __init__(self):
156 156 super(changelogcache, self).__init__()
157 157 _cachedfiles.add((b'00changelog.i', b''))
158 158 _cachedfiles.add((b'00changelog.n', b''))
159 159
160 160 def tracked_paths(self, obj):
161 161 paths = [self.join(obj, b'00changelog.i')]
162 162 if obj.store.opener.options.get(b'persistent-nodemap', False):
163 163 paths.append(self.join(obj, b'00changelog.n'))
164 164 return paths
165 165
166 166
167 167 class manifestlogcache(storecache):
168 168 """filecache for the manifestlog"""
169 169
170 170 def __init__(self):
171 171 super(manifestlogcache, self).__init__()
172 172 _cachedfiles.add((b'00manifest.i', b''))
173 173 _cachedfiles.add((b'00manifest.n', b''))
174 174
175 175 def tracked_paths(self, obj):
176 176 paths = [self.join(obj, b'00manifest.i')]
177 177 if obj.store.opener.options.get(b'persistent-nodemap', False):
178 178 paths.append(self.join(obj, b'00manifest.n'))
179 179 return paths
180 180
181 181
182 182 class mixedrepostorecache(_basefilecache):
183 183 """filecache for a mix files in .hg/store and outside"""
184 184
185 185 def __init__(self, *pathsandlocations):
186 186 # scmutil.filecache only uses the path for passing back into our
187 187 # join(), so we can safely pass a list of paths and locations
188 188 super(mixedrepostorecache, self).__init__(*pathsandlocations)
189 189 _cachedfiles.update(pathsandlocations)
190 190
191 191 def join(self, obj, fnameandlocation):
192 192 fname, location = fnameandlocation
193 193 if location == b'plain':
194 194 return obj.vfs.join(fname)
195 195 else:
196 196 if location != b'':
197 197 raise error.ProgrammingError(
198 198 b'unexpected location: %s' % location
199 199 )
200 200 return obj.sjoin(fname)
201 201
202 202
203 203 def isfilecached(repo, name):
204 204 """check if a repo has already cached "name" filecache-ed property
205 205
206 206 This returns (cachedobj-or-None, iscached) tuple.
207 207 """
208 208 cacheentry = repo.unfiltered()._filecache.get(name, None)
209 209 if not cacheentry:
210 210 return None, False
211 211 return cacheentry.obj, True
212 212
213 213
214 214 class unfilteredpropertycache(util.propertycache):
215 215 """propertycache that apply to unfiltered repo only"""
216 216
217 217 def __get__(self, repo, type=None):
218 218 unfi = repo.unfiltered()
219 219 if unfi is repo:
220 220 return super(unfilteredpropertycache, self).__get__(unfi)
221 221 return getattr(unfi, self.name)
222 222
223 223
224 224 class filteredpropertycache(util.propertycache):
225 225 """propertycache that must take filtering in account"""
226 226
227 227 def cachevalue(self, obj, value):
228 228 object.__setattr__(obj, self.name, value)
229 229
230 230
231 231 def hasunfilteredcache(repo, name):
232 232 """check if a repo has an unfilteredpropertycache value for <name>"""
233 233 return name in vars(repo.unfiltered())
234 234
235 235
236 236 def unfilteredmethod(orig):
237 237 """decorate method that always need to be run on unfiltered version"""
238 238
239 239 @functools.wraps(orig)
240 240 def wrapper(repo, *args, **kwargs):
241 241 return orig(repo.unfiltered(), *args, **kwargs)
242 242
243 243 return wrapper
244 244
245 245
246 246 moderncaps = {
247 247 b'lookup',
248 248 b'branchmap',
249 249 b'pushkey',
250 250 b'known',
251 251 b'getbundle',
252 252 b'unbundle',
253 253 }
254 254 legacycaps = moderncaps.union({b'changegroupsubset'})
255 255
256 256
257 257 @interfaceutil.implementer(repository.ipeercommandexecutor)
258 258 class localcommandexecutor:
259 259 def __init__(self, peer):
260 260 self._peer = peer
261 261 self._sent = False
262 262 self._closed = False
263 263
264 264 def __enter__(self):
265 265 return self
266 266
267 267 def __exit__(self, exctype, excvalue, exctb):
268 268 self.close()
269 269
270 270 def callcommand(self, command, args):
271 271 if self._sent:
272 272 raise error.ProgrammingError(
273 273 b'callcommand() cannot be used after sendcommands()'
274 274 )
275 275
276 276 if self._closed:
277 277 raise error.ProgrammingError(
278 278 b'callcommand() cannot be used after close()'
279 279 )
280 280
281 281 # We don't need to support anything fancy. Just call the named
282 282 # method on the peer and return a resolved future.
283 283 fn = getattr(self._peer, pycompat.sysstr(command))
284 284
285 285 f = futures.Future()
286 286
287 287 try:
288 288 result = fn(**pycompat.strkwargs(args))
289 289 except Exception:
290 290 pycompat.future_set_exception_info(f, sys.exc_info()[1:])
291 291 else:
292 292 f.set_result(result)
293 293
294 294 return f
295 295
296 296 def sendcommands(self):
297 297 self._sent = True
298 298
299 299 def close(self):
300 300 self._closed = True
301 301
302 302
303 303 @interfaceutil.implementer(repository.ipeercommands)
304 304 class localpeer(repository.peer):
305 305 '''peer for a local repo; reflects only the most recent API'''
306 306
307 307 def __init__(self, repo, caps=None, path=None, remotehidden=False):
308 308 super(localpeer, self).__init__(
309 309 repo.ui, path=path, remotehidden=remotehidden
310 310 )
311 311
312 312 if caps is None:
313 313 caps = moderncaps.copy()
314 314 if remotehidden:
315 315 self._repo = repo.filtered(b'served.hidden')
316 316 else:
317 317 self._repo = repo.filtered(b'served')
318 318 if repo._wanted_sidedata:
319 319 formatted = bundle2.format_remote_wanted_sidedata(repo)
320 320 caps.add(b'exp-wanted-sidedata=' + formatted)
321 321
322 322 self._caps = repo._restrictcapabilities(caps)
323 323
324 324 # Begin of _basepeer interface.
325 325
326 326 def url(self):
327 327 return self._repo.url()
328 328
329 329 def local(self):
330 330 return self._repo
331 331
332 332 def canpush(self):
333 333 return True
334 334
335 335 def close(self):
336 336 self._repo.close()
337 337
338 338 # End of _basepeer interface.
339 339
340 340 # Begin of _basewirecommands interface.
341 341
342 342 def branchmap(self):
343 343 return self._repo.branchmap()
344 344
345 345 def capabilities(self):
346 346 return self._caps
347 347
348 348 def get_cached_bundle_inline(self, path):
349 349 # not needed with local peer
350 350 raise NotImplementedError
351 351
352 352 def clonebundles(self):
353 353 return bundlecaches.get_manifest(self._repo)
354 354
355 355 def debugwireargs(self, one, two, three=None, four=None, five=None):
356 356 """Used to test argument passing over the wire"""
357 357 return b"%s %s %s %s %s" % (
358 358 one,
359 359 two,
360 360 pycompat.bytestr(three),
361 361 pycompat.bytestr(four),
362 362 pycompat.bytestr(five),
363 363 )
364 364
365 365 def getbundle(
366 366 self,
367 367 source,
368 368 heads=None,
369 369 common=None,
370 370 bundlecaps=None,
371 371 remote_sidedata=None,
372 372 **kwargs,
373 373 ):
374 374 chunks = exchange.getbundlechunks(
375 375 self._repo,
376 376 source,
377 377 heads=heads,
378 378 common=common,
379 379 bundlecaps=bundlecaps,
380 380 remote_sidedata=remote_sidedata,
381 381 **kwargs,
382 382 )[1]
383 383 cb = util.chunkbuffer(chunks)
384 384
385 385 if exchange.bundle2requested(bundlecaps):
386 386 # When requesting a bundle2, getbundle returns a stream to make the
387 387 # wire level function happier. We need to build a proper object
388 388 # from it in local peer.
389 389 return bundle2.getunbundler(self.ui, cb)
390 390 else:
391 391 return changegroup.getunbundler(b'01', cb, None)
392 392
393 393 def heads(self):
394 394 return self._repo.heads()
395 395
396 396 def known(self, nodes):
397 397 return self._repo.known(nodes)
398 398
399 399 def listkeys(self, namespace):
400 400 return self._repo.listkeys(namespace)
401 401
402 402 def lookup(self, key):
403 403 return self._repo.lookup(key)
404 404
405 405 def pushkey(self, namespace, key, old, new):
406 406 return self._repo.pushkey(namespace, key, old, new)
407 407
408 408 def stream_out(self):
409 409 raise error.Abort(_(b'cannot perform stream clone against local peer'))
410 410
411 411 def unbundle(self, bundle, heads, url):
412 412 """apply a bundle on a repo
413 413
414 414 This function handles the repo locking itself."""
415 415 try:
416 416 try:
417 417 bundle = exchange.readbundle(self.ui, bundle, None)
418 418 ret = exchange.unbundle(self._repo, bundle, heads, b'push', url)
419 419 if hasattr(ret, 'getchunks'):
420 420 # This is a bundle20 object, turn it into an unbundler.
421 421 # This little dance should be dropped eventually when the
422 422 # API is finally improved.
423 423 stream = util.chunkbuffer(ret.getchunks())
424 424 ret = bundle2.getunbundler(self.ui, stream)
425 425 return ret
426 426 except Exception as exc:
427 427 # If the exception contains output salvaged from a bundle2
428 428 # reply, we need to make sure it is printed before continuing
429 429 # to fail. So we build a bundle2 with such output and consume
430 430 # it directly.
431 431 #
432 432 # This is not very elegant but allows a "simple" solution for
433 433 # issue4594
434 434 output = getattr(exc, '_bundle2salvagedoutput', ())
435 435 if output:
436 436 bundler = bundle2.bundle20(self._repo.ui)
437 437 for out in output:
438 438 bundler.addpart(out)
439 439 stream = util.chunkbuffer(bundler.getchunks())
440 440 b = bundle2.getunbundler(self.ui, stream)
441 441 bundle2.processbundle(self._repo, b)
442 442 raise
443 443 except error.PushRaced as exc:
444 444 raise error.ResponseError(
445 445 _(b'push failed:'), stringutil.forcebytestr(exc)
446 446 )
447 447
448 448 # End of _basewirecommands interface.
449 449
450 450 # Begin of peer interface.
451 451
452 452 def commandexecutor(self):
453 453 return localcommandexecutor(self)
454 454
455 455 # End of peer interface.
456 456
457 457
458 458 @interfaceutil.implementer(repository.ipeerlegacycommands)
459 459 class locallegacypeer(localpeer):
460 460 """peer extension which implements legacy methods too; used for tests with
461 461 restricted capabilities"""
462 462
463 463 def __init__(self, repo, path=None, remotehidden=False):
464 464 super(locallegacypeer, self).__init__(
465 465 repo, caps=legacycaps, path=path, remotehidden=remotehidden
466 466 )
467 467
468 468 # Begin of baselegacywirecommands interface.
469 469
470 470 def between(self, pairs):
471 471 return self._repo.between(pairs)
472 472
473 473 def branches(self, nodes):
474 474 return self._repo.branches(nodes)
475 475
476 476 def changegroup(self, nodes, source):
477 477 outgoing = discovery.outgoing(
478 478 self._repo, missingroots=nodes, ancestorsof=self._repo.heads()
479 479 )
480 480 return changegroup.makechangegroup(self._repo, outgoing, b'01', source)
481 481
482 482 def changegroupsubset(self, bases, heads, source):
483 483 outgoing = discovery.outgoing(
484 484 self._repo, missingroots=bases, ancestorsof=heads
485 485 )
486 486 return changegroup.makechangegroup(self._repo, outgoing, b'01', source)
487 487
488 488 # End of baselegacywirecommands interface.
489 489
490 490
491 491 # Functions receiving (ui, features) that extensions can register to impact
492 492 # the ability to load repositories with custom requirements. Only
493 493 # functions defined in loaded extensions are called.
494 494 #
495 495 # The function receives a set of requirement strings that the repository
496 496 # is capable of opening. Functions will typically add elements to the
497 497 # set to reflect that the extension knows how to handle that requirements.
498 498 featuresetupfuncs = set()
499 499
500 500
501 501 def _getsharedvfs(hgvfs, requirements):
502 502 """returns the vfs object pointing to root of shared source
503 503 repo for a shared repository
504 504
505 505 hgvfs is vfs pointing at .hg/ of current repo (shared one)
506 506 requirements is a set of requirements of current repo (shared one)
507 507 """
508 508 # The ``shared`` or ``relshared`` requirements indicate the
509 509 # store lives in the path contained in the ``.hg/sharedpath`` file.
510 510 # This is an absolute path for ``shared`` and relative to
511 511 # ``.hg/`` for ``relshared``.
512 512 sharedpath = hgvfs.read(b'sharedpath').rstrip(b'\n')
513 513 if requirementsmod.RELATIVE_SHARED_REQUIREMENT in requirements:
514 514 sharedpath = util.normpath(hgvfs.join(sharedpath))
515 515
516 516 sharedvfs = vfsmod.vfs(sharedpath, realpath=True)
517 517
518 518 if not sharedvfs.exists():
519 519 raise error.RepoError(
520 520 _(b'.hg/sharedpath points to nonexistent directory %s')
521 521 % sharedvfs.base
522 522 )
523 523 return sharedvfs
524 524
525 525
526 526 def _readrequires(vfs, allowmissing):
527 527 """reads the require file present at root of this vfs
528 528 and return a set of requirements
529 529
530 530 If allowmissing is True, we suppress FileNotFoundError if raised"""
531 531 # requires file contains a newline-delimited list of
532 532 # features/capabilities the opener (us) must have in order to use
533 533 # the repository. This file was introduced in Mercurial 0.9.2,
534 534 # which means very old repositories may not have one. We assume
535 535 # a missing file translates to no requirements.
536 536 read = vfs.tryread if allowmissing else vfs.read
537 537 return set(read(b'requires').splitlines())
538 538
539 539
540 540 def makelocalrepository(baseui, path: bytes, intents=None):
541 541 """Create a local repository object.
542 542
543 543 Given arguments needed to construct a local repository, this function
544 544 performs various early repository loading functionality (such as
545 545 reading the ``.hg/requires`` and ``.hg/hgrc`` files), validates that
546 546 the repository can be opened, derives a type suitable for representing
547 547 that repository, and returns an instance of it.
548 548
549 549 The returned object conforms to the ``repository.completelocalrepository``
550 550 interface.
551 551
552 552 The repository type is derived by calling a series of factory functions
553 553 for each aspect/interface of the final repository. These are defined by
554 554 ``REPO_INTERFACES``.
555 555
556 556 Each factory function is called to produce a type implementing a specific
557 557 interface. The cumulative list of returned types will be combined into a
558 558 new type and that type will be instantiated to represent the local
559 559 repository.
560 560
561 561 The factory functions each receive various state that may be consulted
562 562 as part of deriving a type.
563 563
564 564 Extensions should wrap these factory functions to customize repository type
565 565 creation. Note that an extension's wrapped function may be called even if
566 566 that extension is not loaded for the repo being constructed. Extensions
567 567 should check if their ``__name__`` appears in the
568 568 ``extensionmodulenames`` set passed to the factory function and no-op if
569 569 not.
570 570 """
571 571 ui = baseui.copy()
572 572 # Prevent copying repo configuration.
573 573 ui.copy = baseui.copy
574 574
575 575 # Working directory VFS rooted at repository root.
576 576 wdirvfs = vfsmod.vfs(path, expandpath=True, realpath=True)
577 577
578 578 # Main VFS for .hg/ directory.
579 579 hgpath = wdirvfs.join(b'.hg')
580 580 hgvfs = vfsmod.vfs(hgpath, cacheaudited=True)
581 581 # Whether this repository is shared one or not
582 582 shared = False
583 583 # If this repository is shared, vfs pointing to shared repo
584 584 sharedvfs = None
585 585
586 586 # The .hg/ path should exist and should be a directory. All other
587 587 # cases are errors.
588 588 if not hgvfs.isdir():
589 589 try:
590 590 hgvfs.stat()
591 591 except FileNotFoundError:
592 592 pass
593 593 except ValueError as e:
594 594 # Can be raised on Python 3.8 when path is invalid.
595 595 raise error.Abort(
596 596 _(b'invalid path %s: %s') % (path, stringutil.forcebytestr(e))
597 597 )
598 598
599 599 raise error.RepoError(_(b'repository %s not found') % path)
600 600
601 601 requirements = _readrequires(hgvfs, True)
602 602 shared = (
603 603 requirementsmod.SHARED_REQUIREMENT in requirements
604 604 or requirementsmod.RELATIVE_SHARED_REQUIREMENT in requirements
605 605 )
606 606 storevfs = None
607 607 if shared:
608 608 # This is a shared repo
609 609 sharedvfs = _getsharedvfs(hgvfs, requirements)
610 610 storevfs = vfsmod.vfs(sharedvfs.join(b'store'))
611 611 else:
612 612 storevfs = vfsmod.vfs(hgvfs.join(b'store'))
613 613
614 614 # if .hg/requires contains the sharesafe requirement, it means
615 615 # there exists a `.hg/store/requires` too and we should read it
616 616 # NOTE: presence of SHARESAFE_REQUIREMENT imply that store requirement
617 617 # is present. We never write SHARESAFE_REQUIREMENT for a repo if store
618 618 # is not present, refer checkrequirementscompat() for that
619 619 #
620 620 # However, if SHARESAFE_REQUIREMENT is not present, it means that the
621 621 # repository was shared the old way. We check the share source .hg/requires
622 622 # for SHARESAFE_REQUIREMENT to detect whether the current repository needs
623 623 # to be reshared
624 624 hint = _(b"see `hg help config.format.use-share-safe` for more information")
625 625 if requirementsmod.SHARESAFE_REQUIREMENT in requirements:
626 626 if (
627 627 shared
628 628 and requirementsmod.SHARESAFE_REQUIREMENT
629 629 not in _readrequires(sharedvfs, True)
630 630 ):
631 631 mismatch_warn = ui.configbool(
632 632 b'share', b'safe-mismatch.source-not-safe.warn'
633 633 )
634 634 mismatch_config = ui.config(
635 635 b'share', b'safe-mismatch.source-not-safe'
636 636 )
637 637 mismatch_verbose_upgrade = ui.configbool(
638 638 b'share', b'safe-mismatch.source-not-safe:verbose-upgrade'
639 639 )
640 640 if mismatch_config in (
641 641 b'downgrade-allow',
642 642 b'allow',
643 643 b'downgrade-abort',
644 644 ):
645 645 # prevent cyclic import localrepo -> upgrade -> localrepo
646 646 from . import upgrade
647 647
648 648 upgrade.downgrade_share_to_non_safe(
649 649 ui,
650 650 hgvfs,
651 651 sharedvfs,
652 652 requirements,
653 653 mismatch_config,
654 654 mismatch_warn,
655 655 mismatch_verbose_upgrade,
656 656 )
657 657 elif mismatch_config == b'abort':
658 658 raise error.Abort(
659 659 _(b"share source does not support share-safe requirement"),
660 660 hint=hint,
661 661 )
662 662 else:
663 663 raise error.Abort(
664 664 _(
665 665 b"share-safe mismatch with source.\nUnrecognized"
666 666 b" value '%s' of `share.safe-mismatch.source-not-safe`"
667 667 b" set."
668 668 )
669 669 % mismatch_config,
670 670 hint=hint,
671 671 )
672 672 else:
673 673 requirements |= _readrequires(storevfs, False)
674 674 elif shared:
675 675 sourcerequires = _readrequires(sharedvfs, False)
676 676 if requirementsmod.SHARESAFE_REQUIREMENT in sourcerequires:
677 677 mismatch_config = ui.config(b'share', b'safe-mismatch.source-safe')
678 678 mismatch_warn = ui.configbool(
679 679 b'share', b'safe-mismatch.source-safe.warn'
680 680 )
681 681 mismatch_verbose_upgrade = ui.configbool(
682 682 b'share', b'safe-mismatch.source-safe:verbose-upgrade'
683 683 )
684 684 if mismatch_config in (
685 685 b'upgrade-allow',
686 686 b'allow',
687 687 b'upgrade-abort',
688 688 ):
689 689 # prevent cyclic import localrepo -> upgrade -> localrepo
690 690 from . import upgrade
691 691
692 692 upgrade.upgrade_share_to_safe(
693 693 ui,
694 694 hgvfs,
695 695 storevfs,
696 696 requirements,
697 697 mismatch_config,
698 698 mismatch_warn,
699 699 mismatch_verbose_upgrade,
700 700 )
701 701 elif mismatch_config == b'abort':
702 702 raise error.Abort(
703 703 _(
704 704 b'version mismatch: source uses share-safe'
705 705 b' functionality while the current share does not'
706 706 ),
707 707 hint=hint,
708 708 )
709 709 else:
710 710 raise error.Abort(
711 711 _(
712 712 b"share-safe mismatch with source.\nUnrecognized"
713 713 b" value '%s' of `share.safe-mismatch.source-safe` set."
714 714 )
715 715 % mismatch_config,
716 716 hint=hint,
717 717 )
718 718
719 719 # The .hg/hgrc file may load extensions or contain config options
720 720 # that influence repository construction. Attempt to load it and
721 721 # process any new extensions that it may have pulled in.
722 722 if loadhgrc(ui, wdirvfs, hgvfs, requirements, sharedvfs):
723 723 afterhgrcload(ui, wdirvfs, hgvfs, requirements)
724 724 extensions.loadall(ui)
725 725 extensions.populateui(ui)
726 726
727 727 # Set of module names of extensions loaded for this repository.
728 728 extensionmodulenames = {m.__name__ for n, m in extensions.extensions(ui)}
729 729
730 730 supportedrequirements = gathersupportedrequirements(ui)
731 731
732 732 # We first validate the requirements are known.
733 733 ensurerequirementsrecognized(requirements, supportedrequirements)
734 734
735 735 # Then we validate that the known set is reasonable to use together.
736 736 ensurerequirementscompatible(ui, requirements)
737 737
738 738 # TODO there are unhandled edge cases related to opening repositories with
739 739 # shared storage. If storage is shared, we should also test for requirements
740 740 # compatibility in the pointed-to repo. This entails loading the .hg/hgrc in
741 741 # that repo, as that repo may load extensions needed to open it. This is a
742 742 # bit complicated because we don't want the other hgrc to overwrite settings
743 743 # in this hgrc.
744 744 #
745 745 # This bug is somewhat mitigated by the fact that we copy the .hg/requires
746 746 # file when sharing repos. But if a requirement is added after the share is
747 747 # performed, thereby introducing a new requirement for the opener, we may
748 748 # will not see that and could encounter a run-time error interacting with
749 749 # that shared store since it has an unknown-to-us requirement.
750 750
751 751 # At this point, we know we should be capable of opening the repository.
752 752 # Now get on with doing that.
753 753
754 754 features = set()
755 755
756 756 # The "store" part of the repository holds versioned data. How it is
757 757 # accessed is determined by various requirements. If `shared` or
758 758 # `relshared` requirements are present, this indicates current repository
759 759 # is a share and store exists in path mentioned in `.hg/sharedpath`
760 760 if shared:
761 761 storebasepath = sharedvfs.base
762 762 cachepath = sharedvfs.join(b'cache')
763 763 features.add(repository.REPO_FEATURE_SHARED_STORAGE)
764 764 else:
765 765 storebasepath = hgvfs.base
766 766 cachepath = hgvfs.join(b'cache')
767 767 wcachepath = hgvfs.join(b'wcache')
768 768
769 769 # The store has changed over time and the exact layout is dictated by
770 770 # requirements. The store interface abstracts differences across all
771 771 # of them.
772 772 store = makestore(
773 773 requirements,
774 774 storebasepath,
775 775 lambda base: vfsmod.vfs(base, cacheaudited=True),
776 776 )
777 777 hgvfs.createmode = store.createmode
778 778
779 779 storevfs = store.vfs
780 780 storevfs.options = resolvestorevfsoptions(ui, requirements, features)
781 781
782 782 if (
783 783 requirementsmod.REVLOGV2_REQUIREMENT in requirements
784 784 or requirementsmod.CHANGELOGV2_REQUIREMENT in requirements
785 785 ):
786 786 features.add(repository.REPO_FEATURE_SIDE_DATA)
787 787 # the revlogv2 docket introduced race condition that we need to fix
788 788 features.discard(repository.REPO_FEATURE_STREAM_CLONE)
789 789
790 790 # The cache vfs is used to manage cache files.
791 791 cachevfs = vfsmod.vfs(cachepath, cacheaudited=True)
792 792 cachevfs.createmode = store.createmode
793 793 # The cache vfs is used to manage cache files related to the working copy
794 794 wcachevfs = vfsmod.vfs(wcachepath, cacheaudited=True)
795 795 wcachevfs.createmode = store.createmode
796 796
797 797 # Now resolve the type for the repository object. We do this by repeatedly
798 798 # calling a factory function to produces types for specific aspects of the
799 799 # repo's operation. The aggregate returned types are used as base classes
800 800 # for a dynamically-derived type, which will represent our new repository.
801 801
802 802 bases = []
803 803 extrastate = {}
804 804
805 805 for iface, fn in REPO_INTERFACES:
806 806 # We pass all potentially useful state to give extensions tons of
807 807 # flexibility.
808 808 typ = fn()(
809 809 ui=ui,
810 810 intents=intents,
811 811 requirements=requirements,
812 812 features=features,
813 813 wdirvfs=wdirvfs,
814 814 hgvfs=hgvfs,
815 815 store=store,
816 816 storevfs=storevfs,
817 817 storeoptions=storevfs.options,
818 818 cachevfs=cachevfs,
819 819 wcachevfs=wcachevfs,
820 820 extensionmodulenames=extensionmodulenames,
821 821 extrastate=extrastate,
822 822 baseclasses=bases,
823 823 )
824 824
825 825 if not isinstance(typ, type):
826 826 raise error.ProgrammingError(
827 827 b'unable to construct type for %s' % iface
828 828 )
829 829
830 830 bases.append(typ)
831 831
832 832 # type() allows you to use characters in type names that wouldn't be
833 833 # recognized as Python symbols in source code. We abuse that to add
834 834 # rich information about our constructed repo.
835 835 name = pycompat.sysstr(
836 836 b'derivedrepo:%s<%s>' % (wdirvfs.base, b','.join(sorted(requirements)))
837 837 )
838 838
839 839 cls = type(name, tuple(bases), {})
840 840
841 841 return cls(
842 842 baseui=baseui,
843 843 ui=ui,
844 844 origroot=path,
845 845 wdirvfs=wdirvfs,
846 846 hgvfs=hgvfs,
847 847 requirements=requirements,
848 848 supportedrequirements=supportedrequirements,
849 849 sharedpath=storebasepath,
850 850 store=store,
851 851 cachevfs=cachevfs,
852 852 wcachevfs=wcachevfs,
853 853 features=features,
854 854 intents=intents,
855 855 )
856 856
857 857
858 858 def loadhgrc(
859 859 ui,
860 860 wdirvfs: vfsmod.vfs,
861 861 hgvfs: vfsmod.vfs,
862 862 requirements,
863 863 sharedvfs: Optional[vfsmod.vfs] = None,
864 864 ):
865 865 """Load hgrc files/content into a ui instance.
866 866
867 867 This is called during repository opening to load any additional
868 868 config files or settings relevant to the current repository.
869 869
870 870 Returns a bool indicating whether any additional configs were loaded.
871 871
872 872 Extensions should monkeypatch this function to modify how per-repo
873 873 configs are loaded. For example, an extension may wish to pull in
874 874 configs from alternate files or sources.
875 875
876 876 sharedvfs is vfs object pointing to source repo if the current one is a
877 877 shared one
878 878 """
879 879 if not rcutil.use_repo_hgrc():
880 880 return False
881 881
882 882 ret = False
883 883 # first load config from shared source if we has to
884 884 if requirementsmod.SHARESAFE_REQUIREMENT in requirements and sharedvfs:
885 885 try:
886 886 ui.readconfig(sharedvfs.join(b'hgrc'), root=sharedvfs.base)
887 887 ret = True
888 888 except IOError:
889 889 pass
890 890
891 891 try:
892 892 ui.readconfig(hgvfs.join(b'hgrc'), root=wdirvfs.base)
893 893 ret = True
894 894 except IOError:
895 895 pass
896 896
897 897 try:
898 898 ui.readconfig(hgvfs.join(b'hgrc-not-shared'), root=wdirvfs.base)
899 899 ret = True
900 900 except IOError:
901 901 pass
902 902
903 903 return ret
904 904
905 905
906 906 def afterhgrcload(ui, wdirvfs, hgvfs, requirements):
907 907 """Perform additional actions after .hg/hgrc is loaded.
908 908
909 909 This function is called during repository loading immediately after
910 910 the .hg/hgrc file is loaded and before per-repo extensions are loaded.
911 911
912 912 The function can be used to validate configs, automatically add
913 913 options (including extensions) based on requirements, etc.
914 914 """
915 915
916 916 # Map of requirements to list of extensions to load automatically when
917 917 # requirement is present.
918 918 autoextensions = {
919 919 b'git': [b'git'],
920 920 b'largefiles': [b'largefiles'],
921 921 b'lfs': [b'lfs'],
922 922 }
923 923
924 924 for requirement, names in sorted(autoextensions.items()):
925 925 if requirement not in requirements:
926 926 continue
927 927
928 928 for name in names:
929 929 if not ui.hasconfig(b'extensions', name):
930 930 ui.setconfig(b'extensions', name, b'', source=b'autoload')
931 931
932 932
933 933 def gathersupportedrequirements(ui):
934 934 """Determine the complete set of recognized requirements."""
935 935 # Start with all requirements supported by this file.
936 936 supported = set(localrepository._basesupported)
937 937
938 938 # Execute ``featuresetupfuncs`` entries if they belong to an extension
939 939 # relevant to this ui instance.
940 940 modules = {m.__name__ for n, m in extensions.extensions(ui)}
941 941
942 942 for fn in featuresetupfuncs:
943 943 if fn.__module__ in modules:
944 944 fn(ui, supported)
945 945
946 946 # Add derived requirements from registered compression engines.
947 947 for name in util.compengines:
948 948 engine = util.compengines[name]
949 949 if engine.available() and engine.revlogheader():
950 950 supported.add(b'exp-compression-%s' % name)
951 951 if engine.name() == b'zstd':
952 952 supported.add(requirementsmod.REVLOG_COMPRESSION_ZSTD)
953 953
954 954 return supported
955 955
956 956
957 957 def ensurerequirementsrecognized(requirements, supported):
958 958 """Validate that a set of local requirements is recognized.
959 959
960 960 Receives a set of requirements. Raises an ``error.RepoError`` if there
961 961 exists any requirement in that set that currently loaded code doesn't
962 962 recognize.
963 963
964 964 Returns a set of supported requirements.
965 965 """
966 966 missing = set()
967 967
968 968 for requirement in requirements:
969 969 if requirement in supported:
970 970 continue
971 971
972 972 if not requirement or not requirement[0:1].isalnum():
973 973 raise error.RequirementError(_(b'.hg/requires file is corrupt'))
974 974
975 975 missing.add(requirement)
976 976
977 977 if missing:
978 978 raise error.RequirementError(
979 979 _(b'repository requires features unknown to this Mercurial: %s')
980 980 % b' '.join(sorted(missing)),
981 981 hint=_(
982 982 b'see https://mercurial-scm.org/wiki/MissingRequirement '
983 983 b'for more information'
984 984 ),
985 985 )
986 986
987 987
988 988 def ensurerequirementscompatible(ui, requirements):
989 989 """Validates that a set of recognized requirements is mutually compatible.
990 990
991 991 Some requirements may not be compatible with others or require
992 992 config options that aren't enabled. This function is called during
993 993 repository opening to ensure that the set of requirements needed
994 994 to open a repository is sane and compatible with config options.
995 995
996 996 Extensions can monkeypatch this function to perform additional
997 997 checking.
998 998
999 999 ``error.RepoError`` should be raised on failure.
1000 1000 """
1001 1001 if (
1002 1002 requirementsmod.SPARSE_REQUIREMENT in requirements
1003 1003 and not sparse.enabled
1004 1004 ):
1005 1005 raise error.RepoError(
1006 1006 _(
1007 1007 b'repository is using sparse feature but '
1008 1008 b'sparse is not enabled; enable the '
1009 1009 b'"sparse" extensions to access'
1010 1010 )
1011 1011 )
1012 1012
1013 1013
1014 1014 def makestore(requirements, path, vfstype):
1015 1015 """Construct a storage object for a repository."""
1016 1016 if requirementsmod.STORE_REQUIREMENT in requirements:
1017 1017 if requirementsmod.FNCACHE_REQUIREMENT in requirements:
1018 1018 dotencode = requirementsmod.DOTENCODE_REQUIREMENT in requirements
1019 1019 return storemod.fncachestore(path, vfstype, dotencode)
1020 1020
1021 1021 return storemod.encodedstore(path, vfstype)
1022 1022
1023 1023 return storemod.basicstore(path, vfstype)
1024 1024
1025 1025
1026 1026 def resolvestorevfsoptions(ui, requirements, features):
1027 1027 """Resolve the options to pass to the store vfs opener.
1028 1028
1029 1029 The returned dict is used to influence behavior of the storage layer.
1030 1030 """
1031 1031 options = {}
1032 1032
1033 1033 if requirementsmod.TREEMANIFEST_REQUIREMENT in requirements:
1034 1034 options[b'treemanifest'] = True
1035 1035
1036 1036 # experimental config: format.manifestcachesize
1037 1037 manifestcachesize = ui.configint(b'format', b'manifestcachesize')
1038 1038 if manifestcachesize is not None:
1039 1039 options[b'manifestcachesize'] = manifestcachesize
1040 1040
1041 1041 # In the absence of another requirement superseding a revlog-related
1042 1042 # requirement, we have to assume the repo is using revlog version 0.
1043 1043 # This revlog format is super old and we don't bother trying to parse
1044 1044 # opener options for it because those options wouldn't do anything
1045 1045 # meaningful on such old repos.
1046 1046 if (
1047 1047 requirementsmod.REVLOGV1_REQUIREMENT in requirements
1048 1048 or requirementsmod.REVLOGV2_REQUIREMENT in requirements
1049 1049 ):
1050 1050 options.update(resolverevlogstorevfsoptions(ui, requirements, features))
1051 1051 else: # explicitly mark repo as using revlogv0
1052 1052 options[b'revlogv0'] = True
1053 1053
1054 1054 if requirementsmod.COPIESSDC_REQUIREMENT in requirements:
1055 1055 options[b'copies-storage'] = b'changeset-sidedata'
1056 1056 else:
1057 1057 writecopiesto = ui.config(b'experimental', b'copies.write-to')
1058 1058 copiesextramode = (b'changeset-only', b'compatibility')
1059 1059 if writecopiesto in copiesextramode:
1060 1060 options[b'copies-storage'] = b'extra'
1061 1061
1062 1062 return options
1063 1063
1064 1064
1065 1065 def resolverevlogstorevfsoptions(ui, requirements, features):
1066 1066 """Resolve opener options specific to revlogs."""
1067 1067
1068 1068 options = {}
1069 1069 options[b'flagprocessors'] = {}
1070 1070
1071 1071 feature_config = options[b'feature-config'] = revlog.FeatureConfig()
1072 1072 data_config = options[b'data-config'] = revlog.DataConfig()
1073 1073 delta_config = options[b'delta-config'] = revlog.DeltaConfig()
1074 1074
1075 1075 if requirementsmod.REVLOGV1_REQUIREMENT in requirements:
1076 1076 options[b'revlogv1'] = True
1077 1077 if requirementsmod.REVLOGV2_REQUIREMENT in requirements:
1078 1078 options[b'revlogv2'] = True
1079 1079 if requirementsmod.CHANGELOGV2_REQUIREMENT in requirements:
1080 1080 options[b'changelogv2'] = True
1081 1081 cmp_rank = ui.configbool(b'experimental', b'changelog-v2.compute-rank')
1082 1082 options[b'changelogv2.compute-rank'] = cmp_rank
1083 1083
1084 1084 if requirementsmod.GENERALDELTA_REQUIREMENT in requirements:
1085 1085 options[b'generaldelta'] = True
1086 1086
1087 1087 # experimental config: format.chunkcachesize
1088 1088 chunkcachesize = ui.configint(b'format', b'chunkcachesize')
1089 1089 if chunkcachesize is not None:
1090 1090 data_config.chunk_cache_size = chunkcachesize
1091 1091
1092 1092 memory_profile = scmutil.get_resource_profile(ui, b'memory')
1093 1093 if memory_profile >= scmutil.RESOURCE_MEDIUM:
1094 1094 data_config.uncompressed_cache_count = 10_000
1095 1095 data_config.uncompressed_cache_factor = 4
1096 1096 if memory_profile >= scmutil.RESOURCE_HIGH:
1097 1097 data_config.uncompressed_cache_factor = 10
1098 1098
1099 1099 delta_config.delta_both_parents = ui.configbool(
1100 1100 b'storage', b'revlog.optimize-delta-parent-choice'
1101 1101 )
1102 1102 delta_config.candidate_group_chunk_size = ui.configint(
1103 1103 b'storage',
1104 1104 b'revlog.delta-parent-search.candidate-group-chunk-size',
1105 1105 )
1106 1106 delta_config.debug_delta = ui.configbool(b'debug', b'revlog.debug-delta')
1107 1107
1108 1108 issue6528 = ui.configbool(b'storage', b'revlog.issue6528.fix-incoming')
1109 1109 options[b'issue6528.fix-incoming'] = issue6528
1110 1110
1111 1111 lazydelta = ui.configbool(b'storage', b'revlog.reuse-external-delta')
1112 1112 lazydeltabase = False
1113 1113 if lazydelta:
1114 1114 lazydeltabase = ui.configbool(
1115 1115 b'storage', b'revlog.reuse-external-delta-parent'
1116 1116 )
1117 1117 if lazydeltabase is None:
1118 1118 lazydeltabase = not scmutil.gddeltaconfig(ui)
1119 1119 delta_config.lazy_delta = lazydelta
1120 1120 delta_config.lazy_delta_base = lazydeltabase
1121 1121
1122 1122 chainspan = ui.configbytes(b'experimental', b'maxdeltachainspan')
1123 1123 if 0 <= chainspan:
1124 1124 delta_config.max_deltachain_span = chainspan
1125 1125
1126 1126 mmapindexthreshold = ui.configbytes(b'experimental', b'mmapindexthreshold')
1127 1127 if mmapindexthreshold is not None:
1128 1128 data_config.mmap_index_threshold = mmapindexthreshold
1129 1129
1130 1130 withsparseread = ui.configbool(b'experimental', b'sparse-read')
1131 1131 srdensitythres = float(
1132 1132 ui.config(b'experimental', b'sparse-read.density-threshold')
1133 1133 )
1134 1134 srmingapsize = ui.configbytes(b'experimental', b'sparse-read.min-gap-size')
1135 1135 data_config.with_sparse_read = withsparseread
1136 1136 data_config.sr_density_threshold = srdensitythres
1137 1137 data_config.sr_min_gap_size = srmingapsize
1138 1138
1139 1139 sparserevlog = requirementsmod.SPARSEREVLOG_REQUIREMENT in requirements
1140 1140 delta_config.sparse_revlog = sparserevlog
1141 1141 if sparserevlog:
1142 1142 options[b'generaldelta'] = True
1143 1143 data_config.with_sparse_read = True
1144 1144
1145 1145 maxchainlen = None
1146 1146 if sparserevlog:
1147 1147 maxchainlen = revlogconst.SPARSE_REVLOG_MAX_CHAIN_LENGTH
1148 1148 # experimental config: format.maxchainlen
1149 1149 maxchainlen = ui.configint(b'format', b'maxchainlen', maxchainlen)
1150 1150 if maxchainlen is not None:
1151 1151 delta_config.max_chain_len = maxchainlen
1152 1152
1153 1153 for r in requirements:
1154 1154 # we allow multiple compression engine requirement to co-exist because
1155 1155 # strickly speaking, revlog seems to support mixed compression style.
1156 1156 #
1157 1157 # The compression used for new entries will be "the last one"
1158 1158 prefix = r.startswith
1159 1159 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
1160 1160 feature_config.compression_engine = r.split(b'-', 2)[2]
1161 1161
1162 1162 zlib_level = ui.configint(b'storage', b'revlog.zlib.level')
1163 1163 if zlib_level is not None:
1164 1164 if not (0 <= zlib_level <= 9):
1165 1165 msg = _(b'invalid value for `storage.revlog.zlib.level` config: %d')
1166 1166 raise error.Abort(msg % zlib_level)
1167 1167 feature_config.compression_engine_options[b'zlib.level'] = zlib_level
1168 1168 zstd_level = ui.configint(b'storage', b'revlog.zstd.level')
1169 1169 if zstd_level is not None:
1170 1170 if not (0 <= zstd_level <= 22):
1171 1171 msg = _(b'invalid value for `storage.revlog.zstd.level` config: %d')
1172 1172 raise error.Abort(msg % zstd_level)
1173 1173 feature_config.compression_engine_options[b'zstd.level'] = zstd_level
1174 1174
1175 1175 if requirementsmod.NARROW_REQUIREMENT in requirements:
1176 1176 feature_config.enable_ellipsis = True
1177 1177
1178 1178 if ui.configbool(b'experimental', b'rust.index'):
1179 1179 options[b'rust.index'] = True
1180 1180 if requirementsmod.NODEMAP_REQUIREMENT in requirements:
1181 1181 slow_path = ui.config(
1182 1182 b'storage', b'revlog.persistent-nodemap.slow-path'
1183 1183 )
1184 1184 if slow_path not in (b'allow', b'warn', b'abort'):
1185 1185 default = ui.config_default(
1186 1186 b'storage', b'revlog.persistent-nodemap.slow-path'
1187 1187 )
1188 1188 msg = _(
1189 1189 b'unknown value for config '
1190 1190 b'"storage.revlog.persistent-nodemap.slow-path": "%s"\n'
1191 1191 )
1192 1192 ui.warn(msg % slow_path)
1193 1193 if not ui.quiet:
1194 1194 ui.warn(_(b'falling back to default value: %s\n') % default)
1195 1195 slow_path = default
1196 1196
1197 1197 msg = _(
1198 1198 b"accessing `persistent-nodemap` repository without associated "
1199 1199 b"fast implementation."
1200 1200 )
1201 1201 hint = _(
1202 1202 b"check `hg help config.format.use-persistent-nodemap` "
1203 1203 b"for details"
1204 1204 )
1205 1205 if not revlog.HAS_FAST_PERSISTENT_NODEMAP:
1206 1206 if slow_path == b'warn':
1207 1207 msg = b"warning: " + msg + b'\n'
1208 1208 ui.warn(msg)
1209 1209 if not ui.quiet:
1210 1210 hint = b'(' + hint + b')\n'
1211 1211 ui.warn(hint)
1212 1212 if slow_path == b'abort':
1213 1213 raise error.Abort(msg, hint=hint)
1214 1214 options[b'persistent-nodemap'] = True
1215 1215 if requirementsmod.DIRSTATE_V2_REQUIREMENT in requirements:
1216 1216 slow_path = ui.config(b'storage', b'dirstate-v2.slow-path')
1217 1217 if slow_path not in (b'allow', b'warn', b'abort'):
1218 1218 default = ui.config_default(b'storage', b'dirstate-v2.slow-path')
1219 1219 msg = _(b'unknown value for config "dirstate-v2.slow-path": "%s"\n')
1220 1220 ui.warn(msg % slow_path)
1221 1221 if not ui.quiet:
1222 1222 ui.warn(_(b'falling back to default value: %s\n') % default)
1223 1223 slow_path = default
1224 1224
1225 1225 msg = _(
1226 1226 b"accessing `dirstate-v2` repository without associated "
1227 1227 b"fast implementation."
1228 1228 )
1229 1229 hint = _(
1230 1230 b"check `hg help config.format.use-dirstate-v2` " b"for details"
1231 1231 )
1232 1232 if not dirstate.HAS_FAST_DIRSTATE_V2:
1233 1233 if slow_path == b'warn':
1234 1234 msg = b"warning: " + msg + b'\n'
1235 1235 ui.warn(msg)
1236 1236 if not ui.quiet:
1237 1237 hint = b'(' + hint + b')\n'
1238 1238 ui.warn(hint)
1239 1239 if slow_path == b'abort':
1240 1240 raise error.Abort(msg, hint=hint)
1241 1241 if ui.configbool(b'storage', b'revlog.persistent-nodemap.mmap'):
1242 1242 options[b'persistent-nodemap.mmap'] = True
1243 1243 if ui.configbool(b'devel', b'persistent-nodemap'):
1244 1244 options[b'devel-force-nodemap'] = True
1245 1245
1246 1246 return options
1247 1247
1248 1248
1249 1249 def makemain(**kwargs):
1250 1250 """Produce a type conforming to ``ilocalrepositorymain``."""
1251 1251 return localrepository
1252 1252
1253 1253
1254 1254 @interfaceutil.implementer(repository.ilocalrepositoryfilestorage)
1255 1255 class revlogfilestorage:
1256 1256 """File storage when using revlogs."""
1257 1257
1258 1258 def file(self, path):
1259 1259 if path.startswith(b'/'):
1260 1260 path = path[1:]
1261 1261
1262 1262 try_split = (
1263 1263 self.currenttransaction() is not None
1264 1264 or txnutil.mayhavepending(self.root)
1265 1265 )
1266 1266
1267 1267 return filelog.filelog(self.svfs, path, try_split=try_split)
1268 1268
1269 1269
1270 1270 @interfaceutil.implementer(repository.ilocalrepositoryfilestorage)
1271 1271 class revlognarrowfilestorage:
1272 1272 """File storage when using revlogs and narrow files."""
1273 1273
1274 1274 def file(self, path):
1275 1275 if path.startswith(b'/'):
1276 1276 path = path[1:]
1277 1277
1278 1278 try_split = (
1279 1279 self.currenttransaction() is not None
1280 1280 or txnutil.mayhavepending(self.root)
1281 1281 )
1282 1282 return filelog.narrowfilelog(
1283 1283 self.svfs, path, self._storenarrowmatch, try_split=try_split
1284 1284 )
1285 1285
1286 1286
1287 1287 def makefilestorage(requirements, features, **kwargs):
1288 1288 """Produce a type conforming to ``ilocalrepositoryfilestorage``."""
1289 1289 features.add(repository.REPO_FEATURE_REVLOG_FILE_STORAGE)
1290 1290 features.add(repository.REPO_FEATURE_STREAM_CLONE)
1291 1291
1292 1292 if requirementsmod.NARROW_REQUIREMENT in requirements:
1293 1293 return revlognarrowfilestorage
1294 1294 else:
1295 1295 return revlogfilestorage
1296 1296
1297 1297
1298 1298 # List of repository interfaces and factory functions for them. Each
1299 1299 # will be called in order during ``makelocalrepository()`` to iteratively
1300 1300 # derive the final type for a local repository instance. We capture the
1301 1301 # function as a lambda so we don't hold a reference and the module-level
1302 1302 # functions can be wrapped.
1303 1303 REPO_INTERFACES = [
1304 1304 (repository.ilocalrepositorymain, lambda: makemain),
1305 1305 (repository.ilocalrepositoryfilestorage, lambda: makefilestorage),
1306 1306 ]
1307 1307
1308 1308
1309 1309 @interfaceutil.implementer(repository.ilocalrepositorymain)
1310 1310 class localrepository:
1311 1311 """Main class for representing local repositories.
1312 1312
1313 1313 All local repositories are instances of this class.
1314 1314
1315 1315 Constructed on its own, instances of this class are not usable as
1316 1316 repository objects. To obtain a usable repository object, call
1317 1317 ``hg.repository()``, ``localrepo.instance()``, or
1318 1318 ``localrepo.makelocalrepository()``. The latter is the lowest-level.
1319 1319 ``instance()`` adds support for creating new repositories.
1320 1320 ``hg.repository()`` adds more extension integration, including calling
1321 1321 ``reposetup()``. Generally speaking, ``hg.repository()`` should be
1322 1322 used.
1323 1323 """
1324 1324
1325 1325 _basesupported = {
1326 1326 requirementsmod.ARCHIVED_PHASE_REQUIREMENT,
1327 1327 requirementsmod.BOOKMARKS_IN_STORE_REQUIREMENT,
1328 1328 requirementsmod.CHANGELOGV2_REQUIREMENT,
1329 1329 requirementsmod.COPIESSDC_REQUIREMENT,
1330 1330 requirementsmod.DIRSTATE_TRACKED_HINT_V1,
1331 1331 requirementsmod.DIRSTATE_V2_REQUIREMENT,
1332 1332 requirementsmod.DOTENCODE_REQUIREMENT,
1333 1333 requirementsmod.FNCACHE_REQUIREMENT,
1334 1334 requirementsmod.GENERALDELTA_REQUIREMENT,
1335 1335 requirementsmod.INTERNAL_PHASE_REQUIREMENT,
1336 1336 requirementsmod.NODEMAP_REQUIREMENT,
1337 1337 requirementsmod.RELATIVE_SHARED_REQUIREMENT,
1338 1338 requirementsmod.REVLOGV1_REQUIREMENT,
1339 1339 requirementsmod.REVLOGV2_REQUIREMENT,
1340 1340 requirementsmod.SHARED_REQUIREMENT,
1341 1341 requirementsmod.SHARESAFE_REQUIREMENT,
1342 1342 requirementsmod.SPARSE_REQUIREMENT,
1343 1343 requirementsmod.SPARSEREVLOG_REQUIREMENT,
1344 1344 requirementsmod.STORE_REQUIREMENT,
1345 1345 requirementsmod.TREEMANIFEST_REQUIREMENT,
1346 1346 }
1347 1347
1348 1348 # list of prefix for file which can be written without 'wlock'
1349 1349 # Extensions should extend this list when needed
1350 1350 _wlockfreeprefix = {
1351 1351 # We migh consider requiring 'wlock' for the next
1352 1352 # two, but pretty much all the existing code assume
1353 1353 # wlock is not needed so we keep them excluded for
1354 1354 # now.
1355 1355 b'hgrc',
1356 1356 b'requires',
1357 1357 # XXX cache is a complicatged business someone
1358 1358 # should investigate this in depth at some point
1359 1359 b'cache/',
1360 1360 # XXX bisect was still a bit too messy at the time
1361 1361 # this changeset was introduced. Someone should fix
1362 1362 # the remainig bit and drop this line
1363 1363 b'bisect.state',
1364 1364 }
1365 1365
1366 1366 def __init__(
1367 1367 self,
1368 1368 baseui,
1369 1369 ui,
1370 1370 origroot: bytes,
1371 1371 wdirvfs: vfsmod.vfs,
1372 1372 hgvfs: vfsmod.vfs,
1373 1373 requirements,
1374 1374 supportedrequirements,
1375 1375 sharedpath: bytes,
1376 1376 store,
1377 1377 cachevfs: vfsmod.vfs,
1378 1378 wcachevfs: vfsmod.vfs,
1379 1379 features,
1380 1380 intents=None,
1381 1381 ):
1382 1382 """Create a new local repository instance.
1383 1383
1384 1384 Most callers should use ``hg.repository()``, ``localrepo.instance()``,
1385 1385 or ``localrepo.makelocalrepository()`` for obtaining a new repository
1386 1386 object.
1387 1387
1388 1388 Arguments:
1389 1389
1390 1390 baseui
1391 1391 ``ui.ui`` instance that ``ui`` argument was based off of.
1392 1392
1393 1393 ui
1394 1394 ``ui.ui`` instance for use by the repository.
1395 1395
1396 1396 origroot
1397 1397 ``bytes`` path to working directory root of this repository.
1398 1398
1399 1399 wdirvfs
1400 1400 ``vfs.vfs`` rooted at the working directory.
1401 1401
1402 1402 hgvfs
1403 1403 ``vfs.vfs`` rooted at .hg/
1404 1404
1405 1405 requirements
1406 1406 ``set`` of bytestrings representing repository opening requirements.
1407 1407
1408 1408 supportedrequirements
1409 1409 ``set`` of bytestrings representing repository requirements that we
1410 1410 know how to open. May be a supetset of ``requirements``.
1411 1411
1412 1412 sharedpath
1413 1413 ``bytes`` Defining path to storage base directory. Points to a
1414 1414 ``.hg/`` directory somewhere.
1415 1415
1416 1416 store
1417 1417 ``store.basicstore`` (or derived) instance providing access to
1418 1418 versioned storage.
1419 1419
1420 1420 cachevfs
1421 1421 ``vfs.vfs`` used for cache files.
1422 1422
1423 1423 wcachevfs
1424 1424 ``vfs.vfs`` used for cache files related to the working copy.
1425 1425
1426 1426 features
1427 1427 ``set`` of bytestrings defining features/capabilities of this
1428 1428 instance.
1429 1429
1430 1430 intents
1431 1431 ``set`` of system strings indicating what this repo will be used
1432 1432 for.
1433 1433 """
1434 1434 self.baseui = baseui
1435 1435 self.ui = ui
1436 1436 self.origroot = origroot
1437 1437 # vfs rooted at working directory.
1438 1438 self.wvfs = wdirvfs
1439 1439 self.root = wdirvfs.base
1440 1440 # vfs rooted at .hg/. Used to access most non-store paths.
1441 1441 self.vfs = hgvfs
1442 1442 self.path = hgvfs.base
1443 1443 self.requirements = requirements
1444 1444 self.nodeconstants = sha1nodeconstants
1445 1445 self.nullid = self.nodeconstants.nullid
1446 1446 self.supported = supportedrequirements
1447 1447 self.sharedpath = sharedpath
1448 1448 self.store = store
1449 1449 self.cachevfs = cachevfs
1450 1450 self.wcachevfs = wcachevfs
1451 1451 self.features = features
1452 1452
1453 1453 self.filtername = None
1454 1454
1455 1455 if self.ui.configbool(b'devel', b'all-warnings') or self.ui.configbool(
1456 1456 b'devel', b'check-locks'
1457 1457 ):
1458 1458 self.vfs.audit = self._getvfsward(self.vfs.audit)
1459 1459 # A list of callback to shape the phase if no data were found.
1460 1460 # Callback are in the form: func(repo, roots) --> processed root.
1461 1461 # This list it to be filled by extension during repo setup
1462 1462 self._phasedefaults = []
1463 1463
1464 1464 color.setup(self.ui)
1465 1465
1466 1466 self.spath = self.store.path
1467 1467 self.svfs = self.store.vfs
1468 1468 self.sjoin = self.store.join
1469 1469 if self.ui.configbool(b'devel', b'all-warnings') or self.ui.configbool(
1470 1470 b'devel', b'check-locks'
1471 1471 ):
1472 1472 if hasattr(self.svfs, 'vfs'): # this is filtervfs
1473 1473 self.svfs.vfs.audit = self._getsvfsward(self.svfs.vfs.audit)
1474 1474 else: # standard vfs
1475 1475 self.svfs.audit = self._getsvfsward(self.svfs.audit)
1476 1476
1477 1477 self._dirstatevalidatewarned = False
1478 1478
1479 1479 self._branchcaches = branchmap.BranchMapCache()
1480 1480 self._revbranchcache = None
1481 1481 self._filterpats = {}
1482 1482 self._datafilters = {}
1483 1483 self._transref = self._lockref = self._wlockref = None
1484 1484
1485 1485 # A cache for various files under .hg/ that tracks file changes,
1486 1486 # (used by the filecache decorator)
1487 1487 #
1488 1488 # Maps a property name to its util.filecacheentry
1489 1489 self._filecache = {}
1490 1490
1491 1491 # hold sets of revision to be filtered
1492 1492 # should be cleared when something might have changed the filter value:
1493 1493 # - new changesets,
1494 1494 # - phase change,
1495 1495 # - new obsolescence marker,
1496 1496 # - working directory parent change,
1497 1497 # - bookmark changes
1498 1498 self.filteredrevcache = {}
1499 1499
1500 1500 self._dirstate = None
1501 1501 # post-dirstate-status hooks
1502 1502 self._postdsstatus = []
1503 1503
1504 1504 self._pending_narrow_pats = None
1505 1505 self._pending_narrow_pats_dirstate = None
1506 1506
1507 1507 # generic mapping between names and nodes
1508 1508 self.names = namespaces.namespaces()
1509 1509
1510 1510 # Key to signature value.
1511 1511 self._sparsesignaturecache = {}
1512 1512 # Signature to cached matcher instance.
1513 1513 self._sparsematchercache = {}
1514 1514
1515 1515 self._extrafilterid = repoview.extrafilter(ui)
1516 1516
1517 1517 self.filecopiesmode = None
1518 1518 if requirementsmod.COPIESSDC_REQUIREMENT in self.requirements:
1519 1519 self.filecopiesmode = b'changeset-sidedata'
1520 1520
1521 1521 self._wanted_sidedata = set()
1522 1522 self._sidedata_computers = {}
1523 1523 sidedatamod.set_sidedata_spec_for_repo(self)
1524 1524
1525 1525 def _getvfsward(self, origfunc):
1526 1526 """build a ward for self.vfs"""
1527 1527 rref = weakref.ref(self)
1528 1528
1529 1529 def checkvfs(path, mode=None):
1530 1530 ret = origfunc(path, mode=mode)
1531 1531 repo = rref()
1532 1532 if (
1533 1533 repo is None
1534 1534 or not hasattr(repo, '_wlockref')
1535 1535 or not hasattr(repo, '_lockref')
1536 1536 ):
1537 1537 return
1538 1538 if mode in (None, b'r', b'rb'):
1539 1539 return
1540 1540 if path.startswith(repo.path):
1541 1541 # truncate name relative to the repository (.hg)
1542 1542 path = path[len(repo.path) + 1 :]
1543 1543 if path.startswith(b'cache/'):
1544 1544 msg = b'accessing cache with vfs instead of cachevfs: "%s"'
1545 1545 repo.ui.develwarn(msg % path, stacklevel=3, config=b"cache-vfs")
1546 1546 # path prefixes covered by 'lock'
1547 1547 vfs_path_prefixes = (
1548 1548 b'journal.',
1549 1549 b'undo.',
1550 1550 b'strip-backup/',
1551 1551 b'cache/',
1552 1552 )
1553 1553 if any(path.startswith(prefix) for prefix in vfs_path_prefixes):
1554 1554 if repo._currentlock(repo._lockref) is None:
1555 1555 repo.ui.develwarn(
1556 1556 b'write with no lock: "%s"' % path,
1557 1557 stacklevel=3,
1558 1558 config=b'check-locks',
1559 1559 )
1560 1560 elif repo._currentlock(repo._wlockref) is None:
1561 1561 # rest of vfs files are covered by 'wlock'
1562 1562 #
1563 1563 # exclude special files
1564 1564 for prefix in self._wlockfreeprefix:
1565 1565 if path.startswith(prefix):
1566 1566 return
1567 1567 repo.ui.develwarn(
1568 1568 b'write with no wlock: "%s"' % path,
1569 1569 stacklevel=3,
1570 1570 config=b'check-locks',
1571 1571 )
1572 1572 return ret
1573 1573
1574 1574 return checkvfs
1575 1575
1576 1576 def _getsvfsward(self, origfunc):
1577 1577 """build a ward for self.svfs"""
1578 1578 rref = weakref.ref(self)
1579 1579
1580 1580 def checksvfs(path, mode=None):
1581 1581 ret = origfunc(path, mode=mode)
1582 1582 repo = rref()
1583 1583 if repo is None or not hasattr(repo, '_lockref'):
1584 1584 return
1585 1585 if mode in (None, b'r', b'rb'):
1586 1586 return
1587 1587 if path.startswith(repo.sharedpath):
1588 1588 # truncate name relative to the repository (.hg)
1589 1589 path = path[len(repo.sharedpath) + 1 :]
1590 1590 if repo._currentlock(repo._lockref) is None:
1591 1591 repo.ui.develwarn(
1592 1592 b'write with no lock: "%s"' % path, stacklevel=4
1593 1593 )
1594 1594 return ret
1595 1595
1596 1596 return checksvfs
1597 1597
1598 1598 @property
1599 1599 def vfs_map(self):
1600 1600 return {
1601 1601 b'': self.svfs,
1602 1602 b'plain': self.vfs,
1603 1603 b'store': self.svfs,
1604 1604 }
1605 1605
1606 1606 def close(self):
1607 1607 self._writecaches()
1608 1608
1609 1609 def _writecaches(self):
1610 1610 if self._revbranchcache:
1611 1611 self._revbranchcache.write()
1612 1612
1613 1613 def _restrictcapabilities(self, caps):
1614 1614 if self.ui.configbool(b'experimental', b'bundle2-advertise'):
1615 1615 caps = set(caps)
1616 1616 capsblob = bundle2.encodecaps(
1617 1617 bundle2.getrepocaps(self, role=b'client')
1618 1618 )
1619 1619 caps.add(b'bundle2=' + urlreq.quote(capsblob))
1620 1620 if self.ui.configbool(b'experimental', b'narrow'):
1621 1621 caps.add(wireprototypes.NARROWCAP)
1622 1622 return caps
1623 1623
1624 1624 # Don't cache auditor/nofsauditor, or you'll end up with reference cycle:
1625 1625 # self -> auditor -> self._checknested -> self
1626 1626
1627 1627 @property
1628 1628 def auditor(self):
1629 1629 # This is only used by context.workingctx.match in order to
1630 1630 # detect files in subrepos.
1631 1631 return pathutil.pathauditor(self.root, callback=self._checknested)
1632 1632
1633 1633 @property
1634 1634 def nofsauditor(self):
1635 1635 # This is only used by context.basectx.match in order to detect
1636 1636 # files in subrepos.
1637 1637 return pathutil.pathauditor(
1638 1638 self.root, callback=self._checknested, realfs=False, cached=True
1639 1639 )
1640 1640
1641 1641 def _checknested(self, path):
1642 1642 """Determine if path is a legal nested repository."""
1643 1643 if not path.startswith(self.root):
1644 1644 return False
1645 1645 subpath = path[len(self.root) + 1 :]
1646 1646 normsubpath = util.pconvert(subpath)
1647 1647
1648 1648 # XXX: Checking against the current working copy is wrong in
1649 1649 # the sense that it can reject things like
1650 1650 #
1651 1651 # $ hg cat -r 10 sub/x.txt
1652 1652 #
1653 1653 # if sub/ is no longer a subrepository in the working copy
1654 1654 # parent revision.
1655 1655 #
1656 1656 # However, it can of course also allow things that would have
1657 1657 # been rejected before, such as the above cat command if sub/
1658 1658 # is a subrepository now, but was a normal directory before.
1659 1659 # The old path auditor would have rejected by mistake since it
1660 1660 # panics when it sees sub/.hg/.
1661 1661 #
1662 1662 # All in all, checking against the working copy seems sensible
1663 1663 # since we want to prevent access to nested repositories on
1664 1664 # the filesystem *now*.
1665 1665 ctx = self[None]
1666 1666 parts = util.splitpath(subpath)
1667 1667 while parts:
1668 1668 prefix = b'/'.join(parts)
1669 1669 if prefix in ctx.substate:
1670 1670 if prefix == normsubpath:
1671 1671 return True
1672 1672 else:
1673 1673 sub = ctx.sub(prefix)
1674 1674 return sub.checknested(subpath[len(prefix) + 1 :])
1675 1675 else:
1676 1676 parts.pop()
1677 1677 return False
1678 1678
1679 1679 def peer(self, path=None, remotehidden=False):
1680 1680 return localpeer(
1681 1681 self, path=path, remotehidden=remotehidden
1682 1682 ) # not cached to avoid reference cycle
1683 1683
1684 1684 def unfiltered(self):
1685 1685 """Return unfiltered version of the repository
1686 1686
1687 1687 Intended to be overwritten by filtered repo."""
1688 1688 return self
1689 1689
1690 1690 def filtered(self, name, visibilityexceptions=None):
1691 1691 """Return a filtered version of a repository
1692 1692
1693 1693 The `name` parameter is the identifier of the requested view. This
1694 1694 will return a repoview object set "exactly" to the specified view.
1695 1695
1696 1696 This function does not apply recursive filtering to a repository. For
1697 1697 example calling `repo.filtered("served")` will return a repoview using
1698 1698 the "served" view, regardless of the initial view used by `repo`.
1699 1699
1700 1700 In other word, there is always only one level of `repoview` "filtering".
1701 1701 """
1702 1702 if self._extrafilterid is not None and b'%' not in name:
1703 1703 name = name + b'%' + self._extrafilterid
1704 1704
1705 1705 cls = repoview.newtype(self.unfiltered().__class__)
1706 1706 return cls(self, name, visibilityexceptions)
1707 1707
1708 1708 @mixedrepostorecache(
1709 1709 (b'bookmarks', b'plain'),
1710 1710 (b'bookmarks.current', b'plain'),
1711 1711 (b'bookmarks', b''),
1712 1712 (b'00changelog.i', b''),
1713 1713 )
1714 1714 def _bookmarks(self):
1715 1715 # Since the multiple files involved in the transaction cannot be
1716 1716 # written atomically (with current repository format), there is a race
1717 1717 # condition here.
1718 1718 #
1719 1719 # 1) changelog content A is read
1720 1720 # 2) outside transaction update changelog to content B
1721 1721 # 3) outside transaction update bookmark file referring to content B
1722 1722 # 4) bookmarks file content is read and filtered against changelog-A
1723 1723 #
1724 1724 # When this happens, bookmarks against nodes missing from A are dropped.
1725 1725 #
1726 1726 # Having this happening during read is not great, but it become worse
1727 1727 # when this happen during write because the bookmarks to the "unknown"
1728 1728 # nodes will be dropped for good. However, writes happen within locks.
1729 1729 # This locking makes it possible to have a race free consistent read.
1730 1730 # For this purpose data read from disc before locking are
1731 1731 # "invalidated" right after the locks are taken. This invalidations are
1732 1732 # "light", the `filecache` mechanism keep the data in memory and will
1733 1733 # reuse them if the underlying files did not changed. Not parsing the
1734 1734 # same data multiple times helps performances.
1735 1735 #
1736 1736 # Unfortunately in the case describe above, the files tracked by the
1737 1737 # bookmarks file cache might not have changed, but the in-memory
1738 1738 # content is still "wrong" because we used an older changelog content
1739 1739 # to process the on-disk data. So after locking, the changelog would be
1740 1740 # refreshed but `_bookmarks` would be preserved.
1741 1741 # Adding `00changelog.i` to the list of tracked file is not
1742 1742 # enough, because at the time we build the content for `_bookmarks` in
1743 1743 # (4), the changelog file has already diverged from the content used
1744 1744 # for loading `changelog` in (1)
1745 1745 #
1746 1746 # To prevent the issue, we force the changelog to be explicitly
1747 1747 # reloaded while computing `_bookmarks`. The data race can still happen
1748 1748 # without the lock (with a narrower window), but it would no longer go
1749 1749 # undetected during the lock time refresh.
1750 1750 #
1751 1751 # The new schedule is as follow
1752 1752 #
1753 1753 # 1) filecache logic detect that `_bookmarks` needs to be computed
1754 1754 # 2) cachestat for `bookmarks` and `changelog` are captured (for book)
1755 1755 # 3) We force `changelog` filecache to be tested
1756 1756 # 4) cachestat for `changelog` are captured (for changelog)
1757 1757 # 5) `_bookmarks` is computed and cached
1758 1758 #
1759 1759 # The step in (3) ensure we have a changelog at least as recent as the
1760 1760 # cache stat computed in (1). As a result at locking time:
1761 1761 # * if the changelog did not changed since (1) -> we can reuse the data
1762 1762 # * otherwise -> the bookmarks get refreshed.
1763 1763 self._refreshchangelog()
1764 1764 return bookmarks.bmstore(self)
1765 1765
1766 1766 def _refreshchangelog(self):
1767 1767 """make sure the in memory changelog match the on-disk one"""
1768 1768 if 'changelog' in vars(self) and self.currenttransaction() is None:
1769 1769 del self.changelog
1770 1770
1771 1771 @property
1772 1772 def _activebookmark(self):
1773 1773 return self._bookmarks.active
1774 1774
1775 1775 # _phasesets depend on changelog. what we need is to call
1776 1776 # _phasecache.invalidate() if '00changelog.i' was changed, but it
1777 1777 # can't be easily expressed in filecache mechanism.
1778 1778 @storecache(b'phaseroots', b'00changelog.i')
1779 1779 def _phasecache(self):
1780 1780 return phases.phasecache(self, self._phasedefaults)
1781 1781
1782 1782 @storecache(b'obsstore')
1783 1783 def obsstore(self):
1784 1784 return obsolete.makestore(self.ui, self)
1785 1785
1786 1786 @changelogcache()
1787 1787 def changelog(repo):
1788 1788 # load dirstate before changelog to avoid race see issue6303
1789 1789 repo.dirstate.prefetch_parents()
1790 1790 return repo.store.changelog(
1791 1791 txnutil.mayhavepending(repo.root),
1792 1792 concurrencychecker=revlogchecker.get_checker(repo.ui, b'changelog'),
1793 1793 )
1794 1794
1795 1795 @manifestlogcache()
1796 1796 def manifestlog(self):
1797 1797 return self.store.manifestlog(self, self._storenarrowmatch)
1798 1798
1799 1799 @unfilteredpropertycache
1800 1800 def dirstate(self):
1801 1801 if self._dirstate is None:
1802 1802 self._dirstate = self._makedirstate()
1803 1803 else:
1804 1804 self._dirstate.refresh()
1805 1805 return self._dirstate
1806 1806
1807 1807 def _makedirstate(self):
1808 1808 """Extension point for wrapping the dirstate per-repo."""
1809 1809 sparsematchfn = None
1810 1810 if sparse.use_sparse(self):
1811 1811 sparsematchfn = lambda: sparse.matcher(self)
1812 1812 v2_req = requirementsmod.DIRSTATE_V2_REQUIREMENT
1813 1813 th = requirementsmod.DIRSTATE_TRACKED_HINT_V1
1814 1814 use_dirstate_v2 = v2_req in self.requirements
1815 1815 use_tracked_hint = th in self.requirements
1816 1816
1817 1817 return dirstate.dirstate(
1818 1818 self.vfs,
1819 1819 self.ui,
1820 1820 self.root,
1821 1821 self._dirstatevalidate,
1822 1822 sparsematchfn,
1823 1823 self.nodeconstants,
1824 1824 use_dirstate_v2,
1825 1825 use_tracked_hint=use_tracked_hint,
1826 1826 )
1827 1827
1828 1828 def _dirstatevalidate(self, node):
1829 1829 okay = True
1830 1830 try:
1831 1831 self.changelog.rev(node)
1832 1832 except error.LookupError:
1833 1833 # If the parent are unknown it might just be because the changelog
1834 1834 # in memory is lagging behind the dirstate in memory. So try to
1835 1835 # refresh the changelog first.
1836 1836 #
1837 1837 # We only do so if we don't hold the lock, if we do hold the lock
1838 1838 # the invalidation at that time should have taken care of this and
1839 1839 # something is very fishy.
1840 1840 if self.currentlock() is None:
1841 1841 self.invalidate()
1842 1842 try:
1843 1843 self.changelog.rev(node)
1844 1844 except error.LookupError:
1845 1845 okay = False
1846 1846 else:
1847 1847 # XXX we should consider raising an error here.
1848 1848 okay = False
1849 1849 if okay:
1850 1850 return node
1851 1851 else:
1852 1852 if not self._dirstatevalidatewarned:
1853 1853 self._dirstatevalidatewarned = True
1854 1854 self.ui.warn(
1855 1855 _(b"warning: ignoring unknown working parent %s!\n")
1856 1856 % short(node)
1857 1857 )
1858 1858 return self.nullid
1859 1859
1860 1860 @storecache(narrowspec.FILENAME)
1861 1861 def narrowpats(self):
1862 1862 """matcher patterns for this repository's narrowspec
1863 1863
1864 1864 A tuple of (includes, excludes).
1865 1865 """
1866 1866 # the narrow management should probably move into its own object
1867 1867 val = self._pending_narrow_pats
1868 1868 if val is None:
1869 1869 val = narrowspec.load(self)
1870 1870 return val
1871 1871
1872 1872 @storecache(narrowspec.FILENAME)
1873 1873 def _storenarrowmatch(self):
1874 1874 if requirementsmod.NARROW_REQUIREMENT not in self.requirements:
1875 1875 return matchmod.always()
1876 1876 include, exclude = self.narrowpats
1877 1877 return narrowspec.match(self.root, include=include, exclude=exclude)
1878 1878
1879 1879 @storecache(narrowspec.FILENAME)
1880 1880 def _narrowmatch(self):
1881 1881 if requirementsmod.NARROW_REQUIREMENT not in self.requirements:
1882 1882 return matchmod.always()
1883 1883 narrowspec.checkworkingcopynarrowspec(self)
1884 1884 include, exclude = self.narrowpats
1885 1885 return narrowspec.match(self.root, include=include, exclude=exclude)
1886 1886
1887 1887 def narrowmatch(self, match=None, includeexact=False):
1888 1888 """matcher corresponding the the repo's narrowspec
1889 1889
1890 1890 If `match` is given, then that will be intersected with the narrow
1891 1891 matcher.
1892 1892
1893 1893 If `includeexact` is True, then any exact matches from `match` will
1894 1894 be included even if they're outside the narrowspec.
1895 1895 """
1896 1896 if match:
1897 1897 if includeexact and not self._narrowmatch.always():
1898 1898 # do not exclude explicitly-specified paths so that they can
1899 1899 # be warned later on
1900 1900 em = matchmod.exact(match.files())
1901 1901 nm = matchmod.unionmatcher([self._narrowmatch, em])
1902 1902 return matchmod.intersectmatchers(match, nm)
1903 1903 return matchmod.intersectmatchers(match, self._narrowmatch)
1904 1904 return self._narrowmatch
1905 1905
1906 1906 def setnarrowpats(self, newincludes, newexcludes):
1907 1907 narrowspec.save(self, newincludes, newexcludes)
1908 1908 self.invalidate(clearfilecache=True)
1909 1909
1910 1910 @unfilteredpropertycache
1911 1911 def _quick_access_changeid_null(self):
1912 1912 return {
1913 1913 b'null': (nullrev, self.nodeconstants.nullid),
1914 1914 nullrev: (nullrev, self.nodeconstants.nullid),
1915 1915 self.nullid: (nullrev, self.nullid),
1916 1916 }
1917 1917
1918 1918 @unfilteredpropertycache
1919 1919 def _quick_access_changeid_wc(self):
1920 1920 # also fast path access to the working copy parents
1921 1921 # however, only do it for filter that ensure wc is visible.
1922 1922 quick = self._quick_access_changeid_null.copy()
1923 1923 cl = self.unfiltered().changelog
1924 1924 for node in self.dirstate.parents():
1925 1925 if node == self.nullid:
1926 1926 continue
1927 1927 rev = cl.index.get_rev(node)
1928 1928 if rev is None:
1929 1929 # unknown working copy parent case:
1930 1930 #
1931 1931 # skip the fast path and let higher code deal with it
1932 1932 continue
1933 1933 pair = (rev, node)
1934 1934 quick[rev] = pair
1935 1935 quick[node] = pair
1936 1936 # also add the parents of the parents
1937 1937 for r in cl.parentrevs(rev):
1938 1938 if r == nullrev:
1939 1939 continue
1940 1940 n = cl.node(r)
1941 1941 pair = (r, n)
1942 1942 quick[r] = pair
1943 1943 quick[n] = pair
1944 1944 p1node = self.dirstate.p1()
1945 1945 if p1node != self.nullid:
1946 1946 quick[b'.'] = quick[p1node]
1947 1947 return quick
1948 1948
1949 1949 @unfilteredmethod
1950 1950 def _quick_access_changeid_invalidate(self):
1951 1951 if '_quick_access_changeid_wc' in vars(self):
1952 1952 del self.__dict__['_quick_access_changeid_wc']
1953 1953
1954 1954 @property
1955 1955 def _quick_access_changeid(self):
1956 1956 """an helper dictionnary for __getitem__ calls
1957 1957
1958 1958 This contains a list of symbol we can recognise right away without
1959 1959 further processing.
1960 1960 """
1961 1961 if self.filtername in repoview.filter_has_wc:
1962 1962 return self._quick_access_changeid_wc
1963 1963 return self._quick_access_changeid_null
1964 1964
1965 1965 def __getitem__(self, changeid):
1966 1966 # dealing with special cases
1967 1967 if changeid is None:
1968 1968 return context.workingctx(self)
1969 1969 if isinstance(changeid, context.basectx):
1970 1970 return changeid
1971 1971
1972 1972 # dealing with multiple revisions
1973 1973 if isinstance(changeid, slice):
1974 1974 # wdirrev isn't contiguous so the slice shouldn't include it
1975 1975 return [
1976 1976 self[i]
1977 1977 for i in range(*changeid.indices(len(self)))
1978 1978 if i not in self.changelog.filteredrevs
1979 1979 ]
1980 1980
1981 1981 # dealing with some special values
1982 1982 quick_access = self._quick_access_changeid.get(changeid)
1983 1983 if quick_access is not None:
1984 1984 rev, node = quick_access
1985 1985 return context.changectx(self, rev, node, maybe_filtered=False)
1986 1986 if changeid == b'tip':
1987 1987 node = self.changelog.tip()
1988 1988 rev = self.changelog.rev(node)
1989 1989 return context.changectx(self, rev, node)
1990 1990
1991 1991 # dealing with arbitrary values
1992 1992 try:
1993 1993 if isinstance(changeid, int):
1994 1994 node = self.changelog.node(changeid)
1995 1995 rev = changeid
1996 1996 elif changeid == b'.':
1997 1997 # this is a hack to delay/avoid loading obsmarkers
1998 1998 # when we know that '.' won't be hidden
1999 1999 node = self.dirstate.p1()
2000 2000 rev = self.unfiltered().changelog.rev(node)
2001 2001 elif len(changeid) == self.nodeconstants.nodelen:
2002 2002 try:
2003 2003 node = changeid
2004 2004 rev = self.changelog.rev(changeid)
2005 2005 except error.FilteredLookupError:
2006 2006 changeid = hex(changeid) # for the error message
2007 2007 raise
2008 2008 except LookupError:
2009 2009 # check if it might have come from damaged dirstate
2010 2010 #
2011 2011 # XXX we could avoid the unfiltered if we had a recognizable
2012 2012 # exception for filtered changeset access
2013 2013 if (
2014 2014 self.local()
2015 2015 and changeid in self.unfiltered().dirstate.parents()
2016 2016 ):
2017 2017 msg = _(b"working directory has unknown parent '%s'!")
2018 2018 raise error.Abort(msg % short(changeid))
2019 2019 changeid = hex(changeid) # for the error message
2020 2020 raise
2021 2021
2022 2022 elif len(changeid) == 2 * self.nodeconstants.nodelen:
2023 2023 node = bin(changeid)
2024 2024 rev = self.changelog.rev(node)
2025 2025 else:
2026 2026 raise error.ProgrammingError(
2027 2027 b"unsupported changeid '%s' of type %s"
2028 2028 % (changeid, pycompat.bytestr(type(changeid)))
2029 2029 )
2030 2030
2031 2031 return context.changectx(self, rev, node)
2032 2032
2033 2033 except (error.FilteredIndexError, error.FilteredLookupError):
2034 2034 raise error.FilteredRepoLookupError(
2035 2035 _(b"filtered revision '%s'") % pycompat.bytestr(changeid)
2036 2036 )
2037 2037 except (IndexError, LookupError):
2038 2038 raise error.RepoLookupError(
2039 2039 _(b"unknown revision '%s'") % pycompat.bytestr(changeid)
2040 2040 )
2041 2041 except error.WdirUnsupported:
2042 2042 return context.workingctx(self)
2043 2043
2044 2044 def __contains__(self, changeid):
2045 2045 """True if the given changeid exists"""
2046 2046 try:
2047 2047 self[changeid]
2048 2048 return True
2049 2049 except error.RepoLookupError:
2050 2050 return False
2051 2051
2052 2052 def __nonzero__(self):
2053 2053 return True
2054 2054
2055 2055 __bool__ = __nonzero__
2056 2056
2057 2057 def __len__(self):
2058 2058 # no need to pay the cost of repoview.changelog
2059 2059 unfi = self.unfiltered()
2060 2060 return len(unfi.changelog)
2061 2061
2062 2062 def __iter__(self):
2063 2063 return iter(self.changelog)
2064 2064
2065 2065 def revs(self, expr: bytes, *args):
2066 2066 """Find revisions matching a revset.
2067 2067
2068 2068 The revset is specified as a string ``expr`` that may contain
2069 2069 %-formatting to escape certain types. See ``revsetlang.formatspec``.
2070 2070
2071 2071 Revset aliases from the configuration are not expanded. To expand
2072 2072 user aliases, consider calling ``scmutil.revrange()`` or
2073 2073 ``repo.anyrevs([expr], user=True)``.
2074 2074
2075 2075 Returns a smartset.abstractsmartset, which is a list-like interface
2076 2076 that contains integer revisions.
2077 2077 """
2078 2078 tree = revsetlang.spectree(expr, *args)
2079 2079 return revset.makematcher(tree)(self)
2080 2080
2081 2081 def set(self, expr: bytes, *args):
2082 2082 """Find revisions matching a revset and emit changectx instances.
2083 2083
2084 2084 This is a convenience wrapper around ``revs()`` that iterates the
2085 2085 result and is a generator of changectx instances.
2086 2086
2087 2087 Revset aliases from the configuration are not expanded. To expand
2088 2088 user aliases, consider calling ``scmutil.revrange()``.
2089 2089 """
2090 2090 for r in self.revs(expr, *args):
2091 2091 yield self[r]
2092 2092
2093 2093 def anyrevs(self, specs: bytes, user=False, localalias=None):
2094 2094 """Find revisions matching one of the given revsets.
2095 2095
2096 2096 Revset aliases from the configuration are not expanded by default. To
2097 2097 expand user aliases, specify ``user=True``. To provide some local
2098 2098 definitions overriding user aliases, set ``localalias`` to
2099 2099 ``{name: definitionstring}``.
2100 2100 """
2101 2101 if specs == [b'null']:
2102 2102 return revset.baseset([nullrev])
2103 2103 if specs == [b'.']:
2104 2104 quick_data = self._quick_access_changeid.get(b'.')
2105 2105 if quick_data is not None:
2106 2106 return revset.baseset([quick_data[0]])
2107 2107 if user:
2108 2108 m = revset.matchany(
2109 2109 self.ui,
2110 2110 specs,
2111 2111 lookup=revset.lookupfn(self),
2112 2112 localalias=localalias,
2113 2113 )
2114 2114 else:
2115 2115 m = revset.matchany(None, specs, localalias=localalias)
2116 2116 return m(self)
2117 2117
2118 2118 def url(self) -> bytes:
2119 2119 return b'file:' + self.root
2120 2120
2121 2121 def hook(self, name, throw=False, **args):
2122 2122 """Call a hook, passing this repo instance.
2123 2123
2124 2124 This a convenience method to aid invoking hooks. Extensions likely
2125 2125 won't call this unless they have registered a custom hook or are
2126 2126 replacing code that is expected to call a hook.
2127 2127 """
2128 2128 return hook.hook(self.ui, self, name, throw, **args)
2129 2129
2130 2130 @filteredpropertycache
2131 2131 def _tagscache(self):
2132 2132 """Returns a tagscache object that contains various tags related
2133 2133 caches."""
2134 2134
2135 2135 # This simplifies its cache management by having one decorated
2136 2136 # function (this one) and the rest simply fetch things from it.
2137 2137 class tagscache:
2138 2138 def __init__(self):
2139 2139 # These two define the set of tags for this repository. tags
2140 2140 # maps tag name to node; tagtypes maps tag name to 'global' or
2141 2141 # 'local'. (Global tags are defined by .hgtags across all
2142 2142 # heads, and local tags are defined in .hg/localtags.)
2143 2143 # They constitute the in-memory cache of tags.
2144 2144 self.tags = self.tagtypes = None
2145 2145
2146 2146 self.nodetagscache = self.tagslist = None
2147 2147
2148 2148 cache = tagscache()
2149 2149 cache.tags, cache.tagtypes = self._findtags()
2150 2150
2151 2151 return cache
2152 2152
2153 2153 def tags(self):
2154 2154 '''return a mapping of tag to node'''
2155 2155 t = {}
2156 2156 if self.changelog.filteredrevs:
2157 2157 tags, tt = self._findtags()
2158 2158 else:
2159 2159 tags = self._tagscache.tags
2160 2160 rev = self.changelog.rev
2161 2161 for k, v in tags.items():
2162 2162 try:
2163 2163 # ignore tags to unknown nodes
2164 2164 rev(v)
2165 2165 t[k] = v
2166 2166 except (error.LookupError, ValueError):
2167 2167 pass
2168 2168 return t
2169 2169
2170 2170 def _findtags(self):
2171 2171 """Do the hard work of finding tags. Return a pair of dicts
2172 2172 (tags, tagtypes) where tags maps tag name to node, and tagtypes
2173 2173 maps tag name to a string like \'global\' or \'local\'.
2174 2174 Subclasses or extensions are free to add their own tags, but
2175 2175 should be aware that the returned dicts will be retained for the
2176 2176 duration of the localrepo object."""
2177 2177
2178 2178 # XXX what tagtype should subclasses/extensions use? Currently
2179 2179 # mq and bookmarks add tags, but do not set the tagtype at all.
2180 2180 # Should each extension invent its own tag type? Should there
2181 2181 # be one tagtype for all such "virtual" tags? Or is the status
2182 2182 # quo fine?
2183 2183
2184 2184 # map tag name to (node, hist)
2185 2185 alltags = tagsmod.findglobaltags(self.ui, self)
2186 2186 # map tag name to tag type
2187 2187 tagtypes = {tag: b'global' for tag in alltags}
2188 2188
2189 2189 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
2190 2190
2191 2191 # Build the return dicts. Have to re-encode tag names because
2192 2192 # the tags module always uses UTF-8 (in order not to lose info
2193 2193 # writing to the cache), but the rest of Mercurial wants them in
2194 2194 # local encoding.
2195 2195 tags = {}
2196 2196 for name, (node, hist) in alltags.items():
2197 2197 if node != self.nullid:
2198 2198 tags[encoding.tolocal(name)] = node
2199 2199 tags[b'tip'] = self.changelog.tip()
2200 2200 tagtypes = {
2201 2201 encoding.tolocal(name): value for (name, value) in tagtypes.items()
2202 2202 }
2203 2203 return (tags, tagtypes)
2204 2204
2205 2205 def tagtype(self, tagname):
2206 2206 """
2207 2207 return the type of the given tag. result can be:
2208 2208
2209 2209 'local' : a local tag
2210 2210 'global' : a global tag
2211 2211 None : tag does not exist
2212 2212 """
2213 2213
2214 2214 return self._tagscache.tagtypes.get(tagname)
2215 2215
2216 2216 def tagslist(self):
2217 2217 '''return a list of tags ordered by revision'''
2218 2218 if not self._tagscache.tagslist:
2219 2219 l = []
2220 2220 for t, n in self.tags().items():
2221 2221 l.append((self.changelog.rev(n), t, n))
2222 2222 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
2223 2223
2224 2224 return self._tagscache.tagslist
2225 2225
2226 2226 def nodetags(self, node):
2227 2227 '''return the tags associated with a node'''
2228 2228 if not self._tagscache.nodetagscache:
2229 2229 nodetagscache = {}
2230 2230 for t, n in self._tagscache.tags.items():
2231 2231 nodetagscache.setdefault(n, []).append(t)
2232 2232 for tags in nodetagscache.values():
2233 2233 tags.sort()
2234 2234 self._tagscache.nodetagscache = nodetagscache
2235 2235 return self._tagscache.nodetagscache.get(node, [])
2236 2236
2237 2237 def nodebookmarks(self, node):
2238 2238 """return the list of bookmarks pointing to the specified node"""
2239 2239 return self._bookmarks.names(node)
2240 2240
2241 2241 def branchmap(self):
2242 2242 """returns a dictionary {branch: [branchheads]} with branchheads
2243 2243 ordered by increasing revision number"""
2244 2244 return self._branchcaches[self]
2245 2245
2246 2246 @unfilteredmethod
2247 2247 def revbranchcache(self):
2248 2248 if not self._revbranchcache:
2249 2249 self._revbranchcache = branchmap.revbranchcache(self.unfiltered())
2250 2250 return self._revbranchcache
2251 2251
2252 2252 def register_changeset(self, rev, changelogrevision):
2253 2253 self.revbranchcache().setdata(rev, changelogrevision)
2254 2254
2255 2255 def branchtip(self, branch, ignoremissing=False):
2256 2256 """return the tip node for a given branch
2257 2257
2258 2258 If ignoremissing is True, then this method will not raise an error.
2259 2259 This is helpful for callers that only expect None for a missing branch
2260 2260 (e.g. namespace).
2261 2261
2262 2262 """
2263 2263 try:
2264 2264 return self.branchmap().branchtip(branch)
2265 2265 except KeyError:
2266 2266 if not ignoremissing:
2267 2267 raise error.RepoLookupError(_(b"unknown branch '%s'") % branch)
2268 2268 else:
2269 2269 pass
2270 2270
2271 2271 def lookup(self, key):
2272 2272 node = scmutil.revsymbol(self, key).node()
2273 2273 if node is None:
2274 2274 raise error.RepoLookupError(_(b"unknown revision '%s'") % key)
2275 2275 return node
2276 2276
2277 2277 def lookupbranch(self, key):
2278 2278 if self.branchmap().hasbranch(key):
2279 2279 return key
2280 2280
2281 2281 return scmutil.revsymbol(self, key).branch()
2282 2282
2283 2283 def known(self, nodes):
2284 2284 cl = self.changelog
2285 2285 get_rev = cl.index.get_rev
2286 2286 filtered = cl.filteredrevs
2287 2287 result = []
2288 2288 for n in nodes:
2289 2289 r = get_rev(n)
2290 2290 resp = not (r is None or r in filtered)
2291 2291 result.append(resp)
2292 2292 return result
2293 2293
2294 2294 def local(self):
2295 2295 return self
2296 2296
2297 2297 def publishing(self):
2298 2298 # it's safe (and desirable) to trust the publish flag unconditionally
2299 2299 # so that we don't finalize changes shared between users via ssh or nfs
2300 2300 return self.ui.configbool(b'phases', b'publish', untrusted=True)
2301 2301
2302 2302 def cancopy(self):
2303 2303 # so statichttprepo's override of local() works
2304 2304 if not self.local():
2305 2305 return False
2306 2306 if not self.publishing():
2307 2307 return True
2308 2308 # if publishing we can't copy if there is filtered content
2309 2309 return not self.filtered(b'visible').changelog.filteredrevs
2310 2310
2311 2311 def shared(self):
2312 2312 '''the type of shared repository (None if not shared)'''
2313 2313 if self.sharedpath != self.path:
2314 2314 return b'store'
2315 2315 return None
2316 2316
2317 2317 def wjoin(self, f: bytes, *insidef: bytes) -> bytes:
2318 2318 return self.vfs.reljoin(self.root, f, *insidef)
2319 2319
2320 2320 def setparents(self, p1, p2=None):
2321 2321 if p2 is None:
2322 2322 p2 = self.nullid
2323 2323 self[None].setparents(p1, p2)
2324 2324 self._quick_access_changeid_invalidate()
2325 2325
2326 2326 def filectx(self, path: bytes, changeid=None, fileid=None, changectx=None):
2327 2327 """changeid must be a changeset revision, if specified.
2328 2328 fileid can be a file revision or node."""
2329 2329 return context.filectx(
2330 2330 self, path, changeid, fileid, changectx=changectx
2331 2331 )
2332 2332
2333 2333 def getcwd(self) -> bytes:
2334 2334 return self.dirstate.getcwd()
2335 2335
2336 2336 def pathto(self, f: bytes, cwd: Optional[bytes] = None) -> bytes:
2337 2337 return self.dirstate.pathto(f, cwd)
2338 2338
2339 2339 def _loadfilter(self, filter):
2340 2340 if filter not in self._filterpats:
2341 2341 l = []
2342 2342 for pat, cmd in self.ui.configitems(filter):
2343 2343 if cmd == b'!':
2344 2344 continue
2345 2345 mf = matchmod.match(self.root, b'', [pat])
2346 2346 fn = None
2347 2347 params = cmd
2348 2348 for name, filterfn in self._datafilters.items():
2349 2349 if cmd.startswith(name):
2350 2350 fn = filterfn
2351 2351 params = cmd[len(name) :].lstrip()
2352 2352 break
2353 2353 if not fn:
2354 2354 fn = lambda s, c, **kwargs: procutil.filter(s, c)
2355 2355 fn.__name__ = 'commandfilter'
2356 2356 # Wrap old filters not supporting keyword arguments
2357 2357 if not pycompat.getargspec(fn)[2]:
2358 2358 oldfn = fn
2359 2359 fn = lambda s, c, oldfn=oldfn, **kwargs: oldfn(s, c)
2360 2360 fn.__name__ = 'compat-' + oldfn.__name__
2361 2361 l.append((mf, fn, params))
2362 2362 self._filterpats[filter] = l
2363 2363 return self._filterpats[filter]
2364 2364
2365 2365 def _filter(self, filterpats, filename, data):
2366 2366 for mf, fn, cmd in filterpats:
2367 2367 if mf(filename):
2368 2368 self.ui.debug(
2369 2369 b"filtering %s through %s\n"
2370 2370 % (filename, cmd or pycompat.sysbytes(fn.__name__))
2371 2371 )
2372 2372 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
2373 2373 break
2374 2374
2375 2375 return data
2376 2376
2377 2377 @unfilteredpropertycache
2378 2378 def _encodefilterpats(self):
2379 2379 return self._loadfilter(b'encode')
2380 2380
2381 2381 @unfilteredpropertycache
2382 2382 def _decodefilterpats(self):
2383 2383 return self._loadfilter(b'decode')
2384 2384
2385 2385 def adddatafilter(self, name, filter):
2386 2386 self._datafilters[name] = filter
2387 2387
2388 2388 def wread(self, filename: bytes) -> bytes:
2389 2389 if self.wvfs.islink(filename):
2390 2390 data = self.wvfs.readlink(filename)
2391 2391 else:
2392 2392 data = self.wvfs.read(filename)
2393 2393 return self._filter(self._encodefilterpats, filename, data)
2394 2394
2395 2395 def wwrite(
2396 2396 self,
2397 2397 filename: bytes,
2398 2398 data: bytes,
2399 2399 flags: bytes,
2400 2400 backgroundclose=False,
2401 2401 **kwargs,
2402 2402 ) -> int:
2403 2403 """write ``data`` into ``filename`` in the working directory
2404 2404
2405 2405 This returns length of written (maybe decoded) data.
2406 2406 """
2407 2407 data = self._filter(self._decodefilterpats, filename, data)
2408 2408 if b'l' in flags:
2409 2409 self.wvfs.symlink(data, filename)
2410 2410 else:
2411 2411 self.wvfs.write(
2412 2412 filename, data, backgroundclose=backgroundclose, **kwargs
2413 2413 )
2414 2414 if b'x' in flags:
2415 2415 self.wvfs.setflags(filename, False, True)
2416 2416 else:
2417 2417 self.wvfs.setflags(filename, False, False)
2418 2418 return len(data)
2419 2419
2420 2420 def wwritedata(self, filename: bytes, data: bytes) -> bytes:
2421 2421 return self._filter(self._decodefilterpats, filename, data)
2422 2422
2423 2423 def currenttransaction(self):
2424 2424 """return the current transaction or None if non exists"""
2425 2425 if self._transref:
2426 2426 tr = self._transref()
2427 2427 else:
2428 2428 tr = None
2429 2429
2430 2430 if tr and tr.running():
2431 2431 return tr
2432 2432 return None
2433 2433
2434 2434 def transaction(self, desc, report=None):
2435 2435 if self.ui.configbool(b'devel', b'all-warnings') or self.ui.configbool(
2436 2436 b'devel', b'check-locks'
2437 2437 ):
2438 2438 if self._currentlock(self._lockref) is None:
2439 2439 raise error.ProgrammingError(b'transaction requires locking')
2440 2440 tr = self.currenttransaction()
2441 2441 if tr is not None:
2442 2442 return tr.nest(name=desc)
2443 2443
2444 2444 # abort here if the journal already exists
2445 2445 if self.svfs.exists(b"journal"):
2446 2446 raise error.RepoError(
2447 2447 _(b"abandoned transaction found"),
2448 2448 hint=_(b"run 'hg recover' to clean up transaction"),
2449 2449 )
2450 2450
2451 2451 # At that point your dirstate should be clean:
2452 2452 #
2453 2453 # - If you don't have the wlock, why would you still have a dirty
2454 2454 # dirstate ?
2455 2455 #
2456 2456 # - If you hold the wlock, you should not be opening a transaction in
2457 2457 # the middle of a `distate.changing_*` block. The transaction needs to
2458 2458 # be open before that and wrap the change-context.
2459 2459 #
2460 2460 # - If you are not within a `dirstate.changing_*` context, why is our
2461 2461 # dirstate dirty?
2462 2462 if self.dirstate._dirty:
2463 2463 m = "cannot open a transaction with a dirty dirstate"
2464 2464 raise error.ProgrammingError(m)
2465 2465
2466 2466 idbase = b"%.40f#%f" % (random.random(), time.time())
2467 2467 ha = hex(hashutil.sha1(idbase).digest())
2468 2468 txnid = b'TXN:' + ha
2469 2469 self.hook(b'pretxnopen', throw=True, txnname=desc, txnid=txnid)
2470 2470
2471 2471 self._writejournal(desc)
2472 2472 if report:
2473 2473 rp = report
2474 2474 else:
2475 2475 rp = self.ui.warn
2476 2476 vfsmap = self.vfs_map
2477 2477 # we must avoid cyclic reference between repo and transaction.
2478 2478 reporef = weakref.ref(self)
2479 2479 # Code to track tag movement
2480 2480 #
2481 2481 # Since tags are all handled as file content, it is actually quite hard
2482 2482 # to track these movement from a code perspective. So we fallback to a
2483 2483 # tracking at the repository level. One could envision to track changes
2484 2484 # to the '.hgtags' file through changegroup apply but that fails to
2485 2485 # cope with case where transaction expose new heads without changegroup
2486 2486 # being involved (eg: phase movement).
2487 2487 #
2488 2488 # For now, We gate the feature behind a flag since this likely comes
2489 2489 # with performance impacts. The current code run more often than needed
2490 2490 # and do not use caches as much as it could. The current focus is on
2491 2491 # the behavior of the feature so we disable it by default. The flag
2492 2492 # will be removed when we are happy with the performance impact.
2493 2493 #
2494 2494 # Once this feature is no longer experimental move the following
2495 2495 # documentation to the appropriate help section:
2496 2496 #
2497 2497 # The ``HG_TAG_MOVED`` variable will be set if the transaction touched
2498 2498 # tags (new or changed or deleted tags). In addition the details of
2499 2499 # these changes are made available in a file at:
2500 2500 # ``REPOROOT/.hg/changes/tags.changes``.
2501 2501 # Make sure you check for HG_TAG_MOVED before reading that file as it
2502 2502 # might exist from a previous transaction even if no tag were touched
2503 2503 # in this one. Changes are recorded in a line base format::
2504 2504 #
2505 2505 # <action> <hex-node> <tag-name>\n
2506 2506 #
2507 2507 # Actions are defined as follow:
2508 2508 # "-R": tag is removed,
2509 2509 # "+A": tag is added,
2510 2510 # "-M": tag is moved (old value),
2511 2511 # "+M": tag is moved (new value),
2512 2512 tracktags = lambda x: None
2513 2513 # experimental config: experimental.hook-track-tags
2514 2514 shouldtracktags = self.ui.configbool(
2515 2515 b'experimental', b'hook-track-tags'
2516 2516 )
2517 2517 if desc != b'strip' and shouldtracktags:
2518 2518 oldheads = self.changelog.headrevs()
2519 2519
2520 2520 def tracktags(tr2):
2521 2521 repo = reporef()
2522 2522 assert repo is not None # help pytype
2523 2523 oldfnodes = tagsmod.fnoderevs(repo.ui, repo, oldheads)
2524 2524 newheads = repo.changelog.headrevs()
2525 2525 newfnodes = tagsmod.fnoderevs(repo.ui, repo, newheads)
2526 2526 # notes: we compare lists here.
2527 2527 # As we do it only once buiding set would not be cheaper
2528 2528 changes = tagsmod.difftags(repo.ui, repo, oldfnodes, newfnodes)
2529 2529 if changes:
2530 2530 tr2.hookargs[b'tag_moved'] = b'1'
2531 2531 with repo.vfs(
2532 2532 b'changes/tags.changes', b'w', atomictemp=True
2533 2533 ) as changesfile:
2534 2534 # note: we do not register the file to the transaction
2535 2535 # because we needs it to still exist on the transaction
2536 2536 # is close (for txnclose hooks)
2537 2537 tagsmod.writediff(changesfile, changes)
2538 2538
2539 2539 def validate(tr2):
2540 2540 """will run pre-closing hooks"""
2541 2541 # XXX the transaction API is a bit lacking here so we take a hacky
2542 2542 # path for now
2543 2543 #
2544 2544 # We cannot add this as a "pending" hooks since the 'tr.hookargs'
2545 2545 # dict is copied before these run. In addition we needs the data
2546 2546 # available to in memory hooks too.
2547 2547 #
2548 2548 # Moreover, we also need to make sure this runs before txnclose
2549 2549 # hooks and there is no "pending" mechanism that would execute
2550 2550 # logic only if hooks are about to run.
2551 2551 #
2552 2552 # Fixing this limitation of the transaction is also needed to track
2553 2553 # other families of changes (bookmarks, phases, obsolescence).
2554 2554 #
2555 2555 # This will have to be fixed before we remove the experimental
2556 2556 # gating.
2557 2557 tracktags(tr2)
2558 2558 repo = reporef()
2559 2559 assert repo is not None # help pytype
2560 2560
2561 2561 singleheadopt = (b'experimental', b'single-head-per-branch')
2562 2562 singlehead = repo.ui.configbool(*singleheadopt)
2563 2563 if singlehead:
2564 2564 singleheadsub = repo.ui.configsuboptions(*singleheadopt)[1]
2565 2565 accountclosed = singleheadsub.get(
2566 2566 b"account-closed-heads", False
2567 2567 )
2568 2568 if singleheadsub.get(b"public-changes-only", False):
2569 2569 filtername = b"immutable"
2570 2570 else:
2571 2571 filtername = b"visible"
2572 2572 scmutil.enforcesinglehead(
2573 2573 repo, tr2, desc, accountclosed, filtername
2574 2574 )
2575 2575 if hook.hashook(repo.ui, b'pretxnclose-bookmark'):
2576 2576 for name, (old, new) in sorted(
2577 2577 tr.changes[b'bookmarks'].items()
2578 2578 ):
2579 2579 args = tr.hookargs.copy()
2580 2580 args.update(bookmarks.preparehookargs(name, old, new))
2581 2581 repo.hook(
2582 2582 b'pretxnclose-bookmark',
2583 2583 throw=True,
2584 2584 **pycompat.strkwargs(args),
2585 2585 )
2586 2586 if hook.hashook(repo.ui, b'pretxnclose-phase'):
2587 2587 cl = repo.unfiltered().changelog
2588 2588 for revs, (old, new) in tr.changes[b'phases']:
2589 2589 for rev in revs:
2590 2590 args = tr.hookargs.copy()
2591 2591 node = hex(cl.node(rev))
2592 2592 args.update(phases.preparehookargs(node, old, new))
2593 2593 repo.hook(
2594 2594 b'pretxnclose-phase',
2595 2595 throw=True,
2596 2596 **pycompat.strkwargs(args),
2597 2597 )
2598 2598
2599 2599 repo.hook(
2600 2600 b'pretxnclose', throw=True, **pycompat.strkwargs(tr.hookargs)
2601 2601 )
2602 2602
2603 2603 def releasefn(tr, success):
2604 2604 repo = reporef()
2605 2605 if repo is None:
2606 2606 # If the repo has been GC'd (and this release function is being
2607 2607 # called from transaction.__del__), there's not much we can do,
2608 2608 # so just leave the unfinished transaction there and let the
2609 2609 # user run `hg recover`.
2610 2610 return
2611 2611 if success:
2612 2612 # this should be explicitly invoked here, because
2613 2613 # in-memory changes aren't written out at closing
2614 2614 # transaction, if tr.addfilegenerator (via
2615 2615 # dirstate.write or so) isn't invoked while
2616 2616 # transaction running
2617 2617 repo.dirstate.write(None)
2618 2618 else:
2619 2619 # discard all changes (including ones already written
2620 2620 # out) in this transaction
2621 2621 repo.invalidate(clearfilecache=True)
2622 2622
2623 2623 tr = transaction.transaction(
2624 2624 rp,
2625 2625 self.svfs,
2626 2626 vfsmap,
2627 2627 b"journal",
2628 2628 b"undo",
2629 2629 lambda: None,
2630 2630 self.store.createmode,
2631 2631 validator=validate,
2632 2632 releasefn=releasefn,
2633 2633 checkambigfiles=_cachedfiles,
2634 2634 name=desc,
2635 2635 )
2636 2636 for vfs_id, path in self._journalfiles():
2637 2637 tr.add_journal(vfs_id, path)
2638 2638 tr.changes[b'origrepolen'] = len(self)
2639 2639 tr.changes[b'obsmarkers'] = set()
2640 2640 tr.changes[b'phases'] = []
2641 2641 tr.changes[b'bookmarks'] = {}
2642 2642
2643 2643 tr.hookargs[b'txnid'] = txnid
2644 2644 tr.hookargs[b'txnname'] = desc
2645 2645 tr.hookargs[b'changes'] = tr.changes
2646 2646 # note: writing the fncache only during finalize mean that the file is
2647 2647 # outdated when running hooks. As fncache is used for streaming clone,
2648 2648 # this is not expected to break anything that happen during the hooks.
2649 2649 tr.addfinalize(b'flush-fncache', self.store.write)
2650 2650
2651 2651 def txnclosehook(tr2):
2652 2652 """To be run if transaction is successful, will schedule a hook run"""
2653 2653 # Don't reference tr2 in hook() so we don't hold a reference.
2654 2654 # This reduces memory consumption when there are multiple
2655 2655 # transactions per lock. This can likely go away if issue5045
2656 2656 # fixes the function accumulation.
2657 2657 hookargs = tr2.hookargs
2658 2658
2659 2659 def hookfunc(unused_success):
2660 2660 repo = reporef()
2661 2661 assert repo is not None # help pytype
2662 2662
2663 2663 if hook.hashook(repo.ui, b'txnclose-bookmark'):
2664 2664 bmchanges = sorted(tr.changes[b'bookmarks'].items())
2665 2665 for name, (old, new) in bmchanges:
2666 2666 args = tr.hookargs.copy()
2667 2667 args.update(bookmarks.preparehookargs(name, old, new))
2668 2668 repo.hook(
2669 2669 b'txnclose-bookmark',
2670 2670 throw=False,
2671 2671 **pycompat.strkwargs(args),
2672 2672 )
2673 2673
2674 2674 if hook.hashook(repo.ui, b'txnclose-phase'):
2675 2675 cl = repo.unfiltered().changelog
2676 2676 phasemv = sorted(
2677 2677 tr.changes[b'phases'], key=lambda r: r[0][0]
2678 2678 )
2679 2679 for revs, (old, new) in phasemv:
2680 2680 for rev in revs:
2681 2681 args = tr.hookargs.copy()
2682 2682 node = hex(cl.node(rev))
2683 2683 args.update(phases.preparehookargs(node, old, new))
2684 2684 repo.hook(
2685 2685 b'txnclose-phase',
2686 2686 throw=False,
2687 2687 **pycompat.strkwargs(args),
2688 2688 )
2689 2689
2690 2690 repo.hook(
2691 2691 b'txnclose', throw=False, **pycompat.strkwargs(hookargs)
2692 2692 )
2693 2693
2694 2694 repo = reporef()
2695 2695 assert repo is not None # help pytype
2696 2696 repo._afterlock(hookfunc)
2697 2697
2698 2698 tr.addfinalize(b'txnclose-hook', txnclosehook)
2699 2699 # Include a leading "-" to make it happen before the transaction summary
2700 2700 # reports registered via scmutil.registersummarycallback() whose names
2701 2701 # are 00-txnreport etc. That way, the caches will be warm when the
2702 2702 # callbacks run.
2703 2703 tr.addpostclose(b'-warm-cache', self._buildcacheupdater(tr))
2704 2704
2705 2705 def txnaborthook(tr2):
2706 2706 """To be run if transaction is aborted"""
2707 2707 repo = reporef()
2708 2708 assert repo is not None # help pytype
2709 2709 repo.hook(
2710 2710 b'txnabort', throw=False, **pycompat.strkwargs(tr2.hookargs)
2711 2711 )
2712 2712
2713 2713 tr.addabort(b'txnabort-hook', txnaborthook)
2714 2714 # avoid eager cache invalidation. in-memory data should be identical
2715 2715 # to stored data if transaction has no error.
2716 2716 tr.addpostclose(b'refresh-filecachestats', self._refreshfilecachestats)
2717 2717 self._transref = weakref.ref(tr)
2718 2718 scmutil.registersummarycallback(self, tr, desc)
2719 2719 # This only exist to deal with the need of rollback to have viable
2720 2720 # parents at the end of the operation. So backup viable parents at the
2721 2721 # time of this operation.
2722 2722 #
2723 2723 # We only do it when the `wlock` is taken, otherwise other might be
2724 2724 # altering the dirstate under us.
2725 2725 #
2726 2726 # This is really not a great way to do this (first, because we cannot
2727 2727 # always do it). There are more viable alternative that exists
2728 2728 #
2729 2729 # - backing only the working copy parent in a dedicated files and doing
2730 2730 # a clean "keep-update" to them on `hg rollback`.
2731 2731 #
2732 2732 # - slightly changing the behavior an applying a logic similar to "hg
2733 2733 # strip" to pick a working copy destination on `hg rollback`
2734 2734 if self.currentwlock() is not None:
2735 2735 ds = self.dirstate
2736 2736 if not self.vfs.exists(b'branch'):
2737 2737 # force a file to be written if None exist
2738 2738 ds.setbranch(b'default', None)
2739 2739
2740 2740 def backup_dirstate(tr):
2741 2741 for f in ds.all_file_names():
2742 2742 # hardlink backup is okay because `dirstate` is always
2743 2743 # atomically written and possible data file are append only
2744 2744 # and resistant to trailing data.
2745 2745 tr.addbackup(f, hardlink=True, location=b'plain')
2746 2746
2747 2747 tr.addvalidator(b'dirstate-backup', backup_dirstate)
2748 2748 return tr
2749 2749
2750 2750 def _journalfiles(self):
2751 2751 return (
2752 2752 (self.svfs, b'journal'),
2753 2753 (self.vfs, b'journal.desc'),
2754 2754 )
2755 2755
2756 2756 def undofiles(self):
2757 2757 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
2758 2758
2759 2759 @unfilteredmethod
2760 2760 def _writejournal(self, desc):
2761 2761 self.vfs.write(b"journal.desc", b"%d\n%s\n" % (len(self), desc))
2762 2762
2763 2763 def recover(self):
2764 2764 with self.lock():
2765 2765 if self.svfs.exists(b"journal"):
2766 2766 self.ui.status(_(b"rolling back interrupted transaction\n"))
2767 2767 vfsmap = self.vfs_map
2768 2768 transaction.rollback(
2769 2769 self.svfs,
2770 2770 vfsmap,
2771 2771 b"journal",
2772 2772 self.ui.warn,
2773 2773 checkambigfiles=_cachedfiles,
2774 2774 )
2775 2775 self.invalidate()
2776 2776 return True
2777 2777 else:
2778 2778 self.ui.warn(_(b"no interrupted transaction available\n"))
2779 2779 return False
2780 2780
2781 2781 def rollback(self, dryrun=False, force=False):
2782 2782 wlock = lock = None
2783 2783 try:
2784 2784 wlock = self.wlock()
2785 2785 lock = self.lock()
2786 2786 if self.svfs.exists(b"undo"):
2787 2787 return self._rollback(dryrun, force)
2788 2788 else:
2789 2789 self.ui.warn(_(b"no rollback information available\n"))
2790 2790 return 1
2791 2791 finally:
2792 2792 release(lock, wlock)
2793 2793
2794 2794 @unfilteredmethod # Until we get smarter cache management
2795 2795 def _rollback(self, dryrun, force):
2796 2796 ui = self.ui
2797 2797
2798 2798 parents = self.dirstate.parents()
2799 2799 try:
2800 2800 args = self.vfs.read(b'undo.desc').splitlines()
2801 2801 (oldlen, desc, detail) = (int(args[0]), args[1], None)
2802 2802 if len(args) >= 3:
2803 2803 detail = args[2]
2804 2804 oldtip = oldlen - 1
2805 2805
2806 2806 if detail and ui.verbose:
2807 2807 msg = _(
2808 2808 b'repository tip rolled back to revision %d'
2809 2809 b' (undo %s: %s)\n'
2810 2810 ) % (oldtip, desc, detail)
2811 2811 else:
2812 2812 msg = _(
2813 2813 b'repository tip rolled back to revision %d (undo %s)\n'
2814 2814 ) % (oldtip, desc)
2815 2815 parentgone = any(self[p].rev() > oldtip for p in parents)
2816 2816 except IOError:
2817 2817 msg = _(b'rolling back unknown transaction\n')
2818 2818 desc = None
2819 2819 parentgone = True
2820 2820
2821 2821 if not force and self[b'.'] != self[b'tip'] and desc == b'commit':
2822 2822 raise error.Abort(
2823 2823 _(
2824 2824 b'rollback of last commit while not checked out '
2825 2825 b'may lose data'
2826 2826 ),
2827 2827 hint=_(b'use -f to force'),
2828 2828 )
2829 2829
2830 2830 ui.status(msg)
2831 2831 if dryrun:
2832 2832 return 0
2833 2833
2834 2834 self.destroying()
2835 2835 vfsmap = self.vfs_map
2836 2836 skip_journal_pattern = None
2837 2837 if not parentgone:
2838 2838 skip_journal_pattern = RE_SKIP_DIRSTATE_ROLLBACK
2839 2839 transaction.rollback(
2840 2840 self.svfs,
2841 2841 vfsmap,
2842 2842 b'undo',
2843 2843 ui.warn,
2844 2844 checkambigfiles=_cachedfiles,
2845 2845 skip_journal_pattern=skip_journal_pattern,
2846 2846 )
2847 2847 self.invalidate()
2848 2848 self.dirstate.invalidate()
2849 2849
2850 2850 if parentgone:
2851 2851 # replace this with some explicit parent update in the future.
2852 2852 has_node = self.changelog.index.has_node
2853 2853 if not all(has_node(p) for p in self.dirstate._pl):
2854 2854 # There was no dirstate to backup initially, we need to drop
2855 2855 # the existing one.
2856 2856 with self.dirstate.changing_parents(self):
2857 2857 self.dirstate.setparents(self.nullid)
2858 2858 self.dirstate.clear()
2859 2859
2860 2860 parents = tuple([p.rev() for p in self[None].parents()])
2861 2861 if len(parents) > 1:
2862 2862 ui.status(
2863 2863 _(
2864 2864 b'working directory now based on '
2865 2865 b'revisions %d and %d\n'
2866 2866 )
2867 2867 % parents
2868 2868 )
2869 2869 else:
2870 2870 ui.status(
2871 2871 _(b'working directory now based on revision %d\n') % parents
2872 2872 )
2873 2873 mergestatemod.mergestate.clean(self)
2874 2874
2875 2875 # TODO: if we know which new heads may result from this rollback, pass
2876 2876 # them to destroy(), which will prevent the branchhead cache from being
2877 2877 # invalidated.
2878 2878 self.destroyed()
2879 2879 return 0
2880 2880
2881 2881 def _buildcacheupdater(self, newtransaction):
2882 2882 """called during transaction to build the callback updating cache
2883 2883
2884 2884 Lives on the repository to help extension who might want to augment
2885 2885 this logic. For this purpose, the created transaction is passed to the
2886 2886 method.
2887 2887 """
2888 2888 # we must avoid cyclic reference between repo and transaction.
2889 2889 reporef = weakref.ref(self)
2890 2890
2891 2891 def updater(tr):
2892 2892 repo = reporef()
2893 2893 assert repo is not None # help pytype
2894 2894 repo.updatecaches(tr)
2895 2895
2896 2896 return updater
2897 2897
2898 2898 @unfilteredmethod
2899 2899 def updatecaches(self, tr=None, full=False, caches=None):
2900 2900 """warm appropriate caches
2901 2901
2902 2902 If this function is called after a transaction closed. The transaction
2903 2903 will be available in the 'tr' argument. This can be used to selectively
2904 2904 update caches relevant to the changes in that transaction.
2905 2905
2906 2906 If 'full' is set, make sure all caches the function knows about have
2907 2907 up-to-date data. Even the ones usually loaded more lazily.
2908 2908
2909 2909 The `full` argument can take a special "post-clone" value. In this case
2910 2910 the cache warming is made after a clone and of the slower cache might
2911 2911 be skipped, namely the `.fnodetags` one. This argument is 5.8 specific
2912 2912 as we plan for a cleaner way to deal with this for 5.9.
2913 2913 """
2914 2914 if tr is not None and tr.hookargs.get(b'source') == b'strip':
2915 2915 # During strip, many caches are invalid but
2916 2916 # later call to `destroyed` will refresh them.
2917 2917 return
2918 2918
2919 2919 unfi = self.unfiltered()
2920 2920
2921 2921 if caches is None:
2922 2922 caches = repository.CACHES_DEFAULT
2923 2923
2924 2924 if repository.CACHE_BRANCHMAP_SERVED in caches:
2925 2925 if tr is None or tr.changes[b'origrepolen'] < len(self):
2926 2926 self.ui.debug(b'updating the branch cache\n')
2927 2927 self._branchcaches.update_disk(self.filtered(b'served'))
2928 2928 self._branchcaches.update_disk(self.filtered(b'served.hidden'))
2929 2929
2930 2930 if repository.CACHE_CHANGELOG_CACHE in caches:
2931 2931 self.changelog.update_caches(transaction=tr)
2932 2932
2933 2933 if repository.CACHE_MANIFESTLOG_CACHE in caches:
2934 2934 self.manifestlog.update_caches(transaction=tr)
2935 2935 for entry in self.store.walk():
2936 2936 if not entry.is_revlog:
2937 2937 continue
2938 2938 if not entry.is_manifestlog:
2939 2939 continue
2940 2940 manifestrevlog = entry.get_revlog_instance(self).get_revlog()
2941 2941 if manifestrevlog is not None:
2942 2942 manifestrevlog.update_caches(transaction=tr)
2943 2943
2944 2944 if repository.CACHE_REV_BRANCH in caches:
2945 2945 rbc = unfi.revbranchcache()
2946 2946 for r in unfi.changelog:
2947 2947 rbc.branchinfo(r)
2948 2948 rbc.write()
2949 2949
2950 2950 if repository.CACHE_FULL_MANIFEST in caches:
2951 2951 # ensure the working copy parents are in the manifestfulltextcache
2952 2952 for ctx in self[b'.'].parents():
2953 2953 ctx.manifest() # accessing the manifest is enough
2954 2954
2955 2955 if repository.CACHE_FILE_NODE_TAGS in caches:
2956 2956 # accessing fnode cache warms the cache
2957 2957 tagsmod.fnoderevs(self.ui, unfi, unfi.changelog.revs())
2958 2958
2959 2959 if repository.CACHE_TAGS_DEFAULT in caches:
2960 2960 # accessing tags warm the cache
2961 2961 self.tags()
2962 2962 if repository.CACHE_TAGS_SERVED in caches:
2963 2963 self.filtered(b'served').tags()
2964 2964
2965 2965 if repository.CACHE_BRANCHMAP_ALL in caches:
2966 2966 # The CACHE_BRANCHMAP_ALL updates lazily-loaded caches immediately,
2967 2967 # so we're forcing a write to cause these caches to be warmed up
2968 2968 # even if they haven't explicitly been requested yet (if they've
2969 2969 # never been used by hg, they won't ever have been written, even if
2970 2970 # they're a subset of another kind of cache that *has* been used).
2971 2971 for filt in repoview.filtertable.keys():
2972 2972 filtered = self.filtered(filt)
2973 2973 self._branchcaches.update_disk(filtered)
2974 2974
2975 2975 # flush all possibly delayed write.
2976 2976 self._branchcaches.write_dirty(self)
2977 2977
2978 2978 def invalidatecaches(self):
2979 2979 if '_tagscache' in vars(self):
2980 2980 # can't use delattr on proxy
2981 2981 del self.__dict__['_tagscache']
2982 2982
2983 2983 self._branchcaches.clear()
2984 2984 self.invalidatevolatilesets()
2985 2985 self._sparsesignaturecache.clear()
2986 2986
2987 2987 def invalidatevolatilesets(self):
2988 2988 self.filteredrevcache.clear()
2989 2989 obsolete.clearobscaches(self)
2990 2990 self._quick_access_changeid_invalidate()
2991 2991
2992 2992 def invalidatedirstate(self):
2993 2993 """Invalidates the dirstate, causing the next call to dirstate
2994 2994 to check if it was modified since the last time it was read,
2995 2995 rereading it if it has.
2996 2996
2997 2997 This is different to dirstate.invalidate() that it doesn't always
2998 2998 rereads the dirstate. Use dirstate.invalidate() if you want to
2999 2999 explicitly read the dirstate again (i.e. restoring it to a previous
3000 3000 known good state)."""
3001 3001 unfi = self.unfiltered()
3002 3002 if 'dirstate' in unfi.__dict__:
3003 3003 assert not self.dirstate.is_changing_any
3004 3004 del unfi.__dict__['dirstate']
3005 3005
3006 3006 def invalidate(self, clearfilecache=False):
3007 3007 """Invalidates both store and non-store parts other than dirstate
3008 3008
3009 3009 If a transaction is running, invalidation of store is omitted,
3010 3010 because discarding in-memory changes might cause inconsistency
3011 3011 (e.g. incomplete fncache causes unintentional failure, but
3012 3012 redundant one doesn't).
3013 3013 """
3014 3014 unfiltered = self.unfiltered() # all file caches are stored unfiltered
3015 3015 for k in list(self._filecache.keys()):
3016 3016 if (
3017 3017 k == b'changelog'
3018 3018 and self.currenttransaction()
3019 3019 and self.changelog.is_delaying
3020 3020 ):
3021 3021 # The changelog object may store unwritten revisions. We don't
3022 3022 # want to lose them.
3023 3023 # TODO: Solve the problem instead of working around it.
3024 3024 continue
3025 3025
3026 3026 if clearfilecache:
3027 3027 del self._filecache[k]
3028 3028 try:
3029 3029 # XXX ideally, the key would be a unicode string to match the
3030 3030 # fact it refers to an attribut name. However changing this was
3031 3031 # a bit a scope creep compared to the series cleaning up
3032 3032 # del/set/getattr so we kept thing simple here.
3033 3033 delattr(unfiltered, pycompat.sysstr(k))
3034 3034 except AttributeError:
3035 3035 pass
3036 3036 self.invalidatecaches()
3037 3037 if not self.currenttransaction():
3038 3038 # TODO: Changing contents of store outside transaction
3039 3039 # causes inconsistency. We should make in-memory store
3040 3040 # changes detectable, and abort if changed.
3041 3041 self.store.invalidatecaches()
3042 3042
3043 3043 def invalidateall(self):
3044 3044 """Fully invalidates both store and non-store parts, causing the
3045 3045 subsequent operation to reread any outside changes."""
3046 3046 # extension should hook this to invalidate its caches
3047 3047 self.invalidate()
3048 3048 self.invalidatedirstate()
3049 3049
3050 3050 @unfilteredmethod
3051 3051 def _refreshfilecachestats(self, tr):
3052 3052 """Reload stats of cached files so that they are flagged as valid"""
3053 3053 for k, ce in self._filecache.items():
3054 3054 k = pycompat.sysstr(k)
3055 3055 if k == 'dirstate' or k not in self.__dict__:
3056 3056 continue
3057 3057 ce.refresh()
3058 3058
3059 3059 def _lock(
3060 3060 self,
3061 3061 vfs,
3062 3062 lockname,
3063 3063 wait,
3064 3064 releasefn,
3065 3065 acquirefn,
3066 3066 desc,
3067 3067 ):
3068 3068 timeout = 0
3069 3069 warntimeout = 0
3070 3070 if wait:
3071 3071 timeout = self.ui.configint(b"ui", b"timeout")
3072 3072 warntimeout = self.ui.configint(b"ui", b"timeout.warn")
3073 3073 # internal config: ui.signal-safe-lock
3074 3074 signalsafe = self.ui.configbool(b'ui', b'signal-safe-lock')
3075 sync_file = self.ui.config(b'devel', b'lock-wait-sync-file')
3076 if not sync_file:
3077 sync_file = None
3075 3078
3076 3079 l = lockmod.trylock(
3077 3080 self.ui,
3078 3081 vfs,
3079 3082 lockname,
3080 3083 timeout,
3081 3084 warntimeout,
3082 3085 releasefn=releasefn,
3083 3086 acquirefn=acquirefn,
3084 3087 desc=desc,
3085 3088 signalsafe=signalsafe,
3089 devel_wait_sync_file=sync_file,
3086 3090 )
3087 3091 return l
3088 3092
3089 3093 def _afterlock(self, callback):
3090 3094 """add a callback to be run when the repository is fully unlocked
3091 3095
3092 3096 The callback will be executed when the outermost lock is released
3093 3097 (with wlock being higher level than 'lock')."""
3094 3098 for ref in (self._wlockref, self._lockref):
3095 3099 l = ref and ref()
3096 3100 if l and l.held:
3097 3101 l.postrelease.append(callback)
3098 3102 break
3099 3103 else: # no lock have been found.
3100 3104 callback(True)
3101 3105
3102 3106 def lock(self, wait=True):
3103 3107 """Lock the repository store (.hg/store) and return a weak reference
3104 3108 to the lock. Use this before modifying the store (e.g. committing or
3105 3109 stripping). If you are opening a transaction, get a lock as well.)
3106 3110
3107 3111 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
3108 3112 'wlock' first to avoid a dead-lock hazard."""
3109 3113 l = self._currentlock(self._lockref)
3110 3114 if l is not None:
3111 3115 l.lock()
3112 3116 return l
3113 3117
3114 3118 l = self._lock(
3115 3119 vfs=self.svfs,
3116 3120 lockname=b"lock",
3117 3121 wait=wait,
3118 3122 releasefn=None,
3119 3123 acquirefn=self.invalidate,
3120 3124 desc=_(b'repository %s') % self.origroot,
3121 3125 )
3122 3126 self._lockref = weakref.ref(l)
3123 3127 return l
3124 3128
3125 3129 def wlock(self, wait=True):
3126 3130 """Lock the non-store parts of the repository (everything under
3127 3131 .hg except .hg/store) and return a weak reference to the lock.
3128 3132
3129 3133 Use this before modifying files in .hg.
3130 3134
3131 3135 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
3132 3136 'wlock' first to avoid a dead-lock hazard."""
3133 3137 l = self._wlockref() if self._wlockref else None
3134 3138 if l is not None and l.held:
3135 3139 l.lock()
3136 3140 return l
3137 3141
3138 3142 # We do not need to check for non-waiting lock acquisition. Such
3139 3143 # acquisition would not cause dead-lock as they would just fail.
3140 3144 if wait and (
3141 3145 self.ui.configbool(b'devel', b'all-warnings')
3142 3146 or self.ui.configbool(b'devel', b'check-locks')
3143 3147 ):
3144 3148 if self._currentlock(self._lockref) is not None:
3145 3149 self.ui.develwarn(b'"wlock" acquired after "lock"')
3146 3150
3147 3151 def unlock():
3148 3152 if self.dirstate.is_changing_any:
3149 3153 msg = b"wlock release in the middle of a changing parents"
3150 3154 self.ui.develwarn(msg)
3151 3155 self.dirstate.invalidate()
3152 3156 else:
3153 3157 if self.dirstate._dirty:
3154 3158 msg = b"dirty dirstate on wlock release"
3155 3159 self.ui.develwarn(msg)
3156 3160 self.dirstate.write(None)
3157 3161
3158 3162 unfi = self.unfiltered()
3159 3163 if 'dirstate' in unfi.__dict__:
3160 3164 del unfi.__dict__['dirstate']
3161 3165
3162 3166 l = self._lock(
3163 3167 self.vfs,
3164 3168 b"wlock",
3165 3169 wait,
3166 3170 unlock,
3167 3171 self.invalidatedirstate,
3168 3172 _(b'working directory of %s') % self.origroot,
3169 3173 )
3170 3174 self._wlockref = weakref.ref(l)
3171 3175 return l
3172 3176
3173 3177 def _currentlock(self, lockref):
3174 3178 """Returns the lock if it's held, or None if it's not."""
3175 3179 if lockref is None:
3176 3180 return None
3177 3181 l = lockref()
3178 3182 if l is None or not l.held:
3179 3183 return None
3180 3184 return l
3181 3185
3182 3186 def currentwlock(self):
3183 3187 """Returns the wlock if it's held, or None if it's not."""
3184 3188 return self._currentlock(self._wlockref)
3185 3189
3186 3190 def currentlock(self):
3187 3191 """Returns the lock if it's held, or None if it's not."""
3188 3192 return self._currentlock(self._lockref)
3189 3193
3190 3194 def checkcommitpatterns(self, wctx, match, status, fail):
3191 3195 """check for commit arguments that aren't committable"""
3192 3196 if match.isexact() or match.prefix():
3193 3197 matched = set(status.modified + status.added + status.removed)
3194 3198
3195 3199 for f in match.files():
3196 3200 f = self.dirstate.normalize(f)
3197 3201 if f == b'.' or f in matched or f in wctx.substate:
3198 3202 continue
3199 3203 if f in status.deleted:
3200 3204 fail(f, _(b'file not found!'))
3201 3205 # Is it a directory that exists or used to exist?
3202 3206 if self.wvfs.isdir(f) or wctx.p1().hasdir(f):
3203 3207 d = f + b'/'
3204 3208 for mf in matched:
3205 3209 if mf.startswith(d):
3206 3210 break
3207 3211 else:
3208 3212 fail(f, _(b"no match under directory!"))
3209 3213 elif f not in self.dirstate:
3210 3214 fail(f, _(b"file not tracked!"))
3211 3215
3212 3216 @unfilteredmethod
3213 3217 def commit(
3214 3218 self,
3215 3219 text=b"",
3216 3220 user=None,
3217 3221 date=None,
3218 3222 match=None,
3219 3223 force=False,
3220 3224 editor=None,
3221 3225 extra=None,
3222 3226 ):
3223 3227 """Add a new revision to current repository.
3224 3228
3225 3229 Revision information is gathered from the working directory,
3226 3230 match can be used to filter the committed files. If editor is
3227 3231 supplied, it is called to get a commit message.
3228 3232 """
3229 3233 if extra is None:
3230 3234 extra = {}
3231 3235
3232 3236 def fail(f, msg):
3233 3237 raise error.InputError(b'%s: %s' % (f, msg))
3234 3238
3235 3239 if not match:
3236 3240 match = matchmod.always()
3237 3241
3238 3242 if not force:
3239 3243 match.bad = fail
3240 3244
3241 3245 # lock() for recent changelog (see issue4368)
3242 3246 with self.wlock(), self.lock():
3243 3247 wctx = self[None]
3244 3248 merge = len(wctx.parents()) > 1
3245 3249
3246 3250 if not force and merge and not match.always():
3247 3251 raise error.Abort(
3248 3252 _(
3249 3253 b'cannot partially commit a merge '
3250 3254 b'(do not specify files or patterns)'
3251 3255 )
3252 3256 )
3253 3257
3254 3258 status = self.status(match=match, clean=force)
3255 3259 if force:
3256 3260 status.modified.extend(
3257 3261 status.clean
3258 3262 ) # mq may commit clean files
3259 3263
3260 3264 # check subrepos
3261 3265 subs, commitsubs, newstate = subrepoutil.precommit(
3262 3266 self.ui, wctx, status, match, force=force
3263 3267 )
3264 3268
3265 3269 # make sure all explicit patterns are matched
3266 3270 if not force:
3267 3271 self.checkcommitpatterns(wctx, match, status, fail)
3268 3272
3269 3273 cctx = context.workingcommitctx(
3270 3274 self, status, text, user, date, extra
3271 3275 )
3272 3276
3273 3277 ms = mergestatemod.mergestate.read(self)
3274 3278 mergeutil.checkunresolved(ms)
3275 3279
3276 3280 # internal config: ui.allowemptycommit
3277 3281 if cctx.isempty() and not self.ui.configbool(
3278 3282 b'ui', b'allowemptycommit'
3279 3283 ):
3280 3284 self.ui.debug(b'nothing to commit, clearing merge state\n')
3281 3285 ms.reset()
3282 3286 return None
3283 3287
3284 3288 if merge and cctx.deleted():
3285 3289 raise error.Abort(_(b"cannot commit merge with missing files"))
3286 3290
3287 3291 if editor:
3288 3292 cctx._text = editor(self, cctx, subs)
3289 3293 edited = text != cctx._text
3290 3294
3291 3295 # Save commit message in case this transaction gets rolled back
3292 3296 # (e.g. by a pretxncommit hook). Leave the content alone on
3293 3297 # the assumption that the user will use the same editor again.
3294 3298 msg_path = self.savecommitmessage(cctx._text)
3295 3299
3296 3300 # commit subs and write new state
3297 3301 if subs:
3298 3302 uipathfn = scmutil.getuipathfn(self)
3299 3303 for s in sorted(commitsubs):
3300 3304 sub = wctx.sub(s)
3301 3305 self.ui.status(
3302 3306 _(b'committing subrepository %s\n')
3303 3307 % uipathfn(subrepoutil.subrelpath(sub))
3304 3308 )
3305 3309 sr = sub.commit(cctx._text, user, date)
3306 3310 newstate[s] = (newstate[s][0], sr)
3307 3311 subrepoutil.writestate(self, newstate)
3308 3312
3309 3313 p1, p2 = self.dirstate.parents()
3310 3314 hookp1, hookp2 = hex(p1), (p2 != self.nullid and hex(p2) or b'')
3311 3315 try:
3312 3316 self.hook(
3313 3317 b"precommit", throw=True, parent1=hookp1, parent2=hookp2
3314 3318 )
3315 3319 with self.transaction(b'commit'):
3316 3320 ret = self.commitctx(cctx, True)
3317 3321 # update bookmarks, dirstate and mergestate
3318 3322 bookmarks.update(self, [p1, p2], ret)
3319 3323 cctx.markcommitted(ret)
3320 3324 ms.reset()
3321 3325 except: # re-raises
3322 3326 if edited:
3323 3327 self.ui.write(
3324 3328 _(b'note: commit message saved in %s\n') % msg_path
3325 3329 )
3326 3330 self.ui.write(
3327 3331 _(
3328 3332 b"note: use 'hg commit --logfile "
3329 3333 b"%s --edit' to reuse it\n"
3330 3334 )
3331 3335 % msg_path
3332 3336 )
3333 3337 raise
3334 3338
3335 3339 def commithook(unused_success):
3336 3340 # hack for command that use a temporary commit (eg: histedit)
3337 3341 # temporary commit got stripped before hook release
3338 3342 if self.changelog.hasnode(ret):
3339 3343 self.hook(
3340 3344 b"commit", node=hex(ret), parent1=hookp1, parent2=hookp2
3341 3345 )
3342 3346
3343 3347 self._afterlock(commithook)
3344 3348 return ret
3345 3349
3346 3350 @unfilteredmethod
3347 3351 def commitctx(self, ctx, error=False, origctx=None):
3348 3352 return commit.commitctx(self, ctx, error=error, origctx=origctx)
3349 3353
3350 3354 @unfilteredmethod
3351 3355 def destroying(self):
3352 3356 """Inform the repository that nodes are about to be destroyed.
3353 3357 Intended for use by strip and rollback, so there's a common
3354 3358 place for anything that has to be done before destroying history.
3355 3359
3356 3360 This is mostly useful for saving state that is in memory and waiting
3357 3361 to be flushed when the current lock is released. Because a call to
3358 3362 destroyed is imminent, the repo will be invalidated causing those
3359 3363 changes to stay in memory (waiting for the next unlock), or vanish
3360 3364 completely.
3361 3365 """
3362 3366 # When using the same lock to commit and strip, the phasecache is left
3363 3367 # dirty after committing. Then when we strip, the repo is invalidated,
3364 3368 # causing those changes to disappear.
3365 3369 if '_phasecache' in vars(self):
3366 3370 self._phasecache.write(self)
3367 3371
3368 3372 @unfilteredmethod
3369 3373 def destroyed(self):
3370 3374 """Inform the repository that nodes have been destroyed.
3371 3375 Intended for use by strip and rollback, so there's a common
3372 3376 place for anything that has to be done after destroying history.
3373 3377 """
3374 3378 # refresh all repository caches
3375 3379 self.updatecaches()
3376 3380
3377 3381 # Ensure the persistent tag cache is updated. Doing it now
3378 3382 # means that the tag cache only has to worry about destroyed
3379 3383 # heads immediately after a strip/rollback. That in turn
3380 3384 # guarantees that "cachetip == currenttip" (comparing both rev
3381 3385 # and node) always means no nodes have been added or destroyed.
3382 3386
3383 3387 # XXX this is suboptimal when qrefresh'ing: we strip the current
3384 3388 # head, refresh the tag cache, then immediately add a new head.
3385 3389 # But I think doing it this way is necessary for the "instant
3386 3390 # tag cache retrieval" case to work.
3387 3391 self.invalidate()
3388 3392
3389 3393 def status(
3390 3394 self,
3391 3395 node1=b'.',
3392 3396 node2=None,
3393 3397 match=None,
3394 3398 ignored=False,
3395 3399 clean=False,
3396 3400 unknown=False,
3397 3401 listsubrepos=False,
3398 3402 ):
3399 3403 '''a convenience method that calls node1.status(node2)'''
3400 3404 return self[node1].status(
3401 3405 node2, match, ignored, clean, unknown, listsubrepos
3402 3406 )
3403 3407
3404 3408 def addpostdsstatus(self, ps):
3405 3409 """Add a callback to run within the wlock, at the point at which status
3406 3410 fixups happen.
3407 3411
3408 3412 On status completion, callback(wctx, status) will be called with the
3409 3413 wlock held, unless the dirstate has changed from underneath or the wlock
3410 3414 couldn't be grabbed.
3411 3415
3412 3416 Callbacks should not capture and use a cached copy of the dirstate --
3413 3417 it might change in the meanwhile. Instead, they should access the
3414 3418 dirstate via wctx.repo().dirstate.
3415 3419
3416 3420 This list is emptied out after each status run -- extensions should
3417 3421 make sure it adds to this list each time dirstate.status is called.
3418 3422 Extensions should also make sure they don't call this for statuses
3419 3423 that don't involve the dirstate.
3420 3424 """
3421 3425
3422 3426 # The list is located here for uniqueness reasons -- it is actually
3423 3427 # managed by the workingctx, but that isn't unique per-repo.
3424 3428 self._postdsstatus.append(ps)
3425 3429
3426 3430 def postdsstatus(self):
3427 3431 """Used by workingctx to get the list of post-dirstate-status hooks."""
3428 3432 return self._postdsstatus
3429 3433
3430 3434 def clearpostdsstatus(self):
3431 3435 """Used by workingctx to clear post-dirstate-status hooks."""
3432 3436 del self._postdsstatus[:]
3433 3437
3434 3438 def heads(self, start=None):
3435 3439 if start is None:
3436 3440 cl = self.changelog
3437 3441 headrevs = reversed(cl.headrevs())
3438 3442 return [cl.node(rev) for rev in headrevs]
3439 3443
3440 3444 heads = self.changelog.heads(start)
3441 3445 # sort the output in rev descending order
3442 3446 return sorted(heads, key=self.changelog.rev, reverse=True)
3443 3447
3444 3448 def branchheads(self, branch=None, start=None, closed=False):
3445 3449 """return a (possibly filtered) list of heads for the given branch
3446 3450
3447 3451 Heads are returned in topological order, from newest to oldest.
3448 3452 If branch is None, use the dirstate branch.
3449 3453 If start is not None, return only heads reachable from start.
3450 3454 If closed is True, return heads that are marked as closed as well.
3451 3455 """
3452 3456 if branch is None:
3453 3457 branch = self[None].branch()
3454 3458 branches = self.branchmap()
3455 3459 if not branches.hasbranch(branch):
3456 3460 return []
3457 3461 # the cache returns heads ordered lowest to highest
3458 3462 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
3459 3463 if start is not None:
3460 3464 # filter out the heads that cannot be reached from startrev
3461 3465 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
3462 3466 bheads = [h for h in bheads if h in fbheads]
3463 3467 return bheads
3464 3468
3465 3469 def branches(self, nodes):
3466 3470 if not nodes:
3467 3471 nodes = [self.changelog.tip()]
3468 3472 b = []
3469 3473 for n in nodes:
3470 3474 t = n
3471 3475 while True:
3472 3476 p = self.changelog.parents(n)
3473 3477 if p[1] != self.nullid or p[0] == self.nullid:
3474 3478 b.append((t, n, p[0], p[1]))
3475 3479 break
3476 3480 n = p[0]
3477 3481 return b
3478 3482
3479 3483 def between(self, pairs):
3480 3484 r = []
3481 3485
3482 3486 for top, bottom in pairs:
3483 3487 n, l, i = top, [], 0
3484 3488 f = 1
3485 3489
3486 3490 while n != bottom and n != self.nullid:
3487 3491 p = self.changelog.parents(n)[0]
3488 3492 if i == f:
3489 3493 l.append(n)
3490 3494 f = f * 2
3491 3495 n = p
3492 3496 i += 1
3493 3497
3494 3498 r.append(l)
3495 3499
3496 3500 return r
3497 3501
3498 3502 def checkpush(self, pushop):
3499 3503 """Extensions can override this function if additional checks have
3500 3504 to be performed before pushing, or call it if they override push
3501 3505 command.
3502 3506 """
3503 3507
3504 3508 @unfilteredpropertycache
3505 3509 def prepushoutgoinghooks(self):
3506 3510 """Return util.hooks consists of a pushop with repo, remote, outgoing
3507 3511 methods, which are called before pushing changesets.
3508 3512 """
3509 3513 return util.hooks()
3510 3514
3511 3515 def pushkey(self, namespace, key, old, new):
3512 3516 try:
3513 3517 tr = self.currenttransaction()
3514 3518 hookargs = {}
3515 3519 if tr is not None:
3516 3520 hookargs.update(tr.hookargs)
3517 3521 hookargs = pycompat.strkwargs(hookargs)
3518 3522 hookargs['namespace'] = namespace
3519 3523 hookargs['key'] = key
3520 3524 hookargs['old'] = old
3521 3525 hookargs['new'] = new
3522 3526 self.hook(b'prepushkey', throw=True, **hookargs)
3523 3527 except error.HookAbort as exc:
3524 3528 self.ui.write_err(_(b"pushkey-abort: %s\n") % exc)
3525 3529 if exc.hint:
3526 3530 self.ui.write_err(_(b"(%s)\n") % exc.hint)
3527 3531 return False
3528 3532 self.ui.debug(b'pushing key for "%s:%s"\n' % (namespace, key))
3529 3533 ret = pushkey.push(self, namespace, key, old, new)
3530 3534
3531 3535 def runhook(unused_success):
3532 3536 self.hook(
3533 3537 b'pushkey',
3534 3538 namespace=namespace,
3535 3539 key=key,
3536 3540 old=old,
3537 3541 new=new,
3538 3542 ret=ret,
3539 3543 )
3540 3544
3541 3545 self._afterlock(runhook)
3542 3546 return ret
3543 3547
3544 3548 def listkeys(self, namespace):
3545 3549 self.hook(b'prelistkeys', throw=True, namespace=namespace)
3546 3550 self.ui.debug(b'listing keys for "%s"\n' % namespace)
3547 3551 values = pushkey.list(self, namespace)
3548 3552 self.hook(b'listkeys', namespace=namespace, values=values)
3549 3553 return values
3550 3554
3551 3555 def debugwireargs(self, one, two, three=None, four=None, five=None):
3552 3556 '''used to test argument passing over the wire'''
3553 3557 return b"%s %s %s %s %s" % (
3554 3558 one,
3555 3559 two,
3556 3560 pycompat.bytestr(three),
3557 3561 pycompat.bytestr(four),
3558 3562 pycompat.bytestr(five),
3559 3563 )
3560 3564
3561 3565 def savecommitmessage(self, text):
3562 3566 fp = self.vfs(b'last-message.txt', b'wb')
3563 3567 try:
3564 3568 fp.write(text)
3565 3569 finally:
3566 3570 fp.close()
3567 3571 return self.pathto(fp.name[len(self.root) + 1 :])
3568 3572
3569 3573 def register_wanted_sidedata(self, category):
3570 3574 if repository.REPO_FEATURE_SIDE_DATA not in self.features:
3571 3575 # Only revlogv2 repos can want sidedata.
3572 3576 return
3573 3577 self._wanted_sidedata.add(pycompat.bytestr(category))
3574 3578
3575 3579 def register_sidedata_computer(
3576 3580 self, kind, category, keys, computer, flags, replace=False
3577 3581 ):
3578 3582 if kind not in revlogconst.ALL_KINDS:
3579 3583 msg = _(b"unexpected revlog kind '%s'.")
3580 3584 raise error.ProgrammingError(msg % kind)
3581 3585 category = pycompat.bytestr(category)
3582 3586 already_registered = category in self._sidedata_computers.get(kind, [])
3583 3587 if already_registered and not replace:
3584 3588 msg = _(
3585 3589 b"cannot register a sidedata computer twice for category '%s'."
3586 3590 )
3587 3591 raise error.ProgrammingError(msg % category)
3588 3592 if replace and not already_registered:
3589 3593 msg = _(
3590 3594 b"cannot replace a sidedata computer that isn't registered "
3591 3595 b"for category '%s'."
3592 3596 )
3593 3597 raise error.ProgrammingError(msg % category)
3594 3598 self._sidedata_computers.setdefault(kind, {})
3595 3599 self._sidedata_computers[kind][category] = (keys, computer, flags)
3596 3600
3597 3601
3598 3602 def undoname(fn: bytes) -> bytes:
3599 3603 base, name = os.path.split(fn)
3600 3604 assert name.startswith(b'journal')
3601 3605 return os.path.join(base, name.replace(b'journal', b'undo', 1))
3602 3606
3603 3607
3604 3608 def instance(ui, path: bytes, create, intents=None, createopts=None):
3605 3609 # prevent cyclic import localrepo -> upgrade -> localrepo
3606 3610 from . import upgrade
3607 3611
3608 3612 localpath = urlutil.urllocalpath(path)
3609 3613 if create:
3610 3614 createrepository(ui, localpath, createopts=createopts)
3611 3615
3612 3616 def repo_maker():
3613 3617 return makelocalrepository(ui, localpath, intents=intents)
3614 3618
3615 3619 repo = repo_maker()
3616 3620 repo = upgrade.may_auto_upgrade(repo, repo_maker)
3617 3621 return repo
3618 3622
3619 3623
3620 3624 def islocal(path: bytes) -> bool:
3621 3625 return True
3622 3626
3623 3627
3624 3628 def defaultcreateopts(ui, createopts=None):
3625 3629 """Populate the default creation options for a repository.
3626 3630
3627 3631 A dictionary of explicitly requested creation options can be passed
3628 3632 in. Missing keys will be populated.
3629 3633 """
3630 3634 createopts = dict(createopts or {})
3631 3635
3632 3636 if b'backend' not in createopts:
3633 3637 # experimental config: storage.new-repo-backend
3634 3638 createopts[b'backend'] = ui.config(b'storage', b'new-repo-backend')
3635 3639
3636 3640 return createopts
3637 3641
3638 3642
3639 3643 def clone_requirements(ui, createopts, srcrepo):
3640 3644 """clone the requirements of a local repo for a local clone
3641 3645
3642 3646 The store requirements are unchanged while the working copy requirements
3643 3647 depends on the configuration
3644 3648 """
3645 3649 target_requirements = set()
3646 3650 if not srcrepo.requirements:
3647 3651 # this is a legacy revlog "v0" repository, we cannot do anything fancy
3648 3652 # with it.
3649 3653 return target_requirements
3650 3654 createopts = defaultcreateopts(ui, createopts=createopts)
3651 3655 for r in newreporequirements(ui, createopts):
3652 3656 if r in requirementsmod.WORKING_DIR_REQUIREMENTS:
3653 3657 target_requirements.add(r)
3654 3658
3655 3659 for r in srcrepo.requirements:
3656 3660 if r not in requirementsmod.WORKING_DIR_REQUIREMENTS:
3657 3661 target_requirements.add(r)
3658 3662 return target_requirements
3659 3663
3660 3664
3661 3665 def newreporequirements(ui, createopts):
3662 3666 """Determine the set of requirements for a new local repository.
3663 3667
3664 3668 Extensions can wrap this function to specify custom requirements for
3665 3669 new repositories.
3666 3670 """
3667 3671
3668 3672 if b'backend' not in createopts:
3669 3673 raise error.ProgrammingError(
3670 3674 b'backend key not present in createopts; '
3671 3675 b'was defaultcreateopts() called?'
3672 3676 )
3673 3677
3674 3678 if createopts[b'backend'] != b'revlogv1':
3675 3679 raise error.Abort(
3676 3680 _(
3677 3681 b'unable to determine repository requirements for '
3678 3682 b'storage backend: %s'
3679 3683 )
3680 3684 % createopts[b'backend']
3681 3685 )
3682 3686
3683 3687 requirements = {requirementsmod.REVLOGV1_REQUIREMENT}
3684 3688 if ui.configbool(b'format', b'usestore'):
3685 3689 requirements.add(requirementsmod.STORE_REQUIREMENT)
3686 3690 if ui.configbool(b'format', b'usefncache'):
3687 3691 requirements.add(requirementsmod.FNCACHE_REQUIREMENT)
3688 3692 if ui.configbool(b'format', b'dotencode'):
3689 3693 requirements.add(requirementsmod.DOTENCODE_REQUIREMENT)
3690 3694
3691 3695 compengines = ui.configlist(b'format', b'revlog-compression')
3692 3696 for compengine in compengines:
3693 3697 if compengine in util.compengines:
3694 3698 engine = util.compengines[compengine]
3695 3699 if engine.available() and engine.revlogheader():
3696 3700 break
3697 3701 else:
3698 3702 raise error.Abort(
3699 3703 _(
3700 3704 b'compression engines %s defined by '
3701 3705 b'format.revlog-compression not available'
3702 3706 )
3703 3707 % b', '.join(b'"%s"' % e for e in compengines),
3704 3708 hint=_(
3705 3709 b'run "hg debuginstall" to list available '
3706 3710 b'compression engines'
3707 3711 ),
3708 3712 )
3709 3713
3710 3714 # zlib is the historical default and doesn't need an explicit requirement.
3711 3715 if compengine == b'zstd':
3712 3716 requirements.add(b'revlog-compression-zstd')
3713 3717 elif compengine != b'zlib':
3714 3718 requirements.add(b'exp-compression-%s' % compengine)
3715 3719
3716 3720 if scmutil.gdinitconfig(ui):
3717 3721 requirements.add(requirementsmod.GENERALDELTA_REQUIREMENT)
3718 3722 if ui.configbool(b'format', b'sparse-revlog'):
3719 3723 requirements.add(requirementsmod.SPARSEREVLOG_REQUIREMENT)
3720 3724
3721 3725 # experimental config: format.use-dirstate-v2
3722 3726 # Keep this logic in sync with `has_dirstate_v2()` in `tests/hghave.py`
3723 3727 if ui.configbool(b'format', b'use-dirstate-v2'):
3724 3728 requirements.add(requirementsmod.DIRSTATE_V2_REQUIREMENT)
3725 3729
3726 3730 # experimental config: format.exp-use-copies-side-data-changeset
3727 3731 if ui.configbool(b'format', b'exp-use-copies-side-data-changeset'):
3728 3732 requirements.add(requirementsmod.CHANGELOGV2_REQUIREMENT)
3729 3733 requirements.add(requirementsmod.COPIESSDC_REQUIREMENT)
3730 3734 if ui.configbool(b'experimental', b'treemanifest'):
3731 3735 requirements.add(requirementsmod.TREEMANIFEST_REQUIREMENT)
3732 3736
3733 3737 changelogv2 = ui.config(b'format', b'exp-use-changelog-v2')
3734 3738 if changelogv2 == b'enable-unstable-format-and-corrupt-my-data':
3735 3739 requirements.add(requirementsmod.CHANGELOGV2_REQUIREMENT)
3736 3740
3737 3741 revlogv2 = ui.config(b'experimental', b'revlogv2')
3738 3742 if revlogv2 == b'enable-unstable-format-and-corrupt-my-data':
3739 3743 requirements.discard(requirementsmod.REVLOGV1_REQUIREMENT)
3740 3744 requirements.add(requirementsmod.REVLOGV2_REQUIREMENT)
3741 3745 # experimental config: format.internal-phase
3742 3746 if ui.configbool(b'format', b'use-internal-phase'):
3743 3747 requirements.add(requirementsmod.INTERNAL_PHASE_REQUIREMENT)
3744 3748
3745 3749 # experimental config: format.exp-archived-phase
3746 3750 if ui.configbool(b'format', b'exp-archived-phase'):
3747 3751 requirements.add(requirementsmod.ARCHIVED_PHASE_REQUIREMENT)
3748 3752
3749 3753 if createopts.get(b'narrowfiles'):
3750 3754 requirements.add(requirementsmod.NARROW_REQUIREMENT)
3751 3755
3752 3756 if createopts.get(b'lfs'):
3753 3757 requirements.add(b'lfs')
3754 3758
3755 3759 if ui.configbool(b'format', b'bookmarks-in-store'):
3756 3760 requirements.add(requirementsmod.BOOKMARKS_IN_STORE_REQUIREMENT)
3757 3761
3758 3762 # The feature is disabled unless a fast implementation is available.
3759 3763 persistent_nodemap_default = policy.importrust('revlog') is not None
3760 3764 if ui.configbool(
3761 3765 b'format', b'use-persistent-nodemap', persistent_nodemap_default
3762 3766 ):
3763 3767 requirements.add(requirementsmod.NODEMAP_REQUIREMENT)
3764 3768
3765 3769 # if share-safe is enabled, let's create the new repository with the new
3766 3770 # requirement
3767 3771 if ui.configbool(b'format', b'use-share-safe'):
3768 3772 requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
3769 3773
3770 3774 # if we are creating a share-repo¹ we have to handle requirement
3771 3775 # differently.
3772 3776 #
3773 3777 # [1] (i.e. reusing the store from another repository, just having a
3774 3778 # working copy)
3775 3779 if b'sharedrepo' in createopts:
3776 3780 source_requirements = set(createopts[b'sharedrepo'].requirements)
3777 3781
3778 3782 if requirementsmod.SHARESAFE_REQUIREMENT not in source_requirements:
3779 3783 # share to an old school repository, we have to copy the
3780 3784 # requirements and hope for the best.
3781 3785 requirements = source_requirements
3782 3786 else:
3783 3787 # We have control on the working copy only, so "copy" the non
3784 3788 # working copy part over, ignoring previous logic.
3785 3789 to_drop = set()
3786 3790 for req in requirements:
3787 3791 if req in requirementsmod.WORKING_DIR_REQUIREMENTS:
3788 3792 continue
3789 3793 if req in source_requirements:
3790 3794 continue
3791 3795 to_drop.add(req)
3792 3796 requirements -= to_drop
3793 3797 requirements |= source_requirements
3794 3798
3795 3799 if createopts.get(b'sharedrelative'):
3796 3800 requirements.add(requirementsmod.RELATIVE_SHARED_REQUIREMENT)
3797 3801 else:
3798 3802 requirements.add(requirementsmod.SHARED_REQUIREMENT)
3799 3803
3800 3804 if ui.configbool(b'format', b'use-dirstate-tracked-hint'):
3801 3805 version = ui.configint(b'format', b'use-dirstate-tracked-hint.version')
3802 3806 msg = _(b"ignoring unknown tracked key version: %d\n")
3803 3807 hint = _(
3804 3808 b"see `hg help config.format.use-dirstate-tracked-hint-version"
3805 3809 )
3806 3810 if version != 1:
3807 3811 ui.warn(msg % version, hint=hint)
3808 3812 else:
3809 3813 requirements.add(requirementsmod.DIRSTATE_TRACKED_HINT_V1)
3810 3814
3811 3815 return requirements
3812 3816
3813 3817
3814 3818 def checkrequirementscompat(ui, requirements):
3815 3819 """Checks compatibility of repository requirements enabled and disabled.
3816 3820
3817 3821 Returns a set of requirements which needs to be dropped because dependend
3818 3822 requirements are not enabled. Also warns users about it"""
3819 3823
3820 3824 dropped = set()
3821 3825
3822 3826 if requirementsmod.STORE_REQUIREMENT not in requirements:
3823 3827 if requirementsmod.BOOKMARKS_IN_STORE_REQUIREMENT in requirements:
3824 3828 ui.warn(
3825 3829 _(
3826 3830 b'ignoring enabled \'format.bookmarks-in-store\' config '
3827 3831 b'beacuse it is incompatible with disabled '
3828 3832 b'\'format.usestore\' config\n'
3829 3833 )
3830 3834 )
3831 3835 dropped.add(requirementsmod.BOOKMARKS_IN_STORE_REQUIREMENT)
3832 3836
3833 3837 if (
3834 3838 requirementsmod.SHARED_REQUIREMENT in requirements
3835 3839 or requirementsmod.RELATIVE_SHARED_REQUIREMENT in requirements
3836 3840 ):
3837 3841 raise error.Abort(
3838 3842 _(
3839 3843 b"cannot create shared repository as source was created"
3840 3844 b" with 'format.usestore' config disabled"
3841 3845 )
3842 3846 )
3843 3847
3844 3848 if requirementsmod.SHARESAFE_REQUIREMENT in requirements:
3845 3849 if ui.hasconfig(b'format', b'use-share-safe'):
3846 3850 msg = _(
3847 3851 b"ignoring enabled 'format.use-share-safe' config because "
3848 3852 b"it is incompatible with disabled 'format.usestore'"
3849 3853 b" config\n"
3850 3854 )
3851 3855 ui.warn(msg)
3852 3856 dropped.add(requirementsmod.SHARESAFE_REQUIREMENT)
3853 3857
3854 3858 return dropped
3855 3859
3856 3860
3857 3861 def filterknowncreateopts(ui, createopts):
3858 3862 """Filters a dict of repo creation options against options that are known.
3859 3863
3860 3864 Receives a dict of repo creation options and returns a dict of those
3861 3865 options that we don't know how to handle.
3862 3866
3863 3867 This function is called as part of repository creation. If the
3864 3868 returned dict contains any items, repository creation will not
3865 3869 be allowed, as it means there was a request to create a repository
3866 3870 with options not recognized by loaded code.
3867 3871
3868 3872 Extensions can wrap this function to filter out creation options
3869 3873 they know how to handle.
3870 3874 """
3871 3875 known = {
3872 3876 b'backend',
3873 3877 b'lfs',
3874 3878 b'narrowfiles',
3875 3879 b'sharedrepo',
3876 3880 b'sharedrelative',
3877 3881 b'shareditems',
3878 3882 b'shallowfilestore',
3879 3883 }
3880 3884
3881 3885 return {k: v for k, v in createopts.items() if k not in known}
3882 3886
3883 3887
3884 3888 def createrepository(ui, path: bytes, createopts=None, requirements=None):
3885 3889 """Create a new repository in a vfs.
3886 3890
3887 3891 ``path`` path to the new repo's working directory.
3888 3892 ``createopts`` options for the new repository.
3889 3893 ``requirement`` predefined set of requirements.
3890 3894 (incompatible with ``createopts``)
3891 3895
3892 3896 The following keys for ``createopts`` are recognized:
3893 3897
3894 3898 backend
3895 3899 The storage backend to use.
3896 3900 lfs
3897 3901 Repository will be created with ``lfs`` requirement. The lfs extension
3898 3902 will automatically be loaded when the repository is accessed.
3899 3903 narrowfiles
3900 3904 Set up repository to support narrow file storage.
3901 3905 sharedrepo
3902 3906 Repository object from which storage should be shared.
3903 3907 sharedrelative
3904 3908 Boolean indicating if the path to the shared repo should be
3905 3909 stored as relative. By default, the pointer to the "parent" repo
3906 3910 is stored as an absolute path.
3907 3911 shareditems
3908 3912 Set of items to share to the new repository (in addition to storage).
3909 3913 shallowfilestore
3910 3914 Indicates that storage for files should be shallow (not all ancestor
3911 3915 revisions are known).
3912 3916 """
3913 3917
3914 3918 if requirements is not None:
3915 3919 if createopts is not None:
3916 3920 msg = b'cannot specify both createopts and requirements'
3917 3921 raise error.ProgrammingError(msg)
3918 3922 createopts = {}
3919 3923 else:
3920 3924 createopts = defaultcreateopts(ui, createopts=createopts)
3921 3925
3922 3926 unknownopts = filterknowncreateopts(ui, createopts)
3923 3927
3924 3928 if not isinstance(unknownopts, dict):
3925 3929 raise error.ProgrammingError(
3926 3930 b'filterknowncreateopts() did not return a dict'
3927 3931 )
3928 3932
3929 3933 if unknownopts:
3930 3934 raise error.Abort(
3931 3935 _(
3932 3936 b'unable to create repository because of unknown '
3933 3937 b'creation option: %s'
3934 3938 )
3935 3939 % b', '.join(sorted(unknownopts)),
3936 3940 hint=_(b'is a required extension not loaded?'),
3937 3941 )
3938 3942
3939 3943 requirements = newreporequirements(ui, createopts=createopts)
3940 3944 requirements -= checkrequirementscompat(ui, requirements)
3941 3945
3942 3946 wdirvfs = vfsmod.vfs(path, expandpath=True, realpath=True)
3943 3947
3944 3948 hgvfs = vfsmod.vfs(wdirvfs.join(b'.hg'))
3945 3949 if hgvfs.exists():
3946 3950 raise error.RepoError(_(b'repository %s already exists') % path)
3947 3951
3948 3952 if b'sharedrepo' in createopts:
3949 3953 sharedpath = createopts[b'sharedrepo'].sharedpath
3950 3954
3951 3955 if createopts.get(b'sharedrelative'):
3952 3956 try:
3953 3957 sharedpath = os.path.relpath(sharedpath, hgvfs.base)
3954 3958 sharedpath = util.pconvert(sharedpath)
3955 3959 except (IOError, ValueError) as e:
3956 3960 # ValueError is raised on Windows if the drive letters differ
3957 3961 # on each path.
3958 3962 raise error.Abort(
3959 3963 _(b'cannot calculate relative path'),
3960 3964 hint=stringutil.forcebytestr(e),
3961 3965 )
3962 3966
3963 3967 if not wdirvfs.exists():
3964 3968 wdirvfs.makedirs()
3965 3969
3966 3970 hgvfs.makedir(notindexed=True)
3967 3971 if b'sharedrepo' not in createopts:
3968 3972 hgvfs.mkdir(b'cache')
3969 3973 hgvfs.mkdir(b'wcache')
3970 3974
3971 3975 has_store = requirementsmod.STORE_REQUIREMENT in requirements
3972 3976 if has_store and b'sharedrepo' not in createopts:
3973 3977 hgvfs.mkdir(b'store')
3974 3978
3975 3979 # We create an invalid changelog outside the store so very old
3976 3980 # Mercurial versions (which didn't know about the requirements
3977 3981 # file) encounter an error on reading the changelog. This
3978 3982 # effectively locks out old clients and prevents them from
3979 3983 # mucking with a repo in an unknown format.
3980 3984 #
3981 3985 # The revlog header has version 65535, which won't be recognized by
3982 3986 # such old clients.
3983 3987 hgvfs.append(
3984 3988 b'00changelog.i',
3985 3989 b'\0\0\xFF\xFF dummy changelog to prevent using the old repo '
3986 3990 b'layout',
3987 3991 )
3988 3992
3989 3993 # Filter the requirements into working copy and store ones
3990 3994 wcreq, storereq = scmutil.filterrequirements(requirements)
3991 3995 # write working copy ones
3992 3996 scmutil.writerequires(hgvfs, wcreq)
3993 3997 # If there are store requirements and the current repository
3994 3998 # is not a shared one, write stored requirements
3995 3999 # For new shared repository, we don't need to write the store
3996 4000 # requirements as they are already present in store requires
3997 4001 if storereq and b'sharedrepo' not in createopts:
3998 4002 storevfs = vfsmod.vfs(hgvfs.join(b'store'), cacheaudited=True)
3999 4003 scmutil.writerequires(storevfs, storereq)
4000 4004
4001 4005 # Write out file telling readers where to find the shared store.
4002 4006 if b'sharedrepo' in createopts:
4003 4007 hgvfs.write(b'sharedpath', sharedpath)
4004 4008
4005 4009 if createopts.get(b'shareditems'):
4006 4010 shared = b'\n'.join(sorted(createopts[b'shareditems'])) + b'\n'
4007 4011 hgvfs.write(b'shared', shared)
4008 4012
4009 4013
4010 4014 def poisonrepository(repo):
4011 4015 """Poison a repository instance so it can no longer be used."""
4012 4016 # Perform any cleanup on the instance.
4013 4017 repo.close()
4014 4018
4015 4019 # Our strategy is to replace the type of the object with one that
4016 4020 # has all attribute lookups result in error.
4017 4021 #
4018 4022 # But we have to allow the close() method because some constructors
4019 4023 # of repos call close() on repo references.
4020 4024 class poisonedrepository:
4021 4025 def __getattribute__(self, item):
4022 4026 if item == 'close':
4023 4027 return object.__getattribute__(self, item)
4024 4028
4025 4029 raise error.ProgrammingError(
4026 4030 b'repo instances should not be used after unshare'
4027 4031 )
4028 4032
4029 4033 def close(self):
4030 4034 pass
4031 4035
4032 4036 # We may have a repoview, which intercepts __setattr__. So be sure
4033 4037 # we operate at the lowest level possible.
4034 4038 object.__setattr__(repo, '__class__', poisonedrepository)
@@ -1,402 +1,408 b''
1 1 # lock.py - simple advisory locking scheme for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Olivia Mackall <olivia@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8
9 9 import contextlib
10 10 import errno
11 11 import os
12 12 import signal
13 13 import socket
14 14 import time
15 15 import typing
16 16 import warnings
17 17
18 18 from .i18n import _
19 19
20 20 from . import (
21 21 encoding,
22 22 error,
23 23 pycompat,
24 24 util,
25 25 )
26 26
27 27 from .utils import procutil
28 28
29 29
30 30 def _getlockprefix():
31 31 """Return a string which is used to differentiate pid namespaces
32 32
33 33 It's useful to detect "dead" processes and remove stale locks with
34 34 confidence. Typically it's just hostname. On modern linux, we include an
35 35 extra Linux-specific pid namespace identifier.
36 36 """
37 37 result = encoding.strtolocal(socket.gethostname())
38 38 if pycompat.sysplatform.startswith(b'linux'):
39 39 try:
40 40 result += b'/%x' % os.stat(b'/proc/self/ns/pid').st_ino
41 41 except (FileNotFoundError, PermissionError, NotADirectoryError):
42 42 pass
43 43 return result
44 44
45 45
46 46 @contextlib.contextmanager
47 47 def _delayedinterrupt():
48 48 """Block signal interrupt while doing something critical
49 49
50 50 This makes sure that the code block wrapped by this context manager won't
51 51 be interrupted.
52 52
53 53 For Windows developers: It appears not possible to guard time.sleep()
54 54 from CTRL_C_EVENT, so please don't use time.sleep() to test if this is
55 55 working.
56 56 """
57 57 assertedsigs = []
58 58 blocked = False
59 59 orighandlers = {}
60 60
61 61 def raiseinterrupt(num):
62 62 if num == getattr(signal, 'SIGINT', None) or num == getattr(
63 63 signal, 'CTRL_C_EVENT', None
64 64 ):
65 65 raise KeyboardInterrupt
66 66 else:
67 67 raise error.SignalInterrupt
68 68
69 69 def catchterm(num, frame):
70 70 if blocked:
71 71 assertedsigs.append(num)
72 72 else:
73 73 raiseinterrupt(num)
74 74
75 75 try:
76 76 # save handlers first so they can be restored even if a setup is
77 77 # interrupted between signal.signal() and orighandlers[] =.
78 78 for name in [
79 79 'CTRL_C_EVENT',
80 80 'SIGINT',
81 81 'SIGBREAK',
82 82 'SIGHUP',
83 83 'SIGTERM',
84 84 ]:
85 85 num = getattr(signal, name, None)
86 86 if num and num not in orighandlers:
87 87 orighandlers[num] = signal.getsignal(num)
88 88 try:
89 89 for num in orighandlers:
90 90 signal.signal(num, catchterm)
91 91 except ValueError:
92 92 pass # in a thread? no luck
93 93
94 94 blocked = True
95 95 yield
96 96 finally:
97 97 # no simple way to reliably restore all signal handlers because
98 98 # any loops, recursive function calls, except blocks, etc. can be
99 99 # interrupted. so instead, make catchterm() raise interrupt.
100 100 blocked = False
101 101 try:
102 102 for num, handler in orighandlers.items():
103 103 signal.signal(num, handler)
104 104 except ValueError:
105 105 pass # in a thread?
106 106
107 107 # re-raise interrupt exception if any, which may be shadowed by a new
108 108 # interrupt occurred while re-raising the first one
109 109 if assertedsigs:
110 110 raiseinterrupt(assertedsigs[0])
111 111
112 112
113 113 def trylock(ui, vfs, lockname, timeout, warntimeout, *args, **kwargs):
114 114 """return an acquired lock or raise an a LockHeld exception
115 115
116 116 This function is responsible to issue warnings and or debug messages about
117 117 the held lock while trying to acquires it."""
118 devel_wait_file = kwargs.pop("devel_wait_sync_file", None)
118 119
119 120 def printwarning(printer, locker):
120 121 """issue the usual "waiting on lock" message through any channel"""
121 122 # show more details for new-style locks
122 123 if b':' in locker:
123 124 host, pid = locker.split(b":", 1)
124 125 msg = _(
125 126 b"waiting for lock on %s held by process %r on host %r\n"
126 127 ) % (
127 128 pycompat.bytestr(l.desc),
128 129 pycompat.bytestr(pid),
129 130 pycompat.bytestr(host),
130 131 )
131 132 else:
132 133 msg = _(b"waiting for lock on %s held by %r\n") % (
133 134 l.desc,
134 135 pycompat.bytestr(locker),
135 136 )
136 137 printer(msg)
137 138
138 139 l = lock(vfs, lockname, 0, *args, dolock=False, **kwargs)
139 140
140 141 debugidx = 0 if (warntimeout and timeout) else -1
141 142 warningidx = 0
142 143 if not timeout:
143 144 warningidx = -1
144 145 elif warntimeout:
145 146 warningidx = warntimeout
146 147
147 148 delay = 0
148 149 while True:
149 150 try:
150 151 l._trylock()
151 152 break
152 153 except error.LockHeld as inst:
154 if devel_wait_file is not None:
155 # create the file to signal we are waiting
156 with open(devel_wait_file, 'w'):
157 pass
158
153 159 if delay == debugidx:
154 160 printwarning(ui.debug, inst.locker)
155 161 if delay == warningidx:
156 162 printwarning(ui.warn, inst.locker)
157 163 if timeout <= delay:
158 164 assert isinstance(inst.filename, bytes)
159 165 raise error.LockHeld(
160 166 errno.ETIMEDOUT,
161 167 typing.cast(bytes, inst.filename),
162 168 l.desc,
163 169 inst.locker,
164 170 )
165 171 time.sleep(1)
166 172 delay += 1
167 173
168 174 l.delay = delay
169 175 if l.delay:
170 176 if 0 <= warningidx <= l.delay:
171 177 ui.warn(_(b"got lock after %d seconds\n") % l.delay)
172 178 else:
173 179 ui.debug(b"got lock after %d seconds\n" % l.delay)
174 180 if l.acquirefn:
175 181 l.acquirefn()
176 182 return l
177 183
178 184
179 185 class lock:
180 186 """An advisory lock held by one process to control access to a set
181 187 of files. Non-cooperating processes or incorrectly written scripts
182 188 can ignore Mercurial's locking scheme and stomp all over the
183 189 repository, so don't do that.
184 190
185 191 Typically used via localrepository.lock() to lock the repository
186 192 store (.hg/store/) or localrepository.wlock() to lock everything
187 193 else under .hg/."""
188 194
189 195 # lock is symlink on platforms that support it, file on others.
190 196
191 197 # symlink is used because create of directory entry and contents
192 198 # are atomic even over nfs.
193 199
194 200 # old-style lock: symlink to pid
195 201 # new-style lock: symlink to hostname:pid
196 202
197 203 _host = None
198 204
199 205 def __init__(
200 206 self,
201 207 vfs,
202 208 fname,
203 209 timeout=-1,
204 210 releasefn=None,
205 211 acquirefn=None,
206 212 desc=None,
207 213 signalsafe=True,
208 214 dolock=True,
209 215 ):
210 216 self.vfs = vfs
211 217 self.f = fname
212 218 self.held = 0
213 219 self.timeout = timeout
214 220 self.releasefn = releasefn
215 221 self.acquirefn = acquirefn
216 222 self.desc = desc
217 223 if signalsafe:
218 224 self._maybedelayedinterrupt = _delayedinterrupt
219 225 else:
220 226 self._maybedelayedinterrupt = util.nullcontextmanager
221 227 self.postrelease = []
222 228 self.pid = self._getpid()
223 229 if dolock:
224 230 self.delay = self.lock()
225 231 if self.acquirefn:
226 232 self.acquirefn()
227 233
228 234 def __enter__(self):
229 235 return self
230 236
231 237 def __exit__(self, exc_type, exc_value, exc_tb):
232 238 success = all(a is None for a in (exc_type, exc_value, exc_tb))
233 239 self.release(success=success)
234 240
235 241 def __del__(self):
236 242 if self.held:
237 243 warnings.warn(
238 244 "use lock.release instead of del lock",
239 245 category=DeprecationWarning,
240 246 stacklevel=2,
241 247 )
242 248
243 249 # ensure the lock will be removed
244 250 # even if recursive locking did occur
245 251 self.held = 1
246 252
247 253 self.release()
248 254
249 255 def _getpid(self):
250 256 # wrapper around procutil.getpid() to make testing easier
251 257 return procutil.getpid()
252 258
253 259 def lock(self):
254 260 timeout = self.timeout
255 261 while True:
256 262 try:
257 263 self._trylock()
258 264 return self.timeout - timeout
259 265 except error.LockHeld as inst:
260 266 if timeout != 0:
261 267 time.sleep(1)
262 268 if timeout > 0:
263 269 timeout -= 1
264 270 continue
265 271 raise error.LockHeld(
266 272 errno.ETIMEDOUT, inst.filename, self.desc, inst.locker
267 273 )
268 274
269 275 def _trylock(self):
270 276 if self.held:
271 277 self.held += 1
272 278 return
273 279 if lock._host is None:
274 280 lock._host = _getlockprefix()
275 281 lockname = b'%s:%d' % (lock._host, self.pid)
276 282 retry = 5
277 283 while not self.held and retry:
278 284 retry -= 1
279 285 try:
280 286 with self._maybedelayedinterrupt():
281 287 self.vfs.makelock(lockname, self.f)
282 288 self.held = 1
283 289 except (OSError, IOError) as why:
284 290 if why.errno == errno.EEXIST:
285 291 locker = self._readlock()
286 292 if locker is None:
287 293 continue
288 294
289 295 locker = self._testlock(locker)
290 296 if locker is not None:
291 297 raise error.LockHeld(
292 298 errno.EAGAIN,
293 299 self.vfs.join(self.f),
294 300 self.desc,
295 301 locker,
296 302 )
297 303 else:
298 304 assert isinstance(why.filename, bytes)
299 305 assert isinstance(why.strerror, str)
300 306 raise error.LockUnavailable(
301 307 why.errno,
302 308 why.strerror,
303 309 typing.cast(bytes, why.filename),
304 310 self.desc,
305 311 )
306 312
307 313 if not self.held:
308 314 # use empty locker to mean "busy for frequent lock/unlock
309 315 # by many processes"
310 316 raise error.LockHeld(
311 317 errno.EAGAIN, self.vfs.join(self.f), self.desc, b""
312 318 )
313 319
314 320 def _readlock(self):
315 321 """read lock and return its value
316 322
317 323 Returns None if no lock exists, pid for old-style locks, and host:pid
318 324 for new-style locks.
319 325 """
320 326 try:
321 327 return self.vfs.readlock(self.f)
322 328 except FileNotFoundError:
323 329 return None
324 330
325 331 def _lockshouldbebroken(self, locker):
326 332 if locker is None:
327 333 return False
328 334 try:
329 335 host, pid = locker.split(b":", 1)
330 336 except ValueError:
331 337 return False
332 338 if host != lock._host:
333 339 return False
334 340 try:
335 341 pid = int(pid)
336 342 except ValueError:
337 343 return False
338 344 if procutil.testpid(pid):
339 345 return False
340 346 return True
341 347
342 348 def _testlock(self, locker):
343 349 if not self._lockshouldbebroken(locker):
344 350 return locker
345 351
346 352 # if locker dead, break lock. must do this with another lock
347 353 # held, or can race and break valid lock.
348 354 try:
349 355 with lock(self.vfs, self.f + b'.break', timeout=0):
350 356 locker = self._readlock()
351 357 if not self._lockshouldbebroken(locker):
352 358 return locker
353 359 self.vfs.unlink(self.f)
354 360 except error.LockError:
355 361 return locker
356 362
357 363 def testlock(self):
358 364 """return id of locker if lock is valid, else None.
359 365
360 366 If old-style lock, we cannot tell what machine locker is on.
361 367 with new-style lock, if locker is on this machine, we can
362 368 see if locker is alive. If locker is on this machine but
363 369 not alive, we can safely break lock.
364 370
365 371 The lock file is only deleted when None is returned.
366 372
367 373 """
368 374 locker = self._readlock()
369 375 return self._testlock(locker)
370 376
371 377 def release(self, success=True):
372 378 """release the lock and execute callback function if any
373 379
374 380 If the lock has been acquired multiple times, the actual release is
375 381 delayed to the last release call."""
376 382 if self.held > 1:
377 383 self.held -= 1
378 384 elif self.held == 1:
379 385 self.held = 0
380 386 if self._getpid() != self.pid:
381 387 # we forked, and are not the parent
382 388 return
383 389 try:
384 390 if self.releasefn:
385 391 self.releasefn()
386 392 finally:
387 393 try:
388 394 self.vfs.unlink(self.f)
389 395 except OSError:
390 396 pass
391 397 # The postrelease functions typically assume the lock is not held
392 398 # at all.
393 399 for callback in self.postrelease:
394 400 callback(success)
395 401 # Prevent double usage and help clear cycles.
396 402 self.postrelease = None
397 403
398 404
399 405 def release(*locks):
400 406 for lock in locks:
401 407 if lock is not None:
402 408 lock.release()
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now