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