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