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