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