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