##// END OF EJS Templates
check-code: complain about set.isdisjoint
Matt Mackall -
r14267:6332c02b default
parent child Browse files
Show More
@@ -1,376 +1,377 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # check-code - a style and portability checker for Mercurial
3 # check-code - a style and portability checker for Mercurial
4 #
4 #
5 # Copyright 2010 Matt Mackall <mpm@selenic.com>
5 # Copyright 2010 Matt Mackall <mpm@selenic.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 import re, glob, os, sys
10 import re, glob, os, sys
11 import keyword
11 import keyword
12 import optparse
12 import optparse
13
13
14 def repquote(m):
14 def repquote(m):
15 t = re.sub(r"\w", "x", m.group('text'))
15 t = re.sub(r"\w", "x", m.group('text'))
16 t = re.sub(r"[^\sx]", "o", t)
16 t = re.sub(r"[^\sx]", "o", t)
17 return m.group('quote') + t + m.group('quote')
17 return m.group('quote') + t + m.group('quote')
18
18
19 def reppython(m):
19 def reppython(m):
20 comment = m.group('comment')
20 comment = m.group('comment')
21 if comment:
21 if comment:
22 return "#" * len(comment)
22 return "#" * len(comment)
23 return repquote(m)
23 return repquote(m)
24
24
25 def repcomment(m):
25 def repcomment(m):
26 return m.group(1) + "#" * len(m.group(2))
26 return m.group(1) + "#" * len(m.group(2))
27
27
28 def repccomment(m):
28 def repccomment(m):
29 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
29 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
30 return m.group(1) + t + "*/"
30 return m.group(1) + t + "*/"
31
31
32 def repcallspaces(m):
32 def repcallspaces(m):
33 t = re.sub(r"\n\s+", "\n", m.group(2))
33 t = re.sub(r"\n\s+", "\n", m.group(2))
34 return m.group(1) + t
34 return m.group(1) + t
35
35
36 def repinclude(m):
36 def repinclude(m):
37 return m.group(1) + "<foo>"
37 return m.group(1) + "<foo>"
38
38
39 def rephere(m):
39 def rephere(m):
40 t = re.sub(r"\S", "x", m.group(2))
40 t = re.sub(r"\S", "x", m.group(2))
41 return m.group(1) + t
41 return m.group(1) + t
42
42
43
43
44 testpats = [
44 testpats = [
45 [
45 [
46 (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"),
46 (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"),
47 (r'\W\$?\(\([^\)]*\)\)', "don't use (()) or $(()), use 'expr'"),
47 (r'\W\$?\(\([^\)]*\)\)', "don't use (()) or $(()), use 'expr'"),
48 (r'^function', "don't use 'function', use old style"),
48 (r'^function', "don't use 'function', use old style"),
49 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
49 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
50 (r'echo.*\\n', "don't use 'echo \\n', use printf"),
50 (r'echo.*\\n', "don't use 'echo \\n', use printf"),
51 (r'echo -n', "don't use 'echo -n', use printf"),
51 (r'echo -n', "don't use 'echo -n', use printf"),
52 (r'^diff.*-\w*N', "don't use 'diff -N'"),
52 (r'^diff.*-\w*N', "don't use 'diff -N'"),
53 (r'(^| )wc[^|]*$', "filter wc output"),
53 (r'(^| )wc[^|]*$', "filter wc output"),
54 (r'head -c', "don't use 'head -c', use 'dd'"),
54 (r'head -c', "don't use 'head -c', use 'dd'"),
55 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
55 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
56 (r'printf.*\\\d\d\d', "don't use 'printf \NNN', use Python"),
56 (r'printf.*\\\d\d\d', "don't use 'printf \NNN', use Python"),
57 (r'printf.*\\x', "don't use printf \\x, use Python"),
57 (r'printf.*\\x', "don't use printf \\x, use Python"),
58 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
58 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
59 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
59 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
60 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
60 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
61 "use egrep for extended grep syntax"),
61 "use egrep for extended grep syntax"),
62 (r'/bin/', "don't use explicit paths for tools"),
62 (r'/bin/', "don't use explicit paths for tools"),
63 (r'\$PWD', "don't use $PWD, use `pwd`"),
63 (r'\$PWD', "don't use $PWD, use `pwd`"),
64 (r'[^\n]\Z', "no trailing newline"),
64 (r'[^\n]\Z', "no trailing newline"),
65 (r'export.*=', "don't export and assign at once"),
65 (r'export.*=', "don't export and assign at once"),
66 ('^([^"\']|("[^"]*")|(\'[^\']*\'))*\\^', "^ must be quoted"),
66 ('^([^"\']|("[^"]*")|(\'[^\']*\'))*\\^', "^ must be quoted"),
67 (r'^source\b', "don't use 'source', use '.'"),
67 (r'^source\b', "don't use 'source', use '.'"),
68 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
68 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
69 (r'ls\s+[^|-]+\s+-', "options to 'ls' must come before filenames"),
69 (r'ls\s+[^|-]+\s+-', "options to 'ls' must come before filenames"),
70 (r'[^>]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
70 (r'[^>]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
71 ],
71 ],
72 # warnings
72 # warnings
73 []
73 []
74 ]
74 ]
75
75
76 testfilters = [
76 testfilters = [
77 (r"( *)(#([^\n]*\S)?)", repcomment),
77 (r"( *)(#([^\n]*\S)?)", repcomment),
78 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
78 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
79 ]
79 ]
80
80
81 uprefix = r"^ \$ "
81 uprefix = r"^ \$ "
82 uprefixc = r"^ > "
82 uprefixc = r"^ > "
83 utestpats = [
83 utestpats = [
84 [
84 [
85 (r'^(\S| $ ).*(\S\s+|^\s+)\n', "trailing whitespace on non-output"),
85 (r'^(\S| $ ).*(\S\s+|^\s+)\n', "trailing whitespace on non-output"),
86 (uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"),
86 (uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"),
87 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
87 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
88 (uprefix + r'.*\$\?', "explicit exit code checks unnecessary"),
88 (uprefix + r'.*\$\?', "explicit exit code checks unnecessary"),
89 (uprefix + r'.*\|\| echo.*(fail|error)',
89 (uprefix + r'.*\|\| echo.*(fail|error)',
90 "explicit exit code checks unnecessary"),
90 "explicit exit code checks unnecessary"),
91 (uprefix + r'set -e', "don't use set -e"),
91 (uprefix + r'set -e', "don't use set -e"),
92 (uprefixc + r'( *)\t', "don't use tabs to indent"),
92 (uprefixc + r'( *)\t', "don't use tabs to indent"),
93 ],
93 ],
94 # warnings
94 # warnings
95 []
95 []
96 ]
96 ]
97
97
98 for i in [0, 1]:
98 for i in [0, 1]:
99 for p, m in testpats[i]:
99 for p, m in testpats[i]:
100 if p.startswith('^'):
100 if p.startswith('^'):
101 p = uprefix + p[1:]
101 p = uprefix + p[1:]
102 else:
102 else:
103 p = uprefix + p
103 p = uprefix + p
104 utestpats[i].append((p, m))
104 utestpats[i].append((p, m))
105
105
106 utestfilters = [
106 utestfilters = [
107 (r"( *)(#([^\n]*\S)?)", repcomment),
107 (r"( *)(#([^\n]*\S)?)", repcomment),
108 ]
108 ]
109
109
110 pypats = [
110 pypats = [
111 [
111 [
112 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
112 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
113 "tuple parameter unpacking not available in Python 3+"),
113 "tuple parameter unpacking not available in Python 3+"),
114 (r'lambda\s*\(.*,.*\)',
114 (r'lambda\s*\(.*,.*\)',
115 "tuple parameter unpacking not available in Python 3+"),
115 "tuple parameter unpacking not available in Python 3+"),
116 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
116 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
117 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
117 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
118 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
118 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
119 (r'^\s*\t', "don't use tabs"),
119 (r'^\s*\t', "don't use tabs"),
120 (r'\S;\s*\n', "semicolon"),
120 (r'\S;\s*\n', "semicolon"),
121 (r'\w,\w', "missing whitespace after ,"),
121 (r'\w,\w', "missing whitespace after ,"),
122 (r'\w[+/*\-<>]\w', "missing whitespace in expression"),
122 (r'\w[+/*\-<>]\w', "missing whitespace in expression"),
123 (r'^\s+\w+=\w+[^,)]$', "missing whitespace in assignment"),
123 (r'^\s+\w+=\w+[^,)]$', "missing whitespace in assignment"),
124 (r'.{85}', "line too long"),
124 (r'.{85}', "line too long"),
125 (r'[^\n]\Z', "no trailing newline"),
125 (r'[^\n]\Z', "no trailing newline"),
126 (r'(\S\s+|^\s+)\n', "trailing whitespace"),
126 (r'(\S\s+|^\s+)\n', "trailing whitespace"),
127 # (r'^\s+[^_ ][^_. ]+_[^_]+\s*=', "don't use underbars in identifiers"),
127 # (r'^\s+[^_ ][^_. ]+_[^_]+\s*=', "don't use underbars in identifiers"),
128 # (r'\w*[a-z][A-Z]\w*\s*=', "don't use camelcase in identifiers"),
128 # (r'\w*[a-z][A-Z]\w*\s*=', "don't use camelcase in identifiers"),
129 (r'^\s*(if|while|def|class|except|try)\s[^[]*:\s*[^\]#\s]+',
129 (r'^\s*(if|while|def|class|except|try)\s[^[]*:\s*[^\]#\s]+',
130 "linebreak after :"),
130 "linebreak after :"),
131 (r'class\s[^(]:', "old-style class, use class foo(object)"),
131 (r'class\s[^(]:', "old-style class, use class foo(object)"),
132 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
132 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
133 "Python keyword is not a function"),
133 "Python keyword is not a function"),
134 (r',]', "unneeded trailing ',' in list"),
134 (r',]', "unneeded trailing ',' in list"),
135 # (r'class\s[A-Z][^\(]*\((?!Exception)',
135 # (r'class\s[A-Z][^\(]*\((?!Exception)',
136 # "don't capitalize non-exception classes"),
136 # "don't capitalize non-exception classes"),
137 # (r'in range\(', "use xrange"),
137 # (r'in range\(', "use xrange"),
138 # (r'^\s*print\s+', "avoid using print in core and extensions"),
138 # (r'^\s*print\s+', "avoid using print in core and extensions"),
139 (r'[\x80-\xff]', "non-ASCII character literal"),
139 (r'[\x80-\xff]', "non-ASCII character literal"),
140 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
140 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
141 (r'^\s*with\s+', "with not available in Python 2.4"),
141 (r'^\s*with\s+', "with not available in Python 2.4"),
142 (r'\.isdisjoint\(', "set.isdisjoint not available in Python 2.4"),
142 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
143 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
143 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
144 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
144 (r'(?<!def)\s+(any|all|format)\(',
145 (r'(?<!def)\s+(any|all|format)\(',
145 "any/all/format not available in Python 2.4"),
146 "any/all/format not available in Python 2.4"),
146 (r'(?<!def)\s+(callable)\(',
147 (r'(?<!def)\s+(callable)\(',
147 "callable not available in Python 3, use hasattr(f, '__call__')"),
148 "callable not available in Python 3, use hasattr(f, '__call__')"),
148 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
149 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
149 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
150 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
150 "gratuitous whitespace after Python keyword"),
151 "gratuitous whitespace after Python keyword"),
151 (r'([\(\[]\s\S)|(\S\s[\)\]])', "gratuitous whitespace in () or []"),
152 (r'([\(\[]\s\S)|(\S\s[\)\]])', "gratuitous whitespace in () or []"),
152 # (r'\s\s=', "gratuitous whitespace before ="),
153 # (r'\s\s=', "gratuitous whitespace before ="),
153 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
154 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
154 "missing whitespace around operator"),
155 "missing whitespace around operator"),
155 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\s',
156 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\s',
156 "missing whitespace around operator"),
157 "missing whitespace around operator"),
157 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
158 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
158 "missing whitespace around operator"),
159 "missing whitespace around operator"),
159 (r'[^+=*!<>&| -](\s=|=\s)[^= ]',
160 (r'[^+=*!<>&| -](\s=|=\s)[^= ]',
160 "wrong whitespace around ="),
161 "wrong whitespace around ="),
161 (r'raise Exception', "don't raise generic exceptions"),
162 (r'raise Exception', "don't raise generic exceptions"),
162 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
163 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
163 (r' [=!]=\s+(True|False|None)',
164 (r' [=!]=\s+(True|False|None)',
164 "comparison with singleton, use 'is' or 'is not' instead"),
165 "comparison with singleton, use 'is' or 'is not' instead"),
165 (r'opener\([^)]*\).read\(',
166 (r'opener\([^)]*\).read\(',
166 "use opener.read() instead"),
167 "use opener.read() instead"),
167 (r'opener\([^)]*\).write\(',
168 (r'opener\([^)]*\).write\(',
168 "use opener.write() instead"),
169 "use opener.write() instead"),
169 (r'[\s\(](open|file)\([^)]*\)\.read\(',
170 (r'[\s\(](open|file)\([^)]*\)\.read\(',
170 "use util.readfile() instead"),
171 "use util.readfile() instead"),
171 (r'[\s\(](open|file)\([^)]*\)\.write\(',
172 (r'[\s\(](open|file)\([^)]*\)\.write\(',
172 "use util.readfile() instead"),
173 "use util.readfile() instead"),
173 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
174 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
174 "always assign an opened file to a variable, and close it afterwards"),
175 "always assign an opened file to a variable, and close it afterwards"),
175 (r'[\s\(](open|file)\([^)]*\)\.',
176 (r'[\s\(](open|file)\([^)]*\)\.',
176 "always assign an opened file to a variable, and close it afterwards"),
177 "always assign an opened file to a variable, and close it afterwards"),
177 ],
178 ],
178 # warnings
179 # warnings
179 [
180 [
180 (r'.{81}', "warning: line over 80 characters"),
181 (r'.{81}', "warning: line over 80 characters"),
181 (r'^\s*except:$', "warning: naked except clause"),
182 (r'^\s*except:$', "warning: naked except clause"),
182 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
183 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
183 "warning: unwrapped ui message"),
184 "warning: unwrapped ui message"),
184 ]
185 ]
185 ]
186 ]
186
187
187 pyfilters = [
188 pyfilters = [
188 (r"""(?msx)(?P<comment>\#.*?$)|
189 (r"""(?msx)(?P<comment>\#.*?$)|
189 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
190 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
190 (?P<text>(([^\\]|\\.)*?))
191 (?P<text>(([^\\]|\\.)*?))
191 (?P=quote))""", reppython),
192 (?P=quote))""", reppython),
192 ]
193 ]
193
194
194 cpats = [
195 cpats = [
195 [
196 [
196 (r'//', "don't use //-style comments"),
197 (r'//', "don't use //-style comments"),
197 (r'^ ', "don't use spaces to indent"),
198 (r'^ ', "don't use spaces to indent"),
198 (r'\S\t', "don't use tabs except for indent"),
199 (r'\S\t', "don't use tabs except for indent"),
199 (r'(\S\s+|^\s+)\n', "trailing whitespace"),
200 (r'(\S\s+|^\s+)\n', "trailing whitespace"),
200 (r'.{85}', "line too long"),
201 (r'.{85}', "line too long"),
201 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
202 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
202 (r'return\(', "return is not a function"),
203 (r'return\(', "return is not a function"),
203 (r' ;', "no space before ;"),
204 (r' ;', "no space before ;"),
204 (r'\w+\* \w+', "use int *foo, not int* foo"),
205 (r'\w+\* \w+', "use int *foo, not int* foo"),
205 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
206 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
206 (r'\S+ (\+\+|--)', "use foo++, not foo ++"),
207 (r'\S+ (\+\+|--)', "use foo++, not foo ++"),
207 (r'\w,\w', "missing whitespace after ,"),
208 (r'\w,\w', "missing whitespace after ,"),
208 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
209 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
209 (r'^#\s+\w', "use #foo, not # foo"),
210 (r'^#\s+\w', "use #foo, not # foo"),
210 (r'[^\n]\Z', "no trailing newline"),
211 (r'[^\n]\Z', "no trailing newline"),
211 (r'^\s*#import\b', "use only #include in standard C code"),
212 (r'^\s*#import\b', "use only #include in standard C code"),
212 ],
213 ],
213 # warnings
214 # warnings
214 []
215 []
215 ]
216 ]
216
217
217 cfilters = [
218 cfilters = [
218 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
219 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
219 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
220 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
220 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
221 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
221 (r'(\()([^)]+\))', repcallspaces),
222 (r'(\()([^)]+\))', repcallspaces),
222 ]
223 ]
223
224
224 inutilpats = [
225 inutilpats = [
225 [
226 [
226 (r'\bui\.', "don't use ui in util"),
227 (r'\bui\.', "don't use ui in util"),
227 ],
228 ],
228 # warnings
229 # warnings
229 []
230 []
230 ]
231 ]
231
232
232 inrevlogpats = [
233 inrevlogpats = [
233 [
234 [
234 (r'\brepo\.', "don't use repo in revlog"),
235 (r'\brepo\.', "don't use repo in revlog"),
235 ],
236 ],
236 # warnings
237 # warnings
237 []
238 []
238 ]
239 ]
239
240
240 checks = [
241 checks = [
241 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
242 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
242 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
243 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
243 ('c', r'.*\.c$', cfilters, cpats),
244 ('c', r'.*\.c$', cfilters, cpats),
244 ('unified test', r'.*\.t$', utestfilters, utestpats),
245 ('unified test', r'.*\.t$', utestfilters, utestpats),
245 ('layering violation repo in revlog', r'mercurial/revlog\.py', pyfilters,
246 ('layering violation repo in revlog', r'mercurial/revlog\.py', pyfilters,
246 inrevlogpats),
247 inrevlogpats),
247 ('layering violation ui in util', r'mercurial/util\.py', pyfilters,
248 ('layering violation ui in util', r'mercurial/util\.py', pyfilters,
248 inutilpats),
249 inutilpats),
249 ]
250 ]
250
251
251 class norepeatlogger(object):
252 class norepeatlogger(object):
252 def __init__(self):
253 def __init__(self):
253 self._lastseen = None
254 self._lastseen = None
254
255
255 def log(self, fname, lineno, line, msg, blame):
256 def log(self, fname, lineno, line, msg, blame):
256 """print error related a to given line of a given file.
257 """print error related a to given line of a given file.
257
258
258 The faulty line will also be printed but only once in the case
259 The faulty line will also be printed but only once in the case
259 of multiple errors.
260 of multiple errors.
260
261
261 :fname: filename
262 :fname: filename
262 :lineno: line number
263 :lineno: line number
263 :line: actual content of the line
264 :line: actual content of the line
264 :msg: error message
265 :msg: error message
265 """
266 """
266 msgid = fname, lineno, line
267 msgid = fname, lineno, line
267 if msgid != self._lastseen:
268 if msgid != self._lastseen:
268 if blame:
269 if blame:
269 print "%s:%d (%s):" % (fname, lineno, blame)
270 print "%s:%d (%s):" % (fname, lineno, blame)
270 else:
271 else:
271 print "%s:%d:" % (fname, lineno)
272 print "%s:%d:" % (fname, lineno)
272 print " > %s" % line
273 print " > %s" % line
273 self._lastseen = msgid
274 self._lastseen = msgid
274 print " " + msg
275 print " " + msg
275
276
276 _defaultlogger = norepeatlogger()
277 _defaultlogger = norepeatlogger()
277
278
278 def getblame(f):
279 def getblame(f):
279 lines = []
280 lines = []
280 for l in os.popen('hg annotate -un %s' % f):
281 for l in os.popen('hg annotate -un %s' % f):
281 start, line = l.split(':', 1)
282 start, line = l.split(':', 1)
282 user, rev = start.split()
283 user, rev = start.split()
283 lines.append((line[1:-1], user, rev))
284 lines.append((line[1:-1], user, rev))
284 return lines
285 return lines
285
286
286 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
287 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
287 blame=False, debug=False):
288 blame=False, debug=False):
288 """checks style and portability of a given file
289 """checks style and portability of a given file
289
290
290 :f: filepath
291 :f: filepath
291 :logfunc: function used to report error
292 :logfunc: function used to report error
292 logfunc(filename, linenumber, linecontent, errormessage)
293 logfunc(filename, linenumber, linecontent, errormessage)
293 :maxerr: number of error to display before arborting.
294 :maxerr: number of error to display before arborting.
294 Set to None (default) to report all errors
295 Set to None (default) to report all errors
295
296
296 return True if no error is found, False otherwise.
297 return True if no error is found, False otherwise.
297 """
298 """
298 blamecache = None
299 blamecache = None
299 result = True
300 result = True
300 for name, match, filters, pats in checks:
301 for name, match, filters, pats in checks:
301 if debug:
302 if debug:
302 print name, f
303 print name, f
303 fc = 0
304 fc = 0
304 if not re.match(match, f):
305 if not re.match(match, f):
305 if debug:
306 if debug:
306 print "Skipping %s for %s it doesn't match %s" % (
307 print "Skipping %s for %s it doesn't match %s" % (
307 name, match, f)
308 name, match, f)
308 continue
309 continue
309 fp = open(f)
310 fp = open(f)
310 pre = post = fp.read()
311 pre = post = fp.read()
311 fp.close()
312 fp.close()
312 if "no-" + "check-code" in pre:
313 if "no-" + "check-code" in pre:
313 if debug:
314 if debug:
314 print "Skipping %s for %s it has no- and check-code" % (
315 print "Skipping %s for %s it has no- and check-code" % (
315 name, f)
316 name, f)
316 break
317 break
317 for p, r in filters:
318 for p, r in filters:
318 post = re.sub(p, r, post)
319 post = re.sub(p, r, post)
319 if warnings:
320 if warnings:
320 pats = pats[0] + pats[1]
321 pats = pats[0] + pats[1]
321 else:
322 else:
322 pats = pats[0]
323 pats = pats[0]
323 # print post # uncomment to show filtered version
324 # print post # uncomment to show filtered version
324 z = enumerate(zip(pre.splitlines(), post.splitlines(True)))
325 z = enumerate(zip(pre.splitlines(), post.splitlines(True)))
325 if debug:
326 if debug:
326 print "Checking %s for %s" % (name, f)
327 print "Checking %s for %s" % (name, f)
327 for n, l in z:
328 for n, l in z:
328 if "check-code" + "-ignore" in l[0]:
329 if "check-code" + "-ignore" in l[0]:
329 if debug:
330 if debug:
330 print "Skipping %s for %s:%s (check-code -ignore)" % (
331 print "Skipping %s for %s:%s (check-code -ignore)" % (
331 name, f, n)
332 name, f, n)
332 continue
333 continue
333 for p, msg in pats:
334 for p, msg in pats:
334 if re.search(p, l[1]):
335 if re.search(p, l[1]):
335 bd = ""
336 bd = ""
336 if blame:
337 if blame:
337 bd = 'working directory'
338 bd = 'working directory'
338 if not blamecache:
339 if not blamecache:
339 blamecache = getblame(f)
340 blamecache = getblame(f)
340 if n < len(blamecache):
341 if n < len(blamecache):
341 bl, bu, br = blamecache[n]
342 bl, bu, br = blamecache[n]
342 if bl == l[0]:
343 if bl == l[0]:
343 bd = '%s@%s' % (bu, br)
344 bd = '%s@%s' % (bu, br)
344 logfunc(f, n + 1, l[0], msg, bd)
345 logfunc(f, n + 1, l[0], msg, bd)
345 fc += 1
346 fc += 1
346 result = False
347 result = False
347 if maxerr is not None and fc >= maxerr:
348 if maxerr is not None and fc >= maxerr:
348 print " (too many errors, giving up)"
349 print " (too many errors, giving up)"
349 break
350 break
350 return result
351 return result
351
352
352 if __name__ == "__main__":
353 if __name__ == "__main__":
353 parser = optparse.OptionParser("%prog [options] [files]")
354 parser = optparse.OptionParser("%prog [options] [files]")
354 parser.add_option("-w", "--warnings", action="store_true",
355 parser.add_option("-w", "--warnings", action="store_true",
355 help="include warning-level checks")
356 help="include warning-level checks")
356 parser.add_option("-p", "--per-file", type="int",
357 parser.add_option("-p", "--per-file", type="int",
357 help="max warnings per file")
358 help="max warnings per file")
358 parser.add_option("-b", "--blame", action="store_true",
359 parser.add_option("-b", "--blame", action="store_true",
359 help="use annotate to generate blame info")
360 help="use annotate to generate blame info")
360 parser.add_option("", "--debug", action="store_true",
361 parser.add_option("", "--debug", action="store_true",
361 help="show debug information")
362 help="show debug information")
362
363
363 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False)
364 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False)
364 (options, args) = parser.parse_args()
365 (options, args) = parser.parse_args()
365
366
366 if len(args) == 0:
367 if len(args) == 0:
367 check = glob.glob("*")
368 check = glob.glob("*")
368 else:
369 else:
369 check = args
370 check = args
370
371
371 for f in check:
372 for f in check:
372 ret = 0
373 ret = 0
373 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
374 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
374 blame=options.blame, debug=options.debug):
375 blame=options.blame, debug=options.debug):
375 ret = 1
376 ret = 1
376 sys.exit(ret)
377 sys.exit(ret)
@@ -1,3329 +1,3329 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''manage a stack of patches
8 '''manage a stack of patches
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use :hg:`help command` for more details)::
17 Common tasks (use :hg:`help command` for more details)::
18
18
19 create new patch qnew
19 create new patch qnew
20 import existing patch qimport
20 import existing patch qimport
21
21
22 print patch series qseries
22 print patch series qseries
23 print applied patches qapplied
23 print applied patches qapplied
24
24
25 add known patch to applied stack qpush
25 add known patch to applied stack qpush
26 remove patch from applied stack qpop
26 remove patch from applied stack qpop
27 refresh contents of top applied patch qrefresh
27 refresh contents of top applied patch qrefresh
28
28
29 By default, mq will automatically use git patches when required to
29 By default, mq will automatically use git patches when required to
30 avoid losing file mode changes, copy records, binary files or empty
30 avoid losing file mode changes, copy records, binary files or empty
31 files creations or deletions. This behaviour can be configured with::
31 files creations or deletions. This behaviour can be configured with::
32
32
33 [mq]
33 [mq]
34 git = auto/keep/yes/no
34 git = auto/keep/yes/no
35
35
36 If set to 'keep', mq will obey the [diff] section configuration while
36 If set to 'keep', mq will obey the [diff] section configuration while
37 preserving existing git patches upon qrefresh. If set to 'yes' or
37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 'no', mq will override the [diff] section and always generate git or
38 'no', mq will override the [diff] section and always generate git or
39 regular patches, possibly losing data in the second case.
39 regular patches, possibly losing data in the second case.
40
40
41 You will by default be managing a patch queue named "patches". You can
41 You will by default be managing a patch queue named "patches". You can
42 create other, independent patch queues with the :hg:`qqueue` command.
42 create other, independent patch queues with the :hg:`qqueue` command.
43 '''
43 '''
44
44
45 from mercurial.i18n import _
45 from mercurial.i18n import _
46 from mercurial.node import bin, hex, short, nullid, nullrev
46 from mercurial.node import bin, hex, short, nullid, nullrev
47 from mercurial.lock import release
47 from mercurial.lock import release
48 from mercurial import commands, cmdutil, hg, scmutil, util, revset
48 from mercurial import commands, cmdutil, hg, scmutil, util, revset
49 from mercurial import repair, extensions, url, error
49 from mercurial import repair, extensions, url, error
50 from mercurial import patch as patchmod
50 from mercurial import patch as patchmod
51 import os, sys, re, errno, shutil
51 import os, sys, re, errno, shutil
52
52
53 commands.norepo += " qclone"
53 commands.norepo += " qclone"
54
54
55 # Patch names looks like unix-file names.
55 # Patch names looks like unix-file names.
56 # They must be joinable with queue directory and result in the patch path.
56 # They must be joinable with queue directory and result in the patch path.
57 normname = util.normpath
57 normname = util.normpath
58
58
59 class statusentry(object):
59 class statusentry(object):
60 def __init__(self, node, name):
60 def __init__(self, node, name):
61 self.node, self.name = node, name
61 self.node, self.name = node, name
62 def __repr__(self):
62 def __repr__(self):
63 return hex(self.node) + ':' + self.name
63 return hex(self.node) + ':' + self.name
64
64
65 class patchheader(object):
65 class patchheader(object):
66 def __init__(self, pf, plainmode=False):
66 def __init__(self, pf, plainmode=False):
67 def eatdiff(lines):
67 def eatdiff(lines):
68 while lines:
68 while lines:
69 l = lines[-1]
69 l = lines[-1]
70 if (l.startswith("diff -") or
70 if (l.startswith("diff -") or
71 l.startswith("Index:") or
71 l.startswith("Index:") or
72 l.startswith("===========")):
72 l.startswith("===========")):
73 del lines[-1]
73 del lines[-1]
74 else:
74 else:
75 break
75 break
76 def eatempty(lines):
76 def eatempty(lines):
77 while lines:
77 while lines:
78 if not lines[-1].strip():
78 if not lines[-1].strip():
79 del lines[-1]
79 del lines[-1]
80 else:
80 else:
81 break
81 break
82
82
83 message = []
83 message = []
84 comments = []
84 comments = []
85 user = None
85 user = None
86 date = None
86 date = None
87 parent = None
87 parent = None
88 format = None
88 format = None
89 subject = None
89 subject = None
90 branch = None
90 branch = None
91 nodeid = None
91 nodeid = None
92 diffstart = 0
92 diffstart = 0
93
93
94 for line in file(pf):
94 for line in file(pf):
95 line = line.rstrip()
95 line = line.rstrip()
96 if (line.startswith('diff --git')
96 if (line.startswith('diff --git')
97 or (diffstart and line.startswith('+++ '))):
97 or (diffstart and line.startswith('+++ '))):
98 diffstart = 2
98 diffstart = 2
99 break
99 break
100 diffstart = 0 # reset
100 diffstart = 0 # reset
101 if line.startswith("--- "):
101 if line.startswith("--- "):
102 diffstart = 1
102 diffstart = 1
103 continue
103 continue
104 elif format == "hgpatch":
104 elif format == "hgpatch":
105 # parse values when importing the result of an hg export
105 # parse values when importing the result of an hg export
106 if line.startswith("# User "):
106 if line.startswith("# User "):
107 user = line[7:]
107 user = line[7:]
108 elif line.startswith("# Date "):
108 elif line.startswith("# Date "):
109 date = line[7:]
109 date = line[7:]
110 elif line.startswith("# Parent "):
110 elif line.startswith("# Parent "):
111 parent = line[9:].lstrip()
111 parent = line[9:].lstrip()
112 elif line.startswith("# Branch "):
112 elif line.startswith("# Branch "):
113 branch = line[9:]
113 branch = line[9:]
114 elif line.startswith("# Node ID "):
114 elif line.startswith("# Node ID "):
115 nodeid = line[10:]
115 nodeid = line[10:]
116 elif not line.startswith("# ") and line:
116 elif not line.startswith("# ") and line:
117 message.append(line)
117 message.append(line)
118 format = None
118 format = None
119 elif line == '# HG changeset patch':
119 elif line == '# HG changeset patch':
120 message = []
120 message = []
121 format = "hgpatch"
121 format = "hgpatch"
122 elif (format != "tagdone" and (line.startswith("Subject: ") or
122 elif (format != "tagdone" and (line.startswith("Subject: ") or
123 line.startswith("subject: "))):
123 line.startswith("subject: "))):
124 subject = line[9:]
124 subject = line[9:]
125 format = "tag"
125 format = "tag"
126 elif (format != "tagdone" and (line.startswith("From: ") or
126 elif (format != "tagdone" and (line.startswith("From: ") or
127 line.startswith("from: "))):
127 line.startswith("from: "))):
128 user = line[6:]
128 user = line[6:]
129 format = "tag"
129 format = "tag"
130 elif (format != "tagdone" and (line.startswith("Date: ") or
130 elif (format != "tagdone" and (line.startswith("Date: ") or
131 line.startswith("date: "))):
131 line.startswith("date: "))):
132 date = line[6:]
132 date = line[6:]
133 format = "tag"
133 format = "tag"
134 elif format == "tag" and line == "":
134 elif format == "tag" and line == "":
135 # when looking for tags (subject: from: etc) they
135 # when looking for tags (subject: from: etc) they
136 # end once you find a blank line in the source
136 # end once you find a blank line in the source
137 format = "tagdone"
137 format = "tagdone"
138 elif message or line:
138 elif message or line:
139 message.append(line)
139 message.append(line)
140 comments.append(line)
140 comments.append(line)
141
141
142 eatdiff(message)
142 eatdiff(message)
143 eatdiff(comments)
143 eatdiff(comments)
144 # Remember the exact starting line of the patch diffs before consuming
144 # Remember the exact starting line of the patch diffs before consuming
145 # empty lines, for external use by TortoiseHg and others
145 # empty lines, for external use by TortoiseHg and others
146 self.diffstartline = len(comments)
146 self.diffstartline = len(comments)
147 eatempty(message)
147 eatempty(message)
148 eatempty(comments)
148 eatempty(comments)
149
149
150 # make sure message isn't empty
150 # make sure message isn't empty
151 if format and format.startswith("tag") and subject:
151 if format and format.startswith("tag") and subject:
152 message.insert(0, "")
152 message.insert(0, "")
153 message.insert(0, subject)
153 message.insert(0, subject)
154
154
155 self.message = message
155 self.message = message
156 self.comments = comments
156 self.comments = comments
157 self.user = user
157 self.user = user
158 self.date = date
158 self.date = date
159 self.parent = parent
159 self.parent = parent
160 # nodeid and branch are for external use by TortoiseHg and others
160 # nodeid and branch are for external use by TortoiseHg and others
161 self.nodeid = nodeid
161 self.nodeid = nodeid
162 self.branch = branch
162 self.branch = branch
163 self.haspatch = diffstart > 1
163 self.haspatch = diffstart > 1
164 self.plainmode = plainmode
164 self.plainmode = plainmode
165
165
166 def setuser(self, user):
166 def setuser(self, user):
167 if not self.updateheader(['From: ', '# User '], user):
167 if not self.updateheader(['From: ', '# User '], user):
168 try:
168 try:
169 patchheaderat = self.comments.index('# HG changeset patch')
169 patchheaderat = self.comments.index('# HG changeset patch')
170 self.comments.insert(patchheaderat + 1, '# User ' + user)
170 self.comments.insert(patchheaderat + 1, '# User ' + user)
171 except ValueError:
171 except ValueError:
172 if self.plainmode or self._hasheader(['Date: ']):
172 if self.plainmode or self._hasheader(['Date: ']):
173 self.comments = ['From: ' + user] + self.comments
173 self.comments = ['From: ' + user] + self.comments
174 else:
174 else:
175 tmp = ['# HG changeset patch', '# User ' + user, '']
175 tmp = ['# HG changeset patch', '# User ' + user, '']
176 self.comments = tmp + self.comments
176 self.comments = tmp + self.comments
177 self.user = user
177 self.user = user
178
178
179 def setdate(self, date):
179 def setdate(self, date):
180 if not self.updateheader(['Date: ', '# Date '], date):
180 if not self.updateheader(['Date: ', '# Date '], date):
181 try:
181 try:
182 patchheaderat = self.comments.index('# HG changeset patch')
182 patchheaderat = self.comments.index('# HG changeset patch')
183 self.comments.insert(patchheaderat + 1, '# Date ' + date)
183 self.comments.insert(patchheaderat + 1, '# Date ' + date)
184 except ValueError:
184 except ValueError:
185 if self.plainmode or self._hasheader(['From: ']):
185 if self.plainmode or self._hasheader(['From: ']):
186 self.comments = ['Date: ' + date] + self.comments
186 self.comments = ['Date: ' + date] + self.comments
187 else:
187 else:
188 tmp = ['# HG changeset patch', '# Date ' + date, '']
188 tmp = ['# HG changeset patch', '# Date ' + date, '']
189 self.comments = tmp + self.comments
189 self.comments = tmp + self.comments
190 self.date = date
190 self.date = date
191
191
192 def setparent(self, parent):
192 def setparent(self, parent):
193 if not self.updateheader(['# Parent '], parent):
193 if not self.updateheader(['# Parent '], parent):
194 try:
194 try:
195 patchheaderat = self.comments.index('# HG changeset patch')
195 patchheaderat = self.comments.index('# HG changeset patch')
196 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
196 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
197 except ValueError:
197 except ValueError:
198 pass
198 pass
199 self.parent = parent
199 self.parent = parent
200
200
201 def setmessage(self, message):
201 def setmessage(self, message):
202 if self.comments:
202 if self.comments:
203 self._delmsg()
203 self._delmsg()
204 self.message = [message]
204 self.message = [message]
205 self.comments += self.message
205 self.comments += self.message
206
206
207 def updateheader(self, prefixes, new):
207 def updateheader(self, prefixes, new):
208 '''Update all references to a field in the patch header.
208 '''Update all references to a field in the patch header.
209 Return whether the field is present.'''
209 Return whether the field is present.'''
210 res = False
210 res = False
211 for prefix in prefixes:
211 for prefix in prefixes:
212 for i in xrange(len(self.comments)):
212 for i in xrange(len(self.comments)):
213 if self.comments[i].startswith(prefix):
213 if self.comments[i].startswith(prefix):
214 self.comments[i] = prefix + new
214 self.comments[i] = prefix + new
215 res = True
215 res = True
216 break
216 break
217 return res
217 return res
218
218
219 def _hasheader(self, prefixes):
219 def _hasheader(self, prefixes):
220 '''Check if a header starts with any of the given prefixes.'''
220 '''Check if a header starts with any of the given prefixes.'''
221 for prefix in prefixes:
221 for prefix in prefixes:
222 for comment in self.comments:
222 for comment in self.comments:
223 if comment.startswith(prefix):
223 if comment.startswith(prefix):
224 return True
224 return True
225 return False
225 return False
226
226
227 def __str__(self):
227 def __str__(self):
228 if not self.comments:
228 if not self.comments:
229 return ''
229 return ''
230 return '\n'.join(self.comments) + '\n\n'
230 return '\n'.join(self.comments) + '\n\n'
231
231
232 def _delmsg(self):
232 def _delmsg(self):
233 '''Remove existing message, keeping the rest of the comments fields.
233 '''Remove existing message, keeping the rest of the comments fields.
234 If comments contains 'subject: ', message will prepend
234 If comments contains 'subject: ', message will prepend
235 the field and a blank line.'''
235 the field and a blank line.'''
236 if self.message:
236 if self.message:
237 subj = 'subject: ' + self.message[0].lower()
237 subj = 'subject: ' + self.message[0].lower()
238 for i in xrange(len(self.comments)):
238 for i in xrange(len(self.comments)):
239 if subj == self.comments[i].lower():
239 if subj == self.comments[i].lower():
240 del self.comments[i]
240 del self.comments[i]
241 self.message = self.message[2:]
241 self.message = self.message[2:]
242 break
242 break
243 ci = 0
243 ci = 0
244 for mi in self.message:
244 for mi in self.message:
245 while mi != self.comments[ci]:
245 while mi != self.comments[ci]:
246 ci += 1
246 ci += 1
247 del self.comments[ci]
247 del self.comments[ci]
248
248
249 class queue(object):
249 class queue(object):
250 def __init__(self, ui, path, patchdir=None):
250 def __init__(self, ui, path, patchdir=None):
251 self.basepath = path
251 self.basepath = path
252 try:
252 try:
253 fh = open(os.path.join(path, 'patches.queue'))
253 fh = open(os.path.join(path, 'patches.queue'))
254 cur = fh.read().rstrip()
254 cur = fh.read().rstrip()
255 fh.close()
255 fh.close()
256 if not cur:
256 if not cur:
257 curpath = os.path.join(path, 'patches')
257 curpath = os.path.join(path, 'patches')
258 else:
258 else:
259 curpath = os.path.join(path, 'patches-' + cur)
259 curpath = os.path.join(path, 'patches-' + cur)
260 except IOError:
260 except IOError:
261 curpath = os.path.join(path, 'patches')
261 curpath = os.path.join(path, 'patches')
262 self.path = patchdir or curpath
262 self.path = patchdir or curpath
263 self.opener = scmutil.opener(self.path)
263 self.opener = scmutil.opener(self.path)
264 self.ui = ui
264 self.ui = ui
265 self.applied_dirty = 0
265 self.applied_dirty = 0
266 self.series_dirty = 0
266 self.series_dirty = 0
267 self.added = []
267 self.added = []
268 self.series_path = "series"
268 self.series_path = "series"
269 self.status_path = "status"
269 self.status_path = "status"
270 self.guards_path = "guards"
270 self.guards_path = "guards"
271 self.active_guards = None
271 self.active_guards = None
272 self.guards_dirty = False
272 self.guards_dirty = False
273 # Handle mq.git as a bool with extended values
273 # Handle mq.git as a bool with extended values
274 try:
274 try:
275 gitmode = ui.configbool('mq', 'git', None)
275 gitmode = ui.configbool('mq', 'git', None)
276 if gitmode is None:
276 if gitmode is None:
277 raise error.ConfigError()
277 raise error.ConfigError()
278 self.gitmode = gitmode and 'yes' or 'no'
278 self.gitmode = gitmode and 'yes' or 'no'
279 except error.ConfigError:
279 except error.ConfigError:
280 self.gitmode = ui.config('mq', 'git', 'auto').lower()
280 self.gitmode = ui.config('mq', 'git', 'auto').lower()
281 self.plainmode = ui.configbool('mq', 'plain', False)
281 self.plainmode = ui.configbool('mq', 'plain', False)
282
282
283 @util.propertycache
283 @util.propertycache
284 def applied(self):
284 def applied(self):
285 if os.path.exists(self.join(self.status_path)):
285 if os.path.exists(self.join(self.status_path)):
286 def parselines(lines):
286 def parselines(lines):
287 for l in lines:
287 for l in lines:
288 entry = l.split(':', 1)
288 entry = l.split(':', 1)
289 if len(entry) > 1:
289 if len(entry) > 1:
290 n, name = entry
290 n, name = entry
291 yield statusentry(bin(n), name)
291 yield statusentry(bin(n), name)
292 elif l.strip():
292 elif l.strip():
293 self.ui.warn(_('malformated mq status line: %s\n') % entry)
293 self.ui.warn(_('malformated mq status line: %s\n') % entry)
294 # else we ignore empty lines
294 # else we ignore empty lines
295 lines = self.opener.read(self.status_path).splitlines()
295 lines = self.opener.read(self.status_path).splitlines()
296 return list(parselines(lines))
296 return list(parselines(lines))
297 return []
297 return []
298
298
299 @util.propertycache
299 @util.propertycache
300 def full_series(self):
300 def full_series(self):
301 if os.path.exists(self.join(self.series_path)):
301 if os.path.exists(self.join(self.series_path)):
302 return self.opener.read(self.series_path).splitlines()
302 return self.opener.read(self.series_path).splitlines()
303 return []
303 return []
304
304
305 @util.propertycache
305 @util.propertycache
306 def series(self):
306 def series(self):
307 self.parse_series()
307 self.parse_series()
308 return self.series
308 return self.series
309
309
310 @util.propertycache
310 @util.propertycache
311 def series_guards(self):
311 def series_guards(self):
312 self.parse_series()
312 self.parse_series()
313 return self.series_guards
313 return self.series_guards
314
314
315 def invalidate(self):
315 def invalidate(self):
316 for a in 'applied full_series series series_guards'.split():
316 for a in 'applied full_series series series_guards'.split():
317 if a in self.__dict__:
317 if a in self.__dict__:
318 delattr(self, a)
318 delattr(self, a)
319 self.applied_dirty = 0
319 self.applied_dirty = 0
320 self.series_dirty = 0
320 self.series_dirty = 0
321 self.guards_dirty = False
321 self.guards_dirty = False
322 self.active_guards = None
322 self.active_guards = None
323
323
324 def diffopts(self, opts={}, patchfn=None):
324 def diffopts(self, opts={}, patchfn=None):
325 diffopts = patchmod.diffopts(self.ui, opts)
325 diffopts = patchmod.diffopts(self.ui, opts)
326 if self.gitmode == 'auto':
326 if self.gitmode == 'auto':
327 diffopts.upgrade = True
327 diffopts.upgrade = True
328 elif self.gitmode == 'keep':
328 elif self.gitmode == 'keep':
329 pass
329 pass
330 elif self.gitmode in ('yes', 'no'):
330 elif self.gitmode in ('yes', 'no'):
331 diffopts.git = self.gitmode == 'yes'
331 diffopts.git = self.gitmode == 'yes'
332 else:
332 else:
333 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
333 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
334 ' got %s') % self.gitmode)
334 ' got %s') % self.gitmode)
335 if patchfn:
335 if patchfn:
336 diffopts = self.patchopts(diffopts, patchfn)
336 diffopts = self.patchopts(diffopts, patchfn)
337 return diffopts
337 return diffopts
338
338
339 def patchopts(self, diffopts, *patches):
339 def patchopts(self, diffopts, *patches):
340 """Return a copy of input diff options with git set to true if
340 """Return a copy of input diff options with git set to true if
341 referenced patch is a git patch and should be preserved as such.
341 referenced patch is a git patch and should be preserved as such.
342 """
342 """
343 diffopts = diffopts.copy()
343 diffopts = diffopts.copy()
344 if not diffopts.git and self.gitmode == 'keep':
344 if not diffopts.git and self.gitmode == 'keep':
345 for patchfn in patches:
345 for patchfn in patches:
346 patchf = self.opener(patchfn, 'r')
346 patchf = self.opener(patchfn, 'r')
347 # if the patch was a git patch, refresh it as a git patch
347 # if the patch was a git patch, refresh it as a git patch
348 for line in patchf:
348 for line in patchf:
349 if line.startswith('diff --git'):
349 if line.startswith('diff --git'):
350 diffopts.git = True
350 diffopts.git = True
351 break
351 break
352 patchf.close()
352 patchf.close()
353 return diffopts
353 return diffopts
354
354
355 def join(self, *p):
355 def join(self, *p):
356 return os.path.join(self.path, *p)
356 return os.path.join(self.path, *p)
357
357
358 def find_series(self, patch):
358 def find_series(self, patch):
359 def matchpatch(l):
359 def matchpatch(l):
360 l = l.split('#', 1)[0]
360 l = l.split('#', 1)[0]
361 return l.strip() == patch
361 return l.strip() == patch
362 for index, l in enumerate(self.full_series):
362 for index, l in enumerate(self.full_series):
363 if matchpatch(l):
363 if matchpatch(l):
364 return index
364 return index
365 return None
365 return None
366
366
367 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
367 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
368
368
369 def parse_series(self):
369 def parse_series(self):
370 self.series = []
370 self.series = []
371 self.series_guards = []
371 self.series_guards = []
372 for l in self.full_series:
372 for l in self.full_series:
373 h = l.find('#')
373 h = l.find('#')
374 if h == -1:
374 if h == -1:
375 patch = l
375 patch = l
376 comment = ''
376 comment = ''
377 elif h == 0:
377 elif h == 0:
378 continue
378 continue
379 else:
379 else:
380 patch = l[:h]
380 patch = l[:h]
381 comment = l[h:]
381 comment = l[h:]
382 patch = patch.strip()
382 patch = patch.strip()
383 if patch:
383 if patch:
384 if patch in self.series:
384 if patch in self.series:
385 raise util.Abort(_('%s appears more than once in %s') %
385 raise util.Abort(_('%s appears more than once in %s') %
386 (patch, self.join(self.series_path)))
386 (patch, self.join(self.series_path)))
387 self.series.append(patch)
387 self.series.append(patch)
388 self.series_guards.append(self.guard_re.findall(comment))
388 self.series_guards.append(self.guard_re.findall(comment))
389
389
390 def check_guard(self, guard):
390 def check_guard(self, guard):
391 if not guard:
391 if not guard:
392 return _('guard cannot be an empty string')
392 return _('guard cannot be an empty string')
393 bad_chars = '# \t\r\n\f'
393 bad_chars = '# \t\r\n\f'
394 first = guard[0]
394 first = guard[0]
395 if first in '-+':
395 if first in '-+':
396 return (_('guard %r starts with invalid character: %r') %
396 return (_('guard %r starts with invalid character: %r') %
397 (guard, first))
397 (guard, first))
398 for c in bad_chars:
398 for c in bad_chars:
399 if c in guard:
399 if c in guard:
400 return _('invalid character in guard %r: %r') % (guard, c)
400 return _('invalid character in guard %r: %r') % (guard, c)
401
401
402 def set_active(self, guards):
402 def set_active(self, guards):
403 for guard in guards:
403 for guard in guards:
404 bad = self.check_guard(guard)
404 bad = self.check_guard(guard)
405 if bad:
405 if bad:
406 raise util.Abort(bad)
406 raise util.Abort(bad)
407 guards = sorted(set(guards))
407 guards = sorted(set(guards))
408 self.ui.debug('active guards: %s\n' % ' '.join(guards))
408 self.ui.debug('active guards: %s\n' % ' '.join(guards))
409 self.active_guards = guards
409 self.active_guards = guards
410 self.guards_dirty = True
410 self.guards_dirty = True
411
411
412 def active(self):
412 def active(self):
413 if self.active_guards is None:
413 if self.active_guards is None:
414 self.active_guards = []
414 self.active_guards = []
415 try:
415 try:
416 guards = self.opener.read(self.guards_path).split()
416 guards = self.opener.read(self.guards_path).split()
417 except IOError, err:
417 except IOError, err:
418 if err.errno != errno.ENOENT:
418 if err.errno != errno.ENOENT:
419 raise
419 raise
420 guards = []
420 guards = []
421 for i, guard in enumerate(guards):
421 for i, guard in enumerate(guards):
422 bad = self.check_guard(guard)
422 bad = self.check_guard(guard)
423 if bad:
423 if bad:
424 self.ui.warn('%s:%d: %s\n' %
424 self.ui.warn('%s:%d: %s\n' %
425 (self.join(self.guards_path), i + 1, bad))
425 (self.join(self.guards_path), i + 1, bad))
426 else:
426 else:
427 self.active_guards.append(guard)
427 self.active_guards.append(guard)
428 return self.active_guards
428 return self.active_guards
429
429
430 def set_guards(self, idx, guards):
430 def set_guards(self, idx, guards):
431 for g in guards:
431 for g in guards:
432 if len(g) < 2:
432 if len(g) < 2:
433 raise util.Abort(_('guard %r too short') % g)
433 raise util.Abort(_('guard %r too short') % g)
434 if g[0] not in '-+':
434 if g[0] not in '-+':
435 raise util.Abort(_('guard %r starts with invalid char') % g)
435 raise util.Abort(_('guard %r starts with invalid char') % g)
436 bad = self.check_guard(g[1:])
436 bad = self.check_guard(g[1:])
437 if bad:
437 if bad:
438 raise util.Abort(bad)
438 raise util.Abort(bad)
439 drop = self.guard_re.sub('', self.full_series[idx])
439 drop = self.guard_re.sub('', self.full_series[idx])
440 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
440 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
441 self.parse_series()
441 self.parse_series()
442 self.series_dirty = True
442 self.series_dirty = True
443
443
444 def pushable(self, idx):
444 def pushable(self, idx):
445 if isinstance(idx, str):
445 if isinstance(idx, str):
446 idx = self.series.index(idx)
446 idx = self.series.index(idx)
447 patchguards = self.series_guards[idx]
447 patchguards = self.series_guards[idx]
448 if not patchguards:
448 if not patchguards:
449 return True, None
449 return True, None
450 guards = self.active()
450 guards = self.active()
451 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
451 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
452 if exactneg:
452 if exactneg:
453 return False, exactneg[0]
453 return False, exactneg[0]
454 pos = [g for g in patchguards if g[0] == '+']
454 pos = [g for g in patchguards if g[0] == '+']
455 exactpos = [g for g in pos if g[1:] in guards]
455 exactpos = [g for g in pos if g[1:] in guards]
456 if pos:
456 if pos:
457 if exactpos:
457 if exactpos:
458 return True, exactpos[0]
458 return True, exactpos[0]
459 return False, pos
459 return False, pos
460 return True, ''
460 return True, ''
461
461
462 def explain_pushable(self, idx, all_patches=False):
462 def explain_pushable(self, idx, all_patches=False):
463 write = all_patches and self.ui.write or self.ui.warn
463 write = all_patches and self.ui.write or self.ui.warn
464 if all_patches or self.ui.verbose:
464 if all_patches or self.ui.verbose:
465 if isinstance(idx, str):
465 if isinstance(idx, str):
466 idx = self.series.index(idx)
466 idx = self.series.index(idx)
467 pushable, why = self.pushable(idx)
467 pushable, why = self.pushable(idx)
468 if all_patches and pushable:
468 if all_patches and pushable:
469 if why is None:
469 if why is None:
470 write(_('allowing %s - no guards in effect\n') %
470 write(_('allowing %s - no guards in effect\n') %
471 self.series[idx])
471 self.series[idx])
472 else:
472 else:
473 if not why:
473 if not why:
474 write(_('allowing %s - no matching negative guards\n') %
474 write(_('allowing %s - no matching negative guards\n') %
475 self.series[idx])
475 self.series[idx])
476 else:
476 else:
477 write(_('allowing %s - guarded by %r\n') %
477 write(_('allowing %s - guarded by %r\n') %
478 (self.series[idx], why))
478 (self.series[idx], why))
479 if not pushable:
479 if not pushable:
480 if why:
480 if why:
481 write(_('skipping %s - guarded by %r\n') %
481 write(_('skipping %s - guarded by %r\n') %
482 (self.series[idx], why))
482 (self.series[idx], why))
483 else:
483 else:
484 write(_('skipping %s - no matching guards\n') %
484 write(_('skipping %s - no matching guards\n') %
485 self.series[idx])
485 self.series[idx])
486
486
487 def save_dirty(self):
487 def save_dirty(self):
488 def write_list(items, path):
488 def write_list(items, path):
489 fp = self.opener(path, 'w')
489 fp = self.opener(path, 'w')
490 for i in items:
490 for i in items:
491 fp.write("%s\n" % i)
491 fp.write("%s\n" % i)
492 fp.close()
492 fp.close()
493 if self.applied_dirty:
493 if self.applied_dirty:
494 write_list(map(str, self.applied), self.status_path)
494 write_list(map(str, self.applied), self.status_path)
495 if self.series_dirty:
495 if self.series_dirty:
496 write_list(self.full_series, self.series_path)
496 write_list(self.full_series, self.series_path)
497 if self.guards_dirty:
497 if self.guards_dirty:
498 write_list(self.active_guards, self.guards_path)
498 write_list(self.active_guards, self.guards_path)
499 if self.added:
499 if self.added:
500 qrepo = self.qrepo()
500 qrepo = self.qrepo()
501 if qrepo:
501 if qrepo:
502 qrepo[None].add(f for f in self.added if f not in qrepo[None])
502 qrepo[None].add(f for f in self.added if f not in qrepo[None])
503 self.added = []
503 self.added = []
504
504
505 def removeundo(self, repo):
505 def removeundo(self, repo):
506 undo = repo.sjoin('undo')
506 undo = repo.sjoin('undo')
507 if not os.path.exists(undo):
507 if not os.path.exists(undo):
508 return
508 return
509 try:
509 try:
510 os.unlink(undo)
510 os.unlink(undo)
511 except OSError, inst:
511 except OSError, inst:
512 self.ui.warn(_('error removing undo: %s\n') % str(inst))
512 self.ui.warn(_('error removing undo: %s\n') % str(inst))
513
513
514 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
514 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
515 fp=None, changes=None, opts={}):
515 fp=None, changes=None, opts={}):
516 stat = opts.get('stat')
516 stat = opts.get('stat')
517 m = cmdutil.match(repo, files, opts)
517 m = cmdutil.match(repo, files, opts)
518 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
518 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
519 changes, stat, fp)
519 changes, stat, fp)
520
520
521 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
521 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
522 # first try just applying the patch
522 # first try just applying the patch
523 (err, n) = self.apply(repo, [patch], update_status=False,
523 (err, n) = self.apply(repo, [patch], update_status=False,
524 strict=True, merge=rev)
524 strict=True, merge=rev)
525
525
526 if err == 0:
526 if err == 0:
527 return (err, n)
527 return (err, n)
528
528
529 if n is None:
529 if n is None:
530 raise util.Abort(_("apply failed for patch %s") % patch)
530 raise util.Abort(_("apply failed for patch %s") % patch)
531
531
532 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
532 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
533
533
534 # apply failed, strip away that rev and merge.
534 # apply failed, strip away that rev and merge.
535 hg.clean(repo, head)
535 hg.clean(repo, head)
536 self.strip(repo, [n], update=False, backup='strip')
536 self.strip(repo, [n], update=False, backup='strip')
537
537
538 ctx = repo[rev]
538 ctx = repo[rev]
539 ret = hg.merge(repo, rev)
539 ret = hg.merge(repo, rev)
540 if ret:
540 if ret:
541 raise util.Abort(_("update returned %d") % ret)
541 raise util.Abort(_("update returned %d") % ret)
542 n = repo.commit(ctx.description(), ctx.user(), force=True)
542 n = repo.commit(ctx.description(), ctx.user(), force=True)
543 if n is None:
543 if n is None:
544 raise util.Abort(_("repo commit failed"))
544 raise util.Abort(_("repo commit failed"))
545 try:
545 try:
546 ph = patchheader(mergeq.join(patch), self.plainmode)
546 ph = patchheader(mergeq.join(patch), self.plainmode)
547 except:
547 except:
548 raise util.Abort(_("unable to read %s") % patch)
548 raise util.Abort(_("unable to read %s") % patch)
549
549
550 diffopts = self.patchopts(diffopts, patch)
550 diffopts = self.patchopts(diffopts, patch)
551 patchf = self.opener(patch, "w")
551 patchf = self.opener(patch, "w")
552 comments = str(ph)
552 comments = str(ph)
553 if comments:
553 if comments:
554 patchf.write(comments)
554 patchf.write(comments)
555 self.printdiff(repo, diffopts, head, n, fp=patchf)
555 self.printdiff(repo, diffopts, head, n, fp=patchf)
556 patchf.close()
556 patchf.close()
557 self.removeundo(repo)
557 self.removeundo(repo)
558 return (0, n)
558 return (0, n)
559
559
560 def qparents(self, repo, rev=None):
560 def qparents(self, repo, rev=None):
561 if rev is None:
561 if rev is None:
562 (p1, p2) = repo.dirstate.parents()
562 (p1, p2) = repo.dirstate.parents()
563 if p2 == nullid:
563 if p2 == nullid:
564 return p1
564 return p1
565 if not self.applied:
565 if not self.applied:
566 return None
566 return None
567 return self.applied[-1].node
567 return self.applied[-1].node
568 p1, p2 = repo.changelog.parents(rev)
568 p1, p2 = repo.changelog.parents(rev)
569 if p2 != nullid and p2 in [x.node for x in self.applied]:
569 if p2 != nullid and p2 in [x.node for x in self.applied]:
570 return p2
570 return p2
571 return p1
571 return p1
572
572
573 def mergepatch(self, repo, mergeq, series, diffopts):
573 def mergepatch(self, repo, mergeq, series, diffopts):
574 if not self.applied:
574 if not self.applied:
575 # each of the patches merged in will have two parents. This
575 # each of the patches merged in will have two parents. This
576 # can confuse the qrefresh, qdiff, and strip code because it
576 # can confuse the qrefresh, qdiff, and strip code because it
577 # needs to know which parent is actually in the patch queue.
577 # needs to know which parent is actually in the patch queue.
578 # so, we insert a merge marker with only one parent. This way
578 # so, we insert a merge marker with only one parent. This way
579 # the first patch in the queue is never a merge patch
579 # the first patch in the queue is never a merge patch
580 #
580 #
581 pname = ".hg.patches.merge.marker"
581 pname = ".hg.patches.merge.marker"
582 n = repo.commit('[mq]: merge marker', force=True)
582 n = repo.commit('[mq]: merge marker', force=True)
583 self.removeundo(repo)
583 self.removeundo(repo)
584 self.applied.append(statusentry(n, pname))
584 self.applied.append(statusentry(n, pname))
585 self.applied_dirty = 1
585 self.applied_dirty = 1
586
586
587 head = self.qparents(repo)
587 head = self.qparents(repo)
588
588
589 for patch in series:
589 for patch in series:
590 patch = mergeq.lookup(patch, strict=True)
590 patch = mergeq.lookup(patch, strict=True)
591 if not patch:
591 if not patch:
592 self.ui.warn(_("patch %s does not exist\n") % patch)
592 self.ui.warn(_("patch %s does not exist\n") % patch)
593 return (1, None)
593 return (1, None)
594 pushable, reason = self.pushable(patch)
594 pushable, reason = self.pushable(patch)
595 if not pushable:
595 if not pushable:
596 self.explain_pushable(patch, all_patches=True)
596 self.explain_pushable(patch, all_patches=True)
597 continue
597 continue
598 info = mergeq.isapplied(patch)
598 info = mergeq.isapplied(patch)
599 if not info:
599 if not info:
600 self.ui.warn(_("patch %s is not applied\n") % patch)
600 self.ui.warn(_("patch %s is not applied\n") % patch)
601 return (1, None)
601 return (1, None)
602 rev = info[1]
602 rev = info[1]
603 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
603 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
604 if head:
604 if head:
605 self.applied.append(statusentry(head, patch))
605 self.applied.append(statusentry(head, patch))
606 self.applied_dirty = 1
606 self.applied_dirty = 1
607 if err:
607 if err:
608 return (err, head)
608 return (err, head)
609 self.save_dirty()
609 self.save_dirty()
610 return (0, head)
610 return (0, head)
611
611
612 def patch(self, repo, patchfile):
612 def patch(self, repo, patchfile):
613 '''Apply patchfile to the working directory.
613 '''Apply patchfile to the working directory.
614 patchfile: name of patch file'''
614 patchfile: name of patch file'''
615 files = {}
615 files = {}
616 try:
616 try:
617 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
617 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
618 cwd=repo.root, files=files, eolmode=None)
618 cwd=repo.root, files=files, eolmode=None)
619 return (True, list(files), fuzz)
619 return (True, list(files), fuzz)
620 except Exception, inst:
620 except Exception, inst:
621 self.ui.note(str(inst) + '\n')
621 self.ui.note(str(inst) + '\n')
622 if not self.ui.verbose:
622 if not self.ui.verbose:
623 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
623 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
624 return (False, list(files), False)
624 return (False, list(files), False)
625
625
626 def apply(self, repo, series, list=False, update_status=True,
626 def apply(self, repo, series, list=False, update_status=True,
627 strict=False, patchdir=None, merge=None, all_files=None):
627 strict=False, patchdir=None, merge=None, all_files=None):
628 wlock = lock = tr = None
628 wlock = lock = tr = None
629 try:
629 try:
630 wlock = repo.wlock()
630 wlock = repo.wlock()
631 lock = repo.lock()
631 lock = repo.lock()
632 tr = repo.transaction("qpush")
632 tr = repo.transaction("qpush")
633 try:
633 try:
634 ret = self._apply(repo, series, list, update_status,
634 ret = self._apply(repo, series, list, update_status,
635 strict, patchdir, merge, all_files=all_files)
635 strict, patchdir, merge, all_files=all_files)
636 tr.close()
636 tr.close()
637 self.save_dirty()
637 self.save_dirty()
638 return ret
638 return ret
639 except:
639 except:
640 try:
640 try:
641 tr.abort()
641 tr.abort()
642 finally:
642 finally:
643 repo.invalidate()
643 repo.invalidate()
644 repo.dirstate.invalidate()
644 repo.dirstate.invalidate()
645 raise
645 raise
646 finally:
646 finally:
647 release(tr, lock, wlock)
647 release(tr, lock, wlock)
648 self.removeundo(repo)
648 self.removeundo(repo)
649
649
650 def _apply(self, repo, series, list=False, update_status=True,
650 def _apply(self, repo, series, list=False, update_status=True,
651 strict=False, patchdir=None, merge=None, all_files=None):
651 strict=False, patchdir=None, merge=None, all_files=None):
652 '''returns (error, hash)
652 '''returns (error, hash)
653 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
653 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
654 # TODO unify with commands.py
654 # TODO unify with commands.py
655 if not patchdir:
655 if not patchdir:
656 patchdir = self.path
656 patchdir = self.path
657 err = 0
657 err = 0
658 n = None
658 n = None
659 for patchname in series:
659 for patchname in series:
660 pushable, reason = self.pushable(patchname)
660 pushable, reason = self.pushable(patchname)
661 if not pushable:
661 if not pushable:
662 self.explain_pushable(patchname, all_patches=True)
662 self.explain_pushable(patchname, all_patches=True)
663 continue
663 continue
664 self.ui.status(_("applying %s\n") % patchname)
664 self.ui.status(_("applying %s\n") % patchname)
665 pf = os.path.join(patchdir, patchname)
665 pf = os.path.join(patchdir, patchname)
666
666
667 try:
667 try:
668 ph = patchheader(self.join(patchname), self.plainmode)
668 ph = patchheader(self.join(patchname), self.plainmode)
669 except IOError:
669 except IOError:
670 self.ui.warn(_("unable to read %s\n") % patchname)
670 self.ui.warn(_("unable to read %s\n") % patchname)
671 err = 1
671 err = 1
672 break
672 break
673
673
674 message = ph.message
674 message = ph.message
675 if not message:
675 if not message:
676 # The commit message should not be translated
676 # The commit message should not be translated
677 message = "imported patch %s\n" % patchname
677 message = "imported patch %s\n" % patchname
678 else:
678 else:
679 if list:
679 if list:
680 # The commit message should not be translated
680 # The commit message should not be translated
681 message.append("\nimported patch %s" % patchname)
681 message.append("\nimported patch %s" % patchname)
682 message = '\n'.join(message)
682 message = '\n'.join(message)
683
683
684 if ph.haspatch:
684 if ph.haspatch:
685 (patcherr, files, fuzz) = self.patch(repo, pf)
685 (patcherr, files, fuzz) = self.patch(repo, pf)
686 if all_files is not None:
686 if all_files is not None:
687 all_files.update(files)
687 all_files.update(files)
688 patcherr = not patcherr
688 patcherr = not patcherr
689 else:
689 else:
690 self.ui.warn(_("patch %s is empty\n") % patchname)
690 self.ui.warn(_("patch %s is empty\n") % patchname)
691 patcherr, files, fuzz = 0, [], 0
691 patcherr, files, fuzz = 0, [], 0
692
692
693 if merge and files:
693 if merge and files:
694 # Mark as removed/merged and update dirstate parent info
694 # Mark as removed/merged and update dirstate parent info
695 removed = []
695 removed = []
696 merged = []
696 merged = []
697 for f in files:
697 for f in files:
698 if os.path.lexists(repo.wjoin(f)):
698 if os.path.lexists(repo.wjoin(f)):
699 merged.append(f)
699 merged.append(f)
700 else:
700 else:
701 removed.append(f)
701 removed.append(f)
702 for f in removed:
702 for f in removed:
703 repo.dirstate.remove(f)
703 repo.dirstate.remove(f)
704 for f in merged:
704 for f in merged:
705 repo.dirstate.merge(f)
705 repo.dirstate.merge(f)
706 p1, p2 = repo.dirstate.parents()
706 p1, p2 = repo.dirstate.parents()
707 repo.dirstate.setparents(p1, merge)
707 repo.dirstate.setparents(p1, merge)
708
708
709 match = cmdutil.matchfiles(repo, files or [])
709 match = cmdutil.matchfiles(repo, files or [])
710 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
710 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
711
711
712 if n is None:
712 if n is None:
713 raise util.Abort(_("repository commit failed"))
713 raise util.Abort(_("repository commit failed"))
714
714
715 if update_status:
715 if update_status:
716 self.applied.append(statusentry(n, patchname))
716 self.applied.append(statusentry(n, patchname))
717
717
718 if patcherr:
718 if patcherr:
719 self.ui.warn(_("patch failed, rejects left in working dir\n"))
719 self.ui.warn(_("patch failed, rejects left in working dir\n"))
720 err = 2
720 err = 2
721 break
721 break
722
722
723 if fuzz and strict:
723 if fuzz and strict:
724 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
724 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
725 err = 3
725 err = 3
726 break
726 break
727 return (err, n)
727 return (err, n)
728
728
729 def _cleanup(self, patches, numrevs, keep=False):
729 def _cleanup(self, patches, numrevs, keep=False):
730 if not keep:
730 if not keep:
731 r = self.qrepo()
731 r = self.qrepo()
732 if r:
732 if r:
733 r[None].remove(patches, True)
733 r[None].remove(patches, True)
734 else:
734 else:
735 for p in patches:
735 for p in patches:
736 os.unlink(self.join(p))
736 os.unlink(self.join(p))
737
737
738 if numrevs:
738 if numrevs:
739 qfinished = self.applied[:numrevs]
739 qfinished = self.applied[:numrevs]
740 del self.applied[:numrevs]
740 del self.applied[:numrevs]
741 self.applied_dirty = 1
741 self.applied_dirty = 1
742
742
743 unknown = []
743 unknown = []
744
744
745 for (i, p) in sorted([(self.find_series(p), p) for p in patches],
745 for (i, p) in sorted([(self.find_series(p), p) for p in patches],
746 reverse=True):
746 reverse=True):
747 if i is not None:
747 if i is not None:
748 del self.full_series[i]
748 del self.full_series[i]
749 else:
749 else:
750 unknown.append(p)
750 unknown.append(p)
751
751
752 if unknown:
752 if unknown:
753 if numrevs:
753 if numrevs:
754 rev = dict((entry.name, entry.node) for entry in qfinished)
754 rev = dict((entry.name, entry.node) for entry in qfinished)
755 for p in unknown:
755 for p in unknown:
756 msg = _('revision %s refers to unknown patches: %s\n')
756 msg = _('revision %s refers to unknown patches: %s\n')
757 self.ui.warn(msg % (short(rev[p]), p))
757 self.ui.warn(msg % (short(rev[p]), p))
758 else:
758 else:
759 msg = _('unknown patches: %s\n')
759 msg = _('unknown patches: %s\n')
760 raise util.Abort(''.join(msg % p for p in unknown))
760 raise util.Abort(''.join(msg % p for p in unknown))
761
761
762 self.parse_series()
762 self.parse_series()
763 self.series_dirty = 1
763 self.series_dirty = 1
764
764
765 def _revpatches(self, repo, revs):
765 def _revpatches(self, repo, revs):
766 firstrev = repo[self.applied[0].node].rev()
766 firstrev = repo[self.applied[0].node].rev()
767 patches = []
767 patches = []
768 for i, rev in enumerate(revs):
768 for i, rev in enumerate(revs):
769
769
770 if rev < firstrev:
770 if rev < firstrev:
771 raise util.Abort(_('revision %d is not managed') % rev)
771 raise util.Abort(_('revision %d is not managed') % rev)
772
772
773 ctx = repo[rev]
773 ctx = repo[rev]
774 base = self.applied[i].node
774 base = self.applied[i].node
775 if ctx.node() != base:
775 if ctx.node() != base:
776 msg = _('cannot delete revision %d above applied patches')
776 msg = _('cannot delete revision %d above applied patches')
777 raise util.Abort(msg % rev)
777 raise util.Abort(msg % rev)
778
778
779 patch = self.applied[i].name
779 patch = self.applied[i].name
780 for fmt in ('[mq]: %s', 'imported patch %s'):
780 for fmt in ('[mq]: %s', 'imported patch %s'):
781 if ctx.description() == fmt % patch:
781 if ctx.description() == fmt % patch:
782 msg = _('patch %s finalized without changeset message\n')
782 msg = _('patch %s finalized without changeset message\n')
783 repo.ui.status(msg % patch)
783 repo.ui.status(msg % patch)
784 break
784 break
785
785
786 patches.append(patch)
786 patches.append(patch)
787 return patches
787 return patches
788
788
789 def finish(self, repo, revs):
789 def finish(self, repo, revs):
790 patches = self._revpatches(repo, sorted(revs))
790 patches = self._revpatches(repo, sorted(revs))
791 self._cleanup(patches, len(patches))
791 self._cleanup(patches, len(patches))
792
792
793 def delete(self, repo, patches, opts):
793 def delete(self, repo, patches, opts):
794 if not patches and not opts.get('rev'):
794 if not patches and not opts.get('rev'):
795 raise util.Abort(_('qdelete requires at least one revision or '
795 raise util.Abort(_('qdelete requires at least one revision or '
796 'patch name'))
796 'patch name'))
797
797
798 realpatches = []
798 realpatches = []
799 for patch in patches:
799 for patch in patches:
800 patch = self.lookup(patch, strict=True)
800 patch = self.lookup(patch, strict=True)
801 info = self.isapplied(patch)
801 info = self.isapplied(patch)
802 if info:
802 if info:
803 raise util.Abort(_("cannot delete applied patch %s") % patch)
803 raise util.Abort(_("cannot delete applied patch %s") % patch)
804 if patch not in self.series:
804 if patch not in self.series:
805 raise util.Abort(_("patch %s not in series file") % patch)
805 raise util.Abort(_("patch %s not in series file") % patch)
806 if patch not in realpatches:
806 if patch not in realpatches:
807 realpatches.append(patch)
807 realpatches.append(patch)
808
808
809 numrevs = 0
809 numrevs = 0
810 if opts.get('rev'):
810 if opts.get('rev'):
811 if not self.applied:
811 if not self.applied:
812 raise util.Abort(_('no patches applied'))
812 raise util.Abort(_('no patches applied'))
813 revs = cmdutil.revrange(repo, opts.get('rev'))
813 revs = cmdutil.revrange(repo, opts.get('rev'))
814 if len(revs) > 1 and revs[0] > revs[1]:
814 if len(revs) > 1 and revs[0] > revs[1]:
815 revs.reverse()
815 revs.reverse()
816 revpatches = self._revpatches(repo, revs)
816 revpatches = self._revpatches(repo, revs)
817 realpatches += revpatches
817 realpatches += revpatches
818 numrevs = len(revpatches)
818 numrevs = len(revpatches)
819
819
820 self._cleanup(realpatches, numrevs, opts.get('keep'))
820 self._cleanup(realpatches, numrevs, opts.get('keep'))
821
821
822 def check_toppatch(self, repo):
822 def check_toppatch(self, repo):
823 if self.applied:
823 if self.applied:
824 top = self.applied[-1].node
824 top = self.applied[-1].node
825 patch = self.applied[-1].name
825 patch = self.applied[-1].name
826 pp = repo.dirstate.parents()
826 pp = repo.dirstate.parents()
827 if top not in pp:
827 if top not in pp:
828 raise util.Abort(_("working directory revision is not qtip"))
828 raise util.Abort(_("working directory revision is not qtip"))
829 return top, patch
829 return top, patch
830 return None, None
830 return None, None
831
831
832 def check_substate(self, repo):
832 def check_substate(self, repo):
833 '''return list of subrepos at a different revision than substate.
833 '''return list of subrepos at a different revision than substate.
834 Abort if any subrepos have uncommitted changes.'''
834 Abort if any subrepos have uncommitted changes.'''
835 inclsubs = []
835 inclsubs = []
836 wctx = repo[None]
836 wctx = repo[None]
837 for s in wctx.substate:
837 for s in wctx.substate:
838 if wctx.sub(s).dirty(True):
838 if wctx.sub(s).dirty(True):
839 raise util.Abort(
839 raise util.Abort(
840 _("uncommitted changes in subrepository %s") % s)
840 _("uncommitted changes in subrepository %s") % s)
841 elif wctx.sub(s).dirty():
841 elif wctx.sub(s).dirty():
842 inclsubs.append(s)
842 inclsubs.append(s)
843 return inclsubs
843 return inclsubs
844
844
845 def localchangesfound(self, refresh=True):
845 def localchangesfound(self, refresh=True):
846 if refresh:
846 if refresh:
847 raise util.Abort(_("local changes found, refresh first"))
847 raise util.Abort(_("local changes found, refresh first"))
848 else:
848 else:
849 raise util.Abort(_("local changes found"))
849 raise util.Abort(_("local changes found"))
850
850
851 def check_localchanges(self, repo, force=False, refresh=True):
851 def check_localchanges(self, repo, force=False, refresh=True):
852 m, a, r, d = repo.status()[:4]
852 m, a, r, d = repo.status()[:4]
853 if (m or a or r or d) and not force:
853 if (m or a or r or d) and not force:
854 self.localchangesfound(refresh)
854 self.localchangesfound(refresh)
855 return m, a, r, d
855 return m, a, r, d
856
856
857 _reserved = ('series', 'status', 'guards', '.', '..')
857 _reserved = ('series', 'status', 'guards', '.', '..')
858 def check_reserved_name(self, name):
858 def check_reserved_name(self, name):
859 if name in self._reserved:
859 if name in self._reserved:
860 raise util.Abort(_('"%s" cannot be used as the name of a patch')
860 raise util.Abort(_('"%s" cannot be used as the name of a patch')
861 % name)
861 % name)
862 for prefix in ('.hg', '.mq'):
862 for prefix in ('.hg', '.mq'):
863 if name.startswith(prefix):
863 if name.startswith(prefix):
864 raise util.Abort(_('patch name cannot begin with "%s"')
864 raise util.Abort(_('patch name cannot begin with "%s"')
865 % prefix)
865 % prefix)
866 for c in ('#', ':'):
866 for c in ('#', ':'):
867 if c in name:
867 if c in name:
868 raise util.Abort(_('"%s" cannot be used in the name of a patch')
868 raise util.Abort(_('"%s" cannot be used in the name of a patch')
869 % c)
869 % c)
870
870
871
871
872 def new(self, repo, patchfn, *pats, **opts):
872 def new(self, repo, patchfn, *pats, **opts):
873 """options:
873 """options:
874 msg: a string or a no-argument function returning a string
874 msg: a string or a no-argument function returning a string
875 """
875 """
876 msg = opts.get('msg')
876 msg = opts.get('msg')
877 user = opts.get('user')
877 user = opts.get('user')
878 date = opts.get('date')
878 date = opts.get('date')
879 if date:
879 if date:
880 date = util.parsedate(date)
880 date = util.parsedate(date)
881 diffopts = self.diffopts({'git': opts.get('git')})
881 diffopts = self.diffopts({'git': opts.get('git')})
882 self.check_reserved_name(patchfn)
882 self.check_reserved_name(patchfn)
883 if os.path.exists(self.join(patchfn)):
883 if os.path.exists(self.join(patchfn)):
884 if os.path.isdir(self.join(patchfn)):
884 if os.path.isdir(self.join(patchfn)):
885 raise util.Abort(_('"%s" already exists as a directory')
885 raise util.Abort(_('"%s" already exists as a directory')
886 % patchfn)
886 % patchfn)
887 else:
887 else:
888 raise util.Abort(_('patch "%s" already exists') % patchfn)
888 raise util.Abort(_('patch "%s" already exists') % patchfn)
889
889
890 inclsubs = self.check_substate(repo)
890 inclsubs = self.check_substate(repo)
891 if inclsubs:
891 if inclsubs:
892 inclsubs.append('.hgsubstate')
892 inclsubs.append('.hgsubstate')
893 if opts.get('include') or opts.get('exclude') or pats:
893 if opts.get('include') or opts.get('exclude') or pats:
894 if inclsubs:
894 if inclsubs:
895 pats = list(pats or []) + inclsubs
895 pats = list(pats or []) + inclsubs
896 match = cmdutil.match(repo, pats, opts)
896 match = cmdutil.match(repo, pats, opts)
897 # detect missing files in pats
897 # detect missing files in pats
898 def badfn(f, msg):
898 def badfn(f, msg):
899 if f != '.hgsubstate': # .hgsubstate is auto-created
899 if f != '.hgsubstate': # .hgsubstate is auto-created
900 raise util.Abort('%s: %s' % (f, msg))
900 raise util.Abort('%s: %s' % (f, msg))
901 match.bad = badfn
901 match.bad = badfn
902 m, a, r, d = repo.status(match=match)[:4]
902 m, a, r, d = repo.status(match=match)[:4]
903 else:
903 else:
904 m, a, r, d = self.check_localchanges(repo, force=True)
904 m, a, r, d = self.check_localchanges(repo, force=True)
905 match = cmdutil.matchfiles(repo, m + a + r + inclsubs)
905 match = cmdutil.matchfiles(repo, m + a + r + inclsubs)
906 if len(repo[None].parents()) > 1:
906 if len(repo[None].parents()) > 1:
907 raise util.Abort(_('cannot manage merge changesets'))
907 raise util.Abort(_('cannot manage merge changesets'))
908 commitfiles = m + a + r
908 commitfiles = m + a + r
909 self.check_toppatch(repo)
909 self.check_toppatch(repo)
910 insert = self.full_series_end()
910 insert = self.full_series_end()
911 wlock = repo.wlock()
911 wlock = repo.wlock()
912 try:
912 try:
913 try:
913 try:
914 # if patch file write fails, abort early
914 # if patch file write fails, abort early
915 p = self.opener(patchfn, "w")
915 p = self.opener(patchfn, "w")
916 except IOError, e:
916 except IOError, e:
917 raise util.Abort(_('cannot write patch "%s": %s')
917 raise util.Abort(_('cannot write patch "%s": %s')
918 % (patchfn, e.strerror))
918 % (patchfn, e.strerror))
919 try:
919 try:
920 if self.plainmode:
920 if self.plainmode:
921 if user:
921 if user:
922 p.write("From: " + user + "\n")
922 p.write("From: " + user + "\n")
923 if not date:
923 if not date:
924 p.write("\n")
924 p.write("\n")
925 if date:
925 if date:
926 p.write("Date: %d %d\n\n" % date)
926 p.write("Date: %d %d\n\n" % date)
927 else:
927 else:
928 p.write("# HG changeset patch\n")
928 p.write("# HG changeset patch\n")
929 p.write("# Parent "
929 p.write("# Parent "
930 + hex(repo[None].p1().node()) + "\n")
930 + hex(repo[None].p1().node()) + "\n")
931 if user:
931 if user:
932 p.write("# User " + user + "\n")
932 p.write("# User " + user + "\n")
933 if date:
933 if date:
934 p.write("# Date %s %s\n\n" % date)
934 p.write("# Date %s %s\n\n" % date)
935 if hasattr(msg, '__call__'):
935 if hasattr(msg, '__call__'):
936 msg = msg()
936 msg = msg()
937 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
937 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
938 n = repo.commit(commitmsg, user, date, match=match, force=True)
938 n = repo.commit(commitmsg, user, date, match=match, force=True)
939 if n is None:
939 if n is None:
940 raise util.Abort(_("repo commit failed"))
940 raise util.Abort(_("repo commit failed"))
941 try:
941 try:
942 self.full_series[insert:insert] = [patchfn]
942 self.full_series[insert:insert] = [patchfn]
943 self.applied.append(statusentry(n, patchfn))
943 self.applied.append(statusentry(n, patchfn))
944 self.parse_series()
944 self.parse_series()
945 self.series_dirty = 1
945 self.series_dirty = 1
946 self.applied_dirty = 1
946 self.applied_dirty = 1
947 if msg:
947 if msg:
948 msg = msg + "\n\n"
948 msg = msg + "\n\n"
949 p.write(msg)
949 p.write(msg)
950 if commitfiles:
950 if commitfiles:
951 parent = self.qparents(repo, n)
951 parent = self.qparents(repo, n)
952 chunks = patchmod.diff(repo, node1=parent, node2=n,
952 chunks = patchmod.diff(repo, node1=parent, node2=n,
953 match=match, opts=diffopts)
953 match=match, opts=diffopts)
954 for chunk in chunks:
954 for chunk in chunks:
955 p.write(chunk)
955 p.write(chunk)
956 p.close()
956 p.close()
957 wlock.release()
957 wlock.release()
958 wlock = None
958 wlock = None
959 r = self.qrepo()
959 r = self.qrepo()
960 if r:
960 if r:
961 r[None].add([patchfn])
961 r[None].add([patchfn])
962 except:
962 except:
963 repo.rollback()
963 repo.rollback()
964 raise
964 raise
965 except Exception:
965 except Exception:
966 patchpath = self.join(patchfn)
966 patchpath = self.join(patchfn)
967 try:
967 try:
968 os.unlink(patchpath)
968 os.unlink(patchpath)
969 except:
969 except:
970 self.ui.warn(_('error unlinking %s\n') % patchpath)
970 self.ui.warn(_('error unlinking %s\n') % patchpath)
971 raise
971 raise
972 self.removeundo(repo)
972 self.removeundo(repo)
973 finally:
973 finally:
974 release(wlock)
974 release(wlock)
975
975
976 def strip(self, repo, revs, update=True, backup="all", force=None):
976 def strip(self, repo, revs, update=True, backup="all", force=None):
977 wlock = lock = None
977 wlock = lock = None
978 try:
978 try:
979 wlock = repo.wlock()
979 wlock = repo.wlock()
980 lock = repo.lock()
980 lock = repo.lock()
981
981
982 if update:
982 if update:
983 self.check_localchanges(repo, force=force, refresh=False)
983 self.check_localchanges(repo, force=force, refresh=False)
984 urev = self.qparents(repo, revs[0])
984 urev = self.qparents(repo, revs[0])
985 hg.clean(repo, urev)
985 hg.clean(repo, urev)
986 repo.dirstate.write()
986 repo.dirstate.write()
987
987
988 self.removeundo(repo)
988 self.removeundo(repo)
989 for rev in revs:
989 for rev in revs:
990 repair.strip(self.ui, repo, rev, backup)
990 repair.strip(self.ui, repo, rev, backup)
991 # strip may have unbundled a set of backed up revisions after
991 # strip may have unbundled a set of backed up revisions after
992 # the actual strip
992 # the actual strip
993 self.removeundo(repo)
993 self.removeundo(repo)
994 finally:
994 finally:
995 release(lock, wlock)
995 release(lock, wlock)
996
996
997 def isapplied(self, patch):
997 def isapplied(self, patch):
998 """returns (index, rev, patch)"""
998 """returns (index, rev, patch)"""
999 for i, a in enumerate(self.applied):
999 for i, a in enumerate(self.applied):
1000 if a.name == patch:
1000 if a.name == patch:
1001 return (i, a.node, a.name)
1001 return (i, a.node, a.name)
1002 return None
1002 return None
1003
1003
1004 # if the exact patch name does not exist, we try a few
1004 # if the exact patch name does not exist, we try a few
1005 # variations. If strict is passed, we try only #1
1005 # variations. If strict is passed, we try only #1
1006 #
1006 #
1007 # 1) a number to indicate an offset in the series file
1007 # 1) a number to indicate an offset in the series file
1008 # 2) a unique substring of the patch name was given
1008 # 2) a unique substring of the patch name was given
1009 # 3) patchname[-+]num to indicate an offset in the series file
1009 # 3) patchname[-+]num to indicate an offset in the series file
1010 def lookup(self, patch, strict=False):
1010 def lookup(self, patch, strict=False):
1011 patch = patch and str(patch)
1011 patch = patch and str(patch)
1012
1012
1013 def partial_name(s):
1013 def partial_name(s):
1014 if s in self.series:
1014 if s in self.series:
1015 return s
1015 return s
1016 matches = [x for x in self.series if s in x]
1016 matches = [x for x in self.series if s in x]
1017 if len(matches) > 1:
1017 if len(matches) > 1:
1018 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1018 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1019 for m in matches:
1019 for m in matches:
1020 self.ui.warn(' %s\n' % m)
1020 self.ui.warn(' %s\n' % m)
1021 return None
1021 return None
1022 if matches:
1022 if matches:
1023 return matches[0]
1023 return matches[0]
1024 if self.series and self.applied:
1024 if self.series and self.applied:
1025 if s == 'qtip':
1025 if s == 'qtip':
1026 return self.series[self.series_end(True)-1]
1026 return self.series[self.series_end(True)-1]
1027 if s == 'qbase':
1027 if s == 'qbase':
1028 return self.series[0]
1028 return self.series[0]
1029 return None
1029 return None
1030
1030
1031 if patch is None:
1031 if patch is None:
1032 return None
1032 return None
1033 if patch in self.series:
1033 if patch in self.series:
1034 return patch
1034 return patch
1035
1035
1036 if not os.path.isfile(self.join(patch)):
1036 if not os.path.isfile(self.join(patch)):
1037 try:
1037 try:
1038 sno = int(patch)
1038 sno = int(patch)
1039 except (ValueError, OverflowError):
1039 except (ValueError, OverflowError):
1040 pass
1040 pass
1041 else:
1041 else:
1042 if -len(self.series) <= sno < len(self.series):
1042 if -len(self.series) <= sno < len(self.series):
1043 return self.series[sno]
1043 return self.series[sno]
1044
1044
1045 if not strict:
1045 if not strict:
1046 res = partial_name(patch)
1046 res = partial_name(patch)
1047 if res:
1047 if res:
1048 return res
1048 return res
1049 minus = patch.rfind('-')
1049 minus = patch.rfind('-')
1050 if minus >= 0:
1050 if minus >= 0:
1051 res = partial_name(patch[:minus])
1051 res = partial_name(patch[:minus])
1052 if res:
1052 if res:
1053 i = self.series.index(res)
1053 i = self.series.index(res)
1054 try:
1054 try:
1055 off = int(patch[minus + 1:] or 1)
1055 off = int(patch[minus + 1:] or 1)
1056 except (ValueError, OverflowError):
1056 except (ValueError, OverflowError):
1057 pass
1057 pass
1058 else:
1058 else:
1059 if i - off >= 0:
1059 if i - off >= 0:
1060 return self.series[i - off]
1060 return self.series[i - off]
1061 plus = patch.rfind('+')
1061 plus = patch.rfind('+')
1062 if plus >= 0:
1062 if plus >= 0:
1063 res = partial_name(patch[:plus])
1063 res = partial_name(patch[:plus])
1064 if res:
1064 if res:
1065 i = self.series.index(res)
1065 i = self.series.index(res)
1066 try:
1066 try:
1067 off = int(patch[plus + 1:] or 1)
1067 off = int(patch[plus + 1:] or 1)
1068 except (ValueError, OverflowError):
1068 except (ValueError, OverflowError):
1069 pass
1069 pass
1070 else:
1070 else:
1071 if i + off < len(self.series):
1071 if i + off < len(self.series):
1072 return self.series[i + off]
1072 return self.series[i + off]
1073 raise util.Abort(_("patch %s not in series") % patch)
1073 raise util.Abort(_("patch %s not in series") % patch)
1074
1074
1075 def push(self, repo, patch=None, force=False, list=False,
1075 def push(self, repo, patch=None, force=False, list=False,
1076 mergeq=None, all=False, move=False, exact=False):
1076 mergeq=None, all=False, move=False, exact=False):
1077 diffopts = self.diffopts()
1077 diffopts = self.diffopts()
1078 wlock = repo.wlock()
1078 wlock = repo.wlock()
1079 try:
1079 try:
1080 heads = []
1080 heads = []
1081 for b, ls in repo.branchmap().iteritems():
1081 for b, ls in repo.branchmap().iteritems():
1082 heads += ls
1082 heads += ls
1083 if not heads:
1083 if not heads:
1084 heads = [nullid]
1084 heads = [nullid]
1085 if repo.dirstate.p1() not in heads and not exact:
1085 if repo.dirstate.p1() not in heads and not exact:
1086 self.ui.status(_("(working directory not at a head)\n"))
1086 self.ui.status(_("(working directory not at a head)\n"))
1087
1087
1088 if not self.series:
1088 if not self.series:
1089 self.ui.warn(_('no patches in series\n'))
1089 self.ui.warn(_('no patches in series\n'))
1090 return 0
1090 return 0
1091
1091
1092 patch = self.lookup(patch)
1092 patch = self.lookup(patch)
1093 # Suppose our series file is: A B C and the current 'top'
1093 # Suppose our series file is: A B C and the current 'top'
1094 # patch is B. qpush C should be performed (moving forward)
1094 # patch is B. qpush C should be performed (moving forward)
1095 # qpush B is a NOP (no change) qpush A is an error (can't
1095 # qpush B is a NOP (no change) qpush A is an error (can't
1096 # go backwards with qpush)
1096 # go backwards with qpush)
1097 if patch:
1097 if patch:
1098 info = self.isapplied(patch)
1098 info = self.isapplied(patch)
1099 if info and info[0] >= len(self.applied) - 1:
1099 if info and info[0] >= len(self.applied) - 1:
1100 self.ui.warn(
1100 self.ui.warn(
1101 _('qpush: %s is already at the top\n') % patch)
1101 _('qpush: %s is already at the top\n') % patch)
1102 return 0
1102 return 0
1103
1103
1104 pushable, reason = self.pushable(patch)
1104 pushable, reason = self.pushable(patch)
1105 if pushable:
1105 if pushable:
1106 if self.series.index(patch) < self.series_end():
1106 if self.series.index(patch) < self.series_end():
1107 raise util.Abort(
1107 raise util.Abort(
1108 _("cannot push to a previous patch: %s") % patch)
1108 _("cannot push to a previous patch: %s") % patch)
1109 else:
1109 else:
1110 if reason:
1110 if reason:
1111 reason = _('guarded by %r') % reason
1111 reason = _('guarded by %r') % reason
1112 else:
1112 else:
1113 reason = _('no matching guards')
1113 reason = _('no matching guards')
1114 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1114 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1115 return 1
1115 return 1
1116 elif all:
1116 elif all:
1117 patch = self.series[-1]
1117 patch = self.series[-1]
1118 if self.isapplied(patch):
1118 if self.isapplied(patch):
1119 self.ui.warn(_('all patches are currently applied\n'))
1119 self.ui.warn(_('all patches are currently applied\n'))
1120 return 0
1120 return 0
1121
1121
1122 # Following the above example, starting at 'top' of B:
1122 # Following the above example, starting at 'top' of B:
1123 # qpush should be performed (pushes C), but a subsequent
1123 # qpush should be performed (pushes C), but a subsequent
1124 # qpush without an argument is an error (nothing to
1124 # qpush without an argument is an error (nothing to
1125 # apply). This allows a loop of "...while hg qpush..." to
1125 # apply). This allows a loop of "...while hg qpush..." to
1126 # work as it detects an error when done
1126 # work as it detects an error when done
1127 start = self.series_end()
1127 start = self.series_end()
1128 if start == len(self.series):
1128 if start == len(self.series):
1129 self.ui.warn(_('patch series already fully applied\n'))
1129 self.ui.warn(_('patch series already fully applied\n'))
1130 return 1
1130 return 1
1131
1131
1132 if exact:
1132 if exact:
1133 if move:
1133 if move:
1134 raise util.Abort(_("cannot use --exact and --move together"))
1134 raise util.Abort(_("cannot use --exact and --move together"))
1135 if self.applied:
1135 if self.applied:
1136 raise util.Abort(_("cannot push --exact with applied patches"))
1136 raise util.Abort(_("cannot push --exact with applied patches"))
1137 root = self.series[start]
1137 root = self.series[start]
1138 target = patchheader(self.join(root), self.plainmode).parent
1138 target = patchheader(self.join(root), self.plainmode).parent
1139 if not target:
1139 if not target:
1140 raise util.Abort(_("%s does not have a parent recorded" % root))
1140 raise util.Abort(_("%s does not have a parent recorded" % root))
1141 if not repo[target] == repo['.']:
1141 if not repo[target] == repo['.']:
1142 hg.update(repo, target)
1142 hg.update(repo, target)
1143
1143
1144 if move:
1144 if move:
1145 if not patch:
1145 if not patch:
1146 raise util.Abort(_("please specify the patch to move"))
1146 raise util.Abort(_("please specify the patch to move"))
1147 for i, rpn in enumerate(self.full_series[start:]):
1147 for i, rpn in enumerate(self.full_series[start:]):
1148 # strip markers for patch guards
1148 # strip markers for patch guards
1149 if self.guard_re.split(rpn, 1)[0] == patch:
1149 if self.guard_re.split(rpn, 1)[0] == patch:
1150 break
1150 break
1151 index = start + i
1151 index = start + i
1152 assert index < len(self.full_series)
1152 assert index < len(self.full_series)
1153 fullpatch = self.full_series[index]
1153 fullpatch = self.full_series[index]
1154 del self.full_series[index]
1154 del self.full_series[index]
1155 self.full_series.insert(start, fullpatch)
1155 self.full_series.insert(start, fullpatch)
1156 self.parse_series()
1156 self.parse_series()
1157 self.series_dirty = 1
1157 self.series_dirty = 1
1158
1158
1159 self.applied_dirty = 1
1159 self.applied_dirty = 1
1160 if start > 0:
1160 if start > 0:
1161 self.check_toppatch(repo)
1161 self.check_toppatch(repo)
1162 if not patch:
1162 if not patch:
1163 patch = self.series[start]
1163 patch = self.series[start]
1164 end = start + 1
1164 end = start + 1
1165 else:
1165 else:
1166 end = self.series.index(patch, start) + 1
1166 end = self.series.index(patch, start) + 1
1167
1167
1168 s = self.series[start:end]
1168 s = self.series[start:end]
1169
1169
1170 if not force:
1170 if not force:
1171 mm, aa, rr, dd = repo.status()[:4]
1171 mm, aa, rr, dd = repo.status()[:4]
1172 wcfiles = set(mm + aa + rr + dd)
1172 wcfiles = set(mm + aa + rr + dd)
1173 if wcfiles:
1173 if wcfiles:
1174 for patchname in s:
1174 for patchname in s:
1175 pf = os.path.join(self.path, patchname)
1175 pf = os.path.join(self.path, patchname)
1176 patchfiles = patchmod.changedfiles(pf, strip=1)
1176 patchfiles = patchmod.changedfiles(pf, strip=1)
1177 if not wcfiles.isdisjoint(patchfiles):
1177 if wcfiles.intersection(patchfiles):
1178 self.localchangesfound(self.applied)
1178 self.localchangesfound(self.applied)
1179 elif mergeq:
1179 elif mergeq:
1180 self.check_localchanges(refresh=self.applied)
1180 self.check_localchanges(refresh=self.applied)
1181
1181
1182 all_files = set()
1182 all_files = set()
1183 try:
1183 try:
1184 if mergeq:
1184 if mergeq:
1185 ret = self.mergepatch(repo, mergeq, s, diffopts)
1185 ret = self.mergepatch(repo, mergeq, s, diffopts)
1186 else:
1186 else:
1187 ret = self.apply(repo, s, list, all_files=all_files)
1187 ret = self.apply(repo, s, list, all_files=all_files)
1188 except:
1188 except:
1189 self.ui.warn(_('cleaning up working directory...'))
1189 self.ui.warn(_('cleaning up working directory...'))
1190 node = repo.dirstate.p1()
1190 node = repo.dirstate.p1()
1191 hg.revert(repo, node, None)
1191 hg.revert(repo, node, None)
1192 # only remove unknown files that we know we touched or
1192 # only remove unknown files that we know we touched or
1193 # created while patching
1193 # created while patching
1194 for f in all_files:
1194 for f in all_files:
1195 if f not in repo.dirstate:
1195 if f not in repo.dirstate:
1196 try:
1196 try:
1197 util.unlinkpath(repo.wjoin(f))
1197 util.unlinkpath(repo.wjoin(f))
1198 except OSError, inst:
1198 except OSError, inst:
1199 if inst.errno != errno.ENOENT:
1199 if inst.errno != errno.ENOENT:
1200 raise
1200 raise
1201 self.ui.warn(_('done\n'))
1201 self.ui.warn(_('done\n'))
1202 raise
1202 raise
1203
1203
1204 if not self.applied:
1204 if not self.applied:
1205 return ret[0]
1205 return ret[0]
1206 top = self.applied[-1].name
1206 top = self.applied[-1].name
1207 if ret[0] and ret[0] > 1:
1207 if ret[0] and ret[0] > 1:
1208 msg = _("errors during apply, please fix and refresh %s\n")
1208 msg = _("errors during apply, please fix and refresh %s\n")
1209 self.ui.write(msg % top)
1209 self.ui.write(msg % top)
1210 else:
1210 else:
1211 self.ui.write(_("now at: %s\n") % top)
1211 self.ui.write(_("now at: %s\n") % top)
1212 return ret[0]
1212 return ret[0]
1213
1213
1214 finally:
1214 finally:
1215 wlock.release()
1215 wlock.release()
1216
1216
1217 def pop(self, repo, patch=None, force=False, update=True, all=False):
1217 def pop(self, repo, patch=None, force=False, update=True, all=False):
1218 wlock = repo.wlock()
1218 wlock = repo.wlock()
1219 try:
1219 try:
1220 if patch:
1220 if patch:
1221 # index, rev, patch
1221 # index, rev, patch
1222 info = self.isapplied(patch)
1222 info = self.isapplied(patch)
1223 if not info:
1223 if not info:
1224 patch = self.lookup(patch)
1224 patch = self.lookup(patch)
1225 info = self.isapplied(patch)
1225 info = self.isapplied(patch)
1226 if not info:
1226 if not info:
1227 raise util.Abort(_("patch %s is not applied") % patch)
1227 raise util.Abort(_("patch %s is not applied") % patch)
1228
1228
1229 if not self.applied:
1229 if not self.applied:
1230 # Allow qpop -a to work repeatedly,
1230 # Allow qpop -a to work repeatedly,
1231 # but not qpop without an argument
1231 # but not qpop without an argument
1232 self.ui.warn(_("no patches applied\n"))
1232 self.ui.warn(_("no patches applied\n"))
1233 return not all
1233 return not all
1234
1234
1235 if all:
1235 if all:
1236 start = 0
1236 start = 0
1237 elif patch:
1237 elif patch:
1238 start = info[0] + 1
1238 start = info[0] + 1
1239 else:
1239 else:
1240 start = len(self.applied) - 1
1240 start = len(self.applied) - 1
1241
1241
1242 if start >= len(self.applied):
1242 if start >= len(self.applied):
1243 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1243 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1244 return
1244 return
1245
1245
1246 if not update:
1246 if not update:
1247 parents = repo.dirstate.parents()
1247 parents = repo.dirstate.parents()
1248 rr = [x.node for x in self.applied]
1248 rr = [x.node for x in self.applied]
1249 for p in parents:
1249 for p in parents:
1250 if p in rr:
1250 if p in rr:
1251 self.ui.warn(_("qpop: forcing dirstate update\n"))
1251 self.ui.warn(_("qpop: forcing dirstate update\n"))
1252 update = True
1252 update = True
1253 else:
1253 else:
1254 parents = [p.node() for p in repo[None].parents()]
1254 parents = [p.node() for p in repo[None].parents()]
1255 needupdate = False
1255 needupdate = False
1256 for entry in self.applied[start:]:
1256 for entry in self.applied[start:]:
1257 if entry.node in parents:
1257 if entry.node in parents:
1258 needupdate = True
1258 needupdate = True
1259 break
1259 break
1260 update = needupdate
1260 update = needupdate
1261
1261
1262 self.applied_dirty = 1
1262 self.applied_dirty = 1
1263 end = len(self.applied)
1263 end = len(self.applied)
1264 rev = self.applied[start].node
1264 rev = self.applied[start].node
1265 if update:
1265 if update:
1266 top = self.check_toppatch(repo)[0]
1266 top = self.check_toppatch(repo)[0]
1267
1267
1268 try:
1268 try:
1269 heads = repo.changelog.heads(rev)
1269 heads = repo.changelog.heads(rev)
1270 except error.LookupError:
1270 except error.LookupError:
1271 node = short(rev)
1271 node = short(rev)
1272 raise util.Abort(_('trying to pop unknown node %s') % node)
1272 raise util.Abort(_('trying to pop unknown node %s') % node)
1273
1273
1274 if heads != [self.applied[-1].node]:
1274 if heads != [self.applied[-1].node]:
1275 raise util.Abort(_("popping would remove a revision not "
1275 raise util.Abort(_("popping would remove a revision not "
1276 "managed by this patch queue"))
1276 "managed by this patch queue"))
1277
1277
1278 # we know there are no local changes, so we can make a simplified
1278 # we know there are no local changes, so we can make a simplified
1279 # form of hg.update.
1279 # form of hg.update.
1280 if update:
1280 if update:
1281 qp = self.qparents(repo, rev)
1281 qp = self.qparents(repo, rev)
1282 ctx = repo[qp]
1282 ctx = repo[qp]
1283 m, a, r, d = repo.status(qp, top)[:4]
1283 m, a, r, d = repo.status(qp, top)[:4]
1284 parentfiles = set(m + a + r + d)
1284 parentfiles = set(m + a + r + d)
1285 if not force and parentfiles:
1285 if not force and parentfiles:
1286 mm, aa, rr, dd = repo.status()[:4]
1286 mm, aa, rr, dd = repo.status()[:4]
1287 wcfiles = set(mm + aa + rr + dd)
1287 wcfiles = set(mm + aa + rr + dd)
1288 if not wcfiles.isdisjoint(parentfiles):
1288 if wcfiles.intersection(parentfiles):
1289 self.localchangesfound()
1289 self.localchangesfound()
1290 if d:
1290 if d:
1291 raise util.Abort(_("deletions found between repo revs"))
1291 raise util.Abort(_("deletions found between repo revs"))
1292 for f in a:
1292 for f in a:
1293 try:
1293 try:
1294 util.unlinkpath(repo.wjoin(f))
1294 util.unlinkpath(repo.wjoin(f))
1295 except OSError, e:
1295 except OSError, e:
1296 if e.errno != errno.ENOENT:
1296 if e.errno != errno.ENOENT:
1297 raise
1297 raise
1298 repo.dirstate.forget(f)
1298 repo.dirstate.forget(f)
1299 for f in m + r:
1299 for f in m + r:
1300 fctx = ctx[f]
1300 fctx = ctx[f]
1301 repo.wwrite(f, fctx.data(), fctx.flags())
1301 repo.wwrite(f, fctx.data(), fctx.flags())
1302 repo.dirstate.normal(f)
1302 repo.dirstate.normal(f)
1303 repo.dirstate.setparents(qp, nullid)
1303 repo.dirstate.setparents(qp, nullid)
1304 for patch in reversed(self.applied[start:end]):
1304 for patch in reversed(self.applied[start:end]):
1305 self.ui.status(_("popping %s\n") % patch.name)
1305 self.ui.status(_("popping %s\n") % patch.name)
1306 del self.applied[start:end]
1306 del self.applied[start:end]
1307 self.strip(repo, [rev], update=False, backup='strip')
1307 self.strip(repo, [rev], update=False, backup='strip')
1308 if self.applied:
1308 if self.applied:
1309 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1309 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1310 else:
1310 else:
1311 self.ui.write(_("patch queue now empty\n"))
1311 self.ui.write(_("patch queue now empty\n"))
1312 finally:
1312 finally:
1313 wlock.release()
1313 wlock.release()
1314
1314
1315 def diff(self, repo, pats, opts):
1315 def diff(self, repo, pats, opts):
1316 top, patch = self.check_toppatch(repo)
1316 top, patch = self.check_toppatch(repo)
1317 if not top:
1317 if not top:
1318 self.ui.write(_("no patches applied\n"))
1318 self.ui.write(_("no patches applied\n"))
1319 return
1319 return
1320 qp = self.qparents(repo, top)
1320 qp = self.qparents(repo, top)
1321 if opts.get('reverse'):
1321 if opts.get('reverse'):
1322 node1, node2 = None, qp
1322 node1, node2 = None, qp
1323 else:
1323 else:
1324 node1, node2 = qp, None
1324 node1, node2 = qp, None
1325 diffopts = self.diffopts(opts, patch)
1325 diffopts = self.diffopts(opts, patch)
1326 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1326 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1327
1327
1328 def refresh(self, repo, pats=None, **opts):
1328 def refresh(self, repo, pats=None, **opts):
1329 if not self.applied:
1329 if not self.applied:
1330 self.ui.write(_("no patches applied\n"))
1330 self.ui.write(_("no patches applied\n"))
1331 return 1
1331 return 1
1332 msg = opts.get('msg', '').rstrip()
1332 msg = opts.get('msg', '').rstrip()
1333 newuser = opts.get('user')
1333 newuser = opts.get('user')
1334 newdate = opts.get('date')
1334 newdate = opts.get('date')
1335 if newdate:
1335 if newdate:
1336 newdate = '%d %d' % util.parsedate(newdate)
1336 newdate = '%d %d' % util.parsedate(newdate)
1337 wlock = repo.wlock()
1337 wlock = repo.wlock()
1338
1338
1339 try:
1339 try:
1340 self.check_toppatch(repo)
1340 self.check_toppatch(repo)
1341 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1341 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1342 if repo.changelog.heads(top) != [top]:
1342 if repo.changelog.heads(top) != [top]:
1343 raise util.Abort(_("cannot refresh a revision with children"))
1343 raise util.Abort(_("cannot refresh a revision with children"))
1344
1344
1345 inclsubs = self.check_substate(repo)
1345 inclsubs = self.check_substate(repo)
1346
1346
1347 cparents = repo.changelog.parents(top)
1347 cparents = repo.changelog.parents(top)
1348 patchparent = self.qparents(repo, top)
1348 patchparent = self.qparents(repo, top)
1349 ph = patchheader(self.join(patchfn), self.plainmode)
1349 ph = patchheader(self.join(patchfn), self.plainmode)
1350 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1350 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1351 if msg:
1351 if msg:
1352 ph.setmessage(msg)
1352 ph.setmessage(msg)
1353 if newuser:
1353 if newuser:
1354 ph.setuser(newuser)
1354 ph.setuser(newuser)
1355 if newdate:
1355 if newdate:
1356 ph.setdate(newdate)
1356 ph.setdate(newdate)
1357 ph.setparent(hex(patchparent))
1357 ph.setparent(hex(patchparent))
1358
1358
1359 # only commit new patch when write is complete
1359 # only commit new patch when write is complete
1360 patchf = self.opener(patchfn, 'w', atomictemp=True)
1360 patchf = self.opener(patchfn, 'w', atomictemp=True)
1361
1361
1362 comments = str(ph)
1362 comments = str(ph)
1363 if comments:
1363 if comments:
1364 patchf.write(comments)
1364 patchf.write(comments)
1365
1365
1366 # update the dirstate in place, strip off the qtip commit
1366 # update the dirstate in place, strip off the qtip commit
1367 # and then commit.
1367 # and then commit.
1368 #
1368 #
1369 # this should really read:
1369 # this should really read:
1370 # mm, dd, aa = repo.status(top, patchparent)[:3]
1370 # mm, dd, aa = repo.status(top, patchparent)[:3]
1371 # but we do it backwards to take advantage of manifest/chlog
1371 # but we do it backwards to take advantage of manifest/chlog
1372 # caching against the next repo.status call
1372 # caching against the next repo.status call
1373 mm, aa, dd = repo.status(patchparent, top)[:3]
1373 mm, aa, dd = repo.status(patchparent, top)[:3]
1374 changes = repo.changelog.read(top)
1374 changes = repo.changelog.read(top)
1375 man = repo.manifest.read(changes[0])
1375 man = repo.manifest.read(changes[0])
1376 aaa = aa[:]
1376 aaa = aa[:]
1377 matchfn = cmdutil.match(repo, pats, opts)
1377 matchfn = cmdutil.match(repo, pats, opts)
1378 # in short mode, we only diff the files included in the
1378 # in short mode, we only diff the files included in the
1379 # patch already plus specified files
1379 # patch already plus specified files
1380 if opts.get('short'):
1380 if opts.get('short'):
1381 # if amending a patch, we start with existing
1381 # if amending a patch, we start with existing
1382 # files plus specified files - unfiltered
1382 # files plus specified files - unfiltered
1383 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1383 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1384 # filter with inc/exl options
1384 # filter with inc/exl options
1385 matchfn = cmdutil.match(repo, opts=opts)
1385 matchfn = cmdutil.match(repo, opts=opts)
1386 else:
1386 else:
1387 match = cmdutil.matchall(repo)
1387 match = cmdutil.matchall(repo)
1388 m, a, r, d = repo.status(match=match)[:4]
1388 m, a, r, d = repo.status(match=match)[:4]
1389 mm = set(mm)
1389 mm = set(mm)
1390 aa = set(aa)
1390 aa = set(aa)
1391 dd = set(dd)
1391 dd = set(dd)
1392
1392
1393 # we might end up with files that were added between
1393 # we might end up with files that were added between
1394 # qtip and the dirstate parent, but then changed in the
1394 # qtip and the dirstate parent, but then changed in the
1395 # local dirstate. in this case, we want them to only
1395 # local dirstate. in this case, we want them to only
1396 # show up in the added section
1396 # show up in the added section
1397 for x in m:
1397 for x in m:
1398 if x not in aa:
1398 if x not in aa:
1399 mm.add(x)
1399 mm.add(x)
1400 # we might end up with files added by the local dirstate that
1400 # we might end up with files added by the local dirstate that
1401 # were deleted by the patch. In this case, they should only
1401 # were deleted by the patch. In this case, they should only
1402 # show up in the changed section.
1402 # show up in the changed section.
1403 for x in a:
1403 for x in a:
1404 if x in dd:
1404 if x in dd:
1405 dd.remove(x)
1405 dd.remove(x)
1406 mm.add(x)
1406 mm.add(x)
1407 else:
1407 else:
1408 aa.add(x)
1408 aa.add(x)
1409 # make sure any files deleted in the local dirstate
1409 # make sure any files deleted in the local dirstate
1410 # are not in the add or change column of the patch
1410 # are not in the add or change column of the patch
1411 forget = []
1411 forget = []
1412 for x in d + r:
1412 for x in d + r:
1413 if x in aa:
1413 if x in aa:
1414 aa.remove(x)
1414 aa.remove(x)
1415 forget.append(x)
1415 forget.append(x)
1416 continue
1416 continue
1417 else:
1417 else:
1418 mm.discard(x)
1418 mm.discard(x)
1419 dd.add(x)
1419 dd.add(x)
1420
1420
1421 m = list(mm)
1421 m = list(mm)
1422 r = list(dd)
1422 r = list(dd)
1423 a = list(aa)
1423 a = list(aa)
1424 c = [filter(matchfn, l) for l in (m, a, r)]
1424 c = [filter(matchfn, l) for l in (m, a, r)]
1425 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1425 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1426 chunks = patchmod.diff(repo, patchparent, match=match,
1426 chunks = patchmod.diff(repo, patchparent, match=match,
1427 changes=c, opts=diffopts)
1427 changes=c, opts=diffopts)
1428 for chunk in chunks:
1428 for chunk in chunks:
1429 patchf.write(chunk)
1429 patchf.write(chunk)
1430
1430
1431 try:
1431 try:
1432 if diffopts.git or diffopts.upgrade:
1432 if diffopts.git or diffopts.upgrade:
1433 copies = {}
1433 copies = {}
1434 for dst in a:
1434 for dst in a:
1435 src = repo.dirstate.copied(dst)
1435 src = repo.dirstate.copied(dst)
1436 # during qfold, the source file for copies may
1436 # during qfold, the source file for copies may
1437 # be removed. Treat this as a simple add.
1437 # be removed. Treat this as a simple add.
1438 if src is not None and src in repo.dirstate:
1438 if src is not None and src in repo.dirstate:
1439 copies.setdefault(src, []).append(dst)
1439 copies.setdefault(src, []).append(dst)
1440 repo.dirstate.add(dst)
1440 repo.dirstate.add(dst)
1441 # remember the copies between patchparent and qtip
1441 # remember the copies between patchparent and qtip
1442 for dst in aaa:
1442 for dst in aaa:
1443 f = repo.file(dst)
1443 f = repo.file(dst)
1444 src = f.renamed(man[dst])
1444 src = f.renamed(man[dst])
1445 if src:
1445 if src:
1446 copies.setdefault(src[0], []).extend(
1446 copies.setdefault(src[0], []).extend(
1447 copies.get(dst, []))
1447 copies.get(dst, []))
1448 if dst in a:
1448 if dst in a:
1449 copies[src[0]].append(dst)
1449 copies[src[0]].append(dst)
1450 # we can't copy a file created by the patch itself
1450 # we can't copy a file created by the patch itself
1451 if dst in copies:
1451 if dst in copies:
1452 del copies[dst]
1452 del copies[dst]
1453 for src, dsts in copies.iteritems():
1453 for src, dsts in copies.iteritems():
1454 for dst in dsts:
1454 for dst in dsts:
1455 repo.dirstate.copy(src, dst)
1455 repo.dirstate.copy(src, dst)
1456 else:
1456 else:
1457 for dst in a:
1457 for dst in a:
1458 repo.dirstate.add(dst)
1458 repo.dirstate.add(dst)
1459 # Drop useless copy information
1459 # Drop useless copy information
1460 for f in list(repo.dirstate.copies()):
1460 for f in list(repo.dirstate.copies()):
1461 repo.dirstate.copy(None, f)
1461 repo.dirstate.copy(None, f)
1462 for f in r:
1462 for f in r:
1463 repo.dirstate.remove(f)
1463 repo.dirstate.remove(f)
1464 # if the patch excludes a modified file, mark that
1464 # if the patch excludes a modified file, mark that
1465 # file with mtime=0 so status can see it.
1465 # file with mtime=0 so status can see it.
1466 mm = []
1466 mm = []
1467 for i in xrange(len(m)-1, -1, -1):
1467 for i in xrange(len(m)-1, -1, -1):
1468 if not matchfn(m[i]):
1468 if not matchfn(m[i]):
1469 mm.append(m[i])
1469 mm.append(m[i])
1470 del m[i]
1470 del m[i]
1471 for f in m:
1471 for f in m:
1472 repo.dirstate.normal(f)
1472 repo.dirstate.normal(f)
1473 for f in mm:
1473 for f in mm:
1474 repo.dirstate.normallookup(f)
1474 repo.dirstate.normallookup(f)
1475 for f in forget:
1475 for f in forget:
1476 repo.dirstate.forget(f)
1476 repo.dirstate.forget(f)
1477
1477
1478 if not msg:
1478 if not msg:
1479 if not ph.message:
1479 if not ph.message:
1480 message = "[mq]: %s\n" % patchfn
1480 message = "[mq]: %s\n" % patchfn
1481 else:
1481 else:
1482 message = "\n".join(ph.message)
1482 message = "\n".join(ph.message)
1483 else:
1483 else:
1484 message = msg
1484 message = msg
1485
1485
1486 user = ph.user or changes[1]
1486 user = ph.user or changes[1]
1487
1487
1488 # assumes strip can roll itself back if interrupted
1488 # assumes strip can roll itself back if interrupted
1489 repo.dirstate.setparents(*cparents)
1489 repo.dirstate.setparents(*cparents)
1490 self.applied.pop()
1490 self.applied.pop()
1491 self.applied_dirty = 1
1491 self.applied_dirty = 1
1492 self.strip(repo, [top], update=False,
1492 self.strip(repo, [top], update=False,
1493 backup='strip')
1493 backup='strip')
1494 except:
1494 except:
1495 repo.dirstate.invalidate()
1495 repo.dirstate.invalidate()
1496 raise
1496 raise
1497
1497
1498 try:
1498 try:
1499 # might be nice to attempt to roll back strip after this
1499 # might be nice to attempt to roll back strip after this
1500 n = repo.commit(message, user, ph.date, match=match,
1500 n = repo.commit(message, user, ph.date, match=match,
1501 force=True)
1501 force=True)
1502 # only write patch after a successful commit
1502 # only write patch after a successful commit
1503 patchf.rename()
1503 patchf.rename()
1504 self.applied.append(statusentry(n, patchfn))
1504 self.applied.append(statusentry(n, patchfn))
1505 except:
1505 except:
1506 ctx = repo[cparents[0]]
1506 ctx = repo[cparents[0]]
1507 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1507 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1508 self.save_dirty()
1508 self.save_dirty()
1509 self.ui.warn(_('refresh interrupted while patch was popped! '
1509 self.ui.warn(_('refresh interrupted while patch was popped! '
1510 '(revert --all, qpush to recover)\n'))
1510 '(revert --all, qpush to recover)\n'))
1511 raise
1511 raise
1512 finally:
1512 finally:
1513 wlock.release()
1513 wlock.release()
1514 self.removeundo(repo)
1514 self.removeundo(repo)
1515
1515
1516 def init(self, repo, create=False):
1516 def init(self, repo, create=False):
1517 if not create and os.path.isdir(self.path):
1517 if not create and os.path.isdir(self.path):
1518 raise util.Abort(_("patch queue directory already exists"))
1518 raise util.Abort(_("patch queue directory already exists"))
1519 try:
1519 try:
1520 os.mkdir(self.path)
1520 os.mkdir(self.path)
1521 except OSError, inst:
1521 except OSError, inst:
1522 if inst.errno != errno.EEXIST or not create:
1522 if inst.errno != errno.EEXIST or not create:
1523 raise
1523 raise
1524 if create:
1524 if create:
1525 return self.qrepo(create=True)
1525 return self.qrepo(create=True)
1526
1526
1527 def unapplied(self, repo, patch=None):
1527 def unapplied(self, repo, patch=None):
1528 if patch and patch not in self.series:
1528 if patch and patch not in self.series:
1529 raise util.Abort(_("patch %s is not in series file") % patch)
1529 raise util.Abort(_("patch %s is not in series file") % patch)
1530 if not patch:
1530 if not patch:
1531 start = self.series_end()
1531 start = self.series_end()
1532 else:
1532 else:
1533 start = self.series.index(patch) + 1
1533 start = self.series.index(patch) + 1
1534 unapplied = []
1534 unapplied = []
1535 for i in xrange(start, len(self.series)):
1535 for i in xrange(start, len(self.series)):
1536 pushable, reason = self.pushable(i)
1536 pushable, reason = self.pushable(i)
1537 if pushable:
1537 if pushable:
1538 unapplied.append((i, self.series[i]))
1538 unapplied.append((i, self.series[i]))
1539 self.explain_pushable(i)
1539 self.explain_pushable(i)
1540 return unapplied
1540 return unapplied
1541
1541
1542 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1542 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1543 summary=False):
1543 summary=False):
1544 def displayname(pfx, patchname, state):
1544 def displayname(pfx, patchname, state):
1545 if pfx:
1545 if pfx:
1546 self.ui.write(pfx)
1546 self.ui.write(pfx)
1547 if summary:
1547 if summary:
1548 ph = patchheader(self.join(patchname), self.plainmode)
1548 ph = patchheader(self.join(patchname), self.plainmode)
1549 msg = ph.message and ph.message[0] or ''
1549 msg = ph.message and ph.message[0] or ''
1550 if self.ui.formatted():
1550 if self.ui.formatted():
1551 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1551 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1552 if width > 0:
1552 if width > 0:
1553 msg = util.ellipsis(msg, width)
1553 msg = util.ellipsis(msg, width)
1554 else:
1554 else:
1555 msg = ''
1555 msg = ''
1556 self.ui.write(patchname, label='qseries.' + state)
1556 self.ui.write(patchname, label='qseries.' + state)
1557 self.ui.write(': ')
1557 self.ui.write(': ')
1558 self.ui.write(msg, label='qseries.message.' + state)
1558 self.ui.write(msg, label='qseries.message.' + state)
1559 else:
1559 else:
1560 self.ui.write(patchname, label='qseries.' + state)
1560 self.ui.write(patchname, label='qseries.' + state)
1561 self.ui.write('\n')
1561 self.ui.write('\n')
1562
1562
1563 applied = set([p.name for p in self.applied])
1563 applied = set([p.name for p in self.applied])
1564 if length is None:
1564 if length is None:
1565 length = len(self.series) - start
1565 length = len(self.series) - start
1566 if not missing:
1566 if not missing:
1567 if self.ui.verbose:
1567 if self.ui.verbose:
1568 idxwidth = len(str(start + length - 1))
1568 idxwidth = len(str(start + length - 1))
1569 for i in xrange(start, start + length):
1569 for i in xrange(start, start + length):
1570 patch = self.series[i]
1570 patch = self.series[i]
1571 if patch in applied:
1571 if patch in applied:
1572 char, state = 'A', 'applied'
1572 char, state = 'A', 'applied'
1573 elif self.pushable(i)[0]:
1573 elif self.pushable(i)[0]:
1574 char, state = 'U', 'unapplied'
1574 char, state = 'U', 'unapplied'
1575 else:
1575 else:
1576 char, state = 'G', 'guarded'
1576 char, state = 'G', 'guarded'
1577 pfx = ''
1577 pfx = ''
1578 if self.ui.verbose:
1578 if self.ui.verbose:
1579 pfx = '%*d %s ' % (idxwidth, i, char)
1579 pfx = '%*d %s ' % (idxwidth, i, char)
1580 elif status and status != char:
1580 elif status and status != char:
1581 continue
1581 continue
1582 displayname(pfx, patch, state)
1582 displayname(pfx, patch, state)
1583 else:
1583 else:
1584 msng_list = []
1584 msng_list = []
1585 for root, dirs, files in os.walk(self.path):
1585 for root, dirs, files in os.walk(self.path):
1586 d = root[len(self.path) + 1:]
1586 d = root[len(self.path) + 1:]
1587 for f in files:
1587 for f in files:
1588 fl = os.path.join(d, f)
1588 fl = os.path.join(d, f)
1589 if (fl not in self.series and
1589 if (fl not in self.series and
1590 fl not in (self.status_path, self.series_path,
1590 fl not in (self.status_path, self.series_path,
1591 self.guards_path)
1591 self.guards_path)
1592 and not fl.startswith('.')):
1592 and not fl.startswith('.')):
1593 msng_list.append(fl)
1593 msng_list.append(fl)
1594 for x in sorted(msng_list):
1594 for x in sorted(msng_list):
1595 pfx = self.ui.verbose and ('D ') or ''
1595 pfx = self.ui.verbose and ('D ') or ''
1596 displayname(pfx, x, 'missing')
1596 displayname(pfx, x, 'missing')
1597
1597
1598 def issaveline(self, l):
1598 def issaveline(self, l):
1599 if l.name == '.hg.patches.save.line':
1599 if l.name == '.hg.patches.save.line':
1600 return True
1600 return True
1601
1601
1602 def qrepo(self, create=False):
1602 def qrepo(self, create=False):
1603 ui = self.ui.copy()
1603 ui = self.ui.copy()
1604 ui.setconfig('paths', 'default', '', overlay=False)
1604 ui.setconfig('paths', 'default', '', overlay=False)
1605 ui.setconfig('paths', 'default-push', '', overlay=False)
1605 ui.setconfig('paths', 'default-push', '', overlay=False)
1606 if create or os.path.isdir(self.join(".hg")):
1606 if create or os.path.isdir(self.join(".hg")):
1607 return hg.repository(ui, path=self.path, create=create)
1607 return hg.repository(ui, path=self.path, create=create)
1608
1608
1609 def restore(self, repo, rev, delete=None, qupdate=None):
1609 def restore(self, repo, rev, delete=None, qupdate=None):
1610 desc = repo[rev].description().strip()
1610 desc = repo[rev].description().strip()
1611 lines = desc.splitlines()
1611 lines = desc.splitlines()
1612 i = 0
1612 i = 0
1613 datastart = None
1613 datastart = None
1614 series = []
1614 series = []
1615 applied = []
1615 applied = []
1616 qpp = None
1616 qpp = None
1617 for i, line in enumerate(lines):
1617 for i, line in enumerate(lines):
1618 if line == 'Patch Data:':
1618 if line == 'Patch Data:':
1619 datastart = i + 1
1619 datastart = i + 1
1620 elif line.startswith('Dirstate:'):
1620 elif line.startswith('Dirstate:'):
1621 l = line.rstrip()
1621 l = line.rstrip()
1622 l = l[10:].split(' ')
1622 l = l[10:].split(' ')
1623 qpp = [bin(x) for x in l]
1623 qpp = [bin(x) for x in l]
1624 elif datastart is not None:
1624 elif datastart is not None:
1625 l = line.rstrip()
1625 l = line.rstrip()
1626 n, name = l.split(':', 1)
1626 n, name = l.split(':', 1)
1627 if n:
1627 if n:
1628 applied.append(statusentry(bin(n), name))
1628 applied.append(statusentry(bin(n), name))
1629 else:
1629 else:
1630 series.append(l)
1630 series.append(l)
1631 if datastart is None:
1631 if datastart is None:
1632 self.ui.warn(_("No saved patch data found\n"))
1632 self.ui.warn(_("No saved patch data found\n"))
1633 return 1
1633 return 1
1634 self.ui.warn(_("restoring status: %s\n") % lines[0])
1634 self.ui.warn(_("restoring status: %s\n") % lines[0])
1635 self.full_series = series
1635 self.full_series = series
1636 self.applied = applied
1636 self.applied = applied
1637 self.parse_series()
1637 self.parse_series()
1638 self.series_dirty = 1
1638 self.series_dirty = 1
1639 self.applied_dirty = 1
1639 self.applied_dirty = 1
1640 heads = repo.changelog.heads()
1640 heads = repo.changelog.heads()
1641 if delete:
1641 if delete:
1642 if rev not in heads:
1642 if rev not in heads:
1643 self.ui.warn(_("save entry has children, leaving it alone\n"))
1643 self.ui.warn(_("save entry has children, leaving it alone\n"))
1644 else:
1644 else:
1645 self.ui.warn(_("removing save entry %s\n") % short(rev))
1645 self.ui.warn(_("removing save entry %s\n") % short(rev))
1646 pp = repo.dirstate.parents()
1646 pp = repo.dirstate.parents()
1647 if rev in pp:
1647 if rev in pp:
1648 update = True
1648 update = True
1649 else:
1649 else:
1650 update = False
1650 update = False
1651 self.strip(repo, [rev], update=update, backup='strip')
1651 self.strip(repo, [rev], update=update, backup='strip')
1652 if qpp:
1652 if qpp:
1653 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1653 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1654 (short(qpp[0]), short(qpp[1])))
1654 (short(qpp[0]), short(qpp[1])))
1655 if qupdate:
1655 if qupdate:
1656 self.ui.status(_("updating queue directory\n"))
1656 self.ui.status(_("updating queue directory\n"))
1657 r = self.qrepo()
1657 r = self.qrepo()
1658 if not r:
1658 if not r:
1659 self.ui.warn(_("Unable to load queue repository\n"))
1659 self.ui.warn(_("Unable to load queue repository\n"))
1660 return 1
1660 return 1
1661 hg.clean(r, qpp[0])
1661 hg.clean(r, qpp[0])
1662
1662
1663 def save(self, repo, msg=None):
1663 def save(self, repo, msg=None):
1664 if not self.applied:
1664 if not self.applied:
1665 self.ui.warn(_("save: no patches applied, exiting\n"))
1665 self.ui.warn(_("save: no patches applied, exiting\n"))
1666 return 1
1666 return 1
1667 if self.issaveline(self.applied[-1]):
1667 if self.issaveline(self.applied[-1]):
1668 self.ui.warn(_("status is already saved\n"))
1668 self.ui.warn(_("status is already saved\n"))
1669 return 1
1669 return 1
1670
1670
1671 if not msg:
1671 if not msg:
1672 msg = _("hg patches saved state")
1672 msg = _("hg patches saved state")
1673 else:
1673 else:
1674 msg = "hg patches: " + msg.rstrip('\r\n')
1674 msg = "hg patches: " + msg.rstrip('\r\n')
1675 r = self.qrepo()
1675 r = self.qrepo()
1676 if r:
1676 if r:
1677 pp = r.dirstate.parents()
1677 pp = r.dirstate.parents()
1678 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1678 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1679 msg += "\n\nPatch Data:\n"
1679 msg += "\n\nPatch Data:\n"
1680 msg += ''.join('%s\n' % x for x in self.applied)
1680 msg += ''.join('%s\n' % x for x in self.applied)
1681 msg += ''.join(':%s\n' % x for x in self.full_series)
1681 msg += ''.join(':%s\n' % x for x in self.full_series)
1682 n = repo.commit(msg, force=True)
1682 n = repo.commit(msg, force=True)
1683 if not n:
1683 if not n:
1684 self.ui.warn(_("repo commit failed\n"))
1684 self.ui.warn(_("repo commit failed\n"))
1685 return 1
1685 return 1
1686 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1686 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1687 self.applied_dirty = 1
1687 self.applied_dirty = 1
1688 self.removeundo(repo)
1688 self.removeundo(repo)
1689
1689
1690 def full_series_end(self):
1690 def full_series_end(self):
1691 if self.applied:
1691 if self.applied:
1692 p = self.applied[-1].name
1692 p = self.applied[-1].name
1693 end = self.find_series(p)
1693 end = self.find_series(p)
1694 if end is None:
1694 if end is None:
1695 return len(self.full_series)
1695 return len(self.full_series)
1696 return end + 1
1696 return end + 1
1697 return 0
1697 return 0
1698
1698
1699 def series_end(self, all_patches=False):
1699 def series_end(self, all_patches=False):
1700 """If all_patches is False, return the index of the next pushable patch
1700 """If all_patches is False, return the index of the next pushable patch
1701 in the series, or the series length. If all_patches is True, return the
1701 in the series, or the series length. If all_patches is True, return the
1702 index of the first patch past the last applied one.
1702 index of the first patch past the last applied one.
1703 """
1703 """
1704 end = 0
1704 end = 0
1705 def next(start):
1705 def next(start):
1706 if all_patches or start >= len(self.series):
1706 if all_patches or start >= len(self.series):
1707 return start
1707 return start
1708 for i in xrange(start, len(self.series)):
1708 for i in xrange(start, len(self.series)):
1709 p, reason = self.pushable(i)
1709 p, reason = self.pushable(i)
1710 if p:
1710 if p:
1711 break
1711 break
1712 self.explain_pushable(i)
1712 self.explain_pushable(i)
1713 return i
1713 return i
1714 if self.applied:
1714 if self.applied:
1715 p = self.applied[-1].name
1715 p = self.applied[-1].name
1716 try:
1716 try:
1717 end = self.series.index(p)
1717 end = self.series.index(p)
1718 except ValueError:
1718 except ValueError:
1719 return 0
1719 return 0
1720 return next(end + 1)
1720 return next(end + 1)
1721 return next(end)
1721 return next(end)
1722
1722
1723 def appliedname(self, index):
1723 def appliedname(self, index):
1724 pname = self.applied[index].name
1724 pname = self.applied[index].name
1725 if not self.ui.verbose:
1725 if not self.ui.verbose:
1726 p = pname
1726 p = pname
1727 else:
1727 else:
1728 p = str(self.series.index(pname)) + " " + pname
1728 p = str(self.series.index(pname)) + " " + pname
1729 return p
1729 return p
1730
1730
1731 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1731 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1732 force=None, git=False):
1732 force=None, git=False):
1733 def checkseries(patchname):
1733 def checkseries(patchname):
1734 if patchname in self.series:
1734 if patchname in self.series:
1735 raise util.Abort(_('patch %s is already in the series file')
1735 raise util.Abort(_('patch %s is already in the series file')
1736 % patchname)
1736 % patchname)
1737 def checkfile(patchname):
1737 def checkfile(patchname):
1738 if not force and os.path.exists(self.join(patchname)):
1738 if not force and os.path.exists(self.join(patchname)):
1739 raise util.Abort(_('patch "%s" already exists')
1739 raise util.Abort(_('patch "%s" already exists')
1740 % patchname)
1740 % patchname)
1741
1741
1742 if rev:
1742 if rev:
1743 if files:
1743 if files:
1744 raise util.Abort(_('option "-r" not valid when importing '
1744 raise util.Abort(_('option "-r" not valid when importing '
1745 'files'))
1745 'files'))
1746 rev = cmdutil.revrange(repo, rev)
1746 rev = cmdutil.revrange(repo, rev)
1747 rev.sort(reverse=True)
1747 rev.sort(reverse=True)
1748 if (len(files) > 1 or len(rev) > 1) and patchname:
1748 if (len(files) > 1 or len(rev) > 1) and patchname:
1749 raise util.Abort(_('option "-n" not valid when importing multiple '
1749 raise util.Abort(_('option "-n" not valid when importing multiple '
1750 'patches'))
1750 'patches'))
1751 if rev:
1751 if rev:
1752 # If mq patches are applied, we can only import revisions
1752 # If mq patches are applied, we can only import revisions
1753 # that form a linear path to qbase.
1753 # that form a linear path to qbase.
1754 # Otherwise, they should form a linear path to a head.
1754 # Otherwise, they should form a linear path to a head.
1755 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1755 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1756 if len(heads) > 1:
1756 if len(heads) > 1:
1757 raise util.Abort(_('revision %d is the root of more than one '
1757 raise util.Abort(_('revision %d is the root of more than one '
1758 'branch') % rev[-1])
1758 'branch') % rev[-1])
1759 if self.applied:
1759 if self.applied:
1760 base = repo.changelog.node(rev[0])
1760 base = repo.changelog.node(rev[0])
1761 if base in [n.node for n in self.applied]:
1761 if base in [n.node for n in self.applied]:
1762 raise util.Abort(_('revision %d is already managed')
1762 raise util.Abort(_('revision %d is already managed')
1763 % rev[0])
1763 % rev[0])
1764 if heads != [self.applied[-1].node]:
1764 if heads != [self.applied[-1].node]:
1765 raise util.Abort(_('revision %d is not the parent of '
1765 raise util.Abort(_('revision %d is not the parent of '
1766 'the queue') % rev[0])
1766 'the queue') % rev[0])
1767 base = repo.changelog.rev(self.applied[0].node)
1767 base = repo.changelog.rev(self.applied[0].node)
1768 lastparent = repo.changelog.parentrevs(base)[0]
1768 lastparent = repo.changelog.parentrevs(base)[0]
1769 else:
1769 else:
1770 if heads != [repo.changelog.node(rev[0])]:
1770 if heads != [repo.changelog.node(rev[0])]:
1771 raise util.Abort(_('revision %d has unmanaged children')
1771 raise util.Abort(_('revision %d has unmanaged children')
1772 % rev[0])
1772 % rev[0])
1773 lastparent = None
1773 lastparent = None
1774
1774
1775 diffopts = self.diffopts({'git': git})
1775 diffopts = self.diffopts({'git': git})
1776 for r in rev:
1776 for r in rev:
1777 p1, p2 = repo.changelog.parentrevs(r)
1777 p1, p2 = repo.changelog.parentrevs(r)
1778 n = repo.changelog.node(r)
1778 n = repo.changelog.node(r)
1779 if p2 != nullrev:
1779 if p2 != nullrev:
1780 raise util.Abort(_('cannot import merge revision %d') % r)
1780 raise util.Abort(_('cannot import merge revision %d') % r)
1781 if lastparent and lastparent != r:
1781 if lastparent and lastparent != r:
1782 raise util.Abort(_('revision %d is not the parent of %d')
1782 raise util.Abort(_('revision %d is not the parent of %d')
1783 % (r, lastparent))
1783 % (r, lastparent))
1784 lastparent = p1
1784 lastparent = p1
1785
1785
1786 if not patchname:
1786 if not patchname:
1787 patchname = normname('%d.diff' % r)
1787 patchname = normname('%d.diff' % r)
1788 self.check_reserved_name(patchname)
1788 self.check_reserved_name(patchname)
1789 checkseries(patchname)
1789 checkseries(patchname)
1790 checkfile(patchname)
1790 checkfile(patchname)
1791 self.full_series.insert(0, patchname)
1791 self.full_series.insert(0, patchname)
1792
1792
1793 patchf = self.opener(patchname, "w")
1793 patchf = self.opener(patchname, "w")
1794 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1794 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1795 patchf.close()
1795 patchf.close()
1796
1796
1797 se = statusentry(n, patchname)
1797 se = statusentry(n, patchname)
1798 self.applied.insert(0, se)
1798 self.applied.insert(0, se)
1799
1799
1800 self.added.append(patchname)
1800 self.added.append(patchname)
1801 patchname = None
1801 patchname = None
1802 self.parse_series()
1802 self.parse_series()
1803 self.applied_dirty = 1
1803 self.applied_dirty = 1
1804 self.series_dirty = True
1804 self.series_dirty = True
1805
1805
1806 for i, filename in enumerate(files):
1806 for i, filename in enumerate(files):
1807 if existing:
1807 if existing:
1808 if filename == '-':
1808 if filename == '-':
1809 raise util.Abort(_('-e is incompatible with import from -'))
1809 raise util.Abort(_('-e is incompatible with import from -'))
1810 filename = normname(filename)
1810 filename = normname(filename)
1811 self.check_reserved_name(filename)
1811 self.check_reserved_name(filename)
1812 originpath = self.join(filename)
1812 originpath = self.join(filename)
1813 if not os.path.isfile(originpath):
1813 if not os.path.isfile(originpath):
1814 raise util.Abort(_("patch %s does not exist") % filename)
1814 raise util.Abort(_("patch %s does not exist") % filename)
1815
1815
1816 if patchname:
1816 if patchname:
1817 self.check_reserved_name(patchname)
1817 self.check_reserved_name(patchname)
1818 checkfile(patchname)
1818 checkfile(patchname)
1819
1819
1820 self.ui.write(_('renaming %s to %s\n')
1820 self.ui.write(_('renaming %s to %s\n')
1821 % (filename, patchname))
1821 % (filename, patchname))
1822 util.rename(originpath, self.join(patchname))
1822 util.rename(originpath, self.join(patchname))
1823 else:
1823 else:
1824 patchname = filename
1824 patchname = filename
1825
1825
1826 else:
1826 else:
1827 try:
1827 try:
1828 if filename == '-':
1828 if filename == '-':
1829 if not patchname:
1829 if not patchname:
1830 raise util.Abort(
1830 raise util.Abort(
1831 _('need --name to import a patch from -'))
1831 _('need --name to import a patch from -'))
1832 text = sys.stdin.read()
1832 text = sys.stdin.read()
1833 else:
1833 else:
1834 fp = url.open(self.ui, filename)
1834 fp = url.open(self.ui, filename)
1835 text = fp.read()
1835 text = fp.read()
1836 fp.close()
1836 fp.close()
1837 except (OSError, IOError):
1837 except (OSError, IOError):
1838 raise util.Abort(_("unable to read file %s") % filename)
1838 raise util.Abort(_("unable to read file %s") % filename)
1839 if not patchname:
1839 if not patchname:
1840 patchname = normname(os.path.basename(filename))
1840 patchname = normname(os.path.basename(filename))
1841 self.check_reserved_name(patchname)
1841 self.check_reserved_name(patchname)
1842 checkfile(patchname)
1842 checkfile(patchname)
1843 patchf = self.opener(patchname, "w")
1843 patchf = self.opener(patchname, "w")
1844 patchf.write(text)
1844 patchf.write(text)
1845 patchf.close()
1845 patchf.close()
1846 if not force:
1846 if not force:
1847 checkseries(patchname)
1847 checkseries(patchname)
1848 if patchname not in self.series:
1848 if patchname not in self.series:
1849 index = self.full_series_end() + i
1849 index = self.full_series_end() + i
1850 self.full_series[index:index] = [patchname]
1850 self.full_series[index:index] = [patchname]
1851 self.parse_series()
1851 self.parse_series()
1852 self.series_dirty = True
1852 self.series_dirty = True
1853 self.ui.warn(_("adding %s to series file\n") % patchname)
1853 self.ui.warn(_("adding %s to series file\n") % patchname)
1854 self.added.append(patchname)
1854 self.added.append(patchname)
1855 patchname = None
1855 patchname = None
1856
1856
1857 self.removeundo(repo)
1857 self.removeundo(repo)
1858
1858
1859 def delete(ui, repo, *patches, **opts):
1859 def delete(ui, repo, *patches, **opts):
1860 """remove patches from queue
1860 """remove patches from queue
1861
1861
1862 The patches must not be applied, and at least one patch is required. With
1862 The patches must not be applied, and at least one patch is required. With
1863 -k/--keep, the patch files are preserved in the patch directory.
1863 -k/--keep, the patch files are preserved in the patch directory.
1864
1864
1865 To stop managing a patch and move it into permanent history,
1865 To stop managing a patch and move it into permanent history,
1866 use the :hg:`qfinish` command."""
1866 use the :hg:`qfinish` command."""
1867 q = repo.mq
1867 q = repo.mq
1868 q.delete(repo, patches, opts)
1868 q.delete(repo, patches, opts)
1869 q.save_dirty()
1869 q.save_dirty()
1870 return 0
1870 return 0
1871
1871
1872 def applied(ui, repo, patch=None, **opts):
1872 def applied(ui, repo, patch=None, **opts):
1873 """print the patches already applied
1873 """print the patches already applied
1874
1874
1875 Returns 0 on success."""
1875 Returns 0 on success."""
1876
1876
1877 q = repo.mq
1877 q = repo.mq
1878
1878
1879 if patch:
1879 if patch:
1880 if patch not in q.series:
1880 if patch not in q.series:
1881 raise util.Abort(_("patch %s is not in series file") % patch)
1881 raise util.Abort(_("patch %s is not in series file") % patch)
1882 end = q.series.index(patch) + 1
1882 end = q.series.index(patch) + 1
1883 else:
1883 else:
1884 end = q.series_end(True)
1884 end = q.series_end(True)
1885
1885
1886 if opts.get('last') and not end:
1886 if opts.get('last') and not end:
1887 ui.write(_("no patches applied\n"))
1887 ui.write(_("no patches applied\n"))
1888 return 1
1888 return 1
1889 elif opts.get('last') and end == 1:
1889 elif opts.get('last') and end == 1:
1890 ui.write(_("only one patch applied\n"))
1890 ui.write(_("only one patch applied\n"))
1891 return 1
1891 return 1
1892 elif opts.get('last'):
1892 elif opts.get('last'):
1893 start = end - 2
1893 start = end - 2
1894 end = 1
1894 end = 1
1895 else:
1895 else:
1896 start = 0
1896 start = 0
1897
1897
1898 q.qseries(repo, length=end, start=start, status='A',
1898 q.qseries(repo, length=end, start=start, status='A',
1899 summary=opts.get('summary'))
1899 summary=opts.get('summary'))
1900
1900
1901
1901
1902 def unapplied(ui, repo, patch=None, **opts):
1902 def unapplied(ui, repo, patch=None, **opts):
1903 """print the patches not yet applied
1903 """print the patches not yet applied
1904
1904
1905 Returns 0 on success."""
1905 Returns 0 on success."""
1906
1906
1907 q = repo.mq
1907 q = repo.mq
1908 if patch:
1908 if patch:
1909 if patch not in q.series:
1909 if patch not in q.series:
1910 raise util.Abort(_("patch %s is not in series file") % patch)
1910 raise util.Abort(_("patch %s is not in series file") % patch)
1911 start = q.series.index(patch) + 1
1911 start = q.series.index(patch) + 1
1912 else:
1912 else:
1913 start = q.series_end(True)
1913 start = q.series_end(True)
1914
1914
1915 if start == len(q.series) and opts.get('first'):
1915 if start == len(q.series) and opts.get('first'):
1916 ui.write(_("all patches applied\n"))
1916 ui.write(_("all patches applied\n"))
1917 return 1
1917 return 1
1918
1918
1919 length = opts.get('first') and 1 or None
1919 length = opts.get('first') and 1 or None
1920 q.qseries(repo, start=start, length=length, status='U',
1920 q.qseries(repo, start=start, length=length, status='U',
1921 summary=opts.get('summary'))
1921 summary=opts.get('summary'))
1922
1922
1923 def qimport(ui, repo, *filename, **opts):
1923 def qimport(ui, repo, *filename, **opts):
1924 """import a patch
1924 """import a patch
1925
1925
1926 The patch is inserted into the series after the last applied
1926 The patch is inserted into the series after the last applied
1927 patch. If no patches have been applied, qimport prepends the patch
1927 patch. If no patches have been applied, qimport prepends the patch
1928 to the series.
1928 to the series.
1929
1929
1930 The patch will have the same name as its source file unless you
1930 The patch will have the same name as its source file unless you
1931 give it a new one with -n/--name.
1931 give it a new one with -n/--name.
1932
1932
1933 You can register an existing patch inside the patch directory with
1933 You can register an existing patch inside the patch directory with
1934 the -e/--existing flag.
1934 the -e/--existing flag.
1935
1935
1936 With -f/--force, an existing patch of the same name will be
1936 With -f/--force, an existing patch of the same name will be
1937 overwritten.
1937 overwritten.
1938
1938
1939 An existing changeset may be placed under mq control with -r/--rev
1939 An existing changeset may be placed under mq control with -r/--rev
1940 (e.g. qimport --rev tip -n patch will place tip under mq control).
1940 (e.g. qimport --rev tip -n patch will place tip under mq control).
1941 With -g/--git, patches imported with --rev will use the git diff
1941 With -g/--git, patches imported with --rev will use the git diff
1942 format. See the diffs help topic for information on why this is
1942 format. See the diffs help topic for information on why this is
1943 important for preserving rename/copy information and permission
1943 important for preserving rename/copy information and permission
1944 changes. Use :hg:`qfinish` to remove changesets from mq control.
1944 changes. Use :hg:`qfinish` to remove changesets from mq control.
1945
1945
1946 To import a patch from standard input, pass - as the patch file.
1946 To import a patch from standard input, pass - as the patch file.
1947 When importing from standard input, a patch name must be specified
1947 When importing from standard input, a patch name must be specified
1948 using the --name flag.
1948 using the --name flag.
1949
1949
1950 To import an existing patch while renaming it::
1950 To import an existing patch while renaming it::
1951
1951
1952 hg qimport -e existing-patch -n new-name
1952 hg qimport -e existing-patch -n new-name
1953
1953
1954 Returns 0 if import succeeded.
1954 Returns 0 if import succeeded.
1955 """
1955 """
1956 q = repo.mq
1956 q = repo.mq
1957 try:
1957 try:
1958 q.qimport(repo, filename, patchname=opts.get('name'),
1958 q.qimport(repo, filename, patchname=opts.get('name'),
1959 existing=opts.get('existing'), force=opts.get('force'),
1959 existing=opts.get('existing'), force=opts.get('force'),
1960 rev=opts.get('rev'), git=opts.get('git'))
1960 rev=opts.get('rev'), git=opts.get('git'))
1961 finally:
1961 finally:
1962 q.save_dirty()
1962 q.save_dirty()
1963
1963
1964 if opts.get('push') and not opts.get('rev'):
1964 if opts.get('push') and not opts.get('rev'):
1965 return q.push(repo, None)
1965 return q.push(repo, None)
1966 return 0
1966 return 0
1967
1967
1968 def qinit(ui, repo, create):
1968 def qinit(ui, repo, create):
1969 """initialize a new queue repository
1969 """initialize a new queue repository
1970
1970
1971 This command also creates a series file for ordering patches, and
1971 This command also creates a series file for ordering patches, and
1972 an mq-specific .hgignore file in the queue repository, to exclude
1972 an mq-specific .hgignore file in the queue repository, to exclude
1973 the status and guards files (these contain mostly transient state).
1973 the status and guards files (these contain mostly transient state).
1974
1974
1975 Returns 0 if initialization succeeded."""
1975 Returns 0 if initialization succeeded."""
1976 q = repo.mq
1976 q = repo.mq
1977 r = q.init(repo, create)
1977 r = q.init(repo, create)
1978 q.save_dirty()
1978 q.save_dirty()
1979 if r:
1979 if r:
1980 if not os.path.exists(r.wjoin('.hgignore')):
1980 if not os.path.exists(r.wjoin('.hgignore')):
1981 fp = r.wopener('.hgignore', 'w')
1981 fp = r.wopener('.hgignore', 'w')
1982 fp.write('^\\.hg\n')
1982 fp.write('^\\.hg\n')
1983 fp.write('^\\.mq\n')
1983 fp.write('^\\.mq\n')
1984 fp.write('syntax: glob\n')
1984 fp.write('syntax: glob\n')
1985 fp.write('status\n')
1985 fp.write('status\n')
1986 fp.write('guards\n')
1986 fp.write('guards\n')
1987 fp.close()
1987 fp.close()
1988 if not os.path.exists(r.wjoin('series')):
1988 if not os.path.exists(r.wjoin('series')):
1989 r.wopener('series', 'w').close()
1989 r.wopener('series', 'w').close()
1990 r[None].add(['.hgignore', 'series'])
1990 r[None].add(['.hgignore', 'series'])
1991 commands.add(ui, r)
1991 commands.add(ui, r)
1992 return 0
1992 return 0
1993
1993
1994 def init(ui, repo, **opts):
1994 def init(ui, repo, **opts):
1995 """init a new queue repository (DEPRECATED)
1995 """init a new queue repository (DEPRECATED)
1996
1996
1997 The queue repository is unversioned by default. If
1997 The queue repository is unversioned by default. If
1998 -c/--create-repo is specified, qinit will create a separate nested
1998 -c/--create-repo is specified, qinit will create a separate nested
1999 repository for patches (qinit -c may also be run later to convert
1999 repository for patches (qinit -c may also be run later to convert
2000 an unversioned patch repository into a versioned one). You can use
2000 an unversioned patch repository into a versioned one). You can use
2001 qcommit to commit changes to this queue repository.
2001 qcommit to commit changes to this queue repository.
2002
2002
2003 This command is deprecated. Without -c, it's implied by other relevant
2003 This command is deprecated. Without -c, it's implied by other relevant
2004 commands. With -c, use :hg:`init --mq` instead."""
2004 commands. With -c, use :hg:`init --mq` instead."""
2005 return qinit(ui, repo, create=opts.get('create_repo'))
2005 return qinit(ui, repo, create=opts.get('create_repo'))
2006
2006
2007 def clone(ui, source, dest=None, **opts):
2007 def clone(ui, source, dest=None, **opts):
2008 '''clone main and patch repository at same time
2008 '''clone main and patch repository at same time
2009
2009
2010 If source is local, destination will have no patches applied. If
2010 If source is local, destination will have no patches applied. If
2011 source is remote, this command can not check if patches are
2011 source is remote, this command can not check if patches are
2012 applied in source, so cannot guarantee that patches are not
2012 applied in source, so cannot guarantee that patches are not
2013 applied in destination. If you clone remote repository, be sure
2013 applied in destination. If you clone remote repository, be sure
2014 before that it has no patches applied.
2014 before that it has no patches applied.
2015
2015
2016 Source patch repository is looked for in <src>/.hg/patches by
2016 Source patch repository is looked for in <src>/.hg/patches by
2017 default. Use -p <url> to change.
2017 default. Use -p <url> to change.
2018
2018
2019 The patch directory must be a nested Mercurial repository, as
2019 The patch directory must be a nested Mercurial repository, as
2020 would be created by :hg:`init --mq`.
2020 would be created by :hg:`init --mq`.
2021
2021
2022 Return 0 on success.
2022 Return 0 on success.
2023 '''
2023 '''
2024 def patchdir(repo):
2024 def patchdir(repo):
2025 url = repo.url()
2025 url = repo.url()
2026 if url.endswith('/'):
2026 if url.endswith('/'):
2027 url = url[:-1]
2027 url = url[:-1]
2028 return url + '/.hg/patches'
2028 return url + '/.hg/patches'
2029 if dest is None:
2029 if dest is None:
2030 dest = hg.defaultdest(source)
2030 dest = hg.defaultdest(source)
2031 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
2031 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
2032 if opts.get('patches'):
2032 if opts.get('patches'):
2033 patchespath = ui.expandpath(opts.get('patches'))
2033 patchespath = ui.expandpath(opts.get('patches'))
2034 else:
2034 else:
2035 patchespath = patchdir(sr)
2035 patchespath = patchdir(sr)
2036 try:
2036 try:
2037 hg.repository(ui, patchespath)
2037 hg.repository(ui, patchespath)
2038 except error.RepoError:
2038 except error.RepoError:
2039 raise util.Abort(_('versioned patch repository not found'
2039 raise util.Abort(_('versioned patch repository not found'
2040 ' (see init --mq)'))
2040 ' (see init --mq)'))
2041 qbase, destrev = None, None
2041 qbase, destrev = None, None
2042 if sr.local():
2042 if sr.local():
2043 if sr.mq.applied:
2043 if sr.mq.applied:
2044 qbase = sr.mq.applied[0].node
2044 qbase = sr.mq.applied[0].node
2045 if not hg.islocal(dest):
2045 if not hg.islocal(dest):
2046 heads = set(sr.heads())
2046 heads = set(sr.heads())
2047 destrev = list(heads.difference(sr.heads(qbase)))
2047 destrev = list(heads.difference(sr.heads(qbase)))
2048 destrev.append(sr.changelog.parents(qbase)[0])
2048 destrev.append(sr.changelog.parents(qbase)[0])
2049 elif sr.capable('lookup'):
2049 elif sr.capable('lookup'):
2050 try:
2050 try:
2051 qbase = sr.lookup('qbase')
2051 qbase = sr.lookup('qbase')
2052 except error.RepoError:
2052 except error.RepoError:
2053 pass
2053 pass
2054 ui.note(_('cloning main repository\n'))
2054 ui.note(_('cloning main repository\n'))
2055 sr, dr = hg.clone(ui, sr.url(), dest,
2055 sr, dr = hg.clone(ui, sr.url(), dest,
2056 pull=opts.get('pull'),
2056 pull=opts.get('pull'),
2057 rev=destrev,
2057 rev=destrev,
2058 update=False,
2058 update=False,
2059 stream=opts.get('uncompressed'))
2059 stream=opts.get('uncompressed'))
2060 ui.note(_('cloning patch repository\n'))
2060 ui.note(_('cloning patch repository\n'))
2061 hg.clone(ui, opts.get('patches') or patchdir(sr), patchdir(dr),
2061 hg.clone(ui, opts.get('patches') or patchdir(sr), patchdir(dr),
2062 pull=opts.get('pull'), update=not opts.get('noupdate'),
2062 pull=opts.get('pull'), update=not opts.get('noupdate'),
2063 stream=opts.get('uncompressed'))
2063 stream=opts.get('uncompressed'))
2064 if dr.local():
2064 if dr.local():
2065 if qbase:
2065 if qbase:
2066 ui.note(_('stripping applied patches from destination '
2066 ui.note(_('stripping applied patches from destination '
2067 'repository\n'))
2067 'repository\n'))
2068 dr.mq.strip(dr, [qbase], update=False, backup=None)
2068 dr.mq.strip(dr, [qbase], update=False, backup=None)
2069 if not opts.get('noupdate'):
2069 if not opts.get('noupdate'):
2070 ui.note(_('updating destination repository\n'))
2070 ui.note(_('updating destination repository\n'))
2071 hg.update(dr, dr.changelog.tip())
2071 hg.update(dr, dr.changelog.tip())
2072
2072
2073 def commit(ui, repo, *pats, **opts):
2073 def commit(ui, repo, *pats, **opts):
2074 """commit changes in the queue repository (DEPRECATED)
2074 """commit changes in the queue repository (DEPRECATED)
2075
2075
2076 This command is deprecated; use :hg:`commit --mq` instead."""
2076 This command is deprecated; use :hg:`commit --mq` instead."""
2077 q = repo.mq
2077 q = repo.mq
2078 r = q.qrepo()
2078 r = q.qrepo()
2079 if not r:
2079 if not r:
2080 raise util.Abort('no queue repository')
2080 raise util.Abort('no queue repository')
2081 commands.commit(r.ui, r, *pats, **opts)
2081 commands.commit(r.ui, r, *pats, **opts)
2082
2082
2083 def series(ui, repo, **opts):
2083 def series(ui, repo, **opts):
2084 """print the entire series file
2084 """print the entire series file
2085
2085
2086 Returns 0 on success."""
2086 Returns 0 on success."""
2087 repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
2087 repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
2088 return 0
2088 return 0
2089
2089
2090 def top(ui, repo, **opts):
2090 def top(ui, repo, **opts):
2091 """print the name of the current patch
2091 """print the name of the current patch
2092
2092
2093 Returns 0 on success."""
2093 Returns 0 on success."""
2094 q = repo.mq
2094 q = repo.mq
2095 t = q.applied and q.series_end(True) or 0
2095 t = q.applied and q.series_end(True) or 0
2096 if t:
2096 if t:
2097 q.qseries(repo, start=t - 1, length=1, status='A',
2097 q.qseries(repo, start=t - 1, length=1, status='A',
2098 summary=opts.get('summary'))
2098 summary=opts.get('summary'))
2099 else:
2099 else:
2100 ui.write(_("no patches applied\n"))
2100 ui.write(_("no patches applied\n"))
2101 return 1
2101 return 1
2102
2102
2103 def next(ui, repo, **opts):
2103 def next(ui, repo, **opts):
2104 """print the name of the next patch
2104 """print the name of the next patch
2105
2105
2106 Returns 0 on success."""
2106 Returns 0 on success."""
2107 q = repo.mq
2107 q = repo.mq
2108 end = q.series_end()
2108 end = q.series_end()
2109 if end == len(q.series):
2109 if end == len(q.series):
2110 ui.write(_("all patches applied\n"))
2110 ui.write(_("all patches applied\n"))
2111 return 1
2111 return 1
2112 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2112 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2113
2113
2114 def prev(ui, repo, **opts):
2114 def prev(ui, repo, **opts):
2115 """print the name of the previous patch
2115 """print the name of the previous patch
2116
2116
2117 Returns 0 on success."""
2117 Returns 0 on success."""
2118 q = repo.mq
2118 q = repo.mq
2119 l = len(q.applied)
2119 l = len(q.applied)
2120 if l == 1:
2120 if l == 1:
2121 ui.write(_("only one patch applied\n"))
2121 ui.write(_("only one patch applied\n"))
2122 return 1
2122 return 1
2123 if not l:
2123 if not l:
2124 ui.write(_("no patches applied\n"))
2124 ui.write(_("no patches applied\n"))
2125 return 1
2125 return 1
2126 q.qseries(repo, start=l - 2, length=1, status='A',
2126 q.qseries(repo, start=l - 2, length=1, status='A',
2127 summary=opts.get('summary'))
2127 summary=opts.get('summary'))
2128
2128
2129 def setupheaderopts(ui, opts):
2129 def setupheaderopts(ui, opts):
2130 if not opts.get('user') and opts.get('currentuser'):
2130 if not opts.get('user') and opts.get('currentuser'):
2131 opts['user'] = ui.username()
2131 opts['user'] = ui.username()
2132 if not opts.get('date') and opts.get('currentdate'):
2132 if not opts.get('date') and opts.get('currentdate'):
2133 opts['date'] = "%d %d" % util.makedate()
2133 opts['date'] = "%d %d" % util.makedate()
2134
2134
2135 def new(ui, repo, patch, *args, **opts):
2135 def new(ui, repo, patch, *args, **opts):
2136 """create a new patch
2136 """create a new patch
2137
2137
2138 qnew creates a new patch on top of the currently-applied patch (if
2138 qnew creates a new patch on top of the currently-applied patch (if
2139 any). The patch will be initialized with any outstanding changes
2139 any). The patch will be initialized with any outstanding changes
2140 in the working directory. You may also use -I/--include,
2140 in the working directory. You may also use -I/--include,
2141 -X/--exclude, and/or a list of files after the patch name to add
2141 -X/--exclude, and/or a list of files after the patch name to add
2142 only changes to matching files to the new patch, leaving the rest
2142 only changes to matching files to the new patch, leaving the rest
2143 as uncommitted modifications.
2143 as uncommitted modifications.
2144
2144
2145 -u/--user and -d/--date can be used to set the (given) user and
2145 -u/--user and -d/--date can be used to set the (given) user and
2146 date, respectively. -U/--currentuser and -D/--currentdate set user
2146 date, respectively. -U/--currentuser and -D/--currentdate set user
2147 to current user and date to current date.
2147 to current user and date to current date.
2148
2148
2149 -e/--edit, -m/--message or -l/--logfile set the patch header as
2149 -e/--edit, -m/--message or -l/--logfile set the patch header as
2150 well as the commit message. If none is specified, the header is
2150 well as the commit message. If none is specified, the header is
2151 empty and the commit message is '[mq]: PATCH'.
2151 empty and the commit message is '[mq]: PATCH'.
2152
2152
2153 Use the -g/--git option to keep the patch in the git extended diff
2153 Use the -g/--git option to keep the patch in the git extended diff
2154 format. Read the diffs help topic for more information on why this
2154 format. Read the diffs help topic for more information on why this
2155 is important for preserving permission changes and copy/rename
2155 is important for preserving permission changes and copy/rename
2156 information.
2156 information.
2157
2157
2158 Returns 0 on successful creation of a new patch.
2158 Returns 0 on successful creation of a new patch.
2159 """
2159 """
2160 msg = cmdutil.logmessage(opts)
2160 msg = cmdutil.logmessage(opts)
2161 def getmsg():
2161 def getmsg():
2162 return ui.edit(msg, opts.get('user') or ui.username())
2162 return ui.edit(msg, opts.get('user') or ui.username())
2163 q = repo.mq
2163 q = repo.mq
2164 opts['msg'] = msg
2164 opts['msg'] = msg
2165 if opts.get('edit'):
2165 if opts.get('edit'):
2166 opts['msg'] = getmsg
2166 opts['msg'] = getmsg
2167 else:
2167 else:
2168 opts['msg'] = msg
2168 opts['msg'] = msg
2169 setupheaderopts(ui, opts)
2169 setupheaderopts(ui, opts)
2170 q.new(repo, patch, *args, **opts)
2170 q.new(repo, patch, *args, **opts)
2171 q.save_dirty()
2171 q.save_dirty()
2172 return 0
2172 return 0
2173
2173
2174 def refresh(ui, repo, *pats, **opts):
2174 def refresh(ui, repo, *pats, **opts):
2175 """update the current patch
2175 """update the current patch
2176
2176
2177 If any file patterns are provided, the refreshed patch will
2177 If any file patterns are provided, the refreshed patch will
2178 contain only the modifications that match those patterns; the
2178 contain only the modifications that match those patterns; the
2179 remaining modifications will remain in the working directory.
2179 remaining modifications will remain in the working directory.
2180
2180
2181 If -s/--short is specified, files currently included in the patch
2181 If -s/--short is specified, files currently included in the patch
2182 will be refreshed just like matched files and remain in the patch.
2182 will be refreshed just like matched files and remain in the patch.
2183
2183
2184 If -e/--edit is specified, Mercurial will start your configured editor for
2184 If -e/--edit is specified, Mercurial will start your configured editor for
2185 you to enter a message. In case qrefresh fails, you will find a backup of
2185 you to enter a message. In case qrefresh fails, you will find a backup of
2186 your message in ``.hg/last-message.txt``.
2186 your message in ``.hg/last-message.txt``.
2187
2187
2188 hg add/remove/copy/rename work as usual, though you might want to
2188 hg add/remove/copy/rename work as usual, though you might want to
2189 use git-style patches (-g/--git or [diff] git=1) to track copies
2189 use git-style patches (-g/--git or [diff] git=1) to track copies
2190 and renames. See the diffs help topic for more information on the
2190 and renames. See the diffs help topic for more information on the
2191 git diff format.
2191 git diff format.
2192
2192
2193 Returns 0 on success.
2193 Returns 0 on success.
2194 """
2194 """
2195 q = repo.mq
2195 q = repo.mq
2196 message = cmdutil.logmessage(opts)
2196 message = cmdutil.logmessage(opts)
2197 if opts.get('edit'):
2197 if opts.get('edit'):
2198 if not q.applied:
2198 if not q.applied:
2199 ui.write(_("no patches applied\n"))
2199 ui.write(_("no patches applied\n"))
2200 return 1
2200 return 1
2201 if message:
2201 if message:
2202 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2202 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2203 patch = q.applied[-1].name
2203 patch = q.applied[-1].name
2204 ph = patchheader(q.join(patch), q.plainmode)
2204 ph = patchheader(q.join(patch), q.plainmode)
2205 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2205 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2206 # We don't want to lose the patch message if qrefresh fails (issue2062)
2206 # We don't want to lose the patch message if qrefresh fails (issue2062)
2207 msgfile = repo.opener('last-message.txt', 'wb')
2207 msgfile = repo.opener('last-message.txt', 'wb')
2208 msgfile.write(message)
2208 msgfile.write(message)
2209 msgfile.close()
2209 msgfile.close()
2210 setupheaderopts(ui, opts)
2210 setupheaderopts(ui, opts)
2211 ret = q.refresh(repo, pats, msg=message, **opts)
2211 ret = q.refresh(repo, pats, msg=message, **opts)
2212 q.save_dirty()
2212 q.save_dirty()
2213 return ret
2213 return ret
2214
2214
2215 def diff(ui, repo, *pats, **opts):
2215 def diff(ui, repo, *pats, **opts):
2216 """diff of the current patch and subsequent modifications
2216 """diff of the current patch and subsequent modifications
2217
2217
2218 Shows a diff which includes the current patch as well as any
2218 Shows a diff which includes the current patch as well as any
2219 changes which have been made in the working directory since the
2219 changes which have been made in the working directory since the
2220 last refresh (thus showing what the current patch would become
2220 last refresh (thus showing what the current patch would become
2221 after a qrefresh).
2221 after a qrefresh).
2222
2222
2223 Use :hg:`diff` if you only want to see the changes made since the
2223 Use :hg:`diff` if you only want to see the changes made since the
2224 last qrefresh, or :hg:`export qtip` if you want to see changes
2224 last qrefresh, or :hg:`export qtip` if you want to see changes
2225 made by the current patch without including changes made since the
2225 made by the current patch without including changes made since the
2226 qrefresh.
2226 qrefresh.
2227
2227
2228 Returns 0 on success.
2228 Returns 0 on success.
2229 """
2229 """
2230 repo.mq.diff(repo, pats, opts)
2230 repo.mq.diff(repo, pats, opts)
2231 return 0
2231 return 0
2232
2232
2233 def fold(ui, repo, *files, **opts):
2233 def fold(ui, repo, *files, **opts):
2234 """fold the named patches into the current patch
2234 """fold the named patches into the current patch
2235
2235
2236 Patches must not yet be applied. Each patch will be successively
2236 Patches must not yet be applied. Each patch will be successively
2237 applied to the current patch in the order given. If all the
2237 applied to the current patch in the order given. If all the
2238 patches apply successfully, the current patch will be refreshed
2238 patches apply successfully, the current patch will be refreshed
2239 with the new cumulative patch, and the folded patches will be
2239 with the new cumulative patch, and the folded patches will be
2240 deleted. With -k/--keep, the folded patch files will not be
2240 deleted. With -k/--keep, the folded patch files will not be
2241 removed afterwards.
2241 removed afterwards.
2242
2242
2243 The header for each folded patch will be concatenated with the
2243 The header for each folded patch will be concatenated with the
2244 current patch header, separated by a line of ``* * *``.
2244 current patch header, separated by a line of ``* * *``.
2245
2245
2246 Returns 0 on success."""
2246 Returns 0 on success."""
2247
2247
2248 q = repo.mq
2248 q = repo.mq
2249
2249
2250 if not files:
2250 if not files:
2251 raise util.Abort(_('qfold requires at least one patch name'))
2251 raise util.Abort(_('qfold requires at least one patch name'))
2252 if not q.check_toppatch(repo)[0]:
2252 if not q.check_toppatch(repo)[0]:
2253 raise util.Abort(_('no patches applied'))
2253 raise util.Abort(_('no patches applied'))
2254 q.check_localchanges(repo)
2254 q.check_localchanges(repo)
2255
2255
2256 message = cmdutil.logmessage(opts)
2256 message = cmdutil.logmessage(opts)
2257 if opts.get('edit'):
2257 if opts.get('edit'):
2258 if message:
2258 if message:
2259 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2259 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2260
2260
2261 parent = q.lookup('qtip')
2261 parent = q.lookup('qtip')
2262 patches = []
2262 patches = []
2263 messages = []
2263 messages = []
2264 for f in files:
2264 for f in files:
2265 p = q.lookup(f)
2265 p = q.lookup(f)
2266 if p in patches or p == parent:
2266 if p in patches or p == parent:
2267 ui.warn(_('Skipping already folded patch %s\n') % p)
2267 ui.warn(_('Skipping already folded patch %s\n') % p)
2268 if q.isapplied(p):
2268 if q.isapplied(p):
2269 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2269 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2270 patches.append(p)
2270 patches.append(p)
2271
2271
2272 for p in patches:
2272 for p in patches:
2273 if not message:
2273 if not message:
2274 ph = patchheader(q.join(p), q.plainmode)
2274 ph = patchheader(q.join(p), q.plainmode)
2275 if ph.message:
2275 if ph.message:
2276 messages.append(ph.message)
2276 messages.append(ph.message)
2277 pf = q.join(p)
2277 pf = q.join(p)
2278 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2278 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2279 if not patchsuccess:
2279 if not patchsuccess:
2280 raise util.Abort(_('error folding patch %s') % p)
2280 raise util.Abort(_('error folding patch %s') % p)
2281
2281
2282 if not message:
2282 if not message:
2283 ph = patchheader(q.join(parent), q.plainmode)
2283 ph = patchheader(q.join(parent), q.plainmode)
2284 message, user = ph.message, ph.user
2284 message, user = ph.message, ph.user
2285 for msg in messages:
2285 for msg in messages:
2286 message.append('* * *')
2286 message.append('* * *')
2287 message.extend(msg)
2287 message.extend(msg)
2288 message = '\n'.join(message)
2288 message = '\n'.join(message)
2289
2289
2290 if opts.get('edit'):
2290 if opts.get('edit'):
2291 message = ui.edit(message, user or ui.username())
2291 message = ui.edit(message, user or ui.username())
2292
2292
2293 diffopts = q.patchopts(q.diffopts(), *patches)
2293 diffopts = q.patchopts(q.diffopts(), *patches)
2294 q.refresh(repo, msg=message, git=diffopts.git)
2294 q.refresh(repo, msg=message, git=diffopts.git)
2295 q.delete(repo, patches, opts)
2295 q.delete(repo, patches, opts)
2296 q.save_dirty()
2296 q.save_dirty()
2297
2297
2298 def goto(ui, repo, patch, **opts):
2298 def goto(ui, repo, patch, **opts):
2299 '''push or pop patches until named patch is at top of stack
2299 '''push or pop patches until named patch is at top of stack
2300
2300
2301 Returns 0 on success.'''
2301 Returns 0 on success.'''
2302 q = repo.mq
2302 q = repo.mq
2303 patch = q.lookup(patch)
2303 patch = q.lookup(patch)
2304 if q.isapplied(patch):
2304 if q.isapplied(patch):
2305 ret = q.pop(repo, patch, force=opts.get('force'))
2305 ret = q.pop(repo, patch, force=opts.get('force'))
2306 else:
2306 else:
2307 ret = q.push(repo, patch, force=opts.get('force'))
2307 ret = q.push(repo, patch, force=opts.get('force'))
2308 q.save_dirty()
2308 q.save_dirty()
2309 return ret
2309 return ret
2310
2310
2311 def guard(ui, repo, *args, **opts):
2311 def guard(ui, repo, *args, **opts):
2312 '''set or print guards for a patch
2312 '''set or print guards for a patch
2313
2313
2314 Guards control whether a patch can be pushed. A patch with no
2314 Guards control whether a patch can be pushed. A patch with no
2315 guards is always pushed. A patch with a positive guard ("+foo") is
2315 guards is always pushed. A patch with a positive guard ("+foo") is
2316 pushed only if the :hg:`qselect` command has activated it. A patch with
2316 pushed only if the :hg:`qselect` command has activated it. A patch with
2317 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2317 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2318 has activated it.
2318 has activated it.
2319
2319
2320 With no arguments, print the currently active guards.
2320 With no arguments, print the currently active guards.
2321 With arguments, set guards for the named patch.
2321 With arguments, set guards for the named patch.
2322
2322
2323 .. note::
2323 .. note::
2324 Specifying negative guards now requires '--'.
2324 Specifying negative guards now requires '--'.
2325
2325
2326 To set guards on another patch::
2326 To set guards on another patch::
2327
2327
2328 hg qguard other.patch -- +2.6.17 -stable
2328 hg qguard other.patch -- +2.6.17 -stable
2329
2329
2330 Returns 0 on success.
2330 Returns 0 on success.
2331 '''
2331 '''
2332 def status(idx):
2332 def status(idx):
2333 guards = q.series_guards[idx] or ['unguarded']
2333 guards = q.series_guards[idx] or ['unguarded']
2334 if q.series[idx] in applied:
2334 if q.series[idx] in applied:
2335 state = 'applied'
2335 state = 'applied'
2336 elif q.pushable(idx)[0]:
2336 elif q.pushable(idx)[0]:
2337 state = 'unapplied'
2337 state = 'unapplied'
2338 else:
2338 else:
2339 state = 'guarded'
2339 state = 'guarded'
2340 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2340 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2341 ui.write('%s: ' % ui.label(q.series[idx], label))
2341 ui.write('%s: ' % ui.label(q.series[idx], label))
2342
2342
2343 for i, guard in enumerate(guards):
2343 for i, guard in enumerate(guards):
2344 if guard.startswith('+'):
2344 if guard.startswith('+'):
2345 ui.write(guard, label='qguard.positive')
2345 ui.write(guard, label='qguard.positive')
2346 elif guard.startswith('-'):
2346 elif guard.startswith('-'):
2347 ui.write(guard, label='qguard.negative')
2347 ui.write(guard, label='qguard.negative')
2348 else:
2348 else:
2349 ui.write(guard, label='qguard.unguarded')
2349 ui.write(guard, label='qguard.unguarded')
2350 if i != len(guards) - 1:
2350 if i != len(guards) - 1:
2351 ui.write(' ')
2351 ui.write(' ')
2352 ui.write('\n')
2352 ui.write('\n')
2353 q = repo.mq
2353 q = repo.mq
2354 applied = set(p.name for p in q.applied)
2354 applied = set(p.name for p in q.applied)
2355 patch = None
2355 patch = None
2356 args = list(args)
2356 args = list(args)
2357 if opts.get('list'):
2357 if opts.get('list'):
2358 if args or opts.get('none'):
2358 if args or opts.get('none'):
2359 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2359 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2360 for i in xrange(len(q.series)):
2360 for i in xrange(len(q.series)):
2361 status(i)
2361 status(i)
2362 return
2362 return
2363 if not args or args[0][0:1] in '-+':
2363 if not args or args[0][0:1] in '-+':
2364 if not q.applied:
2364 if not q.applied:
2365 raise util.Abort(_('no patches applied'))
2365 raise util.Abort(_('no patches applied'))
2366 patch = q.applied[-1].name
2366 patch = q.applied[-1].name
2367 if patch is None and args[0][0:1] not in '-+':
2367 if patch is None and args[0][0:1] not in '-+':
2368 patch = args.pop(0)
2368 patch = args.pop(0)
2369 if patch is None:
2369 if patch is None:
2370 raise util.Abort(_('no patch to work with'))
2370 raise util.Abort(_('no patch to work with'))
2371 if args or opts.get('none'):
2371 if args or opts.get('none'):
2372 idx = q.find_series(patch)
2372 idx = q.find_series(patch)
2373 if idx is None:
2373 if idx is None:
2374 raise util.Abort(_('no patch named %s') % patch)
2374 raise util.Abort(_('no patch named %s') % patch)
2375 q.set_guards(idx, args)
2375 q.set_guards(idx, args)
2376 q.save_dirty()
2376 q.save_dirty()
2377 else:
2377 else:
2378 status(q.series.index(q.lookup(patch)))
2378 status(q.series.index(q.lookup(patch)))
2379
2379
2380 def header(ui, repo, patch=None):
2380 def header(ui, repo, patch=None):
2381 """print the header of the topmost or specified patch
2381 """print the header of the topmost or specified patch
2382
2382
2383 Returns 0 on success."""
2383 Returns 0 on success."""
2384 q = repo.mq
2384 q = repo.mq
2385
2385
2386 if patch:
2386 if patch:
2387 patch = q.lookup(patch)
2387 patch = q.lookup(patch)
2388 else:
2388 else:
2389 if not q.applied:
2389 if not q.applied:
2390 ui.write(_('no patches applied\n'))
2390 ui.write(_('no patches applied\n'))
2391 return 1
2391 return 1
2392 patch = q.lookup('qtip')
2392 patch = q.lookup('qtip')
2393 ph = patchheader(q.join(patch), q.plainmode)
2393 ph = patchheader(q.join(patch), q.plainmode)
2394
2394
2395 ui.write('\n'.join(ph.message) + '\n')
2395 ui.write('\n'.join(ph.message) + '\n')
2396
2396
2397 def lastsavename(path):
2397 def lastsavename(path):
2398 (directory, base) = os.path.split(path)
2398 (directory, base) = os.path.split(path)
2399 names = os.listdir(directory)
2399 names = os.listdir(directory)
2400 namere = re.compile("%s.([0-9]+)" % base)
2400 namere = re.compile("%s.([0-9]+)" % base)
2401 maxindex = None
2401 maxindex = None
2402 maxname = None
2402 maxname = None
2403 for f in names:
2403 for f in names:
2404 m = namere.match(f)
2404 m = namere.match(f)
2405 if m:
2405 if m:
2406 index = int(m.group(1))
2406 index = int(m.group(1))
2407 if maxindex is None or index > maxindex:
2407 if maxindex is None or index > maxindex:
2408 maxindex = index
2408 maxindex = index
2409 maxname = f
2409 maxname = f
2410 if maxname:
2410 if maxname:
2411 return (os.path.join(directory, maxname), maxindex)
2411 return (os.path.join(directory, maxname), maxindex)
2412 return (None, None)
2412 return (None, None)
2413
2413
2414 def savename(path):
2414 def savename(path):
2415 (last, index) = lastsavename(path)
2415 (last, index) = lastsavename(path)
2416 if last is None:
2416 if last is None:
2417 index = 0
2417 index = 0
2418 newpath = path + ".%d" % (index + 1)
2418 newpath = path + ".%d" % (index + 1)
2419 return newpath
2419 return newpath
2420
2420
2421 def push(ui, repo, patch=None, **opts):
2421 def push(ui, repo, patch=None, **opts):
2422 """push the next patch onto the stack
2422 """push the next patch onto the stack
2423
2423
2424 When -f/--force is applied, all local changes in patched files
2424 When -f/--force is applied, all local changes in patched files
2425 will be lost.
2425 will be lost.
2426
2426
2427 Return 0 on success.
2427 Return 0 on success.
2428 """
2428 """
2429 q = repo.mq
2429 q = repo.mq
2430 mergeq = None
2430 mergeq = None
2431
2431
2432 if opts.get('merge'):
2432 if opts.get('merge'):
2433 if opts.get('name'):
2433 if opts.get('name'):
2434 newpath = repo.join(opts.get('name'))
2434 newpath = repo.join(opts.get('name'))
2435 else:
2435 else:
2436 newpath, i = lastsavename(q.path)
2436 newpath, i = lastsavename(q.path)
2437 if not newpath:
2437 if not newpath:
2438 ui.warn(_("no saved queues found, please use -n\n"))
2438 ui.warn(_("no saved queues found, please use -n\n"))
2439 return 1
2439 return 1
2440 mergeq = queue(ui, repo.join(""), newpath)
2440 mergeq = queue(ui, repo.join(""), newpath)
2441 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2441 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2442 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2442 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2443 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2443 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2444 exact=opts.get('exact'))
2444 exact=opts.get('exact'))
2445 return ret
2445 return ret
2446
2446
2447 def pop(ui, repo, patch=None, **opts):
2447 def pop(ui, repo, patch=None, **opts):
2448 """pop the current patch off the stack
2448 """pop the current patch off the stack
2449
2449
2450 By default, pops off the top of the patch stack. If given a patch
2450 By default, pops off the top of the patch stack. If given a patch
2451 name, keeps popping off patches until the named patch is at the
2451 name, keeps popping off patches until the named patch is at the
2452 top of the stack.
2452 top of the stack.
2453
2453
2454 Return 0 on success.
2454 Return 0 on success.
2455 """
2455 """
2456 localupdate = True
2456 localupdate = True
2457 if opts.get('name'):
2457 if opts.get('name'):
2458 q = queue(ui, repo.join(""), repo.join(opts.get('name')))
2458 q = queue(ui, repo.join(""), repo.join(opts.get('name')))
2459 ui.warn(_('using patch queue: %s\n') % q.path)
2459 ui.warn(_('using patch queue: %s\n') % q.path)
2460 localupdate = False
2460 localupdate = False
2461 else:
2461 else:
2462 q = repo.mq
2462 q = repo.mq
2463 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2463 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2464 all=opts.get('all'))
2464 all=opts.get('all'))
2465 q.save_dirty()
2465 q.save_dirty()
2466 return ret
2466 return ret
2467
2467
2468 def rename(ui, repo, patch, name=None, **opts):
2468 def rename(ui, repo, patch, name=None, **opts):
2469 """rename a patch
2469 """rename a patch
2470
2470
2471 With one argument, renames the current patch to PATCH1.
2471 With one argument, renames the current patch to PATCH1.
2472 With two arguments, renames PATCH1 to PATCH2.
2472 With two arguments, renames PATCH1 to PATCH2.
2473
2473
2474 Returns 0 on success."""
2474 Returns 0 on success."""
2475
2475
2476 q = repo.mq
2476 q = repo.mq
2477
2477
2478 if not name:
2478 if not name:
2479 name = patch
2479 name = patch
2480 patch = None
2480 patch = None
2481
2481
2482 if patch:
2482 if patch:
2483 patch = q.lookup(patch)
2483 patch = q.lookup(patch)
2484 else:
2484 else:
2485 if not q.applied:
2485 if not q.applied:
2486 ui.write(_('no patches applied\n'))
2486 ui.write(_('no patches applied\n'))
2487 return
2487 return
2488 patch = q.lookup('qtip')
2488 patch = q.lookup('qtip')
2489 absdest = q.join(name)
2489 absdest = q.join(name)
2490 if os.path.isdir(absdest):
2490 if os.path.isdir(absdest):
2491 name = normname(os.path.join(name, os.path.basename(patch)))
2491 name = normname(os.path.join(name, os.path.basename(patch)))
2492 absdest = q.join(name)
2492 absdest = q.join(name)
2493 if os.path.exists(absdest):
2493 if os.path.exists(absdest):
2494 raise util.Abort(_('%s already exists') % absdest)
2494 raise util.Abort(_('%s already exists') % absdest)
2495
2495
2496 if name in q.series:
2496 if name in q.series:
2497 raise util.Abort(
2497 raise util.Abort(
2498 _('A patch named %s already exists in the series file') % name)
2498 _('A patch named %s already exists in the series file') % name)
2499
2499
2500 ui.note(_('renaming %s to %s\n') % (patch, name))
2500 ui.note(_('renaming %s to %s\n') % (patch, name))
2501 i = q.find_series(patch)
2501 i = q.find_series(patch)
2502 guards = q.guard_re.findall(q.full_series[i])
2502 guards = q.guard_re.findall(q.full_series[i])
2503 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2503 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2504 q.parse_series()
2504 q.parse_series()
2505 q.series_dirty = 1
2505 q.series_dirty = 1
2506
2506
2507 info = q.isapplied(patch)
2507 info = q.isapplied(patch)
2508 if info:
2508 if info:
2509 q.applied[info[0]] = statusentry(info[1], name)
2509 q.applied[info[0]] = statusentry(info[1], name)
2510 q.applied_dirty = 1
2510 q.applied_dirty = 1
2511
2511
2512 destdir = os.path.dirname(absdest)
2512 destdir = os.path.dirname(absdest)
2513 if not os.path.isdir(destdir):
2513 if not os.path.isdir(destdir):
2514 os.makedirs(destdir)
2514 os.makedirs(destdir)
2515 util.rename(q.join(patch), absdest)
2515 util.rename(q.join(patch), absdest)
2516 r = q.qrepo()
2516 r = q.qrepo()
2517 if r and patch in r.dirstate:
2517 if r and patch in r.dirstate:
2518 wctx = r[None]
2518 wctx = r[None]
2519 wlock = r.wlock()
2519 wlock = r.wlock()
2520 try:
2520 try:
2521 if r.dirstate[patch] == 'a':
2521 if r.dirstate[patch] == 'a':
2522 r.dirstate.forget(patch)
2522 r.dirstate.forget(patch)
2523 r.dirstate.add(name)
2523 r.dirstate.add(name)
2524 else:
2524 else:
2525 if r.dirstate[name] == 'r':
2525 if r.dirstate[name] == 'r':
2526 wctx.undelete([name])
2526 wctx.undelete([name])
2527 wctx.copy(patch, name)
2527 wctx.copy(patch, name)
2528 wctx.remove([patch], False)
2528 wctx.remove([patch], False)
2529 finally:
2529 finally:
2530 wlock.release()
2530 wlock.release()
2531
2531
2532 q.save_dirty()
2532 q.save_dirty()
2533
2533
2534 def restore(ui, repo, rev, **opts):
2534 def restore(ui, repo, rev, **opts):
2535 """restore the queue state saved by a revision (DEPRECATED)
2535 """restore the queue state saved by a revision (DEPRECATED)
2536
2536
2537 This command is deprecated, use :hg:`rebase` instead."""
2537 This command is deprecated, use :hg:`rebase` instead."""
2538 rev = repo.lookup(rev)
2538 rev = repo.lookup(rev)
2539 q = repo.mq
2539 q = repo.mq
2540 q.restore(repo, rev, delete=opts.get('delete'),
2540 q.restore(repo, rev, delete=opts.get('delete'),
2541 qupdate=opts.get('update'))
2541 qupdate=opts.get('update'))
2542 q.save_dirty()
2542 q.save_dirty()
2543 return 0
2543 return 0
2544
2544
2545 def save(ui, repo, **opts):
2545 def save(ui, repo, **opts):
2546 """save current queue state (DEPRECATED)
2546 """save current queue state (DEPRECATED)
2547
2547
2548 This command is deprecated, use :hg:`rebase` instead."""
2548 This command is deprecated, use :hg:`rebase` instead."""
2549 q = repo.mq
2549 q = repo.mq
2550 message = cmdutil.logmessage(opts)
2550 message = cmdutil.logmessage(opts)
2551 ret = q.save(repo, msg=message)
2551 ret = q.save(repo, msg=message)
2552 if ret:
2552 if ret:
2553 return ret
2553 return ret
2554 q.save_dirty()
2554 q.save_dirty()
2555 if opts.get('copy'):
2555 if opts.get('copy'):
2556 path = q.path
2556 path = q.path
2557 if opts.get('name'):
2557 if opts.get('name'):
2558 newpath = os.path.join(q.basepath, opts.get('name'))
2558 newpath = os.path.join(q.basepath, opts.get('name'))
2559 if os.path.exists(newpath):
2559 if os.path.exists(newpath):
2560 if not os.path.isdir(newpath):
2560 if not os.path.isdir(newpath):
2561 raise util.Abort(_('destination %s exists and is not '
2561 raise util.Abort(_('destination %s exists and is not '
2562 'a directory') % newpath)
2562 'a directory') % newpath)
2563 if not opts.get('force'):
2563 if not opts.get('force'):
2564 raise util.Abort(_('destination %s exists, '
2564 raise util.Abort(_('destination %s exists, '
2565 'use -f to force') % newpath)
2565 'use -f to force') % newpath)
2566 else:
2566 else:
2567 newpath = savename(path)
2567 newpath = savename(path)
2568 ui.warn(_("copy %s to %s\n") % (path, newpath))
2568 ui.warn(_("copy %s to %s\n") % (path, newpath))
2569 util.copyfiles(path, newpath)
2569 util.copyfiles(path, newpath)
2570 if opts.get('empty'):
2570 if opts.get('empty'):
2571 try:
2571 try:
2572 os.unlink(q.join(q.status_path))
2572 os.unlink(q.join(q.status_path))
2573 except:
2573 except:
2574 pass
2574 pass
2575 return 0
2575 return 0
2576
2576
2577 def strip(ui, repo, *revs, **opts):
2577 def strip(ui, repo, *revs, **opts):
2578 """strip changesets and all their descendants from the repository
2578 """strip changesets and all their descendants from the repository
2579
2579
2580 The strip command removes the specified changesets and all their
2580 The strip command removes the specified changesets and all their
2581 descendants. If the working directory has uncommitted changes, the
2581 descendants. If the working directory has uncommitted changes, the
2582 operation is aborted unless the --force flag is supplied, in which
2582 operation is aborted unless the --force flag is supplied, in which
2583 case changes will be discarded.
2583 case changes will be discarded.
2584
2584
2585 If a parent of the working directory is stripped, then the working
2585 If a parent of the working directory is stripped, then the working
2586 directory will automatically be updated to the most recent
2586 directory will automatically be updated to the most recent
2587 available ancestor of the stripped parent after the operation
2587 available ancestor of the stripped parent after the operation
2588 completes.
2588 completes.
2589
2589
2590 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2590 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2591 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2591 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2592 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2592 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2593 where BUNDLE is the bundle file created by the strip. Note that
2593 where BUNDLE is the bundle file created by the strip. Note that
2594 the local revision numbers will in general be different after the
2594 the local revision numbers will in general be different after the
2595 restore.
2595 restore.
2596
2596
2597 Use the --no-backup option to discard the backup bundle once the
2597 Use the --no-backup option to discard the backup bundle once the
2598 operation completes.
2598 operation completes.
2599
2599
2600 Return 0 on success.
2600 Return 0 on success.
2601 """
2601 """
2602 backup = 'all'
2602 backup = 'all'
2603 if opts.get('backup'):
2603 if opts.get('backup'):
2604 backup = 'strip'
2604 backup = 'strip'
2605 elif opts.get('no_backup') or opts.get('nobackup'):
2605 elif opts.get('no_backup') or opts.get('nobackup'):
2606 backup = 'none'
2606 backup = 'none'
2607
2607
2608 cl = repo.changelog
2608 cl = repo.changelog
2609 revs = set(cmdutil.revrange(repo, revs))
2609 revs = set(cmdutil.revrange(repo, revs))
2610 if not revs:
2610 if not revs:
2611 raise util.Abort(_('empty revision set'))
2611 raise util.Abort(_('empty revision set'))
2612
2612
2613 descendants = set(cl.descendants(*revs))
2613 descendants = set(cl.descendants(*revs))
2614 strippedrevs = revs.union(descendants)
2614 strippedrevs = revs.union(descendants)
2615 roots = revs.difference(descendants)
2615 roots = revs.difference(descendants)
2616
2616
2617 update = False
2617 update = False
2618 # if one of the wdir parent is stripped we'll need
2618 # if one of the wdir parent is stripped we'll need
2619 # to update away to an earlier revision
2619 # to update away to an earlier revision
2620 for p in repo.dirstate.parents():
2620 for p in repo.dirstate.parents():
2621 if p != nullid and cl.rev(p) in strippedrevs:
2621 if p != nullid and cl.rev(p) in strippedrevs:
2622 update = True
2622 update = True
2623 break
2623 break
2624
2624
2625 rootnodes = set(cl.node(r) for r in roots)
2625 rootnodes = set(cl.node(r) for r in roots)
2626
2626
2627 q = repo.mq
2627 q = repo.mq
2628 if q.applied:
2628 if q.applied:
2629 # refresh queue state if we're about to strip
2629 # refresh queue state if we're about to strip
2630 # applied patches
2630 # applied patches
2631 if cl.rev(repo.lookup('qtip')) in strippedrevs:
2631 if cl.rev(repo.lookup('qtip')) in strippedrevs:
2632 q.applied_dirty = True
2632 q.applied_dirty = True
2633 start = 0
2633 start = 0
2634 end = len(q.applied)
2634 end = len(q.applied)
2635 for i, statusentry in enumerate(q.applied):
2635 for i, statusentry in enumerate(q.applied):
2636 if statusentry.node in rootnodes:
2636 if statusentry.node in rootnodes:
2637 # if one of the stripped roots is an applied
2637 # if one of the stripped roots is an applied
2638 # patch, only part of the queue is stripped
2638 # patch, only part of the queue is stripped
2639 start = i
2639 start = i
2640 break
2640 break
2641 del q.applied[start:end]
2641 del q.applied[start:end]
2642 q.save_dirty()
2642 q.save_dirty()
2643
2643
2644 revs = list(rootnodes)
2644 revs = list(rootnodes)
2645 if update and opts.get('keep'):
2645 if update and opts.get('keep'):
2646 wlock = repo.wlock()
2646 wlock = repo.wlock()
2647 try:
2647 try:
2648 urev = repo.mq.qparents(repo, revs[0])
2648 urev = repo.mq.qparents(repo, revs[0])
2649 repo.dirstate.rebuild(urev, repo[urev].manifest())
2649 repo.dirstate.rebuild(urev, repo[urev].manifest())
2650 repo.dirstate.write()
2650 repo.dirstate.write()
2651 update = False
2651 update = False
2652 finally:
2652 finally:
2653 wlock.release()
2653 wlock.release()
2654
2654
2655 repo.mq.strip(repo, revs, backup=backup, update=update,
2655 repo.mq.strip(repo, revs, backup=backup, update=update,
2656 force=opts.get('force'))
2656 force=opts.get('force'))
2657 return 0
2657 return 0
2658
2658
2659 def select(ui, repo, *args, **opts):
2659 def select(ui, repo, *args, **opts):
2660 '''set or print guarded patches to push
2660 '''set or print guarded patches to push
2661
2661
2662 Use the :hg:`qguard` command to set or print guards on patch, then use
2662 Use the :hg:`qguard` command to set or print guards on patch, then use
2663 qselect to tell mq which guards to use. A patch will be pushed if
2663 qselect to tell mq which guards to use. A patch will be pushed if
2664 it has no guards or any positive guards match the currently
2664 it has no guards or any positive guards match the currently
2665 selected guard, but will not be pushed if any negative guards
2665 selected guard, but will not be pushed if any negative guards
2666 match the current guard. For example::
2666 match the current guard. For example::
2667
2667
2668 qguard foo.patch -- -stable (negative guard)
2668 qguard foo.patch -- -stable (negative guard)
2669 qguard bar.patch +stable (positive guard)
2669 qguard bar.patch +stable (positive guard)
2670 qselect stable
2670 qselect stable
2671
2671
2672 This activates the "stable" guard. mq will skip foo.patch (because
2672 This activates the "stable" guard. mq will skip foo.patch (because
2673 it has a negative match) but push bar.patch (because it has a
2673 it has a negative match) but push bar.patch (because it has a
2674 positive match).
2674 positive match).
2675
2675
2676 With no arguments, prints the currently active guards.
2676 With no arguments, prints the currently active guards.
2677 With one argument, sets the active guard.
2677 With one argument, sets the active guard.
2678
2678
2679 Use -n/--none to deactivate guards (no other arguments needed).
2679 Use -n/--none to deactivate guards (no other arguments needed).
2680 When no guards are active, patches with positive guards are
2680 When no guards are active, patches with positive guards are
2681 skipped and patches with negative guards are pushed.
2681 skipped and patches with negative guards are pushed.
2682
2682
2683 qselect can change the guards on applied patches. It does not pop
2683 qselect can change the guards on applied patches. It does not pop
2684 guarded patches by default. Use --pop to pop back to the last
2684 guarded patches by default. Use --pop to pop back to the last
2685 applied patch that is not guarded. Use --reapply (which implies
2685 applied patch that is not guarded. Use --reapply (which implies
2686 --pop) to push back to the current patch afterwards, but skip
2686 --pop) to push back to the current patch afterwards, but skip
2687 guarded patches.
2687 guarded patches.
2688
2688
2689 Use -s/--series to print a list of all guards in the series file
2689 Use -s/--series to print a list of all guards in the series file
2690 (no other arguments needed). Use -v for more information.
2690 (no other arguments needed). Use -v for more information.
2691
2691
2692 Returns 0 on success.'''
2692 Returns 0 on success.'''
2693
2693
2694 q = repo.mq
2694 q = repo.mq
2695 guards = q.active()
2695 guards = q.active()
2696 if args or opts.get('none'):
2696 if args or opts.get('none'):
2697 old_unapplied = q.unapplied(repo)
2697 old_unapplied = q.unapplied(repo)
2698 old_guarded = [i for i in xrange(len(q.applied)) if
2698 old_guarded = [i for i in xrange(len(q.applied)) if
2699 not q.pushable(i)[0]]
2699 not q.pushable(i)[0]]
2700 q.set_active(args)
2700 q.set_active(args)
2701 q.save_dirty()
2701 q.save_dirty()
2702 if not args:
2702 if not args:
2703 ui.status(_('guards deactivated\n'))
2703 ui.status(_('guards deactivated\n'))
2704 if not opts.get('pop') and not opts.get('reapply'):
2704 if not opts.get('pop') and not opts.get('reapply'):
2705 unapplied = q.unapplied(repo)
2705 unapplied = q.unapplied(repo)
2706 guarded = [i for i in xrange(len(q.applied))
2706 guarded = [i for i in xrange(len(q.applied))
2707 if not q.pushable(i)[0]]
2707 if not q.pushable(i)[0]]
2708 if len(unapplied) != len(old_unapplied):
2708 if len(unapplied) != len(old_unapplied):
2709 ui.status(_('number of unguarded, unapplied patches has '
2709 ui.status(_('number of unguarded, unapplied patches has '
2710 'changed from %d to %d\n') %
2710 'changed from %d to %d\n') %
2711 (len(old_unapplied), len(unapplied)))
2711 (len(old_unapplied), len(unapplied)))
2712 if len(guarded) != len(old_guarded):
2712 if len(guarded) != len(old_guarded):
2713 ui.status(_('number of guarded, applied patches has changed '
2713 ui.status(_('number of guarded, applied patches has changed '
2714 'from %d to %d\n') %
2714 'from %d to %d\n') %
2715 (len(old_guarded), len(guarded)))
2715 (len(old_guarded), len(guarded)))
2716 elif opts.get('series'):
2716 elif opts.get('series'):
2717 guards = {}
2717 guards = {}
2718 noguards = 0
2718 noguards = 0
2719 for gs in q.series_guards:
2719 for gs in q.series_guards:
2720 if not gs:
2720 if not gs:
2721 noguards += 1
2721 noguards += 1
2722 for g in gs:
2722 for g in gs:
2723 guards.setdefault(g, 0)
2723 guards.setdefault(g, 0)
2724 guards[g] += 1
2724 guards[g] += 1
2725 if ui.verbose:
2725 if ui.verbose:
2726 guards['NONE'] = noguards
2726 guards['NONE'] = noguards
2727 guards = guards.items()
2727 guards = guards.items()
2728 guards.sort(key=lambda x: x[0][1:])
2728 guards.sort(key=lambda x: x[0][1:])
2729 if guards:
2729 if guards:
2730 ui.note(_('guards in series file:\n'))
2730 ui.note(_('guards in series file:\n'))
2731 for guard, count in guards:
2731 for guard, count in guards:
2732 ui.note('%2d ' % count)
2732 ui.note('%2d ' % count)
2733 ui.write(guard, '\n')
2733 ui.write(guard, '\n')
2734 else:
2734 else:
2735 ui.note(_('no guards in series file\n'))
2735 ui.note(_('no guards in series file\n'))
2736 else:
2736 else:
2737 if guards:
2737 if guards:
2738 ui.note(_('active guards:\n'))
2738 ui.note(_('active guards:\n'))
2739 for g in guards:
2739 for g in guards:
2740 ui.write(g, '\n')
2740 ui.write(g, '\n')
2741 else:
2741 else:
2742 ui.write(_('no active guards\n'))
2742 ui.write(_('no active guards\n'))
2743 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
2743 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
2744 popped = False
2744 popped = False
2745 if opts.get('pop') or opts.get('reapply'):
2745 if opts.get('pop') or opts.get('reapply'):
2746 for i in xrange(len(q.applied)):
2746 for i in xrange(len(q.applied)):
2747 pushable, reason = q.pushable(i)
2747 pushable, reason = q.pushable(i)
2748 if not pushable:
2748 if not pushable:
2749 ui.status(_('popping guarded patches\n'))
2749 ui.status(_('popping guarded patches\n'))
2750 popped = True
2750 popped = True
2751 if i == 0:
2751 if i == 0:
2752 q.pop(repo, all=True)
2752 q.pop(repo, all=True)
2753 else:
2753 else:
2754 q.pop(repo, i - 1)
2754 q.pop(repo, i - 1)
2755 break
2755 break
2756 if popped:
2756 if popped:
2757 try:
2757 try:
2758 if reapply:
2758 if reapply:
2759 ui.status(_('reapplying unguarded patches\n'))
2759 ui.status(_('reapplying unguarded patches\n'))
2760 q.push(repo, reapply)
2760 q.push(repo, reapply)
2761 finally:
2761 finally:
2762 q.save_dirty()
2762 q.save_dirty()
2763
2763
2764 def finish(ui, repo, *revrange, **opts):
2764 def finish(ui, repo, *revrange, **opts):
2765 """move applied patches into repository history
2765 """move applied patches into repository history
2766
2766
2767 Finishes the specified revisions (corresponding to applied
2767 Finishes the specified revisions (corresponding to applied
2768 patches) by moving them out of mq control into regular repository
2768 patches) by moving them out of mq control into regular repository
2769 history.
2769 history.
2770
2770
2771 Accepts a revision range or the -a/--applied option. If --applied
2771 Accepts a revision range or the -a/--applied option. If --applied
2772 is specified, all applied mq revisions are removed from mq
2772 is specified, all applied mq revisions are removed from mq
2773 control. Otherwise, the given revisions must be at the base of the
2773 control. Otherwise, the given revisions must be at the base of the
2774 stack of applied patches.
2774 stack of applied patches.
2775
2775
2776 This can be especially useful if your changes have been applied to
2776 This can be especially useful if your changes have been applied to
2777 an upstream repository, or if you are about to push your changes
2777 an upstream repository, or if you are about to push your changes
2778 to upstream.
2778 to upstream.
2779
2779
2780 Returns 0 on success.
2780 Returns 0 on success.
2781 """
2781 """
2782 if not opts.get('applied') and not revrange:
2782 if not opts.get('applied') and not revrange:
2783 raise util.Abort(_('no revisions specified'))
2783 raise util.Abort(_('no revisions specified'))
2784 elif opts.get('applied'):
2784 elif opts.get('applied'):
2785 revrange = ('qbase::qtip',) + revrange
2785 revrange = ('qbase::qtip',) + revrange
2786
2786
2787 q = repo.mq
2787 q = repo.mq
2788 if not q.applied:
2788 if not q.applied:
2789 ui.status(_('no patches applied\n'))
2789 ui.status(_('no patches applied\n'))
2790 return 0
2790 return 0
2791
2791
2792 revs = cmdutil.revrange(repo, revrange)
2792 revs = cmdutil.revrange(repo, revrange)
2793 q.finish(repo, revs)
2793 q.finish(repo, revs)
2794 q.save_dirty()
2794 q.save_dirty()
2795 return 0
2795 return 0
2796
2796
2797 def qqueue(ui, repo, name=None, **opts):
2797 def qqueue(ui, repo, name=None, **opts):
2798 '''manage multiple patch queues
2798 '''manage multiple patch queues
2799
2799
2800 Supports switching between different patch queues, as well as creating
2800 Supports switching between different patch queues, as well as creating
2801 new patch queues and deleting existing ones.
2801 new patch queues and deleting existing ones.
2802
2802
2803 Omitting a queue name or specifying -l/--list will show you the registered
2803 Omitting a queue name or specifying -l/--list will show you the registered
2804 queues - by default the "normal" patches queue is registered. The currently
2804 queues - by default the "normal" patches queue is registered. The currently
2805 active queue will be marked with "(active)".
2805 active queue will be marked with "(active)".
2806
2806
2807 To create a new queue, use -c/--create. The queue is automatically made
2807 To create a new queue, use -c/--create. The queue is automatically made
2808 active, except in the case where there are applied patches from the
2808 active, except in the case where there are applied patches from the
2809 currently active queue in the repository. Then the queue will only be
2809 currently active queue in the repository. Then the queue will only be
2810 created and switching will fail.
2810 created and switching will fail.
2811
2811
2812 To delete an existing queue, use --delete. You cannot delete the currently
2812 To delete an existing queue, use --delete. You cannot delete the currently
2813 active queue.
2813 active queue.
2814
2814
2815 Returns 0 on success.
2815 Returns 0 on success.
2816 '''
2816 '''
2817
2817
2818 q = repo.mq
2818 q = repo.mq
2819
2819
2820 _defaultqueue = 'patches'
2820 _defaultqueue = 'patches'
2821 _allqueues = 'patches.queues'
2821 _allqueues = 'patches.queues'
2822 _activequeue = 'patches.queue'
2822 _activequeue = 'patches.queue'
2823
2823
2824 def _getcurrent():
2824 def _getcurrent():
2825 cur = os.path.basename(q.path)
2825 cur = os.path.basename(q.path)
2826 if cur.startswith('patches-'):
2826 if cur.startswith('patches-'):
2827 cur = cur[8:]
2827 cur = cur[8:]
2828 return cur
2828 return cur
2829
2829
2830 def _noqueues():
2830 def _noqueues():
2831 try:
2831 try:
2832 fh = repo.opener(_allqueues, 'r')
2832 fh = repo.opener(_allqueues, 'r')
2833 fh.close()
2833 fh.close()
2834 except IOError:
2834 except IOError:
2835 return True
2835 return True
2836
2836
2837 return False
2837 return False
2838
2838
2839 def _getqueues():
2839 def _getqueues():
2840 current = _getcurrent()
2840 current = _getcurrent()
2841
2841
2842 try:
2842 try:
2843 fh = repo.opener(_allqueues, 'r')
2843 fh = repo.opener(_allqueues, 'r')
2844 queues = [queue.strip() for queue in fh if queue.strip()]
2844 queues = [queue.strip() for queue in fh if queue.strip()]
2845 fh.close()
2845 fh.close()
2846 if current not in queues:
2846 if current not in queues:
2847 queues.append(current)
2847 queues.append(current)
2848 except IOError:
2848 except IOError:
2849 queues = [_defaultqueue]
2849 queues = [_defaultqueue]
2850
2850
2851 return sorted(queues)
2851 return sorted(queues)
2852
2852
2853 def _setactive(name):
2853 def _setactive(name):
2854 if q.applied:
2854 if q.applied:
2855 raise util.Abort(_('patches applied - cannot set new queue active'))
2855 raise util.Abort(_('patches applied - cannot set new queue active'))
2856 _setactivenocheck(name)
2856 _setactivenocheck(name)
2857
2857
2858 def _setactivenocheck(name):
2858 def _setactivenocheck(name):
2859 fh = repo.opener(_activequeue, 'w')
2859 fh = repo.opener(_activequeue, 'w')
2860 if name != 'patches':
2860 if name != 'patches':
2861 fh.write(name)
2861 fh.write(name)
2862 fh.close()
2862 fh.close()
2863
2863
2864 def _addqueue(name):
2864 def _addqueue(name):
2865 fh = repo.opener(_allqueues, 'a')
2865 fh = repo.opener(_allqueues, 'a')
2866 fh.write('%s\n' % (name,))
2866 fh.write('%s\n' % (name,))
2867 fh.close()
2867 fh.close()
2868
2868
2869 def _queuedir(name):
2869 def _queuedir(name):
2870 if name == 'patches':
2870 if name == 'patches':
2871 return repo.join('patches')
2871 return repo.join('patches')
2872 else:
2872 else:
2873 return repo.join('patches-' + name)
2873 return repo.join('patches-' + name)
2874
2874
2875 def _validname(name):
2875 def _validname(name):
2876 for n in name:
2876 for n in name:
2877 if n in ':\\/.':
2877 if n in ':\\/.':
2878 return False
2878 return False
2879 return True
2879 return True
2880
2880
2881 def _delete(name):
2881 def _delete(name):
2882 if name not in existing:
2882 if name not in existing:
2883 raise util.Abort(_('cannot delete queue that does not exist'))
2883 raise util.Abort(_('cannot delete queue that does not exist'))
2884
2884
2885 current = _getcurrent()
2885 current = _getcurrent()
2886
2886
2887 if name == current:
2887 if name == current:
2888 raise util.Abort(_('cannot delete currently active queue'))
2888 raise util.Abort(_('cannot delete currently active queue'))
2889
2889
2890 fh = repo.opener('patches.queues.new', 'w')
2890 fh = repo.opener('patches.queues.new', 'w')
2891 for queue in existing:
2891 for queue in existing:
2892 if queue == name:
2892 if queue == name:
2893 continue
2893 continue
2894 fh.write('%s\n' % (queue,))
2894 fh.write('%s\n' % (queue,))
2895 fh.close()
2895 fh.close()
2896 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2896 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2897
2897
2898 if not name or opts.get('list'):
2898 if not name or opts.get('list'):
2899 current = _getcurrent()
2899 current = _getcurrent()
2900 for queue in _getqueues():
2900 for queue in _getqueues():
2901 ui.write('%s' % (queue,))
2901 ui.write('%s' % (queue,))
2902 if queue == current and not ui.quiet:
2902 if queue == current and not ui.quiet:
2903 ui.write(_(' (active)\n'))
2903 ui.write(_(' (active)\n'))
2904 else:
2904 else:
2905 ui.write('\n')
2905 ui.write('\n')
2906 return
2906 return
2907
2907
2908 if not _validname(name):
2908 if not _validname(name):
2909 raise util.Abort(
2909 raise util.Abort(
2910 _('invalid queue name, may not contain the characters ":\\/."'))
2910 _('invalid queue name, may not contain the characters ":\\/."'))
2911
2911
2912 existing = _getqueues()
2912 existing = _getqueues()
2913
2913
2914 if opts.get('create'):
2914 if opts.get('create'):
2915 if name in existing:
2915 if name in existing:
2916 raise util.Abort(_('queue "%s" already exists') % name)
2916 raise util.Abort(_('queue "%s" already exists') % name)
2917 if _noqueues():
2917 if _noqueues():
2918 _addqueue(_defaultqueue)
2918 _addqueue(_defaultqueue)
2919 _addqueue(name)
2919 _addqueue(name)
2920 _setactive(name)
2920 _setactive(name)
2921 elif opts.get('rename'):
2921 elif opts.get('rename'):
2922 current = _getcurrent()
2922 current = _getcurrent()
2923 if name == current:
2923 if name == current:
2924 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
2924 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
2925 if name in existing:
2925 if name in existing:
2926 raise util.Abort(_('queue "%s" already exists') % name)
2926 raise util.Abort(_('queue "%s" already exists') % name)
2927
2927
2928 olddir = _queuedir(current)
2928 olddir = _queuedir(current)
2929 newdir = _queuedir(name)
2929 newdir = _queuedir(name)
2930
2930
2931 if os.path.exists(newdir):
2931 if os.path.exists(newdir):
2932 raise util.Abort(_('non-queue directory "%s" already exists') %
2932 raise util.Abort(_('non-queue directory "%s" already exists') %
2933 newdir)
2933 newdir)
2934
2934
2935 fh = repo.opener('patches.queues.new', 'w')
2935 fh = repo.opener('patches.queues.new', 'w')
2936 for queue in existing:
2936 for queue in existing:
2937 if queue == current:
2937 if queue == current:
2938 fh.write('%s\n' % (name,))
2938 fh.write('%s\n' % (name,))
2939 if os.path.exists(olddir):
2939 if os.path.exists(olddir):
2940 util.rename(olddir, newdir)
2940 util.rename(olddir, newdir)
2941 else:
2941 else:
2942 fh.write('%s\n' % (queue,))
2942 fh.write('%s\n' % (queue,))
2943 fh.close()
2943 fh.close()
2944 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2944 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2945 _setactivenocheck(name)
2945 _setactivenocheck(name)
2946 elif opts.get('delete'):
2946 elif opts.get('delete'):
2947 _delete(name)
2947 _delete(name)
2948 elif opts.get('purge'):
2948 elif opts.get('purge'):
2949 if name in existing:
2949 if name in existing:
2950 _delete(name)
2950 _delete(name)
2951 qdir = _queuedir(name)
2951 qdir = _queuedir(name)
2952 if os.path.exists(qdir):
2952 if os.path.exists(qdir):
2953 shutil.rmtree(qdir)
2953 shutil.rmtree(qdir)
2954 else:
2954 else:
2955 if name not in existing:
2955 if name not in existing:
2956 raise util.Abort(_('use --create to create a new queue'))
2956 raise util.Abort(_('use --create to create a new queue'))
2957 _setactive(name)
2957 _setactive(name)
2958
2958
2959 def reposetup(ui, repo):
2959 def reposetup(ui, repo):
2960 class mqrepo(repo.__class__):
2960 class mqrepo(repo.__class__):
2961 @util.propertycache
2961 @util.propertycache
2962 def mq(self):
2962 def mq(self):
2963 return queue(self.ui, self.join(""))
2963 return queue(self.ui, self.join(""))
2964
2964
2965 def abort_if_wdir_patched(self, errmsg, force=False):
2965 def abort_if_wdir_patched(self, errmsg, force=False):
2966 if self.mq.applied and not force:
2966 if self.mq.applied and not force:
2967 parents = self.dirstate.parents()
2967 parents = self.dirstate.parents()
2968 patches = [s.node for s in self.mq.applied]
2968 patches = [s.node for s in self.mq.applied]
2969 if parents[0] in patches or parents[1] in patches:
2969 if parents[0] in patches or parents[1] in patches:
2970 raise util.Abort(errmsg)
2970 raise util.Abort(errmsg)
2971
2971
2972 def commit(self, text="", user=None, date=None, match=None,
2972 def commit(self, text="", user=None, date=None, match=None,
2973 force=False, editor=False, extra={}):
2973 force=False, editor=False, extra={}):
2974 self.abort_if_wdir_patched(
2974 self.abort_if_wdir_patched(
2975 _('cannot commit over an applied mq patch'),
2975 _('cannot commit over an applied mq patch'),
2976 force)
2976 force)
2977
2977
2978 return super(mqrepo, self).commit(text, user, date, match, force,
2978 return super(mqrepo, self).commit(text, user, date, match, force,
2979 editor, extra)
2979 editor, extra)
2980
2980
2981 def checkpush(self, force, revs):
2981 def checkpush(self, force, revs):
2982 if self.mq.applied and not force:
2982 if self.mq.applied and not force:
2983 haspatches = True
2983 haspatches = True
2984 if revs:
2984 if revs:
2985 # Assume applied patches have no non-patch descendants
2985 # Assume applied patches have no non-patch descendants
2986 # and are not on remote already. If they appear in the
2986 # and are not on remote already. If they appear in the
2987 # set of resolved 'revs', bail out.
2987 # set of resolved 'revs', bail out.
2988 applied = set(e.node for e in self.mq.applied)
2988 applied = set(e.node for e in self.mq.applied)
2989 haspatches = bool([n for n in revs if n in applied])
2989 haspatches = bool([n for n in revs if n in applied])
2990 if haspatches:
2990 if haspatches:
2991 raise util.Abort(_('source has mq patches applied'))
2991 raise util.Abort(_('source has mq patches applied'))
2992 super(mqrepo, self).checkpush(force, revs)
2992 super(mqrepo, self).checkpush(force, revs)
2993
2993
2994 def _findtags(self):
2994 def _findtags(self):
2995 '''augment tags from base class with patch tags'''
2995 '''augment tags from base class with patch tags'''
2996 result = super(mqrepo, self)._findtags()
2996 result = super(mqrepo, self)._findtags()
2997
2997
2998 q = self.mq
2998 q = self.mq
2999 if not q.applied:
2999 if not q.applied:
3000 return result
3000 return result
3001
3001
3002 mqtags = [(patch.node, patch.name) for patch in q.applied]
3002 mqtags = [(patch.node, patch.name) for patch in q.applied]
3003
3003
3004 try:
3004 try:
3005 self.changelog.rev(mqtags[-1][0])
3005 self.changelog.rev(mqtags[-1][0])
3006 except error.RepoLookupError:
3006 except error.RepoLookupError:
3007 self.ui.warn(_('mq status file refers to unknown node %s\n')
3007 self.ui.warn(_('mq status file refers to unknown node %s\n')
3008 % short(mqtags[-1][0]))
3008 % short(mqtags[-1][0]))
3009 return result
3009 return result
3010
3010
3011 mqtags.append((mqtags[-1][0], 'qtip'))
3011 mqtags.append((mqtags[-1][0], 'qtip'))
3012 mqtags.append((mqtags[0][0], 'qbase'))
3012 mqtags.append((mqtags[0][0], 'qbase'))
3013 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3013 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3014 tags = result[0]
3014 tags = result[0]
3015 for patch in mqtags:
3015 for patch in mqtags:
3016 if patch[1] in tags:
3016 if patch[1] in tags:
3017 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
3017 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
3018 % patch[1])
3018 % patch[1])
3019 else:
3019 else:
3020 tags[patch[1]] = patch[0]
3020 tags[patch[1]] = patch[0]
3021
3021
3022 return result
3022 return result
3023
3023
3024 def _branchtags(self, partial, lrev):
3024 def _branchtags(self, partial, lrev):
3025 q = self.mq
3025 q = self.mq
3026 if not q.applied:
3026 if not q.applied:
3027 return super(mqrepo, self)._branchtags(partial, lrev)
3027 return super(mqrepo, self)._branchtags(partial, lrev)
3028
3028
3029 cl = self.changelog
3029 cl = self.changelog
3030 qbasenode = q.applied[0].node
3030 qbasenode = q.applied[0].node
3031 try:
3031 try:
3032 qbase = cl.rev(qbasenode)
3032 qbase = cl.rev(qbasenode)
3033 except error.LookupError:
3033 except error.LookupError:
3034 self.ui.warn(_('mq status file refers to unknown node %s\n')
3034 self.ui.warn(_('mq status file refers to unknown node %s\n')
3035 % short(qbasenode))
3035 % short(qbasenode))
3036 return super(mqrepo, self)._branchtags(partial, lrev)
3036 return super(mqrepo, self)._branchtags(partial, lrev)
3037
3037
3038 start = lrev + 1
3038 start = lrev + 1
3039 if start < qbase:
3039 if start < qbase:
3040 # update the cache (excluding the patches) and save it
3040 # update the cache (excluding the patches) and save it
3041 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
3041 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
3042 self._updatebranchcache(partial, ctxgen)
3042 self._updatebranchcache(partial, ctxgen)
3043 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
3043 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
3044 start = qbase
3044 start = qbase
3045 # if start = qbase, the cache is as updated as it should be.
3045 # if start = qbase, the cache is as updated as it should be.
3046 # if start > qbase, the cache includes (part of) the patches.
3046 # if start > qbase, the cache includes (part of) the patches.
3047 # we might as well use it, but we won't save it.
3047 # we might as well use it, but we won't save it.
3048
3048
3049 # update the cache up to the tip
3049 # update the cache up to the tip
3050 ctxgen = (self[r] for r in xrange(start, len(cl)))
3050 ctxgen = (self[r] for r in xrange(start, len(cl)))
3051 self._updatebranchcache(partial, ctxgen)
3051 self._updatebranchcache(partial, ctxgen)
3052
3052
3053 return partial
3053 return partial
3054
3054
3055 if repo.local():
3055 if repo.local():
3056 repo.__class__ = mqrepo
3056 repo.__class__ = mqrepo
3057
3057
3058 def mqimport(orig, ui, repo, *args, **kwargs):
3058 def mqimport(orig, ui, repo, *args, **kwargs):
3059 if (hasattr(repo, 'abort_if_wdir_patched')
3059 if (hasattr(repo, 'abort_if_wdir_patched')
3060 and not kwargs.get('no_commit', False)):
3060 and not kwargs.get('no_commit', False)):
3061 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
3061 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
3062 kwargs.get('force'))
3062 kwargs.get('force'))
3063 return orig(ui, repo, *args, **kwargs)
3063 return orig(ui, repo, *args, **kwargs)
3064
3064
3065 def mqinit(orig, ui, *args, **kwargs):
3065 def mqinit(orig, ui, *args, **kwargs):
3066 mq = kwargs.pop('mq', None)
3066 mq = kwargs.pop('mq', None)
3067
3067
3068 if not mq:
3068 if not mq:
3069 return orig(ui, *args, **kwargs)
3069 return orig(ui, *args, **kwargs)
3070
3070
3071 if args:
3071 if args:
3072 repopath = args[0]
3072 repopath = args[0]
3073 if not hg.islocal(repopath):
3073 if not hg.islocal(repopath):
3074 raise util.Abort(_('only a local queue repository '
3074 raise util.Abort(_('only a local queue repository '
3075 'may be initialized'))
3075 'may be initialized'))
3076 else:
3076 else:
3077 repopath = cmdutil.findrepo(os.getcwd())
3077 repopath = cmdutil.findrepo(os.getcwd())
3078 if not repopath:
3078 if not repopath:
3079 raise util.Abort(_('there is no Mercurial repository here '
3079 raise util.Abort(_('there is no Mercurial repository here '
3080 '(.hg not found)'))
3080 '(.hg not found)'))
3081 repo = hg.repository(ui, repopath)
3081 repo = hg.repository(ui, repopath)
3082 return qinit(ui, repo, True)
3082 return qinit(ui, repo, True)
3083
3083
3084 def mqcommand(orig, ui, repo, *args, **kwargs):
3084 def mqcommand(orig, ui, repo, *args, **kwargs):
3085 """Add --mq option to operate on patch repository instead of main"""
3085 """Add --mq option to operate on patch repository instead of main"""
3086
3086
3087 # some commands do not like getting unknown options
3087 # some commands do not like getting unknown options
3088 mq = kwargs.pop('mq', None)
3088 mq = kwargs.pop('mq', None)
3089
3089
3090 if not mq:
3090 if not mq:
3091 return orig(ui, repo, *args, **kwargs)
3091 return orig(ui, repo, *args, **kwargs)
3092
3092
3093 q = repo.mq
3093 q = repo.mq
3094 r = q.qrepo()
3094 r = q.qrepo()
3095 if not r:
3095 if not r:
3096 raise util.Abort(_('no queue repository'))
3096 raise util.Abort(_('no queue repository'))
3097 return orig(r.ui, r, *args, **kwargs)
3097 return orig(r.ui, r, *args, **kwargs)
3098
3098
3099 def summary(orig, ui, repo, *args, **kwargs):
3099 def summary(orig, ui, repo, *args, **kwargs):
3100 r = orig(ui, repo, *args, **kwargs)
3100 r = orig(ui, repo, *args, **kwargs)
3101 q = repo.mq
3101 q = repo.mq
3102 m = []
3102 m = []
3103 a, u = len(q.applied), len(q.unapplied(repo))
3103 a, u = len(q.applied), len(q.unapplied(repo))
3104 if a:
3104 if a:
3105 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3105 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3106 if u:
3106 if u:
3107 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3107 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3108 if m:
3108 if m:
3109 ui.write("mq: %s\n" % ', '.join(m))
3109 ui.write("mq: %s\n" % ', '.join(m))
3110 else:
3110 else:
3111 ui.note(_("mq: (empty queue)\n"))
3111 ui.note(_("mq: (empty queue)\n"))
3112 return r
3112 return r
3113
3113
3114 def revsetmq(repo, subset, x):
3114 def revsetmq(repo, subset, x):
3115 """``mq()``
3115 """``mq()``
3116 Changesets managed by MQ.
3116 Changesets managed by MQ.
3117 """
3117 """
3118 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3118 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3119 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3119 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3120 return [r for r in subset if r in applied]
3120 return [r for r in subset if r in applied]
3121
3121
3122 def extsetup(ui):
3122 def extsetup(ui):
3123 revset.symbols['mq'] = revsetmq
3123 revset.symbols['mq'] = revsetmq
3124
3124
3125 # tell hggettext to extract docstrings from these functions:
3125 # tell hggettext to extract docstrings from these functions:
3126 i18nfunctions = [revsetmq]
3126 i18nfunctions = [revsetmq]
3127
3127
3128 def uisetup(ui):
3128 def uisetup(ui):
3129 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3129 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3130
3130
3131 extensions.wrapcommand(commands.table, 'import', mqimport)
3131 extensions.wrapcommand(commands.table, 'import', mqimport)
3132 extensions.wrapcommand(commands.table, 'summary', summary)
3132 extensions.wrapcommand(commands.table, 'summary', summary)
3133
3133
3134 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3134 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3135 entry[1].extend(mqopt)
3135 entry[1].extend(mqopt)
3136
3136
3137 nowrap = set(commands.norepo.split(" ") + ['qrecord'])
3137 nowrap = set(commands.norepo.split(" ") + ['qrecord'])
3138
3138
3139 def dotable(cmdtable):
3139 def dotable(cmdtable):
3140 for cmd in cmdtable.keys():
3140 for cmd in cmdtable.keys():
3141 cmd = cmdutil.parsealiases(cmd)[0]
3141 cmd = cmdutil.parsealiases(cmd)[0]
3142 if cmd in nowrap:
3142 if cmd in nowrap:
3143 continue
3143 continue
3144 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3144 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3145 entry[1].extend(mqopt)
3145 entry[1].extend(mqopt)
3146
3146
3147 dotable(commands.table)
3147 dotable(commands.table)
3148
3148
3149 for extname, extmodule in extensions.extensions():
3149 for extname, extmodule in extensions.extensions():
3150 if extmodule.__file__ != __file__:
3150 if extmodule.__file__ != __file__:
3151 dotable(getattr(extmodule, 'cmdtable', {}))
3151 dotable(getattr(extmodule, 'cmdtable', {}))
3152
3152
3153 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
3153 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
3154
3154
3155 cmdtable = {
3155 cmdtable = {
3156 "qapplied":
3156 "qapplied":
3157 (applied,
3157 (applied,
3158 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
3158 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
3159 _('hg qapplied [-1] [-s] [PATCH]')),
3159 _('hg qapplied [-1] [-s] [PATCH]')),
3160 "qclone":
3160 "qclone":
3161 (clone,
3161 (clone,
3162 [('', 'pull', None, _('use pull protocol to copy metadata')),
3162 [('', 'pull', None, _('use pull protocol to copy metadata')),
3163 ('U', 'noupdate', None, _('do not update the new working directories')),
3163 ('U', 'noupdate', None, _('do not update the new working directories')),
3164 ('', 'uncompressed', None,
3164 ('', 'uncompressed', None,
3165 _('use uncompressed transfer (fast over LAN)')),
3165 _('use uncompressed transfer (fast over LAN)')),
3166 ('p', 'patches', '',
3166 ('p', 'patches', '',
3167 _('location of source patch repository'), _('REPO')),
3167 _('location of source patch repository'), _('REPO')),
3168 ] + commands.remoteopts,
3168 ] + commands.remoteopts,
3169 _('hg qclone [OPTION]... SOURCE [DEST]')),
3169 _('hg qclone [OPTION]... SOURCE [DEST]')),
3170 "qcommit|qci":
3170 "qcommit|qci":
3171 (commit,
3171 (commit,
3172 commands.table["^commit|ci"][1],
3172 commands.table["^commit|ci"][1],
3173 _('hg qcommit [OPTION]... [FILE]...')),
3173 _('hg qcommit [OPTION]... [FILE]...')),
3174 "^qdiff":
3174 "^qdiff":
3175 (diff,
3175 (diff,
3176 commands.diffopts + commands.diffopts2 + commands.walkopts,
3176 commands.diffopts + commands.diffopts2 + commands.walkopts,
3177 _('hg qdiff [OPTION]... [FILE]...')),
3177 _('hg qdiff [OPTION]... [FILE]...')),
3178 "qdelete|qremove|qrm":
3178 "qdelete|qremove|qrm":
3179 (delete,
3179 (delete,
3180 [('k', 'keep', None, _('keep patch file')),
3180 [('k', 'keep', None, _('keep patch file')),
3181 ('r', 'rev', [],
3181 ('r', 'rev', [],
3182 _('stop managing a revision (DEPRECATED)'), _('REV'))],
3182 _('stop managing a revision (DEPRECATED)'), _('REV'))],
3183 _('hg qdelete [-k] [PATCH]...')),
3183 _('hg qdelete [-k] [PATCH]...')),
3184 'qfold':
3184 'qfold':
3185 (fold,
3185 (fold,
3186 [('e', 'edit', None, _('edit patch header')),
3186 [('e', 'edit', None, _('edit patch header')),
3187 ('k', 'keep', None, _('keep folded patch files')),
3187 ('k', 'keep', None, _('keep folded patch files')),
3188 ] + commands.commitopts,
3188 ] + commands.commitopts,
3189 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
3189 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
3190 'qgoto':
3190 'qgoto':
3191 (goto,
3191 (goto,
3192 [('f', 'force', None, _('overwrite any local changes'))],
3192 [('f', 'force', None, _('overwrite any local changes'))],
3193 _('hg qgoto [OPTION]... PATCH')),
3193 _('hg qgoto [OPTION]... PATCH')),
3194 'qguard':
3194 'qguard':
3195 (guard,
3195 (guard,
3196 [('l', 'list', None, _('list all patches and guards')),
3196 [('l', 'list', None, _('list all patches and guards')),
3197 ('n', 'none', None, _('drop all guards'))],
3197 ('n', 'none', None, _('drop all guards'))],
3198 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
3198 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
3199 'qheader': (header, [], _('hg qheader [PATCH]')),
3199 'qheader': (header, [], _('hg qheader [PATCH]')),
3200 "qimport":
3200 "qimport":
3201 (qimport,
3201 (qimport,
3202 [('e', 'existing', None, _('import file in patch directory')),
3202 [('e', 'existing', None, _('import file in patch directory')),
3203 ('n', 'name', '',
3203 ('n', 'name', '',
3204 _('name of patch file'), _('NAME')),
3204 _('name of patch file'), _('NAME')),
3205 ('f', 'force', None, _('overwrite existing files')),
3205 ('f', 'force', None, _('overwrite existing files')),
3206 ('r', 'rev', [],
3206 ('r', 'rev', [],
3207 _('place existing revisions under mq control'), _('REV')),
3207 _('place existing revisions under mq control'), _('REV')),
3208 ('g', 'git', None, _('use git extended diff format')),
3208 ('g', 'git', None, _('use git extended diff format')),
3209 ('P', 'push', None, _('qpush after importing'))],
3209 ('P', 'push', None, _('qpush after importing'))],
3210 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
3210 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
3211 "^qinit":
3211 "^qinit":
3212 (init,
3212 (init,
3213 [('c', 'create-repo', None, _('create queue repository'))],
3213 [('c', 'create-repo', None, _('create queue repository'))],
3214 _('hg qinit [-c]')),
3214 _('hg qinit [-c]')),
3215 "^qnew":
3215 "^qnew":
3216 (new,
3216 (new,
3217 [('e', 'edit', None, _('edit commit message')),
3217 [('e', 'edit', None, _('edit commit message')),
3218 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
3218 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
3219 ('g', 'git', None, _('use git extended diff format')),
3219 ('g', 'git', None, _('use git extended diff format')),
3220 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
3220 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
3221 ('u', 'user', '',
3221 ('u', 'user', '',
3222 _('add "From: <USER>" to patch'), _('USER')),
3222 _('add "From: <USER>" to patch'), _('USER')),
3223 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
3223 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
3224 ('d', 'date', '',
3224 ('d', 'date', '',
3225 _('add "Date: <DATE>" to patch'), _('DATE'))
3225 _('add "Date: <DATE>" to patch'), _('DATE'))
3226 ] + commands.walkopts + commands.commitopts,
3226 ] + commands.walkopts + commands.commitopts,
3227 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...')),
3227 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...')),
3228 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
3228 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
3229 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
3229 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
3230 "^qpop":
3230 "^qpop":
3231 (pop,
3231 (pop,
3232 [('a', 'all', None, _('pop all patches')),
3232 [('a', 'all', None, _('pop all patches')),
3233 ('n', 'name', '',
3233 ('n', 'name', '',
3234 _('queue name to pop (DEPRECATED)'), _('NAME')),
3234 _('queue name to pop (DEPRECATED)'), _('NAME')),
3235 ('f', 'force', None, _('forget any local changes to patched files'))],
3235 ('f', 'force', None, _('forget any local changes to patched files'))],
3236 _('hg qpop [-a] [-f] [PATCH | INDEX]')),
3236 _('hg qpop [-a] [-f] [PATCH | INDEX]')),
3237 "^qpush":
3237 "^qpush":
3238 (push,
3238 (push,
3239 [('f', 'force', None, _('apply on top of local changes')),
3239 [('f', 'force', None, _('apply on top of local changes')),
3240 ('e', 'exact', None, _('apply the target patch to its recorded parent')),
3240 ('e', 'exact', None, _('apply the target patch to its recorded parent')),
3241 ('l', 'list', None, _('list patch name in commit text')),
3241 ('l', 'list', None, _('list patch name in commit text')),
3242 ('a', 'all', None, _('apply all patches')),
3242 ('a', 'all', None, _('apply all patches')),
3243 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
3243 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
3244 ('n', 'name', '',
3244 ('n', 'name', '',
3245 _('merge queue name (DEPRECATED)'), _('NAME')),
3245 _('merge queue name (DEPRECATED)'), _('NAME')),
3246 ('', 'move', None, _('reorder patch series and apply only the patch'))],
3246 ('', 'move', None, _('reorder patch series and apply only the patch'))],
3247 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]')),
3247 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]')),
3248 "^qrefresh":
3248 "^qrefresh":
3249 (refresh,
3249 (refresh,
3250 [('e', 'edit', None, _('edit commit message')),
3250 [('e', 'edit', None, _('edit commit message')),
3251 ('g', 'git', None, _('use git extended diff format')),
3251 ('g', 'git', None, _('use git extended diff format')),
3252 ('s', 'short', None,
3252 ('s', 'short', None,
3253 _('refresh only files already in the patch and specified files')),
3253 _('refresh only files already in the patch and specified files')),
3254 ('U', 'currentuser', None,
3254 ('U', 'currentuser', None,
3255 _('add/update author field in patch with current user')),
3255 _('add/update author field in patch with current user')),
3256 ('u', 'user', '',
3256 ('u', 'user', '',
3257 _('add/update author field in patch with given user'), _('USER')),
3257 _('add/update author field in patch with given user'), _('USER')),
3258 ('D', 'currentdate', None,
3258 ('D', 'currentdate', None,
3259 _('add/update date field in patch with current date')),
3259 _('add/update date field in patch with current date')),
3260 ('d', 'date', '',
3260 ('d', 'date', '',
3261 _('add/update date field in patch with given date'), _('DATE'))
3261 _('add/update date field in patch with given date'), _('DATE'))
3262 ] + commands.walkopts + commands.commitopts,
3262 ] + commands.walkopts + commands.commitopts,
3263 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
3263 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
3264 'qrename|qmv':
3264 'qrename|qmv':
3265 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
3265 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
3266 "qrestore":
3266 "qrestore":
3267 (restore,
3267 (restore,
3268 [('d', 'delete', None, _('delete save entry')),
3268 [('d', 'delete', None, _('delete save entry')),
3269 ('u', 'update', None, _('update queue working directory'))],
3269 ('u', 'update', None, _('update queue working directory'))],
3270 _('hg qrestore [-d] [-u] REV')),
3270 _('hg qrestore [-d] [-u] REV')),
3271 "qsave":
3271 "qsave":
3272 (save,
3272 (save,
3273 [('c', 'copy', None, _('copy patch directory')),
3273 [('c', 'copy', None, _('copy patch directory')),
3274 ('n', 'name', '',
3274 ('n', 'name', '',
3275 _('copy directory name'), _('NAME')),
3275 _('copy directory name'), _('NAME')),
3276 ('e', 'empty', None, _('clear queue status file')),
3276 ('e', 'empty', None, _('clear queue status file')),
3277 ('f', 'force', None, _('force copy'))] + commands.commitopts,
3277 ('f', 'force', None, _('force copy'))] + commands.commitopts,
3278 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
3278 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
3279 "qselect":
3279 "qselect":
3280 (select,
3280 (select,
3281 [('n', 'none', None, _('disable all guards')),
3281 [('n', 'none', None, _('disable all guards')),
3282 ('s', 'series', None, _('list all guards in series file')),
3282 ('s', 'series', None, _('list all guards in series file')),
3283 ('', 'pop', None, _('pop to before first guarded applied patch')),
3283 ('', 'pop', None, _('pop to before first guarded applied patch')),
3284 ('', 'reapply', None, _('pop, then reapply patches'))],
3284 ('', 'reapply', None, _('pop, then reapply patches'))],
3285 _('hg qselect [OPTION]... [GUARD]...')),
3285 _('hg qselect [OPTION]... [GUARD]...')),
3286 "qseries":
3286 "qseries":
3287 (series,
3287 (series,
3288 [('m', 'missing', None, _('print patches not in series')),
3288 [('m', 'missing', None, _('print patches not in series')),
3289 ] + seriesopts,
3289 ] + seriesopts,
3290 _('hg qseries [-ms]')),
3290 _('hg qseries [-ms]')),
3291 "strip":
3291 "strip":
3292 (strip,
3292 (strip,
3293 [('f', 'force', None, _('force removal of changesets, discard '
3293 [('f', 'force', None, _('force removal of changesets, discard '
3294 'uncommitted changes (no backup)')),
3294 'uncommitted changes (no backup)')),
3295 ('b', 'backup', None, _('bundle only changesets with local revision'
3295 ('b', 'backup', None, _('bundle only changesets with local revision'
3296 ' number greater than REV which are not'
3296 ' number greater than REV which are not'
3297 ' descendants of REV (DEPRECATED)')),
3297 ' descendants of REV (DEPRECATED)')),
3298 ('n', 'no-backup', None, _('no backups')),
3298 ('n', 'no-backup', None, _('no backups')),
3299 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
3299 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
3300 ('k', 'keep', None, _("do not modify working copy during strip"))],
3300 ('k', 'keep', None, _("do not modify working copy during strip"))],
3301 _('hg strip [-k] [-f] [-n] REV...')),
3301 _('hg strip [-k] [-f] [-n] REV...')),
3302 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
3302 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
3303 "qunapplied":
3303 "qunapplied":
3304 (unapplied,
3304 (unapplied,
3305 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
3305 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
3306 _('hg qunapplied [-1] [-s] [PATCH]')),
3306 _('hg qunapplied [-1] [-s] [PATCH]')),
3307 "qfinish":
3307 "qfinish":
3308 (finish,
3308 (finish,
3309 [('a', 'applied', None, _('finish all applied changesets'))],
3309 [('a', 'applied', None, _('finish all applied changesets'))],
3310 _('hg qfinish [-a] [REV]...')),
3310 _('hg qfinish [-a] [REV]...')),
3311 'qqueue':
3311 'qqueue':
3312 (qqueue,
3312 (qqueue,
3313 [
3313 [
3314 ('l', 'list', False, _('list all available queues')),
3314 ('l', 'list', False, _('list all available queues')),
3315 ('c', 'create', False, _('create new queue')),
3315 ('c', 'create', False, _('create new queue')),
3316 ('', 'rename', False, _('rename active queue')),
3316 ('', 'rename', False, _('rename active queue')),
3317 ('', 'delete', False, _('delete reference to queue')),
3317 ('', 'delete', False, _('delete reference to queue')),
3318 ('', 'purge', False, _('delete queue, and remove patch dir')),
3318 ('', 'purge', False, _('delete queue, and remove patch dir')),
3319 ],
3319 ],
3320 _('[OPTION] [QUEUE]')),
3320 _('[OPTION] [QUEUE]')),
3321 }
3321 }
3322
3322
3323 colortable = {'qguard.negative': 'red',
3323 colortable = {'qguard.negative': 'red',
3324 'qguard.positive': 'yellow',
3324 'qguard.positive': 'yellow',
3325 'qguard.unguarded': 'green',
3325 'qguard.unguarded': 'green',
3326 'qseries.applied': 'blue bold underline',
3326 'qseries.applied': 'blue bold underline',
3327 'qseries.guarded': 'black bold',
3327 'qseries.guarded': 'black bold',
3328 'qseries.missing': 'red bold',
3328 'qseries.missing': 'red bold',
3329 'qseries.unapplied': 'black bold'}
3329 'qseries.unapplied': 'black bold'}
General Comments 0
You need to be logged in to leave comments. Login now