##// END OF EJS Templates
formatting: fix some recent formatting regressions...
Martin von Zweigbergk -
r44307:612951e0 default
parent child Browse files
Show More
@@ -1,363 +1,363 b''
1 from __future__ import absolute_import, print_function
1 from __future__ import absolute_import, print_function
2
2
3 import argparse
3 import argparse
4 import struct
4 import struct
5 import zipfile
5 import zipfile
6
6
7 from mercurial import (
7 from mercurial import (
8 hg,
8 hg,
9 ui as uimod,
9 ui as uimod,
10 )
10 )
11
11
12 ap = argparse.ArgumentParser()
12 ap = argparse.ArgumentParser()
13 ap.add_argument("out", metavar="some.zip", type=str, nargs=1)
13 ap.add_argument("out", metavar="some.zip", type=str, nargs=1)
14 args = ap.parse_args()
14 args = ap.parse_args()
15
15
16
16
17 class deltafrag(object):
17 class deltafrag(object):
18 def __init__(self, start, end, data):
18 def __init__(self, start, end, data):
19 self.start = start
19 self.start = start
20 self.end = end
20 self.end = end
21 self.data = data
21 self.data = data
22
22
23 def __repr__(self):
23 def __repr__(self):
24 # py2 calls __repr__ when you do `bytes(foo)`
24 # py2 calls __repr__ when you do `bytes(foo)`
25 return self.__bytes__()
25 return self.__bytes__()
26
26
27 def __bytes__(self):
27 def __bytes__(self):
28 return (
28 return (
29 struct.pack(">lll", self.start, self.end, len(self.data))
29 struct.pack(">lll", self.start, self.end, len(self.data))
30 + self.data
30 + self.data
31 )
31 )
32
32
33
33
34 class delta(object):
34 class delta(object):
35 def __init__(self, frags):
35 def __init__(self, frags):
36 self.frags = frags
36 self.frags = frags
37
37
38 def __repr__(self):
38 def __repr__(self):
39 # py2 calls __repr__ when you do `bytes(foo)`
39 # py2 calls __repr__ when you do `bytes(foo)`
40 return self.__bytes__()
40 return self.__bytes__()
41
41
42 def __bytes__(self):
42 def __bytes__(self):
43 return b''.join(bytes(f) for f in self.frags)
43 return b''.join(bytes(f) for f in self.frags)
44
44
45
45
46 class corpus(object):
46 class corpus(object):
47 def __init__(self, base, deltas):
47 def __init__(self, base, deltas):
48 self.base = base
48 self.base = base
49 self.deltas = deltas
49 self.deltas = deltas
50
50
51 def __repr__(self):
51 def __repr__(self):
52 # py2 calls __repr__ when you do `bytes(foo)`
52 # py2 calls __repr__ when you do `bytes(foo)`
53 return self.__bytes__()
53 return self.__bytes__()
54
54
55 def __bytes__(self):
55 def __bytes__(self):
56 deltas = [bytes(d) for d in self.deltas]
56 deltas = [bytes(d) for d in self.deltas]
57 parts = (
57 parts = (
58 [
58 [
59 struct.pack(">B", len(deltas) + 1),
59 struct.pack(">B", len(deltas) + 1),
60 struct.pack(">H", len(self.base)),
60 struct.pack(">H", len(self.base)),
61 ]
61 ]
62 + [struct.pack(">H", len(d)) for d in deltas]
62 + [struct.pack(">H", len(d)) for d in deltas]
63 + [self.base]
63 + [self.base]
64 + deltas
64 + deltas
65 )
65 )
66 return b''.join(parts)
66 return b''.join(parts)
67
67
68
68
69 with zipfile.ZipFile(args.out[0], "w", zipfile.ZIP_STORED) as zf:
69 with zipfile.ZipFile(args.out[0], "w", zipfile.ZIP_STORED) as zf:
70 # Manually constructed entries
70 # Manually constructed entries
71 zf.writestr(
71 zf.writestr(
72 "one_delta_applies",
72 "one_delta_applies",
73 bytes(corpus(b'a', [delta([deltafrag(0, 1, b'b')])]))
73 bytes(corpus(b'a', [delta([deltafrag(0, 1, b'b')])])),
74 )
74 )
75 zf.writestr(
75 zf.writestr(
76 "one_delta_starts_late",
76 "one_delta_starts_late",
77 bytes(corpus(b'a', [delta([deltafrag(3, 1, b'b')])]))
77 bytes(corpus(b'a', [delta([deltafrag(3, 1, b'b')])])),
78 )
78 )
79 zf.writestr(
79 zf.writestr(
80 "one_delta_ends_late",
80 "one_delta_ends_late",
81 bytes(corpus(b'a', [delta([deltafrag(0, 20, b'b')])]))
81 bytes(corpus(b'a', [delta([deltafrag(0, 20, b'b')])])),
82 )
82 )
83
83
84 try:
84 try:
85 # Generated from repo data
85 # Generated from repo data
86 r = hg.repository(uimod.ui(), b'../..')
86 r = hg.repository(uimod.ui(), b'../..')
87 fl = r.file(b'mercurial/manifest.py')
87 fl = r.file(b'mercurial/manifest.py')
88 rl = getattr(fl, '_revlog', fl)
88 rl = getattr(fl, '_revlog', fl)
89 bins = rl._chunks(rl._deltachain(10)[0])
89 bins = rl._chunks(rl._deltachain(10)[0])
90 zf.writestr('manifest_py_rev_10', bytes(corpus(bins[0], bins[1:])))
90 zf.writestr('manifest_py_rev_10', bytes(corpus(bins[0], bins[1:])))
91 except: # skip this, so no re-raises
91 except: # skip this, so no re-raises
92 print('skipping seed file from repo data')
92 print('skipping seed file from repo data')
93 # Automatically discovered by running the fuzzer
93 # Automatically discovered by running the fuzzer
94 zf.writestr(
94 zf.writestr(
95 "mpatch_decode_old_overread", b"\x02\x00\x00\x00\x02\x00\x00\x00"
95 "mpatch_decode_old_overread", b"\x02\x00\x00\x00\x02\x00\x00\x00"
96 )
96 )
97 # https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=8876
97 # https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=8876
98 zf.writestr(
98 zf.writestr(
99 "mpatch_ossfuzz_getbe32_ubsan",
99 "mpatch_ossfuzz_getbe32_ubsan",
100 b"\x02\x00\x00\x00\x0c \xff\xff\xff\xff ",
100 b"\x02\x00\x00\x00\x0c \xff\xff\xff\xff ",
101 )
101 )
102 zf.writestr(
102 zf.writestr(
103 "mpatch_apply_over_memcpy",
103 "mpatch_apply_over_memcpy",
104 b'\x13\x01\x00\x05\xd0\x00\x00\x00\x00\x00\x00\x00\x00\n \x00\x00\x00'
104 b'\x13\x01\x00\x05\xd0\x00\x00\x00\x00\x00\x00\x00\x00\n \x00\x00\x00'
105 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
105 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
106 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00'
106 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00'
107 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
107 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
108 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
108 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
109 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
109 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
110 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
110 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
111 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
111 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
112 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
112 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
113 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
113 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
114 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
114 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
115 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
115 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
116 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
116 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
117 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
117 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
118 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
118 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
119 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
119 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
120 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
120 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
121 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
121 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
122 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
122 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
123 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
123 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
124 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
124 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
125 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
125 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
126 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
126 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
127 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
127 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
128 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
128 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
129 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
129 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
130 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
130 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
131 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
131 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
132 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
132 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
133 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
133 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
134 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8c\x00\x00\x00\x00'
134 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8c\x00\x00\x00\x00'
135 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
135 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
136 b'\x00\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00\x00\x00\x00\x00\x00'
136 b'\x00\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00\x00\x00\x00\x00\x00'
137 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
137 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
138 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
138 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
139 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
139 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
140 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
140 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
141 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
141 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
142 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
142 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
143 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
143 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
144 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
144 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
145 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
145 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
146 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
146 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
147 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
147 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
148 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
148 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
149 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
149 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
150 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
150 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
151 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
151 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
152 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
152 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
153 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
153 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
154 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
154 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
155 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
155 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
156 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
156 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
157 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
157 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
158 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
158 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
159 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
159 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
160 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
160 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
161 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
161 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
162 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
162 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
163 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
163 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
164 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
164 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
165 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
165 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
166 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
166 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
167 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
167 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
168 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
168 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
169 b'\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00A\x00\x00\x00\x00'
169 b'\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00A\x00\x00\x00\x00'
170 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
170 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
171 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
171 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
172 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
172 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
173 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
173 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
174 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
174 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
175 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
175 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
176 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
176 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
177 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
177 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
178 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
178 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
179 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
179 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
180 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
180 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
181 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
181 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
182 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
182 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
183 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
183 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
184 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
184 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
185 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
185 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
186 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
186 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
187 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
187 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
188 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
188 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
189 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
189 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
190 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
190 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
191 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
191 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
192 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
192 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
193 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
193 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
194 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x94\x18'
194 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x94\x18'
195 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
195 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
196 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
196 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
197 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
197 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
198 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
198 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
199 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
199 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
200 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
200 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
201 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
201 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
202 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
202 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
203 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
203 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
204 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
204 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
205 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
205 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
206 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
206 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
207 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
207 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
208 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
208 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
209 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
209 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
210 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
210 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
211 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
211 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
212 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
212 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
213 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
213 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
214 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
214 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
215 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
215 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
216 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
216 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
217 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
217 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
218 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
218 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
219 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
219 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
220 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
220 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
221 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
221 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
222 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
222 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
223 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
223 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
224 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
224 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
225 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
225 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
226 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
226 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
227 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
227 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
228 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
228 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
229 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
229 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
230 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
230 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
231 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
231 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
232 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
232 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
233 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
233 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
234 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
234 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
235 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
235 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
236 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
236 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
237 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
237 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
238 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
238 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
239 b'\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
239 b'\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
240 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
240 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
241 b'\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfa\x00\x00\x00\x00\x00\x00\x00'
241 b'\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfa\x00\x00\x00\x00\x00\x00\x00'
242 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
242 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
243 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
243 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
244 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
244 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
245 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
245 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
246 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
246 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
247 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
247 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
248 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
248 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
249 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
249 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
250 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
250 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
251 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
251 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
252 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
252 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
253 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
253 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
254 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
254 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
255 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
255 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
256 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
256 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
257 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
257 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
258 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
258 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
259 b'\x00\x00\x94\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
259 b'\x00\x00\x94\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
260 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
260 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
261 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
261 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
262 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
262 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
263 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
263 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
264 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
264 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
265 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
265 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
266 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
266 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
267 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
267 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
268 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
268 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
269 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
269 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
270 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
270 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
271 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
271 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
272 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
272 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
273 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
273 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
274 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
274 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
275 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
275 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
276 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
276 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
277 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
277 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
278 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
278 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
279 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
279 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
280 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
280 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
281 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
281 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
282 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
282 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
283 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
283 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
284 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
284 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
285 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
285 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
286 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
286 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
287 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
287 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
288 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
288 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
289 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
289 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
290 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
290 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
291 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
291 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
292 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
292 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
293 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
293 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
294 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
294 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
295 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
295 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
296 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
296 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
297 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
297 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
298 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
298 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
299 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
299 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
300 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
300 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
301 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
301 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
302 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
302 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
303 b'\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
303 b'\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
304 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
304 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
305 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfa\x00\x00\x00'
305 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfa\x00\x00\x00'
306 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
306 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
307 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
307 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
308 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
308 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
309 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
309 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
310 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
310 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
311 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
311 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
312 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
312 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
313 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
313 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
314 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
314 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
315 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
315 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
316 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
316 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
317 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
317 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
318 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
318 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
319 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
319 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
320 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
320 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
321 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
321 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
322 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
322 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
323 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
323 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
324 b'\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00'
324 b'\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00'
325 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
325 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
326 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
326 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
327 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
327 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
328 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
328 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
329 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
329 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
330 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
330 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
331 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
331 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
332 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
332 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
333 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
333 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
334 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
334 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
335 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
335 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
336 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
336 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
337 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
337 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
338 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
338 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
339 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00'
339 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00'
340 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
340 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
341 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
341 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
342 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
342 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
343 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
343 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
344 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00]\x00\x00'
344 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00]\x00\x00'
345 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
345 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
346 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
346 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
347 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
347 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
348 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
348 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
349 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
349 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
350 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
350 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
351 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
351 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
352 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
352 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
353 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
353 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
354 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
354 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
355 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
355 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
356 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
356 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
357 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
357 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
358 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
358 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
359 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
359 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
360 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00se\x00\x00'
360 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00se\x00\x00'
361 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
361 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
362 b'\x00\x00\x00\x00',
362 b'\x00\x00\x00\x00',
363 )
363 )
@@ -1,4009 +1,4017 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import copy as copymod
10 import copy as copymod
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 hex,
17 hex,
18 nullid,
18 nullid,
19 nullrev,
19 nullrev,
20 short,
20 short,
21 )
21 )
22 from .pycompat import (
22 from .pycompat import (
23 getattr,
23 getattr,
24 open,
24 open,
25 setattr,
25 setattr,
26 )
26 )
27 from .thirdparty import attr
27 from .thirdparty import attr
28
28
29 from . import (
29 from . import (
30 bookmarks,
30 bookmarks,
31 changelog,
31 changelog,
32 copies,
32 copies,
33 crecord as crecordmod,
33 crecord as crecordmod,
34 dirstateguard,
34 dirstateguard,
35 encoding,
35 encoding,
36 error,
36 error,
37 formatter,
37 formatter,
38 logcmdutil,
38 logcmdutil,
39 match as matchmod,
39 match as matchmod,
40 merge as mergemod,
40 merge as mergemod,
41 mergeutil,
41 mergeutil,
42 obsolete,
42 obsolete,
43 patch,
43 patch,
44 pathutil,
44 pathutil,
45 phases,
45 phases,
46 pycompat,
46 pycompat,
47 repair,
47 repair,
48 revlog,
48 revlog,
49 rewriteutil,
49 rewriteutil,
50 scmutil,
50 scmutil,
51 smartset,
51 smartset,
52 state as statemod,
52 state as statemod,
53 subrepoutil,
53 subrepoutil,
54 templatekw,
54 templatekw,
55 templater,
55 templater,
56 util,
56 util,
57 vfs as vfsmod,
57 vfs as vfsmod,
58 )
58 )
59
59
60 from .utils import (
60 from .utils import (
61 dateutil,
61 dateutil,
62 stringutil,
62 stringutil,
63 )
63 )
64
64
65 if pycompat.TYPE_CHECKING:
65 if pycompat.TYPE_CHECKING:
66 from typing import (
66 from typing import (
67 Any,
67 Any,
68 Dict,
68 Dict,
69 )
69 )
70
70
71 for t in (Any, Dict):
71 for t in (Any, Dict):
72 assert t
72 assert t
73
73
74 stringio = util.stringio
74 stringio = util.stringio
75
75
76 # templates of common command options
76 # templates of common command options
77
77
78 dryrunopts = [
78 dryrunopts = [
79 (b'n', b'dry-run', None, _(b'do not perform actions, just print output')),
79 (b'n', b'dry-run', None, _(b'do not perform actions, just print output')),
80 ]
80 ]
81
81
82 confirmopts = [
82 confirmopts = [
83 (b'', b'confirm', None, _(b'ask before applying actions')),
83 (b'', b'confirm', None, _(b'ask before applying actions')),
84 ]
84 ]
85
85
86 remoteopts = [
86 remoteopts = [
87 (b'e', b'ssh', b'', _(b'specify ssh command to use'), _(b'CMD')),
87 (b'e', b'ssh', b'', _(b'specify ssh command to use'), _(b'CMD')),
88 (
88 (
89 b'',
89 b'',
90 b'remotecmd',
90 b'remotecmd',
91 b'',
91 b'',
92 _(b'specify hg command to run on the remote side'),
92 _(b'specify hg command to run on the remote side'),
93 _(b'CMD'),
93 _(b'CMD'),
94 ),
94 ),
95 (
95 (
96 b'',
96 b'',
97 b'insecure',
97 b'insecure',
98 None,
98 None,
99 _(b'do not verify server certificate (ignoring web.cacerts config)'),
99 _(b'do not verify server certificate (ignoring web.cacerts config)'),
100 ),
100 ),
101 ]
101 ]
102
102
103 walkopts = [
103 walkopts = [
104 (
104 (
105 b'I',
105 b'I',
106 b'include',
106 b'include',
107 [],
107 [],
108 _(b'include names matching the given patterns'),
108 _(b'include names matching the given patterns'),
109 _(b'PATTERN'),
109 _(b'PATTERN'),
110 ),
110 ),
111 (
111 (
112 b'X',
112 b'X',
113 b'exclude',
113 b'exclude',
114 [],
114 [],
115 _(b'exclude names matching the given patterns'),
115 _(b'exclude names matching the given patterns'),
116 _(b'PATTERN'),
116 _(b'PATTERN'),
117 ),
117 ),
118 ]
118 ]
119
119
120 commitopts = [
120 commitopts = [
121 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
121 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
122 (b'l', b'logfile', b'', _(b'read commit message from file'), _(b'FILE')),
122 (b'l', b'logfile', b'', _(b'read commit message from file'), _(b'FILE')),
123 ]
123 ]
124
124
125 commitopts2 = [
125 commitopts2 = [
126 (
126 (
127 b'd',
127 b'd',
128 b'date',
128 b'date',
129 b'',
129 b'',
130 _(b'record the specified date as commit date'),
130 _(b'record the specified date as commit date'),
131 _(b'DATE'),
131 _(b'DATE'),
132 ),
132 ),
133 (
133 (
134 b'u',
134 b'u',
135 b'user',
135 b'user',
136 b'',
136 b'',
137 _(b'record the specified user as committer'),
137 _(b'record the specified user as committer'),
138 _(b'USER'),
138 _(b'USER'),
139 ),
139 ),
140 ]
140 ]
141
141
142 commitopts3 = [
142 commitopts3 = [
143 (b'D', b'currentdate', None, _(b'record the current date as commit date')),
143 (b'D', b'currentdate', None, _(b'record the current date as commit date')),
144 (b'U', b'currentuser', None, _(b'record the current user as committer')),
144 (b'U', b'currentuser', None, _(b'record the current user as committer')),
145 ]
145 ]
146
146
147 formatteropts = [
147 formatteropts = [
148 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
148 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
149 ]
149 ]
150
150
151 templateopts = [
151 templateopts = [
152 (
152 (
153 b'',
153 b'',
154 b'style',
154 b'style',
155 b'',
155 b'',
156 _(b'display using template map file (DEPRECATED)'),
156 _(b'display using template map file (DEPRECATED)'),
157 _(b'STYLE'),
157 _(b'STYLE'),
158 ),
158 ),
159 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
159 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
160 ]
160 ]
161
161
162 logopts = [
162 logopts = [
163 (b'p', b'patch', None, _(b'show patch')),
163 (b'p', b'patch', None, _(b'show patch')),
164 (b'g', b'git', None, _(b'use git extended diff format')),
164 (b'g', b'git', None, _(b'use git extended diff format')),
165 (b'l', b'limit', b'', _(b'limit number of changes displayed'), _(b'NUM')),
165 (b'l', b'limit', b'', _(b'limit number of changes displayed'), _(b'NUM')),
166 (b'M', b'no-merges', None, _(b'do not show merges')),
166 (b'M', b'no-merges', None, _(b'do not show merges')),
167 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
167 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
168 (b'G', b'graph', None, _(b"show the revision DAG")),
168 (b'G', b'graph', None, _(b"show the revision DAG")),
169 ] + templateopts
169 ] + templateopts
170
170
171 diffopts = [
171 diffopts = [
172 (b'a', b'text', None, _(b'treat all files as text')),
172 (b'a', b'text', None, _(b'treat all files as text')),
173 (b'g', b'git', None, _(b'use git extended diff format')),
173 (b'g', b'git', None, _(b'use git extended diff format')),
174 (b'', b'binary', None, _(b'generate binary diffs in git mode (default)')),
174 (b'', b'binary', None, _(b'generate binary diffs in git mode (default)')),
175 (b'', b'nodates', None, _(b'omit dates from diff headers')),
175 (b'', b'nodates', None, _(b'omit dates from diff headers')),
176 ]
176 ]
177
177
178 diffwsopts = [
178 diffwsopts = [
179 (
179 (
180 b'w',
180 b'w',
181 b'ignore-all-space',
181 b'ignore-all-space',
182 None,
182 None,
183 _(b'ignore white space when comparing lines'),
183 _(b'ignore white space when comparing lines'),
184 ),
184 ),
185 (
185 (
186 b'b',
186 b'b',
187 b'ignore-space-change',
187 b'ignore-space-change',
188 None,
188 None,
189 _(b'ignore changes in the amount of white space'),
189 _(b'ignore changes in the amount of white space'),
190 ),
190 ),
191 (
191 (
192 b'B',
192 b'B',
193 b'ignore-blank-lines',
193 b'ignore-blank-lines',
194 None,
194 None,
195 _(b'ignore changes whose lines are all blank'),
195 _(b'ignore changes whose lines are all blank'),
196 ),
196 ),
197 (
197 (
198 b'Z',
198 b'Z',
199 b'ignore-space-at-eol',
199 b'ignore-space-at-eol',
200 None,
200 None,
201 _(b'ignore changes in whitespace at EOL'),
201 _(b'ignore changes in whitespace at EOL'),
202 ),
202 ),
203 ]
203 ]
204
204
205 diffopts2 = (
205 diffopts2 = (
206 [
206 [
207 (b'', b'noprefix', None, _(b'omit a/ and b/ prefixes from filenames')),
207 (b'', b'noprefix', None, _(b'omit a/ and b/ prefixes from filenames')),
208 (
208 (
209 b'p',
209 b'p',
210 b'show-function',
210 b'show-function',
211 None,
211 None,
212 _(b'show which function each change is in'),
212 _(b'show which function each change is in'),
213 ),
213 ),
214 (b'', b'reverse', None, _(b'produce a diff that undoes the changes')),
214 (b'', b'reverse', None, _(b'produce a diff that undoes the changes')),
215 ]
215 ]
216 + diffwsopts
216 + diffwsopts
217 + [
217 + [
218 (
218 (
219 b'U',
219 b'U',
220 b'unified',
220 b'unified',
221 b'',
221 b'',
222 _(b'number of lines of context to show'),
222 _(b'number of lines of context to show'),
223 _(b'NUM'),
223 _(b'NUM'),
224 ),
224 ),
225 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
225 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
226 (
226 (
227 b'',
227 b'',
228 b'root',
228 b'root',
229 b'',
229 b'',
230 _(b'produce diffs relative to subdirectory'),
230 _(b'produce diffs relative to subdirectory'),
231 _(b'DIR'),
231 _(b'DIR'),
232 ),
232 ),
233 ]
233 ]
234 )
234 )
235
235
236 mergetoolopts = [
236 mergetoolopts = [
237 (b't', b'tool', b'', _(b'specify merge tool'), _(b'TOOL')),
237 (b't', b'tool', b'', _(b'specify merge tool'), _(b'TOOL')),
238 ]
238 ]
239
239
240 similarityopts = [
240 similarityopts = [
241 (
241 (
242 b's',
242 b's',
243 b'similarity',
243 b'similarity',
244 b'',
244 b'',
245 _(b'guess renamed files by similarity (0<=s<=100)'),
245 _(b'guess renamed files by similarity (0<=s<=100)'),
246 _(b'SIMILARITY'),
246 _(b'SIMILARITY'),
247 )
247 )
248 ]
248 ]
249
249
250 subrepoopts = [(b'S', b'subrepos', None, _(b'recurse into subrepositories'))]
250 subrepoopts = [(b'S', b'subrepos', None, _(b'recurse into subrepositories'))]
251
251
252 debugrevlogopts = [
252 debugrevlogopts = [
253 (b'c', b'changelog', False, _(b'open changelog')),
253 (b'c', b'changelog', False, _(b'open changelog')),
254 (b'm', b'manifest', False, _(b'open manifest')),
254 (b'm', b'manifest', False, _(b'open manifest')),
255 (b'', b'dir', b'', _(b'open directory manifest')),
255 (b'', b'dir', b'', _(b'open directory manifest')),
256 ]
256 ]
257
257
258 # special string such that everything below this line will be ingored in the
258 # special string such that everything below this line will be ingored in the
259 # editor text
259 # editor text
260 _linebelow = b"^HG: ------------------------ >8 ------------------------$"
260 _linebelow = b"^HG: ------------------------ >8 ------------------------$"
261
261
262
262
263 def resolvecommitoptions(ui, opts):
263 def resolvecommitoptions(ui, opts):
264 """modify commit options dict to handle related options
264 """modify commit options dict to handle related options
265
265
266 The return value indicates that ``rewrite.update-timestamp`` is the reason
266 The return value indicates that ``rewrite.update-timestamp`` is the reason
267 the ``date`` option is set.
267 the ``date`` option is set.
268 """
268 """
269 if opts.get(b'date') and opts.get(b'currentdate'):
269 if opts.get(b'date') and opts.get(b'currentdate'):
270 raise error.Abort(_(b'--date and --currentdate are mutually exclusive'))
270 raise error.Abort(_(b'--date and --currentdate are mutually exclusive'))
271 if opts.get(b'user') and opts.get(b'currentuser'):
271 if opts.get(b'user') and opts.get(b'currentuser'):
272 raise error.Abort(_(b'--user and --currentuser are mutually exclusive'))
272 raise error.Abort(_(b'--user and --currentuser are mutually exclusive'))
273
273
274 datemaydiffer = False # date-only change should be ignored?
274 datemaydiffer = False # date-only change should be ignored?
275
275
276 if opts.get(b'currentdate'):
276 if opts.get(b'currentdate'):
277 opts[b'date'] = b'%d %d' % dateutil.makedate()
277 opts[b'date'] = b'%d %d' % dateutil.makedate()
278 elif (
278 elif (
279 not opts.get(b'date')
279 not opts.get(b'date')
280 and ui.configbool(b'rewrite', b'update-timestamp')
280 and ui.configbool(b'rewrite', b'update-timestamp')
281 and opts.get(b'currentdate') is None
281 and opts.get(b'currentdate') is None
282 ):
282 ):
283 opts[b'date'] = b'%d %d' % dateutil.makedate()
283 opts[b'date'] = b'%d %d' % dateutil.makedate()
284 datemaydiffer = True
284 datemaydiffer = True
285
285
286 if opts.get(b'currentuser'):
286 if opts.get(b'currentuser'):
287 opts[b'user'] = ui.username()
287 opts[b'user'] = ui.username()
288
288
289 return datemaydiffer
289 return datemaydiffer
290
290
291
291
292 def checknotesize(ui, opts):
292 def checknotesize(ui, opts):
293 """ make sure note is of valid format """
293 """ make sure note is of valid format """
294
294
295 note = opts.get(b'note')
295 note = opts.get(b'note')
296 if not note:
296 if not note:
297 return
297 return
298
298
299 if len(note) > 255:
299 if len(note) > 255:
300 raise error.Abort(_(b"cannot store a note of more than 255 bytes"))
300 raise error.Abort(_(b"cannot store a note of more than 255 bytes"))
301 if b'\n' in note:
301 if b'\n' in note:
302 raise error.Abort(_(b"note cannot contain a newline"))
302 raise error.Abort(_(b"note cannot contain a newline"))
303
303
304
304
305 def ishunk(x):
305 def ishunk(x):
306 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
306 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
307 return isinstance(x, hunkclasses)
307 return isinstance(x, hunkclasses)
308
308
309
309
310 def newandmodified(chunks, originalchunks):
310 def newandmodified(chunks, originalchunks):
311 newlyaddedandmodifiedfiles = set()
311 newlyaddedandmodifiedfiles = set()
312 alsorestore = set()
312 alsorestore = set()
313 for chunk in chunks:
313 for chunk in chunks:
314 if (
314 if (
315 ishunk(chunk)
315 ishunk(chunk)
316 and chunk.header.isnewfile()
316 and chunk.header.isnewfile()
317 and chunk not in originalchunks
317 and chunk not in originalchunks
318 ):
318 ):
319 newlyaddedandmodifiedfiles.add(chunk.header.filename())
319 newlyaddedandmodifiedfiles.add(chunk.header.filename())
320 alsorestore.update(
320 alsorestore.update(
321 set(chunk.header.files()) - {chunk.header.filename()}
321 set(chunk.header.files()) - {chunk.header.filename()}
322 )
322 )
323 return newlyaddedandmodifiedfiles, alsorestore
323 return newlyaddedandmodifiedfiles, alsorestore
324
324
325
325
326 def parsealiases(cmd):
326 def parsealiases(cmd):
327 return cmd.split(b"|")
327 return cmd.split(b"|")
328
328
329
329
330 def setupwrapcolorwrite(ui):
330 def setupwrapcolorwrite(ui):
331 # wrap ui.write so diff output can be labeled/colorized
331 # wrap ui.write so diff output can be labeled/colorized
332 def wrapwrite(orig, *args, **kw):
332 def wrapwrite(orig, *args, **kw):
333 label = kw.pop('label', b'')
333 label = kw.pop('label', b'')
334 for chunk, l in patch.difflabel(lambda: args):
334 for chunk, l in patch.difflabel(lambda: args):
335 orig(chunk, label=label + l)
335 orig(chunk, label=label + l)
336
336
337 oldwrite = ui.write
337 oldwrite = ui.write
338
338
339 def wrap(*args, **kwargs):
339 def wrap(*args, **kwargs):
340 return wrapwrite(oldwrite, *args, **kwargs)
340 return wrapwrite(oldwrite, *args, **kwargs)
341
341
342 setattr(ui, 'write', wrap)
342 setattr(ui, 'write', wrap)
343 return oldwrite
343 return oldwrite
344
344
345
345
346 def filterchunks(ui, originalhunks, usecurses, testfile, match, operation=None):
346 def filterchunks(ui, originalhunks, usecurses, testfile, match, operation=None):
347 try:
347 try:
348 if usecurses:
348 if usecurses:
349 if testfile:
349 if testfile:
350 recordfn = crecordmod.testdecorator(
350 recordfn = crecordmod.testdecorator(
351 testfile, crecordmod.testchunkselector
351 testfile, crecordmod.testchunkselector
352 )
352 )
353 else:
353 else:
354 recordfn = crecordmod.chunkselector
354 recordfn = crecordmod.chunkselector
355
355
356 return crecordmod.filterpatch(
356 return crecordmod.filterpatch(
357 ui, originalhunks, recordfn, operation
357 ui, originalhunks, recordfn, operation
358 )
358 )
359 except crecordmod.fallbackerror as e:
359 except crecordmod.fallbackerror as e:
360 ui.warn(b'%s\n' % e.message) # pytype: disable=attribute-error
360 ui.warn(b'%s\n' % e.message) # pytype: disable=attribute-error
361 ui.warn(_(b'falling back to text mode\n'))
361 ui.warn(_(b'falling back to text mode\n'))
362
362
363 return patch.filterpatch(ui, originalhunks, match, operation)
363 return patch.filterpatch(ui, originalhunks, match, operation)
364
364
365
365
366 def recordfilter(ui, originalhunks, match, operation=None):
366 def recordfilter(ui, originalhunks, match, operation=None):
367 """ Prompts the user to filter the originalhunks and return a list of
367 """ Prompts the user to filter the originalhunks and return a list of
368 selected hunks.
368 selected hunks.
369 *operation* is used for to build ui messages to indicate the user what
369 *operation* is used for to build ui messages to indicate the user what
370 kind of filtering they are doing: reverting, committing, shelving, etc.
370 kind of filtering they are doing: reverting, committing, shelving, etc.
371 (see patch.filterpatch).
371 (see patch.filterpatch).
372 """
372 """
373 usecurses = crecordmod.checkcurses(ui)
373 usecurses = crecordmod.checkcurses(ui)
374 testfile = ui.config(b'experimental', b'crecordtest')
374 testfile = ui.config(b'experimental', b'crecordtest')
375 oldwrite = setupwrapcolorwrite(ui)
375 oldwrite = setupwrapcolorwrite(ui)
376 try:
376 try:
377 newchunks, newopts = filterchunks(
377 newchunks, newopts = filterchunks(
378 ui, originalhunks, usecurses, testfile, match, operation
378 ui, originalhunks, usecurses, testfile, match, operation
379 )
379 )
380 finally:
380 finally:
381 ui.write = oldwrite
381 ui.write = oldwrite
382 return newchunks, newopts
382 return newchunks, newopts
383
383
384
384
385 def dorecord(
385 def dorecord(
386 ui, repo, commitfunc, cmdsuggest, backupall, filterfn, *pats, **opts
386 ui, repo, commitfunc, cmdsuggest, backupall, filterfn, *pats, **opts
387 ):
387 ):
388 opts = pycompat.byteskwargs(opts)
388 opts = pycompat.byteskwargs(opts)
389 if not ui.interactive():
389 if not ui.interactive():
390 if cmdsuggest:
390 if cmdsuggest:
391 msg = _(b'running non-interactively, use %s instead') % cmdsuggest
391 msg = _(b'running non-interactively, use %s instead') % cmdsuggest
392 else:
392 else:
393 msg = _(b'running non-interactively')
393 msg = _(b'running non-interactively')
394 raise error.Abort(msg)
394 raise error.Abort(msg)
395
395
396 # make sure username is set before going interactive
396 # make sure username is set before going interactive
397 if not opts.get(b'user'):
397 if not opts.get(b'user'):
398 ui.username() # raise exception, username not provided
398 ui.username() # raise exception, username not provided
399
399
400 def recordfunc(ui, repo, message, match, opts):
400 def recordfunc(ui, repo, message, match, opts):
401 """This is generic record driver.
401 """This is generic record driver.
402
402
403 Its job is to interactively filter local changes, and
403 Its job is to interactively filter local changes, and
404 accordingly prepare working directory into a state in which the
404 accordingly prepare working directory into a state in which the
405 job can be delegated to a non-interactive commit command such as
405 job can be delegated to a non-interactive commit command such as
406 'commit' or 'qrefresh'.
406 'commit' or 'qrefresh'.
407
407
408 After the actual job is done by non-interactive command, the
408 After the actual job is done by non-interactive command, the
409 working directory is restored to its original state.
409 working directory is restored to its original state.
410
410
411 In the end we'll record interesting changes, and everything else
411 In the end we'll record interesting changes, and everything else
412 will be left in place, so the user can continue working.
412 will be left in place, so the user can continue working.
413 """
413 """
414 if not opts.get(b'interactive-unshelve'):
414 if not opts.get(b'interactive-unshelve'):
415 checkunfinished(repo, commit=True)
415 checkunfinished(repo, commit=True)
416 wctx = repo[None]
416 wctx = repo[None]
417 merge = len(wctx.parents()) > 1
417 merge = len(wctx.parents()) > 1
418 if merge:
418 if merge:
419 raise error.Abort(
419 raise error.Abort(
420 _(
420 _(
421 b'cannot partially commit a merge '
421 b'cannot partially commit a merge '
422 b'(use "hg commit" instead)'
422 b'(use "hg commit" instead)'
423 )
423 )
424 )
424 )
425
425
426 def fail(f, msg):
426 def fail(f, msg):
427 raise error.Abort(b'%s: %s' % (f, msg))
427 raise error.Abort(b'%s: %s' % (f, msg))
428
428
429 force = opts.get(b'force')
429 force = opts.get(b'force')
430 if not force:
430 if not force:
431 match = matchmod.badmatch(match, fail)
431 match = matchmod.badmatch(match, fail)
432
432
433 status = repo.status(match=match)
433 status = repo.status(match=match)
434
434
435 overrides = {(b'ui', b'commitsubrepos'): True}
435 overrides = {(b'ui', b'commitsubrepos'): True}
436
436
437 with repo.ui.configoverride(overrides, b'record'):
437 with repo.ui.configoverride(overrides, b'record'):
438 # subrepoutil.precommit() modifies the status
438 # subrepoutil.precommit() modifies the status
439 tmpstatus = scmutil.status(
439 tmpstatus = scmutil.status(
440 copymod.copy(status.modified),
440 copymod.copy(status.modified),
441 copymod.copy(status.added),
441 copymod.copy(status.added),
442 copymod.copy(status.removed),
442 copymod.copy(status.removed),
443 copymod.copy(status.deleted),
443 copymod.copy(status.deleted),
444 copymod.copy(status.unknown),
444 copymod.copy(status.unknown),
445 copymod.copy(status.ignored),
445 copymod.copy(status.ignored),
446 copymod.copy(status.clean), # pytype: disable=wrong-arg-count
446 copymod.copy(status.clean), # pytype: disable=wrong-arg-count
447 )
447 )
448
448
449 # Force allows -X subrepo to skip the subrepo.
449 # Force allows -X subrepo to skip the subrepo.
450 subs, commitsubs, newstate = subrepoutil.precommit(
450 subs, commitsubs, newstate = subrepoutil.precommit(
451 repo.ui, wctx, tmpstatus, match, force=True
451 repo.ui, wctx, tmpstatus, match, force=True
452 )
452 )
453 for s in subs:
453 for s in subs:
454 if s in commitsubs:
454 if s in commitsubs:
455 dirtyreason = wctx.sub(s).dirtyreason(True)
455 dirtyreason = wctx.sub(s).dirtyreason(True)
456 raise error.Abort(dirtyreason)
456 raise error.Abort(dirtyreason)
457
457
458 if not force:
458 if not force:
459 repo.checkcommitpatterns(wctx, match, status, fail)
459 repo.checkcommitpatterns(wctx, match, status, fail)
460 diffopts = patch.difffeatureopts(
460 diffopts = patch.difffeatureopts(
461 ui,
461 ui,
462 opts=opts,
462 opts=opts,
463 whitespace=True,
463 whitespace=True,
464 section=b'commands',
464 section=b'commands',
465 configprefix=b'commit.interactive.',
465 configprefix=b'commit.interactive.',
466 )
466 )
467 diffopts.nodates = True
467 diffopts.nodates = True
468 diffopts.git = True
468 diffopts.git = True
469 diffopts.showfunc = True
469 diffopts.showfunc = True
470 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
470 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
471 originalchunks = patch.parsepatch(originaldiff)
471 originalchunks = patch.parsepatch(originaldiff)
472 match = scmutil.match(repo[None], pats)
472 match = scmutil.match(repo[None], pats)
473
473
474 # 1. filter patch, since we are intending to apply subset of it
474 # 1. filter patch, since we are intending to apply subset of it
475 try:
475 try:
476 chunks, newopts = filterfn(ui, originalchunks, match)
476 chunks, newopts = filterfn(ui, originalchunks, match)
477 except error.PatchError as err:
477 except error.PatchError as err:
478 raise error.Abort(_(b'error parsing patch: %s') % err)
478 raise error.Abort(_(b'error parsing patch: %s') % err)
479 opts.update(newopts)
479 opts.update(newopts)
480
480
481 # We need to keep a backup of files that have been newly added and
481 # We need to keep a backup of files that have been newly added and
482 # modified during the recording process because there is a previous
482 # modified during the recording process because there is a previous
483 # version without the edit in the workdir. We also will need to restore
483 # version without the edit in the workdir. We also will need to restore
484 # files that were the sources of renames so that the patch application
484 # files that were the sources of renames so that the patch application
485 # works.
485 # works.
486 newlyaddedandmodifiedfiles, alsorestore = newandmodified(
486 newlyaddedandmodifiedfiles, alsorestore = newandmodified(
487 chunks, originalchunks
487 chunks, originalchunks
488 )
488 )
489 contenders = set()
489 contenders = set()
490 for h in chunks:
490 for h in chunks:
491 try:
491 try:
492 contenders.update(set(h.files()))
492 contenders.update(set(h.files()))
493 except AttributeError:
493 except AttributeError:
494 pass
494 pass
495
495
496 changed = status.modified + status.added + status.removed
496 changed = status.modified + status.added + status.removed
497 newfiles = [f for f in changed if f in contenders]
497 newfiles = [f for f in changed if f in contenders]
498 if not newfiles:
498 if not newfiles:
499 ui.status(_(b'no changes to record\n'))
499 ui.status(_(b'no changes to record\n'))
500 return 0
500 return 0
501
501
502 modified = set(status.modified)
502 modified = set(status.modified)
503
503
504 # 2. backup changed files, so we can restore them in the end
504 # 2. backup changed files, so we can restore them in the end
505
505
506 if backupall:
506 if backupall:
507 tobackup = changed
507 tobackup = changed
508 else:
508 else:
509 tobackup = [
509 tobackup = [
510 f
510 f
511 for f in newfiles
511 for f in newfiles
512 if f in modified or f in newlyaddedandmodifiedfiles
512 if f in modified or f in newlyaddedandmodifiedfiles
513 ]
513 ]
514 backups = {}
514 backups = {}
515 if tobackup:
515 if tobackup:
516 backupdir = repo.vfs.join(b'record-backups')
516 backupdir = repo.vfs.join(b'record-backups')
517 try:
517 try:
518 os.mkdir(backupdir)
518 os.mkdir(backupdir)
519 except OSError as err:
519 except OSError as err:
520 if err.errno != errno.EEXIST:
520 if err.errno != errno.EEXIST:
521 raise
521 raise
522 try:
522 try:
523 # backup continues
523 # backup continues
524 for f in tobackup:
524 for f in tobackup:
525 fd, tmpname = pycompat.mkstemp(
525 fd, tmpname = pycompat.mkstemp(
526 prefix=f.replace(b'/', b'_') + b'.', dir=backupdir
526 prefix=f.replace(b'/', b'_') + b'.', dir=backupdir
527 )
527 )
528 os.close(fd)
528 os.close(fd)
529 ui.debug(b'backup %r as %r\n' % (f, tmpname))
529 ui.debug(b'backup %r as %r\n' % (f, tmpname))
530 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
530 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
531 backups[f] = tmpname
531 backups[f] = tmpname
532
532
533 fp = stringio()
533 fp = stringio()
534 for c in chunks:
534 for c in chunks:
535 fname = c.filename()
535 fname = c.filename()
536 if fname in backups:
536 if fname in backups:
537 c.write(fp)
537 c.write(fp)
538 dopatch = fp.tell()
538 dopatch = fp.tell()
539 fp.seek(0)
539 fp.seek(0)
540
540
541 # 2.5 optionally review / modify patch in text editor
541 # 2.5 optionally review / modify patch in text editor
542 if opts.get(b'review', False):
542 if opts.get(b'review', False):
543 patchtext = (
543 patchtext = (
544 crecordmod.diffhelptext
544 crecordmod.diffhelptext
545 + crecordmod.patchhelptext
545 + crecordmod.patchhelptext
546 + fp.read()
546 + fp.read()
547 )
547 )
548 reviewedpatch = ui.edit(
548 reviewedpatch = ui.edit(
549 patchtext, b"", action=b"diff", repopath=repo.path
549 patchtext, b"", action=b"diff", repopath=repo.path
550 )
550 )
551 fp.truncate(0)
551 fp.truncate(0)
552 fp.write(reviewedpatch)
552 fp.write(reviewedpatch)
553 fp.seek(0)
553 fp.seek(0)
554
554
555 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
555 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
556 # 3a. apply filtered patch to clean repo (clean)
556 # 3a. apply filtered patch to clean repo (clean)
557 if backups:
557 if backups:
558 # Equivalent to hg.revert
558 # Equivalent to hg.revert
559 m = scmutil.matchfiles(repo, set(backups.keys()) | alsorestore)
559 m = scmutil.matchfiles(repo, set(backups.keys()) | alsorestore)
560 mergemod.update(
560 mergemod.update(
561 repo,
561 repo,
562 repo.dirstate.p1(),
562 repo.dirstate.p1(),
563 branchmerge=False,
563 branchmerge=False,
564 force=True,
564 force=True,
565 matcher=m,
565 matcher=m,
566 )
566 )
567
567
568 # 3b. (apply)
568 # 3b. (apply)
569 if dopatch:
569 if dopatch:
570 try:
570 try:
571 ui.debug(b'applying patch\n')
571 ui.debug(b'applying patch\n')
572 ui.debug(fp.getvalue())
572 ui.debug(fp.getvalue())
573 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
573 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
574 except error.PatchError as err:
574 except error.PatchError as err:
575 raise error.Abort(pycompat.bytestr(err))
575 raise error.Abort(pycompat.bytestr(err))
576 del fp
576 del fp
577
577
578 # 4. We prepared working directory according to filtered
578 # 4. We prepared working directory according to filtered
579 # patch. Now is the time to delegate the job to
579 # patch. Now is the time to delegate the job to
580 # commit/qrefresh or the like!
580 # commit/qrefresh or the like!
581
581
582 # Make all of the pathnames absolute.
582 # Make all of the pathnames absolute.
583 newfiles = [repo.wjoin(nf) for nf in newfiles]
583 newfiles = [repo.wjoin(nf) for nf in newfiles]
584 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
584 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
585 finally:
585 finally:
586 # 5. finally restore backed-up files
586 # 5. finally restore backed-up files
587 try:
587 try:
588 dirstate = repo.dirstate
588 dirstate = repo.dirstate
589 for realname, tmpname in pycompat.iteritems(backups):
589 for realname, tmpname in pycompat.iteritems(backups):
590 ui.debug(b'restoring %r to %r\n' % (tmpname, realname))
590 ui.debug(b'restoring %r to %r\n' % (tmpname, realname))
591
591
592 if dirstate[realname] == b'n':
592 if dirstate[realname] == b'n':
593 # without normallookup, restoring timestamp
593 # without normallookup, restoring timestamp
594 # may cause partially committed files
594 # may cause partially committed files
595 # to be treated as unmodified
595 # to be treated as unmodified
596 dirstate.normallookup(realname)
596 dirstate.normallookup(realname)
597
597
598 # copystat=True here and above are a hack to trick any
598 # copystat=True here and above are a hack to trick any
599 # editors that have f open that we haven't modified them.
599 # editors that have f open that we haven't modified them.
600 #
600 #
601 # Also note that this racy as an editor could notice the
601 # Also note that this racy as an editor could notice the
602 # file's mtime before we've finished writing it.
602 # file's mtime before we've finished writing it.
603 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
603 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
604 os.unlink(tmpname)
604 os.unlink(tmpname)
605 if tobackup:
605 if tobackup:
606 os.rmdir(backupdir)
606 os.rmdir(backupdir)
607 except OSError:
607 except OSError:
608 pass
608 pass
609
609
610 def recordinwlock(ui, repo, message, match, opts):
610 def recordinwlock(ui, repo, message, match, opts):
611 with repo.wlock():
611 with repo.wlock():
612 return recordfunc(ui, repo, message, match, opts)
612 return recordfunc(ui, repo, message, match, opts)
613
613
614 return commit(ui, repo, recordinwlock, pats, opts)
614 return commit(ui, repo, recordinwlock, pats, opts)
615
615
616
616
617 class dirnode(object):
617 class dirnode(object):
618 """
618 """
619 Represent a directory in user working copy with information required for
619 Represent a directory in user working copy with information required for
620 the purpose of tersing its status.
620 the purpose of tersing its status.
621
621
622 path is the path to the directory, without a trailing '/'
622 path is the path to the directory, without a trailing '/'
623
623
624 statuses is a set of statuses of all files in this directory (this includes
624 statuses is a set of statuses of all files in this directory (this includes
625 all the files in all the subdirectories too)
625 all the files in all the subdirectories too)
626
626
627 files is a list of files which are direct child of this directory
627 files is a list of files which are direct child of this directory
628
628
629 subdirs is a dictionary of sub-directory name as the key and it's own
629 subdirs is a dictionary of sub-directory name as the key and it's own
630 dirnode object as the value
630 dirnode object as the value
631 """
631 """
632
632
633 def __init__(self, dirpath):
633 def __init__(self, dirpath):
634 self.path = dirpath
634 self.path = dirpath
635 self.statuses = set()
635 self.statuses = set()
636 self.files = []
636 self.files = []
637 self.subdirs = {}
637 self.subdirs = {}
638
638
639 def _addfileindir(self, filename, status):
639 def _addfileindir(self, filename, status):
640 """Add a file in this directory as a direct child."""
640 """Add a file in this directory as a direct child."""
641 self.files.append((filename, status))
641 self.files.append((filename, status))
642
642
643 def addfile(self, filename, status):
643 def addfile(self, filename, status):
644 """
644 """
645 Add a file to this directory or to its direct parent directory.
645 Add a file to this directory or to its direct parent directory.
646
646
647 If the file is not direct child of this directory, we traverse to the
647 If the file is not direct child of this directory, we traverse to the
648 directory of which this file is a direct child of and add the file
648 directory of which this file is a direct child of and add the file
649 there.
649 there.
650 """
650 """
651
651
652 # the filename contains a path separator, it means it's not the direct
652 # the filename contains a path separator, it means it's not the direct
653 # child of this directory
653 # child of this directory
654 if b'/' in filename:
654 if b'/' in filename:
655 subdir, filep = filename.split(b'/', 1)
655 subdir, filep = filename.split(b'/', 1)
656
656
657 # does the dirnode object for subdir exists
657 # does the dirnode object for subdir exists
658 if subdir not in self.subdirs:
658 if subdir not in self.subdirs:
659 subdirpath = pathutil.join(self.path, subdir)
659 subdirpath = pathutil.join(self.path, subdir)
660 self.subdirs[subdir] = dirnode(subdirpath)
660 self.subdirs[subdir] = dirnode(subdirpath)
661
661
662 # try adding the file in subdir
662 # try adding the file in subdir
663 self.subdirs[subdir].addfile(filep, status)
663 self.subdirs[subdir].addfile(filep, status)
664
664
665 else:
665 else:
666 self._addfileindir(filename, status)
666 self._addfileindir(filename, status)
667
667
668 if status not in self.statuses:
668 if status not in self.statuses:
669 self.statuses.add(status)
669 self.statuses.add(status)
670
670
671 def iterfilepaths(self):
671 def iterfilepaths(self):
672 """Yield (status, path) for files directly under this directory."""
672 """Yield (status, path) for files directly under this directory."""
673 for f, st in self.files:
673 for f, st in self.files:
674 yield st, pathutil.join(self.path, f)
674 yield st, pathutil.join(self.path, f)
675
675
676 def tersewalk(self, terseargs):
676 def tersewalk(self, terseargs):
677 """
677 """
678 Yield (status, path) obtained by processing the status of this
678 Yield (status, path) obtained by processing the status of this
679 dirnode.
679 dirnode.
680
680
681 terseargs is the string of arguments passed by the user with `--terse`
681 terseargs is the string of arguments passed by the user with `--terse`
682 flag.
682 flag.
683
683
684 Following are the cases which can happen:
684 Following are the cases which can happen:
685
685
686 1) All the files in the directory (including all the files in its
686 1) All the files in the directory (including all the files in its
687 subdirectories) share the same status and the user has asked us to terse
687 subdirectories) share the same status and the user has asked us to terse
688 that status. -> yield (status, dirpath). dirpath will end in '/'.
688 that status. -> yield (status, dirpath). dirpath will end in '/'.
689
689
690 2) Otherwise, we do following:
690 2) Otherwise, we do following:
691
691
692 a) Yield (status, filepath) for all the files which are in this
692 a) Yield (status, filepath) for all the files which are in this
693 directory (only the ones in this directory, not the subdirs)
693 directory (only the ones in this directory, not the subdirs)
694
694
695 b) Recurse the function on all the subdirectories of this
695 b) Recurse the function on all the subdirectories of this
696 directory
696 directory
697 """
697 """
698
698
699 if len(self.statuses) == 1:
699 if len(self.statuses) == 1:
700 onlyst = self.statuses.pop()
700 onlyst = self.statuses.pop()
701
701
702 # Making sure we terse only when the status abbreviation is
702 # Making sure we terse only when the status abbreviation is
703 # passed as terse argument
703 # passed as terse argument
704 if onlyst in terseargs:
704 if onlyst in terseargs:
705 yield onlyst, self.path + b'/'
705 yield onlyst, self.path + b'/'
706 return
706 return
707
707
708 # add the files to status list
708 # add the files to status list
709 for st, fpath in self.iterfilepaths():
709 for st, fpath in self.iterfilepaths():
710 yield st, fpath
710 yield st, fpath
711
711
712 # recurse on the subdirs
712 # recurse on the subdirs
713 for dirobj in self.subdirs.values():
713 for dirobj in self.subdirs.values():
714 for st, fpath in dirobj.tersewalk(terseargs):
714 for st, fpath in dirobj.tersewalk(terseargs):
715 yield st, fpath
715 yield st, fpath
716
716
717
717
718 def tersedir(statuslist, terseargs):
718 def tersedir(statuslist, terseargs):
719 """
719 """
720 Terse the status if all the files in a directory shares the same status.
720 Terse the status if all the files in a directory shares the same status.
721
721
722 statuslist is scmutil.status() object which contains a list of files for
722 statuslist is scmutil.status() object which contains a list of files for
723 each status.
723 each status.
724 terseargs is string which is passed by the user as the argument to `--terse`
724 terseargs is string which is passed by the user as the argument to `--terse`
725 flag.
725 flag.
726
726
727 The function makes a tree of objects of dirnode class, and at each node it
727 The function makes a tree of objects of dirnode class, and at each node it
728 stores the information required to know whether we can terse a certain
728 stores the information required to know whether we can terse a certain
729 directory or not.
729 directory or not.
730 """
730 """
731 # the order matters here as that is used to produce final list
731 # the order matters here as that is used to produce final list
732 allst = (b'm', b'a', b'r', b'd', b'u', b'i', b'c')
732 allst = (b'm', b'a', b'r', b'd', b'u', b'i', b'c')
733
733
734 # checking the argument validity
734 # checking the argument validity
735 for s in pycompat.bytestr(terseargs):
735 for s in pycompat.bytestr(terseargs):
736 if s not in allst:
736 if s not in allst:
737 raise error.Abort(_(b"'%s' not recognized") % s)
737 raise error.Abort(_(b"'%s' not recognized") % s)
738
738
739 # creating a dirnode object for the root of the repo
739 # creating a dirnode object for the root of the repo
740 rootobj = dirnode(b'')
740 rootobj = dirnode(b'')
741 pstatus = (
741 pstatus = (
742 b'modified',
742 b'modified',
743 b'added',
743 b'added',
744 b'deleted',
744 b'deleted',
745 b'clean',
745 b'clean',
746 b'unknown',
746 b'unknown',
747 b'ignored',
747 b'ignored',
748 b'removed',
748 b'removed',
749 )
749 )
750
750
751 tersedict = {}
751 tersedict = {}
752 for attrname in pstatus:
752 for attrname in pstatus:
753 statuschar = attrname[0:1]
753 statuschar = attrname[0:1]
754 for f in getattr(statuslist, attrname):
754 for f in getattr(statuslist, attrname):
755 rootobj.addfile(f, statuschar)
755 rootobj.addfile(f, statuschar)
756 tersedict[statuschar] = []
756 tersedict[statuschar] = []
757
757
758 # we won't be tersing the root dir, so add files in it
758 # we won't be tersing the root dir, so add files in it
759 for st, fpath in rootobj.iterfilepaths():
759 for st, fpath in rootobj.iterfilepaths():
760 tersedict[st].append(fpath)
760 tersedict[st].append(fpath)
761
761
762 # process each sub-directory and build tersedict
762 # process each sub-directory and build tersedict
763 for subdir in rootobj.subdirs.values():
763 for subdir in rootobj.subdirs.values():
764 for st, f in subdir.tersewalk(terseargs):
764 for st, f in subdir.tersewalk(terseargs):
765 tersedict[st].append(f)
765 tersedict[st].append(f)
766
766
767 tersedlist = []
767 tersedlist = []
768 for st in allst:
768 for st in allst:
769 tersedict[st].sort()
769 tersedict[st].sort()
770 tersedlist.append(tersedict[st])
770 tersedlist.append(tersedict[st])
771
771
772 return scmutil.status(*tersedlist)
772 return scmutil.status(*tersedlist)
773
773
774
774
775 def _commentlines(raw):
775 def _commentlines(raw):
776 '''Surround lineswith a comment char and a new line'''
776 '''Surround lineswith a comment char and a new line'''
777 lines = raw.splitlines()
777 lines = raw.splitlines()
778 commentedlines = [b'# %s' % line for line in lines]
778 commentedlines = [b'# %s' % line for line in lines]
779 return b'\n'.join(commentedlines) + b'\n'
779 return b'\n'.join(commentedlines) + b'\n'
780
780
781
781
782 @attr.s(frozen=True)
782 @attr.s(frozen=True)
783 class morestatus(object):
783 class morestatus(object):
784 reporoot = attr.ib()
784 reporoot = attr.ib()
785 unfinishedop = attr.ib()
785 unfinishedop = attr.ib()
786 unfinishedmsg = attr.ib()
786 unfinishedmsg = attr.ib()
787 inmergestate = attr.ib()
787 inmergestate = attr.ib()
788 unresolvedpaths = attr.ib()
788 unresolvedpaths = attr.ib()
789 _label = b'status.morestatus'
789 _label = b'status.morestatus'
790
790
791 def formatfile(self, path, fm):
791 def formatfile(self, path, fm):
792 if self.inmergestate and path in self.unresolvedpaths:
792 if self.inmergestate and path in self.unresolvedpaths:
793 fm.data(unresolved=True)
793 fm.data(unresolved=True)
794
794
795 def formatfooter(self, fm):
795 def formatfooter(self, fm):
796 statemsg = _(b'The repository is in an unfinished *%s* state.'
796 statemsg = (
797 ) % self.unfinishedop
797 _(b'The repository is in an unfinished *%s* state.')
798 % self.unfinishedop
799 )
798 fm.plain(b'%s\n' % _commentlines(statemsg), label=self._label)
800 fm.plain(b'%s\n' % _commentlines(statemsg), label=self._label)
799
801
800 self._formatconflicts(fm)
802 self._formatconflicts(fm)
801 if self.unfinishedmsg:
803 if self.unfinishedmsg:
802 fm.plain(b'%s\n' % _commentlines(self.unfinishedmsg),
804 fm.plain(
803 label=self._label)
805 b'%s\n' % _commentlines(self.unfinishedmsg), label=self._label
806 )
804
807
805 def _formatconflicts(self, fm):
808 def _formatconflicts(self, fm):
806 if not self.inmergestate:
809 if not self.inmergestate:
807 return
810 return
808
811
809 if self.unresolvedpaths:
812 if self.unresolvedpaths:
810 mergeliststr = b'\n'.join(
813 mergeliststr = b'\n'.join(
811 [
814 [
812 b' %s' % util.pathto(self.reporoot, encoding.getcwd(),
815 b' %s'
813 path)
816 % util.pathto(self.reporoot, encoding.getcwd(), path)
814 for path in self.unresolvedpaths
817 for path in self.unresolvedpaths
815 ]
818 ]
816 )
819 )
817 msg = (
820 msg = (
818 _(
821 _(
819 '''Unresolved merge conflicts:
822 '''Unresolved merge conflicts:
820
823
821 %s
824 %s
822
825
823 To mark files as resolved: hg resolve --mark FILE'''
826 To mark files as resolved: hg resolve --mark FILE'''
824 )
827 )
825 % mergeliststr
828 % mergeliststr
826 )
829 )
827 else:
830 else:
828 msg = _(b'No unresolved merge conflicts.')
831 msg = _(b'No unresolved merge conflicts.')
829
832
830 fm.plain(b'%s\n' % _commentlines(msg), label=self._label)
833 fm.plain(b'%s\n' % _commentlines(msg), label=self._label)
831
834
832
835
833 def readmorestatus(repo):
836 def readmorestatus(repo):
834 """Returns a morestatus object if the repo has unfinished state."""
837 """Returns a morestatus object if the repo has unfinished state."""
835 statetuple = statemod.getrepostate(repo)
838 statetuple = statemod.getrepostate(repo)
836 if not statetuple:
839 if not statetuple:
837 return None
840 return None
838
841
839 unfinishedop, unfinishedmsg = statetuple
842 unfinishedop, unfinishedmsg = statetuple
840 mergestate = mergemod.mergestate.read(repo)
843 mergestate = mergemod.mergestate.read(repo)
841 unresolved = None
844 unresolved = None
842 if mergestate.active():
845 if mergestate.active():
843 unresolved = sorted(mergestate.unresolved())
846 unresolved = sorted(mergestate.unresolved())
844 return morestatus(repo.root, unfinishedop, unfinishedmsg,
847 return morestatus(
845 unresolved is not None, unresolved)
848 repo.root,
849 unfinishedop,
850 unfinishedmsg,
851 unresolved is not None,
852 unresolved,
853 )
846
854
847
855
848 def findpossible(cmd, table, strict=False):
856 def findpossible(cmd, table, strict=False):
849 """
857 """
850 Return cmd -> (aliases, command table entry)
858 Return cmd -> (aliases, command table entry)
851 for each matching command.
859 for each matching command.
852 Return debug commands (or their aliases) only if no normal command matches.
860 Return debug commands (or their aliases) only if no normal command matches.
853 """
861 """
854 choice = {}
862 choice = {}
855 debugchoice = {}
863 debugchoice = {}
856
864
857 if cmd in table:
865 if cmd in table:
858 # short-circuit exact matches, "log" alias beats "log|history"
866 # short-circuit exact matches, "log" alias beats "log|history"
859 keys = [cmd]
867 keys = [cmd]
860 else:
868 else:
861 keys = table.keys()
869 keys = table.keys()
862
870
863 allcmds = []
871 allcmds = []
864 for e in keys:
872 for e in keys:
865 aliases = parsealiases(e)
873 aliases = parsealiases(e)
866 allcmds.extend(aliases)
874 allcmds.extend(aliases)
867 found = None
875 found = None
868 if cmd in aliases:
876 if cmd in aliases:
869 found = cmd
877 found = cmd
870 elif not strict:
878 elif not strict:
871 for a in aliases:
879 for a in aliases:
872 if a.startswith(cmd):
880 if a.startswith(cmd):
873 found = a
881 found = a
874 break
882 break
875 if found is not None:
883 if found is not None:
876 if aliases[0].startswith(b"debug") or found.startswith(b"debug"):
884 if aliases[0].startswith(b"debug") or found.startswith(b"debug"):
877 debugchoice[found] = (aliases, table[e])
885 debugchoice[found] = (aliases, table[e])
878 else:
886 else:
879 choice[found] = (aliases, table[e])
887 choice[found] = (aliases, table[e])
880
888
881 if not choice and debugchoice:
889 if not choice and debugchoice:
882 choice = debugchoice
890 choice = debugchoice
883
891
884 return choice, allcmds
892 return choice, allcmds
885
893
886
894
887 def findcmd(cmd, table, strict=True):
895 def findcmd(cmd, table, strict=True):
888 """Return (aliases, command table entry) for command string."""
896 """Return (aliases, command table entry) for command string."""
889 choice, allcmds = findpossible(cmd, table, strict)
897 choice, allcmds = findpossible(cmd, table, strict)
890
898
891 if cmd in choice:
899 if cmd in choice:
892 return choice[cmd]
900 return choice[cmd]
893
901
894 if len(choice) > 1:
902 if len(choice) > 1:
895 clist = sorted(choice)
903 clist = sorted(choice)
896 raise error.AmbiguousCommand(cmd, clist)
904 raise error.AmbiguousCommand(cmd, clist)
897
905
898 if choice:
906 if choice:
899 return list(choice.values())[0]
907 return list(choice.values())[0]
900
908
901 raise error.UnknownCommand(cmd, allcmds)
909 raise error.UnknownCommand(cmd, allcmds)
902
910
903
911
904 def changebranch(ui, repo, revs, label):
912 def changebranch(ui, repo, revs, label):
905 """ Change the branch name of given revs to label """
913 """ Change the branch name of given revs to label """
906
914
907 with repo.wlock(), repo.lock(), repo.transaction(b'branches'):
915 with repo.wlock(), repo.lock(), repo.transaction(b'branches'):
908 # abort in case of uncommitted merge or dirty wdir
916 # abort in case of uncommitted merge or dirty wdir
909 bailifchanged(repo)
917 bailifchanged(repo)
910 revs = scmutil.revrange(repo, revs)
918 revs = scmutil.revrange(repo, revs)
911 if not revs:
919 if not revs:
912 raise error.Abort(b"empty revision set")
920 raise error.Abort(b"empty revision set")
913 roots = repo.revs(b'roots(%ld)', revs)
921 roots = repo.revs(b'roots(%ld)', revs)
914 if len(roots) > 1:
922 if len(roots) > 1:
915 raise error.Abort(
923 raise error.Abort(
916 _(b"cannot change branch of non-linear revisions")
924 _(b"cannot change branch of non-linear revisions")
917 )
925 )
918 rewriteutil.precheck(repo, revs, b'change branch of')
926 rewriteutil.precheck(repo, revs, b'change branch of')
919
927
920 root = repo[roots.first()]
928 root = repo[roots.first()]
921 rpb = {parent.branch() for parent in root.parents()}
929 rpb = {parent.branch() for parent in root.parents()}
922 if label not in rpb and label in repo.branchmap():
930 if label not in rpb and label in repo.branchmap():
923 raise error.Abort(_(b"a branch of the same name already exists"))
931 raise error.Abort(_(b"a branch of the same name already exists"))
924
932
925 if repo.revs(b'obsolete() and %ld', revs):
933 if repo.revs(b'obsolete() and %ld', revs):
926 raise error.Abort(
934 raise error.Abort(
927 _(b"cannot change branch of a obsolete changeset")
935 _(b"cannot change branch of a obsolete changeset")
928 )
936 )
929
937
930 # make sure only topological heads
938 # make sure only topological heads
931 if repo.revs(b'heads(%ld) - head()', revs):
939 if repo.revs(b'heads(%ld) - head()', revs):
932 raise error.Abort(_(b"cannot change branch in middle of a stack"))
940 raise error.Abort(_(b"cannot change branch in middle of a stack"))
933
941
934 replacements = {}
942 replacements = {}
935 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
943 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
936 # mercurial.subrepo -> mercurial.cmdutil
944 # mercurial.subrepo -> mercurial.cmdutil
937 from . import context
945 from . import context
938
946
939 for rev in revs:
947 for rev in revs:
940 ctx = repo[rev]
948 ctx = repo[rev]
941 oldbranch = ctx.branch()
949 oldbranch = ctx.branch()
942 # check if ctx has same branch
950 # check if ctx has same branch
943 if oldbranch == label:
951 if oldbranch == label:
944 continue
952 continue
945
953
946 def filectxfn(repo, newctx, path):
954 def filectxfn(repo, newctx, path):
947 try:
955 try:
948 return ctx[path]
956 return ctx[path]
949 except error.ManifestLookupError:
957 except error.ManifestLookupError:
950 return None
958 return None
951
959
952 ui.debug(
960 ui.debug(
953 b"changing branch of '%s' from '%s' to '%s'\n"
961 b"changing branch of '%s' from '%s' to '%s'\n"
954 % (hex(ctx.node()), oldbranch, label)
962 % (hex(ctx.node()), oldbranch, label)
955 )
963 )
956 extra = ctx.extra()
964 extra = ctx.extra()
957 extra[b'branch_change'] = hex(ctx.node())
965 extra[b'branch_change'] = hex(ctx.node())
958 # While changing branch of set of linear commits, make sure that
966 # While changing branch of set of linear commits, make sure that
959 # we base our commits on new parent rather than old parent which
967 # we base our commits on new parent rather than old parent which
960 # was obsoleted while changing the branch
968 # was obsoleted while changing the branch
961 p1 = ctx.p1().node()
969 p1 = ctx.p1().node()
962 p2 = ctx.p2().node()
970 p2 = ctx.p2().node()
963 if p1 in replacements:
971 if p1 in replacements:
964 p1 = replacements[p1][0]
972 p1 = replacements[p1][0]
965 if p2 in replacements:
973 if p2 in replacements:
966 p2 = replacements[p2][0]
974 p2 = replacements[p2][0]
967
975
968 mc = context.memctx(
976 mc = context.memctx(
969 repo,
977 repo,
970 (p1, p2),
978 (p1, p2),
971 ctx.description(),
979 ctx.description(),
972 ctx.files(),
980 ctx.files(),
973 filectxfn,
981 filectxfn,
974 user=ctx.user(),
982 user=ctx.user(),
975 date=ctx.date(),
983 date=ctx.date(),
976 extra=extra,
984 extra=extra,
977 branch=label,
985 branch=label,
978 )
986 )
979
987
980 newnode = repo.commitctx(mc)
988 newnode = repo.commitctx(mc)
981 replacements[ctx.node()] = (newnode,)
989 replacements[ctx.node()] = (newnode,)
982 ui.debug(b'new node id is %s\n' % hex(newnode))
990 ui.debug(b'new node id is %s\n' % hex(newnode))
983
991
984 # create obsmarkers and move bookmarks
992 # create obsmarkers and move bookmarks
985 scmutil.cleanupnodes(
993 scmutil.cleanupnodes(
986 repo, replacements, b'branch-change', fixphase=True
994 repo, replacements, b'branch-change', fixphase=True
987 )
995 )
988
996
989 # move the working copy too
997 # move the working copy too
990 wctx = repo[None]
998 wctx = repo[None]
991 # in-progress merge is a bit too complex for now.
999 # in-progress merge is a bit too complex for now.
992 if len(wctx.parents()) == 1:
1000 if len(wctx.parents()) == 1:
993 newid = replacements.get(wctx.p1().node())
1001 newid = replacements.get(wctx.p1().node())
994 if newid is not None:
1002 if newid is not None:
995 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
1003 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
996 # mercurial.cmdutil
1004 # mercurial.cmdutil
997 from . import hg
1005 from . import hg
998
1006
999 hg.update(repo, newid[0], quietempty=True)
1007 hg.update(repo, newid[0], quietempty=True)
1000
1008
1001 ui.status(_(b"changed branch on %d changesets\n") % len(replacements))
1009 ui.status(_(b"changed branch on %d changesets\n") % len(replacements))
1002
1010
1003
1011
1004 def findrepo(p):
1012 def findrepo(p):
1005 while not os.path.isdir(os.path.join(p, b".hg")):
1013 while not os.path.isdir(os.path.join(p, b".hg")):
1006 oldp, p = p, os.path.dirname(p)
1014 oldp, p = p, os.path.dirname(p)
1007 if p == oldp:
1015 if p == oldp:
1008 return None
1016 return None
1009
1017
1010 return p
1018 return p
1011
1019
1012
1020
1013 def bailifchanged(repo, merge=True, hint=None):
1021 def bailifchanged(repo, merge=True, hint=None):
1014 """ enforce the precondition that working directory must be clean.
1022 """ enforce the precondition that working directory must be clean.
1015
1023
1016 'merge' can be set to false if a pending uncommitted merge should be
1024 'merge' can be set to false if a pending uncommitted merge should be
1017 ignored (such as when 'update --check' runs).
1025 ignored (such as when 'update --check' runs).
1018
1026
1019 'hint' is the usual hint given to Abort exception.
1027 'hint' is the usual hint given to Abort exception.
1020 """
1028 """
1021
1029
1022 if merge and repo.dirstate.p2() != nullid:
1030 if merge and repo.dirstate.p2() != nullid:
1023 raise error.Abort(_(b'outstanding uncommitted merge'), hint=hint)
1031 raise error.Abort(_(b'outstanding uncommitted merge'), hint=hint)
1024 st = repo.status()
1032 st = repo.status()
1025 if st.modified or st.added or st.removed or st.deleted:
1033 if st.modified or st.added or st.removed or st.deleted:
1026 raise error.Abort(_(b'uncommitted changes'), hint=hint)
1034 raise error.Abort(_(b'uncommitted changes'), hint=hint)
1027 ctx = repo[None]
1035 ctx = repo[None]
1028 for s in sorted(ctx.substate):
1036 for s in sorted(ctx.substate):
1029 ctx.sub(s).bailifchanged(hint=hint)
1037 ctx.sub(s).bailifchanged(hint=hint)
1030
1038
1031
1039
1032 def logmessage(ui, opts):
1040 def logmessage(ui, opts):
1033 """ get the log message according to -m and -l option """
1041 """ get the log message according to -m and -l option """
1034 message = opts.get(b'message')
1042 message = opts.get(b'message')
1035 logfile = opts.get(b'logfile')
1043 logfile = opts.get(b'logfile')
1036
1044
1037 if message and logfile:
1045 if message and logfile:
1038 raise error.Abort(
1046 raise error.Abort(
1039 _(b'options --message and --logfile are mutually exclusive')
1047 _(b'options --message and --logfile are mutually exclusive')
1040 )
1048 )
1041 if not message and logfile:
1049 if not message and logfile:
1042 try:
1050 try:
1043 if isstdiofilename(logfile):
1051 if isstdiofilename(logfile):
1044 message = ui.fin.read()
1052 message = ui.fin.read()
1045 else:
1053 else:
1046 message = b'\n'.join(util.readfile(logfile).splitlines())
1054 message = b'\n'.join(util.readfile(logfile).splitlines())
1047 except IOError as inst:
1055 except IOError as inst:
1048 raise error.Abort(
1056 raise error.Abort(
1049 _(b"can't read commit message '%s': %s")
1057 _(b"can't read commit message '%s': %s")
1050 % (logfile, encoding.strtolocal(inst.strerror))
1058 % (logfile, encoding.strtolocal(inst.strerror))
1051 )
1059 )
1052 return message
1060 return message
1053
1061
1054
1062
1055 def mergeeditform(ctxorbool, baseformname):
1063 def mergeeditform(ctxorbool, baseformname):
1056 """return appropriate editform name (referencing a committemplate)
1064 """return appropriate editform name (referencing a committemplate)
1057
1065
1058 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
1066 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
1059 merging is committed.
1067 merging is committed.
1060
1068
1061 This returns baseformname with '.merge' appended if it is a merge,
1069 This returns baseformname with '.merge' appended if it is a merge,
1062 otherwise '.normal' is appended.
1070 otherwise '.normal' is appended.
1063 """
1071 """
1064 if isinstance(ctxorbool, bool):
1072 if isinstance(ctxorbool, bool):
1065 if ctxorbool:
1073 if ctxorbool:
1066 return baseformname + b".merge"
1074 return baseformname + b".merge"
1067 elif len(ctxorbool.parents()) > 1:
1075 elif len(ctxorbool.parents()) > 1:
1068 return baseformname + b".merge"
1076 return baseformname + b".merge"
1069
1077
1070 return baseformname + b".normal"
1078 return baseformname + b".normal"
1071
1079
1072
1080
1073 def getcommiteditor(
1081 def getcommiteditor(
1074 edit=False, finishdesc=None, extramsg=None, editform=b'', **opts
1082 edit=False, finishdesc=None, extramsg=None, editform=b'', **opts
1075 ):
1083 ):
1076 """get appropriate commit message editor according to '--edit' option
1084 """get appropriate commit message editor according to '--edit' option
1077
1085
1078 'finishdesc' is a function to be called with edited commit message
1086 'finishdesc' is a function to be called with edited commit message
1079 (= 'description' of the new changeset) just after editing, but
1087 (= 'description' of the new changeset) just after editing, but
1080 before checking empty-ness. It should return actual text to be
1088 before checking empty-ness. It should return actual text to be
1081 stored into history. This allows to change description before
1089 stored into history. This allows to change description before
1082 storing.
1090 storing.
1083
1091
1084 'extramsg' is a extra message to be shown in the editor instead of
1092 'extramsg' is a extra message to be shown in the editor instead of
1085 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
1093 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
1086 is automatically added.
1094 is automatically added.
1087
1095
1088 'editform' is a dot-separated list of names, to distinguish
1096 'editform' is a dot-separated list of names, to distinguish
1089 the purpose of commit text editing.
1097 the purpose of commit text editing.
1090
1098
1091 'getcommiteditor' returns 'commitforceeditor' regardless of
1099 'getcommiteditor' returns 'commitforceeditor' regardless of
1092 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
1100 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
1093 they are specific for usage in MQ.
1101 they are specific for usage in MQ.
1094 """
1102 """
1095 if edit or finishdesc or extramsg:
1103 if edit or finishdesc or extramsg:
1096 return lambda r, c, s: commitforceeditor(
1104 return lambda r, c, s: commitforceeditor(
1097 r, c, s, finishdesc=finishdesc, extramsg=extramsg, editform=editform
1105 r, c, s, finishdesc=finishdesc, extramsg=extramsg, editform=editform
1098 )
1106 )
1099 elif editform:
1107 elif editform:
1100 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
1108 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
1101 else:
1109 else:
1102 return commiteditor
1110 return commiteditor
1103
1111
1104
1112
1105 def _escapecommandtemplate(tmpl):
1113 def _escapecommandtemplate(tmpl):
1106 parts = []
1114 parts = []
1107 for typ, start, end in templater.scantemplate(tmpl, raw=True):
1115 for typ, start, end in templater.scantemplate(tmpl, raw=True):
1108 if typ == b'string':
1116 if typ == b'string':
1109 parts.append(stringutil.escapestr(tmpl[start:end]))
1117 parts.append(stringutil.escapestr(tmpl[start:end]))
1110 else:
1118 else:
1111 parts.append(tmpl[start:end])
1119 parts.append(tmpl[start:end])
1112 return b''.join(parts)
1120 return b''.join(parts)
1113
1121
1114
1122
1115 def rendercommandtemplate(ui, tmpl, props):
1123 def rendercommandtemplate(ui, tmpl, props):
1116 r"""Expand a literal template 'tmpl' in a way suitable for command line
1124 r"""Expand a literal template 'tmpl' in a way suitable for command line
1117
1125
1118 '\' in outermost string is not taken as an escape character because it
1126 '\' in outermost string is not taken as an escape character because it
1119 is a directory separator on Windows.
1127 is a directory separator on Windows.
1120
1128
1121 >>> from . import ui as uimod
1129 >>> from . import ui as uimod
1122 >>> ui = uimod.ui()
1130 >>> ui = uimod.ui()
1123 >>> rendercommandtemplate(ui, b'c:\\{path}', {b'path': b'foo'})
1131 >>> rendercommandtemplate(ui, b'c:\\{path}', {b'path': b'foo'})
1124 'c:\\foo'
1132 'c:\\foo'
1125 >>> rendercommandtemplate(ui, b'{"c:\\{path}"}', {'path': b'foo'})
1133 >>> rendercommandtemplate(ui, b'{"c:\\{path}"}', {'path': b'foo'})
1126 'c:{path}'
1134 'c:{path}'
1127 """
1135 """
1128 if not tmpl:
1136 if not tmpl:
1129 return tmpl
1137 return tmpl
1130 t = formatter.maketemplater(ui, _escapecommandtemplate(tmpl))
1138 t = formatter.maketemplater(ui, _escapecommandtemplate(tmpl))
1131 return t.renderdefault(props)
1139 return t.renderdefault(props)
1132
1140
1133
1141
1134 def rendertemplate(ctx, tmpl, props=None):
1142 def rendertemplate(ctx, tmpl, props=None):
1135 """Expand a literal template 'tmpl' byte-string against one changeset
1143 """Expand a literal template 'tmpl' byte-string against one changeset
1136
1144
1137 Each props item must be a stringify-able value or a callable returning
1145 Each props item must be a stringify-able value or a callable returning
1138 such value, i.e. no bare list nor dict should be passed.
1146 such value, i.e. no bare list nor dict should be passed.
1139 """
1147 """
1140 repo = ctx.repo()
1148 repo = ctx.repo()
1141 tres = formatter.templateresources(repo.ui, repo)
1149 tres = formatter.templateresources(repo.ui, repo)
1142 t = formatter.maketemplater(
1150 t = formatter.maketemplater(
1143 repo.ui, tmpl, defaults=templatekw.keywords, resources=tres
1151 repo.ui, tmpl, defaults=templatekw.keywords, resources=tres
1144 )
1152 )
1145 mapping = {b'ctx': ctx}
1153 mapping = {b'ctx': ctx}
1146 if props:
1154 if props:
1147 mapping.update(props)
1155 mapping.update(props)
1148 return t.renderdefault(mapping)
1156 return t.renderdefault(mapping)
1149
1157
1150
1158
1151 def _buildfntemplate(pat, total=None, seqno=None, revwidth=None, pathname=None):
1159 def _buildfntemplate(pat, total=None, seqno=None, revwidth=None, pathname=None):
1152 r"""Convert old-style filename format string to template string
1160 r"""Convert old-style filename format string to template string
1153
1161
1154 >>> _buildfntemplate(b'foo-%b-%n.patch', seqno=0)
1162 >>> _buildfntemplate(b'foo-%b-%n.patch', seqno=0)
1155 'foo-{reporoot|basename}-{seqno}.patch'
1163 'foo-{reporoot|basename}-{seqno}.patch'
1156 >>> _buildfntemplate(b'%R{tags % "{tag}"}%H')
1164 >>> _buildfntemplate(b'%R{tags % "{tag}"}%H')
1157 '{rev}{tags % "{tag}"}{node}'
1165 '{rev}{tags % "{tag}"}{node}'
1158
1166
1159 '\' in outermost strings has to be escaped because it is a directory
1167 '\' in outermost strings has to be escaped because it is a directory
1160 separator on Windows:
1168 separator on Windows:
1161
1169
1162 >>> _buildfntemplate(b'c:\\tmp\\%R\\%n.patch', seqno=0)
1170 >>> _buildfntemplate(b'c:\\tmp\\%R\\%n.patch', seqno=0)
1163 'c:\\\\tmp\\\\{rev}\\\\{seqno}.patch'
1171 'c:\\\\tmp\\\\{rev}\\\\{seqno}.patch'
1164 >>> _buildfntemplate(b'\\\\foo\\bar.patch')
1172 >>> _buildfntemplate(b'\\\\foo\\bar.patch')
1165 '\\\\\\\\foo\\\\bar.patch'
1173 '\\\\\\\\foo\\\\bar.patch'
1166 >>> _buildfntemplate(b'\\{tags % "{tag}"}')
1174 >>> _buildfntemplate(b'\\{tags % "{tag}"}')
1167 '\\\\{tags % "{tag}"}'
1175 '\\\\{tags % "{tag}"}'
1168
1176
1169 but inner strings follow the template rules (i.e. '\' is taken as an
1177 but inner strings follow the template rules (i.e. '\' is taken as an
1170 escape character):
1178 escape character):
1171
1179
1172 >>> _buildfntemplate(br'{"c:\tmp"}', seqno=0)
1180 >>> _buildfntemplate(br'{"c:\tmp"}', seqno=0)
1173 '{"c:\\tmp"}'
1181 '{"c:\\tmp"}'
1174 """
1182 """
1175 expander = {
1183 expander = {
1176 b'H': b'{node}',
1184 b'H': b'{node}',
1177 b'R': b'{rev}',
1185 b'R': b'{rev}',
1178 b'h': b'{node|short}',
1186 b'h': b'{node|short}',
1179 b'm': br'{sub(r"[^\w]", "_", desc|firstline)}',
1187 b'm': br'{sub(r"[^\w]", "_", desc|firstline)}',
1180 b'r': b'{if(revwidth, pad(rev, revwidth, "0", left=True), rev)}',
1188 b'r': b'{if(revwidth, pad(rev, revwidth, "0", left=True), rev)}',
1181 b'%': b'%',
1189 b'%': b'%',
1182 b'b': b'{reporoot|basename}',
1190 b'b': b'{reporoot|basename}',
1183 }
1191 }
1184 if total is not None:
1192 if total is not None:
1185 expander[b'N'] = b'{total}'
1193 expander[b'N'] = b'{total}'
1186 if seqno is not None:
1194 if seqno is not None:
1187 expander[b'n'] = b'{seqno}'
1195 expander[b'n'] = b'{seqno}'
1188 if total is not None and seqno is not None:
1196 if total is not None and seqno is not None:
1189 expander[b'n'] = b'{pad(seqno, total|stringify|count, "0", left=True)}'
1197 expander[b'n'] = b'{pad(seqno, total|stringify|count, "0", left=True)}'
1190 if pathname is not None:
1198 if pathname is not None:
1191 expander[b's'] = b'{pathname|basename}'
1199 expander[b's'] = b'{pathname|basename}'
1192 expander[b'd'] = b'{if(pathname|dirname, pathname|dirname, ".")}'
1200 expander[b'd'] = b'{if(pathname|dirname, pathname|dirname, ".")}'
1193 expander[b'p'] = b'{pathname}'
1201 expander[b'p'] = b'{pathname}'
1194
1202
1195 newname = []
1203 newname = []
1196 for typ, start, end in templater.scantemplate(pat, raw=True):
1204 for typ, start, end in templater.scantemplate(pat, raw=True):
1197 if typ != b'string':
1205 if typ != b'string':
1198 newname.append(pat[start:end])
1206 newname.append(pat[start:end])
1199 continue
1207 continue
1200 i = start
1208 i = start
1201 while i < end:
1209 while i < end:
1202 n = pat.find(b'%', i, end)
1210 n = pat.find(b'%', i, end)
1203 if n < 0:
1211 if n < 0:
1204 newname.append(stringutil.escapestr(pat[i:end]))
1212 newname.append(stringutil.escapestr(pat[i:end]))
1205 break
1213 break
1206 newname.append(stringutil.escapestr(pat[i:n]))
1214 newname.append(stringutil.escapestr(pat[i:n]))
1207 if n + 2 > end:
1215 if n + 2 > end:
1208 raise error.Abort(
1216 raise error.Abort(
1209 _(b"incomplete format spec in output filename")
1217 _(b"incomplete format spec in output filename")
1210 )
1218 )
1211 c = pat[n + 1 : n + 2]
1219 c = pat[n + 1 : n + 2]
1212 i = n + 2
1220 i = n + 2
1213 try:
1221 try:
1214 newname.append(expander[c])
1222 newname.append(expander[c])
1215 except KeyError:
1223 except KeyError:
1216 raise error.Abort(
1224 raise error.Abort(
1217 _(b"invalid format spec '%%%s' in output filename") % c
1225 _(b"invalid format spec '%%%s' in output filename") % c
1218 )
1226 )
1219 return b''.join(newname)
1227 return b''.join(newname)
1220
1228
1221
1229
1222 def makefilename(ctx, pat, **props):
1230 def makefilename(ctx, pat, **props):
1223 if not pat:
1231 if not pat:
1224 return pat
1232 return pat
1225 tmpl = _buildfntemplate(pat, **props)
1233 tmpl = _buildfntemplate(pat, **props)
1226 # BUG: alias expansion shouldn't be made against template fragments
1234 # BUG: alias expansion shouldn't be made against template fragments
1227 # rewritten from %-format strings, but we have no easy way to partially
1235 # rewritten from %-format strings, but we have no easy way to partially
1228 # disable the expansion.
1236 # disable the expansion.
1229 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
1237 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
1230
1238
1231
1239
1232 def isstdiofilename(pat):
1240 def isstdiofilename(pat):
1233 """True if the given pat looks like a filename denoting stdin/stdout"""
1241 """True if the given pat looks like a filename denoting stdin/stdout"""
1234 return not pat or pat == b'-'
1242 return not pat or pat == b'-'
1235
1243
1236
1244
1237 class _unclosablefile(object):
1245 class _unclosablefile(object):
1238 def __init__(self, fp):
1246 def __init__(self, fp):
1239 self._fp = fp
1247 self._fp = fp
1240
1248
1241 def close(self):
1249 def close(self):
1242 pass
1250 pass
1243
1251
1244 def __iter__(self):
1252 def __iter__(self):
1245 return iter(self._fp)
1253 return iter(self._fp)
1246
1254
1247 def __getattr__(self, attr):
1255 def __getattr__(self, attr):
1248 return getattr(self._fp, attr)
1256 return getattr(self._fp, attr)
1249
1257
1250 def __enter__(self):
1258 def __enter__(self):
1251 return self
1259 return self
1252
1260
1253 def __exit__(self, exc_type, exc_value, exc_tb):
1261 def __exit__(self, exc_type, exc_value, exc_tb):
1254 pass
1262 pass
1255
1263
1256
1264
1257 def makefileobj(ctx, pat, mode=b'wb', **props):
1265 def makefileobj(ctx, pat, mode=b'wb', **props):
1258 writable = mode not in (b'r', b'rb')
1266 writable = mode not in (b'r', b'rb')
1259
1267
1260 if isstdiofilename(pat):
1268 if isstdiofilename(pat):
1261 repo = ctx.repo()
1269 repo = ctx.repo()
1262 if writable:
1270 if writable:
1263 fp = repo.ui.fout
1271 fp = repo.ui.fout
1264 else:
1272 else:
1265 fp = repo.ui.fin
1273 fp = repo.ui.fin
1266 return _unclosablefile(fp)
1274 return _unclosablefile(fp)
1267 fn = makefilename(ctx, pat, **props)
1275 fn = makefilename(ctx, pat, **props)
1268 return open(fn, mode)
1276 return open(fn, mode)
1269
1277
1270
1278
1271 def openstorage(repo, cmd, file_, opts, returnrevlog=False):
1279 def openstorage(repo, cmd, file_, opts, returnrevlog=False):
1272 """opens the changelog, manifest, a filelog or a given revlog"""
1280 """opens the changelog, manifest, a filelog or a given revlog"""
1273 cl = opts[b'changelog']
1281 cl = opts[b'changelog']
1274 mf = opts[b'manifest']
1282 mf = opts[b'manifest']
1275 dir = opts[b'dir']
1283 dir = opts[b'dir']
1276 msg = None
1284 msg = None
1277 if cl and mf:
1285 if cl and mf:
1278 msg = _(b'cannot specify --changelog and --manifest at the same time')
1286 msg = _(b'cannot specify --changelog and --manifest at the same time')
1279 elif cl and dir:
1287 elif cl and dir:
1280 msg = _(b'cannot specify --changelog and --dir at the same time')
1288 msg = _(b'cannot specify --changelog and --dir at the same time')
1281 elif cl or mf or dir:
1289 elif cl or mf or dir:
1282 if file_:
1290 if file_:
1283 msg = _(b'cannot specify filename with --changelog or --manifest')
1291 msg = _(b'cannot specify filename with --changelog or --manifest')
1284 elif not repo:
1292 elif not repo:
1285 msg = _(
1293 msg = _(
1286 b'cannot specify --changelog or --manifest or --dir '
1294 b'cannot specify --changelog or --manifest or --dir '
1287 b'without a repository'
1295 b'without a repository'
1288 )
1296 )
1289 if msg:
1297 if msg:
1290 raise error.Abort(msg)
1298 raise error.Abort(msg)
1291
1299
1292 r = None
1300 r = None
1293 if repo:
1301 if repo:
1294 if cl:
1302 if cl:
1295 r = repo.unfiltered().changelog
1303 r = repo.unfiltered().changelog
1296 elif dir:
1304 elif dir:
1297 if b'treemanifest' not in repo.requirements:
1305 if b'treemanifest' not in repo.requirements:
1298 raise error.Abort(
1306 raise error.Abort(
1299 _(
1307 _(
1300 b"--dir can only be used on repos with "
1308 b"--dir can only be used on repos with "
1301 b"treemanifest enabled"
1309 b"treemanifest enabled"
1302 )
1310 )
1303 )
1311 )
1304 if not dir.endswith(b'/'):
1312 if not dir.endswith(b'/'):
1305 dir = dir + b'/'
1313 dir = dir + b'/'
1306 dirlog = repo.manifestlog.getstorage(dir)
1314 dirlog = repo.manifestlog.getstorage(dir)
1307 if len(dirlog):
1315 if len(dirlog):
1308 r = dirlog
1316 r = dirlog
1309 elif mf:
1317 elif mf:
1310 r = repo.manifestlog.getstorage(b'')
1318 r = repo.manifestlog.getstorage(b'')
1311 elif file_:
1319 elif file_:
1312 filelog = repo.file(file_)
1320 filelog = repo.file(file_)
1313 if len(filelog):
1321 if len(filelog):
1314 r = filelog
1322 r = filelog
1315
1323
1316 # Not all storage may be revlogs. If requested, try to return an actual
1324 # Not all storage may be revlogs. If requested, try to return an actual
1317 # revlog instance.
1325 # revlog instance.
1318 if returnrevlog:
1326 if returnrevlog:
1319 if isinstance(r, revlog.revlog):
1327 if isinstance(r, revlog.revlog):
1320 pass
1328 pass
1321 elif util.safehasattr(r, b'_revlog'):
1329 elif util.safehasattr(r, b'_revlog'):
1322 r = r._revlog # pytype: disable=attribute-error
1330 r = r._revlog # pytype: disable=attribute-error
1323 elif r is not None:
1331 elif r is not None:
1324 raise error.Abort(_(b'%r does not appear to be a revlog') % r)
1332 raise error.Abort(_(b'%r does not appear to be a revlog') % r)
1325
1333
1326 if not r:
1334 if not r:
1327 if not returnrevlog:
1335 if not returnrevlog:
1328 raise error.Abort(_(b'cannot give path to non-revlog'))
1336 raise error.Abort(_(b'cannot give path to non-revlog'))
1329
1337
1330 if not file_:
1338 if not file_:
1331 raise error.CommandError(cmd, _(b'invalid arguments'))
1339 raise error.CommandError(cmd, _(b'invalid arguments'))
1332 if not os.path.isfile(file_):
1340 if not os.path.isfile(file_):
1333 raise error.Abort(_(b"revlog '%s' not found") % file_)
1341 raise error.Abort(_(b"revlog '%s' not found") % file_)
1334 r = revlog.revlog(
1342 r = revlog.revlog(
1335 vfsmod.vfs(encoding.getcwd(), audit=False), file_[:-2] + b".i"
1343 vfsmod.vfs(encoding.getcwd(), audit=False), file_[:-2] + b".i"
1336 )
1344 )
1337 return r
1345 return r
1338
1346
1339
1347
1340 def openrevlog(repo, cmd, file_, opts):
1348 def openrevlog(repo, cmd, file_, opts):
1341 """Obtain a revlog backing storage of an item.
1349 """Obtain a revlog backing storage of an item.
1342
1350
1343 This is similar to ``openstorage()`` except it always returns a revlog.
1351 This is similar to ``openstorage()`` except it always returns a revlog.
1344
1352
1345 In most cases, a caller cares about the main storage object - not the
1353 In most cases, a caller cares about the main storage object - not the
1346 revlog backing it. Therefore, this function should only be used by code
1354 revlog backing it. Therefore, this function should only be used by code
1347 that needs to examine low-level revlog implementation details. e.g. debug
1355 that needs to examine low-level revlog implementation details. e.g. debug
1348 commands.
1356 commands.
1349 """
1357 """
1350 return openstorage(repo, cmd, file_, opts, returnrevlog=True)
1358 return openstorage(repo, cmd, file_, opts, returnrevlog=True)
1351
1359
1352
1360
1353 def copy(ui, repo, pats, opts, rename=False):
1361 def copy(ui, repo, pats, opts, rename=False):
1354 # called with the repo lock held
1362 # called with the repo lock held
1355 #
1363 #
1356 # hgsep => pathname that uses "/" to separate directories
1364 # hgsep => pathname that uses "/" to separate directories
1357 # ossep => pathname that uses os.sep to separate directories
1365 # ossep => pathname that uses os.sep to separate directories
1358 cwd = repo.getcwd()
1366 cwd = repo.getcwd()
1359 targets = {}
1367 targets = {}
1360 after = opts.get(b"after")
1368 after = opts.get(b"after")
1361 dryrun = opts.get(b"dry_run")
1369 dryrun = opts.get(b"dry_run")
1362 wctx = repo[None]
1370 wctx = repo[None]
1363
1371
1364 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
1372 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
1365
1373
1366 def walkpat(pat):
1374 def walkpat(pat):
1367 srcs = []
1375 srcs = []
1368 if after:
1376 if after:
1369 badstates = b'?'
1377 badstates = b'?'
1370 else:
1378 else:
1371 badstates = b'?r'
1379 badstates = b'?r'
1372 m = scmutil.match(wctx, [pat], opts, globbed=True)
1380 m = scmutil.match(wctx, [pat], opts, globbed=True)
1373 for abs in wctx.walk(m):
1381 for abs in wctx.walk(m):
1374 state = repo.dirstate[abs]
1382 state = repo.dirstate[abs]
1375 rel = uipathfn(abs)
1383 rel = uipathfn(abs)
1376 exact = m.exact(abs)
1384 exact = m.exact(abs)
1377 if state in badstates:
1385 if state in badstates:
1378 if exact and state == b'?':
1386 if exact and state == b'?':
1379 ui.warn(_(b'%s: not copying - file is not managed\n') % rel)
1387 ui.warn(_(b'%s: not copying - file is not managed\n') % rel)
1380 if exact and state == b'r':
1388 if exact and state == b'r':
1381 ui.warn(
1389 ui.warn(
1382 _(
1390 _(
1383 b'%s: not copying - file has been marked for'
1391 b'%s: not copying - file has been marked for'
1384 b' remove\n'
1392 b' remove\n'
1385 )
1393 )
1386 % rel
1394 % rel
1387 )
1395 )
1388 continue
1396 continue
1389 # abs: hgsep
1397 # abs: hgsep
1390 # rel: ossep
1398 # rel: ossep
1391 srcs.append((abs, rel, exact))
1399 srcs.append((abs, rel, exact))
1392 return srcs
1400 return srcs
1393
1401
1394 # abssrc: hgsep
1402 # abssrc: hgsep
1395 # relsrc: ossep
1403 # relsrc: ossep
1396 # otarget: ossep
1404 # otarget: ossep
1397 def copyfile(abssrc, relsrc, otarget, exact):
1405 def copyfile(abssrc, relsrc, otarget, exact):
1398 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1406 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1399 if b'/' in abstarget:
1407 if b'/' in abstarget:
1400 # We cannot normalize abstarget itself, this would prevent
1408 # We cannot normalize abstarget itself, this would prevent
1401 # case only renames, like a => A.
1409 # case only renames, like a => A.
1402 abspath, absname = abstarget.rsplit(b'/', 1)
1410 abspath, absname = abstarget.rsplit(b'/', 1)
1403 abstarget = repo.dirstate.normalize(abspath) + b'/' + absname
1411 abstarget = repo.dirstate.normalize(abspath) + b'/' + absname
1404 reltarget = repo.pathto(abstarget, cwd)
1412 reltarget = repo.pathto(abstarget, cwd)
1405 target = repo.wjoin(abstarget)
1413 target = repo.wjoin(abstarget)
1406 src = repo.wjoin(abssrc)
1414 src = repo.wjoin(abssrc)
1407 state = repo.dirstate[abstarget]
1415 state = repo.dirstate[abstarget]
1408
1416
1409 scmutil.checkportable(ui, abstarget)
1417 scmutil.checkportable(ui, abstarget)
1410
1418
1411 # check for collisions
1419 # check for collisions
1412 prevsrc = targets.get(abstarget)
1420 prevsrc = targets.get(abstarget)
1413 if prevsrc is not None:
1421 if prevsrc is not None:
1414 ui.warn(
1422 ui.warn(
1415 _(b'%s: not overwriting - %s collides with %s\n')
1423 _(b'%s: not overwriting - %s collides with %s\n')
1416 % (
1424 % (
1417 reltarget,
1425 reltarget,
1418 repo.pathto(abssrc, cwd),
1426 repo.pathto(abssrc, cwd),
1419 repo.pathto(prevsrc, cwd),
1427 repo.pathto(prevsrc, cwd),
1420 )
1428 )
1421 )
1429 )
1422 return True # report a failure
1430 return True # report a failure
1423
1431
1424 # check for overwrites
1432 # check for overwrites
1425 exists = os.path.lexists(target)
1433 exists = os.path.lexists(target)
1426 samefile = False
1434 samefile = False
1427 if exists and abssrc != abstarget:
1435 if exists and abssrc != abstarget:
1428 if repo.dirstate.normalize(abssrc) == repo.dirstate.normalize(
1436 if repo.dirstate.normalize(abssrc) == repo.dirstate.normalize(
1429 abstarget
1437 abstarget
1430 ):
1438 ):
1431 if not rename:
1439 if not rename:
1432 ui.warn(_(b"%s: can't copy - same file\n") % reltarget)
1440 ui.warn(_(b"%s: can't copy - same file\n") % reltarget)
1433 return True # report a failure
1441 return True # report a failure
1434 exists = False
1442 exists = False
1435 samefile = True
1443 samefile = True
1436
1444
1437 if not after and exists or after and state in b'mn':
1445 if not after and exists or after and state in b'mn':
1438 if not opts[b'force']:
1446 if not opts[b'force']:
1439 if state in b'mn':
1447 if state in b'mn':
1440 msg = _(b'%s: not overwriting - file already committed\n')
1448 msg = _(b'%s: not overwriting - file already committed\n')
1441 if after:
1449 if after:
1442 flags = b'--after --force'
1450 flags = b'--after --force'
1443 else:
1451 else:
1444 flags = b'--force'
1452 flags = b'--force'
1445 if rename:
1453 if rename:
1446 hint = (
1454 hint = (
1447 _(
1455 _(
1448 b"('hg rename %s' to replace the file by "
1456 b"('hg rename %s' to replace the file by "
1449 b'recording a rename)\n'
1457 b'recording a rename)\n'
1450 )
1458 )
1451 % flags
1459 % flags
1452 )
1460 )
1453 else:
1461 else:
1454 hint = (
1462 hint = (
1455 _(
1463 _(
1456 b"('hg copy %s' to replace the file by "
1464 b"('hg copy %s' to replace the file by "
1457 b'recording a copy)\n'
1465 b'recording a copy)\n'
1458 )
1466 )
1459 % flags
1467 % flags
1460 )
1468 )
1461 else:
1469 else:
1462 msg = _(b'%s: not overwriting - file exists\n')
1470 msg = _(b'%s: not overwriting - file exists\n')
1463 if rename:
1471 if rename:
1464 hint = _(
1472 hint = _(
1465 b"('hg rename --after' to record the rename)\n"
1473 b"('hg rename --after' to record the rename)\n"
1466 )
1474 )
1467 else:
1475 else:
1468 hint = _(b"('hg copy --after' to record the copy)\n")
1476 hint = _(b"('hg copy --after' to record the copy)\n")
1469 ui.warn(msg % reltarget)
1477 ui.warn(msg % reltarget)
1470 ui.warn(hint)
1478 ui.warn(hint)
1471 return True # report a failure
1479 return True # report a failure
1472
1480
1473 if after:
1481 if after:
1474 if not exists:
1482 if not exists:
1475 if rename:
1483 if rename:
1476 ui.warn(
1484 ui.warn(
1477 _(b'%s: not recording move - %s does not exist\n')
1485 _(b'%s: not recording move - %s does not exist\n')
1478 % (relsrc, reltarget)
1486 % (relsrc, reltarget)
1479 )
1487 )
1480 else:
1488 else:
1481 ui.warn(
1489 ui.warn(
1482 _(b'%s: not recording copy - %s does not exist\n')
1490 _(b'%s: not recording copy - %s does not exist\n')
1483 % (relsrc, reltarget)
1491 % (relsrc, reltarget)
1484 )
1492 )
1485 return True # report a failure
1493 return True # report a failure
1486 elif not dryrun:
1494 elif not dryrun:
1487 try:
1495 try:
1488 if exists:
1496 if exists:
1489 os.unlink(target)
1497 os.unlink(target)
1490 targetdir = os.path.dirname(target) or b'.'
1498 targetdir = os.path.dirname(target) or b'.'
1491 if not os.path.isdir(targetdir):
1499 if not os.path.isdir(targetdir):
1492 os.makedirs(targetdir)
1500 os.makedirs(targetdir)
1493 if samefile:
1501 if samefile:
1494 tmp = target + b"~hgrename"
1502 tmp = target + b"~hgrename"
1495 os.rename(src, tmp)
1503 os.rename(src, tmp)
1496 os.rename(tmp, target)
1504 os.rename(tmp, target)
1497 else:
1505 else:
1498 # Preserve stat info on renames, not on copies; this matches
1506 # Preserve stat info on renames, not on copies; this matches
1499 # Linux CLI behavior.
1507 # Linux CLI behavior.
1500 util.copyfile(src, target, copystat=rename)
1508 util.copyfile(src, target, copystat=rename)
1501 srcexists = True
1509 srcexists = True
1502 except IOError as inst:
1510 except IOError as inst:
1503 if inst.errno == errno.ENOENT:
1511 if inst.errno == errno.ENOENT:
1504 ui.warn(_(b'%s: deleted in working directory\n') % relsrc)
1512 ui.warn(_(b'%s: deleted in working directory\n') % relsrc)
1505 srcexists = False
1513 srcexists = False
1506 else:
1514 else:
1507 ui.warn(
1515 ui.warn(
1508 _(b'%s: cannot copy - %s\n')
1516 _(b'%s: cannot copy - %s\n')
1509 % (relsrc, encoding.strtolocal(inst.strerror))
1517 % (relsrc, encoding.strtolocal(inst.strerror))
1510 )
1518 )
1511 return True # report a failure
1519 return True # report a failure
1512
1520
1513 if ui.verbose or not exact:
1521 if ui.verbose or not exact:
1514 if rename:
1522 if rename:
1515 ui.status(_(b'moving %s to %s\n') % (relsrc, reltarget))
1523 ui.status(_(b'moving %s to %s\n') % (relsrc, reltarget))
1516 else:
1524 else:
1517 ui.status(_(b'copying %s to %s\n') % (relsrc, reltarget))
1525 ui.status(_(b'copying %s to %s\n') % (relsrc, reltarget))
1518
1526
1519 targets[abstarget] = abssrc
1527 targets[abstarget] = abssrc
1520
1528
1521 # fix up dirstate
1529 # fix up dirstate
1522 scmutil.dirstatecopy(
1530 scmutil.dirstatecopy(
1523 ui, repo, wctx, abssrc, abstarget, dryrun=dryrun, cwd=cwd
1531 ui, repo, wctx, abssrc, abstarget, dryrun=dryrun, cwd=cwd
1524 )
1532 )
1525 if rename and not dryrun:
1533 if rename and not dryrun:
1526 if not after and srcexists and not samefile:
1534 if not after and srcexists and not samefile:
1527 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
1535 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
1528 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir)
1536 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir)
1529 wctx.forget([abssrc])
1537 wctx.forget([abssrc])
1530
1538
1531 # pat: ossep
1539 # pat: ossep
1532 # dest ossep
1540 # dest ossep
1533 # srcs: list of (hgsep, hgsep, ossep, bool)
1541 # srcs: list of (hgsep, hgsep, ossep, bool)
1534 # return: function that takes hgsep and returns ossep
1542 # return: function that takes hgsep and returns ossep
1535 def targetpathfn(pat, dest, srcs):
1543 def targetpathfn(pat, dest, srcs):
1536 if os.path.isdir(pat):
1544 if os.path.isdir(pat):
1537 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1545 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1538 abspfx = util.localpath(abspfx)
1546 abspfx = util.localpath(abspfx)
1539 if destdirexists:
1547 if destdirexists:
1540 striplen = len(os.path.split(abspfx)[0])
1548 striplen = len(os.path.split(abspfx)[0])
1541 else:
1549 else:
1542 striplen = len(abspfx)
1550 striplen = len(abspfx)
1543 if striplen:
1551 if striplen:
1544 striplen += len(pycompat.ossep)
1552 striplen += len(pycompat.ossep)
1545 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1553 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1546 elif destdirexists:
1554 elif destdirexists:
1547 res = lambda p: os.path.join(
1555 res = lambda p: os.path.join(
1548 dest, os.path.basename(util.localpath(p))
1556 dest, os.path.basename(util.localpath(p))
1549 )
1557 )
1550 else:
1558 else:
1551 res = lambda p: dest
1559 res = lambda p: dest
1552 return res
1560 return res
1553
1561
1554 # pat: ossep
1562 # pat: ossep
1555 # dest ossep
1563 # dest ossep
1556 # srcs: list of (hgsep, hgsep, ossep, bool)
1564 # srcs: list of (hgsep, hgsep, ossep, bool)
1557 # return: function that takes hgsep and returns ossep
1565 # return: function that takes hgsep and returns ossep
1558 def targetpathafterfn(pat, dest, srcs):
1566 def targetpathafterfn(pat, dest, srcs):
1559 if matchmod.patkind(pat):
1567 if matchmod.patkind(pat):
1560 # a mercurial pattern
1568 # a mercurial pattern
1561 res = lambda p: os.path.join(
1569 res = lambda p: os.path.join(
1562 dest, os.path.basename(util.localpath(p))
1570 dest, os.path.basename(util.localpath(p))
1563 )
1571 )
1564 else:
1572 else:
1565 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1573 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1566 if len(abspfx) < len(srcs[0][0]):
1574 if len(abspfx) < len(srcs[0][0]):
1567 # A directory. Either the target path contains the last
1575 # A directory. Either the target path contains the last
1568 # component of the source path or it does not.
1576 # component of the source path or it does not.
1569 def evalpath(striplen):
1577 def evalpath(striplen):
1570 score = 0
1578 score = 0
1571 for s in srcs:
1579 for s in srcs:
1572 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1580 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1573 if os.path.lexists(t):
1581 if os.path.lexists(t):
1574 score += 1
1582 score += 1
1575 return score
1583 return score
1576
1584
1577 abspfx = util.localpath(abspfx)
1585 abspfx = util.localpath(abspfx)
1578 striplen = len(abspfx)
1586 striplen = len(abspfx)
1579 if striplen:
1587 if striplen:
1580 striplen += len(pycompat.ossep)
1588 striplen += len(pycompat.ossep)
1581 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1589 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1582 score = evalpath(striplen)
1590 score = evalpath(striplen)
1583 striplen1 = len(os.path.split(abspfx)[0])
1591 striplen1 = len(os.path.split(abspfx)[0])
1584 if striplen1:
1592 if striplen1:
1585 striplen1 += len(pycompat.ossep)
1593 striplen1 += len(pycompat.ossep)
1586 if evalpath(striplen1) > score:
1594 if evalpath(striplen1) > score:
1587 striplen = striplen1
1595 striplen = striplen1
1588 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1596 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1589 else:
1597 else:
1590 # a file
1598 # a file
1591 if destdirexists:
1599 if destdirexists:
1592 res = lambda p: os.path.join(
1600 res = lambda p: os.path.join(
1593 dest, os.path.basename(util.localpath(p))
1601 dest, os.path.basename(util.localpath(p))
1594 )
1602 )
1595 else:
1603 else:
1596 res = lambda p: dest
1604 res = lambda p: dest
1597 return res
1605 return res
1598
1606
1599 pats = scmutil.expandpats(pats)
1607 pats = scmutil.expandpats(pats)
1600 if not pats:
1608 if not pats:
1601 raise error.Abort(_(b'no source or destination specified'))
1609 raise error.Abort(_(b'no source or destination specified'))
1602 if len(pats) == 1:
1610 if len(pats) == 1:
1603 raise error.Abort(_(b'no destination specified'))
1611 raise error.Abort(_(b'no destination specified'))
1604 dest = pats.pop()
1612 dest = pats.pop()
1605 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1613 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1606 if not destdirexists:
1614 if not destdirexists:
1607 if len(pats) > 1 or matchmod.patkind(pats[0]):
1615 if len(pats) > 1 or matchmod.patkind(pats[0]):
1608 raise error.Abort(
1616 raise error.Abort(
1609 _(
1617 _(
1610 b'with multiple sources, destination must be an '
1618 b'with multiple sources, destination must be an '
1611 b'existing directory'
1619 b'existing directory'
1612 )
1620 )
1613 )
1621 )
1614 if util.endswithsep(dest):
1622 if util.endswithsep(dest):
1615 raise error.Abort(_(b'destination %s is not a directory') % dest)
1623 raise error.Abort(_(b'destination %s is not a directory') % dest)
1616
1624
1617 tfn = targetpathfn
1625 tfn = targetpathfn
1618 if after:
1626 if after:
1619 tfn = targetpathafterfn
1627 tfn = targetpathafterfn
1620 copylist = []
1628 copylist = []
1621 for pat in pats:
1629 for pat in pats:
1622 srcs = walkpat(pat)
1630 srcs = walkpat(pat)
1623 if not srcs:
1631 if not srcs:
1624 continue
1632 continue
1625 copylist.append((tfn(pat, dest, srcs), srcs))
1633 copylist.append((tfn(pat, dest, srcs), srcs))
1626 if not copylist:
1634 if not copylist:
1627 raise error.Abort(_(b'no files to copy'))
1635 raise error.Abort(_(b'no files to copy'))
1628
1636
1629 errors = 0
1637 errors = 0
1630 for targetpath, srcs in copylist:
1638 for targetpath, srcs in copylist:
1631 for abssrc, relsrc, exact in srcs:
1639 for abssrc, relsrc, exact in srcs:
1632 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1640 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1633 errors += 1
1641 errors += 1
1634
1642
1635 return errors != 0
1643 return errors != 0
1636
1644
1637
1645
1638 ## facility to let extension process additional data into an import patch
1646 ## facility to let extension process additional data into an import patch
1639 # list of identifier to be executed in order
1647 # list of identifier to be executed in order
1640 extrapreimport = [] # run before commit
1648 extrapreimport = [] # run before commit
1641 extrapostimport = [] # run after commit
1649 extrapostimport = [] # run after commit
1642 # mapping from identifier to actual import function
1650 # mapping from identifier to actual import function
1643 #
1651 #
1644 # 'preimport' are run before the commit is made and are provided the following
1652 # 'preimport' are run before the commit is made and are provided the following
1645 # arguments:
1653 # arguments:
1646 # - repo: the localrepository instance,
1654 # - repo: the localrepository instance,
1647 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1655 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1648 # - extra: the future extra dictionary of the changeset, please mutate it,
1656 # - extra: the future extra dictionary of the changeset, please mutate it,
1649 # - opts: the import options.
1657 # - opts: the import options.
1650 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1658 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1651 # mutation of in memory commit and more. Feel free to rework the code to get
1659 # mutation of in memory commit and more. Feel free to rework the code to get
1652 # there.
1660 # there.
1653 extrapreimportmap = {}
1661 extrapreimportmap = {}
1654 # 'postimport' are run after the commit is made and are provided the following
1662 # 'postimport' are run after the commit is made and are provided the following
1655 # argument:
1663 # argument:
1656 # - ctx: the changectx created by import.
1664 # - ctx: the changectx created by import.
1657 extrapostimportmap = {}
1665 extrapostimportmap = {}
1658
1666
1659
1667
1660 def tryimportone(ui, repo, patchdata, parents, opts, msgs, updatefunc):
1668 def tryimportone(ui, repo, patchdata, parents, opts, msgs, updatefunc):
1661 """Utility function used by commands.import to import a single patch
1669 """Utility function used by commands.import to import a single patch
1662
1670
1663 This function is explicitly defined here to help the evolve extension to
1671 This function is explicitly defined here to help the evolve extension to
1664 wrap this part of the import logic.
1672 wrap this part of the import logic.
1665
1673
1666 The API is currently a bit ugly because it a simple code translation from
1674 The API is currently a bit ugly because it a simple code translation from
1667 the import command. Feel free to make it better.
1675 the import command. Feel free to make it better.
1668
1676
1669 :patchdata: a dictionary containing parsed patch data (such as from
1677 :patchdata: a dictionary containing parsed patch data (such as from
1670 ``patch.extract()``)
1678 ``patch.extract()``)
1671 :parents: nodes that will be parent of the created commit
1679 :parents: nodes that will be parent of the created commit
1672 :opts: the full dict of option passed to the import command
1680 :opts: the full dict of option passed to the import command
1673 :msgs: list to save commit message to.
1681 :msgs: list to save commit message to.
1674 (used in case we need to save it when failing)
1682 (used in case we need to save it when failing)
1675 :updatefunc: a function that update a repo to a given node
1683 :updatefunc: a function that update a repo to a given node
1676 updatefunc(<repo>, <node>)
1684 updatefunc(<repo>, <node>)
1677 """
1685 """
1678 # avoid cycle context -> subrepo -> cmdutil
1686 # avoid cycle context -> subrepo -> cmdutil
1679 from . import context
1687 from . import context
1680
1688
1681 tmpname = patchdata.get(b'filename')
1689 tmpname = patchdata.get(b'filename')
1682 message = patchdata.get(b'message')
1690 message = patchdata.get(b'message')
1683 user = opts.get(b'user') or patchdata.get(b'user')
1691 user = opts.get(b'user') or patchdata.get(b'user')
1684 date = opts.get(b'date') or patchdata.get(b'date')
1692 date = opts.get(b'date') or patchdata.get(b'date')
1685 branch = patchdata.get(b'branch')
1693 branch = patchdata.get(b'branch')
1686 nodeid = patchdata.get(b'nodeid')
1694 nodeid = patchdata.get(b'nodeid')
1687 p1 = patchdata.get(b'p1')
1695 p1 = patchdata.get(b'p1')
1688 p2 = patchdata.get(b'p2')
1696 p2 = patchdata.get(b'p2')
1689
1697
1690 nocommit = opts.get(b'no_commit')
1698 nocommit = opts.get(b'no_commit')
1691 importbranch = opts.get(b'import_branch')
1699 importbranch = opts.get(b'import_branch')
1692 update = not opts.get(b'bypass')
1700 update = not opts.get(b'bypass')
1693 strip = opts[b"strip"]
1701 strip = opts[b"strip"]
1694 prefix = opts[b"prefix"]
1702 prefix = opts[b"prefix"]
1695 sim = float(opts.get(b'similarity') or 0)
1703 sim = float(opts.get(b'similarity') or 0)
1696
1704
1697 if not tmpname:
1705 if not tmpname:
1698 return None, None, False
1706 return None, None, False
1699
1707
1700 rejects = False
1708 rejects = False
1701
1709
1702 cmdline_message = logmessage(ui, opts)
1710 cmdline_message = logmessage(ui, opts)
1703 if cmdline_message:
1711 if cmdline_message:
1704 # pickup the cmdline msg
1712 # pickup the cmdline msg
1705 message = cmdline_message
1713 message = cmdline_message
1706 elif message:
1714 elif message:
1707 # pickup the patch msg
1715 # pickup the patch msg
1708 message = message.strip()
1716 message = message.strip()
1709 else:
1717 else:
1710 # launch the editor
1718 # launch the editor
1711 message = None
1719 message = None
1712 ui.debug(b'message:\n%s\n' % (message or b''))
1720 ui.debug(b'message:\n%s\n' % (message or b''))
1713
1721
1714 if len(parents) == 1:
1722 if len(parents) == 1:
1715 parents.append(repo[nullid])
1723 parents.append(repo[nullid])
1716 if opts.get(b'exact'):
1724 if opts.get(b'exact'):
1717 if not nodeid or not p1:
1725 if not nodeid or not p1:
1718 raise error.Abort(_(b'not a Mercurial patch'))
1726 raise error.Abort(_(b'not a Mercurial patch'))
1719 p1 = repo[p1]
1727 p1 = repo[p1]
1720 p2 = repo[p2 or nullid]
1728 p2 = repo[p2 or nullid]
1721 elif p2:
1729 elif p2:
1722 try:
1730 try:
1723 p1 = repo[p1]
1731 p1 = repo[p1]
1724 p2 = repo[p2]
1732 p2 = repo[p2]
1725 # Without any options, consider p2 only if the
1733 # Without any options, consider p2 only if the
1726 # patch is being applied on top of the recorded
1734 # patch is being applied on top of the recorded
1727 # first parent.
1735 # first parent.
1728 if p1 != parents[0]:
1736 if p1 != parents[0]:
1729 p1 = parents[0]
1737 p1 = parents[0]
1730 p2 = repo[nullid]
1738 p2 = repo[nullid]
1731 except error.RepoError:
1739 except error.RepoError:
1732 p1, p2 = parents
1740 p1, p2 = parents
1733 if p2.node() == nullid:
1741 if p2.node() == nullid:
1734 ui.warn(
1742 ui.warn(
1735 _(
1743 _(
1736 b"warning: import the patch as a normal revision\n"
1744 b"warning: import the patch as a normal revision\n"
1737 b"(use --exact to import the patch as a merge)\n"
1745 b"(use --exact to import the patch as a merge)\n"
1738 )
1746 )
1739 )
1747 )
1740 else:
1748 else:
1741 p1, p2 = parents
1749 p1, p2 = parents
1742
1750
1743 n = None
1751 n = None
1744 if update:
1752 if update:
1745 if p1 != parents[0]:
1753 if p1 != parents[0]:
1746 updatefunc(repo, p1.node())
1754 updatefunc(repo, p1.node())
1747 if p2 != parents[1]:
1755 if p2 != parents[1]:
1748 repo.setparents(p1.node(), p2.node())
1756 repo.setparents(p1.node(), p2.node())
1749
1757
1750 if opts.get(b'exact') or importbranch:
1758 if opts.get(b'exact') or importbranch:
1751 repo.dirstate.setbranch(branch or b'default')
1759 repo.dirstate.setbranch(branch or b'default')
1752
1760
1753 partial = opts.get(b'partial', False)
1761 partial = opts.get(b'partial', False)
1754 files = set()
1762 files = set()
1755 try:
1763 try:
1756 patch.patch(
1764 patch.patch(
1757 ui,
1765 ui,
1758 repo,
1766 repo,
1759 tmpname,
1767 tmpname,
1760 strip=strip,
1768 strip=strip,
1761 prefix=prefix,
1769 prefix=prefix,
1762 files=files,
1770 files=files,
1763 eolmode=None,
1771 eolmode=None,
1764 similarity=sim / 100.0,
1772 similarity=sim / 100.0,
1765 )
1773 )
1766 except error.PatchError as e:
1774 except error.PatchError as e:
1767 if not partial:
1775 if not partial:
1768 raise error.Abort(pycompat.bytestr(e))
1776 raise error.Abort(pycompat.bytestr(e))
1769 if partial:
1777 if partial:
1770 rejects = True
1778 rejects = True
1771
1779
1772 files = list(files)
1780 files = list(files)
1773 if nocommit:
1781 if nocommit:
1774 if message:
1782 if message:
1775 msgs.append(message)
1783 msgs.append(message)
1776 else:
1784 else:
1777 if opts.get(b'exact') or p2:
1785 if opts.get(b'exact') or p2:
1778 # If you got here, you either use --force and know what
1786 # If you got here, you either use --force and know what
1779 # you are doing or used --exact or a merge patch while
1787 # you are doing or used --exact or a merge patch while
1780 # being updated to its first parent.
1788 # being updated to its first parent.
1781 m = None
1789 m = None
1782 else:
1790 else:
1783 m = scmutil.matchfiles(repo, files or [])
1791 m = scmutil.matchfiles(repo, files or [])
1784 editform = mergeeditform(repo[None], b'import.normal')
1792 editform = mergeeditform(repo[None], b'import.normal')
1785 if opts.get(b'exact'):
1793 if opts.get(b'exact'):
1786 editor = None
1794 editor = None
1787 else:
1795 else:
1788 editor = getcommiteditor(
1796 editor = getcommiteditor(
1789 editform=editform, **pycompat.strkwargs(opts)
1797 editform=editform, **pycompat.strkwargs(opts)
1790 )
1798 )
1791 extra = {}
1799 extra = {}
1792 for idfunc in extrapreimport:
1800 for idfunc in extrapreimport:
1793 extrapreimportmap[idfunc](repo, patchdata, extra, opts)
1801 extrapreimportmap[idfunc](repo, patchdata, extra, opts)
1794 overrides = {}
1802 overrides = {}
1795 if partial:
1803 if partial:
1796 overrides[(b'ui', b'allowemptycommit')] = True
1804 overrides[(b'ui', b'allowemptycommit')] = True
1797 if opts.get(b'secret'):
1805 if opts.get(b'secret'):
1798 overrides[(b'phases', b'new-commit')] = b'secret'
1806 overrides[(b'phases', b'new-commit')] = b'secret'
1799 with repo.ui.configoverride(overrides, b'import'):
1807 with repo.ui.configoverride(overrides, b'import'):
1800 n = repo.commit(
1808 n = repo.commit(
1801 message, user, date, match=m, editor=editor, extra=extra
1809 message, user, date, match=m, editor=editor, extra=extra
1802 )
1810 )
1803 for idfunc in extrapostimport:
1811 for idfunc in extrapostimport:
1804 extrapostimportmap[idfunc](repo[n])
1812 extrapostimportmap[idfunc](repo[n])
1805 else:
1813 else:
1806 if opts.get(b'exact') or importbranch:
1814 if opts.get(b'exact') or importbranch:
1807 branch = branch or b'default'
1815 branch = branch or b'default'
1808 else:
1816 else:
1809 branch = p1.branch()
1817 branch = p1.branch()
1810 store = patch.filestore()
1818 store = patch.filestore()
1811 try:
1819 try:
1812 files = set()
1820 files = set()
1813 try:
1821 try:
1814 patch.patchrepo(
1822 patch.patchrepo(
1815 ui,
1823 ui,
1816 repo,
1824 repo,
1817 p1,
1825 p1,
1818 store,
1826 store,
1819 tmpname,
1827 tmpname,
1820 strip,
1828 strip,
1821 prefix,
1829 prefix,
1822 files,
1830 files,
1823 eolmode=None,
1831 eolmode=None,
1824 )
1832 )
1825 except error.PatchError as e:
1833 except error.PatchError as e:
1826 raise error.Abort(stringutil.forcebytestr(e))
1834 raise error.Abort(stringutil.forcebytestr(e))
1827 if opts.get(b'exact'):
1835 if opts.get(b'exact'):
1828 editor = None
1836 editor = None
1829 else:
1837 else:
1830 editor = getcommiteditor(editform=b'import.bypass')
1838 editor = getcommiteditor(editform=b'import.bypass')
1831 memctx = context.memctx(
1839 memctx = context.memctx(
1832 repo,
1840 repo,
1833 (p1.node(), p2.node()),
1841 (p1.node(), p2.node()),
1834 message,
1842 message,
1835 files=files,
1843 files=files,
1836 filectxfn=store,
1844 filectxfn=store,
1837 user=user,
1845 user=user,
1838 date=date,
1846 date=date,
1839 branch=branch,
1847 branch=branch,
1840 editor=editor,
1848 editor=editor,
1841 )
1849 )
1842 n = memctx.commit()
1850 n = memctx.commit()
1843 finally:
1851 finally:
1844 store.close()
1852 store.close()
1845 if opts.get(b'exact') and nocommit:
1853 if opts.get(b'exact') and nocommit:
1846 # --exact with --no-commit is still useful in that it does merge
1854 # --exact with --no-commit is still useful in that it does merge
1847 # and branch bits
1855 # and branch bits
1848 ui.warn(_(b"warning: can't check exact import with --no-commit\n"))
1856 ui.warn(_(b"warning: can't check exact import with --no-commit\n"))
1849 elif opts.get(b'exact') and (not n or hex(n) != nodeid):
1857 elif opts.get(b'exact') and (not n or hex(n) != nodeid):
1850 raise error.Abort(_(b'patch is damaged or loses information'))
1858 raise error.Abort(_(b'patch is damaged or loses information'))
1851 msg = _(b'applied to working directory')
1859 msg = _(b'applied to working directory')
1852 if n:
1860 if n:
1853 # i18n: refers to a short changeset id
1861 # i18n: refers to a short changeset id
1854 msg = _(b'created %s') % short(n)
1862 msg = _(b'created %s') % short(n)
1855 return msg, n, rejects
1863 return msg, n, rejects
1856
1864
1857
1865
1858 # facility to let extensions include additional data in an exported patch
1866 # facility to let extensions include additional data in an exported patch
1859 # list of identifiers to be executed in order
1867 # list of identifiers to be executed in order
1860 extraexport = []
1868 extraexport = []
1861 # mapping from identifier to actual export function
1869 # mapping from identifier to actual export function
1862 # function as to return a string to be added to the header or None
1870 # function as to return a string to be added to the header or None
1863 # it is given two arguments (sequencenumber, changectx)
1871 # it is given two arguments (sequencenumber, changectx)
1864 extraexportmap = {}
1872 extraexportmap = {}
1865
1873
1866
1874
1867 def _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts):
1875 def _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts):
1868 node = scmutil.binnode(ctx)
1876 node = scmutil.binnode(ctx)
1869 parents = [p.node() for p in ctx.parents() if p]
1877 parents = [p.node() for p in ctx.parents() if p]
1870 branch = ctx.branch()
1878 branch = ctx.branch()
1871 if switch_parent:
1879 if switch_parent:
1872 parents.reverse()
1880 parents.reverse()
1873
1881
1874 if parents:
1882 if parents:
1875 prev = parents[0]
1883 prev = parents[0]
1876 else:
1884 else:
1877 prev = nullid
1885 prev = nullid
1878
1886
1879 fm.context(ctx=ctx)
1887 fm.context(ctx=ctx)
1880 fm.plain(b'# HG changeset patch\n')
1888 fm.plain(b'# HG changeset patch\n')
1881 fm.write(b'user', b'# User %s\n', ctx.user())
1889 fm.write(b'user', b'# User %s\n', ctx.user())
1882 fm.plain(b'# Date %d %d\n' % ctx.date())
1890 fm.plain(b'# Date %d %d\n' % ctx.date())
1883 fm.write(b'date', b'# %s\n', fm.formatdate(ctx.date()))
1891 fm.write(b'date', b'# %s\n', fm.formatdate(ctx.date()))
1884 fm.condwrite(
1892 fm.condwrite(
1885 branch and branch != b'default', b'branch', b'# Branch %s\n', branch
1893 branch and branch != b'default', b'branch', b'# Branch %s\n', branch
1886 )
1894 )
1887 fm.write(b'node', b'# Node ID %s\n', hex(node))
1895 fm.write(b'node', b'# Node ID %s\n', hex(node))
1888 fm.plain(b'# Parent %s\n' % hex(prev))
1896 fm.plain(b'# Parent %s\n' % hex(prev))
1889 if len(parents) > 1:
1897 if len(parents) > 1:
1890 fm.plain(b'# Parent %s\n' % hex(parents[1]))
1898 fm.plain(b'# Parent %s\n' % hex(parents[1]))
1891 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name=b'node'))
1899 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name=b'node'))
1892
1900
1893 # TODO: redesign extraexportmap function to support formatter
1901 # TODO: redesign extraexportmap function to support formatter
1894 for headerid in extraexport:
1902 for headerid in extraexport:
1895 header = extraexportmap[headerid](seqno, ctx)
1903 header = extraexportmap[headerid](seqno, ctx)
1896 if header is not None:
1904 if header is not None:
1897 fm.plain(b'# %s\n' % header)
1905 fm.plain(b'# %s\n' % header)
1898
1906
1899 fm.write(b'desc', b'%s\n', ctx.description().rstrip())
1907 fm.write(b'desc', b'%s\n', ctx.description().rstrip())
1900 fm.plain(b'\n')
1908 fm.plain(b'\n')
1901
1909
1902 if fm.isplain():
1910 if fm.isplain():
1903 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts)
1911 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts)
1904 for chunk, label in chunkiter:
1912 for chunk, label in chunkiter:
1905 fm.plain(chunk, label=label)
1913 fm.plain(chunk, label=label)
1906 else:
1914 else:
1907 chunkiter = patch.diff(repo, prev, node, match, opts=diffopts)
1915 chunkiter = patch.diff(repo, prev, node, match, opts=diffopts)
1908 # TODO: make it structured?
1916 # TODO: make it structured?
1909 fm.data(diff=b''.join(chunkiter))
1917 fm.data(diff=b''.join(chunkiter))
1910
1918
1911
1919
1912 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match):
1920 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match):
1913 """Export changesets to stdout or a single file"""
1921 """Export changesets to stdout or a single file"""
1914 for seqno, rev in enumerate(revs, 1):
1922 for seqno, rev in enumerate(revs, 1):
1915 ctx = repo[rev]
1923 ctx = repo[rev]
1916 if not dest.startswith(b'<'):
1924 if not dest.startswith(b'<'):
1917 repo.ui.note(b"%s\n" % dest)
1925 repo.ui.note(b"%s\n" % dest)
1918 fm.startitem()
1926 fm.startitem()
1919 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts)
1927 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts)
1920
1928
1921
1929
1922 def _exportfntemplate(
1930 def _exportfntemplate(
1923 repo, revs, basefm, fntemplate, switch_parent, diffopts, match
1931 repo, revs, basefm, fntemplate, switch_parent, diffopts, match
1924 ):
1932 ):
1925 """Export changesets to possibly multiple files"""
1933 """Export changesets to possibly multiple files"""
1926 total = len(revs)
1934 total = len(revs)
1927 revwidth = max(len(str(rev)) for rev in revs)
1935 revwidth = max(len(str(rev)) for rev in revs)
1928 filemap = util.sortdict() # filename: [(seqno, rev), ...]
1936 filemap = util.sortdict() # filename: [(seqno, rev), ...]
1929
1937
1930 for seqno, rev in enumerate(revs, 1):
1938 for seqno, rev in enumerate(revs, 1):
1931 ctx = repo[rev]
1939 ctx = repo[rev]
1932 dest = makefilename(
1940 dest = makefilename(
1933 ctx, fntemplate, total=total, seqno=seqno, revwidth=revwidth
1941 ctx, fntemplate, total=total, seqno=seqno, revwidth=revwidth
1934 )
1942 )
1935 filemap.setdefault(dest, []).append((seqno, rev))
1943 filemap.setdefault(dest, []).append((seqno, rev))
1936
1944
1937 for dest in filemap:
1945 for dest in filemap:
1938 with formatter.maybereopen(basefm, dest) as fm:
1946 with formatter.maybereopen(basefm, dest) as fm:
1939 repo.ui.note(b"%s\n" % dest)
1947 repo.ui.note(b"%s\n" % dest)
1940 for seqno, rev in filemap[dest]:
1948 for seqno, rev in filemap[dest]:
1941 fm.startitem()
1949 fm.startitem()
1942 ctx = repo[rev]
1950 ctx = repo[rev]
1943 _exportsingle(
1951 _exportsingle(
1944 repo, ctx, fm, match, switch_parent, seqno, diffopts
1952 repo, ctx, fm, match, switch_parent, seqno, diffopts
1945 )
1953 )
1946
1954
1947
1955
1948 def _prefetchchangedfiles(repo, revs, match):
1956 def _prefetchchangedfiles(repo, revs, match):
1949 allfiles = set()
1957 allfiles = set()
1950 for rev in revs:
1958 for rev in revs:
1951 for file in repo[rev].files():
1959 for file in repo[rev].files():
1952 if not match or match(file):
1960 if not match or match(file):
1953 allfiles.add(file)
1961 allfiles.add(file)
1954 scmutil.prefetchfiles(repo, revs, scmutil.matchfiles(repo, allfiles))
1962 scmutil.prefetchfiles(repo, revs, scmutil.matchfiles(repo, allfiles))
1955
1963
1956
1964
1957 def export(
1965 def export(
1958 repo,
1966 repo,
1959 revs,
1967 revs,
1960 basefm,
1968 basefm,
1961 fntemplate=b'hg-%h.patch',
1969 fntemplate=b'hg-%h.patch',
1962 switch_parent=False,
1970 switch_parent=False,
1963 opts=None,
1971 opts=None,
1964 match=None,
1972 match=None,
1965 ):
1973 ):
1966 '''export changesets as hg patches
1974 '''export changesets as hg patches
1967
1975
1968 Args:
1976 Args:
1969 repo: The repository from which we're exporting revisions.
1977 repo: The repository from which we're exporting revisions.
1970 revs: A list of revisions to export as revision numbers.
1978 revs: A list of revisions to export as revision numbers.
1971 basefm: A formatter to which patches should be written.
1979 basefm: A formatter to which patches should be written.
1972 fntemplate: An optional string to use for generating patch file names.
1980 fntemplate: An optional string to use for generating patch file names.
1973 switch_parent: If True, show diffs against second parent when not nullid.
1981 switch_parent: If True, show diffs against second parent when not nullid.
1974 Default is false, which always shows diff against p1.
1982 Default is false, which always shows diff against p1.
1975 opts: diff options to use for generating the patch.
1983 opts: diff options to use for generating the patch.
1976 match: If specified, only export changes to files matching this matcher.
1984 match: If specified, only export changes to files matching this matcher.
1977
1985
1978 Returns:
1986 Returns:
1979 Nothing.
1987 Nothing.
1980
1988
1981 Side Effect:
1989 Side Effect:
1982 "HG Changeset Patch" data is emitted to one of the following
1990 "HG Changeset Patch" data is emitted to one of the following
1983 destinations:
1991 destinations:
1984 fntemplate specified: Each rev is written to a unique file named using
1992 fntemplate specified: Each rev is written to a unique file named using
1985 the given template.
1993 the given template.
1986 Otherwise: All revs will be written to basefm.
1994 Otherwise: All revs will be written to basefm.
1987 '''
1995 '''
1988 _prefetchchangedfiles(repo, revs, match)
1996 _prefetchchangedfiles(repo, revs, match)
1989
1997
1990 if not fntemplate:
1998 if not fntemplate:
1991 _exportfile(
1999 _exportfile(
1992 repo, revs, basefm, b'<unnamed>', switch_parent, opts, match
2000 repo, revs, basefm, b'<unnamed>', switch_parent, opts, match
1993 )
2001 )
1994 else:
2002 else:
1995 _exportfntemplate(
2003 _exportfntemplate(
1996 repo, revs, basefm, fntemplate, switch_parent, opts, match
2004 repo, revs, basefm, fntemplate, switch_parent, opts, match
1997 )
2005 )
1998
2006
1999
2007
2000 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None):
2008 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None):
2001 """Export changesets to the given file stream"""
2009 """Export changesets to the given file stream"""
2002 _prefetchchangedfiles(repo, revs, match)
2010 _prefetchchangedfiles(repo, revs, match)
2003
2011
2004 dest = getattr(fp, 'name', b'<unnamed>')
2012 dest = getattr(fp, 'name', b'<unnamed>')
2005 with formatter.formatter(repo.ui, fp, b'export', {}) as fm:
2013 with formatter.formatter(repo.ui, fp, b'export', {}) as fm:
2006 _exportfile(repo, revs, fm, dest, switch_parent, opts, match)
2014 _exportfile(repo, revs, fm, dest, switch_parent, opts, match)
2007
2015
2008
2016
2009 def showmarker(fm, marker, index=None):
2017 def showmarker(fm, marker, index=None):
2010 """utility function to display obsolescence marker in a readable way
2018 """utility function to display obsolescence marker in a readable way
2011
2019
2012 To be used by debug function."""
2020 To be used by debug function."""
2013 if index is not None:
2021 if index is not None:
2014 fm.write(b'index', b'%i ', index)
2022 fm.write(b'index', b'%i ', index)
2015 fm.write(b'prednode', b'%s ', hex(marker.prednode()))
2023 fm.write(b'prednode', b'%s ', hex(marker.prednode()))
2016 succs = marker.succnodes()
2024 succs = marker.succnodes()
2017 fm.condwrite(
2025 fm.condwrite(
2018 succs,
2026 succs,
2019 b'succnodes',
2027 b'succnodes',
2020 b'%s ',
2028 b'%s ',
2021 fm.formatlist(map(hex, succs), name=b'node'),
2029 fm.formatlist(map(hex, succs), name=b'node'),
2022 )
2030 )
2023 fm.write(b'flag', b'%X ', marker.flags())
2031 fm.write(b'flag', b'%X ', marker.flags())
2024 parents = marker.parentnodes()
2032 parents = marker.parentnodes()
2025 if parents is not None:
2033 if parents is not None:
2026 fm.write(
2034 fm.write(
2027 b'parentnodes',
2035 b'parentnodes',
2028 b'{%s} ',
2036 b'{%s} ',
2029 fm.formatlist(map(hex, parents), name=b'node', sep=b', '),
2037 fm.formatlist(map(hex, parents), name=b'node', sep=b', '),
2030 )
2038 )
2031 fm.write(b'date', b'(%s) ', fm.formatdate(marker.date()))
2039 fm.write(b'date', b'(%s) ', fm.formatdate(marker.date()))
2032 meta = marker.metadata().copy()
2040 meta = marker.metadata().copy()
2033 meta.pop(b'date', None)
2041 meta.pop(b'date', None)
2034 smeta = pycompat.rapply(pycompat.maybebytestr, meta)
2042 smeta = pycompat.rapply(pycompat.maybebytestr, meta)
2035 fm.write(
2043 fm.write(
2036 b'metadata', b'{%s}', fm.formatdict(smeta, fmt=b'%r: %r', sep=b', ')
2044 b'metadata', b'{%s}', fm.formatdict(smeta, fmt=b'%r: %r', sep=b', ')
2037 )
2045 )
2038 fm.plain(b'\n')
2046 fm.plain(b'\n')
2039
2047
2040
2048
2041 def finddate(ui, repo, date):
2049 def finddate(ui, repo, date):
2042 """Find the tipmost changeset that matches the given date spec"""
2050 """Find the tipmost changeset that matches the given date spec"""
2043
2051
2044 df = dateutil.matchdate(date)
2052 df = dateutil.matchdate(date)
2045 m = scmutil.matchall(repo)
2053 m = scmutil.matchall(repo)
2046 results = {}
2054 results = {}
2047
2055
2048 def prep(ctx, fns):
2056 def prep(ctx, fns):
2049 d = ctx.date()
2057 d = ctx.date()
2050 if df(d[0]):
2058 if df(d[0]):
2051 results[ctx.rev()] = d
2059 results[ctx.rev()] = d
2052
2060
2053 for ctx in walkchangerevs(repo, m, {b'rev': None}, prep):
2061 for ctx in walkchangerevs(repo, m, {b'rev': None}, prep):
2054 rev = ctx.rev()
2062 rev = ctx.rev()
2055 if rev in results:
2063 if rev in results:
2056 ui.status(
2064 ui.status(
2057 _(b"found revision %d from %s\n")
2065 _(b"found revision %d from %s\n")
2058 % (rev, dateutil.datestr(results[rev]))
2066 % (rev, dateutil.datestr(results[rev]))
2059 )
2067 )
2060 return b'%d' % rev
2068 return b'%d' % rev
2061
2069
2062 raise error.Abort(_(b"revision matching date not found"))
2070 raise error.Abort(_(b"revision matching date not found"))
2063
2071
2064
2072
2065 def increasingwindows(windowsize=8, sizelimit=512):
2073 def increasingwindows(windowsize=8, sizelimit=512):
2066 while True:
2074 while True:
2067 yield windowsize
2075 yield windowsize
2068 if windowsize < sizelimit:
2076 if windowsize < sizelimit:
2069 windowsize *= 2
2077 windowsize *= 2
2070
2078
2071
2079
2072 def _walkrevs(repo, opts):
2080 def _walkrevs(repo, opts):
2073 # Default --rev value depends on --follow but --follow behavior
2081 # Default --rev value depends on --follow but --follow behavior
2074 # depends on revisions resolved from --rev...
2082 # depends on revisions resolved from --rev...
2075 follow = opts.get(b'follow') or opts.get(b'follow_first')
2083 follow = opts.get(b'follow') or opts.get(b'follow_first')
2076 if opts.get(b'rev'):
2084 if opts.get(b'rev'):
2077 revs = scmutil.revrange(repo, opts[b'rev'])
2085 revs = scmutil.revrange(repo, opts[b'rev'])
2078 elif follow and repo.dirstate.p1() == nullid:
2086 elif follow and repo.dirstate.p1() == nullid:
2079 revs = smartset.baseset()
2087 revs = smartset.baseset()
2080 elif follow:
2088 elif follow:
2081 revs = repo.revs(b'reverse(:.)')
2089 revs = repo.revs(b'reverse(:.)')
2082 else:
2090 else:
2083 revs = smartset.spanset(repo)
2091 revs = smartset.spanset(repo)
2084 revs.reverse()
2092 revs.reverse()
2085 return revs
2093 return revs
2086
2094
2087
2095
2088 class FileWalkError(Exception):
2096 class FileWalkError(Exception):
2089 pass
2097 pass
2090
2098
2091
2099
2092 def walkfilerevs(repo, match, follow, revs, fncache):
2100 def walkfilerevs(repo, match, follow, revs, fncache):
2093 '''Walks the file history for the matched files.
2101 '''Walks the file history for the matched files.
2094
2102
2095 Returns the changeset revs that are involved in the file history.
2103 Returns the changeset revs that are involved in the file history.
2096
2104
2097 Throws FileWalkError if the file history can't be walked using
2105 Throws FileWalkError if the file history can't be walked using
2098 filelogs alone.
2106 filelogs alone.
2099 '''
2107 '''
2100 wanted = set()
2108 wanted = set()
2101 copies = []
2109 copies = []
2102 minrev, maxrev = min(revs), max(revs)
2110 minrev, maxrev = min(revs), max(revs)
2103
2111
2104 def filerevs(filelog, last):
2112 def filerevs(filelog, last):
2105 """
2113 """
2106 Only files, no patterns. Check the history of each file.
2114 Only files, no patterns. Check the history of each file.
2107
2115
2108 Examines filelog entries within minrev, maxrev linkrev range
2116 Examines filelog entries within minrev, maxrev linkrev range
2109 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
2117 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
2110 tuples in backwards order
2118 tuples in backwards order
2111 """
2119 """
2112 cl_count = len(repo)
2120 cl_count = len(repo)
2113 revs = []
2121 revs = []
2114 for j in pycompat.xrange(0, last + 1):
2122 for j in pycompat.xrange(0, last + 1):
2115 linkrev = filelog.linkrev(j)
2123 linkrev = filelog.linkrev(j)
2116 if linkrev < minrev:
2124 if linkrev < minrev:
2117 continue
2125 continue
2118 # only yield rev for which we have the changelog, it can
2126 # only yield rev for which we have the changelog, it can
2119 # happen while doing "hg log" during a pull or commit
2127 # happen while doing "hg log" during a pull or commit
2120 if linkrev >= cl_count:
2128 if linkrev >= cl_count:
2121 break
2129 break
2122
2130
2123 parentlinkrevs = []
2131 parentlinkrevs = []
2124 for p in filelog.parentrevs(j):
2132 for p in filelog.parentrevs(j):
2125 if p != nullrev:
2133 if p != nullrev:
2126 parentlinkrevs.append(filelog.linkrev(p))
2134 parentlinkrevs.append(filelog.linkrev(p))
2127 n = filelog.node(j)
2135 n = filelog.node(j)
2128 revs.append(
2136 revs.append(
2129 (linkrev, parentlinkrevs, follow and filelog.renamed(n))
2137 (linkrev, parentlinkrevs, follow and filelog.renamed(n))
2130 )
2138 )
2131
2139
2132 return reversed(revs)
2140 return reversed(revs)
2133
2141
2134 def iterfiles():
2142 def iterfiles():
2135 pctx = repo[b'.']
2143 pctx = repo[b'.']
2136 for filename in match.files():
2144 for filename in match.files():
2137 if follow:
2145 if follow:
2138 if filename not in pctx:
2146 if filename not in pctx:
2139 raise error.Abort(
2147 raise error.Abort(
2140 _(
2148 _(
2141 b'cannot follow file not in parent '
2149 b'cannot follow file not in parent '
2142 b'revision: "%s"'
2150 b'revision: "%s"'
2143 )
2151 )
2144 % filename
2152 % filename
2145 )
2153 )
2146 yield filename, pctx[filename].filenode()
2154 yield filename, pctx[filename].filenode()
2147 else:
2155 else:
2148 yield filename, None
2156 yield filename, None
2149 for filename_node in copies:
2157 for filename_node in copies:
2150 yield filename_node
2158 yield filename_node
2151
2159
2152 for file_, node in iterfiles():
2160 for file_, node in iterfiles():
2153 filelog = repo.file(file_)
2161 filelog = repo.file(file_)
2154 if not len(filelog):
2162 if not len(filelog):
2155 if node is None:
2163 if node is None:
2156 # A zero count may be a directory or deleted file, so
2164 # A zero count may be a directory or deleted file, so
2157 # try to find matching entries on the slow path.
2165 # try to find matching entries on the slow path.
2158 if follow:
2166 if follow:
2159 raise error.Abort(
2167 raise error.Abort(
2160 _(b'cannot follow nonexistent file: "%s"') % file_
2168 _(b'cannot follow nonexistent file: "%s"') % file_
2161 )
2169 )
2162 raise FileWalkError(b"Cannot walk via filelog")
2170 raise FileWalkError(b"Cannot walk via filelog")
2163 else:
2171 else:
2164 continue
2172 continue
2165
2173
2166 if node is None:
2174 if node is None:
2167 last = len(filelog) - 1
2175 last = len(filelog) - 1
2168 else:
2176 else:
2169 last = filelog.rev(node)
2177 last = filelog.rev(node)
2170
2178
2171 # keep track of all ancestors of the file
2179 # keep track of all ancestors of the file
2172 ancestors = {filelog.linkrev(last)}
2180 ancestors = {filelog.linkrev(last)}
2173
2181
2174 # iterate from latest to oldest revision
2182 # iterate from latest to oldest revision
2175 for rev, flparentlinkrevs, copied in filerevs(filelog, last):
2183 for rev, flparentlinkrevs, copied in filerevs(filelog, last):
2176 if not follow:
2184 if not follow:
2177 if rev > maxrev:
2185 if rev > maxrev:
2178 continue
2186 continue
2179 else:
2187 else:
2180 # Note that last might not be the first interesting
2188 # Note that last might not be the first interesting
2181 # rev to us:
2189 # rev to us:
2182 # if the file has been changed after maxrev, we'll
2190 # if the file has been changed after maxrev, we'll
2183 # have linkrev(last) > maxrev, and we still need
2191 # have linkrev(last) > maxrev, and we still need
2184 # to explore the file graph
2192 # to explore the file graph
2185 if rev not in ancestors:
2193 if rev not in ancestors:
2186 continue
2194 continue
2187 # XXX insert 1327 fix here
2195 # XXX insert 1327 fix here
2188 if flparentlinkrevs:
2196 if flparentlinkrevs:
2189 ancestors.update(flparentlinkrevs)
2197 ancestors.update(flparentlinkrevs)
2190
2198
2191 fncache.setdefault(rev, []).append(file_)
2199 fncache.setdefault(rev, []).append(file_)
2192 wanted.add(rev)
2200 wanted.add(rev)
2193 if copied:
2201 if copied:
2194 copies.append(copied)
2202 copies.append(copied)
2195
2203
2196 return wanted
2204 return wanted
2197
2205
2198
2206
2199 class _followfilter(object):
2207 class _followfilter(object):
2200 def __init__(self, repo, onlyfirst=False):
2208 def __init__(self, repo, onlyfirst=False):
2201 self.repo = repo
2209 self.repo = repo
2202 self.startrev = nullrev
2210 self.startrev = nullrev
2203 self.roots = set()
2211 self.roots = set()
2204 self.onlyfirst = onlyfirst
2212 self.onlyfirst = onlyfirst
2205
2213
2206 def match(self, rev):
2214 def match(self, rev):
2207 def realparents(rev):
2215 def realparents(rev):
2208 if self.onlyfirst:
2216 if self.onlyfirst:
2209 return self.repo.changelog.parentrevs(rev)[0:1]
2217 return self.repo.changelog.parentrevs(rev)[0:1]
2210 else:
2218 else:
2211 return filter(
2219 return filter(
2212 lambda x: x != nullrev, self.repo.changelog.parentrevs(rev)
2220 lambda x: x != nullrev, self.repo.changelog.parentrevs(rev)
2213 )
2221 )
2214
2222
2215 if self.startrev == nullrev:
2223 if self.startrev == nullrev:
2216 self.startrev = rev
2224 self.startrev = rev
2217 return True
2225 return True
2218
2226
2219 if rev > self.startrev:
2227 if rev > self.startrev:
2220 # forward: all descendants
2228 # forward: all descendants
2221 if not self.roots:
2229 if not self.roots:
2222 self.roots.add(self.startrev)
2230 self.roots.add(self.startrev)
2223 for parent in realparents(rev):
2231 for parent in realparents(rev):
2224 if parent in self.roots:
2232 if parent in self.roots:
2225 self.roots.add(rev)
2233 self.roots.add(rev)
2226 return True
2234 return True
2227 else:
2235 else:
2228 # backwards: all parents
2236 # backwards: all parents
2229 if not self.roots:
2237 if not self.roots:
2230 self.roots.update(realparents(self.startrev))
2238 self.roots.update(realparents(self.startrev))
2231 if rev in self.roots:
2239 if rev in self.roots:
2232 self.roots.remove(rev)
2240 self.roots.remove(rev)
2233 self.roots.update(realparents(rev))
2241 self.roots.update(realparents(rev))
2234 return True
2242 return True
2235
2243
2236 return False
2244 return False
2237
2245
2238
2246
2239 def walkchangerevs(repo, match, opts, prepare):
2247 def walkchangerevs(repo, match, opts, prepare):
2240 '''Iterate over files and the revs in which they changed.
2248 '''Iterate over files and the revs in which they changed.
2241
2249
2242 Callers most commonly need to iterate backwards over the history
2250 Callers most commonly need to iterate backwards over the history
2243 in which they are interested. Doing so has awful (quadratic-looking)
2251 in which they are interested. Doing so has awful (quadratic-looking)
2244 performance, so we use iterators in a "windowed" way.
2252 performance, so we use iterators in a "windowed" way.
2245
2253
2246 We walk a window of revisions in the desired order. Within the
2254 We walk a window of revisions in the desired order. Within the
2247 window, we first walk forwards to gather data, then in the desired
2255 window, we first walk forwards to gather data, then in the desired
2248 order (usually backwards) to display it.
2256 order (usually backwards) to display it.
2249
2257
2250 This function returns an iterator yielding contexts. Before
2258 This function returns an iterator yielding contexts. Before
2251 yielding each context, the iterator will first call the prepare
2259 yielding each context, the iterator will first call the prepare
2252 function on each context in the window in forward order.'''
2260 function on each context in the window in forward order.'''
2253
2261
2254 allfiles = opts.get(b'all_files')
2262 allfiles = opts.get(b'all_files')
2255 follow = opts.get(b'follow') or opts.get(b'follow_first')
2263 follow = opts.get(b'follow') or opts.get(b'follow_first')
2256 revs = _walkrevs(repo, opts)
2264 revs = _walkrevs(repo, opts)
2257 if not revs:
2265 if not revs:
2258 return []
2266 return []
2259 wanted = set()
2267 wanted = set()
2260 slowpath = match.anypats() or (not match.always() and opts.get(b'removed'))
2268 slowpath = match.anypats() or (not match.always() and opts.get(b'removed'))
2261 fncache = {}
2269 fncache = {}
2262 change = repo.__getitem__
2270 change = repo.__getitem__
2263
2271
2264 # First step is to fill wanted, the set of revisions that we want to yield.
2272 # First step is to fill wanted, the set of revisions that we want to yield.
2265 # When it does not induce extra cost, we also fill fncache for revisions in
2273 # When it does not induce extra cost, we also fill fncache for revisions in
2266 # wanted: a cache of filenames that were changed (ctx.files()) and that
2274 # wanted: a cache of filenames that were changed (ctx.files()) and that
2267 # match the file filtering conditions.
2275 # match the file filtering conditions.
2268
2276
2269 if match.always() or allfiles:
2277 if match.always() or allfiles:
2270 # No files, no patterns. Display all revs.
2278 # No files, no patterns. Display all revs.
2271 wanted = revs
2279 wanted = revs
2272 elif not slowpath:
2280 elif not slowpath:
2273 # We only have to read through the filelog to find wanted revisions
2281 # We only have to read through the filelog to find wanted revisions
2274
2282
2275 try:
2283 try:
2276 wanted = walkfilerevs(repo, match, follow, revs, fncache)
2284 wanted = walkfilerevs(repo, match, follow, revs, fncache)
2277 except FileWalkError:
2285 except FileWalkError:
2278 slowpath = True
2286 slowpath = True
2279
2287
2280 # We decided to fall back to the slowpath because at least one
2288 # We decided to fall back to the slowpath because at least one
2281 # of the paths was not a file. Check to see if at least one of them
2289 # of the paths was not a file. Check to see if at least one of them
2282 # existed in history, otherwise simply return
2290 # existed in history, otherwise simply return
2283 for path in match.files():
2291 for path in match.files():
2284 if path == b'.' or path in repo.store:
2292 if path == b'.' or path in repo.store:
2285 break
2293 break
2286 else:
2294 else:
2287 return []
2295 return []
2288
2296
2289 if slowpath:
2297 if slowpath:
2290 # We have to read the changelog to match filenames against
2298 # We have to read the changelog to match filenames against
2291 # changed files
2299 # changed files
2292
2300
2293 if follow:
2301 if follow:
2294 raise error.Abort(
2302 raise error.Abort(
2295 _(b'can only follow copies/renames for explicit filenames')
2303 _(b'can only follow copies/renames for explicit filenames')
2296 )
2304 )
2297
2305
2298 # The slow path checks files modified in every changeset.
2306 # The slow path checks files modified in every changeset.
2299 # This is really slow on large repos, so compute the set lazily.
2307 # This is really slow on large repos, so compute the set lazily.
2300 class lazywantedset(object):
2308 class lazywantedset(object):
2301 def __init__(self):
2309 def __init__(self):
2302 self.set = set()
2310 self.set = set()
2303 self.revs = set(revs)
2311 self.revs = set(revs)
2304
2312
2305 # No need to worry about locality here because it will be accessed
2313 # No need to worry about locality here because it will be accessed
2306 # in the same order as the increasing window below.
2314 # in the same order as the increasing window below.
2307 def __contains__(self, value):
2315 def __contains__(self, value):
2308 if value in self.set:
2316 if value in self.set:
2309 return True
2317 return True
2310 elif not value in self.revs:
2318 elif not value in self.revs:
2311 return False
2319 return False
2312 else:
2320 else:
2313 self.revs.discard(value)
2321 self.revs.discard(value)
2314 ctx = change(value)
2322 ctx = change(value)
2315 if allfiles:
2323 if allfiles:
2316 matches = list(ctx.manifest().walk(match))
2324 matches = list(ctx.manifest().walk(match))
2317 else:
2325 else:
2318 matches = [f for f in ctx.files() if match(f)]
2326 matches = [f for f in ctx.files() if match(f)]
2319 if matches:
2327 if matches:
2320 fncache[value] = matches
2328 fncache[value] = matches
2321 self.set.add(value)
2329 self.set.add(value)
2322 return True
2330 return True
2323 return False
2331 return False
2324
2332
2325 def discard(self, value):
2333 def discard(self, value):
2326 self.revs.discard(value)
2334 self.revs.discard(value)
2327 self.set.discard(value)
2335 self.set.discard(value)
2328
2336
2329 wanted = lazywantedset()
2337 wanted = lazywantedset()
2330
2338
2331 # it might be worthwhile to do this in the iterator if the rev range
2339 # it might be worthwhile to do this in the iterator if the rev range
2332 # is descending and the prune args are all within that range
2340 # is descending and the prune args are all within that range
2333 for rev in opts.get(b'prune', ()):
2341 for rev in opts.get(b'prune', ()):
2334 rev = repo[rev].rev()
2342 rev = repo[rev].rev()
2335 ff = _followfilter(repo)
2343 ff = _followfilter(repo)
2336 stop = min(revs[0], revs[-1])
2344 stop = min(revs[0], revs[-1])
2337 for x in pycompat.xrange(rev, stop - 1, -1):
2345 for x in pycompat.xrange(rev, stop - 1, -1):
2338 if ff.match(x):
2346 if ff.match(x):
2339 wanted = wanted - [x]
2347 wanted = wanted - [x]
2340
2348
2341 # Now that wanted is correctly initialized, we can iterate over the
2349 # Now that wanted is correctly initialized, we can iterate over the
2342 # revision range, yielding only revisions in wanted.
2350 # revision range, yielding only revisions in wanted.
2343 def iterate():
2351 def iterate():
2344 if follow and match.always():
2352 if follow and match.always():
2345 ff = _followfilter(repo, onlyfirst=opts.get(b'follow_first'))
2353 ff = _followfilter(repo, onlyfirst=opts.get(b'follow_first'))
2346
2354
2347 def want(rev):
2355 def want(rev):
2348 return ff.match(rev) and rev in wanted
2356 return ff.match(rev) and rev in wanted
2349
2357
2350 else:
2358 else:
2351
2359
2352 def want(rev):
2360 def want(rev):
2353 return rev in wanted
2361 return rev in wanted
2354
2362
2355 it = iter(revs)
2363 it = iter(revs)
2356 stopiteration = False
2364 stopiteration = False
2357 for windowsize in increasingwindows():
2365 for windowsize in increasingwindows():
2358 nrevs = []
2366 nrevs = []
2359 for i in pycompat.xrange(windowsize):
2367 for i in pycompat.xrange(windowsize):
2360 rev = next(it, None)
2368 rev = next(it, None)
2361 if rev is None:
2369 if rev is None:
2362 stopiteration = True
2370 stopiteration = True
2363 break
2371 break
2364 elif want(rev):
2372 elif want(rev):
2365 nrevs.append(rev)
2373 nrevs.append(rev)
2366 for rev in sorted(nrevs):
2374 for rev in sorted(nrevs):
2367 fns = fncache.get(rev)
2375 fns = fncache.get(rev)
2368 ctx = change(rev)
2376 ctx = change(rev)
2369 if not fns:
2377 if not fns:
2370
2378
2371 def fns_generator():
2379 def fns_generator():
2372 if allfiles:
2380 if allfiles:
2373 fiter = iter(ctx)
2381 fiter = iter(ctx)
2374 else:
2382 else:
2375 fiter = ctx.files()
2383 fiter = ctx.files()
2376 for f in fiter:
2384 for f in fiter:
2377 if match(f):
2385 if match(f):
2378 yield f
2386 yield f
2379
2387
2380 fns = fns_generator()
2388 fns = fns_generator()
2381 prepare(ctx, fns)
2389 prepare(ctx, fns)
2382 for rev in nrevs:
2390 for rev in nrevs:
2383 yield change(rev)
2391 yield change(rev)
2384
2392
2385 if stopiteration:
2393 if stopiteration:
2386 break
2394 break
2387
2395
2388 return iterate()
2396 return iterate()
2389
2397
2390
2398
2391 def add(ui, repo, match, prefix, uipathfn, explicitonly, **opts):
2399 def add(ui, repo, match, prefix, uipathfn, explicitonly, **opts):
2392 bad = []
2400 bad = []
2393
2401
2394 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2402 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2395 names = []
2403 names = []
2396 wctx = repo[None]
2404 wctx = repo[None]
2397 cca = None
2405 cca = None
2398 abort, warn = scmutil.checkportabilityalert(ui)
2406 abort, warn = scmutil.checkportabilityalert(ui)
2399 if abort or warn:
2407 if abort or warn:
2400 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2408 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2401
2409
2402 match = repo.narrowmatch(match, includeexact=True)
2410 match = repo.narrowmatch(match, includeexact=True)
2403 badmatch = matchmod.badmatch(match, badfn)
2411 badmatch = matchmod.badmatch(match, badfn)
2404 dirstate = repo.dirstate
2412 dirstate = repo.dirstate
2405 # We don't want to just call wctx.walk here, since it would return a lot of
2413 # We don't want to just call wctx.walk here, since it would return a lot of
2406 # clean files, which we aren't interested in and takes time.
2414 # clean files, which we aren't interested in and takes time.
2407 for f in sorted(
2415 for f in sorted(
2408 dirstate.walk(
2416 dirstate.walk(
2409 badmatch,
2417 badmatch,
2410 subrepos=sorted(wctx.substate),
2418 subrepos=sorted(wctx.substate),
2411 unknown=True,
2419 unknown=True,
2412 ignored=False,
2420 ignored=False,
2413 full=False,
2421 full=False,
2414 )
2422 )
2415 ):
2423 ):
2416 exact = match.exact(f)
2424 exact = match.exact(f)
2417 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2425 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2418 if cca:
2426 if cca:
2419 cca(f)
2427 cca(f)
2420 names.append(f)
2428 names.append(f)
2421 if ui.verbose or not exact:
2429 if ui.verbose or not exact:
2422 ui.status(
2430 ui.status(
2423 _(b'adding %s\n') % uipathfn(f), label=b'ui.addremove.added'
2431 _(b'adding %s\n') % uipathfn(f), label=b'ui.addremove.added'
2424 )
2432 )
2425
2433
2426 for subpath in sorted(wctx.substate):
2434 for subpath in sorted(wctx.substate):
2427 sub = wctx.sub(subpath)
2435 sub = wctx.sub(subpath)
2428 try:
2436 try:
2429 submatch = matchmod.subdirmatcher(subpath, match)
2437 submatch = matchmod.subdirmatcher(subpath, match)
2430 subprefix = repo.wvfs.reljoin(prefix, subpath)
2438 subprefix = repo.wvfs.reljoin(prefix, subpath)
2431 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2439 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2432 if opts.get('subrepos'):
2440 if opts.get('subrepos'):
2433 bad.extend(
2441 bad.extend(
2434 sub.add(ui, submatch, subprefix, subuipathfn, False, **opts)
2442 sub.add(ui, submatch, subprefix, subuipathfn, False, **opts)
2435 )
2443 )
2436 else:
2444 else:
2437 bad.extend(
2445 bad.extend(
2438 sub.add(ui, submatch, subprefix, subuipathfn, True, **opts)
2446 sub.add(ui, submatch, subprefix, subuipathfn, True, **opts)
2439 )
2447 )
2440 except error.LookupError:
2448 except error.LookupError:
2441 ui.status(
2449 ui.status(
2442 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2450 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2443 )
2451 )
2444
2452
2445 if not opts.get('dry_run'):
2453 if not opts.get('dry_run'):
2446 rejected = wctx.add(names, prefix)
2454 rejected = wctx.add(names, prefix)
2447 bad.extend(f for f in rejected if f in match.files())
2455 bad.extend(f for f in rejected if f in match.files())
2448 return bad
2456 return bad
2449
2457
2450
2458
2451 def addwebdirpath(repo, serverpath, webconf):
2459 def addwebdirpath(repo, serverpath, webconf):
2452 webconf[serverpath] = repo.root
2460 webconf[serverpath] = repo.root
2453 repo.ui.debug(b'adding %s = %s\n' % (serverpath, repo.root))
2461 repo.ui.debug(b'adding %s = %s\n' % (serverpath, repo.root))
2454
2462
2455 for r in repo.revs(b'filelog("path:.hgsub")'):
2463 for r in repo.revs(b'filelog("path:.hgsub")'):
2456 ctx = repo[r]
2464 ctx = repo[r]
2457 for subpath in ctx.substate:
2465 for subpath in ctx.substate:
2458 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2466 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2459
2467
2460
2468
2461 def forget(
2469 def forget(
2462 ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
2470 ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
2463 ):
2471 ):
2464 if dryrun and interactive:
2472 if dryrun and interactive:
2465 raise error.Abort(_(b"cannot specify both --dry-run and --interactive"))
2473 raise error.Abort(_(b"cannot specify both --dry-run and --interactive"))
2466 bad = []
2474 bad = []
2467 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2475 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2468 wctx = repo[None]
2476 wctx = repo[None]
2469 forgot = []
2477 forgot = []
2470
2478
2471 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2479 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2472 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2480 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2473 if explicitonly:
2481 if explicitonly:
2474 forget = [f for f in forget if match.exact(f)]
2482 forget = [f for f in forget if match.exact(f)]
2475
2483
2476 for subpath in sorted(wctx.substate):
2484 for subpath in sorted(wctx.substate):
2477 sub = wctx.sub(subpath)
2485 sub = wctx.sub(subpath)
2478 submatch = matchmod.subdirmatcher(subpath, match)
2486 submatch = matchmod.subdirmatcher(subpath, match)
2479 subprefix = repo.wvfs.reljoin(prefix, subpath)
2487 subprefix = repo.wvfs.reljoin(prefix, subpath)
2480 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2488 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2481 try:
2489 try:
2482 subbad, subforgot = sub.forget(
2490 subbad, subforgot = sub.forget(
2483 submatch,
2491 submatch,
2484 subprefix,
2492 subprefix,
2485 subuipathfn,
2493 subuipathfn,
2486 dryrun=dryrun,
2494 dryrun=dryrun,
2487 interactive=interactive,
2495 interactive=interactive,
2488 )
2496 )
2489 bad.extend([subpath + b'/' + f for f in subbad])
2497 bad.extend([subpath + b'/' + f for f in subbad])
2490 forgot.extend([subpath + b'/' + f for f in subforgot])
2498 forgot.extend([subpath + b'/' + f for f in subforgot])
2491 except error.LookupError:
2499 except error.LookupError:
2492 ui.status(
2500 ui.status(
2493 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2501 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2494 )
2502 )
2495
2503
2496 if not explicitonly:
2504 if not explicitonly:
2497 for f in match.files():
2505 for f in match.files():
2498 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2506 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2499 if f not in forgot:
2507 if f not in forgot:
2500 if repo.wvfs.exists(f):
2508 if repo.wvfs.exists(f):
2501 # Don't complain if the exact case match wasn't given.
2509 # Don't complain if the exact case match wasn't given.
2502 # But don't do this until after checking 'forgot', so
2510 # But don't do this until after checking 'forgot', so
2503 # that subrepo files aren't normalized, and this op is
2511 # that subrepo files aren't normalized, and this op is
2504 # purely from data cached by the status walk above.
2512 # purely from data cached by the status walk above.
2505 if repo.dirstate.normalize(f) in repo.dirstate:
2513 if repo.dirstate.normalize(f) in repo.dirstate:
2506 continue
2514 continue
2507 ui.warn(
2515 ui.warn(
2508 _(
2516 _(
2509 b'not removing %s: '
2517 b'not removing %s: '
2510 b'file is already untracked\n'
2518 b'file is already untracked\n'
2511 )
2519 )
2512 % uipathfn(f)
2520 % uipathfn(f)
2513 )
2521 )
2514 bad.append(f)
2522 bad.append(f)
2515
2523
2516 if interactive:
2524 if interactive:
2517 responses = _(
2525 responses = _(
2518 b'[Ynsa?]'
2526 b'[Ynsa?]'
2519 b'$$ &Yes, forget this file'
2527 b'$$ &Yes, forget this file'
2520 b'$$ &No, skip this file'
2528 b'$$ &No, skip this file'
2521 b'$$ &Skip remaining files'
2529 b'$$ &Skip remaining files'
2522 b'$$ Include &all remaining files'
2530 b'$$ Include &all remaining files'
2523 b'$$ &? (display help)'
2531 b'$$ &? (display help)'
2524 )
2532 )
2525 for filename in forget[:]:
2533 for filename in forget[:]:
2526 r = ui.promptchoice(
2534 r = ui.promptchoice(
2527 _(b'forget %s %s') % (uipathfn(filename), responses)
2535 _(b'forget %s %s') % (uipathfn(filename), responses)
2528 )
2536 )
2529 if r == 4: # ?
2537 if r == 4: # ?
2530 while r == 4:
2538 while r == 4:
2531 for c, t in ui.extractchoices(responses)[1]:
2539 for c, t in ui.extractchoices(responses)[1]:
2532 ui.write(b'%s - %s\n' % (c, encoding.lower(t)))
2540 ui.write(b'%s - %s\n' % (c, encoding.lower(t)))
2533 r = ui.promptchoice(
2541 r = ui.promptchoice(
2534 _(b'forget %s %s') % (uipathfn(filename), responses)
2542 _(b'forget %s %s') % (uipathfn(filename), responses)
2535 )
2543 )
2536 if r == 0: # yes
2544 if r == 0: # yes
2537 continue
2545 continue
2538 elif r == 1: # no
2546 elif r == 1: # no
2539 forget.remove(filename)
2547 forget.remove(filename)
2540 elif r == 2: # Skip
2548 elif r == 2: # Skip
2541 fnindex = forget.index(filename)
2549 fnindex = forget.index(filename)
2542 del forget[fnindex:]
2550 del forget[fnindex:]
2543 break
2551 break
2544 elif r == 3: # All
2552 elif r == 3: # All
2545 break
2553 break
2546
2554
2547 for f in forget:
2555 for f in forget:
2548 if ui.verbose or not match.exact(f) or interactive:
2556 if ui.verbose or not match.exact(f) or interactive:
2549 ui.status(
2557 ui.status(
2550 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2558 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2551 )
2559 )
2552
2560
2553 if not dryrun:
2561 if not dryrun:
2554 rejected = wctx.forget(forget, prefix)
2562 rejected = wctx.forget(forget, prefix)
2555 bad.extend(f for f in rejected if f in match.files())
2563 bad.extend(f for f in rejected if f in match.files())
2556 forgot.extend(f for f in forget if f not in rejected)
2564 forgot.extend(f for f in forget if f not in rejected)
2557 return bad, forgot
2565 return bad, forgot
2558
2566
2559
2567
2560 def files(ui, ctx, m, uipathfn, fm, fmt, subrepos):
2568 def files(ui, ctx, m, uipathfn, fm, fmt, subrepos):
2561 ret = 1
2569 ret = 1
2562
2570
2563 needsfctx = ui.verbose or {b'size', b'flags'} & fm.datahint()
2571 needsfctx = ui.verbose or {b'size', b'flags'} & fm.datahint()
2564 for f in ctx.matches(m):
2572 for f in ctx.matches(m):
2565 fm.startitem()
2573 fm.startitem()
2566 fm.context(ctx=ctx)
2574 fm.context(ctx=ctx)
2567 if needsfctx:
2575 if needsfctx:
2568 fc = ctx[f]
2576 fc = ctx[f]
2569 fm.write(b'size flags', b'% 10d % 1s ', fc.size(), fc.flags())
2577 fm.write(b'size flags', b'% 10d % 1s ', fc.size(), fc.flags())
2570 fm.data(path=f)
2578 fm.data(path=f)
2571 fm.plain(fmt % uipathfn(f))
2579 fm.plain(fmt % uipathfn(f))
2572 ret = 0
2580 ret = 0
2573
2581
2574 for subpath in sorted(ctx.substate):
2582 for subpath in sorted(ctx.substate):
2575 submatch = matchmod.subdirmatcher(subpath, m)
2583 submatch = matchmod.subdirmatcher(subpath, m)
2576 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2584 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2577 if subrepos or m.exact(subpath) or any(submatch.files()):
2585 if subrepos or m.exact(subpath) or any(submatch.files()):
2578 sub = ctx.sub(subpath)
2586 sub = ctx.sub(subpath)
2579 try:
2587 try:
2580 recurse = m.exact(subpath) or subrepos
2588 recurse = m.exact(subpath) or subrepos
2581 if (
2589 if (
2582 sub.printfiles(ui, submatch, subuipathfn, fm, fmt, recurse)
2590 sub.printfiles(ui, submatch, subuipathfn, fm, fmt, recurse)
2583 == 0
2591 == 0
2584 ):
2592 ):
2585 ret = 0
2593 ret = 0
2586 except error.LookupError:
2594 except error.LookupError:
2587 ui.status(
2595 ui.status(
2588 _(b"skipping missing subrepository: %s\n")
2596 _(b"skipping missing subrepository: %s\n")
2589 % uipathfn(subpath)
2597 % uipathfn(subpath)
2590 )
2598 )
2591
2599
2592 return ret
2600 return ret
2593
2601
2594
2602
2595 def remove(
2603 def remove(
2596 ui, repo, m, prefix, uipathfn, after, force, subrepos, dryrun, warnings=None
2604 ui, repo, m, prefix, uipathfn, after, force, subrepos, dryrun, warnings=None
2597 ):
2605 ):
2598 ret = 0
2606 ret = 0
2599 s = repo.status(match=m, clean=True)
2607 s = repo.status(match=m, clean=True)
2600 modified, added, deleted, clean = s.modified, s.added, s.deleted, s.clean
2608 modified, added, deleted, clean = s.modified, s.added, s.deleted, s.clean
2601
2609
2602 wctx = repo[None]
2610 wctx = repo[None]
2603
2611
2604 if warnings is None:
2612 if warnings is None:
2605 warnings = []
2613 warnings = []
2606 warn = True
2614 warn = True
2607 else:
2615 else:
2608 warn = False
2616 warn = False
2609
2617
2610 subs = sorted(wctx.substate)
2618 subs = sorted(wctx.substate)
2611 progress = ui.makeprogress(
2619 progress = ui.makeprogress(
2612 _(b'searching'), total=len(subs), unit=_(b'subrepos')
2620 _(b'searching'), total=len(subs), unit=_(b'subrepos')
2613 )
2621 )
2614 for subpath in subs:
2622 for subpath in subs:
2615 submatch = matchmod.subdirmatcher(subpath, m)
2623 submatch = matchmod.subdirmatcher(subpath, m)
2616 subprefix = repo.wvfs.reljoin(prefix, subpath)
2624 subprefix = repo.wvfs.reljoin(prefix, subpath)
2617 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2625 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2618 if subrepos or m.exact(subpath) or any(submatch.files()):
2626 if subrepos or m.exact(subpath) or any(submatch.files()):
2619 progress.increment()
2627 progress.increment()
2620 sub = wctx.sub(subpath)
2628 sub = wctx.sub(subpath)
2621 try:
2629 try:
2622 if sub.removefiles(
2630 if sub.removefiles(
2623 submatch,
2631 submatch,
2624 subprefix,
2632 subprefix,
2625 subuipathfn,
2633 subuipathfn,
2626 after,
2634 after,
2627 force,
2635 force,
2628 subrepos,
2636 subrepos,
2629 dryrun,
2637 dryrun,
2630 warnings,
2638 warnings,
2631 ):
2639 ):
2632 ret = 1
2640 ret = 1
2633 except error.LookupError:
2641 except error.LookupError:
2634 warnings.append(
2642 warnings.append(
2635 _(b"skipping missing subrepository: %s\n")
2643 _(b"skipping missing subrepository: %s\n")
2636 % uipathfn(subpath)
2644 % uipathfn(subpath)
2637 )
2645 )
2638 progress.complete()
2646 progress.complete()
2639
2647
2640 # warn about failure to delete explicit files/dirs
2648 # warn about failure to delete explicit files/dirs
2641 deleteddirs = pathutil.dirs(deleted)
2649 deleteddirs = pathutil.dirs(deleted)
2642 files = m.files()
2650 files = m.files()
2643 progress = ui.makeprogress(
2651 progress = ui.makeprogress(
2644 _(b'deleting'), total=len(files), unit=_(b'files')
2652 _(b'deleting'), total=len(files), unit=_(b'files')
2645 )
2653 )
2646 for f in files:
2654 for f in files:
2647
2655
2648 def insubrepo():
2656 def insubrepo():
2649 for subpath in wctx.substate:
2657 for subpath in wctx.substate:
2650 if f.startswith(subpath + b'/'):
2658 if f.startswith(subpath + b'/'):
2651 return True
2659 return True
2652 return False
2660 return False
2653
2661
2654 progress.increment()
2662 progress.increment()
2655 isdir = f in deleteddirs or wctx.hasdir(f)
2663 isdir = f in deleteddirs or wctx.hasdir(f)
2656 if f in repo.dirstate or isdir or f == b'.' or insubrepo() or f in subs:
2664 if f in repo.dirstate or isdir or f == b'.' or insubrepo() or f in subs:
2657 continue
2665 continue
2658
2666
2659 if repo.wvfs.exists(f):
2667 if repo.wvfs.exists(f):
2660 if repo.wvfs.isdir(f):
2668 if repo.wvfs.isdir(f):
2661 warnings.append(
2669 warnings.append(
2662 _(b'not removing %s: no tracked files\n') % uipathfn(f)
2670 _(b'not removing %s: no tracked files\n') % uipathfn(f)
2663 )
2671 )
2664 else:
2672 else:
2665 warnings.append(
2673 warnings.append(
2666 _(b'not removing %s: file is untracked\n') % uipathfn(f)
2674 _(b'not removing %s: file is untracked\n') % uipathfn(f)
2667 )
2675 )
2668 # missing files will generate a warning elsewhere
2676 # missing files will generate a warning elsewhere
2669 ret = 1
2677 ret = 1
2670 progress.complete()
2678 progress.complete()
2671
2679
2672 if force:
2680 if force:
2673 list = modified + deleted + clean + added
2681 list = modified + deleted + clean + added
2674 elif after:
2682 elif after:
2675 list = deleted
2683 list = deleted
2676 remaining = modified + added + clean
2684 remaining = modified + added + clean
2677 progress = ui.makeprogress(
2685 progress = ui.makeprogress(
2678 _(b'skipping'), total=len(remaining), unit=_(b'files')
2686 _(b'skipping'), total=len(remaining), unit=_(b'files')
2679 )
2687 )
2680 for f in remaining:
2688 for f in remaining:
2681 progress.increment()
2689 progress.increment()
2682 if ui.verbose or (f in files):
2690 if ui.verbose or (f in files):
2683 warnings.append(
2691 warnings.append(
2684 _(b'not removing %s: file still exists\n') % uipathfn(f)
2692 _(b'not removing %s: file still exists\n') % uipathfn(f)
2685 )
2693 )
2686 ret = 1
2694 ret = 1
2687 progress.complete()
2695 progress.complete()
2688 else:
2696 else:
2689 list = deleted + clean
2697 list = deleted + clean
2690 progress = ui.makeprogress(
2698 progress = ui.makeprogress(
2691 _(b'skipping'), total=(len(modified) + len(added)), unit=_(b'files')
2699 _(b'skipping'), total=(len(modified) + len(added)), unit=_(b'files')
2692 )
2700 )
2693 for f in modified:
2701 for f in modified:
2694 progress.increment()
2702 progress.increment()
2695 warnings.append(
2703 warnings.append(
2696 _(
2704 _(
2697 b'not removing %s: file is modified (use -f'
2705 b'not removing %s: file is modified (use -f'
2698 b' to force removal)\n'
2706 b' to force removal)\n'
2699 )
2707 )
2700 % uipathfn(f)
2708 % uipathfn(f)
2701 )
2709 )
2702 ret = 1
2710 ret = 1
2703 for f in added:
2711 for f in added:
2704 progress.increment()
2712 progress.increment()
2705 warnings.append(
2713 warnings.append(
2706 _(
2714 _(
2707 b"not removing %s: file has been marked for add"
2715 b"not removing %s: file has been marked for add"
2708 b" (use 'hg forget' to undo add)\n"
2716 b" (use 'hg forget' to undo add)\n"
2709 )
2717 )
2710 % uipathfn(f)
2718 % uipathfn(f)
2711 )
2719 )
2712 ret = 1
2720 ret = 1
2713 progress.complete()
2721 progress.complete()
2714
2722
2715 list = sorted(list)
2723 list = sorted(list)
2716 progress = ui.makeprogress(
2724 progress = ui.makeprogress(
2717 _(b'deleting'), total=len(list), unit=_(b'files')
2725 _(b'deleting'), total=len(list), unit=_(b'files')
2718 )
2726 )
2719 for f in list:
2727 for f in list:
2720 if ui.verbose or not m.exact(f):
2728 if ui.verbose or not m.exact(f):
2721 progress.increment()
2729 progress.increment()
2722 ui.status(
2730 ui.status(
2723 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2731 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2724 )
2732 )
2725 progress.complete()
2733 progress.complete()
2726
2734
2727 if not dryrun:
2735 if not dryrun:
2728 with repo.wlock():
2736 with repo.wlock():
2729 if not after:
2737 if not after:
2730 for f in list:
2738 for f in list:
2731 if f in added:
2739 if f in added:
2732 continue # we never unlink added files on remove
2740 continue # we never unlink added files on remove
2733 rmdir = repo.ui.configbool(
2741 rmdir = repo.ui.configbool(
2734 b'experimental', b'removeemptydirs'
2742 b'experimental', b'removeemptydirs'
2735 )
2743 )
2736 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir)
2744 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir)
2737 repo[None].forget(list)
2745 repo[None].forget(list)
2738
2746
2739 if warn:
2747 if warn:
2740 for warning in warnings:
2748 for warning in warnings:
2741 ui.warn(warning)
2749 ui.warn(warning)
2742
2750
2743 return ret
2751 return ret
2744
2752
2745
2753
2746 def _catfmtneedsdata(fm):
2754 def _catfmtneedsdata(fm):
2747 return not fm.datahint() or b'data' in fm.datahint()
2755 return not fm.datahint() or b'data' in fm.datahint()
2748
2756
2749
2757
2750 def _updatecatformatter(fm, ctx, matcher, path, decode):
2758 def _updatecatformatter(fm, ctx, matcher, path, decode):
2751 """Hook for adding data to the formatter used by ``hg cat``.
2759 """Hook for adding data to the formatter used by ``hg cat``.
2752
2760
2753 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2761 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2754 this method first."""
2762 this method first."""
2755
2763
2756 # data() can be expensive to fetch (e.g. lfs), so don't fetch it if it
2764 # data() can be expensive to fetch (e.g. lfs), so don't fetch it if it
2757 # wasn't requested.
2765 # wasn't requested.
2758 data = b''
2766 data = b''
2759 if _catfmtneedsdata(fm):
2767 if _catfmtneedsdata(fm):
2760 data = ctx[path].data()
2768 data = ctx[path].data()
2761 if decode:
2769 if decode:
2762 data = ctx.repo().wwritedata(path, data)
2770 data = ctx.repo().wwritedata(path, data)
2763 fm.startitem()
2771 fm.startitem()
2764 fm.context(ctx=ctx)
2772 fm.context(ctx=ctx)
2765 fm.write(b'data', b'%s', data)
2773 fm.write(b'data', b'%s', data)
2766 fm.data(path=path)
2774 fm.data(path=path)
2767
2775
2768
2776
2769 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2777 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2770 err = 1
2778 err = 1
2771 opts = pycompat.byteskwargs(opts)
2779 opts = pycompat.byteskwargs(opts)
2772
2780
2773 def write(path):
2781 def write(path):
2774 filename = None
2782 filename = None
2775 if fntemplate:
2783 if fntemplate:
2776 filename = makefilename(
2784 filename = makefilename(
2777 ctx, fntemplate, pathname=os.path.join(prefix, path)
2785 ctx, fntemplate, pathname=os.path.join(prefix, path)
2778 )
2786 )
2779 # attempt to create the directory if it does not already exist
2787 # attempt to create the directory if it does not already exist
2780 try:
2788 try:
2781 os.makedirs(os.path.dirname(filename))
2789 os.makedirs(os.path.dirname(filename))
2782 except OSError:
2790 except OSError:
2783 pass
2791 pass
2784 with formatter.maybereopen(basefm, filename) as fm:
2792 with formatter.maybereopen(basefm, filename) as fm:
2785 _updatecatformatter(fm, ctx, matcher, path, opts.get(b'decode'))
2793 _updatecatformatter(fm, ctx, matcher, path, opts.get(b'decode'))
2786
2794
2787 # Automation often uses hg cat on single files, so special case it
2795 # Automation often uses hg cat on single files, so special case it
2788 # for performance to avoid the cost of parsing the manifest.
2796 # for performance to avoid the cost of parsing the manifest.
2789 if len(matcher.files()) == 1 and not matcher.anypats():
2797 if len(matcher.files()) == 1 and not matcher.anypats():
2790 file = matcher.files()[0]
2798 file = matcher.files()[0]
2791 mfl = repo.manifestlog
2799 mfl = repo.manifestlog
2792 mfnode = ctx.manifestnode()
2800 mfnode = ctx.manifestnode()
2793 try:
2801 try:
2794 if mfnode and mfl[mfnode].find(file)[0]:
2802 if mfnode and mfl[mfnode].find(file)[0]:
2795 if _catfmtneedsdata(basefm):
2803 if _catfmtneedsdata(basefm):
2796 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2804 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2797 write(file)
2805 write(file)
2798 return 0
2806 return 0
2799 except KeyError:
2807 except KeyError:
2800 pass
2808 pass
2801
2809
2802 if _catfmtneedsdata(basefm):
2810 if _catfmtneedsdata(basefm):
2803 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2811 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2804
2812
2805 for abs in ctx.walk(matcher):
2813 for abs in ctx.walk(matcher):
2806 write(abs)
2814 write(abs)
2807 err = 0
2815 err = 0
2808
2816
2809 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2817 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2810 for subpath in sorted(ctx.substate):
2818 for subpath in sorted(ctx.substate):
2811 sub = ctx.sub(subpath)
2819 sub = ctx.sub(subpath)
2812 try:
2820 try:
2813 submatch = matchmod.subdirmatcher(subpath, matcher)
2821 submatch = matchmod.subdirmatcher(subpath, matcher)
2814 subprefix = os.path.join(prefix, subpath)
2822 subprefix = os.path.join(prefix, subpath)
2815 if not sub.cat(
2823 if not sub.cat(
2816 submatch,
2824 submatch,
2817 basefm,
2825 basefm,
2818 fntemplate,
2826 fntemplate,
2819 subprefix,
2827 subprefix,
2820 **pycompat.strkwargs(opts)
2828 **pycompat.strkwargs(opts)
2821 ):
2829 ):
2822 err = 0
2830 err = 0
2823 except error.RepoLookupError:
2831 except error.RepoLookupError:
2824 ui.status(
2832 ui.status(
2825 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2833 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2826 )
2834 )
2827
2835
2828 return err
2836 return err
2829
2837
2830
2838
2831 def commit(ui, repo, commitfunc, pats, opts):
2839 def commit(ui, repo, commitfunc, pats, opts):
2832 '''commit the specified files or all outstanding changes'''
2840 '''commit the specified files or all outstanding changes'''
2833 date = opts.get(b'date')
2841 date = opts.get(b'date')
2834 if date:
2842 if date:
2835 opts[b'date'] = dateutil.parsedate(date)
2843 opts[b'date'] = dateutil.parsedate(date)
2836 message = logmessage(ui, opts)
2844 message = logmessage(ui, opts)
2837 matcher = scmutil.match(repo[None], pats, opts)
2845 matcher = scmutil.match(repo[None], pats, opts)
2838
2846
2839 dsguard = None
2847 dsguard = None
2840 # extract addremove carefully -- this function can be called from a command
2848 # extract addremove carefully -- this function can be called from a command
2841 # that doesn't support addremove
2849 # that doesn't support addremove
2842 if opts.get(b'addremove'):
2850 if opts.get(b'addremove'):
2843 dsguard = dirstateguard.dirstateguard(repo, b'commit')
2851 dsguard = dirstateguard.dirstateguard(repo, b'commit')
2844 with dsguard or util.nullcontextmanager():
2852 with dsguard or util.nullcontextmanager():
2845 if dsguard:
2853 if dsguard:
2846 relative = scmutil.anypats(pats, opts)
2854 relative = scmutil.anypats(pats, opts)
2847 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2855 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2848 if scmutil.addremove(repo, matcher, b"", uipathfn, opts) != 0:
2856 if scmutil.addremove(repo, matcher, b"", uipathfn, opts) != 0:
2849 raise error.Abort(
2857 raise error.Abort(
2850 _(b"failed to mark all new/missing files as added/removed")
2858 _(b"failed to mark all new/missing files as added/removed")
2851 )
2859 )
2852
2860
2853 return commitfunc(ui, repo, message, matcher, opts)
2861 return commitfunc(ui, repo, message, matcher, opts)
2854
2862
2855
2863
2856 def samefile(f, ctx1, ctx2):
2864 def samefile(f, ctx1, ctx2):
2857 if f in ctx1.manifest():
2865 if f in ctx1.manifest():
2858 a = ctx1.filectx(f)
2866 a = ctx1.filectx(f)
2859 if f in ctx2.manifest():
2867 if f in ctx2.manifest():
2860 b = ctx2.filectx(f)
2868 b = ctx2.filectx(f)
2861 return not a.cmp(b) and a.flags() == b.flags()
2869 return not a.cmp(b) and a.flags() == b.flags()
2862 else:
2870 else:
2863 return False
2871 return False
2864 else:
2872 else:
2865 return f not in ctx2.manifest()
2873 return f not in ctx2.manifest()
2866
2874
2867
2875
2868 def amend(ui, repo, old, extra, pats, opts):
2876 def amend(ui, repo, old, extra, pats, opts):
2869 # avoid cycle context -> subrepo -> cmdutil
2877 # avoid cycle context -> subrepo -> cmdutil
2870 from . import context
2878 from . import context
2871
2879
2872 # amend will reuse the existing user if not specified, but the obsolete
2880 # amend will reuse the existing user if not specified, but the obsolete
2873 # marker creation requires that the current user's name is specified.
2881 # marker creation requires that the current user's name is specified.
2874 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2882 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2875 ui.username() # raise exception if username not set
2883 ui.username() # raise exception if username not set
2876
2884
2877 ui.note(_(b'amending changeset %s\n') % old)
2885 ui.note(_(b'amending changeset %s\n') % old)
2878 base = old.p1()
2886 base = old.p1()
2879
2887
2880 with repo.wlock(), repo.lock(), repo.transaction(b'amend'):
2888 with repo.wlock(), repo.lock(), repo.transaction(b'amend'):
2881 # Participating changesets:
2889 # Participating changesets:
2882 #
2890 #
2883 # wctx o - workingctx that contains changes from working copy
2891 # wctx o - workingctx that contains changes from working copy
2884 # | to go into amending commit
2892 # | to go into amending commit
2885 # |
2893 # |
2886 # old o - changeset to amend
2894 # old o - changeset to amend
2887 # |
2895 # |
2888 # base o - first parent of the changeset to amend
2896 # base o - first parent of the changeset to amend
2889 wctx = repo[None]
2897 wctx = repo[None]
2890
2898
2891 # Copy to avoid mutating input
2899 # Copy to avoid mutating input
2892 extra = extra.copy()
2900 extra = extra.copy()
2893 # Update extra dict from amended commit (e.g. to preserve graft
2901 # Update extra dict from amended commit (e.g. to preserve graft
2894 # source)
2902 # source)
2895 extra.update(old.extra())
2903 extra.update(old.extra())
2896
2904
2897 # Also update it from the from the wctx
2905 # Also update it from the from the wctx
2898 extra.update(wctx.extra())
2906 extra.update(wctx.extra())
2899
2907
2900 # date-only change should be ignored?
2908 # date-only change should be ignored?
2901 datemaydiffer = resolvecommitoptions(ui, opts)
2909 datemaydiffer = resolvecommitoptions(ui, opts)
2902
2910
2903 date = old.date()
2911 date = old.date()
2904 if opts.get(b'date'):
2912 if opts.get(b'date'):
2905 date = dateutil.parsedate(opts.get(b'date'))
2913 date = dateutil.parsedate(opts.get(b'date'))
2906 user = opts.get(b'user') or old.user()
2914 user = opts.get(b'user') or old.user()
2907
2915
2908 if len(old.parents()) > 1:
2916 if len(old.parents()) > 1:
2909 # ctx.files() isn't reliable for merges, so fall back to the
2917 # ctx.files() isn't reliable for merges, so fall back to the
2910 # slower repo.status() method
2918 # slower repo.status() method
2911 st = base.status(old)
2919 st = base.status(old)
2912 files = set(st.modified) | set(st.added) | set(st.removed)
2920 files = set(st.modified) | set(st.added) | set(st.removed)
2913 else:
2921 else:
2914 files = set(old.files())
2922 files = set(old.files())
2915
2923
2916 # add/remove the files to the working copy if the "addremove" option
2924 # add/remove the files to the working copy if the "addremove" option
2917 # was specified.
2925 # was specified.
2918 matcher = scmutil.match(wctx, pats, opts)
2926 matcher = scmutil.match(wctx, pats, opts)
2919 relative = scmutil.anypats(pats, opts)
2927 relative = scmutil.anypats(pats, opts)
2920 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2928 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2921 if opts.get(b'addremove') and scmutil.addremove(
2929 if opts.get(b'addremove') and scmutil.addremove(
2922 repo, matcher, b"", uipathfn, opts
2930 repo, matcher, b"", uipathfn, opts
2923 ):
2931 ):
2924 raise error.Abort(
2932 raise error.Abort(
2925 _(b"failed to mark all new/missing files as added/removed")
2933 _(b"failed to mark all new/missing files as added/removed")
2926 )
2934 )
2927
2935
2928 # Check subrepos. This depends on in-place wctx._status update in
2936 # Check subrepos. This depends on in-place wctx._status update in
2929 # subrepo.precommit(). To minimize the risk of this hack, we do
2937 # subrepo.precommit(). To minimize the risk of this hack, we do
2930 # nothing if .hgsub does not exist.
2938 # nothing if .hgsub does not exist.
2931 if b'.hgsub' in wctx or b'.hgsub' in old:
2939 if b'.hgsub' in wctx or b'.hgsub' in old:
2932 subs, commitsubs, newsubstate = subrepoutil.precommit(
2940 subs, commitsubs, newsubstate = subrepoutil.precommit(
2933 ui, wctx, wctx._status, matcher
2941 ui, wctx, wctx._status, matcher
2934 )
2942 )
2935 # amend should abort if commitsubrepos is enabled
2943 # amend should abort if commitsubrepos is enabled
2936 assert not commitsubs
2944 assert not commitsubs
2937 if subs:
2945 if subs:
2938 subrepoutil.writestate(repo, newsubstate)
2946 subrepoutil.writestate(repo, newsubstate)
2939
2947
2940 ms = mergemod.mergestate.read(repo)
2948 ms = mergemod.mergestate.read(repo)
2941 mergeutil.checkunresolved(ms)
2949 mergeutil.checkunresolved(ms)
2942
2950
2943 filestoamend = set(f for f in wctx.files() if matcher(f))
2951 filestoamend = set(f for f in wctx.files() if matcher(f))
2944
2952
2945 changes = len(filestoamend) > 0
2953 changes = len(filestoamend) > 0
2946 if changes:
2954 if changes:
2947 # Recompute copies (avoid recording a -> b -> a)
2955 # Recompute copies (avoid recording a -> b -> a)
2948 copied = copies.pathcopies(base, wctx, matcher)
2956 copied = copies.pathcopies(base, wctx, matcher)
2949 if old.p2:
2957 if old.p2:
2950 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
2958 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
2951
2959
2952 # Prune files which were reverted by the updates: if old
2960 # Prune files which were reverted by the updates: if old
2953 # introduced file X and the file was renamed in the working
2961 # introduced file X and the file was renamed in the working
2954 # copy, then those two files are the same and
2962 # copy, then those two files are the same and
2955 # we can discard X from our list of files. Likewise if X
2963 # we can discard X from our list of files. Likewise if X
2956 # was removed, it's no longer relevant. If X is missing (aka
2964 # was removed, it's no longer relevant. If X is missing (aka
2957 # deleted), old X must be preserved.
2965 # deleted), old X must be preserved.
2958 files.update(filestoamend)
2966 files.update(filestoamend)
2959 files = [
2967 files = [
2960 f
2968 f
2961 for f in files
2969 for f in files
2962 if (f not in filestoamend or not samefile(f, wctx, base))
2970 if (f not in filestoamend or not samefile(f, wctx, base))
2963 ]
2971 ]
2964
2972
2965 def filectxfn(repo, ctx_, path):
2973 def filectxfn(repo, ctx_, path):
2966 try:
2974 try:
2967 # If the file being considered is not amongst the files
2975 # If the file being considered is not amongst the files
2968 # to be amended, we should return the file context from the
2976 # to be amended, we should return the file context from the
2969 # old changeset. This avoids issues when only some files in
2977 # old changeset. This avoids issues when only some files in
2970 # the working copy are being amended but there are also
2978 # the working copy are being amended but there are also
2971 # changes to other files from the old changeset.
2979 # changes to other files from the old changeset.
2972 if path not in filestoamend:
2980 if path not in filestoamend:
2973 return old.filectx(path)
2981 return old.filectx(path)
2974
2982
2975 # Return None for removed files.
2983 # Return None for removed files.
2976 if path in wctx.removed():
2984 if path in wctx.removed():
2977 return None
2985 return None
2978
2986
2979 fctx = wctx[path]
2987 fctx = wctx[path]
2980 flags = fctx.flags()
2988 flags = fctx.flags()
2981 mctx = context.memfilectx(
2989 mctx = context.memfilectx(
2982 repo,
2990 repo,
2983 ctx_,
2991 ctx_,
2984 fctx.path(),
2992 fctx.path(),
2985 fctx.data(),
2993 fctx.data(),
2986 islink=b'l' in flags,
2994 islink=b'l' in flags,
2987 isexec=b'x' in flags,
2995 isexec=b'x' in flags,
2988 copysource=copied.get(path),
2996 copysource=copied.get(path),
2989 )
2997 )
2990 return mctx
2998 return mctx
2991 except KeyError:
2999 except KeyError:
2992 return None
3000 return None
2993
3001
2994 else:
3002 else:
2995 ui.note(_(b'copying changeset %s to %s\n') % (old, base))
3003 ui.note(_(b'copying changeset %s to %s\n') % (old, base))
2996
3004
2997 # Use version of files as in the old cset
3005 # Use version of files as in the old cset
2998 def filectxfn(repo, ctx_, path):
3006 def filectxfn(repo, ctx_, path):
2999 try:
3007 try:
3000 return old.filectx(path)
3008 return old.filectx(path)
3001 except KeyError:
3009 except KeyError:
3002 return None
3010 return None
3003
3011
3004 # See if we got a message from -m or -l, if not, open the editor with
3012 # See if we got a message from -m or -l, if not, open the editor with
3005 # the message of the changeset to amend.
3013 # the message of the changeset to amend.
3006 message = logmessage(ui, opts)
3014 message = logmessage(ui, opts)
3007
3015
3008 editform = mergeeditform(old, b'commit.amend')
3016 editform = mergeeditform(old, b'commit.amend')
3009
3017
3010 if not message:
3018 if not message:
3011 message = old.description()
3019 message = old.description()
3012 # Default if message isn't provided and --edit is not passed is to
3020 # Default if message isn't provided and --edit is not passed is to
3013 # invoke editor, but allow --no-edit. If somehow we don't have any
3021 # invoke editor, but allow --no-edit. If somehow we don't have any
3014 # description, let's always start the editor.
3022 # description, let's always start the editor.
3015 doedit = not message or opts.get(b'edit') in [True, None]
3023 doedit = not message or opts.get(b'edit') in [True, None]
3016 else:
3024 else:
3017 # Default if message is provided is to not invoke editor, but allow
3025 # Default if message is provided is to not invoke editor, but allow
3018 # --edit.
3026 # --edit.
3019 doedit = opts.get(b'edit') is True
3027 doedit = opts.get(b'edit') is True
3020 editor = getcommiteditor(edit=doedit, editform=editform)
3028 editor = getcommiteditor(edit=doedit, editform=editform)
3021
3029
3022 pureextra = extra.copy()
3030 pureextra = extra.copy()
3023 extra[b'amend_source'] = old.hex()
3031 extra[b'amend_source'] = old.hex()
3024
3032
3025 new = context.memctx(
3033 new = context.memctx(
3026 repo,
3034 repo,
3027 parents=[base.node(), old.p2().node()],
3035 parents=[base.node(), old.p2().node()],
3028 text=message,
3036 text=message,
3029 files=files,
3037 files=files,
3030 filectxfn=filectxfn,
3038 filectxfn=filectxfn,
3031 user=user,
3039 user=user,
3032 date=date,
3040 date=date,
3033 extra=extra,
3041 extra=extra,
3034 editor=editor,
3042 editor=editor,
3035 )
3043 )
3036
3044
3037 newdesc = changelog.stripdesc(new.description())
3045 newdesc = changelog.stripdesc(new.description())
3038 if (
3046 if (
3039 (not changes)
3047 (not changes)
3040 and newdesc == old.description()
3048 and newdesc == old.description()
3041 and user == old.user()
3049 and user == old.user()
3042 and (date == old.date() or datemaydiffer)
3050 and (date == old.date() or datemaydiffer)
3043 and pureextra == old.extra()
3051 and pureextra == old.extra()
3044 ):
3052 ):
3045 # nothing changed. continuing here would create a new node
3053 # nothing changed. continuing here would create a new node
3046 # anyway because of the amend_source noise.
3054 # anyway because of the amend_source noise.
3047 #
3055 #
3048 # This not what we expect from amend.
3056 # This not what we expect from amend.
3049 return old.node()
3057 return old.node()
3050
3058
3051 commitphase = None
3059 commitphase = None
3052 if opts.get(b'secret'):
3060 if opts.get(b'secret'):
3053 commitphase = phases.secret
3061 commitphase = phases.secret
3054 newid = repo.commitctx(new)
3062 newid = repo.commitctx(new)
3055
3063
3056 # Reroute the working copy parent to the new changeset
3064 # Reroute the working copy parent to the new changeset
3057 repo.setparents(newid, nullid)
3065 repo.setparents(newid, nullid)
3058 mapping = {old.node(): (newid,)}
3066 mapping = {old.node(): (newid,)}
3059 obsmetadata = None
3067 obsmetadata = None
3060 if opts.get(b'note'):
3068 if opts.get(b'note'):
3061 obsmetadata = {b'note': encoding.fromlocal(opts[b'note'])}
3069 obsmetadata = {b'note': encoding.fromlocal(opts[b'note'])}
3062 backup = ui.configbool(b'rewrite', b'backup-bundle')
3070 backup = ui.configbool(b'rewrite', b'backup-bundle')
3063 scmutil.cleanupnodes(
3071 scmutil.cleanupnodes(
3064 repo,
3072 repo,
3065 mapping,
3073 mapping,
3066 b'amend',
3074 b'amend',
3067 metadata=obsmetadata,
3075 metadata=obsmetadata,
3068 fixphase=True,
3076 fixphase=True,
3069 targetphase=commitphase,
3077 targetphase=commitphase,
3070 backup=backup,
3078 backup=backup,
3071 )
3079 )
3072
3080
3073 # Fixing the dirstate because localrepo.commitctx does not update
3081 # Fixing the dirstate because localrepo.commitctx does not update
3074 # it. This is rather convenient because we did not need to update
3082 # it. This is rather convenient because we did not need to update
3075 # the dirstate for all the files in the new commit which commitctx
3083 # the dirstate for all the files in the new commit which commitctx
3076 # could have done if it updated the dirstate. Now, we can
3084 # could have done if it updated the dirstate. Now, we can
3077 # selectively update the dirstate only for the amended files.
3085 # selectively update the dirstate only for the amended files.
3078 dirstate = repo.dirstate
3086 dirstate = repo.dirstate
3079
3087
3080 # Update the state of the files which were added and modified in the
3088 # Update the state of the files which were added and modified in the
3081 # amend to "normal" in the dirstate. We need to use "normallookup" since
3089 # amend to "normal" in the dirstate. We need to use "normallookup" since
3082 # the files may have changed since the command started; using "normal"
3090 # the files may have changed since the command started; using "normal"
3083 # would mark them as clean but with uncommitted contents.
3091 # would mark them as clean but with uncommitted contents.
3084 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
3092 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
3085 for f in normalfiles:
3093 for f in normalfiles:
3086 dirstate.normallookup(f)
3094 dirstate.normallookup(f)
3087
3095
3088 # Update the state of files which were removed in the amend
3096 # Update the state of files which were removed in the amend
3089 # to "removed" in the dirstate.
3097 # to "removed" in the dirstate.
3090 removedfiles = set(wctx.removed()) & filestoamend
3098 removedfiles = set(wctx.removed()) & filestoamend
3091 for f in removedfiles:
3099 for f in removedfiles:
3092 dirstate.drop(f)
3100 dirstate.drop(f)
3093
3101
3094 return newid
3102 return newid
3095
3103
3096
3104
3097 def commiteditor(repo, ctx, subs, editform=b''):
3105 def commiteditor(repo, ctx, subs, editform=b''):
3098 if ctx.description():
3106 if ctx.description():
3099 return ctx.description()
3107 return ctx.description()
3100 return commitforceeditor(
3108 return commitforceeditor(
3101 repo, ctx, subs, editform=editform, unchangedmessagedetection=True
3109 repo, ctx, subs, editform=editform, unchangedmessagedetection=True
3102 )
3110 )
3103
3111
3104
3112
3105 def commitforceeditor(
3113 def commitforceeditor(
3106 repo,
3114 repo,
3107 ctx,
3115 ctx,
3108 subs,
3116 subs,
3109 finishdesc=None,
3117 finishdesc=None,
3110 extramsg=None,
3118 extramsg=None,
3111 editform=b'',
3119 editform=b'',
3112 unchangedmessagedetection=False,
3120 unchangedmessagedetection=False,
3113 ):
3121 ):
3114 if not extramsg:
3122 if not extramsg:
3115 extramsg = _(b"Leave message empty to abort commit.")
3123 extramsg = _(b"Leave message empty to abort commit.")
3116
3124
3117 forms = [e for e in editform.split(b'.') if e]
3125 forms = [e for e in editform.split(b'.') if e]
3118 forms.insert(0, b'changeset')
3126 forms.insert(0, b'changeset')
3119 templatetext = None
3127 templatetext = None
3120 while forms:
3128 while forms:
3121 ref = b'.'.join(forms)
3129 ref = b'.'.join(forms)
3122 if repo.ui.config(b'committemplate', ref):
3130 if repo.ui.config(b'committemplate', ref):
3123 templatetext = committext = buildcommittemplate(
3131 templatetext = committext = buildcommittemplate(
3124 repo, ctx, subs, extramsg, ref
3132 repo, ctx, subs, extramsg, ref
3125 )
3133 )
3126 break
3134 break
3127 forms.pop()
3135 forms.pop()
3128 else:
3136 else:
3129 committext = buildcommittext(repo, ctx, subs, extramsg)
3137 committext = buildcommittext(repo, ctx, subs, extramsg)
3130
3138
3131 # run editor in the repository root
3139 # run editor in the repository root
3132 olddir = encoding.getcwd()
3140 olddir = encoding.getcwd()
3133 os.chdir(repo.root)
3141 os.chdir(repo.root)
3134
3142
3135 # make in-memory changes visible to external process
3143 # make in-memory changes visible to external process
3136 tr = repo.currenttransaction()
3144 tr = repo.currenttransaction()
3137 repo.dirstate.write(tr)
3145 repo.dirstate.write(tr)
3138 pending = tr and tr.writepending() and repo.root
3146 pending = tr and tr.writepending() and repo.root
3139
3147
3140 editortext = repo.ui.edit(
3148 editortext = repo.ui.edit(
3141 committext,
3149 committext,
3142 ctx.user(),
3150 ctx.user(),
3143 ctx.extra(),
3151 ctx.extra(),
3144 editform=editform,
3152 editform=editform,
3145 pending=pending,
3153 pending=pending,
3146 repopath=repo.path,
3154 repopath=repo.path,
3147 action=b'commit',
3155 action=b'commit',
3148 )
3156 )
3149 text = editortext
3157 text = editortext
3150
3158
3151 # strip away anything below this special string (used for editors that want
3159 # strip away anything below this special string (used for editors that want
3152 # to display the diff)
3160 # to display the diff)
3153 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3161 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3154 if stripbelow:
3162 if stripbelow:
3155 text = text[: stripbelow.start()]
3163 text = text[: stripbelow.start()]
3156
3164
3157 text = re.sub(b"(?m)^HG:.*(\n|$)", b"", text)
3165 text = re.sub(b"(?m)^HG:.*(\n|$)", b"", text)
3158 os.chdir(olddir)
3166 os.chdir(olddir)
3159
3167
3160 if finishdesc:
3168 if finishdesc:
3161 text = finishdesc(text)
3169 text = finishdesc(text)
3162 if not text.strip():
3170 if not text.strip():
3163 raise error.Abort(_(b"empty commit message"))
3171 raise error.Abort(_(b"empty commit message"))
3164 if unchangedmessagedetection and editortext == templatetext:
3172 if unchangedmessagedetection and editortext == templatetext:
3165 raise error.Abort(_(b"commit message unchanged"))
3173 raise error.Abort(_(b"commit message unchanged"))
3166
3174
3167 return text
3175 return text
3168
3176
3169
3177
3170 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3178 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3171 ui = repo.ui
3179 ui = repo.ui
3172 spec = formatter.templatespec(ref, None, None)
3180 spec = formatter.templatespec(ref, None, None)
3173 t = logcmdutil.changesettemplater(ui, repo, spec)
3181 t = logcmdutil.changesettemplater(ui, repo, spec)
3174 t.t.cache.update(
3182 t.t.cache.update(
3175 (k, templater.unquotestring(v))
3183 (k, templater.unquotestring(v))
3176 for k, v in repo.ui.configitems(b'committemplate')
3184 for k, v in repo.ui.configitems(b'committemplate')
3177 )
3185 )
3178
3186
3179 if not extramsg:
3187 if not extramsg:
3180 extramsg = b'' # ensure that extramsg is string
3188 extramsg = b'' # ensure that extramsg is string
3181
3189
3182 ui.pushbuffer()
3190 ui.pushbuffer()
3183 t.show(ctx, extramsg=extramsg)
3191 t.show(ctx, extramsg=extramsg)
3184 return ui.popbuffer()
3192 return ui.popbuffer()
3185
3193
3186
3194
3187 def hgprefix(msg):
3195 def hgprefix(msg):
3188 return b"\n".join([b"HG: %s" % a for a in msg.split(b"\n") if a])
3196 return b"\n".join([b"HG: %s" % a for a in msg.split(b"\n") if a])
3189
3197
3190
3198
3191 def buildcommittext(repo, ctx, subs, extramsg):
3199 def buildcommittext(repo, ctx, subs, extramsg):
3192 edittext = []
3200 edittext = []
3193 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3201 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3194 if ctx.description():
3202 if ctx.description():
3195 edittext.append(ctx.description())
3203 edittext.append(ctx.description())
3196 edittext.append(b"")
3204 edittext.append(b"")
3197 edittext.append(b"") # Empty line between message and comments.
3205 edittext.append(b"") # Empty line between message and comments.
3198 edittext.append(
3206 edittext.append(
3199 hgprefix(
3207 hgprefix(
3200 _(
3208 _(
3201 b"Enter commit message."
3209 b"Enter commit message."
3202 b" Lines beginning with 'HG:' are removed."
3210 b" Lines beginning with 'HG:' are removed."
3203 )
3211 )
3204 )
3212 )
3205 )
3213 )
3206 edittext.append(hgprefix(extramsg))
3214 edittext.append(hgprefix(extramsg))
3207 edittext.append(b"HG: --")
3215 edittext.append(b"HG: --")
3208 edittext.append(hgprefix(_(b"user: %s") % ctx.user()))
3216 edittext.append(hgprefix(_(b"user: %s") % ctx.user()))
3209 if ctx.p2():
3217 if ctx.p2():
3210 edittext.append(hgprefix(_(b"branch merge")))
3218 edittext.append(hgprefix(_(b"branch merge")))
3211 if ctx.branch():
3219 if ctx.branch():
3212 edittext.append(hgprefix(_(b"branch '%s'") % ctx.branch()))
3220 edittext.append(hgprefix(_(b"branch '%s'") % ctx.branch()))
3213 if bookmarks.isactivewdirparent(repo):
3221 if bookmarks.isactivewdirparent(repo):
3214 edittext.append(hgprefix(_(b"bookmark '%s'") % repo._activebookmark))
3222 edittext.append(hgprefix(_(b"bookmark '%s'") % repo._activebookmark))
3215 edittext.extend([hgprefix(_(b"subrepo %s") % s) for s in subs])
3223 edittext.extend([hgprefix(_(b"subrepo %s") % s) for s in subs])
3216 edittext.extend([hgprefix(_(b"added %s") % f) for f in added])
3224 edittext.extend([hgprefix(_(b"added %s") % f) for f in added])
3217 edittext.extend([hgprefix(_(b"changed %s") % f) for f in modified])
3225 edittext.extend([hgprefix(_(b"changed %s") % f) for f in modified])
3218 edittext.extend([hgprefix(_(b"removed %s") % f) for f in removed])
3226 edittext.extend([hgprefix(_(b"removed %s") % f) for f in removed])
3219 if not added and not modified and not removed:
3227 if not added and not modified and not removed:
3220 edittext.append(hgprefix(_(b"no files changed")))
3228 edittext.append(hgprefix(_(b"no files changed")))
3221 edittext.append(b"")
3229 edittext.append(b"")
3222
3230
3223 return b"\n".join(edittext)
3231 return b"\n".join(edittext)
3224
3232
3225
3233
3226 def commitstatus(repo, node, branch, bheads=None, opts=None):
3234 def commitstatus(repo, node, branch, bheads=None, opts=None):
3227 if opts is None:
3235 if opts is None:
3228 opts = {}
3236 opts = {}
3229 ctx = repo[node]
3237 ctx = repo[node]
3230 parents = ctx.parents()
3238 parents = ctx.parents()
3231
3239
3232 if (
3240 if (
3233 not opts.get(b'amend')
3241 not opts.get(b'amend')
3234 and bheads
3242 and bheads
3235 and node not in bheads
3243 and node not in bheads
3236 and not [
3244 and not [
3237 x for x in parents if x.node() in bheads and x.branch() == branch
3245 x for x in parents if x.node() in bheads and x.branch() == branch
3238 ]
3246 ]
3239 ):
3247 ):
3240 repo.ui.status(_(b'created new head\n'))
3248 repo.ui.status(_(b'created new head\n'))
3241 # The message is not printed for initial roots. For the other
3249 # The message is not printed for initial roots. For the other
3242 # changesets, it is printed in the following situations:
3250 # changesets, it is printed in the following situations:
3243 #
3251 #
3244 # Par column: for the 2 parents with ...
3252 # Par column: for the 2 parents with ...
3245 # N: null or no parent
3253 # N: null or no parent
3246 # B: parent is on another named branch
3254 # B: parent is on another named branch
3247 # C: parent is a regular non head changeset
3255 # C: parent is a regular non head changeset
3248 # H: parent was a branch head of the current branch
3256 # H: parent was a branch head of the current branch
3249 # Msg column: whether we print "created new head" message
3257 # Msg column: whether we print "created new head" message
3250 # In the following, it is assumed that there already exists some
3258 # In the following, it is assumed that there already exists some
3251 # initial branch heads of the current branch, otherwise nothing is
3259 # initial branch heads of the current branch, otherwise nothing is
3252 # printed anyway.
3260 # printed anyway.
3253 #
3261 #
3254 # Par Msg Comment
3262 # Par Msg Comment
3255 # N N y additional topo root
3263 # N N y additional topo root
3256 #
3264 #
3257 # B N y additional branch root
3265 # B N y additional branch root
3258 # C N y additional topo head
3266 # C N y additional topo head
3259 # H N n usual case
3267 # H N n usual case
3260 #
3268 #
3261 # B B y weird additional branch root
3269 # B B y weird additional branch root
3262 # C B y branch merge
3270 # C B y branch merge
3263 # H B n merge with named branch
3271 # H B n merge with named branch
3264 #
3272 #
3265 # C C y additional head from merge
3273 # C C y additional head from merge
3266 # C H n merge with a head
3274 # C H n merge with a head
3267 #
3275 #
3268 # H H n head merge: head count decreases
3276 # H H n head merge: head count decreases
3269
3277
3270 if not opts.get(b'close_branch'):
3278 if not opts.get(b'close_branch'):
3271 for r in parents:
3279 for r in parents:
3272 if r.closesbranch() and r.branch() == branch:
3280 if r.closesbranch() and r.branch() == branch:
3273 repo.ui.status(
3281 repo.ui.status(
3274 _(b'reopening closed branch head %d\n') % r.rev()
3282 _(b'reopening closed branch head %d\n') % r.rev()
3275 )
3283 )
3276
3284
3277 if repo.ui.debugflag:
3285 if repo.ui.debugflag:
3278 repo.ui.write(
3286 repo.ui.write(
3279 _(b'committed changeset %d:%s\n') % (ctx.rev(), ctx.hex())
3287 _(b'committed changeset %d:%s\n') % (ctx.rev(), ctx.hex())
3280 )
3288 )
3281 elif repo.ui.verbose:
3289 elif repo.ui.verbose:
3282 repo.ui.write(_(b'committed changeset %d:%s\n') % (ctx.rev(), ctx))
3290 repo.ui.write(_(b'committed changeset %d:%s\n') % (ctx.rev(), ctx))
3283
3291
3284
3292
3285 def postcommitstatus(repo, pats, opts):
3293 def postcommitstatus(repo, pats, opts):
3286 return repo.status(match=scmutil.match(repo[None], pats, opts))
3294 return repo.status(match=scmutil.match(repo[None], pats, opts))
3287
3295
3288
3296
3289 def revert(ui, repo, ctx, parents, *pats, **opts):
3297 def revert(ui, repo, ctx, parents, *pats, **opts):
3290 opts = pycompat.byteskwargs(opts)
3298 opts = pycompat.byteskwargs(opts)
3291 parent, p2 = parents
3299 parent, p2 = parents
3292 node = ctx.node()
3300 node = ctx.node()
3293
3301
3294 mf = ctx.manifest()
3302 mf = ctx.manifest()
3295 if node == p2:
3303 if node == p2:
3296 parent = p2
3304 parent = p2
3297
3305
3298 # need all matching names in dirstate and manifest of target rev,
3306 # need all matching names in dirstate and manifest of target rev,
3299 # so have to walk both. do not print errors if files exist in one
3307 # so have to walk both. do not print errors if files exist in one
3300 # but not other. in both cases, filesets should be evaluated against
3308 # but not other. in both cases, filesets should be evaluated against
3301 # workingctx to get consistent result (issue4497). this means 'set:**'
3309 # workingctx to get consistent result (issue4497). this means 'set:**'
3302 # cannot be used to select missing files from target rev.
3310 # cannot be used to select missing files from target rev.
3303
3311
3304 # `names` is a mapping for all elements in working copy and target revision
3312 # `names` is a mapping for all elements in working copy and target revision
3305 # The mapping is in the form:
3313 # The mapping is in the form:
3306 # <abs path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3314 # <abs path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3307 names = {}
3315 names = {}
3308 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
3316 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
3309
3317
3310 with repo.wlock():
3318 with repo.wlock():
3311 ## filling of the `names` mapping
3319 ## filling of the `names` mapping
3312 # walk dirstate to fill `names`
3320 # walk dirstate to fill `names`
3313
3321
3314 interactive = opts.get(b'interactive', False)
3322 interactive = opts.get(b'interactive', False)
3315 wctx = repo[None]
3323 wctx = repo[None]
3316 m = scmutil.match(wctx, pats, opts)
3324 m = scmutil.match(wctx, pats, opts)
3317
3325
3318 # we'll need this later
3326 # we'll need this later
3319 targetsubs = sorted(s for s in wctx.substate if m(s))
3327 targetsubs = sorted(s for s in wctx.substate if m(s))
3320
3328
3321 if not m.always():
3329 if not m.always():
3322 matcher = matchmod.badmatch(m, lambda x, y: False)
3330 matcher = matchmod.badmatch(m, lambda x, y: False)
3323 for abs in wctx.walk(matcher):
3331 for abs in wctx.walk(matcher):
3324 names[abs] = m.exact(abs)
3332 names[abs] = m.exact(abs)
3325
3333
3326 # walk target manifest to fill `names`
3334 # walk target manifest to fill `names`
3327
3335
3328 def badfn(path, msg):
3336 def badfn(path, msg):
3329 if path in names:
3337 if path in names:
3330 return
3338 return
3331 if path in ctx.substate:
3339 if path in ctx.substate:
3332 return
3340 return
3333 path_ = path + b'/'
3341 path_ = path + b'/'
3334 for f in names:
3342 for f in names:
3335 if f.startswith(path_):
3343 if f.startswith(path_):
3336 return
3344 return
3337 ui.warn(b"%s: %s\n" % (uipathfn(path), msg))
3345 ui.warn(b"%s: %s\n" % (uipathfn(path), msg))
3338
3346
3339 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3347 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3340 if abs not in names:
3348 if abs not in names:
3341 names[abs] = m.exact(abs)
3349 names[abs] = m.exact(abs)
3342
3350
3343 # Find status of all file in `names`.
3351 # Find status of all file in `names`.
3344 m = scmutil.matchfiles(repo, names)
3352 m = scmutil.matchfiles(repo, names)
3345
3353
3346 changes = repo.status(
3354 changes = repo.status(
3347 node1=node, match=m, unknown=True, ignored=True, clean=True
3355 node1=node, match=m, unknown=True, ignored=True, clean=True
3348 )
3356 )
3349 else:
3357 else:
3350 changes = repo.status(node1=node, match=m)
3358 changes = repo.status(node1=node, match=m)
3351 for kind in changes:
3359 for kind in changes:
3352 for abs in kind:
3360 for abs in kind:
3353 names[abs] = m.exact(abs)
3361 names[abs] = m.exact(abs)
3354
3362
3355 m = scmutil.matchfiles(repo, names)
3363 m = scmutil.matchfiles(repo, names)
3356
3364
3357 modified = set(changes.modified)
3365 modified = set(changes.modified)
3358 added = set(changes.added)
3366 added = set(changes.added)
3359 removed = set(changes.removed)
3367 removed = set(changes.removed)
3360 _deleted = set(changes.deleted)
3368 _deleted = set(changes.deleted)
3361 unknown = set(changes.unknown)
3369 unknown = set(changes.unknown)
3362 unknown.update(changes.ignored)
3370 unknown.update(changes.ignored)
3363 clean = set(changes.clean)
3371 clean = set(changes.clean)
3364 modadded = set()
3372 modadded = set()
3365
3373
3366 # We need to account for the state of the file in the dirstate,
3374 # We need to account for the state of the file in the dirstate,
3367 # even when we revert against something else than parent. This will
3375 # even when we revert against something else than parent. This will
3368 # slightly alter the behavior of revert (doing back up or not, delete
3376 # slightly alter the behavior of revert (doing back up or not, delete
3369 # or just forget etc).
3377 # or just forget etc).
3370 if parent == node:
3378 if parent == node:
3371 dsmodified = modified
3379 dsmodified = modified
3372 dsadded = added
3380 dsadded = added
3373 dsremoved = removed
3381 dsremoved = removed
3374 # store all local modifications, useful later for rename detection
3382 # store all local modifications, useful later for rename detection
3375 localchanges = dsmodified | dsadded
3383 localchanges = dsmodified | dsadded
3376 modified, added, removed = set(), set(), set()
3384 modified, added, removed = set(), set(), set()
3377 else:
3385 else:
3378 changes = repo.status(node1=parent, match=m)
3386 changes = repo.status(node1=parent, match=m)
3379 dsmodified = set(changes.modified)
3387 dsmodified = set(changes.modified)
3380 dsadded = set(changes.added)
3388 dsadded = set(changes.added)
3381 dsremoved = set(changes.removed)
3389 dsremoved = set(changes.removed)
3382 # store all local modifications, useful later for rename detection
3390 # store all local modifications, useful later for rename detection
3383 localchanges = dsmodified | dsadded
3391 localchanges = dsmodified | dsadded
3384
3392
3385 # only take into account for removes between wc and target
3393 # only take into account for removes between wc and target
3386 clean |= dsremoved - removed
3394 clean |= dsremoved - removed
3387 dsremoved &= removed
3395 dsremoved &= removed
3388 # distinct between dirstate remove and other
3396 # distinct between dirstate remove and other
3389 removed -= dsremoved
3397 removed -= dsremoved
3390
3398
3391 modadded = added & dsmodified
3399 modadded = added & dsmodified
3392 added -= modadded
3400 added -= modadded
3393
3401
3394 # tell newly modified apart.
3402 # tell newly modified apart.
3395 dsmodified &= modified
3403 dsmodified &= modified
3396 dsmodified |= modified & dsadded # dirstate added may need backup
3404 dsmodified |= modified & dsadded # dirstate added may need backup
3397 modified -= dsmodified
3405 modified -= dsmodified
3398
3406
3399 # We need to wait for some post-processing to update this set
3407 # We need to wait for some post-processing to update this set
3400 # before making the distinction. The dirstate will be used for
3408 # before making the distinction. The dirstate will be used for
3401 # that purpose.
3409 # that purpose.
3402 dsadded = added
3410 dsadded = added
3403
3411
3404 # in case of merge, files that are actually added can be reported as
3412 # in case of merge, files that are actually added can be reported as
3405 # modified, we need to post process the result
3413 # modified, we need to post process the result
3406 if p2 != nullid:
3414 if p2 != nullid:
3407 mergeadd = set(dsmodified)
3415 mergeadd = set(dsmodified)
3408 for path in dsmodified:
3416 for path in dsmodified:
3409 if path in mf:
3417 if path in mf:
3410 mergeadd.remove(path)
3418 mergeadd.remove(path)
3411 dsadded |= mergeadd
3419 dsadded |= mergeadd
3412 dsmodified -= mergeadd
3420 dsmodified -= mergeadd
3413
3421
3414 # if f is a rename, update `names` to also revert the source
3422 # if f is a rename, update `names` to also revert the source
3415 for f in localchanges:
3423 for f in localchanges:
3416 src = repo.dirstate.copied(f)
3424 src = repo.dirstate.copied(f)
3417 # XXX should we check for rename down to target node?
3425 # XXX should we check for rename down to target node?
3418 if src and src not in names and repo.dirstate[src] == b'r':
3426 if src and src not in names and repo.dirstate[src] == b'r':
3419 dsremoved.add(src)
3427 dsremoved.add(src)
3420 names[src] = True
3428 names[src] = True
3421
3429
3422 # determine the exact nature of the deleted changesets
3430 # determine the exact nature of the deleted changesets
3423 deladded = set(_deleted)
3431 deladded = set(_deleted)
3424 for path in _deleted:
3432 for path in _deleted:
3425 if path in mf:
3433 if path in mf:
3426 deladded.remove(path)
3434 deladded.remove(path)
3427 deleted = _deleted - deladded
3435 deleted = _deleted - deladded
3428
3436
3429 # distinguish between file to forget and the other
3437 # distinguish between file to forget and the other
3430 added = set()
3438 added = set()
3431 for abs in dsadded:
3439 for abs in dsadded:
3432 if repo.dirstate[abs] != b'a':
3440 if repo.dirstate[abs] != b'a':
3433 added.add(abs)
3441 added.add(abs)
3434 dsadded -= added
3442 dsadded -= added
3435
3443
3436 for abs in deladded:
3444 for abs in deladded:
3437 if repo.dirstate[abs] == b'a':
3445 if repo.dirstate[abs] == b'a':
3438 dsadded.add(abs)
3446 dsadded.add(abs)
3439 deladded -= dsadded
3447 deladded -= dsadded
3440
3448
3441 # For files marked as removed, we check if an unknown file is present at
3449 # For files marked as removed, we check if an unknown file is present at
3442 # the same path. If a such file exists it may need to be backed up.
3450 # the same path. If a such file exists it may need to be backed up.
3443 # Making the distinction at this stage helps have simpler backup
3451 # Making the distinction at this stage helps have simpler backup
3444 # logic.
3452 # logic.
3445 removunk = set()
3453 removunk = set()
3446 for abs in removed:
3454 for abs in removed:
3447 target = repo.wjoin(abs)
3455 target = repo.wjoin(abs)
3448 if os.path.lexists(target):
3456 if os.path.lexists(target):
3449 removunk.add(abs)
3457 removunk.add(abs)
3450 removed -= removunk
3458 removed -= removunk
3451
3459
3452 dsremovunk = set()
3460 dsremovunk = set()
3453 for abs in dsremoved:
3461 for abs in dsremoved:
3454 target = repo.wjoin(abs)
3462 target = repo.wjoin(abs)
3455 if os.path.lexists(target):
3463 if os.path.lexists(target):
3456 dsremovunk.add(abs)
3464 dsremovunk.add(abs)
3457 dsremoved -= dsremovunk
3465 dsremoved -= dsremovunk
3458
3466
3459 # action to be actually performed by revert
3467 # action to be actually performed by revert
3460 # (<list of file>, message>) tuple
3468 # (<list of file>, message>) tuple
3461 actions = {
3469 actions = {
3462 b'revert': ([], _(b'reverting %s\n')),
3470 b'revert': ([], _(b'reverting %s\n')),
3463 b'add': ([], _(b'adding %s\n')),
3471 b'add': ([], _(b'adding %s\n')),
3464 b'remove': ([], _(b'removing %s\n')),
3472 b'remove': ([], _(b'removing %s\n')),
3465 b'drop': ([], _(b'removing %s\n')),
3473 b'drop': ([], _(b'removing %s\n')),
3466 b'forget': ([], _(b'forgetting %s\n')),
3474 b'forget': ([], _(b'forgetting %s\n')),
3467 b'undelete': ([], _(b'undeleting %s\n')),
3475 b'undelete': ([], _(b'undeleting %s\n')),
3468 b'noop': (None, _(b'no changes needed to %s\n')),
3476 b'noop': (None, _(b'no changes needed to %s\n')),
3469 b'unknown': (None, _(b'file not managed: %s\n')),
3477 b'unknown': (None, _(b'file not managed: %s\n')),
3470 }
3478 }
3471
3479
3472 # "constant" that convey the backup strategy.
3480 # "constant" that convey the backup strategy.
3473 # All set to `discard` if `no-backup` is set do avoid checking
3481 # All set to `discard` if `no-backup` is set do avoid checking
3474 # no_backup lower in the code.
3482 # no_backup lower in the code.
3475 # These values are ordered for comparison purposes
3483 # These values are ordered for comparison purposes
3476 backupinteractive = 3 # do backup if interactively modified
3484 backupinteractive = 3 # do backup if interactively modified
3477 backup = 2 # unconditionally do backup
3485 backup = 2 # unconditionally do backup
3478 check = 1 # check if the existing file differs from target
3486 check = 1 # check if the existing file differs from target
3479 discard = 0 # never do backup
3487 discard = 0 # never do backup
3480 if opts.get(b'no_backup'):
3488 if opts.get(b'no_backup'):
3481 backupinteractive = backup = check = discard
3489 backupinteractive = backup = check = discard
3482 if interactive:
3490 if interactive:
3483 dsmodifiedbackup = backupinteractive
3491 dsmodifiedbackup = backupinteractive
3484 else:
3492 else:
3485 dsmodifiedbackup = backup
3493 dsmodifiedbackup = backup
3486 tobackup = set()
3494 tobackup = set()
3487
3495
3488 backupanddel = actions[b'remove']
3496 backupanddel = actions[b'remove']
3489 if not opts.get(b'no_backup'):
3497 if not opts.get(b'no_backup'):
3490 backupanddel = actions[b'drop']
3498 backupanddel = actions[b'drop']
3491
3499
3492 disptable = (
3500 disptable = (
3493 # dispatch table:
3501 # dispatch table:
3494 # file state
3502 # file state
3495 # action
3503 # action
3496 # make backup
3504 # make backup
3497 ## Sets that results that will change file on disk
3505 ## Sets that results that will change file on disk
3498 # Modified compared to target, no local change
3506 # Modified compared to target, no local change
3499 (modified, actions[b'revert'], discard),
3507 (modified, actions[b'revert'], discard),
3500 # Modified compared to target, but local file is deleted
3508 # Modified compared to target, but local file is deleted
3501 (deleted, actions[b'revert'], discard),
3509 (deleted, actions[b'revert'], discard),
3502 # Modified compared to target, local change
3510 # Modified compared to target, local change
3503 (dsmodified, actions[b'revert'], dsmodifiedbackup),
3511 (dsmodified, actions[b'revert'], dsmodifiedbackup),
3504 # Added since target
3512 # Added since target
3505 (added, actions[b'remove'], discard),
3513 (added, actions[b'remove'], discard),
3506 # Added in working directory
3514 # Added in working directory
3507 (dsadded, actions[b'forget'], discard),
3515 (dsadded, actions[b'forget'], discard),
3508 # Added since target, have local modification
3516 # Added since target, have local modification
3509 (modadded, backupanddel, backup),
3517 (modadded, backupanddel, backup),
3510 # Added since target but file is missing in working directory
3518 # Added since target but file is missing in working directory
3511 (deladded, actions[b'drop'], discard),
3519 (deladded, actions[b'drop'], discard),
3512 # Removed since target, before working copy parent
3520 # Removed since target, before working copy parent
3513 (removed, actions[b'add'], discard),
3521 (removed, actions[b'add'], discard),
3514 # Same as `removed` but an unknown file exists at the same path
3522 # Same as `removed` but an unknown file exists at the same path
3515 (removunk, actions[b'add'], check),
3523 (removunk, actions[b'add'], check),
3516 # Removed since targe, marked as such in working copy parent
3524 # Removed since targe, marked as such in working copy parent
3517 (dsremoved, actions[b'undelete'], discard),
3525 (dsremoved, actions[b'undelete'], discard),
3518 # Same as `dsremoved` but an unknown file exists at the same path
3526 # Same as `dsremoved` but an unknown file exists at the same path
3519 (dsremovunk, actions[b'undelete'], check),
3527 (dsremovunk, actions[b'undelete'], check),
3520 ## the following sets does not result in any file changes
3528 ## the following sets does not result in any file changes
3521 # File with no modification
3529 # File with no modification
3522 (clean, actions[b'noop'], discard),
3530 (clean, actions[b'noop'], discard),
3523 # Existing file, not tracked anywhere
3531 # Existing file, not tracked anywhere
3524 (unknown, actions[b'unknown'], discard),
3532 (unknown, actions[b'unknown'], discard),
3525 )
3533 )
3526
3534
3527 for abs, exact in sorted(names.items()):
3535 for abs, exact in sorted(names.items()):
3528 # target file to be touch on disk (relative to cwd)
3536 # target file to be touch on disk (relative to cwd)
3529 target = repo.wjoin(abs)
3537 target = repo.wjoin(abs)
3530 # search the entry in the dispatch table.
3538 # search the entry in the dispatch table.
3531 # if the file is in any of these sets, it was touched in the working
3539 # if the file is in any of these sets, it was touched in the working
3532 # directory parent and we are sure it needs to be reverted.
3540 # directory parent and we are sure it needs to be reverted.
3533 for table, (xlist, msg), dobackup in disptable:
3541 for table, (xlist, msg), dobackup in disptable:
3534 if abs not in table:
3542 if abs not in table:
3535 continue
3543 continue
3536 if xlist is not None:
3544 if xlist is not None:
3537 xlist.append(abs)
3545 xlist.append(abs)
3538 if dobackup:
3546 if dobackup:
3539 # If in interactive mode, don't automatically create
3547 # If in interactive mode, don't automatically create
3540 # .orig files (issue4793)
3548 # .orig files (issue4793)
3541 if dobackup == backupinteractive:
3549 if dobackup == backupinteractive:
3542 tobackup.add(abs)
3550 tobackup.add(abs)
3543 elif backup <= dobackup or wctx[abs].cmp(ctx[abs]):
3551 elif backup <= dobackup or wctx[abs].cmp(ctx[abs]):
3544 absbakname = scmutil.backuppath(ui, repo, abs)
3552 absbakname = scmutil.backuppath(ui, repo, abs)
3545 bakname = os.path.relpath(
3553 bakname = os.path.relpath(
3546 absbakname, start=repo.root
3554 absbakname, start=repo.root
3547 )
3555 )
3548 ui.note(
3556 ui.note(
3549 _(b'saving current version of %s as %s\n')
3557 _(b'saving current version of %s as %s\n')
3550 % (uipathfn(abs), uipathfn(bakname))
3558 % (uipathfn(abs), uipathfn(bakname))
3551 )
3559 )
3552 if not opts.get(b'dry_run'):
3560 if not opts.get(b'dry_run'):
3553 if interactive:
3561 if interactive:
3554 util.copyfile(target, absbakname)
3562 util.copyfile(target, absbakname)
3555 else:
3563 else:
3556 util.rename(target, absbakname)
3564 util.rename(target, absbakname)
3557 if opts.get(b'dry_run'):
3565 if opts.get(b'dry_run'):
3558 if ui.verbose or not exact:
3566 if ui.verbose or not exact:
3559 ui.status(msg % uipathfn(abs))
3567 ui.status(msg % uipathfn(abs))
3560 elif exact:
3568 elif exact:
3561 ui.warn(msg % uipathfn(abs))
3569 ui.warn(msg % uipathfn(abs))
3562 break
3570 break
3563
3571
3564 if not opts.get(b'dry_run'):
3572 if not opts.get(b'dry_run'):
3565 needdata = (b'revert', b'add', b'undelete')
3573 needdata = (b'revert', b'add', b'undelete')
3566 oplist = [actions[name][0] for name in needdata]
3574 oplist = [actions[name][0] for name in needdata]
3567 prefetch = scmutil.prefetchfiles
3575 prefetch = scmutil.prefetchfiles
3568 matchfiles = scmutil.matchfiles
3576 matchfiles = scmutil.matchfiles
3569 prefetch(
3577 prefetch(
3570 repo,
3578 repo,
3571 [ctx.rev()],
3579 [ctx.rev()],
3572 matchfiles(repo, [f for sublist in oplist for f in sublist]),
3580 matchfiles(repo, [f for sublist in oplist for f in sublist]),
3573 )
3581 )
3574 match = scmutil.match(repo[None], pats)
3582 match = scmutil.match(repo[None], pats)
3575 _performrevert(
3583 _performrevert(
3576 repo,
3584 repo,
3577 parents,
3585 parents,
3578 ctx,
3586 ctx,
3579 names,
3587 names,
3580 uipathfn,
3588 uipathfn,
3581 actions,
3589 actions,
3582 match,
3590 match,
3583 interactive,
3591 interactive,
3584 tobackup,
3592 tobackup,
3585 )
3593 )
3586
3594
3587 if targetsubs:
3595 if targetsubs:
3588 # Revert the subrepos on the revert list
3596 # Revert the subrepos on the revert list
3589 for sub in targetsubs:
3597 for sub in targetsubs:
3590 try:
3598 try:
3591 wctx.sub(sub).revert(
3599 wctx.sub(sub).revert(
3592 ctx.substate[sub], *pats, **pycompat.strkwargs(opts)
3600 ctx.substate[sub], *pats, **pycompat.strkwargs(opts)
3593 )
3601 )
3594 except KeyError:
3602 except KeyError:
3595 raise error.Abort(
3603 raise error.Abort(
3596 b"subrepository '%s' does not exist in %s!"
3604 b"subrepository '%s' does not exist in %s!"
3597 % (sub, short(ctx.node()))
3605 % (sub, short(ctx.node()))
3598 )
3606 )
3599
3607
3600
3608
3601 def _performrevert(
3609 def _performrevert(
3602 repo,
3610 repo,
3603 parents,
3611 parents,
3604 ctx,
3612 ctx,
3605 names,
3613 names,
3606 uipathfn,
3614 uipathfn,
3607 actions,
3615 actions,
3608 match,
3616 match,
3609 interactive=False,
3617 interactive=False,
3610 tobackup=None,
3618 tobackup=None,
3611 ):
3619 ):
3612 """function that actually perform all the actions computed for revert
3620 """function that actually perform all the actions computed for revert
3613
3621
3614 This is an independent function to let extension to plug in and react to
3622 This is an independent function to let extension to plug in and react to
3615 the imminent revert.
3623 the imminent revert.
3616
3624
3617 Make sure you have the working directory locked when calling this function.
3625 Make sure you have the working directory locked when calling this function.
3618 """
3626 """
3619 parent, p2 = parents
3627 parent, p2 = parents
3620 node = ctx.node()
3628 node = ctx.node()
3621 excluded_files = []
3629 excluded_files = []
3622
3630
3623 def checkout(f):
3631 def checkout(f):
3624 fc = ctx[f]
3632 fc = ctx[f]
3625 repo.wwrite(f, fc.data(), fc.flags())
3633 repo.wwrite(f, fc.data(), fc.flags())
3626
3634
3627 def doremove(f):
3635 def doremove(f):
3628 try:
3636 try:
3629 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
3637 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
3630 repo.wvfs.unlinkpath(f, rmdir=rmdir)
3638 repo.wvfs.unlinkpath(f, rmdir=rmdir)
3631 except OSError:
3639 except OSError:
3632 pass
3640 pass
3633 repo.dirstate.remove(f)
3641 repo.dirstate.remove(f)
3634
3642
3635 def prntstatusmsg(action, f):
3643 def prntstatusmsg(action, f):
3636 exact = names[f]
3644 exact = names[f]
3637 if repo.ui.verbose or not exact:
3645 if repo.ui.verbose or not exact:
3638 repo.ui.status(actions[action][1] % uipathfn(f))
3646 repo.ui.status(actions[action][1] % uipathfn(f))
3639
3647
3640 audit_path = pathutil.pathauditor(repo.root, cached=True)
3648 audit_path = pathutil.pathauditor(repo.root, cached=True)
3641 for f in actions[b'forget'][0]:
3649 for f in actions[b'forget'][0]:
3642 if interactive:
3650 if interactive:
3643 choice = repo.ui.promptchoice(
3651 choice = repo.ui.promptchoice(
3644 _(b"forget added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3652 _(b"forget added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3645 )
3653 )
3646 if choice == 0:
3654 if choice == 0:
3647 prntstatusmsg(b'forget', f)
3655 prntstatusmsg(b'forget', f)
3648 repo.dirstate.drop(f)
3656 repo.dirstate.drop(f)
3649 else:
3657 else:
3650 excluded_files.append(f)
3658 excluded_files.append(f)
3651 else:
3659 else:
3652 prntstatusmsg(b'forget', f)
3660 prntstatusmsg(b'forget', f)
3653 repo.dirstate.drop(f)
3661 repo.dirstate.drop(f)
3654 for f in actions[b'remove'][0]:
3662 for f in actions[b'remove'][0]:
3655 audit_path(f)
3663 audit_path(f)
3656 if interactive:
3664 if interactive:
3657 choice = repo.ui.promptchoice(
3665 choice = repo.ui.promptchoice(
3658 _(b"remove added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3666 _(b"remove added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3659 )
3667 )
3660 if choice == 0:
3668 if choice == 0:
3661 prntstatusmsg(b'remove', f)
3669 prntstatusmsg(b'remove', f)
3662 doremove(f)
3670 doremove(f)
3663 else:
3671 else:
3664 excluded_files.append(f)
3672 excluded_files.append(f)
3665 else:
3673 else:
3666 prntstatusmsg(b'remove', f)
3674 prntstatusmsg(b'remove', f)
3667 doremove(f)
3675 doremove(f)
3668 for f in actions[b'drop'][0]:
3676 for f in actions[b'drop'][0]:
3669 audit_path(f)
3677 audit_path(f)
3670 prntstatusmsg(b'drop', f)
3678 prntstatusmsg(b'drop', f)
3671 repo.dirstate.remove(f)
3679 repo.dirstate.remove(f)
3672
3680
3673 normal = None
3681 normal = None
3674 if node == parent:
3682 if node == parent:
3675 # We're reverting to our parent. If possible, we'd like status
3683 # We're reverting to our parent. If possible, we'd like status
3676 # to report the file as clean. We have to use normallookup for
3684 # to report the file as clean. We have to use normallookup for
3677 # merges to avoid losing information about merged/dirty files.
3685 # merges to avoid losing information about merged/dirty files.
3678 if p2 != nullid:
3686 if p2 != nullid:
3679 normal = repo.dirstate.normallookup
3687 normal = repo.dirstate.normallookup
3680 else:
3688 else:
3681 normal = repo.dirstate.normal
3689 normal = repo.dirstate.normal
3682
3690
3683 newlyaddedandmodifiedfiles = set()
3691 newlyaddedandmodifiedfiles = set()
3684 if interactive:
3692 if interactive:
3685 # Prompt the user for changes to revert
3693 # Prompt the user for changes to revert
3686 torevert = [f for f in actions[b'revert'][0] if f not in excluded_files]
3694 torevert = [f for f in actions[b'revert'][0] if f not in excluded_files]
3687 m = scmutil.matchfiles(repo, torevert)
3695 m = scmutil.matchfiles(repo, torevert)
3688 diffopts = patch.difffeatureopts(
3696 diffopts = patch.difffeatureopts(
3689 repo.ui,
3697 repo.ui,
3690 whitespace=True,
3698 whitespace=True,
3691 section=b'commands',
3699 section=b'commands',
3692 configprefix=b'revert.interactive.',
3700 configprefix=b'revert.interactive.',
3693 )
3701 )
3694 diffopts.nodates = True
3702 diffopts.nodates = True
3695 diffopts.git = True
3703 diffopts.git = True
3696 operation = b'apply'
3704 operation = b'apply'
3697 if node == parent:
3705 if node == parent:
3698 if repo.ui.configbool(
3706 if repo.ui.configbool(
3699 b'experimental', b'revert.interactive.select-to-keep'
3707 b'experimental', b'revert.interactive.select-to-keep'
3700 ):
3708 ):
3701 operation = b'keep'
3709 operation = b'keep'
3702 else:
3710 else:
3703 operation = b'discard'
3711 operation = b'discard'
3704
3712
3705 if operation == b'apply':
3713 if operation == b'apply':
3706 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3714 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3707 else:
3715 else:
3708 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3716 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3709 originalchunks = patch.parsepatch(diff)
3717 originalchunks = patch.parsepatch(diff)
3710
3718
3711 try:
3719 try:
3712
3720
3713 chunks, opts = recordfilter(
3721 chunks, opts = recordfilter(
3714 repo.ui, originalchunks, match, operation=operation
3722 repo.ui, originalchunks, match, operation=operation
3715 )
3723 )
3716 if operation == b'discard':
3724 if operation == b'discard':
3717 chunks = patch.reversehunks(chunks)
3725 chunks = patch.reversehunks(chunks)
3718
3726
3719 except error.PatchError as err:
3727 except error.PatchError as err:
3720 raise error.Abort(_(b'error parsing patch: %s') % err)
3728 raise error.Abort(_(b'error parsing patch: %s') % err)
3721
3729
3722 # FIXME: when doing an interactive revert of a copy, there's no way of
3730 # FIXME: when doing an interactive revert of a copy, there's no way of
3723 # performing a partial revert of the added file, the only option is
3731 # performing a partial revert of the added file, the only option is
3724 # "remove added file <name> (Yn)?", so we don't need to worry about the
3732 # "remove added file <name> (Yn)?", so we don't need to worry about the
3725 # alsorestore value. Ideally we'd be able to partially revert
3733 # alsorestore value. Ideally we'd be able to partially revert
3726 # copied/renamed files.
3734 # copied/renamed files.
3727 newlyaddedandmodifiedfiles, unusedalsorestore = newandmodified(
3735 newlyaddedandmodifiedfiles, unusedalsorestore = newandmodified(
3728 chunks, originalchunks
3736 chunks, originalchunks
3729 )
3737 )
3730 if tobackup is None:
3738 if tobackup is None:
3731 tobackup = set()
3739 tobackup = set()
3732 # Apply changes
3740 # Apply changes
3733 fp = stringio()
3741 fp = stringio()
3734 # chunks are serialized per file, but files aren't sorted
3742 # chunks are serialized per file, but files aren't sorted
3735 for f in sorted(set(c.header.filename() for c in chunks if ishunk(c))):
3743 for f in sorted(set(c.header.filename() for c in chunks if ishunk(c))):
3736 prntstatusmsg(b'revert', f)
3744 prntstatusmsg(b'revert', f)
3737 files = set()
3745 files = set()
3738 for c in chunks:
3746 for c in chunks:
3739 if ishunk(c):
3747 if ishunk(c):
3740 abs = c.header.filename()
3748 abs = c.header.filename()
3741 # Create a backup file only if this hunk should be backed up
3749 # Create a backup file only if this hunk should be backed up
3742 if c.header.filename() in tobackup:
3750 if c.header.filename() in tobackup:
3743 target = repo.wjoin(abs)
3751 target = repo.wjoin(abs)
3744 bakname = scmutil.backuppath(repo.ui, repo, abs)
3752 bakname = scmutil.backuppath(repo.ui, repo, abs)
3745 util.copyfile(target, bakname)
3753 util.copyfile(target, bakname)
3746 tobackup.remove(abs)
3754 tobackup.remove(abs)
3747 if abs not in files:
3755 if abs not in files:
3748 files.add(abs)
3756 files.add(abs)
3749 if operation == b'keep':
3757 if operation == b'keep':
3750 checkout(abs)
3758 checkout(abs)
3751 c.write(fp)
3759 c.write(fp)
3752 dopatch = fp.tell()
3760 dopatch = fp.tell()
3753 fp.seek(0)
3761 fp.seek(0)
3754 if dopatch:
3762 if dopatch:
3755 try:
3763 try:
3756 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3764 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3757 except error.PatchError as err:
3765 except error.PatchError as err:
3758 raise error.Abort(pycompat.bytestr(err))
3766 raise error.Abort(pycompat.bytestr(err))
3759 del fp
3767 del fp
3760 else:
3768 else:
3761 for f in actions[b'revert'][0]:
3769 for f in actions[b'revert'][0]:
3762 prntstatusmsg(b'revert', f)
3770 prntstatusmsg(b'revert', f)
3763 checkout(f)
3771 checkout(f)
3764 if normal:
3772 if normal:
3765 normal(f)
3773 normal(f)
3766
3774
3767 for f in actions[b'add'][0]:
3775 for f in actions[b'add'][0]:
3768 # Don't checkout modified files, they are already created by the diff
3776 # Don't checkout modified files, they are already created by the diff
3769 if f not in newlyaddedandmodifiedfiles:
3777 if f not in newlyaddedandmodifiedfiles:
3770 prntstatusmsg(b'add', f)
3778 prntstatusmsg(b'add', f)
3771 checkout(f)
3779 checkout(f)
3772 repo.dirstate.add(f)
3780 repo.dirstate.add(f)
3773
3781
3774 normal = repo.dirstate.normallookup
3782 normal = repo.dirstate.normallookup
3775 if node == parent and p2 == nullid:
3783 if node == parent and p2 == nullid:
3776 normal = repo.dirstate.normal
3784 normal = repo.dirstate.normal
3777 for f in actions[b'undelete'][0]:
3785 for f in actions[b'undelete'][0]:
3778 if interactive:
3786 if interactive:
3779 choice = repo.ui.promptchoice(
3787 choice = repo.ui.promptchoice(
3780 _(b"add back removed file %s (Yn)?$$ &Yes $$ &No") % f
3788 _(b"add back removed file %s (Yn)?$$ &Yes $$ &No") % f
3781 )
3789 )
3782 if choice == 0:
3790 if choice == 0:
3783 prntstatusmsg(b'undelete', f)
3791 prntstatusmsg(b'undelete', f)
3784 checkout(f)
3792 checkout(f)
3785 normal(f)
3793 normal(f)
3786 else:
3794 else:
3787 excluded_files.append(f)
3795 excluded_files.append(f)
3788 else:
3796 else:
3789 prntstatusmsg(b'undelete', f)
3797 prntstatusmsg(b'undelete', f)
3790 checkout(f)
3798 checkout(f)
3791 normal(f)
3799 normal(f)
3792
3800
3793 copied = copies.pathcopies(repo[parent], ctx)
3801 copied = copies.pathcopies(repo[parent], ctx)
3794
3802
3795 for f in (
3803 for f in (
3796 actions[b'add'][0] + actions[b'undelete'][0] + actions[b'revert'][0]
3804 actions[b'add'][0] + actions[b'undelete'][0] + actions[b'revert'][0]
3797 ):
3805 ):
3798 if f in copied:
3806 if f in copied:
3799 repo.dirstate.copy(copied[f], f)
3807 repo.dirstate.copy(copied[f], f)
3800
3808
3801
3809
3802 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3810 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3803 # commands.outgoing. "missing" is "missing" of the result of
3811 # commands.outgoing. "missing" is "missing" of the result of
3804 # "findcommonoutgoing()"
3812 # "findcommonoutgoing()"
3805 outgoinghooks = util.hooks()
3813 outgoinghooks = util.hooks()
3806
3814
3807 # a list of (ui, repo) functions called by commands.summary
3815 # a list of (ui, repo) functions called by commands.summary
3808 summaryhooks = util.hooks()
3816 summaryhooks = util.hooks()
3809
3817
3810 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3818 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3811 #
3819 #
3812 # functions should return tuple of booleans below, if 'changes' is None:
3820 # functions should return tuple of booleans below, if 'changes' is None:
3813 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3821 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3814 #
3822 #
3815 # otherwise, 'changes' is a tuple of tuples below:
3823 # otherwise, 'changes' is a tuple of tuples below:
3816 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3824 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3817 # - (desturl, destbranch, destpeer, outgoing)
3825 # - (desturl, destbranch, destpeer, outgoing)
3818 summaryremotehooks = util.hooks()
3826 summaryremotehooks = util.hooks()
3819
3827
3820
3828
3821 def checkunfinished(repo, commit=False, skipmerge=False):
3829 def checkunfinished(repo, commit=False, skipmerge=False):
3822 '''Look for an unfinished multistep operation, like graft, and abort
3830 '''Look for an unfinished multistep operation, like graft, and abort
3823 if found. It's probably good to check this right before
3831 if found. It's probably good to check this right before
3824 bailifchanged().
3832 bailifchanged().
3825 '''
3833 '''
3826 # Check for non-clearable states first, so things like rebase will take
3834 # Check for non-clearable states first, so things like rebase will take
3827 # precedence over update.
3835 # precedence over update.
3828 for state in statemod._unfinishedstates:
3836 for state in statemod._unfinishedstates:
3829 if (
3837 if (
3830 state._clearable
3838 state._clearable
3831 or (commit and state._allowcommit)
3839 or (commit and state._allowcommit)
3832 or state._reportonly
3840 or state._reportonly
3833 ):
3841 ):
3834 continue
3842 continue
3835 if state.isunfinished(repo):
3843 if state.isunfinished(repo):
3836 raise error.Abort(state.msg(), hint=state.hint())
3844 raise error.Abort(state.msg(), hint=state.hint())
3837
3845
3838 for s in statemod._unfinishedstates:
3846 for s in statemod._unfinishedstates:
3839 if (
3847 if (
3840 not s._clearable
3848 not s._clearable
3841 or (commit and s._allowcommit)
3849 or (commit and s._allowcommit)
3842 or (s._opname == b'merge' and skipmerge)
3850 or (s._opname == b'merge' and skipmerge)
3843 or s._reportonly
3851 or s._reportonly
3844 ):
3852 ):
3845 continue
3853 continue
3846 if s.isunfinished(repo):
3854 if s.isunfinished(repo):
3847 raise error.Abort(s.msg(), hint=s.hint())
3855 raise error.Abort(s.msg(), hint=s.hint())
3848
3856
3849
3857
3850 def clearunfinished(repo):
3858 def clearunfinished(repo):
3851 '''Check for unfinished operations (as above), and clear the ones
3859 '''Check for unfinished operations (as above), and clear the ones
3852 that are clearable.
3860 that are clearable.
3853 '''
3861 '''
3854 for state in statemod._unfinishedstates:
3862 for state in statemod._unfinishedstates:
3855 if state._reportonly:
3863 if state._reportonly:
3856 continue
3864 continue
3857 if not state._clearable and state.isunfinished(repo):
3865 if not state._clearable and state.isunfinished(repo):
3858 raise error.Abort(state.msg(), hint=state.hint())
3866 raise error.Abort(state.msg(), hint=state.hint())
3859
3867
3860 for s in statemod._unfinishedstates:
3868 for s in statemod._unfinishedstates:
3861 if s._opname == b'merge' or state._reportonly:
3869 if s._opname == b'merge' or state._reportonly:
3862 continue
3870 continue
3863 if s._clearable and s.isunfinished(repo):
3871 if s._clearable and s.isunfinished(repo):
3864 util.unlink(repo.vfs.join(s._fname))
3872 util.unlink(repo.vfs.join(s._fname))
3865
3873
3866
3874
3867 def getunfinishedstate(repo):
3875 def getunfinishedstate(repo):
3868 ''' Checks for unfinished operations and returns statecheck object
3876 ''' Checks for unfinished operations and returns statecheck object
3869 for it'''
3877 for it'''
3870 for state in statemod._unfinishedstates:
3878 for state in statemod._unfinishedstates:
3871 if state.isunfinished(repo):
3879 if state.isunfinished(repo):
3872 return state
3880 return state
3873 return None
3881 return None
3874
3882
3875
3883
3876 def howtocontinue(repo):
3884 def howtocontinue(repo):
3877 '''Check for an unfinished operation and return the command to finish
3885 '''Check for an unfinished operation and return the command to finish
3878 it.
3886 it.
3879
3887
3880 statemod._unfinishedstates list is checked for an unfinished operation
3888 statemod._unfinishedstates list is checked for an unfinished operation
3881 and the corresponding message to finish it is generated if a method to
3889 and the corresponding message to finish it is generated if a method to
3882 continue is supported by the operation.
3890 continue is supported by the operation.
3883
3891
3884 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3892 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3885 a boolean.
3893 a boolean.
3886 '''
3894 '''
3887 contmsg = _(b"continue: %s")
3895 contmsg = _(b"continue: %s")
3888 for state in statemod._unfinishedstates:
3896 for state in statemod._unfinishedstates:
3889 if not state._continueflag:
3897 if not state._continueflag:
3890 continue
3898 continue
3891 if state.isunfinished(repo):
3899 if state.isunfinished(repo):
3892 return contmsg % state.continuemsg(), True
3900 return contmsg % state.continuemsg(), True
3893 if repo[None].dirty(missing=True, merge=False, branch=False):
3901 if repo[None].dirty(missing=True, merge=False, branch=False):
3894 return contmsg % _(b"hg commit"), False
3902 return contmsg % _(b"hg commit"), False
3895 return None, None
3903 return None, None
3896
3904
3897
3905
3898 def checkafterresolved(repo):
3906 def checkafterresolved(repo):
3899 '''Inform the user about the next action after completing hg resolve
3907 '''Inform the user about the next action after completing hg resolve
3900
3908
3901 If there's a an unfinished operation that supports continue flag,
3909 If there's a an unfinished operation that supports continue flag,
3902 howtocontinue will yield repo.ui.warn as the reporter.
3910 howtocontinue will yield repo.ui.warn as the reporter.
3903
3911
3904 Otherwise, it will yield repo.ui.note.
3912 Otherwise, it will yield repo.ui.note.
3905 '''
3913 '''
3906 msg, warning = howtocontinue(repo)
3914 msg, warning = howtocontinue(repo)
3907 if msg is not None:
3915 if msg is not None:
3908 if warning:
3916 if warning:
3909 repo.ui.warn(b"%s\n" % msg)
3917 repo.ui.warn(b"%s\n" % msg)
3910 else:
3918 else:
3911 repo.ui.note(b"%s\n" % msg)
3919 repo.ui.note(b"%s\n" % msg)
3912
3920
3913
3921
3914 def wrongtooltocontinue(repo, task):
3922 def wrongtooltocontinue(repo, task):
3915 '''Raise an abort suggesting how to properly continue if there is an
3923 '''Raise an abort suggesting how to properly continue if there is an
3916 active task.
3924 active task.
3917
3925
3918 Uses howtocontinue() to find the active task.
3926 Uses howtocontinue() to find the active task.
3919
3927
3920 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3928 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3921 a hint.
3929 a hint.
3922 '''
3930 '''
3923 after = howtocontinue(repo)
3931 after = howtocontinue(repo)
3924 hint = None
3932 hint = None
3925 if after[1]:
3933 if after[1]:
3926 hint = after[0]
3934 hint = after[0]
3927 raise error.Abort(_(b'no %s in progress') % task, hint=hint)
3935 raise error.Abort(_(b'no %s in progress') % task, hint=hint)
3928
3936
3929
3937
3930 def abortgraft(ui, repo, graftstate):
3938 def abortgraft(ui, repo, graftstate):
3931 """abort the interrupted graft and rollbacks to the state before interrupted
3939 """abort the interrupted graft and rollbacks to the state before interrupted
3932 graft"""
3940 graft"""
3933 if not graftstate.exists():
3941 if not graftstate.exists():
3934 raise error.Abort(_(b"no interrupted graft to abort"))
3942 raise error.Abort(_(b"no interrupted graft to abort"))
3935 statedata = readgraftstate(repo, graftstate)
3943 statedata = readgraftstate(repo, graftstate)
3936 newnodes = statedata.get(b'newnodes')
3944 newnodes = statedata.get(b'newnodes')
3937 if newnodes is None:
3945 if newnodes is None:
3938 # and old graft state which does not have all the data required to abort
3946 # and old graft state which does not have all the data required to abort
3939 # the graft
3947 # the graft
3940 raise error.Abort(_(b"cannot abort using an old graftstate"))
3948 raise error.Abort(_(b"cannot abort using an old graftstate"))
3941
3949
3942 # changeset from which graft operation was started
3950 # changeset from which graft operation was started
3943 if len(newnodes) > 0:
3951 if len(newnodes) > 0:
3944 startctx = repo[newnodes[0]].p1()
3952 startctx = repo[newnodes[0]].p1()
3945 else:
3953 else:
3946 startctx = repo[b'.']
3954 startctx = repo[b'.']
3947 # whether to strip or not
3955 # whether to strip or not
3948 cleanup = False
3956 cleanup = False
3949 from . import hg
3957 from . import hg
3950
3958
3951 if newnodes:
3959 if newnodes:
3952 newnodes = [repo[r].rev() for r in newnodes]
3960 newnodes = [repo[r].rev() for r in newnodes]
3953 cleanup = True
3961 cleanup = True
3954 # checking that none of the newnodes turned public or is public
3962 # checking that none of the newnodes turned public or is public
3955 immutable = [c for c in newnodes if not repo[c].mutable()]
3963 immutable = [c for c in newnodes if not repo[c].mutable()]
3956 if immutable:
3964 if immutable:
3957 repo.ui.warn(
3965 repo.ui.warn(
3958 _(b"cannot clean up public changesets %s\n")
3966 _(b"cannot clean up public changesets %s\n")
3959 % b', '.join(bytes(repo[r]) for r in immutable),
3967 % b', '.join(bytes(repo[r]) for r in immutable),
3960 hint=_(b"see 'hg help phases' for details"),
3968 hint=_(b"see 'hg help phases' for details"),
3961 )
3969 )
3962 cleanup = False
3970 cleanup = False
3963
3971
3964 # checking that no new nodes are created on top of grafted revs
3972 # checking that no new nodes are created on top of grafted revs
3965 desc = set(repo.changelog.descendants(newnodes))
3973 desc = set(repo.changelog.descendants(newnodes))
3966 if desc - set(newnodes):
3974 if desc - set(newnodes):
3967 repo.ui.warn(
3975 repo.ui.warn(
3968 _(
3976 _(
3969 b"new changesets detected on destination "
3977 b"new changesets detected on destination "
3970 b"branch, can't strip\n"
3978 b"branch, can't strip\n"
3971 )
3979 )
3972 )
3980 )
3973 cleanup = False
3981 cleanup = False
3974
3982
3975 if cleanup:
3983 if cleanup:
3976 with repo.wlock(), repo.lock():
3984 with repo.wlock(), repo.lock():
3977 hg.updaterepo(repo, startctx.node(), overwrite=True)
3985 hg.updaterepo(repo, startctx.node(), overwrite=True)
3978 # stripping the new nodes created
3986 # stripping the new nodes created
3979 strippoints = [
3987 strippoints = [
3980 c.node() for c in repo.set(b"roots(%ld)", newnodes)
3988 c.node() for c in repo.set(b"roots(%ld)", newnodes)
3981 ]
3989 ]
3982 repair.strip(repo.ui, repo, strippoints, backup=False)
3990 repair.strip(repo.ui, repo, strippoints, backup=False)
3983
3991
3984 if not cleanup:
3992 if not cleanup:
3985 # we don't update to the startnode if we can't strip
3993 # we don't update to the startnode if we can't strip
3986 startctx = repo[b'.']
3994 startctx = repo[b'.']
3987 hg.updaterepo(repo, startctx.node(), overwrite=True)
3995 hg.updaterepo(repo, startctx.node(), overwrite=True)
3988
3996
3989 ui.status(_(b"graft aborted\n"))
3997 ui.status(_(b"graft aborted\n"))
3990 ui.status(_(b"working directory is now at %s\n") % startctx.hex()[:12])
3998 ui.status(_(b"working directory is now at %s\n") % startctx.hex()[:12])
3991 graftstate.delete()
3999 graftstate.delete()
3992 return 0
4000 return 0
3993
4001
3994
4002
3995 def readgraftstate(repo, graftstate):
4003 def readgraftstate(repo, graftstate):
3996 # type: (Any, statemod.cmdstate) -> Dict[bytes, Any]
4004 # type: (Any, statemod.cmdstate) -> Dict[bytes, Any]
3997 """read the graft state file and return a dict of the data stored in it"""
4005 """read the graft state file and return a dict of the data stored in it"""
3998 try:
4006 try:
3999 return graftstate.read()
4007 return graftstate.read()
4000 except error.CorruptedState:
4008 except error.CorruptedState:
4001 nodes = repo.vfs.read(b'graftstate').splitlines()
4009 nodes = repo.vfs.read(b'graftstate').splitlines()
4002 return {b'nodes': nodes}
4010 return {b'nodes': nodes}
4003
4011
4004
4012
4005 def hgabortgraft(ui, repo):
4013 def hgabortgraft(ui, repo):
4006 """ abort logic for aborting graft using 'hg abort'"""
4014 """ abort logic for aborting graft using 'hg abort'"""
4007 with repo.wlock():
4015 with repo.wlock():
4008 graftstate = statemod.cmdstate(repo, b'graftstate')
4016 graftstate = statemod.cmdstate(repo, b'graftstate')
4009 return abortgraft(ui, repo, graftstate)
4017 return abortgraft(ui, repo, graftstate)
General Comments 0
You need to be logged in to leave comments. Login now