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