##// END OF EJS Templates
merge with stable
Augie Fackler -
r45251:f365dfed merge default
parent child Browse files
Show More

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

@@ -1,196 +1,197 b''
1 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0 iD8DBQBEYmO2ywK+sNU5EO8RAnaYAKCO7x15xUn5mnhqWNXqk/ehlhRt2QCfRDfY0LrUq2q4oK/KypuJYPHgq1A=
1 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0 iD8DBQBEYmO2ywK+sNU5EO8RAnaYAKCO7x15xUn5mnhqWNXqk/ehlhRt2QCfRDfY0LrUq2q4oK/KypuJYPHgq1A=
2 2be3001847cb18a23c403439d9e7d0ace30804e9 0 iD8DBQBExUbjywK+sNU5EO8RAhzxAKCtyHAQUzcTSZTqlfJ0by6vhREwWQCghaQFHfkfN0l9/40EowNhuMOKnJk=
2 2be3001847cb18a23c403439d9e7d0ace30804e9 0 iD8DBQBExUbjywK+sNU5EO8RAhzxAKCtyHAQUzcTSZTqlfJ0by6vhREwWQCghaQFHfkfN0l9/40EowNhuMOKnJk=
3 36a957364b1b89c150f2d0e60a99befe0ee08bd3 0 iD8DBQBFfL2QywK+sNU5EO8RAjYFAKCoGlaWRTeMsjdmxAjUYx6diZxOBwCfY6IpBYsKvPTwB3oktnPt5Rmrlys=
3 36a957364b1b89c150f2d0e60a99befe0ee08bd3 0 iD8DBQBFfL2QywK+sNU5EO8RAjYFAKCoGlaWRTeMsjdmxAjUYx6diZxOBwCfY6IpBYsKvPTwB3oktnPt5Rmrlys=
4 27230c29bfec36d5540fbe1c976810aefecfd1d2 0 iD8DBQBFheweywK+sNU5EO8RAt7VAKCrqJQWT2/uo2RWf0ZI4bLp6v82jACgjrMdsaTbxRsypcmEsdPhlG6/8F4=
4 27230c29bfec36d5540fbe1c976810aefecfd1d2 0 iD8DBQBFheweywK+sNU5EO8RAt7VAKCrqJQWT2/uo2RWf0ZI4bLp6v82jACgjrMdsaTbxRsypcmEsdPhlG6/8F4=
5 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0 iD8DBQBGgHicywK+sNU5EO8RAgNxAJ0VG8ixAaeudx4sZbhngI1syu49HQCeNUJQfWBgA8bkJ2pvsFpNxwYaX3I=
5 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0 iD8DBQBGgHicywK+sNU5EO8RAgNxAJ0VG8ixAaeudx4sZbhngI1syu49HQCeNUJQfWBgA8bkJ2pvsFpNxwYaX3I=
6 23889160905a1b09fffe1c07378e9fc1827606eb 0 iD8DBQBHGTzoywK+sNU5EO8RAr/UAJ0Y8s4jQtzgS+G9vM8z6CWBThZ8fwCcCT5XDj2XwxKkz/0s6UELwjsO3LU=
6 23889160905a1b09fffe1c07378e9fc1827606eb 0 iD8DBQBHGTzoywK+sNU5EO8RAr/UAJ0Y8s4jQtzgS+G9vM8z6CWBThZ8fwCcCT5XDj2XwxKkz/0s6UELwjsO3LU=
7 bae2e9c838e90a393bae3973a7850280413e091a 0 iD8DBQBH6DO5ywK+sNU5EO8RAsfrAJ0e4r9c9GF/MJsM7Xjd3NesLRC3+ACffj6+6HXdZf8cswAoFPO+DY00oD0=
7 bae2e9c838e90a393bae3973a7850280413e091a 0 iD8DBQBH6DO5ywK+sNU5EO8RAsfrAJ0e4r9c9GF/MJsM7Xjd3NesLRC3+ACffj6+6HXdZf8cswAoFPO+DY00oD0=
8 d5cbbe2c49cee22a9fbeb9ea41daa0ac4e26b846 0 iD8DBQBINdwsywK+sNU5EO8RAjIUAKCPmlFJSpsPAAUKF+iNHAwVnwmzeQCdEXrL27CWclXuUKdbQC8De7LICtE=
8 d5cbbe2c49cee22a9fbeb9ea41daa0ac4e26b846 0 iD8DBQBINdwsywK+sNU5EO8RAjIUAKCPmlFJSpsPAAUKF+iNHAwVnwmzeQCdEXrL27CWclXuUKdbQC8De7LICtE=
9 d2375bbee6d47e62ba8e415c86e83a465dc4dce9 0 iD8DBQBIo1wpywK+sNU5EO8RAmRNAJ94x3OFt6blbqu/yBoypm/AJ44fuACfUaldXcV5z9tht97hSp22DVTEPGc=
9 d2375bbee6d47e62ba8e415c86e83a465dc4dce9 0 iD8DBQBIo1wpywK+sNU5EO8RAmRNAJ94x3OFt6blbqu/yBoypm/AJ44fuACfUaldXcV5z9tht97hSp22DVTEPGc=
10 2a67430f92f15ea5159c26b09ec4839a0c549a26 0 iEYEABECAAYFAkk1hykACgkQywK+sNU5EO85QACeNJNUanjc2tl4wUoPHNuv+lSj0ZMAoIm93wSTc/feyYnO2YCaQ1iyd9Nu
10 2a67430f92f15ea5159c26b09ec4839a0c549a26 0 iEYEABECAAYFAkk1hykACgkQywK+sNU5EO85QACeNJNUanjc2tl4wUoPHNuv+lSj0ZMAoIm93wSTc/feyYnO2YCaQ1iyd9Nu
11 3773e510d433969e277b1863c317b674cbee2065 0 iEYEABECAAYFAklNbbAACgkQywK+sNU5EO8o+gCfeb2/lfIJZMvyDA1m+G1CsBAxfFsAoIa6iAMG8SBY7hW1Q85Yf/LXEvaE
11 3773e510d433969e277b1863c317b674cbee2065 0 iEYEABECAAYFAklNbbAACgkQywK+sNU5EO8o+gCfeb2/lfIJZMvyDA1m+G1CsBAxfFsAoIa6iAMG8SBY7hW1Q85Yf/LXEvaE
12 11a4eb81fb4f4742451591489e2797dc47903277 0 iEYEABECAAYFAklcAnsACgkQywK+sNU5EO+uXwCbBVHNNsLy1g7BlAyQJwadYVyHOXoAoKvtAVO71+bv7EbVoukwTzT+P4Sx
12 11a4eb81fb4f4742451591489e2797dc47903277 0 iEYEABECAAYFAklcAnsACgkQywK+sNU5EO+uXwCbBVHNNsLy1g7BlAyQJwadYVyHOXoAoKvtAVO71+bv7EbVoukwTzT+P4Sx
13 11efa41037e280d08cfb07c09ad485df30fb0ea8 0 iEYEABECAAYFAkmvJRQACgkQywK+sNU5EO9XZwCeLMgDgPSMWMm6vgjL4lDs2pEc5+0AnRxfiFbpbBfuEFTqKz9nbzeyoBlx
13 11efa41037e280d08cfb07c09ad485df30fb0ea8 0 iEYEABECAAYFAkmvJRQACgkQywK+sNU5EO9XZwCeLMgDgPSMWMm6vgjL4lDs2pEc5+0AnRxfiFbpbBfuEFTqKz9nbzeyoBlx
14 02981000012e3adf40c4849bd7b3d5618f9ce82d 0 iEYEABECAAYFAknEH3wACgkQywK+sNU5EO+uXwCeI+LbLMmhjU1lKSfU3UWJHjjUC7oAoIZLvYDGOL/tNZFUuatc3RnZ2eje
14 02981000012e3adf40c4849bd7b3d5618f9ce82d 0 iEYEABECAAYFAknEH3wACgkQywK+sNU5EO+uXwCeI+LbLMmhjU1lKSfU3UWJHjjUC7oAoIZLvYDGOL/tNZFUuatc3RnZ2eje
15 196d40e7c885fa6e95f89134809b3ec7bdbca34b 0 iEYEABECAAYFAkpL2X4ACgkQywK+sNU5EO9FOwCfXJycjyKJXsvQqKkHrglwOQhEKS4An36GfKzptfN8b1qNc3+ya/5c2WOM
15 196d40e7c885fa6e95f89134809b3ec7bdbca34b 0 iEYEABECAAYFAkpL2X4ACgkQywK+sNU5EO9FOwCfXJycjyKJXsvQqKkHrglwOQhEKS4An36GfKzptfN8b1qNc3+ya/5c2WOM
16 3ef6c14a1e8e83a31226f5881b7fe6095bbfa6f6 0 iEYEABECAAYFAkpopLIACgkQywK+sNU5EO8QSgCfZ0ztsd071rOa2lhmp9Fyue/WoI0AoLTei80/xrhRlB8L/rZEf2KBl8dA
16 3ef6c14a1e8e83a31226f5881b7fe6095bbfa6f6 0 iEYEABECAAYFAkpopLIACgkQywK+sNU5EO8QSgCfZ0ztsd071rOa2lhmp9Fyue/WoI0AoLTei80/xrhRlB8L/rZEf2KBl8dA
17 31ec469f9b556f11819937cf68ee53f2be927ebf 0 iEYEABECAAYFAksBuxAACgkQywK+sNU5EO+mBwCfagB+A0txzWZ6dRpug3LEoK7Z1QsAoKpbk8vsLjv6/oRDicSk/qBu33+m
17 31ec469f9b556f11819937cf68ee53f2be927ebf 0 iEYEABECAAYFAksBuxAACgkQywK+sNU5EO+mBwCfagB+A0txzWZ6dRpug3LEoK7Z1QsAoKpbk8vsLjv6/oRDicSk/qBu33+m
18 439d7ea6fe3aa4ab9ec274a68846779153789de9 0 iEYEABECAAYFAksVw0kACgkQywK+sNU5EO/oZwCfdfBEkgp38xq6wN2F4nj+SzofrJIAnjmxt04vaJSeOOeHylHvk6lzuQsw
18 439d7ea6fe3aa4ab9ec274a68846779153789de9 0 iEYEABECAAYFAksVw0kACgkQywK+sNU5EO/oZwCfdfBEkgp38xq6wN2F4nj+SzofrJIAnjmxt04vaJSeOOeHylHvk6lzuQsw
19 296a0b14a68621f6990c54fdba0083f6f20935bf 0 iEYEABECAAYFAks+jCoACgkQywK+sNU5EO9J8wCeMUGF9E/gS2UBsqIz56WS4HMPRPUAoI5J95mwEIK8Clrl7qFRidNI6APq
19 296a0b14a68621f6990c54fdba0083f6f20935bf 0 iEYEABECAAYFAks+jCoACgkQywK+sNU5EO9J8wCeMUGF9E/gS2UBsqIz56WS4HMPRPUAoI5J95mwEIK8Clrl7qFRidNI6APq
20 4aa619c4c2c09907034d9824ebb1dd0e878206eb 0 iEYEABECAAYFAktm9IsACgkQywK+sNU5EO9XGgCgk4HclRQhexEtooPE5GcUCdB6M8EAn2ptOhMVbIoO+JncA+tNACPFXh0O
20 4aa619c4c2c09907034d9824ebb1dd0e878206eb 0 iEYEABECAAYFAktm9IsACgkQywK+sNU5EO9XGgCgk4HclRQhexEtooPE5GcUCdB6M8EAn2ptOhMVbIoO+JncA+tNACPFXh0O
21 ff2704a8ded37fbebd8b6eb5ec733731d725da8a 0 iEYEABECAAYFAkuRoSQACgkQywK+sNU5EO//3QCeJDc5r2uFyFCtAlpSA27DEE5rrxAAn2FSwTy9fhrB3QAdDQlwkEZcQzDh
21 ff2704a8ded37fbebd8b6eb5ec733731d725da8a 0 iEYEABECAAYFAkuRoSQACgkQywK+sNU5EO//3QCeJDc5r2uFyFCtAlpSA27DEE5rrxAAn2FSwTy9fhrB3QAdDQlwkEZcQzDh
22 2b01dab594167bc0dd33331dbaa6dca3dca1b3aa 0 iEYEABECAAYFAku1IwIACgkQywK+sNU5EO9MjgCdHLVwkTZlNHxhcznZKBL1rjN+J7cAoLLWi9LTL6f/TgBaPSKOy1ublbaW
22 2b01dab594167bc0dd33331dbaa6dca3dca1b3aa 0 iEYEABECAAYFAku1IwIACgkQywK+sNU5EO9MjgCdHLVwkTZlNHxhcznZKBL1rjN+J7cAoLLWi9LTL6f/TgBaPSKOy1ublbaW
23 39f725929f0c48c5fb3b90c071fc3066012456ca 0 iEYEABECAAYFAkvclvsACgkQywK+sNU5EO9FSwCeL9i5x8ALW/LE5+lCX6MFEAe4MhwAn1ev5o6SX6GrNdDfKweiemfO2VBk
23 39f725929f0c48c5fb3b90c071fc3066012456ca 0 iEYEABECAAYFAkvclvsACgkQywK+sNU5EO9FSwCeL9i5x8ALW/LE5+lCX6MFEAe4MhwAn1ev5o6SX6GrNdDfKweiemfO2VBk
24 fdcf80f26604f233dc4d8f0a5ef9d7470e317e8a 0 iEYEABECAAYFAkvsKTkACgkQywK+sNU5EO9qEACgiSiRGvTG2vXGJ65tUSOIYihTuFAAnRzRIqEVSw8M8/RGeUXRps0IzaCO
24 fdcf80f26604f233dc4d8f0a5ef9d7470e317e8a 0 iEYEABECAAYFAkvsKTkACgkQywK+sNU5EO9qEACgiSiRGvTG2vXGJ65tUSOIYihTuFAAnRzRIqEVSw8M8/RGeUXRps0IzaCO
25 24fe2629c6fd0c74c90bd066e77387c2b02e8437 0 iEYEABECAAYFAkwFLRsACgkQywK+sNU5EO+pJACgp13tPI+pbwKZV+LeMjcQ4H6tCZYAoJebzhd6a8yYx6qiwpJxA9BXZNXy
25 24fe2629c6fd0c74c90bd066e77387c2b02e8437 0 iEYEABECAAYFAkwFLRsACgkQywK+sNU5EO+pJACgp13tPI+pbwKZV+LeMjcQ4H6tCZYAoJebzhd6a8yYx6qiwpJxA9BXZNXy
26 f786fc4b8764cd2a5526d259cf2f94d8a66924d9 0 iEYEABECAAYFAkwsyxcACgkQywK+sNU5EO+crACfUpNAF57PmClkSri9nJcBjb2goN4AniPCNaKvnki7TnUsi1u2oxltpKKL
26 f786fc4b8764cd2a5526d259cf2f94d8a66924d9 0 iEYEABECAAYFAkwsyxcACgkQywK+sNU5EO+crACfUpNAF57PmClkSri9nJcBjb2goN4AniPCNaKvnki7TnUsi1u2oxltpKKL
27 bf1774d95bde614af3956d92b20e2a0c68c5fec7 0 iEYEABECAAYFAkxVwccACgkQywK+sNU5EO+oFQCeJzwZ+we1fIIyBGCddHceOUAN++cAnjvT6A8ZWW0zV21NXIFF1qQmjxJd
27 bf1774d95bde614af3956d92b20e2a0c68c5fec7 0 iEYEABECAAYFAkxVwccACgkQywK+sNU5EO+oFQCeJzwZ+we1fIIyBGCddHceOUAN++cAnjvT6A8ZWW0zV21NXIFF1qQmjxJd
28 c00f03a4982e467fb6b6bd45908767db6df4771d 0 iEYEABECAAYFAkxXDqsACgkQywK+sNU5EO/GJACfT9Rz4hZOxPQEs91JwtmfjevO84gAmwSmtfo5mmWSm8gtTUebCcdTv0Kf
28 c00f03a4982e467fb6b6bd45908767db6df4771d 0 iEYEABECAAYFAkxXDqsACgkQywK+sNU5EO/GJACfT9Rz4hZOxPQEs91JwtmfjevO84gAmwSmtfo5mmWSm8gtTUebCcdTv0Kf
29 ff5cec76b1c5b6be9c3bb923aae8c3c6d079d6b9 0 iD8DBQBMdo+qywK+sNU5EO8RAqQpAJ975BL2CCAiWMz9SXthNQ9xG181IwCgp4O+KViHPkufZVFn2aTKMNvcr1A=
29 ff5cec76b1c5b6be9c3bb923aae8c3c6d079d6b9 0 iD8DBQBMdo+qywK+sNU5EO8RAqQpAJ975BL2CCAiWMz9SXthNQ9xG181IwCgp4O+KViHPkufZVFn2aTKMNvcr1A=
30 93d8bff78c96fe7e33237b257558ee97290048a4 0 iD8DBQBMpfvdywK+sNU5EO8RAsxVAJ0UaL1XB51C76JUBhafc9GBefuMxwCdEWkTOzwvE0SarJBe9i008jhbqW4=
30 93d8bff78c96fe7e33237b257558ee97290048a4 0 iD8DBQBMpfvdywK+sNU5EO8RAsxVAJ0UaL1XB51C76JUBhafc9GBefuMxwCdEWkTOzwvE0SarJBe9i008jhbqW4=
31 333421b9e0f96c7bc788e5667c146a58a9440a55 0 iD8DBQBMz0HOywK+sNU5EO8RAlsEAJ0USh6yOG7OrWkADGunVt9QimBQnwCbBqeMnKgSbwEw8jZwE3Iz1mdrYlo=
31 333421b9e0f96c7bc788e5667c146a58a9440a55 0 iD8DBQBMz0HOywK+sNU5EO8RAlsEAJ0USh6yOG7OrWkADGunVt9QimBQnwCbBqeMnKgSbwEw8jZwE3Iz1mdrYlo=
32 4438875ec01bd0fc32be92b0872eb6daeed4d44f 0 iD8DBQBM4WYUywK+sNU5EO8RAhCVAJ0dJswachwFAHALmk1x0RJehxzqPQCbBNskP9n/X689jB+btNTZTyKU/fw=
32 4438875ec01bd0fc32be92b0872eb6daeed4d44f 0 iD8DBQBM4WYUywK+sNU5EO8RAhCVAJ0dJswachwFAHALmk1x0RJehxzqPQCbBNskP9n/X689jB+btNTZTyKU/fw=
33 6aff4f144ad356311318b0011df0bb21f2c97429 0 iD8DBQBM9uxXywK+sNU5EO8RAv+4AKCDj4qKP16GdPaq1tP6BUwpM/M1OACfRyzLPp/qiiN8xJTWoWYSe/XjJug=
33 6aff4f144ad356311318b0011df0bb21f2c97429 0 iD8DBQBM9uxXywK+sNU5EO8RAv+4AKCDj4qKP16GdPaq1tP6BUwpM/M1OACfRyzLPp/qiiN8xJTWoWYSe/XjJug=
34 e3bf16703e2601de99e563cdb3a5d50b64e6d320 0 iD8DBQBNH8WqywK+sNU5EO8RAiQTAJ9sBO+TeiGro4si77VVaQaA6jcRUgCfSA28dBbjj0oFoQwvPoZjANiZBH8=
34 e3bf16703e2601de99e563cdb3a5d50b64e6d320 0 iD8DBQBNH8WqywK+sNU5EO8RAiQTAJ9sBO+TeiGro4si77VVaQaA6jcRUgCfSA28dBbjj0oFoQwvPoZjANiZBH8=
35 a6c855c32ea081da3c3b8ff628f1847ff271482f 0 iD8DBQBNSJJ+ywK+sNU5EO8RAoJaAKCweDEF70fu+r1Zn7pYDXdlk5RuSgCeO9gK/eit8Lin/1n3pO7aYguFLok=
35 a6c855c32ea081da3c3b8ff628f1847ff271482f 0 iD8DBQBNSJJ+ywK+sNU5EO8RAoJaAKCweDEF70fu+r1Zn7pYDXdlk5RuSgCeO9gK/eit8Lin/1n3pO7aYguFLok=
36 2b2155623ee2559caf288fd333f30475966c4525 0 iD8DBQBNSJeBywK+sNU5EO8RAm1KAJ4hW9Cm9nHaaGJguchBaPLlAr+O3wCgqgmMok8bdAS06N6PL60PSTM//Gg=
36 2b2155623ee2559caf288fd333f30475966c4525 0 iD8DBQBNSJeBywK+sNU5EO8RAm1KAJ4hW9Cm9nHaaGJguchBaPLlAr+O3wCgqgmMok8bdAS06N6PL60PSTM//Gg=
37 2616325766e3504c8ae7c84bd15ee610901fe91d 0 iD8DBQBNbWy9ywK+sNU5EO8RAlWCAJ4mW8HbzjJj9GpK98muX7k+7EvEHwCfaTLbC/DH3QEsZBhEP+M8tzL6RU4=
37 2616325766e3504c8ae7c84bd15ee610901fe91d 0 iD8DBQBNbWy9ywK+sNU5EO8RAlWCAJ4mW8HbzjJj9GpK98muX7k+7EvEHwCfaTLbC/DH3QEsZBhEP+M8tzL6RU4=
38 aa1f3be38ab127280761889d2dca906ca465b5f4 0 iD8DBQBNeQq7ywK+sNU5EO8RAlEOAJ4tlEDdetE9lKfjGgjbkcR8PrC3egCfXCfF3qNVvU/2YYjpgvRwevjvDy0=
38 aa1f3be38ab127280761889d2dca906ca465b5f4 0 iD8DBQBNeQq7ywK+sNU5EO8RAlEOAJ4tlEDdetE9lKfjGgjbkcR8PrC3egCfXCfF3qNVvU/2YYjpgvRwevjvDy0=
39 b032bec2c0a651ca0ddecb65714bfe6770f67d70 0 iD8DBQBNlg5kywK+sNU5EO8RAnGEAJ9gmEx6MfaR4XcG2m/93vwtfyzs3gCgltzx8/YdHPwqDwRX/WbpYgi33is=
39 b032bec2c0a651ca0ddecb65714bfe6770f67d70 0 iD8DBQBNlg5kywK+sNU5EO8RAnGEAJ9gmEx6MfaR4XcG2m/93vwtfyzs3gCgltzx8/YdHPwqDwRX/WbpYgi33is=
40 3cb1e95676ad089596bd81d0937cad37d6e3b7fb 0 iD8DBQBNvTy4ywK+sNU5EO8RAmp8AJ9QnxK4jTJ7G722MyeBxf0UXEdGwACgtlM7BKtNQfbEH/fOW5y+45W88VI=
40 3cb1e95676ad089596bd81d0937cad37d6e3b7fb 0 iD8DBQBNvTy4ywK+sNU5EO8RAmp8AJ9QnxK4jTJ7G722MyeBxf0UXEdGwACgtlM7BKtNQfbEH/fOW5y+45W88VI=
41 733af5d9f6b22387913e1d11350fb8cb7c1487dd 0 iD8DBQBN5q/8ywK+sNU5EO8RArRGAKCNGT94GKIYtSuwZ57z1sQbcw6uLACfffpbMV4NAPMl8womAwg+7ZPKnIU=
41 733af5d9f6b22387913e1d11350fb8cb7c1487dd 0 iD8DBQBN5q/8ywK+sNU5EO8RArRGAKCNGT94GKIYtSuwZ57z1sQbcw6uLACfffpbMV4NAPMl8womAwg+7ZPKnIU=
42 de9eb6b1da4fc522b1cab16d86ca166204c24f25 0 iD8DBQBODhfhywK+sNU5EO8RAr2+AJ4ugbAj8ae8/K0bYZzx3sascIAg1QCeK3b+zbbVVqd3b7CDpwFnaX8kTd4=
42 de9eb6b1da4fc522b1cab16d86ca166204c24f25 0 iD8DBQBODhfhywK+sNU5EO8RAr2+AJ4ugbAj8ae8/K0bYZzx3sascIAg1QCeK3b+zbbVVqd3b7CDpwFnaX8kTd4=
43 4a43e23b8c55b4566b8200bf69fe2158485a2634 0 iD8DBQBONzIMywK+sNU5EO8RAj5SAJ0aPS3+JHnyI6bHB2Fl0LImbDmagwCdGbDLp1S7TFobxXudOH49bX45Iik=
43 4a43e23b8c55b4566b8200bf69fe2158485a2634 0 iD8DBQBONzIMywK+sNU5EO8RAj5SAJ0aPS3+JHnyI6bHB2Fl0LImbDmagwCdGbDLp1S7TFobxXudOH49bX45Iik=
44 d629f1e89021103f1753addcef6b310e4435b184 0 iD8DBQBOWAsBywK+sNU5EO8RAht4AJwJl9oNFopuGkj5m8aKuf7bqPkoAQCeNrEm7UhFsZKYT5iUOjnMV7s2LaM=
44 d629f1e89021103f1753addcef6b310e4435b184 0 iD8DBQBOWAsBywK+sNU5EO8RAht4AJwJl9oNFopuGkj5m8aKuf7bqPkoAQCeNrEm7UhFsZKYT5iUOjnMV7s2LaM=
45 351a9292e430e35766c552066ed3e87c557b803b 0 iD8DBQBOh3zUywK+sNU5EO8RApFMAKCD3Y/u3avDFndznwqfG5UeTHMlvACfUivPIVQZyDZnhZMq0UhC6zhCEQg=
45 351a9292e430e35766c552066ed3e87c557b803b 0 iD8DBQBOh3zUywK+sNU5EO8RApFMAKCD3Y/u3avDFndznwqfG5UeTHMlvACfUivPIVQZyDZnhZMq0UhC6zhCEQg=
46 384082750f2c51dc917d85a7145748330fa6ef4d 0 iD8DBQBOmd+OywK+sNU5EO8RAgDgAJ9V/X+G7VLwhTpHrZNiOHabzSyzYQCdE2kKfIevJUYB9QLAWCWP6DPwrwI=
46 384082750f2c51dc917d85a7145748330fa6ef4d 0 iD8DBQBOmd+OywK+sNU5EO8RAgDgAJ9V/X+G7VLwhTpHrZNiOHabzSyzYQCdE2kKfIevJUYB9QLAWCWP6DPwrwI=
47 41453d55b481ddfcc1dacb445179649e24ca861d 0 iD8DBQBOsFhpywK+sNU5EO8RAqM6AKCyfxUae3/zLuiLdQz+JR78690eMACfQ6JTBQib4AbE+rUDdkeFYg9K/+4=
47 41453d55b481ddfcc1dacb445179649e24ca861d 0 iD8DBQBOsFhpywK+sNU5EO8RAqM6AKCyfxUae3/zLuiLdQz+JR78690eMACfQ6JTBQib4AbE+rUDdkeFYg9K/+4=
48 195dbd1cef0c2f9f8bcf4ea303238105f716bda3 0 iD8DBQBO1/fWywK+sNU5EO8RAmoPAKCR5lpv1D6JLURHD8KVLSV4GRVEBgCgnd0Sy78ligNfqAMafmACRDvj7vo=
48 195dbd1cef0c2f9f8bcf4ea303238105f716bda3 0 iD8DBQBO1/fWywK+sNU5EO8RAmoPAKCR5lpv1D6JLURHD8KVLSV4GRVEBgCgnd0Sy78ligNfqAMafmACRDvj7vo=
49 6344043924497cd06d781d9014c66802285072e4 0 iD8DBQBPALgmywK+sNU5EO8RAlfhAJ9nYOdWnhfVDHYtDTJAyJtXBAQS9wCgnefoSQt7QABkbGxM+Q85UYEBuD0=
49 6344043924497cd06d781d9014c66802285072e4 0 iD8DBQBPALgmywK+sNU5EO8RAlfhAJ9nYOdWnhfVDHYtDTJAyJtXBAQS9wCgnefoSQt7QABkbGxM+Q85UYEBuD0=
50 db33555eafeaf9df1e18950e29439eaa706d399b 0 iD8DBQBPGdzxywK+sNU5EO8RAppkAJ9jOXhUVE/97CPgiMA0pMGiIYnesQCfengAszcBiSiKGugiI8Okc9ghU+Y=
50 db33555eafeaf9df1e18950e29439eaa706d399b 0 iD8DBQBPGdzxywK+sNU5EO8RAppkAJ9jOXhUVE/97CPgiMA0pMGiIYnesQCfengAszcBiSiKGugiI8Okc9ghU+Y=
51 2aa5b51f310fb3befd26bed99c02267f5c12c734 0 iD8DBQBPKZ9bywK+sNU5EO8RAt1TAJ45r1eJ0YqSkInzrrayg4TVCh0SnQCgm0GA/Ua74jnnDwVQ60lAwROuz1Q=
51 2aa5b51f310fb3befd26bed99c02267f5c12c734 0 iD8DBQBPKZ9bywK+sNU5EO8RAt1TAJ45r1eJ0YqSkInzrrayg4TVCh0SnQCgm0GA/Ua74jnnDwVQ60lAwROuz1Q=
52 53e2cd303ecf8ca7c7eeebd785c34e5ed6b0f4a4 0 iD8DBQBPT/fvywK+sNU5EO8RAnfYAKCn7d0vwqIb100YfWm1F7nFD5B+FACeM02YHpQLSNsztrBCObtqcnfod7Q=
52 53e2cd303ecf8ca7c7eeebd785c34e5ed6b0f4a4 0 iD8DBQBPT/fvywK+sNU5EO8RAnfYAKCn7d0vwqIb100YfWm1F7nFD5B+FACeM02YHpQLSNsztrBCObtqcnfod7Q=
53 b9bd95e61b49c221c4cca24e6da7c946fc02f992 0 iD8DBQBPeLsIywK+sNU5EO8RAvpNAKCtKe2gitz8dYn52IRF0hFOPCR7AQCfRJL/RWCFweu2T1vH/mUOCf8SXXc=
53 b9bd95e61b49c221c4cca24e6da7c946fc02f992 0 iD8DBQBPeLsIywK+sNU5EO8RAvpNAKCtKe2gitz8dYn52IRF0hFOPCR7AQCfRJL/RWCFweu2T1vH/mUOCf8SXXc=
54 d9e2f09d5488c395ae9ddbb320ceacd24757e055 0 iD8DBQBPju/dywK+sNU5EO8RArBYAJ9xtifdbk+hCOJO8OZa4JfHX8OYZQCeKPMBaBWiT8N/WHoOm1XU0q+iono=
54 d9e2f09d5488c395ae9ddbb320ceacd24757e055 0 iD8DBQBPju/dywK+sNU5EO8RArBYAJ9xtifdbk+hCOJO8OZa4JfHX8OYZQCeKPMBaBWiT8N/WHoOm1XU0q+iono=
55 00182b3d087909e3c3ae44761efecdde8f319ef3 0 iD8DBQBPoFhIywK+sNU5EO8RAhzhAKCBj1n2jxPTkZNJJ5pSp3soa+XHIgCgsZZpAQxOpXwCp0eCdNGe0+pmxmg=
55 00182b3d087909e3c3ae44761efecdde8f319ef3 0 iD8DBQBPoFhIywK+sNU5EO8RAhzhAKCBj1n2jxPTkZNJJ5pSp3soa+XHIgCgsZZpAQxOpXwCp0eCdNGe0+pmxmg=
56 5983de86462c5a9f42a3ad0f5e90ce5b1d221d25 0 iD8DBQBPovNWywK+sNU5EO8RAhgiAJ980T91FdPTRMmVONDhpkMsZwVIMACgg3bKvoWSeuCW28llUhAJtUjrMv0=
56 5983de86462c5a9f42a3ad0f5e90ce5b1d221d25 0 iD8DBQBPovNWywK+sNU5EO8RAhgiAJ980T91FdPTRMmVONDhpkMsZwVIMACgg3bKvoWSeuCW28llUhAJtUjrMv0=
57 85a358df5bbbe404ca25730c9c459b34263441dc 0 iD8DBQBPyZsWywK+sNU5EO8RAnpLAJ48qrGDJRT+pteS0mSQ11haqHstPwCdG4ccGbk+0JHb7aNy8/NRGAOqn9w=
57 85a358df5bbbe404ca25730c9c459b34263441dc 0 iD8DBQBPyZsWywK+sNU5EO8RAnpLAJ48qrGDJRT+pteS0mSQ11haqHstPwCdG4ccGbk+0JHb7aNy8/NRGAOqn9w=
58 b013baa3898e117959984fc64c29d8c784d2f28b 0 iD8DBQBP8QOPywK+sNU5EO8RAqimAKCFRSx0lvG6y8vne2IhNG062Hn0dACeMLI5/zhpWpHBIVeAAquYfx2XFeA=
58 b013baa3898e117959984fc64c29d8c784d2f28b 0 iD8DBQBP8QOPywK+sNU5EO8RAqimAKCFRSx0lvG6y8vne2IhNG062Hn0dACeMLI5/zhpWpHBIVeAAquYfx2XFeA=
59 7f5094bb3f423fc799e471aac2aee81a7ce57a0b 0 iD8DBQBQGiL8ywK+sNU5EO8RAq5oAJ4rMMCPx6O+OuzNXVOexogedWz/QgCeIiIxLd76I4pXO48tdXhr0hQcBuM=
59 7f5094bb3f423fc799e471aac2aee81a7ce57a0b 0 iD8DBQBQGiL8ywK+sNU5EO8RAq5oAJ4rMMCPx6O+OuzNXVOexogedWz/QgCeIiIxLd76I4pXO48tdXhr0hQcBuM=
60 072209ae4ddb654eb2d5fd35bff358c738414432 0 iD8DBQBQQkq0ywK+sNU5EO8RArDTAJ9nk5CySnNAjAXYvqvx4uWCw9ThZwCgqmFRehH/l+oTwj3f8nw8u8qTCdc=
60 072209ae4ddb654eb2d5fd35bff358c738414432 0 iD8DBQBQQkq0ywK+sNU5EO8RArDTAJ9nk5CySnNAjAXYvqvx4uWCw9ThZwCgqmFRehH/l+oTwj3f8nw8u8qTCdc=
61 b3f0f9a39c4e1d0250048cd803ab03542d6f140a 0 iD8DBQBQamltywK+sNU5EO8RAlsqAJ4qF/m6aFu4mJCOKTiAP5RvZFK02ACfawYShUZO6OXEFfveU0aAxDR0M1k=
61 b3f0f9a39c4e1d0250048cd803ab03542d6f140a 0 iD8DBQBQamltywK+sNU5EO8RAlsqAJ4qF/m6aFu4mJCOKTiAP5RvZFK02ACfawYShUZO6OXEFfveU0aAxDR0M1k=
62 d118a4f4fd16d9b558ec3f3e87bfee772861d2b7 0 iD8DBQBQgPV5ywK+sNU5EO8RArylAJ0abcx5NlDjyv3ZDWpAfRIHyRsJtQCgn4TMuEayqgxzrvadQZHdTEU2g38=
62 d118a4f4fd16d9b558ec3f3e87bfee772861d2b7 0 iD8DBQBQgPV5ywK+sNU5EO8RArylAJ0abcx5NlDjyv3ZDWpAfRIHyRsJtQCgn4TMuEayqgxzrvadQZHdTEU2g38=
63 195ad823b5d58c68903a6153a25e3fb4ed25239d 0 iD8DBQBQkuT9ywK+sNU5EO8RAhB4AKCeerItoK2Jipm2cVf4euGofAa/WACeJj3TVd4pFILpb+ogj7ebweFLJi0=
63 195ad823b5d58c68903a6153a25e3fb4ed25239d 0 iD8DBQBQkuT9ywK+sNU5EO8RAhB4AKCeerItoK2Jipm2cVf4euGofAa/WACeJj3TVd4pFILpb+ogj7ebweFLJi0=
64 0c10cf8191469e7c3c8844922e17e71a176cb7cb 0 iD8DBQBQvQWoywK+sNU5EO8RAnq3AJoCn98u4geFx5YaQaeh99gFhCd7bQCgjoBwBSUyOvGd0yBy60E3Vv3VZhM=
64 0c10cf8191469e7c3c8844922e17e71a176cb7cb 0 iD8DBQBQvQWoywK+sNU5EO8RAnq3AJoCn98u4geFx5YaQaeh99gFhCd7bQCgjoBwBSUyOvGd0yBy60E3Vv3VZhM=
65 a4765077b65e6ae29ba42bab7834717b5072d5ba 0 iD8DBQBQ486sywK+sNU5EO8RAhmJAJ90aLfLKZhmcZN7kqphigQJxiFOQACeJ5IUZxjGKH4xzi3MrgIcx9n+dB0=
65 a4765077b65e6ae29ba42bab7834717b5072d5ba 0 iD8DBQBQ486sywK+sNU5EO8RAhmJAJ90aLfLKZhmcZN7kqphigQJxiFOQACeJ5IUZxjGKH4xzi3MrgIcx9n+dB0=
66 f5fbe15ca7449f2c9a3cf817c86d0ae68b307214 0 iD8DBQBQ+yuYywK+sNU5EO8RAm9JAJoD/UciWvpGeKBcpGtZJBFJVcL/HACghDXSgQ+xQDjB+6uGrdgAQsRR1Lg=
66 f5fbe15ca7449f2c9a3cf817c86d0ae68b307214 0 iD8DBQBQ+yuYywK+sNU5EO8RAm9JAJoD/UciWvpGeKBcpGtZJBFJVcL/HACghDXSgQ+xQDjB+6uGrdgAQsRR1Lg=
67 a6088c05e43a8aee0472ca3a4f6f8d7dd914ebbf 0 iD8DBQBRDDROywK+sNU5EO8RAh75AJ9uJCGoCWnP0Lv/+XuYs4hvUl+sAgCcD36QgAnuw8IQXrvv684BAXAnHcA=
67 a6088c05e43a8aee0472ca3a4f6f8d7dd914ebbf 0 iD8DBQBRDDROywK+sNU5EO8RAh75AJ9uJCGoCWnP0Lv/+XuYs4hvUl+sAgCcD36QgAnuw8IQXrvv684BAXAnHcA=
68 7511d4df752e61fe7ae4f3682e0a0008573b0402 0 iD8DBQBRFYaoywK+sNU5EO8RAuErAJoDyhXn+lptU3+AevVdwAIeNFyR2gCdHzPHyWd+JDeWCUR+pSOBi8O2ppM=
68 7511d4df752e61fe7ae4f3682e0a0008573b0402 0 iD8DBQBRFYaoywK+sNU5EO8RAuErAJoDyhXn+lptU3+AevVdwAIeNFyR2gCdHzPHyWd+JDeWCUR+pSOBi8O2ppM=
69 5b7175377babacce80a6c1e12366d8032a6d4340 0 iD8DBQBRMCYgywK+sNU5EO8RAq1/AKCWKlt9ysibyQgYwoxxIOZv5J8rpwCcDSHQaaf1fFZUTnQsOePwcM2Y/Sg=
69 5b7175377babacce80a6c1e12366d8032a6d4340 0 iD8DBQBRMCYgywK+sNU5EO8RAq1/AKCWKlt9ysibyQgYwoxxIOZv5J8rpwCcDSHQaaf1fFZUTnQsOePwcM2Y/Sg=
70 50c922c1b5145dab8baefefb0437d363b6a6c21c 0 iD8DBQBRWnUnywK+sNU5EO8RAuQRAJwM42cJqJPeqJ0jVNdMqKMDqr4dSACeP0cRVGz1gitMuV0x8f3mrZrqc7I=
70 50c922c1b5145dab8baefefb0437d363b6a6c21c 0 iD8DBQBRWnUnywK+sNU5EO8RAuQRAJwM42cJqJPeqJ0jVNdMqKMDqr4dSACeP0cRVGz1gitMuV0x8f3mrZrqc7I=
71 8a7bd2dccd44ed571afe7424cd7f95594f27c092 0 iD8DBQBRXfBvywK+sNU5EO8RAn+LAKCsMmflbuXjYRxlzFwId5ptm8TZcwCdGkyLbZcASBOkzQUm/WW1qfknJHU=
71 8a7bd2dccd44ed571afe7424cd7f95594f27c092 0 iD8DBQBRXfBvywK+sNU5EO8RAn+LAKCsMmflbuXjYRxlzFwId5ptm8TZcwCdGkyLbZcASBOkzQUm/WW1qfknJHU=
72 292cd385856d98bacb2c3086f8897bc660c2beea 0 iD8DBQBRcM0BywK+sNU5EO8RAjp4AKCJBykQbvXhKuvLSMxKx3a2TBiXcACfbr/kLg5GlZTF/XDPmY+PyHgI/GM=
72 292cd385856d98bacb2c3086f8897bc660c2beea 0 iD8DBQBRcM0BywK+sNU5EO8RAjp4AKCJBykQbvXhKuvLSMxKx3a2TBiXcACfbr/kLg5GlZTF/XDPmY+PyHgI/GM=
73 23f785b38af38d2fca6b8f3db56b8007a84cd73a 0 iD8DBQBRgZwNywK+sNU5EO8RAmO4AJ4u2ILGuimRP6MJgE2t65LZ5dAdkACgiENEstIdrlFC80p+sWKD81kKIYI=
73 23f785b38af38d2fca6b8f3db56b8007a84cd73a 0 iD8DBQBRgZwNywK+sNU5EO8RAmO4AJ4u2ILGuimRP6MJgE2t65LZ5dAdkACgiENEstIdrlFC80p+sWKD81kKIYI=
74 ddc7a6be20212d18f3e27d9d7e6f079a66d96f21 0 iD8DBQBRkswvywK+sNU5EO8RAiYYAJsHTHyHbJeAgmGvBTmDrfcKu4doUgCeLm7eGBjx7yAPUvEtxef8rAkQmXI=
74 ddc7a6be20212d18f3e27d9d7e6f079a66d96f21 0 iD8DBQBRkswvywK+sNU5EO8RAiYYAJsHTHyHbJeAgmGvBTmDrfcKu4doUgCeLm7eGBjx7yAPUvEtxef8rAkQmXI=
75 cceaf7af4c9e9e6fa2dbfdcfe9856c5da69c4ffd 0 iD8DBQBRqnFLywK+sNU5EO8RAsWNAJ9RR6t+y1DLFc2HeH0eN9VfZAKF9gCeJ8ezvhtKq/LMs0/nvcgKQc/d5jk=
75 cceaf7af4c9e9e6fa2dbfdcfe9856c5da69c4ffd 0 iD8DBQBRqnFLywK+sNU5EO8RAsWNAJ9RR6t+y1DLFc2HeH0eN9VfZAKF9gCeJ8ezvhtKq/LMs0/nvcgKQc/d5jk=
76 009794acc6e37a650f0fae37872e733382ac1c0c 0 iD8DBQBR0guxywK+sNU5EO8RArNkAKCq9pMihVzP8Os5kCmgbWpe5C37wgCgqzuPZTHvAsXF5wTyaSTMVa9Ccq4=
76 009794acc6e37a650f0fae37872e733382ac1c0c 0 iD8DBQBR0guxywK+sNU5EO8RArNkAKCq9pMihVzP8Os5kCmgbWpe5C37wgCgqzuPZTHvAsXF5wTyaSTMVa9Ccq4=
77 f0d7721d7322dcfb5af33599c2543f27335334bb 0 iD8DBQBR8taaywK+sNU5EO8RAqeEAJ4idDhhDuEsgsUjeQgWNj498matHACfT67gSF5w0ylsrBx1Hb52HkGXDm0=
77 f0d7721d7322dcfb5af33599c2543f27335334bb 0 iD8DBQBR8taaywK+sNU5EO8RAqeEAJ4idDhhDuEsgsUjeQgWNj498matHACfT67gSF5w0ylsrBx1Hb52HkGXDm0=
78 f37b5a17e6a0ee17afde2cdde5393dd74715fb58 0 iD8DBQBR+ymFywK+sNU5EO8RAuSdAJkBMcd9DAZ3rWE9WGKPm2YZ8LBoXACfXn/wbEsVy7ZgJoUwiWmHSnQaWCI=
78 f37b5a17e6a0ee17afde2cdde5393dd74715fb58 0 iD8DBQBR+ymFywK+sNU5EO8RAuSdAJkBMcd9DAZ3rWE9WGKPm2YZ8LBoXACfXn/wbEsVy7ZgJoUwiWmHSnQaWCI=
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=
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 e7fa36d2ad3a7944a52dca126458d6f482db3524 0 iQIVAwUAUktg4yBXgaxoKi1yAQLO0g//du/2ypYYUfmM/yZ4zztNKIvgMSGTDVbCCGB2y2/wk2EcolpjpGTkcgnJT413ksYtw78ZU+mvv0RjgrFCm8DQ8kroJaQZ2qHmtSUb42hPBPvtg6kL9YaA4yvp87uUBpFRavGS5uX4hhEIyvZKzhXUBvqtL3TfwR7ld21bj8j00wudqELyyU9IrojIY9jkJ3XL/4shBGgP7u6OK5g8yJ6zTnWgysUetxHBPrYjG25lziiiZQFvZqK1B3PUqAOaFPltQs0PB8ipOCAHQgJsjaREj8VmC3+rskmSSy66NHm6gAB9+E8oAgOcU7FzWbdYgnz4kR3M7TQvHX9U61NinPXC6Q9d1VPhO3E6sIGvqJ4YeQOn65V9ezYuIpFSlgQzCHMmLVnOV96Uv1R/Z39I4w7D3S5qoZcQT/siQwGbsZoPMGFYmqOK1da5TZWrrJWkYzc9xvzT9m3q3Wds5pmCmo4b/dIqDifWwYEcNAZ0/YLHwCN5SEZWuunkEwtU5o7TZAv3bvDDA6WxUrrHI/y9/qvvhXxsJnY8IueNhshdmWZfXKz+lJi2Dvk7DUlEQ1zZWSsozi1E+3biMPJO47jsxjoT/jmE5+GHLCgcnXXDVBeaVal99IOaTRFukiz2EMsry1s8fnwEE5XKDKRlU/dOPfsje0gc7bgE0QD/u3E4NJ99g9A=
80 e7fa36d2ad3a7944a52dca126458d6f482db3524 0 iQIVAwUAUktg4yBXgaxoKi1yAQLO0g//du/2ypYYUfmM/yZ4zztNKIvgMSGTDVbCCGB2y2/wk2EcolpjpGTkcgnJT413ksYtw78ZU+mvv0RjgrFCm8DQ8kroJaQZ2qHmtSUb42hPBPvtg6kL9YaA4yvp87uUBpFRavGS5uX4hhEIyvZKzhXUBvqtL3TfwR7ld21bj8j00wudqELyyU9IrojIY9jkJ3XL/4shBGgP7u6OK5g8yJ6zTnWgysUetxHBPrYjG25lziiiZQFvZqK1B3PUqAOaFPltQs0PB8ipOCAHQgJsjaREj8VmC3+rskmSSy66NHm6gAB9+E8oAgOcU7FzWbdYgnz4kR3M7TQvHX9U61NinPXC6Q9d1VPhO3E6sIGvqJ4YeQOn65V9ezYuIpFSlgQzCHMmLVnOV96Uv1R/Z39I4w7D3S5qoZcQT/siQwGbsZoPMGFYmqOK1da5TZWrrJWkYzc9xvzT9m3q3Wds5pmCmo4b/dIqDifWwYEcNAZ0/YLHwCN5SEZWuunkEwtU5o7TZAv3bvDDA6WxUrrHI/y9/qvvhXxsJnY8IueNhshdmWZfXKz+lJi2Dvk7DUlEQ1zZWSsozi1E+3biMPJO47jsxjoT/jmE5+GHLCgcnXXDVBeaVal99IOaTRFukiz2EMsry1s8fnwEE5XKDKRlU/dOPfsje0gc7bgE0QD/u3E4NJ99g9A=
81 1596f2d8f2421314b1ddead8f7d0c91009358994 0 iQIVAwUAUmRq+yBXgaxoKi1yAQLolhAAi+l4ZFdQTu9yJDv22YmkmHH4fI3d5VBYgvfJPufpyaj7pX626QNW18UNcGSw2BBpYHIJzWPkk/4XznLVKr4Ciw2N3/yqloEFV0V2SSrTbMWiR9qXI4KJH+Df3KZnKs3FgiYpXkErL4GWkc1jLVR50xQ5RnkMljjtCd0NTeV2PHZ6gP2qbu6CS+5sm3AFhTDGnx8GicbMw76ZNw5M2G+T48yH9jn5KQi2SBThfi4H9Bpr8FDuR7PzQLgw9SbtYxtdQxNkK55k0nG4oLDxduNakU6SH9t8n8tdCfMt58kTzlQVrPFiTFjKu2n2JioDTz2HEivbZ5H757cu7SvpX8gW3paeBc57e+GOLMisMZABXLICq59c3QnrMwFY4FG+5cpiHVXoaZz/0bYCJx+IhU4QLWqZuzb18KSyHUCqQRzXlzS6QV5O7dY5YNQXFC44j/dS5zdgWMYo2mc6mVP2OaPUn7F6aQh5MCDYorPIOkcNjOg7ytajo7DXbzWt5Al8qt6386BJksyR3GAonc09+l8IFeNxk8HZNP4ETQ8aWj0dC9jgBDPK43T2Bju/i84s+U/bRe4tGSQalZUEv06mkIH/VRJp5w2izYTsdIjA4FT9d36OhaxlfoO1X6tHR9AyA3bF/g/ozvBwuo3kTRUUqo+Ggvx/DmcPQdDiZZQIqDBXch0=
81 1596f2d8f2421314b1ddead8f7d0c91009358994 0 iQIVAwUAUmRq+yBXgaxoKi1yAQLolhAAi+l4ZFdQTu9yJDv22YmkmHH4fI3d5VBYgvfJPufpyaj7pX626QNW18UNcGSw2BBpYHIJzWPkk/4XznLVKr4Ciw2N3/yqloEFV0V2SSrTbMWiR9qXI4KJH+Df3KZnKs3FgiYpXkErL4GWkc1jLVR50xQ5RnkMljjtCd0NTeV2PHZ6gP2qbu6CS+5sm3AFhTDGnx8GicbMw76ZNw5M2G+T48yH9jn5KQi2SBThfi4H9Bpr8FDuR7PzQLgw9SbtYxtdQxNkK55k0nG4oLDxduNakU6SH9t8n8tdCfMt58kTzlQVrPFiTFjKu2n2JioDTz2HEivbZ5H757cu7SvpX8gW3paeBc57e+GOLMisMZABXLICq59c3QnrMwFY4FG+5cpiHVXoaZz/0bYCJx+IhU4QLWqZuzb18KSyHUCqQRzXlzS6QV5O7dY5YNQXFC44j/dS5zdgWMYo2mc6mVP2OaPUn7F6aQh5MCDYorPIOkcNjOg7ytajo7DXbzWt5Al8qt6386BJksyR3GAonc09+l8IFeNxk8HZNP4ETQ8aWj0dC9jgBDPK43T2Bju/i84s+U/bRe4tGSQalZUEv06mkIH/VRJp5w2izYTsdIjA4FT9d36OhaxlfoO1X6tHR9AyA3bF/g/ozvBwuo3kTRUUqo+Ggvx/DmcPQdDiZZQIqDBXch0=
82 d825e4025e39d1c39db943cdc89818abd0a87c27 0 iQIVAwUAUnQlXiBXgaxoKi1yAQJd3BAAi7LjMSpXmdR7B8K98C3/By4YHsCOAocMl3JXiLd7SXwKmlta1zxtkgWwWJnNYE3lVJvGCl+l4YsGKmFu755MGXlyORh1x4ohckoC1a8cqnbNAgD6CSvjSaZfnINLGZQP1wIP4yWj0FftKVANQBjj/xkkxO530mjBYnUvyA4PeDd5A1AOUUu6qHzX6S5LcprEt7iktLI+Ae1dYTkiCpckDtyYUKIk3RK/4AGWwGCPddVWeV5bDxLs8GHyMbqdBwx+2EAMtyZfXT+z6MDRsL/gEBVOXHb/UR0qpYED+qFnbtTlxqQkRE/wBhwDoRzUgcSuukQ9iPn79WNDSdT5b6Jd393uEO5BNF/DB6rrOiWmlpoooWgTY9kcwGB02v0hhLrH5r1wkv8baaPl+qjCjBxf4CNKm/83KN5/umGbZlORqPSN5JVxK6vDNwFFmHLaZbMT1g27GsGOWm84VH+dgolgk4nmRNSO37eTNM5Y1C3Zf2amiqDSRcAxCgseg0Jh10G7i52SSTcZPI2MqrwT9eIyg8PTIxT1D5bPcCzkg5nTTL6S7bet7OSwynRnHslhvVUBly8aIj4eY/5cQqAucUUa5sq6xLD8N27Tl+sQi+kE6KtWu2c0ZhpouflYp55XNMHgU4KeFcVcDtHfJRF6THT6tFcHFNauCHbhfN2F33ANMP4=
82 d825e4025e39d1c39db943cdc89818abd0a87c27 0 iQIVAwUAUnQlXiBXgaxoKi1yAQJd3BAAi7LjMSpXmdR7B8K98C3/By4YHsCOAocMl3JXiLd7SXwKmlta1zxtkgWwWJnNYE3lVJvGCl+l4YsGKmFu755MGXlyORh1x4ohckoC1a8cqnbNAgD6CSvjSaZfnINLGZQP1wIP4yWj0FftKVANQBjj/xkkxO530mjBYnUvyA4PeDd5A1AOUUu6qHzX6S5LcprEt7iktLI+Ae1dYTkiCpckDtyYUKIk3RK/4AGWwGCPddVWeV5bDxLs8GHyMbqdBwx+2EAMtyZfXT+z6MDRsL/gEBVOXHb/UR0qpYED+qFnbtTlxqQkRE/wBhwDoRzUgcSuukQ9iPn79WNDSdT5b6Jd393uEO5BNF/DB6rrOiWmlpoooWgTY9kcwGB02v0hhLrH5r1wkv8baaPl+qjCjBxf4CNKm/83KN5/umGbZlORqPSN5JVxK6vDNwFFmHLaZbMT1g27GsGOWm84VH+dgolgk4nmRNSO37eTNM5Y1C3Zf2amiqDSRcAxCgseg0Jh10G7i52SSTcZPI2MqrwT9eIyg8PTIxT1D5bPcCzkg5nTTL6S7bet7OSwynRnHslhvVUBly8aIj4eY/5cQqAucUUa5sq6xLD8N27Tl+sQi+kE6KtWu2c0ZhpouflYp55XNMHgU4KeFcVcDtHfJRF6THT6tFcHFNauCHbhfN2F33ANMP4=
83 209e04a06467e2969c0cc6501335be0406d46ef0 0 iQIVAwUAUpv1oCBXgaxoKi1yAQKOFBAAma2wlsr3w/5NvDwq2rmOrgtNDq1DnNqcXloaOdwegX1z3/N++5uVjLjI0VyguexnwK+7E8rypMZ+4glaiZvIiGPnGMYbG9iOoz5XBhtUHzI5ECYfm5QU81by9VmCIvArDFe5Hlnz4XaXpEGnAwPywD+yzV3/+tyoV7MgsVinCMtbX9OF84/ubWKNzq2810FpQRfYoCOrF8sUed/1TcQrSm1eMB/PnuxjFCFySiR6J7Urd9bJoJIDtdZOQeeHaL5Z8Pcsyzjoe/9oTwJ3L3tl/NMZtRxiQUWtfRA0zvEnQ4QEkZSDMd/JnGiWHPVeP4P92+YN15za9yhneEAtustrTNAmVF2Uh92RIlmkG475HFhvwPJ4DfCx0vU1OOKX/U4c1rifW7H7HaipoaMlsDU2VFsAHcc3YF8ulVt27bH2yUaLGJz7eqpt+3DzZTKp4d/brZA2EkbVgsoYP+XYLbzxfwWlaMwiN3iCnlTFbNogH8MxhfHFWBj6ouikqOz8HlNl6BmSQiUCBnz5fquVpXmW2Md+TDekk+uOW9mvk1QMU62br+Z6PEZupkdTrqKaz+8ZMWvTRct8SiOcu7R11LpfERyrwYGGPei0P2YrEGIWGgXvEobXoPTSl7J+mpOA/rp2Q1zA3ihjgzwtGZZF+ThQXZGIMGaA2YPgzuYRqY8l5oc=
83 209e04a06467e2969c0cc6501335be0406d46ef0 0 iQIVAwUAUpv1oCBXgaxoKi1yAQKOFBAAma2wlsr3w/5NvDwq2rmOrgtNDq1DnNqcXloaOdwegX1z3/N++5uVjLjI0VyguexnwK+7E8rypMZ+4glaiZvIiGPnGMYbG9iOoz5XBhtUHzI5ECYfm5QU81by9VmCIvArDFe5Hlnz4XaXpEGnAwPywD+yzV3/+tyoV7MgsVinCMtbX9OF84/ubWKNzq2810FpQRfYoCOrF8sUed/1TcQrSm1eMB/PnuxjFCFySiR6J7Urd9bJoJIDtdZOQeeHaL5Z8Pcsyzjoe/9oTwJ3L3tl/NMZtRxiQUWtfRA0zvEnQ4QEkZSDMd/JnGiWHPVeP4P92+YN15za9yhneEAtustrTNAmVF2Uh92RIlmkG475HFhvwPJ4DfCx0vU1OOKX/U4c1rifW7H7HaipoaMlsDU2VFsAHcc3YF8ulVt27bH2yUaLGJz7eqpt+3DzZTKp4d/brZA2EkbVgsoYP+XYLbzxfwWlaMwiN3iCnlTFbNogH8MxhfHFWBj6ouikqOz8HlNl6BmSQiUCBnz5fquVpXmW2Md+TDekk+uOW9mvk1QMU62br+Z6PEZupkdTrqKaz+8ZMWvTRct8SiOcu7R11LpfERyrwYGGPei0P2YrEGIWGgXvEobXoPTSl7J+mpOA/rp2Q1zA3ihjgzwtGZZF+ThQXZGIMGaA2YPgzuYRqY8l5oc=
84 ca387377df7a3a67dbb90b6336b781cdadc3ef41 0 iQIVAwUAUsThISBXgaxoKi1yAQJpvRAAkRkCWLjHBZnWxX9Oe6t2HQgkSsmn9wMHvXXGFkcAmrqJ86yfyrxLq2Ns0X7Qwky37kOwKsywM53FQlsx9j//Y+ncnGZoObFTz9YTuSbOHGVsTbAruXWxBrGOf1nFTlg8afcbH0jPfQXwxf3ptfBhgsFCzORcqc8HNopAW+2sgXGhHnbVtq6LF90PWkbKjCCQLiX3da1uETGAElrl4jA5Y2i64S1Q/2X+UFrNslkIIRCGmAJ6BnE6KLJaUftpfbN7Br7a3z9xxWqxRYDOinxDgfAPAucOJPLgMVQ0bJIallaRu7KTmIWKIuSBgg1/hgfoX8I1w49WrTGp0gGY140kl8RWwczAz/SB03Xtbl2+h6PV7rUV2K/5g61DkwdVbWqXM9wmJZmvjEKK0qQbBT0By4QSEDNcKKqtaFFwhFzx4dkXph0igHOtXhSNzMd8PsFx/NRn9NLFIpirxfqVDwakpDNBZw4Q9hUAlTPxSFL3vD9/Zs7lV4/dAvvl+tixJEi2k/iv248b/AI1PrPIQEqDvjrozzzYvrS4HtbkUn+IiHiepQaYnpqKoXvBu6btK/nv0GTxB5OwVJzMA1RPDcxIFfZA2AazHjrXiPAl5uWYEddEvRjaCiF8xkQkfiXzLOoqhKQHdwPGcfMFEs9lNR8BrB2ZOajBJc8RPsFDswhT5h4=
84 ca387377df7a3a67dbb90b6336b781cdadc3ef41 0 iQIVAwUAUsThISBXgaxoKi1yAQJpvRAAkRkCWLjHBZnWxX9Oe6t2HQgkSsmn9wMHvXXGFkcAmrqJ86yfyrxLq2Ns0X7Qwky37kOwKsywM53FQlsx9j//Y+ncnGZoObFTz9YTuSbOHGVsTbAruXWxBrGOf1nFTlg8afcbH0jPfQXwxf3ptfBhgsFCzORcqc8HNopAW+2sgXGhHnbVtq6LF90PWkbKjCCQLiX3da1uETGAElrl4jA5Y2i64S1Q/2X+UFrNslkIIRCGmAJ6BnE6KLJaUftpfbN7Br7a3z9xxWqxRYDOinxDgfAPAucOJPLgMVQ0bJIallaRu7KTmIWKIuSBgg1/hgfoX8I1w49WrTGp0gGY140kl8RWwczAz/SB03Xtbl2+h6PV7rUV2K/5g61DkwdVbWqXM9wmJZmvjEKK0qQbBT0By4QSEDNcKKqtaFFwhFzx4dkXph0igHOtXhSNzMd8PsFx/NRn9NLFIpirxfqVDwakpDNBZw4Q9hUAlTPxSFL3vD9/Zs7lV4/dAvvl+tixJEi2k/iv248b/AI1PrPIQEqDvjrozzzYvrS4HtbkUn+IiHiepQaYnpqKoXvBu6btK/nv0GTxB5OwVJzMA1RPDcxIFfZA2AazHjrXiPAl5uWYEddEvRjaCiF8xkQkfiXzLOoqhKQHdwPGcfMFEs9lNR8BrB2ZOajBJc8RPsFDswhT5h4=
85 8862469e16f9236208581b20de5f96bd13cc039d 0 iQIVAwUAUt7cLSBXgaxoKi1yAQLOkRAAidp501zafqe+JnDwlf7ORcJc+FgCE6mK1gxDfReCbkMsY7AzspogU7orqfSmr6XXdrDwmk3Y5x3mf44OGzNQjvuNWhqnTgJ7sOcU/lICGQUc8WiGNzHEMFGX9S+K4dpUaBf8Tcl8pU3iArhlthDghW6SZeDFB/FDBaUx9dkdFp6eXrmu4OuGRZEvwUvPtCGxIL7nKNnufI1du/MsWQxvC2ORHbMNtRq6tjA0fLZi4SvbySuYifQRS32BfHkFS5Qu4/40+1k7kd0YFyyQUvIsVa17lrix3zDqMavG8x7oOlqM/axDMBT6DhpdBMAdc5qqf8myz8lwjlFjyDUL6u3Z4/yE0nUrmEudXiXwG0xbVoEN8SCNrDmmvFMt6qdCpdDMkHr2TuSh0Hh4FT5CDkzPI8ZRssv/01j/QvIO3c/xlbpGRPWpsPXEVOz3pmjYN4qyQesnBKWCENsQLy/8s2rey8iQgx2GtsrNw8+wGX6XE4v3QtwUrRe12hWoNrEHWl0xnLv2mvAFqdMAMpFY6EpOKLlE4hoCs2CmTJ2dv6e2tiGTXGU6/frI5iuNRK61OXnH5OjEc8DCGH/GC7NXyDOXOB+7BdBvvf50l2C/vxR2TKgTncLtHeLCrR0GHNHsxqRo1UDwOWur0r7fdfCRvb2tIr5LORCqKYVKd60/BAXjHWc=
85 8862469e16f9236208581b20de5f96bd13cc039d 0 iQIVAwUAUt7cLSBXgaxoKi1yAQLOkRAAidp501zafqe+JnDwlf7ORcJc+FgCE6mK1gxDfReCbkMsY7AzspogU7orqfSmr6XXdrDwmk3Y5x3mf44OGzNQjvuNWhqnTgJ7sOcU/lICGQUc8WiGNzHEMFGX9S+K4dpUaBf8Tcl8pU3iArhlthDghW6SZeDFB/FDBaUx9dkdFp6eXrmu4OuGRZEvwUvPtCGxIL7nKNnufI1du/MsWQxvC2ORHbMNtRq6tjA0fLZi4SvbySuYifQRS32BfHkFS5Qu4/40+1k7kd0YFyyQUvIsVa17lrix3zDqMavG8x7oOlqM/axDMBT6DhpdBMAdc5qqf8myz8lwjlFjyDUL6u3Z4/yE0nUrmEudXiXwG0xbVoEN8SCNrDmmvFMt6qdCpdDMkHr2TuSh0Hh4FT5CDkzPI8ZRssv/01j/QvIO3c/xlbpGRPWpsPXEVOz3pmjYN4qyQesnBKWCENsQLy/8s2rey8iQgx2GtsrNw8+wGX6XE4v3QtwUrRe12hWoNrEHWl0xnLv2mvAFqdMAMpFY6EpOKLlE4hoCs2CmTJ2dv6e2tiGTXGU6/frI5iuNRK61OXnH5OjEc8DCGH/GC7NXyDOXOB+7BdBvvf50l2C/vxR2TKgTncLtHeLCrR0GHNHsxqRo1UDwOWur0r7fdfCRvb2tIr5LORCqKYVKd60/BAXjHWc=
86 3cec5134e9c4bceab6a00c60f52a4f80677a78f2 0 iQIVAwUAUu1lIyBXgaxoKi1yAQIzCBAAizSWvTkWt8+tReM9jUetoSToF+XahLhn381AYdErFCBErX4bNL+vyEj+Jt2DHsAfabkvNBe3k7rtFlXHwpq6POa/ciFGPDhFlplNv6yN1jOKBlMsgdjpn7plZKcLHODOigU7IMlgg70Um8qVrRgQ8FhvbVgR2I5+CD6bucFzqo78wNl9mCIHIQCpGKIUoz56GbwT+rUpEB182Z3u6rf4NWj35RZLGAicVV2A2eAAFh4ZvuC+Z0tXMkp6Gq9cINawZgqfLbzVYJeXBtJC39lHPyp5P3LaEVRhntc9YTwbfkVGjyJZR60iYrieeKpOYRnzgHauPVdgVhkTkBxshmEPY7svKYSQqlj8hLuFa+a3ajbIPrpQAAi1MgtamA991atNqGiSTjdZa9kLQvfdn0k80+gkCxpuO56PhvtdjKsYVRgQMTYmQVQdh3x4WbQOSqTADXXIZUaWxx4RmNSlxY7KD+3lPP09teOD+A3B2cP60bC5NsCfULtQFXQzdC7NvfIyYfYBTZa+Pv6HFkVe10cbnqTt83hBy0D77vdaegPRe56qDNU+GrIG2/rosnlKGFjFoK/pTYkR9uzfkrhEjLwyfkoXlBqY+376W0PC5fP10pJeQBS9DuXpCPlgtyW0Jy1ayCT1YR4QJC4n75vZwTFBFRBhSi0HqFquOgy83+O0Q/k=
86 3cec5134e9c4bceab6a00c60f52a4f80677a78f2 0 iQIVAwUAUu1lIyBXgaxoKi1yAQIzCBAAizSWvTkWt8+tReM9jUetoSToF+XahLhn381AYdErFCBErX4bNL+vyEj+Jt2DHsAfabkvNBe3k7rtFlXHwpq6POa/ciFGPDhFlplNv6yN1jOKBlMsgdjpn7plZKcLHODOigU7IMlgg70Um8qVrRgQ8FhvbVgR2I5+CD6bucFzqo78wNl9mCIHIQCpGKIUoz56GbwT+rUpEB182Z3u6rf4NWj35RZLGAicVV2A2eAAFh4ZvuC+Z0tXMkp6Gq9cINawZgqfLbzVYJeXBtJC39lHPyp5P3LaEVRhntc9YTwbfkVGjyJZR60iYrieeKpOYRnzgHauPVdgVhkTkBxshmEPY7svKYSQqlj8hLuFa+a3ajbIPrpQAAi1MgtamA991atNqGiSTjdZa9kLQvfdn0k80+gkCxpuO56PhvtdjKsYVRgQMTYmQVQdh3x4WbQOSqTADXXIZUaWxx4RmNSlxY7KD+3lPP09teOD+A3B2cP60bC5NsCfULtQFXQzdC7NvfIyYfYBTZa+Pv6HFkVe10cbnqTt83hBy0D77vdaegPRe56qDNU+GrIG2/rosnlKGFjFoK/pTYkR9uzfkrhEjLwyfkoXlBqY+376W0PC5fP10pJeQBS9DuXpCPlgtyW0Jy1ayCT1YR4QJC4n75vZwTFBFRBhSi0HqFquOgy83+O0Q/k=
87 b96cb15ec9e04d8ac5ee08b34fcbbe4200588965 0 iQIVAwUAUxJPlyBXgaxoKi1yAQLIRA//Qh9qzoYthPAWAUNbzybWXC/oMBI2X89NQC7l1ivKhv7cn9L79D8SWXM18q7LTwLdlwOkV/a0NTE3tkQTLvxJpfnRLCBbMOcGiIn/PxsAae8IhMAUbR7qz+XOynHOs60ZhK9X8seQHJRf1YtOI9gYTL/WYk8Cnpmc6xZQ90TNhoPPkpdfe8Y236V11SbYtN14fmrPaWQ3GXwyrvQaqM1F7BxSnC/sbm9+/wprsTa8gRQo7YQL/T5jJQgFiatG3yayrDdJtoRq3TZKtsxw8gtQdfVCrrBibbysjM8++dnwA92apHNUY8LzyptPy7rSDXRrIpPUWGGTQTD+6HQwkcLFtIuUpw4I75SV3z2r6LyOLKzDJUIunKOOYFS/rEIQGxZHxZOBAvbI+73mHAn3pJqm+UAA7R1n7tk3JyQncg50qJlm9zIUPGpNFcdEqak5iXzGYx292VlcE+fbJYeIPWggpilaVUgdmXtMCG0O0uX6C8MDmzVDCjd6FzDJ4GTZwgmWJaamvls85CkZgyN/UqlisfFXub0A1h7qAzBSVpP1+Ti+UbBjlrGX8BMRYHRGYIeIq16elcWwSpLgshjDwNn2r2EdwX8xKU5mucgTzSLprbOYGdQaqnvf6e8IX5WMBgwVW9YdY9yJKSLF7kE1AlM9nfVcXwOK4mHoMvnNgiX3zsw=
87 b96cb15ec9e04d8ac5ee08b34fcbbe4200588965 0 iQIVAwUAUxJPlyBXgaxoKi1yAQLIRA//Qh9qzoYthPAWAUNbzybWXC/oMBI2X89NQC7l1ivKhv7cn9L79D8SWXM18q7LTwLdlwOkV/a0NTE3tkQTLvxJpfnRLCBbMOcGiIn/PxsAae8IhMAUbR7qz+XOynHOs60ZhK9X8seQHJRf1YtOI9gYTL/WYk8Cnpmc6xZQ90TNhoPPkpdfe8Y236V11SbYtN14fmrPaWQ3GXwyrvQaqM1F7BxSnC/sbm9+/wprsTa8gRQo7YQL/T5jJQgFiatG3yayrDdJtoRq3TZKtsxw8gtQdfVCrrBibbysjM8++dnwA92apHNUY8LzyptPy7rSDXRrIpPUWGGTQTD+6HQwkcLFtIuUpw4I75SV3z2r6LyOLKzDJUIunKOOYFS/rEIQGxZHxZOBAvbI+73mHAn3pJqm+UAA7R1n7tk3JyQncg50qJlm9zIUPGpNFcdEqak5iXzGYx292VlcE+fbJYeIPWggpilaVUgdmXtMCG0O0uX6C8MDmzVDCjd6FzDJ4GTZwgmWJaamvls85CkZgyN/UqlisfFXub0A1h7qAzBSVpP1+Ti+UbBjlrGX8BMRYHRGYIeIq16elcWwSpLgshjDwNn2r2EdwX8xKU5mucgTzSLprbOYGdQaqnvf6e8IX5WMBgwVW9YdY9yJKSLF7kE1AlM9nfVcXwOK4mHoMvnNgiX3zsw=
88 3f83fc5cfe715d292069ee8417c83804f6c6c1e4 0 iQIVAwUAUztENyBXgaxoKi1yAQIpkhAAmJj5JRTSn0Dn/OTAHggalw8KYFbAck1X35Wg9O7ku7sd+cOnNnkYfqAdz2m5ikqWHP7aWMiNkNy7Ree2110NqkQVYG/2AJStXBdIOmewqnjDlNt+rbJQN/JsjeKSCy+ToNvhqX5cTM9DF2pwRjMsTXVff307S6/3pga244i+RFAeG3WCUrzfDu641MGFLjG4atCj8ZFLg9DcW5bsRiOs5ZK5Il+UAb2yyoS2KNQ70VLhYULhGtqq9tuO4nLRGN3DX/eDcYfncPCav1GckW4OZKakcbLtAdW0goSgGWloxcM+j2E6Z1JZ9tOTTkFN77EvX0ZWZLmYM7sUN1meFnKbVxrtGKlMelwKwlT252c65PAKa9zsTaRUKvN7XclyxZAYVCsiCQ/V08NXhNgXJXcoKUAeGNf6wruOyvRU9teia8fAiuHJoY58WC8jC4nYG3iZTnl+zNj2A5xuEUpYHhjUfe3rNJeK7CwUpJKlbxopu5mnW9AE9ITfI490eaapRLTojOBDJNqCORAtbggMD46fLeCOzzB8Gl70U2p5P34F92Sn6mgERFKh/10XwJcj4ZIeexbQK8lqQ2cIanDN9dAmbvavPTY8grbANuq+vXDGxjIjfxapqzsSPqUJ5KnfTQyLq5NWwquR9t38XvHZfktkd140BFKwIUAIlKKaFfYXXtM=
88 3f83fc5cfe715d292069ee8417c83804f6c6c1e4 0 iQIVAwUAUztENyBXgaxoKi1yAQIpkhAAmJj5JRTSn0Dn/OTAHggalw8KYFbAck1X35Wg9O7ku7sd+cOnNnkYfqAdz2m5ikqWHP7aWMiNkNy7Ree2110NqkQVYG/2AJStXBdIOmewqnjDlNt+rbJQN/JsjeKSCy+ToNvhqX5cTM9DF2pwRjMsTXVff307S6/3pga244i+RFAeG3WCUrzfDu641MGFLjG4atCj8ZFLg9DcW5bsRiOs5ZK5Il+UAb2yyoS2KNQ70VLhYULhGtqq9tuO4nLRGN3DX/eDcYfncPCav1GckW4OZKakcbLtAdW0goSgGWloxcM+j2E6Z1JZ9tOTTkFN77EvX0ZWZLmYM7sUN1meFnKbVxrtGKlMelwKwlT252c65PAKa9zsTaRUKvN7XclyxZAYVCsiCQ/V08NXhNgXJXcoKUAeGNf6wruOyvRU9teia8fAiuHJoY58WC8jC4nYG3iZTnl+zNj2A5xuEUpYHhjUfe3rNJeK7CwUpJKlbxopu5mnW9AE9ITfI490eaapRLTojOBDJNqCORAtbggMD46fLeCOzzB8Gl70U2p5P34F92Sn6mgERFKh/10XwJcj4ZIeexbQK8lqQ2cIanDN9dAmbvavPTY8grbANuq+vXDGxjIjfxapqzsSPqUJ5KnfTQyLq5NWwquR9t38XvHZfktkd140BFKwIUAIlKKaFfYXXtM=
89 564f55b251224f16508dd1311452db7780dafe2b 0 iQIVAwUAU1BmFSBXgaxoKi1yAQJ2Aw//bjK++xJuZCIdktg/i5FxBwoxdbipfTkKsN/YjUwrEmroYM8IkqIsO+U54OGCYWr3NPJ3VS8wUQeJ+NF3ffcjmjC297R9J+X0c5G90DdQUYX44jG/tP8Tqpev4Q7DLCXT26aRwEMdJQpq0eGaqv55E5Cxnyt3RrLCqe7RjPresZFg7iYrro5nq8TGYwBhessHXnCix9QI0HtXiLpms+0UGz8Sbi9nEYW+M0OZCyO1TvykCpFzEsLNwqqtFvhOMD/AMiWcTKNUpjmOn3V83xjWl+jnDUt7BxJ7n1efUnlwl4IeWlSUb73q/durtaymb97cSdKFmXHv4pdAShQEuEpVVGO1WELsKoXmbj30ItTW2V3KvNbjFsvIdDo7zLCpXyTq1HC56W7QCIMINX2qT+hrAMWC12tPQ05f89Cv1+jpk6eOPFqIHFdi663AjyrnGll8nwN7HJWwtA5wTXisu3bec51FAq4yJTzPMtOE9spz36E+Go2hZ1cAv9oCSceZcM0wB8KiMfaZJKNZNZk1jvsdiio4CcdASOFQPOspz07GqQxVP7W+F1Oz32LgwcNAEAS/f3juwDj45GYfAWJrTh3dnJy5DTD2LVC7KtkxxUVkWkqxivnDB9anj++FN9eyekxzut5eFED+WrCfZMcSPW0ai7wbslhKUhCwSf/v3DgGwsM=
89 564f55b251224f16508dd1311452db7780dafe2b 0 iQIVAwUAU1BmFSBXgaxoKi1yAQJ2Aw//bjK++xJuZCIdktg/i5FxBwoxdbipfTkKsN/YjUwrEmroYM8IkqIsO+U54OGCYWr3NPJ3VS8wUQeJ+NF3ffcjmjC297R9J+X0c5G90DdQUYX44jG/tP8Tqpev4Q7DLCXT26aRwEMdJQpq0eGaqv55E5Cxnyt3RrLCqe7RjPresZFg7iYrro5nq8TGYwBhessHXnCix9QI0HtXiLpms+0UGz8Sbi9nEYW+M0OZCyO1TvykCpFzEsLNwqqtFvhOMD/AMiWcTKNUpjmOn3V83xjWl+jnDUt7BxJ7n1efUnlwl4IeWlSUb73q/durtaymb97cSdKFmXHv4pdAShQEuEpVVGO1WELsKoXmbj30ItTW2V3KvNbjFsvIdDo7zLCpXyTq1HC56W7QCIMINX2qT+hrAMWC12tPQ05f89Cv1+jpk6eOPFqIHFdi663AjyrnGll8nwN7HJWwtA5wTXisu3bec51FAq4yJTzPMtOE9spz36E+Go2hZ1cAv9oCSceZcM0wB8KiMfaZJKNZNZk1jvsdiio4CcdASOFQPOspz07GqQxVP7W+F1Oz32LgwcNAEAS/f3juwDj45GYfAWJrTh3dnJy5DTD2LVC7KtkxxUVkWkqxivnDB9anj++FN9eyekxzut5eFED+WrCfZMcSPW0ai7wbslhKUhCwSf/v3DgGwsM=
90 2195ac506c6ababe86985b932f4948837c0891b5 0 iQIVAwUAU2LO/CBXgaxoKi1yAQI/3w/7BT/VRPyxey6tYp7i5cONIlEB3gznebGYwm0SGYNE6lsvS2VLh6ztb+j4eqOadr8Ssna6bslBx+dVsm+VuJ+vrNLMucD5Uc+fhn6dAfVqg+YBzUEaedI5yNsJizcJUDI7hUVsxiPiiYd9hchCWJ+z2tVt2jCyG2lMV2rbW36AM89sgz/wn5/AaAFsgoS6up/uzA3Tmw+qZSO6dZChb4Q8midIUWEbNzVhokgYcw7/HmjmvkvV9RJYiG8aBnMdQmxTE69q2dTjnnDL6wu61WU2FpTN09HRFbemUqzAfoJp8MmXq6jWgfLcm0cI3kRo7ZNpnEkmVKsfKQCXXiaR4alt9IQpQ6Jl7LSYsYI+D4ejpYysIsZyAE8qzltYhBKJWqO27A5V4WdJsoTgA/RwKfPRlci4PY8I4N466S7PBXVz/Cc5EpFkecvrgceTmBafb8JEi+gPiD2Po4vtW3bCeV4xldiEXHeJ77byUz7fZU7jL78SjJVOCCQTJfKZVr36kTz3KlaOz3E700RxzEFDYbK7I41mdANeQBmNNbcvRTy5ma6W6I3McEcAH4wqM5fFQ8YS+QWJxk85Si8KtaDPqoEdC/0dQPavuU/jAVjhV8IbmmkOtO7WvOHQDBtrR15yMxGMnUwMrPHaRNKdHNYRG0LL7lpCtdMi1mzLQgHYY9SRYvI=
90 2195ac506c6ababe86985b932f4948837c0891b5 0 iQIVAwUAU2LO/CBXgaxoKi1yAQI/3w/7BT/VRPyxey6tYp7i5cONIlEB3gznebGYwm0SGYNE6lsvS2VLh6ztb+j4eqOadr8Ssna6bslBx+dVsm+VuJ+vrNLMucD5Uc+fhn6dAfVqg+YBzUEaedI5yNsJizcJUDI7hUVsxiPiiYd9hchCWJ+z2tVt2jCyG2lMV2rbW36AM89sgz/wn5/AaAFsgoS6up/uzA3Tmw+qZSO6dZChb4Q8midIUWEbNzVhokgYcw7/HmjmvkvV9RJYiG8aBnMdQmxTE69q2dTjnnDL6wu61WU2FpTN09HRFbemUqzAfoJp8MmXq6jWgfLcm0cI3kRo7ZNpnEkmVKsfKQCXXiaR4alt9IQpQ6Jl7LSYsYI+D4ejpYysIsZyAE8qzltYhBKJWqO27A5V4WdJsoTgA/RwKfPRlci4PY8I4N466S7PBXVz/Cc5EpFkecvrgceTmBafb8JEi+gPiD2Po4vtW3bCeV4xldiEXHeJ77byUz7fZU7jL78SjJVOCCQTJfKZVr36kTz3KlaOz3E700RxzEFDYbK7I41mdANeQBmNNbcvRTy5ma6W6I3McEcAH4wqM5fFQ8YS+QWJxk85Si8KtaDPqoEdC/0dQPavuU/jAVjhV8IbmmkOtO7WvOHQDBtrR15yMxGMnUwMrPHaRNKdHNYRG0LL7lpCtdMi1mzLQgHYY9SRYvI=
91 269c80ee5b3cb3684fa8edc61501b3506d02eb10 0 iQIVAwUAU4uX5CBXgaxoKi1yAQLpdg/+OxulOKwZN+Nr7xsRhUijYjyAElRf2mGDvMrbAOA2xNf85DOXjOrX5TKETumf1qANA5cHa1twA8wYgxUzhx30H+w5EsLjyeSsOncRnD5WZNqSoIq2XevT0T4c8xdyNftyBqK4h/SC/t2h3vEiSCUaGcfNK8yk4XO45MIk4kk9nlA9jNWdA5ZMLgEFBye2ggz0JjEAPUkVDqlr9sNORDEbnwZxGPV8CK9HaL/I8VWClaFgjKQmjqV3SQsNFe2XPffzXmIipFJ+ODuXVxYpAsvLiGmcfuUfSDHQ4L9QvjBsWe1PgYMr/6CY/lPYmR+xW5mJUE9eIdN4MYcXgicLrmMpdF5pToNccNCMtfa6CDvEasPRqe2bDzL/Q9dQbdOVE/boaYBlgmYLL+/u+dpqip9KkyGgbSo9uJzst1mLTCzJmr5bw+surul28i9HM+4+Lewg4UUdHLz46no1lfTlB5o5EAhiOZBTEVdoBaKfewVpDa/aBRvtWX7UMVRG5qrtA0sXwydN00Jaqkr9m20W0jWjtc1ZC72QCrynVHOyfIb2rN98rnuy2QN4bTvjNpNjHOhhhPTOoVo0YYPdiUupm46vymUTQCmWsglU4Rlaa3vXneP7JenL5TV8WLPs9J28lF0IkOnyBXY7OFcpvYO1euu7iR1VdjfrQukMyaX18usymiA=
91 269c80ee5b3cb3684fa8edc61501b3506d02eb10 0 iQIVAwUAU4uX5CBXgaxoKi1yAQLpdg/+OxulOKwZN+Nr7xsRhUijYjyAElRf2mGDvMrbAOA2xNf85DOXjOrX5TKETumf1qANA5cHa1twA8wYgxUzhx30H+w5EsLjyeSsOncRnD5WZNqSoIq2XevT0T4c8xdyNftyBqK4h/SC/t2h3vEiSCUaGcfNK8yk4XO45MIk4kk9nlA9jNWdA5ZMLgEFBye2ggz0JjEAPUkVDqlr9sNORDEbnwZxGPV8CK9HaL/I8VWClaFgjKQmjqV3SQsNFe2XPffzXmIipFJ+ODuXVxYpAsvLiGmcfuUfSDHQ4L9QvjBsWe1PgYMr/6CY/lPYmR+xW5mJUE9eIdN4MYcXgicLrmMpdF5pToNccNCMtfa6CDvEasPRqe2bDzL/Q9dQbdOVE/boaYBlgmYLL+/u+dpqip9KkyGgbSo9uJzst1mLTCzJmr5bw+surul28i9HM+4+Lewg4UUdHLz46no1lfTlB5o5EAhiOZBTEVdoBaKfewVpDa/aBRvtWX7UMVRG5qrtA0sXwydN00Jaqkr9m20W0jWjtc1ZC72QCrynVHOyfIb2rN98rnuy2QN4bTvjNpNjHOhhhPTOoVo0YYPdiUupm46vymUTQCmWsglU4Rlaa3vXneP7JenL5TV8WLPs9J28lF0IkOnyBXY7OFcpvYO1euu7iR1VdjfrQukMyaX18usymiA=
92 2d8cd3d0e83c7336c0cb45a9f88638363f993848 0 iQIVAwUAU7OLTCBXgaxoKi1yAQJ+pw/+M3yOesgf55eo3PUTZw02QZxDyEg9ElrRc6664/QFXaJuYdz8H3LGG/NYs8uEdYihiGpS1Qc70jwd1IoUlrCELsaSSZpzWQ+VpQFX29aooBoetfL+8WgqV8zJHCtY0E1EBg/Z3ZL3n2OS++fVeWlKtp5mwEq8uLTUmhIS7GseP3bIG/CwF2Zz4bzhmPGK8V2s74aUvELZLCfkBE1ULNs7Nou1iPDGnhYOD53eq1KGIPlIg1rnLbyYw5bhS20wy5IxkWf2eCaXfmQBTG61kO5m3nkzfVgtxmZHLqYggISTJXUovfGsWZcp5a71clCSMVal+Mfviw8L/UPHG0Ie1c36djJiFLxM0f2HlwVMjegQOZSAeMGg1YL1xnIys2zMMsKgEeR+JISTal1pJyLcT9x5mr1HCnUczSGXE5zsixN+PORRnZOqcEZTa2mHJ1h5jJeEm36B/eR57BMJG+i0QgZqTpLzYTFrp2eWokGMjFB1MvgAkL2YoRsw9h6TeIwqzK8mFwLi28bf1c90gX9uMbwY/NOqGzfQKBR9bvCjs2k/gmJ+qd5AbC3DvOxHnN6hRZUqNq76Bo4F+CUVcjQ/NXnfnOIVNbILpl5Un5kl+8wLFM+mNxDxduajaUwLhSHZofKmmCSLbuuaGmQTC7a/4wzhQM9e5dX0X/8sOo8CptW7uw4=
92 2d8cd3d0e83c7336c0cb45a9f88638363f993848 0 iQIVAwUAU7OLTCBXgaxoKi1yAQJ+pw/+M3yOesgf55eo3PUTZw02QZxDyEg9ElrRc6664/QFXaJuYdz8H3LGG/NYs8uEdYihiGpS1Qc70jwd1IoUlrCELsaSSZpzWQ+VpQFX29aooBoetfL+8WgqV8zJHCtY0E1EBg/Z3ZL3n2OS++fVeWlKtp5mwEq8uLTUmhIS7GseP3bIG/CwF2Zz4bzhmPGK8V2s74aUvELZLCfkBE1ULNs7Nou1iPDGnhYOD53eq1KGIPlIg1rnLbyYw5bhS20wy5IxkWf2eCaXfmQBTG61kO5m3nkzfVgtxmZHLqYggISTJXUovfGsWZcp5a71clCSMVal+Mfviw8L/UPHG0Ie1c36djJiFLxM0f2HlwVMjegQOZSAeMGg1YL1xnIys2zMMsKgEeR+JISTal1pJyLcT9x5mr1HCnUczSGXE5zsixN+PORRnZOqcEZTa2mHJ1h5jJeEm36B/eR57BMJG+i0QgZqTpLzYTFrp2eWokGMjFB1MvgAkL2YoRsw9h6TeIwqzK8mFwLi28bf1c90gX9uMbwY/NOqGzfQKBR9bvCjs2k/gmJ+qd5AbC3DvOxHnN6hRZUqNq76Bo4F+CUVcjQ/NXnfnOIVNbILpl5Un5kl+8wLFM+mNxDxduajaUwLhSHZofKmmCSLbuuaGmQTC7a/4wzhQM9e5dX0X/8sOo8CptW7uw4=
93 6c36dc6cd61a0e1b563f1d51e55bdf4dacf12162 0 iQIVAwUAU8n97yBXgaxoKi1yAQKqcA/+MT0VFoP6N8fHnlxj85maoM2HfZbAzX7oEW1B8F1WH6rHESHDexDWIYWJ2XnEeTD4GCXN0/1p+O/I0IMPNzqoSz8BU0SR4+ejhRkGrKG7mcFiF5G8enxaiISn9nmax6DyRfqtOQBzuXYGObXg9PGvMS6zbR0SorJK61xX7fSsUNN6BAvHJfpwcVkOrrFAIpEhs/Gh9wg0oUKCffO/Abs6oS+P6nGLylpIyXqC7rKZ4uPVc6Ljh9DOcpV4NCU6kQbNE7Ty79E0/JWWLsHOEY4F4WBzI7rVh7dOkRMmfNGaqvKkuNkJOEqTR1o1o73Hhbxn4NU7IPbVP/zFKC+/4QVtcPk2IPlpK1MqA1H2hBNYZhJlNhvAa7LwkIxM0916/zQ8dbFAzp6Ay/t/L0tSEcIrudTz2KTrY0WKw+pkzB/nTwaS3XZre6H2B+gszskmf1Y41clkIy/nH9K7zBuzANWyK3+bm40vmMoBbbnsweUAKkyCwqm4KTyQoYQWzu/ZiZcI+Uuk/ajJ9s7EhJbIlSnYG9ttWL/IZ1h+qPU9mqVO9fcaqkeL/NIRh+IsnzaWo0zmHU1bK+/E29PPGGf3v6+IEJmXg7lvNl5pHiMd2tb7RNO/UaNSv1Y2E9naD4FQwSWo38GRBcnRGuKCLdZNHGUR+6dYo6BJCGG8wtZvNXb3TOo=
93 6c36dc6cd61a0e1b563f1d51e55bdf4dacf12162 0 iQIVAwUAU8n97yBXgaxoKi1yAQKqcA/+MT0VFoP6N8fHnlxj85maoM2HfZbAzX7oEW1B8F1WH6rHESHDexDWIYWJ2XnEeTD4GCXN0/1p+O/I0IMPNzqoSz8BU0SR4+ejhRkGrKG7mcFiF5G8enxaiISn9nmax6DyRfqtOQBzuXYGObXg9PGvMS6zbR0SorJK61xX7fSsUNN6BAvHJfpwcVkOrrFAIpEhs/Gh9wg0oUKCffO/Abs6oS+P6nGLylpIyXqC7rKZ4uPVc6Ljh9DOcpV4NCU6kQbNE7Ty79E0/JWWLsHOEY4F4WBzI7rVh7dOkRMmfNGaqvKkuNkJOEqTR1o1o73Hhbxn4NU7IPbVP/zFKC+/4QVtcPk2IPlpK1MqA1H2hBNYZhJlNhvAa7LwkIxM0916/zQ8dbFAzp6Ay/t/L0tSEcIrudTz2KTrY0WKw+pkzB/nTwaS3XZre6H2B+gszskmf1Y41clkIy/nH9K7zBuzANWyK3+bm40vmMoBbbnsweUAKkyCwqm4KTyQoYQWzu/ZiZcI+Uuk/ajJ9s7EhJbIlSnYG9ttWL/IZ1h+qPU9mqVO9fcaqkeL/NIRh+IsnzaWo0zmHU1bK+/E29PPGGf3v6+IEJmXg7lvNl5pHiMd2tb7RNO/UaNSv1Y2E9naD4FQwSWo38GRBcnRGuKCLdZNHGUR+6dYo6BJCGG8wtZvNXb3TOo=
94 3178e49892020336491cdc6945885c4de26ffa8b 0 iQIVAwUAU9whUCBXgaxoKi1yAQJDKxAAoGzdHXV/BvZ598VExEQ8IqkmBVIP1QZDVBr/orMc1eFM4tbGKxumMGbqgJsg+NetI0irkh/YWeJQ13lT4Og72iJ+4UC9eF9pcpUKr/0eBYdU2N/p2MIbVNWh3aF5QkbuQpSri0VbHOWkxqwoqrrwXEjgHaKYP4PKh+Dzukax4yzBUIyzAG38pt4a8hbjnozCl2uAikxk4Ojg+ZufhPoZWgFEuYzSfK5SrwVKOwuxKYFGbbVGTQMIXLvBhOipAmHp4JMEYHfG85kwuyx/DCDbGmXKPQYQfClwjJ4ob/IwG8asyMsPWs+09vrvpVO08HBuph3GjuiWJ1fhEef/ImWmZdQySI9Y4SjwP4dMVfzLCnY+PYPDM9Sq/5Iee13gI2lVM2NtAfQZPXh9l8u6SbCir1UhMNMx0qVMkqMAATmiZ+ETHCO75q4Wdcmnv5fk2PbvaGBVtrHGeiyuz5mK/j4cMbd0R9R0hR1PyC4dOhNqOnbqELNIe0rKNByG1RkpiQYsqZTU6insmnZrv4fVsxfA4JOObPfKNT4oa24MHS73ldLFCfQAuIxVE7RDJJ3bHeh/yO6Smo28FuVRldBl5e+wj2MykS8iVcuSa1smw6gJ14iLBH369nlR3fAAQxI0omVYPDHLr7SsH3vJasTaCD7V3SL4lW6vo/yaAh4ImlTAE+Y=
94 3178e49892020336491cdc6945885c4de26ffa8b 0 iQIVAwUAU9whUCBXgaxoKi1yAQJDKxAAoGzdHXV/BvZ598VExEQ8IqkmBVIP1QZDVBr/orMc1eFM4tbGKxumMGbqgJsg+NetI0irkh/YWeJQ13lT4Og72iJ+4UC9eF9pcpUKr/0eBYdU2N/p2MIbVNWh3aF5QkbuQpSri0VbHOWkxqwoqrrwXEjgHaKYP4PKh+Dzukax4yzBUIyzAG38pt4a8hbjnozCl2uAikxk4Ojg+ZufhPoZWgFEuYzSfK5SrwVKOwuxKYFGbbVGTQMIXLvBhOipAmHp4JMEYHfG85kwuyx/DCDbGmXKPQYQfClwjJ4ob/IwG8asyMsPWs+09vrvpVO08HBuph3GjuiWJ1fhEef/ImWmZdQySI9Y4SjwP4dMVfzLCnY+PYPDM9Sq/5Iee13gI2lVM2NtAfQZPXh9l8u6SbCir1UhMNMx0qVMkqMAATmiZ+ETHCO75q4Wdcmnv5fk2PbvaGBVtrHGeiyuz5mK/j4cMbd0R9R0hR1PyC4dOhNqOnbqELNIe0rKNByG1RkpiQYsqZTU6insmnZrv4fVsxfA4JOObPfKNT4oa24MHS73ldLFCfQAuIxVE7RDJJ3bHeh/yO6Smo28FuVRldBl5e+wj2MykS8iVcuSa1smw6gJ14iLBH369nlR3fAAQxI0omVYPDHLr7SsH3vJasTaCD7V3SL4lW6vo/yaAh4ImlTAE+Y=
95 5dc91146f35369949ea56b40172308158b59063a 0 iQIVAwUAVAUgJyBXgaxoKi1yAQJkEg/9EXFZvPpuvU7AjII1dlIT8F534AXrO30+H6hweg+h2mUCSb/mZnbo3Jr1tATgBWbIKkYmmsiIKNlJMFNPZTWhImGcVA93t6v85tSFiNJRI2QP9ypl5wTt2KhiS/s7GbUYCtPDm6xyNYoSvDo6vXJ5mfGlgFZY5gYLwEHq/lIRWLWD4EWYWbk5yN+B7rHu6A1n3yro73UR8DudEhYYqC23KbWEqFOiNd1IGj3UJlxIHUE4AcDukxbfiMWrKvv1kuT/vXak3X7cLXlO56aUbMopvaUflA3PSr3XAqynDd69cxACo/T36fuwzCQN4ICpdzGTos0rQALSr7CKF5YP9LMhVhCsOn0pCsAkSiw4HxxbcHQLl+t+0rchNysc4dWGwDt6GAfYcdm3fPtGFtA3qsN8lOpCquFH3TAZ3TrIjLFoTOk6s1xX1x5rjP/DAHc/y3KZU0Ffx3TwdQEEEIFaAXaxQG848rdfzV42+dnFnXh1G/MIrKAmv3ZSUkQ3XJfGc7iu82FsYE1NLHriUQDmMRBzCoQ1Rn1Kji119Cxf5rsMcQ6ZISR1f0jDCUS/qxlHvSqETLp8H63NSUfvuKSC7uC6pGvq9XQm1JRNO5UuJfK6tHzy0jv9bt2IRo2xbmvpDu9L5oHHd3JePsAmFmbrFf/7Qem3JyzEvRcpdcdHtefxcxc=
95 5dc91146f35369949ea56b40172308158b59063a 0 iQIVAwUAVAUgJyBXgaxoKi1yAQJkEg/9EXFZvPpuvU7AjII1dlIT8F534AXrO30+H6hweg+h2mUCSb/mZnbo3Jr1tATgBWbIKkYmmsiIKNlJMFNPZTWhImGcVA93t6v85tSFiNJRI2QP9ypl5wTt2KhiS/s7GbUYCtPDm6xyNYoSvDo6vXJ5mfGlgFZY5gYLwEHq/lIRWLWD4EWYWbk5yN+B7rHu6A1n3yro73UR8DudEhYYqC23KbWEqFOiNd1IGj3UJlxIHUE4AcDukxbfiMWrKvv1kuT/vXak3X7cLXlO56aUbMopvaUflA3PSr3XAqynDd69cxACo/T36fuwzCQN4ICpdzGTos0rQALSr7CKF5YP9LMhVhCsOn0pCsAkSiw4HxxbcHQLl+t+0rchNysc4dWGwDt6GAfYcdm3fPtGFtA3qsN8lOpCquFH3TAZ3TrIjLFoTOk6s1xX1x5rjP/DAHc/y3KZU0Ffx3TwdQEEEIFaAXaxQG848rdfzV42+dnFnXh1G/MIrKAmv3ZSUkQ3XJfGc7iu82FsYE1NLHriUQDmMRBzCoQ1Rn1Kji119Cxf5rsMcQ6ZISR1f0jDCUS/qxlHvSqETLp8H63NSUfvuKSC7uC6pGvq9XQm1JRNO5UuJfK6tHzy0jv9bt2IRo2xbmvpDu9L5oHHd3JePsAmFmbrFf/7Qem3JyzEvRcpdcdHtefxcxc=
96 f768c888aaa68d12dd7f509dcc7f01c9584357d0 0 iQIVAwUAVCxczSBXgaxoKi1yAQJYiA/9HnqKuU7IsGACgsUGt+YaqZQumg077Anj158kihSytmSts6xDxqVY1UQB38dqAKLJrQc7RbN0YK0NVCKZZrx/4OqgWvjiL5qWUJKqQzsDx4LGTUlbPlZNZawW2urmmYW6c9ZZDs1EVnVeZMDrOdntddtnBgtILDwrZ8o3U7FwSlfnm03vTkqUMj9okA3AsI8+lQIlo4qbqjQJYwvUC1ZezRdQwaT1LyoWUgjmhoZ1XWcWKOs9baikaJr6fMv8vZpwmaOY1+pztxYlROeSPVWt9P6yOf0Hi/2eg8AwSZLaX96xfk9IvXUSItg/wjTWP9BhnNs/ulwTnN8QOgSXpYxH4RXwsYOyU7BvwAekA9xi17wuzPrGEliScplxICIZ7jiiwv/VngMvM9AYw2mNBvZt2ZIGrrLaK6pq/zBm5tbviwqt5/8U5aqO8k1O0e4XYm5WmQ1c2AkXRO+xwvFpondlSF2y0flzf2FRXP82QMfsy7vxIP0KmaQ4ex+J8krZgMjNTwXh2M4tdYNtu5AehJQEP3l6giy2srkMDuFLqoe1yECjVlGdgA86ve3J/84I8KGgsufYMhfQnwHHGXCbONcNsDvO0QOee6CIQVcdKCG7dac3M89SC6Ns2CjuC8BIYDRnxbGQb7Fvn4ZcadyJKKbXQJzMgRV25K6BAwTIdvYAtgU=
96 f768c888aaa68d12dd7f509dcc7f01c9584357d0 0 iQIVAwUAVCxczSBXgaxoKi1yAQJYiA/9HnqKuU7IsGACgsUGt+YaqZQumg077Anj158kihSytmSts6xDxqVY1UQB38dqAKLJrQc7RbN0YK0NVCKZZrx/4OqgWvjiL5qWUJKqQzsDx4LGTUlbPlZNZawW2urmmYW6c9ZZDs1EVnVeZMDrOdntddtnBgtILDwrZ8o3U7FwSlfnm03vTkqUMj9okA3AsI8+lQIlo4qbqjQJYwvUC1ZezRdQwaT1LyoWUgjmhoZ1XWcWKOs9baikaJr6fMv8vZpwmaOY1+pztxYlROeSPVWt9P6yOf0Hi/2eg8AwSZLaX96xfk9IvXUSItg/wjTWP9BhnNs/ulwTnN8QOgSXpYxH4RXwsYOyU7BvwAekA9xi17wuzPrGEliScplxICIZ7jiiwv/VngMvM9AYw2mNBvZt2ZIGrrLaK6pq/zBm5tbviwqt5/8U5aqO8k1O0e4XYm5WmQ1c2AkXRO+xwvFpondlSF2y0flzf2FRXP82QMfsy7vxIP0KmaQ4ex+J8krZgMjNTwXh2M4tdYNtu5AehJQEP3l6giy2srkMDuFLqoe1yECjVlGdgA86ve3J/84I8KGgsufYMhfQnwHHGXCbONcNsDvO0QOee6CIQVcdKCG7dac3M89SC6Ns2CjuC8BIYDRnxbGQb7Fvn4ZcadyJKKbXQJzMgRV25K6BAwTIdvYAtgU=
97 7f8d16af8cae246fa5a48e723d48d58b015aed94 0 iQIVAwUAVEL0XyBXgaxoKi1yAQJLkRAAjZhpUju5nnSYtN9S0/vXS/tjuAtBTUdGwc0mz97VrM6Yhc6BjSCZL59tjeqQaoH7Lqf94pRAtZyIB2Vj/VVMDbM+/eaoSr1JixxppU+a4eqScaj82944u4C5YMSMC22PMvEwqKmy87RinZKJlFwSQ699zZ5g6mnNq8xeAiDlYhoF2QKzUXwnKxzpvjGsYhYGDMmVS1QPmky4WGvuTl6KeGkv8LidKf7r6/2RZeMcq+yjJ7R0RTtyjo1cM5dMcn/jRdwZxuV4cmFweCAeoy5guV+X6du022TpVndjOSDoKiRgdk7pTuaToXIy+9bleHpEo9bwKx58wvOMg7sirAYjrA4Xcx762RHiUuidTTPktm8sNsBQmgwJZ8Pzm+8TyHjFGLnBfeiDbQQEdLCXloz0jVOVRflDfMays1WpAYUV8XNOsgxnD2jDU8L0NLkJiX5Y0OerGq9AZ+XbgJFVBFhaOfsm2PEc3jq00GOLzrGzA+4b3CGpFzM3EyK9OnnwbP7SqCGb7PJgjmQ7IO8IWEmVYGaKtWONSm8zRLcKdH8xuk8iN1qCkBXMty/wfTEVTkIlMVEDbslYkVfj0rAPJ8B37bfe0Yz4CEMkCmARIB1rIOpMhnavXGuD50OP2PBBY/8DyC5aY97z9f04na/ffk+l7rWaHihjHufKIApt5OnfJ1w=
97 7f8d16af8cae246fa5a48e723d48d58b015aed94 0 iQIVAwUAVEL0XyBXgaxoKi1yAQJLkRAAjZhpUju5nnSYtN9S0/vXS/tjuAtBTUdGwc0mz97VrM6Yhc6BjSCZL59tjeqQaoH7Lqf94pRAtZyIB2Vj/VVMDbM+/eaoSr1JixxppU+a4eqScaj82944u4C5YMSMC22PMvEwqKmy87RinZKJlFwSQ699zZ5g6mnNq8xeAiDlYhoF2QKzUXwnKxzpvjGsYhYGDMmVS1QPmky4WGvuTl6KeGkv8LidKf7r6/2RZeMcq+yjJ7R0RTtyjo1cM5dMcn/jRdwZxuV4cmFweCAeoy5guV+X6du022TpVndjOSDoKiRgdk7pTuaToXIy+9bleHpEo9bwKx58wvOMg7sirAYjrA4Xcx762RHiUuidTTPktm8sNsBQmgwJZ8Pzm+8TyHjFGLnBfeiDbQQEdLCXloz0jVOVRflDfMays1WpAYUV8XNOsgxnD2jDU8L0NLkJiX5Y0OerGq9AZ+XbgJFVBFhaOfsm2PEc3jq00GOLzrGzA+4b3CGpFzM3EyK9OnnwbP7SqCGb7PJgjmQ7IO8IWEmVYGaKtWONSm8zRLcKdH8xuk8iN1qCkBXMty/wfTEVTkIlMVEDbslYkVfj0rAPJ8B37bfe0Yz4CEMkCmARIB1rIOpMhnavXGuD50OP2PBBY/8DyC5aY97z9f04na/ffk+l7rWaHihjHufKIApt5OnfJ1w=
98 ced632394371a36953ce4d394f86278ae51a2aae 0 iQIVAwUAVFWpfSBXgaxoKi1yAQLCQw//cvCi/Di3z/2ZEDQt4Ayyxv18gzewqrYyoElgnEzr5uTynD9Mf25hprstKla/Y5C6q+y0K6qCHPimGOkz3H+wZ2GVUgLKAwMABkfSb5IZiLTGaB2DjAJKZRwB6h43wG/DSFggE3dYszWuyHW88c72ZzVF5CSNc4J1ARLjDSgnNYJQ6XdPw3C9KgiLFDXzynPpZbPg0AK5bdPUKJruMeIKPn36Hx/Tv5GXUrbc2/lcnyRDFWisaDl0X/5eLdA+r3ID0cSmyPLYOeCgszRiW++KGw+PPDsWVeM3ZaZ9SgaBWU7MIn9A7yQMnnSzgDbN+9v/VMT3zbk1WJXlQQK8oA+CCdHH9EY33RfZ6ST/lr3pSQbUG1hdK6Sw+H6WMkOnnEk6HtLwa4xZ3HjDpoPkhVV+S0C7D5WWOovbubxuBiW5v8tK4sIOS6bAaKevTBKRbo4Rs6qmS/Ish5Q+z5bKst80cyEdi4QSoPZ/W+6kh1KfOprMxynwPQhtEcDYW2gfLpgPIM7RdXPKukLlkV2qX3eF/tqApGU4KNdP4I3N80Ri0h+6tVU/K4TMYzlRV3ziLBumJ4TnBrTHU3X6AfZUfTgslQzokX8/7a3tbctX6kZuJPggLGisdFSdirHbrUc+y5VKuJtPr+LxxgZKRFbs2VpJRem6FvwGNyndWLv32v0GMtQ=
98 ced632394371a36953ce4d394f86278ae51a2aae 0 iQIVAwUAVFWpfSBXgaxoKi1yAQLCQw//cvCi/Di3z/2ZEDQt4Ayyxv18gzewqrYyoElgnEzr5uTynD9Mf25hprstKla/Y5C6q+y0K6qCHPimGOkz3H+wZ2GVUgLKAwMABkfSb5IZiLTGaB2DjAJKZRwB6h43wG/DSFggE3dYszWuyHW88c72ZzVF5CSNc4J1ARLjDSgnNYJQ6XdPw3C9KgiLFDXzynPpZbPg0AK5bdPUKJruMeIKPn36Hx/Tv5GXUrbc2/lcnyRDFWisaDl0X/5eLdA+r3ID0cSmyPLYOeCgszRiW++KGw+PPDsWVeM3ZaZ9SgaBWU7MIn9A7yQMnnSzgDbN+9v/VMT3zbk1WJXlQQK8oA+CCdHH9EY33RfZ6ST/lr3pSQbUG1hdK6Sw+H6WMkOnnEk6HtLwa4xZ3HjDpoPkhVV+S0C7D5WWOovbubxuBiW5v8tK4sIOS6bAaKevTBKRbo4Rs6qmS/Ish5Q+z5bKst80cyEdi4QSoPZ/W+6kh1KfOprMxynwPQhtEcDYW2gfLpgPIM7RdXPKukLlkV2qX3eF/tqApGU4KNdP4I3N80Ri0h+6tVU/K4TMYzlRV3ziLBumJ4TnBrTHU3X6AfZUfTgslQzokX8/7a3tbctX6kZuJPggLGisdFSdirHbrUc+y5VKuJtPr+LxxgZKRFbs2VpJRem6FvwGNyndWLv32v0GMtQ=
99 643c58303fb0ec020907af28b9e486be299ba043 0 iQIVAwUAVGKawCBXgaxoKi1yAQL7zxAAjpXKNvzm/PKVlTfDjuVOYZ9H8w9QKUZ0vfrNJrN6Eo6hULIostbdRc25FcMWocegTqvKbz3IG+L2TKOIdZJS9M9QS4URybUd37URq4Jai8kMiJY31KixNNnjO2G1B39aIXUhY+EPx12aY31/OVy4laXIVtN6qpSncjo9baXSOMZmx6RyA1dbyfwXRjT/aODCGHZXgLJHS/kHlkCsThVlqYQ4rUCDkXIeMqIGF1CR0KjfmKpp1fS14OMgpLgdnt9+pnBZ+qcf1YdpOeQob1zwunjMYOyYC74FyOTdwaynU2iDsuBrmkE8kgEedIn7+WWe9fp/6TQJMVOeTQPZBNSRRSUYCw5Tg/0L/+jLtzjc2mY4444sDPbR7scrtU+/GtvlR5z0Y5pofwEdFME7PZNOp9a4kMiSa7ZERyGdN7U1pDu9JU6BZRz+nPzW217PVnTF7YFV/GGUzMTk9i7EZb5M4T9r9gfxFSMPeT5ct712CdBfyRlsSbSWk8XclTXwW385kLVYNDtOukWrvEiwxpA14Xb/ZUXbIDZVf5rP2HrZHMkghzeUYPjRn/IlgYUt7sDNmqFZNIc9mRFrZC9uFQ/Nul5InZodNODQDM+nHpxaztt4xl4qKep8SDEPAQjNr8biC6T9MtLKbWbSKDlqYYNv0pb2PuGub3y9rvkF1Y05mgM=
99 643c58303fb0ec020907af28b9e486be299ba043 0 iQIVAwUAVGKawCBXgaxoKi1yAQL7zxAAjpXKNvzm/PKVlTfDjuVOYZ9H8w9QKUZ0vfrNJrN6Eo6hULIostbdRc25FcMWocegTqvKbz3IG+L2TKOIdZJS9M9QS4URybUd37URq4Jai8kMiJY31KixNNnjO2G1B39aIXUhY+EPx12aY31/OVy4laXIVtN6qpSncjo9baXSOMZmx6RyA1dbyfwXRjT/aODCGHZXgLJHS/kHlkCsThVlqYQ4rUCDkXIeMqIGF1CR0KjfmKpp1fS14OMgpLgdnt9+pnBZ+qcf1YdpOeQob1zwunjMYOyYC74FyOTdwaynU2iDsuBrmkE8kgEedIn7+WWe9fp/6TQJMVOeTQPZBNSRRSUYCw5Tg/0L/+jLtzjc2mY4444sDPbR7scrtU+/GtvlR5z0Y5pofwEdFME7PZNOp9a4kMiSa7ZERyGdN7U1pDu9JU6BZRz+nPzW217PVnTF7YFV/GGUzMTk9i7EZb5M4T9r9gfxFSMPeT5ct712CdBfyRlsSbSWk8XclTXwW385kLVYNDtOukWrvEiwxpA14Xb/ZUXbIDZVf5rP2HrZHMkghzeUYPjRn/IlgYUt7sDNmqFZNIc9mRFrZC9uFQ/Nul5InZodNODQDM+nHpxaztt4xl4qKep8SDEPAQjNr8biC6T9MtLKbWbSKDlqYYNv0pb2PuGub3y9rvkF1Y05mgM=
100 902554884335e5ca3661d63be9978eb4aec3f68a 0 iQIVAwUAVH0KMyBXgaxoKi1yAQLUKxAAjgyYpmqD0Ji5OQ3995yX0dmwHOaaSuYpq71VUsOMYBskjH4xE2UgcTrX8RWUf0E+Ya91Nw3veTf+IZlYLaWuOYuJPRzw+zD1sVY8xprwqBOXNaA7n8SsTqZPSh6qgw4S0pUm0xJUOZzUP1l9S7BtIdJP7KwZ7hs9YZev4r9M3G15xOIPn5qJqBAtIeE6f5+ezoyOpSPZFtLFc4qKQ/YWzOT5uuSaYogXgVByXRFaO84+1TD93LR0PyVWxhwU9JrDU5d7P/bUTW1BXdjsxTbBnigWswKHC71EHpgz/HCYxivVL30qNdOm4Fow1Ec2GdUzGunSqTPrq18ScZDYW1x87f3JuqPM+ce/lxRWBBqP1yE30/8l/Us67m6enWXdGER8aL1lYTGOIWAhvJpfzv9KebaUq1gMFLo6j+OfwR3rYPiCHgi20nTNBa+LOceWFjCGzFa3T9UQWHW/MBElfAxK65uecbGRRYY9V1/+wxtTUiS6ixpmzL8S7uUd5n6oMaeeMiD82NLgPIbMyUHQv6eFEcCj0U9NT2uKbFRmclMs5V+8D+RTCsLJ55R9PD5OoRw/6K/coqqPShYmJvgYsFQPzXVpQdCRae31xdfGFmd5KUetqyrT+4GUdJWzSm0giSgovpEJNxXglrvNdvSO7fX3R1oahhwOwtGqMwNilcK+iDw=
100 902554884335e5ca3661d63be9978eb4aec3f68a 0 iQIVAwUAVH0KMyBXgaxoKi1yAQLUKxAAjgyYpmqD0Ji5OQ3995yX0dmwHOaaSuYpq71VUsOMYBskjH4xE2UgcTrX8RWUf0E+Ya91Nw3veTf+IZlYLaWuOYuJPRzw+zD1sVY8xprwqBOXNaA7n8SsTqZPSh6qgw4S0pUm0xJUOZzUP1l9S7BtIdJP7KwZ7hs9YZev4r9M3G15xOIPn5qJqBAtIeE6f5+ezoyOpSPZFtLFc4qKQ/YWzOT5uuSaYogXgVByXRFaO84+1TD93LR0PyVWxhwU9JrDU5d7P/bUTW1BXdjsxTbBnigWswKHC71EHpgz/HCYxivVL30qNdOm4Fow1Ec2GdUzGunSqTPrq18ScZDYW1x87f3JuqPM+ce/lxRWBBqP1yE30/8l/Us67m6enWXdGER8aL1lYTGOIWAhvJpfzv9KebaUq1gMFLo6j+OfwR3rYPiCHgi20nTNBa+LOceWFjCGzFa3T9UQWHW/MBElfAxK65uecbGRRYY9V1/+wxtTUiS6ixpmzL8S7uUd5n6oMaeeMiD82NLgPIbMyUHQv6eFEcCj0U9NT2uKbFRmclMs5V+8D+RTCsLJ55R9PD5OoRw/6K/coqqPShYmJvgYsFQPzXVpQdCRae31xdfGFmd5KUetqyrT+4GUdJWzSm0giSgovpEJNxXglrvNdvSO7fX3R1oahhwOwtGqMwNilcK+iDw=
101 6dad422ecc5adb63d9fa649eeb8e05a5f9bc4900 0 iQIVAwUAVJNALCBXgaxoKi1yAQKgmw/+OFbHHOMmN2zs2lI2Y0SoMALPNQBInMBq2E6RMCMbfcS9Cn75iD29DnvBwAYNWaWsYEGyheJ7JjGBiuNKPOrLaHkdjG+5ypbhAfNDyHDiteMsXfH7D1L+cTOAB8yvhimZHOTTVF0zb/uRyVIPNowAyervUVRjDptzdfcvjUS+X+/Ufgwms6Y4CcuzFLFCxpmryJhLtOpwUPLlzIqeNkFOYWkHanCgtZX03PNIWhorH3AWOc9yztwWPQ+kcKl3FMlyuNMPhS/ElxSF6GHGtreRbtP+ZLoSIOMb2QBKpGDpZLgJ3JQEHDcZ0h5CLZWL9dDUJR3M8pg1qglqMFSWMgRPTzxPS4QntPgT/Ewd3+U5oCZUh052fG41OeCZ0CnVCpqi5PjUIDhzQkONxRCN2zbjQ2GZY7glbXoqytissihEIVP9m7RmBVq1rbjOKr+yUetJ9gOZcsMtZiCEq4Uj2cbA1x32MQv7rxwAgQP1kgQ62b0sN08HTjQpI7/IkNALLIDHoQWWr45H97i34qK1dd5uCOnYk7juvhGNX5XispxNnC01/CUVNnqChfDHpgnDjgT+1H618LiTgUAD3zo4IVAhCqF5XWsS4pQEENOB3Msffi62fYowvJx7f/htWeRLZ2OA+B85hhDiD4QBdHCRoz3spVp0asNqDxX4f4ndj8RlzfM=
101 6dad422ecc5adb63d9fa649eeb8e05a5f9bc4900 0 iQIVAwUAVJNALCBXgaxoKi1yAQKgmw/+OFbHHOMmN2zs2lI2Y0SoMALPNQBInMBq2E6RMCMbfcS9Cn75iD29DnvBwAYNWaWsYEGyheJ7JjGBiuNKPOrLaHkdjG+5ypbhAfNDyHDiteMsXfH7D1L+cTOAB8yvhimZHOTTVF0zb/uRyVIPNowAyervUVRjDptzdfcvjUS+X+/Ufgwms6Y4CcuzFLFCxpmryJhLtOpwUPLlzIqeNkFOYWkHanCgtZX03PNIWhorH3AWOc9yztwWPQ+kcKl3FMlyuNMPhS/ElxSF6GHGtreRbtP+ZLoSIOMb2QBKpGDpZLgJ3JQEHDcZ0h5CLZWL9dDUJR3M8pg1qglqMFSWMgRPTzxPS4QntPgT/Ewd3+U5oCZUh052fG41OeCZ0CnVCpqi5PjUIDhzQkONxRCN2zbjQ2GZY7glbXoqytissihEIVP9m7RmBVq1rbjOKr+yUetJ9gOZcsMtZiCEq4Uj2cbA1x32MQv7rxwAgQP1kgQ62b0sN08HTjQpI7/IkNALLIDHoQWWr45H97i34qK1dd5uCOnYk7juvhGNX5XispxNnC01/CUVNnqChfDHpgnDjgT+1H618LiTgUAD3zo4IVAhCqF5XWsS4pQEENOB3Msffi62fYowvJx7f/htWeRLZ2OA+B85hhDiD4QBdHCRoz3spVp0asNqDxX4f4ndj8RlzfM=
102 1265a3a71d75396f5d4cf6935ae7d9ba5407a547 0 iQIVAwUAVKXKYCBXgaxoKi1yAQIfsA/+PFfaWuZ6Jna12Y3MpKMnBCXYLWEJgMNlWHWzwU8lD26SKSlvMyHQsVZlkld2JmFugUCn1OV3OA4YWT6BA7VALq6Zsdcu5Dc8LRbyajBUkzGRpOUyWuFzjkCpGVbrQzbCR/bel/BBXzSqL4ipdtWgJ4y+WpZIhWkNXclBkR52b5hUTjN9vzhyhVVI7eURGwIEf7vVs1fDOcEGtaGY/ynzMTzyxIDsEEygCZau86wpKlYlqhCgxKDyzyGfpH3B1UlNGFt1afW8AWe1eHjdqC7TJZpMqmQ/Ju8vco8Xht6OXw4ZLHj7y39lpccfKTBLiK/cAKSg+xgyaH/BLhzoEkNAwYSFAB4i4IoV0KUC8nFxHfsoswBxJnMqU751ziMrpZ/XHZ1xQoEOdXgz2I04vlRn8xtynOVhcgjoAXwtbia7oNh/qCH/hl5/CdAtaawuCxJBf237F+cwur4PMAAvsGefRfZco/DInpr3qegr8rwInTxlO48ZG+o5xA4TPwT0QQTUjMdNfC146ZSbp65wG7VxJDocMZ8KJN/lqPaOvX+FVYWq4YnJhlldiV9DGgmym1AAaP0D3te2GcfHXpt/f6NYUPpgiBHy0GnOlNcQyGnnONg1A6oKVWB3k7WP28+PQbQEiCIFk2nkf5VZmye7OdHRGKOFfuprYFP1WwTWnVoNX9c=
102 1265a3a71d75396f5d4cf6935ae7d9ba5407a547 0 iQIVAwUAVKXKYCBXgaxoKi1yAQIfsA/+PFfaWuZ6Jna12Y3MpKMnBCXYLWEJgMNlWHWzwU8lD26SKSlvMyHQsVZlkld2JmFugUCn1OV3OA4YWT6BA7VALq6Zsdcu5Dc8LRbyajBUkzGRpOUyWuFzjkCpGVbrQzbCR/bel/BBXzSqL4ipdtWgJ4y+WpZIhWkNXclBkR52b5hUTjN9vzhyhVVI7eURGwIEf7vVs1fDOcEGtaGY/ynzMTzyxIDsEEygCZau86wpKlYlqhCgxKDyzyGfpH3B1UlNGFt1afW8AWe1eHjdqC7TJZpMqmQ/Ju8vco8Xht6OXw4ZLHj7y39lpccfKTBLiK/cAKSg+xgyaH/BLhzoEkNAwYSFAB4i4IoV0KUC8nFxHfsoswBxJnMqU751ziMrpZ/XHZ1xQoEOdXgz2I04vlRn8xtynOVhcgjoAXwtbia7oNh/qCH/hl5/CdAtaawuCxJBf237F+cwur4PMAAvsGefRfZco/DInpr3qegr8rwInTxlO48ZG+o5xA4TPwT0QQTUjMdNfC146ZSbp65wG7VxJDocMZ8KJN/lqPaOvX+FVYWq4YnJhlldiV9DGgmym1AAaP0D3te2GcfHXpt/f6NYUPpgiBHy0GnOlNcQyGnnONg1A6oKVWB3k7WP28+PQbQEiCIFk2nkf5VZmye7OdHRGKOFfuprYFP1WwTWnVoNX9c=
103 db8e3f7948b1fdeb9ad12d448fc3525759908b9f 0 iQIVAwUAVLsaciBXgaxoKi1yAQKMIA//a90/GvySL9UID+iYvzV2oDaAPDD0T+4Xs43I7DT5NIoDz+3yq2VV54XevQe5lYiURmsb/Q9nX2VR/Qq1J9c/R6Gy+CIfmJ3HzMZ0aAX8ZlZgQPYZKh/2kY5Ojl++k6MTqbqcrICNs4+UE/4IAxPyOfu5gy7TpdJmRZo2J3lWVC2Jbhd02Mzb+tjtfbOM+QcQxPwt9PpqmQszJceyVYOSm3jvD1uJdSOC04tBQrQwrxktQ09Om0LUMMaB5zFXpJtqUzfw7l4U4AaddEmkd3vUfLtHxc21RB01c3cpe2dJnjifDfwseLsI8rS4jmi/91c74TeBatSOhvbqzEkm/p8xZFXE4Uh+EpWjTsVqmfQaRq6NfNCR7I/kvGv8Ps6w8mg8uX8fd8lx+GJbodj+Uy0X3oqHyqPMky/df5i79zADBDuz+yuxFfDD9i22DJPIYcilfGgwpIUuO2lER5nSMVmReuWTVBnT6SEN66Q4KR8zLtIRr+t1qUUCy6wYbgwrdHVCbgMF8RPOVZPjbs17RIqcHjch0Xc7bShKGhQg4WHDjXHK61w4tOa1Yp7jT6COkl01XC9BLcGxJYKFvNCbeDZQGvVgJNoEvHxBxD9rGMVRjfuxeJawc2fGzZJn0ySyLDW0pfd4EJNgTh9bLdPjWz2VlXqn4A6bgaLgTPqjmN0VBXw=
103 db8e3f7948b1fdeb9ad12d448fc3525759908b9f 0 iQIVAwUAVLsaciBXgaxoKi1yAQKMIA//a90/GvySL9UID+iYvzV2oDaAPDD0T+4Xs43I7DT5NIoDz+3yq2VV54XevQe5lYiURmsb/Q9nX2VR/Qq1J9c/R6Gy+CIfmJ3HzMZ0aAX8ZlZgQPYZKh/2kY5Ojl++k6MTqbqcrICNs4+UE/4IAxPyOfu5gy7TpdJmRZo2J3lWVC2Jbhd02Mzb+tjtfbOM+QcQxPwt9PpqmQszJceyVYOSm3jvD1uJdSOC04tBQrQwrxktQ09Om0LUMMaB5zFXpJtqUzfw7l4U4AaddEmkd3vUfLtHxc21RB01c3cpe2dJnjifDfwseLsI8rS4jmi/91c74TeBatSOhvbqzEkm/p8xZFXE4Uh+EpWjTsVqmfQaRq6NfNCR7I/kvGv8Ps6w8mg8uX8fd8lx+GJbodj+Uy0X3oqHyqPMky/df5i79zADBDuz+yuxFfDD9i22DJPIYcilfGgwpIUuO2lER5nSMVmReuWTVBnT6SEN66Q4KR8zLtIRr+t1qUUCy6wYbgwrdHVCbgMF8RPOVZPjbs17RIqcHjch0Xc7bShKGhQg4WHDjXHK61w4tOa1Yp7jT6COkl01XC9BLcGxJYKFvNCbeDZQGvVgJNoEvHxBxD9rGMVRjfuxeJawc2fGzZJn0ySyLDW0pfd4EJNgTh9bLdPjWz2VlXqn4A6bgaLgTPqjmN0VBXw=
104 fbdd5195528fae4f41feebc1838215c110b25d6a 0 iQIVAwUAVM7fBCBXgaxoKi1yAQKoYw/+LeIGcjQmHIVFQULsiBtPDf+eGAADQoP3mKBy+eX/3Fa0qqUNfES2Q3Y6RRApyZ1maPRMt8BvvhZMgQsu9QIrmf3zsFxZGFwoyrIj4hM3xvAbEZXqmWiR85/Ywd4ImeLaZ0c7mkO1/HGF1n2Mv47bfM4hhNe7VGJSSrTY4srFHDfk4IG9f18DukJVzRD9/dZeBw6eUN1ukuLEgQAD5Sl47bUdKSetglOSR1PjXfZ1hjtz5ywUyBc5P9p3LC4wSvlcJKl22zEvB3L0hkoDcPsdIPEnJAeXxKlR1rQpoA3fEgrstGiSNUW/9Tj0VekAHLO95SExmQyoG/AhbjRRzIj4uQ0aevCJyiAhkv+ffOSf99PMW9L1k3tVjLhpMWEz9BOAWyX7cDFWj5t/iktI046O9HGN9SGVx18e9xM6pEgRcLA2TyjEmtkA4jX0JeN7WeCweMLiSxyGP7pSPSJdpJeXaFtRpSF62p/G0Z5wN9s05LHqDyqNVtCvg4WjkuV5LZSdLbMcYBWGBxQzCG6qowXFXIawmbaFiBZwTfOgNls9ndz5RGupAaxY317prxPFv/pXoesc1P8bdK09ZvjhbmmD66Q/BmS2dOMQ8rXRjuVdlR8j2QBtFZxekMcRD02nBAVnwHg1VWQMIRaGjdgmW4wOkirWVn7me177FnBxrxW1tG4=
104 fbdd5195528fae4f41feebc1838215c110b25d6a 0 iQIVAwUAVM7fBCBXgaxoKi1yAQKoYw/+LeIGcjQmHIVFQULsiBtPDf+eGAADQoP3mKBy+eX/3Fa0qqUNfES2Q3Y6RRApyZ1maPRMt8BvvhZMgQsu9QIrmf3zsFxZGFwoyrIj4hM3xvAbEZXqmWiR85/Ywd4ImeLaZ0c7mkO1/HGF1n2Mv47bfM4hhNe7VGJSSrTY4srFHDfk4IG9f18DukJVzRD9/dZeBw6eUN1ukuLEgQAD5Sl47bUdKSetglOSR1PjXfZ1hjtz5ywUyBc5P9p3LC4wSvlcJKl22zEvB3L0hkoDcPsdIPEnJAeXxKlR1rQpoA3fEgrstGiSNUW/9Tj0VekAHLO95SExmQyoG/AhbjRRzIj4uQ0aevCJyiAhkv+ffOSf99PMW9L1k3tVjLhpMWEz9BOAWyX7cDFWj5t/iktI046O9HGN9SGVx18e9xM6pEgRcLA2TyjEmtkA4jX0JeN7WeCweMLiSxyGP7pSPSJdpJeXaFtRpSF62p/G0Z5wN9s05LHqDyqNVtCvg4WjkuV5LZSdLbMcYBWGBxQzCG6qowXFXIawmbaFiBZwTfOgNls9ndz5RGupAaxY317prxPFv/pXoesc1P8bdK09ZvjhbmmD66Q/BmS2dOMQ8rXRjuVdlR8j2QBtFZxekMcRD02nBAVnwHg1VWQMIRaGjdgmW4wOkirWVn7me177FnBxrxW1tG4=
105 5b4ed033390bf6e2879c8f5c28c84e1ee3b87231 0 iQIVAwUAVPQL9CBXgaxoKi1yAQJIXxAAtD2hWhaKa+lABmCOYG92FE/WdqY/91Xv5atTL8Xeko/MkirIKZiOuxNWX+J34TVevINZSWmMfDSc5TkGxktL9jW/pDB/CXn+CVZpxRabPYFH9HM2K3g8VaTV1MFtV2+feOMDIPCmq5ogMF9/kXjmifiEBrJcFsE82fdexJ3OHoOY4iHFxEhh3GzvNqEQygk4VeU6VYziNvSQj9G//PsK3Bmk7zm5ScsZcMVML3SIYFuej1b1PI1v0N8mmCRooVNBGhD/eA0iLtdh/hSb9s/8UgJ4f9HOcx9zqs8V4i14lpd/fo0+yvFuVrVbWGzrDrk5EKLENhVPwvc1KA32PTQ4Z9u7VQIBIxq3K5lL2VlCMIYc1BSaSQBjuiLm8VdN6iDuf5poNZhk1rvtpQgpxJzh362dlGtR/iTJuLCeW7gCqWUAorLTeHy0bLQ/jSOeTAGys8bUHtlRL4QbnhLbUmJmRYVvCJ+Yt1aTgTSNcoFjoLJarR1169BXgdCA38BgReUL6kB224UJSTzB1hJUyB2LvCWrXZMipZmR99Iwdq7MePD3+AoSIXQNUMY9blxuuF5x7W2ikNXmVWuab4Z8rQRtmGqEuIMBSunxAnZSn+i8057dFKlq+/yGy+WW3RQg+RnLnwZs1zCDTfu98/GT5k5hFpjXZeUWWiOVwQJ5HrqncCw=
105 5b4ed033390bf6e2879c8f5c28c84e1ee3b87231 0 iQIVAwUAVPQL9CBXgaxoKi1yAQJIXxAAtD2hWhaKa+lABmCOYG92FE/WdqY/91Xv5atTL8Xeko/MkirIKZiOuxNWX+J34TVevINZSWmMfDSc5TkGxktL9jW/pDB/CXn+CVZpxRabPYFH9HM2K3g8VaTV1MFtV2+feOMDIPCmq5ogMF9/kXjmifiEBrJcFsE82fdexJ3OHoOY4iHFxEhh3GzvNqEQygk4VeU6VYziNvSQj9G//PsK3Bmk7zm5ScsZcMVML3SIYFuej1b1PI1v0N8mmCRooVNBGhD/eA0iLtdh/hSb9s/8UgJ4f9HOcx9zqs8V4i14lpd/fo0+yvFuVrVbWGzrDrk5EKLENhVPwvc1KA32PTQ4Z9u7VQIBIxq3K5lL2VlCMIYc1BSaSQBjuiLm8VdN6iDuf5poNZhk1rvtpQgpxJzh362dlGtR/iTJuLCeW7gCqWUAorLTeHy0bLQ/jSOeTAGys8bUHtlRL4QbnhLbUmJmRYVvCJ+Yt1aTgTSNcoFjoLJarR1169BXgdCA38BgReUL6kB224UJSTzB1hJUyB2LvCWrXZMipZmR99Iwdq7MePD3+AoSIXQNUMY9blxuuF5x7W2ikNXmVWuab4Z8rQRtmGqEuIMBSunxAnZSn+i8057dFKlq+/yGy+WW3RQg+RnLnwZs1zCDTfu98/GT5k5hFpjXZeUWWiOVwQJ5HrqncCw=
106 07a92bbd02e5e3a625e0820389b47786b02b2cea 0 iQIVAwUAVPSP9SBXgaxoKi1yAQLkBQ//dRQExJHFepJfZ0gvGnUoYI4APsLmne5XtfeXJ8OtUyC4a6RylxA5BavDWgXwUh9BGhOX2cBSz1fyvzohrPrvNnlBrYKAvOIJGEAiBTXHYTxHINEKPtDF92Uz23T0Rn/wnSvvlbWF7Pvd+0DMJpFDEyr9n6jvVLR7mgxMaCqZbVaB1W/wTwDjni780WgVx8OPUXkLx3/DyarMcIiPeI5UN+FeHDovTsBWFC95msFLm80PMRPuHOejWp65yyEemGujZEPO2D5VVah7fshM2HTz63+bkEBYoqrftuv3vXKBRG78MIrUrKpqxmnCKNKDUUWJ4yk3+NwuOiHlKdly5kZ7MNFaL73XKo8HH287lDWz0lIazs91dQA9a9JOyTsp8YqGtIJGGCbhrUDtiQJ199oBU84mw3VH/EEzm4mPv4sW5fm7BnnoH/a+9vXySc+498rkdLlzFwxrQkWyJ/pFOx4UA3mCtGQK+OSwLPc+X4SRqA4fiyqKxVAL1kpLTSDL3QA82I7GzBaXsxUXzS4nmteMhUyzTdwAhKVydL0gC3d7NmkAFSyRjdGzutUUXshYxg0ywRgYebe8uzJcTj4nNRgaalYLdg3guuDulD+dJmILsrcLmA6KD/pvfDn8PYt+4ZjNIvN2E9GF6uXDu4Ux+AlOTLk9BChxUF8uBX9ev5cvWtQ=
106 07a92bbd02e5e3a625e0820389b47786b02b2cea 0 iQIVAwUAVPSP9SBXgaxoKi1yAQLkBQ//dRQExJHFepJfZ0gvGnUoYI4APsLmne5XtfeXJ8OtUyC4a6RylxA5BavDWgXwUh9BGhOX2cBSz1fyvzohrPrvNnlBrYKAvOIJGEAiBTXHYTxHINEKPtDF92Uz23T0Rn/wnSvvlbWF7Pvd+0DMJpFDEyr9n6jvVLR7mgxMaCqZbVaB1W/wTwDjni780WgVx8OPUXkLx3/DyarMcIiPeI5UN+FeHDovTsBWFC95msFLm80PMRPuHOejWp65yyEemGujZEPO2D5VVah7fshM2HTz63+bkEBYoqrftuv3vXKBRG78MIrUrKpqxmnCKNKDUUWJ4yk3+NwuOiHlKdly5kZ7MNFaL73XKo8HH287lDWz0lIazs91dQA9a9JOyTsp8YqGtIJGGCbhrUDtiQJ199oBU84mw3VH/EEzm4mPv4sW5fm7BnnoH/a+9vXySc+498rkdLlzFwxrQkWyJ/pFOx4UA3mCtGQK+OSwLPc+X4SRqA4fiyqKxVAL1kpLTSDL3QA82I7GzBaXsxUXzS4nmteMhUyzTdwAhKVydL0gC3d7NmkAFSyRjdGzutUUXshYxg0ywRgYebe8uzJcTj4nNRgaalYLdg3guuDulD+dJmILsrcLmA6KD/pvfDn8PYt+4ZjNIvN2E9GF6uXDu4Ux+AlOTLk9BChxUF8uBX9ev5cvWtQ=
107 2e2e9a0750f91a6fe0ad88e4de34f8efefdcab08 0 iQIVAwUAVRw4nyBXgaxoKi1yAQIFExAAkbCPtLjQlJvPaYCL1KhNR+ZVAmn7JrFH3XhvR26RayYbs4NxR3W1BhwhDy9+W+28szEx1kQvmr6t1bXAFywY0tNJOeuLU7uFfmbgAfYgkQ9kpsQNqFYkjbCyftw0S9vX9VOJ9DqUoDWuKfX7VzjkwE9dCfKI5F+dvzxnd6ZFjB85nyHBQuTZlzXl0+csY212RJ2G2j/mzEBVyeZj9l7Rm+1X8AC1xQMWRJGiyd0b7nhYqoOcceeJFAV1t9QO4+gjmkM5kL0orjxTnuVsxPTxcC5ca1BfidPWrZEto3duHWNiATGnCDylxxr52BxCAS+BWePW9J0PROtw1pYaZ9pF4N5X5LSXJzqX7ZiNGckxqIjry09+Tbsa8FS0VkkYBEiGotpuo4Jd05V6qpXfW2JqAfEVo6X6aGvPM2B7ZUtKi30I4J+WprrOP3WgZ/ZWHe1ERYKgjDqisn3t/D40q30WQUeQGltGsOX0Udqma2RjBugO5BHGzJ2yer4GdJXg7q1OMzrjAEuz1IoKvIB/o1pg86quVA4H2gQnL1B8t1M38/DIafyw7mrEY4Z3GL44Reev63XVvDE099Vbhqp7ufwq81Fpq7Xxa5vsr9SJ+8IqqQr8AcYSuK3G3L6BmIuSUAYMRqgl35FWoWkGyZIG5c6K6zI8w5Pb0aGi6Lb2Wfb9zbc=
107 2e2e9a0750f91a6fe0ad88e4de34f8efefdcab08 0 iQIVAwUAVRw4nyBXgaxoKi1yAQIFExAAkbCPtLjQlJvPaYCL1KhNR+ZVAmn7JrFH3XhvR26RayYbs4NxR3W1BhwhDy9+W+28szEx1kQvmr6t1bXAFywY0tNJOeuLU7uFfmbgAfYgkQ9kpsQNqFYkjbCyftw0S9vX9VOJ9DqUoDWuKfX7VzjkwE9dCfKI5F+dvzxnd6ZFjB85nyHBQuTZlzXl0+csY212RJ2G2j/mzEBVyeZj9l7Rm+1X8AC1xQMWRJGiyd0b7nhYqoOcceeJFAV1t9QO4+gjmkM5kL0orjxTnuVsxPTxcC5ca1BfidPWrZEto3duHWNiATGnCDylxxr52BxCAS+BWePW9J0PROtw1pYaZ9pF4N5X5LSXJzqX7ZiNGckxqIjry09+Tbsa8FS0VkkYBEiGotpuo4Jd05V6qpXfW2JqAfEVo6X6aGvPM2B7ZUtKi30I4J+WprrOP3WgZ/ZWHe1ERYKgjDqisn3t/D40q30WQUeQGltGsOX0Udqma2RjBugO5BHGzJ2yer4GdJXg7q1OMzrjAEuz1IoKvIB/o1pg86quVA4H2gQnL1B8t1M38/DIafyw7mrEY4Z3GL44Reev63XVvDE099Vbhqp7ufwq81Fpq7Xxa5vsr9SJ+8IqqQr8AcYSuK3G3L6BmIuSUAYMRqgl35FWoWkGyZIG5c6K6zI8w5Pb0aGi6Lb2Wfb9zbc=
108 e89f909edffad558b56f4affa8239e4832f88de0 0 iQIVAwUAVTBozCBXgaxoKi1yAQLHeg/+IvfpPmG7OSqCoHvMVETYdrqT7lKCwfCQWMFOC/2faWs1n4R/qQNm6ckE5OY888RK8tVQ7ue03Pg/iyWgQlYfS7Njd3WPjS4JsnEBxIvuGkIu6TPIXAUAH0PFTBh0cZEICDpPEVT2X3bPRwDHA+hUE9RrxM5zJ39Fpk/pTYCjQ9UKfEhXlEfka75YB39g2Y/ssaSbn5w/tAAx8sL72Y4G96D4IV2seLHZhB3VQ7UZKThEWn6UdVOoKj+urIwGaBYMeekGVtHSh6fnHOw3EtDO9mQ5HtAz2Bl4CwRYN8eSN+Dwgr+mdk8MWpQQJ+i1A8jUhUp8gn1Pe5GkIH4CWZ9+AvLLnshe2MkVaTT1g7EQk37tFkkdZDRBsOHIvpF71B9pEA1gMUlX4gKgh5YwukgpQlDmFCfY7XmX6eXw9Ub+EckEwYuGMz7Fbwe9J/Ce4DxvgJgq3/cu/jb3bmbewH6tZmcrlqziqqA8GySIwcURnF1c37e7+e7x1jhFJfCWpHzvCusjKhUp9tZsl9Rt1Bo/y41QY+avY7//ymhbwTMKgqjzCYoA+ipF4JfZlFiZF+JhvOSIFb0ltkfdqKD+qOjlkFaglvQU1bpGKLJ6cz4Xk2Jqt5zhcrpyDMGVv9aiWywCK2ZP34RNaJ6ZFwzwdpXihqgkm5dBGoZ4ztFUfmjXzIg=
108 e89f909edffad558b56f4affa8239e4832f88de0 0 iQIVAwUAVTBozCBXgaxoKi1yAQLHeg/+IvfpPmG7OSqCoHvMVETYdrqT7lKCwfCQWMFOC/2faWs1n4R/qQNm6ckE5OY888RK8tVQ7ue03Pg/iyWgQlYfS7Njd3WPjS4JsnEBxIvuGkIu6TPIXAUAH0PFTBh0cZEICDpPEVT2X3bPRwDHA+hUE9RrxM5zJ39Fpk/pTYCjQ9UKfEhXlEfka75YB39g2Y/ssaSbn5w/tAAx8sL72Y4G96D4IV2seLHZhB3VQ7UZKThEWn6UdVOoKj+urIwGaBYMeekGVtHSh6fnHOw3EtDO9mQ5HtAz2Bl4CwRYN8eSN+Dwgr+mdk8MWpQQJ+i1A8jUhUp8gn1Pe5GkIH4CWZ9+AvLLnshe2MkVaTT1g7EQk37tFkkdZDRBsOHIvpF71B9pEA1gMUlX4gKgh5YwukgpQlDmFCfY7XmX6eXw9Ub+EckEwYuGMz7Fbwe9J/Ce4DxvgJgq3/cu/jb3bmbewH6tZmcrlqziqqA8GySIwcURnF1c37e7+e7x1jhFJfCWpHzvCusjKhUp9tZsl9Rt1Bo/y41QY+avY7//ymhbwTMKgqjzCYoA+ipF4JfZlFiZF+JhvOSIFb0ltkfdqKD+qOjlkFaglvQU1bpGKLJ6cz4Xk2Jqt5zhcrpyDMGVv9aiWywCK2ZP34RNaJ6ZFwzwdpXihqgkm5dBGoZ4ztFUfmjXzIg=
109 8cc6036bca532e06681c5a8fa37efaa812de67b5 0 iQIVAwUAVUP0xCBXgaxoKi1yAQLIChAAme3kg1Z0V8t5PnWKDoIvscIeAsD2s6EhMy1SofmdZ4wvYD1VmGC6TgXMCY7ssvRBhxqwG3GxwYpwELASuw2GYfVot2scN7+b8Hs5jHtkQevKbxarYni+ZI9mw/KldnJixD1yW3j+LoJFh/Fu6GD2yrfGIhimFLozcwUu3EbLk7JzyHSn7/8NFjLJz0foAYfcbowU9/BFwNVLrQPnsUbWcEifsq5bYso9MBO9k+25yLgqHoqMbGpJcgjubNy1cWoKnlKS+lOJl0/waAk+aIjHXMzFpRRuJDjxEZn7V4VdV5d23nrBTcit1BfMzga5df7VrLPVRbom1Bi0kQ0BDeDex3hHNqHS5X+HSrd/njzP1xp8twG8hTE+njv85PWoGBTo1eUGW/esChIJKA5f3/F4B9ErgBNNOKnYmRgxixd562OWAwAQZK0r0roe2H/Mfg2VvgxT0kHd22NQLoAv0YI4jcXcCFrnV/80vHUQ8AsAYAbkLcz1jkfk3YwYDP8jbJCqcwJRt9ialYKJwvXlEe0TMeGdq7EjCO0z/pIpu82k2R/C0FtCFih3bUvJEmWoVVx8UGkDDQEORLbzxQCt0IOiQGFcoCCxgQmL0x9ZoljCWg5vZuuhU4uSOuRTuM+aa4xoLkeOcvgGRSOXrqfkV8JpWKoJB4dmY2qSuxw8LsAAzK0=
109 8cc6036bca532e06681c5a8fa37efaa812de67b5 0 iQIVAwUAVUP0xCBXgaxoKi1yAQLIChAAme3kg1Z0V8t5PnWKDoIvscIeAsD2s6EhMy1SofmdZ4wvYD1VmGC6TgXMCY7ssvRBhxqwG3GxwYpwELASuw2GYfVot2scN7+b8Hs5jHtkQevKbxarYni+ZI9mw/KldnJixD1yW3j+LoJFh/Fu6GD2yrfGIhimFLozcwUu3EbLk7JzyHSn7/8NFjLJz0foAYfcbowU9/BFwNVLrQPnsUbWcEifsq5bYso9MBO9k+25yLgqHoqMbGpJcgjubNy1cWoKnlKS+lOJl0/waAk+aIjHXMzFpRRuJDjxEZn7V4VdV5d23nrBTcit1BfMzga5df7VrLPVRbom1Bi0kQ0BDeDex3hHNqHS5X+HSrd/njzP1xp8twG8hTE+njv85PWoGBTo1eUGW/esChIJKA5f3/F4B9ErgBNNOKnYmRgxixd562OWAwAQZK0r0roe2H/Mfg2VvgxT0kHd22NQLoAv0YI4jcXcCFrnV/80vHUQ8AsAYAbkLcz1jkfk3YwYDP8jbJCqcwJRt9ialYKJwvXlEe0TMeGdq7EjCO0z/pIpu82k2R/C0FtCFih3bUvJEmWoVVx8UGkDDQEORLbzxQCt0IOiQGFcoCCxgQmL0x9ZoljCWg5vZuuhU4uSOuRTuM+aa4xoLkeOcvgGRSOXrqfkV8JpWKoJB4dmY2qSuxw8LsAAzK0=
110 ed18f4acf435a2824c6f49fba40f42b9df5da7ad 0 iQIVAwUAVWy9mCBXgaxoKi1yAQIm+Q/+I/tV8DC51d4f/6T5OR+motlIx9U5za5p9XUUzfp3tzSY2PutVko/FclajVdFekZsK5pUzlh/GZhfe1jjyEEIr3UC3yWk8hMcvvS+2UDmfy81QxN7Uf0kz4mZOlME6d/fYDzf4cDKkkCXoec3kyZBw7L84mteUcrJoyb5K3fkQBrK5CG/CV7+uZN6b9+quKjtDhDEkAyc6phNanzWNgiHGucEbNgXsKM01HmV1TnN4GXTKx8y2UDalIJOPyes2OWHggibMHbaNnGnwSBAK+k29yaQ5FD0rsA+q0j3TijA1NfqvtluNEPbFOx/wJV4CxonYad93gWyEdgU34LRqqw1bx7PFUvew2/T3TJsxQLoCt67OElE7ScG8evuNEe8/4r3LDnzYFx7QMP5r5+B7PxVpj/DT+buS16BhYS8pXMMqLynFOQkX5uhEM7mNC0JTXQsBMHSDAcizVDrdFCF2OSfQjLpUfFP1VEWX7EInqj7hZrd+GE7TfBD8/rwSBSkkCX2aa9uKyt6Ius1GgQUuEETskAUvvpsNBzZxtvGpMMhqQLGlJYnBbhOmsbOyTSnXU66KJ5e/H3O0KRrF09i74v30DaY4uIH8xG6KpSkfw5s/oiLCtagfc0goUvvojk9pACDR3CKM/jVC63EVp2oUcjT72jUgSLxBgi7siLD8IW86wc=
110 ed18f4acf435a2824c6f49fba40f42b9df5da7ad 0 iQIVAwUAVWy9mCBXgaxoKi1yAQIm+Q/+I/tV8DC51d4f/6T5OR+motlIx9U5za5p9XUUzfp3tzSY2PutVko/FclajVdFekZsK5pUzlh/GZhfe1jjyEEIr3UC3yWk8hMcvvS+2UDmfy81QxN7Uf0kz4mZOlME6d/fYDzf4cDKkkCXoec3kyZBw7L84mteUcrJoyb5K3fkQBrK5CG/CV7+uZN6b9+quKjtDhDEkAyc6phNanzWNgiHGucEbNgXsKM01HmV1TnN4GXTKx8y2UDalIJOPyes2OWHggibMHbaNnGnwSBAK+k29yaQ5FD0rsA+q0j3TijA1NfqvtluNEPbFOx/wJV4CxonYad93gWyEdgU34LRqqw1bx7PFUvew2/T3TJsxQLoCt67OElE7ScG8evuNEe8/4r3LDnzYFx7QMP5r5+B7PxVpj/DT+buS16BhYS8pXMMqLynFOQkX5uhEM7mNC0JTXQsBMHSDAcizVDrdFCF2OSfQjLpUfFP1VEWX7EInqj7hZrd+GE7TfBD8/rwSBSkkCX2aa9uKyt6Ius1GgQUuEETskAUvvpsNBzZxtvGpMMhqQLGlJYnBbhOmsbOyTSnXU66KJ5e/H3O0KRrF09i74v30DaY4uIH8xG6KpSkfw5s/oiLCtagfc0goUvvojk9pACDR3CKM/jVC63EVp2oUcjT72jUgSLxBgi7siLD8IW86wc=
111 540cd0ddac49c1125b2e013aa2ff18ecbd4dd954 0 iQIVAwUAVZRtzSBXgaxoKi1yAQJVLhAAtfn+8OzHIp6wRC4NUbkImAJRLsNTRPKeRSWPCF5O5XXQ84hp+86qjhndIE6mcJSAt4cVP8uky6sEa8ULd6b3ACRBvtgZtsecA9S/KtRjyE9CKr8nP+ogBNqJPaYlTz9RuwGedOd+8I9lYgsnRjfaHSByNMX08WEHtWqAWhSkAz/HO32ardS38cN97fckCgQtA8v7c77nBT7vcw4epgxyUQvMUxUhqmCVVhVfz8JXa5hyJxFrOtqgaVuQ1B5Y/EKxcyZT+JNHPtu3V1uc1awS/w16CEPstNBSFHax5MuT9UbY0mV2ZITP99EkM+vdomh82VHdnMo0i7Pz7XF45ychD4cteroO9gGqDDt9j7hd1rubBX1bfkPsd/APJlyeshusyTj+FqsUD/HDlvM9LRjY1HpU7i7yAlLQQ3851XKMLUPNFYu2r3bo8Wt/CCHtJvB4wYuH+7Wo3muudpU01ziJBxQrUWwPbUrG+7LvO1iEEVxB8l+8Vq0mU3Te7lJi1kGetm6xHNbtvQip5P2YUqvv+lLo/K8KoJDxsh63Y01JGwdmUDb8mnFlRx4J7hQJaoNEvz3cgnc4X8gDJD8sUOjGOPnbtz2QwTY+zj/5+FdLxWDCxNrHX5vvkVdJHcCqEfVvQTKfDMOUeKuhjI7GD7t3xRPfUxq19jjoLPe7aqn1Z1s=
111 540cd0ddac49c1125b2e013aa2ff18ecbd4dd954 0 iQIVAwUAVZRtzSBXgaxoKi1yAQJVLhAAtfn+8OzHIp6wRC4NUbkImAJRLsNTRPKeRSWPCF5O5XXQ84hp+86qjhndIE6mcJSAt4cVP8uky6sEa8ULd6b3ACRBvtgZtsecA9S/KtRjyE9CKr8nP+ogBNqJPaYlTz9RuwGedOd+8I9lYgsnRjfaHSByNMX08WEHtWqAWhSkAz/HO32ardS38cN97fckCgQtA8v7c77nBT7vcw4epgxyUQvMUxUhqmCVVhVfz8JXa5hyJxFrOtqgaVuQ1B5Y/EKxcyZT+JNHPtu3V1uc1awS/w16CEPstNBSFHax5MuT9UbY0mV2ZITP99EkM+vdomh82VHdnMo0i7Pz7XF45ychD4cteroO9gGqDDt9j7hd1rubBX1bfkPsd/APJlyeshusyTj+FqsUD/HDlvM9LRjY1HpU7i7yAlLQQ3851XKMLUPNFYu2r3bo8Wt/CCHtJvB4wYuH+7Wo3muudpU01ziJBxQrUWwPbUrG+7LvO1iEEVxB8l+8Vq0mU3Te7lJi1kGetm6xHNbtvQip5P2YUqvv+lLo/K8KoJDxsh63Y01JGwdmUDb8mnFlRx4J7hQJaoNEvz3cgnc4X8gDJD8sUOjGOPnbtz2QwTY+zj/5+FdLxWDCxNrHX5vvkVdJHcCqEfVvQTKfDMOUeKuhjI7GD7t3xRPfUxq19jjoLPe7aqn1Z1s=
112 96a38d44ba093bd1d1ecfd34119e94056030278b 0 iQIVAwUAVarUUyBXgaxoKi1yAQIfJw/+MG/0736F/9IvzgCTF6omIC+9kS8JH0n/JBGPhpbPAHK4xxjhOOz6m3Ia3c3HNoy+I6calwU6YV7k5dUzlyLhM0Z5oYpdrH+OBNxDEsD5SfhclfR63MK1kmgtD33izijsZ++6a+ZaVfyxpMTksKOktWSIDD63a5b/avb6nKY64KwJcbbeXPdelxvXV7TXYm0GvWc46BgvrHOJpYHCDaXorAn6BMq7EQF8sxdNK4GVMNMVk1njve0HOg3Kz8llPB/7QmddZXYLFGmWqICyUn1IsJDfePxzh8sOYVCbxAgitTJHJJmmH5gzVzw7t7ljtmxSJpcUGQJB2MphejmNFGfgvJPB9c6xOCfUqDjxN5m24V+UYesZntpfgs3lpfvE7785IpVnf6WfKG4PKty01ome/joHlDlrRTekKMlpiBapGMfv8EHvPBrOA+5yAHNfKsmcyCcjD1nvXYZ2/X9qY35AhdcBuNkyp55oPDOdtYIHfnOIxlYMKG1dusDx3Z4eveF0lQTzfRVoE5w+k9A2Ov3Zx0aiSkFFevJjrq5QBfs9dAiT8JYgBmWhaJzCtJm12lQirRMKR/br88Vwt/ry/UVY9cereMNvRYUGOGfC8CGGDCw4WDD+qWvyB3mmrXVuMlXxQRIZRJy5KazaQXsBWuIsx4kgGqC5Uo+yzpiQ1VMuCyI=
112 96a38d44ba093bd1d1ecfd34119e94056030278b 0 iQIVAwUAVarUUyBXgaxoKi1yAQIfJw/+MG/0736F/9IvzgCTF6omIC+9kS8JH0n/JBGPhpbPAHK4xxjhOOz6m3Ia3c3HNoy+I6calwU6YV7k5dUzlyLhM0Z5oYpdrH+OBNxDEsD5SfhclfR63MK1kmgtD33izijsZ++6a+ZaVfyxpMTksKOktWSIDD63a5b/avb6nKY64KwJcbbeXPdelxvXV7TXYm0GvWc46BgvrHOJpYHCDaXorAn6BMq7EQF8sxdNK4GVMNMVk1njve0HOg3Kz8llPB/7QmddZXYLFGmWqICyUn1IsJDfePxzh8sOYVCbxAgitTJHJJmmH5gzVzw7t7ljtmxSJpcUGQJB2MphejmNFGfgvJPB9c6xOCfUqDjxN5m24V+UYesZntpfgs3lpfvE7785IpVnf6WfKG4PKty01ome/joHlDlrRTekKMlpiBapGMfv8EHvPBrOA+5yAHNfKsmcyCcjD1nvXYZ2/X9qY35AhdcBuNkyp55oPDOdtYIHfnOIxlYMKG1dusDx3Z4eveF0lQTzfRVoE5w+k9A2Ov3Zx0aiSkFFevJjrq5QBfs9dAiT8JYgBmWhaJzCtJm12lQirRMKR/br88Vwt/ry/UVY9cereMNvRYUGOGfC8CGGDCw4WDD+qWvyB3mmrXVuMlXxQRIZRJy5KazaQXsBWuIsx4kgGqC5Uo+yzpiQ1VMuCyI=
113 21aa1c313b05b1a85f8ffa1120d51579ddf6bf24 0 iQIVAwUAVbuouCBXgaxoKi1yAQL2ng//eI1w51F4YkDiUAhrZuc8RE/chEd2o4F6Jyu9laA03vbim598ntqGjX3+UkOyTQ/zGVeZfW2cNG8zkJjSLk138DHCYl2YPPD/yxqMOJp/a7U34+HrA0aE5Y2pcfx+FofZHRvRtt40UCngicjKivko8au7Ezayidpa/vQbc6dNvGrwwk4KMgOP2HYIfHgCirR5UmaWtNpzlLhf9E7JSNL5ZXij3nt6AgEPyn0OvmmOLyUARO/JTJ6vVyLEtwiXg7B3sF5RpmyFDhrkZ+MbFHgL4k/3y9Lb97WaZl8nXJIaNPOTPJqkApFY/56S12PKYK4js2OgU+QsX1XWvouAhEx6CC6Jk9EHhr6+9qxYFhBJw7RjbswUG6LvJy/kBe+Ei5UbYg9dATf3VxQ6Gqs19lebtzltERH2yNwaHyVeqqakPSonOaUyxGMRRosvNHyrTTor38j8d27KksgpocXzBPZcc1MlS3vJg2nIwZlc9EKM9z5R0J1KAi1Z/+xzBjiGRYg5EZY6ElAw30eCjGta7tXlBssJiKeHut7QTLxCZHQuX1tKxDDs1qlXlGCMbrFqo0EiF9hTssptRG3ZyLwMdzEjnh4ki6gzONZKDI8uayAS3N+CEtWcGUtiA9OwuiFXTwodmles/Mh14LEhiVZoDK3L9TPcY22o2qRuku/6wq6QKsg=
113 21aa1c313b05b1a85f8ffa1120d51579ddf6bf24 0 iQIVAwUAVbuouCBXgaxoKi1yAQL2ng//eI1w51F4YkDiUAhrZuc8RE/chEd2o4F6Jyu9laA03vbim598ntqGjX3+UkOyTQ/zGVeZfW2cNG8zkJjSLk138DHCYl2YPPD/yxqMOJp/a7U34+HrA0aE5Y2pcfx+FofZHRvRtt40UCngicjKivko8au7Ezayidpa/vQbc6dNvGrwwk4KMgOP2HYIfHgCirR5UmaWtNpzlLhf9E7JSNL5ZXij3nt6AgEPyn0OvmmOLyUARO/JTJ6vVyLEtwiXg7B3sF5RpmyFDhrkZ+MbFHgL4k/3y9Lb97WaZl8nXJIaNPOTPJqkApFY/56S12PKYK4js2OgU+QsX1XWvouAhEx6CC6Jk9EHhr6+9qxYFhBJw7RjbswUG6LvJy/kBe+Ei5UbYg9dATf3VxQ6Gqs19lebtzltERH2yNwaHyVeqqakPSonOaUyxGMRRosvNHyrTTor38j8d27KksgpocXzBPZcc1MlS3vJg2nIwZlc9EKM9z5R0J1KAi1Z/+xzBjiGRYg5EZY6ElAw30eCjGta7tXlBssJiKeHut7QTLxCZHQuX1tKxDDs1qlXlGCMbrFqo0EiF9hTssptRG3ZyLwMdzEjnh4ki6gzONZKDI8uayAS3N+CEtWcGUtiA9OwuiFXTwodmles/Mh14LEhiVZoDK3L9TPcY22o2qRuku/6wq6QKsg=
114 1a45e49a6bed023deb229102a8903234d18054d3 0 iQIVAwUAVeYa2SBXgaxoKi1yAQLWVA//Q7vU0YzngbxIbrTPvfFiNTJcT4bx9u1xMHRZf6QBIE3KtRHKTooJwH9lGR0HHM+8DWWZup3Vzo6JuWHMGoW0v5fzDyk2czwM9BgQQPfEmoJ/ZuBMevTkTZngjgHVwhP3tHFym8Rk9vVxyiZd35EcxP+4F817GCzD+K7XliIBqVggmv9YeQDXfEtvo7UZrMPPec79t8tzt2UadI3KC1jWUriTS1Fg1KxgXW6srD80D10bYyCkkdo/KfF6BGZ9SkF+U3b95cuqSmOfoyyQwUA3JbMXXOnIefnC7lqRC2QTC6mYDx5hIkBiwymXJBe8rpq/S94VVvPGfW6A5upyeCZISLEEnAz0GlykdpIy/NogzhmWpbAMOus05Xnen6xPdNig6c/M5ZleRxVobNrZSd7c5qI3aUUyfMKXlY1j9oiUTjSKH1IizwaI3aL/MM70eErBxXiLs2tpQvZeaVLn3kwCB5YhywO3LK0x+FNx4Gl90deAXMYibGNiLTq9grpB8fuLg9M90JBjFkeYkrSJ2yGYumYyP/WBA3mYEYGDLNstOby4riTU3WCqVl+eah6ss3l+gNDjLxiMtJZ/g0gQACaAvxQ9tYp5eeRMuLRTp79QQPxv97s8IyVwE/TlPlcSFlEXAzsBvqvsolQXRVi9AxA6M2davYabBYAgRf6rRfgujoU=
114 1a45e49a6bed023deb229102a8903234d18054d3 0 iQIVAwUAVeYa2SBXgaxoKi1yAQLWVA//Q7vU0YzngbxIbrTPvfFiNTJcT4bx9u1xMHRZf6QBIE3KtRHKTooJwH9lGR0HHM+8DWWZup3Vzo6JuWHMGoW0v5fzDyk2czwM9BgQQPfEmoJ/ZuBMevTkTZngjgHVwhP3tHFym8Rk9vVxyiZd35EcxP+4F817GCzD+K7XliIBqVggmv9YeQDXfEtvo7UZrMPPec79t8tzt2UadI3KC1jWUriTS1Fg1KxgXW6srD80D10bYyCkkdo/KfF6BGZ9SkF+U3b95cuqSmOfoyyQwUA3JbMXXOnIefnC7lqRC2QTC6mYDx5hIkBiwymXJBe8rpq/S94VVvPGfW6A5upyeCZISLEEnAz0GlykdpIy/NogzhmWpbAMOus05Xnen6xPdNig6c/M5ZleRxVobNrZSd7c5qI3aUUyfMKXlY1j9oiUTjSKH1IizwaI3aL/MM70eErBxXiLs2tpQvZeaVLn3kwCB5YhywO3LK0x+FNx4Gl90deAXMYibGNiLTq9grpB8fuLg9M90JBjFkeYkrSJ2yGYumYyP/WBA3mYEYGDLNstOby4riTU3WCqVl+eah6ss3l+gNDjLxiMtJZ/g0gQACaAvxQ9tYp5eeRMuLRTp79QQPxv97s8IyVwE/TlPlcSFlEXAzsBvqvsolQXRVi9AxA6M2davYabBYAgRf6rRfgujoU=
115 9a466b9f9792e3ad7ae3fc6c43c3ff2e136b718d 0 iQIVAwUAVg1oMSBXgaxoKi1yAQLPag/+Pv0+pR9b9Y5RflEcERUzVu92q+l/JEiP7PHP9pAZuXoQ0ikYBFo1Ygw8tkIG00dgEaLk/2b7E3OxaU9pjU3thoX//XpTcbkJtVhe7Bkjh9/S3dRpm2FWNL9n0qnywebziB45Xs8XzUwBZTYOkVRInYr/NzSo8KNbQH1B4u2g56veb8u/7GtEvBSGnMGVYKhVUZ3jxyDf371QkdafMOJPpogkZcVhXusvMZPDBYtTIzswyxBJ2jxHzjt8+EKs+FI3FxzvQ9Ze3M5Daa7xfiHI3sOgECO8GMVaJi0F49lttKx08KONw8xLlEof+cJ+qxLxQ42X5XOQglJ2/bv5ES5JiZYAti2XSXbZK96p4wexqL4hnaLVU/2iEUfqB9Sj6itEuhGOknPD9fQo1rZXYIS8CT5nGTNG4rEpLFN6VwWn1btIMNkEHw998zU7N3HAOk6adD6zGcntUfMBvQC3V4VK3o7hp8PGeySrWrOLcC/xLKM+XRonz46woJK5D8w8lCVYAxBWEGKAFtj9hv9R8Ye9gCW0Q8BvJ7MwGpn+7fLQ1BVZdV1LZQTSBUr5u8mNeDsRo4H2hITQRhUeElIwlMsUbbN078a4JPOUgPz1+Fi8oHRccBchN6I40QohL934zhcKXQ+NXYN8BgpCicPztSg8O8Y/qvhFP12Zu4tOH8P/dFY=
115 9a466b9f9792e3ad7ae3fc6c43c3ff2e136b718d 0 iQIVAwUAVg1oMSBXgaxoKi1yAQLPag/+Pv0+pR9b9Y5RflEcERUzVu92q+l/JEiP7PHP9pAZuXoQ0ikYBFo1Ygw8tkIG00dgEaLk/2b7E3OxaU9pjU3thoX//XpTcbkJtVhe7Bkjh9/S3dRpm2FWNL9n0qnywebziB45Xs8XzUwBZTYOkVRInYr/NzSo8KNbQH1B4u2g56veb8u/7GtEvBSGnMGVYKhVUZ3jxyDf371QkdafMOJPpogkZcVhXusvMZPDBYtTIzswyxBJ2jxHzjt8+EKs+FI3FxzvQ9Ze3M5Daa7xfiHI3sOgECO8GMVaJi0F49lttKx08KONw8xLlEof+cJ+qxLxQ42X5XOQglJ2/bv5ES5JiZYAti2XSXbZK96p4wexqL4hnaLVU/2iEUfqB9Sj6itEuhGOknPD9fQo1rZXYIS8CT5nGTNG4rEpLFN6VwWn1btIMNkEHw998zU7N3HAOk6adD6zGcntUfMBvQC3V4VK3o7hp8PGeySrWrOLcC/xLKM+XRonz46woJK5D8w8lCVYAxBWEGKAFtj9hv9R8Ye9gCW0Q8BvJ7MwGpn+7fLQ1BVZdV1LZQTSBUr5u8mNeDsRo4H2hITQRhUeElIwlMsUbbN078a4JPOUgPz1+Fi8oHRccBchN6I40QohL934zhcKXQ+NXYN8BgpCicPztSg8O8Y/qvhFP12Zu4tOH8P/dFY=
116 b66e3ca0b90c3095ea28dfd39aa24247bebf5c20 0 iQIVAwUAViarTyBXgaxoKi1yAQLZgRAAh7c7ebn7kUWI5M/b/T6qHGjFrU5azkjamzy9IG+KIa2hZgSMxyEM7JJUFqKP4TiWa3sW03bjKGSM/SjjDSSyheX+JIVSPNyKrBwneYhPq45Ius8eiHziClkt0CSsl2d9xDRpI0JmHbN0Pf8nh7rnbL+231GDAOT6dP+2S8K1HGa/0BgEcL9gpYs4/2GyjL+hBSUjyrabzvwe48DCN5W0tEJbGFw5YEADxdfbVbNEuXL81tR4PFGiJxPW0QKRLDB74MWmiWC0gi2ZC/IhbNBZ2sLb6694d4Bx4PVwtiARh63HNXVMEaBrFu1S9NcMQyHvAOc6Zw4izF/PCeTcdEnPk8J1t5PTz09Lp0EAKxe7CWIViy350ke5eiaxO3ySrNMX6d83BOHLDqEFMSWm+ad+KEMT4CJrK4X/n/XMgEFAaU5nWlIRqrLRIeU2Ifc625T0Xh4BgTqXPpytQxhgV5b+Fi6duNk4cy+QnHT4ymxI6BPD9HvSQwc+O7h37qjvJVZmpQX6AP8O75Yza8ZbcYKRIIxZzOkwNpzE5A/vpvP5bCRn7AGcT3ORWmAYr/etr3vxUvt2fQz6U/R4S915V+AeWBdcp+uExu6VZ42M0vhhh0lyzx1VRJGVdV+LoxFKkaC42d0yT+O1QEhSB7WL1D3/a/iWubv6ieB/cvNMhFaK9DA=
116 b66e3ca0b90c3095ea28dfd39aa24247bebf5c20 0 iQIVAwUAViarTyBXgaxoKi1yAQLZgRAAh7c7ebn7kUWI5M/b/T6qHGjFrU5azkjamzy9IG+KIa2hZgSMxyEM7JJUFqKP4TiWa3sW03bjKGSM/SjjDSSyheX+JIVSPNyKrBwneYhPq45Ius8eiHziClkt0CSsl2d9xDRpI0JmHbN0Pf8nh7rnbL+231GDAOT6dP+2S8K1HGa/0BgEcL9gpYs4/2GyjL+hBSUjyrabzvwe48DCN5W0tEJbGFw5YEADxdfbVbNEuXL81tR4PFGiJxPW0QKRLDB74MWmiWC0gi2ZC/IhbNBZ2sLb6694d4Bx4PVwtiARh63HNXVMEaBrFu1S9NcMQyHvAOc6Zw4izF/PCeTcdEnPk8J1t5PTz09Lp0EAKxe7CWIViy350ke5eiaxO3ySrNMX6d83BOHLDqEFMSWm+ad+KEMT4CJrK4X/n/XMgEFAaU5nWlIRqrLRIeU2Ifc625T0Xh4BgTqXPpytQxhgV5b+Fi6duNk4cy+QnHT4ymxI6BPD9HvSQwc+O7h37qjvJVZmpQX6AP8O75Yza8ZbcYKRIIxZzOkwNpzE5A/vpvP5bCRn7AGcT3ORWmAYr/etr3vxUvt2fQz6U/R4S915V+AeWBdcp+uExu6VZ42M0vhhh0lyzx1VRJGVdV+LoxFKkaC42d0yT+O1QEhSB7WL1D3/a/iWubv6ieB/cvNMhFaK9DA=
117 47dd34f2e7272be9e3b2a5a83cd0d20be44293f4 0 iQIVAwUAVjZiKiBXgaxoKi1yAQKBWQ/+JcE37vprSOA5e0ezs/avC7leR6hTlXy9O5bpFnvMpbVMTUp+KfBE4HxTT0KKXKh9lGtNaQ+lAmHuy1OQE1hBKPIaCUd8/1gunGsXgRM3TJ9LwjFd4qFpOMxvOouc6kW5kmea7V9W2fg6aFNjjc/4/0J3HMOIjmf2fFz87xqR1xX8iezJ57A4pUPNViJlOWXRzfa56cI6VUe5qOMD0NRXcY+JyI5qW25Y/aL5D9loeKflpzd53Ue+Pu3qlhddJd3PVkaAiVDH+DYyRb8sKgwuiEsyaBO18IBgC8eDmTohEJt6707A+WNhwBJwp9aOUhHC7caaKRYhEKuDRQ3op++VqwuxbFRXx22XYR9bEzQIlpsv9GY2k8SShU5MZqUKIhk8vppFI6RaID5bmALnLLmjmXfSPYSJDzDuCP5UTQgI3PKPOATorVrqMdKzfb7FiwtcTvtHAXpOgLaY9P9XIePbnei6Rx9TfoHYDvzFWRqzSjl21xR+ZUrJtG2fx7XLbMjEAZJcnjP++GRvNbHBOi57aX0l2LO1peQqZVMULoIivaoLFP3i16RuXXQ/bvKyHmKjJzGrLc0QCa0yfrvV2m30RRMaYlOv7ToJfdfZLXvSAP0zbAuDaXdjGnq7gpfIlNE3xM+kQ75Akcf4V4fK1p061EGBQvQz6Ov3PkPiWL/bxrQ=
117 47dd34f2e7272be9e3b2a5a83cd0d20be44293f4 0 iQIVAwUAVjZiKiBXgaxoKi1yAQKBWQ/+JcE37vprSOA5e0ezs/avC7leR6hTlXy9O5bpFnvMpbVMTUp+KfBE4HxTT0KKXKh9lGtNaQ+lAmHuy1OQE1hBKPIaCUd8/1gunGsXgRM3TJ9LwjFd4qFpOMxvOouc6kW5kmea7V9W2fg6aFNjjc/4/0J3HMOIjmf2fFz87xqR1xX8iezJ57A4pUPNViJlOWXRzfa56cI6VUe5qOMD0NRXcY+JyI5qW25Y/aL5D9loeKflpzd53Ue+Pu3qlhddJd3PVkaAiVDH+DYyRb8sKgwuiEsyaBO18IBgC8eDmTohEJt6707A+WNhwBJwp9aOUhHC7caaKRYhEKuDRQ3op++VqwuxbFRXx22XYR9bEzQIlpsv9GY2k8SShU5MZqUKIhk8vppFI6RaID5bmALnLLmjmXfSPYSJDzDuCP5UTQgI3PKPOATorVrqMdKzfb7FiwtcTvtHAXpOgLaY9P9XIePbnei6Rx9TfoHYDvzFWRqzSjl21xR+ZUrJtG2fx7XLbMjEAZJcnjP++GRvNbHBOi57aX0l2LO1peQqZVMULoIivaoLFP3i16RuXXQ/bvKyHmKjJzGrLc0QCa0yfrvV2m30RRMaYlOv7ToJfdfZLXvSAP0zbAuDaXdjGnq7gpfIlNE3xM+kQ75Akcf4V4fK1p061EGBQvQz6Ov3PkPiWL/bxrQ=
118 1aa5083cbebbe7575c88f3402ab377539b484897 0 iQIVAwUAVkEdCCBXgaxoKi1yAQKdWg//crTr5gsnHQppuD1p+PPn3/7SMsWJ7bgbuaXgERDLC0zWMfhM2oMmu/4jqXnpangdBVvb0SojejgzxoBo9FfRQiIoKt0vxmmn+S8CrEwb99rpP4M7lgyMAInKPMXQdYxkoDNwL70Afmog6eBtlxjYnu8nmUE/swu6JoVns+tF8UOvIKFYbuCcGujo2pUOQC0xBGiHeHSGRDJOlWmY2d7D/PkQtQE/u/d4QZt7enTHMiV44XVJ8+0U0f1ZQE7V+hNWf+IjwcZtL95dnQzUKs6tXMIln/OwO+eJ3d61BfLvmABvCwUC9IepPssNSFBUfGqBAP5wXOzFIPSYn00IWpmZtCnpUNL99X1IV3RP+p99gnEDTScQFPYt5B0q5I1nFdRh1p48BSF/kjPA7V++UfBwMXrrYLKhUR9BjmrRzYnyXJKwbH6iCNj5hsXUkVrBdBi/FnMczgsVILfFcIXUfnJD3E/dG+1lmuObg6dEynxiGChTuaR4KkLa5ZRkUcUl6fWlSRsqSNbGEEbdwcI+nTCZqJUlLSghumhs0Z89Hs1nltBd1ALX2VLJEHrKMrFQ8NfEBeCB6ENqMJi5qPlq354MCdGOZ9RvisX/HlxE4Q61BW0+EwnyXSch6LFSOS3axOocUazMoK1XiOTJSv/5bAsnwb0ztDWeUj9fZEJL+SWtgB8=
118 1aa5083cbebbe7575c88f3402ab377539b484897 0 iQIVAwUAVkEdCCBXgaxoKi1yAQKdWg//crTr5gsnHQppuD1p+PPn3/7SMsWJ7bgbuaXgERDLC0zWMfhM2oMmu/4jqXnpangdBVvb0SojejgzxoBo9FfRQiIoKt0vxmmn+S8CrEwb99rpP4M7lgyMAInKPMXQdYxkoDNwL70Afmog6eBtlxjYnu8nmUE/swu6JoVns+tF8UOvIKFYbuCcGujo2pUOQC0xBGiHeHSGRDJOlWmY2d7D/PkQtQE/u/d4QZt7enTHMiV44XVJ8+0U0f1ZQE7V+hNWf+IjwcZtL95dnQzUKs6tXMIln/OwO+eJ3d61BfLvmABvCwUC9IepPssNSFBUfGqBAP5wXOzFIPSYn00IWpmZtCnpUNL99X1IV3RP+p99gnEDTScQFPYt5B0q5I1nFdRh1p48BSF/kjPA7V++UfBwMXrrYLKhUR9BjmrRzYnyXJKwbH6iCNj5hsXUkVrBdBi/FnMczgsVILfFcIXUfnJD3E/dG+1lmuObg6dEynxiGChTuaR4KkLa5ZRkUcUl6fWlSRsqSNbGEEbdwcI+nTCZqJUlLSghumhs0Z89Hs1nltBd1ALX2VLJEHrKMrFQ8NfEBeCB6ENqMJi5qPlq354MCdGOZ9RvisX/HlxE4Q61BW0+EwnyXSch6LFSOS3axOocUazMoK1XiOTJSv/5bAsnwb0ztDWeUj9fZEJL+SWtgB8=
119 2d437a0f3355834a9485bbbeb30a52a052c98f19 0 iQIVAwUAVl5U9CBXgaxoKi1yAQLocg//a4YFz9UVSIEzVEJMUPJnN2dBvEXRpwpb5CdKPd428+18K6VWZd5Mc6xNNRV5AV/hCYylgqDplIvyOvwCj7uN8nEOrLUQQ0Pp37M5ZIX8ZVCK/wgchJ2ltabUG1NrZ7/JA84U79VGLAECMnD0Z9WvZDESpVXmdXfxrk1eCc3omRB0ofNghEx+xpYworfZsu8aap1GHQuBsjPv4VyUWGpMq/KA01PdxRTELmrJnfSyr0nPKwxlI5KsbA1GOe+Mk3tp5HJ42DZqLtKSGPirf6E+6lRJeB0H7EpotN4wD3yZDsw6AgRb2C/ay/3T3Oz7CN+45mwuujV9Cxx5zs1EeOgZcqgA/hXMcwlQyvQDMrWpO8ytSBm6MhOuFOTB3HnUxfsnfSocLJsbNwGWKceAzACcXSqapveVAz/7h+InFgl/8Qce28UJdnX5wro5gP6UWt+xrvc7vfmVGgI3oxbiOUrfglhkjmrxBjEiDQy4BWH7HWMZUVxnqPQRcxIE10+dv0KtM/PBkbUtnbGJ88opFBGkFweje5vQcZy/duuPEIufRkPr8EV47QjOxlvldEjlLq3+QUdJZEgCIFw1X0y7Pix4dsPFjwOmAyo4El1ePrdFzG3dXSVA3eHvMDRnYnNlue9wHvKhYbBle5xTOZBgGuMzhDVe+54JLql5JYr4WrI1pvA=
119 2d437a0f3355834a9485bbbeb30a52a052c98f19 0 iQIVAwUAVl5U9CBXgaxoKi1yAQLocg//a4YFz9UVSIEzVEJMUPJnN2dBvEXRpwpb5CdKPd428+18K6VWZd5Mc6xNNRV5AV/hCYylgqDplIvyOvwCj7uN8nEOrLUQQ0Pp37M5ZIX8ZVCK/wgchJ2ltabUG1NrZ7/JA84U79VGLAECMnD0Z9WvZDESpVXmdXfxrk1eCc3omRB0ofNghEx+xpYworfZsu8aap1GHQuBsjPv4VyUWGpMq/KA01PdxRTELmrJnfSyr0nPKwxlI5KsbA1GOe+Mk3tp5HJ42DZqLtKSGPirf6E+6lRJeB0H7EpotN4wD3yZDsw6AgRb2C/ay/3T3Oz7CN+45mwuujV9Cxx5zs1EeOgZcqgA/hXMcwlQyvQDMrWpO8ytSBm6MhOuFOTB3HnUxfsnfSocLJsbNwGWKceAzACcXSqapveVAz/7h+InFgl/8Qce28UJdnX5wro5gP6UWt+xrvc7vfmVGgI3oxbiOUrfglhkjmrxBjEiDQy4BWH7HWMZUVxnqPQRcxIE10+dv0KtM/PBkbUtnbGJ88opFBGkFweje5vQcZy/duuPEIufRkPr8EV47QjOxlvldEjlLq3+QUdJZEgCIFw1X0y7Pix4dsPFjwOmAyo4El1ePrdFzG3dXSVA3eHvMDRnYnNlue9wHvKhYbBle5xTOZBgGuMzhDVe+54JLql5JYr4WrI1pvA=
120 ea389970c08449440587712117f178d33bab3f1e 0 iQIVAwUAVociGyBXgaxoKi1yAQJx9Q//TzMypcls5CQW3DM9xY1Q+RFeIw1LcDIev6NDBjUYxULb2WIK2qPw4Th5czF622SMd+XO/kiQeWYp9IW90MZOUVT1YGgUPKlKWMjkf0lZEPzprHjHq0+z/no1kBCBQg2uUOLsb6Y7zom4hFCyPsxXOk5nnxcFEK0VDbODa9zoKb/flyQ7rtzs+Z6BljIQ0TJAJsXs+6XgrW1XJ/f6nbeqsQyPklIBJuGKiaU1Pg8wQe6QqFaO1NYgM3hBETku6r3OTpUhu/2FTUZ7yDWGGzBqmifxzdHoj7/B+2qzRpII77PlZqoe6XF+UOObSFnhKvXKLjlGY5cy3SXBMbHkPcYtHua8wYR8LqO2bYYnsDd9qD0DJ+LlqH0ZMUkB2Cdk9q/cp1PGJWGlYYecHP87DLuWKwS+a6LhVI9TGkIUosVtLaIMsUUEz83RJFb4sSGOXtjk5DDznn9QW8ltXXMTdGQwFq1vmuiXATYenhszbvagrnbAnDyNFths4IhS1jG8237SB36nGmO3zQm5V7AMHfSrISB/8VPyY4Si7uvAV2kMWxuMhYuQbBwVx/KxbKrYjowuvJvCKaV101rWxvSeU2wDih20v+dnQKPveRNnO8AAK/ICflVVsISkd7hXcfk+SnhfxcPQTr+HQIJEW9wt5Q8WbgHk9wuR8kgXQEX6tCGpT/w=
120 ea389970c08449440587712117f178d33bab3f1e 0 iQIVAwUAVociGyBXgaxoKi1yAQJx9Q//TzMypcls5CQW3DM9xY1Q+RFeIw1LcDIev6NDBjUYxULb2WIK2qPw4Th5czF622SMd+XO/kiQeWYp9IW90MZOUVT1YGgUPKlKWMjkf0lZEPzprHjHq0+z/no1kBCBQg2uUOLsb6Y7zom4hFCyPsxXOk5nnxcFEK0VDbODa9zoKb/flyQ7rtzs+Z6BljIQ0TJAJsXs+6XgrW1XJ/f6nbeqsQyPklIBJuGKiaU1Pg8wQe6QqFaO1NYgM3hBETku6r3OTpUhu/2FTUZ7yDWGGzBqmifxzdHoj7/B+2qzRpII77PlZqoe6XF+UOObSFnhKvXKLjlGY5cy3SXBMbHkPcYtHua8wYR8LqO2bYYnsDd9qD0DJ+LlqH0ZMUkB2Cdk9q/cp1PGJWGlYYecHP87DLuWKwS+a6LhVI9TGkIUosVtLaIMsUUEz83RJFb4sSGOXtjk5DDznn9QW8ltXXMTdGQwFq1vmuiXATYenhszbvagrnbAnDyNFths4IhS1jG8237SB36nGmO3zQm5V7AMHfSrISB/8VPyY4Si7uvAV2kMWxuMhYuQbBwVx/KxbKrYjowuvJvCKaV101rWxvSeU2wDih20v+dnQKPveRNnO8AAK/ICflVVsISkd7hXcfk+SnhfxcPQTr+HQIJEW9wt5Q8WbgHk9wuR8kgXQEX6tCGpT/w=
121 158bdc8965720ca4061f8f8d806563cfc7cdb62e 0 iQIVAwUAVqBhFyBXgaxoKi1yAQLJpQ//S8kdgmVlS+CI0d2hQVGYWB/eK+tcntG+bZKLto4bvVy5d0ymlDL0x7VrJMOkwzkU1u/GaYo3L6CVEiM/JGCgB32bllrpx+KwQ0AyHswMZruo/6xrjDIYymLMEJ9yonXBZsG7pf2saYTHm3C5/ZIPkrDZSlssJHJDdeWqd75hUnx3nX8dZ4jIIxYDhtdB5/EmuEGOVlbeBHVpwfDXidSJUHJRwJvDqezUlN003sQdUvOHHtRqBrhsYEhHqPMOxDidAgCvjSfWZQKOTKaPE/gQo/BP3GU++Fg55jBz+SBXpdfQJI2Gd8FZfjLkhFa9vTTTcd10YCd4CZbYLpj/4R2xWj1U4oTVEFa6d+AA5Yyu8xG53XSCCPyzfagyuyfLqsaq5r1qDZO/Mh5KZCTvc9xSF5KXj57mKvzMDpiNeQcamGmsV4yXxymKJKGMQvbnzqp+ItIdbnfk38Nuac8rqNnGmFYwMIPa50680vSZT/NhrlPJ8FVTJlfHtSUZbdjPpsqw7BgjFWaVUdwgCKIGERiK7zfR0innj9rF5oVwT8EbKiaR1uVxOKnTwZzPCbdO1euNg/HutZLVQmugiLAv5Z38L3YZf5bH7zJdUydhiTI4mGn/mgncsKXoSarnnduhoYu9OsQZc9pndhxjAEuAslEIyBsLy81fR2HOhUzw5FGNgdY=
121 158bdc8965720ca4061f8f8d806563cfc7cdb62e 0 iQIVAwUAVqBhFyBXgaxoKi1yAQLJpQ//S8kdgmVlS+CI0d2hQVGYWB/eK+tcntG+bZKLto4bvVy5d0ymlDL0x7VrJMOkwzkU1u/GaYo3L6CVEiM/JGCgB32bllrpx+KwQ0AyHswMZruo/6xrjDIYymLMEJ9yonXBZsG7pf2saYTHm3C5/ZIPkrDZSlssJHJDdeWqd75hUnx3nX8dZ4jIIxYDhtdB5/EmuEGOVlbeBHVpwfDXidSJUHJRwJvDqezUlN003sQdUvOHHtRqBrhsYEhHqPMOxDidAgCvjSfWZQKOTKaPE/gQo/BP3GU++Fg55jBz+SBXpdfQJI2Gd8FZfjLkhFa9vTTTcd10YCd4CZbYLpj/4R2xWj1U4oTVEFa6d+AA5Yyu8xG53XSCCPyzfagyuyfLqsaq5r1qDZO/Mh5KZCTvc9xSF5KXj57mKvzMDpiNeQcamGmsV4yXxymKJKGMQvbnzqp+ItIdbnfk38Nuac8rqNnGmFYwMIPa50680vSZT/NhrlPJ8FVTJlfHtSUZbdjPpsqw7BgjFWaVUdwgCKIGERiK7zfR0innj9rF5oVwT8EbKiaR1uVxOKnTwZzPCbdO1euNg/HutZLVQmugiLAv5Z38L3YZf5bH7zJdUydhiTI4mGn/mgncsKXoSarnnduhoYu9OsQZc9pndhxjAEuAslEIyBsLy81fR2HOhUzw5FGNgdY=
122 2408645de650d8a29a6ce9e7dce601d8dd0d1474 0 iQIVAwUAVq/xFSBXgaxoKi1yAQLsxhAAg+E6uJCtZZOugrrFi9S6C20SRPBwHwmw22PC5z3Ufp9Vf3vqSL/+zmWI9d/yezIVcTXgM9rKCvq58sZvo4FuO2ngPx7bL9LMJ3qx0IyHUKjwa3AwrzjSzvVhNIrRoimD+lVBI/GLmoszpMICM+Nyg3D41fNJKs6YpnwwsHNJkjMwz0n2SHAShWAgIilyANNVnwnzHE68AIkB/gBkUGtrjf6xB9mXQxAv4GPco/234FAkX9xSWsM0Rx+JLLrSBXoHmIlmu9LPjC0AKn8/DDke+fj7bFaF7hdJBUYOtlYH6f7NIvyZSpw0FHl7jPxoRCtXzIV+1dZEbbIMIXzNtzPFVDYDfMhLqpTgthkZ9x0UaMaHecCUWYYBp8G/IyVS40GJodl8xnRiXUkFejbK/NDdR1f9iZS0dtiFu66cATMdb6d+MG+zW0nDKiQmBt6bwynysqn4g3SIGQFEPyEoRy0bXiefHrlkeHbdfc4zgoejx3ywcRDMGvUbpWs5C43EPu44irKXcqC695vAny3A7nZpt/XP5meDdOF67DNQPvhFdjPPbJBpSsUi2hUlZ+599wUfr3lNVzeEzHT7XApTOf6ysuGtHH3qcVHpFqQSRL1MI0f2xL13UadgTVWYrnHEis7f+ncwlWiR0ucpJB3+dQQh3NVGVo89MfbIZPkA8iil03U=
122 2408645de650d8a29a6ce9e7dce601d8dd0d1474 0 iQIVAwUAVq/xFSBXgaxoKi1yAQLsxhAAg+E6uJCtZZOugrrFi9S6C20SRPBwHwmw22PC5z3Ufp9Vf3vqSL/+zmWI9d/yezIVcTXgM9rKCvq58sZvo4FuO2ngPx7bL9LMJ3qx0IyHUKjwa3AwrzjSzvVhNIrRoimD+lVBI/GLmoszpMICM+Nyg3D41fNJKs6YpnwwsHNJkjMwz0n2SHAShWAgIilyANNVnwnzHE68AIkB/gBkUGtrjf6xB9mXQxAv4GPco/234FAkX9xSWsM0Rx+JLLrSBXoHmIlmu9LPjC0AKn8/DDke+fj7bFaF7hdJBUYOtlYH6f7NIvyZSpw0FHl7jPxoRCtXzIV+1dZEbbIMIXzNtzPFVDYDfMhLqpTgthkZ9x0UaMaHecCUWYYBp8G/IyVS40GJodl8xnRiXUkFejbK/NDdR1f9iZS0dtiFu66cATMdb6d+MG+zW0nDKiQmBt6bwynysqn4g3SIGQFEPyEoRy0bXiefHrlkeHbdfc4zgoejx3ywcRDMGvUbpWs5C43EPu44irKXcqC695vAny3A7nZpt/XP5meDdOF67DNQPvhFdjPPbJBpSsUi2hUlZ+599wUfr3lNVzeEzHT7XApTOf6ysuGtHH3qcVHpFqQSRL1MI0f2xL13UadgTVWYrnHEis7f+ncwlWiR0ucpJB3+dQQh3NVGVo89MfbIZPkA8iil03U=
123 b698abf971e7377d9b7ec7fc8c52df45255b0329 0 iQIVAwUAVrJ4YCBXgaxoKi1yAQJsKw/+JHSR0bIyarO4/VilFwsYxCprOnPxmUdS4qc4yjvpbf7Dqqr/OnOHJA29LrMoqWqsHgREepemjqiNindwNtlZec+KgmbF08ihSBBpls96UTTYTcytKRkkbrB+FhwB0iDl/o8RgGPniyG6M7gOp6p8pXQVRCOToIY1B/G0rtpkcU1N3GbiZntO5Fm/LPAVIE74VaDsamMopQ/wEB8qiERngX/M8SjO1ZSaVNW6KjRUsarLXQB9ziVJBolK/WnQsDwEeuWU2udpjBiOHnFC6h84uBpc8rLGhr419bKMJcjgl+0sl2zHGPY2edQYuJqVjVENzf4zzZA+xPgKw3GrSTpd37PEnGU/fufdJ0X+pp3kvmO1cV3TsvVMTCn7NvS6+w8SGdHdwKQQwelYI6vmJnjuOCATbafJiHMaOQ0GVYYk6PPoGrYcQ081x6dStCMaHIPOV1Wirwd2wq+SN9Ql8H6njftBf5Sa5tVWdW/zrhsltMsdZYZagZ/oFT3t83exL0rgZ96bZFs0j3HO3APELygIVuQ6ybPsFyToMDbURNDvr7ZqPKhQkkdHIUMqEez5ReuVgpbO9CWV/yWpB1/ZCpjNBZyDvw05kG2mOoC7AbHc8aLUS/8DetAmhwyb48LW4qjfUkO7RyxVSxqdnaBOMlsg1wsP2S+SlkZKsDHjcquZJ5U=
123 b698abf971e7377d9b7ec7fc8c52df45255b0329 0 iQIVAwUAVrJ4YCBXgaxoKi1yAQJsKw/+JHSR0bIyarO4/VilFwsYxCprOnPxmUdS4qc4yjvpbf7Dqqr/OnOHJA29LrMoqWqsHgREepemjqiNindwNtlZec+KgmbF08ihSBBpls96UTTYTcytKRkkbrB+FhwB0iDl/o8RgGPniyG6M7gOp6p8pXQVRCOToIY1B/G0rtpkcU1N3GbiZntO5Fm/LPAVIE74VaDsamMopQ/wEB8qiERngX/M8SjO1ZSaVNW6KjRUsarLXQB9ziVJBolK/WnQsDwEeuWU2udpjBiOHnFC6h84uBpc8rLGhr419bKMJcjgl+0sl2zHGPY2edQYuJqVjVENzf4zzZA+xPgKw3GrSTpd37PEnGU/fufdJ0X+pp3kvmO1cV3TsvVMTCn7NvS6+w8SGdHdwKQQwelYI6vmJnjuOCATbafJiHMaOQ0GVYYk6PPoGrYcQ081x6dStCMaHIPOV1Wirwd2wq+SN9Ql8H6njftBf5Sa5tVWdW/zrhsltMsdZYZagZ/oFT3t83exL0rgZ96bZFs0j3HO3APELygIVuQ6ybPsFyToMDbURNDvr7ZqPKhQkkdHIUMqEez5ReuVgpbO9CWV/yWpB1/ZCpjNBZyDvw05kG2mOoC7AbHc8aLUS/8DetAmhwyb48LW4qjfUkO7RyxVSxqdnaBOMlsg1wsP2S+SlkZKsDHjcquZJ5U=
124 d493d64757eb45ada99fcb3693e479a51b7782da 0 iQIVAwUAVtYt4SBXgaxoKi1yAQL6TQ/9FzYE/xOSC2LYqPdPjCXNjGuZdN1WMf/8fUMYT83NNOoLEBGx37C0bAxgD4/P03FwYMuP37IjIcX8vN6fWvtG9Oo0o2n/oR3SKjpsheh2zxhAFX3vXhFD4U18wCz/DnM0O1qGJwJ49kk/99WNgDWeW4n9dMzTFpcaeZBCu1REbZQS40Z+ArXTDCr60g5TLN1XR1WKEzQJvF71rvaE6P8d3GLoGobTIJMLi5UnMwGsnsv2/EIPrWHQiAY9ZEnYq6deU/4RMh9c7afZie9I+ycIA/qVH6vXNt3/a2BP3Frmv8IvKPzqwnoWmIUamew9lLf1joD5joBy8Yu+qMW0/s6DYUGQ4Slk9qIfn6wh4ySgT/7FJUMcayx9ONDq7920RjRc+XFpD8B3Zhj2mM+0g9At1FgX2w2Gkf957oz2nlgTVh9sdPvP6UvWzhqszPMpdG5Vt0oc5vuyobW333qSkufCxi5gmH7do1DIzErMcy8b6IpZUDeQ/dakKwLQpZVVPF15IrNa/zsOW55SrGrL8/ErM/mXNQBBAqvRsOLq2njFqK2JaoG6biH21DMjHVZFw2wBRoLQxbOppfz2/e3mNkNy9HjgJTW3+0iHWvRzMSjwRbk9BlbkmH6kG5163ElHq3Ft3uuQyZBL9I5SQxlHi9s/CV0YSTYthpWR3ChKIMoqBQ0=
124 d493d64757eb45ada99fcb3693e479a51b7782da 0 iQIVAwUAVtYt4SBXgaxoKi1yAQL6TQ/9FzYE/xOSC2LYqPdPjCXNjGuZdN1WMf/8fUMYT83NNOoLEBGx37C0bAxgD4/P03FwYMuP37IjIcX8vN6fWvtG9Oo0o2n/oR3SKjpsheh2zxhAFX3vXhFD4U18wCz/DnM0O1qGJwJ49kk/99WNgDWeW4n9dMzTFpcaeZBCu1REbZQS40Z+ArXTDCr60g5TLN1XR1WKEzQJvF71rvaE6P8d3GLoGobTIJMLi5UnMwGsnsv2/EIPrWHQiAY9ZEnYq6deU/4RMh9c7afZie9I+ycIA/qVH6vXNt3/a2BP3Frmv8IvKPzqwnoWmIUamew9lLf1joD5joBy8Yu+qMW0/s6DYUGQ4Slk9qIfn6wh4ySgT/7FJUMcayx9ONDq7920RjRc+XFpD8B3Zhj2mM+0g9At1FgX2w2Gkf957oz2nlgTVh9sdPvP6UvWzhqszPMpdG5Vt0oc5vuyobW333qSkufCxi5gmH7do1DIzErMcy8b6IpZUDeQ/dakKwLQpZVVPF15IrNa/zsOW55SrGrL8/ErM/mXNQBBAqvRsOLq2njFqK2JaoG6biH21DMjHVZFw2wBRoLQxbOppfz2/e3mNkNy9HjgJTW3+0iHWvRzMSjwRbk9BlbkmH6kG5163ElHq3Ft3uuQyZBL9I5SQxlHi9s/CV0YSTYthpWR3ChKIMoqBQ0=
125 ae279d4a19e9683214cbd1fe8298cf0b50571432 0 iQIVAwUAVvqzViBXgaxoKi1yAQKUCxAAtctMD3ydbe+li3iYjhY5qT0wyHwPr9fcLqsQUJ4ZtD4sK3oxCRZFWFxNBk5bIIyiwusSEJPiPddoQ7NljSZlYDI0HR3R4vns55fmDwPG07Ykf7aSyqr+c2ppCGzn2/2ID476FNtzKqjF+LkVyadgI9vgZk5S4BgdSlfSRBL+1KtB1BlF5etIZnc5U9qs1uqzZJc06xyyF8HlrmMZkAvRUbsx/JzA5LgzZ2WzueaxZgYzYjDk0nPLgyPPBj0DVyWXnW/kdRNmKHNbaZ9aZlWmdPCEoq5iBm71d7Xoa61shmeuVZWvxHNqXdjVMHVeT61cRxjdfxTIkJwvlRGwpy7V17vTgzWFxw6QJpmr7kupRo3idsDydLDPHGUsxP3uMZFsp6+4rEe6qbafjNajkRyiw7kVGCxboOFN0rLVJPZwZGksEIkw58IHcPhZNT1bHHocWOA/uHJTAynfKsAdv/LDdGKcZWUCFOzlokw54xbPvdrBtEOnYNp15OY01IAJd2FCUki5WHvhELUggTjfank1Tc3/Rt1KrGOFhg80CWq6eMiuiWkHGvYq3fjNLbgjl3JJatUFoB+cX1ulDOGsLJEXQ4v5DNHgel0o2H395owNlStksSeW1UBVk0hUK/ADtVUYKAPEIFiboh1iDpEOl40JVnYdsGz3w5FLj2w+16/1vWs=
125 ae279d4a19e9683214cbd1fe8298cf0b50571432 0 iQIVAwUAVvqzViBXgaxoKi1yAQKUCxAAtctMD3ydbe+li3iYjhY5qT0wyHwPr9fcLqsQUJ4ZtD4sK3oxCRZFWFxNBk5bIIyiwusSEJPiPddoQ7NljSZlYDI0HR3R4vns55fmDwPG07Ykf7aSyqr+c2ppCGzn2/2ID476FNtzKqjF+LkVyadgI9vgZk5S4BgdSlfSRBL+1KtB1BlF5etIZnc5U9qs1uqzZJc06xyyF8HlrmMZkAvRUbsx/JzA5LgzZ2WzueaxZgYzYjDk0nPLgyPPBj0DVyWXnW/kdRNmKHNbaZ9aZlWmdPCEoq5iBm71d7Xoa61shmeuVZWvxHNqXdjVMHVeT61cRxjdfxTIkJwvlRGwpy7V17vTgzWFxw6QJpmr7kupRo3idsDydLDPHGUsxP3uMZFsp6+4rEe6qbafjNajkRyiw7kVGCxboOFN0rLVJPZwZGksEIkw58IHcPhZNT1bHHocWOA/uHJTAynfKsAdv/LDdGKcZWUCFOzlokw54xbPvdrBtEOnYNp15OY01IAJd2FCUki5WHvhELUggTjfank1Tc3/Rt1KrGOFhg80CWq6eMiuiWkHGvYq3fjNLbgjl3JJatUFoB+cX1ulDOGsLJEXQ4v5DNHgel0o2H395owNlStksSeW1UBVk0hUK/ADtVUYKAPEIFiboh1iDpEOl40JVnYdsGz3w5FLj2w+16/1vWs=
126 740156eedf2c450aee58b1a90b0e826f47c5da64 0 iQIVAwUAVxLGMCBXgaxoKi1yAQLhIg/8DDX+sCz7LmqO47/FfTo+OqGR+bTTqpfK3WebitL0Z6hbXPj7s45jijqIFGqKgMPqS5oom1xeuGTPHdYA0NNoc/mxSCuNLfuXYolpNWPN71HeSDRV9SnhMThG5HSxI+P0Ye4rbsCHrVV+ib1rV81QE2kZ9aZsJd0HnGd512xJ+2ML7AXweM/4lcLmMthN+oi/dv1OGLzfckrcr/fEATCLZt55eO7idx11J1Fk4ptQ6dQ/bKznlD4hneyy1HMPsGxw+bCXrMF2C/nUiRLHdKgGqZ+cDq6loQRfFlQoIhfoEnWC424qbjH4rvHgkZHqC59Oi/ti9Hi75oq9Tb79yzlCY/fGsdrlJpEzrTQdHFMHUoO9CC+JYObXHRo3ALnC5350ZBKxlkdpmucrHTgcDabfhRlx9vDxP4RDopm2hAjk2LJH7bdxnGEyZYkTOZ3hXKnVpt2hUQb4jyzzC9Kl47TFpPKNVKI+NLqRRZAIdXXiy24KD7WzzE6L0NNK0/IeqKBENLL8I1PmDQ6XmYTQVhTuad1jjm2PZDyGiXmJFZO1O/NGecVTvVynKsDT6XhEvzyEtjXqD98rrhbeMHTcmNSwwJMDvm9ws0075sLQyq2EYFG6ECWFypdA/jfumTmxOTkMtuy/V1Gyq7YJ8YaksZ7fXNY9VuJFP72grmlXc6Dvpr4=
126 740156eedf2c450aee58b1a90b0e826f47c5da64 0 iQIVAwUAVxLGMCBXgaxoKi1yAQLhIg/8DDX+sCz7LmqO47/FfTo+OqGR+bTTqpfK3WebitL0Z6hbXPj7s45jijqIFGqKgMPqS5oom1xeuGTPHdYA0NNoc/mxSCuNLfuXYolpNWPN71HeSDRV9SnhMThG5HSxI+P0Ye4rbsCHrVV+ib1rV81QE2kZ9aZsJd0HnGd512xJ+2ML7AXweM/4lcLmMthN+oi/dv1OGLzfckrcr/fEATCLZt55eO7idx11J1Fk4ptQ6dQ/bKznlD4hneyy1HMPsGxw+bCXrMF2C/nUiRLHdKgGqZ+cDq6loQRfFlQoIhfoEnWC424qbjH4rvHgkZHqC59Oi/ti9Hi75oq9Tb79yzlCY/fGsdrlJpEzrTQdHFMHUoO9CC+JYObXHRo3ALnC5350ZBKxlkdpmucrHTgcDabfhRlx9vDxP4RDopm2hAjk2LJH7bdxnGEyZYkTOZ3hXKnVpt2hUQb4jyzzC9Kl47TFpPKNVKI+NLqRRZAIdXXiy24KD7WzzE6L0NNK0/IeqKBENLL8I1PmDQ6XmYTQVhTuad1jjm2PZDyGiXmJFZO1O/NGecVTvVynKsDT6XhEvzyEtjXqD98rrhbeMHTcmNSwwJMDvm9ws0075sLQyq2EYFG6ECWFypdA/jfumTmxOTkMtuy/V1Gyq7YJ8YaksZ7fXNY9VuJFP72grmlXc6Dvpr4=
127 f85de28eae32e7d3064b1a1321309071bbaaa069 0 iQIVAwUAVyZQaiBXgaxoKi1yAQJhCQ//WrRZ55k3VI/OgY+I/HvgFHOC0sbhe207Kedxvy00a3AtXM6wa5E95GNX04QxUfTWUf5ZHDfEgj0/mQywNrH1oJG47iPZSs+qXNLqtgAaXtrih6r4/ruUwFCRFxqK9mkhjG61SKicw3Q7uGva950g6ZUE5BsZ7XJWgoDcJzWKR+AH992G6H//Fhi4zFQAmB34++sm80wV6wMxVKA/qhQzetooTR2x9qrHpvCKMzKllleJe48yzPLJjQoaaVgXCDav0eIePFNw0WvVSldOEp/ADDdTGa65qsC1rO2BB1Cu5+frJ/vUoo0PwIgqgD6p2i41hfIKvkp6130TxmRVxUx+ma8gBYEpPIabV0flLU72gq8lMlGBBSnQ+fcZsfs/Ug0xRN0tzkEScmZFiDxRGk0y7IalXzv6irwOyC2fZCajXGJDzkROQXWMgy9eKkwuFhZBmPVYtrATSq3jHLVmJg5vfdeiVzA6NKxAgGm2z8AsRrijKK8WRqFYiH6xcWKG5u+FroPQdKa0nGCkPSTH3tvC6fAHTVm7JeXch5QE/LiS9Y575pM2PeIP+k+Fr1ugK0AEvYJAXa5UIIcdszPyI+TwPTtWaQ83X99qGAdmRWLvSYjqevOVr7F/fhO3XKFXRCcHA3EzVYnG7nWiVACYF3H2UgN4PWjStbx/Qhhdi9xAuks=
127 f85de28eae32e7d3064b1a1321309071bbaaa069 0 iQIVAwUAVyZQaiBXgaxoKi1yAQJhCQ//WrRZ55k3VI/OgY+I/HvgFHOC0sbhe207Kedxvy00a3AtXM6wa5E95GNX04QxUfTWUf5ZHDfEgj0/mQywNrH1oJG47iPZSs+qXNLqtgAaXtrih6r4/ruUwFCRFxqK9mkhjG61SKicw3Q7uGva950g6ZUE5BsZ7XJWgoDcJzWKR+AH992G6H//Fhi4zFQAmB34++sm80wV6wMxVKA/qhQzetooTR2x9qrHpvCKMzKllleJe48yzPLJjQoaaVgXCDav0eIePFNw0WvVSldOEp/ADDdTGa65qsC1rO2BB1Cu5+frJ/vUoo0PwIgqgD6p2i41hfIKvkp6130TxmRVxUx+ma8gBYEpPIabV0flLU72gq8lMlGBBSnQ+fcZsfs/Ug0xRN0tzkEScmZFiDxRGk0y7IalXzv6irwOyC2fZCajXGJDzkROQXWMgy9eKkwuFhZBmPVYtrATSq3jHLVmJg5vfdeiVzA6NKxAgGm2z8AsRrijKK8WRqFYiH6xcWKG5u+FroPQdKa0nGCkPSTH3tvC6fAHTVm7JeXch5QE/LiS9Y575pM2PeIP+k+Fr1ugK0AEvYJAXa5UIIcdszPyI+TwPTtWaQ83X99qGAdmRWLvSYjqevOVr7F/fhO3XKFXRCcHA3EzVYnG7nWiVACYF3H2UgN4PWjStbx/Qhhdi9xAuks=
128 a56296f55a5e1038ea5016dace2076b693c28a56 0 iQIVAwUAVyZarCBXgaxoKi1yAQL87g/8D7whM3e08HVGDHHEkVUgqLIfueVy1mx0AkRvelmZmwaocFNGpZTd3AjSwy6qXbRNZFXrWU85JJvQCi3PSo/8bK43kwqLJ4lv+Hv2zVTvz30vbLWTSndH3oVRu38lIA7b5K9J4y50pMCwjKLG9iyp+aQG4RBz76fJMlhXy0gu38A8JZVKEeAnQCbtzxKXBzsC8k0/ku/bEQEoo9D4AAGlVTbl5AsHMp3Z6NWu7kEHAX/52/VKU2I0LxYqRxoL1tjTVGkAQfkOHz1gOhLXUgGSYmA9Fb265AYj9cnGWCfyNonlE0Rrk2kAsrjBTGiLyb8WvK/TZmRo4ZpNukzenS9UuAOKxA22Kf9+oN9kKBu1HnwqusYDH9pto1WInCZKV1al7DMBXbGFcnyTXk2xuiTGhVRG5LzCO2QMByBLXiYl77WqqJnzxK3v5lAc/immJl5qa3ATUlTnVBjAs+6cbsbCoY6sjXCT0ClndA9+iZZ1TjPnmLrSeFh5AoE8WHmnFV6oqGN4caX6wiIW5vO+x5Q2ruSsDrwXosXIYzm+0KYKRq9O+MaTwR44Dvq3/RyeIu/cif/Nc7B8bR5Kf7OiRf2T5u97MYAomwGcQfXqgUfm6y7D3Yg+IdAdAJKitxhRPsqqdxIuteXMvOvwukXNDiWP1zsKoYLI37EcwzvbGLUlZvg=
128 a56296f55a5e1038ea5016dace2076b693c28a56 0 iQIVAwUAVyZarCBXgaxoKi1yAQL87g/8D7whM3e08HVGDHHEkVUgqLIfueVy1mx0AkRvelmZmwaocFNGpZTd3AjSwy6qXbRNZFXrWU85JJvQCi3PSo/8bK43kwqLJ4lv+Hv2zVTvz30vbLWTSndH3oVRu38lIA7b5K9J4y50pMCwjKLG9iyp+aQG4RBz76fJMlhXy0gu38A8JZVKEeAnQCbtzxKXBzsC8k0/ku/bEQEoo9D4AAGlVTbl5AsHMp3Z6NWu7kEHAX/52/VKU2I0LxYqRxoL1tjTVGkAQfkOHz1gOhLXUgGSYmA9Fb265AYj9cnGWCfyNonlE0Rrk2kAsrjBTGiLyb8WvK/TZmRo4ZpNukzenS9UuAOKxA22Kf9+oN9kKBu1HnwqusYDH9pto1WInCZKV1al7DMBXbGFcnyTXk2xuiTGhVRG5LzCO2QMByBLXiYl77WqqJnzxK3v5lAc/immJl5qa3ATUlTnVBjAs+6cbsbCoY6sjXCT0ClndA9+iZZ1TjPnmLrSeFh5AoE8WHmnFV6oqGN4caX6wiIW5vO+x5Q2ruSsDrwXosXIYzm+0KYKRq9O+MaTwR44Dvq3/RyeIu/cif/Nc7B8bR5Kf7OiRf2T5u97MYAomwGcQfXqgUfm6y7D3Yg+IdAdAJKitxhRPsqqdxIuteXMvOvwukXNDiWP1zsKoYLI37EcwzvbGLUlZvg=
129 aaabed77791a75968a12b8c43ad263631a23ee81 0 iQIVAwUAVzpH4CBXgaxoKi1yAQLm5A/9GUYv9CeIepjcdWSBAtNhCBJcqgk2cBcV0XaeQomfxqYWfbW2fze6eE+TrXPKTX1ajycgqquMyo3asQolhHXwasv8+5CQxowjGfyVg7N/kyyjgmJljI+rCi74VfnsEhvG/J4GNr8JLVQmSICfALqQjw7XN8doKthYhwOfIY2vY419613v4oeBQXSsItKC/tfKw9lYvlk4qJKDffJQFyAekgv43ovWqHNkl4LaR6ubtjOsxCnxHfr7OtpX3muM9MLT/obBax5I3EsmiDTQBOjbvI6TcLczs5tVCnTa1opQsPUcEmdA4WpUEiTnLl9lk9le/BIImfYfEP33oVYmubRlKhJYnUiu89ao9L+48FBoqCY88HqbjQI1GO6icfRJN/+NLVeE9wubltbWFETH6e2Q+Ex4+lkul1tQMLPcPt10suMHnEo3/FcOTPt6/DKeMpsYgckHSJq5KzTg632xifyySmb9qkpdGGpY9lRal6FHw3rAhRBqucMgxso4BwC51h04RImtCUQPoA3wpb4BvCHba/thpsUFnHefOvsu3ei4JyHXZK84LPwOj31PcucNFdGDTW6jvKrF1vVUIVS9uMJkJXPu0V4i/oEQSUKifJZivROlpvj1eHy3KeMtjq2kjGyXY2KdzxpT8wX/oYJhCtm1XWMui5f24XBjE6xOcjjm8k4=
129 aaabed77791a75968a12b8c43ad263631a23ee81 0 iQIVAwUAVzpH4CBXgaxoKi1yAQLm5A/9GUYv9CeIepjcdWSBAtNhCBJcqgk2cBcV0XaeQomfxqYWfbW2fze6eE+TrXPKTX1ajycgqquMyo3asQolhHXwasv8+5CQxowjGfyVg7N/kyyjgmJljI+rCi74VfnsEhvG/J4GNr8JLVQmSICfALqQjw7XN8doKthYhwOfIY2vY419613v4oeBQXSsItKC/tfKw9lYvlk4qJKDffJQFyAekgv43ovWqHNkl4LaR6ubtjOsxCnxHfr7OtpX3muM9MLT/obBax5I3EsmiDTQBOjbvI6TcLczs5tVCnTa1opQsPUcEmdA4WpUEiTnLl9lk9le/BIImfYfEP33oVYmubRlKhJYnUiu89ao9L+48FBoqCY88HqbjQI1GO6icfRJN/+NLVeE9wubltbWFETH6e2Q+Ex4+lkul1tQMLPcPt10suMHnEo3/FcOTPt6/DKeMpsYgckHSJq5KzTg632xifyySmb9qkpdGGpY9lRal6FHw3rAhRBqucMgxso4BwC51h04RImtCUQPoA3wpb4BvCHba/thpsUFnHefOvsu3ei4JyHXZK84LPwOj31PcucNFdGDTW6jvKrF1vVUIVS9uMJkJXPu0V4i/oEQSUKifJZivROlpvj1eHy3KeMtjq2kjGyXY2KdzxpT8wX/oYJhCtm1XWMui5f24XBjE6xOcjjm8k4=
130 a9764ab80e11bcf6a37255db7dd079011f767c6c 0 iQIVAwUAV09KHyBXgaxoKi1yAQJBWg/+OywRrqU+zvnL1tHJ95PgatsF7S4ZAHZFR098+oCjUDtKpvnm71o2TKiY4D5cckyD2KNwLWg/qW6V+5+2EYU0Y/ViwPVcngib/ZeJP+Nr44TK3YZMRmfFuUEEzA7sZ2r2Gm8eswv//W79I0hXJeFd/o6FgLnn7AbOjcOn3IhWdGAP6jUHv9zyJigQv6K9wgyvAnK1RQE+2CgMcoyeqao/zs23IPXI6XUHOwfrQ7XrQ83+ciMqN7XNRx+TKsUQoYeUew4AanoDSMPAQ4kIudsP5tOgKeLRPmHX9zg6Y5S1nTpLRNdyAxuNuyZtkQxDYcG5Hft/SIx27tZUo3gywHL2U+9RYD2nvXqaWzT3sYB2sPBOiq7kjHRgvothkXemAFsbq2nKFrN0PRua9WG4l3ny0xYmDFPlJ/s0E9XhmQaqy+uXtVbA2XdLEvE6pQ0YWbHEKMniW26w6LJkx4IV6RX/7Kpq7byw/bW65tu/BzgISKau5FYLY4CqZJH7f8QBg3XWpzB91AR494tdsD+ugM45wrY/6awGQx9CY5SAzGqTyFuSFQxgB2rBurb01seZPf8nqG8V13UYXfX/O3/WMOBMr7U/RVqmAA0ZMYOyEwfVUmHqrFjkxpXX+JdNKRiA1GJp5sdRpCxSeXdQ/Ni6AAGZV2IyRb4G4Y++1vP4yPBalas=
130 a9764ab80e11bcf6a37255db7dd079011f767c6c 0 iQIVAwUAV09KHyBXgaxoKi1yAQJBWg/+OywRrqU+zvnL1tHJ95PgatsF7S4ZAHZFR098+oCjUDtKpvnm71o2TKiY4D5cckyD2KNwLWg/qW6V+5+2EYU0Y/ViwPVcngib/ZeJP+Nr44TK3YZMRmfFuUEEzA7sZ2r2Gm8eswv//W79I0hXJeFd/o6FgLnn7AbOjcOn3IhWdGAP6jUHv9zyJigQv6K9wgyvAnK1RQE+2CgMcoyeqao/zs23IPXI6XUHOwfrQ7XrQ83+ciMqN7XNRx+TKsUQoYeUew4AanoDSMPAQ4kIudsP5tOgKeLRPmHX9zg6Y5S1nTpLRNdyAxuNuyZtkQxDYcG5Hft/SIx27tZUo3gywHL2U+9RYD2nvXqaWzT3sYB2sPBOiq7kjHRgvothkXemAFsbq2nKFrN0PRua9WG4l3ny0xYmDFPlJ/s0E9XhmQaqy+uXtVbA2XdLEvE6pQ0YWbHEKMniW26w6LJkx4IV6RX/7Kpq7byw/bW65tu/BzgISKau5FYLY4CqZJH7f8QBg3XWpzB91AR494tdsD+ugM45wrY/6awGQx9CY5SAzGqTyFuSFQxgB2rBurb01seZPf8nqG8V13UYXfX/O3/WMOBMr7U/RVqmAA0ZMYOyEwfVUmHqrFjkxpXX+JdNKRiA1GJp5sdRpCxSeXdQ/Ni6AAGZV2IyRb4G4Y++1vP4yPBalas=
131 26a5d605b8683a292bb89aea11f37a81b06ac016 0 iQIVAwUAV3bOsSBXgaxoKi1yAQLiDg//fxmcNpTUedsXqEwNdGFJsJ2E25OANgyv1saZHNfbYFWXIR8g4nyjNaj2SjtXF0wzOq5aHlMWXjMZPOT6pQBdTnOYDdgv+O8DGpgHs5x/f+uuxtpVkdxR6uRP0/ImlTEtDix8VQiN3nTu5A0N3C7E2y+D1JIIyTp6vyjzxvGQTY0MD/qgB55Dn6khx8c3phDtMkzmVEwL4ItJxVRVNw1m+2FOXHu++hJEruJdeMV0CKOV6LVbXHho+yt3jQDKhlIgJ65EPLKrf+yRalQtSWpu7y/vUMcEUde9XeQ5x05ebCiI4MkJ0ULQro/Bdx9vBHkAstUC7D+L5y45ZnhHjOwxz9c3GQMZQt1HuyORqbBhf9hvOkUQ2GhlDHc5U04nBe0VhEoCw9ra54n+AgUyqWr4CWimSW6pMTdquCzAAbcJWgdNMwDHrMalCYHhJksKFARKq3uSTR1Noz7sOCSIEQvOozawKSQfOwGxn/5bNepKh4uIRelC1uEDoqculqCLgAruzcMNIMndNVYaJ09IohJzA9jVApa+SZVPAeREg71lnS3d8jaWh1Lu5JFlAAKQeKGVJmNm40Y3HBjtHQDrI67TT59oDAhjo420Wf9VFCaj2k0weYBLWSeJhfUZ5x3PVpAHUvP/rnHPwNYyY0wVoQEvM/bnQdcpICmKhqcK+vKjDrM=
131 26a5d605b8683a292bb89aea11f37a81b06ac016 0 iQIVAwUAV3bOsSBXgaxoKi1yAQLiDg//fxmcNpTUedsXqEwNdGFJsJ2E25OANgyv1saZHNfbYFWXIR8g4nyjNaj2SjtXF0wzOq5aHlMWXjMZPOT6pQBdTnOYDdgv+O8DGpgHs5x/f+uuxtpVkdxR6uRP0/ImlTEtDix8VQiN3nTu5A0N3C7E2y+D1JIIyTp6vyjzxvGQTY0MD/qgB55Dn6khx8c3phDtMkzmVEwL4ItJxVRVNw1m+2FOXHu++hJEruJdeMV0CKOV6LVbXHho+yt3jQDKhlIgJ65EPLKrf+yRalQtSWpu7y/vUMcEUde9XeQ5x05ebCiI4MkJ0ULQro/Bdx9vBHkAstUC7D+L5y45ZnhHjOwxz9c3GQMZQt1HuyORqbBhf9hvOkUQ2GhlDHc5U04nBe0VhEoCw9ra54n+AgUyqWr4CWimSW6pMTdquCzAAbcJWgdNMwDHrMalCYHhJksKFARKq3uSTR1Noz7sOCSIEQvOozawKSQfOwGxn/5bNepKh4uIRelC1uEDoqculqCLgAruzcMNIMndNVYaJ09IohJzA9jVApa+SZVPAeREg71lnS3d8jaWh1Lu5JFlAAKQeKGVJmNm40Y3HBjtHQDrI67TT59oDAhjo420Wf9VFCaj2k0weYBLWSeJhfUZ5x3PVpAHUvP/rnHPwNYyY0wVoQEvM/bnQdcpICmKhqcK+vKjDrM=
132 519bb4f9d3a47a6e83c2b414d58811ed38f503c2 0 iQIVAwUAV42tNyBXgaxoKi1yAQI/Iw//V0NtxpVD4sClotAwffBVW42Uv+SG+07CJoOuFYnmHZv/plOzXuuJlmm95L00/qyRCCTUyAGxK/eP5cAKP2V99ln6rNhh8gpgvmZlnYjU3gqFv8tCQ+fkwgRiWmgKjRL6/bK9FY5cO7ATLVu3kCkFd8CEgzlAaUqBfkNFxZxLDLvKqRlhXxVXhKjvkKg5DZ6eJqRQY7w3UqqR+sF1rMLtVyt490Wqv7YQKwcvY7MEKTyH4twGLx/RhBpBi+GccVKvWC011ffjSjxqAfQqrrSVt0Ld1Khj2/p1bDDYpTgtdDgCzclSXWEQpmSdFRBF5wYs/pDMUreI/E6mlWkB4hfZZk1NBRPRWYikXwnhU3ziubCGesZDyBYLrK1vT+tf6giseo22YQmDnOftbS999Pcn04cyCafeFuOjkubYaINB25T20GS5Wb4a0nHPRAOOVxzk/m/arwYgF0ZZZDDvJ48TRMDf3XOc1jc5qZ7AN/OQKbvh2B08vObnnPm3lmBY1qOnhwzJxpNiq+Z/ypokGXQkGBfKUo7rWHJy5iXLb3Biv9AhxY9d5pSTjBmTAYJEic3q03ztzlnfMyi+C13+YxFAbSSNGBP8Hejkkz0NvmB1TBuCKpnZA8spxY5rhZ/zMx+cCw8hQvWHHDUURps7SQvZEfrJSCGJFPDHL3vbfK+LNwI=
132 519bb4f9d3a47a6e83c2b414d58811ed38f503c2 0 iQIVAwUAV42tNyBXgaxoKi1yAQI/Iw//V0NtxpVD4sClotAwffBVW42Uv+SG+07CJoOuFYnmHZv/plOzXuuJlmm95L00/qyRCCTUyAGxK/eP5cAKP2V99ln6rNhh8gpgvmZlnYjU3gqFv8tCQ+fkwgRiWmgKjRL6/bK9FY5cO7ATLVu3kCkFd8CEgzlAaUqBfkNFxZxLDLvKqRlhXxVXhKjvkKg5DZ6eJqRQY7w3UqqR+sF1rMLtVyt490Wqv7YQKwcvY7MEKTyH4twGLx/RhBpBi+GccVKvWC011ffjSjxqAfQqrrSVt0Ld1Khj2/p1bDDYpTgtdDgCzclSXWEQpmSdFRBF5wYs/pDMUreI/E6mlWkB4hfZZk1NBRPRWYikXwnhU3ziubCGesZDyBYLrK1vT+tf6giseo22YQmDnOftbS999Pcn04cyCafeFuOjkubYaINB25T20GS5Wb4a0nHPRAOOVxzk/m/arwYgF0ZZZDDvJ48TRMDf3XOc1jc5qZ7AN/OQKbvh2B08vObnnPm3lmBY1qOnhwzJxpNiq+Z/ypokGXQkGBfKUo7rWHJy5iXLb3Biv9AhxY9d5pSTjBmTAYJEic3q03ztzlnfMyi+C13+YxFAbSSNGBP8Hejkkz0NvmB1TBuCKpnZA8spxY5rhZ/zMx+cCw8hQvWHHDUURps7SQvZEfrJSCGJFPDHL3vbfK+LNwI=
133 299546f84e68dbb9bd026f0f3a974ce4bdb93686 0 iQIcBAABCAAGBQJXn3rFAAoJELnJ3IJKpb3VmZoQAK0cdOfi/OURglnN0vYYGwdvSXTPpZauPEYEpwML3dW1j6HRnl5L+H8D8vlYzahK95X4+NNBhqtyyB6wmIVI0NkYfXfd6ACntJE/EnTdLIHIP2NAAoVsggIjiNr26ubRegaD5ya63Ofxz+Yq5iRsUUfHet7o+CyFhExyzdu+Vcz1/E9GztxNfTDVpC/mf+RMLwQTfHOhoTVbaamLCmGAIjw39w72X+vRMJoYNF44te6PvsfI67+6uuC0+9DjMnp5eL/hquSQ1qfks71rnWwxuiPcUDZloIueowVmt0z0sO4loSP1nZ5IP/6ZOoAzSjspqsxeay9sKP0kzSYLGsmCi29otyVSnXiKtyMCW5z5iM6k8XQcMi5mWy9RcpqlNYD7RUTn3g0+a8u7F6UEtske3/qoweJLPhtTmBNOfDNw4JXwOBSZea0QnIIjCeCc4ZGqfojPpbvcA4rkRpxI23YoMrT2v/kp4wgwrqK9fi8ctt8WbXpmGoAQDXWj2bWcuzj94HsAhLduFKv6sxoDz871hqjmjjnjQSU7TSNNnVzdzwqYkMB+BvhcNYxk6lcx3Aif3AayGdrWDubtU/ZRNoLzBwe6gm0udRMXBj4D/60GD6TIkYeL7HjJwfBb6Bf7qvQ6y7g0zbYG9uwBmMeduU7XchErGqQGSEyyJH3DG9OLaFOj
133 299546f84e68dbb9bd026f0f3a974ce4bdb93686 0 iQIcBAABCAAGBQJXn3rFAAoJELnJ3IJKpb3VmZoQAK0cdOfi/OURglnN0vYYGwdvSXTPpZauPEYEpwML3dW1j6HRnl5L+H8D8vlYzahK95X4+NNBhqtyyB6wmIVI0NkYfXfd6ACntJE/EnTdLIHIP2NAAoVsggIjiNr26ubRegaD5ya63Ofxz+Yq5iRsUUfHet7o+CyFhExyzdu+Vcz1/E9GztxNfTDVpC/mf+RMLwQTfHOhoTVbaamLCmGAIjw39w72X+vRMJoYNF44te6PvsfI67+6uuC0+9DjMnp5eL/hquSQ1qfks71rnWwxuiPcUDZloIueowVmt0z0sO4loSP1nZ5IP/6ZOoAzSjspqsxeay9sKP0kzSYLGsmCi29otyVSnXiKtyMCW5z5iM6k8XQcMi5mWy9RcpqlNYD7RUTn3g0+a8u7F6UEtske3/qoweJLPhtTmBNOfDNw4JXwOBSZea0QnIIjCeCc4ZGqfojPpbvcA4rkRpxI23YoMrT2v/kp4wgwrqK9fi8ctt8WbXpmGoAQDXWj2bWcuzj94HsAhLduFKv6sxoDz871hqjmjjnjQSU7TSNNnVzdzwqYkMB+BvhcNYxk6lcx3Aif3AayGdrWDubtU/ZRNoLzBwe6gm0udRMXBj4D/60GD6TIkYeL7HjJwfBb6Bf7qvQ6y7g0zbYG9uwBmMeduU7XchErGqQGSEyyJH3DG9OLaFOj
134 ccd436f7db6d5d7b9af89715179b911d031d44f1 0 iQIVAwUAV8h7F0emf/qjRqrOAQjmdhAAgYhom8fzL/YHeVLddm71ZB+pKDviKASKGSrBHY4D5Szrh/pYTedmG9IptYue5vzXpspHAaGvZN5xkwrz1/5nmnCsLA8DFaYT9qCkize6EYzxSBtA/W1S9Mv5tObinr1EX9rCSyI4HEJYE8i1IQM5h07SqUsMKDoasd4e29t6gRWg5pfOYq1kc2MTck35W9ff1Fii8S28dqbO3cLU6g5K0pT0JLCZIq7hyTNQdxHAYfebxkVl7PZrZR383IrnyotXVKFFc44qinv94T50uR4yUNYPQ8Gu0TgoGQQjBjk1Lrxot2xpgPQAy8vx+EOJgpg/yNZnYkmJZMxjDkTGVrwvXtOXZzmy2jti7PniET9hUBCU7aNHnoJJLzIf+Vb1CIRP0ypJl8GYCZx6HIYwOQH6EtcaeUqq3r+WXWv74ijIE7OApotmutM9buTvdOLdZddBzFPIjykc6cXO+W4E0kl6u9/OHtaZ3Nynh0ejBRafRWAVw2yU3T9SgQyICsmYWJCThkj14WqCJr2b7jfGlg9MkQOUG6/3f4xz2R3SgyUD8KiGsq/vdBE53zh0YA9gppLoum6AY+z61G1NhVGlrtps90txZBehuARUUz2dJC0pBMRy8XFwXMewDSIe6ATg25pHZsxHfhcalBpJncBl8pORs7oQl+GKBVxlnV4jm1pCzLU=
134 ccd436f7db6d5d7b9af89715179b911d031d44f1 0 iQIVAwUAV8h7F0emf/qjRqrOAQjmdhAAgYhom8fzL/YHeVLddm71ZB+pKDviKASKGSrBHY4D5Szrh/pYTedmG9IptYue5vzXpspHAaGvZN5xkwrz1/5nmnCsLA8DFaYT9qCkize6EYzxSBtA/W1S9Mv5tObinr1EX9rCSyI4HEJYE8i1IQM5h07SqUsMKDoasd4e29t6gRWg5pfOYq1kc2MTck35W9ff1Fii8S28dqbO3cLU6g5K0pT0JLCZIq7hyTNQdxHAYfebxkVl7PZrZR383IrnyotXVKFFc44qinv94T50uR4yUNYPQ8Gu0TgoGQQjBjk1Lrxot2xpgPQAy8vx+EOJgpg/yNZnYkmJZMxjDkTGVrwvXtOXZzmy2jti7PniET9hUBCU7aNHnoJJLzIf+Vb1CIRP0ypJl8GYCZx6HIYwOQH6EtcaeUqq3r+WXWv74ijIE7OApotmutM9buTvdOLdZddBzFPIjykc6cXO+W4E0kl6u9/OHtaZ3Nynh0ejBRafRWAVw2yU3T9SgQyICsmYWJCThkj14WqCJr2b7jfGlg9MkQOUG6/3f4xz2R3SgyUD8KiGsq/vdBE53zh0YA9gppLoum6AY+z61G1NhVGlrtps90txZBehuARUUz2dJC0pBMRy8XFwXMewDSIe6ATg25pHZsxHfhcalBpJncBl8pORs7oQl+GKBVxlnV4jm1pCzLU=
135 149433e68974eb5c63ccb03f794d8b57339a80c4 0 iQIcBAABAgAGBQJX8AfCAAoJELnJ3IJKpb3VnNAP/3umS8tohcZTr4m6DJm9u4XGr2m3FWQmjTEfimGpsOuBC8oCgsq0eAlORYcV68zDax+vQHQu3pqfPXaX+y4ZFDuz0ForNRiPJn+Q+tj1+NrOT1e8h4gH0nSK4rDxEGaa6x01fyC/xQMqN6iNfzbLLB7+WadZlyBRbHaUeZFDlPxPDf1rjDpu1vqwtOrVzSxMasRGEceiUegwsFdFMAefCq0ya/pKe9oV+GgGfR4qNrP7BfpOBcN/Po/ctkFCbLOhHbu6M7HpBSiD57BUy5lfhQQtSjzCKEVTyrWEH0ApjjXKuJzLSyq7xsHKQSOPMgGQprGehyzdCETlZOdauGrC0t9vBCr7kXEhXtycqxBC03vknA2eNeV610VX+HgO9VpCVZWHtENiArhALCcpoEsJvT29xCBYpSii/wnTpYJFT9yW8tjQCxH0zrmEZJvO1/nMINEBQFScB/nzUELn9asnghNf6vMpSGy0fSM27j87VAXCzJ5lqa6WCL/RrKgvYflow/m5AzUfMQhpqpH1vmh4ba1zZ4123lgnW4pNZDV9kmwXrEagGbWe1rnmsMzHugsECiYQyIngjWzHfpHgyEr49Uc5bMM1MlTypeHYYL4kV1jJ8Ou0SC4aV+49p8Onmb2NlVY7JKV7hqDCuZPI164YXMxhPNst4XK0/ENhoOE+8iB6
135 149433e68974eb5c63ccb03f794d8b57339a80c4 0 iQIcBAABAgAGBQJX8AfCAAoJELnJ3IJKpb3VnNAP/3umS8tohcZTr4m6DJm9u4XGr2m3FWQmjTEfimGpsOuBC8oCgsq0eAlORYcV68zDax+vQHQu3pqfPXaX+y4ZFDuz0ForNRiPJn+Q+tj1+NrOT1e8h4gH0nSK4rDxEGaa6x01fyC/xQMqN6iNfzbLLB7+WadZlyBRbHaUeZFDlPxPDf1rjDpu1vqwtOrVzSxMasRGEceiUegwsFdFMAefCq0ya/pKe9oV+GgGfR4qNrP7BfpOBcN/Po/ctkFCbLOhHbu6M7HpBSiD57BUy5lfhQQtSjzCKEVTyrWEH0ApjjXKuJzLSyq7xsHKQSOPMgGQprGehyzdCETlZOdauGrC0t9vBCr7kXEhXtycqxBC03vknA2eNeV610VX+HgO9VpCVZWHtENiArhALCcpoEsJvT29xCBYpSii/wnTpYJFT9yW8tjQCxH0zrmEZJvO1/nMINEBQFScB/nzUELn9asnghNf6vMpSGy0fSM27j87VAXCzJ5lqa6WCL/RrKgvYflow/m5AzUfMQhpqpH1vmh4ba1zZ4123lgnW4pNZDV9kmwXrEagGbWe1rnmsMzHugsECiYQyIngjWzHfpHgyEr49Uc5bMM1MlTypeHYYL4kV1jJ8Ou0SC4aV+49p8Onmb2NlVY7JKV7hqDCuZPI164YXMxhPNst4XK0/ENhoOE+8iB6
136 438173c415874f6ac653efc1099dec9c9150e90f 0 iQIVAwUAWAZ3okemf/qjRqrOAQj89xAAw/6QZ07yqvH+aZHeGQfgJ/X1Nze/hSMzkqbwGkuUOWD5ztN8+c39EXCn8JlqyLUPD7uGzhTV0299k5fGRihLIseXr0hy/cvVW16uqfeKJ/4/qL9zLS3rwSAgWbaHd1s6UQZVfGCb8V6oC1dkJxfrE9h6kugBqV97wStIRxmCpMDjsFv/zdNwsv6eEdxbiMilLn2/IbWXFOVKJzzv9iEY5Pu5McFR+nnrMyUZQhyGtVPLSkoEPsOysorfCZaVLJ6MnVaJunp9XEv94Pqx9+k+shsQvJHWkc0Nnb6uDHZYkLR5v2AbFsbJ9jDHsdr9A7qeQTiZay7PGI0uPoIrkmLya3cYbU1ADhwloAeQ/3gZLaJaKEjrXcFSsz7AZ9yq74rTwiPulF8uqZxJUodk2m/zy83HBrxxp/vgxWJ5JP2WXPtB8qKY+05umAt4rQS+fd2H/xOu2V2d5Mq1WmgknLBLC0ItaNaf91sSHtgEy22GtcvWQE7S6VWU1PoSYmOLITdJKAsmb7Eq+yKDW9nt0lOpUu2wUhBGctlgXgcWOmJP6gL6edIg66czAkVBp/fpKNl8Z/A0hhpuH7nW7GW/mzLVQnc+JW4wqUVkwlur3NRfvSt5ZyTY/SaR++nRf62h7PHIjU+f0kWQRdCcEQ0X38b8iAjeXcsOW8NCOPpm0zcz3i8=
136 438173c415874f6ac653efc1099dec9c9150e90f 0 iQIVAwUAWAZ3okemf/qjRqrOAQj89xAAw/6QZ07yqvH+aZHeGQfgJ/X1Nze/hSMzkqbwGkuUOWD5ztN8+c39EXCn8JlqyLUPD7uGzhTV0299k5fGRihLIseXr0hy/cvVW16uqfeKJ/4/qL9zLS3rwSAgWbaHd1s6UQZVfGCb8V6oC1dkJxfrE9h6kugBqV97wStIRxmCpMDjsFv/zdNwsv6eEdxbiMilLn2/IbWXFOVKJzzv9iEY5Pu5McFR+nnrMyUZQhyGtVPLSkoEPsOysorfCZaVLJ6MnVaJunp9XEv94Pqx9+k+shsQvJHWkc0Nnb6uDHZYkLR5v2AbFsbJ9jDHsdr9A7qeQTiZay7PGI0uPoIrkmLya3cYbU1ADhwloAeQ/3gZLaJaKEjrXcFSsz7AZ9yq74rTwiPulF8uqZxJUodk2m/zy83HBrxxp/vgxWJ5JP2WXPtB8qKY+05umAt4rQS+fd2H/xOu2V2d5Mq1WmgknLBLC0ItaNaf91sSHtgEy22GtcvWQE7S6VWU1PoSYmOLITdJKAsmb7Eq+yKDW9nt0lOpUu2wUhBGctlgXgcWOmJP6gL6edIg66czAkVBp/fpKNl8Z/A0hhpuH7nW7GW/mzLVQnc+JW4wqUVkwlur3NRfvSt5ZyTY/SaR++nRf62h7PHIjU+f0kWQRdCcEQ0X38b8iAjeXcsOW8NCOPpm0zcz3i8=
137 eab27446995210c334c3d06f1a659e3b9b5da769 0 iQIcBAABCAAGBQJYGNsXAAoJELnJ3IJKpb3Vf30QAK/dq5vEHEkufLGiYxxkvIyiRaswS+8jamXeHMQrdK8CuokcQYhEv9xiUI6FMIoX4Zc0xfoFCBc+X4qE+Ed9SFYWgQkDs/roJq1C1mTYA+KANMqJkDt00QZq536snFQvjCXAA5fwR/DpgGOOuGMRfvbjh7x8mPyVoPr4HDQCGFXnTYdn193HpTOqUsipzIV5OJqQ9p0sfJjwKP4ZfD0tqqdjTkNwMyJuwuRaReXFvGGCjH2PqkZE/FwQG0NJJjt0xaMUmv5U5tXHC9tEVobVV/qEslqfbH2v1YPF5d8Jmdn7F76FU5J0nTd+3rIVjYGYSt01cR6wtGnzvr/7kw9kbChw4wYhXxnmIALSd48FpA1qWjlPcAdHfUUwObxOxfqmlnBGtAQFK+p5VXCsxDZEIT9MSxscfCjyDQZpkY5S5B3PFIRg6V9bdl5a4rEt27aucuKTHj1Ok2vip4WfaIKk28YMjjzuOQRbr6Pp7mJcCC1/ERHUJdLsaQP+dy18z6XbDjX3O2JDRNYbCBexQyV/Kfrt5EOS5fXiByQUHv+PyR+9Ju6QWkkcFBfgsxq25kFl+eos4V9lxPOY5jDpw2BWu9TyHtTWkjL/YxDUGwUO9WA/WzrcT4skr9FYrFV/oEgi8MkwydC0cFICDfd6tr9upqkkr1W025Im1UBXXJ89bTVj
137 eab27446995210c334c3d06f1a659e3b9b5da769 0 iQIcBAABCAAGBQJYGNsXAAoJELnJ3IJKpb3Vf30QAK/dq5vEHEkufLGiYxxkvIyiRaswS+8jamXeHMQrdK8CuokcQYhEv9xiUI6FMIoX4Zc0xfoFCBc+X4qE+Ed9SFYWgQkDs/roJq1C1mTYA+KANMqJkDt00QZq536snFQvjCXAA5fwR/DpgGOOuGMRfvbjh7x8mPyVoPr4HDQCGFXnTYdn193HpTOqUsipzIV5OJqQ9p0sfJjwKP4ZfD0tqqdjTkNwMyJuwuRaReXFvGGCjH2PqkZE/FwQG0NJJjt0xaMUmv5U5tXHC9tEVobVV/qEslqfbH2v1YPF5d8Jmdn7F76FU5J0nTd+3rIVjYGYSt01cR6wtGnzvr/7kw9kbChw4wYhXxnmIALSd48FpA1qWjlPcAdHfUUwObxOxfqmlnBGtAQFK+p5VXCsxDZEIT9MSxscfCjyDQZpkY5S5B3PFIRg6V9bdl5a4rEt27aucuKTHj1Ok2vip4WfaIKk28YMjjzuOQRbr6Pp7mJcCC1/ERHUJdLsaQP+dy18z6XbDjX3O2JDRNYbCBexQyV/Kfrt5EOS5fXiByQUHv+PyR+9Ju6QWkkcFBfgsxq25kFl+eos4V9lxPOY5jDpw2BWu9TyHtTWkjL/YxDUGwUO9WA/WzrcT4skr9FYrFV/oEgi8MkwydC0cFICDfd6tr9upqkkr1W025Im1UBXXJ89bTVj
138 b3b1ae98f6a0e14c1e1ba806a6c18e193b6dae5c 0 iQIVAwUAWECEaEemf/qjRqrOAQjuZw/+IWJKnKOsaUMcB9ly3Fo/eskqDL6A0j69IXTJDeBDGMoyGbQU/gZyX2yc6Sw3EhwTSCXu5vKpzg3a6e8MNrC1iHqli4wJ/jPY7XtmiqTYDixdsBLNk46VfOi73ooFe08wVDSNB65xpZsrtPDSioNmQ2kSJwSHb71UlauS4xGkM74vuDpWvX5OZRSfBqMh6NjG5RwBBnS8mzA0SW2dCI2jSc5SCGIzIZpzM0xUN21xzq0YQbrk9qEsmi7ks0eowdhUjeET2wSWwhOK4jS4IfMyRO7KueUB05yHs4mChj9kNFNWtSzXKwKBQbZzwO/1Y7IJjU+AsbWkiUu+6ipqBPQWzS28gCwGOrv5BcIJS+tzsvLUKWgcixyfy5UAqJ32gCdzKC54FUpT2zL6Ad0vXGM6WkpZA7yworN4RCFPexXbi0x2GSTLG8PyIoZ4Iwgtj5NtsEDHrz0380FxgnKUIC3ny2SVuPlyD+9wepD3QYcxdRk1BIzcFT9ZxNlgil3IXRVPwVejvQ/zr6/ILdhBnZ8ojjvVCy3b86B1OhZj/ZByYo5QaykVqWl0V9vJOZlZfvOpm2HiDhm/2uNrVWxG4O6EwhnekAdaJYmeLq1YbhIfGA6KVOaB9Yi5A5BxK9QGXBZ6sLj+dIUD3QR47r9yAqVQE8Gr/Oh6oQXBQqOQv7WzBBs=
138 b3b1ae98f6a0e14c1e1ba806a6c18e193b6dae5c 0 iQIVAwUAWECEaEemf/qjRqrOAQjuZw/+IWJKnKOsaUMcB9ly3Fo/eskqDL6A0j69IXTJDeBDGMoyGbQU/gZyX2yc6Sw3EhwTSCXu5vKpzg3a6e8MNrC1iHqli4wJ/jPY7XtmiqTYDixdsBLNk46VfOi73ooFe08wVDSNB65xpZsrtPDSioNmQ2kSJwSHb71UlauS4xGkM74vuDpWvX5OZRSfBqMh6NjG5RwBBnS8mzA0SW2dCI2jSc5SCGIzIZpzM0xUN21xzq0YQbrk9qEsmi7ks0eowdhUjeET2wSWwhOK4jS4IfMyRO7KueUB05yHs4mChj9kNFNWtSzXKwKBQbZzwO/1Y7IJjU+AsbWkiUu+6ipqBPQWzS28gCwGOrv5BcIJS+tzsvLUKWgcixyfy5UAqJ32gCdzKC54FUpT2zL6Ad0vXGM6WkpZA7yworN4RCFPexXbi0x2GSTLG8PyIoZ4Iwgtj5NtsEDHrz0380FxgnKUIC3ny2SVuPlyD+9wepD3QYcxdRk1BIzcFT9ZxNlgil3IXRVPwVejvQ/zr6/ILdhBnZ8ojjvVCy3b86B1OhZj/ZByYo5QaykVqWl0V9vJOZlZfvOpm2HiDhm/2uNrVWxG4O6EwhnekAdaJYmeLq1YbhIfGA6KVOaB9Yi5A5BxK9QGXBZ6sLj+dIUD3QR47r9yAqVQE8Gr/Oh6oQXBQqOQv7WzBBs=
139 e69874dc1f4e142746ff3df91e678a09c6fc208c 0 iQIVAwUAWG0oGUemf/qjRqrOAQh3uhAAu4TN7jkkgH7Hxn8S1cB6Ru0x8MQutzzzpjShhsE/G7nzCxsZ5eWdJ5ItwXmKhunb7T0og54CGcTxfmdPtCI7AhhHh9/TM2Hv1EBcsXCiwjG8E+P6X1UJkijgTGjNWuCvEDOsQAvgywslECBNnXp2QA5I5UdCMeqDdTAb8ujvbD8I4pxUx1xXKY18DgQGJh13mRlfkEVnPxUi2n8emnwPLjbVVkVISkMFUkaOl8a4fOeZC1xzDpoQocoH2Q8DYa9RCPPSHHSYPNMWGCdNGN2CoAurcHWWvc7jNU28/tBhTazfFv8LYh63lLQ8SIIPZHJAOxo45ufMspzUfNgoD6y3vlF5aW7DpdxwYHnueh7S1Fxgtd9cOnxmxQsgiF4LK0a+VXOi/Tli/fivZHDRCGHJvJgsMQm7pzkay9sGohes6jAnsOv2E8DwFC71FO/btrAp07IRFxH9WhUeMsXLMS9oBlubMxMM58M+xzSKApK6bz2MkLsx9cewmfmfbJnRIK1xDv+J+77pWWNGlxCCjl1WU+aA3M7G8HzwAqjL75ASOWtBrJlFXvlLgzobwwetg6cm44Rv1P39i3rDySZvi4BDlOQHWFupgMKiXnZ1PeL7eBDs/aawrE0V2ysNkf9An+XJZkos2JSLPWcoNigfXNUu5c1AqsERvHA246XJzqvCEK8=
139 e69874dc1f4e142746ff3df91e678a09c6fc208c 0 iQIVAwUAWG0oGUemf/qjRqrOAQh3uhAAu4TN7jkkgH7Hxn8S1cB6Ru0x8MQutzzzpjShhsE/G7nzCxsZ5eWdJ5ItwXmKhunb7T0og54CGcTxfmdPtCI7AhhHh9/TM2Hv1EBcsXCiwjG8E+P6X1UJkijgTGjNWuCvEDOsQAvgywslECBNnXp2QA5I5UdCMeqDdTAb8ujvbD8I4pxUx1xXKY18DgQGJh13mRlfkEVnPxUi2n8emnwPLjbVVkVISkMFUkaOl8a4fOeZC1xzDpoQocoH2Q8DYa9RCPPSHHSYPNMWGCdNGN2CoAurcHWWvc7jNU28/tBhTazfFv8LYh63lLQ8SIIPZHJAOxo45ufMspzUfNgoD6y3vlF5aW7DpdxwYHnueh7S1Fxgtd9cOnxmxQsgiF4LK0a+VXOi/Tli/fivZHDRCGHJvJgsMQm7pzkay9sGohes6jAnsOv2E8DwFC71FO/btrAp07IRFxH9WhUeMsXLMS9oBlubMxMM58M+xzSKApK6bz2MkLsx9cewmfmfbJnRIK1xDv+J+77pWWNGlxCCjl1WU+aA3M7G8HzwAqjL75ASOWtBrJlFXvlLgzobwwetg6cm44Rv1P39i3rDySZvi4BDlOQHWFupgMKiXnZ1PeL7eBDs/aawrE0V2ysNkf9An+XJZkos2JSLPWcoNigfXNUu5c1AqsERvHA246XJzqvCEK8=
140 a1dd2c0c479e0550040542e392e87bc91262517e 0 iQIcBAABCAAGBQJYgBBEAAoJELnJ3IJKpb3VJosP/10rr3onsVbL8E+ri1Q0TJc8uhqIsBVyD/vS1MJtbxRaAdIV92o13YOent0o5ASFF/0yzVKlOWPQRjsYYbYY967k1TruDaWxJAnpeFgMni2Afl/qyWrW4AY2xegZNZCfMmwJA+uSJDdAn+jPV40XbuCZ+OgyZo5S05dfclHFxdc8rPKeUsJtvs5PMmCL3iQl1sulp1ASjuhRtFWZgSFsC6rb2Y7evD66ikL93+0/BPEB4SVX17vB/XEzdmh4ntyt4+d1XAznLHS33IU8UHbTkUmLy+82WnNH7HBB2V7gO47m/HhvaYjEfeW0bqMzN3aOUf30Vy/wB4HHsvkBGDgL5PYVHRRovGcAuCmnYbOkawqbRewW5oDs7UT3HbShNpxCxfsYpo7deHr11zWA3ooWCSlIRRREU4BfwVmn+Ds1hT5HM28Q6zr6GQZegDUbiT9i1zU0EpyfTpH7gc6NTVQrO1z1p70NBnQMqXcHjWJwjSwLER2Qify9MjrGXTL6ofD5zVZKobeRmq94mf3lDq26H7coraM9X5h9xa49VgAcRHzn/WQ6wcFCKDQr6FT67hTUOlF7Jriv8/5h/ziSZr10fCObKeKWN8Skur29VIAHHY4NuUqbM55WohD+jZ2O3d4tze1eWm5MDgWD8RlrfYhQ+cLOwH65AOtts0LNZwlvJuC7
140 a1dd2c0c479e0550040542e392e87bc91262517e 0 iQIcBAABCAAGBQJYgBBEAAoJELnJ3IJKpb3VJosP/10rr3onsVbL8E+ri1Q0TJc8uhqIsBVyD/vS1MJtbxRaAdIV92o13YOent0o5ASFF/0yzVKlOWPQRjsYYbYY967k1TruDaWxJAnpeFgMni2Afl/qyWrW4AY2xegZNZCfMmwJA+uSJDdAn+jPV40XbuCZ+OgyZo5S05dfclHFxdc8rPKeUsJtvs5PMmCL3iQl1sulp1ASjuhRtFWZgSFsC6rb2Y7evD66ikL93+0/BPEB4SVX17vB/XEzdmh4ntyt4+d1XAznLHS33IU8UHbTkUmLy+82WnNH7HBB2V7gO47m/HhvaYjEfeW0bqMzN3aOUf30Vy/wB4HHsvkBGDgL5PYVHRRovGcAuCmnYbOkawqbRewW5oDs7UT3HbShNpxCxfsYpo7deHr11zWA3ooWCSlIRRREU4BfwVmn+Ds1hT5HM28Q6zr6GQZegDUbiT9i1zU0EpyfTpH7gc6NTVQrO1z1p70NBnQMqXcHjWJwjSwLER2Qify9MjrGXTL6ofD5zVZKobeRmq94mf3lDq26H7coraM9X5h9xa49VgAcRHzn/WQ6wcFCKDQr6FT67hTUOlF7Jriv8/5h/ziSZr10fCObKeKWN8Skur29VIAHHY4NuUqbM55WohD+jZ2O3d4tze1eWm5MDgWD8RlrfYhQ+cLOwH65AOtts0LNZwlvJuC7
141 e1526da1e6d84e03146151c9b6e6950fe9a83d7d 0 iQIVAwUAWJIKpUemf/qjRqrOAQjjThAAvl1K/GZBrkanwEPXomewHkWKTEy1s5d5oWmPPGrSb9G4LM/3/abSbQ7fnzkS6IWi4Ao0za68w/MohaVGKoMAslRbelaTqlus0wE3zxb2yQ/j2NeZzFnFEuR/vbUug7uzH+onko2jXrt7VcPNXLOa1/g5CWwaf/YPfJO4zv+atlzBHvuFcQCkdbcOJkccCnBUoR7y0PJoBJX6K7wJQ+hWLdcY4nVaxkGPRmsZJo9qogXZMw1CwJVjofxRI0S/5vMtEqh8srYsg7qlTNv8eYnwdpfuunn2mI7Khx10Tz85PZDnr3SGRiFvdfmT30pI7jL3bhOHALkaoy2VevteJjIyMxANTvjIUBNQUi+7Kj3VIKmkL9NAMAQBbshiQL1wTrXdqOeC8Nm1BfCQEox2yiC6pDFbXVbguwJZ5VKFizTTK6f6BdNYKTVx8lNEdjAsWH8ojgGWwGXBbTkClULHezJ/sODaZzK/+M/IzbGmlF27jJYpdJX8fUoybZNw9lXwIfQQWHmQHEOJYCljD9G1tvYY70+xAFexgBX5Ib48UK4DRITVNecyQZL7bLTzGcM0TAE0EtD4M42wawsYP3Cva9UxShFLICQdPoa4Wmfs6uLbXG1DDLol/j7b6bL+6W8E3AlW+aAPc8GZm51/w3VlYqqciWTc12OJpu8FiD0pZ/iBw+E=
141 e1526da1e6d84e03146151c9b6e6950fe9a83d7d 0 iQIVAwUAWJIKpUemf/qjRqrOAQjjThAAvl1K/GZBrkanwEPXomewHkWKTEy1s5d5oWmPPGrSb9G4LM/3/abSbQ7fnzkS6IWi4Ao0za68w/MohaVGKoMAslRbelaTqlus0wE3zxb2yQ/j2NeZzFnFEuR/vbUug7uzH+onko2jXrt7VcPNXLOa1/g5CWwaf/YPfJO4zv+atlzBHvuFcQCkdbcOJkccCnBUoR7y0PJoBJX6K7wJQ+hWLdcY4nVaxkGPRmsZJo9qogXZMw1CwJVjofxRI0S/5vMtEqh8srYsg7qlTNv8eYnwdpfuunn2mI7Khx10Tz85PZDnr3SGRiFvdfmT30pI7jL3bhOHALkaoy2VevteJjIyMxANTvjIUBNQUi+7Kj3VIKmkL9NAMAQBbshiQL1wTrXdqOeC8Nm1BfCQEox2yiC6pDFbXVbguwJZ5VKFizTTK6f6BdNYKTVx8lNEdjAsWH8ojgGWwGXBbTkClULHezJ/sODaZzK/+M/IzbGmlF27jJYpdJX8fUoybZNw9lXwIfQQWHmQHEOJYCljD9G1tvYY70+xAFexgBX5Ib48UK4DRITVNecyQZL7bLTzGcM0TAE0EtD4M42wawsYP3Cva9UxShFLICQdPoa4Wmfs6uLbXG1DDLol/j7b6bL+6W8E3AlW+aAPc8GZm51/w3VlYqqciWTc12OJpu8FiD0pZ/iBw+E=
142 25703b624d27e3917d978af56d6ad59331e0464a 0 iQIcBAABCAAGBQJYuMSwAAoJELnJ3IJKpb3VL3YP/iKWY3+K3cLUBD3Ne5MhfS7N3t6rlk9YD4kmU8JnVeV1oAfg36VCylpbJLBnmQdvC8AfBJOkXi6DHp9RKXXmlsOeoppdWYGX5RMOzuwuGPBii6cA6KFd+WBpBJlRtklz61qGCAtv4q8V1mga0yucihghzt4lD/PPz7mk6yUBL8s3rK+bIHGdEhnK2dfnn/U2G0K/vGgsYZESORISuBclCrrc7M3/v1D+FBMCEYX9FXYU4PhYkKXK1mSqzCB7oENu/WP4ijl1nRnEIyzBV9pKO4ylnXTpbZAr/e4PofzjzPXb0zume1191C3wvgJ4eDautGide/Pxls5s6fJRaIowf5XVYQ5srX/NC9N3K77Hy01t5u8nwcyAhjmajZYuB9j37nmiwFawqS/y2eHovrUjkGdelV8OM7/iAexPRC8i2NcGk0m6XuzWy1Dxr8453VD8Hh3tTeafd6v5uHXSLjwogpu/th5rk/i9/5GBzc1MyJgRTwBhVHi/yFxfyakrSU7HT2cwX/Lb5KgWccogqfvrFYQABIBanxLIeZxTv8OIjC75EYknbxYtvvgb35ZdJytwrTHSZN0S7Ua2dHx2KUnHB6thbLu/v9fYrCgFF76DK4Ogd22Cbvv6NqRoglG26d0bqdwz/l1n3o416YjupteW8LMxHzuwiJy69WP1yi10eNDq
142 25703b624d27e3917d978af56d6ad59331e0464a 0 iQIcBAABCAAGBQJYuMSwAAoJELnJ3IJKpb3VL3YP/iKWY3+K3cLUBD3Ne5MhfS7N3t6rlk9YD4kmU8JnVeV1oAfg36VCylpbJLBnmQdvC8AfBJOkXi6DHp9RKXXmlsOeoppdWYGX5RMOzuwuGPBii6cA6KFd+WBpBJlRtklz61qGCAtv4q8V1mga0yucihghzt4lD/PPz7mk6yUBL8s3rK+bIHGdEhnK2dfnn/U2G0K/vGgsYZESORISuBclCrrc7M3/v1D+FBMCEYX9FXYU4PhYkKXK1mSqzCB7oENu/WP4ijl1nRnEIyzBV9pKO4ylnXTpbZAr/e4PofzjzPXb0zume1191C3wvgJ4eDautGide/Pxls5s6fJRaIowf5XVYQ5srX/NC9N3K77Hy01t5u8nwcyAhjmajZYuB9j37nmiwFawqS/y2eHovrUjkGdelV8OM7/iAexPRC8i2NcGk0m6XuzWy1Dxr8453VD8Hh3tTeafd6v5uHXSLjwogpu/th5rk/i9/5GBzc1MyJgRTwBhVHi/yFxfyakrSU7HT2cwX/Lb5KgWccogqfvrFYQABIBanxLIeZxTv8OIjC75EYknbxYtvvgb35ZdJytwrTHSZN0S7Ua2dHx2KUnHB6thbLu/v9fYrCgFF76DK4Ogd22Cbvv6NqRoglG26d0bqdwz/l1n3o416YjupteW8LMxHzuwiJy69WP1yi10eNDq
143 ed5b25874d998ababb181a939dd37a16ea644435 0 iQIcBAABCAAGBQJY4r/gAAoJELnJ3IJKpb3VtwYP/RuTmo252ExXQk/n5zGJZvZQnI86vO1+yGuyOlGFFBwf1v3sOLW1HD7fxF6/GdT8CSQrRqtC17Ya3qtayfY/0AEiSuH2bklBXSB1H5wPyguS5iLqyilCJY0SkHYBIDhJ0xftuIjsa805wdMm3OdclnTOkYT+K1WL8Ylbx/Ni2Lsx1rPpYdcQ/HlTkr5ca1ZbNOOSxSNI4+ilGlKbdSYeEsmqB2sDEiSaDEoxGGoSgzAE9+5Q2FfCGXV0bq4vfmEPoT9lhB4kANE+gcFUvsJTu8Z7EdF8y3CJLiy8+KHO/VLKTGJ1pMperbig9nAXl1AOt+izBFGJGTolbR/ShkkDWB/QVcqIF5CysAWMgnHAx7HjnMDBOANcKzhMMfOi3GUvOCNNIqIIoJHKRHaRk0YbMdt7z2mKpTrRQ9Zadz764jXOqqrPgQFM3jkBHzAvZz9yShrHGh42Y+iReAF9pAN0xPjyZ5Y2qp+DSl0bIQqrAet6Zd3QuoJtXczAeRrAvgn7O9MyLnMyE5s7xxI7o8M7zfWtChLF8ytJUzmRo3iVJNOJH+Zls9N30PGw6vubQAnB5ieaVTv8lnNpcAnEQD/i0tmRSxzyyqoOQbnItIPKFOsaYW+eX9sgJmObU3yDc5k3cs+yAFD2CM/uiUsLcTKyxPNcP1JHBYpwhOjIGczSHVS1
143 ed5b25874d998ababb181a939dd37a16ea644435 0 iQIcBAABCAAGBQJY4r/gAAoJELnJ3IJKpb3VtwYP/RuTmo252ExXQk/n5zGJZvZQnI86vO1+yGuyOlGFFBwf1v3sOLW1HD7fxF6/GdT8CSQrRqtC17Ya3qtayfY/0AEiSuH2bklBXSB1H5wPyguS5iLqyilCJY0SkHYBIDhJ0xftuIjsa805wdMm3OdclnTOkYT+K1WL8Ylbx/Ni2Lsx1rPpYdcQ/HlTkr5ca1ZbNOOSxSNI4+ilGlKbdSYeEsmqB2sDEiSaDEoxGGoSgzAE9+5Q2FfCGXV0bq4vfmEPoT9lhB4kANE+gcFUvsJTu8Z7EdF8y3CJLiy8+KHO/VLKTGJ1pMperbig9nAXl1AOt+izBFGJGTolbR/ShkkDWB/QVcqIF5CysAWMgnHAx7HjnMDBOANcKzhMMfOi3GUvOCNNIqIIoJHKRHaRk0YbMdt7z2mKpTrRQ9Zadz764jXOqqrPgQFM3jkBHzAvZz9yShrHGh42Y+iReAF9pAN0xPjyZ5Y2qp+DSl0bIQqrAet6Zd3QuoJtXczAeRrAvgn7O9MyLnMyE5s7xxI7o8M7zfWtChLF8ytJUzmRo3iVJNOJH+Zls9N30PGw6vubQAnB5ieaVTv8lnNpcAnEQD/i0tmRSxzyyqoOQbnItIPKFOsaYW+eX9sgJmObU3yDc5k3cs+yAFD2CM/uiUsLcTKyxPNcP1JHBYpwhOjIGczSHVS1
144 77eaf9539499a1b8be259ffe7ada787d07857f80 0 iQIcBAABCAAGBQJY9iz9AAoJELnJ3IJKpb3VYqEQAJNkB09sXgYRLA4kGQv3p4v02q9WZ1lHkAhOlNwIh7Zp+pGvT33nHZffByA0v+xtJNV9TNMIFFjkCg3jl5Z42CCe33ZlezGBAzXU+70QPvOR0ojlYk+FdMfeSyCBzWYokIpImwNmwNGKVrUAfywdikCsUC2aRjKg4Mn7GnqWl9WrBG6JEOOUamdx8qV2f6g/utRiqj4YQ86P0y4K3yakwc1LMM+vRfrwvsf1+DZ9t7QRENNKQ6gRnUdfryqSFIWn1VkBVMwIN5W3yIrTMfgH1wAZxbnYHrN5qDK7mcbP7bOA3XWJuEC+3QRnheRFd/21O1dMFuYjaKApXPHRlTGRMOaz2eydbfBopUS1BtfYEh4/B/1yJb9/HDw6LiAjea7ACHiaNec83z643005AvtUuWhjX3QTPkYlQzWaosanGy1IOGtXCPp1L0A+9gUpqyqycfPjQCbST5KRzYSZn3Ngmed5Bb6jsgvg5e5y0En/SQgK/pTKnxemAmFFVvIIrrWGRKj0AD0IFEHEepmwprPRs97EZPoBPFAGmVRuASBeIhFQxSDIXV0ebHJoUmz5w1rTy7U3Eq0ff6nW14kjWOUplatXz5LpWJ3VkZKrI+4gelto5xpTI6gJl2nmezhXQIlInk17cPuxmiHjeMdlOHZRh/zICLhQNL5fGne0ZL+qlrXY
144 77eaf9539499a1b8be259ffe7ada787d07857f80 0 iQIcBAABCAAGBQJY9iz9AAoJELnJ3IJKpb3VYqEQAJNkB09sXgYRLA4kGQv3p4v02q9WZ1lHkAhOlNwIh7Zp+pGvT33nHZffByA0v+xtJNV9TNMIFFjkCg3jl5Z42CCe33ZlezGBAzXU+70QPvOR0ojlYk+FdMfeSyCBzWYokIpImwNmwNGKVrUAfywdikCsUC2aRjKg4Mn7GnqWl9WrBG6JEOOUamdx8qV2f6g/utRiqj4YQ86P0y4K3yakwc1LMM+vRfrwvsf1+DZ9t7QRENNKQ6gRnUdfryqSFIWn1VkBVMwIN5W3yIrTMfgH1wAZxbnYHrN5qDK7mcbP7bOA3XWJuEC+3QRnheRFd/21O1dMFuYjaKApXPHRlTGRMOaz2eydbfBopUS1BtfYEh4/B/1yJb9/HDw6LiAjea7ACHiaNec83z643005AvtUuWhjX3QTPkYlQzWaosanGy1IOGtXCPp1L0A+9gUpqyqycfPjQCbST5KRzYSZn3Ngmed5Bb6jsgvg5e5y0En/SQgK/pTKnxemAmFFVvIIrrWGRKj0AD0IFEHEepmwprPRs97EZPoBPFAGmVRuASBeIhFQxSDIXV0ebHJoUmz5w1rTy7U3Eq0ff6nW14kjWOUplatXz5LpWJ3VkZKrI+4gelto5xpTI6gJl2nmezhXQIlInk17cPuxmiHjeMdlOHZRh/zICLhQNL5fGne0ZL+qlrXY
145 616e788321cc4ae9975b7f0c54c849f36d82182b 0 iQIVAwUAWPZuQkemf/qjRqrOAQjFlg/9HXEegJMv8FP+uILPoaiA2UCiqWUL2MVJ0K1cvafkwUq+Iwir8sTe4VJ1v6V+ZRiOuzs4HMnoGJrIks4vHRbAxJ3J6xCfvrsbHdl59grv54vuoL5FlZvkdIe8L7/ovKrUmNwPWZX2v+ffFPrsEBeVlVrXpp4wOPhDxCKTmjYVOp87YqXfJsud7EQFPqpV4jX8DEDtJWT95OE9x0srBg0HpSE95d/BM4TuXTVNI8fV41YEqearKeFIhLxu37HxUmGmkAALCi8RJmm4hVpUHgk3tAVzImI8DglUqnC6VEfaYb+PKzIqHelhb66JO/48qN2S/JXihpNHAVUBysBT0b1xEnc6eNsF2fQEB+bEcf8IGj7/ILee1cmwPtoK2OXR2+xWWWjlu2keVcKeI0yAajJw/dP21yvVzVq0ypst7iD+EGHLJWJSmZscbyH5ICr+TJ5yQvIGZJtfsAdAUUTM2xpqSDW4mT5kYyg75URbQ3AKI7lOhJBmkkGQErE4zIQMkaAqcWziVF20xiRWfJoFxT2fK5weaRGIjELH49NLlyvZxYc4LlRo9lIdC7l/6lYDdTx15VuEj1zx/91y/d7OtPm+KCA2Bbdqth8m/fMD8trfQ6jSG/wgsvjZ+S0eoXa92qIR/igsCI+6EwP7duuzL2iyKOPXupQVNN10PKI7EuKv4Lk=
145 616e788321cc4ae9975b7f0c54c849f36d82182b 0 iQIVAwUAWPZuQkemf/qjRqrOAQjFlg/9HXEegJMv8FP+uILPoaiA2UCiqWUL2MVJ0K1cvafkwUq+Iwir8sTe4VJ1v6V+ZRiOuzs4HMnoGJrIks4vHRbAxJ3J6xCfvrsbHdl59grv54vuoL5FlZvkdIe8L7/ovKrUmNwPWZX2v+ffFPrsEBeVlVrXpp4wOPhDxCKTmjYVOp87YqXfJsud7EQFPqpV4jX8DEDtJWT95OE9x0srBg0HpSE95d/BM4TuXTVNI8fV41YEqearKeFIhLxu37HxUmGmkAALCi8RJmm4hVpUHgk3tAVzImI8DglUqnC6VEfaYb+PKzIqHelhb66JO/48qN2S/JXihpNHAVUBysBT0b1xEnc6eNsF2fQEB+bEcf8IGj7/ILee1cmwPtoK2OXR2+xWWWjlu2keVcKeI0yAajJw/dP21yvVzVq0ypst7iD+EGHLJWJSmZscbyH5ICr+TJ5yQvIGZJtfsAdAUUTM2xpqSDW4mT5kYyg75URbQ3AKI7lOhJBmkkGQErE4zIQMkaAqcWziVF20xiRWfJoFxT2fK5weaRGIjELH49NLlyvZxYc4LlRo9lIdC7l/6lYDdTx15VuEj1zx/91y/d7OtPm+KCA2Bbdqth8m/fMD8trfQ6jSG/wgsvjZ+S0eoXa92qIR/igsCI+6EwP7duuzL2iyKOPXupQVNN10PKI7EuKv4Lk=
146 bb96d4a497432722623ae60d9bc734a1e360179e 0 iQIVAwUAWQkDfEemf/qjRqrOAQierQ/7BuQ0IW0T0cglgqIgkLuYLx2VXJCTEtRNCWmrH2UMK7fAdpAhN0xf+xedv56zYHrlyHpbskDbWvsKIHJdw/4bQitXaIFTyuMMtSR5vXy4Nly34O/Xs2uGb3Y5qwdubeK2nZr4lSPgiRHb/zI/B1Oy8GX830ljmIOY7B0nUWy4DrXcy/M41SnAMLFyD1K6T/8tkv7M4Fai7dQoF9EmIIkShVPktI3lqp3m7infZ4XnJqcqUB0NSfQZwZaUaoalOdCvEIe3ab5ewgl/CuvlDI4oqMQGjXCtNLbtiZSwo6hvudO6ewT+Zn/VdabkZyRtXUxu56ajjd6h22nU1+vknqDzo5tzw6oh1Ubzf8tzyv3Gmmr+tlOjzfK7tXXnT3vR9aEGli0qri0DzOpsDSY0pDC7EsS4LINPoNdsGQrGQdoX++AISROlNjvyuo4Vrp26tPHCSupkKOXuZaiozycAa2Q+aI1EvkPZSXe8SAXKDVtFn05ZB58YVkFzZKAYAxkE/ven59zb4aIbOgR12tZbJoZZsVHrlf/TcDtiXVfIMEMsCtJ1tPgD1rAsEURWRxK3mJ0Ev6KTHgNz4PeBhq1gIP/Y665aX2+cCjc4+vApPUienh5aOr1bQFpIDyYZsafHGMUFNCwRh8bX98oTGa0hjqz4ypwXE4Wztjdc+48UiHARp/Y=
146 bb96d4a497432722623ae60d9bc734a1e360179e 0 iQIVAwUAWQkDfEemf/qjRqrOAQierQ/7BuQ0IW0T0cglgqIgkLuYLx2VXJCTEtRNCWmrH2UMK7fAdpAhN0xf+xedv56zYHrlyHpbskDbWvsKIHJdw/4bQitXaIFTyuMMtSR5vXy4Nly34O/Xs2uGb3Y5qwdubeK2nZr4lSPgiRHb/zI/B1Oy8GX830ljmIOY7B0nUWy4DrXcy/M41SnAMLFyD1K6T/8tkv7M4Fai7dQoF9EmIIkShVPktI3lqp3m7infZ4XnJqcqUB0NSfQZwZaUaoalOdCvEIe3ab5ewgl/CuvlDI4oqMQGjXCtNLbtiZSwo6hvudO6ewT+Zn/VdabkZyRtXUxu56ajjd6h22nU1+vknqDzo5tzw6oh1Ubzf8tzyv3Gmmr+tlOjzfK7tXXnT3vR9aEGli0qri0DzOpsDSY0pDC7EsS4LINPoNdsGQrGQdoX++AISROlNjvyuo4Vrp26tPHCSupkKOXuZaiozycAa2Q+aI1EvkPZSXe8SAXKDVtFn05ZB58YVkFzZKAYAxkE/ven59zb4aIbOgR12tZbJoZZsVHrlf/TcDtiXVfIMEMsCtJ1tPgD1rAsEURWRxK3mJ0Ev6KTHgNz4PeBhq1gIP/Y665aX2+cCjc4+vApPUienh5aOr1bQFpIDyYZsafHGMUFNCwRh8bX98oTGa0hjqz4ypwXE4Wztjdc+48UiHARp/Y=
147 c850f0ed54c1d42f9aa079ad528f8127e5775217 0 iQIVAwUAWTQINUemf/qjRqrOAQjZDw//b4pEgHYfWRVDEmLZtevysfhlJzbSyLAnWgNnRUVdSwl4WRF1r6ds/q7N4Ege5wQHjOpRtx4jC3y/riMbrLUlaeUXzCdqKgm4JcINS1nXy3IfkeDdUKyOR9upjaVhIEzCMRpyzabdYuflh5CoxayO7GFk2iZ8c1oAl4QzuLSspn9w+znqDg0HrMDbRNijStSulNjkqutih9UqT/PYizhE1UjL0NSnpYyD1vDljsHModJc2dhSzuZ1c4VFZHkienk+CNyeLtVKg8aC+Ej/Ppwq6FlE461T/RxOEzf+WFAc9F4iJibSN2kAFB4ySJ43y+OKkvzAwc5XbUx0y6OlWn2Ph+5T54sIwqasG3DjXyVrwVtAvCrcWUmOyS0RfkKoDVepMPIhFXyrhGqUYSq25Gt6tHVtIrlcWARIGGWlsE+PSHi87qcnSjs4xUzZwVvJWz4fuM1AUG/GTpyt4w3kB85XQikIINkmSTmsM/2/ar75T6jBL3kqOCGOL3n7bVZsGXllhkkQ7e/jqPPWnNXm8scDYdT3WENNu34zZp5ZmqdTXPAIIaqGswnU04KfUSEoYtOMri3E2VvrgMkiINm9BOKpgeTsMb3dkYRw2ZY3UAH9QfdX9BZywk6v3kkE5ghLWMUoQ4sqRlTo7mJKA8+EodjmIGRV/kAv1f7pigg6pIWWEyo=
147 c850f0ed54c1d42f9aa079ad528f8127e5775217 0 iQIVAwUAWTQINUemf/qjRqrOAQjZDw//b4pEgHYfWRVDEmLZtevysfhlJzbSyLAnWgNnRUVdSwl4WRF1r6ds/q7N4Ege5wQHjOpRtx4jC3y/riMbrLUlaeUXzCdqKgm4JcINS1nXy3IfkeDdUKyOR9upjaVhIEzCMRpyzabdYuflh5CoxayO7GFk2iZ8c1oAl4QzuLSspn9w+znqDg0HrMDbRNijStSulNjkqutih9UqT/PYizhE1UjL0NSnpYyD1vDljsHModJc2dhSzuZ1c4VFZHkienk+CNyeLtVKg8aC+Ej/Ppwq6FlE461T/RxOEzf+WFAc9F4iJibSN2kAFB4ySJ43y+OKkvzAwc5XbUx0y6OlWn2Ph+5T54sIwqasG3DjXyVrwVtAvCrcWUmOyS0RfkKoDVepMPIhFXyrhGqUYSq25Gt6tHVtIrlcWARIGGWlsE+PSHi87qcnSjs4xUzZwVvJWz4fuM1AUG/GTpyt4w3kB85XQikIINkmSTmsM/2/ar75T6jBL3kqOCGOL3n7bVZsGXllhkkQ7e/jqPPWnNXm8scDYdT3WENNu34zZp5ZmqdTXPAIIaqGswnU04KfUSEoYtOMri3E2VvrgMkiINm9BOKpgeTsMb3dkYRw2ZY3UAH9QfdX9BZywk6v3kkE5ghLWMUoQ4sqRlTo7mJKA8+EodjmIGRV/kAv1f7pigg6pIWWEyo=
148 26c49ed51a698ec016d2b4c6b44ca3c3f73cc788 0 iQIcBAABCAAGBQJZXQSmAAoJELnJ3IJKpb3VmTwP/jsxFTlKzWU8EnEhEViiP2YREOD3AXU7685DIMnoyVAsZgxrt0CG6Y92b5sINCeh5B0ORPQ7+xi2Xmz6tX8EeAR+/Dpdx6K623yExf8kq91zgfMvYkatNMu6ZVfywibYZAASq02oKoX7WqSPcQG/OwgtdFiGacCrG5iMH7wRv0N9hPc6D5vAV8/H/Inq8twpSG5SGDpCdKj7KPZiY8DFu/3OXatJtl+byg8zWT4FCYKkBPvmZp8/sRhDKBgwr3RvF1p84uuw/QxXjt+DmGxgtjvObjHr+shCMcKBAuZ4RtZmyEo/0L81uaTElHu1ejsEzsEKxs+8YifnH070PTFoV4VXQyXfTc8AyaqHE6rzX96a/HjQiJnL4dFeTZIrUhGK3AkObFLWJxVTo4J8+oliBQQldIh1H2yb1ZMfwapLnUGIqSieHDGZ6K2ccNJK8Q7IRhTCvYc0cjsnbwTpV4cebGqf3WXZhX0cZN+TNfhh/HGRzR1EeAAavjJqpDam1OBA5TmtJd/lHLIRVR5jyG+r4SK0XDlJ8uSfah7MpVH6aQ6UrycPyFusGXQlIqJ1DYQaBrI/SRJfIvRUmvVz9WgKLe83oC3Ui3aWR9rNjMb2InuQuXjeZaeaYfBAUYACcGfCZpZZvoEkMHCqtTng1rbbFnKMFk5kVy9YWuVgK9Iuh0O5
148 26c49ed51a698ec016d2b4c6b44ca3c3f73cc788 0 iQIcBAABCAAGBQJZXQSmAAoJELnJ3IJKpb3VmTwP/jsxFTlKzWU8EnEhEViiP2YREOD3AXU7685DIMnoyVAsZgxrt0CG6Y92b5sINCeh5B0ORPQ7+xi2Xmz6tX8EeAR+/Dpdx6K623yExf8kq91zgfMvYkatNMu6ZVfywibYZAASq02oKoX7WqSPcQG/OwgtdFiGacCrG5iMH7wRv0N9hPc6D5vAV8/H/Inq8twpSG5SGDpCdKj7KPZiY8DFu/3OXatJtl+byg8zWT4FCYKkBPvmZp8/sRhDKBgwr3RvF1p84uuw/QxXjt+DmGxgtjvObjHr+shCMcKBAuZ4RtZmyEo/0L81uaTElHu1ejsEzsEKxs+8YifnH070PTFoV4VXQyXfTc8AyaqHE6rzX96a/HjQiJnL4dFeTZIrUhGK3AkObFLWJxVTo4J8+oliBQQldIh1H2yb1ZMfwapLnUGIqSieHDGZ6K2ccNJK8Q7IRhTCvYc0cjsnbwTpV4cebGqf3WXZhX0cZN+TNfhh/HGRzR1EeAAavjJqpDam1OBA5TmtJd/lHLIRVR5jyG+r4SK0XDlJ8uSfah7MpVH6aQ6UrycPyFusGXQlIqJ1DYQaBrI/SRJfIvRUmvVz9WgKLe83oC3Ui3aWR9rNjMb2InuQuXjeZaeaYfBAUYACcGfCZpZZvoEkMHCqtTng1rbbFnKMFk5kVy9YWuVgK9Iuh0O5
149 857876ebaed4e315f63157bd157d6ce553c7ab73 0 iQIVAwUAWW9XW0emf/qjRqrOAQhI7A//cKXIM4l8vrWWsc1Os4knXm/2UaexmAwV70TpviKL9RxCy5zBP/EapCaGRCH8uNPOQTkWGR9Aucm3CtxhggCMzULQxxeH86mEpWf1xILWLySPXW/t2f+2zxrwLSAxxqFJtuYv83Pe8CnS3y4BlgHnBKYXH8XXuW8uvfc0lHKblhrspGBIAinx7vPLoGQcpYrn9USWUKq5d9FaCLQCDT9501FHKf5dlYQajevCUDnewtn5ohelOXjTJQClW3aygv/z+98Kq7ZhayeIiZu+SeP+Ay7lZPklXcy6eyRiQtGCa1yesb9v53jKtgxWewV4o6zyuUesdknZ/IBeNUgw8LepqTIJo6/ckyvBOsSQcda81DuYNUChZLYTSXYPHEUmYiz6CvNoLEgHF/oO5p6CZXOPWbmLWrAFd+0+1Tuq8BSh+PSdEREM3ZLOikkXoVzTKBgu4zpMvmBnjliBg7WhixkcG0v5WunlV9/oHAIpsKdL7AatU+oCPulp+xDpTKzRazEemYiWG9zYKzwSMk9Nc17e2tk+EtFSPsPo4iVCXMgdIZSTNBvynKEFXZQVPWVa+bYRdAmbSY8awiX7exxYL10UcpnN2q/AH/F7rQzAmo8eZ3OtD0+3Nk3JRx0/CMyzKLPYDpdUgwmaPb+s2Bsy7f7TfmA7jTa69YqB1/zVwlWULr0=
149 857876ebaed4e315f63157bd157d6ce553c7ab73 0 iQIVAwUAWW9XW0emf/qjRqrOAQhI7A//cKXIM4l8vrWWsc1Os4knXm/2UaexmAwV70TpviKL9RxCy5zBP/EapCaGRCH8uNPOQTkWGR9Aucm3CtxhggCMzULQxxeH86mEpWf1xILWLySPXW/t2f+2zxrwLSAxxqFJtuYv83Pe8CnS3y4BlgHnBKYXH8XXuW8uvfc0lHKblhrspGBIAinx7vPLoGQcpYrn9USWUKq5d9FaCLQCDT9501FHKf5dlYQajevCUDnewtn5ohelOXjTJQClW3aygv/z+98Kq7ZhayeIiZu+SeP+Ay7lZPklXcy6eyRiQtGCa1yesb9v53jKtgxWewV4o6zyuUesdknZ/IBeNUgw8LepqTIJo6/ckyvBOsSQcda81DuYNUChZLYTSXYPHEUmYiz6CvNoLEgHF/oO5p6CZXOPWbmLWrAFd+0+1Tuq8BSh+PSdEREM3ZLOikkXoVzTKBgu4zpMvmBnjliBg7WhixkcG0v5WunlV9/oHAIpsKdL7AatU+oCPulp+xDpTKzRazEemYiWG9zYKzwSMk9Nc17e2tk+EtFSPsPo4iVCXMgdIZSTNBvynKEFXZQVPWVa+bYRdAmbSY8awiX7exxYL10UcpnN2q/AH/F7rQzAmo8eZ3OtD0+3Nk3JRx0/CMyzKLPYDpdUgwmaPb+s2Bsy7f7TfmA7jTa69YqB1/zVwlWULr0=
150 5544af8622863796a0027566f6b646e10d522c4c 0 iQIcBAABCAAGBQJZjJflAAoJELnJ3IJKpb3V19kQALCvTdPrpce5+rBNbFtLGNFxTMDol1dUy87EUAWiArnfOzW3rKBdYxvxDL23BpgUfjRm1fAXdayVvlj6VC6Dyb195OLmc/I9z7SjFxsfmxWilF6U0GIa3W0x37i05EjfcccrBIuSLrvR6AWyJhjLOBCcyAqD/HcEom00/L+o2ry9CDQNLEeVuNewJiupcUqsTIG2yS26lWbtLZuoqS2T4Nlg8wjJhiSXlsZSuAF55iUJKlTQP6KyWReiaYuEVfm/Bybp0A2bFcZCYpWPwnwKBdSCHhIalH8PO57gh9J7xJVnyyBg5PU6n4l6PrGOmKhNiU/xyNe36tEAdMW6svcVvt8hiY0dnwWqR6wgnFFDu0lnTMUcjsy5M5FBY6wSw9Fph8zcNRzYyaeUbasNonPvrIrk21nT3ET3RzVR3ri2nJDVF+0GlpogGfk9k7wY3808091BMsyV3448ZPKQeWiK4Yy4UOUwbKV7YAsS5MdDnC1uKjl4GwLn9UCY/+Q2/2R0CBZ13Tox+Nbo6hBRuRGtFIbLK9j7IIUhhZrIZFSh8cDNkC+UMaS52L5z7ECvoYIUpw+MJ7NkMLHIVGZ2Nxn0C7IbGO6uHyR7D6bdNpxilU+WZStHk0ppZItRTm/htar4jifnaCI8F8OQNYmZ3cQhxx6qV2Tyow8arvWb1NYXrocG
150 5544af8622863796a0027566f6b646e10d522c4c 0 iQIcBAABCAAGBQJZjJflAAoJELnJ3IJKpb3V19kQALCvTdPrpce5+rBNbFtLGNFxTMDol1dUy87EUAWiArnfOzW3rKBdYxvxDL23BpgUfjRm1fAXdayVvlj6VC6Dyb195OLmc/I9z7SjFxsfmxWilF6U0GIa3W0x37i05EjfcccrBIuSLrvR6AWyJhjLOBCcyAqD/HcEom00/L+o2ry9CDQNLEeVuNewJiupcUqsTIG2yS26lWbtLZuoqS2T4Nlg8wjJhiSXlsZSuAF55iUJKlTQP6KyWReiaYuEVfm/Bybp0A2bFcZCYpWPwnwKBdSCHhIalH8PO57gh9J7xJVnyyBg5PU6n4l6PrGOmKhNiU/xyNe36tEAdMW6svcVvt8hiY0dnwWqR6wgnFFDu0lnTMUcjsy5M5FBY6wSw9Fph8zcNRzYyaeUbasNonPvrIrk21nT3ET3RzVR3ri2nJDVF+0GlpogGfk9k7wY3808091BMsyV3448ZPKQeWiK4Yy4UOUwbKV7YAsS5MdDnC1uKjl4GwLn9UCY/+Q2/2R0CBZ13Tox+Nbo6hBRuRGtFIbLK9j7IIUhhZrIZFSh8cDNkC+UMaS52L5z7ECvoYIUpw+MJ7NkMLHIVGZ2Nxn0C7IbGO6uHyR7D6bdNpxilU+WZStHk0ppZItRTm/htar4jifnaCI8F8OQNYmZ3cQhxx6qV2Tyow8arvWb1NYXrocG
151 943c91326b23954e6e1c6960d0239511f9530258 0 iQIcBAABCAAGBQJZjKKZAAoJELnJ3IJKpb3VGQkP/0iF6Khef0lBaRhbSAPwa7RUBb3iaBeuwmeic/hUjMoU1E5NR36bDDaF3u2di5mIYPBONFIeCPf9/DKyFkidueX1UnlAQa3mjh/QfKTb4/yO2Nrk7eH+QtrYxVUUYYjwgp4rS0Nd/++I1IUOor54vqJzJ7ZnM5O1RsE7VI1esAC/BTlUuO354bbm08B0owsZBwVvcVvpV4zeTvq5qyPxBJ3M0kw83Pgwh3JZB9IYhOabhSUBcA2fIPHgYGYnJVC+bLOeMWI1HJkJeoYfClNUiQUjAmi0cdTC733eQnHkDw7xyyFi+zkKu6JmU1opxkHSuj4Hrjul7Gtw3vVWWUPufz3AK7oymNp2Xr5y1HQLDtNJP3jicTTG1ae2TdX5Az3ze0I8VGbpR81/6ShAvY2cSKttV3I+2k4epxTTTf0xaZS1eUdnFOox6acElG2reNzx7EYYxpHj17K8N2qNzyY78iPgbJ+L39PBFoiGXMZJqWCxxIHoK1MxlXa8WwSnsXAU768dJvEn2N1x3fl+aeaWzeM4/5Qd83YjFuCeycuRnIo3rejSX3rWFAwZE0qQHKI5YWdKDLxIfdHTjdfMP7np+zLcHt0DV/dHmj2hKQgU0OK04fx7BrmdS1tw67Y9bL3H3TDohn7khU1FrqrKVuqSLbLsxnNyWRbZQF+DCoYrHlIW
151 943c91326b23954e6e1c6960d0239511f9530258 0 iQIcBAABCAAGBQJZjKKZAAoJELnJ3IJKpb3VGQkP/0iF6Khef0lBaRhbSAPwa7RUBb3iaBeuwmeic/hUjMoU1E5NR36bDDaF3u2di5mIYPBONFIeCPf9/DKyFkidueX1UnlAQa3mjh/QfKTb4/yO2Nrk7eH+QtrYxVUUYYjwgp4rS0Nd/++I1IUOor54vqJzJ7ZnM5O1RsE7VI1esAC/BTlUuO354bbm08B0owsZBwVvcVvpV4zeTvq5qyPxBJ3M0kw83Pgwh3JZB9IYhOabhSUBcA2fIPHgYGYnJVC+bLOeMWI1HJkJeoYfClNUiQUjAmi0cdTC733eQnHkDw7xyyFi+zkKu6JmU1opxkHSuj4Hrjul7Gtw3vVWWUPufz3AK7oymNp2Xr5y1HQLDtNJP3jicTTG1ae2TdX5Az3ze0I8VGbpR81/6ShAvY2cSKttV3I+2k4epxTTTf0xaZS1eUdnFOox6acElG2reNzx7EYYxpHj17K8N2qNzyY78iPgbJ+L39PBFoiGXMZJqWCxxIHoK1MxlXa8WwSnsXAU768dJvEn2N1x3fl+aeaWzeM4/5Qd83YjFuCeycuRnIo3rejSX3rWFAwZE0qQHKI5YWdKDLxIfdHTjdfMP7np+zLcHt0DV/dHmj2hKQgU0OK04fx7BrmdS1tw67Y9bL3H3TDohn7khU1FrqrKVuqSLbLsxnNyWRbZQF+DCoYrHlIW
152 3fee7f7d2da04226914c2258cc2884dc27384fd7 0 iQIcBAABCAAGBQJZjOJfAAoJELnJ3IJKpb3VvikP/iGjfahwkl2BDZYGq6Ia64a0bhEh0iltoWTCCDKMbHuuO+7h07fHpBl/XX5XPnS7imBUVWLOARhVL7aDPb0tu5NZzMKN57XUC/0FWFyf7lXXAVaOapR4kP8RtQvnoxfNSLRgiZQL88KIRBgFc8pbl8hLA6UbcHPsOk4dXKvmfPfHBHnzdUEDcSXDdyOBhuyOSzRs8egXVi3WeX6OaXG3twkw/uCF3pgOMOSyWVDwD+KvK+IBmSxCTKXzsb+pqpc7pPOFWhSXjpbuYUcI5Qy7mpd0bFL3qNqgvUNq2gX5mT6zH/TsVD10oSUjYYqKMO+gi34OgTVWRRoQfWBwrQwxsC/MxH6ZeOetl2YkS13OxdmYpNAFNQ8ye0vZigJRA+wHoC9dn0h8c5X4VJt/dufHeXc887EGJpLg6GDXi5Emr2ydAUhBJKlpi2yss22AmiQ4G9NE1hAjxqhPvkgBK/hpbr3FurV4hjTG6XKsF8I0WdbYz2CW/FEbp1+4T49ChhrwW0orZdEQX7IEjXr45Hs5sTInT90Hy2XG3Kovi0uVMt15cKsSEYDoFHkR4NgCZX2Y+qS5ryH8yqor3xtel3KsBIy6Ywn8pAo2f8flW3nro/O6x+0NKGV+ZZ0uo/FctuQLBrQVs025T1ai/6MbscQXvFVZVPKrUzlQaNPf/IwNOaRa
152 3fee7f7d2da04226914c2258cc2884dc27384fd7 0 iQIcBAABCAAGBQJZjOJfAAoJELnJ3IJKpb3VvikP/iGjfahwkl2BDZYGq6Ia64a0bhEh0iltoWTCCDKMbHuuO+7h07fHpBl/XX5XPnS7imBUVWLOARhVL7aDPb0tu5NZzMKN57XUC/0FWFyf7lXXAVaOapR4kP8RtQvnoxfNSLRgiZQL88KIRBgFc8pbl8hLA6UbcHPsOk4dXKvmfPfHBHnzdUEDcSXDdyOBhuyOSzRs8egXVi3WeX6OaXG3twkw/uCF3pgOMOSyWVDwD+KvK+IBmSxCTKXzsb+pqpc7pPOFWhSXjpbuYUcI5Qy7mpd0bFL3qNqgvUNq2gX5mT6zH/TsVD10oSUjYYqKMO+gi34OgTVWRRoQfWBwrQwxsC/MxH6ZeOetl2YkS13OxdmYpNAFNQ8ye0vZigJRA+wHoC9dn0h8c5X4VJt/dufHeXc887EGJpLg6GDXi5Emr2ydAUhBJKlpi2yss22AmiQ4G9NE1hAjxqhPvkgBK/hpbr3FurV4hjTG6XKsF8I0WdbYz2CW/FEbp1+4T49ChhrwW0orZdEQX7IEjXr45Hs5sTInT90Hy2XG3Kovi0uVMt15cKsSEYDoFHkR4NgCZX2Y+qS5ryH8yqor3xtel3KsBIy6Ywn8pAo2f8flW3nro/O6x+0NKGV+ZZ0uo/FctuQLBrQVs025T1ai/6MbscQXvFVZVPKrUzlQaNPf/IwNOaRa
153 920977f72c7b70acfdaf56ab35360584d7845827 0 iQIcBAABCAAGBQJZv+wSAAoJELnJ3IJKpb3VH3kQAJp3OkV6qOPXBnlOSSodbVZveEQ5dGJfG9hk+VokcK6MFnieAFouROoGNlQXQtzj6cMqK+LGCP/NeJEG323gAxpxMzc32g7TqbVEhKNqNK8HvQSt04aCVZXtBmP0cPzc348UPP1X1iPTkyZxaJ0kHulaHVptwGbFZZyhwGefauU4eMafJsYqwgiGmvDpjUFu6P8YJXliYeTo1HX2lNChS1xmvJbop1YHfBYACsi8Eron0vMuhaQ+TKYq8Zd762u2roRYnaQ23ubEaVsjGDUYxXXVmit2gdaEKk+6Rq2I+EgcI5XvFzK8gvoP7siz6FL1jVf715k9/UYoWj9KDNUm8cweiyiUpjHQt0S+Ro9ryKvQy6tQVunRZqBN/kZWVth/FlMbUENbxVyXZcXv+m7OLvk+vyK7UZ7yT+OBzgRr0PyUuafzSVW3e+RZJtGxYGM5ew2bWQ8L6wuBucRYZOSnXXtCw7cKEMlK3BTjfAfpHUdIZIG492R9d6aOECUK/MpNvCiXXaZoh5Kj4a0dARiuWFCZxWwt3bmOg13oQ841zLdzOi/YZe15vCm8OB4Ffg6CkmPKhZhnMwVbFmlaBcoaeMzzpMuog91J1M2zgEUBTYwe/HKiNr/0iilJMPFRpZ+zEb2GvVoc8FMttXi8aomlXf/6LHCC9ndexGC29jIzl41+
153 920977f72c7b70acfdaf56ab35360584d7845827 0 iQIcBAABCAAGBQJZv+wSAAoJELnJ3IJKpb3VH3kQAJp3OkV6qOPXBnlOSSodbVZveEQ5dGJfG9hk+VokcK6MFnieAFouROoGNlQXQtzj6cMqK+LGCP/NeJEG323gAxpxMzc32g7TqbVEhKNqNK8HvQSt04aCVZXtBmP0cPzc348UPP1X1iPTkyZxaJ0kHulaHVptwGbFZZyhwGefauU4eMafJsYqwgiGmvDpjUFu6P8YJXliYeTo1HX2lNChS1xmvJbop1YHfBYACsi8Eron0vMuhaQ+TKYq8Zd762u2roRYnaQ23ubEaVsjGDUYxXXVmit2gdaEKk+6Rq2I+EgcI5XvFzK8gvoP7siz6FL1jVf715k9/UYoWj9KDNUm8cweiyiUpjHQt0S+Ro9ryKvQy6tQVunRZqBN/kZWVth/FlMbUENbxVyXZcXv+m7OLvk+vyK7UZ7yT+OBzgRr0PyUuafzSVW3e+RZJtGxYGM5ew2bWQ8L6wuBucRYZOSnXXtCw7cKEMlK3BTjfAfpHUdIZIG492R9d6aOECUK/MpNvCiXXaZoh5Kj4a0dARiuWFCZxWwt3bmOg13oQ841zLdzOi/YZe15vCm8OB4Ffg6CkmPKhZhnMwVbFmlaBcoaeMzzpMuog91J1M2zgEUBTYwe/HKiNr/0iilJMPFRpZ+zEb2GvVoc8FMttXi8aomlXf/6LHCC9ndexGC29jIzl41+
154 2f427b57bf9019c6dc3750baa539dc22c1be50f6 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlnQtVIQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91TTkD/409sWTM9vUH2qkqNTb1IXyGpqzb9UGOSVDioz6rvgZEBgh9D1oBTWnfBXW8sOWR0A7iCL6qZh2Yi7g7p0mKGXh9LZViLtSwwMSXpNiGBO7RVPW+NQ6DOY5Rhr0i08UBiVEkZXHeIVCd2Bd6mhAiUsm5iUh9Jne10wO8cIxeAUnsx4DBdHBMWLg6AZKWllSgN+r9H+7wnOhDbkvj1Cu6+ugKpEs+xvbTh47OTyM+w9tC1aoZD4HhfR5w5O16FC+TIoE6wmWut6e2pxIMHDB3H08Dky6gNjucY/ntJXvOZW5kYrQA3LHKks8ebpjsIXesOAvReOAsDz0drwzbWZan9Cbj8yWoYz/HCgHCnX3WqKKORSP5pvdrsqYua9DXtJwBeSWY4vbIM2kECAiyw1SrOGudxlyWBlW1f1jhGR2DsBlwoieeAvUVoaNwO7pYirwxR4nFPdLDRCQ4hLK/GFiuyr+lGoc1WUzVRNBYD3udcOZAbqq4JhWLf0Gvd5xP0rn1cJNhHMvrPH4Ki4a5KeeK6gQI7GT9/+PPQzTdpxXj6KwofktJtVNqm5sJmJ+wMIddnobFlNNLZ/F7OMONWajuVhh+vSOV34YLdhqzAR5XItkeJL6qyAJjNH5PjsnhT7nMqjgwriPz6xxYOLJWgtK5ZqcSCx4gWy9KJVVja8wJ7rRUg==
154 2f427b57bf9019c6dc3750baa539dc22c1be50f6 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlnQtVIQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91TTkD/409sWTM9vUH2qkqNTb1IXyGpqzb9UGOSVDioz6rvgZEBgh9D1oBTWnfBXW8sOWR0A7iCL6qZh2Yi7g7p0mKGXh9LZViLtSwwMSXpNiGBO7RVPW+NQ6DOY5Rhr0i08UBiVEkZXHeIVCd2Bd6mhAiUsm5iUh9Jne10wO8cIxeAUnsx4DBdHBMWLg6AZKWllSgN+r9H+7wnOhDbkvj1Cu6+ugKpEs+xvbTh47OTyM+w9tC1aoZD4HhfR5w5O16FC+TIoE6wmWut6e2pxIMHDB3H08Dky6gNjucY/ntJXvOZW5kYrQA3LHKks8ebpjsIXesOAvReOAsDz0drwzbWZan9Cbj8yWoYz/HCgHCnX3WqKKORSP5pvdrsqYua9DXtJwBeSWY4vbIM2kECAiyw1SrOGudxlyWBlW1f1jhGR2DsBlwoieeAvUVoaNwO7pYirwxR4nFPdLDRCQ4hLK/GFiuyr+lGoc1WUzVRNBYD3udcOZAbqq4JhWLf0Gvd5xP0rn1cJNhHMvrPH4Ki4a5KeeK6gQI7GT9/+PPQzTdpxXj6KwofktJtVNqm5sJmJ+wMIddnobFlNNLZ/F7OMONWajuVhh+vSOV34YLdhqzAR5XItkeJL6qyAJjNH5PjsnhT7nMqjgwriPz6xxYOLJWgtK5ZqcSCx4gWy9KJVVja8wJ7rRUg==
155 1e2454b60e5936f5e77498cab2648db469504487 0 iQJVBAABCAA/FiEEOoFVFj0OIKUw/LeGR6Z/+qNGqs4FAlnqRBUhHGtidWxsb2NrK21lcmN1cmlhbEByaW5nd29ybGQub3JnAAoJEEemf/qjRqrOAQQP/28EzmTKFL/RxmNYePdzqrmcdJ2tn+s7OYmGdtneN2sESZ4MK0xb5Q8Mkm+41aXS52zzJdz9ynwdun8DG4wZ3sE5MOG+GgK6K0ecOv1XTKS3a2DkUM0fl5hlcXN7Zz7m7m5M6sy6vSxHP7kTyzQWt//z175ZLSQEu1a0nm/BLH+HP9e8DfnJ2Nfcnwp32kV0Nj1xTqjRV1Yo/oCnXfVvsxEJU+CDUGBiLc29ZcoWVbTw9c1VcxihJ6k0pK711KZ+bedSk7yc1OudiJF7idjB0bLQY6ESHNNNjK8uLppok0RsyuhvvDTAoTsl1rMKGmXMM0Ela3/5oxZ/5lUZB73vEJhzEi48ULvstpq82EO39KylkEfQxwMBPhnBIHQaGRkl7QPLXGOYUDMY6gT08Sm3e8/NqEJc/AgckXehpH3gSS2Ji2xg7/E8H5plGsswFidw//oYTTwm0j0halWpB521TD2wmjkjRHXzk1mj0EoFQUMfwHTIZU3E8flUBasD3mZ9XqZJPr66RV7QCrXayH75B/i0CyNqd/Hv5Tkf2TlC3EkEBZwZyAjqw7EyL1LuS936sc7fWuMFsH5k/fwjVwzIc1LmP+nmk2Dd9hIC66vec4w1QZeeAXuDKgOJjvQzj2n+uYRuObl4kKcxvoXqgQN0glGuB1IW7lPllGHR1kplhoub
155 1e2454b60e5936f5e77498cab2648db469504487 0 iQJVBAABCAA/FiEEOoFVFj0OIKUw/LeGR6Z/+qNGqs4FAlnqRBUhHGtidWxsb2NrK21lcmN1cmlhbEByaW5nd29ybGQub3JnAAoJEEemf/qjRqrOAQQP/28EzmTKFL/RxmNYePdzqrmcdJ2tn+s7OYmGdtneN2sESZ4MK0xb5Q8Mkm+41aXS52zzJdz9ynwdun8DG4wZ3sE5MOG+GgK6K0ecOv1XTKS3a2DkUM0fl5hlcXN7Zz7m7m5M6sy6vSxHP7kTyzQWt//z175ZLSQEu1a0nm/BLH+HP9e8DfnJ2Nfcnwp32kV0Nj1xTqjRV1Yo/oCnXfVvsxEJU+CDUGBiLc29ZcoWVbTw9c1VcxihJ6k0pK711KZ+bedSk7yc1OudiJF7idjB0bLQY6ESHNNNjK8uLppok0RsyuhvvDTAoTsl1rMKGmXMM0Ela3/5oxZ/5lUZB73vEJhzEi48ULvstpq82EO39KylkEfQxwMBPhnBIHQaGRkl7QPLXGOYUDMY6gT08Sm3e8/NqEJc/AgckXehpH3gSS2Ji2xg7/E8H5plGsswFidw//oYTTwm0j0halWpB521TD2wmjkjRHXzk1mj0EoFQUMfwHTIZU3E8flUBasD3mZ9XqZJPr66RV7QCrXayH75B/i0CyNqd/Hv5Tkf2TlC3EkEBZwZyAjqw7EyL1LuS936sc7fWuMFsH5k/fwjVwzIc1LmP+nmk2Dd9hIC66vec4w1QZeeAXuDKgOJjvQzj2n+uYRuObl4kKcxvoXqgQN0glGuB1IW7lPllGHR1kplhoub
156 0ccb43d4cf01d013ae05917ec4f305509f851b2d 0 iQJVBAABCAA/FiEEOoFVFj0OIKUw/LeGR6Z/+qNGqs4FAln6Qp8hHGtidWxsb2NrK21lcmN1cmlhbEByaW5nd29ybGQub3JnAAoJEEemf/qjRqrOJ8MP/2ufm/dbrFoE0F8hewhztG1vS4stus13lZ9lmM9kza8OKeOgY/MDH8GaV3O8GnRiCNUFsVD8JEIexE31c84H2Ie7VQO0GQSUHSyMCRrbED6IvfrWp6EZ6RDNPk4LHBfxCuPmuVHGRoGZtsLKJBPIxIHJKWMlEJlj9BZuUxZp/8kurQ6CXwblVbFzXdOaZQlioOBH27Bk3S0+gXfJ+wA2ed5XOQvT9jwjqC8y/1t8obaoPTpzyAvb9NArG+9RT9vfNN42aWISZNwg6RW5oLJISqoGrAes6EoG7dZfOC0UoKMVYXoNvZzJvVlMHyjugIoid+WI+V8y9bPrRTfbPCmocCzEzCOLEHQta8roNijB0bKcq8hmQPHcMyXlj1Srnqlco49jbhftgJoPTwzb10wQyU0VFvaZDPW/EQUT3M/k4j3sVESjANdyG1iu6EDV080LK1LgAdhjpKMBbf6mcgAe06/07XFMbKNrZMEislOcVFp98BSKjdioUNpy91rCeSmkEsASJ3yMArRnSkuVgpyrtJaGWl79VUcmOwKhUOA/8MXMz/Oqu7hvve/sgv71xlnim460nnLw6YHPyeeCsz6KSoUK3knFXAbTk/0jvU1ixUZbI122aMzX04UgPGeTukCOUw49XfaOdN+x0YXlkl4PsrnRQhIoixY2gosPpK4YO73G
156 0ccb43d4cf01d013ae05917ec4f305509f851b2d 0 iQJVBAABCAA/FiEEOoFVFj0OIKUw/LeGR6Z/+qNGqs4FAln6Qp8hHGtidWxsb2NrK21lcmN1cmlhbEByaW5nd29ybGQub3JnAAoJEEemf/qjRqrOJ8MP/2ufm/dbrFoE0F8hewhztG1vS4stus13lZ9lmM9kza8OKeOgY/MDH8GaV3O8GnRiCNUFsVD8JEIexE31c84H2Ie7VQO0GQSUHSyMCRrbED6IvfrWp6EZ6RDNPk4LHBfxCuPmuVHGRoGZtsLKJBPIxIHJKWMlEJlj9BZuUxZp/8kurQ6CXwblVbFzXdOaZQlioOBH27Bk3S0+gXfJ+wA2ed5XOQvT9jwjqC8y/1t8obaoPTpzyAvb9NArG+9RT9vfNN42aWISZNwg6RW5oLJISqoGrAes6EoG7dZfOC0UoKMVYXoNvZzJvVlMHyjugIoid+WI+V8y9bPrRTfbPCmocCzEzCOLEHQta8roNijB0bKcq8hmQPHcMyXlj1Srnqlco49jbhftgJoPTwzb10wQyU0VFvaZDPW/EQUT3M/k4j3sVESjANdyG1iu6EDV080LK1LgAdhjpKMBbf6mcgAe06/07XFMbKNrZMEislOcVFp98BSKjdioUNpy91rCeSmkEsASJ3yMArRnSkuVgpyrtJaGWl79VUcmOwKhUOA/8MXMz/Oqu7hvve/sgv71xlnim460nnLw6YHPyeeCsz6KSoUK3knFXAbTk/0jvU1ixUZbI122aMzX04UgPGeTukCOUw49XfaOdN+x0YXlkl4PsrnRQhIoixY2gosPpK4YO73G
157 cabc840ffdee8a72f3689fb77dd74d04fdc2bc04 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAloB+EYQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91TfwEAC/pYW7TC8mQnqSJzde4yiv2+zgflfJzRlg5rbvlUQl1gSBla3sFADZcic0ebAc+8XUu8eIzyPX+oa4wjsHvL13silUCkUzTEEQLqfKPX1bhA4mwfSDb5A7v2VZ5q8qhRGnlhTsB79ML8uBOhR/Bigdm2ixURPEZ37pWljiMp9XWBMtxPxXn/m0n5CDViibX6QqQCR4k3orcsIGd72YXU6B8NGbBN8qlqMSd0pGvSF4vM2cgVhz7D71+zU4XL/HVP97aU9GsOwN9QWW029DOJu6KG6x51WWtfD/tzyNDu7+lZ5/IKyqHX4tyqCIXEGAsQ3XypeHgCq5hV3E6LJLRqPcLpUNDiQlCg6tNPRaOuMC878MRIlffKqMH+sWo8Z7zHrut+LfRh5/k1aCh4J+FIlE6Hgbvbvv2Z8JxDpUKl0Tr+i0oHNTapbGXIecq1ZFR4kcdchodUHXBC2E6HWR50/ek5YKPddzw8WPGsBtzXMfkhFr3WkvyP2Gbe2XJnkuYptTJA+u2CfhrvgmWsYlvt/myTaMZQEzZ+uir4Xoo5NvzqTL30SFqPrP4Nh0n9G6vpVJl/eZxoYK9jL3VC0vDhnZXitkvDpjXZuJqw/HgExXWKZFfiQ3X2HY48v1gvJiSegZ5rX+uGGJtW2/Mp5FidePEgnFIqZW/yhBfs2Hzj1D2A==
157 cabc840ffdee8a72f3689fb77dd74d04fdc2bc04 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAloB+EYQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91TfwEAC/pYW7TC8mQnqSJzde4yiv2+zgflfJzRlg5rbvlUQl1gSBla3sFADZcic0ebAc+8XUu8eIzyPX+oa4wjsHvL13silUCkUzTEEQLqfKPX1bhA4mwfSDb5A7v2VZ5q8qhRGnlhTsB79ML8uBOhR/Bigdm2ixURPEZ37pWljiMp9XWBMtxPxXn/m0n5CDViibX6QqQCR4k3orcsIGd72YXU6B8NGbBN8qlqMSd0pGvSF4vM2cgVhz7D71+zU4XL/HVP97aU9GsOwN9QWW029DOJu6KG6x51WWtfD/tzyNDu7+lZ5/IKyqHX4tyqCIXEGAsQ3XypeHgCq5hV3E6LJLRqPcLpUNDiQlCg6tNPRaOuMC878MRIlffKqMH+sWo8Z7zHrut+LfRh5/k1aCh4J+FIlE6Hgbvbvv2Z8JxDpUKl0Tr+i0oHNTapbGXIecq1ZFR4kcdchodUHXBC2E6HWR50/ek5YKPddzw8WPGsBtzXMfkhFr3WkvyP2Gbe2XJnkuYptTJA+u2CfhrvgmWsYlvt/myTaMZQEzZ+uir4Xoo5NvzqTL30SFqPrP4Nh0n9G6vpVJl/eZxoYK9jL3VC0vDhnZXitkvDpjXZuJqw/HgExXWKZFfiQ3X2HY48v1gvJiSegZ5rX+uGGJtW2/Mp5FidePEgnFIqZW/yhBfs2Hzj1D2A==
158 a92b9f8e11ba330614cdfd6af0e03b15c1ff3797 0 iQJVBAABCAA/FiEEOoFVFj0OIKUw/LeGR6Z/+qNGqs4FAlohslshHGtidWxsb2NrK21lcmN1cmlhbEByaW5nd29ybGQub3JnAAoJEEemf/qjRqrO7P8P/1qGts96acEdB9BZbK/Eesalb1wUByLXZoP8j+1wWwqh/Kq/q7V4Qe0z1jw/92oZbmnLy2C8sDhWv/XKxACKv69oPrcqQix1E8M+07u88ZXqHJMSxkOmvA2Vimp9EG1qgje+qchgOVgvhEhysA96bRpEnc6V0RnBqI5UdfbKtlfBmX5mUE/qsoBZhly1FTmzV1bhYlGgNLyqtJQpcbA34wyPoywsp8DRBiHWrIzz5XNR+DJFTOe4Kqio1i5r8R4QSIM5vtTbj5pbsmtGcP2CsFC9S3xTSAU6AEJKxGpubPk3ckNj3P9zolvR7krU5Jt8LIgXSVaKLt9rPhmxCbPrLtORgXkUupJcrwzQl+oYz5bkl9kowFa959waIPYoCuuW402mOTDq/L3xwDH9AKK5rELPl3fNo+5OIDKAKRIu6zRSAzBtyGT6kkfb1NSghumP4scR7cgUmLaNibZBa8eJj92gwf+ucSGoB/dF/YHWNe0jY09LFK3nyCoftmyLzxcRk1JLGNngw8MCIuisHTskhxSm/qlX7qjunoZnA3yy9behhy/YaFt4YzYZbMTivt2gszX5ktToaDqfxWDYdIa79kp8G68rYPeybelTS74LwbK3blXPI3I1nddkW52znHYLvW6BYyi+QQ5jPZLkiOC+AF0q+c4gYmPaLVN/mpMZjjmB
158 a92b9f8e11ba330614cdfd6af0e03b15c1ff3797 0 iQJVBAABCAA/FiEEOoFVFj0OIKUw/LeGR6Z/+qNGqs4FAlohslshHGtidWxsb2NrK21lcmN1cmlhbEByaW5nd29ybGQub3JnAAoJEEemf/qjRqrO7P8P/1qGts96acEdB9BZbK/Eesalb1wUByLXZoP8j+1wWwqh/Kq/q7V4Qe0z1jw/92oZbmnLy2C8sDhWv/XKxACKv69oPrcqQix1E8M+07u88ZXqHJMSxkOmvA2Vimp9EG1qgje+qchgOVgvhEhysA96bRpEnc6V0RnBqI5UdfbKtlfBmX5mUE/qsoBZhly1FTmzV1bhYlGgNLyqtJQpcbA34wyPoywsp8DRBiHWrIzz5XNR+DJFTOe4Kqio1i5r8R4QSIM5vtTbj5pbsmtGcP2CsFC9S3xTSAU6AEJKxGpubPk3ckNj3P9zolvR7krU5Jt8LIgXSVaKLt9rPhmxCbPrLtORgXkUupJcrwzQl+oYz5bkl9kowFa959waIPYoCuuW402mOTDq/L3xwDH9AKK5rELPl3fNo+5OIDKAKRIu6zRSAzBtyGT6kkfb1NSghumP4scR7cgUmLaNibZBa8eJj92gwf+ucSGoB/dF/YHWNe0jY09LFK3nyCoftmyLzxcRk1JLGNngw8MCIuisHTskhxSm/qlX7qjunoZnA3yy9behhy/YaFt4YzYZbMTivt2gszX5ktToaDqfxWDYdIa79kp8G68rYPeybelTS74LwbK3blXPI3I1nddkW52znHYLvW6BYyi+QQ5jPZLkiOC+AF0q+c4gYmPaLVN/mpMZjjmB
159 27b6df1b5adbdf647cf5c6675b40575e1b197c60 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlpmbwIQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91W4BD/4h+y7QH7FkNcueOBrmdci7w1apkPX7KuknKxf8+FmA1QDGWYATnqD6IcAk3+f4reO4n9qc0y2BGrIz/pyTSIHvJW+ORrbPCKVrXlfUgkUK3TumtRObt8B75BVBBNaJ93r1yOALpo/K8wSwRrBF+Yl6aCoFiibUEbfcfaOAHVqZXKC1ZPtLRwq5NHIw0wWB0qNoAXj+FJV1EHO7SEjj2lXqw/r0HriQMdObWLgAb6QVUq7oVMpAumUeuQtZ169qHdqYfF1OLdCnsVBcwYEz/cBLC43bvYiwFxSkbAFyl656caWiwA3PISFSzP9Co0zWU/Qf8f7dTdAdT/orzCfUq8YoXqryfRSxi+8L8/EMxankzdW73Rx5X+0539pSq+gDDtTOyNuW6+CZwa5D84b31rsd+jTx8zVm3SRHRKsoGF2EEMQkWmDbhIFjX5W1fE84Ul3umypv+lPSvCPlQpIqv2hZmcTR12sgjdBjU8z+Zcq22SHFybqiYNmWpkVUtiMvTlHMoJfi5PI6xF8D2dxV4ErG+NflqdjaXydgnbO6D3/A1FCASig0wL4jMxSeRqnRRqLihN3VaGG2QH6MLJ+Ty6YuoonKtopw9JNOZydr/XN7K5LcjX1T3+31qmnHZyBXRSejWl9XN93IDbQcnMBWHkz/cJLN0kKu4pvnV8UGUcyXfA==
159 27b6df1b5adbdf647cf5c6675b40575e1b197c60 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlpmbwIQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91W4BD/4h+y7QH7FkNcueOBrmdci7w1apkPX7KuknKxf8+FmA1QDGWYATnqD6IcAk3+f4reO4n9qc0y2BGrIz/pyTSIHvJW+ORrbPCKVrXlfUgkUK3TumtRObt8B75BVBBNaJ93r1yOALpo/K8wSwRrBF+Yl6aCoFiibUEbfcfaOAHVqZXKC1ZPtLRwq5NHIw0wWB0qNoAXj+FJV1EHO7SEjj2lXqw/r0HriQMdObWLgAb6QVUq7oVMpAumUeuQtZ169qHdqYfF1OLdCnsVBcwYEz/cBLC43bvYiwFxSkbAFyl656caWiwA3PISFSzP9Co0zWU/Qf8f7dTdAdT/orzCfUq8YoXqryfRSxi+8L8/EMxankzdW73Rx5X+0539pSq+gDDtTOyNuW6+CZwa5D84b31rsd+jTx8zVm3SRHRKsoGF2EEMQkWmDbhIFjX5W1fE84Ul3umypv+lPSvCPlQpIqv2hZmcTR12sgjdBjU8z+Zcq22SHFybqiYNmWpkVUtiMvTlHMoJfi5PI6xF8D2dxV4ErG+NflqdjaXydgnbO6D3/A1FCASig0wL4jMxSeRqnRRqLihN3VaGG2QH6MLJ+Ty6YuoonKtopw9JNOZydr/XN7K5LcjX1T3+31qmnHZyBXRSejWl9XN93IDbQcnMBWHkz/cJLN0kKu4pvnV8UGUcyXfA==
160 d334afc585e29577f271c5eda03378736a16ca6b 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlpzZuUQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91TiDEADDD6Tn04UjgrZ36nAqOcHaG1ZT2Cm1/sbTw+6duAhf3+uKWFqi2bgcdCBkdfRH7KfEU0GNsPpiC6mzWw3PDWmGhnLJAkR+9FTBU0edK01hkNW8RelDTL5J9IzIGwrP4KFfcUue6yrxU8GnSxnf5Vy/N5ZZzLV/P3hdBte5We9PD5KHPAwTzzcZ9Wiog700rFDDChyFq7hNQ3H0GpknF6+Ck5XmJ3DOqt1MFHk9V4Z/ASU59cQXKOeaMChlBpTb1gIIWjOE99v5aY06dc1WlwttuHtCZvZgtAduRAB6XYWyniS/7nXBv0MXD3EWbpH1pkOaWUxw217HpNP4g9Yo3u/i8UW+NkSJOeXtC1CFjWmUNj138IhS1pogaiPPnIs+H6eOJsmnGhN2KbOMjA5Dn9vSTi6s/98TarfUSiwxA4L7fJy5qowFETftuBO0fJpbB8+ZtpnjNp0MMKed27OUSv69i6BmLrP+eqk+MVO6PovvIySlWAP9/REM/I5/mFkqoI+ruT4a9osNGDZ4Jqb382b7EmpEMDdgb7+ezsybgDfizuaTs/LBae7h79o1m30DxZ/EZ5C+2LY8twbGSORvZN4ViMVhIhWBTlOE/iVBOj807Y2OaUURcuLfHRmaCcfF1uIzg0uNB/aM/WSE0+AXh2IX+mipoTS3eh/V2EKldBHcOQ==
160 d334afc585e29577f271c5eda03378736a16ca6b 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlpzZuUQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91TiDEADDD6Tn04UjgrZ36nAqOcHaG1ZT2Cm1/sbTw+6duAhf3+uKWFqi2bgcdCBkdfRH7KfEU0GNsPpiC6mzWw3PDWmGhnLJAkR+9FTBU0edK01hkNW8RelDTL5J9IzIGwrP4KFfcUue6yrxU8GnSxnf5Vy/N5ZZzLV/P3hdBte5We9PD5KHPAwTzzcZ9Wiog700rFDDChyFq7hNQ3H0GpknF6+Ck5XmJ3DOqt1MFHk9V4Z/ASU59cQXKOeaMChlBpTb1gIIWjOE99v5aY06dc1WlwttuHtCZvZgtAduRAB6XYWyniS/7nXBv0MXD3EWbpH1pkOaWUxw217HpNP4g9Yo3u/i8UW+NkSJOeXtC1CFjWmUNj138IhS1pogaiPPnIs+H6eOJsmnGhN2KbOMjA5Dn9vSTi6s/98TarfUSiwxA4L7fJy5qowFETftuBO0fJpbB8+ZtpnjNp0MMKed27OUSv69i6BmLrP+eqk+MVO6PovvIySlWAP9/REM/I5/mFkqoI+ruT4a9osNGDZ4Jqb382b7EmpEMDdgb7+ezsybgDfizuaTs/LBae7h79o1m30DxZ/EZ5C+2LY8twbGSORvZN4ViMVhIhWBTlOE/iVBOj807Y2OaUURcuLfHRmaCcfF1uIzg0uNB/aM/WSE0+AXh2IX+mipoTS3eh/V2EKldBHcOQ==
161 369aadf7a3264b03c8b09efce715bc41e6ab4a9b 0 iQJVBAABCAA/FiEEOoFVFj0OIKUw/LeGR6Z/+qNGqs4FAlqe5w8hHGtidWxsb2NrK21lcmN1cmlhbEByaW5nd29ybGQub3JnAAoJEEemf/qjRqrO1lUQAK6+S26rE3AMt6667ClT+ubPl+nNMRkWJXa8EyPplBUGTPdMheViOe+28dCsveJxqUF7A4TMLMA/eIj4cRIwmVbBaivfQKnG5GMZ+9N6j6oqE/OAJujdHzzZ3+o9KJGtRgJP2tzdY/6qkXwL3WN6KULz7pSkrKZLOiNfj4k2bf3bXeB7d3N5erxJYlhddlPBlHXImRkWiPR/bdaAaYJq+EEWCbia6MWXlSAqEjIgQi+ytuh/9Z+QSsJCsECDRqEExZClqHGkCLYhST99NqqdYCGJzAFMgh+xWxZxI0LO08pJxYctHGoHm+vvRVMfmdbxEydEy01H6jX+1e7Yq44bovIiIOkaXCTSuEBol+R5aPKJhgvqgZ5IlcTLoIYQBE3MZMKZ89NWy3TvgcNkQiOPCCkKs1+DukXKqTt62zOTxfa6mIZDCXdGai6vZBJ5b0yeEd3HV96yHb9dFlS5w1cG7prIBRv5BkqEaFbRMGZGV31Ri7BuVu0O68Pfdq+R+4A1YLdJ0H5DySe2dGlwE2DMKhdtVu1bie4UWHK10TphmqhBk6B9Ew2+tASCU7iczAqRzyzMLBTHIfCYO2R+5Yuh0CApt47KV23OcLje9nORyE2yaDTbVUPiXzdOnbRaCQf7eW5/1y/LLjG6OwtuETTcHKh7ruko+u7rFL96a4DNlNdk
161 369aadf7a3264b03c8b09efce715bc41e6ab4a9b 0 iQJVBAABCAA/FiEEOoFVFj0OIKUw/LeGR6Z/+qNGqs4FAlqe5w8hHGtidWxsb2NrK21lcmN1cmlhbEByaW5nd29ybGQub3JnAAoJEEemf/qjRqrO1lUQAK6+S26rE3AMt6667ClT+ubPl+nNMRkWJXa8EyPplBUGTPdMheViOe+28dCsveJxqUF7A4TMLMA/eIj4cRIwmVbBaivfQKnG5GMZ+9N6j6oqE/OAJujdHzzZ3+o9KJGtRgJP2tzdY/6qkXwL3WN6KULz7pSkrKZLOiNfj4k2bf3bXeB7d3N5erxJYlhddlPBlHXImRkWiPR/bdaAaYJq+EEWCbia6MWXlSAqEjIgQi+ytuh/9Z+QSsJCsECDRqEExZClqHGkCLYhST99NqqdYCGJzAFMgh+xWxZxI0LO08pJxYctHGoHm+vvRVMfmdbxEydEy01H6jX+1e7Yq44bovIiIOkaXCTSuEBol+R5aPKJhgvqgZ5IlcTLoIYQBE3MZMKZ89NWy3TvgcNkQiOPCCkKs1+DukXKqTt62zOTxfa6mIZDCXdGai6vZBJ5b0yeEd3HV96yHb9dFlS5w1cG7prIBRv5BkqEaFbRMGZGV31Ri7BuVu0O68Pfdq+R+4A1YLdJ0H5DySe2dGlwE2DMKhdtVu1bie4UWHK10TphmqhBk6B9Ew2+tASCU7iczAqRzyzMLBTHIfCYO2R+5Yuh0CApt47KV23OcLje9nORyE2yaDTbVUPiXzdOnbRaCQf7eW5/1y/LLjG6OwtuETTcHKh7ruko+u7rFL96a4DNlNdk
162 8bba684efde7f45add05f737952093bb2aa07155 0 iQJVBAABCAA/FiEEOoFVFj0OIKUw/LeGR6Z/+qNGqs4FAlqe6dkhHGtidWxsb2NrK21lcmN1cmlhbEByaW5nd29ybGQub3JnAAoJEEemf/qjRqrOJmIQALUVCoWUFYYaRxGH4OpmIQ2o1JrMefvarFhaPY1r3+G87sjXgw15uobEQDtoybTUYbcdSxJQT1KE1FOm3wU0VyN6PY9c1PMEAVgJlve0eDiXNNlBsoYMXnpq1HidZknkjpXgUPdE/LElxpJJRlJQZlS29bkGmEDZQBoOvlcZoBRDSYcbM07wn7d+1gmJkcHViDBMAbSrudfO0OYzDC1BjtGyKm7Mes2WB1yFYw+ySa8hF/xPKEDvoZINOE5n3PBJiCvPuTw3PqsHvWgKOA1Obx9fATlxj7EHBLfKBTNfpUwPMRSH1cmA+qUS9mRDrdLvrThwalr6D3r2RJ2ntOipcZpKMmxARRV+VUAI1K6H0/Ws3XAxENqhF7RgRruJFVq8G8EcHJLZEoVHsR+VOnd/pzgkFKS+tIsYYRcMpL0DdMF8pV3xrEFahgRhaEZOh4jsG3Z+sGLVFFl7DdMqeGs6m/TwDrvfuYtGczfGRB0wqu8KOwhR1BjNJKcr4lk35GKwSXmI1vk6Z1gAm0e13995lqbCJwkuOKynQlHWVOR6hu3ypvAgV/zXLF5t8HHtL48sOJ8a33THuJT4whbXSIb9BQXu/NQnNhK8G3Kly5UN88vL4a3sZi/Y86h4R2fKOSib/txJ3ydLbMeS8LlJMqeF/hrBanVF0r15NZ2CdmL1Qxim
162 8bba684efde7f45add05f737952093bb2aa07155 0 iQJVBAABCAA/FiEEOoFVFj0OIKUw/LeGR6Z/+qNGqs4FAlqe6dkhHGtidWxsb2NrK21lcmN1cmlhbEByaW5nd29ybGQub3JnAAoJEEemf/qjRqrOJmIQALUVCoWUFYYaRxGH4OpmIQ2o1JrMefvarFhaPY1r3+G87sjXgw15uobEQDtoybTUYbcdSxJQT1KE1FOm3wU0VyN6PY9c1PMEAVgJlve0eDiXNNlBsoYMXnpq1HidZknkjpXgUPdE/LElxpJJRlJQZlS29bkGmEDZQBoOvlcZoBRDSYcbM07wn7d+1gmJkcHViDBMAbSrudfO0OYzDC1BjtGyKm7Mes2WB1yFYw+ySa8hF/xPKEDvoZINOE5n3PBJiCvPuTw3PqsHvWgKOA1Obx9fATlxj7EHBLfKBTNfpUwPMRSH1cmA+qUS9mRDrdLvrThwalr6D3r2RJ2ntOipcZpKMmxARRV+VUAI1K6H0/Ws3XAxENqhF7RgRruJFVq8G8EcHJLZEoVHsR+VOnd/pzgkFKS+tIsYYRcMpL0DdMF8pV3xrEFahgRhaEZOh4jsG3Z+sGLVFFl7DdMqeGs6m/TwDrvfuYtGczfGRB0wqu8KOwhR1BjNJKcr4lk35GKwSXmI1vk6Z1gAm0e13995lqbCJwkuOKynQlHWVOR6hu3ypvAgV/zXLF5t8HHtL48sOJ8a33THuJT4whbXSIb9BQXu/NQnNhK8G3Kly5UN88vL4a3sZi/Y86h4R2fKOSib/txJ3ydLbMeS8LlJMqeF/hrBanVF0r15NZ2CdmL1Qxim
163 7de7bd407251af2bc98e5b809c8598ee95830daf 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlrE4p0QHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91c4UD/4tC+mBWxBw/JYm4vlFTKWLHopLEa1/uhFRK/uGsdgcCyexbCDbisjJpl3JTQb+wQDlZnUorm8zB206y418YqhJ7lCauRgcoqKka0e3kvKnwmklwmuGkwOIoruWxxhCcgRCT4C+jZ/ZE3Kre0CKnUvlASsHtbkqrCqFClEcIlPVohlccmjbpQXN+akB40tkMF5Xf0AMBPYG7UievmeHhz3pO/yex/Uc6RhgWAqD4zjA1bh+3REGs3CaoYgKUTXZw/XYI9cqAI0FobRuXSVbq2dqkXCFLfD+WizxUz55rZA+CP4pqLndwxGm4fLy4gk2iLHxKfrHsAul7n5e4tHmxDcOOa1K0fIJDBijuXoNfXN7nF4NQUlfpmtOxUxfniVohvXJeYV8ecepsDMSFqDtEtbdhsep5QDx85lGLNLQAA1f36swJzLBSqGw688Hjql2c9txK2eVrVxNp+M8tqn9qU/h2/firgu9a2DxQB45M7ISfkutmpizN5TNlEyElH0htHnKG7+AIbRAm4novCXfSzP8eepk0kVwj9QMIx/rw4aeicRdPWBTcDIG0gWELb0skunTQqeZwPPESwimntdmwCxfFksgT0t79ZEDAWWfxNLhJP/HWO2mYG5GUJOzNQ4rj/YXLcye6A4KkhvuZlVCaKAbnm60ivoG082HYuozV4qPOQ==
163 7de7bd407251af2bc98e5b809c8598ee95830daf 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlrE4p0QHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91c4UD/4tC+mBWxBw/JYm4vlFTKWLHopLEa1/uhFRK/uGsdgcCyexbCDbisjJpl3JTQb+wQDlZnUorm8zB206y418YqhJ7lCauRgcoqKka0e3kvKnwmklwmuGkwOIoruWxxhCcgRCT4C+jZ/ZE3Kre0CKnUvlASsHtbkqrCqFClEcIlPVohlccmjbpQXN+akB40tkMF5Xf0AMBPYG7UievmeHhz3pO/yex/Uc6RhgWAqD4zjA1bh+3REGs3CaoYgKUTXZw/XYI9cqAI0FobRuXSVbq2dqkXCFLfD+WizxUz55rZA+CP4pqLndwxGm4fLy4gk2iLHxKfrHsAul7n5e4tHmxDcOOa1K0fIJDBijuXoNfXN7nF4NQUlfpmtOxUxfniVohvXJeYV8ecepsDMSFqDtEtbdhsep5QDx85lGLNLQAA1f36swJzLBSqGw688Hjql2c9txK2eVrVxNp+M8tqn9qU/h2/firgu9a2DxQB45M7ISfkutmpizN5TNlEyElH0htHnKG7+AIbRAm4novCXfSzP8eepk0kVwj9QMIx/rw4aeicRdPWBTcDIG0gWELb0skunTQqeZwPPESwimntdmwCxfFksgT0t79ZEDAWWfxNLhJP/HWO2mYG5GUJOzNQ4rj/YXLcye6A4KkhvuZlVCaKAbnm60ivoG082HYuozV4qPOQ==
164 ed5448edcbfa747b9154099e18630e49024fd47b 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlrXnuoQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91fSHEACBVg4FsCE2nN5aEKAQb7l7rG4XTQ9FbvoTYB3tkvmsLQSRfh2GB2ZDBOI7Vswo2UxXupr4qSkUQbeHrwrk9A1s5b/T5e4wSKZuFJOrkwLVZDFfUHumKomqdoVj/D8+LDt7Rz+Wm7OClO/4dTAsl2E4rkl7XPtqjC3jESGad8IBANlPVBhNUMER4eFcPZzq1qi2MrlJKEKpdeZEWJ/ow7gka/aTLqHMfRwhA3kS5X34Yai17kLQZGQdWISWYiM9Zd2b/FSTHZGy8rf9cvjXs3EXfEB5nePveDrFOfmuubVRDplO+/naJjNBqwxeB99jb7Fk3sekPZNW/NqR/w1jvQFA3OP9fS2g1OwfXMWyx6DvBJNfQwppNH3JUvA5PEiorul4GJ2nuubXk+Or1yzoRJtwOGz/GQi2BcsPKaL6niewrInFw18jMVhx/4Jbpu+glaim4EvT/PfJ5KdSwF7pJxsoiqvw7A2C2/DsZRbCeal9GrTulkNf/hgpCJOBK1DqVVq1O5MI/oYQ69HxgMq9Ip1OGJJhse3qjevBJbpNCosCpjb3htlo4go29H8yyGJb09i05WtNW2EQchrTHrlruFr7mKJ5h1mAYket74QQyaGzqwgD5kwSVnIcwHpfb8oiJTwA5R+LtbAQXWC/fFu1g1KEp/4hGOQoRU04+mYuPsrzaA==
164 ed5448edcbfa747b9154099e18630e49024fd47b 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlrXnuoQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91fSHEACBVg4FsCE2nN5aEKAQb7l7rG4XTQ9FbvoTYB3tkvmsLQSRfh2GB2ZDBOI7Vswo2UxXupr4qSkUQbeHrwrk9A1s5b/T5e4wSKZuFJOrkwLVZDFfUHumKomqdoVj/D8+LDt7Rz+Wm7OClO/4dTAsl2E4rkl7XPtqjC3jESGad8IBANlPVBhNUMER4eFcPZzq1qi2MrlJKEKpdeZEWJ/ow7gka/aTLqHMfRwhA3kS5X34Yai17kLQZGQdWISWYiM9Zd2b/FSTHZGy8rf9cvjXs3EXfEB5nePveDrFOfmuubVRDplO+/naJjNBqwxeB99jb7Fk3sekPZNW/NqR/w1jvQFA3OP9fS2g1OwfXMWyx6DvBJNfQwppNH3JUvA5PEiorul4GJ2nuubXk+Or1yzoRJtwOGz/GQi2BcsPKaL6niewrInFw18jMVhx/4Jbpu+glaim4EvT/PfJ5KdSwF7pJxsoiqvw7A2C2/DsZRbCeal9GrTulkNf/hgpCJOBK1DqVVq1O5MI/oYQ69HxgMq9Ip1OGJJhse3qjevBJbpNCosCpjb3htlo4go29H8yyGJb09i05WtNW2EQchrTHrlruFr7mKJ5h1mAYket74QQyaGzqwgD5kwSVnIcwHpfb8oiJTwA5R+LtbAQXWC/fFu1g1KEp/4hGOQoRU04+mYuPsrzaA==
165 1ec874717d8a93b19e0d50628443e0ee5efab3a9 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlraM3wQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91RAJEACSnf/HWwS0/OZaqz4Hfh0UBgkXDmH1IC90Pc/kczf//WuXu5AVnnRHDziOlCYYZAnZ2iKu0EQI6GT2K2garaWkaEhukOnjz4WADVys6DAzJyw5iOXeEpIOlZH6hbYbsW3zVcPjiMPo8cY5tIYEy4E/8RcVly1SDtWxvt/nWYQd2MxObLrpU7bPP6a2Db4Vy8WpGRbZRJmOvDNworld5rB5M/OGgHyMa9hg2Hjn+cLtQSEJY4O92A6h2hix9xpDC7zzfoluD2piDslocTm/gyeln2BJJBAtr+aRoHO9hI0baq5yFRQLO8aqQRJJP8dXgYZIWgSU/9oVGPZoGotJyw24iiB37R/YCisKE+cEUjfVclHTDFCkzmYP2ZMbGaktohJeF7EMau0ZJ8II5F0ja3bj6GrwfpGGY5OOcQrzIYW7nB0msFWTljb34qN3nd7m+hQ5hji3Hp9CFXEbCboVmm46LqwukSDWTmnfcP8knxWbBlJ4xDxySwTtcHAJhnUmKxu7oe3D/0Ttdv7HscI40eeMdr01pLQ0Ee3a4OumQ1hn+oL+o+tlqg8PKT20q528CMHgSJp6aIlU7pEK81b+Zj6B57us4P97qSL6XLNUIfubADCaf/KUDwh1HvKhHXV2aRli1GX1REFsy0ItGZn0yhQxIDJKc/FKsEMBKvlVIHGQFw==
165 1ec874717d8a93b19e0d50628443e0ee5efab3a9 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlraM3wQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91RAJEACSnf/HWwS0/OZaqz4Hfh0UBgkXDmH1IC90Pc/kczf//WuXu5AVnnRHDziOlCYYZAnZ2iKu0EQI6GT2K2garaWkaEhukOnjz4WADVys6DAzJyw5iOXeEpIOlZH6hbYbsW3zVcPjiMPo8cY5tIYEy4E/8RcVly1SDtWxvt/nWYQd2MxObLrpU7bPP6a2Db4Vy8WpGRbZRJmOvDNworld5rB5M/OGgHyMa9hg2Hjn+cLtQSEJY4O92A6h2hix9xpDC7zzfoluD2piDslocTm/gyeln2BJJBAtr+aRoHO9hI0baq5yFRQLO8aqQRJJP8dXgYZIWgSU/9oVGPZoGotJyw24iiB37R/YCisKE+cEUjfVclHTDFCkzmYP2ZMbGaktohJeF7EMau0ZJ8II5F0ja3bj6GrwfpGGY5OOcQrzIYW7nB0msFWTljb34qN3nd7m+hQ5hji3Hp9CFXEbCboVmm46LqwukSDWTmnfcP8knxWbBlJ4xDxySwTtcHAJhnUmKxu7oe3D/0Ttdv7HscI40eeMdr01pLQ0Ee3a4OumQ1hn+oL+o+tlqg8PKT20q528CMHgSJp6aIlU7pEK81b+Zj6B57us4P97qSL6XLNUIfubADCaf/KUDwh1HvKhHXV2aRli1GX1REFsy0ItGZn0yhQxIDJKc/FKsEMBKvlVIHGQFw==
166 6614cac550aea66d19c601e45efd1b7bd08d7c40 0 iQJVBAABCAA/FiEEOoFVFj0OIKUw/LeGR6Z/+qNGqs4FAlruOCQhHGtidWxsb2NrK21lcmN1cmlhbEByaW5nd29ybGQub3JnAAoJEEemf/qjRqrOENQQAI1ttaffqYucUEyBARP1GDlZMIGDJgNG7smPMU4Sw7YEzB9mcmxnBFlPx/9n973ucEnLJVONBSZq0VWIKJwPp1RMBpAHuGrMlhkMvYIAukg5EBN3YpA1UogHYycwLj2Ye7fNgiN5FIkaodt9++c4d1Lfu658A2pAeg8qUn5uJ77vVcZRp988u9eVDQfubS8P6bB4KZc87VDAUUeXy+AcS9KHGBmdRAabwU4m09VPZ4h8NEj3+YUPnKXBaNK9pXK5pnkmB8uFePayimnw6St6093oylQTVw/tfxGLBImnHw+6KCu2ut9r5PxXEVxVYpranGbS4jYqpzRtpQBxyo/Igu7fqrioR2rGLQL5NcHsoUEdOC7VW+0HgHjXKtRy7agmcFcgjFco47D3hor7Y16lwgm+RV2EWQ/u2M4Bbo1EWj1oxQ/0j5DOM5UeAJ3Jh64gb4sCDqJfADR8NQaxh7QiqYhn69IcjsEfzU/11VuqWXlQgghJhEEP/bojRyM0qee87CKLiTescafIfnRsNQhyhsKqdHU1QAp29cCqh3mzNxJH3PDYg4fjRaGW4PM7K5gmSXFn/Ifeza0cuZ4XLdYZ76Z1BG80pqBpKZy1unGob+RpItlSmO5jQw7OoRuf0q3Id92gawUDDLuQ7Xg3zOVqV8/wJBlHM7ZUz162bnNsO5Hn
166 6614cac550aea66d19c601e45efd1b7bd08d7c40 0 iQJVBAABCAA/FiEEOoFVFj0OIKUw/LeGR6Z/+qNGqs4FAlruOCQhHGtidWxsb2NrK21lcmN1cmlhbEByaW5nd29ybGQub3JnAAoJEEemf/qjRqrOENQQAI1ttaffqYucUEyBARP1GDlZMIGDJgNG7smPMU4Sw7YEzB9mcmxnBFlPx/9n973ucEnLJVONBSZq0VWIKJwPp1RMBpAHuGrMlhkMvYIAukg5EBN3YpA1UogHYycwLj2Ye7fNgiN5FIkaodt9++c4d1Lfu658A2pAeg8qUn5uJ77vVcZRp988u9eVDQfubS8P6bB4KZc87VDAUUeXy+AcS9KHGBmdRAabwU4m09VPZ4h8NEj3+YUPnKXBaNK9pXK5pnkmB8uFePayimnw6St6093oylQTVw/tfxGLBImnHw+6KCu2ut9r5PxXEVxVYpranGbS4jYqpzRtpQBxyo/Igu7fqrioR2rGLQL5NcHsoUEdOC7VW+0HgHjXKtRy7agmcFcgjFco47D3hor7Y16lwgm+RV2EWQ/u2M4Bbo1EWj1oxQ/0j5DOM5UeAJ3Jh64gb4sCDqJfADR8NQaxh7QiqYhn69IcjsEfzU/11VuqWXlQgghJhEEP/bojRyM0qee87CKLiTescafIfnRsNQhyhsKqdHU1QAp29cCqh3mzNxJH3PDYg4fjRaGW4PM7K5gmSXFn/Ifeza0cuZ4XLdYZ76Z1BG80pqBpKZy1unGob+RpItlSmO5jQw7OoRuf0q3Id92gawUDDLuQ7Xg3zOVqV8/wJBlHM7ZUz162bnNsO5Hn
167 9c5ced5276d6e7d54f7c3dadf5247b7ee98ec79c 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlsYGdAQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91S3fEACmrG3S5eAUhnKqkXFe+HZUwmUvLKRhyWDLlWQzEHaJZQCFWxqSM1ag7JtAx3WkWwmWrOZ0+T/w/xMv81h9JAv9RsoszUT/RH4RsnWoc2ddcK93Q/PrNJ29kFjvC8j3LF42WfHEIeNqAki5c3GbprUL86KG7XVYuMvpPI/SeNSz8siPaKjXo6sg6bAupPCyapisTmeRHcCUc5UfeTTq4YQdS9UI0p9Fo8/vcqmnWY6XnQCRYs2U8Y2I2QCJBHBE5p4KrxrFsAdPWMCg0dJT0goSbzpfDjukPHQaAnUKjCtXCwrzA/KY8fDH9hm5tt1FnC6nl6BRpEHRoHqTfE1ag2QktJZTn5+JWpzz85qFDl5ktmxj1gS80jkOUJ2699RykBy7NACu+TtLJdBk+E1TN0pAU+zsrTSGiteuikEBjQP/8i4whUZCFIHLPgVlxrHWwn0/oszj1Q/u86sCxnYTflR2GLZs3fbSGBEKDDrjqwetxMlwi/3Qhf0PN9aAI7S13YnA89tGLGRLTsVsOoKiQoTExQaCUpE5jFYBLVjsTPh2AjPhG3Zaf7R5ZIvW4CbVYORNTMaYhFNnFyczILJLRid+INHLVifNiJuaLiAFD5Izq9Me4H+GpwB5AI7aG1r+01Si2KbqqpdfoK430UeDV+U/MvEU7v0RoeF30M7uVYv+kg==
167 9c5ced5276d6e7d54f7c3dadf5247b7ee98ec79c 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlsYGdAQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91S3fEACmrG3S5eAUhnKqkXFe+HZUwmUvLKRhyWDLlWQzEHaJZQCFWxqSM1ag7JtAx3WkWwmWrOZ0+T/w/xMv81h9JAv9RsoszUT/RH4RsnWoc2ddcK93Q/PrNJ29kFjvC8j3LF42WfHEIeNqAki5c3GbprUL86KG7XVYuMvpPI/SeNSz8siPaKjXo6sg6bAupPCyapisTmeRHcCUc5UfeTTq4YQdS9UI0p9Fo8/vcqmnWY6XnQCRYs2U8Y2I2QCJBHBE5p4KrxrFsAdPWMCg0dJT0goSbzpfDjukPHQaAnUKjCtXCwrzA/KY8fDH9hm5tt1FnC6nl6BRpEHRoHqTfE1ag2QktJZTn5+JWpzz85qFDl5ktmxj1gS80jkOUJ2699RykBy7NACu+TtLJdBk+E1TN0pAU+zsrTSGiteuikEBjQP/8i4whUZCFIHLPgVlxrHWwn0/oszj1Q/u86sCxnYTflR2GLZs3fbSGBEKDDrjqwetxMlwi/3Qhf0PN9aAI7S13YnA89tGLGRLTsVsOoKiQoTExQaCUpE5jFYBLVjsTPh2AjPhG3Zaf7R5ZIvW4CbVYORNTMaYhFNnFyczILJLRid+INHLVifNiJuaLiAFD5Izq9Me4H+GpwB5AI7aG1r+01Si2KbqqpdfoK430UeDV+U/MvEU7v0RoeF30M7uVYv+kg==
168 0b63a6743010dfdbf8a8154186e119949bdaa1cc 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAls7n+0QHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91XVGEAC1aPuUmW9R0QjWUmyY4vMO7AOT4F1sHKrkgNaoG/RCvczuZOCz/fGliEKQ52pkvThrOgOvNfJlIGOu91noLKsYUybO8eeTksCzc7agUjk6/Xsed35D8gNEPuiVTNu379sTQRnOA2T/plQnVCY2PjMzBe6nQ2DJYnggJelCUxuqUsLM76OvMEeNlXvyxZmyAcFT5dfSBYbjAt0kklRRQWgaug3GwLJY/+0tmXhq0tCpAF6myXoVQm/ynSxjR+5+2/+F5nudOQmDnL0zGayOAQU97RLAAxf1L+3DTRfbtxams9ZrGfRzQGcI1d4I4ernfnFYI19kSzMPcW4qI7gQQlTfOzs8X5d2fKiqUFjlgOO42hgM6cQv2Hx3u+bxF00sAvrW8sWRjfMQACuNH3FJoeIubpohN5o1Madv4ayGAZkcyskYRCs9X40gn+Q9gv34uknjaF/mep7BBl08JC9zFqwGaLyCssSsHV7ncekkUZfcWfq4TNNEUZFIu7UtsnZYz0aYrueAKMp+4udTjfKKnSZL2o0n1g11iH9KTQO/dWP7rVbu/OIbLeE+D87oXOWGfDNBRyHLItrM70Vum0HxtFuWc1clj8qzF61Mx0umFfUmdGQcl9DGivmc7TLNzBKG11ElDuDIey6Yxc6nwWiAJ6v1H5bO3WBi/klbT2fWguOo5w==
168 0b63a6743010dfdbf8a8154186e119949bdaa1cc 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAls7n+0QHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91XVGEAC1aPuUmW9R0QjWUmyY4vMO7AOT4F1sHKrkgNaoG/RCvczuZOCz/fGliEKQ52pkvThrOgOvNfJlIGOu91noLKsYUybO8eeTksCzc7agUjk6/Xsed35D8gNEPuiVTNu379sTQRnOA2T/plQnVCY2PjMzBe6nQ2DJYnggJelCUxuqUsLM76OvMEeNlXvyxZmyAcFT5dfSBYbjAt0kklRRQWgaug3GwLJY/+0tmXhq0tCpAF6myXoVQm/ynSxjR+5+2/+F5nudOQmDnL0zGayOAQU97RLAAxf1L+3DTRfbtxams9ZrGfRzQGcI1d4I4ernfnFYI19kSzMPcW4qI7gQQlTfOzs8X5d2fKiqUFjlgOO42hgM6cQv2Hx3u+bxF00sAvrW8sWRjfMQACuNH3FJoeIubpohN5o1Madv4ayGAZkcyskYRCs9X40gn+Q9gv34uknjaF/mep7BBl08JC9zFqwGaLyCssSsHV7ncekkUZfcWfq4TNNEUZFIu7UtsnZYz0aYrueAKMp+4udTjfKKnSZL2o0n1g11iH9KTQO/dWP7rVbu/OIbLeE+D87oXOWGfDNBRyHLItrM70Vum0HxtFuWc1clj8qzF61Mx0umFfUmdGQcl9DGivmc7TLNzBKG11ElDuDIey6Yxc6nwWiAJ6v1H5bO3WBi/klbT2fWguOo5w==
169 e90130af47ce8dd53a3109aed9d15876b3e7dee8 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAltQ1bUQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91RQVD/9NA5t2mlt7pFc0Sswktc5dI8GaSYxgeknacLkEdkYx9L+mzg77G7TGueeu5duovjdI/vDIzdadGtJJ+zJE5icCqeUFDfNZNZLQ+7StuC8/f+4i/DaCzjHJ4tDYd0x6R5efisLWRKkWoodI1Iit7gCL493gj1HZaIzRLaqYkbOk3PhOEkTcov2cnhb4h54OKm07qlg6PYH507WGmmTDDnhL9SwdfBXHA2ps9dCe52NzPMyebXoZYA9T5Yz67eQ8D+YCh9bLauA59dW0Iyx59yGJ0tmLwVKBgbUkynAknwk/hdNlF7r6wLqbR00NLKmAZl8crdVSqFUU/vAsPQLn3BkbtpzqjmisIq2BWEt/YWYZOHUvJoK81cRcsVpPuAOIQM/rTm9pprTq7RFtuVnCj+QnmWwEPZJcS/7pnnIXte3gQt76ovLuFxr7dq99anEA7gnTbSdADIzgZhJMM8hJcrcgvbI4xz0H1qKn3webTNl/jPgTsNjAPYcmRZcoU2wUIR+OPhZvfwhvreRX0dGUV6gqxWnx3u3dsWE9jcBIGlNfYnIkLXyqBdOL6f4yQoxaVjRg/ScEt3hU17TknuPIDOXE/iMgWnYpnTqKBolt/Vbx7qB1OiK7AmQvXY1bnhtkIfOoIwZ9X1Zi2vmV1Wz4G0a5Vxq5eNKpQgACA2HE0MS2HQ==
169 e90130af47ce8dd53a3109aed9d15876b3e7dee8 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAltQ1bUQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91RQVD/9NA5t2mlt7pFc0Sswktc5dI8GaSYxgeknacLkEdkYx9L+mzg77G7TGueeu5duovjdI/vDIzdadGtJJ+zJE5icCqeUFDfNZNZLQ+7StuC8/f+4i/DaCzjHJ4tDYd0x6R5efisLWRKkWoodI1Iit7gCL493gj1HZaIzRLaqYkbOk3PhOEkTcov2cnhb4h54OKm07qlg6PYH507WGmmTDDnhL9SwdfBXHA2ps9dCe52NzPMyebXoZYA9T5Yz67eQ8D+YCh9bLauA59dW0Iyx59yGJ0tmLwVKBgbUkynAknwk/hdNlF7r6wLqbR00NLKmAZl8crdVSqFUU/vAsPQLn3BkbtpzqjmisIq2BWEt/YWYZOHUvJoK81cRcsVpPuAOIQM/rTm9pprTq7RFtuVnCj+QnmWwEPZJcS/7pnnIXte3gQt76ovLuFxr7dq99anEA7gnTbSdADIzgZhJMM8hJcrcgvbI4xz0H1qKn3webTNl/jPgTsNjAPYcmRZcoU2wUIR+OPhZvfwhvreRX0dGUV6gqxWnx3u3dsWE9jcBIGlNfYnIkLXyqBdOL6f4yQoxaVjRg/ScEt3hU17TknuPIDOXE/iMgWnYpnTqKBolt/Vbx7qB1OiK7AmQvXY1bnhtkIfOoIwZ9X1Zi2vmV1Wz4G0a5Vxq5eNKpQgACA2HE0MS2HQ==
170 33ac6a72308a215e6086fbced347ec10aa963b0a 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlthwaIQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91atOD/0de4nA55WJpiQzAqTg4xWIRZB6y0pkQ8D4cKNQkNiwPQAdDEPf85RuYmoPusNxhM40qfJlmHOw8sbRaqqabhVBPEzL1DpKe4GBucagLZqoL3pycyMzhkhzMka2RJT6nekCchTKJTIs2gx4FOA/QwaFYNkXFfguAEvi01isVdMo0GFLQ7pf7wU8UO1PPdkYphH0xPUvsreQ3pR3+6WwMLovk4JYW4cSaM4YkLlqJQPSO2YAlyXAwiQRvu2A227ydVqHOgLeV5zMQPy2v2zTgl2AoMdWp8+g2lJrYwclkNR+LAk5OlGYamyZwlmsTO7OX3n7xJYtfjbqdoqEKhO1igMi3ZSjqwkaBxxkXxArrteD19bpUyInTjbwTRO3mSe5aNkEDGoOYWn8UOn5ZkeEo7NyhP4OTXqyxQs9rwjD79xZk+6fGB777vuZDUdLZYRQFOPEximpmCGJDrZWj5PeIALWkrRGWBl2eFJ5sl6/pFlUJDjDEstnrsfosp6NJ3VFiD9EunFWsTlV2qXaueh9+TfaSRmGHVuwFCDt7nATVEzTt8l74xsL3xUPS4u9EcNPuEhCRu1zLojCGjemEA29R9tJS8oWd6SwXKryzjo8SyN7yQVSM/yl212IOiOHTQF8vVZuJnailtcWc3D4NoOxntnnv8fnd1nr8M5QSjYQVzSkHw==
170 33ac6a72308a215e6086fbced347ec10aa963b0a 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlthwaIQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91atOD/0de4nA55WJpiQzAqTg4xWIRZB6y0pkQ8D4cKNQkNiwPQAdDEPf85RuYmoPusNxhM40qfJlmHOw8sbRaqqabhVBPEzL1DpKe4GBucagLZqoL3pycyMzhkhzMka2RJT6nekCchTKJTIs2gx4FOA/QwaFYNkXFfguAEvi01isVdMo0GFLQ7pf7wU8UO1PPdkYphH0xPUvsreQ3pR3+6WwMLovk4JYW4cSaM4YkLlqJQPSO2YAlyXAwiQRvu2A227ydVqHOgLeV5zMQPy2v2zTgl2AoMdWp8+g2lJrYwclkNR+LAk5OlGYamyZwlmsTO7OX3n7xJYtfjbqdoqEKhO1igMi3ZSjqwkaBxxkXxArrteD19bpUyInTjbwTRO3mSe5aNkEDGoOYWn8UOn5ZkeEo7NyhP4OTXqyxQs9rwjD79xZk+6fGB777vuZDUdLZYRQFOPEximpmCGJDrZWj5PeIALWkrRGWBl2eFJ5sl6/pFlUJDjDEstnrsfosp6NJ3VFiD9EunFWsTlV2qXaueh9+TfaSRmGHVuwFCDt7nATVEzTt8l74xsL3xUPS4u9EcNPuEhCRu1zLojCGjemEA29R9tJS8oWd6SwXKryzjo8SyN7yQVSM/yl212IOiOHTQF8vVZuJnailtcWc3D4NoOxntnnv8fnd1nr8M5QSjYQVzSkHw==
171 ede3bf31fe63677fdf5bd8db687977d4e3d792ed 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAluOq84QHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91ao3D/oC9zKNbk+MMUP0cSfl+ESRbP/sAI466IYDkr9f1klooIFMsdqCd16eS36DVwIwrBYapRaNszC6Pg0KCFKCdeAWJLcgeIawwOkZPrLKQmS3I9GTl9gxtExeFvRryaAdP1DAPEU6JkyHo3xmURkJB58VjuBquZz4cYnL2aE1ag04CWAoRFiLu6bt1hEZ8pONU6cbDpHaJVyUZmJRB+llpybgdLnlBTrhfWjNofTh8MM6+vz67lIienYoSbepY+029J98phBTV+UEfWSBWw1hcNT/+QmOBGWWTLfBARsNDZFeYgQQOo3gRghKO7qUA/hqzDTmMG4/a2obs0LGsBlcMZ1Ky//zhdAJ/EN7uH9svM1t1fkw1RgvftmybptK5KiusZ9AWhnggHSwZtj1I6i/sojqsj9MrtdrD+1LfiKuAv/FtcMHSeff8IfItrd2B67JIj4wCzU8vDrAbAAqODHx7AnssvNbYrH2iOigSINFMNJoLU/xLxBhTxitU2Zf8puHA4CQ3+BybgOH9HPqCtGcVAB7bcp4hiezGrachM+2oec2YwcGCpIobMPl43cmWkLhtGF5qfl7APVfbo18UXk8ZGmBY8YAYwEyksk2SBMJV6+XHw9J7uaaugc3uN8PuMVLqvSMpWN1ZdRsSkxrOJK+UNW7kbUi0wHnsV1rN0U0BIfVOQ==
171 ede3bf31fe63677fdf5bd8db687977d4e3d792ed 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAluOq84QHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91ao3D/oC9zKNbk+MMUP0cSfl+ESRbP/sAI466IYDkr9f1klooIFMsdqCd16eS36DVwIwrBYapRaNszC6Pg0KCFKCdeAWJLcgeIawwOkZPrLKQmS3I9GTl9gxtExeFvRryaAdP1DAPEU6JkyHo3xmURkJB58VjuBquZz4cYnL2aE1ag04CWAoRFiLu6bt1hEZ8pONU6cbDpHaJVyUZmJRB+llpybgdLnlBTrhfWjNofTh8MM6+vz67lIienYoSbepY+029J98phBTV+UEfWSBWw1hcNT/+QmOBGWWTLfBARsNDZFeYgQQOo3gRghKO7qUA/hqzDTmMG4/a2obs0LGsBlcMZ1Ky//zhdAJ/EN7uH9svM1t1fkw1RgvftmybptK5KiusZ9AWhnggHSwZtj1I6i/sojqsj9MrtdrD+1LfiKuAv/FtcMHSeff8IfItrd2B67JIj4wCzU8vDrAbAAqODHx7AnssvNbYrH2iOigSINFMNJoLU/xLxBhTxitU2Zf8puHA4CQ3+BybgOH9HPqCtGcVAB7bcp4hiezGrachM+2oec2YwcGCpIobMPl43cmWkLhtGF5qfl7APVfbo18UXk8ZGmBY8YAYwEyksk2SBMJV6+XHw9J7uaaugc3uN8PuMVLqvSMpWN1ZdRsSkxrOJK+UNW7kbUi0wHnsV1rN0U0BIfVOQ==
172 5405cb1a79010ac50c58cd84e6f50c4556bf2a4c 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAluyfokQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91eWpD/0eu/JfD6SfaT4Ozd2767ojNIW4M9BgcRH/FehFBd/3iQ/YQmaMVd6GmdaagM5YUpD9U+rDK95l8rUstuTglXeKD2SVcDM4Oq9ToyZyp5aizWjkxRxHT60W95G5FQO/tBbs63jfNrVDWDElbkpcn/gUG6JbX+q/S/mKd6WsuwNQC1N4VOWp0OWCmFGBWN7t/DqxGLGEajJM0NB97/r/IV6TzrGtaPf1CXaepDVvZwIIeas/eQgGInyqry7WBSn5sCUq4opIh1UigMABUAgzIZbgTg8NLGSmEgRgk0Vb4K+pLejLLDb5YD7ZwuUCkbd8oJImKQfU6++Ajd70TbNQRvVhMtd15iCtOOjLR+VNkUiDXm0g1U53sREMLdj/+SMJZB6Z18DotdgpaeCmwA/wWijXOdt76xwUKjByioxyQilPrzrWGaoSG4ynjiD2Y+eSRS1DxbpDgt4YEuiVA6U3ay99oW7KkhFjQsUtKl4SJ5SQWiEofvgtb2maNrXkPtKOtNRHhc61v73zYnsxtl2qduC99YOTin90FykD80XvgJZfyow/LICb77MNGwYBsJJMDQ3jG1YyUC2CQsb8wyrWM4TO3tspKAQPyMegUaVtBqw7ZhgiC3OXEes+z+AL5YRSZXALfurXPYbja8M8uGL2TYB3/5bKYvBXxvfmSGIeY6VieQ==
172 5405cb1a79010ac50c58cd84e6f50c4556bf2a4c 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAluyfokQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91eWpD/0eu/JfD6SfaT4Ozd2767ojNIW4M9BgcRH/FehFBd/3iQ/YQmaMVd6GmdaagM5YUpD9U+rDK95l8rUstuTglXeKD2SVcDM4Oq9ToyZyp5aizWjkxRxHT60W95G5FQO/tBbs63jfNrVDWDElbkpcn/gUG6JbX+q/S/mKd6WsuwNQC1N4VOWp0OWCmFGBWN7t/DqxGLGEajJM0NB97/r/IV6TzrGtaPf1CXaepDVvZwIIeas/eQgGInyqry7WBSn5sCUq4opIh1UigMABUAgzIZbgTg8NLGSmEgRgk0Vb4K+pLejLLDb5YD7ZwuUCkbd8oJImKQfU6++Ajd70TbNQRvVhMtd15iCtOOjLR+VNkUiDXm0g1U53sREMLdj/+SMJZB6Z18DotdgpaeCmwA/wWijXOdt76xwUKjByioxyQilPrzrWGaoSG4ynjiD2Y+eSRS1DxbpDgt4YEuiVA6U3ay99oW7KkhFjQsUtKl4SJ5SQWiEofvgtb2maNrXkPtKOtNRHhc61v73zYnsxtl2qduC99YOTin90FykD80XvgJZfyow/LICb77MNGwYBsJJMDQ3jG1YyUC2CQsb8wyrWM4TO3tspKAQPyMegUaVtBqw7ZhgiC3OXEes+z+AL5YRSZXALfurXPYbja8M8uGL2TYB3/5bKYvBXxvfmSGIeY6VieQ==
173 956ec6f1320df26f3133ec40f3de866ea0695fd7 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlvOG20QHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91eZ+EACb/XfPWaMkwIX54JaFWtL/nVkDcaL8xLVzlI+PxL0ZtHdQTGVQNp5f1BnZU9RKPZ9QOuz+QKNvb4hOOXBwmCi2AAjmTYUqtKThHmOT50ZRICkllY+YlZ3tI6JXRDhh7pSXaus8jBFG/VwuUlVmK5sA2TP+lIJijOgV9rThszfS4Q2I8sBTIaeZS1hyujFxGRO++tjYR+jPuo/98FhqJ5EylVYvKmnflWkOYLFNFqgDI6DQs7Dl+u2nrNAzZJQlgk+1ekd66T3WyK8U3tcFLZGRQ+gpzINH0Syn6USaaE+0nGi4we1hJS8JK0txWyHXJGNZYaWQAC2l1hIBfA38azwVLSe2w9JatXhS3HWByILy8JkEQ2kSo1xTD4mBkszZo/kWZpZRsAWydxCnzhNgKmTJYxASFTTX1mpdX4EzJBOs/++52y1OjVc0Ko0+6vSwxsC6zgIGJx1Os7vVgWHql0XbDmJ1NDdNmz7q5HjFcbNOWScKf6UGcBKV4dpW1w+7CvdoMFHUsVTa2zn6YOki3NEt0GWLXq+0aXbHSw8XETcyunQKjDi9ddKOw0rYGip6EKUKhOILZimQ0lgYRE23RDdT5Tl2D8s66SUuipgP9vGjbMaE/FhO3OAb7406jyCrOVfDis7sK0Hvw074GhIfZUjA4W4Ey2TeExCZHHhBdoPTrg==
173 956ec6f1320df26f3133ec40f3de866ea0695fd7 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlvOG20QHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91eZ+EACb/XfPWaMkwIX54JaFWtL/nVkDcaL8xLVzlI+PxL0ZtHdQTGVQNp5f1BnZU9RKPZ9QOuz+QKNvb4hOOXBwmCi2AAjmTYUqtKThHmOT50ZRICkllY+YlZ3tI6JXRDhh7pSXaus8jBFG/VwuUlVmK5sA2TP+lIJijOgV9rThszfS4Q2I8sBTIaeZS1hyujFxGRO++tjYR+jPuo/98FhqJ5EylVYvKmnflWkOYLFNFqgDI6DQs7Dl+u2nrNAzZJQlgk+1ekd66T3WyK8U3tcFLZGRQ+gpzINH0Syn6USaaE+0nGi4we1hJS8JK0txWyHXJGNZYaWQAC2l1hIBfA38azwVLSe2w9JatXhS3HWByILy8JkEQ2kSo1xTD4mBkszZo/kWZpZRsAWydxCnzhNgKmTJYxASFTTX1mpdX4EzJBOs/++52y1OjVc0Ko0+6vSwxsC6zgIGJx1Os7vVgWHql0XbDmJ1NDdNmz7q5HjFcbNOWScKf6UGcBKV4dpW1w+7CvdoMFHUsVTa2zn6YOki3NEt0GWLXq+0aXbHSw8XETcyunQKjDi9ddKOw0rYGip6EKUKhOILZimQ0lgYRE23RDdT5Tl2D8s66SUuipgP9vGjbMaE/FhO3OAb7406jyCrOVfDis7sK0Hvw074GhIfZUjA4W4Ey2TeExCZHHhBdoPTrg==
174 a91a2837150bdcb27ae76b3646e6c93cd6a15904 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlvclPMQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91fc0EADF/62jqCARFaQRRcKpobPNBZupwSbnQ7E296ZRwHdZvT8CVGfkWBUIStyh+r8bfmBzzea6d9/SUoRqCoV9rwCXuRbeCZZRMMkqx9IblV3foaIOxyQi0KE2lpzGJAHxPiNxD3czZV4B+P6X2wNmG9OLjmHyQ7o64GvPAJ+Ko/EsND1tkx4qB16mEuEHVxtfaG6hbjgpLekIA3+3xur3E8cWBsNO28HtQBK83r2qURwv6eG3TfkbmiE+Ie5TNC15LPVhAOHVSD7miZdI82uk2063puCKZxIJXsy7EMjHfChTM9c7B4+TdEBjms3y+Byz2EV7kRfjplGOnBbYvfY7qiteTn/22+rLrTTQNkndDN/Sqr1DjwsvxKDeIfsqgXzGQPupLOrGdGf4ILAtA0Reme7VKNN5Px6dNxnjKKwsnSrKTQ7ZcmD+W1LKlL63lBEQvEy+TLmmFLfM2xvvBxL5177AKZrj/8gMUzEi1K2MelDGrasA7OSjTlABoleDvZzVOf1nC0Bv83tFc8FeMHLwNOxkFSsjORvZuIH/G9BYUTAd96iLwQRBxXLOVNitxAOQT+s3hs7JEaUzTHlAY+lNeFAxUujb4H0V40Xgr20O1u7PJ53tzApIrg9JQPgvUXntmRs8fpNo6f3P6Sg8XtaCCHIUAB6qTHiose56llf6bzl66A==
174 a91a2837150bdcb27ae76b3646e6c93cd6a15904 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlvclPMQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91fc0EADF/62jqCARFaQRRcKpobPNBZupwSbnQ7E296ZRwHdZvT8CVGfkWBUIStyh+r8bfmBzzea6d9/SUoRqCoV9rwCXuRbeCZZRMMkqx9IblV3foaIOxyQi0KE2lpzGJAHxPiNxD3czZV4B+P6X2wNmG9OLjmHyQ7o64GvPAJ+Ko/EsND1tkx4qB16mEuEHVxtfaG6hbjgpLekIA3+3xur3E8cWBsNO28HtQBK83r2qURwv6eG3TfkbmiE+Ie5TNC15LPVhAOHVSD7miZdI82uk2063puCKZxIJXsy7EMjHfChTM9c7B4+TdEBjms3y+Byz2EV7kRfjplGOnBbYvfY7qiteTn/22+rLrTTQNkndDN/Sqr1DjwsvxKDeIfsqgXzGQPupLOrGdGf4ILAtA0Reme7VKNN5Px6dNxnjKKwsnSrKTQ7ZcmD+W1LKlL63lBEQvEy+TLmmFLfM2xvvBxL5177AKZrj/8gMUzEi1K2MelDGrasA7OSjTlABoleDvZzVOf1nC0Bv83tFc8FeMHLwNOxkFSsjORvZuIH/G9BYUTAd96iLwQRBxXLOVNitxAOQT+s3hs7JEaUzTHlAY+lNeFAxUujb4H0V40Xgr20O1u7PJ53tzApIrg9JQPgvUXntmRs8fpNo6f3P6Sg8XtaCCHIUAB6qTHiose56llf6bzl66A==
175 1c8c54cf97256f4468da2eb4dbee24f7f3888e71 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlwG+eIQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91YqSD/9IAwdaPrOeiT+DVBW2x33oFeY1X1f5CBG/vCJptalOd2QDIsD0ANEzQHmzV25RKD851v155Txt/BPlkuBfO/kg0BbOoqTpGZk+5CcoFWeyhJct2CxtCLdEpyZ/98/htMR4VfWprCX2GHXPjS813l9pebsN3WgBUOc2VaUdHNRoAGsMVgWC5BWwNP4XSA9oixFL/O4aGLQ6pPfP3vmMFySWXWnIN8gUZ4sm53eKaT0QCICAgzFh+GzRd81uACDfoJn1d8RS9GK+h6j8x0crLY5CpQQy8lRVkokvc0h6XK44ofc57p9GHAOfprHY3DbBhD9H6fLAf5raUsqPkLRYVGqhg8bOsBr3vJ56hiXJYOYPZSYXGjnHRcUrgfPVrY+6mPTeCIQMPmWBHwYH5Tc5TLrPuxxCL4wVywqGbfmIVP+WFUikkykAAwuPOZAswxJJOB0gsnnxcApmTeXRznBXyvzscMlWVZiMjzflKRRJ9V5RI4Fdc6n1wQ4vuLSO4AUnIypIsV6ZFAOBuFKH7x6nPG0tP3FYzcICaMOPbxEx3LStnuU+UuEs6TIxM6IiR3LPiiDGZ2BA2gjJhDxQFV8hAl8KDO3LsYuyUQCv3RTAP+YejH21bIXdnwDlNqy8Hrd53rq7jZsdb2pMVvOZZ3VmIu64f+jVkD/r5msDUkQL3M9jwg==
175 1c8c54cf97256f4468da2eb4dbee24f7f3888e71 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlwG+eIQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91YqSD/9IAwdaPrOeiT+DVBW2x33oFeY1X1f5CBG/vCJptalOd2QDIsD0ANEzQHmzV25RKD851v155Txt/BPlkuBfO/kg0BbOoqTpGZk+5CcoFWeyhJct2CxtCLdEpyZ/98/htMR4VfWprCX2GHXPjS813l9pebsN3WgBUOc2VaUdHNRoAGsMVgWC5BWwNP4XSA9oixFL/O4aGLQ6pPfP3vmMFySWXWnIN8gUZ4sm53eKaT0QCICAgzFh+GzRd81uACDfoJn1d8RS9GK+h6j8x0crLY5CpQQy8lRVkokvc0h6XK44ofc57p9GHAOfprHY3DbBhD9H6fLAf5raUsqPkLRYVGqhg8bOsBr3vJ56hiXJYOYPZSYXGjnHRcUrgfPVrY+6mPTeCIQMPmWBHwYH5Tc5TLrPuxxCL4wVywqGbfmIVP+WFUikkykAAwuPOZAswxJJOB0gsnnxcApmTeXRznBXyvzscMlWVZiMjzflKRRJ9V5RI4Fdc6n1wQ4vuLSO4AUnIypIsV6ZFAOBuFKH7x6nPG0tP3FYzcICaMOPbxEx3LStnuU+UuEs6TIxM6IiR3LPiiDGZ2BA2gjJhDxQFV8hAl8KDO3LsYuyUQCv3RTAP+YejH21bIXdnwDlNqy8Hrd53rq7jZsdb2pMVvOZZ3VmIu64f+jVkD/r5msDUkQL3M9jwg==
176 197f092b2cd9691e2a55d198f717b231af9be6f9 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlwz6DUQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91SbtD/47TJkSFuDJrvrpLuZROeR48opM8kPtMdbFKZxmeUtap/1q1ahBcA8cnkf5t5iEna57OkPfx0FVw7zupFZSD970q8KeQa1C1oRf+DV83rkOqMEzTLmDYZ5YWWILyDb2NrSkBzArhLNhEtWrFFo9uoigwJWiyNGXUkjVd7XUaYvxVYvnHJcmr98l9sW+RxgV2Cm/6ImeW6BkSUjfrJpZlHUecxcHIaDVniSCVzVF7T+tgG0+CxpehmRrPE/qlPTY2DVHuG6ogwjmu7pWr4kW3M6pTmOYICKjkojIhPTAfNDZGNYruJMukEeB2JyxSz+J9jhjPe//9x4JznpCzm/JzCHFO9CfONjHIcUqLa9qxqhmBFpr1U5J7vRir4ch7v8TGtGbcR3833HTUA7EEMu/Ca48XVfGNDmySQs8zgGpj1yzf/lBGbiAzTSp7Zp+ANLu+R3NjeiDUYQbgf3vcpoHL44duk4dzhD+ofFD75PF1SMTluWbeLCSENH9io2pxVDj3I5VhlNxHdbqY1WXb+sDBVr4niIGzQiKqVOV33ghyRpzVJFZ7SaQG7VR/mLL3UnvJuapLYtUV9+/7Si/CHl7m8NntPMvx1nM/Z4t/BN8Z5cdhPn2PLxp9f5VCmCqLlCQDSv94cCTLlatiCTfF7axgE0u7+CWiOUNyyqg/vu0pjTwIA==
176 197f092b2cd9691e2a55d198f717b231af9be6f9 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlwz6DUQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91SbtD/47TJkSFuDJrvrpLuZROeR48opM8kPtMdbFKZxmeUtap/1q1ahBcA8cnkf5t5iEna57OkPfx0FVw7zupFZSD970q8KeQa1C1oRf+DV83rkOqMEzTLmDYZ5YWWILyDb2NrSkBzArhLNhEtWrFFo9uoigwJWiyNGXUkjVd7XUaYvxVYvnHJcmr98l9sW+RxgV2Cm/6ImeW6BkSUjfrJpZlHUecxcHIaDVniSCVzVF7T+tgG0+CxpehmRrPE/qlPTY2DVHuG6ogwjmu7pWr4kW3M6pTmOYICKjkojIhPTAfNDZGNYruJMukEeB2JyxSz+J9jhjPe//9x4JznpCzm/JzCHFO9CfONjHIcUqLa9qxqhmBFpr1U5J7vRir4ch7v8TGtGbcR3833HTUA7EEMu/Ca48XVfGNDmySQs8zgGpj1yzf/lBGbiAzTSp7Zp+ANLu+R3NjeiDUYQbgf3vcpoHL44duk4dzhD+ofFD75PF1SMTluWbeLCSENH9io2pxVDj3I5VhlNxHdbqY1WXb+sDBVr4niIGzQiKqVOV33ghyRpzVJFZ7SaQG7VR/mLL3UnvJuapLYtUV9+/7Si/CHl7m8NntPMvx1nM/Z4t/BN8Z5cdhPn2PLxp9f5VCmCqLlCQDSv94cCTLlatiCTfF7axgE0u7+CWiOUNyyqg/vu0pjTwIA==
177 593718ff5844cad7a27ee3eb5adad89ac8550949 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlxCG6EQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91YptD/9DG76IvubjzVsfX1UiQcV1mqWuSgz/idpeFCrc6Z1dyFB5UmbHKfAaZnrPBR7ly6bGD9+NZupB9A8QRxX92koiq0Hw2ywbwR5oWVrBaDiinIDLiTQTUCPnNMH0FSNrt4Kf9Gj4RqMufZvL+dR0pDYV0n6HP3aGOeTnowNhv0lUbw/Gx20YrcCU9uf3GbgRvMQiFNv9cTJAdQlH++98C8MVLfRU4ZxP11hI7sR8mp1q6ruJoozd0Cta67E6MyC/L2Rp3W89psvvY7DSTg9RwQwoS8I6U9iyQJ16Bb6UgZVV6jqQqOSxWUaPfKUhJLl2ENHH5f3rzoi3NH6jHuy5rq2v9XuvOpQ7LqSi1Ev0oq1xllZiyD4Zm69Z/Is0mxwqPskZGWR5Lh6Uq3Dh0zJW7O5M2m1IHdAYqffHpUr2NgEQVST4VDvO4fR2d7n6+ZNXYbZrpmQ1j4bpOZCEMqWXPfl4HY7a60hWa884mWxtVLGvhYycxnN8r1o5ouS0pAMAI6qEFFW1XFFN4eNDDWl83BkuDa32DTEthoyi15JM5jS7VPDYACdHE3IVqsTsZq7nn60uoFCGpdMcSqrD2mlUd9Z12x8NnCIrxKhlHLkq89OrQAcz8/0bbluGuzm3FHKb+8VQWr0MgkvOLTqqvOqn97oBdKqo0eyT0IPz8QeVYPbZfQ==
177 593718ff5844cad7a27ee3eb5adad89ac8550949 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlxCG6EQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91YptD/9DG76IvubjzVsfX1UiQcV1mqWuSgz/idpeFCrc6Z1dyFB5UmbHKfAaZnrPBR7ly6bGD9+NZupB9A8QRxX92koiq0Hw2ywbwR5oWVrBaDiinIDLiTQTUCPnNMH0FSNrt4Kf9Gj4RqMufZvL+dR0pDYV0n6HP3aGOeTnowNhv0lUbw/Gx20YrcCU9uf3GbgRvMQiFNv9cTJAdQlH++98C8MVLfRU4ZxP11hI7sR8mp1q6ruJoozd0Cta67E6MyC/L2Rp3W89psvvY7DSTg9RwQwoS8I6U9iyQJ16Bb6UgZVV6jqQqOSxWUaPfKUhJLl2ENHH5f3rzoi3NH6jHuy5rq2v9XuvOpQ7LqSi1Ev0oq1xllZiyD4Zm69Z/Is0mxwqPskZGWR5Lh6Uq3Dh0zJW7O5M2m1IHdAYqffHpUr2NgEQVST4VDvO4fR2d7n6+ZNXYbZrpmQ1j4bpOZCEMqWXPfl4HY7a60hWa884mWxtVLGvhYycxnN8r1o5ouS0pAMAI6qEFFW1XFFN4eNDDWl83BkuDa32DTEthoyi15JM5jS7VPDYACdHE3IVqsTsZq7nn60uoFCGpdMcSqrD2mlUd9Z12x8NnCIrxKhlHLkq89OrQAcz8/0bbluGuzm3FHKb+8VQWr0MgkvOLTqqvOqn97oBdKqo0eyT0IPz8QeVYPbZfQ==
178 83377b4b4ae0e9a6b8e579f7b0a693b8cf5c3b10 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlxUk3gQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91aT7EACaycWeal53ShxaNyTNOa5IPZ71+iyWA9xEh7hK6cDDirpItarWLRVWoWqBlWRBBs6uU4BxnpPSCLFkJLu6ts/5p4R6/0Z04Pasd6sFi14bCGslmPJFlwrpfFDpQvFR6xZAtv1xGb8n+rjpK+wfstjRgyf84zn4//0dOdylY5EUXOk4/3zcXKAzPgZHBRper+PlQ0ICgYHiKQUlyDWrFrdSEis6OqBa+PbxdmgzLYbhXi0bvS5XRWM9EVJZa+5ITEVOEGPClRcoA7SJE5DiapMYlwNnB3U6TEazJoj5yuvGhrJzj9lx7/jx9tzZ/mhdOVsSRiSCBu46B/E63fnUDqaMw8KKlFKBRuzKnqnByZD8fuD34YJ6A82hta56W4SJ4pusa/X2nAJn1QbRjESY4wN4FEaNdYiMbpgbG2uBDhmEowAyhXtiuQAPCUra5o42a+E+tAgV5uNUAal8vk0DcPRmzc4UntQiQGwxL0fsTEpMQtG5ryxWRmOIBq6aKGuLVELllPCwOh8UIGLlpAoEynlNi9qJNT6kHpSmwquiU6TG6R1dA/ckBK2H90hewtb/jwLlenGugpylLQ2U/NsDdoWRyHNrdB4eUJiWD/BBPXktZQJVja97Js+Vn44ctCkNjui/53xcBQfIYdHGLttIEq56v/yZiSviCcTUhBPRSEdoUg==
178 83377b4b4ae0e9a6b8e579f7b0a693b8cf5c3b10 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlxUk3gQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91aT7EACaycWeal53ShxaNyTNOa5IPZ71+iyWA9xEh7hK6cDDirpItarWLRVWoWqBlWRBBs6uU4BxnpPSCLFkJLu6ts/5p4R6/0Z04Pasd6sFi14bCGslmPJFlwrpfFDpQvFR6xZAtv1xGb8n+rjpK+wfstjRgyf84zn4//0dOdylY5EUXOk4/3zcXKAzPgZHBRper+PlQ0ICgYHiKQUlyDWrFrdSEis6OqBa+PbxdmgzLYbhXi0bvS5XRWM9EVJZa+5ITEVOEGPClRcoA7SJE5DiapMYlwNnB3U6TEazJoj5yuvGhrJzj9lx7/jx9tzZ/mhdOVsSRiSCBu46B/E63fnUDqaMw8KKlFKBRuzKnqnByZD8fuD34YJ6A82hta56W4SJ4pusa/X2nAJn1QbRjESY4wN4FEaNdYiMbpgbG2uBDhmEowAyhXtiuQAPCUra5o42a+E+tAgV5uNUAal8vk0DcPRmzc4UntQiQGwxL0fsTEpMQtG5ryxWRmOIBq6aKGuLVELllPCwOh8UIGLlpAoEynlNi9qJNT6kHpSmwquiU6TG6R1dA/ckBK2H90hewtb/jwLlenGugpylLQ2U/NsDdoWRyHNrdB4eUJiWD/BBPXktZQJVja97Js+Vn44ctCkNjui/53xcBQfIYdHGLttIEq56v/yZiSviCcTUhBPRSEdoUg==
179 4ea21df312ec7159c5b3633096b6ecf68750b0dd 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlyQ7VYQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91aziD/4uI/Nr+UJgOri1zfa6ObXuMVO2FeadAolKemMDE/c4ddPUN2AwysZyJaOHmqj5VR0nf4a9CpTBc8Ciq9tfaFSWN6XFIJ2s3GPHhsnyhsPbF56c2bpl2W/csxor9eDGpv9TrQOK0qgI4wGxSQVFW0uUgHtZ5Yd6JWupHuyDfWopJf3oonissKI9ykRLeZEQ3sPIP6vTWMM3pdavAmDii3qKVEaCEGWmXgnM/vfBJ/tA1U5LSXpxwkJB7Pi/6Xc6OnGHWmCpsA4L6TSRkoyho4a6tLUA1Qlqm6sMxJjXAer8dmDLpmXL7gF3JhZgkiX74i2zDZnM4i42E6EhO52l3uorF5gtsw85dY20MSoBOmn5bM7k40TCA+vriNZJgmDrTYgY3B00mNysioEuSpDkILPJIV4U9LTazsxR49h3/mH2D1Sdxu6YtCIPE8ggThmveW/dZQy6W1xLfS66pFmDvq8ND0WjDa/Fi9dmjMcQtzA9CZL8AMlSc2aLJs++KjCuN+t6tn/tLhLz1nHaSitqgsIoJmBWb00QjOilnAQq7H8gUpUqMdLyEeL2B9HfJobQx6A8Op2xohjI7qD5gLGAxh+QMmuUmf7wx1h2UuQvrNW5di7S3k3nxfhm87Gkth3j0M/aMy0P6irPOKcKns55r6eOzItC+ezQayXc4A10F+x6Ew==
179 4ea21df312ec7159c5b3633096b6ecf68750b0dd 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlyQ7VYQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91aziD/4uI/Nr+UJgOri1zfa6ObXuMVO2FeadAolKemMDE/c4ddPUN2AwysZyJaOHmqj5VR0nf4a9CpTBc8Ciq9tfaFSWN6XFIJ2s3GPHhsnyhsPbF56c2bpl2W/csxor9eDGpv9TrQOK0qgI4wGxSQVFW0uUgHtZ5Yd6JWupHuyDfWopJf3oonissKI9ykRLeZEQ3sPIP6vTWMM3pdavAmDii3qKVEaCEGWmXgnM/vfBJ/tA1U5LSXpxwkJB7Pi/6Xc6OnGHWmCpsA4L6TSRkoyho4a6tLUA1Qlqm6sMxJjXAer8dmDLpmXL7gF3JhZgkiX74i2zDZnM4i42E6EhO52l3uorF5gtsw85dY20MSoBOmn5bM7k40TCA+vriNZJgmDrTYgY3B00mNysioEuSpDkILPJIV4U9LTazsxR49h3/mH2D1Sdxu6YtCIPE8ggThmveW/dZQy6W1xLfS66pFmDvq8ND0WjDa/Fi9dmjMcQtzA9CZL8AMlSc2aLJs++KjCuN+t6tn/tLhLz1nHaSitqgsIoJmBWb00QjOilnAQq7H8gUpUqMdLyEeL2B9HfJobQx6A8Op2xohjI7qD5gLGAxh+QMmuUmf7wx1h2UuQvrNW5di7S3k3nxfhm87Gkth3j0M/aMy0P6irPOKcKns55r6eOzItC+ezQayXc4A10F+x6Ew==
180 4a8d9ed864754837a185a642170cde24392f9abf 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAly3aLkQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91bpXD/0Qdx3lNv6230rl369PnGM7o56BFywJtGtQ0FjBj81/Q6IKNJkAus/FXA02MevAxnKhyCMPHbiWQn4cn+Fpt9Y7FOFl3MTdoY5v4rGDAbAaJsjyK3BNqSwWD1uFaOnFDzA/112MJ6nDciVaOzeD7qakMj8zdVhvyEfFszN7f7xT1JyGc+cOWfbvcIv/IXWZNrSZC0EzcZspfwxYQwFscgDL3AHeKeYqihJ6vgWxgEg4V8ZnJ6roJeERTp2wwvIj/pKSEpgzfLQfHiEwvH9MKMaJHGx4huzWJxYX2DB83LaK7cgkKqzyQ+z8rsb27oFPMVgb1Kg78+6sRujFdkahFWYYGPT6sFBDWkRQ/J7DRnBzHH2wbBoyNkApmLEfaRGJpxX8wojPFGJkNr6GF12uF7E+djsuE8ZL7l4p2YD33NBSzcEjNTlgruRauj/7SoSC3BgDlrqCypCkNgn5nDDjvf6oJx16qGqZsglHJOl0S2LRiGaMQTpBhpDWAyVIAQBRW/vF1IRnNJaQ+dX7M9VqlVsXnfh8WD+FPKDgpiSLO8hIuvlYlcrtU9rXyWu1njKvCs744G836k4SNBoi+y6bi6XbmU0Uv0GSCLyj1BIsqglfXuac0QHlz5RNmS6LVf7z13ZIn/ePXehYoKHu+PNDmbVGGwAVoZP4HLEqonD3SVpVcQ==
180 4a8d9ed864754837a185a642170cde24392f9abf 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAly3aLkQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91bpXD/0Qdx3lNv6230rl369PnGM7o56BFywJtGtQ0FjBj81/Q6IKNJkAus/FXA02MevAxnKhyCMPHbiWQn4cn+Fpt9Y7FOFl3MTdoY5v4rGDAbAaJsjyK3BNqSwWD1uFaOnFDzA/112MJ6nDciVaOzeD7qakMj8zdVhvyEfFszN7f7xT1JyGc+cOWfbvcIv/IXWZNrSZC0EzcZspfwxYQwFscgDL3AHeKeYqihJ6vgWxgEg4V8ZnJ6roJeERTp2wwvIj/pKSEpgzfLQfHiEwvH9MKMaJHGx4huzWJxYX2DB83LaK7cgkKqzyQ+z8rsb27oFPMVgb1Kg78+6sRujFdkahFWYYGPT6sFBDWkRQ/J7DRnBzHH2wbBoyNkApmLEfaRGJpxX8wojPFGJkNr6GF12uF7E+djsuE8ZL7l4p2YD33NBSzcEjNTlgruRauj/7SoSC3BgDlrqCypCkNgn5nDDjvf6oJx16qGqZsglHJOl0S2LRiGaMQTpBhpDWAyVIAQBRW/vF1IRnNJaQ+dX7M9VqlVsXnfh8WD+FPKDgpiSLO8hIuvlYlcrtU9rXyWu1njKvCs744G836k4SNBoi+y6bi6XbmU0Uv0GSCLyj1BIsqglfXuac0QHlz5RNmS6LVf7z13ZIn/ePXehYoKHu+PNDmbVGGwAVoZP4HLEqonD3SVpVcQ==
181 07e479ef7c9639be0029f00e6a722b96dcc05fee 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlzJ5QYQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91U0QD/4xQ00Suo+XNM/2v01NEALJA8pFxSaUcz1fBVQDwIQbApAHbjVDgIShuFlAXu7Jf582+C5wJu0J8L5Rb+Q9WJuM9sM+6cxUWclT3D3gB326LuQg86y5MYbzmwsSCOnBdRn/MY18on2XTa8t4Mxf0jAaHPUXEadmuwkOw4ds62eUD81lkakGoxgXrD1GUhAlGItNPOb0rp2XFj7i+LvazMX2mWOEXMXA5KPQrOvLsKnoESiPfONXumBfZNVSxVA7fJ3Vl1+PldBax+w9LQMgVGo+BkqPt7i+lPTcnlh2Nbf8y3zERTcItFBzrBxmuG6pINfNpZY/fi+9VL7mpMYlzlxs7VcLF8bVnpYpxpHfDR4hPjP0sq6+/nSSGUfzQXmfGHq0ZdoVGSzrDEv8UzYE9ehWUhHNE+sIU3MpwjC+WiW2YhYzPYN2KOlfSog3LuWLAcn3ZghWg1S4crsPt9CeE0vKxkNWNz9dzvhbniW7VGorXJKFCJzMu6pGaP/UjwpHxR+C6J1MGUW2TQwdIUyhPA8HfHJSVbifFJV+1CYEDcqRcFETpxm4YNrLJNL/Ns7zoWmdmEUXT1NEnK1r3Pe2Xi1o56FHGPffOWASmqFnF/coZCq6b4vmBWK/n8mI/JF1yxltfwacaY+1pEor92ztK34Lme1A+R7zyObGYNDcWiGZgA==
181 07e479ef7c9639be0029f00e6a722b96dcc05fee 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlzJ5QYQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91U0QD/4xQ00Suo+XNM/2v01NEALJA8pFxSaUcz1fBVQDwIQbApAHbjVDgIShuFlAXu7Jf582+C5wJu0J8L5Rb+Q9WJuM9sM+6cxUWclT3D3gB326LuQg86y5MYbzmwsSCOnBdRn/MY18on2XTa8t4Mxf0jAaHPUXEadmuwkOw4ds62eUD81lkakGoxgXrD1GUhAlGItNPOb0rp2XFj7i+LvazMX2mWOEXMXA5KPQrOvLsKnoESiPfONXumBfZNVSxVA7fJ3Vl1+PldBax+w9LQMgVGo+BkqPt7i+lPTcnlh2Nbf8y3zERTcItFBzrBxmuG6pINfNpZY/fi+9VL7mpMYlzlxs7VcLF8bVnpYpxpHfDR4hPjP0sq6+/nSSGUfzQXmfGHq0ZdoVGSzrDEv8UzYE9ehWUhHNE+sIU3MpwjC+WiW2YhYzPYN2KOlfSog3LuWLAcn3ZghWg1S4crsPt9CeE0vKxkNWNz9dzvhbniW7VGorXJKFCJzMu6pGaP/UjwpHxR+C6J1MGUW2TQwdIUyhPA8HfHJSVbifFJV+1CYEDcqRcFETpxm4YNrLJNL/Ns7zoWmdmEUXT1NEnK1r3Pe2Xi1o56FHGPffOWASmqFnF/coZCq6b4vmBWK/n8mI/JF1yxltfwacaY+1pEor92ztK34Lme1A+R7zyObGYNDcWiGZgA==
182 c3484ddbdb9621256d597ed86b90d229c59c2af9 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlz3zjsQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91XWVEACnlQCHCF7dMrvTHwE4nA+i/I1l8UfRwR3ufXhBxjVUqxS75mHMcCsOwClAa2HaqNP97IGbk2fi9y53SOKH67imNVm8NY8yIook1C8T7nKsFmyM3l63FdVQDgUF6AJ0krDt6iJo4vjk8CyRHowAcmL942jcfBU9U5/Jli11Sx33MKF/eMXnuXYRBNESh97f1bDgwydp7QT8dj/T23YvuIVtfq9h8D46qXWkpwbgtnXMnaz21kqcN6A5aKbadG4ELf9175cBlfe+ZpOqpy+OSuQBByOP5eBNl5d0vq/i4WQyJZs8GoVd5Bh559+HjKIKv11Y+gXoaQMf4VSp2JZwwPlTR5Me5N6AJNViXW1Bm108ZWeXR81Hu2+t2eQv6EelcQxnW0e/mTCUot8TaewYFJ+4VWwAAca81FP0X8J0YcdIkvvNmrU9V62B3WYK3iYgbwm7IlR3+7ilQUz3NZCZOqJpo+c7k/yhuoj4ZMDq8JzaqBnBnARbvUF61B4iVhto4xpruUQw8FwFLUuZLohsESCNCCgqdoiyJHnVQVitoNJlCeEPl+W+UUeFfwf9fzrS6nj9xWkNm9lBOahaH+fV69msi5Ex/gy8y4H+4T8z0f3gFO7kp9eKr5C7hoGyKQWv5D61H1qEZOFUZjXHBhMxbe+og40G0apMm3qmsj2KsCNDdQ==
182 c3484ddbdb9621256d597ed86b90d229c59c2af9 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAlz3zjsQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91XWVEACnlQCHCF7dMrvTHwE4nA+i/I1l8UfRwR3ufXhBxjVUqxS75mHMcCsOwClAa2HaqNP97IGbk2fi9y53SOKH67imNVm8NY8yIook1C8T7nKsFmyM3l63FdVQDgUF6AJ0krDt6iJo4vjk8CyRHowAcmL942jcfBU9U5/Jli11Sx33MKF/eMXnuXYRBNESh97f1bDgwydp7QT8dj/T23YvuIVtfq9h8D46qXWkpwbgtnXMnaz21kqcN6A5aKbadG4ELf9175cBlfe+ZpOqpy+OSuQBByOP5eBNl5d0vq/i4WQyJZs8GoVd5Bh559+HjKIKv11Y+gXoaQMf4VSp2JZwwPlTR5Me5N6AJNViXW1Bm108ZWeXR81Hu2+t2eQv6EelcQxnW0e/mTCUot8TaewYFJ+4VWwAAca81FP0X8J0YcdIkvvNmrU9V62B3WYK3iYgbwm7IlR3+7ilQUz3NZCZOqJpo+c7k/yhuoj4ZMDq8JzaqBnBnARbvUF61B4iVhto4xpruUQw8FwFLUuZLohsESCNCCgqdoiyJHnVQVitoNJlCeEPl+W+UUeFfwf9fzrS6nj9xWkNm9lBOahaH+fV69msi5Ex/gy8y4H+4T8z0f3gFO7kp9eKr5C7hoGyKQWv5D61H1qEZOFUZjXHBhMxbe+og40G0apMm3qmsj2KsCNDdQ==
183 97ada9b8d51bef24c5cb4cdca4243f0db694ab6e 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl0kn6UQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91RwND/9uZ3Avf0jXYzGT5t+HhlAeWeqA3wrQOmk0if7ttUholoHYmCbc7V9ufgiQ1jTX/58EhOXHt4L1zlLDf2OMJ7YQz9pfiGjW3vLvVKU7eeQ5epG8J8Hp4BcbEU5gfQBwzZmRMqVfZ9QbNgENysfQxhVT0ONPC5TBUsamAysRQVVPeEQFlW1mSf03LYF1UDjXgquHoIFnnPCZyNUGVRSajW9mDe0OQI95lXE6lISlBkeoTmVs9mR+OeLO3+Dgn2ai8d4gHxdCSU5iDnifSp4aaThfNxueSRFzNI1Q6R6MQrIplqFYZGhAOOXQzZWqThQld6/58IvaBP4aCGs1VxE/qBKNp8txm1QeL/ukOWPgVS9z7Iw5uRuET95aEn/Khisv78lrVGOD5wigt2bb4UiysIgk8+du7HNMqPmS31fCS1vsoJ+y2XoJP2q8bNDiwuVihDWJDlF091HH2+ItmopHGUGeHaxNyRoiSvE7fCBi/u3rleiMsMai8r1QDgBpalUPbaLzBelEKhn2JcDhU5NrG8a+SKRCzpmXkkFPhxrzT1dvEAnoNI0LbmekTDWilp0sZbwdsn2rO51IJ4PU8CgbYROP8Z4DuNMfVyVIpxAEb2zbnIA4YqJ3qcQ3e+qEIw8h9m/ot9YYJ/wCQjIIXN6CUHXLYO30HubNOEDVS4Gem93Gcw==
183 97ada9b8d51bef24c5cb4cdca4243f0db694ab6e 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl0kn6UQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91RwND/9uZ3Avf0jXYzGT5t+HhlAeWeqA3wrQOmk0if7ttUholoHYmCbc7V9ufgiQ1jTX/58EhOXHt4L1zlLDf2OMJ7YQz9pfiGjW3vLvVKU7eeQ5epG8J8Hp4BcbEU5gfQBwzZmRMqVfZ9QbNgENysfQxhVT0ONPC5TBUsamAysRQVVPeEQFlW1mSf03LYF1UDjXgquHoIFnnPCZyNUGVRSajW9mDe0OQI95lXE6lISlBkeoTmVs9mR+OeLO3+Dgn2ai8d4gHxdCSU5iDnifSp4aaThfNxueSRFzNI1Q6R6MQrIplqFYZGhAOOXQzZWqThQld6/58IvaBP4aCGs1VxE/qBKNp8txm1QeL/ukOWPgVS9z7Iw5uRuET95aEn/Khisv78lrVGOD5wigt2bb4UiysIgk8+du7HNMqPmS31fCS1vsoJ+y2XoJP2q8bNDiwuVihDWJDlF091HH2+ItmopHGUGeHaxNyRoiSvE7fCBi/u3rleiMsMai8r1QDgBpalUPbaLzBelEKhn2JcDhU5NrG8a+SKRCzpmXkkFPhxrzT1dvEAnoNI0LbmekTDWilp0sZbwdsn2rO51IJ4PU8CgbYROP8Z4DuNMfVyVIpxAEb2zbnIA4YqJ3qcQ3e+qEIw8h9m/ot9YYJ/wCQjIIXN6CUHXLYO30HubNOEDVS4Gem93Gcw==
184 e386b5f4f8360dbb43a576dd9b1368e386fefa5b 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl01+7cQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91ZM6D/9iWw0AyhcDFI7nEVcSlqDNABQvCnHoNB79UYrTf3GOjuUiyVUTwZ4CIOS+o2wchZXBRWx+T3aHJ1x6qTpXvA3oa9bgerNWFfmVmTuWWMlbQszXS5Lpv5u1lwCoLPDi4sa/gKBSIzt/CMu7zuPzO2yLEnWvR6ljOzjY9LfUx80u1zc899MEEsNuVStkfw9f37lAu+udMRgvQDZeLh+j3Qg5uh3GV3/8Q/I/YFNRHeKSLBkdp5CD3CkUtteBuZfIje/BwttxHG6MdbXMjOe0QmGMNzcSstnVqsENhEa0ZKLxM6NxfwcsxbeKA1uFoTvzT1sFyXXS3NV0noMQBwMrxipzKv4WrjuctmUms6n+VW/w4GMg8gzeUvu7rzqVIehWIBTxV8yWwkWiS9ge6Upiki5vCG+aeMLrwsNqsptOh4BEcsvcpd2ZZtUDRHYFVUK4z/RRlpKb6CdzkGeMWwP6oWAv4N0veD73Y7wPz76ZFNU2yvqViRPxrU2A2P44R8dLFvEOmcO5MHVNwHP0kpaj9dpGwBI0t2A32vDF8LEsnd86LQBm6X5ZWWJ5hGmtZotp4blkH1oFKt+ZeccHcwueIMU3v9e02ElhM4Mo2nD3yyQvMkzDqp5lZEfNqEK8rlj2TNfc8XyjAsp1hKpnjDa1olKKfdq8OniUpsaYDTku4+vuGw==
184 e386b5f4f8360dbb43a576dd9b1368e386fefa5b 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl01+7cQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91ZM6D/9iWw0AyhcDFI7nEVcSlqDNABQvCnHoNB79UYrTf3GOjuUiyVUTwZ4CIOS+o2wchZXBRWx+T3aHJ1x6qTpXvA3oa9bgerNWFfmVmTuWWMlbQszXS5Lpv5u1lwCoLPDi4sa/gKBSIzt/CMu7zuPzO2yLEnWvR6ljOzjY9LfUx80u1zc899MEEsNuVStkfw9f37lAu+udMRgvQDZeLh+j3Qg5uh3GV3/8Q/I/YFNRHeKSLBkdp5CD3CkUtteBuZfIje/BwttxHG6MdbXMjOe0QmGMNzcSstnVqsENhEa0ZKLxM6NxfwcsxbeKA1uFoTvzT1sFyXXS3NV0noMQBwMrxipzKv4WrjuctmUms6n+VW/w4GMg8gzeUvu7rzqVIehWIBTxV8yWwkWiS9ge6Upiki5vCG+aeMLrwsNqsptOh4BEcsvcpd2ZZtUDRHYFVUK4z/RRlpKb6CdzkGeMWwP6oWAv4N0veD73Y7wPz76ZFNU2yvqViRPxrU2A2P44R8dLFvEOmcO5MHVNwHP0kpaj9dpGwBI0t2A32vDF8LEsnd86LQBm6X5ZWWJ5hGmtZotp4blkH1oFKt+ZeccHcwueIMU3v9e02ElhM4Mo2nD3yyQvMkzDqp5lZEfNqEK8rlj2TNfc8XyjAsp1hKpnjDa1olKKfdq8OniUpsaYDTku4+vuGw==
185 e91930d712e8507d1bc1b2dffd96c83edc4cbed3 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl1DD/sQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91bvmD/4/QDZZGVe+WiMUxbT+grfFjwjX4nkg7Vt+6vQbjN68NC5XpSiCzW8uu0LRemX0KJKoOfQxqHk3YKkZZHIk10Fe6RSLWt8dqlfa2J9B2U8DwMEBykCOuxcLlDe7DGaaMXlXXRhNXebRheNPLeNe+r7beMAAjwchTIIJD5xcFnPRFR0nN7Vj7eRUdWIQ9H/s7TolPz1Mf7IWqapLjPtofiwSgtRoXfIAkuuabnE4eMVJ8rsLwcuMhxWP2zjEfEg68YkiGBAFmlnRk+3lJpiB9kVapB3cWcsWv2OBhz0D3NgGp82eWkjJCZZhZ+zHHrQ6L9zbiArzW9NVvPEAKLbl3XUhFUzFTUD+S38wsYLYL5RkzhlCI2/K1LJLOtj7r0Seen0v8X842p0cXmxTg/o1Vg3JOm04l9AwzCsnqwIqV7Ru//KPqH91MFFH6T6tbfjtLHRmjxRjMZmVt7ZQjS84opVCZwgUTZZJB2kd1goROjdowQVK6qsEonlzGjWb9zc3el5L9uzDeim3e5t2GNRVt8veQaLc+U2hHWniVsDJMvqp2Hr9IWUKp+bu/35B1nElvooS40gj2WhkfkCbbXSg9qnVLwGxxcGdF28Z0nhQcfKiJAc+8l9l19GNhdKxOi4zUXlp90opPWfT7wGQmysvTjQeFL2zX9ziuHUZZwlW1YbeMQ==
185 e91930d712e8507d1bc1b2dffd96c83edc4cbed3 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl1DD/sQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91bvmD/4/QDZZGVe+WiMUxbT+grfFjwjX4nkg7Vt+6vQbjN68NC5XpSiCzW8uu0LRemX0KJKoOfQxqHk3YKkZZHIk10Fe6RSLWt8dqlfa2J9B2U8DwMEBykCOuxcLlDe7DGaaMXlXXRhNXebRheNPLeNe+r7beMAAjwchTIIJD5xcFnPRFR0nN7Vj7eRUdWIQ9H/s7TolPz1Mf7IWqapLjPtofiwSgtRoXfIAkuuabnE4eMVJ8rsLwcuMhxWP2zjEfEg68YkiGBAFmlnRk+3lJpiB9kVapB3cWcsWv2OBhz0D3NgGp82eWkjJCZZhZ+zHHrQ6L9zbiArzW9NVvPEAKLbl3XUhFUzFTUD+S38wsYLYL5RkzhlCI2/K1LJLOtj7r0Seen0v8X842p0cXmxTg/o1Vg3JOm04l9AwzCsnqwIqV7Ru//KPqH91MFFH6T6tbfjtLHRmjxRjMZmVt7ZQjS84opVCZwgUTZZJB2kd1goROjdowQVK6qsEonlzGjWb9zc3el5L9uzDeim3e5t2GNRVt8veQaLc+U2hHWniVsDJMvqp2Hr9IWUKp+bu/35B1nElvooS40gj2WhkfkCbbXSg9qnVLwGxxcGdF28Z0nhQcfKiJAc+8l9l19GNhdKxOi4zUXlp90opPWfT7wGQmysvTjQeFL2zX9ziuHUZZwlW1YbeMQ==
186 a4e32fd539ab41489a51b2aa88bda9a73b839562 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl1xTxUQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91ZQgD/96mViQ6fEh84l4XyAlY6Dq3SgMqEXttsUpk/GPoW4ykDFKN6VoiOaPoyNODO/46V3yeAjYjy3vX7Ua4/MY1NlnNoliQcTYtRV3SlDdoueTPOLfO6YSV27LG+dX/HYvPc/htCVmIVItU1JL+KEpXnv+bT50Bk+m6OgzfJMDzdHQ5ICImT8gW7UXlH/mlNtWMOrJDk3cArGhGs/pTFVrfgRTfDfDGSA9xW0/QvsNI5iwZHgMYaqoPFDnw6d/NXWRlk77KNiXkBEOKHf6UEWecMKmiSCm8RePSiX9ezqdcBAHygOg4KUeiR2kPNl4QJtskyG4CwWxlmGlfgKx07s7rGafE+DWLEYC9Wa8qK6/LPiowm17m/UlAYxdFXaBCiN0wgEw7oNmjcx/791ez+CL1+h6pd0+iSVI4bO9/YZ8LPROYef18MFm+IFIDIOgZU4eUbpBrzBb3IM1a519xgnmWXAjtRtGWEZMuHaSoLJf2pDXvaUPX6YpJeqCBFO3q/swbiJsQsy6xRW0Dwtn7umU1PGdmMoTnskTRKy9Kgzv7lf/nsUuRbzzM4ut9m1TOo27AulObMrmQB4YvLi/LEnYaRNx18yaqOceMxb/mS0tHLgcZToy9rTV+vtC21vgwfzGia2neLLe50tnIsBPP/AdTOw9ZDMRfXMCajWM22hPxvnGcw==
186 a4e32fd539ab41489a51b2aa88bda9a73b839562 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl1xTxUQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91ZQgD/96mViQ6fEh84l4XyAlY6Dq3SgMqEXttsUpk/GPoW4ykDFKN6VoiOaPoyNODO/46V3yeAjYjy3vX7Ua4/MY1NlnNoliQcTYtRV3SlDdoueTPOLfO6YSV27LG+dX/HYvPc/htCVmIVItU1JL+KEpXnv+bT50Bk+m6OgzfJMDzdHQ5ICImT8gW7UXlH/mlNtWMOrJDk3cArGhGs/pTFVrfgRTfDfDGSA9xW0/QvsNI5iwZHgMYaqoPFDnw6d/NXWRlk77KNiXkBEOKHf6UEWecMKmiSCm8RePSiX9ezqdcBAHygOg4KUeiR2kPNl4QJtskyG4CwWxlmGlfgKx07s7rGafE+DWLEYC9Wa8qK6/LPiowm17m/UlAYxdFXaBCiN0wgEw7oNmjcx/791ez+CL1+h6pd0+iSVI4bO9/YZ8LPROYef18MFm+IFIDIOgZU4eUbpBrzBb3IM1a519xgnmWXAjtRtGWEZMuHaSoLJf2pDXvaUPX6YpJeqCBFO3q/swbiJsQsy6xRW0Dwtn7umU1PGdmMoTnskTRKy9Kgzv7lf/nsUuRbzzM4ut9m1TOo27AulObMrmQB4YvLi/LEnYaRNx18yaqOceMxb/mS0tHLgcZToy9rTV+vtC21vgwfzGia2neLLe50tnIsBPP/AdTOw9ZDMRfXMCajWM22hPxvnGcw==
187 181e52f2b62f4768aa0d988936c929dc7c4a41a0 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl2UzlMQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91SDzD/0YZqtN+LK5AusJjWaTa61DRIPhJQoZD+HKg4kAzjL8zw8SxBGLxMZkGmve9QFMNzqIr5kkPk6yEKrEWYqyPtpwrv5Xh5D4d8AKfphdzwSr+BvMk4fBEvwnBhrUJtKDEiuYQdbh4+OQfQs1c3xhtinjXn30160uzFvLQY6/h4hxai2XWj4trgoNXqPHDHlQKc6kRfPpmNO2UZhG+2Xfsava2JpcP4xA2R0XkI10be5MDoGU4AFCMUcXZzIto0DYT+HOezowoNpdC1EWVHfa+bdrlzHHO7WPaTLzEPy44/IhXmNhbwFKOk5RZ/qBADQvs9BDfmIDczOoZKTC5+ESZM0PR2np5t7+JFMUeeRcINqBdSc4Aszw3iHjgNbJJ3viU72JZvGGGd9MglP590tA0proVGxQgvXDq3mtq3Se5yOLAjmRnktW5Tnt8/Z3ycuZz+QsTEMXR5uIZvgz63ibfsCGTXFYUz9h7McGgmhfKWvQw9+MH6kRbE9U8qaUumgf4zi4HNzmf8AyaMJo07DIMwWVgjlVUdWUlN/Eg61fU3wC79mV8mLVsi5/TZ986obz4csoYSYXyyez5ScRji+znSw8vUx0YhoiOQbDms/y2QZR/toyon554tHkDZsya2lhpwXs8T0IFZhERXsmz/XmT3fWnhSzyrUe6VjBMep1zn6lvQ==
187 181e52f2b62f4768aa0d988936c929dc7c4a41a0 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl2UzlMQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91SDzD/0YZqtN+LK5AusJjWaTa61DRIPhJQoZD+HKg4kAzjL8zw8SxBGLxMZkGmve9QFMNzqIr5kkPk6yEKrEWYqyPtpwrv5Xh5D4d8AKfphdzwSr+BvMk4fBEvwnBhrUJtKDEiuYQdbh4+OQfQs1c3xhtinjXn30160uzFvLQY6/h4hxai2XWj4trgoNXqPHDHlQKc6kRfPpmNO2UZhG+2Xfsava2JpcP4xA2R0XkI10be5MDoGU4AFCMUcXZzIto0DYT+HOezowoNpdC1EWVHfa+bdrlzHHO7WPaTLzEPy44/IhXmNhbwFKOk5RZ/qBADQvs9BDfmIDczOoZKTC5+ESZM0PR2np5t7+JFMUeeRcINqBdSc4Aszw3iHjgNbJJ3viU72JZvGGGd9MglP590tA0proVGxQgvXDq3mtq3Se5yOLAjmRnktW5Tnt8/Z3ycuZz+QsTEMXR5uIZvgz63ibfsCGTXFYUz9h7McGgmhfKWvQw9+MH6kRbE9U8qaUumgf4zi4HNzmf8AyaMJo07DIMwWVgjlVUdWUlN/Eg61fU3wC79mV8mLVsi5/TZ986obz4csoYSYXyyez5ScRji+znSw8vUx0YhoiOQbDms/y2QZR/toyon554tHkDZsya2lhpwXs8T0IFZhERXsmz/XmT3fWnhSzyrUe6VjBMep1zn6lvQ==
188 59338f9561099de77c684c00f76507f11e46ebe8 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl2ty1MQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91XBUD/wJqwW0cuMCUvuUODLIfWa7ZxNl1mV9eW3tFQEuLGry97s12KDwBe0Erdjj7DASl4/6Xpc4PYxelZwSw4xT1UQg7wd/C3daCq/cDXrAkl7ZNTAHu6iAnHh25mOpIBfhMbh4j3YD0A2OoI17QGScU6S7Uv0Gz1CY20lJmEqsMzuuDPm2zrdPnTWffRUuPgskAg3czaw45Na7nUBeaxN1On0O5WqMYZsCGyi14g5S0Z0LHMKRJzc/s48JUTDjTbbzJ6HBxrxWTW2v8gN2J6QDYykcLBB9kV6laal9jhWs9n/w0yWwHfBfJ+E4EiMXeRdZgGA55OCOuDxnmmONs1/Z0WwPo+vQlowEnjDMT0jPrPePZ5P4BDXZD3tGsmdXDHM7j+VfDyPh1FBFpcaej44t84X1OWtAnLZ3VMPLwobz9MOzz4wr9UuHq23hus0Fen+FJYOAlTx9qPAqBrCTpGl+h1DMKD62D7lF8Z1CxTlqg9PPBB7IZNCXoN7FZ4Wfhv1AarMVNNUgBx6m0r6OScCXrluuFklYDSIZrfgiwosXxsHW27RjxktrV4O+J1GT/chLBJFViTZg/gX/9UC3eLkzp1t6gC6T9SQ+lq0/I+1/rHQkxNaywLycBPOG1yb/59mibEwB9+Mu9anRYKFNHEktNoEmyw5G9UoZhD+1tHt4tkJCwA==
188 59338f9561099de77c684c00f76507f11e46ebe8 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl2ty1MQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91XBUD/wJqwW0cuMCUvuUODLIfWa7ZxNl1mV9eW3tFQEuLGry97s12KDwBe0Erdjj7DASl4/6Xpc4PYxelZwSw4xT1UQg7wd/C3daCq/cDXrAkl7ZNTAHu6iAnHh25mOpIBfhMbh4j3YD0A2OoI17QGScU6S7Uv0Gz1CY20lJmEqsMzuuDPm2zrdPnTWffRUuPgskAg3czaw45Na7nUBeaxN1On0O5WqMYZsCGyi14g5S0Z0LHMKRJzc/s48JUTDjTbbzJ6HBxrxWTW2v8gN2J6QDYykcLBB9kV6laal9jhWs9n/w0yWwHfBfJ+E4EiMXeRdZgGA55OCOuDxnmmONs1/Z0WwPo+vQlowEnjDMT0jPrPePZ5P4BDXZD3tGsmdXDHM7j+VfDyPh1FBFpcaej44t84X1OWtAnLZ3VMPLwobz9MOzz4wr9UuHq23hus0Fen+FJYOAlTx9qPAqBrCTpGl+h1DMKD62D7lF8Z1CxTlqg9PPBB7IZNCXoN7FZ4Wfhv1AarMVNNUgBx6m0r6OScCXrluuFklYDSIZrfgiwosXxsHW27RjxktrV4O+J1GT/chLBJFViTZg/gX/9UC3eLkzp1t6gC6T9SQ+lq0/I+1/rHQkxNaywLycBPOG1yb/59mibEwB9+Mu9anRYKFNHEktNoEmyw5G9UoZhD+1tHt4tkJCwA==
189 ca3dca416f8d5863ca6f5a4a6a6bb835dcd5feeb 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl3BrQ4QHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91ZXjEACfBdZczf0a4bmeaaxRwxXAniSS4rVkF790g22fsvSZFvQEpmwqNtsvbTt3N1V2QSDSZyhBa+/qfpuZ689VXMlR3rcJOVjo/7193QLXHOPfRn7sDeeCxjsbtXXLbLa8UT56gtT5gUa4i0LC2kHBEi+UhV9EGgSaDTBxWUFJ9RY2sosy1XFiOUlkUoHUbqUF28J3/CxEXzULWkqTOPwh94JYsgXSSS69WNZEfsuEBSPCzn8Gd7z7lWudZ/VTZBTpTji7HQxpFtSZxNzpwmcmVOH9HlEKoA1K4JoR+1TMHqSytQXlz3FMF6c6Z1G+OPpwTGCjGTkB9ZAusP3gU8KIZTTEXthiEluRtnRq1yu4K2LTyY172JPJvANAWpVEvBvn4k5c9tDOEt9RCAPqCrgNGzDTrw02+gZyyNkjcS6hPn+cDJ6OQ1j2eCQtHlqfHLSc7FsRjUSTiKSEUTdWvHbNfOYe6Yth/tnQ7TnpnS9S0eiugFzZs2f8P85Gfa3uTFQIDm67Ud+8Yu1uOxa6bhECLaXEACnLofzz8sioLsJMiOoG2HmwhyPyfZUHXlb2zdsSP3LC+gKN39VvzSxhhjrIUJoM4ulP0GP1/lkMVzOady66iLaEwDvEn4FLmu395SubHwbre1Jx83hiCQpZfPkI0PhKnh4yVm+BRGUpX97rMTGjzw==
189 ca3dca416f8d5863ca6f5a4a6a6bb835dcd5feeb 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl3BrQ4QHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91ZXjEACfBdZczf0a4bmeaaxRwxXAniSS4rVkF790g22fsvSZFvQEpmwqNtsvbTt3N1V2QSDSZyhBa+/qfpuZ689VXMlR3rcJOVjo/7193QLXHOPfRn7sDeeCxjsbtXXLbLa8UT56gtT5gUa4i0LC2kHBEi+UhV9EGgSaDTBxWUFJ9RY2sosy1XFiOUlkUoHUbqUF28J3/CxEXzULWkqTOPwh94JYsgXSSS69WNZEfsuEBSPCzn8Gd7z7lWudZ/VTZBTpTji7HQxpFtSZxNzpwmcmVOH9HlEKoA1K4JoR+1TMHqSytQXlz3FMF6c6Z1G+OPpwTGCjGTkB9ZAusP3gU8KIZTTEXthiEluRtnRq1yu4K2LTyY172JPJvANAWpVEvBvn4k5c9tDOEt9RCAPqCrgNGzDTrw02+gZyyNkjcS6hPn+cDJ6OQ1j2eCQtHlqfHLSc7FsRjUSTiKSEUTdWvHbNfOYe6Yth/tnQ7TnpnS9S0eiugFzZs2f8P85Gfa3uTFQIDm67Ud+8Yu1uOxa6bhECLaXEACnLofzz8sioLsJMiOoG2HmwhyPyfZUHXlb2zdsSP3LC+gKN39VvzSxhhjrIUJoM4ulP0GP1/lkMVzOady66iLaEwDvEn4FLmu395SubHwbre1Jx83hiCQpZfPkI0PhKnh4yVm+BRGUpX97rMTGjzw==
190 a50fecefa691c9b72a99e49aa6fe9dd13943c2bf 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl3pEYIQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91duiD/9fwJbyrXXdpoBCeW3pgiz/xKZRQq0N3UqC/5m3PGl2qPfDqTi1GA6J+O24Cpy/FXYLEKlrEG2jy/iBZnGgTpb2sgycHFlWCT7VbuS8SDE3FFloTE8ZOGy5eJRo1UXYu4vsvNtmarN1xJQPrVK4l/Co5XWXFx15H/oMXLaHzS0kzQ/rHsMr7UXM0QwtmLC0S9IMetg5EUQx9GtHHaRnh1PIyP5NxP9VQ9RK4hmT6F2g60bcsMfpgF0I/RgL3tcdUn1RNIZ2OXHBhKYL+xOUe+wadDPIyPDqLXNEqPH7xqi0MQm/jOG++AvUPM7AdVc9Y2eRFOIIBIY0nkU5LL4yVVdqoc8kgwz14xhJXGTpMDRD54F6WrQtxhbHcb+JF7QDe3i9wI1LvurW4IIA5e4DC1q9yKKxNx9cDUOMF5q9ehiW9V120LTXJnYOUwfB7D4bIhe2mpOw8yYABU3gZ0Q6iVBTH+9rZYZ9TETX6vkf/DnJXteo39OhKrZ1Z4Gj6MSAjPJLARnYGnRMgvsyHSbV0TsGA4tdEaBs3dZmUV7maxLbs70sO6r9WwUY37TcYYHGdRplD9AreDLcxvjXA73Iluoy9WBGxRWF8wftQjaE9XR4KkDFrAoqqYZwN2AwHiTjVD1lQx+xvxZeEQ3ZBDprH3Uy6TwqUo5jbvHgR2+HqaZlTg==
190 a50fecefa691c9b72a99e49aa6fe9dd13943c2bf 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl3pEYIQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91duiD/9fwJbyrXXdpoBCeW3pgiz/xKZRQq0N3UqC/5m3PGl2qPfDqTi1GA6J+O24Cpy/FXYLEKlrEG2jy/iBZnGgTpb2sgycHFlWCT7VbuS8SDE3FFloTE8ZOGy5eJRo1UXYu4vsvNtmarN1xJQPrVK4l/Co5XWXFx15H/oMXLaHzS0kzQ/rHsMr7UXM0QwtmLC0S9IMetg5EUQx9GtHHaRnh1PIyP5NxP9VQ9RK4hmT6F2g60bcsMfpgF0I/RgL3tcdUn1RNIZ2OXHBhKYL+xOUe+wadDPIyPDqLXNEqPH7xqi0MQm/jOG++AvUPM7AdVc9Y2eRFOIIBIY0nkU5LL4yVVdqoc8kgwz14xhJXGTpMDRD54F6WrQtxhbHcb+JF7QDe3i9wI1LvurW4IIA5e4DC1q9yKKxNx9cDUOMF5q9ehiW9V120LTXJnYOUwfB7D4bIhe2mpOw8yYABU3gZ0Q6iVBTH+9rZYZ9TETX6vkf/DnJXteo39OhKrZ1Z4Gj6MSAjPJLARnYGnRMgvsyHSbV0TsGA4tdEaBs3dZmUV7maxLbs70sO6r9WwUY37TcYYHGdRplD9AreDLcxvjXA73Iluoy9WBGxRWF8wftQjaE9XR4KkDFrAoqqYZwN2AwHiTjVD1lQx+xvxZeEQ3ZBDprH3Uy6TwqUo5jbvHgR2+HqaZlTg==
191 b4c82b70418022e67cc0e69b1aa3c3aa43aa1d29 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl4TkWgQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91aV6D/4xzlluOwsBhLXWUi7bDp4HtYnyDhq4XuDORAMO5mCZ7I7J6uqGoViqH4AhXoo3yPp1cDiRzzl172xpec38uTL8C5zHhARKuAl5Pn1A8rYORvYzT9nsDh4MAtfTokhg81awRzhun9xtPUT2nETAOgampW0g7r241MSR1j0myAkC7zqO3yf+1rYo7kiv7fh+74MkrSn4HEmEaLsI5gW05tFR+ip6vpm6eikFinqeVJegDCuyTPMvH0D9ZeBNlyoOfdEd6DDYsWvWAmLSO9FGbb03R5aOFRp7RmQRFH/qcueeePa/9Z1zO+YyCeBy0wvWCkjfLMY99HhNhdNfy/qC/69V5RGQYvaapy6BEAi4eCH73hsxzCQpKopUl9VrpwhNasJ41KWc90RsPO91bkTdDddF7e2qjq762aNgm7ysEzIHMgSsMgsE9w8hz70RE7bk/gYn26ak3XP4nCOY0OJQ8mgaElN/FP1kxqqT7MM7WeMiNMFTD1gvWwEAu9Y47AwUedkTrykQsAFzc+CyaIaW+/Kuyv0j5E7v8zAcVTTX4xIyqR4yL2Nwe1rYE4MZgs0L9gQ3rcdyft6899gAiiq96MPR3gLJUPbBz2azH/e0CzNXvDJa39jIm2ez0qC7c88NhTKhFjHE9EW5GI3g8mhS5dJXCnUSq4spgtrJdfGenL3vLw==
191 b4c82b70418022e67cc0e69b1aa3c3aa43aa1d29 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl4TkWgQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91aV6D/4xzlluOwsBhLXWUi7bDp4HtYnyDhq4XuDORAMO5mCZ7I7J6uqGoViqH4AhXoo3yPp1cDiRzzl172xpec38uTL8C5zHhARKuAl5Pn1A8rYORvYzT9nsDh4MAtfTokhg81awRzhun9xtPUT2nETAOgampW0g7r241MSR1j0myAkC7zqO3yf+1rYo7kiv7fh+74MkrSn4HEmEaLsI5gW05tFR+ip6vpm6eikFinqeVJegDCuyTPMvH0D9ZeBNlyoOfdEd6DDYsWvWAmLSO9FGbb03R5aOFRp7RmQRFH/qcueeePa/9Z1zO+YyCeBy0wvWCkjfLMY99HhNhdNfy/qC/69V5RGQYvaapy6BEAi4eCH73hsxzCQpKopUl9VrpwhNasJ41KWc90RsPO91bkTdDddF7e2qjq762aNgm7ysEzIHMgSsMgsE9w8hz70RE7bk/gYn26ak3XP4nCOY0OJQ8mgaElN/FP1kxqqT7MM7WeMiNMFTD1gvWwEAu9Y47AwUedkTrykQsAFzc+CyaIaW+/Kuyv0j5E7v8zAcVTTX4xIyqR4yL2Nwe1rYE4MZgs0L9gQ3rcdyft6899gAiiq96MPR3gLJUPbBz2azH/e0CzNXvDJa39jIm2ez0qC7c88NhTKhFjHE9EW5GI3g8mhS5dJXCnUSq4spgtrJdfGenL3vLw==
192 84a0102c05c7852c8215ef6cf21d809927586b69 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl4nP/4QHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91VaHD/93dVKKFMJtclNMIG2AK3yZjfQ3HaqIuK1CqOuZyVQmk5fbnLydbi5RjIQMkaYPSKjDz0OKlfzDYo6kQrZrZUzIxzPBOz8/NMRSHGAWqvzQMbQGjYILsqDQ+wbol9wk8IDoyFzIcB4gPED1U5kWVCBTEqRrYiGP4siiycXVO5334Q5zOrvcjze0ksufbKQhL6SEUovfLtpX+DW6Z841LmR53aquEH8iBGswHKRt4ukyvmXTQAgea4lWXZXj3DH6oZqe0yzg5ogF4vFaoIgZDpBh2LZKuh6gwJtvA9jsFj5HVOzYDcllkgpaOTV1g/xKPo1EkLpt0W0vd/4vnjSKNo0fmOTvZzI9vCCXLlRSUhoboY6AFHN7XtL9gYWI0rj81p/WrnnQQ7Iv2YHS1KCLr765HW6mjREwFMLD9RrLLDQ0DWIyNuGq8/yrqoruAhidEE9ifITnNh38wVISdiPxORj3onZkAn7VbOWQnlJtYkynlk2t3HnHWfduLGc2G0BkLvg4YfEDsZBA+ssr+TspkZ1dVAq8kf4JKNR01sfjBF6Fj1zRPkoexV40/pPiW55ikfOI9LRHxRiOUyndLviIBv1Mbm90PZ89lT4OTMejD8hhb4omlVxH3HFv4j7TozuPFOuouH7ARRwbPFl/0ldPlESoGvFiyOrqNzlql+JvyLUSbg==
192 84a0102c05c7852c8215ef6cf21d809927586b69 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl4nP/4QHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91VaHD/93dVKKFMJtclNMIG2AK3yZjfQ3HaqIuK1CqOuZyVQmk5fbnLydbi5RjIQMkaYPSKjDz0OKlfzDYo6kQrZrZUzIxzPBOz8/NMRSHGAWqvzQMbQGjYILsqDQ+wbol9wk8IDoyFzIcB4gPED1U5kWVCBTEqRrYiGP4siiycXVO5334Q5zOrvcjze0ksufbKQhL6SEUovfLtpX+DW6Z841LmR53aquEH8iBGswHKRt4ukyvmXTQAgea4lWXZXj3DH6oZqe0yzg5ogF4vFaoIgZDpBh2LZKuh6gwJtvA9jsFj5HVOzYDcllkgpaOTV1g/xKPo1EkLpt0W0vd/4vnjSKNo0fmOTvZzI9vCCXLlRSUhoboY6AFHN7XtL9gYWI0rj81p/WrnnQQ7Iv2YHS1KCLr765HW6mjREwFMLD9RrLLDQ0DWIyNuGq8/yrqoruAhidEE9ifITnNh38wVISdiPxORj3onZkAn7VbOWQnlJtYkynlk2t3HnHWfduLGc2G0BkLvg4YfEDsZBA+ssr+TspkZ1dVAq8kf4JKNR01sfjBF6Fj1zRPkoexV40/pPiW55ikfOI9LRHxRiOUyndLviIBv1Mbm90PZ89lT4OTMejD8hhb4omlVxH3HFv4j7TozuPFOuouH7ARRwbPFl/0ldPlESoGvFiyOrqNzlql+JvyLUSbg==
193 e4344e463c0c888a2f437b78b5982ecdf3f6650a 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl4rFTIQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91eStD/wNSk7/07dvzItYmxg9LuUInYH17pZrXm8+jGEejoYZw74R1BHusFBcnmB1URldbq4IdzlxXNKrcnmJH/lgYCdbZ8OG0MaQrEIyLz0WmY27ARb/AwDuiy/dn0X3NgvQjqPffLHrYHmdqvqBsb0+qG3v7b0xt+BGDkebt1TXCy9wjIa1iqCOQ0EJi2dcuD2dWlhPM2kuslMjKlqe57D5bwaHBDS6K9Sd4VABRdv7mExrMBSr1SnkasrBsvb47UVXYUJRI3GGyA/wYYAi3fW9ZxG25x2SA0rjF5U68c5rmQMD94FLmaSoaqSvigkSBDOF/DIwlRO5vB4NlP7/+TjNOo92r4GbTZyMTnrsORqQJKcMrpfVbM8gRngPTJz2FxBSoz86HQ3wVXnS0gVUJNM+ctWdvzvtrv1Np3wF0/zWHddrtfYdNgnuyKjQL3chpJs7y5aQxdgU1vHdf4X2NwhA77Cf/U6bSemhR+MfZlp4it7pZiu96b8jKsEbKrCi998tKCKVv70WhGXce3gebKPY3Gn/qUL6X3rx4Uj5CPrIjWZNhwRJJ3BXSTnKog2eUIWJC0rXXrGRV6Sf6514zbi0MCOexnAjZM1xs5NUd/wrugDnMp4+P+ZPZyseeVB51NSnGhxlYLwD9EN+4ocjyBzMINOcQw1GPkB5Rrqwh+19q5SnvA==
193 e4344e463c0c888a2f437b78b5982ecdf3f6650a 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl4rFTIQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91eStD/wNSk7/07dvzItYmxg9LuUInYH17pZrXm8+jGEejoYZw74R1BHusFBcnmB1URldbq4IdzlxXNKrcnmJH/lgYCdbZ8OG0MaQrEIyLz0WmY27ARb/AwDuiy/dn0X3NgvQjqPffLHrYHmdqvqBsb0+qG3v7b0xt+BGDkebt1TXCy9wjIa1iqCOQ0EJi2dcuD2dWlhPM2kuslMjKlqe57D5bwaHBDS6K9Sd4VABRdv7mExrMBSr1SnkasrBsvb47UVXYUJRI3GGyA/wYYAi3fW9ZxG25x2SA0rjF5U68c5rmQMD94FLmaSoaqSvigkSBDOF/DIwlRO5vB4NlP7/+TjNOo92r4GbTZyMTnrsORqQJKcMrpfVbM8gRngPTJz2FxBSoz86HQ3wVXnS0gVUJNM+ctWdvzvtrv1Np3wF0/zWHddrtfYdNgnuyKjQL3chpJs7y5aQxdgU1vHdf4X2NwhA77Cf/U6bSemhR+MfZlp4it7pZiu96b8jKsEbKrCi998tKCKVv70WhGXce3gebKPY3Gn/qUL6X3rx4Uj5CPrIjWZNhwRJJ3BXSTnKog2eUIWJC0rXXrGRV6Sf6514zbi0MCOexnAjZM1xs5NUd/wrugDnMp4+P+ZPZyseeVB51NSnGhxlYLwD9EN+4ocjyBzMINOcQw1GPkB5Rrqwh+19q5SnvA==
194 7f5410dfc8a64bb587d19637deb95d378fd1eb5c 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl44RUUQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91WcUD/9em14ckTP9APTrSpe6y4FLS6cIUZabNN6wDXjTrHmS26hoNvWrT+RpWQ5XSOOJhZdhjkR1k87EOw9+m6+36ZaL+RXYnjrbku9fxbbFBraGTFy0JZHAT6v57uQ8P7XwqN4dGvXXpgE5UuY5sp1uDRbtIPNts3iWJKAnIazxUnyotHNtJQNESHySomzR1s93z1oOMpHapAqUmPbcZywg4otWjrOnkhOok3Sa3TgGthpHbM0qmh6J9ZaRBXsKEpLkjCRNggdvqww1w4omcAJzY4V5tG8WfhW+Xl8zBBe0K5m/ug3e25sWR5Dqm4+qUO0HZWQ3m3/M7CCuQrWFXTkr7nKac50vtFzsqHlHNoaiKnvQKoruQs3266TGsrzCCOSy8BqmpysD6sB79owLKoh0LfFOcSwG9kZ8sovEvTfrRn8g3YAp7XbXkDxbcLMijr7P4gWq8sC1NZJn1yhLXitcCfAAuVrVQfPVdt2pp8Ry2NdGnHjikQjOn/wAKlYJ5F8JMdn6eEI/Gveg2g8uR9kp/9zaXRx6rU3ccuZQ7cBQbBlBsmmpd7gJRp2v0NKsV8hXtCPnBvcfCqgYHLg7FQVq1wKe5glvtmx9uPZNsl/S++fSxGoXfp9wVi048J42KyEH6yvoySCvbYeSFQvMfAoD1xJ4xWtT8ZEj6oiHvzHw1u/zgw==
194 7f5410dfc8a64bb587d19637deb95d378fd1eb5c 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl44RUUQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91WcUD/9em14ckTP9APTrSpe6y4FLS6cIUZabNN6wDXjTrHmS26hoNvWrT+RpWQ5XSOOJhZdhjkR1k87EOw9+m6+36ZaL+RXYnjrbku9fxbbFBraGTFy0JZHAT6v57uQ8P7XwqN4dGvXXpgE5UuY5sp1uDRbtIPNts3iWJKAnIazxUnyotHNtJQNESHySomzR1s93z1oOMpHapAqUmPbcZywg4otWjrOnkhOok3Sa3TgGthpHbM0qmh6J9ZaRBXsKEpLkjCRNggdvqww1w4omcAJzY4V5tG8WfhW+Xl8zBBe0K5m/ug3e25sWR5Dqm4+qUO0HZWQ3m3/M7CCuQrWFXTkr7nKac50vtFzsqHlHNoaiKnvQKoruQs3266TGsrzCCOSy8BqmpysD6sB79owLKoh0LfFOcSwG9kZ8sovEvTfrRn8g3YAp7XbXkDxbcLMijr7P4gWq8sC1NZJn1yhLXitcCfAAuVrVQfPVdt2pp8Ry2NdGnHjikQjOn/wAKlYJ5F8JMdn6eEI/Gveg2g8uR9kp/9zaXRx6rU3ccuZQ7cBQbBlBsmmpd7gJRp2v0NKsV8hXtCPnBvcfCqgYHLg7FQVq1wKe5glvtmx9uPZNsl/S++fSxGoXfp9wVi048J42KyEH6yvoySCvbYeSFQvMfAoD1xJ4xWtT8ZEj6oiHvzHw1u/zgw==
195 6d121acbb82e65fe4dd3c2318a1b61981b958492 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl5f3IEQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91WoeD/9qhywGg/TI/FJEeJN5bJjcpB/YQeYDWCHh69yUmMPenf+6CaV/3QPc3R8JyQSKWwGUwc0IgZiJBb/HoUvBzpQyTvmGqddWsIGBpdGAkbLmRrE5BakR7Shs987a3Oq4hB03DJD4sQ1VitWg2OvGNd8rl1kSIF8aIErVI6ZiSw5eYemc/1VyBJXHWSFmcfnQqdsyPppH9e9/TAhio+YP4EmLmoxUcyRSb3UbtO2NT9+DEADaex+H2l9evg7AkTieVd6N163uqsLJIxSfCh5ZVmzaGW6uEoyC4U+9bkAyVE3Cy5z2giYblBzUkO9xqEZoA4tOM+b+gHokY8Sq3iGVw046CIW5+FjU9B5+7hCqWThYjnpnt+RomtHxrkqQ9SSHYnEWb4YTHqs+J7lWbm3ErjF08hYOyMA9/VT47UAKw4XL4Ss/1Pr7YezdmwB4jn7dqvslNvTqRAUOzB/15YeCfbd23SL4YzGaKBs9ajkxFFeCNNpLQ8CRm3a7/K6qkYyfSUpgUX7xBmRQTvUgr3nVk1epH/kOKwryy94Z+nlHF0qEMEq+1QOa5yvt3Kkr4H03pOFbLhdpjID5IYP4rRQTKB9yOS3XWBCE63AQVc7uuaBGPMCSLaKRAFDUXWY7GzCqda88WeN5BFC5iHrQTYE1IQ5YaWu38QMsJt2HHVc27+BuLA==
195 6d121acbb82e65fe4dd3c2318a1b61981b958492 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl5f3IEQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91WoeD/9qhywGg/TI/FJEeJN5bJjcpB/YQeYDWCHh69yUmMPenf+6CaV/3QPc3R8JyQSKWwGUwc0IgZiJBb/HoUvBzpQyTvmGqddWsIGBpdGAkbLmRrE5BakR7Shs987a3Oq4hB03DJD4sQ1VitWg2OvGNd8rl1kSIF8aIErVI6ZiSw5eYemc/1VyBJXHWSFmcfnQqdsyPppH9e9/TAhio+YP4EmLmoxUcyRSb3UbtO2NT9+DEADaex+H2l9evg7AkTieVd6N163uqsLJIxSfCh5ZVmzaGW6uEoyC4U+9bkAyVE3Cy5z2giYblBzUkO9xqEZoA4tOM+b+gHokY8Sq3iGVw046CIW5+FjU9B5+7hCqWThYjnpnt+RomtHxrkqQ9SSHYnEWb4YTHqs+J7lWbm3ErjF08hYOyMA9/VT47UAKw4XL4Ss/1Pr7YezdmwB4jn7dqvslNvTqRAUOzB/15YeCfbd23SL4YzGaKBs9ajkxFFeCNNpLQ8CRm3a7/K6qkYyfSUpgUX7xBmRQTvUgr3nVk1epH/kOKwryy94Z+nlHF0qEMEq+1QOa5yvt3Kkr4H03pOFbLhdpjID5IYP4rRQTKB9yOS3XWBCE63AQVc7uuaBGPMCSLaKRAFDUXWY7GzCqda88WeN5BFC5iHrQTYE1IQ5YaWu38QMsJt2HHVc27+BuLA==
196 8fca7e8449a847e3cf1054f2c07b51237699fad3 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl6GDVQQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91egzEACNEyQwLWCQEeNyxXKuTsnXhYU/au7nSGOti/9+zg/22SSceMsVcIyNr2ZnkMf3hnzBjL7Efsthif0QXyfB0LZDXwNuDmNlDtUV2veyVGSDE2UqiSbDBRu6MYTvtfYX87RmSWla3HHO09pwpcrhxyHs3mliQsXyB2+D+ovTOIjYukQLnh34jQnwiWEYLDXkHEHHTpdXqAnA7tVen3ardLyTWgky6DUwlfcnoVsAPXnDkqQ9aE2w7SoAsNtEAddmkjKoYYdBkV5aUInU/DyFVF7qnlCcvWm+EkN1708xZUQ1KzdAyeeoIrMkBgpSoyeNQ9pcU3T7B100UxLo/FP/A7y96b2kHnKJU6fVyD3OeHvP9SeucurC6jn2YoG3e1wSOQcbEuCsdGjqgAHnKt2SMPsEBu2qJJcUdco9tANN5BdntBo7bLc/zcpXZH3TkRfRSndWXPaXDJaQNvbH7aLIUTCP9oQaqTN+9BQ+Egt7YsB4C58JZmC87FAuekDULc4LWK2gDPFf7F/PvBnMh7+YylPl/8LLrEnz2Q/GM0S1HLhBrDf6vzxV5wVzCu9Q2N0PCkg6lDAJFVWLTEbxcRukKxbyK88Yzrb4GuUY4F5V21fN4vuxkOay7eoiXUcHMN2IN+DwhNWQSm5pUnpqGTfCYj/ZBbAykP2UnVOClL6O2JQA2A==
196 8fca7e8449a847e3cf1054f2c07b51237699fad3 0 iQJEBAABCAAuFiEEK8zhT1xnJaouqK63ucncgkqlvdUFAl6GDVQQHHJhZkBkdXJpbjQyLmNvbQAKCRC5ydyCSqW91egzEACNEyQwLWCQEeNyxXKuTsnXhYU/au7nSGOti/9+zg/22SSceMsVcIyNr2ZnkMf3hnzBjL7Efsthif0QXyfB0LZDXwNuDmNlDtUV2veyVGSDE2UqiSbDBRu6MYTvtfYX87RmSWla3HHO09pwpcrhxyHs3mliQsXyB2+D+ovTOIjYukQLnh34jQnwiWEYLDXkHEHHTpdXqAnA7tVen3ardLyTWgky6DUwlfcnoVsAPXnDkqQ9aE2w7SoAsNtEAddmkjKoYYdBkV5aUInU/DyFVF7qnlCcvWm+EkN1708xZUQ1KzdAyeeoIrMkBgpSoyeNQ9pcU3T7B100UxLo/FP/A7y96b2kHnKJU6fVyD3OeHvP9SeucurC6jn2YoG3e1wSOQcbEuCsdGjqgAHnKt2SMPsEBu2qJJcUdco9tANN5BdntBo7bLc/zcpXZH3TkRfRSndWXPaXDJaQNvbH7aLIUTCP9oQaqTN+9BQ+Egt7YsB4C58JZmC87FAuekDULc4LWK2gDPFf7F/PvBnMh7+YylPl/8LLrEnz2Q/GM0S1HLhBrDf6vzxV5wVzCu9Q2N0PCkg6lDAJFVWLTEbxcRukKxbyK88Yzrb4GuUY4F5V21fN4vuxkOay7eoiXUcHMN2IN+DwhNWQSm5pUnpqGTfCYj/ZBbAykP2UnVOClL6O2JQA2A==
197 26ce8e7515036d3431a03aaeb7bc72dd96cb1112 0 iQJJBAABCgAzFiEE64UTlbQiPuL3ugso2lR0C/CHMroFAl6YlRUVHDc4OTVwdWxraXRAZ21haWwuY29tAAoJENpUdAvwhzK6Z3YP/iOqphn99v0z2OupCl0q8CepbcdZMJWW3j00OAHYSO43M0FULpMpzC2o+kZDeqeLyzN7DsjoGts2cUnAOe9WX73sPkX1n1dbiDcUSsRqNND+tCkEZMtTn4DaGNIq1zSkkm8Q7O/1uwZPnX6FaIRMBs9qGbdfmMPNEvzny2tgrKc3ra1+AA8RCdtsbpqhjy+xf+EKVB/SMsQVVSJEgPkUkW6PwpaspdrxQKgZrb7C7Jx/gRVzMTUmCQe1sVCSnZNO3I/woAqDY2UNg7/hBubeRh/EjoH1o4ONTXgBQdYCl7QdcwDHpDc2HstonrFq51qxBecHDVw+ZKQds63Ixtxuab3SK0o/SWabZ1v8bGaWnyWnRWXL/1qkyFWly+fjEGGlv1kHl3n0UmwlUY8FQJCYDZgR0FqQGXAF3vMJOEp82ysk6jWN/7NRzcnoUC7HpNo1jPMiPRjskgVf3bhErfUQnhlF1YsVu/jPTixyfftbiaZmwILMkaPF8Kg3Cyf63p2cdcnTHdbP1U6ncR+BucthlbFei4WL0J2iERb8TBeCxOyCHlEUq8kampjbmPXN7VxnK4oX3xeBTf8mMbvrD5Fv3svRD+SkCCKu/MwQvB1VT6q425TSKHbCWeNqGjVLvetpx+skVH7eaXLEQ3wlCfo/0OQTRimx2O73EnOF5r8Q2POm
@@ -1,209 +1,210 b''
1 d40cc5aacc31ed673d9b5b24f98bee78c283062c 0.4f
1 d40cc5aacc31ed673d9b5b24f98bee78c283062c 0.4f
2 1c590d34bf61e2ea12c71738e5a746cd74586157 0.4e
2 1c590d34bf61e2ea12c71738e5a746cd74586157 0.4e
3 7eca4cfa8aad5fce9a04f7d8acadcd0452e2f34e 0.4d
3 7eca4cfa8aad5fce9a04f7d8acadcd0452e2f34e 0.4d
4 b4d0c3786ad3e47beacf8412157326a32b6d25a4 0.4c
4 b4d0c3786ad3e47beacf8412157326a32b6d25a4 0.4c
5 f40273b0ad7b3a6d3012fd37736d0611f41ecf54 0.5
5 f40273b0ad7b3a6d3012fd37736d0611f41ecf54 0.5
6 0a28dfe59f8fab54a5118c5be4f40da34a53cdb7 0.5b
6 0a28dfe59f8fab54a5118c5be4f40da34a53cdb7 0.5b
7 12e0fdbc57a0be78f0e817fd1d170a3615cd35da 0.6
7 12e0fdbc57a0be78f0e817fd1d170a3615cd35da 0.6
8 4ccf3de52989b14c3d84e1097f59e39a992e00bd 0.6b
8 4ccf3de52989b14c3d84e1097f59e39a992e00bd 0.6b
9 eac9c8efcd9bd8244e72fb6821f769f450457a32 0.6c
9 eac9c8efcd9bd8244e72fb6821f769f450457a32 0.6c
10 979c049974485125e1f9357f6bbe9c1b548a64c3 0.7
10 979c049974485125e1f9357f6bbe9c1b548a64c3 0.7
11 3a56574f329a368d645853e0f9e09472aee62349 0.8
11 3a56574f329a368d645853e0f9e09472aee62349 0.8
12 6a03cff2b0f5d30281e6addefe96b993582f2eac 0.8.1
12 6a03cff2b0f5d30281e6addefe96b993582f2eac 0.8.1
13 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0.9
13 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0.9
14 2be3001847cb18a23c403439d9e7d0ace30804e9 0.9.1
14 2be3001847cb18a23c403439d9e7d0ace30804e9 0.9.1
15 36a957364b1b89c150f2d0e60a99befe0ee08bd3 0.9.2
15 36a957364b1b89c150f2d0e60a99befe0ee08bd3 0.9.2
16 27230c29bfec36d5540fbe1c976810aefecfd1d2 0.9.3
16 27230c29bfec36d5540fbe1c976810aefecfd1d2 0.9.3
17 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0.9.4
17 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0.9.4
18 23889160905a1b09fffe1c07378e9fc1827606eb 0.9.5
18 23889160905a1b09fffe1c07378e9fc1827606eb 0.9.5
19 bae2e9c838e90a393bae3973a7850280413e091a 1.0
19 bae2e9c838e90a393bae3973a7850280413e091a 1.0
20 d5cbbe2c49cee22a9fbeb9ea41daa0ac4e26b846 1.0.1
20 d5cbbe2c49cee22a9fbeb9ea41daa0ac4e26b846 1.0.1
21 d2375bbee6d47e62ba8e415c86e83a465dc4dce9 1.0.2
21 d2375bbee6d47e62ba8e415c86e83a465dc4dce9 1.0.2
22 2a67430f92f15ea5159c26b09ec4839a0c549a26 1.1
22 2a67430f92f15ea5159c26b09ec4839a0c549a26 1.1
23 3773e510d433969e277b1863c317b674cbee2065 1.1.1
23 3773e510d433969e277b1863c317b674cbee2065 1.1.1
24 11a4eb81fb4f4742451591489e2797dc47903277 1.1.2
24 11a4eb81fb4f4742451591489e2797dc47903277 1.1.2
25 11efa41037e280d08cfb07c09ad485df30fb0ea8 1.2
25 11efa41037e280d08cfb07c09ad485df30fb0ea8 1.2
26 02981000012e3adf40c4849bd7b3d5618f9ce82d 1.2.1
26 02981000012e3adf40c4849bd7b3d5618f9ce82d 1.2.1
27 196d40e7c885fa6e95f89134809b3ec7bdbca34b 1.3
27 196d40e7c885fa6e95f89134809b3ec7bdbca34b 1.3
28 3ef6c14a1e8e83a31226f5881b7fe6095bbfa6f6 1.3.1
28 3ef6c14a1e8e83a31226f5881b7fe6095bbfa6f6 1.3.1
29 31ec469f9b556f11819937cf68ee53f2be927ebf 1.4
29 31ec469f9b556f11819937cf68ee53f2be927ebf 1.4
30 439d7ea6fe3aa4ab9ec274a68846779153789de9 1.4.1
30 439d7ea6fe3aa4ab9ec274a68846779153789de9 1.4.1
31 296a0b14a68621f6990c54fdba0083f6f20935bf 1.4.2
31 296a0b14a68621f6990c54fdba0083f6f20935bf 1.4.2
32 4aa619c4c2c09907034d9824ebb1dd0e878206eb 1.4.3
32 4aa619c4c2c09907034d9824ebb1dd0e878206eb 1.4.3
33 ff2704a8ded37fbebd8b6eb5ec733731d725da8a 1.5
33 ff2704a8ded37fbebd8b6eb5ec733731d725da8a 1.5
34 2b01dab594167bc0dd33331dbaa6dca3dca1b3aa 1.5.1
34 2b01dab594167bc0dd33331dbaa6dca3dca1b3aa 1.5.1
35 39f725929f0c48c5fb3b90c071fc3066012456ca 1.5.2
35 39f725929f0c48c5fb3b90c071fc3066012456ca 1.5.2
36 fdcf80f26604f233dc4d8f0a5ef9d7470e317e8a 1.5.3
36 fdcf80f26604f233dc4d8f0a5ef9d7470e317e8a 1.5.3
37 24fe2629c6fd0c74c90bd066e77387c2b02e8437 1.5.4
37 24fe2629c6fd0c74c90bd066e77387c2b02e8437 1.5.4
38 f786fc4b8764cd2a5526d259cf2f94d8a66924d9 1.6
38 f786fc4b8764cd2a5526d259cf2f94d8a66924d9 1.6
39 bf1774d95bde614af3956d92b20e2a0c68c5fec7 1.6.1
39 bf1774d95bde614af3956d92b20e2a0c68c5fec7 1.6.1
40 c00f03a4982e467fb6b6bd45908767db6df4771d 1.6.2
40 c00f03a4982e467fb6b6bd45908767db6df4771d 1.6.2
41 ff5cec76b1c5b6be9c3bb923aae8c3c6d079d6b9 1.6.3
41 ff5cec76b1c5b6be9c3bb923aae8c3c6d079d6b9 1.6.3
42 93d8bff78c96fe7e33237b257558ee97290048a4 1.6.4
42 93d8bff78c96fe7e33237b257558ee97290048a4 1.6.4
43 333421b9e0f96c7bc788e5667c146a58a9440a55 1.7
43 333421b9e0f96c7bc788e5667c146a58a9440a55 1.7
44 4438875ec01bd0fc32be92b0872eb6daeed4d44f 1.7.1
44 4438875ec01bd0fc32be92b0872eb6daeed4d44f 1.7.1
45 6aff4f144ad356311318b0011df0bb21f2c97429 1.7.2
45 6aff4f144ad356311318b0011df0bb21f2c97429 1.7.2
46 e3bf16703e2601de99e563cdb3a5d50b64e6d320 1.7.3
46 e3bf16703e2601de99e563cdb3a5d50b64e6d320 1.7.3
47 a6c855c32ea081da3c3b8ff628f1847ff271482f 1.7.4
47 a6c855c32ea081da3c3b8ff628f1847ff271482f 1.7.4
48 2b2155623ee2559caf288fd333f30475966c4525 1.7.5
48 2b2155623ee2559caf288fd333f30475966c4525 1.7.5
49 2616325766e3504c8ae7c84bd15ee610901fe91d 1.8
49 2616325766e3504c8ae7c84bd15ee610901fe91d 1.8
50 aa1f3be38ab127280761889d2dca906ca465b5f4 1.8.1
50 aa1f3be38ab127280761889d2dca906ca465b5f4 1.8.1
51 b032bec2c0a651ca0ddecb65714bfe6770f67d70 1.8.2
51 b032bec2c0a651ca0ddecb65714bfe6770f67d70 1.8.2
52 3cb1e95676ad089596bd81d0937cad37d6e3b7fb 1.8.3
52 3cb1e95676ad089596bd81d0937cad37d6e3b7fb 1.8.3
53 733af5d9f6b22387913e1d11350fb8cb7c1487dd 1.8.4
53 733af5d9f6b22387913e1d11350fb8cb7c1487dd 1.8.4
54 de9eb6b1da4fc522b1cab16d86ca166204c24f25 1.9
54 de9eb6b1da4fc522b1cab16d86ca166204c24f25 1.9
55 4a43e23b8c55b4566b8200bf69fe2158485a2634 1.9.1
55 4a43e23b8c55b4566b8200bf69fe2158485a2634 1.9.1
56 d629f1e89021103f1753addcef6b310e4435b184 1.9.2
56 d629f1e89021103f1753addcef6b310e4435b184 1.9.2
57 351a9292e430e35766c552066ed3e87c557b803b 1.9.3
57 351a9292e430e35766c552066ed3e87c557b803b 1.9.3
58 384082750f2c51dc917d85a7145748330fa6ef4d 2.0-rc
58 384082750f2c51dc917d85a7145748330fa6ef4d 2.0-rc
59 41453d55b481ddfcc1dacb445179649e24ca861d 2.0
59 41453d55b481ddfcc1dacb445179649e24ca861d 2.0
60 195dbd1cef0c2f9f8bcf4ea303238105f716bda3 2.0.1
60 195dbd1cef0c2f9f8bcf4ea303238105f716bda3 2.0.1
61 6344043924497cd06d781d9014c66802285072e4 2.0.2
61 6344043924497cd06d781d9014c66802285072e4 2.0.2
62 db33555eafeaf9df1e18950e29439eaa706d399b 2.1-rc
62 db33555eafeaf9df1e18950e29439eaa706d399b 2.1-rc
63 2aa5b51f310fb3befd26bed99c02267f5c12c734 2.1
63 2aa5b51f310fb3befd26bed99c02267f5c12c734 2.1
64 53e2cd303ecf8ca7c7eeebd785c34e5ed6b0f4a4 2.1.1
64 53e2cd303ecf8ca7c7eeebd785c34e5ed6b0f4a4 2.1.1
65 b9bd95e61b49c221c4cca24e6da7c946fc02f992 2.1.2
65 b9bd95e61b49c221c4cca24e6da7c946fc02f992 2.1.2
66 d9e2f09d5488c395ae9ddbb320ceacd24757e055 2.2-rc
66 d9e2f09d5488c395ae9ddbb320ceacd24757e055 2.2-rc
67 00182b3d087909e3c3ae44761efecdde8f319ef3 2.2
67 00182b3d087909e3c3ae44761efecdde8f319ef3 2.2
68 5983de86462c5a9f42a3ad0f5e90ce5b1d221d25 2.2.1
68 5983de86462c5a9f42a3ad0f5e90ce5b1d221d25 2.2.1
69 85a358df5bbbe404ca25730c9c459b34263441dc 2.2.2
69 85a358df5bbbe404ca25730c9c459b34263441dc 2.2.2
70 b013baa3898e117959984fc64c29d8c784d2f28b 2.2.3
70 b013baa3898e117959984fc64c29d8c784d2f28b 2.2.3
71 a06e2681dd1786e2354d84a5fa9c1c88dd4fa3e0 2.3-rc
71 a06e2681dd1786e2354d84a5fa9c1c88dd4fa3e0 2.3-rc
72 7f5094bb3f423fc799e471aac2aee81a7ce57a0b 2.3
72 7f5094bb3f423fc799e471aac2aee81a7ce57a0b 2.3
73 072209ae4ddb654eb2d5fd35bff358c738414432 2.3.1
73 072209ae4ddb654eb2d5fd35bff358c738414432 2.3.1
74 b3f0f9a39c4e1d0250048cd803ab03542d6f140a 2.3.2
74 b3f0f9a39c4e1d0250048cd803ab03542d6f140a 2.3.2
75 d118a4f4fd16d9b558ec3f3e87bfee772861d2b7 2.4-rc
75 d118a4f4fd16d9b558ec3f3e87bfee772861d2b7 2.4-rc
76 195ad823b5d58c68903a6153a25e3fb4ed25239d 2.4
76 195ad823b5d58c68903a6153a25e3fb4ed25239d 2.4
77 0c10cf8191469e7c3c8844922e17e71a176cb7cb 2.4.1
77 0c10cf8191469e7c3c8844922e17e71a176cb7cb 2.4.1
78 a4765077b65e6ae29ba42bab7834717b5072d5ba 2.4.2
78 a4765077b65e6ae29ba42bab7834717b5072d5ba 2.4.2
79 f5fbe15ca7449f2c9a3cf817c86d0ae68b307214 2.5-rc
79 f5fbe15ca7449f2c9a3cf817c86d0ae68b307214 2.5-rc
80 a6088c05e43a8aee0472ca3a4f6f8d7dd914ebbf 2.5
80 a6088c05e43a8aee0472ca3a4f6f8d7dd914ebbf 2.5
81 7511d4df752e61fe7ae4f3682e0a0008573b0402 2.5.1
81 7511d4df752e61fe7ae4f3682e0a0008573b0402 2.5.1
82 5b7175377babacce80a6c1e12366d8032a6d4340 2.5.2
82 5b7175377babacce80a6c1e12366d8032a6d4340 2.5.2
83 50c922c1b5145dab8baefefb0437d363b6a6c21c 2.5.3
83 50c922c1b5145dab8baefefb0437d363b6a6c21c 2.5.3
84 8a7bd2dccd44ed571afe7424cd7f95594f27c092 2.5.4
84 8a7bd2dccd44ed571afe7424cd7f95594f27c092 2.5.4
85 292cd385856d98bacb2c3086f8897bc660c2beea 2.6-rc
85 292cd385856d98bacb2c3086f8897bc660c2beea 2.6-rc
86 23f785b38af38d2fca6b8f3db56b8007a84cd73a 2.6
86 23f785b38af38d2fca6b8f3db56b8007a84cd73a 2.6
87 ddc7a6be20212d18f3e27d9d7e6f079a66d96f21 2.6.1
87 ddc7a6be20212d18f3e27d9d7e6f079a66d96f21 2.6.1
88 cceaf7af4c9e9e6fa2dbfdcfe9856c5da69c4ffd 2.6.2
88 cceaf7af4c9e9e6fa2dbfdcfe9856c5da69c4ffd 2.6.2
89 009794acc6e37a650f0fae37872e733382ac1c0c 2.6.3
89 009794acc6e37a650f0fae37872e733382ac1c0c 2.6.3
90 f0d7721d7322dcfb5af33599c2543f27335334bb 2.7-rc
90 f0d7721d7322dcfb5af33599c2543f27335334bb 2.7-rc
91 f37b5a17e6a0ee17afde2cdde5393dd74715fb58 2.7
91 f37b5a17e6a0ee17afde2cdde5393dd74715fb58 2.7
92 335a558f81dc73afeab4d7be63617392b130117f 2.7.1
92 335a558f81dc73afeab4d7be63617392b130117f 2.7.1
93 e7fa36d2ad3a7944a52dca126458d6f482db3524 2.7.2
93 e7fa36d2ad3a7944a52dca126458d6f482db3524 2.7.2
94 1596f2d8f2421314b1ddead8f7d0c91009358994 2.8-rc
94 1596f2d8f2421314b1ddead8f7d0c91009358994 2.8-rc
95 d825e4025e39d1c39db943cdc89818abd0a87c27 2.8
95 d825e4025e39d1c39db943cdc89818abd0a87c27 2.8
96 209e04a06467e2969c0cc6501335be0406d46ef0 2.8.1
96 209e04a06467e2969c0cc6501335be0406d46ef0 2.8.1
97 ca387377df7a3a67dbb90b6336b781cdadc3ef41 2.8.2
97 ca387377df7a3a67dbb90b6336b781cdadc3ef41 2.8.2
98 8862469e16f9236208581b20de5f96bd13cc039d 2.9-rc
98 8862469e16f9236208581b20de5f96bd13cc039d 2.9-rc
99 3cec5134e9c4bceab6a00c60f52a4f80677a78f2 2.9
99 3cec5134e9c4bceab6a00c60f52a4f80677a78f2 2.9
100 b96cb15ec9e04d8ac5ee08b34fcbbe4200588965 2.9.1
100 b96cb15ec9e04d8ac5ee08b34fcbbe4200588965 2.9.1
101 3f83fc5cfe715d292069ee8417c83804f6c6c1e4 2.9.2
101 3f83fc5cfe715d292069ee8417c83804f6c6c1e4 2.9.2
102 564f55b251224f16508dd1311452db7780dafe2b 3.0-rc
102 564f55b251224f16508dd1311452db7780dafe2b 3.0-rc
103 2195ac506c6ababe86985b932f4948837c0891b5 3.0
103 2195ac506c6ababe86985b932f4948837c0891b5 3.0
104 269c80ee5b3cb3684fa8edc61501b3506d02eb10 3.0.1
104 269c80ee5b3cb3684fa8edc61501b3506d02eb10 3.0.1
105 2d8cd3d0e83c7336c0cb45a9f88638363f993848 3.0.2
105 2d8cd3d0e83c7336c0cb45a9f88638363f993848 3.0.2
106 6c36dc6cd61a0e1b563f1d51e55bdf4dacf12162 3.1-rc
106 6c36dc6cd61a0e1b563f1d51e55bdf4dacf12162 3.1-rc
107 3178e49892020336491cdc6945885c4de26ffa8b 3.1
107 3178e49892020336491cdc6945885c4de26ffa8b 3.1
108 5dc91146f35369949ea56b40172308158b59063a 3.1.1
108 5dc91146f35369949ea56b40172308158b59063a 3.1.1
109 f768c888aaa68d12dd7f509dcc7f01c9584357d0 3.1.2
109 f768c888aaa68d12dd7f509dcc7f01c9584357d0 3.1.2
110 7f8d16af8cae246fa5a48e723d48d58b015aed94 3.2-rc
110 7f8d16af8cae246fa5a48e723d48d58b015aed94 3.2-rc
111 ced632394371a36953ce4d394f86278ae51a2aae 3.2
111 ced632394371a36953ce4d394f86278ae51a2aae 3.2
112 643c58303fb0ec020907af28b9e486be299ba043 3.2.1
112 643c58303fb0ec020907af28b9e486be299ba043 3.2.1
113 902554884335e5ca3661d63be9978eb4aec3f68a 3.2.2
113 902554884335e5ca3661d63be9978eb4aec3f68a 3.2.2
114 6dad422ecc5adb63d9fa649eeb8e05a5f9bc4900 3.2.3
114 6dad422ecc5adb63d9fa649eeb8e05a5f9bc4900 3.2.3
115 1265a3a71d75396f5d4cf6935ae7d9ba5407a547 3.2.4
115 1265a3a71d75396f5d4cf6935ae7d9ba5407a547 3.2.4
116 db8e3f7948b1fdeb9ad12d448fc3525759908b9f 3.3-rc
116 db8e3f7948b1fdeb9ad12d448fc3525759908b9f 3.3-rc
117 fbdd5195528fae4f41feebc1838215c110b25d6a 3.3
117 fbdd5195528fae4f41feebc1838215c110b25d6a 3.3
118 5b4ed033390bf6e2879c8f5c28c84e1ee3b87231 3.3.1
118 5b4ed033390bf6e2879c8f5c28c84e1ee3b87231 3.3.1
119 07a92bbd02e5e3a625e0820389b47786b02b2cea 3.3.2
119 07a92bbd02e5e3a625e0820389b47786b02b2cea 3.3.2
120 2e2e9a0750f91a6fe0ad88e4de34f8efefdcab08 3.3.3
120 2e2e9a0750f91a6fe0ad88e4de34f8efefdcab08 3.3.3
121 e89f909edffad558b56f4affa8239e4832f88de0 3.4-rc
121 e89f909edffad558b56f4affa8239e4832f88de0 3.4-rc
122 8cc6036bca532e06681c5a8fa37efaa812de67b5 3.4
122 8cc6036bca532e06681c5a8fa37efaa812de67b5 3.4
123 ed18f4acf435a2824c6f49fba40f42b9df5da7ad 3.4.1
123 ed18f4acf435a2824c6f49fba40f42b9df5da7ad 3.4.1
124 540cd0ddac49c1125b2e013aa2ff18ecbd4dd954 3.4.2
124 540cd0ddac49c1125b2e013aa2ff18ecbd4dd954 3.4.2
125 96a38d44ba093bd1d1ecfd34119e94056030278b 3.5-rc
125 96a38d44ba093bd1d1ecfd34119e94056030278b 3.5-rc
126 21aa1c313b05b1a85f8ffa1120d51579ddf6bf24 3.5
126 21aa1c313b05b1a85f8ffa1120d51579ddf6bf24 3.5
127 1a45e49a6bed023deb229102a8903234d18054d3 3.5.1
127 1a45e49a6bed023deb229102a8903234d18054d3 3.5.1
128 9a466b9f9792e3ad7ae3fc6c43c3ff2e136b718d 3.5.2
128 9a466b9f9792e3ad7ae3fc6c43c3ff2e136b718d 3.5.2
129 b66e3ca0b90c3095ea28dfd39aa24247bebf5c20 3.6-rc
129 b66e3ca0b90c3095ea28dfd39aa24247bebf5c20 3.6-rc
130 47dd34f2e7272be9e3b2a5a83cd0d20be44293f4 3.6
130 47dd34f2e7272be9e3b2a5a83cd0d20be44293f4 3.6
131 1aa5083cbebbe7575c88f3402ab377539b484897 3.6.1
131 1aa5083cbebbe7575c88f3402ab377539b484897 3.6.1
132 2d437a0f3355834a9485bbbeb30a52a052c98f19 3.6.2
132 2d437a0f3355834a9485bbbeb30a52a052c98f19 3.6.2
133 ea389970c08449440587712117f178d33bab3f1e 3.6.3
133 ea389970c08449440587712117f178d33bab3f1e 3.6.3
134 158bdc8965720ca4061f8f8d806563cfc7cdb62e 3.7-rc
134 158bdc8965720ca4061f8f8d806563cfc7cdb62e 3.7-rc
135 2408645de650d8a29a6ce9e7dce601d8dd0d1474 3.7
135 2408645de650d8a29a6ce9e7dce601d8dd0d1474 3.7
136 b698abf971e7377d9b7ec7fc8c52df45255b0329 3.7.1
136 b698abf971e7377d9b7ec7fc8c52df45255b0329 3.7.1
137 d493d64757eb45ada99fcb3693e479a51b7782da 3.7.2
137 d493d64757eb45ada99fcb3693e479a51b7782da 3.7.2
138 ae279d4a19e9683214cbd1fe8298cf0b50571432 3.7.3
138 ae279d4a19e9683214cbd1fe8298cf0b50571432 3.7.3
139 740156eedf2c450aee58b1a90b0e826f47c5da64 3.8-rc
139 740156eedf2c450aee58b1a90b0e826f47c5da64 3.8-rc
140 f85de28eae32e7d3064b1a1321309071bbaaa069 3.8
140 f85de28eae32e7d3064b1a1321309071bbaaa069 3.8
141 a56296f55a5e1038ea5016dace2076b693c28a56 3.8.1
141 a56296f55a5e1038ea5016dace2076b693c28a56 3.8.1
142 aaabed77791a75968a12b8c43ad263631a23ee81 3.8.2
142 aaabed77791a75968a12b8c43ad263631a23ee81 3.8.2
143 a9764ab80e11bcf6a37255db7dd079011f767c6c 3.8.3
143 a9764ab80e11bcf6a37255db7dd079011f767c6c 3.8.3
144 26a5d605b8683a292bb89aea11f37a81b06ac016 3.8.4
144 26a5d605b8683a292bb89aea11f37a81b06ac016 3.8.4
145 519bb4f9d3a47a6e83c2b414d58811ed38f503c2 3.9-rc
145 519bb4f9d3a47a6e83c2b414d58811ed38f503c2 3.9-rc
146 299546f84e68dbb9bd026f0f3a974ce4bdb93686 3.9
146 299546f84e68dbb9bd026f0f3a974ce4bdb93686 3.9
147 ccd436f7db6d5d7b9af89715179b911d031d44f1 3.9.1
147 ccd436f7db6d5d7b9af89715179b911d031d44f1 3.9.1
148 149433e68974eb5c63ccb03f794d8b57339a80c4 3.9.2
148 149433e68974eb5c63ccb03f794d8b57339a80c4 3.9.2
149 438173c415874f6ac653efc1099dec9c9150e90f 4.0-rc
149 438173c415874f6ac653efc1099dec9c9150e90f 4.0-rc
150 eab27446995210c334c3d06f1a659e3b9b5da769 4.0
150 eab27446995210c334c3d06f1a659e3b9b5da769 4.0
151 b3b1ae98f6a0e14c1e1ba806a6c18e193b6dae5c 4.0.1
151 b3b1ae98f6a0e14c1e1ba806a6c18e193b6dae5c 4.0.1
152 e69874dc1f4e142746ff3df91e678a09c6fc208c 4.0.2
152 e69874dc1f4e142746ff3df91e678a09c6fc208c 4.0.2
153 a1dd2c0c479e0550040542e392e87bc91262517e 4.1-rc
153 a1dd2c0c479e0550040542e392e87bc91262517e 4.1-rc
154 e1526da1e6d84e03146151c9b6e6950fe9a83d7d 4.1
154 e1526da1e6d84e03146151c9b6e6950fe9a83d7d 4.1
155 25703b624d27e3917d978af56d6ad59331e0464a 4.1.1
155 25703b624d27e3917d978af56d6ad59331e0464a 4.1.1
156 ed5b25874d998ababb181a939dd37a16ea644435 4.1.2
156 ed5b25874d998ababb181a939dd37a16ea644435 4.1.2
157 77eaf9539499a1b8be259ffe7ada787d07857f80 4.1.3
157 77eaf9539499a1b8be259ffe7ada787d07857f80 4.1.3
158 616e788321cc4ae9975b7f0c54c849f36d82182b 4.2-rc
158 616e788321cc4ae9975b7f0c54c849f36d82182b 4.2-rc
159 bb96d4a497432722623ae60d9bc734a1e360179e 4.2
159 bb96d4a497432722623ae60d9bc734a1e360179e 4.2
160 c850f0ed54c1d42f9aa079ad528f8127e5775217 4.2.1
160 c850f0ed54c1d42f9aa079ad528f8127e5775217 4.2.1
161 26c49ed51a698ec016d2b4c6b44ca3c3f73cc788 4.2.2
161 26c49ed51a698ec016d2b4c6b44ca3c3f73cc788 4.2.2
162 857876ebaed4e315f63157bd157d6ce553c7ab73 4.3-rc
162 857876ebaed4e315f63157bd157d6ce553c7ab73 4.3-rc
163 5544af8622863796a0027566f6b646e10d522c4c 4.3
163 5544af8622863796a0027566f6b646e10d522c4c 4.3
164 943c91326b23954e6e1c6960d0239511f9530258 4.2.3
164 943c91326b23954e6e1c6960d0239511f9530258 4.2.3
165 3fee7f7d2da04226914c2258cc2884dc27384fd7 4.3.1
165 3fee7f7d2da04226914c2258cc2884dc27384fd7 4.3.1
166 920977f72c7b70acfdaf56ab35360584d7845827 4.3.2
166 920977f72c7b70acfdaf56ab35360584d7845827 4.3.2
167 2f427b57bf9019c6dc3750baa539dc22c1be50f6 4.3.3
167 2f427b57bf9019c6dc3750baa539dc22c1be50f6 4.3.3
168 1e2454b60e5936f5e77498cab2648db469504487 4.4-rc
168 1e2454b60e5936f5e77498cab2648db469504487 4.4-rc
169 0ccb43d4cf01d013ae05917ec4f305509f851b2d 4.4
169 0ccb43d4cf01d013ae05917ec4f305509f851b2d 4.4
170 cabc840ffdee8a72f3689fb77dd74d04fdc2bc04 4.4.1
170 cabc840ffdee8a72f3689fb77dd74d04fdc2bc04 4.4.1
171 a92b9f8e11ba330614cdfd6af0e03b15c1ff3797 4.4.2
171 a92b9f8e11ba330614cdfd6af0e03b15c1ff3797 4.4.2
172 27b6df1b5adbdf647cf5c6675b40575e1b197c60 4.5-rc
172 27b6df1b5adbdf647cf5c6675b40575e1b197c60 4.5-rc
173 d334afc585e29577f271c5eda03378736a16ca6b 4.5
173 d334afc585e29577f271c5eda03378736a16ca6b 4.5
174 369aadf7a3264b03c8b09efce715bc41e6ab4a9b 4.5.1
174 369aadf7a3264b03c8b09efce715bc41e6ab4a9b 4.5.1
175 8bba684efde7f45add05f737952093bb2aa07155 4.5.2
175 8bba684efde7f45add05f737952093bb2aa07155 4.5.2
176 7de7bd407251af2bc98e5b809c8598ee95830daf 4.5.3
176 7de7bd407251af2bc98e5b809c8598ee95830daf 4.5.3
177 ed5448edcbfa747b9154099e18630e49024fd47b 4.6rc0
177 ed5448edcbfa747b9154099e18630e49024fd47b 4.6rc0
178 1ec874717d8a93b19e0d50628443e0ee5efab3a9 4.6rc1
178 1ec874717d8a93b19e0d50628443e0ee5efab3a9 4.6rc1
179 6614cac550aea66d19c601e45efd1b7bd08d7c40 4.6
179 6614cac550aea66d19c601e45efd1b7bd08d7c40 4.6
180 9c5ced5276d6e7d54f7c3dadf5247b7ee98ec79c 4.6.1
180 9c5ced5276d6e7d54f7c3dadf5247b7ee98ec79c 4.6.1
181 0b63a6743010dfdbf8a8154186e119949bdaa1cc 4.6.2
181 0b63a6743010dfdbf8a8154186e119949bdaa1cc 4.6.2
182 e90130af47ce8dd53a3109aed9d15876b3e7dee8 4.7rc0
182 e90130af47ce8dd53a3109aed9d15876b3e7dee8 4.7rc0
183 33ac6a72308a215e6086fbced347ec10aa963b0a 4.7
183 33ac6a72308a215e6086fbced347ec10aa963b0a 4.7
184 ede3bf31fe63677fdf5bd8db687977d4e3d792ed 4.7.1
184 ede3bf31fe63677fdf5bd8db687977d4e3d792ed 4.7.1
185 5405cb1a79010ac50c58cd84e6f50c4556bf2a4c 4.7.2
185 5405cb1a79010ac50c58cd84e6f50c4556bf2a4c 4.7.2
186 956ec6f1320df26f3133ec40f3de866ea0695fd7 4.8rc0
186 956ec6f1320df26f3133ec40f3de866ea0695fd7 4.8rc0
187 a91a2837150bdcb27ae76b3646e6c93cd6a15904 4.8
187 a91a2837150bdcb27ae76b3646e6c93cd6a15904 4.8
188 1c8c54cf97256f4468da2eb4dbee24f7f3888e71 4.8.1
188 1c8c54cf97256f4468da2eb4dbee24f7f3888e71 4.8.1
189 197f092b2cd9691e2a55d198f717b231af9be6f9 4.8.2
189 197f092b2cd9691e2a55d198f717b231af9be6f9 4.8.2
190 593718ff5844cad7a27ee3eb5adad89ac8550949 4.9rc0
190 593718ff5844cad7a27ee3eb5adad89ac8550949 4.9rc0
191 83377b4b4ae0e9a6b8e579f7b0a693b8cf5c3b10 4.9
191 83377b4b4ae0e9a6b8e579f7b0a693b8cf5c3b10 4.9
192 4ea21df312ec7159c5b3633096b6ecf68750b0dd 4.9.1
192 4ea21df312ec7159c5b3633096b6ecf68750b0dd 4.9.1
193 4a8d9ed864754837a185a642170cde24392f9abf 5.0rc0
193 4a8d9ed864754837a185a642170cde24392f9abf 5.0rc0
194 07e479ef7c9639be0029f00e6a722b96dcc05fee 5.0
194 07e479ef7c9639be0029f00e6a722b96dcc05fee 5.0
195 c3484ddbdb9621256d597ed86b90d229c59c2af9 5.0.1
195 c3484ddbdb9621256d597ed86b90d229c59c2af9 5.0.1
196 97ada9b8d51bef24c5cb4cdca4243f0db694ab6e 5.0.2
196 97ada9b8d51bef24c5cb4cdca4243f0db694ab6e 5.0.2
197 e386b5f4f8360dbb43a576dd9b1368e386fefa5b 5.1rc0
197 e386b5f4f8360dbb43a576dd9b1368e386fefa5b 5.1rc0
198 e91930d712e8507d1bc1b2dffd96c83edc4cbed3 5.1
198 e91930d712e8507d1bc1b2dffd96c83edc4cbed3 5.1
199 a4e32fd539ab41489a51b2aa88bda9a73b839562 5.1.1
199 a4e32fd539ab41489a51b2aa88bda9a73b839562 5.1.1
200 181e52f2b62f4768aa0d988936c929dc7c4a41a0 5.1.2
200 181e52f2b62f4768aa0d988936c929dc7c4a41a0 5.1.2
201 59338f9561099de77c684c00f76507f11e46ebe8 5.2rc0
201 59338f9561099de77c684c00f76507f11e46ebe8 5.2rc0
202 ca3dca416f8d5863ca6f5a4a6a6bb835dcd5feeb 5.2
202 ca3dca416f8d5863ca6f5a4a6a6bb835dcd5feeb 5.2
203 a50fecefa691c9b72a99e49aa6fe9dd13943c2bf 5.2.1
203 a50fecefa691c9b72a99e49aa6fe9dd13943c2bf 5.2.1
204 b4c82b70418022e67cc0e69b1aa3c3aa43aa1d29 5.2.2
204 b4c82b70418022e67cc0e69b1aa3c3aa43aa1d29 5.2.2
205 84a0102c05c7852c8215ef6cf21d809927586b69 5.3rc0
205 84a0102c05c7852c8215ef6cf21d809927586b69 5.3rc0
206 e4344e463c0c888a2f437b78b5982ecdf3f6650a 5.3rc1
206 e4344e463c0c888a2f437b78b5982ecdf3f6650a 5.3rc1
207 7f5410dfc8a64bb587d19637deb95d378fd1eb5c 5.3
207 7f5410dfc8a64bb587d19637deb95d378fd1eb5c 5.3
208 6d121acbb82e65fe4dd3c2318a1b61981b958492 5.3.1
208 6d121acbb82e65fe4dd3c2318a1b61981b958492 5.3.1
209 8fca7e8449a847e3cf1054f2c07b51237699fad3 5.3.2
209 8fca7e8449a847e3cf1054f2c07b51237699fad3 5.3.2
210 26ce8e7515036d3431a03aaeb7bc72dd96cb1112 5.4rc0
@@ -1,1266 +1,1278 b''
1 # aws.py - Automation code for Amazon Web Services
1 # aws.py - Automation code for Amazon Web Services
2 #
2 #
3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 # no-check-code because Python 3 native.
8 # no-check-code because Python 3 native.
9
9
10 import contextlib
10 import contextlib
11 import copy
11 import copy
12 import hashlib
12 import hashlib
13 import json
13 import json
14 import os
14 import os
15 import pathlib
15 import pathlib
16 import subprocess
16 import subprocess
17 import time
17 import time
18
18
19 import boto3
19 import boto3
20 import botocore.exceptions
20 import botocore.exceptions
21
21
22 from .linux import BOOTSTRAP_DEBIAN
22 from .linux import BOOTSTRAP_DEBIAN
23 from .ssh import (
23 from .ssh import (
24 exec_command as ssh_exec_command,
24 exec_command as ssh_exec_command,
25 wait_for_ssh,
25 wait_for_ssh,
26 )
26 )
27 from .winrm import (
27 from .winrm import (
28 run_powershell,
28 run_powershell,
29 wait_for_winrm,
29 wait_for_winrm,
30 )
30 )
31
31
32
32
33 SOURCE_ROOT = pathlib.Path(
33 SOURCE_ROOT = pathlib.Path(
34 os.path.abspath(__file__)
34 os.path.abspath(__file__)
35 ).parent.parent.parent.parent
35 ).parent.parent.parent.parent
36
36
37 INSTALL_WINDOWS_DEPENDENCIES = (
37 INSTALL_WINDOWS_DEPENDENCIES = (
38 SOURCE_ROOT / 'contrib' / 'install-windows-dependencies.ps1'
38 SOURCE_ROOT / 'contrib' / 'install-windows-dependencies.ps1'
39 )
39 )
40
40
41
41
42 INSTANCE_TYPES_WITH_STORAGE = {
42 INSTANCE_TYPES_WITH_STORAGE = {
43 'c5d',
43 'c5d',
44 'd2',
44 'd2',
45 'h1',
45 'h1',
46 'i3',
46 'i3',
47 'm5ad',
47 'm5ad',
48 'm5d',
48 'm5d',
49 'r5d',
49 'r5d',
50 'r5ad',
50 'r5ad',
51 'x1',
51 'x1',
52 'z1d',
52 'z1d',
53 }
53 }
54
54
55
55
56 AMAZON_ACCOUNT_ID = '801119661308'
56 AMAZON_ACCOUNT_ID = '801119661308'
57 DEBIAN_ACCOUNT_ID = '379101102735'
57 DEBIAN_ACCOUNT_ID = '379101102735'
58 DEBIAN_ACCOUNT_ID_2 = '136693071363'
58 DEBIAN_ACCOUNT_ID_2 = '136693071363'
59 UBUNTU_ACCOUNT_ID = '099720109477'
59 UBUNTU_ACCOUNT_ID = '099720109477'
60
60
61
61
62 WINDOWS_BASE_IMAGE_NAME = 'Windows_Server-2019-English-Full-Base-2019.11.13'
62 WINDOWS_BASE_IMAGE_NAME = 'Windows_Server-2019-English-Full-Base-*'
63
63
64
64
65 KEY_PAIRS = {
65 KEY_PAIRS = {
66 'automation',
66 'automation',
67 }
67 }
68
68
69
69
70 SECURITY_GROUPS = {
70 SECURITY_GROUPS = {
71 'linux-dev-1': {
71 'linux-dev-1': {
72 'description': 'Mercurial Linux instances that perform build/test automation',
72 'description': 'Mercurial Linux instances that perform build/test automation',
73 'ingress': [
73 'ingress': [
74 {
74 {
75 'FromPort': 22,
75 'FromPort': 22,
76 'ToPort': 22,
76 'ToPort': 22,
77 'IpProtocol': 'tcp',
77 'IpProtocol': 'tcp',
78 'IpRanges': [
78 'IpRanges': [
79 {
79 {
80 'CidrIp': '0.0.0.0/0',
80 'CidrIp': '0.0.0.0/0',
81 'Description': 'SSH from entire Internet',
81 'Description': 'SSH from entire Internet',
82 },
82 },
83 ],
83 ],
84 },
84 },
85 ],
85 ],
86 },
86 },
87 'windows-dev-1': {
87 'windows-dev-1': {
88 'description': 'Mercurial Windows instances that perform build automation',
88 'description': 'Mercurial Windows instances that perform build automation',
89 'ingress': [
89 'ingress': [
90 {
90 {
91 'FromPort': 22,
91 'FromPort': 22,
92 'ToPort': 22,
92 'ToPort': 22,
93 'IpProtocol': 'tcp',
93 'IpProtocol': 'tcp',
94 'IpRanges': [
94 'IpRanges': [
95 {
95 {
96 'CidrIp': '0.0.0.0/0',
96 'CidrIp': '0.0.0.0/0',
97 'Description': 'SSH from entire Internet',
97 'Description': 'SSH from entire Internet',
98 },
98 },
99 ],
99 ],
100 },
100 },
101 {
101 {
102 'FromPort': 3389,
102 'FromPort': 3389,
103 'ToPort': 3389,
103 'ToPort': 3389,
104 'IpProtocol': 'tcp',
104 'IpProtocol': 'tcp',
105 'IpRanges': [
105 'IpRanges': [
106 {
106 {
107 'CidrIp': '0.0.0.0/0',
107 'CidrIp': '0.0.0.0/0',
108 'Description': 'RDP from entire Internet',
108 'Description': 'RDP from entire Internet',
109 },
109 },
110 ],
110 ],
111 },
111 },
112 {
112 {
113 'FromPort': 5985,
113 'FromPort': 5985,
114 'ToPort': 5986,
114 'ToPort': 5986,
115 'IpProtocol': 'tcp',
115 'IpProtocol': 'tcp',
116 'IpRanges': [
116 'IpRanges': [
117 {
117 {
118 'CidrIp': '0.0.0.0/0',
118 'CidrIp': '0.0.0.0/0',
119 'Description': 'PowerShell Remoting (Windows Remote Management)',
119 'Description': 'PowerShell Remoting (Windows Remote Management)',
120 },
120 },
121 ],
121 ],
122 },
122 },
123 ],
123 ],
124 },
124 },
125 }
125 }
126
126
127
127
128 IAM_ROLES = {
128 IAM_ROLES = {
129 'ephemeral-ec2-role-1': {
129 'ephemeral-ec2-role-1': {
130 'description': 'Mercurial temporary EC2 instances',
130 'description': 'Mercurial temporary EC2 instances',
131 'policy_arns': [
131 'policy_arns': [
132 'arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM',
132 'arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM',
133 ],
133 ],
134 },
134 },
135 }
135 }
136
136
137
137
138 ASSUME_ROLE_POLICY_DOCUMENT = '''
138 ASSUME_ROLE_POLICY_DOCUMENT = '''
139 {
139 {
140 "Version": "2012-10-17",
140 "Version": "2012-10-17",
141 "Statement": [
141 "Statement": [
142 {
142 {
143 "Effect": "Allow",
143 "Effect": "Allow",
144 "Principal": {
144 "Principal": {
145 "Service": "ec2.amazonaws.com"
145 "Service": "ec2.amazonaws.com"
146 },
146 },
147 "Action": "sts:AssumeRole"
147 "Action": "sts:AssumeRole"
148 }
148 }
149 ]
149 ]
150 }
150 }
151 '''.strip()
151 '''.strip()
152
152
153
153
154 IAM_INSTANCE_PROFILES = {
154 IAM_INSTANCE_PROFILES = {
155 'ephemeral-ec2-1': {'roles': ['ephemeral-ec2-role-1',],}
155 'ephemeral-ec2-1': {'roles': ['ephemeral-ec2-role-1',],}
156 }
156 }
157
157
158
158
159 # User Data for Windows EC2 instance. Mainly used to set the password
159 # User Data for Windows EC2 instance. Mainly used to set the password
160 # and configure WinRM.
160 # and configure WinRM.
161 # Inspired by the User Data script used by Packer
161 # Inspired by the User Data script used by Packer
162 # (from https://www.packer.io/intro/getting-started/build-image.html).
162 # (from https://www.packer.io/intro/getting-started/build-image.html).
163 WINDOWS_USER_DATA = r'''
163 WINDOWS_USER_DATA = r'''
164 <powershell>
164 <powershell>
165
165
166 # TODO enable this once we figure out what is failing.
166 # TODO enable this once we figure out what is failing.
167 #$ErrorActionPreference = "stop"
167 #$ErrorActionPreference = "stop"
168
168
169 # Set administrator password
169 # Set administrator password
170 net user Administrator "%s"
170 net user Administrator "%s"
171 wmic useraccount where "name='Administrator'" set PasswordExpires=FALSE
171 wmic useraccount where "name='Administrator'" set PasswordExpires=FALSE
172
172
173 # First, make sure WinRM can't be connected to
173 # First, make sure WinRM can't be connected to
174 netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new enable=yes action=block
174 netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new enable=yes action=block
175
175
176 # Delete any existing WinRM listeners
176 # Delete any existing WinRM listeners
177 winrm delete winrm/config/listener?Address=*+Transport=HTTP 2>$Null
177 winrm delete winrm/config/listener?Address=*+Transport=HTTP 2>$Null
178 winrm delete winrm/config/listener?Address=*+Transport=HTTPS 2>$Null
178 winrm delete winrm/config/listener?Address=*+Transport=HTTPS 2>$Null
179
179
180 # Create a new WinRM listener and configure
180 # Create a new WinRM listener and configure
181 winrm create winrm/config/listener?Address=*+Transport=HTTP
181 winrm create winrm/config/listener?Address=*+Transport=HTTP
182 winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="0"}'
182 winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="0"}'
183 winrm set winrm/config '@{MaxTimeoutms="7200000"}'
183 winrm set winrm/config '@{MaxTimeoutms="7200000"}'
184 winrm set winrm/config/service '@{AllowUnencrypted="true"}'
184 winrm set winrm/config/service '@{AllowUnencrypted="true"}'
185 winrm set winrm/config/service '@{MaxConcurrentOperationsPerUser="12000"}'
185 winrm set winrm/config/service '@{MaxConcurrentOperationsPerUser="12000"}'
186 winrm set winrm/config/service/auth '@{Basic="true"}'
186 winrm set winrm/config/service/auth '@{Basic="true"}'
187 winrm set winrm/config/client/auth '@{Basic="true"}'
187 winrm set winrm/config/client/auth '@{Basic="true"}'
188
188
189 # Configure UAC to allow privilege elevation in remote shells
189 # Configure UAC to allow privilege elevation in remote shells
190 $Key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
190 $Key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
191 $Setting = 'LocalAccountTokenFilterPolicy'
191 $Setting = 'LocalAccountTokenFilterPolicy'
192 Set-ItemProperty -Path $Key -Name $Setting -Value 1 -Force
192 Set-ItemProperty -Path $Key -Name $Setting -Value 1 -Force
193
193
194 # Avoid long usernames in the temp directory path because the '~' causes extra quoting in ssh output
194 # Avoid long usernames in the temp directory path because the '~' causes extra quoting in ssh output
195 [System.Environment]::SetEnvironmentVariable('TMP', 'C:\Temp', [System.EnvironmentVariableTarget]::User)
195 [System.Environment]::SetEnvironmentVariable('TMP', 'C:\Temp', [System.EnvironmentVariableTarget]::User)
196 [System.Environment]::SetEnvironmentVariable('TEMP', 'C:\Temp', [System.EnvironmentVariableTarget]::User)
196 [System.Environment]::SetEnvironmentVariable('TEMP', 'C:\Temp', [System.EnvironmentVariableTarget]::User)
197
197
198 # Configure and restart the WinRM Service; Enable the required firewall exception
198 # Configure and restart the WinRM Service; Enable the required firewall exception
199 Stop-Service -Name WinRM
199 Stop-Service -Name WinRM
200 Set-Service -Name WinRM -StartupType Automatic
200 Set-Service -Name WinRM -StartupType Automatic
201 netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new action=allow localip=any remoteip=any
201 netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new action=allow localip=any remoteip=any
202 Start-Service -Name WinRM
202 Start-Service -Name WinRM
203
203
204 # Disable firewall on private network interfaces so prompts don't appear.
204 # Disable firewall on private network interfaces so prompts don't appear.
205 Set-NetFirewallProfile -Name private -Enabled false
205 Set-NetFirewallProfile -Name private -Enabled false
206 </powershell>
206 </powershell>
207 '''.lstrip()
207 '''.lstrip()
208
208
209
209
210 WINDOWS_BOOTSTRAP_POWERSHELL = '''
210 WINDOWS_BOOTSTRAP_POWERSHELL = '''
211 Write-Output "installing PowerShell dependencies"
211 Write-Output "installing PowerShell dependencies"
212 Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
212 Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
213 Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
213 Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
214 Install-Module -Name OpenSSHUtils -RequiredVersion 0.0.2.0
214 Install-Module -Name OpenSSHUtils -RequiredVersion 0.0.2.0
215
215
216 Write-Output "installing OpenSSL server"
216 Write-Output "installing OpenSSL server"
217 Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
217 Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
218 # Various tools will attempt to use older versions of .NET. So we enable
218 # Various tools will attempt to use older versions of .NET. So we enable
219 # the feature that provides them so it doesn't have to be auto-enabled
219 # the feature that provides them so it doesn't have to be auto-enabled
220 # later.
220 # later.
221 Write-Output "enabling .NET Framework feature"
221 Write-Output "enabling .NET Framework feature"
222 Install-WindowsFeature -Name Net-Framework-Core
222 Install-WindowsFeature -Name Net-Framework-Core
223 '''
223 '''
224
224
225
225
226 class AWSConnection:
226 class AWSConnection:
227 """Manages the state of a connection with AWS."""
227 """Manages the state of a connection with AWS."""
228
228
229 def __init__(self, automation, region: str, ensure_ec2_state: bool = True):
229 def __init__(self, automation, region: str, ensure_ec2_state: bool = True):
230 self.automation = automation
230 self.automation = automation
231 self.local_state_path = automation.state_path
231 self.local_state_path = automation.state_path
232
232
233 self.prefix = 'hg-'
233 self.prefix = 'hg-'
234
234
235 self.session = boto3.session.Session(region_name=region)
235 self.session = boto3.session.Session(region_name=region)
236 self.ec2client = self.session.client('ec2')
236 self.ec2client = self.session.client('ec2')
237 self.ec2resource = self.session.resource('ec2')
237 self.ec2resource = self.session.resource('ec2')
238 self.iamclient = self.session.client('iam')
238 self.iamclient = self.session.client('iam')
239 self.iamresource = self.session.resource('iam')
239 self.iamresource = self.session.resource('iam')
240 self.security_groups = {}
240 self.security_groups = {}
241
241
242 if ensure_ec2_state:
242 if ensure_ec2_state:
243 ensure_key_pairs(automation.state_path, self.ec2resource)
243 ensure_key_pairs(automation.state_path, self.ec2resource)
244 self.security_groups = ensure_security_groups(self.ec2resource)
244 self.security_groups = ensure_security_groups(self.ec2resource)
245 ensure_iam_state(self.iamclient, self.iamresource)
245 ensure_iam_state(self.iamclient, self.iamresource)
246
246
247 def key_pair_path_private(self, name):
247 def key_pair_path_private(self, name):
248 """Path to a key pair private key file."""
248 """Path to a key pair private key file."""
249 return self.local_state_path / 'keys' / ('keypair-%s' % name)
249 return self.local_state_path / 'keys' / ('keypair-%s' % name)
250
250
251 def key_pair_path_public(self, name):
251 def key_pair_path_public(self, name):
252 return self.local_state_path / 'keys' / ('keypair-%s.pub' % name)
252 return self.local_state_path / 'keys' / ('keypair-%s.pub' % name)
253
253
254
254
255 def rsa_key_fingerprint(p: pathlib.Path):
255 def rsa_key_fingerprint(p: pathlib.Path):
256 """Compute the fingerprint of an RSA private key."""
256 """Compute the fingerprint of an RSA private key."""
257
257
258 # TODO use rsa package.
258 # TODO use rsa package.
259 res = subprocess.run(
259 res = subprocess.run(
260 [
260 [
261 'openssl',
261 'openssl',
262 'pkcs8',
262 'pkcs8',
263 '-in',
263 '-in',
264 str(p),
264 str(p),
265 '-nocrypt',
265 '-nocrypt',
266 '-topk8',
266 '-topk8',
267 '-outform',
267 '-outform',
268 'DER',
268 'DER',
269 ],
269 ],
270 capture_output=True,
270 capture_output=True,
271 check=True,
271 check=True,
272 )
272 )
273
273
274 sha1 = hashlib.sha1(res.stdout).hexdigest()
274 sha1 = hashlib.sha1(res.stdout).hexdigest()
275 return ':'.join(a + b for a, b in zip(sha1[::2], sha1[1::2]))
275 return ':'.join(a + b for a, b in zip(sha1[::2], sha1[1::2]))
276
276
277
277
278 def ensure_key_pairs(state_path: pathlib.Path, ec2resource, prefix='hg-'):
278 def ensure_key_pairs(state_path: pathlib.Path, ec2resource, prefix='hg-'):
279 remote_existing = {}
279 remote_existing = {}
280
280
281 for kpi in ec2resource.key_pairs.all():
281 for kpi in ec2resource.key_pairs.all():
282 if kpi.name.startswith(prefix):
282 if kpi.name.startswith(prefix):
283 remote_existing[kpi.name[len(prefix) :]] = kpi.key_fingerprint
283 remote_existing[kpi.name[len(prefix) :]] = kpi.key_fingerprint
284
284
285 # Validate that we have these keys locally.
285 # Validate that we have these keys locally.
286 key_path = state_path / 'keys'
286 key_path = state_path / 'keys'
287 key_path.mkdir(exist_ok=True, mode=0o700)
287 key_path.mkdir(exist_ok=True, mode=0o700)
288
288
289 def remove_remote(name):
289 def remove_remote(name):
290 print('deleting key pair %s' % name)
290 print('deleting key pair %s' % name)
291 key = ec2resource.KeyPair(name)
291 key = ec2resource.KeyPair(name)
292 key.delete()
292 key.delete()
293
293
294 def remove_local(name):
294 def remove_local(name):
295 pub_full = key_path / ('keypair-%s.pub' % name)
295 pub_full = key_path / ('keypair-%s.pub' % name)
296 priv_full = key_path / ('keypair-%s' % name)
296 priv_full = key_path / ('keypair-%s' % name)
297
297
298 print('removing %s' % pub_full)
298 print('removing %s' % pub_full)
299 pub_full.unlink()
299 pub_full.unlink()
300 print('removing %s' % priv_full)
300 print('removing %s' % priv_full)
301 priv_full.unlink()
301 priv_full.unlink()
302
302
303 local_existing = {}
303 local_existing = {}
304
304
305 for f in sorted(os.listdir(key_path)):
305 for f in sorted(os.listdir(key_path)):
306 if not f.startswith('keypair-') or not f.endswith('.pub'):
306 if not f.startswith('keypair-') or not f.endswith('.pub'):
307 continue
307 continue
308
308
309 name = f[len('keypair-') : -len('.pub')]
309 name = f[len('keypair-') : -len('.pub')]
310
310
311 pub_full = key_path / f
311 pub_full = key_path / f
312 priv_full = key_path / ('keypair-%s' % name)
312 priv_full = key_path / ('keypair-%s' % name)
313
313
314 with open(pub_full, 'r', encoding='ascii') as fh:
314 with open(pub_full, 'r', encoding='ascii') as fh:
315 data = fh.read()
315 data = fh.read()
316
316
317 if not data.startswith('ssh-rsa '):
317 if not data.startswith('ssh-rsa '):
318 print(
318 print(
319 'unexpected format for key pair file: %s; removing' % pub_full
319 'unexpected format for key pair file: %s; removing' % pub_full
320 )
320 )
321 pub_full.unlink()
321 pub_full.unlink()
322 priv_full.unlink()
322 priv_full.unlink()
323 continue
323 continue
324
324
325 local_existing[name] = rsa_key_fingerprint(priv_full)
325 local_existing[name] = rsa_key_fingerprint(priv_full)
326
326
327 for name in sorted(set(remote_existing) | set(local_existing)):
327 for name in sorted(set(remote_existing) | set(local_existing)):
328 if name not in local_existing:
328 if name not in local_existing:
329 actual = '%s%s' % (prefix, name)
329 actual = '%s%s' % (prefix, name)
330 print('remote key %s does not exist locally' % name)
330 print('remote key %s does not exist locally' % name)
331 remove_remote(actual)
331 remove_remote(actual)
332 del remote_existing[name]
332 del remote_existing[name]
333
333
334 elif name not in remote_existing:
334 elif name not in remote_existing:
335 print('local key %s does not exist remotely' % name)
335 print('local key %s does not exist remotely' % name)
336 remove_local(name)
336 remove_local(name)
337 del local_existing[name]
337 del local_existing[name]
338
338
339 elif remote_existing[name] != local_existing[name]:
339 elif remote_existing[name] != local_existing[name]:
340 print(
340 print(
341 'key fingerprint mismatch for %s; '
341 'key fingerprint mismatch for %s; '
342 'removing from local and remote' % name
342 'removing from local and remote' % name
343 )
343 )
344 remove_local(name)
344 remove_local(name)
345 remove_remote('%s%s' % (prefix, name))
345 remove_remote('%s%s' % (prefix, name))
346 del local_existing[name]
346 del local_existing[name]
347 del remote_existing[name]
347 del remote_existing[name]
348
348
349 missing = KEY_PAIRS - set(remote_existing)
349 missing = KEY_PAIRS - set(remote_existing)
350
350
351 for name in sorted(missing):
351 for name in sorted(missing):
352 actual = '%s%s' % (prefix, name)
352 actual = '%s%s' % (prefix, name)
353 print('creating key pair %s' % actual)
353 print('creating key pair %s' % actual)
354
354
355 priv_full = key_path / ('keypair-%s' % name)
355 priv_full = key_path / ('keypair-%s' % name)
356 pub_full = key_path / ('keypair-%s.pub' % name)
356 pub_full = key_path / ('keypair-%s.pub' % name)
357
357
358 kp = ec2resource.create_key_pair(KeyName=actual)
358 kp = ec2resource.create_key_pair(KeyName=actual)
359
359
360 with priv_full.open('w', encoding='ascii') as fh:
360 with priv_full.open('w', encoding='ascii') as fh:
361 fh.write(kp.key_material)
361 fh.write(kp.key_material)
362 fh.write('\n')
362 fh.write('\n')
363
363
364 priv_full.chmod(0o0600)
364 priv_full.chmod(0o0600)
365
365
366 # SSH public key can be extracted via `ssh-keygen`.
366 # SSH public key can be extracted via `ssh-keygen`.
367 with pub_full.open('w', encoding='ascii') as fh:
367 with pub_full.open('w', encoding='ascii') as fh:
368 subprocess.run(
368 subprocess.run(
369 ['ssh-keygen', '-y', '-f', str(priv_full)],
369 ['ssh-keygen', '-y', '-f', str(priv_full)],
370 stdout=fh,
370 stdout=fh,
371 check=True,
371 check=True,
372 )
372 )
373
373
374 pub_full.chmod(0o0600)
374 pub_full.chmod(0o0600)
375
375
376
376
377 def delete_instance_profile(profile):
377 def delete_instance_profile(profile):
378 for role in profile.roles:
378 for role in profile.roles:
379 print(
379 print(
380 'removing role %s from instance profile %s'
380 'removing role %s from instance profile %s'
381 % (role.name, profile.name)
381 % (role.name, profile.name)
382 )
382 )
383 profile.remove_role(RoleName=role.name)
383 profile.remove_role(RoleName=role.name)
384
384
385 print('deleting instance profile %s' % profile.name)
385 print('deleting instance profile %s' % profile.name)
386 profile.delete()
386 profile.delete()
387
387
388
388
389 def ensure_iam_state(iamclient, iamresource, prefix='hg-'):
389 def ensure_iam_state(iamclient, iamresource, prefix='hg-'):
390 """Ensure IAM state is in sync with our canonical definition."""
390 """Ensure IAM state is in sync with our canonical definition."""
391
391
392 remote_profiles = {}
392 remote_profiles = {}
393
393
394 for profile in iamresource.instance_profiles.all():
394 for profile in iamresource.instance_profiles.all():
395 if profile.name.startswith(prefix):
395 if profile.name.startswith(prefix):
396 remote_profiles[profile.name[len(prefix) :]] = profile
396 remote_profiles[profile.name[len(prefix) :]] = profile
397
397
398 for name in sorted(set(remote_profiles) - set(IAM_INSTANCE_PROFILES)):
398 for name in sorted(set(remote_profiles) - set(IAM_INSTANCE_PROFILES)):
399 delete_instance_profile(remote_profiles[name])
399 delete_instance_profile(remote_profiles[name])
400 del remote_profiles[name]
400 del remote_profiles[name]
401
401
402 remote_roles = {}
402 remote_roles = {}
403
403
404 for role in iamresource.roles.all():
404 for role in iamresource.roles.all():
405 if role.name.startswith(prefix):
405 if role.name.startswith(prefix):
406 remote_roles[role.name[len(prefix) :]] = role
406 remote_roles[role.name[len(prefix) :]] = role
407
407
408 for name in sorted(set(remote_roles) - set(IAM_ROLES)):
408 for name in sorted(set(remote_roles) - set(IAM_ROLES)):
409 role = remote_roles[name]
409 role = remote_roles[name]
410
410
411 print('removing role %s' % role.name)
411 print('removing role %s' % role.name)
412 role.delete()
412 role.delete()
413 del remote_roles[name]
413 del remote_roles[name]
414
414
415 # We've purged remote state that doesn't belong. Create missing
415 # We've purged remote state that doesn't belong. Create missing
416 # instance profiles and roles.
416 # instance profiles and roles.
417 for name in sorted(set(IAM_INSTANCE_PROFILES) - set(remote_profiles)):
417 for name in sorted(set(IAM_INSTANCE_PROFILES) - set(remote_profiles)):
418 actual = '%s%s' % (prefix, name)
418 actual = '%s%s' % (prefix, name)
419 print('creating IAM instance profile %s' % actual)
419 print('creating IAM instance profile %s' % actual)
420
420
421 profile = iamresource.create_instance_profile(
421 profile = iamresource.create_instance_profile(
422 InstanceProfileName=actual
422 InstanceProfileName=actual
423 )
423 )
424 remote_profiles[name] = profile
424 remote_profiles[name] = profile
425
425
426 waiter = iamclient.get_waiter('instance_profile_exists')
426 waiter = iamclient.get_waiter('instance_profile_exists')
427 waiter.wait(InstanceProfileName=actual)
427 waiter.wait(InstanceProfileName=actual)
428 print('IAM instance profile %s is available' % actual)
428 print('IAM instance profile %s is available' % actual)
429
429
430 for name in sorted(set(IAM_ROLES) - set(remote_roles)):
430 for name in sorted(set(IAM_ROLES) - set(remote_roles)):
431 entry = IAM_ROLES[name]
431 entry = IAM_ROLES[name]
432
432
433 actual = '%s%s' % (prefix, name)
433 actual = '%s%s' % (prefix, name)
434 print('creating IAM role %s' % actual)
434 print('creating IAM role %s' % actual)
435
435
436 role = iamresource.create_role(
436 role = iamresource.create_role(
437 RoleName=actual,
437 RoleName=actual,
438 Description=entry['description'],
438 Description=entry['description'],
439 AssumeRolePolicyDocument=ASSUME_ROLE_POLICY_DOCUMENT,
439 AssumeRolePolicyDocument=ASSUME_ROLE_POLICY_DOCUMENT,
440 )
440 )
441
441
442 waiter = iamclient.get_waiter('role_exists')
442 waiter = iamclient.get_waiter('role_exists')
443 waiter.wait(RoleName=actual)
443 waiter.wait(RoleName=actual)
444 print('IAM role %s is available' % actual)
444 print('IAM role %s is available' % actual)
445
445
446 remote_roles[name] = role
446 remote_roles[name] = role
447
447
448 for arn in entry['policy_arns']:
448 for arn in entry['policy_arns']:
449 print('attaching policy %s to %s' % (arn, role.name))
449 print('attaching policy %s to %s' % (arn, role.name))
450 role.attach_policy(PolicyArn=arn)
450 role.attach_policy(PolicyArn=arn)
451
451
452 # Now reconcile state of profiles.
452 # Now reconcile state of profiles.
453 for name, meta in sorted(IAM_INSTANCE_PROFILES.items()):
453 for name, meta in sorted(IAM_INSTANCE_PROFILES.items()):
454 profile = remote_profiles[name]
454 profile = remote_profiles[name]
455 wanted = {'%s%s' % (prefix, role) for role in meta['roles']}
455 wanted = {'%s%s' % (prefix, role) for role in meta['roles']}
456 have = {role.name for role in profile.roles}
456 have = {role.name for role in profile.roles}
457
457
458 for role in sorted(have - wanted):
458 for role in sorted(have - wanted):
459 print('removing role %s from %s' % (role, profile.name))
459 print('removing role %s from %s' % (role, profile.name))
460 profile.remove_role(RoleName=role)
460 profile.remove_role(RoleName=role)
461
461
462 for role in sorted(wanted - have):
462 for role in sorted(wanted - have):
463 print('adding role %s to %s' % (role, profile.name))
463 print('adding role %s to %s' % (role, profile.name))
464 profile.add_role(RoleName=role)
464 profile.add_role(RoleName=role)
465
465
466
466
467 def find_image(ec2resource, owner_id, name):
467 def find_image(ec2resource, owner_id, name, reverse_sort_field=None):
468 """Find an AMI by its owner ID and name."""
468 """Find an AMI by its owner ID and name."""
469
469
470 images = ec2resource.images.filter(
470 images = ec2resource.images.filter(
471 Filters=[
471 Filters=[
472 {'Name': 'owner-id', 'Values': [owner_id],},
472 {'Name': 'owner-id', 'Values': [owner_id],},
473 {'Name': 'state', 'Values': ['available'],},
473 {'Name': 'state', 'Values': ['available'],},
474 {'Name': 'image-type', 'Values': ['machine'],},
474 {'Name': 'image-type', 'Values': ['machine'],},
475 {'Name': 'name', 'Values': [name],},
475 {'Name': 'name', 'Values': [name],},
476 ]
476 ]
477 )
477 )
478
478
479 if reverse_sort_field:
480 images = sorted(
481 images,
482 key=lambda image: getattr(image, reverse_sort_field),
483 reverse=True,
484 )
485
479 for image in images:
486 for image in images:
480 return image
487 return image
481
488
482 raise Exception('unable to find image for %s' % name)
489 raise Exception('unable to find image for %s' % name)
483
490
484
491
485 def ensure_security_groups(ec2resource, prefix='hg-'):
492 def ensure_security_groups(ec2resource, prefix='hg-'):
486 """Ensure all necessary Mercurial security groups are present.
493 """Ensure all necessary Mercurial security groups are present.
487
494
488 All security groups are prefixed with ``hg-`` by default. Any security
495 All security groups are prefixed with ``hg-`` by default. Any security
489 groups having this prefix but aren't in our list are deleted.
496 groups having this prefix but aren't in our list are deleted.
490 """
497 """
491 existing = {}
498 existing = {}
492
499
493 for group in ec2resource.security_groups.all():
500 for group in ec2resource.security_groups.all():
494 if group.group_name.startswith(prefix):
501 if group.group_name.startswith(prefix):
495 existing[group.group_name[len(prefix) :]] = group
502 existing[group.group_name[len(prefix) :]] = group
496
503
497 purge = set(existing) - set(SECURITY_GROUPS)
504 purge = set(existing) - set(SECURITY_GROUPS)
498
505
499 for name in sorted(purge):
506 for name in sorted(purge):
500 group = existing[name]
507 group = existing[name]
501 print('removing legacy security group: %s' % group.group_name)
508 print('removing legacy security group: %s' % group.group_name)
502 group.delete()
509 group.delete()
503
510
504 security_groups = {}
511 security_groups = {}
505
512
506 for name, group in sorted(SECURITY_GROUPS.items()):
513 for name, group in sorted(SECURITY_GROUPS.items()):
507 if name in existing:
514 if name in existing:
508 security_groups[name] = existing[name]
515 security_groups[name] = existing[name]
509 continue
516 continue
510
517
511 actual = '%s%s' % (prefix, name)
518 actual = '%s%s' % (prefix, name)
512 print('adding security group %s' % actual)
519 print('adding security group %s' % actual)
513
520
514 group_res = ec2resource.create_security_group(
521 group_res = ec2resource.create_security_group(
515 Description=group['description'], GroupName=actual,
522 Description=group['description'], GroupName=actual,
516 )
523 )
517
524
518 group_res.authorize_ingress(IpPermissions=group['ingress'],)
525 group_res.authorize_ingress(IpPermissions=group['ingress'],)
519
526
520 security_groups[name] = group_res
527 security_groups[name] = group_res
521
528
522 return security_groups
529 return security_groups
523
530
524
531
525 def terminate_ec2_instances(ec2resource, prefix='hg-'):
532 def terminate_ec2_instances(ec2resource, prefix='hg-'):
526 """Terminate all EC2 instances managed by us."""
533 """Terminate all EC2 instances managed by us."""
527 waiting = []
534 waiting = []
528
535
529 for instance in ec2resource.instances.all():
536 for instance in ec2resource.instances.all():
530 if instance.state['Name'] == 'terminated':
537 if instance.state['Name'] == 'terminated':
531 continue
538 continue
532
539
533 for tag in instance.tags or []:
540 for tag in instance.tags or []:
534 if tag['Key'] == 'Name' and tag['Value'].startswith(prefix):
541 if tag['Key'] == 'Name' and tag['Value'].startswith(prefix):
535 print('terminating %s' % instance.id)
542 print('terminating %s' % instance.id)
536 instance.terminate()
543 instance.terminate()
537 waiting.append(instance)
544 waiting.append(instance)
538
545
539 for instance in waiting:
546 for instance in waiting:
540 instance.wait_until_terminated()
547 instance.wait_until_terminated()
541
548
542
549
543 def remove_resources(c, prefix='hg-'):
550 def remove_resources(c, prefix='hg-'):
544 """Purge all of our resources in this EC2 region."""
551 """Purge all of our resources in this EC2 region."""
545 ec2resource = c.ec2resource
552 ec2resource = c.ec2resource
546 iamresource = c.iamresource
553 iamresource = c.iamresource
547
554
548 terminate_ec2_instances(ec2resource, prefix=prefix)
555 terminate_ec2_instances(ec2resource, prefix=prefix)
549
556
550 for image in ec2resource.images.filter(Owners=['self']):
557 for image in ec2resource.images.filter(Owners=['self']):
551 if image.name.startswith(prefix):
558 if image.name.startswith(prefix):
552 remove_ami(ec2resource, image)
559 remove_ami(ec2resource, image)
553
560
554 for group in ec2resource.security_groups.all():
561 for group in ec2resource.security_groups.all():
555 if group.group_name.startswith(prefix):
562 if group.group_name.startswith(prefix):
556 print('removing security group %s' % group.group_name)
563 print('removing security group %s' % group.group_name)
557 group.delete()
564 group.delete()
558
565
559 for profile in iamresource.instance_profiles.all():
566 for profile in iamresource.instance_profiles.all():
560 if profile.name.startswith(prefix):
567 if profile.name.startswith(prefix):
561 delete_instance_profile(profile)
568 delete_instance_profile(profile)
562
569
563 for role in iamresource.roles.all():
570 for role in iamresource.roles.all():
564 if role.name.startswith(prefix):
571 if role.name.startswith(prefix):
565 for p in role.attached_policies.all():
572 for p in role.attached_policies.all():
566 print('detaching policy %s from %s' % (p.arn, role.name))
573 print('detaching policy %s from %s' % (p.arn, role.name))
567 role.detach_policy(PolicyArn=p.arn)
574 role.detach_policy(PolicyArn=p.arn)
568
575
569 print('removing role %s' % role.name)
576 print('removing role %s' % role.name)
570 role.delete()
577 role.delete()
571
578
572
579
573 def wait_for_ip_addresses(instances):
580 def wait_for_ip_addresses(instances):
574 """Wait for the public IP addresses of an iterable of instances."""
581 """Wait for the public IP addresses of an iterable of instances."""
575 for instance in instances:
582 for instance in instances:
576 while True:
583 while True:
577 if not instance.public_ip_address:
584 if not instance.public_ip_address:
578 time.sleep(2)
585 time.sleep(2)
579 instance.reload()
586 instance.reload()
580 continue
587 continue
581
588
582 print(
589 print(
583 'public IP address for %s: %s'
590 'public IP address for %s: %s'
584 % (instance.id, instance.public_ip_address)
591 % (instance.id, instance.public_ip_address)
585 )
592 )
586 break
593 break
587
594
588
595
589 def remove_ami(ec2resource, image):
596 def remove_ami(ec2resource, image):
590 """Remove an AMI and its underlying snapshots."""
597 """Remove an AMI and its underlying snapshots."""
591 snapshots = []
598 snapshots = []
592
599
593 for device in image.block_device_mappings:
600 for device in image.block_device_mappings:
594 if 'Ebs' in device:
601 if 'Ebs' in device:
595 snapshots.append(ec2resource.Snapshot(device['Ebs']['SnapshotId']))
602 snapshots.append(ec2resource.Snapshot(device['Ebs']['SnapshotId']))
596
603
597 print('deregistering %s' % image.id)
604 print('deregistering %s' % image.id)
598 image.deregister()
605 image.deregister()
599
606
600 for snapshot in snapshots:
607 for snapshot in snapshots:
601 print('deleting snapshot %s' % snapshot.id)
608 print('deleting snapshot %s' % snapshot.id)
602 snapshot.delete()
609 snapshot.delete()
603
610
604
611
605 def wait_for_ssm(ssmclient, instances):
612 def wait_for_ssm(ssmclient, instances):
606 """Wait for SSM to come online for an iterable of instance IDs."""
613 """Wait for SSM to come online for an iterable of instance IDs."""
607 while True:
614 while True:
608 res = ssmclient.describe_instance_information(
615 res = ssmclient.describe_instance_information(
609 Filters=[
616 Filters=[
610 {'Key': 'InstanceIds', 'Values': [i.id for i in instances],},
617 {'Key': 'InstanceIds', 'Values': [i.id for i in instances],},
611 ],
618 ],
612 )
619 )
613
620
614 available = len(res['InstanceInformationList'])
621 available = len(res['InstanceInformationList'])
615 wanted = len(instances)
622 wanted = len(instances)
616
623
617 print('%d/%d instances available in SSM' % (available, wanted))
624 print('%d/%d instances available in SSM' % (available, wanted))
618
625
619 if available == wanted:
626 if available == wanted:
620 return
627 return
621
628
622 time.sleep(2)
629 time.sleep(2)
623
630
624
631
625 def run_ssm_command(ssmclient, instances, document_name, parameters):
632 def run_ssm_command(ssmclient, instances, document_name, parameters):
626 """Run a PowerShell script on an EC2 instance."""
633 """Run a PowerShell script on an EC2 instance."""
627
634
628 res = ssmclient.send_command(
635 res = ssmclient.send_command(
629 InstanceIds=[i.id for i in instances],
636 InstanceIds=[i.id for i in instances],
630 DocumentName=document_name,
637 DocumentName=document_name,
631 Parameters=parameters,
638 Parameters=parameters,
632 CloudWatchOutputConfig={'CloudWatchOutputEnabled': True,},
639 CloudWatchOutputConfig={'CloudWatchOutputEnabled': True,},
633 )
640 )
634
641
635 command_id = res['Command']['CommandId']
642 command_id = res['Command']['CommandId']
636
643
637 for instance in instances:
644 for instance in instances:
638 while True:
645 while True:
639 try:
646 try:
640 res = ssmclient.get_command_invocation(
647 res = ssmclient.get_command_invocation(
641 CommandId=command_id, InstanceId=instance.id,
648 CommandId=command_id, InstanceId=instance.id,
642 )
649 )
643 except botocore.exceptions.ClientError as e:
650 except botocore.exceptions.ClientError as e:
644 if e.response['Error']['Code'] == 'InvocationDoesNotExist':
651 if e.response['Error']['Code'] == 'InvocationDoesNotExist':
645 print('could not find SSM command invocation; waiting')
652 print('could not find SSM command invocation; waiting')
646 time.sleep(1)
653 time.sleep(1)
647 continue
654 continue
648 else:
655 else:
649 raise
656 raise
650
657
651 if res['Status'] == 'Success':
658 if res['Status'] == 'Success':
652 break
659 break
653 elif res['Status'] in ('Pending', 'InProgress', 'Delayed'):
660 elif res['Status'] in ('Pending', 'InProgress', 'Delayed'):
654 time.sleep(2)
661 time.sleep(2)
655 else:
662 else:
656 raise Exception(
663 raise Exception(
657 'command failed on %s: %s' % (instance.id, res['Status'])
664 'command failed on %s: %s' % (instance.id, res['Status'])
658 )
665 )
659
666
660
667
661 @contextlib.contextmanager
668 @contextlib.contextmanager
662 def temporary_ec2_instances(ec2resource, config):
669 def temporary_ec2_instances(ec2resource, config):
663 """Create temporary EC2 instances.
670 """Create temporary EC2 instances.
664
671
665 This is a proxy to ``ec2client.run_instances(**config)`` that takes care of
672 This is a proxy to ``ec2client.run_instances(**config)`` that takes care of
666 managing the lifecycle of the instances.
673 managing the lifecycle of the instances.
667
674
668 When the context manager exits, the instances are terminated.
675 When the context manager exits, the instances are terminated.
669
676
670 The context manager evaluates to the list of data structures
677 The context manager evaluates to the list of data structures
671 describing each created instance. The instances may not be available
678 describing each created instance. The instances may not be available
672 for work immediately: it is up to the caller to wait for the instance
679 for work immediately: it is up to the caller to wait for the instance
673 to start responding.
680 to start responding.
674 """
681 """
675
682
676 ids = None
683 ids = None
677
684
678 try:
685 try:
679 res = ec2resource.create_instances(**config)
686 res = ec2resource.create_instances(**config)
680
687
681 ids = [i.id for i in res]
688 ids = [i.id for i in res]
682 print('started instances: %s' % ' '.join(ids))
689 print('started instances: %s' % ' '.join(ids))
683
690
684 yield res
691 yield res
685 finally:
692 finally:
686 if ids:
693 if ids:
687 print('terminating instances: %s' % ' '.join(ids))
694 print('terminating instances: %s' % ' '.join(ids))
688 for instance in res:
695 for instance in res:
689 instance.terminate()
696 instance.terminate()
690 print('terminated %d instances' % len(ids))
697 print('terminated %d instances' % len(ids))
691
698
692
699
693 @contextlib.contextmanager
700 @contextlib.contextmanager
694 def create_temp_windows_ec2_instances(
701 def create_temp_windows_ec2_instances(
695 c: AWSConnection, config, bootstrap: bool = False
702 c: AWSConnection, config, bootstrap: bool = False
696 ):
703 ):
697 """Create temporary Windows EC2 instances.
704 """Create temporary Windows EC2 instances.
698
705
699 This is a higher-level wrapper around ``create_temp_ec2_instances()`` that
706 This is a higher-level wrapper around ``create_temp_ec2_instances()`` that
700 configures the Windows instance for Windows Remote Management. The emitted
707 configures the Windows instance for Windows Remote Management. The emitted
701 instances will have a ``winrm_client`` attribute containing a
708 instances will have a ``winrm_client`` attribute containing a
702 ``pypsrp.client.Client`` instance bound to the instance.
709 ``pypsrp.client.Client`` instance bound to the instance.
703 """
710 """
704 if 'IamInstanceProfile' in config:
711 if 'IamInstanceProfile' in config:
705 raise ValueError('IamInstanceProfile cannot be provided in config')
712 raise ValueError('IamInstanceProfile cannot be provided in config')
706 if 'UserData' in config:
713 if 'UserData' in config:
707 raise ValueError('UserData cannot be provided in config')
714 raise ValueError('UserData cannot be provided in config')
708
715
709 password = c.automation.default_password()
716 password = c.automation.default_password()
710
717
711 config = copy.deepcopy(config)
718 config = copy.deepcopy(config)
712 config['IamInstanceProfile'] = {
719 config['IamInstanceProfile'] = {
713 'Name': 'hg-ephemeral-ec2-1',
720 'Name': 'hg-ephemeral-ec2-1',
714 }
721 }
715 config.setdefault('TagSpecifications', []).append(
722 config.setdefault('TagSpecifications', []).append(
716 {
723 {
717 'ResourceType': 'instance',
724 'ResourceType': 'instance',
718 'Tags': [{'Key': 'Name', 'Value': 'hg-temp-windows'}],
725 'Tags': [{'Key': 'Name', 'Value': 'hg-temp-windows'}],
719 }
726 }
720 )
727 )
721
728
722 if bootstrap:
729 if bootstrap:
723 config['UserData'] = WINDOWS_USER_DATA % password
730 config['UserData'] = WINDOWS_USER_DATA % password
724
731
725 with temporary_ec2_instances(c.ec2resource, config) as instances:
732 with temporary_ec2_instances(c.ec2resource, config) as instances:
726 wait_for_ip_addresses(instances)
733 wait_for_ip_addresses(instances)
727
734
728 print('waiting for Windows Remote Management service...')
735 print('waiting for Windows Remote Management service...')
729
736
730 for instance in instances:
737 for instance in instances:
731 client = wait_for_winrm(
738 client = wait_for_winrm(
732 instance.public_ip_address, 'Administrator', password
739 instance.public_ip_address, 'Administrator', password
733 )
740 )
734 print('established WinRM connection to %s' % instance.id)
741 print('established WinRM connection to %s' % instance.id)
735 instance.winrm_client = client
742 instance.winrm_client = client
736
743
737 yield instances
744 yield instances
738
745
739
746
740 def resolve_fingerprint(fingerprint):
747 def resolve_fingerprint(fingerprint):
741 fingerprint = json.dumps(fingerprint, sort_keys=True)
748 fingerprint = json.dumps(fingerprint, sort_keys=True)
742 return hashlib.sha256(fingerprint.encode('utf-8')).hexdigest()
749 return hashlib.sha256(fingerprint.encode('utf-8')).hexdigest()
743
750
744
751
745 def find_and_reconcile_image(ec2resource, name, fingerprint):
752 def find_and_reconcile_image(ec2resource, name, fingerprint):
746 """Attempt to find an existing EC2 AMI with a name and fingerprint.
753 """Attempt to find an existing EC2 AMI with a name and fingerprint.
747
754
748 If an image with the specified fingerprint is found, it is returned.
755 If an image with the specified fingerprint is found, it is returned.
749 Otherwise None is returned.
756 Otherwise None is returned.
750
757
751 Existing images for the specified name that don't have the specified
758 Existing images for the specified name that don't have the specified
752 fingerprint or are missing required metadata or deleted.
759 fingerprint or are missing required metadata or deleted.
753 """
760 """
754 # Find existing AMIs with this name and delete the ones that are invalid.
761 # Find existing AMIs with this name and delete the ones that are invalid.
755 # Store a reference to a good image so it can be returned one the
762 # Store a reference to a good image so it can be returned one the
756 # image state is reconciled.
763 # image state is reconciled.
757 images = ec2resource.images.filter(
764 images = ec2resource.images.filter(
758 Filters=[{'Name': 'name', 'Values': [name]}]
765 Filters=[{'Name': 'name', 'Values': [name]}]
759 )
766 )
760
767
761 existing_image = None
768 existing_image = None
762
769
763 for image in images:
770 for image in images:
764 if image.tags is None:
771 if image.tags is None:
765 print(
772 print(
766 'image %s for %s lacks required tags; removing'
773 'image %s for %s lacks required tags; removing'
767 % (image.id, image.name)
774 % (image.id, image.name)
768 )
775 )
769 remove_ami(ec2resource, image)
776 remove_ami(ec2resource, image)
770 else:
777 else:
771 tags = {t['Key']: t['Value'] for t in image.tags}
778 tags = {t['Key']: t['Value'] for t in image.tags}
772
779
773 if tags.get('HGIMAGEFINGERPRINT') == fingerprint:
780 if tags.get('HGIMAGEFINGERPRINT') == fingerprint:
774 existing_image = image
781 existing_image = image
775 else:
782 else:
776 print(
783 print(
777 'image %s for %s has wrong fingerprint; removing'
784 'image %s for %s has wrong fingerprint; removing'
778 % (image.id, image.name)
785 % (image.id, image.name)
779 )
786 )
780 remove_ami(ec2resource, image)
787 remove_ami(ec2resource, image)
781
788
782 return existing_image
789 return existing_image
783
790
784
791
785 def create_ami_from_instance(
792 def create_ami_from_instance(
786 ec2client, instance, name, description, fingerprint
793 ec2client, instance, name, description, fingerprint
787 ):
794 ):
788 """Create an AMI from a running instance.
795 """Create an AMI from a running instance.
789
796
790 Returns the ``ec2resource.Image`` representing the created AMI.
797 Returns the ``ec2resource.Image`` representing the created AMI.
791 """
798 """
792 instance.stop()
799 instance.stop()
793
800
794 ec2client.get_waiter('instance_stopped').wait(
801 ec2client.get_waiter('instance_stopped').wait(
795 InstanceIds=[instance.id], WaiterConfig={'Delay': 5,}
802 InstanceIds=[instance.id], WaiterConfig={'Delay': 5,}
796 )
803 )
797 print('%s is stopped' % instance.id)
804 print('%s is stopped' % instance.id)
798
805
799 image = instance.create_image(Name=name, Description=description,)
806 image = instance.create_image(Name=name, Description=description,)
800
807
801 image.create_tags(
808 image.create_tags(
802 Tags=[{'Key': 'HGIMAGEFINGERPRINT', 'Value': fingerprint,},]
809 Tags=[{'Key': 'HGIMAGEFINGERPRINT', 'Value': fingerprint,},]
803 )
810 )
804
811
805 print('waiting for image %s' % image.id)
812 print('waiting for image %s' % image.id)
806
813
807 ec2client.get_waiter('image_available').wait(ImageIds=[image.id],)
814 ec2client.get_waiter('image_available').wait(ImageIds=[image.id],)
808
815
809 print('image %s available as %s' % (image.id, image.name))
816 print('image %s available as %s' % (image.id, image.name))
810
817
811 return image
818 return image
812
819
813
820
814 def ensure_linux_dev_ami(c: AWSConnection, distro='debian10', prefix='hg-'):
821 def ensure_linux_dev_ami(c: AWSConnection, distro='debian10', prefix='hg-'):
815 """Ensures a Linux development AMI is available and up-to-date.
822 """Ensures a Linux development AMI is available and up-to-date.
816
823
817 Returns an ``ec2.Image`` of either an existing AMI or a newly-built one.
824 Returns an ``ec2.Image`` of either an existing AMI or a newly-built one.
818 """
825 """
819 ec2client = c.ec2client
826 ec2client = c.ec2client
820 ec2resource = c.ec2resource
827 ec2resource = c.ec2resource
821
828
822 name = '%s%s-%s' % (prefix, 'linux-dev', distro)
829 name = '%s%s-%s' % (prefix, 'linux-dev', distro)
823
830
824 if distro == 'debian9':
831 if distro == 'debian9':
825 image = find_image(
832 image = find_image(
826 ec2resource,
833 ec2resource,
827 DEBIAN_ACCOUNT_ID,
834 DEBIAN_ACCOUNT_ID,
828 'debian-stretch-hvm-x86_64-gp2-2019-09-08-17994',
835 'debian-stretch-hvm-x86_64-gp2-2019-09-08-17994',
829 )
836 )
830 ssh_username = 'admin'
837 ssh_username = 'admin'
831 elif distro == 'debian10':
838 elif distro == 'debian10':
832 image = find_image(
839 image = find_image(
833 ec2resource, DEBIAN_ACCOUNT_ID_2, 'debian-10-amd64-20190909-10',
840 ec2resource, DEBIAN_ACCOUNT_ID_2, 'debian-10-amd64-20190909-10',
834 )
841 )
835 ssh_username = 'admin'
842 ssh_username = 'admin'
836 elif distro == 'ubuntu18.04':
843 elif distro == 'ubuntu18.04':
837 image = find_image(
844 image = find_image(
838 ec2resource,
845 ec2resource,
839 UBUNTU_ACCOUNT_ID,
846 UBUNTU_ACCOUNT_ID,
840 'ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190918',
847 'ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190918',
841 )
848 )
842 ssh_username = 'ubuntu'
849 ssh_username = 'ubuntu'
843 elif distro == 'ubuntu19.04':
850 elif distro == 'ubuntu19.04':
844 image = find_image(
851 image = find_image(
845 ec2resource,
852 ec2resource,
846 UBUNTU_ACCOUNT_ID,
853 UBUNTU_ACCOUNT_ID,
847 'ubuntu/images/hvm-ssd/ubuntu-disco-19.04-amd64-server-20190918',
854 'ubuntu/images/hvm-ssd/ubuntu-disco-19.04-amd64-server-20190918',
848 )
855 )
849 ssh_username = 'ubuntu'
856 ssh_username = 'ubuntu'
850 else:
857 else:
851 raise ValueError('unsupported Linux distro: %s' % distro)
858 raise ValueError('unsupported Linux distro: %s' % distro)
852
859
853 config = {
860 config = {
854 'BlockDeviceMappings': [
861 'BlockDeviceMappings': [
855 {
862 {
856 'DeviceName': image.block_device_mappings[0]['DeviceName'],
863 'DeviceName': image.block_device_mappings[0]['DeviceName'],
857 'Ebs': {
864 'Ebs': {
858 'DeleteOnTermination': True,
865 'DeleteOnTermination': True,
859 'VolumeSize': 10,
866 'VolumeSize': 10,
860 'VolumeType': 'gp2',
867 'VolumeType': 'gp2',
861 },
868 },
862 },
869 },
863 ],
870 ],
864 'EbsOptimized': True,
871 'EbsOptimized': True,
865 'ImageId': image.id,
872 'ImageId': image.id,
866 'InstanceInitiatedShutdownBehavior': 'stop',
873 'InstanceInitiatedShutdownBehavior': 'stop',
867 # 8 VCPUs for compiling Python.
874 # 8 VCPUs for compiling Python.
868 'InstanceType': 't3.2xlarge',
875 'InstanceType': 't3.2xlarge',
869 'KeyName': '%sautomation' % prefix,
876 'KeyName': '%sautomation' % prefix,
870 'MaxCount': 1,
877 'MaxCount': 1,
871 'MinCount': 1,
878 'MinCount': 1,
872 'SecurityGroupIds': [c.security_groups['linux-dev-1'].id],
879 'SecurityGroupIds': [c.security_groups['linux-dev-1'].id],
873 }
880 }
874
881
875 requirements2_path = (
882 requirements2_path = (
876 pathlib.Path(__file__).parent.parent / 'linux-requirements-py2.txt'
883 pathlib.Path(__file__).parent.parent / 'linux-requirements-py2.txt'
877 )
884 )
878 requirements3_path = (
885 requirements3_path = (
879 pathlib.Path(__file__).parent.parent / 'linux-requirements-py3.txt'
886 pathlib.Path(__file__).parent.parent / 'linux-requirements-py3.txt'
880 )
887 )
881 with requirements2_path.open('r', encoding='utf-8') as fh:
888 with requirements2_path.open('r', encoding='utf-8') as fh:
882 requirements2 = fh.read()
889 requirements2 = fh.read()
883 with requirements3_path.open('r', encoding='utf-8') as fh:
890 with requirements3_path.open('r', encoding='utf-8') as fh:
884 requirements3 = fh.read()
891 requirements3 = fh.read()
885
892
886 # Compute a deterministic fingerprint to determine whether image needs to
893 # Compute a deterministic fingerprint to determine whether image needs to
887 # be regenerated.
894 # be regenerated.
888 fingerprint = resolve_fingerprint(
895 fingerprint = resolve_fingerprint(
889 {
896 {
890 'instance_config': config,
897 'instance_config': config,
891 'bootstrap_script': BOOTSTRAP_DEBIAN,
898 'bootstrap_script': BOOTSTRAP_DEBIAN,
892 'requirements_py2': requirements2,
899 'requirements_py2': requirements2,
893 'requirements_py3': requirements3,
900 'requirements_py3': requirements3,
894 }
901 }
895 )
902 )
896
903
897 existing_image = find_and_reconcile_image(ec2resource, name, fingerprint)
904 existing_image = find_and_reconcile_image(ec2resource, name, fingerprint)
898
905
899 if existing_image:
906 if existing_image:
900 return existing_image
907 return existing_image
901
908
902 print('no suitable %s image found; creating one...' % name)
909 print('no suitable %s image found; creating one...' % name)
903
910
904 with temporary_ec2_instances(ec2resource, config) as instances:
911 with temporary_ec2_instances(ec2resource, config) as instances:
905 wait_for_ip_addresses(instances)
912 wait_for_ip_addresses(instances)
906
913
907 instance = instances[0]
914 instance = instances[0]
908
915
909 client = wait_for_ssh(
916 client = wait_for_ssh(
910 instance.public_ip_address,
917 instance.public_ip_address,
911 22,
918 22,
912 username=ssh_username,
919 username=ssh_username,
913 key_filename=str(c.key_pair_path_private('automation')),
920 key_filename=str(c.key_pair_path_private('automation')),
914 )
921 )
915
922
916 home = '/home/%s' % ssh_username
923 home = '/home/%s' % ssh_username
917
924
918 with client:
925 with client:
919 print('connecting to SSH server')
926 print('connecting to SSH server')
920 sftp = client.open_sftp()
927 sftp = client.open_sftp()
921
928
922 print('uploading bootstrap files')
929 print('uploading bootstrap files')
923 with sftp.open('%s/bootstrap' % home, 'wb') as fh:
930 with sftp.open('%s/bootstrap' % home, 'wb') as fh:
924 fh.write(BOOTSTRAP_DEBIAN)
931 fh.write(BOOTSTRAP_DEBIAN)
925 fh.chmod(0o0700)
932 fh.chmod(0o0700)
926
933
927 with sftp.open('%s/requirements-py2.txt' % home, 'wb') as fh:
934 with sftp.open('%s/requirements-py2.txt' % home, 'wb') as fh:
928 fh.write(requirements2)
935 fh.write(requirements2)
929 fh.chmod(0o0700)
936 fh.chmod(0o0700)
930
937
931 with sftp.open('%s/requirements-py3.txt' % home, 'wb') as fh:
938 with sftp.open('%s/requirements-py3.txt' % home, 'wb') as fh:
932 fh.write(requirements3)
939 fh.write(requirements3)
933 fh.chmod(0o0700)
940 fh.chmod(0o0700)
934
941
935 print('executing bootstrap')
942 print('executing bootstrap')
936 chan, stdin, stdout = ssh_exec_command(
943 chan, stdin, stdout = ssh_exec_command(
937 client, '%s/bootstrap' % home
944 client, '%s/bootstrap' % home
938 )
945 )
939 stdin.close()
946 stdin.close()
940
947
941 for line in stdout:
948 for line in stdout:
942 print(line, end='')
949 print(line, end='')
943
950
944 res = chan.recv_exit_status()
951 res = chan.recv_exit_status()
945 if res:
952 if res:
946 raise Exception('non-0 exit from bootstrap: %d' % res)
953 raise Exception('non-0 exit from bootstrap: %d' % res)
947
954
948 print(
955 print(
949 'bootstrap completed; stopping %s to create %s'
956 'bootstrap completed; stopping %s to create %s'
950 % (instance.id, name)
957 % (instance.id, name)
951 )
958 )
952
959
953 return create_ami_from_instance(
960 return create_ami_from_instance(
954 ec2client,
961 ec2client,
955 instance,
962 instance,
956 name,
963 name,
957 'Mercurial Linux development environment',
964 'Mercurial Linux development environment',
958 fingerprint,
965 fingerprint,
959 )
966 )
960
967
961
968
962 @contextlib.contextmanager
969 @contextlib.contextmanager
963 def temporary_linux_dev_instances(
970 def temporary_linux_dev_instances(
964 c: AWSConnection,
971 c: AWSConnection,
965 image,
972 image,
966 instance_type,
973 instance_type,
967 prefix='hg-',
974 prefix='hg-',
968 ensure_extra_volume=False,
975 ensure_extra_volume=False,
969 ):
976 ):
970 """Create temporary Linux development EC2 instances.
977 """Create temporary Linux development EC2 instances.
971
978
972 Context manager resolves to a list of ``ec2.Instance`` that were created
979 Context manager resolves to a list of ``ec2.Instance`` that were created
973 and are running.
980 and are running.
974
981
975 ``ensure_extra_volume`` can be set to ``True`` to require that instances
982 ``ensure_extra_volume`` can be set to ``True`` to require that instances
976 have a 2nd storage volume available other than the primary AMI volume.
983 have a 2nd storage volume available other than the primary AMI volume.
977 For instance types with instance storage, this does nothing special.
984 For instance types with instance storage, this does nothing special.
978 But for instance types without instance storage, an additional EBS volume
985 But for instance types without instance storage, an additional EBS volume
979 will be added to the instance.
986 will be added to the instance.
980
987
981 Instances have an ``ssh_client`` attribute containing a paramiko SSHClient
988 Instances have an ``ssh_client`` attribute containing a paramiko SSHClient
982 instance bound to the instance.
989 instance bound to the instance.
983
990
984 Instances have an ``ssh_private_key_path`` attributing containing the
991 Instances have an ``ssh_private_key_path`` attributing containing the
985 str path to the SSH private key to connect to the instance.
992 str path to the SSH private key to connect to the instance.
986 """
993 """
987
994
988 block_device_mappings = [
995 block_device_mappings = [
989 {
996 {
990 'DeviceName': image.block_device_mappings[0]['DeviceName'],
997 'DeviceName': image.block_device_mappings[0]['DeviceName'],
991 'Ebs': {
998 'Ebs': {
992 'DeleteOnTermination': True,
999 'DeleteOnTermination': True,
993 'VolumeSize': 12,
1000 'VolumeSize': 12,
994 'VolumeType': 'gp2',
1001 'VolumeType': 'gp2',
995 },
1002 },
996 }
1003 }
997 ]
1004 ]
998
1005
999 # This is not an exhaustive list of instance types having instance storage.
1006 # This is not an exhaustive list of instance types having instance storage.
1000 # But
1007 # But
1001 if ensure_extra_volume and not instance_type.startswith(
1008 if ensure_extra_volume and not instance_type.startswith(
1002 tuple(INSTANCE_TYPES_WITH_STORAGE)
1009 tuple(INSTANCE_TYPES_WITH_STORAGE)
1003 ):
1010 ):
1004 main_device = block_device_mappings[0]['DeviceName']
1011 main_device = block_device_mappings[0]['DeviceName']
1005
1012
1006 if main_device == 'xvda':
1013 if main_device == 'xvda':
1007 second_device = 'xvdb'
1014 second_device = 'xvdb'
1008 elif main_device == '/dev/sda1':
1015 elif main_device == '/dev/sda1':
1009 second_device = '/dev/sdb'
1016 second_device = '/dev/sdb'
1010 else:
1017 else:
1011 raise ValueError(
1018 raise ValueError(
1012 'unhandled primary EBS device name: %s' % main_device
1019 'unhandled primary EBS device name: %s' % main_device
1013 )
1020 )
1014
1021
1015 block_device_mappings.append(
1022 block_device_mappings.append(
1016 {
1023 {
1017 'DeviceName': second_device,
1024 'DeviceName': second_device,
1018 'Ebs': {
1025 'Ebs': {
1019 'DeleteOnTermination': True,
1026 'DeleteOnTermination': True,
1020 'VolumeSize': 8,
1027 'VolumeSize': 8,
1021 'VolumeType': 'gp2',
1028 'VolumeType': 'gp2',
1022 },
1029 },
1023 }
1030 }
1024 )
1031 )
1025
1032
1026 config = {
1033 config = {
1027 'BlockDeviceMappings': block_device_mappings,
1034 'BlockDeviceMappings': block_device_mappings,
1028 'EbsOptimized': True,
1035 'EbsOptimized': True,
1029 'ImageId': image.id,
1036 'ImageId': image.id,
1030 'InstanceInitiatedShutdownBehavior': 'terminate',
1037 'InstanceInitiatedShutdownBehavior': 'terminate',
1031 'InstanceType': instance_type,
1038 'InstanceType': instance_type,
1032 'KeyName': '%sautomation' % prefix,
1039 'KeyName': '%sautomation' % prefix,
1033 'MaxCount': 1,
1040 'MaxCount': 1,
1034 'MinCount': 1,
1041 'MinCount': 1,
1035 'SecurityGroupIds': [c.security_groups['linux-dev-1'].id],
1042 'SecurityGroupIds': [c.security_groups['linux-dev-1'].id],
1036 }
1043 }
1037
1044
1038 with temporary_ec2_instances(c.ec2resource, config) as instances:
1045 with temporary_ec2_instances(c.ec2resource, config) as instances:
1039 wait_for_ip_addresses(instances)
1046 wait_for_ip_addresses(instances)
1040
1047
1041 ssh_private_key_path = str(c.key_pair_path_private('automation'))
1048 ssh_private_key_path = str(c.key_pair_path_private('automation'))
1042
1049
1043 for instance in instances:
1050 for instance in instances:
1044 client = wait_for_ssh(
1051 client = wait_for_ssh(
1045 instance.public_ip_address,
1052 instance.public_ip_address,
1046 22,
1053 22,
1047 username='hg',
1054 username='hg',
1048 key_filename=ssh_private_key_path,
1055 key_filename=ssh_private_key_path,
1049 )
1056 )
1050
1057
1051 instance.ssh_client = client
1058 instance.ssh_client = client
1052 instance.ssh_private_key_path = ssh_private_key_path
1059 instance.ssh_private_key_path = ssh_private_key_path
1053
1060
1054 try:
1061 try:
1055 yield instances
1062 yield instances
1056 finally:
1063 finally:
1057 for instance in instances:
1064 for instance in instances:
1058 instance.ssh_client.close()
1065 instance.ssh_client.close()
1059
1066
1060
1067
1061 def ensure_windows_dev_ami(
1068 def ensure_windows_dev_ami(
1062 c: AWSConnection, prefix='hg-', base_image_name=WINDOWS_BASE_IMAGE_NAME
1069 c: AWSConnection, prefix='hg-', base_image_name=WINDOWS_BASE_IMAGE_NAME,
1063 ):
1070 ):
1064 """Ensure Windows Development AMI is available and up-to-date.
1071 """Ensure Windows Development AMI is available and up-to-date.
1065
1072
1066 If necessary, a modern AMI will be built by starting a temporary EC2
1073 If necessary, a modern AMI will be built by starting a temporary EC2
1067 instance and bootstrapping it.
1074 instance and bootstrapping it.
1068
1075
1069 Obsolete AMIs will be deleted so there is only a single AMI having the
1076 Obsolete AMIs will be deleted so there is only a single AMI having the
1070 desired name.
1077 desired name.
1071
1078
1072 Returns an ``ec2.Image`` of either an existing AMI or a newly-built
1079 Returns an ``ec2.Image`` of either an existing AMI or a newly-built
1073 one.
1080 one.
1074 """
1081 """
1075 ec2client = c.ec2client
1082 ec2client = c.ec2client
1076 ec2resource = c.ec2resource
1083 ec2resource = c.ec2resource
1077 ssmclient = c.session.client('ssm')
1084 ssmclient = c.session.client('ssm')
1078
1085
1079 name = '%s%s' % (prefix, 'windows-dev')
1086 name = '%s%s' % (prefix, 'windows-dev')
1080
1087
1081 image = find_image(ec2resource, AMAZON_ACCOUNT_ID, base_image_name)
1088 image = find_image(
1089 ec2resource,
1090 AMAZON_ACCOUNT_ID,
1091 base_image_name,
1092 reverse_sort_field="name",
1093 )
1082
1094
1083 config = {
1095 config = {
1084 'BlockDeviceMappings': [
1096 'BlockDeviceMappings': [
1085 {
1097 {
1086 'DeviceName': '/dev/sda1',
1098 'DeviceName': '/dev/sda1',
1087 'Ebs': {
1099 'Ebs': {
1088 'DeleteOnTermination': True,
1100 'DeleteOnTermination': True,
1089 'VolumeSize': 32,
1101 'VolumeSize': 32,
1090 'VolumeType': 'gp2',
1102 'VolumeType': 'gp2',
1091 },
1103 },
1092 }
1104 }
1093 ],
1105 ],
1094 'ImageId': image.id,
1106 'ImageId': image.id,
1095 'InstanceInitiatedShutdownBehavior': 'stop',
1107 'InstanceInitiatedShutdownBehavior': 'stop',
1096 'InstanceType': 't3.medium',
1108 'InstanceType': 't3.medium',
1097 'KeyName': '%sautomation' % prefix,
1109 'KeyName': '%sautomation' % prefix,
1098 'MaxCount': 1,
1110 'MaxCount': 1,
1099 'MinCount': 1,
1111 'MinCount': 1,
1100 'SecurityGroupIds': [c.security_groups['windows-dev-1'].id],
1112 'SecurityGroupIds': [c.security_groups['windows-dev-1'].id],
1101 }
1113 }
1102
1114
1103 commands = [
1115 commands = [
1104 # Need to start the service so sshd_config is generated.
1116 # Need to start the service so sshd_config is generated.
1105 'Start-Service sshd',
1117 'Start-Service sshd',
1106 'Write-Output "modifying sshd_config"',
1118 'Write-Output "modifying sshd_config"',
1107 r'$content = Get-Content C:\ProgramData\ssh\sshd_config',
1119 r'$content = Get-Content C:\ProgramData\ssh\sshd_config',
1108 '$content = $content -replace "Match Group administrators","" -replace "AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys",""',
1120 '$content = $content -replace "Match Group administrators","" -replace "AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys",""',
1109 r'$content | Set-Content C:\ProgramData\ssh\sshd_config',
1121 r'$content | Set-Content C:\ProgramData\ssh\sshd_config',
1110 'Import-Module OpenSSHUtils',
1122 'Import-Module OpenSSHUtils',
1111 r'Repair-SshdConfigPermission C:\ProgramData\ssh\sshd_config -Confirm:$false',
1123 r'Repair-SshdConfigPermission C:\ProgramData\ssh\sshd_config -Confirm:$false',
1112 'Restart-Service sshd',
1124 'Restart-Service sshd',
1113 'Write-Output "installing OpenSSL client"',
1125 'Write-Output "installing OpenSSL client"',
1114 'Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0',
1126 'Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0',
1115 'Set-Service -Name sshd -StartupType "Automatic"',
1127 'Set-Service -Name sshd -StartupType "Automatic"',
1116 'Write-Output "OpenSSH server running"',
1128 'Write-Output "OpenSSH server running"',
1117 ]
1129 ]
1118
1130
1119 with INSTALL_WINDOWS_DEPENDENCIES.open('r', encoding='utf-8') as fh:
1131 with INSTALL_WINDOWS_DEPENDENCIES.open('r', encoding='utf-8') as fh:
1120 commands.extend(l.rstrip() for l in fh)
1132 commands.extend(l.rstrip() for l in fh)
1121
1133
1122 # Schedule run of EC2Launch on next boot. This ensures that UserData
1134 # Schedule run of EC2Launch on next boot. This ensures that UserData
1123 # is executed.
1135 # is executed.
1124 # We disable setComputerName because it forces a reboot.
1136 # We disable setComputerName because it forces a reboot.
1125 # We set an explicit admin password because this causes UserData to run
1137 # We set an explicit admin password because this causes UserData to run
1126 # as Administrator instead of System.
1138 # as Administrator instead of System.
1127 commands.extend(
1139 commands.extend(
1128 [
1140 [
1129 r'''Set-Content -Path C:\ProgramData\Amazon\EC2-Windows\Launch\Config\LaunchConfig.json '''
1141 r'''Set-Content -Path C:\ProgramData\Amazon\EC2-Windows\Launch\Config\LaunchConfig.json '''
1130 r'''-Value '{"setComputerName": false, "setWallpaper": true, "addDnsSuffixList": true, '''
1142 r'''-Value '{"setComputerName": false, "setWallpaper": true, "addDnsSuffixList": true, '''
1131 r'''"extendBootVolumeSize": true, "handleUserData": true, '''
1143 r'''"extendBootVolumeSize": true, "handleUserData": true, '''
1132 r'''"adminPasswordType": "Specify", "adminPassword": "%s"}' '''
1144 r'''"adminPasswordType": "Specify", "adminPassword": "%s"}' '''
1133 % c.automation.default_password(),
1145 % c.automation.default_password(),
1134 r'C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1 '
1146 r'C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1 '
1135 r'–Schedule',
1147 r'–Schedule',
1136 ]
1148 ]
1137 )
1149 )
1138
1150
1139 # Disable Windows Defender when bootstrapping because it just slows
1151 # Disable Windows Defender when bootstrapping because it just slows
1140 # things down.
1152 # things down.
1141 commands.insert(0, 'Set-MpPreference -DisableRealtimeMonitoring $true')
1153 commands.insert(0, 'Set-MpPreference -DisableRealtimeMonitoring $true')
1142 commands.append('Set-MpPreference -DisableRealtimeMonitoring $false')
1154 commands.append('Set-MpPreference -DisableRealtimeMonitoring $false')
1143
1155
1144 # Compute a deterministic fingerprint to determine whether image needs
1156 # Compute a deterministic fingerprint to determine whether image needs
1145 # to be regenerated.
1157 # to be regenerated.
1146 fingerprint = resolve_fingerprint(
1158 fingerprint = resolve_fingerprint(
1147 {
1159 {
1148 'instance_config': config,
1160 'instance_config': config,
1149 'user_data': WINDOWS_USER_DATA,
1161 'user_data': WINDOWS_USER_DATA,
1150 'initial_bootstrap': WINDOWS_BOOTSTRAP_POWERSHELL,
1162 'initial_bootstrap': WINDOWS_BOOTSTRAP_POWERSHELL,
1151 'bootstrap_commands': commands,
1163 'bootstrap_commands': commands,
1152 'base_image_name': base_image_name,
1164 'base_image_name': base_image_name,
1153 }
1165 }
1154 )
1166 )
1155
1167
1156 existing_image = find_and_reconcile_image(ec2resource, name, fingerprint)
1168 existing_image = find_and_reconcile_image(ec2resource, name, fingerprint)
1157
1169
1158 if existing_image:
1170 if existing_image:
1159 return existing_image
1171 return existing_image
1160
1172
1161 print('no suitable Windows development image found; creating one...')
1173 print('no suitable Windows development image found; creating one...')
1162
1174
1163 with create_temp_windows_ec2_instances(
1175 with create_temp_windows_ec2_instances(
1164 c, config, bootstrap=True
1176 c, config, bootstrap=True
1165 ) as instances:
1177 ) as instances:
1166 assert len(instances) == 1
1178 assert len(instances) == 1
1167 instance = instances[0]
1179 instance = instances[0]
1168
1180
1169 wait_for_ssm(ssmclient, [instance])
1181 wait_for_ssm(ssmclient, [instance])
1170
1182
1171 # On first boot, install various Windows updates.
1183 # On first boot, install various Windows updates.
1172 # We would ideally use PowerShell Remoting for this. However, there are
1184 # We would ideally use PowerShell Remoting for this. However, there are
1173 # trust issues that make it difficult to invoke Windows Update
1185 # trust issues that make it difficult to invoke Windows Update
1174 # remotely. So we use SSM, which has a mechanism for running Windows
1186 # remotely. So we use SSM, which has a mechanism for running Windows
1175 # Update.
1187 # Update.
1176 print('installing Windows features...')
1188 print('installing Windows features...')
1177 run_ssm_command(
1189 run_ssm_command(
1178 ssmclient,
1190 ssmclient,
1179 [instance],
1191 [instance],
1180 'AWS-RunPowerShellScript',
1192 'AWS-RunPowerShellScript',
1181 {'commands': WINDOWS_BOOTSTRAP_POWERSHELL.split('\n'),},
1193 {'commands': WINDOWS_BOOTSTRAP_POWERSHELL.split('\n'),},
1182 )
1194 )
1183
1195
1184 # Reboot so all updates are fully applied.
1196 # Reboot so all updates are fully applied.
1185 #
1197 #
1186 # We don't use instance.reboot() here because it is asynchronous and
1198 # We don't use instance.reboot() here because it is asynchronous and
1187 # we don't know when exactly the instance has rebooted. It could take
1199 # we don't know when exactly the instance has rebooted. It could take
1188 # a while to stop and we may start trying to interact with the instance
1200 # a while to stop and we may start trying to interact with the instance
1189 # before it has rebooted.
1201 # before it has rebooted.
1190 print('rebooting instance %s' % instance.id)
1202 print('rebooting instance %s' % instance.id)
1191 instance.stop()
1203 instance.stop()
1192 ec2client.get_waiter('instance_stopped').wait(
1204 ec2client.get_waiter('instance_stopped').wait(
1193 InstanceIds=[instance.id], WaiterConfig={'Delay': 5,}
1205 InstanceIds=[instance.id], WaiterConfig={'Delay': 5,}
1194 )
1206 )
1195
1207
1196 instance.start()
1208 instance.start()
1197 wait_for_ip_addresses([instance])
1209 wait_for_ip_addresses([instance])
1198
1210
1199 # There is a race condition here between the User Data PS script running
1211 # There is a race condition here between the User Data PS script running
1200 # and us connecting to WinRM. This can manifest as
1212 # and us connecting to WinRM. This can manifest as
1201 # "AuthorizationManager check failed" failures during run_powershell().
1213 # "AuthorizationManager check failed" failures during run_powershell().
1202 # TODO figure out a workaround.
1214 # TODO figure out a workaround.
1203
1215
1204 print('waiting for Windows Remote Management to come back...')
1216 print('waiting for Windows Remote Management to come back...')
1205 client = wait_for_winrm(
1217 client = wait_for_winrm(
1206 instance.public_ip_address,
1218 instance.public_ip_address,
1207 'Administrator',
1219 'Administrator',
1208 c.automation.default_password(),
1220 c.automation.default_password(),
1209 )
1221 )
1210 print('established WinRM connection to %s' % instance.id)
1222 print('established WinRM connection to %s' % instance.id)
1211 instance.winrm_client = client
1223 instance.winrm_client = client
1212
1224
1213 print('bootstrapping instance...')
1225 print('bootstrapping instance...')
1214 run_powershell(instance.winrm_client, '\n'.join(commands))
1226 run_powershell(instance.winrm_client, '\n'.join(commands))
1215
1227
1216 print('bootstrap completed; stopping %s to create image' % instance.id)
1228 print('bootstrap completed; stopping %s to create image' % instance.id)
1217 return create_ami_from_instance(
1229 return create_ami_from_instance(
1218 ec2client,
1230 ec2client,
1219 instance,
1231 instance,
1220 name,
1232 name,
1221 'Mercurial Windows development environment',
1233 'Mercurial Windows development environment',
1222 fingerprint,
1234 fingerprint,
1223 )
1235 )
1224
1236
1225
1237
1226 @contextlib.contextmanager
1238 @contextlib.contextmanager
1227 def temporary_windows_dev_instances(
1239 def temporary_windows_dev_instances(
1228 c: AWSConnection,
1240 c: AWSConnection,
1229 image,
1241 image,
1230 instance_type,
1242 instance_type,
1231 prefix='hg-',
1243 prefix='hg-',
1232 disable_antivirus=False,
1244 disable_antivirus=False,
1233 ):
1245 ):
1234 """Create a temporary Windows development EC2 instance.
1246 """Create a temporary Windows development EC2 instance.
1235
1247
1236 Context manager resolves to the list of ``EC2.Instance`` that were created.
1248 Context manager resolves to the list of ``EC2.Instance`` that were created.
1237 """
1249 """
1238 config = {
1250 config = {
1239 'BlockDeviceMappings': [
1251 'BlockDeviceMappings': [
1240 {
1252 {
1241 'DeviceName': '/dev/sda1',
1253 'DeviceName': '/dev/sda1',
1242 'Ebs': {
1254 'Ebs': {
1243 'DeleteOnTermination': True,
1255 'DeleteOnTermination': True,
1244 'VolumeSize': 32,
1256 'VolumeSize': 32,
1245 'VolumeType': 'gp2',
1257 'VolumeType': 'gp2',
1246 },
1258 },
1247 }
1259 }
1248 ],
1260 ],
1249 'ImageId': image.id,
1261 'ImageId': image.id,
1250 'InstanceInitiatedShutdownBehavior': 'stop',
1262 'InstanceInitiatedShutdownBehavior': 'stop',
1251 'InstanceType': instance_type,
1263 'InstanceType': instance_type,
1252 'KeyName': '%sautomation' % prefix,
1264 'KeyName': '%sautomation' % prefix,
1253 'MaxCount': 1,
1265 'MaxCount': 1,
1254 'MinCount': 1,
1266 'MinCount': 1,
1255 'SecurityGroupIds': [c.security_groups['windows-dev-1'].id],
1267 'SecurityGroupIds': [c.security_groups['windows-dev-1'].id],
1256 }
1268 }
1257
1269
1258 with create_temp_windows_ec2_instances(c, config) as instances:
1270 with create_temp_windows_ec2_instances(c, config) as instances:
1259 if disable_antivirus:
1271 if disable_antivirus:
1260 for instance in instances:
1272 for instance in instances:
1261 run_powershell(
1273 run_powershell(
1262 instance.winrm_client,
1274 instance.winrm_client,
1263 'Set-MpPreference -DisableRealtimeMonitoring $true',
1275 'Set-MpPreference -DisableRealtimeMonitoring $true',
1264 )
1276 )
1265
1277
1266 yield instances
1278 yield instances
@@ -1,595 +1,595 b''
1 # linux.py - Linux specific automation functionality
1 # linux.py - Linux specific automation functionality
2 #
2 #
3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 # no-check-code because Python 3 native.
8 # no-check-code because Python 3 native.
9
9
10 import os
10 import os
11 import pathlib
11 import pathlib
12 import shlex
12 import shlex
13 import subprocess
13 import subprocess
14 import tempfile
14 import tempfile
15
15
16 from .ssh import exec_command
16 from .ssh import exec_command
17
17
18
18
19 # Linux distributions that are supported.
19 # Linux distributions that are supported.
20 DISTROS = {
20 DISTROS = {
21 'debian9',
21 'debian9',
22 'debian10',
22 'debian10',
23 'ubuntu18.04',
23 'ubuntu18.04',
24 'ubuntu19.04',
24 'ubuntu19.04',
25 }
25 }
26
26
27 INSTALL_PYTHONS = r'''
27 INSTALL_PYTHONS = r'''
28 PYENV2_VERSIONS="2.7.17 pypy2.7-7.2.0"
28 PYENV2_VERSIONS="2.7.17 pypy2.7-7.2.0"
29 PYENV3_VERSIONS="3.5.7 3.6.9 3.7.5 3.8.0 pypy3.5-7.0.0 pypy3.6-7.2.0"
29 PYENV3_VERSIONS="3.5.9 3.6.10 3.7.7 3.8.2 pypy3.5-7.0.0 pypy3.6-7.3.0"
30
30
31 git clone https://github.com/pyenv/pyenv.git /hgdev/pyenv
31 git clone https://github.com/pyenv/pyenv.git /hgdev/pyenv
32 pushd /hgdev/pyenv
32 pushd /hgdev/pyenv
33 git checkout 0e7cfc3b3d4eca46ad83d632e1505f5932cd179b
33 git checkout 3005c4664372ae13fbe376be699313eb428c8bdd
34 popd
34 popd
35
35
36 export PYENV_ROOT="/hgdev/pyenv"
36 export PYENV_ROOT="/hgdev/pyenv"
37 export PATH="$PYENV_ROOT/bin:$PATH"
37 export PATH="$PYENV_ROOT/bin:$PATH"
38
38
39 # pip 19.2.3.
39 # pip 19.2.3.
40 PIP_SHA256=57e3643ff19f018f8a00dfaa6b7e4620e3c1a7a2171fd218425366ec006b3bfe
40 PIP_SHA256=57e3643ff19f018f8a00dfaa6b7e4620e3c1a7a2171fd218425366ec006b3bfe
41 wget -O get-pip.py --progress dot:mega https://github.com/pypa/get-pip/raw/309a56c5fd94bd1134053a541cb4657a4e47e09d/get-pip.py
41 wget -O get-pip.py --progress dot:mega https://github.com/pypa/get-pip/raw/309a56c5fd94bd1134053a541cb4657a4e47e09d/get-pip.py
42 echo "${PIP_SHA256} get-pip.py" | sha256sum --check -
42 echo "${PIP_SHA256} get-pip.py" | sha256sum --check -
43
43
44 VIRTUALENV_SHA256=f78d81b62d3147396ac33fc9d77579ddc42cc2a98dd9ea38886f616b33bc7fb2
44 VIRTUALENV_SHA256=f78d81b62d3147396ac33fc9d77579ddc42cc2a98dd9ea38886f616b33bc7fb2
45 VIRTUALENV_TARBALL=virtualenv-16.7.5.tar.gz
45 VIRTUALENV_TARBALL=virtualenv-16.7.5.tar.gz
46 wget -O ${VIRTUALENV_TARBALL} --progress dot:mega https://files.pythonhosted.org/packages/66/f0/6867af06d2e2f511e4e1d7094ff663acdebc4f15d4a0cb0fed1007395124/${VIRTUALENV_TARBALL}
46 wget -O ${VIRTUALENV_TARBALL} --progress dot:mega https://files.pythonhosted.org/packages/66/f0/6867af06d2e2f511e4e1d7094ff663acdebc4f15d4a0cb0fed1007395124/${VIRTUALENV_TARBALL}
47 echo "${VIRTUALENV_SHA256} ${VIRTUALENV_TARBALL}" | sha256sum --check -
47 echo "${VIRTUALENV_SHA256} ${VIRTUALENV_TARBALL}" | sha256sum --check -
48
48
49 for v in ${PYENV2_VERSIONS}; do
49 for v in ${PYENV2_VERSIONS}; do
50 pyenv install -v ${v}
50 pyenv install -v ${v}
51 ${PYENV_ROOT}/versions/${v}/bin/python get-pip.py
51 ${PYENV_ROOT}/versions/${v}/bin/python get-pip.py
52 ${PYENV_ROOT}/versions/${v}/bin/pip install ${VIRTUALENV_TARBALL}
52 ${PYENV_ROOT}/versions/${v}/bin/pip install ${VIRTUALENV_TARBALL}
53 ${PYENV_ROOT}/versions/${v}/bin/pip install -r /hgdev/requirements-py2.txt
53 ${PYENV_ROOT}/versions/${v}/bin/pip install -r /hgdev/requirements-py2.txt
54 done
54 done
55
55
56 for v in ${PYENV3_VERSIONS}; do
56 for v in ${PYENV3_VERSIONS}; do
57 pyenv install -v ${v}
57 pyenv install -v ${v}
58 ${PYENV_ROOT}/versions/${v}/bin/python get-pip.py
58 ${PYENV_ROOT}/versions/${v}/bin/python get-pip.py
59 ${PYENV_ROOT}/versions/${v}/bin/pip install -r /hgdev/requirements-py3.txt
59 ${PYENV_ROOT}/versions/${v}/bin/pip install -r /hgdev/requirements-py3.txt
60 done
60 done
61
61
62 pyenv global ${PYENV2_VERSIONS} ${PYENV3_VERSIONS} system
62 pyenv global ${PYENV2_VERSIONS} ${PYENV3_VERSIONS} system
63 '''.lstrip().replace(
63 '''.lstrip().replace(
64 '\r\n', '\n'
64 '\r\n', '\n'
65 )
65 )
66
66
67
67
68 INSTALL_RUST = r'''
68 INSTALL_RUST = r'''
69 RUSTUP_INIT_SHA256=a46fe67199b7bcbbde2dcbc23ae08db6f29883e260e23899a88b9073effc9076
69 RUSTUP_INIT_SHA256=a46fe67199b7bcbbde2dcbc23ae08db6f29883e260e23899a88b9073effc9076
70 wget -O rustup-init --progress dot:mega https://static.rust-lang.org/rustup/archive/1.18.3/x86_64-unknown-linux-gnu/rustup-init
70 wget -O rustup-init --progress dot:mega https://static.rust-lang.org/rustup/archive/1.18.3/x86_64-unknown-linux-gnu/rustup-init
71 echo "${RUSTUP_INIT_SHA256} rustup-init" | sha256sum --check -
71 echo "${RUSTUP_INIT_SHA256} rustup-init" | sha256sum --check -
72
72
73 chmod +x rustup-init
73 chmod +x rustup-init
74 sudo -H -u hg -g hg ./rustup-init -y
74 sudo -H -u hg -g hg ./rustup-init -y
75 sudo -H -u hg -g hg /home/hg/.cargo/bin/rustup install 1.31.1 1.34.2
75 sudo -H -u hg -g hg /home/hg/.cargo/bin/rustup install 1.31.1 1.34.2
76 sudo -H -u hg -g hg /home/hg/.cargo/bin/rustup component add clippy
76 sudo -H -u hg -g hg /home/hg/.cargo/bin/rustup component add clippy
77 '''
77 '''
78
78
79
79
80 BOOTSTRAP_VIRTUALENV = r'''
80 BOOTSTRAP_VIRTUALENV = r'''
81 /usr/bin/virtualenv /hgdev/venv-bootstrap
81 /usr/bin/virtualenv /hgdev/venv-bootstrap
82
82
83 HG_SHA256=35fc8ba5e0379c1b3affa2757e83fb0509e8ac314cbd9f1fd133cf265d16e49f
83 HG_SHA256=35fc8ba5e0379c1b3affa2757e83fb0509e8ac314cbd9f1fd133cf265d16e49f
84 HG_TARBALL=mercurial-5.1.1.tar.gz
84 HG_TARBALL=mercurial-5.1.1.tar.gz
85
85
86 wget -O ${HG_TARBALL} --progress dot:mega https://www.mercurial-scm.org/release/${HG_TARBALL}
86 wget -O ${HG_TARBALL} --progress dot:mega https://www.mercurial-scm.org/release/${HG_TARBALL}
87 echo "${HG_SHA256} ${HG_TARBALL}" | sha256sum --check -
87 echo "${HG_SHA256} ${HG_TARBALL}" | sha256sum --check -
88
88
89 /hgdev/venv-bootstrap/bin/pip install ${HG_TARBALL}
89 /hgdev/venv-bootstrap/bin/pip install ${HG_TARBALL}
90 '''.lstrip().replace(
90 '''.lstrip().replace(
91 '\r\n', '\n'
91 '\r\n', '\n'
92 )
92 )
93
93
94
94
95 BOOTSTRAP_DEBIAN = (
95 BOOTSTRAP_DEBIAN = (
96 r'''
96 r'''
97 #!/bin/bash
97 #!/bin/bash
98
98
99 set -ex
99 set -ex
100
100
101 DISTRO=`grep DISTRIB_ID /etc/lsb-release | awk -F= '{{print $2}}'`
101 DISTRO=`grep DISTRIB_ID /etc/lsb-release | awk -F= '{{print $2}}'`
102 DEBIAN_VERSION=`cat /etc/debian_version`
102 DEBIAN_VERSION=`cat /etc/debian_version`
103 LSB_RELEASE=`lsb_release -cs`
103 LSB_RELEASE=`lsb_release -cs`
104
104
105 sudo /usr/sbin/groupadd hg
105 sudo /usr/sbin/groupadd hg
106 sudo /usr/sbin/groupadd docker
106 sudo /usr/sbin/groupadd docker
107 sudo /usr/sbin/useradd -g hg -G sudo,docker -d /home/hg -m -s /bin/bash hg
107 sudo /usr/sbin/useradd -g hg -G sudo,docker -d /home/hg -m -s /bin/bash hg
108 sudo mkdir /home/hg/.ssh
108 sudo mkdir /home/hg/.ssh
109 sudo cp ~/.ssh/authorized_keys /home/hg/.ssh/authorized_keys
109 sudo cp ~/.ssh/authorized_keys /home/hg/.ssh/authorized_keys
110 sudo chown -R hg:hg /home/hg/.ssh
110 sudo chown -R hg:hg /home/hg/.ssh
111 sudo chmod 700 /home/hg/.ssh
111 sudo chmod 700 /home/hg/.ssh
112 sudo chmod 600 /home/hg/.ssh/authorized_keys
112 sudo chmod 600 /home/hg/.ssh/authorized_keys
113
113
114 cat << EOF | sudo tee /etc/sudoers.d/90-hg
114 cat << EOF | sudo tee /etc/sudoers.d/90-hg
115 hg ALL=(ALL) NOPASSWD:ALL
115 hg ALL=(ALL) NOPASSWD:ALL
116 EOF
116 EOF
117
117
118 sudo apt-get update
118 sudo apt-get update
119 sudo DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade
119 sudo DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade
120
120
121 # Install packages necessary to set up Docker Apt repo.
121 # Install packages necessary to set up Docker Apt repo.
122 sudo DEBIAN_FRONTEND=noninteractive apt-get -yq install --no-install-recommends \
122 sudo DEBIAN_FRONTEND=noninteractive apt-get -yq install --no-install-recommends \
123 apt-transport-https \
123 apt-transport-https \
124 gnupg
124 gnupg
125
125
126 cat > docker-apt-key << EOF
126 cat > docker-apt-key << EOF
127 -----BEGIN PGP PUBLIC KEY BLOCK-----
127 -----BEGIN PGP PUBLIC KEY BLOCK-----
128
128
129 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
186 YT90qFF93M3v01BbxP+EIY2/9tiIPbrd
186 YT90qFF93M3v01BbxP+EIY2/9tiIPbrd
187 =0YYh
187 =0YYh
188 -----END PGP PUBLIC KEY BLOCK-----
188 -----END PGP PUBLIC KEY BLOCK-----
189 EOF
189 EOF
190
190
191 sudo apt-key add docker-apt-key
191 sudo apt-key add docker-apt-key
192
192
193 if [ "$LSB_RELEASE" = "stretch" ]; then
193 if [ "$LSB_RELEASE" = "stretch" ]; then
194 cat << EOF | sudo tee -a /etc/apt/sources.list
194 cat << EOF | sudo tee -a /etc/apt/sources.list
195 # Need backports for clang-format-6.0
195 # Need backports for clang-format-6.0
196 deb http://deb.debian.org/debian stretch-backports main
196 deb http://deb.debian.org/debian stretch-backports main
197 EOF
197 EOF
198 fi
198 fi
199
199
200 if [ "$LSB_RELEASE" = "stretch" -o "$LSB_RELEASE" = "buster" ]; then
200 if [ "$LSB_RELEASE" = "stretch" -o "$LSB_RELEASE" = "buster" ]; then
201 cat << EOF | sudo tee -a /etc/apt/sources.list
201 cat << EOF | sudo tee -a /etc/apt/sources.list
202 # Sources are useful if we want to compile things locally.
202 # Sources are useful if we want to compile things locally.
203 deb-src http://deb.debian.org/debian $LSB_RELEASE main
203 deb-src http://deb.debian.org/debian $LSB_RELEASE main
204 deb-src http://security.debian.org/debian-security $LSB_RELEASE/updates main
204 deb-src http://security.debian.org/debian-security $LSB_RELEASE/updates main
205 deb-src http://deb.debian.org/debian $LSB_RELEASE-updates main
205 deb-src http://deb.debian.org/debian $LSB_RELEASE-updates main
206 deb-src http://deb.debian.org/debian $LSB_RELEASE-backports main
206 deb-src http://deb.debian.org/debian $LSB_RELEASE-backports main
207
207
208 deb [arch=amd64] https://download.docker.com/linux/debian $LSB_RELEASE stable
208 deb [arch=amd64] https://download.docker.com/linux/debian $LSB_RELEASE stable
209 EOF
209 EOF
210
210
211 elif [ "$DISTRO" = "Ubuntu" ]; then
211 elif [ "$DISTRO" = "Ubuntu" ]; then
212 cat << EOF | sudo tee -a /etc/apt/sources.list
212 cat << EOF | sudo tee -a /etc/apt/sources.list
213 deb [arch=amd64] https://download.docker.com/linux/ubuntu $LSB_RELEASE stable
213 deb [arch=amd64] https://download.docker.com/linux/ubuntu $LSB_RELEASE stable
214 EOF
214 EOF
215
215
216 fi
216 fi
217
217
218 sudo apt-get update
218 sudo apt-get update
219
219
220 PACKAGES="\
220 PACKAGES="\
221 awscli \
221 awscli \
222 btrfs-progs \
222 btrfs-progs \
223 build-essential \
223 build-essential \
224 bzr \
224 bzr \
225 clang-format-6.0 \
225 clang-format-6.0 \
226 cvs \
226 cvs \
227 darcs \
227 darcs \
228 debhelper \
228 debhelper \
229 devscripts \
229 devscripts \
230 docker-ce \
230 docker-ce \
231 dpkg-dev \
231 dpkg-dev \
232 dstat \
232 dstat \
233 emacs \
233 emacs \
234 gettext \
234 gettext \
235 git \
235 git \
236 htop \
236 htop \
237 iotop \
237 iotop \
238 jfsutils \
238 jfsutils \
239 libbz2-dev \
239 libbz2-dev \
240 libexpat1-dev \
240 libexpat1-dev \
241 libffi-dev \
241 libffi-dev \
242 libgdbm-dev \
242 libgdbm-dev \
243 liblzma-dev \
243 liblzma-dev \
244 libncurses5-dev \
244 libncurses5-dev \
245 libnss3-dev \
245 libnss3-dev \
246 libreadline-dev \
246 libreadline-dev \
247 libsqlite3-dev \
247 libsqlite3-dev \
248 libssl-dev \
248 libssl-dev \
249 netbase \
249 netbase \
250 ntfs-3g \
250 ntfs-3g \
251 nvme-cli \
251 nvme-cli \
252 pyflakes \
252 pyflakes \
253 pyflakes3 \
253 pyflakes3 \
254 pylint \
254 pylint \
255 pylint3 \
255 pylint3 \
256 python-all-dev \
256 python-all-dev \
257 python-dev \
257 python-dev \
258 python-docutils \
258 python-docutils \
259 python-fuzzywuzzy \
259 python-fuzzywuzzy \
260 python-pygments \
260 python-pygments \
261 python-subversion \
261 python-subversion \
262 python-vcr \
262 python-vcr \
263 python3-boto3 \
263 python3-boto3 \
264 python3-dev \
264 python3-dev \
265 python3-docutils \
265 python3-docutils \
266 python3-fuzzywuzzy \
266 python3-fuzzywuzzy \
267 python3-pygments \
267 python3-pygments \
268 python3-vcr \
268 python3-vcr \
269 python3-venv \
269 python3-venv \
270 rsync \
270 rsync \
271 sqlite3 \
271 sqlite3 \
272 subversion \
272 subversion \
273 tcl-dev \
273 tcl-dev \
274 tk-dev \
274 tk-dev \
275 tla \
275 tla \
276 unzip \
276 unzip \
277 uuid-dev \
277 uuid-dev \
278 vim \
278 vim \
279 virtualenv \
279 virtualenv \
280 wget \
280 wget \
281 xfsprogs \
281 xfsprogs \
282 zip \
282 zip \
283 zlib1g-dev"
283 zlib1g-dev"
284
284
285 if [ "LSB_RELEASE" = "stretch" ]; then
285 if [ "LSB_RELEASE" = "stretch" ]; then
286 PACKAGES="$PACKAGES linux-perf"
286 PACKAGES="$PACKAGES linux-perf"
287 elif [ "$DISTRO" = "Ubuntu" ]; then
287 elif [ "$DISTRO" = "Ubuntu" ]; then
288 PACKAGES="$PACKAGES linux-tools-common"
288 PACKAGES="$PACKAGES linux-tools-common"
289 fi
289 fi
290
290
291 # Monotone only available in older releases.
291 # Monotone only available in older releases.
292 if [ "$LSB_RELEASE" = "stretch" -o "$LSB_RELEASE" = "xenial" ]; then
292 if [ "$LSB_RELEASE" = "stretch" -o "$LSB_RELEASE" = "xenial" ]; then
293 PACKAGES="$PACKAGES monotone"
293 PACKAGES="$PACKAGES monotone"
294 fi
294 fi
295
295
296 sudo DEBIAN_FRONTEND=noninteractive apt-get -yq install --no-install-recommends $PACKAGES
296 sudo DEBIAN_FRONTEND=noninteractive apt-get -yq install --no-install-recommends $PACKAGES
297
297
298 # Create clang-format symlink so test harness finds it.
298 # Create clang-format symlink so test harness finds it.
299 sudo update-alternatives --install /usr/bin/clang-format clang-format \
299 sudo update-alternatives --install /usr/bin/clang-format clang-format \
300 /usr/bin/clang-format-6.0 1000
300 /usr/bin/clang-format-6.0 1000
301
301
302 sudo mkdir /hgdev
302 sudo mkdir /hgdev
303 # Will be normalized to hg:hg later.
303 # Will be normalized to hg:hg later.
304 sudo chown `whoami` /hgdev
304 sudo chown `whoami` /hgdev
305
305
306 {install_rust}
306 {install_rust}
307
307
308 cp requirements-py2.txt /hgdev/requirements-py2.txt
308 cp requirements-py2.txt /hgdev/requirements-py2.txt
309 cp requirements-py3.txt /hgdev/requirements-py3.txt
309 cp requirements-py3.txt /hgdev/requirements-py3.txt
310
310
311 # Disable the pip version check because it uses the network and can
311 # Disable the pip version check because it uses the network and can
312 # be annoying.
312 # be annoying.
313 cat << EOF | sudo tee -a /etc/pip.conf
313 cat << EOF | sudo tee -a /etc/pip.conf
314 [global]
314 [global]
315 disable-pip-version-check = True
315 disable-pip-version-check = True
316 EOF
316 EOF
317
317
318 {install_pythons}
318 {install_pythons}
319 {bootstrap_virtualenv}
319 {bootstrap_virtualenv}
320
320
321 /hgdev/venv-bootstrap/bin/hg clone https://www.mercurial-scm.org/repo/hg /hgdev/src
321 /hgdev/venv-bootstrap/bin/hg clone https://www.mercurial-scm.org/repo/hg /hgdev/src
322
322
323 # Mark the repo as non-publishing.
323 # Mark the repo as non-publishing.
324 cat >> /hgdev/src/.hg/hgrc << EOF
324 cat >> /hgdev/src/.hg/hgrc << EOF
325 [phases]
325 [phases]
326 publish = false
326 publish = false
327 EOF
327 EOF
328
328
329 sudo chown -R hg:hg /hgdev
329 sudo chown -R hg:hg /hgdev
330 '''.lstrip()
330 '''.lstrip()
331 .format(
331 .format(
332 install_rust=INSTALL_RUST,
332 install_rust=INSTALL_RUST,
333 install_pythons=INSTALL_PYTHONS,
333 install_pythons=INSTALL_PYTHONS,
334 bootstrap_virtualenv=BOOTSTRAP_VIRTUALENV,
334 bootstrap_virtualenv=BOOTSTRAP_VIRTUALENV,
335 )
335 )
336 .replace('\r\n', '\n')
336 .replace('\r\n', '\n')
337 )
337 )
338
338
339
339
340 # Prepares /hgdev for operations.
340 # Prepares /hgdev for operations.
341 PREPARE_HGDEV = '''
341 PREPARE_HGDEV = '''
342 #!/bin/bash
342 #!/bin/bash
343
343
344 set -e
344 set -e
345
345
346 FS=$1
346 FS=$1
347
347
348 ensure_device() {
348 ensure_device() {
349 if [ -z "${DEVICE}" ]; then
349 if [ -z "${DEVICE}" ]; then
350 echo "could not find block device to format"
350 echo "could not find block device to format"
351 exit 1
351 exit 1
352 fi
352 fi
353 }
353 }
354
354
355 # Determine device to partition for extra filesystem.
355 # Determine device to partition for extra filesystem.
356 # If only 1 volume is present, it will be the root volume and
356 # If only 1 volume is present, it will be the root volume and
357 # should be /dev/nvme0. If multiple volumes are present, the
357 # should be /dev/nvme0. If multiple volumes are present, the
358 # root volume could be nvme0 or nvme1. Use whichever one doesn't have
358 # root volume could be nvme0 or nvme1. Use whichever one doesn't have
359 # a partition.
359 # a partition.
360 if [ -e /dev/nvme1n1 ]; then
360 if [ -e /dev/nvme1n1 ]; then
361 if [ -e /dev/nvme0n1p1 ]; then
361 if [ -e /dev/nvme0n1p1 ]; then
362 DEVICE=/dev/nvme1n1
362 DEVICE=/dev/nvme1n1
363 else
363 else
364 DEVICE=/dev/nvme0n1
364 DEVICE=/dev/nvme0n1
365 fi
365 fi
366 else
366 else
367 DEVICE=
367 DEVICE=
368 fi
368 fi
369
369
370 sudo mkdir /hgwork
370 sudo mkdir /hgwork
371
371
372 if [ "${FS}" != "default" -a "${FS}" != "tmpfs" ]; then
372 if [ "${FS}" != "default" -a "${FS}" != "tmpfs" ]; then
373 ensure_device
373 ensure_device
374 echo "creating ${FS} filesystem on ${DEVICE}"
374 echo "creating ${FS} filesystem on ${DEVICE}"
375 fi
375 fi
376
376
377 if [ "${FS}" = "default" ]; then
377 if [ "${FS}" = "default" ]; then
378 :
378 :
379
379
380 elif [ "${FS}" = "btrfs" ]; then
380 elif [ "${FS}" = "btrfs" ]; then
381 sudo mkfs.btrfs ${DEVICE}
381 sudo mkfs.btrfs ${DEVICE}
382 sudo mount ${DEVICE} /hgwork
382 sudo mount ${DEVICE} /hgwork
383
383
384 elif [ "${FS}" = "ext3" ]; then
384 elif [ "${FS}" = "ext3" ]; then
385 # lazy_journal_init speeds up filesystem creation at the expense of
385 # lazy_journal_init speeds up filesystem creation at the expense of
386 # integrity if things crash. We are an ephemeral instance, so we don't
386 # integrity if things crash. We are an ephemeral instance, so we don't
387 # care about integrity.
387 # care about integrity.
388 sudo mkfs.ext3 -E lazy_journal_init=1 ${DEVICE}
388 sudo mkfs.ext3 -E lazy_journal_init=1 ${DEVICE}
389 sudo mount ${DEVICE} /hgwork
389 sudo mount ${DEVICE} /hgwork
390
390
391 elif [ "${FS}" = "ext4" ]; then
391 elif [ "${FS}" = "ext4" ]; then
392 sudo mkfs.ext4 -E lazy_journal_init=1 ${DEVICE}
392 sudo mkfs.ext4 -E lazy_journal_init=1 ${DEVICE}
393 sudo mount ${DEVICE} /hgwork
393 sudo mount ${DEVICE} /hgwork
394
394
395 elif [ "${FS}" = "jfs" ]; then
395 elif [ "${FS}" = "jfs" ]; then
396 sudo mkfs.jfs ${DEVICE}
396 sudo mkfs.jfs ${DEVICE}
397 sudo mount ${DEVICE} /hgwork
397 sudo mount ${DEVICE} /hgwork
398
398
399 elif [ "${FS}" = "tmpfs" ]; then
399 elif [ "${FS}" = "tmpfs" ]; then
400 echo "creating tmpfs volume in /hgwork"
400 echo "creating tmpfs volume in /hgwork"
401 sudo mount -t tmpfs -o size=1024M tmpfs /hgwork
401 sudo mount -t tmpfs -o size=1024M tmpfs /hgwork
402
402
403 elif [ "${FS}" = "xfs" ]; then
403 elif [ "${FS}" = "xfs" ]; then
404 sudo mkfs.xfs ${DEVICE}
404 sudo mkfs.xfs ${DEVICE}
405 sudo mount ${DEVICE} /hgwork
405 sudo mount ${DEVICE} /hgwork
406
406
407 else
407 else
408 echo "unsupported filesystem: ${FS}"
408 echo "unsupported filesystem: ${FS}"
409 exit 1
409 exit 1
410 fi
410 fi
411
411
412 echo "/hgwork ready"
412 echo "/hgwork ready"
413
413
414 sudo chown hg:hg /hgwork
414 sudo chown hg:hg /hgwork
415 mkdir /hgwork/tmp
415 mkdir /hgwork/tmp
416 chown hg:hg /hgwork/tmp
416 chown hg:hg /hgwork/tmp
417
417
418 rsync -a /hgdev/src /hgwork/
418 rsync -a /hgdev/src /hgwork/
419 '''.lstrip().replace(
419 '''.lstrip().replace(
420 '\r\n', '\n'
420 '\r\n', '\n'
421 )
421 )
422
422
423
423
424 HG_UPDATE_CLEAN = '''
424 HG_UPDATE_CLEAN = '''
425 set -ex
425 set -ex
426
426
427 HG=/hgdev/venv-bootstrap/bin/hg
427 HG=/hgdev/venv-bootstrap/bin/hg
428
428
429 cd /hgwork/src
429 cd /hgwork/src
430 ${HG} --config extensions.purge= purge --all
430 ${HG} --config extensions.purge= purge --all
431 ${HG} update -C $1
431 ${HG} update -C $1
432 ${HG} log -r .
432 ${HG} log -r .
433 '''.lstrip().replace(
433 '''.lstrip().replace(
434 '\r\n', '\n'
434 '\r\n', '\n'
435 )
435 )
436
436
437
437
438 def prepare_exec_environment(ssh_client, filesystem='default'):
438 def prepare_exec_environment(ssh_client, filesystem='default'):
439 """Prepare an EC2 instance to execute things.
439 """Prepare an EC2 instance to execute things.
440
440
441 The AMI has an ``/hgdev`` bootstrapped with various Python installs
441 The AMI has an ``/hgdev`` bootstrapped with various Python installs
442 and a clone of the Mercurial repo.
442 and a clone of the Mercurial repo.
443
443
444 In EC2, EBS volumes launched from snapshots have wonky performance behavior.
444 In EC2, EBS volumes launched from snapshots have wonky performance behavior.
445 Notably, blocks have to be copied on first access, which makes volume
445 Notably, blocks have to be copied on first access, which makes volume
446 I/O extremely slow on fresh volumes.
446 I/O extremely slow on fresh volumes.
447
447
448 Furthermore, we may want to run operations, tests, etc on alternative
448 Furthermore, we may want to run operations, tests, etc on alternative
449 filesystems so we examine behavior on different filesystems.
449 filesystems so we examine behavior on different filesystems.
450
450
451 This function is used to facilitate executing operations on alternate
451 This function is used to facilitate executing operations on alternate
452 volumes.
452 volumes.
453 """
453 """
454 sftp = ssh_client.open_sftp()
454 sftp = ssh_client.open_sftp()
455
455
456 with sftp.open('/hgdev/prepare-hgdev', 'wb') as fh:
456 with sftp.open('/hgdev/prepare-hgdev', 'wb') as fh:
457 fh.write(PREPARE_HGDEV)
457 fh.write(PREPARE_HGDEV)
458 fh.chmod(0o0777)
458 fh.chmod(0o0777)
459
459
460 command = 'sudo /hgdev/prepare-hgdev %s' % filesystem
460 command = 'sudo /hgdev/prepare-hgdev %s' % filesystem
461 chan, stdin, stdout = exec_command(ssh_client, command)
461 chan, stdin, stdout = exec_command(ssh_client, command)
462 stdin.close()
462 stdin.close()
463
463
464 for line in stdout:
464 for line in stdout:
465 print(line, end='')
465 print(line, end='')
466
466
467 res = chan.recv_exit_status()
467 res = chan.recv_exit_status()
468
468
469 if res:
469 if res:
470 raise Exception('non-0 exit code updating working directory; %d' % res)
470 raise Exception('non-0 exit code updating working directory; %d' % res)
471
471
472
472
473 def synchronize_hg(
473 def synchronize_hg(
474 source_path: pathlib.Path, ec2_instance, revision: str = None
474 source_path: pathlib.Path, ec2_instance, revision: str = None
475 ):
475 ):
476 """Synchronize a local Mercurial source path to remote EC2 instance."""
476 """Synchronize a local Mercurial source path to remote EC2 instance."""
477
477
478 with tempfile.TemporaryDirectory() as temp_dir:
478 with tempfile.TemporaryDirectory() as temp_dir:
479 temp_dir = pathlib.Path(temp_dir)
479 temp_dir = pathlib.Path(temp_dir)
480
480
481 ssh_dir = temp_dir / '.ssh'
481 ssh_dir = temp_dir / '.ssh'
482 ssh_dir.mkdir()
482 ssh_dir.mkdir()
483 ssh_dir.chmod(0o0700)
483 ssh_dir.chmod(0o0700)
484
484
485 public_ip = ec2_instance.public_ip_address
485 public_ip = ec2_instance.public_ip_address
486
486
487 ssh_config = ssh_dir / 'config'
487 ssh_config = ssh_dir / 'config'
488
488
489 with ssh_config.open('w', encoding='utf-8') as fh:
489 with ssh_config.open('w', encoding='utf-8') as fh:
490 fh.write('Host %s\n' % public_ip)
490 fh.write('Host %s\n' % public_ip)
491 fh.write(' User hg\n')
491 fh.write(' User hg\n')
492 fh.write(' StrictHostKeyChecking no\n')
492 fh.write(' StrictHostKeyChecking no\n')
493 fh.write(' UserKnownHostsFile %s\n' % (ssh_dir / 'known_hosts'))
493 fh.write(' UserKnownHostsFile %s\n' % (ssh_dir / 'known_hosts'))
494 fh.write(' IdentityFile %s\n' % ec2_instance.ssh_private_key_path)
494 fh.write(' IdentityFile %s\n' % ec2_instance.ssh_private_key_path)
495
495
496 if not (source_path / '.hg').is_dir():
496 if not (source_path / '.hg').is_dir():
497 raise Exception(
497 raise Exception(
498 '%s is not a Mercurial repository; synchronization '
498 '%s is not a Mercurial repository; synchronization '
499 'not yet supported' % source_path
499 'not yet supported' % source_path
500 )
500 )
501
501
502 env = dict(os.environ)
502 env = dict(os.environ)
503 env['HGPLAIN'] = '1'
503 env['HGPLAIN'] = '1'
504 env['HGENCODING'] = 'utf-8'
504 env['HGENCODING'] = 'utf-8'
505
505
506 hg_bin = source_path / 'hg'
506 hg_bin = source_path / 'hg'
507
507
508 res = subprocess.run(
508 res = subprocess.run(
509 ['python2.7', str(hg_bin), 'log', '-r', revision, '-T', '{node}'],
509 ['python2.7', str(hg_bin), 'log', '-r', revision, '-T', '{node}'],
510 cwd=str(source_path),
510 cwd=str(source_path),
511 env=env,
511 env=env,
512 check=True,
512 check=True,
513 capture_output=True,
513 capture_output=True,
514 )
514 )
515
515
516 full_revision = res.stdout.decode('ascii')
516 full_revision = res.stdout.decode('ascii')
517
517
518 args = [
518 args = [
519 'python2.7',
519 'python2.7',
520 str(hg_bin),
520 str(hg_bin),
521 '--config',
521 '--config',
522 'ui.ssh=ssh -F %s' % ssh_config,
522 'ui.ssh=ssh -F %s' % ssh_config,
523 '--config',
523 '--config',
524 'ui.remotecmd=/hgdev/venv-bootstrap/bin/hg',
524 'ui.remotecmd=/hgdev/venv-bootstrap/bin/hg',
525 # Also ensure .hgtags changes are present so auto version
525 # Also ensure .hgtags changes are present so auto version
526 # calculation works.
526 # calculation works.
527 'push',
527 'push',
528 '-f',
528 '-f',
529 '-r',
529 '-r',
530 full_revision,
530 full_revision,
531 '-r',
531 '-r',
532 'file(.hgtags)',
532 'file(.hgtags)',
533 'ssh://%s//hgwork/src' % public_ip,
533 'ssh://%s//hgwork/src' % public_ip,
534 ]
534 ]
535
535
536 res = subprocess.run(args, cwd=str(source_path), env=env)
536 res = subprocess.run(args, cwd=str(source_path), env=env)
537
537
538 # Allow 1 (no-op) to not trigger error.
538 # Allow 1 (no-op) to not trigger error.
539 if res.returncode not in (0, 1):
539 if res.returncode not in (0, 1):
540 res.check_returncode()
540 res.check_returncode()
541
541
542 # TODO support synchronizing dirty working directory.
542 # TODO support synchronizing dirty working directory.
543
543
544 sftp = ec2_instance.ssh_client.open_sftp()
544 sftp = ec2_instance.ssh_client.open_sftp()
545
545
546 with sftp.open('/hgdev/hgup', 'wb') as fh:
546 with sftp.open('/hgdev/hgup', 'wb') as fh:
547 fh.write(HG_UPDATE_CLEAN)
547 fh.write(HG_UPDATE_CLEAN)
548 fh.chmod(0o0700)
548 fh.chmod(0o0700)
549
549
550 chan, stdin, stdout = exec_command(
550 chan, stdin, stdout = exec_command(
551 ec2_instance.ssh_client, '/hgdev/hgup %s' % full_revision
551 ec2_instance.ssh_client, '/hgdev/hgup %s' % full_revision
552 )
552 )
553 stdin.close()
553 stdin.close()
554
554
555 for line in stdout:
555 for line in stdout:
556 print(line, end='')
556 print(line, end='')
557
557
558 res = chan.recv_exit_status()
558 res = chan.recv_exit_status()
559
559
560 if res:
560 if res:
561 raise Exception(
561 raise Exception(
562 'non-0 exit code updating working directory; %d' % res
562 'non-0 exit code updating working directory; %d' % res
563 )
563 )
564
564
565
565
566 def run_tests(ssh_client, python_version, test_flags=None):
566 def run_tests(ssh_client, python_version, test_flags=None):
567 """Run tests on a remote Linux machine via an SSH client."""
567 """Run tests on a remote Linux machine via an SSH client."""
568 test_flags = test_flags or []
568 test_flags = test_flags or []
569
569
570 print('running tests')
570 print('running tests')
571
571
572 if python_version == 'system2':
572 if python_version == 'system2':
573 python = '/usr/bin/python2'
573 python = '/usr/bin/python2'
574 elif python_version == 'system3':
574 elif python_version == 'system3':
575 python = '/usr/bin/python3'
575 python = '/usr/bin/python3'
576 elif python_version.startswith('pypy'):
576 elif python_version.startswith('pypy'):
577 python = '/hgdev/pyenv/shims/%s' % python_version
577 python = '/hgdev/pyenv/shims/%s' % python_version
578 else:
578 else:
579 python = '/hgdev/pyenv/shims/python%s' % python_version
579 python = '/hgdev/pyenv/shims/python%s' % python_version
580
580
581 test_flags = ' '.join(shlex.quote(a) for a in test_flags)
581 test_flags = ' '.join(shlex.quote(a) for a in test_flags)
582
582
583 command = (
583 command = (
584 '/bin/sh -c "export TMPDIR=/hgwork/tmp; '
584 '/bin/sh -c "export TMPDIR=/hgwork/tmp; '
585 'cd /hgwork/src/tests && %s run-tests.py %s"' % (python, test_flags)
585 'cd /hgwork/src/tests && %s run-tests.py %s"' % (python, test_flags)
586 )
586 )
587
587
588 chan, stdin, stdout = exec_command(ssh_client, command)
588 chan, stdin, stdout = exec_command(ssh_client, command)
589
589
590 stdin.close()
590 stdin.close()
591
591
592 for line in stdout:
592 for line in stdout:
593 print(line, end='')
593 print(line, end='')
594
594
595 return chan.recv_exit_status()
595 return chan.recv_exit_status()
@@ -1,200 +1,200 b''
1 # install-dependencies.ps1 - Install Windows dependencies for building Mercurial
1 # install-dependencies.ps1 - Install Windows dependencies for building Mercurial
2 #
2 #
3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 # This script can be used to bootstrap a Mercurial build environment on
8 # This script can be used to bootstrap a Mercurial build environment on
9 # Windows.
9 # Windows.
10 #
10 #
11 # The script makes a lot of assumptions about how things should work.
11 # The script makes a lot of assumptions about how things should work.
12 # For example, the install location of Python is hardcoded to c:\hgdev\*.
12 # For example, the install location of Python is hardcoded to c:\hgdev\*.
13 #
13 #
14 # The script should be executed from a PowerShell with elevated privileges
14 # The script should be executed from a PowerShell with elevated privileges
15 # if you don't want to see a UAC prompt for various installers.
15 # if you don't want to see a UAC prompt for various installers.
16 #
16 #
17 # The script is tested on Windows 10 and Windows Server 2019 (in EC2).
17 # The script is tested on Windows 10 and Windows Server 2019 (in EC2).
18
18
19 $VS_BUILD_TOOLS_URL = "https://download.visualstudio.microsoft.com/download/pr/a1603c02-8a66-4b83-b821-811e3610a7c4/aa2db8bb39e0cbd23e9940d8951e0bc3/vs_buildtools.exe"
19 $VS_BUILD_TOOLS_URL = "https://download.visualstudio.microsoft.com/download/pr/a1603c02-8a66-4b83-b821-811e3610a7c4/aa2db8bb39e0cbd23e9940d8951e0bc3/vs_buildtools.exe"
20 $VS_BUILD_TOOLS_SHA256 = "911E292B8E6E5F46CBC17003BDCD2D27A70E616E8D5E6E69D5D489A605CAA139"
20 $VS_BUILD_TOOLS_SHA256 = "911E292B8E6E5F46CBC17003BDCD2D27A70E616E8D5E6E69D5D489A605CAA139"
21
21
22 $VC9_PYTHON_URL = "https://download.microsoft.com/download/7/9/6/796EF2E4-801B-4FC4-AB28-B59FBF6D907B/VCForPython27.msi"
22 $VC9_PYTHON_URL = "https://download.microsoft.com/download/7/9/6/796EF2E4-801B-4FC4-AB28-B59FBF6D907B/VCForPython27.msi"
23 $VC9_PYTHON_SHA256 = "070474db76a2e625513a5835df4595df9324d820f9cc97eab2a596dcbc2f5cbf"
23 $VC9_PYTHON_SHA256 = "070474db76a2e625513a5835df4595df9324d820f9cc97eab2a596dcbc2f5cbf"
24
24
25 $PYTHON27_x64_URL = "https://www.python.org/ftp/python/2.7.17/python-2.7.17.amd64.msi"
25 $PYTHON27_x64_URL = "https://www.python.org/ftp/python/2.7.18/python-2.7.18.amd64.msi"
26 $PYTHON27_x64_SHA256 = "3b934447e3620e51d2daf5b2f258c9b617bcc686ca2f777a49aa3b47893abf1b"
26 $PYTHON27_x64_SHA256 = "b74a3afa1e0bf2a6fc566a7b70d15c9bfabba3756fb077797d16fffa27800c05"
27 $PYTHON27_X86_URL = "https://www.python.org/ftp/python/2.7.17/python-2.7.17.msi"
27 $PYTHON27_X86_URL = "https://www.python.org/ftp/python/2.7.18/python-2.7.18.msi"
28 $PYTHON27_X86_SHA256 = "a4e3a321517c6b0c2693d6f712a0d18c82600b3d0c759c299b3d14384a17f863"
28 $PYTHON27_X86_SHA256 = "d901802e90026e9bad76b8a81f8dd7e43c7d7e8269d9281c9e9df7a9c40480a9"
29
29
30 $PYTHON35_x86_URL = "https://www.python.org/ftp/python/3.5.4/python-3.5.4.exe"
30 $PYTHON35_x86_URL = "https://www.python.org/ftp/python/3.5.4/python-3.5.4.exe"
31 $PYTHON35_x86_SHA256 = "F27C2D67FD9688E4970F3BFF799BB9D722A0D6C2C13B04848E1F7D620B524B0E"
31 $PYTHON35_x86_SHA256 = "F27C2D67FD9688E4970F3BFF799BB9D722A0D6C2C13B04848E1F7D620B524B0E"
32 $PYTHON35_x64_URL = "https://www.python.org/ftp/python/3.5.4/python-3.5.4-amd64.exe"
32 $PYTHON35_x64_URL = "https://www.python.org/ftp/python/3.5.4/python-3.5.4-amd64.exe"
33 $PYTHON35_x64_SHA256 = "9B7741CC32357573A77D2EE64987717E527628C38FD7EAF3E2AACA853D45A1EE"
33 $PYTHON35_x64_SHA256 = "9B7741CC32357573A77D2EE64987717E527628C38FD7EAF3E2AACA853D45A1EE"
34
34
35 $PYTHON36_x86_URL = "https://www.python.org/ftp/python/3.6.8/python-3.6.8.exe"
35 $PYTHON36_x86_URL = "https://www.python.org/ftp/python/3.6.8/python-3.6.8.exe"
36 $PYTHON36_x86_SHA256 = "89871D432BC06E4630D7B64CB1A8451E53C80E68DE29029976B12AAD7DBFA5A0"
36 $PYTHON36_x86_SHA256 = "89871D432BC06E4630D7B64CB1A8451E53C80E68DE29029976B12AAD7DBFA5A0"
37 $PYTHON36_x64_URL = "https://www.python.org/ftp/python/3.6.8/python-3.6.8-amd64.exe"
37 $PYTHON36_x64_URL = "https://www.python.org/ftp/python/3.6.8/python-3.6.8-amd64.exe"
38 $PYTHON36_x64_SHA256 = "96088A58B7C43BC83B84E6B67F15E8706C614023DD64F9A5A14E81FF824ADADC"
38 $PYTHON36_x64_SHA256 = "96088A58B7C43BC83B84E6B67F15E8706C614023DD64F9A5A14E81FF824ADADC"
39
39
40 $PYTHON37_x86_URL = "https://www.python.org/ftp/python/3.7.5/python-3.7.5.exe"
40 $PYTHON37_x86_URL = "https://www.python.org/ftp/python/3.7.7/python-3.7.7.exe"
41 $PYTHON37_x86_SHA256 = "3c2ae8f72b48e6e0c2b482206e322bf5d0344ff91abc3b3c200cec9e275c7168"
41 $PYTHON37_x86_SHA256 = "27fbffcd342d5055acc64050db4c35d0025661521e642b59c381dcba2e162c6a"
42 $PYTHON37_X64_URL = "https://www.python.org/ftp/python/3.7.5/python-3.7.5-amd64.exe"
42 $PYTHON37_X64_URL = "https://www.python.org/ftp/python/3.7.7/python-3.7.7-amd64.exe"
43 $PYTHON37_x64_SHA256 = "f3d60c127e7a92ed547efa3321bf70cd96b75c53bf4b903147015257c1314981"
43 $PYTHON37_x64_SHA256 = "1a0368663ceff999d865de955992b6ea3cb0c8cb15a1a296a8eb7df19cc59e69"
44
44
45 $PYTHON38_x86_URL = "https://www.python.org/ftp/python/3.8.0/python-3.8.0.exe"
45 $PYTHON38_x86_URL = "https://www.python.org/ftp/python/3.8.2/python-3.8.2.exe"
46 $PYTHON38_x86_SHA256 = "b471908de5e10d8fb5c3351a5affb1172da7790c533e0c9ffbaeec9c11611b15"
46 $PYTHON38_x86_SHA256 = "03ac5754a69c9c11c08d1f4d694c14625a4d27348ad4dd2d1253e2547819db2c"
47 $PYTHON38_x64_URL = "https://www.python.org/ftp/python/3.8.0/python-3.8.0-amd64.exe"
47 $PYTHON38_x64_URL = "https://www.python.org/ftp/python/3.8.2/python-3.8.2-amd64.exe"
48 $PYTHON38_x64_SHA256 = "a9bbc6088a3e4c7112826e21bfee6277f7b6d93259f7c57176139231bb7071e4"
48 $PYTHON38_x64_SHA256 = "8e400e3f32cdcb746e62e0db4d3ae4cba1f927141ebc4d0d5a4006b0daee8921"
49
49
50 # PIP 19.2.3.
50 # PIP 19.2.3.
51 $PIP_URL = "https://github.com/pypa/get-pip/raw/309a56c5fd94bd1134053a541cb4657a4e47e09d/get-pip.py"
51 $PIP_URL = "https://github.com/pypa/get-pip/raw/309a56c5fd94bd1134053a541cb4657a4e47e09d/get-pip.py"
52 $PIP_SHA256 = "57e3643ff19f018f8a00dfaa6b7e4620e3c1a7a2171fd218425366ec006b3bfe"
52 $PIP_SHA256 = "57e3643ff19f018f8a00dfaa6b7e4620e3c1a7a2171fd218425366ec006b3bfe"
53
53
54 $VIRTUALENV_URL = "https://files.pythonhosted.org/packages/66/f0/6867af06d2e2f511e4e1d7094ff663acdebc4f15d4a0cb0fed1007395124/virtualenv-16.7.5.tar.gz"
54 $VIRTUALENV_URL = "https://files.pythonhosted.org/packages/66/f0/6867af06d2e2f511e4e1d7094ff663acdebc4f15d4a0cb0fed1007395124/virtualenv-16.7.5.tar.gz"
55 $VIRTUALENV_SHA256 = "f78d81b62d3147396ac33fc9d77579ddc42cc2a98dd9ea38886f616b33bc7fb2"
55 $VIRTUALENV_SHA256 = "f78d81b62d3147396ac33fc9d77579ddc42cc2a98dd9ea38886f616b33bc7fb2"
56
56
57 $INNO_SETUP_URL = "http://files.jrsoftware.org/is/5/innosetup-5.6.1-unicode.exe"
57 $INNO_SETUP_URL = "http://files.jrsoftware.org/is/5/innosetup-5.6.1-unicode.exe"
58 $INNO_SETUP_SHA256 = "27D49E9BC769E9D1B214C153011978DB90DC01C2ACD1DDCD9ED7B3FE3B96B538"
58 $INNO_SETUP_SHA256 = "27D49E9BC769E9D1B214C153011978DB90DC01C2ACD1DDCD9ED7B3FE3B96B538"
59
59
60 $MINGW_BIN_URL = "https://osdn.net/frs/redir.php?m=constant&f=mingw%2F68260%2Fmingw-get-0.6.3-mingw32-pre-20170905-1-bin.zip"
60 $MINGW_BIN_URL = "https://osdn.net/frs/redir.php?m=constant&f=mingw%2F68260%2Fmingw-get-0.6.3-mingw32-pre-20170905-1-bin.zip"
61 $MINGW_BIN_SHA256 = "2AB8EFD7C7D1FC8EAF8B2FA4DA4EEF8F3E47768284C021599BC7435839A046DF"
61 $MINGW_BIN_SHA256 = "2AB8EFD7C7D1FC8EAF8B2FA4DA4EEF8F3E47768284C021599BC7435839A046DF"
62
62
63 $MERCURIAL_WHEEL_FILENAME = "mercurial-5.1.2-cp27-cp27m-win_amd64.whl"
63 $MERCURIAL_WHEEL_FILENAME = "mercurial-5.1.2-cp27-cp27m-win_amd64.whl"
64 $MERCURIAL_WHEEL_URL = "https://files.pythonhosted.org/packages/6d/47/e031e47f7fe9b16e4e3383da47e2b0a7eae6e603996bc67a03ec4fa1b3f4/$MERCURIAL_WHEEL_FILENAME"
64 $MERCURIAL_WHEEL_URL = "https://files.pythonhosted.org/packages/6d/47/e031e47f7fe9b16e4e3383da47e2b0a7eae6e603996bc67a03ec4fa1b3f4/$MERCURIAL_WHEEL_FILENAME"
65 $MERCURIAL_WHEEL_SHA256 = "1d18c7f6ca1456f0f62ee65c9a50c14cbba48ce6e924930cdb10537f5c9eaf5f"
65 $MERCURIAL_WHEEL_SHA256 = "1d18c7f6ca1456f0f62ee65c9a50c14cbba48ce6e924930cdb10537f5c9eaf5f"
66
66
67 # Writing progress slows down downloads substantially. So disable it.
67 # Writing progress slows down downloads substantially. So disable it.
68 $progressPreference = 'silentlyContinue'
68 $progressPreference = 'silentlyContinue'
69
69
70 function Secure-Download($url, $path, $sha256) {
70 function Secure-Download($url, $path, $sha256) {
71 if (Test-Path -Path $path) {
71 if (Test-Path -Path $path) {
72 Get-FileHash -Path $path -Algorithm SHA256 -OutVariable hash
72 Get-FileHash -Path $path -Algorithm SHA256 -OutVariable hash
73
73
74 if ($hash.Hash -eq $sha256) {
74 if ($hash.Hash -eq $sha256) {
75 Write-Output "SHA256 of $path verified as $sha256"
75 Write-Output "SHA256 of $path verified as $sha256"
76 return
76 return
77 }
77 }
78
78
79 Write-Output "hash mismatch on $path; downloading again"
79 Write-Output "hash mismatch on $path; downloading again"
80 }
80 }
81
81
82 Write-Output "downloading $url to $path"
82 Write-Output "downloading $url to $path"
83 Invoke-WebRequest -Uri $url -OutFile $path
83 Invoke-WebRequest -Uri $url -OutFile $path
84 Get-FileHash -Path $path -Algorithm SHA256 -OutVariable hash
84 Get-FileHash -Path $path -Algorithm SHA256 -OutVariable hash
85
85
86 if ($hash.Hash -ne $sha256) {
86 if ($hash.Hash -ne $sha256) {
87 Remove-Item -Path $path
87 Remove-Item -Path $path
88 throw "hash mismatch when downloading $url; got $($hash.Hash), expected $sha256"
88 throw "hash mismatch when downloading $url; got $($hash.Hash), expected $sha256"
89 }
89 }
90 }
90 }
91
91
92 function Invoke-Process($path, $arguments) {
92 function Invoke-Process($path, $arguments) {
93 $p = Start-Process -FilePath $path -ArgumentList $arguments -Wait -PassThru -WindowStyle Hidden
93 $p = Start-Process -FilePath $path -ArgumentList $arguments -Wait -PassThru -WindowStyle Hidden
94
94
95 if ($p.ExitCode -ne 0) {
95 if ($p.ExitCode -ne 0) {
96 throw "process exited non-0: $($p.ExitCode)"
96 throw "process exited non-0: $($p.ExitCode)"
97 }
97 }
98 }
98 }
99
99
100 function Install-Python3($name, $installer, $dest, $pip) {
100 function Install-Python3($name, $installer, $dest, $pip) {
101 Write-Output "installing $name"
101 Write-Output "installing $name"
102
102
103 # We hit this when running the script as part of Simple Systems Manager in
103 # We hit this when running the script as part of Simple Systems Manager in
104 # EC2. The Python 3 installer doesn't seem to like per-user installs
104 # EC2. The Python 3 installer doesn't seem to like per-user installs
105 # when running as the SYSTEM user. So enable global installs if executed in
105 # when running as the SYSTEM user. So enable global installs if executed in
106 # this mode.
106 # this mode.
107 if ($env:USERPROFILE -eq "C:\Windows\system32\config\systemprofile") {
107 if ($env:USERPROFILE -eq "C:\Windows\system32\config\systemprofile") {
108 Write-Output "running with SYSTEM account; installing for all users"
108 Write-Output "running with SYSTEM account; installing for all users"
109 $allusers = "1"
109 $allusers = "1"
110 }
110 }
111 else {
111 else {
112 $allusers = "0"
112 $allusers = "0"
113 }
113 }
114
114
115 Invoke-Process $installer "/quiet TargetDir=${dest} InstallAllUsers=${allusers} AssociateFiles=0 CompileAll=0 PrependPath=0 Include_doc=0 Include_launcher=0 InstallLauncherAllUsers=0 Include_pip=0 Include_test=0"
115 Invoke-Process $installer "/quiet TargetDir=${dest} InstallAllUsers=${allusers} AssociateFiles=0 CompileAll=0 PrependPath=0 Include_doc=0 Include_launcher=0 InstallLauncherAllUsers=0 Include_pip=0 Include_test=0"
116 Invoke-Process ${dest}\python.exe $pip
116 Invoke-Process ${dest}\python.exe $pip
117 }
117 }
118
118
119 function Install-Dependencies($prefix) {
119 function Install-Dependencies($prefix) {
120 if (!(Test-Path -Path $prefix\assets)) {
120 if (!(Test-Path -Path $prefix\assets)) {
121 New-Item -Path $prefix\assets -ItemType Directory
121 New-Item -Path $prefix\assets -ItemType Directory
122 }
122 }
123
123
124 $pip = "${prefix}\assets\get-pip.py"
124 $pip = "${prefix}\assets\get-pip.py"
125
125
126 Secure-Download $VC9_PYTHON_URL ${prefix}\assets\VCForPython27.msi $VC9_PYTHON_SHA256
126 Secure-Download $VC9_PYTHON_URL ${prefix}\assets\VCForPython27.msi $VC9_PYTHON_SHA256
127 Secure-Download $PYTHON27_x86_URL ${prefix}\assets\python27-x86.msi $PYTHON27_x86_SHA256
127 Secure-Download $PYTHON27_x86_URL ${prefix}\assets\python27-x86.msi $PYTHON27_x86_SHA256
128 Secure-Download $PYTHON27_x64_URL ${prefix}\assets\python27-x64.msi $PYTHON27_x64_SHA256
128 Secure-Download $PYTHON27_x64_URL ${prefix}\assets\python27-x64.msi $PYTHON27_x64_SHA256
129 Secure-Download $PYTHON35_x86_URL ${prefix}\assets\python35-x86.exe $PYTHON35_x86_SHA256
129 Secure-Download $PYTHON35_x86_URL ${prefix}\assets\python35-x86.exe $PYTHON35_x86_SHA256
130 Secure-Download $PYTHON35_x64_URL ${prefix}\assets\python35-x64.exe $PYTHON35_x64_SHA256
130 Secure-Download $PYTHON35_x64_URL ${prefix}\assets\python35-x64.exe $PYTHON35_x64_SHA256
131 Secure-Download $PYTHON36_x86_URL ${prefix}\assets\python36-x86.exe $PYTHON36_x86_SHA256
131 Secure-Download $PYTHON36_x86_URL ${prefix}\assets\python36-x86.exe $PYTHON36_x86_SHA256
132 Secure-Download $PYTHON36_x64_URL ${prefix}\assets\python36-x64.exe $PYTHON36_x64_SHA256
132 Secure-Download $PYTHON36_x64_URL ${prefix}\assets\python36-x64.exe $PYTHON36_x64_SHA256
133 Secure-Download $PYTHON37_x86_URL ${prefix}\assets\python37-x86.exe $PYTHON37_x86_SHA256
133 Secure-Download $PYTHON37_x86_URL ${prefix}\assets\python37-x86.exe $PYTHON37_x86_SHA256
134 Secure-Download $PYTHON37_x64_URL ${prefix}\assets\python37-x64.exe $PYTHON37_x64_SHA256
134 Secure-Download $PYTHON37_x64_URL ${prefix}\assets\python37-x64.exe $PYTHON37_x64_SHA256
135 Secure-Download $PYTHON38_x86_URL ${prefix}\assets\python38-x86.exe $PYTHON38_x86_SHA256
135 Secure-Download $PYTHON38_x86_URL ${prefix}\assets\python38-x86.exe $PYTHON38_x86_SHA256
136 Secure-Download $PYTHON38_x64_URL ${prefix}\assets\python38-x64.exe $PYTHON38_x64_SHA256
136 Secure-Download $PYTHON38_x64_URL ${prefix}\assets\python38-x64.exe $PYTHON38_x64_SHA256
137 Secure-Download $PIP_URL ${pip} $PIP_SHA256
137 Secure-Download $PIP_URL ${pip} $PIP_SHA256
138 Secure-Download $VIRTUALENV_URL ${prefix}\assets\virtualenv.tar.gz $VIRTUALENV_SHA256
138 Secure-Download $VIRTUALENV_URL ${prefix}\assets\virtualenv.tar.gz $VIRTUALENV_SHA256
139 Secure-Download $VS_BUILD_TOOLS_URL ${prefix}\assets\vs_buildtools.exe $VS_BUILD_TOOLS_SHA256
139 Secure-Download $VS_BUILD_TOOLS_URL ${prefix}\assets\vs_buildtools.exe $VS_BUILD_TOOLS_SHA256
140 Secure-Download $INNO_SETUP_URL ${prefix}\assets\InnoSetup.exe $INNO_SETUP_SHA256
140 Secure-Download $INNO_SETUP_URL ${prefix}\assets\InnoSetup.exe $INNO_SETUP_SHA256
141 Secure-Download $MINGW_BIN_URL ${prefix}\assets\mingw-get-bin.zip $MINGW_BIN_SHA256
141 Secure-Download $MINGW_BIN_URL ${prefix}\assets\mingw-get-bin.zip $MINGW_BIN_SHA256
142 Secure-Download $MERCURIAL_WHEEL_URL ${prefix}\assets\${MERCURIAL_WHEEL_FILENAME} $MERCURIAL_WHEEL_SHA256
142 Secure-Download $MERCURIAL_WHEEL_URL ${prefix}\assets\${MERCURIAL_WHEEL_FILENAME} $MERCURIAL_WHEEL_SHA256
143
143
144 Write-Output "installing Python 2.7 32-bit"
144 Write-Output "installing Python 2.7 32-bit"
145 Invoke-Process msiexec.exe "/i ${prefix}\assets\python27-x86.msi /l* ${prefix}\assets\python27-x86.log /q TARGETDIR=${prefix}\python27-x86 ALLUSERS="
145 Invoke-Process msiexec.exe "/i ${prefix}\assets\python27-x86.msi /l* ${prefix}\assets\python27-x86.log /q TARGETDIR=${prefix}\python27-x86 ALLUSERS="
146 Invoke-Process ${prefix}\python27-x86\python.exe ${prefix}\assets\get-pip.py
146 Invoke-Process ${prefix}\python27-x86\python.exe ${prefix}\assets\get-pip.py
147 Invoke-Process ${prefix}\python27-x86\Scripts\pip.exe "install ${prefix}\assets\virtualenv.tar.gz"
147 Invoke-Process ${prefix}\python27-x86\Scripts\pip.exe "install ${prefix}\assets\virtualenv.tar.gz"
148
148
149 Write-Output "installing Python 2.7 64-bit"
149 Write-Output "installing Python 2.7 64-bit"
150 Invoke-Process msiexec.exe "/i ${prefix}\assets\python27-x64.msi /l* ${prefix}\assets\python27-x64.log /q TARGETDIR=${prefix}\python27-x64 ALLUSERS="
150 Invoke-Process msiexec.exe "/i ${prefix}\assets\python27-x64.msi /l* ${prefix}\assets\python27-x64.log /q TARGETDIR=${prefix}\python27-x64 ALLUSERS="
151 Invoke-Process ${prefix}\python27-x64\python.exe ${prefix}\assets\get-pip.py
151 Invoke-Process ${prefix}\python27-x64\python.exe ${prefix}\assets\get-pip.py
152 Invoke-Process ${prefix}\python27-x64\Scripts\pip.exe "install ${prefix}\assets\virtualenv.tar.gz"
152 Invoke-Process ${prefix}\python27-x64\Scripts\pip.exe "install ${prefix}\assets\virtualenv.tar.gz"
153
153
154 Install-Python3 "Python 3.5 32-bit" ${prefix}\assets\python35-x86.exe ${prefix}\python35-x86 ${pip}
154 Install-Python3 "Python 3.5 32-bit" ${prefix}\assets\python35-x86.exe ${prefix}\python35-x86 ${pip}
155 Install-Python3 "Python 3.5 64-bit" ${prefix}\assets\python35-x64.exe ${prefix}\python35-x64 ${pip}
155 Install-Python3 "Python 3.5 64-bit" ${prefix}\assets\python35-x64.exe ${prefix}\python35-x64 ${pip}
156 Install-Python3 "Python 3.6 32-bit" ${prefix}\assets\python36-x86.exe ${prefix}\python36-x86 ${pip}
156 Install-Python3 "Python 3.6 32-bit" ${prefix}\assets\python36-x86.exe ${prefix}\python36-x86 ${pip}
157 Install-Python3 "Python 3.6 64-bit" ${prefix}\assets\python36-x64.exe ${prefix}\python36-x64 ${pip}
157 Install-Python3 "Python 3.6 64-bit" ${prefix}\assets\python36-x64.exe ${prefix}\python36-x64 ${pip}
158 Install-Python3 "Python 3.7 32-bit" ${prefix}\assets\python37-x86.exe ${prefix}\python37-x86 ${pip}
158 Install-Python3 "Python 3.7 32-bit" ${prefix}\assets\python37-x86.exe ${prefix}\python37-x86 ${pip}
159 Install-Python3 "Python 3.7 64-bit" ${prefix}\assets\python37-x64.exe ${prefix}\python37-x64 ${pip}
159 Install-Python3 "Python 3.7 64-bit" ${prefix}\assets\python37-x64.exe ${prefix}\python37-x64 ${pip}
160 Install-Python3 "Python 3.8 32-bit" ${prefix}\assets\python38-x86.exe ${prefix}\python38-x86 ${pip}
160 Install-Python3 "Python 3.8 32-bit" ${prefix}\assets\python38-x86.exe ${prefix}\python38-x86 ${pip}
161 Install-Python3 "Python 3.8 64-bit" ${prefix}\assets\python38-x64.exe ${prefix}\python38-x64 ${pip}
161 Install-Python3 "Python 3.8 64-bit" ${prefix}\assets\python38-x64.exe ${prefix}\python38-x64 ${pip}
162
162
163 Write-Output "installing Visual Studio 2017 Build Tools and SDKs"
163 Write-Output "installing Visual Studio 2017 Build Tools and SDKs"
164 Invoke-Process ${prefix}\assets\vs_buildtools.exe "--quiet --wait --norestart --nocache --channelUri https://aka.ms/vs/15/release/channel --add Microsoft.VisualStudio.Workload.MSBuildTools --add Microsoft.VisualStudio.Component.Windows10SDK.17763 --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.Windows10SDK --add Microsoft.VisualStudio.Component.VC.140"
164 Invoke-Process ${prefix}\assets\vs_buildtools.exe "--quiet --wait --norestart --nocache --channelUri https://aka.ms/vs/15/release/channel --add Microsoft.VisualStudio.Workload.MSBuildTools --add Microsoft.VisualStudio.Component.Windows10SDK.17763 --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.Windows10SDK --add Microsoft.VisualStudio.Component.VC.140"
165
165
166 Write-Output "installing Visual C++ 9.0 for Python 2.7"
166 Write-Output "installing Visual C++ 9.0 for Python 2.7"
167 Invoke-Process msiexec.exe "/i ${prefix}\assets\VCForPython27.msi /l* ${prefix}\assets\VCForPython27.log /q"
167 Invoke-Process msiexec.exe "/i ${prefix}\assets\VCForPython27.msi /l* ${prefix}\assets\VCForPython27.log /q"
168
168
169 Write-Output "installing Inno Setup"
169 Write-Output "installing Inno Setup"
170 Invoke-Process ${prefix}\assets\InnoSetup.exe "/SP- /VERYSILENT /SUPPRESSMSGBOXES"
170 Invoke-Process ${prefix}\assets\InnoSetup.exe "/SP- /VERYSILENT /SUPPRESSMSGBOXES"
171
171
172 Write-Output "extracting MinGW base archive"
172 Write-Output "extracting MinGW base archive"
173 Expand-Archive -Path ${prefix}\assets\mingw-get-bin.zip -DestinationPath "${prefix}\MinGW" -Force
173 Expand-Archive -Path ${prefix}\assets\mingw-get-bin.zip -DestinationPath "${prefix}\MinGW" -Force
174
174
175 Write-Output "updating MinGW package catalogs"
175 Write-Output "updating MinGW package catalogs"
176 Invoke-Process ${prefix}\MinGW\bin\mingw-get.exe "update"
176 Invoke-Process ${prefix}\MinGW\bin\mingw-get.exe "update"
177
177
178 Write-Output "installing MinGW packages"
178 Write-Output "installing MinGW packages"
179 Invoke-Process ${prefix}\MinGW\bin\mingw-get.exe "install msys-base msys-coreutils msys-diffutils msys-unzip"
179 Invoke-Process ${prefix}\MinGW\bin\mingw-get.exe "install msys-base msys-coreutils msys-diffutils msys-unzip"
180
180
181 # Construct a virtualenv useful for bootstrapping. It conveniently contains a
181 # Construct a virtualenv useful for bootstrapping. It conveniently contains a
182 # Mercurial install.
182 # Mercurial install.
183 Write-Output "creating bootstrap virtualenv with Mercurial"
183 Write-Output "creating bootstrap virtualenv with Mercurial"
184 Invoke-Process "$prefix\python27-x64\Scripts\virtualenv.exe" "${prefix}\venv-bootstrap"
184 Invoke-Process "$prefix\python27-x64\Scripts\virtualenv.exe" "${prefix}\venv-bootstrap"
185 Invoke-Process "${prefix}\venv-bootstrap\Scripts\pip.exe" "install ${prefix}\assets\${MERCURIAL_WHEEL_FILENAME}"
185 Invoke-Process "${prefix}\venv-bootstrap\Scripts\pip.exe" "install ${prefix}\assets\${MERCURIAL_WHEEL_FILENAME}"
186 }
186 }
187
187
188 function Clone-Mercurial-Repo($prefix, $repo_url, $dest) {
188 function Clone-Mercurial-Repo($prefix, $repo_url, $dest) {
189 Write-Output "cloning $repo_url to $dest"
189 Write-Output "cloning $repo_url to $dest"
190 # TODO Figure out why CA verification isn't working in EC2 and remove
190 # TODO Figure out why CA verification isn't working in EC2 and remove
191 # --insecure.
191 # --insecure.
192 Invoke-Process "${prefix}\venv-bootstrap\Scripts\hg.exe" "clone --insecure $repo_url $dest"
192 Invoke-Process "${prefix}\venv-bootstrap\Scripts\hg.exe" "clone --insecure $repo_url $dest"
193
193
194 # Mark repo as non-publishing by default for convenience.
194 # Mark repo as non-publishing by default for convenience.
195 Add-Content -Path "$dest\.hg\hgrc" -Value "`n[phases]`npublish = false"
195 Add-Content -Path "$dest\.hg\hgrc" -Value "`n[phases]`npublish = false"
196 }
196 }
197
197
198 $prefix = "c:\hgdev"
198 $prefix = "c:\hgdev"
199 Install-Dependencies $prefix
199 Install-Dependencies $prefix
200 Clone-Mercurial-Repo $prefix "https://www.mercurial-scm.org/repo/hg" $prefix\src
200 Clone-Mercurial-Repo $prefix "https://www.mercurial-scm.org/repo/hg" $prefix\src
@@ -1,39 +1,49 b''
1 #
1 #
2 # This file is autogenerated by pip-compile
2 # This file is autogenerated by pip-compile
3 # To update, run:
3 # To update, run:
4 #
4 #
5 # pip-compile --generate-hashes --output-file=contrib/packaging/requirements.txt contrib/packaging/requirements.txt.in
5 # pip-compile --generate-hashes --output-file=contrib/packaging/requirements.txt contrib/packaging/requirements.txt.in
6 #
6 #
7 jinja2==2.10.3 \
7 docutils==0.16 \
8 --hash=sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f \
8 --hash=sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af \
9 --hash=sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de
9 --hash=sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc \
10 # via -r contrib/packaging/requirements.txt.in
11 jinja2==2.11.2 \
12 --hash=sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0 \
13 --hash=sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035 \
14 # via -r contrib/packaging/requirements.txt.in
10 markupsafe==1.1.1 \
15 markupsafe==1.1.1 \
11 --hash=sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473 \
16 --hash=sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473 \
12 --hash=sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161 \
17 --hash=sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161 \
13 --hash=sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235 \
18 --hash=sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235 \
14 --hash=sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5 \
19 --hash=sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5 \
20 --hash=sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42 \
15 --hash=sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff \
21 --hash=sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff \
16 --hash=sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b \
22 --hash=sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b \
17 --hash=sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1 \
23 --hash=sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1 \
18 --hash=sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e \
24 --hash=sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e \
19 --hash=sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183 \
25 --hash=sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183 \
20 --hash=sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66 \
26 --hash=sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66 \
27 --hash=sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b \
21 --hash=sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1 \
28 --hash=sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1 \
29 --hash=sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15 \
22 --hash=sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1 \
30 --hash=sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1 \
23 --hash=sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e \
31 --hash=sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e \
24 --hash=sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b \
32 --hash=sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b \
25 --hash=sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905 \
33 --hash=sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905 \
26 --hash=sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735 \
34 --hash=sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735 \
27 --hash=sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d \
35 --hash=sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d \
28 --hash=sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e \
36 --hash=sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e \
29 --hash=sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d \
37 --hash=sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d \
30 --hash=sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c \
38 --hash=sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c \
31 --hash=sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21 \
39 --hash=sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21 \
32 --hash=sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2 \
40 --hash=sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2 \
33 --hash=sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5 \
41 --hash=sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5 \
34 --hash=sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b \
42 --hash=sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b \
35 --hash=sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6 \
43 --hash=sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6 \
36 --hash=sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f \
44 --hash=sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f \
37 --hash=sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f \
45 --hash=sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f \
46 --hash=sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2 \
38 --hash=sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7 \
47 --hash=sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7 \
48 --hash=sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be \
39 # via jinja2
49 # via jinja2
@@ -1,1 +1,2 b''
1 docutils
1 jinja2
2 jinja2
@@ -1,598 +1,602 b''
1 # discovery.py - protocol changeset discovery functions
1 # discovery.py - protocol changeset discovery functions
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import functools
10 import functools
11
11
12 from .i18n import _
12 from .i18n import _
13 from .node import (
13 from .node import (
14 hex,
14 hex,
15 nullid,
15 nullid,
16 short,
16 short,
17 )
17 )
18
18
19 from . import (
19 from . import (
20 bookmarks,
20 bookmarks,
21 branchmap,
21 branchmap,
22 error,
22 error,
23 phases,
23 phases,
24 pycompat,
24 pycompat,
25 scmutil,
25 scmutil,
26 setdiscovery,
26 setdiscovery,
27 treediscovery,
27 treediscovery,
28 util,
28 util,
29 )
29 )
30
30
31
31
32 def findcommonincoming(repo, remote, heads=None, force=False, ancestorsof=None):
32 def findcommonincoming(repo, remote, heads=None, force=False, ancestorsof=None):
33 """Return a tuple (common, anyincoming, heads) used to identify the common
33 """Return a tuple (common, anyincoming, heads) used to identify the common
34 subset of nodes between repo and remote.
34 subset of nodes between repo and remote.
35
35
36 "common" is a list of (at least) the heads of the common subset.
36 "common" is a list of (at least) the heads of the common subset.
37 "anyincoming" is testable as a boolean indicating if any nodes are missing
37 "anyincoming" is testable as a boolean indicating if any nodes are missing
38 locally. If remote does not support getbundle, this actually is a list of
38 locally. If remote does not support getbundle, this actually is a list of
39 roots of the nodes that would be incoming, to be supplied to
39 roots of the nodes that would be incoming, to be supplied to
40 changegroupsubset. No code except for pull should be relying on this fact
40 changegroupsubset. No code except for pull should be relying on this fact
41 any longer.
41 any longer.
42 "heads" is either the supplied heads, or else the remote's heads.
42 "heads" is either the supplied heads, or else the remote's heads.
43 "ancestorsof" if not None, restrict the discovery to a subset defined by
43 "ancestorsof" if not None, restrict the discovery to a subset defined by
44 these nodes. Changeset outside of this set won't be considered (and
44 these nodes. Changeset outside of this set won't be considered (and
45 won't appears in "common")
45 won't appears in "common")
46
46
47 If you pass heads and they are all known locally, the response lists just
47 If you pass heads and they are all known locally, the response lists just
48 these heads in "common" and in "heads".
48 these heads in "common" and in "heads".
49
49
50 Please use findcommonoutgoing to compute the set of outgoing nodes to give
50 Please use findcommonoutgoing to compute the set of outgoing nodes to give
51 extensions a good hook into outgoing.
51 extensions a good hook into outgoing.
52 """
52 """
53
53
54 if not remote.capable(b'getbundle'):
54 if not remote.capable(b'getbundle'):
55 return treediscovery.findcommonincoming(repo, remote, heads, force)
55 return treediscovery.findcommonincoming(repo, remote, heads, force)
56
56
57 if heads:
57 if heads:
58 knownnode = repo.changelog.hasnode # no nodemap until it is filtered
58 knownnode = repo.changelog.hasnode # no nodemap until it is filtered
59 if all(knownnode(h) for h in heads):
59 if all(knownnode(h) for h in heads):
60 return (heads, False, heads)
60 return (heads, False, heads)
61
61
62 res = setdiscovery.findcommonheads(
62 res = setdiscovery.findcommonheads(
63 repo.ui,
63 repo.ui,
64 repo,
64 repo,
65 remote,
65 remote,
66 abortwhenunrelated=not force,
66 abortwhenunrelated=not force,
67 ancestorsof=ancestorsof,
67 ancestorsof=ancestorsof,
68 )
68 )
69 common, anyinc, srvheads = res
69 common, anyinc, srvheads = res
70 if heads and not anyinc:
71 # server could be lying on the advertised heads
72 has_node = repo.changelog.hasnode
73 anyinc = any(not has_node(n) for n in heads)
70 return (list(common), anyinc, heads or list(srvheads))
74 return (list(common), anyinc, heads or list(srvheads))
71
75
72
76
73 class outgoing(object):
77 class outgoing(object):
74 '''Represents the set of nodes present in a local repo but not in a
78 '''Represents the set of nodes present in a local repo but not in a
75 (possibly) remote one.
79 (possibly) remote one.
76
80
77 Members:
81 Members:
78
82
79 missing is a list of all nodes present in local but not in remote.
83 missing is a list of all nodes present in local but not in remote.
80 common is a list of all nodes shared between the two repos.
84 common is a list of all nodes shared between the two repos.
81 excluded is the list of missing changeset that shouldn't be sent remotely.
85 excluded is the list of missing changeset that shouldn't be sent remotely.
82 missingheads is the list of heads of missing.
86 missingheads is the list of heads of missing.
83 commonheads is the list of heads of common.
87 commonheads is the list of heads of common.
84
88
85 The sets are computed on demand from the heads, unless provided upfront
89 The sets are computed on demand from the heads, unless provided upfront
86 by discovery.'''
90 by discovery.'''
87
91
88 def __init__(
92 def __init__(
89 self, repo, commonheads=None, missingheads=None, missingroots=None
93 self, repo, commonheads=None, missingheads=None, missingroots=None
90 ):
94 ):
91 # at least one of them must not be set
95 # at least one of them must not be set
92 assert None in (commonheads, missingroots)
96 assert None in (commonheads, missingroots)
93 cl = repo.changelog
97 cl = repo.changelog
94 if missingheads is None:
98 if missingheads is None:
95 missingheads = cl.heads()
99 missingheads = cl.heads()
96 if missingroots:
100 if missingroots:
97 discbases = []
101 discbases = []
98 for n in missingroots:
102 for n in missingroots:
99 discbases.extend([p for p in cl.parents(n) if p != nullid])
103 discbases.extend([p for p in cl.parents(n) if p != nullid])
100 # TODO remove call to nodesbetween.
104 # TODO remove call to nodesbetween.
101 # TODO populate attributes on outgoing instance instead of setting
105 # TODO populate attributes on outgoing instance instead of setting
102 # discbases.
106 # discbases.
103 csets, roots, heads = cl.nodesbetween(missingroots, missingheads)
107 csets, roots, heads = cl.nodesbetween(missingroots, missingheads)
104 included = set(csets)
108 included = set(csets)
105 missingheads = heads
109 missingheads = heads
106 commonheads = [n for n in discbases if n not in included]
110 commonheads = [n for n in discbases if n not in included]
107 elif not commonheads:
111 elif not commonheads:
108 commonheads = [nullid]
112 commonheads = [nullid]
109 self.commonheads = commonheads
113 self.commonheads = commonheads
110 self.missingheads = missingheads
114 self.missingheads = missingheads
111 self._revlog = cl
115 self._revlog = cl
112 self._common = None
116 self._common = None
113 self._missing = None
117 self._missing = None
114 self.excluded = []
118 self.excluded = []
115
119
116 def _computecommonmissing(self):
120 def _computecommonmissing(self):
117 sets = self._revlog.findcommonmissing(
121 sets = self._revlog.findcommonmissing(
118 self.commonheads, self.missingheads
122 self.commonheads, self.missingheads
119 )
123 )
120 self._common, self._missing = sets
124 self._common, self._missing = sets
121
125
122 @util.propertycache
126 @util.propertycache
123 def common(self):
127 def common(self):
124 if self._common is None:
128 if self._common is None:
125 self._computecommonmissing()
129 self._computecommonmissing()
126 return self._common
130 return self._common
127
131
128 @util.propertycache
132 @util.propertycache
129 def missing(self):
133 def missing(self):
130 if self._missing is None:
134 if self._missing is None:
131 self._computecommonmissing()
135 self._computecommonmissing()
132 return self._missing
136 return self._missing
133
137
134
138
135 def findcommonoutgoing(
139 def findcommonoutgoing(
136 repo, other, onlyheads=None, force=False, commoninc=None, portable=False
140 repo, other, onlyheads=None, force=False, commoninc=None, portable=False
137 ):
141 ):
138 '''Return an outgoing instance to identify the nodes present in repo but
142 '''Return an outgoing instance to identify the nodes present in repo but
139 not in other.
143 not in other.
140
144
141 If onlyheads is given, only nodes ancestral to nodes in onlyheads
145 If onlyheads is given, only nodes ancestral to nodes in onlyheads
142 (inclusive) are included. If you already know the local repo's heads,
146 (inclusive) are included. If you already know the local repo's heads,
143 passing them in onlyheads is faster than letting them be recomputed here.
147 passing them in onlyheads is faster than letting them be recomputed here.
144
148
145 If commoninc is given, it must be the result of a prior call to
149 If commoninc is given, it must be the result of a prior call to
146 findcommonincoming(repo, other, force) to avoid recomputing it here.
150 findcommonincoming(repo, other, force) to avoid recomputing it here.
147
151
148 If portable is given, compute more conservative common and missingheads,
152 If portable is given, compute more conservative common and missingheads,
149 to make bundles created from the instance more portable.'''
153 to make bundles created from the instance more portable.'''
150 # declare an empty outgoing object to be filled later
154 # declare an empty outgoing object to be filled later
151 og = outgoing(repo, None, None)
155 og = outgoing(repo, None, None)
152
156
153 # get common set if not provided
157 # get common set if not provided
154 if commoninc is None:
158 if commoninc is None:
155 commoninc = findcommonincoming(
159 commoninc = findcommonincoming(
156 repo, other, force=force, ancestorsof=onlyheads
160 repo, other, force=force, ancestorsof=onlyheads
157 )
161 )
158 og.commonheads, _any, _hds = commoninc
162 og.commonheads, _any, _hds = commoninc
159
163
160 # compute outgoing
164 # compute outgoing
161 mayexclude = repo._phasecache.phaseroots[phases.secret] or repo.obsstore
165 mayexclude = repo._phasecache.phaseroots[phases.secret] or repo.obsstore
162 if not mayexclude:
166 if not mayexclude:
163 og.missingheads = onlyheads or repo.heads()
167 og.missingheads = onlyheads or repo.heads()
164 elif onlyheads is None:
168 elif onlyheads is None:
165 # use visible heads as it should be cached
169 # use visible heads as it should be cached
166 og.missingheads = repo.filtered(b"served").heads()
170 og.missingheads = repo.filtered(b"served").heads()
167 og.excluded = [ctx.node() for ctx in repo.set(b'secret() or extinct()')]
171 og.excluded = [ctx.node() for ctx in repo.set(b'secret() or extinct()')]
168 else:
172 else:
169 # compute common, missing and exclude secret stuff
173 # compute common, missing and exclude secret stuff
170 sets = repo.changelog.findcommonmissing(og.commonheads, onlyheads)
174 sets = repo.changelog.findcommonmissing(og.commonheads, onlyheads)
171 og._common, allmissing = sets
175 og._common, allmissing = sets
172 og._missing = missing = []
176 og._missing = missing = []
173 og.excluded = excluded = []
177 og.excluded = excluded = []
174 for node in allmissing:
178 for node in allmissing:
175 ctx = repo[node]
179 ctx = repo[node]
176 if ctx.phase() >= phases.secret or ctx.extinct():
180 if ctx.phase() >= phases.secret or ctx.extinct():
177 excluded.append(node)
181 excluded.append(node)
178 else:
182 else:
179 missing.append(node)
183 missing.append(node)
180 if len(missing) == len(allmissing):
184 if len(missing) == len(allmissing):
181 missingheads = onlyheads
185 missingheads = onlyheads
182 else: # update missing heads
186 else: # update missing heads
183 missingheads = phases.newheads(repo, onlyheads, excluded)
187 missingheads = phases.newheads(repo, onlyheads, excluded)
184 og.missingheads = missingheads
188 og.missingheads = missingheads
185 if portable:
189 if portable:
186 # recompute common and missingheads as if -r<rev> had been given for
190 # recompute common and missingheads as if -r<rev> had been given for
187 # each head of missing, and --base <rev> for each head of the proper
191 # each head of missing, and --base <rev> for each head of the proper
188 # ancestors of missing
192 # ancestors of missing
189 og._computecommonmissing()
193 og._computecommonmissing()
190 cl = repo.changelog
194 cl = repo.changelog
191 missingrevs = {cl.rev(n) for n in og._missing}
195 missingrevs = {cl.rev(n) for n in og._missing}
192 og._common = set(cl.ancestors(missingrevs)) - missingrevs
196 og._common = set(cl.ancestors(missingrevs)) - missingrevs
193 commonheads = set(og.commonheads)
197 commonheads = set(og.commonheads)
194 og.missingheads = [h for h in og.missingheads if h not in commonheads]
198 og.missingheads = [h for h in og.missingheads if h not in commonheads]
195
199
196 return og
200 return og
197
201
198
202
199 def _headssummary(pushop):
203 def _headssummary(pushop):
200 """compute a summary of branch and heads status before and after push
204 """compute a summary of branch and heads status before and after push
201
205
202 return {'branch': ([remoteheads], [newheads],
206 return {'branch': ([remoteheads], [newheads],
203 [unsyncedheads], [discardedheads])} mapping
207 [unsyncedheads], [discardedheads])} mapping
204
208
205 - branch: the branch name,
209 - branch: the branch name,
206 - remoteheads: the list of remote heads known locally
210 - remoteheads: the list of remote heads known locally
207 None if the branch is new,
211 None if the branch is new,
208 - newheads: the new remote heads (known locally) with outgoing pushed,
212 - newheads: the new remote heads (known locally) with outgoing pushed,
209 - unsyncedheads: the list of remote heads unknown locally,
213 - unsyncedheads: the list of remote heads unknown locally,
210 - discardedheads: the list of heads made obsolete by the push.
214 - discardedheads: the list of heads made obsolete by the push.
211 """
215 """
212 repo = pushop.repo.unfiltered()
216 repo = pushop.repo.unfiltered()
213 remote = pushop.remote
217 remote = pushop.remote
214 outgoing = pushop.outgoing
218 outgoing = pushop.outgoing
215 cl = repo.changelog
219 cl = repo.changelog
216 headssum = {}
220 headssum = {}
217 missingctx = set()
221 missingctx = set()
218 # A. Create set of branches involved in the push.
222 # A. Create set of branches involved in the push.
219 branches = set()
223 branches = set()
220 for n in outgoing.missing:
224 for n in outgoing.missing:
221 ctx = repo[n]
225 ctx = repo[n]
222 missingctx.add(ctx)
226 missingctx.add(ctx)
223 branches.add(ctx.branch())
227 branches.add(ctx.branch())
224
228
225 with remote.commandexecutor() as e:
229 with remote.commandexecutor() as e:
226 remotemap = e.callcommand(b'branchmap', {}).result()
230 remotemap = e.callcommand(b'branchmap', {}).result()
227
231
228 knownnode = cl.hasnode # do not use nodemap until it is filtered
232 knownnode = cl.hasnode # do not use nodemap until it is filtered
229 # A. register remote heads of branches which are in outgoing set
233 # A. register remote heads of branches which are in outgoing set
230 for branch, heads in pycompat.iteritems(remotemap):
234 for branch, heads in pycompat.iteritems(remotemap):
231 # don't add head info about branches which we don't have locally
235 # don't add head info about branches which we don't have locally
232 if branch not in branches:
236 if branch not in branches:
233 continue
237 continue
234 known = []
238 known = []
235 unsynced = []
239 unsynced = []
236 for h in heads:
240 for h in heads:
237 if knownnode(h):
241 if knownnode(h):
238 known.append(h)
242 known.append(h)
239 else:
243 else:
240 unsynced.append(h)
244 unsynced.append(h)
241 headssum[branch] = (known, list(known), unsynced)
245 headssum[branch] = (known, list(known), unsynced)
242
246
243 # B. add new branch data
247 # B. add new branch data
244 for branch in branches:
248 for branch in branches:
245 if branch not in headssum:
249 if branch not in headssum:
246 headssum[branch] = (None, [], [])
250 headssum[branch] = (None, [], [])
247
251
248 # C. Update newmap with outgoing changes.
252 # C. Update newmap with outgoing changes.
249 # This will possibly add new heads and remove existing ones.
253 # This will possibly add new heads and remove existing ones.
250 newmap = branchmap.remotebranchcache(
254 newmap = branchmap.remotebranchcache(
251 (branch, heads[1])
255 (branch, heads[1])
252 for branch, heads in pycompat.iteritems(headssum)
256 for branch, heads in pycompat.iteritems(headssum)
253 if heads[0] is not None
257 if heads[0] is not None
254 )
258 )
255 newmap.update(repo, (ctx.rev() for ctx in missingctx))
259 newmap.update(repo, (ctx.rev() for ctx in missingctx))
256 for branch, newheads in pycompat.iteritems(newmap):
260 for branch, newheads in pycompat.iteritems(newmap):
257 headssum[branch][1][:] = newheads
261 headssum[branch][1][:] = newheads
258 for branch, items in pycompat.iteritems(headssum):
262 for branch, items in pycompat.iteritems(headssum):
259 for l in items:
263 for l in items:
260 if l is not None:
264 if l is not None:
261 l.sort()
265 l.sort()
262 headssum[branch] = items + ([],)
266 headssum[branch] = items + ([],)
263
267
264 # If there are no obsstore, no post processing are needed.
268 # If there are no obsstore, no post processing are needed.
265 if repo.obsstore:
269 if repo.obsstore:
266 torev = repo.changelog.rev
270 torev = repo.changelog.rev
267 futureheads = {torev(h) for h in outgoing.missingheads}
271 futureheads = {torev(h) for h in outgoing.missingheads}
268 futureheads |= {torev(h) for h in outgoing.commonheads}
272 futureheads |= {torev(h) for h in outgoing.commonheads}
269 allfuturecommon = repo.changelog.ancestors(futureheads, inclusive=True)
273 allfuturecommon = repo.changelog.ancestors(futureheads, inclusive=True)
270 for branch, heads in sorted(pycompat.iteritems(headssum)):
274 for branch, heads in sorted(pycompat.iteritems(headssum)):
271 remoteheads, newheads, unsyncedheads, placeholder = heads
275 remoteheads, newheads, unsyncedheads, placeholder = heads
272 result = _postprocessobsolete(pushop, allfuturecommon, newheads)
276 result = _postprocessobsolete(pushop, allfuturecommon, newheads)
273 headssum[branch] = (
277 headssum[branch] = (
274 remoteheads,
278 remoteheads,
275 sorted(result[0]),
279 sorted(result[0]),
276 unsyncedheads,
280 unsyncedheads,
277 sorted(result[1]),
281 sorted(result[1]),
278 )
282 )
279 return headssum
283 return headssum
280
284
281
285
282 def _oldheadssummary(repo, remoteheads, outgoing, inc=False):
286 def _oldheadssummary(repo, remoteheads, outgoing, inc=False):
283 """Compute branchmapsummary for repo without branchmap support"""
287 """Compute branchmapsummary for repo without branchmap support"""
284
288
285 # 1-4b. old servers: Check for new topological heads.
289 # 1-4b. old servers: Check for new topological heads.
286 # Construct {old,new}map with branch = None (topological branch).
290 # Construct {old,new}map with branch = None (topological branch).
287 # (code based on update)
291 # (code based on update)
288 knownnode = repo.changelog.hasnode # no nodemap until it is filtered
292 knownnode = repo.changelog.hasnode # no nodemap until it is filtered
289 oldheads = sorted(h for h in remoteheads if knownnode(h))
293 oldheads = sorted(h for h in remoteheads if knownnode(h))
290 # all nodes in outgoing.missing are children of either:
294 # all nodes in outgoing.missing are children of either:
291 # - an element of oldheads
295 # - an element of oldheads
292 # - another element of outgoing.missing
296 # - another element of outgoing.missing
293 # - nullrev
297 # - nullrev
294 # This explains why the new head are very simple to compute.
298 # This explains why the new head are very simple to compute.
295 r = repo.set(b'heads(%ln + %ln)', oldheads, outgoing.missing)
299 r = repo.set(b'heads(%ln + %ln)', oldheads, outgoing.missing)
296 newheads = sorted(c.node() for c in r)
300 newheads = sorted(c.node() for c in r)
297 # set some unsynced head to issue the "unsynced changes" warning
301 # set some unsynced head to issue the "unsynced changes" warning
298 if inc:
302 if inc:
299 unsynced = [None]
303 unsynced = [None]
300 else:
304 else:
301 unsynced = []
305 unsynced = []
302 return {None: (oldheads, newheads, unsynced, [])}
306 return {None: (oldheads, newheads, unsynced, [])}
303
307
304
308
305 def _nowarnheads(pushop):
309 def _nowarnheads(pushop):
306 # Compute newly pushed bookmarks. We don't warn about bookmarked heads.
310 # Compute newly pushed bookmarks. We don't warn about bookmarked heads.
307 repo = pushop.repo.unfiltered()
311 repo = pushop.repo.unfiltered()
308 remote = pushop.remote
312 remote = pushop.remote
309 localbookmarks = repo._bookmarks
313 localbookmarks = repo._bookmarks
310
314
311 with remote.commandexecutor() as e:
315 with remote.commandexecutor() as e:
312 remotebookmarks = e.callcommand(
316 remotebookmarks = e.callcommand(
313 b'listkeys', {b'namespace': b'bookmarks',}
317 b'listkeys', {b'namespace': b'bookmarks',}
314 ).result()
318 ).result()
315
319
316 bookmarkedheads = set()
320 bookmarkedheads = set()
317
321
318 # internal config: bookmarks.pushing
322 # internal config: bookmarks.pushing
319 newbookmarks = [
323 newbookmarks = [
320 localbookmarks.expandname(b)
324 localbookmarks.expandname(b)
321 for b in pushop.ui.configlist(b'bookmarks', b'pushing')
325 for b in pushop.ui.configlist(b'bookmarks', b'pushing')
322 ]
326 ]
323
327
324 for bm in localbookmarks:
328 for bm in localbookmarks:
325 rnode = remotebookmarks.get(bm)
329 rnode = remotebookmarks.get(bm)
326 if rnode and rnode in repo:
330 if rnode and rnode in repo:
327 lctx, rctx = repo[localbookmarks[bm]], repo[rnode]
331 lctx, rctx = repo[localbookmarks[bm]], repo[rnode]
328 if bookmarks.validdest(repo, rctx, lctx):
332 if bookmarks.validdest(repo, rctx, lctx):
329 bookmarkedheads.add(lctx.node())
333 bookmarkedheads.add(lctx.node())
330 else:
334 else:
331 if bm in newbookmarks and bm not in remotebookmarks:
335 if bm in newbookmarks and bm not in remotebookmarks:
332 bookmarkedheads.add(localbookmarks[bm])
336 bookmarkedheads.add(localbookmarks[bm])
333
337
334 return bookmarkedheads
338 return bookmarkedheads
335
339
336
340
337 def checkheads(pushop):
341 def checkheads(pushop):
338 """Check that a push won't add any outgoing head
342 """Check that a push won't add any outgoing head
339
343
340 raise Abort error and display ui message as needed.
344 raise Abort error and display ui message as needed.
341 """
345 """
342
346
343 repo = pushop.repo.unfiltered()
347 repo = pushop.repo.unfiltered()
344 remote = pushop.remote
348 remote = pushop.remote
345 outgoing = pushop.outgoing
349 outgoing = pushop.outgoing
346 remoteheads = pushop.remoteheads
350 remoteheads = pushop.remoteheads
347 newbranch = pushop.newbranch
351 newbranch = pushop.newbranch
348 inc = bool(pushop.incoming)
352 inc = bool(pushop.incoming)
349
353
350 # Check for each named branch if we're creating new remote heads.
354 # Check for each named branch if we're creating new remote heads.
351 # To be a remote head after push, node must be either:
355 # To be a remote head after push, node must be either:
352 # - unknown locally
356 # - unknown locally
353 # - a local outgoing head descended from update
357 # - a local outgoing head descended from update
354 # - a remote head that's known locally and not
358 # - a remote head that's known locally and not
355 # ancestral to an outgoing head
359 # ancestral to an outgoing head
356 if remoteheads == [nullid]:
360 if remoteheads == [nullid]:
357 # remote is empty, nothing to check.
361 # remote is empty, nothing to check.
358 return
362 return
359
363
360 if remote.capable(b'branchmap'):
364 if remote.capable(b'branchmap'):
361 headssum = _headssummary(pushop)
365 headssum = _headssummary(pushop)
362 else:
366 else:
363 headssum = _oldheadssummary(repo, remoteheads, outgoing, inc)
367 headssum = _oldheadssummary(repo, remoteheads, outgoing, inc)
364 pushop.pushbranchmap = headssum
368 pushop.pushbranchmap = headssum
365 newbranches = [
369 newbranches = [
366 branch
370 branch
367 for branch, heads in pycompat.iteritems(headssum)
371 for branch, heads in pycompat.iteritems(headssum)
368 if heads[0] is None
372 if heads[0] is None
369 ]
373 ]
370 # 1. Check for new branches on the remote.
374 # 1. Check for new branches on the remote.
371 if newbranches and not newbranch: # new branch requires --new-branch
375 if newbranches and not newbranch: # new branch requires --new-branch
372 branchnames = b', '.join(sorted(newbranches))
376 branchnames = b', '.join(sorted(newbranches))
373 # Calculate how many of the new branches are closed branches
377 # Calculate how many of the new branches are closed branches
374 closedbranches = set()
378 closedbranches = set()
375 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
379 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
376 if isclosed:
380 if isclosed:
377 closedbranches.add(tag)
381 closedbranches.add(tag)
378 closedbranches = closedbranches & set(newbranches)
382 closedbranches = closedbranches & set(newbranches)
379 if closedbranches:
383 if closedbranches:
380 errmsg = _(b"push creates new remote branches: %s (%d closed)!") % (
384 errmsg = _(b"push creates new remote branches: %s (%d closed)!") % (
381 branchnames,
385 branchnames,
382 len(closedbranches),
386 len(closedbranches),
383 )
387 )
384 else:
388 else:
385 errmsg = _(b"push creates new remote branches: %s!") % branchnames
389 errmsg = _(b"push creates new remote branches: %s!") % branchnames
386 hint = _(b"use 'hg push --new-branch' to create new remote branches")
390 hint = _(b"use 'hg push --new-branch' to create new remote branches")
387 raise error.Abort(errmsg, hint=hint)
391 raise error.Abort(errmsg, hint=hint)
388
392
389 # 2. Find heads that we need not warn about
393 # 2. Find heads that we need not warn about
390 nowarnheads = _nowarnheads(pushop)
394 nowarnheads = _nowarnheads(pushop)
391
395
392 # 3. Check for new heads.
396 # 3. Check for new heads.
393 # If there are more heads after the push than before, a suitable
397 # If there are more heads after the push than before, a suitable
394 # error message, depending on unsynced status, is displayed.
398 # error message, depending on unsynced status, is displayed.
395 errormsg = None
399 errormsg = None
396 for branch, heads in sorted(pycompat.iteritems(headssum)):
400 for branch, heads in sorted(pycompat.iteritems(headssum)):
397 remoteheads, newheads, unsyncedheads, discardedheads = heads
401 remoteheads, newheads, unsyncedheads, discardedheads = heads
398 # add unsynced data
402 # add unsynced data
399 if remoteheads is None:
403 if remoteheads is None:
400 oldhs = set()
404 oldhs = set()
401 else:
405 else:
402 oldhs = set(remoteheads)
406 oldhs = set(remoteheads)
403 oldhs.update(unsyncedheads)
407 oldhs.update(unsyncedheads)
404 dhs = None # delta heads, the new heads on branch
408 dhs = None # delta heads, the new heads on branch
405 newhs = set(newheads)
409 newhs = set(newheads)
406 newhs.update(unsyncedheads)
410 newhs.update(unsyncedheads)
407 if unsyncedheads:
411 if unsyncedheads:
408 if None in unsyncedheads:
412 if None in unsyncedheads:
409 # old remote, no heads data
413 # old remote, no heads data
410 heads = None
414 heads = None
411 else:
415 else:
412 heads = scmutil.nodesummaries(repo, unsyncedheads)
416 heads = scmutil.nodesummaries(repo, unsyncedheads)
413 if heads is None:
417 if heads is None:
414 repo.ui.status(
418 repo.ui.status(
415 _(b"remote has heads that are not known locally\n")
419 _(b"remote has heads that are not known locally\n")
416 )
420 )
417 elif branch is None:
421 elif branch is None:
418 repo.ui.status(
422 repo.ui.status(
419 _(b"remote has heads that are not known locally: %s\n")
423 _(b"remote has heads that are not known locally: %s\n")
420 % heads
424 % heads
421 )
425 )
422 else:
426 else:
423 repo.ui.status(
427 repo.ui.status(
424 _(
428 _(
425 b"remote has heads on branch '%s' that are "
429 b"remote has heads on branch '%s' that are "
426 b"not known locally: %s\n"
430 b"not known locally: %s\n"
427 )
431 )
428 % (branch, heads)
432 % (branch, heads)
429 )
433 )
430 if remoteheads is None:
434 if remoteheads is None:
431 if len(newhs) > 1:
435 if len(newhs) > 1:
432 dhs = list(newhs)
436 dhs = list(newhs)
433 if errormsg is None:
437 if errormsg is None:
434 errormsg = (
438 errormsg = (
435 _(b"push creates new branch '%s' with multiple heads")
439 _(b"push creates new branch '%s' with multiple heads")
436 % branch
440 % branch
437 )
441 )
438 hint = _(
442 hint = _(
439 b"merge or"
443 b"merge or"
440 b" see 'hg help push' for details about"
444 b" see 'hg help push' for details about"
441 b" pushing new heads"
445 b" pushing new heads"
442 )
446 )
443 elif len(newhs) > len(oldhs):
447 elif len(newhs) > len(oldhs):
444 # remove bookmarked or existing remote heads from the new heads list
448 # remove bookmarked or existing remote heads from the new heads list
445 dhs = sorted(newhs - nowarnheads - oldhs)
449 dhs = sorted(newhs - nowarnheads - oldhs)
446 if dhs:
450 if dhs:
447 if errormsg is None:
451 if errormsg is None:
448 if branch not in (b'default', None):
452 if branch not in (b'default', None):
449 errormsg = _(
453 errormsg = _(
450 b"push creates new remote head %s on branch '%s'!"
454 b"push creates new remote head %s on branch '%s'!"
451 ) % (short(dhs[0]), branch,)
455 ) % (short(dhs[0]), branch,)
452 elif repo[dhs[0]].bookmarks():
456 elif repo[dhs[0]].bookmarks():
453 errormsg = _(
457 errormsg = _(
454 b"push creates new remote head %s "
458 b"push creates new remote head %s "
455 b"with bookmark '%s'!"
459 b"with bookmark '%s'!"
456 ) % (short(dhs[0]), repo[dhs[0]].bookmarks()[0])
460 ) % (short(dhs[0]), repo[dhs[0]].bookmarks()[0])
457 else:
461 else:
458 errormsg = _(b"push creates new remote head %s!") % short(
462 errormsg = _(b"push creates new remote head %s!") % short(
459 dhs[0]
463 dhs[0]
460 )
464 )
461 if unsyncedheads:
465 if unsyncedheads:
462 hint = _(
466 hint = _(
463 b"pull and merge or"
467 b"pull and merge or"
464 b" see 'hg help push' for details about"
468 b" see 'hg help push' for details about"
465 b" pushing new heads"
469 b" pushing new heads"
466 )
470 )
467 else:
471 else:
468 hint = _(
472 hint = _(
469 b"merge or"
473 b"merge or"
470 b" see 'hg help push' for details about"
474 b" see 'hg help push' for details about"
471 b" pushing new heads"
475 b" pushing new heads"
472 )
476 )
473 if branch is None:
477 if branch is None:
474 repo.ui.note(_(b"new remote heads:\n"))
478 repo.ui.note(_(b"new remote heads:\n"))
475 else:
479 else:
476 repo.ui.note(_(b"new remote heads on branch '%s':\n") % branch)
480 repo.ui.note(_(b"new remote heads on branch '%s':\n") % branch)
477 for h in dhs:
481 for h in dhs:
478 repo.ui.note(b" %s\n" % short(h))
482 repo.ui.note(b" %s\n" % short(h))
479 if errormsg:
483 if errormsg:
480 raise error.Abort(errormsg, hint=hint)
484 raise error.Abort(errormsg, hint=hint)
481
485
482
486
483 def _postprocessobsolete(pushop, futurecommon, candidate_newhs):
487 def _postprocessobsolete(pushop, futurecommon, candidate_newhs):
484 """post process the list of new heads with obsolescence information
488 """post process the list of new heads with obsolescence information
485
489
486 Exists as a sub-function to contain the complexity and allow extensions to
490 Exists as a sub-function to contain the complexity and allow extensions to
487 experiment with smarter logic.
491 experiment with smarter logic.
488
492
489 Returns (newheads, discarded_heads) tuple
493 Returns (newheads, discarded_heads) tuple
490 """
494 """
491 # known issue
495 # known issue
492 #
496 #
493 # * We "silently" skip processing on all changeset unknown locally
497 # * We "silently" skip processing on all changeset unknown locally
494 #
498 #
495 # * if <nh> is public on the remote, it won't be affected by obsolete
499 # * if <nh> is public on the remote, it won't be affected by obsolete
496 # marker and a new is created
500 # marker and a new is created
497
501
498 # define various utilities and containers
502 # define various utilities and containers
499 repo = pushop.repo
503 repo = pushop.repo
500 unfi = repo.unfiltered()
504 unfi = repo.unfiltered()
501 torev = unfi.changelog.index.get_rev
505 torev = unfi.changelog.index.get_rev
502 public = phases.public
506 public = phases.public
503 getphase = unfi._phasecache.phase
507 getphase = unfi._phasecache.phase
504 ispublic = lambda r: getphase(unfi, r) == public
508 ispublic = lambda r: getphase(unfi, r) == public
505 ispushed = lambda n: torev(n) in futurecommon
509 ispushed = lambda n: torev(n) in futurecommon
506 hasoutmarker = functools.partial(pushingmarkerfor, unfi.obsstore, ispushed)
510 hasoutmarker = functools.partial(pushingmarkerfor, unfi.obsstore, ispushed)
507 successorsmarkers = unfi.obsstore.successors
511 successorsmarkers = unfi.obsstore.successors
508 newhs = set() # final set of new heads
512 newhs = set() # final set of new heads
509 discarded = set() # new head of fully replaced branch
513 discarded = set() # new head of fully replaced branch
510
514
511 localcandidate = set() # candidate heads known locally
515 localcandidate = set() # candidate heads known locally
512 unknownheads = set() # candidate heads unknown locally
516 unknownheads = set() # candidate heads unknown locally
513 for h in candidate_newhs:
517 for h in candidate_newhs:
514 if h in unfi:
518 if h in unfi:
515 localcandidate.add(h)
519 localcandidate.add(h)
516 else:
520 else:
517 if successorsmarkers.get(h) is not None:
521 if successorsmarkers.get(h) is not None:
518 msg = (
522 msg = (
519 b'checkheads: remote head unknown locally has'
523 b'checkheads: remote head unknown locally has'
520 b' local marker: %s\n'
524 b' local marker: %s\n'
521 )
525 )
522 repo.ui.debug(msg % hex(h))
526 repo.ui.debug(msg % hex(h))
523 unknownheads.add(h)
527 unknownheads.add(h)
524
528
525 # fast path the simple case
529 # fast path the simple case
526 if len(localcandidate) == 1:
530 if len(localcandidate) == 1:
527 return unknownheads | set(candidate_newhs), set()
531 return unknownheads | set(candidate_newhs), set()
528
532
529 # actually process branch replacement
533 # actually process branch replacement
530 while localcandidate:
534 while localcandidate:
531 nh = localcandidate.pop()
535 nh = localcandidate.pop()
532 current_branch = unfi[nh].branch()
536 current_branch = unfi[nh].branch()
533 # run this check early to skip the evaluation of the whole branch
537 # run this check early to skip the evaluation of the whole branch
534 if torev(nh) in futurecommon or ispublic(torev(nh)):
538 if torev(nh) in futurecommon or ispublic(torev(nh)):
535 newhs.add(nh)
539 newhs.add(nh)
536 continue
540 continue
537
541
538 # Get all revs/nodes on the branch exclusive to this head
542 # Get all revs/nodes on the branch exclusive to this head
539 # (already filtered heads are "ignored"))
543 # (already filtered heads are "ignored"))
540 branchrevs = unfi.revs(
544 branchrevs = unfi.revs(
541 b'only(%n, (%ln+%ln))', nh, localcandidate, newhs
545 b'only(%n, (%ln+%ln))', nh, localcandidate, newhs
542 )
546 )
543
547
544 branchnodes = []
548 branchnodes = []
545 for r in branchrevs:
549 for r in branchrevs:
546 c = unfi[r]
550 c = unfi[r]
547 if c.branch() == current_branch:
551 if c.branch() == current_branch:
548 branchnodes.append(c.node())
552 branchnodes.append(c.node())
549
553
550 # The branch won't be hidden on the remote if
554 # The branch won't be hidden on the remote if
551 # * any part of it is public,
555 # * any part of it is public,
552 # * any part of it is considered part of the result by previous logic,
556 # * any part of it is considered part of the result by previous logic,
553 # * if we have no markers to push to obsolete it.
557 # * if we have no markers to push to obsolete it.
554 if (
558 if (
555 any(ispublic(r) for r in branchrevs)
559 any(ispublic(r) for r in branchrevs)
556 or any(torev(n) in futurecommon for n in branchnodes)
560 or any(torev(n) in futurecommon for n in branchnodes)
557 or any(not hasoutmarker(n) for n in branchnodes)
561 or any(not hasoutmarker(n) for n in branchnodes)
558 ):
562 ):
559 newhs.add(nh)
563 newhs.add(nh)
560 else:
564 else:
561 # note: there is a corner case if there is a merge in the branch.
565 # note: there is a corner case if there is a merge in the branch.
562 # we might end up with -more- heads. However, these heads are not
566 # we might end up with -more- heads. However, these heads are not
563 # "added" by the push, but more by the "removal" on the remote so I
567 # "added" by the push, but more by the "removal" on the remote so I
564 # think is a okay to ignore them,
568 # think is a okay to ignore them,
565 discarded.add(nh)
569 discarded.add(nh)
566 newhs |= unknownheads
570 newhs |= unknownheads
567 return newhs, discarded
571 return newhs, discarded
568
572
569
573
570 def pushingmarkerfor(obsstore, ispushed, node):
574 def pushingmarkerfor(obsstore, ispushed, node):
571 """true if some markers are to be pushed for node
575 """true if some markers are to be pushed for node
572
576
573 We cannot just look in to the pushed obsmarkers from the pushop because
577 We cannot just look in to the pushed obsmarkers from the pushop because
574 discovery might have filtered relevant markers. In addition listing all
578 discovery might have filtered relevant markers. In addition listing all
575 markers relevant to all changesets in the pushed set would be too expensive
579 markers relevant to all changesets in the pushed set would be too expensive
576 (O(len(repo)))
580 (O(len(repo)))
577
581
578 (note: There are cache opportunity in this function. but it would requires
582 (note: There are cache opportunity in this function. but it would requires
579 a two dimensional stack.)
583 a two dimensional stack.)
580 """
584 """
581 successorsmarkers = obsstore.successors
585 successorsmarkers = obsstore.successors
582 stack = [node]
586 stack = [node]
583 seen = set(stack)
587 seen = set(stack)
584 while stack:
588 while stack:
585 current = stack.pop()
589 current = stack.pop()
586 if ispushed(current):
590 if ispushed(current):
587 return True
591 return True
588 markers = successorsmarkers.get(current, ())
592 markers = successorsmarkers.get(current, ())
589 # markers fields = ('prec', 'succs', 'flag', 'meta', 'date', 'parents')
593 # markers fields = ('prec', 'succs', 'flag', 'meta', 'date', 'parents')
590 for m in markers:
594 for m in markers:
591 nexts = m[1] # successors
595 nexts = m[1] # successors
592 if not nexts: # this is a prune marker
596 if not nexts: # this is a prune marker
593 nexts = m[5] or () # parents
597 nexts = m[5] or () # parents
594 for n in nexts:
598 for n in nexts:
595 if n not in seen:
599 if n not in seen:
596 seen.add(n)
600 seen.add(n)
597 stack.append(n)
601 stack.append(n)
598 return False
602 return False
@@ -1,628 +1,633 b''
1 # nodemap.py - nodemap related code and utilities
1 # nodemap.py - nodemap related code and utilities
2 #
2 #
3 # Copyright 2019 Pierre-Yves David <pierre-yves.david@octobus.net>
3 # Copyright 2019 Pierre-Yves David <pierre-yves.david@octobus.net>
4 # Copyright 2019 George Racinet <georges.racinet@octobus.net>
4 # Copyright 2019 George Racinet <georges.racinet@octobus.net>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14 import struct
14 import struct
15
15
16 from .. import (
16 from .. import (
17 error,
17 error,
18 node as nodemod,
18 node as nodemod,
19 util,
19 util,
20 )
20 )
21
21
22
22
23 class NodeMap(dict):
23 class NodeMap(dict):
24 def __missing__(self, x):
24 def __missing__(self, x):
25 raise error.RevlogError(b'unknown node: %s' % x)
25 raise error.RevlogError(b'unknown node: %s' % x)
26
26
27
27
28 def persisted_data(revlog):
28 def persisted_data(revlog):
29 """read the nodemap for a revlog from disk"""
29 """read the nodemap for a revlog from disk"""
30 if revlog.nodemap_file is None:
30 if revlog.nodemap_file is None:
31 return None
31 return None
32 pdata = revlog.opener.tryread(revlog.nodemap_file)
32 pdata = revlog.opener.tryread(revlog.nodemap_file)
33 if not pdata:
33 if not pdata:
34 return None
34 return None
35 offset = 0
35 offset = 0
36 (version,) = S_VERSION.unpack(pdata[offset : offset + S_VERSION.size])
36 (version,) = S_VERSION.unpack(pdata[offset : offset + S_VERSION.size])
37 if version != ONDISK_VERSION:
37 if version != ONDISK_VERSION:
38 return None
38 return None
39 offset += S_VERSION.size
39 offset += S_VERSION.size
40 headers = S_HEADER.unpack(pdata[offset : offset + S_HEADER.size])
40 headers = S_HEADER.unpack(pdata[offset : offset + S_HEADER.size])
41 uid_size, tip_rev, data_length, data_unused, tip_node_size = headers
41 uid_size, tip_rev, data_length, data_unused, tip_node_size = headers
42 offset += S_HEADER.size
42 offset += S_HEADER.size
43 docket = NodeMapDocket(pdata[offset : offset + uid_size])
43 docket = NodeMapDocket(pdata[offset : offset + uid_size])
44 offset += uid_size
44 offset += uid_size
45 docket.tip_rev = tip_rev
45 docket.tip_rev = tip_rev
46 docket.tip_node = pdata[offset : offset + tip_node_size]
46 docket.tip_node = pdata[offset : offset + tip_node_size]
47 docket.data_length = data_length
47 docket.data_length = data_length
48 docket.data_unused = data_unused
48 docket.data_unused = data_unused
49
49
50 filename = _rawdata_filepath(revlog, docket)
50 filename = _rawdata_filepath(revlog, docket)
51 use_mmap = revlog.opener.options.get(b"exp-persistent-nodemap.mmap")
51 use_mmap = revlog.opener.options.get(b"exp-persistent-nodemap.mmap")
52 try:
52 try:
53 with revlog.opener(filename) as fd:
53 with revlog.opener(filename) as fd:
54 if use_mmap:
54 if use_mmap:
55 data = util.buffer(util.mmapread(fd, data_length))
55 data = util.buffer(util.mmapread(fd, data_length))
56 else:
56 else:
57 data = fd.read(data_length)
57 data = fd.read(data_length)
58 except OSError as e:
58 except OSError as e:
59 if e.errno != errno.ENOENT:
59 if e.errno != errno.ENOENT:
60 raise
60 raise
61 if len(data) < data_length:
61 if len(data) < data_length:
62 return None
62 return None
63 return docket, data
63 return docket, data
64
64
65
65
66 def setup_persistent_nodemap(tr, revlog):
66 def setup_persistent_nodemap(tr, revlog):
67 """Install whatever is needed transaction side to persist a nodemap on disk
67 """Install whatever is needed transaction side to persist a nodemap on disk
68
68
69 (only actually persist the nodemap if this is relevant for this revlog)
69 (only actually persist the nodemap if this is relevant for this revlog)
70 """
70 """
71 if revlog._inline:
71 if revlog._inline:
72 return # inlined revlog are too small for this to be relevant
72 return # inlined revlog are too small for this to be relevant
73 if revlog.nodemap_file is None:
73 if revlog.nodemap_file is None:
74 return # we do not use persistent_nodemap on this revlog
74 return # we do not use persistent_nodemap on this revlog
75
75
76 # we need to happen after the changelog finalization, in that use "cl-"
76 # we need to happen after the changelog finalization, in that use "cl-"
77 callback_id = b"nm-revlog-persistent-nodemap-%s" % revlog.nodemap_file
77 callback_id = b"nm-revlog-persistent-nodemap-%s" % revlog.nodemap_file
78 if tr.hasfinalize(callback_id):
78 if tr.hasfinalize(callback_id):
79 return # no need to register again
79 return # no need to register again
80 tr.addpending(
80 tr.addpending(
81 callback_id, lambda tr: _persist_nodemap(tr, revlog, pending=True)
81 callback_id, lambda tr: _persist_nodemap(tr, revlog, pending=True)
82 )
82 )
83 tr.addfinalize(callback_id, lambda tr: _persist_nodemap(tr, revlog))
83 tr.addfinalize(callback_id, lambda tr: _persist_nodemap(tr, revlog))
84
84
85
85
86 class _NoTransaction(object):
86 class _NoTransaction(object):
87 """transaction like object to update the nodemap outside a transaction
87 """transaction like object to update the nodemap outside a transaction
88 """
88 """
89
89
90 def __init__(self):
90 def __init__(self):
91 self._postclose = {}
91 self._postclose = {}
92
92
93 def addpostclose(self, callback_id, callback_func):
93 def addpostclose(self, callback_id, callback_func):
94 self._postclose[callback_id] = callback_func
94 self._postclose[callback_id] = callback_func
95
95
96 def registertmp(self, *args, **kwargs):
96 def registertmp(self, *args, **kwargs):
97 pass
97 pass
98
98
99 def addbackup(self, *args, **kwargs):
99 def addbackup(self, *args, **kwargs):
100 pass
100 pass
101
101
102 def add(self, *args, **kwargs):
102 def add(self, *args, **kwargs):
103 pass
103 pass
104
104
105 def addabort(self, *args, **kwargs):
105 def addabort(self, *args, **kwargs):
106 pass
106 pass
107
107
108
108
109 def update_persistent_nodemap(revlog):
109 def update_persistent_nodemap(revlog):
110 """update the persistent nodemap right now
110 """update the persistent nodemap right now
111
111
112 To be used for updating the nodemap on disk outside of a normal transaction
112 To be used for updating the nodemap on disk outside of a normal transaction
113 setup (eg, `debugupdatecache`).
113 setup (eg, `debugupdatecache`).
114 """
114 """
115 if revlog._inline:
116 return # inlined revlog are too small for this to be relevant
117 if revlog.nodemap_file is None:
118 return # we do not use persistent_nodemap on this revlog
119
115 notr = _NoTransaction()
120 notr = _NoTransaction()
116 _persist_nodemap(notr, revlog)
121 _persist_nodemap(notr, revlog)
117 for k in sorted(notr._postclose):
122 for k in sorted(notr._postclose):
118 notr._postclose[k](None)
123 notr._postclose[k](None)
119
124
120
125
121 def _persist_nodemap(tr, revlog, pending=False):
126 def _persist_nodemap(tr, revlog, pending=False):
122 """Write nodemap data on disk for a given revlog
127 """Write nodemap data on disk for a given revlog
123 """
128 """
124 if getattr(revlog, 'filteredrevs', ()):
129 if getattr(revlog, 'filteredrevs', ()):
125 raise error.ProgrammingError(
130 raise error.ProgrammingError(
126 "cannot persist nodemap of a filtered changelog"
131 "cannot persist nodemap of a filtered changelog"
127 )
132 )
128 if revlog.nodemap_file is None:
133 if revlog.nodemap_file is None:
129 msg = "calling persist nodemap on a revlog without the feature enableb"
134 msg = "calling persist nodemap on a revlog without the feature enableb"
130 raise error.ProgrammingError(msg)
135 raise error.ProgrammingError(msg)
131
136
132 can_incremental = util.safehasattr(revlog.index, "nodemap_data_incremental")
137 can_incremental = util.safehasattr(revlog.index, "nodemap_data_incremental")
133 ondisk_docket = revlog._nodemap_docket
138 ondisk_docket = revlog._nodemap_docket
134 feed_data = util.safehasattr(revlog.index, "update_nodemap_data")
139 feed_data = util.safehasattr(revlog.index, "update_nodemap_data")
135 use_mmap = revlog.opener.options.get(b"exp-persistent-nodemap.mmap")
140 use_mmap = revlog.opener.options.get(b"exp-persistent-nodemap.mmap")
136
141
137 data = None
142 data = None
138 # first attemp an incremental update of the data
143 # first attemp an incremental update of the data
139 if can_incremental and ondisk_docket is not None:
144 if can_incremental and ondisk_docket is not None:
140 target_docket = revlog._nodemap_docket.copy()
145 target_docket = revlog._nodemap_docket.copy()
141 (
146 (
142 src_docket,
147 src_docket,
143 data_changed_count,
148 data_changed_count,
144 data,
149 data,
145 ) = revlog.index.nodemap_data_incremental()
150 ) = revlog.index.nodemap_data_incremental()
146 new_length = target_docket.data_length + len(data)
151 new_length = target_docket.data_length + len(data)
147 new_unused = target_docket.data_unused + data_changed_count
152 new_unused = target_docket.data_unused + data_changed_count
148 if src_docket != target_docket:
153 if src_docket != target_docket:
149 data = None
154 data = None
150 elif new_length <= (new_unused * 10): # under 10% of unused data
155 elif new_length <= (new_unused * 10): # under 10% of unused data
151 data = None
156 data = None
152 else:
157 else:
153 datafile = _rawdata_filepath(revlog, target_docket)
158 datafile = _rawdata_filepath(revlog, target_docket)
154 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
159 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
155 # store vfs
160 # store vfs
156 tr.add(datafile, target_docket.data_length)
161 tr.add(datafile, target_docket.data_length)
157 with revlog.opener(datafile, b'r+') as fd:
162 with revlog.opener(datafile, b'r+') as fd:
158 fd.seek(target_docket.data_length)
163 fd.seek(target_docket.data_length)
159 fd.write(data)
164 fd.write(data)
160 if feed_data:
165 if feed_data:
161 if use_mmap:
166 if use_mmap:
162 fd.seek(0)
167 fd.seek(0)
163 new_data = fd.read(new_length)
168 new_data = fd.read(new_length)
164 else:
169 else:
165 fd.flush()
170 fd.flush()
166 new_data = util.buffer(util.mmapread(fd, new_length))
171 new_data = util.buffer(util.mmapread(fd, new_length))
167 target_docket.data_length = new_length
172 target_docket.data_length = new_length
168 target_docket.data_unused = new_unused
173 target_docket.data_unused = new_unused
169
174
170 if data is None:
175 if data is None:
171 # otherwise fallback to a full new export
176 # otherwise fallback to a full new export
172 target_docket = NodeMapDocket()
177 target_docket = NodeMapDocket()
173 datafile = _rawdata_filepath(revlog, target_docket)
178 datafile = _rawdata_filepath(revlog, target_docket)
174 if util.safehasattr(revlog.index, "nodemap_data_all"):
179 if util.safehasattr(revlog.index, "nodemap_data_all"):
175 data = revlog.index.nodemap_data_all()
180 data = revlog.index.nodemap_data_all()
176 else:
181 else:
177 data = persistent_data(revlog.index)
182 data = persistent_data(revlog.index)
178 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
183 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
179 # store vfs
184 # store vfs
180
185
181 tryunlink = revlog.opener.tryunlink
186 tryunlink = revlog.opener.tryunlink
182
187
183 def abortck(tr):
188 def abortck(tr):
184 tryunlink(datafile)
189 tryunlink(datafile)
185
190
186 callback_id = b"delete-%s" % datafile
191 callback_id = b"delete-%s" % datafile
187
192
188 # some flavor of the transaction abort does not cleanup new file, it
193 # some flavor of the transaction abort does not cleanup new file, it
189 # simply empty them.
194 # simply empty them.
190 tr.addabort(callback_id, abortck)
195 tr.addabort(callback_id, abortck)
191 with revlog.opener(datafile, b'w+') as fd:
196 with revlog.opener(datafile, b'w+') as fd:
192 fd.write(data)
197 fd.write(data)
193 if feed_data:
198 if feed_data:
194 if use_mmap:
199 if use_mmap:
195 new_data = data
200 new_data = data
196 else:
201 else:
197 fd.flush()
202 fd.flush()
198 new_data = util.buffer(util.mmapread(fd, len(data)))
203 new_data = util.buffer(util.mmapread(fd, len(data)))
199 target_docket.data_length = len(data)
204 target_docket.data_length = len(data)
200 target_docket.tip_rev = revlog.tiprev()
205 target_docket.tip_rev = revlog.tiprev()
201 target_docket.tip_node = revlog.node(target_docket.tip_rev)
206 target_docket.tip_node = revlog.node(target_docket.tip_rev)
202 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
207 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
203 # store vfs
208 # store vfs
204 file_path = revlog.nodemap_file
209 file_path = revlog.nodemap_file
205 if pending:
210 if pending:
206 file_path += b'.a'
211 file_path += b'.a'
207 tr.registertmp(file_path)
212 tr.registertmp(file_path)
208 else:
213 else:
209 tr.addbackup(file_path)
214 tr.addbackup(file_path)
210
215
211 with revlog.opener(file_path, b'w', atomictemp=True) as fp:
216 with revlog.opener(file_path, b'w', atomictemp=True) as fp:
212 fp.write(target_docket.serialize())
217 fp.write(target_docket.serialize())
213 revlog._nodemap_docket = target_docket
218 revlog._nodemap_docket = target_docket
214 if feed_data:
219 if feed_data:
215 revlog.index.update_nodemap_data(target_docket, new_data)
220 revlog.index.update_nodemap_data(target_docket, new_data)
216
221
217 # search for old index file in all cases, some older process might have
222 # search for old index file in all cases, some older process might have
218 # left one behind.
223 # left one behind.
219 olds = _other_rawdata_filepath(revlog, target_docket)
224 olds = _other_rawdata_filepath(revlog, target_docket)
220 if olds:
225 if olds:
221 realvfs = getattr(revlog, '_realopener', revlog.opener)
226 realvfs = getattr(revlog, '_realopener', revlog.opener)
222
227
223 def cleanup(tr):
228 def cleanup(tr):
224 for oldfile in olds:
229 for oldfile in olds:
225 realvfs.tryunlink(oldfile)
230 realvfs.tryunlink(oldfile)
226
231
227 callback_id = b"revlog-cleanup-nodemap-%s" % revlog.nodemap_file
232 callback_id = b"revlog-cleanup-nodemap-%s" % revlog.nodemap_file
228 tr.addpostclose(callback_id, cleanup)
233 tr.addpostclose(callback_id, cleanup)
229
234
230
235
231 ### Nodemap docket file
236 ### Nodemap docket file
232 #
237 #
233 # The nodemap data are stored on disk using 2 files:
238 # The nodemap data are stored on disk using 2 files:
234 #
239 #
235 # * a raw data files containing a persistent nodemap
240 # * a raw data files containing a persistent nodemap
236 # (see `Nodemap Trie` section)
241 # (see `Nodemap Trie` section)
237 #
242 #
238 # * a small "docket" file containing medatadata
243 # * a small "docket" file containing medatadata
239 #
244 #
240 # While the nodemap data can be multiple tens of megabytes, the "docket" is
245 # While the nodemap data can be multiple tens of megabytes, the "docket" is
241 # small, it is easy to update it automatically or to duplicated its content
246 # small, it is easy to update it automatically or to duplicated its content
242 # during a transaction.
247 # during a transaction.
243 #
248 #
244 # Multiple raw data can exist at the same time (The currently valid one and a
249 # Multiple raw data can exist at the same time (The currently valid one and a
245 # new one beind used by an in progress transaction). To accomodate this, the
250 # new one beind used by an in progress transaction). To accomodate this, the
246 # filename hosting the raw data has a variable parts. The exact filename is
251 # filename hosting the raw data has a variable parts. The exact filename is
247 # specified inside the "docket" file.
252 # specified inside the "docket" file.
248 #
253 #
249 # The docket file contains information to find, qualify and validate the raw
254 # The docket file contains information to find, qualify and validate the raw
250 # data. Its content is currently very light, but it will expand as the on disk
255 # data. Its content is currently very light, but it will expand as the on disk
251 # nodemap gains the necessary features to be used in production.
256 # nodemap gains the necessary features to be used in production.
252
257
253 # version 0 is experimental, no BC garantee, do no use outside of tests.
258 # version 0 is experimental, no BC garantee, do no use outside of tests.
254 ONDISK_VERSION = 0
259 ONDISK_VERSION = 0
255 S_VERSION = struct.Struct(">B")
260 S_VERSION = struct.Struct(">B")
256 S_HEADER = struct.Struct(">BQQQQ")
261 S_HEADER = struct.Struct(">BQQQQ")
257
262
258 ID_SIZE = 8
263 ID_SIZE = 8
259
264
260
265
261 def _make_uid():
266 def _make_uid():
262 """return a new unique identifier.
267 """return a new unique identifier.
263
268
264 The identifier is random and composed of ascii characters."""
269 The identifier is random and composed of ascii characters."""
265 return nodemod.hex(os.urandom(ID_SIZE))
270 return nodemod.hex(os.urandom(ID_SIZE))
266
271
267
272
268 class NodeMapDocket(object):
273 class NodeMapDocket(object):
269 """metadata associated with persistent nodemap data
274 """metadata associated with persistent nodemap data
270
275
271 The persistent data may come from disk or be on their way to disk.
276 The persistent data may come from disk or be on their way to disk.
272 """
277 """
273
278
274 def __init__(self, uid=None):
279 def __init__(self, uid=None):
275 if uid is None:
280 if uid is None:
276 uid = _make_uid()
281 uid = _make_uid()
277 # a unique identifier for the data file:
282 # a unique identifier for the data file:
278 # - When new data are appended, it is preserved.
283 # - When new data are appended, it is preserved.
279 # - When a new data file is created, a new identifier is generated.
284 # - When a new data file is created, a new identifier is generated.
280 self.uid = uid
285 self.uid = uid
281 # the tipmost revision stored in the data file. This revision and all
286 # the tipmost revision stored in the data file. This revision and all
282 # revision before it are expected to be encoded in the data file.
287 # revision before it are expected to be encoded in the data file.
283 self.tip_rev = None
288 self.tip_rev = None
284 # the node of that tipmost revision, if it mismatch the current index
289 # the node of that tipmost revision, if it mismatch the current index
285 # data the docket is not valid for the current index and should be
290 # data the docket is not valid for the current index and should be
286 # discarded.
291 # discarded.
287 #
292 #
288 # note: this method is not perfect as some destructive operation could
293 # note: this method is not perfect as some destructive operation could
289 # preserve the same tip_rev + tip_node while altering lower revision.
294 # preserve the same tip_rev + tip_node while altering lower revision.
290 # However this multiple other caches have the same vulnerability (eg:
295 # However this multiple other caches have the same vulnerability (eg:
291 # brancmap cache).
296 # brancmap cache).
292 self.tip_node = None
297 self.tip_node = None
293 # the size (in bytes) of the persisted data to encode the nodemap valid
298 # the size (in bytes) of the persisted data to encode the nodemap valid
294 # for `tip_rev`.
299 # for `tip_rev`.
295 # - data file shorter than this are corrupted,
300 # - data file shorter than this are corrupted,
296 # - any extra data should be ignored.
301 # - any extra data should be ignored.
297 self.data_length = None
302 self.data_length = None
298 # the amount (in bytes) of "dead" data, still in the data file but no
303 # the amount (in bytes) of "dead" data, still in the data file but no
299 # longer used for the nodemap.
304 # longer used for the nodemap.
300 self.data_unused = 0
305 self.data_unused = 0
301
306
302 def copy(self):
307 def copy(self):
303 new = NodeMapDocket(uid=self.uid)
308 new = NodeMapDocket(uid=self.uid)
304 new.tip_rev = self.tip_rev
309 new.tip_rev = self.tip_rev
305 new.tip_node = self.tip_node
310 new.tip_node = self.tip_node
306 new.data_length = self.data_length
311 new.data_length = self.data_length
307 new.data_unused = self.data_unused
312 new.data_unused = self.data_unused
308 return new
313 return new
309
314
310 def __cmp__(self, other):
315 def __cmp__(self, other):
311 if self.uid < other.uid:
316 if self.uid < other.uid:
312 return -1
317 return -1
313 if self.uid > other.uid:
318 if self.uid > other.uid:
314 return 1
319 return 1
315 elif self.data_length < other.data_length:
320 elif self.data_length < other.data_length:
316 return -1
321 return -1
317 elif self.data_length > other.data_length:
322 elif self.data_length > other.data_length:
318 return 1
323 return 1
319 return 0
324 return 0
320
325
321 def __eq__(self, other):
326 def __eq__(self, other):
322 return self.uid == other.uid and self.data_length == other.data_length
327 return self.uid == other.uid and self.data_length == other.data_length
323
328
324 def serialize(self):
329 def serialize(self):
325 """return serialized bytes for a docket using the passed uid"""
330 """return serialized bytes for a docket using the passed uid"""
326 data = []
331 data = []
327 data.append(S_VERSION.pack(ONDISK_VERSION))
332 data.append(S_VERSION.pack(ONDISK_VERSION))
328 headers = (
333 headers = (
329 len(self.uid),
334 len(self.uid),
330 self.tip_rev,
335 self.tip_rev,
331 self.data_length,
336 self.data_length,
332 self.data_unused,
337 self.data_unused,
333 len(self.tip_node),
338 len(self.tip_node),
334 )
339 )
335 data.append(S_HEADER.pack(*headers))
340 data.append(S_HEADER.pack(*headers))
336 data.append(self.uid)
341 data.append(self.uid)
337 data.append(self.tip_node)
342 data.append(self.tip_node)
338 return b''.join(data)
343 return b''.join(data)
339
344
340
345
341 def _rawdata_filepath(revlog, docket):
346 def _rawdata_filepath(revlog, docket):
342 """The (vfs relative) nodemap's rawdata file for a given uid"""
347 """The (vfs relative) nodemap's rawdata file for a given uid"""
343 if revlog.nodemap_file.endswith(b'.n.a'):
348 if revlog.nodemap_file.endswith(b'.n.a'):
344 prefix = revlog.nodemap_file[:-4]
349 prefix = revlog.nodemap_file[:-4]
345 else:
350 else:
346 prefix = revlog.nodemap_file[:-2]
351 prefix = revlog.nodemap_file[:-2]
347 return b"%s-%s.nd" % (prefix, docket.uid)
352 return b"%s-%s.nd" % (prefix, docket.uid)
348
353
349
354
350 def _other_rawdata_filepath(revlog, docket):
355 def _other_rawdata_filepath(revlog, docket):
351 prefix = revlog.nodemap_file[:-2]
356 prefix = revlog.nodemap_file[:-2]
352 pattern = re.compile(br"(^|/)%s-[0-9a-f]+\.nd$" % prefix)
357 pattern = re.compile(br"(^|/)%s-[0-9a-f]+\.nd$" % prefix)
353 new_file_path = _rawdata_filepath(revlog, docket)
358 new_file_path = _rawdata_filepath(revlog, docket)
354 new_file_name = revlog.opener.basename(new_file_path)
359 new_file_name = revlog.opener.basename(new_file_path)
355 dirpath = revlog.opener.dirname(new_file_path)
360 dirpath = revlog.opener.dirname(new_file_path)
356 others = []
361 others = []
357 for f in revlog.opener.listdir(dirpath):
362 for f in revlog.opener.listdir(dirpath):
358 if pattern.match(f) and f != new_file_name:
363 if pattern.match(f) and f != new_file_name:
359 others.append(f)
364 others.append(f)
360 return others
365 return others
361
366
362
367
363 ### Nodemap Trie
368 ### Nodemap Trie
364 #
369 #
365 # This is a simple reference implementation to compute and persist a nodemap
370 # This is a simple reference implementation to compute and persist a nodemap
366 # trie. This reference implementation is write only. The python version of this
371 # trie. This reference implementation is write only. The python version of this
367 # is not expected to be actually used, since it wont provide performance
372 # is not expected to be actually used, since it wont provide performance
368 # improvement over existing non-persistent C implementation.
373 # improvement over existing non-persistent C implementation.
369 #
374 #
370 # The nodemap is persisted as Trie using 4bits-address/16-entries block. each
375 # The nodemap is persisted as Trie using 4bits-address/16-entries block. each
371 # revision can be adressed using its node shortest prefix.
376 # revision can be adressed using its node shortest prefix.
372 #
377 #
373 # The trie is stored as a sequence of block. Each block contains 16 entries
378 # The trie is stored as a sequence of block. Each block contains 16 entries
374 # (signed 64bit integer, big endian). Each entry can be one of the following:
379 # (signed 64bit integer, big endian). Each entry can be one of the following:
375 #
380 #
376 # * value >= 0 -> index of sub-block
381 # * value >= 0 -> index of sub-block
377 # * value == -1 -> no value
382 # * value == -1 -> no value
378 # * value < -1 -> a revision value: rev = -(value+10)
383 # * value < -1 -> a revision value: rev = -(value+10)
379 #
384 #
380 # The implementation focus on simplicity, not on performance. A Rust
385 # The implementation focus on simplicity, not on performance. A Rust
381 # implementation should provide a efficient version of the same binary
386 # implementation should provide a efficient version of the same binary
382 # persistence. This reference python implementation is never meant to be
387 # persistence. This reference python implementation is never meant to be
383 # extensively use in production.
388 # extensively use in production.
384
389
385
390
386 def persistent_data(index):
391 def persistent_data(index):
387 """return the persistent binary form for a nodemap for a given index
392 """return the persistent binary form for a nodemap for a given index
388 """
393 """
389 trie = _build_trie(index)
394 trie = _build_trie(index)
390 return _persist_trie(trie)
395 return _persist_trie(trie)
391
396
392
397
393 def update_persistent_data(index, root, max_idx, last_rev):
398 def update_persistent_data(index, root, max_idx, last_rev):
394 """return the incremental update for persistent nodemap from a given index
399 """return the incremental update for persistent nodemap from a given index
395 """
400 """
396 changed_block, trie = _update_trie(index, root, last_rev)
401 changed_block, trie = _update_trie(index, root, last_rev)
397 return (
402 return (
398 changed_block * S_BLOCK.size,
403 changed_block * S_BLOCK.size,
399 _persist_trie(trie, existing_idx=max_idx),
404 _persist_trie(trie, existing_idx=max_idx),
400 )
405 )
401
406
402
407
403 S_BLOCK = struct.Struct(">" + ("l" * 16))
408 S_BLOCK = struct.Struct(">" + ("l" * 16))
404
409
405 NO_ENTRY = -1
410 NO_ENTRY = -1
406 # rev 0 need to be -2 because 0 is used by block, -1 is a special value.
411 # rev 0 need to be -2 because 0 is used by block, -1 is a special value.
407 REV_OFFSET = 2
412 REV_OFFSET = 2
408
413
409
414
410 def _transform_rev(rev):
415 def _transform_rev(rev):
411 """Return the number used to represent the rev in the tree.
416 """Return the number used to represent the rev in the tree.
412
417
413 (or retrieve a rev number from such representation)
418 (or retrieve a rev number from such representation)
414
419
415 Note that this is an involution, a function equal to its inverse (i.e.
420 Note that this is an involution, a function equal to its inverse (i.e.
416 which gives the identity when applied to itself).
421 which gives the identity when applied to itself).
417 """
422 """
418 return -(rev + REV_OFFSET)
423 return -(rev + REV_OFFSET)
419
424
420
425
421 def _to_int(hex_digit):
426 def _to_int(hex_digit):
422 """turn an hexadecimal digit into a proper integer"""
427 """turn an hexadecimal digit into a proper integer"""
423 return int(hex_digit, 16)
428 return int(hex_digit, 16)
424
429
425
430
426 class Block(dict):
431 class Block(dict):
427 """represent a block of the Trie
432 """represent a block of the Trie
428
433
429 contains up to 16 entry indexed from 0 to 15"""
434 contains up to 16 entry indexed from 0 to 15"""
430
435
431 def __init__(self):
436 def __init__(self):
432 super(Block, self).__init__()
437 super(Block, self).__init__()
433 # If this block exist on disk, here is its ID
438 # If this block exist on disk, here is its ID
434 self.ondisk_id = None
439 self.ondisk_id = None
435
440
436 def __iter__(self):
441 def __iter__(self):
437 return iter(self.get(i) for i in range(16))
442 return iter(self.get(i) for i in range(16))
438
443
439
444
440 def _build_trie(index):
445 def _build_trie(index):
441 """build a nodemap trie
446 """build a nodemap trie
442
447
443 The nodemap stores revision number for each unique prefix.
448 The nodemap stores revision number for each unique prefix.
444
449
445 Each block is a dictionary with keys in `[0, 15]`. Values are either
450 Each block is a dictionary with keys in `[0, 15]`. Values are either
446 another block or a revision number.
451 another block or a revision number.
447 """
452 """
448 root = Block()
453 root = Block()
449 for rev in range(len(index)):
454 for rev in range(len(index)):
450 hex = nodemod.hex(index[rev][7])
455 hex = nodemod.hex(index[rev][7])
451 _insert_into_block(index, 0, root, rev, hex)
456 _insert_into_block(index, 0, root, rev, hex)
452 return root
457 return root
453
458
454
459
455 def _update_trie(index, root, last_rev):
460 def _update_trie(index, root, last_rev):
456 """consume"""
461 """consume"""
457 changed = 0
462 changed = 0
458 for rev in range(last_rev + 1, len(index)):
463 for rev in range(last_rev + 1, len(index)):
459 hex = nodemod.hex(index[rev][7])
464 hex = nodemod.hex(index[rev][7])
460 changed += _insert_into_block(index, 0, root, rev, hex)
465 changed += _insert_into_block(index, 0, root, rev, hex)
461 return changed, root
466 return changed, root
462
467
463
468
464 def _insert_into_block(index, level, block, current_rev, current_hex):
469 def _insert_into_block(index, level, block, current_rev, current_hex):
465 """insert a new revision in a block
470 """insert a new revision in a block
466
471
467 index: the index we are adding revision for
472 index: the index we are adding revision for
468 level: the depth of the current block in the trie
473 level: the depth of the current block in the trie
469 block: the block currently being considered
474 block: the block currently being considered
470 current_rev: the revision number we are adding
475 current_rev: the revision number we are adding
471 current_hex: the hexadecimal representation of the of that revision
476 current_hex: the hexadecimal representation of the of that revision
472 """
477 """
473 changed = 1
478 changed = 1
474 if block.ondisk_id is not None:
479 if block.ondisk_id is not None:
475 block.ondisk_id = None
480 block.ondisk_id = None
476 hex_digit = _to_int(current_hex[level : level + 1])
481 hex_digit = _to_int(current_hex[level : level + 1])
477 entry = block.get(hex_digit)
482 entry = block.get(hex_digit)
478 if entry is None:
483 if entry is None:
479 # no entry, simply store the revision number
484 # no entry, simply store the revision number
480 block[hex_digit] = current_rev
485 block[hex_digit] = current_rev
481 elif isinstance(entry, dict):
486 elif isinstance(entry, dict):
482 # need to recurse to an underlying block
487 # need to recurse to an underlying block
483 changed += _insert_into_block(
488 changed += _insert_into_block(
484 index, level + 1, entry, current_rev, current_hex
489 index, level + 1, entry, current_rev, current_hex
485 )
490 )
486 else:
491 else:
487 # collision with a previously unique prefix, inserting new
492 # collision with a previously unique prefix, inserting new
488 # vertices to fit both entry.
493 # vertices to fit both entry.
489 other_hex = nodemod.hex(index[entry][7])
494 other_hex = nodemod.hex(index[entry][7])
490 other_rev = entry
495 other_rev = entry
491 new = Block()
496 new = Block()
492 block[hex_digit] = new
497 block[hex_digit] = new
493 _insert_into_block(index, level + 1, new, other_rev, other_hex)
498 _insert_into_block(index, level + 1, new, other_rev, other_hex)
494 _insert_into_block(index, level + 1, new, current_rev, current_hex)
499 _insert_into_block(index, level + 1, new, current_rev, current_hex)
495 return changed
500 return changed
496
501
497
502
498 def _persist_trie(root, existing_idx=None):
503 def _persist_trie(root, existing_idx=None):
499 """turn a nodemap trie into persistent binary data
504 """turn a nodemap trie into persistent binary data
500
505
501 See `_build_trie` for nodemap trie structure"""
506 See `_build_trie` for nodemap trie structure"""
502 block_map = {}
507 block_map = {}
503 if existing_idx is not None:
508 if existing_idx is not None:
504 base_idx = existing_idx + 1
509 base_idx = existing_idx + 1
505 else:
510 else:
506 base_idx = 0
511 base_idx = 0
507 chunks = []
512 chunks = []
508 for tn in _walk_trie(root):
513 for tn in _walk_trie(root):
509 if tn.ondisk_id is not None:
514 if tn.ondisk_id is not None:
510 block_map[id(tn)] = tn.ondisk_id
515 block_map[id(tn)] = tn.ondisk_id
511 else:
516 else:
512 block_map[id(tn)] = len(chunks) + base_idx
517 block_map[id(tn)] = len(chunks) + base_idx
513 chunks.append(_persist_block(tn, block_map))
518 chunks.append(_persist_block(tn, block_map))
514 return b''.join(chunks)
519 return b''.join(chunks)
515
520
516
521
517 def _walk_trie(block):
522 def _walk_trie(block):
518 """yield all the block in a trie
523 """yield all the block in a trie
519
524
520 Children blocks are always yield before their parent block.
525 Children blocks are always yield before their parent block.
521 """
526 """
522 for (__, item) in sorted(block.items()):
527 for (__, item) in sorted(block.items()):
523 if isinstance(item, dict):
528 if isinstance(item, dict):
524 for sub_block in _walk_trie(item):
529 for sub_block in _walk_trie(item):
525 yield sub_block
530 yield sub_block
526 yield block
531 yield block
527
532
528
533
529 def _persist_block(block_node, block_map):
534 def _persist_block(block_node, block_map):
530 """produce persistent binary data for a single block
535 """produce persistent binary data for a single block
531
536
532 Children block are assumed to be already persisted and present in
537 Children block are assumed to be already persisted and present in
533 block_map.
538 block_map.
534 """
539 """
535 data = tuple(_to_value(v, block_map) for v in block_node)
540 data = tuple(_to_value(v, block_map) for v in block_node)
536 return S_BLOCK.pack(*data)
541 return S_BLOCK.pack(*data)
537
542
538
543
539 def _to_value(item, block_map):
544 def _to_value(item, block_map):
540 """persist any value as an integer"""
545 """persist any value as an integer"""
541 if item is None:
546 if item is None:
542 return NO_ENTRY
547 return NO_ENTRY
543 elif isinstance(item, dict):
548 elif isinstance(item, dict):
544 return block_map[id(item)]
549 return block_map[id(item)]
545 else:
550 else:
546 return _transform_rev(item)
551 return _transform_rev(item)
547
552
548
553
549 def parse_data(data):
554 def parse_data(data):
550 """parse parse nodemap data into a nodemap Trie"""
555 """parse parse nodemap data into a nodemap Trie"""
551 if (len(data) % S_BLOCK.size) != 0:
556 if (len(data) % S_BLOCK.size) != 0:
552 msg = "nodemap data size is not a multiple of block size (%d): %d"
557 msg = "nodemap data size is not a multiple of block size (%d): %d"
553 raise error.Abort(msg % (S_BLOCK.size, len(data)))
558 raise error.Abort(msg % (S_BLOCK.size, len(data)))
554 if not data:
559 if not data:
555 return Block(), None
560 return Block(), None
556 block_map = {}
561 block_map = {}
557 new_blocks = []
562 new_blocks = []
558 for i in range(0, len(data), S_BLOCK.size):
563 for i in range(0, len(data), S_BLOCK.size):
559 block = Block()
564 block = Block()
560 block.ondisk_id = len(block_map)
565 block.ondisk_id = len(block_map)
561 block_map[block.ondisk_id] = block
566 block_map[block.ondisk_id] = block
562 block_data = data[i : i + S_BLOCK.size]
567 block_data = data[i : i + S_BLOCK.size]
563 values = S_BLOCK.unpack(block_data)
568 values = S_BLOCK.unpack(block_data)
564 new_blocks.append((block, values))
569 new_blocks.append((block, values))
565 for b, values in new_blocks:
570 for b, values in new_blocks:
566 for idx, v in enumerate(values):
571 for idx, v in enumerate(values):
567 if v == NO_ENTRY:
572 if v == NO_ENTRY:
568 continue
573 continue
569 elif v >= 0:
574 elif v >= 0:
570 b[idx] = block_map[v]
575 b[idx] = block_map[v]
571 else:
576 else:
572 b[idx] = _transform_rev(v)
577 b[idx] = _transform_rev(v)
573 return block, i // S_BLOCK.size
578 return block, i // S_BLOCK.size
574
579
575
580
576 # debug utility
581 # debug utility
577
582
578
583
579 def check_data(ui, index, data):
584 def check_data(ui, index, data):
580 """verify that the provided nodemap data are valid for the given idex"""
585 """verify that the provided nodemap data are valid for the given idex"""
581 ret = 0
586 ret = 0
582 ui.status((b"revision in index: %d\n") % len(index))
587 ui.status((b"revision in index: %d\n") % len(index))
583 root, __ = parse_data(data)
588 root, __ = parse_data(data)
584 all_revs = set(_all_revisions(root))
589 all_revs = set(_all_revisions(root))
585 ui.status((b"revision in nodemap: %d\n") % len(all_revs))
590 ui.status((b"revision in nodemap: %d\n") % len(all_revs))
586 for r in range(len(index)):
591 for r in range(len(index)):
587 if r not in all_revs:
592 if r not in all_revs:
588 msg = b" revision missing from nodemap: %d\n" % r
593 msg = b" revision missing from nodemap: %d\n" % r
589 ui.write_err(msg)
594 ui.write_err(msg)
590 ret = 1
595 ret = 1
591 else:
596 else:
592 all_revs.remove(r)
597 all_revs.remove(r)
593 nm_rev = _find_node(root, nodemod.hex(index[r][7]))
598 nm_rev = _find_node(root, nodemod.hex(index[r][7]))
594 if nm_rev is None:
599 if nm_rev is None:
595 msg = b" revision node does not match any entries: %d\n" % r
600 msg = b" revision node does not match any entries: %d\n" % r
596 ui.write_err(msg)
601 ui.write_err(msg)
597 ret = 1
602 ret = 1
598 elif nm_rev != r:
603 elif nm_rev != r:
599 msg = (
604 msg = (
600 b" revision node does not match the expected revision: "
605 b" revision node does not match the expected revision: "
601 b"%d != %d\n" % (r, nm_rev)
606 b"%d != %d\n" % (r, nm_rev)
602 )
607 )
603 ui.write_err(msg)
608 ui.write_err(msg)
604 ret = 1
609 ret = 1
605
610
606 if all_revs:
611 if all_revs:
607 for r in sorted(all_revs):
612 for r in sorted(all_revs):
608 msg = b" extra revision in nodemap: %d\n" % r
613 msg = b" extra revision in nodemap: %d\n" % r
609 ui.write_err(msg)
614 ui.write_err(msg)
610 ret = 1
615 ret = 1
611 return ret
616 return ret
612
617
613
618
614 def _all_revisions(root):
619 def _all_revisions(root):
615 """return all revisions stored in a Trie"""
620 """return all revisions stored in a Trie"""
616 for block in _walk_trie(root):
621 for block in _walk_trie(root):
617 for v in block:
622 for v in block:
618 if v is None or isinstance(v, Block):
623 if v is None or isinstance(v, Block):
619 continue
624 continue
620 yield v
625 yield v
621
626
622
627
623 def _find_node(block, node):
628 def _find_node(block, node):
624 """find the revision associated with a given node"""
629 """find the revision associated with a given node"""
625 entry = block.get(_to_int(node[0:1]))
630 entry = block.get(_to_int(node[0:1]))
626 if isinstance(entry, dict):
631 if isinstance(entry, dict):
627 return _find_node(entry, node[1:])
632 return _find_node(entry, node[1:])
628 return entry
633 return entry
@@ -1,1400 +1,1398 b''
1 # upgrade.py - functions for in place upgrade of Mercurial repository
1 # upgrade.py - functions for in place upgrade of Mercurial repository
2 #
2 #
3 # Copyright (c) 2016-present, Gregory Szorc
3 # Copyright (c) 2016-present, Gregory Szorc
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import stat
10 import stat
11
11
12 from .i18n import _
12 from .i18n import _
13 from .pycompat import getattr
13 from .pycompat import getattr
14 from . import (
14 from . import (
15 changelog,
15 changelog,
16 copies,
16 copies,
17 error,
17 error,
18 filelog,
18 filelog,
19 hg,
19 hg,
20 localrepo,
20 localrepo,
21 manifest,
21 manifest,
22 pycompat,
22 pycompat,
23 revlog,
23 revlog,
24 scmutil,
24 scmutil,
25 util,
25 util,
26 vfs as vfsmod,
26 vfs as vfsmod,
27 )
27 )
28
28
29 from .utils import compression
29 from .utils import compression
30
30
31 # list of requirements that request a clone of all revlog if added/removed
31 # list of requirements that request a clone of all revlog if added/removed
32 RECLONES_REQUIREMENTS = {
32 RECLONES_REQUIREMENTS = {
33 b'generaldelta',
33 b'generaldelta',
34 localrepo.SPARSEREVLOG_REQUIREMENT,
34 localrepo.SPARSEREVLOG_REQUIREMENT,
35 }
35 }
36
36
37
37
38 def requiredsourcerequirements(repo):
38 def requiredsourcerequirements(repo):
39 """Obtain requirements required to be present to upgrade a repo.
39 """Obtain requirements required to be present to upgrade a repo.
40
40
41 An upgrade will not be allowed if the repository doesn't have the
41 An upgrade will not be allowed if the repository doesn't have the
42 requirements returned by this function.
42 requirements returned by this function.
43 """
43 """
44 return {
44 return {
45 # Introduced in Mercurial 0.9.2.
45 # Introduced in Mercurial 0.9.2.
46 b'revlogv1',
46 b'revlogv1',
47 # Introduced in Mercurial 0.9.2.
47 # Introduced in Mercurial 0.9.2.
48 b'store',
48 b'store',
49 }
49 }
50
50
51
51
52 def blocksourcerequirements(repo):
52 def blocksourcerequirements(repo):
53 """Obtain requirements that will prevent an upgrade from occurring.
53 """Obtain requirements that will prevent an upgrade from occurring.
54
54
55 An upgrade cannot be performed if the source repository contains a
55 An upgrade cannot be performed if the source repository contains a
56 requirements in the returned set.
56 requirements in the returned set.
57 """
57 """
58 return {
58 return {
59 # The upgrade code does not yet support these experimental features.
59 # The upgrade code does not yet support these experimental features.
60 # This is an artificial limitation.
60 # This is an artificial limitation.
61 b'treemanifest',
61 b'treemanifest',
62 # This was a precursor to generaldelta and was never enabled by default.
62 # This was a precursor to generaldelta and was never enabled by default.
63 # It should (hopefully) not exist in the wild.
63 # It should (hopefully) not exist in the wild.
64 b'parentdelta',
64 b'parentdelta',
65 # Upgrade should operate on the actual store, not the shared link.
65 # Upgrade should operate on the actual store, not the shared link.
66 b'shared',
66 b'shared',
67 }
67 }
68
68
69
69
70 def supportremovedrequirements(repo):
70 def supportremovedrequirements(repo):
71 """Obtain requirements that can be removed during an upgrade.
71 """Obtain requirements that can be removed during an upgrade.
72
72
73 If an upgrade were to create a repository that dropped a requirement,
73 If an upgrade were to create a repository that dropped a requirement,
74 the dropped requirement must appear in the returned set for the upgrade
74 the dropped requirement must appear in the returned set for the upgrade
75 to be allowed.
75 to be allowed.
76 """
76 """
77 supported = {
77 supported = {
78 localrepo.SPARSEREVLOG_REQUIREMENT,
78 localrepo.SPARSEREVLOG_REQUIREMENT,
79 localrepo.SIDEDATA_REQUIREMENT,
79 localrepo.SIDEDATA_REQUIREMENT,
80 localrepo.COPIESSDC_REQUIREMENT,
80 localrepo.COPIESSDC_REQUIREMENT,
81 }
81 }
82 for name in compression.compengines:
82 for name in compression.compengines:
83 engine = compression.compengines[name]
83 engine = compression.compengines[name]
84 if engine.available() and engine.revlogheader():
84 if engine.available() and engine.revlogheader():
85 supported.add(b'exp-compression-%s' % name)
85 supported.add(b'exp-compression-%s' % name)
86 if engine.name() == b'zstd':
86 if engine.name() == b'zstd':
87 supported.add(b'revlog-compression-zstd')
87 supported.add(b'revlog-compression-zstd')
88 return supported
88 return supported
89
89
90
90
91 def supporteddestrequirements(repo):
91 def supporteddestrequirements(repo):
92 """Obtain requirements that upgrade supports in the destination.
92 """Obtain requirements that upgrade supports in the destination.
93
93
94 If the result of the upgrade would create requirements not in this set,
94 If the result of the upgrade would create requirements not in this set,
95 the upgrade is disallowed.
95 the upgrade is disallowed.
96
96
97 Extensions should monkeypatch this to add their custom requirements.
97 Extensions should monkeypatch this to add their custom requirements.
98 """
98 """
99 supported = {
99 supported = {
100 b'dotencode',
100 b'dotencode',
101 b'fncache',
101 b'fncache',
102 b'generaldelta',
102 b'generaldelta',
103 b'revlogv1',
103 b'revlogv1',
104 b'store',
104 b'store',
105 localrepo.SPARSEREVLOG_REQUIREMENT,
105 localrepo.SPARSEREVLOG_REQUIREMENT,
106 localrepo.SIDEDATA_REQUIREMENT,
106 localrepo.SIDEDATA_REQUIREMENT,
107 localrepo.COPIESSDC_REQUIREMENT,
107 localrepo.COPIESSDC_REQUIREMENT,
108 }
108 }
109 for name in compression.compengines:
109 for name in compression.compengines:
110 engine = compression.compengines[name]
110 engine = compression.compengines[name]
111 if engine.available() and engine.revlogheader():
111 if engine.available() and engine.revlogheader():
112 supported.add(b'exp-compression-%s' % name)
112 supported.add(b'exp-compression-%s' % name)
113 if engine.name() == b'zstd':
113 if engine.name() == b'zstd':
114 supported.add(b'revlog-compression-zstd')
114 supported.add(b'revlog-compression-zstd')
115 return supported
115 return supported
116
116
117
117
118 def allowednewrequirements(repo):
118 def allowednewrequirements(repo):
119 """Obtain requirements that can be added to a repository during upgrade.
119 """Obtain requirements that can be added to a repository during upgrade.
120
120
121 This is used to disallow proposed requirements from being added when
121 This is used to disallow proposed requirements from being added when
122 they weren't present before.
122 they weren't present before.
123
123
124 We use a list of allowed requirement additions instead of a list of known
124 We use a list of allowed requirement additions instead of a list of known
125 bad additions because the whitelist approach is safer and will prevent
125 bad additions because the whitelist approach is safer and will prevent
126 future, unknown requirements from accidentally being added.
126 future, unknown requirements from accidentally being added.
127 """
127 """
128 supported = {
128 supported = {
129 b'dotencode',
129 b'dotencode',
130 b'fncache',
130 b'fncache',
131 b'generaldelta',
131 b'generaldelta',
132 localrepo.SPARSEREVLOG_REQUIREMENT,
132 localrepo.SPARSEREVLOG_REQUIREMENT,
133 localrepo.SIDEDATA_REQUIREMENT,
133 localrepo.SIDEDATA_REQUIREMENT,
134 localrepo.COPIESSDC_REQUIREMENT,
134 localrepo.COPIESSDC_REQUIREMENT,
135 }
135 }
136 for name in compression.compengines:
136 for name in compression.compengines:
137 engine = compression.compengines[name]
137 engine = compression.compengines[name]
138 if engine.available() and engine.revlogheader():
138 if engine.available() and engine.revlogheader():
139 supported.add(b'exp-compression-%s' % name)
139 supported.add(b'exp-compression-%s' % name)
140 if engine.name() == b'zstd':
140 if engine.name() == b'zstd':
141 supported.add(b'revlog-compression-zstd')
141 supported.add(b'revlog-compression-zstd')
142 return supported
142 return supported
143
143
144
144
145 def preservedrequirements(repo):
145 def preservedrequirements(repo):
146 return set()
146 return set()
147
147
148
148
149 deficiency = b'deficiency'
149 deficiency = b'deficiency'
150 optimisation = b'optimization'
150 optimisation = b'optimization'
151
151
152
152
153 class improvement(object):
153 class improvement(object):
154 """Represents an improvement that can be made as part of an upgrade.
154 """Represents an improvement that can be made as part of an upgrade.
155
155
156 The following attributes are defined on each instance:
156 The following attributes are defined on each instance:
157
157
158 name
158 name
159 Machine-readable string uniquely identifying this improvement. It
159 Machine-readable string uniquely identifying this improvement. It
160 will be mapped to an action later in the upgrade process.
160 will be mapped to an action later in the upgrade process.
161
161
162 type
162 type
163 Either ``deficiency`` or ``optimisation``. A deficiency is an obvious
163 Either ``deficiency`` or ``optimisation``. A deficiency is an obvious
164 problem. An optimization is an action (sometimes optional) that
164 problem. An optimization is an action (sometimes optional) that
165 can be taken to further improve the state of the repository.
165 can be taken to further improve the state of the repository.
166
166
167 description
167 description
168 Message intended for humans explaining the improvement in more detail,
168 Message intended for humans explaining the improvement in more detail,
169 including the implications of it. For ``deficiency`` types, should be
169 including the implications of it. For ``deficiency`` types, should be
170 worded in the present tense. For ``optimisation`` types, should be
170 worded in the present tense. For ``optimisation`` types, should be
171 worded in the future tense.
171 worded in the future tense.
172
172
173 upgrademessage
173 upgrademessage
174 Message intended for humans explaining what an upgrade addressing this
174 Message intended for humans explaining what an upgrade addressing this
175 issue will do. Should be worded in the future tense.
175 issue will do. Should be worded in the future tense.
176 """
176 """
177
177
178 def __init__(self, name, type, description, upgrademessage):
178 def __init__(self, name, type, description, upgrademessage):
179 self.name = name
179 self.name = name
180 self.type = type
180 self.type = type
181 self.description = description
181 self.description = description
182 self.upgrademessage = upgrademessage
182 self.upgrademessage = upgrademessage
183
183
184 def __eq__(self, other):
184 def __eq__(self, other):
185 if not isinstance(other, improvement):
185 if not isinstance(other, improvement):
186 # This is what python tell use to do
186 # This is what python tell use to do
187 return NotImplemented
187 return NotImplemented
188 return self.name == other.name
188 return self.name == other.name
189
189
190 def __ne__(self, other):
190 def __ne__(self, other):
191 return not (self == other)
191 return not (self == other)
192
192
193 def __hash__(self):
193 def __hash__(self):
194 return hash(self.name)
194 return hash(self.name)
195
195
196
196
197 allformatvariant = []
197 allformatvariant = []
198
198
199
199
200 def registerformatvariant(cls):
200 def registerformatvariant(cls):
201 allformatvariant.append(cls)
201 allformatvariant.append(cls)
202 return cls
202 return cls
203
203
204
204
205 class formatvariant(improvement):
205 class formatvariant(improvement):
206 """an improvement subclass dedicated to repository format"""
206 """an improvement subclass dedicated to repository format"""
207
207
208 type = deficiency
208 type = deficiency
209 ### The following attributes should be defined for each class:
209 ### The following attributes should be defined for each class:
210
210
211 # machine-readable string uniquely identifying this improvement. it will be
211 # machine-readable string uniquely identifying this improvement. it will be
212 # mapped to an action later in the upgrade process.
212 # mapped to an action later in the upgrade process.
213 name = None
213 name = None
214
214
215 # message intended for humans explaining the improvement in more detail,
215 # message intended for humans explaining the improvement in more detail,
216 # including the implications of it ``deficiency`` types, should be worded
216 # including the implications of it ``deficiency`` types, should be worded
217 # in the present tense.
217 # in the present tense.
218 description = None
218 description = None
219
219
220 # message intended for humans explaining what an upgrade addressing this
220 # message intended for humans explaining what an upgrade addressing this
221 # issue will do. should be worded in the future tense.
221 # issue will do. should be worded in the future tense.
222 upgrademessage = None
222 upgrademessage = None
223
223
224 # value of current Mercurial default for new repository
224 # value of current Mercurial default for new repository
225 default = None
225 default = None
226
226
227 def __init__(self):
227 def __init__(self):
228 raise NotImplementedError()
228 raise NotImplementedError()
229
229
230 @staticmethod
230 @staticmethod
231 def fromrepo(repo):
231 def fromrepo(repo):
232 """current value of the variant in the repository"""
232 """current value of the variant in the repository"""
233 raise NotImplementedError()
233 raise NotImplementedError()
234
234
235 @staticmethod
235 @staticmethod
236 def fromconfig(repo):
236 def fromconfig(repo):
237 """current value of the variant in the configuration"""
237 """current value of the variant in the configuration"""
238 raise NotImplementedError()
238 raise NotImplementedError()
239
239
240
240
241 class requirementformatvariant(formatvariant):
241 class requirementformatvariant(formatvariant):
242 """formatvariant based on a 'requirement' name.
242 """formatvariant based on a 'requirement' name.
243
243
244 Many format variant are controlled by a 'requirement'. We define a small
244 Many format variant are controlled by a 'requirement'. We define a small
245 subclass to factor the code.
245 subclass to factor the code.
246 """
246 """
247
247
248 # the requirement that control this format variant
248 # the requirement that control this format variant
249 _requirement = None
249 _requirement = None
250
250
251 @staticmethod
251 @staticmethod
252 def _newreporequirements(ui):
252 def _newreporequirements(ui):
253 return localrepo.newreporequirements(
253 return localrepo.newreporequirements(
254 ui, localrepo.defaultcreateopts(ui)
254 ui, localrepo.defaultcreateopts(ui)
255 )
255 )
256
256
257 @classmethod
257 @classmethod
258 def fromrepo(cls, repo):
258 def fromrepo(cls, repo):
259 assert cls._requirement is not None
259 assert cls._requirement is not None
260 return cls._requirement in repo.requirements
260 return cls._requirement in repo.requirements
261
261
262 @classmethod
262 @classmethod
263 def fromconfig(cls, repo):
263 def fromconfig(cls, repo):
264 assert cls._requirement is not None
264 assert cls._requirement is not None
265 return cls._requirement in cls._newreporequirements(repo.ui)
265 return cls._requirement in cls._newreporequirements(repo.ui)
266
266
267
267
268 @registerformatvariant
268 @registerformatvariant
269 class fncache(requirementformatvariant):
269 class fncache(requirementformatvariant):
270 name = b'fncache'
270 name = b'fncache'
271
271
272 _requirement = b'fncache'
272 _requirement = b'fncache'
273
273
274 default = True
274 default = True
275
275
276 description = _(
276 description = _(
277 b'long and reserved filenames may not work correctly; '
277 b'long and reserved filenames may not work correctly; '
278 b'repository performance is sub-optimal'
278 b'repository performance is sub-optimal'
279 )
279 )
280
280
281 upgrademessage = _(
281 upgrademessage = _(
282 b'repository will be more resilient to storing '
282 b'repository will be more resilient to storing '
283 b'certain paths and performance of certain '
283 b'certain paths and performance of certain '
284 b'operations should be improved'
284 b'operations should be improved'
285 )
285 )
286
286
287
287
288 @registerformatvariant
288 @registerformatvariant
289 class dotencode(requirementformatvariant):
289 class dotencode(requirementformatvariant):
290 name = b'dotencode'
290 name = b'dotencode'
291
291
292 _requirement = b'dotencode'
292 _requirement = b'dotencode'
293
293
294 default = True
294 default = True
295
295
296 description = _(
296 description = _(
297 b'storage of filenames beginning with a period or '
297 b'storage of filenames beginning with a period or '
298 b'space may not work correctly'
298 b'space may not work correctly'
299 )
299 )
300
300
301 upgrademessage = _(
301 upgrademessage = _(
302 b'repository will be better able to store files '
302 b'repository will be better able to store files '
303 b'beginning with a space or period'
303 b'beginning with a space or period'
304 )
304 )
305
305
306
306
307 @registerformatvariant
307 @registerformatvariant
308 class generaldelta(requirementformatvariant):
308 class generaldelta(requirementformatvariant):
309 name = b'generaldelta'
309 name = b'generaldelta'
310
310
311 _requirement = b'generaldelta'
311 _requirement = b'generaldelta'
312
312
313 default = True
313 default = True
314
314
315 description = _(
315 description = _(
316 b'deltas within internal storage are unable to '
316 b'deltas within internal storage are unable to '
317 b'choose optimal revisions; repository is larger and '
317 b'choose optimal revisions; repository is larger and '
318 b'slower than it could be; interaction with other '
318 b'slower than it could be; interaction with other '
319 b'repositories may require extra network and CPU '
319 b'repositories may require extra network and CPU '
320 b'resources, making "hg push" and "hg pull" slower'
320 b'resources, making "hg push" and "hg pull" slower'
321 )
321 )
322
322
323 upgrademessage = _(
323 upgrademessage = _(
324 b'repository storage will be able to create '
324 b'repository storage will be able to create '
325 b'optimal deltas; new repository data will be '
325 b'optimal deltas; new repository data will be '
326 b'smaller and read times should decrease; '
326 b'smaller and read times should decrease; '
327 b'interacting with other repositories using this '
327 b'interacting with other repositories using this '
328 b'storage model should require less network and '
328 b'storage model should require less network and '
329 b'CPU resources, making "hg push" and "hg pull" '
329 b'CPU resources, making "hg push" and "hg pull" '
330 b'faster'
330 b'faster'
331 )
331 )
332
332
333
333
334 @registerformatvariant
334 @registerformatvariant
335 class sparserevlog(requirementformatvariant):
335 class sparserevlog(requirementformatvariant):
336 name = b'sparserevlog'
336 name = b'sparserevlog'
337
337
338 _requirement = localrepo.SPARSEREVLOG_REQUIREMENT
338 _requirement = localrepo.SPARSEREVLOG_REQUIREMENT
339
339
340 default = True
340 default = True
341
341
342 description = _(
342 description = _(
343 b'in order to limit disk reading and memory usage on older '
343 b'in order to limit disk reading and memory usage on older '
344 b'version, the span of a delta chain from its root to its '
344 b'version, the span of a delta chain from its root to its '
345 b'end is limited, whatever the relevant data in this span. '
345 b'end is limited, whatever the relevant data in this span. '
346 b'This can severly limit Mercurial ability to build good '
346 b'This can severly limit Mercurial ability to build good '
347 b'chain of delta resulting is much more storage space being '
347 b'chain of delta resulting is much more storage space being '
348 b'taken and limit reusability of on disk delta during '
348 b'taken and limit reusability of on disk delta during '
349 b'exchange.'
349 b'exchange.'
350 )
350 )
351
351
352 upgrademessage = _(
352 upgrademessage = _(
353 b'Revlog supports delta chain with more unused data '
353 b'Revlog supports delta chain with more unused data '
354 b'between payload. These gaps will be skipped at read '
354 b'between payload. These gaps will be skipped at read '
355 b'time. This allows for better delta chains, making a '
355 b'time. This allows for better delta chains, making a '
356 b'better compression and faster exchange with server.'
356 b'better compression and faster exchange with server.'
357 )
357 )
358
358
359
359
360 @registerformatvariant
360 @registerformatvariant
361 class sidedata(requirementformatvariant):
361 class sidedata(requirementformatvariant):
362 name = b'sidedata'
362 name = b'sidedata'
363
363
364 _requirement = localrepo.SIDEDATA_REQUIREMENT
364 _requirement = localrepo.SIDEDATA_REQUIREMENT
365
365
366 default = False
366 default = False
367
367
368 description = _(
368 description = _(
369 b'Allows storage of extra data alongside a revision, '
369 b'Allows storage of extra data alongside a revision, '
370 b'unlocking various caching options.'
370 b'unlocking various caching options.'
371 )
371 )
372
372
373 upgrademessage = _(b'Allows storage of extra data alongside a revision.')
373 upgrademessage = _(b'Allows storage of extra data alongside a revision.')
374
374
375
375
376 @registerformatvariant
376 @registerformatvariant
377 class copiessdc(requirementformatvariant):
377 class copiessdc(requirementformatvariant):
378 name = b'copies-sdc'
378 name = b'copies-sdc'
379
379
380 _requirement = localrepo.COPIESSDC_REQUIREMENT
380 _requirement = localrepo.COPIESSDC_REQUIREMENT
381
381
382 default = False
382 default = False
383
383
384 description = _(b'Stores copies information alongside changesets.')
384 description = _(b'Stores copies information alongside changesets.')
385
385
386 upgrademessage = _(
386 upgrademessage = _(
387 b'Allows to use more efficient algorithm to deal with ' b'copy tracing.'
387 b'Allows to use more efficient algorithm to deal with ' b'copy tracing.'
388 )
388 )
389
389
390
390
391 @registerformatvariant
391 @registerformatvariant
392 class removecldeltachain(formatvariant):
392 class removecldeltachain(formatvariant):
393 name = b'plain-cl-delta'
393 name = b'plain-cl-delta'
394
394
395 default = True
395 default = True
396
396
397 description = _(
397 description = _(
398 b'changelog storage is using deltas instead of '
398 b'changelog storage is using deltas instead of '
399 b'raw entries; changelog reading and any '
399 b'raw entries; changelog reading and any '
400 b'operation relying on changelog data are slower '
400 b'operation relying on changelog data are slower '
401 b'than they could be'
401 b'than they could be'
402 )
402 )
403
403
404 upgrademessage = _(
404 upgrademessage = _(
405 b'changelog storage will be reformated to '
405 b'changelog storage will be reformated to '
406 b'store raw entries; changelog reading will be '
406 b'store raw entries; changelog reading will be '
407 b'faster; changelog size may be reduced'
407 b'faster; changelog size may be reduced'
408 )
408 )
409
409
410 @staticmethod
410 @staticmethod
411 def fromrepo(repo):
411 def fromrepo(repo):
412 # Mercurial 4.0 changed changelogs to not use delta chains. Search for
412 # Mercurial 4.0 changed changelogs to not use delta chains. Search for
413 # changelogs with deltas.
413 # changelogs with deltas.
414 cl = repo.changelog
414 cl = repo.changelog
415 chainbase = cl.chainbase
415 chainbase = cl.chainbase
416 return all(rev == chainbase(rev) for rev in cl)
416 return all(rev == chainbase(rev) for rev in cl)
417
417
418 @staticmethod
418 @staticmethod
419 def fromconfig(repo):
419 def fromconfig(repo):
420 return True
420 return True
421
421
422
422
423 @registerformatvariant
423 @registerformatvariant
424 class compressionengine(formatvariant):
424 class compressionengine(formatvariant):
425 name = b'compression'
425 name = b'compression'
426 default = b'zlib'
426 default = b'zlib'
427
427
428 description = _(
428 description = _(
429 b'Compresion algorithm used to compress data. '
429 b'Compresion algorithm used to compress data. '
430 b'Some engine are faster than other'
430 b'Some engine are faster than other'
431 )
431 )
432
432
433 upgrademessage = _(
433 upgrademessage = _(
434 b'revlog content will be recompressed with the new algorithm.'
434 b'revlog content will be recompressed with the new algorithm.'
435 )
435 )
436
436
437 @classmethod
437 @classmethod
438 def fromrepo(cls, repo):
438 def fromrepo(cls, repo):
439 # we allow multiple compression engine requirement to co-exist because
439 # we allow multiple compression engine requirement to co-exist because
440 # strickly speaking, revlog seems to support mixed compression style.
440 # strickly speaking, revlog seems to support mixed compression style.
441 #
441 #
442 # The compression used for new entries will be "the last one"
442 # The compression used for new entries will be "the last one"
443 compression = b'zlib'
443 compression = b'zlib'
444 for req in repo.requirements:
444 for req in repo.requirements:
445 prefix = req.startswith
445 prefix = req.startswith
446 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
446 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
447 compression = req.split(b'-', 2)[2]
447 compression = req.split(b'-', 2)[2]
448 return compression
448 return compression
449
449
450 @classmethod
450 @classmethod
451 def fromconfig(cls, repo):
451 def fromconfig(cls, repo):
452 compengines = repo.ui.configlist(b'format', b'revlog-compression')
452 compengines = repo.ui.configlist(b'format', b'revlog-compression')
453 # return the first valid value as the selection code would do
453 # return the first valid value as the selection code would do
454 for comp in compengines:
454 for comp in compengines:
455 if comp in util.compengines:
455 if comp in util.compengines:
456 return comp
456 return comp
457
457
458 # no valide compression found lets display it all for clarity
458 # no valide compression found lets display it all for clarity
459 return b','.join(compengines)
459 return b','.join(compengines)
460
460
461
461
462 @registerformatvariant
462 @registerformatvariant
463 class compressionlevel(formatvariant):
463 class compressionlevel(formatvariant):
464 name = b'compression-level'
464 name = b'compression-level'
465 default = b'default'
465 default = b'default'
466
466
467 description = _(b'compression level')
467 description = _(b'compression level')
468
468
469 upgrademessage = _(b'revlog content will be recompressed')
469 upgrademessage = _(b'revlog content will be recompressed')
470
470
471 @classmethod
471 @classmethod
472 def fromrepo(cls, repo):
472 def fromrepo(cls, repo):
473 comp = compressionengine.fromrepo(repo)
473 comp = compressionengine.fromrepo(repo)
474 level = None
474 level = None
475 if comp == b'zlib':
475 if comp == b'zlib':
476 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
476 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
477 elif comp == b'zstd':
477 elif comp == b'zstd':
478 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
478 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
479 if level is None:
479 if level is None:
480 return b'default'
480 return b'default'
481 return bytes(level)
481 return bytes(level)
482
482
483 @classmethod
483 @classmethod
484 def fromconfig(cls, repo):
484 def fromconfig(cls, repo):
485 comp = compressionengine.fromconfig(repo)
485 comp = compressionengine.fromconfig(repo)
486 level = None
486 level = None
487 if comp == b'zlib':
487 if comp == b'zlib':
488 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
488 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
489 elif comp == b'zstd':
489 elif comp == b'zstd':
490 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
490 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
491 if level is None:
491 if level is None:
492 return b'default'
492 return b'default'
493 return bytes(level)
493 return bytes(level)
494
494
495
495
496 def finddeficiencies(repo):
496 def finddeficiencies(repo):
497 """returns a list of deficiencies that the repo suffer from"""
497 """returns a list of deficiencies that the repo suffer from"""
498 deficiencies = []
498 deficiencies = []
499
499
500 # We could detect lack of revlogv1 and store here, but they were added
500 # We could detect lack of revlogv1 and store here, but they were added
501 # in 0.9.2 and we don't support upgrading repos without these
501 # in 0.9.2 and we don't support upgrading repos without these
502 # requirements, so let's not bother.
502 # requirements, so let's not bother.
503
503
504 for fv in allformatvariant:
504 for fv in allformatvariant:
505 if not fv.fromrepo(repo):
505 if not fv.fromrepo(repo):
506 deficiencies.append(fv)
506 deficiencies.append(fv)
507
507
508 return deficiencies
508 return deficiencies
509
509
510
510
511 # search without '-' to support older form on newer client.
511 # search without '-' to support older form on newer client.
512 #
512 #
513 # We don't enforce backward compatibility for debug command so this
513 # We don't enforce backward compatibility for debug command so this
514 # might eventually be dropped. However, having to use two different
514 # might eventually be dropped. However, having to use two different
515 # forms in script when comparing result is anoying enough to add
515 # forms in script when comparing result is anoying enough to add
516 # backward compatibility for a while.
516 # backward compatibility for a while.
517 legacy_opts_map = {
517 legacy_opts_map = {
518 b'redeltaparent': b're-delta-parent',
518 b'redeltaparent': b're-delta-parent',
519 b'redeltamultibase': b're-delta-multibase',
519 b'redeltamultibase': b're-delta-multibase',
520 b'redeltaall': b're-delta-all',
520 b'redeltaall': b're-delta-all',
521 b'redeltafulladd': b're-delta-fulladd',
521 b'redeltafulladd': b're-delta-fulladd',
522 }
522 }
523
523
524
524
525 def findoptimizations(repo):
525 def findoptimizations(repo):
526 """Determine optimisation that could be used during upgrade"""
526 """Determine optimisation that could be used during upgrade"""
527 # These are unconditionally added. There is logic later that figures out
527 # These are unconditionally added. There is logic later that figures out
528 # which ones to apply.
528 # which ones to apply.
529 optimizations = []
529 optimizations = []
530
530
531 optimizations.append(
531 optimizations.append(
532 improvement(
532 improvement(
533 name=b're-delta-parent',
533 name=b're-delta-parent',
534 type=optimisation,
534 type=optimisation,
535 description=_(
535 description=_(
536 b'deltas within internal storage will be recalculated to '
536 b'deltas within internal storage will be recalculated to '
537 b'choose an optimal base revision where this was not '
537 b'choose an optimal base revision where this was not '
538 b'already done; the size of the repository may shrink and '
538 b'already done; the size of the repository may shrink and '
539 b'various operations may become faster; the first time '
539 b'various operations may become faster; the first time '
540 b'this optimization is performed could slow down upgrade '
540 b'this optimization is performed could slow down upgrade '
541 b'execution considerably; subsequent invocations should '
541 b'execution considerably; subsequent invocations should '
542 b'not run noticeably slower'
542 b'not run noticeably slower'
543 ),
543 ),
544 upgrademessage=_(
544 upgrademessage=_(
545 b'deltas within internal storage will choose a new '
545 b'deltas within internal storage will choose a new '
546 b'base revision if needed'
546 b'base revision if needed'
547 ),
547 ),
548 )
548 )
549 )
549 )
550
550
551 optimizations.append(
551 optimizations.append(
552 improvement(
552 improvement(
553 name=b're-delta-multibase',
553 name=b're-delta-multibase',
554 type=optimisation,
554 type=optimisation,
555 description=_(
555 description=_(
556 b'deltas within internal storage will be recalculated '
556 b'deltas within internal storage will be recalculated '
557 b'against multiple base revision and the smallest '
557 b'against multiple base revision and the smallest '
558 b'difference will be used; the size of the repository may '
558 b'difference will be used; the size of the repository may '
559 b'shrink significantly when there are many merges; this '
559 b'shrink significantly when there are many merges; this '
560 b'optimization will slow down execution in proportion to '
560 b'optimization will slow down execution in proportion to '
561 b'the number of merges in the repository and the amount '
561 b'the number of merges in the repository and the amount '
562 b'of files in the repository; this slow down should not '
562 b'of files in the repository; this slow down should not '
563 b'be significant unless there are tens of thousands of '
563 b'be significant unless there are tens of thousands of '
564 b'files and thousands of merges'
564 b'files and thousands of merges'
565 ),
565 ),
566 upgrademessage=_(
566 upgrademessage=_(
567 b'deltas within internal storage will choose an '
567 b'deltas within internal storage will choose an '
568 b'optimal delta by computing deltas against multiple '
568 b'optimal delta by computing deltas against multiple '
569 b'parents; may slow down execution time '
569 b'parents; may slow down execution time '
570 b'significantly'
570 b'significantly'
571 ),
571 ),
572 )
572 )
573 )
573 )
574
574
575 optimizations.append(
575 optimizations.append(
576 improvement(
576 improvement(
577 name=b're-delta-all',
577 name=b're-delta-all',
578 type=optimisation,
578 type=optimisation,
579 description=_(
579 description=_(
580 b'deltas within internal storage will always be '
580 b'deltas within internal storage will always be '
581 b'recalculated without reusing prior deltas; this will '
581 b'recalculated without reusing prior deltas; this will '
582 b'likely make execution run several times slower; this '
582 b'likely make execution run several times slower; this '
583 b'optimization is typically not needed'
583 b'optimization is typically not needed'
584 ),
584 ),
585 upgrademessage=_(
585 upgrademessage=_(
586 b'deltas within internal storage will be fully '
586 b'deltas within internal storage will be fully '
587 b'recomputed; this will likely drastically slow down '
587 b'recomputed; this will likely drastically slow down '
588 b'execution time'
588 b'execution time'
589 ),
589 ),
590 )
590 )
591 )
591 )
592
592
593 optimizations.append(
593 optimizations.append(
594 improvement(
594 improvement(
595 name=b're-delta-fulladd',
595 name=b're-delta-fulladd',
596 type=optimisation,
596 type=optimisation,
597 description=_(
597 description=_(
598 b'every revision will be re-added as if it was new '
598 b'every revision will be re-added as if it was new '
599 b'content. It will go through the full storage '
599 b'content. It will go through the full storage '
600 b'mechanism giving extensions a chance to process it '
600 b'mechanism giving extensions a chance to process it '
601 b'(eg. lfs). This is similar to "re-delta-all" but even '
601 b'(eg. lfs). This is similar to "re-delta-all" but even '
602 b'slower since more logic is involved.'
602 b'slower since more logic is involved.'
603 ),
603 ),
604 upgrademessage=_(
604 upgrademessage=_(
605 b'each revision will be added as new content to the '
605 b'each revision will be added as new content to the '
606 b'internal storage; this will likely drastically slow '
606 b'internal storage; this will likely drastically slow '
607 b'down execution time, but some extensions might need '
607 b'down execution time, but some extensions might need '
608 b'it'
608 b'it'
609 ),
609 ),
610 )
610 )
611 )
611 )
612
612
613 return optimizations
613 return optimizations
614
614
615
615
616 def determineactions(repo, deficiencies, sourcereqs, destreqs):
616 def determineactions(repo, deficiencies, sourcereqs, destreqs):
617 """Determine upgrade actions that will be performed.
617 """Determine upgrade actions that will be performed.
618
618
619 Given a list of improvements as returned by ``finddeficiencies`` and
619 Given a list of improvements as returned by ``finddeficiencies`` and
620 ``findoptimizations``, determine the list of upgrade actions that
620 ``findoptimizations``, determine the list of upgrade actions that
621 will be performed.
621 will be performed.
622
622
623 The role of this function is to filter improvements if needed, apply
623 The role of this function is to filter improvements if needed, apply
624 recommended optimizations from the improvements list that make sense,
624 recommended optimizations from the improvements list that make sense,
625 etc.
625 etc.
626
626
627 Returns a list of action names.
627 Returns a list of action names.
628 """
628 """
629 newactions = []
629 newactions = []
630
630
631 knownreqs = supporteddestrequirements(repo)
632
633 for d in deficiencies:
631 for d in deficiencies:
634 name = d.name
632 name = d._requirement
635
633
636 # If the action is a requirement that doesn't show up in the
634 # If the action is a requirement that doesn't show up in the
637 # destination requirements, prune the action.
635 # destination requirements, prune the action.
638 if name in knownreqs and name not in destreqs:
636 if name is not None and name not in destreqs:
639 continue
637 continue
640
638
641 newactions.append(d)
639 newactions.append(d)
642
640
643 # FUTURE consider adding some optimizations here for certain transitions.
641 # FUTURE consider adding some optimizations here for certain transitions.
644 # e.g. adding generaldelta could schedule parent redeltas.
642 # e.g. adding generaldelta could schedule parent redeltas.
645
643
646 return newactions
644 return newactions
647
645
648
646
649 def _revlogfrompath(repo, path):
647 def _revlogfrompath(repo, path):
650 """Obtain a revlog from a repo path.
648 """Obtain a revlog from a repo path.
651
649
652 An instance of the appropriate class is returned.
650 An instance of the appropriate class is returned.
653 """
651 """
654 if path == b'00changelog.i':
652 if path == b'00changelog.i':
655 return changelog.changelog(repo.svfs)
653 return changelog.changelog(repo.svfs)
656 elif path.endswith(b'00manifest.i'):
654 elif path.endswith(b'00manifest.i'):
657 mandir = path[: -len(b'00manifest.i')]
655 mandir = path[: -len(b'00manifest.i')]
658 return manifest.manifestrevlog(repo.svfs, tree=mandir)
656 return manifest.manifestrevlog(repo.svfs, tree=mandir)
659 else:
657 else:
660 # reverse of "/".join(("data", path + ".i"))
658 # reverse of "/".join(("data", path + ".i"))
661 return filelog.filelog(repo.svfs, path[5:-2])
659 return filelog.filelog(repo.svfs, path[5:-2])
662
660
663
661
664 def _copyrevlog(tr, destrepo, oldrl, unencodedname):
662 def _copyrevlog(tr, destrepo, oldrl, unencodedname):
665 """copy all relevant files for `oldrl` into `destrepo` store
663 """copy all relevant files for `oldrl` into `destrepo` store
666
664
667 Files are copied "as is" without any transformation. The copy is performed
665 Files are copied "as is" without any transformation. The copy is performed
668 without extra checks. Callers are responsible for making sure the copied
666 without extra checks. Callers are responsible for making sure the copied
669 content is compatible with format of the destination repository.
667 content is compatible with format of the destination repository.
670 """
668 """
671 oldrl = getattr(oldrl, '_revlog', oldrl)
669 oldrl = getattr(oldrl, '_revlog', oldrl)
672 newrl = _revlogfrompath(destrepo, unencodedname)
670 newrl = _revlogfrompath(destrepo, unencodedname)
673 newrl = getattr(newrl, '_revlog', newrl)
671 newrl = getattr(newrl, '_revlog', newrl)
674
672
675 oldvfs = oldrl.opener
673 oldvfs = oldrl.opener
676 newvfs = newrl.opener
674 newvfs = newrl.opener
677 oldindex = oldvfs.join(oldrl.indexfile)
675 oldindex = oldvfs.join(oldrl.indexfile)
678 newindex = newvfs.join(newrl.indexfile)
676 newindex = newvfs.join(newrl.indexfile)
679 olddata = oldvfs.join(oldrl.datafile)
677 olddata = oldvfs.join(oldrl.datafile)
680 newdata = newvfs.join(newrl.datafile)
678 newdata = newvfs.join(newrl.datafile)
681
679
682 with newvfs(newrl.indexfile, b'w'):
680 with newvfs(newrl.indexfile, b'w'):
683 pass # create all the directories
681 pass # create all the directories
684
682
685 util.copyfile(oldindex, newindex)
683 util.copyfile(oldindex, newindex)
686 copydata = oldrl.opener.exists(oldrl.datafile)
684 copydata = oldrl.opener.exists(oldrl.datafile)
687 if copydata:
685 if copydata:
688 util.copyfile(olddata, newdata)
686 util.copyfile(olddata, newdata)
689
687
690 if not (
688 if not (
691 unencodedname.endswith(b'00changelog.i')
689 unencodedname.endswith(b'00changelog.i')
692 or unencodedname.endswith(b'00manifest.i')
690 or unencodedname.endswith(b'00manifest.i')
693 ):
691 ):
694 destrepo.svfs.fncache.add(unencodedname)
692 destrepo.svfs.fncache.add(unencodedname)
695 if copydata:
693 if copydata:
696 destrepo.svfs.fncache.add(unencodedname[:-2] + b'.d')
694 destrepo.svfs.fncache.add(unencodedname[:-2] + b'.d')
697
695
698
696
699 UPGRADE_CHANGELOG = object()
697 UPGRADE_CHANGELOG = object()
700 UPGRADE_MANIFEST = object()
698 UPGRADE_MANIFEST = object()
701 UPGRADE_FILELOG = object()
699 UPGRADE_FILELOG = object()
702
700
703 UPGRADE_ALL_REVLOGS = frozenset(
701 UPGRADE_ALL_REVLOGS = frozenset(
704 [UPGRADE_CHANGELOG, UPGRADE_MANIFEST, UPGRADE_FILELOG]
702 [UPGRADE_CHANGELOG, UPGRADE_MANIFEST, UPGRADE_FILELOG]
705 )
703 )
706
704
707
705
708 def getsidedatacompanion(srcrepo, dstrepo):
706 def getsidedatacompanion(srcrepo, dstrepo):
709 sidedatacompanion = None
707 sidedatacompanion = None
710 removedreqs = srcrepo.requirements - dstrepo.requirements
708 removedreqs = srcrepo.requirements - dstrepo.requirements
711 addedreqs = dstrepo.requirements - srcrepo.requirements
709 addedreqs = dstrepo.requirements - srcrepo.requirements
712 if localrepo.SIDEDATA_REQUIREMENT in removedreqs:
710 if localrepo.SIDEDATA_REQUIREMENT in removedreqs:
713
711
714 def sidedatacompanion(rl, rev):
712 def sidedatacompanion(rl, rev):
715 rl = getattr(rl, '_revlog', rl)
713 rl = getattr(rl, '_revlog', rl)
716 if rl.flags(rev) & revlog.REVIDX_SIDEDATA:
714 if rl.flags(rev) & revlog.REVIDX_SIDEDATA:
717 return True, (), {}
715 return True, (), {}
718 return False, (), {}
716 return False, (), {}
719
717
720 elif localrepo.COPIESSDC_REQUIREMENT in addedreqs:
718 elif localrepo.COPIESSDC_REQUIREMENT in addedreqs:
721 sidedatacompanion = copies.getsidedataadder(srcrepo, dstrepo)
719 sidedatacompanion = copies.getsidedataadder(srcrepo, dstrepo)
722 elif localrepo.COPIESSDC_REQUIREMENT in removedreqs:
720 elif localrepo.COPIESSDC_REQUIREMENT in removedreqs:
723 sidedatacompanion = copies.getsidedataremover(srcrepo, dstrepo)
721 sidedatacompanion = copies.getsidedataremover(srcrepo, dstrepo)
724 return sidedatacompanion
722 return sidedatacompanion
725
723
726
724
727 def matchrevlog(revlogfilter, entry):
725 def matchrevlog(revlogfilter, entry):
728 """check is a revlog is selected for cloning
726 """check is a revlog is selected for cloning
729
727
730 The store entry is checked against the passed filter"""
728 The store entry is checked against the passed filter"""
731 if entry.endswith(b'00changelog.i'):
729 if entry.endswith(b'00changelog.i'):
732 return UPGRADE_CHANGELOG in revlogfilter
730 return UPGRADE_CHANGELOG in revlogfilter
733 elif entry.endswith(b'00manifest.i'):
731 elif entry.endswith(b'00manifest.i'):
734 return UPGRADE_MANIFEST in revlogfilter
732 return UPGRADE_MANIFEST in revlogfilter
735 return UPGRADE_FILELOG in revlogfilter
733 return UPGRADE_FILELOG in revlogfilter
736
734
737
735
738 def _clonerevlogs(
736 def _clonerevlogs(
739 ui,
737 ui,
740 srcrepo,
738 srcrepo,
741 dstrepo,
739 dstrepo,
742 tr,
740 tr,
743 deltareuse,
741 deltareuse,
744 forcedeltabothparents,
742 forcedeltabothparents,
745 revlogs=UPGRADE_ALL_REVLOGS,
743 revlogs=UPGRADE_ALL_REVLOGS,
746 ):
744 ):
747 """Copy revlogs between 2 repos."""
745 """Copy revlogs between 2 repos."""
748 revcount = 0
746 revcount = 0
749 srcsize = 0
747 srcsize = 0
750 srcrawsize = 0
748 srcrawsize = 0
751 dstsize = 0
749 dstsize = 0
752 fcount = 0
750 fcount = 0
753 frevcount = 0
751 frevcount = 0
754 fsrcsize = 0
752 fsrcsize = 0
755 frawsize = 0
753 frawsize = 0
756 fdstsize = 0
754 fdstsize = 0
757 mcount = 0
755 mcount = 0
758 mrevcount = 0
756 mrevcount = 0
759 msrcsize = 0
757 msrcsize = 0
760 mrawsize = 0
758 mrawsize = 0
761 mdstsize = 0
759 mdstsize = 0
762 crevcount = 0
760 crevcount = 0
763 csrcsize = 0
761 csrcsize = 0
764 crawsize = 0
762 crawsize = 0
765 cdstsize = 0
763 cdstsize = 0
766
764
767 alldatafiles = list(srcrepo.store.walk())
765 alldatafiles = list(srcrepo.store.walk())
768
766
769 # Perform a pass to collect metadata. This validates we can open all
767 # Perform a pass to collect metadata. This validates we can open all
770 # source files and allows a unified progress bar to be displayed.
768 # source files and allows a unified progress bar to be displayed.
771 for unencoded, encoded, size in alldatafiles:
769 for unencoded, encoded, size in alldatafiles:
772 if unencoded.endswith(b'.d'):
770 if unencoded.endswith(b'.d'):
773 continue
771 continue
774
772
775 rl = _revlogfrompath(srcrepo, unencoded)
773 rl = _revlogfrompath(srcrepo, unencoded)
776
774
777 info = rl.storageinfo(
775 info = rl.storageinfo(
778 exclusivefiles=True,
776 exclusivefiles=True,
779 revisionscount=True,
777 revisionscount=True,
780 trackedsize=True,
778 trackedsize=True,
781 storedsize=True,
779 storedsize=True,
782 )
780 )
783
781
784 revcount += info[b'revisionscount'] or 0
782 revcount += info[b'revisionscount'] or 0
785 datasize = info[b'storedsize'] or 0
783 datasize = info[b'storedsize'] or 0
786 rawsize = info[b'trackedsize'] or 0
784 rawsize = info[b'trackedsize'] or 0
787
785
788 srcsize += datasize
786 srcsize += datasize
789 srcrawsize += rawsize
787 srcrawsize += rawsize
790
788
791 # This is for the separate progress bars.
789 # This is for the separate progress bars.
792 if isinstance(rl, changelog.changelog):
790 if isinstance(rl, changelog.changelog):
793 crevcount += len(rl)
791 crevcount += len(rl)
794 csrcsize += datasize
792 csrcsize += datasize
795 crawsize += rawsize
793 crawsize += rawsize
796 elif isinstance(rl, manifest.manifestrevlog):
794 elif isinstance(rl, manifest.manifestrevlog):
797 mcount += 1
795 mcount += 1
798 mrevcount += len(rl)
796 mrevcount += len(rl)
799 msrcsize += datasize
797 msrcsize += datasize
800 mrawsize += rawsize
798 mrawsize += rawsize
801 elif isinstance(rl, filelog.filelog):
799 elif isinstance(rl, filelog.filelog):
802 fcount += 1
800 fcount += 1
803 frevcount += len(rl)
801 frevcount += len(rl)
804 fsrcsize += datasize
802 fsrcsize += datasize
805 frawsize += rawsize
803 frawsize += rawsize
806 else:
804 else:
807 error.ProgrammingError(b'unknown revlog type')
805 error.ProgrammingError(b'unknown revlog type')
808
806
809 if not revcount:
807 if not revcount:
810 return
808 return
811
809
812 ui.write(
810 ui.write(
813 _(
811 _(
814 b'migrating %d total revisions (%d in filelogs, %d in manifests, '
812 b'migrating %d total revisions (%d in filelogs, %d in manifests, '
815 b'%d in changelog)\n'
813 b'%d in changelog)\n'
816 )
814 )
817 % (revcount, frevcount, mrevcount, crevcount)
815 % (revcount, frevcount, mrevcount, crevcount)
818 )
816 )
819 ui.write(
817 ui.write(
820 _(b'migrating %s in store; %s tracked data\n')
818 _(b'migrating %s in store; %s tracked data\n')
821 % ((util.bytecount(srcsize), util.bytecount(srcrawsize)))
819 % ((util.bytecount(srcsize), util.bytecount(srcrawsize)))
822 )
820 )
823
821
824 # Used to keep track of progress.
822 # Used to keep track of progress.
825 progress = None
823 progress = None
826
824
827 def oncopiedrevision(rl, rev, node):
825 def oncopiedrevision(rl, rev, node):
828 progress.increment()
826 progress.increment()
829
827
830 sidedatacompanion = getsidedatacompanion(srcrepo, dstrepo)
828 sidedatacompanion = getsidedatacompanion(srcrepo, dstrepo)
831
829
832 # Do the actual copying.
830 # Do the actual copying.
833 # FUTURE this operation can be farmed off to worker processes.
831 # FUTURE this operation can be farmed off to worker processes.
834 seen = set()
832 seen = set()
835 for unencoded, encoded, size in alldatafiles:
833 for unencoded, encoded, size in alldatafiles:
836 if unencoded.endswith(b'.d'):
834 if unencoded.endswith(b'.d'):
837 continue
835 continue
838
836
839 oldrl = _revlogfrompath(srcrepo, unencoded)
837 oldrl = _revlogfrompath(srcrepo, unencoded)
840
838
841 if isinstance(oldrl, changelog.changelog) and b'c' not in seen:
839 if isinstance(oldrl, changelog.changelog) and b'c' not in seen:
842 ui.write(
840 ui.write(
843 _(
841 _(
844 b'finished migrating %d manifest revisions across %d '
842 b'finished migrating %d manifest revisions across %d '
845 b'manifests; change in size: %s\n'
843 b'manifests; change in size: %s\n'
846 )
844 )
847 % (mrevcount, mcount, util.bytecount(mdstsize - msrcsize))
845 % (mrevcount, mcount, util.bytecount(mdstsize - msrcsize))
848 )
846 )
849
847
850 ui.write(
848 ui.write(
851 _(
849 _(
852 b'migrating changelog containing %d revisions '
850 b'migrating changelog containing %d revisions '
853 b'(%s in store; %s tracked data)\n'
851 b'(%s in store; %s tracked data)\n'
854 )
852 )
855 % (
853 % (
856 crevcount,
854 crevcount,
857 util.bytecount(csrcsize),
855 util.bytecount(csrcsize),
858 util.bytecount(crawsize),
856 util.bytecount(crawsize),
859 )
857 )
860 )
858 )
861 seen.add(b'c')
859 seen.add(b'c')
862 progress = srcrepo.ui.makeprogress(
860 progress = srcrepo.ui.makeprogress(
863 _(b'changelog revisions'), total=crevcount
861 _(b'changelog revisions'), total=crevcount
864 )
862 )
865 elif isinstance(oldrl, manifest.manifestrevlog) and b'm' not in seen:
863 elif isinstance(oldrl, manifest.manifestrevlog) and b'm' not in seen:
866 ui.write(
864 ui.write(
867 _(
865 _(
868 b'finished migrating %d filelog revisions across %d '
866 b'finished migrating %d filelog revisions across %d '
869 b'filelogs; change in size: %s\n'
867 b'filelogs; change in size: %s\n'
870 )
868 )
871 % (frevcount, fcount, util.bytecount(fdstsize - fsrcsize))
869 % (frevcount, fcount, util.bytecount(fdstsize - fsrcsize))
872 )
870 )
873
871
874 ui.write(
872 ui.write(
875 _(
873 _(
876 b'migrating %d manifests containing %d revisions '
874 b'migrating %d manifests containing %d revisions '
877 b'(%s in store; %s tracked data)\n'
875 b'(%s in store; %s tracked data)\n'
878 )
876 )
879 % (
877 % (
880 mcount,
878 mcount,
881 mrevcount,
879 mrevcount,
882 util.bytecount(msrcsize),
880 util.bytecount(msrcsize),
883 util.bytecount(mrawsize),
881 util.bytecount(mrawsize),
884 )
882 )
885 )
883 )
886 seen.add(b'm')
884 seen.add(b'm')
887 if progress:
885 if progress:
888 progress.complete()
886 progress.complete()
889 progress = srcrepo.ui.makeprogress(
887 progress = srcrepo.ui.makeprogress(
890 _(b'manifest revisions'), total=mrevcount
888 _(b'manifest revisions'), total=mrevcount
891 )
889 )
892 elif b'f' not in seen:
890 elif b'f' not in seen:
893 ui.write(
891 ui.write(
894 _(
892 _(
895 b'migrating %d filelogs containing %d revisions '
893 b'migrating %d filelogs containing %d revisions '
896 b'(%s in store; %s tracked data)\n'
894 b'(%s in store; %s tracked data)\n'
897 )
895 )
898 % (
896 % (
899 fcount,
897 fcount,
900 frevcount,
898 frevcount,
901 util.bytecount(fsrcsize),
899 util.bytecount(fsrcsize),
902 util.bytecount(frawsize),
900 util.bytecount(frawsize),
903 )
901 )
904 )
902 )
905 seen.add(b'f')
903 seen.add(b'f')
906 if progress:
904 if progress:
907 progress.complete()
905 progress.complete()
908 progress = srcrepo.ui.makeprogress(
906 progress = srcrepo.ui.makeprogress(
909 _(b'file revisions'), total=frevcount
907 _(b'file revisions'), total=frevcount
910 )
908 )
911
909
912 if matchrevlog(revlogs, unencoded):
910 if matchrevlog(revlogs, unencoded):
913 ui.note(
911 ui.note(
914 _(b'cloning %d revisions from %s\n') % (len(oldrl), unencoded)
912 _(b'cloning %d revisions from %s\n') % (len(oldrl), unencoded)
915 )
913 )
916 newrl = _revlogfrompath(dstrepo, unencoded)
914 newrl = _revlogfrompath(dstrepo, unencoded)
917 oldrl.clone(
915 oldrl.clone(
918 tr,
916 tr,
919 newrl,
917 newrl,
920 addrevisioncb=oncopiedrevision,
918 addrevisioncb=oncopiedrevision,
921 deltareuse=deltareuse,
919 deltareuse=deltareuse,
922 forcedeltabothparents=forcedeltabothparents,
920 forcedeltabothparents=forcedeltabothparents,
923 sidedatacompanion=sidedatacompanion,
921 sidedatacompanion=sidedatacompanion,
924 )
922 )
925 else:
923 else:
926 msg = _(b'blindly copying %s containing %i revisions\n')
924 msg = _(b'blindly copying %s containing %i revisions\n')
927 ui.note(msg % (unencoded, len(oldrl)))
925 ui.note(msg % (unencoded, len(oldrl)))
928 _copyrevlog(tr, dstrepo, oldrl, unencoded)
926 _copyrevlog(tr, dstrepo, oldrl, unencoded)
929
927
930 newrl = _revlogfrompath(dstrepo, unencoded)
928 newrl = _revlogfrompath(dstrepo, unencoded)
931
929
932 info = newrl.storageinfo(storedsize=True)
930 info = newrl.storageinfo(storedsize=True)
933 datasize = info[b'storedsize'] or 0
931 datasize = info[b'storedsize'] or 0
934
932
935 dstsize += datasize
933 dstsize += datasize
936
934
937 if isinstance(newrl, changelog.changelog):
935 if isinstance(newrl, changelog.changelog):
938 cdstsize += datasize
936 cdstsize += datasize
939 elif isinstance(newrl, manifest.manifestrevlog):
937 elif isinstance(newrl, manifest.manifestrevlog):
940 mdstsize += datasize
938 mdstsize += datasize
941 else:
939 else:
942 fdstsize += datasize
940 fdstsize += datasize
943
941
944 progress.complete()
942 progress.complete()
945
943
946 ui.write(
944 ui.write(
947 _(
945 _(
948 b'finished migrating %d changelog revisions; change in size: '
946 b'finished migrating %d changelog revisions; change in size: '
949 b'%s\n'
947 b'%s\n'
950 )
948 )
951 % (crevcount, util.bytecount(cdstsize - csrcsize))
949 % (crevcount, util.bytecount(cdstsize - csrcsize))
952 )
950 )
953
951
954 ui.write(
952 ui.write(
955 _(
953 _(
956 b'finished migrating %d total revisions; total change in store '
954 b'finished migrating %d total revisions; total change in store '
957 b'size: %s\n'
955 b'size: %s\n'
958 )
956 )
959 % (revcount, util.bytecount(dstsize - srcsize))
957 % (revcount, util.bytecount(dstsize - srcsize))
960 )
958 )
961
959
962
960
963 def _filterstorefile(srcrepo, dstrepo, requirements, path, mode, st):
961 def _filterstorefile(srcrepo, dstrepo, requirements, path, mode, st):
964 """Determine whether to copy a store file during upgrade.
962 """Determine whether to copy a store file during upgrade.
965
963
966 This function is called when migrating store files from ``srcrepo`` to
964 This function is called when migrating store files from ``srcrepo`` to
967 ``dstrepo`` as part of upgrading a repository.
965 ``dstrepo`` as part of upgrading a repository.
968
966
969 Args:
967 Args:
970 srcrepo: repo we are copying from
968 srcrepo: repo we are copying from
971 dstrepo: repo we are copying to
969 dstrepo: repo we are copying to
972 requirements: set of requirements for ``dstrepo``
970 requirements: set of requirements for ``dstrepo``
973 path: store file being examined
971 path: store file being examined
974 mode: the ``ST_MODE`` file type of ``path``
972 mode: the ``ST_MODE`` file type of ``path``
975 st: ``stat`` data structure for ``path``
973 st: ``stat`` data structure for ``path``
976
974
977 Function should return ``True`` if the file is to be copied.
975 Function should return ``True`` if the file is to be copied.
978 """
976 """
979 # Skip revlogs.
977 # Skip revlogs.
980 if path.endswith((b'.i', b'.d')):
978 if path.endswith((b'.i', b'.d')):
981 return False
979 return False
982 # Skip transaction related files.
980 # Skip transaction related files.
983 if path.startswith(b'undo'):
981 if path.startswith(b'undo'):
984 return False
982 return False
985 # Only copy regular files.
983 # Only copy regular files.
986 if mode != stat.S_IFREG:
984 if mode != stat.S_IFREG:
987 return False
985 return False
988 # Skip other skipped files.
986 # Skip other skipped files.
989 if path in (b'lock', b'fncache'):
987 if path in (b'lock', b'fncache'):
990 return False
988 return False
991
989
992 return True
990 return True
993
991
994
992
995 def _finishdatamigration(ui, srcrepo, dstrepo, requirements):
993 def _finishdatamigration(ui, srcrepo, dstrepo, requirements):
996 """Hook point for extensions to perform additional actions during upgrade.
994 """Hook point for extensions to perform additional actions during upgrade.
997
995
998 This function is called after revlogs and store files have been copied but
996 This function is called after revlogs and store files have been copied but
999 before the new store is swapped into the original location.
997 before the new store is swapped into the original location.
1000 """
998 """
1001
999
1002
1000
1003 def _upgraderepo(
1001 def _upgraderepo(
1004 ui, srcrepo, dstrepo, requirements, actions, revlogs=UPGRADE_ALL_REVLOGS
1002 ui, srcrepo, dstrepo, requirements, actions, revlogs=UPGRADE_ALL_REVLOGS
1005 ):
1003 ):
1006 """Do the low-level work of upgrading a repository.
1004 """Do the low-level work of upgrading a repository.
1007
1005
1008 The upgrade is effectively performed as a copy between a source
1006 The upgrade is effectively performed as a copy between a source
1009 repository and a temporary destination repository.
1007 repository and a temporary destination repository.
1010
1008
1011 The source repository is unmodified for as long as possible so the
1009 The source repository is unmodified for as long as possible so the
1012 upgrade can abort at any time without causing loss of service for
1010 upgrade can abort at any time without causing loss of service for
1013 readers and without corrupting the source repository.
1011 readers and without corrupting the source repository.
1014 """
1012 """
1015 assert srcrepo.currentwlock()
1013 assert srcrepo.currentwlock()
1016 assert dstrepo.currentwlock()
1014 assert dstrepo.currentwlock()
1017
1015
1018 ui.write(
1016 ui.write(
1019 _(
1017 _(
1020 b'(it is safe to interrupt this process any time before '
1018 b'(it is safe to interrupt this process any time before '
1021 b'data migration completes)\n'
1019 b'data migration completes)\n'
1022 )
1020 )
1023 )
1021 )
1024
1022
1025 if b're-delta-all' in actions:
1023 if b're-delta-all' in actions:
1026 deltareuse = revlog.revlog.DELTAREUSENEVER
1024 deltareuse = revlog.revlog.DELTAREUSENEVER
1027 elif b're-delta-parent' in actions:
1025 elif b're-delta-parent' in actions:
1028 deltareuse = revlog.revlog.DELTAREUSESAMEREVS
1026 deltareuse = revlog.revlog.DELTAREUSESAMEREVS
1029 elif b're-delta-multibase' in actions:
1027 elif b're-delta-multibase' in actions:
1030 deltareuse = revlog.revlog.DELTAREUSESAMEREVS
1028 deltareuse = revlog.revlog.DELTAREUSESAMEREVS
1031 elif b're-delta-fulladd' in actions:
1029 elif b're-delta-fulladd' in actions:
1032 deltareuse = revlog.revlog.DELTAREUSEFULLADD
1030 deltareuse = revlog.revlog.DELTAREUSEFULLADD
1033 else:
1031 else:
1034 deltareuse = revlog.revlog.DELTAREUSEALWAYS
1032 deltareuse = revlog.revlog.DELTAREUSEALWAYS
1035
1033
1036 with dstrepo.transaction(b'upgrade') as tr:
1034 with dstrepo.transaction(b'upgrade') as tr:
1037 _clonerevlogs(
1035 _clonerevlogs(
1038 ui,
1036 ui,
1039 srcrepo,
1037 srcrepo,
1040 dstrepo,
1038 dstrepo,
1041 tr,
1039 tr,
1042 deltareuse,
1040 deltareuse,
1043 b're-delta-multibase' in actions,
1041 b're-delta-multibase' in actions,
1044 revlogs=revlogs,
1042 revlogs=revlogs,
1045 )
1043 )
1046
1044
1047 # Now copy other files in the store directory.
1045 # Now copy other files in the store directory.
1048 # The sorted() makes execution deterministic.
1046 # The sorted() makes execution deterministic.
1049 for p, kind, st in sorted(srcrepo.store.vfs.readdir(b'', stat=True)):
1047 for p, kind, st in sorted(srcrepo.store.vfs.readdir(b'', stat=True)):
1050 if not _filterstorefile(srcrepo, dstrepo, requirements, p, kind, st):
1048 if not _filterstorefile(srcrepo, dstrepo, requirements, p, kind, st):
1051 continue
1049 continue
1052
1050
1053 srcrepo.ui.write(_(b'copying %s\n') % p)
1051 srcrepo.ui.write(_(b'copying %s\n') % p)
1054 src = srcrepo.store.rawvfs.join(p)
1052 src = srcrepo.store.rawvfs.join(p)
1055 dst = dstrepo.store.rawvfs.join(p)
1053 dst = dstrepo.store.rawvfs.join(p)
1056 util.copyfile(src, dst, copystat=True)
1054 util.copyfile(src, dst, copystat=True)
1057
1055
1058 _finishdatamigration(ui, srcrepo, dstrepo, requirements)
1056 _finishdatamigration(ui, srcrepo, dstrepo, requirements)
1059
1057
1060 ui.write(_(b'data fully migrated to temporary repository\n'))
1058 ui.write(_(b'data fully migrated to temporary repository\n'))
1061
1059
1062 backuppath = pycompat.mkdtemp(prefix=b'upgradebackup.', dir=srcrepo.path)
1060 backuppath = pycompat.mkdtemp(prefix=b'upgradebackup.', dir=srcrepo.path)
1063 backupvfs = vfsmod.vfs(backuppath)
1061 backupvfs = vfsmod.vfs(backuppath)
1064
1062
1065 # Make a backup of requires file first, as it is the first to be modified.
1063 # Make a backup of requires file first, as it is the first to be modified.
1066 util.copyfile(srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires'))
1064 util.copyfile(srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires'))
1067
1065
1068 # We install an arbitrary requirement that clients must not support
1066 # We install an arbitrary requirement that clients must not support
1069 # as a mechanism to lock out new clients during the data swap. This is
1067 # as a mechanism to lock out new clients during the data swap. This is
1070 # better than allowing a client to continue while the repository is in
1068 # better than allowing a client to continue while the repository is in
1071 # an inconsistent state.
1069 # an inconsistent state.
1072 ui.write(
1070 ui.write(
1073 _(
1071 _(
1074 b'marking source repository as being upgraded; clients will be '
1072 b'marking source repository as being upgraded; clients will be '
1075 b'unable to read from repository\n'
1073 b'unable to read from repository\n'
1076 )
1074 )
1077 )
1075 )
1078 scmutil.writerequires(
1076 scmutil.writerequires(
1079 srcrepo.vfs, srcrepo.requirements | {b'upgradeinprogress'}
1077 srcrepo.vfs, srcrepo.requirements | {b'upgradeinprogress'}
1080 )
1078 )
1081
1079
1082 ui.write(_(b'starting in-place swap of repository data\n'))
1080 ui.write(_(b'starting in-place swap of repository data\n'))
1083 ui.write(_(b'replaced files will be backed up at %s\n') % backuppath)
1081 ui.write(_(b'replaced files will be backed up at %s\n') % backuppath)
1084
1082
1085 # Now swap in the new store directory. Doing it as a rename should make
1083 # Now swap in the new store directory. Doing it as a rename should make
1086 # the operation nearly instantaneous and atomic (at least in well-behaved
1084 # the operation nearly instantaneous and atomic (at least in well-behaved
1087 # environments).
1085 # environments).
1088 ui.write(_(b'replacing store...\n'))
1086 ui.write(_(b'replacing store...\n'))
1089 tstart = util.timer()
1087 tstart = util.timer()
1090 util.rename(srcrepo.spath, backupvfs.join(b'store'))
1088 util.rename(srcrepo.spath, backupvfs.join(b'store'))
1091 util.rename(dstrepo.spath, srcrepo.spath)
1089 util.rename(dstrepo.spath, srcrepo.spath)
1092 elapsed = util.timer() - tstart
1090 elapsed = util.timer() - tstart
1093 ui.write(
1091 ui.write(
1094 _(
1092 _(
1095 b'store replacement complete; repository was inconsistent for '
1093 b'store replacement complete; repository was inconsistent for '
1096 b'%0.1fs\n'
1094 b'%0.1fs\n'
1097 )
1095 )
1098 % elapsed
1096 % elapsed
1099 )
1097 )
1100
1098
1101 # We first write the requirements file. Any new requirements will lock
1099 # We first write the requirements file. Any new requirements will lock
1102 # out legacy clients.
1100 # out legacy clients.
1103 ui.write(
1101 ui.write(
1104 _(
1102 _(
1105 b'finalizing requirements file and making repository readable '
1103 b'finalizing requirements file and making repository readable '
1106 b'again\n'
1104 b'again\n'
1107 )
1105 )
1108 )
1106 )
1109 scmutil.writerequires(srcrepo.vfs, requirements)
1107 scmutil.writerequires(srcrepo.vfs, requirements)
1110
1108
1111 # The lock file from the old store won't be removed because nothing has a
1109 # The lock file from the old store won't be removed because nothing has a
1112 # reference to its new location. So clean it up manually. Alternatively, we
1110 # reference to its new location. So clean it up manually. Alternatively, we
1113 # could update srcrepo.svfs and other variables to point to the new
1111 # could update srcrepo.svfs and other variables to point to the new
1114 # location. This is simpler.
1112 # location. This is simpler.
1115 backupvfs.unlink(b'store/lock')
1113 backupvfs.unlink(b'store/lock')
1116
1114
1117 return backuppath
1115 return backuppath
1118
1116
1119
1117
1120 def upgraderepo(
1118 def upgraderepo(
1121 ui,
1119 ui,
1122 repo,
1120 repo,
1123 run=False,
1121 run=False,
1124 optimize=None,
1122 optimize=None,
1125 backup=True,
1123 backup=True,
1126 manifest=None,
1124 manifest=None,
1127 changelog=None,
1125 changelog=None,
1128 ):
1126 ):
1129 """Upgrade a repository in place."""
1127 """Upgrade a repository in place."""
1130 if optimize is None:
1128 if optimize is None:
1131 optimize = []
1129 optimize = []
1132 optimize = {legacy_opts_map.get(o, o) for o in optimize}
1130 optimize = {legacy_opts_map.get(o, o) for o in optimize}
1133 repo = repo.unfiltered()
1131 repo = repo.unfiltered()
1134
1132
1135 revlogs = set(UPGRADE_ALL_REVLOGS)
1133 revlogs = set(UPGRADE_ALL_REVLOGS)
1136 specentries = ((b'c', changelog), (b'm', manifest))
1134 specentries = ((b'c', changelog), (b'm', manifest))
1137 specified = [(y, x) for (y, x) in specentries if x is not None]
1135 specified = [(y, x) for (y, x) in specentries if x is not None]
1138 if specified:
1136 if specified:
1139 # we have some limitation on revlogs to be recloned
1137 # we have some limitation on revlogs to be recloned
1140 if any(x for y, x in specified):
1138 if any(x for y, x in specified):
1141 revlogs = set()
1139 revlogs = set()
1142 for r, enabled in specified:
1140 for r, enabled in specified:
1143 if enabled:
1141 if enabled:
1144 if r == b'c':
1142 if r == b'c':
1145 revlogs.add(UPGRADE_CHANGELOG)
1143 revlogs.add(UPGRADE_CHANGELOG)
1146 elif r == b'm':
1144 elif r == b'm':
1147 revlogs.add(UPGRADE_MANIFEST)
1145 revlogs.add(UPGRADE_MANIFEST)
1148 else:
1146 else:
1149 # none are enabled
1147 # none are enabled
1150 for r, __ in specified:
1148 for r, __ in specified:
1151 if r == b'c':
1149 if r == b'c':
1152 revlogs.discard(UPGRADE_CHANGELOG)
1150 revlogs.discard(UPGRADE_CHANGELOG)
1153 elif r == b'm':
1151 elif r == b'm':
1154 revlogs.discard(UPGRADE_MANIFEST)
1152 revlogs.discard(UPGRADE_MANIFEST)
1155
1153
1156 # Ensure the repository can be upgraded.
1154 # Ensure the repository can be upgraded.
1157 missingreqs = requiredsourcerequirements(repo) - repo.requirements
1155 missingreqs = requiredsourcerequirements(repo) - repo.requirements
1158 if missingreqs:
1156 if missingreqs:
1159 raise error.Abort(
1157 raise error.Abort(
1160 _(b'cannot upgrade repository; requirement missing: %s')
1158 _(b'cannot upgrade repository; requirement missing: %s')
1161 % _(b', ').join(sorted(missingreqs))
1159 % _(b', ').join(sorted(missingreqs))
1162 )
1160 )
1163
1161
1164 blockedreqs = blocksourcerequirements(repo) & repo.requirements
1162 blockedreqs = blocksourcerequirements(repo) & repo.requirements
1165 if blockedreqs:
1163 if blockedreqs:
1166 raise error.Abort(
1164 raise error.Abort(
1167 _(
1165 _(
1168 b'cannot upgrade repository; unsupported source '
1166 b'cannot upgrade repository; unsupported source '
1169 b'requirement: %s'
1167 b'requirement: %s'
1170 )
1168 )
1171 % _(b', ').join(sorted(blockedreqs))
1169 % _(b', ').join(sorted(blockedreqs))
1172 )
1170 )
1173
1171
1174 # FUTURE there is potentially a need to control the wanted requirements via
1172 # FUTURE there is potentially a need to control the wanted requirements via
1175 # command arguments or via an extension hook point.
1173 # command arguments or via an extension hook point.
1176 newreqs = localrepo.newreporequirements(
1174 newreqs = localrepo.newreporequirements(
1177 repo.ui, localrepo.defaultcreateopts(repo.ui)
1175 repo.ui, localrepo.defaultcreateopts(repo.ui)
1178 )
1176 )
1179 newreqs.update(preservedrequirements(repo))
1177 newreqs.update(preservedrequirements(repo))
1180
1178
1181 noremovereqs = (
1179 noremovereqs = (
1182 repo.requirements - newreqs - supportremovedrequirements(repo)
1180 repo.requirements - newreqs - supportremovedrequirements(repo)
1183 )
1181 )
1184 if noremovereqs:
1182 if noremovereqs:
1185 raise error.Abort(
1183 raise error.Abort(
1186 _(
1184 _(
1187 b'cannot upgrade repository; requirement would be '
1185 b'cannot upgrade repository; requirement would be '
1188 b'removed: %s'
1186 b'removed: %s'
1189 )
1187 )
1190 % _(b', ').join(sorted(noremovereqs))
1188 % _(b', ').join(sorted(noremovereqs))
1191 )
1189 )
1192
1190
1193 noaddreqs = newreqs - repo.requirements - allowednewrequirements(repo)
1191 noaddreqs = newreqs - repo.requirements - allowednewrequirements(repo)
1194 if noaddreqs:
1192 if noaddreqs:
1195 raise error.Abort(
1193 raise error.Abort(
1196 _(
1194 _(
1197 b'cannot upgrade repository; do not support adding '
1195 b'cannot upgrade repository; do not support adding '
1198 b'requirement: %s'
1196 b'requirement: %s'
1199 )
1197 )
1200 % _(b', ').join(sorted(noaddreqs))
1198 % _(b', ').join(sorted(noaddreqs))
1201 )
1199 )
1202
1200
1203 unsupportedreqs = newreqs - supporteddestrequirements(repo)
1201 unsupportedreqs = newreqs - supporteddestrequirements(repo)
1204 if unsupportedreqs:
1202 if unsupportedreqs:
1205 raise error.Abort(
1203 raise error.Abort(
1206 _(
1204 _(
1207 b'cannot upgrade repository; do not support '
1205 b'cannot upgrade repository; do not support '
1208 b'destination requirement: %s'
1206 b'destination requirement: %s'
1209 )
1207 )
1210 % _(b', ').join(sorted(unsupportedreqs))
1208 % _(b', ').join(sorted(unsupportedreqs))
1211 )
1209 )
1212
1210
1213 # Find and validate all improvements that can be made.
1211 # Find and validate all improvements that can be made.
1214 alloptimizations = findoptimizations(repo)
1212 alloptimizations = findoptimizations(repo)
1215
1213
1216 # Apply and Validate arguments.
1214 # Apply and Validate arguments.
1217 optimizations = []
1215 optimizations = []
1218 for o in alloptimizations:
1216 for o in alloptimizations:
1219 if o.name in optimize:
1217 if o.name in optimize:
1220 optimizations.append(o)
1218 optimizations.append(o)
1221 optimize.discard(o.name)
1219 optimize.discard(o.name)
1222
1220
1223 if optimize: # anything left is unknown
1221 if optimize: # anything left is unknown
1224 raise error.Abort(
1222 raise error.Abort(
1225 _(b'unknown optimization action requested: %s')
1223 _(b'unknown optimization action requested: %s')
1226 % b', '.join(sorted(optimize)),
1224 % b', '.join(sorted(optimize)),
1227 hint=_(b'run without arguments to see valid optimizations'),
1225 hint=_(b'run without arguments to see valid optimizations'),
1228 )
1226 )
1229
1227
1230 deficiencies = finddeficiencies(repo)
1228 deficiencies = finddeficiencies(repo)
1231 actions = determineactions(repo, deficiencies, repo.requirements, newreqs)
1229 actions = determineactions(repo, deficiencies, repo.requirements, newreqs)
1232 actions.extend(
1230 actions.extend(
1233 o
1231 o
1234 for o in sorted(optimizations)
1232 for o in sorted(optimizations)
1235 # determineactions could have added optimisation
1233 # determineactions could have added optimisation
1236 if o not in actions
1234 if o not in actions
1237 )
1235 )
1238
1236
1239 removedreqs = repo.requirements - newreqs
1237 removedreqs = repo.requirements - newreqs
1240 addedreqs = newreqs - repo.requirements
1238 addedreqs = newreqs - repo.requirements
1241
1239
1242 if revlogs != UPGRADE_ALL_REVLOGS:
1240 if revlogs != UPGRADE_ALL_REVLOGS:
1243 incompatible = RECLONES_REQUIREMENTS & (removedreqs | addedreqs)
1241 incompatible = RECLONES_REQUIREMENTS & (removedreqs | addedreqs)
1244 if incompatible:
1242 if incompatible:
1245 msg = _(
1243 msg = _(
1246 b'ignoring revlogs selection flags, format requirements '
1244 b'ignoring revlogs selection flags, format requirements '
1247 b'change: %s\n'
1245 b'change: %s\n'
1248 )
1246 )
1249 ui.warn(msg % b', '.join(sorted(incompatible)))
1247 ui.warn(msg % b', '.join(sorted(incompatible)))
1250 revlogs = UPGRADE_ALL_REVLOGS
1248 revlogs = UPGRADE_ALL_REVLOGS
1251
1249
1252 def write_labeled(l, label):
1250 def write_labeled(l, label):
1253 first = True
1251 first = True
1254 for r in sorted(l):
1252 for r in sorted(l):
1255 if not first:
1253 if not first:
1256 ui.write(b', ')
1254 ui.write(b', ')
1257 ui.write(r, label=label)
1255 ui.write(r, label=label)
1258 first = False
1256 first = False
1259
1257
1260 def printrequirements():
1258 def printrequirements():
1261 ui.write(_(b'requirements\n'))
1259 ui.write(_(b'requirements\n'))
1262 ui.write(_(b' preserved: '))
1260 ui.write(_(b' preserved: '))
1263 write_labeled(
1261 write_labeled(
1264 newreqs & repo.requirements, "upgrade-repo.requirement.preserved"
1262 newreqs & repo.requirements, "upgrade-repo.requirement.preserved"
1265 )
1263 )
1266 ui.write((b'\n'))
1264 ui.write((b'\n'))
1267 removed = repo.requirements - newreqs
1265 removed = repo.requirements - newreqs
1268 if repo.requirements - newreqs:
1266 if repo.requirements - newreqs:
1269 ui.write(_(b' removed: '))
1267 ui.write(_(b' removed: '))
1270 write_labeled(removed, "upgrade-repo.requirement.removed")
1268 write_labeled(removed, "upgrade-repo.requirement.removed")
1271 ui.write((b'\n'))
1269 ui.write((b'\n'))
1272 added = newreqs - repo.requirements
1270 added = newreqs - repo.requirements
1273 if added:
1271 if added:
1274 ui.write(_(b' added: '))
1272 ui.write(_(b' added: '))
1275 write_labeled(added, "upgrade-repo.requirement.added")
1273 write_labeled(added, "upgrade-repo.requirement.added")
1276 ui.write((b'\n'))
1274 ui.write((b'\n'))
1277 ui.write(b'\n')
1275 ui.write(b'\n')
1278
1276
1279 def printupgradeactions():
1277 def printupgradeactions():
1280 for a in actions:
1278 for a in actions:
1281 ui.write(b'%s\n %s\n\n' % (a.name, a.upgrademessage))
1279 ui.write(b'%s\n %s\n\n' % (a.name, a.upgrademessage))
1282
1280
1283 if not run:
1281 if not run:
1284 fromconfig = []
1282 fromconfig = []
1285 onlydefault = []
1283 onlydefault = []
1286
1284
1287 for d in deficiencies:
1285 for d in deficiencies:
1288 if d.fromconfig(repo):
1286 if d.fromconfig(repo):
1289 fromconfig.append(d)
1287 fromconfig.append(d)
1290 elif d.default:
1288 elif d.default:
1291 onlydefault.append(d)
1289 onlydefault.append(d)
1292
1290
1293 if fromconfig or onlydefault:
1291 if fromconfig or onlydefault:
1294
1292
1295 if fromconfig:
1293 if fromconfig:
1296 ui.write(
1294 ui.write(
1297 _(
1295 _(
1298 b'repository lacks features recommended by '
1296 b'repository lacks features recommended by '
1299 b'current config options:\n\n'
1297 b'current config options:\n\n'
1300 )
1298 )
1301 )
1299 )
1302 for i in fromconfig:
1300 for i in fromconfig:
1303 ui.write(b'%s\n %s\n\n' % (i.name, i.description))
1301 ui.write(b'%s\n %s\n\n' % (i.name, i.description))
1304
1302
1305 if onlydefault:
1303 if onlydefault:
1306 ui.write(
1304 ui.write(
1307 _(
1305 _(
1308 b'repository lacks features used by the default '
1306 b'repository lacks features used by the default '
1309 b'config options:\n\n'
1307 b'config options:\n\n'
1310 )
1308 )
1311 )
1309 )
1312 for i in onlydefault:
1310 for i in onlydefault:
1313 ui.write(b'%s\n %s\n\n' % (i.name, i.description))
1311 ui.write(b'%s\n %s\n\n' % (i.name, i.description))
1314
1312
1315 ui.write(b'\n')
1313 ui.write(b'\n')
1316 else:
1314 else:
1317 ui.write(
1315 ui.write(
1318 _(
1316 _(
1319 b'(no feature deficiencies found in existing '
1317 b'(no feature deficiencies found in existing '
1320 b'repository)\n'
1318 b'repository)\n'
1321 )
1319 )
1322 )
1320 )
1323
1321
1324 ui.write(
1322 ui.write(
1325 _(
1323 _(
1326 b'performing an upgrade with "--run" will make the following '
1324 b'performing an upgrade with "--run" will make the following '
1327 b'changes:\n\n'
1325 b'changes:\n\n'
1328 )
1326 )
1329 )
1327 )
1330
1328
1331 printrequirements()
1329 printrequirements()
1332 printupgradeactions()
1330 printupgradeactions()
1333
1331
1334 unusedoptimize = [i for i in alloptimizations if i not in actions]
1332 unusedoptimize = [i for i in alloptimizations if i not in actions]
1335
1333
1336 if unusedoptimize:
1334 if unusedoptimize:
1337 ui.write(
1335 ui.write(
1338 _(
1336 _(
1339 b'additional optimizations are available by specifying '
1337 b'additional optimizations are available by specifying '
1340 b'"--optimize <name>":\n\n'
1338 b'"--optimize <name>":\n\n'
1341 )
1339 )
1342 )
1340 )
1343 for i in unusedoptimize:
1341 for i in unusedoptimize:
1344 ui.write(_(b'%s\n %s\n\n') % (i.name, i.description))
1342 ui.write(_(b'%s\n %s\n\n') % (i.name, i.description))
1345 return
1343 return
1346
1344
1347 # Else we're in the run=true case.
1345 # Else we're in the run=true case.
1348 ui.write(_(b'upgrade will perform the following actions:\n\n'))
1346 ui.write(_(b'upgrade will perform the following actions:\n\n'))
1349 printrequirements()
1347 printrequirements()
1350 printupgradeactions()
1348 printupgradeactions()
1351
1349
1352 upgradeactions = [a.name for a in actions]
1350 upgradeactions = [a.name for a in actions]
1353
1351
1354 ui.write(_(b'beginning upgrade...\n'))
1352 ui.write(_(b'beginning upgrade...\n'))
1355 with repo.wlock(), repo.lock():
1353 with repo.wlock(), repo.lock():
1356 ui.write(_(b'repository locked and read-only\n'))
1354 ui.write(_(b'repository locked and read-only\n'))
1357 # Our strategy for upgrading the repository is to create a new,
1355 # Our strategy for upgrading the repository is to create a new,
1358 # temporary repository, write data to it, then do a swap of the
1356 # temporary repository, write data to it, then do a swap of the
1359 # data. There are less heavyweight ways to do this, but it is easier
1357 # data. There are less heavyweight ways to do this, but it is easier
1360 # to create a new repo object than to instantiate all the components
1358 # to create a new repo object than to instantiate all the components
1361 # (like the store) separately.
1359 # (like the store) separately.
1362 tmppath = pycompat.mkdtemp(prefix=b'upgrade.', dir=repo.path)
1360 tmppath = pycompat.mkdtemp(prefix=b'upgrade.', dir=repo.path)
1363 backuppath = None
1361 backuppath = None
1364 try:
1362 try:
1365 ui.write(
1363 ui.write(
1366 _(
1364 _(
1367 b'creating temporary repository to stage migrated '
1365 b'creating temporary repository to stage migrated '
1368 b'data: %s\n'
1366 b'data: %s\n'
1369 )
1367 )
1370 % tmppath
1368 % tmppath
1371 )
1369 )
1372
1370
1373 # clone ui without using ui.copy because repo.ui is protected
1371 # clone ui without using ui.copy because repo.ui is protected
1374 repoui = repo.ui.__class__(repo.ui)
1372 repoui = repo.ui.__class__(repo.ui)
1375 dstrepo = hg.repository(repoui, path=tmppath, create=True)
1373 dstrepo = hg.repository(repoui, path=tmppath, create=True)
1376
1374
1377 with dstrepo.wlock(), dstrepo.lock():
1375 with dstrepo.wlock(), dstrepo.lock():
1378 backuppath = _upgraderepo(
1376 backuppath = _upgraderepo(
1379 ui, repo, dstrepo, newreqs, upgradeactions, revlogs=revlogs
1377 ui, repo, dstrepo, newreqs, upgradeactions, revlogs=revlogs
1380 )
1378 )
1381 if not (backup or backuppath is None):
1379 if not (backup or backuppath is None):
1382 ui.write(_(b'removing old repository content%s\n') % backuppath)
1380 ui.write(_(b'removing old repository content%s\n') % backuppath)
1383 repo.vfs.rmtree(backuppath, forcibly=True)
1381 repo.vfs.rmtree(backuppath, forcibly=True)
1384 backuppath = None
1382 backuppath = None
1385
1383
1386 finally:
1384 finally:
1387 ui.write(_(b'removing temporary repository %s\n') % tmppath)
1385 ui.write(_(b'removing temporary repository %s\n') % tmppath)
1388 repo.vfs.rmtree(tmppath, forcibly=True)
1386 repo.vfs.rmtree(tmppath, forcibly=True)
1389
1387
1390 if backuppath:
1388 if backuppath:
1391 ui.warn(
1389 ui.warn(
1392 _(b'copy of old repository backed up at %s\n') % backuppath
1390 _(b'copy of old repository backed up at %s\n') % backuppath
1393 )
1391 )
1394 ui.warn(
1392 ui.warn(
1395 _(
1393 _(
1396 b'the old repository will not be deleted; remove '
1394 b'the old repository will not be deleted; remove '
1397 b'it to free up disk space once the upgraded '
1395 b'it to free up disk space once the upgraded '
1398 b'repository is verified\n'
1396 b'repository is verified\n'
1399 )
1397 )
1400 )
1398 )
@@ -1,911 +1,913 b''
1 // status.rs
1 // status.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Rust implementation of dirstate.status (dirstate.py).
8 //! Rust implementation of dirstate.status (dirstate.py).
9 //! It is currently missing a lot of functionality compared to the Python one
9 //! It is currently missing a lot of functionality compared to the Python one
10 //! and will only be triggered in narrow cases.
10 //! and will only be triggered in narrow cases.
11
11
12 use crate::{
12 use crate::{
13 dirstate::SIZE_FROM_OTHER_PARENT,
13 dirstate::SIZE_FROM_OTHER_PARENT,
14 filepatterns::PatternFileWarning,
14 filepatterns::PatternFileWarning,
15 matchers::{get_ignore_function, Matcher, VisitChildrenSet},
15 matchers::{get_ignore_function, Matcher, VisitChildrenSet},
16 utils::{
16 utils::{
17 files::{find_dirs, HgMetadata},
17 files::{find_dirs, HgMetadata},
18 hg_path::{
18 hg_path::{
19 hg_path_to_path_buf, os_string_to_hg_path_buf, HgPath, HgPathBuf,
19 hg_path_to_path_buf, os_string_to_hg_path_buf, HgPath, HgPathBuf,
20 HgPathError,
20 HgPathError,
21 },
21 },
22 path_auditor::PathAuditor,
22 path_auditor::PathAuditor,
23 },
23 },
24 CopyMap, DirstateEntry, DirstateMap, EntryState, FastHashMap,
24 CopyMap, DirstateEntry, DirstateMap, EntryState, FastHashMap,
25 PatternError,
25 PatternError,
26 };
26 };
27 use lazy_static::lazy_static;
27 use lazy_static::lazy_static;
28 use micro_timer::timed;
28 use micro_timer::timed;
29 use rayon::prelude::*;
29 use rayon::prelude::*;
30 use std::{
30 use std::{
31 borrow::Cow,
31 borrow::Cow,
32 collections::HashSet,
32 collections::HashSet,
33 fs::{read_dir, DirEntry},
33 fs::{read_dir, DirEntry},
34 io::ErrorKind,
34 io::ErrorKind,
35 ops::Deref,
35 ops::Deref,
36 path::{Path, PathBuf},
36 path::{Path, PathBuf},
37 };
37 };
38
38
39 /// Wrong type of file from a `BadMatch`
39 /// Wrong type of file from a `BadMatch`
40 /// Note: a lot of those don't exist on all platforms.
40 /// Note: a lot of those don't exist on all platforms.
41 #[derive(Debug, Copy, Clone)]
41 #[derive(Debug, Copy, Clone)]
42 pub enum BadType {
42 pub enum BadType {
43 CharacterDevice,
43 CharacterDevice,
44 BlockDevice,
44 BlockDevice,
45 FIFO,
45 FIFO,
46 Socket,
46 Socket,
47 Directory,
47 Directory,
48 Unknown,
48 Unknown,
49 }
49 }
50
50
51 impl ToString for BadType {
51 impl ToString for BadType {
52 fn to_string(&self) -> String {
52 fn to_string(&self) -> String {
53 match self {
53 match self {
54 BadType::CharacterDevice => "character device",
54 BadType::CharacterDevice => "character device",
55 BadType::BlockDevice => "block device",
55 BadType::BlockDevice => "block device",
56 BadType::FIFO => "fifo",
56 BadType::FIFO => "fifo",
57 BadType::Socket => "socket",
57 BadType::Socket => "socket",
58 BadType::Directory => "directory",
58 BadType::Directory => "directory",
59 BadType::Unknown => "unknown",
59 BadType::Unknown => "unknown",
60 }
60 }
61 .to_string()
61 .to_string()
62 }
62 }
63 }
63 }
64
64
65 /// Was explicitly matched but cannot be found/accessed
65 /// Was explicitly matched but cannot be found/accessed
66 #[derive(Debug, Copy, Clone)]
66 #[derive(Debug, Copy, Clone)]
67 pub enum BadMatch {
67 pub enum BadMatch {
68 OsError(i32),
68 OsError(i32),
69 BadType(BadType),
69 BadType(BadType),
70 }
70 }
71
71
72 /// Marker enum used to dispatch new status entries into the right collections.
72 /// Marker enum used to dispatch new status entries into the right collections.
73 /// Is similar to `crate::EntryState`, but represents the transient state of
73 /// Is similar to `crate::EntryState`, but represents the transient state of
74 /// entries during the lifetime of a command.
74 /// entries during the lifetime of a command.
75 #[derive(Debug, Copy, Clone)]
75 #[derive(Debug, Copy, Clone)]
76 enum Dispatch {
76 enum Dispatch {
77 Unsure,
77 Unsure,
78 Modified,
78 Modified,
79 Added,
79 Added,
80 Removed,
80 Removed,
81 Deleted,
81 Deleted,
82 Clean,
82 Clean,
83 Unknown,
83 Unknown,
84 Ignored,
84 Ignored,
85 /// Empty dispatch, the file is not worth listing
85 /// Empty dispatch, the file is not worth listing
86 None,
86 None,
87 /// Was explicitly matched but cannot be found/accessed
87 /// Was explicitly matched but cannot be found/accessed
88 Bad(BadMatch),
88 Bad(BadMatch),
89 Directory {
89 Directory {
90 /// True if the directory used to be a file in the dmap so we can say
90 /// True if the directory used to be a file in the dmap so we can say
91 /// that it's been removed.
91 /// that it's been removed.
92 was_file: bool,
92 was_file: bool,
93 },
93 },
94 }
94 }
95
95
96 type IoResult<T> = std::io::Result<T>;
96 type IoResult<T> = std::io::Result<T>;
97 /// `Box<dyn Trait>` is syntactic sugar for `Box<dyn Trait, 'static>`, so add
97 /// `Box<dyn Trait>` is syntactic sugar for `Box<dyn Trait, 'static>`, so add
98 /// an explicit lifetime here to not fight `'static` bounds "out of nowhere".
98 /// an explicit lifetime here to not fight `'static` bounds "out of nowhere".
99 type IgnoreFnType<'a> = Box<dyn for<'r> Fn(&'r HgPath) -> bool + Sync + 'a>;
99 type IgnoreFnType<'a> = Box<dyn for<'r> Fn(&'r HgPath) -> bool + Sync + 'a>;
100
100
101 /// Dates and times that are outside the 31-bit signed range are compared
101 /// Dates and times that are outside the 31-bit signed range are compared
102 /// modulo 2^31. This should prevent hg from behaving badly with very large
102 /// modulo 2^31. This should prevent hg from behaving badly with very large
103 /// files or corrupt dates while still having a high probability of detecting
103 /// files or corrupt dates while still having a high probability of detecting
104 /// changes. (issue2608)
104 /// changes. (issue2608)
105 /// TODO I haven't found a way of having `b` be `Into<i32>`, since `From<u64>`
105 /// TODO I haven't found a way of having `b` be `Into<i32>`, since `From<u64>`
106 /// is not defined for `i32`, and there is no `As` trait. This forces the
106 /// is not defined for `i32`, and there is no `As` trait. This forces the
107 /// caller to cast `b` as `i32`.
107 /// caller to cast `b` as `i32`.
108 fn mod_compare(a: i32, b: i32) -> bool {
108 fn mod_compare(a: i32, b: i32) -> bool {
109 a & i32::max_value() != b & i32::max_value()
109 a & i32::max_value() != b & i32::max_value()
110 }
110 }
111
111
112 /// Return a sorted list containing information about the entries
112 /// Return a sorted list containing information about the entries
113 /// in the directory.
113 /// in the directory.
114 ///
114 ///
115 /// * `skip_dot_hg` - Return an empty vec if `path` contains a `.hg` directory
115 /// * `skip_dot_hg` - Return an empty vec if `path` contains a `.hg` directory
116 fn list_directory(
116 fn list_directory(
117 path: impl AsRef<Path>,
117 path: impl AsRef<Path>,
118 skip_dot_hg: bool,
118 skip_dot_hg: bool,
119 ) -> std::io::Result<Vec<(HgPathBuf, DirEntry)>> {
119 ) -> std::io::Result<Vec<(HgPathBuf, DirEntry)>> {
120 let mut results = vec![];
120 let mut results = vec![];
121 let entries = read_dir(path.as_ref())?;
121 let entries = read_dir(path.as_ref())?;
122
122
123 for entry in entries {
123 for entry in entries {
124 let entry = entry?;
124 let entry = entry?;
125 let filename = os_string_to_hg_path_buf(entry.file_name())?;
125 let filename = os_string_to_hg_path_buf(entry.file_name())?;
126 let file_type = entry.file_type()?;
126 let file_type = entry.file_type()?;
127 if skip_dot_hg && filename.as_bytes() == b".hg" && file_type.is_dir() {
127 if skip_dot_hg && filename.as_bytes() == b".hg" && file_type.is_dir() {
128 return Ok(vec![]);
128 return Ok(vec![]);
129 } else {
129 } else {
130 results.push((HgPathBuf::from(filename), entry))
130 results.push((HgPathBuf::from(filename), entry))
131 }
131 }
132 }
132 }
133
133
134 results.sort_unstable_by_key(|e| e.0.clone());
134 results.sort_unstable_by_key(|e| e.0.clone());
135 Ok(results)
135 Ok(results)
136 }
136 }
137
137
138 /// The file corresponding to the dirstate entry was found on the filesystem.
138 /// The file corresponding to the dirstate entry was found on the filesystem.
139 fn dispatch_found(
139 fn dispatch_found(
140 filename: impl AsRef<HgPath>,
140 filename: impl AsRef<HgPath>,
141 entry: DirstateEntry,
141 entry: DirstateEntry,
142 metadata: HgMetadata,
142 metadata: HgMetadata,
143 copy_map: &CopyMap,
143 copy_map: &CopyMap,
144 options: StatusOptions,
144 options: StatusOptions,
145 ) -> Dispatch {
145 ) -> Dispatch {
146 let DirstateEntry {
146 let DirstateEntry {
147 state,
147 state,
148 mode,
148 mode,
149 mtime,
149 mtime,
150 size,
150 size,
151 } = entry;
151 } = entry;
152
152
153 let HgMetadata {
153 let HgMetadata {
154 st_mode,
154 st_mode,
155 st_size,
155 st_size,
156 st_mtime,
156 st_mtime,
157 ..
157 ..
158 } = metadata;
158 } = metadata;
159
159
160 match state {
160 match state {
161 EntryState::Normal => {
161 EntryState::Normal => {
162 let size_changed = mod_compare(size, st_size as i32);
162 let size_changed = mod_compare(size, st_size as i32);
163 let mode_changed =
163 let mode_changed =
164 (mode ^ st_mode as i32) & 0o100 != 0o000 && options.check_exec;
164 (mode ^ st_mode as i32) & 0o100 != 0o000 && options.check_exec;
165 let metadata_changed = size >= 0 && (size_changed || mode_changed);
165 let metadata_changed = size >= 0 && (size_changed || mode_changed);
166 let other_parent = size == SIZE_FROM_OTHER_PARENT;
166 let other_parent = size == SIZE_FROM_OTHER_PARENT;
167 if metadata_changed
167 if metadata_changed
168 || other_parent
168 || other_parent
169 || copy_map.contains_key(filename.as_ref())
169 || copy_map.contains_key(filename.as_ref())
170 {
170 {
171 Dispatch::Modified
171 Dispatch::Modified
172 } else if mod_compare(mtime, st_mtime as i32) {
172 } else if mod_compare(mtime, st_mtime as i32) {
173 Dispatch::Unsure
173 Dispatch::Unsure
174 } else if st_mtime == options.last_normal_time {
174 } else if st_mtime == options.last_normal_time {
175 // the file may have just been marked as normal and
175 // the file may have just been marked as normal and
176 // it may have changed in the same second without
176 // it may have changed in the same second without
177 // changing its size. This can happen if we quickly
177 // changing its size. This can happen if we quickly
178 // do multiple commits. Force lookup, so we don't
178 // do multiple commits. Force lookup, so we don't
179 // miss such a racy file change.
179 // miss such a racy file change.
180 Dispatch::Unsure
180 Dispatch::Unsure
181 } else if options.list_clean {
181 } else if options.list_clean {
182 Dispatch::Clean
182 Dispatch::Clean
183 } else {
183 } else {
184 Dispatch::None
184 Dispatch::None
185 }
185 }
186 }
186 }
187 EntryState::Merged => Dispatch::Modified,
187 EntryState::Merged => Dispatch::Modified,
188 EntryState::Added => Dispatch::Added,
188 EntryState::Added => Dispatch::Added,
189 EntryState::Removed => Dispatch::Removed,
189 EntryState::Removed => Dispatch::Removed,
190 EntryState::Unknown => Dispatch::Unknown,
190 EntryState::Unknown => Dispatch::Unknown,
191 }
191 }
192 }
192 }
193
193
194 /// The file corresponding to this Dirstate entry is missing.
194 /// The file corresponding to this Dirstate entry is missing.
195 fn dispatch_missing(state: EntryState) -> Dispatch {
195 fn dispatch_missing(state: EntryState) -> Dispatch {
196 match state {
196 match state {
197 // File was removed from the filesystem during commands
197 // File was removed from the filesystem during commands
198 EntryState::Normal | EntryState::Merged | EntryState::Added => {
198 EntryState::Normal | EntryState::Merged | EntryState::Added => {
199 Dispatch::Deleted
199 Dispatch::Deleted
200 }
200 }
201 // File was removed, everything is normal
201 // File was removed, everything is normal
202 EntryState::Removed => Dispatch::Removed,
202 EntryState::Removed => Dispatch::Removed,
203 // File is unknown to Mercurial, everything is normal
203 // File is unknown to Mercurial, everything is normal
204 EntryState::Unknown => Dispatch::Unknown,
204 EntryState::Unknown => Dispatch::Unknown,
205 }
205 }
206 }
206 }
207
207
208 lazy_static! {
208 lazy_static! {
209 static ref DEFAULT_WORK: HashSet<&'static HgPath> = {
209 static ref DEFAULT_WORK: HashSet<&'static HgPath> = {
210 let mut h = HashSet::new();
210 let mut h = HashSet::new();
211 h.insert(HgPath::new(b""));
211 h.insert(HgPath::new(b""));
212 h
212 h
213 };
213 };
214 }
214 }
215
215
216 /// Get stat data about the files explicitly specified by match.
216 /// Get stat data about the files explicitly specified by match.
217 /// TODO subrepos
217 /// TODO subrepos
218 #[timed]
218 #[timed]
219 fn walk_explicit<'a>(
219 fn walk_explicit<'a>(
220 files: Option<&'a HashSet<&HgPath>>,
220 files: Option<&'a HashSet<&HgPath>>,
221 dmap: &'a DirstateMap,
221 dmap: &'a DirstateMap,
222 root_dir: impl AsRef<Path> + Sync + Send + 'a,
222 root_dir: impl AsRef<Path> + Sync + Send + 'a,
223 options: StatusOptions,
223 options: StatusOptions,
224 ) -> impl ParallelIterator<Item = IoResult<(&'a HgPath, Dispatch)>> {
224 ) -> impl ParallelIterator<Item = IoResult<(&'a HgPath, Dispatch)>> {
225 files
225 files
226 .unwrap_or(&DEFAULT_WORK)
226 .unwrap_or(&DEFAULT_WORK)
227 .par_iter()
227 .par_iter()
228 .map(move |filename| {
228 .map(move |filename| {
229 // TODO normalization
229 // TODO normalization
230 let normalized = filename.as_ref();
230 let normalized = filename.as_ref();
231
231
232 let buf = match hg_path_to_path_buf(normalized) {
232 let buf = match hg_path_to_path_buf(normalized) {
233 Ok(x) => x,
233 Ok(x) => x,
234 Err(e) => return Some(Err(e.into())),
234 Err(e) => return Some(Err(e.into())),
235 };
235 };
236 let target = root_dir.as_ref().join(buf);
236 let target = root_dir.as_ref().join(buf);
237 let st = target.symlink_metadata();
237 let st = target.symlink_metadata();
238 let in_dmap = dmap.get(normalized);
238 let in_dmap = dmap.get(normalized);
239 match st {
239 match st {
240 Ok(meta) => {
240 Ok(meta) => {
241 let file_type = meta.file_type();
241 let file_type = meta.file_type();
242 return if file_type.is_file() || file_type.is_symlink() {
242 return if file_type.is_file() || file_type.is_symlink() {
243 if let Some(entry) = in_dmap {
243 if let Some(entry) = in_dmap {
244 return Some(Ok((
244 return Some(Ok((
245 normalized,
245 normalized,
246 dispatch_found(
246 dispatch_found(
247 &normalized,
247 &normalized,
248 *entry,
248 *entry,
249 HgMetadata::from_metadata(meta),
249 HgMetadata::from_metadata(meta),
250 &dmap.copy_map,
250 &dmap.copy_map,
251 options,
251 options,
252 ),
252 ),
253 )));
253 )));
254 }
254 }
255 Some(Ok((normalized, Dispatch::Unknown)))
255 Some(Ok((normalized, Dispatch::Unknown)))
256 } else {
256 } else {
257 if file_type.is_dir() {
257 if file_type.is_dir() {
258 Some(Ok((
258 Some(Ok((
259 normalized,
259 normalized,
260 Dispatch::Directory {
260 Dispatch::Directory {
261 was_file: in_dmap.is_some(),
261 was_file: in_dmap.is_some(),
262 },
262 },
263 )))
263 )))
264 } else {
264 } else {
265 Some(Ok((
265 Some(Ok((
266 normalized,
266 normalized,
267 Dispatch::Bad(BadMatch::BadType(
267 Dispatch::Bad(BadMatch::BadType(
268 // TODO do more than unknown
268 // TODO do more than unknown
269 // Support for all `BadType` variant
269 // Support for all `BadType` variant
270 // varies greatly between platforms.
270 // varies greatly between platforms.
271 // So far, no tests check the type and
271 // So far, no tests check the type and
272 // this should be good enough for most
272 // this should be good enough for most
273 // users.
273 // users.
274 BadType::Unknown,
274 BadType::Unknown,
275 )),
275 )),
276 )))
276 )))
277 }
277 }
278 };
278 };
279 }
279 }
280 Err(_) => {
280 Err(_) => {
281 if let Some(entry) = in_dmap {
281 if let Some(entry) = in_dmap {
282 return Some(Ok((
282 return Some(Ok((
283 normalized,
283 normalized,
284 dispatch_missing(entry.state),
284 dispatch_missing(entry.state),
285 )));
285 )));
286 }
286 }
287 }
287 }
288 };
288 };
289 None
289 None
290 })
290 })
291 .flatten()
291 .flatten()
292 }
292 }
293
293
294 #[derive(Debug, Copy, Clone)]
294 #[derive(Debug, Copy, Clone)]
295 pub struct StatusOptions {
295 pub struct StatusOptions {
296 /// Remember the most recent modification timeslot for status, to make
296 /// Remember the most recent modification timeslot for status, to make
297 /// sure we won't miss future size-preserving file content modifications
297 /// sure we won't miss future size-preserving file content modifications
298 /// that happen within the same timeslot.
298 /// that happen within the same timeslot.
299 pub last_normal_time: i64,
299 pub last_normal_time: i64,
300 /// Whether we are on a filesystem with UNIX-like exec flags
300 /// Whether we are on a filesystem with UNIX-like exec flags
301 pub check_exec: bool,
301 pub check_exec: bool,
302 pub list_clean: bool,
302 pub list_clean: bool,
303 pub list_unknown: bool,
303 pub list_unknown: bool,
304 pub list_ignored: bool,
304 pub list_ignored: bool,
305 }
305 }
306
306
307 /// Dispatch a single entry (file, folder, symlink...) found during `traverse`.
307 /// Dispatch a single entry (file, folder, symlink...) found during `traverse`.
308 /// If the entry is a folder that needs to be traversed, it will be handled
308 /// If the entry is a folder that needs to be traversed, it will be handled
309 /// in a separate thread.
309 /// in a separate thread.
310
311 fn handle_traversed_entry<'a>(
310 fn handle_traversed_entry<'a>(
312 scope: &rayon::Scope<'a>,
311 scope: &rayon::Scope<'a>,
313 files_sender: &'a crossbeam::Sender<IoResult<(HgPathBuf, Dispatch)>>,
312 files_sender: &'a crossbeam::Sender<IoResult<(HgPathBuf, Dispatch)>>,
314 matcher: &'a (impl Matcher + Sync),
313 matcher: &'a (impl Matcher + Sync),
315 root_dir: impl AsRef<Path> + Sync + Send + Copy + 'a,
314 root_dir: impl AsRef<Path> + Sync + Send + Copy + 'a,
316 dmap: &'a DirstateMap,
315 dmap: &'a DirstateMap,
317 old_results: &'a FastHashMap<Cow<HgPath>, Dispatch>,
316 old_results: &'a FastHashMap<Cow<HgPath>, Dispatch>,
318 ignore_fn: &'a IgnoreFnType,
317 ignore_fn: &'a IgnoreFnType,
319 dir_ignore_fn: &'a IgnoreFnType,
318 dir_ignore_fn: &'a IgnoreFnType,
320 options: StatusOptions,
319 options: StatusOptions,
321 filename: HgPathBuf,
320 filename: HgPathBuf,
322 dir_entry: DirEntry,
321 dir_entry: DirEntry,
323 ) -> IoResult<()> {
322 ) -> IoResult<()> {
324 let file_type = dir_entry.file_type()?;
323 let file_type = dir_entry.file_type()?;
325 let entry_option = dmap.get(&filename);
324 let entry_option = dmap.get(&filename);
326
325
326 if filename.as_bytes() == b".hg" {
327 // Could be a directory or a symlink
328 return Ok(());
329 }
330
327 if file_type.is_dir() {
331 if file_type.is_dir() {
328 handle_traversed_dir(
332 handle_traversed_dir(
329 scope,
333 scope,
330 files_sender,
334 files_sender,
331 matcher,
335 matcher,
332 root_dir,
336 root_dir,
333 dmap,
337 dmap,
334 old_results,
338 old_results,
335 ignore_fn,
339 ignore_fn,
336 dir_ignore_fn,
340 dir_ignore_fn,
337 options,
341 options,
338 entry_option,
342 entry_option,
339 filename,
343 filename,
340 );
344 );
341 } else if file_type.is_file() || file_type.is_symlink() {
345 } else if file_type.is_file() || file_type.is_symlink() {
342 if let Some(entry) = entry_option {
346 if let Some(entry) = entry_option {
343 if matcher.matches_everything() || matcher.matches(&filename) {
347 if matcher.matches_everything() || matcher.matches(&filename) {
344 let metadata = dir_entry.metadata()?;
348 let metadata = dir_entry.metadata()?;
345 files_sender
349 files_sender
346 .send(Ok((
350 .send(Ok((
347 filename.to_owned(),
351 filename.to_owned(),
348 dispatch_found(
352 dispatch_found(
349 &filename,
353 &filename,
350 *entry,
354 *entry,
351 HgMetadata::from_metadata(metadata),
355 HgMetadata::from_metadata(metadata),
352 &dmap.copy_map,
356 &dmap.copy_map,
353 options,
357 options,
354 ),
358 ),
355 )))
359 )))
356 .unwrap();
360 .unwrap();
357 }
361 }
358 } else if (matcher.matches_everything() || matcher.matches(&filename))
362 } else if (matcher.matches_everything() || matcher.matches(&filename))
359 && !ignore_fn(&filename)
363 && !ignore_fn(&filename)
360 {
364 {
361 if (options.list_ignored || matcher.exact_match(&filename))
365 if (options.list_ignored || matcher.exact_match(&filename))
362 && dir_ignore_fn(&filename)
366 && dir_ignore_fn(&filename)
363 {
367 {
364 if options.list_ignored {
368 if options.list_ignored {
365 files_sender
369 files_sender
366 .send(Ok((filename.to_owned(), Dispatch::Ignored)))
370 .send(Ok((filename.to_owned(), Dispatch::Ignored)))
367 .unwrap();
371 .unwrap();
368 }
372 }
369 } else {
373 } else {
370 files_sender
374 files_sender
371 .send(Ok((filename.to_owned(), Dispatch::Unknown)))
375 .send(Ok((filename.to_owned(), Dispatch::Unknown)))
372 .unwrap();
376 .unwrap();
373 }
377 }
374 } else if ignore_fn(&filename) && options.list_ignored {
378 } else if ignore_fn(&filename) && options.list_ignored {
375 files_sender
379 files_sender
376 .send(Ok((filename.to_owned(), Dispatch::Ignored)))
380 .send(Ok((filename.to_owned(), Dispatch::Ignored)))
377 .unwrap();
381 .unwrap();
378 }
382 }
379 } else if let Some(entry) = entry_option {
383 } else if let Some(entry) = entry_option {
380 // Used to be a file or a folder, now something else.
384 // Used to be a file or a folder, now something else.
381 if matcher.matches_everything() || matcher.matches(&filename) {
385 if matcher.matches_everything() || matcher.matches(&filename) {
382 files_sender
386 files_sender
383 .send(Ok((filename.to_owned(), dispatch_missing(entry.state))))
387 .send(Ok((filename.to_owned(), dispatch_missing(entry.state))))
384 .unwrap();
388 .unwrap();
385 }
389 }
386 }
390 }
387
391
388 Ok(())
392 Ok(())
389 }
393 }
390
394
391 /// A directory was found in the filesystem and needs to be traversed
395 /// A directory was found in the filesystem and needs to be traversed
392 fn handle_traversed_dir<'a>(
396 fn handle_traversed_dir<'a>(
393 scope: &rayon::Scope<'a>,
397 scope: &rayon::Scope<'a>,
394 files_sender: &'a crossbeam::Sender<IoResult<(HgPathBuf, Dispatch)>>,
398 files_sender: &'a crossbeam::Sender<IoResult<(HgPathBuf, Dispatch)>>,
395 matcher: &'a (impl Matcher + Sync),
399 matcher: &'a (impl Matcher + Sync),
396 root_dir: impl AsRef<Path> + Sync + Send + Copy + 'a,
400 root_dir: impl AsRef<Path> + Sync + Send + Copy + 'a,
397 dmap: &'a DirstateMap,
401 dmap: &'a DirstateMap,
398 old_results: &'a FastHashMap<Cow<HgPath>, Dispatch>,
402 old_results: &'a FastHashMap<Cow<HgPath>, Dispatch>,
399 ignore_fn: &'a IgnoreFnType,
403 ignore_fn: &'a IgnoreFnType,
400 dir_ignore_fn: &'a IgnoreFnType,
404 dir_ignore_fn: &'a IgnoreFnType,
401 options: StatusOptions,
405 options: StatusOptions,
402 entry_option: Option<&'a DirstateEntry>,
406 entry_option: Option<&'a DirstateEntry>,
403 directory: HgPathBuf,
407 directory: HgPathBuf,
404 ) {
408 ) {
405 scope.spawn(move |_| {
409 scope.spawn(move |_| {
406 // Nested `if` until `rust-lang/rust#53668` is stable
410 // Nested `if` until `rust-lang/rust#53668` is stable
407 if let Some(entry) = entry_option {
411 if let Some(entry) = entry_option {
408 // Used to be a file, is now a folder
412 // Used to be a file, is now a folder
409 if matcher.matches_everything() || matcher.matches(&directory) {
413 if matcher.matches_everything() || matcher.matches(&directory) {
410 files_sender
414 files_sender
411 .send(Ok((
415 .send(Ok((
412 directory.to_owned(),
416 directory.to_owned(),
413 dispatch_missing(entry.state),
417 dispatch_missing(entry.state),
414 )))
418 )))
415 .unwrap();
419 .unwrap();
416 }
420 }
417 }
421 }
418 // Do we need to traverse it?
422 // Do we need to traverse it?
419 if !ignore_fn(&directory) || options.list_ignored {
423 if !ignore_fn(&directory) || options.list_ignored {
420 traverse_dir(
424 traverse_dir(
421 files_sender,
425 files_sender,
422 matcher,
426 matcher,
423 root_dir,
427 root_dir,
424 dmap,
428 dmap,
425 directory,
429 directory,
426 &old_results,
430 &old_results,
427 ignore_fn,
431 ignore_fn,
428 dir_ignore_fn,
432 dir_ignore_fn,
429 options,
433 options,
430 )
434 )
431 .unwrap_or_else(|e| files_sender.send(Err(e)).unwrap())
435 .unwrap_or_else(|e| files_sender.send(Err(e)).unwrap())
432 }
436 }
433 });
437 });
434 }
438 }
435
439
436 /// Decides whether the directory needs to be listed, and if so handles the
440 /// Decides whether the directory needs to be listed, and if so handles the
437 /// entries in a separate thread.
441 /// entries in a separate thread.
438 fn traverse_dir<'a>(
442 fn traverse_dir<'a>(
439 files_sender: &crossbeam::Sender<IoResult<(HgPathBuf, Dispatch)>>,
443 files_sender: &crossbeam::Sender<IoResult<(HgPathBuf, Dispatch)>>,
440 matcher: &'a (impl Matcher + Sync),
444 matcher: &'a (impl Matcher + Sync),
441 root_dir: impl AsRef<Path> + Sync + Send + Copy,
445 root_dir: impl AsRef<Path> + Sync + Send + Copy,
442 dmap: &'a DirstateMap,
446 dmap: &'a DirstateMap,
443 directory: impl AsRef<HgPath>,
447 directory: impl AsRef<HgPath>,
444 old_results: &FastHashMap<Cow<'a, HgPath>, Dispatch>,
448 old_results: &FastHashMap<Cow<'a, HgPath>, Dispatch>,
445 ignore_fn: &IgnoreFnType,
449 ignore_fn: &IgnoreFnType,
446 dir_ignore_fn: &IgnoreFnType,
450 dir_ignore_fn: &IgnoreFnType,
447 options: StatusOptions,
451 options: StatusOptions,
448 ) -> IoResult<()> {
452 ) -> IoResult<()> {
449 let directory = directory.as_ref();
453 let directory = directory.as_ref();
450 if directory.as_bytes() == b".hg" {
454
451 return Ok(());
452 }
453 let visit_entries = match matcher.visit_children_set(directory) {
455 let visit_entries = match matcher.visit_children_set(directory) {
454 VisitChildrenSet::Empty => return Ok(()),
456 VisitChildrenSet::Empty => return Ok(()),
455 VisitChildrenSet::This | VisitChildrenSet::Recursive => None,
457 VisitChildrenSet::This | VisitChildrenSet::Recursive => None,
456 VisitChildrenSet::Set(set) => Some(set),
458 VisitChildrenSet::Set(set) => Some(set),
457 };
459 };
458 let buf = hg_path_to_path_buf(directory)?;
460 let buf = hg_path_to_path_buf(directory)?;
459 let dir_path = root_dir.as_ref().join(buf);
461 let dir_path = root_dir.as_ref().join(buf);
460
462
461 let skip_dot_hg = !directory.as_bytes().is_empty();
463 let skip_dot_hg = !directory.as_bytes().is_empty();
462 let entries = match list_directory(dir_path, skip_dot_hg) {
464 let entries = match list_directory(dir_path, skip_dot_hg) {
463 Err(e) => match e.kind() {
465 Err(e) => match e.kind() {
464 ErrorKind::NotFound | ErrorKind::PermissionDenied => {
466 ErrorKind::NotFound | ErrorKind::PermissionDenied => {
465 files_sender
467 files_sender
466 .send(Ok((
468 .send(Ok((
467 directory.to_owned(),
469 directory.to_owned(),
468 Dispatch::Bad(BadMatch::OsError(
470 Dispatch::Bad(BadMatch::OsError(
469 // Unwrapping here is OK because the error always
471 // Unwrapping here is OK because the error always
470 // is a real os error
472 // is a real os error
471 e.raw_os_error().unwrap(),
473 e.raw_os_error().unwrap(),
472 )),
474 )),
473 )))
475 )))
474 .unwrap();
476 .unwrap();
475 return Ok(());
477 return Ok(());
476 }
478 }
477 _ => return Err(e),
479 _ => return Err(e),
478 },
480 },
479 Ok(entries) => entries,
481 Ok(entries) => entries,
480 };
482 };
481
483
482 rayon::scope(|scope| -> IoResult<()> {
484 rayon::scope(|scope| -> IoResult<()> {
483 for (filename, dir_entry) in entries {
485 for (filename, dir_entry) in entries {
484 if let Some(ref set) = visit_entries {
486 if let Some(ref set) = visit_entries {
485 if !set.contains(filename.deref()) {
487 if !set.contains(filename.deref()) {
486 continue;
488 continue;
487 }
489 }
488 }
490 }
489 // TODO normalize
491 // TODO normalize
490 let filename = if directory.is_empty() {
492 let filename = if directory.is_empty() {
491 filename.to_owned()
493 filename.to_owned()
492 } else {
494 } else {
493 directory.join(&filename)
495 directory.join(&filename)
494 };
496 };
495
497
496 if !old_results.contains_key(filename.deref()) {
498 if !old_results.contains_key(filename.deref()) {
497 handle_traversed_entry(
499 handle_traversed_entry(
498 scope,
500 scope,
499 files_sender,
501 files_sender,
500 matcher,
502 matcher,
501 root_dir,
503 root_dir,
502 dmap,
504 dmap,
503 old_results,
505 old_results,
504 ignore_fn,
506 ignore_fn,
505 dir_ignore_fn,
507 dir_ignore_fn,
506 options,
508 options,
507 filename,
509 filename,
508 dir_entry,
510 dir_entry,
509 )?;
511 )?;
510 }
512 }
511 }
513 }
512 Ok(())
514 Ok(())
513 })
515 })
514 }
516 }
515
517
516 /// Walk the working directory recursively to look for changes compared to the
518 /// Walk the working directory recursively to look for changes compared to the
517 /// current `DirstateMap`.
519 /// current `DirstateMap`.
518 ///
520 ///
519 /// This takes a mutable reference to the results to account for the `extend`
521 /// This takes a mutable reference to the results to account for the `extend`
520 /// in timings
522 /// in timings
521 #[timed]
523 #[timed]
522 fn traverse<'a>(
524 fn traverse<'a>(
523 matcher: &'a (impl Matcher + Sync),
525 matcher: &'a (impl Matcher + Sync),
524 root_dir: impl AsRef<Path> + Sync + Send + Copy,
526 root_dir: impl AsRef<Path> + Sync + Send + Copy,
525 dmap: &'a DirstateMap,
527 dmap: &'a DirstateMap,
526 path: impl AsRef<HgPath>,
528 path: impl AsRef<HgPath>,
527 old_results: &FastHashMap<Cow<'a, HgPath>, Dispatch>,
529 old_results: &FastHashMap<Cow<'a, HgPath>, Dispatch>,
528 ignore_fn: &IgnoreFnType,
530 ignore_fn: &IgnoreFnType,
529 dir_ignore_fn: &IgnoreFnType,
531 dir_ignore_fn: &IgnoreFnType,
530 options: StatusOptions,
532 options: StatusOptions,
531 results: &mut Vec<(Cow<'a, HgPath>, Dispatch)>,
533 results: &mut Vec<(Cow<'a, HgPath>, Dispatch)>,
532 ) -> IoResult<()> {
534 ) -> IoResult<()> {
533 let root_dir = root_dir.as_ref();
535 let root_dir = root_dir.as_ref();
534
536
535 // The traversal is done in parallel, so use a channel to gather entries.
537 // The traversal is done in parallel, so use a channel to gather entries.
536 // `crossbeam::Sender` is `Send`, while `mpsc::Sender` is not.
538 // `crossbeam::Sender` is `Send`, while `mpsc::Sender` is not.
537 let (files_transmitter, files_receiver) = crossbeam::channel::unbounded();
539 let (files_transmitter, files_receiver) = crossbeam::channel::unbounded();
538
540
539 traverse_dir(
541 traverse_dir(
540 &files_transmitter,
542 &files_transmitter,
541 matcher,
543 matcher,
542 root_dir,
544 root_dir,
543 &dmap,
545 &dmap,
544 path,
546 path,
545 &old_results,
547 &old_results,
546 &ignore_fn,
548 &ignore_fn,
547 &dir_ignore_fn,
549 &dir_ignore_fn,
548 options,
550 options,
549 )?;
551 )?;
550
552
551 // Disconnect the channel so the receiver stops waiting
553 // Disconnect the channel so the receiver stops waiting
552 drop(files_transmitter);
554 drop(files_transmitter);
553
555
554 // TODO don't collect. Find a way of replicating the behavior of
556 // TODO don't collect. Find a way of replicating the behavior of
555 // `itertools::process_results`, but for `rayon::ParallelIterator`
557 // `itertools::process_results`, but for `rayon::ParallelIterator`
556 let new_results: IoResult<Vec<(Cow<'a, HgPath>, Dispatch)>> =
558 let new_results: IoResult<Vec<(Cow<'a, HgPath>, Dispatch)>> =
557 files_receiver
559 files_receiver
558 .into_iter()
560 .into_iter()
559 .map(|item| {
561 .map(|item| {
560 let (f, d) = item?;
562 let (f, d) = item?;
561 Ok((Cow::Owned(f), d))
563 Ok((Cow::Owned(f), d))
562 })
564 })
563 .collect();
565 .collect();
564
566
565 results.par_extend(new_results?);
567 results.par_extend(new_results?);
566
568
567 Ok(())
569 Ok(())
568 }
570 }
569
571
570 /// Stat all entries in the `DirstateMap` and mark them for dispatch.
572 /// Stat all entries in the `DirstateMap` and mark them for dispatch.
571 fn stat_dmap_entries(
573 fn stat_dmap_entries(
572 dmap: &DirstateMap,
574 dmap: &DirstateMap,
573 root_dir: impl AsRef<Path> + Sync + Send,
575 root_dir: impl AsRef<Path> + Sync + Send,
574 options: StatusOptions,
576 options: StatusOptions,
575 ) -> impl ParallelIterator<Item = IoResult<(&HgPath, Dispatch)>> {
577 ) -> impl ParallelIterator<Item = IoResult<(&HgPath, Dispatch)>> {
576 dmap.par_iter().map(move |(filename, entry)| {
578 dmap.par_iter().map(move |(filename, entry)| {
577 let filename: &HgPath = filename;
579 let filename: &HgPath = filename;
578 let filename_as_path = hg_path_to_path_buf(filename)?;
580 let filename_as_path = hg_path_to_path_buf(filename)?;
579 let meta = root_dir.as_ref().join(filename_as_path).symlink_metadata();
581 let meta = root_dir.as_ref().join(filename_as_path).symlink_metadata();
580
582
581 match meta {
583 match meta {
582 Ok(ref m)
584 Ok(ref m)
583 if !(m.file_type().is_file()
585 if !(m.file_type().is_file()
584 || m.file_type().is_symlink()) =>
586 || m.file_type().is_symlink()) =>
585 {
587 {
586 Ok((filename, dispatch_missing(entry.state)))
588 Ok((filename, dispatch_missing(entry.state)))
587 }
589 }
588 Ok(m) => Ok((
590 Ok(m) => Ok((
589 filename,
591 filename,
590 dispatch_found(
592 dispatch_found(
591 filename,
593 filename,
592 *entry,
594 *entry,
593 HgMetadata::from_metadata(m),
595 HgMetadata::from_metadata(m),
594 &dmap.copy_map,
596 &dmap.copy_map,
595 options,
597 options,
596 ),
598 ),
597 )),
599 )),
598 Err(ref e)
600 Err(ref e)
599 if e.kind() == ErrorKind::NotFound
601 if e.kind() == ErrorKind::NotFound
600 || e.raw_os_error() == Some(20) =>
602 || e.raw_os_error() == Some(20) =>
601 {
603 {
602 // Rust does not yet have an `ErrorKind` for
604 // Rust does not yet have an `ErrorKind` for
603 // `NotADirectory` (errno 20)
605 // `NotADirectory` (errno 20)
604 // It happens if the dirstate contains `foo/bar` and
606 // It happens if the dirstate contains `foo/bar` and
605 // foo is not a directory
607 // foo is not a directory
606 Ok((filename, dispatch_missing(entry.state)))
608 Ok((filename, dispatch_missing(entry.state)))
607 }
609 }
608 Err(e) => Err(e),
610 Err(e) => Err(e),
609 }
611 }
610 })
612 })
611 }
613 }
612
614
613 /// This takes a mutable reference to the results to account for the `extend`
615 /// This takes a mutable reference to the results to account for the `extend`
614 /// in timings
616 /// in timings
615 #[timed]
617 #[timed]
616 fn extend_from_dmap<'a>(
618 fn extend_from_dmap<'a>(
617 dmap: &'a DirstateMap,
619 dmap: &'a DirstateMap,
618 root_dir: impl AsRef<Path> + Sync + Send,
620 root_dir: impl AsRef<Path> + Sync + Send,
619 options: StatusOptions,
621 options: StatusOptions,
620 results: &mut Vec<(Cow<'a, HgPath>, Dispatch)>,
622 results: &mut Vec<(Cow<'a, HgPath>, Dispatch)>,
621 ) {
623 ) {
622 results.par_extend(
624 results.par_extend(
623 stat_dmap_entries(dmap, root_dir, options)
625 stat_dmap_entries(dmap, root_dir, options)
624 .flatten()
626 .flatten()
625 .map(|(filename, dispatch)| (Cow::Borrowed(filename), dispatch)),
627 .map(|(filename, dispatch)| (Cow::Borrowed(filename), dispatch)),
626 );
628 );
627 }
629 }
628
630
629 #[derive(Debug)]
631 #[derive(Debug)]
630 pub struct DirstateStatus<'a> {
632 pub struct DirstateStatus<'a> {
631 pub modified: Vec<Cow<'a, HgPath>>,
633 pub modified: Vec<Cow<'a, HgPath>>,
632 pub added: Vec<Cow<'a, HgPath>>,
634 pub added: Vec<Cow<'a, HgPath>>,
633 pub removed: Vec<Cow<'a, HgPath>>,
635 pub removed: Vec<Cow<'a, HgPath>>,
634 pub deleted: Vec<Cow<'a, HgPath>>,
636 pub deleted: Vec<Cow<'a, HgPath>>,
635 pub clean: Vec<Cow<'a, HgPath>>,
637 pub clean: Vec<Cow<'a, HgPath>>,
636 pub ignored: Vec<Cow<'a, HgPath>>,
638 pub ignored: Vec<Cow<'a, HgPath>>,
637 pub unknown: Vec<Cow<'a, HgPath>>,
639 pub unknown: Vec<Cow<'a, HgPath>>,
638 pub bad: Vec<(Cow<'a, HgPath>, BadMatch)>,
640 pub bad: Vec<(Cow<'a, HgPath>, BadMatch)>,
639 }
641 }
640
642
641 #[timed]
643 #[timed]
642 fn build_response<'a>(
644 fn build_response<'a>(
643 results: impl IntoIterator<Item = (Cow<'a, HgPath>, Dispatch)>,
645 results: impl IntoIterator<Item = (Cow<'a, HgPath>, Dispatch)>,
644 ) -> (Vec<Cow<'a, HgPath>>, DirstateStatus<'a>) {
646 ) -> (Vec<Cow<'a, HgPath>>, DirstateStatus<'a>) {
645 let mut lookup = vec![];
647 let mut lookup = vec![];
646 let mut modified = vec![];
648 let mut modified = vec![];
647 let mut added = vec![];
649 let mut added = vec![];
648 let mut removed = vec![];
650 let mut removed = vec![];
649 let mut deleted = vec![];
651 let mut deleted = vec![];
650 let mut clean = vec![];
652 let mut clean = vec![];
651 let mut ignored = vec![];
653 let mut ignored = vec![];
652 let mut unknown = vec![];
654 let mut unknown = vec![];
653 let mut bad = vec![];
655 let mut bad = vec![];
654
656
655 for (filename, dispatch) in results.into_iter() {
657 for (filename, dispatch) in results.into_iter() {
656 match dispatch {
658 match dispatch {
657 Dispatch::Unknown => unknown.push(filename),
659 Dispatch::Unknown => unknown.push(filename),
658 Dispatch::Unsure => lookup.push(filename),
660 Dispatch::Unsure => lookup.push(filename),
659 Dispatch::Modified => modified.push(filename),
661 Dispatch::Modified => modified.push(filename),
660 Dispatch::Added => added.push(filename),
662 Dispatch::Added => added.push(filename),
661 Dispatch::Removed => removed.push(filename),
663 Dispatch::Removed => removed.push(filename),
662 Dispatch::Deleted => deleted.push(filename),
664 Dispatch::Deleted => deleted.push(filename),
663 Dispatch::Clean => clean.push(filename),
665 Dispatch::Clean => clean.push(filename),
664 Dispatch::Ignored => ignored.push(filename),
666 Dispatch::Ignored => ignored.push(filename),
665 Dispatch::None => {}
667 Dispatch::None => {}
666 Dispatch::Bad(reason) => bad.push((filename, reason)),
668 Dispatch::Bad(reason) => bad.push((filename, reason)),
667 Dispatch::Directory { .. } => {}
669 Dispatch::Directory { .. } => {}
668 }
670 }
669 }
671 }
670
672
671 (
673 (
672 lookup,
674 lookup,
673 DirstateStatus {
675 DirstateStatus {
674 modified,
676 modified,
675 added,
677 added,
676 removed,
678 removed,
677 deleted,
679 deleted,
678 clean,
680 clean,
679 ignored,
681 ignored,
680 unknown,
682 unknown,
681 bad,
683 bad,
682 },
684 },
683 )
685 )
684 }
686 }
685
687
686 #[derive(Debug)]
688 #[derive(Debug)]
687 pub enum StatusError {
689 pub enum StatusError {
688 IO(std::io::Error),
690 IO(std::io::Error),
689 Path(HgPathError),
691 Path(HgPathError),
690 Pattern(PatternError),
692 Pattern(PatternError),
691 }
693 }
692
694
693 pub type StatusResult<T> = Result<T, StatusError>;
695 pub type StatusResult<T> = Result<T, StatusError>;
694
696
695 impl From<PatternError> for StatusError {
697 impl From<PatternError> for StatusError {
696 fn from(e: PatternError) -> Self {
698 fn from(e: PatternError) -> Self {
697 StatusError::Pattern(e)
699 StatusError::Pattern(e)
698 }
700 }
699 }
701 }
700 impl From<HgPathError> for StatusError {
702 impl From<HgPathError> for StatusError {
701 fn from(e: HgPathError) -> Self {
703 fn from(e: HgPathError) -> Self {
702 StatusError::Path(e)
704 StatusError::Path(e)
703 }
705 }
704 }
706 }
705 impl From<std::io::Error> for StatusError {
707 impl From<std::io::Error> for StatusError {
706 fn from(e: std::io::Error) -> Self {
708 fn from(e: std::io::Error) -> Self {
707 StatusError::IO(e)
709 StatusError::IO(e)
708 }
710 }
709 }
711 }
710
712
711 impl ToString for StatusError {
713 impl ToString for StatusError {
712 fn to_string(&self) -> String {
714 fn to_string(&self) -> String {
713 match self {
715 match self {
714 StatusError::IO(e) => e.to_string(),
716 StatusError::IO(e) => e.to_string(),
715 StatusError::Path(e) => e.to_string(),
717 StatusError::Path(e) => e.to_string(),
716 StatusError::Pattern(e) => e.to_string(),
718 StatusError::Pattern(e) => e.to_string(),
717 }
719 }
718 }
720 }
719 }
721 }
720
722
721 /// This takes a mutable reference to the results to account for the `extend`
723 /// This takes a mutable reference to the results to account for the `extend`
722 /// in timings
724 /// in timings
723 #[timed]
725 #[timed]
724 fn handle_unknowns<'a>(
726 fn handle_unknowns<'a>(
725 dmap: &'a DirstateMap,
727 dmap: &'a DirstateMap,
726 matcher: &(impl Matcher + Sync),
728 matcher: &(impl Matcher + Sync),
727 root_dir: impl AsRef<Path> + Sync + Send + Copy,
729 root_dir: impl AsRef<Path> + Sync + Send + Copy,
728 options: StatusOptions,
730 options: StatusOptions,
729 results: &mut Vec<(Cow<'a, HgPath>, Dispatch)>,
731 results: &mut Vec<(Cow<'a, HgPath>, Dispatch)>,
730 ) -> IoResult<()> {
732 ) -> IoResult<()> {
731 let to_visit: Vec<(&HgPath, &DirstateEntry)> = if results.is_empty()
733 let to_visit: Vec<(&HgPath, &DirstateEntry)> = if results.is_empty()
732 && matcher.matches_everything()
734 && matcher.matches_everything()
733 {
735 {
734 dmap.iter().map(|(f, e)| (f.deref(), e)).collect()
736 dmap.iter().map(|(f, e)| (f.deref(), e)).collect()
735 } else {
737 } else {
736 // Only convert to a hashmap if needed.
738 // Only convert to a hashmap if needed.
737 let old_results: FastHashMap<_, _> = results.iter().cloned().collect();
739 let old_results: FastHashMap<_, _> = results.iter().cloned().collect();
738 dmap.iter()
740 dmap.iter()
739 .filter_map(move |(f, e)| {
741 .filter_map(move |(f, e)| {
740 if !old_results.contains_key(f.deref()) && matcher.matches(f) {
742 if !old_results.contains_key(f.deref()) && matcher.matches(f) {
741 Some((f.deref(), e))
743 Some((f.deref(), e))
742 } else {
744 } else {
743 None
745 None
744 }
746 }
745 })
747 })
746 .collect()
748 .collect()
747 };
749 };
748
750
749 // We walked all dirs under the roots that weren't ignored, and
751 // We walked all dirs under the roots that weren't ignored, and
750 // everything that matched was stat'ed and is already in results.
752 // everything that matched was stat'ed and is already in results.
751 // The rest must thus be ignored or under a symlink.
753 // The rest must thus be ignored or under a symlink.
752 let path_auditor = PathAuditor::new(root_dir);
754 let path_auditor = PathAuditor::new(root_dir);
753
755
754 // TODO don't collect. Find a way of replicating the behavior of
756 // TODO don't collect. Find a way of replicating the behavior of
755 // `itertools::process_results`, but for `rayon::ParallelIterator`
757 // `itertools::process_results`, but for `rayon::ParallelIterator`
756 let new_results: IoResult<Vec<_>> = to_visit
758 let new_results: IoResult<Vec<_>> = to_visit
757 .into_par_iter()
759 .into_par_iter()
758 .filter_map(|(filename, entry)| -> Option<IoResult<_>> {
760 .filter_map(|(filename, entry)| -> Option<IoResult<_>> {
759 // Report ignored items in the dmap as long as they are not
761 // Report ignored items in the dmap as long as they are not
760 // under a symlink directory.
762 // under a symlink directory.
761 if path_auditor.check(filename) {
763 if path_auditor.check(filename) {
762 // TODO normalize for case-insensitive filesystems
764 // TODO normalize for case-insensitive filesystems
763 let buf = match hg_path_to_path_buf(filename) {
765 let buf = match hg_path_to_path_buf(filename) {
764 Ok(x) => x,
766 Ok(x) => x,
765 Err(e) => return Some(Err(e.into())),
767 Err(e) => return Some(Err(e.into())),
766 };
768 };
767 Some(Ok((
769 Some(Ok((
768 Cow::Borrowed(filename),
770 Cow::Borrowed(filename),
769 match root_dir.as_ref().join(&buf).symlink_metadata() {
771 match root_dir.as_ref().join(&buf).symlink_metadata() {
770 // File was just ignored, no links, and exists
772 // File was just ignored, no links, and exists
771 Ok(meta) => {
773 Ok(meta) => {
772 let metadata = HgMetadata::from_metadata(meta);
774 let metadata = HgMetadata::from_metadata(meta);
773 dispatch_found(
775 dispatch_found(
774 filename,
776 filename,
775 *entry,
777 *entry,
776 metadata,
778 metadata,
777 &dmap.copy_map,
779 &dmap.copy_map,
778 options,
780 options,
779 )
781 )
780 }
782 }
781 // File doesn't exist
783 // File doesn't exist
782 Err(_) => dispatch_missing(entry.state),
784 Err(_) => dispatch_missing(entry.state),
783 },
785 },
784 )))
786 )))
785 } else {
787 } else {
786 // It's either missing or under a symlink directory which
788 // It's either missing or under a symlink directory which
787 // we, in this case, report as missing.
789 // we, in this case, report as missing.
788 Some(Ok((
790 Some(Ok((
789 Cow::Borrowed(filename),
791 Cow::Borrowed(filename),
790 dispatch_missing(entry.state),
792 dispatch_missing(entry.state),
791 )))
793 )))
792 }
794 }
793 })
795 })
794 .collect();
796 .collect();
795
797
796 results.par_extend(new_results?);
798 results.par_extend(new_results?);
797
799
798 Ok(())
800 Ok(())
799 }
801 }
800
802
801 /// Get the status of files in the working directory.
803 /// Get the status of files in the working directory.
802 ///
804 ///
803 /// This is the current entry-point for `hg-core` and is realistically unusable
805 /// This is the current entry-point for `hg-core` and is realistically unusable
804 /// outside of a Python context because its arguments need to provide a lot of
806 /// outside of a Python context because its arguments need to provide a lot of
805 /// information that will not be necessary in the future.
807 /// information that will not be necessary in the future.
806 #[timed]
808 #[timed]
807 pub fn status<'a: 'c, 'b: 'c, 'c>(
809 pub fn status<'a: 'c, 'b: 'c, 'c>(
808 dmap: &'a DirstateMap,
810 dmap: &'a DirstateMap,
809 matcher: &'b (impl Matcher + Sync),
811 matcher: &'b (impl Matcher + Sync),
810 root_dir: impl AsRef<Path> + Sync + Send + Copy + 'c,
812 root_dir: impl AsRef<Path> + Sync + Send + Copy + 'c,
811 ignore_files: Vec<PathBuf>,
813 ignore_files: Vec<PathBuf>,
812 options: StatusOptions,
814 options: StatusOptions,
813 ) -> StatusResult<(
815 ) -> StatusResult<(
814 (Vec<Cow<'c, HgPath>>, DirstateStatus<'c>),
816 (Vec<Cow<'c, HgPath>>, DirstateStatus<'c>),
815 Vec<PatternFileWarning>,
817 Vec<PatternFileWarning>,
816 )> {
818 )> {
817 // Needs to outlive `dir_ignore_fn` since it's captured.
819 // Needs to outlive `dir_ignore_fn` since it's captured.
818 let mut ignore_fn: IgnoreFnType;
820 let mut ignore_fn: IgnoreFnType;
819
821
820 // Only involve real ignore mechanism if we're listing unknowns or ignored.
822 // Only involve real ignore mechanism if we're listing unknowns or ignored.
821 let (dir_ignore_fn, warnings): (IgnoreFnType, _) = if options.list_ignored
823 let (dir_ignore_fn, warnings): (IgnoreFnType, _) = if options.list_ignored
822 || options.list_unknown
824 || options.list_unknown
823 {
825 {
824 let (ignore, warnings) = get_ignore_function(ignore_files, root_dir)?;
826 let (ignore, warnings) = get_ignore_function(ignore_files, root_dir)?;
825
827
826 ignore_fn = ignore;
828 ignore_fn = ignore;
827 let dir_ignore_fn = Box::new(|dir: &_| {
829 let dir_ignore_fn = Box::new(|dir: &_| {
828 // Is the path or one of its ancestors ignored?
830 // Is the path or one of its ancestors ignored?
829 if ignore_fn(dir) {
831 if ignore_fn(dir) {
830 true
832 true
831 } else {
833 } else {
832 for p in find_dirs(dir) {
834 for p in find_dirs(dir) {
833 if ignore_fn(p) {
835 if ignore_fn(p) {
834 return true;
836 return true;
835 }
837 }
836 }
838 }
837 false
839 false
838 }
840 }
839 });
841 });
840 (dir_ignore_fn, warnings)
842 (dir_ignore_fn, warnings)
841 } else {
843 } else {
842 ignore_fn = Box::new(|&_| true);
844 ignore_fn = Box::new(|&_| true);
843 (Box::new(|&_| true), vec![])
845 (Box::new(|&_| true), vec![])
844 };
846 };
845
847
846 let files = matcher.file_set();
848 let files = matcher.file_set();
847
849
848 // Step 1: check the files explicitly mentioned by the user
850 // Step 1: check the files explicitly mentioned by the user
849 let explicit = walk_explicit(files, &dmap, root_dir, options);
851 let explicit = walk_explicit(files, &dmap, root_dir, options);
850
852
851 // Collect results into a `Vec` because we do very few lookups in most
853 // Collect results into a `Vec` because we do very few lookups in most
852 // cases.
854 // cases.
853 let (work, mut results): (Vec<_>, Vec<_>) = explicit
855 let (work, mut results): (Vec<_>, Vec<_>) = explicit
854 .filter_map(Result::ok)
856 .filter_map(Result::ok)
855 .map(|(filename, dispatch)| (Cow::Borrowed(filename), dispatch))
857 .map(|(filename, dispatch)| (Cow::Borrowed(filename), dispatch))
856 .partition(|(_, dispatch)| match dispatch {
858 .partition(|(_, dispatch)| match dispatch {
857 Dispatch::Directory { .. } => true,
859 Dispatch::Directory { .. } => true,
858 _ => false,
860 _ => false,
859 });
861 });
860
862
861 if !work.is_empty() {
863 if !work.is_empty() {
862 // Hashmaps are quite a bit slower to build than vecs, so only build it
864 // Hashmaps are quite a bit slower to build than vecs, so only build it
863 // if needed.
865 // if needed.
864 let old_results = results.iter().cloned().collect();
866 let old_results = results.iter().cloned().collect();
865
867
866 // Step 2: recursively check the working directory for changes if
868 // Step 2: recursively check the working directory for changes if
867 // needed
869 // needed
868 for (dir, dispatch) in work {
870 for (dir, dispatch) in work {
869 match dispatch {
871 match dispatch {
870 Dispatch::Directory { was_file } => {
872 Dispatch::Directory { was_file } => {
871 if was_file {
873 if was_file {
872 results.push((dir.to_owned(), Dispatch::Removed));
874 results.push((dir.to_owned(), Dispatch::Removed));
873 }
875 }
874 if options.list_ignored
876 if options.list_ignored
875 || options.list_unknown && !dir_ignore_fn(&dir)
877 || options.list_unknown && !dir_ignore_fn(&dir)
876 {
878 {
877 traverse(
879 traverse(
878 matcher,
880 matcher,
879 root_dir,
881 root_dir,
880 &dmap,
882 &dmap,
881 &dir,
883 &dir,
882 &old_results,
884 &old_results,
883 &ignore_fn,
885 &ignore_fn,
884 &dir_ignore_fn,
886 &dir_ignore_fn,
885 options,
887 options,
886 &mut results,
888 &mut results,
887 )?;
889 )?;
888 }
890 }
889 }
891 }
890 _ => unreachable!("There can only be directories in `work`"),
892 _ => unreachable!("There can only be directories in `work`"),
891 }
893 }
892 }
894 }
893 }
895 }
894
896
895 if !matcher.is_exact() {
897 if !matcher.is_exact() {
896 // Step 3: Check the remaining files from the dmap.
898 // Step 3: Check the remaining files from the dmap.
897 // If a dmap file is not in results yet, it was either
899 // If a dmap file is not in results yet, it was either
898 // a) not matched b) ignored, c) missing, or d) under a
900 // a) not matched b) ignored, c) missing, or d) under a
899 // symlink directory.
901 // symlink directory.
900
902
901 if options.list_unknown {
903 if options.list_unknown {
902 handle_unknowns(dmap, matcher, root_dir, options, &mut results)?;
904 handle_unknowns(dmap, matcher, root_dir, options, &mut results)?;
903 } else {
905 } else {
904 // We may not have walked the full directory tree above, so stat
906 // We may not have walked the full directory tree above, so stat
905 // and check everything we missed.
907 // and check everything we missed.
906 extend_from_dmap(&dmap, root_dir, options, &mut results);
908 extend_from_dmap(&dmap, root_dir, options, &mut results);
907 }
909 }
908 }
910 }
909
911
910 Ok((build_response(results), warnings))
912 Ok((build_response(results), warnings))
911 }
913 }
@@ -1,1753 +1,1760 b''
1 #
1 #
2 # This is the mercurial setup script.
2 # This is the mercurial setup script.
3 #
3 #
4 # 'python setup.py install', or
4 # 'python setup.py install', or
5 # 'python setup.py --help' for more options
5 # 'python setup.py --help' for more options
6 import os
6 import os
7
7
8 # Mercurial will never work on Python 3 before 3.5 due to a lack
8 # Mercurial will never work on Python 3 before 3.5 due to a lack
9 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
9 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
10 # due to a bug in % formatting in bytestrings.
10 # due to a bug in % formatting in bytestrings.
11 # We cannot support Python 3.5.0, 3.5.1, 3.5.2 because of bug in
11 # We cannot support Python 3.5.0, 3.5.1, 3.5.2 because of bug in
12 # codecs.escape_encode() where it raises SystemError on empty bytestring
12 # codecs.escape_encode() where it raises SystemError on empty bytestring
13 # bug link: https://bugs.python.org/issue25270
13 # bug link: https://bugs.python.org/issue25270
14 supportedpy = ','.join(
14 supportedpy = ','.join(
15 [
15 [
16 '>=2.7',
16 '>=2.7',
17 '!=3.0.*',
17 '!=3.0.*',
18 '!=3.1.*',
18 '!=3.1.*',
19 '!=3.2.*',
19 '!=3.2.*',
20 '!=3.3.*',
20 '!=3.3.*',
21 '!=3.4.*',
21 '!=3.4.*',
22 '!=3.5.0',
22 '!=3.5.0',
23 '!=3.5.1',
23 '!=3.5.1',
24 '!=3.5.2',
24 '!=3.5.2',
25 '!=3.6.0',
25 '!=3.6.0',
26 '!=3.6.1',
26 '!=3.6.1',
27 ]
27 ]
28 )
28 )
29
29
30 import sys, platform
30 import sys, platform
31 import sysconfig
31 import sysconfig
32
32
33 if sys.version_info[0] >= 3:
33 if sys.version_info[0] >= 3:
34 printf = eval('print')
34 printf = eval('print')
35 libdir_escape = 'unicode_escape'
35 libdir_escape = 'unicode_escape'
36
36
37 def sysstr(s):
37 def sysstr(s):
38 return s.decode('latin-1')
38 return s.decode('latin-1')
39
39
40
40
41 else:
41 else:
42 libdir_escape = 'string_escape'
42 libdir_escape = 'string_escape'
43
43
44 def printf(*args, **kwargs):
44 def printf(*args, **kwargs):
45 f = kwargs.get('file', sys.stdout)
45 f = kwargs.get('file', sys.stdout)
46 end = kwargs.get('end', '\n')
46 end = kwargs.get('end', '\n')
47 f.write(b' '.join(args) + end)
47 f.write(b' '.join(args) + end)
48
48
49 def sysstr(s):
49 def sysstr(s):
50 return s
50 return s
51
51
52
52
53 # Attempt to guide users to a modern pip - this means that 2.6 users
53 # Attempt to guide users to a modern pip - this means that 2.6 users
54 # should have a chance of getting a 4.2 release, and when we ratchet
54 # should have a chance of getting a 4.2 release, and when we ratchet
55 # the version requirement forward again hopefully everyone will get
55 # the version requirement forward again hopefully everyone will get
56 # something that works for them.
56 # something that works for them.
57 if sys.version_info < (2, 7, 0, 'final'):
57 if sys.version_info < (2, 7, 0, 'final'):
58 pip_message = (
58 pip_message = (
59 'This may be due to an out of date pip. '
59 'This may be due to an out of date pip. '
60 'Make sure you have pip >= 9.0.1.'
60 'Make sure you have pip >= 9.0.1.'
61 )
61 )
62 try:
62 try:
63 import pip
63 import pip
64
64
65 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
65 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
66 if pip_version < (9, 0, 1):
66 if pip_version < (9, 0, 1):
67 pip_message = (
67 pip_message = (
68 'Your pip version is out of date, please install '
68 'Your pip version is out of date, please install '
69 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__)
69 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__)
70 )
70 )
71 else:
71 else:
72 # pip is new enough - it must be something else
72 # pip is new enough - it must be something else
73 pip_message = ''
73 pip_message = ''
74 except Exception:
74 except Exception:
75 pass
75 pass
76 error = """
76 error = """
77 Mercurial does not support Python older than 2.7.
77 Mercurial does not support Python older than 2.7.
78 Python {py} detected.
78 Python {py} detected.
79 {pip}
79 {pip}
80 """.format(
80 """.format(
81 py=sys.version_info, pip=pip_message
81 py=sys.version_info, pip=pip_message
82 )
82 )
83 printf(error, file=sys.stderr)
83 printf(error, file=sys.stderr)
84 sys.exit(1)
84 sys.exit(1)
85
85
86 if sys.version_info[0] >= 3:
86 if sys.version_info[0] >= 3:
87 DYLIB_SUFFIX = sysconfig.get_config_vars()['EXT_SUFFIX']
87 DYLIB_SUFFIX = sysconfig.get_config_vars()['EXT_SUFFIX']
88 else:
88 else:
89 # deprecated in Python 3
89 # deprecated in Python 3
90 DYLIB_SUFFIX = sysconfig.get_config_vars()['SO']
90 DYLIB_SUFFIX = sysconfig.get_config_vars()['SO']
91
91
92 # Solaris Python packaging brain damage
92 # Solaris Python packaging brain damage
93 try:
93 try:
94 import hashlib
94 import hashlib
95
95
96 sha = hashlib.sha1()
96 sha = hashlib.sha1()
97 except ImportError:
97 except ImportError:
98 try:
98 try:
99 import sha
99 import sha
100
100
101 sha.sha # silence unused import warning
101 sha.sha # silence unused import warning
102 except ImportError:
102 except ImportError:
103 raise SystemExit(
103 raise SystemExit(
104 "Couldn't import standard hashlib (incomplete Python install)."
104 "Couldn't import standard hashlib (incomplete Python install)."
105 )
105 )
106
106
107 try:
107 try:
108 import zlib
108 import zlib
109
109
110 zlib.compressobj # silence unused import warning
110 zlib.compressobj # silence unused import warning
111 except ImportError:
111 except ImportError:
112 raise SystemExit(
112 raise SystemExit(
113 "Couldn't import standard zlib (incomplete Python install)."
113 "Couldn't import standard zlib (incomplete Python install)."
114 )
114 )
115
115
116 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
116 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
117 isironpython = False
117 isironpython = False
118 try:
118 try:
119 isironpython = (
119 isironpython = (
120 platform.python_implementation().lower().find("ironpython") != -1
120 platform.python_implementation().lower().find("ironpython") != -1
121 )
121 )
122 except AttributeError:
122 except AttributeError:
123 pass
123 pass
124
124
125 if isironpython:
125 if isironpython:
126 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
126 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
127 else:
127 else:
128 try:
128 try:
129 import bz2
129 import bz2
130
130
131 bz2.BZ2Compressor # silence unused import warning
131 bz2.BZ2Compressor # silence unused import warning
132 except ImportError:
132 except ImportError:
133 raise SystemExit(
133 raise SystemExit(
134 "Couldn't import standard bz2 (incomplete Python install)."
134 "Couldn't import standard bz2 (incomplete Python install)."
135 )
135 )
136
136
137 ispypy = "PyPy" in sys.version
137 ispypy = "PyPy" in sys.version
138
138
139 import ctypes
139 import ctypes
140 import errno
140 import errno
141 import stat, subprocess, time
141 import stat, subprocess, time
142 import re
142 import re
143 import shutil
143 import shutil
144 import tempfile
144 import tempfile
145 from distutils import log
145 from distutils import log
146
146
147 # We have issues with setuptools on some platforms and builders. Until
147 # We have issues with setuptools on some platforms and builders. Until
148 # those are resolved, setuptools is opt-in except for platforms where
148 # those are resolved, setuptools is opt-in except for platforms where
149 # we don't have issues.
149 # we don't have issues.
150 issetuptools = os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ
150 issetuptools = os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ
151 if issetuptools:
151 if issetuptools:
152 from setuptools import setup
152 from setuptools import setup
153 else:
153 else:
154 from distutils.core import setup
154 from distutils.core import setup
155 from distutils.ccompiler import new_compiler
155 from distutils.ccompiler import new_compiler
156 from distutils.core import Command, Extension
156 from distutils.core import Command, Extension
157 from distutils.dist import Distribution
157 from distutils.dist import Distribution
158 from distutils.command.build import build
158 from distutils.command.build import build
159 from distutils.command.build_ext import build_ext
159 from distutils.command.build_ext import build_ext
160 from distutils.command.build_py import build_py
160 from distutils.command.build_py import build_py
161 from distutils.command.build_scripts import build_scripts
161 from distutils.command.build_scripts import build_scripts
162 from distutils.command.install import install
162 from distutils.command.install import install
163 from distutils.command.install_lib import install_lib
163 from distutils.command.install_lib import install_lib
164 from distutils.command.install_scripts import install_scripts
164 from distutils.command.install_scripts import install_scripts
165 from distutils.spawn import spawn, find_executable
165 from distutils.spawn import spawn, find_executable
166 from distutils import file_util
166 from distutils import file_util
167 from distutils.errors import (
167 from distutils.errors import (
168 CCompilerError,
168 CCompilerError,
169 DistutilsError,
169 DistutilsError,
170 DistutilsExecError,
170 DistutilsExecError,
171 )
171 )
172 from distutils.sysconfig import get_python_inc, get_config_var
172 from distutils.sysconfig import get_python_inc, get_config_var
173 from distutils.version import StrictVersion
173 from distutils.version import StrictVersion
174
174
175 # Explain to distutils.StrictVersion how our release candidates are versionned
175 # Explain to distutils.StrictVersion how our release candidates are versionned
176 StrictVersion.version_re = re.compile(r'^(\d+)\.(\d+)(\.(\d+))?-?(rc(\d+))?$')
176 StrictVersion.version_re = re.compile(r'^(\d+)\.(\d+)(\.(\d+))?-?(rc(\d+))?$')
177
177
178
178
179 def write_if_changed(path, content):
179 def write_if_changed(path, content):
180 """Write content to a file iff the content hasn't changed."""
180 """Write content to a file iff the content hasn't changed."""
181 if os.path.exists(path):
181 if os.path.exists(path):
182 with open(path, 'rb') as fh:
182 with open(path, 'rb') as fh:
183 current = fh.read()
183 current = fh.read()
184 else:
184 else:
185 current = b''
185 current = b''
186
186
187 if current != content:
187 if current != content:
188 with open(path, 'wb') as fh:
188 with open(path, 'wb') as fh:
189 fh.write(content)
189 fh.write(content)
190
190
191
191
192 scripts = ['hg']
192 scripts = ['hg']
193 if os.name == 'nt':
193 if os.name == 'nt':
194 # We remove hg.bat if we are able to build hg.exe.
194 # We remove hg.bat if we are able to build hg.exe.
195 scripts.append('contrib/win32/hg.bat')
195 scripts.append('contrib/win32/hg.bat')
196
196
197
197
198 def cancompile(cc, code):
198 def cancompile(cc, code):
199 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
199 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
200 devnull = oldstderr = None
200 devnull = oldstderr = None
201 try:
201 try:
202 fname = os.path.join(tmpdir, 'testcomp.c')
202 fname = os.path.join(tmpdir, 'testcomp.c')
203 f = open(fname, 'w')
203 f = open(fname, 'w')
204 f.write(code)
204 f.write(code)
205 f.close()
205 f.close()
206 # Redirect stderr to /dev/null to hide any error messages
206 # Redirect stderr to /dev/null to hide any error messages
207 # from the compiler.
207 # from the compiler.
208 # This will have to be changed if we ever have to check
208 # This will have to be changed if we ever have to check
209 # for a function on Windows.
209 # for a function on Windows.
210 devnull = open('/dev/null', 'w')
210 devnull = open('/dev/null', 'w')
211 oldstderr = os.dup(sys.stderr.fileno())
211 oldstderr = os.dup(sys.stderr.fileno())
212 os.dup2(devnull.fileno(), sys.stderr.fileno())
212 os.dup2(devnull.fileno(), sys.stderr.fileno())
213 objects = cc.compile([fname], output_dir=tmpdir)
213 objects = cc.compile([fname], output_dir=tmpdir)
214 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
214 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
215 return True
215 return True
216 except Exception:
216 except Exception:
217 return False
217 return False
218 finally:
218 finally:
219 if oldstderr is not None:
219 if oldstderr is not None:
220 os.dup2(oldstderr, sys.stderr.fileno())
220 os.dup2(oldstderr, sys.stderr.fileno())
221 if devnull is not None:
221 if devnull is not None:
222 devnull.close()
222 devnull.close()
223 shutil.rmtree(tmpdir)
223 shutil.rmtree(tmpdir)
224
224
225
225
226 # simplified version of distutils.ccompiler.CCompiler.has_function
226 # simplified version of distutils.ccompiler.CCompiler.has_function
227 # that actually removes its temporary files.
227 # that actually removes its temporary files.
228 def hasfunction(cc, funcname):
228 def hasfunction(cc, funcname):
229 code = 'int main(void) { %s(); }\n' % funcname
229 code = 'int main(void) { %s(); }\n' % funcname
230 return cancompile(cc, code)
230 return cancompile(cc, code)
231
231
232
232
233 def hasheader(cc, headername):
233 def hasheader(cc, headername):
234 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
234 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
235 return cancompile(cc, code)
235 return cancompile(cc, code)
236
236
237
237
238 # py2exe needs to be installed to work
238 # py2exe needs to be installed to work
239 try:
239 try:
240 import py2exe
240 import py2exe
241
241
242 py2exe.Distribution # silence unused import warning
242 py2exe.Distribution # silence unused import warning
243 py2exeloaded = True
243 py2exeloaded = True
244 # import py2exe's patched Distribution class
244 # import py2exe's patched Distribution class
245 from distutils.core import Distribution
245 from distutils.core import Distribution
246 except ImportError:
246 except ImportError:
247 py2exeloaded = False
247 py2exeloaded = False
248
248
249
249
250 def runcmd(cmd, env, cwd=None):
250 def runcmd(cmd, env, cwd=None):
251 p = subprocess.Popen(
251 p = subprocess.Popen(
252 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, cwd=cwd
252 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, cwd=cwd
253 )
253 )
254 out, err = p.communicate()
254 out, err = p.communicate()
255 return p.returncode, out, err
255 return p.returncode, out, err
256
256
257
257
258 class hgcommand(object):
258 class hgcommand(object):
259 def __init__(self, cmd, env):
259 def __init__(self, cmd, env):
260 self.cmd = cmd
260 self.cmd = cmd
261 self.env = env
261 self.env = env
262
262
263 def run(self, args):
263 def run(self, args):
264 cmd = self.cmd + args
264 cmd = self.cmd + args
265 returncode, out, err = runcmd(cmd, self.env)
265 returncode, out, err = runcmd(cmd, self.env)
266 err = filterhgerr(err)
266 err = filterhgerr(err)
267 if err or returncode != 0:
267 if err or returncode != 0:
268 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
268 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
269 printf(err, file=sys.stderr)
269 printf(err, file=sys.stderr)
270 return b''
270 return b''
271 return out
271 return out
272
272
273
273
274 def filterhgerr(err):
274 def filterhgerr(err):
275 # If root is executing setup.py, but the repository is owned by
275 # If root is executing setup.py, but the repository is owned by
276 # another user (as in "sudo python setup.py install") we will get
276 # another user (as in "sudo python setup.py install") we will get
277 # trust warnings since the .hg/hgrc file is untrusted. That is
277 # trust warnings since the .hg/hgrc file is untrusted. That is
278 # fine, we don't want to load it anyway. Python may warn about
278 # fine, we don't want to load it anyway. Python may warn about
279 # a missing __init__.py in mercurial/locale, we also ignore that.
279 # a missing __init__.py in mercurial/locale, we also ignore that.
280 err = [
280 err = [
281 e
281 e
282 for e in err.splitlines()
282 for e in err.splitlines()
283 if (
283 if (
284 not e.startswith(b'not trusting file')
284 not e.startswith(b'not trusting file')
285 and not e.startswith(b'warning: Not importing')
285 and not e.startswith(b'warning: Not importing')
286 and not e.startswith(b'obsolete feature not enabled')
286 and not e.startswith(b'obsolete feature not enabled')
287 and not e.startswith(b'*** failed to import extension')
287 and not e.startswith(b'*** failed to import extension')
288 and not e.startswith(b'devel-warn:')
288 and not e.startswith(b'devel-warn:')
289 and not (
289 and not (
290 e.startswith(b'(third party extension')
290 e.startswith(b'(third party extension')
291 and e.endswith(b'or newer of Mercurial; disabling)')
291 and e.endswith(b'or newer of Mercurial; disabling)')
292 )
292 )
293 )
293 )
294 ]
294 ]
295 return b'\n'.join(b' ' + e for e in err)
295 return b'\n'.join(b' ' + e for e in err)
296
296
297
297
298 def findhg():
298 def findhg():
299 """Try to figure out how we should invoke hg for examining the local
299 """Try to figure out how we should invoke hg for examining the local
300 repository contents.
300 repository contents.
301
301
302 Returns an hgcommand object."""
302 Returns an hgcommand object."""
303 # By default, prefer the "hg" command in the user's path. This was
303 # By default, prefer the "hg" command in the user's path. This was
304 # presumably the hg command that the user used to create this repository.
304 # presumably the hg command that the user used to create this repository.
305 #
305 #
306 # This repository may require extensions or other settings that would not
306 # This repository may require extensions or other settings that would not
307 # be enabled by running the hg script directly from this local repository.
307 # be enabled by running the hg script directly from this local repository.
308 hgenv = os.environ.copy()
308 hgenv = os.environ.copy()
309 # Use HGPLAIN to disable hgrc settings that would change output formatting,
309 # Use HGPLAIN to disable hgrc settings that would change output formatting,
310 # and disable localization for the same reasons.
310 # and disable localization for the same reasons.
311 hgenv['HGPLAIN'] = '1'
311 hgenv['HGPLAIN'] = '1'
312 hgenv['LANGUAGE'] = 'C'
312 hgenv['LANGUAGE'] = 'C'
313 hgcmd = ['hg']
313 hgcmd = ['hg']
314 # Run a simple "hg log" command just to see if using hg from the user's
314 # Run a simple "hg log" command just to see if using hg from the user's
315 # path works and can successfully interact with this repository. Windows
315 # path works and can successfully interact with this repository. Windows
316 # gives precedence to hg.exe in the current directory, so fall back to the
316 # gives precedence to hg.exe in the current directory, so fall back to the
317 # python invocation of local hg, where pythonXY.dll can always be found.
317 # python invocation of local hg, where pythonXY.dll can always be found.
318 check_cmd = ['log', '-r.', '-Ttest']
318 check_cmd = ['log', '-r.', '-Ttest']
319 if os.name != 'nt' or not os.path.exists("hg.exe"):
319 if os.name != 'nt' or not os.path.exists("hg.exe"):
320 try:
320 try:
321 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
321 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
322 except EnvironmentError:
322 except EnvironmentError:
323 retcode = -1
323 retcode = -1
324 if retcode == 0 and not filterhgerr(err):
324 if retcode == 0 and not filterhgerr(err):
325 return hgcommand(hgcmd, hgenv)
325 return hgcommand(hgcmd, hgenv)
326
326
327 # Fall back to trying the local hg installation.
327 # Fall back to trying the local hg installation.
328 hgenv = localhgenv()
328 hgenv = localhgenv()
329 hgcmd = [sys.executable, 'hg']
329 hgcmd = [sys.executable, 'hg']
330 try:
330 try:
331 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
331 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
332 except EnvironmentError:
332 except EnvironmentError:
333 retcode = -1
333 retcode = -1
334 if retcode == 0 and not filterhgerr(err):
334 if retcode == 0 and not filterhgerr(err):
335 return hgcommand(hgcmd, hgenv)
335 return hgcommand(hgcmd, hgenv)
336
336
337 raise SystemExit(
337 raise SystemExit(
338 'Unable to find a working hg binary to extract the '
338 'Unable to find a working hg binary to extract the '
339 'version from the repository tags'
339 'version from the repository tags'
340 )
340 )
341
341
342
342
343 def localhgenv():
343 def localhgenv():
344 """Get an environment dictionary to use for invoking or importing
344 """Get an environment dictionary to use for invoking or importing
345 mercurial from the local repository."""
345 mercurial from the local repository."""
346 # Execute hg out of this directory with a custom environment which takes
346 # Execute hg out of this directory with a custom environment which takes
347 # care to not use any hgrc files and do no localization.
347 # care to not use any hgrc files and do no localization.
348 env = {
348 env = {
349 'HGMODULEPOLICY': 'py',
349 'HGMODULEPOLICY': 'py',
350 'HGRCPATH': '',
350 'HGRCPATH': '',
351 'LANGUAGE': 'C',
351 'LANGUAGE': 'C',
352 'PATH': '',
352 'PATH': '',
353 } # make pypi modules that use os.environ['PATH'] happy
353 } # make pypi modules that use os.environ['PATH'] happy
354 if 'LD_LIBRARY_PATH' in os.environ:
354 if 'LD_LIBRARY_PATH' in os.environ:
355 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
355 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
356 if 'SystemRoot' in os.environ:
356 if 'SystemRoot' in os.environ:
357 # SystemRoot is required by Windows to load various DLLs. See:
357 # SystemRoot is required by Windows to load various DLLs. See:
358 # https://bugs.python.org/issue13524#msg148850
358 # https://bugs.python.org/issue13524#msg148850
359 env['SystemRoot'] = os.environ['SystemRoot']
359 env['SystemRoot'] = os.environ['SystemRoot']
360 return env
360 return env
361
361
362
362
363 version = ''
363 version = ''
364
364
365 if os.path.isdir('.hg'):
365 if os.path.isdir('.hg'):
366 hg = findhg()
366 hg = findhg()
367 cmd = ['log', '-r', '.', '--template', '{tags}\n']
367 cmd = ['log', '-r', '.', '--template', '{tags}\n']
368 numerictags = [t for t in sysstr(hg.run(cmd)).split() if t[0:1].isdigit()]
368 numerictags = [t for t in sysstr(hg.run(cmd)).split() if t[0:1].isdigit()]
369 hgid = sysstr(hg.run(['id', '-i'])).strip()
369 hgid = sysstr(hg.run(['id', '-i'])).strip()
370 if not hgid:
370 if not hgid:
371 # Bail out if hg is having problems interacting with this repository,
371 # Bail out if hg is having problems interacting with this repository,
372 # rather than falling through and producing a bogus version number.
372 # rather than falling through and producing a bogus version number.
373 # Continuing with an invalid version number will break extensions
373 # Continuing with an invalid version number will break extensions
374 # that define minimumhgversion.
374 # that define minimumhgversion.
375 raise SystemExit('Unable to determine hg version from local repository')
375 raise SystemExit('Unable to determine hg version from local repository')
376 if numerictags: # tag(s) found
376 if numerictags: # tag(s) found
377 version = numerictags[-1]
377 version = numerictags[-1]
378 if hgid.endswith('+'): # propagate the dirty status to the tag
378 if hgid.endswith('+'): # propagate the dirty status to the tag
379 version += '+'
379 version += '+'
380 else: # no tag found
380 else: # no tag found
381 ltagcmd = ['parents', '--template', '{latesttag}']
381 ltagcmd = ['parents', '--template', '{latesttag}']
382 ltag = sysstr(hg.run(ltagcmd))
382 ltag = sysstr(hg.run(ltagcmd))
383 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
383 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
384 changessince = len(hg.run(changessincecmd).splitlines())
384 changessince = len(hg.run(changessincecmd).splitlines())
385 version = '%s+%s-%s' % (ltag, changessince, hgid)
385 version = '%s+%s-%s' % (ltag, changessince, hgid)
386 if version.endswith('+'):
386 if version.endswith('+'):
387 version += time.strftime('%Y%m%d')
387 version += time.strftime('%Y%m%d')
388 elif os.path.exists('.hg_archival.txt'):
388 elif os.path.exists('.hg_archival.txt'):
389 kw = dict(
389 kw = dict(
390 [[t.strip() for t in l.split(':', 1)] for l in open('.hg_archival.txt')]
390 [[t.strip() for t in l.split(':', 1)] for l in open('.hg_archival.txt')]
391 )
391 )
392 if 'tag' in kw:
392 if 'tag' in kw:
393 version = kw['tag']
393 version = kw['tag']
394 elif 'latesttag' in kw:
394 elif 'latesttag' in kw:
395 if 'changessincelatesttag' in kw:
395 if 'changessincelatesttag' in kw:
396 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
396 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
397 else:
397 else:
398 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
398 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
399 else:
399 else:
400 version = kw.get('node', '')[:12]
400 version = kw.get('node', '')[:12]
401
401
402 if version:
402 if version:
403 versionb = version
403 versionb = version
404 if not isinstance(versionb, bytes):
404 if not isinstance(versionb, bytes):
405 versionb = versionb.encode('ascii')
405 versionb = versionb.encode('ascii')
406
406
407 write_if_changed(
407 write_if_changed(
408 'mercurial/__version__.py',
408 'mercurial/__version__.py',
409 b''.join(
409 b''.join(
410 [
410 [
411 b'# this file is autogenerated by setup.py\n'
411 b'# this file is autogenerated by setup.py\n'
412 b'version = b"%s"\n' % versionb,
412 b'version = b"%s"\n' % versionb,
413 ]
413 ]
414 ),
414 ),
415 )
415 )
416
416
417 try:
417 try:
418 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
418 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
419 os.environ['HGMODULEPOLICY'] = 'py'
419 os.environ['HGMODULEPOLICY'] = 'py'
420 from mercurial import __version__
420 from mercurial import __version__
421
421
422 version = __version__.version
422 version = __version__.version
423 except ImportError:
423 except ImportError:
424 version = b'unknown'
424 version = b'unknown'
425 finally:
425 finally:
426 if oldpolicy is None:
426 if oldpolicy is None:
427 del os.environ['HGMODULEPOLICY']
427 del os.environ['HGMODULEPOLICY']
428 else:
428 else:
429 os.environ['HGMODULEPOLICY'] = oldpolicy
429 os.environ['HGMODULEPOLICY'] = oldpolicy
430
430
431
431
432 class hgbuild(build):
432 class hgbuild(build):
433 # Insert hgbuildmo first so that files in mercurial/locale/ are found
433 # Insert hgbuildmo first so that files in mercurial/locale/ are found
434 # when build_py is run next.
434 # when build_py is run next.
435 sub_commands = [('build_mo', None)] + build.sub_commands
435 sub_commands = [('build_mo', None)] + build.sub_commands
436
436
437
437
438 class hgbuildmo(build):
438 class hgbuildmo(build):
439
439
440 description = "build translations (.mo files)"
440 description = "build translations (.mo files)"
441
441
442 def run(self):
442 def run(self):
443 if not find_executable('msgfmt'):
443 if not find_executable('msgfmt'):
444 self.warn(
444 self.warn(
445 "could not find msgfmt executable, no translations "
445 "could not find msgfmt executable, no translations "
446 "will be built"
446 "will be built"
447 )
447 )
448 return
448 return
449
449
450 podir = 'i18n'
450 podir = 'i18n'
451 if not os.path.isdir(podir):
451 if not os.path.isdir(podir):
452 self.warn("could not find %s/ directory" % podir)
452 self.warn("could not find %s/ directory" % podir)
453 return
453 return
454
454
455 join = os.path.join
455 join = os.path.join
456 for po in os.listdir(podir):
456 for po in os.listdir(podir):
457 if not po.endswith('.po'):
457 if not po.endswith('.po'):
458 continue
458 continue
459 pofile = join(podir, po)
459 pofile = join(podir, po)
460 modir = join('locale', po[:-3], 'LC_MESSAGES')
460 modir = join('locale', po[:-3], 'LC_MESSAGES')
461 mofile = join(modir, 'hg.mo')
461 mofile = join(modir, 'hg.mo')
462 mobuildfile = join('mercurial', mofile)
462 mobuildfile = join('mercurial', mofile)
463 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
463 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
464 if sys.platform != 'sunos5':
464 if sys.platform != 'sunos5':
465 # msgfmt on Solaris does not know about -c
465 # msgfmt on Solaris does not know about -c
466 cmd.append('-c')
466 cmd.append('-c')
467 self.mkpath(join('mercurial', modir))
467 self.mkpath(join('mercurial', modir))
468 self.make_file([pofile], mobuildfile, spawn, (cmd,))
468 self.make_file([pofile], mobuildfile, spawn, (cmd,))
469
469
470
470
471 class hgdist(Distribution):
471 class hgdist(Distribution):
472 pure = False
472 pure = False
473 rust = False
473 rust = False
474 no_rust = False
474 no_rust = False
475 cffi = ispypy
475 cffi = ispypy
476
476
477 global_options = Distribution.global_options + [
477 global_options = Distribution.global_options + [
478 ('pure', None, "use pure (slow) Python code instead of C extensions"),
478 ('pure', None, "use pure (slow) Python code instead of C extensions"),
479 ('rust', None, "use Rust extensions additionally to C extensions"),
479 ('rust', None, "use Rust extensions additionally to C extensions"),
480 (
480 (
481 'no-rust',
481 'no-rust',
482 None,
482 None,
483 "do not use Rust extensions additionally to C extensions",
483 "do not use Rust extensions additionally to C extensions",
484 ),
484 ),
485 ]
485 ]
486
486
487 negative_opt = Distribution.negative_opt.copy()
487 negative_opt = Distribution.negative_opt.copy()
488 boolean_options = ['pure', 'rust', 'no-rust']
488 boolean_options = ['pure', 'rust', 'no-rust']
489 negative_opt['no-rust'] = 'rust'
489 negative_opt['no-rust'] = 'rust'
490
490
491 def _set_command_options(self, command_obj, option_dict=None):
491 def _set_command_options(self, command_obj, option_dict=None):
492 # Not all distutils versions in the wild have boolean_options.
492 # Not all distutils versions in the wild have boolean_options.
493 # This should be cleaned up when we're Python 3 only.
493 # This should be cleaned up when we're Python 3 only.
494 command_obj.boolean_options = (
494 command_obj.boolean_options = (
495 getattr(command_obj, 'boolean_options', []) + self.boolean_options
495 getattr(command_obj, 'boolean_options', []) + self.boolean_options
496 )
496 )
497 return Distribution._set_command_options(
497 return Distribution._set_command_options(
498 self, command_obj, option_dict=option_dict
498 self, command_obj, option_dict=option_dict
499 )
499 )
500
500
501 def parse_command_line(self):
501 def parse_command_line(self):
502 ret = Distribution.parse_command_line(self)
502 ret = Distribution.parse_command_line(self)
503 if not (self.rust or self.no_rust):
503 if not (self.rust or self.no_rust):
504 hgrustext = os.environ.get('HGWITHRUSTEXT')
504 hgrustext = os.environ.get('HGWITHRUSTEXT')
505 # TODO record it for proper rebuild upon changes
505 # TODO record it for proper rebuild upon changes
506 # (see mercurial/__modulepolicy__.py)
506 # (see mercurial/__modulepolicy__.py)
507 if hgrustext != 'cpython' and hgrustext is not None:
507 if hgrustext != 'cpython' and hgrustext is not None:
508 if hgrustext:
508 if hgrustext:
509 msg = 'unkown HGWITHRUSTEXT value: %s' % hgrustext
509 msg = 'unkown HGWITHRUSTEXT value: %s' % hgrustext
510 printf(msg, file=sys.stderr)
510 printf(msg, file=sys.stderr)
511 hgrustext = None
511 hgrustext = None
512 self.rust = hgrustext is not None
512 self.rust = hgrustext is not None
513 self.no_rust = not self.rust
513 self.no_rust = not self.rust
514 return ret
514 return ret
515
515
516 def has_ext_modules(self):
516 def has_ext_modules(self):
517 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
517 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
518 # too late for some cases
518 # too late for some cases
519 return not self.pure and Distribution.has_ext_modules(self)
519 return not self.pure and Distribution.has_ext_modules(self)
520
520
521
521
522 # This is ugly as a one-liner. So use a variable.
522 # This is ugly as a one-liner. So use a variable.
523 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
523 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
524 buildextnegops['no-zstd'] = 'zstd'
524 buildextnegops['no-zstd'] = 'zstd'
525 buildextnegops['no-rust'] = 'rust'
525 buildextnegops['no-rust'] = 'rust'
526
526
527
527
528 class hgbuildext(build_ext):
528 class hgbuildext(build_ext):
529 user_options = build_ext.user_options + [
529 user_options = build_ext.user_options + [
530 ('zstd', None, 'compile zstd bindings [default]'),
530 ('zstd', None, 'compile zstd bindings [default]'),
531 ('no-zstd', None, 'do not compile zstd bindings'),
531 ('no-zstd', None, 'do not compile zstd bindings'),
532 (
532 (
533 'rust',
533 'rust',
534 None,
534 None,
535 'compile Rust extensions if they are in use '
535 'compile Rust extensions if they are in use '
536 '(requires Cargo) [default]',
536 '(requires Cargo) [default]',
537 ),
537 ),
538 ('no-rust', None, 'do not compile Rust extensions'),
538 ('no-rust', None, 'do not compile Rust extensions'),
539 ]
539 ]
540
540
541 boolean_options = build_ext.boolean_options + ['zstd', 'rust']
541 boolean_options = build_ext.boolean_options + ['zstd', 'rust']
542 negative_opt = buildextnegops
542 negative_opt = buildextnegops
543
543
544 def initialize_options(self):
544 def initialize_options(self):
545 self.zstd = True
545 self.zstd = True
546 self.rust = True
546 self.rust = True
547
547
548 return build_ext.initialize_options(self)
548 return build_ext.initialize_options(self)
549
549
550 def finalize_options(self):
550 def finalize_options(self):
551 # Unless overridden by the end user, build extensions in parallel.
551 # Unless overridden by the end user, build extensions in parallel.
552 # Only influences behavior on Python 3.5+.
552 # Only influences behavior on Python 3.5+.
553 if getattr(self, 'parallel', None) is None:
553 if getattr(self, 'parallel', None) is None:
554 self.parallel = True
554 self.parallel = True
555
555
556 return build_ext.finalize_options(self)
556 return build_ext.finalize_options(self)
557
557
558 def build_extensions(self):
558 def build_extensions(self):
559 ruststandalones = [
559 ruststandalones = [
560 e for e in self.extensions if isinstance(e, RustStandaloneExtension)
560 e for e in self.extensions if isinstance(e, RustStandaloneExtension)
561 ]
561 ]
562 self.extensions = [
562 self.extensions = [
563 e for e in self.extensions if e not in ruststandalones
563 e for e in self.extensions if e not in ruststandalones
564 ]
564 ]
565 # Filter out zstd if disabled via argument.
565 # Filter out zstd if disabled via argument.
566 if not self.zstd:
566 if not self.zstd:
567 self.extensions = [
567 self.extensions = [
568 e for e in self.extensions if e.name != 'mercurial.zstd'
568 e for e in self.extensions if e.name != 'mercurial.zstd'
569 ]
569 ]
570
570
571 # Build Rust standalon extensions if it'll be used
571 # Build Rust standalon extensions if it'll be used
572 # and its build is not explictely disabled (for external build
572 # and its build is not explictely disabled (for external build
573 # as Linux distributions would do)
573 # as Linux distributions would do)
574 if self.distribution.rust and self.rust:
574 if self.distribution.rust and self.rust:
575 for rustext in ruststandalones:
575 for rustext in ruststandalones:
576 rustext.build('' if self.inplace else self.build_lib)
576 rustext.build('' if self.inplace else self.build_lib)
577
577
578 return build_ext.build_extensions(self)
578 return build_ext.build_extensions(self)
579
579
580 def build_extension(self, ext):
580 def build_extension(self, ext):
581 if (
581 if (
582 self.distribution.rust
582 self.distribution.rust
583 and self.rust
583 and self.rust
584 and isinstance(ext, RustExtension)
584 and isinstance(ext, RustExtension)
585 ):
585 ):
586 ext.rustbuild()
586 ext.rustbuild()
587 try:
587 try:
588 build_ext.build_extension(self, ext)
588 build_ext.build_extension(self, ext)
589 except CCompilerError:
589 except CCompilerError:
590 if not getattr(ext, 'optional', False):
590 if not getattr(ext, 'optional', False):
591 raise
591 raise
592 log.warn(
592 log.warn(
593 "Failed to build optional extension '%s' (skipping)", ext.name
593 "Failed to build optional extension '%s' (skipping)", ext.name
594 )
594 )
595
595
596
596
597 class hgbuildscripts(build_scripts):
597 class hgbuildscripts(build_scripts):
598 def run(self):
598 def run(self):
599 if os.name != 'nt' or self.distribution.pure:
599 if os.name != 'nt' or self.distribution.pure:
600 return build_scripts.run(self)
600 return build_scripts.run(self)
601
601
602 exebuilt = False
602 exebuilt = False
603 try:
603 try:
604 self.run_command('build_hgexe')
604 self.run_command('build_hgexe')
605 exebuilt = True
605 exebuilt = True
606 except (DistutilsError, CCompilerError):
606 except (DistutilsError, CCompilerError):
607 log.warn('failed to build optional hg.exe')
607 log.warn('failed to build optional hg.exe')
608
608
609 if exebuilt:
609 if exebuilt:
610 # Copying hg.exe to the scripts build directory ensures it is
610 # Copying hg.exe to the scripts build directory ensures it is
611 # installed by the install_scripts command.
611 # installed by the install_scripts command.
612 hgexecommand = self.get_finalized_command('build_hgexe')
612 hgexecommand = self.get_finalized_command('build_hgexe')
613 dest = os.path.join(self.build_dir, 'hg.exe')
613 dest = os.path.join(self.build_dir, 'hg.exe')
614 self.mkpath(self.build_dir)
614 self.mkpath(self.build_dir)
615 self.copy_file(hgexecommand.hgexepath, dest)
615 self.copy_file(hgexecommand.hgexepath, dest)
616
616
617 # Remove hg.bat because it is redundant with hg.exe.
617 # Remove hg.bat because it is redundant with hg.exe.
618 self.scripts.remove('contrib/win32/hg.bat')
618 self.scripts.remove('contrib/win32/hg.bat')
619
619
620 return build_scripts.run(self)
620 return build_scripts.run(self)
621
621
622
622
623 class hgbuildpy(build_py):
623 class hgbuildpy(build_py):
624 def finalize_options(self):
624 def finalize_options(self):
625 build_py.finalize_options(self)
625 build_py.finalize_options(self)
626
626
627 if self.distribution.pure:
627 if self.distribution.pure:
628 self.distribution.ext_modules = []
628 self.distribution.ext_modules = []
629 elif self.distribution.cffi:
629 elif self.distribution.cffi:
630 from mercurial.cffi import (
630 from mercurial.cffi import (
631 bdiffbuild,
631 bdiffbuild,
632 mpatchbuild,
632 mpatchbuild,
633 )
633 )
634
634
635 exts = [
635 exts = [
636 mpatchbuild.ffi.distutils_extension(),
636 mpatchbuild.ffi.distutils_extension(),
637 bdiffbuild.ffi.distutils_extension(),
637 bdiffbuild.ffi.distutils_extension(),
638 ]
638 ]
639 # cffi modules go here
639 # cffi modules go here
640 if sys.platform == 'darwin':
640 if sys.platform == 'darwin':
641 from mercurial.cffi import osutilbuild
641 from mercurial.cffi import osutilbuild
642
642
643 exts.append(osutilbuild.ffi.distutils_extension())
643 exts.append(osutilbuild.ffi.distutils_extension())
644 self.distribution.ext_modules = exts
644 self.distribution.ext_modules = exts
645 else:
645 else:
646 h = os.path.join(get_python_inc(), 'Python.h')
646 h = os.path.join(get_python_inc(), 'Python.h')
647 if not os.path.exists(h):
647 if not os.path.exists(h):
648 raise SystemExit(
648 raise SystemExit(
649 'Python headers are required to build '
649 'Python headers are required to build '
650 'Mercurial but weren\'t found in %s' % h
650 'Mercurial but weren\'t found in %s' % h
651 )
651 )
652
652
653 def run(self):
653 def run(self):
654 basepath = os.path.join(self.build_lib, 'mercurial')
654 basepath = os.path.join(self.build_lib, 'mercurial')
655 self.mkpath(basepath)
655 self.mkpath(basepath)
656
656
657 rust = self.distribution.rust
657 rust = self.distribution.rust
658 if self.distribution.pure:
658 if self.distribution.pure:
659 modulepolicy = 'py'
659 modulepolicy = 'py'
660 elif self.build_lib == '.':
660 elif self.build_lib == '.':
661 # in-place build should run without rebuilding and Rust extensions
661 # in-place build should run without rebuilding and Rust extensions
662 modulepolicy = 'rust+c-allow' if rust else 'allow'
662 modulepolicy = 'rust+c-allow' if rust else 'allow'
663 else:
663 else:
664 modulepolicy = 'rust+c' if rust else 'c'
664 modulepolicy = 'rust+c' if rust else 'c'
665
665
666 content = b''.join(
666 content = b''.join(
667 [
667 [
668 b'# this file is autogenerated by setup.py\n',
668 b'# this file is autogenerated by setup.py\n',
669 b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
669 b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
670 ]
670 ]
671 )
671 )
672 write_if_changed(os.path.join(basepath, '__modulepolicy__.py'), content)
672 write_if_changed(os.path.join(basepath, '__modulepolicy__.py'), content)
673
673
674 build_py.run(self)
674 build_py.run(self)
675
675
676
676
677 class buildhgextindex(Command):
677 class buildhgextindex(Command):
678 description = 'generate prebuilt index of hgext (for frozen package)'
678 description = 'generate prebuilt index of hgext (for frozen package)'
679 user_options = []
679 user_options = []
680 _indexfilename = 'hgext/__index__.py'
680 _indexfilename = 'hgext/__index__.py'
681
681
682 def initialize_options(self):
682 def initialize_options(self):
683 pass
683 pass
684
684
685 def finalize_options(self):
685 def finalize_options(self):
686 pass
686 pass
687
687
688 def run(self):
688 def run(self):
689 if os.path.exists(self._indexfilename):
689 if os.path.exists(self._indexfilename):
690 with open(self._indexfilename, 'w') as f:
690 with open(self._indexfilename, 'w') as f:
691 f.write('# empty\n')
691 f.write('# empty\n')
692
692
693 # here no extension enabled, disabled() lists up everything
693 # here no extension enabled, disabled() lists up everything
694 code = (
694 code = (
695 'import pprint; from mercurial import extensions; '
695 'import pprint; from mercurial import extensions; '
696 'ext = extensions.disabled();'
696 'ext = extensions.disabled();'
697 'ext.pop("__index__", None);'
697 'ext.pop("__index__", None);'
698 'pprint.pprint(ext)'
698 'pprint.pprint(ext)'
699 )
699 )
700 returncode, out, err = runcmd(
700 returncode, out, err = runcmd(
701 [sys.executable, '-c', code], localhgenv()
701 [sys.executable, '-c', code], localhgenv()
702 )
702 )
703 if err or returncode != 0:
703 if err or returncode != 0:
704 raise DistutilsExecError(err)
704 raise DistutilsExecError(err)
705
705
706 with open(self._indexfilename, 'wb') as f:
706 with open(self._indexfilename, 'wb') as f:
707 f.write(b'# this file is autogenerated by setup.py\n')
707 f.write(b'# this file is autogenerated by setup.py\n')
708 f.write(b'docs = ')
708 f.write(b'docs = ')
709 f.write(out)
709 f.write(out)
710
710
711
711
712 class buildhgexe(build_ext):
712 class buildhgexe(build_ext):
713 description = 'compile hg.exe from mercurial/exewrapper.c'
713 description = 'compile hg.exe from mercurial/exewrapper.c'
714 user_options = build_ext.user_options + [
714 user_options = build_ext.user_options + [
715 (
715 (
716 'long-paths-support',
716 'long-paths-support',
717 None,
717 None,
718 'enable support for long paths on '
718 'enable support for long paths on '
719 'Windows (off by default and '
719 'Windows (off by default and '
720 'experimental)',
720 'experimental)',
721 ),
721 ),
722 ]
722 ]
723
723
724 LONG_PATHS_MANIFEST = """
724 LONG_PATHS_MANIFEST = """
725 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
725 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
726 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
726 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
727 <application>
727 <application>
728 <windowsSettings
728 <windowsSettings
729 xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
729 xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
730 <ws2:longPathAware>true</ws2:longPathAware>
730 <ws2:longPathAware>true</ws2:longPathAware>
731 </windowsSettings>
731 </windowsSettings>
732 </application>
732 </application>
733 </assembly>"""
733 </assembly>"""
734
734
735 def initialize_options(self):
735 def initialize_options(self):
736 build_ext.initialize_options(self)
736 build_ext.initialize_options(self)
737 self.long_paths_support = False
737 self.long_paths_support = False
738
738
739 def build_extensions(self):
739 def build_extensions(self):
740 if os.name != 'nt':
740 if os.name != 'nt':
741 return
741 return
742 if isinstance(self.compiler, HackedMingw32CCompiler):
742 if isinstance(self.compiler, HackedMingw32CCompiler):
743 self.compiler.compiler_so = self.compiler.compiler # no -mdll
743 self.compiler.compiler_so = self.compiler.compiler # no -mdll
744 self.compiler.dll_libraries = [] # no -lmsrvc90
744 self.compiler.dll_libraries = [] # no -lmsrvc90
745
745
746 pythonlib = None
746 pythonlib = None
747
747
748 if getattr(sys, 'dllhandle', None):
748 if getattr(sys, 'dllhandle', None):
749 # Different Python installs can have different Python library
749 # Different Python installs can have different Python library
750 # names. e.g. the official CPython distribution uses pythonXY.dll
750 # names. e.g. the official CPython distribution uses pythonXY.dll
751 # and MinGW uses libpythonX.Y.dll.
751 # and MinGW uses libpythonX.Y.dll.
752 _kernel32 = ctypes.windll.kernel32
752 _kernel32 = ctypes.windll.kernel32
753 _kernel32.GetModuleFileNameA.argtypes = [
753 _kernel32.GetModuleFileNameA.argtypes = [
754 ctypes.c_void_p,
754 ctypes.c_void_p,
755 ctypes.c_void_p,
755 ctypes.c_void_p,
756 ctypes.c_ulong,
756 ctypes.c_ulong,
757 ]
757 ]
758 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
758 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
759 size = 1000
759 size = 1000
760 buf = ctypes.create_string_buffer(size + 1)
760 buf = ctypes.create_string_buffer(size + 1)
761 filelen = _kernel32.GetModuleFileNameA(
761 filelen = _kernel32.GetModuleFileNameA(
762 sys.dllhandle, ctypes.byref(buf), size
762 sys.dllhandle, ctypes.byref(buf), size
763 )
763 )
764
764
765 if filelen > 0 and filelen != size:
765 if filelen > 0 and filelen != size:
766 dllbasename = os.path.basename(buf.value)
766 dllbasename = os.path.basename(buf.value)
767 if not dllbasename.lower().endswith(b'.dll'):
767 if not dllbasename.lower().endswith(b'.dll'):
768 raise SystemExit(
768 raise SystemExit(
769 'Python DLL does not end with .dll: %s' % dllbasename
769 'Python DLL does not end with .dll: %s' % dllbasename
770 )
770 )
771 pythonlib = dllbasename[:-4]
771 pythonlib = dllbasename[:-4]
772
772
773 if not pythonlib:
773 if not pythonlib:
774 log.warn(
774 log.warn(
775 'could not determine Python DLL filename; assuming pythonXY'
775 'could not determine Python DLL filename; assuming pythonXY'
776 )
776 )
777
777
778 hv = sys.hexversion
778 hv = sys.hexversion
779 pythonlib = b'python%d%d' % (hv >> 24, (hv >> 16) & 0xFF)
779 pythonlib = b'python%d%d' % (hv >> 24, (hv >> 16) & 0xFF)
780
780
781 log.info('using %s as Python library name' % pythonlib)
781 log.info('using %s as Python library name' % pythonlib)
782 with open('mercurial/hgpythonlib.h', 'wb') as f:
782 with open('mercurial/hgpythonlib.h', 'wb') as f:
783 f.write(b'/* this file is autogenerated by setup.py */\n')
783 f.write(b'/* this file is autogenerated by setup.py */\n')
784 f.write(b'#define HGPYTHONLIB "%s"\n' % pythonlib)
784 f.write(b'#define HGPYTHONLIB "%s"\n' % pythonlib)
785
785
786 macros = None
786 macros = None
787 if sys.version_info[0] >= 3:
787 if sys.version_info[0] >= 3:
788 macros = [('_UNICODE', None), ('UNICODE', None)]
788 macros = [('_UNICODE', None), ('UNICODE', None)]
789
789
790 objects = self.compiler.compile(
790 objects = self.compiler.compile(
791 ['mercurial/exewrapper.c'],
791 ['mercurial/exewrapper.c'],
792 output_dir=self.build_temp,
792 output_dir=self.build_temp,
793 macros=macros,
793 macros=macros,
794 )
794 )
795 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
795 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
796 self.hgtarget = os.path.join(dir, 'hg')
796 self.hgtarget = os.path.join(dir, 'hg')
797 self.compiler.link_executable(
797 self.compiler.link_executable(
798 objects, self.hgtarget, libraries=[], output_dir=self.build_temp
798 objects, self.hgtarget, libraries=[], output_dir=self.build_temp
799 )
799 )
800 if self.long_paths_support:
800 if self.long_paths_support:
801 self.addlongpathsmanifest()
801 self.addlongpathsmanifest()
802
802
803 def addlongpathsmanifest(self):
803 def addlongpathsmanifest(self):
804 r"""Add manifest pieces so that hg.exe understands long paths
804 r"""Add manifest pieces so that hg.exe understands long paths
805
805
806 This is an EXPERIMENTAL feature, use with care.
806 This is an EXPERIMENTAL feature, use with care.
807 To enable long paths support, one needs to do two things:
807 To enable long paths support, one needs to do two things:
808 - build Mercurial with --long-paths-support option
808 - build Mercurial with --long-paths-support option
809 - change HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\
809 - change HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\
810 LongPathsEnabled to have value 1.
810 LongPathsEnabled to have value 1.
811
811
812 Please ignore 'warning 81010002: Unrecognized Element "longPathAware"';
812 Please ignore 'warning 81010002: Unrecognized Element "longPathAware"';
813 it happens because Mercurial uses mt.exe circa 2008, which is not
813 it happens because Mercurial uses mt.exe circa 2008, which is not
814 yet aware of long paths support in the manifest (I think so at least).
814 yet aware of long paths support in the manifest (I think so at least).
815 This does not stop mt.exe from embedding/merging the XML properly.
815 This does not stop mt.exe from embedding/merging the XML properly.
816
816
817 Why resource #1 should be used for .exe manifests? I don't know and
817 Why resource #1 should be used for .exe manifests? I don't know and
818 wasn't able to find an explanation for mortals. But it seems to work.
818 wasn't able to find an explanation for mortals. But it seems to work.
819 """
819 """
820 exefname = self.compiler.executable_filename(self.hgtarget)
820 exefname = self.compiler.executable_filename(self.hgtarget)
821 fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
821 fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
822 os.close(fdauto)
822 os.close(fdauto)
823 with open(manfname, 'w') as f:
823 with open(manfname, 'w') as f:
824 f.write(self.LONG_PATHS_MANIFEST)
824 f.write(self.LONG_PATHS_MANIFEST)
825 log.info("long paths manifest is written to '%s'" % manfname)
825 log.info("long paths manifest is written to '%s'" % manfname)
826 inputresource = '-inputresource:%s;#1' % exefname
826 inputresource = '-inputresource:%s;#1' % exefname
827 outputresource = '-outputresource:%s;#1' % exefname
827 outputresource = '-outputresource:%s;#1' % exefname
828 log.info("running mt.exe to update hg.exe's manifest in-place")
828 log.info("running mt.exe to update hg.exe's manifest in-place")
829 # supplying both -manifest and -inputresource to mt.exe makes
829 # supplying both -manifest and -inputresource to mt.exe makes
830 # it merge the embedded and supplied manifests in the -outputresource
830 # it merge the embedded and supplied manifests in the -outputresource
831 self.spawn(
831 self.spawn(
832 [
832 [
833 'mt.exe',
833 'mt.exe',
834 '-nologo',
834 '-nologo',
835 '-manifest',
835 '-manifest',
836 manfname,
836 manfname,
837 inputresource,
837 inputresource,
838 outputresource,
838 outputresource,
839 ]
839 ]
840 )
840 )
841 log.info("done updating hg.exe's manifest")
841 log.info("done updating hg.exe's manifest")
842 os.remove(manfname)
842 os.remove(manfname)
843
843
844 @property
844 @property
845 def hgexepath(self):
845 def hgexepath(self):
846 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
846 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
847 return os.path.join(self.build_temp, dir, 'hg.exe')
847 return os.path.join(self.build_temp, dir, 'hg.exe')
848
848
849
849
850 class hgbuilddoc(Command):
850 class hgbuilddoc(Command):
851 description = 'build documentation'
851 description = 'build documentation'
852 user_options = [
852 user_options = [
853 ('man', None, 'generate man pages'),
853 ('man', None, 'generate man pages'),
854 ('html', None, 'generate html pages'),
854 ('html', None, 'generate html pages'),
855 ]
855 ]
856
856
857 def initialize_options(self):
857 def initialize_options(self):
858 self.man = None
858 self.man = None
859 self.html = None
859 self.html = None
860
860
861 def finalize_options(self):
861 def finalize_options(self):
862 # If --man or --html are set, only generate what we're told to.
862 # If --man or --html are set, only generate what we're told to.
863 # Otherwise generate everything.
863 # Otherwise generate everything.
864 have_subset = self.man is not None or self.html is not None
864 have_subset = self.man is not None or self.html is not None
865
865
866 if have_subset:
866 if have_subset:
867 self.man = True if self.man else False
867 self.man = True if self.man else False
868 self.html = True if self.html else False
868 self.html = True if self.html else False
869 else:
869 else:
870 self.man = True
870 self.man = True
871 self.html = True
871 self.html = True
872
872
873 def run(self):
873 def run(self):
874 def normalizecrlf(p):
874 def normalizecrlf(p):
875 with open(p, 'rb') as fh:
875 with open(p, 'rb') as fh:
876 orig = fh.read()
876 orig = fh.read()
877
877
878 if b'\r\n' not in orig:
878 if b'\r\n' not in orig:
879 return
879 return
880
880
881 log.info('normalizing %s to LF line endings' % p)
881 log.info('normalizing %s to LF line endings' % p)
882 with open(p, 'wb') as fh:
882 with open(p, 'wb') as fh:
883 fh.write(orig.replace(b'\r\n', b'\n'))
883 fh.write(orig.replace(b'\r\n', b'\n'))
884
884
885 def gentxt(root):
885 def gentxt(root):
886 txt = 'doc/%s.txt' % root
886 txt = 'doc/%s.txt' % root
887 log.info('generating %s' % txt)
887 log.info('generating %s' % txt)
888 res, out, err = runcmd(
888 res, out, err = runcmd(
889 [sys.executable, 'gendoc.py', root], os.environ, cwd='doc'
889 [sys.executable, 'gendoc.py', root], os.environ, cwd='doc'
890 )
890 )
891 if res:
891 if res:
892 raise SystemExit(
892 raise SystemExit(
893 'error running gendoc.py: %s' % '\n'.join([out, err])
893 'error running gendoc.py: %s'
894 % '\n'.join([sysstr(out), sysstr(err)])
894 )
895 )
895
896
896 with open(txt, 'wb') as fh:
897 with open(txt, 'wb') as fh:
897 fh.write(out)
898 fh.write(out)
898
899
899 def gengendoc(root):
900 def gengendoc(root):
900 gendoc = 'doc/%s.gendoc.txt' % root
901 gendoc = 'doc/%s.gendoc.txt' % root
901
902
902 log.info('generating %s' % gendoc)
903 log.info('generating %s' % gendoc)
903 res, out, err = runcmd(
904 res, out, err = runcmd(
904 [sys.executable, 'gendoc.py', '%s.gendoc' % root],
905 [sys.executable, 'gendoc.py', '%s.gendoc' % root],
905 os.environ,
906 os.environ,
906 cwd='doc',
907 cwd='doc',
907 )
908 )
908 if res:
909 if res:
909 raise SystemExit(
910 raise SystemExit(
910 'error running gendoc: %s' % '\n'.join([out, err])
911 'error running gendoc: %s'
912 % '\n'.join([sysstr(out), sysstr(err)])
911 )
913 )
912
914
913 with open(gendoc, 'wb') as fh:
915 with open(gendoc, 'wb') as fh:
914 fh.write(out)
916 fh.write(out)
915
917
916 def genman(root):
918 def genman(root):
917 log.info('generating doc/%s' % root)
919 log.info('generating doc/%s' % root)
918 res, out, err = runcmd(
920 res, out, err = runcmd(
919 [
921 [
920 sys.executable,
922 sys.executable,
921 'runrst',
923 'runrst',
922 'hgmanpage',
924 'hgmanpage',
923 '--halt',
925 '--halt',
924 'warning',
926 'warning',
925 '--strip-elements-with-class',
927 '--strip-elements-with-class',
926 'htmlonly',
928 'htmlonly',
927 '%s.txt' % root,
929 '%s.txt' % root,
928 root,
930 root,
929 ],
931 ],
930 os.environ,
932 os.environ,
931 cwd='doc',
933 cwd='doc',
932 )
934 )
933 if res:
935 if res:
934 raise SystemExit(
936 raise SystemExit(
935 'error running runrst: %s' % '\n'.join([out, err])
937 'error running runrst: %s'
938 % '\n'.join([sysstr(out), sysstr(err)])
936 )
939 )
937
940
938 normalizecrlf('doc/%s' % root)
941 normalizecrlf('doc/%s' % root)
939
942
940 def genhtml(root):
943 def genhtml(root):
941 log.info('generating doc/%s.html' % root)
944 log.info('generating doc/%s.html' % root)
942 res, out, err = runcmd(
945 res, out, err = runcmd(
943 [
946 [
944 sys.executable,
947 sys.executable,
945 'runrst',
948 'runrst',
946 'html',
949 'html',
947 '--halt',
950 '--halt',
948 'warning',
951 'warning',
949 '--link-stylesheet',
952 '--link-stylesheet',
950 '--stylesheet-path',
953 '--stylesheet-path',
951 'style.css',
954 'style.css',
952 '%s.txt' % root,
955 '%s.txt' % root,
953 '%s.html' % root,
956 '%s.html' % root,
954 ],
957 ],
955 os.environ,
958 os.environ,
956 cwd='doc',
959 cwd='doc',
957 )
960 )
958 if res:
961 if res:
959 raise SystemExit(
962 raise SystemExit(
960 'error running runrst: %s' % '\n'.join([out, err])
963 'error running runrst: %s'
964 % '\n'.join([sysstr(out), sysstr(err)])
961 )
965 )
962
966
963 normalizecrlf('doc/%s.html' % root)
967 normalizecrlf('doc/%s.html' % root)
964
968
965 # This logic is duplicated in doc/Makefile.
969 # This logic is duplicated in doc/Makefile.
966 sources = {
970 sources = {
967 f
971 f
968 for f in os.listdir('mercurial/helptext')
972 for f in os.listdir('mercurial/helptext')
969 if re.search(r'[0-9]\.txt$', f)
973 if re.search(r'[0-9]\.txt$', f)
970 }
974 }
971
975
972 # common.txt is a one-off.
976 # common.txt is a one-off.
973 gentxt('common')
977 gentxt('common')
974
978
975 for source in sorted(sources):
979 for source in sorted(sources):
976 assert source[-4:] == '.txt'
980 assert source[-4:] == '.txt'
977 root = source[:-4]
981 root = source[:-4]
978
982
979 gentxt(root)
983 gentxt(root)
980 gengendoc(root)
984 gengendoc(root)
981
985
982 if self.man:
986 if self.man:
983 genman(root)
987 genman(root)
984 if self.html:
988 if self.html:
985 genhtml(root)
989 genhtml(root)
986
990
987
991
988 class hginstall(install):
992 class hginstall(install):
989
993
990 user_options = install.user_options + [
994 user_options = install.user_options + [
991 (
995 (
992 'old-and-unmanageable',
996 'old-and-unmanageable',
993 None,
997 None,
994 'noop, present for eggless setuptools compat',
998 'noop, present for eggless setuptools compat',
995 ),
999 ),
996 (
1000 (
997 'single-version-externally-managed',
1001 'single-version-externally-managed',
998 None,
1002 None,
999 'noop, present for eggless setuptools compat',
1003 'noop, present for eggless setuptools compat',
1000 ),
1004 ),
1001 ]
1005 ]
1002
1006
1003 # Also helps setuptools not be sad while we refuse to create eggs.
1007 # Also helps setuptools not be sad while we refuse to create eggs.
1004 single_version_externally_managed = True
1008 single_version_externally_managed = True
1005
1009
1006 def get_sub_commands(self):
1010 def get_sub_commands(self):
1007 # Screen out egg related commands to prevent egg generation. But allow
1011 # Screen out egg related commands to prevent egg generation. But allow
1008 # mercurial.egg-info generation, since that is part of modern
1012 # mercurial.egg-info generation, since that is part of modern
1009 # packaging.
1013 # packaging.
1010 excl = {'bdist_egg'}
1014 excl = {'bdist_egg'}
1011 return filter(lambda x: x not in excl, install.get_sub_commands(self))
1015 return filter(lambda x: x not in excl, install.get_sub_commands(self))
1012
1016
1013
1017
1014 class hginstalllib(install_lib):
1018 class hginstalllib(install_lib):
1015 '''
1019 '''
1016 This is a specialization of install_lib that replaces the copy_file used
1020 This is a specialization of install_lib that replaces the copy_file used
1017 there so that it supports setting the mode of files after copying them,
1021 there so that it supports setting the mode of files after copying them,
1018 instead of just preserving the mode that the files originally had. If your
1022 instead of just preserving the mode that the files originally had. If your
1019 system has a umask of something like 027, preserving the permissions when
1023 system has a umask of something like 027, preserving the permissions when
1020 copying will lead to a broken install.
1024 copying will lead to a broken install.
1021
1025
1022 Note that just passing keep_permissions=False to copy_file would be
1026 Note that just passing keep_permissions=False to copy_file would be
1023 insufficient, as it might still be applying a umask.
1027 insufficient, as it might still be applying a umask.
1024 '''
1028 '''
1025
1029
1026 def run(self):
1030 def run(self):
1027 realcopyfile = file_util.copy_file
1031 realcopyfile = file_util.copy_file
1028
1032
1029 def copyfileandsetmode(*args, **kwargs):
1033 def copyfileandsetmode(*args, **kwargs):
1030 src, dst = args[0], args[1]
1034 src, dst = args[0], args[1]
1031 dst, copied = realcopyfile(*args, **kwargs)
1035 dst, copied = realcopyfile(*args, **kwargs)
1032 if copied:
1036 if copied:
1033 st = os.stat(src)
1037 st = os.stat(src)
1034 # Persist executable bit (apply it to group and other if user
1038 # Persist executable bit (apply it to group and other if user
1035 # has it)
1039 # has it)
1036 if st[stat.ST_MODE] & stat.S_IXUSR:
1040 if st[stat.ST_MODE] & stat.S_IXUSR:
1037 setmode = int('0755', 8)
1041 setmode = int('0755', 8)
1038 else:
1042 else:
1039 setmode = int('0644', 8)
1043 setmode = int('0644', 8)
1040 m = stat.S_IMODE(st[stat.ST_MODE])
1044 m = stat.S_IMODE(st[stat.ST_MODE])
1041 m = (m & ~int('0777', 8)) | setmode
1045 m = (m & ~int('0777', 8)) | setmode
1042 os.chmod(dst, m)
1046 os.chmod(dst, m)
1043
1047
1044 file_util.copy_file = copyfileandsetmode
1048 file_util.copy_file = copyfileandsetmode
1045 try:
1049 try:
1046 install_lib.run(self)
1050 install_lib.run(self)
1047 finally:
1051 finally:
1048 file_util.copy_file = realcopyfile
1052 file_util.copy_file = realcopyfile
1049
1053
1050
1054
1051 class hginstallscripts(install_scripts):
1055 class hginstallscripts(install_scripts):
1052 '''
1056 '''
1053 This is a specialization of install_scripts that replaces the @LIBDIR@ with
1057 This is a specialization of install_scripts that replaces the @LIBDIR@ with
1054 the configured directory for modules. If possible, the path is made relative
1058 the configured directory for modules. If possible, the path is made relative
1055 to the directory for scripts.
1059 to the directory for scripts.
1056 '''
1060 '''
1057
1061
1058 def initialize_options(self):
1062 def initialize_options(self):
1059 install_scripts.initialize_options(self)
1063 install_scripts.initialize_options(self)
1060
1064
1061 self.install_lib = None
1065 self.install_lib = None
1062
1066
1063 def finalize_options(self):
1067 def finalize_options(self):
1064 install_scripts.finalize_options(self)
1068 install_scripts.finalize_options(self)
1065 self.set_undefined_options('install', ('install_lib', 'install_lib'))
1069 self.set_undefined_options('install', ('install_lib', 'install_lib'))
1066
1070
1067 def run(self):
1071 def run(self):
1068 install_scripts.run(self)
1072 install_scripts.run(self)
1069
1073
1070 # It only makes sense to replace @LIBDIR@ with the install path if
1074 # It only makes sense to replace @LIBDIR@ with the install path if
1071 # the install path is known. For wheels, the logic below calculates
1075 # the install path is known. For wheels, the logic below calculates
1072 # the libdir to be "../..". This is because the internal layout of a
1076 # the libdir to be "../..". This is because the internal layout of a
1073 # wheel archive looks like:
1077 # wheel archive looks like:
1074 #
1078 #
1075 # mercurial-3.6.1.data/scripts/hg
1079 # mercurial-3.6.1.data/scripts/hg
1076 # mercurial/__init__.py
1080 # mercurial/__init__.py
1077 #
1081 #
1078 # When installing wheels, the subdirectories of the "<pkg>.data"
1082 # When installing wheels, the subdirectories of the "<pkg>.data"
1079 # directory are translated to system local paths and files therein
1083 # directory are translated to system local paths and files therein
1080 # are copied in place. The mercurial/* files are installed into the
1084 # are copied in place. The mercurial/* files are installed into the
1081 # site-packages directory. However, the site-packages directory
1085 # site-packages directory. However, the site-packages directory
1082 # isn't known until wheel install time. This means we have no clue
1086 # isn't known until wheel install time. This means we have no clue
1083 # at wheel generation time what the installed site-packages directory
1087 # at wheel generation time what the installed site-packages directory
1084 # will be. And, wheels don't appear to provide the ability to register
1088 # will be. And, wheels don't appear to provide the ability to register
1085 # custom code to run during wheel installation. This all means that
1089 # custom code to run during wheel installation. This all means that
1086 # we can't reliably set the libdir in wheels: the default behavior
1090 # we can't reliably set the libdir in wheels: the default behavior
1087 # of looking in sys.path must do.
1091 # of looking in sys.path must do.
1088
1092
1089 if (
1093 if (
1090 os.path.splitdrive(self.install_dir)[0]
1094 os.path.splitdrive(self.install_dir)[0]
1091 != os.path.splitdrive(self.install_lib)[0]
1095 != os.path.splitdrive(self.install_lib)[0]
1092 ):
1096 ):
1093 # can't make relative paths from one drive to another, so use an
1097 # can't make relative paths from one drive to another, so use an
1094 # absolute path instead
1098 # absolute path instead
1095 libdir = self.install_lib
1099 libdir = self.install_lib
1096 else:
1100 else:
1097 libdir = os.path.relpath(self.install_lib, self.install_dir)
1101 libdir = os.path.relpath(self.install_lib, self.install_dir)
1098
1102
1099 for outfile in self.outfiles:
1103 for outfile in self.outfiles:
1100 with open(outfile, 'rb') as fp:
1104 with open(outfile, 'rb') as fp:
1101 data = fp.read()
1105 data = fp.read()
1102
1106
1103 # skip binary files
1107 # skip binary files
1104 if b'\0' in data:
1108 if b'\0' in data:
1105 continue
1109 continue
1106
1110
1107 # During local installs, the shebang will be rewritten to the final
1111 # During local installs, the shebang will be rewritten to the final
1108 # install path. During wheel packaging, the shebang has a special
1112 # install path. During wheel packaging, the shebang has a special
1109 # value.
1113 # value.
1110 if data.startswith(b'#!python'):
1114 if data.startswith(b'#!python'):
1111 log.info(
1115 log.info(
1112 'not rewriting @LIBDIR@ in %s because install path '
1116 'not rewriting @LIBDIR@ in %s because install path '
1113 'not known' % outfile
1117 'not known' % outfile
1114 )
1118 )
1115 continue
1119 continue
1116
1120
1117 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
1121 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
1118 with open(outfile, 'wb') as fp:
1122 with open(outfile, 'wb') as fp:
1119 fp.write(data)
1123 fp.write(data)
1120
1124
1121
1125
1122 # virtualenv installs custom distutils/__init__.py and
1126 # virtualenv installs custom distutils/__init__.py and
1123 # distutils/distutils.cfg files which essentially proxy back to the
1127 # distutils/distutils.cfg files which essentially proxy back to the
1124 # "real" distutils in the main Python install. The presence of this
1128 # "real" distutils in the main Python install. The presence of this
1125 # directory causes py2exe to pick up the "hacked" distutils package
1129 # directory causes py2exe to pick up the "hacked" distutils package
1126 # from the virtualenv and "import distutils" will fail from the py2exe
1130 # from the virtualenv and "import distutils" will fail from the py2exe
1127 # build because the "real" distutils files can't be located.
1131 # build because the "real" distutils files can't be located.
1128 #
1132 #
1129 # We work around this by monkeypatching the py2exe code finding Python
1133 # We work around this by monkeypatching the py2exe code finding Python
1130 # modules to replace the found virtualenv distutils modules with the
1134 # modules to replace the found virtualenv distutils modules with the
1131 # original versions via filesystem scanning. This is a bit hacky. But
1135 # original versions via filesystem scanning. This is a bit hacky. But
1132 # it allows us to use virtualenvs for py2exe packaging, which is more
1136 # it allows us to use virtualenvs for py2exe packaging, which is more
1133 # deterministic and reproducible.
1137 # deterministic and reproducible.
1134 #
1138 #
1135 # It's worth noting that the common StackOverflow suggestions for this
1139 # It's worth noting that the common StackOverflow suggestions for this
1136 # problem involve copying the original distutils files into the
1140 # problem involve copying the original distutils files into the
1137 # virtualenv or into the staging directory after setup() is invoked.
1141 # virtualenv or into the staging directory after setup() is invoked.
1138 # The former is very brittle and can easily break setup(). Our hacking
1142 # The former is very brittle and can easily break setup(). Our hacking
1139 # of the found modules routine has a similar result as copying the files
1143 # of the found modules routine has a similar result as copying the files
1140 # manually. But it makes fewer assumptions about how py2exe works and
1144 # manually. But it makes fewer assumptions about how py2exe works and
1141 # is less brittle.
1145 # is less brittle.
1142
1146
1143 # This only catches virtualenvs made with virtualenv (as opposed to
1147 # This only catches virtualenvs made with virtualenv (as opposed to
1144 # venv, which is likely what Python 3 uses).
1148 # venv, which is likely what Python 3 uses).
1145 py2exehacked = py2exeloaded and getattr(sys, 'real_prefix', None) is not None
1149 py2exehacked = py2exeloaded and getattr(sys, 'real_prefix', None) is not None
1146
1150
1147 if py2exehacked:
1151 if py2exehacked:
1148 from distutils.command.py2exe import py2exe as buildpy2exe
1152 from distutils.command.py2exe import py2exe as buildpy2exe
1149 from py2exe.mf import Module as py2exemodule
1153 from py2exe.mf import Module as py2exemodule
1150
1154
1151 class hgbuildpy2exe(buildpy2exe):
1155 class hgbuildpy2exe(buildpy2exe):
1152 def find_needed_modules(self, mf, files, modules):
1156 def find_needed_modules(self, mf, files, modules):
1153 res = buildpy2exe.find_needed_modules(self, mf, files, modules)
1157 res = buildpy2exe.find_needed_modules(self, mf, files, modules)
1154
1158
1155 # Replace virtualenv's distutils modules with the real ones.
1159 # Replace virtualenv's distutils modules with the real ones.
1156 modules = {}
1160 modules = {}
1157 for k, v in res.modules.items():
1161 for k, v in res.modules.items():
1158 if k != 'distutils' and not k.startswith('distutils.'):
1162 if k != 'distutils' and not k.startswith('distutils.'):
1159 modules[k] = v
1163 modules[k] = v
1160
1164
1161 res.modules = modules
1165 res.modules = modules
1162
1166
1163 import opcode
1167 import opcode
1164
1168
1165 distutilsreal = os.path.join(
1169 distutilsreal = os.path.join(
1166 os.path.dirname(opcode.__file__), 'distutils'
1170 os.path.dirname(opcode.__file__), 'distutils'
1167 )
1171 )
1168
1172
1169 for root, dirs, files in os.walk(distutilsreal):
1173 for root, dirs, files in os.walk(distutilsreal):
1170 for f in sorted(files):
1174 for f in sorted(files):
1171 if not f.endswith('.py'):
1175 if not f.endswith('.py'):
1172 continue
1176 continue
1173
1177
1174 full = os.path.join(root, f)
1178 full = os.path.join(root, f)
1175
1179
1176 parents = ['distutils']
1180 parents = ['distutils']
1177
1181
1178 if root != distutilsreal:
1182 if root != distutilsreal:
1179 rel = os.path.relpath(root, distutilsreal)
1183 rel = os.path.relpath(root, distutilsreal)
1180 parents.extend(p for p in rel.split(os.sep))
1184 parents.extend(p for p in rel.split(os.sep))
1181
1185
1182 modname = '%s.%s' % ('.'.join(parents), f[:-3])
1186 modname = '%s.%s' % ('.'.join(parents), f[:-3])
1183
1187
1184 if modname.startswith('distutils.tests.'):
1188 if modname.startswith('distutils.tests.'):
1185 continue
1189 continue
1186
1190
1187 if modname.endswith('.__init__'):
1191 if modname.endswith('.__init__'):
1188 modname = modname[: -len('.__init__')]
1192 modname = modname[: -len('.__init__')]
1189 path = os.path.dirname(full)
1193 path = os.path.dirname(full)
1190 else:
1194 else:
1191 path = None
1195 path = None
1192
1196
1193 res.modules[modname] = py2exemodule(
1197 res.modules[modname] = py2exemodule(
1194 modname, full, path=path
1198 modname, full, path=path
1195 )
1199 )
1196
1200
1197 if 'distutils' not in res.modules:
1201 if 'distutils' not in res.modules:
1198 raise SystemExit('could not find distutils modules')
1202 raise SystemExit('could not find distutils modules')
1199
1203
1200 return res
1204 return res
1201
1205
1202
1206
1203 cmdclass = {
1207 cmdclass = {
1204 'build': hgbuild,
1208 'build': hgbuild,
1205 'build_doc': hgbuilddoc,
1209 'build_doc': hgbuilddoc,
1206 'build_mo': hgbuildmo,
1210 'build_mo': hgbuildmo,
1207 'build_ext': hgbuildext,
1211 'build_ext': hgbuildext,
1208 'build_py': hgbuildpy,
1212 'build_py': hgbuildpy,
1209 'build_scripts': hgbuildscripts,
1213 'build_scripts': hgbuildscripts,
1210 'build_hgextindex': buildhgextindex,
1214 'build_hgextindex': buildhgextindex,
1211 'install': hginstall,
1215 'install': hginstall,
1212 'install_lib': hginstalllib,
1216 'install_lib': hginstalllib,
1213 'install_scripts': hginstallscripts,
1217 'install_scripts': hginstallscripts,
1214 'build_hgexe': buildhgexe,
1218 'build_hgexe': buildhgexe,
1215 }
1219 }
1216
1220
1217 if py2exehacked:
1221 if py2exehacked:
1218 cmdclass['py2exe'] = hgbuildpy2exe
1222 cmdclass['py2exe'] = hgbuildpy2exe
1219
1223
1220 packages = [
1224 packages = [
1221 'mercurial',
1225 'mercurial',
1222 'mercurial.cext',
1226 'mercurial.cext',
1223 'mercurial.cffi',
1227 'mercurial.cffi',
1224 'mercurial.defaultrc',
1228 'mercurial.defaultrc',
1225 'mercurial.helptext',
1229 'mercurial.helptext',
1226 'mercurial.helptext.internals',
1230 'mercurial.helptext.internals',
1227 'mercurial.hgweb',
1231 'mercurial.hgweb',
1228 'mercurial.interfaces',
1232 'mercurial.interfaces',
1229 'mercurial.pure',
1233 'mercurial.pure',
1230 'mercurial.thirdparty',
1234 'mercurial.thirdparty',
1231 'mercurial.thirdparty.attr',
1235 'mercurial.thirdparty.attr',
1232 'mercurial.thirdparty.zope',
1236 'mercurial.thirdparty.zope',
1233 'mercurial.thirdparty.zope.interface',
1237 'mercurial.thirdparty.zope.interface',
1234 'mercurial.utils',
1238 'mercurial.utils',
1235 'mercurial.revlogutils',
1239 'mercurial.revlogutils',
1236 'mercurial.testing',
1240 'mercurial.testing',
1237 'hgext',
1241 'hgext',
1238 'hgext.convert',
1242 'hgext.convert',
1239 'hgext.fsmonitor',
1243 'hgext.fsmonitor',
1240 'hgext.fastannotate',
1244 'hgext.fastannotate',
1241 'hgext.fsmonitor.pywatchman',
1245 'hgext.fsmonitor.pywatchman',
1242 'hgext.git',
1246 'hgext.git',
1243 'hgext.highlight',
1247 'hgext.highlight',
1244 'hgext.hooklib',
1248 'hgext.hooklib',
1245 'hgext.infinitepush',
1249 'hgext.infinitepush',
1246 'hgext.largefiles',
1250 'hgext.largefiles',
1247 'hgext.lfs',
1251 'hgext.lfs',
1248 'hgext.narrow',
1252 'hgext.narrow',
1249 'hgext.remotefilelog',
1253 'hgext.remotefilelog',
1250 'hgext.zeroconf',
1254 'hgext.zeroconf',
1251 'hgext3rd',
1255 'hgext3rd',
1252 'hgdemandimport',
1256 'hgdemandimport',
1253 ]
1257 ]
1254 if sys.version_info[0] == 2:
1258 if sys.version_info[0] == 2:
1255 packages.extend(
1259 packages.extend(
1256 [
1260 [
1257 'mercurial.thirdparty.concurrent',
1261 'mercurial.thirdparty.concurrent',
1258 'mercurial.thirdparty.concurrent.futures',
1262 'mercurial.thirdparty.concurrent.futures',
1259 ]
1263 ]
1260 )
1264 )
1261
1265
1262 if 'HG_PY2EXE_EXTRA_INSTALL_PACKAGES' in os.environ:
1266 if 'HG_PY2EXE_EXTRA_INSTALL_PACKAGES' in os.environ:
1263 # py2exe can't cope with namespace packages very well, so we have to
1267 # py2exe can't cope with namespace packages very well, so we have to
1264 # install any hgext3rd.* extensions that we want in the final py2exe
1268 # install any hgext3rd.* extensions that we want in the final py2exe
1265 # image here. This is gross, but you gotta do what you gotta do.
1269 # image here. This is gross, but you gotta do what you gotta do.
1266 packages.extend(os.environ['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'].split(' '))
1270 packages.extend(os.environ['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'].split(' '))
1267
1271
1268 common_depends = [
1272 common_depends = [
1269 'mercurial/bitmanipulation.h',
1273 'mercurial/bitmanipulation.h',
1270 'mercurial/compat.h',
1274 'mercurial/compat.h',
1271 'mercurial/cext/util.h',
1275 'mercurial/cext/util.h',
1272 ]
1276 ]
1273 common_include_dirs = ['mercurial']
1277 common_include_dirs = ['mercurial']
1274
1278
1275 common_cflags = []
1279 common_cflags = []
1276
1280
1277 # MSVC 2008 still needs declarations at the top of the scope, but Python 3.9
1281 # MSVC 2008 still needs declarations at the top of the scope, but Python 3.9
1278 # makes declarations not at the top of a scope in the headers.
1282 # makes declarations not at the top of a scope in the headers.
1279 if os.name != 'nt' and sys.version_info[1] < 9:
1283 if os.name != 'nt' and sys.version_info[1] < 9:
1280 common_cflags = ['-Werror=declaration-after-statement']
1284 common_cflags = ['-Werror=declaration-after-statement']
1281
1285
1282 osutil_cflags = []
1286 osutil_cflags = []
1283 osutil_ldflags = []
1287 osutil_ldflags = []
1284
1288
1285 # platform specific macros
1289 # platform specific macros
1286 for plat, func in [('bsd', 'setproctitle')]:
1290 for plat, func in [('bsd', 'setproctitle')]:
1287 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
1291 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
1288 osutil_cflags.append('-DHAVE_%s' % func.upper())
1292 osutil_cflags.append('-DHAVE_%s' % func.upper())
1289
1293
1290 for plat, macro, code in [
1294 for plat, macro, code in [
1291 (
1295 (
1292 'bsd|darwin',
1296 'bsd|darwin',
1293 'BSD_STATFS',
1297 'BSD_STATFS',
1294 '''
1298 '''
1295 #include <sys/param.h>
1299 #include <sys/param.h>
1296 #include <sys/mount.h>
1300 #include <sys/mount.h>
1297 int main() { struct statfs s; return sizeof(s.f_fstypename); }
1301 int main() { struct statfs s; return sizeof(s.f_fstypename); }
1298 ''',
1302 ''',
1299 ),
1303 ),
1300 (
1304 (
1301 'linux',
1305 'linux',
1302 'LINUX_STATFS',
1306 'LINUX_STATFS',
1303 '''
1307 '''
1304 #include <linux/magic.h>
1308 #include <linux/magic.h>
1305 #include <sys/vfs.h>
1309 #include <sys/vfs.h>
1306 int main() { struct statfs s; return sizeof(s.f_type); }
1310 int main() { struct statfs s; return sizeof(s.f_type); }
1307 ''',
1311 ''',
1308 ),
1312 ),
1309 ]:
1313 ]:
1310 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
1314 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
1311 osutil_cflags.append('-DHAVE_%s' % macro)
1315 osutil_cflags.append('-DHAVE_%s' % macro)
1312
1316
1313 if sys.platform == 'darwin':
1317 if sys.platform == 'darwin':
1314 osutil_ldflags += ['-framework', 'ApplicationServices']
1318 osutil_ldflags += ['-framework', 'ApplicationServices']
1315
1319
1320 if sys.platform == 'sunos5':
1321 osutil_ldflags += ['-lsocket']
1322
1316 xdiff_srcs = [
1323 xdiff_srcs = [
1317 'mercurial/thirdparty/xdiff/xdiffi.c',
1324 'mercurial/thirdparty/xdiff/xdiffi.c',
1318 'mercurial/thirdparty/xdiff/xprepare.c',
1325 'mercurial/thirdparty/xdiff/xprepare.c',
1319 'mercurial/thirdparty/xdiff/xutils.c',
1326 'mercurial/thirdparty/xdiff/xutils.c',
1320 ]
1327 ]
1321
1328
1322 xdiff_headers = [
1329 xdiff_headers = [
1323 'mercurial/thirdparty/xdiff/xdiff.h',
1330 'mercurial/thirdparty/xdiff/xdiff.h',
1324 'mercurial/thirdparty/xdiff/xdiffi.h',
1331 'mercurial/thirdparty/xdiff/xdiffi.h',
1325 'mercurial/thirdparty/xdiff/xinclude.h',
1332 'mercurial/thirdparty/xdiff/xinclude.h',
1326 'mercurial/thirdparty/xdiff/xmacros.h',
1333 'mercurial/thirdparty/xdiff/xmacros.h',
1327 'mercurial/thirdparty/xdiff/xprepare.h',
1334 'mercurial/thirdparty/xdiff/xprepare.h',
1328 'mercurial/thirdparty/xdiff/xtypes.h',
1335 'mercurial/thirdparty/xdiff/xtypes.h',
1329 'mercurial/thirdparty/xdiff/xutils.h',
1336 'mercurial/thirdparty/xdiff/xutils.h',
1330 ]
1337 ]
1331
1338
1332
1339
1333 class RustCompilationError(CCompilerError):
1340 class RustCompilationError(CCompilerError):
1334 """Exception class for Rust compilation errors."""
1341 """Exception class for Rust compilation errors."""
1335
1342
1336
1343
1337 class RustExtension(Extension):
1344 class RustExtension(Extension):
1338 """Base classes for concrete Rust Extension classes.
1345 """Base classes for concrete Rust Extension classes.
1339 """
1346 """
1340
1347
1341 rusttargetdir = os.path.join('rust', 'target', 'release')
1348 rusttargetdir = os.path.join('rust', 'target', 'release')
1342
1349
1343 def __init__(
1350 def __init__(
1344 self, mpath, sources, rustlibname, subcrate, py3_features=None, **kw
1351 self, mpath, sources, rustlibname, subcrate, py3_features=None, **kw
1345 ):
1352 ):
1346 Extension.__init__(self, mpath, sources, **kw)
1353 Extension.__init__(self, mpath, sources, **kw)
1347 srcdir = self.rustsrcdir = os.path.join('rust', subcrate)
1354 srcdir = self.rustsrcdir = os.path.join('rust', subcrate)
1348 self.py3_features = py3_features
1355 self.py3_features = py3_features
1349
1356
1350 # adding Rust source and control files to depends so that the extension
1357 # adding Rust source and control files to depends so that the extension
1351 # gets rebuilt if they've changed
1358 # gets rebuilt if they've changed
1352 self.depends.append(os.path.join(srcdir, 'Cargo.toml'))
1359 self.depends.append(os.path.join(srcdir, 'Cargo.toml'))
1353 cargo_lock = os.path.join(srcdir, 'Cargo.lock')
1360 cargo_lock = os.path.join(srcdir, 'Cargo.lock')
1354 if os.path.exists(cargo_lock):
1361 if os.path.exists(cargo_lock):
1355 self.depends.append(cargo_lock)
1362 self.depends.append(cargo_lock)
1356 for dirpath, subdir, fnames in os.walk(os.path.join(srcdir, 'src')):
1363 for dirpath, subdir, fnames in os.walk(os.path.join(srcdir, 'src')):
1357 self.depends.extend(
1364 self.depends.extend(
1358 os.path.join(dirpath, fname)
1365 os.path.join(dirpath, fname)
1359 for fname in fnames
1366 for fname in fnames
1360 if os.path.splitext(fname)[1] == '.rs'
1367 if os.path.splitext(fname)[1] == '.rs'
1361 )
1368 )
1362
1369
1363 @staticmethod
1370 @staticmethod
1364 def rustdylibsuffix():
1371 def rustdylibsuffix():
1365 """Return the suffix for shared libraries produced by rustc.
1372 """Return the suffix for shared libraries produced by rustc.
1366
1373
1367 See also: https://doc.rust-lang.org/reference/linkage.html
1374 See also: https://doc.rust-lang.org/reference/linkage.html
1368 """
1375 """
1369 if sys.platform == 'darwin':
1376 if sys.platform == 'darwin':
1370 return '.dylib'
1377 return '.dylib'
1371 elif os.name == 'nt':
1378 elif os.name == 'nt':
1372 return '.dll'
1379 return '.dll'
1373 else:
1380 else:
1374 return '.so'
1381 return '.so'
1375
1382
1376 def rustbuild(self):
1383 def rustbuild(self):
1377 env = os.environ.copy()
1384 env = os.environ.copy()
1378 if 'HGTEST_RESTOREENV' in env:
1385 if 'HGTEST_RESTOREENV' in env:
1379 # Mercurial tests change HOME to a temporary directory,
1386 # Mercurial tests change HOME to a temporary directory,
1380 # but, if installed with rustup, the Rust toolchain needs
1387 # but, if installed with rustup, the Rust toolchain needs
1381 # HOME to be correct (otherwise the 'no default toolchain'
1388 # HOME to be correct (otherwise the 'no default toolchain'
1382 # error message is issued and the build fails).
1389 # error message is issued and the build fails).
1383 # This happens currently with test-hghave.t, which does
1390 # This happens currently with test-hghave.t, which does
1384 # invoke this build.
1391 # invoke this build.
1385
1392
1386 # Unix only fix (os.path.expanduser not really reliable if
1393 # Unix only fix (os.path.expanduser not really reliable if
1387 # HOME is shadowed like this)
1394 # HOME is shadowed like this)
1388 import pwd
1395 import pwd
1389
1396
1390 env['HOME'] = pwd.getpwuid(os.getuid()).pw_dir
1397 env['HOME'] = pwd.getpwuid(os.getuid()).pw_dir
1391
1398
1392 cargocmd = ['cargo', 'rustc', '-vv', '--release']
1399 cargocmd = ['cargo', 'rustc', '-vv', '--release']
1393
1400
1394 feature_flags = []
1401 feature_flags = []
1395
1402
1396 if sys.version_info[0] == 3 and self.py3_features is not None:
1403 if sys.version_info[0] == 3 and self.py3_features is not None:
1397 feature_flags.append(self.py3_features)
1404 feature_flags.append(self.py3_features)
1398 cargocmd.append('--no-default-features')
1405 cargocmd.append('--no-default-features')
1399
1406
1400 rust_features = env.get("HG_RUST_FEATURES")
1407 rust_features = env.get("HG_RUST_FEATURES")
1401 if rust_features:
1408 if rust_features:
1402 feature_flags.append(rust_features)
1409 feature_flags.append(rust_features)
1403
1410
1404 cargocmd.extend(('--features', " ".join(feature_flags)))
1411 cargocmd.extend(('--features', " ".join(feature_flags)))
1405
1412
1406 cargocmd.append('--')
1413 cargocmd.append('--')
1407 if sys.platform == 'darwin':
1414 if sys.platform == 'darwin':
1408 cargocmd.extend(
1415 cargocmd.extend(
1409 ("-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup")
1416 ("-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup")
1410 )
1417 )
1411 try:
1418 try:
1412 subprocess.check_call(cargocmd, env=env, cwd=self.rustsrcdir)
1419 subprocess.check_call(cargocmd, env=env, cwd=self.rustsrcdir)
1413 except OSError as exc:
1420 except OSError as exc:
1414 if exc.errno == errno.ENOENT:
1421 if exc.errno == errno.ENOENT:
1415 raise RustCompilationError("Cargo not found")
1422 raise RustCompilationError("Cargo not found")
1416 elif exc.errno == errno.EACCES:
1423 elif exc.errno == errno.EACCES:
1417 raise RustCompilationError(
1424 raise RustCompilationError(
1418 "Cargo found, but permisssion to execute it is denied"
1425 "Cargo found, but permisssion to execute it is denied"
1419 )
1426 )
1420 else:
1427 else:
1421 raise
1428 raise
1422 except subprocess.CalledProcessError:
1429 except subprocess.CalledProcessError:
1423 raise RustCompilationError(
1430 raise RustCompilationError(
1424 "Cargo failed. Working directory: %r, "
1431 "Cargo failed. Working directory: %r, "
1425 "command: %r, environment: %r"
1432 "command: %r, environment: %r"
1426 % (self.rustsrcdir, cargocmd, env)
1433 % (self.rustsrcdir, cargocmd, env)
1427 )
1434 )
1428
1435
1429
1436
1430 class RustStandaloneExtension(RustExtension):
1437 class RustStandaloneExtension(RustExtension):
1431 def __init__(self, pydottedname, rustcrate, dylibname, **kw):
1438 def __init__(self, pydottedname, rustcrate, dylibname, **kw):
1432 RustExtension.__init__(
1439 RustExtension.__init__(
1433 self, pydottedname, [], dylibname, rustcrate, **kw
1440 self, pydottedname, [], dylibname, rustcrate, **kw
1434 )
1441 )
1435 self.dylibname = dylibname
1442 self.dylibname = dylibname
1436
1443
1437 def build(self, target_dir):
1444 def build(self, target_dir):
1438 self.rustbuild()
1445 self.rustbuild()
1439 target = [target_dir]
1446 target = [target_dir]
1440 target.extend(self.name.split('.'))
1447 target.extend(self.name.split('.'))
1441 target[-1] += DYLIB_SUFFIX
1448 target[-1] += DYLIB_SUFFIX
1442 shutil.copy2(
1449 shutil.copy2(
1443 os.path.join(
1450 os.path.join(
1444 self.rusttargetdir, self.dylibname + self.rustdylibsuffix()
1451 self.rusttargetdir, self.dylibname + self.rustdylibsuffix()
1445 ),
1452 ),
1446 os.path.join(*target),
1453 os.path.join(*target),
1447 )
1454 )
1448
1455
1449
1456
1450 extmodules = [
1457 extmodules = [
1451 Extension(
1458 Extension(
1452 'mercurial.cext.base85',
1459 'mercurial.cext.base85',
1453 ['mercurial/cext/base85.c'],
1460 ['mercurial/cext/base85.c'],
1454 include_dirs=common_include_dirs,
1461 include_dirs=common_include_dirs,
1455 extra_compile_args=common_cflags,
1462 extra_compile_args=common_cflags,
1456 depends=common_depends,
1463 depends=common_depends,
1457 ),
1464 ),
1458 Extension(
1465 Extension(
1459 'mercurial.cext.bdiff',
1466 'mercurial.cext.bdiff',
1460 ['mercurial/bdiff.c', 'mercurial/cext/bdiff.c'] + xdiff_srcs,
1467 ['mercurial/bdiff.c', 'mercurial/cext/bdiff.c'] + xdiff_srcs,
1461 include_dirs=common_include_dirs,
1468 include_dirs=common_include_dirs,
1462 extra_compile_args=common_cflags,
1469 extra_compile_args=common_cflags,
1463 depends=common_depends + ['mercurial/bdiff.h'] + xdiff_headers,
1470 depends=common_depends + ['mercurial/bdiff.h'] + xdiff_headers,
1464 ),
1471 ),
1465 Extension(
1472 Extension(
1466 'mercurial.cext.mpatch',
1473 'mercurial.cext.mpatch',
1467 ['mercurial/mpatch.c', 'mercurial/cext/mpatch.c'],
1474 ['mercurial/mpatch.c', 'mercurial/cext/mpatch.c'],
1468 include_dirs=common_include_dirs,
1475 include_dirs=common_include_dirs,
1469 extra_compile_args=common_cflags,
1476 extra_compile_args=common_cflags,
1470 depends=common_depends,
1477 depends=common_depends,
1471 ),
1478 ),
1472 Extension(
1479 Extension(
1473 'mercurial.cext.parsers',
1480 'mercurial.cext.parsers',
1474 [
1481 [
1475 'mercurial/cext/charencode.c',
1482 'mercurial/cext/charencode.c',
1476 'mercurial/cext/dirs.c',
1483 'mercurial/cext/dirs.c',
1477 'mercurial/cext/manifest.c',
1484 'mercurial/cext/manifest.c',
1478 'mercurial/cext/parsers.c',
1485 'mercurial/cext/parsers.c',
1479 'mercurial/cext/pathencode.c',
1486 'mercurial/cext/pathencode.c',
1480 'mercurial/cext/revlog.c',
1487 'mercurial/cext/revlog.c',
1481 ],
1488 ],
1482 include_dirs=common_include_dirs,
1489 include_dirs=common_include_dirs,
1483 extra_compile_args=common_cflags,
1490 extra_compile_args=common_cflags,
1484 depends=common_depends
1491 depends=common_depends
1485 + ['mercurial/cext/charencode.h', 'mercurial/cext/revlog.h',],
1492 + ['mercurial/cext/charencode.h', 'mercurial/cext/revlog.h',],
1486 ),
1493 ),
1487 Extension(
1494 Extension(
1488 'mercurial.cext.osutil',
1495 'mercurial.cext.osutil',
1489 ['mercurial/cext/osutil.c'],
1496 ['mercurial/cext/osutil.c'],
1490 include_dirs=common_include_dirs,
1497 include_dirs=common_include_dirs,
1491 extra_compile_args=common_cflags + osutil_cflags,
1498 extra_compile_args=common_cflags + osutil_cflags,
1492 extra_link_args=osutil_ldflags,
1499 extra_link_args=osutil_ldflags,
1493 depends=common_depends,
1500 depends=common_depends,
1494 ),
1501 ),
1495 Extension(
1502 Extension(
1496 'mercurial.thirdparty.zope.interface._zope_interface_coptimizations',
1503 'mercurial.thirdparty.zope.interface._zope_interface_coptimizations',
1497 [
1504 [
1498 'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c',
1505 'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c',
1499 ],
1506 ],
1500 extra_compile_args=common_cflags,
1507 extra_compile_args=common_cflags,
1501 ),
1508 ),
1502 Extension(
1509 Extension(
1503 'mercurial.thirdparty.sha1dc',
1510 'mercurial.thirdparty.sha1dc',
1504 [
1511 [
1505 'mercurial/thirdparty/sha1dc/cext.c',
1512 'mercurial/thirdparty/sha1dc/cext.c',
1506 'mercurial/thirdparty/sha1dc/lib/sha1.c',
1513 'mercurial/thirdparty/sha1dc/lib/sha1.c',
1507 'mercurial/thirdparty/sha1dc/lib/ubc_check.c',
1514 'mercurial/thirdparty/sha1dc/lib/ubc_check.c',
1508 ],
1515 ],
1509 extra_compile_args=common_cflags,
1516 extra_compile_args=common_cflags,
1510 ),
1517 ),
1511 Extension(
1518 Extension(
1512 'hgext.fsmonitor.pywatchman.bser',
1519 'hgext.fsmonitor.pywatchman.bser',
1513 ['hgext/fsmonitor/pywatchman/bser.c'],
1520 ['hgext/fsmonitor/pywatchman/bser.c'],
1514 extra_compile_args=common_cflags,
1521 extra_compile_args=common_cflags,
1515 ),
1522 ),
1516 RustStandaloneExtension(
1523 RustStandaloneExtension(
1517 'mercurial.rustext', 'hg-cpython', 'librusthg', py3_features='python3'
1524 'mercurial.rustext', 'hg-cpython', 'librusthg', py3_features='python3'
1518 ),
1525 ),
1519 ]
1526 ]
1520
1527
1521
1528
1522 sys.path.insert(0, 'contrib/python-zstandard')
1529 sys.path.insert(0, 'contrib/python-zstandard')
1523 import setup_zstd
1530 import setup_zstd
1524
1531
1525 zstd = setup_zstd.get_c_extension(
1532 zstd = setup_zstd.get_c_extension(
1526 name='mercurial.zstd', root=os.path.abspath(os.path.dirname(__file__))
1533 name='mercurial.zstd', root=os.path.abspath(os.path.dirname(__file__))
1527 )
1534 )
1528 zstd.extra_compile_args += common_cflags
1535 zstd.extra_compile_args += common_cflags
1529 extmodules.append(zstd)
1536 extmodules.append(zstd)
1530
1537
1531 try:
1538 try:
1532 from distutils import cygwinccompiler
1539 from distutils import cygwinccompiler
1533
1540
1534 # the -mno-cygwin option has been deprecated for years
1541 # the -mno-cygwin option has been deprecated for years
1535 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
1542 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
1536
1543
1537 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
1544 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
1538 def __init__(self, *args, **kwargs):
1545 def __init__(self, *args, **kwargs):
1539 mingw32compilerclass.__init__(self, *args, **kwargs)
1546 mingw32compilerclass.__init__(self, *args, **kwargs)
1540 for i in 'compiler compiler_so linker_exe linker_so'.split():
1547 for i in 'compiler compiler_so linker_exe linker_so'.split():
1541 try:
1548 try:
1542 getattr(self, i).remove('-mno-cygwin')
1549 getattr(self, i).remove('-mno-cygwin')
1543 except ValueError:
1550 except ValueError:
1544 pass
1551 pass
1545
1552
1546 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
1553 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
1547 except ImportError:
1554 except ImportError:
1548 # the cygwinccompiler package is not available on some Python
1555 # the cygwinccompiler package is not available on some Python
1549 # distributions like the ones from the optware project for Synology
1556 # distributions like the ones from the optware project for Synology
1550 # DiskStation boxes
1557 # DiskStation boxes
1551 class HackedMingw32CCompiler(object):
1558 class HackedMingw32CCompiler(object):
1552 pass
1559 pass
1553
1560
1554
1561
1555 if os.name == 'nt':
1562 if os.name == 'nt':
1556 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
1563 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
1557 # extra_link_args to distutils.extensions.Extension() doesn't have any
1564 # extra_link_args to distutils.extensions.Extension() doesn't have any
1558 # effect.
1565 # effect.
1559 from distutils import msvccompiler
1566 from distutils import msvccompiler
1560
1567
1561 msvccompilerclass = msvccompiler.MSVCCompiler
1568 msvccompilerclass = msvccompiler.MSVCCompiler
1562
1569
1563 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
1570 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
1564 def initialize(self):
1571 def initialize(self):
1565 msvccompilerclass.initialize(self)
1572 msvccompilerclass.initialize(self)
1566 # "warning LNK4197: export 'func' specified multiple times"
1573 # "warning LNK4197: export 'func' specified multiple times"
1567 self.ldflags_shared.append('/ignore:4197')
1574 self.ldflags_shared.append('/ignore:4197')
1568 self.ldflags_shared_debug.append('/ignore:4197')
1575 self.ldflags_shared_debug.append('/ignore:4197')
1569
1576
1570 msvccompiler.MSVCCompiler = HackedMSVCCompiler
1577 msvccompiler.MSVCCompiler = HackedMSVCCompiler
1571
1578
1572 packagedata = {
1579 packagedata = {
1573 'mercurial': [
1580 'mercurial': [
1574 'locale/*/LC_MESSAGES/hg.mo',
1581 'locale/*/LC_MESSAGES/hg.mo',
1575 'defaultrc/*.rc',
1582 'defaultrc/*.rc',
1576 'dummycert.pem',
1583 'dummycert.pem',
1577 ],
1584 ],
1578 'mercurial.helptext': ['*.txt',],
1585 'mercurial.helptext': ['*.txt',],
1579 'mercurial.helptext.internals': ['*.txt',],
1586 'mercurial.helptext.internals': ['*.txt',],
1580 }
1587 }
1581
1588
1582
1589
1583 def ordinarypath(p):
1590 def ordinarypath(p):
1584 return p and p[0] != '.' and p[-1] != '~'
1591 return p and p[0] != '.' and p[-1] != '~'
1585
1592
1586
1593
1587 for root in ('templates',):
1594 for root in ('templates',):
1588 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
1595 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
1589 curdir = curdir.split(os.sep, 1)[1]
1596 curdir = curdir.split(os.sep, 1)[1]
1590 dirs[:] = filter(ordinarypath, dirs)
1597 dirs[:] = filter(ordinarypath, dirs)
1591 for f in filter(ordinarypath, files):
1598 for f in filter(ordinarypath, files):
1592 f = os.path.join(curdir, f)
1599 f = os.path.join(curdir, f)
1593 packagedata['mercurial'].append(f)
1600 packagedata['mercurial'].append(f)
1594
1601
1595 datafiles = []
1602 datafiles = []
1596
1603
1597 # distutils expects version to be str/unicode. Converting it to
1604 # distutils expects version to be str/unicode. Converting it to
1598 # unicode on Python 2 still works because it won't contain any
1605 # unicode on Python 2 still works because it won't contain any
1599 # non-ascii bytes and will be implicitly converted back to bytes
1606 # non-ascii bytes and will be implicitly converted back to bytes
1600 # when operated on.
1607 # when operated on.
1601 assert isinstance(version, bytes)
1608 assert isinstance(version, bytes)
1602 setupversion = version.decode('ascii')
1609 setupversion = version.decode('ascii')
1603
1610
1604 extra = {}
1611 extra = {}
1605
1612
1606 py2exepackages = [
1613 py2exepackages = [
1607 'hgdemandimport',
1614 'hgdemandimport',
1608 'hgext3rd',
1615 'hgext3rd',
1609 'hgext',
1616 'hgext',
1610 'email',
1617 'email',
1611 # implicitly imported per module policy
1618 # implicitly imported per module policy
1612 # (cffi wouldn't be used as a frozen exe)
1619 # (cffi wouldn't be used as a frozen exe)
1613 'mercurial.cext',
1620 'mercurial.cext',
1614 #'mercurial.cffi',
1621 #'mercurial.cffi',
1615 'mercurial.pure',
1622 'mercurial.pure',
1616 ]
1623 ]
1617
1624
1618 py2exeexcludes = []
1625 py2exeexcludes = []
1619 py2exedllexcludes = ['crypt32.dll']
1626 py2exedllexcludes = ['crypt32.dll']
1620
1627
1621 if issetuptools:
1628 if issetuptools:
1622 extra['python_requires'] = supportedpy
1629 extra['python_requires'] = supportedpy
1623
1630
1624 if py2exeloaded:
1631 if py2exeloaded:
1625 extra['console'] = [
1632 extra['console'] = [
1626 {
1633 {
1627 'script': 'hg',
1634 'script': 'hg',
1628 'copyright': 'Copyright (C) 2005-2020 Matt Mackall and others',
1635 'copyright': 'Copyright (C) 2005-2020 Matt Mackall and others',
1629 'product_version': version,
1636 'product_version': version,
1630 }
1637 }
1631 ]
1638 ]
1632 # Sub command of 'build' because 'py2exe' does not handle sub_commands.
1639 # Sub command of 'build' because 'py2exe' does not handle sub_commands.
1633 # Need to override hgbuild because it has a private copy of
1640 # Need to override hgbuild because it has a private copy of
1634 # build.sub_commands.
1641 # build.sub_commands.
1635 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1642 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1636 # put dlls in sub directory so that they won't pollute PATH
1643 # put dlls in sub directory so that they won't pollute PATH
1637 extra['zipfile'] = 'lib/library.zip'
1644 extra['zipfile'] = 'lib/library.zip'
1638
1645
1639 # We allow some configuration to be supplemented via environment
1646 # We allow some configuration to be supplemented via environment
1640 # variables. This is better than setup.cfg files because it allows
1647 # variables. This is better than setup.cfg files because it allows
1641 # supplementing configs instead of replacing them.
1648 # supplementing configs instead of replacing them.
1642 extrapackages = os.environ.get('HG_PY2EXE_EXTRA_PACKAGES')
1649 extrapackages = os.environ.get('HG_PY2EXE_EXTRA_PACKAGES')
1643 if extrapackages:
1650 if extrapackages:
1644 py2exepackages.extend(extrapackages.split(' '))
1651 py2exepackages.extend(extrapackages.split(' '))
1645
1652
1646 excludes = os.environ.get('HG_PY2EXE_EXTRA_EXCLUDES')
1653 excludes = os.environ.get('HG_PY2EXE_EXTRA_EXCLUDES')
1647 if excludes:
1654 if excludes:
1648 py2exeexcludes.extend(excludes.split(' '))
1655 py2exeexcludes.extend(excludes.split(' '))
1649
1656
1650 dllexcludes = os.environ.get('HG_PY2EXE_EXTRA_DLL_EXCLUDES')
1657 dllexcludes = os.environ.get('HG_PY2EXE_EXTRA_DLL_EXCLUDES')
1651 if dllexcludes:
1658 if dllexcludes:
1652 py2exedllexcludes.extend(dllexcludes.split(' '))
1659 py2exedllexcludes.extend(dllexcludes.split(' '))
1653
1660
1654 if os.name == 'nt':
1661 if os.name == 'nt':
1655 # Windows binary file versions for exe/dll files must have the
1662 # Windows binary file versions for exe/dll files must have the
1656 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
1663 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
1657 setupversion = setupversion.split(r'+', 1)[0]
1664 setupversion = setupversion.split(r'+', 1)[0]
1658
1665
1659 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
1666 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
1660 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
1667 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
1661 if version:
1668 if version:
1662 version = version[0]
1669 version = version[0]
1663 if sys.version_info[0] == 3:
1670 if sys.version_info[0] == 3:
1664 version = version.decode('utf-8')
1671 version = version.decode('utf-8')
1665 xcode4 = version.startswith('Xcode') and StrictVersion(
1672 xcode4 = version.startswith('Xcode') and StrictVersion(
1666 version.split()[1]
1673 version.split()[1]
1667 ) >= StrictVersion('4.0')
1674 ) >= StrictVersion('4.0')
1668 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
1675 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
1669 else:
1676 else:
1670 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
1677 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
1671 # installed, but instead with only command-line tools. Assume
1678 # installed, but instead with only command-line tools. Assume
1672 # that only happens on >= Lion, thus no PPC support.
1679 # that only happens on >= Lion, thus no PPC support.
1673 xcode4 = True
1680 xcode4 = True
1674 xcode51 = False
1681 xcode51 = False
1675
1682
1676 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
1683 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
1677 # distutils.sysconfig
1684 # distutils.sysconfig
1678 if xcode4:
1685 if xcode4:
1679 os.environ['ARCHFLAGS'] = ''
1686 os.environ['ARCHFLAGS'] = ''
1680
1687
1681 # XCode 5.1 changes clang such that it now fails to compile if the
1688 # XCode 5.1 changes clang such that it now fails to compile if the
1682 # -mno-fused-madd flag is passed, but the version of Python shipped with
1689 # -mno-fused-madd flag is passed, but the version of Python shipped with
1683 # OS X 10.9 Mavericks includes this flag. This causes problems in all
1690 # OS X 10.9 Mavericks includes this flag. This causes problems in all
1684 # C extension modules, and a bug has been filed upstream at
1691 # C extension modules, and a bug has been filed upstream at
1685 # http://bugs.python.org/issue21244. We also need to patch this here
1692 # http://bugs.python.org/issue21244. We also need to patch this here
1686 # so Mercurial can continue to compile in the meantime.
1693 # so Mercurial can continue to compile in the meantime.
1687 if xcode51:
1694 if xcode51:
1688 cflags = get_config_var('CFLAGS')
1695 cflags = get_config_var('CFLAGS')
1689 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
1696 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
1690 os.environ['CFLAGS'] = (
1697 os.environ['CFLAGS'] = (
1691 os.environ.get('CFLAGS', '') + ' -Qunused-arguments'
1698 os.environ.get('CFLAGS', '') + ' -Qunused-arguments'
1692 )
1699 )
1693
1700
1694 setup(
1701 setup(
1695 name='mercurial',
1702 name='mercurial',
1696 version=setupversion,
1703 version=setupversion,
1697 author='Matt Mackall and many others',
1704 author='Matt Mackall and many others',
1698 author_email='mercurial@mercurial-scm.org',
1705 author_email='mercurial@mercurial-scm.org',
1699 url='https://mercurial-scm.org/',
1706 url='https://mercurial-scm.org/',
1700 download_url='https://mercurial-scm.org/release/',
1707 download_url='https://mercurial-scm.org/release/',
1701 description=(
1708 description=(
1702 'Fast scalable distributed SCM (revision control, version '
1709 'Fast scalable distributed SCM (revision control, version '
1703 'control) system'
1710 'control) system'
1704 ),
1711 ),
1705 long_description=(
1712 long_description=(
1706 'Mercurial is a distributed SCM tool written in Python.'
1713 'Mercurial is a distributed SCM tool written in Python.'
1707 ' It is used by a number of large projects that require'
1714 ' It is used by a number of large projects that require'
1708 ' fast, reliable distributed revision control, such as '
1715 ' fast, reliable distributed revision control, such as '
1709 'Mozilla.'
1716 'Mozilla.'
1710 ),
1717 ),
1711 license='GNU GPLv2 or any later version',
1718 license='GNU GPLv2 or any later version',
1712 classifiers=[
1719 classifiers=[
1713 'Development Status :: 6 - Mature',
1720 'Development Status :: 6 - Mature',
1714 'Environment :: Console',
1721 'Environment :: Console',
1715 'Intended Audience :: Developers',
1722 'Intended Audience :: Developers',
1716 'Intended Audience :: System Administrators',
1723 'Intended Audience :: System Administrators',
1717 'License :: OSI Approved :: GNU General Public License (GPL)',
1724 'License :: OSI Approved :: GNU General Public License (GPL)',
1718 'Natural Language :: Danish',
1725 'Natural Language :: Danish',
1719 'Natural Language :: English',
1726 'Natural Language :: English',
1720 'Natural Language :: German',
1727 'Natural Language :: German',
1721 'Natural Language :: Italian',
1728 'Natural Language :: Italian',
1722 'Natural Language :: Japanese',
1729 'Natural Language :: Japanese',
1723 'Natural Language :: Portuguese (Brazilian)',
1730 'Natural Language :: Portuguese (Brazilian)',
1724 'Operating System :: Microsoft :: Windows',
1731 'Operating System :: Microsoft :: Windows',
1725 'Operating System :: OS Independent',
1732 'Operating System :: OS Independent',
1726 'Operating System :: POSIX',
1733 'Operating System :: POSIX',
1727 'Programming Language :: C',
1734 'Programming Language :: C',
1728 'Programming Language :: Python',
1735 'Programming Language :: Python',
1729 'Topic :: Software Development :: Version Control',
1736 'Topic :: Software Development :: Version Control',
1730 ],
1737 ],
1731 scripts=scripts,
1738 scripts=scripts,
1732 packages=packages,
1739 packages=packages,
1733 ext_modules=extmodules,
1740 ext_modules=extmodules,
1734 data_files=datafiles,
1741 data_files=datafiles,
1735 package_data=packagedata,
1742 package_data=packagedata,
1736 cmdclass=cmdclass,
1743 cmdclass=cmdclass,
1737 distclass=hgdist,
1744 distclass=hgdist,
1738 options={
1745 options={
1739 'py2exe': {
1746 'py2exe': {
1740 'bundle_files': 3,
1747 'bundle_files': 3,
1741 'dll_excludes': py2exedllexcludes,
1748 'dll_excludes': py2exedllexcludes,
1742 'excludes': py2exeexcludes,
1749 'excludes': py2exeexcludes,
1743 'packages': py2exepackages,
1750 'packages': py2exepackages,
1744 },
1751 },
1745 'bdist_mpkg': {
1752 'bdist_mpkg': {
1746 'zipdist': False,
1753 'zipdist': False,
1747 'license': 'COPYING',
1754 'license': 'COPYING',
1748 'readme': 'contrib/packaging/macosx/Readme.html',
1755 'readme': 'contrib/packaging/macosx/Readme.html',
1749 'welcome': 'contrib/packaging/macosx/Welcome.html',
1756 'welcome': 'contrib/packaging/macosx/Welcome.html',
1750 },
1757 },
1751 },
1758 },
1752 **extra
1759 **extra
1753 )
1760 )
@@ -1,227 +1,227 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > convert=
3 > convert=
4 > [convert]
4 > [convert]
5 > hg.saverev=False
5 > hg.saverev=False
6 > EOF
6 > EOF
7 $ hg init orig
7 $ hg init orig
8 $ cd orig
8 $ cd orig
9 $ echo foo > foo
9 $ echo foo > foo
10 $ echo bar > bar
10 $ echo bar > bar
11 $ hg ci -qAm 'add foo bar' -d '0 0'
11 $ hg ci -qAm 'add foo bar' -d '0 0'
12 $ echo >> foo
12 $ echo >> foo
13 $ hg ci -m 'change foo' -d '1 0'
13 $ hg ci -m 'change foo' -d '1 0'
14 $ hg up -qC 0
14 $ hg up -qC 0
15 $ hg copy --after --force foo bar
15 $ hg copy --after --force foo bar
16 $ hg copy foo baz
16 $ hg copy foo baz
17 $ hg ci -m 'make bar and baz copies of foo' -d '2 0'
17 $ hg ci -m 'make bar and baz copies of foo' -d '2 0'
18 created new head
18 created new head
19
19
20 Test that template can print all file copies (issue4362)
20 Test that template can print all file copies (issue4362)
21 $ hg log -r . --template "{file_copies % ' File: {file_copy}\n'}"
21 $ hg log -r . --template "{file_copies % ' File: {file_copy}\n'}"
22 File: bar (foo)
22 File: bar (foo)
23 File: baz (foo)
23 File: baz (foo)
24
24
25 $ hg bookmark premerge1
25 $ hg bookmark premerge1
26 $ hg merge -r 1
26 $ hg merge -r 1
27 merging baz and foo to baz
27 merging baz and foo to baz
28 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
28 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
29 (branch merge, don't forget to commit)
29 (branch merge, don't forget to commit)
30 $ hg ci -m 'merge local copy' -d '3 0'
30 $ hg ci -m 'merge local copy' -d '3 0'
31 $ hg up -C 1
31 $ hg up -C 1
32 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
32 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
33 (leaving bookmark premerge1)
33 (leaving bookmark premerge1)
34 $ hg bookmark premerge2
34 $ hg bookmark premerge2
35 $ hg merge 2
35 $ hg merge 2
36 merging foo and baz to baz
36 merging foo and baz to baz
37 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
37 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
38 (branch merge, don't forget to commit)
38 (branch merge, don't forget to commit)
39 $ hg ci -m 'merge remote copy' -d '4 0'
39 $ hg ci -m 'merge remote copy' -d '4 0'
40 created new head
40 created new head
41
41
42 Make and delete some tags
42 Make and delete some tags
43
43
44 $ hg tag that
44 $ hg tag that
45 $ hg tag --remove that
45 $ hg tag --remove that
46 $ hg tag this
46 $ hg tag this
47
47
48 #if execbit
48 #if execbit
49 $ chmod +x baz
49 $ chmod +x baz
50 #else
50 #else
51 $ echo some other change to make sure we get a rev 5 > baz
51 $ echo some other change to make sure we get a rev 5 > baz
52 #endif
52 #endif
53 $ hg ci -m 'mark baz executable' -d '5 0'
53 $ hg ci -m 'mark baz executable' -d '5 0'
54 $ cd ..
54 $ cd ..
55 $ hg convert --datesort orig new 2>&1 | grep -v 'subversion python bindings could not be loaded'
55 $ hg convert --datesort orig new 2>&1 | grep -v 'subversion python bindings could not be loaded'
56 initializing destination new repository
56 initializing destination new repository
57 scanning source...
57 scanning source...
58 sorting...
58 sorting...
59 converting...
59 converting...
60 8 add foo bar
60 8 add foo bar
61 7 change foo
61 7 change foo
62 6 make bar and baz copies of foo
62 6 make bar and baz copies of foo
63 5 merge local copy
63 5 merge local copy
64 4 merge remote copy
64 4 merge remote copy
65 3 Added tag that for changeset 8601262d7472
65 3 Added tag that for changeset 8601262d7472
66 2 Removed tag that
66 2 Removed tag that
67 1 Added tag this for changeset 706614b458c1
67 1 Added tag this for changeset 706614b458c1
68 0 mark baz executable
68 0 mark baz executable
69 updating bookmarks
69 updating bookmarks
70 $ cd new
70 $ cd new
71 $ hg out ../orig
71 $ hg out ../orig
72 comparing with ../orig
72 comparing with ../orig
73 searching for changes
73 searching for changes
74 no changes found
74 no changes found
75 [1]
75 [1]
76 #if execbit
76 #if execbit
77 $ hg bookmarks
77 $ hg bookmarks
78 premerge1 3:973ef48a98a4
78 premerge1 3:973ef48a98a4
79 premerge2 8:c4968fdf2e5d
79 premerge2 8:c4968fdf2e5d
80 #else
80 #else
81 Different hash because no x bit
81 Different hash because no x bit
82 $ hg bookmarks
82 $ hg bookmarks
83 premerge1 3:973ef48a98a4
83 premerge1 3:973ef48a98a4
84 premerge2 8:3537b15eaaca
84 premerge2 8:1cc21e701444
85 #endif
85 #endif
86
86
87 Test that redoing a convert results in an identical graph
87 Test that redoing a convert results in an identical graph
88 $ cd ../
88 $ cd ../
89 $ rm new/.hg/shamap
89 $ rm new/.hg/shamap
90 $ hg convert --datesort orig new 2>&1 | grep -v 'subversion python bindings could not be loaded'
90 $ hg convert --datesort orig new 2>&1 | grep -v 'subversion python bindings could not be loaded'
91 scanning source...
91 scanning source...
92 sorting...
92 sorting...
93 converting...
93 converting...
94 8 add foo bar
94 8 add foo bar
95 7 change foo
95 7 change foo
96 6 make bar and baz copies of foo
96 6 make bar and baz copies of foo
97 5 merge local copy
97 5 merge local copy
98 4 merge remote copy
98 4 merge remote copy
99 3 Added tag that for changeset 8601262d7472
99 3 Added tag that for changeset 8601262d7472
100 2 Removed tag that
100 2 Removed tag that
101 1 Added tag this for changeset 706614b458c1
101 1 Added tag this for changeset 706614b458c1
102 0 mark baz executable
102 0 mark baz executable
103 updating bookmarks
103 updating bookmarks
104 $ hg -R new log -G -T '{rev} {desc}'
104 $ hg -R new log -G -T '{rev} {desc}'
105 o 8 mark baz executable
105 o 8 mark baz executable
106 |
106 |
107 o 7 Added tag this for changeset 706614b458c1
107 o 7 Added tag this for changeset 706614b458c1
108 |
108 |
109 o 6 Removed tag that
109 o 6 Removed tag that
110 |
110 |
111 o 5 Added tag that for changeset 8601262d7472
111 o 5 Added tag that for changeset 8601262d7472
112 |
112 |
113 o 4 merge remote copy
113 o 4 merge remote copy
114 |\
114 |\
115 +---o 3 merge local copy
115 +---o 3 merge local copy
116 | |/
116 | |/
117 | o 2 make bar and baz copies of foo
117 | o 2 make bar and baz copies of foo
118 | |
118 | |
119 o | 1 change foo
119 o | 1 change foo
120 |/
120 |/
121 o 0 add foo bar
121 o 0 add foo bar
122
122
123
123
124 check shamap LF and CRLF handling
124 check shamap LF and CRLF handling
125
125
126 $ cat > rewrite.py <<EOF
126 $ cat > rewrite.py <<EOF
127 > import sys
127 > import sys
128 > # Interlace LF and CRLF
128 > # Interlace LF and CRLF
129 > lines = [(l.rstrip() + ((i % 2) and b'\n' or b'\r\n'))
129 > lines = [(l.rstrip() + ((i % 2) and b'\n' or b'\r\n'))
130 > for i, l in enumerate(open(sys.argv[1], 'rb'))]
130 > for i, l in enumerate(open(sys.argv[1], 'rb'))]
131 > open(sys.argv[1], 'wb').write(b''.join(lines))
131 > open(sys.argv[1], 'wb').write(b''.join(lines))
132 > EOF
132 > EOF
133 $ "$PYTHON" rewrite.py new/.hg/shamap
133 $ "$PYTHON" rewrite.py new/.hg/shamap
134 $ cd orig
134 $ cd orig
135 $ hg up -qC 1
135 $ hg up -qC 1
136 $ echo foo >> foo
136 $ echo foo >> foo
137 $ hg ci -qm 'change foo again'
137 $ hg ci -qm 'change foo again'
138 $ hg up -qC 2
138 $ hg up -qC 2
139 $ echo foo >> foo
139 $ echo foo >> foo
140 $ hg ci -qm 'change foo again again'
140 $ hg ci -qm 'change foo again again'
141 $ cd ..
141 $ cd ..
142 $ hg convert --datesort orig new 2>&1 | grep -v 'subversion python bindings could not be loaded'
142 $ hg convert --datesort orig new 2>&1 | grep -v 'subversion python bindings could not be loaded'
143 scanning source...
143 scanning source...
144 sorting...
144 sorting...
145 converting...
145 converting...
146 1 change foo again again
146 1 change foo again again
147 0 change foo again
147 0 change foo again
148 updating bookmarks
148 updating bookmarks
149
149
150 init broken repository
150 init broken repository
151
151
152 $ hg init broken
152 $ hg init broken
153 $ cd broken
153 $ cd broken
154 $ echo a >> a
154 $ echo a >> a
155 $ echo b >> b
155 $ echo b >> b
156 $ hg ci -qAm init
156 $ hg ci -qAm init
157 $ echo a >> a
157 $ echo a >> a
158 $ echo b >> b
158 $ echo b >> b
159 $ hg copy b c
159 $ hg copy b c
160 $ hg ci -qAm changeall
160 $ hg ci -qAm changeall
161 $ hg up -qC 0
161 $ hg up -qC 0
162 $ echo bc >> b
162 $ echo bc >> b
163 $ hg ci -m changebagain
163 $ hg ci -m changebagain
164 created new head
164 created new head
165 $ HGMERGE=internal:local hg -q merge
165 $ HGMERGE=internal:local hg -q merge
166 $ hg ci -m merge
166 $ hg ci -m merge
167 $ hg mv b d
167 $ hg mv b d
168 $ hg ci -m moveb
168 $ hg ci -m moveb
169
169
170 break it
170 break it
171
171
172 #if reporevlogstore
172 #if reporevlogstore
173 $ rm .hg/store/data/b.*
173 $ rm .hg/store/data/b.*
174 #endif
174 #endif
175 #if reposimplestore
175 #if reposimplestore
176 $ rm .hg/store/data/b/*
176 $ rm .hg/store/data/b/*
177 #endif
177 #endif
178 $ cd ..
178 $ cd ..
179 $ hg --config convert.hg.ignoreerrors=True convert broken fixed
179 $ hg --config convert.hg.ignoreerrors=True convert broken fixed
180 initializing destination fixed repository
180 initializing destination fixed repository
181 scanning source...
181 scanning source...
182 sorting...
182 sorting...
183 converting...
183 converting...
184 4 init
184 4 init
185 ignoring: data/b.i@1e88685f5dde: no match found (reporevlogstore !)
185 ignoring: data/b.i@1e88685f5dde: no match found (reporevlogstore !)
186 ignoring: data/b/index@1e88685f5dde: no node (reposimplestore !)
186 ignoring: data/b/index@1e88685f5dde: no node (reposimplestore !)
187 3 changeall
187 3 changeall
188 2 changebagain
188 2 changebagain
189 1 merge
189 1 merge
190 0 moveb
190 0 moveb
191 $ hg -R fixed verify
191 $ hg -R fixed verify
192 checking changesets
192 checking changesets
193 checking manifests
193 checking manifests
194 crosschecking files in changesets and manifests
194 crosschecking files in changesets and manifests
195 checking files
195 checking files
196 checked 5 changesets with 5 changes to 3 files
196 checked 5 changesets with 5 changes to 3 files
197
197
198 manifest -r 0
198 manifest -r 0
199
199
200 $ hg -R fixed manifest -r 0
200 $ hg -R fixed manifest -r 0
201 a
201 a
202
202
203 manifest -r tip
203 manifest -r tip
204
204
205 $ hg -R fixed manifest -r tip
205 $ hg -R fixed manifest -r tip
206 a
206 a
207 c
207 c
208 d
208 d
209 $ cd ..
209 $ cd ..
210
210
211 $ hg init commit-references
211 $ hg init commit-references
212 $ cd commit-references
212 $ cd commit-references
213 $ echo a > a
213 $ echo a > a
214 $ hg ci -Aqm initial
214 $ hg ci -Aqm initial
215 $ echo b > b
215 $ echo b > b
216 $ hg ci -Aqm 'the previous commit was 1451231c8757'
216 $ hg ci -Aqm 'the previous commit was 1451231c8757'
217 $ echo c > c
217 $ echo c > c
218 $ hg ci -Aqm 'the working copy is called ffffffffffff'
218 $ hg ci -Aqm 'the working copy is called ffffffffffff'
219
219
220 $ cd ..
220 $ cd ..
221 $ hg convert commit-references new-commit-references -q \
221 $ hg convert commit-references new-commit-references -q \
222 > --config convert.hg.sourcename=yes
222 > --config convert.hg.sourcename=yes
223 $ cd new-commit-references
223 $ cd new-commit-references
224 $ hg log -T '{node|short} {desc}\n'
224 $ hg log -T '{node|short} {desc}\n'
225 fe295c9e6bc6 the working copy is called ffffffffffff
225 fe295c9e6bc6 the working copy is called ffffffffffff
226 642508659503 the previous commit was c2491f685436
226 642508659503 the previous commit was c2491f685436
227 c2491f685436 initial
227 c2491f685436 initial
@@ -1,443 +1,443 b''
1 #require no-reposimplestore
1 #require no-reposimplestore
2
2
3 Testing the case when there is no infinitepush extension present on the client
3 Testing the case when there is no infinitepush extension present on the client
4 side and the server routes each push to bundlestore. This case is very much
4 side and the server routes each push to bundlestore. This case is very much
5 similar to CI use case.
5 similar to CI use case.
6
6
7 Setup
7 Setup
8 -----
8 -----
9
9
10 $ . "$TESTDIR/library-infinitepush.sh"
10 $ . "$TESTDIR/library-infinitepush.sh"
11 $ cat >> $HGRCPATH <<EOF
11 $ cat >> $HGRCPATH <<EOF
12 > [ui]
12 > [ui]
13 > ssh = python "$TESTDIR/dummyssh"
13 > ssh = python "$TESTDIR/dummyssh"
14 > [alias]
14 > [alias]
15 > glog = log -GT "{rev}:{node|short} {desc}\n{phase}"
15 > glog = log -GT "{rev}:{node|short} {desc}\n{phase}"
16 > EOF
16 > EOF
17 $ cp $HGRCPATH $TESTTMP/defaulthgrc
17 $ cp $HGRCPATH $TESTTMP/defaulthgrc
18 $ hg init repo
18 $ hg init repo
19 $ cd repo
19 $ cd repo
20 $ setupserver
20 $ setupserver
21 $ echo "pushtobundlestore = True" >> .hg/hgrc
21 $ echo "pushtobundlestore = True" >> .hg/hgrc
22 $ echo "[extensions]" >> .hg/hgrc
22 $ echo "[extensions]" >> .hg/hgrc
23 $ echo "infinitepush=" >> .hg/hgrc
23 $ echo "infinitepush=" >> .hg/hgrc
24 $ echo initialcommit > initialcommit
24 $ echo initialcommit > initialcommit
25 $ hg ci -Aqm "initialcommit"
25 $ hg ci -Aqm "initialcommit"
26 $ hg phase --public .
26 $ hg phase --public .
27
27
28 $ cd ..
28 $ cd ..
29 $ hg clone repo client -q
29 $ hg clone repo client -q
30 $ hg clone repo client2 -q
30 $ hg clone repo client2 -q
31 $ hg clone ssh://user@dummy/repo client3 -q
31 $ hg clone ssh://user@dummy/repo client3 -q
32 $ cd client
32 $ cd client
33
33
34 Pushing a new commit from the client to the server
34 Pushing a new commit from the client to the server
35 -----------------------------------------------------
35 -----------------------------------------------------
36
36
37 $ echo foobar > a
37 $ echo foobar > a
38 $ hg ci -Aqm "added a"
38 $ hg ci -Aqm "added a"
39 $ hg glog
39 $ hg glog
40 @ 1:6cb0989601f1 added a
40 @ 1:6cb0989601f1 added a
41 | draft
41 | draft
42 o 0:67145f466344 initialcommit
42 o 0:67145f466344 initialcommit
43 public
43 public
44
44
45 $ hg push
45 $ hg push
46 pushing to $TESTTMP/repo
46 pushing to $TESTTMP/repo
47 searching for changes
47 searching for changes
48 storing changesets on the bundlestore
48 storing changesets on the bundlestore
49 pushing 1 commit:
49 pushing 1 commit:
50 6cb0989601f1 added a
50 6cb0989601f1 added a
51
51
52 $ scratchnodes
52 $ scratchnodes
53 6cb0989601f1fb5805238edfb16f3606713d9a0b 3b414252ff8acab801318445d88ff48faf4a28c3
53 6cb0989601f1fb5805238edfb16f3606713d9a0b 3b414252ff8acab801318445d88ff48faf4a28c3
54
54
55 Understanding how data is stored on the bundlestore in server
55 Understanding how data is stored on the bundlestore in server
56 -------------------------------------------------------------
56 -------------------------------------------------------------
57
57
58 There are two things, filebundlestore and index
58 There are two things, filebundlestore and index
59 $ ls ../repo/.hg/scratchbranches
59 $ ls ../repo/.hg/scratchbranches
60 filebundlestore
60 filebundlestore
61 index
61 index
62
62
63 filebundlestore stores the bundles
63 filebundlestore stores the bundles
64 $ ls ../repo/.hg/scratchbranches/filebundlestore/3b/41/
64 $ ls ../repo/.hg/scratchbranches/filebundlestore/3b/41/
65 3b414252ff8acab801318445d88ff48faf4a28c3
65 3b414252ff8acab801318445d88ff48faf4a28c3
66
66
67 index/nodemap stores a map of node id and file in which bundle is stored in filebundlestore
67 index/nodemap stores a map of node id and file in which bundle is stored in filebundlestore
68 $ ls ../repo/.hg/scratchbranches/index/
68 $ ls ../repo/.hg/scratchbranches/index/
69 nodemap
69 nodemap
70 $ ls ../repo/.hg/scratchbranches/index/nodemap/
70 $ ls ../repo/.hg/scratchbranches/index/nodemap/
71 6cb0989601f1fb5805238edfb16f3606713d9a0b
71 6cb0989601f1fb5805238edfb16f3606713d9a0b
72
72
73 $ cd ../repo
73 $ cd ../repo
74
74
75 Checking that the commit was not applied to revlog on the server
75 Checking that the commit was not applied to revlog on the server
76 ------------------------------------------------------------------
76 ------------------------------------------------------------------
77
77
78 $ hg glog
78 $ hg glog
79 @ 0:67145f466344 initialcommit
79 @ 0:67145f466344 initialcommit
80 public
80 public
81
81
82 Applying the changeset from the bundlestore
82 Applying the changeset from the bundlestore
83 --------------------------------------------
83 --------------------------------------------
84
84
85 $ hg unbundle .hg/scratchbranches/filebundlestore/3b/41/3b414252ff8acab801318445d88ff48faf4a28c3
85 $ hg unbundle .hg/scratchbranches/filebundlestore/3b/41/3b414252ff8acab801318445d88ff48faf4a28c3
86 adding changesets
86 adding changesets
87 adding manifests
87 adding manifests
88 adding file changes
88 adding file changes
89 added 1 changesets with 1 changes to 1 files
89 added 1 changesets with 1 changes to 1 files
90 new changesets 6cb0989601f1
90 new changesets 6cb0989601f1
91 (run 'hg update' to get a working copy)
91 (run 'hg update' to get a working copy)
92
92
93 $ hg glog
93 $ hg glog
94 o 1:6cb0989601f1 added a
94 o 1:6cb0989601f1 added a
95 | public
95 | public
96 @ 0:67145f466344 initialcommit
96 @ 0:67145f466344 initialcommit
97 public
97 public
98
98
99 Pushing more changesets from the local repo
99 Pushing more changesets from the local repo
100 --------------------------------------------
100 --------------------------------------------
101
101
102 $ cd ../client
102 $ cd ../client
103 $ echo b > b
103 $ echo b > b
104 $ hg ci -Aqm "added b"
104 $ hg ci -Aqm "added b"
105 $ echo c > c
105 $ echo c > c
106 $ hg ci -Aqm "added c"
106 $ hg ci -Aqm "added c"
107 $ hg glog
107 $ hg glog
108 @ 3:bf8a6e3011b3 added c
108 @ 3:bf8a6e3011b3 added c
109 | draft
109 | draft
110 o 2:eaba929e866c added b
110 o 2:eaba929e866c added b
111 | draft
111 | draft
112 o 1:6cb0989601f1 added a
112 o 1:6cb0989601f1 added a
113 | public
113 | public
114 o 0:67145f466344 initialcommit
114 o 0:67145f466344 initialcommit
115 public
115 public
116
116
117 $ hg push
117 $ hg push
118 pushing to $TESTTMP/repo
118 pushing to $TESTTMP/repo
119 searching for changes
119 searching for changes
120 storing changesets on the bundlestore
120 storing changesets on the bundlestore
121 pushing 2 commits:
121 pushing 2 commits:
122 eaba929e866c added b
122 eaba929e866c added b
123 bf8a6e3011b3 added c
123 bf8a6e3011b3 added c
124
124
125 Checking that changesets are not applied on the server
125 Checking that changesets are not applied on the server
126 ------------------------------------------------------
126 ------------------------------------------------------
127
127
128 $ hg glog -R ../repo
128 $ hg glog -R ../repo
129 o 1:6cb0989601f1 added a
129 o 1:6cb0989601f1 added a
130 | public
130 | public
131 @ 0:67145f466344 initialcommit
131 @ 0:67145f466344 initialcommit
132 public
132 public
133
133
134 Both of the new changesets are stored in a single bundle-file
134 Both of the new changesets are stored in a single bundle-file
135 $ scratchnodes
135 $ scratchnodes
136 6cb0989601f1fb5805238edfb16f3606713d9a0b 3b414252ff8acab801318445d88ff48faf4a28c3
136 6cb0989601f1fb5805238edfb16f3606713d9a0b 3b414252ff8acab801318445d88ff48faf4a28c3
137 bf8a6e3011b345146bbbedbcb1ebd4837571492a 239585f5e61f0c09ce7106bdc1097bff731738f4
137 bf8a6e3011b345146bbbedbcb1ebd4837571492a 239585f5e61f0c09ce7106bdc1097bff731738f4
138 eaba929e866c59bc9a6aada5a9dd2f6990db83c0 239585f5e61f0c09ce7106bdc1097bff731738f4
138 eaba929e866c59bc9a6aada5a9dd2f6990db83c0 239585f5e61f0c09ce7106bdc1097bff731738f4
139
139
140 Pushing more changesets to the server
140 Pushing more changesets to the server
141 -------------------------------------
141 -------------------------------------
142
142
143 $ echo d > d
143 $ echo d > d
144 $ hg ci -Aqm "added d"
144 $ hg ci -Aqm "added d"
145 $ echo e > e
145 $ echo e > e
146 $ hg ci -Aqm "added e"
146 $ hg ci -Aqm "added e"
147
147
148 XXX: we should have pushed only the parts which are not in bundlestore
148 XXX: we should have pushed only the parts which are not in bundlestore
149 $ hg push
149 $ hg push
150 pushing to $TESTTMP/repo
150 pushing to $TESTTMP/repo
151 searching for changes
151 searching for changes
152 storing changesets on the bundlestore
152 storing changesets on the bundlestore
153 pushing 4 commits:
153 pushing 4 commits:
154 eaba929e866c added b
154 eaba929e866c added b
155 bf8a6e3011b3 added c
155 bf8a6e3011b3 added c
156 1bb96358eda2 added d
156 1bb96358eda2 added d
157 b4e4bce66051 added e
157 b4e4bce66051 added e
158
158
159 Sneak peek into the bundlestore at the server
159 Sneak peek into the bundlestore at the server
160 $ scratchnodes
160 $ scratchnodes
161 1bb96358eda285b536c6d1c66846a7cdb2336cea 98fbae0016662521b0007da1b7bc349cd3caacd1
161 1bb96358eda285b536c6d1c66846a7cdb2336cea 98fbae0016662521b0007da1b7bc349cd3caacd1
162 6cb0989601f1fb5805238edfb16f3606713d9a0b 3b414252ff8acab801318445d88ff48faf4a28c3
162 6cb0989601f1fb5805238edfb16f3606713d9a0b 3b414252ff8acab801318445d88ff48faf4a28c3
163 b4e4bce660512ad3e71189e14588a70ac8e31fef 98fbae0016662521b0007da1b7bc349cd3caacd1
163 b4e4bce660512ad3e71189e14588a70ac8e31fef 98fbae0016662521b0007da1b7bc349cd3caacd1
164 bf8a6e3011b345146bbbedbcb1ebd4837571492a 98fbae0016662521b0007da1b7bc349cd3caacd1
164 bf8a6e3011b345146bbbedbcb1ebd4837571492a 98fbae0016662521b0007da1b7bc349cd3caacd1
165 eaba929e866c59bc9a6aada5a9dd2f6990db83c0 98fbae0016662521b0007da1b7bc349cd3caacd1
165 eaba929e866c59bc9a6aada5a9dd2f6990db83c0 98fbae0016662521b0007da1b7bc349cd3caacd1
166
166
167 Checking if `hg pull` pulls something or `hg incoming` shows something
167 Checking if `hg pull` pulls something or `hg incoming` shows something
168 -----------------------------------------------------------------------
168 -----------------------------------------------------------------------
169
169
170 $ hg incoming
170 $ hg incoming
171 comparing with $TESTTMP/repo
171 comparing with $TESTTMP/repo
172 searching for changes
172 searching for changes
173 no changes found
173 no changes found
174 [1]
174 [1]
175
175
176 $ hg pull
176 $ hg pull
177 pulling from $TESTTMP/repo
177 pulling from $TESTTMP/repo
178 searching for changes
178 searching for changes
179 no changes found
179 no changes found
180
180
181 Pulling from second client which is a localpeer to test `hg pull -r <rev>`
181 Pulling from second client which is a localpeer to test `hg pull -r <rev>`
182 --------------------------------------------------------------------------
182 --------------------------------------------------------------------------
183
183
184 Pulling the revision which is applied
184 Pulling the revision which is applied
185
185
186 $ cd ../client2
186 $ cd ../client2
187 $ hg pull -r 6cb0989601f1
187 $ hg pull -r 6cb0989601f1
188 pulling from $TESTTMP/repo
188 pulling from $TESTTMP/repo
189 searching for changes
189 searching for changes
190 adding changesets
190 adding changesets
191 adding manifests
191 adding manifests
192 adding file changes
192 adding file changes
193 added 1 changesets with 1 changes to 1 files
193 added 1 changesets with 1 changes to 1 files
194 new changesets 6cb0989601f1
194 new changesets 6cb0989601f1
195 (run 'hg update' to get a working copy)
195 (run 'hg update' to get a working copy)
196 $ hg glog
196 $ hg glog
197 o 1:6cb0989601f1 added a
197 o 1:6cb0989601f1 added a
198 | public
198 | public
199 @ 0:67145f466344 initialcommit
199 @ 0:67145f466344 initialcommit
200 public
200 public
201
201
202 Pulling the revision which is in bundlestore
202 Pulling the revision which is in bundlestore
203 XXX: we should support pulling revisions from a local peers bundlestore without
203 XXX: we should support pulling revisions from a local peers bundlestore without
204 client side wrapping
204 client side wrapping
205
205
206 $ hg pull -r b4e4bce660512ad3e71189e14588a70ac8e31fef
206 $ hg pull -r b4e4bce660512ad3e71189e14588a70ac8e31fef
207 pulling from $TESTTMP/repo
207 pulling from $TESTTMP/repo
208 abort: unknown revision 'b4e4bce660512ad3e71189e14588a70ac8e31fef'!
208 abort: unknown revision 'b4e4bce660512ad3e71189e14588a70ac8e31fef'!
209 [255]
209 [255]
210 $ hg glog
210 $ hg glog
211 o 1:6cb0989601f1 added a
211 o 1:6cb0989601f1 added a
212 | public
212 | public
213 @ 0:67145f466344 initialcommit
213 @ 0:67145f466344 initialcommit
214 public
214 public
215
215
216 $ cd ../client
216 $ cd ../client
217
217
218 Pulling from third client which is not a localpeer
218 Pulling from third client which is not a localpeer
219 ---------------------------------------------------
219 ---------------------------------------------------
220
220
221 Pulling the revision which is applied
221 Pulling the revision which is applied
222
222
223 $ cd ../client3
223 $ cd ../client3
224 $ hg pull -r 6cb0989601f1
224 $ hg pull -r 6cb0989601f1
225 pulling from ssh://user@dummy/repo
225 pulling from ssh://user@dummy/repo
226 searching for changes
226 searching for changes
227 adding changesets
227 adding changesets
228 adding manifests
228 adding manifests
229 adding file changes
229 adding file changes
230 added 1 changesets with 1 changes to 1 files
230 added 1 changesets with 1 changes to 1 files
231 new changesets 6cb0989601f1
231 new changesets 6cb0989601f1
232 (run 'hg update' to get a working copy)
232 (run 'hg update' to get a working copy)
233 $ hg glog
233 $ hg glog
234 o 1:6cb0989601f1 added a
234 o 1:6cb0989601f1 added a
235 | public
235 | public
236 @ 0:67145f466344 initialcommit
236 @ 0:67145f466344 initialcommit
237 public
237 public
238
238
239 Pulling the revision which is in bundlestore
239 Pulling the revision which is in bundlestore
240
240
241 Trying to specify short hash
241 Trying to specify short hash
242 XXX: we should support this
242 XXX: we should support this
243 $ hg pull -r b4e4bce660512
243 $ hg pull -r b4e4bce660512
244 pulling from ssh://user@dummy/repo
244 pulling from ssh://user@dummy/repo
245 abort: unknown revision 'b4e4bce660512'!
245 abort: unknown revision 'b4e4bce660512'!
246 [255]
246 [255]
247
247
248 XXX: we should show better message when the pull is happening from bundlestore
248 XXX: we should show better message when the pull is happening from bundlestore
249 $ hg pull -r b4e4bce660512ad3e71189e14588a70ac8e31fef
249 $ hg pull -r b4e4bce660512ad3e71189e14588a70ac8e31fef
250 pulling from ssh://user@dummy/repo
250 pulling from ssh://user@dummy/repo
251 searching for changes
251 searching for changes
252 no changes found
253 adding changesets
252 adding changesets
254 adding manifests
253 adding manifests
255 adding file changes
254 adding file changes
256 added 4 changesets with 4 changes to 4 files
255 added 4 changesets with 4 changes to 4 files
257 new changesets eaba929e866c:b4e4bce66051
256 new changesets eaba929e866c:b4e4bce66051
257 (run 'hg update' to get a working copy)
258 $ hg glog
258 $ hg glog
259 o 5:b4e4bce66051 added e
259 o 5:b4e4bce66051 added e
260 | public
260 | public
261 o 4:1bb96358eda2 added d
261 o 4:1bb96358eda2 added d
262 | public
262 | public
263 o 3:bf8a6e3011b3 added c
263 o 3:bf8a6e3011b3 added c
264 | public
264 | public
265 o 2:eaba929e866c added b
265 o 2:eaba929e866c added b
266 | public
266 | public
267 o 1:6cb0989601f1 added a
267 o 1:6cb0989601f1 added a
268 | public
268 | public
269 @ 0:67145f466344 initialcommit
269 @ 0:67145f466344 initialcommit
270 public
270 public
271
271
272 $ cd ../client
272 $ cd ../client
273
273
274 Checking storage of phase information with the bundle on bundlestore
274 Checking storage of phase information with the bundle on bundlestore
275 ---------------------------------------------------------------------
275 ---------------------------------------------------------------------
276
276
277 creating a draft commit
277 creating a draft commit
278 $ cat >> $HGRCPATH <<EOF
278 $ cat >> $HGRCPATH <<EOF
279 > [phases]
279 > [phases]
280 > publish = False
280 > publish = False
281 > EOF
281 > EOF
282 $ echo f > f
282 $ echo f > f
283 $ hg ci -Aqm "added f"
283 $ hg ci -Aqm "added f"
284 $ hg glog -r '.^::'
284 $ hg glog -r '.^::'
285 @ 6:9b42578d4447 added f
285 @ 6:9b42578d4447 added f
286 | draft
286 | draft
287 o 5:b4e4bce66051 added e
287 o 5:b4e4bce66051 added e
288 | public
288 | public
289 ~
289 ~
290
290
291 $ hg push
291 $ hg push
292 pushing to $TESTTMP/repo
292 pushing to $TESTTMP/repo
293 searching for changes
293 searching for changes
294 storing changesets on the bundlestore
294 storing changesets on the bundlestore
295 pushing 5 commits:
295 pushing 5 commits:
296 eaba929e866c added b
296 eaba929e866c added b
297 bf8a6e3011b3 added c
297 bf8a6e3011b3 added c
298 1bb96358eda2 added d
298 1bb96358eda2 added d
299 b4e4bce66051 added e
299 b4e4bce66051 added e
300 9b42578d4447 added f
300 9b42578d4447 added f
301
301
302 XXX: the phase of 9b42578d4447 should not be changed here
302 XXX: the phase of 9b42578d4447 should not be changed here
303 $ hg glog -r .
303 $ hg glog -r .
304 @ 6:9b42578d4447 added f
304 @ 6:9b42578d4447 added f
305 | public
305 | public
306 ~
306 ~
307
307
308 applying the bundle on the server to check preservation of phase-information
308 applying the bundle on the server to check preservation of phase-information
309
309
310 $ cd ../repo
310 $ cd ../repo
311 $ scratchnodes
311 $ scratchnodes
312 1bb96358eda285b536c6d1c66846a7cdb2336cea 280a46a259a268f0e740c81c5a7751bdbfaec85f
312 1bb96358eda285b536c6d1c66846a7cdb2336cea 280a46a259a268f0e740c81c5a7751bdbfaec85f
313 6cb0989601f1fb5805238edfb16f3606713d9a0b 3b414252ff8acab801318445d88ff48faf4a28c3
313 6cb0989601f1fb5805238edfb16f3606713d9a0b 3b414252ff8acab801318445d88ff48faf4a28c3
314 9b42578d44473575994109161430d65dd147d16d 280a46a259a268f0e740c81c5a7751bdbfaec85f
314 9b42578d44473575994109161430d65dd147d16d 280a46a259a268f0e740c81c5a7751bdbfaec85f
315 b4e4bce660512ad3e71189e14588a70ac8e31fef 280a46a259a268f0e740c81c5a7751bdbfaec85f
315 b4e4bce660512ad3e71189e14588a70ac8e31fef 280a46a259a268f0e740c81c5a7751bdbfaec85f
316 bf8a6e3011b345146bbbedbcb1ebd4837571492a 280a46a259a268f0e740c81c5a7751bdbfaec85f
316 bf8a6e3011b345146bbbedbcb1ebd4837571492a 280a46a259a268f0e740c81c5a7751bdbfaec85f
317 eaba929e866c59bc9a6aada5a9dd2f6990db83c0 280a46a259a268f0e740c81c5a7751bdbfaec85f
317 eaba929e866c59bc9a6aada5a9dd2f6990db83c0 280a46a259a268f0e740c81c5a7751bdbfaec85f
318
318
319 $ hg unbundle .hg/scratchbranches/filebundlestore/28/0a/280a46a259a268f0e740c81c5a7751bdbfaec85f
319 $ hg unbundle .hg/scratchbranches/filebundlestore/28/0a/280a46a259a268f0e740c81c5a7751bdbfaec85f
320 adding changesets
320 adding changesets
321 adding manifests
321 adding manifests
322 adding file changes
322 adding file changes
323 added 5 changesets with 5 changes to 5 files
323 added 5 changesets with 5 changes to 5 files
324 new changesets eaba929e866c:9b42578d4447 (1 drafts)
324 new changesets eaba929e866c:9b42578d4447 (1 drafts)
325 (run 'hg update' to get a working copy)
325 (run 'hg update' to get a working copy)
326
326
327 $ hg glog
327 $ hg glog
328 o 6:9b42578d4447 added f
328 o 6:9b42578d4447 added f
329 | draft
329 | draft
330 o 5:b4e4bce66051 added e
330 o 5:b4e4bce66051 added e
331 | public
331 | public
332 o 4:1bb96358eda2 added d
332 o 4:1bb96358eda2 added d
333 | public
333 | public
334 o 3:bf8a6e3011b3 added c
334 o 3:bf8a6e3011b3 added c
335 | public
335 | public
336 o 2:eaba929e866c added b
336 o 2:eaba929e866c added b
337 | public
337 | public
338 o 1:6cb0989601f1 added a
338 o 1:6cb0989601f1 added a
339 | public
339 | public
340 @ 0:67145f466344 initialcommit
340 @ 0:67145f466344 initialcommit
341 public
341 public
342
342
343 Checking storage of obsmarkers in the bundlestore
343 Checking storage of obsmarkers in the bundlestore
344 --------------------------------------------------
344 --------------------------------------------------
345
345
346 enabling obsmarkers and rebase extension
346 enabling obsmarkers and rebase extension
347
347
348 $ cat >> $HGRCPATH << EOF
348 $ cat >> $HGRCPATH << EOF
349 > [experimental]
349 > [experimental]
350 > evolution = all
350 > evolution = all
351 > [extensions]
351 > [extensions]
352 > rebase =
352 > rebase =
353 > EOF
353 > EOF
354
354
355 $ cd ../client
355 $ cd ../client
356
356
357 $ hg phase -r . --draft --force
357 $ hg phase -r . --draft --force
358 $ hg rebase -r 6 -d 3
358 $ hg rebase -r 6 -d 3
359 rebasing 6:9b42578d4447 "added f" (tip)
359 rebasing 6:9b42578d4447 "added f" (tip)
360
360
361 $ hg glog
361 $ hg glog
362 @ 7:99949238d9ac added f
362 @ 7:99949238d9ac added f
363 | draft
363 | draft
364 | o 5:b4e4bce66051 added e
364 | o 5:b4e4bce66051 added e
365 | | public
365 | | public
366 | o 4:1bb96358eda2 added d
366 | o 4:1bb96358eda2 added d
367 |/ public
367 |/ public
368 o 3:bf8a6e3011b3 added c
368 o 3:bf8a6e3011b3 added c
369 | public
369 | public
370 o 2:eaba929e866c added b
370 o 2:eaba929e866c added b
371 | public
371 | public
372 o 1:6cb0989601f1 added a
372 o 1:6cb0989601f1 added a
373 | public
373 | public
374 o 0:67145f466344 initialcommit
374 o 0:67145f466344 initialcommit
375 public
375 public
376
376
377 $ hg push -f
377 $ hg push -f
378 pushing to $TESTTMP/repo
378 pushing to $TESTTMP/repo
379 searching for changes
379 searching for changes
380 storing changesets on the bundlestore
380 storing changesets on the bundlestore
381 pushing 1 commit:
381 pushing 1 commit:
382 99949238d9ac added f
382 99949238d9ac added f
383
383
384 XXX: the phase should not have changed here
384 XXX: the phase should not have changed here
385 $ hg glog -r .
385 $ hg glog -r .
386 @ 7:99949238d9ac added f
386 @ 7:99949238d9ac added f
387 | public
387 | public
388 ~
388 ~
389
389
390 Unbundling on server to see obsmarkers being applied
390 Unbundling on server to see obsmarkers being applied
391
391
392 $ cd ../repo
392 $ cd ../repo
393
393
394 $ scratchnodes
394 $ scratchnodes
395 1bb96358eda285b536c6d1c66846a7cdb2336cea 280a46a259a268f0e740c81c5a7751bdbfaec85f
395 1bb96358eda285b536c6d1c66846a7cdb2336cea 280a46a259a268f0e740c81c5a7751bdbfaec85f
396 6cb0989601f1fb5805238edfb16f3606713d9a0b 3b414252ff8acab801318445d88ff48faf4a28c3
396 6cb0989601f1fb5805238edfb16f3606713d9a0b 3b414252ff8acab801318445d88ff48faf4a28c3
397 99949238d9ac7f2424a33a46dface6f866afd059 090a24fe63f31d3b4bee714447f835c8c362ff57
397 99949238d9ac7f2424a33a46dface6f866afd059 090a24fe63f31d3b4bee714447f835c8c362ff57
398 9b42578d44473575994109161430d65dd147d16d 280a46a259a268f0e740c81c5a7751bdbfaec85f
398 9b42578d44473575994109161430d65dd147d16d 280a46a259a268f0e740c81c5a7751bdbfaec85f
399 b4e4bce660512ad3e71189e14588a70ac8e31fef 280a46a259a268f0e740c81c5a7751bdbfaec85f
399 b4e4bce660512ad3e71189e14588a70ac8e31fef 280a46a259a268f0e740c81c5a7751bdbfaec85f
400 bf8a6e3011b345146bbbedbcb1ebd4837571492a 280a46a259a268f0e740c81c5a7751bdbfaec85f
400 bf8a6e3011b345146bbbedbcb1ebd4837571492a 280a46a259a268f0e740c81c5a7751bdbfaec85f
401 eaba929e866c59bc9a6aada5a9dd2f6990db83c0 280a46a259a268f0e740c81c5a7751bdbfaec85f
401 eaba929e866c59bc9a6aada5a9dd2f6990db83c0 280a46a259a268f0e740c81c5a7751bdbfaec85f
402
402
403 $ hg glog
403 $ hg glog
404 o 6:9b42578d4447 added f
404 o 6:9b42578d4447 added f
405 | draft
405 | draft
406 o 5:b4e4bce66051 added e
406 o 5:b4e4bce66051 added e
407 | public
407 | public
408 o 4:1bb96358eda2 added d
408 o 4:1bb96358eda2 added d
409 | public
409 | public
410 o 3:bf8a6e3011b3 added c
410 o 3:bf8a6e3011b3 added c
411 | public
411 | public
412 o 2:eaba929e866c added b
412 o 2:eaba929e866c added b
413 | public
413 | public
414 o 1:6cb0989601f1 added a
414 o 1:6cb0989601f1 added a
415 | public
415 | public
416 @ 0:67145f466344 initialcommit
416 @ 0:67145f466344 initialcommit
417 public
417 public
418
418
419 $ hg unbundle .hg/scratchbranches/filebundlestore/09/0a/090a24fe63f31d3b4bee714447f835c8c362ff57
419 $ hg unbundle .hg/scratchbranches/filebundlestore/09/0a/090a24fe63f31d3b4bee714447f835c8c362ff57
420 adding changesets
420 adding changesets
421 adding manifests
421 adding manifests
422 adding file changes
422 adding file changes
423 added 1 changesets with 0 changes to 1 files (+1 heads)
423 added 1 changesets with 0 changes to 1 files (+1 heads)
424 1 new obsolescence markers
424 1 new obsolescence markers
425 obsoleted 1 changesets
425 obsoleted 1 changesets
426 new changesets 99949238d9ac (1 drafts)
426 new changesets 99949238d9ac (1 drafts)
427 (run 'hg heads' to see heads, 'hg merge' to merge)
427 (run 'hg heads' to see heads, 'hg merge' to merge)
428
428
429 $ hg glog
429 $ hg glog
430 o 7:99949238d9ac added f
430 o 7:99949238d9ac added f
431 | draft
431 | draft
432 | o 5:b4e4bce66051 added e
432 | o 5:b4e4bce66051 added e
433 | | public
433 | | public
434 | o 4:1bb96358eda2 added d
434 | o 4:1bb96358eda2 added d
435 |/ public
435 |/ public
436 o 3:bf8a6e3011b3 added c
436 o 3:bf8a6e3011b3 added c
437 | public
437 | public
438 o 2:eaba929e866c added b
438 o 2:eaba929e866c added b
439 | public
439 | public
440 o 1:6cb0989601f1 added a
440 o 1:6cb0989601f1 added a
441 | public
441 | public
442 @ 0:67145f466344 initialcommit
442 @ 0:67145f466344 initialcommit
443 public
443 public
@@ -1,721 +1,715 b''
1 #testcases lfsremote-on lfsremote-off
1 #testcases lfsremote-on lfsremote-off
2 #require serve no-reposimplestore no-chg
2 #require serve no-reposimplestore no-chg
3
3
4 This test splits `hg serve` with and without using the extension into separate
4 This test splits `hg serve` with and without using the extension into separate
5 tests cases. The tests are broken down as follows, where "LFS"/"No-LFS"
5 tests cases. The tests are broken down as follows, where "LFS"/"No-LFS"
6 indicates whether or not there are commits that use an LFS file, and "D"/"E"
6 indicates whether or not there are commits that use an LFS file, and "D"/"E"
7 indicates whether or not the extension is loaded. The "X" cases are not tested
7 indicates whether or not the extension is loaded. The "X" cases are not tested
8 individually, because the lfs requirement causes the process to bail early if
8 individually, because the lfs requirement causes the process to bail early if
9 the extension is disabled.
9 the extension is disabled.
10
10
11 . Server
11 . Server
12 .
12 .
13 . No-LFS LFS
13 . No-LFS LFS
14 . +----------------------------+
14 . +----------------------------+
15 . | || D | E | D | E |
15 . | || D | E | D | E |
16 . |---++=======================|
16 . |---++=======================|
17 . C | D || N/A | #1 | X | #4 |
17 . C | D || N/A | #1 | X | #4 |
18 . l No +---++-----------------------|
18 . l No +---++-----------------------|
19 . i LFS | E || #2 | #2 | X | #5 |
19 . i LFS | E || #2 | #2 | X | #5 |
20 . e +---++-----------------------|
20 . e +---++-----------------------|
21 . n | D || X | X | X | X |
21 . n | D || X | X | X | X |
22 . t LFS |---++-----------------------|
22 . t LFS |---++-----------------------|
23 . | E || #3 | #3 | X | #6 |
23 . | E || #3 | #3 | X | #6 |
24 . |---++-----------------------+
24 . |---++-----------------------+
25
25
26 make command server magic visible
26 make command server magic visible
27
27
28 #if windows
28 #if windows
29 $ PYTHONPATH="$TESTDIR/../contrib;$PYTHONPATH"
29 $ PYTHONPATH="$TESTDIR/../contrib;$PYTHONPATH"
30 #else
30 #else
31 $ PYTHONPATH="$TESTDIR/../contrib:$PYTHONPATH"
31 $ PYTHONPATH="$TESTDIR/../contrib:$PYTHONPATH"
32 #endif
32 #endif
33 $ export PYTHONPATH
33 $ export PYTHONPATH
34
34
35 $ hg init server
35 $ hg init server
36 $ SERVER_REQUIRES="$TESTTMP/server/.hg/requires"
36 $ SERVER_REQUIRES="$TESTTMP/server/.hg/requires"
37
37
38 $ cat > $TESTTMP/debugprocessors.py <<EOF
38 $ cat > $TESTTMP/debugprocessors.py <<EOF
39 > from mercurial import (
39 > from mercurial import (
40 > cmdutil,
40 > cmdutil,
41 > commands,
41 > commands,
42 > pycompat,
42 > pycompat,
43 > registrar,
43 > registrar,
44 > )
44 > )
45 > cmdtable = {}
45 > cmdtable = {}
46 > command = registrar.command(cmdtable)
46 > command = registrar.command(cmdtable)
47 > @command(b'debugprocessors', [], b'FILE')
47 > @command(b'debugprocessors', [], b'FILE')
48 > def debugprocessors(ui, repo, file_=None, **opts):
48 > def debugprocessors(ui, repo, file_=None, **opts):
49 > opts = pycompat.byteskwargs(opts)
49 > opts = pycompat.byteskwargs(opts)
50 > opts[b'changelog'] = False
50 > opts[b'changelog'] = False
51 > opts[b'manifest'] = False
51 > opts[b'manifest'] = False
52 > opts[b'dir'] = False
52 > opts[b'dir'] = False
53 > rl = cmdutil.openrevlog(repo, b'debugprocessors', file_, opts)
53 > rl = cmdutil.openrevlog(repo, b'debugprocessors', file_, opts)
54 > for flag, proc in rl._flagprocessors.items():
54 > for flag, proc in rl._flagprocessors.items():
55 > ui.status(b"registered processor '%#x'\n" % (flag))
55 > ui.status(b"registered processor '%#x'\n" % (flag))
56 > EOF
56 > EOF
57
57
58 Skip the experimental.changegroup3=True config. Failure to agree on this comes
58 Skip the experimental.changegroup3=True config. Failure to agree on this comes
59 first, and causes an "abort: no common changegroup version" if the extension is
59 first, and causes an "abort: no common changegroup version" if the extension is
60 only loaded on one side. If that *is* enabled, the subsequent failure is "abort:
60 only loaded on one side. If that *is* enabled, the subsequent failure is "abort:
61 missing processor for flag '0x2000'!" if the extension is only loaded on one side
61 missing processor for flag '0x2000'!" if the extension is only loaded on one side
62 (possibly also masked by the Internal Server Error message).
62 (possibly also masked by the Internal Server Error message).
63 $ cat >> $HGRCPATH <<EOF
63 $ cat >> $HGRCPATH <<EOF
64 > [extensions]
64 > [extensions]
65 > debugprocessors = $TESTTMP/debugprocessors.py
65 > debugprocessors = $TESTTMP/debugprocessors.py
66 > [experimental]
66 > [experimental]
67 > lfs.disableusercache = True
67 > lfs.disableusercache = True
68 > lfs.worker-enable = False
68 > lfs.worker-enable = False
69 > [lfs]
69 > [lfs]
70 > threshold=10
70 > threshold=10
71 > [web]
71 > [web]
72 > allow_push=*
72 > allow_push=*
73 > push_ssl=False
73 > push_ssl=False
74 > EOF
74 > EOF
75
75
76 $ cp $HGRCPATH $HGRCPATH.orig
76 $ cp $HGRCPATH $HGRCPATH.orig
77
77
78 #if lfsremote-on
78 #if lfsremote-on
79 $ hg --config extensions.lfs= -R server \
79 $ hg --config extensions.lfs= -R server \
80 > serve -p $HGPORT -d --pid-file=hg.pid --errorlog=$TESTTMP/errors.log
80 > serve -p $HGPORT -d --pid-file=hg.pid --errorlog=$TESTTMP/errors.log
81 #else
81 #else
82 $ hg --config extensions.lfs=! -R server \
82 $ hg --config extensions.lfs=! -R server \
83 > serve -p $HGPORT -d --pid-file=hg.pid --errorlog=$TESTTMP/errors.log
83 > serve -p $HGPORT -d --pid-file=hg.pid --errorlog=$TESTTMP/errors.log
84 #endif
84 #endif
85
85
86 $ cat hg.pid >> $DAEMON_PIDS
86 $ cat hg.pid >> $DAEMON_PIDS
87 $ hg clone -q http://localhost:$HGPORT client
87 $ hg clone -q http://localhost:$HGPORT client
88 $ grep 'lfs' client/.hg/requires $SERVER_REQUIRES
88 $ grep 'lfs' client/.hg/requires $SERVER_REQUIRES
89 [1]
89 [1]
90
90
91 This trivial repo will force commandserver to load the extension, but not call
91 This trivial repo will force commandserver to load the extension, but not call
92 reposetup() on another repo actually being operated on. This gives coverage
92 reposetup() on another repo actually being operated on. This gives coverage
93 that wrapper functions are not assuming reposetup() was called.
93 that wrapper functions are not assuming reposetup() was called.
94
94
95 $ hg init $TESTTMP/cmdservelfs
95 $ hg init $TESTTMP/cmdservelfs
96 $ cat >> $TESTTMP/cmdservelfs/.hg/hgrc << EOF
96 $ cat >> $TESTTMP/cmdservelfs/.hg/hgrc << EOF
97 > [extensions]
97 > [extensions]
98 > lfs =
98 > lfs =
99 > EOF
99 > EOF
100
100
101 --------------------------------------------------------------------------------
101 --------------------------------------------------------------------------------
102 Case #1: client with non-lfs content and the extension disabled; server with
102 Case #1: client with non-lfs content and the extension disabled; server with
103 non-lfs content, and the extension enabled.
103 non-lfs content, and the extension enabled.
104
104
105 $ cd client
105 $ cd client
106 $ echo 'non-lfs' > nonlfs.txt
106 $ echo 'non-lfs' > nonlfs.txt
107 >>> from __future__ import absolute_import
107 >>> from __future__ import absolute_import
108 >>> from hgclient import check, readchannel, runcommand
108 >>> from hgclient import check, readchannel, runcommand
109 >>> @check
109 >>> @check
110 ... def diff(server):
110 ... def diff(server):
111 ... readchannel(server)
111 ... readchannel(server)
112 ... # run an arbitrary command in the repo with the extension loaded
112 ... # run an arbitrary command in the repo with the extension loaded
113 ... runcommand(server, [b'id', b'-R', b'../cmdservelfs'])
113 ... runcommand(server, [b'id', b'-R', b'../cmdservelfs'])
114 ... # now run a command in a repo without the extension to ensure that
114 ... # now run a command in a repo without the extension to ensure that
115 ... # files are added safely..
115 ... # files are added safely..
116 ... runcommand(server, [b'ci', b'-Aqm', b'non-lfs'])
116 ... runcommand(server, [b'ci', b'-Aqm', b'non-lfs'])
117 ... # .. and that scmutil.prefetchfiles() safely no-ops..
117 ... # .. and that scmutil.prefetchfiles() safely no-ops..
118 ... runcommand(server, [b'diff', b'-r', b'.~1'])
118 ... runcommand(server, [b'diff', b'-r', b'.~1'])
119 ... # .. and that debugupgraderepo safely no-ops.
119 ... # .. and that debugupgraderepo safely no-ops.
120 ... runcommand(server, [b'debugupgraderepo', b'-q', b'--run'])
120 ... runcommand(server, [b'debugupgraderepo', b'-q', b'--run'])
121 *** runcommand id -R ../cmdservelfs
121 *** runcommand id -R ../cmdservelfs
122 000000000000 tip
122 000000000000 tip
123 *** runcommand ci -Aqm non-lfs
123 *** runcommand ci -Aqm non-lfs
124 *** runcommand diff -r .~1
124 *** runcommand diff -r .~1
125 diff -r 000000000000 nonlfs.txt
125 diff -r 000000000000 nonlfs.txt
126 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
126 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
127 +++ b/nonlfs.txt Thu Jan 01 00:00:00 1970 +0000
127 +++ b/nonlfs.txt Thu Jan 01 00:00:00 1970 +0000
128 @@ -0,0 +1,1 @@
128 @@ -0,0 +1,1 @@
129 +non-lfs
129 +non-lfs
130 *** runcommand debugupgraderepo -q --run
130 *** runcommand debugupgraderepo -q --run
131 upgrade will perform the following actions:
131 upgrade will perform the following actions:
132
132
133 requirements
133 requirements
134 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store
134 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store
135
135
136 sidedata
137 Allows storage of extra data alongside a revision.
138
139 copies-sdc
140 Allows to use more efficient algorithm to deal with copy tracing.
141
142 beginning upgrade...
136 beginning upgrade...
143 repository locked and read-only
137 repository locked and read-only
144 creating temporary repository to stage migrated data: * (glob)
138 creating temporary repository to stage migrated data: * (glob)
145 (it is safe to interrupt this process any time before data migration completes)
139 (it is safe to interrupt this process any time before data migration completes)
146 migrating 3 total revisions (1 in filelogs, 1 in manifests, 1 in changelog)
140 migrating 3 total revisions (1 in filelogs, 1 in manifests, 1 in changelog)
147 migrating 324 bytes in store; 129 bytes tracked data
141 migrating 324 bytes in store; 129 bytes tracked data
148 migrating 1 filelogs containing 1 revisions (73 bytes in store; 8 bytes tracked data)
142 migrating 1 filelogs containing 1 revisions (73 bytes in store; 8 bytes tracked data)
149 finished migrating 1 filelog revisions across 1 filelogs; change in size: 0 bytes
143 finished migrating 1 filelog revisions across 1 filelogs; change in size: 0 bytes
150 migrating 1 manifests containing 1 revisions (117 bytes in store; 52 bytes tracked data)
144 migrating 1 manifests containing 1 revisions (117 bytes in store; 52 bytes tracked data)
151 finished migrating 1 manifest revisions across 1 manifests; change in size: 0 bytes
145 finished migrating 1 manifest revisions across 1 manifests; change in size: 0 bytes
152 migrating changelog containing 1 revisions (134 bytes in store; 69 bytes tracked data)
146 migrating changelog containing 1 revisions (134 bytes in store; 69 bytes tracked data)
153 finished migrating 1 changelog revisions; change in size: 0 bytes
147 finished migrating 1 changelog revisions; change in size: 0 bytes
154 finished migrating 3 total revisions; total change in store size: 0 bytes
148 finished migrating 3 total revisions; total change in store size: 0 bytes
155 copying phaseroots
149 copying phaseroots
156 data fully migrated to temporary repository
150 data fully migrated to temporary repository
157 marking source repository as being upgraded; clients will be unable to read from repository
151 marking source repository as being upgraded; clients will be unable to read from repository
158 starting in-place swap of repository data
152 starting in-place swap of repository data
159 replaced files will be backed up at * (glob)
153 replaced files will be backed up at * (glob)
160 replacing store...
154 replacing store...
161 store replacement complete; repository was inconsistent for *s (glob)
155 store replacement complete; repository was inconsistent for *s (glob)
162 finalizing requirements file and making repository readable again
156 finalizing requirements file and making repository readable again
163 removing temporary repository * (glob)
157 removing temporary repository * (glob)
164 copy of old repository backed up at * (glob)
158 copy of old repository backed up at * (glob)
165 the old repository will not be deleted; remove it to free up disk space once the upgraded repository is verified
159 the old repository will not be deleted; remove it to free up disk space once the upgraded repository is verified
166
160
167 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
161 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
168 [1]
162 [1]
169
163
170 #if lfsremote-on
164 #if lfsremote-on
171
165
172 $ hg push -q
166 $ hg push -q
173 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
167 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
174 [1]
168 [1]
175
169
176 $ hg clone -q http://localhost:$HGPORT $TESTTMP/client1_clone
170 $ hg clone -q http://localhost:$HGPORT $TESTTMP/client1_clone
177 $ grep 'lfs' $TESTTMP/client1_clone/.hg/requires $SERVER_REQUIRES
171 $ grep 'lfs' $TESTTMP/client1_clone/.hg/requires $SERVER_REQUIRES
178 [1]
172 [1]
179
173
180 $ hg init $TESTTMP/client1_pull
174 $ hg init $TESTTMP/client1_pull
181 $ hg -R $TESTTMP/client1_pull pull -q http://localhost:$HGPORT
175 $ hg -R $TESTTMP/client1_pull pull -q http://localhost:$HGPORT
182 $ grep 'lfs' $TESTTMP/client1_pull/.hg/requires $SERVER_REQUIRES
176 $ grep 'lfs' $TESTTMP/client1_pull/.hg/requires $SERVER_REQUIRES
183 [1]
177 [1]
184
178
185 $ hg identify http://localhost:$HGPORT
179 $ hg identify http://localhost:$HGPORT
186 d437e1d24fbd
180 d437e1d24fbd
187
181
188 #endif
182 #endif
189
183
190 --------------------------------------------------------------------------------
184 --------------------------------------------------------------------------------
191 Case #2: client with non-lfs content and the extension enabled; server with
185 Case #2: client with non-lfs content and the extension enabled; server with
192 non-lfs content, and the extension state controlled by #testcases.
186 non-lfs content, and the extension state controlled by #testcases.
193
187
194 $ cat >> $HGRCPATH <<EOF
188 $ cat >> $HGRCPATH <<EOF
195 > [extensions]
189 > [extensions]
196 > lfs =
190 > lfs =
197 > EOF
191 > EOF
198 $ echo 'non-lfs' > nonlfs2.txt
192 $ echo 'non-lfs' > nonlfs2.txt
199 $ hg ci -Aqm 'non-lfs file with lfs client'
193 $ hg ci -Aqm 'non-lfs file with lfs client'
200
194
201 Since no lfs content has been added yet, the push is allowed, even when the
195 Since no lfs content has been added yet, the push is allowed, even when the
202 extension is not enabled remotely.
196 extension is not enabled remotely.
203
197
204 $ hg push -q
198 $ hg push -q
205 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
199 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
206 [1]
200 [1]
207
201
208 $ hg clone -q http://localhost:$HGPORT $TESTTMP/client2_clone
202 $ hg clone -q http://localhost:$HGPORT $TESTTMP/client2_clone
209 $ grep 'lfs' $TESTTMP/client2_clone/.hg/requires $SERVER_REQUIRES
203 $ grep 'lfs' $TESTTMP/client2_clone/.hg/requires $SERVER_REQUIRES
210 [1]
204 [1]
211
205
212 $ hg init $TESTTMP/client2_pull
206 $ hg init $TESTTMP/client2_pull
213 $ hg -R $TESTTMP/client2_pull pull -q http://localhost:$HGPORT
207 $ hg -R $TESTTMP/client2_pull pull -q http://localhost:$HGPORT
214 $ grep 'lfs' $TESTTMP/client2_pull/.hg/requires $SERVER_REQUIRES
208 $ grep 'lfs' $TESTTMP/client2_pull/.hg/requires $SERVER_REQUIRES
215 [1]
209 [1]
216
210
217 $ hg identify http://localhost:$HGPORT
211 $ hg identify http://localhost:$HGPORT
218 1477875038c6
212 1477875038c6
219
213
220 --------------------------------------------------------------------------------
214 --------------------------------------------------------------------------------
221 Case #3: client with lfs content and the extension enabled; server with
215 Case #3: client with lfs content and the extension enabled; server with
222 non-lfs content, and the extension state controlled by #testcases. The server
216 non-lfs content, and the extension state controlled by #testcases. The server
223 should have an 'lfs' requirement after it picks up its first commit with a blob.
217 should have an 'lfs' requirement after it picks up its first commit with a blob.
224
218
225 $ echo 'this is a big lfs file' > lfs.bin
219 $ echo 'this is a big lfs file' > lfs.bin
226 $ hg ci -Aqm 'lfs'
220 $ hg ci -Aqm 'lfs'
227 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
221 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
228 .hg/requires:lfs
222 .hg/requires:lfs
229
223
230 #if lfsremote-off
224 #if lfsremote-off
231 $ hg push -q
225 $ hg push -q
232 abort: required features are not supported in the destination: lfs
226 abort: required features are not supported in the destination: lfs
233 (enable the lfs extension on the server)
227 (enable the lfs extension on the server)
234 [255]
228 [255]
235 #else
229 #else
236 $ hg push -q
230 $ hg push -q
237 #endif
231 #endif
238 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
232 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
239 .hg/requires:lfs
233 .hg/requires:lfs
240 $TESTTMP/server/.hg/requires:lfs (lfsremote-on !)
234 $TESTTMP/server/.hg/requires:lfs (lfsremote-on !)
241
235
242 $ hg clone -q http://localhost:$HGPORT $TESTTMP/client3_clone
236 $ hg clone -q http://localhost:$HGPORT $TESTTMP/client3_clone
243 $ grep 'lfs' $TESTTMP/client3_clone/.hg/requires $SERVER_REQUIRES || true
237 $ grep 'lfs' $TESTTMP/client3_clone/.hg/requires $SERVER_REQUIRES || true
244 $TESTTMP/client3_clone/.hg/requires:lfs (lfsremote-on !)
238 $TESTTMP/client3_clone/.hg/requires:lfs (lfsremote-on !)
245 $TESTTMP/server/.hg/requires:lfs (lfsremote-on !)
239 $TESTTMP/server/.hg/requires:lfs (lfsremote-on !)
246
240
247 $ hg init $TESTTMP/client3_pull
241 $ hg init $TESTTMP/client3_pull
248 $ hg -R $TESTTMP/client3_pull pull -q http://localhost:$HGPORT
242 $ hg -R $TESTTMP/client3_pull pull -q http://localhost:$HGPORT
249 $ grep 'lfs' $TESTTMP/client3_pull/.hg/requires $SERVER_REQUIRES || true
243 $ grep 'lfs' $TESTTMP/client3_pull/.hg/requires $SERVER_REQUIRES || true
250 $TESTTMP/client3_pull/.hg/requires:lfs (lfsremote-on !)
244 $TESTTMP/client3_pull/.hg/requires:lfs (lfsremote-on !)
251 $TESTTMP/server/.hg/requires:lfs (lfsremote-on !)
245 $TESTTMP/server/.hg/requires:lfs (lfsremote-on !)
252
246
253 Test that the commit/changegroup requirement check hook can be run multiple
247 Test that the commit/changegroup requirement check hook can be run multiple
254 times.
248 times.
255
249
256 $ hg clone -qr 0 http://localhost:$HGPORT $TESTTMP/cmdserve_client3
250 $ hg clone -qr 0 http://localhost:$HGPORT $TESTTMP/cmdserve_client3
257
251
258 $ cd ../cmdserve_client3
252 $ cd ../cmdserve_client3
259
253
260 >>> from __future__ import absolute_import
254 >>> from __future__ import absolute_import
261 >>> from hgclient import check, readchannel, runcommand
255 >>> from hgclient import check, readchannel, runcommand
262 >>> @check
256 >>> @check
263 ... def addrequirement(server):
257 ... def addrequirement(server):
264 ... readchannel(server)
258 ... readchannel(server)
265 ... # change the repo in a way that adds the lfs requirement
259 ... # change the repo in a way that adds the lfs requirement
266 ... runcommand(server, [b'pull', b'-qu'])
260 ... runcommand(server, [b'pull', b'-qu'])
267 ... # Now cause the requirement adding hook to fire again, without going
261 ... # Now cause the requirement adding hook to fire again, without going
268 ... # through reposetup() again.
262 ... # through reposetup() again.
269 ... with open('file.txt', 'wb') as fp:
263 ... with open('file.txt', 'wb') as fp:
270 ... fp.write(b'data')
264 ... fp.write(b'data')
271 ... runcommand(server, [b'ci', b'-Aqm', b'non-lfs'])
265 ... runcommand(server, [b'ci', b'-Aqm', b'non-lfs'])
272 *** runcommand pull -qu
266 *** runcommand pull -qu
273 *** runcommand ci -Aqm non-lfs
267 *** runcommand ci -Aqm non-lfs
274
268
275 $ cd ../client
269 $ cd ../client
276
270
277 The difference here is the push failed above when the extension isn't
271 The difference here is the push failed above when the extension isn't
278 enabled on the server.
272 enabled on the server.
279 $ hg identify http://localhost:$HGPORT
273 $ hg identify http://localhost:$HGPORT
280 8374dc4052cb (lfsremote-on !)
274 8374dc4052cb (lfsremote-on !)
281 1477875038c6 (lfsremote-off !)
275 1477875038c6 (lfsremote-off !)
282
276
283 Don't bother testing the lfsremote-off cases- the server won't be able
277 Don't bother testing the lfsremote-off cases- the server won't be able
284 to launch if there's lfs content and the extension is disabled.
278 to launch if there's lfs content and the extension is disabled.
285
279
286 #if lfsremote-on
280 #if lfsremote-on
287
281
288 --------------------------------------------------------------------------------
282 --------------------------------------------------------------------------------
289 Case #4: client with non-lfs content and the extension disabled; server with
283 Case #4: client with non-lfs content and the extension disabled; server with
290 lfs content, and the extension enabled.
284 lfs content, and the extension enabled.
291
285
292 $ cat >> $HGRCPATH <<EOF
286 $ cat >> $HGRCPATH <<EOF
293 > [extensions]
287 > [extensions]
294 > lfs = !
288 > lfs = !
295 > EOF
289 > EOF
296
290
297 $ hg init $TESTTMP/client4
291 $ hg init $TESTTMP/client4
298 $ cd $TESTTMP/client4
292 $ cd $TESTTMP/client4
299 $ cat >> .hg/hgrc <<EOF
293 $ cat >> .hg/hgrc <<EOF
300 > [paths]
294 > [paths]
301 > default = http://localhost:$HGPORT
295 > default = http://localhost:$HGPORT
302 > EOF
296 > EOF
303 $ echo 'non-lfs' > nonlfs2.txt
297 $ echo 'non-lfs' > nonlfs2.txt
304 $ hg ci -Aqm 'non-lfs'
298 $ hg ci -Aqm 'non-lfs'
305 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
299 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
306 $TESTTMP/server/.hg/requires:lfs
300 $TESTTMP/server/.hg/requires:lfs
307
301
308 $ hg push -q --force
302 $ hg push -q --force
309 warning: repository is unrelated
303 warning: repository is unrelated
310 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
304 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
311 $TESTTMP/server/.hg/requires:lfs
305 $TESTTMP/server/.hg/requires:lfs
312
306
313 $ hg clone http://localhost:$HGPORT $TESTTMP/client4_clone
307 $ hg clone http://localhost:$HGPORT $TESTTMP/client4_clone
314 (remote is using large file support (lfs), but it is explicitly disabled in the local configuration)
308 (remote is using large file support (lfs), but it is explicitly disabled in the local configuration)
315 abort: repository requires features unknown to this Mercurial: lfs!
309 abort: repository requires features unknown to this Mercurial: lfs!
316 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
310 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
317 [255]
311 [255]
318 $ grep 'lfs' $TESTTMP/client4_clone/.hg/requires $SERVER_REQUIRES
312 $ grep 'lfs' $TESTTMP/client4_clone/.hg/requires $SERVER_REQUIRES
319 grep: $TESTTMP/client4_clone/.hg/requires: $ENOENT$
313 grep: $TESTTMP/client4_clone/.hg/requires: $ENOENT$
320 $TESTTMP/server/.hg/requires:lfs
314 $TESTTMP/server/.hg/requires:lfs
321 [2]
315 [2]
322
316
323 TODO: fail more gracefully.
317 TODO: fail more gracefully.
324
318
325 $ hg init $TESTTMP/client4_pull
319 $ hg init $TESTTMP/client4_pull
326 $ hg -R $TESTTMP/client4_pull pull http://localhost:$HGPORT
320 $ hg -R $TESTTMP/client4_pull pull http://localhost:$HGPORT
327 pulling from http://localhost:$HGPORT/
321 pulling from http://localhost:$HGPORT/
328 requesting all changes
322 requesting all changes
329 remote: abort: no common changegroup version
323 remote: abort: no common changegroup version
330 abort: pull failed on remote
324 abort: pull failed on remote
331 [255]
325 [255]
332 $ grep 'lfs' $TESTTMP/client4_pull/.hg/requires $SERVER_REQUIRES
326 $ grep 'lfs' $TESTTMP/client4_pull/.hg/requires $SERVER_REQUIRES
333 $TESTTMP/server/.hg/requires:lfs
327 $TESTTMP/server/.hg/requires:lfs
334
328
335 $ hg identify http://localhost:$HGPORT
329 $ hg identify http://localhost:$HGPORT
336 03b080fa9d93
330 03b080fa9d93
337
331
338 --------------------------------------------------------------------------------
332 --------------------------------------------------------------------------------
339 Case #5: client with non-lfs content and the extension enabled; server with
333 Case #5: client with non-lfs content and the extension enabled; server with
340 lfs content, and the extension enabled.
334 lfs content, and the extension enabled.
341
335
342 $ cat >> $HGRCPATH <<EOF
336 $ cat >> $HGRCPATH <<EOF
343 > [extensions]
337 > [extensions]
344 > lfs =
338 > lfs =
345 > EOF
339 > EOF
346 $ echo 'non-lfs' > nonlfs3.txt
340 $ echo 'non-lfs' > nonlfs3.txt
347 $ hg ci -Aqm 'non-lfs file with lfs client'
341 $ hg ci -Aqm 'non-lfs file with lfs client'
348
342
349 $ hg push -q
343 $ hg push -q
350 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
344 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
351 $TESTTMP/server/.hg/requires:lfs
345 $TESTTMP/server/.hg/requires:lfs
352
346
353 $ hg clone -q http://localhost:$HGPORT $TESTTMP/client5_clone
347 $ hg clone -q http://localhost:$HGPORT $TESTTMP/client5_clone
354 $ grep 'lfs' $TESTTMP/client5_clone/.hg/requires $SERVER_REQUIRES
348 $ grep 'lfs' $TESTTMP/client5_clone/.hg/requires $SERVER_REQUIRES
355 $TESTTMP/client5_clone/.hg/requires:lfs
349 $TESTTMP/client5_clone/.hg/requires:lfs
356 $TESTTMP/server/.hg/requires:lfs
350 $TESTTMP/server/.hg/requires:lfs
357
351
358 $ hg init $TESTTMP/client5_pull
352 $ hg init $TESTTMP/client5_pull
359 $ hg -R $TESTTMP/client5_pull pull -q http://localhost:$HGPORT
353 $ hg -R $TESTTMP/client5_pull pull -q http://localhost:$HGPORT
360 $ grep 'lfs' $TESTTMP/client5_pull/.hg/requires $SERVER_REQUIRES
354 $ grep 'lfs' $TESTTMP/client5_pull/.hg/requires $SERVER_REQUIRES
361 $TESTTMP/client5_pull/.hg/requires:lfs
355 $TESTTMP/client5_pull/.hg/requires:lfs
362 $TESTTMP/server/.hg/requires:lfs
356 $TESTTMP/server/.hg/requires:lfs
363
357
364 $ hg identify http://localhost:$HGPORT
358 $ hg identify http://localhost:$HGPORT
365 c729025cc5e3
359 c729025cc5e3
366
360
367 $ mv $HGRCPATH $HGRCPATH.tmp
361 $ mv $HGRCPATH $HGRCPATH.tmp
368 $ cp $HGRCPATH.orig $HGRCPATH
362 $ cp $HGRCPATH.orig $HGRCPATH
369
363
370 >>> from __future__ import absolute_import
364 >>> from __future__ import absolute_import
371 >>> from hgclient import bprint, check, readchannel, runcommand, stdout
365 >>> from hgclient import bprint, check, readchannel, runcommand, stdout
372 >>> @check
366 >>> @check
373 ... def checkflags(server):
367 ... def checkflags(server):
374 ... readchannel(server)
368 ... readchannel(server)
375 ... bprint(b'')
369 ... bprint(b'')
376 ... bprint(b'# LFS required- both lfs and non-lfs revlogs have 0x2000 flag')
370 ... bprint(b'# LFS required- both lfs and non-lfs revlogs have 0x2000 flag')
377 ... stdout.flush()
371 ... stdout.flush()
378 ... runcommand(server, [b'debugprocessors', b'lfs.bin', b'-R',
372 ... runcommand(server, [b'debugprocessors', b'lfs.bin', b'-R',
379 ... b'../server'])
373 ... b'../server'])
380 ... runcommand(server, [b'debugprocessors', b'nonlfs2.txt', b'-R',
374 ... runcommand(server, [b'debugprocessors', b'nonlfs2.txt', b'-R',
381 ... b'../server'])
375 ... b'../server'])
382 ... runcommand(server, [b'config', b'extensions', b'--cwd',
376 ... runcommand(server, [b'config', b'extensions', b'--cwd',
383 ... b'../server'])
377 ... b'../server'])
384 ...
378 ...
385 ... bprint(b"\n# LFS not enabled- revlogs don't have 0x2000 flag")
379 ... bprint(b"\n# LFS not enabled- revlogs don't have 0x2000 flag")
386 ... stdout.flush()
380 ... stdout.flush()
387 ... runcommand(server, [b'debugprocessors', b'nonlfs3.txt'])
381 ... runcommand(server, [b'debugprocessors', b'nonlfs3.txt'])
388 ... runcommand(server, [b'config', b'extensions'])
382 ... runcommand(server, [b'config', b'extensions'])
389
383
390 # LFS required- both lfs and non-lfs revlogs have 0x2000 flag
384 # LFS required- both lfs and non-lfs revlogs have 0x2000 flag
391 *** runcommand debugprocessors lfs.bin -R ../server
385 *** runcommand debugprocessors lfs.bin -R ../server
392 registered processor '0x8000'
386 registered processor '0x8000'
393 registered processor '0x2000'
387 registered processor '0x2000'
394 *** runcommand debugprocessors nonlfs2.txt -R ../server
388 *** runcommand debugprocessors nonlfs2.txt -R ../server
395 registered processor '0x8000'
389 registered processor '0x8000'
396 registered processor '0x2000'
390 registered processor '0x2000'
397 *** runcommand config extensions --cwd ../server
391 *** runcommand config extensions --cwd ../server
398 extensions.debugprocessors=$TESTTMP/debugprocessors.py
392 extensions.debugprocessors=$TESTTMP/debugprocessors.py
399 extensions.lfs=
393 extensions.lfs=
400
394
401 # LFS not enabled- revlogs don't have 0x2000 flag
395 # LFS not enabled- revlogs don't have 0x2000 flag
402 *** runcommand debugprocessors nonlfs3.txt
396 *** runcommand debugprocessors nonlfs3.txt
403 registered processor '0x8000'
397 registered processor '0x8000'
404 *** runcommand config extensions
398 *** runcommand config extensions
405 extensions.debugprocessors=$TESTTMP/debugprocessors.py
399 extensions.debugprocessors=$TESTTMP/debugprocessors.py
406
400
407 $ rm $HGRCPATH
401 $ rm $HGRCPATH
408 $ mv $HGRCPATH.tmp $HGRCPATH
402 $ mv $HGRCPATH.tmp $HGRCPATH
409
403
410 $ hg clone $TESTTMP/client $TESTTMP/nonlfs -qr 0 --config extensions.lfs=
404 $ hg clone $TESTTMP/client $TESTTMP/nonlfs -qr 0 --config extensions.lfs=
411 $ cat >> $TESTTMP/nonlfs/.hg/hgrc <<EOF
405 $ cat >> $TESTTMP/nonlfs/.hg/hgrc <<EOF
412 > [extensions]
406 > [extensions]
413 > lfs = !
407 > lfs = !
414 > EOF
408 > EOF
415
409
416 >>> from __future__ import absolute_import, print_function
410 >>> from __future__ import absolute_import, print_function
417 >>> from hgclient import bprint, check, readchannel, runcommand, stdout
411 >>> from hgclient import bprint, check, readchannel, runcommand, stdout
418 >>> @check
412 >>> @check
419 ... def checkflags2(server):
413 ... def checkflags2(server):
420 ... readchannel(server)
414 ... readchannel(server)
421 ... bprint(b'')
415 ... bprint(b'')
422 ... bprint(b'# LFS enabled- both lfs and non-lfs revlogs have 0x2000 flag')
416 ... bprint(b'# LFS enabled- both lfs and non-lfs revlogs have 0x2000 flag')
423 ... stdout.flush()
417 ... stdout.flush()
424 ... runcommand(server, [b'debugprocessors', b'lfs.bin', b'-R',
418 ... runcommand(server, [b'debugprocessors', b'lfs.bin', b'-R',
425 ... b'../server'])
419 ... b'../server'])
426 ... runcommand(server, [b'debugprocessors', b'nonlfs2.txt', b'-R',
420 ... runcommand(server, [b'debugprocessors', b'nonlfs2.txt', b'-R',
427 ... b'../server'])
421 ... b'../server'])
428 ... runcommand(server, [b'config', b'extensions', b'--cwd',
422 ... runcommand(server, [b'config', b'extensions', b'--cwd',
429 ... b'../server'])
423 ... b'../server'])
430 ...
424 ...
431 ... bprint(b'\n# LFS enabled without requirement- revlogs have 0x2000 flag')
425 ... bprint(b'\n# LFS enabled without requirement- revlogs have 0x2000 flag')
432 ... stdout.flush()
426 ... stdout.flush()
433 ... runcommand(server, [b'debugprocessors', b'nonlfs3.txt'])
427 ... runcommand(server, [b'debugprocessors', b'nonlfs3.txt'])
434 ... runcommand(server, [b'config', b'extensions'])
428 ... runcommand(server, [b'config', b'extensions'])
435 ...
429 ...
436 ... bprint(b"\n# LFS disabled locally- revlogs don't have 0x2000 flag")
430 ... bprint(b"\n# LFS disabled locally- revlogs don't have 0x2000 flag")
437 ... stdout.flush()
431 ... stdout.flush()
438 ... runcommand(server, [b'debugprocessors', b'nonlfs.txt', b'-R',
432 ... runcommand(server, [b'debugprocessors', b'nonlfs.txt', b'-R',
439 ... b'../nonlfs'])
433 ... b'../nonlfs'])
440 ... runcommand(server, [b'config', b'extensions', b'--cwd',
434 ... runcommand(server, [b'config', b'extensions', b'--cwd',
441 ... b'../nonlfs'])
435 ... b'../nonlfs'])
442
436
443 # LFS enabled- both lfs and non-lfs revlogs have 0x2000 flag
437 # LFS enabled- both lfs and non-lfs revlogs have 0x2000 flag
444 *** runcommand debugprocessors lfs.bin -R ../server
438 *** runcommand debugprocessors lfs.bin -R ../server
445 registered processor '0x8000'
439 registered processor '0x8000'
446 registered processor '0x2000'
440 registered processor '0x2000'
447 *** runcommand debugprocessors nonlfs2.txt -R ../server
441 *** runcommand debugprocessors nonlfs2.txt -R ../server
448 registered processor '0x8000'
442 registered processor '0x8000'
449 registered processor '0x2000'
443 registered processor '0x2000'
450 *** runcommand config extensions --cwd ../server
444 *** runcommand config extensions --cwd ../server
451 extensions.debugprocessors=$TESTTMP/debugprocessors.py
445 extensions.debugprocessors=$TESTTMP/debugprocessors.py
452 extensions.lfs=
446 extensions.lfs=
453
447
454 # LFS enabled without requirement- revlogs have 0x2000 flag
448 # LFS enabled without requirement- revlogs have 0x2000 flag
455 *** runcommand debugprocessors nonlfs3.txt
449 *** runcommand debugprocessors nonlfs3.txt
456 registered processor '0x8000'
450 registered processor '0x8000'
457 registered processor '0x2000'
451 registered processor '0x2000'
458 *** runcommand config extensions
452 *** runcommand config extensions
459 extensions.debugprocessors=$TESTTMP/debugprocessors.py
453 extensions.debugprocessors=$TESTTMP/debugprocessors.py
460 extensions.lfs=
454 extensions.lfs=
461
455
462 # LFS disabled locally- revlogs don't have 0x2000 flag
456 # LFS disabled locally- revlogs don't have 0x2000 flag
463 *** runcommand debugprocessors nonlfs.txt -R ../nonlfs
457 *** runcommand debugprocessors nonlfs.txt -R ../nonlfs
464 registered processor '0x8000'
458 registered processor '0x8000'
465 *** runcommand config extensions --cwd ../nonlfs
459 *** runcommand config extensions --cwd ../nonlfs
466 extensions.debugprocessors=$TESTTMP/debugprocessors.py
460 extensions.debugprocessors=$TESTTMP/debugprocessors.py
467 extensions.lfs=!
461 extensions.lfs=!
468
462
469 --------------------------------------------------------------------------------
463 --------------------------------------------------------------------------------
470 Case #6: client with lfs content and the extension enabled; server with
464 Case #6: client with lfs content and the extension enabled; server with
471 lfs content, and the extension enabled.
465 lfs content, and the extension enabled.
472
466
473 $ echo 'this is another lfs file' > lfs2.txt
467 $ echo 'this is another lfs file' > lfs2.txt
474 $ hg ci -Aqm 'lfs file with lfs client'
468 $ hg ci -Aqm 'lfs file with lfs client'
475
469
476 $ hg --config paths.default= push -v http://localhost:$HGPORT
470 $ hg --config paths.default= push -v http://localhost:$HGPORT
477 pushing to http://localhost:$HGPORT/
471 pushing to http://localhost:$HGPORT/
478 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
472 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
479 searching for changes
473 searching for changes
480 remote has heads on branch 'default' that are not known locally: 8374dc4052cb
474 remote has heads on branch 'default' that are not known locally: 8374dc4052cb
481 lfs: uploading a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de (25 bytes)
475 lfs: uploading a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de (25 bytes)
482 lfs: processed: a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de
476 lfs: processed: a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de
483 lfs: uploaded 1 files (25 bytes)
477 lfs: uploaded 1 files (25 bytes)
484 1 changesets found
478 1 changesets found
485 uncompressed size of bundle content:
479 uncompressed size of bundle content:
486 206 (changelog)
480 206 (changelog)
487 172 (manifests)
481 172 (manifests)
488 275 lfs2.txt
482 275 lfs2.txt
489 remote: adding changesets
483 remote: adding changesets
490 remote: adding manifests
484 remote: adding manifests
491 remote: adding file changes
485 remote: adding file changes
492 remote: added 1 changesets with 1 changes to 1 files
486 remote: added 1 changesets with 1 changes to 1 files
493 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
487 $ grep 'lfs' .hg/requires $SERVER_REQUIRES
494 .hg/requires:lfs
488 .hg/requires:lfs
495 $TESTTMP/server/.hg/requires:lfs
489 $TESTTMP/server/.hg/requires:lfs
496
490
497 $ hg clone -q http://localhost:$HGPORT $TESTTMP/client6_clone
491 $ hg clone -q http://localhost:$HGPORT $TESTTMP/client6_clone
498 $ grep 'lfs' $TESTTMP/client6_clone/.hg/requires $SERVER_REQUIRES
492 $ grep 'lfs' $TESTTMP/client6_clone/.hg/requires $SERVER_REQUIRES
499 $TESTTMP/client6_clone/.hg/requires:lfs
493 $TESTTMP/client6_clone/.hg/requires:lfs
500 $TESTTMP/server/.hg/requires:lfs
494 $TESTTMP/server/.hg/requires:lfs
501
495
502 $ hg init $TESTTMP/client6_pull
496 $ hg init $TESTTMP/client6_pull
503 $ hg -R $TESTTMP/client6_pull pull -u -v http://localhost:$HGPORT
497 $ hg -R $TESTTMP/client6_pull pull -u -v http://localhost:$HGPORT
504 pulling from http://localhost:$HGPORT/
498 pulling from http://localhost:$HGPORT/
505 requesting all changes
499 requesting all changes
506 adding changesets
500 adding changesets
507 adding manifests
501 adding manifests
508 adding file changes
502 adding file changes
509 calling hook pretxnchangegroup.lfs: hgext.lfs.checkrequireslfs
503 calling hook pretxnchangegroup.lfs: hgext.lfs.checkrequireslfs
510 added 6 changesets with 5 changes to 5 files (+1 heads)
504 added 6 changesets with 5 changes to 5 files (+1 heads)
511 new changesets d437e1d24fbd:d3b84d50eacb
505 new changesets d437e1d24fbd:d3b84d50eacb
512 resolving manifests
506 resolving manifests
513 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
507 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
514 lfs: downloading a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de (25 bytes)
508 lfs: downloading a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de (25 bytes)
515 lfs: processed: a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de
509 lfs: processed: a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de
516 lfs: downloaded 1 files (25 bytes)
510 lfs: downloaded 1 files (25 bytes)
517 getting lfs2.txt
511 getting lfs2.txt
518 lfs: found a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de in the local lfs store
512 lfs: found a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de in the local lfs store
519 getting nonlfs2.txt
513 getting nonlfs2.txt
520 getting nonlfs3.txt
514 getting nonlfs3.txt
521 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
515 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
522 updated to "d3b84d50eacb: lfs file with lfs client"
516 updated to "d3b84d50eacb: lfs file with lfs client"
523 1 other heads for branch "default"
517 1 other heads for branch "default"
524 (sent 3 HTTP requests and * bytes; received * bytes in responses) (glob)
518 (sent 3 HTTP requests and * bytes; received * bytes in responses) (glob)
525 $ grep 'lfs' $TESTTMP/client6_pull/.hg/requires $SERVER_REQUIRES
519 $ grep 'lfs' $TESTTMP/client6_pull/.hg/requires $SERVER_REQUIRES
526 $TESTTMP/client6_pull/.hg/requires:lfs
520 $TESTTMP/client6_pull/.hg/requires:lfs
527 $TESTTMP/server/.hg/requires:lfs
521 $TESTTMP/server/.hg/requires:lfs
528
522
529 $ hg identify http://localhost:$HGPORT
523 $ hg identify http://localhost:$HGPORT
530 d3b84d50eacb
524 d3b84d50eacb
531
525
532 --------------------------------------------------------------------------------
526 --------------------------------------------------------------------------------
533 Misc: process dies early if a requirement exists and the extension is disabled
527 Misc: process dies early if a requirement exists and the extension is disabled
534
528
535 $ hg --config extensions.lfs=! summary
529 $ hg --config extensions.lfs=! summary
536 abort: repository requires features unknown to this Mercurial: lfs!
530 abort: repository requires features unknown to this Mercurial: lfs!
537 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
531 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
538 [255]
532 [255]
539
533
540 $ echo 'this is an lfs file' > $TESTTMP/client6_clone/lfspair1.bin
534 $ echo 'this is an lfs file' > $TESTTMP/client6_clone/lfspair1.bin
541 $ echo 'this is an lfs file too' > $TESTTMP/client6_clone/lfspair2.bin
535 $ echo 'this is an lfs file too' > $TESTTMP/client6_clone/lfspair2.bin
542 $ hg -R $TESTTMP/client6_clone ci -Aqm 'add lfs pair'
536 $ hg -R $TESTTMP/client6_clone ci -Aqm 'add lfs pair'
543 $ hg -R $TESTTMP/client6_clone push -q
537 $ hg -R $TESTTMP/client6_clone push -q
544
538
545 $ hg clone -qU http://localhost:$HGPORT $TESTTMP/bulkfetch
539 $ hg clone -qU http://localhost:$HGPORT $TESTTMP/bulkfetch
546
540
547 Cat doesn't prefetch unless data is needed (e.g. '-T {rawdata}' doesn't need it)
541 Cat doesn't prefetch unless data is needed (e.g. '-T {rawdata}' doesn't need it)
548
542
549 $ hg --cwd $TESTTMP/bulkfetch cat -vr tip lfspair1.bin -T '{rawdata}\n{path}\n'
543 $ hg --cwd $TESTTMP/bulkfetch cat -vr tip lfspair1.bin -T '{rawdata}\n{path}\n'
550 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
544 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
551 version https://git-lfs.github.com/spec/v1
545 version https://git-lfs.github.com/spec/v1
552 oid sha256:cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782
546 oid sha256:cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782
553 size 20
547 size 20
554 x-is-binary 0
548 x-is-binary 0
555
549
556 lfspair1.bin
550 lfspair1.bin
557
551
558 $ hg --cwd $TESTTMP/bulkfetch cat -vr tip lfspair1.bin -T json
552 $ hg --cwd $TESTTMP/bulkfetch cat -vr tip lfspair1.bin -T json
559 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
553 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
560 [lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
554 [lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
561 lfs: downloading cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782 (20 bytes)
555 lfs: downloading cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782 (20 bytes)
562 lfs: processed: cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782
556 lfs: processed: cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782
563 lfs: downloaded 1 files (20 bytes)
557 lfs: downloaded 1 files (20 bytes)
564 lfs: found cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782 in the local lfs store
558 lfs: found cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782 in the local lfs store
565
559
566 {
560 {
567 "data": "this is an lfs file\n",
561 "data": "this is an lfs file\n",
568 "path": "lfspair1.bin",
562 "path": "lfspair1.bin",
569 "rawdata": "version https://git-lfs.github.com/spec/v1\noid sha256:cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782\nsize 20\nx-is-binary 0\n"
563 "rawdata": "version https://git-lfs.github.com/spec/v1\noid sha256:cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782\nsize 20\nx-is-binary 0\n"
570 }
564 }
571 ]
565 ]
572
566
573 $ rm -r $TESTTMP/bulkfetch/.hg/store/lfs
567 $ rm -r $TESTTMP/bulkfetch/.hg/store/lfs
574
568
575 $ hg --cwd $TESTTMP/bulkfetch cat -vr tip lfspair1.bin -T '{data}\n'
569 $ hg --cwd $TESTTMP/bulkfetch cat -vr tip lfspair1.bin -T '{data}\n'
576 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
570 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
577 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
571 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
578 lfs: downloading cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782 (20 bytes)
572 lfs: downloading cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782 (20 bytes)
579 lfs: processed: cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782
573 lfs: processed: cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782
580 lfs: downloaded 1 files (20 bytes)
574 lfs: downloaded 1 files (20 bytes)
581 lfs: found cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782 in the local lfs store
575 lfs: found cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782 in the local lfs store
582 this is an lfs file
576 this is an lfs file
583
577
584 $ hg --cwd $TESTTMP/bulkfetch cat -vr tip lfspair2.bin
578 $ hg --cwd $TESTTMP/bulkfetch cat -vr tip lfspair2.bin
585 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
579 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
586 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
580 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
587 lfs: downloading d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e (24 bytes)
581 lfs: downloading d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e (24 bytes)
588 lfs: processed: d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e
582 lfs: processed: d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e
589 lfs: downloaded 1 files (24 bytes)
583 lfs: downloaded 1 files (24 bytes)
590 lfs: found d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e in the local lfs store
584 lfs: found d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e in the local lfs store
591 this is an lfs file too
585 this is an lfs file too
592
586
593 Export will prefetch all needed files across all needed revisions
587 Export will prefetch all needed files across all needed revisions
594
588
595 $ rm -r $TESTTMP/bulkfetch/.hg/store/lfs
589 $ rm -r $TESTTMP/bulkfetch/.hg/store/lfs
596 $ hg -R $TESTTMP/bulkfetch -v export -r 0:tip -o all.export
590 $ hg -R $TESTTMP/bulkfetch -v export -r 0:tip -o all.export
597 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
591 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
598 exporting patches:
592 exporting patches:
599 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
593 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
600 lfs: need to transfer 4 objects (92 bytes)
594 lfs: need to transfer 4 objects (92 bytes)
601 lfs: downloading a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de (25 bytes)
595 lfs: downloading a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de (25 bytes)
602 lfs: processed: a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de
596 lfs: processed: a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de
603 lfs: downloading bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc (23 bytes)
597 lfs: downloading bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc (23 bytes)
604 lfs: processed: bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc
598 lfs: processed: bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc
605 lfs: downloading cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782 (20 bytes)
599 lfs: downloading cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782 (20 bytes)
606 lfs: processed: cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782
600 lfs: processed: cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782
607 lfs: downloading d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e (24 bytes)
601 lfs: downloading d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e (24 bytes)
608 lfs: processed: d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e
602 lfs: processed: d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e
609 lfs: downloaded 4 files (92 bytes)
603 lfs: downloaded 4 files (92 bytes)
610 all.export
604 all.export
611 lfs: found bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc in the local lfs store
605 lfs: found bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc in the local lfs store
612 lfs: found a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de in the local lfs store
606 lfs: found a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de in the local lfs store
613 lfs: found cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782 in the local lfs store
607 lfs: found cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782 in the local lfs store
614 lfs: found d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e in the local lfs store
608 lfs: found d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e in the local lfs store
615
609
616 Export with selected files is used with `extdiff --patch`
610 Export with selected files is used with `extdiff --patch`
617
611
618 $ rm -r $TESTTMP/bulkfetch/.hg/store/lfs
612 $ rm -r $TESTTMP/bulkfetch/.hg/store/lfs
619 $ hg --config extensions.extdiff= \
613 $ hg --config extensions.extdiff= \
620 > -R $TESTTMP/bulkfetch -v extdiff -r 2:tip --patch $TESTTMP/bulkfetch/lfs.bin
614 > -R $TESTTMP/bulkfetch -v extdiff -r 2:tip --patch $TESTTMP/bulkfetch/lfs.bin
621 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
615 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
622 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
616 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
623 lfs: downloading bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc (23 bytes)
617 lfs: downloading bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc (23 bytes)
624 lfs: processed: bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc
618 lfs: processed: bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc
625 lfs: downloaded 1 files (23 bytes)
619 lfs: downloaded 1 files (23 bytes)
626 */hg-8374dc4052cb.patch (glob)
620 */hg-8374dc4052cb.patch (glob)
627 lfs: found bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc in the local lfs store
621 lfs: found bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc in the local lfs store
628 */hg-9640b57e77b1.patch (glob)
622 */hg-9640b57e77b1.patch (glob)
629 --- */hg-8374dc4052cb.patch * (glob)
623 --- */hg-8374dc4052cb.patch * (glob)
630 +++ */hg-9640b57e77b1.patch * (glob)
624 +++ */hg-9640b57e77b1.patch * (glob)
631 @@ -2,12 +2,7 @@
625 @@ -2,12 +2,7 @@
632 # User test
626 # User test
633 # Date 0 0
627 # Date 0 0
634 # Thu Jan 01 00:00:00 1970 +0000
628 # Thu Jan 01 00:00:00 1970 +0000
635 -# Node ID 8374dc4052cbd388e79d9dc4ddb29784097aa354
629 -# Node ID 8374dc4052cbd388e79d9dc4ddb29784097aa354
636 -# Parent 1477875038c60152e391238920a16381c627b487
630 -# Parent 1477875038c60152e391238920a16381c627b487
637 -lfs
631 -lfs
638 +# Node ID 9640b57e77b14c3a0144fb4478b6cc13e13ea0d1
632 +# Node ID 9640b57e77b14c3a0144fb4478b6cc13e13ea0d1
639 +# Parent d3b84d50eacbd56638e11abce6b8616aaba54420
633 +# Parent d3b84d50eacbd56638e11abce6b8616aaba54420
640 +add lfs pair
634 +add lfs pair
641
635
642 -diff -r 1477875038c6 -r 8374dc4052cb lfs.bin
636 -diff -r 1477875038c6 -r 8374dc4052cb lfs.bin
643 ---- /dev/null Thu Jan 01 00:00:00 1970 +0000
637 ---- /dev/null Thu Jan 01 00:00:00 1970 +0000
644 -+++ b/lfs.bin Thu Jan 01 00:00:00 1970 +0000
638 -+++ b/lfs.bin Thu Jan 01 00:00:00 1970 +0000
645 -@@ -0,0 +1,1 @@
639 -@@ -0,0 +1,1 @@
646 -+this is a big lfs file
640 -+this is a big lfs file
647 cleaning up temp directory
641 cleaning up temp directory
648 [1]
642 [1]
649
643
650 Diff will prefetch files
644 Diff will prefetch files
651
645
652 $ rm -r $TESTTMP/bulkfetch/.hg/store/lfs
646 $ rm -r $TESTTMP/bulkfetch/.hg/store/lfs
653 $ hg -R $TESTTMP/bulkfetch -v diff -r 2:tip
647 $ hg -R $TESTTMP/bulkfetch -v diff -r 2:tip
654 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
648 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
655 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
649 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
656 lfs: need to transfer 4 objects (92 bytes)
650 lfs: need to transfer 4 objects (92 bytes)
657 lfs: downloading a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de (25 bytes)
651 lfs: downloading a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de (25 bytes)
658 lfs: processed: a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de
652 lfs: processed: a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de
659 lfs: downloading bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc (23 bytes)
653 lfs: downloading bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc (23 bytes)
660 lfs: processed: bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc
654 lfs: processed: bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc
661 lfs: downloading cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782 (20 bytes)
655 lfs: downloading cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782 (20 bytes)
662 lfs: processed: cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782
656 lfs: processed: cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782
663 lfs: downloading d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e (24 bytes)
657 lfs: downloading d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e (24 bytes)
664 lfs: processed: d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e
658 lfs: processed: d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e
665 lfs: downloaded 4 files (92 bytes)
659 lfs: downloaded 4 files (92 bytes)
666 lfs: found bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc in the local lfs store
660 lfs: found bed80f00180ac404b843628ab56a1c1984d6145c391cd1628a7dd7d2598d71fc in the local lfs store
667 lfs: found a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de in the local lfs store
661 lfs: found a82f1c5cea0d40e3bb3a849686bb4e6ae47ca27e614de55c1ed0325698ef68de in the local lfs store
668 lfs: found cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782 in the local lfs store
662 lfs: found cf1b2787b74e66547d931b6ebe28ff63303e803cb2baa14a8f57c4383d875782 in the local lfs store
669 lfs: found d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e in the local lfs store
663 lfs: found d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e in the local lfs store
670 diff -r 8374dc4052cb -r 9640b57e77b1 lfs.bin
664 diff -r 8374dc4052cb -r 9640b57e77b1 lfs.bin
671 --- a/lfs.bin Thu Jan 01 00:00:00 1970 +0000
665 --- a/lfs.bin Thu Jan 01 00:00:00 1970 +0000
672 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
666 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
673 @@ -1,1 +0,0 @@
667 @@ -1,1 +0,0 @@
674 -this is a big lfs file
668 -this is a big lfs file
675 diff -r 8374dc4052cb -r 9640b57e77b1 lfs2.txt
669 diff -r 8374dc4052cb -r 9640b57e77b1 lfs2.txt
676 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
670 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
677 +++ b/lfs2.txt Thu Jan 01 00:00:00 1970 +0000
671 +++ b/lfs2.txt Thu Jan 01 00:00:00 1970 +0000
678 @@ -0,0 +1,1 @@
672 @@ -0,0 +1,1 @@
679 +this is another lfs file
673 +this is another lfs file
680 diff -r 8374dc4052cb -r 9640b57e77b1 lfspair1.bin
674 diff -r 8374dc4052cb -r 9640b57e77b1 lfspair1.bin
681 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
675 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
682 +++ b/lfspair1.bin Thu Jan 01 00:00:00 1970 +0000
676 +++ b/lfspair1.bin Thu Jan 01 00:00:00 1970 +0000
683 @@ -0,0 +1,1 @@
677 @@ -0,0 +1,1 @@
684 +this is an lfs file
678 +this is an lfs file
685 diff -r 8374dc4052cb -r 9640b57e77b1 lfspair2.bin
679 diff -r 8374dc4052cb -r 9640b57e77b1 lfspair2.bin
686 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
680 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
687 +++ b/lfspair2.bin Thu Jan 01 00:00:00 1970 +0000
681 +++ b/lfspair2.bin Thu Jan 01 00:00:00 1970 +0000
688 @@ -0,0 +1,1 @@
682 @@ -0,0 +1,1 @@
689 +this is an lfs file too
683 +this is an lfs file too
690 diff -r 8374dc4052cb -r 9640b57e77b1 nonlfs.txt
684 diff -r 8374dc4052cb -r 9640b57e77b1 nonlfs.txt
691 --- a/nonlfs.txt Thu Jan 01 00:00:00 1970 +0000
685 --- a/nonlfs.txt Thu Jan 01 00:00:00 1970 +0000
692 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
686 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
693 @@ -1,1 +0,0 @@
687 @@ -1,1 +0,0 @@
694 -non-lfs
688 -non-lfs
695 diff -r 8374dc4052cb -r 9640b57e77b1 nonlfs3.txt
689 diff -r 8374dc4052cb -r 9640b57e77b1 nonlfs3.txt
696 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
690 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
697 +++ b/nonlfs3.txt Thu Jan 01 00:00:00 1970 +0000
691 +++ b/nonlfs3.txt Thu Jan 01 00:00:00 1970 +0000
698 @@ -0,0 +1,1 @@
692 @@ -0,0 +1,1 @@
699 +non-lfs
693 +non-lfs
700
694
701 Only the files required by diff are prefetched
695 Only the files required by diff are prefetched
702
696
703 $ rm -r $TESTTMP/bulkfetch/.hg/store/lfs
697 $ rm -r $TESTTMP/bulkfetch/.hg/store/lfs
704 $ hg -R $TESTTMP/bulkfetch -v diff -r 2:tip $TESTTMP/bulkfetch/lfspair2.bin
698 $ hg -R $TESTTMP/bulkfetch -v diff -r 2:tip $TESTTMP/bulkfetch/lfspair2.bin
705 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
699 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
706 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
700 lfs: assuming remote store: http://localhost:$HGPORT/.git/info/lfs
707 lfs: downloading d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e (24 bytes)
701 lfs: downloading d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e (24 bytes)
708 lfs: processed: d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e
702 lfs: processed: d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e
709 lfs: downloaded 1 files (24 bytes)
703 lfs: downloaded 1 files (24 bytes)
710 lfs: found d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e in the local lfs store
704 lfs: found d96eda2c74b56e95cfb5ffb66b6503e198cc6fc4a09dc877de925feebc65786e in the local lfs store
711 diff -r 8374dc4052cb -r 9640b57e77b1 lfspair2.bin
705 diff -r 8374dc4052cb -r 9640b57e77b1 lfspair2.bin
712 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
706 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
713 +++ b/lfspair2.bin Thu Jan 01 00:00:00 1970 +0000
707 +++ b/lfspair2.bin Thu Jan 01 00:00:00 1970 +0000
714 @@ -0,0 +1,1 @@
708 @@ -0,0 +1,1 @@
715 +this is an lfs file too
709 +this is an lfs file too
716
710
717 #endif
711 #endif
718
712
719 $ "$PYTHON" $TESTDIR/killdaemons.py $DAEMON_PIDS
713 $ "$PYTHON" $TESTDIR/killdaemons.py $DAEMON_PIDS
720
714
721 $ cat $TESTTMP/errors.log
715 $ cat $TESTTMP/errors.log
@@ -1,2790 +1,2790 b''
1 Log on empty repository: checking consistency
1 Log on empty repository: checking consistency
2
2
3 $ hg init empty
3 $ hg init empty
4 $ cd empty
4 $ cd empty
5 $ hg log
5 $ hg log
6 $ hg log -r 1
6 $ hg log -r 1
7 abort: unknown revision '1'!
7 abort: unknown revision '1'!
8 [255]
8 [255]
9 $ hg log -r -1:0
9 $ hg log -r -1:0
10 abort: unknown revision '-1'!
10 abort: unknown revision '-1'!
11 [255]
11 [255]
12 $ hg log -r 'branch(name)'
12 $ hg log -r 'branch(name)'
13 abort: unknown revision 'name'!
13 abort: unknown revision 'name'!
14 [255]
14 [255]
15 $ hg log -r null -q
15 $ hg log -r null -q
16 -1:000000000000
16 -1:000000000000
17
17
18 $ cd ..
18 $ cd ..
19
19
20 The g is crafted to have 2 filelog topological heads in a linear
20 The g is crafted to have 2 filelog topological heads in a linear
21 changeset graph
21 changeset graph
22
22
23 $ hg init a
23 $ hg init a
24 $ cd a
24 $ cd a
25 $ echo a > a
25 $ echo a > a
26 $ echo f > f
26 $ echo f > f
27 $ hg ci -Ama -d '1 0'
27 $ hg ci -Ama -d '1 0'
28 adding a
28 adding a
29 adding f
29 adding f
30
30
31 $ hg cp a b
31 $ hg cp a b
32 $ hg cp f g
32 $ hg cp f g
33 $ hg ci -mb -d '2 0'
33 $ hg ci -mb -d '2 0'
34
34
35 $ mkdir dir
35 $ mkdir dir
36 $ hg mv b dir
36 $ hg mv b dir
37 $ echo g >> g
37 $ echo g >> g
38 $ echo f >> f
38 $ echo f >> f
39 $ hg ci -mc -d '3 0'
39 $ hg ci -mc -d '3 0'
40
40
41 $ hg mv a b
41 $ hg mv a b
42 $ hg cp -f f g
42 $ hg cp -f f g
43 $ echo a > d
43 $ echo a > d
44 $ hg add d
44 $ hg add d
45 $ hg ci -md -d '4 0'
45 $ hg ci -md -d '4 0'
46
46
47 $ hg mv dir/b e
47 $ hg mv dir/b e
48 $ hg ci -me -d '5 0'
48 $ hg ci -me -d '5 0'
49
49
50 Make sure largefiles doesn't interfere with logging a regular file
50 Make sure largefiles doesn't interfere with logging a regular file
51 $ hg --debug log a -T '{rev}: {desc}\n' --config extensions.largefiles=
51 $ hg --debug log a -T '{rev}: {desc}\n' --config extensions.largefiles=
52 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
52 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
53 updated patterns: .hglf/a, a
53 updated patterns: .hglf/a, a
54 0: a
54 0: a
55 $ hg log a
55 $ hg log a
56 changeset: 0:9161b9aeaf16
56 changeset: 0:9161b9aeaf16
57 user: test
57 user: test
58 date: Thu Jan 01 00:00:01 1970 +0000
58 date: Thu Jan 01 00:00:01 1970 +0000
59 summary: a
59 summary: a
60
60
61 $ hg log glob:a*
61 $ hg log glob:a*
62 changeset: 3:2ca5ba701980
62 changeset: 3:2ca5ba701980
63 user: test
63 user: test
64 date: Thu Jan 01 00:00:04 1970 +0000
64 date: Thu Jan 01 00:00:04 1970 +0000
65 summary: d
65 summary: d
66
66
67 changeset: 0:9161b9aeaf16
67 changeset: 0:9161b9aeaf16
68 user: test
68 user: test
69 date: Thu Jan 01 00:00:01 1970 +0000
69 date: Thu Jan 01 00:00:01 1970 +0000
70 summary: a
70 summary: a
71
71
72 $ hg --debug log glob:a* -T '{rev}: {desc}\n' --config extensions.largefiles=
72 $ hg --debug log glob:a* -T '{rev}: {desc}\n' --config extensions.largefiles=
73 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
73 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
74 updated patterns: glob:.hglf/a*, glob:a*
74 updated patterns: glob:.hglf/a*, glob:a*
75 3: d
75 3: d
76 0: a
76 0: a
77
77
78 log on directory
78 log on directory
79
79
80 $ hg log dir
80 $ hg log dir
81 changeset: 4:7e4639b4691b
81 changeset: 4:7e4639b4691b
82 tag: tip
82 tag: tip
83 user: test
83 user: test
84 date: Thu Jan 01 00:00:05 1970 +0000
84 date: Thu Jan 01 00:00:05 1970 +0000
85 summary: e
85 summary: e
86
86
87 changeset: 2:f8954cd4dc1f
87 changeset: 2:f8954cd4dc1f
88 user: test
88 user: test
89 date: Thu Jan 01 00:00:03 1970 +0000
89 date: Thu Jan 01 00:00:03 1970 +0000
90 summary: c
90 summary: c
91
91
92 $ hg log somethingthatdoesntexist dir
92 $ hg log somethingthatdoesntexist dir
93 changeset: 4:7e4639b4691b
93 changeset: 4:7e4639b4691b
94 tag: tip
94 tag: tip
95 user: test
95 user: test
96 date: Thu Jan 01 00:00:05 1970 +0000
96 date: Thu Jan 01 00:00:05 1970 +0000
97 summary: e
97 summary: e
98
98
99 changeset: 2:f8954cd4dc1f
99 changeset: 2:f8954cd4dc1f
100 user: test
100 user: test
101 date: Thu Jan 01 00:00:03 1970 +0000
101 date: Thu Jan 01 00:00:03 1970 +0000
102 summary: c
102 summary: c
103
103
104
104
105 -X, with explicit path
105 -X, with explicit path
106
106
107 $ hg log a -X a
107 $ hg log a -X a
108
108
109 -f, non-existent directory
109 -f, non-existent directory
110
110
111 $ hg log -f dir
111 $ hg log -f dir
112 abort: cannot follow file not in parent revision: "dir"
112 abort: cannot follow file not in parent revision: "dir"
113 [255]
113 [255]
114
114
115 -f, directory
115 -f, directory
116
116
117 $ hg up -q 3
117 $ hg up -q 3
118 $ hg log -f dir
118 $ hg log -f dir
119 changeset: 2:f8954cd4dc1f
119 changeset: 2:f8954cd4dc1f
120 user: test
120 user: test
121 date: Thu Jan 01 00:00:03 1970 +0000
121 date: Thu Jan 01 00:00:03 1970 +0000
122 summary: c
122 summary: c
123
123
124 -f, directory with --patch
124 -f, directory with --patch
125
125
126 $ hg log -f dir -p
126 $ hg log -f dir -p
127 changeset: 2:f8954cd4dc1f
127 changeset: 2:f8954cd4dc1f
128 user: test
128 user: test
129 date: Thu Jan 01 00:00:03 1970 +0000
129 date: Thu Jan 01 00:00:03 1970 +0000
130 summary: c
130 summary: c
131
131
132 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
132 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
133 --- /dev/null* (glob)
133 --- /dev/null* (glob)
134 +++ b/dir/b* (glob)
134 +++ b/dir/b* (glob)
135 @@ -0,0 +1,1 @@
135 @@ -0,0 +1,1 @@
136 +a
136 +a
137
137
138
138
139 -f, pattern
139 -f, pattern
140
140
141 $ hg log -f -I 'dir**' -p
141 $ hg log -f -I 'dir**' -p
142 changeset: 2:f8954cd4dc1f
142 changeset: 2:f8954cd4dc1f
143 user: test
143 user: test
144 date: Thu Jan 01 00:00:03 1970 +0000
144 date: Thu Jan 01 00:00:03 1970 +0000
145 summary: c
145 summary: c
146
146
147 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
147 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
148 --- /dev/null* (glob)
148 --- /dev/null* (glob)
149 +++ b/dir/b* (glob)
149 +++ b/dir/b* (glob)
150 @@ -0,0 +1,1 @@
150 @@ -0,0 +1,1 @@
151 +a
151 +a
152
152
153 $ hg up -q 4
153 $ hg up -q 4
154
154
155 -f, a wrong style
155 -f, a wrong style
156
156
157 $ hg log -f -l1 --style something
157 $ hg log -f -l1 --style something
158 abort: style 'something' not found
158 abort: style 'something' not found
159 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
159 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
160 [255]
160 [255]
161
161
162 -f, phases style
162 -f, phases style
163
163
164
164
165 $ hg log -f -l1 --style phases
165 $ hg log -f -l1 --style phases
166 changeset: 4:7e4639b4691b
166 changeset: 4:7e4639b4691b
167 tag: tip
167 tag: tip
168 phase: draft
168 phase: draft
169 user: test
169 user: test
170 date: Thu Jan 01 00:00:05 1970 +0000
170 date: Thu Jan 01 00:00:05 1970 +0000
171 summary: e
171 summary: e
172
172
173
173
174 $ hg log -f -l1 --style phases -q
174 $ hg log -f -l1 --style phases -q
175 4:7e4639b4691b
175 4:7e4639b4691b
176
176
177 -f, but no args
177 -f, but no args
178
178
179 $ hg log -f
179 $ hg log -f
180 changeset: 4:7e4639b4691b
180 changeset: 4:7e4639b4691b
181 tag: tip
181 tag: tip
182 user: test
182 user: test
183 date: Thu Jan 01 00:00:05 1970 +0000
183 date: Thu Jan 01 00:00:05 1970 +0000
184 summary: e
184 summary: e
185
185
186 changeset: 3:2ca5ba701980
186 changeset: 3:2ca5ba701980
187 user: test
187 user: test
188 date: Thu Jan 01 00:00:04 1970 +0000
188 date: Thu Jan 01 00:00:04 1970 +0000
189 summary: d
189 summary: d
190
190
191 changeset: 2:f8954cd4dc1f
191 changeset: 2:f8954cd4dc1f
192 user: test
192 user: test
193 date: Thu Jan 01 00:00:03 1970 +0000
193 date: Thu Jan 01 00:00:03 1970 +0000
194 summary: c
194 summary: c
195
195
196 changeset: 1:d89b0a12d229
196 changeset: 1:d89b0a12d229
197 user: test
197 user: test
198 date: Thu Jan 01 00:00:02 1970 +0000
198 date: Thu Jan 01 00:00:02 1970 +0000
199 summary: b
199 summary: b
200
200
201 changeset: 0:9161b9aeaf16
201 changeset: 0:9161b9aeaf16
202 user: test
202 user: test
203 date: Thu Jan 01 00:00:01 1970 +0000
203 date: Thu Jan 01 00:00:01 1970 +0000
204 summary: a
204 summary: a
205
205
206
206
207 one rename
207 one rename
208
208
209 $ hg up -q 2
209 $ hg up -q 2
210 $ hg log -vf a
210 $ hg log -vf a
211 changeset: 0:9161b9aeaf16
211 changeset: 0:9161b9aeaf16
212 user: test
212 user: test
213 date: Thu Jan 01 00:00:01 1970 +0000
213 date: Thu Jan 01 00:00:01 1970 +0000
214 files: a f
214 files: a f
215 description:
215 description:
216 a
216 a
217
217
218
218
219
219
220 many renames
220 many renames
221
221
222 $ hg up -q tip
222 $ hg up -q tip
223 $ hg log -vf e
223 $ hg log -vf e
224 changeset: 4:7e4639b4691b
224 changeset: 4:7e4639b4691b
225 tag: tip
225 tag: tip
226 user: test
226 user: test
227 date: Thu Jan 01 00:00:05 1970 +0000
227 date: Thu Jan 01 00:00:05 1970 +0000
228 files: dir/b e
228 files: dir/b e
229 description:
229 description:
230 e
230 e
231
231
232
232
233 changeset: 2:f8954cd4dc1f
233 changeset: 2:f8954cd4dc1f
234 user: test
234 user: test
235 date: Thu Jan 01 00:00:03 1970 +0000
235 date: Thu Jan 01 00:00:03 1970 +0000
236 files: b dir/b f g
236 files: b dir/b f g
237 description:
237 description:
238 c
238 c
239
239
240
240
241 changeset: 1:d89b0a12d229
241 changeset: 1:d89b0a12d229
242 user: test
242 user: test
243 date: Thu Jan 01 00:00:02 1970 +0000
243 date: Thu Jan 01 00:00:02 1970 +0000
244 files: b g
244 files: b g
245 description:
245 description:
246 b
246 b
247
247
248
248
249 changeset: 0:9161b9aeaf16
249 changeset: 0:9161b9aeaf16
250 user: test
250 user: test
251 date: Thu Jan 01 00:00:01 1970 +0000
251 date: Thu Jan 01 00:00:01 1970 +0000
252 files: a f
252 files: a f
253 description:
253 description:
254 a
254 a
255
255
256
256
257
257
258
258
259 log -pf dir/b
259 log -pf dir/b
260
260
261 $ hg up -q 3
261 $ hg up -q 3
262 $ hg log -pf dir/b
262 $ hg log -pf dir/b
263 changeset: 2:f8954cd4dc1f
263 changeset: 2:f8954cd4dc1f
264 user: test
264 user: test
265 date: Thu Jan 01 00:00:03 1970 +0000
265 date: Thu Jan 01 00:00:03 1970 +0000
266 summary: c
266 summary: c
267
267
268 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
268 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
269 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
269 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
270 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
270 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
271 @@ -0,0 +1,1 @@
271 @@ -0,0 +1,1 @@
272 +a
272 +a
273
273
274 changeset: 1:d89b0a12d229
274 changeset: 1:d89b0a12d229
275 user: test
275 user: test
276 date: Thu Jan 01 00:00:02 1970 +0000
276 date: Thu Jan 01 00:00:02 1970 +0000
277 summary: b
277 summary: b
278
278
279 diff -r 9161b9aeaf16 -r d89b0a12d229 b
279 diff -r 9161b9aeaf16 -r d89b0a12d229 b
280 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
280 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
281 +++ b/b Thu Jan 01 00:00:02 1970 +0000
281 +++ b/b Thu Jan 01 00:00:02 1970 +0000
282 @@ -0,0 +1,1 @@
282 @@ -0,0 +1,1 @@
283 +a
283 +a
284
284
285 changeset: 0:9161b9aeaf16
285 changeset: 0:9161b9aeaf16
286 user: test
286 user: test
287 date: Thu Jan 01 00:00:01 1970 +0000
287 date: Thu Jan 01 00:00:01 1970 +0000
288 summary: a
288 summary: a
289
289
290 diff -r 000000000000 -r 9161b9aeaf16 a
290 diff -r 000000000000 -r 9161b9aeaf16 a
291 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
291 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
292 +++ b/a Thu Jan 01 00:00:01 1970 +0000
292 +++ b/a Thu Jan 01 00:00:01 1970 +0000
293 @@ -0,0 +1,1 @@
293 @@ -0,0 +1,1 @@
294 +a
294 +a
295
295
296
296
297 log -pf b inside dir
297 log -pf b inside dir
298
298
299 $ hg --cwd=dir log -pf b
299 $ hg --cwd=dir log -pf b
300 changeset: 2:f8954cd4dc1f
300 changeset: 2:f8954cd4dc1f
301 user: test
301 user: test
302 date: Thu Jan 01 00:00:03 1970 +0000
302 date: Thu Jan 01 00:00:03 1970 +0000
303 summary: c
303 summary: c
304
304
305 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
305 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
306 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
306 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
307 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
307 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
308 @@ -0,0 +1,1 @@
308 @@ -0,0 +1,1 @@
309 +a
309 +a
310
310
311 changeset: 1:d89b0a12d229
311 changeset: 1:d89b0a12d229
312 user: test
312 user: test
313 date: Thu Jan 01 00:00:02 1970 +0000
313 date: Thu Jan 01 00:00:02 1970 +0000
314 summary: b
314 summary: b
315
315
316 diff -r 9161b9aeaf16 -r d89b0a12d229 b
316 diff -r 9161b9aeaf16 -r d89b0a12d229 b
317 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
317 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
318 +++ b/b Thu Jan 01 00:00:02 1970 +0000
318 +++ b/b Thu Jan 01 00:00:02 1970 +0000
319 @@ -0,0 +1,1 @@
319 @@ -0,0 +1,1 @@
320 +a
320 +a
321
321
322 changeset: 0:9161b9aeaf16
322 changeset: 0:9161b9aeaf16
323 user: test
323 user: test
324 date: Thu Jan 01 00:00:01 1970 +0000
324 date: Thu Jan 01 00:00:01 1970 +0000
325 summary: a
325 summary: a
326
326
327 diff -r 000000000000 -r 9161b9aeaf16 a
327 diff -r 000000000000 -r 9161b9aeaf16 a
328 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
328 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
329 +++ b/a Thu Jan 01 00:00:01 1970 +0000
329 +++ b/a Thu Jan 01 00:00:01 1970 +0000
330 @@ -0,0 +1,1 @@
330 @@ -0,0 +1,1 @@
331 +a
331 +a
332
332
333
333
334 log -pf, but no args
334 log -pf, but no args
335
335
336 $ hg log -pf
336 $ hg log -pf
337 changeset: 3:2ca5ba701980
337 changeset: 3:2ca5ba701980
338 user: test
338 user: test
339 date: Thu Jan 01 00:00:04 1970 +0000
339 date: Thu Jan 01 00:00:04 1970 +0000
340 summary: d
340 summary: d
341
341
342 diff -r f8954cd4dc1f -r 2ca5ba701980 a
342 diff -r f8954cd4dc1f -r 2ca5ba701980 a
343 --- a/a Thu Jan 01 00:00:03 1970 +0000
343 --- a/a Thu Jan 01 00:00:03 1970 +0000
344 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
344 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
345 @@ -1,1 +0,0 @@
345 @@ -1,1 +0,0 @@
346 -a
346 -a
347 diff -r f8954cd4dc1f -r 2ca5ba701980 b
347 diff -r f8954cd4dc1f -r 2ca5ba701980 b
348 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
348 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
349 +++ b/b Thu Jan 01 00:00:04 1970 +0000
349 +++ b/b Thu Jan 01 00:00:04 1970 +0000
350 @@ -0,0 +1,1 @@
350 @@ -0,0 +1,1 @@
351 +a
351 +a
352 diff -r f8954cd4dc1f -r 2ca5ba701980 d
352 diff -r f8954cd4dc1f -r 2ca5ba701980 d
353 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
353 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
354 +++ b/d Thu Jan 01 00:00:04 1970 +0000
354 +++ b/d Thu Jan 01 00:00:04 1970 +0000
355 @@ -0,0 +1,1 @@
355 @@ -0,0 +1,1 @@
356 +a
356 +a
357 diff -r f8954cd4dc1f -r 2ca5ba701980 g
357 diff -r f8954cd4dc1f -r 2ca5ba701980 g
358 --- a/g Thu Jan 01 00:00:03 1970 +0000
358 --- a/g Thu Jan 01 00:00:03 1970 +0000
359 +++ b/g Thu Jan 01 00:00:04 1970 +0000
359 +++ b/g Thu Jan 01 00:00:04 1970 +0000
360 @@ -1,2 +1,2 @@
360 @@ -1,2 +1,2 @@
361 f
361 f
362 -g
362 -g
363 +f
363 +f
364
364
365 changeset: 2:f8954cd4dc1f
365 changeset: 2:f8954cd4dc1f
366 user: test
366 user: test
367 date: Thu Jan 01 00:00:03 1970 +0000
367 date: Thu Jan 01 00:00:03 1970 +0000
368 summary: c
368 summary: c
369
369
370 diff -r d89b0a12d229 -r f8954cd4dc1f b
370 diff -r d89b0a12d229 -r f8954cd4dc1f b
371 --- a/b Thu Jan 01 00:00:02 1970 +0000
371 --- a/b Thu Jan 01 00:00:02 1970 +0000
372 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
372 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
373 @@ -1,1 +0,0 @@
373 @@ -1,1 +0,0 @@
374 -a
374 -a
375 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
375 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
376 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
376 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
377 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
377 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
378 @@ -0,0 +1,1 @@
378 @@ -0,0 +1,1 @@
379 +a
379 +a
380 diff -r d89b0a12d229 -r f8954cd4dc1f f
380 diff -r d89b0a12d229 -r f8954cd4dc1f f
381 --- a/f Thu Jan 01 00:00:02 1970 +0000
381 --- a/f Thu Jan 01 00:00:02 1970 +0000
382 +++ b/f Thu Jan 01 00:00:03 1970 +0000
382 +++ b/f Thu Jan 01 00:00:03 1970 +0000
383 @@ -1,1 +1,2 @@
383 @@ -1,1 +1,2 @@
384 f
384 f
385 +f
385 +f
386 diff -r d89b0a12d229 -r f8954cd4dc1f g
386 diff -r d89b0a12d229 -r f8954cd4dc1f g
387 --- a/g Thu Jan 01 00:00:02 1970 +0000
387 --- a/g Thu Jan 01 00:00:02 1970 +0000
388 +++ b/g Thu Jan 01 00:00:03 1970 +0000
388 +++ b/g Thu Jan 01 00:00:03 1970 +0000
389 @@ -1,1 +1,2 @@
389 @@ -1,1 +1,2 @@
390 f
390 f
391 +g
391 +g
392
392
393 changeset: 1:d89b0a12d229
393 changeset: 1:d89b0a12d229
394 user: test
394 user: test
395 date: Thu Jan 01 00:00:02 1970 +0000
395 date: Thu Jan 01 00:00:02 1970 +0000
396 summary: b
396 summary: b
397
397
398 diff -r 9161b9aeaf16 -r d89b0a12d229 b
398 diff -r 9161b9aeaf16 -r d89b0a12d229 b
399 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
399 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
400 +++ b/b Thu Jan 01 00:00:02 1970 +0000
400 +++ b/b Thu Jan 01 00:00:02 1970 +0000
401 @@ -0,0 +1,1 @@
401 @@ -0,0 +1,1 @@
402 +a
402 +a
403 diff -r 9161b9aeaf16 -r d89b0a12d229 g
403 diff -r 9161b9aeaf16 -r d89b0a12d229 g
404 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
404 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
405 +++ b/g Thu Jan 01 00:00:02 1970 +0000
405 +++ b/g Thu Jan 01 00:00:02 1970 +0000
406 @@ -0,0 +1,1 @@
406 @@ -0,0 +1,1 @@
407 +f
407 +f
408
408
409 changeset: 0:9161b9aeaf16
409 changeset: 0:9161b9aeaf16
410 user: test
410 user: test
411 date: Thu Jan 01 00:00:01 1970 +0000
411 date: Thu Jan 01 00:00:01 1970 +0000
412 summary: a
412 summary: a
413
413
414 diff -r 000000000000 -r 9161b9aeaf16 a
414 diff -r 000000000000 -r 9161b9aeaf16 a
415 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
415 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
416 +++ b/a Thu Jan 01 00:00:01 1970 +0000
416 +++ b/a Thu Jan 01 00:00:01 1970 +0000
417 @@ -0,0 +1,1 @@
417 @@ -0,0 +1,1 @@
418 +a
418 +a
419 diff -r 000000000000 -r 9161b9aeaf16 f
419 diff -r 000000000000 -r 9161b9aeaf16 f
420 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
420 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
421 +++ b/f Thu Jan 01 00:00:01 1970 +0000
421 +++ b/f Thu Jan 01 00:00:01 1970 +0000
422 @@ -0,0 +1,1 @@
422 @@ -0,0 +1,1 @@
423 +f
423 +f
424
424
425
425
426 log -vf dir/b
426 log -vf dir/b
427
427
428 $ hg log -vf dir/b
428 $ hg log -vf dir/b
429 changeset: 2:f8954cd4dc1f
429 changeset: 2:f8954cd4dc1f
430 user: test
430 user: test
431 date: Thu Jan 01 00:00:03 1970 +0000
431 date: Thu Jan 01 00:00:03 1970 +0000
432 files: b dir/b f g
432 files: b dir/b f g
433 description:
433 description:
434 c
434 c
435
435
436
436
437 changeset: 1:d89b0a12d229
437 changeset: 1:d89b0a12d229
438 user: test
438 user: test
439 date: Thu Jan 01 00:00:02 1970 +0000
439 date: Thu Jan 01 00:00:02 1970 +0000
440 files: b g
440 files: b g
441 description:
441 description:
442 b
442 b
443
443
444
444
445 changeset: 0:9161b9aeaf16
445 changeset: 0:9161b9aeaf16
446 user: test
446 user: test
447 date: Thu Jan 01 00:00:01 1970 +0000
447 date: Thu Jan 01 00:00:01 1970 +0000
448 files: a f
448 files: a f
449 description:
449 description:
450 a
450 a
451
451
452
452
453
453
454
454
455 -f and multiple filelog heads
455 -f and multiple filelog heads
456
456
457 $ hg up -q 2
457 $ hg up -q 2
458 $ hg log -f g --template '{rev}\n'
458 $ hg log -f g --template '{rev}\n'
459 2
459 2
460 1
460 1
461 0
461 0
462 $ hg up -q tip
462 $ hg up -q tip
463 $ hg log -f g --template '{rev}\n'
463 $ hg log -f g --template '{rev}\n'
464 3
464 3
465 2
465 2
466 0
466 0
467
467
468 follow files from the specified revisions (issue4959)
468 follow files from the specified revisions (issue4959)
469
469
470 $ hg log -G -T '{rev} {files},{file_copies % " {source}->{name}"}\n'
470 $ hg log -G -T '{rev} {files},{file_copies % " {source}->{name}"}\n'
471 @ 4 dir/b e, dir/b->e
471 @ 4 dir/b e, dir/b->e
472 |
472 |
473 o 3 a b d g, a->b f->g
473 o 3 a b d g, a->b f->g
474 |
474 |
475 o 2 b dir/b f g, b->dir/b
475 o 2 b dir/b f g, b->dir/b
476 |
476 |
477 o 1 b g, a->b f->g
477 o 1 b g, a->b f->g
478 |
478 |
479 o 0 a f,
479 o 0 a f,
480
480
481
481
482 $ hg log -T '{rev}\n' -fr 4 e
482 $ hg log -T '{rev}\n' -fr 4 e
483 4
483 4
484 2
484 2
485 1
485 1
486 0
486 0
487 $ hg log -T '{rev}\n' -fr 2 g
487 $ hg log -T '{rev}\n' -fr 2 g
488 2
488 2
489 1
489 1
490 0
490 0
491 $ hg log -T '{rev}\n' -fr '2+3' g
491 $ hg log -T '{rev}\n' -fr '2+3' g
492 3
492 3
493 2
493 2
494 1
494 1
495 0
495 0
496
496
497 follow files from the specified revisions with glob patterns (issue5053)
497 follow files from the specified revisions with glob patterns (issue5053)
498 (BROKEN: should follow copies from e@4)
498 (BROKEN: should follow copies from e@4)
499
499
500 $ hg log -T '{rev}\n' -fr4 e -X '[abcdfg]'
500 $ hg log -T '{rev}\n' -fr4 e -X '[abcdfg]'
501 4
501 4
502 2 (false !)
502 2 (false !)
503 1 (false !)
503 1 (false !)
504 0 (false !)
504 0 (false !)
505
505
506 follow files from the specified revisions with missing patterns
506 follow files from the specified revisions with missing patterns
507 (BROKEN: should follow copies from e@4)
507 (BROKEN: should follow copies from e@4)
508
508
509 $ hg log -T '{rev}\n' -fr4 e x
509 $ hg log -T '{rev}\n' -fr4 e x
510 4
510 4
511 2 (false !)
511 2 (false !)
512 1 (false !)
512 1 (false !)
513 0 (false !)
513 0 (false !)
514
514
515 follow files from the specified revisions across copies with -p/--patch
515 follow files from the specified revisions across copies with -p/--patch
516
516
517 $ hg log -T '== rev: {rev},{file_copies % " {source}->{name}"} ==\n' -fpr 4 e g
517 $ hg log -T '== rev: {rev},{file_copies % " {source}->{name}"} ==\n' -fpr 4 e g
518 == rev: 4, dir/b->e ==
518 == rev: 4, dir/b->e ==
519 diff -r 2ca5ba701980 -r 7e4639b4691b e
519 diff -r 2ca5ba701980 -r 7e4639b4691b e
520 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
520 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
521 +++ b/e Thu Jan 01 00:00:05 1970 +0000
521 +++ b/e Thu Jan 01 00:00:05 1970 +0000
522 @@ -0,0 +1,1 @@
522 @@ -0,0 +1,1 @@
523 +a
523 +a
524
524
525 == rev: 3, a->b f->g ==
525 == rev: 3, a->b f->g ==
526 diff -r f8954cd4dc1f -r 2ca5ba701980 g
526 diff -r f8954cd4dc1f -r 2ca5ba701980 g
527 --- a/g Thu Jan 01 00:00:03 1970 +0000
527 --- a/g Thu Jan 01 00:00:03 1970 +0000
528 +++ b/g Thu Jan 01 00:00:04 1970 +0000
528 +++ b/g Thu Jan 01 00:00:04 1970 +0000
529 @@ -1,2 +1,2 @@
529 @@ -1,2 +1,2 @@
530 f
530 f
531 -g
531 -g
532 +f
532 +f
533
533
534 == rev: 2, b->dir/b ==
534 == rev: 2, b->dir/b ==
535 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
535 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
536 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
536 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
537 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
537 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
538 @@ -0,0 +1,1 @@
538 @@ -0,0 +1,1 @@
539 +a
539 +a
540 diff -r d89b0a12d229 -r f8954cd4dc1f f
540 diff -r d89b0a12d229 -r f8954cd4dc1f f
541 --- a/f Thu Jan 01 00:00:02 1970 +0000
541 --- a/f Thu Jan 01 00:00:02 1970 +0000
542 +++ b/f Thu Jan 01 00:00:03 1970 +0000
542 +++ b/f Thu Jan 01 00:00:03 1970 +0000
543 @@ -1,1 +1,2 @@
543 @@ -1,1 +1,2 @@
544 f
544 f
545 +f
545 +f
546
546
547 == rev: 1, a->b f->g ==
547 == rev: 1, a->b f->g ==
548 diff -r 9161b9aeaf16 -r d89b0a12d229 b
548 diff -r 9161b9aeaf16 -r d89b0a12d229 b
549 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
549 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
550 +++ b/b Thu Jan 01 00:00:02 1970 +0000
550 +++ b/b Thu Jan 01 00:00:02 1970 +0000
551 @@ -0,0 +1,1 @@
551 @@ -0,0 +1,1 @@
552 +a
552 +a
553
553
554 == rev: 0, ==
554 == rev: 0, ==
555 diff -r 000000000000 -r 9161b9aeaf16 a
555 diff -r 000000000000 -r 9161b9aeaf16 a
556 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
556 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
557 +++ b/a Thu Jan 01 00:00:01 1970 +0000
557 +++ b/a Thu Jan 01 00:00:01 1970 +0000
558 @@ -0,0 +1,1 @@
558 @@ -0,0 +1,1 @@
559 +a
559 +a
560 diff -r 000000000000 -r 9161b9aeaf16 f
560 diff -r 000000000000 -r 9161b9aeaf16 f
561 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
561 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
562 +++ b/f Thu Jan 01 00:00:01 1970 +0000
562 +++ b/f Thu Jan 01 00:00:01 1970 +0000
563 @@ -0,0 +1,1 @@
563 @@ -0,0 +1,1 @@
564 +f
564 +f
565
565
566
566
567 log copies with --copies
567 log copies with --copies
568
568
569 $ hg log -vC --template '{rev} {file_copies}\n'
569 $ hg log -vC --template '{rev} {file_copies}\n'
570 4 e (dir/b)
570 4 e (dir/b)
571 3 b (a)g (f)
571 3 b (a)g (f)
572 2 dir/b (b)
572 2 dir/b (b)
573 1 b (a)g (f)
573 1 b (a)g (f)
574 0
574 0
575
575
576 log copies switch without --copies, with old filecopy template
576 log copies switch without --copies, with old filecopy template
577
577
578 $ hg log -v --template '{rev} {file_copies_switch%filecopy}\n'
578 $ hg log -v --template '{rev} {file_copies_switch%filecopy}\n'
579 4
579 4
580 3
580 3
581 2
581 2
582 1
582 1
583 0
583 0
584
584
585 log copies switch with --copies
585 log copies switch with --copies
586
586
587 $ hg log -vC --template '{rev} {file_copies_switch}\n'
587 $ hg log -vC --template '{rev} {file_copies_switch}\n'
588 4 e (dir/b)
588 4 e (dir/b)
589 3 b (a)g (f)
589 3 b (a)g (f)
590 2 dir/b (b)
590 2 dir/b (b)
591 1 b (a)g (f)
591 1 b (a)g (f)
592 0
592 0
593
593
594
594
595 log copies with hardcoded style and with --style=default
595 log copies with hardcoded style and with --style=default
596
596
597 $ hg log -vC -r4
597 $ hg log -vC -r4
598 changeset: 4:7e4639b4691b
598 changeset: 4:7e4639b4691b
599 tag: tip
599 tag: tip
600 user: test
600 user: test
601 date: Thu Jan 01 00:00:05 1970 +0000
601 date: Thu Jan 01 00:00:05 1970 +0000
602 files: dir/b e
602 files: dir/b e
603 copies: e (dir/b)
603 copies: e (dir/b)
604 description:
604 description:
605 e
605 e
606
606
607
607
608 $ hg log -vC -r4 --style=default
608 $ hg log -vC -r4 --style=default
609 changeset: 4:7e4639b4691b
609 changeset: 4:7e4639b4691b
610 tag: tip
610 tag: tip
611 user: test
611 user: test
612 date: Thu Jan 01 00:00:05 1970 +0000
612 date: Thu Jan 01 00:00:05 1970 +0000
613 files: dir/b e
613 files: dir/b e
614 copies: e (dir/b)
614 copies: e (dir/b)
615 description:
615 description:
616 e
616 e
617
617
618
618
619 $ hg log -vC -r4 -Tjson
619 $ hg log -vC -r4 -Tjson
620 [
620 [
621 {
621 {
622 "bookmarks": [],
622 "bookmarks": [],
623 "branch": "default",
623 "branch": "default",
624 "copies": {"e": "dir/b"},
624 "copies": {"e": "dir/b"},
625 "date": [5, 0],
625 "date": [5, 0],
626 "desc": "e",
626 "desc": "e",
627 "files": ["dir/b", "e"],
627 "files": ["dir/b", "e"],
628 "node": "7e4639b4691b9f84b81036a8d4fb218ce3c5e3a3",
628 "node": "7e4639b4691b9f84b81036a8d4fb218ce3c5e3a3",
629 "parents": ["2ca5ba7019804f1f597249caddf22a64d34df0ba"],
629 "parents": ["2ca5ba7019804f1f597249caddf22a64d34df0ba"],
630 "phase": "draft",
630 "phase": "draft",
631 "rev": 4,
631 "rev": 4,
632 "tags": ["tip"],
632 "tags": ["tip"],
633 "user": "test"
633 "user": "test"
634 }
634 }
635 ]
635 ]
636
636
637 log copies, non-linear manifest
637 log copies, non-linear manifest
638
638
639 $ hg up -C 3
639 $ hg up -C 3
640 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
640 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
641 $ hg mv dir/b e
641 $ hg mv dir/b e
642 $ echo foo > foo
642 $ echo foo > foo
643 $ hg ci -Ame2 -d '6 0'
643 $ hg ci -Ame2 -d '6 0'
644 adding foo
644 adding foo
645 created new head
645 created new head
646 $ hg log -v --template '{rev} {file_copies}\n' -r 5
646 $ hg log -v --template '{rev} {file_copies}\n' -r 5
647 5 e (dir/b)
647 5 e (dir/b)
648
648
649
649
650 log copies, execute bit set
650 log copies, execute bit set
651
651
652 #if execbit
652 #if execbit
653 $ chmod +x e
653 $ chmod +x e
654 $ hg ci -me3 -d '7 0'
654 $ hg ci -me3 -d '7 0'
655 $ hg log -v --template '{rev} {file_copies}\n' -r 6
655 $ hg log -v --template '{rev} {file_copies}\n' -r 6
656 6
656 6
657 #endif
657 #endif
658
658
659 log copies, empty set
659 log copies, empty set
660
660
661 $ hg log --copies -r '0 and not 0'
661 $ hg log --copies -r '0 and not 0'
662
662
663 log -p d
663 log -p d
664
664
665 $ hg log -pv d
665 $ hg log -pv d
666 changeset: 3:2ca5ba701980
666 changeset: 3:2ca5ba701980
667 user: test
667 user: test
668 date: Thu Jan 01 00:00:04 1970 +0000
668 date: Thu Jan 01 00:00:04 1970 +0000
669 files: a b d g
669 files: a b d g
670 description:
670 description:
671 d
671 d
672
672
673
673
674 diff -r f8954cd4dc1f -r 2ca5ba701980 d
674 diff -r f8954cd4dc1f -r 2ca5ba701980 d
675 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
675 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
676 +++ b/d Thu Jan 01 00:00:04 1970 +0000
676 +++ b/d Thu Jan 01 00:00:04 1970 +0000
677 @@ -0,0 +1,1 @@
677 @@ -0,0 +1,1 @@
678 +a
678 +a
679
679
680
680
681
681
682 log --removed file
682 log --removed file
683
683
684 $ hg log --removed -v a
684 $ hg log --removed -v a
685 changeset: 3:2ca5ba701980
685 changeset: 3:2ca5ba701980
686 user: test
686 user: test
687 date: Thu Jan 01 00:00:04 1970 +0000
687 date: Thu Jan 01 00:00:04 1970 +0000
688 files: a b d g
688 files: a b d g
689 description:
689 description:
690 d
690 d
691
691
692
692
693 changeset: 0:9161b9aeaf16
693 changeset: 0:9161b9aeaf16
694 user: test
694 user: test
695 date: Thu Jan 01 00:00:01 1970 +0000
695 date: Thu Jan 01 00:00:01 1970 +0000
696 files: a f
696 files: a f
697 description:
697 description:
698 a
698 a
699
699
700
700
701
701
702 log --removed revrange file
702 log --removed revrange file
703
703
704 $ hg log --removed -v -r0:2 a
704 $ hg log --removed -v -r0:2 a
705 changeset: 0:9161b9aeaf16
705 changeset: 0:9161b9aeaf16
706 user: test
706 user: test
707 date: Thu Jan 01 00:00:01 1970 +0000
707 date: Thu Jan 01 00:00:01 1970 +0000
708 files: a f
708 files: a f
709 description:
709 description:
710 a
710 a
711
711
712
712
713 $ cd ..
713 $ cd ..
714
714
715 log --follow tests
715 log --follow tests
716
716
717 $ hg init follow
717 $ hg init follow
718 $ cd follow
718 $ cd follow
719
719
720 $ echo base > base
720 $ echo base > base
721 $ hg ci -Ambase -d '1 0'
721 $ hg ci -Ambase -d '1 0'
722 adding base
722 adding base
723
723
724 $ echo r1 >> base
724 $ echo r1 >> base
725 $ hg ci -Amr1 -d '1 0'
725 $ hg ci -Amr1 -d '1 0'
726 $ echo r2 >> base
726 $ echo r2 >> base
727 $ hg ci -Amr2 -d '1 0'
727 $ hg ci -Amr2 -d '1 0'
728
728
729 $ hg up -C 1
729 $ hg up -C 1
730 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
730 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
731 $ echo b1 > b1
731 $ echo b1 > b1
732
732
733 log -r "follow('set:clean()')"
733 log -r "follow('set:clean()')"
734
734
735 $ hg log -r "follow('set:clean()')"
735 $ hg log -r "follow('set:clean()')"
736 changeset: 0:67e992f2c4f3
736 changeset: 0:67e992f2c4f3
737 user: test
737 user: test
738 date: Thu Jan 01 00:00:01 1970 +0000
738 date: Thu Jan 01 00:00:01 1970 +0000
739 summary: base
739 summary: base
740
740
741 changeset: 1:3d5bf5654eda
741 changeset: 1:3d5bf5654eda
742 user: test
742 user: test
743 date: Thu Jan 01 00:00:01 1970 +0000
743 date: Thu Jan 01 00:00:01 1970 +0000
744 summary: r1
744 summary: r1
745
745
746
746
747 $ hg ci -Amb1 -d '1 0'
747 $ hg ci -Amb1 -d '1 0'
748 adding b1
748 adding b1
749 created new head
749 created new head
750
750
751
751
752 log -f
752 log -f
753
753
754 $ hg log -f
754 $ hg log -f
755 changeset: 3:e62f78d544b4
755 changeset: 3:e62f78d544b4
756 tag: tip
756 tag: tip
757 parent: 1:3d5bf5654eda
757 parent: 1:3d5bf5654eda
758 user: test
758 user: test
759 date: Thu Jan 01 00:00:01 1970 +0000
759 date: Thu Jan 01 00:00:01 1970 +0000
760 summary: b1
760 summary: b1
761
761
762 changeset: 1:3d5bf5654eda
762 changeset: 1:3d5bf5654eda
763 user: test
763 user: test
764 date: Thu Jan 01 00:00:01 1970 +0000
764 date: Thu Jan 01 00:00:01 1970 +0000
765 summary: r1
765 summary: r1
766
766
767 changeset: 0:67e992f2c4f3
767 changeset: 0:67e992f2c4f3
768 user: test
768 user: test
769 date: Thu Jan 01 00:00:01 1970 +0000
769 date: Thu Jan 01 00:00:01 1970 +0000
770 summary: base
770 summary: base
771
771
772
772
773 log -r follow('glob:b*')
773 log -r follow('glob:b*')
774
774
775 $ hg log -r "follow('glob:b*')"
775 $ hg log -r "follow('glob:b*')"
776 changeset: 0:67e992f2c4f3
776 changeset: 0:67e992f2c4f3
777 user: test
777 user: test
778 date: Thu Jan 01 00:00:01 1970 +0000
778 date: Thu Jan 01 00:00:01 1970 +0000
779 summary: base
779 summary: base
780
780
781 changeset: 1:3d5bf5654eda
781 changeset: 1:3d5bf5654eda
782 user: test
782 user: test
783 date: Thu Jan 01 00:00:01 1970 +0000
783 date: Thu Jan 01 00:00:01 1970 +0000
784 summary: r1
784 summary: r1
785
785
786 changeset: 3:e62f78d544b4
786 changeset: 3:e62f78d544b4
787 tag: tip
787 tag: tip
788 parent: 1:3d5bf5654eda
788 parent: 1:3d5bf5654eda
789 user: test
789 user: test
790 date: Thu Jan 01 00:00:01 1970 +0000
790 date: Thu Jan 01 00:00:01 1970 +0000
791 summary: b1
791 summary: b1
792
792
793 log -f -r '1 + 4'
793 log -f -r '1 + 4'
794
794
795 $ hg up -C 0
795 $ hg up -C 0
796 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
796 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
797 $ echo b2 > b2
797 $ echo b2 > b2
798 $ hg ci -Amb2 -d '1 0'
798 $ hg ci -Amb2 -d '1 0'
799 adding b2
799 adding b2
800 created new head
800 created new head
801 $ hg log -f -r '1 + 4'
801 $ hg log -f -r '1 + 4'
802 changeset: 4:ddb82e70d1a1
802 changeset: 4:ddb82e70d1a1
803 tag: tip
803 tag: tip
804 parent: 0:67e992f2c4f3
804 parent: 0:67e992f2c4f3
805 user: test
805 user: test
806 date: Thu Jan 01 00:00:01 1970 +0000
806 date: Thu Jan 01 00:00:01 1970 +0000
807 summary: b2
807 summary: b2
808
808
809 changeset: 1:3d5bf5654eda
809 changeset: 1:3d5bf5654eda
810 user: test
810 user: test
811 date: Thu Jan 01 00:00:01 1970 +0000
811 date: Thu Jan 01 00:00:01 1970 +0000
812 summary: r1
812 summary: r1
813
813
814 changeset: 0:67e992f2c4f3
814 changeset: 0:67e992f2c4f3
815 user: test
815 user: test
816 date: Thu Jan 01 00:00:01 1970 +0000
816 date: Thu Jan 01 00:00:01 1970 +0000
817 summary: base
817 summary: base
818
818
819
819
820 log -fr with aliases: 'A' should be expanded, but 'reverse()' should have no
820 log -fr with aliases: 'A' should be expanded, but 'reverse()' should have no
821 effect
821 effect
822
822
823 $ hg log --config 'revsetalias.reverse(x)=x' --config 'revsetalias.A=1+4' -qfrA
823 $ hg log --config 'revsetalias.reverse(x)=x' --config 'revsetalias.A=1+4' -qfrA
824 4:ddb82e70d1a1
824 4:ddb82e70d1a1
825 1:3d5bf5654eda
825 1:3d5bf5654eda
826 0:67e992f2c4f3
826 0:67e992f2c4f3
827
827
828 log -r "follow('set:grep(b2)')"
828 log -r "follow('set:grep(b2)')"
829
829
830 $ hg log -r "follow('set:grep(b2)')"
830 $ hg log -r "follow('set:grep(b2)')"
831 changeset: 4:ddb82e70d1a1
831 changeset: 4:ddb82e70d1a1
832 tag: tip
832 tag: tip
833 parent: 0:67e992f2c4f3
833 parent: 0:67e992f2c4f3
834 user: test
834 user: test
835 date: Thu Jan 01 00:00:01 1970 +0000
835 date: Thu Jan 01 00:00:01 1970 +0000
836 summary: b2
836 summary: b2
837
837
838 log -r "follow('set:grep(b2)', 4)"
838 log -r "follow('set:grep(b2)', 4)"
839
839
840 $ hg up -qC 0
840 $ hg up -qC 0
841 $ hg log -r "follow('set:grep(b2)', 4)"
841 $ hg log -r "follow('set:grep(b2)', 4)"
842 changeset: 4:ddb82e70d1a1
842 changeset: 4:ddb82e70d1a1
843 tag: tip
843 tag: tip
844 parent: 0:67e992f2c4f3
844 parent: 0:67e992f2c4f3
845 user: test
845 user: test
846 date: Thu Jan 01 00:00:01 1970 +0000
846 date: Thu Jan 01 00:00:01 1970 +0000
847 summary: b2
847 summary: b2
848
848
849
849
850 follow files starting from multiple revisions:
850 follow files starting from multiple revisions:
851
851
852 $ hg log -T '{rev}: {files}\n' -r "follow('glob:b?', startrev=2+3+4)"
852 $ hg log -T '{rev}: {files}\n' -r "follow('glob:b?', startrev=2+3+4)"
853 3: b1
853 3: b1
854 4: b2
854 4: b2
855
855
856 follow files starting from empty revision:
856 follow files starting from empty revision:
857
857
858 $ hg log -T '{rev}: {files}\n' -r "follow('glob:*', startrev=.-.)"
858 $ hg log -T '{rev}: {files}\n' -r "follow('glob:*', startrev=.-.)"
859
859
860 follow starting from revisions:
860 follow starting from revisions:
861
861
862 $ hg log -Gq -r "follow(startrev=2+4)"
862 $ hg log -Gq -r "follow(startrev=2+4)"
863 o 4:ddb82e70d1a1
863 o 4:ddb82e70d1a1
864 |
864 |
865 | o 2:60c670bf5b30
865 | o 2:60c670bf5b30
866 | |
866 | |
867 | o 1:3d5bf5654eda
867 | o 1:3d5bf5654eda
868 |/
868 |/
869 @ 0:67e992f2c4f3
869 @ 0:67e992f2c4f3
870
870
871
871
872 follow the current revision:
872 follow the current revision:
873
873
874 $ hg log -Gq -r "follow()"
874 $ hg log -Gq -r "follow()"
875 @ 0:67e992f2c4f3
875 @ 0:67e992f2c4f3
876
876
877
877
878 $ hg up -qC 4
878 $ hg up -qC 4
879
879
880 log -f -r null
880 log -f -r null
881
881
882 $ hg log -f -r null
882 $ hg log -f -r null
883 changeset: -1:000000000000
883 changeset: -1:000000000000
884 user:
884 user:
885 date: Thu Jan 01 00:00:00 1970 +0000
885 date: Thu Jan 01 00:00:00 1970 +0000
886
886
887 $ hg log -f -r null -G
887 $ hg log -f -r null -G
888 o changeset: -1:000000000000
888 o changeset: -1:000000000000
889 user:
889 user:
890 date: Thu Jan 01 00:00:00 1970 +0000
890 date: Thu Jan 01 00:00:00 1970 +0000
891
891
892
892
893
893
894 log -f with null parent
894 log -f with null parent
895
895
896 $ hg up -C null
896 $ hg up -C null
897 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
897 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
898 $ hg log -f
898 $ hg log -f
899
899
900
900
901 log -r . with two parents
901 log -r . with two parents
902
902
903 $ hg up -C 3
903 $ hg up -C 3
904 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
904 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
905 $ hg merge tip
905 $ hg merge tip
906 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
906 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
907 (branch merge, don't forget to commit)
907 (branch merge, don't forget to commit)
908 $ hg log -r .
908 $ hg log -r .
909 changeset: 3:e62f78d544b4
909 changeset: 3:e62f78d544b4
910 parent: 1:3d5bf5654eda
910 parent: 1:3d5bf5654eda
911 user: test
911 user: test
912 date: Thu Jan 01 00:00:01 1970 +0000
912 date: Thu Jan 01 00:00:01 1970 +0000
913 summary: b1
913 summary: b1
914
914
915
915
916
916
917 log -r . with one parent
917 log -r . with one parent
918
918
919 $ hg ci -mm12 -d '1 0'
919 $ hg ci -mm12 -d '1 0'
920 $ hg log -r .
920 $ hg log -r .
921 changeset: 5:302e9dd6890d
921 changeset: 5:302e9dd6890d
922 tag: tip
922 tag: tip
923 parent: 3:e62f78d544b4
923 parent: 3:e62f78d544b4
924 parent: 4:ddb82e70d1a1
924 parent: 4:ddb82e70d1a1
925 user: test
925 user: test
926 date: Thu Jan 01 00:00:01 1970 +0000
926 date: Thu Jan 01 00:00:01 1970 +0000
927 summary: m12
927 summary: m12
928
928
929
929
930 $ echo postm >> b1
930 $ echo postm >> b1
931 $ hg ci -Amb1.1 -d'1 0'
931 $ hg ci -Amb1.1 -d'1 0'
932
932
933
933
934 log --follow-first
934 log --follow-first
935
935
936 $ hg log --follow-first
936 $ hg log --follow-first
937 changeset: 6:2404bbcab562
937 changeset: 6:2404bbcab562
938 tag: tip
938 tag: tip
939 user: test
939 user: test
940 date: Thu Jan 01 00:00:01 1970 +0000
940 date: Thu Jan 01 00:00:01 1970 +0000
941 summary: b1.1
941 summary: b1.1
942
942
943 changeset: 5:302e9dd6890d
943 changeset: 5:302e9dd6890d
944 parent: 3:e62f78d544b4
944 parent: 3:e62f78d544b4
945 parent: 4:ddb82e70d1a1
945 parent: 4:ddb82e70d1a1
946 user: test
946 user: test
947 date: Thu Jan 01 00:00:01 1970 +0000
947 date: Thu Jan 01 00:00:01 1970 +0000
948 summary: m12
948 summary: m12
949
949
950 changeset: 3:e62f78d544b4
950 changeset: 3:e62f78d544b4
951 parent: 1:3d5bf5654eda
951 parent: 1:3d5bf5654eda
952 user: test
952 user: test
953 date: Thu Jan 01 00:00:01 1970 +0000
953 date: Thu Jan 01 00:00:01 1970 +0000
954 summary: b1
954 summary: b1
955
955
956 changeset: 1:3d5bf5654eda
956 changeset: 1:3d5bf5654eda
957 user: test
957 user: test
958 date: Thu Jan 01 00:00:01 1970 +0000
958 date: Thu Jan 01 00:00:01 1970 +0000
959 summary: r1
959 summary: r1
960
960
961 changeset: 0:67e992f2c4f3
961 changeset: 0:67e992f2c4f3
962 user: test
962 user: test
963 date: Thu Jan 01 00:00:01 1970 +0000
963 date: Thu Jan 01 00:00:01 1970 +0000
964 summary: base
964 summary: base
965
965
966
966
967
967
968 log -P 2
968 log -P 2
969
969
970 $ hg log -P 2
970 $ hg log -P 2
971 changeset: 6:2404bbcab562
971 changeset: 6:2404bbcab562
972 tag: tip
972 tag: tip
973 user: test
973 user: test
974 date: Thu Jan 01 00:00:01 1970 +0000
974 date: Thu Jan 01 00:00:01 1970 +0000
975 summary: b1.1
975 summary: b1.1
976
976
977 changeset: 5:302e9dd6890d
977 changeset: 5:302e9dd6890d
978 parent: 3:e62f78d544b4
978 parent: 3:e62f78d544b4
979 parent: 4:ddb82e70d1a1
979 parent: 4:ddb82e70d1a1
980 user: test
980 user: test
981 date: Thu Jan 01 00:00:01 1970 +0000
981 date: Thu Jan 01 00:00:01 1970 +0000
982 summary: m12
982 summary: m12
983
983
984 changeset: 4:ddb82e70d1a1
984 changeset: 4:ddb82e70d1a1
985 parent: 0:67e992f2c4f3
985 parent: 0:67e992f2c4f3
986 user: test
986 user: test
987 date: Thu Jan 01 00:00:01 1970 +0000
987 date: Thu Jan 01 00:00:01 1970 +0000
988 summary: b2
988 summary: b2
989
989
990 changeset: 3:e62f78d544b4
990 changeset: 3:e62f78d544b4
991 parent: 1:3d5bf5654eda
991 parent: 1:3d5bf5654eda
992 user: test
992 user: test
993 date: Thu Jan 01 00:00:01 1970 +0000
993 date: Thu Jan 01 00:00:01 1970 +0000
994 summary: b1
994 summary: b1
995
995
996
996
997
997
998 log -r tip -p --git
998 log -r tip -p --git
999
999
1000 $ hg log -r tip -p --git
1000 $ hg log -r tip -p --git
1001 changeset: 6:2404bbcab562
1001 changeset: 6:2404bbcab562
1002 tag: tip
1002 tag: tip
1003 user: test
1003 user: test
1004 date: Thu Jan 01 00:00:01 1970 +0000
1004 date: Thu Jan 01 00:00:01 1970 +0000
1005 summary: b1.1
1005 summary: b1.1
1006
1006
1007 diff --git a/b1 b/b1
1007 diff --git a/b1 b/b1
1008 --- a/b1
1008 --- a/b1
1009 +++ b/b1
1009 +++ b/b1
1010 @@ -1,1 +1,2 @@
1010 @@ -1,1 +1,2 @@
1011 b1
1011 b1
1012 +postm
1012 +postm
1013
1013
1014
1014
1015
1015
1016 log -r ""
1016 log -r ""
1017
1017
1018 $ hg log -r ''
1018 $ hg log -r ''
1019 hg: parse error: empty query
1019 hg: parse error: empty query
1020 [255]
1020 [255]
1021
1021
1022 log -r <some unknown node id>
1022 log -r <some unknown node id>
1023
1023
1024 $ hg log -r 1000000000000000000000000000000000000000
1024 $ hg log -r 1000000000000000000000000000000000000000
1025 abort: unknown revision '1000000000000000000000000000000000000000'!
1025 abort: unknown revision '1000000000000000000000000000000000000000'!
1026 [255]
1026 [255]
1027
1027
1028 log -k r1
1028 log -k r1
1029
1029
1030 $ hg log -k r1
1030 $ hg log -k r1
1031 changeset: 1:3d5bf5654eda
1031 changeset: 1:3d5bf5654eda
1032 user: test
1032 user: test
1033 date: Thu Jan 01 00:00:01 1970 +0000
1033 date: Thu Jan 01 00:00:01 1970 +0000
1034 summary: r1
1034 summary: r1
1035
1035
1036 log -p -l2 --color=always
1036 log -p -l2 --color=always
1037
1037
1038 $ hg --config extensions.color= --config color.mode=ansi \
1038 $ hg --config extensions.color= --config color.mode=ansi \
1039 > log -p -l2 --color=always
1039 > log -p -l2 --color=always
1040 \x1b[0;33mchangeset: 6:2404bbcab562\x1b[0m (esc)
1040 \x1b[0;33mchangeset: 6:2404bbcab562\x1b[0m (esc)
1041 tag: tip
1041 tag: tip
1042 user: test
1042 user: test
1043 date: Thu Jan 01 00:00:01 1970 +0000
1043 date: Thu Jan 01 00:00:01 1970 +0000
1044 summary: b1.1
1044 summary: b1.1
1045
1045
1046 \x1b[0;1mdiff -r 302e9dd6890d -r 2404bbcab562 b1\x1b[0m (esc)
1046 \x1b[0;1mdiff -r 302e9dd6890d -r 2404bbcab562 b1\x1b[0m (esc)
1047 \x1b[0;31;1m--- a/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
1047 \x1b[0;31;1m--- a/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
1048 \x1b[0;32;1m+++ b/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
1048 \x1b[0;32;1m+++ b/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
1049 \x1b[0;35m@@ -1,1 +1,2 @@\x1b[0m (esc)
1049 \x1b[0;35m@@ -1,1 +1,2 @@\x1b[0m (esc)
1050 b1
1050 b1
1051 \x1b[0;32m+postm\x1b[0m (esc)
1051 \x1b[0;32m+postm\x1b[0m (esc)
1052
1052
1053 \x1b[0;33mchangeset: 5:302e9dd6890d\x1b[0m (esc)
1053 \x1b[0;33mchangeset: 5:302e9dd6890d\x1b[0m (esc)
1054 parent: 3:e62f78d544b4
1054 parent: 3:e62f78d544b4
1055 parent: 4:ddb82e70d1a1
1055 parent: 4:ddb82e70d1a1
1056 user: test
1056 user: test
1057 date: Thu Jan 01 00:00:01 1970 +0000
1057 date: Thu Jan 01 00:00:01 1970 +0000
1058 summary: m12
1058 summary: m12
1059
1059
1060 \x1b[0;1mdiff -r e62f78d544b4 -r 302e9dd6890d b2\x1b[0m (esc)
1060 \x1b[0;1mdiff -r e62f78d544b4 -r 302e9dd6890d b2\x1b[0m (esc)
1061 \x1b[0;31;1m--- /dev/null Thu Jan 01 00:00:00 1970 +0000\x1b[0m (esc)
1061 \x1b[0;31;1m--- /dev/null Thu Jan 01 00:00:00 1970 +0000\x1b[0m (esc)
1062 \x1b[0;32;1m+++ b/b2 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
1062 \x1b[0;32;1m+++ b/b2 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
1063 \x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m (esc)
1063 \x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m (esc)
1064 \x1b[0;32m+b2\x1b[0m (esc)
1064 \x1b[0;32m+b2\x1b[0m (esc)
1065
1065
1066
1066
1067
1067
1068 log -r tip --stat
1068 log -r tip --stat
1069
1069
1070 $ hg log -r tip --stat
1070 $ hg log -r tip --stat
1071 changeset: 6:2404bbcab562
1071 changeset: 6:2404bbcab562
1072 tag: tip
1072 tag: tip
1073 user: test
1073 user: test
1074 date: Thu Jan 01 00:00:01 1970 +0000
1074 date: Thu Jan 01 00:00:01 1970 +0000
1075 summary: b1.1
1075 summary: b1.1
1076
1076
1077 b1 | 1 +
1077 b1 | 1 +
1078 1 files changed, 1 insertions(+), 0 deletions(-)
1078 1 files changed, 1 insertions(+), 0 deletions(-)
1079
1079
1080
1080
1081 $ cd ..
1081 $ cd ..
1082
1082
1083 log --follow --patch FILE in repository where linkrev isn't trustworthy
1083 log --follow --patch FILE in repository where linkrev isn't trustworthy
1084 (issue5376, issue6124)
1084 (issue5376, issue6124)
1085
1085
1086 $ hg init follow-dup
1086 $ hg init follow-dup
1087 $ cd follow-dup
1087 $ cd follow-dup
1088 $ cat <<EOF >> .hg/hgrc
1088 $ cat <<EOF >> .hg/hgrc
1089 > [ui]
1089 > [ui]
1090 > logtemplate = '=== {rev}: {desc}\n'
1090 > logtemplate = '=== {rev}: {desc}\n'
1091 > [diff]
1091 > [diff]
1092 > nodates = True
1092 > nodates = True
1093 > EOF
1093 > EOF
1094 $ echo 0 >> a
1094 $ echo 0 >> a
1095 $ hg ci -qAm 'a0'
1095 $ hg ci -qAm 'a0'
1096 $ echo 1 >> a
1096 $ echo 1 >> a
1097 $ hg ci -m 'a1'
1097 $ hg ci -m 'a1'
1098 $ hg up -q 0
1098 $ hg up -q 0
1099 $ echo 1 >> a
1099 $ echo 1 >> a
1100 $ touch b
1100 $ touch b
1101 $ hg ci -qAm 'a1 with b'
1101 $ hg ci -qAm 'a1 with b'
1102 $ echo 3 >> a
1102 $ echo 3 >> a
1103 $ hg ci -m 'a3'
1103 $ hg ci -m 'a3'
1104
1104
1105 fctx.rev() == 2, but fctx.linkrev() == 1
1105 fctx.rev() == 2, but fctx.linkrev() == 1
1106
1106
1107 $ hg log -pf a
1107 $ hg log -pf a
1108 === 3: a3
1108 === 3: a3
1109 diff -r 4ea02ba94d66 -r e7a6331a34f0 a
1109 diff -r 4ea02ba94d66 -r e7a6331a34f0 a
1110 --- a/a
1110 --- a/a
1111 +++ b/a
1111 +++ b/a
1112 @@ -1,2 +1,3 @@
1112 @@ -1,2 +1,3 @@
1113 0
1113 0
1114 1
1114 1
1115 +3
1115 +3
1116
1116
1117 === 2: a1 with b
1117 === 2: a1 with b
1118 diff -r 49b5e81287e2 -r 4ea02ba94d66 a
1118 diff -r 49b5e81287e2 -r 4ea02ba94d66 a
1119 --- a/a
1119 --- a/a
1120 +++ b/a
1120 +++ b/a
1121 @@ -1,1 +1,2 @@
1121 @@ -1,1 +1,2 @@
1122 0
1122 0
1123 +1
1123 +1
1124
1124
1125 === 0: a0
1125 === 0: a0
1126 diff -r 000000000000 -r 49b5e81287e2 a
1126 diff -r 000000000000 -r 49b5e81287e2 a
1127 --- /dev/null
1127 --- /dev/null
1128 +++ b/a
1128 +++ b/a
1129 @@ -0,0 +1,1 @@
1129 @@ -0,0 +1,1 @@
1130 +0
1130 +0
1131
1131
1132 $ hg log -pr . a
1132 $ hg log -pr . a
1133 === 3: a3
1133 === 3: a3
1134 diff -r 4ea02ba94d66 -r e7a6331a34f0 a
1134 diff -r 4ea02ba94d66 -r e7a6331a34f0 a
1135 --- a/a
1135 --- a/a
1136 +++ b/a
1136 +++ b/a
1137 @@ -1,2 +1,3 @@
1137 @@ -1,2 +1,3 @@
1138 0
1138 0
1139 1
1139 1
1140 +3
1140 +3
1141
1141
1142
1142
1143 fctx.introrev() == 2, but fctx.linkrev() == 1
1143 fctx.introrev() == 2, but fctx.linkrev() == 1
1144
1144
1145 $ hg up -q 2
1145 $ hg up -q 2
1146 $ hg log -pf a
1146 $ hg log -pf a
1147 === 2: a1 with b
1147 === 2: a1 with b
1148 diff -r 49b5e81287e2 -r 4ea02ba94d66 a
1148 diff -r 49b5e81287e2 -r 4ea02ba94d66 a
1149 --- a/a
1149 --- a/a
1150 +++ b/a
1150 +++ b/a
1151 @@ -1,1 +1,2 @@
1151 @@ -1,1 +1,2 @@
1152 0
1152 0
1153 +1
1153 +1
1154
1154
1155 === 0: a0
1155 === 0: a0
1156 diff -r 000000000000 -r 49b5e81287e2 a
1156 diff -r 000000000000 -r 49b5e81287e2 a
1157 --- /dev/null
1157 --- /dev/null
1158 +++ b/a
1158 +++ b/a
1159 @@ -0,0 +1,1 @@
1159 @@ -0,0 +1,1 @@
1160 +0
1160 +0
1161
1161
1162
1162
1163 BROKEN: should show the same diff as for rev 2 above
1163 BROKEN: should show the same diff as for rev 2 above
1164 $ hg log -pr . a
1164 $ hg log -pr . a
1165
1165
1166 $ cd ..
1166 $ cd ..
1167
1167
1168 Multiple copy sources of a file:
1168 Multiple copy sources of a file:
1169
1169
1170 $ hg init follow-multi
1170 $ hg init follow-multi
1171 $ cd follow-multi
1171 $ cd follow-multi
1172 $ echo 0 >> a
1172 $ echo 0 >> a
1173 $ hg ci -qAm 'a'
1173 $ hg ci -qAm 'a'
1174 $ hg cp a b
1174 $ hg cp a b
1175 $ hg ci -m 'a->b'
1175 $ hg ci -m 'a->b'
1176 $ echo 2 >> a
1176 $ echo 2 >> a
1177 $ hg ci -m 'a'
1177 $ hg ci -m 'a'
1178 $ echo 3 >> b
1178 $ echo 3 >> b
1179 $ hg ci -m 'b'
1179 $ hg ci -m 'b'
1180 $ echo 4 >> a
1180 $ echo 4 >> a
1181 $ echo 4 >> b
1181 $ echo 4 >> b
1182 $ hg ci -m 'a,b'
1182 $ hg ci -m 'a,b'
1183 $ echo 5 >> a
1183 $ echo 5 >> a
1184 $ hg ci -m 'a0'
1184 $ hg ci -m 'a0'
1185 $ echo 6 >> b
1185 $ echo 6 >> b
1186 $ hg ci -m 'b0'
1186 $ hg ci -m 'b0'
1187 $ hg up -q 4
1187 $ hg up -q 4
1188 $ echo 7 >> b
1188 $ echo 7 >> b
1189 $ hg ci -m 'b1'
1189 $ hg ci -m 'b1'
1190 created new head
1190 created new head
1191 $ echo 8 >> a
1191 $ echo 8 >> a
1192 $ hg ci -m 'a1'
1192 $ hg ci -m 'a1'
1193 $ hg rm a
1193 $ hg rm a
1194 $ hg mv b a
1194 $ hg mv b a
1195 $ hg ci -m 'b1->a1'
1195 $ hg ci -m 'b1->a1'
1196 $ hg merge -qt :local
1196 $ hg merge -qt :local
1197 $ hg ci -m '(a0,b1->a1)->a'
1197 $ hg ci -m '(a0,b1->a1)->a'
1198
1198
1199 $ hg log -GT '{rev}: {desc}\n'
1199 $ hg log -GT '{rev}: {desc}\n'
1200 @ 10: (a0,b1->a1)->a
1200 @ 10: (a0,b1->a1)->a
1201 |\
1201 |\
1202 | o 9: b1->a1
1202 | o 9: b1->a1
1203 | |
1203 | |
1204 | o 8: a1
1204 | o 8: a1
1205 | |
1205 | |
1206 | o 7: b1
1206 | o 7: b1
1207 | |
1207 | |
1208 o | 6: b0
1208 o | 6: b0
1209 | |
1209 | |
1210 o | 5: a0
1210 o | 5: a0
1211 |/
1211 |/
1212 o 4: a,b
1212 o 4: a,b
1213 |
1213 |
1214 o 3: b
1214 o 3: b
1215 |
1215 |
1216 o 2: a
1216 o 2: a
1217 |
1217 |
1218 o 1: a->b
1218 o 1: a->b
1219 |
1219 |
1220 o 0: a
1220 o 0: a
1221
1221
1222
1222
1223 since file 'a' has multiple copy sources at the revision 4, ancestors can't
1223 since file 'a' has multiple copy sources at the revision 4, ancestors can't
1224 be indexed solely by fctx.linkrev().
1224 be indexed solely by fctx.linkrev().
1225
1225
1226 $ hg log -T '{rev}: {desc}\n' -f a
1226 $ hg log -T '{rev}: {desc}\n' -f a
1227 10: (a0,b1->a1)->a
1227 10: (a0,b1->a1)->a
1228 9: b1->a1
1228 9: b1->a1
1229 7: b1
1229 7: b1
1230 5: a0
1230 5: a0
1231 4: a,b
1231 4: a,b
1232 3: b
1232 3: b
1233 2: a
1233 2: a
1234 1: a->b
1234 1: a->b
1235 0: a
1235 0: a
1236
1236
1237 $ cd ..
1237 $ cd ..
1238
1238
1239 Test that log should respect the order of -rREV even if multiple OR conditions
1239 Test that log should respect the order of -rREV even if multiple OR conditions
1240 are specified (issue5100):
1240 are specified (issue5100):
1241
1241
1242 $ hg init revorder
1242 $ hg init revorder
1243 $ cd revorder
1243 $ cd revorder
1244
1244
1245 $ hg branch -q b0
1245 $ hg branch -q b0
1246 $ echo 0 >> f0
1246 $ echo 0 >> f0
1247 $ hg ci -qAm k0 -u u0
1247 $ hg ci -qAm k0 -u u0
1248 $ hg branch -q b1
1248 $ hg branch -q b1
1249 $ echo 1 >> f1
1249 $ echo 1 >> f1
1250 $ hg ci -qAm k1 -u u1
1250 $ hg ci -qAm k1 -u u1
1251 $ hg branch -q b2
1251 $ hg branch -q b2
1252 $ echo 2 >> f2
1252 $ echo 2 >> f2
1253 $ hg ci -qAm k2 -u u2
1253 $ hg ci -qAm k2 -u u2
1254
1254
1255 $ hg update -q b2
1255 $ hg update -q b2
1256 $ echo 3 >> f2
1256 $ echo 3 >> f2
1257 $ hg ci -qAm k2 -u u2
1257 $ hg ci -qAm k2 -u u2
1258 $ hg update -q b1
1258 $ hg update -q b1
1259 $ echo 4 >> f1
1259 $ echo 4 >> f1
1260 $ hg ci -qAm k1 -u u1
1260 $ hg ci -qAm k1 -u u1
1261 $ hg update -q b0
1261 $ hg update -q b0
1262 $ echo 5 >> f0
1262 $ echo 5 >> f0
1263 $ hg ci -qAm k0 -u u0
1263 $ hg ci -qAm k0 -u u0
1264
1264
1265 summary of revisions:
1265 summary of revisions:
1266
1266
1267 $ hg log -G -T '{rev} {branch} {author} {desc} {files}\n'
1267 $ hg log -G -T '{rev} {branch} {author} {desc} {files}\n'
1268 @ 5 b0 u0 k0 f0
1268 @ 5 b0 u0 k0 f0
1269 |
1269 |
1270 | o 4 b1 u1 k1 f1
1270 | o 4 b1 u1 k1 f1
1271 | |
1271 | |
1272 | | o 3 b2 u2 k2 f2
1272 | | o 3 b2 u2 k2 f2
1273 | | |
1273 | | |
1274 | | o 2 b2 u2 k2 f2
1274 | | o 2 b2 u2 k2 f2
1275 | |/
1275 | |/
1276 | o 1 b1 u1 k1 f1
1276 | o 1 b1 u1 k1 f1
1277 |/
1277 |/
1278 o 0 b0 u0 k0 f0
1278 o 0 b0 u0 k0 f0
1279
1279
1280
1280
1281 log -b BRANCH in ascending order:
1281 log -b BRANCH in ascending order:
1282
1282
1283 $ hg log -r0:tip -T '{rev} {branch}\n' -b b0 -b b1
1283 $ hg log -r0:tip -T '{rev} {branch}\n' -b b0 -b b1
1284 0 b0
1284 0 b0
1285 1 b1
1285 1 b1
1286 4 b1
1286 4 b1
1287 5 b0
1287 5 b0
1288 $ hg log -r0:tip -T '{rev} {branch}\n' -b b1 -b b0
1288 $ hg log -r0:tip -T '{rev} {branch}\n' -b b1 -b b0
1289 0 b0
1289 0 b0
1290 1 b1
1290 1 b1
1291 4 b1
1291 4 b1
1292 5 b0
1292 5 b0
1293
1293
1294 log --only-branch BRANCH in descending order:
1294 log --only-branch BRANCH in descending order:
1295
1295
1296 $ hg log -rtip:0 -T '{rev} {branch}\n' --only-branch b1 --only-branch b2
1296 $ hg log -rtip:0 -T '{rev} {branch}\n' --only-branch b1 --only-branch b2
1297 4 b1
1297 4 b1
1298 3 b2
1298 3 b2
1299 2 b2
1299 2 b2
1300 1 b1
1300 1 b1
1301 $ hg log -rtip:0 -T '{rev} {branch}\n' --only-branch b2 --only-branch b1
1301 $ hg log -rtip:0 -T '{rev} {branch}\n' --only-branch b2 --only-branch b1
1302 4 b1
1302 4 b1
1303 3 b2
1303 3 b2
1304 2 b2
1304 2 b2
1305 1 b1
1305 1 b1
1306
1306
1307 log -u USER in ascending order, against compound set:
1307 log -u USER in ascending order, against compound set:
1308
1308
1309 $ hg log -r'::head()' -T '{rev} {author}\n' -u u0 -u u2
1309 $ hg log -r'::head()' -T '{rev} {author}\n' -u u0 -u u2
1310 0 u0
1310 0 u0
1311 2 u2
1311 2 u2
1312 3 u2
1312 3 u2
1313 5 u0
1313 5 u0
1314 $ hg log -r'::head()' -T '{rev} {author}\n' -u u2 -u u0
1314 $ hg log -r'::head()' -T '{rev} {author}\n' -u u2 -u u0
1315 0 u0
1315 0 u0
1316 2 u2
1316 2 u2
1317 3 u2
1317 3 u2
1318 5 u0
1318 5 u0
1319
1319
1320 log -k TEXT in descending order, against compound set:
1320 log -k TEXT in descending order, against compound set:
1321
1321
1322 $ hg log -r'5 + reverse(::3)' -T '{rev} {desc}\n' -k k0 -k k1 -k k2
1322 $ hg log -r'5 + reverse(::3)' -T '{rev} {desc}\n' -k k0 -k k1 -k k2
1323 5 k0
1323 5 k0
1324 3 k2
1324 3 k2
1325 2 k2
1325 2 k2
1326 1 k1
1326 1 k1
1327 0 k0
1327 0 k0
1328 $ hg log -r'5 + reverse(::3)' -T '{rev} {desc}\n' -k k2 -k k1 -k k0
1328 $ hg log -r'5 + reverse(::3)' -T '{rev} {desc}\n' -k k2 -k k1 -k k0
1329 5 k0
1329 5 k0
1330 3 k2
1330 3 k2
1331 2 k2
1331 2 k2
1332 1 k1
1332 1 k1
1333 0 k0
1333 0 k0
1334
1334
1335 log FILE in ascending order, against dagrange:
1335 log FILE in ascending order, against dagrange:
1336
1336
1337 $ hg log -r1:: -T '{rev} {files}\n' f1 f2
1337 $ hg log -r1:: -T '{rev} {files}\n' f1 f2
1338 1 f1
1338 1 f1
1339 2 f2
1339 2 f2
1340 3 f2
1340 3 f2
1341 4 f1
1341 4 f1
1342 $ hg log -r1:: -T '{rev} {files}\n' f2 f1
1342 $ hg log -r1:: -T '{rev} {files}\n' f2 f1
1343 1 f1
1343 1 f1
1344 2 f2
1344 2 f2
1345 3 f2
1345 3 f2
1346 4 f1
1346 4 f1
1347
1347
1348 $ cd ..
1348 $ cd ..
1349
1349
1350 User
1350 User
1351
1351
1352 $ hg init usertest
1352 $ hg init usertest
1353 $ cd usertest
1353 $ cd usertest
1354
1354
1355 $ echo a > a
1355 $ echo a > a
1356 $ hg ci -A -m "a" -u "User One <user1@example.org>"
1356 $ hg ci -A -m "a" -u "User One <user1@example.org>"
1357 adding a
1357 adding a
1358 $ echo b > b
1358 $ echo b > b
1359 $ hg ci -A -m "b" -u "User Two <user2@example.org>"
1359 $ hg ci -A -m "b" -u "User Two <user2@example.org>"
1360 adding b
1360 adding b
1361
1361
1362 $ hg log -u "User One <user1@example.org>"
1362 $ hg log -u "User One <user1@example.org>"
1363 changeset: 0:29a4c94f1924
1363 changeset: 0:29a4c94f1924
1364 user: User One <user1@example.org>
1364 user: User One <user1@example.org>
1365 date: Thu Jan 01 00:00:00 1970 +0000
1365 date: Thu Jan 01 00:00:00 1970 +0000
1366 summary: a
1366 summary: a
1367
1367
1368 $ hg log -u "user1" -u "user2"
1368 $ hg log -u "user1" -u "user2"
1369 changeset: 1:e834b5e69c0e
1369 changeset: 1:e834b5e69c0e
1370 tag: tip
1370 tag: tip
1371 user: User Two <user2@example.org>
1371 user: User Two <user2@example.org>
1372 date: Thu Jan 01 00:00:00 1970 +0000
1372 date: Thu Jan 01 00:00:00 1970 +0000
1373 summary: b
1373 summary: b
1374
1374
1375 changeset: 0:29a4c94f1924
1375 changeset: 0:29a4c94f1924
1376 user: User One <user1@example.org>
1376 user: User One <user1@example.org>
1377 date: Thu Jan 01 00:00:00 1970 +0000
1377 date: Thu Jan 01 00:00:00 1970 +0000
1378 summary: a
1378 summary: a
1379
1379
1380 $ hg log -u "user3"
1380 $ hg log -u "user3"
1381
1381
1382 "-u USER" shouldn't be overridden by "user(USER)" alias
1382 "-u USER" shouldn't be overridden by "user(USER)" alias
1383
1383
1384 $ hg log --config 'revsetalias.user(x)=branch(x)' -u default
1384 $ hg log --config 'revsetalias.user(x)=branch(x)' -u default
1385 $ hg log --config 'revsetalias.user(x)=branch(x)' -u user1
1385 $ hg log --config 'revsetalias.user(x)=branch(x)' -u user1
1386 changeset: 0:29a4c94f1924
1386 changeset: 0:29a4c94f1924
1387 user: User One <user1@example.org>
1387 user: User One <user1@example.org>
1388 date: Thu Jan 01 00:00:00 1970 +0000
1388 date: Thu Jan 01 00:00:00 1970 +0000
1389 summary: a
1389 summary: a
1390
1390
1391
1391
1392 $ cd ..
1392 $ cd ..
1393
1393
1394 $ hg init branches
1394 $ hg init branches
1395 $ cd branches
1395 $ cd branches
1396
1396
1397 $ echo a > a
1397 $ echo a > a
1398 $ hg ci -A -m "commit on default"
1398 $ hg ci -A -m "commit on default"
1399 adding a
1399 adding a
1400 $ hg branch test
1400 $ hg branch test
1401 marked working directory as branch test
1401 marked working directory as branch test
1402 (branches are permanent and global, did you want a bookmark?)
1402 (branches are permanent and global, did you want a bookmark?)
1403 $ echo b > b
1403 $ echo b > b
1404 $ hg ci -A -m "commit on test"
1404 $ hg ci -A -m "commit on test"
1405 adding b
1405 adding b
1406
1406
1407 $ hg up default
1407 $ hg up default
1408 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1408 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1409 $ echo c > c
1409 $ echo c > c
1410 $ hg ci -A -m "commit on default"
1410 $ hg ci -A -m "commit on default"
1411 adding c
1411 adding c
1412 $ hg up test
1412 $ hg up test
1413 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1413 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1414 $ echo c > c
1414 $ echo c > c
1415 $ hg ci -A -m "commit on test"
1415 $ hg ci -A -m "commit on test"
1416 adding c
1416 adding c
1417
1417
1418
1418
1419 log -b default
1419 log -b default
1420
1420
1421 $ hg log -b default
1421 $ hg log -b default
1422 changeset: 2:c3a4f03cc9a7
1422 changeset: 2:c3a4f03cc9a7
1423 parent: 0:24427303d56f
1423 parent: 0:24427303d56f
1424 user: test
1424 user: test
1425 date: Thu Jan 01 00:00:00 1970 +0000
1425 date: Thu Jan 01 00:00:00 1970 +0000
1426 summary: commit on default
1426 summary: commit on default
1427
1427
1428 changeset: 0:24427303d56f
1428 changeset: 0:24427303d56f
1429 user: test
1429 user: test
1430 date: Thu Jan 01 00:00:00 1970 +0000
1430 date: Thu Jan 01 00:00:00 1970 +0000
1431 summary: commit on default
1431 summary: commit on default
1432
1432
1433
1433
1434
1434
1435 log -b test
1435 log -b test
1436
1436
1437 $ hg log -b test
1437 $ hg log -b test
1438 changeset: 3:f5d8de11c2e2
1438 changeset: 3:f5d8de11c2e2
1439 branch: test
1439 branch: test
1440 tag: tip
1440 tag: tip
1441 parent: 1:d32277701ccb
1441 parent: 1:d32277701ccb
1442 user: test
1442 user: test
1443 date: Thu Jan 01 00:00:00 1970 +0000
1443 date: Thu Jan 01 00:00:00 1970 +0000
1444 summary: commit on test
1444 summary: commit on test
1445
1445
1446 changeset: 1:d32277701ccb
1446 changeset: 1:d32277701ccb
1447 branch: test
1447 branch: test
1448 user: test
1448 user: test
1449 date: Thu Jan 01 00:00:00 1970 +0000
1449 date: Thu Jan 01 00:00:00 1970 +0000
1450 summary: commit on test
1450 summary: commit on test
1451
1451
1452
1452
1453
1453
1454 log -b dummy
1454 log -b dummy
1455
1455
1456 $ hg log -b dummy
1456 $ hg log -b dummy
1457 abort: unknown revision 'dummy'!
1457 abort: unknown revision 'dummy'!
1458 [255]
1458 [255]
1459
1459
1460
1460
1461 log -b .
1461 log -b .
1462
1462
1463 $ hg log -b .
1463 $ hg log -b .
1464 changeset: 3:f5d8de11c2e2
1464 changeset: 3:f5d8de11c2e2
1465 branch: test
1465 branch: test
1466 tag: tip
1466 tag: tip
1467 parent: 1:d32277701ccb
1467 parent: 1:d32277701ccb
1468 user: test
1468 user: test
1469 date: Thu Jan 01 00:00:00 1970 +0000
1469 date: Thu Jan 01 00:00:00 1970 +0000
1470 summary: commit on test
1470 summary: commit on test
1471
1471
1472 changeset: 1:d32277701ccb
1472 changeset: 1:d32277701ccb
1473 branch: test
1473 branch: test
1474 user: test
1474 user: test
1475 date: Thu Jan 01 00:00:00 1970 +0000
1475 date: Thu Jan 01 00:00:00 1970 +0000
1476 summary: commit on test
1476 summary: commit on test
1477
1477
1478
1478
1479
1479
1480 log -b default -b test
1480 log -b default -b test
1481
1481
1482 $ hg log -b default -b test
1482 $ hg log -b default -b test
1483 changeset: 3:f5d8de11c2e2
1483 changeset: 3:f5d8de11c2e2
1484 branch: test
1484 branch: test
1485 tag: tip
1485 tag: tip
1486 parent: 1:d32277701ccb
1486 parent: 1:d32277701ccb
1487 user: test
1487 user: test
1488 date: Thu Jan 01 00:00:00 1970 +0000
1488 date: Thu Jan 01 00:00:00 1970 +0000
1489 summary: commit on test
1489 summary: commit on test
1490
1490
1491 changeset: 2:c3a4f03cc9a7
1491 changeset: 2:c3a4f03cc9a7
1492 parent: 0:24427303d56f
1492 parent: 0:24427303d56f
1493 user: test
1493 user: test
1494 date: Thu Jan 01 00:00:00 1970 +0000
1494 date: Thu Jan 01 00:00:00 1970 +0000
1495 summary: commit on default
1495 summary: commit on default
1496
1496
1497 changeset: 1:d32277701ccb
1497 changeset: 1:d32277701ccb
1498 branch: test
1498 branch: test
1499 user: test
1499 user: test
1500 date: Thu Jan 01 00:00:00 1970 +0000
1500 date: Thu Jan 01 00:00:00 1970 +0000
1501 summary: commit on test
1501 summary: commit on test
1502
1502
1503 changeset: 0:24427303d56f
1503 changeset: 0:24427303d56f
1504 user: test
1504 user: test
1505 date: Thu Jan 01 00:00:00 1970 +0000
1505 date: Thu Jan 01 00:00:00 1970 +0000
1506 summary: commit on default
1506 summary: commit on default
1507
1507
1508
1508
1509
1509
1510 log -b default -b .
1510 log -b default -b .
1511
1511
1512 $ hg log -b default -b .
1512 $ hg log -b default -b .
1513 changeset: 3:f5d8de11c2e2
1513 changeset: 3:f5d8de11c2e2
1514 branch: test
1514 branch: test
1515 tag: tip
1515 tag: tip
1516 parent: 1:d32277701ccb
1516 parent: 1:d32277701ccb
1517 user: test
1517 user: test
1518 date: Thu Jan 01 00:00:00 1970 +0000
1518 date: Thu Jan 01 00:00:00 1970 +0000
1519 summary: commit on test
1519 summary: commit on test
1520
1520
1521 changeset: 2:c3a4f03cc9a7
1521 changeset: 2:c3a4f03cc9a7
1522 parent: 0:24427303d56f
1522 parent: 0:24427303d56f
1523 user: test
1523 user: test
1524 date: Thu Jan 01 00:00:00 1970 +0000
1524 date: Thu Jan 01 00:00:00 1970 +0000
1525 summary: commit on default
1525 summary: commit on default
1526
1526
1527 changeset: 1:d32277701ccb
1527 changeset: 1:d32277701ccb
1528 branch: test
1528 branch: test
1529 user: test
1529 user: test
1530 date: Thu Jan 01 00:00:00 1970 +0000
1530 date: Thu Jan 01 00:00:00 1970 +0000
1531 summary: commit on test
1531 summary: commit on test
1532
1532
1533 changeset: 0:24427303d56f
1533 changeset: 0:24427303d56f
1534 user: test
1534 user: test
1535 date: Thu Jan 01 00:00:00 1970 +0000
1535 date: Thu Jan 01 00:00:00 1970 +0000
1536 summary: commit on default
1536 summary: commit on default
1537
1537
1538
1538
1539
1539
1540 log -b . -b test
1540 log -b . -b test
1541
1541
1542 $ hg log -b . -b test
1542 $ hg log -b . -b test
1543 changeset: 3:f5d8de11c2e2
1543 changeset: 3:f5d8de11c2e2
1544 branch: test
1544 branch: test
1545 tag: tip
1545 tag: tip
1546 parent: 1:d32277701ccb
1546 parent: 1:d32277701ccb
1547 user: test
1547 user: test
1548 date: Thu Jan 01 00:00:00 1970 +0000
1548 date: Thu Jan 01 00:00:00 1970 +0000
1549 summary: commit on test
1549 summary: commit on test
1550
1550
1551 changeset: 1:d32277701ccb
1551 changeset: 1:d32277701ccb
1552 branch: test
1552 branch: test
1553 user: test
1553 user: test
1554 date: Thu Jan 01 00:00:00 1970 +0000
1554 date: Thu Jan 01 00:00:00 1970 +0000
1555 summary: commit on test
1555 summary: commit on test
1556
1556
1557
1557
1558
1558
1559 log -b 2
1559 log -b 2
1560
1560
1561 $ hg log -b 2
1561 $ hg log -b 2
1562 changeset: 2:c3a4f03cc9a7
1562 changeset: 2:c3a4f03cc9a7
1563 parent: 0:24427303d56f
1563 parent: 0:24427303d56f
1564 user: test
1564 user: test
1565 date: Thu Jan 01 00:00:00 1970 +0000
1565 date: Thu Jan 01 00:00:00 1970 +0000
1566 summary: commit on default
1566 summary: commit on default
1567
1567
1568 changeset: 0:24427303d56f
1568 changeset: 0:24427303d56f
1569 user: test
1569 user: test
1570 date: Thu Jan 01 00:00:00 1970 +0000
1570 date: Thu Jan 01 00:00:00 1970 +0000
1571 summary: commit on default
1571 summary: commit on default
1572
1572
1573 #if gettext
1573 #if gettext
1574
1574
1575 Test that all log names are translated (e.g. branches, bookmarks, tags):
1575 Test that all log names are translated (e.g. branches, bookmarks, tags):
1576
1576
1577 $ hg bookmark babar -r tip
1577 $ hg bookmark babar -r tip
1578
1578
1579 $ HGENCODING=UTF-8 LANGUAGE=de hg log -r tip
1579 $ HGENCODING=UTF-8 LANGUAGE=de hg log -r tip
1580 \xc3\x84nderung: 3:f5d8de11c2e2 (esc)
1580 \xc3\x84nderung: 3:f5d8de11c2e2 (esc)
1581 Zweig: test
1581 Zweig: test
1582 Lesezeichen: babar
1582 Lesezeichen: babar
1583 Marke: tip
1583 Marke: tip
1584 Vorg\xc3\xa4nger: 1:d32277701ccb (esc)
1584 Vorg\xc3\xa4nger: 1:d32277701ccb (esc)
1585 Nutzer: test
1585 Nutzer: test
1586 Datum: Thu Jan 01 00:00:00 1970 +0000
1586 Datum: Thu Jan 01 00:00:00 1970 +0000
1587 Zusammenfassung: commit on test
1587 Zusammenfassung: commit on test
1588
1588
1589 $ hg bookmark -d babar
1589 $ hg bookmark -d babar
1590
1590
1591 #endif
1591 #endif
1592
1592
1593 log -p --cwd dir (in subdir)
1593 log -p --cwd dir (in subdir)
1594
1594
1595 $ mkdir dir
1595 $ mkdir dir
1596 $ hg log -p --cwd dir
1596 $ hg log -p --cwd dir
1597 changeset: 3:f5d8de11c2e2
1597 changeset: 3:f5d8de11c2e2
1598 branch: test
1598 branch: test
1599 tag: tip
1599 tag: tip
1600 parent: 1:d32277701ccb
1600 parent: 1:d32277701ccb
1601 user: test
1601 user: test
1602 date: Thu Jan 01 00:00:00 1970 +0000
1602 date: Thu Jan 01 00:00:00 1970 +0000
1603 summary: commit on test
1603 summary: commit on test
1604
1604
1605 diff -r d32277701ccb -r f5d8de11c2e2 c
1605 diff -r d32277701ccb -r f5d8de11c2e2 c
1606 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1606 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1607 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1607 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1608 @@ -0,0 +1,1 @@
1608 @@ -0,0 +1,1 @@
1609 +c
1609 +c
1610
1610
1611 changeset: 2:c3a4f03cc9a7
1611 changeset: 2:c3a4f03cc9a7
1612 parent: 0:24427303d56f
1612 parent: 0:24427303d56f
1613 user: test
1613 user: test
1614 date: Thu Jan 01 00:00:00 1970 +0000
1614 date: Thu Jan 01 00:00:00 1970 +0000
1615 summary: commit on default
1615 summary: commit on default
1616
1616
1617 diff -r 24427303d56f -r c3a4f03cc9a7 c
1617 diff -r 24427303d56f -r c3a4f03cc9a7 c
1618 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1618 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1619 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1619 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1620 @@ -0,0 +1,1 @@
1620 @@ -0,0 +1,1 @@
1621 +c
1621 +c
1622
1622
1623 changeset: 1:d32277701ccb
1623 changeset: 1:d32277701ccb
1624 branch: test
1624 branch: test
1625 user: test
1625 user: test
1626 date: Thu Jan 01 00:00:00 1970 +0000
1626 date: Thu Jan 01 00:00:00 1970 +0000
1627 summary: commit on test
1627 summary: commit on test
1628
1628
1629 diff -r 24427303d56f -r d32277701ccb b
1629 diff -r 24427303d56f -r d32277701ccb b
1630 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1630 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1631 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1631 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1632 @@ -0,0 +1,1 @@
1632 @@ -0,0 +1,1 @@
1633 +b
1633 +b
1634
1634
1635 changeset: 0:24427303d56f
1635 changeset: 0:24427303d56f
1636 user: test
1636 user: test
1637 date: Thu Jan 01 00:00:00 1970 +0000
1637 date: Thu Jan 01 00:00:00 1970 +0000
1638 summary: commit on default
1638 summary: commit on default
1639
1639
1640 diff -r 000000000000 -r 24427303d56f a
1640 diff -r 000000000000 -r 24427303d56f a
1641 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1641 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1642 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1642 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1643 @@ -0,0 +1,1 @@
1643 @@ -0,0 +1,1 @@
1644 +a
1644 +a
1645
1645
1646
1646
1647
1647
1648 log -p -R repo
1648 log -p -R repo
1649
1649
1650 $ cd dir
1650 $ cd dir
1651 $ hg log -p -R .. ../a
1651 $ hg log -p -R .. ../a
1652 changeset: 0:24427303d56f
1652 changeset: 0:24427303d56f
1653 user: test
1653 user: test
1654 date: Thu Jan 01 00:00:00 1970 +0000
1654 date: Thu Jan 01 00:00:00 1970 +0000
1655 summary: commit on default
1655 summary: commit on default
1656
1656
1657 diff -r 000000000000 -r 24427303d56f a
1657 diff -r 000000000000 -r 24427303d56f a
1658 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1658 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1659 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1659 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1660 @@ -0,0 +1,1 @@
1660 @@ -0,0 +1,1 @@
1661 +a
1661 +a
1662
1662
1663
1663
1664 $ cd ../..
1664 $ cd ../..
1665
1665
1666 $ hg init follow2
1666 $ hg init follow2
1667 $ cd follow2
1667 $ cd follow2
1668
1668
1669 # Build the following history:
1669 # Build the following history:
1670 # tip - o - x - o - x - x
1670 # tip - o - x - o - x - x
1671 # \ /
1671 # \ /
1672 # o - o - o - x
1672 # o - o - o - x
1673 # \ /
1673 # \ /
1674 # o
1674 # o
1675 #
1675 #
1676 # Where "o" is a revision containing "foo" and
1676 # Where "o" is a revision containing "foo" and
1677 # "x" is a revision without "foo"
1677 # "x" is a revision without "foo"
1678
1678
1679 $ touch init
1679 $ touch init
1680 $ hg ci -A -m "init, unrelated"
1680 $ hg ci -A -m "init, unrelated"
1681 adding init
1681 adding init
1682 $ echo 'foo' > init
1682 $ echo 'foo' > init
1683 $ hg ci -m "change, unrelated"
1683 $ hg ci -m "change, unrelated"
1684 $ echo 'foo' > foo
1684 $ echo 'foo' > foo
1685 $ hg ci -A -m "add unrelated old foo"
1685 $ hg ci -A -m "add unrelated old foo"
1686 adding foo
1686 adding foo
1687 $ hg rm foo
1687 $ hg rm foo
1688 $ hg ci -m "delete foo, unrelated"
1688 $ hg ci -m "delete foo, unrelated"
1689 $ echo 'related' > foo
1689 $ echo 'related' > foo
1690 $ hg ci -A -m "add foo, related"
1690 $ hg ci -A -m "add foo, related"
1691 adding foo
1691 adding foo
1692
1692
1693 $ hg up 0
1693 $ hg up 0
1694 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1694 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1695 $ touch branch
1695 $ touch branch
1696 $ hg ci -A -m "first branch, unrelated"
1696 $ hg ci -A -m "first branch, unrelated"
1697 adding branch
1697 adding branch
1698 created new head
1698 created new head
1699 $ touch foo
1699 $ touch foo
1700 $ hg ci -A -m "create foo, related"
1700 $ hg ci -A -m "create foo, related"
1701 adding foo
1701 adding foo
1702 $ echo 'change' > foo
1702 $ echo 'change' > foo
1703 $ hg ci -m "change foo, related"
1703 $ hg ci -m "change foo, related"
1704
1704
1705 $ hg up 6
1705 $ hg up 6
1706 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1706 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1707 $ echo 'change foo in branch' > foo
1707 $ echo 'change foo in branch' > foo
1708 $ hg ci -m "change foo in branch, related"
1708 $ hg ci -m "change foo in branch, related"
1709 created new head
1709 created new head
1710 $ hg merge 7
1710 $ hg merge 7
1711 merging foo
1711 merging foo
1712 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
1712 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
1713 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1713 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1714 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1714 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1715 [1]
1715 [1]
1716 $ echo 'merge 1' > foo
1716 $ echo 'merge 1' > foo
1717 $ hg resolve -m foo
1717 $ hg resolve -m foo
1718 (no more unresolved files)
1718 (no more unresolved files)
1719 $ hg ci -m "First merge, related"
1719 $ hg ci -m "First merge, related"
1720
1720
1721 $ hg merge 4
1721 $ hg merge 4
1722 merging foo
1722 merging foo
1723 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
1723 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
1724 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
1724 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
1725 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1725 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1726 [1]
1726 [1]
1727 $ echo 'merge 2' > foo
1727 $ echo 'merge 2' > foo
1728 $ hg resolve -m foo
1728 $ hg resolve -m foo
1729 (no more unresolved files)
1729 (no more unresolved files)
1730 $ hg ci -m "Last merge, related"
1730 $ hg ci -m "Last merge, related"
1731
1731
1732 $ hg log --graph
1732 $ hg log --graph
1733 @ changeset: 10:4dae8563d2c5
1733 @ changeset: 10:4dae8563d2c5
1734 |\ tag: tip
1734 |\ tag: tip
1735 | | parent: 9:7b35701b003e
1735 | | parent: 9:7b35701b003e
1736 | | parent: 4:88176d361b69
1736 | | parent: 4:88176d361b69
1737 | | user: test
1737 | | user: test
1738 | | date: Thu Jan 01 00:00:00 1970 +0000
1738 | | date: Thu Jan 01 00:00:00 1970 +0000
1739 | | summary: Last merge, related
1739 | | summary: Last merge, related
1740 | |
1740 | |
1741 | o changeset: 9:7b35701b003e
1741 | o changeset: 9:7b35701b003e
1742 | |\ parent: 8:e5416ad8a855
1742 | |\ parent: 8:e5416ad8a855
1743 | | | parent: 7:87fe3144dcfa
1743 | | | parent: 7:87fe3144dcfa
1744 | | | user: test
1744 | | | user: test
1745 | | | date: Thu Jan 01 00:00:00 1970 +0000
1745 | | | date: Thu Jan 01 00:00:00 1970 +0000
1746 | | | summary: First merge, related
1746 | | | summary: First merge, related
1747 | | |
1747 | | |
1748 | | o changeset: 8:e5416ad8a855
1748 | | o changeset: 8:e5416ad8a855
1749 | | | parent: 6:dc6c325fe5ee
1749 | | | parent: 6:dc6c325fe5ee
1750 | | | user: test
1750 | | | user: test
1751 | | | date: Thu Jan 01 00:00:00 1970 +0000
1751 | | | date: Thu Jan 01 00:00:00 1970 +0000
1752 | | | summary: change foo in branch, related
1752 | | | summary: change foo in branch, related
1753 | | |
1753 | | |
1754 | o | changeset: 7:87fe3144dcfa
1754 | o | changeset: 7:87fe3144dcfa
1755 | |/ user: test
1755 | |/ user: test
1756 | | date: Thu Jan 01 00:00:00 1970 +0000
1756 | | date: Thu Jan 01 00:00:00 1970 +0000
1757 | | summary: change foo, related
1757 | | summary: change foo, related
1758 | |
1758 | |
1759 | o changeset: 6:dc6c325fe5ee
1759 | o changeset: 6:dc6c325fe5ee
1760 | | user: test
1760 | | user: test
1761 | | date: Thu Jan 01 00:00:00 1970 +0000
1761 | | date: Thu Jan 01 00:00:00 1970 +0000
1762 | | summary: create foo, related
1762 | | summary: create foo, related
1763 | |
1763 | |
1764 | o changeset: 5:73db34516eb9
1764 | o changeset: 5:73db34516eb9
1765 | | parent: 0:e87515fd044a
1765 | | parent: 0:e87515fd044a
1766 | | user: test
1766 | | user: test
1767 | | date: Thu Jan 01 00:00:00 1970 +0000
1767 | | date: Thu Jan 01 00:00:00 1970 +0000
1768 | | summary: first branch, unrelated
1768 | | summary: first branch, unrelated
1769 | |
1769 | |
1770 o | changeset: 4:88176d361b69
1770 o | changeset: 4:88176d361b69
1771 | | user: test
1771 | | user: test
1772 | | date: Thu Jan 01 00:00:00 1970 +0000
1772 | | date: Thu Jan 01 00:00:00 1970 +0000
1773 | | summary: add foo, related
1773 | | summary: add foo, related
1774 | |
1774 | |
1775 o | changeset: 3:dd78ae4afb56
1775 o | changeset: 3:dd78ae4afb56
1776 | | user: test
1776 | | user: test
1777 | | date: Thu Jan 01 00:00:00 1970 +0000
1777 | | date: Thu Jan 01 00:00:00 1970 +0000
1778 | | summary: delete foo, unrelated
1778 | | summary: delete foo, unrelated
1779 | |
1779 | |
1780 o | changeset: 2:c4c64aedf0f7
1780 o | changeset: 2:c4c64aedf0f7
1781 | | user: test
1781 | | user: test
1782 | | date: Thu Jan 01 00:00:00 1970 +0000
1782 | | date: Thu Jan 01 00:00:00 1970 +0000
1783 | | summary: add unrelated old foo
1783 | | summary: add unrelated old foo
1784 | |
1784 | |
1785 o | changeset: 1:e5faa7440653
1785 o | changeset: 1:e5faa7440653
1786 |/ user: test
1786 |/ user: test
1787 | date: Thu Jan 01 00:00:00 1970 +0000
1787 | date: Thu Jan 01 00:00:00 1970 +0000
1788 | summary: change, unrelated
1788 | summary: change, unrelated
1789 |
1789 |
1790 o changeset: 0:e87515fd044a
1790 o changeset: 0:e87515fd044a
1791 user: test
1791 user: test
1792 date: Thu Jan 01 00:00:00 1970 +0000
1792 date: Thu Jan 01 00:00:00 1970 +0000
1793 summary: init, unrelated
1793 summary: init, unrelated
1794
1794
1795
1795
1796 $ hg --traceback log -f foo
1796 $ hg --traceback log -f foo
1797 changeset: 10:4dae8563d2c5
1797 changeset: 10:4dae8563d2c5
1798 tag: tip
1798 tag: tip
1799 parent: 9:7b35701b003e
1799 parent: 9:7b35701b003e
1800 parent: 4:88176d361b69
1800 parent: 4:88176d361b69
1801 user: test
1801 user: test
1802 date: Thu Jan 01 00:00:00 1970 +0000
1802 date: Thu Jan 01 00:00:00 1970 +0000
1803 summary: Last merge, related
1803 summary: Last merge, related
1804
1804
1805 changeset: 9:7b35701b003e
1805 changeset: 9:7b35701b003e
1806 parent: 8:e5416ad8a855
1806 parent: 8:e5416ad8a855
1807 parent: 7:87fe3144dcfa
1807 parent: 7:87fe3144dcfa
1808 user: test
1808 user: test
1809 date: Thu Jan 01 00:00:00 1970 +0000
1809 date: Thu Jan 01 00:00:00 1970 +0000
1810 summary: First merge, related
1810 summary: First merge, related
1811
1811
1812 changeset: 8:e5416ad8a855
1812 changeset: 8:e5416ad8a855
1813 parent: 6:dc6c325fe5ee
1813 parent: 6:dc6c325fe5ee
1814 user: test
1814 user: test
1815 date: Thu Jan 01 00:00:00 1970 +0000
1815 date: Thu Jan 01 00:00:00 1970 +0000
1816 summary: change foo in branch, related
1816 summary: change foo in branch, related
1817
1817
1818 changeset: 7:87fe3144dcfa
1818 changeset: 7:87fe3144dcfa
1819 user: test
1819 user: test
1820 date: Thu Jan 01 00:00:00 1970 +0000
1820 date: Thu Jan 01 00:00:00 1970 +0000
1821 summary: change foo, related
1821 summary: change foo, related
1822
1822
1823 changeset: 6:dc6c325fe5ee
1823 changeset: 6:dc6c325fe5ee
1824 user: test
1824 user: test
1825 date: Thu Jan 01 00:00:00 1970 +0000
1825 date: Thu Jan 01 00:00:00 1970 +0000
1826 summary: create foo, related
1826 summary: create foo, related
1827
1827
1828 changeset: 4:88176d361b69
1828 changeset: 4:88176d361b69
1829 user: test
1829 user: test
1830 date: Thu Jan 01 00:00:00 1970 +0000
1830 date: Thu Jan 01 00:00:00 1970 +0000
1831 summary: add foo, related
1831 summary: add foo, related
1832
1832
1833
1833
1834 Also check when maxrev < lastrevfilelog
1834 Also check when maxrev < lastrevfilelog
1835
1835
1836 $ hg --traceback log -f -r4 foo
1836 $ hg --traceback log -f -r4 foo
1837 changeset: 4:88176d361b69
1837 changeset: 4:88176d361b69
1838 user: test
1838 user: test
1839 date: Thu Jan 01 00:00:00 1970 +0000
1839 date: Thu Jan 01 00:00:00 1970 +0000
1840 summary: add foo, related
1840 summary: add foo, related
1841
1841
1842 $ cd ..
1842 $ cd ..
1843
1843
1844 Issue2383: hg log showing _less_ differences than hg diff
1844 Issue2383: hg log showing _less_ differences than hg diff
1845
1845
1846 $ hg init issue2383
1846 $ hg init issue2383
1847 $ cd issue2383
1847 $ cd issue2383
1848
1848
1849 Create a test repo:
1849 Create a test repo:
1850
1850
1851 $ echo a > a
1851 $ echo a > a
1852 $ hg ci -Am0
1852 $ hg ci -Am0
1853 adding a
1853 adding a
1854 $ echo b > b
1854 $ echo b > b
1855 $ hg ci -Am1
1855 $ hg ci -Am1
1856 adding b
1856 adding b
1857 $ hg co 0
1857 $ hg co 0
1858 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1858 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1859 $ echo b > a
1859 $ echo b > a
1860 $ hg ci -m2
1860 $ hg ci -m2
1861 created new head
1861 created new head
1862
1862
1863 Merge:
1863 Merge:
1864
1864
1865 $ hg merge
1865 $ hg merge
1866 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1866 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1867 (branch merge, don't forget to commit)
1867 (branch merge, don't forget to commit)
1868
1868
1869 Make sure there's a file listed in the merge to trigger the bug:
1869 Make sure there's a file listed in the merge to trigger the bug:
1870
1870
1871 $ echo c > a
1871 $ echo c > a
1872 $ hg ci -m3
1872 $ hg ci -m3
1873
1873
1874 Two files shown here in diff:
1874 Two files shown here in diff:
1875
1875
1876 $ hg diff --rev 2:3
1876 $ hg diff --rev 2:3
1877 diff -r b09be438c43a -r 8e07aafe1edc a
1877 diff -r b09be438c43a -r 8e07aafe1edc a
1878 --- a/a Thu Jan 01 00:00:00 1970 +0000
1878 --- a/a Thu Jan 01 00:00:00 1970 +0000
1879 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1879 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1880 @@ -1,1 +1,1 @@
1880 @@ -1,1 +1,1 @@
1881 -b
1881 -b
1882 +c
1882 +c
1883 diff -r b09be438c43a -r 8e07aafe1edc b
1883 diff -r b09be438c43a -r 8e07aafe1edc b
1884 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1884 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1885 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1885 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1886 @@ -0,0 +1,1 @@
1886 @@ -0,0 +1,1 @@
1887 +b
1887 +b
1888
1888
1889 Diff here should be the same:
1889 Diff here should be the same:
1890
1890
1891 $ hg log -vpr 3
1891 $ hg log -vpr 3
1892 changeset: 3:8e07aafe1edc
1892 changeset: 3:8e07aafe1edc
1893 tag: tip
1893 tag: tip
1894 parent: 2:b09be438c43a
1894 parent: 2:b09be438c43a
1895 parent: 1:925d80f479bb
1895 parent: 1:925d80f479bb
1896 user: test
1896 user: test
1897 date: Thu Jan 01 00:00:00 1970 +0000
1897 date: Thu Jan 01 00:00:00 1970 +0000
1898 files: a
1898 files: a
1899 description:
1899 description:
1900 3
1900 3
1901
1901
1902
1902
1903 diff -r b09be438c43a -r 8e07aafe1edc a
1903 diff -r b09be438c43a -r 8e07aafe1edc a
1904 --- a/a Thu Jan 01 00:00:00 1970 +0000
1904 --- a/a Thu Jan 01 00:00:00 1970 +0000
1905 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1905 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1906 @@ -1,1 +1,1 @@
1906 @@ -1,1 +1,1 @@
1907 -b
1907 -b
1908 +c
1908 +c
1909 diff -r b09be438c43a -r 8e07aafe1edc b
1909 diff -r b09be438c43a -r 8e07aafe1edc b
1910 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1910 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1911 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1911 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1912 @@ -0,0 +1,1 @@
1912 @@ -0,0 +1,1 @@
1913 +b
1913 +b
1914
1914
1915 $ cd ..
1915 $ cd ..
1916
1916
1917 'hg log -r rev fn' when last(filelog(fn)) != rev
1917 'hg log -r rev fn' when last(filelog(fn)) != rev
1918
1918
1919 $ hg init simplelog
1919 $ hg init simplelog
1920 $ cd simplelog
1920 $ cd simplelog
1921 $ echo f > a
1921 $ echo f > a
1922 $ hg ci -Am'a' -d '0 0'
1922 $ hg ci -Am'a' -d '0 0'
1923 adding a
1923 adding a
1924 $ echo f >> a
1924 $ echo f >> a
1925 $ hg ci -Am'a bis' -d '1 0'
1925 $ hg ci -Am'a bis' -d '1 0'
1926
1926
1927 $ hg log -r0 a
1927 $ hg log -r0 a
1928 changeset: 0:9f758d63dcde
1928 changeset: 0:9f758d63dcde
1929 user: test
1929 user: test
1930 date: Thu Jan 01 00:00:00 1970 +0000
1930 date: Thu Jan 01 00:00:00 1970 +0000
1931 summary: a
1931 summary: a
1932
1932
1933 enable obsolete to test hidden feature
1933 enable obsolete to test hidden feature
1934
1934
1935 $ cat >> $HGRCPATH << EOF
1935 $ cat >> $HGRCPATH << EOF
1936 > [experimental]
1936 > [experimental]
1937 > evolution.createmarkers=True
1937 > evolution.createmarkers=True
1938 > EOF
1938 > EOF
1939
1939
1940 $ hg log --template='{rev}:{node}\n'
1940 $ hg log --template='{rev}:{node}\n'
1941 1:a765632148dc55d38c35c4f247c618701886cb2f
1941 1:a765632148dc55d38c35c4f247c618701886cb2f
1942 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1942 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1943 $ hg debugobsolete a765632148dc55d38c35c4f247c618701886cb2f
1943 $ hg debugobsolete a765632148dc55d38c35c4f247c618701886cb2f
1944 1 new obsolescence markers
1944 1 new obsolescence markers
1945 obsoleted 1 changesets
1945 obsoleted 1 changesets
1946 $ hg up null -q
1946 $ hg up null -q
1947 $ hg log --template='{rev}:{node}\n'
1947 $ hg log --template='{rev}:{node}\n'
1948 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1948 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1949 $ hg log --template='{rev}:{node}\n' --hidden
1949 $ hg log --template='{rev}:{node}\n' --hidden
1950 1:a765632148dc55d38c35c4f247c618701886cb2f
1950 1:a765632148dc55d38c35c4f247c618701886cb2f
1951 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1951 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1952 $ hg log -r a
1952 $ hg log -r a
1953 abort: hidden revision 'a' is pruned!
1953 abort: hidden revision 'a' is pruned!
1954 (use --hidden to access hidden revisions)
1954 (use --hidden to access hidden revisions)
1955 [255]
1955 [255]
1956
1956
1957 test that parent prevent a changeset to be hidden
1957 test that parent prevent a changeset to be hidden
1958
1958
1959 $ hg up 1 -q --hidden
1959 $ hg up 1 -q --hidden
1960 updated to hidden changeset a765632148dc
1960 updated to hidden changeset a765632148dc
1961 (hidden revision 'a765632148dc' is pruned)
1961 (hidden revision 'a765632148dc' is pruned)
1962 $ hg log --template='{rev}:{node}\n'
1962 $ hg log --template='{rev}:{node}\n'
1963 1:a765632148dc55d38c35c4f247c618701886cb2f
1963 1:a765632148dc55d38c35c4f247c618701886cb2f
1964 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1964 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1965
1965
1966 test that second parent prevent a changeset to be hidden too
1966 test that second parent prevent a changeset to be hidden too
1967
1967
1968 $ hg debugsetparents 0 1 # nothing suitable to merge here
1968 $ hg debugsetparents 0 1 # nothing suitable to merge here
1969 $ hg log --template='{rev}:{node}\n'
1969 $ hg log --template='{rev}:{node}\n'
1970 1:a765632148dc55d38c35c4f247c618701886cb2f
1970 1:a765632148dc55d38c35c4f247c618701886cb2f
1971 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1971 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1972 $ hg debugsetparents 1
1972 $ hg debugsetparents 1
1973 $ hg up -q null
1973 $ hg up -q null
1974
1974
1975 bookmarks prevent a changeset being hidden
1975 bookmarks prevent a changeset being hidden
1976
1976
1977 $ hg bookmark --hidden -r 1 X
1977 $ hg bookmark --hidden -r 1 X
1978 bookmarking hidden changeset a765632148dc
1978 bookmarking hidden changeset a765632148dc
1979 (hidden revision 'a765632148dc' is pruned)
1979 (hidden revision 'a765632148dc' is pruned)
1980 $ hg log --template '{rev}:{node}\n'
1980 $ hg log --template '{rev}:{node}\n'
1981 1:a765632148dc55d38c35c4f247c618701886cb2f
1981 1:a765632148dc55d38c35c4f247c618701886cb2f
1982 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1982 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1983 $ hg bookmark -d X
1983 $ hg bookmark -d X
1984
1984
1985 divergent bookmarks are not hidden
1985 divergent bookmarks are not hidden
1986
1986
1987 $ hg bookmark --hidden -r 1 X@foo
1987 $ hg bookmark --hidden -r 1 X@foo
1988 bookmarking hidden changeset a765632148dc
1988 bookmarking hidden changeset a765632148dc
1989 (hidden revision 'a765632148dc' is pruned)
1989 (hidden revision 'a765632148dc' is pruned)
1990 $ hg log --template '{rev}:{node}\n'
1990 $ hg log --template '{rev}:{node}\n'
1991 1:a765632148dc55d38c35c4f247c618701886cb2f
1991 1:a765632148dc55d38c35c4f247c618701886cb2f
1992 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1992 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1993
1993
1994 test hidden revision 0 (issue5385)
1994 test hidden revision 0 (issue5385)
1995
1995
1996 $ hg bookmark -d X@foo
1996 $ hg bookmark -d X@foo
1997 $ hg up null -q
1997 $ hg up null -q
1998 $ hg debugobsolete 9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1998 $ hg debugobsolete 9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1999 1 new obsolescence markers
1999 1 new obsolescence markers
2000 obsoleted 1 changesets
2000 obsoleted 1 changesets
2001 $ echo f > b
2001 $ echo f > b
2002 $ hg ci -Am'b' -d '2 0'
2002 $ hg ci -Am'b' -d '2 0'
2003 adding b
2003 adding b
2004 $ echo f >> b
2004 $ echo f >> b
2005 $ hg ci -m'b bis' -d '3 0'
2005 $ hg ci -m'b bis' -d '3 0'
2006 $ hg log -T'{rev}:{node}\n'
2006 $ hg log -T'{rev}:{node}\n'
2007 3:d7d28b288a6b83d5d2cf49f10c5974deed3a1d2e
2007 3:d7d28b288a6b83d5d2cf49f10c5974deed3a1d2e
2008 2:94375ec45bddd2a824535fc04855bd058c926ec0
2008 2:94375ec45bddd2a824535fc04855bd058c926ec0
2009
2009
2010 $ hg log -T'{rev}:{node}\n' -r:
2010 $ hg log -T'{rev}:{node}\n' -r:
2011 2:94375ec45bddd2a824535fc04855bd058c926ec0
2011 2:94375ec45bddd2a824535fc04855bd058c926ec0
2012 3:d7d28b288a6b83d5d2cf49f10c5974deed3a1d2e
2012 3:d7d28b288a6b83d5d2cf49f10c5974deed3a1d2e
2013 $ hg log -T'{rev}:{node}\n' -r:tip
2013 $ hg log -T'{rev}:{node}\n' -r:tip
2014 2:94375ec45bddd2a824535fc04855bd058c926ec0
2014 2:94375ec45bddd2a824535fc04855bd058c926ec0
2015 3:d7d28b288a6b83d5d2cf49f10c5974deed3a1d2e
2015 3:d7d28b288a6b83d5d2cf49f10c5974deed3a1d2e
2016 $ hg log -T'{rev}:{node}\n' -r:0
2016 $ hg log -T'{rev}:{node}\n' -r:0
2017 abort: hidden revision '0' is pruned!
2017 abort: hidden revision '0' is pruned!
2018 (use --hidden to access hidden revisions)
2018 (use --hidden to access hidden revisions)
2019 [255]
2019 [255]
2020 $ hg log -T'{rev}:{node}\n' -f
2020 $ hg log -T'{rev}:{node}\n' -f
2021 3:d7d28b288a6b83d5d2cf49f10c5974deed3a1d2e
2021 3:d7d28b288a6b83d5d2cf49f10c5974deed3a1d2e
2022 2:94375ec45bddd2a824535fc04855bd058c926ec0
2022 2:94375ec45bddd2a824535fc04855bd058c926ec0
2023
2023
2024 clear extensions configuration
2024 clear extensions configuration
2025 $ echo '[extensions]' >> $HGRCPATH
2025 $ echo '[extensions]' >> $HGRCPATH
2026 $ echo "obs=!" >> $HGRCPATH
2026 $ echo "obs=!" >> $HGRCPATH
2027 $ cd ..
2027 $ cd ..
2028
2028
2029 test -u/-k for problematic encoding
2029 test -u/-k for problematic encoding
2030 # unicode: cp932:
2030 # unicode: cp932:
2031 # u30A2 0x83 0x41(= 'A')
2031 # u30A2 0x83 0x41(= 'A')
2032 # u30C2 0x83 0x61(= 'a')
2032 # u30C2 0x83 0x61(= 'a')
2033
2033
2034 $ hg init problematicencoding
2034 $ hg init problematicencoding
2035 $ cd problematicencoding
2035 $ cd problematicencoding
2036
2036
2037 >>> with open('setup.sh', 'wb') as f:
2037 >>> with open('setup.sh', 'wb') as f:
2038 ... f.write(u'''
2038 ... f.write(u'''
2039 ... echo a > text
2039 ... echo a > text
2040 ... hg add text
2040 ... hg add text
2041 ... hg --encoding utf-8 commit -u '\u30A2' -m none
2041 ... hg --encoding utf-8 commit -u '\u30A2' -m none
2042 ... echo b > text
2042 ... echo b > text
2043 ... hg --encoding utf-8 commit -u '\u30C2' -m none
2043 ... hg --encoding utf-8 commit -u '\u30C2' -m none
2044 ... echo c > text
2044 ... echo c > text
2045 ... hg --encoding utf-8 commit -u none -m '\u30A2'
2045 ... hg --encoding utf-8 commit -u none -m '\u30A2'
2046 ... echo d > text
2046 ... echo d > text
2047 ... hg --encoding utf-8 commit -u none -m '\u30C2'
2047 ... hg --encoding utf-8 commit -u none -m '\u30C2'
2048 ... '''.encode('utf-8')) and None
2048 ... '''.encode('utf-8')) and None
2049 $ sh < setup.sh
2049 $ sh < setup.sh
2050
2050
2051 test in problematic encoding
2051 test in problematic encoding
2052 >>> with open('test.sh', 'wb') as f:
2052 >>> with open('test.sh', 'wb') as f:
2053 ... f.write(u'''
2053 ... f.write(u'''
2054 ... hg --encoding cp932 log --template '{rev}\\n' -u '\u30A2'
2054 ... hg --encoding cp932 log --template '{rev}\\n' -u '\u30A2'
2055 ... echo ====
2055 ... echo ====
2056 ... hg --encoding cp932 log --template '{rev}\\n' -u '\u30C2'
2056 ... hg --encoding cp932 log --template '{rev}\\n' -u '\u30C2'
2057 ... echo ====
2057 ... echo ====
2058 ... hg --encoding cp932 log --template '{rev}\\n' -k '\u30A2'
2058 ... hg --encoding cp932 log --template '{rev}\\n' -k '\u30A2'
2059 ... echo ====
2059 ... echo ====
2060 ... hg --encoding cp932 log --template '{rev}\\n' -k '\u30C2'
2060 ... hg --encoding cp932 log --template '{rev}\\n' -k '\u30C2'
2061 ... '''.encode('cp932')) and None
2061 ... '''.encode('cp932')) and None
2062 $ sh < test.sh
2062 $ sh < test.sh
2063 0
2063 0
2064 ====
2064 ====
2065 1
2065 1
2066 ====
2066 ====
2067 2
2067 2
2068 0
2068 0
2069 ====
2069 ====
2070 3
2070 3
2071 1
2071 1
2072
2072
2073 $ cd ..
2073 $ cd ..
2074
2074
2075 test hg log on non-existent files and on directories
2075 test hg log on non-existent files and on directories
2076 $ hg init issue1340
2076 $ hg init issue1340
2077 $ cd issue1340
2077 $ cd issue1340
2078 $ mkdir d1; mkdir D2; mkdir D3.i; mkdir d4.hg; mkdir d5.d; mkdir .d6
2078 $ mkdir d1; mkdir D2; mkdir D3.i; mkdir d4.hg; mkdir d5.d; mkdir .d6
2079 $ echo 1 > d1/f1
2079 $ echo 1 > d1/f1
2080 $ echo 1 > D2/f1
2080 $ echo 1 > D2/f1
2081 $ echo 1 > D3.i/f1
2081 $ echo 1 > D3.i/f1
2082 $ echo 1 > d4.hg/f1
2082 $ echo 1 > d4.hg/f1
2083 $ echo 1 > d5.d/f1
2083 $ echo 1 > d5.d/f1
2084 $ echo 1 > .d6/f1
2084 $ echo 1 > .d6/f1
2085 $ hg -q add .
2085 $ hg -q add .
2086 $ hg commit -m "a bunch of weird directories"
2086 $ hg commit -m "a bunch of weird directories"
2087 $ hg log -l1 d1/f1 | grep changeset
2087 $ hg log -l1 d1/f1 | grep changeset
2088 changeset: 0:65624cd9070a
2088 changeset: 0:65624cd9070a
2089 $ hg log -l1 f1
2089 $ hg log -l1 f1
2090 $ hg log -l1 . | grep changeset
2090 $ hg log -l1 . | grep changeset
2091 changeset: 0:65624cd9070a
2091 changeset: 0:65624cd9070a
2092 $ hg log -l1 ./ | grep changeset
2092 $ hg log -l1 ./ | grep changeset
2093 changeset: 0:65624cd9070a
2093 changeset: 0:65624cd9070a
2094 $ hg log -l1 d1 | grep changeset
2094 $ hg log -l1 d1 | grep changeset
2095 changeset: 0:65624cd9070a
2095 changeset: 0:65624cd9070a
2096 $ hg log -l1 D2 | grep changeset
2096 $ hg log -l1 D2 | grep changeset
2097 changeset: 0:65624cd9070a
2097 changeset: 0:65624cd9070a
2098 $ hg log -l1 D2/f1 | grep changeset
2098 $ hg log -l1 D2/f1 | grep changeset
2099 changeset: 0:65624cd9070a
2099 changeset: 0:65624cd9070a
2100 $ hg log -l1 D3.i | grep changeset
2100 $ hg log -l1 D3.i | grep changeset
2101 changeset: 0:65624cd9070a
2101 changeset: 0:65624cd9070a
2102 $ hg log -l1 D3.i/f1 | grep changeset
2102 $ hg log -l1 D3.i/f1 | grep changeset
2103 changeset: 0:65624cd9070a
2103 changeset: 0:65624cd9070a
2104 $ hg log -l1 d4.hg | grep changeset
2104 $ hg log -l1 d4.hg | grep changeset
2105 changeset: 0:65624cd9070a
2105 changeset: 0:65624cd9070a
2106 $ hg log -l1 d4.hg/f1 | grep changeset
2106 $ hg log -l1 d4.hg/f1 | grep changeset
2107 changeset: 0:65624cd9070a
2107 changeset: 0:65624cd9070a
2108 $ hg log -l1 d5.d | grep changeset
2108 $ hg log -l1 d5.d | grep changeset
2109 changeset: 0:65624cd9070a
2109 changeset: 0:65624cd9070a
2110 $ hg log -l1 d5.d/f1 | grep changeset
2110 $ hg log -l1 d5.d/f1 | grep changeset
2111 changeset: 0:65624cd9070a
2111 changeset: 0:65624cd9070a
2112 $ hg log -l1 .d6 | grep changeset
2112 $ hg log -l1 .d6 | grep changeset
2113 changeset: 0:65624cd9070a
2113 changeset: 0:65624cd9070a
2114 $ hg log -l1 .d6/f1 | grep changeset
2114 $ hg log -l1 .d6/f1 | grep changeset
2115 changeset: 0:65624cd9070a
2115 changeset: 0:65624cd9070a
2116
2116
2117 issue3772: hg log -r :null showing revision 0 as well
2117 issue3772: hg log -r :null showing revision 0 as well
2118
2118
2119 $ hg log -r :null
2119 $ hg log -r :null
2120 changeset: 0:65624cd9070a
2120 changeset: 0:65624cd9070a
2121 tag: tip
2121 tag: tip
2122 user: test
2122 user: test
2123 date: Thu Jan 01 00:00:00 1970 +0000
2123 date: Thu Jan 01 00:00:00 1970 +0000
2124 summary: a bunch of weird directories
2124 summary: a bunch of weird directories
2125
2125
2126 changeset: -1:000000000000
2126 changeset: -1:000000000000
2127 user:
2127 user:
2128 date: Thu Jan 01 00:00:00 1970 +0000
2128 date: Thu Jan 01 00:00:00 1970 +0000
2129
2129
2130 $ hg log -r null:null
2130 $ hg log -r null:null
2131 changeset: -1:000000000000
2131 changeset: -1:000000000000
2132 user:
2132 user:
2133 date: Thu Jan 01 00:00:00 1970 +0000
2133 date: Thu Jan 01 00:00:00 1970 +0000
2134
2134
2135 working-directory revision requires special treatment
2135 working-directory revision requires special treatment
2136
2136
2137 clean:
2137 clean:
2138
2138
2139 $ hg log -r 'wdir()' --debug
2139 $ hg log -r 'wdir()' --debug
2140 changeset: 2147483647:ffffffffffffffffffffffffffffffffffffffff
2140 changeset: 2147483647:ffffffffffffffffffffffffffffffffffffffff
2141 phase: draft
2141 phase: draft
2142 parent: 0:65624cd9070a035fa7191a54f2b8af39f16b0c08
2142 parent: 0:65624cd9070a035fa7191a54f2b8af39f16b0c08
2143 parent: -1:0000000000000000000000000000000000000000
2143 parent: -1:0000000000000000000000000000000000000000
2144 manifest: 2147483647:ffffffffffffffffffffffffffffffffffffffff
2144 manifest: 2147483647:ffffffffffffffffffffffffffffffffffffffff
2145 user: test
2145 user: test
2146 date: [A-Za-z0-9:+ ]+ (re)
2146 date: [A-Za-z0-9:+ ]+ (re)
2147 extra: branch=default
2147 extra: branch=default
2148
2148
2149 $ hg log -r 'wdir()' -p --stat
2149 $ hg log -r 'wdir()' -p --stat
2150 changeset: 2147483647:ffffffffffff
2150 changeset: 2147483647:ffffffffffff
2151 parent: 0:65624cd9070a
2151 parent: 0:65624cd9070a
2152 user: test
2152 user: test
2153 date: [A-Za-z0-9:+ ]+ (re)
2153 date: [A-Za-z0-9:+ ]+ (re)
2154
2154
2155
2155
2156
2156
2157
2157
2158 dirty:
2158 dirty:
2159
2159
2160 $ echo 2 >> d1/f1
2160 $ echo 2 >> d1/f1
2161 $ echo 2 > d1/f2
2161 $ echo 2 > d1/f2
2162 $ hg add d1/f2
2162 $ hg add d1/f2
2163 $ hg remove .d6/f1
2163 $ hg remove .d6/f1
2164 $ hg status
2164 $ hg status
2165 M d1/f1
2165 M d1/f1
2166 A d1/f2
2166 A d1/f2
2167 R .d6/f1
2167 R .d6/f1
2168
2168
2169 $ hg log -r 'wdir()'
2169 $ hg log -r 'wdir()'
2170 changeset: 2147483647:ffffffffffff
2170 changeset: 2147483647:ffffffffffff
2171 parent: 0:65624cd9070a
2171 parent: 0:65624cd9070a
2172 user: test
2172 user: test
2173 date: [A-Za-z0-9:+ ]+ (re)
2173 date: [A-Za-z0-9:+ ]+ (re)
2174
2174
2175 $ hg log -r 'wdir()' -q
2175 $ hg log -r 'wdir()' -q
2176 2147483647:ffffffffffff
2176 2147483647:ffffffffffff
2177
2177
2178 $ hg log -r 'wdir()' --debug
2178 $ hg log -r 'wdir()' --debug
2179 changeset: 2147483647:ffffffffffffffffffffffffffffffffffffffff
2179 changeset: 2147483647:ffffffffffffffffffffffffffffffffffffffff
2180 phase: draft
2180 phase: draft
2181 parent: 0:65624cd9070a035fa7191a54f2b8af39f16b0c08
2181 parent: 0:65624cd9070a035fa7191a54f2b8af39f16b0c08
2182 parent: -1:0000000000000000000000000000000000000000
2182 parent: -1:0000000000000000000000000000000000000000
2183 manifest: 2147483647:ffffffffffffffffffffffffffffffffffffffff
2183 manifest: 2147483647:ffffffffffffffffffffffffffffffffffffffff
2184 user: test
2184 user: test
2185 date: [A-Za-z0-9:+ ]+ (re)
2185 date: [A-Za-z0-9:+ ]+ (re)
2186 files: d1/f1
2186 files: d1/f1
2187 files+: d1/f2
2187 files+: d1/f2
2188 files-: .d6/f1
2188 files-: .d6/f1
2189 extra: branch=default
2189 extra: branch=default
2190
2190
2191 $ hg log -r 'wdir()' -p --stat --git
2191 $ hg log -r 'wdir()' -p --stat --git
2192 changeset: 2147483647:ffffffffffff
2192 changeset: 2147483647:ffffffffffff
2193 parent: 0:65624cd9070a
2193 parent: 0:65624cd9070a
2194 user: test
2194 user: test
2195 date: [A-Za-z0-9:+ ]+ (re)
2195 date: [A-Za-z0-9:+ ]+ (re)
2196
2196
2197 .d6/f1 | 1 -
2197 .d6/f1 | 1 -
2198 d1/f1 | 1 +
2198 d1/f1 | 1 +
2199 d1/f2 | 1 +
2199 d1/f2 | 1 +
2200 3 files changed, 2 insertions(+), 1 deletions(-)
2200 3 files changed, 2 insertions(+), 1 deletions(-)
2201
2201
2202 diff --git a/.d6/f1 b/.d6/f1
2202 diff --git a/.d6/f1 b/.d6/f1
2203 deleted file mode 100644
2203 deleted file mode 100644
2204 --- a/.d6/f1
2204 --- a/.d6/f1
2205 +++ /dev/null
2205 +++ /dev/null
2206 @@ -1,1 +0,0 @@
2206 @@ -1,1 +0,0 @@
2207 -1
2207 -1
2208 diff --git a/d1/f1 b/d1/f1
2208 diff --git a/d1/f1 b/d1/f1
2209 --- a/d1/f1
2209 --- a/d1/f1
2210 +++ b/d1/f1
2210 +++ b/d1/f1
2211 @@ -1,1 +1,2 @@
2211 @@ -1,1 +1,2 @@
2212 1
2212 1
2213 +2
2213 +2
2214 diff --git a/d1/f2 b/d1/f2
2214 diff --git a/d1/f2 b/d1/f2
2215 new file mode 100644
2215 new file mode 100644
2216 --- /dev/null
2216 --- /dev/null
2217 +++ b/d1/f2
2217 +++ b/d1/f2
2218 @@ -0,0 +1,1 @@
2218 @@ -0,0 +1,1 @@
2219 +2
2219 +2
2220
2220
2221 $ hg log -r 'wdir()' -Tjson
2221 $ hg log -r 'wdir()' -Tjson
2222 [
2222 [
2223 {
2223 {
2224 "bookmarks": [],
2224 "bookmarks": [],
2225 "branch": "default",
2225 "branch": "default",
2226 "date": [*, 0], (glob)
2226 "date": [*, 0], (glob)
2227 "desc": "",
2227 "desc": "",
2228 "node": "ffffffffffffffffffffffffffffffffffffffff",
2228 "node": "ffffffffffffffffffffffffffffffffffffffff",
2229 "parents": ["65624cd9070a035fa7191a54f2b8af39f16b0c08"],
2229 "parents": ["65624cd9070a035fa7191a54f2b8af39f16b0c08"],
2230 "phase": "draft",
2230 "phase": "draft",
2231 "rev": 2147483647,
2231 "rev": 2147483647,
2232 "tags": [],
2232 "tags": [],
2233 "user": "test"
2233 "user": "test"
2234 }
2234 }
2235 ]
2235 ]
2236
2236
2237 $ hg log -r 'wdir()' -Tjson -q
2237 $ hg log -r 'wdir()' -Tjson -q
2238 [
2238 [
2239 {
2239 {
2240 "node": "ffffffffffffffffffffffffffffffffffffffff",
2240 "node": "ffffffffffffffffffffffffffffffffffffffff",
2241 "rev": 2147483647
2241 "rev": 2147483647
2242 }
2242 }
2243 ]
2243 ]
2244
2244
2245 $ hg log -r 'wdir()' -Tjson --debug
2245 $ hg log -r 'wdir()' -Tjson --debug
2246 [
2246 [
2247 {
2247 {
2248 "added": ["d1/f2"],
2248 "added": ["d1/f2"],
2249 "bookmarks": [],
2249 "bookmarks": [],
2250 "branch": "default",
2250 "branch": "default",
2251 "date": [*, 0], (glob)
2251 "date": [*, 0], (glob)
2252 "desc": "",
2252 "desc": "",
2253 "extra": {"branch": "default"},
2253 "extra": {"branch": "default"},
2254 "manifest": "ffffffffffffffffffffffffffffffffffffffff",
2254 "manifest": "ffffffffffffffffffffffffffffffffffffffff",
2255 "modified": ["d1/f1"],
2255 "modified": ["d1/f1"],
2256 "node": "ffffffffffffffffffffffffffffffffffffffff",
2256 "node": "ffffffffffffffffffffffffffffffffffffffff",
2257 "parents": ["65624cd9070a035fa7191a54f2b8af39f16b0c08"],
2257 "parents": ["65624cd9070a035fa7191a54f2b8af39f16b0c08"],
2258 "phase": "draft",
2258 "phase": "draft",
2259 "removed": [".d6/f1"],
2259 "removed": [".d6/f1"],
2260 "rev": 2147483647,
2260 "rev": 2147483647,
2261 "tags": [],
2261 "tags": [],
2262 "user": "test"
2262 "user": "test"
2263 }
2263 }
2264 ]
2264 ]
2265
2265
2266 $ hg revert -aqC
2266 $ hg revert -aqC
2267
2267
2268 Check that adding an arbitrary name shows up in log automatically
2268 Check that adding an arbitrary name shows up in log automatically
2269
2269
2270 $ cat > ../names.py <<EOF
2270 $ cat > ../names.py <<EOF
2271 > """A small extension to test adding arbitrary names to a repo"""
2271 > """A small extension to test adding arbitrary names to a repo"""
2272 > from __future__ import absolute_import
2272 > from __future__ import absolute_import
2273 > from mercurial import namespaces
2273 > from mercurial import namespaces
2274 >
2274 >
2275 > def reposetup(ui, repo):
2275 > def reposetup(ui, repo):
2276 > if not repo.local():
2276 > if not repo.local():
2277 > return
2277 > return
2278 > foo = {b'foo': repo[0].node()}
2278 > foo = {b'foo': repo[0].node()}
2279 > names = lambda r: foo.keys()
2279 > names = lambda r: foo.keys()
2280 > namemap = lambda r, name: foo.get(name)
2280 > namemap = lambda r, name: foo.get(name)
2281 > nodemap = lambda r, node: [name for name, n in foo.items()
2281 > nodemap = lambda r, node: [name for name, n in foo.items()
2282 > if n == node]
2282 > if n == node]
2283 > ns = namespaces.namespace(
2283 > ns = namespaces.namespace(
2284 > b"bars", templatename=b"bar", logname=b"barlog",
2284 > b"bars", templatename=b"bar", logname=b"barlog",
2285 > colorname=b"barcolor", listnames=names, namemap=namemap,
2285 > colorname=b"barcolor", listnames=names, namemap=namemap,
2286 > nodemap=nodemap)
2286 > nodemap=nodemap)
2287 >
2287 >
2288 > repo.names.addnamespace(ns)
2288 > repo.names.addnamespace(ns)
2289 > EOF
2289 > EOF
2290
2290
2291 $ hg --config extensions.names=../names.py log -r 0
2291 $ hg --config extensions.names=../names.py log -r 0
2292 changeset: 0:65624cd9070a
2292 changeset: 0:65624cd9070a
2293 tag: tip
2293 tag: tip
2294 barlog: foo
2294 barlog: foo
2295 user: test
2295 user: test
2296 date: Thu Jan 01 00:00:00 1970 +0000
2296 date: Thu Jan 01 00:00:00 1970 +0000
2297 summary: a bunch of weird directories
2297 summary: a bunch of weird directories
2298
2298
2299 $ hg --config extensions.names=../names.py \
2299 $ hg --config extensions.names=../names.py \
2300 > --config extensions.color= --config color.log.barcolor=red \
2300 > --config extensions.color= --config color.log.barcolor=red \
2301 > --color=always log -r 0
2301 > --color=always log -r 0
2302 \x1b[0;33mchangeset: 0:65624cd9070a\x1b[0m (esc)
2302 \x1b[0;33mchangeset: 0:65624cd9070a\x1b[0m (esc)
2303 tag: tip
2303 tag: tip
2304 \x1b[0;31mbarlog: foo\x1b[0m (esc)
2304 \x1b[0;31mbarlog: foo\x1b[0m (esc)
2305 user: test
2305 user: test
2306 date: Thu Jan 01 00:00:00 1970 +0000
2306 date: Thu Jan 01 00:00:00 1970 +0000
2307 summary: a bunch of weird directories
2307 summary: a bunch of weird directories
2308
2308
2309 $ hg --config extensions.names=../names.py log -r 0 --template '{bars}\n'
2309 $ hg --config extensions.names=../names.py log -r 0 --template '{bars}\n'
2310 foo
2310 foo
2311
2311
2312 Templater parse errors:
2312 Templater parse errors:
2313
2313
2314 simple error
2314 simple error
2315 $ hg log -r . -T '{shortest(node}'
2315 $ hg log -r . -T '{shortest(node}'
2316 hg: parse error at 14: unexpected token: end
2316 hg: parse error at 14: unexpected token: end
2317 ({shortest(node}
2317 ({shortest(node}
2318 ^ here)
2318 ^ here)
2319 [255]
2319 [255]
2320
2320
2321 multi-line template with error
2321 multi-line template with error
2322 $ hg log -r . -T 'line 1
2322 $ hg log -r . -T 'line 1
2323 > line2
2323 > line2
2324 > {shortest(node}
2324 > {shortest(node}
2325 > line4\nline5'
2325 > line4\nline5'
2326 hg: parse error at 27: unexpected token: end
2326 hg: parse error at 27: unexpected token: end
2327 (line 1\nline2\n{shortest(node}\nline4\nline5
2327 (line 1\nline2\n{shortest(node}\nline4\nline5
2328 ^ here)
2328 ^ here)
2329 [255]
2329 [255]
2330
2330
2331 $ cd ..
2331 $ cd ..
2332
2332
2333 New namespace is registered per repo instance, but the template keyword
2333 New namespace is registered per repo instance, but the template keyword
2334 is global. So we shouldn't expect the namespace always exists. Using
2334 is global. So we shouldn't expect the namespace always exists. Using
2335 ssh:// makes sure a bundle repository is created from scratch. (issue6301)
2335 ssh:// makes sure a bundle repository is created from scratch. (issue6301)
2336
2336
2337 $ hg clone -e "'$PYTHON' '$TESTDIR/dummyssh'" \
2337 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" \
2338 > -qr0 "ssh://user@dummy/`pwd`/a" a-clone
2338 > -qr0 "ssh://user@dummy/`pwd`/a" a-clone
2339 $ hg incoming --config extensions.names=names.py -R a-clone \
2339 $ hg incoming --config extensions.names=names.py -R a-clone \
2340 > -e "'$PYTHON' '$TESTDIR/dummyssh'" -T '{bars}\n' -l1
2340 > -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" -T '{bars}\n' -l1
2341 comparing with ssh://user@dummy/$TESTTMP/a
2341 comparing with ssh://user@dummy/$TESTTMP/a
2342 searching for changes
2342 searching for changes
2343
2343
2344
2344
2345 hg log -f dir across branches
2345 hg log -f dir across branches
2346
2346
2347 $ hg init acrossbranches
2347 $ hg init acrossbranches
2348 $ cd acrossbranches
2348 $ cd acrossbranches
2349 $ mkdir d
2349 $ mkdir d
2350 $ echo a > d/a && hg ci -Aqm a
2350 $ echo a > d/a && hg ci -Aqm a
2351 $ echo b > d/a && hg ci -Aqm b
2351 $ echo b > d/a && hg ci -Aqm b
2352 $ hg up -q 0
2352 $ hg up -q 0
2353 $ echo b > d/a && hg ci -Aqm c
2353 $ echo b > d/a && hg ci -Aqm c
2354 $ hg log -f d -T '{desc}' -G
2354 $ hg log -f d -T '{desc}' -G
2355 @ c
2355 @ c
2356 |
2356 |
2357 o a
2357 o a
2358
2358
2359 Ensure that largefiles doesn't interfere with following a normal file
2359 Ensure that largefiles doesn't interfere with following a normal file
2360 $ hg --config extensions.largefiles= log -f d -T '{desc}' -G
2360 $ hg --config extensions.largefiles= log -f d -T '{desc}' -G
2361 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
2361 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
2362 @ c
2362 @ c
2363 |
2363 |
2364 o a
2364 o a
2365
2365
2366 $ hg log -f d/a -T '{desc}' -G
2366 $ hg log -f d/a -T '{desc}' -G
2367 @ c
2367 @ c
2368 |
2368 |
2369 o a
2369 o a
2370
2370
2371 $ cd ..
2371 $ cd ..
2372
2372
2373 hg log -f with linkrev pointing to another branch
2373 hg log -f with linkrev pointing to another branch
2374 -------------------------------------------------
2374 -------------------------------------------------
2375
2375
2376 create history with a filerev whose linkrev points to another branch
2376 create history with a filerev whose linkrev points to another branch
2377
2377
2378 $ hg init branchedlinkrev
2378 $ hg init branchedlinkrev
2379 $ cd branchedlinkrev
2379 $ cd branchedlinkrev
2380 $ echo 1 > a
2380 $ echo 1 > a
2381 $ hg commit -Am 'content1'
2381 $ hg commit -Am 'content1'
2382 adding a
2382 adding a
2383 $ echo 2 > a
2383 $ echo 2 > a
2384 $ hg commit -m 'content2'
2384 $ hg commit -m 'content2'
2385 $ hg up --rev 'desc(content1)'
2385 $ hg up --rev 'desc(content1)'
2386 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2386 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2387 $ echo unrelated > unrelated
2387 $ echo unrelated > unrelated
2388 $ hg commit -Am 'unrelated'
2388 $ hg commit -Am 'unrelated'
2389 adding unrelated
2389 adding unrelated
2390 created new head
2390 created new head
2391 $ hg graft -r 'desc(content2)'
2391 $ hg graft -r 'desc(content2)'
2392 grafting 1:2294ae80ad84 "content2"
2392 grafting 1:2294ae80ad84 "content2"
2393 $ echo 3 > a
2393 $ echo 3 > a
2394 $ hg commit -m 'content3'
2394 $ hg commit -m 'content3'
2395 $ hg log -G
2395 $ hg log -G
2396 @ changeset: 4:50b9b36e9c5d
2396 @ changeset: 4:50b9b36e9c5d
2397 | tag: tip
2397 | tag: tip
2398 | user: test
2398 | user: test
2399 | date: Thu Jan 01 00:00:00 1970 +0000
2399 | date: Thu Jan 01 00:00:00 1970 +0000
2400 | summary: content3
2400 | summary: content3
2401 |
2401 |
2402 o changeset: 3:15b2327059e5
2402 o changeset: 3:15b2327059e5
2403 | user: test
2403 | user: test
2404 | date: Thu Jan 01 00:00:00 1970 +0000
2404 | date: Thu Jan 01 00:00:00 1970 +0000
2405 | summary: content2
2405 | summary: content2
2406 |
2406 |
2407 o changeset: 2:2029acd1168c
2407 o changeset: 2:2029acd1168c
2408 | parent: 0:ae0a3c9f9e95
2408 | parent: 0:ae0a3c9f9e95
2409 | user: test
2409 | user: test
2410 | date: Thu Jan 01 00:00:00 1970 +0000
2410 | date: Thu Jan 01 00:00:00 1970 +0000
2411 | summary: unrelated
2411 | summary: unrelated
2412 |
2412 |
2413 | o changeset: 1:2294ae80ad84
2413 | o changeset: 1:2294ae80ad84
2414 |/ user: test
2414 |/ user: test
2415 | date: Thu Jan 01 00:00:00 1970 +0000
2415 | date: Thu Jan 01 00:00:00 1970 +0000
2416 | summary: content2
2416 | summary: content2
2417 |
2417 |
2418 o changeset: 0:ae0a3c9f9e95
2418 o changeset: 0:ae0a3c9f9e95
2419 user: test
2419 user: test
2420 date: Thu Jan 01 00:00:00 1970 +0000
2420 date: Thu Jan 01 00:00:00 1970 +0000
2421 summary: content1
2421 summary: content1
2422
2422
2423
2423
2424 log -f on the file should list the graft result.
2424 log -f on the file should list the graft result.
2425
2425
2426 $ hg log -Gf a
2426 $ hg log -Gf a
2427 @ changeset: 4:50b9b36e9c5d
2427 @ changeset: 4:50b9b36e9c5d
2428 | tag: tip
2428 | tag: tip
2429 | user: test
2429 | user: test
2430 | date: Thu Jan 01 00:00:00 1970 +0000
2430 | date: Thu Jan 01 00:00:00 1970 +0000
2431 | summary: content3
2431 | summary: content3
2432 |
2432 |
2433 o changeset: 3:15b2327059e5
2433 o changeset: 3:15b2327059e5
2434 : user: test
2434 : user: test
2435 : date: Thu Jan 01 00:00:00 1970 +0000
2435 : date: Thu Jan 01 00:00:00 1970 +0000
2436 : summary: content2
2436 : summary: content2
2437 :
2437 :
2438 o changeset: 0:ae0a3c9f9e95
2438 o changeset: 0:ae0a3c9f9e95
2439 user: test
2439 user: test
2440 date: Thu Jan 01 00:00:00 1970 +0000
2440 date: Thu Jan 01 00:00:00 1970 +0000
2441 summary: content1
2441 summary: content1
2442
2442
2443
2443
2444 plain log lists the original version
2444 plain log lists the original version
2445 (XXX we should probably list both)
2445 (XXX we should probably list both)
2446
2446
2447 $ hg log -G a
2447 $ hg log -G a
2448 @ changeset: 4:50b9b36e9c5d
2448 @ changeset: 4:50b9b36e9c5d
2449 : tag: tip
2449 : tag: tip
2450 : user: test
2450 : user: test
2451 : date: Thu Jan 01 00:00:00 1970 +0000
2451 : date: Thu Jan 01 00:00:00 1970 +0000
2452 : summary: content3
2452 : summary: content3
2453 :
2453 :
2454 : o changeset: 1:2294ae80ad84
2454 : o changeset: 1:2294ae80ad84
2455 :/ user: test
2455 :/ user: test
2456 : date: Thu Jan 01 00:00:00 1970 +0000
2456 : date: Thu Jan 01 00:00:00 1970 +0000
2457 : summary: content2
2457 : summary: content2
2458 :
2458 :
2459 o changeset: 0:ae0a3c9f9e95
2459 o changeset: 0:ae0a3c9f9e95
2460 user: test
2460 user: test
2461 date: Thu Jan 01 00:00:00 1970 +0000
2461 date: Thu Jan 01 00:00:00 1970 +0000
2462 summary: content1
2462 summary: content1
2463
2463
2464
2464
2465 hg log -f from the grafted changeset
2465 hg log -f from the grafted changeset
2466 (The bootstrap should properly take the topology in account)
2466 (The bootstrap should properly take the topology in account)
2467
2467
2468 $ hg up 'desc(content3)^'
2468 $ hg up 'desc(content3)^'
2469 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2469 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2470 $ hg log -Gf a
2470 $ hg log -Gf a
2471 @ changeset: 3:15b2327059e5
2471 @ changeset: 3:15b2327059e5
2472 : user: test
2472 : user: test
2473 : date: Thu Jan 01 00:00:00 1970 +0000
2473 : date: Thu Jan 01 00:00:00 1970 +0000
2474 : summary: content2
2474 : summary: content2
2475 :
2475 :
2476 o changeset: 0:ae0a3c9f9e95
2476 o changeset: 0:ae0a3c9f9e95
2477 user: test
2477 user: test
2478 date: Thu Jan 01 00:00:00 1970 +0000
2478 date: Thu Jan 01 00:00:00 1970 +0000
2479 summary: content1
2479 summary: content1
2480
2480
2481
2481
2482 Test that we use the first non-hidden changeset in that case.
2482 Test that we use the first non-hidden changeset in that case.
2483
2483
2484 (hide the changeset)
2484 (hide the changeset)
2485
2485
2486 $ hg log -T '{node}\n' -r 1
2486 $ hg log -T '{node}\n' -r 1
2487 2294ae80ad8447bc78383182eeac50cb049df623
2487 2294ae80ad8447bc78383182eeac50cb049df623
2488 $ hg debugobsolete 2294ae80ad8447bc78383182eeac50cb049df623
2488 $ hg debugobsolete 2294ae80ad8447bc78383182eeac50cb049df623
2489 1 new obsolescence markers
2489 1 new obsolescence markers
2490 obsoleted 1 changesets
2490 obsoleted 1 changesets
2491 $ hg log -G
2491 $ hg log -G
2492 o changeset: 4:50b9b36e9c5d
2492 o changeset: 4:50b9b36e9c5d
2493 | tag: tip
2493 | tag: tip
2494 | user: test
2494 | user: test
2495 | date: Thu Jan 01 00:00:00 1970 +0000
2495 | date: Thu Jan 01 00:00:00 1970 +0000
2496 | summary: content3
2496 | summary: content3
2497 |
2497 |
2498 @ changeset: 3:15b2327059e5
2498 @ changeset: 3:15b2327059e5
2499 | user: test
2499 | user: test
2500 | date: Thu Jan 01 00:00:00 1970 +0000
2500 | date: Thu Jan 01 00:00:00 1970 +0000
2501 | summary: content2
2501 | summary: content2
2502 |
2502 |
2503 o changeset: 2:2029acd1168c
2503 o changeset: 2:2029acd1168c
2504 | parent: 0:ae0a3c9f9e95
2504 | parent: 0:ae0a3c9f9e95
2505 | user: test
2505 | user: test
2506 | date: Thu Jan 01 00:00:00 1970 +0000
2506 | date: Thu Jan 01 00:00:00 1970 +0000
2507 | summary: unrelated
2507 | summary: unrelated
2508 |
2508 |
2509 o changeset: 0:ae0a3c9f9e95
2509 o changeset: 0:ae0a3c9f9e95
2510 user: test
2510 user: test
2511 date: Thu Jan 01 00:00:00 1970 +0000
2511 date: Thu Jan 01 00:00:00 1970 +0000
2512 summary: content1
2512 summary: content1
2513
2513
2514
2514
2515 Check that log on the file does not drop the file revision.
2515 Check that log on the file does not drop the file revision.
2516
2516
2517 $ hg log -G a
2517 $ hg log -G a
2518 o changeset: 4:50b9b36e9c5d
2518 o changeset: 4:50b9b36e9c5d
2519 | tag: tip
2519 | tag: tip
2520 | user: test
2520 | user: test
2521 | date: Thu Jan 01 00:00:00 1970 +0000
2521 | date: Thu Jan 01 00:00:00 1970 +0000
2522 | summary: content3
2522 | summary: content3
2523 |
2523 |
2524 @ changeset: 3:15b2327059e5
2524 @ changeset: 3:15b2327059e5
2525 : user: test
2525 : user: test
2526 : date: Thu Jan 01 00:00:00 1970 +0000
2526 : date: Thu Jan 01 00:00:00 1970 +0000
2527 : summary: content2
2527 : summary: content2
2528 :
2528 :
2529 o changeset: 0:ae0a3c9f9e95
2529 o changeset: 0:ae0a3c9f9e95
2530 user: test
2530 user: test
2531 date: Thu Jan 01 00:00:00 1970 +0000
2531 date: Thu Jan 01 00:00:00 1970 +0000
2532 summary: content1
2532 summary: content1
2533
2533
2534
2534
2535 Even when a head revision is linkrev-shadowed.
2535 Even when a head revision is linkrev-shadowed.
2536
2536
2537 $ hg log -T '{node}\n' -r 4
2537 $ hg log -T '{node}\n' -r 4
2538 50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
2538 50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
2539 $ hg debugobsolete 50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
2539 $ hg debugobsolete 50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
2540 1 new obsolescence markers
2540 1 new obsolescence markers
2541 obsoleted 1 changesets
2541 obsoleted 1 changesets
2542 $ hg log -G a
2542 $ hg log -G a
2543 @ changeset: 3:15b2327059e5
2543 @ changeset: 3:15b2327059e5
2544 : tag: tip
2544 : tag: tip
2545 : user: test
2545 : user: test
2546 : date: Thu Jan 01 00:00:00 1970 +0000
2546 : date: Thu Jan 01 00:00:00 1970 +0000
2547 : summary: content2
2547 : summary: content2
2548 :
2548 :
2549 o changeset: 0:ae0a3c9f9e95
2549 o changeset: 0:ae0a3c9f9e95
2550 user: test
2550 user: test
2551 date: Thu Jan 01 00:00:00 1970 +0000
2551 date: Thu Jan 01 00:00:00 1970 +0000
2552 summary: content1
2552 summary: content1
2553
2553
2554
2554
2555 $ cd ..
2555 $ cd ..
2556
2556
2557 Even when the file revision is missing from some head:
2557 Even when the file revision is missing from some head:
2558
2558
2559 $ hg init issue4490
2559 $ hg init issue4490
2560 $ cd issue4490
2560 $ cd issue4490
2561 $ echo '[experimental]' >> .hg/hgrc
2561 $ echo '[experimental]' >> .hg/hgrc
2562 $ echo 'evolution.createmarkers=True' >> .hg/hgrc
2562 $ echo 'evolution.createmarkers=True' >> .hg/hgrc
2563 $ echo a > a
2563 $ echo a > a
2564 $ hg ci -Am0
2564 $ hg ci -Am0
2565 adding a
2565 adding a
2566 $ echo b > b
2566 $ echo b > b
2567 $ hg ci -Am1
2567 $ hg ci -Am1
2568 adding b
2568 adding b
2569 $ echo B > b
2569 $ echo B > b
2570 $ hg ci --amend -m 1
2570 $ hg ci --amend -m 1
2571 $ hg up 0
2571 $ hg up 0
2572 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2572 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2573 $ echo c > c
2573 $ echo c > c
2574 $ hg ci -Am2
2574 $ hg ci -Am2
2575 adding c
2575 adding c
2576 created new head
2576 created new head
2577 $ hg up 'head() and not .'
2577 $ hg up 'head() and not .'
2578 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
2578 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
2579 $ hg log -G
2579 $ hg log -G
2580 o changeset: 3:db815d6d32e6
2580 o changeset: 3:db815d6d32e6
2581 | tag: tip
2581 | tag: tip
2582 | parent: 0:f7b1eb17ad24
2582 | parent: 0:f7b1eb17ad24
2583 | user: test
2583 | user: test
2584 | date: Thu Jan 01 00:00:00 1970 +0000
2584 | date: Thu Jan 01 00:00:00 1970 +0000
2585 | summary: 2
2585 | summary: 2
2586 |
2586 |
2587 | @ changeset: 2:9bc8ce7f9356
2587 | @ changeset: 2:9bc8ce7f9356
2588 |/ parent: 0:f7b1eb17ad24
2588 |/ parent: 0:f7b1eb17ad24
2589 | user: test
2589 | user: test
2590 | date: Thu Jan 01 00:00:00 1970 +0000
2590 | date: Thu Jan 01 00:00:00 1970 +0000
2591 | summary: 1
2591 | summary: 1
2592 |
2592 |
2593 o changeset: 0:f7b1eb17ad24
2593 o changeset: 0:f7b1eb17ad24
2594 user: test
2594 user: test
2595 date: Thu Jan 01 00:00:00 1970 +0000
2595 date: Thu Jan 01 00:00:00 1970 +0000
2596 summary: 0
2596 summary: 0
2597
2597
2598 $ hg log -f -G b
2598 $ hg log -f -G b
2599 @ changeset: 2:9bc8ce7f9356
2599 @ changeset: 2:9bc8ce7f9356
2600 | parent: 0:f7b1eb17ad24
2600 | parent: 0:f7b1eb17ad24
2601 ~ user: test
2601 ~ user: test
2602 date: Thu Jan 01 00:00:00 1970 +0000
2602 date: Thu Jan 01 00:00:00 1970 +0000
2603 summary: 1
2603 summary: 1
2604
2604
2605 $ hg log -G b
2605 $ hg log -G b
2606 @ changeset: 2:9bc8ce7f9356
2606 @ changeset: 2:9bc8ce7f9356
2607 | parent: 0:f7b1eb17ad24
2607 | parent: 0:f7b1eb17ad24
2608 ~ user: test
2608 ~ user: test
2609 date: Thu Jan 01 00:00:00 1970 +0000
2609 date: Thu Jan 01 00:00:00 1970 +0000
2610 summary: 1
2610 summary: 1
2611
2611
2612 $ cd ..
2612 $ cd ..
2613
2613
2614 Check proper report when the manifest changes but not the file issue4499
2614 Check proper report when the manifest changes but not the file issue4499
2615 ------------------------------------------------------------------------
2615 ------------------------------------------------------------------------
2616
2616
2617 $ hg init issue4499
2617 $ hg init issue4499
2618 $ cd issue4499
2618 $ cd issue4499
2619 $ for f in A B C D F E G H I J K L M N O P Q R S T U; do
2619 $ for f in A B C D F E G H I J K L M N O P Q R S T U; do
2620 > echo 1 > $f;
2620 > echo 1 > $f;
2621 > hg add $f;
2621 > hg add $f;
2622 > done
2622 > done
2623 $ hg commit -m 'A1B1C1'
2623 $ hg commit -m 'A1B1C1'
2624 $ echo 2 > A
2624 $ echo 2 > A
2625 $ echo 2 > B
2625 $ echo 2 > B
2626 $ echo 2 > C
2626 $ echo 2 > C
2627 $ hg commit -m 'A2B2C2'
2627 $ hg commit -m 'A2B2C2'
2628 $ hg up 0
2628 $ hg up 0
2629 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
2629 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
2630 $ echo 3 > A
2630 $ echo 3 > A
2631 $ echo 2 > B
2631 $ echo 2 > B
2632 $ echo 2 > C
2632 $ echo 2 > C
2633 $ hg commit -m 'A3B2C2'
2633 $ hg commit -m 'A3B2C2'
2634 created new head
2634 created new head
2635
2635
2636 $ hg log -G
2636 $ hg log -G
2637 @ changeset: 2:fe5fc3d0eb17
2637 @ changeset: 2:fe5fc3d0eb17
2638 | tag: tip
2638 | tag: tip
2639 | parent: 0:abf4f0e38563
2639 | parent: 0:abf4f0e38563
2640 | user: test
2640 | user: test
2641 | date: Thu Jan 01 00:00:00 1970 +0000
2641 | date: Thu Jan 01 00:00:00 1970 +0000
2642 | summary: A3B2C2
2642 | summary: A3B2C2
2643 |
2643 |
2644 | o changeset: 1:07dcc6b312c0
2644 | o changeset: 1:07dcc6b312c0
2645 |/ user: test
2645 |/ user: test
2646 | date: Thu Jan 01 00:00:00 1970 +0000
2646 | date: Thu Jan 01 00:00:00 1970 +0000
2647 | summary: A2B2C2
2647 | summary: A2B2C2
2648 |
2648 |
2649 o changeset: 0:abf4f0e38563
2649 o changeset: 0:abf4f0e38563
2650 user: test
2650 user: test
2651 date: Thu Jan 01 00:00:00 1970 +0000
2651 date: Thu Jan 01 00:00:00 1970 +0000
2652 summary: A1B1C1
2652 summary: A1B1C1
2653
2653
2654
2654
2655 Log -f on B should reports current changesets
2655 Log -f on B should reports current changesets
2656
2656
2657 $ hg log -fG B
2657 $ hg log -fG B
2658 @ changeset: 2:fe5fc3d0eb17
2658 @ changeset: 2:fe5fc3d0eb17
2659 | tag: tip
2659 | tag: tip
2660 | parent: 0:abf4f0e38563
2660 | parent: 0:abf4f0e38563
2661 | user: test
2661 | user: test
2662 | date: Thu Jan 01 00:00:00 1970 +0000
2662 | date: Thu Jan 01 00:00:00 1970 +0000
2663 | summary: A3B2C2
2663 | summary: A3B2C2
2664 |
2664 |
2665 o changeset: 0:abf4f0e38563
2665 o changeset: 0:abf4f0e38563
2666 user: test
2666 user: test
2667 date: Thu Jan 01 00:00:00 1970 +0000
2667 date: Thu Jan 01 00:00:00 1970 +0000
2668 summary: A1B1C1
2668 summary: A1B1C1
2669
2669
2670 $ cd ..
2670 $ cd ..
2671
2671
2672 --- going to test line wrap fix on using both --stat and -G (issue5800)
2672 --- going to test line wrap fix on using both --stat and -G (issue5800)
2673 $ hg init issue5800
2673 $ hg init issue5800
2674 $ cd issue5800
2674 $ cd issue5800
2675 $ touch a
2675 $ touch a
2676 $ hg ci -Am 'add a'
2676 $ hg ci -Am 'add a'
2677 adding a
2677 adding a
2678 ---- now we are going to add 300 lines to a
2678 ---- now we are going to add 300 lines to a
2679 $ for i in `$TESTDIR/seq.py 1 300`; do echo $i >> a; done
2679 $ for i in `$TESTDIR/seq.py 1 300`; do echo $i >> a; done
2680 $ hg ci -m 'modify a'
2680 $ hg ci -m 'modify a'
2681 $ hg log
2681 $ hg log
2682 changeset: 1:a98683e6a834
2682 changeset: 1:a98683e6a834
2683 tag: tip
2683 tag: tip
2684 user: test
2684 user: test
2685 date: Thu Jan 01 00:00:00 1970 +0000
2685 date: Thu Jan 01 00:00:00 1970 +0000
2686 summary: modify a
2686 summary: modify a
2687
2687
2688 changeset: 0:ac82d8b1f7c4
2688 changeset: 0:ac82d8b1f7c4
2689 user: test
2689 user: test
2690 date: Thu Jan 01 00:00:00 1970 +0000
2690 date: Thu Jan 01 00:00:00 1970 +0000
2691 summary: add a
2691 summary: add a
2692
2692
2693 ---- now visualise the changes we made without template
2693 ---- now visualise the changes we made without template
2694 $ hg log -l1 -r a98683e6a834 --stat -G
2694 $ hg log -l1 -r a98683e6a834 --stat -G
2695 @ changeset: 1:a98683e6a834
2695 @ changeset: 1:a98683e6a834
2696 | tag: tip
2696 | tag: tip
2697 ~ user: test
2697 ~ user: test
2698 date: Thu Jan 01 00:00:00 1970 +0000
2698 date: Thu Jan 01 00:00:00 1970 +0000
2699 summary: modify a
2699 summary: modify a
2700
2700
2701 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2701 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2702 1 files changed, 300 insertions(+), 0 deletions(-)
2702 1 files changed, 300 insertions(+), 0 deletions(-)
2703
2703
2704 ---- with template
2704 ---- with template
2705 $ hg log -l1 -r a98683e6a834 --stat -G -T bisect
2705 $ hg log -l1 -r a98683e6a834 --stat -G -T bisect
2706 @ changeset: 1:a98683e6a834
2706 @ changeset: 1:a98683e6a834
2707 | bisect:
2707 | bisect:
2708 ~ tag: tip
2708 ~ tag: tip
2709 user: test
2709 user: test
2710 date: Thu Jan 01 00:00:00 1970 +0000
2710 date: Thu Jan 01 00:00:00 1970 +0000
2711 summary: modify a
2711 summary: modify a
2712
2712
2713 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2713 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2714 1 files changed, 300 insertions(+), 0 deletions(-)
2714 1 files changed, 300 insertions(+), 0 deletions(-)
2715
2715
2716 $ hg log -l1 -r a98683e6a834 --stat -G -T changelog
2716 $ hg log -l1 -r a98683e6a834 --stat -G -T changelog
2717 1970-01-01 test <test>
2717 1970-01-01 test <test>
2718
2718
2719 @ * a:
2719 @ * a:
2720 | modify a
2720 | modify a
2721 ~ [a98683e6a834] [tip]
2721 ~ [a98683e6a834] [tip]
2722
2722
2723 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2723 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2724 1 files changed, 300 insertions(+), 0 deletions(-)
2724 1 files changed, 300 insertions(+), 0 deletions(-)
2725
2725
2726 $ hg log -l1 -r a98683e6a834 --stat -G -T compact
2726 $ hg log -l1 -r a98683e6a834 --stat -G -T compact
2727 @ 1[tip] a98683e6a834 1970-01-01 00:00 +0000 test
2727 @ 1[tip] a98683e6a834 1970-01-01 00:00 +0000 test
2728 | modify a
2728 | modify a
2729 ~
2729 ~
2730 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2730 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2731 1 files changed, 300 insertions(+), 0 deletions(-)
2731 1 files changed, 300 insertions(+), 0 deletions(-)
2732
2732
2733 $ hg log -l1 -r a98683e6a834 --stat -G -T default
2733 $ hg log -l1 -r a98683e6a834 --stat -G -T default
2734 @ changeset: 1:a98683e6a834
2734 @ changeset: 1:a98683e6a834
2735 | tag: tip
2735 | tag: tip
2736 ~ user: test
2736 ~ user: test
2737 date: Thu Jan 01 00:00:00 1970 +0000
2737 date: Thu Jan 01 00:00:00 1970 +0000
2738 summary: modify a
2738 summary: modify a
2739
2739
2740 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2740 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2741 1 files changed, 300 insertions(+), 0 deletions(-)
2741 1 files changed, 300 insertions(+), 0 deletions(-)
2742
2742
2743 $ hg log -l1 -r a98683e6a834 --stat -G -T phases
2743 $ hg log -l1 -r a98683e6a834 --stat -G -T phases
2744 @ changeset: 1:a98683e6a834
2744 @ changeset: 1:a98683e6a834
2745 | tag: tip
2745 | tag: tip
2746 ~ phase: draft
2746 ~ phase: draft
2747 user: test
2747 user: test
2748 date: Thu Jan 01 00:00:00 1970 +0000
2748 date: Thu Jan 01 00:00:00 1970 +0000
2749 summary: modify a
2749 summary: modify a
2750
2750
2751 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2751 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2752 1 files changed, 300 insertions(+), 0 deletions(-)
2752 1 files changed, 300 insertions(+), 0 deletions(-)
2753
2753
2754 $ hg log -l1 -r a98683e6a834 --stat -G -T show
2754 $ hg log -l1 -r a98683e6a834 --stat -G -T show
2755 @ changeset: 1:a98683e6a834
2755 @ changeset: 1:a98683e6a834
2756 | tag: tip
2756 | tag: tip
2757 ~ user: test
2757 ~ user: test
2758 date: Thu Jan 01 00:00:00 1970 +0000
2758 date: Thu Jan 01 00:00:00 1970 +0000
2759 summary: modify a
2759 summary: modify a
2760
2760
2761 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2761 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2762 1 files changed, 300 insertions(+), 0 deletions(-)
2762 1 files changed, 300 insertions(+), 0 deletions(-)
2763
2763
2764 $ hg log -l1 -r a98683e6a834 --stat -G -T status
2764 $ hg log -l1 -r a98683e6a834 --stat -G -T status
2765 @ changeset: 1:a98683e6a834
2765 @ changeset: 1:a98683e6a834
2766 | tag: tip
2766 | tag: tip
2767 ~ user: test
2767 ~ user: test
2768 date: Thu Jan 01 00:00:00 1970 +0000
2768 date: Thu Jan 01 00:00:00 1970 +0000
2769 summary: modify a
2769 summary: modify a
2770 files:
2770 files:
2771 M a
2771 M a
2772
2772
2773 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2773 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2774 1 files changed, 300 insertions(+), 0 deletions(-)
2774 1 files changed, 300 insertions(+), 0 deletions(-)
2775
2775
2776 $ hg log -l1 -r a98683e6a834 --stat -G -T xml
2776 $ hg log -l1 -r a98683e6a834 --stat -G -T xml
2777 <?xml version="1.0"?>
2777 <?xml version="1.0"?>
2778 <log>
2778 <log>
2779 @ <logentry revision="1" node="a98683e6a8340830a7683909768b62871e84bc9d">
2779 @ <logentry revision="1" node="a98683e6a8340830a7683909768b62871e84bc9d">
2780 | <tag>tip</tag>
2780 | <tag>tip</tag>
2781 ~ <author email="test">test</author>
2781 ~ <author email="test">test</author>
2782 <date>1970-01-01T00:00:00+00:00</date>
2782 <date>1970-01-01T00:00:00+00:00</date>
2783 <msg xml:space="preserve">modify a</msg>
2783 <msg xml:space="preserve">modify a</msg>
2784 </logentry>
2784 </logentry>
2785 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2785 a | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2786 1 files changed, 300 insertions(+), 0 deletions(-)
2786 1 files changed, 300 insertions(+), 0 deletions(-)
2787
2787
2788 </log>
2788 </log>
2789
2789
2790 $ cd ..
2790 $ cd ..
@@ -1,674 +1,682 b''
1 $ hg init repo1
1 $ hg init repo1
2 $ cd repo1
2 $ cd repo1
3 $ mkdir a b a/1 b/1 b/2
3 $ mkdir a b a/1 b/1 b/2
4 $ touch in_root a/in_a b/in_b a/1/in_a_1 b/1/in_b_1 b/2/in_b_2
4 $ touch in_root a/in_a b/in_b a/1/in_a_1 b/1/in_b_1 b/2/in_b_2
5
5
6 hg status in repo root:
6 hg status in repo root:
7
7
8 $ hg status
8 $ hg status
9 ? a/1/in_a_1
9 ? a/1/in_a_1
10 ? a/in_a
10 ? a/in_a
11 ? b/1/in_b_1
11 ? b/1/in_b_1
12 ? b/2/in_b_2
12 ? b/2/in_b_2
13 ? b/in_b
13 ? b/in_b
14 ? in_root
14 ? in_root
15
15
16 hg status . in repo root:
16 hg status . in repo root:
17
17
18 $ hg status .
18 $ hg status .
19 ? a/1/in_a_1
19 ? a/1/in_a_1
20 ? a/in_a
20 ? a/in_a
21 ? b/1/in_b_1
21 ? b/1/in_b_1
22 ? b/2/in_b_2
22 ? b/2/in_b_2
23 ? b/in_b
23 ? b/in_b
24 ? in_root
24 ? in_root
25
25
26 $ hg status --cwd a
26 $ hg status --cwd a
27 ? a/1/in_a_1
27 ? a/1/in_a_1
28 ? a/in_a
28 ? a/in_a
29 ? b/1/in_b_1
29 ? b/1/in_b_1
30 ? b/2/in_b_2
30 ? b/2/in_b_2
31 ? b/in_b
31 ? b/in_b
32 ? in_root
32 ? in_root
33 $ hg status --cwd a .
33 $ hg status --cwd a .
34 ? 1/in_a_1
34 ? 1/in_a_1
35 ? in_a
35 ? in_a
36 $ hg status --cwd a ..
36 $ hg status --cwd a ..
37 ? 1/in_a_1
37 ? 1/in_a_1
38 ? in_a
38 ? in_a
39 ? ../b/1/in_b_1
39 ? ../b/1/in_b_1
40 ? ../b/2/in_b_2
40 ? ../b/2/in_b_2
41 ? ../b/in_b
41 ? ../b/in_b
42 ? ../in_root
42 ? ../in_root
43
43
44 $ hg status --cwd b
44 $ hg status --cwd b
45 ? a/1/in_a_1
45 ? a/1/in_a_1
46 ? a/in_a
46 ? a/in_a
47 ? b/1/in_b_1
47 ? b/1/in_b_1
48 ? b/2/in_b_2
48 ? b/2/in_b_2
49 ? b/in_b
49 ? b/in_b
50 ? in_root
50 ? in_root
51 $ hg status --cwd b .
51 $ hg status --cwd b .
52 ? 1/in_b_1
52 ? 1/in_b_1
53 ? 2/in_b_2
53 ? 2/in_b_2
54 ? in_b
54 ? in_b
55 $ hg status --cwd b ..
55 $ hg status --cwd b ..
56 ? ../a/1/in_a_1
56 ? ../a/1/in_a_1
57 ? ../a/in_a
57 ? ../a/in_a
58 ? 1/in_b_1
58 ? 1/in_b_1
59 ? 2/in_b_2
59 ? 2/in_b_2
60 ? in_b
60 ? in_b
61 ? ../in_root
61 ? ../in_root
62
62
63 $ hg status --cwd a/1
63 $ hg status --cwd a/1
64 ? a/1/in_a_1
64 ? a/1/in_a_1
65 ? a/in_a
65 ? a/in_a
66 ? b/1/in_b_1
66 ? b/1/in_b_1
67 ? b/2/in_b_2
67 ? b/2/in_b_2
68 ? b/in_b
68 ? b/in_b
69 ? in_root
69 ? in_root
70 $ hg status --cwd a/1 .
70 $ hg status --cwd a/1 .
71 ? in_a_1
71 ? in_a_1
72 $ hg status --cwd a/1 ..
72 $ hg status --cwd a/1 ..
73 ? in_a_1
73 ? in_a_1
74 ? ../in_a
74 ? ../in_a
75
75
76 $ hg status --cwd b/1
76 $ hg status --cwd b/1
77 ? a/1/in_a_1
77 ? a/1/in_a_1
78 ? a/in_a
78 ? a/in_a
79 ? b/1/in_b_1
79 ? b/1/in_b_1
80 ? b/2/in_b_2
80 ? b/2/in_b_2
81 ? b/in_b
81 ? b/in_b
82 ? in_root
82 ? in_root
83 $ hg status --cwd b/1 .
83 $ hg status --cwd b/1 .
84 ? in_b_1
84 ? in_b_1
85 $ hg status --cwd b/1 ..
85 $ hg status --cwd b/1 ..
86 ? in_b_1
86 ? in_b_1
87 ? ../2/in_b_2
87 ? ../2/in_b_2
88 ? ../in_b
88 ? ../in_b
89
89
90 $ hg status --cwd b/2
90 $ hg status --cwd b/2
91 ? a/1/in_a_1
91 ? a/1/in_a_1
92 ? a/in_a
92 ? a/in_a
93 ? b/1/in_b_1
93 ? b/1/in_b_1
94 ? b/2/in_b_2
94 ? b/2/in_b_2
95 ? b/in_b
95 ? b/in_b
96 ? in_root
96 ? in_root
97 $ hg status --cwd b/2 .
97 $ hg status --cwd b/2 .
98 ? in_b_2
98 ? in_b_2
99 $ hg status --cwd b/2 ..
99 $ hg status --cwd b/2 ..
100 ? ../1/in_b_1
100 ? ../1/in_b_1
101 ? in_b_2
101 ? in_b_2
102 ? ../in_b
102 ? ../in_b
103
103
104 combining patterns with root and patterns without a root works
104 combining patterns with root and patterns without a root works
105
105
106 $ hg st a/in_a re:.*b$
106 $ hg st a/in_a re:.*b$
107 ? a/in_a
107 ? a/in_a
108 ? b/in_b
108 ? b/in_b
109
109
110 tweaking defaults works
110 tweaking defaults works
111 $ hg status --cwd a --config ui.tweakdefaults=yes
111 $ hg status --cwd a --config ui.tweakdefaults=yes
112 ? 1/in_a_1
112 ? 1/in_a_1
113 ? in_a
113 ? in_a
114 ? ../b/1/in_b_1
114 ? ../b/1/in_b_1
115 ? ../b/2/in_b_2
115 ? ../b/2/in_b_2
116 ? ../b/in_b
116 ? ../b/in_b
117 ? ../in_root
117 ? ../in_root
118 $ HGPLAIN=1 hg status --cwd a --config ui.tweakdefaults=yes
118 $ HGPLAIN=1 hg status --cwd a --config ui.tweakdefaults=yes
119 ? a/1/in_a_1 (glob)
119 ? a/1/in_a_1 (glob)
120 ? a/in_a (glob)
120 ? a/in_a (glob)
121 ? b/1/in_b_1 (glob)
121 ? b/1/in_b_1 (glob)
122 ? b/2/in_b_2 (glob)
122 ? b/2/in_b_2 (glob)
123 ? b/in_b (glob)
123 ? b/in_b (glob)
124 ? in_root
124 ? in_root
125 $ HGPLAINEXCEPT=tweakdefaults hg status --cwd a --config ui.tweakdefaults=yes
125 $ HGPLAINEXCEPT=tweakdefaults hg status --cwd a --config ui.tweakdefaults=yes
126 ? 1/in_a_1
126 ? 1/in_a_1
127 ? in_a
127 ? in_a
128 ? ../b/1/in_b_1
128 ? ../b/1/in_b_1
129 ? ../b/2/in_b_2
129 ? ../b/2/in_b_2
130 ? ../b/in_b
130 ? ../b/in_b
131 ? ../in_root (glob)
131 ? ../in_root (glob)
132
132
133 relative paths can be requested
133 relative paths can be requested
134
134
135 $ hg status --cwd a --config ui.relative-paths=yes
135 $ hg status --cwd a --config ui.relative-paths=yes
136 ? 1/in_a_1
136 ? 1/in_a_1
137 ? in_a
137 ? in_a
138 ? ../b/1/in_b_1
138 ? ../b/1/in_b_1
139 ? ../b/2/in_b_2
139 ? ../b/2/in_b_2
140 ? ../b/in_b
140 ? ../b/in_b
141 ? ../in_root
141 ? ../in_root
142
142
143 $ hg status --cwd a . --config ui.relative-paths=legacy
143 $ hg status --cwd a . --config ui.relative-paths=legacy
144 ? 1/in_a_1
144 ? 1/in_a_1
145 ? in_a
145 ? in_a
146 $ hg status --cwd a . --config ui.relative-paths=no
146 $ hg status --cwd a . --config ui.relative-paths=no
147 ? a/1/in_a_1
147 ? a/1/in_a_1
148 ? a/in_a
148 ? a/in_a
149
149
150 commands.status.relative overrides ui.relative-paths
150 commands.status.relative overrides ui.relative-paths
151
151
152 $ cat >> $HGRCPATH <<EOF
152 $ cat >> $HGRCPATH <<EOF
153 > [ui]
153 > [ui]
154 > relative-paths = False
154 > relative-paths = False
155 > [commands]
155 > [commands]
156 > status.relative = True
156 > status.relative = True
157 > EOF
157 > EOF
158 $ hg status --cwd a
158 $ hg status --cwd a
159 ? 1/in_a_1
159 ? 1/in_a_1
160 ? in_a
160 ? in_a
161 ? ../b/1/in_b_1
161 ? ../b/1/in_b_1
162 ? ../b/2/in_b_2
162 ? ../b/2/in_b_2
163 ? ../b/in_b
163 ? ../b/in_b
164 ? ../in_root
164 ? ../in_root
165 $ HGPLAIN=1 hg status --cwd a
165 $ HGPLAIN=1 hg status --cwd a
166 ? a/1/in_a_1 (glob)
166 ? a/1/in_a_1 (glob)
167 ? a/in_a (glob)
167 ? a/in_a (glob)
168 ? b/1/in_b_1 (glob)
168 ? b/1/in_b_1 (glob)
169 ? b/2/in_b_2 (glob)
169 ? b/2/in_b_2 (glob)
170 ? b/in_b (glob)
170 ? b/in_b (glob)
171 ? in_root
171 ? in_root
172
172
173 if relative paths are explicitly off, tweakdefaults doesn't change it
173 if relative paths are explicitly off, tweakdefaults doesn't change it
174 $ cat >> $HGRCPATH <<EOF
174 $ cat >> $HGRCPATH <<EOF
175 > [commands]
175 > [commands]
176 > status.relative = False
176 > status.relative = False
177 > EOF
177 > EOF
178 $ hg status --cwd a --config ui.tweakdefaults=yes
178 $ hg status --cwd a --config ui.tweakdefaults=yes
179 ? a/1/in_a_1
179 ? a/1/in_a_1
180 ? a/in_a
180 ? a/in_a
181 ? b/1/in_b_1
181 ? b/1/in_b_1
182 ? b/2/in_b_2
182 ? b/2/in_b_2
183 ? b/in_b
183 ? b/in_b
184 ? in_root
184 ? in_root
185
185
186 $ cd ..
186 $ cd ..
187
187
188 $ hg init repo2
188 $ hg init repo2
189 $ cd repo2
189 $ cd repo2
190 $ touch modified removed deleted ignored
190 $ touch modified removed deleted ignored
191 $ echo "^ignored$" > .hgignore
191 $ echo "^ignored$" > .hgignore
192 $ hg ci -A -m 'initial checkin'
192 $ hg ci -A -m 'initial checkin'
193 adding .hgignore
193 adding .hgignore
194 adding deleted
194 adding deleted
195 adding modified
195 adding modified
196 adding removed
196 adding removed
197 $ touch modified added unknown ignored
197 $ touch modified added unknown ignored
198 $ hg add added
198 $ hg add added
199 $ hg remove removed
199 $ hg remove removed
200 $ rm deleted
200 $ rm deleted
201
201
202 hg status:
202 hg status:
203
203
204 $ hg status
204 $ hg status
205 A added
205 A added
206 R removed
206 R removed
207 ! deleted
207 ! deleted
208 ? unknown
208 ? unknown
209
209
210 hg status modified added removed deleted unknown never-existed ignored:
210 hg status modified added removed deleted unknown never-existed ignored:
211
211
212 $ hg status modified added removed deleted unknown never-existed ignored
212 $ hg status modified added removed deleted unknown never-existed ignored
213 never-existed: * (glob)
213 never-existed: * (glob)
214 A added
214 A added
215 R removed
215 R removed
216 ! deleted
216 ! deleted
217 ? unknown
217 ? unknown
218
218
219 $ hg copy modified copied
219 $ hg copy modified copied
220
220
221 hg status -C:
221 hg status -C:
222
222
223 $ hg status -C
223 $ hg status -C
224 A added
224 A added
225 A copied
225 A copied
226 modified
226 modified
227 R removed
227 R removed
228 ! deleted
228 ! deleted
229 ? unknown
229 ? unknown
230
230
231 hg status -A:
231 hg status -A:
232
232
233 $ hg status -A
233 $ hg status -A
234 A added
234 A added
235 A copied
235 A copied
236 modified
236 modified
237 R removed
237 R removed
238 ! deleted
238 ! deleted
239 ? unknown
239 ? unknown
240 I ignored
240 I ignored
241 C .hgignore
241 C .hgignore
242 C modified
242 C modified
243
243
244 $ hg status -A -T '{status} {path} {node|shortest}\n'
244 $ hg status -A -T '{status} {path} {node|shortest}\n'
245 A added ffff
245 A added ffff
246 A copied ffff
246 A copied ffff
247 R removed ffff
247 R removed ffff
248 ! deleted ffff
248 ! deleted ffff
249 ? unknown ffff
249 ? unknown ffff
250 I ignored ffff
250 I ignored ffff
251 C .hgignore ffff
251 C .hgignore ffff
252 C modified ffff
252 C modified ffff
253
253
254 $ hg status -A -Tjson
254 $ hg status -A -Tjson
255 [
255 [
256 {
256 {
257 "itemtype": "file",
257 "itemtype": "file",
258 "path": "added",
258 "path": "added",
259 "status": "A"
259 "status": "A"
260 },
260 },
261 {
261 {
262 "itemtype": "file",
262 "itemtype": "file",
263 "path": "copied",
263 "path": "copied",
264 "source": "modified",
264 "source": "modified",
265 "status": "A"
265 "status": "A"
266 },
266 },
267 {
267 {
268 "itemtype": "file",
268 "itemtype": "file",
269 "path": "removed",
269 "path": "removed",
270 "status": "R"
270 "status": "R"
271 },
271 },
272 {
272 {
273 "itemtype": "file",
273 "itemtype": "file",
274 "path": "deleted",
274 "path": "deleted",
275 "status": "!"
275 "status": "!"
276 },
276 },
277 {
277 {
278 "itemtype": "file",
278 "itemtype": "file",
279 "path": "unknown",
279 "path": "unknown",
280 "status": "?"
280 "status": "?"
281 },
281 },
282 {
282 {
283 "itemtype": "file",
283 "itemtype": "file",
284 "path": "ignored",
284 "path": "ignored",
285 "status": "I"
285 "status": "I"
286 },
286 },
287 {
287 {
288 "itemtype": "file",
288 "itemtype": "file",
289 "path": ".hgignore",
289 "path": ".hgignore",
290 "status": "C"
290 "status": "C"
291 },
291 },
292 {
292 {
293 "itemtype": "file",
293 "itemtype": "file",
294 "path": "modified",
294 "path": "modified",
295 "status": "C"
295 "status": "C"
296 }
296 }
297 ]
297 ]
298
298
299 $ hg status -A -Tpickle > pickle
299 $ hg status -A -Tpickle > pickle
300 >>> from __future__ import print_function
300 >>> from __future__ import print_function
301 >>> from mercurial import util
301 >>> from mercurial import util
302 >>> pickle = util.pickle
302 >>> pickle = util.pickle
303 >>> data = sorted((x[b'status'].decode(), x[b'path'].decode()) for x in pickle.load(open("pickle", r"rb")))
303 >>> data = sorted((x[b'status'].decode(), x[b'path'].decode()) for x in pickle.load(open("pickle", r"rb")))
304 >>> for s, p in data: print("%s %s" % (s, p))
304 >>> for s, p in data: print("%s %s" % (s, p))
305 ! deleted
305 ! deleted
306 ? pickle
306 ? pickle
307 ? unknown
307 ? unknown
308 A added
308 A added
309 A copied
309 A copied
310 C .hgignore
310 C .hgignore
311 C modified
311 C modified
312 I ignored
312 I ignored
313 R removed
313 R removed
314 $ rm pickle
314 $ rm pickle
315
315
316 $ echo "^ignoreddir$" > .hgignore
316 $ echo "^ignoreddir$" > .hgignore
317 $ mkdir ignoreddir
317 $ mkdir ignoreddir
318 $ touch ignoreddir/file
318 $ touch ignoreddir/file
319
319
320 Test templater support:
320 Test templater support:
321
321
322 $ hg status -AT "[{status}]\t{if(source, '{source} -> ')}{path}\n"
322 $ hg status -AT "[{status}]\t{if(source, '{source} -> ')}{path}\n"
323 [M] .hgignore
323 [M] .hgignore
324 [A] added
324 [A] added
325 [A] modified -> copied
325 [A] modified -> copied
326 [R] removed
326 [R] removed
327 [!] deleted
327 [!] deleted
328 [?] ignored
328 [?] ignored
329 [?] unknown
329 [?] unknown
330 [I] ignoreddir/file
330 [I] ignoreddir/file
331 [C] modified
331 [C] modified
332 $ hg status -AT default
332 $ hg status -AT default
333 M .hgignore
333 M .hgignore
334 A added
334 A added
335 A copied
335 A copied
336 modified
336 modified
337 R removed
337 R removed
338 ! deleted
338 ! deleted
339 ? ignored
339 ? ignored
340 ? unknown
340 ? unknown
341 I ignoreddir/file
341 I ignoreddir/file
342 C modified
342 C modified
343 $ hg status -T compact
343 $ hg status -T compact
344 abort: "status" not in template map
344 abort: "status" not in template map
345 [255]
345 [255]
346
346
347 hg status ignoreddir/file:
347 hg status ignoreddir/file:
348
348
349 $ hg status ignoreddir/file
349 $ hg status ignoreddir/file
350
350
351 hg status -i ignoreddir/file:
351 hg status -i ignoreddir/file:
352
352
353 $ hg status -i ignoreddir/file
353 $ hg status -i ignoreddir/file
354 I ignoreddir/file
354 I ignoreddir/file
355 $ cd ..
355 $ cd ..
356
356
357 Check 'status -q' and some combinations
357 Check 'status -q' and some combinations
358
358
359 $ hg init repo3
359 $ hg init repo3
360 $ cd repo3
360 $ cd repo3
361 $ touch modified removed deleted ignored
361 $ touch modified removed deleted ignored
362 $ echo "^ignored$" > .hgignore
362 $ echo "^ignored$" > .hgignore
363 $ hg commit -A -m 'initial checkin'
363 $ hg commit -A -m 'initial checkin'
364 adding .hgignore
364 adding .hgignore
365 adding deleted
365 adding deleted
366 adding modified
366 adding modified
367 adding removed
367 adding removed
368 $ touch added unknown ignored
368 $ touch added unknown ignored
369 $ hg add added
369 $ hg add added
370 $ echo "test" >> modified
370 $ echo "test" >> modified
371 $ hg remove removed
371 $ hg remove removed
372 $ rm deleted
372 $ rm deleted
373 $ hg copy modified copied
373 $ hg copy modified copied
374
374
375 Specify working directory revision explicitly, that should be the same as
375 Specify working directory revision explicitly, that should be the same as
376 "hg status"
376 "hg status"
377
377
378 $ hg status --change "wdir()"
378 $ hg status --change "wdir()"
379 M modified
379 M modified
380 A added
380 A added
381 A copied
381 A copied
382 R removed
382 R removed
383 ! deleted
383 ! deleted
384 ? unknown
384 ? unknown
385
385
386 Run status with 2 different flags.
386 Run status with 2 different flags.
387 Check if result is the same or different.
387 Check if result is the same or different.
388 If result is not as expected, raise error
388 If result is not as expected, raise error
389
389
390 $ assert() {
390 $ assert() {
391 > hg status $1 > ../a
391 > hg status $1 > ../a
392 > hg status $2 > ../b
392 > hg status $2 > ../b
393 > if diff ../a ../b > /dev/null; then
393 > if diff ../a ../b > /dev/null; then
394 > out=0
394 > out=0
395 > else
395 > else
396 > out=1
396 > out=1
397 > fi
397 > fi
398 > if [ $3 -eq 0 ]; then
398 > if [ $3 -eq 0 ]; then
399 > df="same"
399 > df="same"
400 > else
400 > else
401 > df="different"
401 > df="different"
402 > fi
402 > fi
403 > if [ $out -ne $3 ]; then
403 > if [ $out -ne $3 ]; then
404 > echo "Error on $1 and $2, should be $df."
404 > echo "Error on $1 and $2, should be $df."
405 > fi
405 > fi
406 > }
406 > }
407
407
408 Assert flag1 flag2 [0-same | 1-different]
408 Assert flag1 flag2 [0-same | 1-different]
409
409
410 $ assert "-q" "-mard" 0
410 $ assert "-q" "-mard" 0
411 $ assert "-A" "-marduicC" 0
411 $ assert "-A" "-marduicC" 0
412 $ assert "-qA" "-mardcC" 0
412 $ assert "-qA" "-mardcC" 0
413 $ assert "-qAui" "-A" 0
413 $ assert "-qAui" "-A" 0
414 $ assert "-qAu" "-marducC" 0
414 $ assert "-qAu" "-marducC" 0
415 $ assert "-qAi" "-mardicC" 0
415 $ assert "-qAi" "-mardicC" 0
416 $ assert "-qu" "-u" 0
416 $ assert "-qu" "-u" 0
417 $ assert "-q" "-u" 1
417 $ assert "-q" "-u" 1
418 $ assert "-m" "-a" 1
418 $ assert "-m" "-a" 1
419 $ assert "-r" "-d" 1
419 $ assert "-r" "-d" 1
420 $ cd ..
420 $ cd ..
421
421
422 $ hg init repo4
422 $ hg init repo4
423 $ cd repo4
423 $ cd repo4
424 $ touch modified removed deleted
424 $ touch modified removed deleted
425 $ hg ci -q -A -m 'initial checkin'
425 $ hg ci -q -A -m 'initial checkin'
426 $ touch added unknown
426 $ touch added unknown
427 $ hg add added
427 $ hg add added
428 $ hg remove removed
428 $ hg remove removed
429 $ rm deleted
429 $ rm deleted
430 $ echo x > modified
430 $ echo x > modified
431 $ hg copy modified copied
431 $ hg copy modified copied
432 $ hg ci -m 'test checkin' -d "1000001 0"
432 $ hg ci -m 'test checkin' -d "1000001 0"
433 $ rm *
433 $ rm *
434 $ touch unrelated
434 $ touch unrelated
435 $ hg ci -q -A -m 'unrelated checkin' -d "1000002 0"
435 $ hg ci -q -A -m 'unrelated checkin' -d "1000002 0"
436
436
437 hg status --change 1:
437 hg status --change 1:
438
438
439 $ hg status --change 1
439 $ hg status --change 1
440 M modified
440 M modified
441 A added
441 A added
442 A copied
442 A copied
443 R removed
443 R removed
444
444
445 hg status --change 1 unrelated:
445 hg status --change 1 unrelated:
446
446
447 $ hg status --change 1 unrelated
447 $ hg status --change 1 unrelated
448
448
449 hg status -C --change 1 added modified copied removed deleted:
449 hg status -C --change 1 added modified copied removed deleted:
450
450
451 $ hg status -C --change 1 added modified copied removed deleted
451 $ hg status -C --change 1 added modified copied removed deleted
452 M modified
452 M modified
453 A added
453 A added
454 A copied
454 A copied
455 modified
455 modified
456 R removed
456 R removed
457
457
458 hg status -A --change 1 and revset:
458 hg status -A --change 1 and revset:
459
459
460 $ hg status -A --change '1|1'
460 $ hg status -A --change '1|1'
461 M modified
461 M modified
462 A added
462 A added
463 A copied
463 A copied
464 modified
464 modified
465 R removed
465 R removed
466 C deleted
466 C deleted
467
467
468 $ cd ..
468 $ cd ..
469
469
470 hg status with --rev and reverted changes:
470 hg status with --rev and reverted changes:
471
471
472 $ hg init reverted-changes-repo
472 $ hg init reverted-changes-repo
473 $ cd reverted-changes-repo
473 $ cd reverted-changes-repo
474 $ echo a > file
474 $ echo a > file
475 $ hg add file
475 $ hg add file
476 $ hg ci -m a
476 $ hg ci -m a
477 $ echo b > file
477 $ echo b > file
478 $ hg ci -m b
478 $ hg ci -m b
479
479
480 reverted file should appear clean
480 reverted file should appear clean
481
481
482 $ hg revert -r 0 .
482 $ hg revert -r 0 .
483 reverting file
483 reverting file
484 $ hg status -A --rev 0
484 $ hg status -A --rev 0
485 C file
485 C file
486
486
487 #if execbit
487 #if execbit
488 reverted file with changed flag should appear modified
488 reverted file with changed flag should appear modified
489
489
490 $ chmod +x file
490 $ chmod +x file
491 $ hg status -A --rev 0
491 $ hg status -A --rev 0
492 M file
492 M file
493
493
494 $ hg revert -r 0 .
494 $ hg revert -r 0 .
495 reverting file
495 reverting file
496
496
497 reverted and committed file with changed flag should appear modified
497 reverted and committed file with changed flag should appear modified
498
498
499 $ hg co -C .
499 $ hg co -C .
500 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
500 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
501 $ chmod +x file
501 $ chmod +x file
502 $ hg ci -m 'change flag'
502 $ hg ci -m 'change flag'
503 $ hg status -A --rev 1 --rev 2
503 $ hg status -A --rev 1 --rev 2
504 M file
504 M file
505 $ hg diff -r 1 -r 2
505 $ hg diff -r 1 -r 2
506
506
507 #endif
507 #endif
508
508
509 $ cd ..
509 $ cd ..
510
510
511 hg status of binary file starting with '\1\n', a separator for metadata:
511 hg status of binary file starting with '\1\n', a separator for metadata:
512
512
513 $ hg init repo5
513 $ hg init repo5
514 $ cd repo5
514 $ cd repo5
515 >>> open("010a", r"wb").write(b"\1\nfoo") and None
515 >>> open("010a", r"wb").write(b"\1\nfoo") and None
516 $ hg ci -q -A -m 'initial checkin'
516 $ hg ci -q -A -m 'initial checkin'
517 $ hg status -A
517 $ hg status -A
518 C 010a
518 C 010a
519
519
520 >>> open("010a", r"wb").write(b"\1\nbar") and None
520 >>> open("010a", r"wb").write(b"\1\nbar") and None
521 $ hg status -A
521 $ hg status -A
522 M 010a
522 M 010a
523 $ hg ci -q -m 'modify 010a'
523 $ hg ci -q -m 'modify 010a'
524 $ hg status -A --rev 0:1
524 $ hg status -A --rev 0:1
525 M 010a
525 M 010a
526
526
527 $ touch empty
527 $ touch empty
528 $ hg ci -q -A -m 'add another file'
528 $ hg ci -q -A -m 'add another file'
529 $ hg status -A --rev 1:2 010a
529 $ hg status -A --rev 1:2 010a
530 C 010a
530 C 010a
531
531
532 $ cd ..
532 $ cd ..
533
533
534 test "hg status" with "directory pattern" which matches against files
534 test "hg status" with "directory pattern" which matches against files
535 only known on target revision.
535 only known on target revision.
536
536
537 $ hg init repo6
537 $ hg init repo6
538 $ cd repo6
538 $ cd repo6
539
539
540 $ echo a > a.txt
540 $ echo a > a.txt
541 $ hg add a.txt
541 $ hg add a.txt
542 $ hg commit -m '#0'
542 $ hg commit -m '#0'
543 $ mkdir -p 1/2/3/4/5
543 $ mkdir -p 1/2/3/4/5
544 $ echo b > 1/2/3/4/5/b.txt
544 $ echo b > 1/2/3/4/5/b.txt
545 $ hg add 1/2/3/4/5/b.txt
545 $ hg add 1/2/3/4/5/b.txt
546 $ hg commit -m '#1'
546 $ hg commit -m '#1'
547
547
548 $ hg update -C 0 > /dev/null
548 $ hg update -C 0 > /dev/null
549 $ hg status -A
549 $ hg status -A
550 C a.txt
550 C a.txt
551
551
552 the directory matching against specified pattern should be removed,
552 the directory matching against specified pattern should be removed,
553 because directory existence prevents 'dirstate.walk()' from showing
553 because directory existence prevents 'dirstate.walk()' from showing
554 warning message about such pattern.
554 warning message about such pattern.
555
555
556 $ test ! -d 1
556 $ test ! -d 1
557 $ hg status -A --rev 1 1/2/3/4/5/b.txt
557 $ hg status -A --rev 1 1/2/3/4/5/b.txt
558 R 1/2/3/4/5/b.txt
558 R 1/2/3/4/5/b.txt
559 $ hg status -A --rev 1 1/2/3/4/5
559 $ hg status -A --rev 1 1/2/3/4/5
560 R 1/2/3/4/5/b.txt
560 R 1/2/3/4/5/b.txt
561 $ hg status -A --rev 1 1/2/3
561 $ hg status -A --rev 1 1/2/3
562 R 1/2/3/4/5/b.txt
562 R 1/2/3/4/5/b.txt
563 $ hg status -A --rev 1 1
563 $ hg status -A --rev 1 1
564 R 1/2/3/4/5/b.txt
564 R 1/2/3/4/5/b.txt
565
565
566 $ hg status --config ui.formatdebug=True --rev 1 1
566 $ hg status --config ui.formatdebug=True --rev 1 1
567 status = [
567 status = [
568 {
568 {
569 'itemtype': 'file',
569 'itemtype': 'file',
570 'path': '1/2/3/4/5/b.txt',
570 'path': '1/2/3/4/5/b.txt',
571 'status': 'R'
571 'status': 'R'
572 },
572 },
573 ]
573 ]
574
574
575 #if windows
575 #if windows
576 $ hg --config ui.slash=false status -A --rev 1 1
576 $ hg --config ui.slash=false status -A --rev 1 1
577 R 1\2\3\4\5\b.txt
577 R 1\2\3\4\5\b.txt
578 #endif
578 #endif
579
579
580 $ cd ..
580 $ cd ..
581
581
582 Status after move overwriting a file (issue4458)
582 Status after move overwriting a file (issue4458)
583 =================================================
583 =================================================
584
584
585
585
586 $ hg init issue4458
586 $ hg init issue4458
587 $ cd issue4458
587 $ cd issue4458
588 $ echo a > a
588 $ echo a > a
589 $ echo b > b
589 $ echo b > b
590 $ hg commit -Am base
590 $ hg commit -Am base
591 adding a
591 adding a
592 adding b
592 adding b
593
593
594
594
595 with --force
595 with --force
596
596
597 $ hg mv b --force a
597 $ hg mv b --force a
598 $ hg st --copies
598 $ hg st --copies
599 M a
599 M a
600 b
600 b
601 R b
601 R b
602 $ hg revert --all
602 $ hg revert --all
603 reverting a
603 reverting a
604 undeleting b
604 undeleting b
605 $ rm *.orig
605 $ rm *.orig
606
606
607 without force
607 without force
608
608
609 $ hg rm a
609 $ hg rm a
610 $ hg st --copies
610 $ hg st --copies
611 R a
611 R a
612 $ hg mv b a
612 $ hg mv b a
613 $ hg st --copies
613 $ hg st --copies
614 M a
614 M a
615 b
615 b
616 R b
616 R b
617
617
618 using ui.statuscopies setting
618 using ui.statuscopies setting
619 $ hg st --config ui.statuscopies=true
619 $ hg st --config ui.statuscopies=true
620 M a
620 M a
621 b
621 b
622 R b
622 R b
623 $ hg st --config ui.statuscopies=false
623 $ hg st --config ui.statuscopies=false
624 M a
624 M a
625 R b
625 R b
626 $ hg st --config ui.tweakdefaults=yes
626 $ hg st --config ui.tweakdefaults=yes
627 M a
627 M a
628 b
628 b
629 R b
629 R b
630
630
631 using log status template (issue5155)
631 using log status template (issue5155)
632 $ hg log -Tstatus -r 'wdir()' -C
632 $ hg log -Tstatus -r 'wdir()' -C
633 changeset: 2147483647:ffffffffffff
633 changeset: 2147483647:ffffffffffff
634 parent: 0:8c55c58b4c0e
634 parent: 0:8c55c58b4c0e
635 user: test
635 user: test
636 date: * (glob)
636 date: * (glob)
637 files:
637 files:
638 M a
638 M a
639 b
639 b
640 R b
640 R b
641
641
642 $ hg log -GTstatus -r 'wdir()' -C
642 $ hg log -GTstatus -r 'wdir()' -C
643 o changeset: 2147483647:ffffffffffff
643 o changeset: 2147483647:ffffffffffff
644 | parent: 0:8c55c58b4c0e
644 | parent: 0:8c55c58b4c0e
645 ~ user: test
645 ~ user: test
646 date: * (glob)
646 date: * (glob)
647 files:
647 files:
648 M a
648 M a
649 b
649 b
650 R b
650 R b
651
651
652
652
653 Other "bug" highlight, the revision status does not report the copy information.
653 Other "bug" highlight, the revision status does not report the copy information.
654 This is buggy behavior.
654 This is buggy behavior.
655
655
656 $ hg commit -m 'blah'
656 $ hg commit -m 'blah'
657 $ hg st --copies --change .
657 $ hg st --copies --change .
658 M a
658 M a
659 R b
659 R b
660
660
661 using log status template, the copy information is displayed correctly.
661 using log status template, the copy information is displayed correctly.
662 $ hg log -Tstatus -r. -C
662 $ hg log -Tstatus -r. -C
663 changeset: 1:6685fde43d21
663 changeset: 1:6685fde43d21
664 tag: tip
664 tag: tip
665 user: test
665 user: test
666 date: * (glob)
666 date: * (glob)
667 summary: blah
667 summary: blah
668 files:
668 files:
669 M a
669 M a
670 b
670 b
671 R b
671 R b
672
672
673
673
674 $ cd ..
674 $ cd ..
675
676 Make sure .hg doesn't show up even as a symlink
677
678 $ hg init repo0
679 $ mkdir symlink-repo0
680 $ cd symlink-repo0
681 $ ln -s ../repo0/.hg
682 $ hg status
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
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