##// END OF EJS Templates
merge with stable
Matt Mackall -
r19852:57479e0d merge default
parent child Browse files
Show More

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

1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,79 +1,80
1 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0 iD8DBQBEYmO2ywK+sNU5EO8RAnaYAKCO7x15xUn5mnhqWNXqk/ehlhRt2QCfRDfY0LrUq2q4oK/KypuJYPHgq1A=
1 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0 iD8DBQBEYmO2ywK+sNU5EO8RAnaYAKCO7x15xUn5mnhqWNXqk/ehlhRt2QCfRDfY0LrUq2q4oK/KypuJYPHgq1A=
2 2be3001847cb18a23c403439d9e7d0ace30804e9 0 iD8DBQBExUbjywK+sNU5EO8RAhzxAKCtyHAQUzcTSZTqlfJ0by6vhREwWQCghaQFHfkfN0l9/40EowNhuMOKnJk=
2 2be3001847cb18a23c403439d9e7d0ace30804e9 0 iD8DBQBExUbjywK+sNU5EO8RAhzxAKCtyHAQUzcTSZTqlfJ0by6vhREwWQCghaQFHfkfN0l9/40EowNhuMOKnJk=
3 36a957364b1b89c150f2d0e60a99befe0ee08bd3 0 iD8DBQBFfL2QywK+sNU5EO8RAjYFAKCoGlaWRTeMsjdmxAjUYx6diZxOBwCfY6IpBYsKvPTwB3oktnPt5Rmrlys=
3 36a957364b1b89c150f2d0e60a99befe0ee08bd3 0 iD8DBQBFfL2QywK+sNU5EO8RAjYFAKCoGlaWRTeMsjdmxAjUYx6diZxOBwCfY6IpBYsKvPTwB3oktnPt5Rmrlys=
4 27230c29bfec36d5540fbe1c976810aefecfd1d2 0 iD8DBQBFheweywK+sNU5EO8RAt7VAKCrqJQWT2/uo2RWf0ZI4bLp6v82jACgjrMdsaTbxRsypcmEsdPhlG6/8F4=
4 27230c29bfec36d5540fbe1c976810aefecfd1d2 0 iD8DBQBFheweywK+sNU5EO8RAt7VAKCrqJQWT2/uo2RWf0ZI4bLp6v82jACgjrMdsaTbxRsypcmEsdPhlG6/8F4=
5 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0 iD8DBQBGgHicywK+sNU5EO8RAgNxAJ0VG8ixAaeudx4sZbhngI1syu49HQCeNUJQfWBgA8bkJ2pvsFpNxwYaX3I=
5 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0 iD8DBQBGgHicywK+sNU5EO8RAgNxAJ0VG8ixAaeudx4sZbhngI1syu49HQCeNUJQfWBgA8bkJ2pvsFpNxwYaX3I=
6 23889160905a1b09fffe1c07378e9fc1827606eb 0 iD8DBQBHGTzoywK+sNU5EO8RAr/UAJ0Y8s4jQtzgS+G9vM8z6CWBThZ8fwCcCT5XDj2XwxKkz/0s6UELwjsO3LU=
6 23889160905a1b09fffe1c07378e9fc1827606eb 0 iD8DBQBHGTzoywK+sNU5EO8RAr/UAJ0Y8s4jQtzgS+G9vM8z6CWBThZ8fwCcCT5XDj2XwxKkz/0s6UELwjsO3LU=
7 bae2e9c838e90a393bae3973a7850280413e091a 0 iD8DBQBH6DO5ywK+sNU5EO8RAsfrAJ0e4r9c9GF/MJsM7Xjd3NesLRC3+ACffj6+6HXdZf8cswAoFPO+DY00oD0=
7 bae2e9c838e90a393bae3973a7850280413e091a 0 iD8DBQBH6DO5ywK+sNU5EO8RAsfrAJ0e4r9c9GF/MJsM7Xjd3NesLRC3+ACffj6+6HXdZf8cswAoFPO+DY00oD0=
8 d5cbbe2c49cee22a9fbeb9ea41daa0ac4e26b846 0 iD8DBQBINdwsywK+sNU5EO8RAjIUAKCPmlFJSpsPAAUKF+iNHAwVnwmzeQCdEXrL27CWclXuUKdbQC8De7LICtE=
8 d5cbbe2c49cee22a9fbeb9ea41daa0ac4e26b846 0 iD8DBQBINdwsywK+sNU5EO8RAjIUAKCPmlFJSpsPAAUKF+iNHAwVnwmzeQCdEXrL27CWclXuUKdbQC8De7LICtE=
9 d2375bbee6d47e62ba8e415c86e83a465dc4dce9 0 iD8DBQBIo1wpywK+sNU5EO8RAmRNAJ94x3OFt6blbqu/yBoypm/AJ44fuACfUaldXcV5z9tht97hSp22DVTEPGc=
9 d2375bbee6d47e62ba8e415c86e83a465dc4dce9 0 iD8DBQBIo1wpywK+sNU5EO8RAmRNAJ94x3OFt6blbqu/yBoypm/AJ44fuACfUaldXcV5z9tht97hSp22DVTEPGc=
10 2a67430f92f15ea5159c26b09ec4839a0c549a26 0 iEYEABECAAYFAkk1hykACgkQywK+sNU5EO85QACeNJNUanjc2tl4wUoPHNuv+lSj0ZMAoIm93wSTc/feyYnO2YCaQ1iyd9Nu
10 2a67430f92f15ea5159c26b09ec4839a0c549a26 0 iEYEABECAAYFAkk1hykACgkQywK+sNU5EO85QACeNJNUanjc2tl4wUoPHNuv+lSj0ZMAoIm93wSTc/feyYnO2YCaQ1iyd9Nu
11 3773e510d433969e277b1863c317b674cbee2065 0 iEYEABECAAYFAklNbbAACgkQywK+sNU5EO8o+gCfeb2/lfIJZMvyDA1m+G1CsBAxfFsAoIa6iAMG8SBY7hW1Q85Yf/LXEvaE
11 3773e510d433969e277b1863c317b674cbee2065 0 iEYEABECAAYFAklNbbAACgkQywK+sNU5EO8o+gCfeb2/lfIJZMvyDA1m+G1CsBAxfFsAoIa6iAMG8SBY7hW1Q85Yf/LXEvaE
12 11a4eb81fb4f4742451591489e2797dc47903277 0 iEYEABECAAYFAklcAnsACgkQywK+sNU5EO+uXwCbBVHNNsLy1g7BlAyQJwadYVyHOXoAoKvtAVO71+bv7EbVoukwTzT+P4Sx
12 11a4eb81fb4f4742451591489e2797dc47903277 0 iEYEABECAAYFAklcAnsACgkQywK+sNU5EO+uXwCbBVHNNsLy1g7BlAyQJwadYVyHOXoAoKvtAVO71+bv7EbVoukwTzT+P4Sx
13 11efa41037e280d08cfb07c09ad485df30fb0ea8 0 iEYEABECAAYFAkmvJRQACgkQywK+sNU5EO9XZwCeLMgDgPSMWMm6vgjL4lDs2pEc5+0AnRxfiFbpbBfuEFTqKz9nbzeyoBlx
13 11efa41037e280d08cfb07c09ad485df30fb0ea8 0 iEYEABECAAYFAkmvJRQACgkQywK+sNU5EO9XZwCeLMgDgPSMWMm6vgjL4lDs2pEc5+0AnRxfiFbpbBfuEFTqKz9nbzeyoBlx
14 02981000012e3adf40c4849bd7b3d5618f9ce82d 0 iEYEABECAAYFAknEH3wACgkQywK+sNU5EO+uXwCeI+LbLMmhjU1lKSfU3UWJHjjUC7oAoIZLvYDGOL/tNZFUuatc3RnZ2eje
14 02981000012e3adf40c4849bd7b3d5618f9ce82d 0 iEYEABECAAYFAknEH3wACgkQywK+sNU5EO+uXwCeI+LbLMmhjU1lKSfU3UWJHjjUC7oAoIZLvYDGOL/tNZFUuatc3RnZ2eje
15 196d40e7c885fa6e95f89134809b3ec7bdbca34b 0 iEYEABECAAYFAkpL2X4ACgkQywK+sNU5EO9FOwCfXJycjyKJXsvQqKkHrglwOQhEKS4An36GfKzptfN8b1qNc3+ya/5c2WOM
15 196d40e7c885fa6e95f89134809b3ec7bdbca34b 0 iEYEABECAAYFAkpL2X4ACgkQywK+sNU5EO9FOwCfXJycjyKJXsvQqKkHrglwOQhEKS4An36GfKzptfN8b1qNc3+ya/5c2WOM
16 3ef6c14a1e8e83a31226f5881b7fe6095bbfa6f6 0 iEYEABECAAYFAkpopLIACgkQywK+sNU5EO8QSgCfZ0ztsd071rOa2lhmp9Fyue/WoI0AoLTei80/xrhRlB8L/rZEf2KBl8dA
16 3ef6c14a1e8e83a31226f5881b7fe6095bbfa6f6 0 iEYEABECAAYFAkpopLIACgkQywK+sNU5EO8QSgCfZ0ztsd071rOa2lhmp9Fyue/WoI0AoLTei80/xrhRlB8L/rZEf2KBl8dA
17 31ec469f9b556f11819937cf68ee53f2be927ebf 0 iEYEABECAAYFAksBuxAACgkQywK+sNU5EO+mBwCfagB+A0txzWZ6dRpug3LEoK7Z1QsAoKpbk8vsLjv6/oRDicSk/qBu33+m
17 31ec469f9b556f11819937cf68ee53f2be927ebf 0 iEYEABECAAYFAksBuxAACgkQywK+sNU5EO+mBwCfagB+A0txzWZ6dRpug3LEoK7Z1QsAoKpbk8vsLjv6/oRDicSk/qBu33+m
18 439d7ea6fe3aa4ab9ec274a68846779153789de9 0 iEYEABECAAYFAksVw0kACgkQywK+sNU5EO/oZwCfdfBEkgp38xq6wN2F4nj+SzofrJIAnjmxt04vaJSeOOeHylHvk6lzuQsw
18 439d7ea6fe3aa4ab9ec274a68846779153789de9 0 iEYEABECAAYFAksVw0kACgkQywK+sNU5EO/oZwCfdfBEkgp38xq6wN2F4nj+SzofrJIAnjmxt04vaJSeOOeHylHvk6lzuQsw
19 296a0b14a68621f6990c54fdba0083f6f20935bf 0 iEYEABECAAYFAks+jCoACgkQywK+sNU5EO9J8wCeMUGF9E/gS2UBsqIz56WS4HMPRPUAoI5J95mwEIK8Clrl7qFRidNI6APq
19 296a0b14a68621f6990c54fdba0083f6f20935bf 0 iEYEABECAAYFAks+jCoACgkQywK+sNU5EO9J8wCeMUGF9E/gS2UBsqIz56WS4HMPRPUAoI5J95mwEIK8Clrl7qFRidNI6APq
20 4aa619c4c2c09907034d9824ebb1dd0e878206eb 0 iEYEABECAAYFAktm9IsACgkQywK+sNU5EO9XGgCgk4HclRQhexEtooPE5GcUCdB6M8EAn2ptOhMVbIoO+JncA+tNACPFXh0O
20 4aa619c4c2c09907034d9824ebb1dd0e878206eb 0 iEYEABECAAYFAktm9IsACgkQywK+sNU5EO9XGgCgk4HclRQhexEtooPE5GcUCdB6M8EAn2ptOhMVbIoO+JncA+tNACPFXh0O
21 ff2704a8ded37fbebd8b6eb5ec733731d725da8a 0 iEYEABECAAYFAkuRoSQACgkQywK+sNU5EO//3QCeJDc5r2uFyFCtAlpSA27DEE5rrxAAn2FSwTy9fhrB3QAdDQlwkEZcQzDh
21 ff2704a8ded37fbebd8b6eb5ec733731d725da8a 0 iEYEABECAAYFAkuRoSQACgkQywK+sNU5EO//3QCeJDc5r2uFyFCtAlpSA27DEE5rrxAAn2FSwTy9fhrB3QAdDQlwkEZcQzDh
22 2b01dab594167bc0dd33331dbaa6dca3dca1b3aa 0 iEYEABECAAYFAku1IwIACgkQywK+sNU5EO9MjgCdHLVwkTZlNHxhcznZKBL1rjN+J7cAoLLWi9LTL6f/TgBaPSKOy1ublbaW
22 2b01dab594167bc0dd33331dbaa6dca3dca1b3aa 0 iEYEABECAAYFAku1IwIACgkQywK+sNU5EO9MjgCdHLVwkTZlNHxhcznZKBL1rjN+J7cAoLLWi9LTL6f/TgBaPSKOy1ublbaW
23 39f725929f0c48c5fb3b90c071fc3066012456ca 0 iEYEABECAAYFAkvclvsACgkQywK+sNU5EO9FSwCeL9i5x8ALW/LE5+lCX6MFEAe4MhwAn1ev5o6SX6GrNdDfKweiemfO2VBk
23 39f725929f0c48c5fb3b90c071fc3066012456ca 0 iEYEABECAAYFAkvclvsACgkQywK+sNU5EO9FSwCeL9i5x8ALW/LE5+lCX6MFEAe4MhwAn1ev5o6SX6GrNdDfKweiemfO2VBk
24 fdcf80f26604f233dc4d8f0a5ef9d7470e317e8a 0 iEYEABECAAYFAkvsKTkACgkQywK+sNU5EO9qEACgiSiRGvTG2vXGJ65tUSOIYihTuFAAnRzRIqEVSw8M8/RGeUXRps0IzaCO
24 fdcf80f26604f233dc4d8f0a5ef9d7470e317e8a 0 iEYEABECAAYFAkvsKTkACgkQywK+sNU5EO9qEACgiSiRGvTG2vXGJ65tUSOIYihTuFAAnRzRIqEVSw8M8/RGeUXRps0IzaCO
25 24fe2629c6fd0c74c90bd066e77387c2b02e8437 0 iEYEABECAAYFAkwFLRsACgkQywK+sNU5EO+pJACgp13tPI+pbwKZV+LeMjcQ4H6tCZYAoJebzhd6a8yYx6qiwpJxA9BXZNXy
25 24fe2629c6fd0c74c90bd066e77387c2b02e8437 0 iEYEABECAAYFAkwFLRsACgkQywK+sNU5EO+pJACgp13tPI+pbwKZV+LeMjcQ4H6tCZYAoJebzhd6a8yYx6qiwpJxA9BXZNXy
26 f786fc4b8764cd2a5526d259cf2f94d8a66924d9 0 iEYEABECAAYFAkwsyxcACgkQywK+sNU5EO+crACfUpNAF57PmClkSri9nJcBjb2goN4AniPCNaKvnki7TnUsi1u2oxltpKKL
26 f786fc4b8764cd2a5526d259cf2f94d8a66924d9 0 iEYEABECAAYFAkwsyxcACgkQywK+sNU5EO+crACfUpNAF57PmClkSri9nJcBjb2goN4AniPCNaKvnki7TnUsi1u2oxltpKKL
27 bf1774d95bde614af3956d92b20e2a0c68c5fec7 0 iEYEABECAAYFAkxVwccACgkQywK+sNU5EO+oFQCeJzwZ+we1fIIyBGCddHceOUAN++cAnjvT6A8ZWW0zV21NXIFF1qQmjxJd
27 bf1774d95bde614af3956d92b20e2a0c68c5fec7 0 iEYEABECAAYFAkxVwccACgkQywK+sNU5EO+oFQCeJzwZ+we1fIIyBGCddHceOUAN++cAnjvT6A8ZWW0zV21NXIFF1qQmjxJd
28 c00f03a4982e467fb6b6bd45908767db6df4771d 0 iEYEABECAAYFAkxXDqsACgkQywK+sNU5EO/GJACfT9Rz4hZOxPQEs91JwtmfjevO84gAmwSmtfo5mmWSm8gtTUebCcdTv0Kf
28 c00f03a4982e467fb6b6bd45908767db6df4771d 0 iEYEABECAAYFAkxXDqsACgkQywK+sNU5EO/GJACfT9Rz4hZOxPQEs91JwtmfjevO84gAmwSmtfo5mmWSm8gtTUebCcdTv0Kf
29 ff5cec76b1c5b6be9c3bb923aae8c3c6d079d6b9 0 iD8DBQBMdo+qywK+sNU5EO8RAqQpAJ975BL2CCAiWMz9SXthNQ9xG181IwCgp4O+KViHPkufZVFn2aTKMNvcr1A=
29 ff5cec76b1c5b6be9c3bb923aae8c3c6d079d6b9 0 iD8DBQBMdo+qywK+sNU5EO8RAqQpAJ975BL2CCAiWMz9SXthNQ9xG181IwCgp4O+KViHPkufZVFn2aTKMNvcr1A=
30 93d8bff78c96fe7e33237b257558ee97290048a4 0 iD8DBQBMpfvdywK+sNU5EO8RAsxVAJ0UaL1XB51C76JUBhafc9GBefuMxwCdEWkTOzwvE0SarJBe9i008jhbqW4=
30 93d8bff78c96fe7e33237b257558ee97290048a4 0 iD8DBQBMpfvdywK+sNU5EO8RAsxVAJ0UaL1XB51C76JUBhafc9GBefuMxwCdEWkTOzwvE0SarJBe9i008jhbqW4=
31 333421b9e0f96c7bc788e5667c146a58a9440a55 0 iD8DBQBMz0HOywK+sNU5EO8RAlsEAJ0USh6yOG7OrWkADGunVt9QimBQnwCbBqeMnKgSbwEw8jZwE3Iz1mdrYlo=
31 333421b9e0f96c7bc788e5667c146a58a9440a55 0 iD8DBQBMz0HOywK+sNU5EO8RAlsEAJ0USh6yOG7OrWkADGunVt9QimBQnwCbBqeMnKgSbwEw8jZwE3Iz1mdrYlo=
32 4438875ec01bd0fc32be92b0872eb6daeed4d44f 0 iD8DBQBM4WYUywK+sNU5EO8RAhCVAJ0dJswachwFAHALmk1x0RJehxzqPQCbBNskP9n/X689jB+btNTZTyKU/fw=
32 4438875ec01bd0fc32be92b0872eb6daeed4d44f 0 iD8DBQBM4WYUywK+sNU5EO8RAhCVAJ0dJswachwFAHALmk1x0RJehxzqPQCbBNskP9n/X689jB+btNTZTyKU/fw=
33 6aff4f144ad356311318b0011df0bb21f2c97429 0 iD8DBQBM9uxXywK+sNU5EO8RAv+4AKCDj4qKP16GdPaq1tP6BUwpM/M1OACfRyzLPp/qiiN8xJTWoWYSe/XjJug=
33 6aff4f144ad356311318b0011df0bb21f2c97429 0 iD8DBQBM9uxXywK+sNU5EO8RAv+4AKCDj4qKP16GdPaq1tP6BUwpM/M1OACfRyzLPp/qiiN8xJTWoWYSe/XjJug=
34 e3bf16703e2601de99e563cdb3a5d50b64e6d320 0 iD8DBQBNH8WqywK+sNU5EO8RAiQTAJ9sBO+TeiGro4si77VVaQaA6jcRUgCfSA28dBbjj0oFoQwvPoZjANiZBH8=
34 e3bf16703e2601de99e563cdb3a5d50b64e6d320 0 iD8DBQBNH8WqywK+sNU5EO8RAiQTAJ9sBO+TeiGro4si77VVaQaA6jcRUgCfSA28dBbjj0oFoQwvPoZjANiZBH8=
35 a6c855c32ea081da3c3b8ff628f1847ff271482f 0 iD8DBQBNSJJ+ywK+sNU5EO8RAoJaAKCweDEF70fu+r1Zn7pYDXdlk5RuSgCeO9gK/eit8Lin/1n3pO7aYguFLok=
35 a6c855c32ea081da3c3b8ff628f1847ff271482f 0 iD8DBQBNSJJ+ywK+sNU5EO8RAoJaAKCweDEF70fu+r1Zn7pYDXdlk5RuSgCeO9gK/eit8Lin/1n3pO7aYguFLok=
36 2b2155623ee2559caf288fd333f30475966c4525 0 iD8DBQBNSJeBywK+sNU5EO8RAm1KAJ4hW9Cm9nHaaGJguchBaPLlAr+O3wCgqgmMok8bdAS06N6PL60PSTM//Gg=
36 2b2155623ee2559caf288fd333f30475966c4525 0 iD8DBQBNSJeBywK+sNU5EO8RAm1KAJ4hW9Cm9nHaaGJguchBaPLlAr+O3wCgqgmMok8bdAS06N6PL60PSTM//Gg=
37 2616325766e3504c8ae7c84bd15ee610901fe91d 0 iD8DBQBNbWy9ywK+sNU5EO8RAlWCAJ4mW8HbzjJj9GpK98muX7k+7EvEHwCfaTLbC/DH3QEsZBhEP+M8tzL6RU4=
37 2616325766e3504c8ae7c84bd15ee610901fe91d 0 iD8DBQBNbWy9ywK+sNU5EO8RAlWCAJ4mW8HbzjJj9GpK98muX7k+7EvEHwCfaTLbC/DH3QEsZBhEP+M8tzL6RU4=
38 aa1f3be38ab127280761889d2dca906ca465b5f4 0 iD8DBQBNeQq7ywK+sNU5EO8RAlEOAJ4tlEDdetE9lKfjGgjbkcR8PrC3egCfXCfF3qNVvU/2YYjpgvRwevjvDy0=
38 aa1f3be38ab127280761889d2dca906ca465b5f4 0 iD8DBQBNeQq7ywK+sNU5EO8RAlEOAJ4tlEDdetE9lKfjGgjbkcR8PrC3egCfXCfF3qNVvU/2YYjpgvRwevjvDy0=
39 b032bec2c0a651ca0ddecb65714bfe6770f67d70 0 iD8DBQBNlg5kywK+sNU5EO8RAnGEAJ9gmEx6MfaR4XcG2m/93vwtfyzs3gCgltzx8/YdHPwqDwRX/WbpYgi33is=
39 b032bec2c0a651ca0ddecb65714bfe6770f67d70 0 iD8DBQBNlg5kywK+sNU5EO8RAnGEAJ9gmEx6MfaR4XcG2m/93vwtfyzs3gCgltzx8/YdHPwqDwRX/WbpYgi33is=
40 3cb1e95676ad089596bd81d0937cad37d6e3b7fb 0 iD8DBQBNvTy4ywK+sNU5EO8RAmp8AJ9QnxK4jTJ7G722MyeBxf0UXEdGwACgtlM7BKtNQfbEH/fOW5y+45W88VI=
40 3cb1e95676ad089596bd81d0937cad37d6e3b7fb 0 iD8DBQBNvTy4ywK+sNU5EO8RAmp8AJ9QnxK4jTJ7G722MyeBxf0UXEdGwACgtlM7BKtNQfbEH/fOW5y+45W88VI=
41 733af5d9f6b22387913e1d11350fb8cb7c1487dd 0 iD8DBQBN5q/8ywK+sNU5EO8RArRGAKCNGT94GKIYtSuwZ57z1sQbcw6uLACfffpbMV4NAPMl8womAwg+7ZPKnIU=
41 733af5d9f6b22387913e1d11350fb8cb7c1487dd 0 iD8DBQBN5q/8ywK+sNU5EO8RArRGAKCNGT94GKIYtSuwZ57z1sQbcw6uLACfffpbMV4NAPMl8womAwg+7ZPKnIU=
42 de9eb6b1da4fc522b1cab16d86ca166204c24f25 0 iD8DBQBODhfhywK+sNU5EO8RAr2+AJ4ugbAj8ae8/K0bYZzx3sascIAg1QCeK3b+zbbVVqd3b7CDpwFnaX8kTd4=
42 de9eb6b1da4fc522b1cab16d86ca166204c24f25 0 iD8DBQBODhfhywK+sNU5EO8RAr2+AJ4ugbAj8ae8/K0bYZzx3sascIAg1QCeK3b+zbbVVqd3b7CDpwFnaX8kTd4=
43 4a43e23b8c55b4566b8200bf69fe2158485a2634 0 iD8DBQBONzIMywK+sNU5EO8RAj5SAJ0aPS3+JHnyI6bHB2Fl0LImbDmagwCdGbDLp1S7TFobxXudOH49bX45Iik=
43 4a43e23b8c55b4566b8200bf69fe2158485a2634 0 iD8DBQBONzIMywK+sNU5EO8RAj5SAJ0aPS3+JHnyI6bHB2Fl0LImbDmagwCdGbDLp1S7TFobxXudOH49bX45Iik=
44 d629f1e89021103f1753addcef6b310e4435b184 0 iD8DBQBOWAsBywK+sNU5EO8RAht4AJwJl9oNFopuGkj5m8aKuf7bqPkoAQCeNrEm7UhFsZKYT5iUOjnMV7s2LaM=
44 d629f1e89021103f1753addcef6b310e4435b184 0 iD8DBQBOWAsBywK+sNU5EO8RAht4AJwJl9oNFopuGkj5m8aKuf7bqPkoAQCeNrEm7UhFsZKYT5iUOjnMV7s2LaM=
45 351a9292e430e35766c552066ed3e87c557b803b 0 iD8DBQBOh3zUywK+sNU5EO8RApFMAKCD3Y/u3avDFndznwqfG5UeTHMlvACfUivPIVQZyDZnhZMq0UhC6zhCEQg=
45 351a9292e430e35766c552066ed3e87c557b803b 0 iD8DBQBOh3zUywK+sNU5EO8RApFMAKCD3Y/u3avDFndznwqfG5UeTHMlvACfUivPIVQZyDZnhZMq0UhC6zhCEQg=
46 384082750f2c51dc917d85a7145748330fa6ef4d 0 iD8DBQBOmd+OywK+sNU5EO8RAgDgAJ9V/X+G7VLwhTpHrZNiOHabzSyzYQCdE2kKfIevJUYB9QLAWCWP6DPwrwI=
46 384082750f2c51dc917d85a7145748330fa6ef4d 0 iD8DBQBOmd+OywK+sNU5EO8RAgDgAJ9V/X+G7VLwhTpHrZNiOHabzSyzYQCdE2kKfIevJUYB9QLAWCWP6DPwrwI=
47 41453d55b481ddfcc1dacb445179649e24ca861d 0 iD8DBQBOsFhpywK+sNU5EO8RAqM6AKCyfxUae3/zLuiLdQz+JR78690eMACfQ6JTBQib4AbE+rUDdkeFYg9K/+4=
47 41453d55b481ddfcc1dacb445179649e24ca861d 0 iD8DBQBOsFhpywK+sNU5EO8RAqM6AKCyfxUae3/zLuiLdQz+JR78690eMACfQ6JTBQib4AbE+rUDdkeFYg9K/+4=
48 195dbd1cef0c2f9f8bcf4ea303238105f716bda3 0 iD8DBQBO1/fWywK+sNU5EO8RAmoPAKCR5lpv1D6JLURHD8KVLSV4GRVEBgCgnd0Sy78ligNfqAMafmACRDvj7vo=
48 195dbd1cef0c2f9f8bcf4ea303238105f716bda3 0 iD8DBQBO1/fWywK+sNU5EO8RAmoPAKCR5lpv1D6JLURHD8KVLSV4GRVEBgCgnd0Sy78ligNfqAMafmACRDvj7vo=
49 6344043924497cd06d781d9014c66802285072e4 0 iD8DBQBPALgmywK+sNU5EO8RAlfhAJ9nYOdWnhfVDHYtDTJAyJtXBAQS9wCgnefoSQt7QABkbGxM+Q85UYEBuD0=
49 6344043924497cd06d781d9014c66802285072e4 0 iD8DBQBPALgmywK+sNU5EO8RAlfhAJ9nYOdWnhfVDHYtDTJAyJtXBAQS9wCgnefoSQt7QABkbGxM+Q85UYEBuD0=
50 db33555eafeaf9df1e18950e29439eaa706d399b 0 iD8DBQBPGdzxywK+sNU5EO8RAppkAJ9jOXhUVE/97CPgiMA0pMGiIYnesQCfengAszcBiSiKGugiI8Okc9ghU+Y=
50 db33555eafeaf9df1e18950e29439eaa706d399b 0 iD8DBQBPGdzxywK+sNU5EO8RAppkAJ9jOXhUVE/97CPgiMA0pMGiIYnesQCfengAszcBiSiKGugiI8Okc9ghU+Y=
51 2aa5b51f310fb3befd26bed99c02267f5c12c734 0 iD8DBQBPKZ9bywK+sNU5EO8RAt1TAJ45r1eJ0YqSkInzrrayg4TVCh0SnQCgm0GA/Ua74jnnDwVQ60lAwROuz1Q=
51 2aa5b51f310fb3befd26bed99c02267f5c12c734 0 iD8DBQBPKZ9bywK+sNU5EO8RAt1TAJ45r1eJ0YqSkInzrrayg4TVCh0SnQCgm0GA/Ua74jnnDwVQ60lAwROuz1Q=
52 53e2cd303ecf8ca7c7eeebd785c34e5ed6b0f4a4 0 iD8DBQBPT/fvywK+sNU5EO8RAnfYAKCn7d0vwqIb100YfWm1F7nFD5B+FACeM02YHpQLSNsztrBCObtqcnfod7Q=
52 53e2cd303ecf8ca7c7eeebd785c34e5ed6b0f4a4 0 iD8DBQBPT/fvywK+sNU5EO8RAnfYAKCn7d0vwqIb100YfWm1F7nFD5B+FACeM02YHpQLSNsztrBCObtqcnfod7Q=
53 b9bd95e61b49c221c4cca24e6da7c946fc02f992 0 iD8DBQBPeLsIywK+sNU5EO8RAvpNAKCtKe2gitz8dYn52IRF0hFOPCR7AQCfRJL/RWCFweu2T1vH/mUOCf8SXXc=
53 b9bd95e61b49c221c4cca24e6da7c946fc02f992 0 iD8DBQBPeLsIywK+sNU5EO8RAvpNAKCtKe2gitz8dYn52IRF0hFOPCR7AQCfRJL/RWCFweu2T1vH/mUOCf8SXXc=
54 d9e2f09d5488c395ae9ddbb320ceacd24757e055 0 iD8DBQBPju/dywK+sNU5EO8RArBYAJ9xtifdbk+hCOJO8OZa4JfHX8OYZQCeKPMBaBWiT8N/WHoOm1XU0q+iono=
54 d9e2f09d5488c395ae9ddbb320ceacd24757e055 0 iD8DBQBPju/dywK+sNU5EO8RArBYAJ9xtifdbk+hCOJO8OZa4JfHX8OYZQCeKPMBaBWiT8N/WHoOm1XU0q+iono=
55 00182b3d087909e3c3ae44761efecdde8f319ef3 0 iD8DBQBPoFhIywK+sNU5EO8RAhzhAKCBj1n2jxPTkZNJJ5pSp3soa+XHIgCgsZZpAQxOpXwCp0eCdNGe0+pmxmg=
55 00182b3d087909e3c3ae44761efecdde8f319ef3 0 iD8DBQBPoFhIywK+sNU5EO8RAhzhAKCBj1n2jxPTkZNJJ5pSp3soa+XHIgCgsZZpAQxOpXwCp0eCdNGe0+pmxmg=
56 5983de86462c5a9f42a3ad0f5e90ce5b1d221d25 0 iD8DBQBPovNWywK+sNU5EO8RAhgiAJ980T91FdPTRMmVONDhpkMsZwVIMACgg3bKvoWSeuCW28llUhAJtUjrMv0=
56 5983de86462c5a9f42a3ad0f5e90ce5b1d221d25 0 iD8DBQBPovNWywK+sNU5EO8RAhgiAJ980T91FdPTRMmVONDhpkMsZwVIMACgg3bKvoWSeuCW28llUhAJtUjrMv0=
57 85a358df5bbbe404ca25730c9c459b34263441dc 0 iD8DBQBPyZsWywK+sNU5EO8RAnpLAJ48qrGDJRT+pteS0mSQ11haqHstPwCdG4ccGbk+0JHb7aNy8/NRGAOqn9w=
57 85a358df5bbbe404ca25730c9c459b34263441dc 0 iD8DBQBPyZsWywK+sNU5EO8RAnpLAJ48qrGDJRT+pteS0mSQ11haqHstPwCdG4ccGbk+0JHb7aNy8/NRGAOqn9w=
58 b013baa3898e117959984fc64c29d8c784d2f28b 0 iD8DBQBP8QOPywK+sNU5EO8RAqimAKCFRSx0lvG6y8vne2IhNG062Hn0dACeMLI5/zhpWpHBIVeAAquYfx2XFeA=
58 b013baa3898e117959984fc64c29d8c784d2f28b 0 iD8DBQBP8QOPywK+sNU5EO8RAqimAKCFRSx0lvG6y8vne2IhNG062Hn0dACeMLI5/zhpWpHBIVeAAquYfx2XFeA=
59 7f5094bb3f423fc799e471aac2aee81a7ce57a0b 0 iD8DBQBQGiL8ywK+sNU5EO8RAq5oAJ4rMMCPx6O+OuzNXVOexogedWz/QgCeIiIxLd76I4pXO48tdXhr0hQcBuM=
59 7f5094bb3f423fc799e471aac2aee81a7ce57a0b 0 iD8DBQBQGiL8ywK+sNU5EO8RAq5oAJ4rMMCPx6O+OuzNXVOexogedWz/QgCeIiIxLd76I4pXO48tdXhr0hQcBuM=
60 072209ae4ddb654eb2d5fd35bff358c738414432 0 iD8DBQBQQkq0ywK+sNU5EO8RArDTAJ9nk5CySnNAjAXYvqvx4uWCw9ThZwCgqmFRehH/l+oTwj3f8nw8u8qTCdc=
60 072209ae4ddb654eb2d5fd35bff358c738414432 0 iD8DBQBQQkq0ywK+sNU5EO8RArDTAJ9nk5CySnNAjAXYvqvx4uWCw9ThZwCgqmFRehH/l+oTwj3f8nw8u8qTCdc=
61 b3f0f9a39c4e1d0250048cd803ab03542d6f140a 0 iD8DBQBQamltywK+sNU5EO8RAlsqAJ4qF/m6aFu4mJCOKTiAP5RvZFK02ACfawYShUZO6OXEFfveU0aAxDR0M1k=
61 b3f0f9a39c4e1d0250048cd803ab03542d6f140a 0 iD8DBQBQamltywK+sNU5EO8RAlsqAJ4qF/m6aFu4mJCOKTiAP5RvZFK02ACfawYShUZO6OXEFfveU0aAxDR0M1k=
62 d118a4f4fd16d9b558ec3f3e87bfee772861d2b7 0 iD8DBQBQgPV5ywK+sNU5EO8RArylAJ0abcx5NlDjyv3ZDWpAfRIHyRsJtQCgn4TMuEayqgxzrvadQZHdTEU2g38=
62 d118a4f4fd16d9b558ec3f3e87bfee772861d2b7 0 iD8DBQBQgPV5ywK+sNU5EO8RArylAJ0abcx5NlDjyv3ZDWpAfRIHyRsJtQCgn4TMuEayqgxzrvadQZHdTEU2g38=
63 195ad823b5d58c68903a6153a25e3fb4ed25239d 0 iD8DBQBQkuT9ywK+sNU5EO8RAhB4AKCeerItoK2Jipm2cVf4euGofAa/WACeJj3TVd4pFILpb+ogj7ebweFLJi0=
63 195ad823b5d58c68903a6153a25e3fb4ed25239d 0 iD8DBQBQkuT9ywK+sNU5EO8RAhB4AKCeerItoK2Jipm2cVf4euGofAa/WACeJj3TVd4pFILpb+ogj7ebweFLJi0=
64 0c10cf8191469e7c3c8844922e17e71a176cb7cb 0 iD8DBQBQvQWoywK+sNU5EO8RAnq3AJoCn98u4geFx5YaQaeh99gFhCd7bQCgjoBwBSUyOvGd0yBy60E3Vv3VZhM=
64 0c10cf8191469e7c3c8844922e17e71a176cb7cb 0 iD8DBQBQvQWoywK+sNU5EO8RAnq3AJoCn98u4geFx5YaQaeh99gFhCd7bQCgjoBwBSUyOvGd0yBy60E3Vv3VZhM=
65 a4765077b65e6ae29ba42bab7834717b5072d5ba 0 iD8DBQBQ486sywK+sNU5EO8RAhmJAJ90aLfLKZhmcZN7kqphigQJxiFOQACeJ5IUZxjGKH4xzi3MrgIcx9n+dB0=
65 a4765077b65e6ae29ba42bab7834717b5072d5ba 0 iD8DBQBQ486sywK+sNU5EO8RAhmJAJ90aLfLKZhmcZN7kqphigQJxiFOQACeJ5IUZxjGKH4xzi3MrgIcx9n+dB0=
66 f5fbe15ca7449f2c9a3cf817c86d0ae68b307214 0 iD8DBQBQ+yuYywK+sNU5EO8RAm9JAJoD/UciWvpGeKBcpGtZJBFJVcL/HACghDXSgQ+xQDjB+6uGrdgAQsRR1Lg=
66 f5fbe15ca7449f2c9a3cf817c86d0ae68b307214 0 iD8DBQBQ+yuYywK+sNU5EO8RAm9JAJoD/UciWvpGeKBcpGtZJBFJVcL/HACghDXSgQ+xQDjB+6uGrdgAQsRR1Lg=
67 a6088c05e43a8aee0472ca3a4f6f8d7dd914ebbf 0 iD8DBQBRDDROywK+sNU5EO8RAh75AJ9uJCGoCWnP0Lv/+XuYs4hvUl+sAgCcD36QgAnuw8IQXrvv684BAXAnHcA=
67 a6088c05e43a8aee0472ca3a4f6f8d7dd914ebbf 0 iD8DBQBRDDROywK+sNU5EO8RAh75AJ9uJCGoCWnP0Lv/+XuYs4hvUl+sAgCcD36QgAnuw8IQXrvv684BAXAnHcA=
68 7511d4df752e61fe7ae4f3682e0a0008573b0402 0 iD8DBQBRFYaoywK+sNU5EO8RAuErAJoDyhXn+lptU3+AevVdwAIeNFyR2gCdHzPHyWd+JDeWCUR+pSOBi8O2ppM=
68 7511d4df752e61fe7ae4f3682e0a0008573b0402 0 iD8DBQBRFYaoywK+sNU5EO8RAuErAJoDyhXn+lptU3+AevVdwAIeNFyR2gCdHzPHyWd+JDeWCUR+pSOBi8O2ppM=
69 5b7175377babacce80a6c1e12366d8032a6d4340 0 iD8DBQBRMCYgywK+sNU5EO8RAq1/AKCWKlt9ysibyQgYwoxxIOZv5J8rpwCcDSHQaaf1fFZUTnQsOePwcM2Y/Sg=
69 5b7175377babacce80a6c1e12366d8032a6d4340 0 iD8DBQBRMCYgywK+sNU5EO8RAq1/AKCWKlt9ysibyQgYwoxxIOZv5J8rpwCcDSHQaaf1fFZUTnQsOePwcM2Y/Sg=
70 50c922c1b5145dab8baefefb0437d363b6a6c21c 0 iD8DBQBRWnUnywK+sNU5EO8RAuQRAJwM42cJqJPeqJ0jVNdMqKMDqr4dSACeP0cRVGz1gitMuV0x8f3mrZrqc7I=
70 50c922c1b5145dab8baefefb0437d363b6a6c21c 0 iD8DBQBRWnUnywK+sNU5EO8RAuQRAJwM42cJqJPeqJ0jVNdMqKMDqr4dSACeP0cRVGz1gitMuV0x8f3mrZrqc7I=
71 8a7bd2dccd44ed571afe7424cd7f95594f27c092 0 iD8DBQBRXfBvywK+sNU5EO8RAn+LAKCsMmflbuXjYRxlzFwId5ptm8TZcwCdGkyLbZcASBOkzQUm/WW1qfknJHU=
71 8a7bd2dccd44ed571afe7424cd7f95594f27c092 0 iD8DBQBRXfBvywK+sNU5EO8RAn+LAKCsMmflbuXjYRxlzFwId5ptm8TZcwCdGkyLbZcASBOkzQUm/WW1qfknJHU=
72 292cd385856d98bacb2c3086f8897bc660c2beea 0 iD8DBQBRcM0BywK+sNU5EO8RAjp4AKCJBykQbvXhKuvLSMxKx3a2TBiXcACfbr/kLg5GlZTF/XDPmY+PyHgI/GM=
72 292cd385856d98bacb2c3086f8897bc660c2beea 0 iD8DBQBRcM0BywK+sNU5EO8RAjp4AKCJBykQbvXhKuvLSMxKx3a2TBiXcACfbr/kLg5GlZTF/XDPmY+PyHgI/GM=
73 23f785b38af38d2fca6b8f3db56b8007a84cd73a 0 iD8DBQBRgZwNywK+sNU5EO8RAmO4AJ4u2ILGuimRP6MJgE2t65LZ5dAdkACgiENEstIdrlFC80p+sWKD81kKIYI=
73 23f785b38af38d2fca6b8f3db56b8007a84cd73a 0 iD8DBQBRgZwNywK+sNU5EO8RAmO4AJ4u2ILGuimRP6MJgE2t65LZ5dAdkACgiENEstIdrlFC80p+sWKD81kKIYI=
74 ddc7a6be20212d18f3e27d9d7e6f079a66d96f21 0 iD8DBQBRkswvywK+sNU5EO8RAiYYAJsHTHyHbJeAgmGvBTmDrfcKu4doUgCeLm7eGBjx7yAPUvEtxef8rAkQmXI=
74 ddc7a6be20212d18f3e27d9d7e6f079a66d96f21 0 iD8DBQBRkswvywK+sNU5EO8RAiYYAJsHTHyHbJeAgmGvBTmDrfcKu4doUgCeLm7eGBjx7yAPUvEtxef8rAkQmXI=
75 cceaf7af4c9e9e6fa2dbfdcfe9856c5da69c4ffd 0 iD8DBQBRqnFLywK+sNU5EO8RAsWNAJ9RR6t+y1DLFc2HeH0eN9VfZAKF9gCeJ8ezvhtKq/LMs0/nvcgKQc/d5jk=
75 cceaf7af4c9e9e6fa2dbfdcfe9856c5da69c4ffd 0 iD8DBQBRqnFLywK+sNU5EO8RAsWNAJ9RR6t+y1DLFc2HeH0eN9VfZAKF9gCeJ8ezvhtKq/LMs0/nvcgKQc/d5jk=
76 009794acc6e37a650f0fae37872e733382ac1c0c 0 iD8DBQBR0guxywK+sNU5EO8RArNkAKCq9pMihVzP8Os5kCmgbWpe5C37wgCgqzuPZTHvAsXF5wTyaSTMVa9Ccq4=
76 009794acc6e37a650f0fae37872e733382ac1c0c 0 iD8DBQBR0guxywK+sNU5EO8RArNkAKCq9pMihVzP8Os5kCmgbWpe5C37wgCgqzuPZTHvAsXF5wTyaSTMVa9Ccq4=
77 f0d7721d7322dcfb5af33599c2543f27335334bb 0 iD8DBQBR8taaywK+sNU5EO8RAqeEAJ4idDhhDuEsgsUjeQgWNj498matHACfT67gSF5w0ylsrBx1Hb52HkGXDm0=
77 f0d7721d7322dcfb5af33599c2543f27335334bb 0 iD8DBQBR8taaywK+sNU5EO8RAqeEAJ4idDhhDuEsgsUjeQgWNj498matHACfT67gSF5w0ylsrBx1Hb52HkGXDm0=
78 f37b5a17e6a0ee17afde2cdde5393dd74715fb58 0 iD8DBQBR+ymFywK+sNU5EO8RAuSdAJkBMcd9DAZ3rWE9WGKPm2YZ8LBoXACfXn/wbEsVy7ZgJoUwiWmHSnQaWCI=
78 f37b5a17e6a0ee17afde2cdde5393dd74715fb58 0 iD8DBQBR+ymFywK+sNU5EO8RAuSdAJkBMcd9DAZ3rWE9WGKPm2YZ8LBoXACfXn/wbEsVy7ZgJoUwiWmHSnQaWCI=
79 335a558f81dc73afeab4d7be63617392b130117f 0 iQIVAwUAUiZrIyBXgaxoKi1yAQK2iw//cquNqqSkc8Re5/TZT9I6NH+lh6DbOKjJP0Xl1Wqq0K+KSIUgZG4G32ovaEb2l5X0uY+3unRPiZ0ebl0YSw4Fb2ZiPIADXLBTOYRrY2Wwd3tpJeGI6wEgZt3SfcITV/g7NJrCjT3FlYoSOIayrExM80InSdcEM0Q3Rx6HKzY2acyxzgZeAtAW5ohFvHilSvY6p5Gcm4+QptMxvw45GPdreUmjeXZxNXNXZ8P+MjMz/QJbai/N7PjmK8lqnhkBsT48Ng/KhhmOkGntNJ2/ImBWLFGcWngSvJ7sfWwnyhndvGhe0Hq1NcCf7I8TjNDxU5TR+m+uW7xjXdLoDbUjBdX4sKXnh8ZjbYiODKBOrrDq25cf8nA/tnpKyE/qsVy60kOk6loY4XKiYmn1V49Ta0emmDx0hqo3HgxHHsHX0NDnGdWGol7cPRET0RzVobKq1A0jnrhPooWidvLh9bPzLonrWDo+ib+DuySoRkuYUK4pgZJ2mbg6daFOBEZygkSyRB8bo1UQUP7EgQDrWe4khb/5GHEfDkrQz3qu/sXvc0Ir1mOUWBFPHC2DjjCn/oMJuUkG1SwM8l2Bfv7h67ssES6YQ2+RjOix4yid7EXS/Ogl45PzCIPSI5+BbNs10JhE0w5uErBHlF53EDTe/TSLc+GU6DB6PP6dH912Njdr3jpNSUQ=
79 335a558f81dc73afeab4d7be63617392b130117f 0 iQIVAwUAUiZrIyBXgaxoKi1yAQK2iw//cquNqqSkc8Re5/TZT9I6NH+lh6DbOKjJP0Xl1Wqq0K+KSIUgZG4G32ovaEb2l5X0uY+3unRPiZ0ebl0YSw4Fb2ZiPIADXLBTOYRrY2Wwd3tpJeGI6wEgZt3SfcITV/g7NJrCjT3FlYoSOIayrExM80InSdcEM0Q3Rx6HKzY2acyxzgZeAtAW5ohFvHilSvY6p5Gcm4+QptMxvw45GPdreUmjeXZxNXNXZ8P+MjMz/QJbai/N7PjmK8lqnhkBsT48Ng/KhhmOkGntNJ2/ImBWLFGcWngSvJ7sfWwnyhndvGhe0Hq1NcCf7I8TjNDxU5TR+m+uW7xjXdLoDbUjBdX4sKXnh8ZjbYiODKBOrrDq25cf8nA/tnpKyE/qsVy60kOk6loY4XKiYmn1V49Ta0emmDx0hqo3HgxHHsHX0NDnGdWGol7cPRET0RzVobKq1A0jnrhPooWidvLh9bPzLonrWDo+ib+DuySoRkuYUK4pgZJ2mbg6daFOBEZygkSyRB8bo1UQUP7EgQDrWe4khb/5GHEfDkrQz3qu/sXvc0Ir1mOUWBFPHC2DjjCn/oMJuUkG1SwM8l2Bfv7h67ssES6YQ2+RjOix4yid7EXS/Ogl45PzCIPSI5+BbNs10JhE0w5uErBHlF53EDTe/TSLc+GU6DB6PP6dH912Njdr3jpNSUQ=
80 e7fa36d2ad3a7944a52dca126458d6f482db3524 0 iQIVAwUAUktg4yBXgaxoKi1yAQLO0g//du/2ypYYUfmM/yZ4zztNKIvgMSGTDVbCCGB2y2/wk2EcolpjpGTkcgnJT413ksYtw78ZU+mvv0RjgrFCm8DQ8kroJaQZ2qHmtSUb42hPBPvtg6kL9YaA4yvp87uUBpFRavGS5uX4hhEIyvZKzhXUBvqtL3TfwR7ld21bj8j00wudqELyyU9IrojIY9jkJ3XL/4shBGgP7u6OK5g8yJ6zTnWgysUetxHBPrYjG25lziiiZQFvZqK1B3PUqAOaFPltQs0PB8ipOCAHQgJsjaREj8VmC3+rskmSSy66NHm6gAB9+E8oAgOcU7FzWbdYgnz4kR3M7TQvHX9U61NinPXC6Q9d1VPhO3E6sIGvqJ4YeQOn65V9ezYuIpFSlgQzCHMmLVnOV96Uv1R/Z39I4w7D3S5qoZcQT/siQwGbsZoPMGFYmqOK1da5TZWrrJWkYzc9xvzT9m3q3Wds5pmCmo4b/dIqDifWwYEcNAZ0/YLHwCN5SEZWuunkEwtU5o7TZAv3bvDDA6WxUrrHI/y9/qvvhXxsJnY8IueNhshdmWZfXKz+lJi2Dvk7DUlEQ1zZWSsozi1E+3biMPJO47jsxjoT/jmE5+GHLCgcnXXDVBeaVal99IOaTRFukiz2EMsry1s8fnwEE5XKDKRlU/dOPfsje0gc7bgE0QD/u3E4NJ99g9A=
@@ -1,92 +1,93
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
@@ -1,901 +1,900
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 # Commands:
33 # Commands:
34 # p, pick = use commit
34 # p, pick = use commit
35 # e, edit = use commit, but stop for amending
35 # e, edit = use commit, but stop for amending
36 # f, fold = use commit, but fold into previous commit (combines N and N-1)
36 # f, fold = use commit, but fold into previous commit (combines N and N-1)
37 # d, drop = remove commit from history
37 # d, drop = remove commit from history
38 # m, mess = edit message without changing commit content
38 # m, mess = edit message without changing commit content
39 #
39 #
40
40
41 In this file, lines beginning with ``#`` are ignored. You must specify a rule
41 In this file, lines beginning with ``#`` are ignored. You must specify a rule
42 for each revision in your history. For example, if you had meant to add gamma
42 for each revision in your history. For example, if you had meant to add gamma
43 before beta, and then wanted to add delta in the same revision as beta, you
43 before beta, and then wanted to add delta in the same revision as beta, you
44 would reorganize the file to look like this::
44 would reorganize the file to look like this::
45
45
46 pick 030b686bedc4 Add gamma
46 pick 030b686bedc4 Add gamma
47 pick c561b4e977df Add beta
47 pick c561b4e977df Add beta
48 fold 7c2fd3b9020c Add delta
48 fold 7c2fd3b9020c Add delta
49
49
50 # Edit history between c561b4e977df and 7c2fd3b9020c
50 # Edit history between c561b4e977df and 7c2fd3b9020c
51 #
51 #
52 # Commands:
52 # Commands:
53 # p, pick = use commit
53 # p, pick = use commit
54 # e, edit = use commit, but stop for amending
54 # e, edit = use commit, but stop for amending
55 # f, fold = use commit, but fold into previous commit (combines N and N-1)
55 # f, fold = use commit, but fold into previous commit (combines N and N-1)
56 # d, drop = remove commit from history
56 # d, drop = remove commit from history
57 # m, mess = edit message without changing commit content
57 # m, mess = edit message without changing commit content
58 #
58 #
59
59
60 At which point you close the editor and ``histedit`` starts working. When you
60 At which point you close the editor and ``histedit`` starts working. When you
61 specify a ``fold`` operation, ``histedit`` will open an editor when it folds
61 specify a ``fold`` operation, ``histedit`` will open an editor when it folds
62 those revisions together, offering you a chance to clean up the commit message::
62 those revisions together, offering you a chance to clean up the commit message::
63
63
64 Add beta
64 Add beta
65 ***
65 ***
66 Add delta
66 Add delta
67
67
68 Edit the commit message to your liking, then close the editor. For
68 Edit the commit message to your liking, then close the editor. For
69 this example, let's assume that the commit message was changed to
69 this example, let's assume that the commit message was changed to
70 ``Add beta and delta.`` After histedit has run and had a chance to
70 ``Add beta and delta.`` After histedit has run and had a chance to
71 remove any old or temporary revisions it needed, the history looks
71 remove any old or temporary revisions it needed, the history looks
72 like this::
72 like this::
73
73
74 @ 2[tip] 989b4d060121 2009-04-27 18:04 -0500 durin42
74 @ 2[tip] 989b4d060121 2009-04-27 18:04 -0500 durin42
75 | Add beta and delta.
75 | Add beta and delta.
76 |
76 |
77 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
77 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
78 | Add gamma
78 | Add gamma
79 |
79 |
80 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
80 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
81 Add alpha
81 Add alpha
82
82
83 Note that ``histedit`` does *not* remove any revisions (even its own temporary
83 Note that ``histedit`` does *not* remove any revisions (even its own temporary
84 ones) until after it has completed all the editing operations, so it will
84 ones) until after it has completed all the editing operations, so it will
85 probably perform several strip operations when it's done. For the above example,
85 probably perform several strip operations when it's done. For the above example,
86 it had to run strip twice. Strip can be slow depending on a variety of factors,
86 it had to run strip twice. Strip can be slow depending on a variety of factors,
87 so you might need to be a little patient. You can choose to keep the original
87 so you might need to be a little patient. You can choose to keep the original
88 revisions by passing the ``--keep`` flag.
88 revisions by passing the ``--keep`` flag.
89
89
90 The ``edit`` operation will drop you back to a command prompt,
90 The ``edit`` operation will drop you back to a command prompt,
91 allowing you to edit files freely, or even use ``hg record`` to commit
91 allowing you to edit files freely, or even use ``hg record`` to commit
92 some changes as a separate commit. When you're done, any remaining
92 some changes as a separate commit. When you're done, any remaining
93 uncommitted changes will be committed as well. When done, run ``hg
93 uncommitted changes will be committed as well. When done, run ``hg
94 histedit --continue`` to finish this step. You'll be prompted for a
94 histedit --continue`` to finish this step. You'll be prompted for a
95 new commit message, but the default commit message will be the
95 new commit message, but the default commit message will be the
96 original message for the ``edit`` ed revision.
96 original message for the ``edit`` ed revision.
97
97
98 The ``message`` operation will give you a chance to revise a commit
98 The ``message`` operation will give you a chance to revise a commit
99 message without changing the contents. It's a shortcut for doing
99 message without changing the contents. It's a shortcut for doing
100 ``edit`` immediately followed by `hg histedit --continue``.
100 ``edit`` immediately followed by `hg histedit --continue``.
101
101
102 If ``histedit`` encounters a conflict when moving a revision (while
102 If ``histedit`` encounters a conflict when moving a revision (while
103 handling ``pick`` or ``fold``), it'll stop in a similar manner to
103 handling ``pick`` or ``fold``), it'll stop in a similar manner to
104 ``edit`` with the difference that it won't prompt you for a commit
104 ``edit`` with the difference that it won't prompt you for a commit
105 message when done. If you decide at this point that you don't like how
105 message when done. If you decide at this point that you don't like how
106 much work it will be to rearrange history, or that you made a mistake,
106 much work it will be to rearrange history, or that you made a mistake,
107 you can use ``hg histedit --abort`` to abandon the new changes you
107 you can use ``hg histedit --abort`` to abandon the new changes you
108 have made and return to the state before you attempted to edit your
108 have made and return to the state before you attempted to edit your
109 history.
109 history.
110
110
111 If we clone the histedit-ed example repository above and add four more
111 If we clone the histedit-ed example repository above and add four more
112 changes, such that we have the following history::
112 changes, such that we have the following history::
113
113
114 @ 6[tip] 038383181893 2009-04-27 18:04 -0500 stefan
114 @ 6[tip] 038383181893 2009-04-27 18:04 -0500 stefan
115 | Add theta
115 | Add theta
116 |
116 |
117 o 5 140988835471 2009-04-27 18:04 -0500 stefan
117 o 5 140988835471 2009-04-27 18:04 -0500 stefan
118 | Add eta
118 | Add eta
119 |
119 |
120 o 4 122930637314 2009-04-27 18:04 -0500 stefan
120 o 4 122930637314 2009-04-27 18:04 -0500 stefan
121 | Add zeta
121 | Add zeta
122 |
122 |
123 o 3 836302820282 2009-04-27 18:04 -0500 stefan
123 o 3 836302820282 2009-04-27 18:04 -0500 stefan
124 | Add epsilon
124 | Add epsilon
125 |
125 |
126 o 2 989b4d060121 2009-04-27 18:04 -0500 durin42
126 o 2 989b4d060121 2009-04-27 18:04 -0500 durin42
127 | Add beta and delta.
127 | Add beta and delta.
128 |
128 |
129 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
129 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
130 | Add gamma
130 | Add gamma
131 |
131 |
132 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
132 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
133 Add alpha
133 Add alpha
134
134
135 If you run ``hg histedit --outgoing`` on the clone then it is the same
135 If you run ``hg histedit --outgoing`` on the clone then it is the same
136 as running ``hg histedit 836302820282``. If you need plan to push to a
136 as running ``hg histedit 836302820282``. If you need plan to push to a
137 repository that Mercurial does not detect to be related to the source
137 repository that Mercurial does not detect to be related to the source
138 repo, you can add a ``--force`` option.
138 repo, you can add a ``--force`` option.
139 """
139 """
140
140
141 try:
141 try:
142 import cPickle as pickle
142 import cPickle as pickle
143 pickle.dump # import now
143 pickle.dump # import now
144 except ImportError:
144 except ImportError:
145 import pickle
145 import pickle
146 import os
146 import os
147 import sys
147 import sys
148
148
149 from mercurial import cmdutil
149 from mercurial import cmdutil
150 from mercurial import discovery
150 from mercurial import discovery
151 from mercurial import error
151 from mercurial import error
152 from mercurial import copies
152 from mercurial import copies
153 from mercurial import context
153 from mercurial import context
154 from mercurial import hg
154 from mercurial import hg
155 from mercurial import lock as lockmod
155 from mercurial import lock as lockmod
156 from mercurial import node
156 from mercurial import node
157 from mercurial import repair
157 from mercurial import repair
158 from mercurial import scmutil
158 from mercurial import scmutil
159 from mercurial import util
159 from mercurial import util
160 from mercurial import obsolete
160 from mercurial import obsolete
161 from mercurial import merge as mergemod
161 from mercurial import merge as mergemod
162 from mercurial.i18n import _
162 from mercurial.i18n import _
163
163
164 cmdtable = {}
164 cmdtable = {}
165 command = cmdutil.command(cmdtable)
165 command = cmdutil.command(cmdtable)
166
166
167 testedwith = 'internal'
167 testedwith = 'internal'
168
168
169 # i18n: command names and abbreviations must remain untranslated
169 # i18n: command names and abbreviations must remain untranslated
170 editcomment = _("""# Edit history between %s and %s
170 editcomment = _("""# Edit history between %s and %s
171 #
171 #
172 # Commands:
172 # Commands:
173 # p, pick = use commit
173 # p, pick = use commit
174 # e, edit = use commit, but stop for amending
174 # e, edit = use commit, but stop for amending
175 # f, fold = use commit, but fold into previous commit (combines N and N-1)
175 # f, fold = use commit, but fold into previous commit (combines N and N-1)
176 # d, drop = remove commit from history
176 # d, drop = remove commit from history
177 # m, mess = edit message without changing commit content
177 # m, mess = edit message without changing commit content
178 #
178 #
179 """)
179 """)
180
180
181 def commitfuncfor(repo, src):
181 def commitfuncfor(repo, src):
182 """Build a commit function for the replacement of <src>
182 """Build a commit function for the replacement of <src>
183
183
184 This function ensure we apply the same treatment to all changesets.
184 This function ensure we apply the same treatment to all changesets.
185
185
186 - Add a 'histedit_source' entry in extra.
186 - Add a 'histedit_source' entry in extra.
187
187
188 Note that fold have its own separated logic because its handling is a bit
188 Note that fold have its own separated logic because its handling is a bit
189 different and not easily factored out of the fold method.
189 different and not easily factored out of the fold method.
190 """
190 """
191 phasemin = src.phase()
191 phasemin = src.phase()
192 def commitfunc(**kwargs):
192 def commitfunc(**kwargs):
193 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
193 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
194 try:
194 try:
195 repo.ui.setconfig('phases', 'new-commit', phasemin)
195 repo.ui.setconfig('phases', 'new-commit', phasemin)
196 extra = kwargs.get('extra', {}).copy()
196 extra = kwargs.get('extra', {}).copy()
197 extra['histedit_source'] = src.hex()
197 extra['histedit_source'] = src.hex()
198 kwargs['extra'] = extra
198 kwargs['extra'] = extra
199 return repo.commit(**kwargs)
199 return repo.commit(**kwargs)
200 finally:
200 finally:
201 repo.ui.restoreconfig(phasebackup)
201 repo.ui.restoreconfig(phasebackup)
202 return commitfunc
202 return commitfunc
203
203
204
204
205
205
206 def applychanges(ui, repo, ctx, opts):
206 def applychanges(ui, repo, ctx, opts):
207 """Merge changeset from ctx (only) in the current working directory"""
207 """Merge changeset from ctx (only) in the current working directory"""
208 wcpar = repo.dirstate.parents()[0]
208 wcpar = repo.dirstate.parents()[0]
209 if ctx.p1().node() == wcpar:
209 if ctx.p1().node() == wcpar:
210 # edition ar "in place" we do not need to make any merge,
210 # edition ar "in place" we do not need to make any merge,
211 # just applies changes on parent for edition
211 # just applies changes on parent for edition
212 cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True)
212 cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True)
213 stats = None
213 stats = None
214 else:
214 else:
215 try:
215 try:
216 # ui.forcemerge is an internal variable, do not document
216 # ui.forcemerge is an internal variable, do not document
217 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
217 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
218 stats = mergemod.update(repo, ctx.node(), True, True, False,
218 stats = mergemod.update(repo, ctx.node(), True, True, False,
219 ctx.p1().node())
219 ctx.p1().node())
220 finally:
220 finally:
221 repo.ui.setconfig('ui', 'forcemerge', '')
221 repo.ui.setconfig('ui', 'forcemerge', '')
222 repo.setparents(wcpar, node.nullid)
222 repo.setparents(wcpar, node.nullid)
223 repo.dirstate.write()
223 repo.dirstate.write()
224 # fix up dirstate for copies and renames
224 # fix up dirstate for copies and renames
225 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
225 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
226 return stats
226 return stats
227
227
228 def collapse(repo, first, last, commitopts):
228 def collapse(repo, first, last, commitopts):
229 """collapse the set of revisions from first to last as new one.
229 """collapse the set of revisions from first to last as new one.
230
230
231 Expected commit options are:
231 Expected commit options are:
232 - message
232 - message
233 - date
233 - date
234 - username
234 - username
235 Commit message is edited in all cases.
235 Commit message is edited in all cases.
236
236
237 This function works in memory."""
237 This function works in memory."""
238 ctxs = list(repo.set('%d::%d', first, last))
238 ctxs = list(repo.set('%d::%d', first, last))
239 if not ctxs:
239 if not ctxs:
240 return None
240 return None
241 base = first.parents()[0]
241 base = first.parents()[0]
242
242
243 # commit a new version of the old changeset, including the update
243 # commit a new version of the old changeset, including the update
244 # collect all files which might be affected
244 # collect all files which might be affected
245 files = set()
245 files = set()
246 for ctx in ctxs:
246 for ctx in ctxs:
247 files.update(ctx.files())
247 files.update(ctx.files())
248
248
249 # Recompute copies (avoid recording a -> b -> a)
249 # Recompute copies (avoid recording a -> b -> a)
250 copied = copies.pathcopies(base, last)
250 copied = copies.pathcopies(base, last)
251
251
252 # prune files which were reverted by the updates
252 # prune files which were reverted by the updates
253 def samefile(f):
253 def samefile(f):
254 if f in last.manifest():
254 if f in last.manifest():
255 a = last.filectx(f)
255 a = last.filectx(f)
256 if f in base.manifest():
256 if f in base.manifest():
257 b = base.filectx(f)
257 b = base.filectx(f)
258 return (a.data() == b.data()
258 return (a.data() == b.data()
259 and a.flags() == b.flags())
259 and a.flags() == b.flags())
260 else:
260 else:
261 return False
261 return False
262 else:
262 else:
263 return f not in base.manifest()
263 return f not in base.manifest()
264 files = [f for f in files if not samefile(f)]
264 files = [f for f in files if not samefile(f)]
265 # commit version of these files as defined by head
265 # commit version of these files as defined by head
266 headmf = last.manifest()
266 headmf = last.manifest()
267 def filectxfn(repo, ctx, path):
267 def filectxfn(repo, ctx, path):
268 if path in headmf:
268 if path in headmf:
269 fctx = last[path]
269 fctx = last[path]
270 flags = fctx.flags()
270 flags = fctx.flags()
271 mctx = context.memfilectx(fctx.path(), fctx.data(),
271 mctx = context.memfilectx(fctx.path(), fctx.data(),
272 islink='l' in flags,
272 islink='l' in flags,
273 isexec='x' in flags,
273 isexec='x' in flags,
274 copied=copied.get(path))
274 copied=copied.get(path))
275 return mctx
275 return mctx
276 raise IOError()
276 raise IOError()
277
277
278 if commitopts.get('message'):
278 if commitopts.get('message'):
279 message = commitopts['message']
279 message = commitopts['message']
280 else:
280 else:
281 message = first.description()
281 message = first.description()
282 user = commitopts.get('user')
282 user = commitopts.get('user')
283 date = commitopts.get('date')
283 date = commitopts.get('date')
284 extra = commitopts.get('extra')
284 extra = commitopts.get('extra')
285
285
286 parents = (first.p1().node(), first.p2().node())
286 parents = (first.p1().node(), first.p2().node())
287 new = context.memctx(repo,
287 new = context.memctx(repo,
288 parents=parents,
288 parents=parents,
289 text=message,
289 text=message,
290 files=files,
290 files=files,
291 filectxfn=filectxfn,
291 filectxfn=filectxfn,
292 user=user,
292 user=user,
293 date=date,
293 date=date,
294 extra=extra)
294 extra=extra)
295 new._text = cmdutil.commitforceeditor(repo, new, [])
295 new._text = cmdutil.commitforceeditor(repo, new, [])
296 return repo.commitctx(new)
296 return repo.commitctx(new)
297
297
298 def pick(ui, repo, ctx, ha, opts):
298 def pick(ui, repo, ctx, ha, opts):
299 oldctx = repo[ha]
299 oldctx = repo[ha]
300 if oldctx.parents()[0] == ctx:
300 if oldctx.parents()[0] == ctx:
301 ui.debug('node %s unchanged\n' % ha)
301 ui.debug('node %s unchanged\n' % ha)
302 return oldctx, []
302 return oldctx, []
303 hg.update(repo, ctx.node())
303 hg.update(repo, ctx.node())
304 stats = applychanges(ui, repo, oldctx, opts)
304 stats = applychanges(ui, repo, oldctx, opts)
305 if stats and stats[3] > 0:
305 if stats and stats[3] > 0:
306 raise error.InterventionRequired(_('Fix up the change and run '
306 raise error.InterventionRequired(_('Fix up the change and run '
307 'hg histedit --continue'))
307 'hg histedit --continue'))
308 # drop the second merge parent
308 # drop the second merge parent
309 commit = commitfuncfor(repo, oldctx)
309 commit = commitfuncfor(repo, oldctx)
310 n = commit(text=oldctx.description(), user=oldctx.user(),
310 n = commit(text=oldctx.description(), user=oldctx.user(),
311 date=oldctx.date(), extra=oldctx.extra())
311 date=oldctx.date(), extra=oldctx.extra())
312 if n is None:
312 if n is None:
313 ui.warn(_('%s: empty changeset\n')
313 ui.warn(_('%s: empty changeset\n')
314 % node.hex(ha))
314 % node.hex(ha))
315 return ctx, []
315 return ctx, []
316 new = repo[n]
316 new = repo[n]
317 return new, [(oldctx.node(), (n,))]
317 return new, [(oldctx.node(), (n,))]
318
318
319
319
320 def edit(ui, repo, ctx, ha, opts):
320 def edit(ui, repo, ctx, ha, opts):
321 oldctx = repo[ha]
321 oldctx = repo[ha]
322 hg.update(repo, ctx.node())
322 hg.update(repo, ctx.node())
323 applychanges(ui, repo, oldctx, opts)
323 applychanges(ui, repo, oldctx, opts)
324 raise error.InterventionRequired(
324 raise error.InterventionRequired(
325 _('Make changes as needed, you may commit or record as needed now.\n'
325 _('Make changes as needed, you may commit or record as needed now.\n'
326 'When you are finished, run hg histedit --continue to resume.'))
326 'When you are finished, run hg histedit --continue to resume.'))
327
327
328 def fold(ui, repo, ctx, ha, opts):
328 def fold(ui, repo, ctx, ha, opts):
329 oldctx = repo[ha]
329 oldctx = repo[ha]
330 hg.update(repo, ctx.node())
330 hg.update(repo, ctx.node())
331 stats = applychanges(ui, repo, oldctx, opts)
331 stats = applychanges(ui, repo, oldctx, opts)
332 if stats and stats[3] > 0:
332 if stats and stats[3] > 0:
333 raise error.InterventionRequired(
333 raise error.InterventionRequired(
334 _('Fix up the change and run hg histedit --continue'))
334 _('Fix up the change and run hg histedit --continue'))
335 n = repo.commit(text='fold-temp-revision %s' % ha, user=oldctx.user(),
335 n = repo.commit(text='fold-temp-revision %s' % ha, user=oldctx.user(),
336 date=oldctx.date(), extra=oldctx.extra())
336 date=oldctx.date(), extra=oldctx.extra())
337 if n is None:
337 if n is None:
338 ui.warn(_('%s: empty changeset')
338 ui.warn(_('%s: empty changeset')
339 % node.hex(ha))
339 % node.hex(ha))
340 return ctx, []
340 return ctx, []
341 return finishfold(ui, repo, ctx, oldctx, n, opts, [])
341 return finishfold(ui, repo, ctx, oldctx, n, opts, [])
342
342
343 def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges):
343 def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges):
344 parent = ctx.parents()[0].node()
344 parent = ctx.parents()[0].node()
345 hg.update(repo, parent)
345 hg.update(repo, parent)
346 ### prepare new commit data
346 ### prepare new commit data
347 commitopts = opts.copy()
347 commitopts = opts.copy()
348 # username
348 # username
349 if ctx.user() == oldctx.user():
349 if ctx.user() == oldctx.user():
350 username = ctx.user()
350 username = ctx.user()
351 else:
351 else:
352 username = ui.username()
352 username = ui.username()
353 commitopts['user'] = username
353 commitopts['user'] = username
354 # commit message
354 # commit message
355 newmessage = '\n***\n'.join(
355 newmessage = '\n***\n'.join(
356 [ctx.description()] +
356 [ctx.description()] +
357 [repo[r].description() for r in internalchanges] +
357 [repo[r].description() for r in internalchanges] +
358 [oldctx.description()]) + '\n'
358 [oldctx.description()]) + '\n'
359 commitopts['message'] = newmessage
359 commitopts['message'] = newmessage
360 # date
360 # date
361 commitopts['date'] = max(ctx.date(), oldctx.date())
361 commitopts['date'] = max(ctx.date(), oldctx.date())
362 extra = ctx.extra().copy()
362 extra = ctx.extra().copy()
363 # histedit_source
363 # histedit_source
364 # note: ctx is likely a temporary commit but that the best we can do here
364 # note: ctx is likely a temporary commit but that the best we can do here
365 # This is sufficient to solve issue3681 anyway
365 # This is sufficient to solve issue3681 anyway
366 extra['histedit_source'] = '%s,%s' % (ctx.hex(), oldctx.hex())
366 extra['histedit_source'] = '%s,%s' % (ctx.hex(), oldctx.hex())
367 commitopts['extra'] = extra
367 commitopts['extra'] = extra
368 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
368 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
369 try:
369 try:
370 phasemin = max(ctx.phase(), oldctx.phase())
370 phasemin = max(ctx.phase(), oldctx.phase())
371 repo.ui.setconfig('phases', 'new-commit', phasemin)
371 repo.ui.setconfig('phases', 'new-commit', phasemin)
372 n = collapse(repo, ctx, repo[newnode], commitopts)
372 n = collapse(repo, ctx, repo[newnode], commitopts)
373 finally:
373 finally:
374 repo.ui.restoreconfig(phasebackup)
374 repo.ui.restoreconfig(phasebackup)
375 if n is None:
375 if n is None:
376 return ctx, []
376 return ctx, []
377 hg.update(repo, n)
377 hg.update(repo, n)
378 replacements = [(oldctx.node(), (newnode,)),
378 replacements = [(oldctx.node(), (newnode,)),
379 (ctx.node(), (n,)),
379 (ctx.node(), (n,)),
380 (newnode, (n,)),
380 (newnode, (n,)),
381 ]
381 ]
382 for ich in internalchanges:
382 for ich in internalchanges:
383 replacements.append((ich, (n,)))
383 replacements.append((ich, (n,)))
384 return repo[n], replacements
384 return repo[n], replacements
385
385
386 def drop(ui, repo, ctx, ha, opts):
386 def drop(ui, repo, ctx, ha, opts):
387 return ctx, [(repo[ha].node(), ())]
387 return ctx, [(repo[ha].node(), ())]
388
388
389
389
390 def message(ui, repo, ctx, ha, opts):
390 def message(ui, repo, ctx, ha, opts):
391 oldctx = repo[ha]
391 oldctx = repo[ha]
392 hg.update(repo, ctx.node())
392 hg.update(repo, ctx.node())
393 stats = applychanges(ui, repo, oldctx, opts)
393 stats = applychanges(ui, repo, oldctx, opts)
394 if stats and stats[3] > 0:
394 if stats and stats[3] > 0:
395 raise error.InterventionRequired(
395 raise error.InterventionRequired(
396 _('Fix up the change and run hg histedit --continue'))
396 _('Fix up the change and run hg histedit --continue'))
397 message = oldctx.description() + '\n'
397 message = oldctx.description() + '\n'
398 message = ui.edit(message, ui.username())
398 message = ui.edit(message, ui.username())
399 commit = commitfuncfor(repo, oldctx)
399 commit = commitfuncfor(repo, oldctx)
400 new = commit(text=message, user=oldctx.user(), date=oldctx.date(),
400 new = commit(text=message, user=oldctx.user(), date=oldctx.date(),
401 extra=oldctx.extra())
401 extra=oldctx.extra())
402 newctx = repo[new]
402 newctx = repo[new]
403 if oldctx.node() != newctx.node():
403 if oldctx.node() != newctx.node():
404 return newctx, [(oldctx.node(), (new,))]
404 return newctx, [(oldctx.node(), (new,))]
405 # We didn't make an edit, so just indicate no replaced nodes
405 # We didn't make an edit, so just indicate no replaced nodes
406 return newctx, []
406 return newctx, []
407
407
408 def findoutgoing(ui, repo, remote=None, force=False, opts={}):
408 def findoutgoing(ui, repo, remote=None, force=False, opts={}):
409 """utility function to find the first outgoing changeset
409 """utility function to find the first outgoing changeset
410
410
411 Used by initialisation code"""
411 Used by initialisation code"""
412 dest = ui.expandpath(remote or 'default-push', remote or 'default')
412 dest = ui.expandpath(remote or 'default-push', remote or 'default')
413 dest, revs = hg.parseurl(dest, None)[:2]
413 dest, revs = hg.parseurl(dest, None)[:2]
414 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
414 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
415
415
416 revs, checkout = hg.addbranchrevs(repo, repo, revs, None)
416 revs, checkout = hg.addbranchrevs(repo, repo, revs, None)
417 other = hg.peer(repo, opts, dest)
417 other = hg.peer(repo, opts, dest)
418
418
419 if revs:
419 if revs:
420 revs = [repo.lookup(rev) for rev in revs]
420 revs = [repo.lookup(rev) for rev in revs]
421
421
422 outgoing = discovery.findcommonoutgoing(repo, other, revs, force=force)
422 outgoing = discovery.findcommonoutgoing(repo, other, revs, force=force)
423 if not outgoing.missing:
423 if not outgoing.missing:
424 raise util.Abort(_('no outgoing ancestors'))
424 raise util.Abort(_('no outgoing ancestors'))
425 roots = list(repo.revs("roots(%ln)", outgoing.missing))
425 roots = list(repo.revs("roots(%ln)", outgoing.missing))
426 if 1 < len(roots):
426 if 1 < len(roots):
427 msg = _('there are ambiguous outgoing revisions')
427 msg = _('there are ambiguous outgoing revisions')
428 hint = _('see "hg help histedit" for more detail')
428 hint = _('see "hg help histedit" for more detail')
429 raise util.Abort(msg, hint=hint)
429 raise util.Abort(msg, hint=hint)
430 return repo.lookup(roots[0])
430 return repo.lookup(roots[0])
431
431
432 actiontable = {'p': pick,
432 actiontable = {'p': pick,
433 'pick': pick,
433 'pick': pick,
434 'e': edit,
434 'e': edit,
435 'edit': edit,
435 'edit': edit,
436 'f': fold,
436 'f': fold,
437 'fold': fold,
437 'fold': fold,
438 'd': drop,
438 'd': drop,
439 'drop': drop,
439 'drop': drop,
440 'm': message,
440 'm': message,
441 'mess': message,
441 'mess': message,
442 }
442 }
443
443
444 @command('histedit',
444 @command('histedit',
445 [('', 'commands', '',
445 [('', 'commands', '',
446 _('Read history edits from the specified file.')),
446 _('Read history edits from the specified file.')),
447 ('c', 'continue', False, _('continue an edit already in progress')),
447 ('c', 'continue', False, _('continue an edit already in progress')),
448 ('k', 'keep', False,
448 ('k', 'keep', False,
449 _("don't strip old nodes after edit is complete")),
449 _("don't strip old nodes after edit is complete")),
450 ('', 'abort', False, _('abort an edit in progress')),
450 ('', 'abort', False, _('abort an edit in progress')),
451 ('o', 'outgoing', False, _('changesets not found in destination')),
451 ('o', 'outgoing', False, _('changesets not found in destination')),
452 ('f', 'force', False,
452 ('f', 'force', False,
453 _('force outgoing even for unrelated repositories')),
453 _('force outgoing even for unrelated repositories')),
454 ('r', 'rev', [], _('first revision to be edited'))],
454 ('r', 'rev', [], _('first revision to be edited'))],
455 _("ANCESTOR | --outgoing [URL]"))
455 _("ANCESTOR | --outgoing [URL]"))
456 def histedit(ui, repo, *freeargs, **opts):
456 def histedit(ui, repo, *freeargs, **opts):
457 """interactively edit changeset history
457 """interactively edit changeset history
458
458
459 This command edits changesets between ANCESTOR and the parent of
459 This command edits changesets between ANCESTOR and the parent of
460 the working directory.
460 the working directory.
461
461
462 With --outgoing, this edits changesets not found in the
462 With --outgoing, this edits changesets not found in the
463 destination repository. If URL of the destination is omitted, the
463 destination repository. If URL of the destination is omitted, the
464 'default-push' (or 'default') path will be used.
464 'default-push' (or 'default') path will be used.
465
465
466 For safety, this command is aborted, also if there are ambiguous
466 For safety, this command is aborted, also if there are ambiguous
467 outgoing revisions which may confuse users: for example, there are
467 outgoing revisions which may confuse users: for example, there are
468 multiple branches containing outgoing revisions.
468 multiple branches containing outgoing revisions.
469
469
470 Use "min(outgoing() and ::.)" or similar revset specification
470 Use "min(outgoing() and ::.)" or similar revset specification
471 instead of --outgoing to specify edit target revision exactly in
471 instead of --outgoing to specify edit target revision exactly in
472 such ambiguous situation. See :hg:`help revsets` for detail about
472 such ambiguous situation. See :hg:`help revsets` for detail about
473 selecting revisions.
473 selecting revisions.
474 """
474 """
475 # TODO only abort if we try and histedit mq patches, not just
475 # TODO only abort if we try and histedit mq patches, not just
476 # blanket if mq patches are applied somewhere
476 # blanket if mq patches are applied somewhere
477 mq = getattr(repo, 'mq', None)
477 mq = getattr(repo, 'mq', None)
478 if mq and mq.applied:
478 if mq and mq.applied:
479 raise util.Abort(_('source has mq patches applied'))
479 raise util.Abort(_('source has mq patches applied'))
480
480
481 # basic argument incompatibility processing
481 # basic argument incompatibility processing
482 outg = opts.get('outgoing')
482 outg = opts.get('outgoing')
483 cont = opts.get('continue')
483 cont = opts.get('continue')
484 abort = opts.get('abort')
484 abort = opts.get('abort')
485 force = opts.get('force')
485 force = opts.get('force')
486 rules = opts.get('commands', '')
486 rules = opts.get('commands', '')
487 revs = opts.get('rev', [])
487 revs = opts.get('rev', [])
488 goal = 'new' # This invocation goal, in new, continue, abort
488 goal = 'new' # This invocation goal, in new, continue, abort
489 if force and not outg:
489 if force and not outg:
490 raise util.Abort(_('--force only allowed with --outgoing'))
490 raise util.Abort(_('--force only allowed with --outgoing'))
491 if cont:
491 if cont:
492 if util.any((outg, abort, revs, freeargs, rules)):
492 if util.any((outg, abort, revs, freeargs, rules)):
493 raise util.Abort(_('no arguments allowed with --continue'))
493 raise util.Abort(_('no arguments allowed with --continue'))
494 goal = 'continue'
494 goal = 'continue'
495 elif abort:
495 elif abort:
496 if util.any((outg, revs, freeargs, rules)):
496 if util.any((outg, revs, freeargs, rules)):
497 raise util.Abort(_('no arguments allowed with --abort'))
497 raise util.Abort(_('no arguments allowed with --abort'))
498 goal = 'abort'
498 goal = 'abort'
499 else:
499 else:
500 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
500 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
501 raise util.Abort(_('history edit already in progress, try '
501 raise util.Abort(_('history edit already in progress, try '
502 '--continue or --abort'))
502 '--continue or --abort'))
503 if outg:
503 if outg:
504 if revs:
504 if revs:
505 raise util.Abort(_('no revisions allowed with --outgoing'))
505 raise util.Abort(_('no revisions allowed with --outgoing'))
506 if len(freeargs) > 1:
506 if len(freeargs) > 1:
507 raise util.Abort(
507 raise util.Abort(
508 _('only one repo argument allowed with --outgoing'))
508 _('only one repo argument allowed with --outgoing'))
509 else:
509 else:
510 revs.extend(freeargs)
510 revs.extend(freeargs)
511 if len(revs) != 1:
511 if len(revs) != 1:
512 raise util.Abort(
512 raise util.Abort(
513 _('histedit requires exactly one ancestor revision'))
513 _('histedit requires exactly one ancestor revision'))
514
514
515
515
516 if goal == 'continue':
516 if goal == 'continue':
517 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo)
517 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo)
518 parentctx = repo[parentctxnode]
518 parentctx = repo[parentctxnode]
519 parentctx, repl = bootstrapcontinue(ui, repo, parentctx, rules, opts)
519 parentctx, repl = bootstrapcontinue(ui, repo, parentctx, rules, opts)
520 replacements.extend(repl)
520 replacements.extend(repl)
521 elif goal == 'abort':
521 elif goal == 'abort':
522 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo)
522 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo)
523 mapping, tmpnodes, leafs, _ntm = processreplacement(repo, replacements)
523 mapping, tmpnodes, leafs, _ntm = processreplacement(repo, replacements)
524 ui.debug('restore wc to old parent %s\n' % node.short(topmost))
524 ui.debug('restore wc to old parent %s\n' % node.short(topmost))
525 # check whether we should update away
525 # check whether we should update away
526 parentnodes = [c.node() for c in repo[None].parents()]
526 parentnodes = [c.node() for c in repo[None].parents()]
527 for n in leafs | set([parentctxnode]):
527 for n in leafs | set([parentctxnode]):
528 if n in parentnodes:
528 if n in parentnodes:
529 hg.clean(repo, topmost)
529 hg.clean(repo, topmost)
530 break
530 break
531 else:
531 else:
532 pass
532 pass
533 cleanupnode(ui, repo, 'created', tmpnodes)
533 cleanupnode(ui, repo, 'created', tmpnodes)
534 cleanupnode(ui, repo, 'temp', leafs)
534 cleanupnode(ui, repo, 'temp', leafs)
535 os.unlink(os.path.join(repo.path, 'histedit-state'))
535 os.unlink(os.path.join(repo.path, 'histedit-state'))
536 return
536 return
537 else:
537 else:
538 cmdutil.checkunfinished(repo)
538 cmdutil.checkunfinished(repo)
539 cmdutil.bailifchanged(repo)
539 cmdutil.bailifchanged(repo)
540
540
541 topmost, empty = repo.dirstate.parents()
541 topmost, empty = repo.dirstate.parents()
542 if outg:
542 if outg:
543 if freeargs:
543 if freeargs:
544 remote = freeargs[0]
544 remote = freeargs[0]
545 else:
545 else:
546 remote = None
546 remote = None
547 root = findoutgoing(ui, repo, remote, force, opts)
547 root = findoutgoing(ui, repo, remote, force, opts)
548 else:
548 else:
549 root = revs[0]
549 root = revs[0]
550 root = scmutil.revsingle(repo, root).node()
550 root = scmutil.revsingle(repo, root).node()
551
551
552 keep = opts.get('keep', False)
552 keep = opts.get('keep', False)
553 revs = between(repo, root, topmost, keep)
553 revs = between(repo, root, topmost, keep)
554 if not revs:
554 if not revs:
555 raise util.Abort(_('%s is not an ancestor of working directory') %
555 raise util.Abort(_('%s is not an ancestor of working directory') %
556 node.short(root))
556 node.short(root))
557
557
558 ctxs = [repo[r] for r in revs]
558 ctxs = [repo[r] for r in revs]
559 if not rules:
559 if not rules:
560 rules = '\n'.join([makedesc(c) for c in ctxs])
560 rules = '\n'.join([makedesc(c) for c in ctxs])
561 rules += '\n\n'
561 rules += '\n\n'
562 rules += editcomment % (node.short(root), node.short(topmost))
562 rules += editcomment % (node.short(root), node.short(topmost))
563 rules = ui.edit(rules, ui.username())
563 rules = ui.edit(rules, ui.username())
564 # Save edit rules in .hg/histedit-last-edit.txt in case
564 # Save edit rules in .hg/histedit-last-edit.txt in case
565 # the user needs to ask for help after something
565 # the user needs to ask for help after something
566 # surprising happens.
566 # surprising happens.
567 f = open(repo.join('histedit-last-edit.txt'), 'w')
567 f = open(repo.join('histedit-last-edit.txt'), 'w')
568 f.write(rules)
568 f.write(rules)
569 f.close()
569 f.close()
570 else:
570 else:
571 if rules == '-':
571 if rules == '-':
572 f = sys.stdin
572 f = sys.stdin
573 else:
573 else:
574 f = open(rules)
574 f = open(rules)
575 rules = f.read()
575 rules = f.read()
576 f.close()
576 f.close()
577 rules = [l for l in (r.strip() for r in rules.splitlines())
577 rules = [l for l in (r.strip() for r in rules.splitlines())
578 if l and not l[0] == '#']
578 if l and not l[0] == '#']
579 rules = verifyrules(rules, repo, ctxs)
579 rules = verifyrules(rules, repo, ctxs)
580
580
581 parentctx = repo[root].parents()[0]
581 parentctx = repo[root].parents()[0]
582 keep = opts.get('keep', False)
582 keep = opts.get('keep', False)
583 replacements = []
583 replacements = []
584
584
585
585
586 while rules:
586 while rules:
587 writestate(repo, parentctx.node(), rules, keep, topmost, replacements)
587 writestate(repo, parentctx.node(), rules, keep, topmost, replacements)
588 action, ha = rules.pop(0)
588 action, ha = rules.pop(0)
589 ui.debug('histedit: processing %s %s\n' % (action, ha))
589 ui.debug('histedit: processing %s %s\n' % (action, ha))
590 actfunc = actiontable[action]
590 actfunc = actiontable[action]
591 parentctx, replacement_ = actfunc(ui, repo, parentctx, ha, opts)
591 parentctx, replacement_ = actfunc(ui, repo, parentctx, ha, opts)
592 replacements.extend(replacement_)
592 replacements.extend(replacement_)
593
593
594 hg.update(repo, parentctx.node())
594 hg.update(repo, parentctx.node())
595
595
596 mapping, tmpnodes, created, ntm = processreplacement(repo, replacements)
596 mapping, tmpnodes, created, ntm = processreplacement(repo, replacements)
597 if mapping:
597 if mapping:
598 for prec, succs in mapping.iteritems():
598 for prec, succs in mapping.iteritems():
599 if not succs:
599 if not succs:
600 ui.debug('histedit: %s is dropped\n' % node.short(prec))
600 ui.debug('histedit: %s is dropped\n' % node.short(prec))
601 else:
601 else:
602 ui.debug('histedit: %s is replaced by %s\n' % (
602 ui.debug('histedit: %s is replaced by %s\n' % (
603 node.short(prec), node.short(succs[0])))
603 node.short(prec), node.short(succs[0])))
604 if len(succs) > 1:
604 if len(succs) > 1:
605 m = 'histedit: %s'
605 m = 'histedit: %s'
606 for n in succs[1:]:
606 for n in succs[1:]:
607 ui.debug(m % node.short(n))
607 ui.debug(m % node.short(n))
608
608
609 if not keep:
609 if not keep:
610 if mapping:
610 if mapping:
611 movebookmarks(ui, repo, mapping, topmost, ntm)
611 movebookmarks(ui, repo, mapping, topmost, ntm)
612 # TODO update mq state
612 # TODO update mq state
613 if obsolete._enabled:
613 if obsolete._enabled:
614 markers = []
614 markers = []
615 # sort by revision number because it sound "right"
615 # sort by revision number because it sound "right"
616 for prec in sorted(mapping, key=repo.changelog.rev):
616 for prec in sorted(mapping, key=repo.changelog.rev):
617 succs = mapping[prec]
617 succs = mapping[prec]
618 markers.append((repo[prec],
618 markers.append((repo[prec],
619 tuple(repo[s] for s in succs)))
619 tuple(repo[s] for s in succs)))
620 if markers:
620 if markers:
621 obsolete.createmarkers(repo, markers)
621 obsolete.createmarkers(repo, markers)
622 else:
622 else:
623 cleanupnode(ui, repo, 'replaced', mapping)
623 cleanupnode(ui, repo, 'replaced', mapping)
624
624
625 cleanupnode(ui, repo, 'temp', tmpnodes)
625 cleanupnode(ui, repo, 'temp', tmpnodes)
626 os.unlink(os.path.join(repo.path, 'histedit-state'))
626 os.unlink(os.path.join(repo.path, 'histedit-state'))
627 if os.path.exists(repo.sjoin('undo')):
627 if os.path.exists(repo.sjoin('undo')):
628 os.unlink(repo.sjoin('undo'))
628 os.unlink(repo.sjoin('undo'))
629
629
630
630
631 def bootstrapcontinue(ui, repo, parentctx, rules, opts):
631 def bootstrapcontinue(ui, repo, parentctx, rules, opts):
632 action, currentnode = rules.pop(0)
632 action, currentnode = rules.pop(0)
633 ctx = repo[currentnode]
633 ctx = repo[currentnode]
634 # is there any new commit between the expected parent and "."
634 # is there any new commit between the expected parent and "."
635 #
635 #
636 # note: does not take non linear new change in account (but previous
636 # note: does not take non linear new change in account (but previous
637 # implementation didn't used them anyway (issue3655)
637 # implementation didn't used them anyway (issue3655)
638 newchildren = [c.node() for c in repo.set('(%d::.)', parentctx)]
638 newchildren = [c.node() for c in repo.set('(%d::.)', parentctx)]
639 if parentctx.node() != node.nullid:
639 if parentctx.node() != node.nullid:
640 if not newchildren:
640 if not newchildren:
641 # `parentctxnode` should match but no result. This means that
641 # `parentctxnode` should match but no result. This means that
642 # currentnode is not a descendant from parentctxnode.
642 # currentnode is not a descendant from parentctxnode.
643 msg = _('%s is not an ancestor of working directory')
643 msg = _('%s is not an ancestor of working directory')
644 hint = _('update to %s or descendant and run "hg histedit '
644 hint = _('use "histedit --abort" to clear broken state')
645 '--continue" again') % parentctx
646 raise util.Abort(msg % parentctx, hint=hint)
645 raise util.Abort(msg % parentctx, hint=hint)
647 newchildren.pop(0) # remove parentctxnode
646 newchildren.pop(0) # remove parentctxnode
648 # Commit dirty working directory if necessary
647 # Commit dirty working directory if necessary
649 new = None
648 new = None
650 m, a, r, d = repo.status()[:4]
649 m, a, r, d = repo.status()[:4]
651 if m or a or r or d:
650 if m or a or r or d:
652 # prepare the message for the commit to comes
651 # prepare the message for the commit to comes
653 if action in ('f', 'fold'):
652 if action in ('f', 'fold'):
654 message = 'fold-temp-revision %s' % currentnode
653 message = 'fold-temp-revision %s' % currentnode
655 else:
654 else:
656 message = ctx.description() + '\n'
655 message = ctx.description() + '\n'
657 if action in ('e', 'edit', 'm', 'mess'):
656 if action in ('e', 'edit', 'm', 'mess'):
658 editor = cmdutil.commitforceeditor
657 editor = cmdutil.commitforceeditor
659 else:
658 else:
660 editor = False
659 editor = False
661 commit = commitfuncfor(repo, ctx)
660 commit = commitfuncfor(repo, ctx)
662 new = commit(text=message, user=ctx.user(),
661 new = commit(text=message, user=ctx.user(),
663 date=ctx.date(), extra=ctx.extra(),
662 date=ctx.date(), extra=ctx.extra(),
664 editor=editor)
663 editor=editor)
665 if new is not None:
664 if new is not None:
666 newchildren.append(new)
665 newchildren.append(new)
667
666
668 replacements = []
667 replacements = []
669 # track replacements
668 # track replacements
670 if ctx.node() not in newchildren:
669 if ctx.node() not in newchildren:
671 # note: new children may be empty when the changeset is dropped.
670 # note: new children may be empty when the changeset is dropped.
672 # this happen e.g during conflicting pick where we revert content
671 # this happen e.g during conflicting pick where we revert content
673 # to parent.
672 # to parent.
674 replacements.append((ctx.node(), tuple(newchildren)))
673 replacements.append((ctx.node(), tuple(newchildren)))
675
674
676 if action in ('f', 'fold'):
675 if action in ('f', 'fold'):
677 if newchildren:
676 if newchildren:
678 # finalize fold operation if applicable
677 # finalize fold operation if applicable
679 if new is None:
678 if new is None:
680 new = newchildren[-1]
679 new = newchildren[-1]
681 else:
680 else:
682 newchildren.pop() # remove new from internal changes
681 newchildren.pop() # remove new from internal changes
683 parentctx, repl = finishfold(ui, repo, parentctx, ctx, new, opts,
682 parentctx, repl = finishfold(ui, repo, parentctx, ctx, new, opts,
684 newchildren)
683 newchildren)
685 replacements.extend(repl)
684 replacements.extend(repl)
686 else:
685 else:
687 # newchildren is empty if the fold did not result in any commit
686 # newchildren is empty if the fold did not result in any commit
688 # this happen when all folded change are discarded during the
687 # this happen when all folded change are discarded during the
689 # merge.
688 # merge.
690 replacements.append((ctx.node(), (parentctx.node(),)))
689 replacements.append((ctx.node(), (parentctx.node(),)))
691 elif newchildren:
690 elif newchildren:
692 # otherwise update "parentctx" before proceeding to further operation
691 # otherwise update "parentctx" before proceeding to further operation
693 parentctx = repo[newchildren[-1]]
692 parentctx = repo[newchildren[-1]]
694 return parentctx, replacements
693 return parentctx, replacements
695
694
696
695
697 def between(repo, old, new, keep):
696 def between(repo, old, new, keep):
698 """select and validate the set of revision to edit
697 """select and validate the set of revision to edit
699
698
700 When keep is false, the specified set can't have children."""
699 When keep is false, the specified set can't have children."""
701 ctxs = list(repo.set('%n::%n', old, new))
700 ctxs = list(repo.set('%n::%n', old, new))
702 if ctxs and not keep:
701 if ctxs and not keep:
703 if (not obsolete._enabled and
702 if (not obsolete._enabled and
704 repo.revs('(%ld::) - (%ld)', ctxs, ctxs)):
703 repo.revs('(%ld::) - (%ld)', ctxs, ctxs)):
705 raise util.Abort(_('cannot edit history that would orphan nodes'))
704 raise util.Abort(_('cannot edit history that would orphan nodes'))
706 if repo.revs('(%ld) and merge()', ctxs):
705 if repo.revs('(%ld) and merge()', ctxs):
707 raise util.Abort(_('cannot edit history that contains merges'))
706 raise util.Abort(_('cannot edit history that contains merges'))
708 root = ctxs[0] # list is already sorted by repo.set
707 root = ctxs[0] # list is already sorted by repo.set
709 if not root.phase():
708 if not root.phase():
710 raise util.Abort(_('cannot edit immutable changeset: %s') % root)
709 raise util.Abort(_('cannot edit immutable changeset: %s') % root)
711 return [c.node() for c in ctxs]
710 return [c.node() for c in ctxs]
712
711
713
712
714 def writestate(repo, parentnode, rules, keep, topmost, replacements):
713 def writestate(repo, parentnode, rules, keep, topmost, replacements):
715 fp = open(os.path.join(repo.path, 'histedit-state'), 'w')
714 fp = open(os.path.join(repo.path, 'histedit-state'), 'w')
716 pickle.dump((parentnode, rules, keep, topmost, replacements), fp)
715 pickle.dump((parentnode, rules, keep, topmost, replacements), fp)
717 fp.close()
716 fp.close()
718
717
719 def readstate(repo):
718 def readstate(repo):
720 """Returns a tuple of (parentnode, rules, keep, topmost, replacements).
719 """Returns a tuple of (parentnode, rules, keep, topmost, replacements).
721 """
720 """
722 fp = open(os.path.join(repo.path, 'histedit-state'))
721 fp = open(os.path.join(repo.path, 'histedit-state'))
723 return pickle.load(fp)
722 return pickle.load(fp)
724
723
725
724
726 def makedesc(c):
725 def makedesc(c):
727 """build a initial action line for a ctx `c`
726 """build a initial action line for a ctx `c`
728
727
729 line are in the form:
728 line are in the form:
730
729
731 pick <hash> <rev> <summary>
730 pick <hash> <rev> <summary>
732 """
731 """
733 summary = ''
732 summary = ''
734 if c.description():
733 if c.description():
735 summary = c.description().splitlines()[0]
734 summary = c.description().splitlines()[0]
736 line = 'pick %s %d %s' % (c, c.rev(), summary)
735 line = 'pick %s %d %s' % (c, c.rev(), summary)
737 return line[:80] # trim to 80 chars so it's not stupidly wide in my editor
736 return line[:80] # trim to 80 chars so it's not stupidly wide in my editor
738
737
739 def verifyrules(rules, repo, ctxs):
738 def verifyrules(rules, repo, ctxs):
740 """Verify that there exists exactly one edit rule per given changeset.
739 """Verify that there exists exactly one edit rule per given changeset.
741
740
742 Will abort if there are to many or too few rules, a malformed rule,
741 Will abort if there are to many or too few rules, a malformed rule,
743 or a rule on a changeset outside of the user-given range.
742 or a rule on a changeset outside of the user-given range.
744 """
743 """
745 parsed = []
744 parsed = []
746 expected = set(str(c) for c in ctxs)
745 expected = set(str(c) for c in ctxs)
747 seen = set()
746 seen = set()
748 for r in rules:
747 for r in rules:
749 if ' ' not in r:
748 if ' ' not in r:
750 raise util.Abort(_('malformed line "%s"') % r)
749 raise util.Abort(_('malformed line "%s"') % r)
751 action, rest = r.split(' ', 1)
750 action, rest = r.split(' ', 1)
752 ha = rest.strip().split(' ', 1)[0]
751 ha = rest.strip().split(' ', 1)[0]
753 try:
752 try:
754 ha = str(repo[ha]) # ensure its a short hash
753 ha = str(repo[ha]) # ensure its a short hash
755 except error.RepoError:
754 except error.RepoError:
756 raise util.Abort(_('unknown changeset %s listed') % ha)
755 raise util.Abort(_('unknown changeset %s listed') % ha)
757 if ha not in expected:
756 if ha not in expected:
758 raise util.Abort(
757 raise util.Abort(
759 _('may not use changesets other than the ones listed'))
758 _('may not use changesets other than the ones listed'))
760 if ha in seen:
759 if ha in seen:
761 raise util.Abort(_('duplicated command for changeset %s') % ha)
760 raise util.Abort(_('duplicated command for changeset %s') % ha)
762 seen.add(ha)
761 seen.add(ha)
763 if action not in actiontable:
762 if action not in actiontable:
764 raise util.Abort(_('unknown action "%s"') % action)
763 raise util.Abort(_('unknown action "%s"') % action)
765 parsed.append([action, ha])
764 parsed.append([action, ha])
766 missing = sorted(expected - seen) # sort to stabilize output
765 missing = sorted(expected - seen) # sort to stabilize output
767 if missing:
766 if missing:
768 raise util.Abort(_('missing rules for changeset %s') % missing[0],
767 raise util.Abort(_('missing rules for changeset %s') % missing[0],
769 hint=_('do you want to use the drop action?'))
768 hint=_('do you want to use the drop action?'))
770 return parsed
769 return parsed
771
770
772 def processreplacement(repo, replacements):
771 def processreplacement(repo, replacements):
773 """process the list of replacements to return
772 """process the list of replacements to return
774
773
775 1) the final mapping between original and created nodes
774 1) the final mapping between original and created nodes
776 2) the list of temporary node created by histedit
775 2) the list of temporary node created by histedit
777 3) the list of new commit created by histedit"""
776 3) the list of new commit created by histedit"""
778 allsuccs = set()
777 allsuccs = set()
779 replaced = set()
778 replaced = set()
780 fullmapping = {}
779 fullmapping = {}
781 # initialise basic set
780 # initialise basic set
782 # fullmapping record all operation recorded in replacement
781 # fullmapping record all operation recorded in replacement
783 for rep in replacements:
782 for rep in replacements:
784 allsuccs.update(rep[1])
783 allsuccs.update(rep[1])
785 replaced.add(rep[0])
784 replaced.add(rep[0])
786 fullmapping.setdefault(rep[0], set()).update(rep[1])
785 fullmapping.setdefault(rep[0], set()).update(rep[1])
787 new = allsuccs - replaced
786 new = allsuccs - replaced
788 tmpnodes = allsuccs & replaced
787 tmpnodes = allsuccs & replaced
789 # Reduce content fullmapping into direct relation between original nodes
788 # Reduce content fullmapping into direct relation between original nodes
790 # and final node created during history edition
789 # and final node created during history edition
791 # Dropped changeset are replaced by an empty list
790 # Dropped changeset are replaced by an empty list
792 toproceed = set(fullmapping)
791 toproceed = set(fullmapping)
793 final = {}
792 final = {}
794 while toproceed:
793 while toproceed:
795 for x in list(toproceed):
794 for x in list(toproceed):
796 succs = fullmapping[x]
795 succs = fullmapping[x]
797 for s in list(succs):
796 for s in list(succs):
798 if s in toproceed:
797 if s in toproceed:
799 # non final node with unknown closure
798 # non final node with unknown closure
800 # We can't process this now
799 # We can't process this now
801 break
800 break
802 elif s in final:
801 elif s in final:
803 # non final node, replace with closure
802 # non final node, replace with closure
804 succs.remove(s)
803 succs.remove(s)
805 succs.update(final[s])
804 succs.update(final[s])
806 else:
805 else:
807 final[x] = succs
806 final[x] = succs
808 toproceed.remove(x)
807 toproceed.remove(x)
809 # remove tmpnodes from final mapping
808 # remove tmpnodes from final mapping
810 for n in tmpnodes:
809 for n in tmpnodes:
811 del final[n]
810 del final[n]
812 # we expect all changes involved in final to exist in the repo
811 # we expect all changes involved in final to exist in the repo
813 # turn `final` into list (topologically sorted)
812 # turn `final` into list (topologically sorted)
814 nm = repo.changelog.nodemap
813 nm = repo.changelog.nodemap
815 for prec, succs in final.items():
814 for prec, succs in final.items():
816 final[prec] = sorted(succs, key=nm.get)
815 final[prec] = sorted(succs, key=nm.get)
817
816
818 # computed topmost element (necessary for bookmark)
817 # computed topmost element (necessary for bookmark)
819 if new:
818 if new:
820 newtopmost = sorted(new, key=repo.changelog.rev)[-1]
819 newtopmost = sorted(new, key=repo.changelog.rev)[-1]
821 elif not final:
820 elif not final:
822 # Nothing rewritten at all. we won't need `newtopmost`
821 # Nothing rewritten at all. we won't need `newtopmost`
823 # It is the same as `oldtopmost` and `processreplacement` know it
822 # It is the same as `oldtopmost` and `processreplacement` know it
824 newtopmost = None
823 newtopmost = None
825 else:
824 else:
826 # every body died. The newtopmost is the parent of the root.
825 # every body died. The newtopmost is the parent of the root.
827 newtopmost = repo[sorted(final, key=repo.changelog.rev)[0]].p1().node()
826 newtopmost = repo[sorted(final, key=repo.changelog.rev)[0]].p1().node()
828
827
829 return final, tmpnodes, new, newtopmost
828 return final, tmpnodes, new, newtopmost
830
829
831 def movebookmarks(ui, repo, mapping, oldtopmost, newtopmost):
830 def movebookmarks(ui, repo, mapping, oldtopmost, newtopmost):
832 """Move bookmark from old to newly created node"""
831 """Move bookmark from old to newly created node"""
833 if not mapping:
832 if not mapping:
834 # if nothing got rewritten there is not purpose for this function
833 # if nothing got rewritten there is not purpose for this function
835 return
834 return
836 moves = []
835 moves = []
837 for bk, old in sorted(repo._bookmarks.iteritems()):
836 for bk, old in sorted(repo._bookmarks.iteritems()):
838 if old == oldtopmost:
837 if old == oldtopmost:
839 # special case ensure bookmark stay on tip.
838 # special case ensure bookmark stay on tip.
840 #
839 #
841 # This is arguably a feature and we may only want that for the
840 # This is arguably a feature and we may only want that for the
842 # active bookmark. But the behavior is kept compatible with the old
841 # active bookmark. But the behavior is kept compatible with the old
843 # version for now.
842 # version for now.
844 moves.append((bk, newtopmost))
843 moves.append((bk, newtopmost))
845 continue
844 continue
846 base = old
845 base = old
847 new = mapping.get(base, None)
846 new = mapping.get(base, None)
848 if new is None:
847 if new is None:
849 continue
848 continue
850 while not new:
849 while not new:
851 # base is killed, trying with parent
850 # base is killed, trying with parent
852 base = repo[base].p1().node()
851 base = repo[base].p1().node()
853 new = mapping.get(base, (base,))
852 new = mapping.get(base, (base,))
854 # nothing to move
853 # nothing to move
855 moves.append((bk, new[-1]))
854 moves.append((bk, new[-1]))
856 if moves:
855 if moves:
857 marks = repo._bookmarks
856 marks = repo._bookmarks
858 for mark, new in moves:
857 for mark, new in moves:
859 old = marks[mark]
858 old = marks[mark]
860 ui.note(_('histedit: moving bookmarks %s from %s to %s\n')
859 ui.note(_('histedit: moving bookmarks %s from %s to %s\n')
861 % (mark, node.short(old), node.short(new)))
860 % (mark, node.short(old), node.short(new)))
862 marks[mark] = new
861 marks[mark] = new
863 marks.write()
862 marks.write()
864
863
865 def cleanupnode(ui, repo, name, nodes):
864 def cleanupnode(ui, repo, name, nodes):
866 """strip a group of nodes from the repository
865 """strip a group of nodes from the repository
867
866
868 The set of node to strip may contains unknown nodes."""
867 The set of node to strip may contains unknown nodes."""
869 ui.debug('should strip %s nodes %s\n' %
868 ui.debug('should strip %s nodes %s\n' %
870 (name, ', '.join([node.short(n) for n in nodes])))
869 (name, ', '.join([node.short(n) for n in nodes])))
871 lock = None
870 lock = None
872 try:
871 try:
873 lock = repo.lock()
872 lock = repo.lock()
874 # Find all node that need to be stripped
873 # Find all node that need to be stripped
875 # (we hg %lr instead of %ln to silently ignore unknown item
874 # (we hg %lr instead of %ln to silently ignore unknown item
876 nm = repo.changelog.nodemap
875 nm = repo.changelog.nodemap
877 nodes = [n for n in nodes if n in nm]
876 nodes = [n for n in nodes if n in nm]
878 roots = [c.node() for c in repo.set("roots(%ln)", nodes)]
877 roots = [c.node() for c in repo.set("roots(%ln)", nodes)]
879 for c in roots:
878 for c in roots:
880 # We should process node in reverse order to strip tip most first.
879 # We should process node in reverse order to strip tip most first.
881 # but this trigger a bug in changegroup hook.
880 # but this trigger a bug in changegroup hook.
882 # This would reduce bundle overhead
881 # This would reduce bundle overhead
883 repair.strip(ui, repo, c)
882 repair.strip(ui, repo, c)
884 finally:
883 finally:
885 lockmod.release(lock)
884 lockmod.release(lock)
886
885
887 def summaryhook(ui, repo):
886 def summaryhook(ui, repo):
888 if not os.path.exists(repo.join('histedit-state')):
887 if not os.path.exists(repo.join('histedit-state')):
889 return
888 return
890 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo)
889 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo)
891 if rules:
890 if rules:
892 # i18n: column positioning for "hg summary"
891 # i18n: column positioning for "hg summary"
893 ui.write(_('hist: %s (histedit --continue)\n') %
892 ui.write(_('hist: %s (histedit --continue)\n') %
894 (ui.label(_('%d remaining'), 'histedit.remaining') %
893 (ui.label(_('%d remaining'), 'histedit.remaining') %
895 len(rules)))
894 len(rules)))
896
895
897 def extsetup(ui):
896 def extsetup(ui):
898 cmdutil.summaryhooks.add('histedit', summaryhook)
897 cmdutil.summaryhooks.add('histedit', summaryhook)
899 cmdutil.unfinishedstates.append(
898 cmdutil.unfinishedstates.append(
900 ['histedit-state', False, True, _('histedit in progress'),
899 ['histedit-state', False, True, _('histedit in progress'),
901 _("use 'hg histedit --continue' or 'hg histedit --abort'")])
900 _("use 'hg histedit --continue' or 'hg histedit --abort'")])
@@ -1,822 +1,839
1 # rebase.py - rebasing feature for mercurial
1 # rebase.py - rebasing feature for mercurial
2 #
2 #
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot 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 '''command to move sets of revisions to a different ancestor
8 '''command to move sets of revisions to a different ancestor
9
9
10 This extension lets you rebase changesets in an existing Mercurial
10 This extension lets you rebase changesets in an existing Mercurial
11 repository.
11 repository.
12
12
13 For more information:
13 For more information:
14 http://mercurial.selenic.com/wiki/RebaseExtension
14 http://mercurial.selenic.com/wiki/RebaseExtension
15 '''
15 '''
16
16
17 from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
17 from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
18 from mercurial import extensions, patch, scmutil, phases, obsolete, error
18 from mercurial import extensions, patch, scmutil, phases, obsolete, error
19 from mercurial.commands import templateopts
19 from mercurial.commands import templateopts
20 from mercurial.node import nullrev
20 from mercurial.node import nullrev
21 from mercurial.lock import release
21 from mercurial.lock import release
22 from mercurial.i18n import _
22 from mercurial.i18n import _
23 import os, errno
23 import os, errno
24
24
25 nullmerge = -2
25 nullmerge = -2
26 revignored = -3
26 revignored = -3
27
27
28 cmdtable = {}
28 cmdtable = {}
29 command = cmdutil.command(cmdtable)
29 command = cmdutil.command(cmdtable)
30 testedwith = 'internal'
30 testedwith = 'internal'
31
31
32 @command('rebase',
32 @command('rebase',
33 [('s', 'source', '',
33 [('s', 'source', '',
34 _('rebase from the specified changeset'), _('REV')),
34 _('rebase from the specified changeset'), _('REV')),
35 ('b', 'base', '',
35 ('b', 'base', '',
36 _('rebase from the base of the specified changeset '
36 _('rebase from the base of the specified changeset '
37 '(up to greatest common ancestor of base and dest)'),
37 '(up to greatest common ancestor of base and dest)'),
38 _('REV')),
38 _('REV')),
39 ('r', 'rev', [],
39 ('r', 'rev', [],
40 _('rebase these revisions'),
40 _('rebase these revisions'),
41 _('REV')),
41 _('REV')),
42 ('d', 'dest', '',
42 ('d', 'dest', '',
43 _('rebase onto the specified changeset'), _('REV')),
43 _('rebase onto the specified changeset'), _('REV')),
44 ('', 'collapse', False, _('collapse the rebased changesets')),
44 ('', 'collapse', False, _('collapse the rebased changesets')),
45 ('m', 'message', '',
45 ('m', 'message', '',
46 _('use text as collapse commit message'), _('TEXT')),
46 _('use text as collapse commit message'), _('TEXT')),
47 ('e', 'edit', False, _('invoke editor on commit messages')),
47 ('e', 'edit', False, _('invoke editor on commit messages')),
48 ('l', 'logfile', '',
48 ('l', 'logfile', '',
49 _('read collapse commit message from file'), _('FILE')),
49 _('read collapse commit message from file'), _('FILE')),
50 ('', 'keep', False, _('keep original changesets')),
50 ('', 'keep', False, _('keep original changesets')),
51 ('', 'keepbranches', False, _('keep original branch names')),
51 ('', 'keepbranches', False, _('keep original branch names')),
52 ('D', 'detach', False, _('(DEPRECATED)')),
52 ('D', 'detach', False, _('(DEPRECATED)')),
53 ('t', 'tool', '', _('specify merge tool')),
53 ('t', 'tool', '', _('specify merge tool')),
54 ('c', 'continue', False, _('continue an interrupted rebase')),
54 ('c', 'continue', False, _('continue an interrupted rebase')),
55 ('a', 'abort', False, _('abort an interrupted rebase'))] +
55 ('a', 'abort', False, _('abort an interrupted rebase'))] +
56 templateopts,
56 templateopts,
57 _('[-s REV | -b REV] [-d REV] [OPTION]'))
57 _('[-s REV | -b REV] [-d REV] [OPTION]'))
58 def rebase(ui, repo, **opts):
58 def rebase(ui, repo, **opts):
59 """move changeset (and descendants) to a different branch
59 """move changeset (and descendants) to a different branch
60
60
61 Rebase uses repeated merging to graft changesets from one part of
61 Rebase uses repeated merging to graft changesets from one part of
62 history (the source) onto another (the destination). This can be
62 history (the source) onto another (the destination). This can be
63 useful for linearizing *local* changes relative to a master
63 useful for linearizing *local* changes relative to a master
64 development tree.
64 development tree.
65
65
66 You should not rebase changesets that have already been shared
66 You should not rebase changesets that have already been shared
67 with others. Doing so will force everybody else to perform the
67 with others. Doing so will force everybody else to perform the
68 same rebase or they will end up with duplicated changesets after
68 same rebase or they will end up with duplicated changesets after
69 pulling in your rebased changesets.
69 pulling in your rebased changesets.
70
70
71 In its default configuration, Mercurial will prevent you from
71 In its default configuration, Mercurial will prevent you from
72 rebasing published changes. See :hg:`help phases` for details.
72 rebasing published changes. See :hg:`help phases` for details.
73
73
74 If you don't specify a destination changeset (``-d/--dest``),
74 If you don't specify a destination changeset (``-d/--dest``),
75 rebase uses the current branch tip as the destination. (The
75 rebase uses the current branch tip as the destination. (The
76 destination changeset is not modified by rebasing, but new
76 destination changeset is not modified by rebasing, but new
77 changesets are added as its descendants.)
77 changesets are added as its descendants.)
78
78
79 You can specify which changesets to rebase in two ways: as a
79 You can specify which changesets to rebase in two ways: as a
80 "source" changeset or as a "base" changeset. Both are shorthand
80 "source" changeset or as a "base" changeset. Both are shorthand
81 for a topologically related set of changesets (the "source
81 for a topologically related set of changesets (the "source
82 branch"). If you specify source (``-s/--source``), rebase will
82 branch"). If you specify source (``-s/--source``), rebase will
83 rebase that changeset and all of its descendants onto dest. If you
83 rebase that changeset and all of its descendants onto dest. If you
84 specify base (``-b/--base``), rebase will select ancestors of base
84 specify base (``-b/--base``), rebase will select ancestors of base
85 back to but not including the common ancestor with dest. Thus,
85 back to but not including the common ancestor with dest. Thus,
86 ``-b`` is less precise but more convenient than ``-s``: you can
86 ``-b`` is less precise but more convenient than ``-s``: you can
87 specify any changeset in the source branch, and rebase will select
87 specify any changeset in the source branch, and rebase will select
88 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
88 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
89 uses the parent of the working directory as the base.
89 uses the parent of the working directory as the base.
90
90
91 For advanced usage, a third way is available through the ``--rev``
91 For advanced usage, a third way is available through the ``--rev``
92 option. It allows you to specify an arbitrary set of changesets to
92 option. It allows you to specify an arbitrary set of changesets to
93 rebase. Descendants of revs you specify with this option are not
93 rebase. Descendants of revs you specify with this option are not
94 automatically included in the rebase.
94 automatically included in the rebase.
95
95
96 By default, rebase recreates the changesets in the source branch
96 By default, rebase recreates the changesets in the source branch
97 as descendants of dest and then destroys the originals. Use
97 as descendants of dest and then destroys the originals. Use
98 ``--keep`` to preserve the original source changesets. Some
98 ``--keep`` to preserve the original source changesets. Some
99 changesets in the source branch (e.g. merges from the destination
99 changesets in the source branch (e.g. merges from the destination
100 branch) may be dropped if they no longer contribute any change.
100 branch) may be dropped if they no longer contribute any change.
101
101
102 One result of the rules for selecting the destination changeset
102 One result of the rules for selecting the destination changeset
103 and source branch is that, unlike ``merge``, rebase will do
103 and source branch is that, unlike ``merge``, rebase will do
104 nothing if you are at the branch tip of a named branch
104 nothing if you are at the branch tip of a named branch
105 with two heads. You need to explicitly specify source and/or
105 with two heads. You need to explicitly specify source and/or
106 destination (or ``update`` to the other head, if it's the head of
106 destination (or ``update`` to the other head, if it's the head of
107 the intended source branch).
107 the intended source branch).
108
108
109 If a rebase is interrupted to manually resolve a merge, it can be
109 If a rebase is interrupted to manually resolve a merge, it can be
110 continued with --continue/-c or aborted with --abort/-a.
110 continued with --continue/-c or aborted with --abort/-a.
111
111
112 Returns 0 on success, 1 if nothing to rebase.
112 Returns 0 on success, 1 if nothing to rebase.
113 """
113 """
114 originalwd = target = None
114 originalwd = target = None
115 activebookmark = None
115 activebookmark = None
116 external = nullrev
116 external = nullrev
117 state = {}
117 state = {}
118 skipped = set()
118 skipped = set()
119 targetancestors = set()
119 targetancestors = set()
120
120
121 editor = None
121 editor = None
122 if opts.get('edit'):
122 if opts.get('edit'):
123 editor = cmdutil.commitforceeditor
123 editor = cmdutil.commitforceeditor
124
124
125 lock = wlock = None
125 lock = wlock = None
126 try:
126 try:
127 wlock = repo.wlock()
127 wlock = repo.wlock()
128 lock = repo.lock()
128 lock = repo.lock()
129
129
130 # Validate input and define rebasing points
130 # Validate input and define rebasing points
131 destf = opts.get('dest', None)
131 destf = opts.get('dest', None)
132 srcf = opts.get('source', None)
132 srcf = opts.get('source', None)
133 basef = opts.get('base', None)
133 basef = opts.get('base', None)
134 revf = opts.get('rev', [])
134 revf = opts.get('rev', [])
135 contf = opts.get('continue')
135 contf = opts.get('continue')
136 abortf = opts.get('abort')
136 abortf = opts.get('abort')
137 collapsef = opts.get('collapse', False)
137 collapsef = opts.get('collapse', False)
138 collapsemsg = cmdutil.logmessage(ui, opts)
138 collapsemsg = cmdutil.logmessage(ui, opts)
139 extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
139 extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
140 keepf = opts.get('keep', False)
140 keepf = opts.get('keep', False)
141 keepbranchesf = opts.get('keepbranches', False)
141 keepbranchesf = opts.get('keepbranches', False)
142 # keepopen is not meant for use on the command line, but by
142 # keepopen is not meant for use on the command line, but by
143 # other extensions
143 # other extensions
144 keepopen = opts.get('keepopen', False)
144 keepopen = opts.get('keepopen', False)
145
145
146 if collapsemsg and not collapsef:
146 if collapsemsg and not collapsef:
147 raise util.Abort(
147 raise util.Abort(
148 _('message can only be specified with collapse'))
148 _('message can only be specified with collapse'))
149
149
150 if contf or abortf:
150 if contf or abortf:
151 if contf and abortf:
151 if contf and abortf:
152 raise util.Abort(_('cannot use both abort and continue'))
152 raise util.Abort(_('cannot use both abort and continue'))
153 if collapsef:
153 if collapsef:
154 raise util.Abort(
154 raise util.Abort(
155 _('cannot use collapse with continue or abort'))
155 _('cannot use collapse with continue or abort'))
156 if srcf or basef or destf:
156 if srcf or basef or destf:
157 raise util.Abort(
157 raise util.Abort(
158 _('abort and continue do not allow specifying revisions'))
158 _('abort and continue do not allow specifying revisions'))
159 if opts.get('tool', False):
159 if opts.get('tool', False):
160 ui.warn(_('tool option will be ignored\n'))
160 ui.warn(_('tool option will be ignored\n'))
161
161
162 (originalwd, target, state, skipped, collapsef, keepf,
162 try:
163 keepbranchesf, external, activebookmark) = restorestatus(repo)
163 (originalwd, target, state, skipped, collapsef, keepf,
164 keepbranchesf, external, activebookmark) = restorestatus(repo)
165 except error.RepoLookupError:
166 if abortf:
167 clearstatus(repo)
168 repo.ui.warn(_('rebase aborted (no revision is removed,'
169 ' only broken state is cleared)\n'))
170 return 0
171 else:
172 msg = _('cannot continue inconsistent rebase')
173 hint = _('use "hg rebase --abort" to clear borken state')
174 raise util.Abort(msg, hint=hint)
164 if abortf:
175 if abortf:
165 return abort(repo, originalwd, target, state)
176 return abort(repo, originalwd, target, state)
166 else:
177 else:
167 if srcf and basef:
178 if srcf and basef:
168 raise util.Abort(_('cannot specify both a '
179 raise util.Abort(_('cannot specify both a '
169 'source and a base'))
180 'source and a base'))
170 if revf and basef:
181 if revf and basef:
171 raise util.Abort(_('cannot specify both a '
182 raise util.Abort(_('cannot specify both a '
172 'revision and a base'))
183 'revision and a base'))
173 if revf and srcf:
184 if revf and srcf:
174 raise util.Abort(_('cannot specify both a '
185 raise util.Abort(_('cannot specify both a '
175 'revision and a source'))
186 'revision and a source'))
176
187
177 cmdutil.checkunfinished(repo)
188 cmdutil.checkunfinished(repo)
178 cmdutil.bailifchanged(repo)
189 cmdutil.bailifchanged(repo)
179
190
180 if not destf:
191 if not destf:
181 # Destination defaults to the latest revision in the
192 # Destination defaults to the latest revision in the
182 # current branch
193 # current branch
183 branch = repo[None].branch()
194 branch = repo[None].branch()
184 dest = repo[branch]
195 dest = repo[branch]
185 else:
196 else:
186 dest = scmutil.revsingle(repo, destf)
197 dest = scmutil.revsingle(repo, destf)
187
198
188 if revf:
199 if revf:
189 rebaseset = scmutil.revrange(repo, revf)
200 rebaseset = scmutil.revrange(repo, revf)
190 elif srcf:
201 elif srcf:
191 src = scmutil.revrange(repo, [srcf])
202 src = scmutil.revrange(repo, [srcf])
192 rebaseset = repo.revs('(%ld)::', src)
203 rebaseset = repo.revs('(%ld)::', src)
193 else:
204 else:
194 base = scmutil.revrange(repo, [basef or '.'])
205 base = scmutil.revrange(repo, [basef or '.'])
195 rebaseset = repo.revs(
206 rebaseset = repo.revs(
196 '(children(ancestor(%ld, %d)) and ::(%ld))::',
207 '(children(ancestor(%ld, %d)) and ::(%ld))::',
197 base, dest, base)
208 base, dest, base)
198 if rebaseset:
209 if rebaseset:
199 root = min(rebaseset)
210 root = min(rebaseset)
200 else:
211 else:
201 root = None
212 root = None
202
213
203 if not rebaseset:
214 if not rebaseset:
204 repo.ui.debug('base is ancestor of destination\n')
215 repo.ui.debug('base is ancestor of destination\n')
205 result = None
216 result = None
206 elif (not (keepf or obsolete._enabled)
217 elif (not (keepf or obsolete._enabled)
207 and repo.revs('first(children(%ld) - %ld)',
218 and repo.revs('first(children(%ld) - %ld)',
208 rebaseset, rebaseset)):
219 rebaseset, rebaseset)):
209 raise util.Abort(
220 raise util.Abort(
210 _("can't remove original changesets with"
221 _("can't remove original changesets with"
211 " unrebased descendants"),
222 " unrebased descendants"),
212 hint=_('use --keep to keep original changesets'))
223 hint=_('use --keep to keep original changesets'))
213 else:
224 else:
214 result = buildstate(repo, dest, rebaseset, collapsef)
225 result = buildstate(repo, dest, rebaseset, collapsef)
215
226
216 if not result:
227 if not result:
217 # Empty state built, nothing to rebase
228 # Empty state built, nothing to rebase
218 ui.status(_('nothing to rebase\n'))
229 ui.status(_('nothing to rebase\n'))
219 return 1
230 return 1
220 elif not keepf and not repo[root].mutable():
231 elif not keepf and not repo[root].mutable():
221 raise util.Abort(_("can't rebase immutable changeset %s")
232 raise util.Abort(_("can't rebase immutable changeset %s")
222 % repo[root],
233 % repo[root],
223 hint=_('see hg help phases for details'))
234 hint=_('see hg help phases for details'))
224 else:
235 else:
225 originalwd, target, state = result
236 originalwd, target, state = result
226 if collapsef:
237 if collapsef:
227 targetancestors = repo.changelog.ancestors([target],
238 targetancestors = repo.changelog.ancestors([target],
228 inclusive=True)
239 inclusive=True)
229 external = checkexternal(repo, state, targetancestors)
240 external = checkexternal(repo, state, targetancestors)
230
241
231 if keepbranchesf:
242 if keepbranchesf:
232 assert not extrafn, 'cannot use both keepbranches and extrafn'
243 assert not extrafn, 'cannot use both keepbranches and extrafn'
233 def extrafn(ctx, extra):
244 def extrafn(ctx, extra):
234 extra['branch'] = ctx.branch()
245 extra['branch'] = ctx.branch()
235 if collapsef:
246 if collapsef:
236 branches = set()
247 branches = set()
237 for rev in state:
248 for rev in state:
238 branches.add(repo[rev].branch())
249 branches.add(repo[rev].branch())
239 if len(branches) > 1:
250 if len(branches) > 1:
240 raise util.Abort(_('cannot collapse multiple named '
251 raise util.Abort(_('cannot collapse multiple named '
241 'branches'))
252 'branches'))
242
253
243
254
244 # Rebase
255 # Rebase
245 if not targetancestors:
256 if not targetancestors:
246 targetancestors = repo.changelog.ancestors([target], inclusive=True)
257 targetancestors = repo.changelog.ancestors([target], inclusive=True)
247
258
248 # Keep track of the current bookmarks in order to reset them later
259 # Keep track of the current bookmarks in order to reset them later
249 currentbookmarks = repo._bookmarks.copy()
260 currentbookmarks = repo._bookmarks.copy()
250 activebookmark = activebookmark or repo._bookmarkcurrent
261 activebookmark = activebookmark or repo._bookmarkcurrent
251 if activebookmark:
262 if activebookmark:
252 bookmarks.unsetcurrent(repo)
263 bookmarks.unsetcurrent(repo)
253
264
254 sortedstate = sorted(state)
265 sortedstate = sorted(state)
255 total = len(sortedstate)
266 total = len(sortedstate)
256 pos = 0
267 pos = 0
257 for rev in sortedstate:
268 for rev in sortedstate:
258 pos += 1
269 pos += 1
259 if state[rev] == -1:
270 if state[rev] == -1:
260 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
271 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
261 _('changesets'), total)
272 _('changesets'), total)
262 p1, p2 = defineparents(repo, rev, target, state,
273 p1, p2 = defineparents(repo, rev, target, state,
263 targetancestors)
274 targetancestors)
264 storestatus(repo, originalwd, target, state, collapsef, keepf,
275 storestatus(repo, originalwd, target, state, collapsef, keepf,
265 keepbranchesf, external, activebookmark)
276 keepbranchesf, external, activebookmark)
266 if len(repo.parents()) == 2:
277 if len(repo.parents()) == 2:
267 repo.ui.debug('resuming interrupted rebase\n')
278 repo.ui.debug('resuming interrupted rebase\n')
268 else:
279 else:
269 try:
280 try:
270 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
281 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
271 stats = rebasenode(repo, rev, p1, state, collapsef)
282 stats = rebasenode(repo, rev, p1, state, collapsef)
272 if stats and stats[3] > 0:
283 if stats and stats[3] > 0:
273 raise error.InterventionRequired(
284 raise error.InterventionRequired(
274 _('unresolved conflicts (see hg '
285 _('unresolved conflicts (see hg '
275 'resolve, then hg rebase --continue)'))
286 'resolve, then hg rebase --continue)'))
276 finally:
287 finally:
277 ui.setconfig('ui', 'forcemerge', '')
288 ui.setconfig('ui', 'forcemerge', '')
278 cmdutil.duplicatecopies(repo, rev, target)
289 cmdutil.duplicatecopies(repo, rev, target)
279 if not collapsef:
290 if not collapsef:
280 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn,
291 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn,
281 editor=editor)
292 editor=editor)
282 else:
293 else:
283 # Skip commit if we are collapsing
294 # Skip commit if we are collapsing
284 repo.setparents(repo[p1].node())
295 repo.setparents(repo[p1].node())
285 newrev = None
296 newrev = None
286 # Update the state
297 # Update the state
287 if newrev is not None:
298 if newrev is not None:
288 state[rev] = repo[newrev].rev()
299 state[rev] = repo[newrev].rev()
289 else:
300 else:
290 if not collapsef:
301 if not collapsef:
291 ui.note(_('no changes, revision %d skipped\n') % rev)
302 ui.note(_('no changes, revision %d skipped\n') % rev)
292 ui.debug('next revision set to %s\n' % p1)
303 ui.debug('next revision set to %s\n' % p1)
293 skipped.add(rev)
304 skipped.add(rev)
294 state[rev] = p1
305 state[rev] = p1
295
306
296 ui.progress(_('rebasing'), None)
307 ui.progress(_('rebasing'), None)
297 ui.note(_('rebase merging completed\n'))
308 ui.note(_('rebase merging completed\n'))
298
309
299 if collapsef and not keepopen:
310 if collapsef and not keepopen:
300 p1, p2 = defineparents(repo, min(state), target,
311 p1, p2 = defineparents(repo, min(state), target,
301 state, targetancestors)
312 state, targetancestors)
302 if collapsemsg:
313 if collapsemsg:
303 commitmsg = collapsemsg
314 commitmsg = collapsemsg
304 else:
315 else:
305 commitmsg = 'Collapsed revision'
316 commitmsg = 'Collapsed revision'
306 for rebased in state:
317 for rebased in state:
307 if rebased not in skipped and state[rebased] > nullmerge:
318 if rebased not in skipped and state[rebased] > nullmerge:
308 commitmsg += '\n* %s' % repo[rebased].description()
319 commitmsg += '\n* %s' % repo[rebased].description()
309 commitmsg = ui.edit(commitmsg, repo.ui.username())
320 commitmsg = ui.edit(commitmsg, repo.ui.username())
310 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
321 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
311 extrafn=extrafn, editor=editor)
322 extrafn=extrafn, editor=editor)
312
323
313 if 'qtip' in repo.tags():
324 if 'qtip' in repo.tags():
314 updatemq(repo, state, skipped, **opts)
325 updatemq(repo, state, skipped, **opts)
315
326
316 if currentbookmarks:
327 if currentbookmarks:
317 # Nodeids are needed to reset bookmarks
328 # Nodeids are needed to reset bookmarks
318 nstate = {}
329 nstate = {}
319 for k, v in state.iteritems():
330 for k, v in state.iteritems():
320 if v > nullmerge:
331 if v > nullmerge:
321 nstate[repo[k].node()] = repo[v].node()
332 nstate[repo[k].node()] = repo[v].node()
322 # XXX this is the same as dest.node() for the non-continue path --
333 # XXX this is the same as dest.node() for the non-continue path --
323 # this should probably be cleaned up
334 # this should probably be cleaned up
324 targetnode = repo[target].node()
335 targetnode = repo[target].node()
325
336
326 if not keepf:
337 if not keepf:
327 collapsedas = None
338 collapsedas = None
328 if collapsef:
339 if collapsef:
329 collapsedas = newrev
340 collapsedas = newrev
330 clearrebased(ui, repo, state, skipped, collapsedas)
341 clearrebased(ui, repo, state, skipped, collapsedas)
331
342
332 if currentbookmarks:
343 if currentbookmarks:
333 updatebookmarks(repo, targetnode, nstate, currentbookmarks)
344 updatebookmarks(repo, targetnode, nstate, currentbookmarks)
334
345
335 clearstatus(repo)
346 clearstatus(repo)
336 ui.note(_("rebase completed\n"))
347 ui.note(_("rebase completed\n"))
337 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
348 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
338 if skipped:
349 if skipped:
339 ui.note(_("%d revisions have been skipped\n") % len(skipped))
350 ui.note(_("%d revisions have been skipped\n") % len(skipped))
340
351
341 if (activebookmark and
352 if (activebookmark and
342 repo['tip'].node() == repo._bookmarks[activebookmark]):
353 repo['tip'].node() == repo._bookmarks[activebookmark]):
343 bookmarks.setcurrent(repo, activebookmark)
354 bookmarks.setcurrent(repo, activebookmark)
344
355
345 finally:
356 finally:
346 release(lock, wlock)
357 release(lock, wlock)
347
358
348 def checkexternal(repo, state, targetancestors):
359 def checkexternal(repo, state, targetancestors):
349 """Check whether one or more external revisions need to be taken in
360 """Check whether one or more external revisions need to be taken in
350 consideration. In the latter case, abort.
361 consideration. In the latter case, abort.
351 """
362 """
352 external = nullrev
363 external = nullrev
353 source = min(state)
364 source = min(state)
354 for rev in state:
365 for rev in state:
355 if rev == source:
366 if rev == source:
356 continue
367 continue
357 # Check externals and fail if there are more than one
368 # Check externals and fail if there are more than one
358 for p in repo[rev].parents():
369 for p in repo[rev].parents():
359 if (p.rev() not in state
370 if (p.rev() not in state
360 and p.rev() not in targetancestors):
371 and p.rev() not in targetancestors):
361 if external != nullrev:
372 if external != nullrev:
362 raise util.Abort(_('unable to collapse, there is more '
373 raise util.Abort(_('unable to collapse, there is more '
363 'than one external parent'))
374 'than one external parent'))
364 external = p.rev()
375 external = p.rev()
365 return external
376 return external
366
377
367 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
378 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
368 'Commit the changes and store useful information in extra'
379 'Commit the changes and store useful information in extra'
369 try:
380 try:
370 repo.setparents(repo[p1].node(), repo[p2].node())
381 repo.setparents(repo[p1].node(), repo[p2].node())
371 ctx = repo[rev]
382 ctx = repo[rev]
372 if commitmsg is None:
383 if commitmsg is None:
373 commitmsg = ctx.description()
384 commitmsg = ctx.description()
374 extra = {'rebase_source': ctx.hex()}
385 extra = {'rebase_source': ctx.hex()}
375 if extrafn:
386 if extrafn:
376 extrafn(ctx, extra)
387 extrafn(ctx, extra)
377 # Commit might fail if unresolved files exist
388 # Commit might fail if unresolved files exist
378 newrev = repo.commit(text=commitmsg, user=ctx.user(),
389 newrev = repo.commit(text=commitmsg, user=ctx.user(),
379 date=ctx.date(), extra=extra, editor=editor)
390 date=ctx.date(), extra=extra, editor=editor)
380 repo.dirstate.setbranch(repo[newrev].branch())
391 repo.dirstate.setbranch(repo[newrev].branch())
381 targetphase = max(ctx.phase(), phases.draft)
392 targetphase = max(ctx.phase(), phases.draft)
382 # retractboundary doesn't overwrite upper phase inherited from parent
393 # retractboundary doesn't overwrite upper phase inherited from parent
383 newnode = repo[newrev].node()
394 newnode = repo[newrev].node()
384 if newnode:
395 if newnode:
385 phases.retractboundary(repo, targetphase, [newnode])
396 phases.retractboundary(repo, targetphase, [newnode])
386 return newrev
397 return newrev
387 except util.Abort:
398 except util.Abort:
388 # Invalidate the previous setparents
399 # Invalidate the previous setparents
389 repo.dirstate.invalidate()
400 repo.dirstate.invalidate()
390 raise
401 raise
391
402
392 def rebasenode(repo, rev, p1, state, collapse):
403 def rebasenode(repo, rev, p1, state, collapse):
393 'Rebase a single revision'
404 'Rebase a single revision'
394 # Merge phase
405 # Merge phase
395 # Update to target and merge it with local
406 # Update to target and merge it with local
396 if repo['.'].rev() != repo[p1].rev():
407 if repo['.'].rev() != repo[p1].rev():
397 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
408 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
398 merge.update(repo, p1, False, True, False)
409 merge.update(repo, p1, False, True, False)
399 else:
410 else:
400 repo.ui.debug(" already in target\n")
411 repo.ui.debug(" already in target\n")
401 repo.dirstate.write()
412 repo.dirstate.write()
402 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
413 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
403 base = None
414 base = None
404 if repo[rev].rev() != repo[min(state)].rev():
415 if repo[rev].rev() != repo[min(state)].rev():
405 base = repo[rev].p1().node()
416 base = repo[rev].p1().node()
406 # When collapsing in-place, the parent is the common ancestor, we
417 # When collapsing in-place, the parent is the common ancestor, we
407 # have to allow merging with it.
418 # have to allow merging with it.
408 return merge.update(repo, rev, True, True, False, base, collapse)
419 return merge.update(repo, rev, True, True, False, base, collapse)
409
420
410 def nearestrebased(repo, rev, state):
421 def nearestrebased(repo, rev, state):
411 """return the nearest ancestors of rev in the rebase result"""
422 """return the nearest ancestors of rev in the rebase result"""
412 rebased = [r for r in state if state[r] > nullmerge]
423 rebased = [r for r in state if state[r] > nullmerge]
413 candidates = repo.revs('max(%ld and (::%d))', rebased, rev)
424 candidates = repo.revs('max(%ld and (::%d))', rebased, rev)
414 if candidates:
425 if candidates:
415 return state[candidates[0]]
426 return state[candidates[0]]
416 else:
427 else:
417 return None
428 return None
418
429
419 def defineparents(repo, rev, target, state, targetancestors):
430 def defineparents(repo, rev, target, state, targetancestors):
420 'Return the new parent relationship of the revision that will be rebased'
431 'Return the new parent relationship of the revision that will be rebased'
421 parents = repo[rev].parents()
432 parents = repo[rev].parents()
422 p1 = p2 = nullrev
433 p1 = p2 = nullrev
423
434
424 P1n = parents[0].rev()
435 P1n = parents[0].rev()
425 if P1n in targetancestors:
436 if P1n in targetancestors:
426 p1 = target
437 p1 = target
427 elif P1n in state:
438 elif P1n in state:
428 if state[P1n] == nullmerge:
439 if state[P1n] == nullmerge:
429 p1 = target
440 p1 = target
430 elif state[P1n] == revignored:
441 elif state[P1n] == revignored:
431 p1 = nearestrebased(repo, P1n, state)
442 p1 = nearestrebased(repo, P1n, state)
432 if p1 is None:
443 if p1 is None:
433 p1 = target
444 p1 = target
434 else:
445 else:
435 p1 = state[P1n]
446 p1 = state[P1n]
436 else: # P1n external
447 else: # P1n external
437 p1 = target
448 p1 = target
438 p2 = P1n
449 p2 = P1n
439
450
440 if len(parents) == 2 and parents[1].rev() not in targetancestors:
451 if len(parents) == 2 and parents[1].rev() not in targetancestors:
441 P2n = parents[1].rev()
452 P2n = parents[1].rev()
442 # interesting second parent
453 # interesting second parent
443 if P2n in state:
454 if P2n in state:
444 if p1 == target: # P1n in targetancestors or external
455 if p1 == target: # P1n in targetancestors or external
445 p1 = state[P2n]
456 p1 = state[P2n]
446 elif state[P2n] == revignored:
457 elif state[P2n] == revignored:
447 p2 = nearestrebased(repo, P2n, state)
458 p2 = nearestrebased(repo, P2n, state)
448 if p2 is None:
459 if p2 is None:
449 # no ancestors rebased yet, detach
460 # no ancestors rebased yet, detach
450 p2 = target
461 p2 = target
451 else:
462 else:
452 p2 = state[P2n]
463 p2 = state[P2n]
453 else: # P2n external
464 else: # P2n external
454 if p2 != nullrev: # P1n external too => rev is a merged revision
465 if p2 != nullrev: # P1n external too => rev is a merged revision
455 raise util.Abort(_('cannot use revision %d as base, result '
466 raise util.Abort(_('cannot use revision %d as base, result '
456 'would have 3 parents') % rev)
467 'would have 3 parents') % rev)
457 p2 = P2n
468 p2 = P2n
458 repo.ui.debug(" future parents are %d and %d\n" %
469 repo.ui.debug(" future parents are %d and %d\n" %
459 (repo[p1].rev(), repo[p2].rev()))
470 (repo[p1].rev(), repo[p2].rev()))
460 return p1, p2
471 return p1, p2
461
472
462 def isagitpatch(repo, patchname):
473 def isagitpatch(repo, patchname):
463 'Return true if the given patch is in git format'
474 'Return true if the given patch is in git format'
464 mqpatch = os.path.join(repo.mq.path, patchname)
475 mqpatch = os.path.join(repo.mq.path, patchname)
465 for line in patch.linereader(file(mqpatch, 'rb')):
476 for line in patch.linereader(file(mqpatch, 'rb')):
466 if line.startswith('diff --git'):
477 if line.startswith('diff --git'):
467 return True
478 return True
468 return False
479 return False
469
480
470 def updatemq(repo, state, skipped, **opts):
481 def updatemq(repo, state, skipped, **opts):
471 'Update rebased mq patches - finalize and then import them'
482 'Update rebased mq patches - finalize and then import them'
472 mqrebase = {}
483 mqrebase = {}
473 mq = repo.mq
484 mq = repo.mq
474 original_series = mq.fullseries[:]
485 original_series = mq.fullseries[:]
475 skippedpatches = set()
486 skippedpatches = set()
476
487
477 for p in mq.applied:
488 for p in mq.applied:
478 rev = repo[p.node].rev()
489 rev = repo[p.node].rev()
479 if rev in state:
490 if rev in state:
480 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
491 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
481 (rev, p.name))
492 (rev, p.name))
482 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
493 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
483 else:
494 else:
484 # Applied but not rebased, not sure this should happen
495 # Applied but not rebased, not sure this should happen
485 skippedpatches.add(p.name)
496 skippedpatches.add(p.name)
486
497
487 if mqrebase:
498 if mqrebase:
488 mq.finish(repo, mqrebase.keys())
499 mq.finish(repo, mqrebase.keys())
489
500
490 # We must start import from the newest revision
501 # We must start import from the newest revision
491 for rev in sorted(mqrebase, reverse=True):
502 for rev in sorted(mqrebase, reverse=True):
492 if rev not in skipped:
503 if rev not in skipped:
493 name, isgit = mqrebase[rev]
504 name, isgit = mqrebase[rev]
494 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
505 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
495 mq.qimport(repo, (), patchname=name, git=isgit,
506 mq.qimport(repo, (), patchname=name, git=isgit,
496 rev=[str(state[rev])])
507 rev=[str(state[rev])])
497 else:
508 else:
498 # Rebased and skipped
509 # Rebased and skipped
499 skippedpatches.add(mqrebase[rev][0])
510 skippedpatches.add(mqrebase[rev][0])
500
511
501 # Patches were either applied and rebased and imported in
512 # Patches were either applied and rebased and imported in
502 # order, applied and removed or unapplied. Discard the removed
513 # order, applied and removed or unapplied. Discard the removed
503 # ones while preserving the original series order and guards.
514 # ones while preserving the original series order and guards.
504 newseries = [s for s in original_series
515 newseries = [s for s in original_series
505 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
516 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
506 mq.fullseries[:] = newseries
517 mq.fullseries[:] = newseries
507 mq.seriesdirty = True
518 mq.seriesdirty = True
508 mq.savedirty()
519 mq.savedirty()
509
520
510 def updatebookmarks(repo, targetnode, nstate, originalbookmarks):
521 def updatebookmarks(repo, targetnode, nstate, originalbookmarks):
511 'Move bookmarks to their correct changesets, and delete divergent ones'
522 'Move bookmarks to their correct changesets, and delete divergent ones'
512 marks = repo._bookmarks
523 marks = repo._bookmarks
513 for k, v in originalbookmarks.iteritems():
524 for k, v in originalbookmarks.iteritems():
514 if v in nstate:
525 if v in nstate:
515 # update the bookmarks for revs that have moved
526 # update the bookmarks for revs that have moved
516 marks[k] = nstate[v]
527 marks[k] = nstate[v]
517 bookmarks.deletedivergent(repo, [targetnode], k)
528 bookmarks.deletedivergent(repo, [targetnode], k)
518
529
519 marks.write()
530 marks.write()
520
531
521 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
532 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
522 external, activebookmark):
533 external, activebookmark):
523 'Store the current status to allow recovery'
534 'Store the current status to allow recovery'
524 f = repo.opener("rebasestate", "w")
535 f = repo.opener("rebasestate", "w")
525 f.write(repo[originalwd].hex() + '\n')
536 f.write(repo[originalwd].hex() + '\n')
526 f.write(repo[target].hex() + '\n')
537 f.write(repo[target].hex() + '\n')
527 f.write(repo[external].hex() + '\n')
538 f.write(repo[external].hex() + '\n')
528 f.write('%d\n' % int(collapse))
539 f.write('%d\n' % int(collapse))
529 f.write('%d\n' % int(keep))
540 f.write('%d\n' % int(keep))
530 f.write('%d\n' % int(keepbranches))
541 f.write('%d\n' % int(keepbranches))
531 f.write('%s\n' % (activebookmark or ''))
542 f.write('%s\n' % (activebookmark or ''))
532 for d, v in state.iteritems():
543 for d, v in state.iteritems():
533 oldrev = repo[d].hex()
544 oldrev = repo[d].hex()
534 if v > nullmerge:
545 if v > nullmerge:
535 newrev = repo[v].hex()
546 newrev = repo[v].hex()
536 else:
547 else:
537 newrev = v
548 newrev = v
538 f.write("%s:%s\n" % (oldrev, newrev))
549 f.write("%s:%s\n" % (oldrev, newrev))
539 f.close()
550 f.close()
540 repo.ui.debug('rebase status stored\n')
551 repo.ui.debug('rebase status stored\n')
541
552
542 def clearstatus(repo):
553 def clearstatus(repo):
543 'Remove the status files'
554 'Remove the status files'
544 util.unlinkpath(repo.join("rebasestate"), ignoremissing=True)
555 util.unlinkpath(repo.join("rebasestate"), ignoremissing=True)
545
556
546 def restorestatus(repo):
557 def restorestatus(repo):
547 'Restore a previously stored status'
558 'Restore a previously stored status'
548 try:
559 try:
549 target = None
560 target = None
550 collapse = False
561 collapse = False
551 external = nullrev
562 external = nullrev
552 activebookmark = None
563 activebookmark = None
553 state = {}
564 state = {}
554 f = repo.opener("rebasestate")
565 f = repo.opener("rebasestate")
555 for i, l in enumerate(f.read().splitlines()):
566 for i, l in enumerate(f.read().splitlines()):
556 if i == 0:
567 if i == 0:
557 originalwd = repo[l].rev()
568 originalwd = repo[l].rev()
558 elif i == 1:
569 elif i == 1:
559 target = repo[l].rev()
570 target = repo[l].rev()
560 elif i == 2:
571 elif i == 2:
561 external = repo[l].rev()
572 external = repo[l].rev()
562 elif i == 3:
573 elif i == 3:
563 collapse = bool(int(l))
574 collapse = bool(int(l))
564 elif i == 4:
575 elif i == 4:
565 keep = bool(int(l))
576 keep = bool(int(l))
566 elif i == 5:
577 elif i == 5:
567 keepbranches = bool(int(l))
578 keepbranches = bool(int(l))
568 elif i == 6 and not (len(l) == 81 and ':' in l):
579 elif i == 6 and not (len(l) == 81 and ':' in l):
569 # line 6 is a recent addition, so for backwards compatibility
580 # line 6 is a recent addition, so for backwards compatibility
570 # check that the line doesn't look like the oldrev:newrev lines
581 # check that the line doesn't look like the oldrev:newrev lines
571 activebookmark = l
582 activebookmark = l
572 else:
583 else:
573 oldrev, newrev = l.split(':')
584 oldrev, newrev = l.split(':')
574 if newrev in (str(nullmerge), str(revignored)):
585 if newrev in (str(nullmerge), str(revignored)):
575 state[repo[oldrev].rev()] = int(newrev)
586 state[repo[oldrev].rev()] = int(newrev)
576 else:
587 else:
577 state[repo[oldrev].rev()] = repo[newrev].rev()
588 state[repo[oldrev].rev()] = repo[newrev].rev()
578 skipped = set()
589 skipped = set()
579 # recompute the set of skipped revs
590 # recompute the set of skipped revs
580 if not collapse:
591 if not collapse:
581 seen = set([target])
592 seen = set([target])
582 for old, new in sorted(state.items()):
593 for old, new in sorted(state.items()):
583 if new != nullrev and new in seen:
594 if new != nullrev and new in seen:
584 skipped.add(old)
595 skipped.add(old)
585 seen.add(new)
596 seen.add(new)
586 repo.ui.debug('computed skipped revs: %s\n' % skipped)
597 repo.ui.debug('computed skipped revs: %s\n' % skipped)
587 repo.ui.debug('rebase status resumed\n')
598 repo.ui.debug('rebase status resumed\n')
588 return (originalwd, target, state, skipped,
599 return (originalwd, target, state, skipped,
589 collapse, keep, keepbranches, external, activebookmark)
600 collapse, keep, keepbranches, external, activebookmark)
590 except IOError, err:
601 except IOError, err:
591 if err.errno != errno.ENOENT:
602 if err.errno != errno.ENOENT:
592 raise
603 raise
593 raise util.Abort(_('no rebase in progress'))
604 raise util.Abort(_('no rebase in progress'))
594
605
595 def inrebase(repo, originalwd, state):
606 def inrebase(repo, originalwd, state):
596 '''check whether the workdir is in an interrupted rebase'''
607 '''check whether the workdir is in an interrupted rebase'''
597 parents = [p.rev() for p in repo.parents()]
608 parents = [p.rev() for p in repo.parents()]
598 if originalwd in parents:
609 if originalwd in parents:
599 return True
610 return True
600
611
601 for newrev in state.itervalues():
612 for newrev in state.itervalues():
602 if newrev in parents:
613 if newrev in parents:
603 return True
614 return True
604
615
605 return False
616 return False
606
617
607 def abort(repo, originalwd, target, state):
618 def abort(repo, originalwd, target, state):
608 'Restore the repository to its original state'
619 'Restore the repository to its original state'
609 dstates = [s for s in state.values() if s != nullrev]
620 dstates = [s for s in state.values() if s != nullrev]
610 immutable = [d for d in dstates if not repo[d].mutable()]
621 immutable = [d for d in dstates if not repo[d].mutable()]
611 cleanup = True
622 cleanup = True
612 if immutable:
623 if immutable:
613 repo.ui.warn(_("warning: can't clean up immutable changesets %s\n")
624 repo.ui.warn(_("warning: can't clean up immutable changesets %s\n")
614 % ', '.join(str(repo[r]) for r in immutable),
625 % ', '.join(str(repo[r]) for r in immutable),
615 hint=_('see hg help phases for details'))
626 hint=_('see hg help phases for details'))
616 cleanup = False
627 cleanup = False
617
628
618 descendants = set()
629 descendants = set()
619 if dstates:
630 if dstates:
620 descendants = set(repo.changelog.descendants(dstates))
631 descendants = set(repo.changelog.descendants(dstates))
621 if descendants - set(dstates):
632 if descendants - set(dstates):
622 repo.ui.warn(_("warning: new changesets detected on target branch, "
633 repo.ui.warn(_("warning: new changesets detected on target branch, "
623 "can't strip\n"))
634 "can't strip\n"))
624 cleanup = False
635 cleanup = False
625
636
626 if cleanup:
637 if cleanup:
627 # Update away from the rebase if necessary
638 # Update away from the rebase if necessary
628 if inrebase(repo, originalwd, state):
639 if inrebase(repo, originalwd, state):
629 merge.update(repo, repo[originalwd].rev(), False, True, False)
640 merge.update(repo, repo[originalwd].rev(), False, True, False)
630
641
631 # Strip from the first rebased revision
642 # Strip from the first rebased revision
632 rebased = filter(lambda x: x > -1 and x != target, state.values())
643 rebased = filter(lambda x: x > -1 and x != target, state.values())
633 if rebased:
644 if rebased:
634 strippoints = [c.node() for c in repo.set('roots(%ld)', rebased)]
645 strippoints = [c.node() for c in repo.set('roots(%ld)', rebased)]
635 # no backup of rebased cset versions needed
646 # no backup of rebased cset versions needed
636 repair.strip(repo.ui, repo, strippoints)
647 repair.strip(repo.ui, repo, strippoints)
637
648
638 clearstatus(repo)
649 clearstatus(repo)
639 repo.ui.warn(_('rebase aborted\n'))
650 repo.ui.warn(_('rebase aborted\n'))
640 return 0
651 return 0
641
652
642 def buildstate(repo, dest, rebaseset, collapse):
653 def buildstate(repo, dest, rebaseset, collapse):
643 '''Define which revisions are going to be rebased and where
654 '''Define which revisions are going to be rebased and where
644
655
645 repo: repo
656 repo: repo
646 dest: context
657 dest: context
647 rebaseset: set of rev
658 rebaseset: set of rev
648 '''
659 '''
649
660
650 # This check isn't strictly necessary, since mq detects commits over an
661 # This check isn't strictly necessary, since mq detects commits over an
651 # applied patch. But it prevents messing up the working directory when
662 # applied patch. But it prevents messing up the working directory when
652 # a partially completed rebase is blocked by mq.
663 # a partially completed rebase is blocked by mq.
653 if 'qtip' in repo.tags() and (dest.node() in
664 if 'qtip' in repo.tags() and (dest.node() in
654 [s.node for s in repo.mq.applied]):
665 [s.node for s in repo.mq.applied]):
655 raise util.Abort(_('cannot rebase onto an applied mq patch'))
666 raise util.Abort(_('cannot rebase onto an applied mq patch'))
656
667
657 roots = list(repo.set('roots(%ld)', rebaseset))
668 roots = list(repo.set('roots(%ld)', rebaseset))
658 if not roots:
669 if not roots:
659 raise util.Abort(_('no matching revisions'))
670 raise util.Abort(_('no matching revisions'))
660 roots.sort()
671 roots.sort()
661 state = {}
672 state = {}
662 detachset = set()
673 detachset = set()
663 for root in roots:
674 for root in roots:
664 commonbase = root.ancestor(dest)
675 commonbase = root.ancestor(dest)
665 if commonbase == root:
676 if commonbase == root:
666 raise util.Abort(_('source is ancestor of destination'))
677 raise util.Abort(_('source is ancestor of destination'))
667 if commonbase == dest:
678 if commonbase == dest:
668 samebranch = root.branch() == dest.branch()
679 samebranch = root.branch() == dest.branch()
669 if not collapse and samebranch and root in dest.children():
680 if not collapse and samebranch and root in dest.children():
670 repo.ui.debug('source is a child of destination\n')
681 repo.ui.debug('source is a child of destination\n')
671 return None
682 return None
672
683
673 repo.ui.debug('rebase onto %d starting from %s\n' % (dest, roots))
684 repo.ui.debug('rebase onto %d starting from %s\n' % (dest, roots))
674 state.update(dict.fromkeys(rebaseset, nullrev))
685 state.update(dict.fromkeys(rebaseset, nullrev))
675 # Rebase tries to turn <dest> into a parent of <root> while
686 # Rebase tries to turn <dest> into a parent of <root> while
676 # preserving the number of parents of rebased changesets:
687 # preserving the number of parents of rebased changesets:
677 #
688 #
678 # - A changeset with a single parent will always be rebased as a
689 # - A changeset with a single parent will always be rebased as a
679 # changeset with a single parent.
690 # changeset with a single parent.
680 #
691 #
681 # - A merge will be rebased as merge unless its parents are both
692 # - A merge will be rebased as merge unless its parents are both
682 # ancestors of <dest> or are themselves in the rebased set and
693 # ancestors of <dest> or are themselves in the rebased set and
683 # pruned while rebased.
694 # pruned while rebased.
684 #
695 #
685 # If one parent of <root> is an ancestor of <dest>, the rebased
696 # If one parent of <root> is an ancestor of <dest>, the rebased
686 # version of this parent will be <dest>. This is always true with
697 # version of this parent will be <dest>. This is always true with
687 # --base option.
698 # --base option.
688 #
699 #
689 # Otherwise, we need to *replace* the original parents with
700 # Otherwise, we need to *replace* the original parents with
690 # <dest>. This "detaches" the rebased set from its former location
701 # <dest>. This "detaches" the rebased set from its former location
691 # and rebases it onto <dest>. Changes introduced by ancestors of
702 # and rebases it onto <dest>. Changes introduced by ancestors of
692 # <root> not common with <dest> (the detachset, marked as
703 # <root> not common with <dest> (the detachset, marked as
693 # nullmerge) are "removed" from the rebased changesets.
704 # nullmerge) are "removed" from the rebased changesets.
694 #
705 #
695 # - If <root> has a single parent, set it to <dest>.
706 # - If <root> has a single parent, set it to <dest>.
696 #
707 #
697 # - If <root> is a merge, we cannot decide which parent to
708 # - If <root> is a merge, we cannot decide which parent to
698 # replace, the rebase operation is not clearly defined.
709 # replace, the rebase operation is not clearly defined.
699 #
710 #
700 # The table below sums up this behavior:
711 # The table below sums up this behavior:
701 #
712 #
702 # +------------------+----------------------+-------------------------+
713 # +------------------+----------------------+-------------------------+
703 # | | one parent | merge |
714 # | | one parent | merge |
704 # +------------------+----------------------+-------------------------+
715 # +------------------+----------------------+-------------------------+
705 # | parent in | new parent is <dest> | parents in ::<dest> are |
716 # | parent in | new parent is <dest> | parents in ::<dest> are |
706 # | ::<dest> | | remapped to <dest> |
717 # | ::<dest> | | remapped to <dest> |
707 # +------------------+----------------------+-------------------------+
718 # +------------------+----------------------+-------------------------+
708 # | unrelated source | new parent is <dest> | ambiguous, abort |
719 # | unrelated source | new parent is <dest> | ambiguous, abort |
709 # +------------------+----------------------+-------------------------+
720 # +------------------+----------------------+-------------------------+
710 #
721 #
711 # The actual abort is handled by `defineparents`
722 # The actual abort is handled by `defineparents`
712 if len(root.parents()) <= 1:
723 if len(root.parents()) <= 1:
713 # ancestors of <root> not ancestors of <dest>
724 # ancestors of <root> not ancestors of <dest>
714 detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
725 detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
715 [root.rev()]))
726 [root.rev()]))
716 for r in detachset:
727 for r in detachset:
717 if r not in state:
728 if r not in state:
718 state[r] = nullmerge
729 state[r] = nullmerge
719 if len(roots) > 1:
730 if len(roots) > 1:
720 # If we have multiple roots, we may have "hole" in the rebase set.
731 # If we have multiple roots, we may have "hole" in the rebase set.
721 # Rebase roots that descend from those "hole" should not be detached as
732 # Rebase roots that descend from those "hole" should not be detached as
722 # other root are. We use the special `revignored` to inform rebase that
733 # other root are. We use the special `revignored` to inform rebase that
723 # the revision should be ignored but that `defineparents` should search
734 # the revision should be ignored but that `defineparents` should search
724 # a rebase destination that make sense regarding rebased topology.
735 # a rebase destination that make sense regarding rebased topology.
725 rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
736 rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
726 for ignored in set(rebasedomain) - set(rebaseset):
737 for ignored in set(rebasedomain) - set(rebaseset):
727 state[ignored] = revignored
738 state[ignored] = revignored
728 return repo['.'].rev(), dest.rev(), state
739 return repo['.'].rev(), dest.rev(), state
729
740
730 def clearrebased(ui, repo, state, skipped, collapsedas=None):
741 def clearrebased(ui, repo, state, skipped, collapsedas=None):
731 """dispose of rebased revision at the end of the rebase
742 """dispose of rebased revision at the end of the rebase
732
743
733 If `collapsedas` is not None, the rebase was a collapse whose result if the
744 If `collapsedas` is not None, the rebase was a collapse whose result if the
734 `collapsedas` node."""
745 `collapsedas` node."""
735 if obsolete._enabled:
746 if obsolete._enabled:
736 markers = []
747 markers = []
737 for rev, newrev in sorted(state.items()):
748 for rev, newrev in sorted(state.items()):
738 if newrev >= 0:
749 if newrev >= 0:
739 if rev in skipped:
750 if rev in skipped:
740 succs = ()
751 succs = ()
741 elif collapsedas is not None:
752 elif collapsedas is not None:
742 succs = (repo[collapsedas],)
753 succs = (repo[collapsedas],)
743 else:
754 else:
744 succs = (repo[newrev],)
755 succs = (repo[newrev],)
745 markers.append((repo[rev], succs))
756 markers.append((repo[rev], succs))
746 if markers:
757 if markers:
747 obsolete.createmarkers(repo, markers)
758 obsolete.createmarkers(repo, markers)
748 else:
759 else:
749 rebased = [rev for rev in state if state[rev] > nullmerge]
760 rebased = [rev for rev in state if state[rev] > nullmerge]
750 if rebased:
761 if rebased:
751 stripped = []
762 stripped = []
752 for root in repo.set('roots(%ld)', rebased):
763 for root in repo.set('roots(%ld)', rebased):
753 if set(repo.changelog.descendants([root.rev()])) - set(state):
764 if set(repo.changelog.descendants([root.rev()])) - set(state):
754 ui.warn(_("warning: new changesets detected "
765 ui.warn(_("warning: new changesets detected "
755 "on source branch, not stripping\n"))
766 "on source branch, not stripping\n"))
756 else:
767 else:
757 stripped.append(root.node())
768 stripped.append(root.node())
758 if stripped:
769 if stripped:
759 # backup the old csets by default
770 # backup the old csets by default
760 repair.strip(ui, repo, stripped, "all")
771 repair.strip(ui, repo, stripped, "all")
761
772
762
773
763 def pullrebase(orig, ui, repo, *args, **opts):
774 def pullrebase(orig, ui, repo, *args, **opts):
764 'Call rebase after pull if the latter has been invoked with --rebase'
775 'Call rebase after pull if the latter has been invoked with --rebase'
765 if opts.get('rebase'):
776 if opts.get('rebase'):
766 if opts.get('update'):
777 if opts.get('update'):
767 del opts['update']
778 del opts['update']
768 ui.debug('--update and --rebase are not compatible, ignoring '
779 ui.debug('--update and --rebase are not compatible, ignoring '
769 'the update flag\n')
780 'the update flag\n')
770
781
771 movemarkfrom = repo['.'].node()
782 movemarkfrom = repo['.'].node()
772 revsprepull = len(repo)
783 revsprepull = len(repo)
773 origpostincoming = commands.postincoming
784 origpostincoming = commands.postincoming
774 def _dummy(*args, **kwargs):
785 def _dummy(*args, **kwargs):
775 pass
786 pass
776 commands.postincoming = _dummy
787 commands.postincoming = _dummy
777 try:
788 try:
778 orig(ui, repo, *args, **opts)
789 orig(ui, repo, *args, **opts)
779 finally:
790 finally:
780 commands.postincoming = origpostincoming
791 commands.postincoming = origpostincoming
781 revspostpull = len(repo)
792 revspostpull = len(repo)
782 if revspostpull > revsprepull:
793 if revspostpull > revsprepull:
783 # --rev option from pull conflict with rebase own --rev
794 # --rev option from pull conflict with rebase own --rev
784 # dropping it
795 # dropping it
785 if 'rev' in opts:
796 if 'rev' in opts:
786 del opts['rev']
797 del opts['rev']
787 rebase(ui, repo, **opts)
798 rebase(ui, repo, **opts)
788 branch = repo[None].branch()
799 branch = repo[None].branch()
789 dest = repo[branch].rev()
800 dest = repo[branch].rev()
790 if dest != repo['.'].rev():
801 if dest != repo['.'].rev():
791 # there was nothing to rebase we force an update
802 # there was nothing to rebase we force an update
792 hg.update(repo, dest)
803 hg.update(repo, dest)
793 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
804 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
794 ui.status(_("updating bookmark %s\n")
805 ui.status(_("updating bookmark %s\n")
795 % repo._bookmarkcurrent)
806 % repo._bookmarkcurrent)
796 else:
807 else:
797 if opts.get('tool'):
808 if opts.get('tool'):
798 raise util.Abort(_('--tool can only be used with --rebase'))
809 raise util.Abort(_('--tool can only be used with --rebase'))
799 orig(ui, repo, *args, **opts)
810 orig(ui, repo, *args, **opts)
800
811
801 def summaryhook(ui, repo):
812 def summaryhook(ui, repo):
802 if not os.path.exists(repo.join('rebasestate')):
813 if not os.path.exists(repo.join('rebasestate')):
803 return
814 return
804 state = restorestatus(repo)[2]
815 try:
816 state = restorestatus(repo)[2]
817 except error.RepoLookupError:
818 # i18n: column positioning for "hg summary"
819 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
820 ui.write(msg)
821 return
805 numrebased = len([i for i in state.itervalues() if i != -1])
822 numrebased = len([i for i in state.itervalues() if i != -1])
806 # i18n: column positioning for "hg summary"
823 # i18n: column positioning for "hg summary"
807 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
824 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
808 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
825 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
809 ui.label(_('%d remaining'), 'rebase.remaining') %
826 ui.label(_('%d remaining'), 'rebase.remaining') %
810 (len(state) - numrebased)))
827 (len(state) - numrebased)))
811
828
812 def uisetup(ui):
829 def uisetup(ui):
813 'Replace pull with a decorator to provide --rebase option'
830 'Replace pull with a decorator to provide --rebase option'
814 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
831 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
815 entry[1].append(('', 'rebase', None,
832 entry[1].append(('', 'rebase', None,
816 _("rebase working directory to branch head")))
833 _("rebase working directory to branch head")))
817 entry[1].append(('t', 'tool', '',
834 entry[1].append(('t', 'tool', '',
818 _("specify merge tool for rebase")))
835 _("specify merge tool for rebase")))
819 cmdutil.summaryhooks.add('rebase', summaryhook)
836 cmdutil.summaryhooks.add('rebase', summaryhook)
820 cmdutil.unfinishedstates.append(
837 cmdutil.unfinishedstates.append(
821 ['rebasestate', False, False, _('rebase in progress'),
838 ['rebasestate', False, False, _('rebase in progress'),
822 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
839 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
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