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