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