##// END OF EJS Templates
merge with i18n
Alexander Sauta -
r22065:45a01832 merge stable
parent child Browse files
Show More

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

@@ -1,93 +1,94 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,106 +1,107 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
@@ -1,342 +1,343 b''
1 # git.py - git support for the convert extension
1 # git.py - git support for the convert extension
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import os
8 import os
9 import subprocess
9 import subprocess
10 from mercurial import util, config
10 from mercurial import util, config
11 from mercurial.node import hex, nullid
11 from mercurial.node import hex, nullid
12 from mercurial.i18n import _
12 from mercurial.i18n import _
13
13
14 from common import NoRepo, commit, converter_source, checktool
14 from common import NoRepo, commit, converter_source, checktool
15
15
16 class submodule(object):
16 class submodule(object):
17 def __init__(self, path, node, url):
17 def __init__(self, path, node, url):
18 self.path = path
18 self.path = path
19 self.node = node
19 self.node = node
20 self.url = url
20 self.url = url
21
21
22 def hgsub(self):
22 def hgsub(self):
23 return "%s = [git]%s" % (self.path, self.url)
23 return "%s = [git]%s" % (self.path, self.url)
24
24
25 def hgsubstate(self):
25 def hgsubstate(self):
26 return "%s %s" % (self.node, self.path)
26 return "%s %s" % (self.node, self.path)
27
27
28 class convert_git(converter_source):
28 class convert_git(converter_source):
29 # Windows does not support GIT_DIR= construct while other systems
29 # Windows does not support GIT_DIR= construct while other systems
30 # cannot remove environment variable. Just assume none have
30 # cannot remove environment variable. Just assume none have
31 # both issues.
31 # both issues.
32 if util.safehasattr(os, 'unsetenv'):
32 if util.safehasattr(os, 'unsetenv'):
33 def gitopen(self, s, err=None):
33 def gitopen(self, s, err=None):
34 prevgitdir = os.environ.get('GIT_DIR')
34 prevgitdir = os.environ.get('GIT_DIR')
35 os.environ['GIT_DIR'] = self.path
35 os.environ['GIT_DIR'] = self.path
36 try:
36 try:
37 if err == subprocess.PIPE:
37 if err == subprocess.PIPE:
38 (stdin, stdout, stderr) = util.popen3(s)
38 (stdin, stdout, stderr) = util.popen3(s)
39 return stdout
39 return stdout
40 elif err == subprocess.STDOUT:
40 elif err == subprocess.STDOUT:
41 return self.popen_with_stderr(s)
41 return self.popen_with_stderr(s)
42 else:
42 else:
43 return util.popen(s, 'rb')
43 return util.popen(s, 'rb')
44 finally:
44 finally:
45 if prevgitdir is None:
45 if prevgitdir is None:
46 del os.environ['GIT_DIR']
46 del os.environ['GIT_DIR']
47 else:
47 else:
48 os.environ['GIT_DIR'] = prevgitdir
48 os.environ['GIT_DIR'] = prevgitdir
49
49
50 def gitpipe(self, s):
50 def gitpipe(self, s):
51 prevgitdir = os.environ.get('GIT_DIR')
51 prevgitdir = os.environ.get('GIT_DIR')
52 os.environ['GIT_DIR'] = self.path
52 os.environ['GIT_DIR'] = self.path
53 try:
53 try:
54 return util.popen3(s)
54 return util.popen3(s)
55 finally:
55 finally:
56 if prevgitdir is None:
56 if prevgitdir is None:
57 del os.environ['GIT_DIR']
57 del os.environ['GIT_DIR']
58 else:
58 else:
59 os.environ['GIT_DIR'] = prevgitdir
59 os.environ['GIT_DIR'] = prevgitdir
60
60
61 else:
61 else:
62 def gitopen(self, s, err=None):
62 def gitopen(self, s, err=None):
63 if err == subprocess.PIPE:
63 if err == subprocess.PIPE:
64 (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s))
64 (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s))
65 return so
65 return so
66 elif err == subprocess.STDOUT:
66 elif err == subprocess.STDOUT:
67 return self.popen_with_stderr(s)
67 return self.popen_with_stderr(s)
68 else:
68 else:
69 return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
69 return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
70
70
71 def gitpipe(self, s):
71 def gitpipe(self, s):
72 return util.popen3('GIT_DIR=%s %s' % (self.path, s))
72 return util.popen3('GIT_DIR=%s %s' % (self.path, s))
73
73
74 def popen_with_stderr(self, s):
74 def popen_with_stderr(self, s):
75 p = subprocess.Popen(s, shell=True, bufsize=-1,
75 p = subprocess.Popen(s, shell=True, bufsize=-1,
76 close_fds=util.closefds,
76 close_fds=util.closefds,
77 stdin=subprocess.PIPE,
77 stdin=subprocess.PIPE,
78 stdout=subprocess.PIPE,
78 stdout=subprocess.PIPE,
79 stderr=subprocess.STDOUT,
79 stderr=subprocess.STDOUT,
80 universal_newlines=False,
80 universal_newlines=False,
81 env=None)
81 env=None)
82 return p.stdout
82 return p.stdout
83
83
84 def gitread(self, s):
84 def gitread(self, s):
85 fh = self.gitopen(s)
85 fh = self.gitopen(s)
86 data = fh.read()
86 data = fh.read()
87 return data, fh.close()
87 return data, fh.close()
88
88
89 def __init__(self, ui, path, rev=None):
89 def __init__(self, ui, path, rev=None):
90 super(convert_git, self).__init__(ui, path, rev=rev)
90 super(convert_git, self).__init__(ui, path, rev=rev)
91
91
92 if os.path.isdir(path + "/.git"):
92 if os.path.isdir(path + "/.git"):
93 path += "/.git"
93 path += "/.git"
94 if not os.path.exists(path + "/objects"):
94 if not os.path.exists(path + "/objects"):
95 raise NoRepo(_("%s does not look like a Git repository") % path)
95 raise NoRepo(_("%s does not look like a Git repository") % path)
96
96
97 checktool('git', 'git')
97 checktool('git', 'git')
98
98
99 self.path = path
99 self.path = path
100 self.submodules = []
100 self.submodules = []
101
101
102 self.catfilepipe = self.gitpipe('git cat-file --batch')
102 self.catfilepipe = self.gitpipe('git cat-file --batch')
103
103
104 def after(self):
104 def after(self):
105 for f in self.catfilepipe:
105 for f in self.catfilepipe:
106 f.close()
106 f.close()
107
107
108 def getheads(self):
108 def getheads(self):
109 if not self.rev:
109 if not self.rev:
110 heads, ret = self.gitread('git rev-parse --branches --remotes')
110 heads, ret = self.gitread('git rev-parse --branches --remotes')
111 heads = heads.splitlines()
111 heads = heads.splitlines()
112 else:
112 else:
113 heads, ret = self.gitread("git rev-parse --verify %s" % self.rev)
113 heads, ret = self.gitread("git rev-parse --verify %s" % self.rev)
114 heads = [heads[:-1]]
114 heads = [heads[:-1]]
115 if ret:
115 if ret:
116 raise util.Abort(_('cannot retrieve git heads'))
116 raise util.Abort(_('cannot retrieve git heads'))
117 return heads
117 return heads
118
118
119 def catfile(self, rev, type):
119 def catfile(self, rev, type):
120 if rev == hex(nullid):
120 if rev == hex(nullid):
121 raise IOError
121 raise IOError
122 self.catfilepipe[0].write(rev+'\n')
122 self.catfilepipe[0].write(rev+'\n')
123 self.catfilepipe[0].flush()
123 self.catfilepipe[0].flush()
124 info = self.catfilepipe[1].readline().split()
124 info = self.catfilepipe[1].readline().split()
125 if info[1] != type:
125 if info[1] != type:
126 raise util.Abort(_('cannot read %r object at %s') % (type, rev))
126 raise util.Abort(_('cannot read %r object at %s') % (type, rev))
127 size = int(info[2])
127 size = int(info[2])
128 data = self.catfilepipe[1].read(size)
128 data = self.catfilepipe[1].read(size)
129 if len(data) < size:
129 if len(data) < size:
130 raise util.Abort(_('cannot read %r object at %s: %s') % (type, rev))
130 raise util.Abort(_('cannot read %r object at %s: unexpected size')
131 % (type, rev))
131 # read the trailing newline
132 # read the trailing newline
132 self.catfilepipe[1].read(1)
133 self.catfilepipe[1].read(1)
133 return data
134 return data
134
135
135 def getfile(self, name, rev):
136 def getfile(self, name, rev):
136 if rev == hex(nullid):
137 if rev == hex(nullid):
137 raise IOError
138 raise IOError
138 if name == '.hgsub':
139 if name == '.hgsub':
139 data = '\n'.join([m.hgsub() for m in self.submoditer()])
140 data = '\n'.join([m.hgsub() for m in self.submoditer()])
140 mode = ''
141 mode = ''
141 elif name == '.hgsubstate':
142 elif name == '.hgsubstate':
142 data = '\n'.join([m.hgsubstate() for m in self.submoditer()])
143 data = '\n'.join([m.hgsubstate() for m in self.submoditer()])
143 mode = ''
144 mode = ''
144 else:
145 else:
145 data = self.catfile(rev, "blob")
146 data = self.catfile(rev, "blob")
146 mode = self.modecache[(name, rev)]
147 mode = self.modecache[(name, rev)]
147 return data, mode
148 return data, mode
148
149
149 def submoditer(self):
150 def submoditer(self):
150 null = hex(nullid)
151 null = hex(nullid)
151 for m in sorted(self.submodules, key=lambda p: p.path):
152 for m in sorted(self.submodules, key=lambda p: p.path):
152 if m.node != null:
153 if m.node != null:
153 yield m
154 yield m
154
155
155 def parsegitmodules(self, content):
156 def parsegitmodules(self, content):
156 """Parse the formatted .gitmodules file, example file format:
157 """Parse the formatted .gitmodules file, example file format:
157 [submodule "sub"]\n
158 [submodule "sub"]\n
158 \tpath = sub\n
159 \tpath = sub\n
159 \turl = git://giturl\n
160 \turl = git://giturl\n
160 """
161 """
161 self.submodules = []
162 self.submodules = []
162 c = config.config()
163 c = config.config()
163 # Each item in .gitmodules starts with \t that cant be parsed
164 # Each item in .gitmodules starts with \t that cant be parsed
164 c.parse('.gitmodules', content.replace('\t',''))
165 c.parse('.gitmodules', content.replace('\t',''))
165 for sec in c.sections():
166 for sec in c.sections():
166 s = c[sec]
167 s = c[sec]
167 if 'url' in s and 'path' in s:
168 if 'url' in s and 'path' in s:
168 self.submodules.append(submodule(s['path'], '', s['url']))
169 self.submodules.append(submodule(s['path'], '', s['url']))
169
170
170 def retrievegitmodules(self, version):
171 def retrievegitmodules(self, version):
171 modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules'))
172 modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules'))
172 if ret:
173 if ret:
173 raise util.Abort(_('cannot read submodules config file in %s') %
174 raise util.Abort(_('cannot read submodules config file in %s') %
174 version)
175 version)
175 self.parsegitmodules(modules)
176 self.parsegitmodules(modules)
176 for m in self.submodules:
177 for m in self.submodules:
177 node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path))
178 node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path))
178 if ret:
179 if ret:
179 continue
180 continue
180 m.node = node.strip()
181 m.node = node.strip()
181
182
182 def getchanges(self, version):
183 def getchanges(self, version):
183 self.modecache = {}
184 self.modecache = {}
184 fh = self.gitopen("git diff-tree -z --root -m -r %s" % version)
185 fh = self.gitopen("git diff-tree -z --root -m -r %s" % version)
185 changes = []
186 changes = []
186 seen = set()
187 seen = set()
187 entry = None
188 entry = None
188 subexists = False
189 subexists = False
189 subdeleted = False
190 subdeleted = False
190 for l in fh.read().split('\x00'):
191 for l in fh.read().split('\x00'):
191 if not entry:
192 if not entry:
192 if not l.startswith(':'):
193 if not l.startswith(':'):
193 continue
194 continue
194 entry = l
195 entry = l
195 continue
196 continue
196 f = l
197 f = l
197 if f not in seen:
198 if f not in seen:
198 seen.add(f)
199 seen.add(f)
199 entry = entry.split()
200 entry = entry.split()
200 h = entry[3]
201 h = entry[3]
201 p = (entry[1] == "100755")
202 p = (entry[1] == "100755")
202 s = (entry[1] == "120000")
203 s = (entry[1] == "120000")
203
204
204 if f == '.gitmodules':
205 if f == '.gitmodules':
205 subexists = True
206 subexists = True
206 if entry[4] == 'D':
207 if entry[4] == 'D':
207 subdeleted = True
208 subdeleted = True
208 changes.append(('.hgsub', hex(nullid)))
209 changes.append(('.hgsub', hex(nullid)))
209 else:
210 else:
210 changes.append(('.hgsub', ''))
211 changes.append(('.hgsub', ''))
211 elif entry[1] == '160000' or entry[0] == ':160000':
212 elif entry[1] == '160000' or entry[0] == ':160000':
212 subexists = True
213 subexists = True
213 else:
214 else:
214 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
215 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
215 changes.append((f, h))
216 changes.append((f, h))
216 entry = None
217 entry = None
217 if fh.close():
218 if fh.close():
218 raise util.Abort(_('cannot read changes in %s') % version)
219 raise util.Abort(_('cannot read changes in %s') % version)
219
220
220 if subexists:
221 if subexists:
221 if subdeleted:
222 if subdeleted:
222 changes.append(('.hgsubstate', hex(nullid)))
223 changes.append(('.hgsubstate', hex(nullid)))
223 else:
224 else:
224 self.retrievegitmodules(version)
225 self.retrievegitmodules(version)
225 changes.append(('.hgsubstate', ''))
226 changes.append(('.hgsubstate', ''))
226 return (changes, {})
227 return (changes, {})
227
228
228 def getcommit(self, version):
229 def getcommit(self, version):
229 c = self.catfile(version, "commit") # read the commit hash
230 c = self.catfile(version, "commit") # read the commit hash
230 end = c.find("\n\n")
231 end = c.find("\n\n")
231 message = c[end + 2:]
232 message = c[end + 2:]
232 message = self.recode(message)
233 message = self.recode(message)
233 l = c[:end].splitlines()
234 l = c[:end].splitlines()
234 parents = []
235 parents = []
235 author = committer = None
236 author = committer = None
236 for e in l[1:]:
237 for e in l[1:]:
237 n, v = e.split(" ", 1)
238 n, v = e.split(" ", 1)
238 if n == "author":
239 if n == "author":
239 p = v.split()
240 p = v.split()
240 tm, tz = p[-2:]
241 tm, tz = p[-2:]
241 author = " ".join(p[:-2])
242 author = " ".join(p[:-2])
242 if author[0] == "<": author = author[1:-1]
243 if author[0] == "<": author = author[1:-1]
243 author = self.recode(author)
244 author = self.recode(author)
244 if n == "committer":
245 if n == "committer":
245 p = v.split()
246 p = v.split()
246 tm, tz = p[-2:]
247 tm, tz = p[-2:]
247 committer = " ".join(p[:-2])
248 committer = " ".join(p[:-2])
248 if committer[0] == "<": committer = committer[1:-1]
249 if committer[0] == "<": committer = committer[1:-1]
249 committer = self.recode(committer)
250 committer = self.recode(committer)
250 if n == "parent":
251 if n == "parent":
251 parents.append(v)
252 parents.append(v)
252
253
253 if committer and committer != author:
254 if committer and committer != author:
254 message += "\ncommitter: %s\n" % committer
255 message += "\ncommitter: %s\n" % committer
255 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
256 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
256 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
257 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
257 date = tm + " " + str(tz)
258 date = tm + " " + str(tz)
258
259
259 c = commit(parents=parents, date=date, author=author, desc=message,
260 c = commit(parents=parents, date=date, author=author, desc=message,
260 rev=version)
261 rev=version)
261 return c
262 return c
262
263
263 def gettags(self):
264 def gettags(self):
264 tags = {}
265 tags = {}
265 alltags = {}
266 alltags = {}
266 fh = self.gitopen('git ls-remote --tags "%s"' % self.path,
267 fh = self.gitopen('git ls-remote --tags "%s"' % self.path,
267 err=subprocess.STDOUT)
268 err=subprocess.STDOUT)
268 prefix = 'refs/tags/'
269 prefix = 'refs/tags/'
269
270
270 # Build complete list of tags, both annotated and bare ones
271 # Build complete list of tags, both annotated and bare ones
271 for line in fh:
272 for line in fh:
272 line = line.strip()
273 line = line.strip()
273 if line.startswith("error:") or line.startswith("fatal:"):
274 if line.startswith("error:") or line.startswith("fatal:"):
274 raise util.Abort(_('cannot read tags from %s') % self.path)
275 raise util.Abort(_('cannot read tags from %s') % self.path)
275 node, tag = line.split(None, 1)
276 node, tag = line.split(None, 1)
276 if not tag.startswith(prefix):
277 if not tag.startswith(prefix):
277 continue
278 continue
278 alltags[tag[len(prefix):]] = node
279 alltags[tag[len(prefix):]] = node
279 if fh.close():
280 if fh.close():
280 raise util.Abort(_('cannot read tags from %s') % self.path)
281 raise util.Abort(_('cannot read tags from %s') % self.path)
281
282
282 # Filter out tag objects for annotated tag refs
283 # Filter out tag objects for annotated tag refs
283 for tag in alltags:
284 for tag in alltags:
284 if tag.endswith('^{}'):
285 if tag.endswith('^{}'):
285 tags[tag[:-3]] = alltags[tag]
286 tags[tag[:-3]] = alltags[tag]
286 else:
287 else:
287 if tag + '^{}' in alltags:
288 if tag + '^{}' in alltags:
288 continue
289 continue
289 else:
290 else:
290 tags[tag] = alltags[tag]
291 tags[tag] = alltags[tag]
291
292
292 return tags
293 return tags
293
294
294 def getchangedfiles(self, version, i):
295 def getchangedfiles(self, version, i):
295 changes = []
296 changes = []
296 if i is None:
297 if i is None:
297 fh = self.gitopen("git diff-tree --root -m -r %s" % version)
298 fh = self.gitopen("git diff-tree --root -m -r %s" % version)
298 for l in fh:
299 for l in fh:
299 if "\t" not in l:
300 if "\t" not in l:
300 continue
301 continue
301 m, f = l[:-1].split("\t")
302 m, f = l[:-1].split("\t")
302 changes.append(f)
303 changes.append(f)
303 else:
304 else:
304 fh = self.gitopen('git diff-tree --name-only --root -r %s '
305 fh = self.gitopen('git diff-tree --name-only --root -r %s '
305 '"%s^%s" --' % (version, version, i + 1))
306 '"%s^%s" --' % (version, version, i + 1))
306 changes = [f.rstrip('\n') for f in fh]
307 changes = [f.rstrip('\n') for f in fh]
307 if fh.close():
308 if fh.close():
308 raise util.Abort(_('cannot read changes in %s') % version)
309 raise util.Abort(_('cannot read changes in %s') % version)
309
310
310 return changes
311 return changes
311
312
312 def getbookmarks(self):
313 def getbookmarks(self):
313 bookmarks = {}
314 bookmarks = {}
314
315
315 # Interesting references in git are prefixed
316 # Interesting references in git are prefixed
316 prefix = 'refs/heads/'
317 prefix = 'refs/heads/'
317 prefixlen = len(prefix)
318 prefixlen = len(prefix)
318
319
319 # factor two commands
320 # factor two commands
320 gitcmd = { 'remote/': 'git ls-remote --heads origin',
321 gitcmd = { 'remote/': 'git ls-remote --heads origin',
321 '': 'git show-ref'}
322 '': 'git show-ref'}
322
323
323 # Origin heads
324 # Origin heads
324 for reftype in gitcmd:
325 for reftype in gitcmd:
325 try:
326 try:
326 fh = self.gitopen(gitcmd[reftype], err=subprocess.PIPE)
327 fh = self.gitopen(gitcmd[reftype], err=subprocess.PIPE)
327 for line in fh:
328 for line in fh:
328 line = line.strip()
329 line = line.strip()
329 rev, name = line.split(None, 1)
330 rev, name = line.split(None, 1)
330 if not name.startswith(prefix):
331 if not name.startswith(prefix):
331 continue
332 continue
332 name = '%s%s' % (reftype, name[prefixlen:])
333 name = '%s%s' % (reftype, name[prefixlen:])
333 bookmarks[name] = rev
334 bookmarks[name] = rev
334 except Exception:
335 except Exception:
335 pass
336 pass
336
337
337 return bookmarks
338 return bookmarks
338
339
339 def checkrevformat(self, revstr, mapname='splicemap'):
340 def checkrevformat(self, revstr, mapname='splicemap'):
340 """ git revision string is a 40 byte hex """
341 """ git revision string is a 40 byte hex """
341 self.checkhexformat(revstr, mapname)
342 self.checkhexformat(revstr, mapname)
342
343
@@ -1,155 +1,155 b''
1 # fetch.py - pull and merge remote changes
1 # fetch.py - pull and merge remote changes
2 #
2 #
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''pull, update and merge in one command (DEPRECATED)'''
8 '''pull, update and merge in one command (DEPRECATED)'''
9
9
10 from mercurial.i18n import _
10 from mercurial.i18n import _
11 from mercurial.node import nullid, short
11 from mercurial.node import nullid, short
12 from mercurial import commands, cmdutil, hg, util, error
12 from mercurial import commands, cmdutil, hg, util, error
13 from mercurial.lock import release
13 from mercurial.lock import release
14
14
15 cmdtable = {}
15 cmdtable = {}
16 command = cmdutil.command(cmdtable)
16 command = cmdutil.command(cmdtable)
17 testedwith = 'internal'
17 testedwith = 'internal'
18
18
19 @command('fetch',
19 @command('fetch',
20 [('r', 'rev', [],
20 [('r', 'rev', [],
21 _('a specific revision you would like to pull'), _('REV')),
21 _('a specific revision you would like to pull'), _('REV')),
22 ('e', 'edit', None, _('edit commit message')),
22 ('e', 'edit', None, _('invoke editor on commit messages')),
23 ('', 'force-editor', None, _('edit commit message (DEPRECATED)')),
23 ('', 'force-editor', None, _('edit commit message (DEPRECATED)')),
24 ('', 'switch-parent', None, _('switch parents when merging')),
24 ('', 'switch-parent', None, _('switch parents when merging')),
25 ] + commands.commitopts + commands.commitopts2 + commands.remoteopts,
25 ] + commands.commitopts + commands.commitopts2 + commands.remoteopts,
26 _('hg fetch [SOURCE]'))
26 _('hg fetch [SOURCE]'))
27 def fetch(ui, repo, source='default', **opts):
27 def fetch(ui, repo, source='default', **opts):
28 '''pull changes from a remote repository, merge new changes if needed.
28 '''pull changes from a remote repository, merge new changes if needed.
29
29
30 This finds all changes from the repository at the specified path
30 This finds all changes from the repository at the specified path
31 or URL and adds them to the local repository.
31 or URL and adds them to the local repository.
32
32
33 If the pulled changes add a new branch head, the head is
33 If the pulled changes add a new branch head, the head is
34 automatically merged, and the result of the merge is committed.
34 automatically merged, and the result of the merge is committed.
35 Otherwise, the working directory is updated to include the new
35 Otherwise, the working directory is updated to include the new
36 changes.
36 changes.
37
37
38 When a merge is needed, the working directory is first updated to
38 When a merge is needed, the working directory is first updated to
39 the newly pulled changes. Local changes are then merged into the
39 the newly pulled changes. Local changes are then merged into the
40 pulled changes. To switch the merge order, use --switch-parent.
40 pulled changes. To switch the merge order, use --switch-parent.
41
41
42 See :hg:`help dates` for a list of formats valid for -d/--date.
42 See :hg:`help dates` for a list of formats valid for -d/--date.
43
43
44 Returns 0 on success.
44 Returns 0 on success.
45 '''
45 '''
46
46
47 date = opts.get('date')
47 date = opts.get('date')
48 if date:
48 if date:
49 opts['date'] = util.parsedate(date)
49 opts['date'] = util.parsedate(date)
50
50
51 parent, p2 = repo.dirstate.parents()
51 parent, p2 = repo.dirstate.parents()
52 branch = repo.dirstate.branch()
52 branch = repo.dirstate.branch()
53 try:
53 try:
54 branchnode = repo.branchtip(branch)
54 branchnode = repo.branchtip(branch)
55 except error.RepoLookupError:
55 except error.RepoLookupError:
56 branchnode = None
56 branchnode = None
57 if parent != branchnode:
57 if parent != branchnode:
58 raise util.Abort(_('working dir not at branch tip '
58 raise util.Abort(_('working dir not at branch tip '
59 '(use "hg update" to check out branch tip)'))
59 '(use "hg update" to check out branch tip)'))
60
60
61 if p2 != nullid:
61 if p2 != nullid:
62 raise util.Abort(_('outstanding uncommitted merge'))
62 raise util.Abort(_('outstanding uncommitted merge'))
63
63
64 wlock = lock = None
64 wlock = lock = None
65 try:
65 try:
66 wlock = repo.wlock()
66 wlock = repo.wlock()
67 lock = repo.lock()
67 lock = repo.lock()
68 mod, add, rem, del_ = repo.status()[:4]
68 mod, add, rem, del_ = repo.status()[:4]
69
69
70 if mod or add or rem:
70 if mod or add or rem:
71 raise util.Abort(_('outstanding uncommitted changes'))
71 raise util.Abort(_('outstanding uncommitted changes'))
72 if del_:
72 if del_:
73 raise util.Abort(_('working directory is missing some files'))
73 raise util.Abort(_('working directory is missing some files'))
74 bheads = repo.branchheads(branch)
74 bheads = repo.branchheads(branch)
75 bheads = [head for head in bheads if len(repo[head].children()) == 0]
75 bheads = [head for head in bheads if len(repo[head].children()) == 0]
76 if len(bheads) > 1:
76 if len(bheads) > 1:
77 raise util.Abort(_('multiple heads in this branch '
77 raise util.Abort(_('multiple heads in this branch '
78 '(use "hg heads ." and "hg merge" to merge)'))
78 '(use "hg heads ." and "hg merge" to merge)'))
79
79
80 other = hg.peer(repo, opts, ui.expandpath(source))
80 other = hg.peer(repo, opts, ui.expandpath(source))
81 ui.status(_('pulling from %s\n') %
81 ui.status(_('pulling from %s\n') %
82 util.hidepassword(ui.expandpath(source)))
82 util.hidepassword(ui.expandpath(source)))
83 revs = None
83 revs = None
84 if opts['rev']:
84 if opts['rev']:
85 try:
85 try:
86 revs = [other.lookup(rev) for rev in opts['rev']]
86 revs = [other.lookup(rev) for rev in opts['rev']]
87 except error.CapabilityError:
87 except error.CapabilityError:
88 err = _("other repository doesn't support revision lookup, "
88 err = _("other repository doesn't support revision lookup, "
89 "so a rev cannot be specified.")
89 "so a rev cannot be specified.")
90 raise util.Abort(err)
90 raise util.Abort(err)
91
91
92 # Are there any changes at all?
92 # Are there any changes at all?
93 modheads = repo.pull(other, heads=revs)
93 modheads = repo.pull(other, heads=revs)
94 if modheads == 0:
94 if modheads == 0:
95 return 0
95 return 0
96
96
97 # Is this a simple fast-forward along the current branch?
97 # Is this a simple fast-forward along the current branch?
98 newheads = repo.branchheads(branch)
98 newheads = repo.branchheads(branch)
99 newchildren = repo.changelog.nodesbetween([parent], newheads)[2]
99 newchildren = repo.changelog.nodesbetween([parent], newheads)[2]
100 if len(newheads) == 1 and len(newchildren):
100 if len(newheads) == 1 and len(newchildren):
101 if newchildren[0] != parent:
101 if newchildren[0] != parent:
102 return hg.update(repo, newchildren[0])
102 return hg.update(repo, newchildren[0])
103 else:
103 else:
104 return 0
104 return 0
105
105
106 # Are there more than one additional branch heads?
106 # Are there more than one additional branch heads?
107 newchildren = [n for n in newchildren if n != parent]
107 newchildren = [n for n in newchildren if n != parent]
108 newparent = parent
108 newparent = parent
109 if newchildren:
109 if newchildren:
110 newparent = newchildren[0]
110 newparent = newchildren[0]
111 hg.clean(repo, newparent)
111 hg.clean(repo, newparent)
112 newheads = [n for n in newheads if n != newparent]
112 newheads = [n for n in newheads if n != newparent]
113 if len(newheads) > 1:
113 if len(newheads) > 1:
114 ui.status(_('not merging with %d other new branch heads '
114 ui.status(_('not merging with %d other new branch heads '
115 '(use "hg heads ." and "hg merge" to merge them)\n') %
115 '(use "hg heads ." and "hg merge" to merge them)\n') %
116 (len(newheads) - 1))
116 (len(newheads) - 1))
117 return 1
117 return 1
118
118
119 if not newheads:
119 if not newheads:
120 return 0
120 return 0
121
121
122 # Otherwise, let's merge.
122 # Otherwise, let's merge.
123 err = False
123 err = False
124 if newheads:
124 if newheads:
125 # By default, we consider the repository we're pulling
125 # By default, we consider the repository we're pulling
126 # *from* as authoritative, so we merge our changes into
126 # *from* as authoritative, so we merge our changes into
127 # theirs.
127 # theirs.
128 if opts['switch_parent']:
128 if opts['switch_parent']:
129 firstparent, secondparent = newparent, newheads[0]
129 firstparent, secondparent = newparent, newheads[0]
130 else:
130 else:
131 firstparent, secondparent = newheads[0], newparent
131 firstparent, secondparent = newheads[0], newparent
132 ui.status(_('updating to %d:%s\n') %
132 ui.status(_('updating to %d:%s\n') %
133 (repo.changelog.rev(firstparent),
133 (repo.changelog.rev(firstparent),
134 short(firstparent)))
134 short(firstparent)))
135 hg.clean(repo, firstparent)
135 hg.clean(repo, firstparent)
136 ui.status(_('merging with %d:%s\n') %
136 ui.status(_('merging with %d:%s\n') %
137 (repo.changelog.rev(secondparent), short(secondparent)))
137 (repo.changelog.rev(secondparent), short(secondparent)))
138 err = hg.merge(repo, secondparent, remind=False)
138 err = hg.merge(repo, secondparent, remind=False)
139
139
140 if not err:
140 if not err:
141 # we don't translate commit messages
141 # we don't translate commit messages
142 message = (cmdutil.logmessage(ui, opts) or
142 message = (cmdutil.logmessage(ui, opts) or
143 ('Automated merge with %s' %
143 ('Automated merge with %s' %
144 util.removeauth(other.url())))
144 util.removeauth(other.url())))
145 editopt = opts.get('edit') or opts.get('force_editor')
145 editopt = opts.get('edit') or opts.get('force_editor')
146 n = repo.commit(message, opts['user'], opts['date'],
146 n = repo.commit(message, opts['user'], opts['date'],
147 editor=cmdutil.getcommiteditor(edit=editopt))
147 editor=cmdutil.getcommiteditor(edit=editopt))
148 ui.status(_('new changeset %d:%s merges remote changes '
148 ui.status(_('new changeset %d:%s merges remote changes '
149 'with local\n') % (repo.changelog.rev(n),
149 'with local\n') % (repo.changelog.rev(n),
150 short(n)))
150 short(n)))
151
151
152 return err
152 return err
153
153
154 finally:
154 finally:
155 release(lock, wlock)
155 release(lock, wlock)
@@ -1,297 +1,297 b''
1 # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
1 # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
2 #
2 #
3 # This software may be used and distributed according to the terms of the
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
4 # GNU General Public License version 2 or any later version.
5
5
6 '''commands to sign and verify changesets'''
6 '''commands to sign and verify changesets'''
7
7
8 import os, tempfile, binascii
8 import os, tempfile, binascii
9 from mercurial import util, commands, match, cmdutil
9 from mercurial import util, commands, match, cmdutil
10 from mercurial import node as hgnode
10 from mercurial import node as hgnode
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12
12
13 cmdtable = {}
13 cmdtable = {}
14 command = cmdutil.command(cmdtable)
14 command = cmdutil.command(cmdtable)
15 testedwith = 'internal'
15 testedwith = 'internal'
16
16
17 class gpg(object):
17 class gpg(object):
18 def __init__(self, path, key=None):
18 def __init__(self, path, key=None):
19 self.path = path
19 self.path = path
20 self.key = (key and " --local-user \"%s\"" % key) or ""
20 self.key = (key and " --local-user \"%s\"" % key) or ""
21
21
22 def sign(self, data):
22 def sign(self, data):
23 gpgcmd = "%s --sign --detach-sign%s" % (self.path, self.key)
23 gpgcmd = "%s --sign --detach-sign%s" % (self.path, self.key)
24 return util.filter(data, gpgcmd)
24 return util.filter(data, gpgcmd)
25
25
26 def verify(self, data, sig):
26 def verify(self, data, sig):
27 """ returns of the good and bad signatures"""
27 """ returns of the good and bad signatures"""
28 sigfile = datafile = None
28 sigfile = datafile = None
29 try:
29 try:
30 # create temporary files
30 # create temporary files
31 fd, sigfile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".sig")
31 fd, sigfile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".sig")
32 fp = os.fdopen(fd, 'wb')
32 fp = os.fdopen(fd, 'wb')
33 fp.write(sig)
33 fp.write(sig)
34 fp.close()
34 fp.close()
35 fd, datafile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".txt")
35 fd, datafile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".txt")
36 fp = os.fdopen(fd, 'wb')
36 fp = os.fdopen(fd, 'wb')
37 fp.write(data)
37 fp.write(data)
38 fp.close()
38 fp.close()
39 gpgcmd = ("%s --logger-fd 1 --status-fd 1 --verify "
39 gpgcmd = ("%s --logger-fd 1 --status-fd 1 --verify "
40 "\"%s\" \"%s\"" % (self.path, sigfile, datafile))
40 "\"%s\" \"%s\"" % (self.path, sigfile, datafile))
41 ret = util.filter("", gpgcmd)
41 ret = util.filter("", gpgcmd)
42 finally:
42 finally:
43 for f in (sigfile, datafile):
43 for f in (sigfile, datafile):
44 try:
44 try:
45 if f:
45 if f:
46 os.unlink(f)
46 os.unlink(f)
47 except OSError:
47 except OSError:
48 pass
48 pass
49 keys = []
49 keys = []
50 key, fingerprint = None, None
50 key, fingerprint = None, None
51 for l in ret.splitlines():
51 for l in ret.splitlines():
52 # see DETAILS in the gnupg documentation
52 # see DETAILS in the gnupg documentation
53 # filter the logger output
53 # filter the logger output
54 if not l.startswith("[GNUPG:]"):
54 if not l.startswith("[GNUPG:]"):
55 continue
55 continue
56 l = l[9:]
56 l = l[9:]
57 if l.startswith("VALIDSIG"):
57 if l.startswith("VALIDSIG"):
58 # fingerprint of the primary key
58 # fingerprint of the primary key
59 fingerprint = l.split()[10]
59 fingerprint = l.split()[10]
60 elif l.startswith("ERRSIG"):
60 elif l.startswith("ERRSIG"):
61 key = l.split(" ", 3)[:2]
61 key = l.split(" ", 3)[:2]
62 key.append("")
62 key.append("")
63 fingerprint = None
63 fingerprint = None
64 elif (l.startswith("GOODSIG") or
64 elif (l.startswith("GOODSIG") or
65 l.startswith("EXPSIG") or
65 l.startswith("EXPSIG") or
66 l.startswith("EXPKEYSIG") or
66 l.startswith("EXPKEYSIG") or
67 l.startswith("BADSIG")):
67 l.startswith("BADSIG")):
68 if key is not None:
68 if key is not None:
69 keys.append(key + [fingerprint])
69 keys.append(key + [fingerprint])
70 key = l.split(" ", 2)
70 key = l.split(" ", 2)
71 fingerprint = None
71 fingerprint = None
72 if key is not None:
72 if key is not None:
73 keys.append(key + [fingerprint])
73 keys.append(key + [fingerprint])
74 return keys
74 return keys
75
75
76 def newgpg(ui, **opts):
76 def newgpg(ui, **opts):
77 """create a new gpg instance"""
77 """create a new gpg instance"""
78 gpgpath = ui.config("gpg", "cmd", "gpg")
78 gpgpath = ui.config("gpg", "cmd", "gpg")
79 gpgkey = opts.get('key')
79 gpgkey = opts.get('key')
80 if not gpgkey:
80 if not gpgkey:
81 gpgkey = ui.config("gpg", "key", None)
81 gpgkey = ui.config("gpg", "key", None)
82 return gpg(gpgpath, gpgkey)
82 return gpg(gpgpath, gpgkey)
83
83
84 def sigwalk(repo):
84 def sigwalk(repo):
85 """
85 """
86 walk over every sigs, yields a couple
86 walk over every sigs, yields a couple
87 ((node, version, sig), (filename, linenumber))
87 ((node, version, sig), (filename, linenumber))
88 """
88 """
89 def parsefile(fileiter, context):
89 def parsefile(fileiter, context):
90 ln = 1
90 ln = 1
91 for l in fileiter:
91 for l in fileiter:
92 if not l:
92 if not l:
93 continue
93 continue
94 yield (l.split(" ", 2), (context, ln))
94 yield (l.split(" ", 2), (context, ln))
95 ln += 1
95 ln += 1
96
96
97 # read the heads
97 # read the heads
98 fl = repo.file(".hgsigs")
98 fl = repo.file(".hgsigs")
99 for r in reversed(fl.heads()):
99 for r in reversed(fl.heads()):
100 fn = ".hgsigs|%s" % hgnode.short(r)
100 fn = ".hgsigs|%s" % hgnode.short(r)
101 for item in parsefile(fl.read(r).splitlines(), fn):
101 for item in parsefile(fl.read(r).splitlines(), fn):
102 yield item
102 yield item
103 try:
103 try:
104 # read local signatures
104 # read local signatures
105 fn = "localsigs"
105 fn = "localsigs"
106 for item in parsefile(repo.opener(fn), fn):
106 for item in parsefile(repo.opener(fn), fn):
107 yield item
107 yield item
108 except IOError:
108 except IOError:
109 pass
109 pass
110
110
111 def getkeys(ui, repo, mygpg, sigdata, context):
111 def getkeys(ui, repo, mygpg, sigdata, context):
112 """get the keys who signed a data"""
112 """get the keys who signed a data"""
113 fn, ln = context
113 fn, ln = context
114 node, version, sig = sigdata
114 node, version, sig = sigdata
115 prefix = "%s:%d" % (fn, ln)
115 prefix = "%s:%d" % (fn, ln)
116 node = hgnode.bin(node)
116 node = hgnode.bin(node)
117
117
118 data = node2txt(repo, node, version)
118 data = node2txt(repo, node, version)
119 sig = binascii.a2b_base64(sig)
119 sig = binascii.a2b_base64(sig)
120 keys = mygpg.verify(data, sig)
120 keys = mygpg.verify(data, sig)
121
121
122 validkeys = []
122 validkeys = []
123 # warn for expired key and/or sigs
123 # warn for expired key and/or sigs
124 for key in keys:
124 for key in keys:
125 if key[0] == "ERRSIG":
125 if key[0] == "ERRSIG":
126 ui.write(_("%s Unknown key ID \"%s\"\n")
126 ui.write(_("%s Unknown key ID \"%s\"\n")
127 % (prefix, shortkey(ui, key[1][:15])))
127 % (prefix, shortkey(ui, key[1][:15])))
128 continue
128 continue
129 if key[0] == "BADSIG":
129 if key[0] == "BADSIG":
130 ui.write(_("%s Bad signature from \"%s\"\n") % (prefix, key[2]))
130 ui.write(_("%s Bad signature from \"%s\"\n") % (prefix, key[2]))
131 continue
131 continue
132 if key[0] == "EXPSIG":
132 if key[0] == "EXPSIG":
133 ui.write(_("%s Note: Signature has expired"
133 ui.write(_("%s Note: Signature has expired"
134 " (signed by: \"%s\")\n") % (prefix, key[2]))
134 " (signed by: \"%s\")\n") % (prefix, key[2]))
135 elif key[0] == "EXPKEYSIG":
135 elif key[0] == "EXPKEYSIG":
136 ui.write(_("%s Note: This key has expired"
136 ui.write(_("%s Note: This key has expired"
137 " (signed by: \"%s\")\n") % (prefix, key[2]))
137 " (signed by: \"%s\")\n") % (prefix, key[2]))
138 validkeys.append((key[1], key[2], key[3]))
138 validkeys.append((key[1], key[2], key[3]))
139 return validkeys
139 return validkeys
140
140
141 @command("sigs", [], _('hg sigs'))
141 @command("sigs", [], _('hg sigs'))
142 def sigs(ui, repo):
142 def sigs(ui, repo):
143 """list signed changesets"""
143 """list signed changesets"""
144 mygpg = newgpg(ui)
144 mygpg = newgpg(ui)
145 revs = {}
145 revs = {}
146
146
147 for data, context in sigwalk(repo):
147 for data, context in sigwalk(repo):
148 node, version, sig = data
148 node, version, sig = data
149 fn, ln = context
149 fn, ln = context
150 try:
150 try:
151 n = repo.lookup(node)
151 n = repo.lookup(node)
152 except KeyError:
152 except KeyError:
153 ui.warn(_("%s:%d node does not exist\n") % (fn, ln))
153 ui.warn(_("%s:%d node does not exist\n") % (fn, ln))
154 continue
154 continue
155 r = repo.changelog.rev(n)
155 r = repo.changelog.rev(n)
156 keys = getkeys(ui, repo, mygpg, data, context)
156 keys = getkeys(ui, repo, mygpg, data, context)
157 if not keys:
157 if not keys:
158 continue
158 continue
159 revs.setdefault(r, [])
159 revs.setdefault(r, [])
160 revs[r].extend(keys)
160 revs[r].extend(keys)
161 for rev in sorted(revs, reverse=True):
161 for rev in sorted(revs, reverse=True):
162 for k in revs[rev]:
162 for k in revs[rev]:
163 r = "%5d:%s" % (rev, hgnode.hex(repo.changelog.node(rev)))
163 r = "%5d:%s" % (rev, hgnode.hex(repo.changelog.node(rev)))
164 ui.write("%-30s %s\n" % (keystr(ui, k), r))
164 ui.write("%-30s %s\n" % (keystr(ui, k), r))
165
165
166 @command("sigcheck", [], _('hg sigcheck REV'))
166 @command("sigcheck", [], _('hg sigcheck REV'))
167 def check(ui, repo, rev):
167 def check(ui, repo, rev):
168 """verify all the signatures there may be for a particular revision"""
168 """verify all the signatures there may be for a particular revision"""
169 mygpg = newgpg(ui)
169 mygpg = newgpg(ui)
170 rev = repo.lookup(rev)
170 rev = repo.lookup(rev)
171 hexrev = hgnode.hex(rev)
171 hexrev = hgnode.hex(rev)
172 keys = []
172 keys = []
173
173
174 for data, context in sigwalk(repo):
174 for data, context in sigwalk(repo):
175 node, version, sig = data
175 node, version, sig = data
176 if node == hexrev:
176 if node == hexrev:
177 k = getkeys(ui, repo, mygpg, data, context)
177 k = getkeys(ui, repo, mygpg, data, context)
178 if k:
178 if k:
179 keys.extend(k)
179 keys.extend(k)
180
180
181 if not keys:
181 if not keys:
182 ui.write(_("no valid signature for %s\n") % hgnode.short(rev))
182 ui.write(_("no valid signature for %s\n") % hgnode.short(rev))
183 return
183 return
184
184
185 # print summary
185 # print summary
186 ui.write("%s is signed by:\n" % hgnode.short(rev))
186 ui.write("%s is signed by:\n" % hgnode.short(rev))
187 for key in keys:
187 for key in keys:
188 ui.write(" %s\n" % keystr(ui, key))
188 ui.write(" %s\n" % keystr(ui, key))
189
189
190 def keystr(ui, key):
190 def keystr(ui, key):
191 """associate a string to a key (username, comment)"""
191 """associate a string to a key (username, comment)"""
192 keyid, user, fingerprint = key
192 keyid, user, fingerprint = key
193 comment = ui.config("gpg", fingerprint, None)
193 comment = ui.config("gpg", fingerprint, None)
194 if comment:
194 if comment:
195 return "%s (%s)" % (user, comment)
195 return "%s (%s)" % (user, comment)
196 else:
196 else:
197 return user
197 return user
198
198
199 @command("sign",
199 @command("sign",
200 [('l', 'local', None, _('make the signature local')),
200 [('l', 'local', None, _('make the signature local')),
201 ('f', 'force', None, _('sign even if the sigfile is modified')),
201 ('f', 'force', None, _('sign even if the sigfile is modified')),
202 ('', 'no-commit', None, _('do not commit the sigfile after signing')),
202 ('', 'no-commit', None, _('do not commit the sigfile after signing')),
203 ('k', 'key', '',
203 ('k', 'key', '',
204 _('the key id to sign with'), _('ID')),
204 _('the key id to sign with'), _('ID')),
205 ('m', 'message', '',
205 ('m', 'message', '',
206 _('commit message'), _('TEXT')),
206 _('use text as commit message'), _('TEXT')),
207 ('e', 'edit', False, _('invoke editor on commit messages')),
207 ('e', 'edit', False, _('invoke editor on commit messages')),
208 ] + commands.commitopts2,
208 ] + commands.commitopts2,
209 _('hg sign [OPTION]... [REV]...'))
209 _('hg sign [OPTION]... [REV]...'))
210 def sign(ui, repo, *revs, **opts):
210 def sign(ui, repo, *revs, **opts):
211 """add a signature for the current or given revision
211 """add a signature for the current or given revision
212
212
213 If no revision is given, the parent of the working directory is used,
213 If no revision is given, the parent of the working directory is used,
214 or tip if no revision is checked out.
214 or tip if no revision is checked out.
215
215
216 See :hg:`help dates` for a list of formats valid for -d/--date.
216 See :hg:`help dates` for a list of formats valid for -d/--date.
217 """
217 """
218
218
219 mygpg = newgpg(ui, **opts)
219 mygpg = newgpg(ui, **opts)
220 sigver = "0"
220 sigver = "0"
221 sigmessage = ""
221 sigmessage = ""
222
222
223 date = opts.get('date')
223 date = opts.get('date')
224 if date:
224 if date:
225 opts['date'] = util.parsedate(date)
225 opts['date'] = util.parsedate(date)
226
226
227 if revs:
227 if revs:
228 nodes = [repo.lookup(n) for n in revs]
228 nodes = [repo.lookup(n) for n in revs]
229 else:
229 else:
230 nodes = [node for node in repo.dirstate.parents()
230 nodes = [node for node in repo.dirstate.parents()
231 if node != hgnode.nullid]
231 if node != hgnode.nullid]
232 if len(nodes) > 1:
232 if len(nodes) > 1:
233 raise util.Abort(_('uncommitted merge - please provide a '
233 raise util.Abort(_('uncommitted merge - please provide a '
234 'specific revision'))
234 'specific revision'))
235 if not nodes:
235 if not nodes:
236 nodes = [repo.changelog.tip()]
236 nodes = [repo.changelog.tip()]
237
237
238 for n in nodes:
238 for n in nodes:
239 hexnode = hgnode.hex(n)
239 hexnode = hgnode.hex(n)
240 ui.write(_("signing %d:%s\n") % (repo.changelog.rev(n),
240 ui.write(_("signing %d:%s\n") % (repo.changelog.rev(n),
241 hgnode.short(n)))
241 hgnode.short(n)))
242 # build data
242 # build data
243 data = node2txt(repo, n, sigver)
243 data = node2txt(repo, n, sigver)
244 sig = mygpg.sign(data)
244 sig = mygpg.sign(data)
245 if not sig:
245 if not sig:
246 raise util.Abort(_("error while signing"))
246 raise util.Abort(_("error while signing"))
247 sig = binascii.b2a_base64(sig)
247 sig = binascii.b2a_base64(sig)
248 sig = sig.replace("\n", "")
248 sig = sig.replace("\n", "")
249 sigmessage += "%s %s %s\n" % (hexnode, sigver, sig)
249 sigmessage += "%s %s %s\n" % (hexnode, sigver, sig)
250
250
251 # write it
251 # write it
252 if opts['local']:
252 if opts['local']:
253 repo.opener.append("localsigs", sigmessage)
253 repo.opener.append("localsigs", sigmessage)
254 return
254 return
255
255
256 msigs = match.exact(repo.root, '', ['.hgsigs'])
256 msigs = match.exact(repo.root, '', ['.hgsigs'])
257 s = repo.status(match=msigs, unknown=True, ignored=True)[:6]
257 s = repo.status(match=msigs, unknown=True, ignored=True)[:6]
258 if util.any(s) and not opts["force"]:
258 if util.any(s) and not opts["force"]:
259 raise util.Abort(_("working copy of .hgsigs is changed "
259 raise util.Abort(_("working copy of .hgsigs is changed "
260 "(please commit .hgsigs manually "
260 "(please commit .hgsigs manually "
261 "or use --force)"))
261 "or use --force)"))
262
262
263 sigsfile = repo.wfile(".hgsigs", "ab")
263 sigsfile = repo.wfile(".hgsigs", "ab")
264 sigsfile.write(sigmessage)
264 sigsfile.write(sigmessage)
265 sigsfile.close()
265 sigsfile.close()
266
266
267 if '.hgsigs' not in repo.dirstate:
267 if '.hgsigs' not in repo.dirstate:
268 repo[None].add([".hgsigs"])
268 repo[None].add([".hgsigs"])
269
269
270 if opts["no_commit"]:
270 if opts["no_commit"]:
271 return
271 return
272
272
273 message = opts['message']
273 message = opts['message']
274 if not message:
274 if not message:
275 # we don't translate commit messages
275 # we don't translate commit messages
276 message = "\n".join(["Added signature for changeset %s"
276 message = "\n".join(["Added signature for changeset %s"
277 % hgnode.short(n)
277 % hgnode.short(n)
278 for n in nodes])
278 for n in nodes])
279 try:
279 try:
280 repo.commit(message, opts['user'], opts['date'], match=msigs,
280 repo.commit(message, opts['user'], opts['date'], match=msigs,
281 editor=cmdutil.getcommiteditor(**opts))
281 editor=cmdutil.getcommiteditor(**opts))
282 except ValueError, inst:
282 except ValueError, inst:
283 raise util.Abort(str(inst))
283 raise util.Abort(str(inst))
284
284
285 def shortkey(ui, key):
285 def shortkey(ui, key):
286 if len(key) != 16:
286 if len(key) != 16:
287 ui.debug("key ID \"%s\" format error\n" % key)
287 ui.debug("key ID \"%s\" format error\n" % key)
288 return key
288 return key
289
289
290 return key[-8:]
290 return key[-8:]
291
291
292 def node2txt(repo, node, ver):
292 def node2txt(repo, node, ver):
293 """map a manifest into some text"""
293 """map a manifest into some text"""
294 if ver == "0":
294 if ver == "0":
295 return "%s\n" % hgnode.hex(node)
295 return "%s\n" % hgnode.hex(node)
296 else:
296 else:
297 raise util.Abort(_("unknown signature version"))
297 raise util.Abort(_("unknown signature version"))
@@ -1,928 +1,929 b''
1 # histedit.py - interactive history editing for mercurial
1 # histedit.py - interactive history editing for mercurial
2 #
2 #
3 # Copyright 2009 Augie Fackler <raf@durin42.com>
3 # Copyright 2009 Augie Fackler <raf@durin42.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 """interactive history editing
7 """interactive history editing
8
8
9 With this extension installed, Mercurial gains one new command: histedit. Usage
9 With this extension installed, Mercurial gains one new command: histedit. Usage
10 is as follows, assuming the following history::
10 is as follows, assuming the following history::
11
11
12 @ 3[tip] 7c2fd3b9020c 2009-04-27 18:04 -0500 durin42
12 @ 3[tip] 7c2fd3b9020c 2009-04-27 18:04 -0500 durin42
13 | Add delta
13 | Add delta
14 |
14 |
15 o 2 030b686bedc4 2009-04-27 18:04 -0500 durin42
15 o 2 030b686bedc4 2009-04-27 18:04 -0500 durin42
16 | Add gamma
16 | Add gamma
17 |
17 |
18 o 1 c561b4e977df 2009-04-27 18:04 -0500 durin42
18 o 1 c561b4e977df 2009-04-27 18:04 -0500 durin42
19 | Add beta
19 | Add beta
20 |
20 |
21 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
21 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
22 Add alpha
22 Add alpha
23
23
24 If you were to run ``hg histedit c561b4e977df``, you would see the following
24 If you were to run ``hg histedit c561b4e977df``, you would see the following
25 file open in your editor::
25 file open in your editor::
26
26
27 pick c561b4e977df Add beta
27 pick c561b4e977df Add beta
28 pick 030b686bedc4 Add gamma
28 pick 030b686bedc4 Add gamma
29 pick 7c2fd3b9020c Add delta
29 pick 7c2fd3b9020c Add delta
30
30
31 # Edit history between c561b4e977df and 7c2fd3b9020c
31 # Edit history between c561b4e977df and 7c2fd3b9020c
32 #
32 #
33 # Commits are listed from least to most recent
33 # Commits are listed from least to most recent
34 #
34 #
35 # Commands:
35 # Commands:
36 # p, pick = use commit
36 # p, pick = use commit
37 # e, edit = use commit, but stop for amending
37 # e, edit = use commit, but stop for amending
38 # f, fold = use commit, but combine it with the one above
38 # f, fold = use commit, but combine it with the one above
39 # d, drop = remove commit from history
39 # d, drop = remove commit from history
40 # m, mess = edit message without changing commit content
40 # m, mess = edit message without changing commit content
41 #
41 #
42
42
43 In this file, lines beginning with ``#`` are ignored. You must specify a rule
43 In this file, lines beginning with ``#`` are ignored. You must specify a rule
44 for each revision in your history. For example, if you had meant to add gamma
44 for each revision in your history. For example, if you had meant to add gamma
45 before beta, and then wanted to add delta in the same revision as beta, you
45 before beta, and then wanted to add delta in the same revision as beta, you
46 would reorganize the file to look like this::
46 would reorganize the file to look like this::
47
47
48 pick 030b686bedc4 Add gamma
48 pick 030b686bedc4 Add gamma
49 pick c561b4e977df Add beta
49 pick c561b4e977df Add beta
50 fold 7c2fd3b9020c Add delta
50 fold 7c2fd3b9020c Add delta
51
51
52 # Edit history between c561b4e977df and 7c2fd3b9020c
52 # Edit history between c561b4e977df and 7c2fd3b9020c
53 #
53 #
54 # Commits are listed from least to most recent
54 # Commits are listed from least to most recent
55 #
55 #
56 # Commands:
56 # Commands:
57 # p, pick = use commit
57 # p, pick = use commit
58 # e, edit = use commit, but stop for amending
58 # e, edit = use commit, but stop for amending
59 # f, fold = use commit, but combine it with the one above
59 # f, fold = use commit, but combine it with the one above
60 # d, drop = remove commit from history
60 # d, drop = remove commit from history
61 # m, mess = edit message without changing commit content
61 # m, mess = edit message without changing commit content
62 #
62 #
63
63
64 At which point you close the editor and ``histedit`` starts working. When you
64 At which point you close the editor and ``histedit`` starts working. When you
65 specify a ``fold`` operation, ``histedit`` will open an editor when it folds
65 specify a ``fold`` operation, ``histedit`` will open an editor when it folds
66 those revisions together, offering you a chance to clean up the commit message::
66 those revisions together, offering you a chance to clean up the commit message::
67
67
68 Add beta
68 Add beta
69 ***
69 ***
70 Add delta
70 Add delta
71
71
72 Edit the commit message to your liking, then close the editor. For
72 Edit the commit message to your liking, then close the editor. For
73 this example, let's assume that the commit message was changed to
73 this example, let's assume that the commit message was changed to
74 ``Add beta and delta.`` After histedit has run and had a chance to
74 ``Add beta and delta.`` After histedit has run and had a chance to
75 remove any old or temporary revisions it needed, the history looks
75 remove any old or temporary revisions it needed, the history looks
76 like this::
76 like this::
77
77
78 @ 2[tip] 989b4d060121 2009-04-27 18:04 -0500 durin42
78 @ 2[tip] 989b4d060121 2009-04-27 18:04 -0500 durin42
79 | Add beta and delta.
79 | Add beta and delta.
80 |
80 |
81 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
81 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
82 | Add gamma
82 | Add gamma
83 |
83 |
84 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
84 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
85 Add alpha
85 Add alpha
86
86
87 Note that ``histedit`` does *not* remove any revisions (even its own temporary
87 Note that ``histedit`` does *not* remove any revisions (even its own temporary
88 ones) until after it has completed all the editing operations, so it will
88 ones) until after it has completed all the editing operations, so it will
89 probably perform several strip operations when it's done. For the above example,
89 probably perform several strip operations when it's done. For the above example,
90 it had to run strip twice. Strip can be slow depending on a variety of factors,
90 it had to run strip twice. Strip can be slow depending on a variety of factors,
91 so you might need to be a little patient. You can choose to keep the original
91 so you might need to be a little patient. You can choose to keep the original
92 revisions by passing the ``--keep`` flag.
92 revisions by passing the ``--keep`` flag.
93
93
94 The ``edit`` operation will drop you back to a command prompt,
94 The ``edit`` operation will drop you back to a command prompt,
95 allowing you to edit files freely, or even use ``hg record`` to commit
95 allowing you to edit files freely, or even use ``hg record`` to commit
96 some changes as a separate commit. When you're done, any remaining
96 some changes as a separate commit. When you're done, any remaining
97 uncommitted changes will be committed as well. When done, run ``hg
97 uncommitted changes will be committed as well. When done, run ``hg
98 histedit --continue`` to finish this step. You'll be prompted for a
98 histedit --continue`` to finish this step. You'll be prompted for a
99 new commit message, but the default commit message will be the
99 new commit message, but the default commit message will be the
100 original message for the ``edit`` ed revision.
100 original message for the ``edit`` ed revision.
101
101
102 The ``message`` operation will give you a chance to revise a commit
102 The ``message`` operation will give you a chance to revise a commit
103 message without changing the contents. It's a shortcut for doing
103 message without changing the contents. It's a shortcut for doing
104 ``edit`` immediately followed by `hg histedit --continue``.
104 ``edit`` immediately followed by `hg histedit --continue``.
105
105
106 If ``histedit`` encounters a conflict when moving a revision (while
106 If ``histedit`` encounters a conflict when moving a revision (while
107 handling ``pick`` or ``fold``), it'll stop in a similar manner to
107 handling ``pick`` or ``fold``), it'll stop in a similar manner to
108 ``edit`` with the difference that it won't prompt you for a commit
108 ``edit`` with the difference that it won't prompt you for a commit
109 message when done. If you decide at this point that you don't like how
109 message when done. If you decide at this point that you don't like how
110 much work it will be to rearrange history, or that you made a mistake,
110 much work it will be to rearrange history, or that you made a mistake,
111 you can use ``hg histedit --abort`` to abandon the new changes you
111 you can use ``hg histedit --abort`` to abandon the new changes you
112 have made and return to the state before you attempted to edit your
112 have made and return to the state before you attempted to edit your
113 history.
113 history.
114
114
115 If we clone the histedit-ed example repository above and add four more
115 If we clone the histedit-ed example repository above and add four more
116 changes, such that we have the following history::
116 changes, such that we have the following history::
117
117
118 @ 6[tip] 038383181893 2009-04-27 18:04 -0500 stefan
118 @ 6[tip] 038383181893 2009-04-27 18:04 -0500 stefan
119 | Add theta
119 | Add theta
120 |
120 |
121 o 5 140988835471 2009-04-27 18:04 -0500 stefan
121 o 5 140988835471 2009-04-27 18:04 -0500 stefan
122 | Add eta
122 | Add eta
123 |
123 |
124 o 4 122930637314 2009-04-27 18:04 -0500 stefan
124 o 4 122930637314 2009-04-27 18:04 -0500 stefan
125 | Add zeta
125 | Add zeta
126 |
126 |
127 o 3 836302820282 2009-04-27 18:04 -0500 stefan
127 o 3 836302820282 2009-04-27 18:04 -0500 stefan
128 | Add epsilon
128 | Add epsilon
129 |
129 |
130 o 2 989b4d060121 2009-04-27 18:04 -0500 durin42
130 o 2 989b4d060121 2009-04-27 18:04 -0500 durin42
131 | Add beta and delta.
131 | Add beta and delta.
132 |
132 |
133 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
133 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
134 | Add gamma
134 | Add gamma
135 |
135 |
136 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
136 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
137 Add alpha
137 Add alpha
138
138
139 If you run ``hg histedit --outgoing`` on the clone then it is the same
139 If you run ``hg histedit --outgoing`` on the clone then it is the same
140 as running ``hg histedit 836302820282``. If you need plan to push to a
140 as running ``hg histedit 836302820282``. If you need plan to push to a
141 repository that Mercurial does not detect to be related to the source
141 repository that Mercurial does not detect to be related to the source
142 repo, you can add a ``--force`` option.
142 repo, you can add a ``--force`` option.
143 """
143 """
144
144
145 try:
145 try:
146 import cPickle as pickle
146 import cPickle as pickle
147 pickle.dump # import now
147 pickle.dump # import now
148 except ImportError:
148 except ImportError:
149 import pickle
149 import pickle
150 import os
150 import os
151 import sys
151 import sys
152
152
153 from mercurial import cmdutil
153 from mercurial import cmdutil
154 from mercurial import discovery
154 from mercurial import discovery
155 from mercurial import error
155 from mercurial import error
156 from mercurial import copies
156 from mercurial import copies
157 from mercurial import context
157 from mercurial import context
158 from mercurial import hg
158 from mercurial import hg
159 from mercurial import node
159 from mercurial import node
160 from mercurial import repair
160 from mercurial import repair
161 from mercurial import scmutil
161 from mercurial import util
162 from mercurial import util
162 from mercurial import obsolete
163 from mercurial import obsolete
163 from mercurial import merge as mergemod
164 from mercurial import merge as mergemod
164 from mercurial.lock import release
165 from mercurial.lock import release
165 from mercurial.i18n import _
166 from mercurial.i18n import _
166
167
167 cmdtable = {}
168 cmdtable = {}
168 command = cmdutil.command(cmdtable)
169 command = cmdutil.command(cmdtable)
169
170
170 testedwith = 'internal'
171 testedwith = 'internal'
171
172
172 # i18n: command names and abbreviations must remain untranslated
173 # i18n: command names and abbreviations must remain untranslated
173 editcomment = _("""# Edit history between %s and %s
174 editcomment = _("""# Edit history between %s and %s
174 #
175 #
175 # Commits are listed from least to most recent
176 # Commits are listed from least to most recent
176 #
177 #
177 # Commands:
178 # Commands:
178 # p, pick = use commit
179 # p, pick = use commit
179 # e, edit = use commit, but stop for amending
180 # e, edit = use commit, but stop for amending
180 # f, fold = use commit, but combine it with the one above
181 # f, fold = use commit, but combine it with the one above
181 # d, drop = remove commit from history
182 # d, drop = remove commit from history
182 # m, mess = edit message without changing commit content
183 # m, mess = edit message without changing commit content
183 #
184 #
184 """)
185 """)
185
186
186 def commitfuncfor(repo, src):
187 def commitfuncfor(repo, src):
187 """Build a commit function for the replacement of <src>
188 """Build a commit function for the replacement of <src>
188
189
189 This function ensure we apply the same treatment to all changesets.
190 This function ensure we apply the same treatment to all changesets.
190
191
191 - Add a 'histedit_source' entry in extra.
192 - Add a 'histedit_source' entry in extra.
192
193
193 Note that fold have its own separated logic because its handling is a bit
194 Note that fold have its own separated logic because its handling is a bit
194 different and not easily factored out of the fold method.
195 different and not easily factored out of the fold method.
195 """
196 """
196 phasemin = src.phase()
197 phasemin = src.phase()
197 def commitfunc(**kwargs):
198 def commitfunc(**kwargs):
198 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
199 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
199 try:
200 try:
200 repo.ui.setconfig('phases', 'new-commit', phasemin,
201 repo.ui.setconfig('phases', 'new-commit', phasemin,
201 'histedit')
202 'histedit')
202 extra = kwargs.get('extra', {}).copy()
203 extra = kwargs.get('extra', {}).copy()
203 extra['histedit_source'] = src.hex()
204 extra['histedit_source'] = src.hex()
204 kwargs['extra'] = extra
205 kwargs['extra'] = extra
205 return repo.commit(**kwargs)
206 return repo.commit(**kwargs)
206 finally:
207 finally:
207 repo.ui.restoreconfig(phasebackup)
208 repo.ui.restoreconfig(phasebackup)
208 return commitfunc
209 return commitfunc
209
210
210
211
211
212
212 def applychanges(ui, repo, ctx, opts):
213 def applychanges(ui, repo, ctx, opts):
213 """Merge changeset from ctx (only) in the current working directory"""
214 """Merge changeset from ctx (only) in the current working directory"""
214 wcpar = repo.dirstate.parents()[0]
215 wcpar = repo.dirstate.parents()[0]
215 if ctx.p1().node() == wcpar:
216 if ctx.p1().node() == wcpar:
216 # edition ar "in place" we do not need to make any merge,
217 # edition ar "in place" we do not need to make any merge,
217 # just applies changes on parent for edition
218 # just applies changes on parent for edition
218 cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True)
219 cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True)
219 stats = None
220 stats = None
220 else:
221 else:
221 try:
222 try:
222 # ui.forcemerge is an internal variable, do not document
223 # ui.forcemerge is an internal variable, do not document
223 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
224 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
224 'histedit')
225 'histedit')
225 stats = mergemod.update(repo, ctx.node(), True, True, False,
226 stats = mergemod.update(repo, ctx.node(), True, True, False,
226 ctx.p1().node())
227 ctx.p1().node())
227 finally:
228 finally:
228 repo.ui.setconfig('ui', 'forcemerge', '', 'histedit')
229 repo.ui.setconfig('ui', 'forcemerge', '', 'histedit')
229 repo.setparents(wcpar, node.nullid)
230 repo.setparents(wcpar, node.nullid)
230 repo.dirstate.write()
231 repo.dirstate.write()
231 # fix up dirstate for copies and renames
232 # fix up dirstate for copies and renames
232 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
233 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
233 return stats
234 return stats
234
235
235 def collapse(repo, first, last, commitopts):
236 def collapse(repo, first, last, commitopts):
236 """collapse the set of revisions from first to last as new one.
237 """collapse the set of revisions from first to last as new one.
237
238
238 Expected commit options are:
239 Expected commit options are:
239 - message
240 - message
240 - date
241 - date
241 - username
242 - username
242 Commit message is edited in all cases.
243 Commit message is edited in all cases.
243
244
244 This function works in memory."""
245 This function works in memory."""
245 ctxs = list(repo.set('%d::%d', first, last))
246 ctxs = list(repo.set('%d::%d', first, last))
246 if not ctxs:
247 if not ctxs:
247 return None
248 return None
248 base = first.parents()[0]
249 base = first.parents()[0]
249
250
250 # commit a new version of the old changeset, including the update
251 # commit a new version of the old changeset, including the update
251 # collect all files which might be affected
252 # collect all files which might be affected
252 files = set()
253 files = set()
253 for ctx in ctxs:
254 for ctx in ctxs:
254 files.update(ctx.files())
255 files.update(ctx.files())
255
256
256 # Recompute copies (avoid recording a -> b -> a)
257 # Recompute copies (avoid recording a -> b -> a)
257 copied = copies.pathcopies(base, last)
258 copied = copies.pathcopies(base, last)
258
259
259 # prune files which were reverted by the updates
260 # prune files which were reverted by the updates
260 def samefile(f):
261 def samefile(f):
261 if f in last.manifest():
262 if f in last.manifest():
262 a = last.filectx(f)
263 a = last.filectx(f)
263 if f in base.manifest():
264 if f in base.manifest():
264 b = base.filectx(f)
265 b = base.filectx(f)
265 return (a.data() == b.data()
266 return (a.data() == b.data()
266 and a.flags() == b.flags())
267 and a.flags() == b.flags())
267 else:
268 else:
268 return False
269 return False
269 else:
270 else:
270 return f not in base.manifest()
271 return f not in base.manifest()
271 files = [f for f in files if not samefile(f)]
272 files = [f for f in files if not samefile(f)]
272 # commit version of these files as defined by head
273 # commit version of these files as defined by head
273 headmf = last.manifest()
274 headmf = last.manifest()
274 def filectxfn(repo, ctx, path):
275 def filectxfn(repo, ctx, path):
275 if path in headmf:
276 if path in headmf:
276 fctx = last[path]
277 fctx = last[path]
277 flags = fctx.flags()
278 flags = fctx.flags()
278 mctx = context.memfilectx(repo,
279 mctx = context.memfilectx(repo,
279 fctx.path(), fctx.data(),
280 fctx.path(), fctx.data(),
280 islink='l' in flags,
281 islink='l' in flags,
281 isexec='x' in flags,
282 isexec='x' in flags,
282 copied=copied.get(path))
283 copied=copied.get(path))
283 return mctx
284 return mctx
284 raise IOError()
285 raise IOError()
285
286
286 if commitopts.get('message'):
287 if commitopts.get('message'):
287 message = commitopts['message']
288 message = commitopts['message']
288 else:
289 else:
289 message = first.description()
290 message = first.description()
290 user = commitopts.get('user')
291 user = commitopts.get('user')
291 date = commitopts.get('date')
292 date = commitopts.get('date')
292 extra = commitopts.get('extra')
293 extra = commitopts.get('extra')
293
294
294 parents = (first.p1().node(), first.p2().node())
295 parents = (first.p1().node(), first.p2().node())
295 new = context.memctx(repo,
296 new = context.memctx(repo,
296 parents=parents,
297 parents=parents,
297 text=message,
298 text=message,
298 files=files,
299 files=files,
299 filectxfn=filectxfn,
300 filectxfn=filectxfn,
300 user=user,
301 user=user,
301 date=date,
302 date=date,
302 extra=extra,
303 extra=extra,
303 editor=cmdutil.getcommiteditor(edit=True))
304 editor=cmdutil.getcommiteditor(edit=True))
304 return repo.commitctx(new)
305 return repo.commitctx(new)
305
306
306 def pick(ui, repo, ctx, ha, opts):
307 def pick(ui, repo, ctx, ha, opts):
307 oldctx = repo[ha]
308 oldctx = repo[ha]
308 if oldctx.parents()[0] == ctx:
309 if oldctx.parents()[0] == ctx:
309 ui.debug('node %s unchanged\n' % ha)
310 ui.debug('node %s unchanged\n' % ha)
310 return oldctx, []
311 return oldctx, []
311 hg.update(repo, ctx.node())
312 hg.update(repo, ctx.node())
312 stats = applychanges(ui, repo, oldctx, opts)
313 stats = applychanges(ui, repo, oldctx, opts)
313 if stats and stats[3] > 0:
314 if stats and stats[3] > 0:
314 raise error.InterventionRequired(_('Fix up the change and run '
315 raise error.InterventionRequired(_('Fix up the change and run '
315 'hg histedit --continue'))
316 'hg histedit --continue'))
316 # drop the second merge parent
317 # drop the second merge parent
317 commit = commitfuncfor(repo, oldctx)
318 commit = commitfuncfor(repo, oldctx)
318 n = commit(text=oldctx.description(), user=oldctx.user(),
319 n = commit(text=oldctx.description(), user=oldctx.user(),
319 date=oldctx.date(), extra=oldctx.extra())
320 date=oldctx.date(), extra=oldctx.extra())
320 if n is None:
321 if n is None:
321 ui.warn(_('%s: empty changeset\n')
322 ui.warn(_('%s: empty changeset\n')
322 % node.hex(ha))
323 % node.hex(ha))
323 return ctx, []
324 return ctx, []
324 new = repo[n]
325 new = repo[n]
325 return new, [(oldctx.node(), (n,))]
326 return new, [(oldctx.node(), (n,))]
326
327
327
328
328 def edit(ui, repo, ctx, ha, opts):
329 def edit(ui, repo, ctx, ha, opts):
329 oldctx = repo[ha]
330 oldctx = repo[ha]
330 hg.update(repo, ctx.node())
331 hg.update(repo, ctx.node())
331 applychanges(ui, repo, oldctx, opts)
332 applychanges(ui, repo, oldctx, opts)
332 raise error.InterventionRequired(
333 raise error.InterventionRequired(
333 _('Make changes as needed, you may commit or record as needed now.\n'
334 _('Make changes as needed, you may commit or record as needed now.\n'
334 'When you are finished, run hg histedit --continue to resume.'))
335 'When you are finished, run hg histedit --continue to resume.'))
335
336
336 def fold(ui, repo, ctx, ha, opts):
337 def fold(ui, repo, ctx, ha, opts):
337 oldctx = repo[ha]
338 oldctx = repo[ha]
338 hg.update(repo, ctx.node())
339 hg.update(repo, ctx.node())
339 stats = applychanges(ui, repo, oldctx, opts)
340 stats = applychanges(ui, repo, oldctx, opts)
340 if stats and stats[3] > 0:
341 if stats and stats[3] > 0:
341 raise error.InterventionRequired(
342 raise error.InterventionRequired(
342 _('Fix up the change and run hg histedit --continue'))
343 _('Fix up the change and run hg histedit --continue'))
343 n = repo.commit(text='fold-temp-revision %s' % ha, user=oldctx.user(),
344 n = repo.commit(text='fold-temp-revision %s' % ha, user=oldctx.user(),
344 date=oldctx.date(), extra=oldctx.extra())
345 date=oldctx.date(), extra=oldctx.extra())
345 if n is None:
346 if n is None:
346 ui.warn(_('%s: empty changeset')
347 ui.warn(_('%s: empty changeset')
347 % node.hex(ha))
348 % node.hex(ha))
348 return ctx, []
349 return ctx, []
349 return finishfold(ui, repo, ctx, oldctx, n, opts, [])
350 return finishfold(ui, repo, ctx, oldctx, n, opts, [])
350
351
351 def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges):
352 def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges):
352 parent = ctx.parents()[0].node()
353 parent = ctx.parents()[0].node()
353 hg.update(repo, parent)
354 hg.update(repo, parent)
354 ### prepare new commit data
355 ### prepare new commit data
355 commitopts = opts.copy()
356 commitopts = opts.copy()
356 # username
357 # username
357 if ctx.user() == oldctx.user():
358 if ctx.user() == oldctx.user():
358 username = ctx.user()
359 username = ctx.user()
359 else:
360 else:
360 username = ui.username()
361 username = ui.username()
361 commitopts['user'] = username
362 commitopts['user'] = username
362 # commit message
363 # commit message
363 newmessage = '\n***\n'.join(
364 newmessage = '\n***\n'.join(
364 [ctx.description()] +
365 [ctx.description()] +
365 [repo[r].description() for r in internalchanges] +
366 [repo[r].description() for r in internalchanges] +
366 [oldctx.description()]) + '\n'
367 [oldctx.description()]) + '\n'
367 commitopts['message'] = newmessage
368 commitopts['message'] = newmessage
368 # date
369 # date
369 commitopts['date'] = max(ctx.date(), oldctx.date())
370 commitopts['date'] = max(ctx.date(), oldctx.date())
370 extra = ctx.extra().copy()
371 extra = ctx.extra().copy()
371 # histedit_source
372 # histedit_source
372 # note: ctx is likely a temporary commit but that the best we can do here
373 # note: ctx is likely a temporary commit but that the best we can do here
373 # This is sufficient to solve issue3681 anyway
374 # This is sufficient to solve issue3681 anyway
374 extra['histedit_source'] = '%s,%s' % (ctx.hex(), oldctx.hex())
375 extra['histedit_source'] = '%s,%s' % (ctx.hex(), oldctx.hex())
375 commitopts['extra'] = extra
376 commitopts['extra'] = extra
376 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
377 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
377 try:
378 try:
378 phasemin = max(ctx.phase(), oldctx.phase())
379 phasemin = max(ctx.phase(), oldctx.phase())
379 repo.ui.setconfig('phases', 'new-commit', phasemin, 'histedit')
380 repo.ui.setconfig('phases', 'new-commit', phasemin, 'histedit')
380 n = collapse(repo, ctx, repo[newnode], commitopts)
381 n = collapse(repo, ctx, repo[newnode], commitopts)
381 finally:
382 finally:
382 repo.ui.restoreconfig(phasebackup)
383 repo.ui.restoreconfig(phasebackup)
383 if n is None:
384 if n is None:
384 return ctx, []
385 return ctx, []
385 hg.update(repo, n)
386 hg.update(repo, n)
386 replacements = [(oldctx.node(), (newnode,)),
387 replacements = [(oldctx.node(), (newnode,)),
387 (ctx.node(), (n,)),
388 (ctx.node(), (n,)),
388 (newnode, (n,)),
389 (newnode, (n,)),
389 ]
390 ]
390 for ich in internalchanges:
391 for ich in internalchanges:
391 replacements.append((ich, (n,)))
392 replacements.append((ich, (n,)))
392 return repo[n], replacements
393 return repo[n], replacements
393
394
394 def drop(ui, repo, ctx, ha, opts):
395 def drop(ui, repo, ctx, ha, opts):
395 return ctx, [(repo[ha].node(), ())]
396 return ctx, [(repo[ha].node(), ())]
396
397
397
398
398 def message(ui, repo, ctx, ha, opts):
399 def message(ui, repo, ctx, ha, opts):
399 oldctx = repo[ha]
400 oldctx = repo[ha]
400 hg.update(repo, ctx.node())
401 hg.update(repo, ctx.node())
401 stats = applychanges(ui, repo, oldctx, opts)
402 stats = applychanges(ui, repo, oldctx, opts)
402 if stats and stats[3] > 0:
403 if stats and stats[3] > 0:
403 raise error.InterventionRequired(
404 raise error.InterventionRequired(
404 _('Fix up the change and run hg histedit --continue'))
405 _('Fix up the change and run hg histedit --continue'))
405 message = oldctx.description()
406 message = oldctx.description()
406 commit = commitfuncfor(repo, oldctx)
407 commit = commitfuncfor(repo, oldctx)
407 new = commit(text=message, user=oldctx.user(), date=oldctx.date(),
408 new = commit(text=message, user=oldctx.user(), date=oldctx.date(),
408 extra=oldctx.extra(),
409 extra=oldctx.extra(),
409 editor=cmdutil.getcommiteditor(edit=True))
410 editor=cmdutil.getcommiteditor(edit=True))
410 newctx = repo[new]
411 newctx = repo[new]
411 if oldctx.node() != newctx.node():
412 if oldctx.node() != newctx.node():
412 return newctx, [(oldctx.node(), (new,))]
413 return newctx, [(oldctx.node(), (new,))]
413 # We didn't make an edit, so just indicate no replaced nodes
414 # We didn't make an edit, so just indicate no replaced nodes
414 return newctx, []
415 return newctx, []
415
416
416 def findoutgoing(ui, repo, remote=None, force=False, opts={}):
417 def findoutgoing(ui, repo, remote=None, force=False, opts={}):
417 """utility function to find the first outgoing changeset
418 """utility function to find the first outgoing changeset
418
419
419 Used by initialisation code"""
420 Used by initialisation code"""
420 dest = ui.expandpath(remote or 'default-push', remote or 'default')
421 dest = ui.expandpath(remote or 'default-push', remote or 'default')
421 dest, revs = hg.parseurl(dest, None)[:2]
422 dest, revs = hg.parseurl(dest, None)[:2]
422 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
423 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
423
424
424 revs, checkout = hg.addbranchrevs(repo, repo, revs, None)
425 revs, checkout = hg.addbranchrevs(repo, repo, revs, None)
425 other = hg.peer(repo, opts, dest)
426 other = hg.peer(repo, opts, dest)
426
427
427 if revs:
428 if revs:
428 revs = [repo.lookup(rev) for rev in revs]
429 revs = [repo.lookup(rev) for rev in revs]
429
430
430 outgoing = discovery.findcommonoutgoing(repo, other, revs, force=force)
431 outgoing = discovery.findcommonoutgoing(repo, other, revs, force=force)
431 if not outgoing.missing:
432 if not outgoing.missing:
432 raise util.Abort(_('no outgoing ancestors'))
433 raise util.Abort(_('no outgoing ancestors'))
433 roots = list(repo.revs("roots(%ln)", outgoing.missing))
434 roots = list(repo.revs("roots(%ln)", outgoing.missing))
434 if 1 < len(roots):
435 if 1 < len(roots):
435 msg = _('there are ambiguous outgoing revisions')
436 msg = _('there are ambiguous outgoing revisions')
436 hint = _('see "hg help histedit" for more detail')
437 hint = _('see "hg help histedit" for more detail')
437 raise util.Abort(msg, hint=hint)
438 raise util.Abort(msg, hint=hint)
438 return repo.lookup(roots[0])
439 return repo.lookup(roots[0])
439
440
440 actiontable = {'p': pick,
441 actiontable = {'p': pick,
441 'pick': pick,
442 'pick': pick,
442 'e': edit,
443 'e': edit,
443 'edit': edit,
444 'edit': edit,
444 'f': fold,
445 'f': fold,
445 'fold': fold,
446 'fold': fold,
446 'd': drop,
447 'd': drop,
447 'drop': drop,
448 'drop': drop,
448 'm': message,
449 'm': message,
449 'mess': message,
450 'mess': message,
450 }
451 }
451
452
452 @command('histedit',
453 @command('histedit',
453 [('', 'commands', '',
454 [('', 'commands', '',
454 _('Read history edits from the specified file.')),
455 _('Read history edits from the specified file.')),
455 ('c', 'continue', False, _('continue an edit already in progress')),
456 ('c', 'continue', False, _('continue an edit already in progress')),
456 ('k', 'keep', False,
457 ('k', 'keep', False,
457 _("don't strip old nodes after edit is complete")),
458 _("don't strip old nodes after edit is complete")),
458 ('', 'abort', False, _('abort an edit in progress')),
459 ('', 'abort', False, _('abort an edit in progress')),
459 ('o', 'outgoing', False, _('changesets not found in destination')),
460 ('o', 'outgoing', False, _('changesets not found in destination')),
460 ('f', 'force', False,
461 ('f', 'force', False,
461 _('force outgoing even for unrelated repositories')),
462 _('force outgoing even for unrelated repositories')),
462 ('r', 'rev', [], _('first revision to be edited'))],
463 ('r', 'rev', [], _('first revision to be edited'))],
463 _("ANCESTOR | --outgoing [URL]"))
464 _("ANCESTOR | --outgoing [URL]"))
464 def histedit(ui, repo, *freeargs, **opts):
465 def histedit(ui, repo, *freeargs, **opts):
465 """interactively edit changeset history
466 """interactively edit changeset history
466
467
467 This command edits changesets between ANCESTOR and the parent of
468 This command edits changesets between ANCESTOR and the parent of
468 the working directory.
469 the working directory.
469
470
470 With --outgoing, this edits changesets not found in the
471 With --outgoing, this edits changesets not found in the
471 destination repository. If URL of the destination is omitted, the
472 destination repository. If URL of the destination is omitted, the
472 'default-push' (or 'default') path will be used.
473 'default-push' (or 'default') path will be used.
473
474
474 For safety, this command is aborted, also if there are ambiguous
475 For safety, this command is aborted, also if there are ambiguous
475 outgoing revisions which may confuse users: for example, there are
476 outgoing revisions which may confuse users: for example, there are
476 multiple branches containing outgoing revisions.
477 multiple branches containing outgoing revisions.
477
478
478 Use "min(outgoing() and ::.)" or similar revset specification
479 Use "min(outgoing() and ::.)" or similar revset specification
479 instead of --outgoing to specify edit target revision exactly in
480 instead of --outgoing to specify edit target revision exactly in
480 such ambiguous situation. See :hg:`help revsets` for detail about
481 such ambiguous situation. See :hg:`help revsets` for detail about
481 selecting revisions.
482 selecting revisions.
482
483
483 Returns 0 on success, 1 if user intervention is required (not only
484 Returns 0 on success, 1 if user intervention is required (not only
484 for intentional "edit" command, but also for resolving unexpected
485 for intentional "edit" command, but also for resolving unexpected
485 conflicts).
486 conflicts).
486 """
487 """
487 lock = wlock = None
488 lock = wlock = None
488 try:
489 try:
489 wlock = repo.wlock()
490 wlock = repo.wlock()
490 lock = repo.lock()
491 lock = repo.lock()
491 _histedit(ui, repo, *freeargs, **opts)
492 _histedit(ui, repo, *freeargs, **opts)
492 finally:
493 finally:
493 release(lock, wlock)
494 release(lock, wlock)
494
495
495 def _histedit(ui, repo, *freeargs, **opts):
496 def _histedit(ui, repo, *freeargs, **opts):
496 # TODO only abort if we try and histedit mq patches, not just
497 # TODO only abort if we try and histedit mq patches, not just
497 # blanket if mq patches are applied somewhere
498 # blanket if mq patches are applied somewhere
498 mq = getattr(repo, 'mq', None)
499 mq = getattr(repo, 'mq', None)
499 if mq and mq.applied:
500 if mq and mq.applied:
500 raise util.Abort(_('source has mq patches applied'))
501 raise util.Abort(_('source has mq patches applied'))
501
502
502 # basic argument incompatibility processing
503 # basic argument incompatibility processing
503 outg = opts.get('outgoing')
504 outg = opts.get('outgoing')
504 cont = opts.get('continue')
505 cont = opts.get('continue')
505 abort = opts.get('abort')
506 abort = opts.get('abort')
506 force = opts.get('force')
507 force = opts.get('force')
507 rules = opts.get('commands', '')
508 rules = opts.get('commands', '')
508 revs = opts.get('rev', [])
509 revs = opts.get('rev', [])
509 goal = 'new' # This invocation goal, in new, continue, abort
510 goal = 'new' # This invocation goal, in new, continue, abort
510 if force and not outg:
511 if force and not outg:
511 raise util.Abort(_('--force only allowed with --outgoing'))
512 raise util.Abort(_('--force only allowed with --outgoing'))
512 if cont:
513 if cont:
513 if util.any((outg, abort, revs, freeargs, rules)):
514 if util.any((outg, abort, revs, freeargs, rules)):
514 raise util.Abort(_('no arguments allowed with --continue'))
515 raise util.Abort(_('no arguments allowed with --continue'))
515 goal = 'continue'
516 goal = 'continue'
516 elif abort:
517 elif abort:
517 if util.any((outg, revs, freeargs, rules)):
518 if util.any((outg, revs, freeargs, rules)):
518 raise util.Abort(_('no arguments allowed with --abort'))
519 raise util.Abort(_('no arguments allowed with --abort'))
519 goal = 'abort'
520 goal = 'abort'
520 else:
521 else:
521 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
522 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
522 raise util.Abort(_('history edit already in progress, try '
523 raise util.Abort(_('history edit already in progress, try '
523 '--continue or --abort'))
524 '--continue or --abort'))
524 if outg:
525 if outg:
525 if revs:
526 if revs:
526 raise util.Abort(_('no revisions allowed with --outgoing'))
527 raise util.Abort(_('no revisions allowed with --outgoing'))
527 if len(freeargs) > 1:
528 if len(freeargs) > 1:
528 raise util.Abort(
529 raise util.Abort(
529 _('only one repo argument allowed with --outgoing'))
530 _('only one repo argument allowed with --outgoing'))
530 else:
531 else:
531 revs.extend(freeargs)
532 revs.extend(freeargs)
532 if len(revs) != 1:
533 if len(revs) != 1:
533 raise util.Abort(
534 raise util.Abort(
534 _('histedit requires exactly one ancestor revision'))
535 _('histedit requires exactly one ancestor revision'))
535
536
536
537
537 if goal == 'continue':
538 if goal == 'continue':
538 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo)
539 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo)
539 parentctx = repo[parentctxnode]
540 parentctx = repo[parentctxnode]
540 parentctx, repl = bootstrapcontinue(ui, repo, parentctx, rules, opts)
541 parentctx, repl = bootstrapcontinue(ui, repo, parentctx, rules, opts)
541 replacements.extend(repl)
542 replacements.extend(repl)
542 elif goal == 'abort':
543 elif goal == 'abort':
543 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo)
544 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo)
544 mapping, tmpnodes, leafs, _ntm = processreplacement(repo, replacements)
545 mapping, tmpnodes, leafs, _ntm = processreplacement(repo, replacements)
545 ui.debug('restore wc to old parent %s\n' % node.short(topmost))
546 ui.debug('restore wc to old parent %s\n' % node.short(topmost))
546 # check whether we should update away
547 # check whether we should update away
547 parentnodes = [c.node() for c in repo[None].parents()]
548 parentnodes = [c.node() for c in repo[None].parents()]
548 for n in leafs | set([parentctxnode]):
549 for n in leafs | set([parentctxnode]):
549 if n in parentnodes:
550 if n in parentnodes:
550 hg.clean(repo, topmost)
551 hg.clean(repo, topmost)
551 break
552 break
552 else:
553 else:
553 pass
554 pass
554 cleanupnode(ui, repo, 'created', tmpnodes)
555 cleanupnode(ui, repo, 'created', tmpnodes)
555 cleanupnode(ui, repo, 'temp', leafs)
556 cleanupnode(ui, repo, 'temp', leafs)
556 os.unlink(os.path.join(repo.path, 'histedit-state'))
557 os.unlink(os.path.join(repo.path, 'histedit-state'))
557 return
558 return
558 else:
559 else:
559 cmdutil.checkunfinished(repo)
560 cmdutil.checkunfinished(repo)
560 cmdutil.bailifchanged(repo)
561 cmdutil.bailifchanged(repo)
561
562
562 topmost, empty = repo.dirstate.parents()
563 topmost, empty = repo.dirstate.parents()
563 if outg:
564 if outg:
564 if freeargs:
565 if freeargs:
565 remote = freeargs[0]
566 remote = freeargs[0]
566 else:
567 else:
567 remote = None
568 remote = None
568 root = findoutgoing(ui, repo, remote, force, opts)
569 root = findoutgoing(ui, repo, remote, force, opts)
569 else:
570 else:
570 rootrevs = list(repo.set('roots(%lr)', revs))
571 rr = list(repo.set('roots(%ld)', scmutil.revrange(repo, revs)))
571 if len(rootrevs) != 1:
572 if len(rr) != 1:
572 raise util.Abort(_('The specified revisions must have '
573 raise util.Abort(_('The specified revisions must have '
573 'exactly one common root'))
574 'exactly one common root'))
574 root = rootrevs[0].node()
575 root = rr[0].node()
575
576
576 keep = opts.get('keep', False)
577 keep = opts.get('keep', False)
577 revs = between(repo, root, topmost, keep)
578 revs = between(repo, root, topmost, keep)
578 if not revs:
579 if not revs:
579 raise util.Abort(_('%s is not an ancestor of working directory') %
580 raise util.Abort(_('%s is not an ancestor of working directory') %
580 node.short(root))
581 node.short(root))
581
582
582 ctxs = [repo[r] for r in revs]
583 ctxs = [repo[r] for r in revs]
583 if not rules:
584 if not rules:
584 rules = '\n'.join([makedesc(c) for c in ctxs])
585 rules = '\n'.join([makedesc(c) for c in ctxs])
585 rules += '\n\n'
586 rules += '\n\n'
586 rules += editcomment % (node.short(root), node.short(topmost))
587 rules += editcomment % (node.short(root), node.short(topmost))
587 rules = ui.edit(rules, ui.username())
588 rules = ui.edit(rules, ui.username())
588 # Save edit rules in .hg/histedit-last-edit.txt in case
589 # Save edit rules in .hg/histedit-last-edit.txt in case
589 # the user needs to ask for help after something
590 # the user needs to ask for help after something
590 # surprising happens.
591 # surprising happens.
591 f = open(repo.join('histedit-last-edit.txt'), 'w')
592 f = open(repo.join('histedit-last-edit.txt'), 'w')
592 f.write(rules)
593 f.write(rules)
593 f.close()
594 f.close()
594 else:
595 else:
595 if rules == '-':
596 if rules == '-':
596 f = sys.stdin
597 f = sys.stdin
597 else:
598 else:
598 f = open(rules)
599 f = open(rules)
599 rules = f.read()
600 rules = f.read()
600 f.close()
601 f.close()
601 rules = [l for l in (r.strip() for r in rules.splitlines())
602 rules = [l for l in (r.strip() for r in rules.splitlines())
602 if l and not l[0] == '#']
603 if l and not l[0] == '#']
603 rules = verifyrules(rules, repo, ctxs)
604 rules = verifyrules(rules, repo, ctxs)
604
605
605 parentctx = repo[root].parents()[0]
606 parentctx = repo[root].parents()[0]
606 keep = opts.get('keep', False)
607 keep = opts.get('keep', False)
607 replacements = []
608 replacements = []
608
609
609
610
610 while rules:
611 while rules:
611 writestate(repo, parentctx.node(), rules, keep, topmost, replacements)
612 writestate(repo, parentctx.node(), rules, keep, topmost, replacements)
612 action, ha = rules.pop(0)
613 action, ha = rules.pop(0)
613 ui.debug('histedit: processing %s %s\n' % (action, ha))
614 ui.debug('histedit: processing %s %s\n' % (action, ha))
614 actfunc = actiontable[action]
615 actfunc = actiontable[action]
615 parentctx, replacement_ = actfunc(ui, repo, parentctx, ha, opts)
616 parentctx, replacement_ = actfunc(ui, repo, parentctx, ha, opts)
616 replacements.extend(replacement_)
617 replacements.extend(replacement_)
617
618
618 hg.update(repo, parentctx.node())
619 hg.update(repo, parentctx.node())
619
620
620 mapping, tmpnodes, created, ntm = processreplacement(repo, replacements)
621 mapping, tmpnodes, created, ntm = processreplacement(repo, replacements)
621 if mapping:
622 if mapping:
622 for prec, succs in mapping.iteritems():
623 for prec, succs in mapping.iteritems():
623 if not succs:
624 if not succs:
624 ui.debug('histedit: %s is dropped\n' % node.short(prec))
625 ui.debug('histedit: %s is dropped\n' % node.short(prec))
625 else:
626 else:
626 ui.debug('histedit: %s is replaced by %s\n' % (
627 ui.debug('histedit: %s is replaced by %s\n' % (
627 node.short(prec), node.short(succs[0])))
628 node.short(prec), node.short(succs[0])))
628 if len(succs) > 1:
629 if len(succs) > 1:
629 m = 'histedit: %s'
630 m = 'histedit: %s'
630 for n in succs[1:]:
631 for n in succs[1:]:
631 ui.debug(m % node.short(n))
632 ui.debug(m % node.short(n))
632
633
633 if not keep:
634 if not keep:
634 if mapping:
635 if mapping:
635 movebookmarks(ui, repo, mapping, topmost, ntm)
636 movebookmarks(ui, repo, mapping, topmost, ntm)
636 # TODO update mq state
637 # TODO update mq state
637 if obsolete._enabled:
638 if obsolete._enabled:
638 markers = []
639 markers = []
639 # sort by revision number because it sound "right"
640 # sort by revision number because it sound "right"
640 for prec in sorted(mapping, key=repo.changelog.rev):
641 for prec in sorted(mapping, key=repo.changelog.rev):
641 succs = mapping[prec]
642 succs = mapping[prec]
642 markers.append((repo[prec],
643 markers.append((repo[prec],
643 tuple(repo[s] for s in succs)))
644 tuple(repo[s] for s in succs)))
644 if markers:
645 if markers:
645 obsolete.createmarkers(repo, markers)
646 obsolete.createmarkers(repo, markers)
646 else:
647 else:
647 cleanupnode(ui, repo, 'replaced', mapping)
648 cleanupnode(ui, repo, 'replaced', mapping)
648
649
649 cleanupnode(ui, repo, 'temp', tmpnodes)
650 cleanupnode(ui, repo, 'temp', tmpnodes)
650 os.unlink(os.path.join(repo.path, 'histedit-state'))
651 os.unlink(os.path.join(repo.path, 'histedit-state'))
651 if os.path.exists(repo.sjoin('undo')):
652 if os.path.exists(repo.sjoin('undo')):
652 os.unlink(repo.sjoin('undo'))
653 os.unlink(repo.sjoin('undo'))
653
654
654 def gatherchildren(repo, ctx):
655 def gatherchildren(repo, ctx):
655 # is there any new commit between the expected parent and "."
656 # is there any new commit between the expected parent and "."
656 #
657 #
657 # note: does not take non linear new change in account (but previous
658 # note: does not take non linear new change in account (but previous
658 # implementation didn't used them anyway (issue3655)
659 # implementation didn't used them anyway (issue3655)
659 newchildren = [c.node() for c in repo.set('(%d::.)', ctx)]
660 newchildren = [c.node() for c in repo.set('(%d::.)', ctx)]
660 if ctx.node() != node.nullid:
661 if ctx.node() != node.nullid:
661 if not newchildren:
662 if not newchildren:
662 # `ctx` should match but no result. This means that
663 # `ctx` should match but no result. This means that
663 # currentnode is not a descendant from ctx.
664 # currentnode is not a descendant from ctx.
664 msg = _('%s is not an ancestor of working directory')
665 msg = _('%s is not an ancestor of working directory')
665 hint = _('use "histedit --abort" to clear broken state')
666 hint = _('use "histedit --abort" to clear broken state')
666 raise util.Abort(msg % ctx, hint=hint)
667 raise util.Abort(msg % ctx, hint=hint)
667 newchildren.pop(0) # remove ctx
668 newchildren.pop(0) # remove ctx
668 return newchildren
669 return newchildren
669
670
670 def bootstrapcontinue(ui, repo, parentctx, rules, opts):
671 def bootstrapcontinue(ui, repo, parentctx, rules, opts):
671 action, currentnode = rules.pop(0)
672 action, currentnode = rules.pop(0)
672 ctx = repo[currentnode]
673 ctx = repo[currentnode]
673
674
674 newchildren = gatherchildren(repo, parentctx)
675 newchildren = gatherchildren(repo, parentctx)
675
676
676 # Commit dirty working directory if necessary
677 # Commit dirty working directory if necessary
677 new = None
678 new = None
678 m, a, r, d = repo.status()[:4]
679 m, a, r, d = repo.status()[:4]
679 if m or a or r or d:
680 if m or a or r or d:
680 # prepare the message for the commit to comes
681 # prepare the message for the commit to comes
681 if action in ('f', 'fold'):
682 if action in ('f', 'fold'):
682 message = 'fold-temp-revision %s' % currentnode
683 message = 'fold-temp-revision %s' % currentnode
683 else:
684 else:
684 message = ctx.description()
685 message = ctx.description()
685 editopt = action in ('e', 'edit', 'm', 'mess')
686 editopt = action in ('e', 'edit', 'm', 'mess')
686 editor = cmdutil.getcommiteditor(edit=editopt)
687 editor = cmdutil.getcommiteditor(edit=editopt)
687 commit = commitfuncfor(repo, ctx)
688 commit = commitfuncfor(repo, ctx)
688 new = commit(text=message, user=ctx.user(),
689 new = commit(text=message, user=ctx.user(),
689 date=ctx.date(), extra=ctx.extra(),
690 date=ctx.date(), extra=ctx.extra(),
690 editor=editor)
691 editor=editor)
691 if new is not None:
692 if new is not None:
692 newchildren.append(new)
693 newchildren.append(new)
693
694
694 replacements = []
695 replacements = []
695 # track replacements
696 # track replacements
696 if ctx.node() not in newchildren:
697 if ctx.node() not in newchildren:
697 # note: new children may be empty when the changeset is dropped.
698 # note: new children may be empty when the changeset is dropped.
698 # this happen e.g during conflicting pick where we revert content
699 # this happen e.g during conflicting pick where we revert content
699 # to parent.
700 # to parent.
700 replacements.append((ctx.node(), tuple(newchildren)))
701 replacements.append((ctx.node(), tuple(newchildren)))
701
702
702 if action in ('f', 'fold'):
703 if action in ('f', 'fold'):
703 if newchildren:
704 if newchildren:
704 # finalize fold operation if applicable
705 # finalize fold operation if applicable
705 if new is None:
706 if new is None:
706 new = newchildren[-1]
707 new = newchildren[-1]
707 else:
708 else:
708 newchildren.pop() # remove new from internal changes
709 newchildren.pop() # remove new from internal changes
709 parentctx, repl = finishfold(ui, repo, parentctx, ctx, new, opts,
710 parentctx, repl = finishfold(ui, repo, parentctx, ctx, new, opts,
710 newchildren)
711 newchildren)
711 replacements.extend(repl)
712 replacements.extend(repl)
712 else:
713 else:
713 # newchildren is empty if the fold did not result in any commit
714 # newchildren is empty if the fold did not result in any commit
714 # this happen when all folded change are discarded during the
715 # this happen when all folded change are discarded during the
715 # merge.
716 # merge.
716 replacements.append((ctx.node(), (parentctx.node(),)))
717 replacements.append((ctx.node(), (parentctx.node(),)))
717 elif newchildren:
718 elif newchildren:
718 # otherwise update "parentctx" before proceeding to further operation
719 # otherwise update "parentctx" before proceeding to further operation
719 parentctx = repo[newchildren[-1]]
720 parentctx = repo[newchildren[-1]]
720 return parentctx, replacements
721 return parentctx, replacements
721
722
722
723
723 def between(repo, old, new, keep):
724 def between(repo, old, new, keep):
724 """select and validate the set of revision to edit
725 """select and validate the set of revision to edit
725
726
726 When keep is false, the specified set can't have children."""
727 When keep is false, the specified set can't have children."""
727 ctxs = list(repo.set('%n::%n', old, new))
728 ctxs = list(repo.set('%n::%n', old, new))
728 if ctxs and not keep:
729 if ctxs and not keep:
729 if (not obsolete._enabled and
730 if (not obsolete._enabled and
730 repo.revs('(%ld::) - (%ld)', ctxs, ctxs)):
731 repo.revs('(%ld::) - (%ld)', ctxs, ctxs)):
731 raise util.Abort(_('cannot edit history that would orphan nodes'))
732 raise util.Abort(_('cannot edit history that would orphan nodes'))
732 if repo.revs('(%ld) and merge()', ctxs):
733 if repo.revs('(%ld) and merge()', ctxs):
733 raise util.Abort(_('cannot edit history that contains merges'))
734 raise util.Abort(_('cannot edit history that contains merges'))
734 root = ctxs[0] # list is already sorted by repo.set
735 root = ctxs[0] # list is already sorted by repo.set
735 if not root.phase():
736 if not root.phase():
736 raise util.Abort(_('cannot edit immutable changeset: %s') % root)
737 raise util.Abort(_('cannot edit immutable changeset: %s') % root)
737 return [c.node() for c in ctxs]
738 return [c.node() for c in ctxs]
738
739
739
740
740 def writestate(repo, parentnode, rules, keep, topmost, replacements):
741 def writestate(repo, parentnode, rules, keep, topmost, replacements):
741 fp = open(os.path.join(repo.path, 'histedit-state'), 'w')
742 fp = open(os.path.join(repo.path, 'histedit-state'), 'w')
742 pickle.dump((parentnode, rules, keep, topmost, replacements), fp)
743 pickle.dump((parentnode, rules, keep, topmost, replacements), fp)
743 fp.close()
744 fp.close()
744
745
745 def readstate(repo):
746 def readstate(repo):
746 """Returns a tuple of (parentnode, rules, keep, topmost, replacements).
747 """Returns a tuple of (parentnode, rules, keep, topmost, replacements).
747 """
748 """
748 fp = open(os.path.join(repo.path, 'histedit-state'))
749 fp = open(os.path.join(repo.path, 'histedit-state'))
749 return pickle.load(fp)
750 return pickle.load(fp)
750
751
751
752
752 def makedesc(c):
753 def makedesc(c):
753 """build a initial action line for a ctx `c`
754 """build a initial action line for a ctx `c`
754
755
755 line are in the form:
756 line are in the form:
756
757
757 pick <hash> <rev> <summary>
758 pick <hash> <rev> <summary>
758 """
759 """
759 summary = ''
760 summary = ''
760 if c.description():
761 if c.description():
761 summary = c.description().splitlines()[0]
762 summary = c.description().splitlines()[0]
762 line = 'pick %s %d %s' % (c, c.rev(), summary)
763 line = 'pick %s %d %s' % (c, c.rev(), summary)
763 # trim to 80 columns so it's not stupidly wide in my editor
764 # trim to 80 columns so it's not stupidly wide in my editor
764 return util.ellipsis(line, 80)
765 return util.ellipsis(line, 80)
765
766
766 def verifyrules(rules, repo, ctxs):
767 def verifyrules(rules, repo, ctxs):
767 """Verify that there exists exactly one edit rule per given changeset.
768 """Verify that there exists exactly one edit rule per given changeset.
768
769
769 Will abort if there are to many or too few rules, a malformed rule,
770 Will abort if there are to many or too few rules, a malformed rule,
770 or a rule on a changeset outside of the user-given range.
771 or a rule on a changeset outside of the user-given range.
771 """
772 """
772 parsed = []
773 parsed = []
773 expected = set(str(c) for c in ctxs)
774 expected = set(str(c) for c in ctxs)
774 seen = set()
775 seen = set()
775 for r in rules:
776 for r in rules:
776 if ' ' not in r:
777 if ' ' not in r:
777 raise util.Abort(_('malformed line "%s"') % r)
778 raise util.Abort(_('malformed line "%s"') % r)
778 action, rest = r.split(' ', 1)
779 action, rest = r.split(' ', 1)
779 ha = rest.strip().split(' ', 1)[0]
780 ha = rest.strip().split(' ', 1)[0]
780 try:
781 try:
781 ha = str(repo[ha]) # ensure its a short hash
782 ha = str(repo[ha]) # ensure its a short hash
782 except error.RepoError:
783 except error.RepoError:
783 raise util.Abort(_('unknown changeset %s listed') % ha)
784 raise util.Abort(_('unknown changeset %s listed') % ha)
784 if ha not in expected:
785 if ha not in expected:
785 raise util.Abort(
786 raise util.Abort(
786 _('may not use changesets other than the ones listed'))
787 _('may not use changesets other than the ones listed'))
787 if ha in seen:
788 if ha in seen:
788 raise util.Abort(_('duplicated command for changeset %s') % ha)
789 raise util.Abort(_('duplicated command for changeset %s') % ha)
789 seen.add(ha)
790 seen.add(ha)
790 if action not in actiontable:
791 if action not in actiontable:
791 raise util.Abort(_('unknown action "%s"') % action)
792 raise util.Abort(_('unknown action "%s"') % action)
792 parsed.append([action, ha])
793 parsed.append([action, ha])
793 missing = sorted(expected - seen) # sort to stabilize output
794 missing = sorted(expected - seen) # sort to stabilize output
794 if missing:
795 if missing:
795 raise util.Abort(_('missing rules for changeset %s') % missing[0],
796 raise util.Abort(_('missing rules for changeset %s') % missing[0],
796 hint=_('do you want to use the drop action?'))
797 hint=_('do you want to use the drop action?'))
797 return parsed
798 return parsed
798
799
799 def processreplacement(repo, replacements):
800 def processreplacement(repo, replacements):
800 """process the list of replacements to return
801 """process the list of replacements to return
801
802
802 1) the final mapping between original and created nodes
803 1) the final mapping between original and created nodes
803 2) the list of temporary node created by histedit
804 2) the list of temporary node created by histedit
804 3) the list of new commit created by histedit"""
805 3) the list of new commit created by histedit"""
805 allsuccs = set()
806 allsuccs = set()
806 replaced = set()
807 replaced = set()
807 fullmapping = {}
808 fullmapping = {}
808 # initialise basic set
809 # initialise basic set
809 # fullmapping record all operation recorded in replacement
810 # fullmapping record all operation recorded in replacement
810 for rep in replacements:
811 for rep in replacements:
811 allsuccs.update(rep[1])
812 allsuccs.update(rep[1])
812 replaced.add(rep[0])
813 replaced.add(rep[0])
813 fullmapping.setdefault(rep[0], set()).update(rep[1])
814 fullmapping.setdefault(rep[0], set()).update(rep[1])
814 new = allsuccs - replaced
815 new = allsuccs - replaced
815 tmpnodes = allsuccs & replaced
816 tmpnodes = allsuccs & replaced
816 # Reduce content fullmapping into direct relation between original nodes
817 # Reduce content fullmapping into direct relation between original nodes
817 # and final node created during history edition
818 # and final node created during history edition
818 # Dropped changeset are replaced by an empty list
819 # Dropped changeset are replaced by an empty list
819 toproceed = set(fullmapping)
820 toproceed = set(fullmapping)
820 final = {}
821 final = {}
821 while toproceed:
822 while toproceed:
822 for x in list(toproceed):
823 for x in list(toproceed):
823 succs = fullmapping[x]
824 succs = fullmapping[x]
824 for s in list(succs):
825 for s in list(succs):
825 if s in toproceed:
826 if s in toproceed:
826 # non final node with unknown closure
827 # non final node with unknown closure
827 # We can't process this now
828 # We can't process this now
828 break
829 break
829 elif s in final:
830 elif s in final:
830 # non final node, replace with closure
831 # non final node, replace with closure
831 succs.remove(s)
832 succs.remove(s)
832 succs.update(final[s])
833 succs.update(final[s])
833 else:
834 else:
834 final[x] = succs
835 final[x] = succs
835 toproceed.remove(x)
836 toproceed.remove(x)
836 # remove tmpnodes from final mapping
837 # remove tmpnodes from final mapping
837 for n in tmpnodes:
838 for n in tmpnodes:
838 del final[n]
839 del final[n]
839 # we expect all changes involved in final to exist in the repo
840 # we expect all changes involved in final to exist in the repo
840 # turn `final` into list (topologically sorted)
841 # turn `final` into list (topologically sorted)
841 nm = repo.changelog.nodemap
842 nm = repo.changelog.nodemap
842 for prec, succs in final.items():
843 for prec, succs in final.items():
843 final[prec] = sorted(succs, key=nm.get)
844 final[prec] = sorted(succs, key=nm.get)
844
845
845 # computed topmost element (necessary for bookmark)
846 # computed topmost element (necessary for bookmark)
846 if new:
847 if new:
847 newtopmost = sorted(new, key=repo.changelog.rev)[-1]
848 newtopmost = sorted(new, key=repo.changelog.rev)[-1]
848 elif not final:
849 elif not final:
849 # Nothing rewritten at all. we won't need `newtopmost`
850 # Nothing rewritten at all. we won't need `newtopmost`
850 # It is the same as `oldtopmost` and `processreplacement` know it
851 # It is the same as `oldtopmost` and `processreplacement` know it
851 newtopmost = None
852 newtopmost = None
852 else:
853 else:
853 # every body died. The newtopmost is the parent of the root.
854 # every body died. The newtopmost is the parent of the root.
854 newtopmost = repo[sorted(final, key=repo.changelog.rev)[0]].p1().node()
855 newtopmost = repo[sorted(final, key=repo.changelog.rev)[0]].p1().node()
855
856
856 return final, tmpnodes, new, newtopmost
857 return final, tmpnodes, new, newtopmost
857
858
858 def movebookmarks(ui, repo, mapping, oldtopmost, newtopmost):
859 def movebookmarks(ui, repo, mapping, oldtopmost, newtopmost):
859 """Move bookmark from old to newly created node"""
860 """Move bookmark from old to newly created node"""
860 if not mapping:
861 if not mapping:
861 # if nothing got rewritten there is not purpose for this function
862 # if nothing got rewritten there is not purpose for this function
862 return
863 return
863 moves = []
864 moves = []
864 for bk, old in sorted(repo._bookmarks.iteritems()):
865 for bk, old in sorted(repo._bookmarks.iteritems()):
865 if old == oldtopmost:
866 if old == oldtopmost:
866 # special case ensure bookmark stay on tip.
867 # special case ensure bookmark stay on tip.
867 #
868 #
868 # This is arguably a feature and we may only want that for the
869 # This is arguably a feature and we may only want that for the
869 # active bookmark. But the behavior is kept compatible with the old
870 # active bookmark. But the behavior is kept compatible with the old
870 # version for now.
871 # version for now.
871 moves.append((bk, newtopmost))
872 moves.append((bk, newtopmost))
872 continue
873 continue
873 base = old
874 base = old
874 new = mapping.get(base, None)
875 new = mapping.get(base, None)
875 if new is None:
876 if new is None:
876 continue
877 continue
877 while not new:
878 while not new:
878 # base is killed, trying with parent
879 # base is killed, trying with parent
879 base = repo[base].p1().node()
880 base = repo[base].p1().node()
880 new = mapping.get(base, (base,))
881 new = mapping.get(base, (base,))
881 # nothing to move
882 # nothing to move
882 moves.append((bk, new[-1]))
883 moves.append((bk, new[-1]))
883 if moves:
884 if moves:
884 marks = repo._bookmarks
885 marks = repo._bookmarks
885 for mark, new in moves:
886 for mark, new in moves:
886 old = marks[mark]
887 old = marks[mark]
887 ui.note(_('histedit: moving bookmarks %s from %s to %s\n')
888 ui.note(_('histedit: moving bookmarks %s from %s to %s\n')
888 % (mark, node.short(old), node.short(new)))
889 % (mark, node.short(old), node.short(new)))
889 marks[mark] = new
890 marks[mark] = new
890 marks.write()
891 marks.write()
891
892
892 def cleanupnode(ui, repo, name, nodes):
893 def cleanupnode(ui, repo, name, nodes):
893 """strip a group of nodes from the repository
894 """strip a group of nodes from the repository
894
895
895 The set of node to strip may contains unknown nodes."""
896 The set of node to strip may contains unknown nodes."""
896 ui.debug('should strip %s nodes %s\n' %
897 ui.debug('should strip %s nodes %s\n' %
897 (name, ', '.join([node.short(n) for n in nodes])))
898 (name, ', '.join([node.short(n) for n in nodes])))
898 lock = None
899 lock = None
899 try:
900 try:
900 lock = repo.lock()
901 lock = repo.lock()
901 # Find all node that need to be stripped
902 # Find all node that need to be stripped
902 # (we hg %lr instead of %ln to silently ignore unknown item
903 # (we hg %lr instead of %ln to silently ignore unknown item
903 nm = repo.changelog.nodemap
904 nm = repo.changelog.nodemap
904 nodes = [n for n in nodes if n in nm]
905 nodes = [n for n in nodes if n in nm]
905 roots = [c.node() for c in repo.set("roots(%ln)", nodes)]
906 roots = [c.node() for c in repo.set("roots(%ln)", nodes)]
906 for c in roots:
907 for c in roots:
907 # We should process node in reverse order to strip tip most first.
908 # We should process node in reverse order to strip tip most first.
908 # but this trigger a bug in changegroup hook.
909 # but this trigger a bug in changegroup hook.
909 # This would reduce bundle overhead
910 # This would reduce bundle overhead
910 repair.strip(ui, repo, c)
911 repair.strip(ui, repo, c)
911 finally:
912 finally:
912 release(lock)
913 release(lock)
913
914
914 def summaryhook(ui, repo):
915 def summaryhook(ui, repo):
915 if not os.path.exists(repo.join('histedit-state')):
916 if not os.path.exists(repo.join('histedit-state')):
916 return
917 return
917 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo)
918 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo)
918 if rules:
919 if rules:
919 # i18n: column positioning for "hg summary"
920 # i18n: column positioning for "hg summary"
920 ui.write(_('hist: %s (histedit --continue)\n') %
921 ui.write(_('hist: %s (histedit --continue)\n') %
921 (ui.label(_('%d remaining'), 'histedit.remaining') %
922 (ui.label(_('%d remaining'), 'histedit.remaining') %
922 len(rules)))
923 len(rules)))
923
924
924 def extsetup(ui):
925 def extsetup(ui):
925 cmdutil.summaryhooks.add('histedit', summaryhook)
926 cmdutil.summaryhooks.add('histedit', summaryhook)
926 cmdutil.unfinishedstates.append(
927 cmdutil.unfinishedstates.append(
927 ['histedit-state', False, True, _('histedit in progress'),
928 ['histedit-state', False, True, _('histedit in progress'),
928 _("use 'hg histedit --continue' or 'hg histedit --abort'")])
929 _("use 'hg histedit --continue' or 'hg histedit --abort'")])
@@ -1,3462 +1,3462 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''manage a stack of patches
8 '''manage a stack of patches
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use :hg:`help command` for more details)::
17 Common tasks (use :hg:`help command` for more details)::
18
18
19 create new patch qnew
19 create new patch qnew
20 import existing patch qimport
20 import existing patch qimport
21
21
22 print patch series qseries
22 print patch series qseries
23 print applied patches qapplied
23 print applied patches qapplied
24
24
25 add known patch to applied stack qpush
25 add known patch to applied stack qpush
26 remove patch from applied stack qpop
26 remove patch from applied stack qpop
27 refresh contents of top applied patch qrefresh
27 refresh contents of top applied patch qrefresh
28
28
29 By default, mq will automatically use git patches when required to
29 By default, mq will automatically use git patches when required to
30 avoid losing file mode changes, copy records, binary files or empty
30 avoid losing file mode changes, copy records, binary files or empty
31 files creations or deletions. This behaviour can be configured with::
31 files creations or deletions. This behaviour can be configured with::
32
32
33 [mq]
33 [mq]
34 git = auto/keep/yes/no
34 git = auto/keep/yes/no
35
35
36 If set to 'keep', mq will obey the [diff] section configuration while
36 If set to 'keep', mq will obey the [diff] section configuration while
37 preserving existing git patches upon qrefresh. If set to 'yes' or
37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 'no', mq will override the [diff] section and always generate git or
38 'no', mq will override the [diff] section and always generate git or
39 regular patches, possibly losing data in the second case.
39 regular patches, possibly losing data in the second case.
40
40
41 It may be desirable for mq changesets to be kept in the secret phase (see
41 It may be desirable for mq changesets to be kept in the secret phase (see
42 :hg:`help phases`), which can be enabled with the following setting::
42 :hg:`help phases`), which can be enabled with the following setting::
43
43
44 [mq]
44 [mq]
45 secret = True
45 secret = True
46
46
47 You will by default be managing a patch queue named "patches". You can
47 You will by default be managing a patch queue named "patches". You can
48 create other, independent patch queues with the :hg:`qqueue` command.
48 create other, independent patch queues with the :hg:`qqueue` command.
49
49
50 If the working directory contains uncommitted files, qpush, qpop and
50 If the working directory contains uncommitted files, qpush, qpop and
51 qgoto abort immediately. If -f/--force is used, the changes are
51 qgoto abort immediately. If -f/--force is used, the changes are
52 discarded. Setting::
52 discarded. Setting::
53
53
54 [mq]
54 [mq]
55 keepchanges = True
55 keepchanges = True
56
56
57 make them behave as if --keep-changes were passed, and non-conflicting
57 make them behave as if --keep-changes were passed, and non-conflicting
58 local changes will be tolerated and preserved. If incompatible options
58 local changes will be tolerated and preserved. If incompatible options
59 such as -f/--force or --exact are passed, this setting is ignored.
59 such as -f/--force or --exact are passed, this setting is ignored.
60
60
61 This extension used to provide a strip command. This command now lives
61 This extension used to provide a strip command. This command now lives
62 in the strip extension.
62 in the strip extension.
63 '''
63 '''
64
64
65 from mercurial.i18n import _
65 from mercurial.i18n import _
66 from mercurial.node import bin, hex, short, nullid, nullrev
66 from mercurial.node import bin, hex, short, nullid, nullrev
67 from mercurial.lock import release
67 from mercurial.lock import release
68 from mercurial import commands, cmdutil, hg, scmutil, util, revset
68 from mercurial import commands, cmdutil, hg, scmutil, util, revset
69 from mercurial import extensions, error, phases
69 from mercurial import extensions, error, phases
70 from mercurial import patch as patchmod
70 from mercurial import patch as patchmod
71 from mercurial import localrepo
71 from mercurial import localrepo
72 from mercurial import subrepo
72 from mercurial import subrepo
73 import os, re, errno, shutil
73 import os, re, errno, shutil
74
74
75 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
75 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
76
76
77 cmdtable = {}
77 cmdtable = {}
78 command = cmdutil.command(cmdtable)
78 command = cmdutil.command(cmdtable)
79 testedwith = 'internal'
79 testedwith = 'internal'
80
80
81 # force load strip extension formerly included in mq and import some utility
81 # force load strip extension formerly included in mq and import some utility
82 try:
82 try:
83 stripext = extensions.find('strip')
83 stripext = extensions.find('strip')
84 except KeyError:
84 except KeyError:
85 # note: load is lazy so we could avoid the try-except,
85 # note: load is lazy so we could avoid the try-except,
86 # but I (marmoute) prefer this explicit code.
86 # but I (marmoute) prefer this explicit code.
87 class dummyui(object):
87 class dummyui(object):
88 def debug(self, msg):
88 def debug(self, msg):
89 pass
89 pass
90 stripext = extensions.load(dummyui(), 'strip', '')
90 stripext = extensions.load(dummyui(), 'strip', '')
91
91
92 strip = stripext.strip
92 strip = stripext.strip
93 checksubstate = stripext.checksubstate
93 checksubstate = stripext.checksubstate
94 checklocalchanges = stripext.checklocalchanges
94 checklocalchanges = stripext.checklocalchanges
95
95
96
96
97 # Patch names looks like unix-file names.
97 # Patch names looks like unix-file names.
98 # They must be joinable with queue directory and result in the patch path.
98 # They must be joinable with queue directory and result in the patch path.
99 normname = util.normpath
99 normname = util.normpath
100
100
101 class statusentry(object):
101 class statusentry(object):
102 def __init__(self, node, name):
102 def __init__(self, node, name):
103 self.node, self.name = node, name
103 self.node, self.name = node, name
104 def __repr__(self):
104 def __repr__(self):
105 return hex(self.node) + ':' + self.name
105 return hex(self.node) + ':' + self.name
106
106
107 class patchheader(object):
107 class patchheader(object):
108 def __init__(self, pf, plainmode=False):
108 def __init__(self, pf, plainmode=False):
109 def eatdiff(lines):
109 def eatdiff(lines):
110 while lines:
110 while lines:
111 l = lines[-1]
111 l = lines[-1]
112 if (l.startswith("diff -") or
112 if (l.startswith("diff -") or
113 l.startswith("Index:") or
113 l.startswith("Index:") or
114 l.startswith("===========")):
114 l.startswith("===========")):
115 del lines[-1]
115 del lines[-1]
116 else:
116 else:
117 break
117 break
118 def eatempty(lines):
118 def eatempty(lines):
119 while lines:
119 while lines:
120 if not lines[-1].strip():
120 if not lines[-1].strip():
121 del lines[-1]
121 del lines[-1]
122 else:
122 else:
123 break
123 break
124
124
125 message = []
125 message = []
126 comments = []
126 comments = []
127 user = None
127 user = None
128 date = None
128 date = None
129 parent = None
129 parent = None
130 format = None
130 format = None
131 subject = None
131 subject = None
132 branch = None
132 branch = None
133 nodeid = None
133 nodeid = None
134 diffstart = 0
134 diffstart = 0
135
135
136 for line in file(pf):
136 for line in file(pf):
137 line = line.rstrip()
137 line = line.rstrip()
138 if (line.startswith('diff --git')
138 if (line.startswith('diff --git')
139 or (diffstart and line.startswith('+++ '))):
139 or (diffstart and line.startswith('+++ '))):
140 diffstart = 2
140 diffstart = 2
141 break
141 break
142 diffstart = 0 # reset
142 diffstart = 0 # reset
143 if line.startswith("--- "):
143 if line.startswith("--- "):
144 diffstart = 1
144 diffstart = 1
145 continue
145 continue
146 elif format == "hgpatch":
146 elif format == "hgpatch":
147 # parse values when importing the result of an hg export
147 # parse values when importing the result of an hg export
148 if line.startswith("# User "):
148 if line.startswith("# User "):
149 user = line[7:]
149 user = line[7:]
150 elif line.startswith("# Date "):
150 elif line.startswith("# Date "):
151 date = line[7:]
151 date = line[7:]
152 elif line.startswith("# Parent "):
152 elif line.startswith("# Parent "):
153 parent = line[9:].lstrip()
153 parent = line[9:].lstrip()
154 elif line.startswith("# Branch "):
154 elif line.startswith("# Branch "):
155 branch = line[9:]
155 branch = line[9:]
156 elif line.startswith("# Node ID "):
156 elif line.startswith("# Node ID "):
157 nodeid = line[10:]
157 nodeid = line[10:]
158 elif not line.startswith("# ") and line:
158 elif not line.startswith("# ") and line:
159 message.append(line)
159 message.append(line)
160 format = None
160 format = None
161 elif line == '# HG changeset patch':
161 elif line == '# HG changeset patch':
162 message = []
162 message = []
163 format = "hgpatch"
163 format = "hgpatch"
164 elif (format != "tagdone" and (line.startswith("Subject: ") or
164 elif (format != "tagdone" and (line.startswith("Subject: ") or
165 line.startswith("subject: "))):
165 line.startswith("subject: "))):
166 subject = line[9:]
166 subject = line[9:]
167 format = "tag"
167 format = "tag"
168 elif (format != "tagdone" and (line.startswith("From: ") or
168 elif (format != "tagdone" and (line.startswith("From: ") or
169 line.startswith("from: "))):
169 line.startswith("from: "))):
170 user = line[6:]
170 user = line[6:]
171 format = "tag"
171 format = "tag"
172 elif (format != "tagdone" and (line.startswith("Date: ") or
172 elif (format != "tagdone" and (line.startswith("Date: ") or
173 line.startswith("date: "))):
173 line.startswith("date: "))):
174 date = line[6:]
174 date = line[6:]
175 format = "tag"
175 format = "tag"
176 elif format == "tag" and line == "":
176 elif format == "tag" and line == "":
177 # when looking for tags (subject: from: etc) they
177 # when looking for tags (subject: from: etc) they
178 # end once you find a blank line in the source
178 # end once you find a blank line in the source
179 format = "tagdone"
179 format = "tagdone"
180 elif message or line:
180 elif message or line:
181 message.append(line)
181 message.append(line)
182 comments.append(line)
182 comments.append(line)
183
183
184 eatdiff(message)
184 eatdiff(message)
185 eatdiff(comments)
185 eatdiff(comments)
186 # Remember the exact starting line of the patch diffs before consuming
186 # Remember the exact starting line of the patch diffs before consuming
187 # empty lines, for external use by TortoiseHg and others
187 # empty lines, for external use by TortoiseHg and others
188 self.diffstartline = len(comments)
188 self.diffstartline = len(comments)
189 eatempty(message)
189 eatempty(message)
190 eatempty(comments)
190 eatempty(comments)
191
191
192 # make sure message isn't empty
192 # make sure message isn't empty
193 if format and format.startswith("tag") and subject:
193 if format and format.startswith("tag") and subject:
194 message.insert(0, "")
194 message.insert(0, "")
195 message.insert(0, subject)
195 message.insert(0, subject)
196
196
197 self.message = message
197 self.message = message
198 self.comments = comments
198 self.comments = comments
199 self.user = user
199 self.user = user
200 self.date = date
200 self.date = date
201 self.parent = parent
201 self.parent = parent
202 # nodeid and branch are for external use by TortoiseHg and others
202 # nodeid and branch are for external use by TortoiseHg and others
203 self.nodeid = nodeid
203 self.nodeid = nodeid
204 self.branch = branch
204 self.branch = branch
205 self.haspatch = diffstart > 1
205 self.haspatch = diffstart > 1
206 self.plainmode = plainmode
206 self.plainmode = plainmode
207
207
208 def setuser(self, user):
208 def setuser(self, user):
209 if not self.updateheader(['From: ', '# User '], user):
209 if not self.updateheader(['From: ', '# User '], user):
210 try:
210 try:
211 patchheaderat = self.comments.index('# HG changeset patch')
211 patchheaderat = self.comments.index('# HG changeset patch')
212 self.comments.insert(patchheaderat + 1, '# User ' + user)
212 self.comments.insert(patchheaderat + 1, '# User ' + user)
213 except ValueError:
213 except ValueError:
214 if self.plainmode or self._hasheader(['Date: ']):
214 if self.plainmode or self._hasheader(['Date: ']):
215 self.comments = ['From: ' + user] + self.comments
215 self.comments = ['From: ' + user] + self.comments
216 else:
216 else:
217 tmp = ['# HG changeset patch', '# User ' + user, '']
217 tmp = ['# HG changeset patch', '# User ' + user, '']
218 self.comments = tmp + self.comments
218 self.comments = tmp + self.comments
219 self.user = user
219 self.user = user
220
220
221 def setdate(self, date):
221 def setdate(self, date):
222 if not self.updateheader(['Date: ', '# Date '], date):
222 if not self.updateheader(['Date: ', '# Date '], date):
223 try:
223 try:
224 patchheaderat = self.comments.index('# HG changeset patch')
224 patchheaderat = self.comments.index('# HG changeset patch')
225 self.comments.insert(patchheaderat + 1, '# Date ' + date)
225 self.comments.insert(patchheaderat + 1, '# Date ' + date)
226 except ValueError:
226 except ValueError:
227 if self.plainmode or self._hasheader(['From: ']):
227 if self.plainmode or self._hasheader(['From: ']):
228 self.comments = ['Date: ' + date] + self.comments
228 self.comments = ['Date: ' + date] + self.comments
229 else:
229 else:
230 tmp = ['# HG changeset patch', '# Date ' + date, '']
230 tmp = ['# HG changeset patch', '# Date ' + date, '']
231 self.comments = tmp + self.comments
231 self.comments = tmp + self.comments
232 self.date = date
232 self.date = date
233
233
234 def setparent(self, parent):
234 def setparent(self, parent):
235 if not self.updateheader(['# Parent '], parent):
235 if not self.updateheader(['# Parent '], parent):
236 try:
236 try:
237 patchheaderat = self.comments.index('# HG changeset patch')
237 patchheaderat = self.comments.index('# HG changeset patch')
238 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
238 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
239 except ValueError:
239 except ValueError:
240 pass
240 pass
241 self.parent = parent
241 self.parent = parent
242
242
243 def setmessage(self, message):
243 def setmessage(self, message):
244 if self.comments:
244 if self.comments:
245 self._delmsg()
245 self._delmsg()
246 self.message = [message]
246 self.message = [message]
247 self.comments += self.message
247 self.comments += self.message
248
248
249 def updateheader(self, prefixes, new):
249 def updateheader(self, prefixes, new):
250 '''Update all references to a field in the patch header.
250 '''Update all references to a field in the patch header.
251 Return whether the field is present.'''
251 Return whether the field is present.'''
252 res = False
252 res = False
253 for prefix in prefixes:
253 for prefix in prefixes:
254 for i in xrange(len(self.comments)):
254 for i in xrange(len(self.comments)):
255 if self.comments[i].startswith(prefix):
255 if self.comments[i].startswith(prefix):
256 self.comments[i] = prefix + new
256 self.comments[i] = prefix + new
257 res = True
257 res = True
258 break
258 break
259 return res
259 return res
260
260
261 def _hasheader(self, prefixes):
261 def _hasheader(self, prefixes):
262 '''Check if a header starts with any of the given prefixes.'''
262 '''Check if a header starts with any of the given prefixes.'''
263 for prefix in prefixes:
263 for prefix in prefixes:
264 for comment in self.comments:
264 for comment in self.comments:
265 if comment.startswith(prefix):
265 if comment.startswith(prefix):
266 return True
266 return True
267 return False
267 return False
268
268
269 def __str__(self):
269 def __str__(self):
270 if not self.comments:
270 if not self.comments:
271 return ''
271 return ''
272 return '\n'.join(self.comments) + '\n\n'
272 return '\n'.join(self.comments) + '\n\n'
273
273
274 def _delmsg(self):
274 def _delmsg(self):
275 '''Remove existing message, keeping the rest of the comments fields.
275 '''Remove existing message, keeping the rest of the comments fields.
276 If comments contains 'subject: ', message will prepend
276 If comments contains 'subject: ', message will prepend
277 the field and a blank line.'''
277 the field and a blank line.'''
278 if self.message:
278 if self.message:
279 subj = 'subject: ' + self.message[0].lower()
279 subj = 'subject: ' + self.message[0].lower()
280 for i in xrange(len(self.comments)):
280 for i in xrange(len(self.comments)):
281 if subj == self.comments[i].lower():
281 if subj == self.comments[i].lower():
282 del self.comments[i]
282 del self.comments[i]
283 self.message = self.message[2:]
283 self.message = self.message[2:]
284 break
284 break
285 ci = 0
285 ci = 0
286 for mi in self.message:
286 for mi in self.message:
287 while mi != self.comments[ci]:
287 while mi != self.comments[ci]:
288 ci += 1
288 ci += 1
289 del self.comments[ci]
289 del self.comments[ci]
290
290
291 def newcommit(repo, phase, *args, **kwargs):
291 def newcommit(repo, phase, *args, **kwargs):
292 """helper dedicated to ensure a commit respect mq.secret setting
292 """helper dedicated to ensure a commit respect mq.secret setting
293
293
294 It should be used instead of repo.commit inside the mq source for operation
294 It should be used instead of repo.commit inside the mq source for operation
295 creating new changeset.
295 creating new changeset.
296 """
296 """
297 repo = repo.unfiltered()
297 repo = repo.unfiltered()
298 if phase is None:
298 if phase is None:
299 if repo.ui.configbool('mq', 'secret', False):
299 if repo.ui.configbool('mq', 'secret', False):
300 phase = phases.secret
300 phase = phases.secret
301 if phase is not None:
301 if phase is not None:
302 backup = repo.ui.backupconfig('phases', 'new-commit')
302 backup = repo.ui.backupconfig('phases', 'new-commit')
303 try:
303 try:
304 if phase is not None:
304 if phase is not None:
305 repo.ui.setconfig('phases', 'new-commit', phase, 'mq')
305 repo.ui.setconfig('phases', 'new-commit', phase, 'mq')
306 return repo.commit(*args, **kwargs)
306 return repo.commit(*args, **kwargs)
307 finally:
307 finally:
308 if phase is not None:
308 if phase is not None:
309 repo.ui.restoreconfig(backup)
309 repo.ui.restoreconfig(backup)
310
310
311 class AbortNoCleanup(error.Abort):
311 class AbortNoCleanup(error.Abort):
312 pass
312 pass
313
313
314 class queue(object):
314 class queue(object):
315 def __init__(self, ui, baseui, path, patchdir=None):
315 def __init__(self, ui, baseui, path, patchdir=None):
316 self.basepath = path
316 self.basepath = path
317 try:
317 try:
318 fh = open(os.path.join(path, 'patches.queue'))
318 fh = open(os.path.join(path, 'patches.queue'))
319 cur = fh.read().rstrip()
319 cur = fh.read().rstrip()
320 fh.close()
320 fh.close()
321 if not cur:
321 if not cur:
322 curpath = os.path.join(path, 'patches')
322 curpath = os.path.join(path, 'patches')
323 else:
323 else:
324 curpath = os.path.join(path, 'patches-' + cur)
324 curpath = os.path.join(path, 'patches-' + cur)
325 except IOError:
325 except IOError:
326 curpath = os.path.join(path, 'patches')
326 curpath = os.path.join(path, 'patches')
327 self.path = patchdir or curpath
327 self.path = patchdir or curpath
328 self.opener = scmutil.opener(self.path)
328 self.opener = scmutil.opener(self.path)
329 self.ui = ui
329 self.ui = ui
330 self.baseui = baseui
330 self.baseui = baseui
331 self.applieddirty = False
331 self.applieddirty = False
332 self.seriesdirty = False
332 self.seriesdirty = False
333 self.added = []
333 self.added = []
334 self.seriespath = "series"
334 self.seriespath = "series"
335 self.statuspath = "status"
335 self.statuspath = "status"
336 self.guardspath = "guards"
336 self.guardspath = "guards"
337 self.activeguards = None
337 self.activeguards = None
338 self.guardsdirty = False
338 self.guardsdirty = False
339 # Handle mq.git as a bool with extended values
339 # Handle mq.git as a bool with extended values
340 try:
340 try:
341 gitmode = ui.configbool('mq', 'git', None)
341 gitmode = ui.configbool('mq', 'git', None)
342 if gitmode is None:
342 if gitmode is None:
343 raise error.ConfigError
343 raise error.ConfigError
344 self.gitmode = gitmode and 'yes' or 'no'
344 self.gitmode = gitmode and 'yes' or 'no'
345 except error.ConfigError:
345 except error.ConfigError:
346 self.gitmode = ui.config('mq', 'git', 'auto').lower()
346 self.gitmode = ui.config('mq', 'git', 'auto').lower()
347 self.plainmode = ui.configbool('mq', 'plain', False)
347 self.plainmode = ui.configbool('mq', 'plain', False)
348 self.checkapplied = True
348 self.checkapplied = True
349
349
350 @util.propertycache
350 @util.propertycache
351 def applied(self):
351 def applied(self):
352 def parselines(lines):
352 def parselines(lines):
353 for l in lines:
353 for l in lines:
354 entry = l.split(':', 1)
354 entry = l.split(':', 1)
355 if len(entry) > 1:
355 if len(entry) > 1:
356 n, name = entry
356 n, name = entry
357 yield statusentry(bin(n), name)
357 yield statusentry(bin(n), name)
358 elif l.strip():
358 elif l.strip():
359 self.ui.warn(_('malformated mq status line: %s\n') % entry)
359 self.ui.warn(_('malformated mq status line: %s\n') % entry)
360 # else we ignore empty lines
360 # else we ignore empty lines
361 try:
361 try:
362 lines = self.opener.read(self.statuspath).splitlines()
362 lines = self.opener.read(self.statuspath).splitlines()
363 return list(parselines(lines))
363 return list(parselines(lines))
364 except IOError, e:
364 except IOError, e:
365 if e.errno == errno.ENOENT:
365 if e.errno == errno.ENOENT:
366 return []
366 return []
367 raise
367 raise
368
368
369 @util.propertycache
369 @util.propertycache
370 def fullseries(self):
370 def fullseries(self):
371 try:
371 try:
372 return self.opener.read(self.seriespath).splitlines()
372 return self.opener.read(self.seriespath).splitlines()
373 except IOError, e:
373 except IOError, e:
374 if e.errno == errno.ENOENT:
374 if e.errno == errno.ENOENT:
375 return []
375 return []
376 raise
376 raise
377
377
378 @util.propertycache
378 @util.propertycache
379 def series(self):
379 def series(self):
380 self.parseseries()
380 self.parseseries()
381 return self.series
381 return self.series
382
382
383 @util.propertycache
383 @util.propertycache
384 def seriesguards(self):
384 def seriesguards(self):
385 self.parseseries()
385 self.parseseries()
386 return self.seriesguards
386 return self.seriesguards
387
387
388 def invalidate(self):
388 def invalidate(self):
389 for a in 'applied fullseries series seriesguards'.split():
389 for a in 'applied fullseries series seriesguards'.split():
390 if a in self.__dict__:
390 if a in self.__dict__:
391 delattr(self, a)
391 delattr(self, a)
392 self.applieddirty = False
392 self.applieddirty = False
393 self.seriesdirty = False
393 self.seriesdirty = False
394 self.guardsdirty = False
394 self.guardsdirty = False
395 self.activeguards = None
395 self.activeguards = None
396
396
397 def diffopts(self, opts={}, patchfn=None):
397 def diffopts(self, opts={}, patchfn=None):
398 diffopts = patchmod.diffopts(self.ui, opts)
398 diffopts = patchmod.diffopts(self.ui, opts)
399 if self.gitmode == 'auto':
399 if self.gitmode == 'auto':
400 diffopts.upgrade = True
400 diffopts.upgrade = True
401 elif self.gitmode == 'keep':
401 elif self.gitmode == 'keep':
402 pass
402 pass
403 elif self.gitmode in ('yes', 'no'):
403 elif self.gitmode in ('yes', 'no'):
404 diffopts.git = self.gitmode == 'yes'
404 diffopts.git = self.gitmode == 'yes'
405 else:
405 else:
406 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
406 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
407 ' got %s') % self.gitmode)
407 ' got %s') % self.gitmode)
408 if patchfn:
408 if patchfn:
409 diffopts = self.patchopts(diffopts, patchfn)
409 diffopts = self.patchopts(diffopts, patchfn)
410 return diffopts
410 return diffopts
411
411
412 def patchopts(self, diffopts, *patches):
412 def patchopts(self, diffopts, *patches):
413 """Return a copy of input diff options with git set to true if
413 """Return a copy of input diff options with git set to true if
414 referenced patch is a git patch and should be preserved as such.
414 referenced patch is a git patch and should be preserved as such.
415 """
415 """
416 diffopts = diffopts.copy()
416 diffopts = diffopts.copy()
417 if not diffopts.git and self.gitmode == 'keep':
417 if not diffopts.git and self.gitmode == 'keep':
418 for patchfn in patches:
418 for patchfn in patches:
419 patchf = self.opener(patchfn, 'r')
419 patchf = self.opener(patchfn, 'r')
420 # if the patch was a git patch, refresh it as a git patch
420 # if the patch was a git patch, refresh it as a git patch
421 for line in patchf:
421 for line in patchf:
422 if line.startswith('diff --git'):
422 if line.startswith('diff --git'):
423 diffopts.git = True
423 diffopts.git = True
424 break
424 break
425 patchf.close()
425 patchf.close()
426 return diffopts
426 return diffopts
427
427
428 def join(self, *p):
428 def join(self, *p):
429 return os.path.join(self.path, *p)
429 return os.path.join(self.path, *p)
430
430
431 def findseries(self, patch):
431 def findseries(self, patch):
432 def matchpatch(l):
432 def matchpatch(l):
433 l = l.split('#', 1)[0]
433 l = l.split('#', 1)[0]
434 return l.strip() == patch
434 return l.strip() == patch
435 for index, l in enumerate(self.fullseries):
435 for index, l in enumerate(self.fullseries):
436 if matchpatch(l):
436 if matchpatch(l):
437 return index
437 return index
438 return None
438 return None
439
439
440 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
440 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
441
441
442 def parseseries(self):
442 def parseseries(self):
443 self.series = []
443 self.series = []
444 self.seriesguards = []
444 self.seriesguards = []
445 for l in self.fullseries:
445 for l in self.fullseries:
446 h = l.find('#')
446 h = l.find('#')
447 if h == -1:
447 if h == -1:
448 patch = l
448 patch = l
449 comment = ''
449 comment = ''
450 elif h == 0:
450 elif h == 0:
451 continue
451 continue
452 else:
452 else:
453 patch = l[:h]
453 patch = l[:h]
454 comment = l[h:]
454 comment = l[h:]
455 patch = patch.strip()
455 patch = patch.strip()
456 if patch:
456 if patch:
457 if patch in self.series:
457 if patch in self.series:
458 raise util.Abort(_('%s appears more than once in %s') %
458 raise util.Abort(_('%s appears more than once in %s') %
459 (patch, self.join(self.seriespath)))
459 (patch, self.join(self.seriespath)))
460 self.series.append(patch)
460 self.series.append(patch)
461 self.seriesguards.append(self.guard_re.findall(comment))
461 self.seriesguards.append(self.guard_re.findall(comment))
462
462
463 def checkguard(self, guard):
463 def checkguard(self, guard):
464 if not guard:
464 if not guard:
465 return _('guard cannot be an empty string')
465 return _('guard cannot be an empty string')
466 bad_chars = '# \t\r\n\f'
466 bad_chars = '# \t\r\n\f'
467 first = guard[0]
467 first = guard[0]
468 if first in '-+':
468 if first in '-+':
469 return (_('guard %r starts with invalid character: %r') %
469 return (_('guard %r starts with invalid character: %r') %
470 (guard, first))
470 (guard, first))
471 for c in bad_chars:
471 for c in bad_chars:
472 if c in guard:
472 if c in guard:
473 return _('invalid character in guard %r: %r') % (guard, c)
473 return _('invalid character in guard %r: %r') % (guard, c)
474
474
475 def setactive(self, guards):
475 def setactive(self, guards):
476 for guard in guards:
476 for guard in guards:
477 bad = self.checkguard(guard)
477 bad = self.checkguard(guard)
478 if bad:
478 if bad:
479 raise util.Abort(bad)
479 raise util.Abort(bad)
480 guards = sorted(set(guards))
480 guards = sorted(set(guards))
481 self.ui.debug('active guards: %s\n' % ' '.join(guards))
481 self.ui.debug('active guards: %s\n' % ' '.join(guards))
482 self.activeguards = guards
482 self.activeguards = guards
483 self.guardsdirty = True
483 self.guardsdirty = True
484
484
485 def active(self):
485 def active(self):
486 if self.activeguards is None:
486 if self.activeguards is None:
487 self.activeguards = []
487 self.activeguards = []
488 try:
488 try:
489 guards = self.opener.read(self.guardspath).split()
489 guards = self.opener.read(self.guardspath).split()
490 except IOError, err:
490 except IOError, err:
491 if err.errno != errno.ENOENT:
491 if err.errno != errno.ENOENT:
492 raise
492 raise
493 guards = []
493 guards = []
494 for i, guard in enumerate(guards):
494 for i, guard in enumerate(guards):
495 bad = self.checkguard(guard)
495 bad = self.checkguard(guard)
496 if bad:
496 if bad:
497 self.ui.warn('%s:%d: %s\n' %
497 self.ui.warn('%s:%d: %s\n' %
498 (self.join(self.guardspath), i + 1, bad))
498 (self.join(self.guardspath), i + 1, bad))
499 else:
499 else:
500 self.activeguards.append(guard)
500 self.activeguards.append(guard)
501 return self.activeguards
501 return self.activeguards
502
502
503 def setguards(self, idx, guards):
503 def setguards(self, idx, guards):
504 for g in guards:
504 for g in guards:
505 if len(g) < 2:
505 if len(g) < 2:
506 raise util.Abort(_('guard %r too short') % g)
506 raise util.Abort(_('guard %r too short') % g)
507 if g[0] not in '-+':
507 if g[0] not in '-+':
508 raise util.Abort(_('guard %r starts with invalid char') % g)
508 raise util.Abort(_('guard %r starts with invalid char') % g)
509 bad = self.checkguard(g[1:])
509 bad = self.checkguard(g[1:])
510 if bad:
510 if bad:
511 raise util.Abort(bad)
511 raise util.Abort(bad)
512 drop = self.guard_re.sub('', self.fullseries[idx])
512 drop = self.guard_re.sub('', self.fullseries[idx])
513 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
513 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
514 self.parseseries()
514 self.parseseries()
515 self.seriesdirty = True
515 self.seriesdirty = True
516
516
517 def pushable(self, idx):
517 def pushable(self, idx):
518 if isinstance(idx, str):
518 if isinstance(idx, str):
519 idx = self.series.index(idx)
519 idx = self.series.index(idx)
520 patchguards = self.seriesguards[idx]
520 patchguards = self.seriesguards[idx]
521 if not patchguards:
521 if not patchguards:
522 return True, None
522 return True, None
523 guards = self.active()
523 guards = self.active()
524 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
524 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
525 if exactneg:
525 if exactneg:
526 return False, repr(exactneg[0])
526 return False, repr(exactneg[0])
527 pos = [g for g in patchguards if g[0] == '+']
527 pos = [g for g in patchguards if g[0] == '+']
528 exactpos = [g for g in pos if g[1:] in guards]
528 exactpos = [g for g in pos if g[1:] in guards]
529 if pos:
529 if pos:
530 if exactpos:
530 if exactpos:
531 return True, repr(exactpos[0])
531 return True, repr(exactpos[0])
532 return False, ' '.join(map(repr, pos))
532 return False, ' '.join(map(repr, pos))
533 return True, ''
533 return True, ''
534
534
535 def explainpushable(self, idx, all_patches=False):
535 def explainpushable(self, idx, all_patches=False):
536 write = all_patches and self.ui.write or self.ui.warn
536 write = all_patches and self.ui.write or self.ui.warn
537 if all_patches or self.ui.verbose:
537 if all_patches or self.ui.verbose:
538 if isinstance(idx, str):
538 if isinstance(idx, str):
539 idx = self.series.index(idx)
539 idx = self.series.index(idx)
540 pushable, why = self.pushable(idx)
540 pushable, why = self.pushable(idx)
541 if all_patches and pushable:
541 if all_patches and pushable:
542 if why is None:
542 if why is None:
543 write(_('allowing %s - no guards in effect\n') %
543 write(_('allowing %s - no guards in effect\n') %
544 self.series[idx])
544 self.series[idx])
545 else:
545 else:
546 if not why:
546 if not why:
547 write(_('allowing %s - no matching negative guards\n') %
547 write(_('allowing %s - no matching negative guards\n') %
548 self.series[idx])
548 self.series[idx])
549 else:
549 else:
550 write(_('allowing %s - guarded by %s\n') %
550 write(_('allowing %s - guarded by %s\n') %
551 (self.series[idx], why))
551 (self.series[idx], why))
552 if not pushable:
552 if not pushable:
553 if why:
553 if why:
554 write(_('skipping %s - guarded by %s\n') %
554 write(_('skipping %s - guarded by %s\n') %
555 (self.series[idx], why))
555 (self.series[idx], why))
556 else:
556 else:
557 write(_('skipping %s - no matching guards\n') %
557 write(_('skipping %s - no matching guards\n') %
558 self.series[idx])
558 self.series[idx])
559
559
560 def savedirty(self):
560 def savedirty(self):
561 def writelist(items, path):
561 def writelist(items, path):
562 fp = self.opener(path, 'w')
562 fp = self.opener(path, 'w')
563 for i in items:
563 for i in items:
564 fp.write("%s\n" % i)
564 fp.write("%s\n" % i)
565 fp.close()
565 fp.close()
566 if self.applieddirty:
566 if self.applieddirty:
567 writelist(map(str, self.applied), self.statuspath)
567 writelist(map(str, self.applied), self.statuspath)
568 self.applieddirty = False
568 self.applieddirty = False
569 if self.seriesdirty:
569 if self.seriesdirty:
570 writelist(self.fullseries, self.seriespath)
570 writelist(self.fullseries, self.seriespath)
571 self.seriesdirty = False
571 self.seriesdirty = False
572 if self.guardsdirty:
572 if self.guardsdirty:
573 writelist(self.activeguards, self.guardspath)
573 writelist(self.activeguards, self.guardspath)
574 self.guardsdirty = False
574 self.guardsdirty = False
575 if self.added:
575 if self.added:
576 qrepo = self.qrepo()
576 qrepo = self.qrepo()
577 if qrepo:
577 if qrepo:
578 qrepo[None].add(f for f in self.added if f not in qrepo[None])
578 qrepo[None].add(f for f in self.added if f not in qrepo[None])
579 self.added = []
579 self.added = []
580
580
581 def removeundo(self, repo):
581 def removeundo(self, repo):
582 undo = repo.sjoin('undo')
582 undo = repo.sjoin('undo')
583 if not os.path.exists(undo):
583 if not os.path.exists(undo):
584 return
584 return
585 try:
585 try:
586 os.unlink(undo)
586 os.unlink(undo)
587 except OSError, inst:
587 except OSError, inst:
588 self.ui.warn(_('error removing undo: %s\n') % str(inst))
588 self.ui.warn(_('error removing undo: %s\n') % str(inst))
589
589
590 def backup(self, repo, files, copy=False):
590 def backup(self, repo, files, copy=False):
591 # backup local changes in --force case
591 # backup local changes in --force case
592 for f in sorted(files):
592 for f in sorted(files):
593 absf = repo.wjoin(f)
593 absf = repo.wjoin(f)
594 if os.path.lexists(absf):
594 if os.path.lexists(absf):
595 self.ui.note(_('saving current version of %s as %s\n') %
595 self.ui.note(_('saving current version of %s as %s\n') %
596 (f, f + '.orig'))
596 (f, f + '.orig'))
597 if copy:
597 if copy:
598 util.copyfile(absf, absf + '.orig')
598 util.copyfile(absf, absf + '.orig')
599 else:
599 else:
600 util.rename(absf, absf + '.orig')
600 util.rename(absf, absf + '.orig')
601
601
602 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
602 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
603 fp=None, changes=None, opts={}):
603 fp=None, changes=None, opts={}):
604 stat = opts.get('stat')
604 stat = opts.get('stat')
605 m = scmutil.match(repo[node1], files, opts)
605 m = scmutil.match(repo[node1], files, opts)
606 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
606 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
607 changes, stat, fp)
607 changes, stat, fp)
608
608
609 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
609 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
610 # first try just applying the patch
610 # first try just applying the patch
611 (err, n) = self.apply(repo, [patch], update_status=False,
611 (err, n) = self.apply(repo, [patch], update_status=False,
612 strict=True, merge=rev)
612 strict=True, merge=rev)
613
613
614 if err == 0:
614 if err == 0:
615 return (err, n)
615 return (err, n)
616
616
617 if n is None:
617 if n is None:
618 raise util.Abort(_("apply failed for patch %s") % patch)
618 raise util.Abort(_("apply failed for patch %s") % patch)
619
619
620 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
620 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
621
621
622 # apply failed, strip away that rev and merge.
622 # apply failed, strip away that rev and merge.
623 hg.clean(repo, head)
623 hg.clean(repo, head)
624 strip(self.ui, repo, [n], update=False, backup='strip')
624 strip(self.ui, repo, [n], update=False, backup='strip')
625
625
626 ctx = repo[rev]
626 ctx = repo[rev]
627 ret = hg.merge(repo, rev)
627 ret = hg.merge(repo, rev)
628 if ret:
628 if ret:
629 raise util.Abort(_("update returned %d") % ret)
629 raise util.Abort(_("update returned %d") % ret)
630 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
630 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
631 if n is None:
631 if n is None:
632 raise util.Abort(_("repo commit failed"))
632 raise util.Abort(_("repo commit failed"))
633 try:
633 try:
634 ph = patchheader(mergeq.join(patch), self.plainmode)
634 ph = patchheader(mergeq.join(patch), self.plainmode)
635 except Exception:
635 except Exception:
636 raise util.Abort(_("unable to read %s") % patch)
636 raise util.Abort(_("unable to read %s") % patch)
637
637
638 diffopts = self.patchopts(diffopts, patch)
638 diffopts = self.patchopts(diffopts, patch)
639 patchf = self.opener(patch, "w")
639 patchf = self.opener(patch, "w")
640 comments = str(ph)
640 comments = str(ph)
641 if comments:
641 if comments:
642 patchf.write(comments)
642 patchf.write(comments)
643 self.printdiff(repo, diffopts, head, n, fp=patchf)
643 self.printdiff(repo, diffopts, head, n, fp=patchf)
644 patchf.close()
644 patchf.close()
645 self.removeundo(repo)
645 self.removeundo(repo)
646 return (0, n)
646 return (0, n)
647
647
648 def qparents(self, repo, rev=None):
648 def qparents(self, repo, rev=None):
649 """return the mq handled parent or p1
649 """return the mq handled parent or p1
650
650
651 In some case where mq get himself in being the parent of a merge the
651 In some case where mq get himself in being the parent of a merge the
652 appropriate parent may be p2.
652 appropriate parent may be p2.
653 (eg: an in progress merge started with mq disabled)
653 (eg: an in progress merge started with mq disabled)
654
654
655 If no parent are managed by mq, p1 is returned.
655 If no parent are managed by mq, p1 is returned.
656 """
656 """
657 if rev is None:
657 if rev is None:
658 (p1, p2) = repo.dirstate.parents()
658 (p1, p2) = repo.dirstate.parents()
659 if p2 == nullid:
659 if p2 == nullid:
660 return p1
660 return p1
661 if not self.applied:
661 if not self.applied:
662 return None
662 return None
663 return self.applied[-1].node
663 return self.applied[-1].node
664 p1, p2 = repo.changelog.parents(rev)
664 p1, p2 = repo.changelog.parents(rev)
665 if p2 != nullid and p2 in [x.node for x in self.applied]:
665 if p2 != nullid and p2 in [x.node for x in self.applied]:
666 return p2
666 return p2
667 return p1
667 return p1
668
668
669 def mergepatch(self, repo, mergeq, series, diffopts):
669 def mergepatch(self, repo, mergeq, series, diffopts):
670 if not self.applied:
670 if not self.applied:
671 # each of the patches merged in will have two parents. This
671 # each of the patches merged in will have two parents. This
672 # can confuse the qrefresh, qdiff, and strip code because it
672 # can confuse the qrefresh, qdiff, and strip code because it
673 # needs to know which parent is actually in the patch queue.
673 # needs to know which parent is actually in the patch queue.
674 # so, we insert a merge marker with only one parent. This way
674 # so, we insert a merge marker with only one parent. This way
675 # the first patch in the queue is never a merge patch
675 # the first patch in the queue is never a merge patch
676 #
676 #
677 pname = ".hg.patches.merge.marker"
677 pname = ".hg.patches.merge.marker"
678 n = newcommit(repo, None, '[mq]: merge marker', force=True)
678 n = newcommit(repo, None, '[mq]: merge marker', force=True)
679 self.removeundo(repo)
679 self.removeundo(repo)
680 self.applied.append(statusentry(n, pname))
680 self.applied.append(statusentry(n, pname))
681 self.applieddirty = True
681 self.applieddirty = True
682
682
683 head = self.qparents(repo)
683 head = self.qparents(repo)
684
684
685 for patch in series:
685 for patch in series:
686 patch = mergeq.lookup(patch, strict=True)
686 patch = mergeq.lookup(patch, strict=True)
687 if not patch:
687 if not patch:
688 self.ui.warn(_("patch %s does not exist\n") % patch)
688 self.ui.warn(_("patch %s does not exist\n") % patch)
689 return (1, None)
689 return (1, None)
690 pushable, reason = self.pushable(patch)
690 pushable, reason = self.pushable(patch)
691 if not pushable:
691 if not pushable:
692 self.explainpushable(patch, all_patches=True)
692 self.explainpushable(patch, all_patches=True)
693 continue
693 continue
694 info = mergeq.isapplied(patch)
694 info = mergeq.isapplied(patch)
695 if not info:
695 if not info:
696 self.ui.warn(_("patch %s is not applied\n") % patch)
696 self.ui.warn(_("patch %s is not applied\n") % patch)
697 return (1, None)
697 return (1, None)
698 rev = info[1]
698 rev = info[1]
699 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
699 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
700 if head:
700 if head:
701 self.applied.append(statusentry(head, patch))
701 self.applied.append(statusentry(head, patch))
702 self.applieddirty = True
702 self.applieddirty = True
703 if err:
703 if err:
704 return (err, head)
704 return (err, head)
705 self.savedirty()
705 self.savedirty()
706 return (0, head)
706 return (0, head)
707
707
708 def patch(self, repo, patchfile):
708 def patch(self, repo, patchfile):
709 '''Apply patchfile to the working directory.
709 '''Apply patchfile to the working directory.
710 patchfile: name of patch file'''
710 patchfile: name of patch file'''
711 files = set()
711 files = set()
712 try:
712 try:
713 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
713 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
714 files=files, eolmode=None)
714 files=files, eolmode=None)
715 return (True, list(files), fuzz)
715 return (True, list(files), fuzz)
716 except Exception, inst:
716 except Exception, inst:
717 self.ui.note(str(inst) + '\n')
717 self.ui.note(str(inst) + '\n')
718 if not self.ui.verbose:
718 if not self.ui.verbose:
719 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
719 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
720 self.ui.traceback()
720 self.ui.traceback()
721 return (False, list(files), False)
721 return (False, list(files), False)
722
722
723 def apply(self, repo, series, list=False, update_status=True,
723 def apply(self, repo, series, list=False, update_status=True,
724 strict=False, patchdir=None, merge=None, all_files=None,
724 strict=False, patchdir=None, merge=None, all_files=None,
725 tobackup=None, keepchanges=False):
725 tobackup=None, keepchanges=False):
726 wlock = lock = tr = None
726 wlock = lock = tr = None
727 try:
727 try:
728 wlock = repo.wlock()
728 wlock = repo.wlock()
729 lock = repo.lock()
729 lock = repo.lock()
730 tr = repo.transaction("qpush")
730 tr = repo.transaction("qpush")
731 try:
731 try:
732 ret = self._apply(repo, series, list, update_status,
732 ret = self._apply(repo, series, list, update_status,
733 strict, patchdir, merge, all_files=all_files,
733 strict, patchdir, merge, all_files=all_files,
734 tobackup=tobackup, keepchanges=keepchanges)
734 tobackup=tobackup, keepchanges=keepchanges)
735 tr.close()
735 tr.close()
736 self.savedirty()
736 self.savedirty()
737 return ret
737 return ret
738 except AbortNoCleanup:
738 except AbortNoCleanup:
739 tr.close()
739 tr.close()
740 self.savedirty()
740 self.savedirty()
741 return 2, repo.dirstate.p1()
741 return 2, repo.dirstate.p1()
742 except: # re-raises
742 except: # re-raises
743 try:
743 try:
744 tr.abort()
744 tr.abort()
745 finally:
745 finally:
746 repo.invalidate()
746 repo.invalidate()
747 repo.dirstate.invalidate()
747 repo.dirstate.invalidate()
748 self.invalidate()
748 self.invalidate()
749 raise
749 raise
750 finally:
750 finally:
751 release(tr, lock, wlock)
751 release(tr, lock, wlock)
752 self.removeundo(repo)
752 self.removeundo(repo)
753
753
754 def _apply(self, repo, series, list=False, update_status=True,
754 def _apply(self, repo, series, list=False, update_status=True,
755 strict=False, patchdir=None, merge=None, all_files=None,
755 strict=False, patchdir=None, merge=None, all_files=None,
756 tobackup=None, keepchanges=False):
756 tobackup=None, keepchanges=False):
757 """returns (error, hash)
757 """returns (error, hash)
758
758
759 error = 1 for unable to read, 2 for patch failed, 3 for patch
759 error = 1 for unable to read, 2 for patch failed, 3 for patch
760 fuzz. tobackup is None or a set of files to backup before they
760 fuzz. tobackup is None or a set of files to backup before they
761 are modified by a patch.
761 are modified by a patch.
762 """
762 """
763 # TODO unify with commands.py
763 # TODO unify with commands.py
764 if not patchdir:
764 if not patchdir:
765 patchdir = self.path
765 patchdir = self.path
766 err = 0
766 err = 0
767 n = None
767 n = None
768 for patchname in series:
768 for patchname in series:
769 pushable, reason = self.pushable(patchname)
769 pushable, reason = self.pushable(patchname)
770 if not pushable:
770 if not pushable:
771 self.explainpushable(patchname, all_patches=True)
771 self.explainpushable(patchname, all_patches=True)
772 continue
772 continue
773 self.ui.status(_("applying %s\n") % patchname)
773 self.ui.status(_("applying %s\n") % patchname)
774 pf = os.path.join(patchdir, patchname)
774 pf = os.path.join(patchdir, patchname)
775
775
776 try:
776 try:
777 ph = patchheader(self.join(patchname), self.plainmode)
777 ph = patchheader(self.join(patchname), self.plainmode)
778 except IOError:
778 except IOError:
779 self.ui.warn(_("unable to read %s\n") % patchname)
779 self.ui.warn(_("unable to read %s\n") % patchname)
780 err = 1
780 err = 1
781 break
781 break
782
782
783 message = ph.message
783 message = ph.message
784 if not message:
784 if not message:
785 # The commit message should not be translated
785 # The commit message should not be translated
786 message = "imported patch %s\n" % patchname
786 message = "imported patch %s\n" % patchname
787 else:
787 else:
788 if list:
788 if list:
789 # The commit message should not be translated
789 # The commit message should not be translated
790 message.append("\nimported patch %s" % patchname)
790 message.append("\nimported patch %s" % patchname)
791 message = '\n'.join(message)
791 message = '\n'.join(message)
792
792
793 if ph.haspatch:
793 if ph.haspatch:
794 if tobackup:
794 if tobackup:
795 touched = patchmod.changedfiles(self.ui, repo, pf)
795 touched = patchmod.changedfiles(self.ui, repo, pf)
796 touched = set(touched) & tobackup
796 touched = set(touched) & tobackup
797 if touched and keepchanges:
797 if touched and keepchanges:
798 raise AbortNoCleanup(
798 raise AbortNoCleanup(
799 _("local changes found, refresh first"))
799 _("local changes found, refresh first"))
800 self.backup(repo, touched, copy=True)
800 self.backup(repo, touched, copy=True)
801 tobackup = tobackup - touched
801 tobackup = tobackup - touched
802 (patcherr, files, fuzz) = self.patch(repo, pf)
802 (patcherr, files, fuzz) = self.patch(repo, pf)
803 if all_files is not None:
803 if all_files is not None:
804 all_files.update(files)
804 all_files.update(files)
805 patcherr = not patcherr
805 patcherr = not patcherr
806 else:
806 else:
807 self.ui.warn(_("patch %s is empty\n") % patchname)
807 self.ui.warn(_("patch %s is empty\n") % patchname)
808 patcherr, files, fuzz = 0, [], 0
808 patcherr, files, fuzz = 0, [], 0
809
809
810 if merge and files:
810 if merge and files:
811 # Mark as removed/merged and update dirstate parent info
811 # Mark as removed/merged and update dirstate parent info
812 removed = []
812 removed = []
813 merged = []
813 merged = []
814 for f in files:
814 for f in files:
815 if os.path.lexists(repo.wjoin(f)):
815 if os.path.lexists(repo.wjoin(f)):
816 merged.append(f)
816 merged.append(f)
817 else:
817 else:
818 removed.append(f)
818 removed.append(f)
819 for f in removed:
819 for f in removed:
820 repo.dirstate.remove(f)
820 repo.dirstate.remove(f)
821 for f in merged:
821 for f in merged:
822 repo.dirstate.merge(f)
822 repo.dirstate.merge(f)
823 p1, p2 = repo.dirstate.parents()
823 p1, p2 = repo.dirstate.parents()
824 repo.setparents(p1, merge)
824 repo.setparents(p1, merge)
825
825
826 if all_files and '.hgsubstate' in all_files:
826 if all_files and '.hgsubstate' in all_files:
827 wctx = repo[None]
827 wctx = repo[None]
828 pctx = repo['.']
828 pctx = repo['.']
829 overwrite = False
829 overwrite = False
830 mergedsubstate = subrepo.submerge(repo, pctx, wctx, wctx,
830 mergedsubstate = subrepo.submerge(repo, pctx, wctx, wctx,
831 overwrite)
831 overwrite)
832 files += mergedsubstate.keys()
832 files += mergedsubstate.keys()
833
833
834 match = scmutil.matchfiles(repo, files or [])
834 match = scmutil.matchfiles(repo, files or [])
835 oldtip = repo['tip']
835 oldtip = repo['tip']
836 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
836 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
837 force=True)
837 force=True)
838 if repo['tip'] == oldtip:
838 if repo['tip'] == oldtip:
839 raise util.Abort(_("qpush exactly duplicates child changeset"))
839 raise util.Abort(_("qpush exactly duplicates child changeset"))
840 if n is None:
840 if n is None:
841 raise util.Abort(_("repository commit failed"))
841 raise util.Abort(_("repository commit failed"))
842
842
843 if update_status:
843 if update_status:
844 self.applied.append(statusentry(n, patchname))
844 self.applied.append(statusentry(n, patchname))
845
845
846 if patcherr:
846 if patcherr:
847 self.ui.warn(_("patch failed, rejects left in working dir\n"))
847 self.ui.warn(_("patch failed, rejects left in working dir\n"))
848 err = 2
848 err = 2
849 break
849 break
850
850
851 if fuzz and strict:
851 if fuzz and strict:
852 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
852 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
853 err = 3
853 err = 3
854 break
854 break
855 return (err, n)
855 return (err, n)
856
856
857 def _cleanup(self, patches, numrevs, keep=False):
857 def _cleanup(self, patches, numrevs, keep=False):
858 if not keep:
858 if not keep:
859 r = self.qrepo()
859 r = self.qrepo()
860 if r:
860 if r:
861 r[None].forget(patches)
861 r[None].forget(patches)
862 for p in patches:
862 for p in patches:
863 try:
863 try:
864 os.unlink(self.join(p))
864 os.unlink(self.join(p))
865 except OSError, inst:
865 except OSError, inst:
866 if inst.errno != errno.ENOENT:
866 if inst.errno != errno.ENOENT:
867 raise
867 raise
868
868
869 qfinished = []
869 qfinished = []
870 if numrevs:
870 if numrevs:
871 qfinished = self.applied[:numrevs]
871 qfinished = self.applied[:numrevs]
872 del self.applied[:numrevs]
872 del self.applied[:numrevs]
873 self.applieddirty = True
873 self.applieddirty = True
874
874
875 unknown = []
875 unknown = []
876
876
877 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
877 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
878 reverse=True):
878 reverse=True):
879 if i is not None:
879 if i is not None:
880 del self.fullseries[i]
880 del self.fullseries[i]
881 else:
881 else:
882 unknown.append(p)
882 unknown.append(p)
883
883
884 if unknown:
884 if unknown:
885 if numrevs:
885 if numrevs:
886 rev = dict((entry.name, entry.node) for entry in qfinished)
886 rev = dict((entry.name, entry.node) for entry in qfinished)
887 for p in unknown:
887 for p in unknown:
888 msg = _('revision %s refers to unknown patches: %s\n')
888 msg = _('revision %s refers to unknown patches: %s\n')
889 self.ui.warn(msg % (short(rev[p]), p))
889 self.ui.warn(msg % (short(rev[p]), p))
890 else:
890 else:
891 msg = _('unknown patches: %s\n')
891 msg = _('unknown patches: %s\n')
892 raise util.Abort(''.join(msg % p for p in unknown))
892 raise util.Abort(''.join(msg % p for p in unknown))
893
893
894 self.parseseries()
894 self.parseseries()
895 self.seriesdirty = True
895 self.seriesdirty = True
896 return [entry.node for entry in qfinished]
896 return [entry.node for entry in qfinished]
897
897
898 def _revpatches(self, repo, revs):
898 def _revpatches(self, repo, revs):
899 firstrev = repo[self.applied[0].node].rev()
899 firstrev = repo[self.applied[0].node].rev()
900 patches = []
900 patches = []
901 for i, rev in enumerate(revs):
901 for i, rev in enumerate(revs):
902
902
903 if rev < firstrev:
903 if rev < firstrev:
904 raise util.Abort(_('revision %d is not managed') % rev)
904 raise util.Abort(_('revision %d is not managed') % rev)
905
905
906 ctx = repo[rev]
906 ctx = repo[rev]
907 base = self.applied[i].node
907 base = self.applied[i].node
908 if ctx.node() != base:
908 if ctx.node() != base:
909 msg = _('cannot delete revision %d above applied patches')
909 msg = _('cannot delete revision %d above applied patches')
910 raise util.Abort(msg % rev)
910 raise util.Abort(msg % rev)
911
911
912 patch = self.applied[i].name
912 patch = self.applied[i].name
913 for fmt in ('[mq]: %s', 'imported patch %s'):
913 for fmt in ('[mq]: %s', 'imported patch %s'):
914 if ctx.description() == fmt % patch:
914 if ctx.description() == fmt % patch:
915 msg = _('patch %s finalized without changeset message\n')
915 msg = _('patch %s finalized without changeset message\n')
916 repo.ui.status(msg % patch)
916 repo.ui.status(msg % patch)
917 break
917 break
918
918
919 patches.append(patch)
919 patches.append(patch)
920 return patches
920 return patches
921
921
922 def finish(self, repo, revs):
922 def finish(self, repo, revs):
923 # Manually trigger phase computation to ensure phasedefaults is
923 # Manually trigger phase computation to ensure phasedefaults is
924 # executed before we remove the patches.
924 # executed before we remove the patches.
925 repo._phasecache
925 repo._phasecache
926 patches = self._revpatches(repo, sorted(revs))
926 patches = self._revpatches(repo, sorted(revs))
927 qfinished = self._cleanup(patches, len(patches))
927 qfinished = self._cleanup(patches, len(patches))
928 if qfinished and repo.ui.configbool('mq', 'secret', False):
928 if qfinished and repo.ui.configbool('mq', 'secret', False):
929 # only use this logic when the secret option is added
929 # only use this logic when the secret option is added
930 oldqbase = repo[qfinished[0]]
930 oldqbase = repo[qfinished[0]]
931 tphase = repo.ui.config('phases', 'new-commit', phases.draft)
931 tphase = repo.ui.config('phases', 'new-commit', phases.draft)
932 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
932 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
933 phases.advanceboundary(repo, tphase, qfinished)
933 phases.advanceboundary(repo, tphase, qfinished)
934
934
935 def delete(self, repo, patches, opts):
935 def delete(self, repo, patches, opts):
936 if not patches and not opts.get('rev'):
936 if not patches and not opts.get('rev'):
937 raise util.Abort(_('qdelete requires at least one revision or '
937 raise util.Abort(_('qdelete requires at least one revision or '
938 'patch name'))
938 'patch name'))
939
939
940 realpatches = []
940 realpatches = []
941 for patch in patches:
941 for patch in patches:
942 patch = self.lookup(patch, strict=True)
942 patch = self.lookup(patch, strict=True)
943 info = self.isapplied(patch)
943 info = self.isapplied(patch)
944 if info:
944 if info:
945 raise util.Abort(_("cannot delete applied patch %s") % patch)
945 raise util.Abort(_("cannot delete applied patch %s") % patch)
946 if patch not in self.series:
946 if patch not in self.series:
947 raise util.Abort(_("patch %s not in series file") % patch)
947 raise util.Abort(_("patch %s not in series file") % patch)
948 if patch not in realpatches:
948 if patch not in realpatches:
949 realpatches.append(patch)
949 realpatches.append(patch)
950
950
951 numrevs = 0
951 numrevs = 0
952 if opts.get('rev'):
952 if opts.get('rev'):
953 if not self.applied:
953 if not self.applied:
954 raise util.Abort(_('no patches applied'))
954 raise util.Abort(_('no patches applied'))
955 revs = scmutil.revrange(repo, opts.get('rev'))
955 revs = scmutil.revrange(repo, opts.get('rev'))
956 if len(revs) > 1 and revs[0] > revs[1]:
956 if len(revs) > 1 and revs[0] > revs[1]:
957 revs.reverse()
957 revs.reverse()
958 revpatches = self._revpatches(repo, revs)
958 revpatches = self._revpatches(repo, revs)
959 realpatches += revpatches
959 realpatches += revpatches
960 numrevs = len(revpatches)
960 numrevs = len(revpatches)
961
961
962 self._cleanup(realpatches, numrevs, opts.get('keep'))
962 self._cleanup(realpatches, numrevs, opts.get('keep'))
963
963
964 def checktoppatch(self, repo):
964 def checktoppatch(self, repo):
965 '''check that working directory is at qtip'''
965 '''check that working directory is at qtip'''
966 if self.applied:
966 if self.applied:
967 top = self.applied[-1].node
967 top = self.applied[-1].node
968 patch = self.applied[-1].name
968 patch = self.applied[-1].name
969 if repo.dirstate.p1() != top:
969 if repo.dirstate.p1() != top:
970 raise util.Abort(_("working directory revision is not qtip"))
970 raise util.Abort(_("working directory revision is not qtip"))
971 return top, patch
971 return top, patch
972 return None, None
972 return None, None
973
973
974 def putsubstate2changes(self, substatestate, changes):
974 def putsubstate2changes(self, substatestate, changes):
975 for files in changes[:3]:
975 for files in changes[:3]:
976 if '.hgsubstate' in files:
976 if '.hgsubstate' in files:
977 return # already listed up
977 return # already listed up
978 # not yet listed up
978 # not yet listed up
979 if substatestate in 'a?':
979 if substatestate in 'a?':
980 changes[1].append('.hgsubstate')
980 changes[1].append('.hgsubstate')
981 elif substatestate in 'r':
981 elif substatestate in 'r':
982 changes[2].append('.hgsubstate')
982 changes[2].append('.hgsubstate')
983 else: # modified
983 else: # modified
984 changes[0].append('.hgsubstate')
984 changes[0].append('.hgsubstate')
985
985
986 def checklocalchanges(self, repo, force=False, refresh=True):
986 def checklocalchanges(self, repo, force=False, refresh=True):
987 excsuffix = ''
987 excsuffix = ''
988 if refresh:
988 if refresh:
989 excsuffix = ', refresh first'
989 excsuffix = ', refresh first'
990 # plain versions for i18n tool to detect them
990 # plain versions for i18n tool to detect them
991 _("local changes found, refresh first")
991 _("local changes found, refresh first")
992 _("local changed subrepos found, refresh first")
992 _("local changed subrepos found, refresh first")
993 return checklocalchanges(repo, force, excsuffix)
993 return checklocalchanges(repo, force, excsuffix)
994
994
995 _reserved = ('series', 'status', 'guards', '.', '..')
995 _reserved = ('series', 'status', 'guards', '.', '..')
996 def checkreservedname(self, name):
996 def checkreservedname(self, name):
997 if name in self._reserved:
997 if name in self._reserved:
998 raise util.Abort(_('"%s" cannot be used as the name of a patch')
998 raise util.Abort(_('"%s" cannot be used as the name of a patch')
999 % name)
999 % name)
1000 for prefix in ('.hg', '.mq'):
1000 for prefix in ('.hg', '.mq'):
1001 if name.startswith(prefix):
1001 if name.startswith(prefix):
1002 raise util.Abort(_('patch name cannot begin with "%s"')
1002 raise util.Abort(_('patch name cannot begin with "%s"')
1003 % prefix)
1003 % prefix)
1004 for c in ('#', ':'):
1004 for c in ('#', ':'):
1005 if c in name:
1005 if c in name:
1006 raise util.Abort(_('"%s" cannot be used in the name of a patch')
1006 raise util.Abort(_('"%s" cannot be used in the name of a patch')
1007 % c)
1007 % c)
1008
1008
1009 def checkpatchname(self, name, force=False):
1009 def checkpatchname(self, name, force=False):
1010 self.checkreservedname(name)
1010 self.checkreservedname(name)
1011 if not force and os.path.exists(self.join(name)):
1011 if not force and os.path.exists(self.join(name)):
1012 if os.path.isdir(self.join(name)):
1012 if os.path.isdir(self.join(name)):
1013 raise util.Abort(_('"%s" already exists as a directory')
1013 raise util.Abort(_('"%s" already exists as a directory')
1014 % name)
1014 % name)
1015 else:
1015 else:
1016 raise util.Abort(_('patch "%s" already exists') % name)
1016 raise util.Abort(_('patch "%s" already exists') % name)
1017
1017
1018 def checkkeepchanges(self, keepchanges, force):
1018 def checkkeepchanges(self, keepchanges, force):
1019 if force and keepchanges:
1019 if force and keepchanges:
1020 raise util.Abort(_('cannot use both --force and --keep-changes'))
1020 raise util.Abort(_('cannot use both --force and --keep-changes'))
1021
1021
1022 def new(self, repo, patchfn, *pats, **opts):
1022 def new(self, repo, patchfn, *pats, **opts):
1023 """options:
1023 """options:
1024 msg: a string or a no-argument function returning a string
1024 msg: a string or a no-argument function returning a string
1025 """
1025 """
1026 msg = opts.get('msg')
1026 msg = opts.get('msg')
1027 edit = opts.get('edit')
1027 edit = opts.get('edit')
1028 user = opts.get('user')
1028 user = opts.get('user')
1029 date = opts.get('date')
1029 date = opts.get('date')
1030 if date:
1030 if date:
1031 date = util.parsedate(date)
1031 date = util.parsedate(date)
1032 diffopts = self.diffopts({'git': opts.get('git')})
1032 diffopts = self.diffopts({'git': opts.get('git')})
1033 if opts.get('checkname', True):
1033 if opts.get('checkname', True):
1034 self.checkpatchname(patchfn)
1034 self.checkpatchname(patchfn)
1035 inclsubs = checksubstate(repo)
1035 inclsubs = checksubstate(repo)
1036 if inclsubs:
1036 if inclsubs:
1037 substatestate = repo.dirstate['.hgsubstate']
1037 substatestate = repo.dirstate['.hgsubstate']
1038 if opts.get('include') or opts.get('exclude') or pats:
1038 if opts.get('include') or opts.get('exclude') or pats:
1039 match = scmutil.match(repo[None], pats, opts)
1039 match = scmutil.match(repo[None], pats, opts)
1040 # detect missing files in pats
1040 # detect missing files in pats
1041 def badfn(f, msg):
1041 def badfn(f, msg):
1042 if f != '.hgsubstate': # .hgsubstate is auto-created
1042 if f != '.hgsubstate': # .hgsubstate is auto-created
1043 raise util.Abort('%s: %s' % (f, msg))
1043 raise util.Abort('%s: %s' % (f, msg))
1044 match.bad = badfn
1044 match.bad = badfn
1045 changes = repo.status(match=match)
1045 changes = repo.status(match=match)
1046 else:
1046 else:
1047 changes = self.checklocalchanges(repo, force=True)
1047 changes = self.checklocalchanges(repo, force=True)
1048 commitfiles = list(inclsubs)
1048 commitfiles = list(inclsubs)
1049 for files in changes[:3]:
1049 for files in changes[:3]:
1050 commitfiles.extend(files)
1050 commitfiles.extend(files)
1051 match = scmutil.matchfiles(repo, commitfiles)
1051 match = scmutil.matchfiles(repo, commitfiles)
1052 if len(repo[None].parents()) > 1:
1052 if len(repo[None].parents()) > 1:
1053 raise util.Abort(_('cannot manage merge changesets'))
1053 raise util.Abort(_('cannot manage merge changesets'))
1054 self.checktoppatch(repo)
1054 self.checktoppatch(repo)
1055 insert = self.fullseriesend()
1055 insert = self.fullseriesend()
1056 wlock = repo.wlock()
1056 wlock = repo.wlock()
1057 try:
1057 try:
1058 try:
1058 try:
1059 # if patch file write fails, abort early
1059 # if patch file write fails, abort early
1060 p = self.opener(patchfn, "w")
1060 p = self.opener(patchfn, "w")
1061 except IOError, e:
1061 except IOError, e:
1062 raise util.Abort(_('cannot write patch "%s": %s')
1062 raise util.Abort(_('cannot write patch "%s": %s')
1063 % (patchfn, e.strerror))
1063 % (patchfn, e.strerror))
1064 try:
1064 try:
1065 if self.plainmode:
1065 if self.plainmode:
1066 if user:
1066 if user:
1067 p.write("From: " + user + "\n")
1067 p.write("From: " + user + "\n")
1068 if not date:
1068 if not date:
1069 p.write("\n")
1069 p.write("\n")
1070 if date:
1070 if date:
1071 p.write("Date: %d %d\n\n" % date)
1071 p.write("Date: %d %d\n\n" % date)
1072 else:
1072 else:
1073 p.write("# HG changeset patch\n")
1073 p.write("# HG changeset patch\n")
1074 p.write("# Parent "
1074 p.write("# Parent "
1075 + hex(repo[None].p1().node()) + "\n")
1075 + hex(repo[None].p1().node()) + "\n")
1076 if user:
1076 if user:
1077 p.write("# User " + user + "\n")
1077 p.write("# User " + user + "\n")
1078 if date:
1078 if date:
1079 p.write("# Date %s %s\n\n" % date)
1079 p.write("# Date %s %s\n\n" % date)
1080
1080
1081 defaultmsg = "[mq]: %s" % patchfn
1081 defaultmsg = "[mq]: %s" % patchfn
1082 editor = cmdutil.getcommiteditor()
1082 editor = cmdutil.getcommiteditor()
1083 if edit:
1083 if edit:
1084 def finishdesc(desc):
1084 def finishdesc(desc):
1085 if desc.rstrip():
1085 if desc.rstrip():
1086 return desc
1086 return desc
1087 else:
1087 else:
1088 return defaultmsg
1088 return defaultmsg
1089 # i18n: this message is shown in editor with "HG: " prefix
1089 # i18n: this message is shown in editor with "HG: " prefix
1090 extramsg = _('Leave message empty to use default message.')
1090 extramsg = _('Leave message empty to use default message.')
1091 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1091 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1092 extramsg=extramsg)
1092 extramsg=extramsg)
1093 commitmsg = msg
1093 commitmsg = msg
1094 else:
1094 else:
1095 commitmsg = msg or defaultmsg
1095 commitmsg = msg or defaultmsg
1096
1096
1097 n = newcommit(repo, None, commitmsg, user, date, match=match,
1097 n = newcommit(repo, None, commitmsg, user, date, match=match,
1098 force=True, editor=editor)
1098 force=True, editor=editor)
1099 if n is None:
1099 if n is None:
1100 raise util.Abort(_("repo commit failed"))
1100 raise util.Abort(_("repo commit failed"))
1101 try:
1101 try:
1102 self.fullseries[insert:insert] = [patchfn]
1102 self.fullseries[insert:insert] = [patchfn]
1103 self.applied.append(statusentry(n, patchfn))
1103 self.applied.append(statusentry(n, patchfn))
1104 self.parseseries()
1104 self.parseseries()
1105 self.seriesdirty = True
1105 self.seriesdirty = True
1106 self.applieddirty = True
1106 self.applieddirty = True
1107 nctx = repo[n]
1107 nctx = repo[n]
1108 if nctx.description() != defaultmsg.rstrip():
1108 if nctx.description() != defaultmsg.rstrip():
1109 msg = nctx.description() + "\n\n"
1109 msg = nctx.description() + "\n\n"
1110 p.write(msg)
1110 p.write(msg)
1111 if commitfiles:
1111 if commitfiles:
1112 parent = self.qparents(repo, n)
1112 parent = self.qparents(repo, n)
1113 if inclsubs:
1113 if inclsubs:
1114 self.putsubstate2changes(substatestate, changes)
1114 self.putsubstate2changes(substatestate, changes)
1115 chunks = patchmod.diff(repo, node1=parent, node2=n,
1115 chunks = patchmod.diff(repo, node1=parent, node2=n,
1116 changes=changes, opts=diffopts)
1116 changes=changes, opts=diffopts)
1117 for chunk in chunks:
1117 for chunk in chunks:
1118 p.write(chunk)
1118 p.write(chunk)
1119 p.close()
1119 p.close()
1120 r = self.qrepo()
1120 r = self.qrepo()
1121 if r:
1121 if r:
1122 r[None].add([patchfn])
1122 r[None].add([patchfn])
1123 except: # re-raises
1123 except: # re-raises
1124 repo.rollback()
1124 repo.rollback()
1125 raise
1125 raise
1126 except Exception:
1126 except Exception:
1127 patchpath = self.join(patchfn)
1127 patchpath = self.join(patchfn)
1128 try:
1128 try:
1129 os.unlink(patchpath)
1129 os.unlink(patchpath)
1130 except OSError:
1130 except OSError:
1131 self.ui.warn(_('error unlinking %s\n') % patchpath)
1131 self.ui.warn(_('error unlinking %s\n') % patchpath)
1132 raise
1132 raise
1133 self.removeundo(repo)
1133 self.removeundo(repo)
1134 finally:
1134 finally:
1135 release(wlock)
1135 release(wlock)
1136
1136
1137 def isapplied(self, patch):
1137 def isapplied(self, patch):
1138 """returns (index, rev, patch)"""
1138 """returns (index, rev, patch)"""
1139 for i, a in enumerate(self.applied):
1139 for i, a in enumerate(self.applied):
1140 if a.name == patch:
1140 if a.name == patch:
1141 return (i, a.node, a.name)
1141 return (i, a.node, a.name)
1142 return None
1142 return None
1143
1143
1144 # if the exact patch name does not exist, we try a few
1144 # if the exact patch name does not exist, we try a few
1145 # variations. If strict is passed, we try only #1
1145 # variations. If strict is passed, we try only #1
1146 #
1146 #
1147 # 1) a number (as string) to indicate an offset in the series file
1147 # 1) a number (as string) to indicate an offset in the series file
1148 # 2) a unique substring of the patch name was given
1148 # 2) a unique substring of the patch name was given
1149 # 3) patchname[-+]num to indicate an offset in the series file
1149 # 3) patchname[-+]num to indicate an offset in the series file
1150 def lookup(self, patch, strict=False):
1150 def lookup(self, patch, strict=False):
1151 def partialname(s):
1151 def partialname(s):
1152 if s in self.series:
1152 if s in self.series:
1153 return s
1153 return s
1154 matches = [x for x in self.series if s in x]
1154 matches = [x for x in self.series if s in x]
1155 if len(matches) > 1:
1155 if len(matches) > 1:
1156 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1156 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1157 for m in matches:
1157 for m in matches:
1158 self.ui.warn(' %s\n' % m)
1158 self.ui.warn(' %s\n' % m)
1159 return None
1159 return None
1160 if matches:
1160 if matches:
1161 return matches[0]
1161 return matches[0]
1162 if self.series and self.applied:
1162 if self.series and self.applied:
1163 if s == 'qtip':
1163 if s == 'qtip':
1164 return self.series[self.seriesend(True) - 1]
1164 return self.series[self.seriesend(True) - 1]
1165 if s == 'qbase':
1165 if s == 'qbase':
1166 return self.series[0]
1166 return self.series[0]
1167 return None
1167 return None
1168
1168
1169 if patch in self.series:
1169 if patch in self.series:
1170 return patch
1170 return patch
1171
1171
1172 if not os.path.isfile(self.join(patch)):
1172 if not os.path.isfile(self.join(patch)):
1173 try:
1173 try:
1174 sno = int(patch)
1174 sno = int(patch)
1175 except (ValueError, OverflowError):
1175 except (ValueError, OverflowError):
1176 pass
1176 pass
1177 else:
1177 else:
1178 if -len(self.series) <= sno < len(self.series):
1178 if -len(self.series) <= sno < len(self.series):
1179 return self.series[sno]
1179 return self.series[sno]
1180
1180
1181 if not strict:
1181 if not strict:
1182 res = partialname(patch)
1182 res = partialname(patch)
1183 if res:
1183 if res:
1184 return res
1184 return res
1185 minus = patch.rfind('-')
1185 minus = patch.rfind('-')
1186 if minus >= 0:
1186 if minus >= 0:
1187 res = partialname(patch[:minus])
1187 res = partialname(patch[:minus])
1188 if res:
1188 if res:
1189 i = self.series.index(res)
1189 i = self.series.index(res)
1190 try:
1190 try:
1191 off = int(patch[minus + 1:] or 1)
1191 off = int(patch[minus + 1:] or 1)
1192 except (ValueError, OverflowError):
1192 except (ValueError, OverflowError):
1193 pass
1193 pass
1194 else:
1194 else:
1195 if i - off >= 0:
1195 if i - off >= 0:
1196 return self.series[i - off]
1196 return self.series[i - off]
1197 plus = patch.rfind('+')
1197 plus = patch.rfind('+')
1198 if plus >= 0:
1198 if plus >= 0:
1199 res = partialname(patch[:plus])
1199 res = partialname(patch[:plus])
1200 if res:
1200 if res:
1201 i = self.series.index(res)
1201 i = self.series.index(res)
1202 try:
1202 try:
1203 off = int(patch[plus + 1:] or 1)
1203 off = int(patch[plus + 1:] or 1)
1204 except (ValueError, OverflowError):
1204 except (ValueError, OverflowError):
1205 pass
1205 pass
1206 else:
1206 else:
1207 if i + off < len(self.series):
1207 if i + off < len(self.series):
1208 return self.series[i + off]
1208 return self.series[i + off]
1209 raise util.Abort(_("patch %s not in series") % patch)
1209 raise util.Abort(_("patch %s not in series") % patch)
1210
1210
1211 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1211 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1212 all=False, move=False, exact=False, nobackup=False,
1212 all=False, move=False, exact=False, nobackup=False,
1213 keepchanges=False):
1213 keepchanges=False):
1214 self.checkkeepchanges(keepchanges, force)
1214 self.checkkeepchanges(keepchanges, force)
1215 diffopts = self.diffopts()
1215 diffopts = self.diffopts()
1216 wlock = repo.wlock()
1216 wlock = repo.wlock()
1217 try:
1217 try:
1218 heads = []
1218 heads = []
1219 for hs in repo.branchmap().itervalues():
1219 for hs in repo.branchmap().itervalues():
1220 heads.extend(hs)
1220 heads.extend(hs)
1221 if not heads:
1221 if not heads:
1222 heads = [nullid]
1222 heads = [nullid]
1223 if repo.dirstate.p1() not in heads and not exact:
1223 if repo.dirstate.p1() not in heads and not exact:
1224 self.ui.status(_("(working directory not at a head)\n"))
1224 self.ui.status(_("(working directory not at a head)\n"))
1225
1225
1226 if not self.series:
1226 if not self.series:
1227 self.ui.warn(_('no patches in series\n'))
1227 self.ui.warn(_('no patches in series\n'))
1228 return 0
1228 return 0
1229
1229
1230 # Suppose our series file is: A B C and the current 'top'
1230 # Suppose our series file is: A B C and the current 'top'
1231 # patch is B. qpush C should be performed (moving forward)
1231 # patch is B. qpush C should be performed (moving forward)
1232 # qpush B is a NOP (no change) qpush A is an error (can't
1232 # qpush B is a NOP (no change) qpush A is an error (can't
1233 # go backwards with qpush)
1233 # go backwards with qpush)
1234 if patch:
1234 if patch:
1235 patch = self.lookup(patch)
1235 patch = self.lookup(patch)
1236 info = self.isapplied(patch)
1236 info = self.isapplied(patch)
1237 if info and info[0] >= len(self.applied) - 1:
1237 if info and info[0] >= len(self.applied) - 1:
1238 self.ui.warn(
1238 self.ui.warn(
1239 _('qpush: %s is already at the top\n') % patch)
1239 _('qpush: %s is already at the top\n') % patch)
1240 return 0
1240 return 0
1241
1241
1242 pushable, reason = self.pushable(patch)
1242 pushable, reason = self.pushable(patch)
1243 if pushable:
1243 if pushable:
1244 if self.series.index(patch) < self.seriesend():
1244 if self.series.index(patch) < self.seriesend():
1245 raise util.Abort(
1245 raise util.Abort(
1246 _("cannot push to a previous patch: %s") % patch)
1246 _("cannot push to a previous patch: %s") % patch)
1247 else:
1247 else:
1248 if reason:
1248 if reason:
1249 reason = _('guarded by %s') % reason
1249 reason = _('guarded by %s') % reason
1250 else:
1250 else:
1251 reason = _('no matching guards')
1251 reason = _('no matching guards')
1252 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1252 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1253 return 1
1253 return 1
1254 elif all:
1254 elif all:
1255 patch = self.series[-1]
1255 patch = self.series[-1]
1256 if self.isapplied(patch):
1256 if self.isapplied(patch):
1257 self.ui.warn(_('all patches are currently applied\n'))
1257 self.ui.warn(_('all patches are currently applied\n'))
1258 return 0
1258 return 0
1259
1259
1260 # Following the above example, starting at 'top' of B:
1260 # Following the above example, starting at 'top' of B:
1261 # qpush should be performed (pushes C), but a subsequent
1261 # qpush should be performed (pushes C), but a subsequent
1262 # qpush without an argument is an error (nothing to
1262 # qpush without an argument is an error (nothing to
1263 # apply). This allows a loop of "...while hg qpush..." to
1263 # apply). This allows a loop of "...while hg qpush..." to
1264 # work as it detects an error when done
1264 # work as it detects an error when done
1265 start = self.seriesend()
1265 start = self.seriesend()
1266 if start == len(self.series):
1266 if start == len(self.series):
1267 self.ui.warn(_('patch series already fully applied\n'))
1267 self.ui.warn(_('patch series already fully applied\n'))
1268 return 1
1268 return 1
1269 if not force and not keepchanges:
1269 if not force and not keepchanges:
1270 self.checklocalchanges(repo, refresh=self.applied)
1270 self.checklocalchanges(repo, refresh=self.applied)
1271
1271
1272 if exact:
1272 if exact:
1273 if keepchanges:
1273 if keepchanges:
1274 raise util.Abort(
1274 raise util.Abort(
1275 _("cannot use --exact and --keep-changes together"))
1275 _("cannot use --exact and --keep-changes together"))
1276 if move:
1276 if move:
1277 raise util.Abort(_('cannot use --exact and --move '
1277 raise util.Abort(_('cannot use --exact and --move '
1278 'together'))
1278 'together'))
1279 if self.applied:
1279 if self.applied:
1280 raise util.Abort(_('cannot push --exact with applied '
1280 raise util.Abort(_('cannot push --exact with applied '
1281 'patches'))
1281 'patches'))
1282 root = self.series[start]
1282 root = self.series[start]
1283 target = patchheader(self.join(root), self.plainmode).parent
1283 target = patchheader(self.join(root), self.plainmode).parent
1284 if not target:
1284 if not target:
1285 raise util.Abort(
1285 raise util.Abort(
1286 _("%s does not have a parent recorded") % root)
1286 _("%s does not have a parent recorded") % root)
1287 if not repo[target] == repo['.']:
1287 if not repo[target] == repo['.']:
1288 hg.update(repo, target)
1288 hg.update(repo, target)
1289
1289
1290 if move:
1290 if move:
1291 if not patch:
1291 if not patch:
1292 raise util.Abort(_("please specify the patch to move"))
1292 raise util.Abort(_("please specify the patch to move"))
1293 for fullstart, rpn in enumerate(self.fullseries):
1293 for fullstart, rpn in enumerate(self.fullseries):
1294 # strip markers for patch guards
1294 # strip markers for patch guards
1295 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1295 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1296 break
1296 break
1297 for i, rpn in enumerate(self.fullseries[fullstart:]):
1297 for i, rpn in enumerate(self.fullseries[fullstart:]):
1298 # strip markers for patch guards
1298 # strip markers for patch guards
1299 if self.guard_re.split(rpn, 1)[0] == patch:
1299 if self.guard_re.split(rpn, 1)[0] == patch:
1300 break
1300 break
1301 index = fullstart + i
1301 index = fullstart + i
1302 assert index < len(self.fullseries)
1302 assert index < len(self.fullseries)
1303 fullpatch = self.fullseries[index]
1303 fullpatch = self.fullseries[index]
1304 del self.fullseries[index]
1304 del self.fullseries[index]
1305 self.fullseries.insert(fullstart, fullpatch)
1305 self.fullseries.insert(fullstart, fullpatch)
1306 self.parseseries()
1306 self.parseseries()
1307 self.seriesdirty = True
1307 self.seriesdirty = True
1308
1308
1309 self.applieddirty = True
1309 self.applieddirty = True
1310 if start > 0:
1310 if start > 0:
1311 self.checktoppatch(repo)
1311 self.checktoppatch(repo)
1312 if not patch:
1312 if not patch:
1313 patch = self.series[start]
1313 patch = self.series[start]
1314 end = start + 1
1314 end = start + 1
1315 else:
1315 else:
1316 end = self.series.index(patch, start) + 1
1316 end = self.series.index(patch, start) + 1
1317
1317
1318 tobackup = set()
1318 tobackup = set()
1319 if (not nobackup and force) or keepchanges:
1319 if (not nobackup and force) or keepchanges:
1320 m, a, r, d = self.checklocalchanges(repo, force=True)
1320 m, a, r, d = self.checklocalchanges(repo, force=True)
1321 if keepchanges:
1321 if keepchanges:
1322 tobackup.update(m + a + r + d)
1322 tobackup.update(m + a + r + d)
1323 else:
1323 else:
1324 tobackup.update(m + a)
1324 tobackup.update(m + a)
1325
1325
1326 s = self.series[start:end]
1326 s = self.series[start:end]
1327 all_files = set()
1327 all_files = set()
1328 try:
1328 try:
1329 if mergeq:
1329 if mergeq:
1330 ret = self.mergepatch(repo, mergeq, s, diffopts)
1330 ret = self.mergepatch(repo, mergeq, s, diffopts)
1331 else:
1331 else:
1332 ret = self.apply(repo, s, list, all_files=all_files,
1332 ret = self.apply(repo, s, list, all_files=all_files,
1333 tobackup=tobackup, keepchanges=keepchanges)
1333 tobackup=tobackup, keepchanges=keepchanges)
1334 except: # re-raises
1334 except: # re-raises
1335 self.ui.warn(_('cleaning up working directory...'))
1335 self.ui.warn(_('cleaning up working directory...'))
1336 node = repo.dirstate.p1()
1336 node = repo.dirstate.p1()
1337 hg.revert(repo, node, None)
1337 hg.revert(repo, node, None)
1338 # only remove unknown files that we know we touched or
1338 # only remove unknown files that we know we touched or
1339 # created while patching
1339 # created while patching
1340 for f in all_files:
1340 for f in all_files:
1341 if f not in repo.dirstate:
1341 if f not in repo.dirstate:
1342 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1342 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1343 self.ui.warn(_('done\n'))
1343 self.ui.warn(_('done\n'))
1344 raise
1344 raise
1345
1345
1346 if not self.applied:
1346 if not self.applied:
1347 return ret[0]
1347 return ret[0]
1348 top = self.applied[-1].name
1348 top = self.applied[-1].name
1349 if ret[0] and ret[0] > 1:
1349 if ret[0] and ret[0] > 1:
1350 msg = _("errors during apply, please fix and refresh %s\n")
1350 msg = _("errors during apply, please fix and refresh %s\n")
1351 self.ui.write(msg % top)
1351 self.ui.write(msg % top)
1352 else:
1352 else:
1353 self.ui.write(_("now at: %s\n") % top)
1353 self.ui.write(_("now at: %s\n") % top)
1354 return ret[0]
1354 return ret[0]
1355
1355
1356 finally:
1356 finally:
1357 wlock.release()
1357 wlock.release()
1358
1358
1359 def pop(self, repo, patch=None, force=False, update=True, all=False,
1359 def pop(self, repo, patch=None, force=False, update=True, all=False,
1360 nobackup=False, keepchanges=False):
1360 nobackup=False, keepchanges=False):
1361 self.checkkeepchanges(keepchanges, force)
1361 self.checkkeepchanges(keepchanges, force)
1362 wlock = repo.wlock()
1362 wlock = repo.wlock()
1363 try:
1363 try:
1364 if patch:
1364 if patch:
1365 # index, rev, patch
1365 # index, rev, patch
1366 info = self.isapplied(patch)
1366 info = self.isapplied(patch)
1367 if not info:
1367 if not info:
1368 patch = self.lookup(patch)
1368 patch = self.lookup(patch)
1369 info = self.isapplied(patch)
1369 info = self.isapplied(patch)
1370 if not info:
1370 if not info:
1371 raise util.Abort(_("patch %s is not applied") % patch)
1371 raise util.Abort(_("patch %s is not applied") % patch)
1372
1372
1373 if not self.applied:
1373 if not self.applied:
1374 # Allow qpop -a to work repeatedly,
1374 # Allow qpop -a to work repeatedly,
1375 # but not qpop without an argument
1375 # but not qpop without an argument
1376 self.ui.warn(_("no patches applied\n"))
1376 self.ui.warn(_("no patches applied\n"))
1377 return not all
1377 return not all
1378
1378
1379 if all:
1379 if all:
1380 start = 0
1380 start = 0
1381 elif patch:
1381 elif patch:
1382 start = info[0] + 1
1382 start = info[0] + 1
1383 else:
1383 else:
1384 start = len(self.applied) - 1
1384 start = len(self.applied) - 1
1385
1385
1386 if start >= len(self.applied):
1386 if start >= len(self.applied):
1387 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1387 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1388 return
1388 return
1389
1389
1390 if not update:
1390 if not update:
1391 parents = repo.dirstate.parents()
1391 parents = repo.dirstate.parents()
1392 rr = [x.node for x in self.applied]
1392 rr = [x.node for x in self.applied]
1393 for p in parents:
1393 for p in parents:
1394 if p in rr:
1394 if p in rr:
1395 self.ui.warn(_("qpop: forcing dirstate update\n"))
1395 self.ui.warn(_("qpop: forcing dirstate update\n"))
1396 update = True
1396 update = True
1397 else:
1397 else:
1398 parents = [p.node() for p in repo[None].parents()]
1398 parents = [p.node() for p in repo[None].parents()]
1399 needupdate = False
1399 needupdate = False
1400 for entry in self.applied[start:]:
1400 for entry in self.applied[start:]:
1401 if entry.node in parents:
1401 if entry.node in parents:
1402 needupdate = True
1402 needupdate = True
1403 break
1403 break
1404 update = needupdate
1404 update = needupdate
1405
1405
1406 tobackup = set()
1406 tobackup = set()
1407 if update:
1407 if update:
1408 m, a, r, d = self.checklocalchanges(
1408 m, a, r, d = self.checklocalchanges(
1409 repo, force=force or keepchanges)
1409 repo, force=force or keepchanges)
1410 if force:
1410 if force:
1411 if not nobackup:
1411 if not nobackup:
1412 tobackup.update(m + a)
1412 tobackup.update(m + a)
1413 elif keepchanges:
1413 elif keepchanges:
1414 tobackup.update(m + a + r + d)
1414 tobackup.update(m + a + r + d)
1415
1415
1416 self.applieddirty = True
1416 self.applieddirty = True
1417 end = len(self.applied)
1417 end = len(self.applied)
1418 rev = self.applied[start].node
1418 rev = self.applied[start].node
1419
1419
1420 try:
1420 try:
1421 heads = repo.changelog.heads(rev)
1421 heads = repo.changelog.heads(rev)
1422 except error.LookupError:
1422 except error.LookupError:
1423 node = short(rev)
1423 node = short(rev)
1424 raise util.Abort(_('trying to pop unknown node %s') % node)
1424 raise util.Abort(_('trying to pop unknown node %s') % node)
1425
1425
1426 if heads != [self.applied[-1].node]:
1426 if heads != [self.applied[-1].node]:
1427 raise util.Abort(_("popping would remove a revision not "
1427 raise util.Abort(_("popping would remove a revision not "
1428 "managed by this patch queue"))
1428 "managed by this patch queue"))
1429 if not repo[self.applied[-1].node].mutable():
1429 if not repo[self.applied[-1].node].mutable():
1430 raise util.Abort(
1430 raise util.Abort(
1431 _("popping would remove an immutable revision"),
1431 _("popping would remove an immutable revision"),
1432 hint=_('see "hg help phases" for details'))
1432 hint=_('see "hg help phases" for details'))
1433
1433
1434 # we know there are no local changes, so we can make a simplified
1434 # we know there are no local changes, so we can make a simplified
1435 # form of hg.update.
1435 # form of hg.update.
1436 if update:
1436 if update:
1437 qp = self.qparents(repo, rev)
1437 qp = self.qparents(repo, rev)
1438 ctx = repo[qp]
1438 ctx = repo[qp]
1439 m, a, r, d = repo.status(qp, '.')[:4]
1439 m, a, r, d = repo.status(qp, '.')[:4]
1440 if d:
1440 if d:
1441 raise util.Abort(_("deletions found between repo revs"))
1441 raise util.Abort(_("deletions found between repo revs"))
1442
1442
1443 tobackup = set(a + m + r) & tobackup
1443 tobackup = set(a + m + r) & tobackup
1444 if keepchanges and tobackup:
1444 if keepchanges and tobackup:
1445 raise util.Abort(_("local changes found, refresh first"))
1445 raise util.Abort(_("local changes found, refresh first"))
1446 self.backup(repo, tobackup)
1446 self.backup(repo, tobackup)
1447
1447
1448 for f in a:
1448 for f in a:
1449 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1449 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1450 repo.dirstate.drop(f)
1450 repo.dirstate.drop(f)
1451 for f in m + r:
1451 for f in m + r:
1452 fctx = ctx[f]
1452 fctx = ctx[f]
1453 repo.wwrite(f, fctx.data(), fctx.flags())
1453 repo.wwrite(f, fctx.data(), fctx.flags())
1454 repo.dirstate.normal(f)
1454 repo.dirstate.normal(f)
1455 repo.setparents(qp, nullid)
1455 repo.setparents(qp, nullid)
1456 for patch in reversed(self.applied[start:end]):
1456 for patch in reversed(self.applied[start:end]):
1457 self.ui.status(_("popping %s\n") % patch.name)
1457 self.ui.status(_("popping %s\n") % patch.name)
1458 del self.applied[start:end]
1458 del self.applied[start:end]
1459 strip(self.ui, repo, [rev], update=False, backup='strip')
1459 strip(self.ui, repo, [rev], update=False, backup='strip')
1460 for s, state in repo['.'].substate.items():
1460 for s, state in repo['.'].substate.items():
1461 repo['.'].sub(s).get(state)
1461 repo['.'].sub(s).get(state)
1462 if self.applied:
1462 if self.applied:
1463 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1463 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1464 else:
1464 else:
1465 self.ui.write(_("patch queue now empty\n"))
1465 self.ui.write(_("patch queue now empty\n"))
1466 finally:
1466 finally:
1467 wlock.release()
1467 wlock.release()
1468
1468
1469 def diff(self, repo, pats, opts):
1469 def diff(self, repo, pats, opts):
1470 top, patch = self.checktoppatch(repo)
1470 top, patch = self.checktoppatch(repo)
1471 if not top:
1471 if not top:
1472 self.ui.write(_("no patches applied\n"))
1472 self.ui.write(_("no patches applied\n"))
1473 return
1473 return
1474 qp = self.qparents(repo, top)
1474 qp = self.qparents(repo, top)
1475 if opts.get('reverse'):
1475 if opts.get('reverse'):
1476 node1, node2 = None, qp
1476 node1, node2 = None, qp
1477 else:
1477 else:
1478 node1, node2 = qp, None
1478 node1, node2 = qp, None
1479 diffopts = self.diffopts(opts, patch)
1479 diffopts = self.diffopts(opts, patch)
1480 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1480 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1481
1481
1482 def refresh(self, repo, pats=None, **opts):
1482 def refresh(self, repo, pats=None, **opts):
1483 if not self.applied:
1483 if not self.applied:
1484 self.ui.write(_("no patches applied\n"))
1484 self.ui.write(_("no patches applied\n"))
1485 return 1
1485 return 1
1486 msg = opts.get('msg', '').rstrip()
1486 msg = opts.get('msg', '').rstrip()
1487 edit = opts.get('edit')
1487 edit = opts.get('edit')
1488 newuser = opts.get('user')
1488 newuser = opts.get('user')
1489 newdate = opts.get('date')
1489 newdate = opts.get('date')
1490 if newdate:
1490 if newdate:
1491 newdate = '%d %d' % util.parsedate(newdate)
1491 newdate = '%d %d' % util.parsedate(newdate)
1492 wlock = repo.wlock()
1492 wlock = repo.wlock()
1493
1493
1494 try:
1494 try:
1495 self.checktoppatch(repo)
1495 self.checktoppatch(repo)
1496 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1496 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1497 if repo.changelog.heads(top) != [top]:
1497 if repo.changelog.heads(top) != [top]:
1498 raise util.Abort(_("cannot refresh a revision with children"))
1498 raise util.Abort(_("cannot refresh a revision with children"))
1499 if not repo[top].mutable():
1499 if not repo[top].mutable():
1500 raise util.Abort(_("cannot refresh immutable revision"),
1500 raise util.Abort(_("cannot refresh immutable revision"),
1501 hint=_('see "hg help phases" for details'))
1501 hint=_('see "hg help phases" for details'))
1502
1502
1503 cparents = repo.changelog.parents(top)
1503 cparents = repo.changelog.parents(top)
1504 patchparent = self.qparents(repo, top)
1504 patchparent = self.qparents(repo, top)
1505
1505
1506 inclsubs = checksubstate(repo, hex(patchparent))
1506 inclsubs = checksubstate(repo, hex(patchparent))
1507 if inclsubs:
1507 if inclsubs:
1508 substatestate = repo.dirstate['.hgsubstate']
1508 substatestate = repo.dirstate['.hgsubstate']
1509
1509
1510 ph = patchheader(self.join(patchfn), self.plainmode)
1510 ph = patchheader(self.join(patchfn), self.plainmode)
1511 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1511 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1512 if newuser:
1512 if newuser:
1513 ph.setuser(newuser)
1513 ph.setuser(newuser)
1514 if newdate:
1514 if newdate:
1515 ph.setdate(newdate)
1515 ph.setdate(newdate)
1516 ph.setparent(hex(patchparent))
1516 ph.setparent(hex(patchparent))
1517
1517
1518 # only commit new patch when write is complete
1518 # only commit new patch when write is complete
1519 patchf = self.opener(patchfn, 'w', atomictemp=True)
1519 patchf = self.opener(patchfn, 'w', atomictemp=True)
1520
1520
1521 # update the dirstate in place, strip off the qtip commit
1521 # update the dirstate in place, strip off the qtip commit
1522 # and then commit.
1522 # and then commit.
1523 #
1523 #
1524 # this should really read:
1524 # this should really read:
1525 # mm, dd, aa = repo.status(top, patchparent)[:3]
1525 # mm, dd, aa = repo.status(top, patchparent)[:3]
1526 # but we do it backwards to take advantage of manifest/changelog
1526 # but we do it backwards to take advantage of manifest/changelog
1527 # caching against the next repo.status call
1527 # caching against the next repo.status call
1528 mm, aa, dd = repo.status(patchparent, top)[:3]
1528 mm, aa, dd = repo.status(patchparent, top)[:3]
1529 changes = repo.changelog.read(top)
1529 changes = repo.changelog.read(top)
1530 man = repo.manifest.read(changes[0])
1530 man = repo.manifest.read(changes[0])
1531 aaa = aa[:]
1531 aaa = aa[:]
1532 matchfn = scmutil.match(repo[None], pats, opts)
1532 matchfn = scmutil.match(repo[None], pats, opts)
1533 # in short mode, we only diff the files included in the
1533 # in short mode, we only diff the files included in the
1534 # patch already plus specified files
1534 # patch already plus specified files
1535 if opts.get('short'):
1535 if opts.get('short'):
1536 # if amending a patch, we start with existing
1536 # if amending a patch, we start with existing
1537 # files plus specified files - unfiltered
1537 # files plus specified files - unfiltered
1538 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1538 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1539 # filter with include/exclude options
1539 # filter with include/exclude options
1540 matchfn = scmutil.match(repo[None], opts=opts)
1540 matchfn = scmutil.match(repo[None], opts=opts)
1541 else:
1541 else:
1542 match = scmutil.matchall(repo)
1542 match = scmutil.matchall(repo)
1543 m, a, r, d = repo.status(match=match)[:4]
1543 m, a, r, d = repo.status(match=match)[:4]
1544 mm = set(mm)
1544 mm = set(mm)
1545 aa = set(aa)
1545 aa = set(aa)
1546 dd = set(dd)
1546 dd = set(dd)
1547
1547
1548 # we might end up with files that were added between
1548 # we might end up with files that were added between
1549 # qtip and the dirstate parent, but then changed in the
1549 # qtip and the dirstate parent, but then changed in the
1550 # local dirstate. in this case, we want them to only
1550 # local dirstate. in this case, we want them to only
1551 # show up in the added section
1551 # show up in the added section
1552 for x in m:
1552 for x in m:
1553 if x not in aa:
1553 if x not in aa:
1554 mm.add(x)
1554 mm.add(x)
1555 # we might end up with files added by the local dirstate that
1555 # we might end up with files added by the local dirstate that
1556 # were deleted by the patch. In this case, they should only
1556 # were deleted by the patch. In this case, they should only
1557 # show up in the changed section.
1557 # show up in the changed section.
1558 for x in a:
1558 for x in a:
1559 if x in dd:
1559 if x in dd:
1560 dd.remove(x)
1560 dd.remove(x)
1561 mm.add(x)
1561 mm.add(x)
1562 else:
1562 else:
1563 aa.add(x)
1563 aa.add(x)
1564 # make sure any files deleted in the local dirstate
1564 # make sure any files deleted in the local dirstate
1565 # are not in the add or change column of the patch
1565 # are not in the add or change column of the patch
1566 forget = []
1566 forget = []
1567 for x in d + r:
1567 for x in d + r:
1568 if x in aa:
1568 if x in aa:
1569 aa.remove(x)
1569 aa.remove(x)
1570 forget.append(x)
1570 forget.append(x)
1571 continue
1571 continue
1572 else:
1572 else:
1573 mm.discard(x)
1573 mm.discard(x)
1574 dd.add(x)
1574 dd.add(x)
1575
1575
1576 m = list(mm)
1576 m = list(mm)
1577 r = list(dd)
1577 r = list(dd)
1578 a = list(aa)
1578 a = list(aa)
1579
1579
1580 # create 'match' that includes the files to be recommitted.
1580 # create 'match' that includes the files to be recommitted.
1581 # apply matchfn via repo.status to ensure correct case handling.
1581 # apply matchfn via repo.status to ensure correct case handling.
1582 cm, ca, cr, cd = repo.status(patchparent, match=matchfn)[:4]
1582 cm, ca, cr, cd = repo.status(patchparent, match=matchfn)[:4]
1583 allmatches = set(cm + ca + cr + cd)
1583 allmatches = set(cm + ca + cr + cd)
1584 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1584 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1585
1585
1586 files = set(inclsubs)
1586 files = set(inclsubs)
1587 for x in refreshchanges:
1587 for x in refreshchanges:
1588 files.update(x)
1588 files.update(x)
1589 match = scmutil.matchfiles(repo, files)
1589 match = scmutil.matchfiles(repo, files)
1590
1590
1591 bmlist = repo[top].bookmarks()
1591 bmlist = repo[top].bookmarks()
1592
1592
1593 try:
1593 try:
1594 if diffopts.git or diffopts.upgrade:
1594 if diffopts.git or diffopts.upgrade:
1595 copies = {}
1595 copies = {}
1596 for dst in a:
1596 for dst in a:
1597 src = repo.dirstate.copied(dst)
1597 src = repo.dirstate.copied(dst)
1598 # during qfold, the source file for copies may
1598 # during qfold, the source file for copies may
1599 # be removed. Treat this as a simple add.
1599 # be removed. Treat this as a simple add.
1600 if src is not None and src in repo.dirstate:
1600 if src is not None and src in repo.dirstate:
1601 copies.setdefault(src, []).append(dst)
1601 copies.setdefault(src, []).append(dst)
1602 repo.dirstate.add(dst)
1602 repo.dirstate.add(dst)
1603 # remember the copies between patchparent and qtip
1603 # remember the copies between patchparent and qtip
1604 for dst in aaa:
1604 for dst in aaa:
1605 f = repo.file(dst)
1605 f = repo.file(dst)
1606 src = f.renamed(man[dst])
1606 src = f.renamed(man[dst])
1607 if src:
1607 if src:
1608 copies.setdefault(src[0], []).extend(
1608 copies.setdefault(src[0], []).extend(
1609 copies.get(dst, []))
1609 copies.get(dst, []))
1610 if dst in a:
1610 if dst in a:
1611 copies[src[0]].append(dst)
1611 copies[src[0]].append(dst)
1612 # we can't copy a file created by the patch itself
1612 # we can't copy a file created by the patch itself
1613 if dst in copies:
1613 if dst in copies:
1614 del copies[dst]
1614 del copies[dst]
1615 for src, dsts in copies.iteritems():
1615 for src, dsts in copies.iteritems():
1616 for dst in dsts:
1616 for dst in dsts:
1617 repo.dirstate.copy(src, dst)
1617 repo.dirstate.copy(src, dst)
1618 else:
1618 else:
1619 for dst in a:
1619 for dst in a:
1620 repo.dirstate.add(dst)
1620 repo.dirstate.add(dst)
1621 # Drop useless copy information
1621 # Drop useless copy information
1622 for f in list(repo.dirstate.copies()):
1622 for f in list(repo.dirstate.copies()):
1623 repo.dirstate.copy(None, f)
1623 repo.dirstate.copy(None, f)
1624 for f in r:
1624 for f in r:
1625 repo.dirstate.remove(f)
1625 repo.dirstate.remove(f)
1626 # if the patch excludes a modified file, mark that
1626 # if the patch excludes a modified file, mark that
1627 # file with mtime=0 so status can see it.
1627 # file with mtime=0 so status can see it.
1628 mm = []
1628 mm = []
1629 for i in xrange(len(m) - 1, -1, -1):
1629 for i in xrange(len(m) - 1, -1, -1):
1630 if not matchfn(m[i]):
1630 if not matchfn(m[i]):
1631 mm.append(m[i])
1631 mm.append(m[i])
1632 del m[i]
1632 del m[i]
1633 for f in m:
1633 for f in m:
1634 repo.dirstate.normal(f)
1634 repo.dirstate.normal(f)
1635 for f in mm:
1635 for f in mm:
1636 repo.dirstate.normallookup(f)
1636 repo.dirstate.normallookup(f)
1637 for f in forget:
1637 for f in forget:
1638 repo.dirstate.drop(f)
1638 repo.dirstate.drop(f)
1639
1639
1640 user = ph.user or changes[1]
1640 user = ph.user or changes[1]
1641
1641
1642 oldphase = repo[top].phase()
1642 oldphase = repo[top].phase()
1643
1643
1644 # assumes strip can roll itself back if interrupted
1644 # assumes strip can roll itself back if interrupted
1645 repo.setparents(*cparents)
1645 repo.setparents(*cparents)
1646 self.applied.pop()
1646 self.applied.pop()
1647 self.applieddirty = True
1647 self.applieddirty = True
1648 strip(self.ui, repo, [top], update=False, backup='strip')
1648 strip(self.ui, repo, [top], update=False, backup='strip')
1649 except: # re-raises
1649 except: # re-raises
1650 repo.dirstate.invalidate()
1650 repo.dirstate.invalidate()
1651 raise
1651 raise
1652
1652
1653 try:
1653 try:
1654 # might be nice to attempt to roll back strip after this
1654 # might be nice to attempt to roll back strip after this
1655
1655
1656 defaultmsg = "[mq]: %s" % patchfn
1656 defaultmsg = "[mq]: %s" % patchfn
1657 editor = cmdutil.getcommiteditor()
1657 editor = cmdutil.getcommiteditor()
1658 if edit:
1658 if edit:
1659 def finishdesc(desc):
1659 def finishdesc(desc):
1660 if desc.rstrip():
1660 if desc.rstrip():
1661 ph.setmessage(desc)
1661 ph.setmessage(desc)
1662 return desc
1662 return desc
1663 return defaultmsg
1663 return defaultmsg
1664 # i18n: this message is shown in editor with "HG: " prefix
1664 # i18n: this message is shown in editor with "HG: " prefix
1665 extramsg = _('Leave message empty to use default message.')
1665 extramsg = _('Leave message empty to use default message.')
1666 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1666 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1667 extramsg=extramsg)
1667 extramsg=extramsg)
1668 message = msg or "\n".join(ph.message)
1668 message = msg or "\n".join(ph.message)
1669 elif not msg:
1669 elif not msg:
1670 if not ph.message:
1670 if not ph.message:
1671 message = defaultmsg
1671 message = defaultmsg
1672 else:
1672 else:
1673 message = "\n".join(ph.message)
1673 message = "\n".join(ph.message)
1674 else:
1674 else:
1675 message = msg
1675 message = msg
1676 ph.setmessage(msg)
1676 ph.setmessage(msg)
1677
1677
1678 # Ensure we create a new changeset in the same phase than
1678 # Ensure we create a new changeset in the same phase than
1679 # the old one.
1679 # the old one.
1680 n = newcommit(repo, oldphase, message, user, ph.date,
1680 n = newcommit(repo, oldphase, message, user, ph.date,
1681 match=match, force=True, editor=editor)
1681 match=match, force=True, editor=editor)
1682 # only write patch after a successful commit
1682 # only write patch after a successful commit
1683 c = [list(x) for x in refreshchanges]
1683 c = [list(x) for x in refreshchanges]
1684 if inclsubs:
1684 if inclsubs:
1685 self.putsubstate2changes(substatestate, c)
1685 self.putsubstate2changes(substatestate, c)
1686 chunks = patchmod.diff(repo, patchparent,
1686 chunks = patchmod.diff(repo, patchparent,
1687 changes=c, opts=diffopts)
1687 changes=c, opts=diffopts)
1688 comments = str(ph)
1688 comments = str(ph)
1689 if comments:
1689 if comments:
1690 patchf.write(comments)
1690 patchf.write(comments)
1691 for chunk in chunks:
1691 for chunk in chunks:
1692 patchf.write(chunk)
1692 patchf.write(chunk)
1693 patchf.close()
1693 patchf.close()
1694
1694
1695 marks = repo._bookmarks
1695 marks = repo._bookmarks
1696 for bm in bmlist:
1696 for bm in bmlist:
1697 marks[bm] = n
1697 marks[bm] = n
1698 marks.write()
1698 marks.write()
1699
1699
1700 self.applied.append(statusentry(n, patchfn))
1700 self.applied.append(statusentry(n, patchfn))
1701 except: # re-raises
1701 except: # re-raises
1702 ctx = repo[cparents[0]]
1702 ctx = repo[cparents[0]]
1703 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1703 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1704 self.savedirty()
1704 self.savedirty()
1705 self.ui.warn(_('refresh interrupted while patch was popped! '
1705 self.ui.warn(_('refresh interrupted while patch was popped! '
1706 '(revert --all, qpush to recover)\n'))
1706 '(revert --all, qpush to recover)\n'))
1707 raise
1707 raise
1708 finally:
1708 finally:
1709 wlock.release()
1709 wlock.release()
1710 self.removeundo(repo)
1710 self.removeundo(repo)
1711
1711
1712 def init(self, repo, create=False):
1712 def init(self, repo, create=False):
1713 if not create and os.path.isdir(self.path):
1713 if not create and os.path.isdir(self.path):
1714 raise util.Abort(_("patch queue directory already exists"))
1714 raise util.Abort(_("patch queue directory already exists"))
1715 try:
1715 try:
1716 os.mkdir(self.path)
1716 os.mkdir(self.path)
1717 except OSError, inst:
1717 except OSError, inst:
1718 if inst.errno != errno.EEXIST or not create:
1718 if inst.errno != errno.EEXIST or not create:
1719 raise
1719 raise
1720 if create:
1720 if create:
1721 return self.qrepo(create=True)
1721 return self.qrepo(create=True)
1722
1722
1723 def unapplied(self, repo, patch=None):
1723 def unapplied(self, repo, patch=None):
1724 if patch and patch not in self.series:
1724 if patch and patch not in self.series:
1725 raise util.Abort(_("patch %s is not in series file") % patch)
1725 raise util.Abort(_("patch %s is not in series file") % patch)
1726 if not patch:
1726 if not patch:
1727 start = self.seriesend()
1727 start = self.seriesend()
1728 else:
1728 else:
1729 start = self.series.index(patch) + 1
1729 start = self.series.index(patch) + 1
1730 unapplied = []
1730 unapplied = []
1731 for i in xrange(start, len(self.series)):
1731 for i in xrange(start, len(self.series)):
1732 pushable, reason = self.pushable(i)
1732 pushable, reason = self.pushable(i)
1733 if pushable:
1733 if pushable:
1734 unapplied.append((i, self.series[i]))
1734 unapplied.append((i, self.series[i]))
1735 self.explainpushable(i)
1735 self.explainpushable(i)
1736 return unapplied
1736 return unapplied
1737
1737
1738 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1738 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1739 summary=False):
1739 summary=False):
1740 def displayname(pfx, patchname, state):
1740 def displayname(pfx, patchname, state):
1741 if pfx:
1741 if pfx:
1742 self.ui.write(pfx)
1742 self.ui.write(pfx)
1743 if summary:
1743 if summary:
1744 ph = patchheader(self.join(patchname), self.plainmode)
1744 ph = patchheader(self.join(patchname), self.plainmode)
1745 msg = ph.message and ph.message[0] or ''
1745 msg = ph.message and ph.message[0] or ''
1746 if self.ui.formatted():
1746 if self.ui.formatted():
1747 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1747 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1748 if width > 0:
1748 if width > 0:
1749 msg = util.ellipsis(msg, width)
1749 msg = util.ellipsis(msg, width)
1750 else:
1750 else:
1751 msg = ''
1751 msg = ''
1752 self.ui.write(patchname, label='qseries.' + state)
1752 self.ui.write(patchname, label='qseries.' + state)
1753 self.ui.write(': ')
1753 self.ui.write(': ')
1754 self.ui.write(msg, label='qseries.message.' + state)
1754 self.ui.write(msg, label='qseries.message.' + state)
1755 else:
1755 else:
1756 self.ui.write(patchname, label='qseries.' + state)
1756 self.ui.write(patchname, label='qseries.' + state)
1757 self.ui.write('\n')
1757 self.ui.write('\n')
1758
1758
1759 applied = set([p.name for p in self.applied])
1759 applied = set([p.name for p in self.applied])
1760 if length is None:
1760 if length is None:
1761 length = len(self.series) - start
1761 length = len(self.series) - start
1762 if not missing:
1762 if not missing:
1763 if self.ui.verbose:
1763 if self.ui.verbose:
1764 idxwidth = len(str(start + length - 1))
1764 idxwidth = len(str(start + length - 1))
1765 for i in xrange(start, start + length):
1765 for i in xrange(start, start + length):
1766 patch = self.series[i]
1766 patch = self.series[i]
1767 if patch in applied:
1767 if patch in applied:
1768 char, state = 'A', 'applied'
1768 char, state = 'A', 'applied'
1769 elif self.pushable(i)[0]:
1769 elif self.pushable(i)[0]:
1770 char, state = 'U', 'unapplied'
1770 char, state = 'U', 'unapplied'
1771 else:
1771 else:
1772 char, state = 'G', 'guarded'
1772 char, state = 'G', 'guarded'
1773 pfx = ''
1773 pfx = ''
1774 if self.ui.verbose:
1774 if self.ui.verbose:
1775 pfx = '%*d %s ' % (idxwidth, i, char)
1775 pfx = '%*d %s ' % (idxwidth, i, char)
1776 elif status and status != char:
1776 elif status and status != char:
1777 continue
1777 continue
1778 displayname(pfx, patch, state)
1778 displayname(pfx, patch, state)
1779 else:
1779 else:
1780 msng_list = []
1780 msng_list = []
1781 for root, dirs, files in os.walk(self.path):
1781 for root, dirs, files in os.walk(self.path):
1782 d = root[len(self.path) + 1:]
1782 d = root[len(self.path) + 1:]
1783 for f in files:
1783 for f in files:
1784 fl = os.path.join(d, f)
1784 fl = os.path.join(d, f)
1785 if (fl not in self.series and
1785 if (fl not in self.series and
1786 fl not in (self.statuspath, self.seriespath,
1786 fl not in (self.statuspath, self.seriespath,
1787 self.guardspath)
1787 self.guardspath)
1788 and not fl.startswith('.')):
1788 and not fl.startswith('.')):
1789 msng_list.append(fl)
1789 msng_list.append(fl)
1790 for x in sorted(msng_list):
1790 for x in sorted(msng_list):
1791 pfx = self.ui.verbose and ('D ') or ''
1791 pfx = self.ui.verbose and ('D ') or ''
1792 displayname(pfx, x, 'missing')
1792 displayname(pfx, x, 'missing')
1793
1793
1794 def issaveline(self, l):
1794 def issaveline(self, l):
1795 if l.name == '.hg.patches.save.line':
1795 if l.name == '.hg.patches.save.line':
1796 return True
1796 return True
1797
1797
1798 def qrepo(self, create=False):
1798 def qrepo(self, create=False):
1799 ui = self.baseui.copy()
1799 ui = self.baseui.copy()
1800 if create or os.path.isdir(self.join(".hg")):
1800 if create or os.path.isdir(self.join(".hg")):
1801 return hg.repository(ui, path=self.path, create=create)
1801 return hg.repository(ui, path=self.path, create=create)
1802
1802
1803 def restore(self, repo, rev, delete=None, qupdate=None):
1803 def restore(self, repo, rev, delete=None, qupdate=None):
1804 desc = repo[rev].description().strip()
1804 desc = repo[rev].description().strip()
1805 lines = desc.splitlines()
1805 lines = desc.splitlines()
1806 i = 0
1806 i = 0
1807 datastart = None
1807 datastart = None
1808 series = []
1808 series = []
1809 applied = []
1809 applied = []
1810 qpp = None
1810 qpp = None
1811 for i, line in enumerate(lines):
1811 for i, line in enumerate(lines):
1812 if line == 'Patch Data:':
1812 if line == 'Patch Data:':
1813 datastart = i + 1
1813 datastart = i + 1
1814 elif line.startswith('Dirstate:'):
1814 elif line.startswith('Dirstate:'):
1815 l = line.rstrip()
1815 l = line.rstrip()
1816 l = l[10:].split(' ')
1816 l = l[10:].split(' ')
1817 qpp = [bin(x) for x in l]
1817 qpp = [bin(x) for x in l]
1818 elif datastart is not None:
1818 elif datastart is not None:
1819 l = line.rstrip()
1819 l = line.rstrip()
1820 n, name = l.split(':', 1)
1820 n, name = l.split(':', 1)
1821 if n:
1821 if n:
1822 applied.append(statusentry(bin(n), name))
1822 applied.append(statusentry(bin(n), name))
1823 else:
1823 else:
1824 series.append(l)
1824 series.append(l)
1825 if datastart is None:
1825 if datastart is None:
1826 self.ui.warn(_("no saved patch data found\n"))
1826 self.ui.warn(_("no saved patch data found\n"))
1827 return 1
1827 return 1
1828 self.ui.warn(_("restoring status: %s\n") % lines[0])
1828 self.ui.warn(_("restoring status: %s\n") % lines[0])
1829 self.fullseries = series
1829 self.fullseries = series
1830 self.applied = applied
1830 self.applied = applied
1831 self.parseseries()
1831 self.parseseries()
1832 self.seriesdirty = True
1832 self.seriesdirty = True
1833 self.applieddirty = True
1833 self.applieddirty = True
1834 heads = repo.changelog.heads()
1834 heads = repo.changelog.heads()
1835 if delete:
1835 if delete:
1836 if rev not in heads:
1836 if rev not in heads:
1837 self.ui.warn(_("save entry has children, leaving it alone\n"))
1837 self.ui.warn(_("save entry has children, leaving it alone\n"))
1838 else:
1838 else:
1839 self.ui.warn(_("removing save entry %s\n") % short(rev))
1839 self.ui.warn(_("removing save entry %s\n") % short(rev))
1840 pp = repo.dirstate.parents()
1840 pp = repo.dirstate.parents()
1841 if rev in pp:
1841 if rev in pp:
1842 update = True
1842 update = True
1843 else:
1843 else:
1844 update = False
1844 update = False
1845 strip(self.ui, repo, [rev], update=update, backup='strip')
1845 strip(self.ui, repo, [rev], update=update, backup='strip')
1846 if qpp:
1846 if qpp:
1847 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1847 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1848 (short(qpp[0]), short(qpp[1])))
1848 (short(qpp[0]), short(qpp[1])))
1849 if qupdate:
1849 if qupdate:
1850 self.ui.status(_("updating queue directory\n"))
1850 self.ui.status(_("updating queue directory\n"))
1851 r = self.qrepo()
1851 r = self.qrepo()
1852 if not r:
1852 if not r:
1853 self.ui.warn(_("unable to load queue repository\n"))
1853 self.ui.warn(_("unable to load queue repository\n"))
1854 return 1
1854 return 1
1855 hg.clean(r, qpp[0])
1855 hg.clean(r, qpp[0])
1856
1856
1857 def save(self, repo, msg=None):
1857 def save(self, repo, msg=None):
1858 if not self.applied:
1858 if not self.applied:
1859 self.ui.warn(_("save: no patches applied, exiting\n"))
1859 self.ui.warn(_("save: no patches applied, exiting\n"))
1860 return 1
1860 return 1
1861 if self.issaveline(self.applied[-1]):
1861 if self.issaveline(self.applied[-1]):
1862 self.ui.warn(_("status is already saved\n"))
1862 self.ui.warn(_("status is already saved\n"))
1863 return 1
1863 return 1
1864
1864
1865 if not msg:
1865 if not msg:
1866 msg = _("hg patches saved state")
1866 msg = _("hg patches saved state")
1867 else:
1867 else:
1868 msg = "hg patches: " + msg.rstrip('\r\n')
1868 msg = "hg patches: " + msg.rstrip('\r\n')
1869 r = self.qrepo()
1869 r = self.qrepo()
1870 if r:
1870 if r:
1871 pp = r.dirstate.parents()
1871 pp = r.dirstate.parents()
1872 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1872 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1873 msg += "\n\nPatch Data:\n"
1873 msg += "\n\nPatch Data:\n"
1874 msg += ''.join('%s\n' % x for x in self.applied)
1874 msg += ''.join('%s\n' % x for x in self.applied)
1875 msg += ''.join(':%s\n' % x for x in self.fullseries)
1875 msg += ''.join(':%s\n' % x for x in self.fullseries)
1876 n = repo.commit(msg, force=True)
1876 n = repo.commit(msg, force=True)
1877 if not n:
1877 if not n:
1878 self.ui.warn(_("repo commit failed\n"))
1878 self.ui.warn(_("repo commit failed\n"))
1879 return 1
1879 return 1
1880 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1880 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1881 self.applieddirty = True
1881 self.applieddirty = True
1882 self.removeundo(repo)
1882 self.removeundo(repo)
1883
1883
1884 def fullseriesend(self):
1884 def fullseriesend(self):
1885 if self.applied:
1885 if self.applied:
1886 p = self.applied[-1].name
1886 p = self.applied[-1].name
1887 end = self.findseries(p)
1887 end = self.findseries(p)
1888 if end is None:
1888 if end is None:
1889 return len(self.fullseries)
1889 return len(self.fullseries)
1890 return end + 1
1890 return end + 1
1891 return 0
1891 return 0
1892
1892
1893 def seriesend(self, all_patches=False):
1893 def seriesend(self, all_patches=False):
1894 """If all_patches is False, return the index of the next pushable patch
1894 """If all_patches is False, return the index of the next pushable patch
1895 in the series, or the series length. If all_patches is True, return the
1895 in the series, or the series length. If all_patches is True, return the
1896 index of the first patch past the last applied one.
1896 index of the first patch past the last applied one.
1897 """
1897 """
1898 end = 0
1898 end = 0
1899 def nextpatch(start):
1899 def nextpatch(start):
1900 if all_patches or start >= len(self.series):
1900 if all_patches or start >= len(self.series):
1901 return start
1901 return start
1902 for i in xrange(start, len(self.series)):
1902 for i in xrange(start, len(self.series)):
1903 p, reason = self.pushable(i)
1903 p, reason = self.pushable(i)
1904 if p:
1904 if p:
1905 return i
1905 return i
1906 self.explainpushable(i)
1906 self.explainpushable(i)
1907 return len(self.series)
1907 return len(self.series)
1908 if self.applied:
1908 if self.applied:
1909 p = self.applied[-1].name
1909 p = self.applied[-1].name
1910 try:
1910 try:
1911 end = self.series.index(p)
1911 end = self.series.index(p)
1912 except ValueError:
1912 except ValueError:
1913 return 0
1913 return 0
1914 return nextpatch(end + 1)
1914 return nextpatch(end + 1)
1915 return nextpatch(end)
1915 return nextpatch(end)
1916
1916
1917 def appliedname(self, index):
1917 def appliedname(self, index):
1918 pname = self.applied[index].name
1918 pname = self.applied[index].name
1919 if not self.ui.verbose:
1919 if not self.ui.verbose:
1920 p = pname
1920 p = pname
1921 else:
1921 else:
1922 p = str(self.series.index(pname)) + " " + pname
1922 p = str(self.series.index(pname)) + " " + pname
1923 return p
1923 return p
1924
1924
1925 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1925 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1926 force=None, git=False):
1926 force=None, git=False):
1927 def checkseries(patchname):
1927 def checkseries(patchname):
1928 if patchname in self.series:
1928 if patchname in self.series:
1929 raise util.Abort(_('patch %s is already in the series file')
1929 raise util.Abort(_('patch %s is already in the series file')
1930 % patchname)
1930 % patchname)
1931
1931
1932 if rev:
1932 if rev:
1933 if files:
1933 if files:
1934 raise util.Abort(_('option "-r" not valid when importing '
1934 raise util.Abort(_('option "-r" not valid when importing '
1935 'files'))
1935 'files'))
1936 rev = scmutil.revrange(repo, rev)
1936 rev = scmutil.revrange(repo, rev)
1937 rev.sort(reverse=True)
1937 rev.sort(reverse=True)
1938 elif not files:
1938 elif not files:
1939 raise util.Abort(_('no files or revisions specified'))
1939 raise util.Abort(_('no files or revisions specified'))
1940 if (len(files) > 1 or len(rev) > 1) and patchname:
1940 if (len(files) > 1 or len(rev) > 1) and patchname:
1941 raise util.Abort(_('option "-n" not valid when importing multiple '
1941 raise util.Abort(_('option "-n" not valid when importing multiple '
1942 'patches'))
1942 'patches'))
1943 imported = []
1943 imported = []
1944 if rev:
1944 if rev:
1945 # If mq patches are applied, we can only import revisions
1945 # If mq patches are applied, we can only import revisions
1946 # that form a linear path to qbase.
1946 # that form a linear path to qbase.
1947 # Otherwise, they should form a linear path to a head.
1947 # Otherwise, they should form a linear path to a head.
1948 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1948 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1949 if len(heads) > 1:
1949 if len(heads) > 1:
1950 raise util.Abort(_('revision %d is the root of more than one '
1950 raise util.Abort(_('revision %d is the root of more than one '
1951 'branch') % rev[-1])
1951 'branch') % rev[-1])
1952 if self.applied:
1952 if self.applied:
1953 base = repo.changelog.node(rev[0])
1953 base = repo.changelog.node(rev[0])
1954 if base in [n.node for n in self.applied]:
1954 if base in [n.node for n in self.applied]:
1955 raise util.Abort(_('revision %d is already managed')
1955 raise util.Abort(_('revision %d is already managed')
1956 % rev[0])
1956 % rev[0])
1957 if heads != [self.applied[-1].node]:
1957 if heads != [self.applied[-1].node]:
1958 raise util.Abort(_('revision %d is not the parent of '
1958 raise util.Abort(_('revision %d is not the parent of '
1959 'the queue') % rev[0])
1959 'the queue') % rev[0])
1960 base = repo.changelog.rev(self.applied[0].node)
1960 base = repo.changelog.rev(self.applied[0].node)
1961 lastparent = repo.changelog.parentrevs(base)[0]
1961 lastparent = repo.changelog.parentrevs(base)[0]
1962 else:
1962 else:
1963 if heads != [repo.changelog.node(rev[0])]:
1963 if heads != [repo.changelog.node(rev[0])]:
1964 raise util.Abort(_('revision %d has unmanaged children')
1964 raise util.Abort(_('revision %d has unmanaged children')
1965 % rev[0])
1965 % rev[0])
1966 lastparent = None
1966 lastparent = None
1967
1967
1968 diffopts = self.diffopts({'git': git})
1968 diffopts = self.diffopts({'git': git})
1969 for r in rev:
1969 for r in rev:
1970 if not repo[r].mutable():
1970 if not repo[r].mutable():
1971 raise util.Abort(_('revision %d is not mutable') % r,
1971 raise util.Abort(_('revision %d is not mutable') % r,
1972 hint=_('see "hg help phases" for details'))
1972 hint=_('see "hg help phases" for details'))
1973 p1, p2 = repo.changelog.parentrevs(r)
1973 p1, p2 = repo.changelog.parentrevs(r)
1974 n = repo.changelog.node(r)
1974 n = repo.changelog.node(r)
1975 if p2 != nullrev:
1975 if p2 != nullrev:
1976 raise util.Abort(_('cannot import merge revision %d') % r)
1976 raise util.Abort(_('cannot import merge revision %d') % r)
1977 if lastparent and lastparent != r:
1977 if lastparent and lastparent != r:
1978 raise util.Abort(_('revision %d is not the parent of %d')
1978 raise util.Abort(_('revision %d is not the parent of %d')
1979 % (r, lastparent))
1979 % (r, lastparent))
1980 lastparent = p1
1980 lastparent = p1
1981
1981
1982 if not patchname:
1982 if not patchname:
1983 patchname = normname('%d.diff' % r)
1983 patchname = normname('%d.diff' % r)
1984 checkseries(patchname)
1984 checkseries(patchname)
1985 self.checkpatchname(patchname, force)
1985 self.checkpatchname(patchname, force)
1986 self.fullseries.insert(0, patchname)
1986 self.fullseries.insert(0, patchname)
1987
1987
1988 patchf = self.opener(patchname, "w")
1988 patchf = self.opener(patchname, "w")
1989 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1989 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1990 patchf.close()
1990 patchf.close()
1991
1991
1992 se = statusentry(n, patchname)
1992 se = statusentry(n, patchname)
1993 self.applied.insert(0, se)
1993 self.applied.insert(0, se)
1994
1994
1995 self.added.append(patchname)
1995 self.added.append(patchname)
1996 imported.append(patchname)
1996 imported.append(patchname)
1997 patchname = None
1997 patchname = None
1998 if rev and repo.ui.configbool('mq', 'secret', False):
1998 if rev and repo.ui.configbool('mq', 'secret', False):
1999 # if we added anything with --rev, we must move the secret root
1999 # if we added anything with --rev, move the secret root
2000 phases.retractboundary(repo, phases.secret, [n])
2000 phases.retractboundary(repo, phases.secret, [n])
2001 self.parseseries()
2001 self.parseseries()
2002 self.applieddirty = True
2002 self.applieddirty = True
2003 self.seriesdirty = True
2003 self.seriesdirty = True
2004
2004
2005 for i, filename in enumerate(files):
2005 for i, filename in enumerate(files):
2006 if existing:
2006 if existing:
2007 if filename == '-':
2007 if filename == '-':
2008 raise util.Abort(_('-e is incompatible with import from -'))
2008 raise util.Abort(_('-e is incompatible with import from -'))
2009 filename = normname(filename)
2009 filename = normname(filename)
2010 self.checkreservedname(filename)
2010 self.checkreservedname(filename)
2011 if util.url(filename).islocal():
2011 if util.url(filename).islocal():
2012 originpath = self.join(filename)
2012 originpath = self.join(filename)
2013 if not os.path.isfile(originpath):
2013 if not os.path.isfile(originpath):
2014 raise util.Abort(
2014 raise util.Abort(
2015 _("patch %s does not exist") % filename)
2015 _("patch %s does not exist") % filename)
2016
2016
2017 if patchname:
2017 if patchname:
2018 self.checkpatchname(patchname, force)
2018 self.checkpatchname(patchname, force)
2019
2019
2020 self.ui.write(_('renaming %s to %s\n')
2020 self.ui.write(_('renaming %s to %s\n')
2021 % (filename, patchname))
2021 % (filename, patchname))
2022 util.rename(originpath, self.join(patchname))
2022 util.rename(originpath, self.join(patchname))
2023 else:
2023 else:
2024 patchname = filename
2024 patchname = filename
2025
2025
2026 else:
2026 else:
2027 if filename == '-' and not patchname:
2027 if filename == '-' and not patchname:
2028 raise util.Abort(_('need --name to import a patch from -'))
2028 raise util.Abort(_('need --name to import a patch from -'))
2029 elif not patchname:
2029 elif not patchname:
2030 patchname = normname(os.path.basename(filename.rstrip('/')))
2030 patchname = normname(os.path.basename(filename.rstrip('/')))
2031 self.checkpatchname(patchname, force)
2031 self.checkpatchname(patchname, force)
2032 try:
2032 try:
2033 if filename == '-':
2033 if filename == '-':
2034 text = self.ui.fin.read()
2034 text = self.ui.fin.read()
2035 else:
2035 else:
2036 fp = hg.openpath(self.ui, filename)
2036 fp = hg.openpath(self.ui, filename)
2037 text = fp.read()
2037 text = fp.read()
2038 fp.close()
2038 fp.close()
2039 except (OSError, IOError):
2039 except (OSError, IOError):
2040 raise util.Abort(_("unable to read file %s") % filename)
2040 raise util.Abort(_("unable to read file %s") % filename)
2041 patchf = self.opener(patchname, "w")
2041 patchf = self.opener(patchname, "w")
2042 patchf.write(text)
2042 patchf.write(text)
2043 patchf.close()
2043 patchf.close()
2044 if not force:
2044 if not force:
2045 checkseries(patchname)
2045 checkseries(patchname)
2046 if patchname not in self.series:
2046 if patchname not in self.series:
2047 index = self.fullseriesend() + i
2047 index = self.fullseriesend() + i
2048 self.fullseries[index:index] = [patchname]
2048 self.fullseries[index:index] = [patchname]
2049 self.parseseries()
2049 self.parseseries()
2050 self.seriesdirty = True
2050 self.seriesdirty = True
2051 self.ui.warn(_("adding %s to series file\n") % patchname)
2051 self.ui.warn(_("adding %s to series file\n") % patchname)
2052 self.added.append(patchname)
2052 self.added.append(patchname)
2053 imported.append(patchname)
2053 imported.append(patchname)
2054 patchname = None
2054 patchname = None
2055
2055
2056 self.removeundo(repo)
2056 self.removeundo(repo)
2057 return imported
2057 return imported
2058
2058
2059 def fixkeepchangesopts(ui, opts):
2059 def fixkeepchangesopts(ui, opts):
2060 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2060 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2061 or opts.get('exact')):
2061 or opts.get('exact')):
2062 return opts
2062 return opts
2063 opts = dict(opts)
2063 opts = dict(opts)
2064 opts['keep_changes'] = True
2064 opts['keep_changes'] = True
2065 return opts
2065 return opts
2066
2066
2067 @command("qdelete|qremove|qrm",
2067 @command("qdelete|qremove|qrm",
2068 [('k', 'keep', None, _('keep patch file')),
2068 [('k', 'keep', None, _('keep patch file')),
2069 ('r', 'rev', [],
2069 ('r', 'rev', [],
2070 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2070 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2071 _('hg qdelete [-k] [PATCH]...'))
2071 _('hg qdelete [-k] [PATCH]...'))
2072 def delete(ui, repo, *patches, **opts):
2072 def delete(ui, repo, *patches, **opts):
2073 """remove patches from queue
2073 """remove patches from queue
2074
2074
2075 The patches must not be applied, and at least one patch is required. Exact
2075 The patches must not be applied, and at least one patch is required. Exact
2076 patch identifiers must be given. With -k/--keep, the patch files are
2076 patch identifiers must be given. With -k/--keep, the patch files are
2077 preserved in the patch directory.
2077 preserved in the patch directory.
2078
2078
2079 To stop managing a patch and move it into permanent history,
2079 To stop managing a patch and move it into permanent history,
2080 use the :hg:`qfinish` command."""
2080 use the :hg:`qfinish` command."""
2081 q = repo.mq
2081 q = repo.mq
2082 q.delete(repo, patches, opts)
2082 q.delete(repo, patches, opts)
2083 q.savedirty()
2083 q.savedirty()
2084 return 0
2084 return 0
2085
2085
2086 @command("qapplied",
2086 @command("qapplied",
2087 [('1', 'last', None, _('show only the preceding applied patch'))
2087 [('1', 'last', None, _('show only the preceding applied patch'))
2088 ] + seriesopts,
2088 ] + seriesopts,
2089 _('hg qapplied [-1] [-s] [PATCH]'))
2089 _('hg qapplied [-1] [-s] [PATCH]'))
2090 def applied(ui, repo, patch=None, **opts):
2090 def applied(ui, repo, patch=None, **opts):
2091 """print the patches already applied
2091 """print the patches already applied
2092
2092
2093 Returns 0 on success."""
2093 Returns 0 on success."""
2094
2094
2095 q = repo.mq
2095 q = repo.mq
2096
2096
2097 if patch:
2097 if patch:
2098 if patch not in q.series:
2098 if patch not in q.series:
2099 raise util.Abort(_("patch %s is not in series file") % patch)
2099 raise util.Abort(_("patch %s is not in series file") % patch)
2100 end = q.series.index(patch) + 1
2100 end = q.series.index(patch) + 1
2101 else:
2101 else:
2102 end = q.seriesend(True)
2102 end = q.seriesend(True)
2103
2103
2104 if opts.get('last') and not end:
2104 if opts.get('last') and not end:
2105 ui.write(_("no patches applied\n"))
2105 ui.write(_("no patches applied\n"))
2106 return 1
2106 return 1
2107 elif opts.get('last') and end == 1:
2107 elif opts.get('last') and end == 1:
2108 ui.write(_("only one patch applied\n"))
2108 ui.write(_("only one patch applied\n"))
2109 return 1
2109 return 1
2110 elif opts.get('last'):
2110 elif opts.get('last'):
2111 start = end - 2
2111 start = end - 2
2112 end = 1
2112 end = 1
2113 else:
2113 else:
2114 start = 0
2114 start = 0
2115
2115
2116 q.qseries(repo, length=end, start=start, status='A',
2116 q.qseries(repo, length=end, start=start, status='A',
2117 summary=opts.get('summary'))
2117 summary=opts.get('summary'))
2118
2118
2119
2119
2120 @command("qunapplied",
2120 @command("qunapplied",
2121 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2121 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2122 _('hg qunapplied [-1] [-s] [PATCH]'))
2122 _('hg qunapplied [-1] [-s] [PATCH]'))
2123 def unapplied(ui, repo, patch=None, **opts):
2123 def unapplied(ui, repo, patch=None, **opts):
2124 """print the patches not yet applied
2124 """print the patches not yet applied
2125
2125
2126 Returns 0 on success."""
2126 Returns 0 on success."""
2127
2127
2128 q = repo.mq
2128 q = repo.mq
2129 if patch:
2129 if patch:
2130 if patch not in q.series:
2130 if patch not in q.series:
2131 raise util.Abort(_("patch %s is not in series file") % patch)
2131 raise util.Abort(_("patch %s is not in series file") % patch)
2132 start = q.series.index(patch) + 1
2132 start = q.series.index(patch) + 1
2133 else:
2133 else:
2134 start = q.seriesend(True)
2134 start = q.seriesend(True)
2135
2135
2136 if start == len(q.series) and opts.get('first'):
2136 if start == len(q.series) and opts.get('first'):
2137 ui.write(_("all patches applied\n"))
2137 ui.write(_("all patches applied\n"))
2138 return 1
2138 return 1
2139
2139
2140 length = opts.get('first') and 1 or None
2140 length = opts.get('first') and 1 or None
2141 q.qseries(repo, start=start, length=length, status='U',
2141 q.qseries(repo, start=start, length=length, status='U',
2142 summary=opts.get('summary'))
2142 summary=opts.get('summary'))
2143
2143
2144 @command("qimport",
2144 @command("qimport",
2145 [('e', 'existing', None, _('import file in patch directory')),
2145 [('e', 'existing', None, _('import file in patch directory')),
2146 ('n', 'name', '',
2146 ('n', 'name', '',
2147 _('name of patch file'), _('NAME')),
2147 _('name of patch file'), _('NAME')),
2148 ('f', 'force', None, _('overwrite existing files')),
2148 ('f', 'force', None, _('overwrite existing files')),
2149 ('r', 'rev', [],
2149 ('r', 'rev', [],
2150 _('place existing revisions under mq control'), _('REV')),
2150 _('place existing revisions under mq control'), _('REV')),
2151 ('g', 'git', None, _('use git extended diff format')),
2151 ('g', 'git', None, _('use git extended diff format')),
2152 ('P', 'push', None, _('qpush after importing'))],
2152 ('P', 'push', None, _('qpush after importing'))],
2153 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2153 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2154 def qimport(ui, repo, *filename, **opts):
2154 def qimport(ui, repo, *filename, **opts):
2155 """import a patch or existing changeset
2155 """import a patch or existing changeset
2156
2156
2157 The patch is inserted into the series after the last applied
2157 The patch is inserted into the series after the last applied
2158 patch. If no patches have been applied, qimport prepends the patch
2158 patch. If no patches have been applied, qimport prepends the patch
2159 to the series.
2159 to the series.
2160
2160
2161 The patch will have the same name as its source file unless you
2161 The patch will have the same name as its source file unless you
2162 give it a new one with -n/--name.
2162 give it a new one with -n/--name.
2163
2163
2164 You can register an existing patch inside the patch directory with
2164 You can register an existing patch inside the patch directory with
2165 the -e/--existing flag.
2165 the -e/--existing flag.
2166
2166
2167 With -f/--force, an existing patch of the same name will be
2167 With -f/--force, an existing patch of the same name will be
2168 overwritten.
2168 overwritten.
2169
2169
2170 An existing changeset may be placed under mq control with -r/--rev
2170 An existing changeset may be placed under mq control with -r/--rev
2171 (e.g. qimport --rev . -n patch will place the current revision
2171 (e.g. qimport --rev . -n patch will place the current revision
2172 under mq control). With -g/--git, patches imported with --rev will
2172 under mq control). With -g/--git, patches imported with --rev will
2173 use the git diff format. See the diffs help topic for information
2173 use the git diff format. See the diffs help topic for information
2174 on why this is important for preserving rename/copy information
2174 on why this is important for preserving rename/copy information
2175 and permission changes. Use :hg:`qfinish` to remove changesets
2175 and permission changes. Use :hg:`qfinish` to remove changesets
2176 from mq control.
2176 from mq control.
2177
2177
2178 To import a patch from standard input, pass - as the patch file.
2178 To import a patch from standard input, pass - as the patch file.
2179 When importing from standard input, a patch name must be specified
2179 When importing from standard input, a patch name must be specified
2180 using the --name flag.
2180 using the --name flag.
2181
2181
2182 To import an existing patch while renaming it::
2182 To import an existing patch while renaming it::
2183
2183
2184 hg qimport -e existing-patch -n new-name
2184 hg qimport -e existing-patch -n new-name
2185
2185
2186 Returns 0 if import succeeded.
2186 Returns 0 if import succeeded.
2187 """
2187 """
2188 lock = repo.lock() # cause this may move phase
2188 lock = repo.lock() # cause this may move phase
2189 try:
2189 try:
2190 q = repo.mq
2190 q = repo.mq
2191 try:
2191 try:
2192 imported = q.qimport(
2192 imported = q.qimport(
2193 repo, filename, patchname=opts.get('name'),
2193 repo, filename, patchname=opts.get('name'),
2194 existing=opts.get('existing'), force=opts.get('force'),
2194 existing=opts.get('existing'), force=opts.get('force'),
2195 rev=opts.get('rev'), git=opts.get('git'))
2195 rev=opts.get('rev'), git=opts.get('git'))
2196 finally:
2196 finally:
2197 q.savedirty()
2197 q.savedirty()
2198 finally:
2198 finally:
2199 lock.release()
2199 lock.release()
2200
2200
2201 if imported and opts.get('push') and not opts.get('rev'):
2201 if imported and opts.get('push') and not opts.get('rev'):
2202 return q.push(repo, imported[-1])
2202 return q.push(repo, imported[-1])
2203 return 0
2203 return 0
2204
2204
2205 def qinit(ui, repo, create):
2205 def qinit(ui, repo, create):
2206 """initialize a new queue repository
2206 """initialize a new queue repository
2207
2207
2208 This command also creates a series file for ordering patches, and
2208 This command also creates a series file for ordering patches, and
2209 an mq-specific .hgignore file in the queue repository, to exclude
2209 an mq-specific .hgignore file in the queue repository, to exclude
2210 the status and guards files (these contain mostly transient state).
2210 the status and guards files (these contain mostly transient state).
2211
2211
2212 Returns 0 if initialization succeeded."""
2212 Returns 0 if initialization succeeded."""
2213 q = repo.mq
2213 q = repo.mq
2214 r = q.init(repo, create)
2214 r = q.init(repo, create)
2215 q.savedirty()
2215 q.savedirty()
2216 if r:
2216 if r:
2217 if not os.path.exists(r.wjoin('.hgignore')):
2217 if not os.path.exists(r.wjoin('.hgignore')):
2218 fp = r.wopener('.hgignore', 'w')
2218 fp = r.wopener('.hgignore', 'w')
2219 fp.write('^\\.hg\n')
2219 fp.write('^\\.hg\n')
2220 fp.write('^\\.mq\n')
2220 fp.write('^\\.mq\n')
2221 fp.write('syntax: glob\n')
2221 fp.write('syntax: glob\n')
2222 fp.write('status\n')
2222 fp.write('status\n')
2223 fp.write('guards\n')
2223 fp.write('guards\n')
2224 fp.close()
2224 fp.close()
2225 if not os.path.exists(r.wjoin('series')):
2225 if not os.path.exists(r.wjoin('series')):
2226 r.wopener('series', 'w').close()
2226 r.wopener('series', 'w').close()
2227 r[None].add(['.hgignore', 'series'])
2227 r[None].add(['.hgignore', 'series'])
2228 commands.add(ui, r)
2228 commands.add(ui, r)
2229 return 0
2229 return 0
2230
2230
2231 @command("^qinit",
2231 @command("^qinit",
2232 [('c', 'create-repo', None, _('create queue repository'))],
2232 [('c', 'create-repo', None, _('create queue repository'))],
2233 _('hg qinit [-c]'))
2233 _('hg qinit [-c]'))
2234 def init(ui, repo, **opts):
2234 def init(ui, repo, **opts):
2235 """init a new queue repository (DEPRECATED)
2235 """init a new queue repository (DEPRECATED)
2236
2236
2237 The queue repository is unversioned by default. If
2237 The queue repository is unversioned by default. If
2238 -c/--create-repo is specified, qinit will create a separate nested
2238 -c/--create-repo is specified, qinit will create a separate nested
2239 repository for patches (qinit -c may also be run later to convert
2239 repository for patches (qinit -c may also be run later to convert
2240 an unversioned patch repository into a versioned one). You can use
2240 an unversioned patch repository into a versioned one). You can use
2241 qcommit to commit changes to this queue repository.
2241 qcommit to commit changes to this queue repository.
2242
2242
2243 This command is deprecated. Without -c, it's implied by other relevant
2243 This command is deprecated. Without -c, it's implied by other relevant
2244 commands. With -c, use :hg:`init --mq` instead."""
2244 commands. With -c, use :hg:`init --mq` instead."""
2245 return qinit(ui, repo, create=opts.get('create_repo'))
2245 return qinit(ui, repo, create=opts.get('create_repo'))
2246
2246
2247 @command("qclone",
2247 @command("qclone",
2248 [('', 'pull', None, _('use pull protocol to copy metadata')),
2248 [('', 'pull', None, _('use pull protocol to copy metadata')),
2249 ('U', 'noupdate', None,
2249 ('U', 'noupdate', None,
2250 _('do not update the new working directories')),
2250 _('do not update the new working directories')),
2251 ('', 'uncompressed', None,
2251 ('', 'uncompressed', None,
2252 _('use uncompressed transfer (fast over LAN)')),
2252 _('use uncompressed transfer (fast over LAN)')),
2253 ('p', 'patches', '',
2253 ('p', 'patches', '',
2254 _('location of source patch repository'), _('REPO')),
2254 _('location of source patch repository'), _('REPO')),
2255 ] + commands.remoteopts,
2255 ] + commands.remoteopts,
2256 _('hg qclone [OPTION]... SOURCE [DEST]'),
2256 _('hg qclone [OPTION]... SOURCE [DEST]'),
2257 norepo=True)
2257 norepo=True)
2258 def clone(ui, source, dest=None, **opts):
2258 def clone(ui, source, dest=None, **opts):
2259 '''clone main and patch repository at same time
2259 '''clone main and patch repository at same time
2260
2260
2261 If source is local, destination will have no patches applied. If
2261 If source is local, destination will have no patches applied. If
2262 source is remote, this command can not check if patches are
2262 source is remote, this command can not check if patches are
2263 applied in source, so cannot guarantee that patches are not
2263 applied in source, so cannot guarantee that patches are not
2264 applied in destination. If you clone remote repository, be sure
2264 applied in destination. If you clone remote repository, be sure
2265 before that it has no patches applied.
2265 before that it has no patches applied.
2266
2266
2267 Source patch repository is looked for in <src>/.hg/patches by
2267 Source patch repository is looked for in <src>/.hg/patches by
2268 default. Use -p <url> to change.
2268 default. Use -p <url> to change.
2269
2269
2270 The patch directory must be a nested Mercurial repository, as
2270 The patch directory must be a nested Mercurial repository, as
2271 would be created by :hg:`init --mq`.
2271 would be created by :hg:`init --mq`.
2272
2272
2273 Return 0 on success.
2273 Return 0 on success.
2274 '''
2274 '''
2275 def patchdir(repo):
2275 def patchdir(repo):
2276 """compute a patch repo url from a repo object"""
2276 """compute a patch repo url from a repo object"""
2277 url = repo.url()
2277 url = repo.url()
2278 if url.endswith('/'):
2278 if url.endswith('/'):
2279 url = url[:-1]
2279 url = url[:-1]
2280 return url + '/.hg/patches'
2280 return url + '/.hg/patches'
2281
2281
2282 # main repo (destination and sources)
2282 # main repo (destination and sources)
2283 if dest is None:
2283 if dest is None:
2284 dest = hg.defaultdest(source)
2284 dest = hg.defaultdest(source)
2285 sr = hg.peer(ui, opts, ui.expandpath(source))
2285 sr = hg.peer(ui, opts, ui.expandpath(source))
2286
2286
2287 # patches repo (source only)
2287 # patches repo (source only)
2288 if opts.get('patches'):
2288 if opts.get('patches'):
2289 patchespath = ui.expandpath(opts.get('patches'))
2289 patchespath = ui.expandpath(opts.get('patches'))
2290 else:
2290 else:
2291 patchespath = patchdir(sr)
2291 patchespath = patchdir(sr)
2292 try:
2292 try:
2293 hg.peer(ui, opts, patchespath)
2293 hg.peer(ui, opts, patchespath)
2294 except error.RepoError:
2294 except error.RepoError:
2295 raise util.Abort(_('versioned patch repository not found'
2295 raise util.Abort(_('versioned patch repository not found'
2296 ' (see init --mq)'))
2296 ' (see init --mq)'))
2297 qbase, destrev = None, None
2297 qbase, destrev = None, None
2298 if sr.local():
2298 if sr.local():
2299 repo = sr.local()
2299 repo = sr.local()
2300 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2300 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2301 qbase = repo.mq.applied[0].node
2301 qbase = repo.mq.applied[0].node
2302 if not hg.islocal(dest):
2302 if not hg.islocal(dest):
2303 heads = set(repo.heads())
2303 heads = set(repo.heads())
2304 destrev = list(heads.difference(repo.heads(qbase)))
2304 destrev = list(heads.difference(repo.heads(qbase)))
2305 destrev.append(repo.changelog.parents(qbase)[0])
2305 destrev.append(repo.changelog.parents(qbase)[0])
2306 elif sr.capable('lookup'):
2306 elif sr.capable('lookup'):
2307 try:
2307 try:
2308 qbase = sr.lookup('qbase')
2308 qbase = sr.lookup('qbase')
2309 except error.RepoError:
2309 except error.RepoError:
2310 pass
2310 pass
2311
2311
2312 ui.note(_('cloning main repository\n'))
2312 ui.note(_('cloning main repository\n'))
2313 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2313 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2314 pull=opts.get('pull'),
2314 pull=opts.get('pull'),
2315 rev=destrev,
2315 rev=destrev,
2316 update=False,
2316 update=False,
2317 stream=opts.get('uncompressed'))
2317 stream=opts.get('uncompressed'))
2318
2318
2319 ui.note(_('cloning patch repository\n'))
2319 ui.note(_('cloning patch repository\n'))
2320 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2320 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2321 pull=opts.get('pull'), update=not opts.get('noupdate'),
2321 pull=opts.get('pull'), update=not opts.get('noupdate'),
2322 stream=opts.get('uncompressed'))
2322 stream=opts.get('uncompressed'))
2323
2323
2324 if dr.local():
2324 if dr.local():
2325 repo = dr.local()
2325 repo = dr.local()
2326 if qbase:
2326 if qbase:
2327 ui.note(_('stripping applied patches from destination '
2327 ui.note(_('stripping applied patches from destination '
2328 'repository\n'))
2328 'repository\n'))
2329 strip(ui, repo, [qbase], update=False, backup=None)
2329 strip(ui, repo, [qbase], update=False, backup=None)
2330 if not opts.get('noupdate'):
2330 if not opts.get('noupdate'):
2331 ui.note(_('updating destination repository\n'))
2331 ui.note(_('updating destination repository\n'))
2332 hg.update(repo, repo.changelog.tip())
2332 hg.update(repo, repo.changelog.tip())
2333
2333
2334 @command("qcommit|qci",
2334 @command("qcommit|qci",
2335 commands.table["^commit|ci"][1],
2335 commands.table["^commit|ci"][1],
2336 _('hg qcommit [OPTION]... [FILE]...'),
2336 _('hg qcommit [OPTION]... [FILE]...'),
2337 inferrepo=True)
2337 inferrepo=True)
2338 def commit(ui, repo, *pats, **opts):
2338 def commit(ui, repo, *pats, **opts):
2339 """commit changes in the queue repository (DEPRECATED)
2339 """commit changes in the queue repository (DEPRECATED)
2340
2340
2341 This command is deprecated; use :hg:`commit --mq` instead."""
2341 This command is deprecated; use :hg:`commit --mq` instead."""
2342 q = repo.mq
2342 q = repo.mq
2343 r = q.qrepo()
2343 r = q.qrepo()
2344 if not r:
2344 if not r:
2345 raise util.Abort('no queue repository')
2345 raise util.Abort('no queue repository')
2346 commands.commit(r.ui, r, *pats, **opts)
2346 commands.commit(r.ui, r, *pats, **opts)
2347
2347
2348 @command("qseries",
2348 @command("qseries",
2349 [('m', 'missing', None, _('print patches not in series')),
2349 [('m', 'missing', None, _('print patches not in series')),
2350 ] + seriesopts,
2350 ] + seriesopts,
2351 _('hg qseries [-ms]'))
2351 _('hg qseries [-ms]'))
2352 def series(ui, repo, **opts):
2352 def series(ui, repo, **opts):
2353 """print the entire series file
2353 """print the entire series file
2354
2354
2355 Returns 0 on success."""
2355 Returns 0 on success."""
2356 repo.mq.qseries(repo, missing=opts.get('missing'),
2356 repo.mq.qseries(repo, missing=opts.get('missing'),
2357 summary=opts.get('summary'))
2357 summary=opts.get('summary'))
2358 return 0
2358 return 0
2359
2359
2360 @command("qtop", seriesopts, _('hg qtop [-s]'))
2360 @command("qtop", seriesopts, _('hg qtop [-s]'))
2361 def top(ui, repo, **opts):
2361 def top(ui, repo, **opts):
2362 """print the name of the current patch
2362 """print the name of the current patch
2363
2363
2364 Returns 0 on success."""
2364 Returns 0 on success."""
2365 q = repo.mq
2365 q = repo.mq
2366 t = q.applied and q.seriesend(True) or 0
2366 t = q.applied and q.seriesend(True) or 0
2367 if t:
2367 if t:
2368 q.qseries(repo, start=t - 1, length=1, status='A',
2368 q.qseries(repo, start=t - 1, length=1, status='A',
2369 summary=opts.get('summary'))
2369 summary=opts.get('summary'))
2370 else:
2370 else:
2371 ui.write(_("no patches applied\n"))
2371 ui.write(_("no patches applied\n"))
2372 return 1
2372 return 1
2373
2373
2374 @command("qnext", seriesopts, _('hg qnext [-s]'))
2374 @command("qnext", seriesopts, _('hg qnext [-s]'))
2375 def next(ui, repo, **opts):
2375 def next(ui, repo, **opts):
2376 """print the name of the next pushable patch
2376 """print the name of the next pushable patch
2377
2377
2378 Returns 0 on success."""
2378 Returns 0 on success."""
2379 q = repo.mq
2379 q = repo.mq
2380 end = q.seriesend()
2380 end = q.seriesend()
2381 if end == len(q.series):
2381 if end == len(q.series):
2382 ui.write(_("all patches applied\n"))
2382 ui.write(_("all patches applied\n"))
2383 return 1
2383 return 1
2384 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2384 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2385
2385
2386 @command("qprev", seriesopts, _('hg qprev [-s]'))
2386 @command("qprev", seriesopts, _('hg qprev [-s]'))
2387 def prev(ui, repo, **opts):
2387 def prev(ui, repo, **opts):
2388 """print the name of the preceding applied patch
2388 """print the name of the preceding applied patch
2389
2389
2390 Returns 0 on success."""
2390 Returns 0 on success."""
2391 q = repo.mq
2391 q = repo.mq
2392 l = len(q.applied)
2392 l = len(q.applied)
2393 if l == 1:
2393 if l == 1:
2394 ui.write(_("only one patch applied\n"))
2394 ui.write(_("only one patch applied\n"))
2395 return 1
2395 return 1
2396 if not l:
2396 if not l:
2397 ui.write(_("no patches applied\n"))
2397 ui.write(_("no patches applied\n"))
2398 return 1
2398 return 1
2399 idx = q.series.index(q.applied[-2].name)
2399 idx = q.series.index(q.applied[-2].name)
2400 q.qseries(repo, start=idx, length=1, status='A',
2400 q.qseries(repo, start=idx, length=1, status='A',
2401 summary=opts.get('summary'))
2401 summary=opts.get('summary'))
2402
2402
2403 def setupheaderopts(ui, opts):
2403 def setupheaderopts(ui, opts):
2404 if not opts.get('user') and opts.get('currentuser'):
2404 if not opts.get('user') and opts.get('currentuser'):
2405 opts['user'] = ui.username()
2405 opts['user'] = ui.username()
2406 if not opts.get('date') and opts.get('currentdate'):
2406 if not opts.get('date') and opts.get('currentdate'):
2407 opts['date'] = "%d %d" % util.makedate()
2407 opts['date'] = "%d %d" % util.makedate()
2408
2408
2409 @command("^qnew",
2409 @command("^qnew",
2410 [('e', 'edit', None, _('edit commit message')),
2410 [('e', 'edit', None, _('invoke editor on commit messages')),
2411 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2411 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2412 ('g', 'git', None, _('use git extended diff format')),
2412 ('g', 'git', None, _('use git extended diff format')),
2413 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2413 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2414 ('u', 'user', '',
2414 ('u', 'user', '',
2415 _('add "From: <USER>" to patch'), _('USER')),
2415 _('add "From: <USER>" to patch'), _('USER')),
2416 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2416 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2417 ('d', 'date', '',
2417 ('d', 'date', '',
2418 _('add "Date: <DATE>" to patch'), _('DATE'))
2418 _('add "Date: <DATE>" to patch'), _('DATE'))
2419 ] + commands.walkopts + commands.commitopts,
2419 ] + commands.walkopts + commands.commitopts,
2420 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'),
2420 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'),
2421 inferrepo=True)
2421 inferrepo=True)
2422 def new(ui, repo, patch, *args, **opts):
2422 def new(ui, repo, patch, *args, **opts):
2423 """create a new patch
2423 """create a new patch
2424
2424
2425 qnew creates a new patch on top of the currently-applied patch (if
2425 qnew creates a new patch on top of the currently-applied patch (if
2426 any). The patch will be initialized with any outstanding changes
2426 any). The patch will be initialized with any outstanding changes
2427 in the working directory. You may also use -I/--include,
2427 in the working directory. You may also use -I/--include,
2428 -X/--exclude, and/or a list of files after the patch name to add
2428 -X/--exclude, and/or a list of files after the patch name to add
2429 only changes to matching files to the new patch, leaving the rest
2429 only changes to matching files to the new patch, leaving the rest
2430 as uncommitted modifications.
2430 as uncommitted modifications.
2431
2431
2432 -u/--user and -d/--date can be used to set the (given) user and
2432 -u/--user and -d/--date can be used to set the (given) user and
2433 date, respectively. -U/--currentuser and -D/--currentdate set user
2433 date, respectively. -U/--currentuser and -D/--currentdate set user
2434 to current user and date to current date.
2434 to current user and date to current date.
2435
2435
2436 -e/--edit, -m/--message or -l/--logfile set the patch header as
2436 -e/--edit, -m/--message or -l/--logfile set the patch header as
2437 well as the commit message. If none is specified, the header is
2437 well as the commit message. If none is specified, the header is
2438 empty and the commit message is '[mq]: PATCH'.
2438 empty and the commit message is '[mq]: PATCH'.
2439
2439
2440 Use the -g/--git option to keep the patch in the git extended diff
2440 Use the -g/--git option to keep the patch in the git extended diff
2441 format. Read the diffs help topic for more information on why this
2441 format. Read the diffs help topic for more information on why this
2442 is important for preserving permission changes and copy/rename
2442 is important for preserving permission changes and copy/rename
2443 information.
2443 information.
2444
2444
2445 Returns 0 on successful creation of a new patch.
2445 Returns 0 on successful creation of a new patch.
2446 """
2446 """
2447 msg = cmdutil.logmessage(ui, opts)
2447 msg = cmdutil.logmessage(ui, opts)
2448 q = repo.mq
2448 q = repo.mq
2449 opts['msg'] = msg
2449 opts['msg'] = msg
2450 setupheaderopts(ui, opts)
2450 setupheaderopts(ui, opts)
2451 q.new(repo, patch, *args, **opts)
2451 q.new(repo, patch, *args, **opts)
2452 q.savedirty()
2452 q.savedirty()
2453 return 0
2453 return 0
2454
2454
2455 @command("^qrefresh",
2455 @command("^qrefresh",
2456 [('e', 'edit', None, _('edit commit message')),
2456 [('e', 'edit', None, _('invoke editor on commit messages')),
2457 ('g', 'git', None, _('use git extended diff format')),
2457 ('g', 'git', None, _('use git extended diff format')),
2458 ('s', 'short', None,
2458 ('s', 'short', None,
2459 _('refresh only files already in the patch and specified files')),
2459 _('refresh only files already in the patch and specified files')),
2460 ('U', 'currentuser', None,
2460 ('U', 'currentuser', None,
2461 _('add/update author field in patch with current user')),
2461 _('add/update author field in patch with current user')),
2462 ('u', 'user', '',
2462 ('u', 'user', '',
2463 _('add/update author field in patch with given user'), _('USER')),
2463 _('add/update author field in patch with given user'), _('USER')),
2464 ('D', 'currentdate', None,
2464 ('D', 'currentdate', None,
2465 _('add/update date field in patch with current date')),
2465 _('add/update date field in patch with current date')),
2466 ('d', 'date', '',
2466 ('d', 'date', '',
2467 _('add/update date field in patch with given date'), _('DATE'))
2467 _('add/update date field in patch with given date'), _('DATE'))
2468 ] + commands.walkopts + commands.commitopts,
2468 ] + commands.walkopts + commands.commitopts,
2469 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'),
2469 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'),
2470 inferrepo=True)
2470 inferrepo=True)
2471 def refresh(ui, repo, *pats, **opts):
2471 def refresh(ui, repo, *pats, **opts):
2472 """update the current patch
2472 """update the current patch
2473
2473
2474 If any file patterns are provided, the refreshed patch will
2474 If any file patterns are provided, the refreshed patch will
2475 contain only the modifications that match those patterns; the
2475 contain only the modifications that match those patterns; the
2476 remaining modifications will remain in the working directory.
2476 remaining modifications will remain in the working directory.
2477
2477
2478 If -s/--short is specified, files currently included in the patch
2478 If -s/--short is specified, files currently included in the patch
2479 will be refreshed just like matched files and remain in the patch.
2479 will be refreshed just like matched files and remain in the patch.
2480
2480
2481 If -e/--edit is specified, Mercurial will start your configured editor for
2481 If -e/--edit is specified, Mercurial will start your configured editor for
2482 you to enter a message. In case qrefresh fails, you will find a backup of
2482 you to enter a message. In case qrefresh fails, you will find a backup of
2483 your message in ``.hg/last-message.txt``.
2483 your message in ``.hg/last-message.txt``.
2484
2484
2485 hg add/remove/copy/rename work as usual, though you might want to
2485 hg add/remove/copy/rename work as usual, though you might want to
2486 use git-style patches (-g/--git or [diff] git=1) to track copies
2486 use git-style patches (-g/--git or [diff] git=1) to track copies
2487 and renames. See the diffs help topic for more information on the
2487 and renames. See the diffs help topic for more information on the
2488 git diff format.
2488 git diff format.
2489
2489
2490 Returns 0 on success.
2490 Returns 0 on success.
2491 """
2491 """
2492 q = repo.mq
2492 q = repo.mq
2493 message = cmdutil.logmessage(ui, opts)
2493 message = cmdutil.logmessage(ui, opts)
2494 setupheaderopts(ui, opts)
2494 setupheaderopts(ui, opts)
2495 wlock = repo.wlock()
2495 wlock = repo.wlock()
2496 try:
2496 try:
2497 ret = q.refresh(repo, pats, msg=message, **opts)
2497 ret = q.refresh(repo, pats, msg=message, **opts)
2498 q.savedirty()
2498 q.savedirty()
2499 return ret
2499 return ret
2500 finally:
2500 finally:
2501 wlock.release()
2501 wlock.release()
2502
2502
2503 @command("^qdiff",
2503 @command("^qdiff",
2504 commands.diffopts + commands.diffopts2 + commands.walkopts,
2504 commands.diffopts + commands.diffopts2 + commands.walkopts,
2505 _('hg qdiff [OPTION]... [FILE]...'),
2505 _('hg qdiff [OPTION]... [FILE]...'),
2506 inferrepo=True)
2506 inferrepo=True)
2507 def diff(ui, repo, *pats, **opts):
2507 def diff(ui, repo, *pats, **opts):
2508 """diff of the current patch and subsequent modifications
2508 """diff of the current patch and subsequent modifications
2509
2509
2510 Shows a diff which includes the current patch as well as any
2510 Shows a diff which includes the current patch as well as any
2511 changes which have been made in the working directory since the
2511 changes which have been made in the working directory since the
2512 last refresh (thus showing what the current patch would become
2512 last refresh (thus showing what the current patch would become
2513 after a qrefresh).
2513 after a qrefresh).
2514
2514
2515 Use :hg:`diff` if you only want to see the changes made since the
2515 Use :hg:`diff` if you only want to see the changes made since the
2516 last qrefresh, or :hg:`export qtip` if you want to see changes
2516 last qrefresh, or :hg:`export qtip` if you want to see changes
2517 made by the current patch without including changes made since the
2517 made by the current patch without including changes made since the
2518 qrefresh.
2518 qrefresh.
2519
2519
2520 Returns 0 on success.
2520 Returns 0 on success.
2521 """
2521 """
2522 repo.mq.diff(repo, pats, opts)
2522 repo.mq.diff(repo, pats, opts)
2523 return 0
2523 return 0
2524
2524
2525 @command('qfold',
2525 @command('qfold',
2526 [('e', 'edit', None, _('edit patch header')),
2526 [('e', 'edit', None, _('invoke editor on commit messages')),
2527 ('k', 'keep', None, _('keep folded patch files')),
2527 ('k', 'keep', None, _('keep folded patch files')),
2528 ] + commands.commitopts,
2528 ] + commands.commitopts,
2529 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2529 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2530 def fold(ui, repo, *files, **opts):
2530 def fold(ui, repo, *files, **opts):
2531 """fold the named patches into the current patch
2531 """fold the named patches into the current patch
2532
2532
2533 Patches must not yet be applied. Each patch will be successively
2533 Patches must not yet be applied. Each patch will be successively
2534 applied to the current patch in the order given. If all the
2534 applied to the current patch in the order given. If all the
2535 patches apply successfully, the current patch will be refreshed
2535 patches apply successfully, the current patch will be refreshed
2536 with the new cumulative patch, and the folded patches will be
2536 with the new cumulative patch, and the folded patches will be
2537 deleted. With -k/--keep, the folded patch files will not be
2537 deleted. With -k/--keep, the folded patch files will not be
2538 removed afterwards.
2538 removed afterwards.
2539
2539
2540 The header for each folded patch will be concatenated with the
2540 The header for each folded patch will be concatenated with the
2541 current patch header, separated by a line of ``* * *``.
2541 current patch header, separated by a line of ``* * *``.
2542
2542
2543 Returns 0 on success."""
2543 Returns 0 on success."""
2544 q = repo.mq
2544 q = repo.mq
2545 if not files:
2545 if not files:
2546 raise util.Abort(_('qfold requires at least one patch name'))
2546 raise util.Abort(_('qfold requires at least one patch name'))
2547 if not q.checktoppatch(repo)[0]:
2547 if not q.checktoppatch(repo)[0]:
2548 raise util.Abort(_('no patches applied'))
2548 raise util.Abort(_('no patches applied'))
2549 q.checklocalchanges(repo)
2549 q.checklocalchanges(repo)
2550
2550
2551 message = cmdutil.logmessage(ui, opts)
2551 message = cmdutil.logmessage(ui, opts)
2552
2552
2553 parent = q.lookup('qtip')
2553 parent = q.lookup('qtip')
2554 patches = []
2554 patches = []
2555 messages = []
2555 messages = []
2556 for f in files:
2556 for f in files:
2557 p = q.lookup(f)
2557 p = q.lookup(f)
2558 if p in patches or p == parent:
2558 if p in patches or p == parent:
2559 ui.warn(_('skipping already folded patch %s\n') % p)
2559 ui.warn(_('skipping already folded patch %s\n') % p)
2560 if q.isapplied(p):
2560 if q.isapplied(p):
2561 raise util.Abort(_('qfold cannot fold already applied patch %s')
2561 raise util.Abort(_('qfold cannot fold already applied patch %s')
2562 % p)
2562 % p)
2563 patches.append(p)
2563 patches.append(p)
2564
2564
2565 for p in patches:
2565 for p in patches:
2566 if not message:
2566 if not message:
2567 ph = patchheader(q.join(p), q.plainmode)
2567 ph = patchheader(q.join(p), q.plainmode)
2568 if ph.message:
2568 if ph.message:
2569 messages.append(ph.message)
2569 messages.append(ph.message)
2570 pf = q.join(p)
2570 pf = q.join(p)
2571 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2571 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2572 if not patchsuccess:
2572 if not patchsuccess:
2573 raise util.Abort(_('error folding patch %s') % p)
2573 raise util.Abort(_('error folding patch %s') % p)
2574
2574
2575 if not message:
2575 if not message:
2576 ph = patchheader(q.join(parent), q.plainmode)
2576 ph = patchheader(q.join(parent), q.plainmode)
2577 message = ph.message
2577 message = ph.message
2578 for msg in messages:
2578 for msg in messages:
2579 if msg:
2579 if msg:
2580 if message:
2580 if message:
2581 message.append('* * *')
2581 message.append('* * *')
2582 message.extend(msg)
2582 message.extend(msg)
2583 message = '\n'.join(message)
2583 message = '\n'.join(message)
2584
2584
2585 diffopts = q.patchopts(q.diffopts(), *patches)
2585 diffopts = q.patchopts(q.diffopts(), *patches)
2586 wlock = repo.wlock()
2586 wlock = repo.wlock()
2587 try:
2587 try:
2588 q.refresh(repo, msg=message, git=diffopts.git, edit=opts.get('edit'))
2588 q.refresh(repo, msg=message, git=diffopts.git, edit=opts.get('edit'))
2589 q.delete(repo, patches, opts)
2589 q.delete(repo, patches, opts)
2590 q.savedirty()
2590 q.savedirty()
2591 finally:
2591 finally:
2592 wlock.release()
2592 wlock.release()
2593
2593
2594 @command("qgoto",
2594 @command("qgoto",
2595 [('', 'keep-changes', None,
2595 [('', 'keep-changes', None,
2596 _('tolerate non-conflicting local changes')),
2596 _('tolerate non-conflicting local changes')),
2597 ('f', 'force', None, _('overwrite any local changes')),
2597 ('f', 'force', None, _('overwrite any local changes')),
2598 ('', 'no-backup', None, _('do not save backup copies of files'))],
2598 ('', 'no-backup', None, _('do not save backup copies of files'))],
2599 _('hg qgoto [OPTION]... PATCH'))
2599 _('hg qgoto [OPTION]... PATCH'))
2600 def goto(ui, repo, patch, **opts):
2600 def goto(ui, repo, patch, **opts):
2601 '''push or pop patches until named patch is at top of stack
2601 '''push or pop patches until named patch is at top of stack
2602
2602
2603 Returns 0 on success.'''
2603 Returns 0 on success.'''
2604 opts = fixkeepchangesopts(ui, opts)
2604 opts = fixkeepchangesopts(ui, opts)
2605 q = repo.mq
2605 q = repo.mq
2606 patch = q.lookup(patch)
2606 patch = q.lookup(patch)
2607 nobackup = opts.get('no_backup')
2607 nobackup = opts.get('no_backup')
2608 keepchanges = opts.get('keep_changes')
2608 keepchanges = opts.get('keep_changes')
2609 if q.isapplied(patch):
2609 if q.isapplied(patch):
2610 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2610 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2611 keepchanges=keepchanges)
2611 keepchanges=keepchanges)
2612 else:
2612 else:
2613 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2613 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2614 keepchanges=keepchanges)
2614 keepchanges=keepchanges)
2615 q.savedirty()
2615 q.savedirty()
2616 return ret
2616 return ret
2617
2617
2618 @command("qguard",
2618 @command("qguard",
2619 [('l', 'list', None, _('list all patches and guards')),
2619 [('l', 'list', None, _('list all patches and guards')),
2620 ('n', 'none', None, _('drop all guards'))],
2620 ('n', 'none', None, _('drop all guards'))],
2621 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2621 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2622 def guard(ui, repo, *args, **opts):
2622 def guard(ui, repo, *args, **opts):
2623 '''set or print guards for a patch
2623 '''set or print guards for a patch
2624
2624
2625 Guards control whether a patch can be pushed. A patch with no
2625 Guards control whether a patch can be pushed. A patch with no
2626 guards is always pushed. A patch with a positive guard ("+foo") is
2626 guards is always pushed. A patch with a positive guard ("+foo") is
2627 pushed only if the :hg:`qselect` command has activated it. A patch with
2627 pushed only if the :hg:`qselect` command has activated it. A patch with
2628 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2628 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2629 has activated it.
2629 has activated it.
2630
2630
2631 With no arguments, print the currently active guards.
2631 With no arguments, print the currently active guards.
2632 With arguments, set guards for the named patch.
2632 With arguments, set guards for the named patch.
2633
2633
2634 .. note::
2634 .. note::
2635
2635
2636 Specifying negative guards now requires '--'.
2636 Specifying negative guards now requires '--'.
2637
2637
2638 To set guards on another patch::
2638 To set guards on another patch::
2639
2639
2640 hg qguard other.patch -- +2.6.17 -stable
2640 hg qguard other.patch -- +2.6.17 -stable
2641
2641
2642 Returns 0 on success.
2642 Returns 0 on success.
2643 '''
2643 '''
2644 def status(idx):
2644 def status(idx):
2645 guards = q.seriesguards[idx] or ['unguarded']
2645 guards = q.seriesguards[idx] or ['unguarded']
2646 if q.series[idx] in applied:
2646 if q.series[idx] in applied:
2647 state = 'applied'
2647 state = 'applied'
2648 elif q.pushable(idx)[0]:
2648 elif q.pushable(idx)[0]:
2649 state = 'unapplied'
2649 state = 'unapplied'
2650 else:
2650 else:
2651 state = 'guarded'
2651 state = 'guarded'
2652 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2652 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2653 ui.write('%s: ' % ui.label(q.series[idx], label))
2653 ui.write('%s: ' % ui.label(q.series[idx], label))
2654
2654
2655 for i, guard in enumerate(guards):
2655 for i, guard in enumerate(guards):
2656 if guard.startswith('+'):
2656 if guard.startswith('+'):
2657 ui.write(guard, label='qguard.positive')
2657 ui.write(guard, label='qguard.positive')
2658 elif guard.startswith('-'):
2658 elif guard.startswith('-'):
2659 ui.write(guard, label='qguard.negative')
2659 ui.write(guard, label='qguard.negative')
2660 else:
2660 else:
2661 ui.write(guard, label='qguard.unguarded')
2661 ui.write(guard, label='qguard.unguarded')
2662 if i != len(guards) - 1:
2662 if i != len(guards) - 1:
2663 ui.write(' ')
2663 ui.write(' ')
2664 ui.write('\n')
2664 ui.write('\n')
2665 q = repo.mq
2665 q = repo.mq
2666 applied = set(p.name for p in q.applied)
2666 applied = set(p.name for p in q.applied)
2667 patch = None
2667 patch = None
2668 args = list(args)
2668 args = list(args)
2669 if opts.get('list'):
2669 if opts.get('list'):
2670 if args or opts.get('none'):
2670 if args or opts.get('none'):
2671 raise util.Abort(_('cannot mix -l/--list with options or '
2671 raise util.Abort(_('cannot mix -l/--list with options or '
2672 'arguments'))
2672 'arguments'))
2673 for i in xrange(len(q.series)):
2673 for i in xrange(len(q.series)):
2674 status(i)
2674 status(i)
2675 return
2675 return
2676 if not args or args[0][0:1] in '-+':
2676 if not args or args[0][0:1] in '-+':
2677 if not q.applied:
2677 if not q.applied:
2678 raise util.Abort(_('no patches applied'))
2678 raise util.Abort(_('no patches applied'))
2679 patch = q.applied[-1].name
2679 patch = q.applied[-1].name
2680 if patch is None and args[0][0:1] not in '-+':
2680 if patch is None and args[0][0:1] not in '-+':
2681 patch = args.pop(0)
2681 patch = args.pop(0)
2682 if patch is None:
2682 if patch is None:
2683 raise util.Abort(_('no patch to work with'))
2683 raise util.Abort(_('no patch to work with'))
2684 if args or opts.get('none'):
2684 if args or opts.get('none'):
2685 idx = q.findseries(patch)
2685 idx = q.findseries(patch)
2686 if idx is None:
2686 if idx is None:
2687 raise util.Abort(_('no patch named %s') % patch)
2687 raise util.Abort(_('no patch named %s') % patch)
2688 q.setguards(idx, args)
2688 q.setguards(idx, args)
2689 q.savedirty()
2689 q.savedirty()
2690 else:
2690 else:
2691 status(q.series.index(q.lookup(patch)))
2691 status(q.series.index(q.lookup(patch)))
2692
2692
2693 @command("qheader", [], _('hg qheader [PATCH]'))
2693 @command("qheader", [], _('hg qheader [PATCH]'))
2694 def header(ui, repo, patch=None):
2694 def header(ui, repo, patch=None):
2695 """print the header of the topmost or specified patch
2695 """print the header of the topmost or specified patch
2696
2696
2697 Returns 0 on success."""
2697 Returns 0 on success."""
2698 q = repo.mq
2698 q = repo.mq
2699
2699
2700 if patch:
2700 if patch:
2701 patch = q.lookup(patch)
2701 patch = q.lookup(patch)
2702 else:
2702 else:
2703 if not q.applied:
2703 if not q.applied:
2704 ui.write(_('no patches applied\n'))
2704 ui.write(_('no patches applied\n'))
2705 return 1
2705 return 1
2706 patch = q.lookup('qtip')
2706 patch = q.lookup('qtip')
2707 ph = patchheader(q.join(patch), q.plainmode)
2707 ph = patchheader(q.join(patch), q.plainmode)
2708
2708
2709 ui.write('\n'.join(ph.message) + '\n')
2709 ui.write('\n'.join(ph.message) + '\n')
2710
2710
2711 def lastsavename(path):
2711 def lastsavename(path):
2712 (directory, base) = os.path.split(path)
2712 (directory, base) = os.path.split(path)
2713 names = os.listdir(directory)
2713 names = os.listdir(directory)
2714 namere = re.compile("%s.([0-9]+)" % base)
2714 namere = re.compile("%s.([0-9]+)" % base)
2715 maxindex = None
2715 maxindex = None
2716 maxname = None
2716 maxname = None
2717 for f in names:
2717 for f in names:
2718 m = namere.match(f)
2718 m = namere.match(f)
2719 if m:
2719 if m:
2720 index = int(m.group(1))
2720 index = int(m.group(1))
2721 if maxindex is None or index > maxindex:
2721 if maxindex is None or index > maxindex:
2722 maxindex = index
2722 maxindex = index
2723 maxname = f
2723 maxname = f
2724 if maxname:
2724 if maxname:
2725 return (os.path.join(directory, maxname), maxindex)
2725 return (os.path.join(directory, maxname), maxindex)
2726 return (None, None)
2726 return (None, None)
2727
2727
2728 def savename(path):
2728 def savename(path):
2729 (last, index) = lastsavename(path)
2729 (last, index) = lastsavename(path)
2730 if last is None:
2730 if last is None:
2731 index = 0
2731 index = 0
2732 newpath = path + ".%d" % (index + 1)
2732 newpath = path + ".%d" % (index + 1)
2733 return newpath
2733 return newpath
2734
2734
2735 @command("^qpush",
2735 @command("^qpush",
2736 [('', 'keep-changes', None,
2736 [('', 'keep-changes', None,
2737 _('tolerate non-conflicting local changes')),
2737 _('tolerate non-conflicting local changes')),
2738 ('f', 'force', None, _('apply on top of local changes')),
2738 ('f', 'force', None, _('apply on top of local changes')),
2739 ('e', 'exact', None,
2739 ('e', 'exact', None,
2740 _('apply the target patch to its recorded parent')),
2740 _('apply the target patch to its recorded parent')),
2741 ('l', 'list', None, _('list patch name in commit text')),
2741 ('l', 'list', None, _('list patch name in commit text')),
2742 ('a', 'all', None, _('apply all patches')),
2742 ('a', 'all', None, _('apply all patches')),
2743 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2743 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2744 ('n', 'name', '',
2744 ('n', 'name', '',
2745 _('merge queue name (DEPRECATED)'), _('NAME')),
2745 _('merge queue name (DEPRECATED)'), _('NAME')),
2746 ('', 'move', None,
2746 ('', 'move', None,
2747 _('reorder patch series and apply only the patch')),
2747 _('reorder patch series and apply only the patch')),
2748 ('', 'no-backup', None, _('do not save backup copies of files'))],
2748 ('', 'no-backup', None, _('do not save backup copies of files'))],
2749 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2749 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2750 def push(ui, repo, patch=None, **opts):
2750 def push(ui, repo, patch=None, **opts):
2751 """push the next patch onto the stack
2751 """push the next patch onto the stack
2752
2752
2753 By default, abort if the working directory contains uncommitted
2753 By default, abort if the working directory contains uncommitted
2754 changes. With --keep-changes, abort only if the uncommitted files
2754 changes. With --keep-changes, abort only if the uncommitted files
2755 overlap with patched files. With -f/--force, backup and patch over
2755 overlap with patched files. With -f/--force, backup and patch over
2756 uncommitted changes.
2756 uncommitted changes.
2757
2757
2758 Return 0 on success.
2758 Return 0 on success.
2759 """
2759 """
2760 q = repo.mq
2760 q = repo.mq
2761 mergeq = None
2761 mergeq = None
2762
2762
2763 opts = fixkeepchangesopts(ui, opts)
2763 opts = fixkeepchangesopts(ui, opts)
2764 if opts.get('merge'):
2764 if opts.get('merge'):
2765 if opts.get('name'):
2765 if opts.get('name'):
2766 newpath = repo.join(opts.get('name'))
2766 newpath = repo.join(opts.get('name'))
2767 else:
2767 else:
2768 newpath, i = lastsavename(q.path)
2768 newpath, i = lastsavename(q.path)
2769 if not newpath:
2769 if not newpath:
2770 ui.warn(_("no saved queues found, please use -n\n"))
2770 ui.warn(_("no saved queues found, please use -n\n"))
2771 return 1
2771 return 1
2772 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2772 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2773 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2773 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2774 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2774 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2775 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2775 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2776 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2776 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2777 keepchanges=opts.get('keep_changes'))
2777 keepchanges=opts.get('keep_changes'))
2778 return ret
2778 return ret
2779
2779
2780 @command("^qpop",
2780 @command("^qpop",
2781 [('a', 'all', None, _('pop all patches')),
2781 [('a', 'all', None, _('pop all patches')),
2782 ('n', 'name', '',
2782 ('n', 'name', '',
2783 _('queue name to pop (DEPRECATED)'), _('NAME')),
2783 _('queue name to pop (DEPRECATED)'), _('NAME')),
2784 ('', 'keep-changes', None,
2784 ('', 'keep-changes', None,
2785 _('tolerate non-conflicting local changes')),
2785 _('tolerate non-conflicting local changes')),
2786 ('f', 'force', None, _('forget any local changes to patched files')),
2786 ('f', 'force', None, _('forget any local changes to patched files')),
2787 ('', 'no-backup', None, _('do not save backup copies of files'))],
2787 ('', 'no-backup', None, _('do not save backup copies of files'))],
2788 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2788 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2789 def pop(ui, repo, patch=None, **opts):
2789 def pop(ui, repo, patch=None, **opts):
2790 """pop the current patch off the stack
2790 """pop the current patch off the stack
2791
2791
2792 Without argument, pops off the top of the patch stack. If given a
2792 Without argument, pops off the top of the patch stack. If given a
2793 patch name, keeps popping off patches until the named patch is at
2793 patch name, keeps popping off patches until the named patch is at
2794 the top of the stack.
2794 the top of the stack.
2795
2795
2796 By default, abort if the working directory contains uncommitted
2796 By default, abort if the working directory contains uncommitted
2797 changes. With --keep-changes, abort only if the uncommitted files
2797 changes. With --keep-changes, abort only if the uncommitted files
2798 overlap with patched files. With -f/--force, backup and discard
2798 overlap with patched files. With -f/--force, backup and discard
2799 changes made to such files.
2799 changes made to such files.
2800
2800
2801 Return 0 on success.
2801 Return 0 on success.
2802 """
2802 """
2803 opts = fixkeepchangesopts(ui, opts)
2803 opts = fixkeepchangesopts(ui, opts)
2804 localupdate = True
2804 localupdate = True
2805 if opts.get('name'):
2805 if opts.get('name'):
2806 q = queue(ui, repo.baseui, repo.path, repo.join(opts.get('name')))
2806 q = queue(ui, repo.baseui, repo.path, repo.join(opts.get('name')))
2807 ui.warn(_('using patch queue: %s\n') % q.path)
2807 ui.warn(_('using patch queue: %s\n') % q.path)
2808 localupdate = False
2808 localupdate = False
2809 else:
2809 else:
2810 q = repo.mq
2810 q = repo.mq
2811 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2811 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2812 all=opts.get('all'), nobackup=opts.get('no_backup'),
2812 all=opts.get('all'), nobackup=opts.get('no_backup'),
2813 keepchanges=opts.get('keep_changes'))
2813 keepchanges=opts.get('keep_changes'))
2814 q.savedirty()
2814 q.savedirty()
2815 return ret
2815 return ret
2816
2816
2817 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2817 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2818 def rename(ui, repo, patch, name=None, **opts):
2818 def rename(ui, repo, patch, name=None, **opts):
2819 """rename a patch
2819 """rename a patch
2820
2820
2821 With one argument, renames the current patch to PATCH1.
2821 With one argument, renames the current patch to PATCH1.
2822 With two arguments, renames PATCH1 to PATCH2.
2822 With two arguments, renames PATCH1 to PATCH2.
2823
2823
2824 Returns 0 on success."""
2824 Returns 0 on success."""
2825 q = repo.mq
2825 q = repo.mq
2826 if not name:
2826 if not name:
2827 name = patch
2827 name = patch
2828 patch = None
2828 patch = None
2829
2829
2830 if patch:
2830 if patch:
2831 patch = q.lookup(patch)
2831 patch = q.lookup(patch)
2832 else:
2832 else:
2833 if not q.applied:
2833 if not q.applied:
2834 ui.write(_('no patches applied\n'))
2834 ui.write(_('no patches applied\n'))
2835 return
2835 return
2836 patch = q.lookup('qtip')
2836 patch = q.lookup('qtip')
2837 absdest = q.join(name)
2837 absdest = q.join(name)
2838 if os.path.isdir(absdest):
2838 if os.path.isdir(absdest):
2839 name = normname(os.path.join(name, os.path.basename(patch)))
2839 name = normname(os.path.join(name, os.path.basename(patch)))
2840 absdest = q.join(name)
2840 absdest = q.join(name)
2841 q.checkpatchname(name)
2841 q.checkpatchname(name)
2842
2842
2843 ui.note(_('renaming %s to %s\n') % (patch, name))
2843 ui.note(_('renaming %s to %s\n') % (patch, name))
2844 i = q.findseries(patch)
2844 i = q.findseries(patch)
2845 guards = q.guard_re.findall(q.fullseries[i])
2845 guards = q.guard_re.findall(q.fullseries[i])
2846 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2846 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2847 q.parseseries()
2847 q.parseseries()
2848 q.seriesdirty = True
2848 q.seriesdirty = True
2849
2849
2850 info = q.isapplied(patch)
2850 info = q.isapplied(patch)
2851 if info:
2851 if info:
2852 q.applied[info[0]] = statusentry(info[1], name)
2852 q.applied[info[0]] = statusentry(info[1], name)
2853 q.applieddirty = True
2853 q.applieddirty = True
2854
2854
2855 destdir = os.path.dirname(absdest)
2855 destdir = os.path.dirname(absdest)
2856 if not os.path.isdir(destdir):
2856 if not os.path.isdir(destdir):
2857 os.makedirs(destdir)
2857 os.makedirs(destdir)
2858 util.rename(q.join(patch), absdest)
2858 util.rename(q.join(patch), absdest)
2859 r = q.qrepo()
2859 r = q.qrepo()
2860 if r and patch in r.dirstate:
2860 if r and patch in r.dirstate:
2861 wctx = r[None]
2861 wctx = r[None]
2862 wlock = r.wlock()
2862 wlock = r.wlock()
2863 try:
2863 try:
2864 if r.dirstate[patch] == 'a':
2864 if r.dirstate[patch] == 'a':
2865 r.dirstate.drop(patch)
2865 r.dirstate.drop(patch)
2866 r.dirstate.add(name)
2866 r.dirstate.add(name)
2867 else:
2867 else:
2868 wctx.copy(patch, name)
2868 wctx.copy(patch, name)
2869 wctx.forget([patch])
2869 wctx.forget([patch])
2870 finally:
2870 finally:
2871 wlock.release()
2871 wlock.release()
2872
2872
2873 q.savedirty()
2873 q.savedirty()
2874
2874
2875 @command("qrestore",
2875 @command("qrestore",
2876 [('d', 'delete', None, _('delete save entry')),
2876 [('d', 'delete', None, _('delete save entry')),
2877 ('u', 'update', None, _('update queue working directory'))],
2877 ('u', 'update', None, _('update queue working directory'))],
2878 _('hg qrestore [-d] [-u] REV'))
2878 _('hg qrestore [-d] [-u] REV'))
2879 def restore(ui, repo, rev, **opts):
2879 def restore(ui, repo, rev, **opts):
2880 """restore the queue state saved by a revision (DEPRECATED)
2880 """restore the queue state saved by a revision (DEPRECATED)
2881
2881
2882 This command is deprecated, use :hg:`rebase` instead."""
2882 This command is deprecated, use :hg:`rebase` instead."""
2883 rev = repo.lookup(rev)
2883 rev = repo.lookup(rev)
2884 q = repo.mq
2884 q = repo.mq
2885 q.restore(repo, rev, delete=opts.get('delete'),
2885 q.restore(repo, rev, delete=opts.get('delete'),
2886 qupdate=opts.get('update'))
2886 qupdate=opts.get('update'))
2887 q.savedirty()
2887 q.savedirty()
2888 return 0
2888 return 0
2889
2889
2890 @command("qsave",
2890 @command("qsave",
2891 [('c', 'copy', None, _('copy patch directory')),
2891 [('c', 'copy', None, _('copy patch directory')),
2892 ('n', 'name', '',
2892 ('n', 'name', '',
2893 _('copy directory name'), _('NAME')),
2893 _('copy directory name'), _('NAME')),
2894 ('e', 'empty', None, _('clear queue status file')),
2894 ('e', 'empty', None, _('clear queue status file')),
2895 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2895 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2896 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2896 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2897 def save(ui, repo, **opts):
2897 def save(ui, repo, **opts):
2898 """save current queue state (DEPRECATED)
2898 """save current queue state (DEPRECATED)
2899
2899
2900 This command is deprecated, use :hg:`rebase` instead."""
2900 This command is deprecated, use :hg:`rebase` instead."""
2901 q = repo.mq
2901 q = repo.mq
2902 message = cmdutil.logmessage(ui, opts)
2902 message = cmdutil.logmessage(ui, opts)
2903 ret = q.save(repo, msg=message)
2903 ret = q.save(repo, msg=message)
2904 if ret:
2904 if ret:
2905 return ret
2905 return ret
2906 q.savedirty() # save to .hg/patches before copying
2906 q.savedirty() # save to .hg/patches before copying
2907 if opts.get('copy'):
2907 if opts.get('copy'):
2908 path = q.path
2908 path = q.path
2909 if opts.get('name'):
2909 if opts.get('name'):
2910 newpath = os.path.join(q.basepath, opts.get('name'))
2910 newpath = os.path.join(q.basepath, opts.get('name'))
2911 if os.path.exists(newpath):
2911 if os.path.exists(newpath):
2912 if not os.path.isdir(newpath):
2912 if not os.path.isdir(newpath):
2913 raise util.Abort(_('destination %s exists and is not '
2913 raise util.Abort(_('destination %s exists and is not '
2914 'a directory') % newpath)
2914 'a directory') % newpath)
2915 if not opts.get('force'):
2915 if not opts.get('force'):
2916 raise util.Abort(_('destination %s exists, '
2916 raise util.Abort(_('destination %s exists, '
2917 'use -f to force') % newpath)
2917 'use -f to force') % newpath)
2918 else:
2918 else:
2919 newpath = savename(path)
2919 newpath = savename(path)
2920 ui.warn(_("copy %s to %s\n") % (path, newpath))
2920 ui.warn(_("copy %s to %s\n") % (path, newpath))
2921 util.copyfiles(path, newpath)
2921 util.copyfiles(path, newpath)
2922 if opts.get('empty'):
2922 if opts.get('empty'):
2923 del q.applied[:]
2923 del q.applied[:]
2924 q.applieddirty = True
2924 q.applieddirty = True
2925 q.savedirty()
2925 q.savedirty()
2926 return 0
2926 return 0
2927
2927
2928
2928
2929 @command("qselect",
2929 @command("qselect",
2930 [('n', 'none', None, _('disable all guards')),
2930 [('n', 'none', None, _('disable all guards')),
2931 ('s', 'series', None, _('list all guards in series file')),
2931 ('s', 'series', None, _('list all guards in series file')),
2932 ('', 'pop', None, _('pop to before first guarded applied patch')),
2932 ('', 'pop', None, _('pop to before first guarded applied patch')),
2933 ('', 'reapply', None, _('pop, then reapply patches'))],
2933 ('', 'reapply', None, _('pop, then reapply patches'))],
2934 _('hg qselect [OPTION]... [GUARD]...'))
2934 _('hg qselect [OPTION]... [GUARD]...'))
2935 def select(ui, repo, *args, **opts):
2935 def select(ui, repo, *args, **opts):
2936 '''set or print guarded patches to push
2936 '''set or print guarded patches to push
2937
2937
2938 Use the :hg:`qguard` command to set or print guards on patch, then use
2938 Use the :hg:`qguard` command to set or print guards on patch, then use
2939 qselect to tell mq which guards to use. A patch will be pushed if
2939 qselect to tell mq which guards to use. A patch will be pushed if
2940 it has no guards or any positive guards match the currently
2940 it has no guards or any positive guards match the currently
2941 selected guard, but will not be pushed if any negative guards
2941 selected guard, but will not be pushed if any negative guards
2942 match the current guard. For example::
2942 match the current guard. For example::
2943
2943
2944 qguard foo.patch -- -stable (negative guard)
2944 qguard foo.patch -- -stable (negative guard)
2945 qguard bar.patch +stable (positive guard)
2945 qguard bar.patch +stable (positive guard)
2946 qselect stable
2946 qselect stable
2947
2947
2948 This activates the "stable" guard. mq will skip foo.patch (because
2948 This activates the "stable" guard. mq will skip foo.patch (because
2949 it has a negative match) but push bar.patch (because it has a
2949 it has a negative match) but push bar.patch (because it has a
2950 positive match).
2950 positive match).
2951
2951
2952 With no arguments, prints the currently active guards.
2952 With no arguments, prints the currently active guards.
2953 With one argument, sets the active guard.
2953 With one argument, sets the active guard.
2954
2954
2955 Use -n/--none to deactivate guards (no other arguments needed).
2955 Use -n/--none to deactivate guards (no other arguments needed).
2956 When no guards are active, patches with positive guards are
2956 When no guards are active, patches with positive guards are
2957 skipped and patches with negative guards are pushed.
2957 skipped and patches with negative guards are pushed.
2958
2958
2959 qselect can change the guards on applied patches. It does not pop
2959 qselect can change the guards on applied patches. It does not pop
2960 guarded patches by default. Use --pop to pop back to the last
2960 guarded patches by default. Use --pop to pop back to the last
2961 applied patch that is not guarded. Use --reapply (which implies
2961 applied patch that is not guarded. Use --reapply (which implies
2962 --pop) to push back to the current patch afterwards, but skip
2962 --pop) to push back to the current patch afterwards, but skip
2963 guarded patches.
2963 guarded patches.
2964
2964
2965 Use -s/--series to print a list of all guards in the series file
2965 Use -s/--series to print a list of all guards in the series file
2966 (no other arguments needed). Use -v for more information.
2966 (no other arguments needed). Use -v for more information.
2967
2967
2968 Returns 0 on success.'''
2968 Returns 0 on success.'''
2969
2969
2970 q = repo.mq
2970 q = repo.mq
2971 guards = q.active()
2971 guards = q.active()
2972 if args or opts.get('none'):
2972 if args or opts.get('none'):
2973 old_unapplied = q.unapplied(repo)
2973 old_unapplied = q.unapplied(repo)
2974 old_guarded = [i for i in xrange(len(q.applied)) if
2974 old_guarded = [i for i in xrange(len(q.applied)) if
2975 not q.pushable(i)[0]]
2975 not q.pushable(i)[0]]
2976 q.setactive(args)
2976 q.setactive(args)
2977 q.savedirty()
2977 q.savedirty()
2978 if not args:
2978 if not args:
2979 ui.status(_('guards deactivated\n'))
2979 ui.status(_('guards deactivated\n'))
2980 if not opts.get('pop') and not opts.get('reapply'):
2980 if not opts.get('pop') and not opts.get('reapply'):
2981 unapplied = q.unapplied(repo)
2981 unapplied = q.unapplied(repo)
2982 guarded = [i for i in xrange(len(q.applied))
2982 guarded = [i for i in xrange(len(q.applied))
2983 if not q.pushable(i)[0]]
2983 if not q.pushable(i)[0]]
2984 if len(unapplied) != len(old_unapplied):
2984 if len(unapplied) != len(old_unapplied):
2985 ui.status(_('number of unguarded, unapplied patches has '
2985 ui.status(_('number of unguarded, unapplied patches has '
2986 'changed from %d to %d\n') %
2986 'changed from %d to %d\n') %
2987 (len(old_unapplied), len(unapplied)))
2987 (len(old_unapplied), len(unapplied)))
2988 if len(guarded) != len(old_guarded):
2988 if len(guarded) != len(old_guarded):
2989 ui.status(_('number of guarded, applied patches has changed '
2989 ui.status(_('number of guarded, applied patches has changed '
2990 'from %d to %d\n') %
2990 'from %d to %d\n') %
2991 (len(old_guarded), len(guarded)))
2991 (len(old_guarded), len(guarded)))
2992 elif opts.get('series'):
2992 elif opts.get('series'):
2993 guards = {}
2993 guards = {}
2994 noguards = 0
2994 noguards = 0
2995 for gs in q.seriesguards:
2995 for gs in q.seriesguards:
2996 if not gs:
2996 if not gs:
2997 noguards += 1
2997 noguards += 1
2998 for g in gs:
2998 for g in gs:
2999 guards.setdefault(g, 0)
2999 guards.setdefault(g, 0)
3000 guards[g] += 1
3000 guards[g] += 1
3001 if ui.verbose:
3001 if ui.verbose:
3002 guards['NONE'] = noguards
3002 guards['NONE'] = noguards
3003 guards = guards.items()
3003 guards = guards.items()
3004 guards.sort(key=lambda x: x[0][1:])
3004 guards.sort(key=lambda x: x[0][1:])
3005 if guards:
3005 if guards:
3006 ui.note(_('guards in series file:\n'))
3006 ui.note(_('guards in series file:\n'))
3007 for guard, count in guards:
3007 for guard, count in guards:
3008 ui.note('%2d ' % count)
3008 ui.note('%2d ' % count)
3009 ui.write(guard, '\n')
3009 ui.write(guard, '\n')
3010 else:
3010 else:
3011 ui.note(_('no guards in series file\n'))
3011 ui.note(_('no guards in series file\n'))
3012 else:
3012 else:
3013 if guards:
3013 if guards:
3014 ui.note(_('active guards:\n'))
3014 ui.note(_('active guards:\n'))
3015 for g in guards:
3015 for g in guards:
3016 ui.write(g, '\n')
3016 ui.write(g, '\n')
3017 else:
3017 else:
3018 ui.write(_('no active guards\n'))
3018 ui.write(_('no active guards\n'))
3019 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
3019 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
3020 popped = False
3020 popped = False
3021 if opts.get('pop') or opts.get('reapply'):
3021 if opts.get('pop') or opts.get('reapply'):
3022 for i in xrange(len(q.applied)):
3022 for i in xrange(len(q.applied)):
3023 pushable, reason = q.pushable(i)
3023 pushable, reason = q.pushable(i)
3024 if not pushable:
3024 if not pushable:
3025 ui.status(_('popping guarded patches\n'))
3025 ui.status(_('popping guarded patches\n'))
3026 popped = True
3026 popped = True
3027 if i == 0:
3027 if i == 0:
3028 q.pop(repo, all=True)
3028 q.pop(repo, all=True)
3029 else:
3029 else:
3030 q.pop(repo, str(i - 1))
3030 q.pop(repo, str(i - 1))
3031 break
3031 break
3032 if popped:
3032 if popped:
3033 try:
3033 try:
3034 if reapply:
3034 if reapply:
3035 ui.status(_('reapplying unguarded patches\n'))
3035 ui.status(_('reapplying unguarded patches\n'))
3036 q.push(repo, reapply)
3036 q.push(repo, reapply)
3037 finally:
3037 finally:
3038 q.savedirty()
3038 q.savedirty()
3039
3039
3040 @command("qfinish",
3040 @command("qfinish",
3041 [('a', 'applied', None, _('finish all applied changesets'))],
3041 [('a', 'applied', None, _('finish all applied changesets'))],
3042 _('hg qfinish [-a] [REV]...'))
3042 _('hg qfinish [-a] [REV]...'))
3043 def finish(ui, repo, *revrange, **opts):
3043 def finish(ui, repo, *revrange, **opts):
3044 """move applied patches into repository history
3044 """move applied patches into repository history
3045
3045
3046 Finishes the specified revisions (corresponding to applied
3046 Finishes the specified revisions (corresponding to applied
3047 patches) by moving them out of mq control into regular repository
3047 patches) by moving them out of mq control into regular repository
3048 history.
3048 history.
3049
3049
3050 Accepts a revision range or the -a/--applied option. If --applied
3050 Accepts a revision range or the -a/--applied option. If --applied
3051 is specified, all applied mq revisions are removed from mq
3051 is specified, all applied mq revisions are removed from mq
3052 control. Otherwise, the given revisions must be at the base of the
3052 control. Otherwise, the given revisions must be at the base of the
3053 stack of applied patches.
3053 stack of applied patches.
3054
3054
3055 This can be especially useful if your changes have been applied to
3055 This can be especially useful if your changes have been applied to
3056 an upstream repository, or if you are about to push your changes
3056 an upstream repository, or if you are about to push your changes
3057 to upstream.
3057 to upstream.
3058
3058
3059 Returns 0 on success.
3059 Returns 0 on success.
3060 """
3060 """
3061 if not opts.get('applied') and not revrange:
3061 if not opts.get('applied') and not revrange:
3062 raise util.Abort(_('no revisions specified'))
3062 raise util.Abort(_('no revisions specified'))
3063 elif opts.get('applied'):
3063 elif opts.get('applied'):
3064 revrange = ('qbase::qtip',) + revrange
3064 revrange = ('qbase::qtip',) + revrange
3065
3065
3066 q = repo.mq
3066 q = repo.mq
3067 if not q.applied:
3067 if not q.applied:
3068 ui.status(_('no patches applied\n'))
3068 ui.status(_('no patches applied\n'))
3069 return 0
3069 return 0
3070
3070
3071 revs = scmutil.revrange(repo, revrange)
3071 revs = scmutil.revrange(repo, revrange)
3072 if repo['.'].rev() in revs and repo[None].files():
3072 if repo['.'].rev() in revs and repo[None].files():
3073 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3073 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3074 # queue.finish may changes phases but leave the responsibility to lock the
3074 # queue.finish may changes phases but leave the responsibility to lock the
3075 # repo to the caller to avoid deadlock with wlock. This command code is
3075 # repo to the caller to avoid deadlock with wlock. This command code is
3076 # responsibility for this locking.
3076 # responsibility for this locking.
3077 lock = repo.lock()
3077 lock = repo.lock()
3078 try:
3078 try:
3079 q.finish(repo, revs)
3079 q.finish(repo, revs)
3080 q.savedirty()
3080 q.savedirty()
3081 finally:
3081 finally:
3082 lock.release()
3082 lock.release()
3083 return 0
3083 return 0
3084
3084
3085 @command("qqueue",
3085 @command("qqueue",
3086 [('l', 'list', False, _('list all available queues')),
3086 [('l', 'list', False, _('list all available queues')),
3087 ('', 'active', False, _('print name of active queue')),
3087 ('', 'active', False, _('print name of active queue')),
3088 ('c', 'create', False, _('create new queue')),
3088 ('c', 'create', False, _('create new queue')),
3089 ('', 'rename', False, _('rename active queue')),
3089 ('', 'rename', False, _('rename active queue')),
3090 ('', 'delete', False, _('delete reference to queue')),
3090 ('', 'delete', False, _('delete reference to queue')),
3091 ('', 'purge', False, _('delete queue, and remove patch dir')),
3091 ('', 'purge', False, _('delete queue, and remove patch dir')),
3092 ],
3092 ],
3093 _('[OPTION] [QUEUE]'))
3093 _('[OPTION] [QUEUE]'))
3094 def qqueue(ui, repo, name=None, **opts):
3094 def qqueue(ui, repo, name=None, **opts):
3095 '''manage multiple patch queues
3095 '''manage multiple patch queues
3096
3096
3097 Supports switching between different patch queues, as well as creating
3097 Supports switching between different patch queues, as well as creating
3098 new patch queues and deleting existing ones.
3098 new patch queues and deleting existing ones.
3099
3099
3100 Omitting a queue name or specifying -l/--list will show you the registered
3100 Omitting a queue name or specifying -l/--list will show you the registered
3101 queues - by default the "normal" patches queue is registered. The currently
3101 queues - by default the "normal" patches queue is registered. The currently
3102 active queue will be marked with "(active)". Specifying --active will print
3102 active queue will be marked with "(active)". Specifying --active will print
3103 only the name of the active queue.
3103 only the name of the active queue.
3104
3104
3105 To create a new queue, use -c/--create. The queue is automatically made
3105 To create a new queue, use -c/--create. The queue is automatically made
3106 active, except in the case where there are applied patches from the
3106 active, except in the case where there are applied patches from the
3107 currently active queue in the repository. Then the queue will only be
3107 currently active queue in the repository. Then the queue will only be
3108 created and switching will fail.
3108 created and switching will fail.
3109
3109
3110 To delete an existing queue, use --delete. You cannot delete the currently
3110 To delete an existing queue, use --delete. You cannot delete the currently
3111 active queue.
3111 active queue.
3112
3112
3113 Returns 0 on success.
3113 Returns 0 on success.
3114 '''
3114 '''
3115 q = repo.mq
3115 q = repo.mq
3116 _defaultqueue = 'patches'
3116 _defaultqueue = 'patches'
3117 _allqueues = 'patches.queues'
3117 _allqueues = 'patches.queues'
3118 _activequeue = 'patches.queue'
3118 _activequeue = 'patches.queue'
3119
3119
3120 def _getcurrent():
3120 def _getcurrent():
3121 cur = os.path.basename(q.path)
3121 cur = os.path.basename(q.path)
3122 if cur.startswith('patches-'):
3122 if cur.startswith('patches-'):
3123 cur = cur[8:]
3123 cur = cur[8:]
3124 return cur
3124 return cur
3125
3125
3126 def _noqueues():
3126 def _noqueues():
3127 try:
3127 try:
3128 fh = repo.opener(_allqueues, 'r')
3128 fh = repo.opener(_allqueues, 'r')
3129 fh.close()
3129 fh.close()
3130 except IOError:
3130 except IOError:
3131 return True
3131 return True
3132
3132
3133 return False
3133 return False
3134
3134
3135 def _getqueues():
3135 def _getqueues():
3136 current = _getcurrent()
3136 current = _getcurrent()
3137
3137
3138 try:
3138 try:
3139 fh = repo.opener(_allqueues, 'r')
3139 fh = repo.opener(_allqueues, 'r')
3140 queues = [queue.strip() for queue in fh if queue.strip()]
3140 queues = [queue.strip() for queue in fh if queue.strip()]
3141 fh.close()
3141 fh.close()
3142 if current not in queues:
3142 if current not in queues:
3143 queues.append(current)
3143 queues.append(current)
3144 except IOError:
3144 except IOError:
3145 queues = [_defaultqueue]
3145 queues = [_defaultqueue]
3146
3146
3147 return sorted(queues)
3147 return sorted(queues)
3148
3148
3149 def _setactive(name):
3149 def _setactive(name):
3150 if q.applied:
3150 if q.applied:
3151 raise util.Abort(_('new queue created, but cannot make active '
3151 raise util.Abort(_('new queue created, but cannot make active '
3152 'as patches are applied'))
3152 'as patches are applied'))
3153 _setactivenocheck(name)
3153 _setactivenocheck(name)
3154
3154
3155 def _setactivenocheck(name):
3155 def _setactivenocheck(name):
3156 fh = repo.opener(_activequeue, 'w')
3156 fh = repo.opener(_activequeue, 'w')
3157 if name != 'patches':
3157 if name != 'patches':
3158 fh.write(name)
3158 fh.write(name)
3159 fh.close()
3159 fh.close()
3160
3160
3161 def _addqueue(name):
3161 def _addqueue(name):
3162 fh = repo.opener(_allqueues, 'a')
3162 fh = repo.opener(_allqueues, 'a')
3163 fh.write('%s\n' % (name,))
3163 fh.write('%s\n' % (name,))
3164 fh.close()
3164 fh.close()
3165
3165
3166 def _queuedir(name):
3166 def _queuedir(name):
3167 if name == 'patches':
3167 if name == 'patches':
3168 return repo.join('patches')
3168 return repo.join('patches')
3169 else:
3169 else:
3170 return repo.join('patches-' + name)
3170 return repo.join('patches-' + name)
3171
3171
3172 def _validname(name):
3172 def _validname(name):
3173 for n in name:
3173 for n in name:
3174 if n in ':\\/.':
3174 if n in ':\\/.':
3175 return False
3175 return False
3176 return True
3176 return True
3177
3177
3178 def _delete(name):
3178 def _delete(name):
3179 if name not in existing:
3179 if name not in existing:
3180 raise util.Abort(_('cannot delete queue that does not exist'))
3180 raise util.Abort(_('cannot delete queue that does not exist'))
3181
3181
3182 current = _getcurrent()
3182 current = _getcurrent()
3183
3183
3184 if name == current:
3184 if name == current:
3185 raise util.Abort(_('cannot delete currently active queue'))
3185 raise util.Abort(_('cannot delete currently active queue'))
3186
3186
3187 fh = repo.opener('patches.queues.new', 'w')
3187 fh = repo.opener('patches.queues.new', 'w')
3188 for queue in existing:
3188 for queue in existing:
3189 if queue == name:
3189 if queue == name:
3190 continue
3190 continue
3191 fh.write('%s\n' % (queue,))
3191 fh.write('%s\n' % (queue,))
3192 fh.close()
3192 fh.close()
3193 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3193 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3194
3194
3195 if not name or opts.get('list') or opts.get('active'):
3195 if not name or opts.get('list') or opts.get('active'):
3196 current = _getcurrent()
3196 current = _getcurrent()
3197 if opts.get('active'):
3197 if opts.get('active'):
3198 ui.write('%s\n' % (current,))
3198 ui.write('%s\n' % (current,))
3199 return
3199 return
3200 for queue in _getqueues():
3200 for queue in _getqueues():
3201 ui.write('%s' % (queue,))
3201 ui.write('%s' % (queue,))
3202 if queue == current and not ui.quiet:
3202 if queue == current and not ui.quiet:
3203 ui.write(_(' (active)\n'))
3203 ui.write(_(' (active)\n'))
3204 else:
3204 else:
3205 ui.write('\n')
3205 ui.write('\n')
3206 return
3206 return
3207
3207
3208 if not _validname(name):
3208 if not _validname(name):
3209 raise util.Abort(
3209 raise util.Abort(
3210 _('invalid queue name, may not contain the characters ":\\/."'))
3210 _('invalid queue name, may not contain the characters ":\\/."'))
3211
3211
3212 existing = _getqueues()
3212 existing = _getqueues()
3213
3213
3214 if opts.get('create'):
3214 if opts.get('create'):
3215 if name in existing:
3215 if name in existing:
3216 raise util.Abort(_('queue "%s" already exists') % name)
3216 raise util.Abort(_('queue "%s" already exists') % name)
3217 if _noqueues():
3217 if _noqueues():
3218 _addqueue(_defaultqueue)
3218 _addqueue(_defaultqueue)
3219 _addqueue(name)
3219 _addqueue(name)
3220 _setactive(name)
3220 _setactive(name)
3221 elif opts.get('rename'):
3221 elif opts.get('rename'):
3222 current = _getcurrent()
3222 current = _getcurrent()
3223 if name == current:
3223 if name == current:
3224 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3224 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3225 if name in existing:
3225 if name in existing:
3226 raise util.Abort(_('queue "%s" already exists') % name)
3226 raise util.Abort(_('queue "%s" already exists') % name)
3227
3227
3228 olddir = _queuedir(current)
3228 olddir = _queuedir(current)
3229 newdir = _queuedir(name)
3229 newdir = _queuedir(name)
3230
3230
3231 if os.path.exists(newdir):
3231 if os.path.exists(newdir):
3232 raise util.Abort(_('non-queue directory "%s" already exists') %
3232 raise util.Abort(_('non-queue directory "%s" already exists') %
3233 newdir)
3233 newdir)
3234
3234
3235 fh = repo.opener('patches.queues.new', 'w')
3235 fh = repo.opener('patches.queues.new', 'w')
3236 for queue in existing:
3236 for queue in existing:
3237 if queue == current:
3237 if queue == current:
3238 fh.write('%s\n' % (name,))
3238 fh.write('%s\n' % (name,))
3239 if os.path.exists(olddir):
3239 if os.path.exists(olddir):
3240 util.rename(olddir, newdir)
3240 util.rename(olddir, newdir)
3241 else:
3241 else:
3242 fh.write('%s\n' % (queue,))
3242 fh.write('%s\n' % (queue,))
3243 fh.close()
3243 fh.close()
3244 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3244 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3245 _setactivenocheck(name)
3245 _setactivenocheck(name)
3246 elif opts.get('delete'):
3246 elif opts.get('delete'):
3247 _delete(name)
3247 _delete(name)
3248 elif opts.get('purge'):
3248 elif opts.get('purge'):
3249 if name in existing:
3249 if name in existing:
3250 _delete(name)
3250 _delete(name)
3251 qdir = _queuedir(name)
3251 qdir = _queuedir(name)
3252 if os.path.exists(qdir):
3252 if os.path.exists(qdir):
3253 shutil.rmtree(qdir)
3253 shutil.rmtree(qdir)
3254 else:
3254 else:
3255 if name not in existing:
3255 if name not in existing:
3256 raise util.Abort(_('use --create to create a new queue'))
3256 raise util.Abort(_('use --create to create a new queue'))
3257 _setactive(name)
3257 _setactive(name)
3258
3258
3259 def mqphasedefaults(repo, roots):
3259 def mqphasedefaults(repo, roots):
3260 """callback used to set mq changeset as secret when no phase data exists"""
3260 """callback used to set mq changeset as secret when no phase data exists"""
3261 if repo.mq.applied:
3261 if repo.mq.applied:
3262 if repo.ui.configbool('mq', 'secret', False):
3262 if repo.ui.configbool('mq', 'secret', False):
3263 mqphase = phases.secret
3263 mqphase = phases.secret
3264 else:
3264 else:
3265 mqphase = phases.draft
3265 mqphase = phases.draft
3266 qbase = repo[repo.mq.applied[0].node]
3266 qbase = repo[repo.mq.applied[0].node]
3267 roots[mqphase].add(qbase.node())
3267 roots[mqphase].add(qbase.node())
3268 return roots
3268 return roots
3269
3269
3270 def reposetup(ui, repo):
3270 def reposetup(ui, repo):
3271 class mqrepo(repo.__class__):
3271 class mqrepo(repo.__class__):
3272 @localrepo.unfilteredpropertycache
3272 @localrepo.unfilteredpropertycache
3273 def mq(self):
3273 def mq(self):
3274 return queue(self.ui, self.baseui, self.path)
3274 return queue(self.ui, self.baseui, self.path)
3275
3275
3276 def invalidateall(self):
3276 def invalidateall(self):
3277 super(mqrepo, self).invalidateall()
3277 super(mqrepo, self).invalidateall()
3278 if localrepo.hasunfilteredcache(self, 'mq'):
3278 if localrepo.hasunfilteredcache(self, 'mq'):
3279 # recreate mq in case queue path was changed
3279 # recreate mq in case queue path was changed
3280 delattr(self.unfiltered(), 'mq')
3280 delattr(self.unfiltered(), 'mq')
3281
3281
3282 def abortifwdirpatched(self, errmsg, force=False):
3282 def abortifwdirpatched(self, errmsg, force=False):
3283 if self.mq.applied and self.mq.checkapplied and not force:
3283 if self.mq.applied and self.mq.checkapplied and not force:
3284 parents = self.dirstate.parents()
3284 parents = self.dirstate.parents()
3285 patches = [s.node for s in self.mq.applied]
3285 patches = [s.node for s in self.mq.applied]
3286 if parents[0] in patches or parents[1] in patches:
3286 if parents[0] in patches or parents[1] in patches:
3287 raise util.Abort(errmsg)
3287 raise util.Abort(errmsg)
3288
3288
3289 def commit(self, text="", user=None, date=None, match=None,
3289 def commit(self, text="", user=None, date=None, match=None,
3290 force=False, editor=False, extra={}):
3290 force=False, editor=False, extra={}):
3291 self.abortifwdirpatched(
3291 self.abortifwdirpatched(
3292 _('cannot commit over an applied mq patch'),
3292 _('cannot commit over an applied mq patch'),
3293 force)
3293 force)
3294
3294
3295 return super(mqrepo, self).commit(text, user, date, match, force,
3295 return super(mqrepo, self).commit(text, user, date, match, force,
3296 editor, extra)
3296 editor, extra)
3297
3297
3298 def checkpush(self, pushop):
3298 def checkpush(self, pushop):
3299 if self.mq.applied and self.mq.checkapplied and not pushop.force:
3299 if self.mq.applied and self.mq.checkapplied and not pushop.force:
3300 outapplied = [e.node for e in self.mq.applied]
3300 outapplied = [e.node for e in self.mq.applied]
3301 if pushop.revs:
3301 if pushop.revs:
3302 # Assume applied patches have no non-patch descendants and
3302 # Assume applied patches have no non-patch descendants and
3303 # are not on remote already. Filtering any changeset not
3303 # are not on remote already. Filtering any changeset not
3304 # pushed.
3304 # pushed.
3305 heads = set(pushop.revs)
3305 heads = set(pushop.revs)
3306 for node in reversed(outapplied):
3306 for node in reversed(outapplied):
3307 if node in heads:
3307 if node in heads:
3308 break
3308 break
3309 else:
3309 else:
3310 outapplied.pop()
3310 outapplied.pop()
3311 # looking for pushed and shared changeset
3311 # looking for pushed and shared changeset
3312 for node in outapplied:
3312 for node in outapplied:
3313 if self[node].phase() < phases.secret:
3313 if self[node].phase() < phases.secret:
3314 raise util.Abort(_('source has mq patches applied'))
3314 raise util.Abort(_('source has mq patches applied'))
3315 # no non-secret patches pushed
3315 # no non-secret patches pushed
3316 super(mqrepo, self).checkpush(pushop)
3316 super(mqrepo, self).checkpush(pushop)
3317
3317
3318 def _findtags(self):
3318 def _findtags(self):
3319 '''augment tags from base class with patch tags'''
3319 '''augment tags from base class with patch tags'''
3320 result = super(mqrepo, self)._findtags()
3320 result = super(mqrepo, self)._findtags()
3321
3321
3322 q = self.mq
3322 q = self.mq
3323 if not q.applied:
3323 if not q.applied:
3324 return result
3324 return result
3325
3325
3326 mqtags = [(patch.node, patch.name) for patch in q.applied]
3326 mqtags = [(patch.node, patch.name) for patch in q.applied]
3327
3327
3328 try:
3328 try:
3329 # for now ignore filtering business
3329 # for now ignore filtering business
3330 self.unfiltered().changelog.rev(mqtags[-1][0])
3330 self.unfiltered().changelog.rev(mqtags[-1][0])
3331 except error.LookupError:
3331 except error.LookupError:
3332 self.ui.warn(_('mq status file refers to unknown node %s\n')
3332 self.ui.warn(_('mq status file refers to unknown node %s\n')
3333 % short(mqtags[-1][0]))
3333 % short(mqtags[-1][0]))
3334 return result
3334 return result
3335
3335
3336 # do not add fake tags for filtered revisions
3336 # do not add fake tags for filtered revisions
3337 included = self.changelog.hasnode
3337 included = self.changelog.hasnode
3338 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3338 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3339 if not mqtags:
3339 if not mqtags:
3340 return result
3340 return result
3341
3341
3342 mqtags.append((mqtags[-1][0], 'qtip'))
3342 mqtags.append((mqtags[-1][0], 'qtip'))
3343 mqtags.append((mqtags[0][0], 'qbase'))
3343 mqtags.append((mqtags[0][0], 'qbase'))
3344 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3344 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3345 tags = result[0]
3345 tags = result[0]
3346 for patch in mqtags:
3346 for patch in mqtags:
3347 if patch[1] in tags:
3347 if patch[1] in tags:
3348 self.ui.warn(_('tag %s overrides mq patch of the same '
3348 self.ui.warn(_('tag %s overrides mq patch of the same '
3349 'name\n') % patch[1])
3349 'name\n') % patch[1])
3350 else:
3350 else:
3351 tags[patch[1]] = patch[0]
3351 tags[patch[1]] = patch[0]
3352
3352
3353 return result
3353 return result
3354
3354
3355 if repo.local():
3355 if repo.local():
3356 repo.__class__ = mqrepo
3356 repo.__class__ = mqrepo
3357
3357
3358 repo._phasedefaults.append(mqphasedefaults)
3358 repo._phasedefaults.append(mqphasedefaults)
3359
3359
3360 def mqimport(orig, ui, repo, *args, **kwargs):
3360 def mqimport(orig, ui, repo, *args, **kwargs):
3361 if (util.safehasattr(repo, 'abortifwdirpatched')
3361 if (util.safehasattr(repo, 'abortifwdirpatched')
3362 and not kwargs.get('no_commit', False)):
3362 and not kwargs.get('no_commit', False)):
3363 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3363 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3364 kwargs.get('force'))
3364 kwargs.get('force'))
3365 return orig(ui, repo, *args, **kwargs)
3365 return orig(ui, repo, *args, **kwargs)
3366
3366
3367 def mqinit(orig, ui, *args, **kwargs):
3367 def mqinit(orig, ui, *args, **kwargs):
3368 mq = kwargs.pop('mq', None)
3368 mq = kwargs.pop('mq', None)
3369
3369
3370 if not mq:
3370 if not mq:
3371 return orig(ui, *args, **kwargs)
3371 return orig(ui, *args, **kwargs)
3372
3372
3373 if args:
3373 if args:
3374 repopath = args[0]
3374 repopath = args[0]
3375 if not hg.islocal(repopath):
3375 if not hg.islocal(repopath):
3376 raise util.Abort(_('only a local queue repository '
3376 raise util.Abort(_('only a local queue repository '
3377 'may be initialized'))
3377 'may be initialized'))
3378 else:
3378 else:
3379 repopath = cmdutil.findrepo(os.getcwd())
3379 repopath = cmdutil.findrepo(os.getcwd())
3380 if not repopath:
3380 if not repopath:
3381 raise util.Abort(_('there is no Mercurial repository here '
3381 raise util.Abort(_('there is no Mercurial repository here '
3382 '(.hg not found)'))
3382 '(.hg not found)'))
3383 repo = hg.repository(ui, repopath)
3383 repo = hg.repository(ui, repopath)
3384 return qinit(ui, repo, True)
3384 return qinit(ui, repo, True)
3385
3385
3386 def mqcommand(orig, ui, repo, *args, **kwargs):
3386 def mqcommand(orig, ui, repo, *args, **kwargs):
3387 """Add --mq option to operate on patch repository instead of main"""
3387 """Add --mq option to operate on patch repository instead of main"""
3388
3388
3389 # some commands do not like getting unknown options
3389 # some commands do not like getting unknown options
3390 mq = kwargs.pop('mq', None)
3390 mq = kwargs.pop('mq', None)
3391
3391
3392 if not mq:
3392 if not mq:
3393 return orig(ui, repo, *args, **kwargs)
3393 return orig(ui, repo, *args, **kwargs)
3394
3394
3395 q = repo.mq
3395 q = repo.mq
3396 r = q.qrepo()
3396 r = q.qrepo()
3397 if not r:
3397 if not r:
3398 raise util.Abort(_('no queue repository'))
3398 raise util.Abort(_('no queue repository'))
3399 return orig(r.ui, r, *args, **kwargs)
3399 return orig(r.ui, r, *args, **kwargs)
3400
3400
3401 def summaryhook(ui, repo):
3401 def summaryhook(ui, repo):
3402 q = repo.mq
3402 q = repo.mq
3403 m = []
3403 m = []
3404 a, u = len(q.applied), len(q.unapplied(repo))
3404 a, u = len(q.applied), len(q.unapplied(repo))
3405 if a:
3405 if a:
3406 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3406 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3407 if u:
3407 if u:
3408 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3408 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3409 if m:
3409 if m:
3410 # i18n: column positioning for "hg summary"
3410 # i18n: column positioning for "hg summary"
3411 ui.write(_("mq: %s\n") % ', '.join(m))
3411 ui.write(_("mq: %s\n") % ', '.join(m))
3412 else:
3412 else:
3413 # i18n: column positioning for "hg summary"
3413 # i18n: column positioning for "hg summary"
3414 ui.note(_("mq: (empty queue)\n"))
3414 ui.note(_("mq: (empty queue)\n"))
3415
3415
3416 def revsetmq(repo, subset, x):
3416 def revsetmq(repo, subset, x):
3417 """``mq()``
3417 """``mq()``
3418 Changesets managed by MQ.
3418 Changesets managed by MQ.
3419 """
3419 """
3420 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3420 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3421 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3421 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3422 return revset.baseset([r for r in subset if r in applied])
3422 return revset.baseset([r for r in subset if r in applied])
3423
3423
3424 # tell hggettext to extract docstrings from these functions:
3424 # tell hggettext to extract docstrings from these functions:
3425 i18nfunctions = [revsetmq]
3425 i18nfunctions = [revsetmq]
3426
3426
3427 def extsetup(ui):
3427 def extsetup(ui):
3428 # Ensure mq wrappers are called first, regardless of extension load order by
3428 # Ensure mq wrappers are called first, regardless of extension load order by
3429 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3429 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3430 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3430 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3431
3431
3432 extensions.wrapcommand(commands.table, 'import', mqimport)
3432 extensions.wrapcommand(commands.table, 'import', mqimport)
3433 cmdutil.summaryhooks.add('mq', summaryhook)
3433 cmdutil.summaryhooks.add('mq', summaryhook)
3434
3434
3435 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3435 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3436 entry[1].extend(mqopt)
3436 entry[1].extend(mqopt)
3437
3437
3438 nowrap = set(commands.norepo.split(" "))
3438 nowrap = set(commands.norepo.split(" "))
3439
3439
3440 def dotable(cmdtable):
3440 def dotable(cmdtable):
3441 for cmd in cmdtable.keys():
3441 for cmd in cmdtable.keys():
3442 cmd = cmdutil.parsealiases(cmd)[0]
3442 cmd = cmdutil.parsealiases(cmd)[0]
3443 if cmd in nowrap:
3443 if cmd in nowrap:
3444 continue
3444 continue
3445 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3445 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3446 entry[1].extend(mqopt)
3446 entry[1].extend(mqopt)
3447
3447
3448 dotable(commands.table)
3448 dotable(commands.table)
3449
3449
3450 for extname, extmodule in extensions.extensions():
3450 for extname, extmodule in extensions.extensions():
3451 if extmodule.__file__ != __file__:
3451 if extmodule.__file__ != __file__:
3452 dotable(getattr(extmodule, 'cmdtable', {}))
3452 dotable(getattr(extmodule, 'cmdtable', {}))
3453
3453
3454 revset.symbols['mq'] = revsetmq
3454 revset.symbols['mq'] = revsetmq
3455
3455
3456 colortable = {'qguard.negative': 'red',
3456 colortable = {'qguard.negative': 'red',
3457 'qguard.positive': 'yellow',
3457 'qguard.positive': 'yellow',
3458 'qguard.unguarded': 'green',
3458 'qguard.unguarded': 'green',
3459 'qseries.applied': 'blue bold underline',
3459 'qseries.applied': 'blue bold underline',
3460 'qseries.guarded': 'black bold',
3460 'qseries.guarded': 'black bold',
3461 'qseries.missing': 'red bold',
3461 'qseries.missing': 'red bold',
3462 'qseries.unapplied': 'black bold'}
3462 'qseries.unapplied': 'black bold'}
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