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