##// END OF EJS Templates
dispatch: stop supporting non-use of @command...
Augie Fackler -
r30485:acd30a95 default
parent child Browse files
Show More
@@ -1,3608 +1,3607 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 behavior can be configured with::
31 files creations or deletions. This behavior can be configured with::
32
32
33 [mq]
33 [mq]
34 git = auto/keep/yes/no
34 git = auto/keep/yes/no
35
35
36 If set to 'keep', mq will obey the [diff] section configuration while
36 If set to 'keep', mq will obey the [diff] section configuration while
37 preserving existing git patches upon qrefresh. If set to 'yes' or
37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 'no', mq will override the [diff] section and always generate git or
38 'no', mq will override the [diff] section and always generate git or
39 regular patches, possibly losing data in the second case.
39 regular patches, possibly losing data in the second case.
40
40
41 It may be desirable for mq changesets to be kept in the secret phase (see
41 It may be desirable for mq changesets to be kept in the secret phase (see
42 :hg:`help phases`), which can be enabled with the following setting::
42 :hg:`help phases`), which can be enabled with the following setting::
43
43
44 [mq]
44 [mq]
45 secret = True
45 secret = True
46
46
47 You will by default be managing a patch queue named "patches". You can
47 You will by default be managing a patch queue named "patches". You can
48 create other, independent patch queues with the :hg:`qqueue` command.
48 create other, independent patch queues with the :hg:`qqueue` command.
49
49
50 If the working directory contains uncommitted files, qpush, qpop and
50 If the working directory contains uncommitted files, qpush, qpop and
51 qgoto abort immediately. If -f/--force is used, the changes are
51 qgoto abort immediately. If -f/--force is used, the changes are
52 discarded. Setting::
52 discarded. Setting::
53
53
54 [mq]
54 [mq]
55 keepchanges = True
55 keepchanges = True
56
56
57 make them behave as if --keep-changes were passed, and non-conflicting
57 make them behave as if --keep-changes were passed, and non-conflicting
58 local changes will be tolerated and preserved. If incompatible options
58 local changes will be tolerated and preserved. If incompatible options
59 such as -f/--force or --exact are passed, this setting is ignored.
59 such as -f/--force or --exact are passed, this setting is ignored.
60
60
61 This extension used to provide a strip command. This command now lives
61 This extension used to provide a strip command. This command now lives
62 in the strip extension.
62 in the strip extension.
63 '''
63 '''
64
64
65 from __future__ import absolute_import
65 from __future__ import absolute_import
66
66
67 import errno
67 import errno
68 import os
68 import os
69 import re
69 import re
70 import shutil
70 import shutil
71 from mercurial.i18n import _
71 from mercurial.i18n import _
72 from mercurial.node import (
72 from mercurial.node import (
73 bin,
73 bin,
74 hex,
74 hex,
75 nullid,
75 nullid,
76 nullrev,
76 nullrev,
77 short,
77 short,
78 )
78 )
79 from mercurial import (
79 from mercurial import (
80 cmdutil,
80 cmdutil,
81 commands,
81 commands,
82 dispatch,
83 error,
82 error,
84 extensions,
83 extensions,
85 hg,
84 hg,
86 localrepo,
85 localrepo,
87 lock as lockmod,
86 lock as lockmod,
88 patch as patchmod,
87 patch as patchmod,
89 phases,
88 phases,
90 registrar,
89 registrar,
91 revset,
90 revset,
92 scmutil,
91 scmutil,
93 subrepo,
92 subrepo,
94 util,
93 util,
95 )
94 )
96
95
97 release = lockmod.release
96 release = lockmod.release
98 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
97 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
99
98
100 cmdtable = {}
99 cmdtable = {}
101 command = cmdutil.command(cmdtable)
100 command = cmdutil.command(cmdtable)
102 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
101 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
103 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
102 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
104 # be specifying the version(s) of Mercurial they are tested with, or
103 # be specifying the version(s) of Mercurial they are tested with, or
105 # leave the attribute unspecified.
104 # leave the attribute unspecified.
106 testedwith = 'ships-with-hg-core'
105 testedwith = 'ships-with-hg-core'
107
106
108 # force load strip extension formerly included in mq and import some utility
107 # force load strip extension formerly included in mq and import some utility
109 try:
108 try:
110 stripext = extensions.find('strip')
109 stripext = extensions.find('strip')
111 except KeyError:
110 except KeyError:
112 # note: load is lazy so we could avoid the try-except,
111 # note: load is lazy so we could avoid the try-except,
113 # but I (marmoute) prefer this explicit code.
112 # but I (marmoute) prefer this explicit code.
114 class dummyui(object):
113 class dummyui(object):
115 def debug(self, msg):
114 def debug(self, msg):
116 pass
115 pass
117 stripext = extensions.load(dummyui(), 'strip', '')
116 stripext = extensions.load(dummyui(), 'strip', '')
118
117
119 strip = stripext.strip
118 strip = stripext.strip
120 checksubstate = stripext.checksubstate
119 checksubstate = stripext.checksubstate
121 checklocalchanges = stripext.checklocalchanges
120 checklocalchanges = stripext.checklocalchanges
122
121
123
122
124 # Patch names looks like unix-file names.
123 # Patch names looks like unix-file names.
125 # They must be joinable with queue directory and result in the patch path.
124 # They must be joinable with queue directory and result in the patch path.
126 normname = util.normpath
125 normname = util.normpath
127
126
128 class statusentry(object):
127 class statusentry(object):
129 def __init__(self, node, name):
128 def __init__(self, node, name):
130 self.node, self.name = node, name
129 self.node, self.name = node, name
131 def __repr__(self):
130 def __repr__(self):
132 return hex(self.node) + ':' + self.name
131 return hex(self.node) + ':' + self.name
133
132
134 # The order of the headers in 'hg export' HG patches:
133 # The order of the headers in 'hg export' HG patches:
135 HGHEADERS = [
134 HGHEADERS = [
136 # '# HG changeset patch',
135 # '# HG changeset patch',
137 '# User ',
136 '# User ',
138 '# Date ',
137 '# Date ',
139 '# ',
138 '# ',
140 '# Branch ',
139 '# Branch ',
141 '# Node ID ',
140 '# Node ID ',
142 '# Parent ', # can occur twice for merges - but that is not relevant for mq
141 '# Parent ', # can occur twice for merges - but that is not relevant for mq
143 ]
142 ]
144 # The order of headers in plain 'mail style' patches:
143 # The order of headers in plain 'mail style' patches:
145 PLAINHEADERS = {
144 PLAINHEADERS = {
146 'from': 0,
145 'from': 0,
147 'date': 1,
146 'date': 1,
148 'subject': 2,
147 'subject': 2,
149 }
148 }
150
149
151 def inserthgheader(lines, header, value):
150 def inserthgheader(lines, header, value):
152 """Assuming lines contains a HG patch header, add a header line with value.
151 """Assuming lines contains a HG patch header, add a header line with value.
153 >>> try: inserthgheader([], '# Date ', 'z')
152 >>> try: inserthgheader([], '# Date ', 'z')
154 ... except ValueError, inst: print "oops"
153 ... except ValueError, inst: print "oops"
155 oops
154 oops
156 >>> inserthgheader(['# HG changeset patch'], '# Date ', 'z')
155 >>> inserthgheader(['# HG changeset patch'], '# Date ', 'z')
157 ['# HG changeset patch', '# Date z']
156 ['# HG changeset patch', '# Date z']
158 >>> inserthgheader(['# HG changeset patch', ''], '# Date ', 'z')
157 >>> inserthgheader(['# HG changeset patch', ''], '# Date ', 'z')
159 ['# HG changeset patch', '# Date z', '']
158 ['# HG changeset patch', '# Date z', '']
160 >>> inserthgheader(['# HG changeset patch', '# User y'], '# Date ', 'z')
159 >>> inserthgheader(['# HG changeset patch', '# User y'], '# Date ', 'z')
161 ['# HG changeset patch', '# User y', '# Date z']
160 ['# HG changeset patch', '# User y', '# Date z']
162 >>> inserthgheader(['# HG changeset patch', '# Date x', '# User y'],
161 >>> inserthgheader(['# HG changeset patch', '# Date x', '# User y'],
163 ... '# User ', 'z')
162 ... '# User ', 'z')
164 ['# HG changeset patch', '# Date x', '# User z']
163 ['# HG changeset patch', '# Date x', '# User z']
165 >>> inserthgheader(['# HG changeset patch', '# Date y'], '# Date ', 'z')
164 >>> inserthgheader(['# HG changeset patch', '# Date y'], '# Date ', 'z')
166 ['# HG changeset patch', '# Date z']
165 ['# HG changeset patch', '# Date z']
167 >>> inserthgheader(['# HG changeset patch', '', '# Date y'], '# Date ', 'z')
166 >>> inserthgheader(['# HG changeset patch', '', '# Date y'], '# Date ', 'z')
168 ['# HG changeset patch', '# Date z', '', '# Date y']
167 ['# HG changeset patch', '# Date z', '', '# Date y']
169 >>> inserthgheader(['# HG changeset patch', '# Parent y'], '# Date ', 'z')
168 >>> inserthgheader(['# HG changeset patch', '# Parent y'], '# Date ', 'z')
170 ['# HG changeset patch', '# Date z', '# Parent y']
169 ['# HG changeset patch', '# Date z', '# Parent y']
171 """
170 """
172 start = lines.index('# HG changeset patch') + 1
171 start = lines.index('# HG changeset patch') + 1
173 newindex = HGHEADERS.index(header)
172 newindex = HGHEADERS.index(header)
174 bestpos = len(lines)
173 bestpos = len(lines)
175 for i in range(start, len(lines)):
174 for i in range(start, len(lines)):
176 line = lines[i]
175 line = lines[i]
177 if not line.startswith('# '):
176 if not line.startswith('# '):
178 bestpos = min(bestpos, i)
177 bestpos = min(bestpos, i)
179 break
178 break
180 for lineindex, h in enumerate(HGHEADERS):
179 for lineindex, h in enumerate(HGHEADERS):
181 if line.startswith(h):
180 if line.startswith(h):
182 if lineindex == newindex:
181 if lineindex == newindex:
183 lines[i] = header + value
182 lines[i] = header + value
184 return lines
183 return lines
185 if lineindex > newindex:
184 if lineindex > newindex:
186 bestpos = min(bestpos, i)
185 bestpos = min(bestpos, i)
187 break # next line
186 break # next line
188 lines.insert(bestpos, header + value)
187 lines.insert(bestpos, header + value)
189 return lines
188 return lines
190
189
191 def insertplainheader(lines, header, value):
190 def insertplainheader(lines, header, value):
192 """For lines containing a plain patch header, add a header line with value.
191 """For lines containing a plain patch header, add a header line with value.
193 >>> insertplainheader([], 'Date', 'z')
192 >>> insertplainheader([], 'Date', 'z')
194 ['Date: z']
193 ['Date: z']
195 >>> insertplainheader([''], 'Date', 'z')
194 >>> insertplainheader([''], 'Date', 'z')
196 ['Date: z', '']
195 ['Date: z', '']
197 >>> insertplainheader(['x'], 'Date', 'z')
196 >>> insertplainheader(['x'], 'Date', 'z')
198 ['Date: z', '', 'x']
197 ['Date: z', '', 'x']
199 >>> insertplainheader(['From: y', 'x'], 'Date', 'z')
198 >>> insertplainheader(['From: y', 'x'], 'Date', 'z')
200 ['From: y', 'Date: z', '', 'x']
199 ['From: y', 'Date: z', '', 'x']
201 >>> insertplainheader([' date : x', ' from : y', ''], 'From', 'z')
200 >>> insertplainheader([' date : x', ' from : y', ''], 'From', 'z')
202 [' date : x', 'From: z', '']
201 [' date : x', 'From: z', '']
203 >>> insertplainheader(['', 'Date: y'], 'Date', 'z')
202 >>> insertplainheader(['', 'Date: y'], 'Date', 'z')
204 ['Date: z', '', 'Date: y']
203 ['Date: z', '', 'Date: y']
205 >>> insertplainheader(['foo: bar', 'DATE: z', 'x'], 'From', 'y')
204 >>> insertplainheader(['foo: bar', 'DATE: z', 'x'], 'From', 'y')
206 ['From: y', 'foo: bar', 'DATE: z', '', 'x']
205 ['From: y', 'foo: bar', 'DATE: z', '', 'x']
207 """
206 """
208 newprio = PLAINHEADERS[header.lower()]
207 newprio = PLAINHEADERS[header.lower()]
209 bestpos = len(lines)
208 bestpos = len(lines)
210 for i, line in enumerate(lines):
209 for i, line in enumerate(lines):
211 if ':' in line:
210 if ':' in line:
212 lheader = line.split(':', 1)[0].strip().lower()
211 lheader = line.split(':', 1)[0].strip().lower()
213 lprio = PLAINHEADERS.get(lheader, newprio + 1)
212 lprio = PLAINHEADERS.get(lheader, newprio + 1)
214 if lprio == newprio:
213 if lprio == newprio:
215 lines[i] = '%s: %s' % (header, value)
214 lines[i] = '%s: %s' % (header, value)
216 return lines
215 return lines
217 if lprio > newprio and i < bestpos:
216 if lprio > newprio and i < bestpos:
218 bestpos = i
217 bestpos = i
219 else:
218 else:
220 if line:
219 if line:
221 lines.insert(i, '')
220 lines.insert(i, '')
222 if i < bestpos:
221 if i < bestpos:
223 bestpos = i
222 bestpos = i
224 break
223 break
225 lines.insert(bestpos, '%s: %s' % (header, value))
224 lines.insert(bestpos, '%s: %s' % (header, value))
226 return lines
225 return lines
227
226
228 class patchheader(object):
227 class patchheader(object):
229 def __init__(self, pf, plainmode=False):
228 def __init__(self, pf, plainmode=False):
230 def eatdiff(lines):
229 def eatdiff(lines):
231 while lines:
230 while lines:
232 l = lines[-1]
231 l = lines[-1]
233 if (l.startswith("diff -") or
232 if (l.startswith("diff -") or
234 l.startswith("Index:") or
233 l.startswith("Index:") or
235 l.startswith("===========")):
234 l.startswith("===========")):
236 del lines[-1]
235 del lines[-1]
237 else:
236 else:
238 break
237 break
239 def eatempty(lines):
238 def eatempty(lines):
240 while lines:
239 while lines:
241 if not lines[-1].strip():
240 if not lines[-1].strip():
242 del lines[-1]
241 del lines[-1]
243 else:
242 else:
244 break
243 break
245
244
246 message = []
245 message = []
247 comments = []
246 comments = []
248 user = None
247 user = None
249 date = None
248 date = None
250 parent = None
249 parent = None
251 format = None
250 format = None
252 subject = None
251 subject = None
253 branch = None
252 branch = None
254 nodeid = None
253 nodeid = None
255 diffstart = 0
254 diffstart = 0
256
255
257 for line in file(pf):
256 for line in file(pf):
258 line = line.rstrip()
257 line = line.rstrip()
259 if (line.startswith('diff --git')
258 if (line.startswith('diff --git')
260 or (diffstart and line.startswith('+++ '))):
259 or (diffstart and line.startswith('+++ '))):
261 diffstart = 2
260 diffstart = 2
262 break
261 break
263 diffstart = 0 # reset
262 diffstart = 0 # reset
264 if line.startswith("--- "):
263 if line.startswith("--- "):
265 diffstart = 1
264 diffstart = 1
266 continue
265 continue
267 elif format == "hgpatch":
266 elif format == "hgpatch":
268 # parse values when importing the result of an hg export
267 # parse values when importing the result of an hg export
269 if line.startswith("# User "):
268 if line.startswith("# User "):
270 user = line[7:]
269 user = line[7:]
271 elif line.startswith("# Date "):
270 elif line.startswith("# Date "):
272 date = line[7:]
271 date = line[7:]
273 elif line.startswith("# Parent "):
272 elif line.startswith("# Parent "):
274 parent = line[9:].lstrip() # handle double trailing space
273 parent = line[9:].lstrip() # handle double trailing space
275 elif line.startswith("# Branch "):
274 elif line.startswith("# Branch "):
276 branch = line[9:]
275 branch = line[9:]
277 elif line.startswith("# Node ID "):
276 elif line.startswith("# Node ID "):
278 nodeid = line[10:]
277 nodeid = line[10:]
279 elif not line.startswith("# ") and line:
278 elif not line.startswith("# ") and line:
280 message.append(line)
279 message.append(line)
281 format = None
280 format = None
282 elif line == '# HG changeset patch':
281 elif line == '# HG changeset patch':
283 message = []
282 message = []
284 format = "hgpatch"
283 format = "hgpatch"
285 elif (format != "tagdone" and (line.startswith("Subject: ") or
284 elif (format != "tagdone" and (line.startswith("Subject: ") or
286 line.startswith("subject: "))):
285 line.startswith("subject: "))):
287 subject = line[9:]
286 subject = line[9:]
288 format = "tag"
287 format = "tag"
289 elif (format != "tagdone" and (line.startswith("From: ") or
288 elif (format != "tagdone" and (line.startswith("From: ") or
290 line.startswith("from: "))):
289 line.startswith("from: "))):
291 user = line[6:]
290 user = line[6:]
292 format = "tag"
291 format = "tag"
293 elif (format != "tagdone" and (line.startswith("Date: ") or
292 elif (format != "tagdone" and (line.startswith("Date: ") or
294 line.startswith("date: "))):
293 line.startswith("date: "))):
295 date = line[6:]
294 date = line[6:]
296 format = "tag"
295 format = "tag"
297 elif format == "tag" and line == "":
296 elif format == "tag" and line == "":
298 # when looking for tags (subject: from: etc) they
297 # when looking for tags (subject: from: etc) they
299 # end once you find a blank line in the source
298 # end once you find a blank line in the source
300 format = "tagdone"
299 format = "tagdone"
301 elif message or line:
300 elif message or line:
302 message.append(line)
301 message.append(line)
303 comments.append(line)
302 comments.append(line)
304
303
305 eatdiff(message)
304 eatdiff(message)
306 eatdiff(comments)
305 eatdiff(comments)
307 # Remember the exact starting line of the patch diffs before consuming
306 # Remember the exact starting line of the patch diffs before consuming
308 # empty lines, for external use by TortoiseHg and others
307 # empty lines, for external use by TortoiseHg and others
309 self.diffstartline = len(comments)
308 self.diffstartline = len(comments)
310 eatempty(message)
309 eatempty(message)
311 eatempty(comments)
310 eatempty(comments)
312
311
313 # make sure message isn't empty
312 # make sure message isn't empty
314 if format and format.startswith("tag") and subject:
313 if format and format.startswith("tag") and subject:
315 message.insert(0, subject)
314 message.insert(0, subject)
316
315
317 self.message = message
316 self.message = message
318 self.comments = comments
317 self.comments = comments
319 self.user = user
318 self.user = user
320 self.date = date
319 self.date = date
321 self.parent = parent
320 self.parent = parent
322 # nodeid and branch are for external use by TortoiseHg and others
321 # nodeid and branch are for external use by TortoiseHg and others
323 self.nodeid = nodeid
322 self.nodeid = nodeid
324 self.branch = branch
323 self.branch = branch
325 self.haspatch = diffstart > 1
324 self.haspatch = diffstart > 1
326 self.plainmode = (plainmode or
325 self.plainmode = (plainmode or
327 '# HG changeset patch' not in self.comments and
326 '# HG changeset patch' not in self.comments and
328 any(c.startswith('Date: ') or
327 any(c.startswith('Date: ') or
329 c.startswith('From: ')
328 c.startswith('From: ')
330 for c in self.comments))
329 for c in self.comments))
331
330
332 def setuser(self, user):
331 def setuser(self, user):
333 try:
332 try:
334 inserthgheader(self.comments, '# User ', user)
333 inserthgheader(self.comments, '# User ', user)
335 except ValueError:
334 except ValueError:
336 if self.plainmode:
335 if self.plainmode:
337 insertplainheader(self.comments, 'From', user)
336 insertplainheader(self.comments, 'From', user)
338 else:
337 else:
339 tmp = ['# HG changeset patch', '# User ' + user]
338 tmp = ['# HG changeset patch', '# User ' + user]
340 self.comments = tmp + self.comments
339 self.comments = tmp + self.comments
341 self.user = user
340 self.user = user
342
341
343 def setdate(self, date):
342 def setdate(self, date):
344 try:
343 try:
345 inserthgheader(self.comments, '# Date ', date)
344 inserthgheader(self.comments, '# Date ', date)
346 except ValueError:
345 except ValueError:
347 if self.plainmode:
346 if self.plainmode:
348 insertplainheader(self.comments, 'Date', date)
347 insertplainheader(self.comments, 'Date', date)
349 else:
348 else:
350 tmp = ['# HG changeset patch', '# Date ' + date]
349 tmp = ['# HG changeset patch', '# Date ' + date]
351 self.comments = tmp + self.comments
350 self.comments = tmp + self.comments
352 self.date = date
351 self.date = date
353
352
354 def setparent(self, parent):
353 def setparent(self, parent):
355 try:
354 try:
356 inserthgheader(self.comments, '# Parent ', parent)
355 inserthgheader(self.comments, '# Parent ', parent)
357 except ValueError:
356 except ValueError:
358 if not self.plainmode:
357 if not self.plainmode:
359 tmp = ['# HG changeset patch', '# Parent ' + parent]
358 tmp = ['# HG changeset patch', '# Parent ' + parent]
360 self.comments = tmp + self.comments
359 self.comments = tmp + self.comments
361 self.parent = parent
360 self.parent = parent
362
361
363 def setmessage(self, message):
362 def setmessage(self, message):
364 if self.comments:
363 if self.comments:
365 self._delmsg()
364 self._delmsg()
366 self.message = [message]
365 self.message = [message]
367 if message:
366 if message:
368 if self.plainmode and self.comments and self.comments[-1]:
367 if self.plainmode and self.comments and self.comments[-1]:
369 self.comments.append('')
368 self.comments.append('')
370 self.comments.append(message)
369 self.comments.append(message)
371
370
372 def __str__(self):
371 def __str__(self):
373 s = '\n'.join(self.comments).rstrip()
372 s = '\n'.join(self.comments).rstrip()
374 if not s:
373 if not s:
375 return ''
374 return ''
376 return s + '\n\n'
375 return s + '\n\n'
377
376
378 def _delmsg(self):
377 def _delmsg(self):
379 '''Remove existing message, keeping the rest of the comments fields.
378 '''Remove existing message, keeping the rest of the comments fields.
380 If comments contains 'subject: ', message will prepend
379 If comments contains 'subject: ', message will prepend
381 the field and a blank line.'''
380 the field and a blank line.'''
382 if self.message:
381 if self.message:
383 subj = 'subject: ' + self.message[0].lower()
382 subj = 'subject: ' + self.message[0].lower()
384 for i in xrange(len(self.comments)):
383 for i in xrange(len(self.comments)):
385 if subj == self.comments[i].lower():
384 if subj == self.comments[i].lower():
386 del self.comments[i]
385 del self.comments[i]
387 self.message = self.message[2:]
386 self.message = self.message[2:]
388 break
387 break
389 ci = 0
388 ci = 0
390 for mi in self.message:
389 for mi in self.message:
391 while mi != self.comments[ci]:
390 while mi != self.comments[ci]:
392 ci += 1
391 ci += 1
393 del self.comments[ci]
392 del self.comments[ci]
394
393
395 def newcommit(repo, phase, *args, **kwargs):
394 def newcommit(repo, phase, *args, **kwargs):
396 """helper dedicated to ensure a commit respect mq.secret setting
395 """helper dedicated to ensure a commit respect mq.secret setting
397
396
398 It should be used instead of repo.commit inside the mq source for operation
397 It should be used instead of repo.commit inside the mq source for operation
399 creating new changeset.
398 creating new changeset.
400 """
399 """
401 repo = repo.unfiltered()
400 repo = repo.unfiltered()
402 if phase is None:
401 if phase is None:
403 if repo.ui.configbool('mq', 'secret', False):
402 if repo.ui.configbool('mq', 'secret', False):
404 phase = phases.secret
403 phase = phases.secret
405 if phase is not None:
404 if phase is not None:
406 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
405 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
407 allowemptybackup = repo.ui.backupconfig('ui', 'allowemptycommit')
406 allowemptybackup = repo.ui.backupconfig('ui', 'allowemptycommit')
408 try:
407 try:
409 if phase is not None:
408 if phase is not None:
410 repo.ui.setconfig('phases', 'new-commit', phase, 'mq')
409 repo.ui.setconfig('phases', 'new-commit', phase, 'mq')
411 repo.ui.setconfig('ui', 'allowemptycommit', True)
410 repo.ui.setconfig('ui', 'allowemptycommit', True)
412 return repo.commit(*args, **kwargs)
411 return repo.commit(*args, **kwargs)
413 finally:
412 finally:
414 repo.ui.restoreconfig(allowemptybackup)
413 repo.ui.restoreconfig(allowemptybackup)
415 if phase is not None:
414 if phase is not None:
416 repo.ui.restoreconfig(phasebackup)
415 repo.ui.restoreconfig(phasebackup)
417
416
418 class AbortNoCleanup(error.Abort):
417 class AbortNoCleanup(error.Abort):
419 pass
418 pass
420
419
421 class queue(object):
420 class queue(object):
422 def __init__(self, ui, baseui, path, patchdir=None):
421 def __init__(self, ui, baseui, path, patchdir=None):
423 self.basepath = path
422 self.basepath = path
424 try:
423 try:
425 fh = open(os.path.join(path, 'patches.queue'))
424 fh = open(os.path.join(path, 'patches.queue'))
426 cur = fh.read().rstrip()
425 cur = fh.read().rstrip()
427 fh.close()
426 fh.close()
428 if not cur:
427 if not cur:
429 curpath = os.path.join(path, 'patches')
428 curpath = os.path.join(path, 'patches')
430 else:
429 else:
431 curpath = os.path.join(path, 'patches-' + cur)
430 curpath = os.path.join(path, 'patches-' + cur)
432 except IOError:
431 except IOError:
433 curpath = os.path.join(path, 'patches')
432 curpath = os.path.join(path, 'patches')
434 self.path = patchdir or curpath
433 self.path = patchdir or curpath
435 self.opener = scmutil.opener(self.path)
434 self.opener = scmutil.opener(self.path)
436 self.ui = ui
435 self.ui = ui
437 self.baseui = baseui
436 self.baseui = baseui
438 self.applieddirty = False
437 self.applieddirty = False
439 self.seriesdirty = False
438 self.seriesdirty = False
440 self.added = []
439 self.added = []
441 self.seriespath = "series"
440 self.seriespath = "series"
442 self.statuspath = "status"
441 self.statuspath = "status"
443 self.guardspath = "guards"
442 self.guardspath = "guards"
444 self.activeguards = None
443 self.activeguards = None
445 self.guardsdirty = False
444 self.guardsdirty = False
446 # Handle mq.git as a bool with extended values
445 # Handle mq.git as a bool with extended values
447 try:
446 try:
448 gitmode = ui.configbool('mq', 'git', None)
447 gitmode = ui.configbool('mq', 'git', None)
449 if gitmode is None:
448 if gitmode is None:
450 raise error.ConfigError
449 raise error.ConfigError
451 if gitmode:
450 if gitmode:
452 self.gitmode = 'yes'
451 self.gitmode = 'yes'
453 else:
452 else:
454 self.gitmode = 'no'
453 self.gitmode = 'no'
455 except error.ConfigError:
454 except error.ConfigError:
456 # let's have check-config ignore the type mismatch
455 # let's have check-config ignore the type mismatch
457 self.gitmode = ui.config(r'mq', 'git', 'auto').lower()
456 self.gitmode = ui.config(r'mq', 'git', 'auto').lower()
458 # deprecated config: mq.plain
457 # deprecated config: mq.plain
459 self.plainmode = ui.configbool('mq', 'plain', False)
458 self.plainmode = ui.configbool('mq', 'plain', False)
460 self.checkapplied = True
459 self.checkapplied = True
461
460
462 @util.propertycache
461 @util.propertycache
463 def applied(self):
462 def applied(self):
464 def parselines(lines):
463 def parselines(lines):
465 for l in lines:
464 for l in lines:
466 entry = l.split(':', 1)
465 entry = l.split(':', 1)
467 if len(entry) > 1:
466 if len(entry) > 1:
468 n, name = entry
467 n, name = entry
469 yield statusentry(bin(n), name)
468 yield statusentry(bin(n), name)
470 elif l.strip():
469 elif l.strip():
471 self.ui.warn(_('malformated mq status line: %s\n') % entry)
470 self.ui.warn(_('malformated mq status line: %s\n') % entry)
472 # else we ignore empty lines
471 # else we ignore empty lines
473 try:
472 try:
474 lines = self.opener.read(self.statuspath).splitlines()
473 lines = self.opener.read(self.statuspath).splitlines()
475 return list(parselines(lines))
474 return list(parselines(lines))
476 except IOError as e:
475 except IOError as e:
477 if e.errno == errno.ENOENT:
476 if e.errno == errno.ENOENT:
478 return []
477 return []
479 raise
478 raise
480
479
481 @util.propertycache
480 @util.propertycache
482 def fullseries(self):
481 def fullseries(self):
483 try:
482 try:
484 return self.opener.read(self.seriespath).splitlines()
483 return self.opener.read(self.seriespath).splitlines()
485 except IOError as e:
484 except IOError as e:
486 if e.errno == errno.ENOENT:
485 if e.errno == errno.ENOENT:
487 return []
486 return []
488 raise
487 raise
489
488
490 @util.propertycache
489 @util.propertycache
491 def series(self):
490 def series(self):
492 self.parseseries()
491 self.parseseries()
493 return self.series
492 return self.series
494
493
495 @util.propertycache
494 @util.propertycache
496 def seriesguards(self):
495 def seriesguards(self):
497 self.parseseries()
496 self.parseseries()
498 return self.seriesguards
497 return self.seriesguards
499
498
500 def invalidate(self):
499 def invalidate(self):
501 for a in 'applied fullseries series seriesguards'.split():
500 for a in 'applied fullseries series seriesguards'.split():
502 if a in self.__dict__:
501 if a in self.__dict__:
503 delattr(self, a)
502 delattr(self, a)
504 self.applieddirty = False
503 self.applieddirty = False
505 self.seriesdirty = False
504 self.seriesdirty = False
506 self.guardsdirty = False
505 self.guardsdirty = False
507 self.activeguards = None
506 self.activeguards = None
508
507
509 def diffopts(self, opts=None, patchfn=None):
508 def diffopts(self, opts=None, patchfn=None):
510 diffopts = patchmod.diffopts(self.ui, opts)
509 diffopts = patchmod.diffopts(self.ui, opts)
511 if self.gitmode == 'auto':
510 if self.gitmode == 'auto':
512 diffopts.upgrade = True
511 diffopts.upgrade = True
513 elif self.gitmode == 'keep':
512 elif self.gitmode == 'keep':
514 pass
513 pass
515 elif self.gitmode in ('yes', 'no'):
514 elif self.gitmode in ('yes', 'no'):
516 diffopts.git = self.gitmode == 'yes'
515 diffopts.git = self.gitmode == 'yes'
517 else:
516 else:
518 raise error.Abort(_('mq.git option can be auto/keep/yes/no'
517 raise error.Abort(_('mq.git option can be auto/keep/yes/no'
519 ' got %s') % self.gitmode)
518 ' got %s') % self.gitmode)
520 if patchfn:
519 if patchfn:
521 diffopts = self.patchopts(diffopts, patchfn)
520 diffopts = self.patchopts(diffopts, patchfn)
522 return diffopts
521 return diffopts
523
522
524 def patchopts(self, diffopts, *patches):
523 def patchopts(self, diffopts, *patches):
525 """Return a copy of input diff options with git set to true if
524 """Return a copy of input diff options with git set to true if
526 referenced patch is a git patch and should be preserved as such.
525 referenced patch is a git patch and should be preserved as such.
527 """
526 """
528 diffopts = diffopts.copy()
527 diffopts = diffopts.copy()
529 if not diffopts.git and self.gitmode == 'keep':
528 if not diffopts.git and self.gitmode == 'keep':
530 for patchfn in patches:
529 for patchfn in patches:
531 patchf = self.opener(patchfn, 'r')
530 patchf = self.opener(patchfn, 'r')
532 # if the patch was a git patch, refresh it as a git patch
531 # if the patch was a git patch, refresh it as a git patch
533 for line in patchf:
532 for line in patchf:
534 if line.startswith('diff --git'):
533 if line.startswith('diff --git'):
535 diffopts.git = True
534 diffopts.git = True
536 break
535 break
537 patchf.close()
536 patchf.close()
538 return diffopts
537 return diffopts
539
538
540 def join(self, *p):
539 def join(self, *p):
541 return os.path.join(self.path, *p)
540 return os.path.join(self.path, *p)
542
541
543 def findseries(self, patch):
542 def findseries(self, patch):
544 def matchpatch(l):
543 def matchpatch(l):
545 l = l.split('#', 1)[0]
544 l = l.split('#', 1)[0]
546 return l.strip() == patch
545 return l.strip() == patch
547 for index, l in enumerate(self.fullseries):
546 for index, l in enumerate(self.fullseries):
548 if matchpatch(l):
547 if matchpatch(l):
549 return index
548 return index
550 return None
549 return None
551
550
552 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
551 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
553
552
554 def parseseries(self):
553 def parseseries(self):
555 self.series = []
554 self.series = []
556 self.seriesguards = []
555 self.seriesguards = []
557 for l in self.fullseries:
556 for l in self.fullseries:
558 h = l.find('#')
557 h = l.find('#')
559 if h == -1:
558 if h == -1:
560 patch = l
559 patch = l
561 comment = ''
560 comment = ''
562 elif h == 0:
561 elif h == 0:
563 continue
562 continue
564 else:
563 else:
565 patch = l[:h]
564 patch = l[:h]
566 comment = l[h:]
565 comment = l[h:]
567 patch = patch.strip()
566 patch = patch.strip()
568 if patch:
567 if patch:
569 if patch in self.series:
568 if patch in self.series:
570 raise error.Abort(_('%s appears more than once in %s') %
569 raise error.Abort(_('%s appears more than once in %s') %
571 (patch, self.join(self.seriespath)))
570 (patch, self.join(self.seriespath)))
572 self.series.append(patch)
571 self.series.append(patch)
573 self.seriesguards.append(self.guard_re.findall(comment))
572 self.seriesguards.append(self.guard_re.findall(comment))
574
573
575 def checkguard(self, guard):
574 def checkguard(self, guard):
576 if not guard:
575 if not guard:
577 return _('guard cannot be an empty string')
576 return _('guard cannot be an empty string')
578 bad_chars = '# \t\r\n\f'
577 bad_chars = '# \t\r\n\f'
579 first = guard[0]
578 first = guard[0]
580 if first in '-+':
579 if first in '-+':
581 return (_('guard %r starts with invalid character: %r') %
580 return (_('guard %r starts with invalid character: %r') %
582 (guard, first))
581 (guard, first))
583 for c in bad_chars:
582 for c in bad_chars:
584 if c in guard:
583 if c in guard:
585 return _('invalid character in guard %r: %r') % (guard, c)
584 return _('invalid character in guard %r: %r') % (guard, c)
586
585
587 def setactive(self, guards):
586 def setactive(self, guards):
588 for guard in guards:
587 for guard in guards:
589 bad = self.checkguard(guard)
588 bad = self.checkguard(guard)
590 if bad:
589 if bad:
591 raise error.Abort(bad)
590 raise error.Abort(bad)
592 guards = sorted(set(guards))
591 guards = sorted(set(guards))
593 self.ui.debug('active guards: %s\n' % ' '.join(guards))
592 self.ui.debug('active guards: %s\n' % ' '.join(guards))
594 self.activeguards = guards
593 self.activeguards = guards
595 self.guardsdirty = True
594 self.guardsdirty = True
596
595
597 def active(self):
596 def active(self):
598 if self.activeguards is None:
597 if self.activeguards is None:
599 self.activeguards = []
598 self.activeguards = []
600 try:
599 try:
601 guards = self.opener.read(self.guardspath).split()
600 guards = self.opener.read(self.guardspath).split()
602 except IOError as err:
601 except IOError as err:
603 if err.errno != errno.ENOENT:
602 if err.errno != errno.ENOENT:
604 raise
603 raise
605 guards = []
604 guards = []
606 for i, guard in enumerate(guards):
605 for i, guard in enumerate(guards):
607 bad = self.checkguard(guard)
606 bad = self.checkguard(guard)
608 if bad:
607 if bad:
609 self.ui.warn('%s:%d: %s\n' %
608 self.ui.warn('%s:%d: %s\n' %
610 (self.join(self.guardspath), i + 1, bad))
609 (self.join(self.guardspath), i + 1, bad))
611 else:
610 else:
612 self.activeguards.append(guard)
611 self.activeguards.append(guard)
613 return self.activeguards
612 return self.activeguards
614
613
615 def setguards(self, idx, guards):
614 def setguards(self, idx, guards):
616 for g in guards:
615 for g in guards:
617 if len(g) < 2:
616 if len(g) < 2:
618 raise error.Abort(_('guard %r too short') % g)
617 raise error.Abort(_('guard %r too short') % g)
619 if g[0] not in '-+':
618 if g[0] not in '-+':
620 raise error.Abort(_('guard %r starts with invalid char') % g)
619 raise error.Abort(_('guard %r starts with invalid char') % g)
621 bad = self.checkguard(g[1:])
620 bad = self.checkguard(g[1:])
622 if bad:
621 if bad:
623 raise error.Abort(bad)
622 raise error.Abort(bad)
624 drop = self.guard_re.sub('', self.fullseries[idx])
623 drop = self.guard_re.sub('', self.fullseries[idx])
625 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
624 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
626 self.parseseries()
625 self.parseseries()
627 self.seriesdirty = True
626 self.seriesdirty = True
628
627
629 def pushable(self, idx):
628 def pushable(self, idx):
630 if isinstance(idx, str):
629 if isinstance(idx, str):
631 idx = self.series.index(idx)
630 idx = self.series.index(idx)
632 patchguards = self.seriesguards[idx]
631 patchguards = self.seriesguards[idx]
633 if not patchguards:
632 if not patchguards:
634 return True, None
633 return True, None
635 guards = self.active()
634 guards = self.active()
636 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
635 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
637 if exactneg:
636 if exactneg:
638 return False, repr(exactneg[0])
637 return False, repr(exactneg[0])
639 pos = [g for g in patchguards if g[0] == '+']
638 pos = [g for g in patchguards if g[0] == '+']
640 exactpos = [g for g in pos if g[1:] in guards]
639 exactpos = [g for g in pos if g[1:] in guards]
641 if pos:
640 if pos:
642 if exactpos:
641 if exactpos:
643 return True, repr(exactpos[0])
642 return True, repr(exactpos[0])
644 return False, ' '.join(map(repr, pos))
643 return False, ' '.join(map(repr, pos))
645 return True, ''
644 return True, ''
646
645
647 def explainpushable(self, idx, all_patches=False):
646 def explainpushable(self, idx, all_patches=False):
648 if all_patches:
647 if all_patches:
649 write = self.ui.write
648 write = self.ui.write
650 else:
649 else:
651 write = self.ui.warn
650 write = self.ui.warn
652
651
653 if all_patches or self.ui.verbose:
652 if all_patches or self.ui.verbose:
654 if isinstance(idx, str):
653 if isinstance(idx, str):
655 idx = self.series.index(idx)
654 idx = self.series.index(idx)
656 pushable, why = self.pushable(idx)
655 pushable, why = self.pushable(idx)
657 if all_patches and pushable:
656 if all_patches and pushable:
658 if why is None:
657 if why is None:
659 write(_('allowing %s - no guards in effect\n') %
658 write(_('allowing %s - no guards in effect\n') %
660 self.series[idx])
659 self.series[idx])
661 else:
660 else:
662 if not why:
661 if not why:
663 write(_('allowing %s - no matching negative guards\n') %
662 write(_('allowing %s - no matching negative guards\n') %
664 self.series[idx])
663 self.series[idx])
665 else:
664 else:
666 write(_('allowing %s - guarded by %s\n') %
665 write(_('allowing %s - guarded by %s\n') %
667 (self.series[idx], why))
666 (self.series[idx], why))
668 if not pushable:
667 if not pushable:
669 if why:
668 if why:
670 write(_('skipping %s - guarded by %s\n') %
669 write(_('skipping %s - guarded by %s\n') %
671 (self.series[idx], why))
670 (self.series[idx], why))
672 else:
671 else:
673 write(_('skipping %s - no matching guards\n') %
672 write(_('skipping %s - no matching guards\n') %
674 self.series[idx])
673 self.series[idx])
675
674
676 def savedirty(self):
675 def savedirty(self):
677 def writelist(items, path):
676 def writelist(items, path):
678 fp = self.opener(path, 'w')
677 fp = self.opener(path, 'w')
679 for i in items:
678 for i in items:
680 fp.write("%s\n" % i)
679 fp.write("%s\n" % i)
681 fp.close()
680 fp.close()
682 if self.applieddirty:
681 if self.applieddirty:
683 writelist(map(str, self.applied), self.statuspath)
682 writelist(map(str, self.applied), self.statuspath)
684 self.applieddirty = False
683 self.applieddirty = False
685 if self.seriesdirty:
684 if self.seriesdirty:
686 writelist(self.fullseries, self.seriespath)
685 writelist(self.fullseries, self.seriespath)
687 self.seriesdirty = False
686 self.seriesdirty = False
688 if self.guardsdirty:
687 if self.guardsdirty:
689 writelist(self.activeguards, self.guardspath)
688 writelist(self.activeguards, self.guardspath)
690 self.guardsdirty = False
689 self.guardsdirty = False
691 if self.added:
690 if self.added:
692 qrepo = self.qrepo()
691 qrepo = self.qrepo()
693 if qrepo:
692 if qrepo:
694 qrepo[None].add(f for f in self.added if f not in qrepo[None])
693 qrepo[None].add(f for f in self.added if f not in qrepo[None])
695 self.added = []
694 self.added = []
696
695
697 def removeundo(self, repo):
696 def removeundo(self, repo):
698 undo = repo.sjoin('undo')
697 undo = repo.sjoin('undo')
699 if not os.path.exists(undo):
698 if not os.path.exists(undo):
700 return
699 return
701 try:
700 try:
702 os.unlink(undo)
701 os.unlink(undo)
703 except OSError as inst:
702 except OSError as inst:
704 self.ui.warn(_('error removing undo: %s\n') % str(inst))
703 self.ui.warn(_('error removing undo: %s\n') % str(inst))
705
704
706 def backup(self, repo, files, copy=False):
705 def backup(self, repo, files, copy=False):
707 # backup local changes in --force case
706 # backup local changes in --force case
708 for f in sorted(files):
707 for f in sorted(files):
709 absf = repo.wjoin(f)
708 absf = repo.wjoin(f)
710 if os.path.lexists(absf):
709 if os.path.lexists(absf):
711 self.ui.note(_('saving current version of %s as %s\n') %
710 self.ui.note(_('saving current version of %s as %s\n') %
712 (f, scmutil.origpath(self.ui, repo, f)))
711 (f, scmutil.origpath(self.ui, repo, f)))
713
712
714 absorig = scmutil.origpath(self.ui, repo, absf)
713 absorig = scmutil.origpath(self.ui, repo, absf)
715 if copy:
714 if copy:
716 util.copyfile(absf, absorig)
715 util.copyfile(absf, absorig)
717 else:
716 else:
718 util.rename(absf, absorig)
717 util.rename(absf, absorig)
719
718
720 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
719 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
721 fp=None, changes=None, opts={}):
720 fp=None, changes=None, opts={}):
722 stat = opts.get('stat')
721 stat = opts.get('stat')
723 m = scmutil.match(repo[node1], files, opts)
722 m = scmutil.match(repo[node1], files, opts)
724 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
723 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
725 changes, stat, fp)
724 changes, stat, fp)
726
725
727 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
726 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
728 # first try just applying the patch
727 # first try just applying the patch
729 (err, n) = self.apply(repo, [patch], update_status=False,
728 (err, n) = self.apply(repo, [patch], update_status=False,
730 strict=True, merge=rev)
729 strict=True, merge=rev)
731
730
732 if err == 0:
731 if err == 0:
733 return (err, n)
732 return (err, n)
734
733
735 if n is None:
734 if n is None:
736 raise error.Abort(_("apply failed for patch %s") % patch)
735 raise error.Abort(_("apply failed for patch %s") % patch)
737
736
738 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
737 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
739
738
740 # apply failed, strip away that rev and merge.
739 # apply failed, strip away that rev and merge.
741 hg.clean(repo, head)
740 hg.clean(repo, head)
742 strip(self.ui, repo, [n], update=False, backup=False)
741 strip(self.ui, repo, [n], update=False, backup=False)
743
742
744 ctx = repo[rev]
743 ctx = repo[rev]
745 ret = hg.merge(repo, rev)
744 ret = hg.merge(repo, rev)
746 if ret:
745 if ret:
747 raise error.Abort(_("update returned %d") % ret)
746 raise error.Abort(_("update returned %d") % ret)
748 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
747 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
749 if n is None:
748 if n is None:
750 raise error.Abort(_("repo commit failed"))
749 raise error.Abort(_("repo commit failed"))
751 try:
750 try:
752 ph = patchheader(mergeq.join(patch), self.plainmode)
751 ph = patchheader(mergeq.join(patch), self.plainmode)
753 except Exception:
752 except Exception:
754 raise error.Abort(_("unable to read %s") % patch)
753 raise error.Abort(_("unable to read %s") % patch)
755
754
756 diffopts = self.patchopts(diffopts, patch)
755 diffopts = self.patchopts(diffopts, patch)
757 patchf = self.opener(patch, "w")
756 patchf = self.opener(patch, "w")
758 comments = str(ph)
757 comments = str(ph)
759 if comments:
758 if comments:
760 patchf.write(comments)
759 patchf.write(comments)
761 self.printdiff(repo, diffopts, head, n, fp=patchf)
760 self.printdiff(repo, diffopts, head, n, fp=patchf)
762 patchf.close()
761 patchf.close()
763 self.removeundo(repo)
762 self.removeundo(repo)
764 return (0, n)
763 return (0, n)
765
764
766 def qparents(self, repo, rev=None):
765 def qparents(self, repo, rev=None):
767 """return the mq handled parent or p1
766 """return the mq handled parent or p1
768
767
769 In some case where mq get himself in being the parent of a merge the
768 In some case where mq get himself in being the parent of a merge the
770 appropriate parent may be p2.
769 appropriate parent may be p2.
771 (eg: an in progress merge started with mq disabled)
770 (eg: an in progress merge started with mq disabled)
772
771
773 If no parent are managed by mq, p1 is returned.
772 If no parent are managed by mq, p1 is returned.
774 """
773 """
775 if rev is None:
774 if rev is None:
776 (p1, p2) = repo.dirstate.parents()
775 (p1, p2) = repo.dirstate.parents()
777 if p2 == nullid:
776 if p2 == nullid:
778 return p1
777 return p1
779 if not self.applied:
778 if not self.applied:
780 return None
779 return None
781 return self.applied[-1].node
780 return self.applied[-1].node
782 p1, p2 = repo.changelog.parents(rev)
781 p1, p2 = repo.changelog.parents(rev)
783 if p2 != nullid and p2 in [x.node for x in self.applied]:
782 if p2 != nullid and p2 in [x.node for x in self.applied]:
784 return p2
783 return p2
785 return p1
784 return p1
786
785
787 def mergepatch(self, repo, mergeq, series, diffopts):
786 def mergepatch(self, repo, mergeq, series, diffopts):
788 if not self.applied:
787 if not self.applied:
789 # each of the patches merged in will have two parents. This
788 # each of the patches merged in will have two parents. This
790 # can confuse the qrefresh, qdiff, and strip code because it
789 # can confuse the qrefresh, qdiff, and strip code because it
791 # needs to know which parent is actually in the patch queue.
790 # needs to know which parent is actually in the patch queue.
792 # so, we insert a merge marker with only one parent. This way
791 # so, we insert a merge marker with only one parent. This way
793 # the first patch in the queue is never a merge patch
792 # the first patch in the queue is never a merge patch
794 #
793 #
795 pname = ".hg.patches.merge.marker"
794 pname = ".hg.patches.merge.marker"
796 n = newcommit(repo, None, '[mq]: merge marker', force=True)
795 n = newcommit(repo, None, '[mq]: merge marker', force=True)
797 self.removeundo(repo)
796 self.removeundo(repo)
798 self.applied.append(statusentry(n, pname))
797 self.applied.append(statusentry(n, pname))
799 self.applieddirty = True
798 self.applieddirty = True
800
799
801 head = self.qparents(repo)
800 head = self.qparents(repo)
802
801
803 for patch in series:
802 for patch in series:
804 patch = mergeq.lookup(patch, strict=True)
803 patch = mergeq.lookup(patch, strict=True)
805 if not patch:
804 if not patch:
806 self.ui.warn(_("patch %s does not exist\n") % patch)
805 self.ui.warn(_("patch %s does not exist\n") % patch)
807 return (1, None)
806 return (1, None)
808 pushable, reason = self.pushable(patch)
807 pushable, reason = self.pushable(patch)
809 if not pushable:
808 if not pushable:
810 self.explainpushable(patch, all_patches=True)
809 self.explainpushable(patch, all_patches=True)
811 continue
810 continue
812 info = mergeq.isapplied(patch)
811 info = mergeq.isapplied(patch)
813 if not info:
812 if not info:
814 self.ui.warn(_("patch %s is not applied\n") % patch)
813 self.ui.warn(_("patch %s is not applied\n") % patch)
815 return (1, None)
814 return (1, None)
816 rev = info[1]
815 rev = info[1]
817 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
816 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
818 if head:
817 if head:
819 self.applied.append(statusentry(head, patch))
818 self.applied.append(statusentry(head, patch))
820 self.applieddirty = True
819 self.applieddirty = True
821 if err:
820 if err:
822 return (err, head)
821 return (err, head)
823 self.savedirty()
822 self.savedirty()
824 return (0, head)
823 return (0, head)
825
824
826 def patch(self, repo, patchfile):
825 def patch(self, repo, patchfile):
827 '''Apply patchfile to the working directory.
826 '''Apply patchfile to the working directory.
828 patchfile: name of patch file'''
827 patchfile: name of patch file'''
829 files = set()
828 files = set()
830 try:
829 try:
831 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
830 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
832 files=files, eolmode=None)
831 files=files, eolmode=None)
833 return (True, list(files), fuzz)
832 return (True, list(files), fuzz)
834 except Exception as inst:
833 except Exception as inst:
835 self.ui.note(str(inst) + '\n')
834 self.ui.note(str(inst) + '\n')
836 if not self.ui.verbose:
835 if not self.ui.verbose:
837 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
836 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
838 self.ui.traceback()
837 self.ui.traceback()
839 return (False, list(files), False)
838 return (False, list(files), False)
840
839
841 def apply(self, repo, series, list=False, update_status=True,
840 def apply(self, repo, series, list=False, update_status=True,
842 strict=False, patchdir=None, merge=None, all_files=None,
841 strict=False, patchdir=None, merge=None, all_files=None,
843 tobackup=None, keepchanges=False):
842 tobackup=None, keepchanges=False):
844 wlock = lock = tr = None
843 wlock = lock = tr = None
845 try:
844 try:
846 wlock = repo.wlock()
845 wlock = repo.wlock()
847 lock = repo.lock()
846 lock = repo.lock()
848 tr = repo.transaction("qpush")
847 tr = repo.transaction("qpush")
849 try:
848 try:
850 ret = self._apply(repo, series, list, update_status,
849 ret = self._apply(repo, series, list, update_status,
851 strict, patchdir, merge, all_files=all_files,
850 strict, patchdir, merge, all_files=all_files,
852 tobackup=tobackup, keepchanges=keepchanges)
851 tobackup=tobackup, keepchanges=keepchanges)
853 tr.close()
852 tr.close()
854 self.savedirty()
853 self.savedirty()
855 return ret
854 return ret
856 except AbortNoCleanup:
855 except AbortNoCleanup:
857 tr.close()
856 tr.close()
858 self.savedirty()
857 self.savedirty()
859 raise
858 raise
860 except: # re-raises
859 except: # re-raises
861 try:
860 try:
862 tr.abort()
861 tr.abort()
863 finally:
862 finally:
864 self.invalidate()
863 self.invalidate()
865 raise
864 raise
866 finally:
865 finally:
867 release(tr, lock, wlock)
866 release(tr, lock, wlock)
868 self.removeundo(repo)
867 self.removeundo(repo)
869
868
870 def _apply(self, repo, series, list=False, update_status=True,
869 def _apply(self, repo, series, list=False, update_status=True,
871 strict=False, patchdir=None, merge=None, all_files=None,
870 strict=False, patchdir=None, merge=None, all_files=None,
872 tobackup=None, keepchanges=False):
871 tobackup=None, keepchanges=False):
873 """returns (error, hash)
872 """returns (error, hash)
874
873
875 error = 1 for unable to read, 2 for patch failed, 3 for patch
874 error = 1 for unable to read, 2 for patch failed, 3 for patch
876 fuzz. tobackup is None or a set of files to backup before they
875 fuzz. tobackup is None or a set of files to backup before they
877 are modified by a patch.
876 are modified by a patch.
878 """
877 """
879 # TODO unify with commands.py
878 # TODO unify with commands.py
880 if not patchdir:
879 if not patchdir:
881 patchdir = self.path
880 patchdir = self.path
882 err = 0
881 err = 0
883 n = None
882 n = None
884 for patchname in series:
883 for patchname in series:
885 pushable, reason = self.pushable(patchname)
884 pushable, reason = self.pushable(patchname)
886 if not pushable:
885 if not pushable:
887 self.explainpushable(patchname, all_patches=True)
886 self.explainpushable(patchname, all_patches=True)
888 continue
887 continue
889 self.ui.status(_("applying %s\n") % patchname)
888 self.ui.status(_("applying %s\n") % patchname)
890 pf = os.path.join(patchdir, patchname)
889 pf = os.path.join(patchdir, patchname)
891
890
892 try:
891 try:
893 ph = patchheader(self.join(patchname), self.plainmode)
892 ph = patchheader(self.join(patchname), self.plainmode)
894 except IOError:
893 except IOError:
895 self.ui.warn(_("unable to read %s\n") % patchname)
894 self.ui.warn(_("unable to read %s\n") % patchname)
896 err = 1
895 err = 1
897 break
896 break
898
897
899 message = ph.message
898 message = ph.message
900 if not message:
899 if not message:
901 # The commit message should not be translated
900 # The commit message should not be translated
902 message = "imported patch %s\n" % patchname
901 message = "imported patch %s\n" % patchname
903 else:
902 else:
904 if list:
903 if list:
905 # The commit message should not be translated
904 # The commit message should not be translated
906 message.append("\nimported patch %s" % patchname)
905 message.append("\nimported patch %s" % patchname)
907 message = '\n'.join(message)
906 message = '\n'.join(message)
908
907
909 if ph.haspatch:
908 if ph.haspatch:
910 if tobackup:
909 if tobackup:
911 touched = patchmod.changedfiles(self.ui, repo, pf)
910 touched = patchmod.changedfiles(self.ui, repo, pf)
912 touched = set(touched) & tobackup
911 touched = set(touched) & tobackup
913 if touched and keepchanges:
912 if touched and keepchanges:
914 raise AbortNoCleanup(
913 raise AbortNoCleanup(
915 _("conflicting local changes found"),
914 _("conflicting local changes found"),
916 hint=_("did you forget to qrefresh?"))
915 hint=_("did you forget to qrefresh?"))
917 self.backup(repo, touched, copy=True)
916 self.backup(repo, touched, copy=True)
918 tobackup = tobackup - touched
917 tobackup = tobackup - touched
919 (patcherr, files, fuzz) = self.patch(repo, pf)
918 (patcherr, files, fuzz) = self.patch(repo, pf)
920 if all_files is not None:
919 if all_files is not None:
921 all_files.update(files)
920 all_files.update(files)
922 patcherr = not patcherr
921 patcherr = not patcherr
923 else:
922 else:
924 self.ui.warn(_("patch %s is empty\n") % patchname)
923 self.ui.warn(_("patch %s is empty\n") % patchname)
925 patcherr, files, fuzz = 0, [], 0
924 patcherr, files, fuzz = 0, [], 0
926
925
927 if merge and files:
926 if merge and files:
928 # Mark as removed/merged and update dirstate parent info
927 # Mark as removed/merged and update dirstate parent info
929 removed = []
928 removed = []
930 merged = []
929 merged = []
931 for f in files:
930 for f in files:
932 if os.path.lexists(repo.wjoin(f)):
931 if os.path.lexists(repo.wjoin(f)):
933 merged.append(f)
932 merged.append(f)
934 else:
933 else:
935 removed.append(f)
934 removed.append(f)
936 repo.dirstate.beginparentchange()
935 repo.dirstate.beginparentchange()
937 for f in removed:
936 for f in removed:
938 repo.dirstate.remove(f)
937 repo.dirstate.remove(f)
939 for f in merged:
938 for f in merged:
940 repo.dirstate.merge(f)
939 repo.dirstate.merge(f)
941 p1, p2 = repo.dirstate.parents()
940 p1, p2 = repo.dirstate.parents()
942 repo.setparents(p1, merge)
941 repo.setparents(p1, merge)
943 repo.dirstate.endparentchange()
942 repo.dirstate.endparentchange()
944
943
945 if all_files and '.hgsubstate' in all_files:
944 if all_files and '.hgsubstate' in all_files:
946 wctx = repo[None]
945 wctx = repo[None]
947 pctx = repo['.']
946 pctx = repo['.']
948 overwrite = False
947 overwrite = False
949 mergedsubstate = subrepo.submerge(repo, pctx, wctx, wctx,
948 mergedsubstate = subrepo.submerge(repo, pctx, wctx, wctx,
950 overwrite)
949 overwrite)
951 files += mergedsubstate.keys()
950 files += mergedsubstate.keys()
952
951
953 match = scmutil.matchfiles(repo, files or [])
952 match = scmutil.matchfiles(repo, files or [])
954 oldtip = repo['tip']
953 oldtip = repo['tip']
955 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
954 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
956 force=True)
955 force=True)
957 if repo['tip'] == oldtip:
956 if repo['tip'] == oldtip:
958 raise error.Abort(_("qpush exactly duplicates child changeset"))
957 raise error.Abort(_("qpush exactly duplicates child changeset"))
959 if n is None:
958 if n is None:
960 raise error.Abort(_("repository commit failed"))
959 raise error.Abort(_("repository commit failed"))
961
960
962 if update_status:
961 if update_status:
963 self.applied.append(statusentry(n, patchname))
962 self.applied.append(statusentry(n, patchname))
964
963
965 if patcherr:
964 if patcherr:
966 self.ui.warn(_("patch failed, rejects left in working "
965 self.ui.warn(_("patch failed, rejects left in working "
967 "directory\n"))
966 "directory\n"))
968 err = 2
967 err = 2
969 break
968 break
970
969
971 if fuzz and strict:
970 if fuzz and strict:
972 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
971 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
973 err = 3
972 err = 3
974 break
973 break
975 return (err, n)
974 return (err, n)
976
975
977 def _cleanup(self, patches, numrevs, keep=False):
976 def _cleanup(self, patches, numrevs, keep=False):
978 if not keep:
977 if not keep:
979 r = self.qrepo()
978 r = self.qrepo()
980 if r:
979 if r:
981 r[None].forget(patches)
980 r[None].forget(patches)
982 for p in patches:
981 for p in patches:
983 try:
982 try:
984 os.unlink(self.join(p))
983 os.unlink(self.join(p))
985 except OSError as inst:
984 except OSError as inst:
986 if inst.errno != errno.ENOENT:
985 if inst.errno != errno.ENOENT:
987 raise
986 raise
988
987
989 qfinished = []
988 qfinished = []
990 if numrevs:
989 if numrevs:
991 qfinished = self.applied[:numrevs]
990 qfinished = self.applied[:numrevs]
992 del self.applied[:numrevs]
991 del self.applied[:numrevs]
993 self.applieddirty = True
992 self.applieddirty = True
994
993
995 unknown = []
994 unknown = []
996
995
997 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
996 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
998 reverse=True):
997 reverse=True):
999 if i is not None:
998 if i is not None:
1000 del self.fullseries[i]
999 del self.fullseries[i]
1001 else:
1000 else:
1002 unknown.append(p)
1001 unknown.append(p)
1003
1002
1004 if unknown:
1003 if unknown:
1005 if numrevs:
1004 if numrevs:
1006 rev = dict((entry.name, entry.node) for entry in qfinished)
1005 rev = dict((entry.name, entry.node) for entry in qfinished)
1007 for p in unknown:
1006 for p in unknown:
1008 msg = _('revision %s refers to unknown patches: %s\n')
1007 msg = _('revision %s refers to unknown patches: %s\n')
1009 self.ui.warn(msg % (short(rev[p]), p))
1008 self.ui.warn(msg % (short(rev[p]), p))
1010 else:
1009 else:
1011 msg = _('unknown patches: %s\n')
1010 msg = _('unknown patches: %s\n')
1012 raise error.Abort(''.join(msg % p for p in unknown))
1011 raise error.Abort(''.join(msg % p for p in unknown))
1013
1012
1014 self.parseseries()
1013 self.parseseries()
1015 self.seriesdirty = True
1014 self.seriesdirty = True
1016 return [entry.node for entry in qfinished]
1015 return [entry.node for entry in qfinished]
1017
1016
1018 def _revpatches(self, repo, revs):
1017 def _revpatches(self, repo, revs):
1019 firstrev = repo[self.applied[0].node].rev()
1018 firstrev = repo[self.applied[0].node].rev()
1020 patches = []
1019 patches = []
1021 for i, rev in enumerate(revs):
1020 for i, rev in enumerate(revs):
1022
1021
1023 if rev < firstrev:
1022 if rev < firstrev:
1024 raise error.Abort(_('revision %d is not managed') % rev)
1023 raise error.Abort(_('revision %d is not managed') % rev)
1025
1024
1026 ctx = repo[rev]
1025 ctx = repo[rev]
1027 base = self.applied[i].node
1026 base = self.applied[i].node
1028 if ctx.node() != base:
1027 if ctx.node() != base:
1029 msg = _('cannot delete revision %d above applied patches')
1028 msg = _('cannot delete revision %d above applied patches')
1030 raise error.Abort(msg % rev)
1029 raise error.Abort(msg % rev)
1031
1030
1032 patch = self.applied[i].name
1031 patch = self.applied[i].name
1033 for fmt in ('[mq]: %s', 'imported patch %s'):
1032 for fmt in ('[mq]: %s', 'imported patch %s'):
1034 if ctx.description() == fmt % patch:
1033 if ctx.description() == fmt % patch:
1035 msg = _('patch %s finalized without changeset message\n')
1034 msg = _('patch %s finalized without changeset message\n')
1036 repo.ui.status(msg % patch)
1035 repo.ui.status(msg % patch)
1037 break
1036 break
1038
1037
1039 patches.append(patch)
1038 patches.append(patch)
1040 return patches
1039 return patches
1041
1040
1042 def finish(self, repo, revs):
1041 def finish(self, repo, revs):
1043 # Manually trigger phase computation to ensure phasedefaults is
1042 # Manually trigger phase computation to ensure phasedefaults is
1044 # executed before we remove the patches.
1043 # executed before we remove the patches.
1045 repo._phasecache
1044 repo._phasecache
1046 patches = self._revpatches(repo, sorted(revs))
1045 patches = self._revpatches(repo, sorted(revs))
1047 qfinished = self._cleanup(patches, len(patches))
1046 qfinished = self._cleanup(patches, len(patches))
1048 if qfinished and repo.ui.configbool('mq', 'secret', False):
1047 if qfinished and repo.ui.configbool('mq', 'secret', False):
1049 # only use this logic when the secret option is added
1048 # only use this logic when the secret option is added
1050 oldqbase = repo[qfinished[0]]
1049 oldqbase = repo[qfinished[0]]
1051 tphase = repo.ui.config('phases', 'new-commit', phases.draft)
1050 tphase = repo.ui.config('phases', 'new-commit', phases.draft)
1052 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
1051 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
1053 with repo.transaction('qfinish') as tr:
1052 with repo.transaction('qfinish') as tr:
1054 phases.advanceboundary(repo, tr, tphase, qfinished)
1053 phases.advanceboundary(repo, tr, tphase, qfinished)
1055
1054
1056 def delete(self, repo, patches, opts):
1055 def delete(self, repo, patches, opts):
1057 if not patches and not opts.get('rev'):
1056 if not patches and not opts.get('rev'):
1058 raise error.Abort(_('qdelete requires at least one revision or '
1057 raise error.Abort(_('qdelete requires at least one revision or '
1059 'patch name'))
1058 'patch name'))
1060
1059
1061 realpatches = []
1060 realpatches = []
1062 for patch in patches:
1061 for patch in patches:
1063 patch = self.lookup(patch, strict=True)
1062 patch = self.lookup(patch, strict=True)
1064 info = self.isapplied(patch)
1063 info = self.isapplied(patch)
1065 if info:
1064 if info:
1066 raise error.Abort(_("cannot delete applied patch %s") % patch)
1065 raise error.Abort(_("cannot delete applied patch %s") % patch)
1067 if patch not in self.series:
1066 if patch not in self.series:
1068 raise error.Abort(_("patch %s not in series file") % patch)
1067 raise error.Abort(_("patch %s not in series file") % patch)
1069 if patch not in realpatches:
1068 if patch not in realpatches:
1070 realpatches.append(patch)
1069 realpatches.append(patch)
1071
1070
1072 numrevs = 0
1071 numrevs = 0
1073 if opts.get('rev'):
1072 if opts.get('rev'):
1074 if not self.applied:
1073 if not self.applied:
1075 raise error.Abort(_('no patches applied'))
1074 raise error.Abort(_('no patches applied'))
1076 revs = scmutil.revrange(repo, opts.get('rev'))
1075 revs = scmutil.revrange(repo, opts.get('rev'))
1077 revs.sort()
1076 revs.sort()
1078 revpatches = self._revpatches(repo, revs)
1077 revpatches = self._revpatches(repo, revs)
1079 realpatches += revpatches
1078 realpatches += revpatches
1080 numrevs = len(revpatches)
1079 numrevs = len(revpatches)
1081
1080
1082 self._cleanup(realpatches, numrevs, opts.get('keep'))
1081 self._cleanup(realpatches, numrevs, opts.get('keep'))
1083
1082
1084 def checktoppatch(self, repo):
1083 def checktoppatch(self, repo):
1085 '''check that working directory is at qtip'''
1084 '''check that working directory is at qtip'''
1086 if self.applied:
1085 if self.applied:
1087 top = self.applied[-1].node
1086 top = self.applied[-1].node
1088 patch = self.applied[-1].name
1087 patch = self.applied[-1].name
1089 if repo.dirstate.p1() != top:
1088 if repo.dirstate.p1() != top:
1090 raise error.Abort(_("working directory revision is not qtip"))
1089 raise error.Abort(_("working directory revision is not qtip"))
1091 return top, patch
1090 return top, patch
1092 return None, None
1091 return None, None
1093
1092
1094 def putsubstate2changes(self, substatestate, changes):
1093 def putsubstate2changes(self, substatestate, changes):
1095 for files in changes[:3]:
1094 for files in changes[:3]:
1096 if '.hgsubstate' in files:
1095 if '.hgsubstate' in files:
1097 return # already listed up
1096 return # already listed up
1098 # not yet listed up
1097 # not yet listed up
1099 if substatestate in 'a?':
1098 if substatestate in 'a?':
1100 changes[1].append('.hgsubstate')
1099 changes[1].append('.hgsubstate')
1101 elif substatestate in 'r':
1100 elif substatestate in 'r':
1102 changes[2].append('.hgsubstate')
1101 changes[2].append('.hgsubstate')
1103 else: # modified
1102 else: # modified
1104 changes[0].append('.hgsubstate')
1103 changes[0].append('.hgsubstate')
1105
1104
1106 def checklocalchanges(self, repo, force=False, refresh=True):
1105 def checklocalchanges(self, repo, force=False, refresh=True):
1107 excsuffix = ''
1106 excsuffix = ''
1108 if refresh:
1107 if refresh:
1109 excsuffix = ', qrefresh first'
1108 excsuffix = ', qrefresh first'
1110 # plain versions for i18n tool to detect them
1109 # plain versions for i18n tool to detect them
1111 _("local changes found, qrefresh first")
1110 _("local changes found, qrefresh first")
1112 _("local changed subrepos found, qrefresh first")
1111 _("local changed subrepos found, qrefresh first")
1113 return checklocalchanges(repo, force, excsuffix)
1112 return checklocalchanges(repo, force, excsuffix)
1114
1113
1115 _reserved = ('series', 'status', 'guards', '.', '..')
1114 _reserved = ('series', 'status', 'guards', '.', '..')
1116 def checkreservedname(self, name):
1115 def checkreservedname(self, name):
1117 if name in self._reserved:
1116 if name in self._reserved:
1118 raise error.Abort(_('"%s" cannot be used as the name of a patch')
1117 raise error.Abort(_('"%s" cannot be used as the name of a patch')
1119 % name)
1118 % name)
1120 for prefix in ('.hg', '.mq'):
1119 for prefix in ('.hg', '.mq'):
1121 if name.startswith(prefix):
1120 if name.startswith(prefix):
1122 raise error.Abort(_('patch name cannot begin with "%s"')
1121 raise error.Abort(_('patch name cannot begin with "%s"')
1123 % prefix)
1122 % prefix)
1124 for c in ('#', ':', '\r', '\n'):
1123 for c in ('#', ':', '\r', '\n'):
1125 if c in name:
1124 if c in name:
1126 raise error.Abort(_('%r cannot be used in the name of a patch')
1125 raise error.Abort(_('%r cannot be used in the name of a patch')
1127 % c)
1126 % c)
1128
1127
1129 def checkpatchname(self, name, force=False):
1128 def checkpatchname(self, name, force=False):
1130 self.checkreservedname(name)
1129 self.checkreservedname(name)
1131 if not force and os.path.exists(self.join(name)):
1130 if not force and os.path.exists(self.join(name)):
1132 if os.path.isdir(self.join(name)):
1131 if os.path.isdir(self.join(name)):
1133 raise error.Abort(_('"%s" already exists as a directory')
1132 raise error.Abort(_('"%s" already exists as a directory')
1134 % name)
1133 % name)
1135 else:
1134 else:
1136 raise error.Abort(_('patch "%s" already exists') % name)
1135 raise error.Abort(_('patch "%s" already exists') % name)
1137
1136
1138 def makepatchname(self, title, fallbackname):
1137 def makepatchname(self, title, fallbackname):
1139 """Return a suitable filename for title, adding a suffix to make
1138 """Return a suitable filename for title, adding a suffix to make
1140 it unique in the existing list"""
1139 it unique in the existing list"""
1141 namebase = re.sub('[\s\W_]+', '_', title.lower()).strip('_')
1140 namebase = re.sub('[\s\W_]+', '_', title.lower()).strip('_')
1142 namebase = namebase[:75] # avoid too long name (issue5117)
1141 namebase = namebase[:75] # avoid too long name (issue5117)
1143 if namebase:
1142 if namebase:
1144 try:
1143 try:
1145 self.checkreservedname(namebase)
1144 self.checkreservedname(namebase)
1146 except error.Abort:
1145 except error.Abort:
1147 namebase = fallbackname
1146 namebase = fallbackname
1148 else:
1147 else:
1149 namebase = fallbackname
1148 namebase = fallbackname
1150 name = namebase
1149 name = namebase
1151 i = 0
1150 i = 0
1152 while True:
1151 while True:
1153 if name not in self.fullseries:
1152 if name not in self.fullseries:
1154 try:
1153 try:
1155 self.checkpatchname(name)
1154 self.checkpatchname(name)
1156 break
1155 break
1157 except error.Abort:
1156 except error.Abort:
1158 pass
1157 pass
1159 i += 1
1158 i += 1
1160 name = '%s__%s' % (namebase, i)
1159 name = '%s__%s' % (namebase, i)
1161 return name
1160 return name
1162
1161
1163 def checkkeepchanges(self, keepchanges, force):
1162 def checkkeepchanges(self, keepchanges, force):
1164 if force and keepchanges:
1163 if force and keepchanges:
1165 raise error.Abort(_('cannot use both --force and --keep-changes'))
1164 raise error.Abort(_('cannot use both --force and --keep-changes'))
1166
1165
1167 def new(self, repo, patchfn, *pats, **opts):
1166 def new(self, repo, patchfn, *pats, **opts):
1168 """options:
1167 """options:
1169 msg: a string or a no-argument function returning a string
1168 msg: a string or a no-argument function returning a string
1170 """
1169 """
1171 msg = opts.get('msg')
1170 msg = opts.get('msg')
1172 edit = opts.get('edit')
1171 edit = opts.get('edit')
1173 editform = opts.get('editform', 'mq.qnew')
1172 editform = opts.get('editform', 'mq.qnew')
1174 user = opts.get('user')
1173 user = opts.get('user')
1175 date = opts.get('date')
1174 date = opts.get('date')
1176 if date:
1175 if date:
1177 date = util.parsedate(date)
1176 date = util.parsedate(date)
1178 diffopts = self.diffopts({'git': opts.get('git')})
1177 diffopts = self.diffopts({'git': opts.get('git')})
1179 if opts.get('checkname', True):
1178 if opts.get('checkname', True):
1180 self.checkpatchname(patchfn)
1179 self.checkpatchname(patchfn)
1181 inclsubs = checksubstate(repo)
1180 inclsubs = checksubstate(repo)
1182 if inclsubs:
1181 if inclsubs:
1183 substatestate = repo.dirstate['.hgsubstate']
1182 substatestate = repo.dirstate['.hgsubstate']
1184 if opts.get('include') or opts.get('exclude') or pats:
1183 if opts.get('include') or opts.get('exclude') or pats:
1185 # detect missing files in pats
1184 # detect missing files in pats
1186 def badfn(f, msg):
1185 def badfn(f, msg):
1187 if f != '.hgsubstate': # .hgsubstate is auto-created
1186 if f != '.hgsubstate': # .hgsubstate is auto-created
1188 raise error.Abort('%s: %s' % (f, msg))
1187 raise error.Abort('%s: %s' % (f, msg))
1189 match = scmutil.match(repo[None], pats, opts, badfn=badfn)
1188 match = scmutil.match(repo[None], pats, opts, badfn=badfn)
1190 changes = repo.status(match=match)
1189 changes = repo.status(match=match)
1191 else:
1190 else:
1192 changes = self.checklocalchanges(repo, force=True)
1191 changes = self.checklocalchanges(repo, force=True)
1193 commitfiles = list(inclsubs)
1192 commitfiles = list(inclsubs)
1194 for files in changes[:3]:
1193 for files in changes[:3]:
1195 commitfiles.extend(files)
1194 commitfiles.extend(files)
1196 match = scmutil.matchfiles(repo, commitfiles)
1195 match = scmutil.matchfiles(repo, commitfiles)
1197 if len(repo[None].parents()) > 1:
1196 if len(repo[None].parents()) > 1:
1198 raise error.Abort(_('cannot manage merge changesets'))
1197 raise error.Abort(_('cannot manage merge changesets'))
1199 self.checktoppatch(repo)
1198 self.checktoppatch(repo)
1200 insert = self.fullseriesend()
1199 insert = self.fullseriesend()
1201 with repo.wlock():
1200 with repo.wlock():
1202 try:
1201 try:
1203 # if patch file write fails, abort early
1202 # if patch file write fails, abort early
1204 p = self.opener(patchfn, "w")
1203 p = self.opener(patchfn, "w")
1205 except IOError as e:
1204 except IOError as e:
1206 raise error.Abort(_('cannot write patch "%s": %s')
1205 raise error.Abort(_('cannot write patch "%s": %s')
1207 % (patchfn, e.strerror))
1206 % (patchfn, e.strerror))
1208 try:
1207 try:
1209 defaultmsg = "[mq]: %s" % patchfn
1208 defaultmsg = "[mq]: %s" % patchfn
1210 editor = cmdutil.getcommiteditor(editform=editform)
1209 editor = cmdutil.getcommiteditor(editform=editform)
1211 if edit:
1210 if edit:
1212 def finishdesc(desc):
1211 def finishdesc(desc):
1213 if desc.rstrip():
1212 if desc.rstrip():
1214 return desc
1213 return desc
1215 else:
1214 else:
1216 return defaultmsg
1215 return defaultmsg
1217 # i18n: this message is shown in editor with "HG: " prefix
1216 # i18n: this message is shown in editor with "HG: " prefix
1218 extramsg = _('Leave message empty to use default message.')
1217 extramsg = _('Leave message empty to use default message.')
1219 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1218 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1220 extramsg=extramsg,
1219 extramsg=extramsg,
1221 editform=editform)
1220 editform=editform)
1222 commitmsg = msg
1221 commitmsg = msg
1223 else:
1222 else:
1224 commitmsg = msg or defaultmsg
1223 commitmsg = msg or defaultmsg
1225
1224
1226 n = newcommit(repo, None, commitmsg, user, date, match=match,
1225 n = newcommit(repo, None, commitmsg, user, date, match=match,
1227 force=True, editor=editor)
1226 force=True, editor=editor)
1228 if n is None:
1227 if n is None:
1229 raise error.Abort(_("repo commit failed"))
1228 raise error.Abort(_("repo commit failed"))
1230 try:
1229 try:
1231 self.fullseries[insert:insert] = [patchfn]
1230 self.fullseries[insert:insert] = [patchfn]
1232 self.applied.append(statusentry(n, patchfn))
1231 self.applied.append(statusentry(n, patchfn))
1233 self.parseseries()
1232 self.parseseries()
1234 self.seriesdirty = True
1233 self.seriesdirty = True
1235 self.applieddirty = True
1234 self.applieddirty = True
1236 nctx = repo[n]
1235 nctx = repo[n]
1237 ph = patchheader(self.join(patchfn), self.plainmode)
1236 ph = patchheader(self.join(patchfn), self.plainmode)
1238 if user:
1237 if user:
1239 ph.setuser(user)
1238 ph.setuser(user)
1240 if date:
1239 if date:
1241 ph.setdate('%s %s' % date)
1240 ph.setdate('%s %s' % date)
1242 ph.setparent(hex(nctx.p1().node()))
1241 ph.setparent(hex(nctx.p1().node()))
1243 msg = nctx.description().strip()
1242 msg = nctx.description().strip()
1244 if msg == defaultmsg.strip():
1243 if msg == defaultmsg.strip():
1245 msg = ''
1244 msg = ''
1246 ph.setmessage(msg)
1245 ph.setmessage(msg)
1247 p.write(str(ph))
1246 p.write(str(ph))
1248 if commitfiles:
1247 if commitfiles:
1249 parent = self.qparents(repo, n)
1248 parent = self.qparents(repo, n)
1250 if inclsubs:
1249 if inclsubs:
1251 self.putsubstate2changes(substatestate, changes)
1250 self.putsubstate2changes(substatestate, changes)
1252 chunks = patchmod.diff(repo, node1=parent, node2=n,
1251 chunks = patchmod.diff(repo, node1=parent, node2=n,
1253 changes=changes, opts=diffopts)
1252 changes=changes, opts=diffopts)
1254 for chunk in chunks:
1253 for chunk in chunks:
1255 p.write(chunk)
1254 p.write(chunk)
1256 p.close()
1255 p.close()
1257 r = self.qrepo()
1256 r = self.qrepo()
1258 if r:
1257 if r:
1259 r[None].add([patchfn])
1258 r[None].add([patchfn])
1260 except: # re-raises
1259 except: # re-raises
1261 repo.rollback()
1260 repo.rollback()
1262 raise
1261 raise
1263 except Exception:
1262 except Exception:
1264 patchpath = self.join(patchfn)
1263 patchpath = self.join(patchfn)
1265 try:
1264 try:
1266 os.unlink(patchpath)
1265 os.unlink(patchpath)
1267 except OSError:
1266 except OSError:
1268 self.ui.warn(_('error unlinking %s\n') % patchpath)
1267 self.ui.warn(_('error unlinking %s\n') % patchpath)
1269 raise
1268 raise
1270 self.removeundo(repo)
1269 self.removeundo(repo)
1271
1270
1272 def isapplied(self, patch):
1271 def isapplied(self, patch):
1273 """returns (index, rev, patch)"""
1272 """returns (index, rev, patch)"""
1274 for i, a in enumerate(self.applied):
1273 for i, a in enumerate(self.applied):
1275 if a.name == patch:
1274 if a.name == patch:
1276 return (i, a.node, a.name)
1275 return (i, a.node, a.name)
1277 return None
1276 return None
1278
1277
1279 # if the exact patch name does not exist, we try a few
1278 # if the exact patch name does not exist, we try a few
1280 # variations. If strict is passed, we try only #1
1279 # variations. If strict is passed, we try only #1
1281 #
1280 #
1282 # 1) a number (as string) to indicate an offset in the series file
1281 # 1) a number (as string) to indicate an offset in the series file
1283 # 2) a unique substring of the patch name was given
1282 # 2) a unique substring of the patch name was given
1284 # 3) patchname[-+]num to indicate an offset in the series file
1283 # 3) patchname[-+]num to indicate an offset in the series file
1285 def lookup(self, patch, strict=False):
1284 def lookup(self, patch, strict=False):
1286 def partialname(s):
1285 def partialname(s):
1287 if s in self.series:
1286 if s in self.series:
1288 return s
1287 return s
1289 matches = [x for x in self.series if s in x]
1288 matches = [x for x in self.series if s in x]
1290 if len(matches) > 1:
1289 if len(matches) > 1:
1291 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1290 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1292 for m in matches:
1291 for m in matches:
1293 self.ui.warn(' %s\n' % m)
1292 self.ui.warn(' %s\n' % m)
1294 return None
1293 return None
1295 if matches:
1294 if matches:
1296 return matches[0]
1295 return matches[0]
1297 if self.series and self.applied:
1296 if self.series and self.applied:
1298 if s == 'qtip':
1297 if s == 'qtip':
1299 return self.series[self.seriesend(True) - 1]
1298 return self.series[self.seriesend(True) - 1]
1300 if s == 'qbase':
1299 if s == 'qbase':
1301 return self.series[0]
1300 return self.series[0]
1302 return None
1301 return None
1303
1302
1304 if patch in self.series:
1303 if patch in self.series:
1305 return patch
1304 return patch
1306
1305
1307 if not os.path.isfile(self.join(patch)):
1306 if not os.path.isfile(self.join(patch)):
1308 try:
1307 try:
1309 sno = int(patch)
1308 sno = int(patch)
1310 except (ValueError, OverflowError):
1309 except (ValueError, OverflowError):
1311 pass
1310 pass
1312 else:
1311 else:
1313 if -len(self.series) <= sno < len(self.series):
1312 if -len(self.series) <= sno < len(self.series):
1314 return self.series[sno]
1313 return self.series[sno]
1315
1314
1316 if not strict:
1315 if not strict:
1317 res = partialname(patch)
1316 res = partialname(patch)
1318 if res:
1317 if res:
1319 return res
1318 return res
1320 minus = patch.rfind('-')
1319 minus = patch.rfind('-')
1321 if minus >= 0:
1320 if minus >= 0:
1322 res = partialname(patch[:minus])
1321 res = partialname(patch[:minus])
1323 if res:
1322 if res:
1324 i = self.series.index(res)
1323 i = self.series.index(res)
1325 try:
1324 try:
1326 off = int(patch[minus + 1:] or 1)
1325 off = int(patch[minus + 1:] or 1)
1327 except (ValueError, OverflowError):
1326 except (ValueError, OverflowError):
1328 pass
1327 pass
1329 else:
1328 else:
1330 if i - off >= 0:
1329 if i - off >= 0:
1331 return self.series[i - off]
1330 return self.series[i - off]
1332 plus = patch.rfind('+')
1331 plus = patch.rfind('+')
1333 if plus >= 0:
1332 if plus >= 0:
1334 res = partialname(patch[:plus])
1333 res = partialname(patch[:plus])
1335 if res:
1334 if res:
1336 i = self.series.index(res)
1335 i = self.series.index(res)
1337 try:
1336 try:
1338 off = int(patch[plus + 1:] or 1)
1337 off = int(patch[plus + 1:] or 1)
1339 except (ValueError, OverflowError):
1338 except (ValueError, OverflowError):
1340 pass
1339 pass
1341 else:
1340 else:
1342 if i + off < len(self.series):
1341 if i + off < len(self.series):
1343 return self.series[i + off]
1342 return self.series[i + off]
1344 raise error.Abort(_("patch %s not in series") % patch)
1343 raise error.Abort(_("patch %s not in series") % patch)
1345
1344
1346 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1345 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1347 all=False, move=False, exact=False, nobackup=False,
1346 all=False, move=False, exact=False, nobackup=False,
1348 keepchanges=False):
1347 keepchanges=False):
1349 self.checkkeepchanges(keepchanges, force)
1348 self.checkkeepchanges(keepchanges, force)
1350 diffopts = self.diffopts()
1349 diffopts = self.diffopts()
1351 with repo.wlock():
1350 with repo.wlock():
1352 heads = []
1351 heads = []
1353 for hs in repo.branchmap().itervalues():
1352 for hs in repo.branchmap().itervalues():
1354 heads.extend(hs)
1353 heads.extend(hs)
1355 if not heads:
1354 if not heads:
1356 heads = [nullid]
1355 heads = [nullid]
1357 if repo.dirstate.p1() not in heads and not exact:
1356 if repo.dirstate.p1() not in heads and not exact:
1358 self.ui.status(_("(working directory not at a head)\n"))
1357 self.ui.status(_("(working directory not at a head)\n"))
1359
1358
1360 if not self.series:
1359 if not self.series:
1361 self.ui.warn(_('no patches in series\n'))
1360 self.ui.warn(_('no patches in series\n'))
1362 return 0
1361 return 0
1363
1362
1364 # Suppose our series file is: A B C and the current 'top'
1363 # Suppose our series file is: A B C and the current 'top'
1365 # patch is B. qpush C should be performed (moving forward)
1364 # patch is B. qpush C should be performed (moving forward)
1366 # qpush B is a NOP (no change) qpush A is an error (can't
1365 # qpush B is a NOP (no change) qpush A is an error (can't
1367 # go backwards with qpush)
1366 # go backwards with qpush)
1368 if patch:
1367 if patch:
1369 patch = self.lookup(patch)
1368 patch = self.lookup(patch)
1370 info = self.isapplied(patch)
1369 info = self.isapplied(patch)
1371 if info and info[0] >= len(self.applied) - 1:
1370 if info and info[0] >= len(self.applied) - 1:
1372 self.ui.warn(
1371 self.ui.warn(
1373 _('qpush: %s is already at the top\n') % patch)
1372 _('qpush: %s is already at the top\n') % patch)
1374 return 0
1373 return 0
1375
1374
1376 pushable, reason = self.pushable(patch)
1375 pushable, reason = self.pushable(patch)
1377 if pushable:
1376 if pushable:
1378 if self.series.index(patch) < self.seriesend():
1377 if self.series.index(patch) < self.seriesend():
1379 raise error.Abort(
1378 raise error.Abort(
1380 _("cannot push to a previous patch: %s") % patch)
1379 _("cannot push to a previous patch: %s") % patch)
1381 else:
1380 else:
1382 if reason:
1381 if reason:
1383 reason = _('guarded by %s') % reason
1382 reason = _('guarded by %s') % reason
1384 else:
1383 else:
1385 reason = _('no matching guards')
1384 reason = _('no matching guards')
1386 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1385 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1387 return 1
1386 return 1
1388 elif all:
1387 elif all:
1389 patch = self.series[-1]
1388 patch = self.series[-1]
1390 if self.isapplied(patch):
1389 if self.isapplied(patch):
1391 self.ui.warn(_('all patches are currently applied\n'))
1390 self.ui.warn(_('all patches are currently applied\n'))
1392 return 0
1391 return 0
1393
1392
1394 # Following the above example, starting at 'top' of B:
1393 # Following the above example, starting at 'top' of B:
1395 # qpush should be performed (pushes C), but a subsequent
1394 # qpush should be performed (pushes C), but a subsequent
1396 # qpush without an argument is an error (nothing to
1395 # qpush without an argument is an error (nothing to
1397 # apply). This allows a loop of "...while hg qpush..." to
1396 # apply). This allows a loop of "...while hg qpush..." to
1398 # work as it detects an error when done
1397 # work as it detects an error when done
1399 start = self.seriesend()
1398 start = self.seriesend()
1400 if start == len(self.series):
1399 if start == len(self.series):
1401 self.ui.warn(_('patch series already fully applied\n'))
1400 self.ui.warn(_('patch series already fully applied\n'))
1402 return 1
1401 return 1
1403 if not force and not keepchanges:
1402 if not force and not keepchanges:
1404 self.checklocalchanges(repo, refresh=self.applied)
1403 self.checklocalchanges(repo, refresh=self.applied)
1405
1404
1406 if exact:
1405 if exact:
1407 if keepchanges:
1406 if keepchanges:
1408 raise error.Abort(
1407 raise error.Abort(
1409 _("cannot use --exact and --keep-changes together"))
1408 _("cannot use --exact and --keep-changes together"))
1410 if move:
1409 if move:
1411 raise error.Abort(_('cannot use --exact and --move '
1410 raise error.Abort(_('cannot use --exact and --move '
1412 'together'))
1411 'together'))
1413 if self.applied:
1412 if self.applied:
1414 raise error.Abort(_('cannot push --exact with applied '
1413 raise error.Abort(_('cannot push --exact with applied '
1415 'patches'))
1414 'patches'))
1416 root = self.series[start]
1415 root = self.series[start]
1417 target = patchheader(self.join(root), self.plainmode).parent
1416 target = patchheader(self.join(root), self.plainmode).parent
1418 if not target:
1417 if not target:
1419 raise error.Abort(
1418 raise error.Abort(
1420 _("%s does not have a parent recorded") % root)
1419 _("%s does not have a parent recorded") % root)
1421 if not repo[target] == repo['.']:
1420 if not repo[target] == repo['.']:
1422 hg.update(repo, target)
1421 hg.update(repo, target)
1423
1422
1424 if move:
1423 if move:
1425 if not patch:
1424 if not patch:
1426 raise error.Abort(_("please specify the patch to move"))
1425 raise error.Abort(_("please specify the patch to move"))
1427 for fullstart, rpn in enumerate(self.fullseries):
1426 for fullstart, rpn in enumerate(self.fullseries):
1428 # strip markers for patch guards
1427 # strip markers for patch guards
1429 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1428 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1430 break
1429 break
1431 for i, rpn in enumerate(self.fullseries[fullstart:]):
1430 for i, rpn in enumerate(self.fullseries[fullstart:]):
1432 # strip markers for patch guards
1431 # strip markers for patch guards
1433 if self.guard_re.split(rpn, 1)[0] == patch:
1432 if self.guard_re.split(rpn, 1)[0] == patch:
1434 break
1433 break
1435 index = fullstart + i
1434 index = fullstart + i
1436 assert index < len(self.fullseries)
1435 assert index < len(self.fullseries)
1437 fullpatch = self.fullseries[index]
1436 fullpatch = self.fullseries[index]
1438 del self.fullseries[index]
1437 del self.fullseries[index]
1439 self.fullseries.insert(fullstart, fullpatch)
1438 self.fullseries.insert(fullstart, fullpatch)
1440 self.parseseries()
1439 self.parseseries()
1441 self.seriesdirty = True
1440 self.seriesdirty = True
1442
1441
1443 self.applieddirty = True
1442 self.applieddirty = True
1444 if start > 0:
1443 if start > 0:
1445 self.checktoppatch(repo)
1444 self.checktoppatch(repo)
1446 if not patch:
1445 if not patch:
1447 patch = self.series[start]
1446 patch = self.series[start]
1448 end = start + 1
1447 end = start + 1
1449 else:
1448 else:
1450 end = self.series.index(patch, start) + 1
1449 end = self.series.index(patch, start) + 1
1451
1450
1452 tobackup = set()
1451 tobackup = set()
1453 if (not nobackup and force) or keepchanges:
1452 if (not nobackup and force) or keepchanges:
1454 status = self.checklocalchanges(repo, force=True)
1453 status = self.checklocalchanges(repo, force=True)
1455 if keepchanges:
1454 if keepchanges:
1456 tobackup.update(status.modified + status.added +
1455 tobackup.update(status.modified + status.added +
1457 status.removed + status.deleted)
1456 status.removed + status.deleted)
1458 else:
1457 else:
1459 tobackup.update(status.modified + status.added)
1458 tobackup.update(status.modified + status.added)
1460
1459
1461 s = self.series[start:end]
1460 s = self.series[start:end]
1462 all_files = set()
1461 all_files = set()
1463 try:
1462 try:
1464 if mergeq:
1463 if mergeq:
1465 ret = self.mergepatch(repo, mergeq, s, diffopts)
1464 ret = self.mergepatch(repo, mergeq, s, diffopts)
1466 else:
1465 else:
1467 ret = self.apply(repo, s, list, all_files=all_files,
1466 ret = self.apply(repo, s, list, all_files=all_files,
1468 tobackup=tobackup, keepchanges=keepchanges)
1467 tobackup=tobackup, keepchanges=keepchanges)
1469 except AbortNoCleanup:
1468 except AbortNoCleanup:
1470 raise
1469 raise
1471 except: # re-raises
1470 except: # re-raises
1472 self.ui.warn(_('cleaning up working directory...\n'))
1471 self.ui.warn(_('cleaning up working directory...\n'))
1473 cmdutil.revert(self.ui, repo, repo['.'],
1472 cmdutil.revert(self.ui, repo, repo['.'],
1474 repo.dirstate.parents(), no_backup=True)
1473 repo.dirstate.parents(), no_backup=True)
1475 # only remove unknown files that we know we touched or
1474 # only remove unknown files that we know we touched or
1476 # created while patching
1475 # created while patching
1477 for f in all_files:
1476 for f in all_files:
1478 if f not in repo.dirstate:
1477 if f not in repo.dirstate:
1479 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1478 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1480 self.ui.warn(_('done\n'))
1479 self.ui.warn(_('done\n'))
1481 raise
1480 raise
1482
1481
1483 if not self.applied:
1482 if not self.applied:
1484 return ret[0]
1483 return ret[0]
1485 top = self.applied[-1].name
1484 top = self.applied[-1].name
1486 if ret[0] and ret[0] > 1:
1485 if ret[0] and ret[0] > 1:
1487 msg = _("errors during apply, please fix and qrefresh %s\n")
1486 msg = _("errors during apply, please fix and qrefresh %s\n")
1488 self.ui.write(msg % top)
1487 self.ui.write(msg % top)
1489 else:
1488 else:
1490 self.ui.write(_("now at: %s\n") % top)
1489 self.ui.write(_("now at: %s\n") % top)
1491 return ret[0]
1490 return ret[0]
1492
1491
1493 def pop(self, repo, patch=None, force=False, update=True, all=False,
1492 def pop(self, repo, patch=None, force=False, update=True, all=False,
1494 nobackup=False, keepchanges=False):
1493 nobackup=False, keepchanges=False):
1495 self.checkkeepchanges(keepchanges, force)
1494 self.checkkeepchanges(keepchanges, force)
1496 with repo.wlock():
1495 with repo.wlock():
1497 if patch:
1496 if patch:
1498 # index, rev, patch
1497 # index, rev, patch
1499 info = self.isapplied(patch)
1498 info = self.isapplied(patch)
1500 if not info:
1499 if not info:
1501 patch = self.lookup(patch)
1500 patch = self.lookup(patch)
1502 info = self.isapplied(patch)
1501 info = self.isapplied(patch)
1503 if not info:
1502 if not info:
1504 raise error.Abort(_("patch %s is not applied") % patch)
1503 raise error.Abort(_("patch %s is not applied") % patch)
1505
1504
1506 if not self.applied:
1505 if not self.applied:
1507 # Allow qpop -a to work repeatedly,
1506 # Allow qpop -a to work repeatedly,
1508 # but not qpop without an argument
1507 # but not qpop without an argument
1509 self.ui.warn(_("no patches applied\n"))
1508 self.ui.warn(_("no patches applied\n"))
1510 return not all
1509 return not all
1511
1510
1512 if all:
1511 if all:
1513 start = 0
1512 start = 0
1514 elif patch:
1513 elif patch:
1515 start = info[0] + 1
1514 start = info[0] + 1
1516 else:
1515 else:
1517 start = len(self.applied) - 1
1516 start = len(self.applied) - 1
1518
1517
1519 if start >= len(self.applied):
1518 if start >= len(self.applied):
1520 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1519 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1521 return
1520 return
1522
1521
1523 if not update:
1522 if not update:
1524 parents = repo.dirstate.parents()
1523 parents = repo.dirstate.parents()
1525 rr = [x.node for x in self.applied]
1524 rr = [x.node for x in self.applied]
1526 for p in parents:
1525 for p in parents:
1527 if p in rr:
1526 if p in rr:
1528 self.ui.warn(_("qpop: forcing dirstate update\n"))
1527 self.ui.warn(_("qpop: forcing dirstate update\n"))
1529 update = True
1528 update = True
1530 else:
1529 else:
1531 parents = [p.node() for p in repo[None].parents()]
1530 parents = [p.node() for p in repo[None].parents()]
1532 needupdate = False
1531 needupdate = False
1533 for entry in self.applied[start:]:
1532 for entry in self.applied[start:]:
1534 if entry.node in parents:
1533 if entry.node in parents:
1535 needupdate = True
1534 needupdate = True
1536 break
1535 break
1537 update = needupdate
1536 update = needupdate
1538
1537
1539 tobackup = set()
1538 tobackup = set()
1540 if update:
1539 if update:
1541 s = self.checklocalchanges(repo, force=force or keepchanges)
1540 s = self.checklocalchanges(repo, force=force or keepchanges)
1542 if force:
1541 if force:
1543 if not nobackup:
1542 if not nobackup:
1544 tobackup.update(s.modified + s.added)
1543 tobackup.update(s.modified + s.added)
1545 elif keepchanges:
1544 elif keepchanges:
1546 tobackup.update(s.modified + s.added +
1545 tobackup.update(s.modified + s.added +
1547 s.removed + s.deleted)
1546 s.removed + s.deleted)
1548
1547
1549 self.applieddirty = True
1548 self.applieddirty = True
1550 end = len(self.applied)
1549 end = len(self.applied)
1551 rev = self.applied[start].node
1550 rev = self.applied[start].node
1552
1551
1553 try:
1552 try:
1554 heads = repo.changelog.heads(rev)
1553 heads = repo.changelog.heads(rev)
1555 except error.LookupError:
1554 except error.LookupError:
1556 node = short(rev)
1555 node = short(rev)
1557 raise error.Abort(_('trying to pop unknown node %s') % node)
1556 raise error.Abort(_('trying to pop unknown node %s') % node)
1558
1557
1559 if heads != [self.applied[-1].node]:
1558 if heads != [self.applied[-1].node]:
1560 raise error.Abort(_("popping would remove a revision not "
1559 raise error.Abort(_("popping would remove a revision not "
1561 "managed by this patch queue"))
1560 "managed by this patch queue"))
1562 if not repo[self.applied[-1].node].mutable():
1561 if not repo[self.applied[-1].node].mutable():
1563 raise error.Abort(
1562 raise error.Abort(
1564 _("popping would remove a public revision"),
1563 _("popping would remove a public revision"),
1565 hint=_("see 'hg help phases' for details"))
1564 hint=_("see 'hg help phases' for details"))
1566
1565
1567 # we know there are no local changes, so we can make a simplified
1566 # we know there are no local changes, so we can make a simplified
1568 # form of hg.update.
1567 # form of hg.update.
1569 if update:
1568 if update:
1570 qp = self.qparents(repo, rev)
1569 qp = self.qparents(repo, rev)
1571 ctx = repo[qp]
1570 ctx = repo[qp]
1572 m, a, r, d = repo.status(qp, '.')[:4]
1571 m, a, r, d = repo.status(qp, '.')[:4]
1573 if d:
1572 if d:
1574 raise error.Abort(_("deletions found between repo revs"))
1573 raise error.Abort(_("deletions found between repo revs"))
1575
1574
1576 tobackup = set(a + m + r) & tobackup
1575 tobackup = set(a + m + r) & tobackup
1577 if keepchanges and tobackup:
1576 if keepchanges and tobackup:
1578 raise error.Abort(_("local changes found, qrefresh first"))
1577 raise error.Abort(_("local changes found, qrefresh first"))
1579 self.backup(repo, tobackup)
1578 self.backup(repo, tobackup)
1580 repo.dirstate.beginparentchange()
1579 repo.dirstate.beginparentchange()
1581 for f in a:
1580 for f in a:
1582 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1581 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1583 repo.dirstate.drop(f)
1582 repo.dirstate.drop(f)
1584 for f in m + r:
1583 for f in m + r:
1585 fctx = ctx[f]
1584 fctx = ctx[f]
1586 repo.wwrite(f, fctx.data(), fctx.flags())
1585 repo.wwrite(f, fctx.data(), fctx.flags())
1587 repo.dirstate.normal(f)
1586 repo.dirstate.normal(f)
1588 repo.setparents(qp, nullid)
1587 repo.setparents(qp, nullid)
1589 repo.dirstate.endparentchange()
1588 repo.dirstate.endparentchange()
1590 for patch in reversed(self.applied[start:end]):
1589 for patch in reversed(self.applied[start:end]):
1591 self.ui.status(_("popping %s\n") % patch.name)
1590 self.ui.status(_("popping %s\n") % patch.name)
1592 del self.applied[start:end]
1591 del self.applied[start:end]
1593 strip(self.ui, repo, [rev], update=False, backup=False)
1592 strip(self.ui, repo, [rev], update=False, backup=False)
1594 for s, state in repo['.'].substate.items():
1593 for s, state in repo['.'].substate.items():
1595 repo['.'].sub(s).get(state)
1594 repo['.'].sub(s).get(state)
1596 if self.applied:
1595 if self.applied:
1597 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1596 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1598 else:
1597 else:
1599 self.ui.write(_("patch queue now empty\n"))
1598 self.ui.write(_("patch queue now empty\n"))
1600
1599
1601 def diff(self, repo, pats, opts):
1600 def diff(self, repo, pats, opts):
1602 top, patch = self.checktoppatch(repo)
1601 top, patch = self.checktoppatch(repo)
1603 if not top:
1602 if not top:
1604 self.ui.write(_("no patches applied\n"))
1603 self.ui.write(_("no patches applied\n"))
1605 return
1604 return
1606 qp = self.qparents(repo, top)
1605 qp = self.qparents(repo, top)
1607 if opts.get('reverse'):
1606 if opts.get('reverse'):
1608 node1, node2 = None, qp
1607 node1, node2 = None, qp
1609 else:
1608 else:
1610 node1, node2 = qp, None
1609 node1, node2 = qp, None
1611 diffopts = self.diffopts(opts, patch)
1610 diffopts = self.diffopts(opts, patch)
1612 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1611 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1613
1612
1614 def refresh(self, repo, pats=None, **opts):
1613 def refresh(self, repo, pats=None, **opts):
1615 if not self.applied:
1614 if not self.applied:
1616 self.ui.write(_("no patches applied\n"))
1615 self.ui.write(_("no patches applied\n"))
1617 return 1
1616 return 1
1618 msg = opts.get('msg', '').rstrip()
1617 msg = opts.get('msg', '').rstrip()
1619 edit = opts.get('edit')
1618 edit = opts.get('edit')
1620 editform = opts.get('editform', 'mq.qrefresh')
1619 editform = opts.get('editform', 'mq.qrefresh')
1621 newuser = opts.get('user')
1620 newuser = opts.get('user')
1622 newdate = opts.get('date')
1621 newdate = opts.get('date')
1623 if newdate:
1622 if newdate:
1624 newdate = '%d %d' % util.parsedate(newdate)
1623 newdate = '%d %d' % util.parsedate(newdate)
1625 wlock = repo.wlock()
1624 wlock = repo.wlock()
1626
1625
1627 try:
1626 try:
1628 self.checktoppatch(repo)
1627 self.checktoppatch(repo)
1629 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1628 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1630 if repo.changelog.heads(top) != [top]:
1629 if repo.changelog.heads(top) != [top]:
1631 raise error.Abort(_("cannot qrefresh a revision with children"))
1630 raise error.Abort(_("cannot qrefresh a revision with children"))
1632 if not repo[top].mutable():
1631 if not repo[top].mutable():
1633 raise error.Abort(_("cannot qrefresh public revision"),
1632 raise error.Abort(_("cannot qrefresh public revision"),
1634 hint=_("see 'hg help phases' for details"))
1633 hint=_("see 'hg help phases' for details"))
1635
1634
1636 cparents = repo.changelog.parents(top)
1635 cparents = repo.changelog.parents(top)
1637 patchparent = self.qparents(repo, top)
1636 patchparent = self.qparents(repo, top)
1638
1637
1639 inclsubs = checksubstate(repo, hex(patchparent))
1638 inclsubs = checksubstate(repo, hex(patchparent))
1640 if inclsubs:
1639 if inclsubs:
1641 substatestate = repo.dirstate['.hgsubstate']
1640 substatestate = repo.dirstate['.hgsubstate']
1642
1641
1643 ph = patchheader(self.join(patchfn), self.plainmode)
1642 ph = patchheader(self.join(patchfn), self.plainmode)
1644 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1643 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1645 if newuser:
1644 if newuser:
1646 ph.setuser(newuser)
1645 ph.setuser(newuser)
1647 if newdate:
1646 if newdate:
1648 ph.setdate(newdate)
1647 ph.setdate(newdate)
1649 ph.setparent(hex(patchparent))
1648 ph.setparent(hex(patchparent))
1650
1649
1651 # only commit new patch when write is complete
1650 # only commit new patch when write is complete
1652 patchf = self.opener(patchfn, 'w', atomictemp=True)
1651 patchf = self.opener(patchfn, 'w', atomictemp=True)
1653
1652
1654 # update the dirstate in place, strip off the qtip commit
1653 # update the dirstate in place, strip off the qtip commit
1655 # and then commit.
1654 # and then commit.
1656 #
1655 #
1657 # this should really read:
1656 # this should really read:
1658 # mm, dd, aa = repo.status(top, patchparent)[:3]
1657 # mm, dd, aa = repo.status(top, patchparent)[:3]
1659 # but we do it backwards to take advantage of manifest/changelog
1658 # but we do it backwards to take advantage of manifest/changelog
1660 # caching against the next repo.status call
1659 # caching against the next repo.status call
1661 mm, aa, dd = repo.status(patchparent, top)[:3]
1660 mm, aa, dd = repo.status(patchparent, top)[:3]
1662 changes = repo.changelog.read(top)
1661 changes = repo.changelog.read(top)
1663 man = repo.manifestlog[changes[0]].read()
1662 man = repo.manifestlog[changes[0]].read()
1664 aaa = aa[:]
1663 aaa = aa[:]
1665 matchfn = scmutil.match(repo[None], pats, opts)
1664 matchfn = scmutil.match(repo[None], pats, opts)
1666 # in short mode, we only diff the files included in the
1665 # in short mode, we only diff the files included in the
1667 # patch already plus specified files
1666 # patch already plus specified files
1668 if opts.get('short'):
1667 if opts.get('short'):
1669 # if amending a patch, we start with existing
1668 # if amending a patch, we start with existing
1670 # files plus specified files - unfiltered
1669 # files plus specified files - unfiltered
1671 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1670 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1672 # filter with include/exclude options
1671 # filter with include/exclude options
1673 matchfn = scmutil.match(repo[None], opts=opts)
1672 matchfn = scmutil.match(repo[None], opts=opts)
1674 else:
1673 else:
1675 match = scmutil.matchall(repo)
1674 match = scmutil.matchall(repo)
1676 m, a, r, d = repo.status(match=match)[:4]
1675 m, a, r, d = repo.status(match=match)[:4]
1677 mm = set(mm)
1676 mm = set(mm)
1678 aa = set(aa)
1677 aa = set(aa)
1679 dd = set(dd)
1678 dd = set(dd)
1680
1679
1681 # we might end up with files that were added between
1680 # we might end up with files that were added between
1682 # qtip and the dirstate parent, but then changed in the
1681 # qtip and the dirstate parent, but then changed in the
1683 # local dirstate. in this case, we want them to only
1682 # local dirstate. in this case, we want them to only
1684 # show up in the added section
1683 # show up in the added section
1685 for x in m:
1684 for x in m:
1686 if x not in aa:
1685 if x not in aa:
1687 mm.add(x)
1686 mm.add(x)
1688 # we might end up with files added by the local dirstate that
1687 # we might end up with files added by the local dirstate that
1689 # were deleted by the patch. In this case, they should only
1688 # were deleted by the patch. In this case, they should only
1690 # show up in the changed section.
1689 # show up in the changed section.
1691 for x in a:
1690 for x in a:
1692 if x in dd:
1691 if x in dd:
1693 dd.remove(x)
1692 dd.remove(x)
1694 mm.add(x)
1693 mm.add(x)
1695 else:
1694 else:
1696 aa.add(x)
1695 aa.add(x)
1697 # make sure any files deleted in the local dirstate
1696 # make sure any files deleted in the local dirstate
1698 # are not in the add or change column of the patch
1697 # are not in the add or change column of the patch
1699 forget = []
1698 forget = []
1700 for x in d + r:
1699 for x in d + r:
1701 if x in aa:
1700 if x in aa:
1702 aa.remove(x)
1701 aa.remove(x)
1703 forget.append(x)
1702 forget.append(x)
1704 continue
1703 continue
1705 else:
1704 else:
1706 mm.discard(x)
1705 mm.discard(x)
1707 dd.add(x)
1706 dd.add(x)
1708
1707
1709 m = list(mm)
1708 m = list(mm)
1710 r = list(dd)
1709 r = list(dd)
1711 a = list(aa)
1710 a = list(aa)
1712
1711
1713 # create 'match' that includes the files to be recommitted.
1712 # create 'match' that includes the files to be recommitted.
1714 # apply matchfn via repo.status to ensure correct case handling.
1713 # apply matchfn via repo.status to ensure correct case handling.
1715 cm, ca, cr, cd = repo.status(patchparent, match=matchfn)[:4]
1714 cm, ca, cr, cd = repo.status(patchparent, match=matchfn)[:4]
1716 allmatches = set(cm + ca + cr + cd)
1715 allmatches = set(cm + ca + cr + cd)
1717 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1716 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1718
1717
1719 files = set(inclsubs)
1718 files = set(inclsubs)
1720 for x in refreshchanges:
1719 for x in refreshchanges:
1721 files.update(x)
1720 files.update(x)
1722 match = scmutil.matchfiles(repo, files)
1721 match = scmutil.matchfiles(repo, files)
1723
1722
1724 bmlist = repo[top].bookmarks()
1723 bmlist = repo[top].bookmarks()
1725
1724
1726 dsguard = None
1725 dsguard = None
1727 try:
1726 try:
1728 dsguard = cmdutil.dirstateguard(repo, 'mq.refresh')
1727 dsguard = cmdutil.dirstateguard(repo, 'mq.refresh')
1729 if diffopts.git or diffopts.upgrade:
1728 if diffopts.git or diffopts.upgrade:
1730 copies = {}
1729 copies = {}
1731 for dst in a:
1730 for dst in a:
1732 src = repo.dirstate.copied(dst)
1731 src = repo.dirstate.copied(dst)
1733 # during qfold, the source file for copies may
1732 # during qfold, the source file for copies may
1734 # be removed. Treat this as a simple add.
1733 # be removed. Treat this as a simple add.
1735 if src is not None and src in repo.dirstate:
1734 if src is not None and src in repo.dirstate:
1736 copies.setdefault(src, []).append(dst)
1735 copies.setdefault(src, []).append(dst)
1737 repo.dirstate.add(dst)
1736 repo.dirstate.add(dst)
1738 # remember the copies between patchparent and qtip
1737 # remember the copies between patchparent and qtip
1739 for dst in aaa:
1738 for dst in aaa:
1740 f = repo.file(dst)
1739 f = repo.file(dst)
1741 src = f.renamed(man[dst])
1740 src = f.renamed(man[dst])
1742 if src:
1741 if src:
1743 copies.setdefault(src[0], []).extend(
1742 copies.setdefault(src[0], []).extend(
1744 copies.get(dst, []))
1743 copies.get(dst, []))
1745 if dst in a:
1744 if dst in a:
1746 copies[src[0]].append(dst)
1745 copies[src[0]].append(dst)
1747 # we can't copy a file created by the patch itself
1746 # we can't copy a file created by the patch itself
1748 if dst in copies:
1747 if dst in copies:
1749 del copies[dst]
1748 del copies[dst]
1750 for src, dsts in copies.iteritems():
1749 for src, dsts in copies.iteritems():
1751 for dst in dsts:
1750 for dst in dsts:
1752 repo.dirstate.copy(src, dst)
1751 repo.dirstate.copy(src, dst)
1753 else:
1752 else:
1754 for dst in a:
1753 for dst in a:
1755 repo.dirstate.add(dst)
1754 repo.dirstate.add(dst)
1756 # Drop useless copy information
1755 # Drop useless copy information
1757 for f in list(repo.dirstate.copies()):
1756 for f in list(repo.dirstate.copies()):
1758 repo.dirstate.copy(None, f)
1757 repo.dirstate.copy(None, f)
1759 for f in r:
1758 for f in r:
1760 repo.dirstate.remove(f)
1759 repo.dirstate.remove(f)
1761 # if the patch excludes a modified file, mark that
1760 # if the patch excludes a modified file, mark that
1762 # file with mtime=0 so status can see it.
1761 # file with mtime=0 so status can see it.
1763 mm = []
1762 mm = []
1764 for i in xrange(len(m) - 1, -1, -1):
1763 for i in xrange(len(m) - 1, -1, -1):
1765 if not matchfn(m[i]):
1764 if not matchfn(m[i]):
1766 mm.append(m[i])
1765 mm.append(m[i])
1767 del m[i]
1766 del m[i]
1768 for f in m:
1767 for f in m:
1769 repo.dirstate.normal(f)
1768 repo.dirstate.normal(f)
1770 for f in mm:
1769 for f in mm:
1771 repo.dirstate.normallookup(f)
1770 repo.dirstate.normallookup(f)
1772 for f in forget:
1771 for f in forget:
1773 repo.dirstate.drop(f)
1772 repo.dirstate.drop(f)
1774
1773
1775 user = ph.user or changes[1]
1774 user = ph.user or changes[1]
1776
1775
1777 oldphase = repo[top].phase()
1776 oldphase = repo[top].phase()
1778
1777
1779 # assumes strip can roll itself back if interrupted
1778 # assumes strip can roll itself back if interrupted
1780 repo.setparents(*cparents)
1779 repo.setparents(*cparents)
1781 self.applied.pop()
1780 self.applied.pop()
1782 self.applieddirty = True
1781 self.applieddirty = True
1783 strip(self.ui, repo, [top], update=False, backup=False)
1782 strip(self.ui, repo, [top], update=False, backup=False)
1784 dsguard.close()
1783 dsguard.close()
1785 finally:
1784 finally:
1786 release(dsguard)
1785 release(dsguard)
1787
1786
1788 try:
1787 try:
1789 # might be nice to attempt to roll back strip after this
1788 # might be nice to attempt to roll back strip after this
1790
1789
1791 defaultmsg = "[mq]: %s" % patchfn
1790 defaultmsg = "[mq]: %s" % patchfn
1792 editor = cmdutil.getcommiteditor(editform=editform)
1791 editor = cmdutil.getcommiteditor(editform=editform)
1793 if edit:
1792 if edit:
1794 def finishdesc(desc):
1793 def finishdesc(desc):
1795 if desc.rstrip():
1794 if desc.rstrip():
1796 ph.setmessage(desc)
1795 ph.setmessage(desc)
1797 return desc
1796 return desc
1798 return defaultmsg
1797 return defaultmsg
1799 # i18n: this message is shown in editor with "HG: " prefix
1798 # i18n: this message is shown in editor with "HG: " prefix
1800 extramsg = _('Leave message empty to use default message.')
1799 extramsg = _('Leave message empty to use default message.')
1801 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1800 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1802 extramsg=extramsg,
1801 extramsg=extramsg,
1803 editform=editform)
1802 editform=editform)
1804 message = msg or "\n".join(ph.message)
1803 message = msg or "\n".join(ph.message)
1805 elif not msg:
1804 elif not msg:
1806 if not ph.message:
1805 if not ph.message:
1807 message = defaultmsg
1806 message = defaultmsg
1808 else:
1807 else:
1809 message = "\n".join(ph.message)
1808 message = "\n".join(ph.message)
1810 else:
1809 else:
1811 message = msg
1810 message = msg
1812 ph.setmessage(msg)
1811 ph.setmessage(msg)
1813
1812
1814 # Ensure we create a new changeset in the same phase than
1813 # Ensure we create a new changeset in the same phase than
1815 # the old one.
1814 # the old one.
1816 lock = tr = None
1815 lock = tr = None
1817 try:
1816 try:
1818 lock = repo.lock()
1817 lock = repo.lock()
1819 tr = repo.transaction('mq')
1818 tr = repo.transaction('mq')
1820 n = newcommit(repo, oldphase, message, user, ph.date,
1819 n = newcommit(repo, oldphase, message, user, ph.date,
1821 match=match, force=True, editor=editor)
1820 match=match, force=True, editor=editor)
1822 # only write patch after a successful commit
1821 # only write patch after a successful commit
1823 c = [list(x) for x in refreshchanges]
1822 c = [list(x) for x in refreshchanges]
1824 if inclsubs:
1823 if inclsubs:
1825 self.putsubstate2changes(substatestate, c)
1824 self.putsubstate2changes(substatestate, c)
1826 chunks = patchmod.diff(repo, patchparent,
1825 chunks = patchmod.diff(repo, patchparent,
1827 changes=c, opts=diffopts)
1826 changes=c, opts=diffopts)
1828 comments = str(ph)
1827 comments = str(ph)
1829 if comments:
1828 if comments:
1830 patchf.write(comments)
1829 patchf.write(comments)
1831 for chunk in chunks:
1830 for chunk in chunks:
1832 patchf.write(chunk)
1831 patchf.write(chunk)
1833 patchf.close()
1832 patchf.close()
1834
1833
1835 marks = repo._bookmarks
1834 marks = repo._bookmarks
1836 for bm in bmlist:
1835 for bm in bmlist:
1837 marks[bm] = n
1836 marks[bm] = n
1838 marks.recordchange(tr)
1837 marks.recordchange(tr)
1839 tr.close()
1838 tr.close()
1840
1839
1841 self.applied.append(statusentry(n, patchfn))
1840 self.applied.append(statusentry(n, patchfn))
1842 finally:
1841 finally:
1843 lockmod.release(tr, lock)
1842 lockmod.release(tr, lock)
1844 except: # re-raises
1843 except: # re-raises
1845 ctx = repo[cparents[0]]
1844 ctx = repo[cparents[0]]
1846 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1845 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1847 self.savedirty()
1846 self.savedirty()
1848 self.ui.warn(_('qrefresh interrupted while patch was popped! '
1847 self.ui.warn(_('qrefresh interrupted while patch was popped! '
1849 '(revert --all, qpush to recover)\n'))
1848 '(revert --all, qpush to recover)\n'))
1850 raise
1849 raise
1851 finally:
1850 finally:
1852 wlock.release()
1851 wlock.release()
1853 self.removeundo(repo)
1852 self.removeundo(repo)
1854
1853
1855 def init(self, repo, create=False):
1854 def init(self, repo, create=False):
1856 if not create and os.path.isdir(self.path):
1855 if not create and os.path.isdir(self.path):
1857 raise error.Abort(_("patch queue directory already exists"))
1856 raise error.Abort(_("patch queue directory already exists"))
1858 try:
1857 try:
1859 os.mkdir(self.path)
1858 os.mkdir(self.path)
1860 except OSError as inst:
1859 except OSError as inst:
1861 if inst.errno != errno.EEXIST or not create:
1860 if inst.errno != errno.EEXIST or not create:
1862 raise
1861 raise
1863 if create:
1862 if create:
1864 return self.qrepo(create=True)
1863 return self.qrepo(create=True)
1865
1864
1866 def unapplied(self, repo, patch=None):
1865 def unapplied(self, repo, patch=None):
1867 if patch and patch not in self.series:
1866 if patch and patch not in self.series:
1868 raise error.Abort(_("patch %s is not in series file") % patch)
1867 raise error.Abort(_("patch %s is not in series file") % patch)
1869 if not patch:
1868 if not patch:
1870 start = self.seriesend()
1869 start = self.seriesend()
1871 else:
1870 else:
1872 start = self.series.index(patch) + 1
1871 start = self.series.index(patch) + 1
1873 unapplied = []
1872 unapplied = []
1874 for i in xrange(start, len(self.series)):
1873 for i in xrange(start, len(self.series)):
1875 pushable, reason = self.pushable(i)
1874 pushable, reason = self.pushable(i)
1876 if pushable:
1875 if pushable:
1877 unapplied.append((i, self.series[i]))
1876 unapplied.append((i, self.series[i]))
1878 self.explainpushable(i)
1877 self.explainpushable(i)
1879 return unapplied
1878 return unapplied
1880
1879
1881 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1880 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1882 summary=False):
1881 summary=False):
1883 def displayname(pfx, patchname, state):
1882 def displayname(pfx, patchname, state):
1884 if pfx:
1883 if pfx:
1885 self.ui.write(pfx)
1884 self.ui.write(pfx)
1886 if summary:
1885 if summary:
1887 ph = patchheader(self.join(patchname), self.plainmode)
1886 ph = patchheader(self.join(patchname), self.plainmode)
1888 if ph.message:
1887 if ph.message:
1889 msg = ph.message[0]
1888 msg = ph.message[0]
1890 else:
1889 else:
1891 msg = ''
1890 msg = ''
1892
1891
1893 if self.ui.formatted():
1892 if self.ui.formatted():
1894 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1893 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1895 if width > 0:
1894 if width > 0:
1896 msg = util.ellipsis(msg, width)
1895 msg = util.ellipsis(msg, width)
1897 else:
1896 else:
1898 msg = ''
1897 msg = ''
1899 self.ui.write(patchname, label='qseries.' + state)
1898 self.ui.write(patchname, label='qseries.' + state)
1900 self.ui.write(': ')
1899 self.ui.write(': ')
1901 self.ui.write(msg, label='qseries.message.' + state)
1900 self.ui.write(msg, label='qseries.message.' + state)
1902 else:
1901 else:
1903 self.ui.write(patchname, label='qseries.' + state)
1902 self.ui.write(patchname, label='qseries.' + state)
1904 self.ui.write('\n')
1903 self.ui.write('\n')
1905
1904
1906 applied = set([p.name for p in self.applied])
1905 applied = set([p.name for p in self.applied])
1907 if length is None:
1906 if length is None:
1908 length = len(self.series) - start
1907 length = len(self.series) - start
1909 if not missing:
1908 if not missing:
1910 if self.ui.verbose:
1909 if self.ui.verbose:
1911 idxwidth = len(str(start + length - 1))
1910 idxwidth = len(str(start + length - 1))
1912 for i in xrange(start, start + length):
1911 for i in xrange(start, start + length):
1913 patch = self.series[i]
1912 patch = self.series[i]
1914 if patch in applied:
1913 if patch in applied:
1915 char, state = 'A', 'applied'
1914 char, state = 'A', 'applied'
1916 elif self.pushable(i)[0]:
1915 elif self.pushable(i)[0]:
1917 char, state = 'U', 'unapplied'
1916 char, state = 'U', 'unapplied'
1918 else:
1917 else:
1919 char, state = 'G', 'guarded'
1918 char, state = 'G', 'guarded'
1920 pfx = ''
1919 pfx = ''
1921 if self.ui.verbose:
1920 if self.ui.verbose:
1922 pfx = '%*d %s ' % (idxwidth, i, char)
1921 pfx = '%*d %s ' % (idxwidth, i, char)
1923 elif status and status != char:
1922 elif status and status != char:
1924 continue
1923 continue
1925 displayname(pfx, patch, state)
1924 displayname(pfx, patch, state)
1926 else:
1925 else:
1927 msng_list = []
1926 msng_list = []
1928 for root, dirs, files in os.walk(self.path):
1927 for root, dirs, files in os.walk(self.path):
1929 d = root[len(self.path) + 1:]
1928 d = root[len(self.path) + 1:]
1930 for f in files:
1929 for f in files:
1931 fl = os.path.join(d, f)
1930 fl = os.path.join(d, f)
1932 if (fl not in self.series and
1931 if (fl not in self.series and
1933 fl not in (self.statuspath, self.seriespath,
1932 fl not in (self.statuspath, self.seriespath,
1934 self.guardspath)
1933 self.guardspath)
1935 and not fl.startswith('.')):
1934 and not fl.startswith('.')):
1936 msng_list.append(fl)
1935 msng_list.append(fl)
1937 for x in sorted(msng_list):
1936 for x in sorted(msng_list):
1938 pfx = self.ui.verbose and ('D ') or ''
1937 pfx = self.ui.verbose and ('D ') or ''
1939 displayname(pfx, x, 'missing')
1938 displayname(pfx, x, 'missing')
1940
1939
1941 def issaveline(self, l):
1940 def issaveline(self, l):
1942 if l.name == '.hg.patches.save.line':
1941 if l.name == '.hg.patches.save.line':
1943 return True
1942 return True
1944
1943
1945 def qrepo(self, create=False):
1944 def qrepo(self, create=False):
1946 ui = self.baseui.copy()
1945 ui = self.baseui.copy()
1947 if create or os.path.isdir(self.join(".hg")):
1946 if create or os.path.isdir(self.join(".hg")):
1948 return hg.repository(ui, path=self.path, create=create)
1947 return hg.repository(ui, path=self.path, create=create)
1949
1948
1950 def restore(self, repo, rev, delete=None, qupdate=None):
1949 def restore(self, repo, rev, delete=None, qupdate=None):
1951 desc = repo[rev].description().strip()
1950 desc = repo[rev].description().strip()
1952 lines = desc.splitlines()
1951 lines = desc.splitlines()
1953 i = 0
1952 i = 0
1954 datastart = None
1953 datastart = None
1955 series = []
1954 series = []
1956 applied = []
1955 applied = []
1957 qpp = None
1956 qpp = None
1958 for i, line in enumerate(lines):
1957 for i, line in enumerate(lines):
1959 if line == 'Patch Data:':
1958 if line == 'Patch Data:':
1960 datastart = i + 1
1959 datastart = i + 1
1961 elif line.startswith('Dirstate:'):
1960 elif line.startswith('Dirstate:'):
1962 l = line.rstrip()
1961 l = line.rstrip()
1963 l = l[10:].split(' ')
1962 l = l[10:].split(' ')
1964 qpp = [bin(x) for x in l]
1963 qpp = [bin(x) for x in l]
1965 elif datastart is not None:
1964 elif datastart is not None:
1966 l = line.rstrip()
1965 l = line.rstrip()
1967 n, name = l.split(':', 1)
1966 n, name = l.split(':', 1)
1968 if n:
1967 if n:
1969 applied.append(statusentry(bin(n), name))
1968 applied.append(statusentry(bin(n), name))
1970 else:
1969 else:
1971 series.append(l)
1970 series.append(l)
1972 if datastart is None:
1971 if datastart is None:
1973 self.ui.warn(_("no saved patch data found\n"))
1972 self.ui.warn(_("no saved patch data found\n"))
1974 return 1
1973 return 1
1975 self.ui.warn(_("restoring status: %s\n") % lines[0])
1974 self.ui.warn(_("restoring status: %s\n") % lines[0])
1976 self.fullseries = series
1975 self.fullseries = series
1977 self.applied = applied
1976 self.applied = applied
1978 self.parseseries()
1977 self.parseseries()
1979 self.seriesdirty = True
1978 self.seriesdirty = True
1980 self.applieddirty = True
1979 self.applieddirty = True
1981 heads = repo.changelog.heads()
1980 heads = repo.changelog.heads()
1982 if delete:
1981 if delete:
1983 if rev not in heads:
1982 if rev not in heads:
1984 self.ui.warn(_("save entry has children, leaving it alone\n"))
1983 self.ui.warn(_("save entry has children, leaving it alone\n"))
1985 else:
1984 else:
1986 self.ui.warn(_("removing save entry %s\n") % short(rev))
1985 self.ui.warn(_("removing save entry %s\n") % short(rev))
1987 pp = repo.dirstate.parents()
1986 pp = repo.dirstate.parents()
1988 if rev in pp:
1987 if rev in pp:
1989 update = True
1988 update = True
1990 else:
1989 else:
1991 update = False
1990 update = False
1992 strip(self.ui, repo, [rev], update=update, backup=False)
1991 strip(self.ui, repo, [rev], update=update, backup=False)
1993 if qpp:
1992 if qpp:
1994 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1993 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1995 (short(qpp[0]), short(qpp[1])))
1994 (short(qpp[0]), short(qpp[1])))
1996 if qupdate:
1995 if qupdate:
1997 self.ui.status(_("updating queue directory\n"))
1996 self.ui.status(_("updating queue directory\n"))
1998 r = self.qrepo()
1997 r = self.qrepo()
1999 if not r:
1998 if not r:
2000 self.ui.warn(_("unable to load queue repository\n"))
1999 self.ui.warn(_("unable to load queue repository\n"))
2001 return 1
2000 return 1
2002 hg.clean(r, qpp[0])
2001 hg.clean(r, qpp[0])
2003
2002
2004 def save(self, repo, msg=None):
2003 def save(self, repo, msg=None):
2005 if not self.applied:
2004 if not self.applied:
2006 self.ui.warn(_("save: no patches applied, exiting\n"))
2005 self.ui.warn(_("save: no patches applied, exiting\n"))
2007 return 1
2006 return 1
2008 if self.issaveline(self.applied[-1]):
2007 if self.issaveline(self.applied[-1]):
2009 self.ui.warn(_("status is already saved\n"))
2008 self.ui.warn(_("status is already saved\n"))
2010 return 1
2009 return 1
2011
2010
2012 if not msg:
2011 if not msg:
2013 msg = _("hg patches saved state")
2012 msg = _("hg patches saved state")
2014 else:
2013 else:
2015 msg = "hg patches: " + msg.rstrip('\r\n')
2014 msg = "hg patches: " + msg.rstrip('\r\n')
2016 r = self.qrepo()
2015 r = self.qrepo()
2017 if r:
2016 if r:
2018 pp = r.dirstate.parents()
2017 pp = r.dirstate.parents()
2019 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
2018 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
2020 msg += "\n\nPatch Data:\n"
2019 msg += "\n\nPatch Data:\n"
2021 msg += ''.join('%s\n' % x for x in self.applied)
2020 msg += ''.join('%s\n' % x for x in self.applied)
2022 msg += ''.join(':%s\n' % x for x in self.fullseries)
2021 msg += ''.join(':%s\n' % x for x in self.fullseries)
2023 n = repo.commit(msg, force=True)
2022 n = repo.commit(msg, force=True)
2024 if not n:
2023 if not n:
2025 self.ui.warn(_("repo commit failed\n"))
2024 self.ui.warn(_("repo commit failed\n"))
2026 return 1
2025 return 1
2027 self.applied.append(statusentry(n, '.hg.patches.save.line'))
2026 self.applied.append(statusentry(n, '.hg.patches.save.line'))
2028 self.applieddirty = True
2027 self.applieddirty = True
2029 self.removeundo(repo)
2028 self.removeundo(repo)
2030
2029
2031 def fullseriesend(self):
2030 def fullseriesend(self):
2032 if self.applied:
2031 if self.applied:
2033 p = self.applied[-1].name
2032 p = self.applied[-1].name
2034 end = self.findseries(p)
2033 end = self.findseries(p)
2035 if end is None:
2034 if end is None:
2036 return len(self.fullseries)
2035 return len(self.fullseries)
2037 return end + 1
2036 return end + 1
2038 return 0
2037 return 0
2039
2038
2040 def seriesend(self, all_patches=False):
2039 def seriesend(self, all_patches=False):
2041 """If all_patches is False, return the index of the next pushable patch
2040 """If all_patches is False, return the index of the next pushable patch
2042 in the series, or the series length. If all_patches is True, return the
2041 in the series, or the series length. If all_patches is True, return the
2043 index of the first patch past the last applied one.
2042 index of the first patch past the last applied one.
2044 """
2043 """
2045 end = 0
2044 end = 0
2046 def nextpatch(start):
2045 def nextpatch(start):
2047 if all_patches or start >= len(self.series):
2046 if all_patches or start >= len(self.series):
2048 return start
2047 return start
2049 for i in xrange(start, len(self.series)):
2048 for i in xrange(start, len(self.series)):
2050 p, reason = self.pushable(i)
2049 p, reason = self.pushable(i)
2051 if p:
2050 if p:
2052 return i
2051 return i
2053 self.explainpushable(i)
2052 self.explainpushable(i)
2054 return len(self.series)
2053 return len(self.series)
2055 if self.applied:
2054 if self.applied:
2056 p = self.applied[-1].name
2055 p = self.applied[-1].name
2057 try:
2056 try:
2058 end = self.series.index(p)
2057 end = self.series.index(p)
2059 except ValueError:
2058 except ValueError:
2060 return 0
2059 return 0
2061 return nextpatch(end + 1)
2060 return nextpatch(end + 1)
2062 return nextpatch(end)
2061 return nextpatch(end)
2063
2062
2064 def appliedname(self, index):
2063 def appliedname(self, index):
2065 pname = self.applied[index].name
2064 pname = self.applied[index].name
2066 if not self.ui.verbose:
2065 if not self.ui.verbose:
2067 p = pname
2066 p = pname
2068 else:
2067 else:
2069 p = str(self.series.index(pname)) + " " + pname
2068 p = str(self.series.index(pname)) + " " + pname
2070 return p
2069 return p
2071
2070
2072 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
2071 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
2073 force=None, git=False):
2072 force=None, git=False):
2074 def checkseries(patchname):
2073 def checkseries(patchname):
2075 if patchname in self.series:
2074 if patchname in self.series:
2076 raise error.Abort(_('patch %s is already in the series file')
2075 raise error.Abort(_('patch %s is already in the series file')
2077 % patchname)
2076 % patchname)
2078
2077
2079 if rev:
2078 if rev:
2080 if files:
2079 if files:
2081 raise error.Abort(_('option "-r" not valid when importing '
2080 raise error.Abort(_('option "-r" not valid when importing '
2082 'files'))
2081 'files'))
2083 rev = scmutil.revrange(repo, rev)
2082 rev = scmutil.revrange(repo, rev)
2084 rev.sort(reverse=True)
2083 rev.sort(reverse=True)
2085 elif not files:
2084 elif not files:
2086 raise error.Abort(_('no files or revisions specified'))
2085 raise error.Abort(_('no files or revisions specified'))
2087 if (len(files) > 1 or len(rev) > 1) and patchname:
2086 if (len(files) > 1 or len(rev) > 1) and patchname:
2088 raise error.Abort(_('option "-n" not valid when importing multiple '
2087 raise error.Abort(_('option "-n" not valid when importing multiple '
2089 'patches'))
2088 'patches'))
2090 imported = []
2089 imported = []
2091 if rev:
2090 if rev:
2092 # If mq patches are applied, we can only import revisions
2091 # If mq patches are applied, we can only import revisions
2093 # that form a linear path to qbase.
2092 # that form a linear path to qbase.
2094 # Otherwise, they should form a linear path to a head.
2093 # Otherwise, they should form a linear path to a head.
2095 heads = repo.changelog.heads(repo.changelog.node(rev.first()))
2094 heads = repo.changelog.heads(repo.changelog.node(rev.first()))
2096 if len(heads) > 1:
2095 if len(heads) > 1:
2097 raise error.Abort(_('revision %d is the root of more than one '
2096 raise error.Abort(_('revision %d is the root of more than one '
2098 'branch') % rev.last())
2097 'branch') % rev.last())
2099 if self.applied:
2098 if self.applied:
2100 base = repo.changelog.node(rev.first())
2099 base = repo.changelog.node(rev.first())
2101 if base in [n.node for n in self.applied]:
2100 if base in [n.node for n in self.applied]:
2102 raise error.Abort(_('revision %d is already managed')
2101 raise error.Abort(_('revision %d is already managed')
2103 % rev.first())
2102 % rev.first())
2104 if heads != [self.applied[-1].node]:
2103 if heads != [self.applied[-1].node]:
2105 raise error.Abort(_('revision %d is not the parent of '
2104 raise error.Abort(_('revision %d is not the parent of '
2106 'the queue') % rev.first())
2105 'the queue') % rev.first())
2107 base = repo.changelog.rev(self.applied[0].node)
2106 base = repo.changelog.rev(self.applied[0].node)
2108 lastparent = repo.changelog.parentrevs(base)[0]
2107 lastparent = repo.changelog.parentrevs(base)[0]
2109 else:
2108 else:
2110 if heads != [repo.changelog.node(rev.first())]:
2109 if heads != [repo.changelog.node(rev.first())]:
2111 raise error.Abort(_('revision %d has unmanaged children')
2110 raise error.Abort(_('revision %d has unmanaged children')
2112 % rev.first())
2111 % rev.first())
2113 lastparent = None
2112 lastparent = None
2114
2113
2115 diffopts = self.diffopts({'git': git})
2114 diffopts = self.diffopts({'git': git})
2116 with repo.transaction('qimport') as tr:
2115 with repo.transaction('qimport') as tr:
2117 for r in rev:
2116 for r in rev:
2118 if not repo[r].mutable():
2117 if not repo[r].mutable():
2119 raise error.Abort(_('revision %d is not mutable') % r,
2118 raise error.Abort(_('revision %d is not mutable') % r,
2120 hint=_("see 'hg help phases' "
2119 hint=_("see 'hg help phases' "
2121 'for details'))
2120 'for details'))
2122 p1, p2 = repo.changelog.parentrevs(r)
2121 p1, p2 = repo.changelog.parentrevs(r)
2123 n = repo.changelog.node(r)
2122 n = repo.changelog.node(r)
2124 if p2 != nullrev:
2123 if p2 != nullrev:
2125 raise error.Abort(_('cannot import merge revision %d')
2124 raise error.Abort(_('cannot import merge revision %d')
2126 % r)
2125 % r)
2127 if lastparent and lastparent != r:
2126 if lastparent and lastparent != r:
2128 raise error.Abort(_('revision %d is not the parent of '
2127 raise error.Abort(_('revision %d is not the parent of '
2129 '%d')
2128 '%d')
2130 % (r, lastparent))
2129 % (r, lastparent))
2131 lastparent = p1
2130 lastparent = p1
2132
2131
2133 if not patchname:
2132 if not patchname:
2134 patchname = self.makepatchname(
2133 patchname = self.makepatchname(
2135 repo[r].description().split('\n', 1)[0],
2134 repo[r].description().split('\n', 1)[0],
2136 '%d.diff' % r)
2135 '%d.diff' % r)
2137 checkseries(patchname)
2136 checkseries(patchname)
2138 self.checkpatchname(patchname, force)
2137 self.checkpatchname(patchname, force)
2139 self.fullseries.insert(0, patchname)
2138 self.fullseries.insert(0, patchname)
2140
2139
2141 patchf = self.opener(patchname, "w")
2140 patchf = self.opener(patchname, "w")
2142 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
2141 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
2143 patchf.close()
2142 patchf.close()
2144
2143
2145 se = statusentry(n, patchname)
2144 se = statusentry(n, patchname)
2146 self.applied.insert(0, se)
2145 self.applied.insert(0, se)
2147
2146
2148 self.added.append(patchname)
2147 self.added.append(patchname)
2149 imported.append(patchname)
2148 imported.append(patchname)
2150 patchname = None
2149 patchname = None
2151 if rev and repo.ui.configbool('mq', 'secret', False):
2150 if rev and repo.ui.configbool('mq', 'secret', False):
2152 # if we added anything with --rev, move the secret root
2151 # if we added anything with --rev, move the secret root
2153 phases.retractboundary(repo, tr, phases.secret, [n])
2152 phases.retractboundary(repo, tr, phases.secret, [n])
2154 self.parseseries()
2153 self.parseseries()
2155 self.applieddirty = True
2154 self.applieddirty = True
2156 self.seriesdirty = True
2155 self.seriesdirty = True
2157
2156
2158 for i, filename in enumerate(files):
2157 for i, filename in enumerate(files):
2159 if existing:
2158 if existing:
2160 if filename == '-':
2159 if filename == '-':
2161 raise error.Abort(_('-e is incompatible with import from -')
2160 raise error.Abort(_('-e is incompatible with import from -')
2162 )
2161 )
2163 filename = normname(filename)
2162 filename = normname(filename)
2164 self.checkreservedname(filename)
2163 self.checkreservedname(filename)
2165 if util.url(filename).islocal():
2164 if util.url(filename).islocal():
2166 originpath = self.join(filename)
2165 originpath = self.join(filename)
2167 if not os.path.isfile(originpath):
2166 if not os.path.isfile(originpath):
2168 raise error.Abort(
2167 raise error.Abort(
2169 _("patch %s does not exist") % filename)
2168 _("patch %s does not exist") % filename)
2170
2169
2171 if patchname:
2170 if patchname:
2172 self.checkpatchname(patchname, force)
2171 self.checkpatchname(patchname, force)
2173
2172
2174 self.ui.write(_('renaming %s to %s\n')
2173 self.ui.write(_('renaming %s to %s\n')
2175 % (filename, patchname))
2174 % (filename, patchname))
2176 util.rename(originpath, self.join(patchname))
2175 util.rename(originpath, self.join(patchname))
2177 else:
2176 else:
2178 patchname = filename
2177 patchname = filename
2179
2178
2180 else:
2179 else:
2181 if filename == '-' and not patchname:
2180 if filename == '-' and not patchname:
2182 raise error.Abort(_('need --name to import a patch from -'))
2181 raise error.Abort(_('need --name to import a patch from -'))
2183 elif not patchname:
2182 elif not patchname:
2184 patchname = normname(os.path.basename(filename.rstrip('/')))
2183 patchname = normname(os.path.basename(filename.rstrip('/')))
2185 self.checkpatchname(patchname, force)
2184 self.checkpatchname(patchname, force)
2186 try:
2185 try:
2187 if filename == '-':
2186 if filename == '-':
2188 text = self.ui.fin.read()
2187 text = self.ui.fin.read()
2189 else:
2188 else:
2190 fp = hg.openpath(self.ui, filename)
2189 fp = hg.openpath(self.ui, filename)
2191 text = fp.read()
2190 text = fp.read()
2192 fp.close()
2191 fp.close()
2193 except (OSError, IOError):
2192 except (OSError, IOError):
2194 raise error.Abort(_("unable to read file %s") % filename)
2193 raise error.Abort(_("unable to read file %s") % filename)
2195 patchf = self.opener(patchname, "w")
2194 patchf = self.opener(patchname, "w")
2196 patchf.write(text)
2195 patchf.write(text)
2197 patchf.close()
2196 patchf.close()
2198 if not force:
2197 if not force:
2199 checkseries(patchname)
2198 checkseries(patchname)
2200 if patchname not in self.series:
2199 if patchname not in self.series:
2201 index = self.fullseriesend() + i
2200 index = self.fullseriesend() + i
2202 self.fullseries[index:index] = [patchname]
2201 self.fullseries[index:index] = [patchname]
2203 self.parseseries()
2202 self.parseseries()
2204 self.seriesdirty = True
2203 self.seriesdirty = True
2205 self.ui.warn(_("adding %s to series file\n") % patchname)
2204 self.ui.warn(_("adding %s to series file\n") % patchname)
2206 self.added.append(patchname)
2205 self.added.append(patchname)
2207 imported.append(patchname)
2206 imported.append(patchname)
2208 patchname = None
2207 patchname = None
2209
2208
2210 self.removeundo(repo)
2209 self.removeundo(repo)
2211 return imported
2210 return imported
2212
2211
2213 def fixkeepchangesopts(ui, opts):
2212 def fixkeepchangesopts(ui, opts):
2214 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2213 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2215 or opts.get('exact')):
2214 or opts.get('exact')):
2216 return opts
2215 return opts
2217 opts = dict(opts)
2216 opts = dict(opts)
2218 opts['keep_changes'] = True
2217 opts['keep_changes'] = True
2219 return opts
2218 return opts
2220
2219
2221 @command("qdelete|qremove|qrm",
2220 @command("qdelete|qremove|qrm",
2222 [('k', 'keep', None, _('keep patch file')),
2221 [('k', 'keep', None, _('keep patch file')),
2223 ('r', 'rev', [],
2222 ('r', 'rev', [],
2224 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2223 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2225 _('hg qdelete [-k] [PATCH]...'))
2224 _('hg qdelete [-k] [PATCH]...'))
2226 def delete(ui, repo, *patches, **opts):
2225 def delete(ui, repo, *patches, **opts):
2227 """remove patches from queue
2226 """remove patches from queue
2228
2227
2229 The patches must not be applied, and at least one patch is required. Exact
2228 The patches must not be applied, and at least one patch is required. Exact
2230 patch identifiers must be given. With -k/--keep, the patch files are
2229 patch identifiers must be given. With -k/--keep, the patch files are
2231 preserved in the patch directory.
2230 preserved in the patch directory.
2232
2231
2233 To stop managing a patch and move it into permanent history,
2232 To stop managing a patch and move it into permanent history,
2234 use the :hg:`qfinish` command."""
2233 use the :hg:`qfinish` command."""
2235 q = repo.mq
2234 q = repo.mq
2236 q.delete(repo, patches, opts)
2235 q.delete(repo, patches, opts)
2237 q.savedirty()
2236 q.savedirty()
2238 return 0
2237 return 0
2239
2238
2240 @command("qapplied",
2239 @command("qapplied",
2241 [('1', 'last', None, _('show only the preceding applied patch'))
2240 [('1', 'last', None, _('show only the preceding applied patch'))
2242 ] + seriesopts,
2241 ] + seriesopts,
2243 _('hg qapplied [-1] [-s] [PATCH]'))
2242 _('hg qapplied [-1] [-s] [PATCH]'))
2244 def applied(ui, repo, patch=None, **opts):
2243 def applied(ui, repo, patch=None, **opts):
2245 """print the patches already applied
2244 """print the patches already applied
2246
2245
2247 Returns 0 on success."""
2246 Returns 0 on success."""
2248
2247
2249 q = repo.mq
2248 q = repo.mq
2250
2249
2251 if patch:
2250 if patch:
2252 if patch not in q.series:
2251 if patch not in q.series:
2253 raise error.Abort(_("patch %s is not in series file") % patch)
2252 raise error.Abort(_("patch %s is not in series file") % patch)
2254 end = q.series.index(patch) + 1
2253 end = q.series.index(patch) + 1
2255 else:
2254 else:
2256 end = q.seriesend(True)
2255 end = q.seriesend(True)
2257
2256
2258 if opts.get('last') and not end:
2257 if opts.get('last') and not end:
2259 ui.write(_("no patches applied\n"))
2258 ui.write(_("no patches applied\n"))
2260 return 1
2259 return 1
2261 elif opts.get('last') and end == 1:
2260 elif opts.get('last') and end == 1:
2262 ui.write(_("only one patch applied\n"))
2261 ui.write(_("only one patch applied\n"))
2263 return 1
2262 return 1
2264 elif opts.get('last'):
2263 elif opts.get('last'):
2265 start = end - 2
2264 start = end - 2
2266 end = 1
2265 end = 1
2267 else:
2266 else:
2268 start = 0
2267 start = 0
2269
2268
2270 q.qseries(repo, length=end, start=start, status='A',
2269 q.qseries(repo, length=end, start=start, status='A',
2271 summary=opts.get('summary'))
2270 summary=opts.get('summary'))
2272
2271
2273
2272
2274 @command("qunapplied",
2273 @command("qunapplied",
2275 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2274 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2276 _('hg qunapplied [-1] [-s] [PATCH]'))
2275 _('hg qunapplied [-1] [-s] [PATCH]'))
2277 def unapplied(ui, repo, patch=None, **opts):
2276 def unapplied(ui, repo, patch=None, **opts):
2278 """print the patches not yet applied
2277 """print the patches not yet applied
2279
2278
2280 Returns 0 on success."""
2279 Returns 0 on success."""
2281
2280
2282 q = repo.mq
2281 q = repo.mq
2283 if patch:
2282 if patch:
2284 if patch not in q.series:
2283 if patch not in q.series:
2285 raise error.Abort(_("patch %s is not in series file") % patch)
2284 raise error.Abort(_("patch %s is not in series file") % patch)
2286 start = q.series.index(patch) + 1
2285 start = q.series.index(patch) + 1
2287 else:
2286 else:
2288 start = q.seriesend(True)
2287 start = q.seriesend(True)
2289
2288
2290 if start == len(q.series) and opts.get('first'):
2289 if start == len(q.series) and opts.get('first'):
2291 ui.write(_("all patches applied\n"))
2290 ui.write(_("all patches applied\n"))
2292 return 1
2291 return 1
2293
2292
2294 if opts.get('first'):
2293 if opts.get('first'):
2295 length = 1
2294 length = 1
2296 else:
2295 else:
2297 length = None
2296 length = None
2298 q.qseries(repo, start=start, length=length, status='U',
2297 q.qseries(repo, start=start, length=length, status='U',
2299 summary=opts.get('summary'))
2298 summary=opts.get('summary'))
2300
2299
2301 @command("qimport",
2300 @command("qimport",
2302 [('e', 'existing', None, _('import file in patch directory')),
2301 [('e', 'existing', None, _('import file in patch directory')),
2303 ('n', 'name', '',
2302 ('n', 'name', '',
2304 _('name of patch file'), _('NAME')),
2303 _('name of patch file'), _('NAME')),
2305 ('f', 'force', None, _('overwrite existing files')),
2304 ('f', 'force', None, _('overwrite existing files')),
2306 ('r', 'rev', [],
2305 ('r', 'rev', [],
2307 _('place existing revisions under mq control'), _('REV')),
2306 _('place existing revisions under mq control'), _('REV')),
2308 ('g', 'git', None, _('use git extended diff format')),
2307 ('g', 'git', None, _('use git extended diff format')),
2309 ('P', 'push', None, _('qpush after importing'))],
2308 ('P', 'push', None, _('qpush after importing'))],
2310 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2309 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2311 def qimport(ui, repo, *filename, **opts):
2310 def qimport(ui, repo, *filename, **opts):
2312 """import a patch or existing changeset
2311 """import a patch or existing changeset
2313
2312
2314 The patch is inserted into the series after the last applied
2313 The patch is inserted into the series after the last applied
2315 patch. If no patches have been applied, qimport prepends the patch
2314 patch. If no patches have been applied, qimport prepends the patch
2316 to the series.
2315 to the series.
2317
2316
2318 The patch will have the same name as its source file unless you
2317 The patch will have the same name as its source file unless you
2319 give it a new one with -n/--name.
2318 give it a new one with -n/--name.
2320
2319
2321 You can register an existing patch inside the patch directory with
2320 You can register an existing patch inside the patch directory with
2322 the -e/--existing flag.
2321 the -e/--existing flag.
2323
2322
2324 With -f/--force, an existing patch of the same name will be
2323 With -f/--force, an existing patch of the same name will be
2325 overwritten.
2324 overwritten.
2326
2325
2327 An existing changeset may be placed under mq control with -r/--rev
2326 An existing changeset may be placed under mq control with -r/--rev
2328 (e.g. qimport --rev . -n patch will place the current revision
2327 (e.g. qimport --rev . -n patch will place the current revision
2329 under mq control). With -g/--git, patches imported with --rev will
2328 under mq control). With -g/--git, patches imported with --rev will
2330 use the git diff format. See the diffs help topic for information
2329 use the git diff format. See the diffs help topic for information
2331 on why this is important for preserving rename/copy information
2330 on why this is important for preserving rename/copy information
2332 and permission changes. Use :hg:`qfinish` to remove changesets
2331 and permission changes. Use :hg:`qfinish` to remove changesets
2333 from mq control.
2332 from mq control.
2334
2333
2335 To import a patch from standard input, pass - as the patch file.
2334 To import a patch from standard input, pass - as the patch file.
2336 When importing from standard input, a patch name must be specified
2335 When importing from standard input, a patch name must be specified
2337 using the --name flag.
2336 using the --name flag.
2338
2337
2339 To import an existing patch while renaming it::
2338 To import an existing patch while renaming it::
2340
2339
2341 hg qimport -e existing-patch -n new-name
2340 hg qimport -e existing-patch -n new-name
2342
2341
2343 Returns 0 if import succeeded.
2342 Returns 0 if import succeeded.
2344 """
2343 """
2345 with repo.lock(): # cause this may move phase
2344 with repo.lock(): # cause this may move phase
2346 q = repo.mq
2345 q = repo.mq
2347 try:
2346 try:
2348 imported = q.qimport(
2347 imported = q.qimport(
2349 repo, filename, patchname=opts.get('name'),
2348 repo, filename, patchname=opts.get('name'),
2350 existing=opts.get('existing'), force=opts.get('force'),
2349 existing=opts.get('existing'), force=opts.get('force'),
2351 rev=opts.get('rev'), git=opts.get('git'))
2350 rev=opts.get('rev'), git=opts.get('git'))
2352 finally:
2351 finally:
2353 q.savedirty()
2352 q.savedirty()
2354
2353
2355 if imported and opts.get('push') and not opts.get('rev'):
2354 if imported and opts.get('push') and not opts.get('rev'):
2356 return q.push(repo, imported[-1])
2355 return q.push(repo, imported[-1])
2357 return 0
2356 return 0
2358
2357
2359 def qinit(ui, repo, create):
2358 def qinit(ui, repo, create):
2360 """initialize a new queue repository
2359 """initialize a new queue repository
2361
2360
2362 This command also creates a series file for ordering patches, and
2361 This command also creates a series file for ordering patches, and
2363 an mq-specific .hgignore file in the queue repository, to exclude
2362 an mq-specific .hgignore file in the queue repository, to exclude
2364 the status and guards files (these contain mostly transient state).
2363 the status and guards files (these contain mostly transient state).
2365
2364
2366 Returns 0 if initialization succeeded."""
2365 Returns 0 if initialization succeeded."""
2367 q = repo.mq
2366 q = repo.mq
2368 r = q.init(repo, create)
2367 r = q.init(repo, create)
2369 q.savedirty()
2368 q.savedirty()
2370 if r:
2369 if r:
2371 if not os.path.exists(r.wjoin('.hgignore')):
2370 if not os.path.exists(r.wjoin('.hgignore')):
2372 fp = r.wvfs('.hgignore', 'w')
2371 fp = r.wvfs('.hgignore', 'w')
2373 fp.write('^\\.hg\n')
2372 fp.write('^\\.hg\n')
2374 fp.write('^\\.mq\n')
2373 fp.write('^\\.mq\n')
2375 fp.write('syntax: glob\n')
2374 fp.write('syntax: glob\n')
2376 fp.write('status\n')
2375 fp.write('status\n')
2377 fp.write('guards\n')
2376 fp.write('guards\n')
2378 fp.close()
2377 fp.close()
2379 if not os.path.exists(r.wjoin('series')):
2378 if not os.path.exists(r.wjoin('series')):
2380 r.wvfs('series', 'w').close()
2379 r.wvfs('series', 'w').close()
2381 r[None].add(['.hgignore', 'series'])
2380 r[None].add(['.hgignore', 'series'])
2382 commands.add(ui, r)
2381 commands.add(ui, r)
2383 return 0
2382 return 0
2384
2383
2385 @command("^qinit",
2384 @command("^qinit",
2386 [('c', 'create-repo', None, _('create queue repository'))],
2385 [('c', 'create-repo', None, _('create queue repository'))],
2387 _('hg qinit [-c]'))
2386 _('hg qinit [-c]'))
2388 def init(ui, repo, **opts):
2387 def init(ui, repo, **opts):
2389 """init a new queue repository (DEPRECATED)
2388 """init a new queue repository (DEPRECATED)
2390
2389
2391 The queue repository is unversioned by default. If
2390 The queue repository is unversioned by default. If
2392 -c/--create-repo is specified, qinit will create a separate nested
2391 -c/--create-repo is specified, qinit will create a separate nested
2393 repository for patches (qinit -c may also be run later to convert
2392 repository for patches (qinit -c may also be run later to convert
2394 an unversioned patch repository into a versioned one). You can use
2393 an unversioned patch repository into a versioned one). You can use
2395 qcommit to commit changes to this queue repository.
2394 qcommit to commit changes to this queue repository.
2396
2395
2397 This command is deprecated. Without -c, it's implied by other relevant
2396 This command is deprecated. Without -c, it's implied by other relevant
2398 commands. With -c, use :hg:`init --mq` instead."""
2397 commands. With -c, use :hg:`init --mq` instead."""
2399 return qinit(ui, repo, create=opts.get('create_repo'))
2398 return qinit(ui, repo, create=opts.get('create_repo'))
2400
2399
2401 @command("qclone",
2400 @command("qclone",
2402 [('', 'pull', None, _('use pull protocol to copy metadata')),
2401 [('', 'pull', None, _('use pull protocol to copy metadata')),
2403 ('U', 'noupdate', None,
2402 ('U', 'noupdate', None,
2404 _('do not update the new working directories')),
2403 _('do not update the new working directories')),
2405 ('', 'uncompressed', None,
2404 ('', 'uncompressed', None,
2406 _('use uncompressed transfer (fast over LAN)')),
2405 _('use uncompressed transfer (fast over LAN)')),
2407 ('p', 'patches', '',
2406 ('p', 'patches', '',
2408 _('location of source patch repository'), _('REPO')),
2407 _('location of source patch repository'), _('REPO')),
2409 ] + commands.remoteopts,
2408 ] + commands.remoteopts,
2410 _('hg qclone [OPTION]... SOURCE [DEST]'),
2409 _('hg qclone [OPTION]... SOURCE [DEST]'),
2411 norepo=True)
2410 norepo=True)
2412 def clone(ui, source, dest=None, **opts):
2411 def clone(ui, source, dest=None, **opts):
2413 '''clone main and patch repository at same time
2412 '''clone main and patch repository at same time
2414
2413
2415 If source is local, destination will have no patches applied. If
2414 If source is local, destination will have no patches applied. If
2416 source is remote, this command can not check if patches are
2415 source is remote, this command can not check if patches are
2417 applied in source, so cannot guarantee that patches are not
2416 applied in source, so cannot guarantee that patches are not
2418 applied in destination. If you clone remote repository, be sure
2417 applied in destination. If you clone remote repository, be sure
2419 before that it has no patches applied.
2418 before that it has no patches applied.
2420
2419
2421 Source patch repository is looked for in <src>/.hg/patches by
2420 Source patch repository is looked for in <src>/.hg/patches by
2422 default. Use -p <url> to change.
2421 default. Use -p <url> to change.
2423
2422
2424 The patch directory must be a nested Mercurial repository, as
2423 The patch directory must be a nested Mercurial repository, as
2425 would be created by :hg:`init --mq`.
2424 would be created by :hg:`init --mq`.
2426
2425
2427 Return 0 on success.
2426 Return 0 on success.
2428 '''
2427 '''
2429 def patchdir(repo):
2428 def patchdir(repo):
2430 """compute a patch repo url from a repo object"""
2429 """compute a patch repo url from a repo object"""
2431 url = repo.url()
2430 url = repo.url()
2432 if url.endswith('/'):
2431 if url.endswith('/'):
2433 url = url[:-1]
2432 url = url[:-1]
2434 return url + '/.hg/patches'
2433 return url + '/.hg/patches'
2435
2434
2436 # main repo (destination and sources)
2435 # main repo (destination and sources)
2437 if dest is None:
2436 if dest is None:
2438 dest = hg.defaultdest(source)
2437 dest = hg.defaultdest(source)
2439 sr = hg.peer(ui, opts, ui.expandpath(source))
2438 sr = hg.peer(ui, opts, ui.expandpath(source))
2440
2439
2441 # patches repo (source only)
2440 # patches repo (source only)
2442 if opts.get('patches'):
2441 if opts.get('patches'):
2443 patchespath = ui.expandpath(opts.get('patches'))
2442 patchespath = ui.expandpath(opts.get('patches'))
2444 else:
2443 else:
2445 patchespath = patchdir(sr)
2444 patchespath = patchdir(sr)
2446 try:
2445 try:
2447 hg.peer(ui, opts, patchespath)
2446 hg.peer(ui, opts, patchespath)
2448 except error.RepoError:
2447 except error.RepoError:
2449 raise error.Abort(_('versioned patch repository not found'
2448 raise error.Abort(_('versioned patch repository not found'
2450 ' (see init --mq)'))
2449 ' (see init --mq)'))
2451 qbase, destrev = None, None
2450 qbase, destrev = None, None
2452 if sr.local():
2451 if sr.local():
2453 repo = sr.local()
2452 repo = sr.local()
2454 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2453 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2455 qbase = repo.mq.applied[0].node
2454 qbase = repo.mq.applied[0].node
2456 if not hg.islocal(dest):
2455 if not hg.islocal(dest):
2457 heads = set(repo.heads())
2456 heads = set(repo.heads())
2458 destrev = list(heads.difference(repo.heads(qbase)))
2457 destrev = list(heads.difference(repo.heads(qbase)))
2459 destrev.append(repo.changelog.parents(qbase)[0])
2458 destrev.append(repo.changelog.parents(qbase)[0])
2460 elif sr.capable('lookup'):
2459 elif sr.capable('lookup'):
2461 try:
2460 try:
2462 qbase = sr.lookup('qbase')
2461 qbase = sr.lookup('qbase')
2463 except error.RepoError:
2462 except error.RepoError:
2464 pass
2463 pass
2465
2464
2466 ui.note(_('cloning main repository\n'))
2465 ui.note(_('cloning main repository\n'))
2467 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2466 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2468 pull=opts.get('pull'),
2467 pull=opts.get('pull'),
2469 rev=destrev,
2468 rev=destrev,
2470 update=False,
2469 update=False,
2471 stream=opts.get('uncompressed'))
2470 stream=opts.get('uncompressed'))
2472
2471
2473 ui.note(_('cloning patch repository\n'))
2472 ui.note(_('cloning patch repository\n'))
2474 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2473 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2475 pull=opts.get('pull'), update=not opts.get('noupdate'),
2474 pull=opts.get('pull'), update=not opts.get('noupdate'),
2476 stream=opts.get('uncompressed'))
2475 stream=opts.get('uncompressed'))
2477
2476
2478 if dr.local():
2477 if dr.local():
2479 repo = dr.local()
2478 repo = dr.local()
2480 if qbase:
2479 if qbase:
2481 ui.note(_('stripping applied patches from destination '
2480 ui.note(_('stripping applied patches from destination '
2482 'repository\n'))
2481 'repository\n'))
2483 strip(ui, repo, [qbase], update=False, backup=None)
2482 strip(ui, repo, [qbase], update=False, backup=None)
2484 if not opts.get('noupdate'):
2483 if not opts.get('noupdate'):
2485 ui.note(_('updating destination repository\n'))
2484 ui.note(_('updating destination repository\n'))
2486 hg.update(repo, repo.changelog.tip())
2485 hg.update(repo, repo.changelog.tip())
2487
2486
2488 @command("qcommit|qci",
2487 @command("qcommit|qci",
2489 commands.table["^commit|ci"][1],
2488 commands.table["^commit|ci"][1],
2490 _('hg qcommit [OPTION]... [FILE]...'),
2489 _('hg qcommit [OPTION]... [FILE]...'),
2491 inferrepo=True)
2490 inferrepo=True)
2492 def commit(ui, repo, *pats, **opts):
2491 def commit(ui, repo, *pats, **opts):
2493 """commit changes in the queue repository (DEPRECATED)
2492 """commit changes in the queue repository (DEPRECATED)
2494
2493
2495 This command is deprecated; use :hg:`commit --mq` instead."""
2494 This command is deprecated; use :hg:`commit --mq` instead."""
2496 q = repo.mq
2495 q = repo.mq
2497 r = q.qrepo()
2496 r = q.qrepo()
2498 if not r:
2497 if not r:
2499 raise error.Abort('no queue repository')
2498 raise error.Abort('no queue repository')
2500 commands.commit(r.ui, r, *pats, **opts)
2499 commands.commit(r.ui, r, *pats, **opts)
2501
2500
2502 @command("qseries",
2501 @command("qseries",
2503 [('m', 'missing', None, _('print patches not in series')),
2502 [('m', 'missing', None, _('print patches not in series')),
2504 ] + seriesopts,
2503 ] + seriesopts,
2505 _('hg qseries [-ms]'))
2504 _('hg qseries [-ms]'))
2506 def series(ui, repo, **opts):
2505 def series(ui, repo, **opts):
2507 """print the entire series file
2506 """print the entire series file
2508
2507
2509 Returns 0 on success."""
2508 Returns 0 on success."""
2510 repo.mq.qseries(repo, missing=opts.get('missing'),
2509 repo.mq.qseries(repo, missing=opts.get('missing'),
2511 summary=opts.get('summary'))
2510 summary=opts.get('summary'))
2512 return 0
2511 return 0
2513
2512
2514 @command("qtop", seriesopts, _('hg qtop [-s]'))
2513 @command("qtop", seriesopts, _('hg qtop [-s]'))
2515 def top(ui, repo, **opts):
2514 def top(ui, repo, **opts):
2516 """print the name of the current patch
2515 """print the name of the current patch
2517
2516
2518 Returns 0 on success."""
2517 Returns 0 on success."""
2519 q = repo.mq
2518 q = repo.mq
2520 if q.applied:
2519 if q.applied:
2521 t = q.seriesend(True)
2520 t = q.seriesend(True)
2522 else:
2521 else:
2523 t = 0
2522 t = 0
2524
2523
2525 if t:
2524 if t:
2526 q.qseries(repo, start=t - 1, length=1, status='A',
2525 q.qseries(repo, start=t - 1, length=1, status='A',
2527 summary=opts.get('summary'))
2526 summary=opts.get('summary'))
2528 else:
2527 else:
2529 ui.write(_("no patches applied\n"))
2528 ui.write(_("no patches applied\n"))
2530 return 1
2529 return 1
2531
2530
2532 @command("qnext", seriesopts, _('hg qnext [-s]'))
2531 @command("qnext", seriesopts, _('hg qnext [-s]'))
2533 def next(ui, repo, **opts):
2532 def next(ui, repo, **opts):
2534 """print the name of the next pushable patch
2533 """print the name of the next pushable patch
2535
2534
2536 Returns 0 on success."""
2535 Returns 0 on success."""
2537 q = repo.mq
2536 q = repo.mq
2538 end = q.seriesend()
2537 end = q.seriesend()
2539 if end == len(q.series):
2538 if end == len(q.series):
2540 ui.write(_("all patches applied\n"))
2539 ui.write(_("all patches applied\n"))
2541 return 1
2540 return 1
2542 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2541 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2543
2542
2544 @command("qprev", seriesopts, _('hg qprev [-s]'))
2543 @command("qprev", seriesopts, _('hg qprev [-s]'))
2545 def prev(ui, repo, **opts):
2544 def prev(ui, repo, **opts):
2546 """print the name of the preceding applied patch
2545 """print the name of the preceding applied patch
2547
2546
2548 Returns 0 on success."""
2547 Returns 0 on success."""
2549 q = repo.mq
2548 q = repo.mq
2550 l = len(q.applied)
2549 l = len(q.applied)
2551 if l == 1:
2550 if l == 1:
2552 ui.write(_("only one patch applied\n"))
2551 ui.write(_("only one patch applied\n"))
2553 return 1
2552 return 1
2554 if not l:
2553 if not l:
2555 ui.write(_("no patches applied\n"))
2554 ui.write(_("no patches applied\n"))
2556 return 1
2555 return 1
2557 idx = q.series.index(q.applied[-2].name)
2556 idx = q.series.index(q.applied[-2].name)
2558 q.qseries(repo, start=idx, length=1, status='A',
2557 q.qseries(repo, start=idx, length=1, status='A',
2559 summary=opts.get('summary'))
2558 summary=opts.get('summary'))
2560
2559
2561 def setupheaderopts(ui, opts):
2560 def setupheaderopts(ui, opts):
2562 if not opts.get('user') and opts.get('currentuser'):
2561 if not opts.get('user') and opts.get('currentuser'):
2563 opts['user'] = ui.username()
2562 opts['user'] = ui.username()
2564 if not opts.get('date') and opts.get('currentdate'):
2563 if not opts.get('date') and opts.get('currentdate'):
2565 opts['date'] = "%d %d" % util.makedate()
2564 opts['date'] = "%d %d" % util.makedate()
2566
2565
2567 @command("^qnew",
2566 @command("^qnew",
2568 [('e', 'edit', None, _('invoke editor on commit messages')),
2567 [('e', 'edit', None, _('invoke editor on commit messages')),
2569 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2568 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2570 ('g', 'git', None, _('use git extended diff format')),
2569 ('g', 'git', None, _('use git extended diff format')),
2571 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2570 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2572 ('u', 'user', '',
2571 ('u', 'user', '',
2573 _('add "From: <USER>" to patch'), _('USER')),
2572 _('add "From: <USER>" to patch'), _('USER')),
2574 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2573 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2575 ('d', 'date', '',
2574 ('d', 'date', '',
2576 _('add "Date: <DATE>" to patch'), _('DATE'))
2575 _('add "Date: <DATE>" to patch'), _('DATE'))
2577 ] + commands.walkopts + commands.commitopts,
2576 ] + commands.walkopts + commands.commitopts,
2578 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'),
2577 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'),
2579 inferrepo=True)
2578 inferrepo=True)
2580 def new(ui, repo, patch, *args, **opts):
2579 def new(ui, repo, patch, *args, **opts):
2581 """create a new patch
2580 """create a new patch
2582
2581
2583 qnew creates a new patch on top of the currently-applied patch (if
2582 qnew creates a new patch on top of the currently-applied patch (if
2584 any). The patch will be initialized with any outstanding changes
2583 any). The patch will be initialized with any outstanding changes
2585 in the working directory. You may also use -I/--include,
2584 in the working directory. You may also use -I/--include,
2586 -X/--exclude, and/or a list of files after the patch name to add
2585 -X/--exclude, and/or a list of files after the patch name to add
2587 only changes to matching files to the new patch, leaving the rest
2586 only changes to matching files to the new patch, leaving the rest
2588 as uncommitted modifications.
2587 as uncommitted modifications.
2589
2588
2590 -u/--user and -d/--date can be used to set the (given) user and
2589 -u/--user and -d/--date can be used to set the (given) user and
2591 date, respectively. -U/--currentuser and -D/--currentdate set user
2590 date, respectively. -U/--currentuser and -D/--currentdate set user
2592 to current user and date to current date.
2591 to current user and date to current date.
2593
2592
2594 -e/--edit, -m/--message or -l/--logfile set the patch header as
2593 -e/--edit, -m/--message or -l/--logfile set the patch header as
2595 well as the commit message. If none is specified, the header is
2594 well as the commit message. If none is specified, the header is
2596 empty and the commit message is '[mq]: PATCH'.
2595 empty and the commit message is '[mq]: PATCH'.
2597
2596
2598 Use the -g/--git option to keep the patch in the git extended diff
2597 Use the -g/--git option to keep the patch in the git extended diff
2599 format. Read the diffs help topic for more information on why this
2598 format. Read the diffs help topic for more information on why this
2600 is important for preserving permission changes and copy/rename
2599 is important for preserving permission changes and copy/rename
2601 information.
2600 information.
2602
2601
2603 Returns 0 on successful creation of a new patch.
2602 Returns 0 on successful creation of a new patch.
2604 """
2603 """
2605 msg = cmdutil.logmessage(ui, opts)
2604 msg = cmdutil.logmessage(ui, opts)
2606 q = repo.mq
2605 q = repo.mq
2607 opts['msg'] = msg
2606 opts['msg'] = msg
2608 setupheaderopts(ui, opts)
2607 setupheaderopts(ui, opts)
2609 q.new(repo, patch, *args, **opts)
2608 q.new(repo, patch, *args, **opts)
2610 q.savedirty()
2609 q.savedirty()
2611 return 0
2610 return 0
2612
2611
2613 @command("^qrefresh",
2612 @command("^qrefresh",
2614 [('e', 'edit', None, _('invoke editor on commit messages')),
2613 [('e', 'edit', None, _('invoke editor on commit messages')),
2615 ('g', 'git', None, _('use git extended diff format')),
2614 ('g', 'git', None, _('use git extended diff format')),
2616 ('s', 'short', None,
2615 ('s', 'short', None,
2617 _('refresh only files already in the patch and specified files')),
2616 _('refresh only files already in the patch and specified files')),
2618 ('U', 'currentuser', None,
2617 ('U', 'currentuser', None,
2619 _('add/update author field in patch with current user')),
2618 _('add/update author field in patch with current user')),
2620 ('u', 'user', '',
2619 ('u', 'user', '',
2621 _('add/update author field in patch with given user'), _('USER')),
2620 _('add/update author field in patch with given user'), _('USER')),
2622 ('D', 'currentdate', None,
2621 ('D', 'currentdate', None,
2623 _('add/update date field in patch with current date')),
2622 _('add/update date field in patch with current date')),
2624 ('d', 'date', '',
2623 ('d', 'date', '',
2625 _('add/update date field in patch with given date'), _('DATE'))
2624 _('add/update date field in patch with given date'), _('DATE'))
2626 ] + commands.walkopts + commands.commitopts,
2625 ] + commands.walkopts + commands.commitopts,
2627 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'),
2626 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'),
2628 inferrepo=True)
2627 inferrepo=True)
2629 def refresh(ui, repo, *pats, **opts):
2628 def refresh(ui, repo, *pats, **opts):
2630 """update the current patch
2629 """update the current patch
2631
2630
2632 If any file patterns are provided, the refreshed patch will
2631 If any file patterns are provided, the refreshed patch will
2633 contain only the modifications that match those patterns; the
2632 contain only the modifications that match those patterns; the
2634 remaining modifications will remain in the working directory.
2633 remaining modifications will remain in the working directory.
2635
2634
2636 If -s/--short is specified, files currently included in the patch
2635 If -s/--short is specified, files currently included in the patch
2637 will be refreshed just like matched files and remain in the patch.
2636 will be refreshed just like matched files and remain in the patch.
2638
2637
2639 If -e/--edit is specified, Mercurial will start your configured editor for
2638 If -e/--edit is specified, Mercurial will start your configured editor for
2640 you to enter a message. In case qrefresh fails, you will find a backup of
2639 you to enter a message. In case qrefresh fails, you will find a backup of
2641 your message in ``.hg/last-message.txt``.
2640 your message in ``.hg/last-message.txt``.
2642
2641
2643 hg add/remove/copy/rename work as usual, though you might want to
2642 hg add/remove/copy/rename work as usual, though you might want to
2644 use git-style patches (-g/--git or [diff] git=1) to track copies
2643 use git-style patches (-g/--git or [diff] git=1) to track copies
2645 and renames. See the diffs help topic for more information on the
2644 and renames. See the diffs help topic for more information on the
2646 git diff format.
2645 git diff format.
2647
2646
2648 Returns 0 on success.
2647 Returns 0 on success.
2649 """
2648 """
2650 q = repo.mq
2649 q = repo.mq
2651 message = cmdutil.logmessage(ui, opts)
2650 message = cmdutil.logmessage(ui, opts)
2652 setupheaderopts(ui, opts)
2651 setupheaderopts(ui, opts)
2653 with repo.wlock():
2652 with repo.wlock():
2654 ret = q.refresh(repo, pats, msg=message, **opts)
2653 ret = q.refresh(repo, pats, msg=message, **opts)
2655 q.savedirty()
2654 q.savedirty()
2656 return ret
2655 return ret
2657
2656
2658 @command("^qdiff",
2657 @command("^qdiff",
2659 commands.diffopts + commands.diffopts2 + commands.walkopts,
2658 commands.diffopts + commands.diffopts2 + commands.walkopts,
2660 _('hg qdiff [OPTION]... [FILE]...'),
2659 _('hg qdiff [OPTION]... [FILE]...'),
2661 inferrepo=True)
2660 inferrepo=True)
2662 def diff(ui, repo, *pats, **opts):
2661 def diff(ui, repo, *pats, **opts):
2663 """diff of the current patch and subsequent modifications
2662 """diff of the current patch and subsequent modifications
2664
2663
2665 Shows a diff which includes the current patch as well as any
2664 Shows a diff which includes the current patch as well as any
2666 changes which have been made in the working directory since the
2665 changes which have been made in the working directory since the
2667 last refresh (thus showing what the current patch would become
2666 last refresh (thus showing what the current patch would become
2668 after a qrefresh).
2667 after a qrefresh).
2669
2668
2670 Use :hg:`diff` if you only want to see the changes made since the
2669 Use :hg:`diff` if you only want to see the changes made since the
2671 last qrefresh, or :hg:`export qtip` if you want to see changes
2670 last qrefresh, or :hg:`export qtip` if you want to see changes
2672 made by the current patch without including changes made since the
2671 made by the current patch without including changes made since the
2673 qrefresh.
2672 qrefresh.
2674
2673
2675 Returns 0 on success.
2674 Returns 0 on success.
2676 """
2675 """
2677 repo.mq.diff(repo, pats, opts)
2676 repo.mq.diff(repo, pats, opts)
2678 return 0
2677 return 0
2679
2678
2680 @command('qfold',
2679 @command('qfold',
2681 [('e', 'edit', None, _('invoke editor on commit messages')),
2680 [('e', 'edit', None, _('invoke editor on commit messages')),
2682 ('k', 'keep', None, _('keep folded patch files')),
2681 ('k', 'keep', None, _('keep folded patch files')),
2683 ] + commands.commitopts,
2682 ] + commands.commitopts,
2684 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2683 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2685 def fold(ui, repo, *files, **opts):
2684 def fold(ui, repo, *files, **opts):
2686 """fold the named patches into the current patch
2685 """fold the named patches into the current patch
2687
2686
2688 Patches must not yet be applied. Each patch will be successively
2687 Patches must not yet be applied. Each patch will be successively
2689 applied to the current patch in the order given. If all the
2688 applied to the current patch in the order given. If all the
2690 patches apply successfully, the current patch will be refreshed
2689 patches apply successfully, the current patch will be refreshed
2691 with the new cumulative patch, and the folded patches will be
2690 with the new cumulative patch, and the folded patches will be
2692 deleted. With -k/--keep, the folded patch files will not be
2691 deleted. With -k/--keep, the folded patch files will not be
2693 removed afterwards.
2692 removed afterwards.
2694
2693
2695 The header for each folded patch will be concatenated with the
2694 The header for each folded patch will be concatenated with the
2696 current patch header, separated by a line of ``* * *``.
2695 current patch header, separated by a line of ``* * *``.
2697
2696
2698 Returns 0 on success."""
2697 Returns 0 on success."""
2699 q = repo.mq
2698 q = repo.mq
2700 if not files:
2699 if not files:
2701 raise error.Abort(_('qfold requires at least one patch name'))
2700 raise error.Abort(_('qfold requires at least one patch name'))
2702 if not q.checktoppatch(repo)[0]:
2701 if not q.checktoppatch(repo)[0]:
2703 raise error.Abort(_('no patches applied'))
2702 raise error.Abort(_('no patches applied'))
2704 q.checklocalchanges(repo)
2703 q.checklocalchanges(repo)
2705
2704
2706 message = cmdutil.logmessage(ui, opts)
2705 message = cmdutil.logmessage(ui, opts)
2707
2706
2708 parent = q.lookup('qtip')
2707 parent = q.lookup('qtip')
2709 patches = []
2708 patches = []
2710 messages = []
2709 messages = []
2711 for f in files:
2710 for f in files:
2712 p = q.lookup(f)
2711 p = q.lookup(f)
2713 if p in patches or p == parent:
2712 if p in patches or p == parent:
2714 ui.warn(_('skipping already folded patch %s\n') % p)
2713 ui.warn(_('skipping already folded patch %s\n') % p)
2715 if q.isapplied(p):
2714 if q.isapplied(p):
2716 raise error.Abort(_('qfold cannot fold already applied patch %s')
2715 raise error.Abort(_('qfold cannot fold already applied patch %s')
2717 % p)
2716 % p)
2718 patches.append(p)
2717 patches.append(p)
2719
2718
2720 for p in patches:
2719 for p in patches:
2721 if not message:
2720 if not message:
2722 ph = patchheader(q.join(p), q.plainmode)
2721 ph = patchheader(q.join(p), q.plainmode)
2723 if ph.message:
2722 if ph.message:
2724 messages.append(ph.message)
2723 messages.append(ph.message)
2725 pf = q.join(p)
2724 pf = q.join(p)
2726 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2725 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2727 if not patchsuccess:
2726 if not patchsuccess:
2728 raise error.Abort(_('error folding patch %s') % p)
2727 raise error.Abort(_('error folding patch %s') % p)
2729
2728
2730 if not message:
2729 if not message:
2731 ph = patchheader(q.join(parent), q.plainmode)
2730 ph = patchheader(q.join(parent), q.plainmode)
2732 message = ph.message
2731 message = ph.message
2733 for msg in messages:
2732 for msg in messages:
2734 if msg:
2733 if msg:
2735 if message:
2734 if message:
2736 message.append('* * *')
2735 message.append('* * *')
2737 message.extend(msg)
2736 message.extend(msg)
2738 message = '\n'.join(message)
2737 message = '\n'.join(message)
2739
2738
2740 diffopts = q.patchopts(q.diffopts(), *patches)
2739 diffopts = q.patchopts(q.diffopts(), *patches)
2741 with repo.wlock():
2740 with repo.wlock():
2742 q.refresh(repo, msg=message, git=diffopts.git, edit=opts.get('edit'),
2741 q.refresh(repo, msg=message, git=diffopts.git, edit=opts.get('edit'),
2743 editform='mq.qfold')
2742 editform='mq.qfold')
2744 q.delete(repo, patches, opts)
2743 q.delete(repo, patches, opts)
2745 q.savedirty()
2744 q.savedirty()
2746
2745
2747 @command("qgoto",
2746 @command("qgoto",
2748 [('', 'keep-changes', None,
2747 [('', 'keep-changes', None,
2749 _('tolerate non-conflicting local changes')),
2748 _('tolerate non-conflicting local changes')),
2750 ('f', 'force', None, _('overwrite any local changes')),
2749 ('f', 'force', None, _('overwrite any local changes')),
2751 ('', 'no-backup', None, _('do not save backup copies of files'))],
2750 ('', 'no-backup', None, _('do not save backup copies of files'))],
2752 _('hg qgoto [OPTION]... PATCH'))
2751 _('hg qgoto [OPTION]... PATCH'))
2753 def goto(ui, repo, patch, **opts):
2752 def goto(ui, repo, patch, **opts):
2754 '''push or pop patches until named patch is at top of stack
2753 '''push or pop patches until named patch is at top of stack
2755
2754
2756 Returns 0 on success.'''
2755 Returns 0 on success.'''
2757 opts = fixkeepchangesopts(ui, opts)
2756 opts = fixkeepchangesopts(ui, opts)
2758 q = repo.mq
2757 q = repo.mq
2759 patch = q.lookup(patch)
2758 patch = q.lookup(patch)
2760 nobackup = opts.get('no_backup')
2759 nobackup = opts.get('no_backup')
2761 keepchanges = opts.get('keep_changes')
2760 keepchanges = opts.get('keep_changes')
2762 if q.isapplied(patch):
2761 if q.isapplied(patch):
2763 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2762 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2764 keepchanges=keepchanges)
2763 keepchanges=keepchanges)
2765 else:
2764 else:
2766 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2765 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2767 keepchanges=keepchanges)
2766 keepchanges=keepchanges)
2768 q.savedirty()
2767 q.savedirty()
2769 return ret
2768 return ret
2770
2769
2771 @command("qguard",
2770 @command("qguard",
2772 [('l', 'list', None, _('list all patches and guards')),
2771 [('l', 'list', None, _('list all patches and guards')),
2773 ('n', 'none', None, _('drop all guards'))],
2772 ('n', 'none', None, _('drop all guards'))],
2774 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2773 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2775 def guard(ui, repo, *args, **opts):
2774 def guard(ui, repo, *args, **opts):
2776 '''set or print guards for a patch
2775 '''set or print guards for a patch
2777
2776
2778 Guards control whether a patch can be pushed. A patch with no
2777 Guards control whether a patch can be pushed. A patch with no
2779 guards is always pushed. A patch with a positive guard ("+foo") is
2778 guards is always pushed. A patch with a positive guard ("+foo") is
2780 pushed only if the :hg:`qselect` command has activated it. A patch with
2779 pushed only if the :hg:`qselect` command has activated it. A patch with
2781 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2780 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2782 has activated it.
2781 has activated it.
2783
2782
2784 With no arguments, print the currently active guards.
2783 With no arguments, print the currently active guards.
2785 With arguments, set guards for the named patch.
2784 With arguments, set guards for the named patch.
2786
2785
2787 .. note::
2786 .. note::
2788
2787
2789 Specifying negative guards now requires '--'.
2788 Specifying negative guards now requires '--'.
2790
2789
2791 To set guards on another patch::
2790 To set guards on another patch::
2792
2791
2793 hg qguard other.patch -- +2.6.17 -stable
2792 hg qguard other.patch -- +2.6.17 -stable
2794
2793
2795 Returns 0 on success.
2794 Returns 0 on success.
2796 '''
2795 '''
2797 def status(idx):
2796 def status(idx):
2798 guards = q.seriesguards[idx] or ['unguarded']
2797 guards = q.seriesguards[idx] or ['unguarded']
2799 if q.series[idx] in applied:
2798 if q.series[idx] in applied:
2800 state = 'applied'
2799 state = 'applied'
2801 elif q.pushable(idx)[0]:
2800 elif q.pushable(idx)[0]:
2802 state = 'unapplied'
2801 state = 'unapplied'
2803 else:
2802 else:
2804 state = 'guarded'
2803 state = 'guarded'
2805 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2804 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2806 ui.write('%s: ' % ui.label(q.series[idx], label))
2805 ui.write('%s: ' % ui.label(q.series[idx], label))
2807
2806
2808 for i, guard in enumerate(guards):
2807 for i, guard in enumerate(guards):
2809 if guard.startswith('+'):
2808 if guard.startswith('+'):
2810 ui.write(guard, label='qguard.positive')
2809 ui.write(guard, label='qguard.positive')
2811 elif guard.startswith('-'):
2810 elif guard.startswith('-'):
2812 ui.write(guard, label='qguard.negative')
2811 ui.write(guard, label='qguard.negative')
2813 else:
2812 else:
2814 ui.write(guard, label='qguard.unguarded')
2813 ui.write(guard, label='qguard.unguarded')
2815 if i != len(guards) - 1:
2814 if i != len(guards) - 1:
2816 ui.write(' ')
2815 ui.write(' ')
2817 ui.write('\n')
2816 ui.write('\n')
2818 q = repo.mq
2817 q = repo.mq
2819 applied = set(p.name for p in q.applied)
2818 applied = set(p.name for p in q.applied)
2820 patch = None
2819 patch = None
2821 args = list(args)
2820 args = list(args)
2822 if opts.get('list'):
2821 if opts.get('list'):
2823 if args or opts.get('none'):
2822 if args or opts.get('none'):
2824 raise error.Abort(_('cannot mix -l/--list with options or '
2823 raise error.Abort(_('cannot mix -l/--list with options or '
2825 'arguments'))
2824 'arguments'))
2826 for i in xrange(len(q.series)):
2825 for i in xrange(len(q.series)):
2827 status(i)
2826 status(i)
2828 return
2827 return
2829 if not args or args[0][0:1] in '-+':
2828 if not args or args[0][0:1] in '-+':
2830 if not q.applied:
2829 if not q.applied:
2831 raise error.Abort(_('no patches applied'))
2830 raise error.Abort(_('no patches applied'))
2832 patch = q.applied[-1].name
2831 patch = q.applied[-1].name
2833 if patch is None and args[0][0:1] not in '-+':
2832 if patch is None and args[0][0:1] not in '-+':
2834 patch = args.pop(0)
2833 patch = args.pop(0)
2835 if patch is None:
2834 if patch is None:
2836 raise error.Abort(_('no patch to work with'))
2835 raise error.Abort(_('no patch to work with'))
2837 if args or opts.get('none'):
2836 if args or opts.get('none'):
2838 idx = q.findseries(patch)
2837 idx = q.findseries(patch)
2839 if idx is None:
2838 if idx is None:
2840 raise error.Abort(_('no patch named %s') % patch)
2839 raise error.Abort(_('no patch named %s') % patch)
2841 q.setguards(idx, args)
2840 q.setguards(idx, args)
2842 q.savedirty()
2841 q.savedirty()
2843 else:
2842 else:
2844 status(q.series.index(q.lookup(patch)))
2843 status(q.series.index(q.lookup(patch)))
2845
2844
2846 @command("qheader", [], _('hg qheader [PATCH]'))
2845 @command("qheader", [], _('hg qheader [PATCH]'))
2847 def header(ui, repo, patch=None):
2846 def header(ui, repo, patch=None):
2848 """print the header of the topmost or specified patch
2847 """print the header of the topmost or specified patch
2849
2848
2850 Returns 0 on success."""
2849 Returns 0 on success."""
2851 q = repo.mq
2850 q = repo.mq
2852
2851
2853 if patch:
2852 if patch:
2854 patch = q.lookup(patch)
2853 patch = q.lookup(patch)
2855 else:
2854 else:
2856 if not q.applied:
2855 if not q.applied:
2857 ui.write(_('no patches applied\n'))
2856 ui.write(_('no patches applied\n'))
2858 return 1
2857 return 1
2859 patch = q.lookup('qtip')
2858 patch = q.lookup('qtip')
2860 ph = patchheader(q.join(patch), q.plainmode)
2859 ph = patchheader(q.join(patch), q.plainmode)
2861
2860
2862 ui.write('\n'.join(ph.message) + '\n')
2861 ui.write('\n'.join(ph.message) + '\n')
2863
2862
2864 def lastsavename(path):
2863 def lastsavename(path):
2865 (directory, base) = os.path.split(path)
2864 (directory, base) = os.path.split(path)
2866 names = os.listdir(directory)
2865 names = os.listdir(directory)
2867 namere = re.compile("%s.([0-9]+)" % base)
2866 namere = re.compile("%s.([0-9]+)" % base)
2868 maxindex = None
2867 maxindex = None
2869 maxname = None
2868 maxname = None
2870 for f in names:
2869 for f in names:
2871 m = namere.match(f)
2870 m = namere.match(f)
2872 if m:
2871 if m:
2873 index = int(m.group(1))
2872 index = int(m.group(1))
2874 if maxindex is None or index > maxindex:
2873 if maxindex is None or index > maxindex:
2875 maxindex = index
2874 maxindex = index
2876 maxname = f
2875 maxname = f
2877 if maxname:
2876 if maxname:
2878 return (os.path.join(directory, maxname), maxindex)
2877 return (os.path.join(directory, maxname), maxindex)
2879 return (None, None)
2878 return (None, None)
2880
2879
2881 def savename(path):
2880 def savename(path):
2882 (last, index) = lastsavename(path)
2881 (last, index) = lastsavename(path)
2883 if last is None:
2882 if last is None:
2884 index = 0
2883 index = 0
2885 newpath = path + ".%d" % (index + 1)
2884 newpath = path + ".%d" % (index + 1)
2886 return newpath
2885 return newpath
2887
2886
2888 @command("^qpush",
2887 @command("^qpush",
2889 [('', 'keep-changes', None,
2888 [('', 'keep-changes', None,
2890 _('tolerate non-conflicting local changes')),
2889 _('tolerate non-conflicting local changes')),
2891 ('f', 'force', None, _('apply on top of local changes')),
2890 ('f', 'force', None, _('apply on top of local changes')),
2892 ('e', 'exact', None,
2891 ('e', 'exact', None,
2893 _('apply the target patch to its recorded parent')),
2892 _('apply the target patch to its recorded parent')),
2894 ('l', 'list', None, _('list patch name in commit text')),
2893 ('l', 'list', None, _('list patch name in commit text')),
2895 ('a', 'all', None, _('apply all patches')),
2894 ('a', 'all', None, _('apply all patches')),
2896 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2895 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2897 ('n', 'name', '',
2896 ('n', 'name', '',
2898 _('merge queue name (DEPRECATED)'), _('NAME')),
2897 _('merge queue name (DEPRECATED)'), _('NAME')),
2899 ('', 'move', None,
2898 ('', 'move', None,
2900 _('reorder patch series and apply only the patch')),
2899 _('reorder patch series and apply only the patch')),
2901 ('', 'no-backup', None, _('do not save backup copies of files'))],
2900 ('', 'no-backup', None, _('do not save backup copies of files'))],
2902 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2901 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2903 def push(ui, repo, patch=None, **opts):
2902 def push(ui, repo, patch=None, **opts):
2904 """push the next patch onto the stack
2903 """push the next patch onto the stack
2905
2904
2906 By default, abort if the working directory contains uncommitted
2905 By default, abort if the working directory contains uncommitted
2907 changes. With --keep-changes, abort only if the uncommitted files
2906 changes. With --keep-changes, abort only if the uncommitted files
2908 overlap with patched files. With -f/--force, backup and patch over
2907 overlap with patched files. With -f/--force, backup and patch over
2909 uncommitted changes.
2908 uncommitted changes.
2910
2909
2911 Return 0 on success.
2910 Return 0 on success.
2912 """
2911 """
2913 q = repo.mq
2912 q = repo.mq
2914 mergeq = None
2913 mergeq = None
2915
2914
2916 opts = fixkeepchangesopts(ui, opts)
2915 opts = fixkeepchangesopts(ui, opts)
2917 if opts.get('merge'):
2916 if opts.get('merge'):
2918 if opts.get('name'):
2917 if opts.get('name'):
2919 newpath = repo.join(opts.get('name'))
2918 newpath = repo.join(opts.get('name'))
2920 else:
2919 else:
2921 newpath, i = lastsavename(q.path)
2920 newpath, i = lastsavename(q.path)
2922 if not newpath:
2921 if not newpath:
2923 ui.warn(_("no saved queues found, please use -n\n"))
2922 ui.warn(_("no saved queues found, please use -n\n"))
2924 return 1
2923 return 1
2925 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2924 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2926 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2925 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2927 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2926 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2928 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2927 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2929 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2928 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2930 keepchanges=opts.get('keep_changes'))
2929 keepchanges=opts.get('keep_changes'))
2931 return ret
2930 return ret
2932
2931
2933 @command("^qpop",
2932 @command("^qpop",
2934 [('a', 'all', None, _('pop all patches')),
2933 [('a', 'all', None, _('pop all patches')),
2935 ('n', 'name', '',
2934 ('n', 'name', '',
2936 _('queue name to pop (DEPRECATED)'), _('NAME')),
2935 _('queue name to pop (DEPRECATED)'), _('NAME')),
2937 ('', 'keep-changes', None,
2936 ('', 'keep-changes', None,
2938 _('tolerate non-conflicting local changes')),
2937 _('tolerate non-conflicting local changes')),
2939 ('f', 'force', None, _('forget any local changes to patched files')),
2938 ('f', 'force', None, _('forget any local changes to patched files')),
2940 ('', 'no-backup', None, _('do not save backup copies of files'))],
2939 ('', 'no-backup', None, _('do not save backup copies of files'))],
2941 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2940 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2942 def pop(ui, repo, patch=None, **opts):
2941 def pop(ui, repo, patch=None, **opts):
2943 """pop the current patch off the stack
2942 """pop the current patch off the stack
2944
2943
2945 Without argument, pops off the top of the patch stack. If given a
2944 Without argument, pops off the top of the patch stack. If given a
2946 patch name, keeps popping off patches until the named patch is at
2945 patch name, keeps popping off patches until the named patch is at
2947 the top of the stack.
2946 the top of the stack.
2948
2947
2949 By default, abort if the working directory contains uncommitted
2948 By default, abort if the working directory contains uncommitted
2950 changes. With --keep-changes, abort only if the uncommitted files
2949 changes. With --keep-changes, abort only if the uncommitted files
2951 overlap with patched files. With -f/--force, backup and discard
2950 overlap with patched files. With -f/--force, backup and discard
2952 changes made to such files.
2951 changes made to such files.
2953
2952
2954 Return 0 on success.
2953 Return 0 on success.
2955 """
2954 """
2956 opts = fixkeepchangesopts(ui, opts)
2955 opts = fixkeepchangesopts(ui, opts)
2957 localupdate = True
2956 localupdate = True
2958 if opts.get('name'):
2957 if opts.get('name'):
2959 q = queue(ui, repo.baseui, repo.path, repo.join(opts.get('name')))
2958 q = queue(ui, repo.baseui, repo.path, repo.join(opts.get('name')))
2960 ui.warn(_('using patch queue: %s\n') % q.path)
2959 ui.warn(_('using patch queue: %s\n') % q.path)
2961 localupdate = False
2960 localupdate = False
2962 else:
2961 else:
2963 q = repo.mq
2962 q = repo.mq
2964 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2963 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2965 all=opts.get('all'), nobackup=opts.get('no_backup'),
2964 all=opts.get('all'), nobackup=opts.get('no_backup'),
2966 keepchanges=opts.get('keep_changes'))
2965 keepchanges=opts.get('keep_changes'))
2967 q.savedirty()
2966 q.savedirty()
2968 return ret
2967 return ret
2969
2968
2970 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2969 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2971 def rename(ui, repo, patch, name=None, **opts):
2970 def rename(ui, repo, patch, name=None, **opts):
2972 """rename a patch
2971 """rename a patch
2973
2972
2974 With one argument, renames the current patch to PATCH1.
2973 With one argument, renames the current patch to PATCH1.
2975 With two arguments, renames PATCH1 to PATCH2.
2974 With two arguments, renames PATCH1 to PATCH2.
2976
2975
2977 Returns 0 on success."""
2976 Returns 0 on success."""
2978 q = repo.mq
2977 q = repo.mq
2979 if not name:
2978 if not name:
2980 name = patch
2979 name = patch
2981 patch = None
2980 patch = None
2982
2981
2983 if patch:
2982 if patch:
2984 patch = q.lookup(patch)
2983 patch = q.lookup(patch)
2985 else:
2984 else:
2986 if not q.applied:
2985 if not q.applied:
2987 ui.write(_('no patches applied\n'))
2986 ui.write(_('no patches applied\n'))
2988 return
2987 return
2989 patch = q.lookup('qtip')
2988 patch = q.lookup('qtip')
2990 absdest = q.join(name)
2989 absdest = q.join(name)
2991 if os.path.isdir(absdest):
2990 if os.path.isdir(absdest):
2992 name = normname(os.path.join(name, os.path.basename(patch)))
2991 name = normname(os.path.join(name, os.path.basename(patch)))
2993 absdest = q.join(name)
2992 absdest = q.join(name)
2994 q.checkpatchname(name)
2993 q.checkpatchname(name)
2995
2994
2996 ui.note(_('renaming %s to %s\n') % (patch, name))
2995 ui.note(_('renaming %s to %s\n') % (patch, name))
2997 i = q.findseries(patch)
2996 i = q.findseries(patch)
2998 guards = q.guard_re.findall(q.fullseries[i])
2997 guards = q.guard_re.findall(q.fullseries[i])
2999 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2998 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
3000 q.parseseries()
2999 q.parseseries()
3001 q.seriesdirty = True
3000 q.seriesdirty = True
3002
3001
3003 info = q.isapplied(patch)
3002 info = q.isapplied(patch)
3004 if info:
3003 if info:
3005 q.applied[info[0]] = statusentry(info[1], name)
3004 q.applied[info[0]] = statusentry(info[1], name)
3006 q.applieddirty = True
3005 q.applieddirty = True
3007
3006
3008 destdir = os.path.dirname(absdest)
3007 destdir = os.path.dirname(absdest)
3009 if not os.path.isdir(destdir):
3008 if not os.path.isdir(destdir):
3010 os.makedirs(destdir)
3009 os.makedirs(destdir)
3011 util.rename(q.join(patch), absdest)
3010 util.rename(q.join(patch), absdest)
3012 r = q.qrepo()
3011 r = q.qrepo()
3013 if r and patch in r.dirstate:
3012 if r and patch in r.dirstate:
3014 wctx = r[None]
3013 wctx = r[None]
3015 with r.wlock():
3014 with r.wlock():
3016 if r.dirstate[patch] == 'a':
3015 if r.dirstate[patch] == 'a':
3017 r.dirstate.drop(patch)
3016 r.dirstate.drop(patch)
3018 r.dirstate.add(name)
3017 r.dirstate.add(name)
3019 else:
3018 else:
3020 wctx.copy(patch, name)
3019 wctx.copy(patch, name)
3021 wctx.forget([patch])
3020 wctx.forget([patch])
3022
3021
3023 q.savedirty()
3022 q.savedirty()
3024
3023
3025 @command("qrestore",
3024 @command("qrestore",
3026 [('d', 'delete', None, _('delete save entry')),
3025 [('d', 'delete', None, _('delete save entry')),
3027 ('u', 'update', None, _('update queue working directory'))],
3026 ('u', 'update', None, _('update queue working directory'))],
3028 _('hg qrestore [-d] [-u] REV'))
3027 _('hg qrestore [-d] [-u] REV'))
3029 def restore(ui, repo, rev, **opts):
3028 def restore(ui, repo, rev, **opts):
3030 """restore the queue state saved by a revision (DEPRECATED)
3029 """restore the queue state saved by a revision (DEPRECATED)
3031
3030
3032 This command is deprecated, use :hg:`rebase` instead."""
3031 This command is deprecated, use :hg:`rebase` instead."""
3033 rev = repo.lookup(rev)
3032 rev = repo.lookup(rev)
3034 q = repo.mq
3033 q = repo.mq
3035 q.restore(repo, rev, delete=opts.get('delete'),
3034 q.restore(repo, rev, delete=opts.get('delete'),
3036 qupdate=opts.get('update'))
3035 qupdate=opts.get('update'))
3037 q.savedirty()
3036 q.savedirty()
3038 return 0
3037 return 0
3039
3038
3040 @command("qsave",
3039 @command("qsave",
3041 [('c', 'copy', None, _('copy patch directory')),
3040 [('c', 'copy', None, _('copy patch directory')),
3042 ('n', 'name', '',
3041 ('n', 'name', '',
3043 _('copy directory name'), _('NAME')),
3042 _('copy directory name'), _('NAME')),
3044 ('e', 'empty', None, _('clear queue status file')),
3043 ('e', 'empty', None, _('clear queue status file')),
3045 ('f', 'force', None, _('force copy'))] + commands.commitopts,
3044 ('f', 'force', None, _('force copy'))] + commands.commitopts,
3046 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
3045 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
3047 def save(ui, repo, **opts):
3046 def save(ui, repo, **opts):
3048 """save current queue state (DEPRECATED)
3047 """save current queue state (DEPRECATED)
3049
3048
3050 This command is deprecated, use :hg:`rebase` instead."""
3049 This command is deprecated, use :hg:`rebase` instead."""
3051 q = repo.mq
3050 q = repo.mq
3052 message = cmdutil.logmessage(ui, opts)
3051 message = cmdutil.logmessage(ui, opts)
3053 ret = q.save(repo, msg=message)
3052 ret = q.save(repo, msg=message)
3054 if ret:
3053 if ret:
3055 return ret
3054 return ret
3056 q.savedirty() # save to .hg/patches before copying
3055 q.savedirty() # save to .hg/patches before copying
3057 if opts.get('copy'):
3056 if opts.get('copy'):
3058 path = q.path
3057 path = q.path
3059 if opts.get('name'):
3058 if opts.get('name'):
3060 newpath = os.path.join(q.basepath, opts.get('name'))
3059 newpath = os.path.join(q.basepath, opts.get('name'))
3061 if os.path.exists(newpath):
3060 if os.path.exists(newpath):
3062 if not os.path.isdir(newpath):
3061 if not os.path.isdir(newpath):
3063 raise error.Abort(_('destination %s exists and is not '
3062 raise error.Abort(_('destination %s exists and is not '
3064 'a directory') % newpath)
3063 'a directory') % newpath)
3065 if not opts.get('force'):
3064 if not opts.get('force'):
3066 raise error.Abort(_('destination %s exists, '
3065 raise error.Abort(_('destination %s exists, '
3067 'use -f to force') % newpath)
3066 'use -f to force') % newpath)
3068 else:
3067 else:
3069 newpath = savename(path)
3068 newpath = savename(path)
3070 ui.warn(_("copy %s to %s\n") % (path, newpath))
3069 ui.warn(_("copy %s to %s\n") % (path, newpath))
3071 util.copyfiles(path, newpath)
3070 util.copyfiles(path, newpath)
3072 if opts.get('empty'):
3071 if opts.get('empty'):
3073 del q.applied[:]
3072 del q.applied[:]
3074 q.applieddirty = True
3073 q.applieddirty = True
3075 q.savedirty()
3074 q.savedirty()
3076 return 0
3075 return 0
3077
3076
3078
3077
3079 @command("qselect",
3078 @command("qselect",
3080 [('n', 'none', None, _('disable all guards')),
3079 [('n', 'none', None, _('disable all guards')),
3081 ('s', 'series', None, _('list all guards in series file')),
3080 ('s', 'series', None, _('list all guards in series file')),
3082 ('', 'pop', None, _('pop to before first guarded applied patch')),
3081 ('', 'pop', None, _('pop to before first guarded applied patch')),
3083 ('', 'reapply', None, _('pop, then reapply patches'))],
3082 ('', 'reapply', None, _('pop, then reapply patches'))],
3084 _('hg qselect [OPTION]... [GUARD]...'))
3083 _('hg qselect [OPTION]... [GUARD]...'))
3085 def select(ui, repo, *args, **opts):
3084 def select(ui, repo, *args, **opts):
3086 '''set or print guarded patches to push
3085 '''set or print guarded patches to push
3087
3086
3088 Use the :hg:`qguard` command to set or print guards on patch, then use
3087 Use the :hg:`qguard` command to set or print guards on patch, then use
3089 qselect to tell mq which guards to use. A patch will be pushed if
3088 qselect to tell mq which guards to use. A patch will be pushed if
3090 it has no guards or any positive guards match the currently
3089 it has no guards or any positive guards match the currently
3091 selected guard, but will not be pushed if any negative guards
3090 selected guard, but will not be pushed if any negative guards
3092 match the current guard. For example::
3091 match the current guard. For example::
3093
3092
3094 qguard foo.patch -- -stable (negative guard)
3093 qguard foo.patch -- -stable (negative guard)
3095 qguard bar.patch +stable (positive guard)
3094 qguard bar.patch +stable (positive guard)
3096 qselect stable
3095 qselect stable
3097
3096
3098 This activates the "stable" guard. mq will skip foo.patch (because
3097 This activates the "stable" guard. mq will skip foo.patch (because
3099 it has a negative match) but push bar.patch (because it has a
3098 it has a negative match) but push bar.patch (because it has a
3100 positive match).
3099 positive match).
3101
3100
3102 With no arguments, prints the currently active guards.
3101 With no arguments, prints the currently active guards.
3103 With one argument, sets the active guard.
3102 With one argument, sets the active guard.
3104
3103
3105 Use -n/--none to deactivate guards (no other arguments needed).
3104 Use -n/--none to deactivate guards (no other arguments needed).
3106 When no guards are active, patches with positive guards are
3105 When no guards are active, patches with positive guards are
3107 skipped and patches with negative guards are pushed.
3106 skipped and patches with negative guards are pushed.
3108
3107
3109 qselect can change the guards on applied patches. It does not pop
3108 qselect can change the guards on applied patches. It does not pop
3110 guarded patches by default. Use --pop to pop back to the last
3109 guarded patches by default. Use --pop to pop back to the last
3111 applied patch that is not guarded. Use --reapply (which implies
3110 applied patch that is not guarded. Use --reapply (which implies
3112 --pop) to push back to the current patch afterwards, but skip
3111 --pop) to push back to the current patch afterwards, but skip
3113 guarded patches.
3112 guarded patches.
3114
3113
3115 Use -s/--series to print a list of all guards in the series file
3114 Use -s/--series to print a list of all guards in the series file
3116 (no other arguments needed). Use -v for more information.
3115 (no other arguments needed). Use -v for more information.
3117
3116
3118 Returns 0 on success.'''
3117 Returns 0 on success.'''
3119
3118
3120 q = repo.mq
3119 q = repo.mq
3121 guards = q.active()
3120 guards = q.active()
3122 pushable = lambda i: q.pushable(q.applied[i].name)[0]
3121 pushable = lambda i: q.pushable(q.applied[i].name)[0]
3123 if args or opts.get('none'):
3122 if args or opts.get('none'):
3124 old_unapplied = q.unapplied(repo)
3123 old_unapplied = q.unapplied(repo)
3125 old_guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3124 old_guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3126 q.setactive(args)
3125 q.setactive(args)
3127 q.savedirty()
3126 q.savedirty()
3128 if not args:
3127 if not args:
3129 ui.status(_('guards deactivated\n'))
3128 ui.status(_('guards deactivated\n'))
3130 if not opts.get('pop') and not opts.get('reapply'):
3129 if not opts.get('pop') and not opts.get('reapply'):
3131 unapplied = q.unapplied(repo)
3130 unapplied = q.unapplied(repo)
3132 guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3131 guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3133 if len(unapplied) != len(old_unapplied):
3132 if len(unapplied) != len(old_unapplied):
3134 ui.status(_('number of unguarded, unapplied patches has '
3133 ui.status(_('number of unguarded, unapplied patches has '
3135 'changed from %d to %d\n') %
3134 'changed from %d to %d\n') %
3136 (len(old_unapplied), len(unapplied)))
3135 (len(old_unapplied), len(unapplied)))
3137 if len(guarded) != len(old_guarded):
3136 if len(guarded) != len(old_guarded):
3138 ui.status(_('number of guarded, applied patches has changed '
3137 ui.status(_('number of guarded, applied patches has changed '
3139 'from %d to %d\n') %
3138 'from %d to %d\n') %
3140 (len(old_guarded), len(guarded)))
3139 (len(old_guarded), len(guarded)))
3141 elif opts.get('series'):
3140 elif opts.get('series'):
3142 guards = {}
3141 guards = {}
3143 noguards = 0
3142 noguards = 0
3144 for gs in q.seriesguards:
3143 for gs in q.seriesguards:
3145 if not gs:
3144 if not gs:
3146 noguards += 1
3145 noguards += 1
3147 for g in gs:
3146 for g in gs:
3148 guards.setdefault(g, 0)
3147 guards.setdefault(g, 0)
3149 guards[g] += 1
3148 guards[g] += 1
3150 if ui.verbose:
3149 if ui.verbose:
3151 guards['NONE'] = noguards
3150 guards['NONE'] = noguards
3152 guards = guards.items()
3151 guards = guards.items()
3153 guards.sort(key=lambda x: x[0][1:])
3152 guards.sort(key=lambda x: x[0][1:])
3154 if guards:
3153 if guards:
3155 ui.note(_('guards in series file:\n'))
3154 ui.note(_('guards in series file:\n'))
3156 for guard, count in guards:
3155 for guard, count in guards:
3157 ui.note('%2d ' % count)
3156 ui.note('%2d ' % count)
3158 ui.write(guard, '\n')
3157 ui.write(guard, '\n')
3159 else:
3158 else:
3160 ui.note(_('no guards in series file\n'))
3159 ui.note(_('no guards in series file\n'))
3161 else:
3160 else:
3162 if guards:
3161 if guards:
3163 ui.note(_('active guards:\n'))
3162 ui.note(_('active guards:\n'))
3164 for g in guards:
3163 for g in guards:
3165 ui.write(g, '\n')
3164 ui.write(g, '\n')
3166 else:
3165 else:
3167 ui.write(_('no active guards\n'))
3166 ui.write(_('no active guards\n'))
3168 reapply = opts.get('reapply') and q.applied and q.applied[-1].name
3167 reapply = opts.get('reapply') and q.applied and q.applied[-1].name
3169 popped = False
3168 popped = False
3170 if opts.get('pop') or opts.get('reapply'):
3169 if opts.get('pop') or opts.get('reapply'):
3171 for i in xrange(len(q.applied)):
3170 for i in xrange(len(q.applied)):
3172 if not pushable(i):
3171 if not pushable(i):
3173 ui.status(_('popping guarded patches\n'))
3172 ui.status(_('popping guarded patches\n'))
3174 popped = True
3173 popped = True
3175 if i == 0:
3174 if i == 0:
3176 q.pop(repo, all=True)
3175 q.pop(repo, all=True)
3177 else:
3176 else:
3178 q.pop(repo, q.applied[i - 1].name)
3177 q.pop(repo, q.applied[i - 1].name)
3179 break
3178 break
3180 if popped:
3179 if popped:
3181 try:
3180 try:
3182 if reapply:
3181 if reapply:
3183 ui.status(_('reapplying unguarded patches\n'))
3182 ui.status(_('reapplying unguarded patches\n'))
3184 q.push(repo, reapply)
3183 q.push(repo, reapply)
3185 finally:
3184 finally:
3186 q.savedirty()
3185 q.savedirty()
3187
3186
3188 @command("qfinish",
3187 @command("qfinish",
3189 [('a', 'applied', None, _('finish all applied changesets'))],
3188 [('a', 'applied', None, _('finish all applied changesets'))],
3190 _('hg qfinish [-a] [REV]...'))
3189 _('hg qfinish [-a] [REV]...'))
3191 def finish(ui, repo, *revrange, **opts):
3190 def finish(ui, repo, *revrange, **opts):
3192 """move applied patches into repository history
3191 """move applied patches into repository history
3193
3192
3194 Finishes the specified revisions (corresponding to applied
3193 Finishes the specified revisions (corresponding to applied
3195 patches) by moving them out of mq control into regular repository
3194 patches) by moving them out of mq control into regular repository
3196 history.
3195 history.
3197
3196
3198 Accepts a revision range or the -a/--applied option. If --applied
3197 Accepts a revision range or the -a/--applied option. If --applied
3199 is specified, all applied mq revisions are removed from mq
3198 is specified, all applied mq revisions are removed from mq
3200 control. Otherwise, the given revisions must be at the base of the
3199 control. Otherwise, the given revisions must be at the base of the
3201 stack of applied patches.
3200 stack of applied patches.
3202
3201
3203 This can be especially useful if your changes have been applied to
3202 This can be especially useful if your changes have been applied to
3204 an upstream repository, or if you are about to push your changes
3203 an upstream repository, or if you are about to push your changes
3205 to upstream.
3204 to upstream.
3206
3205
3207 Returns 0 on success.
3206 Returns 0 on success.
3208 """
3207 """
3209 if not opts.get('applied') and not revrange:
3208 if not opts.get('applied') and not revrange:
3210 raise error.Abort(_('no revisions specified'))
3209 raise error.Abort(_('no revisions specified'))
3211 elif opts.get('applied'):
3210 elif opts.get('applied'):
3212 revrange = ('qbase::qtip',) + revrange
3211 revrange = ('qbase::qtip',) + revrange
3213
3212
3214 q = repo.mq
3213 q = repo.mq
3215 if not q.applied:
3214 if not q.applied:
3216 ui.status(_('no patches applied\n'))
3215 ui.status(_('no patches applied\n'))
3217 return 0
3216 return 0
3218
3217
3219 revs = scmutil.revrange(repo, revrange)
3218 revs = scmutil.revrange(repo, revrange)
3220 if repo['.'].rev() in revs and repo[None].files():
3219 if repo['.'].rev() in revs and repo[None].files():
3221 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3220 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3222 # queue.finish may changes phases but leave the responsibility to lock the
3221 # queue.finish may changes phases but leave the responsibility to lock the
3223 # repo to the caller to avoid deadlock with wlock. This command code is
3222 # repo to the caller to avoid deadlock with wlock. This command code is
3224 # responsibility for this locking.
3223 # responsibility for this locking.
3225 with repo.lock():
3224 with repo.lock():
3226 q.finish(repo, revs)
3225 q.finish(repo, revs)
3227 q.savedirty()
3226 q.savedirty()
3228 return 0
3227 return 0
3229
3228
3230 @command("qqueue",
3229 @command("qqueue",
3231 [('l', 'list', False, _('list all available queues')),
3230 [('l', 'list', False, _('list all available queues')),
3232 ('', 'active', False, _('print name of active queue')),
3231 ('', 'active', False, _('print name of active queue')),
3233 ('c', 'create', False, _('create new queue')),
3232 ('c', 'create', False, _('create new queue')),
3234 ('', 'rename', False, _('rename active queue')),
3233 ('', 'rename', False, _('rename active queue')),
3235 ('', 'delete', False, _('delete reference to queue')),
3234 ('', 'delete', False, _('delete reference to queue')),
3236 ('', 'purge', False, _('delete queue, and remove patch dir')),
3235 ('', 'purge', False, _('delete queue, and remove patch dir')),
3237 ],
3236 ],
3238 _('[OPTION] [QUEUE]'))
3237 _('[OPTION] [QUEUE]'))
3239 def qqueue(ui, repo, name=None, **opts):
3238 def qqueue(ui, repo, name=None, **opts):
3240 '''manage multiple patch queues
3239 '''manage multiple patch queues
3241
3240
3242 Supports switching between different patch queues, as well as creating
3241 Supports switching between different patch queues, as well as creating
3243 new patch queues and deleting existing ones.
3242 new patch queues and deleting existing ones.
3244
3243
3245 Omitting a queue name or specifying -l/--list will show you the registered
3244 Omitting a queue name or specifying -l/--list will show you the registered
3246 queues - by default the "normal" patches queue is registered. The currently
3245 queues - by default the "normal" patches queue is registered. The currently
3247 active queue will be marked with "(active)". Specifying --active will print
3246 active queue will be marked with "(active)". Specifying --active will print
3248 only the name of the active queue.
3247 only the name of the active queue.
3249
3248
3250 To create a new queue, use -c/--create. The queue is automatically made
3249 To create a new queue, use -c/--create. The queue is automatically made
3251 active, except in the case where there are applied patches from the
3250 active, except in the case where there are applied patches from the
3252 currently active queue in the repository. Then the queue will only be
3251 currently active queue in the repository. Then the queue will only be
3253 created and switching will fail.
3252 created and switching will fail.
3254
3253
3255 To delete an existing queue, use --delete. You cannot delete the currently
3254 To delete an existing queue, use --delete. You cannot delete the currently
3256 active queue.
3255 active queue.
3257
3256
3258 Returns 0 on success.
3257 Returns 0 on success.
3259 '''
3258 '''
3260 q = repo.mq
3259 q = repo.mq
3261 _defaultqueue = 'patches'
3260 _defaultqueue = 'patches'
3262 _allqueues = 'patches.queues'
3261 _allqueues = 'patches.queues'
3263 _activequeue = 'patches.queue'
3262 _activequeue = 'patches.queue'
3264
3263
3265 def _getcurrent():
3264 def _getcurrent():
3266 cur = os.path.basename(q.path)
3265 cur = os.path.basename(q.path)
3267 if cur.startswith('patches-'):
3266 if cur.startswith('patches-'):
3268 cur = cur[8:]
3267 cur = cur[8:]
3269 return cur
3268 return cur
3270
3269
3271 def _noqueues():
3270 def _noqueues():
3272 try:
3271 try:
3273 fh = repo.vfs(_allqueues, 'r')
3272 fh = repo.vfs(_allqueues, 'r')
3274 fh.close()
3273 fh.close()
3275 except IOError:
3274 except IOError:
3276 return True
3275 return True
3277
3276
3278 return False
3277 return False
3279
3278
3280 def _getqueues():
3279 def _getqueues():
3281 current = _getcurrent()
3280 current = _getcurrent()
3282
3281
3283 try:
3282 try:
3284 fh = repo.vfs(_allqueues, 'r')
3283 fh = repo.vfs(_allqueues, 'r')
3285 queues = [queue.strip() for queue in fh if queue.strip()]
3284 queues = [queue.strip() for queue in fh if queue.strip()]
3286 fh.close()
3285 fh.close()
3287 if current not in queues:
3286 if current not in queues:
3288 queues.append(current)
3287 queues.append(current)
3289 except IOError:
3288 except IOError:
3290 queues = [_defaultqueue]
3289 queues = [_defaultqueue]
3291
3290
3292 return sorted(queues)
3291 return sorted(queues)
3293
3292
3294 def _setactive(name):
3293 def _setactive(name):
3295 if q.applied:
3294 if q.applied:
3296 raise error.Abort(_('new queue created, but cannot make active '
3295 raise error.Abort(_('new queue created, but cannot make active '
3297 'as patches are applied'))
3296 'as patches are applied'))
3298 _setactivenocheck(name)
3297 _setactivenocheck(name)
3299
3298
3300 def _setactivenocheck(name):
3299 def _setactivenocheck(name):
3301 fh = repo.vfs(_activequeue, 'w')
3300 fh = repo.vfs(_activequeue, 'w')
3302 if name != 'patches':
3301 if name != 'patches':
3303 fh.write(name)
3302 fh.write(name)
3304 fh.close()
3303 fh.close()
3305
3304
3306 def _addqueue(name):
3305 def _addqueue(name):
3307 fh = repo.vfs(_allqueues, 'a')
3306 fh = repo.vfs(_allqueues, 'a')
3308 fh.write('%s\n' % (name,))
3307 fh.write('%s\n' % (name,))
3309 fh.close()
3308 fh.close()
3310
3309
3311 def _queuedir(name):
3310 def _queuedir(name):
3312 if name == 'patches':
3311 if name == 'patches':
3313 return repo.join('patches')
3312 return repo.join('patches')
3314 else:
3313 else:
3315 return repo.join('patches-' + name)
3314 return repo.join('patches-' + name)
3316
3315
3317 def _validname(name):
3316 def _validname(name):
3318 for n in name:
3317 for n in name:
3319 if n in ':\\/.':
3318 if n in ':\\/.':
3320 return False
3319 return False
3321 return True
3320 return True
3322
3321
3323 def _delete(name):
3322 def _delete(name):
3324 if name not in existing:
3323 if name not in existing:
3325 raise error.Abort(_('cannot delete queue that does not exist'))
3324 raise error.Abort(_('cannot delete queue that does not exist'))
3326
3325
3327 current = _getcurrent()
3326 current = _getcurrent()
3328
3327
3329 if name == current:
3328 if name == current:
3330 raise error.Abort(_('cannot delete currently active queue'))
3329 raise error.Abort(_('cannot delete currently active queue'))
3331
3330
3332 fh = repo.vfs('patches.queues.new', 'w')
3331 fh = repo.vfs('patches.queues.new', 'w')
3333 for queue in existing:
3332 for queue in existing:
3334 if queue == name:
3333 if queue == name:
3335 continue
3334 continue
3336 fh.write('%s\n' % (queue,))
3335 fh.write('%s\n' % (queue,))
3337 fh.close()
3336 fh.close()
3338 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3337 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3339
3338
3340 if not name or opts.get('list') or opts.get('active'):
3339 if not name or opts.get('list') or opts.get('active'):
3341 current = _getcurrent()
3340 current = _getcurrent()
3342 if opts.get('active'):
3341 if opts.get('active'):
3343 ui.write('%s\n' % (current,))
3342 ui.write('%s\n' % (current,))
3344 return
3343 return
3345 for queue in _getqueues():
3344 for queue in _getqueues():
3346 ui.write('%s' % (queue,))
3345 ui.write('%s' % (queue,))
3347 if queue == current and not ui.quiet:
3346 if queue == current and not ui.quiet:
3348 ui.write(_(' (active)\n'))
3347 ui.write(_(' (active)\n'))
3349 else:
3348 else:
3350 ui.write('\n')
3349 ui.write('\n')
3351 return
3350 return
3352
3351
3353 if not _validname(name):
3352 if not _validname(name):
3354 raise error.Abort(
3353 raise error.Abort(
3355 _('invalid queue name, may not contain the characters ":\\/."'))
3354 _('invalid queue name, may not contain the characters ":\\/."'))
3356
3355
3357 with repo.wlock():
3356 with repo.wlock():
3358 existing = _getqueues()
3357 existing = _getqueues()
3359
3358
3360 if opts.get('create'):
3359 if opts.get('create'):
3361 if name in existing:
3360 if name in existing:
3362 raise error.Abort(_('queue "%s" already exists') % name)
3361 raise error.Abort(_('queue "%s" already exists') % name)
3363 if _noqueues():
3362 if _noqueues():
3364 _addqueue(_defaultqueue)
3363 _addqueue(_defaultqueue)
3365 _addqueue(name)
3364 _addqueue(name)
3366 _setactive(name)
3365 _setactive(name)
3367 elif opts.get('rename'):
3366 elif opts.get('rename'):
3368 current = _getcurrent()
3367 current = _getcurrent()
3369 if name == current:
3368 if name == current:
3370 raise error.Abort(_('can\'t rename "%s" to its current name')
3369 raise error.Abort(_('can\'t rename "%s" to its current name')
3371 % name)
3370 % name)
3372 if name in existing:
3371 if name in existing:
3373 raise error.Abort(_('queue "%s" already exists') % name)
3372 raise error.Abort(_('queue "%s" already exists') % name)
3374
3373
3375 olddir = _queuedir(current)
3374 olddir = _queuedir(current)
3376 newdir = _queuedir(name)
3375 newdir = _queuedir(name)
3377
3376
3378 if os.path.exists(newdir):
3377 if os.path.exists(newdir):
3379 raise error.Abort(_('non-queue directory "%s" already exists') %
3378 raise error.Abort(_('non-queue directory "%s" already exists') %
3380 newdir)
3379 newdir)
3381
3380
3382 fh = repo.vfs('patches.queues.new', 'w')
3381 fh = repo.vfs('patches.queues.new', 'w')
3383 for queue in existing:
3382 for queue in existing:
3384 if queue == current:
3383 if queue == current:
3385 fh.write('%s\n' % (name,))
3384 fh.write('%s\n' % (name,))
3386 if os.path.exists(olddir):
3385 if os.path.exists(olddir):
3387 util.rename(olddir, newdir)
3386 util.rename(olddir, newdir)
3388 else:
3387 else:
3389 fh.write('%s\n' % (queue,))
3388 fh.write('%s\n' % (queue,))
3390 fh.close()
3389 fh.close()
3391 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3390 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3392 _setactivenocheck(name)
3391 _setactivenocheck(name)
3393 elif opts.get('delete'):
3392 elif opts.get('delete'):
3394 _delete(name)
3393 _delete(name)
3395 elif opts.get('purge'):
3394 elif opts.get('purge'):
3396 if name in existing:
3395 if name in existing:
3397 _delete(name)
3396 _delete(name)
3398 qdir = _queuedir(name)
3397 qdir = _queuedir(name)
3399 if os.path.exists(qdir):
3398 if os.path.exists(qdir):
3400 shutil.rmtree(qdir)
3399 shutil.rmtree(qdir)
3401 else:
3400 else:
3402 if name not in existing:
3401 if name not in existing:
3403 raise error.Abort(_('use --create to create a new queue'))
3402 raise error.Abort(_('use --create to create a new queue'))
3404 _setactive(name)
3403 _setactive(name)
3405
3404
3406 def mqphasedefaults(repo, roots):
3405 def mqphasedefaults(repo, roots):
3407 """callback used to set mq changeset as secret when no phase data exists"""
3406 """callback used to set mq changeset as secret when no phase data exists"""
3408 if repo.mq.applied:
3407 if repo.mq.applied:
3409 if repo.ui.configbool('mq', 'secret', False):
3408 if repo.ui.configbool('mq', 'secret', False):
3410 mqphase = phases.secret
3409 mqphase = phases.secret
3411 else:
3410 else:
3412 mqphase = phases.draft
3411 mqphase = phases.draft
3413 qbase = repo[repo.mq.applied[0].node]
3412 qbase = repo[repo.mq.applied[0].node]
3414 roots[mqphase].add(qbase.node())
3413 roots[mqphase].add(qbase.node())
3415 return roots
3414 return roots
3416
3415
3417 def reposetup(ui, repo):
3416 def reposetup(ui, repo):
3418 class mqrepo(repo.__class__):
3417 class mqrepo(repo.__class__):
3419 @localrepo.unfilteredpropertycache
3418 @localrepo.unfilteredpropertycache
3420 def mq(self):
3419 def mq(self):
3421 return queue(self.ui, self.baseui, self.path)
3420 return queue(self.ui, self.baseui, self.path)
3422
3421
3423 def invalidateall(self):
3422 def invalidateall(self):
3424 super(mqrepo, self).invalidateall()
3423 super(mqrepo, self).invalidateall()
3425 if localrepo.hasunfilteredcache(self, 'mq'):
3424 if localrepo.hasunfilteredcache(self, 'mq'):
3426 # recreate mq in case queue path was changed
3425 # recreate mq in case queue path was changed
3427 delattr(self.unfiltered(), 'mq')
3426 delattr(self.unfiltered(), 'mq')
3428
3427
3429 def abortifwdirpatched(self, errmsg, force=False):
3428 def abortifwdirpatched(self, errmsg, force=False):
3430 if self.mq.applied and self.mq.checkapplied and not force:
3429 if self.mq.applied and self.mq.checkapplied and not force:
3431 parents = self.dirstate.parents()
3430 parents = self.dirstate.parents()
3432 patches = [s.node for s in self.mq.applied]
3431 patches = [s.node for s in self.mq.applied]
3433 if parents[0] in patches or parents[1] in patches:
3432 if parents[0] in patches or parents[1] in patches:
3434 raise error.Abort(errmsg)
3433 raise error.Abort(errmsg)
3435
3434
3436 def commit(self, text="", user=None, date=None, match=None,
3435 def commit(self, text="", user=None, date=None, match=None,
3437 force=False, editor=False, extra={}):
3436 force=False, editor=False, extra={}):
3438 self.abortifwdirpatched(
3437 self.abortifwdirpatched(
3439 _('cannot commit over an applied mq patch'),
3438 _('cannot commit over an applied mq patch'),
3440 force)
3439 force)
3441
3440
3442 return super(mqrepo, self).commit(text, user, date, match, force,
3441 return super(mqrepo, self).commit(text, user, date, match, force,
3443 editor, extra)
3442 editor, extra)
3444
3443
3445 def checkpush(self, pushop):
3444 def checkpush(self, pushop):
3446 if self.mq.applied and self.mq.checkapplied and not pushop.force:
3445 if self.mq.applied and self.mq.checkapplied and not pushop.force:
3447 outapplied = [e.node for e in self.mq.applied]
3446 outapplied = [e.node for e in self.mq.applied]
3448 if pushop.revs:
3447 if pushop.revs:
3449 # Assume applied patches have no non-patch descendants and
3448 # Assume applied patches have no non-patch descendants and
3450 # are not on remote already. Filtering any changeset not
3449 # are not on remote already. Filtering any changeset not
3451 # pushed.
3450 # pushed.
3452 heads = set(pushop.revs)
3451 heads = set(pushop.revs)
3453 for node in reversed(outapplied):
3452 for node in reversed(outapplied):
3454 if node in heads:
3453 if node in heads:
3455 break
3454 break
3456 else:
3455 else:
3457 outapplied.pop()
3456 outapplied.pop()
3458 # looking for pushed and shared changeset
3457 # looking for pushed and shared changeset
3459 for node in outapplied:
3458 for node in outapplied:
3460 if self[node].phase() < phases.secret:
3459 if self[node].phase() < phases.secret:
3461 raise error.Abort(_('source has mq patches applied'))
3460 raise error.Abort(_('source has mq patches applied'))
3462 # no non-secret patches pushed
3461 # no non-secret patches pushed
3463 super(mqrepo, self).checkpush(pushop)
3462 super(mqrepo, self).checkpush(pushop)
3464
3463
3465 def _findtags(self):
3464 def _findtags(self):
3466 '''augment tags from base class with patch tags'''
3465 '''augment tags from base class with patch tags'''
3467 result = super(mqrepo, self)._findtags()
3466 result = super(mqrepo, self)._findtags()
3468
3467
3469 q = self.mq
3468 q = self.mq
3470 if not q.applied:
3469 if not q.applied:
3471 return result
3470 return result
3472
3471
3473 mqtags = [(patch.node, patch.name) for patch in q.applied]
3472 mqtags = [(patch.node, patch.name) for patch in q.applied]
3474
3473
3475 try:
3474 try:
3476 # for now ignore filtering business
3475 # for now ignore filtering business
3477 self.unfiltered().changelog.rev(mqtags[-1][0])
3476 self.unfiltered().changelog.rev(mqtags[-1][0])
3478 except error.LookupError:
3477 except error.LookupError:
3479 self.ui.warn(_('mq status file refers to unknown node %s\n')
3478 self.ui.warn(_('mq status file refers to unknown node %s\n')
3480 % short(mqtags[-1][0]))
3479 % short(mqtags[-1][0]))
3481 return result
3480 return result
3482
3481
3483 # do not add fake tags for filtered revisions
3482 # do not add fake tags for filtered revisions
3484 included = self.changelog.hasnode
3483 included = self.changelog.hasnode
3485 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3484 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3486 if not mqtags:
3485 if not mqtags:
3487 return result
3486 return result
3488
3487
3489 mqtags.append((mqtags[-1][0], 'qtip'))
3488 mqtags.append((mqtags[-1][0], 'qtip'))
3490 mqtags.append((mqtags[0][0], 'qbase'))
3489 mqtags.append((mqtags[0][0], 'qbase'))
3491 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3490 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3492 tags = result[0]
3491 tags = result[0]
3493 for patch in mqtags:
3492 for patch in mqtags:
3494 if patch[1] in tags:
3493 if patch[1] in tags:
3495 self.ui.warn(_('tag %s overrides mq patch of the same '
3494 self.ui.warn(_('tag %s overrides mq patch of the same '
3496 'name\n') % patch[1])
3495 'name\n') % patch[1])
3497 else:
3496 else:
3498 tags[patch[1]] = patch[0]
3497 tags[patch[1]] = patch[0]
3499
3498
3500 return result
3499 return result
3501
3500
3502 if repo.local():
3501 if repo.local():
3503 repo.__class__ = mqrepo
3502 repo.__class__ = mqrepo
3504
3503
3505 repo._phasedefaults.append(mqphasedefaults)
3504 repo._phasedefaults.append(mqphasedefaults)
3506
3505
3507 def mqimport(orig, ui, repo, *args, **kwargs):
3506 def mqimport(orig, ui, repo, *args, **kwargs):
3508 if (util.safehasattr(repo, 'abortifwdirpatched')
3507 if (util.safehasattr(repo, 'abortifwdirpatched')
3509 and not kwargs.get('no_commit', False)):
3508 and not kwargs.get('no_commit', False)):
3510 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3509 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3511 kwargs.get('force'))
3510 kwargs.get('force'))
3512 return orig(ui, repo, *args, **kwargs)
3511 return orig(ui, repo, *args, **kwargs)
3513
3512
3514 def mqinit(orig, ui, *args, **kwargs):
3513 def mqinit(orig, ui, *args, **kwargs):
3515 mq = kwargs.pop('mq', None)
3514 mq = kwargs.pop('mq', None)
3516
3515
3517 if not mq:
3516 if not mq:
3518 return orig(ui, *args, **kwargs)
3517 return orig(ui, *args, **kwargs)
3519
3518
3520 if args:
3519 if args:
3521 repopath = args[0]
3520 repopath = args[0]
3522 if not hg.islocal(repopath):
3521 if not hg.islocal(repopath):
3523 raise error.Abort(_('only a local queue repository '
3522 raise error.Abort(_('only a local queue repository '
3524 'may be initialized'))
3523 'may be initialized'))
3525 else:
3524 else:
3526 repopath = cmdutil.findrepo(os.getcwd())
3525 repopath = cmdutil.findrepo(os.getcwd())
3527 if not repopath:
3526 if not repopath:
3528 raise error.Abort(_('there is no Mercurial repository here '
3527 raise error.Abort(_('there is no Mercurial repository here '
3529 '(.hg not found)'))
3528 '(.hg not found)'))
3530 repo = hg.repository(ui, repopath)
3529 repo = hg.repository(ui, repopath)
3531 return qinit(ui, repo, True)
3530 return qinit(ui, repo, True)
3532
3531
3533 def mqcommand(orig, ui, repo, *args, **kwargs):
3532 def mqcommand(orig, ui, repo, *args, **kwargs):
3534 """Add --mq option to operate on patch repository instead of main"""
3533 """Add --mq option to operate on patch repository instead of main"""
3535
3534
3536 # some commands do not like getting unknown options
3535 # some commands do not like getting unknown options
3537 mq = kwargs.pop('mq', None)
3536 mq = kwargs.pop('mq', None)
3538
3537
3539 if not mq:
3538 if not mq:
3540 return orig(ui, repo, *args, **kwargs)
3539 return orig(ui, repo, *args, **kwargs)
3541
3540
3542 q = repo.mq
3541 q = repo.mq
3543 r = q.qrepo()
3542 r = q.qrepo()
3544 if not r:
3543 if not r:
3545 raise error.Abort(_('no queue repository'))
3544 raise error.Abort(_('no queue repository'))
3546 return orig(r.ui, r, *args, **kwargs)
3545 return orig(r.ui, r, *args, **kwargs)
3547
3546
3548 def summaryhook(ui, repo):
3547 def summaryhook(ui, repo):
3549 q = repo.mq
3548 q = repo.mq
3550 m = []
3549 m = []
3551 a, u = len(q.applied), len(q.unapplied(repo))
3550 a, u = len(q.applied), len(q.unapplied(repo))
3552 if a:
3551 if a:
3553 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3552 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3554 if u:
3553 if u:
3555 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3554 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3556 if m:
3555 if m:
3557 # i18n: column positioning for "hg summary"
3556 # i18n: column positioning for "hg summary"
3558 ui.write(_("mq: %s\n") % ', '.join(m))
3557 ui.write(_("mq: %s\n") % ', '.join(m))
3559 else:
3558 else:
3560 # i18n: column positioning for "hg summary"
3559 # i18n: column positioning for "hg summary"
3561 ui.note(_("mq: (empty queue)\n"))
3560 ui.note(_("mq: (empty queue)\n"))
3562
3561
3563 revsetpredicate = registrar.revsetpredicate()
3562 revsetpredicate = registrar.revsetpredicate()
3564
3563
3565 @revsetpredicate('mq()')
3564 @revsetpredicate('mq()')
3566 def revsetmq(repo, subset, x):
3565 def revsetmq(repo, subset, x):
3567 """Changesets managed by MQ.
3566 """Changesets managed by MQ.
3568 """
3567 """
3569 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3568 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3570 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3569 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3571 return revset.baseset([r for r in subset if r in applied])
3570 return revset.baseset([r for r in subset if r in applied])
3572
3571
3573 # tell hggettext to extract docstrings from these functions:
3572 # tell hggettext to extract docstrings from these functions:
3574 i18nfunctions = [revsetmq]
3573 i18nfunctions = [revsetmq]
3575
3574
3576 def extsetup(ui):
3575 def extsetup(ui):
3577 # Ensure mq wrappers are called first, regardless of extension load order by
3576 # Ensure mq wrappers are called first, regardless of extension load order by
3578 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3577 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3579 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3578 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3580
3579
3581 extensions.wrapcommand(commands.table, 'import', mqimport)
3580 extensions.wrapcommand(commands.table, 'import', mqimport)
3582 cmdutil.summaryhooks.add('mq', summaryhook)
3581 cmdutil.summaryhooks.add('mq', summaryhook)
3583
3582
3584 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3583 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3585 entry[1].extend(mqopt)
3584 entry[1].extend(mqopt)
3586
3585
3587 def dotable(cmdtable):
3586 def dotable(cmdtable):
3588 for cmd, entry in cmdtable.iteritems():
3587 for cmd, entry in cmdtable.iteritems():
3589 cmd = cmdutil.parsealiases(cmd)[0]
3588 cmd = cmdutil.parsealiases(cmd)[0]
3590 func = entry[0]
3589 func = entry[0]
3591 if dispatch._cmdattr(ui, cmd, func, 'norepo'):
3590 if func.norepo:
3592 continue
3591 continue
3593 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3592 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3594 entry[1].extend(mqopt)
3593 entry[1].extend(mqopt)
3595
3594
3596 dotable(commands.table)
3595 dotable(commands.table)
3597
3596
3598 for extname, extmodule in extensions.extensions():
3597 for extname, extmodule in extensions.extensions():
3599 if extmodule.__file__ != __file__:
3598 if extmodule.__file__ != __file__:
3600 dotable(getattr(extmodule, 'cmdtable', {}))
3599 dotable(getattr(extmodule, 'cmdtable', {}))
3601
3600
3602 colortable = {'qguard.negative': 'red',
3601 colortable = {'qguard.negative': 'red',
3603 'qguard.positive': 'yellow',
3602 'qguard.positive': 'yellow',
3604 'qguard.unguarded': 'green',
3603 'qguard.unguarded': 'green',
3605 'qseries.applied': 'blue bold underline',
3604 'qseries.applied': 'blue bold underline',
3606 'qseries.guarded': 'black bold',
3605 'qseries.guarded': 'black bold',
3607 'qseries.missing': 'red bold',
3606 'qseries.missing': 'red bold',
3608 'qseries.unapplied': 'black bold'}
3607 'qseries.unapplied': 'black bold'}
@@ -1,984 +1,975 b''
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import, print_function
8 from __future__ import absolute_import, print_function
9
9
10 import atexit
10 import atexit
11 import difflib
11 import difflib
12 import errno
12 import errno
13 import os
13 import os
14 import pdb
14 import pdb
15 import re
15 import re
16 import shlex
16 import shlex
17 import signal
17 import signal
18 import socket
18 import socket
19 import sys
19 import sys
20 import time
20 import time
21 import traceback
21 import traceback
22
22
23
23
24 from .i18n import _
24 from .i18n import _
25
25
26 from . import (
26 from . import (
27 cmdutil,
27 cmdutil,
28 commands,
28 commands,
29 debugcommands,
29 debugcommands,
30 demandimport,
30 demandimport,
31 encoding,
31 encoding,
32 error,
32 error,
33 extensions,
33 extensions,
34 fancyopts,
34 fancyopts,
35 fileset,
35 fileset,
36 hg,
36 hg,
37 hook,
37 hook,
38 profiling,
38 profiling,
39 pycompat,
39 pycompat,
40 revset,
40 revset,
41 templatefilters,
41 templatefilters,
42 templatekw,
42 templatekw,
43 templater,
43 templater,
44 ui as uimod,
44 ui as uimod,
45 util,
45 util,
46 )
46 )
47
47
48 class request(object):
48 class request(object):
49 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
49 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
50 ferr=None):
50 ferr=None):
51 self.args = args
51 self.args = args
52 self.ui = ui
52 self.ui = ui
53 self.repo = repo
53 self.repo = repo
54
54
55 # input/output/error streams
55 # input/output/error streams
56 self.fin = fin
56 self.fin = fin
57 self.fout = fout
57 self.fout = fout
58 self.ferr = ferr
58 self.ferr = ferr
59
59
60 def run():
60 def run():
61 "run the command in sys.argv"
61 "run the command in sys.argv"
62 sys.exit((dispatch(request(pycompat.sysargv[1:])) or 0) & 255)
62 sys.exit((dispatch(request(pycompat.sysargv[1:])) or 0) & 255)
63
63
64 def _getsimilar(symbols, value):
64 def _getsimilar(symbols, value):
65 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
65 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
66 # The cutoff for similarity here is pretty arbitrary. It should
66 # The cutoff for similarity here is pretty arbitrary. It should
67 # probably be investigated and tweaked.
67 # probably be investigated and tweaked.
68 return [s for s in symbols if sim(s) > 0.6]
68 return [s for s in symbols if sim(s) > 0.6]
69
69
70 def _reportsimilar(write, similar):
70 def _reportsimilar(write, similar):
71 if len(similar) == 1:
71 if len(similar) == 1:
72 write(_("(did you mean %s?)\n") % similar[0])
72 write(_("(did you mean %s?)\n") % similar[0])
73 elif similar:
73 elif similar:
74 ss = ", ".join(sorted(similar))
74 ss = ", ".join(sorted(similar))
75 write(_("(did you mean one of %s?)\n") % ss)
75 write(_("(did you mean one of %s?)\n") % ss)
76
76
77 def _formatparse(write, inst):
77 def _formatparse(write, inst):
78 similar = []
78 similar = []
79 if isinstance(inst, error.UnknownIdentifier):
79 if isinstance(inst, error.UnknownIdentifier):
80 # make sure to check fileset first, as revset can invoke fileset
80 # make sure to check fileset first, as revset can invoke fileset
81 similar = _getsimilar(inst.symbols, inst.function)
81 similar = _getsimilar(inst.symbols, inst.function)
82 if len(inst.args) > 1:
82 if len(inst.args) > 1:
83 write(_("hg: parse error at %s: %s\n") %
83 write(_("hg: parse error at %s: %s\n") %
84 (inst.args[1], inst.args[0]))
84 (inst.args[1], inst.args[0]))
85 if (inst.args[0][0] == ' '):
85 if (inst.args[0][0] == ' '):
86 write(_("unexpected leading whitespace\n"))
86 write(_("unexpected leading whitespace\n"))
87 else:
87 else:
88 write(_("hg: parse error: %s\n") % inst.args[0])
88 write(_("hg: parse error: %s\n") % inst.args[0])
89 _reportsimilar(write, similar)
89 _reportsimilar(write, similar)
90 if inst.hint:
90 if inst.hint:
91 write(_("(%s)\n") % inst.hint)
91 write(_("(%s)\n") % inst.hint)
92
92
93 def dispatch(req):
93 def dispatch(req):
94 "run the command specified in req.args"
94 "run the command specified in req.args"
95 if req.ferr:
95 if req.ferr:
96 ferr = req.ferr
96 ferr = req.ferr
97 elif req.ui:
97 elif req.ui:
98 ferr = req.ui.ferr
98 ferr = req.ui.ferr
99 else:
99 else:
100 ferr = util.stderr
100 ferr = util.stderr
101
101
102 try:
102 try:
103 if not req.ui:
103 if not req.ui:
104 req.ui = uimod.ui()
104 req.ui = uimod.ui()
105 if '--traceback' in req.args:
105 if '--traceback' in req.args:
106 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
106 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
107
107
108 # set ui streams from the request
108 # set ui streams from the request
109 if req.fin:
109 if req.fin:
110 req.ui.fin = req.fin
110 req.ui.fin = req.fin
111 if req.fout:
111 if req.fout:
112 req.ui.fout = req.fout
112 req.ui.fout = req.fout
113 if req.ferr:
113 if req.ferr:
114 req.ui.ferr = req.ferr
114 req.ui.ferr = req.ferr
115 except error.Abort as inst:
115 except error.Abort as inst:
116 ferr.write(_("abort: %s\n") % inst)
116 ferr.write(_("abort: %s\n") % inst)
117 if inst.hint:
117 if inst.hint:
118 ferr.write(_("(%s)\n") % inst.hint)
118 ferr.write(_("(%s)\n") % inst.hint)
119 return -1
119 return -1
120 except error.ParseError as inst:
120 except error.ParseError as inst:
121 _formatparse(ferr.write, inst)
121 _formatparse(ferr.write, inst)
122 return -1
122 return -1
123
123
124 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
124 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
125 starttime = time.time()
125 starttime = time.time()
126 ret = None
126 ret = None
127 try:
127 try:
128 ret = _runcatch(req)
128 ret = _runcatch(req)
129 except KeyboardInterrupt:
129 except KeyboardInterrupt:
130 try:
130 try:
131 req.ui.warn(_("interrupted!\n"))
131 req.ui.warn(_("interrupted!\n"))
132 except IOError as inst:
132 except IOError as inst:
133 if inst.errno != errno.EPIPE:
133 if inst.errno != errno.EPIPE:
134 raise
134 raise
135 ret = -1
135 ret = -1
136 finally:
136 finally:
137 duration = time.time() - starttime
137 duration = time.time() - starttime
138 req.ui.flush()
138 req.ui.flush()
139 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
139 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
140 msg, ret or 0, duration)
140 msg, ret or 0, duration)
141 return ret
141 return ret
142
142
143 def _runcatch(req):
143 def _runcatch(req):
144 def catchterm(*args):
144 def catchterm(*args):
145 raise error.SignalInterrupt
145 raise error.SignalInterrupt
146
146
147 ui = req.ui
147 ui = req.ui
148 try:
148 try:
149 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
149 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
150 num = getattr(signal, name, None)
150 num = getattr(signal, name, None)
151 if num:
151 if num:
152 signal.signal(num, catchterm)
152 signal.signal(num, catchterm)
153 except ValueError:
153 except ValueError:
154 pass # happens if called in a thread
154 pass # happens if called in a thread
155
155
156 def _runcatchfunc():
156 def _runcatchfunc():
157 try:
157 try:
158 debugger = 'pdb'
158 debugger = 'pdb'
159 debugtrace = {
159 debugtrace = {
160 'pdb' : pdb.set_trace
160 'pdb' : pdb.set_trace
161 }
161 }
162 debugmortem = {
162 debugmortem = {
163 'pdb' : pdb.post_mortem
163 'pdb' : pdb.post_mortem
164 }
164 }
165
165
166 # read --config before doing anything else
166 # read --config before doing anything else
167 # (e.g. to change trust settings for reading .hg/hgrc)
167 # (e.g. to change trust settings for reading .hg/hgrc)
168 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
168 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
169
169
170 if req.repo:
170 if req.repo:
171 # copy configs that were passed on the cmdline (--config) to
171 # copy configs that were passed on the cmdline (--config) to
172 # the repo ui
172 # the repo ui
173 for sec, name, val in cfgs:
173 for sec, name, val in cfgs:
174 req.repo.ui.setconfig(sec, name, val, source='--config')
174 req.repo.ui.setconfig(sec, name, val, source='--config')
175
175
176 # developer config: ui.debugger
176 # developer config: ui.debugger
177 debugger = ui.config("ui", "debugger")
177 debugger = ui.config("ui", "debugger")
178 debugmod = pdb
178 debugmod = pdb
179 if not debugger or ui.plain():
179 if not debugger or ui.plain():
180 # if we are in HGPLAIN mode, then disable custom debugging
180 # if we are in HGPLAIN mode, then disable custom debugging
181 debugger = 'pdb'
181 debugger = 'pdb'
182 elif '--debugger' in req.args:
182 elif '--debugger' in req.args:
183 # This import can be slow for fancy debuggers, so only
183 # This import can be slow for fancy debuggers, so only
184 # do it when absolutely necessary, i.e. when actual
184 # do it when absolutely necessary, i.e. when actual
185 # debugging has been requested
185 # debugging has been requested
186 with demandimport.deactivated():
186 with demandimport.deactivated():
187 try:
187 try:
188 debugmod = __import__(debugger)
188 debugmod = __import__(debugger)
189 except ImportError:
189 except ImportError:
190 pass # Leave debugmod = pdb
190 pass # Leave debugmod = pdb
191
191
192 debugtrace[debugger] = debugmod.set_trace
192 debugtrace[debugger] = debugmod.set_trace
193 debugmortem[debugger] = debugmod.post_mortem
193 debugmortem[debugger] = debugmod.post_mortem
194
194
195 # enter the debugger before command execution
195 # enter the debugger before command execution
196 if '--debugger' in req.args:
196 if '--debugger' in req.args:
197 ui.warn(_("entering debugger - "
197 ui.warn(_("entering debugger - "
198 "type c to continue starting hg or h for help\n"))
198 "type c to continue starting hg or h for help\n"))
199
199
200 if (debugger != 'pdb' and
200 if (debugger != 'pdb' and
201 debugtrace[debugger] == debugtrace['pdb']):
201 debugtrace[debugger] == debugtrace['pdb']):
202 ui.warn(_("%s debugger specified "
202 ui.warn(_("%s debugger specified "
203 "but its module was not found\n") % debugger)
203 "but its module was not found\n") % debugger)
204 with demandimport.deactivated():
204 with demandimport.deactivated():
205 debugtrace[debugger]()
205 debugtrace[debugger]()
206 try:
206 try:
207 return _dispatch(req)
207 return _dispatch(req)
208 finally:
208 finally:
209 ui.flush()
209 ui.flush()
210 except: # re-raises
210 except: # re-raises
211 # enter the debugger when we hit an exception
211 # enter the debugger when we hit an exception
212 if '--debugger' in req.args:
212 if '--debugger' in req.args:
213 traceback.print_exc()
213 traceback.print_exc()
214 debugmortem[debugger](sys.exc_info()[2])
214 debugmortem[debugger](sys.exc_info()[2])
215 ui.traceback()
215 ui.traceback()
216 raise
216 raise
217
217
218 return callcatch(ui, _runcatchfunc)
218 return callcatch(ui, _runcatchfunc)
219
219
220 def callcatch(ui, func):
220 def callcatch(ui, func):
221 """call func() with global exception handling
221 """call func() with global exception handling
222
222
223 return func() if no exception happens. otherwise do some error handling
223 return func() if no exception happens. otherwise do some error handling
224 and return an exit code accordingly.
224 and return an exit code accordingly.
225 """
225 """
226 try:
226 try:
227 return func()
227 return func()
228 # Global exception handling, alphabetically
228 # Global exception handling, alphabetically
229 # Mercurial-specific first, followed by built-in and library exceptions
229 # Mercurial-specific first, followed by built-in and library exceptions
230 except error.AmbiguousCommand as inst:
230 except error.AmbiguousCommand as inst:
231 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
231 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
232 (inst.args[0], " ".join(inst.args[1])))
232 (inst.args[0], " ".join(inst.args[1])))
233 except error.ParseError as inst:
233 except error.ParseError as inst:
234 _formatparse(ui.warn, inst)
234 _formatparse(ui.warn, inst)
235 return -1
235 return -1
236 except error.LockHeld as inst:
236 except error.LockHeld as inst:
237 if inst.errno == errno.ETIMEDOUT:
237 if inst.errno == errno.ETIMEDOUT:
238 reason = _('timed out waiting for lock held by %s') % inst.locker
238 reason = _('timed out waiting for lock held by %s') % inst.locker
239 else:
239 else:
240 reason = _('lock held by %s') % inst.locker
240 reason = _('lock held by %s') % inst.locker
241 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
241 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
242 except error.LockUnavailable as inst:
242 except error.LockUnavailable as inst:
243 ui.warn(_("abort: could not lock %s: %s\n") %
243 ui.warn(_("abort: could not lock %s: %s\n") %
244 (inst.desc or inst.filename, inst.strerror))
244 (inst.desc or inst.filename, inst.strerror))
245 except error.CommandError as inst:
245 except error.CommandError as inst:
246 if inst.args[0]:
246 if inst.args[0]:
247 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
247 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
248 commands.help_(ui, inst.args[0], full=False, command=True)
248 commands.help_(ui, inst.args[0], full=False, command=True)
249 else:
249 else:
250 ui.warn(_("hg: %s\n") % inst.args[1])
250 ui.warn(_("hg: %s\n") % inst.args[1])
251 commands.help_(ui, 'shortlist')
251 commands.help_(ui, 'shortlist')
252 except error.OutOfBandError as inst:
252 except error.OutOfBandError as inst:
253 if inst.args:
253 if inst.args:
254 msg = _("abort: remote error:\n")
254 msg = _("abort: remote error:\n")
255 else:
255 else:
256 msg = _("abort: remote error\n")
256 msg = _("abort: remote error\n")
257 ui.warn(msg)
257 ui.warn(msg)
258 if inst.args:
258 if inst.args:
259 ui.warn(''.join(inst.args))
259 ui.warn(''.join(inst.args))
260 if inst.hint:
260 if inst.hint:
261 ui.warn('(%s)\n' % inst.hint)
261 ui.warn('(%s)\n' % inst.hint)
262 except error.RepoError as inst:
262 except error.RepoError as inst:
263 ui.warn(_("abort: %s!\n") % inst)
263 ui.warn(_("abort: %s!\n") % inst)
264 if inst.hint:
264 if inst.hint:
265 ui.warn(_("(%s)\n") % inst.hint)
265 ui.warn(_("(%s)\n") % inst.hint)
266 except error.ResponseError as inst:
266 except error.ResponseError as inst:
267 ui.warn(_("abort: %s") % inst.args[0])
267 ui.warn(_("abort: %s") % inst.args[0])
268 if not isinstance(inst.args[1], basestring):
268 if not isinstance(inst.args[1], basestring):
269 ui.warn(" %r\n" % (inst.args[1],))
269 ui.warn(" %r\n" % (inst.args[1],))
270 elif not inst.args[1]:
270 elif not inst.args[1]:
271 ui.warn(_(" empty string\n"))
271 ui.warn(_(" empty string\n"))
272 else:
272 else:
273 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
273 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
274 except error.CensoredNodeError as inst:
274 except error.CensoredNodeError as inst:
275 ui.warn(_("abort: file censored %s!\n") % inst)
275 ui.warn(_("abort: file censored %s!\n") % inst)
276 except error.RevlogError as inst:
276 except error.RevlogError as inst:
277 ui.warn(_("abort: %s!\n") % inst)
277 ui.warn(_("abort: %s!\n") % inst)
278 except error.SignalInterrupt:
278 except error.SignalInterrupt:
279 ui.warn(_("killed!\n"))
279 ui.warn(_("killed!\n"))
280 except error.UnknownCommand as inst:
280 except error.UnknownCommand as inst:
281 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
281 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
282 try:
282 try:
283 # check if the command is in a disabled extension
283 # check if the command is in a disabled extension
284 # (but don't check for extensions themselves)
284 # (but don't check for extensions themselves)
285 commands.help_(ui, inst.args[0], unknowncmd=True)
285 commands.help_(ui, inst.args[0], unknowncmd=True)
286 except (error.UnknownCommand, error.Abort):
286 except (error.UnknownCommand, error.Abort):
287 suggested = False
287 suggested = False
288 if len(inst.args) == 2:
288 if len(inst.args) == 2:
289 sim = _getsimilar(inst.args[1], inst.args[0])
289 sim = _getsimilar(inst.args[1], inst.args[0])
290 if sim:
290 if sim:
291 _reportsimilar(ui.warn, sim)
291 _reportsimilar(ui.warn, sim)
292 suggested = True
292 suggested = True
293 if not suggested:
293 if not suggested:
294 commands.help_(ui, 'shortlist')
294 commands.help_(ui, 'shortlist')
295 except error.InterventionRequired as inst:
295 except error.InterventionRequired as inst:
296 ui.warn("%s\n" % inst)
296 ui.warn("%s\n" % inst)
297 if inst.hint:
297 if inst.hint:
298 ui.warn(_("(%s)\n") % inst.hint)
298 ui.warn(_("(%s)\n") % inst.hint)
299 return 1
299 return 1
300 except error.Abort as inst:
300 except error.Abort as inst:
301 ui.warn(_("abort: %s\n") % inst)
301 ui.warn(_("abort: %s\n") % inst)
302 if inst.hint:
302 if inst.hint:
303 ui.warn(_("(%s)\n") % inst.hint)
303 ui.warn(_("(%s)\n") % inst.hint)
304 except ImportError as inst:
304 except ImportError as inst:
305 ui.warn(_("abort: %s!\n") % inst)
305 ui.warn(_("abort: %s!\n") % inst)
306 m = str(inst).split()[-1]
306 m = str(inst).split()[-1]
307 if m in "mpatch bdiff".split():
307 if m in "mpatch bdiff".split():
308 ui.warn(_("(did you forget to compile extensions?)\n"))
308 ui.warn(_("(did you forget to compile extensions?)\n"))
309 elif m in "zlib".split():
309 elif m in "zlib".split():
310 ui.warn(_("(is your Python install correct?)\n"))
310 ui.warn(_("(is your Python install correct?)\n"))
311 except IOError as inst:
311 except IOError as inst:
312 if util.safehasattr(inst, "code"):
312 if util.safehasattr(inst, "code"):
313 ui.warn(_("abort: %s\n") % inst)
313 ui.warn(_("abort: %s\n") % inst)
314 elif util.safehasattr(inst, "reason"):
314 elif util.safehasattr(inst, "reason"):
315 try: # usually it is in the form (errno, strerror)
315 try: # usually it is in the form (errno, strerror)
316 reason = inst.reason.args[1]
316 reason = inst.reason.args[1]
317 except (AttributeError, IndexError):
317 except (AttributeError, IndexError):
318 # it might be anything, for example a string
318 # it might be anything, for example a string
319 reason = inst.reason
319 reason = inst.reason
320 if isinstance(reason, unicode):
320 if isinstance(reason, unicode):
321 # SSLError of Python 2.7.9 contains a unicode
321 # SSLError of Python 2.7.9 contains a unicode
322 reason = reason.encode(encoding.encoding, 'replace')
322 reason = reason.encode(encoding.encoding, 'replace')
323 ui.warn(_("abort: error: %s\n") % reason)
323 ui.warn(_("abort: error: %s\n") % reason)
324 elif (util.safehasattr(inst, "args")
324 elif (util.safehasattr(inst, "args")
325 and inst.args and inst.args[0] == errno.EPIPE):
325 and inst.args and inst.args[0] == errno.EPIPE):
326 pass
326 pass
327 elif getattr(inst, "strerror", None):
327 elif getattr(inst, "strerror", None):
328 if getattr(inst, "filename", None):
328 if getattr(inst, "filename", None):
329 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
329 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
330 else:
330 else:
331 ui.warn(_("abort: %s\n") % inst.strerror)
331 ui.warn(_("abort: %s\n") % inst.strerror)
332 else:
332 else:
333 raise
333 raise
334 except OSError as inst:
334 except OSError as inst:
335 if getattr(inst, "filename", None) is not None:
335 if getattr(inst, "filename", None) is not None:
336 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
336 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
337 else:
337 else:
338 ui.warn(_("abort: %s\n") % inst.strerror)
338 ui.warn(_("abort: %s\n") % inst.strerror)
339 except KeyboardInterrupt:
339 except KeyboardInterrupt:
340 raise
340 raise
341 except MemoryError:
341 except MemoryError:
342 ui.warn(_("abort: out of memory\n"))
342 ui.warn(_("abort: out of memory\n"))
343 except SystemExit as inst:
343 except SystemExit as inst:
344 # Commands shouldn't sys.exit directly, but give a return code.
344 # Commands shouldn't sys.exit directly, but give a return code.
345 # Just in case catch this and and pass exit code to caller.
345 # Just in case catch this and and pass exit code to caller.
346 return inst.code
346 return inst.code
347 except socket.error as inst:
347 except socket.error as inst:
348 ui.warn(_("abort: %s\n") % inst.args[-1])
348 ui.warn(_("abort: %s\n") % inst.args[-1])
349 except: # perhaps re-raises
349 except: # perhaps re-raises
350 if not handlecommandexception(ui):
350 if not handlecommandexception(ui):
351 raise
351 raise
352
352
353 return -1
353 return -1
354
354
355 def aliasargs(fn, givenargs):
355 def aliasargs(fn, givenargs):
356 args = getattr(fn, 'args', [])
356 args = getattr(fn, 'args', [])
357 if args:
357 if args:
358 cmd = ' '.join(map(util.shellquote, args))
358 cmd = ' '.join(map(util.shellquote, args))
359
359
360 nums = []
360 nums = []
361 def replacer(m):
361 def replacer(m):
362 num = int(m.group(1)) - 1
362 num = int(m.group(1)) - 1
363 nums.append(num)
363 nums.append(num)
364 if num < len(givenargs):
364 if num < len(givenargs):
365 return givenargs[num]
365 return givenargs[num]
366 raise error.Abort(_('too few arguments for command alias'))
366 raise error.Abort(_('too few arguments for command alias'))
367 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
367 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
368 givenargs = [x for i, x in enumerate(givenargs)
368 givenargs = [x for i, x in enumerate(givenargs)
369 if i not in nums]
369 if i not in nums]
370 args = shlex.split(cmd)
370 args = shlex.split(cmd)
371 return args + givenargs
371 return args + givenargs
372
372
373 def aliasinterpolate(name, args, cmd):
373 def aliasinterpolate(name, args, cmd):
374 '''interpolate args into cmd for shell aliases
374 '''interpolate args into cmd for shell aliases
375
375
376 This also handles $0, $@ and "$@".
376 This also handles $0, $@ and "$@".
377 '''
377 '''
378 # util.interpolate can't deal with "$@" (with quotes) because it's only
378 # util.interpolate can't deal with "$@" (with quotes) because it's only
379 # built to match prefix + patterns.
379 # built to match prefix + patterns.
380 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
380 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
381 replacemap['$0'] = name
381 replacemap['$0'] = name
382 replacemap['$$'] = '$'
382 replacemap['$$'] = '$'
383 replacemap['$@'] = ' '.join(args)
383 replacemap['$@'] = ' '.join(args)
384 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
384 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
385 # parameters, separated out into words. Emulate the same behavior here by
385 # parameters, separated out into words. Emulate the same behavior here by
386 # quoting the arguments individually. POSIX shells will then typically
386 # quoting the arguments individually. POSIX shells will then typically
387 # tokenize each argument into exactly one word.
387 # tokenize each argument into exactly one word.
388 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
388 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
389 # escape '\$' for regex
389 # escape '\$' for regex
390 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
390 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
391 r = re.compile(regex)
391 r = re.compile(regex)
392 return r.sub(lambda x: replacemap[x.group()], cmd)
392 return r.sub(lambda x: replacemap[x.group()], cmd)
393
393
394 class cmdalias(object):
394 class cmdalias(object):
395 def __init__(self, name, definition, cmdtable, source):
395 def __init__(self, name, definition, cmdtable, source):
396 self.name = self.cmd = name
396 self.name = self.cmd = name
397 self.cmdname = ''
397 self.cmdname = ''
398 self.definition = definition
398 self.definition = definition
399 self.fn = None
399 self.fn = None
400 self.givenargs = []
400 self.givenargs = []
401 self.opts = []
401 self.opts = []
402 self.help = ''
402 self.help = ''
403 self.badalias = None
403 self.badalias = None
404 self.unknowncmd = False
404 self.unknowncmd = False
405 self.source = source
405 self.source = source
406
406
407 try:
407 try:
408 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
408 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
409 for alias, e in cmdtable.iteritems():
409 for alias, e in cmdtable.iteritems():
410 if e is entry:
410 if e is entry:
411 self.cmd = alias
411 self.cmd = alias
412 break
412 break
413 self.shadows = True
413 self.shadows = True
414 except error.UnknownCommand:
414 except error.UnknownCommand:
415 self.shadows = False
415 self.shadows = False
416
416
417 if not self.definition:
417 if not self.definition:
418 self.badalias = _("no definition for alias '%s'") % self.name
418 self.badalias = _("no definition for alias '%s'") % self.name
419 return
419 return
420
420
421 if self.definition.startswith('!'):
421 if self.definition.startswith('!'):
422 self.shell = True
422 self.shell = True
423 def fn(ui, *args):
423 def fn(ui, *args):
424 env = {'HG_ARGS': ' '.join((self.name,) + args)}
424 env = {'HG_ARGS': ' '.join((self.name,) + args)}
425 def _checkvar(m):
425 def _checkvar(m):
426 if m.groups()[0] == '$':
426 if m.groups()[0] == '$':
427 return m.group()
427 return m.group()
428 elif int(m.groups()[0]) <= len(args):
428 elif int(m.groups()[0]) <= len(args):
429 return m.group()
429 return m.group()
430 else:
430 else:
431 ui.debug("No argument found for substitution "
431 ui.debug("No argument found for substitution "
432 "of %i variable in alias '%s' definition."
432 "of %i variable in alias '%s' definition."
433 % (int(m.groups()[0]), self.name))
433 % (int(m.groups()[0]), self.name))
434 return ''
434 return ''
435 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
435 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
436 cmd = aliasinterpolate(self.name, args, cmd)
436 cmd = aliasinterpolate(self.name, args, cmd)
437 return ui.system(cmd, environ=env)
437 return ui.system(cmd, environ=env)
438 self.fn = fn
438 self.fn = fn
439 return
439 return
440
440
441 try:
441 try:
442 args = shlex.split(self.definition)
442 args = shlex.split(self.definition)
443 except ValueError as inst:
443 except ValueError as inst:
444 self.badalias = (_("error in definition for alias '%s': %s")
444 self.badalias = (_("error in definition for alias '%s': %s")
445 % (self.name, inst))
445 % (self.name, inst))
446 return
446 return
447 self.cmdname = cmd = args.pop(0)
447 self.cmdname = cmd = args.pop(0)
448 self.givenargs = args
448 self.givenargs = args
449
449
450 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
450 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
451 if _earlygetopt([invalidarg], args):
451 if _earlygetopt([invalidarg], args):
452 self.badalias = (_("error in definition for alias '%s': %s may "
452 self.badalias = (_("error in definition for alias '%s': %s may "
453 "only be given on the command line")
453 "only be given on the command line")
454 % (self.name, invalidarg))
454 % (self.name, invalidarg))
455 return
455 return
456
456
457 try:
457 try:
458 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
458 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
459 if len(tableentry) > 2:
459 if len(tableentry) > 2:
460 self.fn, self.opts, self.help = tableentry
460 self.fn, self.opts, self.help = tableentry
461 else:
461 else:
462 self.fn, self.opts = tableentry
462 self.fn, self.opts = tableentry
463
463
464 if self.help.startswith("hg " + cmd):
464 if self.help.startswith("hg " + cmd):
465 # drop prefix in old-style help lines so hg shows the alias
465 # drop prefix in old-style help lines so hg shows the alias
466 self.help = self.help[4 + len(cmd):]
466 self.help = self.help[4 + len(cmd):]
467 self.__doc__ = self.fn.__doc__
467 self.__doc__ = self.fn.__doc__
468
468
469 except error.UnknownCommand:
469 except error.UnknownCommand:
470 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
470 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
471 % (self.name, cmd))
471 % (self.name, cmd))
472 self.unknowncmd = True
472 self.unknowncmd = True
473 except error.AmbiguousCommand:
473 except error.AmbiguousCommand:
474 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
474 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
475 % (self.name, cmd))
475 % (self.name, cmd))
476
476
477 @property
477 @property
478 def args(self):
478 def args(self):
479 args = map(util.expandpath, self.givenargs)
479 args = map(util.expandpath, self.givenargs)
480 return aliasargs(self.fn, args)
480 return aliasargs(self.fn, args)
481
481
482 def __getattr__(self, name):
482 def __getattr__(self, name):
483 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
483 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
484 if name not in adefaults:
484 if name not in adefaults:
485 raise AttributeError(name)
485 raise AttributeError(name)
486 if self.badalias or util.safehasattr(self, 'shell'):
486 if self.badalias or util.safehasattr(self, 'shell'):
487 return adefaults[name]
487 return adefaults[name]
488 return getattr(self.fn, name)
488 return getattr(self.fn, name)
489
489
490 def __call__(self, ui, *args, **opts):
490 def __call__(self, ui, *args, **opts):
491 if self.badalias:
491 if self.badalias:
492 hint = None
492 hint = None
493 if self.unknowncmd:
493 if self.unknowncmd:
494 try:
494 try:
495 # check if the command is in a disabled extension
495 # check if the command is in a disabled extension
496 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
496 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
497 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
497 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
498 except error.UnknownCommand:
498 except error.UnknownCommand:
499 pass
499 pass
500 raise error.Abort(self.badalias, hint=hint)
500 raise error.Abort(self.badalias, hint=hint)
501 if self.shadows:
501 if self.shadows:
502 ui.debug("alias '%s' shadows command '%s'\n" %
502 ui.debug("alias '%s' shadows command '%s'\n" %
503 (self.name, self.cmdname))
503 (self.name, self.cmdname))
504
504
505 ui.log('commandalias', "alias '%s' expands to '%s'\n",
505 ui.log('commandalias', "alias '%s' expands to '%s'\n",
506 self.name, self.definition)
506 self.name, self.definition)
507 if util.safehasattr(self, 'shell'):
507 if util.safehasattr(self, 'shell'):
508 return self.fn(ui, *args, **opts)
508 return self.fn(ui, *args, **opts)
509 else:
509 else:
510 try:
510 try:
511 return util.checksignature(self.fn)(ui, *args, **opts)
511 return util.checksignature(self.fn)(ui, *args, **opts)
512 except error.SignatureError:
512 except error.SignatureError:
513 args = ' '.join([self.cmdname] + self.args)
513 args = ' '.join([self.cmdname] + self.args)
514 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
514 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
515 raise
515 raise
516
516
517 def addaliases(ui, cmdtable):
517 def addaliases(ui, cmdtable):
518 # aliases are processed after extensions have been loaded, so they
518 # aliases are processed after extensions have been loaded, so they
519 # may use extension commands. Aliases can also use other alias definitions,
519 # may use extension commands. Aliases can also use other alias definitions,
520 # but only if they have been defined prior to the current definition.
520 # but only if they have been defined prior to the current definition.
521 for alias, definition in ui.configitems('alias'):
521 for alias, definition in ui.configitems('alias'):
522 source = ui.configsource('alias', alias)
522 source = ui.configsource('alias', alias)
523 aliasdef = cmdalias(alias, definition, cmdtable, source)
523 aliasdef = cmdalias(alias, definition, cmdtable, source)
524
524
525 try:
525 try:
526 olddef = cmdtable[aliasdef.cmd][0]
526 olddef = cmdtable[aliasdef.cmd][0]
527 if olddef.definition == aliasdef.definition:
527 if olddef.definition == aliasdef.definition:
528 continue
528 continue
529 except (KeyError, AttributeError):
529 except (KeyError, AttributeError):
530 # definition might not exist or it might not be a cmdalias
530 # definition might not exist or it might not be a cmdalias
531 pass
531 pass
532
532
533 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
533 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
534
534
535 def _parse(ui, args):
535 def _parse(ui, args):
536 options = {}
536 options = {}
537 cmdoptions = {}
537 cmdoptions = {}
538
538
539 try:
539 try:
540 args = fancyopts.fancyopts(args, commands.globalopts, options)
540 args = fancyopts.fancyopts(args, commands.globalopts, options)
541 except fancyopts.getopt.GetoptError as inst:
541 except fancyopts.getopt.GetoptError as inst:
542 raise error.CommandError(None, inst)
542 raise error.CommandError(None, inst)
543
543
544 if args:
544 if args:
545 cmd, args = args[0], args[1:]
545 cmd, args = args[0], args[1:]
546 aliases, entry = cmdutil.findcmd(cmd, commands.table,
546 aliases, entry = cmdutil.findcmd(cmd, commands.table,
547 ui.configbool("ui", "strict"))
547 ui.configbool("ui", "strict"))
548 cmd = aliases[0]
548 cmd = aliases[0]
549 args = aliasargs(entry[0], args)
549 args = aliasargs(entry[0], args)
550 defaults = ui.config("defaults", cmd)
550 defaults = ui.config("defaults", cmd)
551 if defaults:
551 if defaults:
552 args = map(util.expandpath, shlex.split(defaults)) + args
552 args = map(util.expandpath, shlex.split(defaults)) + args
553 c = list(entry[1])
553 c = list(entry[1])
554 else:
554 else:
555 cmd = None
555 cmd = None
556 c = []
556 c = []
557
557
558 # combine global options into local
558 # combine global options into local
559 for o in commands.globalopts:
559 for o in commands.globalopts:
560 c.append((o[0], o[1], options[o[1]], o[3]))
560 c.append((o[0], o[1], options[o[1]], o[3]))
561
561
562 try:
562 try:
563 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
563 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
564 except fancyopts.getopt.GetoptError as inst:
564 except fancyopts.getopt.GetoptError as inst:
565 raise error.CommandError(cmd, inst)
565 raise error.CommandError(cmd, inst)
566
566
567 # separate global options back out
567 # separate global options back out
568 for o in commands.globalopts:
568 for o in commands.globalopts:
569 n = o[1]
569 n = o[1]
570 options[n] = cmdoptions[n]
570 options[n] = cmdoptions[n]
571 del cmdoptions[n]
571 del cmdoptions[n]
572
572
573 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
573 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
574
574
575 def _parseconfig(ui, config):
575 def _parseconfig(ui, config):
576 """parse the --config options from the command line"""
576 """parse the --config options from the command line"""
577 configs = []
577 configs = []
578
578
579 for cfg in config:
579 for cfg in config:
580 try:
580 try:
581 name, value = [cfgelem.strip()
581 name, value = [cfgelem.strip()
582 for cfgelem in cfg.split('=', 1)]
582 for cfgelem in cfg.split('=', 1)]
583 section, name = name.split('.', 1)
583 section, name = name.split('.', 1)
584 if not section or not name:
584 if not section or not name:
585 raise IndexError
585 raise IndexError
586 ui.setconfig(section, name, value, '--config')
586 ui.setconfig(section, name, value, '--config')
587 configs.append((section, name, value))
587 configs.append((section, name, value))
588 except (IndexError, ValueError):
588 except (IndexError, ValueError):
589 raise error.Abort(_('malformed --config option: %r '
589 raise error.Abort(_('malformed --config option: %r '
590 '(use --config section.name=value)') % cfg)
590 '(use --config section.name=value)') % cfg)
591
591
592 return configs
592 return configs
593
593
594 def _earlygetopt(aliases, args):
594 def _earlygetopt(aliases, args):
595 """Return list of values for an option (or aliases).
595 """Return list of values for an option (or aliases).
596
596
597 The values are listed in the order they appear in args.
597 The values are listed in the order they appear in args.
598 The options and values are removed from args.
598 The options and values are removed from args.
599
599
600 >>> args = ['x', '--cwd', 'foo', 'y']
600 >>> args = ['x', '--cwd', 'foo', 'y']
601 >>> _earlygetopt(['--cwd'], args), args
601 >>> _earlygetopt(['--cwd'], args), args
602 (['foo'], ['x', 'y'])
602 (['foo'], ['x', 'y'])
603
603
604 >>> args = ['x', '--cwd=bar', 'y']
604 >>> args = ['x', '--cwd=bar', 'y']
605 >>> _earlygetopt(['--cwd'], args), args
605 >>> _earlygetopt(['--cwd'], args), args
606 (['bar'], ['x', 'y'])
606 (['bar'], ['x', 'y'])
607
607
608 >>> args = ['x', '-R', 'foo', 'y']
608 >>> args = ['x', '-R', 'foo', 'y']
609 >>> _earlygetopt(['-R'], args), args
609 >>> _earlygetopt(['-R'], args), args
610 (['foo'], ['x', 'y'])
610 (['foo'], ['x', 'y'])
611
611
612 >>> args = ['x', '-Rbar', 'y']
612 >>> args = ['x', '-Rbar', 'y']
613 >>> _earlygetopt(['-R'], args), args
613 >>> _earlygetopt(['-R'], args), args
614 (['bar'], ['x', 'y'])
614 (['bar'], ['x', 'y'])
615 """
615 """
616 try:
616 try:
617 argcount = args.index("--")
617 argcount = args.index("--")
618 except ValueError:
618 except ValueError:
619 argcount = len(args)
619 argcount = len(args)
620 shortopts = [opt for opt in aliases if len(opt) == 2]
620 shortopts = [opt for opt in aliases if len(opt) == 2]
621 values = []
621 values = []
622 pos = 0
622 pos = 0
623 while pos < argcount:
623 while pos < argcount:
624 fullarg = arg = args[pos]
624 fullarg = arg = args[pos]
625 equals = arg.find('=')
625 equals = arg.find('=')
626 if equals > -1:
626 if equals > -1:
627 arg = arg[:equals]
627 arg = arg[:equals]
628 if arg in aliases:
628 if arg in aliases:
629 del args[pos]
629 del args[pos]
630 if equals > -1:
630 if equals > -1:
631 values.append(fullarg[equals + 1:])
631 values.append(fullarg[equals + 1:])
632 argcount -= 1
632 argcount -= 1
633 else:
633 else:
634 if pos + 1 >= argcount:
634 if pos + 1 >= argcount:
635 # ignore and let getopt report an error if there is no value
635 # ignore and let getopt report an error if there is no value
636 break
636 break
637 values.append(args.pop(pos))
637 values.append(args.pop(pos))
638 argcount -= 2
638 argcount -= 2
639 elif arg[:2] in shortopts:
639 elif arg[:2] in shortopts:
640 # short option can have no following space, e.g. hg log -Rfoo
640 # short option can have no following space, e.g. hg log -Rfoo
641 values.append(args.pop(pos)[2:])
641 values.append(args.pop(pos)[2:])
642 argcount -= 1
642 argcount -= 1
643 else:
643 else:
644 pos += 1
644 pos += 1
645 return values
645 return values
646
646
647 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
647 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
648 # run pre-hook, and abort if it fails
648 # run pre-hook, and abort if it fails
649 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
649 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
650 pats=cmdpats, opts=cmdoptions)
650 pats=cmdpats, opts=cmdoptions)
651 try:
651 try:
652 ret = _runcommand(ui, options, cmd, d)
652 ret = _runcommand(ui, options, cmd, d)
653 # run post-hook, passing command result
653 # run post-hook, passing command result
654 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
654 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
655 result=ret, pats=cmdpats, opts=cmdoptions)
655 result=ret, pats=cmdpats, opts=cmdoptions)
656 except Exception:
656 except Exception:
657 # run failure hook and re-raise
657 # run failure hook and re-raise
658 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
658 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
659 pats=cmdpats, opts=cmdoptions)
659 pats=cmdpats, opts=cmdoptions)
660 raise
660 raise
661 return ret
661 return ret
662
662
663 def _getlocal(ui, rpath, wd=None):
663 def _getlocal(ui, rpath, wd=None):
664 """Return (path, local ui object) for the given target path.
664 """Return (path, local ui object) for the given target path.
665
665
666 Takes paths in [cwd]/.hg/hgrc into account."
666 Takes paths in [cwd]/.hg/hgrc into account."
667 """
667 """
668 if wd is None:
668 if wd is None:
669 try:
669 try:
670 wd = os.getcwd()
670 wd = os.getcwd()
671 except OSError as e:
671 except OSError as e:
672 raise error.Abort(_("error getting current working directory: %s") %
672 raise error.Abort(_("error getting current working directory: %s") %
673 e.strerror)
673 e.strerror)
674 path = cmdutil.findrepo(wd) or ""
674 path = cmdutil.findrepo(wd) or ""
675 if not path:
675 if not path:
676 lui = ui
676 lui = ui
677 else:
677 else:
678 lui = ui.copy()
678 lui = ui.copy()
679 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
679 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
680
680
681 if rpath and rpath[-1]:
681 if rpath and rpath[-1]:
682 path = lui.expandpath(rpath[-1])
682 path = lui.expandpath(rpath[-1])
683 lui = ui.copy()
683 lui = ui.copy()
684 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
684 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
685
685
686 return path, lui
686 return path, lui
687
687
688 def _checkshellalias(lui, ui, args):
688 def _checkshellalias(lui, ui, args):
689 """Return the function to run the shell alias, if it is required"""
689 """Return the function to run the shell alias, if it is required"""
690 options = {}
690 options = {}
691
691
692 try:
692 try:
693 args = fancyopts.fancyopts(args, commands.globalopts, options)
693 args = fancyopts.fancyopts(args, commands.globalopts, options)
694 except fancyopts.getopt.GetoptError:
694 except fancyopts.getopt.GetoptError:
695 return
695 return
696
696
697 if not args:
697 if not args:
698 return
698 return
699
699
700 cmdtable = commands.table
700 cmdtable = commands.table
701
701
702 cmd = args[0]
702 cmd = args[0]
703 try:
703 try:
704 strict = ui.configbool("ui", "strict")
704 strict = ui.configbool("ui", "strict")
705 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
705 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
706 except (error.AmbiguousCommand, error.UnknownCommand):
706 except (error.AmbiguousCommand, error.UnknownCommand):
707 return
707 return
708
708
709 cmd = aliases[0]
709 cmd = aliases[0]
710 fn = entry[0]
710 fn = entry[0]
711
711
712 if cmd and util.safehasattr(fn, 'shell'):
712 if cmd and util.safehasattr(fn, 'shell'):
713 d = lambda: fn(ui, *args[1:])
713 d = lambda: fn(ui, *args[1:])
714 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
714 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
715 [], {})
715 [], {})
716
716
717 def _cmdattr(ui, cmd, func, attr):
718 try:
719 return getattr(func, attr)
720 except AttributeError:
721 ui.deprecwarn("missing attribute '%s', use @command decorator "
722 "to register '%s'" % (attr, cmd), '3.8')
723 return False
724
725 _loaded = set()
717 _loaded = set()
726
718
727 # list of (objname, loadermod, loadername) tuple:
719 # list of (objname, loadermod, loadername) tuple:
728 # - objname is the name of an object in extension module, from which
720 # - objname is the name of an object in extension module, from which
729 # extra information is loaded
721 # extra information is loaded
730 # - loadermod is the module where loader is placed
722 # - loadermod is the module where loader is placed
731 # - loadername is the name of the function, which takes (ui, extensionname,
723 # - loadername is the name of the function, which takes (ui, extensionname,
732 # extraobj) arguments
724 # extraobj) arguments
733 extraloaders = [
725 extraloaders = [
734 ('cmdtable', commands, 'loadcmdtable'),
726 ('cmdtable', commands, 'loadcmdtable'),
735 ('filesetpredicate', fileset, 'loadpredicate'),
727 ('filesetpredicate', fileset, 'loadpredicate'),
736 ('revsetpredicate', revset, 'loadpredicate'),
728 ('revsetpredicate', revset, 'loadpredicate'),
737 ('templatefilter', templatefilters, 'loadfilter'),
729 ('templatefilter', templatefilters, 'loadfilter'),
738 ('templatefunc', templater, 'loadfunction'),
730 ('templatefunc', templater, 'loadfunction'),
739 ('templatekeyword', templatekw, 'loadkeyword'),
731 ('templatekeyword', templatekw, 'loadkeyword'),
740 ]
732 ]
741
733
742 def _dispatch(req):
734 def _dispatch(req):
743 args = req.args
735 args = req.args
744 ui = req.ui
736 ui = req.ui
745
737
746 # check for cwd
738 # check for cwd
747 cwd = _earlygetopt(['--cwd'], args)
739 cwd = _earlygetopt(['--cwd'], args)
748 if cwd:
740 if cwd:
749 os.chdir(cwd[-1])
741 os.chdir(cwd[-1])
750
742
751 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
743 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
752 path, lui = _getlocal(ui, rpath)
744 path, lui = _getlocal(ui, rpath)
753
745
754 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
746 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
755 # reposetup. Programs like TortoiseHg will call _dispatch several
747 # reposetup. Programs like TortoiseHg will call _dispatch several
756 # times so we keep track of configured extensions in _loaded.
748 # times so we keep track of configured extensions in _loaded.
757 extensions.loadall(lui)
749 extensions.loadall(lui)
758 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
750 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
759 # Propagate any changes to lui.__class__ by extensions
751 # Propagate any changes to lui.__class__ by extensions
760 ui.__class__ = lui.__class__
752 ui.__class__ = lui.__class__
761
753
762 # (uisetup and extsetup are handled in extensions.loadall)
754 # (uisetup and extsetup are handled in extensions.loadall)
763
755
764 for name, module in exts:
756 for name, module in exts:
765 for objname, loadermod, loadername in extraloaders:
757 for objname, loadermod, loadername in extraloaders:
766 extraobj = getattr(module, objname, None)
758 extraobj = getattr(module, objname, None)
767 if extraobj is not None:
759 if extraobj is not None:
768 getattr(loadermod, loadername)(ui, name, extraobj)
760 getattr(loadermod, loadername)(ui, name, extraobj)
769 _loaded.add(name)
761 _loaded.add(name)
770
762
771 # (reposetup is handled in hg.repository)
763 # (reposetup is handled in hg.repository)
772
764
773 # Side-effect of accessing is debugcommands module is guaranteed to be
765 # Side-effect of accessing is debugcommands module is guaranteed to be
774 # imported and commands.table is populated.
766 # imported and commands.table is populated.
775 debugcommands.command
767 debugcommands.command
776
768
777 addaliases(lui, commands.table)
769 addaliases(lui, commands.table)
778
770
779 # All aliases and commands are completely defined, now.
771 # All aliases and commands are completely defined, now.
780 # Check abbreviation/ambiguity of shell alias.
772 # Check abbreviation/ambiguity of shell alias.
781 shellaliasfn = _checkshellalias(lui, ui, args)
773 shellaliasfn = _checkshellalias(lui, ui, args)
782 if shellaliasfn:
774 if shellaliasfn:
783 with profiling.maybeprofile(lui):
775 with profiling.maybeprofile(lui):
784 return shellaliasfn()
776 return shellaliasfn()
785
777
786 # check for fallback encoding
778 # check for fallback encoding
787 fallback = lui.config('ui', 'fallbackencoding')
779 fallback = lui.config('ui', 'fallbackencoding')
788 if fallback:
780 if fallback:
789 encoding.fallbackencoding = fallback
781 encoding.fallbackencoding = fallback
790
782
791 fullargs = args
783 fullargs = args
792 cmd, func, args, options, cmdoptions = _parse(lui, args)
784 cmd, func, args, options, cmdoptions = _parse(lui, args)
793
785
794 if options["config"]:
786 if options["config"]:
795 raise error.Abort(_("option --config may not be abbreviated!"))
787 raise error.Abort(_("option --config may not be abbreviated!"))
796 if options["cwd"]:
788 if options["cwd"]:
797 raise error.Abort(_("option --cwd may not be abbreviated!"))
789 raise error.Abort(_("option --cwd may not be abbreviated!"))
798 if options["repository"]:
790 if options["repository"]:
799 raise error.Abort(_(
791 raise error.Abort(_(
800 "option -R has to be separated from other options (e.g. not -qR) "
792 "option -R has to be separated from other options (e.g. not -qR) "
801 "and --repository may only be abbreviated as --repo!"))
793 "and --repository may only be abbreviated as --repo!"))
802
794
803 if options["encoding"]:
795 if options["encoding"]:
804 encoding.encoding = options["encoding"]
796 encoding.encoding = options["encoding"]
805 if options["encodingmode"]:
797 if options["encodingmode"]:
806 encoding.encodingmode = options["encodingmode"]
798 encoding.encodingmode = options["encodingmode"]
807 if options["time"]:
799 if options["time"]:
808 def get_times():
800 def get_times():
809 t = os.times()
801 t = os.times()
810 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
802 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
811 t = (t[0], t[1], t[2], t[3], time.clock())
803 t = (t[0], t[1], t[2], t[3], time.clock())
812 return t
804 return t
813 s = get_times()
805 s = get_times()
814 def print_time():
806 def print_time():
815 t = get_times()
807 t = get_times()
816 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
808 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
817 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
809 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
818 atexit.register(print_time)
810 atexit.register(print_time)
819
811
820 uis = set([ui, lui])
812 uis = set([ui, lui])
821
813
822 if req.repo:
814 if req.repo:
823 uis.add(req.repo.ui)
815 uis.add(req.repo.ui)
824
816
825 if options['verbose'] or options['debug'] or options['quiet']:
817 if options['verbose'] or options['debug'] or options['quiet']:
826 for opt in ('verbose', 'debug', 'quiet'):
818 for opt in ('verbose', 'debug', 'quiet'):
827 val = str(bool(options[opt]))
819 val = str(bool(options[opt]))
828 for ui_ in uis:
820 for ui_ in uis:
829 ui_.setconfig('ui', opt, val, '--' + opt)
821 ui_.setconfig('ui', opt, val, '--' + opt)
830
822
831 if options['profile']:
823 if options['profile']:
832 for ui_ in uis:
824 for ui_ in uis:
833 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
825 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
834
826
835 if options['traceback']:
827 if options['traceback']:
836 for ui_ in uis:
828 for ui_ in uis:
837 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
829 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
838
830
839 if options['noninteractive']:
831 if options['noninteractive']:
840 for ui_ in uis:
832 for ui_ in uis:
841 ui_.setconfig('ui', 'interactive', 'off', '-y')
833 ui_.setconfig('ui', 'interactive', 'off', '-y')
842
834
843 if cmdoptions.get('insecure', False):
835 if cmdoptions.get('insecure', False):
844 for ui_ in uis:
836 for ui_ in uis:
845 ui_.insecureconnections = True
837 ui_.insecureconnections = True
846
838
847 if options['version']:
839 if options['version']:
848 return commands.version_(ui)
840 return commands.version_(ui)
849 if options['help']:
841 if options['help']:
850 return commands.help_(ui, cmd, command=cmd is not None)
842 return commands.help_(ui, cmd, command=cmd is not None)
851 elif not cmd:
843 elif not cmd:
852 return commands.help_(ui, 'shortlist')
844 return commands.help_(ui, 'shortlist')
853
845
854 with profiling.maybeprofile(lui):
846 with profiling.maybeprofile(lui):
855 repo = None
847 repo = None
856 cmdpats = args[:]
848 cmdpats = args[:]
857 if not _cmdattr(ui, cmd, func, 'norepo'):
849 if not func.norepo:
858 # use the repo from the request only if we don't have -R
850 # use the repo from the request only if we don't have -R
859 if not rpath and not cwd:
851 if not rpath and not cwd:
860 repo = req.repo
852 repo = req.repo
861
853
862 if repo:
854 if repo:
863 # set the descriptors of the repo ui to those of ui
855 # set the descriptors of the repo ui to those of ui
864 repo.ui.fin = ui.fin
856 repo.ui.fin = ui.fin
865 repo.ui.fout = ui.fout
857 repo.ui.fout = ui.fout
866 repo.ui.ferr = ui.ferr
858 repo.ui.ferr = ui.ferr
867 else:
859 else:
868 try:
860 try:
869 repo = hg.repository(ui, path=path)
861 repo = hg.repository(ui, path=path)
870 if not repo.local():
862 if not repo.local():
871 raise error.Abort(_("repository '%s' is not local")
863 raise error.Abort(_("repository '%s' is not local")
872 % path)
864 % path)
873 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
865 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
874 'repo')
866 'repo')
875 except error.RequirementError:
867 except error.RequirementError:
876 raise
868 raise
877 except error.RepoError:
869 except error.RepoError:
878 if rpath and rpath[-1]: # invalid -R path
870 if rpath and rpath[-1]: # invalid -R path
879 raise
871 raise
880 if not _cmdattr(ui, cmd, func, 'optionalrepo'):
872 if not func.optionalrepo:
881 if (_cmdattr(ui, cmd, func, 'inferrepo') and
873 if func.inferrepo and args and not path:
882 args and not path):
883 # try to infer -R from command args
874 # try to infer -R from command args
884 repos = map(cmdutil.findrepo, args)
875 repos = map(cmdutil.findrepo, args)
885 guess = repos[0]
876 guess = repos[0]
886 if guess and repos.count(guess) == len(repos):
877 if guess and repos.count(guess) == len(repos):
887 req.args = ['--repository', guess] + fullargs
878 req.args = ['--repository', guess] + fullargs
888 return _dispatch(req)
879 return _dispatch(req)
889 if not path:
880 if not path:
890 raise error.RepoError(_("no repository found in"
881 raise error.RepoError(_("no repository found in"
891 " '%s' (.hg not found)")
882 " '%s' (.hg not found)")
892 % os.getcwd())
883 % os.getcwd())
893 raise
884 raise
894 if repo:
885 if repo:
895 ui = repo.ui
886 ui = repo.ui
896 if options['hidden']:
887 if options['hidden']:
897 repo = repo.unfiltered()
888 repo = repo.unfiltered()
898 args.insert(0, repo)
889 args.insert(0, repo)
899 elif rpath:
890 elif rpath:
900 ui.warn(_("warning: --repository ignored\n"))
891 ui.warn(_("warning: --repository ignored\n"))
901
892
902 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
893 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
903 ui.log("command", '%s\n', msg)
894 ui.log("command", '%s\n', msg)
904 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
895 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
905 try:
896 try:
906 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
897 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
907 cmdpats, cmdoptions)
898 cmdpats, cmdoptions)
908 finally:
899 finally:
909 if repo and repo != req.repo:
900 if repo and repo != req.repo:
910 repo.close()
901 repo.close()
911
902
912 def _runcommand(ui, options, cmd, cmdfunc):
903 def _runcommand(ui, options, cmd, cmdfunc):
913 """Run a command function, possibly with profiling enabled."""
904 """Run a command function, possibly with profiling enabled."""
914 try:
905 try:
915 return cmdfunc()
906 return cmdfunc()
916 except error.SignatureError:
907 except error.SignatureError:
917 raise error.CommandError(cmd, _('invalid arguments'))
908 raise error.CommandError(cmd, _('invalid arguments'))
918
909
919 def _exceptionwarning(ui):
910 def _exceptionwarning(ui):
920 """Produce a warning message for the current active exception"""
911 """Produce a warning message for the current active exception"""
921
912
922 # For compatibility checking, we discard the portion of the hg
913 # For compatibility checking, we discard the portion of the hg
923 # version after the + on the assumption that if a "normal
914 # version after the + on the assumption that if a "normal
924 # user" is running a build with a + in it the packager
915 # user" is running a build with a + in it the packager
925 # probably built from fairly close to a tag and anyone with a
916 # probably built from fairly close to a tag and anyone with a
926 # 'make local' copy of hg (where the version number can be out
917 # 'make local' copy of hg (where the version number can be out
927 # of date) will be clueful enough to notice the implausible
918 # of date) will be clueful enough to notice the implausible
928 # version number and try updating.
919 # version number and try updating.
929 ct = util.versiontuple(n=2)
920 ct = util.versiontuple(n=2)
930 worst = None, ct, ''
921 worst = None, ct, ''
931 if ui.config('ui', 'supportcontact', None) is None:
922 if ui.config('ui', 'supportcontact', None) is None:
932 for name, mod in extensions.extensions():
923 for name, mod in extensions.extensions():
933 testedwith = getattr(mod, 'testedwith', '')
924 testedwith = getattr(mod, 'testedwith', '')
934 report = getattr(mod, 'buglink', _('the extension author.'))
925 report = getattr(mod, 'buglink', _('the extension author.'))
935 if not testedwith.strip():
926 if not testedwith.strip():
936 # We found an untested extension. It's likely the culprit.
927 # We found an untested extension. It's likely the culprit.
937 worst = name, 'unknown', report
928 worst = name, 'unknown', report
938 break
929 break
939
930
940 # Never blame on extensions bundled with Mercurial.
931 # Never blame on extensions bundled with Mercurial.
941 if extensions.ismoduleinternal(mod):
932 if extensions.ismoduleinternal(mod):
942 continue
933 continue
943
934
944 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
935 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
945 if ct in tested:
936 if ct in tested:
946 continue
937 continue
947
938
948 lower = [t for t in tested if t < ct]
939 lower = [t for t in tested if t < ct]
949 nearest = max(lower or tested)
940 nearest = max(lower or tested)
950 if worst[0] is None or nearest < worst[1]:
941 if worst[0] is None or nearest < worst[1]:
951 worst = name, nearest, report
942 worst = name, nearest, report
952 if worst[0] is not None:
943 if worst[0] is not None:
953 name, testedwith, report = worst
944 name, testedwith, report = worst
954 if not isinstance(testedwith, str):
945 if not isinstance(testedwith, str):
955 testedwith = '.'.join([str(c) for c in testedwith])
946 testedwith = '.'.join([str(c) for c in testedwith])
956 warning = (_('** Unknown exception encountered with '
947 warning = (_('** Unknown exception encountered with '
957 'possibly-broken third-party extension %s\n'
948 'possibly-broken third-party extension %s\n'
958 '** which supports versions %s of Mercurial.\n'
949 '** which supports versions %s of Mercurial.\n'
959 '** Please disable %s and try your action again.\n'
950 '** Please disable %s and try your action again.\n'
960 '** If that fixes the bug please report it to %s\n')
951 '** If that fixes the bug please report it to %s\n')
961 % (name, testedwith, name, report))
952 % (name, testedwith, name, report))
962 else:
953 else:
963 bugtracker = ui.config('ui', 'supportcontact', None)
954 bugtracker = ui.config('ui', 'supportcontact', None)
964 if bugtracker is None:
955 if bugtracker is None:
965 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
956 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
966 warning = (_("** unknown exception encountered, "
957 warning = (_("** unknown exception encountered, "
967 "please report by visiting\n** ") + bugtracker + '\n')
958 "please report by visiting\n** ") + bugtracker + '\n')
968 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
959 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
969 (_("** Mercurial Distributed SCM (version %s)\n") %
960 (_("** Mercurial Distributed SCM (version %s)\n") %
970 util.version()) +
961 util.version()) +
971 (_("** Extensions loaded: %s\n") %
962 (_("** Extensions loaded: %s\n") %
972 ", ".join([x[0] for x in extensions.extensions()])))
963 ", ".join([x[0] for x in extensions.extensions()])))
973 return warning
964 return warning
974
965
975 def handlecommandexception(ui):
966 def handlecommandexception(ui):
976 """Produce a warning message for broken commands
967 """Produce a warning message for broken commands
977
968
978 Called when handling an exception; the exception is reraised if
969 Called when handling an exception; the exception is reraised if
979 this function returns False, ignored otherwise.
970 this function returns False, ignored otherwise.
980 """
971 """
981 warning = _exceptionwarning(ui)
972 warning = _exceptionwarning(ui)
982 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
973 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
983 ui.warn(warning)
974 ui.warn(warning)
984 return False # re-raise the exception
975 return False # re-raise the exception
@@ -1,1577 +1,1535 b''
1 Test basic extension support
1 Test basic extension support
2
2
3 $ cat > foobar.py <<EOF
3 $ cat > foobar.py <<EOF
4 > import os
4 > import os
5 > from mercurial import cmdutil, commands
5 > from mercurial import cmdutil, commands
6 > cmdtable = {}
6 > cmdtable = {}
7 > command = cmdutil.command(cmdtable)
7 > command = cmdutil.command(cmdtable)
8 > def uisetup(ui):
8 > def uisetup(ui):
9 > ui.write("uisetup called\\n")
9 > ui.write("uisetup called\\n")
10 > ui.flush()
10 > ui.flush()
11 > def reposetup(ui, repo):
11 > def reposetup(ui, repo):
12 > ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
12 > ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
13 > ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
13 > ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
14 > ui.flush()
14 > ui.flush()
15 > @command('foo', [], 'hg foo')
15 > @command('foo', [], 'hg foo')
16 > def foo(ui, *args, **kwargs):
16 > def foo(ui, *args, **kwargs):
17 > ui.write("Foo\\n")
17 > ui.write("Foo\\n")
18 > @command('bar', [], 'hg bar', norepo=True)
18 > @command('bar', [], 'hg bar', norepo=True)
19 > def bar(ui, *args, **kwargs):
19 > def bar(ui, *args, **kwargs):
20 > ui.write("Bar\\n")
20 > ui.write("Bar\\n")
21 > EOF
21 > EOF
22 $ abspath=`pwd`/foobar.py
22 $ abspath=`pwd`/foobar.py
23
23
24 $ mkdir barfoo
24 $ mkdir barfoo
25 $ cp foobar.py barfoo/__init__.py
25 $ cp foobar.py barfoo/__init__.py
26 $ barfoopath=`pwd`/barfoo
26 $ barfoopath=`pwd`/barfoo
27
27
28 $ hg init a
28 $ hg init a
29 $ cd a
29 $ cd a
30 $ echo foo > file
30 $ echo foo > file
31 $ hg add file
31 $ hg add file
32 $ hg commit -m 'add file'
32 $ hg commit -m 'add file'
33
33
34 $ echo '[extensions]' >> $HGRCPATH
34 $ echo '[extensions]' >> $HGRCPATH
35 $ echo "foobar = $abspath" >> $HGRCPATH
35 $ echo "foobar = $abspath" >> $HGRCPATH
36 $ hg foo
36 $ hg foo
37 uisetup called
37 uisetup called
38 reposetup called for a
38 reposetup called for a
39 ui == repo.ui
39 ui == repo.ui
40 Foo
40 Foo
41
41
42 $ cd ..
42 $ cd ..
43 $ hg clone a b
43 $ hg clone a b
44 uisetup called
44 uisetup called
45 reposetup called for a
45 reposetup called for a
46 ui == repo.ui
46 ui == repo.ui
47 reposetup called for b
47 reposetup called for b
48 ui == repo.ui
48 ui == repo.ui
49 updating to branch default
49 updating to branch default
50 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
50 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
51
51
52 $ hg bar
52 $ hg bar
53 uisetup called
53 uisetup called
54 Bar
54 Bar
55 $ echo 'foobar = !' >> $HGRCPATH
55 $ echo 'foobar = !' >> $HGRCPATH
56
56
57 module/__init__.py-style
57 module/__init__.py-style
58
58
59 $ echo "barfoo = $barfoopath" >> $HGRCPATH
59 $ echo "barfoo = $barfoopath" >> $HGRCPATH
60 $ cd a
60 $ cd a
61 $ hg foo
61 $ hg foo
62 uisetup called
62 uisetup called
63 reposetup called for a
63 reposetup called for a
64 ui == repo.ui
64 ui == repo.ui
65 Foo
65 Foo
66 $ echo 'barfoo = !' >> $HGRCPATH
66 $ echo 'barfoo = !' >> $HGRCPATH
67
67
68 Check that extensions are loaded in phases:
68 Check that extensions are loaded in phases:
69
69
70 $ cat > foo.py <<EOF
70 $ cat > foo.py <<EOF
71 > import os
71 > import os
72 > name = os.path.basename(__file__).rsplit('.', 1)[0]
72 > name = os.path.basename(__file__).rsplit('.', 1)[0]
73 > print "1) %s imported" % name
73 > print "1) %s imported" % name
74 > def uisetup(ui):
74 > def uisetup(ui):
75 > print "2) %s uisetup" % name
75 > print "2) %s uisetup" % name
76 > def extsetup():
76 > def extsetup():
77 > print "3) %s extsetup" % name
77 > print "3) %s extsetup" % name
78 > def reposetup(ui, repo):
78 > def reposetup(ui, repo):
79 > print "4) %s reposetup" % name
79 > print "4) %s reposetup" % name
80 > EOF
80 > EOF
81
81
82 $ cp foo.py bar.py
82 $ cp foo.py bar.py
83 $ echo 'foo = foo.py' >> $HGRCPATH
83 $ echo 'foo = foo.py' >> $HGRCPATH
84 $ echo 'bar = bar.py' >> $HGRCPATH
84 $ echo 'bar = bar.py' >> $HGRCPATH
85
85
86 Command with no output, we just want to see the extensions loaded:
86 Command with no output, we just want to see the extensions loaded:
87
87
88 $ hg paths
88 $ hg paths
89 1) foo imported
89 1) foo imported
90 1) bar imported
90 1) bar imported
91 2) foo uisetup
91 2) foo uisetup
92 2) bar uisetup
92 2) bar uisetup
93 3) foo extsetup
93 3) foo extsetup
94 3) bar extsetup
94 3) bar extsetup
95 4) foo reposetup
95 4) foo reposetup
96 4) bar reposetup
96 4) bar reposetup
97
97
98 Check hgweb's load order:
98 Check hgweb's load order:
99
99
100 $ cat > hgweb.cgi <<EOF
100 $ cat > hgweb.cgi <<EOF
101 > #!/usr/bin/env python
101 > #!/usr/bin/env python
102 > from mercurial import demandimport; demandimport.enable()
102 > from mercurial import demandimport; demandimport.enable()
103 > from mercurial.hgweb import hgweb
103 > from mercurial.hgweb import hgweb
104 > from mercurial.hgweb import wsgicgi
104 > from mercurial.hgweb import wsgicgi
105 > application = hgweb('.', 'test repo')
105 > application = hgweb('.', 'test repo')
106 > wsgicgi.launch(application)
106 > wsgicgi.launch(application)
107 > EOF
107 > EOF
108
108
109 $ REQUEST_METHOD='GET' PATH_INFO='/' SCRIPT_NAME='' QUERY_STRING='' \
109 $ REQUEST_METHOD='GET' PATH_INFO='/' SCRIPT_NAME='' QUERY_STRING='' \
110 > SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
110 > SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
111 > | grep '^[0-9]) ' # ignores HTML output
111 > | grep '^[0-9]) ' # ignores HTML output
112 1) foo imported
112 1) foo imported
113 1) bar imported
113 1) bar imported
114 2) foo uisetup
114 2) foo uisetup
115 2) bar uisetup
115 2) bar uisetup
116 3) foo extsetup
116 3) foo extsetup
117 3) bar extsetup
117 3) bar extsetup
118 4) foo reposetup
118 4) foo reposetup
119 4) bar reposetup
119 4) bar reposetup
120
120
121 $ echo 'foo = !' >> $HGRCPATH
121 $ echo 'foo = !' >> $HGRCPATH
122 $ echo 'bar = !' >> $HGRCPATH
122 $ echo 'bar = !' >> $HGRCPATH
123
123
124 Check "from __future__ import absolute_import" support for external libraries
124 Check "from __future__ import absolute_import" support for external libraries
125
125
126 #if windows
126 #if windows
127 $ PATHSEP=";"
127 $ PATHSEP=";"
128 #else
128 #else
129 $ PATHSEP=":"
129 $ PATHSEP=":"
130 #endif
130 #endif
131 $ export PATHSEP
131 $ export PATHSEP
132
132
133 $ mkdir $TESTTMP/libroot
133 $ mkdir $TESTTMP/libroot
134 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
134 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
135 $ mkdir $TESTTMP/libroot/mod
135 $ mkdir $TESTTMP/libroot/mod
136 $ touch $TESTTMP/libroot/mod/__init__.py
136 $ touch $TESTTMP/libroot/mod/__init__.py
137 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
137 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
138
138
139 #if absimport
139 #if absimport
140 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
140 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
141 > from __future__ import absolute_import
141 > from __future__ import absolute_import
142 > import ambig # should load "libroot/ambig.py"
142 > import ambig # should load "libroot/ambig.py"
143 > s = ambig.s
143 > s = ambig.s
144 > EOF
144 > EOF
145 $ cat > loadabs.py <<EOF
145 $ cat > loadabs.py <<EOF
146 > import mod.ambigabs as ambigabs
146 > import mod.ambigabs as ambigabs
147 > def extsetup():
147 > def extsetup():
148 > print 'ambigabs.s=%s' % ambigabs.s
148 > print 'ambigabs.s=%s' % ambigabs.s
149 > EOF
149 > EOF
150 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
150 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
151 ambigabs.s=libroot/ambig.py
151 ambigabs.s=libroot/ambig.py
152 $TESTTMP/a (glob)
152 $TESTTMP/a (glob)
153 #endif
153 #endif
154
154
155 #if no-py3k
155 #if no-py3k
156 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
156 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
157 > import ambig # should load "libroot/mod/ambig.py"
157 > import ambig # should load "libroot/mod/ambig.py"
158 > s = ambig.s
158 > s = ambig.s
159 > EOF
159 > EOF
160 $ cat > loadrel.py <<EOF
160 $ cat > loadrel.py <<EOF
161 > import mod.ambigrel as ambigrel
161 > import mod.ambigrel as ambigrel
162 > def extsetup():
162 > def extsetup():
163 > print 'ambigrel.s=%s' % ambigrel.s
163 > print 'ambigrel.s=%s' % ambigrel.s
164 > EOF
164 > EOF
165 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
165 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
166 ambigrel.s=libroot/mod/ambig.py
166 ambigrel.s=libroot/mod/ambig.py
167 $TESTTMP/a (glob)
167 $TESTTMP/a (glob)
168 #endif
168 #endif
169
169
170 Check absolute/relative import of extension specific modules
170 Check absolute/relative import of extension specific modules
171
171
172 $ mkdir $TESTTMP/extroot
172 $ mkdir $TESTTMP/extroot
173 $ cat > $TESTTMP/extroot/bar.py <<EOF
173 $ cat > $TESTTMP/extroot/bar.py <<EOF
174 > s = 'this is extroot.bar'
174 > s = 'this is extroot.bar'
175 > EOF
175 > EOF
176 $ mkdir $TESTTMP/extroot/sub1
176 $ mkdir $TESTTMP/extroot/sub1
177 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
177 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
178 > s = 'this is extroot.sub1.__init__'
178 > s = 'this is extroot.sub1.__init__'
179 > EOF
179 > EOF
180 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
180 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
181 > s = 'this is extroot.sub1.baz'
181 > s = 'this is extroot.sub1.baz'
182 > EOF
182 > EOF
183 $ cat > $TESTTMP/extroot/__init__.py <<EOF
183 $ cat > $TESTTMP/extroot/__init__.py <<EOF
184 > s = 'this is extroot.__init__'
184 > s = 'this is extroot.__init__'
185 > import foo
185 > import foo
186 > def extsetup(ui):
186 > def extsetup(ui):
187 > ui.write('(extroot) ', foo.func(), '\n')
187 > ui.write('(extroot) ', foo.func(), '\n')
188 > ui.flush()
188 > ui.flush()
189 > EOF
189 > EOF
190
190
191 $ cat > $TESTTMP/extroot/foo.py <<EOF
191 $ cat > $TESTTMP/extroot/foo.py <<EOF
192 > # test absolute import
192 > # test absolute import
193 > buf = []
193 > buf = []
194 > def func():
194 > def func():
195 > # "not locals" case
195 > # "not locals" case
196 > import extroot.bar
196 > import extroot.bar
197 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
197 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
198 > return '\n(extroot) '.join(buf)
198 > return '\n(extroot) '.join(buf)
199 > # "fromlist == ('*',)" case
199 > # "fromlist == ('*',)" case
200 > from extroot.bar import *
200 > from extroot.bar import *
201 > buf.append('from extroot.bar import *: %s' % s)
201 > buf.append('from extroot.bar import *: %s' % s)
202 > # "not fromlist" and "if '.' in name" case
202 > # "not fromlist" and "if '.' in name" case
203 > import extroot.sub1.baz
203 > import extroot.sub1.baz
204 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
204 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
205 > # "not fromlist" and NOT "if '.' in name" case
205 > # "not fromlist" and NOT "if '.' in name" case
206 > import extroot
206 > import extroot
207 > buf.append('import extroot: %s' % extroot.s)
207 > buf.append('import extroot: %s' % extroot.s)
208 > # NOT "not fromlist" and NOT "level != -1" case
208 > # NOT "not fromlist" and NOT "level != -1" case
209 > from extroot.bar import s
209 > from extroot.bar import s
210 > buf.append('from extroot.bar import s: %s' % s)
210 > buf.append('from extroot.bar import s: %s' % s)
211 > EOF
211 > EOF
212 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
212 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
213 (extroot) from extroot.bar import *: this is extroot.bar
213 (extroot) from extroot.bar import *: this is extroot.bar
214 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
214 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
215 (extroot) import extroot: this is extroot.__init__
215 (extroot) import extroot: this is extroot.__init__
216 (extroot) from extroot.bar import s: this is extroot.bar
216 (extroot) from extroot.bar import s: this is extroot.bar
217 (extroot) import extroot.bar in func(): this is extroot.bar
217 (extroot) import extroot.bar in func(): this is extroot.bar
218 $TESTTMP/a (glob)
218 $TESTTMP/a (glob)
219
219
220 #if no-py3k
220 #if no-py3k
221 $ rm "$TESTTMP"/extroot/foo.*
221 $ rm "$TESTTMP"/extroot/foo.*
222 $ cat > $TESTTMP/extroot/foo.py <<EOF
222 $ cat > $TESTTMP/extroot/foo.py <<EOF
223 > # test relative import
223 > # test relative import
224 > buf = []
224 > buf = []
225 > def func():
225 > def func():
226 > # "not locals" case
226 > # "not locals" case
227 > import bar
227 > import bar
228 > buf.append('import bar in func(): %s' % bar.s)
228 > buf.append('import bar in func(): %s' % bar.s)
229 > return '\n(extroot) '.join(buf)
229 > return '\n(extroot) '.join(buf)
230 > # "fromlist == ('*',)" case
230 > # "fromlist == ('*',)" case
231 > from bar import *
231 > from bar import *
232 > buf.append('from bar import *: %s' % s)
232 > buf.append('from bar import *: %s' % s)
233 > # "not fromlist" and "if '.' in name" case
233 > # "not fromlist" and "if '.' in name" case
234 > import sub1.baz
234 > import sub1.baz
235 > buf.append('import sub1.baz: %s' % sub1.baz.s)
235 > buf.append('import sub1.baz: %s' % sub1.baz.s)
236 > # "not fromlist" and NOT "if '.' in name" case
236 > # "not fromlist" and NOT "if '.' in name" case
237 > import sub1
237 > import sub1
238 > buf.append('import sub1: %s' % sub1.s)
238 > buf.append('import sub1: %s' % sub1.s)
239 > # NOT "not fromlist" and NOT "level != -1" case
239 > # NOT "not fromlist" and NOT "level != -1" case
240 > from bar import s
240 > from bar import s
241 > buf.append('from bar import s: %s' % s)
241 > buf.append('from bar import s: %s' % s)
242 > EOF
242 > EOF
243 $ hg --config extensions.extroot=$TESTTMP/extroot root
243 $ hg --config extensions.extroot=$TESTTMP/extroot root
244 (extroot) from bar import *: this is extroot.bar
244 (extroot) from bar import *: this is extroot.bar
245 (extroot) import sub1.baz: this is extroot.sub1.baz
245 (extroot) import sub1.baz: this is extroot.sub1.baz
246 (extroot) import sub1: this is extroot.sub1.__init__
246 (extroot) import sub1: this is extroot.sub1.__init__
247 (extroot) from bar import s: this is extroot.bar
247 (extroot) from bar import s: this is extroot.bar
248 (extroot) import bar in func(): this is extroot.bar
248 (extroot) import bar in func(): this is extroot.bar
249 $TESTTMP/a (glob)
249 $TESTTMP/a (glob)
250 #endif
250 #endif
251
251
252 #if demandimport absimport
252 #if demandimport absimport
253
253
254 Examine whether module loading is delayed until actual referring, even
254 Examine whether module loading is delayed until actual referring, even
255 though module is imported with "absolute_import" feature.
255 though module is imported with "absolute_import" feature.
256
256
257 Files below in each packages are used for described purpose:
257 Files below in each packages are used for described purpose:
258
258
259 - "called": examine whether "from MODULE import ATTR" works correctly
259 - "called": examine whether "from MODULE import ATTR" works correctly
260 - "unused": examine whether loading is delayed correctly
260 - "unused": examine whether loading is delayed correctly
261 - "used": examine whether "from PACKAGE import MODULE" works correctly
261 - "used": examine whether "from PACKAGE import MODULE" works correctly
262
262
263 Package hierarchy is needed to examine whether demand importing works
263 Package hierarchy is needed to examine whether demand importing works
264 as expected for "from SUB.PACK.AGE import MODULE".
264 as expected for "from SUB.PACK.AGE import MODULE".
265
265
266 Setup "external library" to be imported with "absolute_import"
266 Setup "external library" to be imported with "absolute_import"
267 feature.
267 feature.
268
268
269 $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
269 $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
270 $ touch $TESTTMP/extlibroot/__init__.py
270 $ touch $TESTTMP/extlibroot/__init__.py
271 $ touch $TESTTMP/extlibroot/lsub1/__init__.py
271 $ touch $TESTTMP/extlibroot/lsub1/__init__.py
272 $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
272 $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
273
273
274 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<EOF
274 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<EOF
275 > def func():
275 > def func():
276 > return "this is extlibroot.lsub1.lsub2.called.func()"
276 > return "this is extlibroot.lsub1.lsub2.called.func()"
277 > EOF
277 > EOF
278 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<EOF
278 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<EOF
279 > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
279 > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
280 > EOF
280 > EOF
281 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<EOF
281 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<EOF
282 > detail = "this is extlibroot.lsub1.lsub2.used"
282 > detail = "this is extlibroot.lsub1.lsub2.used"
283 > EOF
283 > EOF
284
284
285 Setup sub-package of "external library", which causes instantiation of
285 Setup sub-package of "external library", which causes instantiation of
286 demandmod in "recurse down the module chain" code path. Relative
286 demandmod in "recurse down the module chain" code path. Relative
287 importing with "absolute_import" feature isn't tested, because "level
287 importing with "absolute_import" feature isn't tested, because "level
288 >=1 " doesn't cause instantiation of demandmod.
288 >=1 " doesn't cause instantiation of demandmod.
289
289
290 $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
290 $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
291 $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<EOF
291 $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<EOF
292 > detail = "this is extlibroot.recursedown.abs.used"
292 > detail = "this is extlibroot.recursedown.abs.used"
293 > EOF
293 > EOF
294 $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<EOF
294 $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<EOF
295 > from __future__ import absolute_import
295 > from __future__ import absolute_import
296 > from extlibroot.recursedown.abs.used import detail
296 > from extlibroot.recursedown.abs.used import detail
297 > EOF
297 > EOF
298
298
299 $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
299 $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
300 $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<EOF
300 $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<EOF
301 > detail = "this is extlibroot.recursedown.legacy.used"
301 > detail = "this is extlibroot.recursedown.legacy.used"
302 > EOF
302 > EOF
303 $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<EOF
303 $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<EOF
304 > # legacy style (level == -1) import
304 > # legacy style (level == -1) import
305 > from extlibroot.recursedown.legacy.used import detail
305 > from extlibroot.recursedown.legacy.used import detail
306 > EOF
306 > EOF
307
307
308 $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<EOF
308 $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<EOF
309 > from __future__ import absolute_import
309 > from __future__ import absolute_import
310 > from extlibroot.recursedown.abs import detail as absdetail
310 > from extlibroot.recursedown.abs import detail as absdetail
311 > from .legacy import detail as legacydetail
311 > from .legacy import detail as legacydetail
312 > EOF
312 > EOF
313
313
314 Setup extension local modules to be imported with "absolute_import"
314 Setup extension local modules to be imported with "absolute_import"
315 feature.
315 feature.
316
316
317 $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
317 $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
318 $ touch $TESTTMP/absextroot/xsub1/__init__.py
318 $ touch $TESTTMP/absextroot/xsub1/__init__.py
319 $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
319 $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
320
320
321 $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<EOF
321 $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<EOF
322 > def func():
322 > def func():
323 > return "this is absextroot.xsub1.xsub2.called.func()"
323 > return "this is absextroot.xsub1.xsub2.called.func()"
324 > EOF
324 > EOF
325 $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<EOF
325 $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<EOF
326 > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
326 > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
327 > EOF
327 > EOF
328 $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<EOF
328 $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<EOF
329 > detail = "this is absextroot.xsub1.xsub2.used"
329 > detail = "this is absextroot.xsub1.xsub2.used"
330 > EOF
330 > EOF
331
331
332 Setup extension local modules to examine whether demand importing
332 Setup extension local modules to examine whether demand importing
333 works as expected in "level > 1" case.
333 works as expected in "level > 1" case.
334
334
335 $ cat > $TESTTMP/absextroot/relimportee.py <<EOF
335 $ cat > $TESTTMP/absextroot/relimportee.py <<EOF
336 > detail = "this is absextroot.relimportee"
336 > detail = "this is absextroot.relimportee"
337 > EOF
337 > EOF
338 $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<EOF
338 $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<EOF
339 > from __future__ import absolute_import
339 > from __future__ import absolute_import
340 > from ... import relimportee
340 > from ... import relimportee
341 > detail = "this relimporter imports %r" % (relimportee.detail)
341 > detail = "this relimporter imports %r" % (relimportee.detail)
342 > EOF
342 > EOF
343
343
344 Setup modules, which actually import extension local modules at
344 Setup modules, which actually import extension local modules at
345 runtime.
345 runtime.
346
346
347 $ cat > $TESTTMP/absextroot/absolute.py << EOF
347 $ cat > $TESTTMP/absextroot/absolute.py << EOF
348 > from __future__ import absolute_import
348 > from __future__ import absolute_import
349 >
349 >
350 > # import extension local modules absolutely (level = 0)
350 > # import extension local modules absolutely (level = 0)
351 > from absextroot.xsub1.xsub2 import used, unused
351 > from absextroot.xsub1.xsub2 import used, unused
352 > from absextroot.xsub1.xsub2.called import func
352 > from absextroot.xsub1.xsub2.called import func
353 >
353 >
354 > def getresult():
354 > def getresult():
355 > result = []
355 > result = []
356 > result.append(used.detail)
356 > result.append(used.detail)
357 > result.append(func())
357 > result.append(func())
358 > return result
358 > return result
359 > EOF
359 > EOF
360
360
361 $ cat > $TESTTMP/absextroot/relative.py << EOF
361 $ cat > $TESTTMP/absextroot/relative.py << EOF
362 > from __future__ import absolute_import
362 > from __future__ import absolute_import
363 >
363 >
364 > # import extension local modules relatively (level == 1)
364 > # import extension local modules relatively (level == 1)
365 > from .xsub1.xsub2 import used, unused
365 > from .xsub1.xsub2 import used, unused
366 > from .xsub1.xsub2.called import func
366 > from .xsub1.xsub2.called import func
367 >
367 >
368 > # import a module, which implies "importing with level > 1"
368 > # import a module, which implies "importing with level > 1"
369 > from .xsub1.xsub2 import relimporter
369 > from .xsub1.xsub2 import relimporter
370 >
370 >
371 > def getresult():
371 > def getresult():
372 > result = []
372 > result = []
373 > result.append(used.detail)
373 > result.append(used.detail)
374 > result.append(func())
374 > result.append(func())
375 > result.append(relimporter.detail)
375 > result.append(relimporter.detail)
376 > return result
376 > return result
377 > EOF
377 > EOF
378
378
379 Setup main procedure of extension.
379 Setup main procedure of extension.
380
380
381 $ cat > $TESTTMP/absextroot/__init__.py <<EOF
381 $ cat > $TESTTMP/absextroot/__init__.py <<EOF
382 > from __future__ import absolute_import
382 > from __future__ import absolute_import
383 > from mercurial import cmdutil
383 > from mercurial import cmdutil
384 > cmdtable = {}
384 > cmdtable = {}
385 > command = cmdutil.command(cmdtable)
385 > command = cmdutil.command(cmdtable)
386 >
386 >
387 > # "absolute" and "relative" shouldn't be imported before actual
387 > # "absolute" and "relative" shouldn't be imported before actual
388 > # command execution, because (1) they import same modules, and (2)
388 > # command execution, because (1) they import same modules, and (2)
389 > # preceding import (= instantiate "demandmod" object instead of
389 > # preceding import (= instantiate "demandmod" object instead of
390 > # real "module" object) might hide problem of succeeding import.
390 > # real "module" object) might hide problem of succeeding import.
391 >
391 >
392 > @command('showabsolute', [], norepo=True)
392 > @command('showabsolute', [], norepo=True)
393 > def showabsolute(ui, *args, **opts):
393 > def showabsolute(ui, *args, **opts):
394 > from absextroot import absolute
394 > from absextroot import absolute
395 > ui.write('ABS: %s\n' % '\nABS: '.join(absolute.getresult()))
395 > ui.write('ABS: %s\n' % '\nABS: '.join(absolute.getresult()))
396 >
396 >
397 > @command('showrelative', [], norepo=True)
397 > @command('showrelative', [], norepo=True)
398 > def showrelative(ui, *args, **opts):
398 > def showrelative(ui, *args, **opts):
399 > from . import relative
399 > from . import relative
400 > ui.write('REL: %s\n' % '\nREL: '.join(relative.getresult()))
400 > ui.write('REL: %s\n' % '\nREL: '.join(relative.getresult()))
401 >
401 >
402 > # import modules from external library
402 > # import modules from external library
403 > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
403 > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
404 > from extlibroot.lsub1.lsub2.called import func as lfunc
404 > from extlibroot.lsub1.lsub2.called import func as lfunc
405 > from extlibroot.recursedown import absdetail, legacydetail
405 > from extlibroot.recursedown import absdetail, legacydetail
406 >
406 >
407 > def uisetup(ui):
407 > def uisetup(ui):
408 > result = []
408 > result = []
409 > result.append(lused.detail)
409 > result.append(lused.detail)
410 > result.append(lfunc())
410 > result.append(lfunc())
411 > result.append(absdetail)
411 > result.append(absdetail)
412 > result.append(legacydetail)
412 > result.append(legacydetail)
413 > ui.write('LIB: %s\n' % '\nLIB: '.join(result))
413 > ui.write('LIB: %s\n' % '\nLIB: '.join(result))
414 > EOF
414 > EOF
415
415
416 Examine module importing.
416 Examine module importing.
417
417
418 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
418 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
419 LIB: this is extlibroot.lsub1.lsub2.used
419 LIB: this is extlibroot.lsub1.lsub2.used
420 LIB: this is extlibroot.lsub1.lsub2.called.func()
420 LIB: this is extlibroot.lsub1.lsub2.called.func()
421 LIB: this is extlibroot.recursedown.abs.used
421 LIB: this is extlibroot.recursedown.abs.used
422 LIB: this is extlibroot.recursedown.legacy.used
422 LIB: this is extlibroot.recursedown.legacy.used
423 ABS: this is absextroot.xsub1.xsub2.used
423 ABS: this is absextroot.xsub1.xsub2.used
424 ABS: this is absextroot.xsub1.xsub2.called.func()
424 ABS: this is absextroot.xsub1.xsub2.called.func()
425
425
426 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
426 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
427 LIB: this is extlibroot.lsub1.lsub2.used
427 LIB: this is extlibroot.lsub1.lsub2.used
428 LIB: this is extlibroot.lsub1.lsub2.called.func()
428 LIB: this is extlibroot.lsub1.lsub2.called.func()
429 LIB: this is extlibroot.recursedown.abs.used
429 LIB: this is extlibroot.recursedown.abs.used
430 LIB: this is extlibroot.recursedown.legacy.used
430 LIB: this is extlibroot.recursedown.legacy.used
431 REL: this is absextroot.xsub1.xsub2.used
431 REL: this is absextroot.xsub1.xsub2.used
432 REL: this is absextroot.xsub1.xsub2.called.func()
432 REL: this is absextroot.xsub1.xsub2.called.func()
433 REL: this relimporter imports 'this is absextroot.relimportee'
433 REL: this relimporter imports 'this is absextroot.relimportee'
434
434
435 Examine whether sub-module is imported relatively as expected.
435 Examine whether sub-module is imported relatively as expected.
436
436
437 See also issue5208 for detail about example case on Python 3.x.
437 See also issue5208 for detail about example case on Python 3.x.
438
438
439 $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
439 $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
440 $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
440 $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
441
441
442 $ cat > $TESTTMP/notexist.py <<EOF
442 $ cat > $TESTTMP/notexist.py <<EOF
443 > text = 'notexist.py at root is loaded unintentionally\n'
443 > text = 'notexist.py at root is loaded unintentionally\n'
444 > EOF
444 > EOF
445
445
446 $ cat > $TESTTMP/checkrelativity.py <<EOF
446 $ cat > $TESTTMP/checkrelativity.py <<EOF
447 > from mercurial import cmdutil
447 > from mercurial import cmdutil
448 > cmdtable = {}
448 > cmdtable = {}
449 > command = cmdutil.command(cmdtable)
449 > command = cmdutil.command(cmdtable)
450 >
450 >
451 > # demand import avoids failure of importing notexist here
451 > # demand import avoids failure of importing notexist here
452 > import extlibroot.lsub1.lsub2.notexist
452 > import extlibroot.lsub1.lsub2.notexist
453 >
453 >
454 > @command('checkrelativity', [], norepo=True)
454 > @command('checkrelativity', [], norepo=True)
455 > def checkrelativity(ui, *args, **opts):
455 > def checkrelativity(ui, *args, **opts):
456 > try:
456 > try:
457 > ui.write(extlibroot.lsub1.lsub2.notexist.text)
457 > ui.write(extlibroot.lsub1.lsub2.notexist.text)
458 > return 1 # unintentional success
458 > return 1 # unintentional success
459 > except ImportError:
459 > except ImportError:
460 > pass # intentional failure
460 > pass # intentional failure
461 > EOF
461 > EOF
462
462
463 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity)
463 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity)
464
464
465 #endif
465 #endif
466
466
467 $ cd ..
467 $ cd ..
468
468
469 hide outer repo
469 hide outer repo
470 $ hg init
470 $ hg init
471
471
472 $ cat > empty.py <<EOF
472 $ cat > empty.py <<EOF
473 > '''empty cmdtable
473 > '''empty cmdtable
474 > '''
474 > '''
475 > cmdtable = {}
475 > cmdtable = {}
476 > EOF
476 > EOF
477 $ emptypath=`pwd`/empty.py
477 $ emptypath=`pwd`/empty.py
478 $ echo "empty = $emptypath" >> $HGRCPATH
478 $ echo "empty = $emptypath" >> $HGRCPATH
479 $ hg help empty
479 $ hg help empty
480 empty extension - empty cmdtable
480 empty extension - empty cmdtable
481
481
482 no commands defined
482 no commands defined
483
483
484
484
485 $ echo 'empty = !' >> $HGRCPATH
485 $ echo 'empty = !' >> $HGRCPATH
486
486
487 $ cat > debugextension.py <<EOF
487 $ cat > debugextension.py <<EOF
488 > '''only debugcommands
488 > '''only debugcommands
489 > '''
489 > '''
490 > from mercurial import cmdutil
490 > from mercurial import cmdutil
491 > cmdtable = {}
491 > cmdtable = {}
492 > command = cmdutil.command(cmdtable)
492 > command = cmdutil.command(cmdtable)
493 > @command('debugfoobar', [], 'hg debugfoobar')
493 > @command('debugfoobar', [], 'hg debugfoobar')
494 > def debugfoobar(ui, repo, *args, **opts):
494 > def debugfoobar(ui, repo, *args, **opts):
495 > "yet another debug command"
495 > "yet another debug command"
496 > pass
496 > pass
497 > @command('foo', [], 'hg foo')
497 > @command('foo', [], 'hg foo')
498 > def foo(ui, repo, *args, **opts):
498 > def foo(ui, repo, *args, **opts):
499 > """yet another foo command
499 > """yet another foo command
500 > This command has been DEPRECATED since forever.
500 > This command has been DEPRECATED since forever.
501 > """
501 > """
502 > pass
502 > pass
503 > EOF
503 > EOF
504 $ debugpath=`pwd`/debugextension.py
504 $ debugpath=`pwd`/debugextension.py
505 $ echo "debugextension = $debugpath" >> $HGRCPATH
505 $ echo "debugextension = $debugpath" >> $HGRCPATH
506
506
507 $ hg help debugextension
507 $ hg help debugextension
508 hg debugextensions
508 hg debugextensions
509
509
510 show information about active extensions
510 show information about active extensions
511
511
512 options:
512 options:
513
513
514 (some details hidden, use --verbose to show complete help)
514 (some details hidden, use --verbose to show complete help)
515
515
516
516
517 $ hg --verbose help debugextension
517 $ hg --verbose help debugextension
518 hg debugextensions
518 hg debugextensions
519
519
520 show information about active extensions
520 show information about active extensions
521
521
522 options:
522 options:
523
523
524 -T --template TEMPLATE display with template (EXPERIMENTAL)
524 -T --template TEMPLATE display with template (EXPERIMENTAL)
525
525
526 global options ([+] can be repeated):
526 global options ([+] can be repeated):
527
527
528 -R --repository REPO repository root directory or name of overlay bundle
528 -R --repository REPO repository root directory or name of overlay bundle
529 file
529 file
530 --cwd DIR change working directory
530 --cwd DIR change working directory
531 -y --noninteractive do not prompt, automatically pick the first choice for
531 -y --noninteractive do not prompt, automatically pick the first choice for
532 all prompts
532 all prompts
533 -q --quiet suppress output
533 -q --quiet suppress output
534 -v --verbose enable additional output
534 -v --verbose enable additional output
535 --config CONFIG [+] set/override config option (use 'section.name=value')
535 --config CONFIG [+] set/override config option (use 'section.name=value')
536 --debug enable debugging output
536 --debug enable debugging output
537 --debugger start debugger
537 --debugger start debugger
538 --encoding ENCODE set the charset encoding (default: ascii)
538 --encoding ENCODE set the charset encoding (default: ascii)
539 --encodingmode MODE set the charset encoding mode (default: strict)
539 --encodingmode MODE set the charset encoding mode (default: strict)
540 --traceback always print a traceback on exception
540 --traceback always print a traceback on exception
541 --time time how long the command takes
541 --time time how long the command takes
542 --profile print command execution profile
542 --profile print command execution profile
543 --version output version information and exit
543 --version output version information and exit
544 -h --help display help and exit
544 -h --help display help and exit
545 --hidden consider hidden changesets
545 --hidden consider hidden changesets
546
546
547
547
548
548
549
549
550
550
551
551
552 $ hg --debug help debugextension
552 $ hg --debug help debugextension
553 hg debugextensions
553 hg debugextensions
554
554
555 show information about active extensions
555 show information about active extensions
556
556
557 options:
557 options:
558
558
559 -T --template TEMPLATE display with template (EXPERIMENTAL)
559 -T --template TEMPLATE display with template (EXPERIMENTAL)
560
560
561 global options ([+] can be repeated):
561 global options ([+] can be repeated):
562
562
563 -R --repository REPO repository root directory or name of overlay bundle
563 -R --repository REPO repository root directory or name of overlay bundle
564 file
564 file
565 --cwd DIR change working directory
565 --cwd DIR change working directory
566 -y --noninteractive do not prompt, automatically pick the first choice for
566 -y --noninteractive do not prompt, automatically pick the first choice for
567 all prompts
567 all prompts
568 -q --quiet suppress output
568 -q --quiet suppress output
569 -v --verbose enable additional output
569 -v --verbose enable additional output
570 --config CONFIG [+] set/override config option (use 'section.name=value')
570 --config CONFIG [+] set/override config option (use 'section.name=value')
571 --debug enable debugging output
571 --debug enable debugging output
572 --debugger start debugger
572 --debugger start debugger
573 --encoding ENCODE set the charset encoding (default: ascii)
573 --encoding ENCODE set the charset encoding (default: ascii)
574 --encodingmode MODE set the charset encoding mode (default: strict)
574 --encodingmode MODE set the charset encoding mode (default: strict)
575 --traceback always print a traceback on exception
575 --traceback always print a traceback on exception
576 --time time how long the command takes
576 --time time how long the command takes
577 --profile print command execution profile
577 --profile print command execution profile
578 --version output version information and exit
578 --version output version information and exit
579 -h --help display help and exit
579 -h --help display help and exit
580 --hidden consider hidden changesets
580 --hidden consider hidden changesets
581
581
582
582
583
583
584
584
585
585
586 $ echo 'debugextension = !' >> $HGRCPATH
586 $ echo 'debugextension = !' >> $HGRCPATH
587
587
588 Asking for help about a deprecated extension should do something useful:
588 Asking for help about a deprecated extension should do something useful:
589
589
590 $ hg help glog
590 $ hg help glog
591 'glog' is provided by the following extension:
591 'glog' is provided by the following extension:
592
592
593 graphlog command to view revision graphs from a shell (DEPRECATED)
593 graphlog command to view revision graphs from a shell (DEPRECATED)
594
594
595 (use 'hg help extensions' for information on enabling extensions)
595 (use 'hg help extensions' for information on enabling extensions)
596
596
597 Extension module help vs command help:
597 Extension module help vs command help:
598
598
599 $ echo 'extdiff =' >> $HGRCPATH
599 $ echo 'extdiff =' >> $HGRCPATH
600 $ hg help extdiff
600 $ hg help extdiff
601 hg extdiff [OPT]... [FILE]...
601 hg extdiff [OPT]... [FILE]...
602
602
603 use external program to diff repository (or selected files)
603 use external program to diff repository (or selected files)
604
604
605 Show differences between revisions for the specified files, using an
605 Show differences between revisions for the specified files, using an
606 external program. The default program used is diff, with default options
606 external program. The default program used is diff, with default options
607 "-Npru".
607 "-Npru".
608
608
609 To select a different program, use the -p/--program option. The program
609 To select a different program, use the -p/--program option. The program
610 will be passed the names of two directories to compare. To pass additional
610 will be passed the names of two directories to compare. To pass additional
611 options to the program, use -o/--option. These will be passed before the
611 options to the program, use -o/--option. These will be passed before the
612 names of the directories to compare.
612 names of the directories to compare.
613
613
614 When two revision arguments are given, then changes are shown between
614 When two revision arguments are given, then changes are shown between
615 those revisions. If only one revision is specified then that revision is
615 those revisions. If only one revision is specified then that revision is
616 compared to the working directory, and, when no revisions are specified,
616 compared to the working directory, and, when no revisions are specified,
617 the working directory files are compared to its parent.
617 the working directory files are compared to its parent.
618
618
619 (use 'hg help -e extdiff' to show help for the extdiff extension)
619 (use 'hg help -e extdiff' to show help for the extdiff extension)
620
620
621 options ([+] can be repeated):
621 options ([+] can be repeated):
622
622
623 -p --program CMD comparison program to run
623 -p --program CMD comparison program to run
624 -o --option OPT [+] pass option to comparison program
624 -o --option OPT [+] pass option to comparison program
625 -r --rev REV [+] revision
625 -r --rev REV [+] revision
626 -c --change REV change made by revision
626 -c --change REV change made by revision
627 --patch compare patches for two revisions
627 --patch compare patches for two revisions
628 -I --include PATTERN [+] include names matching the given patterns
628 -I --include PATTERN [+] include names matching the given patterns
629 -X --exclude PATTERN [+] exclude names matching the given patterns
629 -X --exclude PATTERN [+] exclude names matching the given patterns
630 -S --subrepos recurse into subrepositories
630 -S --subrepos recurse into subrepositories
631
631
632 (some details hidden, use --verbose to show complete help)
632 (some details hidden, use --verbose to show complete help)
633
633
634
634
635
635
636
636
637
637
638
638
639
639
640
640
641
641
642
642
643 $ hg help --extension extdiff
643 $ hg help --extension extdiff
644 extdiff extension - command to allow external programs to compare revisions
644 extdiff extension - command to allow external programs to compare revisions
645
645
646 The extdiff Mercurial extension allows you to use external programs to compare
646 The extdiff Mercurial extension allows you to use external programs to compare
647 revisions, or revision with working directory. The external diff programs are
647 revisions, or revision with working directory. The external diff programs are
648 called with a configurable set of options and two non-option arguments: paths
648 called with a configurable set of options and two non-option arguments: paths
649 to directories containing snapshots of files to compare.
649 to directories containing snapshots of files to compare.
650
650
651 The extdiff extension also allows you to configure new diff commands, so you
651 The extdiff extension also allows you to configure new diff commands, so you
652 do not need to type 'hg extdiff -p kdiff3' always.
652 do not need to type 'hg extdiff -p kdiff3' always.
653
653
654 [extdiff]
654 [extdiff]
655 # add new command that runs GNU diff(1) in 'context diff' mode
655 # add new command that runs GNU diff(1) in 'context diff' mode
656 cdiff = gdiff -Nprc5
656 cdiff = gdiff -Nprc5
657 ## or the old way:
657 ## or the old way:
658 #cmd.cdiff = gdiff
658 #cmd.cdiff = gdiff
659 #opts.cdiff = -Nprc5
659 #opts.cdiff = -Nprc5
660
660
661 # add new command called meld, runs meld (no need to name twice). If
661 # add new command called meld, runs meld (no need to name twice). If
662 # the meld executable is not available, the meld tool in [merge-tools]
662 # the meld executable is not available, the meld tool in [merge-tools]
663 # will be used, if available
663 # will be used, if available
664 meld =
664 meld =
665
665
666 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
666 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
667 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
667 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
668 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
668 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
669 # your .vimrc
669 # your .vimrc
670 vimdiff = gvim -f "+next" \
670 vimdiff = gvim -f "+next" \
671 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
671 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
672
672
673 Tool arguments can include variables that are expanded at runtime:
673 Tool arguments can include variables that are expanded at runtime:
674
674
675 $parent1, $plabel1 - filename, descriptive label of first parent
675 $parent1, $plabel1 - filename, descriptive label of first parent
676 $child, $clabel - filename, descriptive label of child revision
676 $child, $clabel - filename, descriptive label of child revision
677 $parent2, $plabel2 - filename, descriptive label of second parent
677 $parent2, $plabel2 - filename, descriptive label of second parent
678 $root - repository root
678 $root - repository root
679 $parent is an alias for $parent1.
679 $parent is an alias for $parent1.
680
680
681 The extdiff extension will look in your [diff-tools] and [merge-tools]
681 The extdiff extension will look in your [diff-tools] and [merge-tools]
682 sections for diff tool arguments, when none are specified in [extdiff].
682 sections for diff tool arguments, when none are specified in [extdiff].
683
683
684 [extdiff]
684 [extdiff]
685 kdiff3 =
685 kdiff3 =
686
686
687 [diff-tools]
687 [diff-tools]
688 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
688 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
689
689
690 You can use -I/-X and list of file or directory names like normal 'hg diff'
690 You can use -I/-X and list of file or directory names like normal 'hg diff'
691 command. The extdiff extension makes snapshots of only needed files, so
691 command. The extdiff extension makes snapshots of only needed files, so
692 running the external diff program will actually be pretty fast (at least
692 running the external diff program will actually be pretty fast (at least
693 faster than having to compare the entire tree).
693 faster than having to compare the entire tree).
694
694
695 list of commands:
695 list of commands:
696
696
697 extdiff use external program to diff repository (or selected files)
697 extdiff use external program to diff repository (or selected files)
698
698
699 (use 'hg help -v -e extdiff' to show built-in aliases and global options)
699 (use 'hg help -v -e extdiff' to show built-in aliases and global options)
700
700
701
701
702
702
703
703
704
704
705
705
706
706
707
707
708
708
709
709
710
710
711
711
712
712
713
713
714
714
715
715
716 $ echo 'extdiff = !' >> $HGRCPATH
716 $ echo 'extdiff = !' >> $HGRCPATH
717
717
718 Test help topic with same name as extension
718 Test help topic with same name as extension
719
719
720 $ cat > multirevs.py <<EOF
720 $ cat > multirevs.py <<EOF
721 > from mercurial import cmdutil, commands
721 > from mercurial import cmdutil, commands
722 > cmdtable = {}
722 > cmdtable = {}
723 > command = cmdutil.command(cmdtable)
723 > command = cmdutil.command(cmdtable)
724 > """multirevs extension
724 > """multirevs extension
725 > Big multi-line module docstring."""
725 > Big multi-line module docstring."""
726 > @command('multirevs', [], 'ARG', norepo=True)
726 > @command('multirevs', [], 'ARG', norepo=True)
727 > def multirevs(ui, repo, arg, *args, **opts):
727 > def multirevs(ui, repo, arg, *args, **opts):
728 > """multirevs command"""
728 > """multirevs command"""
729 > pass
729 > pass
730 > EOF
730 > EOF
731 $ echo "multirevs = multirevs.py" >> $HGRCPATH
731 $ echo "multirevs = multirevs.py" >> $HGRCPATH
732
732
733 $ hg help multirevs
733 $ hg help multirevs
734 Specifying Multiple Revisions
734 Specifying Multiple Revisions
735 """""""""""""""""""""""""""""
735 """""""""""""""""""""""""""""
736
736
737 When Mercurial accepts more than one revision, they may be specified
737 When Mercurial accepts more than one revision, they may be specified
738 individually, or provided as a topologically continuous range, separated
738 individually, or provided as a topologically continuous range, separated
739 by the ":" character.
739 by the ":" character.
740
740
741 The syntax of range notation is [BEGIN]:[END], where BEGIN and END are
741 The syntax of range notation is [BEGIN]:[END], where BEGIN and END are
742 revision identifiers. Both BEGIN and END are optional. If BEGIN is not
742 revision identifiers. Both BEGIN and END are optional. If BEGIN is not
743 specified, it defaults to revision number 0. If END is not specified, it
743 specified, it defaults to revision number 0. If END is not specified, it
744 defaults to the tip. The range ":" thus means "all revisions".
744 defaults to the tip. The range ":" thus means "all revisions".
745
745
746 If BEGIN is greater than END, revisions are treated in reverse order.
746 If BEGIN is greater than END, revisions are treated in reverse order.
747
747
748 A range acts as a closed interval. This means that a range of 3:5 gives 3,
748 A range acts as a closed interval. This means that a range of 3:5 gives 3,
749 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6.
749 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6.
750
750
751 use 'hg help -c multirevs' to see help for the multirevs command
751 use 'hg help -c multirevs' to see help for the multirevs command
752
752
753
753
754
754
755
755
756
756
757
757
758 $ hg help -c multirevs
758 $ hg help -c multirevs
759 hg multirevs ARG
759 hg multirevs ARG
760
760
761 multirevs command
761 multirevs command
762
762
763 (some details hidden, use --verbose to show complete help)
763 (some details hidden, use --verbose to show complete help)
764
764
765
765
766
766
767 $ hg multirevs
767 $ hg multirevs
768 hg multirevs: invalid arguments
768 hg multirevs: invalid arguments
769 hg multirevs ARG
769 hg multirevs ARG
770
770
771 multirevs command
771 multirevs command
772
772
773 (use 'hg multirevs -h' to show more help)
773 (use 'hg multirevs -h' to show more help)
774 [255]
774 [255]
775
775
776
776
777
777
778 $ echo "multirevs = !" >> $HGRCPATH
778 $ echo "multirevs = !" >> $HGRCPATH
779
779
780 Issue811: Problem loading extensions twice (by site and by user)
780 Issue811: Problem loading extensions twice (by site and by user)
781
781
782 $ cat <<EOF >> $HGRCPATH
782 $ cat <<EOF >> $HGRCPATH
783 > mq =
783 > mq =
784 > strip =
784 > strip =
785 > hgext.mq =
785 > hgext.mq =
786 > hgext/mq =
786 > hgext/mq =
787 > EOF
787 > EOF
788
788
789 Show extensions:
789 Show extensions:
790 (note that mq force load strip, also checking it's not loaded twice)
790 (note that mq force load strip, also checking it's not loaded twice)
791
791
792 $ hg debugextensions
792 $ hg debugextensions
793 mq
793 mq
794 strip
794 strip
795
795
796 For extensions, which name matches one of its commands, help
796 For extensions, which name matches one of its commands, help
797 message should ask '-v -e' to get list of built-in aliases
797 message should ask '-v -e' to get list of built-in aliases
798 along with extension help itself
798 along with extension help itself
799
799
800 $ mkdir $TESTTMP/d
800 $ mkdir $TESTTMP/d
801 $ cat > $TESTTMP/d/dodo.py <<EOF
801 $ cat > $TESTTMP/d/dodo.py <<EOF
802 > """
802 > """
803 > This is an awesome 'dodo' extension. It does nothing and
803 > This is an awesome 'dodo' extension. It does nothing and
804 > writes 'Foo foo'
804 > writes 'Foo foo'
805 > """
805 > """
806 > from mercurial import cmdutil, commands
806 > from mercurial import cmdutil, commands
807 > cmdtable = {}
807 > cmdtable = {}
808 > command = cmdutil.command(cmdtable)
808 > command = cmdutil.command(cmdtable)
809 > @command('dodo', [], 'hg dodo')
809 > @command('dodo', [], 'hg dodo')
810 > def dodo(ui, *args, **kwargs):
810 > def dodo(ui, *args, **kwargs):
811 > """Does nothing"""
811 > """Does nothing"""
812 > ui.write("I do nothing. Yay\\n")
812 > ui.write("I do nothing. Yay\\n")
813 > @command('foofoo', [], 'hg foofoo')
813 > @command('foofoo', [], 'hg foofoo')
814 > def foofoo(ui, *args, **kwargs):
814 > def foofoo(ui, *args, **kwargs):
815 > """Writes 'Foo foo'"""
815 > """Writes 'Foo foo'"""
816 > ui.write("Foo foo\\n")
816 > ui.write("Foo foo\\n")
817 > EOF
817 > EOF
818 $ dodopath=$TESTTMP/d/dodo.py
818 $ dodopath=$TESTTMP/d/dodo.py
819
819
820 $ echo "dodo = $dodopath" >> $HGRCPATH
820 $ echo "dodo = $dodopath" >> $HGRCPATH
821
821
822 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
822 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
823 $ hg help -e dodo
823 $ hg help -e dodo
824 dodo extension -
824 dodo extension -
825
825
826 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
826 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
827
827
828 list of commands:
828 list of commands:
829
829
830 dodo Does nothing
830 dodo Does nothing
831 foofoo Writes 'Foo foo'
831 foofoo Writes 'Foo foo'
832
832
833 (use 'hg help -v -e dodo' to show built-in aliases and global options)
833 (use 'hg help -v -e dodo' to show built-in aliases and global options)
834
834
835 Make sure that '-v -e' prints list of built-in aliases along with
835 Make sure that '-v -e' prints list of built-in aliases along with
836 extension help itself
836 extension help itself
837 $ hg help -v -e dodo
837 $ hg help -v -e dodo
838 dodo extension -
838 dodo extension -
839
839
840 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
840 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
841
841
842 list of commands:
842 list of commands:
843
843
844 dodo Does nothing
844 dodo Does nothing
845 foofoo Writes 'Foo foo'
845 foofoo Writes 'Foo foo'
846
846
847 global options ([+] can be repeated):
847 global options ([+] can be repeated):
848
848
849 -R --repository REPO repository root directory or name of overlay bundle
849 -R --repository REPO repository root directory or name of overlay bundle
850 file
850 file
851 --cwd DIR change working directory
851 --cwd DIR change working directory
852 -y --noninteractive do not prompt, automatically pick the first choice for
852 -y --noninteractive do not prompt, automatically pick the first choice for
853 all prompts
853 all prompts
854 -q --quiet suppress output
854 -q --quiet suppress output
855 -v --verbose enable additional output
855 -v --verbose enable additional output
856 --config CONFIG [+] set/override config option (use 'section.name=value')
856 --config CONFIG [+] set/override config option (use 'section.name=value')
857 --debug enable debugging output
857 --debug enable debugging output
858 --debugger start debugger
858 --debugger start debugger
859 --encoding ENCODE set the charset encoding (default: ascii)
859 --encoding ENCODE set the charset encoding (default: ascii)
860 --encodingmode MODE set the charset encoding mode (default: strict)
860 --encodingmode MODE set the charset encoding mode (default: strict)
861 --traceback always print a traceback on exception
861 --traceback always print a traceback on exception
862 --time time how long the command takes
862 --time time how long the command takes
863 --profile print command execution profile
863 --profile print command execution profile
864 --version output version information and exit
864 --version output version information and exit
865 -h --help display help and exit
865 -h --help display help and exit
866 --hidden consider hidden changesets
866 --hidden consider hidden changesets
867
867
868 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
868 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
869 $ hg help -v dodo
869 $ hg help -v dodo
870 hg dodo
870 hg dodo
871
871
872 Does nothing
872 Does nothing
873
873
874 (use 'hg help -e dodo' to show help for the dodo extension)
874 (use 'hg help -e dodo' to show help for the dodo extension)
875
875
876 options:
876 options:
877
877
878 --mq operate on patch repository
878 --mq operate on patch repository
879
879
880 global options ([+] can be repeated):
880 global options ([+] can be repeated):
881
881
882 -R --repository REPO repository root directory or name of overlay bundle
882 -R --repository REPO repository root directory or name of overlay bundle
883 file
883 file
884 --cwd DIR change working directory
884 --cwd DIR change working directory
885 -y --noninteractive do not prompt, automatically pick the first choice for
885 -y --noninteractive do not prompt, automatically pick the first choice for
886 all prompts
886 all prompts
887 -q --quiet suppress output
887 -q --quiet suppress output
888 -v --verbose enable additional output
888 -v --verbose enable additional output
889 --config CONFIG [+] set/override config option (use 'section.name=value')
889 --config CONFIG [+] set/override config option (use 'section.name=value')
890 --debug enable debugging output
890 --debug enable debugging output
891 --debugger start debugger
891 --debugger start debugger
892 --encoding ENCODE set the charset encoding (default: ascii)
892 --encoding ENCODE set the charset encoding (default: ascii)
893 --encodingmode MODE set the charset encoding mode (default: strict)
893 --encodingmode MODE set the charset encoding mode (default: strict)
894 --traceback always print a traceback on exception
894 --traceback always print a traceback on exception
895 --time time how long the command takes
895 --time time how long the command takes
896 --profile print command execution profile
896 --profile print command execution profile
897 --version output version information and exit
897 --version output version information and exit
898 -h --help display help and exit
898 -h --help display help and exit
899 --hidden consider hidden changesets
899 --hidden consider hidden changesets
900
900
901 In case when extension name doesn't match any of its commands,
901 In case when extension name doesn't match any of its commands,
902 help message should ask for '-v' to get list of built-in aliases
902 help message should ask for '-v' to get list of built-in aliases
903 along with extension help
903 along with extension help
904 $ cat > $TESTTMP/d/dudu.py <<EOF
904 $ cat > $TESTTMP/d/dudu.py <<EOF
905 > """
905 > """
906 > This is an awesome 'dudu' extension. It does something and
906 > This is an awesome 'dudu' extension. It does something and
907 > also writes 'Beep beep'
907 > also writes 'Beep beep'
908 > """
908 > """
909 > from mercurial import cmdutil, commands
909 > from mercurial import cmdutil, commands
910 > cmdtable = {}
910 > cmdtable = {}
911 > command = cmdutil.command(cmdtable)
911 > command = cmdutil.command(cmdtable)
912 > @command('something', [], 'hg something')
912 > @command('something', [], 'hg something')
913 > def something(ui, *args, **kwargs):
913 > def something(ui, *args, **kwargs):
914 > """Does something"""
914 > """Does something"""
915 > ui.write("I do something. Yaaay\\n")
915 > ui.write("I do something. Yaaay\\n")
916 > @command('beep', [], 'hg beep')
916 > @command('beep', [], 'hg beep')
917 > def beep(ui, *args, **kwargs):
917 > def beep(ui, *args, **kwargs):
918 > """Writes 'Beep beep'"""
918 > """Writes 'Beep beep'"""
919 > ui.write("Beep beep\\n")
919 > ui.write("Beep beep\\n")
920 > EOF
920 > EOF
921 $ dudupath=$TESTTMP/d/dudu.py
921 $ dudupath=$TESTTMP/d/dudu.py
922
922
923 $ echo "dudu = $dudupath" >> $HGRCPATH
923 $ echo "dudu = $dudupath" >> $HGRCPATH
924
924
925 $ hg help -e dudu
925 $ hg help -e dudu
926 dudu extension -
926 dudu extension -
927
927
928 This is an awesome 'dudu' extension. It does something and also writes 'Beep
928 This is an awesome 'dudu' extension. It does something and also writes 'Beep
929 beep'
929 beep'
930
930
931 list of commands:
931 list of commands:
932
932
933 beep Writes 'Beep beep'
933 beep Writes 'Beep beep'
934 something Does something
934 something Does something
935
935
936 (use 'hg help -v dudu' to show built-in aliases and global options)
936 (use 'hg help -v dudu' to show built-in aliases and global options)
937
937
938 In case when extension name doesn't match any of its commands,
938 In case when extension name doesn't match any of its commands,
939 help options '-v' and '-v -e' should be equivalent
939 help options '-v' and '-v -e' should be equivalent
940 $ hg help -v dudu
940 $ hg help -v dudu
941 dudu extension -
941 dudu extension -
942
942
943 This is an awesome 'dudu' extension. It does something and also writes 'Beep
943 This is an awesome 'dudu' extension. It does something and also writes 'Beep
944 beep'
944 beep'
945
945
946 list of commands:
946 list of commands:
947
947
948 beep Writes 'Beep beep'
948 beep Writes 'Beep beep'
949 something Does something
949 something Does something
950
950
951 global options ([+] can be repeated):
951 global options ([+] can be repeated):
952
952
953 -R --repository REPO repository root directory or name of overlay bundle
953 -R --repository REPO repository root directory or name of overlay bundle
954 file
954 file
955 --cwd DIR change working directory
955 --cwd DIR change working directory
956 -y --noninteractive do not prompt, automatically pick the first choice for
956 -y --noninteractive do not prompt, automatically pick the first choice for
957 all prompts
957 all prompts
958 -q --quiet suppress output
958 -q --quiet suppress output
959 -v --verbose enable additional output
959 -v --verbose enable additional output
960 --config CONFIG [+] set/override config option (use 'section.name=value')
960 --config CONFIG [+] set/override config option (use 'section.name=value')
961 --debug enable debugging output
961 --debug enable debugging output
962 --debugger start debugger
962 --debugger start debugger
963 --encoding ENCODE set the charset encoding (default: ascii)
963 --encoding ENCODE set the charset encoding (default: ascii)
964 --encodingmode MODE set the charset encoding mode (default: strict)
964 --encodingmode MODE set the charset encoding mode (default: strict)
965 --traceback always print a traceback on exception
965 --traceback always print a traceback on exception
966 --time time how long the command takes
966 --time time how long the command takes
967 --profile print command execution profile
967 --profile print command execution profile
968 --version output version information and exit
968 --version output version information and exit
969 -h --help display help and exit
969 -h --help display help and exit
970 --hidden consider hidden changesets
970 --hidden consider hidden changesets
971
971
972 $ hg help -v -e dudu
972 $ hg help -v -e dudu
973 dudu extension -
973 dudu extension -
974
974
975 This is an awesome 'dudu' extension. It does something and also writes 'Beep
975 This is an awesome 'dudu' extension. It does something and also writes 'Beep
976 beep'
976 beep'
977
977
978 list of commands:
978 list of commands:
979
979
980 beep Writes 'Beep beep'
980 beep Writes 'Beep beep'
981 something Does something
981 something Does something
982
982
983 global options ([+] can be repeated):
983 global options ([+] can be repeated):
984
984
985 -R --repository REPO repository root directory or name of overlay bundle
985 -R --repository REPO repository root directory or name of overlay bundle
986 file
986 file
987 --cwd DIR change working directory
987 --cwd DIR change working directory
988 -y --noninteractive do not prompt, automatically pick the first choice for
988 -y --noninteractive do not prompt, automatically pick the first choice for
989 all prompts
989 all prompts
990 -q --quiet suppress output
990 -q --quiet suppress output
991 -v --verbose enable additional output
991 -v --verbose enable additional output
992 --config CONFIG [+] set/override config option (use 'section.name=value')
992 --config CONFIG [+] set/override config option (use 'section.name=value')
993 --debug enable debugging output
993 --debug enable debugging output
994 --debugger start debugger
994 --debugger start debugger
995 --encoding ENCODE set the charset encoding (default: ascii)
995 --encoding ENCODE set the charset encoding (default: ascii)
996 --encodingmode MODE set the charset encoding mode (default: strict)
996 --encodingmode MODE set the charset encoding mode (default: strict)
997 --traceback always print a traceback on exception
997 --traceback always print a traceback on exception
998 --time time how long the command takes
998 --time time how long the command takes
999 --profile print command execution profile
999 --profile print command execution profile
1000 --version output version information and exit
1000 --version output version information and exit
1001 -h --help display help and exit
1001 -h --help display help and exit
1002 --hidden consider hidden changesets
1002 --hidden consider hidden changesets
1003
1003
1004 Disabled extension commands:
1004 Disabled extension commands:
1005
1005
1006 $ ORGHGRCPATH=$HGRCPATH
1006 $ ORGHGRCPATH=$HGRCPATH
1007 $ HGRCPATH=
1007 $ HGRCPATH=
1008 $ export HGRCPATH
1008 $ export HGRCPATH
1009 $ hg help email
1009 $ hg help email
1010 'email' is provided by the following extension:
1010 'email' is provided by the following extension:
1011
1011
1012 patchbomb command to send changesets as (a series of) patch emails
1012 patchbomb command to send changesets as (a series of) patch emails
1013
1013
1014 (use 'hg help extensions' for information on enabling extensions)
1014 (use 'hg help extensions' for information on enabling extensions)
1015
1015
1016
1016
1017 $ hg qdel
1017 $ hg qdel
1018 hg: unknown command 'qdel'
1018 hg: unknown command 'qdel'
1019 'qdelete' is provided by the following extension:
1019 'qdelete' is provided by the following extension:
1020
1020
1021 mq manage a stack of patches
1021 mq manage a stack of patches
1022
1022
1023 (use 'hg help extensions' for information on enabling extensions)
1023 (use 'hg help extensions' for information on enabling extensions)
1024 [255]
1024 [255]
1025
1025
1026
1026
1027 $ hg churn
1027 $ hg churn
1028 hg: unknown command 'churn'
1028 hg: unknown command 'churn'
1029 'churn' is provided by the following extension:
1029 'churn' is provided by the following extension:
1030
1030
1031 churn command to display statistics about repository history
1031 churn command to display statistics about repository history
1032
1032
1033 (use 'hg help extensions' for information on enabling extensions)
1033 (use 'hg help extensions' for information on enabling extensions)
1034 [255]
1034 [255]
1035
1035
1036
1036
1037
1037
1038 Disabled extensions:
1038 Disabled extensions:
1039
1039
1040 $ hg help churn
1040 $ hg help churn
1041 churn extension - command to display statistics about repository history
1041 churn extension - command to display statistics about repository history
1042
1042
1043 (use 'hg help extensions' for information on enabling extensions)
1043 (use 'hg help extensions' for information on enabling extensions)
1044
1044
1045 $ hg help patchbomb
1045 $ hg help patchbomb
1046 patchbomb extension - command to send changesets as (a series of) patch emails
1046 patchbomb extension - command to send changesets as (a series of) patch emails
1047
1047
1048 The series is started off with a "[PATCH 0 of N]" introduction, which
1048 The series is started off with a "[PATCH 0 of N]" introduction, which
1049 describes the series as a whole.
1049 describes the series as a whole.
1050
1050
1051 Each patch email has a Subject line of "[PATCH M of N] ...", using the first
1051 Each patch email has a Subject line of "[PATCH M of N] ...", using the first
1052 line of the changeset description as the subject text. The message contains
1052 line of the changeset description as the subject text. The message contains
1053 two or three body parts:
1053 two or three body parts:
1054
1054
1055 - The changeset description.
1055 - The changeset description.
1056 - [Optional] The result of running diffstat on the patch.
1056 - [Optional] The result of running diffstat on the patch.
1057 - The patch itself, as generated by 'hg export'.
1057 - The patch itself, as generated by 'hg export'.
1058
1058
1059 Each message refers to the first in the series using the In-Reply-To and
1059 Each message refers to the first in the series using the In-Reply-To and
1060 References headers, so they will show up as a sequence in threaded mail and
1060 References headers, so they will show up as a sequence in threaded mail and
1061 news readers, and in mail archives.
1061 news readers, and in mail archives.
1062
1062
1063 To configure other defaults, add a section like this to your configuration
1063 To configure other defaults, add a section like this to your configuration
1064 file:
1064 file:
1065
1065
1066 [email]
1066 [email]
1067 from = My Name <my@email>
1067 from = My Name <my@email>
1068 to = recipient1, recipient2, ...
1068 to = recipient1, recipient2, ...
1069 cc = cc1, cc2, ...
1069 cc = cc1, cc2, ...
1070 bcc = bcc1, bcc2, ...
1070 bcc = bcc1, bcc2, ...
1071 reply-to = address1, address2, ...
1071 reply-to = address1, address2, ...
1072
1072
1073 Use "[patchbomb]" as configuration section name if you need to override global
1073 Use "[patchbomb]" as configuration section name if you need to override global
1074 "[email]" address settings.
1074 "[email]" address settings.
1075
1075
1076 Then you can use the 'hg email' command to mail a series of changesets as a
1076 Then you can use the 'hg email' command to mail a series of changesets as a
1077 patchbomb.
1077 patchbomb.
1078
1078
1079 You can also either configure the method option in the email section to be a
1079 You can also either configure the method option in the email section to be a
1080 sendmail compatible mailer or fill out the [smtp] section so that the
1080 sendmail compatible mailer or fill out the [smtp] section so that the
1081 patchbomb extension can automatically send patchbombs directly from the
1081 patchbomb extension can automatically send patchbombs directly from the
1082 commandline. See the [email] and [smtp] sections in hgrc(5) for details.
1082 commandline. See the [email] and [smtp] sections in hgrc(5) for details.
1083
1083
1084 By default, 'hg email' will prompt for a "To" or "CC" header if you do not
1084 By default, 'hg email' will prompt for a "To" or "CC" header if you do not
1085 supply one via configuration or the command line. You can override this to
1085 supply one via configuration or the command line. You can override this to
1086 never prompt by configuring an empty value:
1086 never prompt by configuring an empty value:
1087
1087
1088 [email]
1088 [email]
1089 cc =
1089 cc =
1090
1090
1091 You can control the default inclusion of an introduction message with the
1091 You can control the default inclusion of an introduction message with the
1092 "patchbomb.intro" configuration option. The configuration is always
1092 "patchbomb.intro" configuration option. The configuration is always
1093 overwritten by command line flags like --intro and --desc:
1093 overwritten by command line flags like --intro and --desc:
1094
1094
1095 [patchbomb]
1095 [patchbomb]
1096 intro=auto # include introduction message if more than 1 patch (default)
1096 intro=auto # include introduction message if more than 1 patch (default)
1097 intro=never # never include an introduction message
1097 intro=never # never include an introduction message
1098 intro=always # always include an introduction message
1098 intro=always # always include an introduction message
1099
1099
1100 You can set patchbomb to always ask for confirmation by setting
1100 You can set patchbomb to always ask for confirmation by setting
1101 "patchbomb.confirm" to true.
1101 "patchbomb.confirm" to true.
1102
1102
1103 (use 'hg help extensions' for information on enabling extensions)
1103 (use 'hg help extensions' for information on enabling extensions)
1104
1104
1105
1105
1106 Broken disabled extension and command:
1106 Broken disabled extension and command:
1107
1107
1108 $ mkdir hgext
1108 $ mkdir hgext
1109 $ echo > hgext/__init__.py
1109 $ echo > hgext/__init__.py
1110 $ cat > hgext/broken.py <<EOF
1110 $ cat > hgext/broken.py <<EOF
1111 > "broken extension'
1111 > "broken extension'
1112 > EOF
1112 > EOF
1113 $ cat > path.py <<EOF
1113 $ cat > path.py <<EOF
1114 > import os, sys
1114 > import os, sys
1115 > sys.path.insert(0, os.environ['HGEXTPATH'])
1115 > sys.path.insert(0, os.environ['HGEXTPATH'])
1116 > EOF
1116 > EOF
1117 $ HGEXTPATH=`pwd`
1117 $ HGEXTPATH=`pwd`
1118 $ export HGEXTPATH
1118 $ export HGEXTPATH
1119
1119
1120 $ hg --config extensions.path=./path.py help broken
1120 $ hg --config extensions.path=./path.py help broken
1121 broken extension - (no help text available)
1121 broken extension - (no help text available)
1122
1122
1123 (use 'hg help extensions' for information on enabling extensions)
1123 (use 'hg help extensions' for information on enabling extensions)
1124
1124
1125
1125
1126 $ cat > hgext/forest.py <<EOF
1126 $ cat > hgext/forest.py <<EOF
1127 > cmdtable = None
1127 > cmdtable = None
1128 > EOF
1128 > EOF
1129 $ hg --config extensions.path=./path.py help foo > /dev/null
1129 $ hg --config extensions.path=./path.py help foo > /dev/null
1130 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
1130 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
1131 abort: no such help topic: foo
1131 abort: no such help topic: foo
1132 (try 'hg help --keyword foo')
1132 (try 'hg help --keyword foo')
1133 [255]
1133 [255]
1134
1134
1135 $ cat > throw.py <<EOF
1135 $ cat > throw.py <<EOF
1136 > from mercurial import cmdutil, commands, util
1136 > from mercurial import cmdutil, commands, util
1137 > cmdtable = {}
1137 > cmdtable = {}
1138 > command = cmdutil.command(cmdtable)
1138 > command = cmdutil.command(cmdtable)
1139 > class Bogon(Exception): pass
1139 > class Bogon(Exception): pass
1140 > @command('throw', [], 'hg throw', norepo=True)
1140 > @command('throw', [], 'hg throw', norepo=True)
1141 > def throw(ui, **opts):
1141 > def throw(ui, **opts):
1142 > """throws an exception"""
1142 > """throws an exception"""
1143 > raise Bogon()
1143 > raise Bogon()
1144 > EOF
1144 > EOF
1145
1145
1146 No declared supported version, extension complains:
1146 No declared supported version, extension complains:
1147 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1147 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1148 ** Unknown exception encountered with possibly-broken third-party extension throw
1148 ** Unknown exception encountered with possibly-broken third-party extension throw
1149 ** which supports versions unknown of Mercurial.
1149 ** which supports versions unknown of Mercurial.
1150 ** Please disable throw and try your action again.
1150 ** Please disable throw and try your action again.
1151 ** If that fixes the bug please report it to the extension author.
1151 ** If that fixes the bug please report it to the extension author.
1152 ** Python * (glob)
1152 ** Python * (glob)
1153 ** Mercurial Distributed SCM * (glob)
1153 ** Mercurial Distributed SCM * (glob)
1154 ** Extensions loaded: throw
1154 ** Extensions loaded: throw
1155
1155
1156 empty declaration of supported version, extension complains:
1156 empty declaration of supported version, extension complains:
1157 $ echo "testedwith = ''" >> throw.py
1157 $ echo "testedwith = ''" >> throw.py
1158 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1158 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1159 ** Unknown exception encountered with possibly-broken third-party extension throw
1159 ** Unknown exception encountered with possibly-broken third-party extension throw
1160 ** which supports versions unknown of Mercurial.
1160 ** which supports versions unknown of Mercurial.
1161 ** Please disable throw and try your action again.
1161 ** Please disable throw and try your action again.
1162 ** If that fixes the bug please report it to the extension author.
1162 ** If that fixes the bug please report it to the extension author.
1163 ** Python * (glob)
1163 ** Python * (glob)
1164 ** Mercurial Distributed SCM (*) (glob)
1164 ** Mercurial Distributed SCM (*) (glob)
1165 ** Extensions loaded: throw
1165 ** Extensions loaded: throw
1166
1166
1167 If the extension specifies a buglink, show that:
1167 If the extension specifies a buglink, show that:
1168 $ echo 'buglink = "http://example.com/bts"' >> throw.py
1168 $ echo 'buglink = "http://example.com/bts"' >> throw.py
1169 $ rm -f throw.pyc throw.pyo
1169 $ rm -f throw.pyc throw.pyo
1170 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1170 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1171 ** Unknown exception encountered with possibly-broken third-party extension throw
1171 ** Unknown exception encountered with possibly-broken third-party extension throw
1172 ** which supports versions unknown of Mercurial.
1172 ** which supports versions unknown of Mercurial.
1173 ** Please disable throw and try your action again.
1173 ** Please disable throw and try your action again.
1174 ** If that fixes the bug please report it to http://example.com/bts
1174 ** If that fixes the bug please report it to http://example.com/bts
1175 ** Python * (glob)
1175 ** Python * (glob)
1176 ** Mercurial Distributed SCM (*) (glob)
1176 ** Mercurial Distributed SCM (*) (glob)
1177 ** Extensions loaded: throw
1177 ** Extensions loaded: throw
1178
1178
1179 If the extensions declare outdated versions, accuse the older extension first:
1179 If the extensions declare outdated versions, accuse the older extension first:
1180 $ echo "from mercurial import util" >> older.py
1180 $ echo "from mercurial import util" >> older.py
1181 $ echo "util.version = lambda:'2.2'" >> older.py
1181 $ echo "util.version = lambda:'2.2'" >> older.py
1182 $ echo "testedwith = '1.9.3'" >> older.py
1182 $ echo "testedwith = '1.9.3'" >> older.py
1183 $ echo "testedwith = '2.1.1'" >> throw.py
1183 $ echo "testedwith = '2.1.1'" >> throw.py
1184 $ rm -f throw.pyc throw.pyo
1184 $ rm -f throw.pyc throw.pyo
1185 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1185 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1186 > throw 2>&1 | egrep '^\*\*'
1186 > throw 2>&1 | egrep '^\*\*'
1187 ** Unknown exception encountered with possibly-broken third-party extension older
1187 ** Unknown exception encountered with possibly-broken third-party extension older
1188 ** which supports versions 1.9 of Mercurial.
1188 ** which supports versions 1.9 of Mercurial.
1189 ** Please disable older and try your action again.
1189 ** Please disable older and try your action again.
1190 ** If that fixes the bug please report it to the extension author.
1190 ** If that fixes the bug please report it to the extension author.
1191 ** Python * (glob)
1191 ** Python * (glob)
1192 ** Mercurial Distributed SCM (version 2.2)
1192 ** Mercurial Distributed SCM (version 2.2)
1193 ** Extensions loaded: throw, older
1193 ** Extensions loaded: throw, older
1194
1194
1195 One extension only tested with older, one only with newer versions:
1195 One extension only tested with older, one only with newer versions:
1196 $ echo "util.version = lambda:'2.1'" >> older.py
1196 $ echo "util.version = lambda:'2.1'" >> older.py
1197 $ rm -f older.pyc older.pyo
1197 $ rm -f older.pyc older.pyo
1198 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1198 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1199 > throw 2>&1 | egrep '^\*\*'
1199 > throw 2>&1 | egrep '^\*\*'
1200 ** Unknown exception encountered with possibly-broken third-party extension older
1200 ** Unknown exception encountered with possibly-broken third-party extension older
1201 ** which supports versions 1.9 of Mercurial.
1201 ** which supports versions 1.9 of Mercurial.
1202 ** Please disable older and try your action again.
1202 ** Please disable older and try your action again.
1203 ** If that fixes the bug please report it to the extension author.
1203 ** If that fixes the bug please report it to the extension author.
1204 ** Python * (glob)
1204 ** Python * (glob)
1205 ** Mercurial Distributed SCM (version 2.1)
1205 ** Mercurial Distributed SCM (version 2.1)
1206 ** Extensions loaded: throw, older
1206 ** Extensions loaded: throw, older
1207
1207
1208 Older extension is tested with current version, the other only with newer:
1208 Older extension is tested with current version, the other only with newer:
1209 $ echo "util.version = lambda:'1.9.3'" >> older.py
1209 $ echo "util.version = lambda:'1.9.3'" >> older.py
1210 $ rm -f older.pyc older.pyo
1210 $ rm -f older.pyc older.pyo
1211 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1211 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1212 > throw 2>&1 | egrep '^\*\*'
1212 > throw 2>&1 | egrep '^\*\*'
1213 ** Unknown exception encountered with possibly-broken third-party extension throw
1213 ** Unknown exception encountered with possibly-broken third-party extension throw
1214 ** which supports versions 2.1 of Mercurial.
1214 ** which supports versions 2.1 of Mercurial.
1215 ** Please disable throw and try your action again.
1215 ** Please disable throw and try your action again.
1216 ** If that fixes the bug please report it to http://example.com/bts
1216 ** If that fixes the bug please report it to http://example.com/bts
1217 ** Python * (glob)
1217 ** Python * (glob)
1218 ** Mercurial Distributed SCM (version 1.9.3)
1218 ** Mercurial Distributed SCM (version 1.9.3)
1219 ** Extensions loaded: throw, older
1219 ** Extensions loaded: throw, older
1220
1220
1221 Ability to point to a different point
1221 Ability to point to a different point
1222 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1222 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1223 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
1223 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
1224 ** unknown exception encountered, please report by visiting
1224 ** unknown exception encountered, please report by visiting
1225 ** Your Local Goat Lenders
1225 ** Your Local Goat Lenders
1226 ** Python * (glob)
1226 ** Python * (glob)
1227 ** Mercurial Distributed SCM (*) (glob)
1227 ** Mercurial Distributed SCM (*) (glob)
1228 ** Extensions loaded: throw, older
1228 ** Extensions loaded: throw, older
1229
1229
1230 Declare the version as supporting this hg version, show regular bts link:
1230 Declare the version as supporting this hg version, show regular bts link:
1231 $ hgver=`hg debuginstall -T '{hgver}'`
1231 $ hgver=`hg debuginstall -T '{hgver}'`
1232 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
1232 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
1233 $ if [ -z "$hgver" ]; then
1233 $ if [ -z "$hgver" ]; then
1234 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
1234 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
1235 > fi
1235 > fi
1236 $ rm -f throw.pyc throw.pyo
1236 $ rm -f throw.pyc throw.pyo
1237 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1237 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1238 ** unknown exception encountered, please report by visiting
1238 ** unknown exception encountered, please report by visiting
1239 ** https://mercurial-scm.org/wiki/BugTracker
1239 ** https://mercurial-scm.org/wiki/BugTracker
1240 ** Python * (glob)
1240 ** Python * (glob)
1241 ** Mercurial Distributed SCM (*) (glob)
1241 ** Mercurial Distributed SCM (*) (glob)
1242 ** Extensions loaded: throw
1242 ** Extensions loaded: throw
1243
1243
1244 Patch version is ignored during compatibility check
1244 Patch version is ignored during compatibility check
1245 $ echo "testedwith = '3.2'" >> throw.py
1245 $ echo "testedwith = '3.2'" >> throw.py
1246 $ echo "util.version = lambda:'3.2.2'" >> throw.py
1246 $ echo "util.version = lambda:'3.2.2'" >> throw.py
1247 $ rm -f throw.pyc throw.pyo
1247 $ rm -f throw.pyc throw.pyo
1248 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1248 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1249 ** unknown exception encountered, please report by visiting
1249 ** unknown exception encountered, please report by visiting
1250 ** https://mercurial-scm.org/wiki/BugTracker
1250 ** https://mercurial-scm.org/wiki/BugTracker
1251 ** Python * (glob)
1251 ** Python * (glob)
1252 ** Mercurial Distributed SCM (*) (glob)
1252 ** Mercurial Distributed SCM (*) (glob)
1253 ** Extensions loaded: throw
1253 ** Extensions loaded: throw
1254
1254
1255 Test version number support in 'hg version':
1255 Test version number support in 'hg version':
1256 $ echo '__version__ = (1, 2, 3)' >> throw.py
1256 $ echo '__version__ = (1, 2, 3)' >> throw.py
1257 $ rm -f throw.pyc throw.pyo
1257 $ rm -f throw.pyc throw.pyo
1258 $ hg version -v
1258 $ hg version -v
1259 Mercurial Distributed SCM (version *) (glob)
1259 Mercurial Distributed SCM (version *) (glob)
1260 (see https://mercurial-scm.org for more information)
1260 (see https://mercurial-scm.org for more information)
1261
1261
1262 Copyright (C) 2005-* Matt Mackall and others (glob)
1262 Copyright (C) 2005-* Matt Mackall and others (glob)
1263 This is free software; see the source for copying conditions. There is NO
1263 This is free software; see the source for copying conditions. There is NO
1264 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1264 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1265
1265
1266 Enabled extensions:
1266 Enabled extensions:
1267
1267
1268
1268
1269 $ hg version -v --config extensions.throw=throw.py
1269 $ hg version -v --config extensions.throw=throw.py
1270 Mercurial Distributed SCM (version *) (glob)
1270 Mercurial Distributed SCM (version *) (glob)
1271 (see https://mercurial-scm.org for more information)
1271 (see https://mercurial-scm.org for more information)
1272
1272
1273 Copyright (C) 2005-* Matt Mackall and others (glob)
1273 Copyright (C) 2005-* Matt Mackall and others (glob)
1274 This is free software; see the source for copying conditions. There is NO
1274 This is free software; see the source for copying conditions. There is NO
1275 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1275 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1276
1276
1277 Enabled extensions:
1277 Enabled extensions:
1278
1278
1279 throw external 1.2.3
1279 throw external 1.2.3
1280 $ echo 'getversion = lambda: "1.twentythree"' >> throw.py
1280 $ echo 'getversion = lambda: "1.twentythree"' >> throw.py
1281 $ rm -f throw.pyc throw.pyo
1281 $ rm -f throw.pyc throw.pyo
1282 $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
1282 $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
1283 Mercurial Distributed SCM (version *) (glob)
1283 Mercurial Distributed SCM (version *) (glob)
1284 (see https://mercurial-scm.org for more information)
1284 (see https://mercurial-scm.org for more information)
1285
1285
1286 Copyright (C) 2005-* Matt Mackall and others (glob)
1286 Copyright (C) 2005-* Matt Mackall and others (glob)
1287 This is free software; see the source for copying conditions. There is NO
1287 This is free software; see the source for copying conditions. There is NO
1288 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1288 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1289
1289
1290 Enabled extensions:
1290 Enabled extensions:
1291
1291
1292 throw external 1.twentythree
1292 throw external 1.twentythree
1293 strip internal
1293 strip internal
1294
1294
1295 $ hg version -q --config extensions.throw=throw.py
1295 $ hg version -q --config extensions.throw=throw.py
1296 Mercurial Distributed SCM (version *) (glob)
1296 Mercurial Distributed SCM (version *) (glob)
1297
1297
1298 Test JSON output of version:
1298 Test JSON output of version:
1299
1299
1300 $ hg version -Tjson
1300 $ hg version -Tjson
1301 [
1301 [
1302 {
1302 {
1303 "extensions": [],
1303 "extensions": [],
1304 "ver": "*" (glob)
1304 "ver": "*" (glob)
1305 }
1305 }
1306 ]
1306 ]
1307
1307
1308 $ hg version --config extensions.throw=throw.py -Tjson
1308 $ hg version --config extensions.throw=throw.py -Tjson
1309 [
1309 [
1310 {
1310 {
1311 "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
1311 "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
1312 "ver": "3.2.2"
1312 "ver": "3.2.2"
1313 }
1313 }
1314 ]
1314 ]
1315
1315
1316 $ hg version --config extensions.strip= -Tjson
1316 $ hg version --config extensions.strip= -Tjson
1317 [
1317 [
1318 {
1318 {
1319 "extensions": [{"bundled": true, "name": "strip", "ver": null}],
1319 "extensions": [{"bundled": true, "name": "strip", "ver": null}],
1320 "ver": "*" (glob)
1320 "ver": "*" (glob)
1321 }
1321 }
1322 ]
1322 ]
1323
1323
1324 Test template output of version:
1324 Test template output of version:
1325
1325
1326 $ hg version --config extensions.throw=throw.py --config extensions.strip= \
1326 $ hg version --config extensions.throw=throw.py --config extensions.strip= \
1327 > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}'
1327 > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}'
1328 throw 1.twentythree (external)
1328 throw 1.twentythree (external)
1329 strip (internal)
1329 strip (internal)
1330
1330
1331 Refuse to load extensions with minimum version requirements
1331 Refuse to load extensions with minimum version requirements
1332
1332
1333 $ cat > minversion1.py << EOF
1333 $ cat > minversion1.py << EOF
1334 > from mercurial import util
1334 > from mercurial import util
1335 > util.version = lambda: '3.5.2'
1335 > util.version = lambda: '3.5.2'
1336 > minimumhgversion = '3.6'
1336 > minimumhgversion = '3.6'
1337 > EOF
1337 > EOF
1338 $ hg --config extensions.minversion=minversion1.py version
1338 $ hg --config extensions.minversion=minversion1.py version
1339 (third party extension minversion requires version 3.6 or newer of Mercurial; disabling)
1339 (third party extension minversion requires version 3.6 or newer of Mercurial; disabling)
1340 Mercurial Distributed SCM (version 3.5.2)
1340 Mercurial Distributed SCM (version 3.5.2)
1341 (see https://mercurial-scm.org for more information)
1341 (see https://mercurial-scm.org for more information)
1342
1342
1343 Copyright (C) 2005-* Matt Mackall and others (glob)
1343 Copyright (C) 2005-* Matt Mackall and others (glob)
1344 This is free software; see the source for copying conditions. There is NO
1344 This is free software; see the source for copying conditions. There is NO
1345 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1345 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1346
1346
1347 $ cat > minversion2.py << EOF
1347 $ cat > minversion2.py << EOF
1348 > from mercurial import util
1348 > from mercurial import util
1349 > util.version = lambda: '3.6'
1349 > util.version = lambda: '3.6'
1350 > minimumhgversion = '3.7'
1350 > minimumhgversion = '3.7'
1351 > EOF
1351 > EOF
1352 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1352 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1353 (third party extension minversion requires version 3.7 or newer of Mercurial; disabling)
1353 (third party extension minversion requires version 3.7 or newer of Mercurial; disabling)
1354
1354
1355 Can load version that is only off by point release
1355 Can load version that is only off by point release
1356
1356
1357 $ cat > minversion2.py << EOF
1357 $ cat > minversion2.py << EOF
1358 > from mercurial import util
1358 > from mercurial import util
1359 > util.version = lambda: '3.6.1'
1359 > util.version = lambda: '3.6.1'
1360 > minimumhgversion = '3.6'
1360 > minimumhgversion = '3.6'
1361 > EOF
1361 > EOF
1362 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1362 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1363 [1]
1363 [1]
1364
1364
1365 Can load minimum version identical to current
1365 Can load minimum version identical to current
1366
1366
1367 $ cat > minversion3.py << EOF
1367 $ cat > minversion3.py << EOF
1368 > from mercurial import util
1368 > from mercurial import util
1369 > util.version = lambda: '3.5'
1369 > util.version = lambda: '3.5'
1370 > minimumhgversion = '3.5'
1370 > minimumhgversion = '3.5'
1371 > EOF
1371 > EOF
1372 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1372 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1373 [1]
1373 [1]
1374
1374
1375 Restore HGRCPATH
1375 Restore HGRCPATH
1376
1376
1377 $ HGRCPATH=$ORGHGRCPATH
1377 $ HGRCPATH=$ORGHGRCPATH
1378 $ export HGRCPATH
1378 $ export HGRCPATH
1379
1379
1380 Commands handling multiple repositories at a time should invoke only
1380 Commands handling multiple repositories at a time should invoke only
1381 "reposetup()" of extensions enabling in the target repository.
1381 "reposetup()" of extensions enabling in the target repository.
1382
1382
1383 $ mkdir reposetup-test
1383 $ mkdir reposetup-test
1384 $ cd reposetup-test
1384 $ cd reposetup-test
1385
1385
1386 $ cat > $TESTTMP/reposetuptest.py <<EOF
1386 $ cat > $TESTTMP/reposetuptest.py <<EOF
1387 > from mercurial import extensions
1387 > from mercurial import extensions
1388 > def reposetup(ui, repo):
1388 > def reposetup(ui, repo):
1389 > ui.write('reposetup() for %s\n' % (repo.root))
1389 > ui.write('reposetup() for %s\n' % (repo.root))
1390 > ui.flush()
1390 > ui.flush()
1391 > EOF
1391 > EOF
1392 $ hg init src
1392 $ hg init src
1393 $ echo a > src/a
1393 $ echo a > src/a
1394 $ hg -R src commit -Am '#0 at src/a'
1394 $ hg -R src commit -Am '#0 at src/a'
1395 adding a
1395 adding a
1396 $ echo '[extensions]' >> src/.hg/hgrc
1396 $ echo '[extensions]' >> src/.hg/hgrc
1397 $ echo '# enable extension locally' >> src/.hg/hgrc
1397 $ echo '# enable extension locally' >> src/.hg/hgrc
1398 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1398 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1399 $ hg -R src status
1399 $ hg -R src status
1400 reposetup() for $TESTTMP/reposetup-test/src (glob)
1400 reposetup() for $TESTTMP/reposetup-test/src (glob)
1401
1401
1402 $ hg clone -U src clone-dst1
1402 $ hg clone -U src clone-dst1
1403 reposetup() for $TESTTMP/reposetup-test/src (glob)
1403 reposetup() for $TESTTMP/reposetup-test/src (glob)
1404 $ hg init push-dst1
1404 $ hg init push-dst1
1405 $ hg -q -R src push push-dst1
1405 $ hg -q -R src push push-dst1
1406 reposetup() for $TESTTMP/reposetup-test/src (glob)
1406 reposetup() for $TESTTMP/reposetup-test/src (glob)
1407 $ hg init pull-src1
1407 $ hg init pull-src1
1408 $ hg -q -R pull-src1 pull src
1408 $ hg -q -R pull-src1 pull src
1409 reposetup() for $TESTTMP/reposetup-test/src (glob)
1409 reposetup() for $TESTTMP/reposetup-test/src (glob)
1410
1410
1411 $ cat <<EOF >> $HGRCPATH
1411 $ cat <<EOF >> $HGRCPATH
1412 > [extensions]
1412 > [extensions]
1413 > # disable extension globally and explicitly
1413 > # disable extension globally and explicitly
1414 > reposetuptest = !
1414 > reposetuptest = !
1415 > EOF
1415 > EOF
1416 $ hg clone -U src clone-dst2
1416 $ hg clone -U src clone-dst2
1417 reposetup() for $TESTTMP/reposetup-test/src (glob)
1417 reposetup() for $TESTTMP/reposetup-test/src (glob)
1418 $ hg init push-dst2
1418 $ hg init push-dst2
1419 $ hg -q -R src push push-dst2
1419 $ hg -q -R src push push-dst2
1420 reposetup() for $TESTTMP/reposetup-test/src (glob)
1420 reposetup() for $TESTTMP/reposetup-test/src (glob)
1421 $ hg init pull-src2
1421 $ hg init pull-src2
1422 $ hg -q -R pull-src2 pull src
1422 $ hg -q -R pull-src2 pull src
1423 reposetup() for $TESTTMP/reposetup-test/src (glob)
1423 reposetup() for $TESTTMP/reposetup-test/src (glob)
1424
1424
1425 $ cat <<EOF >> $HGRCPATH
1425 $ cat <<EOF >> $HGRCPATH
1426 > [extensions]
1426 > [extensions]
1427 > # enable extension globally
1427 > # enable extension globally
1428 > reposetuptest = $TESTTMP/reposetuptest.py
1428 > reposetuptest = $TESTTMP/reposetuptest.py
1429 > EOF
1429 > EOF
1430 $ hg clone -U src clone-dst3
1430 $ hg clone -U src clone-dst3
1431 reposetup() for $TESTTMP/reposetup-test/src (glob)
1431 reposetup() for $TESTTMP/reposetup-test/src (glob)
1432 reposetup() for $TESTTMP/reposetup-test/clone-dst3 (glob)
1432 reposetup() for $TESTTMP/reposetup-test/clone-dst3 (glob)
1433 $ hg init push-dst3
1433 $ hg init push-dst3
1434 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1434 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1435 $ hg -q -R src push push-dst3
1435 $ hg -q -R src push push-dst3
1436 reposetup() for $TESTTMP/reposetup-test/src (glob)
1436 reposetup() for $TESTTMP/reposetup-test/src (glob)
1437 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1437 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1438 $ hg init pull-src3
1438 $ hg init pull-src3
1439 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1439 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1440 $ hg -q -R pull-src3 pull src
1440 $ hg -q -R pull-src3 pull src
1441 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1441 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1442 reposetup() for $TESTTMP/reposetup-test/src (glob)
1442 reposetup() for $TESTTMP/reposetup-test/src (glob)
1443
1443
1444 $ echo '[extensions]' >> src/.hg/hgrc
1444 $ echo '[extensions]' >> src/.hg/hgrc
1445 $ echo '# disable extension locally' >> src/.hg/hgrc
1445 $ echo '# disable extension locally' >> src/.hg/hgrc
1446 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1446 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1447 $ hg clone -U src clone-dst4
1447 $ hg clone -U src clone-dst4
1448 reposetup() for $TESTTMP/reposetup-test/clone-dst4 (glob)
1448 reposetup() for $TESTTMP/reposetup-test/clone-dst4 (glob)
1449 $ hg init push-dst4
1449 $ hg init push-dst4
1450 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1450 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1451 $ hg -q -R src push push-dst4
1451 $ hg -q -R src push push-dst4
1452 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1452 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1453 $ hg init pull-src4
1453 $ hg init pull-src4
1454 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1454 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1455 $ hg -q -R pull-src4 pull src
1455 $ hg -q -R pull-src4 pull src
1456 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1456 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1457
1457
1458 disabling in command line overlays with all configuration
1458 disabling in command line overlays with all configuration
1459 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1459 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1460 $ hg --config extensions.reposetuptest=! init push-dst5
1460 $ hg --config extensions.reposetuptest=! init push-dst5
1461 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1461 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1462 $ hg --config extensions.reposetuptest=! init pull-src5
1462 $ hg --config extensions.reposetuptest=! init pull-src5
1463 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1463 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1464
1464
1465 $ cat <<EOF >> $HGRCPATH
1465 $ cat <<EOF >> $HGRCPATH
1466 > [extensions]
1466 > [extensions]
1467 > # disable extension globally and explicitly
1467 > # disable extension globally and explicitly
1468 > reposetuptest = !
1468 > reposetuptest = !
1469 > EOF
1469 > EOF
1470 $ hg init parent
1470 $ hg init parent
1471 $ hg init parent/sub1
1471 $ hg init parent/sub1
1472 $ echo 1 > parent/sub1/1
1472 $ echo 1 > parent/sub1/1
1473 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1473 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1474 adding 1
1474 adding 1
1475 $ hg init parent/sub2
1475 $ hg init parent/sub2
1476 $ hg init parent/sub2/sub21
1476 $ hg init parent/sub2/sub21
1477 $ echo 21 > parent/sub2/sub21/21
1477 $ echo 21 > parent/sub2/sub21/21
1478 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1478 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1479 adding 21
1479 adding 21
1480 $ cat > parent/sub2/.hgsub <<EOF
1480 $ cat > parent/sub2/.hgsub <<EOF
1481 > sub21 = sub21
1481 > sub21 = sub21
1482 > EOF
1482 > EOF
1483 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1483 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1484 adding .hgsub
1484 adding .hgsub
1485 $ hg init parent/sub3
1485 $ hg init parent/sub3
1486 $ echo 3 > parent/sub3/3
1486 $ echo 3 > parent/sub3/3
1487 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1487 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1488 adding 3
1488 adding 3
1489 $ cat > parent/.hgsub <<EOF
1489 $ cat > parent/.hgsub <<EOF
1490 > sub1 = sub1
1490 > sub1 = sub1
1491 > sub2 = sub2
1491 > sub2 = sub2
1492 > sub3 = sub3
1492 > sub3 = sub3
1493 > EOF
1493 > EOF
1494 $ hg -R parent commit -Am '#0 at parent'
1494 $ hg -R parent commit -Am '#0 at parent'
1495 adding .hgsub
1495 adding .hgsub
1496 $ echo '[extensions]' >> parent/.hg/hgrc
1496 $ echo '[extensions]' >> parent/.hg/hgrc
1497 $ echo '# enable extension locally' >> parent/.hg/hgrc
1497 $ echo '# enable extension locally' >> parent/.hg/hgrc
1498 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1498 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1499 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1499 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1500 $ hg -R parent status -S -A
1500 $ hg -R parent status -S -A
1501 reposetup() for $TESTTMP/reposetup-test/parent (glob)
1501 reposetup() for $TESTTMP/reposetup-test/parent (glob)
1502 reposetup() for $TESTTMP/reposetup-test/parent/sub2 (glob)
1502 reposetup() for $TESTTMP/reposetup-test/parent/sub2 (glob)
1503 C .hgsub
1503 C .hgsub
1504 C .hgsubstate
1504 C .hgsubstate
1505 C sub1/1
1505 C sub1/1
1506 C sub2/.hgsub
1506 C sub2/.hgsub
1507 C sub2/.hgsubstate
1507 C sub2/.hgsubstate
1508 C sub2/sub21/21
1508 C sub2/sub21/21
1509 C sub3/3
1509 C sub3/3
1510
1510
1511 $ cd ..
1511 $ cd ..
1512
1512
1513 Test compatibility with extension commands that don't use @command (issue5137)
1514
1515 $ hg init deprecated
1516 $ cd deprecated
1517
1518 $ cat <<EOF > deprecatedcmd.py
1519 > def deprecatedcmd(repo, ui):
1520 > pass
1521 > cmdtable = {
1522 > 'deprecatedcmd': (deprecatedcmd, [], ''),
1523 > }
1524 > EOF
1525 $ cat <<EOF > .hg/hgrc
1526 > [extensions]
1527 > deprecatedcmd = `pwd`/deprecatedcmd.py
1528 > mq = !
1529 > hgext.mq = !
1530 > hgext/mq = !
1531 > [alias]
1532 > deprecatedalias = deprecatedcmd
1533 > EOF
1534
1535 $ hg deprecatedcmd
1536 devel-warn: missing attribute 'norepo', use @command decorator to register 'deprecatedcmd'
1537 (compatibility will be dropped after Mercurial-3.8, update your code.) at: * (glob)
1538
1539 $ hg deprecatedalias
1540 devel-warn: missing attribute 'norepo', use @command decorator to register 'deprecatedalias'
1541 (compatibility will be dropped after Mercurial-3.8, update your code.) at: * (glob)
1542
1543 no warning unless command is executed:
1544
1545 $ hg paths
1546
1547 but mq iterates over command table:
1548
1549 $ hg --config extensions.mq= paths
1550 devel-warn: missing attribute 'norepo', use @command decorator to register 'deprecatedcmd'
1551 (compatibility will be dropped after Mercurial-3.8, update your code.) at: * (glob)
1552
1553 $ cd ..
1554
1555 Test synopsis and docstring extending
1513 Test synopsis and docstring extending
1556
1514
1557 $ hg init exthelp
1515 $ hg init exthelp
1558 $ cat > exthelp.py <<EOF
1516 $ cat > exthelp.py <<EOF
1559 > from mercurial import commands, extensions
1517 > from mercurial import commands, extensions
1560 > def exbookmarks(orig, *args, **opts):
1518 > def exbookmarks(orig, *args, **opts):
1561 > return orig(*args, **opts)
1519 > return orig(*args, **opts)
1562 > def uisetup(ui):
1520 > def uisetup(ui):
1563 > synopsis = ' GREPME [--foo] [-x]'
1521 > synopsis = ' GREPME [--foo] [-x]'
1564 > docstring = '''
1522 > docstring = '''
1565 > GREPME make sure that this is in the help!
1523 > GREPME make sure that this is in the help!
1566 > '''
1524 > '''
1567 > extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks,
1525 > extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks,
1568 > synopsis, docstring)
1526 > synopsis, docstring)
1569 > EOF
1527 > EOF
1570 $ abspath=`pwd`/exthelp.py
1528 $ abspath=`pwd`/exthelp.py
1571 $ echo '[extensions]' >> $HGRCPATH
1529 $ echo '[extensions]' >> $HGRCPATH
1572 $ echo "exthelp = $abspath" >> $HGRCPATH
1530 $ echo "exthelp = $abspath" >> $HGRCPATH
1573 $ cd exthelp
1531 $ cd exthelp
1574 $ hg help bookmarks | grep GREPME
1532 $ hg help bookmarks | grep GREPME
1575 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1533 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1576 GREPME make sure that this is in the help!
1534 GREPME make sure that this is in the help!
1577
1535
General Comments 0
You need to be logged in to leave comments. Login now