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