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

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

@@ -1,135 +1,137 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 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
@@ -1,148 +1,150 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
150 eab27446995210c334c3d06f1a659e3b9b5da769 4.0
@@ -1,657 +1,671 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # check-code - a style and portability checker for Mercurial
3 # check-code - a style and portability checker for Mercurial
4 #
4 #
5 # Copyright 2010 Matt Mackall <mpm@selenic.com>
5 # Copyright 2010 Matt Mackall <mpm@selenic.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 """style and portability checker for Mercurial
10 """style and portability checker for Mercurial
11
11
12 when a rule triggers wrong, do one of the following (prefer one from top):
12 when a rule triggers wrong, do one of the following (prefer one from top):
13 * do the work-around the rule suggests
13 * do the work-around the rule suggests
14 * doublecheck that it is a false match
14 * doublecheck that it is a false match
15 * improve the rule pattern
15 * improve the rule pattern
16 * add an ignore pattern to the rule (3rd arg) which matches your good line
16 * add an ignore pattern to the rule (3rd arg) which matches your good line
17 (you can append a short comment and match this, like: #re-raises)
17 (you can append a short comment and match this, like: #re-raises)
18 * change the pattern to a warning and list the exception in test-check-code-hg
18 * change the pattern to a warning and list the exception in test-check-code-hg
19 * ONLY use no--check-code for skipping entire files from external sources
19 * ONLY use no--check-code for skipping entire files from external sources
20 """
20 """
21
21
22 from __future__ import absolute_import, print_function
22 from __future__ import absolute_import, print_function
23 import glob
23 import glob
24 import keyword
24 import keyword
25 import optparse
25 import optparse
26 import os
26 import os
27 import re
27 import re
28 import sys
28 import sys
29 if sys.version_info[0] < 3:
29 if sys.version_info[0] < 3:
30 opentext = open
30 opentext = open
31 else:
31 else:
32 def opentext(f):
32 def opentext(f):
33 return open(f, encoding='ascii')
33 return open(f, encoding='ascii')
34 try:
34 try:
35 xrange
35 xrange
36 except NameError:
36 except NameError:
37 xrange = range
37 xrange = range
38 try:
38 try:
39 import re2
39 import re2
40 except ImportError:
40 except ImportError:
41 re2 = None
41 re2 = None
42
42
43 def compilere(pat, multiline=False):
43 def compilere(pat, multiline=False):
44 if multiline:
44 if multiline:
45 pat = '(?m)' + pat
45 pat = '(?m)' + pat
46 if re2:
46 if re2:
47 try:
47 try:
48 return re2.compile(pat)
48 return re2.compile(pat)
49 except re2.error:
49 except re2.error:
50 pass
50 pass
51 return re.compile(pat)
51 return re.compile(pat)
52
52
53 # check "rules depending on implementation of repquote()" in each
53 # check "rules depending on implementation of repquote()" in each
54 # patterns (especially pypats), before changing around repquote()
54 # patterns (especially pypats), before changing around repquote()
55 _repquotefixedmap = {' ': ' ', '\n': '\n', '.': 'p', ':': 'q',
55 _repquotefixedmap = {' ': ' ', '\n': '\n', '.': 'p', ':': 'q',
56 '%': '%', '\\': 'b', '*': 'A', '+': 'P', '-': 'M'}
56 '%': '%', '\\': 'b', '*': 'A', '+': 'P', '-': 'M'}
57 def _repquoteencodechr(i):
57 def _repquoteencodechr(i):
58 if i > 255:
58 if i > 255:
59 return 'u'
59 return 'u'
60 c = chr(i)
60 c = chr(i)
61 if c in _repquotefixedmap:
61 if c in _repquotefixedmap:
62 return _repquotefixedmap[c]
62 return _repquotefixedmap[c]
63 if c.isalpha():
63 if c.isalpha():
64 return 'x'
64 return 'x'
65 if c.isdigit():
65 if c.isdigit():
66 return 'n'
66 return 'n'
67 return 'o'
67 return 'o'
68 _repquotett = ''.join(_repquoteencodechr(i) for i in xrange(256))
68 _repquotett = ''.join(_repquoteencodechr(i) for i in xrange(256))
69
69
70 def repquote(m):
70 def repquote(m):
71 t = m.group('text')
71 t = m.group('text')
72 t = t.translate(_repquotett)
72 t = t.translate(_repquotett)
73 return m.group('quote') + t + m.group('quote')
73 return m.group('quote') + t + m.group('quote')
74
74
75 def reppython(m):
75 def reppython(m):
76 comment = m.group('comment')
76 comment = m.group('comment')
77 if comment:
77 if comment:
78 l = len(comment.rstrip())
78 l = len(comment.rstrip())
79 return "#" * l + comment[l:]
79 return "#" * l + comment[l:]
80 return repquote(m)
80 return repquote(m)
81
81
82 def repcomment(m):
82 def repcomment(m):
83 return m.group(1) + "#" * len(m.group(2))
83 return m.group(1) + "#" * len(m.group(2))
84
84
85 def repccomment(m):
85 def repccomment(m):
86 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
86 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
87 return m.group(1) + t + "*/"
87 return m.group(1) + t + "*/"
88
88
89 def repcallspaces(m):
89 def repcallspaces(m):
90 t = re.sub(r"\n\s+", "\n", m.group(2))
90 t = re.sub(r"\n\s+", "\n", m.group(2))
91 return m.group(1) + t
91 return m.group(1) + t
92
92
93 def repinclude(m):
93 def repinclude(m):
94 return m.group(1) + "<foo>"
94 return m.group(1) + "<foo>"
95
95
96 def rephere(m):
96 def rephere(m):
97 t = re.sub(r"\S", "x", m.group(2))
97 t = re.sub(r"\S", "x", m.group(2))
98 return m.group(1) + t
98 return m.group(1) + t
99
99
100
100
101 testpats = [
101 testpats = [
102 [
102 [
103 (r'pushd|popd', "don't use 'pushd' or 'popd', use 'cd'"),
103 (r'pushd|popd', "don't use 'pushd' or 'popd', use 'cd'"),
104 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"),
104 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"),
105 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
105 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
106 (r'(?<!hg )grep.* -a', "don't use 'grep -a', use in-line python"),
106 (r'(?<!hg )grep.* -a', "don't use 'grep -a', use in-line python"),
107 (r'sed.*-i', "don't use 'sed -i', use a temporary file"),
107 (r'sed.*-i', "don't use 'sed -i', use a temporary file"),
108 (r'\becho\b.*\\n', "don't use 'echo \\n', use printf"),
108 (r'\becho\b.*\\n', "don't use 'echo \\n', use printf"),
109 (r'echo -n', "don't use 'echo -n', use printf"),
109 (r'echo -n', "don't use 'echo -n', use printf"),
110 (r'(^|\|\s*)\bwc\b[^|]*$\n(?!.*\(re\))', "filter wc output"),
110 (r'(^|\|\s*)\bwc\b[^|]*$\n(?!.*\(re\))', "filter wc output"),
111 (r'head -c', "don't use 'head -c', use 'dd'"),
111 (r'head -c', "don't use 'head -c', use 'dd'"),
112 (r'tail -n', "don't use the '-n' option to tail, just use '-<num>'"),
112 (r'tail -n', "don't use the '-n' option to tail, just use '-<num>'"),
113 (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"),
113 (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"),
114 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
114 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
115 (r'printf.*[^\\]\\([1-9]|0\d)', r"don't use 'printf \NNN', use Python"),
115 (r'printf.*[^\\]\\([1-9]|0\d)', r"don't use 'printf \NNN', use Python"),
116 (r'printf.*[^\\]\\x', "don't use printf \\x, use Python"),
116 (r'printf.*[^\\]\\x', "don't use printf \\x, use Python"),
117 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
117 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
118 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
118 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
119 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
119 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
120 "use egrep for extended grep syntax"),
120 "use egrep for extended grep syntax"),
121 (r'/bin/', "don't use explicit paths for tools"),
121 (r'/bin/', "don't use explicit paths for tools"),
122 (r'[^\n]\Z', "no trailing newline"),
122 (r'[^\n]\Z', "no trailing newline"),
123 (r'export .*=', "don't export and assign at once"),
123 (r'export .*=', "don't export and assign at once"),
124 (r'^source\b', "don't use 'source', use '.'"),
124 (r'^source\b', "don't use 'source', use '.'"),
125 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
125 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
126 (r'\bls +[^|\n-]+ +-', "options to 'ls' must come before filenames"),
126 (r'\bls +[^|\n-]+ +-', "options to 'ls' must come before filenames"),
127 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
127 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
128 (r'^stop\(\)', "don't use 'stop' as a shell function name"),
128 (r'^stop\(\)', "don't use 'stop' as a shell function name"),
129 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"),
129 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"),
130 (r'\[\[\s+[^\]]*\]\]', "don't use '[[ ]]', use '[ ]'"),
130 (r'\[\[\s+[^\]]*\]\]', "don't use '[[ ]]', use '[ ]'"),
131 (r'^alias\b.*=', "don't use alias, use a function"),
131 (r'^alias\b.*=', "don't use alias, use a function"),
132 (r'if\s*!', "don't use '!' to negate exit status"),
132 (r'if\s*!', "don't use '!' to negate exit status"),
133 (r'/dev/u?random', "don't use entropy, use /dev/zero"),
133 (r'/dev/u?random', "don't use entropy, use /dev/zero"),
134 (r'do\s*true;\s*done', "don't use true as loop body, use sleep 0"),
134 (r'do\s*true;\s*done', "don't use true as loop body, use sleep 0"),
135 (r'^( *)\t', "don't use tabs to indent"),
135 (r'^( *)\t', "don't use tabs to indent"),
136 (r'sed (-e )?\'(\d+|/[^/]*/)i(?!\\\n)',
136 (r'sed (-e )?\'(\d+|/[^/]*/)i(?!\\\n)',
137 "put a backslash-escaped newline after sed 'i' command"),
137 "put a backslash-escaped newline after sed 'i' command"),
138 (r'^diff *-\w*[uU].*$\n(^ \$ |^$)', "prefix diff -u/-U with cmp"),
138 (r'^diff *-\w*[uU].*$\n(^ \$ |^$)', "prefix diff -u/-U with cmp"),
139 (r'^\s+(if)? diff *-\w*[uU]', "prefix diff -u/-U with cmp"),
139 (r'^\s+(if)? diff *-\w*[uU]', "prefix diff -u/-U with cmp"),
140 (r'seq ', "don't use 'seq', use $TESTDIR/seq.py"),
140 (r'seq ', "don't use 'seq', use $TESTDIR/seq.py"),
141 (r'\butil\.Abort\b', "directly use error.Abort"),
141 (r'\butil\.Abort\b', "directly use error.Abort"),
142 (r'\|&', "don't use |&, use 2>&1"),
142 (r'\|&', "don't use |&, use 2>&1"),
143 (r'\w = +\w', "only one space after = allowed"),
143 (r'\w = +\w', "only one space after = allowed"),
144 (r'\bsed\b.*[^\\]\\n', "don't use 'sed ... \\n', use a \\ and a newline"),
144 (r'\bsed\b.*[^\\]\\n', "don't use 'sed ... \\n', use a \\ and a newline"),
145 (r'env.*-u', "don't use 'env -u VAR', use 'unset VAR'")
145 (r'env.*-u', "don't use 'env -u VAR', use 'unset VAR'")
146 ],
146 ],
147 # warnings
147 # warnings
148 [
148 [
149 (r'^function', "don't use 'function', use old style"),
149 (r'^function', "don't use 'function', use old style"),
150 (r'^diff.*-\w*N', "don't use 'diff -N'"),
150 (r'^diff.*-\w*N', "don't use 'diff -N'"),
151 (r'\$PWD|\${PWD}', "don't use $PWD, use `pwd`"),
151 (r'\$PWD|\${PWD}', "don't use $PWD, use `pwd`"),
152 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"),
152 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"),
153 (r'kill (`|\$\()', "don't use kill, use killdaemons.py")
153 (r'kill (`|\$\()', "don't use kill, use killdaemons.py")
154 ]
154 ]
155 ]
155 ]
156
156
157 testfilters = [
157 testfilters = [
158 (r"( *)(#([^\n]*\S)?)", repcomment),
158 (r"( *)(#([^\n]*\S)?)", repcomment),
159 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
159 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
160 ]
160 ]
161
161
162 winglobmsg = "use (glob) to match Windows paths too"
162 winglobmsg = "use (glob) to match Windows paths too"
163 uprefix = r"^ \$ "
163 uprefix = r"^ \$ "
164 utestpats = [
164 utestpats = [
165 [
165 [
166 (r'^(\S.*|| [$>] \S.*)[ \t]\n', "trailing whitespace on non-output"),
166 (r'^(\S.*|| [$>] \S.*)[ \t]\n', "trailing whitespace on non-output"),
167 (uprefix + r'.*\|\s*sed[^|>\n]*\n',
167 (uprefix + r'.*\|\s*sed[^|>\n]*\n',
168 "use regex test output patterns instead of sed"),
168 "use regex test output patterns instead of sed"),
169 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
169 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
170 (uprefix + r'.*(?<!\[)\$\?', "explicit exit code checks unnecessary"),
170 (uprefix + r'.*(?<!\[)\$\?', "explicit exit code checks unnecessary"),
171 (uprefix + r'.*\|\| echo.*(fail|error)',
171 (uprefix + r'.*\|\| echo.*(fail|error)',
172 "explicit exit code checks unnecessary"),
172 "explicit exit code checks unnecessary"),
173 (uprefix + r'set -e', "don't use set -e"),
173 (uprefix + r'set -e', "don't use set -e"),
174 (uprefix + r'(\s|fi\b|done\b)', "use > for continued lines"),
174 (uprefix + r'(\s|fi\b|done\b)', "use > for continued lines"),
175 (uprefix + r'.*:\.\S*/', "x:.y in a path does not work on msys, rewrite "
175 (uprefix + r'.*:\.\S*/', "x:.y in a path does not work on msys, rewrite "
176 "as x://.y, or see `hg log -k msys` for alternatives", r'-\S+:\.|' #-Rxxx
176 "as x://.y, or see `hg log -k msys` for alternatives", r'-\S+:\.|' #-Rxxx
177 '# no-msys'), # in test-pull.t which is skipped on windows
177 '# no-msys'), # in test-pull.t which is skipped on windows
178 (r'^ saved backup bundle to \$TESTTMP.*\.hg$', winglobmsg),
178 (r'^ saved backup bundle to \$TESTTMP.*\.hg$', winglobmsg),
179 (r'^ changeset .* references (corrupted|missing) \$TESTTMP/.*[^)]$',
179 (r'^ changeset .* references (corrupted|missing) \$TESTTMP/.*[^)]$',
180 winglobmsg),
180 winglobmsg),
181 (r'^ pulling from \$TESTTMP/.*[^)]$', winglobmsg,
181 (r'^ pulling from \$TESTTMP/.*[^)]$', winglobmsg,
182 '\$TESTTMP/unix-repo$'), # in test-issue1802.t which skipped on windows
182 '\$TESTTMP/unix-repo$'), # in test-issue1802.t which skipped on windows
183 (r'^ reverting (?!subrepo ).*/.*[^)]$', winglobmsg),
183 (r'^ reverting (?!subrepo ).*/.*[^)]$', winglobmsg),
184 (r'^ cloning subrepo \S+/.*[^)]$', winglobmsg),
184 (r'^ cloning subrepo \S+/.*[^)]$', winglobmsg),
185 (r'^ pushing to \$TESTTMP/.*[^)]$', winglobmsg),
185 (r'^ pushing to \$TESTTMP/.*[^)]$', winglobmsg),
186 (r'^ pushing subrepo \S+/\S+ to.*[^)]$', winglobmsg),
186 (r'^ pushing subrepo \S+/\S+ to.*[^)]$', winglobmsg),
187 (r'^ moving \S+/.*[^)]$', winglobmsg),
187 (r'^ moving \S+/.*[^)]$', winglobmsg),
188 (r'^ no changes made to subrepo since.*/.*[^)]$', winglobmsg),
188 (r'^ no changes made to subrepo since.*/.*[^)]$', winglobmsg),
189 (r'^ .*: largefile \S+ not available from file:.*/.*[^)]$', winglobmsg),
189 (r'^ .*: largefile \S+ not available from file:.*/.*[^)]$', winglobmsg),
190 (r'^ .*file://\$TESTTMP',
190 (r'^ .*file://\$TESTTMP',
191 'write "file:/*/$TESTTMP" + (glob) to match on windows too'),
191 'write "file:/*/$TESTTMP" + (glob) to match on windows too'),
192 (r'^ [^$>].*27\.0\.0\.1.*[^)]$',
192 (r'^ [^$>].*27\.0\.0\.1.*[^)]$',
193 'use (glob) to match localhost IP on hosts without 127.0.0.1 too'),
193 'use (glob) to match localhost IP on hosts without 127.0.0.1 too'),
194 (r'^ (cat|find): .*: No such file or directory',
194 (r'^ (cat|find): .*: No such file or directory',
195 'use test -f to test for file existence'),
195 'use test -f to test for file existence'),
196 (r'^ diff -[^ -]*p',
196 (r'^ diff -[^ -]*p',
197 "don't use (external) diff with -p for portability"),
197 "don't use (external) diff with -p for portability"),
198 (r'^ [-+][-+][-+] .* [-+]0000 \(glob\)',
198 (r'^ [-+][-+][-+] .* [-+]0000 \(glob\)',
199 "glob timezone field in diff output for portability"),
199 "glob timezone field in diff output for portability"),
200 (r'^ @@ -[0-9]+ [+][0-9]+,[0-9]+ @@',
200 (r'^ @@ -[0-9]+ [+][0-9]+,[0-9]+ @@',
201 "use '@@ -N* +N,n @@ (glob)' style chunk header for portability"),
201 "use '@@ -N* +N,n @@ (glob)' style chunk header for portability"),
202 (r'^ @@ -[0-9]+,[0-9]+ [+][0-9]+ @@',
202 (r'^ @@ -[0-9]+,[0-9]+ [+][0-9]+ @@',
203 "use '@@ -N,n +N* @@ (glob)' style chunk header for portability"),
203 "use '@@ -N,n +N* @@ (glob)' style chunk header for portability"),
204 (r'^ @@ -[0-9]+ [+][0-9]+ @@',
204 (r'^ @@ -[0-9]+ [+][0-9]+ @@',
205 "use '@@ -N* +N* @@ (glob)' style chunk header for portability"),
205 "use '@@ -N* +N* @@ (glob)' style chunk header for portability"),
206 (uprefix + r'hg( +-[^ ]+( +[^ ]+)?)* +extdiff'
206 (uprefix + r'hg( +-[^ ]+( +[^ ]+)?)* +extdiff'
207 r'( +(-[^ po-]+|--(?!program|option)[^ ]+|[^-][^ ]*))*$',
207 r'( +(-[^ po-]+|--(?!program|option)[^ ]+|[^-][^ ]*))*$',
208 "use $RUNTESTDIR/pdiff via extdiff (or -o/-p for false-positives)"),
208 "use $RUNTESTDIR/pdiff via extdiff (or -o/-p for false-positives)"),
209 ],
209 ],
210 # warnings
210 # warnings
211 [
211 [
212 (r'^ (?!.*127\.0\.0\.1)[^*?/\n]* \(glob\)$',
212 (r'^ (?!.*127\.0\.0\.1)[^*?/\n]* \(glob\)$',
213 "glob match with no glob string (?, *, /, and 127.0.0.1)"),
213 "glob match with no glob string (?, *, /, and 127.0.0.1)"),
214 ]
214 ]
215 ]
215 ]
216
216
217 for i in [0, 1]:
217 for i in [0, 1]:
218 for tp in testpats[i]:
218 for tp in testpats[i]:
219 p = tp[0]
219 p = tp[0]
220 m = tp[1]
220 m = tp[1]
221 if p.startswith(r'^'):
221 if p.startswith(r'^'):
222 p = r"^ [$>] (%s)" % p[1:]
222 p = r"^ [$>] (%s)" % p[1:]
223 else:
223 else:
224 p = r"^ [$>] .*(%s)" % p
224 p = r"^ [$>] .*(%s)" % p
225 utestpats[i].append((p, m) + tp[2:])
225 utestpats[i].append((p, m) + tp[2:])
226
226
227 utestfilters = [
227 utestfilters = [
228 (r"<<(\S+)((.|\n)*?\n > \1)", rephere),
228 (r"<<(\S+)((.|\n)*?\n > \1)", rephere),
229 (r"( +)(#([^\n]*\S)?)", repcomment),
229 (r"( +)(#([^\n]*\S)?)", repcomment),
230 ]
230 ]
231
231
232 pypats = [
232 pypats = [
233 [
233 [
234 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
234 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
235 "tuple parameter unpacking not available in Python 3+"),
235 "tuple parameter unpacking not available in Python 3+"),
236 (r'lambda\s*\(.*,.*\)',
236 (r'lambda\s*\(.*,.*\)',
237 "tuple parameter unpacking not available in Python 3+"),
237 "tuple parameter unpacking not available in Python 3+"),
238 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
238 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
239 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
239 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
240 (r'\bdict\(.*=', 'dict() is different in Py2 and 3 and is slower than {}',
240 (r'\bdict\(.*=', 'dict() is different in Py2 and 3 and is slower than {}',
241 'dict-from-generator'),
241 'dict-from-generator'),
242 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
242 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
243 (r'\s<>\s', '<> operator is not available in Python 3+, use !='),
243 (r'\s<>\s', '<> operator is not available in Python 3+, use !='),
244 (r'^\s*\t', "don't use tabs"),
244 (r'^\s*\t', "don't use tabs"),
245 (r'\S;\s*\n', "semicolon"),
245 (r'\S;\s*\n', "semicolon"),
246 (r'[^_]_\([ \t\n]*(?:"[^"]+"[ \t\n+]*)+%', "don't use % inside _()"),
246 (r'[^_]_\([ \t\n]*(?:"[^"]+"[ \t\n+]*)+%', "don't use % inside _()"),
247 (r"[^_]_\([ \t\n]*(?:'[^']+'[ \t\n+]*)+%", "don't use % inside _()"),
247 (r"[^_]_\([ \t\n]*(?:'[^']+'[ \t\n+]*)+%", "don't use % inside _()"),
248 (r'(\w|\)),\w', "missing whitespace after ,"),
248 (r'(\w|\)),\w', "missing whitespace after ,"),
249 (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"),
249 (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"),
250 (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"),
250 (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"),
251 (r'\w\s=\s\s+\w', "gratuitous whitespace after ="),
251 (r'\w\s=\s\s+\w', "gratuitous whitespace after ="),
252 (r'.{81}', "line too long"),
252 (r'.{81}', "line too long"),
253 (r'[^\n]\Z', "no trailing newline"),
253 (r'[^\n]\Z', "no trailing newline"),
254 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
254 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
255 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=',
255 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=',
256 # "don't use underbars in identifiers"),
256 # "don't use underbars in identifiers"),
257 (r'^\s+(self\.)?[A-za-z][a-z0-9]+[A-Z]\w* = ',
257 (r'^\s+(self\.)?[A-za-z][a-z0-9]+[A-Z]\w* = ',
258 "don't use camelcase in identifiers"),
258 "don't use camelcase in identifiers"),
259 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+',
259 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+',
260 "linebreak after :"),
260 "linebreak after :"),
261 (r'class\s[^( \n]+:', "old-style class, use class foo(object)",
261 (r'class\s[^( \n]+:', "old-style class, use class foo(object)",
262 r'#.*old-style'),
262 r'#.*old-style'),
263 (r'class\s[^( \n]+\(\):',
263 (r'class\s[^( \n]+\(\):',
264 "class foo() creates old style object, use class foo(object)",
264 "class foo() creates old style object, use class foo(object)",
265 r'#.*old-style'),
265 r'#.*old-style'),
266 (r'\b(%s)\(' % '|'.join(k for k in keyword.kwlist
266 (r'\b(%s)\(' % '|'.join(k for k in keyword.kwlist
267 if k not in ('print', 'exec')),
267 if k not in ('print', 'exec')),
268 "Python keyword is not a function"),
268 "Python keyword is not a function"),
269 (r',]', "unneeded trailing ',' in list"),
269 (r',]', "unneeded trailing ',' in list"),
270 # (r'class\s[A-Z][^\(]*\((?!Exception)',
270 # (r'class\s[A-Z][^\(]*\((?!Exception)',
271 # "don't capitalize non-exception classes"),
271 # "don't capitalize non-exception classes"),
272 # (r'in range\(', "use xrange"),
272 # (r'in range\(', "use xrange"),
273 # (r'^\s*print\s+', "avoid using print in core and extensions"),
273 # (r'^\s*print\s+', "avoid using print in core and extensions"),
274 (r'[\x80-\xff]', "non-ASCII character literal"),
274 (r'[\x80-\xff]', "non-ASCII character literal"),
275 (r'("\')\.format\(', "str.format() has no bytes counterpart, use %"),
275 (r'("\')\.format\(', "str.format() has no bytes counterpart, use %"),
276 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
276 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
277 "gratuitous whitespace after Python keyword"),
277 "gratuitous whitespace after Python keyword"),
278 (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"),
278 (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"),
279 # (r'\s\s=', "gratuitous whitespace before ="),
279 # (r'\s\s=', "gratuitous whitespace before ="),
280 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
280 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
281 "missing whitespace around operator"),
281 "missing whitespace around operator"),
282 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\s',
282 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\s',
283 "missing whitespace around operator"),
283 "missing whitespace around operator"),
284 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
284 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
285 "missing whitespace around operator"),
285 "missing whitespace around operator"),
286 (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]',
286 (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]',
287 "wrong whitespace around ="),
287 "wrong whitespace around ="),
288 (r'\([^()]*( =[^=]|[^<>!=]= )',
288 (r'\([^()]*( =[^=]|[^<>!=]= )',
289 "no whitespace around = for named parameters"),
289 "no whitespace around = for named parameters"),
290 (r'raise Exception', "don't raise generic exceptions"),
290 (r'raise Exception', "don't raise generic exceptions"),
291 (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$',
291 (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$',
292 "don't use old-style two-argument raise, use Exception(message)"),
292 "don't use old-style two-argument raise, use Exception(message)"),
293 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
293 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
294 (r' [=!]=\s+(True|False|None)',
294 (r' [=!]=\s+(True|False|None)',
295 "comparison with singleton, use 'is' or 'is not' instead"),
295 "comparison with singleton, use 'is' or 'is not' instead"),
296 (r'^\s*(while|if) [01]:',
296 (r'^\s*(while|if) [01]:',
297 "use True/False for constant Boolean expression"),
297 "use True/False for constant Boolean expression"),
298 (r'(?:(?<!def)\s+|\()hasattr\(',
298 (r'(?:(?<!def)\s+|\()hasattr\(',
299 'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
299 'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
300 (r'opener\([^)]*\).read\(',
300 (r'opener\([^)]*\).read\(',
301 "use opener.read() instead"),
301 "use opener.read() instead"),
302 (r'opener\([^)]*\).write\(',
302 (r'opener\([^)]*\).write\(',
303 "use opener.write() instead"),
303 "use opener.write() instead"),
304 (r'[\s\(](open|file)\([^)]*\)\.read\(',
304 (r'[\s\(](open|file)\([^)]*\)\.read\(',
305 "use util.readfile() instead"),
305 "use util.readfile() instead"),
306 (r'[\s\(](open|file)\([^)]*\)\.write\(',
306 (r'[\s\(](open|file)\([^)]*\)\.write\(',
307 "use util.writefile() instead"),
307 "use util.writefile() instead"),
308 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
308 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
309 "always assign an opened file to a variable, and close it afterwards"),
309 "always assign an opened file to a variable, and close it afterwards"),
310 (r'[\s\(](open|file)\([^)]*\)\.',
310 (r'[\s\(](open|file)\([^)]*\)\.',
311 "always assign an opened file to a variable, and close it afterwards"),
311 "always assign an opened file to a variable, and close it afterwards"),
312 (r'(?i)descend[e]nt', "the proper spelling is descendAnt"),
312 (r'(?i)descend[e]nt', "the proper spelling is descendAnt"),
313 (r'\.debug\(\_', "don't mark debug messages for translation"),
313 (r'\.debug\(\_', "don't mark debug messages for translation"),
314 (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"),
314 (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"),
315 (r'^\s*except\s*:', "naked except clause", r'#.*re-raises'),
315 (r'^\s*except\s*:', "naked except clause", r'#.*re-raises'),
316 (r'^\s*except\s([^\(,]+|\([^\)]+\))\s*,',
316 (r'^\s*except\s([^\(,]+|\([^\)]+\))\s*,',
317 'legacy exception syntax; use "as" instead of ","'),
317 'legacy exception syntax; use "as" instead of ","'),
318 (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"),
318 (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"),
319 (r'release\(.*wlock, .*lock\)', "wrong lock release order"),
319 (r'release\(.*wlock, .*lock\)', "wrong lock release order"),
320 (r'\b__bool__\b', "__bool__ should be __nonzero__ in Python 2"),
320 (r'\b__bool__\b', "__bool__ should be __nonzero__ in Python 2"),
321 (r'os\.path\.join\(.*, *(""|\'\')\)',
321 (r'os\.path\.join\(.*, *(""|\'\')\)',
322 "use pathutil.normasprefix(path) instead of os.path.join(path, '')"),
322 "use pathutil.normasprefix(path) instead of os.path.join(path, '')"),
323 (r'\s0[0-7]+\b', 'legacy octal syntax; use "0o" prefix instead of "0"'),
323 (r'\s0[0-7]+\b', 'legacy octal syntax; use "0o" prefix instead of "0"'),
324 # XXX only catch mutable arguments on the first line of the definition
324 # XXX only catch mutable arguments on the first line of the definition
325 (r'def.*[( ]\w+=\{\}', "don't use mutable default arguments"),
325 (r'def.*[( ]\w+=\{\}', "don't use mutable default arguments"),
326 (r'\butil\.Abort\b', "directly use error.Abort"),
326 (r'\butil\.Abort\b', "directly use error.Abort"),
327 (r'^import Queue', "don't use Queue, use util.queue + util.empty"),
327 (r'^import Queue', "don't use Queue, use util.queue + util.empty"),
328 (r'^import cStringIO', "don't use cStringIO.StringIO, use util.stringio"),
328 (r'^import cStringIO', "don't use cStringIO.StringIO, use util.stringio"),
329 (r'^import urllib', "don't use urllib, use util.urlreq/util.urlerr"),
329 (r'^import urllib', "don't use urllib, use util.urlreq/util.urlerr"),
330 (r'^import SocketServer', "don't use SockerServer, use util.socketserver"),
330 (r'^import SocketServer', "don't use SockerServer, use util.socketserver"),
331 (r'^import urlparse', "don't use urlparse, use util.urlparse"),
331 (r'^import urlparse', "don't use urlparse, use util.urlparse"),
332 (r'^import xmlrpclib', "don't use xmlrpclib, use util.xmlrpclib"),
332 (r'^import xmlrpclib', "don't use xmlrpclib, use util.xmlrpclib"),
333 (r'^import cPickle', "don't use cPickle, use util.pickle"),
333 (r'^import cPickle', "don't use cPickle, use util.pickle"),
334 (r'^import pickle', "don't use pickle, use util.pickle"),
334 (r'^import pickle', "don't use pickle, use util.pickle"),
335 (r'^import httplib', "don't use httplib, use util.httplib"),
335 (r'^import httplib', "don't use httplib, use util.httplib"),
336 (r'^import BaseHTTPServer', "use util.httpserver instead"),
336 (r'^import BaseHTTPServer', "use util.httpserver instead"),
337 (r'\.next\(\)', "don't use .next(), use next(...)"),
337 (r'\.next\(\)', "don't use .next(), use next(...)"),
338
338
339 # rules depending on implementation of repquote()
339 # rules depending on implementation of repquote()
340 (r' x+[xpqo%APM][\'"]\n\s+[\'"]x',
340 (r' x+[xpqo%APM][\'"]\n\s+[\'"]x',
341 'string join across lines with no space'),
341 'string join across lines with no space'),
342 (r'''(?x)ui\.(status|progress|write|note|warn)\(
342 (r'''(?x)ui\.(status|progress|write|note|warn)\(
343 [ \t\n#]*
343 [ \t\n#]*
344 (?# any strings/comments might precede a string, which
344 (?# any strings/comments might precede a string, which
345 # contains translatable message)
345 # contains translatable message)
346 ((['"]|\'\'\'|""")[ \npq%bAPMxno]*(['"]|\'\'\'|""")[ \t\n#]+)*
346 ((['"]|\'\'\'|""")[ \npq%bAPMxno]*(['"]|\'\'\'|""")[ \t\n#]+)*
347 (?# sequence consisting of below might precede translatable message
347 (?# sequence consisting of below might precede translatable message
348 # - formatting string: "% 10s", "%05d", "% -3.2f", "%*s", "%%" ...
348 # - formatting string: "% 10s", "%05d", "% -3.2f", "%*s", "%%" ...
349 # - escaped character: "\\", "\n", "\0" ...
349 # - escaped character: "\\", "\n", "\0" ...
350 # - character other than '%', 'b' as '\', and 'x' as alphabet)
350 # - character other than '%', 'b' as '\', and 'x' as alphabet)
351 (['"]|\'\'\'|""")
351 (['"]|\'\'\'|""")
352 ((%([ n]?[PM]?([np]+|A))?x)|%%|b[bnx]|[ \nnpqAPMo])*x
352 ((%([ n]?[PM]?([np]+|A))?x)|%%|b[bnx]|[ \nnpqAPMo])*x
353 (?# this regexp can't use [^...] style,
353 (?# this regexp can't use [^...] style,
354 # because _preparepats forcibly adds "\n" into [^...],
354 # because _preparepats forcibly adds "\n" into [^...],
355 # even though this regexp wants match it against "\n")''',
355 # even though this regexp wants match it against "\n")''',
356 "missing _() in ui message (use () to hide false-positives)"),
356 "missing _() in ui message (use () to hide false-positives)"),
357 ],
357 ],
358 # warnings
358 # warnings
359 [
359 [
360 # rules depending on implementation of repquote()
360 # rules depending on implementation of repquote()
361 (r'(^| )pp +xxxxqq[ \n][^\n]', "add two newlines after '.. note::'"),
361 (r'(^| )pp +xxxxqq[ \n][^\n]', "add two newlines after '.. note::'"),
362 ]
362 ]
363 ]
363 ]
364
364
365 pyfilters = [
365 pyfilters = [
366 (r"""(?msx)(?P<comment>\#.*?$)|
366 (r"""(?msx)(?P<comment>\#.*?$)|
367 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
367 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
368 (?P<text>(([^\\]|\\.)*?))
368 (?P<text>(([^\\]|\\.)*?))
369 (?P=quote))""", reppython),
369 (?P=quote))""", reppython),
370 ]
370 ]
371
371
372 txtfilters = []
372 txtfilters = []
373
373
374 txtpats = [
374 txtpats = [
375 [
375 [
376 ('\s$', 'trailing whitespace'),
376 ('\s$', 'trailing whitespace'),
377 ('.. note::[ \n][^\n]', 'add two newlines after note::')
377 ('.. note::[ \n][^\n]', 'add two newlines after note::')
378 ],
378 ],
379 []
379 []
380 ]
380 ]
381
381
382 cpats = [
382 cpats = [
383 [
383 [
384 (r'//', "don't use //-style comments"),
384 (r'//', "don't use //-style comments"),
385 (r'^ ', "don't use spaces to indent"),
385 (r'^ ', "don't use spaces to indent"),
386 (r'\S\t', "don't use tabs except for indent"),
386 (r'\S\t', "don't use tabs except for indent"),
387 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
387 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
388 (r'.{81}', "line too long"),
388 (r'.{81}', "line too long"),
389 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
389 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
390 (r'return\(', "return is not a function"),
390 (r'return\(', "return is not a function"),
391 (r' ;', "no space before ;"),
391 (r' ;', "no space before ;"),
392 (r'[^;] \)', "no space before )"),
392 (r'[^;] \)', "no space before )"),
393 (r'[)][{]', "space between ) and {"),
393 (r'[)][{]', "space between ) and {"),
394 (r'\w+\* \w+', "use int *foo, not int* foo"),
394 (r'\w+\* \w+', "use int *foo, not int* foo"),
395 (r'\W\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
395 (r'\W\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
396 (r'\w+ (\+\+|--)', "use foo++, not foo ++"),
396 (r'\w+ (\+\+|--)', "use foo++, not foo ++"),
397 (r'\w,\w', "missing whitespace after ,"),
397 (r'\w,\w', "missing whitespace after ,"),
398 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
398 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
399 (r'\w\s=\s\s+\w', "gratuitous whitespace after ="),
399 (r'\w\s=\s\s+\w', "gratuitous whitespace after ="),
400 (r'^#\s+\w', "use #foo, not # foo"),
400 (r'^#\s+\w', "use #foo, not # foo"),
401 (r'[^\n]\Z', "no trailing newline"),
401 (r'[^\n]\Z', "no trailing newline"),
402 (r'^\s*#import\b', "use only #include in standard C code"),
402 (r'^\s*#import\b', "use only #include in standard C code"),
403 (r'strcpy\(', "don't use strcpy, use strlcpy or memcpy"),
403 (r'strcpy\(', "don't use strcpy, use strlcpy or memcpy"),
404 (r'strcat\(', "don't use strcat"),
404 (r'strcat\(', "don't use strcat"),
405
405
406 # rules depending on implementation of repquote()
406 # rules depending on implementation of repquote()
407 ],
407 ],
408 # warnings
408 # warnings
409 [
409 [
410 # rules depending on implementation of repquote()
410 # rules depending on implementation of repquote()
411 ]
411 ]
412 ]
412 ]
413
413
414 cfilters = [
414 cfilters = [
415 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
415 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
416 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
416 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
417 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
417 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
418 (r'(\()([^)]+\))', repcallspaces),
418 (r'(\()([^)]+\))', repcallspaces),
419 ]
419 ]
420
420
421 inutilpats = [
421 inutilpats = [
422 [
422 [
423 (r'\bui\.', "don't use ui in util"),
423 (r'\bui\.', "don't use ui in util"),
424 ],
424 ],
425 # warnings
425 # warnings
426 []
426 []
427 ]
427 ]
428
428
429 inrevlogpats = [
429 inrevlogpats = [
430 [
430 [
431 (r'\brepo\.', "don't use repo in revlog"),
431 (r'\brepo\.', "don't use repo in revlog"),
432 ],
432 ],
433 # warnings
433 # warnings
434 []
434 []
435 ]
435 ]
436
436
437 webtemplatefilters = []
437 webtemplatefilters = []
438
438
439 webtemplatepats = [
439 webtemplatepats = [
440 [],
440 [],
441 [
441 [
442 (r'{desc(\|(?!websub|firstline)[^\|]*)+}',
442 (r'{desc(\|(?!websub|firstline)[^\|]*)+}',
443 'follow desc keyword with either firstline or websub'),
443 'follow desc keyword with either firstline or websub'),
444 ]
444 ]
445 ]
445 ]
446
446
447 allfilesfilters = []
448
449 allfilespats = [
450 [
451 (r'(http|https)://[a-zA-Z0-9./]*selenic.com/',
452 'use mercurial-scm.org domain URL'),
453 ],
454 # warnings
455 [],
456 ]
457
447 checks = [
458 checks = [
448 ('python', r'.*\.(py|cgi)$', r'^#!.*python', pyfilters, pypats),
459 ('python', r'.*\.(py|cgi)$', r'^#!.*python', pyfilters, pypats),
449 ('test script', r'(.*/)?test-[^.~]*$', '', testfilters, testpats),
460 ('test script', r'(.*/)?test-[^.~]*$', '', testfilters, testpats),
450 ('c', r'.*\.[ch]$', '', cfilters, cpats),
461 ('c', r'.*\.[ch]$', '', cfilters, cpats),
451 ('unified test', r'.*\.t$', '', utestfilters, utestpats),
462 ('unified test', r'.*\.t$', '', utestfilters, utestpats),
452 ('layering violation repo in revlog', r'mercurial/revlog\.py', '',
463 ('layering violation repo in revlog', r'mercurial/revlog\.py', '',
453 pyfilters, inrevlogpats),
464 pyfilters, inrevlogpats),
454 ('layering violation ui in util', r'mercurial/util\.py', '', pyfilters,
465 ('layering violation ui in util', r'mercurial/util\.py', '', pyfilters,
455 inutilpats),
466 inutilpats),
456 ('txt', r'.*\.txt$', '', txtfilters, txtpats),
467 ('txt', r'.*\.txt$', '', txtfilters, txtpats),
457 ('web template', r'mercurial/templates/.*\.tmpl', '',
468 ('web template', r'mercurial/templates/.*\.tmpl', '',
458 webtemplatefilters, webtemplatepats),
469 webtemplatefilters, webtemplatepats),
470 ('all except for .po', r'.*(?<!\.po)$', '',
471 allfilesfilters, allfilespats),
459 ]
472 ]
460
473
461 def _preparepats():
474 def _preparepats():
462 for c in checks:
475 for c in checks:
463 failandwarn = c[-1]
476 failandwarn = c[-1]
464 for pats in failandwarn:
477 for pats in failandwarn:
465 for i, pseq in enumerate(pats):
478 for i, pseq in enumerate(pats):
466 # fix-up regexes for multi-line searches
479 # fix-up regexes for multi-line searches
467 p = pseq[0]
480 p = pseq[0]
468 # \s doesn't match \n
481 # \s doesn't match \n
469 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
482 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
470 # [^...] doesn't match newline
483 # [^...] doesn't match newline
471 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p)
484 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p)
472
485
473 pats[i] = (re.compile(p, re.MULTILINE),) + pseq[1:]
486 pats[i] = (re.compile(p, re.MULTILINE),) + pseq[1:]
474 filters = c[3]
487 filters = c[3]
475 for i, flt in enumerate(filters):
488 for i, flt in enumerate(filters):
476 filters[i] = re.compile(flt[0]), flt[1]
489 filters[i] = re.compile(flt[0]), flt[1]
477
490
478 class norepeatlogger(object):
491 class norepeatlogger(object):
479 def __init__(self):
492 def __init__(self):
480 self._lastseen = None
493 self._lastseen = None
481
494
482 def log(self, fname, lineno, line, msg, blame):
495 def log(self, fname, lineno, line, msg, blame):
483 """print error related a to given line of a given file.
496 """print error related a to given line of a given file.
484
497
485 The faulty line will also be printed but only once in the case
498 The faulty line will also be printed but only once in the case
486 of multiple errors.
499 of multiple errors.
487
500
488 :fname: filename
501 :fname: filename
489 :lineno: line number
502 :lineno: line number
490 :line: actual content of the line
503 :line: actual content of the line
491 :msg: error message
504 :msg: error message
492 """
505 """
493 msgid = fname, lineno, line
506 msgid = fname, lineno, line
494 if msgid != self._lastseen:
507 if msgid != self._lastseen:
495 if blame:
508 if blame:
496 print("%s:%d (%s):" % (fname, lineno, blame))
509 print("%s:%d (%s):" % (fname, lineno, blame))
497 else:
510 else:
498 print("%s:%d:" % (fname, lineno))
511 print("%s:%d:" % (fname, lineno))
499 print(" > %s" % line)
512 print(" > %s" % line)
500 self._lastseen = msgid
513 self._lastseen = msgid
501 print(" " + msg)
514 print(" " + msg)
502
515
503 _defaultlogger = norepeatlogger()
516 _defaultlogger = norepeatlogger()
504
517
505 def getblame(f):
518 def getblame(f):
506 lines = []
519 lines = []
507 for l in os.popen('hg annotate -un %s' % f):
520 for l in os.popen('hg annotate -un %s' % f):
508 start, line = l.split(':', 1)
521 start, line = l.split(':', 1)
509 user, rev = start.split()
522 user, rev = start.split()
510 lines.append((line[1:-1], user, rev))
523 lines.append((line[1:-1], user, rev))
511 return lines
524 return lines
512
525
513 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
526 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
514 blame=False, debug=False, lineno=True):
527 blame=False, debug=False, lineno=True):
515 """checks style and portability of a given file
528 """checks style and portability of a given file
516
529
517 :f: filepath
530 :f: filepath
518 :logfunc: function used to report error
531 :logfunc: function used to report error
519 logfunc(filename, linenumber, linecontent, errormessage)
532 logfunc(filename, linenumber, linecontent, errormessage)
520 :maxerr: number of error to display before aborting.
533 :maxerr: number of error to display before aborting.
521 Set to false (default) to report all errors
534 Set to false (default) to report all errors
522
535
523 return True if no error is found, False otherwise.
536 return True if no error is found, False otherwise.
524 """
537 """
525 blamecache = None
538 blamecache = None
526 result = True
539 result = True
527
540
528 try:
541 try:
529 with opentext(f) as fp:
542 with opentext(f) as fp:
530 try:
543 try:
531 pre = post = fp.read()
544 pre = post = fp.read()
532 except UnicodeDecodeError as e:
545 except UnicodeDecodeError as e:
533 print("%s while reading %s" % (e, f))
546 print("%s while reading %s" % (e, f))
534 return result
547 return result
535 except IOError as e:
548 except IOError as e:
536 print("Skipping %s, %s" % (f, str(e).split(':', 1)[0]))
549 print("Skipping %s, %s" % (f, str(e).split(':', 1)[0]))
537 return result
550 return result
538
551
539 for name, match, magic, filters, pats in checks:
552 for name, match, magic, filters, pats in checks:
553 post = pre # discard filtering result of previous check
540 if debug:
554 if debug:
541 print(name, f)
555 print(name, f)
542 fc = 0
556 fc = 0
543 if not (re.match(match, f) or (magic and re.search(magic, pre))):
557 if not (re.match(match, f) or (magic and re.search(magic, pre))):
544 if debug:
558 if debug:
545 print("Skipping %s for %s it doesn't match %s" % (
559 print("Skipping %s for %s it doesn't match %s" % (
546 name, match, f))
560 name, match, f))
547 continue
561 continue
548 if "no-" "check-code" in pre:
562 if "no-" "check-code" in pre:
549 # If you're looking at this line, it's because a file has:
563 # If you're looking at this line, it's because a file has:
550 # no- check- code
564 # no- check- code
551 # but the reason to output skipping is to make life for
565 # but the reason to output skipping is to make life for
552 # tests easier. So, instead of writing it with a normal
566 # tests easier. So, instead of writing it with a normal
553 # spelling, we write it with the expected spelling from
567 # spelling, we write it with the expected spelling from
554 # tests/test-check-code.t
568 # tests/test-check-code.t
555 print("Skipping %s it has no-che?k-code (glob)" % f)
569 print("Skipping %s it has no-che?k-code (glob)" % f)
556 return "Skip" # skip checking this file
570 return "Skip" # skip checking this file
557 for p, r in filters:
571 for p, r in filters:
558 post = re.sub(p, r, post)
572 post = re.sub(p, r, post)
559 nerrs = len(pats[0]) # nerr elements are errors
573 nerrs = len(pats[0]) # nerr elements are errors
560 if warnings:
574 if warnings:
561 pats = pats[0] + pats[1]
575 pats = pats[0] + pats[1]
562 else:
576 else:
563 pats = pats[0]
577 pats = pats[0]
564 # print post # uncomment to show filtered version
578 # print post # uncomment to show filtered version
565
579
566 if debug:
580 if debug:
567 print("Checking %s for %s" % (name, f))
581 print("Checking %s for %s" % (name, f))
568
582
569 prelines = None
583 prelines = None
570 errors = []
584 errors = []
571 for i, pat in enumerate(pats):
585 for i, pat in enumerate(pats):
572 if len(pat) == 3:
586 if len(pat) == 3:
573 p, msg, ignore = pat
587 p, msg, ignore = pat
574 else:
588 else:
575 p, msg = pat
589 p, msg = pat
576 ignore = None
590 ignore = None
577 if i >= nerrs:
591 if i >= nerrs:
578 msg = "warning: " + msg
592 msg = "warning: " + msg
579
593
580 pos = 0
594 pos = 0
581 n = 0
595 n = 0
582 for m in p.finditer(post):
596 for m in p.finditer(post):
583 if prelines is None:
597 if prelines is None:
584 prelines = pre.splitlines()
598 prelines = pre.splitlines()
585 postlines = post.splitlines(True)
599 postlines = post.splitlines(True)
586
600
587 start = m.start()
601 start = m.start()
588 while n < len(postlines):
602 while n < len(postlines):
589 step = len(postlines[n])
603 step = len(postlines[n])
590 if pos + step > start:
604 if pos + step > start:
591 break
605 break
592 pos += step
606 pos += step
593 n += 1
607 n += 1
594 l = prelines[n]
608 l = prelines[n]
595
609
596 if ignore and re.search(ignore, l, re.MULTILINE):
610 if ignore and re.search(ignore, l, re.MULTILINE):
597 if debug:
611 if debug:
598 print("Skipping %s for %s:%s (ignore pattern)" % (
612 print("Skipping %s for %s:%s (ignore pattern)" % (
599 name, f, n))
613 name, f, n))
600 continue
614 continue
601 bd = ""
615 bd = ""
602 if blame:
616 if blame:
603 bd = 'working directory'
617 bd = 'working directory'
604 if not blamecache:
618 if not blamecache:
605 blamecache = getblame(f)
619 blamecache = getblame(f)
606 if n < len(blamecache):
620 if n < len(blamecache):
607 bl, bu, br = blamecache[n]
621 bl, bu, br = blamecache[n]
608 if bl == l:
622 if bl == l:
609 bd = '%s@%s' % (bu, br)
623 bd = '%s@%s' % (bu, br)
610
624
611 errors.append((f, lineno and n + 1, l, msg, bd))
625 errors.append((f, lineno and n + 1, l, msg, bd))
612 result = False
626 result = False
613
627
614 errors.sort()
628 errors.sort()
615 for e in errors:
629 for e in errors:
616 logfunc(*e)
630 logfunc(*e)
617 fc += 1
631 fc += 1
618 if maxerr and fc >= maxerr:
632 if maxerr and fc >= maxerr:
619 print(" (too many errors, giving up)")
633 print(" (too many errors, giving up)")
620 break
634 break
621
635
622 return result
636 return result
623
637
624 def main():
638 def main():
625 parser = optparse.OptionParser("%prog [options] [files]")
639 parser = optparse.OptionParser("%prog [options] [files]")
626 parser.add_option("-w", "--warnings", action="store_true",
640 parser.add_option("-w", "--warnings", action="store_true",
627 help="include warning-level checks")
641 help="include warning-level checks")
628 parser.add_option("-p", "--per-file", type="int",
642 parser.add_option("-p", "--per-file", type="int",
629 help="max warnings per file")
643 help="max warnings per file")
630 parser.add_option("-b", "--blame", action="store_true",
644 parser.add_option("-b", "--blame", action="store_true",
631 help="use annotate to generate blame info")
645 help="use annotate to generate blame info")
632 parser.add_option("", "--debug", action="store_true",
646 parser.add_option("", "--debug", action="store_true",
633 help="show debug information")
647 help="show debug information")
634 parser.add_option("", "--nolineno", action="store_false",
648 parser.add_option("", "--nolineno", action="store_false",
635 dest='lineno', help="don't show line numbers")
649 dest='lineno', help="don't show line numbers")
636
650
637 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False,
651 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False,
638 lineno=True)
652 lineno=True)
639 (options, args) = parser.parse_args()
653 (options, args) = parser.parse_args()
640
654
641 if len(args) == 0:
655 if len(args) == 0:
642 check = glob.glob("*")
656 check = glob.glob("*")
643 else:
657 else:
644 check = args
658 check = args
645
659
646 _preparepats()
660 _preparepats()
647
661
648 ret = 0
662 ret = 0
649 for f in check:
663 for f in check:
650 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
664 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
651 blame=options.blame, debug=options.debug,
665 blame=options.blame, debug=options.debug,
652 lineno=options.lineno):
666 lineno=options.lineno):
653 ret = 1
667 ret = 1
654 return ret
668 return ret
655
669
656 if __name__ == "__main__":
670 if __name__ == "__main__":
657 sys.exit(main())
671 sys.exit(main())
@@ -1,1103 +1,1103 b''
1 # perf.py - performance test routines
1 # perf.py - performance test routines
2 '''helper extension to measure performance'''
2 '''helper extension to measure performance'''
3
3
4 # "historical portability" policy of perf.py:
4 # "historical portability" policy of perf.py:
5 #
5 #
6 # We have to do:
6 # We have to do:
7 # - make perf.py "loadable" with as wide Mercurial version as possible
7 # - make perf.py "loadable" with as wide Mercurial version as possible
8 # This doesn't mean that perf commands work correctly with that Mercurial.
8 # This doesn't mean that perf commands work correctly with that Mercurial.
9 # BTW, perf.py itself has been available since 1.1 (or eb240755386d).
9 # BTW, perf.py itself has been available since 1.1 (or eb240755386d).
10 # - make historical perf command work correctly with as wide Mercurial
10 # - make historical perf command work correctly with as wide Mercurial
11 # version as possible
11 # version as possible
12 #
12 #
13 # We have to do, if possible with reasonable cost:
13 # We have to do, if possible with reasonable cost:
14 # - make recent perf command for historical feature work correctly
14 # - make recent perf command for historical feature work correctly
15 # with early Mercurial
15 # with early Mercurial
16 #
16 #
17 # We don't have to do:
17 # We don't have to do:
18 # - make perf command for recent feature work correctly with early
18 # - make perf command for recent feature work correctly with early
19 # Mercurial
19 # Mercurial
20
20
21 from __future__ import absolute_import
21 from __future__ import absolute_import
22 import functools
22 import functools
23 import os
23 import os
24 import random
24 import random
25 import sys
25 import sys
26 import time
26 import time
27 from mercurial import (
27 from mercurial import (
28 changegroup,
28 changegroup,
29 cmdutil,
29 cmdutil,
30 commands,
30 commands,
31 copies,
31 copies,
32 error,
32 error,
33 extensions,
33 extensions,
34 mdiff,
34 mdiff,
35 merge,
35 merge,
36 revlog,
36 revlog,
37 util,
37 util,
38 )
38 )
39
39
40 # for "historical portability":
40 # for "historical portability":
41 # try to import modules separately (in dict order), and ignore
41 # try to import modules separately (in dict order), and ignore
42 # failure, because these aren't available with early Mercurial
42 # failure, because these aren't available with early Mercurial
43 try:
43 try:
44 from mercurial import branchmap # since 2.5 (or bcee63733aad)
44 from mercurial import branchmap # since 2.5 (or bcee63733aad)
45 except ImportError:
45 except ImportError:
46 pass
46 pass
47 try:
47 try:
48 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
48 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
49 except ImportError:
49 except ImportError:
50 pass
50 pass
51 try:
51 try:
52 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
52 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
53 except ImportError:
53 except ImportError:
54 pass
54 pass
55 try:
55 try:
56 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
56 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
57 except ImportError:
57 except ImportError:
58 pass
58 pass
59
59
60 # for "historical portability":
60 # for "historical portability":
61 # define util.safehasattr forcibly, because util.safehasattr has been
61 # define util.safehasattr forcibly, because util.safehasattr has been
62 # available since 1.9.3 (or 94b200a11cf7)
62 # available since 1.9.3 (or 94b200a11cf7)
63 _undefined = object()
63 _undefined = object()
64 def safehasattr(thing, attr):
64 def safehasattr(thing, attr):
65 return getattr(thing, attr, _undefined) is not _undefined
65 return getattr(thing, attr, _undefined) is not _undefined
66 setattr(util, 'safehasattr', safehasattr)
66 setattr(util, 'safehasattr', safehasattr)
67
67
68 # for "historical portability":
68 # for "historical portability":
69 # use locally defined empty option list, if formatteropts isn't
69 # use locally defined empty option list, if formatteropts isn't
70 # available, because commands.formatteropts has been available since
70 # available, because commands.formatteropts has been available since
71 # 3.2 (or 7a7eed5176a4), even though formatting itself has been
71 # 3.2 (or 7a7eed5176a4), even though formatting itself has been
72 # available since 2.2 (or ae5f92e154d3)
72 # available since 2.2 (or ae5f92e154d3)
73 formatteropts = getattr(commands, "formatteropts", [])
73 formatteropts = getattr(commands, "formatteropts", [])
74
74
75 # for "historical portability":
75 # for "historical portability":
76 # use locally defined option list, if debugrevlogopts isn't available,
76 # use locally defined option list, if debugrevlogopts isn't available,
77 # because commands.debugrevlogopts has been available since 3.7 (or
77 # because commands.debugrevlogopts has been available since 3.7 (or
78 # 5606f7d0d063), even though cmdutil.openrevlog() has been available
78 # 5606f7d0d063), even though cmdutil.openrevlog() has been available
79 # since 1.9 (or a79fea6b3e77).
79 # since 1.9 (or a79fea6b3e77).
80 revlogopts = getattr(commands, "debugrevlogopts", [
80 revlogopts = getattr(commands, "debugrevlogopts", [
81 ('c', 'changelog', False, ('open changelog')),
81 ('c', 'changelog', False, ('open changelog')),
82 ('m', 'manifest', False, ('open manifest')),
82 ('m', 'manifest', False, ('open manifest')),
83 ('', 'dir', False, ('open directory manifest')),
83 ('', 'dir', False, ('open directory manifest')),
84 ])
84 ])
85
85
86 cmdtable = {}
86 cmdtable = {}
87
87
88 # for "historical portability":
88 # for "historical portability":
89 # define parsealiases locally, because cmdutil.parsealiases has been
89 # define parsealiases locally, because cmdutil.parsealiases has been
90 # available since 1.5 (or 6252852b4332)
90 # available since 1.5 (or 6252852b4332)
91 def parsealiases(cmd):
91 def parsealiases(cmd):
92 return cmd.lstrip("^").split("|")
92 return cmd.lstrip("^").split("|")
93
93
94 if safehasattr(cmdutil, 'command'):
94 if safehasattr(cmdutil, 'command'):
95 import inspect
95 import inspect
96 command = cmdutil.command(cmdtable)
96 command = cmdutil.command(cmdtable)
97 if 'norepo' not in inspect.getargspec(command)[0]:
97 if 'norepo' not in inspect.getargspec(command)[0]:
98 # for "historical portability":
98 # for "historical portability":
99 # wrap original cmdutil.command, because "norepo" option has
99 # wrap original cmdutil.command, because "norepo" option has
100 # been available since 3.1 (or 75a96326cecb)
100 # been available since 3.1 (or 75a96326cecb)
101 _command = command
101 _command = command
102 def command(name, options=(), synopsis=None, norepo=False):
102 def command(name, options=(), synopsis=None, norepo=False):
103 if norepo:
103 if norepo:
104 commands.norepo += ' %s' % ' '.join(parsealiases(name))
104 commands.norepo += ' %s' % ' '.join(parsealiases(name))
105 return _command(name, list(options), synopsis)
105 return _command(name, list(options), synopsis)
106 else:
106 else:
107 # for "historical portability":
107 # for "historical portability":
108 # define "@command" annotation locally, because cmdutil.command
108 # define "@command" annotation locally, because cmdutil.command
109 # has been available since 1.9 (or 2daa5179e73f)
109 # has been available since 1.9 (or 2daa5179e73f)
110 def command(name, options=(), synopsis=None, norepo=False):
110 def command(name, options=(), synopsis=None, norepo=False):
111 def decorator(func):
111 def decorator(func):
112 if synopsis:
112 if synopsis:
113 cmdtable[name] = func, list(options), synopsis
113 cmdtable[name] = func, list(options), synopsis
114 else:
114 else:
115 cmdtable[name] = func, list(options)
115 cmdtable[name] = func, list(options)
116 if norepo:
116 if norepo:
117 commands.norepo += ' %s' % ' '.join(parsealiases(name))
117 commands.norepo += ' %s' % ' '.join(parsealiases(name))
118 return func
118 return func
119 return decorator
119 return decorator
120
120
121 def getlen(ui):
121 def getlen(ui):
122 if ui.configbool("perf", "stub"):
122 if ui.configbool("perf", "stub"):
123 return lambda x: 1
123 return lambda x: 1
124 return len
124 return len
125
125
126 def gettimer(ui, opts=None):
126 def gettimer(ui, opts=None):
127 """return a timer function and formatter: (timer, formatter)
127 """return a timer function and formatter: (timer, formatter)
128
128
129 This function exists to gather the creation of formatter in a single
129 This function exists to gather the creation of formatter in a single
130 place instead of duplicating it in all performance commands."""
130 place instead of duplicating it in all performance commands."""
131
131
132 # enforce an idle period before execution to counteract power management
132 # enforce an idle period before execution to counteract power management
133 # experimental config: perf.presleep
133 # experimental config: perf.presleep
134 time.sleep(getint(ui, "perf", "presleep", 1))
134 time.sleep(getint(ui, "perf", "presleep", 1))
135
135
136 if opts is None:
136 if opts is None:
137 opts = {}
137 opts = {}
138 # redirect all to stderr
138 # redirect all to stderr
139 ui = ui.copy()
139 ui = ui.copy()
140 uifout = safeattrsetter(ui, 'fout', ignoremissing=True)
140 uifout = safeattrsetter(ui, 'fout', ignoremissing=True)
141 if uifout:
141 if uifout:
142 # for "historical portability":
142 # for "historical portability":
143 # ui.fout/ferr have been available since 1.9 (or 4e1ccd4c2b6d)
143 # ui.fout/ferr have been available since 1.9 (or 4e1ccd4c2b6d)
144 uifout.set(ui.ferr)
144 uifout.set(ui.ferr)
145
145
146 # get a formatter
146 # get a formatter
147 uiformatter = getattr(ui, 'formatter', None)
147 uiformatter = getattr(ui, 'formatter', None)
148 if uiformatter:
148 if uiformatter:
149 fm = uiformatter('perf', opts)
149 fm = uiformatter('perf', opts)
150 else:
150 else:
151 # for "historical portability":
151 # for "historical portability":
152 # define formatter locally, because ui.formatter has been
152 # define formatter locally, because ui.formatter has been
153 # available since 2.2 (or ae5f92e154d3)
153 # available since 2.2 (or ae5f92e154d3)
154 from mercurial import node
154 from mercurial import node
155 class defaultformatter(object):
155 class defaultformatter(object):
156 """Minimized composition of baseformatter and plainformatter
156 """Minimized composition of baseformatter and plainformatter
157 """
157 """
158 def __init__(self, ui, topic, opts):
158 def __init__(self, ui, topic, opts):
159 self._ui = ui
159 self._ui = ui
160 if ui.debugflag:
160 if ui.debugflag:
161 self.hexfunc = node.hex
161 self.hexfunc = node.hex
162 else:
162 else:
163 self.hexfunc = node.short
163 self.hexfunc = node.short
164 def __nonzero__(self):
164 def __nonzero__(self):
165 return False
165 return False
166 def startitem(self):
166 def startitem(self):
167 pass
167 pass
168 def data(self, **data):
168 def data(self, **data):
169 pass
169 pass
170 def write(self, fields, deftext, *fielddata, **opts):
170 def write(self, fields, deftext, *fielddata, **opts):
171 self._ui.write(deftext % fielddata, **opts)
171 self._ui.write(deftext % fielddata, **opts)
172 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
172 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
173 if cond:
173 if cond:
174 self._ui.write(deftext % fielddata, **opts)
174 self._ui.write(deftext % fielddata, **opts)
175 def plain(self, text, **opts):
175 def plain(self, text, **opts):
176 self._ui.write(text, **opts)
176 self._ui.write(text, **opts)
177 def end(self):
177 def end(self):
178 pass
178 pass
179 fm = defaultformatter(ui, 'perf', opts)
179 fm = defaultformatter(ui, 'perf', opts)
180
180
181 # stub function, runs code only once instead of in a loop
181 # stub function, runs code only once instead of in a loop
182 # experimental config: perf.stub
182 # experimental config: perf.stub
183 if ui.configbool("perf", "stub"):
183 if ui.configbool("perf", "stub"):
184 return functools.partial(stub_timer, fm), fm
184 return functools.partial(stub_timer, fm), fm
185 return functools.partial(_timer, fm), fm
185 return functools.partial(_timer, fm), fm
186
186
187 def stub_timer(fm, func, title=None):
187 def stub_timer(fm, func, title=None):
188 func()
188 func()
189
189
190 def _timer(fm, func, title=None):
190 def _timer(fm, func, title=None):
191 results = []
191 results = []
192 begin = time.time()
192 begin = time.time()
193 count = 0
193 count = 0
194 while True:
194 while True:
195 ostart = os.times()
195 ostart = os.times()
196 cstart = time.time()
196 cstart = time.time()
197 r = func()
197 r = func()
198 cstop = time.time()
198 cstop = time.time()
199 ostop = os.times()
199 ostop = os.times()
200 count += 1
200 count += 1
201 a, b = ostart, ostop
201 a, b = ostart, ostop
202 results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
202 results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
203 if cstop - begin > 3 and count >= 100:
203 if cstop - begin > 3 and count >= 100:
204 break
204 break
205 if cstop - begin > 10 and count >= 3:
205 if cstop - begin > 10 and count >= 3:
206 break
206 break
207
207
208 fm.startitem()
208 fm.startitem()
209
209
210 if title:
210 if title:
211 fm.write('title', '! %s\n', title)
211 fm.write('title', '! %s\n', title)
212 if r:
212 if r:
213 fm.write('result', '! result: %s\n', r)
213 fm.write('result', '! result: %s\n', r)
214 m = min(results)
214 m = min(results)
215 fm.plain('!')
215 fm.plain('!')
216 fm.write('wall', ' wall %f', m[0])
216 fm.write('wall', ' wall %f', m[0])
217 fm.write('comb', ' comb %f', m[1] + m[2])
217 fm.write('comb', ' comb %f', m[1] + m[2])
218 fm.write('user', ' user %f', m[1])
218 fm.write('user', ' user %f', m[1])
219 fm.write('sys', ' sys %f', m[2])
219 fm.write('sys', ' sys %f', m[2])
220 fm.write('count', ' (best of %d)', count)
220 fm.write('count', ' (best of %d)', count)
221 fm.plain('\n')
221 fm.plain('\n')
222
222
223 # utilities for historical portability
223 # utilities for historical portability
224
224
225 def getint(ui, section, name, default):
225 def getint(ui, section, name, default):
226 # for "historical portability":
226 # for "historical portability":
227 # ui.configint has been available since 1.9 (or fa2b596db182)
227 # ui.configint has been available since 1.9 (or fa2b596db182)
228 v = ui.config(section, name, None)
228 v = ui.config(section, name, None)
229 if v is None:
229 if v is None:
230 return default
230 return default
231 try:
231 try:
232 return int(v)
232 return int(v)
233 except ValueError:
233 except ValueError:
234 raise error.ConfigError(("%s.%s is not an integer ('%s')")
234 raise error.ConfigError(("%s.%s is not an integer ('%s')")
235 % (section, name, v))
235 % (section, name, v))
236
236
237 def safeattrsetter(obj, name, ignoremissing=False):
237 def safeattrsetter(obj, name, ignoremissing=False):
238 """Ensure that 'obj' has 'name' attribute before subsequent setattr
238 """Ensure that 'obj' has 'name' attribute before subsequent setattr
239
239
240 This function is aborted, if 'obj' doesn't have 'name' attribute
240 This function is aborted, if 'obj' doesn't have 'name' attribute
241 at runtime. This avoids overlooking removal of an attribute, which
241 at runtime. This avoids overlooking removal of an attribute, which
242 breaks assumption of performance measurement, in the future.
242 breaks assumption of performance measurement, in the future.
243
243
244 This function returns the object to (1) assign a new value, and
244 This function returns the object to (1) assign a new value, and
245 (2) restore an original value to the attribute.
245 (2) restore an original value to the attribute.
246
246
247 If 'ignoremissing' is true, missing 'name' attribute doesn't cause
247 If 'ignoremissing' is true, missing 'name' attribute doesn't cause
248 abortion, and this function returns None. This is useful to
248 abortion, and this function returns None. This is useful to
249 examine an attribute, which isn't ensured in all Mercurial
249 examine an attribute, which isn't ensured in all Mercurial
250 versions.
250 versions.
251 """
251 """
252 if not util.safehasattr(obj, name):
252 if not util.safehasattr(obj, name):
253 if ignoremissing:
253 if ignoremissing:
254 return None
254 return None
255 raise error.Abort(("missing attribute %s of %s might break assumption"
255 raise error.Abort(("missing attribute %s of %s might break assumption"
256 " of performance measurement") % (name, obj))
256 " of performance measurement") % (name, obj))
257
257
258 origvalue = getattr(obj, name)
258 origvalue = getattr(obj, name)
259 class attrutil(object):
259 class attrutil(object):
260 def set(self, newvalue):
260 def set(self, newvalue):
261 setattr(obj, name, newvalue)
261 setattr(obj, name, newvalue)
262 def restore(self):
262 def restore(self):
263 setattr(obj, name, origvalue)
263 setattr(obj, name, origvalue)
264
264
265 return attrutil()
265 return attrutil()
266
266
267 # utilities to examine each internal API changes
267 # utilities to examine each internal API changes
268
268
269 def getbranchmapsubsettable():
269 def getbranchmapsubsettable():
270 # for "historical portability":
270 # for "historical portability":
271 # subsettable is defined in:
271 # subsettable is defined in:
272 # - branchmap since 2.9 (or 175c6fd8cacc)
272 # - branchmap since 2.9 (or 175c6fd8cacc)
273 # - repoview since 2.5 (or 59a9f18d4587)
273 # - repoview since 2.5 (or 59a9f18d4587)
274 for mod in (branchmap, repoview):
274 for mod in (branchmap, repoview):
275 subsettable = getattr(mod, 'subsettable', None)
275 subsettable = getattr(mod, 'subsettable', None)
276 if subsettable:
276 if subsettable:
277 return subsettable
277 return subsettable
278
278
279 # bisecting in bcee63733aad::59a9f18d4587 can reach here (both
279 # bisecting in bcee63733aad::59a9f18d4587 can reach here (both
280 # branchmap and repoview modules exist, but subsettable attribute
280 # branchmap and repoview modules exist, but subsettable attribute
281 # doesn't)
281 # doesn't)
282 raise error.Abort(("perfbranchmap not available with this Mercurial"),
282 raise error.Abort(("perfbranchmap not available with this Mercurial"),
283 hint="use 2.5 or later")
283 hint="use 2.5 or later")
284
284
285 def getsvfs(repo):
285 def getsvfs(repo):
286 """Return appropriate object to access files under .hg/store
286 """Return appropriate object to access files under .hg/store
287 """
287 """
288 # for "historical portability":
288 # for "historical portability":
289 # repo.svfs has been available since 2.3 (or 7034365089bf)
289 # repo.svfs has been available since 2.3 (or 7034365089bf)
290 svfs = getattr(repo, 'svfs', None)
290 svfs = getattr(repo, 'svfs', None)
291 if svfs:
291 if svfs:
292 return svfs
292 return svfs
293 else:
293 else:
294 return getattr(repo, 'sopener')
294 return getattr(repo, 'sopener')
295
295
296 def getvfs(repo):
296 def getvfs(repo):
297 """Return appropriate object to access files under .hg
297 """Return appropriate object to access files under .hg
298 """
298 """
299 # for "historical portability":
299 # for "historical portability":
300 # repo.vfs has been available since 2.3 (or 7034365089bf)
300 # repo.vfs has been available since 2.3 (or 7034365089bf)
301 vfs = getattr(repo, 'vfs', None)
301 vfs = getattr(repo, 'vfs', None)
302 if vfs:
302 if vfs:
303 return vfs
303 return vfs
304 else:
304 else:
305 return getattr(repo, 'opener')
305 return getattr(repo, 'opener')
306
306
307 def repocleartagscachefunc(repo):
307 def repocleartagscachefunc(repo):
308 """Return the function to clear tags cache according to repo internal API
308 """Return the function to clear tags cache according to repo internal API
309 """
309 """
310 if util.safehasattr(repo, '_tagscache'): # since 2.0 (or 9dca7653b525)
310 if util.safehasattr(repo, '_tagscache'): # since 2.0 (or 9dca7653b525)
311 # in this case, setattr(repo, '_tagscache', None) or so isn't
311 # in this case, setattr(repo, '_tagscache', None) or so isn't
312 # correct way to clear tags cache, because existing code paths
312 # correct way to clear tags cache, because existing code paths
313 # expect _tagscache to be a structured object.
313 # expect _tagscache to be a structured object.
314 def clearcache():
314 def clearcache():
315 # _tagscache has been filteredpropertycache since 2.5 (or
315 # _tagscache has been filteredpropertycache since 2.5 (or
316 # 98c867ac1330), and delattr() can't work in such case
316 # 98c867ac1330), and delattr() can't work in such case
317 if '_tagscache' in vars(repo):
317 if '_tagscache' in vars(repo):
318 del repo.__dict__['_tagscache']
318 del repo.__dict__['_tagscache']
319 return clearcache
319 return clearcache
320
320
321 repotags = safeattrsetter(repo, '_tags', ignoremissing=True)
321 repotags = safeattrsetter(repo, '_tags', ignoremissing=True)
322 if repotags: # since 1.4 (or 5614a628d173)
322 if repotags: # since 1.4 (or 5614a628d173)
323 return lambda : repotags.set(None)
323 return lambda : repotags.set(None)
324
324
325 repotagscache = safeattrsetter(repo, 'tagscache', ignoremissing=True)
325 repotagscache = safeattrsetter(repo, 'tagscache', ignoremissing=True)
326 if repotagscache: # since 0.6 (or d7df759d0e97)
326 if repotagscache: # since 0.6 (or d7df759d0e97)
327 return lambda : repotagscache.set(None)
327 return lambda : repotagscache.set(None)
328
328
329 # Mercurial earlier than 0.6 (or d7df759d0e97) logically reaches
329 # Mercurial earlier than 0.6 (or d7df759d0e97) logically reaches
330 # this point, but it isn't so problematic, because:
330 # this point, but it isn't so problematic, because:
331 # - repo.tags of such Mercurial isn't "callable", and repo.tags()
331 # - repo.tags of such Mercurial isn't "callable", and repo.tags()
332 # in perftags() causes failure soon
332 # in perftags() causes failure soon
333 # - perf.py itself has been available since 1.1 (or eb240755386d)
333 # - perf.py itself has been available since 1.1 (or eb240755386d)
334 raise error.Abort(("tags API of this hg command is unknown"))
334 raise error.Abort(("tags API of this hg command is unknown"))
335
335
336 # perf commands
336 # perf commands
337
337
338 @command('perfwalk', formatteropts)
338 @command('perfwalk', formatteropts)
339 def perfwalk(ui, repo, *pats, **opts):
339 def perfwalk(ui, repo, *pats, **opts):
340 timer, fm = gettimer(ui, opts)
340 timer, fm = gettimer(ui, opts)
341 try:
341 try:
342 m = scmutil.match(repo[None], pats, {})
342 m = scmutil.match(repo[None], pats, {})
343 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
343 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
344 except Exception:
344 except Exception:
345 try:
345 try:
346 m = scmutil.match(repo[None], pats, {})
346 m = scmutil.match(repo[None], pats, {})
347 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
347 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
348 except Exception:
348 except Exception:
349 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
349 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
350 fm.end()
350 fm.end()
351
351
352 @command('perfannotate', formatteropts)
352 @command('perfannotate', formatteropts)
353 def perfannotate(ui, repo, f, **opts):
353 def perfannotate(ui, repo, f, **opts):
354 timer, fm = gettimer(ui, opts)
354 timer, fm = gettimer(ui, opts)
355 fc = repo['.'][f]
355 fc = repo['.'][f]
356 timer(lambda: len(fc.annotate(True)))
356 timer(lambda: len(fc.annotate(True)))
357 fm.end()
357 fm.end()
358
358
359 @command('perfstatus',
359 @command('perfstatus',
360 [('u', 'unknown', False,
360 [('u', 'unknown', False,
361 'ask status to look for unknown files')] + formatteropts)
361 'ask status to look for unknown files')] + formatteropts)
362 def perfstatus(ui, repo, **opts):
362 def perfstatus(ui, repo, **opts):
363 #m = match.always(repo.root, repo.getcwd())
363 #m = match.always(repo.root, repo.getcwd())
364 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
364 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
365 # False))))
365 # False))))
366 timer, fm = gettimer(ui, opts)
366 timer, fm = gettimer(ui, opts)
367 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
367 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
368 fm.end()
368 fm.end()
369
369
370 @command('perfaddremove', formatteropts)
370 @command('perfaddremove', formatteropts)
371 def perfaddremove(ui, repo, **opts):
371 def perfaddremove(ui, repo, **opts):
372 timer, fm = gettimer(ui, opts)
372 timer, fm = gettimer(ui, opts)
373 try:
373 try:
374 oldquiet = repo.ui.quiet
374 oldquiet = repo.ui.quiet
375 repo.ui.quiet = True
375 repo.ui.quiet = True
376 matcher = scmutil.match(repo[None])
376 matcher = scmutil.match(repo[None])
377 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
377 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
378 finally:
378 finally:
379 repo.ui.quiet = oldquiet
379 repo.ui.quiet = oldquiet
380 fm.end()
380 fm.end()
381
381
382 def clearcaches(cl):
382 def clearcaches(cl):
383 # behave somewhat consistently across internal API changes
383 # behave somewhat consistently across internal API changes
384 if util.safehasattr(cl, 'clearcaches'):
384 if util.safehasattr(cl, 'clearcaches'):
385 cl.clearcaches()
385 cl.clearcaches()
386 elif util.safehasattr(cl, '_nodecache'):
386 elif util.safehasattr(cl, '_nodecache'):
387 from mercurial.node import nullid, nullrev
387 from mercurial.node import nullid, nullrev
388 cl._nodecache = {nullid: nullrev}
388 cl._nodecache = {nullid: nullrev}
389 cl._nodepos = None
389 cl._nodepos = None
390
390
391 @command('perfheads', formatteropts)
391 @command('perfheads', formatteropts)
392 def perfheads(ui, repo, **opts):
392 def perfheads(ui, repo, **opts):
393 timer, fm = gettimer(ui, opts)
393 timer, fm = gettimer(ui, opts)
394 cl = repo.changelog
394 cl = repo.changelog
395 def d():
395 def d():
396 len(cl.headrevs())
396 len(cl.headrevs())
397 clearcaches(cl)
397 clearcaches(cl)
398 timer(d)
398 timer(d)
399 fm.end()
399 fm.end()
400
400
401 @command('perftags', formatteropts)
401 @command('perftags', formatteropts)
402 def perftags(ui, repo, **opts):
402 def perftags(ui, repo, **opts):
403 import mercurial.changelog
403 import mercurial.changelog
404 import mercurial.manifest
404 import mercurial.manifest
405 timer, fm = gettimer(ui, opts)
405 timer, fm = gettimer(ui, opts)
406 svfs = getsvfs(repo)
406 svfs = getsvfs(repo)
407 repocleartagscache = repocleartagscachefunc(repo)
407 repocleartagscache = repocleartagscachefunc(repo)
408 def t():
408 def t():
409 repo.changelog = mercurial.changelog.changelog(svfs)
409 repo.changelog = mercurial.changelog.changelog(svfs)
410 repo.manifest = mercurial.manifest.manifest(svfs)
410 repo.manifestlog = mercurial.manifest.manifestlog(svfs, repo)
411 repocleartagscache()
411 repocleartagscache()
412 return len(repo.tags())
412 return len(repo.tags())
413 timer(t)
413 timer(t)
414 fm.end()
414 fm.end()
415
415
416 @command('perfancestors', formatteropts)
416 @command('perfancestors', formatteropts)
417 def perfancestors(ui, repo, **opts):
417 def perfancestors(ui, repo, **opts):
418 timer, fm = gettimer(ui, opts)
418 timer, fm = gettimer(ui, opts)
419 heads = repo.changelog.headrevs()
419 heads = repo.changelog.headrevs()
420 def d():
420 def d():
421 for a in repo.changelog.ancestors(heads):
421 for a in repo.changelog.ancestors(heads):
422 pass
422 pass
423 timer(d)
423 timer(d)
424 fm.end()
424 fm.end()
425
425
426 @command('perfancestorset', formatteropts)
426 @command('perfancestorset', formatteropts)
427 def perfancestorset(ui, repo, revset, **opts):
427 def perfancestorset(ui, repo, revset, **opts):
428 timer, fm = gettimer(ui, opts)
428 timer, fm = gettimer(ui, opts)
429 revs = repo.revs(revset)
429 revs = repo.revs(revset)
430 heads = repo.changelog.headrevs()
430 heads = repo.changelog.headrevs()
431 def d():
431 def d():
432 s = repo.changelog.ancestors(heads)
432 s = repo.changelog.ancestors(heads)
433 for rev in revs:
433 for rev in revs:
434 rev in s
434 rev in s
435 timer(d)
435 timer(d)
436 fm.end()
436 fm.end()
437
437
438 @command('perfchangegroupchangelog', formatteropts +
438 @command('perfchangegroupchangelog', formatteropts +
439 [('', 'version', '02', 'changegroup version'),
439 [('', 'version', '02', 'changegroup version'),
440 ('r', 'rev', '', 'revisions to add to changegroup')])
440 ('r', 'rev', '', 'revisions to add to changegroup')])
441 def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts):
441 def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts):
442 """Benchmark producing a changelog group for a changegroup.
442 """Benchmark producing a changelog group for a changegroup.
443
443
444 This measures the time spent processing the changelog during a
444 This measures the time spent processing the changelog during a
445 bundle operation. This occurs during `hg bundle` and on a server
445 bundle operation. This occurs during `hg bundle` and on a server
446 processing a `getbundle` wire protocol request (handles clones
446 processing a `getbundle` wire protocol request (handles clones
447 and pull requests).
447 and pull requests).
448
448
449 By default, all revisions are added to the changegroup.
449 By default, all revisions are added to the changegroup.
450 """
450 """
451 cl = repo.changelog
451 cl = repo.changelog
452 revs = [cl.lookup(r) for r in repo.revs(rev or 'all()')]
452 revs = [cl.lookup(r) for r in repo.revs(rev or 'all()')]
453 bundler = changegroup.getbundler(version, repo)
453 bundler = changegroup.getbundler(version, repo)
454
454
455 def lookup(node):
455 def lookup(node):
456 # The real bundler reads the revision in order to access the
456 # The real bundler reads the revision in order to access the
457 # manifest node and files list. Do that here.
457 # manifest node and files list. Do that here.
458 cl.read(node)
458 cl.read(node)
459 return node
459 return node
460
460
461 def d():
461 def d():
462 for chunk in bundler.group(revs, cl, lookup):
462 for chunk in bundler.group(revs, cl, lookup):
463 pass
463 pass
464
464
465 timer, fm = gettimer(ui, opts)
465 timer, fm = gettimer(ui, opts)
466 timer(d)
466 timer(d)
467 fm.end()
467 fm.end()
468
468
469 @command('perfdirs', formatteropts)
469 @command('perfdirs', formatteropts)
470 def perfdirs(ui, repo, **opts):
470 def perfdirs(ui, repo, **opts):
471 timer, fm = gettimer(ui, opts)
471 timer, fm = gettimer(ui, opts)
472 dirstate = repo.dirstate
472 dirstate = repo.dirstate
473 'a' in dirstate
473 'a' in dirstate
474 def d():
474 def d():
475 dirstate.dirs()
475 dirstate.dirs()
476 del dirstate._dirs
476 del dirstate._dirs
477 timer(d)
477 timer(d)
478 fm.end()
478 fm.end()
479
479
480 @command('perfdirstate', formatteropts)
480 @command('perfdirstate', formatteropts)
481 def perfdirstate(ui, repo, **opts):
481 def perfdirstate(ui, repo, **opts):
482 timer, fm = gettimer(ui, opts)
482 timer, fm = gettimer(ui, opts)
483 "a" in repo.dirstate
483 "a" in repo.dirstate
484 def d():
484 def d():
485 repo.dirstate.invalidate()
485 repo.dirstate.invalidate()
486 "a" in repo.dirstate
486 "a" in repo.dirstate
487 timer(d)
487 timer(d)
488 fm.end()
488 fm.end()
489
489
490 @command('perfdirstatedirs', formatteropts)
490 @command('perfdirstatedirs', formatteropts)
491 def perfdirstatedirs(ui, repo, **opts):
491 def perfdirstatedirs(ui, repo, **opts):
492 timer, fm = gettimer(ui, opts)
492 timer, fm = gettimer(ui, opts)
493 "a" in repo.dirstate
493 "a" in repo.dirstate
494 def d():
494 def d():
495 "a" in repo.dirstate._dirs
495 "a" in repo.dirstate._dirs
496 del repo.dirstate._dirs
496 del repo.dirstate._dirs
497 timer(d)
497 timer(d)
498 fm.end()
498 fm.end()
499
499
500 @command('perfdirstatefoldmap', formatteropts)
500 @command('perfdirstatefoldmap', formatteropts)
501 def perfdirstatefoldmap(ui, repo, **opts):
501 def perfdirstatefoldmap(ui, repo, **opts):
502 timer, fm = gettimer(ui, opts)
502 timer, fm = gettimer(ui, opts)
503 dirstate = repo.dirstate
503 dirstate = repo.dirstate
504 'a' in dirstate
504 'a' in dirstate
505 def d():
505 def d():
506 dirstate._filefoldmap.get('a')
506 dirstate._filefoldmap.get('a')
507 del dirstate._filefoldmap
507 del dirstate._filefoldmap
508 timer(d)
508 timer(d)
509 fm.end()
509 fm.end()
510
510
511 @command('perfdirfoldmap', formatteropts)
511 @command('perfdirfoldmap', formatteropts)
512 def perfdirfoldmap(ui, repo, **opts):
512 def perfdirfoldmap(ui, repo, **opts):
513 timer, fm = gettimer(ui, opts)
513 timer, fm = gettimer(ui, opts)
514 dirstate = repo.dirstate
514 dirstate = repo.dirstate
515 'a' in dirstate
515 'a' in dirstate
516 def d():
516 def d():
517 dirstate._dirfoldmap.get('a')
517 dirstate._dirfoldmap.get('a')
518 del dirstate._dirfoldmap
518 del dirstate._dirfoldmap
519 del dirstate._dirs
519 del dirstate._dirs
520 timer(d)
520 timer(d)
521 fm.end()
521 fm.end()
522
522
523 @command('perfdirstatewrite', formatteropts)
523 @command('perfdirstatewrite', formatteropts)
524 def perfdirstatewrite(ui, repo, **opts):
524 def perfdirstatewrite(ui, repo, **opts):
525 timer, fm = gettimer(ui, opts)
525 timer, fm = gettimer(ui, opts)
526 ds = repo.dirstate
526 ds = repo.dirstate
527 "a" in ds
527 "a" in ds
528 def d():
528 def d():
529 ds._dirty = True
529 ds._dirty = True
530 ds.write(repo.currenttransaction())
530 ds.write(repo.currenttransaction())
531 timer(d)
531 timer(d)
532 fm.end()
532 fm.end()
533
533
534 @command('perfmergecalculate',
534 @command('perfmergecalculate',
535 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
535 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
536 def perfmergecalculate(ui, repo, rev, **opts):
536 def perfmergecalculate(ui, repo, rev, **opts):
537 timer, fm = gettimer(ui, opts)
537 timer, fm = gettimer(ui, opts)
538 wctx = repo[None]
538 wctx = repo[None]
539 rctx = scmutil.revsingle(repo, rev, rev)
539 rctx = scmutil.revsingle(repo, rev, rev)
540 ancestor = wctx.ancestor(rctx)
540 ancestor = wctx.ancestor(rctx)
541 # we don't want working dir files to be stat'd in the benchmark, so prime
541 # we don't want working dir files to be stat'd in the benchmark, so prime
542 # that cache
542 # that cache
543 wctx.dirty()
543 wctx.dirty()
544 def d():
544 def d():
545 # acceptremote is True because we don't want prompts in the middle of
545 # acceptremote is True because we don't want prompts in the middle of
546 # our benchmark
546 # our benchmark
547 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
547 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
548 acceptremote=True, followcopies=True)
548 acceptremote=True, followcopies=True)
549 timer(d)
549 timer(d)
550 fm.end()
550 fm.end()
551
551
552 @command('perfpathcopies', [], "REV REV")
552 @command('perfpathcopies', [], "REV REV")
553 def perfpathcopies(ui, repo, rev1, rev2, **opts):
553 def perfpathcopies(ui, repo, rev1, rev2, **opts):
554 timer, fm = gettimer(ui, opts)
554 timer, fm = gettimer(ui, opts)
555 ctx1 = scmutil.revsingle(repo, rev1, rev1)
555 ctx1 = scmutil.revsingle(repo, rev1, rev1)
556 ctx2 = scmutil.revsingle(repo, rev2, rev2)
556 ctx2 = scmutil.revsingle(repo, rev2, rev2)
557 def d():
557 def d():
558 copies.pathcopies(ctx1, ctx2)
558 copies.pathcopies(ctx1, ctx2)
559 timer(d)
559 timer(d)
560 fm.end()
560 fm.end()
561
561
562 @command('perfmanifest', [], 'REV')
562 @command('perfmanifest', [], 'REV')
563 def perfmanifest(ui, repo, rev, **opts):
563 def perfmanifest(ui, repo, rev, **opts):
564 timer, fm = gettimer(ui, opts)
564 timer, fm = gettimer(ui, opts)
565 ctx = scmutil.revsingle(repo, rev, rev)
565 ctx = scmutil.revsingle(repo, rev, rev)
566 t = ctx.manifestnode()
566 t = ctx.manifestnode()
567 def d():
567 def d():
568 repo.manifest.clearcaches()
568 repo.manifest.clearcaches()
569 repo.manifest.read(t)
569 repo.manifest.read(t)
570 timer(d)
570 timer(d)
571 fm.end()
571 fm.end()
572
572
573 @command('perfchangeset', formatteropts)
573 @command('perfchangeset', formatteropts)
574 def perfchangeset(ui, repo, rev, **opts):
574 def perfchangeset(ui, repo, rev, **opts):
575 timer, fm = gettimer(ui, opts)
575 timer, fm = gettimer(ui, opts)
576 n = repo[rev].node()
576 n = repo[rev].node()
577 def d():
577 def d():
578 repo.changelog.read(n)
578 repo.changelog.read(n)
579 #repo.changelog._cache = None
579 #repo.changelog._cache = None
580 timer(d)
580 timer(d)
581 fm.end()
581 fm.end()
582
582
583 @command('perfindex', formatteropts)
583 @command('perfindex', formatteropts)
584 def perfindex(ui, repo, **opts):
584 def perfindex(ui, repo, **opts):
585 import mercurial.revlog
585 import mercurial.revlog
586 timer, fm = gettimer(ui, opts)
586 timer, fm = gettimer(ui, opts)
587 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
587 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
588 n = repo["tip"].node()
588 n = repo["tip"].node()
589 svfs = getsvfs(repo)
589 svfs = getsvfs(repo)
590 def d():
590 def d():
591 cl = mercurial.revlog.revlog(svfs, "00changelog.i")
591 cl = mercurial.revlog.revlog(svfs, "00changelog.i")
592 cl.rev(n)
592 cl.rev(n)
593 timer(d)
593 timer(d)
594 fm.end()
594 fm.end()
595
595
596 @command('perfstartup', formatteropts)
596 @command('perfstartup', formatteropts)
597 def perfstartup(ui, repo, **opts):
597 def perfstartup(ui, repo, **opts):
598 timer, fm = gettimer(ui, opts)
598 timer, fm = gettimer(ui, opts)
599 cmd = sys.argv[0]
599 cmd = sys.argv[0]
600 def d():
600 def d():
601 if os.name != 'nt':
601 if os.name != 'nt':
602 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
602 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
603 else:
603 else:
604 os.environ['HGRCPATH'] = ''
604 os.environ['HGRCPATH'] = ''
605 os.system("%s version -q > NUL" % cmd)
605 os.system("%s version -q > NUL" % cmd)
606 timer(d)
606 timer(d)
607 fm.end()
607 fm.end()
608
608
609 @command('perfparents', formatteropts)
609 @command('perfparents', formatteropts)
610 def perfparents(ui, repo, **opts):
610 def perfparents(ui, repo, **opts):
611 timer, fm = gettimer(ui, opts)
611 timer, fm = gettimer(ui, opts)
612 # control the number of commits perfparents iterates over
612 # control the number of commits perfparents iterates over
613 # experimental config: perf.parentscount
613 # experimental config: perf.parentscount
614 count = getint(ui, "perf", "parentscount", 1000)
614 count = getint(ui, "perf", "parentscount", 1000)
615 if len(repo.changelog) < count:
615 if len(repo.changelog) < count:
616 raise error.Abort("repo needs %d commits for this test" % count)
616 raise error.Abort("repo needs %d commits for this test" % count)
617 repo = repo.unfiltered()
617 repo = repo.unfiltered()
618 nl = [repo.changelog.node(i) for i in xrange(count)]
618 nl = [repo.changelog.node(i) for i in xrange(count)]
619 def d():
619 def d():
620 for n in nl:
620 for n in nl:
621 repo.changelog.parents(n)
621 repo.changelog.parents(n)
622 timer(d)
622 timer(d)
623 fm.end()
623 fm.end()
624
624
625 @command('perfctxfiles', formatteropts)
625 @command('perfctxfiles', formatteropts)
626 def perfctxfiles(ui, repo, x, **opts):
626 def perfctxfiles(ui, repo, x, **opts):
627 x = int(x)
627 x = int(x)
628 timer, fm = gettimer(ui, opts)
628 timer, fm = gettimer(ui, opts)
629 def d():
629 def d():
630 len(repo[x].files())
630 len(repo[x].files())
631 timer(d)
631 timer(d)
632 fm.end()
632 fm.end()
633
633
634 @command('perfrawfiles', formatteropts)
634 @command('perfrawfiles', formatteropts)
635 def perfrawfiles(ui, repo, x, **opts):
635 def perfrawfiles(ui, repo, x, **opts):
636 x = int(x)
636 x = int(x)
637 timer, fm = gettimer(ui, opts)
637 timer, fm = gettimer(ui, opts)
638 cl = repo.changelog
638 cl = repo.changelog
639 def d():
639 def d():
640 len(cl.read(x)[3])
640 len(cl.read(x)[3])
641 timer(d)
641 timer(d)
642 fm.end()
642 fm.end()
643
643
644 @command('perflookup', formatteropts)
644 @command('perflookup', formatteropts)
645 def perflookup(ui, repo, rev, **opts):
645 def perflookup(ui, repo, rev, **opts):
646 timer, fm = gettimer(ui, opts)
646 timer, fm = gettimer(ui, opts)
647 timer(lambda: len(repo.lookup(rev)))
647 timer(lambda: len(repo.lookup(rev)))
648 fm.end()
648 fm.end()
649
649
650 @command('perfrevrange', formatteropts)
650 @command('perfrevrange', formatteropts)
651 def perfrevrange(ui, repo, *specs, **opts):
651 def perfrevrange(ui, repo, *specs, **opts):
652 timer, fm = gettimer(ui, opts)
652 timer, fm = gettimer(ui, opts)
653 revrange = scmutil.revrange
653 revrange = scmutil.revrange
654 timer(lambda: len(revrange(repo, specs)))
654 timer(lambda: len(revrange(repo, specs)))
655 fm.end()
655 fm.end()
656
656
657 @command('perfnodelookup', formatteropts)
657 @command('perfnodelookup', formatteropts)
658 def perfnodelookup(ui, repo, rev, **opts):
658 def perfnodelookup(ui, repo, rev, **opts):
659 timer, fm = gettimer(ui, opts)
659 timer, fm = gettimer(ui, opts)
660 import mercurial.revlog
660 import mercurial.revlog
661 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
661 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
662 n = repo[rev].node()
662 n = repo[rev].node()
663 cl = mercurial.revlog.revlog(getsvfs(repo), "00changelog.i")
663 cl = mercurial.revlog.revlog(getsvfs(repo), "00changelog.i")
664 def d():
664 def d():
665 cl.rev(n)
665 cl.rev(n)
666 clearcaches(cl)
666 clearcaches(cl)
667 timer(d)
667 timer(d)
668 fm.end()
668 fm.end()
669
669
670 @command('perflog',
670 @command('perflog',
671 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
671 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
672 def perflog(ui, repo, rev=None, **opts):
672 def perflog(ui, repo, rev=None, **opts):
673 if rev is None:
673 if rev is None:
674 rev=[]
674 rev=[]
675 timer, fm = gettimer(ui, opts)
675 timer, fm = gettimer(ui, opts)
676 ui.pushbuffer()
676 ui.pushbuffer()
677 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
677 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
678 copies=opts.get('rename')))
678 copies=opts.get('rename')))
679 ui.popbuffer()
679 ui.popbuffer()
680 fm.end()
680 fm.end()
681
681
682 @command('perfmoonwalk', formatteropts)
682 @command('perfmoonwalk', formatteropts)
683 def perfmoonwalk(ui, repo, **opts):
683 def perfmoonwalk(ui, repo, **opts):
684 """benchmark walking the changelog backwards
684 """benchmark walking the changelog backwards
685
685
686 This also loads the changelog data for each revision in the changelog.
686 This also loads the changelog data for each revision in the changelog.
687 """
687 """
688 timer, fm = gettimer(ui, opts)
688 timer, fm = gettimer(ui, opts)
689 def moonwalk():
689 def moonwalk():
690 for i in xrange(len(repo), -1, -1):
690 for i in xrange(len(repo), -1, -1):
691 ctx = repo[i]
691 ctx = repo[i]
692 ctx.branch() # read changelog data (in addition to the index)
692 ctx.branch() # read changelog data (in addition to the index)
693 timer(moonwalk)
693 timer(moonwalk)
694 fm.end()
694 fm.end()
695
695
696 @command('perftemplating', formatteropts)
696 @command('perftemplating', formatteropts)
697 def perftemplating(ui, repo, rev=None, **opts):
697 def perftemplating(ui, repo, rev=None, **opts):
698 if rev is None:
698 if rev is None:
699 rev=[]
699 rev=[]
700 timer, fm = gettimer(ui, opts)
700 timer, fm = gettimer(ui, opts)
701 ui.pushbuffer()
701 ui.pushbuffer()
702 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
702 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
703 template='{date|shortdate} [{rev}:{node|short}]'
703 template='{date|shortdate} [{rev}:{node|short}]'
704 ' {author|person}: {desc|firstline}\n'))
704 ' {author|person}: {desc|firstline}\n'))
705 ui.popbuffer()
705 ui.popbuffer()
706 fm.end()
706 fm.end()
707
707
708 @command('perfcca', formatteropts)
708 @command('perfcca', formatteropts)
709 def perfcca(ui, repo, **opts):
709 def perfcca(ui, repo, **opts):
710 timer, fm = gettimer(ui, opts)
710 timer, fm = gettimer(ui, opts)
711 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
711 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
712 fm.end()
712 fm.end()
713
713
714 @command('perffncacheload', formatteropts)
714 @command('perffncacheload', formatteropts)
715 def perffncacheload(ui, repo, **opts):
715 def perffncacheload(ui, repo, **opts):
716 timer, fm = gettimer(ui, opts)
716 timer, fm = gettimer(ui, opts)
717 s = repo.store
717 s = repo.store
718 def d():
718 def d():
719 s.fncache._load()
719 s.fncache._load()
720 timer(d)
720 timer(d)
721 fm.end()
721 fm.end()
722
722
723 @command('perffncachewrite', formatteropts)
723 @command('perffncachewrite', formatteropts)
724 def perffncachewrite(ui, repo, **opts):
724 def perffncachewrite(ui, repo, **opts):
725 timer, fm = gettimer(ui, opts)
725 timer, fm = gettimer(ui, opts)
726 s = repo.store
726 s = repo.store
727 s.fncache._load()
727 s.fncache._load()
728 lock = repo.lock()
728 lock = repo.lock()
729 tr = repo.transaction('perffncachewrite')
729 tr = repo.transaction('perffncachewrite')
730 def d():
730 def d():
731 s.fncache._dirty = True
731 s.fncache._dirty = True
732 s.fncache.write(tr)
732 s.fncache.write(tr)
733 timer(d)
733 timer(d)
734 tr.close()
734 tr.close()
735 lock.release()
735 lock.release()
736 fm.end()
736 fm.end()
737
737
738 @command('perffncacheencode', formatteropts)
738 @command('perffncacheencode', formatteropts)
739 def perffncacheencode(ui, repo, **opts):
739 def perffncacheencode(ui, repo, **opts):
740 timer, fm = gettimer(ui, opts)
740 timer, fm = gettimer(ui, opts)
741 s = repo.store
741 s = repo.store
742 s.fncache._load()
742 s.fncache._load()
743 def d():
743 def d():
744 for p in s.fncache.entries:
744 for p in s.fncache.entries:
745 s.encode(p)
745 s.encode(p)
746 timer(d)
746 timer(d)
747 fm.end()
747 fm.end()
748
748
749 @command('perfdiffwd', formatteropts)
749 @command('perfdiffwd', formatteropts)
750 def perfdiffwd(ui, repo, **opts):
750 def perfdiffwd(ui, repo, **opts):
751 """Profile diff of working directory changes"""
751 """Profile diff of working directory changes"""
752 timer, fm = gettimer(ui, opts)
752 timer, fm = gettimer(ui, opts)
753 options = {
753 options = {
754 'w': 'ignore_all_space',
754 'w': 'ignore_all_space',
755 'b': 'ignore_space_change',
755 'b': 'ignore_space_change',
756 'B': 'ignore_blank_lines',
756 'B': 'ignore_blank_lines',
757 }
757 }
758
758
759 for diffopt in ('', 'w', 'b', 'B', 'wB'):
759 for diffopt in ('', 'w', 'b', 'B', 'wB'):
760 opts = dict((options[c], '1') for c in diffopt)
760 opts = dict((options[c], '1') for c in diffopt)
761 def d():
761 def d():
762 ui.pushbuffer()
762 ui.pushbuffer()
763 commands.diff(ui, repo, **opts)
763 commands.diff(ui, repo, **opts)
764 ui.popbuffer()
764 ui.popbuffer()
765 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
765 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
766 timer(d, title)
766 timer(d, title)
767 fm.end()
767 fm.end()
768
768
769 @command('perfrevlog', revlogopts + formatteropts +
769 @command('perfrevlog', revlogopts + formatteropts +
770 [('d', 'dist', 100, 'distance between the revisions'),
770 [('d', 'dist', 100, 'distance between the revisions'),
771 ('s', 'startrev', 0, 'revision to start reading at'),
771 ('s', 'startrev', 0, 'revision to start reading at'),
772 ('', 'reverse', False, 'read in reverse')],
772 ('', 'reverse', False, 'read in reverse')],
773 '-c|-m|FILE')
773 '-c|-m|FILE')
774 def perfrevlog(ui, repo, file_=None, startrev=0, reverse=False, **opts):
774 def perfrevlog(ui, repo, file_=None, startrev=0, reverse=False, **opts):
775 """Benchmark reading a series of revisions from a revlog.
775 """Benchmark reading a series of revisions from a revlog.
776
776
777 By default, we read every ``-d/--dist`` revision from 0 to tip of
777 By default, we read every ``-d/--dist`` revision from 0 to tip of
778 the specified revlog.
778 the specified revlog.
779
779
780 The start revision can be defined via ``-s/--startrev``.
780 The start revision can be defined via ``-s/--startrev``.
781 """
781 """
782 timer, fm = gettimer(ui, opts)
782 timer, fm = gettimer(ui, opts)
783 _len = getlen(ui)
783 _len = getlen(ui)
784
784
785 def d():
785 def d():
786 r = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts)
786 r = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts)
787
787
788 startrev = 0
788 startrev = 0
789 endrev = _len(r)
789 endrev = _len(r)
790 dist = opts['dist']
790 dist = opts['dist']
791
791
792 if reverse:
792 if reverse:
793 startrev, endrev = endrev, startrev
793 startrev, endrev = endrev, startrev
794 dist = -1 * dist
794 dist = -1 * dist
795
795
796 for x in xrange(startrev, endrev, dist):
796 for x in xrange(startrev, endrev, dist):
797 r.revision(r.node(x))
797 r.revision(r.node(x))
798
798
799 timer(d)
799 timer(d)
800 fm.end()
800 fm.end()
801
801
802 @command('perfrevlogrevision', revlogopts + formatteropts +
802 @command('perfrevlogrevision', revlogopts + formatteropts +
803 [('', 'cache', False, 'use caches instead of clearing')],
803 [('', 'cache', False, 'use caches instead of clearing')],
804 '-c|-m|FILE REV')
804 '-c|-m|FILE REV')
805 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
805 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
806 """Benchmark obtaining a revlog revision.
806 """Benchmark obtaining a revlog revision.
807
807
808 Obtaining a revlog revision consists of roughly the following steps:
808 Obtaining a revlog revision consists of roughly the following steps:
809
809
810 1. Compute the delta chain
810 1. Compute the delta chain
811 2. Obtain the raw chunks for that delta chain
811 2. Obtain the raw chunks for that delta chain
812 3. Decompress each raw chunk
812 3. Decompress each raw chunk
813 4. Apply binary patches to obtain fulltext
813 4. Apply binary patches to obtain fulltext
814 5. Verify hash of fulltext
814 5. Verify hash of fulltext
815
815
816 This command measures the time spent in each of these phases.
816 This command measures the time spent in each of these phases.
817 """
817 """
818 if opts.get('changelog') or opts.get('manifest'):
818 if opts.get('changelog') or opts.get('manifest'):
819 file_, rev = None, file_
819 file_, rev = None, file_
820 elif rev is None:
820 elif rev is None:
821 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
821 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
822
822
823 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
823 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
824 node = r.lookup(rev)
824 node = r.lookup(rev)
825 rev = r.rev(node)
825 rev = r.rev(node)
826
826
827 def dodeltachain(rev):
827 def dodeltachain(rev):
828 if not cache:
828 if not cache:
829 r.clearcaches()
829 r.clearcaches()
830 r._deltachain(rev)
830 r._deltachain(rev)
831
831
832 def doread(chain):
832 def doread(chain):
833 if not cache:
833 if not cache:
834 r.clearcaches()
834 r.clearcaches()
835 r._chunkraw(chain[0], chain[-1])
835 r._chunkraw(chain[0], chain[-1])
836
836
837 def dodecompress(data, chain):
837 def dodecompress(data, chain):
838 if not cache:
838 if not cache:
839 r.clearcaches()
839 r.clearcaches()
840
840
841 start = r.start
841 start = r.start
842 length = r.length
842 length = r.length
843 inline = r._inline
843 inline = r._inline
844 iosize = r._io.size
844 iosize = r._io.size
845 buffer = util.buffer
845 buffer = util.buffer
846 offset = start(chain[0])
846 offset = start(chain[0])
847
847
848 for rev in chain:
848 for rev in chain:
849 chunkstart = start(rev)
849 chunkstart = start(rev)
850 if inline:
850 if inline:
851 chunkstart += (rev + 1) * iosize
851 chunkstart += (rev + 1) * iosize
852 chunklength = length(rev)
852 chunklength = length(rev)
853 b = buffer(data, chunkstart - offset, chunklength)
853 b = buffer(data, chunkstart - offset, chunklength)
854 revlog.decompress(b)
854 revlog.decompress(b)
855
855
856 def dopatch(text, bins):
856 def dopatch(text, bins):
857 if not cache:
857 if not cache:
858 r.clearcaches()
858 r.clearcaches()
859 mdiff.patches(text, bins)
859 mdiff.patches(text, bins)
860
860
861 def dohash(text):
861 def dohash(text):
862 if not cache:
862 if not cache:
863 r.clearcaches()
863 r.clearcaches()
864 r._checkhash(text, node, rev)
864 r._checkhash(text, node, rev)
865
865
866 def dorevision():
866 def dorevision():
867 if not cache:
867 if not cache:
868 r.clearcaches()
868 r.clearcaches()
869 r.revision(node)
869 r.revision(node)
870
870
871 chain = r._deltachain(rev)[0]
871 chain = r._deltachain(rev)[0]
872 data = r._chunkraw(chain[0], chain[-1])[1]
872 data = r._chunkraw(chain[0], chain[-1])[1]
873 bins = r._chunks(chain)
873 bins = r._chunks(chain)
874 text = str(bins[0])
874 text = str(bins[0])
875 bins = bins[1:]
875 bins = bins[1:]
876 text = mdiff.patches(text, bins)
876 text = mdiff.patches(text, bins)
877
877
878 benches = [
878 benches = [
879 (lambda: dorevision(), 'full'),
879 (lambda: dorevision(), 'full'),
880 (lambda: dodeltachain(rev), 'deltachain'),
880 (lambda: dodeltachain(rev), 'deltachain'),
881 (lambda: doread(chain), 'read'),
881 (lambda: doread(chain), 'read'),
882 (lambda: dodecompress(data, chain), 'decompress'),
882 (lambda: dodecompress(data, chain), 'decompress'),
883 (lambda: dopatch(text, bins), 'patch'),
883 (lambda: dopatch(text, bins), 'patch'),
884 (lambda: dohash(text), 'hash'),
884 (lambda: dohash(text), 'hash'),
885 ]
885 ]
886
886
887 for fn, title in benches:
887 for fn, title in benches:
888 timer, fm = gettimer(ui, opts)
888 timer, fm = gettimer(ui, opts)
889 timer(fn, title=title)
889 timer(fn, title=title)
890 fm.end()
890 fm.end()
891
891
892 @command('perfrevset',
892 @command('perfrevset',
893 [('C', 'clear', False, 'clear volatile cache between each call.'),
893 [('C', 'clear', False, 'clear volatile cache between each call.'),
894 ('', 'contexts', False, 'obtain changectx for each revision')]
894 ('', 'contexts', False, 'obtain changectx for each revision')]
895 + formatteropts, "REVSET")
895 + formatteropts, "REVSET")
896 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
896 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
897 """benchmark the execution time of a revset
897 """benchmark the execution time of a revset
898
898
899 Use the --clean option if need to evaluate the impact of build volatile
899 Use the --clean option if need to evaluate the impact of build volatile
900 revisions set cache on the revset execution. Volatile cache hold filtered
900 revisions set cache on the revset execution. Volatile cache hold filtered
901 and obsolete related cache."""
901 and obsolete related cache."""
902 timer, fm = gettimer(ui, opts)
902 timer, fm = gettimer(ui, opts)
903 def d():
903 def d():
904 if clear:
904 if clear:
905 repo.invalidatevolatilesets()
905 repo.invalidatevolatilesets()
906 if contexts:
906 if contexts:
907 for ctx in repo.set(expr): pass
907 for ctx in repo.set(expr): pass
908 else:
908 else:
909 for r in repo.revs(expr): pass
909 for r in repo.revs(expr): pass
910 timer(d)
910 timer(d)
911 fm.end()
911 fm.end()
912
912
913 @command('perfvolatilesets', formatteropts)
913 @command('perfvolatilesets', formatteropts)
914 def perfvolatilesets(ui, repo, *names, **opts):
914 def perfvolatilesets(ui, repo, *names, **opts):
915 """benchmark the computation of various volatile set
915 """benchmark the computation of various volatile set
916
916
917 Volatile set computes element related to filtering and obsolescence."""
917 Volatile set computes element related to filtering and obsolescence."""
918 timer, fm = gettimer(ui, opts)
918 timer, fm = gettimer(ui, opts)
919 repo = repo.unfiltered()
919 repo = repo.unfiltered()
920
920
921 def getobs(name):
921 def getobs(name):
922 def d():
922 def d():
923 repo.invalidatevolatilesets()
923 repo.invalidatevolatilesets()
924 obsolete.getrevs(repo, name)
924 obsolete.getrevs(repo, name)
925 return d
925 return d
926
926
927 allobs = sorted(obsolete.cachefuncs)
927 allobs = sorted(obsolete.cachefuncs)
928 if names:
928 if names:
929 allobs = [n for n in allobs if n in names]
929 allobs = [n for n in allobs if n in names]
930
930
931 for name in allobs:
931 for name in allobs:
932 timer(getobs(name), title=name)
932 timer(getobs(name), title=name)
933
933
934 def getfiltered(name):
934 def getfiltered(name):
935 def d():
935 def d():
936 repo.invalidatevolatilesets()
936 repo.invalidatevolatilesets()
937 repoview.filterrevs(repo, name)
937 repoview.filterrevs(repo, name)
938 return d
938 return d
939
939
940 allfilter = sorted(repoview.filtertable)
940 allfilter = sorted(repoview.filtertable)
941 if names:
941 if names:
942 allfilter = [n for n in allfilter if n in names]
942 allfilter = [n for n in allfilter if n in names]
943
943
944 for name in allfilter:
944 for name in allfilter:
945 timer(getfiltered(name), title=name)
945 timer(getfiltered(name), title=name)
946 fm.end()
946 fm.end()
947
947
948 @command('perfbranchmap',
948 @command('perfbranchmap',
949 [('f', 'full', False,
949 [('f', 'full', False,
950 'Includes build time of subset'),
950 'Includes build time of subset'),
951 ] + formatteropts)
951 ] + formatteropts)
952 def perfbranchmap(ui, repo, full=False, **opts):
952 def perfbranchmap(ui, repo, full=False, **opts):
953 """benchmark the update of a branchmap
953 """benchmark the update of a branchmap
954
954
955 This benchmarks the full repo.branchmap() call with read and write disabled
955 This benchmarks the full repo.branchmap() call with read and write disabled
956 """
956 """
957 timer, fm = gettimer(ui, opts)
957 timer, fm = gettimer(ui, opts)
958 def getbranchmap(filtername):
958 def getbranchmap(filtername):
959 """generate a benchmark function for the filtername"""
959 """generate a benchmark function for the filtername"""
960 if filtername is None:
960 if filtername is None:
961 view = repo
961 view = repo
962 else:
962 else:
963 view = repo.filtered(filtername)
963 view = repo.filtered(filtername)
964 def d():
964 def d():
965 if full:
965 if full:
966 view._branchcaches.clear()
966 view._branchcaches.clear()
967 else:
967 else:
968 view._branchcaches.pop(filtername, None)
968 view._branchcaches.pop(filtername, None)
969 view.branchmap()
969 view.branchmap()
970 return d
970 return d
971 # add filter in smaller subset to bigger subset
971 # add filter in smaller subset to bigger subset
972 possiblefilters = set(repoview.filtertable)
972 possiblefilters = set(repoview.filtertable)
973 subsettable = getbranchmapsubsettable()
973 subsettable = getbranchmapsubsettable()
974 allfilters = []
974 allfilters = []
975 while possiblefilters:
975 while possiblefilters:
976 for name in possiblefilters:
976 for name in possiblefilters:
977 subset = subsettable.get(name)
977 subset = subsettable.get(name)
978 if subset not in possiblefilters:
978 if subset not in possiblefilters:
979 break
979 break
980 else:
980 else:
981 assert False, 'subset cycle %s!' % possiblefilters
981 assert False, 'subset cycle %s!' % possiblefilters
982 allfilters.append(name)
982 allfilters.append(name)
983 possiblefilters.remove(name)
983 possiblefilters.remove(name)
984
984
985 # warm the cache
985 # warm the cache
986 if not full:
986 if not full:
987 for name in allfilters:
987 for name in allfilters:
988 repo.filtered(name).branchmap()
988 repo.filtered(name).branchmap()
989 # add unfiltered
989 # add unfiltered
990 allfilters.append(None)
990 allfilters.append(None)
991
991
992 branchcacheread = safeattrsetter(branchmap, 'read')
992 branchcacheread = safeattrsetter(branchmap, 'read')
993 branchcachewrite = safeattrsetter(branchmap.branchcache, 'write')
993 branchcachewrite = safeattrsetter(branchmap.branchcache, 'write')
994 branchcacheread.set(lambda repo: None)
994 branchcacheread.set(lambda repo: None)
995 branchcachewrite.set(lambda bc, repo: None)
995 branchcachewrite.set(lambda bc, repo: None)
996 try:
996 try:
997 for name in allfilters:
997 for name in allfilters:
998 timer(getbranchmap(name), title=str(name))
998 timer(getbranchmap(name), title=str(name))
999 finally:
999 finally:
1000 branchcacheread.restore()
1000 branchcacheread.restore()
1001 branchcachewrite.restore()
1001 branchcachewrite.restore()
1002 fm.end()
1002 fm.end()
1003
1003
1004 @command('perfloadmarkers')
1004 @command('perfloadmarkers')
1005 def perfloadmarkers(ui, repo):
1005 def perfloadmarkers(ui, repo):
1006 """benchmark the time to parse the on-disk markers for a repo
1006 """benchmark the time to parse the on-disk markers for a repo
1007
1007
1008 Result is the number of markers in the repo."""
1008 Result is the number of markers in the repo."""
1009 timer, fm = gettimer(ui)
1009 timer, fm = gettimer(ui)
1010 svfs = getsvfs(repo)
1010 svfs = getsvfs(repo)
1011 timer(lambda: len(obsolete.obsstore(svfs)))
1011 timer(lambda: len(obsolete.obsstore(svfs)))
1012 fm.end()
1012 fm.end()
1013
1013
1014 @command('perflrucachedict', formatteropts +
1014 @command('perflrucachedict', formatteropts +
1015 [('', 'size', 4, 'size of cache'),
1015 [('', 'size', 4, 'size of cache'),
1016 ('', 'gets', 10000, 'number of key lookups'),
1016 ('', 'gets', 10000, 'number of key lookups'),
1017 ('', 'sets', 10000, 'number of key sets'),
1017 ('', 'sets', 10000, 'number of key sets'),
1018 ('', 'mixed', 10000, 'number of mixed mode operations'),
1018 ('', 'mixed', 10000, 'number of mixed mode operations'),
1019 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
1019 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
1020 norepo=True)
1020 norepo=True)
1021 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
1021 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
1022 mixedgetfreq=50, **opts):
1022 mixedgetfreq=50, **opts):
1023 def doinit():
1023 def doinit():
1024 for i in xrange(10000):
1024 for i in xrange(10000):
1025 util.lrucachedict(size)
1025 util.lrucachedict(size)
1026
1026
1027 values = []
1027 values = []
1028 for i in xrange(size):
1028 for i in xrange(size):
1029 values.append(random.randint(0, sys.maxint))
1029 values.append(random.randint(0, sys.maxint))
1030
1030
1031 # Get mode fills the cache and tests raw lookup performance with no
1031 # Get mode fills the cache and tests raw lookup performance with no
1032 # eviction.
1032 # eviction.
1033 getseq = []
1033 getseq = []
1034 for i in xrange(gets):
1034 for i in xrange(gets):
1035 getseq.append(random.choice(values))
1035 getseq.append(random.choice(values))
1036
1036
1037 def dogets():
1037 def dogets():
1038 d = util.lrucachedict(size)
1038 d = util.lrucachedict(size)
1039 for v in values:
1039 for v in values:
1040 d[v] = v
1040 d[v] = v
1041 for key in getseq:
1041 for key in getseq:
1042 value = d[key]
1042 value = d[key]
1043 value # silence pyflakes warning
1043 value # silence pyflakes warning
1044
1044
1045 # Set mode tests insertion speed with cache eviction.
1045 # Set mode tests insertion speed with cache eviction.
1046 setseq = []
1046 setseq = []
1047 for i in xrange(sets):
1047 for i in xrange(sets):
1048 setseq.append(random.randint(0, sys.maxint))
1048 setseq.append(random.randint(0, sys.maxint))
1049
1049
1050 def dosets():
1050 def dosets():
1051 d = util.lrucachedict(size)
1051 d = util.lrucachedict(size)
1052 for v in setseq:
1052 for v in setseq:
1053 d[v] = v
1053 d[v] = v
1054
1054
1055 # Mixed mode randomly performs gets and sets with eviction.
1055 # Mixed mode randomly performs gets and sets with eviction.
1056 mixedops = []
1056 mixedops = []
1057 for i in xrange(mixed):
1057 for i in xrange(mixed):
1058 r = random.randint(0, 100)
1058 r = random.randint(0, 100)
1059 if r < mixedgetfreq:
1059 if r < mixedgetfreq:
1060 op = 0
1060 op = 0
1061 else:
1061 else:
1062 op = 1
1062 op = 1
1063
1063
1064 mixedops.append((op, random.randint(0, size * 2)))
1064 mixedops.append((op, random.randint(0, size * 2)))
1065
1065
1066 def domixed():
1066 def domixed():
1067 d = util.lrucachedict(size)
1067 d = util.lrucachedict(size)
1068
1068
1069 for op, v in mixedops:
1069 for op, v in mixedops:
1070 if op == 0:
1070 if op == 0:
1071 try:
1071 try:
1072 d[v]
1072 d[v]
1073 except KeyError:
1073 except KeyError:
1074 pass
1074 pass
1075 else:
1075 else:
1076 d[v] = v
1076 d[v] = v
1077
1077
1078 benches = [
1078 benches = [
1079 (doinit, 'init'),
1079 (doinit, 'init'),
1080 (dogets, 'gets'),
1080 (dogets, 'gets'),
1081 (dosets, 'sets'),
1081 (dosets, 'sets'),
1082 (domixed, 'mixed')
1082 (domixed, 'mixed')
1083 ]
1083 ]
1084
1084
1085 for fn, title in benches:
1085 for fn, title in benches:
1086 timer, fm = gettimer(ui, opts)
1086 timer, fm = gettimer(ui, opts)
1087 timer(fn, title=title)
1087 timer(fn, title=title)
1088 fm.end()
1088 fm.end()
1089
1089
1090 def uisetup(ui):
1090 def uisetup(ui):
1091 if (util.safehasattr(cmdutil, 'openrevlog') and
1091 if (util.safehasattr(cmdutil, 'openrevlog') and
1092 not util.safehasattr(commands, 'debugrevlogopts')):
1092 not util.safehasattr(commands, 'debugrevlogopts')):
1093 # for "historical portability":
1093 # for "historical portability":
1094 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
1094 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
1095 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
1095 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
1096 # openrevlog() should cause failure, because it has been
1096 # openrevlog() should cause failure, because it has been
1097 # available since 3.5 (or 49c583ca48c4).
1097 # available since 3.5 (or 49c583ca48c4).
1098 def openrevlog(orig, repo, cmd, file_, opts):
1098 def openrevlog(orig, repo, cmd, file_, opts):
1099 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
1099 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
1100 raise error.Abort("This version doesn't support --dir option",
1100 raise error.Abort("This version doesn't support --dir option",
1101 hint="use 3.5 or later")
1101 hint="use 3.5 or later")
1102 return orig(repo, cmd, file_, opts)
1102 return orig(repo, cmd, file_, opts)
1103 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
1103 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
@@ -1,1427 +1,1439 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
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 '''Overridden Mercurial commands and functions for the largefiles extension'''
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 import copy
12 import copy
13 import os
13 import os
14
14
15 from mercurial.i18n import _
15 from mercurial.i18n import _
16
16
17 from mercurial import (
17 from mercurial import (
18 archival,
18 archival,
19 cmdutil,
19 cmdutil,
20 error,
20 error,
21 hg,
21 hg,
22 match as matchmod,
22 match as matchmod,
23 pathutil,
23 pathutil,
24 registrar,
24 registrar,
25 revset,
25 revset,
26 scmutil,
26 scmutil,
27 util,
27 util,
28 )
28 )
29
29
30 from . import (
30 from . import (
31 lfcommands,
31 lfcommands,
32 lfutil,
32 lfutil,
33 storefactory,
33 storefactory,
34 )
34 )
35
35
36 # -- Utility functions: commonly/repeatedly needed functionality ---------------
36 # -- Utility functions: commonly/repeatedly needed functionality ---------------
37
37
38 def composelargefilematcher(match, manifest):
38 def composelargefilematcher(match, manifest):
39 '''create a matcher that matches only the largefiles in the original
39 '''create a matcher that matches only the largefiles in the original
40 matcher'''
40 matcher'''
41 m = copy.copy(match)
41 m = copy.copy(match)
42 lfile = lambda f: lfutil.standin(f) in manifest
42 lfile = lambda f: lfutil.standin(f) in manifest
43 m._files = filter(lfile, m._files)
43 m._files = filter(lfile, m._files)
44 m._fileroots = set(m._files)
44 m._fileroots = set(m._files)
45 m._always = False
45 m._always = False
46 origmatchfn = m.matchfn
46 origmatchfn = m.matchfn
47 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
47 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
48 return m
48 return m
49
49
50 def composenormalfilematcher(match, manifest, exclude=None):
50 def composenormalfilematcher(match, manifest, exclude=None):
51 excluded = set()
51 excluded = set()
52 if exclude is not None:
52 if exclude is not None:
53 excluded.update(exclude)
53 excluded.update(exclude)
54
54
55 m = copy.copy(match)
55 m = copy.copy(match)
56 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
56 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
57 manifest or f in excluded)
57 manifest or f in excluded)
58 m._files = filter(notlfile, m._files)
58 m._files = filter(notlfile, m._files)
59 m._fileroots = set(m._files)
59 m._fileroots = set(m._files)
60 m._always = False
60 m._always = False
61 origmatchfn = m.matchfn
61 origmatchfn = m.matchfn
62 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
62 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
63 return m
63 return m
64
64
65 def installnormalfilesmatchfn(manifest):
65 def installnormalfilesmatchfn(manifest):
66 '''installmatchfn with a matchfn that ignores all largefiles'''
66 '''installmatchfn with a matchfn that ignores all largefiles'''
67 def overridematch(ctx, pats=(), opts=None, globbed=False,
67 def overridematch(ctx, pats=(), opts=None, globbed=False,
68 default='relpath', badfn=None):
68 default='relpath', badfn=None):
69 if opts is None:
69 if opts is None:
70 opts = {}
70 opts = {}
71 match = oldmatch(ctx, pats, opts, globbed, default, badfn=badfn)
71 match = oldmatch(ctx, pats, opts, globbed, default, badfn=badfn)
72 return composenormalfilematcher(match, manifest)
72 return composenormalfilematcher(match, manifest)
73 oldmatch = installmatchfn(overridematch)
73 oldmatch = installmatchfn(overridematch)
74
74
75 def installmatchfn(f):
75 def installmatchfn(f):
76 '''monkey patch the scmutil module with a custom match function.
76 '''monkey patch the scmutil module with a custom match function.
77 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
77 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
78 oldmatch = scmutil.match
78 oldmatch = scmutil.match
79 setattr(f, 'oldmatch', oldmatch)
79 setattr(f, 'oldmatch', oldmatch)
80 scmutil.match = f
80 scmutil.match = f
81 return oldmatch
81 return oldmatch
82
82
83 def restorematchfn():
83 def restorematchfn():
84 '''restores scmutil.match to what it was before installmatchfn
84 '''restores scmutil.match to what it was before installmatchfn
85 was called. no-op if scmutil.match is its original function.
85 was called. no-op if scmutil.match is its original function.
86
86
87 Note that n calls to installmatchfn will require n calls to
87 Note that n calls to installmatchfn will require n calls to
88 restore the original matchfn.'''
88 restore the original matchfn.'''
89 scmutil.match = getattr(scmutil.match, 'oldmatch')
89 scmutil.match = getattr(scmutil.match, 'oldmatch')
90
90
91 def installmatchandpatsfn(f):
91 def installmatchandpatsfn(f):
92 oldmatchandpats = scmutil.matchandpats
92 oldmatchandpats = scmutil.matchandpats
93 setattr(f, 'oldmatchandpats', oldmatchandpats)
93 setattr(f, 'oldmatchandpats', oldmatchandpats)
94 scmutil.matchandpats = f
94 scmutil.matchandpats = f
95 return oldmatchandpats
95 return oldmatchandpats
96
96
97 def restorematchandpatsfn():
97 def restorematchandpatsfn():
98 '''restores scmutil.matchandpats to what it was before
98 '''restores scmutil.matchandpats to what it was before
99 installmatchandpatsfn was called. No-op if scmutil.matchandpats
99 installmatchandpatsfn was called. No-op if scmutil.matchandpats
100 is its original function.
100 is its original function.
101
101
102 Note that n calls to installmatchandpatsfn will require n calls
102 Note that n calls to installmatchandpatsfn will require n calls
103 to restore the original matchfn.'''
103 to restore the original matchfn.'''
104 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
104 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
105 scmutil.matchandpats)
105 scmutil.matchandpats)
106
106
107 def addlargefiles(ui, repo, isaddremove, matcher, **opts):
107 def addlargefiles(ui, repo, isaddremove, matcher, **opts):
108 large = opts.get('large')
108 large = opts.get('large')
109 lfsize = lfutil.getminsize(
109 lfsize = lfutil.getminsize(
110 ui, lfutil.islfilesrepo(repo), opts.get('lfsize'))
110 ui, lfutil.islfilesrepo(repo), opts.get('lfsize'))
111
111
112 lfmatcher = None
112 lfmatcher = None
113 if lfutil.islfilesrepo(repo):
113 if lfutil.islfilesrepo(repo):
114 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
114 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
115 if lfpats:
115 if lfpats:
116 lfmatcher = matchmod.match(repo.root, '', list(lfpats))
116 lfmatcher = matchmod.match(repo.root, '', list(lfpats))
117
117
118 lfnames = []
118 lfnames = []
119 m = matcher
119 m = matcher
120
120
121 wctx = repo[None]
121 wctx = repo[None]
122 for f in repo.walk(matchmod.badmatch(m, lambda x, y: None)):
122 for f in repo.walk(matchmod.badmatch(m, lambda x, y: None)):
123 exact = m.exact(f)
123 exact = m.exact(f)
124 lfile = lfutil.standin(f) in wctx
124 lfile = lfutil.standin(f) in wctx
125 nfile = f in wctx
125 nfile = f in wctx
126 exists = lfile or nfile
126 exists = lfile or nfile
127
127
128 # addremove in core gets fancy with the name, add doesn't
128 # addremove in core gets fancy with the name, add doesn't
129 if isaddremove:
129 if isaddremove:
130 name = m.uipath(f)
130 name = m.uipath(f)
131 else:
131 else:
132 name = m.rel(f)
132 name = m.rel(f)
133
133
134 # Don't warn the user when they attempt to add a normal tracked file.
134 # Don't warn the user when they attempt to add a normal tracked file.
135 # The normal add code will do that for us.
135 # The normal add code will do that for us.
136 if exact and exists:
136 if exact and exists:
137 if lfile:
137 if lfile:
138 ui.warn(_('%s already a largefile\n') % name)
138 ui.warn(_('%s already a largefile\n') % name)
139 continue
139 continue
140
140
141 if (exact or not exists) and not lfutil.isstandin(f):
141 if (exact or not exists) and not lfutil.isstandin(f):
142 # In case the file was removed previously, but not committed
142 # In case the file was removed previously, but not committed
143 # (issue3507)
143 # (issue3507)
144 if not repo.wvfs.exists(f):
144 if not repo.wvfs.exists(f):
145 continue
145 continue
146
146
147 abovemin = (lfsize and
147 abovemin = (lfsize and
148 repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024)
148 repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024)
149 if large or abovemin or (lfmatcher and lfmatcher(f)):
149 if large or abovemin or (lfmatcher and lfmatcher(f)):
150 lfnames.append(f)
150 lfnames.append(f)
151 if ui.verbose or not exact:
151 if ui.verbose or not exact:
152 ui.status(_('adding %s as a largefile\n') % name)
152 ui.status(_('adding %s as a largefile\n') % name)
153
153
154 bad = []
154 bad = []
155
155
156 # Need to lock, otherwise there could be a race condition between
156 # Need to lock, otherwise there could be a race condition between
157 # when standins are created and added to the repo.
157 # when standins are created and added to the repo.
158 with repo.wlock():
158 with repo.wlock():
159 if not opts.get('dry_run'):
159 if not opts.get('dry_run'):
160 standins = []
160 standins = []
161 lfdirstate = lfutil.openlfdirstate(ui, repo)
161 lfdirstate = lfutil.openlfdirstate(ui, repo)
162 for f in lfnames:
162 for f in lfnames:
163 standinname = lfutil.standin(f)
163 standinname = lfutil.standin(f)
164 lfutil.writestandin(repo, standinname, hash='',
164 lfutil.writestandin(repo, standinname, hash='',
165 executable=lfutil.getexecutable(repo.wjoin(f)))
165 executable=lfutil.getexecutable(repo.wjoin(f)))
166 standins.append(standinname)
166 standins.append(standinname)
167 if lfdirstate[f] == 'r':
167 if lfdirstate[f] == 'r':
168 lfdirstate.normallookup(f)
168 lfdirstate.normallookup(f)
169 else:
169 else:
170 lfdirstate.add(f)
170 lfdirstate.add(f)
171 lfdirstate.write()
171 lfdirstate.write()
172 bad += [lfutil.splitstandin(f)
172 bad += [lfutil.splitstandin(f)
173 for f in repo[None].add(standins)
173 for f in repo[None].add(standins)
174 if f in m.files()]
174 if f in m.files()]
175
175
176 added = [f for f in lfnames if f not in bad]
176 added = [f for f in lfnames if f not in bad]
177 return added, bad
177 return added, bad
178
178
179 def removelargefiles(ui, repo, isaddremove, matcher, **opts):
179 def removelargefiles(ui, repo, isaddremove, matcher, **opts):
180 after = opts.get('after')
180 after = opts.get('after')
181 m = composelargefilematcher(matcher, repo[None].manifest())
181 m = composelargefilematcher(matcher, repo[None].manifest())
182 try:
182 try:
183 repo.lfstatus = True
183 repo.lfstatus = True
184 s = repo.status(match=m, clean=not isaddremove)
184 s = repo.status(match=m, clean=not isaddremove)
185 finally:
185 finally:
186 repo.lfstatus = False
186 repo.lfstatus = False
187 manifest = repo[None].manifest()
187 manifest = repo[None].manifest()
188 modified, added, deleted, clean = [[f for f in list
188 modified, added, deleted, clean = [[f for f in list
189 if lfutil.standin(f) in manifest]
189 if lfutil.standin(f) in manifest]
190 for list in (s.modified, s.added,
190 for list in (s.modified, s.added,
191 s.deleted, s.clean)]
191 s.deleted, s.clean)]
192
192
193 def warn(files, msg):
193 def warn(files, msg):
194 for f in files:
194 for f in files:
195 ui.warn(msg % m.rel(f))
195 ui.warn(msg % m.rel(f))
196 return int(len(files) > 0)
196 return int(len(files) > 0)
197
197
198 result = 0
198 result = 0
199
199
200 if after:
200 if after:
201 remove = deleted
201 remove = deleted
202 result = warn(modified + added + clean,
202 result = warn(modified + added + clean,
203 _('not removing %s: file still exists\n'))
203 _('not removing %s: file still exists\n'))
204 else:
204 else:
205 remove = deleted + clean
205 remove = deleted + clean
206 result = warn(modified, _('not removing %s: file is modified (use -f'
206 result = warn(modified, _('not removing %s: file is modified (use -f'
207 ' to force removal)\n'))
207 ' to force removal)\n'))
208 result = warn(added, _('not removing %s: file has been marked for add'
208 result = warn(added, _('not removing %s: file has been marked for add'
209 ' (use forget to undo)\n')) or result
209 ' (use forget to undo)\n')) or result
210
210
211 # Need to lock because standin files are deleted then removed from the
211 # Need to lock because standin files are deleted then removed from the
212 # repository and we could race in-between.
212 # repository and we could race in-between.
213 with repo.wlock():
213 with repo.wlock():
214 lfdirstate = lfutil.openlfdirstate(ui, repo)
214 lfdirstate = lfutil.openlfdirstate(ui, repo)
215 for f in sorted(remove):
215 for f in sorted(remove):
216 if ui.verbose or not m.exact(f):
216 if ui.verbose or not m.exact(f):
217 # addremove in core gets fancy with the name, remove doesn't
217 # addremove in core gets fancy with the name, remove doesn't
218 if isaddremove:
218 if isaddremove:
219 name = m.uipath(f)
219 name = m.uipath(f)
220 else:
220 else:
221 name = m.rel(f)
221 name = m.rel(f)
222 ui.status(_('removing %s\n') % name)
222 ui.status(_('removing %s\n') % name)
223
223
224 if not opts.get('dry_run'):
224 if not opts.get('dry_run'):
225 if not after:
225 if not after:
226 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
226 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
227
227
228 if opts.get('dry_run'):
228 if opts.get('dry_run'):
229 return result
229 return result
230
230
231 remove = [lfutil.standin(f) for f in remove]
231 remove = [lfutil.standin(f) for f in remove]
232 # If this is being called by addremove, let the original addremove
232 # If this is being called by addremove, let the original addremove
233 # function handle this.
233 # function handle this.
234 if not isaddremove:
234 if not isaddremove:
235 for f in remove:
235 for f in remove:
236 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
236 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
237 repo[None].forget(remove)
237 repo[None].forget(remove)
238
238
239 for f in remove:
239 for f in remove:
240 lfutil.synclfdirstate(repo, lfdirstate, lfutil.splitstandin(f),
240 lfutil.synclfdirstate(repo, lfdirstate, lfutil.splitstandin(f),
241 False)
241 False)
242
242
243 lfdirstate.write()
243 lfdirstate.write()
244
244
245 return result
245 return result
246
246
247 # For overriding mercurial.hgweb.webcommands so that largefiles will
247 # For overriding mercurial.hgweb.webcommands so that largefiles will
248 # appear at their right place in the manifests.
248 # appear at their right place in the manifests.
249 def decodepath(orig, path):
249 def decodepath(orig, path):
250 return lfutil.splitstandin(path) or path
250 return lfutil.splitstandin(path) or path
251
251
252 # -- Wrappers: modify existing commands --------------------------------
252 # -- Wrappers: modify existing commands --------------------------------
253
253
254 def overrideadd(orig, ui, repo, *pats, **opts):
254 def overrideadd(orig, ui, repo, *pats, **opts):
255 if opts.get('normal') and opts.get('large'):
255 if opts.get('normal') and opts.get('large'):
256 raise error.Abort(_('--normal cannot be used with --large'))
256 raise error.Abort(_('--normal cannot be used with --large'))
257 return orig(ui, repo, *pats, **opts)
257 return orig(ui, repo, *pats, **opts)
258
258
259 def cmdutiladd(orig, ui, repo, matcher, prefix, explicitonly, **opts):
259 def cmdutiladd(orig, ui, repo, matcher, prefix, explicitonly, **opts):
260 # The --normal flag short circuits this override
260 # The --normal flag short circuits this override
261 if opts.get('normal'):
261 if opts.get('normal'):
262 return orig(ui, repo, matcher, prefix, explicitonly, **opts)
262 return orig(ui, repo, matcher, prefix, explicitonly, **opts)
263
263
264 ladded, lbad = addlargefiles(ui, repo, False, matcher, **opts)
264 ladded, lbad = addlargefiles(ui, repo, False, matcher, **opts)
265 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest(),
265 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest(),
266 ladded)
266 ladded)
267 bad = orig(ui, repo, normalmatcher, prefix, explicitonly, **opts)
267 bad = orig(ui, repo, normalmatcher, prefix, explicitonly, **opts)
268
268
269 bad.extend(f for f in lbad)
269 bad.extend(f for f in lbad)
270 return bad
270 return bad
271
271
272 def cmdutilremove(orig, ui, repo, matcher, prefix, after, force, subrepos):
272 def cmdutilremove(orig, ui, repo, matcher, prefix, after, force, subrepos):
273 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
273 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
274 result = orig(ui, repo, normalmatcher, prefix, after, force, subrepos)
274 result = orig(ui, repo, normalmatcher, prefix, after, force, subrepos)
275 return removelargefiles(ui, repo, False, matcher, after=after,
275 return removelargefiles(ui, repo, False, matcher, after=after,
276 force=force) or result
276 force=force) or result
277
277
278 def overridestatusfn(orig, repo, rev2, **opts):
278 def overridestatusfn(orig, repo, rev2, **opts):
279 try:
279 try:
280 repo._repo.lfstatus = True
280 repo._repo.lfstatus = True
281 return orig(repo, rev2, **opts)
281 return orig(repo, rev2, **opts)
282 finally:
282 finally:
283 repo._repo.lfstatus = False
283 repo._repo.lfstatus = False
284
284
285 def overridestatus(orig, ui, repo, *pats, **opts):
285 def overridestatus(orig, ui, repo, *pats, **opts):
286 try:
286 try:
287 repo.lfstatus = True
287 repo.lfstatus = True
288 return orig(ui, repo, *pats, **opts)
288 return orig(ui, repo, *pats, **opts)
289 finally:
289 finally:
290 repo.lfstatus = False
290 repo.lfstatus = False
291
291
292 def overridedirty(orig, repo, ignoreupdate=False):
292 def overridedirty(orig, repo, ignoreupdate=False):
293 try:
293 try:
294 repo._repo.lfstatus = True
294 repo._repo.lfstatus = True
295 return orig(repo, ignoreupdate)
295 return orig(repo, ignoreupdate)
296 finally:
296 finally:
297 repo._repo.lfstatus = False
297 repo._repo.lfstatus = False
298
298
299 def overridelog(orig, ui, repo, *pats, **opts):
299 def overridelog(orig, ui, repo, *pats, **opts):
300 def overridematchandpats(ctx, pats=(), opts=None, globbed=False,
300 def overridematchandpats(ctx, pats=(), opts=None, globbed=False,
301 default='relpath', badfn=None):
301 default='relpath', badfn=None):
302 """Matcher that merges root directory with .hglf, suitable for log.
302 """Matcher that merges root directory with .hglf, suitable for log.
303 It is still possible to match .hglf directly.
303 It is still possible to match .hglf directly.
304 For any listed files run log on the standin too.
304 For any listed files run log on the standin too.
305 matchfn tries both the given filename and with .hglf stripped.
305 matchfn tries both the given filename and with .hglf stripped.
306 """
306 """
307 if opts is None:
307 if opts is None:
308 opts = {}
308 opts = {}
309 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default,
309 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default,
310 badfn=badfn)
310 badfn=badfn)
311 m, p = copy.copy(matchandpats)
311 m, p = copy.copy(matchandpats)
312
312
313 if m.always():
313 if m.always():
314 # We want to match everything anyway, so there's no benefit trying
314 # We want to match everything anyway, so there's no benefit trying
315 # to add standins.
315 # to add standins.
316 return matchandpats
316 return matchandpats
317
317
318 pats = set(p)
318 pats = set(p)
319
319
320 def fixpats(pat, tostandin=lfutil.standin):
320 def fixpats(pat, tostandin=lfutil.standin):
321 if pat.startswith('set:'):
321 if pat.startswith('set:'):
322 return pat
322 return pat
323
323
324 kindpat = matchmod._patsplit(pat, None)
324 kindpat = matchmod._patsplit(pat, None)
325
325
326 if kindpat[0] is not None:
326 if kindpat[0] is not None:
327 return kindpat[0] + ':' + tostandin(kindpat[1])
327 return kindpat[0] + ':' + tostandin(kindpat[1])
328 return tostandin(kindpat[1])
328 return tostandin(kindpat[1])
329
329
330 if m._cwd:
330 if m._cwd:
331 hglf = lfutil.shortname
331 hglf = lfutil.shortname
332 back = util.pconvert(m.rel(hglf)[:-len(hglf)])
332 back = util.pconvert(m.rel(hglf)[:-len(hglf)])
333
333
334 def tostandin(f):
334 def tostandin(f):
335 # The file may already be a standin, so truncate the back
335 # The file may already be a standin, so truncate the back
336 # prefix and test before mangling it. This avoids turning
336 # prefix and test before mangling it. This avoids turning
337 # 'glob:../.hglf/foo*' into 'glob:../.hglf/../.hglf/foo*'.
337 # 'glob:../.hglf/foo*' into 'glob:../.hglf/../.hglf/foo*'.
338 if f.startswith(back) and lfutil.splitstandin(f[len(back):]):
338 if f.startswith(back) and lfutil.splitstandin(f[len(back):]):
339 return f
339 return f
340
340
341 # An absolute path is from outside the repo, so truncate the
341 # An absolute path is from outside the repo, so truncate the
342 # path to the root before building the standin. Otherwise cwd
342 # path to the root before building the standin. Otherwise cwd
343 # is somewhere in the repo, relative to root, and needs to be
343 # is somewhere in the repo, relative to root, and needs to be
344 # prepended before building the standin.
344 # prepended before building the standin.
345 if os.path.isabs(m._cwd):
345 if os.path.isabs(m._cwd):
346 f = f[len(back):]
346 f = f[len(back):]
347 else:
347 else:
348 f = m._cwd + '/' + f
348 f = m._cwd + '/' + f
349 return back + lfutil.standin(f)
349 return back + lfutil.standin(f)
350
350
351 pats.update(fixpats(f, tostandin) for f in p)
351 pats.update(fixpats(f, tostandin) for f in p)
352 else:
352 else:
353 def tostandin(f):
353 def tostandin(f):
354 if lfutil.splitstandin(f):
354 if lfutil.splitstandin(f):
355 return f
355 return f
356 return lfutil.standin(f)
356 return lfutil.standin(f)
357 pats.update(fixpats(f, tostandin) for f in p)
357 pats.update(fixpats(f, tostandin) for f in p)
358
358
359 for i in range(0, len(m._files)):
359 for i in range(0, len(m._files)):
360 # Don't add '.hglf' to m.files, since that is already covered by '.'
360 # Don't add '.hglf' to m.files, since that is already covered by '.'
361 if m._files[i] == '.':
361 if m._files[i] == '.':
362 continue
362 continue
363 standin = lfutil.standin(m._files[i])
363 standin = lfutil.standin(m._files[i])
364 # If the "standin" is a directory, append instead of replace to
364 # If the "standin" is a directory, append instead of replace to
365 # support naming a directory on the command line with only
365 # support naming a directory on the command line with only
366 # largefiles. The original directory is kept to support normal
366 # largefiles. The original directory is kept to support normal
367 # files.
367 # files.
368 if standin in repo[ctx.node()]:
368 if standin in repo[ctx.node()]:
369 m._files[i] = standin
369 m._files[i] = standin
370 elif m._files[i] not in repo[ctx.node()] \
370 elif m._files[i] not in repo[ctx.node()] \
371 and repo.wvfs.isdir(standin):
371 and repo.wvfs.isdir(standin):
372 m._files.append(standin)
372 m._files.append(standin)
373
373
374 m._fileroots = set(m._files)
374 m._fileroots = set(m._files)
375 m._always = False
375 m._always = False
376 origmatchfn = m.matchfn
376 origmatchfn = m.matchfn
377 def lfmatchfn(f):
377 def lfmatchfn(f):
378 lf = lfutil.splitstandin(f)
378 lf = lfutil.splitstandin(f)
379 if lf is not None and origmatchfn(lf):
379 if lf is not None and origmatchfn(lf):
380 return True
380 return True
381 r = origmatchfn(f)
381 r = origmatchfn(f)
382 return r
382 return r
383 m.matchfn = lfmatchfn
383 m.matchfn = lfmatchfn
384
384
385 ui.debug('updated patterns: %s\n' % sorted(pats))
385 ui.debug('updated patterns: %s\n' % sorted(pats))
386 return m, pats
386 return m, pats
387
387
388 # For hg log --patch, the match object is used in two different senses:
388 # For hg log --patch, the match object is used in two different senses:
389 # (1) to determine what revisions should be printed out, and
389 # (1) to determine what revisions should be printed out, and
390 # (2) to determine what files to print out diffs for.
390 # (2) to determine what files to print out diffs for.
391 # The magic matchandpats override should be used for case (1) but not for
391 # The magic matchandpats override should be used for case (1) but not for
392 # case (2).
392 # case (2).
393 def overridemakelogfilematcher(repo, pats, opts, badfn=None):
393 def overridemakelogfilematcher(repo, pats, opts, badfn=None):
394 wctx = repo[None]
394 wctx = repo[None]
395 match, pats = oldmatchandpats(wctx, pats, opts, badfn=badfn)
395 match, pats = oldmatchandpats(wctx, pats, opts, badfn=badfn)
396 return lambda rev: match
396 return lambda rev: match
397
397
398 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
398 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
399 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
399 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
400 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
400 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
401
401
402 try:
402 try:
403 return orig(ui, repo, *pats, **opts)
403 return orig(ui, repo, *pats, **opts)
404 finally:
404 finally:
405 restorematchandpatsfn()
405 restorematchandpatsfn()
406 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
406 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
407
407
408 def overrideverify(orig, ui, repo, *pats, **opts):
408 def overrideverify(orig, ui, repo, *pats, **opts):
409 large = opts.pop('large', False)
409 large = opts.pop('large', False)
410 all = opts.pop('lfa', False)
410 all = opts.pop('lfa', False)
411 contents = opts.pop('lfc', False)
411 contents = opts.pop('lfc', False)
412
412
413 result = orig(ui, repo, *pats, **opts)
413 result = orig(ui, repo, *pats, **opts)
414 if large or all or contents:
414 if large or all or contents:
415 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
415 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
416 return result
416 return result
417
417
418 def overridedebugstate(orig, ui, repo, *pats, **opts):
418 def overridedebugstate(orig, ui, repo, *pats, **opts):
419 large = opts.pop('large', False)
419 large = opts.pop('large', False)
420 if large:
420 if large:
421 class fakerepo(object):
421 class fakerepo(object):
422 dirstate = lfutil.openlfdirstate(ui, repo)
422 dirstate = lfutil.openlfdirstate(ui, repo)
423 orig(ui, fakerepo, *pats, **opts)
423 orig(ui, fakerepo, *pats, **opts)
424 else:
424 else:
425 orig(ui, repo, *pats, **opts)
425 orig(ui, repo, *pats, **opts)
426
426
427 # Before starting the manifest merge, merge.updates will call
427 # Before starting the manifest merge, merge.updates will call
428 # _checkunknownfile to check if there are any files in the merged-in
428 # _checkunknownfile to check if there are any files in the merged-in
429 # changeset that collide with unknown files in the working copy.
429 # changeset that collide with unknown files in the working copy.
430 #
430 #
431 # The largefiles are seen as unknown, so this prevents us from merging
431 # The largefiles are seen as unknown, so this prevents us from merging
432 # in a file 'foo' if we already have a largefile with the same name.
432 # in a file 'foo' if we already have a largefile with the same name.
433 #
433 #
434 # The overridden function filters the unknown files by removing any
434 # The overridden function filters the unknown files by removing any
435 # largefiles. This makes the merge proceed and we can then handle this
435 # largefiles. This makes the merge proceed and we can then handle this
436 # case further in the overridden calculateupdates function below.
436 # case further in the overridden calculateupdates function below.
437 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
437 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
438 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
438 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
439 return False
439 return False
440 return origfn(repo, wctx, mctx, f, f2)
440 return origfn(repo, wctx, mctx, f, f2)
441
441
442 # The manifest merge handles conflicts on the manifest level. We want
442 # The manifest merge handles conflicts on the manifest level. We want
443 # to handle changes in largefile-ness of files at this level too.
443 # to handle changes in largefile-ness of files at this level too.
444 #
444 #
445 # The strategy is to run the original calculateupdates and then process
445 # The strategy is to run the original calculateupdates and then process
446 # the action list it outputs. There are two cases we need to deal with:
446 # the action list it outputs. There are two cases we need to deal with:
447 #
447 #
448 # 1. Normal file in p1, largefile in p2. Here the largefile is
448 # 1. Normal file in p1, largefile in p2. Here the largefile is
449 # detected via its standin file, which will enter the working copy
449 # detected via its standin file, which will enter the working copy
450 # with a "get" action. It is not "merge" since the standin is all
450 # with a "get" action. It is not "merge" since the standin is all
451 # Mercurial is concerned with at this level -- the link to the
451 # Mercurial is concerned with at this level -- the link to the
452 # existing normal file is not relevant here.
452 # existing normal file is not relevant here.
453 #
453 #
454 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
454 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
455 # since the largefile will be present in the working copy and
455 # since the largefile will be present in the working copy and
456 # different from the normal file in p2. Mercurial therefore
456 # different from the normal file in p2. Mercurial therefore
457 # triggers a merge action.
457 # triggers a merge action.
458 #
458 #
459 # In both cases, we prompt the user and emit new actions to either
459 # In both cases, we prompt the user and emit new actions to either
460 # remove the standin (if the normal file was kept) or to remove the
460 # remove the standin (if the normal file was kept) or to remove the
461 # normal file and get the standin (if the largefile was kept). The
461 # normal file and get the standin (if the largefile was kept). The
462 # default prompt answer is to use the largefile version since it was
462 # default prompt answer is to use the largefile version since it was
463 # presumably changed on purpose.
463 # presumably changed on purpose.
464 #
464 #
465 # Finally, the merge.applyupdates function will then take care of
465 # Finally, the merge.applyupdates function will then take care of
466 # writing the files into the working copy and lfcommands.updatelfiles
466 # writing the files into the working copy and lfcommands.updatelfiles
467 # will update the largefiles.
467 # will update the largefiles.
468 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
468 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
469 acceptremote, *args, **kwargs):
469 acceptremote, *args, **kwargs):
470 overwrite = force and not branchmerge
470 overwrite = force and not branchmerge
471 actions, diverge, renamedelete = origfn(
471 actions, diverge, renamedelete = origfn(
472 repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs)
472 repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs)
473
473
474 if overwrite:
474 if overwrite:
475 return actions, diverge, renamedelete
475 return actions, diverge, renamedelete
476
476
477 # Convert to dictionary with filename as key and action as value.
477 # Convert to dictionary with filename as key and action as value.
478 lfiles = set()
478 lfiles = set()
479 for f in actions:
479 for f in actions:
480 splitstandin = lfutil.splitstandin(f)
480 splitstandin = lfutil.splitstandin(f)
481 if splitstandin in p1:
481 if splitstandin in p1:
482 lfiles.add(splitstandin)
482 lfiles.add(splitstandin)
483 elif lfutil.standin(f) in p1:
483 elif lfutil.standin(f) in p1:
484 lfiles.add(f)
484 lfiles.add(f)
485
485
486 for lfile in sorted(lfiles):
486 for lfile in sorted(lfiles):
487 standin = lfutil.standin(lfile)
487 standin = lfutil.standin(lfile)
488 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
488 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
489 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
489 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
490 if sm in ('g', 'dc') and lm != 'r':
490 if sm in ('g', 'dc') and lm != 'r':
491 if sm == 'dc':
491 if sm == 'dc':
492 f1, f2, fa, move, anc = sargs
492 f1, f2, fa, move, anc = sargs
493 sargs = (p2[f2].flags(), False)
493 sargs = (p2[f2].flags(), False)
494 # Case 1: normal file in the working copy, largefile in
494 # Case 1: normal file in the working copy, largefile in
495 # the second parent
495 # the second parent
496 usermsg = _('remote turned local normal file %s into a largefile\n'
496 usermsg = _('remote turned local normal file %s into a largefile\n'
497 'use (l)argefile or keep (n)ormal file?'
497 'use (l)argefile or keep (n)ormal file?'
498 '$$ &Largefile $$ &Normal file') % lfile
498 '$$ &Largefile $$ &Normal file') % lfile
499 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
499 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
500 actions[lfile] = ('r', None, 'replaced by standin')
500 actions[lfile] = ('r', None, 'replaced by standin')
501 actions[standin] = ('g', sargs, 'replaces standin')
501 actions[standin] = ('g', sargs, 'replaces standin')
502 else: # keep local normal file
502 else: # keep local normal file
503 actions[lfile] = ('k', None, 'replaces standin')
503 actions[lfile] = ('k', None, 'replaces standin')
504 if branchmerge:
504 if branchmerge:
505 actions[standin] = ('k', None, 'replaced by non-standin')
505 actions[standin] = ('k', None, 'replaced by non-standin')
506 else:
506 else:
507 actions[standin] = ('r', None, 'replaced by non-standin')
507 actions[standin] = ('r', None, 'replaced by non-standin')
508 elif lm in ('g', 'dc') and sm != 'r':
508 elif lm in ('g', 'dc') and sm != 'r':
509 if lm == 'dc':
509 if lm == 'dc':
510 f1, f2, fa, move, anc = largs
510 f1, f2, fa, move, anc = largs
511 largs = (p2[f2].flags(), False)
511 largs = (p2[f2].flags(), False)
512 # Case 2: largefile in the working copy, normal file in
512 # Case 2: largefile in the working copy, normal file in
513 # the second parent
513 # the second parent
514 usermsg = _('remote turned local largefile %s into a normal file\n'
514 usermsg = _('remote turned local largefile %s into a normal file\n'
515 'keep (l)argefile or use (n)ormal file?'
515 'keep (l)argefile or use (n)ormal file?'
516 '$$ &Largefile $$ &Normal file') % lfile
516 '$$ &Largefile $$ &Normal file') % lfile
517 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
517 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
518 if branchmerge:
518 if branchmerge:
519 # largefile can be restored from standin safely
519 # largefile can be restored from standin safely
520 actions[lfile] = ('k', None, 'replaced by standin')
520 actions[lfile] = ('k', None, 'replaced by standin')
521 actions[standin] = ('k', None, 'replaces standin')
521 actions[standin] = ('k', None, 'replaces standin')
522 else:
522 else:
523 # "lfile" should be marked as "removed" without
523 # "lfile" should be marked as "removed" without
524 # removal of itself
524 # removal of itself
525 actions[lfile] = ('lfmr', None,
525 actions[lfile] = ('lfmr', None,
526 'forget non-standin largefile')
526 'forget non-standin largefile')
527
527
528 # linear-merge should treat this largefile as 're-added'
528 # linear-merge should treat this largefile as 're-added'
529 actions[standin] = ('a', None, 'keep standin')
529 actions[standin] = ('a', None, 'keep standin')
530 else: # pick remote normal file
530 else: # pick remote normal file
531 actions[lfile] = ('g', largs, 'replaces standin')
531 actions[lfile] = ('g', largs, 'replaces standin')
532 actions[standin] = ('r', None, 'replaced by non-standin')
532 actions[standin] = ('r', None, 'replaced by non-standin')
533
533
534 return actions, diverge, renamedelete
534 return actions, diverge, renamedelete
535
535
536 def mergerecordupdates(orig, repo, actions, branchmerge):
536 def mergerecordupdates(orig, repo, actions, branchmerge):
537 if 'lfmr' in actions:
537 if 'lfmr' in actions:
538 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
538 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
539 for lfile, args, msg in actions['lfmr']:
539 for lfile, args, msg in actions['lfmr']:
540 # this should be executed before 'orig', to execute 'remove'
540 # this should be executed before 'orig', to execute 'remove'
541 # before all other actions
541 # before all other actions
542 repo.dirstate.remove(lfile)
542 repo.dirstate.remove(lfile)
543 # make sure lfile doesn't get synclfdirstate'd as normal
543 # make sure lfile doesn't get synclfdirstate'd as normal
544 lfdirstate.add(lfile)
544 lfdirstate.add(lfile)
545 lfdirstate.write()
545 lfdirstate.write()
546
546
547 return orig(repo, actions, branchmerge)
547 return orig(repo, actions, branchmerge)
548
548
549 # Override filemerge to prompt the user about how they wish to merge
549 # Override filemerge to prompt the user about how they wish to merge
550 # largefiles. This will handle identical edits without prompting the user.
550 # largefiles. This will handle identical edits without prompting the user.
551 def overridefilemerge(origfn, premerge, repo, mynode, orig, fcd, fco, fca,
551 def overridefilemerge(origfn, premerge, repo, mynode, orig, fcd, fco, fca,
552 labels=None):
552 labels=None):
553 if not lfutil.isstandin(orig) or fcd.isabsent() or fco.isabsent():
553 if not lfutil.isstandin(orig) or fcd.isabsent() or fco.isabsent():
554 return origfn(premerge, repo, mynode, orig, fcd, fco, fca,
554 return origfn(premerge, repo, mynode, orig, fcd, fco, fca,
555 labels=labels)
555 labels=labels)
556
556
557 ahash = fca.data().strip().lower()
557 ahash = fca.data().strip().lower()
558 dhash = fcd.data().strip().lower()
558 dhash = fcd.data().strip().lower()
559 ohash = fco.data().strip().lower()
559 ohash = fco.data().strip().lower()
560 if (ohash != ahash and
560 if (ohash != ahash and
561 ohash != dhash and
561 ohash != dhash and
562 (dhash == ahash or
562 (dhash == ahash or
563 repo.ui.promptchoice(
563 repo.ui.promptchoice(
564 _('largefile %s has a merge conflict\nancestor was %s\n'
564 _('largefile %s has a merge conflict\nancestor was %s\n'
565 'keep (l)ocal %s or\ntake (o)ther %s?'
565 'keep (l)ocal %s or\ntake (o)ther %s?'
566 '$$ &Local $$ &Other') %
566 '$$ &Local $$ &Other') %
567 (lfutil.splitstandin(orig), ahash, dhash, ohash),
567 (lfutil.splitstandin(orig), ahash, dhash, ohash),
568 0) == 1)):
568 0) == 1)):
569 repo.wwrite(fcd.path(), fco.data(), fco.flags())
569 repo.wwrite(fcd.path(), fco.data(), fco.flags())
570 return True, 0, False
570 return True, 0, False
571
571
572 def copiespathcopies(orig, ctx1, ctx2, match=None):
572 def copiespathcopies(orig, ctx1, ctx2, match=None):
573 copies = orig(ctx1, ctx2, match=match)
573 copies = orig(ctx1, ctx2, match=match)
574 updated = {}
574 updated = {}
575
575
576 for k, v in copies.iteritems():
576 for k, v in copies.iteritems():
577 updated[lfutil.splitstandin(k) or k] = lfutil.splitstandin(v) or v
577 updated[lfutil.splitstandin(k) or k] = lfutil.splitstandin(v) or v
578
578
579 return updated
579 return updated
580
580
581 # Copy first changes the matchers to match standins instead of
581 # Copy first changes the matchers to match standins instead of
582 # largefiles. Then it overrides util.copyfile in that function it
582 # largefiles. Then it overrides util.copyfile in that function it
583 # checks if the destination largefile already exists. It also keeps a
583 # checks if the destination largefile already exists. It also keeps a
584 # list of copied files so that the largefiles can be copied and the
584 # list of copied files so that the largefiles can be copied and the
585 # dirstate updated.
585 # dirstate updated.
586 def overridecopy(orig, ui, repo, pats, opts, rename=False):
586 def overridecopy(orig, ui, repo, pats, opts, rename=False):
587 # doesn't remove largefile on rename
587 # doesn't remove largefile on rename
588 if len(pats) < 2:
588 if len(pats) < 2:
589 # this isn't legal, let the original function deal with it
589 # this isn't legal, let the original function deal with it
590 return orig(ui, repo, pats, opts, rename)
590 return orig(ui, repo, pats, opts, rename)
591
591
592 # This could copy both lfiles and normal files in one command,
592 # This could copy both lfiles and normal files in one command,
593 # but we don't want to do that. First replace their matcher to
593 # but we don't want to do that. First replace their matcher to
594 # only match normal files and run it, then replace it to just
594 # only match normal files and run it, then replace it to just
595 # match largefiles and run it again.
595 # match largefiles and run it again.
596 nonormalfiles = False
596 nonormalfiles = False
597 nolfiles = False
597 nolfiles = False
598 installnormalfilesmatchfn(repo[None].manifest())
598 installnormalfilesmatchfn(repo[None].manifest())
599 try:
599 try:
600 result = orig(ui, repo, pats, opts, rename)
600 result = orig(ui, repo, pats, opts, rename)
601 except error.Abort as e:
601 except error.Abort as e:
602 if str(e) != _('no files to copy'):
602 if str(e) != _('no files to copy'):
603 raise e
603 raise e
604 else:
604 else:
605 nonormalfiles = True
605 nonormalfiles = True
606 result = 0
606 result = 0
607 finally:
607 finally:
608 restorematchfn()
608 restorematchfn()
609
609
610 # The first rename can cause our current working directory to be removed.
610 # The first rename can cause our current working directory to be removed.
611 # In that case there is nothing left to copy/rename so just quit.
611 # In that case there is nothing left to copy/rename so just quit.
612 try:
612 try:
613 repo.getcwd()
613 repo.getcwd()
614 except OSError:
614 except OSError:
615 return result
615 return result
616
616
617 def makestandin(relpath):
617 def makestandin(relpath):
618 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
618 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
619 return repo.wvfs.join(lfutil.standin(path))
619 return repo.wvfs.join(lfutil.standin(path))
620
620
621 fullpats = scmutil.expandpats(pats)
621 fullpats = scmutil.expandpats(pats)
622 dest = fullpats[-1]
622 dest = fullpats[-1]
623
623
624 if os.path.isdir(dest):
624 if os.path.isdir(dest):
625 if not os.path.isdir(makestandin(dest)):
625 if not os.path.isdir(makestandin(dest)):
626 os.makedirs(makestandin(dest))
626 os.makedirs(makestandin(dest))
627
627
628 try:
628 try:
629 # When we call orig below it creates the standins but we don't add
629 # When we call orig below it creates the standins but we don't add
630 # them to the dir state until later so lock during that time.
630 # them to the dir state until later so lock during that time.
631 wlock = repo.wlock()
631 wlock = repo.wlock()
632
632
633 manifest = repo[None].manifest()
633 manifest = repo[None].manifest()
634 def overridematch(ctx, pats=(), opts=None, globbed=False,
634 def overridematch(ctx, pats=(), opts=None, globbed=False,
635 default='relpath', badfn=None):
635 default='relpath', badfn=None):
636 if opts is None:
636 if opts is None:
637 opts = {}
637 opts = {}
638 newpats = []
638 newpats = []
639 # The patterns were previously mangled to add the standin
639 # The patterns were previously mangled to add the standin
640 # directory; we need to remove that now
640 # directory; we need to remove that now
641 for pat in pats:
641 for pat in pats:
642 if matchmod.patkind(pat) is None and lfutil.shortname in pat:
642 if matchmod.patkind(pat) is None and lfutil.shortname in pat:
643 newpats.append(pat.replace(lfutil.shortname, ''))
643 newpats.append(pat.replace(lfutil.shortname, ''))
644 else:
644 else:
645 newpats.append(pat)
645 newpats.append(pat)
646 match = oldmatch(ctx, newpats, opts, globbed, default, badfn=badfn)
646 match = oldmatch(ctx, newpats, opts, globbed, default, badfn=badfn)
647 m = copy.copy(match)
647 m = copy.copy(match)
648 lfile = lambda f: lfutil.standin(f) in manifest
648 lfile = lambda f: lfutil.standin(f) in manifest
649 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
649 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
650 m._fileroots = set(m._files)
650 m._fileroots = set(m._files)
651 origmatchfn = m.matchfn
651 origmatchfn = m.matchfn
652 m.matchfn = lambda f: (lfutil.isstandin(f) and
652 m.matchfn = lambda f: (lfutil.isstandin(f) and
653 (f in manifest) and
653 (f in manifest) and
654 origmatchfn(lfutil.splitstandin(f)) or
654 origmatchfn(lfutil.splitstandin(f)) or
655 None)
655 None)
656 return m
656 return m
657 oldmatch = installmatchfn(overridematch)
657 oldmatch = installmatchfn(overridematch)
658 listpats = []
658 listpats = []
659 for pat in pats:
659 for pat in pats:
660 if matchmod.patkind(pat) is not None:
660 if matchmod.patkind(pat) is not None:
661 listpats.append(pat)
661 listpats.append(pat)
662 else:
662 else:
663 listpats.append(makestandin(pat))
663 listpats.append(makestandin(pat))
664
664
665 try:
665 try:
666 origcopyfile = util.copyfile
666 origcopyfile = util.copyfile
667 copiedfiles = []
667 copiedfiles = []
668 def overridecopyfile(src, dest):
668 def overridecopyfile(src, dest):
669 if (lfutil.shortname in src and
669 if (lfutil.shortname in src and
670 dest.startswith(repo.wjoin(lfutil.shortname))):
670 dest.startswith(repo.wjoin(lfutil.shortname))):
671 destlfile = dest.replace(lfutil.shortname, '')
671 destlfile = dest.replace(lfutil.shortname, '')
672 if not opts['force'] and os.path.exists(destlfile):
672 if not opts['force'] and os.path.exists(destlfile):
673 raise IOError('',
673 raise IOError('',
674 _('destination largefile already exists'))
674 _('destination largefile already exists'))
675 copiedfiles.append((src, dest))
675 copiedfiles.append((src, dest))
676 origcopyfile(src, dest)
676 origcopyfile(src, dest)
677
677
678 util.copyfile = overridecopyfile
678 util.copyfile = overridecopyfile
679 result += orig(ui, repo, listpats, opts, rename)
679 result += orig(ui, repo, listpats, opts, rename)
680 finally:
680 finally:
681 util.copyfile = origcopyfile
681 util.copyfile = origcopyfile
682
682
683 lfdirstate = lfutil.openlfdirstate(ui, repo)
683 lfdirstate = lfutil.openlfdirstate(ui, repo)
684 for (src, dest) in copiedfiles:
684 for (src, dest) in copiedfiles:
685 if (lfutil.shortname in src and
685 if (lfutil.shortname in src and
686 dest.startswith(repo.wjoin(lfutil.shortname))):
686 dest.startswith(repo.wjoin(lfutil.shortname))):
687 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
687 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
688 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
688 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
689 destlfiledir = repo.wvfs.dirname(repo.wjoin(destlfile)) or '.'
689 destlfiledir = repo.wvfs.dirname(repo.wjoin(destlfile)) or '.'
690 if not os.path.isdir(destlfiledir):
690 if not os.path.isdir(destlfiledir):
691 os.makedirs(destlfiledir)
691 os.makedirs(destlfiledir)
692 if rename:
692 if rename:
693 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
693 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
694
694
695 # The file is gone, but this deletes any empty parent
695 # The file is gone, but this deletes any empty parent
696 # directories as a side-effect.
696 # directories as a side-effect.
697 util.unlinkpath(repo.wjoin(srclfile), True)
697 util.unlinkpath(repo.wjoin(srclfile), True)
698 lfdirstate.remove(srclfile)
698 lfdirstate.remove(srclfile)
699 else:
699 else:
700 util.copyfile(repo.wjoin(srclfile),
700 util.copyfile(repo.wjoin(srclfile),
701 repo.wjoin(destlfile))
701 repo.wjoin(destlfile))
702
702
703 lfdirstate.add(destlfile)
703 lfdirstate.add(destlfile)
704 lfdirstate.write()
704 lfdirstate.write()
705 except error.Abort as e:
705 except error.Abort as e:
706 if str(e) != _('no files to copy'):
706 if str(e) != _('no files to copy'):
707 raise e
707 raise e
708 else:
708 else:
709 nolfiles = True
709 nolfiles = True
710 finally:
710 finally:
711 restorematchfn()
711 restorematchfn()
712 wlock.release()
712 wlock.release()
713
713
714 if nolfiles and nonormalfiles:
714 if nolfiles and nonormalfiles:
715 raise error.Abort(_('no files to copy'))
715 raise error.Abort(_('no files to copy'))
716
716
717 return result
717 return result
718
718
719 # When the user calls revert, we have to be careful to not revert any
719 # When the user calls revert, we have to be careful to not revert any
720 # changes to other largefiles accidentally. This means we have to keep
720 # changes to other largefiles accidentally. This means we have to keep
721 # track of the largefiles that are being reverted so we only pull down
721 # track of the largefiles that are being reverted so we only pull down
722 # the necessary largefiles.
722 # the necessary largefiles.
723 #
723 #
724 # Standins are only updated (to match the hash of largefiles) before
724 # Standins are only updated (to match the hash of largefiles) before
725 # commits. Update the standins then run the original revert, changing
725 # commits. Update the standins then run the original revert, changing
726 # the matcher to hit standins instead of largefiles. Based on the
726 # the matcher to hit standins instead of largefiles. Based on the
727 # resulting standins update the largefiles.
727 # resulting standins update the largefiles.
728 def overriderevert(orig, ui, repo, ctx, parents, *pats, **opts):
728 def overriderevert(orig, ui, repo, ctx, parents, *pats, **opts):
729 # Because we put the standins in a bad state (by updating them)
729 # Because we put the standins in a bad state (by updating them)
730 # and then return them to a correct state we need to lock to
730 # and then return them to a correct state we need to lock to
731 # prevent others from changing them in their incorrect state.
731 # prevent others from changing them in their incorrect state.
732 with repo.wlock():
732 with repo.wlock():
733 lfdirstate = lfutil.openlfdirstate(ui, repo)
733 lfdirstate = lfutil.openlfdirstate(ui, repo)
734 s = lfutil.lfdirstatestatus(lfdirstate, repo)
734 s = lfutil.lfdirstatestatus(lfdirstate, repo)
735 lfdirstate.write()
735 lfdirstate.write()
736 for lfile in s.modified:
736 for lfile in s.modified:
737 lfutil.updatestandin(repo, lfutil.standin(lfile))
737 lfutil.updatestandin(repo, lfutil.standin(lfile))
738 for lfile in s.deleted:
738 for lfile in s.deleted:
739 if (repo.wvfs.exists(lfutil.standin(lfile))):
739 if (repo.wvfs.exists(lfutil.standin(lfile))):
740 repo.wvfs.unlink(lfutil.standin(lfile))
740 repo.wvfs.unlink(lfutil.standin(lfile))
741
741
742 oldstandins = lfutil.getstandinsstate(repo)
742 oldstandins = lfutil.getstandinsstate(repo)
743
743
744 def overridematch(mctx, pats=(), opts=None, globbed=False,
744 def overridematch(mctx, pats=(), opts=None, globbed=False,
745 default='relpath', badfn=None):
745 default='relpath', badfn=None):
746 if opts is None:
746 if opts is None:
747 opts = {}
747 opts = {}
748 match = oldmatch(mctx, pats, opts, globbed, default, badfn=badfn)
748 match = oldmatch(mctx, pats, opts, globbed, default, badfn=badfn)
749 m = copy.copy(match)
749 m = copy.copy(match)
750
750
751 # revert supports recursing into subrepos, and though largefiles
751 # revert supports recursing into subrepos, and though largefiles
752 # currently doesn't work correctly in that case, this match is
752 # currently doesn't work correctly in that case, this match is
753 # called, so the lfdirstate above may not be the correct one for
753 # called, so the lfdirstate above may not be the correct one for
754 # this invocation of match.
754 # this invocation of match.
755 lfdirstate = lfutil.openlfdirstate(mctx.repo().ui, mctx.repo(),
755 lfdirstate = lfutil.openlfdirstate(mctx.repo().ui, mctx.repo(),
756 False)
756 False)
757
757
758 def tostandin(f):
758 def tostandin(f):
759 standin = lfutil.standin(f)
759 standin = lfutil.standin(f)
760 if standin in ctx or standin in mctx:
760 if standin in ctx or standin in mctx:
761 return standin
761 return standin
762 elif standin in repo[None] or lfdirstate[f] == 'r':
762 elif standin in repo[None] or lfdirstate[f] == 'r':
763 return None
763 return None
764 return f
764 return f
765 m._files = [tostandin(f) for f in m._files]
765 m._files = [tostandin(f) for f in m._files]
766 m._files = [f for f in m._files if f is not None]
766 m._files = [f for f in m._files if f is not None]
767 m._fileroots = set(m._files)
767 m._fileroots = set(m._files)
768 origmatchfn = m.matchfn
768 origmatchfn = m.matchfn
769 def matchfn(f):
769 def matchfn(f):
770 if lfutil.isstandin(f):
770 if lfutil.isstandin(f):
771 return (origmatchfn(lfutil.splitstandin(f)) and
771 return (origmatchfn(lfutil.splitstandin(f)) and
772 (f in ctx or f in mctx))
772 (f in ctx or f in mctx))
773 return origmatchfn(f)
773 return origmatchfn(f)
774 m.matchfn = matchfn
774 m.matchfn = matchfn
775 return m
775 return m
776 oldmatch = installmatchfn(overridematch)
776 oldmatch = installmatchfn(overridematch)
777 try:
777 try:
778 orig(ui, repo, ctx, parents, *pats, **opts)
778 orig(ui, repo, ctx, parents, *pats, **opts)
779 finally:
779 finally:
780 restorematchfn()
780 restorematchfn()
781
781
782 newstandins = lfutil.getstandinsstate(repo)
782 newstandins = lfutil.getstandinsstate(repo)
783 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
783 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
784 # lfdirstate should be 'normallookup'-ed for updated files,
784 # lfdirstate should be 'normallookup'-ed for updated files,
785 # because reverting doesn't touch dirstate for 'normal' files
785 # because reverting doesn't touch dirstate for 'normal' files
786 # when target revision is explicitly specified: in such case,
786 # when target revision is explicitly specified: in such case,
787 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
787 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
788 # of target (standin) file.
788 # of target (standin) file.
789 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
789 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
790 normallookup=True)
790 normallookup=True)
791
791
792 # after pulling changesets, we need to take some extra care to get
792 # after pulling changesets, we need to take some extra care to get
793 # largefiles updated remotely
793 # largefiles updated remotely
794 def overridepull(orig, ui, repo, source=None, **opts):
794 def overridepull(orig, ui, repo, source=None, **opts):
795 revsprepull = len(repo)
795 revsprepull = len(repo)
796 if not source:
796 if not source:
797 source = 'default'
797 source = 'default'
798 repo.lfpullsource = source
798 repo.lfpullsource = source
799 result = orig(ui, repo, source, **opts)
799 result = orig(ui, repo, source, **opts)
800 revspostpull = len(repo)
800 revspostpull = len(repo)
801 lfrevs = opts.get('lfrev', [])
801 lfrevs = opts.get('lfrev', [])
802 if opts.get('all_largefiles'):
802 if opts.get('all_largefiles'):
803 lfrevs.append('pulled()')
803 lfrevs.append('pulled()')
804 if lfrevs and revspostpull > revsprepull:
804 if lfrevs and revspostpull > revsprepull:
805 numcached = 0
805 numcached = 0
806 repo.firstpulled = revsprepull # for pulled() revset expression
806 repo.firstpulled = revsprepull # for pulled() revset expression
807 try:
807 try:
808 for rev in scmutil.revrange(repo, lfrevs):
808 for rev in scmutil.revrange(repo, lfrevs):
809 ui.note(_('pulling largefiles for revision %s\n') % rev)
809 ui.note(_('pulling largefiles for revision %s\n') % rev)
810 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
810 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
811 numcached += len(cached)
811 numcached += len(cached)
812 finally:
812 finally:
813 del repo.firstpulled
813 del repo.firstpulled
814 ui.status(_("%d largefiles cached\n") % numcached)
814 ui.status(_("%d largefiles cached\n") % numcached)
815 return result
815 return result
816
816
817 def overridepush(orig, ui, repo, *args, **kwargs):
817 def overridepush(orig, ui, repo, *args, **kwargs):
818 """Override push command and store --lfrev parameters in opargs"""
818 """Override push command and store --lfrev parameters in opargs"""
819 lfrevs = kwargs.pop('lfrev', None)
819 lfrevs = kwargs.pop('lfrev', None)
820 if lfrevs:
820 if lfrevs:
821 opargs = kwargs.setdefault('opargs', {})
821 opargs = kwargs.setdefault('opargs', {})
822 opargs['lfrevs'] = scmutil.revrange(repo, lfrevs)
822 opargs['lfrevs'] = scmutil.revrange(repo, lfrevs)
823 return orig(ui, repo, *args, **kwargs)
823 return orig(ui, repo, *args, **kwargs)
824
824
825 def exchangepushoperation(orig, *args, **kwargs):
825 def exchangepushoperation(orig, *args, **kwargs):
826 """Override pushoperation constructor and store lfrevs parameter"""
826 """Override pushoperation constructor and store lfrevs parameter"""
827 lfrevs = kwargs.pop('lfrevs', None)
827 lfrevs = kwargs.pop('lfrevs', None)
828 pushop = orig(*args, **kwargs)
828 pushop = orig(*args, **kwargs)
829 pushop.lfrevs = lfrevs
829 pushop.lfrevs = lfrevs
830 return pushop
830 return pushop
831
831
832 revsetpredicate = registrar.revsetpredicate()
832 revsetpredicate = registrar.revsetpredicate()
833
833
834 @revsetpredicate('pulled()')
834 @revsetpredicate('pulled()')
835 def pulledrevsetsymbol(repo, subset, x):
835 def pulledrevsetsymbol(repo, subset, x):
836 """Changesets that just has been pulled.
836 """Changesets that just has been pulled.
837
837
838 Only available with largefiles from pull --lfrev expressions.
838 Only available with largefiles from pull --lfrev expressions.
839
839
840 .. container:: verbose
840 .. container:: verbose
841
841
842 Some examples:
842 Some examples:
843
843
844 - pull largefiles for all new changesets::
844 - pull largefiles for all new changesets::
845
845
846 hg pull -lfrev "pulled()"
846 hg pull -lfrev "pulled()"
847
847
848 - pull largefiles for all new branch heads::
848 - pull largefiles for all new branch heads::
849
849
850 hg pull -lfrev "head(pulled()) and not closed()"
850 hg pull -lfrev "head(pulled()) and not closed()"
851
851
852 """
852 """
853
853
854 try:
854 try:
855 firstpulled = repo.firstpulled
855 firstpulled = repo.firstpulled
856 except AttributeError:
856 except AttributeError:
857 raise error.Abort(_("pulled() only available in --lfrev"))
857 raise error.Abort(_("pulled() only available in --lfrev"))
858 return revset.baseset([r for r in subset if r >= firstpulled])
858 return revset.baseset([r for r in subset if r >= firstpulled])
859
859
860 def overrideclone(orig, ui, source, dest=None, **opts):
860 def overrideclone(orig, ui, source, dest=None, **opts):
861 d = dest
861 d = dest
862 if d is None:
862 if d is None:
863 d = hg.defaultdest(source)
863 d = hg.defaultdest(source)
864 if opts.get('all_largefiles') and not hg.islocal(d):
864 if opts.get('all_largefiles') and not hg.islocal(d):
865 raise error.Abort(_(
865 raise error.Abort(_(
866 '--all-largefiles is incompatible with non-local destination %s') %
866 '--all-largefiles is incompatible with non-local destination %s') %
867 d)
867 d)
868
868
869 return orig(ui, source, dest, **opts)
869 return orig(ui, source, dest, **opts)
870
870
871 def hgclone(orig, ui, opts, *args, **kwargs):
871 def hgclone(orig, ui, opts, *args, **kwargs):
872 result = orig(ui, opts, *args, **kwargs)
872 result = orig(ui, opts, *args, **kwargs)
873
873
874 if result is not None:
874 if result is not None:
875 sourcerepo, destrepo = result
875 sourcerepo, destrepo = result
876 repo = destrepo.local()
876 repo = destrepo.local()
877
877
878 # When cloning to a remote repo (like through SSH), no repo is available
878 # When cloning to a remote repo (like through SSH), no repo is available
879 # from the peer. Therefore the largefiles can't be downloaded and the
879 # from the peer. Therefore the largefiles can't be downloaded and the
880 # hgrc can't be updated.
880 # hgrc can't be updated.
881 if not repo:
881 if not repo:
882 return result
882 return result
883
883
884 # If largefiles is required for this repo, permanently enable it locally
884 # If largefiles is required for this repo, permanently enable it locally
885 if 'largefiles' in repo.requirements:
885 if 'largefiles' in repo.requirements:
886 with repo.vfs('hgrc', 'a', text=True) as fp:
886 with repo.vfs('hgrc', 'a', text=True) as fp:
887 fp.write('\n[extensions]\nlargefiles=\n')
887 fp.write('\n[extensions]\nlargefiles=\n')
888
888
889 # Caching is implicitly limited to 'rev' option, since the dest repo was
889 # Caching is implicitly limited to 'rev' option, since the dest repo was
890 # truncated at that point. The user may expect a download count with
890 # truncated at that point. The user may expect a download count with
891 # this option, so attempt whether or not this is a largefile repo.
891 # this option, so attempt whether or not this is a largefile repo.
892 if opts.get('all_largefiles'):
892 if opts.get('all_largefiles'):
893 success, missing = lfcommands.downloadlfiles(ui, repo, None)
893 success, missing = lfcommands.downloadlfiles(ui, repo, None)
894
894
895 if missing != 0:
895 if missing != 0:
896 return None
896 return None
897
897
898 return result
898 return result
899
899
900 def overriderebase(orig, ui, repo, **opts):
900 def overriderebase(orig, ui, repo, **opts):
901 if not util.safehasattr(repo, '_largefilesenabled'):
901 if not util.safehasattr(repo, '_largefilesenabled'):
902 return orig(ui, repo, **opts)
902 return orig(ui, repo, **opts)
903
903
904 resuming = opts.get('continue')
904 resuming = opts.get('continue')
905 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
905 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
906 repo._lfstatuswriters.append(lambda *msg, **opts: None)
906 repo._lfstatuswriters.append(lambda *msg, **opts: None)
907 try:
907 try:
908 return orig(ui, repo, **opts)
908 return orig(ui, repo, **opts)
909 finally:
909 finally:
910 repo._lfstatuswriters.pop()
910 repo._lfstatuswriters.pop()
911 repo._lfcommithooks.pop()
911 repo._lfcommithooks.pop()
912
912
913 def overridearchivecmd(orig, ui, repo, dest, **opts):
913 def overridearchivecmd(orig, ui, repo, dest, **opts):
914 repo.unfiltered().lfstatus = True
914 repo.unfiltered().lfstatus = True
915
915
916 try:
916 try:
917 return orig(ui, repo.unfiltered(), dest, **opts)
917 return orig(ui, repo.unfiltered(), dest, **opts)
918 finally:
918 finally:
919 repo.unfiltered().lfstatus = False
919 repo.unfiltered().lfstatus = False
920
920
921 def hgwebarchive(orig, web, req, tmpl):
921 def hgwebarchive(orig, web, req, tmpl):
922 web.repo.lfstatus = True
922 web.repo.lfstatus = True
923
923
924 try:
924 try:
925 return orig(web, req, tmpl)
925 return orig(web, req, tmpl)
926 finally:
926 finally:
927 web.repo.lfstatus = False
927 web.repo.lfstatus = False
928
928
929 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
929 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
930 prefix='', mtime=None, subrepos=None):
930 prefix='', mtime=None, subrepos=None):
931 # For some reason setting repo.lfstatus in hgwebarchive only changes the
931 # For some reason setting repo.lfstatus in hgwebarchive only changes the
932 # unfiltered repo's attr, so check that as well.
932 # unfiltered repo's attr, so check that as well.
933 if not repo.lfstatus and not repo.unfiltered().lfstatus:
933 if not repo.lfstatus and not repo.unfiltered().lfstatus:
934 return orig(repo, dest, node, kind, decode, matchfn, prefix, mtime,
934 return orig(repo, dest, node, kind, decode, matchfn, prefix, mtime,
935 subrepos)
935 subrepos)
936
936
937 # No need to lock because we are only reading history and
937 # No need to lock because we are only reading history and
938 # largefile caches, neither of which are modified.
938 # largefile caches, neither of which are modified.
939 if node is not None:
939 if node is not None:
940 lfcommands.cachelfiles(repo.ui, repo, node)
940 lfcommands.cachelfiles(repo.ui, repo, node)
941
941
942 if kind not in archival.archivers:
942 if kind not in archival.archivers:
943 raise error.Abort(_("unknown archive type '%s'") % kind)
943 raise error.Abort(_("unknown archive type '%s'") % kind)
944
944
945 ctx = repo[node]
945 ctx = repo[node]
946
946
947 if kind == 'files':
947 if kind == 'files':
948 if prefix:
948 if prefix:
949 raise error.Abort(
949 raise error.Abort(
950 _('cannot give prefix when archiving to files'))
950 _('cannot give prefix when archiving to files'))
951 else:
951 else:
952 prefix = archival.tidyprefix(dest, kind, prefix)
952 prefix = archival.tidyprefix(dest, kind, prefix)
953
953
954 def write(name, mode, islink, getdata):
954 def write(name, mode, islink, getdata):
955 if matchfn and not matchfn(name):
955 if matchfn and not matchfn(name):
956 return
956 return
957 data = getdata()
957 data = getdata()
958 if decode:
958 if decode:
959 data = repo.wwritedata(name, data)
959 data = repo.wwritedata(name, data)
960 archiver.addfile(prefix + name, mode, islink, data)
960 archiver.addfile(prefix + name, mode, islink, data)
961
961
962 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
962 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
963
963
964 if repo.ui.configbool("ui", "archivemeta", True):
964 if repo.ui.configbool("ui", "archivemeta", True):
965 write('.hg_archival.txt', 0o644, False,
965 write('.hg_archival.txt', 0o644, False,
966 lambda: archival.buildmetadata(ctx))
966 lambda: archival.buildmetadata(ctx))
967
967
968 for f in ctx:
968 for f in ctx:
969 ff = ctx.flags(f)
969 ff = ctx.flags(f)
970 getdata = ctx[f].data
970 getdata = ctx[f].data
971 if lfutil.isstandin(f):
971 if lfutil.isstandin(f):
972 if node is not None:
972 if node is not None:
973 path = lfutil.findfile(repo, getdata().strip())
973 path = lfutil.findfile(repo, getdata().strip())
974
974
975 if path is None:
975 if path is None:
976 raise error.Abort(
976 raise error.Abort(
977 _('largefile %s not found in repo store or system cache')
977 _('largefile %s not found in repo store or system cache')
978 % lfutil.splitstandin(f))
978 % lfutil.splitstandin(f))
979 else:
979 else:
980 path = lfutil.splitstandin(f)
980 path = lfutil.splitstandin(f)
981
981
982 f = lfutil.splitstandin(f)
982 f = lfutil.splitstandin(f)
983
983
984 getdata = lambda: util.readfile(path)
984 getdata = lambda: util.readfile(path)
985 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, getdata)
985 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, getdata)
986
986
987 if subrepos:
987 if subrepos:
988 for subpath in sorted(ctx.substate):
988 for subpath in sorted(ctx.substate):
989 sub = ctx.workingsub(subpath)
989 sub = ctx.workingsub(subpath)
990 submatch = matchmod.subdirmatcher(subpath, matchfn)
990 submatch = matchmod.subdirmatcher(subpath, matchfn)
991 sub._repo.lfstatus = True
991 sub._repo.lfstatus = True
992 sub.archive(archiver, prefix, submatch)
992 sub.archive(archiver, prefix, submatch)
993
993
994 archiver.done()
994 archiver.done()
995
995
996 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None):
996 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None):
997 if not repo._repo.lfstatus:
997 if not repo._repo.lfstatus:
998 return orig(repo, archiver, prefix, match)
998 return orig(repo, archiver, prefix, match)
999
999
1000 repo._get(repo._state + ('hg',))
1000 repo._get(repo._state + ('hg',))
1001 rev = repo._state[1]
1001 rev = repo._state[1]
1002 ctx = repo._repo[rev]
1002 ctx = repo._repo[rev]
1003
1003
1004 if ctx.node() is not None:
1004 if ctx.node() is not None:
1005 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
1005 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
1006
1006
1007 def write(name, mode, islink, getdata):
1007 def write(name, mode, islink, getdata):
1008 # At this point, the standin has been replaced with the largefile name,
1008 # At this point, the standin has been replaced with the largefile name,
1009 # so the normal matcher works here without the lfutil variants.
1009 # so the normal matcher works here without the lfutil variants.
1010 if match and not match(f):
1010 if match and not match(f):
1011 return
1011 return
1012 data = getdata()
1012 data = getdata()
1013
1013
1014 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
1014 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
1015
1015
1016 for f in ctx:
1016 for f in ctx:
1017 ff = ctx.flags(f)
1017 ff = ctx.flags(f)
1018 getdata = ctx[f].data
1018 getdata = ctx[f].data
1019 if lfutil.isstandin(f):
1019 if lfutil.isstandin(f):
1020 if ctx.node() is not None:
1020 if ctx.node() is not None:
1021 path = lfutil.findfile(repo._repo, getdata().strip())
1021 path = lfutil.findfile(repo._repo, getdata().strip())
1022
1022
1023 if path is None:
1023 if path is None:
1024 raise error.Abort(
1024 raise error.Abort(
1025 _('largefile %s not found in repo store or system cache')
1025 _('largefile %s not found in repo store or system cache')
1026 % lfutil.splitstandin(f))
1026 % lfutil.splitstandin(f))
1027 else:
1027 else:
1028 path = lfutil.splitstandin(f)
1028 path = lfutil.splitstandin(f)
1029
1029
1030 f = lfutil.splitstandin(f)
1030 f = lfutil.splitstandin(f)
1031
1031
1032 getdata = lambda: util.readfile(os.path.join(prefix, path))
1032 getdata = lambda: util.readfile(os.path.join(prefix, path))
1033
1033
1034 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, getdata)
1034 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, getdata)
1035
1035
1036 for subpath in sorted(ctx.substate):
1036 for subpath in sorted(ctx.substate):
1037 sub = ctx.workingsub(subpath)
1037 sub = ctx.workingsub(subpath)
1038 submatch = matchmod.subdirmatcher(subpath, match)
1038 submatch = matchmod.subdirmatcher(subpath, match)
1039 sub._repo.lfstatus = True
1039 sub._repo.lfstatus = True
1040 sub.archive(archiver, prefix + repo._path + '/', submatch)
1040 sub.archive(archiver, prefix + repo._path + '/', submatch)
1041
1041
1042 # If a largefile is modified, the change is not reflected in its
1042 # If a largefile is modified, the change is not reflected in its
1043 # standin until a commit. cmdutil.bailifchanged() raises an exception
1043 # standin until a commit. cmdutil.bailifchanged() raises an exception
1044 # if the repo has uncommitted changes. Wrap it to also check if
1044 # if the repo has uncommitted changes. Wrap it to also check if
1045 # largefiles were changed. This is used by bisect, backout and fetch.
1045 # largefiles were changed. This is used by bisect, backout and fetch.
1046 def overridebailifchanged(orig, repo, *args, **kwargs):
1046 def overridebailifchanged(orig, repo, *args, **kwargs):
1047 orig(repo, *args, **kwargs)
1047 orig(repo, *args, **kwargs)
1048 repo.lfstatus = True
1048 repo.lfstatus = True
1049 s = repo.status()
1049 s = repo.status()
1050 repo.lfstatus = False
1050 repo.lfstatus = False
1051 if s.modified or s.added or s.removed or s.deleted:
1051 if s.modified or s.added or s.removed or s.deleted:
1052 raise error.Abort(_('uncommitted changes'))
1052 raise error.Abort(_('uncommitted changes'))
1053
1053
1054 def postcommitstatus(orig, repo, *args, **kwargs):
1054 def postcommitstatus(orig, repo, *args, **kwargs):
1055 repo.lfstatus = True
1055 repo.lfstatus = True
1056 try:
1056 try:
1057 return orig(repo, *args, **kwargs)
1057 return orig(repo, *args, **kwargs)
1058 finally:
1058 finally:
1059 repo.lfstatus = False
1059 repo.lfstatus = False
1060
1060
1061 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly):
1061 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly):
1062 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1062 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1063 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly)
1063 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly)
1064 m = composelargefilematcher(match, repo[None].manifest())
1064 m = composelargefilematcher(match, repo[None].manifest())
1065
1065
1066 try:
1066 try:
1067 repo.lfstatus = True
1067 repo.lfstatus = True
1068 s = repo.status(match=m, clean=True)
1068 s = repo.status(match=m, clean=True)
1069 finally:
1069 finally:
1070 repo.lfstatus = False
1070 repo.lfstatus = False
1071 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1071 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1072 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
1072 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
1073
1073
1074 for f in forget:
1074 for f in forget:
1075 if lfutil.standin(f) not in repo.dirstate and not \
1075 if lfutil.standin(f) not in repo.dirstate and not \
1076 repo.wvfs.isdir(lfutil.standin(f)):
1076 repo.wvfs.isdir(lfutil.standin(f)):
1077 ui.warn(_('not removing %s: file is already untracked\n')
1077 ui.warn(_('not removing %s: file is already untracked\n')
1078 % m.rel(f))
1078 % m.rel(f))
1079 bad.append(f)
1079 bad.append(f)
1080
1080
1081 for f in forget:
1081 for f in forget:
1082 if ui.verbose or not m.exact(f):
1082 if ui.verbose or not m.exact(f):
1083 ui.status(_('removing %s\n') % m.rel(f))
1083 ui.status(_('removing %s\n') % m.rel(f))
1084
1084
1085 # Need to lock because standin files are deleted then removed from the
1085 # Need to lock because standin files are deleted then removed from the
1086 # repository and we could race in-between.
1086 # repository and we could race in-between.
1087 with repo.wlock():
1087 with repo.wlock():
1088 lfdirstate = lfutil.openlfdirstate(ui, repo)
1088 lfdirstate = lfutil.openlfdirstate(ui, repo)
1089 for f in forget:
1089 for f in forget:
1090 if lfdirstate[f] == 'a':
1090 if lfdirstate[f] == 'a':
1091 lfdirstate.drop(f)
1091 lfdirstate.drop(f)
1092 else:
1092 else:
1093 lfdirstate.remove(f)
1093 lfdirstate.remove(f)
1094 lfdirstate.write()
1094 lfdirstate.write()
1095 standins = [lfutil.standin(f) for f in forget]
1095 standins = [lfutil.standin(f) for f in forget]
1096 for f in standins:
1096 for f in standins:
1097 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1097 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1098 rejected = repo[None].forget(standins)
1098 rejected = repo[None].forget(standins)
1099
1099
1100 bad.extend(f for f in rejected if f in m.files())
1100 bad.extend(f for f in rejected if f in m.files())
1101 forgot.extend(f for f in forget if f not in rejected)
1101 forgot.extend(f for f in forget if f not in rejected)
1102 return bad, forgot
1102 return bad, forgot
1103
1103
1104 def _getoutgoings(repo, other, missing, addfunc):
1104 def _getoutgoings(repo, other, missing, addfunc):
1105 """get pairs of filename and largefile hash in outgoing revisions
1105 """get pairs of filename and largefile hash in outgoing revisions
1106 in 'missing'.
1106 in 'missing'.
1107
1107
1108 largefiles already existing on 'other' repository are ignored.
1108 largefiles already existing on 'other' repository are ignored.
1109
1109
1110 'addfunc' is invoked with each unique pairs of filename and
1110 'addfunc' is invoked with each unique pairs of filename and
1111 largefile hash value.
1111 largefile hash value.
1112 """
1112 """
1113 knowns = set()
1113 knowns = set()
1114 lfhashes = set()
1114 lfhashes = set()
1115 def dedup(fn, lfhash):
1115 def dedup(fn, lfhash):
1116 k = (fn, lfhash)
1116 k = (fn, lfhash)
1117 if k not in knowns:
1117 if k not in knowns:
1118 knowns.add(k)
1118 knowns.add(k)
1119 lfhashes.add(lfhash)
1119 lfhashes.add(lfhash)
1120 lfutil.getlfilestoupload(repo, missing, dedup)
1120 lfutil.getlfilestoupload(repo, missing, dedup)
1121 if lfhashes:
1121 if lfhashes:
1122 lfexists = storefactory.openstore(repo, other).exists(lfhashes)
1122 lfexists = storefactory.openstore(repo, other).exists(lfhashes)
1123 for fn, lfhash in knowns:
1123 for fn, lfhash in knowns:
1124 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1124 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1125 addfunc(fn, lfhash)
1125 addfunc(fn, lfhash)
1126
1126
1127 def outgoinghook(ui, repo, other, opts, missing):
1127 def outgoinghook(ui, repo, other, opts, missing):
1128 if opts.pop('large', None):
1128 if opts.pop('large', None):
1129 lfhashes = set()
1129 lfhashes = set()
1130 if ui.debugflag:
1130 if ui.debugflag:
1131 toupload = {}
1131 toupload = {}
1132 def addfunc(fn, lfhash):
1132 def addfunc(fn, lfhash):
1133 if fn not in toupload:
1133 if fn not in toupload:
1134 toupload[fn] = []
1134 toupload[fn] = []
1135 toupload[fn].append(lfhash)
1135 toupload[fn].append(lfhash)
1136 lfhashes.add(lfhash)
1136 lfhashes.add(lfhash)
1137 def showhashes(fn):
1137 def showhashes(fn):
1138 for lfhash in sorted(toupload[fn]):
1138 for lfhash in sorted(toupload[fn]):
1139 ui.debug(' %s\n' % (lfhash))
1139 ui.debug(' %s\n' % (lfhash))
1140 else:
1140 else:
1141 toupload = set()
1141 toupload = set()
1142 def addfunc(fn, lfhash):
1142 def addfunc(fn, lfhash):
1143 toupload.add(fn)
1143 toupload.add(fn)
1144 lfhashes.add(lfhash)
1144 lfhashes.add(lfhash)
1145 def showhashes(fn):
1145 def showhashes(fn):
1146 pass
1146 pass
1147 _getoutgoings(repo, other, missing, addfunc)
1147 _getoutgoings(repo, other, missing, addfunc)
1148
1148
1149 if not toupload:
1149 if not toupload:
1150 ui.status(_('largefiles: no files to upload\n'))
1150 ui.status(_('largefiles: no files to upload\n'))
1151 else:
1151 else:
1152 ui.status(_('largefiles to upload (%d entities):\n')
1152 ui.status(_('largefiles to upload (%d entities):\n')
1153 % (len(lfhashes)))
1153 % (len(lfhashes)))
1154 for file in sorted(toupload):
1154 for file in sorted(toupload):
1155 ui.status(lfutil.splitstandin(file) + '\n')
1155 ui.status(lfutil.splitstandin(file) + '\n')
1156 showhashes(file)
1156 showhashes(file)
1157 ui.status('\n')
1157 ui.status('\n')
1158
1158
1159 def summaryremotehook(ui, repo, opts, changes):
1159 def summaryremotehook(ui, repo, opts, changes):
1160 largeopt = opts.get('large', False)
1160 largeopt = opts.get('large', False)
1161 if changes is None:
1161 if changes is None:
1162 if largeopt:
1162 if largeopt:
1163 return (False, True) # only outgoing check is needed
1163 return (False, True) # only outgoing check is needed
1164 else:
1164 else:
1165 return (False, False)
1165 return (False, False)
1166 elif largeopt:
1166 elif largeopt:
1167 url, branch, peer, outgoing = changes[1]
1167 url, branch, peer, outgoing = changes[1]
1168 if peer is None:
1168 if peer is None:
1169 # i18n: column positioning for "hg summary"
1169 # i18n: column positioning for "hg summary"
1170 ui.status(_('largefiles: (no remote repo)\n'))
1170 ui.status(_('largefiles: (no remote repo)\n'))
1171 return
1171 return
1172
1172
1173 toupload = set()
1173 toupload = set()
1174 lfhashes = set()
1174 lfhashes = set()
1175 def addfunc(fn, lfhash):
1175 def addfunc(fn, lfhash):
1176 toupload.add(fn)
1176 toupload.add(fn)
1177 lfhashes.add(lfhash)
1177 lfhashes.add(lfhash)
1178 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1178 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1179
1179
1180 if not toupload:
1180 if not toupload:
1181 # i18n: column positioning for "hg summary"
1181 # i18n: column positioning for "hg summary"
1182 ui.status(_('largefiles: (no files to upload)\n'))
1182 ui.status(_('largefiles: (no files to upload)\n'))
1183 else:
1183 else:
1184 # i18n: column positioning for "hg summary"
1184 # i18n: column positioning for "hg summary"
1185 ui.status(_('largefiles: %d entities for %d files to upload\n')
1185 ui.status(_('largefiles: %d entities for %d files to upload\n')
1186 % (len(lfhashes), len(toupload)))
1186 % (len(lfhashes), len(toupload)))
1187
1187
1188 def overridesummary(orig, ui, repo, *pats, **opts):
1188 def overridesummary(orig, ui, repo, *pats, **opts):
1189 try:
1189 try:
1190 repo.lfstatus = True
1190 repo.lfstatus = True
1191 orig(ui, repo, *pats, **opts)
1191 orig(ui, repo, *pats, **opts)
1192 finally:
1192 finally:
1193 repo.lfstatus = False
1193 repo.lfstatus = False
1194
1194
1195 def scmutiladdremove(orig, repo, matcher, prefix, opts=None, dry_run=None,
1195 def scmutiladdremove(orig, repo, matcher, prefix, opts=None, dry_run=None,
1196 similarity=None):
1196 similarity=None):
1197 if opts is None:
1197 if opts is None:
1198 opts = {}
1198 opts = {}
1199 if not lfutil.islfilesrepo(repo):
1199 if not lfutil.islfilesrepo(repo):
1200 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1200 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1201 # Get the list of missing largefiles so we can remove them
1201 # Get the list of missing largefiles so we can remove them
1202 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1202 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1203 unsure, s = lfdirstate.status(matchmod.always(repo.root, repo.getcwd()), [],
1203 unsure, s = lfdirstate.status(matchmod.always(repo.root, repo.getcwd()), [],
1204 False, False, False)
1204 False, False, False)
1205
1205
1206 # Call into the normal remove code, but the removing of the standin, we want
1206 # Call into the normal remove code, but the removing of the standin, we want
1207 # to have handled by original addremove. Monkey patching here makes sure
1207 # to have handled by original addremove. Monkey patching here makes sure
1208 # we don't remove the standin in the largefiles code, preventing a very
1208 # we don't remove the standin in the largefiles code, preventing a very
1209 # confused state later.
1209 # confused state later.
1210 if s.deleted:
1210 if s.deleted:
1211 m = copy.copy(matcher)
1211 m = copy.copy(matcher)
1212
1212
1213 # The m._files and m._map attributes are not changed to the deleted list
1213 # The m._files and m._map attributes are not changed to the deleted list
1214 # because that affects the m.exact() test, which in turn governs whether
1214 # because that affects the m.exact() test, which in turn governs whether
1215 # or not the file name is printed, and how. Simply limit the original
1215 # or not the file name is printed, and how. Simply limit the original
1216 # matches to those in the deleted status list.
1216 # matches to those in the deleted status list.
1217 matchfn = m.matchfn
1217 matchfn = m.matchfn
1218 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1218 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1219
1219
1220 removelargefiles(repo.ui, repo, True, m, **opts)
1220 removelargefiles(repo.ui, repo, True, m, **opts)
1221 # Call into the normal add code, and any files that *should* be added as
1221 # Call into the normal add code, and any files that *should* be added as
1222 # largefiles will be
1222 # largefiles will be
1223 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1223 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1224 # Now that we've handled largefiles, hand off to the original addremove
1224 # Now that we've handled largefiles, hand off to the original addremove
1225 # function to take care of the rest. Make sure it doesn't do anything with
1225 # function to take care of the rest. Make sure it doesn't do anything with
1226 # largefiles by passing a matcher that will ignore them.
1226 # largefiles by passing a matcher that will ignore them.
1227 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1227 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1228 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1228 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1229
1229
1230 # Calling purge with --all will cause the largefiles to be deleted.
1230 # Calling purge with --all will cause the largefiles to be deleted.
1231 # Override repo.status to prevent this from happening.
1231 # Override repo.status to prevent this from happening.
1232 def overridepurge(orig, ui, repo, *dirs, **opts):
1232 def overridepurge(orig, ui, repo, *dirs, **opts):
1233 # XXX Monkey patching a repoview will not work. The assigned attribute will
1233 # XXX Monkey patching a repoview will not work. The assigned attribute will
1234 # be set on the unfiltered repo, but we will only lookup attributes in the
1234 # be set on the unfiltered repo, but we will only lookup attributes in the
1235 # unfiltered repo if the lookup in the repoview object itself fails. As the
1235 # unfiltered repo if the lookup in the repoview object itself fails. As the
1236 # monkey patched method exists on the repoview class the lookup will not
1236 # monkey patched method exists on the repoview class the lookup will not
1237 # fail. As a result, the original version will shadow the monkey patched
1237 # fail. As a result, the original version will shadow the monkey patched
1238 # one, defeating the monkey patch.
1238 # one, defeating the monkey patch.
1239 #
1239 #
1240 # As a work around we use an unfiltered repo here. We should do something
1240 # As a work around we use an unfiltered repo here. We should do something
1241 # cleaner instead.
1241 # cleaner instead.
1242 repo = repo.unfiltered()
1242 repo = repo.unfiltered()
1243 oldstatus = repo.status
1243 oldstatus = repo.status
1244 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1244 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1245 clean=False, unknown=False, listsubrepos=False):
1245 clean=False, unknown=False, listsubrepos=False):
1246 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1246 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1247 listsubrepos)
1247 listsubrepos)
1248 lfdirstate = lfutil.openlfdirstate(ui, repo)
1248 lfdirstate = lfutil.openlfdirstate(ui, repo)
1249 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1249 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1250 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1250 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1251 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1251 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1252 unknown, ignored, r.clean)
1252 unknown, ignored, r.clean)
1253 repo.status = overridestatus
1253 repo.status = overridestatus
1254 orig(ui, repo, *dirs, **opts)
1254 orig(ui, repo, *dirs, **opts)
1255 repo.status = oldstatus
1255 repo.status = oldstatus
1256 def overriderollback(orig, ui, repo, **opts):
1256 def overriderollback(orig, ui, repo, **opts):
1257 with repo.wlock():
1257 with repo.wlock():
1258 before = repo.dirstate.parents()
1258 before = repo.dirstate.parents()
1259 orphans = set(f for f in repo.dirstate
1259 orphans = set(f for f in repo.dirstate
1260 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1260 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1261 result = orig(ui, repo, **opts)
1261 result = orig(ui, repo, **opts)
1262 after = repo.dirstate.parents()
1262 after = repo.dirstate.parents()
1263 if before == after:
1263 if before == after:
1264 return result # no need to restore standins
1264 return result # no need to restore standins
1265
1265
1266 pctx = repo['.']
1266 pctx = repo['.']
1267 for f in repo.dirstate:
1267 for f in repo.dirstate:
1268 if lfutil.isstandin(f):
1268 if lfutil.isstandin(f):
1269 orphans.discard(f)
1269 orphans.discard(f)
1270 if repo.dirstate[f] == 'r':
1270 if repo.dirstate[f] == 'r':
1271 repo.wvfs.unlinkpath(f, ignoremissing=True)
1271 repo.wvfs.unlinkpath(f, ignoremissing=True)
1272 elif f in pctx:
1272 elif f in pctx:
1273 fctx = pctx[f]
1273 fctx = pctx[f]
1274 repo.wwrite(f, fctx.data(), fctx.flags())
1274 repo.wwrite(f, fctx.data(), fctx.flags())
1275 else:
1275 else:
1276 # content of standin is not so important in 'a',
1276 # content of standin is not so important in 'a',
1277 # 'm' or 'n' (coming from the 2nd parent) cases
1277 # 'm' or 'n' (coming from the 2nd parent) cases
1278 lfutil.writestandin(repo, f, '', False)
1278 lfutil.writestandin(repo, f, '', False)
1279 for standin in orphans:
1279 for standin in orphans:
1280 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1280 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1281
1281
1282 lfdirstate = lfutil.openlfdirstate(ui, repo)
1282 lfdirstate = lfutil.openlfdirstate(ui, repo)
1283 orphans = set(lfdirstate)
1283 orphans = set(lfdirstate)
1284 lfiles = lfutil.listlfiles(repo)
1284 lfiles = lfutil.listlfiles(repo)
1285 for file in lfiles:
1285 for file in lfiles:
1286 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1286 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1287 orphans.discard(file)
1287 orphans.discard(file)
1288 for lfile in orphans:
1288 for lfile in orphans:
1289 lfdirstate.drop(lfile)
1289 lfdirstate.drop(lfile)
1290 lfdirstate.write()
1290 lfdirstate.write()
1291 return result
1291 return result
1292
1292
1293 def overridetransplant(orig, ui, repo, *revs, **opts):
1293 def overridetransplant(orig, ui, repo, *revs, **opts):
1294 resuming = opts.get('continue')
1294 resuming = opts.get('continue')
1295 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1295 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1296 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1296 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1297 try:
1297 try:
1298 result = orig(ui, repo, *revs, **opts)
1298 result = orig(ui, repo, *revs, **opts)
1299 finally:
1299 finally:
1300 repo._lfstatuswriters.pop()
1300 repo._lfstatuswriters.pop()
1301 repo._lfcommithooks.pop()
1301 repo._lfcommithooks.pop()
1302 return result
1302 return result
1303
1303
1304 def overridecat(orig, ui, repo, file1, *pats, **opts):
1304 def overridecat(orig, ui, repo, file1, *pats, **opts):
1305 ctx = scmutil.revsingle(repo, opts.get('rev'))
1305 ctx = scmutil.revsingle(repo, opts.get('rev'))
1306 err = 1
1306 err = 1
1307 notbad = set()
1307 notbad = set()
1308 m = scmutil.match(ctx, (file1,) + pats, opts)
1308 m = scmutil.match(ctx, (file1,) + pats, opts)
1309 origmatchfn = m.matchfn
1309 origmatchfn = m.matchfn
1310 def lfmatchfn(f):
1310 def lfmatchfn(f):
1311 if origmatchfn(f):
1311 if origmatchfn(f):
1312 return True
1312 return True
1313 lf = lfutil.splitstandin(f)
1313 lf = lfutil.splitstandin(f)
1314 if lf is None:
1314 if lf is None:
1315 return False
1315 return False
1316 notbad.add(lf)
1316 notbad.add(lf)
1317 return origmatchfn(lf)
1317 return origmatchfn(lf)
1318 m.matchfn = lfmatchfn
1318 m.matchfn = lfmatchfn
1319 origbadfn = m.bad
1319 origbadfn = m.bad
1320 def lfbadfn(f, msg):
1320 def lfbadfn(f, msg):
1321 if not f in notbad:
1321 if not f in notbad:
1322 origbadfn(f, msg)
1322 origbadfn(f, msg)
1323 m.bad = lfbadfn
1323 m.bad = lfbadfn
1324
1324
1325 origvisitdirfn = m.visitdir
1325 origvisitdirfn = m.visitdir
1326 def lfvisitdirfn(dir):
1326 def lfvisitdirfn(dir):
1327 if dir == lfutil.shortname:
1327 if dir == lfutil.shortname:
1328 return True
1328 return True
1329 ret = origvisitdirfn(dir)
1329 ret = origvisitdirfn(dir)
1330 if ret:
1330 if ret:
1331 return ret
1331 return ret
1332 lf = lfutil.splitstandin(dir)
1332 lf = lfutil.splitstandin(dir)
1333 if lf is None:
1333 if lf is None:
1334 return False
1334 return False
1335 return origvisitdirfn(lf)
1335 return origvisitdirfn(lf)
1336 m.visitdir = lfvisitdirfn
1336 m.visitdir = lfvisitdirfn
1337
1337
1338 for f in ctx.walk(m):
1338 for f in ctx.walk(m):
1339 with cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1339 with cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1340 pathname=f) as fp:
1340 pathname=f) as fp:
1341 lf = lfutil.splitstandin(f)
1341 lf = lfutil.splitstandin(f)
1342 if lf is None or origmatchfn(f):
1342 if lf is None or origmatchfn(f):
1343 # duplicating unreachable code from commands.cat
1343 # duplicating unreachable code from commands.cat
1344 data = ctx[f].data()
1344 data = ctx[f].data()
1345 if opts.get('decode'):
1345 if opts.get('decode'):
1346 data = repo.wwritedata(f, data)
1346 data = repo.wwritedata(f, data)
1347 fp.write(data)
1347 fp.write(data)
1348 else:
1348 else:
1349 hash = lfutil.readstandin(repo, lf, ctx.rev())
1349 hash = lfutil.readstandin(repo, lf, ctx.rev())
1350 if not lfutil.inusercache(repo.ui, hash):
1350 if not lfutil.inusercache(repo.ui, hash):
1351 store = storefactory.openstore(repo)
1351 store = storefactory.openstore(repo)
1352 success, missing = store.get([(lf, hash)])
1352 success, missing = store.get([(lf, hash)])
1353 if len(success) != 1:
1353 if len(success) != 1:
1354 raise error.Abort(
1354 raise error.Abort(
1355 _('largefile %s is not in cache and could not be '
1355 _('largefile %s is not in cache and could not be '
1356 'downloaded') % lf)
1356 'downloaded') % lf)
1357 path = lfutil.usercachepath(repo.ui, hash)
1357 path = lfutil.usercachepath(repo.ui, hash)
1358 with open(path, "rb") as fpin:
1358 with open(path, "rb") as fpin:
1359 for chunk in util.filechunkiter(fpin):
1359 for chunk in util.filechunkiter(fpin):
1360 fp.write(chunk)
1360 fp.write(chunk)
1361 err = 0
1361 err = 0
1362 return err
1362 return err
1363
1363
1364 def mergeupdate(orig, repo, node, branchmerge, force,
1364 def mergeupdate(orig, repo, node, branchmerge, force,
1365 *args, **kwargs):
1365 *args, **kwargs):
1366 matcher = kwargs.get('matcher', None)
1366 matcher = kwargs.get('matcher', None)
1367 # note if this is a partial update
1367 # note if this is a partial update
1368 partial = matcher and not matcher.always()
1368 partial = matcher and not matcher.always()
1369 with repo.wlock():
1369 with repo.wlock():
1370 # branch | | |
1370 # branch | | |
1371 # merge | force | partial | action
1371 # merge | force | partial | action
1372 # -------+-------+---------+--------------
1372 # -------+-------+---------+--------------
1373 # x | x | x | linear-merge
1373 # x | x | x | linear-merge
1374 # o | x | x | branch-merge
1374 # o | x | x | branch-merge
1375 # x | o | x | overwrite (as clean update)
1375 # x | o | x | overwrite (as clean update)
1376 # o | o | x | force-branch-merge (*1)
1376 # o | o | x | force-branch-merge (*1)
1377 # x | x | o | (*)
1377 # x | x | o | (*)
1378 # o | x | o | (*)
1378 # o | x | o | (*)
1379 # x | o | o | overwrite (as revert)
1379 # x | o | o | overwrite (as revert)
1380 # o | o | o | (*)
1380 # o | o | o | (*)
1381 #
1381 #
1382 # (*) don't care
1382 # (*) don't care
1383 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1383 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1384
1384
1385 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1385 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1386 unsure, s = lfdirstate.status(matchmod.always(repo.root,
1386 unsure, s = lfdirstate.status(matchmod.always(repo.root,
1387 repo.getcwd()),
1387 repo.getcwd()),
1388 [], False, False, False)
1388 [], False, True, False)
1389 oldclean = set(s.clean)
1389 pctx = repo['.']
1390 pctx = repo['.']
1390 for lfile in unsure + s.modified:
1391 for lfile in unsure + s.modified:
1391 lfileabs = repo.wvfs.join(lfile)
1392 lfileabs = repo.wvfs.join(lfile)
1392 if not repo.wvfs.exists(lfileabs):
1393 if not repo.wvfs.exists(lfileabs):
1393 continue
1394 continue
1394 lfhash = lfutil.hashrepofile(repo, lfile)
1395 lfhash = lfutil.hashrepofile(repo, lfile)
1395 standin = lfutil.standin(lfile)
1396 standin = lfutil.standin(lfile)
1396 lfutil.writestandin(repo, standin, lfhash,
1397 lfutil.writestandin(repo, standin, lfhash,
1397 lfutil.getexecutable(lfileabs))
1398 lfutil.getexecutable(lfileabs))
1398 if (standin in pctx and
1399 if (standin in pctx and
1399 lfhash == lfutil.readstandin(repo, lfile, '.')):
1400 lfhash == lfutil.readstandin(repo, lfile, '.')):
1400 lfdirstate.normal(lfile)
1401 oldclean.add(lfile)
1401 for lfile in s.added:
1402 for lfile in s.added:
1402 lfutil.updatestandin(repo, lfutil.standin(lfile))
1403 lfutil.updatestandin(repo, lfutil.standin(lfile))
1404 # mark all clean largefiles as dirty, just in case the update gets
1405 # interrupted before largefiles and lfdirstate are synchronized
1406 for lfile in oldclean:
1407 lfdirstate.normallookup(lfile)
1403 lfdirstate.write()
1408 lfdirstate.write()
1404
1409
1405 oldstandins = lfutil.getstandinsstate(repo)
1410 oldstandins = lfutil.getstandinsstate(repo)
1406
1411
1407 result = orig(repo, node, branchmerge, force, *args, **kwargs)
1412 result = orig(repo, node, branchmerge, force, *args, **kwargs)
1408
1413
1409 newstandins = lfutil.getstandinsstate(repo)
1414 newstandins = lfutil.getstandinsstate(repo)
1410 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1415 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1416
1417 # to avoid leaving all largefiles as dirty and thus rehash them, mark
1418 # all the ones that didn't change as clean
1419 for lfile in oldclean.difference(filelist):
1420 lfdirstate.normal(lfile)
1421 lfdirstate.write()
1422
1411 if branchmerge or force or partial:
1423 if branchmerge or force or partial:
1412 filelist.extend(s.deleted + s.removed)
1424 filelist.extend(s.deleted + s.removed)
1413
1425
1414 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1426 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1415 normallookup=partial)
1427 normallookup=partial)
1416
1428
1417 return result
1429 return result
1418
1430
1419 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1431 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1420 result = orig(repo, files, *args, **kwargs)
1432 result = orig(repo, files, *args, **kwargs)
1421
1433
1422 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1434 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1423 if filelist:
1435 if filelist:
1424 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1436 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1425 printmessage=False, normallookup=True)
1437 printmessage=False, normallookup=True)
1426
1438
1427 return result
1439 return result
@@ -1,385 +1,389 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
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 '''setup for largefiles repositories: reposetup'''
9 '''setup for largefiles repositories: reposetup'''
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 import copy
12 import copy
13
13
14 from mercurial.i18n import _
14 from mercurial.i18n import _
15
15
16 from mercurial import (
16 from mercurial import (
17 error,
17 error,
18 localrepo,
18 localrepo,
19 match as matchmod,
19 match as matchmod,
20 scmutil,
20 scmutil,
21 )
21 )
22
22
23 from . import (
23 from . import (
24 lfcommands,
24 lfcommands,
25 lfutil,
25 lfutil,
26 )
26 )
27
27
28 def reposetup(ui, repo):
28 def reposetup(ui, repo):
29 # wire repositories should be given new wireproto functions
29 # wire repositories should be given new wireproto functions
30 # by "proto.wirereposetup()" via "hg.wirepeersetupfuncs"
30 # by "proto.wirereposetup()" via "hg.wirepeersetupfuncs"
31 if not repo.local():
31 if not repo.local():
32 return
32 return
33
33
34 class lfilesrepo(repo.__class__):
34 class lfilesrepo(repo.__class__):
35 # the mark to examine whether "repo" object enables largefiles or not
35 # the mark to examine whether "repo" object enables largefiles or not
36 _largefilesenabled = True
36 _largefilesenabled = True
37
37
38 lfstatus = False
38 lfstatus = False
39 def status_nolfiles(self, *args, **kwargs):
39 def status_nolfiles(self, *args, **kwargs):
40 return super(lfilesrepo, self).status(*args, **kwargs)
40 return super(lfilesrepo, self).status(*args, **kwargs)
41
41
42 # When lfstatus is set, return a context that gives the names
42 # When lfstatus is set, return a context that gives the names
43 # of largefiles instead of their corresponding standins and
43 # of largefiles instead of their corresponding standins and
44 # identifies the largefiles as always binary, regardless of
44 # identifies the largefiles as always binary, regardless of
45 # their actual contents.
45 # their actual contents.
46 def __getitem__(self, changeid):
46 def __getitem__(self, changeid):
47 ctx = super(lfilesrepo, self).__getitem__(changeid)
47 ctx = super(lfilesrepo, self).__getitem__(changeid)
48 if self.lfstatus:
48 if self.lfstatus:
49 class lfilesctx(ctx.__class__):
49 class lfilesctx(ctx.__class__):
50 def files(self):
50 def files(self):
51 filenames = super(lfilesctx, self).files()
51 filenames = super(lfilesctx, self).files()
52 return [lfutil.splitstandin(f) or f for f in filenames]
52 return [lfutil.splitstandin(f) or f for f in filenames]
53 def manifest(self):
53 def manifest(self):
54 man1 = super(lfilesctx, self).manifest()
54 man1 = super(lfilesctx, self).manifest()
55 class lfilesmanifest(man1.__class__):
55 class lfilesmanifest(man1.__class__):
56 def __contains__(self, filename):
56 def __contains__(self, filename):
57 orig = super(lfilesmanifest, self).__contains__
57 orig = super(lfilesmanifest, self).__contains__
58 return (orig(filename) or
58 return (orig(filename) or
59 orig(lfutil.standin(filename)))
59 orig(lfutil.standin(filename)))
60 man1.__class__ = lfilesmanifest
60 man1.__class__ = lfilesmanifest
61 return man1
61 return man1
62 def filectx(self, path, fileid=None, filelog=None):
62 def filectx(self, path, fileid=None, filelog=None):
63 orig = super(lfilesctx, self).filectx
63 orig = super(lfilesctx, self).filectx
64 try:
64 try:
65 if filelog is not None:
65 if filelog is not None:
66 result = orig(path, fileid, filelog)
66 result = orig(path, fileid, filelog)
67 else:
67 else:
68 result = orig(path, fileid)
68 result = orig(path, fileid)
69 except error.LookupError:
69 except error.LookupError:
70 # Adding a null character will cause Mercurial to
70 # Adding a null character will cause Mercurial to
71 # identify this as a binary file.
71 # identify this as a binary file.
72 if filelog is not None:
72 if filelog is not None:
73 result = orig(lfutil.standin(path), fileid,
73 result = orig(lfutil.standin(path), fileid,
74 filelog)
74 filelog)
75 else:
75 else:
76 result = orig(lfutil.standin(path), fileid)
76 result = orig(lfutil.standin(path), fileid)
77 olddata = result.data
77 olddata = result.data
78 result.data = lambda: olddata() + '\0'
78 result.data = lambda: olddata() + '\0'
79 return result
79 return result
80 ctx.__class__ = lfilesctx
80 ctx.__class__ = lfilesctx
81 return ctx
81 return ctx
82
82
83 # Figure out the status of big files and insert them into the
83 # Figure out the status of big files and insert them into the
84 # appropriate list in the result. Also removes standin files
84 # appropriate list in the result. Also removes standin files
85 # from the listing. Revert to the original status if
85 # from the listing. Revert to the original status if
86 # self.lfstatus is False.
86 # self.lfstatus is False.
87 # XXX large file status is buggy when used on repo proxy.
87 # XXX large file status is buggy when used on repo proxy.
88 # XXX this needs to be investigated.
88 # XXX this needs to be investigated.
89 @localrepo.unfilteredmethod
89 @localrepo.unfilteredmethod
90 def status(self, node1='.', node2=None, match=None, ignored=False,
90 def status(self, node1='.', node2=None, match=None, ignored=False,
91 clean=False, unknown=False, listsubrepos=False):
91 clean=False, unknown=False, listsubrepos=False):
92 listignored, listclean, listunknown = ignored, clean, unknown
92 listignored, listclean, listunknown = ignored, clean, unknown
93 orig = super(lfilesrepo, self).status
93 orig = super(lfilesrepo, self).status
94 if not self.lfstatus:
94 if not self.lfstatus:
95 return orig(node1, node2, match, listignored, listclean,
95 return orig(node1, node2, match, listignored, listclean,
96 listunknown, listsubrepos)
96 listunknown, listsubrepos)
97
97
98 # some calls in this function rely on the old version of status
98 # some calls in this function rely on the old version of status
99 self.lfstatus = False
99 self.lfstatus = False
100 ctx1 = self[node1]
100 ctx1 = self[node1]
101 ctx2 = self[node2]
101 ctx2 = self[node2]
102 working = ctx2.rev() is None
102 working = ctx2.rev() is None
103 parentworking = working and ctx1 == self['.']
103 parentworking = working and ctx1 == self['.']
104
104
105 if match is None:
105 if match is None:
106 match = matchmod.always(self.root, self.getcwd())
106 match = matchmod.always(self.root, self.getcwd())
107
107
108 wlock = None
108 wlock = None
109 try:
109 try:
110 try:
110 try:
111 # updating the dirstate is optional
111 # updating the dirstate is optional
112 # so we don't wait on the lock
112 # so we don't wait on the lock
113 wlock = self.wlock(False)
113 wlock = self.wlock(False)
114 except error.LockError:
114 except error.LockError:
115 pass
115 pass
116
116
117 # First check if paths or patterns were specified on the
117 # First check if paths or patterns were specified on the
118 # command line. If there were, and they don't match any
118 # command line. If there were, and they don't match any
119 # largefiles, we should just bail here and let super
119 # largefiles, we should just bail here and let super
120 # handle it -- thus gaining a big performance boost.
120 # handle it -- thus gaining a big performance boost.
121 lfdirstate = lfutil.openlfdirstate(ui, self)
121 lfdirstate = lfutil.openlfdirstate(ui, self)
122 if not match.always():
122 if not match.always():
123 for f in lfdirstate:
123 for f in lfdirstate:
124 if match(f):
124 if match(f):
125 break
125 break
126 else:
126 else:
127 return orig(node1, node2, match, listignored, listclean,
127 return orig(node1, node2, match, listignored, listclean,
128 listunknown, listsubrepos)
128 listunknown, listsubrepos)
129
129
130 # Create a copy of match that matches standins instead
130 # Create a copy of match that matches standins instead
131 # of largefiles.
131 # of largefiles.
132 def tostandins(files):
132 def tostandins(files):
133 if not working:
133 if not working:
134 return files
134 return files
135 newfiles = []
135 newfiles = []
136 dirstate = self.dirstate
136 dirstate = self.dirstate
137 for f in files:
137 for f in files:
138 sf = lfutil.standin(f)
138 sf = lfutil.standin(f)
139 if sf in dirstate:
139 if sf in dirstate:
140 newfiles.append(sf)
140 newfiles.append(sf)
141 elif sf in dirstate.dirs():
141 elif sf in dirstate.dirs():
142 # Directory entries could be regular or
142 # Directory entries could be regular or
143 # standin, check both
143 # standin, check both
144 newfiles.extend((f, sf))
144 newfiles.extend((f, sf))
145 else:
145 else:
146 newfiles.append(f)
146 newfiles.append(f)
147 return newfiles
147 return newfiles
148
148
149 m = copy.copy(match)
149 m = copy.copy(match)
150 m._files = tostandins(m._files)
150 m._files = tostandins(m._files)
151
151
152 result = orig(node1, node2, m, ignored, clean, unknown,
152 result = orig(node1, node2, m, ignored, clean, unknown,
153 listsubrepos)
153 listsubrepos)
154 if working:
154 if working:
155
155
156 def sfindirstate(f):
156 def sfindirstate(f):
157 sf = lfutil.standin(f)
157 sf = lfutil.standin(f)
158 dirstate = self.dirstate
158 dirstate = self.dirstate
159 return sf in dirstate or sf in dirstate.dirs()
159 return sf in dirstate or sf in dirstate.dirs()
160
160
161 match._files = [f for f in match._files
161 match._files = [f for f in match._files
162 if sfindirstate(f)]
162 if sfindirstate(f)]
163 # Don't waste time getting the ignored and unknown
163 # Don't waste time getting the ignored and unknown
164 # files from lfdirstate
164 # files from lfdirstate
165 unsure, s = lfdirstate.status(match, [], False, listclean,
165 unsure, s = lfdirstate.status(match, [], False, listclean,
166 False)
166 False)
167 (modified, added, removed, clean) = (s.modified, s.added,
167 (modified, added, removed, deleted, clean) = (
168 s.removed, s.clean)
168 s.modified, s.added, s.removed, s.deleted, s.clean)
169 if parentworking:
169 if parentworking:
170 for lfile in unsure:
170 for lfile in unsure:
171 standin = lfutil.standin(lfile)
171 standin = lfutil.standin(lfile)
172 if standin not in ctx1:
172 if standin not in ctx1:
173 # from second parent
173 # from second parent
174 modified.append(lfile)
174 modified.append(lfile)
175 elif ctx1[standin].data().strip() \
175 elif ctx1[standin].data().strip() \
176 != lfutil.hashfile(self.wjoin(lfile)):
176 != lfutil.hashfile(self.wjoin(lfile)):
177 modified.append(lfile)
177 modified.append(lfile)
178 else:
178 else:
179 if listclean:
179 if listclean:
180 clean.append(lfile)
180 clean.append(lfile)
181 lfdirstate.normal(lfile)
181 lfdirstate.normal(lfile)
182 else:
182 else:
183 tocheck = unsure + modified + added + clean
183 tocheck = unsure + modified + added + clean
184 modified, added, clean = [], [], []
184 modified, added, clean = [], [], []
185 checkexec = self.dirstate._checkexec
185 checkexec = self.dirstate._checkexec
186
186
187 for lfile in tocheck:
187 for lfile in tocheck:
188 standin = lfutil.standin(lfile)
188 standin = lfutil.standin(lfile)
189 if standin in ctx1:
189 if standin in ctx1:
190 abslfile = self.wjoin(lfile)
190 abslfile = self.wjoin(lfile)
191 if ((ctx1[standin].data().strip() !=
191 if ((ctx1[standin].data().strip() !=
192 lfutil.hashfile(abslfile)) or
192 lfutil.hashfile(abslfile)) or
193 (checkexec and
193 (checkexec and
194 ('x' in ctx1.flags(standin)) !=
194 ('x' in ctx1.flags(standin)) !=
195 bool(lfutil.getexecutable(abslfile)))):
195 bool(lfutil.getexecutable(abslfile)))):
196 modified.append(lfile)
196 modified.append(lfile)
197 elif listclean:
197 elif listclean:
198 clean.append(lfile)
198 clean.append(lfile)
199 else:
199 else:
200 added.append(lfile)
200 added.append(lfile)
201
201
202 # at this point, 'removed' contains largefiles
202 # at this point, 'removed' contains largefiles
203 # marked as 'R' in the working context.
203 # marked as 'R' in the working context.
204 # then, largefiles not managed also in the target
204 # then, largefiles not managed also in the target
205 # context should be excluded from 'removed'.
205 # context should be excluded from 'removed'.
206 removed = [lfile for lfile in removed
206 removed = [lfile for lfile in removed
207 if lfutil.standin(lfile) in ctx1]
207 if lfutil.standin(lfile) in ctx1]
208
208
209 # Standins no longer found in lfdirstate has been
209 # Standins no longer found in lfdirstate have been deleted
210 # removed
211 for standin in ctx1.walk(lfutil.getstandinmatcher(self)):
210 for standin in ctx1.walk(lfutil.getstandinmatcher(self)):
212 lfile = lfutil.splitstandin(standin)
211 lfile = lfutil.splitstandin(standin)
213 if not match(lfile):
212 if not match(lfile):
214 continue
213 continue
215 if lfile not in lfdirstate:
214 if lfile not in lfdirstate:
216 removed.append(lfile)
215 deleted.append(lfile)
216 # Sync "largefile has been removed" back to the
217 # standin. Removing a file as a side effect of
218 # running status is gross, but the alternatives (if
219 # any) are worse.
220 self.wvfs.unlinkpath(standin, ignoremissing=True)
217
221
218 # Filter result lists
222 # Filter result lists
219 result = list(result)
223 result = list(result)
220
224
221 # Largefiles are not really removed when they're
225 # Largefiles are not really removed when they're
222 # still in the normal dirstate. Likewise, normal
226 # still in the normal dirstate. Likewise, normal
223 # files are not really removed if they are still in
227 # files are not really removed if they are still in
224 # lfdirstate. This happens in merges where files
228 # lfdirstate. This happens in merges where files
225 # change type.
229 # change type.
226 removed = [f for f in removed
230 removed = [f for f in removed
227 if f not in self.dirstate]
231 if f not in self.dirstate]
228 result[2] = [f for f in result[2]
232 result[2] = [f for f in result[2]
229 if f not in lfdirstate]
233 if f not in lfdirstate]
230
234
231 lfiles = set(lfdirstate._map)
235 lfiles = set(lfdirstate._map)
232 # Unknown files
236 # Unknown files
233 result[4] = set(result[4]).difference(lfiles)
237 result[4] = set(result[4]).difference(lfiles)
234 # Ignored files
238 # Ignored files
235 result[5] = set(result[5]).difference(lfiles)
239 result[5] = set(result[5]).difference(lfiles)
236 # combine normal files and largefiles
240 # combine normal files and largefiles
237 normals = [[fn for fn in filelist
241 normals = [[fn for fn in filelist
238 if not lfutil.isstandin(fn)]
242 if not lfutil.isstandin(fn)]
239 for filelist in result]
243 for filelist in result]
240 lfstatus = (modified, added, removed, s.deleted, [], [],
244 lfstatus = (modified, added, removed, deleted, [], [],
241 clean)
245 clean)
242 result = [sorted(list1 + list2)
246 result = [sorted(list1 + list2)
243 for (list1, list2) in zip(normals, lfstatus)]
247 for (list1, list2) in zip(normals, lfstatus)]
244 else: # not against working directory
248 else: # not against working directory
245 result = [[lfutil.splitstandin(f) or f for f in items]
249 result = [[lfutil.splitstandin(f) or f for f in items]
246 for items in result]
250 for items in result]
247
251
248 if wlock:
252 if wlock:
249 lfdirstate.write()
253 lfdirstate.write()
250
254
251 finally:
255 finally:
252 if wlock:
256 if wlock:
253 wlock.release()
257 wlock.release()
254
258
255 self.lfstatus = True
259 self.lfstatus = True
256 return scmutil.status(*result)
260 return scmutil.status(*result)
257
261
258 def commitctx(self, ctx, *args, **kwargs):
262 def commitctx(self, ctx, *args, **kwargs):
259 node = super(lfilesrepo, self).commitctx(ctx, *args, **kwargs)
263 node = super(lfilesrepo, self).commitctx(ctx, *args, **kwargs)
260 class lfilesctx(ctx.__class__):
264 class lfilesctx(ctx.__class__):
261 def markcommitted(self, node):
265 def markcommitted(self, node):
262 orig = super(lfilesctx, self).markcommitted
266 orig = super(lfilesctx, self).markcommitted
263 return lfutil.markcommitted(orig, self, node)
267 return lfutil.markcommitted(orig, self, node)
264 ctx.__class__ = lfilesctx
268 ctx.__class__ = lfilesctx
265 return node
269 return node
266
270
267 # Before commit, largefile standins have not had their
271 # Before commit, largefile standins have not had their
268 # contents updated to reflect the hash of their largefile.
272 # contents updated to reflect the hash of their largefile.
269 # Do that here.
273 # Do that here.
270 def commit(self, text="", user=None, date=None, match=None,
274 def commit(self, text="", user=None, date=None, match=None,
271 force=False, editor=False, extra={}):
275 force=False, editor=False, extra={}):
272 orig = super(lfilesrepo, self).commit
276 orig = super(lfilesrepo, self).commit
273
277
274 with self.wlock():
278 with self.wlock():
275 lfcommithook = self._lfcommithooks[-1]
279 lfcommithook = self._lfcommithooks[-1]
276 match = lfcommithook(self, match)
280 match = lfcommithook(self, match)
277 result = orig(text=text, user=user, date=date, match=match,
281 result = orig(text=text, user=user, date=date, match=match,
278 force=force, editor=editor, extra=extra)
282 force=force, editor=editor, extra=extra)
279 return result
283 return result
280
284
281 def push(self, remote, force=False, revs=None, newbranch=False):
285 def push(self, remote, force=False, revs=None, newbranch=False):
282 if remote.local():
286 if remote.local():
283 missing = set(self.requirements) - remote.local().supported
287 missing = set(self.requirements) - remote.local().supported
284 if missing:
288 if missing:
285 msg = _("required features are not"
289 msg = _("required features are not"
286 " supported in the destination:"
290 " supported in the destination:"
287 " %s") % (', '.join(sorted(missing)))
291 " %s") % (', '.join(sorted(missing)))
288 raise error.Abort(msg)
292 raise error.Abort(msg)
289 return super(lfilesrepo, self).push(remote, force=force, revs=revs,
293 return super(lfilesrepo, self).push(remote, force=force, revs=revs,
290 newbranch=newbranch)
294 newbranch=newbranch)
291
295
292 # TODO: _subdirlfs should be moved into "lfutil.py", because
296 # TODO: _subdirlfs should be moved into "lfutil.py", because
293 # it is referred only from "lfutil.updatestandinsbymatch"
297 # it is referred only from "lfutil.updatestandinsbymatch"
294 def _subdirlfs(self, files, lfiles):
298 def _subdirlfs(self, files, lfiles):
295 '''
299 '''
296 Adjust matched file list
300 Adjust matched file list
297 If we pass a directory to commit whose only committable files
301 If we pass a directory to commit whose only committable files
298 are largefiles, the core commit code aborts before finding
302 are largefiles, the core commit code aborts before finding
299 the largefiles.
303 the largefiles.
300 So we do the following:
304 So we do the following:
301 For directories that only have largefiles as matches,
305 For directories that only have largefiles as matches,
302 we explicitly add the largefiles to the match list and remove
306 we explicitly add the largefiles to the match list and remove
303 the directory.
307 the directory.
304 In other cases, we leave the match list unmodified.
308 In other cases, we leave the match list unmodified.
305 '''
309 '''
306 actualfiles = []
310 actualfiles = []
307 dirs = []
311 dirs = []
308 regulars = []
312 regulars = []
309
313
310 for f in files:
314 for f in files:
311 if lfutil.isstandin(f + '/'):
315 if lfutil.isstandin(f + '/'):
312 raise error.Abort(
316 raise error.Abort(
313 _('file "%s" is a largefile standin') % f,
317 _('file "%s" is a largefile standin') % f,
314 hint=('commit the largefile itself instead'))
318 hint=('commit the largefile itself instead'))
315 # Scan directories
319 # Scan directories
316 if self.wvfs.isdir(f):
320 if self.wvfs.isdir(f):
317 dirs.append(f)
321 dirs.append(f)
318 else:
322 else:
319 regulars.append(f)
323 regulars.append(f)
320
324
321 for f in dirs:
325 for f in dirs:
322 matcheddir = False
326 matcheddir = False
323 d = self.dirstate.normalize(f) + '/'
327 d = self.dirstate.normalize(f) + '/'
324 # Check for matched normal files
328 # Check for matched normal files
325 for mf in regulars:
329 for mf in regulars:
326 if self.dirstate.normalize(mf).startswith(d):
330 if self.dirstate.normalize(mf).startswith(d):
327 actualfiles.append(f)
331 actualfiles.append(f)
328 matcheddir = True
332 matcheddir = True
329 break
333 break
330 if not matcheddir:
334 if not matcheddir:
331 # If no normal match, manually append
335 # If no normal match, manually append
332 # any matching largefiles
336 # any matching largefiles
333 for lf in lfiles:
337 for lf in lfiles:
334 if self.dirstate.normalize(lf).startswith(d):
338 if self.dirstate.normalize(lf).startswith(d):
335 actualfiles.append(lf)
339 actualfiles.append(lf)
336 if not matcheddir:
340 if not matcheddir:
337 # There may still be normal files in the dir, so
341 # There may still be normal files in the dir, so
338 # add a directory to the list, which
342 # add a directory to the list, which
339 # forces status/dirstate to walk all files and
343 # forces status/dirstate to walk all files and
340 # call the match function on the matcher, even
344 # call the match function on the matcher, even
341 # on case sensitive filesystems.
345 # on case sensitive filesystems.
342 actualfiles.append('.')
346 actualfiles.append('.')
343 matcheddir = True
347 matcheddir = True
344 # Nothing in dir, so readd it
348 # Nothing in dir, so readd it
345 # and let commit reject it
349 # and let commit reject it
346 if not matcheddir:
350 if not matcheddir:
347 actualfiles.append(f)
351 actualfiles.append(f)
348
352
349 # Always add normal files
353 # Always add normal files
350 actualfiles += regulars
354 actualfiles += regulars
351 return actualfiles
355 return actualfiles
352
356
353 repo.__class__ = lfilesrepo
357 repo.__class__ = lfilesrepo
354
358
355 # stack of hooks being executed before committing.
359 # stack of hooks being executed before committing.
356 # only last element ("_lfcommithooks[-1]") is used for each committing.
360 # only last element ("_lfcommithooks[-1]") is used for each committing.
357 repo._lfcommithooks = [lfutil.updatestandinsbymatch]
361 repo._lfcommithooks = [lfutil.updatestandinsbymatch]
358
362
359 # Stack of status writer functions taking "*msg, **opts" arguments
363 # Stack of status writer functions taking "*msg, **opts" arguments
360 # like "ui.status()". Only last element ("_lfstatuswriters[-1]")
364 # like "ui.status()". Only last element ("_lfstatuswriters[-1]")
361 # is used to write status out.
365 # is used to write status out.
362 repo._lfstatuswriters = [ui.status]
366 repo._lfstatuswriters = [ui.status]
363
367
364 def prepushoutgoinghook(pushop):
368 def prepushoutgoinghook(pushop):
365 """Push largefiles for pushop before pushing revisions."""
369 """Push largefiles for pushop before pushing revisions."""
366 lfrevs = pushop.lfrevs
370 lfrevs = pushop.lfrevs
367 if lfrevs is None:
371 if lfrevs is None:
368 lfrevs = pushop.outgoing.missing
372 lfrevs = pushop.outgoing.missing
369 if lfrevs:
373 if lfrevs:
370 toupload = set()
374 toupload = set()
371 addfunc = lambda fn, lfhash: toupload.add(lfhash)
375 addfunc = lambda fn, lfhash: toupload.add(lfhash)
372 lfutil.getlfilestoupload(pushop.repo, lfrevs,
376 lfutil.getlfilestoupload(pushop.repo, lfrevs,
373 addfunc)
377 addfunc)
374 lfcommands.uploadlfiles(ui, pushop.repo, pushop.remote, toupload)
378 lfcommands.uploadlfiles(ui, pushop.repo, pushop.remote, toupload)
375 repo.prepushoutgoinghooks.add("largefiles", prepushoutgoinghook)
379 repo.prepushoutgoinghooks.add("largefiles", prepushoutgoinghook)
376
380
377 def checkrequireslfiles(ui, repo, **kwargs):
381 def checkrequireslfiles(ui, repo, **kwargs):
378 if 'largefiles' not in repo.requirements and any(
382 if 'largefiles' not in repo.requirements and any(
379 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
383 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
380 repo.requirements.add('largefiles')
384 repo.requirements.add('largefiles')
381 repo._writerequirements()
385 repo._writerequirements()
382
386
383 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles,
387 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles,
384 'largefiles')
388 'largefiles')
385 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles, 'largefiles')
389 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles, 'largefiles')
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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