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